123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- /**
- * @file:
- * Converts textfield to a autocomplete deluxe widget.
- */
- (function($) {
- Drupal.autocomplete_deluxe = Drupal.autocomplete_deluxe || {};
- Drupal.behaviors.autocomplete_deluxe = {
- attach: function(context) {
- var autocomplete_settings = Drupal.settings.autocomplete_deluxe;
- $('input.autocomplete-deluxe-form').once( function() {
- if (autocomplete_settings[$(this).attr('id')].multiple === true) {
- new Drupal.autocomplete_deluxe.MultipleWidget(this, autocomplete_settings[$(this).attr('id')]);
- } else {
- new Drupal.autocomplete_deluxe.SingleWidget(autocomplete_settings[$(this).attr('id')]);
- }
- });
- }
- };
- /**
- * Autogrow plugin which auto resizes the input of the multiple value.
- *
- * http://stackoverflow.com/questions/931207/is-there-a-jquery-autogrow-plugin-for-text-fields
- *
- */
- $.fn.autoGrowInput = function(o) {
- o = $.extend({
- maxWidth: 1000,
- minWidth: 0,
- comfortZone: 70
- }, o);
- this.filter('input:text').each(function(){
- var minWidth = o.minWidth || $(this).width(),
- val = '',
- input = $(this),
- testSubject = $('<tester/>').css({
- position: 'absolute',
- top: -9999,
- left: -9999,
- width: 'auto',
- fontSize: input.css('fontSize'),
- fontFamily: input.css('fontFamily'),
- fontWeight: input.css('fontWeight'),
- letterSpacing: input.css('letterSpacing'),
- whiteSpace: 'nowrap'
- }),
- check = function() {
- if (val === (val = input.val())) {return;}
- // Enter new content into testSubject
- var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>');
- testSubject.html(escaped);
- // Calculate new width + whether to change
- var testerWidth = testSubject.width(),
- newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth,
- currentWidth = input.width(),
- isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
- || (newWidth > minWidth && newWidth < o.maxWidth);
- // Animate width
- if (isValidWidthChange) {
- input.width(newWidth);
- }
- };
- testSubject.insertAfter(input);
- $(this).bind('keyup keydown blur update', check);
- });
- return this;
- };
- /**
- * Unescapes the given string.
- */
- Drupal.autocomplete_deluxe.unescape = function (input) {
- // Unescaping is done via a textarea, since the text inside of it is never
- // executed. This method also allows us to support older browsers like
- // IE 9 and below.
- var textArea = document.createElement('textarea');
- textArea.innerHTML = input;
- var decoded = textArea.value;
- if ('remove' in Element.prototype) {
- textArea.remove();
- }
- return decoded;
- };
- /**
- * If there is no result this label will be shown.
- * @type {{label: string, value: string}}
- */
- Drupal.autocomplete_deluxe.empty = {label: '- ' + Drupal.t('None') + ' -', value: "" };
- /**
- * EscapeRegex function from jquery autocomplete, is not included in drupal.
- */
- Drupal.autocomplete_deluxe.escapeRegex = function(value) {
- return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/gi, "\\$&");
- };
- /**
- * Filter function from jquery autocomplete, is not included in drupal.
- */
- Drupal.autocomplete_deluxe.filter = function(array, term) {
- var matcher = new RegExp(Drupal.autocomplete_deluxe.escapeRegex(term), "i");
- return $.grep(array, function(value) {
- return matcher.test(value.label || value.value || value);
- });
- };
- Drupal.autocomplete_deluxe.Widget = function() {
- };
- /**
- * Url for the callback.
- */
- Drupal.autocomplete_deluxe.Widget.prototype.uri = null;
- /**
- * Allows widgets to filter terms.
- * @param term
- * A term that should be accepted or not.
- * @return {Boolean}
- * True if the term should be accepted.
- */
- Drupal.autocomplete_deluxe.Widget.prototype.acceptTerm = function(term) {
- return true;
- };
- Drupal.autocomplete_deluxe.Widget.prototype.init = function(settings) {
- if(navigator.appVersion.indexOf("MSIE 6.") != -1) {
- return;
- }
- this.id = settings.input_id;
- this.jqObject = $('#' + this.id);
- this.uri = settings.uri;
- this.multiple = settings.multiple;
- this.required = settings.required;
- this.limit = settings.limit;
- this.synonyms = typeof settings.use_synonyms == 'undefined' ? false : settings.use_synonyms;
- this.not_found_message = typeof settings.use_synonyms == 'undefined' ? "The term '@term' will be added." : settings.not_found_message;
- this.wrapper = '""';
- if (typeof settings.delimiter == 'undefined') {
- this.delimiter = true;
- } else {
- this.delimiter = settings.delimiter.charCodeAt(0);
- }
- this.items = {};
- var self = this;
- var parent = this.jqObject.parent();
- var parents_parent = this.jqObject.parent().parent();
- parents_parent.append(this.jqObject);
- parent.remove();
- parent = parents_parent;
- var generateValues = function(data, term) {
- var result = new Array();
- for (var terms in data) {
- if (self.acceptTerm(terms)) {
- result.push({
- label: data[terms],
- value: terms
- });
- }
- }
- if ($.isEmptyObject(result)) {
- result.push({
- label: Drupal.t(self.not_found_message, {'@term' : term}),
- value: term,
- newTerm: true
- });
- }
- return result;
- };
- var cache = {};
- var lastXhr = null;
- this.source = function(request, response) {
- var term = request.term;
- if (term in cache) {
- response(generateValues(cache[term], term));
- return;
- }
- // Some server collapse two slashes if the term is empty, so insert at
- // least a whitespace. This whitespace will later on be trimmed in the
- // autocomplete callback.
- if (!term) {
- term = " ";
- }
- request.synonyms = self.synonyms;
- var url = settings.uri + '/' + term +'/' + self.limit;
- lastXhr = $.getJSON(url, request, function(data, status, xhr) {
- cache[term] = data;
- if (xhr === lastXhr) {
- response(generateValues(data, term));
- }
- });
- };
- this.jqObject.autocomplete({
- 'source' : this.source,
- 'minLength': settings.min_length
- });
- var jqObject = this.jqObject;
- var autocompleteDataKey = typeof(this.jqObject.data('autocomplete')) === 'object' ? 'item.autocomplete' : 'ui-autocomplete';
- var throbber = $('<div class="autocomplete-deluxe-throbber autocomplete-deluxe-closed"> </div>').insertAfter(jqObject);
- this.jqObject.bind("autocompletesearch", function(event, ui) {
- throbber.removeClass('autocomplete-deluxe-closed');
- throbber.addClass('autocomplete-deluxe-open');
- });
- this.jqObject.bind("autocompleteopen", function(event, ui) {
- throbber.addClass('autocomplete-deluxe-closed');
- throbber.removeClass('autocomplete-deluxe-open');
- });
- // Monkey patch the _renderItem function jquery so we can highlight the
- // text, that we already entered.
- $.ui.autocomplete.prototype._renderItem = function( ul, item) {
- var t = item.label;
- if (this.term != "") {
- var escapedValue = Drupal.autocomplete_deluxe.escapeRegex( this.term );
- var re = new RegExp('()*""' + escapedValue + '""|' + escapedValue + '()*', 'gi');
- var t = item.label.replace(re,"<span class='autocomplete-deluxe-highlight-char'>$&</span>");
- }
- return $( "<li></li>" )
- .data(autocompleteDataKey, item)
- .append( "<a>" + t + "</a>" )
- .appendTo( ul );
- };
- };
- Drupal.autocomplete_deluxe.Widget.prototype.generateValues = function(data) {
- var result = new Array();
- for (var index in data) {
- result.push(data[index]);
- }
- return result;
- };
- /**
- * Generates a single selecting widget.
- */
- Drupal.autocomplete_deluxe.SingleWidget = function(settings) {
- this.init(settings);
- this.setup();
- this.jqObject.addClass('autocomplete-deluxe-form-single');
- };
- Drupal.autocomplete_deluxe.SingleWidget.prototype = new Drupal.autocomplete_deluxe.Widget();
- Drupal.autocomplete_deluxe.SingleWidget.prototype.setup = function() {
- var jqObject = this.jqObject;
- var parent = jqObject.parent();
- parent.mousedown(function() {
- if (parent.hasClass('autocomplete-deluxe-single-open')) {
- jqObject.autocomplete('close');
- } else {
- jqObject.autocomplete('search', '');
- }
- });
- };
- /**
- * Creates a multiple selecting widget.
- */
- Drupal.autocomplete_deluxe.MultipleWidget = function(input, settings) {
- this.init(settings);
- this.setup();
- };
- Drupal.autocomplete_deluxe.MultipleWidget.prototype = new Drupal.autocomplete_deluxe.Widget();
- Drupal.autocomplete_deluxe.MultipleWidget.prototype.items = new Object();
- Drupal.autocomplete_deluxe.MultipleWidget.prototype.acceptTerm = function(term) {
- // Accept only terms, that are not in our items list.
- return !(term in this.items);
- };
- Drupal.autocomplete_deluxe.MultipleWidget.Item = function (widget, item) {
- if (item.newTerm === true) {
- item.label = item.value;
- }
- this.value = item.value;
- this.element = $('<span class="autocomplete-deluxe-item"></span>');
- this.element.text(item.label);
- this.widget = widget;
- this.item = item;
- var self = this;
- var close = $('<a class="autocomplete-deluxe-item-delete" href="javascript:void(0)"></a>').appendTo(this.element);
- // Use single quotes because of the double quote encoded stuff.
- var input = $('<input type="hidden"/>')
- input.val(this.value);
- input.appendTo(this.element);
- close.mousedown(function() {
- self.remove(item);
- });
- };
- Drupal.autocomplete_deluxe.MultipleWidget.Item.prototype.remove = function() {
- this.element.remove();
- var values = this.widget.valueForm.val();
- var escapedValue = Drupal.autocomplete_deluxe.escapeRegex( this.item.value );
- var regex = new RegExp('()*""' + escapedValue + '""()*', 'gi');
- this.widget.valueForm.val(values.replace(regex, ''));
- delete this.widget.items[this.value];
- };
- Drupal.autocomplete_deluxe.MultipleWidget.prototype.setup = function() {
- var jqObject = this.jqObject;
- var parent = jqObject.parents('.autocomplete-deluxe-container');
- var value_container = parent.next();
- var value_input = value_container.find('input');
- var items = this.items;
- var self = this;
- this.valueForm = value_input;
- // Override the resize function, so that the suggestion list doesn't resizes
- // all the time.
- var autocompleteDataKey = typeof(this.jqObject.data('autocomplete')) === 'object' ? 'autocomplete' : 'ui-autocomplete';
- jqObject.data(autocompleteDataKey)._resizeMenu = function() {};
- jqObject.show();
- value_container.hide();
- // Add the default values to the box.
- var default_values = value_input.val();
- default_values = $.trim(default_values);
- default_values = default_values.substr(2, default_values.length-4);
- default_values = default_values.split('"" ""');
- for (var index in default_values) {
- var value = default_values[index];
- if (value != '') {
- // If a terms is encoded in double quotes, then the label should have
- // no double quotes.
- var label = value.match(/["][\w|\s|\D|]*["]/gi) !== null ? value.substr(1, value.length-2) : value;
- var item = {
- label : label,
- value : value
- };
- var item = new Drupal.autocomplete_deluxe.MultipleWidget.Item(self, item);
- item.element.insertBefore(jqObject);
- items[item.value] = item;
- }
- }
- jqObject.addClass('autocomplete-deluxe-multiple');
- parent.addClass('autocomplete-deluxe-multiple');
- // Adds a value to the list.
- this.addValue = function(ui_item) {
- var item = new Drupal.autocomplete_deluxe.MultipleWidget.Item(self, ui_item);
- item.element.insertBefore(jqObject);
- items[ui_item.value] = item;
- var new_value = ' ' + self.wrapper + ui_item.value + self.wrapper;
- var values = value_input.val();
- value_input.val(values + new_value);
- jqObject.val('');
- };
- parent.mouseup(function() {
- jqObject.autocomplete('search', '');
- jqObject.focus();
- });
- jqObject.bind("autocompleteselect", function(event, ui) {
- // JQuery ui autocomplete needs the terms escaped, otherwise it would be
- // open to XSS issues. Drupal.autocomplete.Item also escapes on rendering
- // the DOM elements. Thus we have to unescape the label here before adding
- // the new item.
- var item = ui.item;
- item.label = Drupal.autocomplete_deluxe.unescape(item.label);
- self.addValue(item);
- jqObject.width(25);
- // Return false to prevent setting the last term as value for the jqObject.
- return false;
- });
- jqObject.bind("autocompletechange", function(event, ui) {
- jqObject.val('');
- });
- jqObject.blur(function() {
- var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
- last_element.removeClass('autocomplete-deluxe-item-focus');
- });
- var clear = false;
- jqObject.keypress(function (event) {
- var value = jqObject.val();
- // If a comma was entered and there is none or more then one comma,or the
- // enter key was entered, then enter the new term.
- if ((event.which == self.delimiter && (value.split('"').length - 1) != 1) || (event.which == 13 && jqObject.val() != "")) {
- value = value.substr(0, value.length);
- if (typeof self.items[value] == 'undefined' && value != '') {
- var ui_item = {
- label: value,
- value: value
- };
- self.addValue(ui_item);
- }
- clear = true;
- if (event.which == 13) {
- return false;
- }
- }
- // If the Backspace key was hit and the input is empty
- if (event.which == 8 && value == '') {
- var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
- // then mark the last item for deletion or deleted it if already marked.
- if (last_element.hasClass('autocomplete-deluxe-item-focus')) {
- var value = last_element.children('input').val();
- self.items[value].remove(self.items[value]);
- jqObject.autocomplete('search', '');
- } else {
- last_element.addClass('autocomplete-deluxe-item-focus');
- }
- } else {
- // Remove the focus class if any other key was hit.
- var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
- last_element.removeClass('autocomplete-deluxe-item-focus');
- }
- });
- jqObject.autoGrowInput({
- comfortZone: 50,
- minWidth: 10,
- maxWidth: 460
- });
- jqObject.keyup(function (event) {
- if (clear) {
- // Trigger the search, so it display the values for an empty string.
- jqObject.autocomplete('search', '');
- jqObject.val('');
- clear = false;
- // Return false to prevent entering the last character.
- return false;
- }
- });
- };
- })(jQuery);
|