| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ; |
| $.browser = navigator.userAgent; |
| (function($) { |
| $.fn.extend({ |
| autocomplete: function(urlOrData, options) { |
| var isUrl = typeof urlOrData == "string"; |
| options = $.extend({}, $.Autocompleter.defaults, { |
| url: isUrl ? urlOrData : null, |
| data: isUrl ? null : urlOrData, |
| delay: isUrl ? $.Autocompleter.defaults.delay : 10, |
| max: options && !options.scroll ? 10 : 150 |
| }, options); |
| options.highlight = options.highlight || |
| function(value) { |
| return value; |
| }; |
| options.formatMatch = options.formatMatch || options.formatItem; |
| return this.each(function() { |
| new $.Autocompleter(this, options); |
| }); |
| }, |
| result: function(handler) { |
| return this.bind("result", handler); |
| }, |
| search: function(handler) { |
| return this.trigger("search", [handler]); |
| }, |
| flushCache: function() { |
| return this.trigger("flushCache"); |
| }, |
| setOptions: function(options) { |
| return this.trigger("setOptions", [options]); |
| }, |
| unautocomplete: function() { |
| return this.trigger("unautocomplete"); |
| } |
| }); |
| $.Autocompleter = function(input, options) { |
| var KEY = { |
| UP: 38, |
| DOWN: 40, |
| DEL: 46, |
| TAB: 9, |
| RETURN: 13, |
| ESC: 27, |
| COMMA: 188, |
| PAGEUP: 33, |
| PAGEDOWN: 34, |
| BACKSPACE: 8 |
| }; |
| var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); |
| var timeout; |
| var previousValue = ""; |
| var cache = $.Autocompleter.Cache(options); |
| var hasFocus = 0; |
| var lastKeyPressCode; |
| var config = { |
| mouseDownOnSelect: false |
| }; |
| var select = $.Autocompleter.Select(options, input, selectCurrent, config); |
| var blockSubmit; |
| $.browser.opera && $(input.form).bind("submit.autocomplete", function() { |
| if (blockSubmit) { |
| blockSubmit = false; |
| return false; |
| } |
| }); |
| $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { |
| hasFocus = 1; |
| lastKeyPressCode = event.keyCode; |
| switch (event.keyCode) { |
| case KEY.UP: |
| event.preventDefault(); |
| if (select.visible()) { |
| select.prev(); |
| } else { |
| onChange(0, true); |
| } |
| break; |
| case KEY.DOWN: |
| event.preventDefault(); |
| if (select.visible()) { |
| select.next(); |
| } else { |
| onChange(0, true); |
| } |
| break; |
| case KEY.PAGEUP: |
| event.preventDefault(); |
| if (select.visible()) { |
| select.pageUp(); |
| } else { |
| onChange(0, true); |
| } |
| break; |
| case KEY.PAGEDOWN: |
| event.preventDefault(); |
| if (select.visible()) { |
| select.pageDown(); |
| } else { |
| onChange(0, true); |
| } |
| break; |
| case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: |
| case KEY.TAB: |
| case KEY.RETURN: |
| if (selectCurrent()) { |
| event.preventDefault(); |
| blockSubmit = true; |
| return false; |
| } |
| break; |
| case KEY.ESC: |
| select.hide(); |
| break; |
| default: |
| clearTimeout(timeout); |
| timeout = setTimeout(onChange, options.delay); |
| break; |
| } |
| }).focus(function() { |
| hasFocus++; |
| }).blur(function() { |
| hasFocus = 0; |
| if (!config.mouseDownOnSelect) { |
| hideResults(); |
| } |
| }).click(function() { |
| if (hasFocus++ > 1 && !select.visible()) { |
| onChange(0, true); |
| } |
| }).bind("search", function() { |
| var fn = (arguments.length > 1) ? arguments[1] : null; |
|
|
| function findValueCallback(q, data) { |
| var result; |
| if (data && data.length) { |
| for (var i = 0; i < data.length; i++) { |
| if (data[i].result.toLowerCase() == q.toLowerCase()) { |
| result = data[i]; |
| break; |
| } |
| } |
| } |
| if (typeof fn == "function") fn(result); |
| else $input.trigger("result", result && [result.data, result.value]); |
| } |
| $.each(trimWords($input.val()), function(i, value) { |
| request(value, findValueCallback, findValueCallback); |
| }); |
| }).bind("flushCache", function() { |
| cache.flush(); |
| }).bind("setOptions", function() { |
| $.extend(options, arguments[1]); |
| if ("data" in arguments[1]) cache.populate(); |
| }).bind("unautocomplete", function() { |
| select.unbind(); |
| $input.unbind(); |
| $(input.form).unbind(".autocomplete"); |
| }); |
|
|
| function selectCurrent() { |
| var selected = select.selected(); |
| if (!selected) return false; |
| var v = selected.result; |
| previousValue = v; |
| if (options.multiple) { |
| var words = trimWords($input.val()); |
| if (words.length > 1) { |
| var seperator = options.multipleSeparator.length; |
| var cursorAt = $(input).selection().start; |
| var wordAt, progress = 0; |
| $.each(words, function(i, word) { |
| progress += word.length; |
| if (cursorAt <= progress) { |
| wordAt = i; |
| return false; |
| } |
| progress += seperator; |
| }); |
| words[wordAt] = v; |
| v = words.join(options.multipleSeparator); |
| } |
| v += options.multipleSeparator; |
| } |
| $input.val(v); |
| hideResultsNow(); |
| $input.trigger("result", [selected.data, selected.value]); |
| return true; |
| } |
| function onChange(crap, skipPrevCheck) { |
| if (lastKeyPressCode == KEY.DEL) { |
| select.hide(); |
| return; |
| } |
| var currentValue = $input.val(); |
| if (!skipPrevCheck && currentValue == previousValue) return; |
| previousValue = currentValue; |
| currentValue = lastWord(currentValue); |
| if (currentValue.length >= options.minChars) { |
| $input.addClass(options.loadingClass); |
| if (!options.matchCase) currentValue = currentValue.toLowerCase(); |
| request(currentValue, receiveData, hideResultsNow); |
| } else { |
| stopLoading(); |
| select.hide(); |
| } |
| }; |
|
|
| function trimWords(value) { |
| if (!value) return [""]; |
| if (!options.multiple) return [$.trim(value)]; |
| return $.map(value.split(options.multipleSeparator), function(word) { |
| return $.trim(value).length ? $.trim(word) : null; |
| }); |
| } |
| function lastWord(value) { |
| if (!options.multiple) return value; |
| var words = trimWords(value); |
| if (words.length == 1) return words[0]; |
| var cursorAt = $(input).selection().start; |
| if (cursorAt == value.length) { |
| words = trimWords(value) |
| } else { |
| words = trimWords(value.replace(value.substring(cursorAt), "")); |
| } |
| return words[words.length - 1]; |
| } |
| function autoFill(q, sValue) { |
| if (options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE) { |
| $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); |
| $(input).selection(previousValue.length, previousValue.length + sValue.length); |
| } |
| }; |
|
|
| function hideResults() { |
| clearTimeout(timeout); |
| timeout = setTimeout(hideResultsNow, 200); |
| }; |
|
|
| function hideResultsNow() { |
| var wasVisible = select.visible(); |
| select.hide(); |
| clearTimeout(timeout); |
| stopLoading(); |
| if (options.mustMatch) { |
| $input.search(function(result) { |
| if (!result) { |
| if (options.multiple) { |
| var words = trimWords($input.val()).slice(0, -1); |
| $input.val(words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "")); |
| } else { |
| $input.val(""); |
| $input.trigger("result", null); |
| } |
| } |
| }); |
| } |
| }; |
|
|
| function receiveData(q, data) { |
| if (data && data.length && hasFocus) { |
| stopLoading(); |
| select.display(data, q); |
| autoFill(q, data[0].value); |
| select.show(); |
| } else { |
| hideResultsNow(); |
| } |
| }; |
|
|
| function request(term, success, failure) { |
| if (!options.matchCase) term = term.toLowerCase(); |
| var data = cache.load(term); |
| if (data && data.length) { |
| success(term, data); |
| } else if ((typeof options.url == "string") && (options.url.length > 0)) { |
| var extraParams = { |
| timestamp: +new Date() |
| }; |
| $.each(options.extraParams, function(key, param) { |
| extraParams[key] = typeof param == "function" ? param() : param; |
| }); |
| $.ajax({ |
| mode: "abort", |
| port: "autocomplete" + input.name, |
| dataType: options.dataType, |
| url: options.url, |
| data: $.extend({ |
| wd: lastWord(term), |
| limit: options.max |
| }, extraParams), |
| success: function(data) { |
| var parsed = options.parse && options.parse(data) || parse(data); |
| cache.add(term, parsed); |
| success(term, parsed); |
| } |
| }); |
| } else { |
| select.emptyList(); |
| failure(term); |
| } |
| }; |
|
|
| function parse(data) { |
| var parsed = []; |
| var rows = data.split("\n"); |
| for (var i = 0; i < rows.length; i++) { |
| var row = $.trim(rows[i]); |
| if (row) { |
| row = row.split("|"); |
| parsed[parsed.length] = { |
| data: row, |
| value: row[0], |
| result: options.formatResult && options.formatResult(row, row[0]) || row[0] |
| }; |
| } |
| } |
| return parsed; |
| }; |
|
|
| function stopLoading() { |
| $input.removeClass(options.loadingClass); |
| }; |
| }; |
| $.Autocompleter.defaults = { |
| inputClass: "ac_input", |
| resultsClass: "ac_results", |
| loadingClass: "ac_loading", |
| minChars: 1, |
| delay: 400, |
| matchCase: false, |
| matchSubset: false, |
| matchContains: false, |
| cacheLength: 10, |
| max: 100, |
| mustMatch: false, |
| extraParams: {}, |
| selectFirst: true, |
| formatItem: function(row) { |
| return row[0]; |
| }, |
| formatMatch: null, |
| autoFill: false, |
| width: 0, |
| multiple: false, |
| multipleSeparator: ", ", |
| highlight: function(value, term) { |
| return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); |
| }, |
| scroll: true, |
| scrollHeight: 180 |
| }; |
| $.Autocompleter.Cache = function(options) { |
| var data = {}; |
| var length = 0; |
|
|
| function matchSubset(s, sub) { |
| if (!options.matchCase) s = s.toLowerCase(); |
| var i = s.indexOf(sub); |
| if (options.matchContains == "word") { |
| i = s.toLowerCase().search("\\b" + sub.toLowerCase()); |
| } |
| if (i == -1) return false; |
| return i == 0 || options.matchContains; |
| }; |
|
|
| function add(q, value) { |
| if (length > options.cacheLength) { |
| flush(); |
| } |
| if (!data[q]) { |
| length++; |
| } |
| data[q] = value; |
| } |
| function populate() { |
| if (!options.data) return false; |
| var stMatchSets = {}, |
| nullData = 0; |
| if (!options.url) options.cacheLength = 1; |
| stMatchSets[""] = []; |
| for (var i = 0, ol = options.data.length; i < ol; i++) { |
| var rawValue = options.data[i]; |
| rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; |
| var value = options.formatMatch(rawValue, i + 1, options.data.length); |
| if (value === false) continue; |
| var firstChar = value.charAt(0).toLowerCase(); |
| if (!stMatchSets[firstChar]) stMatchSets[firstChar] = []; |
| var row = { |
| value: value, |
| data: rawValue, |
| result: options.formatResult && options.formatResult(rawValue) || value |
| }; |
| stMatchSets[firstChar].push(row); |
| if (nullData++ < options.max) { |
| stMatchSets[""].push(row); |
| } |
| }; |
| $.each(stMatchSets, function(i, value) { |
| options.cacheLength++; |
| add(i, value); |
| }); |
| } |
| setTimeout(populate, 25); |
|
|
| function flush() { |
| data = {}; |
| length = 0; |
| } |
| return { |
| flush: flush, |
| add: add, |
| populate: populate, |
| load: function(q) { |
| if (!options.cacheLength || !length) return null; |
| if (!options.url && options.matchContains) { |
| var csub = []; |
| for (var k in data) { |
| if (k.length > 0) { |
| var c = data[k]; |
| $.each(c, function(i, x) { |
| if (matchSubset(x.value, q)) { |
| csub.push(x); |
| } |
| }); |
| } |
| } |
| return csub; |
| } else if (data[q]) { |
| return data[q]; |
| } else if (options.matchSubset) { |
| for (var i = q.length - 1; i >= options.minChars; i--) { |
| var c = data[q.substr(0, i)]; |
| if (c) { |
| var csub = []; |
| $.each(c, function(i, x) { |
| if (matchSubset(x.value, q)) { |
| csub[csub.length] = x; |
| } |
| }); |
| return csub; |
| } |
| } |
| } |
| return null; |
| } |
| }; |
| }; |
| $.Autocompleter.Select = function(options, input, select, config) { |
| var CLASSES = { |
| ACTIVE: "ac_over" |
| }; |
| var listItems, active = -1, |
| data, term = "", |
| needsInit = true, |
| element, list; |
|
|
| function init() { |
| if (!needsInit) return; |
| element = $("<div/>").hide().addClass(options.resultsClass).css("position", "absolute").appendTo(document.body); |
| list = $("<ul/>").appendTo(element).mouseover(function(event) { |
| if (target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { |
| active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); |
| $(target(event)).addClass(CLASSES.ACTIVE); |
| } |
| }).click(function(event) { |
| $(target(event)).addClass(CLASSES.ACTIVE); |
| select(); |
| input.focus(); |
| return false; |
| }).mousedown(function() { |
| config.mouseDownOnSelect = true; |
| }).mouseup(function() { |
| config.mouseDownOnSelect = false; |
| }); |
| if (options.width > 0) element.css("width", options.width); |
| needsInit = false; |
| } |
| function target(event) { |
| var element = event.target; |
| while (element && element.tagName != "LI") element = element.parentNode; |
| if (!element) return []; |
| return element; |
| } |
| function moveSelect(step) { |
| listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); |
| movePosition(step); |
| var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); |
| if (options.scroll) { |
| var offset = 0; |
| listItems.slice(0, active).each(function() { |
| offset += this.offsetHeight; |
| }); |
| if ((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { |
| list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); |
| } else if (offset < list.scrollTop()) { |
| list.scrollTop(offset); |
| } |
| } |
| }; |
|
|
| function movePosition(step) { |
| active += step; |
| if (active < 0) { |
| active = listItems.size() - 1; |
| } else if (active >= listItems.size()) { |
| active = 0; |
| } |
| } |
| function limitNumberOfItems(available) { |
| return options.max && options.max < available ? options.max : available; |
| } |
| function fillList() { |
| list.empty(); |
| var max = limitNumberOfItems(data.length); |
| for (var i = 0; i < max; i++) { |
| if (!data[i]) continue; |
| var formatted = options.formatItem(data[i].data, i + 1, max, data[i].value, term); |
| if (formatted === false) continue; |
| var li = $("<li/>").html(options.highlight(formatted, term)).addClass(i % 2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; |
| $.data(li, "ac_data", data[i]); |
| } |
| listItems = list.find("li"); |
| if (options.selectFirst) { |
| listItems.slice(0, 1).addClass(CLASSES.ACTIVE); |
| active = 0; |
| } |
| if ($.fn.bgiframe) list.bgiframe(); |
| } |
| return { |
| display: function(d, q) { |
| init(); |
| data = d; |
| term = q; |
| fillList(); |
| }, |
| next: function() { |
| moveSelect(1); |
| }, |
| prev: function() { |
| moveSelect(-1); |
| }, |
| pageUp: function() { |
| if (active != 0 && active - 8 < 0) { |
| moveSelect(-active); |
| } else { |
| moveSelect(-8); |
| } |
| }, |
| pageDown: function() { |
| if (active != listItems.size() - 1 && active + 8 > listItems.size()) { |
| moveSelect(listItems.size() - 1 - active); |
| } else { |
| moveSelect(8); |
| } |
| }, |
| hide: function() { |
| element && element.hide(); |
| listItems && listItems.removeClass(CLASSES.ACTIVE); |
| active = -1; |
| }, |
| visible: function() { |
| return element && element.is(":visible"); |
| }, |
| current: function() { |
| return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); |
| }, |
| show: function() { |
| var offset = $(input).offset(); |
| element.css({ |
| width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), |
| top: offset.top + input.offsetHeight, |
| left: offset.left |
| }).show(); |
| if (options.scroll) { |
| list.scrollTop(0); |
| list.css({ |
| maxHeight: options.scrollHeight, |
| overflow: 'auto' |
| }); |
| if ($.browser.msie && typeof document.body.style.maxHeight === "undefined") { |
| var listHeight = 0; |
| listItems.each(function() { |
| listHeight += this.offsetHeight; |
| }); |
| var scrollbarsVisible = listHeight > options.scrollHeight; |
| list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight); |
| if (!scrollbarsVisible) { |
| listItems.width(list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right"))); |
| } |
| } |
| } |
| }, |
| selected: function() { |
| var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); |
| return selected && selected.length && $.data(selected[0], "ac_data"); |
| }, |
| emptyList: function() { |
| list && list.empty(); |
| }, |
| unbind: function() { |
| element && element.remove(); |
| } |
| }; |
| }; |
| $.fn.selection = function(start, end) { |
| if (start !== undefined) { |
| return this.each(function() { |
| if (this.createTextRange) { |
| var selRange = this.createTextRange(); |
| if (end === undefined || start == end) { |
| selRange.move("character", start); |
| selRange.select(); |
| } else { |
| selRange.collapse(true); |
| selRange.moveStart("character", start); |
| selRange.moveEnd("character", end); |
| selRange.select(); |
| } |
| } else if (this.setSelectionRange) { |
| this.setSelectionRange(start, end); |
| } else if (this.selectionStart) { |
| this.selectionStart = start; |
| this.selectionEnd = end; |
| } |
| }); |
| } |
| var field = this[0]; |
| if (field.createTextRange) { |
| var range = document.selection.createRange(), |
| orig = field.value, |
| teststring = "<->", |
| textLength = range.text.length; |
| range.text = teststring; |
| var caretAt = field.value.indexOf(teststring); |
| field.value = orig; |
| this.selection(caretAt, caretAt + textLength); |
| return { |
| start: caretAt, |
| end: caretAt + textLength |
| } |
| } else if (field.selectionStart !== undefined) { |
| return { |
| start: field.selectionStart, |
| end: field.selectionEnd |
| } |
| } |
| }; |
| })(jQuery); |