Example #1
0
    function (require) {
        var esui = require('esui');
        var lib = require('esui/lib');
        var u = require('underscore');
        var Layer = require('esui/Layer');
        var Extension = require('esui/Extension');
        var eoo = require('eoo');
        var CursorPositionHelper = require('./helper/CursorPositionHelper');
        var keyboard = require('esui/behavior/keyboard');
        require('esui/behavior/jquery-ui');

        var TEXT_LINE = 'TextLine';
        var TEXT_BOX = 'TextBox';
        var INPUT = 'input';
        var $ = require('jquery');

        /**
         * @class AutoCompleteLayer
         * @exports esui.Layer
         */
        var AutoCompleteLayer = eoo.create(
            Layer,
            {

                /**
                 * 自动提示层构造器
                 *
                 * @param {Object} [control] TextBox控件
                 */
                constructor: function (control) {
                    this.$super(arguments);
                    // 对于input单行,要求layer宽度不大于input宽度
                    var controlType = control.type;
                    if (controlType === TEXT_BOX) {
                        var ele = lib.g(control.inputId);
                        if (ele.tagName.toLowerCase() === INPUT) {
                            this.dock = {
                                strictWidth: true
                            };
                        }
                    }
                    this.initStructure();
                    this.initEvents();
                },

                type: 'AutoCompleteLayer',

                initStructure: function () {
                    var helper = this.control.helper;

                    this.addCustomClasses(
                        [
                            helper.getPrefixClass('autocomplete'),
                            helper.getPrefixClass('dropdown')
                        ]
                    );

                    var element = this.getElement();
                    $(this.control.main).after(element);
                },

                initEvents: function () {
                    var me = this;
                    var target = me.control;
                    var helper = target.helper;

                    var inputElement = this.inputElement = this.getInput();

                    var layerElement = me.getElement(false);
                    helper.addDOMEvent(
                        layerElement,
                        'click',
                        'li',
                        function (e) {
                            var clickedTarget = e.currentTarget;
                            me.hide();

                            var firstChild = $(clickedTarget.firstChild);
                            var text = firstChild.text();
                            var id = firstChild.closest('li').data('id');

                            // 事件参数
                            var args = {value: text};
                            // 如果选择项存在id属性,则在事件参数中加上
                            if (id !== undefined) {
                                args.id = id;
                            }
                            // 为了与 `select` 事件区分,用了 `targetselect`
                            var selectEvent = me.control.fire('targetselect', args);
                            if (selectEvent.isDefaultPrevented()) {
                                return;
                            }

                            /**
                             * @deprecated
                             */
                            var deprecatedEvent = me.control.fire('select', text);
                            if (deprecatedEvent.isDefaultPrevented()) {
                                return;
                            }

                            setTargetValue.call(me, text);
                        }
                    );

                    helper.addDOMEvent(
                        inputElement,
                        'keydown',
                        function (e) {
                            if (me.isHidden()) {
                                return;
                            }

                            switch (e.keyCode) {
                                // up
                                case keyboard.UP:
                                    e.preventDefault();
                                    moveTo.call(me, 'up');
                                    break;
                                // down
                                case keyboard.DOWN:
                                    e.preventDefault();
                                    moveTo.call(me, 'down');
                                    break;
                                // esc
                                case keyboard.ESC:
                                    me.hide();
                                    break;
                                // enter
                                case keyboard.RETURN:
                                    e.preventDefault();
                                    var selectedItem = me.getSelectedItem();
                                    if (!selectedItem) {
                                        return;
                                    }
                                    me.hide();

                                    var firstChild = $(selectedItem.firstChild);
                                    var text = firstChild.text();
                                    var id = firstChild.closest('li').data('id');

                                    // 事件参数
                                    var args = {value: text};
                                    // 如果选择项存在id属性,则在事件参数中加上
                                    if (id !== undefined) {
                                        args.id = id;
                                    }
                                    // 为了与 `select` 事件区分,用了 `targetselect`
                                    var selectEvent = me.control.fire('targetselect', args);
                                    if (selectEvent.isDefaultPrevented()) {
                                        return;
                                    }

                                    /**
                                     * @deprecated
                                     */
                                    var deprecatedEvent = me.control.fire('select', text);
                                    if (deprecatedEvent.isDefaultPrevented()) {
                                        return;
                                    }

                                    setTimeout(
                                        function () {
                                            setTargetValue.call(me, text);
                                        },
                                        0
                                    );
                                    break;
                            }
                        }
                    );

                    this.control.on('input', onInput);

                    /**
                     * 用户输入时触发,根据输入下拉提示
                     *
                     * @param {Event} event 事件对象
                     */
                    function onInput(event) {
                        var elementValue = inputElement.value;

                        // 空格或逗号结尾都忽略
                        if (!elementValue || /(?:\s|\,)$/.test(elementValue)) {
                            repaintSuggest.call(me, '');
                            me.hide();
                            return;
                        }

                        if (u.isFunction(target.extractWord)) {
                            elementValue = target.extractWord(elementValue);
                        }
                        else {
                            elementValue = extractMatchingWord(elementValue);
                        }

                        if (!elementValue) {
                            return;
                        }

                        if (target.search && target.search(elementValue) === false) {
                            return;
                        }

                        repaintSuggest.call(me, elementValue);
                    }

                },

                repaint: function (value) {
                    var element = this.getElement(false);
                    if (element) {
                        this.render(element, value);
                    }
                },

                render: function (element, value) {
                    if (value != null) {
                        element.innerHTML = value;
                    }
                },

                getSelectedItemIndex: function () {
                    var element = this.getElement(false);
                    var items = element.children;
                    var selectedItemIndex = -1;
                    for (var i = 0, len = items.length; i < len; i++) {
                        if ($(items[i]).hasClass(
                            this.control.helper.getPrefixClass('autocomplete-item-hover')
                        )) {
                            selectedItemIndex = i;
                            break;
                        }
                    }
                    return selectedItemIndex;
                },

                getSelectedItem: function () {
                    var element = this.getElement(false);
                    var selectedItem;
                    var selectedItemIndex = this.getSelectedItemIndex();
                    if (selectedItemIndex !== -1) {
                        selectedItem = element.children[selectedItemIndex];
                    }
                    return selectedItem;
                },

                /**
                 * @override
                 * 对textarea自行实现position
                 */
                position: function () {
                    var input = this.inputElement;
                    if (input.nodeName.toLowerCase() !== 'textarea') {
                        this.$super(arguments);
                    }
                },

                show: function () {
                    this.$super(arguments);
                    var input = this.inputElement;
                    var $ele = $(this.getElement(false));
                    if (input.nodeName.toLowerCase() === 'textarea') {
                        var cursorInstance = CursorPositionHelper.getInstance(input);
                        var pos = cursorInstance.getCaretPosition();
                        $ele.position(
                            {
                                of: input,
                                at: 'left+' + pos.left + ' top+' + pos.top,
                                my: 'left top'
                            }
                        );
                    }
                },

                isHidden: function () {
                    var element = this.getElement();
                    return $(element).is(':hidden');
                },

                /**
                 * 获取内部输入元素
                 *
                 * @return {Element}
                 */
                getInput: function () {
                    var control = this.control;
                    if (control.type === TEXT_BOX) {
                        return lib.g(control.inputId);
                    }
                    else if (control.type === TEXT_LINE) {
                        return control.getTextArea();
                    }
                    return null;
                },

                nodeName: 'ol'
            }
        );

        /**
         * 匹配已输入值的算法
         *
         * @param {string} value 当前用户输入
         * @param {Array} datasource 数据源
         * @return {Array}
         */
        function filter(value, datasource) {
            return u.filter(
                datasource,
                function (data) {
                    var text = u.isObject(data) ? data.text : data;
                    return (new RegExp(escapeRegex(value), 'i')).test(text);
                }
            );
        }

        /**
         * 特殊字符处理,这些字符排除在匹配算法外
         *
         * @param {string} value 用户输入
         * @return {string}
         */
        function escapeRegex(value) {
            return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
        }

        /**
         * 根据用户输入绘制下拉选择列表
         *
         * @param {sttring} value 用户输入
         */
        function repaintSuggest(value) {
            if (!value) {
                renderSuggest.call(this);
                return;
            }
            var me = this;
            var datasource = this.control.datasource;
            if (typeof datasource === 'function') {
                datasource.call(
                    this,
                    value,
                    function (data) {
                        renderSuggest.call(me, data, value);
                    }
                );
            }
            else if (datasource && datasource.length) {
                renderSuggest.call(me, filter(value, datasource), value);
            }
        }

        function renderSuggest(data, inputValue) {

            var helper = this.control.helper;

            /**
             * 将text中匹配到的搜索词高亮
             *
             * @param {string} word 要高亮的关键字
             * @return {string}
             */
            function highlightWord(word) {
                return '<i class="'
                    + helper.getPrefixClass('autocomplete-item-char-selected') + '">'
                    + word + '</i>';
            }

            var ret = [];
            if (data && data.length) {
                for (var i = 0, len = data.length; i < len; i++) {
                    var item = data[i];
                    var text = u.isObject(item) && item.text || item;
                    var desc = u.isObject(item) && item.desc || undefined;
                    var html = lib.format(
                        '<li tabindex="-1" ${dataId} class="${lineClasses}">'
                            + '<span class="${itemClasses}">${text}</span>${desc}</li>',
                        {
                            dataId: u.isObject(item) && item.id ? ' data-id="' + item.id + '"' : '',
                            lineClasses: helper.getPrefixClass('autocomplete-item')
                                + (i === 0 ? ' ' + helper.getPrefixClass('autocomplete-item-hover') : ''),
                            itemClasses: helper.getPrefixClass('autocomplete-item-text'),
                            text: text.replace(
                                new RegExp(escapeRegex(inputValue), 'i'),
                                highlightWord
                            ),
                            desc: desc ? '<span class="' + helper.getPrefixClass('autocomplete-item-desc')
                                + '">' + item.desc + '</span>' : ''
                        }
                    );
                    ret.push(html);
                }
            }
            ret = ret.join('');
            this.repaint(ret);
            ret ? this.show() : this.hide();
        }


        /**
         * 将用户选中值回填到input输入框
         *
         * @param {string} value 用户选择值
         */
        function setTargetValue(value) {
            var input = this.getInput();
            var targetValue = input.value;
            targetValue = lib.trim(targetValue);
            var items = [];
            if (/\n/.test(targetValue)) {
                items = targetValue.split(/\n/);
                targetValue = items && items.pop();
            }

            var words = targetValue.split(',');
            words.pop();
            words.push(value);

            if (items) {
                items.push(words.join(','));
                value = items.join('\n');
            }
            this.control.setValue(value);
        }

        function extractMatchingWord(value) {
            var lines = value.split(/\n/);
            var line = lines.pop();
            var words = line.split(',');
            var word = words && words.pop();
            return lib.trim(word);
        }

        /**
         * 下拉建议列表中上下选择
         *
         * @param {string} updown up / down
         */
        function moveTo(updown) {
            var element = this.getElement(false);
            var items = element.children;
            var selectedItemIndex = this.getSelectedItemIndex();

            if (selectedItemIndex !== -1) {
                var selectedItem = items[selectedItemIndex];
                if (selectedItem) {
                    lib.removeClass(
                        selectedItem,
                        this.control.helper.getPrefixClass('autocomplete-item-hover')
                    );
                }
            }


            if (updown === 'up') {
                if (selectedItemIndex === -1 || selectedItemIndex === 0) {
                    selectedItemIndex = items.length - 1;
                }
                else {
                    selectedItemIndex--;
                }
            }
            else if (updown === 'down') {
                if (selectedItemIndex === -1 || selectedItemIndex === items.length - 1) {
                    selectedItemIndex = 0;
                }
                else {
                    selectedItemIndex++;
                }
            }
            selectedItem = items[selectedItemIndex];
            $(selectedItem).addClass(this.control.helper.getPrefixClass('autocomplete-item-hover'));

            selectedItem && selectedItem.focus();
            this.inputElement.focus();
        }

        var AutoComplete = eoo.create(
            Extension,
            {

                /**
                 * 输入控件自动提示扩展
                 *
                 * 当输入控件加上此扩展后,其自动提示功能将由扩展自动提供
                 *
                 * @class extension.AutoComplete
                 * @extends Extension
                 * @constructor
                 */
                constructor: function () {
                    this.$super(arguments);
                },

                /**
                 * 指定扩展类型,始终为`"AutoComplete"`
                 *
                 * @type {string}
                 */
                type: 'AutoComplete',

                attachTo: function () {
                    this.$super(arguments);

                    var me = this;
                    setTimeout(function () {
                        me.layer = new AutoCompleteLayer(me.target);
                    }, 0);
                },

                /**
                 * 激活扩展
                 *
                 * @override
                 */
                activate: function () {
                    // 只对`TextBox` 和 `TextLine`控件生效
                    var type = this.target.type;

                    if (!(type === TEXT_LINE
                        || type  === TEXT_BOX)) {
                        return;
                    }
                    this.$super(arguments);
                },

                /**
                 * 取消扩展的激活状态
                 *
                 * @override
                 */
                inactivate: function () {
                    var helper = this.target.helper;
                    var inputEle = this.inputElement;

                    helper.removeDOMEvent(inputEle, INPUT);

                    var layerMain = this.layer.getElement(false);

                    if (layerMain) {
                        helper.removeDOMEvent(layerMain, 'click');
                        $(layerMain).remove();
                    }
                    helper.removeDOMEvent(inputEle, 'keydown');
                    this.$super(arguments);
                }
            }
        );

        esui.registerExtension(AutoComplete);
        return AutoComplete;
    }
