Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form attribute support #802

Merged
merged 29 commits into from
Dec 15, 2016
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
049c0f8
Add polyfill.
Sep 21, 2016
791a0a8
Add form attributes to bottom buttons and checkboxes in search.
Sep 21, 2016
67d5f0c
Adjust checkbox-select-all to allow for [form] attributes.
Sep 21, 2016
fc0e2bc
Adjust unit test.
Sep 22, 2016
67fe24f
Merge remote-tracking branch 'origin/master' into form-attr-cart
Sep 22, 2016
246b2c4
Remove magic script form retention hack.
Sep 22, 2016
233e16f
Expand _clickedButton logic in lightbox to include [form] attrs.
Sep 22, 2016
ecdf8f0
Eslint. Remove console.log.
Sep 22, 2016
f6e44e2
Merge remote-tracking branch 'origin/master' into form-attr-cart
Sep 22, 2016
9b5572e
grecaptcha reset fix.
Sep 22, 2016
ceeb2a6
Polyfill fixed to run on document ready.
Sep 22, 2016
2d5763d
Merge branch 'master' into form-attr-cart
demiankatz Sep 26, 2016
b228715
removed <script> hack
demiankatz Sep 26, 2016
655f8c9
Find form attributed checkboxes as well for search cart adding.
Sep 27, 2016
f1c6954
Merge remote-tracking branch 'origin/release-3.1' into form-attr-cart
Sep 27, 2016
26db220
eslint
Sep 27, 2016
f158fee
Merge branch 'master' into form-attr-cart
Sep 29, 2016
3fb1d45
formAttr parameter for bulk-action-buttons.
Sep 29, 2016
328cc82
Merge remote-tracking branch 'origin/master' into form-attr-cart
Oct 4, 2016
4fb2cae
Switch idPrefix to formAttr in one missed place.
Oct 14, 2016
0088e6c
Consolidate recaptcha resetting logic.
Oct 21, 2016
6ac9da3
Merge remote-tracking branch 'origin/master' into form-attr-cart
Oct 21, 2016
fea6d6b
Returning some flexibility to cart update button submission.
Nov 8, 2016
9d8d7bf
Move form-attr-polyfill to /lib
Nov 8, 2016
25fdfdf
Merge remote-tracking branch 'origin/master' into form-attr-cart
Dec 2, 2016
649347a
Improve form-attr-polyfill.js
Dec 13, 2016
e97311f
Remove unnecessary class.
Dec 13, 2016
bc5be43
Pass in formAttr for bottom search bulk action form.
Dec 13, 2016
0fe706d
Whitespace fix.
Dec 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion module/VuFind/src/VuFind/Service/ReCaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function getHtml($name = null)
$divregex = '/<div[^>]*id=[\'"]recaptcha_widget[\'"][^>]*>/';

$scriptRegex = '|<script[^>]*></script>|';
$scriptReplacement = '<script>/*form magic*/</script>';
$scriptReplacement = ''; // remove

