diff options
Diffstat (limited to 'scripts/autocomplete.js')
| -rw-r--r-- | scripts/autocomplete.js | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/scripts/autocomplete.js b/scripts/autocomplete.js new file mode 100644 index 00000000..24396ea5 --- /dev/null +++ b/scripts/autocomplete.js @@ -0,0 +1,233 @@ +/* + * autocompleteTrigger (based on jQuery-UI Autocomplete) + * https://github.com/experteer/autocompleteTrigger + * + * Copyright 2013, Experteer GmbH, Munich + * + * @version: 1.4 + * @author <a href="mailto:daniel.mattes@experteer.com">Daniel Mattes</a> + * + * @requires jQuery 1.6> and jQuery-Ui (including Autocomplete) 1.8> + * @requires https://github.com/accursoft/caret + * + * @description + * autocompleteTrigger allows you to specify a trigger (e. g. @ like twitter or facebook) and bind it to a textarea or input text field. + * If a user writes the trigger-sign into the textbox, the autocomplete dialog will displayed and the text after the trigger-sign + * will be used as search-query for the autocomplete options. If one of the suggested items will be selected, the value and trigger + * will be added to the textfield. + * + * Thanks to https://github.com/kof/fieldSelection (getCursorPosition) and + * http://stackoverflow.com/questions/4564378/jquery-autocomplete-plugin-that-triggers-after-token + * + * Dual licensed under MIT or GPLv2 licenses + * http://en.wikipedia.org/wiki/MIT_License + * http://en.wikipedia.org/wiki/GNU_General_Public_License + * + * @example + * $('input').autocompleteTrigger({ + * triggerStart : '@', + * triggerEnd: '', + * source: [ + "Asp", + "BASIC", + "COBOL", + "ColdFusion", + "Erlang", + "Fortran", + "Groovy", + "Java", + "JavaScript", + "Lisp", + "Perl", + "PHP", + "Python", + "Ruby", + "Scala", + "Scheme" + ] + * }); + */ + +; +(function ($, window, document, undefined) { + $.widget("ui.autocompleteTrigger", { + + //Options to be used as defaults + options: { + triggerStart: "%{", + triggerEnd: "}" + }, + + + _create: function () { + this.triggered = false; + + this.element.autocomplete($.extend({ + + search: function () { + /** + * @description only make a request and suggest items if acTrigger.triggered is true + */ + var acTrigger = $(this).data("autocompleteTrigger") || $(this).data("uiAutocompleteTrigger"); + + return acTrigger.triggered; + }, + select: function (event, ui) { + /** + * @description if a item is selected, insert the value between triggerStart and triggerEnd + */ + var acTrigger = $(this).data("autocompleteTrigger") || $(this).data("uiAutocompleteTrigger"); + var trigger = acTrigger.options.triggerStart; + var cursorPosition = acTrigger.getCursorPosition(); + + if ($(this).is('input,textarea')) { + var text = $(this).val(); + var lastTriggerPosition = text.substring(0, cursorPosition).lastIndexOf(trigger); + var firstTextPart = text.substring(0, lastTriggerPosition + trigger.length) + + ui.item.value + + acTrigger.options.triggerEnd; + $(this).val(firstTextPart + text.substring(cursorPosition, text.length)); + acTrigger.setCursorPosition(firstTextPart.length); + } else { + + var text = $(this).text(); + var html = $(this).html(); + + var searchTerm = text.substring(0, cursorPosition); + + var i = 0; + var index = 0; + while (i < searchTerm.length) { + index = html.lastIndexOf(searchTerm.substring(i)); + if (index != -1) { + break; + } + i++; + } +// console.log({html: html, index: index, searchTerm: searchTerm.substring(i) }) + + var htmlCursorPosition = index + searchTerm.substring(i).length; + var htmlLastTriggerPosition = html.substring(0, htmlCursorPosition).lastIndexOf(trigger); + var htmlFirstTextPart = html.substring(0, htmlLastTriggerPosition + trigger.length) + + ui.item.value + + acTrigger.options.triggerEnd; +// console.log({htmlCursorPosition: htmlCursorPosition, htmlLastTriggerPosition: htmlLastTriggerPosition, htmlFirstTextPart: htmlFirstTextPart }) + + // necessary to set cursor position + var lastTriggerPosition = text.substring(0, cursorPosition).lastIndexOf(trigger); + var firstTextPart = text.substring(0, lastTriggerPosition + trigger.length) + + ui.item.value + + acTrigger.options.triggerEnd; +// console.log({lastTriggerPosition: lastTriggerPosition, firstTextPart: firstTextPart, length: firstTextPart.length}) + + $(this).html(htmlFirstTextPart + html.substring(htmlCursorPosition, html.length)); + acTrigger.setCursorPosition(firstTextPart.length); + } + + acTrigger.triggered = false; + return false; + }, + focus: function () { + /** + * @description prevent to replace the hole text, if a item is hovered + */ + + return false; + }, + minLength: 0 + }, this.options)) + + .bind("keyup", function (event) { + /** + * @description Bind to keyup-events to detect text changes. + * If the trigger is found before the cursor, autocomplete will be called + */ + var widget = $(this); + var acTrigger = $(this).data("autocompleteTrigger") || $(this).data("uiAutocompleteTrigger"); + var delay = typeof acTrigger.options.delay === 'undefined' ? 0 : acTrigger.options.delay; + + if (event.keyCode != $.ui.keyCode.UP && event.keyCode != $.ui.keyCode.DOWN) { + + if ($(this).is('input,textarea')) { + var text = $(this).val(); + } else { + var text = $(this).text(); + } + + acTrigger.textValue = text; + if (typeof acTrigger.locked === 'undefined') { + acTrigger.locked = false; + } + + if (!acTrigger.locked) { + acTrigger.locked = true; + acTrigger.timeout = setTimeout(function () { + acTrigger.launchAutocomplete(acTrigger, widget); + }, delay); + } + } + + }); + }, + + /** + * @description Destroy an instantiated plugin and clean up modifications the widget has made to the DOM + */ + destroy: function () { + + // this.element.removeStuff(); + // For UI 1.8, destroy must be invoked from the + // base widget + $.Widget.prototype.destroy.call(this); + // For UI 1.9, define _destroy instead and don't + // worry about + // calling the base widget + }, + + /** + * @description calculates the the current cursor position in the bound textfield, area,... + * @returns {int} the position of the cursor. + */ + getCursorPosition: function () { + var elem = this.element[0]; + return jQuery(elem).caret(); + }, + + /** + * @description set the position of the cursor in a textfield, area,... + */ + setCursorPosition: function (position) { + var elem = this.element[0]; + return jQuery(elem).caret(position); + }, + + launchAutocomplete: function (acTrigger, widget) { + acTrigger.locked = false; + var text = acTrigger.textValue; + var textLength = text.length; + var cursorPosition = acTrigger.getCursorPosition(); + var lastString; + var query; + var lastTriggerPosition; + var trigger = acTrigger.options.triggerStart; + var triggerEnd = acTrigger.options.triggerEnd; + + lastTriggerPosition = text.substring(0, cursorPosition).lastIndexOf(trigger); + lastTriggerEndPosition = text.substring(0, cursorPosition).lastIndexOf(triggerEnd); + +// console.log('autocomplete: '+text+", lastTriggerPosition: "+lastTriggerPosition+", lastTriggerEndPosition: "+lastTriggerEndPosition + ", cursorPosition: "+cursorPosition); + if ((lastTriggerEndPosition < lastTriggerPosition || textLength >= trigger.length) && lastTriggerPosition != -1) { + query = text.substring(lastTriggerPosition + trigger.length, cursorPosition); +// console.log('query '+query); + acTrigger.triggered = true; + widget.autocomplete("search", query); + } else { + acTrigger.triggered = false; + widget.autocomplete("close"); + } + + } + + + }); +})(jQuery, window, document);
\ No newline at end of file |