Example #2
0
    function (require) {
        var u = require('underscore');
        var Table = require('../Table');
        var Extension = require('../Extension');
        var eoo = require('eoo');
        var esui = require('esui');

        /**
         * 表格自动排序扩展
         *
         * 当表格加上此扩展后,其排序功能将由扩展自动提供
         *
         * 扩展默认使用简单的两值相减(字符串用`localeCompare`)的方法判断大小,
         * 也可以在表格具体列的配置中给出`comparer`属性来提供自定义的排序算法
         *
         * @class extension.AutoSort
         * @extends Extension
         * @constructor
         */
        var AutoSort = eoo.create(
            Extension,
            {

                /**
                 * 指定扩展类型,始终为`"AutoSort"`
                 *
                 * @type {string}
                 */
                type: 'AutoSort',

                /**
                 * 激活扩展
                 *
                 * @override
                 */
                activate: function () {
                    // 只对`Table`控件生效
                    if (!(this.target instanceof Table)) {
                        return;
                    }

                    this.target.on('sort', sort);

                    this.$super(arguments);
                },

                /**
                 * 取消扩展的激活状态
                 *
                 * @override
                 */
                inactivate: function () {
                    // 只对`Table`控件生效
                    if (!(this.target instanceof Table)) {
                        return;
                    }

                    this.target.un('sort', sort);

                    this.$super(arguments);
                }
            }
        );

        function sort(e) {
            var computeDiff = e.field.comparer;
            if (!computeDiff) {
                var fieldName = e.field.field;
                computeDiff = function (x, y) {
                    if (fieldName) {
                        x = x[fieldName];
                        y = y[fieldName];
                    }

                    return u.isString(x) && u.isString(y)
                        ? x.localeCompare(y)
                        : x - y;
                };
            }

            function compare(x, y) {
                var diff = computeDiff(x, y);
                return e.order === 'asc' ? diff : -diff;
            }

            var datasource = this.datasource;
            datasource.sort(compare);
            this.setDatasource(datasource);
        }

        esui.registerExtension(AutoSort);
        return AutoSort;
    }
