diff --git a/selectable/static/selectable/js/jquery.dj.selectable.js b/selectable/static/selectable/js/jquery.dj.selectable.js
index 7555a41..e82311c 100644
--- a/selectable/static/selectable/js/jquery.dj.selectable.js
+++ b/selectable/static/selectable/js/jquery.dj.selectable.js
@@ -11,383 +11,397 @@
* Copyright 2010-2014, Mark Lavin
* BSD License
*
-*/
-(function ($) {
+ */
+document.addEventListener("DOMContentLoaded", function() {
+ (function($) {
- $.widget("ui.djselectable", $.ui.autocomplete, {
+ $.widget("ui.djselectable", $.ui.autocomplete, {
- options: {
- removeIcon: "ui-icon-close",
- comboboxIcon: "ui-icon-triangle-1-s",
- defaultClasses: {
- "text": "ui-widget ui-widget-content ui-corner-all",
- "combobox": "ui-widget ui-widget-content ui-corner-left ui-combo-input"
- },
- prepareQuery: null,
- highlightMatch: true,
- formatLabel: null
+ options: {
+ removeIcon: "ui-icon-close",
+ comboboxIcon: "ui-icon-triangle-1-s",
+ defaultClasses: {
+ "text": "ui-widget ui-widget-content ui-corner-all",
+ "combobox": "ui-widget ui-widget-content ui-corner-left ui-combo-input"
},
+ prepareQuery: null,
+ highlightMatch: true,
+ formatLabel: null
+ },
- _initDeck: function () {
- /* Create list display for currently selected items for multi-select */
- var self = this;
- var data = $(this.element).data();
- var style = data.selectablePosition || data['selectable-position'] || 'bottom';
- this.deck = $('
').addClass('ui-widget selectable-deck selectable-deck-' + style);
- if (style === 'bottom' || style === 'bottom-inline') {
- $(this.element).after(this.deck);
- } else {
- $(this.element).before(this.deck);
- }
- $(self.hiddenMultipleSelector).each(function (i, input) {
- self._addDeckItem(input);
- });
- },
+ _initDeck: function() {
+ /* Create list display for currently selected items for multi-select */
+ var self = this;
+ var data = $(this.element).data();
+ var style = data.selectablePosition || data['selectable-position'] || 'bottom';
+ this.deck = $('').addClass('ui-widget selectable-deck selectable-deck-' + style);
+ if (style === 'bottom' || style === 'bottom-inline') {
+ $(this.element).after(this.deck);
+ } else {
+ $(this.element).before(this.deck);
+ }
+ $(self.hiddenMultipleSelector).each(function(i, input) {
+ self._addDeckItem(input);
+ });
+ },
- _addDeckItem: function (input) {
- /* Add new deck list item from a given hidden input */
- var self = this,
- li = $('- ').addClass('selectable-deck-item'),
- item = {element: self.element, input: input, wrapper: li, deck: self.deck},
- button;
- li.html($(input).attr('title'));
- if (self._trigger("add", null, item) === false) {
- input.remove();
- } else {
- button = this._removeButtonTemplate(item);
- button.click(function (e) {
- e.preventDefault();
- if (self._trigger("remove", e, item) !== false) {
- $(input).remove();
- li.remove();
- }
- });
- li.append(button).appendTo(this.deck);
+ _addDeckItem: function(input) {
+ /* Add new deck list item from a given hidden input */
+ var self = this,
+ li = $('
- ').addClass('selectable-deck-item'),
+ item = {
+ element: self.element,
+ input: input,
+ wrapper: li,
+ deck: self.deck
+ },
+ button;
+ li.html($(input).attr('title'));
+ if (self._trigger("add", null, item) === false) {
+ input.remove();
+ } else {
+ button = this._removeButtonTemplate(item);
+ button.click(function(e) {
+ e.preventDefault();
+ if (self._trigger("remove", e, item) !== false) {
+ $(input).remove();
+ li.remove();
}
- },
+ });
+ li.append(button).appendTo(this.deck);
+ }
+ },
- _removeButtonTemplate: function (item) {
- var options = {
- icons: {
- primary: this.options.removeIcon
- },
- text: false,
- disabled: this.disabled
- },
- button = $('')
- .attr('href', '#')
- .addClass('selectable-deck-remove')
- .button(options);
- return button;
- },
+ _removeButtonTemplate: function(item) {
+ var options = {
+ icons: {
+ primary: this.options.removeIcon
+ },
+ text: false,
+ disabled: this.disabled
+ },
+ button = $('')
+ .attr('href', '#')
+ .addClass('selectable-deck-remove')
+ .button(options);
+ return button;
+ },
- select: function (item, event) {
- /* Trigger selection of a given item.
- Item should contain two properties: id and value
- Event is the original select event if there is one.
- Event should not be passed if triggered manually.
- */
- var $input = $(this.element);
- $input.removeClass('ui-state-error');
- this._setHidden(item);
- if (item) {
- if (this.allowMultiple) {
- $input.val("");
- this.term = "";
- if ($(this.hiddenMultipleSelector + '[value="' + item.id + '"]').length === 0) {
- var newInput = $('', {
- 'type': 'hidden',
- 'name': this.hiddenName,
- 'value': item.id,
- 'title': item.value,
- 'data-selectable-type': 'hidden-multiple'
- });
- $input.after(newInput);
- this._addDeckItem(newInput);
- }
- return false;
- } else {
- $input.val(item.value);
- var ui = {item: item};
- if (typeof(event) === 'undefined' || event.type !== "djselectableselect") {
- this.element.trigger("djselectableselect", [ui ]);
- }
- }
+ select: function(item, event) {
+ /* Trigger selection of a given item.
+ Item should contain two properties: id and value
+ Event is the original select event if there is one.
+ Event should not be passed if triggered manually.
+ */
+ var $input = $(this.element);
+ $input.removeClass('ui-state-error');
+ this._setHidden(item);
+ if (item) {
+ if (this.allowMultiple) {
+ $input.val("");
+ this.term = "";
+ if ($(this.hiddenMultipleSelector + '[value="' + item.id + '"]').length === 0) {
+ var newInput = $('', {
+ 'type': 'hidden',
+ 'name': this.hiddenName,
+ 'value': item.id,
+ 'title': item.value,
+ 'data-selectable-type': 'hidden-multiple'
+ });
+ $input.after(newInput);
+ this._addDeckItem(newInput);
}
- },
-
- _setHidden: function (item) {
- /* Set or clear single hidden input */
- var $elem = $(this.hiddenSelector);
- if (item && item.id) {
- $elem.val(item.id);
- } else {
- $elem.val("");
+ return false;
+ } else {
+ $input.val(item.value);
+ var ui = {
+ item: item
+ };
+ if (typeof(event) === 'undefined' || event.type !== "djselectableselect") {
+ this.element.trigger("djselectableselect", [ui]);
}
- },
+ }
+ }
+ },
- _comboButtonTemplate: function (input) {
- // Add show all items button
- var options = {
- icons: {
- primary: this.options.comboboxIcon
- },
- text: false,
- disabled: this.disabled
- },
- button = $("")
- .html(" ")
- .attr("tabIndex", -1)
- .attr("title", "Show All Items")
- .button(options)
- .removeClass("ui-corner-all")
- .addClass("ui-corner-right ui-button-icon ui-combo-button");
- return button;
- },
+ _setHidden: function(item) {
+ /* Set or clear single hidden input */
+ var $elem = $(this.hiddenSelector);
+ if (item && item.id) {
+ $elem.val(item.id);
+ } else {
+ $elem.val("");
+ }
+ },
- _create: function () {
- /* Initialize a new selectable widget */
- var self = this,
- $input = $(this.element),
- data = $input.data(),
- options, button;
- this.url = data.selectableUrl || data['selectable-url'];
- this.allowNew = data.selectableAllowNew || data['selectable-allow-new'];
- this.allowMultiple = data.selectableMultiple || data['selectable-multiple'];
- this.textName = $input.attr('name');
- this.hiddenName = this.textName.replace(new RegExp('_0$'), '_1');
- this.hiddenSelector = ':input[data-selectable-type=hidden][name=' + this.hiddenName + ']';
- this.hiddenMultipleSelector = ':input[data-selectable-type=hidden-multiple][name=' + this.hiddenName + ']';
- this.selectableType = data.selectableType || data['selectable-type'];
- this.disabled = $input.prop('disabled');
- if (this.allowMultiple) {
- this.allowNew = false;
- $input.val("");
- this._initDeck();
- }
- options = data.selectableOptions || data['selectable-options'];
- if (options) {
- this._setOptions(options);
- }
- // Call super-create
- // This could be replaced by this._super() with jQuery UI 1.9
- $.ui.autocomplete.prototype._create.call(this);
- $input.addClass(this.options.defaultClasses[this.selectableType]);
- // Additional work for combobox widgets
- if (this.selectableType === 'combobox') {
- // Add show all items button
- button = this._comboButtonTemplate($input);
- button.insertAfter($input).click(function (e) {
- e.preventDefault();
- // close if already visible
- if (self.widget().is(":visible")) {
- self.close();
- }
- // pass empty string as value to search for, displaying all results
- self._search("");
- $input.focus();
- });
- }
- },
+ _comboButtonTemplate: function(input) {
+ // Add show all items button
+ var options = {
+ icons: {
+ primary: this.options.comboboxIcon
+ },
+ text: false,
+ disabled: this.disabled
+ },
+ button = $("")
+ .html(" ")
+ .attr("tabIndex", -1)
+ .attr("title", "Show All Items")
+ .button(options)
+ .removeClass("ui-corner-all")
+ .addClass("ui-corner-right ui-button-icon ui-combo-button");
+ return button;
+ },
- // Override the default source creation
- _initSource: function () {
- var self = this,
- $input = $(this.element);
- this.source = function dataSource(request, response) {
- /* Custom data source to uses the lookup url with pagination
- Adds hook for adjusting query parameters.
- Includes timestamp to prevent browser caching the lookup. */
- var now = new Date().getTime(),
- query = {term: request.term, timestamp: now},
- page = $input.data("page");
- if (self.options.prepareQuery) {
- self.options.prepareQuery.apply(self, [query]);
- }
- if (page) {
- query.page = page;
- }
- function unwrapResponse(data) {
- var results = data.data,
- meta = data.meta;
- if (meta.next_page && meta.more) {
- results.push({
- id: '',
- value: '',
- label: meta.more,
- page: meta.next_page,
- term: request.term
- });
- }
- return response(results);
- }
- $.getJSON(self.url, query, unwrapResponse);
- };
- },
- // Override the default auto-complete render.
- _renderItem: function (ul, item) {
- /* Adds hook for additional formatting, allows HTML in the label,
- highlights term matches and handles pagination. */
- var label = item.label,
- self = this,
- $input = $(this.element),
- re, html, li;
- if (this.options.formatLabel && !item.page) {
- label = this.options.formatLabel.apply(this, [label, item]);
- }
- if (this.options.highlightMatch && this.term && !item.page) {
- re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" +
- $.ui.autocomplete.escapeRegex(this.term) +
- ")(?![^<>]*>)(?![^&;]+;)", "gi");
- if (label.html) {
- html = label.html();
- html = html.replace(re, "$1");
- label.html(html);
- } else {
- label = label.replace(re, "$1");
- }
- }
- li = $("")
- .data("item.autocomplete", item)
- .append($("").append(label))
- .appendTo(ul);
- if (item.page) {
- li.addClass('selectable-paginator');
- }
- return li;
- },
- // Override the default auto-complete suggest.
- _suggest: function (items) {
- /* Needed for handling pagination links */
- var $input = $(this.element),
- page = $input.data('page'),
- ul = this.menu.element;
- if (page) {
- $('.selectable-paginator', ul).remove();
- } else {
- ul.empty();
- }
- $input.data('page', null);
- ul.css("zIndex", $input.css("zIndex") + 1);
- this._renderMenu(ul, items);
- // jQuery UI menu does not define deactivate
- if (this.menu.deactivate) {
- this.menu.deactivate();
- }
- this.menu.refresh();
- // size and position menu
- ul.show();
- this._resizeMenu();
- ul.position($.extend({of: this.element}, this.options.position));
- if (this.options.autoFocus) {
- this.menu.next(new $.Event("mouseover"));
- } else if (page) {
- $input.focus();
+ _create: function() {
+ /* Initialize a new selectable widget */
+ var self = this,
+ $input = $(this.element),
+ data = $input.data(),
+ options, button;
+ this.url = data.selectableUrl || data['selectable-url'];
+ this.allowNew = data.selectableAllowNew || data['selectable-allow-new'];
+ this.allowMultiple = data.selectableMultiple || data['selectable-multiple'];
+ this.textName = $input.attr('name');
+ this.hiddenName = this.textName.replace(new RegExp('_0$'), '_1');
+ this.hiddenSelector = ':input[data-selectable-type=hidden][name=' + this.hiddenName + ']';
+ this.hiddenMultipleSelector = ':input[data-selectable-type=hidden-multiple][name=' + this.hiddenName + ']';
+ this.selectableType = data.selectableType || data['selectable-type'];
+ this.disabled = $input.prop('disabled');
+ if (this.allowMultiple) {
+ this.allowNew = false;
+ $input.val("");
+ this._initDeck();
+ }
+ options = data.selectableOptions || data['selectable-options'];
+ if (options) {
+ this._setOptions(options);
+ }
+ // Call super-create
+ // This could be replaced by this._super() with jQuery UI 1.9
+ $.ui.autocomplete.prototype._create.call(this);
+ $input.addClass(this.options.defaultClasses[this.selectableType]);
+ // Additional work for combobox widgets
+ if (this.selectableType === 'combobox') {
+ // Add show all items button
+ button = this._comboButtonTemplate($input);
+ button.insertAfter($input).click(function(e) {
+ e.preventDefault();
+ // close if already visible
+ if (self.widget().is(":visible")) {
+ self.close();
}
- },
- // Override default trigger for additional change/select logic
- _trigger: function (type, event, data) {
- var $input = $(this.element),
- self = this;
- if (type === "select") {
- $input.removeClass('ui-state-error');
- if (data.item.page) {
- $input.data("page", data.item.page);
- this._search(data.item.term);
- return false;
- }
- return this.select(data.item, event);
- } else if (type === "change") {
- $input.removeClass('ui-state-error');
- this._setHidden(data.item);
- if ($input.val() && !data.item) {
- if (!this.allowNew) {
- $input.addClass('ui-state-error');
- }
- }
- if (this.allowMultiple && !$input.hasClass('ui-state-error')) {
- $input.val("");
- this.term = "";
- }
+ // pass empty string as value to search for, displaying all results
+ self._search("");
+ $input.focus();
+ });
+ }
+ },
+
+ // Override the default source creation
+ _initSource: function() {
+ var self = this,
+ $input = $(this.element);
+ this.source = function dataSource(request, response) {
+ /* Custom data source to uses the lookup url with pagination
+ Adds hook for adjusting query parameters.
+ Includes timestamp to prevent browser caching the lookup. */
+ var now = new Date().getTime(),
+ query = {
+ term: request.term,
+ timestamp: now
+ },
+ page = $input.data("page");
+ if (self.options.prepareQuery) {
+ self.options.prepareQuery.apply(self, [query]);
+ }
+ if (page) {
+ query.page = page;
+ }
+
+ function unwrapResponse(data) {
+ var results = data.data,
+ meta = data.meta;
+ if (meta.next_page && meta.more) {
+ results.push({
+ id: '',
+ value: '',
+ label: meta.more,
+ page: meta.next_page,
+ term: request.term
+ });
}
- // Call super trigger
- // This could be replaced by this._super() with jQuery UI 1.9
- return $.ui.autocomplete.prototype._trigger.apply(this, arguments);
- },
- close: function (event) {
- var page = $(this.element).data('page');
- if (page !== null) {
- return;
+ return response(results);
+ }
+ $.getJSON(self.url, query, unwrapResponse);
+ };
+ },
+ // Override the default auto-complete render.
+ _renderItem: function(ul, item) {
+ /* Adds hook for additional formatting, allows HTML in the label,
+ highlights term matches and handles pagination. */
+ var label = item.label,
+ self = this,
+ $input = $(this.element),
+ re, html, li;
+ if (this.options.formatLabel && !item.page) {
+ label = this.options.formatLabel.apply(this, [label, item]);
+ }
+ if (this.options.highlightMatch && this.term && !item.page) {
+ re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" +
+ $.ui.autocomplete.escapeRegex(this.term) +
+ ")(?![^<>]*>)(?![^&;]+;)", "gi");
+ if (label.html) {
+ html = label.html();
+ html = html.replace(re, "$1");
+ label.html(html);
+ } else {
+ label = label.replace(re, "$1");
+ }
+ }
+ li = $("
")
+ .data("item.autocomplete", item)
+ .append($("").append(label))
+ .appendTo(ul);
+ if (item.page) {
+ li.addClass('selectable-paginator');
+ }
+ return li;
+ },
+ // Override the default auto-complete suggest.
+ _suggest: function(items) {
+ /* Needed for handling pagination links */
+ var $input = $(this.element),
+ page = $input.data('page'),
+ ul = this.menu.element;
+ if (page) {
+ $('.selectable-paginator', ul).remove();
+ } else {
+ ul.empty();
+ }
+ $input.data('page', null);
+ ul.css("zIndex", $input.css("zIndex") + 1);
+ this._renderMenu(ul, items);
+ // jQuery UI menu does not define deactivate
+ if (this.menu.deactivate) {
+ this.menu.deactivate();
+ }
+ this.menu.refresh();
+ // size and position menu
+ ul.show();
+ this._resizeMenu();
+ ul.position($.extend({ of: this.element
+ }, this.options.position));
+ if (this.options.autoFocus) {
+ this.menu.next(new $.Event("mouseover"));
+ } else if (page) {
+ $input.focus();
+ }
+ },
+ // Override default trigger for additional change/select logic
+ _trigger: function(type, event, data) {
+ var $input = $(this.element),
+ self = this;
+ if (type === "select") {
+ $input.removeClass('ui-state-error');
+ if (data.item.page) {
+ $input.data("page", data.item.page);
+ this._search(data.item.term);
+ return false;
+ }
+ return this.select(data.item, event);
+ } else if (type === "change") {
+ $input.removeClass('ui-state-error');
+ this._setHidden(data.item);
+ if ($input.val() && !data.item) {
+ if (!this.allowNew) {
+ $input.addClass('ui-state-error');
}
- // Call super trigger
- // This could be replaced by this._super() with jQuery UI 1.9
- return $.ui.autocomplete.prototype.close.apply(this, arguments);
+ }
+ if (this.allowMultiple && !$input.hasClass('ui-state-error')) {
+ $input.val("");
+ this.term = "";
+ }
}
- });
+ // Call super trigger
+ // This could be replaced by this._super() with jQuery UI 1.9
+ return $.ui.autocomplete.prototype._trigger.apply(this, arguments);
+ },
+ close: function(event) {
+ var page = $(this.element).data('page');
+ if (page !== null) {
+ return;
+ }
+ // Call super trigger
+ // This could be replaced by this._super() with jQuery UI 1.9
+ return $.ui.autocomplete.prototype.close.apply(this, arguments);
+ }
+ });
- window.bindSelectables = function (context) {
- /* Bind all selectable widgets in a given context.
- Automatically called on document.ready.
- Additional calls can be made for dynamically added widgets.
- */
- $(":input[data-selectable-type=text]", context).djselectable();
- $(":input[data-selectable-type=combobox]", context).djselectable();
+ window.bindSelectables = function(context) {
+ /* Bind all selectable widgets in a given context.
+ Automatically called on document.ready.
+ Additional calls can be made for dynamically added widgets.
+ */
+ $(":input[data-selectable-type=text]", context).djselectable();
+ $(":input[data-selectable-type=combobox]", context).djselectable();
};
function djangoAdminPatches() {
- /* Listen for new rows being added to the dynamic inlines.
- Requires Django 1.5+ */
- $('body').on('click', '.add-row', function (e) {
- var wrapper = $(this).parents('.inline-related'),
- newRow = $('.form-row:not(.empty-form)', wrapper).last();
- window.bindSelectables(newRow);
- });
+ /* Listen for new rows being added to the dynamic inlines.
+ Requires Django 1.5+ */
+ $('body').on('click', '.add-row', function(e) {
+ var wrapper = $(this).parents('.inline-related'),
+ newRow = $('.form-row:not(.empty-form)', wrapper).last();
+ window.bindSelectables(newRow);
+ });
- /* Monkey-patch Django's dismissAddAnotherPopup(), if defined */
- if (typeof(dismissAddAnotherPopup) !== "undefined" &&
- typeof(windowname_to_id) !== "undefined" &&
- typeof(html_unescape) !== "undefined") {
- var django_dismissAddAnotherPopup = dismissAddAnotherPopup;
- dismissAddAnotherPopup = function (win, newId, newRepr) {
- /* See if the popup came from a selectable field.
- If not, pass control to Django's code.
- If so, handle it. */
- var fieldName = windowname_to_id(win.name); /* e.g. "id_fieldname" */
- var field = $('#' + fieldName);
- var multiField = $('#' + fieldName + '_0');
- /* Check for bound selectable */
- var singleWidget = field.data('djselectable');
- var multiWidget = multiField.data('djselectable');
- if (singleWidget || multiWidget) {
- // newId and newRepr are expected to have previously been escaped by
- // django.utils.html.escape.
- var item = {
- id: html_unescape(newId),
- value: html_unescape(newRepr)
- };
- if (singleWidget) {
- field.djselectable('select', item);
- }
- if (multiWidget) {
- multiField.djselectable('select', item);
- }
- win.close();
- } else {
- /* Not ours, pass on to original function. */
- return django_dismissAddAnotherPopup(win, newId, newRepr);
- }
+ /* Monkey-patch Django's dismissAddAnotherPopup(), if defined */
+ if (typeof(dismissAddAnotherPopup) !== "undefined" &&
+ typeof(windowname_to_id) !== "undefined" &&
+ typeof(html_unescape) !== "undefined") {
+ var django_dismissAddAnotherPopup = dismissAddAnotherPopup;
+ dismissAddAnotherPopup = function(win, newId, newRepr) {
+ /* See if the popup came from a selectable field.
+ If not, pass control to Django's code.
+ If so, handle it. */
+ var fieldName = windowname_to_id(win.name); /* e.g. "id_fieldname" */
+ var field = $('#' + fieldName);
+ var multiField = $('#' + fieldName + '_0');
+ /* Check for bound selectable */
+ var singleWidget = field.data('djselectable');
+ var multiWidget = multiField.data('djselectable');
+ if (singleWidget || multiWidget) {
+ // newId and newRepr are expected to have previously been escaped by
+ // django.utils.html.escape.
+ var item = {
+ id: html_unescape(newId),
+ value: html_unescape(newRepr)
};
- }
+ if (singleWidget) {
+ field.djselectable('select', item);
+ }
+ if (multiWidget) {
+ multiField.djselectable('select', item);
+ }
+ win.close();
+ } else {
+ /* Not ours, pass on to original function. */
+ return django_dismissAddAnotherPopup(win, newId, newRepr);
+ }
+ };
+ }
}
- $(document).ready(function () {
- // Patch the django admin JS
- if (typeof(djselectableAdminPatch) === "undefined" || djselectableAdminPatch) {
- djangoAdminPatches();
- }
- // Bind existing widgets on document ready
- if (typeof(djselectableAutoLoad) === "undefined" || djselectableAutoLoad) {
- window.bindSelectables('body');
- }
+ $(document).ready(function() {
+ // Patch the django admin JS
+ if (typeof(djselectableAdminPatch) === "undefined" || djselectableAdminPatch) {
+ djangoAdminPatches();
+ }
+ // Bind existing widgets on document ready
+ if (typeof(djselectableAutoLoad) === "undefined" || djselectableAutoLoad) {
+ window.bindSelectables('body');
+ }
});
-})(jQuery || grp.jQuery);
+ })(jQuery || grp.jQuery);
+});