--- /dev/null
+(function($) {
+
+ $.widget("ui.djselectable", {
+
+ options: {
+ removeIcon: "ui-icon-close",
+ comboboxIcon: "ui-icon-triangle-1-s",
+ prepareQuery: null
+ },
+
+ _initDeck: function(hiddenInputs) {
+ var self = this;
+ var data = $(this.element).data();
+ var style = data.selectablePosition || data['selectable-position'] || 'bottom';
+ this.deck = $('<ul>').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);
+ }
+ $(hiddenInputs).each(function(i, input) {
+ self._addDeckItem(input);
+ });
+ },
+
+ _addDeckItem: function(input) {
+ var self = this;
+ $('<li>')
+ .text($(input).attr('title'))
+ .addClass('selectable-deck-item')
+ .appendTo(this.deck)
+ .append(
+ $('<div>')
+ .addClass('selectable-deck-remove')
+ .append(
+ $('<button>')
+ .button({
+ icons: {
+ primary: self.options.removeIcon
+ },
+ text: false
+ })
+ .click(function() {
+ $(input).remove();
+ $(this).closest('li').remove();
+ return false;
+ })
+ )
+ );
+ },
+
+ _create: function() {
+ var self = this,
+ input = this.element;
+ var data = $(input).data();
+ var allowNew = data.selectableAllowNew || data['selectable-allow-new'];
+ var allowMultiple = data.selectableMultiple || data['selectable-multiple'];
+ var textName = $(input).attr('name');
+ var hiddenName = textName.replace('_0', '_1');
+ var hiddenSelector = 'input[type=hidden][data-selectable-type=hidden-multiple][name=' + hiddenName + ']';
+ if (allowMultiple) {
+ allowNew = false;
+ $(input).val("");
+ this._initDeck(hiddenSelector);
+ }
+
+ function dataSource(request, response) {
+ var url = data.selectableUrl || data['selectable-url'];
+ var now = new Date().getTime();
+ var query = {term: request.term, timestamp: now};
+ if (self.options.prepareQuery) {
+ self.options.prepareQuery(query);
+ }
+ var page = $(input).data("page");
+ if (page) {
+ query.page = page;
+ }
+ $.getJSON(url, query, response);
+ }
+
+ $(input).autocomplete({
+ source: dataSource,
+ change: function(event, ui) {
+ $(input).removeClass('ui-state-error');
+ if ($(input).val() && !ui.item) {
+ if (!allowNew) {
+ $(input).addClass('ui-state-error');
+ }
+ }
+ if (allowMultiple && !$(input).hasClass('ui-state-error')) {
+ $(input).val("");
+ $(input).data("autocomplete").term = "";
+ }
+ },
+ select: function(event, ui) {
+ $(input).removeClass('ui-state-error');
+ if (ui.item && ui.item.page) {
+ $(input).data("page", ui.item.page);
+ $('.selectable-paginator', self.menu).remove();
+ $(input).autocomplete("search");
+ return false;
+ }
+ if (ui.item && allowMultiple) {
+ $(input).val("");
+ $(input).data("autocomplete").term = "";
+ if ($(hiddenSelector + '[value=' + ui.item.id + ']').length === 0) {
+ var newInput = $('<input />', {
+ 'type': 'hidden',
+ 'name': hiddenName,
+ 'value': ui.item.id,
+ 'title': ui.item.value,
+ 'data-selectable-type': 'hidden-multiple'
+ });
+ $(input).after(newInput);
+ self._addDeckItem(newInput);
+ return false;
+ }
+ }
+ }
+ }).addClass("ui-widget ui-widget-content ui-corner-all");
+ $(input).data("autocomplete")._renderItem = function(ul, item) {
+ var li = $("<li></li>")
+ .data("item.autocomplete", item)
+ .append($("<a></a>").text(item.label))
+ .appendTo(ul);
+ if (item.page) {
+ li.addClass('selectable-paginator');
+ }
+ return li;
+ };
+ $(input).data("autocomplete")._suggest = function(items) {
+ var page = $(input).data('page');
+ var ul = this.menu.element;
+ if (!page) {
+ ul.empty();
+ }
+ $(input).data('page', null);
+ ul.zIndex(this.element.zIndex() + 1);
+ this._renderMenu(ul, items);
+ 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"));
+ }
+ };
+ var selectableType = data.selectableType || data['selectable-type'];
+ if (selectableType === 'combobox') {
+ // Change auto-complete options
+ $(input).autocomplete("option", {
+ delay: 0,
+ minLength: 0
+ })
+ .removeClass("ui-corner-all")
+ .addClass("ui-corner-left ui-combo-input");
+
+ $("<button> </button>").attr("tabIndex", -1).attr("title", "Show All Items")
+ .insertAfter($(input))
+ .button({
+ icons: {
+ primary: self.options.comboboxIcon
+ },
+ text: false
+ })
+ .removeClass("ui-corner-all")
+ .addClass("ui-corner-right ui-button-icon ui-combo-button")
+ .click(function() {
+ // close if already visible
+ if ($(input).autocomplete("widget").is(":visible")) {
+ $(input).autocomplete("close");
+ return false;
+ }
+
+ // pass empty string as value to search for, displaying all results
+ $(input).autocomplete("search", "");
+ $(input).focus();
+ return false;
+ });
+ }
+ }
+ });
+})(jQuery);
+
+function bindSelectables(context) {
+ $(":input[data-selectable-type=text]", context).djselectable();
+ $(":input[data-selectable-type=combobox]", context).djselectable();
+ $(":input[data-selectable-type=hidden]", context).each(function(i, elem) {
+ var hiddenName = $(elem).attr('name');
+ var textName = hiddenName.replace('_1', '_0');
+ $(":input[name=" + textName + "][data-selectable-url]").bind(
+ "autocompletechange autocompleteselect",
+ function(event, ui) {
+ if (ui.item && ui.item.id) {
+ $(elem).val(ui.item.id);
+ } else {
+ $(elem).val("");
+ }
+ }
+ );
+ });
+}
+
+if (typeof(django) != "undefined" && typeof(django.jQuery) != "undefined") {
+ if (django.jQuery.fn.formset) {
+ var oldformset = django.jQuery.fn.formset;
+ django.jQuery.fn.formset = function(opts) {
+ var options = $.extend({}, opts);
+ var addedevent = function(row) {
+ bindSelectables($(row));
+ };
+ var added = null;
+ if (options.added) {
+ var oldadded = options.added;
+ added = function(row) { oldadded(row); addedevent(row); };
+ }
+ options.added = added || addedevent;
+ return oldformset.call(this, options);
+ };
+ }
+}
+
+$(document).ready(function() {
+ bindSelectables('body');
+});