Example #3
0
    function (require) {
        var esui = require('esui');
        var lib = require('esui/lib');
        var u = require('underscore');
        var Layer = require('esui/Layer');
        var Extension = require('esui/Extension');
        var eoo = require('eoo');
        var textCursorHelper= require('./helper/TextCursorHelper');
        var keyboard = require('esui/behavior/keyboard');
        require('esui/behavior/jquery-ui');

        var TEXT_LINE = 'TextLine';
        var TEXT_BOX = 'TextBox';
        var INPUT = 'input';
        var $ = require('jquery');

        /**
         * @class AutoCompleteLayer
         * @exports esui.Layer
         */
        var AutoCompleteLayer = eoo.create(
            Layer,
            {

                /**
                 * 自动提示层构造器
                 *
                 * @param {Object} [control] TextBox控件
                 */
                constructor: function (control) {
                    this.$super(arguments);
                    // 对于input单行,要求layer宽度不大于input宽度
                    var controlType = control.type;
                    if (controlType === TEXT_BOX) {
                        var ele = lib.g(control.inputId);
                        if (ele.tagName.toLowerCase() === INPUT) {
                            this.dock = {
                                strictWidth: true
                            };
                        }
                    }
                    this.initStructure();
                    this.initEvents();
                },

                type: 'AutoCompleteLayer',

                initStructure: function () {
                    var helper = this.control.helper;

                    this.addCustomClasses(
                        [
                            helper.getPrefixClass('autocomplete'),
                            helper.getPrefixClass('dropdown')
                        ]
                    );

                    var element = this.getElement();
                    $(this.control.main).after(element);
                },

                initEvents: function () {
                    var me = this;
                    var target = me.control;
                    var helper = target.helper;

                    var inputElement = this.inputElement = this.getInput();

                    var layerElement = me.getElement(false);
                    helper.addDOMEvent(
                        layerElement,
                        'click',
                        'li',
                        function (e) {
                            var clickedTarget = e.currentTarget;
                            me.hide();

                            var firstChild = $(clickedTarget.firstChild);
                            var text = firstChild.text();
                            var id = firstChild.closest('li').data('id');

                            // 事件参数
                            var args = {value: text};
                            // 如果选择项存在id属性,则在事件参数中加上
                            if (id !== undefined) {
                                args.id = id;
                            }
                            // 为了与 `select` 事件区分,用了 `targetselect`
                            var selectEvent = me.control.fire('targetselect', args);
                            if (selectEvent.isDefaultPrevented()) {
                                return;
                            }

                            /**
                             * @deprecated
                             */
                            var deprecatedEvent = me.control.fire('select', text);
                            if (deprecatedEvent.isDefaultPrevented()) {
                                return;
                            }

                            setTargetValue.call(me, text);
                        }
                    );

                    helper.addDOMEvent(
                        inputElement,
                        'keydown',
                        function (e) {
                            if (me.isHidden()) {
                                return;
                            }

                            switch (e.keyCode) {
                                // up
                                case keyboard.UP:
                                    e.preventDefault();
                                    moveTo.call(me, 'up');
                                    break;
                                // down
                                case keyboard.DOWN:
                                    e.preventDefault();
                                    moveTo.call(me, 'down');
                                    break;
                                // esc
                                case keyboard.ESC:
                                    me.hide();
                                    break;
                                // enter
                                case keyboard.RETURN:
                                    e.preventDefault();
                                    var selectedItem = me.getSelectedItem();
                                    if (!selectedItem) {
                                        return;
                                    }
                                    me.hide();

                                    var firstChild = $(selectedItem.firstChild);
                                    var text = firstChild.text();
                                    var id = firstChild.closest('li').data('id');

                                    // 事件参数
                                    var args = {value: text};
                                    // 如果选择项存在id属性,则在事件参数中加上
                                    if (id !== undefined) {
                                        args.id = id;
                                    }
                                    // 为了与 `select` 事件区分,用了 `targetselect`
                                    var selectEvent = me.control.fire('targetselect', args);
                                    if (selectEvent.isDefaultPrevented()) {
                                        return;
                                    }

                                    /**
                                     * @deprecated
                                     */
                                    var deprecatedEvent = me.control.fire('select', text);
                                    if (deprecatedEvent.isDefaultPrevented()) {
                                        return;
                                    }

                                    setTimeout(
                                        function () {
                                            setTargetValue.call(me, text);
                                        },
                                        0
                                    );
                                    break;
                            }
                        }
                    );

                    this.control.on('input', onInput);

                    /**
                     * 用户输入时触发,根据输入下拉提示
                     *
                     * @param {Event} event 事件对象
                     */
                    function onInput(event) {
                        // 保留光标位置,待会儿插入时需要知道在哪儿插入
                        me.caretPos = textCursorHelper.getCaretPosition(inputElement);

                        // 获取光标前的字符
                        var val = textCursorHelper.getTextBeforeCaret(inputElement);

                        // 进入数据面板触发逻辑
                        repaintHelperSlector.call(me, val);

                        this.fire('change', {args: val});
                    }

                },

                repaint: function (value) {
                    var element = this.getElement(false);
                    if (element) {
                        this.render(element, value);
                    }
                },

                render: function (element, value) {
                    if (value != null) {
                        element.innerHTML = value;
                    }
                },

                getSelectedItemIndex: function () {
                    var element = this.getElement(false);
                    var items = element.children;
                    var selectedItemIndex = -1;
                    for (var i = 0, len = items.length; i < len; i++) {
                        if ($(items[i]).hasClass(
                            this.control.helper.getPrefixClass('autocomplete-item-hover')
                        )) {
                            selectedItemIndex = i;
                            break;
                        }
                    }
                    return selectedItemIndex;
                },

                getSelectedItem: function () {
                    var element = this.getElement(false);
                    var selectedItem;
                    var selectedItemIndex = this.getSelectedItemIndex();
                    if (selectedItemIndex !== -1) {
                        selectedItem = element.children[selectedItemIndex];
                    }
                    return selectedItem;
                },

                /**
                 * @override
                 * 对textarea自行实现position
                 */
                position: function () {
                    var input = this.inputElement;
                    if (input.nodeName.toLowerCase() !== 'textarea') {
                        this.$super(arguments);
                    }
                },

                show: function () {
                    this.$super(arguments);
                    var input = this.inputElement;
                    var $ele = $(this.getElement(false));
                    if (input.nodeName.toLowerCase() === 'textarea') {
                        var pos = textCursorHelper.getCaretPositionStyle(input);
                        $ele.position(
                            {
                                of: input,
                                at: 'left+' + pos.left + ' top+' + pos.top,
                                my: 'left top'
                            }
                        );
                    }
                },

                isHidden: function () {
                    var element = this.getElement();
                    return $(element).is(':hidden');
                },

                /**
                 * 获取内部输入元素
                 *
                 * @return {Element}
                 */
                getInput: function () {
                    var control = this.control;
                    if (control.type === TEXT_BOX) {
                        return lib.g(control.inputId);
                    }
                    else if (control.type === TEXT_LINE) {
                        return control.getTextArea();
                    }
                    return null;
                },

                /**
                 * 获取查询词,也即 at 符号后边的词
                 *
                 * @param  {string} val 截取字符
                 * @return {string} 查询词
                 */
                getQuery: function (val) {
                    var lastAtIndex = val.lastIndexOf(this.control.openAt);
                    return val.slice(lastAtIndex + 1);
                },

                nodeName: 'ol'
            }
        );

        /**
         * 匹配已输入值的算法
         *
         * @param {string} value 当前用户输入
         * @param {Array} datasource 数据源
         * @return {Array}
         */
        function filter(value, datasource) {
            return u.filter(
                datasource,
                function (data) {
                    var text = u.isObject(data) ? data.text : data;
                    return (new RegExp(escapeRegex(value), 'i')).test(text);
                }
            );
        }

        /**
         * 特殊字符处理,这些字符排除在匹配算法外
         *
         * @param {string} value 用户输入
         * @return {string}
         */
        function escapeRegex(value) {
            return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
        }

        /**
         * 根据用户输入绘制下拉选择列表
         *
         * @param {string} value 用户输入
         */
        function repaintHelperSlector(value) {
            // 判断是否需要弹出帮助面板
            if (canShowSelector.call(this, value)) {

                // 获取搜索词
                this.query = this.getQuery(value);

                var me = this;
                var datasource = this.control.datasource;
                if (typeof datasource === 'function') {
                    datasource.call(
                        this,
                        value,
                        function (data) {
                            renderSlector.call(me, data, value);
                        }
                    );
                }
                else if (datasource && datasource.length) {
                    // 根据搜索词,获取过滤后的优选词数据列表
                    var list = filter(this.query, datasource);

                    // 渲染帮助面板
                    renderSlector.call(me, list, value);
                }
            }
            else {
                this.hide();
            }
        }

        /**
         * 绘制下拉选择列表
         *
         * @param {Array} data 需要渲染的数据源
         * @param {string} inputValue 需要检索的字符串
         */
        function renderSlector(data, inputValue) {
            var helper = this.control.helper;

            /**
             * 将text中匹配到的搜索词高亮
             *
             * @param {string} word 要高亮的关键字
             * @return {string}
             */
            function highlightWord(word) {
                return '<i class="'
                    + helper.getPrefixClass('autocomplete-item-char-selected') + '">'
                    + word + '</i>';
            }

            var ret = [];
            if (data && data.length) {
                for (var i = 0, len = data.length; i < len; i++) {
                    var item = data[i];
                    var text = u.isObject(item) && item.text || item;
                    var desc = u.isObject(item) && item.desc || undefined;
                    var html = lib.format(
                        '<li tabindex="-1" ${dataId} class="${lineClasses}">'
                            + '<span class="${itemClasses}">${text}</span>${desc}</li>',
                        {
                            dataId: u.isObject(item) && item.id ? ' data-id="' + item.id + '"' : '',
                            lineClasses: helper.getPrefixClass('autocomplete-item')
                                + (i === 0 ? ' ' + helper.getPrefixClass('autocomplete-item-hover') : ''),
                            itemClasses: helper.getPrefixClass('autocomplete-item-text'),
                            text: text.replace(
                                new RegExp(escapeRegex(inputValue), 'i'),
                                highlightWord
                            ),
                            desc: desc ? '<span class="' + helper.getPrefixClass('autocomplete-item-desc')
                                + '">' + item.desc + '</span>' : ''
                        }
                    );
                    ret.push(html);
                }
            }
            ret = ret.join('');
            this.repaint(ret);
            ret ? this.show() : this.hide();
        }

        /**
         * 将用户选中值回填到input输入框
         *
         * @param {string} value 用户选择值
         */
        function setTargetValue(value) {
            var input = this.getInput();
            var closeTag = this.control.closeAt ? this.control.closeAt : '';
            // 执行插入操作,先删除查询词然后插入提示词
            textCursorHelper.del(input, -this.query.length, this.caretPos);
            textCursorHelper.add(input, value + closeTag, this.caretPos - this.query.length);
        }

        function extractMatchingWord(value) {
            var lines = value.split(/\n/);
            var line = lines.pop();
            var words = line.split(',');
            var word = words && words.pop();
            return lib.trim(word);
        }

        /**
         * 检测是否需要显示数据面板,检测逻辑如下:
         *
         * 一:局部替换,开闭符号都包含
         * - 包含触发符号 `{`
         * - 最后一个触发符号 `{` 需要出现在最后一个触发结束符号 `}` 后边
         *
         * 二:全部替换,只有开合(openAt)没有闭合(closeAt)
         * - 这时候会在开始输入的时候就给提示并进行替换
         *
         * @param {string} val 光标前的数据
         * @return {boolean} 需要显示返回 true,否则返回 false
         */
        function canShowSelector (val) {
            var openIndex = -1;
            var closeIndex = -1;
            var openTag = this.control.openAt;
            var closeTag = this.control.closeAt;

            if (openTag) {
                openIndex = val.lastIndexOf(openTag);
            }
            if (closeTag) {
                closeIndex = val.lastIndexOf(closeTag);
            }

            if (openIndex >= 0 && openIndex > closeIndex) {
                return true;
            }
            else if (openTag && !closeTag) {
                return true;
            }

            return false;
        };

        /**
         * 下拉建议列表中上下选择
         *
         * @param {string} updown up / down
         */
        function moveTo(updown) {
            var element = this.getElement(false);
            var items = element.children;
            var selectedItemIndex = this.getSelectedItemIndex();

            if (selectedItemIndex !== -1) {
                var selectedItem = items[selectedItemIndex];
                if (selectedItem) {
                    lib.removeClass(
                        selectedItem,
                        this.control.helper.getPrefixClass('autocomplete-item-hover')
                    );
                }
            }


            if (updown === 'up') {
                if (selectedItemIndex === -1 || selectedItemIndex === 0) {
                    selectedItemIndex = items.length - 1;
                }
                else {
                    selectedItemIndex--;
                }
            }
            else if (updown === 'down') {
                if (selectedItemIndex === -1 || selectedItemIndex === items.length - 1) {
                    selectedItemIndex = 0;
                }
                else {
                    selectedItemIndex++;
                }
            }
            selectedItem = items[selectedItemIndex];
            $(selectedItem).addClass(this.control.helper.getPrefixClass('autocomplete-item-hover'));

            selectedItem && selectedItem.focus();
            this.inputElement.focus();
        }

        var AutoComplete = eoo.create(
            Extension,
            {

                /**
                 * 输入控件自动提示扩展
                 *
                 * 当输入控件加上此扩展后,其自动提示功能将由扩展自动提供
                 *
                 * @class extension.AutoComplete
                 * @extends Extension
                 * @constructor
                 */
                constructor: function () {
                    this.$super(arguments);
                },

                /**
                 * 指定扩展类型,始终为`"AutoComplete"`
                 *
                 * @type {string}
                 */
                type: 'AutoComplete',

                attachTo: function () {
                    this.$super(arguments);

                    var me = this;
                    setTimeout(function () {
                        me.layer = new AutoCompleteLayer(me.target);
                    }, 0);
                },

                /**
                 * 激活扩展
                 *
                 * @override
                 */
                activate: function () {
                    // 只对`TextBox` 和 `TextLine`控件生效
                    var type = this.target.type;

                    if (!(type === TEXT_LINE
                        || type  === TEXT_BOX)) {
                        return;
                    }
                    this.$super(arguments);
                },

                /**
                 * 取消扩展的激活状态
                 *
                 * @override
                 */
                inactivate: function () {
                    var helper = this.target.helper;
                    var inputEle = this.inputElement;

                    helper.removeDOMEvent(inputEle, INPUT);

                    var layerMain = this.layer.getElement(false);

                    if (layerMain) {
                        helper.removeDOMEvent(layerMain, 'click');
                        $(layerMain).remove();
                    }
                    helper.removeDOMEvent(inputEle, 'keydown');
                    this.$super(arguments);
                }
            }
        );

        esui.registerExtension(AutoComplete);
        return AutoComplete;
    }
