Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * jQuery Autocomplete plugin 1.1
 *
 * Copyright (c) 2009 Jörn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
 */;
(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({
                                                q : 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 : true,
                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);
                        }else{
                                options.width = 232;
                                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);