return preg_replace(
[$divregex, $scriptRegex],
Expand Down
6 changes: 5 additions & 1 deletion module/VuFind/src/VuFind/View/Helper/Root/Record.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,20 @@ public function getSearchResult($view)
* Render an HTML checkbox control for the current record.
*
* @param string $idPrefix Prefix for checkbox HTML ids
* @param string $formAttr ID of form for [form] attribute
*
* @return string
*/
public function getCheckbox($idPrefix = '')
public function getCheckbox($idPrefix = '', $formAttr = false)
{
static $checkboxCount = 0;
$id = $this->driver->getSourceIdentifier() . '|'
. $this->driver->getUniqueId();
$context
= ['id' => $id, 'count' => $checkboxCount++, 'prefix' => $idPrefix];
if ($formAttr) {
$context['formAttr'] = $formAttr;
}
return $this->contextHelper->renderInContext(
'record/checkbox.phtml', $context
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ public function testGetCheckbox()
{
$context = $this->getMockContext();
$context->expects($this->at(1))->method('renderInContext')
->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 0, 'prefix' => 'bar']))
->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 0, 'prefix' => 'bar', 'formAttr' => 'foo']))
->will($this->returnValue('success'));
$context->expects($this->at(2))->method('renderInContext')
->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 1, 'prefix' => 'bar']))
->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 1, 'prefix' => 'bar', 'formAttr' => 'foo']))
->will($this->returnValue('success'));
$record = $this->getRecord(
$this->loadRecordFixture('testbug1.json'), [], $context
Expand Down
82 changes: 42 additions & 40 deletions themes/bootstrap3/js/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,47 +122,50 @@ VuFind.register('cart', function Cart() {
}

var _cartNotificationTimeout = false;
function _registerUpdate($form) {
if ($form) {
$("#updateCart, #bottom_updateCart").unbind('click').click(function cartUpdate() {
var elId = this.id;
var selectedBoxes = $("input[name='ids[]']:checked", $form);
var selected = [];
$(selectedBoxes).each(function cartCheckboxValues(i) {
selected[i] = this.value;
function _registerUpdate() {
var $form = $('form[name="bulkActionForm"]');
$("#updateCart, #bottom_updateCart").unbind('click').click(function cartUpdate() {
var elId = this.id;
var selected = [];
var selectedInForm = $form.find('input[name="ids[]"]:checked');
var selectedFormAttr = $('input[form="' + $form.attr('id') + '"][name="ids[]"]:checked');
$(selectedInForm).each(function cartFormCheckboxValues() {
selected.push(this.value);
});
$(selectedFormAttr).each(function cartAttrCheckboxValues() {
selected.push(this.value);
});
if (selected.length > 0) {
var msg = "";
var orig = getFullItems();
$(selected).each(function cartCheckedItemsAdd() {
var data = this.split('|');
addItem(data[1], data[0]);
});
if (selected.length > 0) {
var msg = "";
var orig = getFullItems();
$(selected).each(function cartCheckedItemsAdd() {
var data = this.split('|');
addItem(data[1], data[0]);
});
var updated = getFullItems();
var added = updated.length - orig.length;
var inCart = selected.length - added;
msg += added + " " + VuFind.translate('itemsAddBag');
if (updated.length >= parseInt(VuFind.translate('bookbagMax'), 10)) {
msg += "<br/>" + VuFind.translate('bookbagFull');
}
if (inCart > 0 && orig.length > 0) {
msg += "<br/>" + inCart + " " + VuFind.translate('itemsInBag');
}
$('#' + elId).data('bs.popover').options.content = msg;
$('#cartItems strong').html(updated.length);
} else {
$('#' + elId).data('bs.popover').options.content = VuFind.translate('bulk_noitems_advice');
var updated = getFullItems();
var added = updated.length - orig.length;
var inCart = selected.length - added;
msg += added + " " + VuFind.translate('itemsAddBag');
if (updated.length >= parseInt(VuFind.translate('bookbagMax'), 10)) {
msg += "<br/>" + VuFind.translate('bookbagFull');
}
$('#' + elId).popover('show');
if (_cartNotificationTimeout !== false) {
clearTimeout(_cartNotificationTimeout);
if (inCart > 0 && orig.length > 0) {
msg += "<br/>" + inCart + " " + VuFind.translate('itemsInBag');
}
_cartNotificationTimeout = setTimeout(function notificationHide() {
$('#' + elId).popover('hide');
}, 5000);
return false;
});
}
$('#' + elId).data('bs.popover').options.content = msg;
$('#cartItems strong').html(updated.length);
} else {
$('#' + elId).data('bs.popover').options.content = VuFind.translate('bulk_noitems_advice');
}
$('#' + elId).popover('show');
if (_cartNotificationTimeout !== false) {
clearTimeout(_cartNotificationTimeout);
}
_cartNotificationTimeout = setTimeout(function notificationHide() {
$('#' + elId).popover('hide');
}, 5000);
return false;
});
}

function init() {
Expand Down Expand Up @@ -192,8 +195,7 @@ VuFind.register('cart', function Cart() {
});
} else {
// Search results
var $form = $('form[name="bulkActionForm"]');
_registerUpdate($form);
_registerUpdate();
}
$("#updateCart, #bottom_updateCart").popover({content: '', html: true, trigger: 'manual'});
updateCount();
Expand Down
14 changes: 12 additions & 2 deletions themes/bootstrap3/js/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,20 @@ $(document).ready(function commonDocReady() {

// Checkbox select all
$('.checkbox-select-all').change(function selectAllCheckboxes() {
$(this).closest('form').find('.checkbox-select-item').prop('checked', this.checked);
var $form = $(this).closest('form')
$form.find('.checkbox-select-item').prop('checked', this.checked);
$('[form="' + $form.attr('id') + '"]').prop('checked', this.checked);
});
$('.checkbox-select-item').change(function selectAllDisable() {
$(this).closest('form').find('.checkbox-select-all').prop('checked', false);
var $form = $(this).closest('form');
if ($form.length === 0 && this.form) {
$form = $(this.form);
}
if ($form.length === 0) {
return;
}
$form.find('.checkbox-select-all').prop('checked', false);
$('.checkbox-select-all[form="' + $form.attr('id') + '"]').prop('checked', false);
});

// handle QR code links
Expand Down
10 changes: 8 additions & 2 deletions themes/bootstrap3/js/lightbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,10 @@ VuFind.register('lightbox', function Lightbox() {
data: data
}).done(function recaptchaReset() {
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset($(form).find('.g-recaptcha').data('captchaId'));
var captcha = $(form).find('.g-recaptcha');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This captcha-related change appears to pop up several times in this PR. Is this related to the form change, or is this an unrelated improvement? Do we perhaps want a resetReCaptcha(form) function in common.js so we don't have to repeat the same four lines of code multiple times, or is that overkill?

if (captcha.length > 0) {
grecaptcha.reset(captcha.data('captchaId'));
}
}
});

Expand All @@ -299,7 +302,10 @@ VuFind.register('lightbox', function Lightbox() {
// Handle submit buttons attached to a form as well as those in a form. Store
// information about which button was clicked here as checking focused button
// doesn't work on all browsers and platforms.
$('form[data-lightbox] [type=submit]').click(_storeClickedStatus);
$('form[data-lightbox]').each(function bindFormSubmitsLightbox(i, form) {
$(form).find('[type=submit]').click(_storeClickedStatus);
$('[type="submit"][form="' + form.id + '"]').click(_storeClickedStatus);
});
}

function reset() {
Expand Down
10 changes: 8 additions & 2 deletions themes/bootstrap3/js/record.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ function refreshCommentList($target, recordId, recordSource) {
});
$target.find('.comment-form input[type="submit"]').button('reset');
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
var captcha = $target.find('.g-recaptcha');
if (captcha.length > 0) {
grecaptcha.reset(captcha.data('captchaId'));
}
}
});
}
Expand Down Expand Up @@ -117,7 +120,10 @@ function registerAjaxCommentRecord() {
$(form).find('textarea[name="comment"]').val('');
$(form).find('input[type="submit"]').button('loading');
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset($(form).find('.g-recaptcha').data('captchaId'));
var captcha = $(form).find('.g-recaptcha');
if (captcha.length > 0) {
grecaptcha.reset(captcha.data('captchaId'));
}
}
})
.fail(function addCommentFail(response, textStatus) {
Expand Down
78 changes: 78 additions & 0 deletions themes/bootstrap3/js/vendor/form-attr-polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* From http://stackoverflow.com/questions/17742275/polyfill-html5-input-form-attribute/26696165#26696165
* Recommended by https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
* Adapted to eslint styling and updated detection by Chris Hallberg (@crhallberg) for VuFind
*/
(function formAttrPolyfill($) {
/**
* polyfill for html5 form attr
*/

// every browser supports except IE
if (typeof document.documentMode == 'undefined') {
// any other browser? skip
return;
}

$(document).ready(function formAttrReady() {
/**
* Append a field to a form
*
*/
$.fn.appendField = function appendField(_data) {
var data = (!$.isArray(_data) && _data.name && _data.value)
? [_data] // wrap data
: _data;
// for form only
if (!this.is('form')) {
return;
}

var $form = this;

// attach new params
$.each(data, function appendFieldEach(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.val(item.value).appendTo($form);
});

return $form;
};

/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function locateFormAttr(/*e*/) {
// serialize data
var data = $('[form=' + this.id + ']').serializeArray();
// append data to form
$(this).appendField(data);
}).each(function locateFormAttrEach() {
var form = this,
$fields = $('[form=' + this.id + ']');

$fields.filter('button, input').filter('[type=reset],[type=submit]').click(function formAttrButtonSubmit() {
var type = this.type.toLowerCase();
if (type === 'reset') {
// reset form
form.reset();
// for elements outside form
$fields.each(function formAttrGatherFieldData() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function formAttrGatherSelectData() {
$(this).find('option').each(function formAttrGatherOptionData() {
this.selected = this.defaultSelected;
});
});
} else if (type.match(/^submit|image$/i)) {
$(form).appendField({name: this.name, value: this.value}).submit();
}
});
});
});

})(jQuery);
2 changes: 0 additions & 2 deletions themes/bootstrap3/templates/RecordTab/usercomments.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
<textarea name="comment" class="form-control" rows="3" required></textarea><br/>
<? if ($this->tab->isRecaptchaActive()): ?>
<?=$this->recaptcha()->html(true, false) ?><br/>
<? else: ?>
<script>/* workaround for nested form bug */</script>
<? endif; ?>
<input class="btn btn-primary" data-loading-text="<?=$this->transEsc('Submitting') ?>..." type="submit" value="<?=$this->transEsc("Add your comment")?>"/>
<? else: ?>
Expand Down
4 changes: 2 additions & 2 deletions themes/bootstrap3/templates/record/checkbox.phtml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"/>
<input type="hidden" name="idsAll[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"/>
<input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<? if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<? endif; ?>/>
<input type="hidden" name="idsAll[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<? if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<? endif; ?>/>
14 changes: 7 additions & 7 deletions themes/bootstrap3/templates/search/bulk-action-buttons.phtml
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
<? if((isset($this->showBulkOptions) && $this->showBulkOptions)
|| (isset($this->showCartControls) && $this->showCartControls)): ?>
<div class="bulkActionButtons hidden-print">
<div class="bulkActionButtons hidden-print<?if($this->idPrefix):?> form-inline<? endif; ?>">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it might make more sense to include a separate attribute for form name, rather than checking idPrefix? We can rely on idPrefix based on current usage, but it's not really an intuitively logical trigger for form attribute functionality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you've changed most of the new idPrefix checks to formAttr checks, except for the one right above these comments. Is that intentional or a typo?

<div class="checkbox">
<label>
<input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"/> <?=$this->transEsc('select_page')?>
<input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> <?=$this->transEsc('select_page')?>
&#124; <?=$this->transEsc('with_selected')?>:
</label>
</div>
<div class="btn-group">
<? if (isset($this->showBulkOptions) && $this->showBulkOptions): ?>
<input id="ribbon-email" class="btn btn-default" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"/>
<input id="ribbon-email" class="btn btn-default" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/>
<? $exportOptions = $this->export()->getBulkOptions(); if (count($exportOptions) > 0): ?>
<input id="ribbon-export" class="btn btn-default" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"/>
<input id="ribbon-export" class="btn btn-default" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/>
<? endif; ?>
<input id="ribbon-print" class="btn btn-default" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"/>
<input id="ribbon-print" class="btn btn-default" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/>
<? if ($this->userlist()->getMode() !== 'disabled'): ?>
<input id="ribbon-save" class="btn btn-default" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"/>
<input id="ribbon-save" class="btn btn-default" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/>
<? endif; ?>
<? endif; ?>
<? if (isset($this->showCartControls) && $this->showCartControls): ?>
<input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-default" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/>
<input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-default" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/>
<? endif; ?>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions themes/bootstrap3/templates/search/list-list.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div class="checkbox hidden-print">
<? if ($showCheckboxes): ?>
<label>
<?=$this->record($current)->getCheckbox()?>
<?=$this->record($current)->getCheckbox('', 'search-cart-form')?>
<?=$recordNumber ?>
</label>
<? else: ?>
Expand All @@ -16,4 +16,4 @@
</div>
<?=$this->record($current)->getSearchResult('list')?>
</div>
<? endforeach; ?>
<? endforeach; ?>
8 changes: 4 additions & 4 deletions themes/bootstrap3/templates/search/results.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@
<? endif; ?>
<? endforeach; ?>
<? else: ?>
<form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler">
<form id="search-cart-form" class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler">
<?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => ''])?>
<?=$this->render('search/list-' . $this->params->getView() . '.phtml')?>
<?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => 'bottom_'])?>
<?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results, 'options' => isset($this->paginationOptions) ? $this->paginationOptions : []])?>
</form>
<?=$this->render('search/list-' . $this->params->getView() . '.phtml')?>
<?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => 'bottom_'])?>
<?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results, 'options' => isset($this->paginationOptions) ? $this->paginationOptions : []])?>

<div class="searchtools hidden-print">
<strong><?=$this->transEsc('Search Tools')?>:</strong>
Expand Down
1 change: 1 addition & 0 deletions themes/bootstrap3/theme.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'vendor/bootstrap.min.js',
'vendor/bootstrap-accessibility.min.js',
'vendor/validator.min.js',
'vendor/form-attr-polyfill.js', // input[form] polyfill, cannot load conditionally, since we need all versions of IE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really belong in the vendor directory if you wrote it? At very least, should it get its own repo somewhere in addition to being dropped here?

Copy link
Contributor Author

@crhallberg crhallberg Oct 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not write it, I found it online and updated it. I think maybe we need a new folder for the elements that stand alone, but are custom to VuFind. They exist between vendor and regular scripting. Like autocomplete and the new channels slider.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might "lib" be an appropriate directory name for this sort of thing?

'autocomplete.js',
'common.js',
'lightbox.js',
Expand Down