Example #4
0
    function (require) {
        var lib = require('esui/lib');
        var ui = require('esui');
        var u = require('underscore');
        var Extension = require('esui/Extension');

        require('esui/TipLayer');

        /**
         * 用于表格的Tip扩展,为操作列图标添加Tip
         *
         * 使用此扩展,只要操作列图标包含`"table-operation"`这一class,
         * 同时有`"table-operation-{type}"`的class,
         * 即可在鼠标移到图标上时,使用`Tip`控件显示该图标对应的操作
         *
         * 如以下HTML:
         *
         *     <span class="table-operation table-operation-edit">编辑</span>
         *
         * 即会在鼠标移到元素上时,出现一个Tip控件,提示内容为“编辑”
         *
         * **注意,此扩展不支持IE7**
         *
         * @extends esui.Extension
         * @constructor
         */
        function TableTip() {
            Extension.apply(this, arguments);
        }

        /**
         * 扩展的类型,始终为`"TableTip"`
         *
         * @type {string}
         * @override
         */
        TableTip.prototype.type = 'TableTip';

        var typeRule = /table-operation-(\w+)/;

        function getTipType(element) {
            return typeRule.exec(element.className)[1];
        }

        /**
         * 创建`Tip`控件并附加到相应元素上
         *
         * @param {HTMLElement[]} elements 需要`Tip`的元素
         * @param {string} type 操作类型
         */
        TableTip.prototype.createAndAttachTip = function (elements, type) {
            var options = {
                id: 'table-operation-tip-' + u.escape(type),
                viewContext: this.target.viewContext,
                content: elements[0].innerHTML,
                arrow: true,
                skin: 'table-tip'
            };
            var tip = ui.create('TipLayer', options);
            tip.appendTo(document.body);
            u.each(
                elements, 
                function (element) {
                    var options = {
                        targetDOM: element,
                        showMode: 'over',
                        delayTime: 200,
                        positionOpt: {
                            bottom: 'bottom',
                            left: 'left'
                        }
                    };
                    tip.attachTo(options);
                }
            );
        };

        /**
         * 初始化操作列的`Tip`控件
         */
        TableTip.prototype.initTips = function () {
            if (!document.querySelectorAll) {
                return;
            }

            var elements = document.querySelectorAll('.table-operation');

            u.chain(elements)
                .groupBy(getTipType)
                .each(u.bind(this.createAndAttachTip, this));
        };

        /**
         * 激活扩展
         *
         * @override
         */
        TableTip.prototype.activate = function () {
            Extension.prototype.activate.apply(this, arguments);

            this.target.on('afterrender', this.initTips, this);
        };

        /**
         * 取消扩展的激活状态
         *
         * @override
         */
        TableTip.prototype.inactivate = function () {
            this.target.un('afterrender', this.initTips, this);

            Extension.prototype.inactivate.apply(this, arguments);
        };

        lib.inherits(TableTip, Extension);
        ui.registerExtension(TableTip);

        return TableTip;
    }
Example #5
0
define(function (require) {

    var util = require('er/util');
    var esui = require('esui');
    var Extension = require('esui/Extension');
    var aop = require('library/base/aop');
    var us = require('underscore');
    var localStorage = require('library/framework/storage/localStorage');
    
    /**
     * 凤巢定制版TipLayer控件的关闭扩展功能。
     * 
     * @param {Object}
     * {
     *     targetKey: '', // bubble所对应的唯一key值。
     *     closeTimes: 1 // 关闭次数
     * }
     * 
     * @constructor
     * @extends Extension
     */
    function FcBubbleLayerClosable(options) {
        options = options || {};
        options.closeTimes = +options.closeTimes || 0;
        Extension.apply(this, arguments);
    }
    
    util.inherits(FcBubbleLayerClosable, Extension);
    
    /**
     * 指定扩展类型,始终为`"FcBubbleLayerClosable"`
     *
     * @type {string}
     */
    FcBubbleLayerClosable.prototype.type = 'FcBubbleLayerClosable';

    /**
     * 气泡的宽度。
     * 
     * @const
     * @type {number}
     */
    var BUBBLE_LAYER_WIDTH = 260;
    
    /**
     * 渲染关闭按钮。
     * 
     * @protected
     */
    FcBubbleLayerClosable.prototype.renderCloseButton = function () {
        var me = this;
        var tipLayer = me.target;
        
        var closeButton = $( ''
            + '<a href="javascript:;" class="' 
            + tipLayer.helper.getPartClasses('close-button').join(' ')
            + '">'
            +     '<i class="font-icon font-icon-times"></i>'
            +'</a>'
        );
        closeButton.prependTo(tipLayer.main);
        
        tipLayer.helper.addDOMEvent(
            closeButton[0],
            'click',
            function () {
                tipLayer.fire('close');
                tipLayer.hide();
            }
        );
        
        tipLayer.helper.addPartClasses('body-panel-extra', tipLayer.getBody().main);
    };
    
    /**
     * 激活扩展
     *
     * @override
     */
    FcBubbleLayerClosable.prototype.activate = function () {
        var me = this;
        var tipLayer = me.target;
        $(tipLayer.main).width(BUBBLE_LAYER_WIDTH);
        
        if (tipLayer.helper.isInStage('RENDERED')) {
            me.renderCloseButton();
        } else {
            tipLayer.on('afterrender', me.renderCloseButton, me);
        }
        
        Extension.prototype.activate.apply(this, arguments);
        
        tipLayer.on('close', me.recordCloseTimes, me);
        
        aop.after(tipLayer, 'attachTo', function (options) {
            me.extraShow();
        });
        tipLayer.on('show', me.extraShow, me);
    };
    
    /**
     * 显示时,需要执行的额外的方法。
     * 
     * @protected
     */
    FcBubbleLayerClosable.prototype.extraShow = function () {
        var me = this;
        me.cancelClickBodyEvent();
        me.computeCloseTimes();
    };
    
    /**
     * 取消tip layer的点击任意地方消失事件。
     * 
     * @protected
     */
    FcBubbleLayerClosable.prototype.cancelClickBodyEvent = function () {
        var me = this;
        var tipLayer = me.target;
        var delayTime = tipLayer.delayTime;
        if (!+delayTime) {
            delayTime = 0;
        }
        // 由于TipLayer中,添加body点击事件是通过setTimeout,并等待delayTime再注册,
        // 所以这里,移除注册就要在delayTime + 1之后。
        setTimeout(function () {
            // 移除鼠标点击别处事件。
            tipLayer.helper.removeDOMEvent($('body')[0], 'click');
        }, delayTime + 1);
    };
    
    /**
     * 记录关闭次数。
     * 
     * @protected
     */
    FcBubbleLayerClosable.prototype.recordCloseTimes = function () {
        var me = this;
        if (me.closeTimes) {
            var targetKey = this.targetKey;
            var bubbleRecord = me.getBubbleRecord();
            var closedTimes = bubbleRecord[targetKey];
            if (closedTimes < me.closeTimes) {
                ++bubbleRecord[targetKey];
                localStorage.updateItem('bubbleRecord', bubbleRecord);
            }
        }
    };
    
    /**
     * 当bubble关闭的次数超过了指定的关闭次数,就将其隐藏。
     * 
     * @protected
     */
    FcBubbleLayerClosable.prototype.computeCloseTimes = function () {
        var me = this;
        var tipLayer = me.target;
        var targetKey = this.targetKey;
        var bubbleRecord = me.getBubbleRecord();
        var closedTimes = bubbleRecord[targetKey];
        if (me.closeTimes) {
            if (closedTimes >= me.closeTimes) {
                tipLayer.hide();
            }
        }
    };
    
    /**
     * 从storage中获取bubble record对象
     * 
     * @return {Object}
     */
    FcBubbleLayerClosable.prototype.getBubbleRecord = function () {
        var bubbleRecord = localStorage.getItem('bubbleRecord');
        if (!bubbleRecord) {
            localStorage.setItem('bubbleRecord', {});
            bubbleRecord = {};
        }
        var targetKey = this.targetKey;
        if (!bubbleRecord[targetKey]) {
            bubbleRecord[targetKey] = 0;
        }
        return bubbleRecord;
    };
    
    /**
     * 取消扩展的激活状态
     *
     * @override
     */
    FcBubbleLayerClosable.prototype.inactivate = function () {
        var me = this;
        var tipLayer = me.target;
        
        tipLayer.un('afterrender', me.renderCloseButton, me);
        tipLayer.un('close', me.recordCloseTimes, me);
        tipLayer.un('show', me.extraShow, me);
        
        Extension.prototype.activate.apply(me, arguments);
    };
    
    esui.registerExtension(FcBubbleLayerClosable);

    return FcBubbleLayerClosable;
});
Example #6
0
    function (require) {
        var lib = require('../lib');
        var ui = require('esui');
        var u = require('underscore');
        var Extension = require('esui/Extension');

        require('esui/TipLayer');

        var exports = {};

        // BIG BROTHER IS WATCHING YOU
        var tipId = 0x1984;

        /**
         * 为控件快速添加Tip显示
         *
         * 使用此扩展,鼠标移入控件后如果悬浮在带有`"data-title"`属性的元素内,会按其
         * 内容显示相应Tip。内容可以通过引用的方式读取扩展对象中的`data`属性。
         *
         * 例如控件`tip`属性为:
         *
         *     { del: '点击进行<strong>删除(不可恢复)</strong>' }
         *
         * 书写HTML模板时需添加:
         *
         *     data-ui-extension-tip-type="QuickTip"
         *     data-ui-extension-tip-data="@tipData" <- 当使用引用方式读取时必传
         *
         * 控件内有元素:
         *
         *     <button data-title="点击进行编辑">编辑</button>
         *     <button data-title="@del">删除</button>
         *
         * 即会在鼠标移到元素上时,出现一个Tip控件,提示内容按`data-title`设置给出。
         *
         * @extends esui.Extension
         * @constructor
         */
        exports.constructor = function () {
            Extension.apply(this, arguments);
        };

        /**
         * 扩展的类型,始终为`"QuickTip"`
         *
         * @type {string}
         * @override
         */
        exports.type = 'QuickTip';

        function getProperty(target, path) {
            var value = target;
            for (var i = 0; i < path.length; i++) {
                value = value[path[i]];
            }

            return value;
        }

        exports.getTipContent = function (element) {
            var content = element.getAttribute('data-title');

            // 引用目标控件的属性值
            if (content.charAt(0) === '@') {
                var path = content.substring(1).split('.');
                var data = this.data;
                if (!data) {
                    return '';
                }
                var value = data[path[0]];
                return path.length > 1
                    ? getProperty(value, path.slice(1))
                    : value;
            }
            // 字符串直接量
            return content;
        };

        /**
         * 创建`Tip`控件并附加到相应元素上
         *
         * @param {HTMLElement} element 需要`Tip`的元素
         */
        exports.showTip = function (element) {
            var content = this.getTipContent(element);
            content = typeof content === 'string' ? {content: content} : content;
            var tip = this.current;

            // 每个扩展只支持同时显示一个Tip
            if (!tip) {
                var options = {
                    id: 'ui-tip-' + tipId++,
                    viewContext: this.target.viewContext,
                    arrow: true
                };
                u.extend(options, content);
                tip = ui.create('TipLayer', options);
                tip.appendTo(document.body);
                this.current = tip;
            }
            else {
                tip.setProperties(content);
            }

            var attachOptions = {
                targetDOM: element,
                showMode: 'over',
                delayTime: 200,
                positionOpt: {
                    top: 'top',
                    right: 'left'
                }
            };
            tip.attachTo(attachOptions);
            tip.show(element, attachOptions.positionOpt);
        };



        /**
         * 鼠标移入的逻辑
         *
         * @param {Event} evt 事件对象
         */
        function mouseEnterHandler(evt) {
            var target = evt.target;
            this.showTip(target);
        }

        /**
         * 鼠标移出的逻辑
         *
         * @param {Event} evt 事件对象
         */
        function mouseLeaveHandler(evt) {
            if (this.current) {
                this.current.hide();
            }
        }

        /**
         * 激活扩展
         *
         * @override
         */
        exports.activate = function () {
            Extension.prototype.activate.apply(this, arguments);

            this.mouseEnterHandler = u.bind(mouseEnterHandler, this);
            this.mouseLeaveHandler = u.bind(mouseLeaveHandler, this);
            lib.on(this.target.main, '[data-title]', 'mouseenter', this.mouseEnterHandler);
            lib.on(this.target.main, '[data-title]', 'mouseleave', this.mouseLeaveHandler);
        };

        /**
         * 取消扩展的激活状态
         *
         * @override
         */
        exports.inactivate = function () {
            if (this.current) {
                this.current.dispose();
            }
            lib.un(this.target.main, 'mouseenter', this.mouseOverHandler);
            lib.un(this.target.main, 'mouseleave', this.mouseLeaveHandler);

            Extension.prototype.inactivate.apply(this, arguments);
        };

        var QuickTip = require('eoo').create(Extension, exports);
        ui.registerExtension(QuickTip);

        return QuickTip;
    }