function (require) {
        var esui = require('esui');
        var lib = require('esui/lib');
        var u = require('underscore');
        var eoo = require('eoo');
        var painters = require('esui/painters');

        require('esui/Select');
        require('esui/Panel');

        var TableRichSelector = require('./TableRichSelector');

        /**
         * 控件类
         *
         * @class ui.TableRichSelectorWithFilter
         * @extends ui.RichSelector
         */
        var TableRichSelectorWithFilter = eoo.create(
            TableRichSelector,
            {

                /**
                 * 控件类型,始终为`"TableRichSelectorWithFilter"`
                 *
                 * @type {string}
                 * @override
                 */
                type: 'TableRichSelectorWithFilter',

                /**
                 * @override
                 */
                styleType: 'RichSelector',


                /**
                 * 在搜索框的旁边增加筛选
                 * @override
                 */
                getSearchBoxHTML: function () {
                    var searchBoxHTML = [
                        // 搜索区
                        '<div data-ui="type:Panel;childName:searchBoxArea"',
                        ' class="' + this.helper.getPartClassName('search-wrapper') + '">',
                        '   <div style="float:left" data-ui="type:Select;childName:filter;"></div>',
                        '   <div',
                        '   data-ui="buttonPosition:right;buttonVariants:bordered icon;',
                        '   type:SearchBox;childName:itemSearch;variants:clear-border',
                        '   hide-searched-button;searchMode:instant;">',
                        '   </div>',
                        '</div>'
                    ].join('');

                    return searchBoxHTML;
                },

                /**
                 * 刷新筛选区
                 * @public
                 */
                refreshFilter: function () {
                    var filter = this.getFilter();

                    if (filter) {
                        filter.setProperties({datasource: this.filterDatasource});
                        filter.on('change', u.bind(this.search, this));
                    }
                },

                /**
                 * 重新渲染视图
                 * 仅当生命周期处于RENDER时,该方法才重新渲染
                 *
                 * @param {Array=} 变更过的属性的集合
                 * @override
                 */
                repaint: painters.createRepaint(
                    TableRichSelector.prototype.repaint,
                    {
                        name: 'filterDatasource',
                        paint: function (control, filterDatasource) {
                            control.refreshFilter();
                        }
                    }
                ),

                /**
                 * @override
                 */
                initStructure: function () {
                    this.$super(arguments);
                    // 状态筛选,最终调用search函数
                    var filter = this.getFilter();
                    filter.extensions[0].activate();

                    this.addState('with-filter');
                },

                /**
                 * @override
                 */
                search: function (args) {
                    var filterData = [];
                    // 取自带搜索框的值
                    var searchBox = this.getSearchBox();
                    if (searchBox) {
                        filterData.push({value: lib.trim(searchBox.getValue())});
                    }

                    var filterSelect = this.getFilter();
                    if (filterSelect) {
                        var value = filterSelect.getValue();
                        if (value && value !== '') {
                            filterData.push({keys: [this.filterField], value: filterSelect.getRawValue()});
                        }
                    }

                    if (filterData.length) {
                        // 查询,更新数据源
                        this.queryItem(filterData);
                        // 更新腿部总结果
                        this.refreshFoot();
                        // 更新头部总结果
                        this.refreshHead();
                        // 更新状态
                        this.addState('queried');
                    }
                    // 相当于执行清空操作
                    else {
                        this.clearQuery();
                    }
                },

                /**
                 * 获取筛选区Panel
                 * @return {esui.Panel}
                 */
                getFilter: function () {
                    return this.getChild('body').getChild('searchBoxArea').getChild('filter');
                }
            }
        );

        esui.register(TableRichSelectorWithFilter);
        return TableRichSelectorWithFilter;
    }
示例#2
0
    function (require) {
        var $ = require('jquery');
        var u = require('underscore');
        var esui = require('esui');
        var Control = require('esui/Control');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        require('./ImagePanel');
        require('esui/Pager');

        var ImageList = eoo.create(
            Control,
            {

                /**
                 * 控件类型,始终为 `ImageList`
                 *
                 * @type {string}
                 * @readonly
                 * @override
                 */
                type: 'ImageList',

                /**
                 * 初始化参数
                 *
                 * @param {Object} options 参数对象
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        datasource: [],         // 数据源
                        row: 2,                 // 行数
                        column: 5,              // 列数
                        width: 0,               // 宽度,单位px
                        async: false,           // 是否异步,
                        needDesc: true,         // 是否需要显示图标描述信息
                        canSelect: true,        // 能否选中
                        operates: '',           // 右上角的操作按钮
                        imageWidth: 100,        // 图标长
                        imageId: 0,              // 默认选中的图标ID
                        noneTip: ''             // 没有数据时显示的tip
                    };

                    u.extend(properties, options);
                    // originDatasource 保存一开始传入的datasource
                    properties.originDatasource = u.clone(properties.datasource);
                    this.setProperties(properties);
                },

                /**
                 * 初始化DOM结构
                 *
                 * @override
                 * @protected
                 */
                initStructure: function () {
                    buildDOMStructure.call(this);
                    this.initChildren();
                },

                /**
                 * 初始化事件
                 *
                 * @override
                 * @protected
                 */
                initEvents: function () {
                    var pager = this.getPager();
                    var that = this;

                    // 翻页事件
                    pager.onchangepage = function () {
                        var page = this.get('page');
                        var pageSize = this.get('pageSize');

                        that.fire('pagerchange', {page: page, pageSize: pageSize});

                        // 异步情况下,不执行默认翻页操作
                        if (that.async) {
                            return;
                        }

                        // 默认翻页事件
                        var datasource = that.datasource;
                        var ds = datasource.slice((page - 1) * pageSize, page * pageSize);
                        that.getImagePanel().setDatasource(ds);
                    };

                    // 改变每页大小事件
                    pager.onchangepagesize = function () {
                        var pageSize = this.get('pageSize');
                        that.row = pageSize / that.column;

                        that.fire('pagerchange', {page: 1, pageSize: pageSize});

                        // 异步情况下,不执行默认改变页面大小操作
                        if (that.async) {
                            return;
                        }

                        // 默认改变页面大小事件
                        var datasource = that.datasource;
                        this.set('page', 1);
                        var ds = datasource.slice(0, pageSize);
                        that.getImagePanel().setDatasource(ds);
                    };
                },

                /**
                 * 获取imagepanel
                 *
                 * @return {Object}
                 */
                getImagePanel: function () {
                    return this.getChild('imagePanel');
                },

                /**
                 * 获取pager
                 *
                 * @return {Object}
                 */
                getPager: function () {
                    return this.getChild('pager');
                },

                /**
                 * datasource重新赋值,并刷新
                 *
                 * @param {Array} [datasource] 数据源
                 * @param {number} [count] 数据总数,异步下有效
                 * @param {number} [page] 当前页码,异步下有效
                 */
                setDatasource: function (datasource, count, page) {
                    this.datasource = u.clone(datasource);
                    this.originDatasource = u.clone(datasource);
                    this.count = count;
                    this.page = page;
                    buildDOMStructure.call(this);
                },

                /**
                 * 默认筛选函数,异步下不要调用
                 *
                 * @param {Object} [arg] 对象或函数
                 */
                filter: function (arg) {
                    var filteredDS = null;
                    var ds = u.clone(this.originDatasource);

                    // arg可以是对象或者函数
                    // arg是对象时,根据对象包含的属性进行筛选
                    if (u.isObject(arg)) {
                        var keys = u.keys(arg);

                        // 根据filter中的属性和值进行筛选
                        filteredDS = u.filter(ds, function (icon) {
                            var value = true;
                            u.each(keys, function (key) {
                                if (arg[key] !== '' && icon[key] !== arg[key]) {
                                    value = false;
                                }
                            });
                            return value;
                        });
                    }
                    // arg是函数时,将datasource传给这个函数作为参数,此函数必须返回一个结果
                    else if (u.isFunction(arg)) {
                        filteredDS = arg(ds);
                    }
                    // 不支持其他类型
                    else {
                        return;
                    }

                    // 将筛选结构重新赋值给datasource,供翻页操作
                    this.datasource = filteredDS;

                    var pager = this.getPager();
                    var pageSize = pager.get('pageSize');
                    pager.set('count', filteredDS.length);
                    pager.set('page', 1);
                    filteredDS = filteredDS.slice(0, pageSize);
                    this.getImagePanel().setDatasource(filteredDS);
                },

                /**
                 * 默认排序函数,异步下不要调用
                 *
                 * @param {Object} [arg] 对象或函数
                 */
                sort: function (arg) {
                    var sortedDS = null;
                    var ds = this.datasource;

                    // arg可以是对象或者函数
                    // arg是对象时,根据对象包含的属性进行排序
                    // arg必须包含'field'、'order'两个属性
                    if (u.isObject(arg)) {
                        var field = arg.field;
                        var order = arg.order.toLowerCase();

                        // 根据field排序,升序
                        sortedDS = u.sortBy(ds, field);

                        // 如果是降序,则反转
                        if (order === 'desc') {
                            sortedDS.reverse();
                        }
                    }
                    // arg是函数时,将datasource传给这个函数作为参数,此函数必须返回一个结果
                    else if (u.isFunction(arg)) {
                        sortedDS  = arg(ds);
                    }
                    // 不支持其他类型
                    else {
                        return;
                    }

                    // 将筛选结构重新赋值给datasource,供翻页操作
                    this.datasource = sortedDS;

                    var pager = this.getPager();
                    var pageSize = pager.get('pageSize');
                    pager.set('page', 1);
                    sortedDS = sortedDS.slice(0, pageSize);
                    this.getImagePanel().setDatasource(sortedDS);
                },

                /**
                 * 重渲染
                 *
                 * @override
                 * @protected
                 */
                repaint: painters.createRepaint(
                    Control.prototype.repaint,
                    {
                        name: 'datasource',
                        paint: function (iconList, datasource) {
                            iconList.setDatasource(datasource);
                        }
                    }
                )
            }
        );

        /**
         * 创建DOM节点
         *
         * @param {string} [part] part名称
         * @param {string} [nodeName] 标签名称
         * @param {string} [innerHTML] innerHTML
         *
         * @return {string} html
         */
        function create(part, nodeName, innerHTML) {
            return this.helper.getPartBeginTag(part, nodeName)
                + innerHTML
                + this.helper.getPartEndTag(part, nodeName);
        }

        /**
         * 构建DOM结构
         */
        function buildDOMStructure() {
            var imagePanel = this.getImagePanel();
            var pager = this.getPager();
            var pageSize = this.row * this.column;
            var pageSizes = [pageSize, 2 * pageSize, 5 * pageSize, 10 * pageSize];

            // 构造main html
            if (!imagePanel || !pager) {
                imagePanel = null;
                pager = null;

                var imagePanelWrapper = create.call(this, 'image-panel', 'div', '');
                var pagerWrapper = create.call(this, 'pager', 'div', '');
                var html = create.call(this, '', 'div', imagePanelWrapper + pagerWrapper);
                this.main.innerHTML = html;
            }

            // ImagePanel appendTo main
            var ds = this.datasource.slice(0, pageSize);
            if (imagePanel) {
                imagePanel.setDatasource(ds);
            }
            else {
                // iconpanel的默认属性
                var keys = [
                    'needDesc',         // 是否需要显示图标描述信息
                    'canSelect',        // 能否选中
                    'operates',         // 右上角的操作按钮
                    'imageWidth',       // 图标长
                    'datasource',       // 数据源
                    'imageId',           // 默认选中的图标ID
                    'noneTip'           // 没有数据时显示的tip
                ];
                // 从this取出iconpanel配置
                var properties = u.pick(this, keys);
                imagePanel = esui.create('ImagePanel', properties);
                this.addChild(imagePanel, 'imagePanel');
                imagePanel.appendTo($('#' + this.helper.getId('image-panel'))[0]);
            }

            // Pager appendTo main
            var page = this.async ? this.page : 1;
            var count = this.async ? this.count : this.datasource.length;
            if (pager) {
                pager.set('count', count);
                pager.set('page', page);
            }
            else {
                // 根据 row 和 column设置pageSize 和 pageSizes
                pager = esui.create('Pager', {
                    page: page,
                    count: count,
                    pageSize: pageSize,
                    pageSizes: pageSizes,
                    pageType: 'plain',
                    layout: 'alignRight'
                });
                this.addChild(pager, 'pager');
                pager.appendTo($('#' + this.helper.getId('pager'))[0]);
            }

            if (this.width > 0) {
                $(this.main).css(this.width);
            }
        }

        esui.register(ImageList);
        return ImageList;
    }
示例#3
0
    function (require) {
        require('esui/Panel');
        require('esui/TextBox');
        require('esui/Select');

        // 注册验证类
        require('esui/validator/RequiredRule');
        require('esui/validator/MaxRule');
        require('esui/validator/MinRule');
        require('esui/validator/PatternRule');

        var esui = require('esui');
        var u = require('underscore');
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');

        var exports = {};

        /**
         * 控件的类型
         * @override
         * @type {String}
         */
        exports.type = 'Slider';

        /**
         * 参数的初始化
         * @protected
         * @override
         * @param  {Object} options [初始化的参数]
         */
        exports.initOptions = function (options) {
            /**
             * 默认的属性
             * @type {Object}
             * @type {string} defaults.orientation 滑块的形式 横着为'' 竖着为’vertical‘
             * @type {number} defaults.start 起始值 默认是0
             * @type {number} defaults.end 结束值 默认是10,
             * @type {number} defaults.step 滑动杆值的步长默认是1
             * @type {number | Arrary} defaults.value 滑动杆的值 默认为min或[min, max]
             * @type {number} defaults.min 最小值 不能小于start, 无值时与start相同
             * @type {number} defaults.max 最大值 不能大于end,无值时与end相同
             * @type {string} defaults.unitText 滑动杆内数值后面的单位
             * @type {boolean} defaults.isShowSelectedBG 滑杆已选择的部分是否加背景色显示 显示true 不显示false 默认true
             * @type {boolean} defaults.hasHead 是否显示标题和头部 显示true 不显示false
             * @type {string} defaults.title 滑动杆的头部标题
             * @type {string} defaults.headType 默认label 还可以是textbox、select
             * @type {string} defaults.pattern 文本框时验证的正则表达式
             * @type {string} defaults.errorMessage 正则验证错误时的提示信息
             * @type {Array} defaults.datasource 下拉框时的数据源
             * @type {boolean} defaults.hasFoot 是否有脚 有true 无false 默认false
             * @type {number} defaults.footStep 显示角标的间隔 默认为2
             * @type {Number} defaults.footLiWidth 每个角标的宽度
             * @type {boolean} defaults.range 滑动杆控件是否是选择区间 默认false 是true
             */
            var defaults = {
                orientation: '',
                start: 0,
                end: 10,
                step: 1,

                unitText: '',
                isShowSelectedBG: true,

                hasHead: false,
                title: '标题',
                headType: 'label',
                pattern: '^-?[1-9]\d*|0$',
                errorMessgae: '输入的值必须为数字',
                datasource: [],

                hasFoot: false,
                footStep: 2,

                range: false
            };

            var properties = {};

            lib.extend(properties, defaults, options);

            // 字符串转数字
            properties.start = +properties.start;
            properties.end = +properties.end;
            properties.step = +properties.step;

            // 处理min和max
            properties.min =
                typeof properties.min !== 'undefined' ? properties.min : properties.start;
            properties.max =
                typeof properties.max !== 'undefined' ? properties.max : properties.end;

            // min和max只能在start和end的中间
            properties.min = Math.max(properties.min, properties.start);
            properties.max = Math.min(properties.max, properties.end);

            // 水平、竖直滑动杆时的设置
            if (properties.orientation === 'vertical') {
                // 竖直滑动杆时参数的设置
                properties.leftTop = 'top';
                properties.rightBottom = 'bottom';
                properties.widthHeight = 'height';
                properties.cursorWH = 'height';
                properties.pageXY = 'pageY';
            }
            else {
                // 水平时参数的设置
                properties.leftTop = 'left';
                properties.rightBottom = 'right';
                properties.widthHeight = 'width';
                properties.cursorWH = 'width';
                properties.pageXY = 'pageX';
            }

            // 适配value的数据
            properties = adaptValue.call(this, properties);

            this.$super([properties]);
        };

        /**
         * 将字符串类型的值转换成原始格式,复杂类型的输入控件需要重写此接口
         *
         * @param {string} value 要转换的string
         * @param {Object} properties 参数对象
         * @return {Mixed}
         * @protected
         */
        exports.parseValue = function (value, properties) {
            if ((properties && properties.range) || this.range) {
                if (typeof value === 'string') {
                    var arr = value.split(',');
                    return [+arr[0], +arr[1]];
                }
            }

            return value;
        };

        /**
         * 适配控件的value
         * @param  {Object} properties 参数
         * @return {Object}            适配后的参数
         */
        function adaptValue (properties) {

            var value = properties.value;
            delete properties.value;

            if (value != null && properties.rawValue == null) {
                properties.rawValue = this.parseValue(value, properties);
            }

            properties.min =
                typeof properties.min !== 'undefined' ? properties.min : this.min;
            properties.max =
                typeof properties.max !== 'undefined' ? properties.max : this.max;

            if (properties.range || this.range) {
                // 值类型是区间时
                properties.rawValue =
                    typeof properties.rawValue === 'undefined'
                        ? [properties.min, properties.max] : properties.rawValue;

                // 结果是区间时
                properties.minRangeValue = properties.rawValue[0];
                properties.maxRangeValue = properties.rawValue[1];

                properties.minRangeValue =
                    Math.max(properties.minRangeValue, properties.min);
                properties.maxRangeValue =
                    Math.min(properties.maxRangeValue, properties.max);

                // value只能在[min, max]之间
                properties.rawValue = [
                    properties.minRangeValue,
                    properties.maxRangeValue
                ];
            }
            else {
                // 值类型是单个值时
                properties.rawValue =
                    typeof properties.rawValue === 'undefined'
                        ? properties.min : properties.rawValue;

                // value只能在min 和 max中间
                properties.rawValue = Math.max(properties.rawValue, properties.min);
                properties.rawValue = Math.min(properties.rawValue, properties.max);
            }

            return properties;
        }

        /**
         * 批量设置控件的属性值
         * @param {Object} properties 属性值集合
         * @override
         */
        exports.setProperties = function (properties) {

            // 给控件设值的时候适配数据用
            if (properties.hasOwnProperty('rawValue')) {
                properties = adaptValue.call(this, properties);

            }

            this.$super([properties]);
        };

        /**
         * 创建滑动杆的头部,
         * 有标题、显示值的label或输入框(文本框或者下拉框),
         * 放在原型里为了可以重写
         * @return {Panel} 返回Panel的对象
         * @protected
         */
        exports.createHead = function () {
            if (this.hasHead) {
                // 有头
                var headTpl = '<label for="${headValueDomId}" class="${headLabelClasses}">'
                                + '${title}:'
                            + '</label>';

                var headData = {
                    title: this.title,
                    value: this.rawValue,
                    headLabelClasses: this.helper.getPartClasses('head-label').join(),
                    headValueClasses: this.helper.getPartClasses('head-value').join(),
                    headValueDomId: this.helper.getId('head-value'),
                    headValueId: this.id + '-head-value'
                };

                switch (this.headType) {
                    case 'textbox':
                        // textbox时

                        headTpl += '<div class="${headValueClasses} ${headTextBoxClasses}"'
                                    + 'data-ui="id:${headValueId};childName:headValue;type:TextBox;'
                                    + 'required:required;value:${value};'
                                    + '${max}${min}${pattern}${errorMessgae}">'
                                + '</div>';

                        headData.headTextBoxClasses =
                            this.helper.getPartClasses('head-textbox').join();

                        headData.max =
                            typeof this.max !== 'undefined' ? ('max:' + this.max + ';') : '';
                        headData.min =
                            typeof this.min !== 'undefined' ? ('min:' + this.min + ';') : '';

                        headData.pattern =
                            this.pattern ? ('pattern:' + this.pattern + ';') : '';

                        headData.errorMessgae = this.errorMessgae
                            ? ('patternErrorMessage:' + this.errorMessgae + ';') : '';


                        break;
                    case 'select':
                        // select时

                        headTpl += '<div class="${headValueClasses} ${headSelectClasses}"'
                                    + 'data-ui="id:${headValueId};childName:headValue;type:Select;'
                                    + 'value:${value};">'
                                + '</div>';

                        headData.headSelectClasses = this.helper.getPartClasses('head-select').join();

                        break;
                    default:
                        // 其它情况都当label处理

                        headTpl += '<span class="${headValueClasses} ${headSpanClasses}"'
                                    + 'id="${headValueDomId}">'
                                    + '${value}'
                                + '</span>';

                        headData.headSpanClasses = this.helper.getPartClasses('head-span').join();

                        break;
                }

                // 滑动杆的数值有单位
                if (this.unitText) {
                    headData.unitText = this.unitText;
                    headData.headUnitClasses =
                        this.helper.getPartClasses('head-unit').join();
                    headTpl += '<span class="${headUnitClasses}">${unitText}</span>';
                }

                var headHtml = lib.format(headTpl, headData);
                var headDom = this.helper.createPart('head');

                headDom.innerHTML = headHtml;
                this.main.appendChild(headDom);

                var options = {
                    main: headDom,
                    renderOptions: this.renderOptions
                };

                // 创建个panel来做容器
                var panel = esui.create('Panel', options);

                panel.initChildren();
                this.addChild(panel, 'head');

                if (this.headType === 'select') {
                    // select时 设置数据源
                    var select = panel.getChild('headValue');

                    select.setProperties(
                        {
                            datasource: this.datasource,
                            value: this.value
                        }
                    );
                }

                return panel;
            }
        };

        /**
         * 获取头部的元素
         * @return {[type]} [description]
         */
        exports.getHeadTarget =  function () {
            if (!this.hasHead) {
                return null;
            }

            var headTarget;

            if (this.headType === 'label') {
                // label
                headTarget = this.helper.getPart('head-value');
            }
            else {
                // textbox 或 select
                headTarget = this.getChild('head').getChild('headValue');
            }

            return headTarget;
        };

        /**
         * 创建滑动杆体
         * 有滑块的范围和滑块,
         * 滑块的范围分为显示的范围、已选的范围
         * 滑动杆可能有一个滑块或两个滑块,类型是区间时可能有两个滑块,最大值和最小值
         * 任意一个是显示的起始值时显示一个滑块
         * 放在原型里是为了可重写
         * @protected
         */
        exports.createBody = function () {
            var bodyElement = this.bodyElement = this.helper.createPart('body');
            var cursorElement = this.cursorElement = this.helper.createPart('body-cursor');

            bodyElement.appendChild(cursorElement);

            // 区间时需要两个滑块
            if (this.range) {
                var cursorElementTwo
                    = this.cursorElementTwo
                    = this.helper.createPart('body-cursortwo');

                lib.addClass(this.cursorElementTwo, this.helper.getPartClasses('body-cursor'));

                bodyElement.appendChild(cursorElementTwo);
            }

            // 已选择的范围加个背景色
            if (this.isShowSelectedBG) {
                // 已选择的区间元素
                var bodySelectedElement
                    = this.bodySelectedElement
                    = this.helper.createPart('body-selected');

                bodyElement.appendChild(bodySelectedElement);
            }

            this.main.appendChild(bodyElement);


            // 初始化body内元素的宽度和位置
            initBodyElements(this);
        };

        exports.footTemplate = '<li>${text}${unitText}</li>';

        /**
         * 创建滑动杆的脚
         * 脚就是一些数值的标尺显示
         * 放在原型上是为了可重写
         * @protected
         */
        exports.createFoot = function () {
            if (!this.hasFoot) {
                return;
            }

            var footElement = this.helper.getPart('foot');

            // 有就移除 重新创建
            if (footElement) {
                lib.removeNode(footElement);
            }

            footElement = this.helper.createPart('foot', 'ul');

            var footHtml = '';
            // 显示的角标个数为len+1
            var len = (this.end - this.start) / this.footStep;
            // 每个角标的宽度
            var footStepWidth = this.width / len;
            // 控件值后面显示的单位
            var unitText = '';

            if (this.unitText) {
                unitText = this.unitText;
            }

            // 创建角标的元素
            for (var i = 0; i <= len; i++) {
                var data = {
                    text: this.start + i * this.footStep,
                    unitText: unitText
                };
                footHtml += lib.format(this.footTemplate, data);
            }
            footElement.innerHTML = footHtml;
            this.main.appendChild(footElement);

            u.each(
                lib.getChildren(footElement),
                function (elem, index) {
                    elem.style.left = (index * footStepWidth - lib.getOffset(elem).width / 2) + 'px';
                }
            )

        };

        /**
         * 初始化dom结构,仅在第一次渲染的时候使用
         * @protected
         * @override
         */
        exports.initStructure = function () {
            // 竖直滑动杆时增加样式
            if (this.orientation === 'vertical') {
                lib.addClass(this.main, this.helper.getPartClasses('vertical')[0]);
            }

            /\d+/.test(this.size) && (this.main.style[this.widthHeight] = this.size + 'px');
            this.createHead();
            this.createBody();
            this.createFoot();
        };

        /**
         * 绑定滑块拖拽的事件
         * @private
         */
        function bindCursorEvents() {
            var body = this.helper.getPart('body');

            // 给滑块绑定事件
            if (body) {
                initDragEvents(this, body);
            }
        }

        function returnFalse() {
            return false;
        }

        /**
         * 根据滑块left或top的值来计算value
         * @param  {number} cursorLeftTop 滑块位置left或top的值
         * @return {number}      滑块的值
         * @private
         */
        function getValueByLeftTop(cursorLeftTop) {
            var widthHeight = this.widthHeight;

            // 滑块的宽度
            var cursorWH = getCursorWH(this.cursorElement, widthHeight);
            // 滑块容器的宽度
            var tmpWidthHeight = this[widthHeight];
            // 选择的宽度
            var selectedWidthHeight = cursorLeftTop + cursorWH / 2;
            var similarValue =
                (selectedWidthHeight / tmpWidthHeight) * (this.end - this.start);

            // 根据步长算值
            similarValue = similarValue - similarValue % this.step;

            var value = this.start + Math.round(similarValue);

            return value;
        }

        /**
         * 根据值获取滑块的位置
         * @param  {number} value 滑块的值
         * @return {number}       滑块的左侧位置
         * @private
         */
        function getLeftTopByValue(value) {
            var widthHeight = this.widthHeight;
            var cursorWH = getCursorWH(this.cursorElement, widthHeight);

            var tmpwidthHeight = this[widthHeight];
            var start = this.start;
            var end = this.end;

            var cursorLeftTop =
                (value - start) / (end - start) * tmpwidthHeight - cursorWH / 2;

            return cursorLeftTop;
        }

        /**
         * 根据值去做相应的调整,包括head里显示、赋值和微调滑块的位置
         * 为啥要微调位置,因为你不知道鼠标会停在哪,比如1,2之间跨度太大时 要落到值上
         * @param {Slider} slider 滑动杆控件
         * @param {number} value  滑动杆的值
         * @param {boolean} isSyncValue 需要设置控件的值true 不需要false
         */
        function setByValue(slider, value, isSyncValue) {
            var cursorElement = slider.cursorElement;
            var cursorLeftTop;

            var leftTop = slider.leftTop;
            var widthHeight = slider.widthHeight;

            if (slider.range) {
                var cursorElementTwo = slider.cursorElementTwo;
                var cursorLeftTopTwo = getLeftTopByValue.call(slider, value[1]);

                cursorElementTwo.style[leftTop] = cursorLeftTopTwo + 'px';
                cursorLeftTop = getLeftTopByValue.call(slider, value[0]);

                // hack: 默认第一个滑块的z-index是2 第二个滑块的z-index的是3
                // 因为区间的值可以是2,2这种,当两个滑块值是这种切最大值时,这时只能滑块1可拖动
                // 这时要把它放在第二个滑块上面
                if (value[0] === value[1] && value[0] === slider.max) {
                    cursorElement.style.zIndex = 3;
                    cursorElementTwo.style.zIndex = 2;
                }
                else {
                    cursorElement.style.zIndex = 2;
                    cursorElementTwo.style.zIndex = 3;
                }
            }
            else {
                cursorLeftTop = getLeftTopByValue.call(slider, value);
            }

            // 调整滑块的位置
            cursorElement.style[leftTop] = cursorLeftTop + 'px';

            // 已选择的部分加个背景色显示
            if (slider.isShowSelectedBG) {

                var cursorWH = getCursorWH(slider.cursorElement, widthHeight);
                if (slider.range) {
                    slider.bodySelectedElement.style[leftTop] = cursorLeftTop + cursorWH / 2 + 'px';

                    slider.bodySelectedElement.style[widthHeight] = cursorLeftTopTwo - cursorLeftTop + 'px';
                }
                else {
                    slider.bodySelectedElement.style[widthHeight] = cursorLeftTop + cursorWH / 2 + 'px';
                }
            }

            // 内部文本框或者下拉框触发的此操作 不需要再同步它们的值了 别的都需要
            if (isSyncValue) {
                syncValue(slider, value);
            }
        }

        /**
         * 绑定头部的事件
         * @param  {Slider} slider slider控件对象
         */
        function bindHeadEvents(slider) {
            if (!slider.hasHead) {
                return;
            }

            // 获取头元素
            var headTarget = slider.getHeadTarget();

            if (headTarget.type === 'TextBox') {
                headTarget.on('blur', blurHandler, slider);
            }
            else if (headTarget.type === 'Select') {
                headTarget.on('change', changeHandler, slider);
            }
        }

        /**
         * 解除头部事件的绑定
         * @param  {Slider} slider slider控件对象
         */
        function unbindHeadEvents(slider) {
            if (!slider.hasHead) {
                return;
            }

            // 获取头部的元素
            var headTarget = slider.getHeadTarget();

            if (headTarget.type === 'TextBox') {
                headTarget.un('blur');
            }
            else if (headTarget.type === 'Select') {
                headTarget.un('change');
            }
        }

        /**
         * 同步控件内数值的显示
         * @param {Slider} slider 滑杆对象
         * @param {number} value  滑杆的值
         */
        function syncValue(slider, value) {
            // 同步头部的值
            if (slider.hasHead) {
                // 获取头元素
                var headTarget = slider.getHeadTarget();

                if (headTarget instanceof InputControl) {
                    // 先去掉绑定的事件 防止循环调用
                    unbindHeadEvents(slider);

                    headTarget.setProperties(
                        {
                            value: value
                        }
                    );

                    // 再捆绑事件
                    bindHeadEvents(slider);
                }
                else {
                    // label时
                    headTarget.innerHTML = value;
                }
            }
        }

        /**
         * 鼠标移动的事件
         * @param {Event} e 事件对象
         * @param {boolean} isMouseUp 是否是鼠标松开的触发 是为true 不是为false
         * @return {number} 返回value 让mouseup用
         * @private
         */
        function mousemoveHandler(e, isMouseUp) {
            var target = this.activeCursorElement;
            var cursorElement = this.cursorElement;
            var mousePos = lib.event.getMousePosition(e);

            var pageXY = this.pageXY;
            var leftTop = this.leftTop;
            var widthHeight = this.widthHeight;

            // 拖动的滑块距left的值
            var cursorLeftTop;

            // 滑块区间的时候
            if (this.range) {
                // 拖动的是否是第一个滑块
                var isFirst = false;
                // 另外一个滑块的left
                var otherLeftTop;
                // 另一个滑块的值
                var otherValue;

                // 滑块是第一个时
                if (target.id === cursorElement.id) {
                    otherLeftTop = getLeftTopByValue.call(this, this.maxRangeValue);
                    otherValue = this.maxRangeValue;
                    isFirst = true;

                    cursorLeftTop =
                        Math.max(
                            this.minStartPos - this.startPos,
                            mousePos[pageXY] - this.startPos
                        );

                    cursorLeftTop = Math.min(cursorLeftTop, otherLeftTop);
                }
                else {
                    // 滑块是第二个时
                    otherLeftTop = getLeftTopByValue.call(this, this.minRangeValue);
                    otherValue = this.minRangeValue;

                    cursorLeftTop =
                        Math.max(otherLeftTop, mousePos[pageXY] - this.startPos);

                    cursorLeftTop = Math.min(cursorLeftTop, this.maxEndPos - this.startPos);
                }
            }
            else {
                target = cursorElement;

                cursorLeftTop = Math.max(
                    this.minStartPos - this.startPos,
                    mousePos[pageXY] - this.startPos
                );

                cursorLeftTop = Math.min(
                    cursorLeftTop,
                    this.maxEndPos - this.startPos
                );
            }

            // 根据left来计算值
            var value;
            var curValue = getValueByLeftTop.call(this, cursorLeftTop);

            if (this.range) {
                if (isFirst) {
                    value = [curValue, otherValue];
                }
                else {
                    value = [otherValue, curValue];
                }
            }
            else {
                value = curValue;
            }

            if (!isMouseUp) {
                // 避免抖动,这里根据value值重新计算出leftTop
                cursorLeftTop = getLeftTopByValue.call(this, curValue);
                target.style[this.leftTop] = cursorLeftTop + 'px';

                // 已选择的部分加个背景色显示
                if (this.isShowSelectedBG) {
                    if (this.range) {

                        var tmpWidthHeight;

                        if (isFirst) {
                            this.bodySelectedElement.style[leftTop] = cursorLeftTop + 'px';
                            tmpWidthHeight = otherLeftTop - cursorLeftTop;
                        }
                        else {
                            this.bodySelectedElement.style[leftTop] = otherLeftTop + 'px';
                            tmpWidthHeight = cursorLeftTop - otherLeftTop;
                        }

                        this.bodySelectedElement.style[widthHeight] = tmpWidthHeight + 'px';

                    }
                    else {
                        var cursorWH = getCursorWH(this.cursorElement, widthHeight);
                        this.bodySelectedElement.style[widthHeight]
                            = cursorLeftTop + cursorWH / 2 + 'px';
                    }
                }

                // 滑动的时候触发move事件
                this.fire('move', value);
            }

            // 同步头部的数值显示
            syncValue(this, value);

            return value;
        }

        /**
         * 鼠标的松开事件
         * @param {Event} e 事件的对象
         * @private
         */
        function mouseupHandler(e) {
            // 去掉active的样式
            lib.removeClass(
                this.activeCursorElement,
                this.helper.getPartClasses('body-cursor-active')[0]
            );

            // 放开和mousemove时做得事是一样的,再做一遍
            var value = mousemoveHandler.call(this, e, true);

            // 设置控件的值,因为是内部设值不涉及重绘,所以不调set*方法了
            this.rawValue = value;
            this.minRangeValue = value[0];
            this.maxRangeValue = value[1];

            setByValue(this, value, true);

            // 放开鼠标的时候触发change事件
            this.fire('change', value);

            var doc = document;

            this.helper.removeDOMEvent(doc, 'mouseup', mouseupHandler);
            this.helper.removeDOMEvent(doc, 'mousemove', mousemoveHandler);
            // 清除浏览器select的事件
            e.preventDefault();
        }

        /**
         * 初始化body内元素的坐标和宽度
         * @param  {Slider}  slider 滑动杆控件
         */
        function initBodyElements(slider) {
            var bodyElement = slider.bodyElement;
            var cursorElement = slider.cursorElement;
            // 获取滑块容器的位置
            var bodyPos = lib.getOffset(bodyElement);

            var leftTop = slider.leftTop;
            var rightBottom = slider.rightBottom;
            var widthHeight = slider.widthHeight;

            // 获取滑块容器的宽度,用来计算值用
            slider[widthHeight] = bodyPos[widthHeight];

            // 滑块能去的最左边
            if (typeof slider.min !== 'undefined') {
                var minLeftTop = getLeftTopByValue.call(slider, slider.min);
                // 滑块所能去的最左边
                slider.minStartPos = bodyPos[leftTop] + minLeftTop;
                // 滑竿范围的最左边
                slider.startPos = bodyPos[leftTop];
            }

            // 滑块能去的最右侧
            if (typeof slider.max !== 'undefined') {
                var maxLeftTop = getLeftTopByValue.call(slider, slider.max);

                slider.maxEndPos = bodyPos[leftTop] + maxLeftTop;
                slider.endPos = bodyPos[rightBottom];
            }
        }

        /**
         * 根据鼠标位置,寻找离鼠标位置最近的handle
         * @param {Event} e 事件对象
         * @private
         */
        function findNearestCursorElement(e) {
            var mousePos = lib.event.getMousePosition(e);
            var pageXY = this.pageXY;
            var leftTop = this.leftTop;
            var bodyElement = this.helper.getPart('body');
            var bodyPos = lib.getOffset(bodyElement);

            var mouseLeftTop = mousePos[pageXY] - bodyPos[leftTop];

            // 有两个滑块
            if (this.range) {
                var firstLeftTop = getLeftTopByValue.call(this, this.minRangeValue);
                var secondLeftTop = getLeftTopByValue.call(this, this.maxRangeValue);
                var middleLeftTop = firstLeftTop + (secondLeftTop - firstLeftTop) / 2;
                if (mouseLeftTop > middleLeftTop && this.cursorElementTwo) {
                    return this.cursorElementTwo;
                }
            }
            return this.cursorElement;
        }

        /**
         * 获取滑块的宽高,由于页面加载过程中存在css抖动,这个值最好即时获取
         */
        function getCursorWH(elem, widthHeight) {
            return parseInt(lib.getStyle(elem, widthHeight), 10);
        }

        /**
         * 鼠标的按下事件
         * @param {Event} e 事件对象
         * @private
         */
        function mousedownHandler(e) {
            var cursorElement = findNearestCursorElement.call(this, e);

            // 存住活动的对象
            this.activeCursorElement = cursorElement;

            // 增加active的样式
            lib.addClass(cursorElement, this.helper.getPartClasses('body-cursor-active')[0]);

            // 点击的时候再初始化各种坐标 为了一些初始化时不在屏幕内的控件
            initBodyElements(this);

            // 禁止鼠标反选
            e.preventDefault();

            // 滑块首先移动到鼠标点击位置
            mousemoveHandler.call(this, e);

            var doc = document;
            // 鼠标的松开事件
            this.helper.addDOMEvent(doc, 'mouseup', mouseupHandler);
            // 鼠标的移动事件
            this.helper.addDOMEvent(doc, 'mousemove', mousemoveHandler);

        }

        /**
         * 处理拖拽事件
         * @param  {Slider} slider  控件的实例
         * @param  {Element} element  处理事件的dom元素
         * @param  {boolean} unbind  绑定为false 解绑为true
         * @private
         */
        function initDragEvents(slider, element, unbind) {
            if (unbind) {
                slider.helper.removeDOMEvent(element, 'mousedown', mousedownHandler);
            }
            else {
                slider.helper.addDOMEvent(element, 'mousedown', mousedownHandler);
            }
        }

        /**
         * 头部文本框的blur处理事件
         * @param  {Event} e 事件对象
         * @private
         */
        function blurHandler(e) {
            var target = e.target;
            var isValid = target.validate();

            if (isValid) {
                // 验证通过
                var value = target.getValue();

                this.rawValue = +value;
                setByValue(this, value);
            }
        }

        /**
         * 头部下拉框的change处理事件
         * @param  {Event} e 事件对象
         * @private
         */
        function changeHandler(e) {
            var target = e.target;
            var value = target.getValue();

            this.value = +value;
            setByValue(this, value);
        }

        /**
         * 初始化事件的交互
         * @protected
         * @override
         */
        exports.initEvents = function () {
            // 绑定滑块的事件
            bindCursorEvents.call(this);

            // 绑定头部的事件
            bindHeadEvents(this);
        };

        /**
         * 获取滑动杆的值
         * @return {*} 滑动杆的值
         */
        exports.getValue = function () {
            var value;

            if (this.range) {
                value = [this.minRangeValue, this.maxRangeValue];
            }
            else {
                value = this.getRawValue();
            }

            return value;
        };

        /**
         * 重新渲染
         * @protected
         * @override
         * @type {Function} 重新渲染时要执行的函数
         */
        exports.repaint = require('esui/painters').createRepaint(
            InputControl.prototype.repaint,
            {
                name: 'rawValue',
                paint: function (slider, value) {
                    setByValue(slider, value, true);
                }
            }
        );

        /**
         * 销毁控件
         * @protected
         * @override
         */
        exports.dispose = function () {
            this.bodyElement = null;
            this.cursorElement = null;
            this.bodySelectedElement = null;
            this.activeCursorElement = null;

            if (this.range) {
                this.cursorElementTwo = null;
            }


            this.$super(arguments);
        };


        var Slider = require('eoo').create(InputControl, exports);
        esui.register(Slider);

        return Slider;
    }
示例#4
0
    function (require) {

        var eoo = require('eoo');
        var InputControl = require('esui/InputControl');

        var lib = require('esui/lib');
        var u = require('underscore');
        var m = require('moment');
        var ui = require('esui');
        var painters = require('esui/painters');
        var $ = require('jquery');

        require('esui/behavior/mousewheel');

        /**
         * 微调输入控件
         *
         * @class ui.Spinner
         * @extends esui.InputControl
         */
        var Spinner = eoo.create(
            InputControl,
            {

                /**
                 * 控件类型
                 *
                 * @type {string}
                 * @override
                 */
                type: 'Spinner',

                /**
                 * 微调输入控件
                 *
                 * @constructor
                 */
                constructor: function () {
                    this.$super(arguments);
                    // 短按计时器
                    this.timer = 0;
                    // 长按计时器
                    this.longTimer = 0;
                },

                initOptions: function (options) {
                    var properties = {};
                    u.extend(properties, Spinner.defaultProperties, options);

                    var format = properties.format;
                    var max = properties.max;
                    var min = properties.min;
                    if (format === 'number') {
                        max = max === 'indefinite' ? Number.MAX_VALUE : parseToNum(max);
                        min = min === 'indefinite' ? -(Number.MAX_VALUE) : parseToNum(min);
                    }
                    else {
                        max = max === 'indefinite' ? m().add(50, 'years') : m(max, format);
                        min = min === 'indefinite' ? m().subtract(50, 'years') : m(min, format);
                    }
                    properties.max = max;
                    properties.min = min;

                    var scale = properties.scale;
                    // 根据步长计算精度
                    if (format === 'number') {
                        scale = scale + '';
                        var dotPosition = scale.indexOf('.');
                        if (dotPosition > -1) {
                            properties.precision = scale.length - dotPosition - 1;
                        }
                        else {
                            properties.precision = 0;
                        }
                    }

                    // scale
                    if (format !== 'number' && /^\s*\{/.test(scale)) {
                        properties.scale = $.parseJSON(scale);
                    }

                    this.setProperties(properties);
                },

                /**
                 * 构建spinner
                 */
                initStructure: function () {
                    var helper = this.helper;

                    var spinnerTpl = [
                        '<input id="${inputId}" type="text" />',
                        '<span id="${upId}" class="${upClass} ${iconClass}"></span>',
                        '<span id=${downId} class="${downClass} ${iconClass}"></span>'
                    ].join('');

                    var mainElement = this.main;

                    mainElement.innerHTML = lib.format(
                        spinnerTpl,
                        {
                            inputId: helper.getId('input'),
                            upId: helper.getId('up'),
                            upClass: helper.getPartClasses('up'),
                            downId: helper.getId('down'),
                            downClass: helper.getPartClasses('down'),
                            iconClass: helper.getIconClass()
                        }
                    );

                    $(mainElement).addClass(helper.getPrefixClass('textbox'));
                },

                /**
                 * 初始化spinner事件
                 */
                initEvents: function () {
                    var helper = this.helper;
                    var up = helper.getPart('up');
                    var down = helper.getPart('down');

                    helper.addDOMEvent(up, 'mousedown', mouseDownHandler);
                    helper.addDOMEvent(down, 'mousedown', mouseDownHandler);
                    helper.addDOMEvent(up, 'mouseup',  mouseUpHandler);
                    helper.addDOMEvent(down, 'mouseup',  mouseUpHandler);
                    helper.addDOMEvent(up, 'mouseout',  mouseUpHandler);
                    helper.addDOMEvent(down, 'mouseout',  mouseUpHandler);

                    // focus时支持鼠标滚轮
                    var input = this.getInput();
                    helper.addDOMEvent(
                        input,
                        'focus',
                        function () {
                            helper.addDOMEvent(this.main, 'mousewheel', mouseWheelHandler);
                        }
                    );

                    helper.addDOMEvent(
                        input,
                        'blur',
                        function () {
                            helper.removeDOMEvent(this.main, 'mousewheel', mouseWheelHandler);
                        }
                    );
                },

                /**
                 * 渲染自身
                 *
                 * @override
                 * @protected
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: ['rawValue'],
                        paint: function (spinner, rawValue) {
                            var max = spinner.max;
                            var min = spinner.min;
                            var format = spinner.format;
                            if (spinner.format === 'number') {
                                rawValue = parseToNum(rawValue);
                                rawValue = Math.max(rawValue, min);
                                rawValue = Math.min(rawValue, max);
                            }
                            else {
                                rawValue = m(rawValue, format);
                                if (rawValue.isValid()) {
                                    rawValue = m.max(rawValue, min);
                                    rawValue = m.min(rawValue, max);
                                }
                                else {
                                    rawValue = min;
                                }
                                rawValue = m(rawValue, format).format(format);
                            }
                            setInputValue.call(spinner, rawValue);
                        }
                    },
                    {
                        name: ['width'],
                        paint: function (spinner, width) {
                            width = parseInt(width, 10);
                            spinner.main.style.width = width + 'px';
                        }
                    },
                    {
                        name: ['disabled', 'readOnly'],
                        paint: function (spinner, disabled, readOnly) {
                            var input = spinner.getInput();
                            input.disabled = disabled;
                            input.readOnly = readOnly;
                        }
                    }
                ),

                /**
                 * 获取input元素
                 *
                 * @return {Element}
                 */
                getInput: function () {
                    return lib.g(this.helper.getId('input'));
                },

                /**
                 * 获取控件值
                 *
                 * @return {string}
                 */
                getValue: function () {
                    var input = this.getInput();
                    return input.value;
                },

                /**
                 * 设置控件值
                 *
                 * @param {string} value 控件值
                 */
                setValue: function (value) {
                    setInputValue.call(this, value);
                }
            }
        );

        /**
         * 解析数字类型方法
         *
         * @param {string} value 要解析的值
         * @return {string}
         */
        function parseToNum(value) {
            if (value) {
                value = parseFloat(value);
            }
            return isNaN(value) ? '' : value;
        }

        /**
         * 微调方向枚举值
         *
         * @enum {string}
         */
        var Direct = {
            UP: 'up',
            DOWN: 'down'
        };

        /**
         * 更新日期类型方法
         *
         * @param {Direct} direct up / down
         */
        function updateDate(direct) {
            var input = this.getInput();
            var scale = typeof this.scale === 'object' ? this.scale : parseToNum(this.scale);
            var timeFormat = this.format;
            var value = m(input.value, timeFormat);
            var max = this.max;
            var min = this.min;

            // 如果用户手动输入一个非法值,会默认显示最小值
            value = value.isValid() ? value : min;
            if (direct === Direct.UP) {
                value = value.add(scale.value, scale.key);
                if (m.max(value, max) === max) {
                    value = m(value, timeFormat).format(timeFormat);
                }
                else {
                    if (!!this.turn && this.turn !== 'false') {
                        value = m(min, timeFormat).format(timeFormat);
                    }
                    else {
                        value = m(max, timeFormat).format(timeFormat);
                    }
                }
            }
            else {
                value = value.subtract(scale.value, scale.key);
                if (m.min(value, min) === min) {
                    value = m(value, timeFormat).format(timeFormat);
                }
                else {
                    if (!!this.turn && this.turn !== 'false') {
                        value = m(max, timeFormat).format(timeFormat);
                    }
                    else {
                        value = m(min, timeFormat).format(timeFormat);
                    }
                }
            }
            setInputValue.call(this, value);
        }

        /**
         * 更新数值类型的方法
         *
         * @param {Direct} direct up / down
         */
        function updateNumber(direct) {
            var input = this.getInput();
            var scale = parseToNum(this.scale);
            var value = parseToNum(input.value);
            var max = this.max;
            var min = this.min;
            if (direct === Direct.UP) {
                value += scale;
                if (value > max) {
                    if (!!this.turn && this.turn !== 'false') {
                        value = min;
                    }
                    else {
                        value = max;
                    }
                }
            }
            else {
                value -= scale;
                if (value < min) {
                    if (!!this.turn && this.turn !== 'false') {
                        value = max;
                    }
                    else {
                        value = min;
                    }
                }
            }
            setInputValue.call(this, value);
        }

        /**
         * 更新值方法,用来判断值类型是数字类型还是时间类型
         *
         * @param {Direct} direct 方向
         */
        function updateValue(direct) {
            if (this.format !== 'number') {
                updateDate.call(this, direct);
            }
            else {
                updateNumber.call(this, direct);
            }
        }

        /**
         * 改变value方法,该方法会触发 scrollValue 事件
         * 如果用户想自定义方法,可以通过preventDefault()阻止默认行为
         *
         * @param {Event} e 事件对象
         */
        function scrollValue(e) {
            if (!this.disabled && !this.readOnly) {
                var direct = (e.target.id === this.helper.getId('up')) ? Direct.UP : Direct.DOWN;
                var args = {
                    direct: direct
                };
                var eventArgs = this.fire('scrollValue', args);
                if (!eventArgs.isDefaultPrevented()) {
                    updateValue.call(this, direct);
                }
            }
        }

        /**
         * 长按按钮自动更新方法
         * 长按3秒时,速度加倍
         *
         * @param {Event} e 事件对象
         */
        function autoUpdate(e) {
            var me = this;
            this.timer = setInterval(
                function () {
                    return scrollValue.call(me, e);
                },
                +parseToNum(this.timeInterval)
            );
            this.longTimer = setTimeout(
                function () {
                    clearInterval(me.timer);
                    me.timer = setInterval(
                        function () {
                            return scrollValue.call(me, e);
                        },
                        parseToNum(this.timeInterval) / 2
                    );
                },
                3000
            );
        }

        /**
         * 鼠标滚轮方法
         *
         * @param {Event} e 事件对象
         * @param {Direct} direct 方向
         */
        function mouseWheelHandler(e, direct) {
            direct = direct === 1 ? Direct.UP : Direct.DOWN;
            updateValue.call(this, direct);
            e.preventDefault();
        }

        /**
         * 鼠标点击方法
         *
         * @param {Event} e 事件对象
         */
        function mouseDownHandler(e) {
            var delayTime = 1200 - this.timeInterval;
            scrollValue.call(this, e);
            var me = this;
            this.timer = setTimeout(
                function () {
                    return autoUpdate.call(me, e);
                },
                delayTime
            );
            // 阻止鼠标双击后反选控件其他部分
            e.preventDefault();
        }

        /**
         * 取消事件方法
         *
         * @param {Event} e 事件对象
         */
        function mouseUpHandler(e) {
            clearInterval(this.timer);
            clearTimeout(this.timer);
            clearTimeout(this.longTimer);
            this.timer = 0;
            this.longTimer = 0;
        }


        function setInputValue(value) {
            if (this.precision) {
                value = value.toFixed(this.precision);
            }
            var input = this.getInput();
            input.value = value;
        }

        /**
         * Spinner默认属性
         * turn: 当值到边界时是否反转
         * scale: 刻度单位, 如果format为 number 类型,则为数字类型, 如果format为 日期类型,则为Object类型,格式为:
         *          {
         *           key:   ***,    //时间单位,如 'days', 'years'等,具体请参考 {@link moment}
         *           value: ***     //单位时间, 数字类型
         *          }
         * max: 上界
         * min: 下界
         * format: 值的格式,包括 number 和 日期 两种,如果使用日期格式,format按照{@link moment}的格式进行设置
         * timeInterval: 长按按钮时,数值滚动的时间间隔
         * @type {{spaceHolder: string, turn: boolean, scale: number, range: Array}}
         */
        Spinner.defaultProperties = {
            turn: true,
            scale: 1,
            width: 200,
            height: 30,
            max: 'indefinite',
            min: 'indefinite',
            format: 'number',
            timeInterval: 100
        };

        ui.register(Spinner);

        return Spinner;
    }
示例#5
0
    function (require) {
        var lib = require('./lib');
        var ui = require('esui');
        var Control = require('./Control');

        require('./TextBox');
        require('./Button');

        /**
         * 搜索框控件,由一个文本框和一个搜索按钮组成
         *
         * @extends Control
         * @param {Object} [options] 初始化参数
         * @constructor
         */
        function SearchBox(options) {
            Control.apply(this, arguments);
        }

        SearchBox.prototype.type = 'SearchBox';

        /**
         * 初始化参数
         *
         * @param {Object} [options] 构造函数传入的参数
         * @protected
         * @override
         */
        SearchBox.prototype.initOptions = function (options) {
            var properties = {};
            lib.extend(properties, options);

            if (properties.disabled === 'false') {
                properties.disabled = false;
            }

            if (lib.isInput(this.main)) {
                if (!properties.placeholder) {
                    properties.placeholder =
                        lib.getAttribute(this.main, 'placeholder');
                }

                if (!properties.text) {
                    properties.text = this.main.value;
                }

                if (!properties.maxLength && ( lib.hasAttribute(this.main, 'maxlength') || this.main.maxLength > 0)) {
                    properties.maxLength = this.main.maxLength;
                }
            }
            //TODO: custom elments 的兼容
            else {
                if (!properties.text) {
                    properties.text = lib.getText(this.main);
                }
            }

            if (!properties.title) {
                properties.title = this.main.title;
            }

            Control.prototype.initOptions.call(this, properties);
        };

        /**
         * 初始化DOM结构
         *
         * @protected
         * @override
         */
        SearchBox.prototype.initStructure = function () {
            // 一个搜索框由一个文本框和一个按钮组成
            var textboxOptions = {
                mode: 'text',
                childName: 'text',
                height: this.height,
                viewContext: this.viewContext,
                placeholder: this.placeholder
            };

            if (lib.isInput(this.main)) {
                this.helper.replaceMain();
            }

            var textbox = ui.create('TextBox', textboxOptions);
            textbox.appendTo(this.main);
            this.addChild(textbox);

            var buttonOptions = {
                main: document.createElement('span'),
                childName: 'button',
                content: '搜索',
                viewContext: this.viewContext
            };
            var button = ui.create('Button', buttonOptions);
            button.appendTo(this.main);
            this.addChild(button);
        };

        /**
         * 初始化事件交互
         *
         * @protected
         * @override
         */
        SearchBox.prototype.initEvents = function () {
            var textbox = this.getChild('text');
            var delegate = require('mini-event').delegate;

            delegate(textbox, this, 'input');
            delegate(textbox, 'enter', this, 'search');
            // 回车时要取消掉默认行为,否则会把所在的表单给提交了
            textbox.on(
                'keypress',
                function (e) {
                    if (e.keyCode === 13) {
                        e.preventDefault();
                    }
                }
            );
            textbox.on('focus', focus, this);
            textbox.on('blur', lib.bind(this.removeState, this, 'focus'));

            var button = this.getChild('button');
            button.on('click', click, this);
        };

        function focus() {
            this.removeState('clear');
            this.addState('focus');
        }

        function click() {
            if (this.hasState('clear')) {
                this.getChild('text').setValue('');
                this.removeState('clear');
            }
            this.fire('search');
        }

        /**
         * 获取输入值
         *
         * @return {string}
         * @override
         */
        SearchBox.prototype.getValue = function () {
            var text = this.getChild('text');
            return text.getValue();
        };

        var paint = require('./painters');

        /**
         * 渲染自身
         *
         * @protected
         * @override
         */
        SearchBox.prototype.repaint = paint.createRepaint(
            Control.prototype.repaint,
            paint.attribute('title'),
            {
                name: [
                    'maxLength', 'placeholder', 'text',
                    'width', 'disabled', 'readOnly'
                ],
                paint: function (box, maxLength, placeholder, text, width, disabled, readOnly) {
                    var properties = {
                        /**
                         * @property {number} maxLength
                         *
                         * 最大长度,参考{@link TextBox#maxLength}
                         */
                        maxLength: maxLength,
                        /**
                         * @property {string} placeholder
                         *
                         * 无内容时的提示文字,参考{@link TextBox#placeholder}
                         */
                        placeholder: placeholder,
                        /**
                         * @property {string} text
                         *
                         * 文字内容
                         */
                        value: text,

                        /**
                         * @property {number} width
                         *
                         * 设定文本框宽度,参考{@link TextBox#width}
                         */
                        width: width,
                        disabled: disabled,
                        readOnly: readOnly
                    };
                    box.getChild('text').setProperties(properties);
                }
            },
            {
                name: 'disabled',
                paint: function (box, disabled) {
                    if (disabled === 'false') {
                        disabled = false;
                    }

                    var button = box.getChild('button');
                    button.set('disabled', disabled);
                }
            },
            {
                /**
                 * @property {boolean} fitWidth
                 *
                 * 设定当前控件是否独占一行宽度
                 */
                name: 'fitWidth',
                paint: function (box, fitWidth) {
                    var method = fitWidth ? 'addState' : 'removeState';
                    box[method]('fit-width');
                }
            }
        );

        /**
         * 获取用于比对的text属性值
         *
         * @return {string}
         * @protected
         */
        SearchBox.prototype.getTextProperty = function () {
            var textbox = this.getChild('text');
            return textbox ? textbox.getValue() : this.text;
        };

        lib.inherits(SearchBox, Control);
        ui.register(SearchBox);
        return SearchBox;
    }
示例#6
0
define(function (require) {
    var u = require('underscore');
    var esui = require('esui');
    var lib = require('esui/lib');
    var Control = require('esui/Control');
    var eoo = require('eoo');
    var $ = require('jquery');
    var painters = require('esui/painters');
    var previewHelper = require('./helper/previewHelper');

    /**
     * 图片,视频和Flash预览控件
     *
     * 显示图片,视频和Flash资源,并提供配置
     *
     * @extends {Control}
     * @param {Object} options 初始化参数
     * @constructor
     */
    var MediaPreview = eoo.create(
        Control,
        {
            /**
             * 控件类型,始终为`"MediaPreview"`
             *
             * @type {string}
             * @readonly
             * @override
             */
            type: 'MediaPreview',

            /**
             * 初始化参数
             *
             * 对于图片资源,显示的宽度为容器宽度
             * 而高度是默认高度,最大高度为容器高度
             *
             * @param {Object} [options] 构造函数传入的参数
             * @protected
             * @override
             */
            initOptions: function (options) {
                var properties = {
                    /**
                     * @property {number} [width=300]
                     *
                     * 默认宽度
                     */
                    width: 300,
                    /**
                     * @property {number} [width=500]
                     *
                     * 默认高度
                     */
                    height: 500,
                    /**
                     * @property {number} [width=0]
                     *
                     * 预览media要显示的宽度
                     */
                    mediaWidth: 0,
                    /**
                     * @property {number} [width=0]
                     *
                     * 预览media要显示的高度
                     */
                    mediaHeight: 0,
                    /**
                     * @property {String} [toolRole='']
                     *
                     * 用以标注添加工具icon的属性,要以','分隔,并由role生成相应的class
                     */
                    toolRole: '',
                    /**
                     * @property {String} [sourceURL='']
                     *
                     * 资源路径
                     */
                    sourceURL: '',
                    /**
                     * @property {String} [sourceType='']
                     *
                     * 资源的类别,共支持以下四种类型
                     * 1.image: 图片类型
                     * 2.flash: 带有'swf'后缀名的flash文件
                     * 3.flv: 带有'flv'后缀名的视频文件
                     * 4.video: 带有'mp4|mov|mkv|mpg|avi|rmvb|rm|ogg|wmv|mp3|wma|mid'后缀名的文件
                     */
                    sourceType: '',
                    /**
                     * @property {String} [swfPath='']
                     *
                     * 要使用的视频播放器路径
                     */
                    swfPath: ''
                };

                u.extend(properties, MediaPreview.defaultProperties, options);

                this.setProperties(properties);
            },

            /**
             * 初始化DOM结构
             *
             * @protected
             * @override
             */
            initStructure: function () {
                var helper = this.helper;

                var tpl = [
                    '<div id="${containerId}" class="${containerClass}">',
                        '<div id="${bodyId}" class="${bodyClass}"></div>',
                        '<div id="${toolId}" class="${toolClass}"></div>',
                    '</div>'
                ].join('');

                var mainElement = this.main;

                mainElement.innerHTML = lib.format(
                    tpl,
                    {
                        containerId: helper.getId('container'),
                        containerClass: helper.getPartClasses('container'),
                        bodyId: helper.getId('body'),
                        bodyClass: helper.getPartClasses('body'),
                        toolId: helper.getId('tool'),
                        toolClass: helper.getPartClasses('tool')
                    }
                );
            },

            /**
             * 重渲染
             *
             * @method
             * @protected
             * @override
             */
            repaint: painters.createRepaint(
                Control.prototype.repaint,
                /**
                 * @property {String} sourceURL
                 * @property {String} sourceType
                 * @property {number} mediaWidth
                 * @property {number} mediaHeight
                 *
                 * 资源的路径以及type
                 */
                {
                    name: ['sourceURL', 'sourceType', 'mediaWidth', 'mediaHeight'],
                    paint: function (mediaPreview, sourceURL, sourceType) {
                        initBody.call(mediaPreview);
                    }
                },
                /**
                 * @property {String} toolRole
                 *
                 * 工具栏工具的自定义属性,以','分隔,用来标志工具icon
                 */
                {
                    name: ['toolRole'],
                    paint: function (mediaPreview, toolRole) {
                        initToolBar.call(mediaPreview);
                    }
                },
                /**
                 * @property {number} width
                 * @property {number} height
                 *
                 * 文本框的宽度及高度
                 */
                {
                    name: ['width', 'height'],
                    paint: function (mediaPreview, width, height) {
                        $(mediaPreview.main).width(width).height(height);
                    }
                }
            ),

            /**
             * 初始化事件交互
             *
             * @protected
             * @override
             */
            initEvents: function () {
                var me = this;

                // 为所有的具有'data-role="tool"'属性的span节点添加点击事件
                // 并在该控件上fire一个'iconclick'的事件,参数是点击的span的toolRole
                this.helper.addDOMEvent(
                    this.helper.getPart('tool'),
                    'click',
                    '[data-role="tool"]',
                    function (event) {
                        var toolNode = event.target;
                        me.fire('iconclick', {roleName: $(toolNode).attr('data-tool-role')});
                    }
                );
            }
        }
    );

    /**
     * 默认属性值
     *
     * @type {Object}
     * @public
     */
    MediaPreview.defaultProperties = {
        errorHTML: '无法预览该内容!'
    };

    /**
     * 构造预览资源的html结构
     *
     * @ignore
     */
    function initBody() {
        var errorTpl = '<p class="${errorClass}">' + this.errorHTML + '</p>';

        var width = u.min([this.mediaWidth || this.width, this.width]);
        var height = u.min([this.mediaHeight || this.height, this.height]);
        if (this.sourceType === 'image') {
            width = '100%';
            height = 'auto';
        }
        var html = previewHelper.preview({
            width: width,
            height: height,
            url: this.sourceURL,
            type: this.sourceType,
            swfPath: this.swfPath
        });

        // 如果previewHelper中无法将其渲染出来,这里要显示一个错误的模版
        if (!html) {
            html = lib.format(
                errorTpl,
                {
                    errorClass: this.helper.getPartClasses('error')
                }
            );
        }

        // 获取body节点
        var bodyElement = $(this.helper.getPart('body'));
        bodyElement.html('');
        if (typeof html === 'string') {
            bodyElement.html(html);
        }
        else {
            if (html.appendTo) {
                html.appendTo(bodyElement[0]);
            }
            else {
                bodyElement.append(html);
            }
        }
    }

    /**
     * 构造工具栏的html结构
     *
     * @ignore
     */
    function initToolBar() {
        var toolRole = this.toolRole;
        if (!toolRole) {
            return;
        }

        var helper = this.helper;
        var toolTpl = '<span data-role="tool" class="${iconClass}" data-tool-role="${iconRole}"></span>';
        u.each(toolRole.split(','), function (role) {
            $(helper.getPart('tool')).append(lib.format(
                toolTpl,
                {
                    iconClass: helper.getIconClass() + '-' + role,
                    iconRole: role
                }
            ));
        });
    }

    esui.register(MediaPreview);
    return MediaPreview;
});
示例#7
0
define(function (require) {
    var esui = require('esui');
    var lib = require('esui/lib');
    var u = require('underscore');
    var eoo = require('eoo');
    var Control = require('esui/Control');
    var painters = require('esui/painters');
    var $ = require('jquery');
    var previewHelper = require('./helper/previewHelper');
    require('./helper/swfHelper');

    require('esui/Dialog');

    var LightBox = eoo.create(
        Control,
        {

            /**
             * 资源预览弹出框控件
             *
             * @extends Control
             * @constructor
             */
            constructor: function () {
                this.$super(arguments);
                this.dialog = null;
            },

            type: 'LightBox',

            /**
             * 初始化配置
             *
             * @protected
             * @override
             */
            initOptions: function (options) {
                var properties = {
                    currentIndex: 0,
                    width: 'auto',
                    height: 'auto',
                    dialogVariants: 'lightbox',
                    loadingStyle: this.helper.getPartClassName('media-loading'),
                    loadFailedStyle: this.helper.getPartClassName('media-load-failed'),
                    group: null,
                    groupContainerId: null
                };
                u.extend(properties, LightBox.defaultProperties, options);
                this.setProperties(properties);
            },

            /**
             * 初始化DOM结构
             *
             * @protected
             * @override
             */
            initStructure: function () {
                var properties = {
                    content: '',
                    closeButton: true,
                    mask: true,
                    alwaysTop: true,
                    closeOnHide: false,
                    width: 'auto'
                };

                u.extend(
                    properties,
                    {
                        title: this.title || '',
                        foot: this.foot || '',
                        draggable: this.draggable || false,
                        needfoot: this.needfoot || false,
                        variants: this.dialogVariants
                    }
                );
                var dialog = esui.create('Dialog', properties);
                dialog.appendTo(document.body);
                this.dialog = dialog;
            },

            /**
             * 初始化事件交互
             *
             * @protected
             * @override
             */
            initEvents: function () {
                this.initCarousel();
                var leftLink = lib.g(this.dialog.helper.getId('link-left'));
                var rightLink = lib.g(this.dialog.helper.getId('link-right'));

                var me = this;

                this.dialog.helper.addDOMEvent(
                    leftLink,
                    'click',
                    function (e) {
                        me.showPreviousMedia();
                    }
                );
                this.dialog.helper.addDOMEvent(
                    rightLink,
                    'click',
                    function (e) {
                        me.showNextMedia();
                    }
                );

                if (this.group) {
                    var container = this.groupContainerId ? lib.g(this.groupContainerId) : document.body;

                    me.helper.addDOMEvent(
                        container,
                        'click',
                        '[data-lightbox-group]',
                        function (e) {
                            var target = e.currentTarget;
                            e.preventDefault();

                            var $groupElements = $(container).find('[data-lightbox-group="' + me.group + '"]');
                            var i = $groupElements.index(target);
                            var datasource = [];
                            $groupElements.each(function (i, element) {
                                var $el = $(element);
                                var item = {
                                    url: $el.attr('href')
                                };

                                var dataType = $el.attr('data-lightbox-type');
                                item.width = $el.attr('data-lightbox-width');
                                item.height = $el.attr('data-lightbox-height');
                                dataType && (item.type = dataType);

                                datasource.push(item);
                            });
                            me.datasource = datasource;
                            me.show({
                                currentIndex: i
                            });
                        }
                    );
                }
            },

            /**
             * 初始化图片/视频轮播
             *
             * @protected
             */
            initCarousel: function () {
                var tpl = [
                    '<div id="${mediaId}" class="${mediaStyle}"></div>',
                    '<div id="${linkId}" class="${linkStyle}">',
                    '<a href="javascript:;" id="${leftLinkId}" class="${leftLinkStyle}"></a>',
                    '<a href="javascript:;" id="${rightLinkId}" class="${rightLinkStyle}"></a>',
                    '</div>'
                ].join('');
                var body = this.dialog.getBody();
                var dialogHelper = this.dialog.helper;
                var leftIcon = dialogHelper.getPartClassName('lightbox-content-link-left')
                    + ' '
                    + dialogHelper.getIconClass();
                var rightIcon = dialogHelper.getPartClassName('lightbox-content-link-right')
                    + ' '
                    + dialogHelper.getIconClass();
                body.setContent(
                    lib.format(
                        tpl,
                        {
                            mediaId: dialogHelper.getId('media'),
                            mediaStyle: dialogHelper.getPartClassName('lightbox-content-media'),
                            linkId: dialogHelper.getId('link'),
                            linkStyle: dialogHelper.getPartClassName('lightbox-content-link'),
                            leftLinkId: dialogHelper.getId('link-left'),
                            leftLinkStyle: leftIcon,
                            rightLinkId: dialogHelper.getId('link-right'),
                            rightLinkStyle: rightIcon
                        }
                    )
                );
            },

            mediaContainer: function () {
                return lib.g(this.dialog.helper.getId('media'));
            },

            /**
             * 显示图片/视频对话框容器
             *
             * @param {Object} args 显示对话框时传入的参数
             * @protected
             */
            show: function (args) {
                args && this.setProperties(args);
                var link = lib.g(this.dialog.helper.getId('link'));
                link.style.display = this.datasource.length <= 1 ? 'none' : '';
                this.showMedia();
            },

            /**
             * 隐藏图片/视频对话框容器
             *
             * @protected
             */
            hide: function () {
                this.dialog.hide();
            },

            /**
             * 填充内容
             *
             * @param {Array} list 图片或视频数据列表
             * @protected
             */
            setContent: function (list) {
                this.setProperties(
                    {
                        datasource: list
                    }
                );
            },

            /**
             * 显示图片/视频
             *
             * @protected
             */
            showMedia: function () {
                var data = this.datasource[this.currentIndex];
                this.showLoading();

                if (!data.type) {
                    if (/\.(?:jpg|png|gif|jpeg|bmp)$/i.test(data.url)) {
                        data.type = 'image';
                    }
                    else if (/\.swf/i.test(data.url)) {
                        data.type = 'flash';
                    }
                    else if (/\.(?:mp4|flv|mov|mkv|mpg|avi|rmvb|rm|ogg|wmv|mp3|wma|mid)/i.test(data.url)) {
                        data.type = 'video';
                    }
                }
                this.preview(data);
            },

            /**
             * 显示加载状态
             *
             * @protected
             */
            showLoading: function () {
                $(this.dialog.main).addClass(this.helper.getPartClassName('loading'));
            },

            /**
             * 取消加载状态
             *
             * @protected
             */
            hideLoading: function () {
                $(this.dialog.main).removeClass(this.helper.getPartClassName('loading'));
            },

            /**
             * 预览图片/视频/flash
             *
             * @protected
             * @param {Object} options 预览参数
             */
            preview: function (options) {
                if (options) {
                    var type = options.type;
                    options.id = options.id || 'preiew-' + Math.random();
                    options.width = options.width || this.width;
                    options.height = options.height || this.height;

                    type = type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
                    (this['preview' + type] || this.previewNotSupported).call(this, options);
                }
            },

            /**
             * 预览图片
             *
             * @param {Object} options 图片数据
             * @protected
             */
            previewImage: function (options) {
                var me = this;
                options.width += 'px';
                options.height += 'px';
                var img = previewHelper.preview(options);
                img.onload = function () {
                    me.hideLoading();
                    me.mediaContainer().innerHTML = '';
                    me.mediaContainer().appendChild(img);
                    me.dialog.show();
                    img.onload = img.onerror = null;
                };

                img.onerror = function () {
                    me.hideLoading();
                    me.mediaContainer().innerHTML = lib.format(this.LOAD_FAILED_TPL, me);
                    img.onload = img.onerror = null;
                    me.dialog.show();
                };
            },

            /**
             * 预览Flash
             *
             * @param {Object} options flash数据
             * @protected
             */
            previewFlash: function (options) {
                var html = previewHelper.preview(options);
                this.hideLoading();
                this.mediaContainer().innerHTML = '';
                this.mediaContainer().appendChild(html);
                this.dialog.show();
            },

            /**
             * 预览视频
             *
             * @param {Object} options 视频数据
             * @protected
             */
            previewVideo: function (options) {
                var url = options.url;
                var html = '';
                if (/\.flv/.test(url)) {
                    options.type = 'flv';
                }
                else if (/\.mp4|\.mov/.test(url)) {
                    options.type = 'video';
                }
                options.swfPath = this.swfPath;
                html = previewHelper.preview(options);
                var $container = $(this.mediaContainer());
                this.hideLoading();
                $container.html('');
                $container.append($(html));
                this.dialog.show();
            },

            previewNotSupported: function () {
                this.hideLoading();
                this.mediaContainer().innerHTML = this.NOT_SUPPORT_MESSAGE;
                this.dialog.show();
            },

            /**
             * 显示下一个图片/视频
             *
             * @protected
             */
            showNextMedia: function () {
                this.currentIndex = ++this.currentIndex % this.datasource.length;
                this.showMedia();
            },

            /**
             * 显示上一个图片/视频
             *
             * @protected
             */
            showPreviousMedia: function () {
                this.currentIndex = (--this.currentIndex + this.datasource.length) % this.datasource.length;
                this.showMedia();
            },

            /**
             * 重渲染
             *
             * @method
             * @protected
             * @override
             */
            repaint: painters.createRepaint(
                Control.prototype.repaint,
                {
                    name: ['title'],
                    paint: function (control, title) {
                        control.dialog.setTitle(title || '');
                    }
                }
            )
        }
    );

    LightBox.defaultProperties = {
        NOT_SUPPORT_MESSAGE: '暂不支持该格式预览',
        LOAD_FAILED_TPL: '<div class="${loadFailedStyle}">加载图片失败</div>'
    };

    esui.register(LightBox);
    return LightBox;
});
示例#8
0
    function (require) {
        var Control = require('esui/Control');
        var lib = require('esui/lib');
        var ui = require('esui');

        require('esui/Panel');
        require('esui/Overlay');

        /**
         * 折叠控件
         *
         * @class ui.TogglePanel
         * @extends.esui.Control
         */
        var exports = {};

        /**
         * 控件类型,始终为`"TogglePanel"`
         *
         * @type {string}
         * @override
         */
        exports.type = 'TogglePanel';

        /**
         * 初始化参数
         *
         * @param {Object} options 构造函数传入的参数
         * @override
         * @protected
         */
        exports.initOptions = function (options) {
            var defaults = {
                expanded: false,
                position: 'layer'
            };

            var properties = lib.extend(defaults, options);

            this.setProperties(properties);
        };

        /**
         * 初始化DOM结构
         *
         * @override
         * @protected
         */
        exports.initStructure = function () {
            var children = lib.getChildren(this.main);
            var titleElem = children[0];
            var contentElem = children[1];

            // 初始化Title部分的DOM结构
            initTitle.call(this, titleElem);

            // 初始化content部分的DOM结构
            var position = this.position;
            if (position === 'fixed') {
                // 占位
                initContentPanel.call(this, contentElem);
            }
            else {
                // 不占位
                initContentOverlay.call(this, contentElem);
            }
        };

        /**
         * 初始化Title部分的DOM结构
         *
         * @inner
         * @param {Object} titleElem Title的DOM对象
         */
        function initTitle(titleElem) {
            var titlePanel = ui.create('Panel', {main: titleElem});
            this.helper.addPartClasses('title', titlePanel.main);
            this.addChild(titlePanel, 'title');
            titlePanel.render();
            this.set('title', titleElem && titleElem.innerHTML);
        }

        /**
         * 按Panel模式初始化Content部分的DOM结构
         *
         * @inner
         * @param {Object} contentElem content的DOM对象
         */
        function initContentPanel(contentElem) {
            var options = {
                main: contentElem,
                childName: 'content',
                viewContext: this.viewContext,
                renderOptions: this.renderOptions
            };

            var contentPanel = ui.create('Panel', options);
            this.helper.addPartClasses('content', contentPanel.main);
            this.addChild(contentPanel, 'content');
            contentPanel.render();
        }

        /**
         * 按Overlay模式初始化Content部分的DOM结构
         *
         * @inner
         * @param {Object} contentElem content的DOM对象
         */
        function initContentOverlay(contentElem) {
            var overlayMain = this.helper.createPart('layer', 'div');
            lib.addClass(overlayMain, this.helper.getPartClassName('layer'));

            var options = {
                main: contentElem,
                childName: 'content',
                attachedDOM: this.main,
                attachedLayout: 'bottom,left',
                autoClose: false,
                viewContext: this.viewContext,
                renderOptions: this.renderOptions
            };
            var contentLayer = ui.create('Overlay', options);

            this.helper.addPartClasses('content', contentLayer.main);
            this.addChild(contentLayer, 'content');
            contentLayer.render();

            var globalEvent = lib.bind(close, this);

            contentLayer.on(
                'show',
                function () {
                    this.helper.addDOMEvent(document, 'mousedown', globalEvent);
                }
            );

            contentLayer.on(
                'hide',
                function () {
                    this.helper.removeDOMEvent(document, 'mousedown', globalEvent);
                }
            );
        }

        /**
         * 关闭layer层的事件处理句柄
         *
         * @param {mini-event.Event} e 事件对象
         * @inner
         */
        function close(e) {
            var target = e.target;
            var layer = this.getChild('content');

            if (!layer) {
                return;
            }

            var isChild = lib.dom.contains(layer.main, target);

            if (!isChild) {
                layer.hide();

                // 如果是点击attachedTarget的话,需要保持expanded状态.
                // 如果是点击其他空白区域的话,直接去掉expanded就行。
                var attachedTarget = layer.attachedTarget;
                var isAttachedTarget = lib.dom.contains(attachedTarget, target) || attachedTarget === target;

                if (!isAttachedTarget) {
                    this.removeState('expanded');
                    this.removeState('active');
                }
            }
        }

        /**
         * 点击Title区域的句柄
         *
         * @inner
         */
        function onToggle() {
            this.toggleContent();
        }

        /**
         * 切换展开/收起状态
         *
         * @inner
         */
        exports.toggleContent = function () {
            this.toggleStates();
            this.fire('change');
        };

        exports.toggleStates = function () {
            var position = this.position;

            if (position === 'fixed') {
                // 占位模式
                this.toggleState('expanded');
                this.toggleState('active');
            }
            else {
                // 浮层模式
                var contentLayer = this.getChild('content');

                if (this.isExpanded()) {
                    this.removeState('expanded');
                    this.removeState('active');
                    contentLayer.hide();
                }
                else {
                    this.addState('expanded');
                    this.addState('active');
                    contentLayer.show();
                }
            }
        };

        exports.initEvents = function () {
            var me = this;
            me.$super(arguments);
            var titlePanel = me.getChild('title');
            me.helper.addDOMEvent(titlePanel.main, 'click', lib.bind(onToggle, me));
        };

        var painters = require('esui/painters');
        /**
         * 重绘
         *
         * @override
         * @protected
         */
        exports.repaint = painters.createRepaint(
            Control.prototype.repaint,
            painters.state('expanded'),
            {
                name: 'title',
                paint: function (panel, title) {
                    panel.getChild('title').set('content', title);
                }
            },
            {
                name: 'content',
                paint: function (panel, content) {
                    panel.getChild('content').set('content', content);
                }
            },
            /**
             * @property {number} width
             *
             * 宽度
             */
            painters.style('width')
        );

        exports.isExpanded = function () {
            return this.hasState('expanded');
        };

        var TogglePanel = require('eoo').create(Control, exports);

        ui.register(TogglePanel);

        return TogglePanel;
    }
示例#9
0
    function (require) {
        var Control = require('esui/Control');
        var lib = require('esui/lib');
        var ui = require('esui');

        require('esui/Panel');

        /**
         * 折叠控件
         */
        function TogglePanel() {
            Control.apply(this, arguments);
        }

        TogglePanel.prototype.type = 'TogglePanel';

        /**
         * 初始化参数
         *
         * @param {Object} options 构造函数传入的参数
         * @override
         * @protected
         */
        TogglePanel.prototype.initOptions = function (options) {
            var defaults = {
                expanded: false
            };

            var properties = lib.extend(defaults, options);

            this.setProperties(properties);
        };

        /**
         * 初始化DOM结构
         *
         * @override
         * @protected
         */
        TogglePanel.prototype.initStructure = function () {
            var children = lib.getChildren(this.main);
            var titleElem = children[0];
            var contentElem = children[1];

            var titlePanel = ui.create('Panel', { main: titleElem });
            this.helper.addPartClasses('title', titlePanel.main);
            this.addChild(titlePanel, 'title');
            titlePanel.render();
            this.set('title', titleElem && titleElem.innerHTML);
            titlePanel.helper.addDOMEvent(titlePanel.main, 'click', lib.bind(onToggle, this));

            var contentPanel = ui.create('Panel', { main: contentElem });
            this.helper.addPartClasses('content', contentPanel.main);
            this.addChild(contentPanel, 'content');
            this.set('content', contentElem && contentElem.innerHTML);
        };

        function onToggle() {
            this.toggleState('expanded');
            this.fire('change');
        }

        var painters = require('esui/painters');
        /**
         * 重绘
         *
         * @override
         * @protected
         */
        TogglePanel.prototype.repaint = painters.createRepaint(
            Control.prototype.repaint,
            painters.state('expanded'),
            {
                name: 'title',
                paint: function (panel, title) {
                    panel.getChild('title').set('content', title);
                }
            },
            {
                name: 'content',
                paint: function (panel, content) {
                    panel.getChild('content').set('content', content);
                }
            }
        );

        TogglePanel.prototype.isExpanded = function () {
            return this.hasState('expanded');
        };

        lib.inherits(TogglePanel, Control);
        ui.register(TogglePanel);
        return TogglePanel;
    }
示例#10
0
    function (require) {
        var u = require('underscore');
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var Layer = require('esui/Layer');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        var $ = require('jquery');
        var esui = require('esui');

        /**
         * 'MultiSelect'控件使用的层类
         * @extends Layer
         * @ignore
         * @constructor
         */
        var MultiSelectLayer = eoo.create(Layer, {
            create: function () {
                var ele = this.$super(arguments);
                $(ele).addClass(this.control.helper.getPrefixClass('dropdown'));
                $(this.control.main).after(ele);
                return ele;
            },

            layerFootTpl: [
                '<button type="button" class="${confirmClass}" data-ui="type:Button;variants:primary"'
                    + 'data-ui-child-name="confirm">${okText}</button>',
                '<button type="button" class="${cancelClass}" data-ui="type:Button;variants:link"'
                    + 'data-ui-child-name="cancel">${cancelText}</button>'
            ].join(''),

            render: function (element) {
                var multiSelector = this.control;
                var helper = multiSelector.helper;

                // 渲染layer主体部分
                var html = helper.getPartBeginTag('wrapper', 'div');
                for (var i = 0; i < this.control.datasource.length; i++) {
                    html += multiSelector.getItemHTML(i);
                }
                html += helper.getPartEndTag('wrapper', 'div');

                // 渲染layer底部确认和取消按钮
                if (multiSelector.footer) {
                    html += this.getLayerFoot();
                }

                element.innerHTML = html;
                helper.initChildren(element);
            },

            getLayerFoot: function () {
                var multiSelect = this.control;
                var helper = multiSelect.helper;
                var footBegin = helper.getPartBeginTag('layer-footer', 'div');
                var footEnd = helper.getPartEndTag('layer-footer', 'div');
                var data = {
                    confirmClass: helper.getPrimaryClassName('layer-confirm'),
                    cancelClass: helper.getPrimaryClassName('layer-cancel'),
                    okText: multiSelect.okText,
                    cancelText: multiSelect.cancelText
                };
                var footBody = lib.format(this.layerFootTpl, data);
                return footBegin + footBody + footEnd;
            },

            initBehavior: function (element) {
                var multiSelect = this.control;

                // item点击事件
                $(element).on('click', 'input', u.bind(itemClickHandler, multiSelect));

                // 确定、取消事件绑定
                if (multiSelect.footer) {
                    var layerConfirm = multiSelect.getChild('confirm');
                    var layerCancel = multiSelect.getChild('cancel');
                    layerConfirm.on('click', u.bind(confirmHandler, multiSelect));
                    layerCancel.on('click', u.bind(cancelHandler, multiSelect));
                }
            },

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

                // 根据selectedIndex的值更新视图
                var selectClass = helper.getPrimaryClassName('item-checked');
                var selectedIndex = lib.toDictionary(this.control.selectedIndex);

                for (var i = 0; i < this.control.datasource.length; i++) {
                    var checkInput = $(element).find('input[data-index=' + i + ']');
                    var checkItem = $(checkInput).parent();
                    if (selectedIndex[i]) {
                        checkInput.prop('checked', true);
                        $(checkItem).addClass(selectClass);
                    }
                    else {
                        checkInput.prop('checked', false);
                        $(checkItem).removeClass(selectClass);
                    }
                }
            },

            dock: {
                strictWidth: true
            },

            hide: function () {
                var multiSelect = this.control;
                // 没有确定按钮时,弹层隐藏即为确定
                if (multiSelect.footer) {
                    this.syncState(this.getElement(false));
                }
                else {
                    multiSelect.set('selectedIndex', [].concat(multiSelect.newSelectedIndex));
                }
                this.$super(arguments);
            }
        });

        /**
         * 下拉多选控件
         * @extends InputControl
         * @constructor
         */
        var MultiSelect = eoo.create(InputControl, {
            constructor: function () {
                this.$super(arguments);
                this.layer = new MultiSelectLayer(this);
            },

            /**
             * 控件类型,始终为'MultiSelect'
             * @type {string}
             * @readonly
             * @override
             */
            type: 'MultiSelect',

            initOptions: function (options) {
                var properties = {
                    multi: false,
                    title: '',
                    width: 200,
                    datasource: [],
                    selectedIndex: [],
                    footer: false,
                    maxLength: null,
                    displayText: null
                };
                u.extend(properties, MultiSelect.defaultProperties, options);

                // 如果主元素是个'<select>'元素,则需要从元素中抽取数据源,
                // 这种情况下构造函数中传入的'datasource'无效
                if (this.main.nodeName.toLowerCase() === 'select') {
                    if ($(this.main).attr('multiple') === 'multiple') {
                        properties.multi = true;
                    }
                    properties.datasource = [];
                    var elements = this.main.getElementsByTagName('option');
                    for (var i = 0, length = elements.length; i < length; i++) {
                        var item = elements[i];
                        var dataItem = {
                            name: item.name || item.text,
                            value: item.value
                        };
                        if (item.disabled) {
                            dataItem.disabled = true;
                        }
                        properties.datasource.push(dataItem);
                    }
                    this.helper.extractOptionsFromInput(this.main, properties);
                }
                properties.newSelectedIndex = [].concat(properties.selectedIndex);

                this.setProperties(properties);
            },

            /**
             * 每个节点显示的内容的模板
             * @type {string}
             */
            itemTemplate: [
                '<div title="${title}" class="${wrapperClass}">',
                    '<div class="${itemWrapperClass}">',
                    '    <input type="${type}" name="${name}" id="${id}" data-index="${dataIndex}"'
                        + 'title="${title}" value="${value}" ${checked} ${disabled} />',
                    '    <label for="${id}">${title}</label>',
                    '</div>',
                '</div>'
            ].join(''),

            headItemTemplate: [
                '<div title="${title}" class="${headerClass}">',
                '    <span">${title}</span>',
                '</div>'
            ].join(''),

            /**
             * 获取每个节点显示的内容
             *
             * @param {number} index 当前节点的索引
             * @return {string} 返回HTML片段
             */
            getItemHTML: function (index) {
                var item = this.datasource[index];
                if (u.isFunction(this.getCustomItemHTML)) {
                    return this.getCustomItemHTML(item);
                }
                var helper = this.helper;
                var classes = helper.getPartClasses('item');

                // checked
                var wrapperClass = '';
                var itemWrapperClass = '';
                if (this.multi) {
                    itemWrapperClass += [
                        helper.getPrefixClass('checkbox-custom'),
                        helper.getPrefixClass('checkbox-single')
                    ].join(' ');
                }
                else {
                    itemWrapperClass += [
                        helper.getPrefixClass('radio-custom'),
                        helper.getPrefixClass('radio-single')
                    ].join(' ');
                }

                var valueIndex = lib.toDictionary(this.selectedIndex);
                if (valueIndex[index]) {
                    wrapperClass += ' ' + helper.getPartClassName('item-checked');
                }

                var headerClass = '';
                if (item.header) {
                    headerClass += ' ' + helper.getPartClassName('item-header');
                }

                if (item.disabled) {
                    wrapperClass += ' ' + helper.getPartClassName('item-disabled');
                }

                var data = {
                    wrapperClass: classes.join(' ') + ' ' + wrapperClass,
                    id: helper.getId('multiselect-' + index),
                    type: this.multi ? 'checkbox' : 'radio',
                    name: this.name,
                    title: lib.trim(item.title || item.name || item.text),
                    value: item.value,
                    checked: valueIndex[index] ? ' checked="checked"' : '',
                    dataIndex: index,
                    headerClass: headerClass,
                    itemWrapperClass: itemWrapperClass,
                    disabled: item.disabled ? 'disabled' : ''
                };

                var tpl = item.header ? this.headItemTemplate : this.itemTemplate;
                return lib.format(tpl, data);
            },

            /**
             * 显示选中值的模板
             *
             * @type {string}
             */
            displayTemplate: '${text}',

            /**
             * 获取选中值的内容
             *
             * @param {meta.SelectItem | null} item 选中节点的数据项,
             * 如果{@link Select#emptyText}属性值不为空且未选中任何节点,则传递'null'
             * @return {string} 显示的HTML片段
             */
            getDisplayHTML: function (item) {
                if (u.isFunction(this.getCustomDisplayHTML)) {
                    return this.getCustomDisplayHTML(item);
                }
                if (this.displayText) {
                    return this.displayText;
                }
                if (item.length === 0) {
                    return u.escape(this.emptyText);
                }
                var displayText = '';
                $.each(item, function (index, selectItem) {
                    displayText += u.escape(selectItem.name || selectItem.text);
                    if (index !== item.length - 1) {
                        displayText += ',';
                    }
                });
                return lib.format(this.displayTemplate, {text: displayText});
            },

            /**
             * 初始化DOM结构
             *
             * @protected
             * @override
             */
            initStructure: function () {
                var helper = this.helper;
                var arrow = 'arrow';
                var span = 'span';
                var mainElement = this.main;

                // 如果主元素是'<select>',删之替换成'<div>'
                if (mainElement.nodeName.toLowerCase() === 'select') {
                    helper.replaceMain();
                    mainElement = this.main;
                }

                this.layer.autoCloseExcludeElements = [mainElement];
                mainElement.innerHTML = helper.getPartHTML('text', span) + helper.getPartHTML(arrow, span);

                $(helper.getPart(arrow)).addClass(helper.getIconClass());
            },

            /**
             * 初始化事件交互
             *
             * @protected
             * @override
             */
            initEvents: function () {
                this.helper.addDOMEvent(this.main, 'click', toggle);
                this.layer.on('rendered', u.bind(addLayerClass, this));
            },

            /**
             * 将字符串类型的值转换成原始格式
             *
             * @param {string} value 字符串值
             * @return {string[]}
             * @protected
             * @override
             */
            parseValue: function (value) {
                /**
                 * @property {string} [value=""]
                 *
                 * `MultiSelect`的字符串形式的值为逗号分隔的多个值
                 */
                return value.split(',');
            },

            /**
             * 获取值
             *
             * @return {Mixed}
             * @override
             */
            getRawValue: function () {
                if (this.selectedIndex.length === 0) {
                    return null;
                }
                var values = [];
                var datasource = this.datasource;
                u.each(this.selectedIndex, function (index) {
                    var item = datasource[index];
                    if (item) {
                        values.push(item.value);
                    }
                });
                return values;
            },

            /**
             * 重渲染
             *
             * @method
             * @protected
             * @override
             */
            repaint: painters.createRepaint(
                InputControl.prototype.repaint,
                painters.style('width'),
                {
                    name: 'datasource',
                    paint: function (select) {
                        select.layer.repaint();
                    }
                },
                {
                    name: ['selectedIndex'],
                    paint: function (select, selectedIndex) {
                        updateValue(select);
                    }
                },
                {
                    name: ['rawValue'],
                    paint: function (select, rawValue) {
                        if (u.isArray(rawValue) && rawValue.length) {
                            var selectedItems = u.filter(
                                select.datasource,
                                function (item) {
                                    return u.indexOf(rawValue, item.value) > -1;
                                }
                            );
                            if (!select.multi) {
                                selectedItems = selectedItems.slice(-1);
                            }
                            select.selectedIndex = u.map(
                                selectedItems,
                                function (item) {
                                    return u.indexOf(select.datasource, item);
                                }
                            );
                            updateValue(select);
                        }
                    }
                },
                {
                    name: ['disabled', 'hidden', 'readOnly'],
                    paint: function (select, disabled, hidden, readOnly) {
                        if (disabled || hidden || readOnly) {
                            select.layer.hide();
                        }
                    }
                }
            ),

            /**
             * 更新{@link Select#datasource}属性,无论传递的值是否变化都会进行更新
             *
             * @param {meta.SelectItem[]} datasource 新的数据源对象
             */
            updateDatasource: function (datasource) {
                if (!datasource) {
                    datasource = this.datasource;
                }
                this.datasource = datasource;
                var record = {name: 'datasource'};
                this.repaint([record], {datasource: record});
            },

            /**
             * 批量更新属性并重绘
             *
             * @param {Object} properties 需更新的属性
             * @override
             * @fires change
             */
            setProperties: function (properties) {
                if (properties.datasource == null) {
                    properties.datasource = this.datasource;
                }

                if (properties.value == null
                    && properties.rawValue == null
                    && properties.selectedIndex == null
                    && properties.datasource === this.datasource
                ) {
                    properties.selectedIndex = this.selectedIndex;
                }

                var changes = this.$super(arguments);

                return changes;
            },

            /**
             * 销毁控件
             *
             * @override
             */
            dispose: function () {
                if (this.helper.isInStage('DISPOSED')) {
                    return;
                }

                if (this.layer) {
                    this.layer.dispose();
                    this.layer = null;
                }

                this.$super(arguments);
            },

            /**
             * 获取当前选中的{@link meta.SelectItem}对象
             *
             * @return {meta.SelectItem}
             */
            getSelectedItem: function () {
                var datasource = this.datasource;
                var selectedItems = [];
                if (u.isArray(this.selectedIndex)) {
                    $.each(this.selectedIndex, function (index, itemIndex) {
                        if (datasource[itemIndex]) {
                            selectedItems.push(datasource[itemIndex]);
                        }
                    });
                }
                return selectedItems;
            }
        });

        MultiSelect.defaultProperties = {
            emptyText: '请选择',
            okText: '确认',
            cancelText: '取消'
        };

        /**
         * 切换下拉框
         *
         * @param {Event} e click事件对象
         */
        function toggle(e) {
            this.layer.toggle.call(this.layer, e);
            this.layer.syncState(this.layer.getElement(false));
            this.newSelectedIndex = [].concat(this.selectedIndex);
        }

        function itemClickHandler(e) {
            var target = e.target;
            // 值是否发生更改的标志位
            var isChanged = false;
            if (!this.helper.isPart(target, 'item-disabled')) {
                var index = target.getAttribute('data-index');
                var newIndex = +index;
                if (this.multi) {
                    var isNewIndex = true;
                    for (var i = 0; i < this.newSelectedIndex.length; i++) {
                        if (this.newSelectedIndex[i] === newIndex) {
                            isNewIndex = false;
                            isChanged = true;
                            this.newSelectedIndex.splice(i, 1);
                        }
                    }
                    if (isNewIndex) {
                        this.newSelectedIndex.push(+index);
                        isChanged = true;
                    }
                }
                else {
                    this.newSelectedIndex = [newIndex];
                    isChanged = true;
                }
                this.fire('itemclick');
            }
            if (this.maxLength && this.newSelectedIndex.length > this.maxLength) {
                var shiftIndex = this.newSelectedIndex.shift();
                $(this.layer.getElement(false)).find('input[data-index=' + shiftIndex + ']').removeAttr('checked');
                isChanged = false;
            }
            if (isChanged && !this.footer) {
                this.set('selectedIndex', [].concat(this.newSelectedIndex));
                this.fire('change');
            }
        }

        function confirmHandler(e) {
            this.set('selectedIndex', [].concat(this.newSelectedIndex));
            this.fire('change');
            this.layer.hide();
        }

        function cancelHandler() {
            this.set('selectedIndex', this.selectedIndex);
            this.newSelectedIndex = [].concat(this.selectedIndex);
            updateValue(this);
            this.layer.hide();
        }

        function addLayerClass() {
            this.fire('layerrendered', {layer: this.layer});
        }

        /**
         * 根据控件的值更新其视图
         *
         * @param {Select} select 控件实例
         * @ignore
         */
        function updateValue(select) {
            // 同步显示的文字
            var textHolder = select.helper.getPart('text');
            var selectedItem = select.getSelectedItem();
            var text = select.getDisplayHTML(selectedItem);

            textHolder.innerHTML = text;
            textHolder.title = text;

            var layerElement = select.layer.getElement(false);
            if (layerElement) {
                select.layer.syncState(layerElement);
            }
        }

        esui.register(MultiSelect);
        return MultiSelect;
    }
示例#11
0
    function (require) {
        var $ = require('jquery');
        var esui = require('esui');
        var u = require('underscore');
        var TogglePanel = require('../TogglePanel');
        var eoo = require('eoo');

        /**
         * @class ToggleSelector
         * @extends TogglePanel
         */
        var ToggleSelector = eoo.create(
            TogglePanel,
            {

                /**
                 * @override
                 */
                type: 'ToggleSelector',

                getCategory: function () {
                    return 'input';
                },

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        textField: null,
                        collapseAfterChange: true,
                        defaultText: '请选择'
                    };
                    options = u.extend(properties, options);

                    if (!options.title) {
                        options.title = options.defaultText;
                    }

                    this.$super(arguments);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    var me = this;

                    var controlHelper = this.helper;
                    this.triggerElement = this.main;
                    me.$super(arguments);
                    var $mainElement = $(me.main);

                    var $children = $mainElement.children();
                    var $text = $children.eq(0);
                    var $contentLayer = $children.eq(1);
                    var $caret = $('<span></span>').addClass(
                        controlHelper.getPrefixClass('select-arrow')
                        + ' '
                        + controlHelper.getIconClass()
                    );

                    $mainElement.addClass(controlHelper.getPrefixClass('select'));
                    $text.addClass(controlHelper.getPrefixClass('select-text'));
                    $mainElement.append($caret);
                    $contentLayer.insertAfter($mainElement);
                },

                /**
                 * @override
                 */
                initEvents: function () {
                    var me = this;
                    var target = me.viewContext.getSafely(me.targetControl);
                    var controlHelper = me.helper;
                    target.on('change', u.bind(changeHandler, me));
                    target.on('add', u.bind(addHandler, me));
                    me.updateDisplayText(target);
                    controlHelper.addDOMEvent(
                        me.main,
                        'click',
                        me.toggleContent
                    );
                },

                /**
                 * @override
                 */
                toggleContent: function () {
                    if (!this.isDisabled()) {
                        this.toggleStates();
                    }
                },

                updateDisplayText: function (target) {
                    var displayText = this.title;
                    // 要render了以后才能获取到value
                    if (target.helper.isInStage('RENDERED')) {
                        var rawValue = target.getRawValue();
                        // 因为只针对单选控件,因此即便是多个也默认选第一个
                        if (u.isArray(rawValue)) {
                            rawValue = rawValue[0];
                        }
                        displayText = rawValue && rawValue[this.textField]
                            ? rawValue[this.textField] : this.defaultText;
                    }
                    this.set('title', u.escape(displayText));
                },

                getRawValue: function () {
                    var target = this.viewContext.getSafely(this.targetControl);
                    var rawValue = target.getRawValue();
                    if (rawValue) {
                        return rawValue[this.valueField];
                    }
                },

                getFullRawValue: function () {
                    var target = this.viewContext.getSafely(this.targetControl);
                    return target.getRawValue();
                },

                setRawValue: function (value) {
                    var target = this.viewContext.getSafely(this.targetControl);
                    target.setRawValue(value);
                },

                getValue: function () {
                    return this.getRawValue();
                },

                setValue: function (value) {
                    var rawValue = [{id: value}];

                    this.setRawValue(rawValue);
                },

                /**
                 * 进行验证
                 *
                 * @return {boolean}
                 */
                validate: function () {
                    var target = this.viewContext.get(this.targetControl);

                    if (!target) {
                        return true;
                    }

                    if (typeof target.validate === 'function') {
                        return target.validate();
                    }
                }
            }
        );

        /**
         * 数据变化时如果没有阻止,则更新显示文字
         *
         * @event
         * @param {Object} e 事件对象
         */
        function changeHandler(e) {
            var event = this.fire('change');
            if (!event.isDefaultPrevented()) {
                this.updateDisplayText(e.target);
            }
        }

        /**
         * 添加数据时才控制展开收起
         *
         * @event
         * @param {Object} e 事件对象
         */
        function addHandler(e) {
            if (this.collapseAfterChange && this.hasState('expanded')) {
                this.toggleContent();
            }
        }

        esui.register(ToggleSelector);
        return ToggleSelector;
    }
示例#12
0
    function (require) {
        require('esui/Button');
        require('./FileInput');
        require('./Progress');

        var eoo = require('eoo');
        var esui = require('esui');
        var ui = require('esui/main');
        var Control = require('esui/Control');
        var painters = require('esui/painters');

        var File = require('./File');

        var $ = require('jquery');
        var u = require('underscore');
        var lib = require('esui/lib');


        var supportXHR = (window.File && window.XMLHttpRequest);
        var supportXHR2 = (supportXHR && new window.XMLHttpRequest().upload);

        /**
         * 控件类
         *
         * 上传控件有如下结构特点:
         *
         * - 上传组件          (必须,至少一个)
         *   -- 文件上传控件
         * - 上传列表        (可选,可自定义容器)
         *   -- 由一个或多个进度条组件构成
         *
         * 上传控件有两种模式:
         *
         * - 单文件上传
         * - 多文件上传
         *
         * @class ui.Uploader
         * @extends esui.Control
         */
        var Uploader = eoo.create(
            Control,
            {

                /**
                 * 控件类型,始终为`"Uploader"`
                 *
                 * @type {string}
                 * @readonly
                 * @override
                 */
                type: 'Uploader',

                /*
                 * 文件上传队列
                 */
                queue: {
                    // 队列长度
                    length: 0,
                    // 正在上传的文件
                    uploadingFiles: [],
                    // 等待开始的文件
                    waitingFiles: [],
                    // 出错的文件
                    failedFiles: [],
                    // 遗弃的文件
                    // 当文件过长时会遗弃多余的文件
                    abandonedFiles: [],
                    // 上传完成的文件
                    completeFiles: []
                },

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {};
                    u.extend(properties, this.$self.defaultProperties, options);

                    var adaptProperties = ['sequentialUploads', 'showProgress', 'multiple'];

                    u.each(
                        adaptProperties,
                        function (propertyName) {
                            if (properties[propertyName] === 'false') {
                                properties[propertyName] = false;
                            }
                        }
                    );

                    this.$super([properties]);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    var tpl = [
                        '<div class="${uploadComboxClass}">',
                        // 上传input
                        '   <div data-ui-child-name="fileInput"',
                        '      data-ui="type:FileInput;accept:${accept};multiple:${multiple};name:${paramKey};"></div>',
                        // 伪装ge按钮
                        '   <div data-ui-child-name="submitButton" ',
                        '      data-ui="type:Button;">${text}</div>',
                        '</div>',
                        '<div id="${defaultProgressContainerId}"></div>'
                    ].join('');
                    this.main.innerHTML = lib.format(
                        tpl,
                        {
                            uploadComboxClass: this.helper.getPartClassName('combox'),
                            accept: this.accept,
                            multiple: this.multiple,
                            text: this.text,
                            variants: this.buttonVariants || '',
                            paramKey: this.paramKey,
                            defaultProgressContainerId: this.helper.getId('default-progress-container')
                        }
                    );

                    // 创建控件树
                    this.helper.initChildren();

                    // 绑事件
                    var fileInput = this.getChild('fileInput');
                    fileInput.on('change', u.bind(inputChangeHandler, this));

                    var submitButton = this.getChild('submitButton');
                    submitButton.on(
                        'click',
                        function (e) {
                            fileInput.triggerUploadOutside();
                            e.preventDefault();
                        }
                    );
                },

                /**
                 * @override
                 */
                repaint: painters.createRepaint(
                    Control.prototype.repaint,
                    {
                        name: ['text'],
                        paint: function (uploader, text) {
                            var button = uploader.getChild('submitButton');
                            button.setContent(text);
                        }
                    },
                    {
                        name: ['disabled', 'readOnly'],
                        paint: function (uploader, disabled, readOnly) {
                            var input = uploader.getChild('fileInput');
                            var button = uploader.getChild('submitButton');
                            input.setProperties({disabled: disabled});
                            input.setProperties({readOnly: readOnly});
                            button.setProperties({disabled: disabled});
                            button.setProperties({readOnly: readOnly});
                        }
                    },
                    painters.style('width')
                ),

                /**
                 * 接收文件并做上传前的校验
                 *
                 * @param {Array} files 接收到的文件,可以是input中取到的,也可以是drag外部传入的
                 * @public
                 */
                receiveFile: function (files) {
                    // 如果仍然在uploading,则不执行新的上传操作
                    if (this.stage === 'UPLOADING') {
                        return;
                    }

                    var event = this.fire('beforeupload', {files: files});
                    if (event.isDefaultPrevented()) {
                        return;
                    }

                    this.doUpload(files);
                },

                /**
                 * 开始上传
                 *
                 * @param {Array} files 接收到的文件
                 * @protected
                 */
                doUpload: function (files) {
                    // 超出最大限制,直接返回
                    if (files.length > this.maxFileNumber) {
                        this.notifyFail(this.message.ERROR_FILE_MAX_NUMBER);
                        return;
                    }

                    files = u.map(
                        files,
                        function (file, index) {
                            // 文件格式检查
                            if (!this.checkFileFormat(file)) {
                                file.status = 'client-error';
                                file.message = this.message.ERROR_FILE_EXTENSIONS;
                            }
                            else if (!this.checkFileSize(file)) {
                                file.status = 'client-error';
                                file.message = this.message.ERROR_FILE_MAX_SIEZ;
                            }
                            else {
                                file.status = 'waiting';
                                // 单文件上传要覆盖之前的文件
                                if (this.multiple === false) {
                                    this.queue.waitingFiles = [file];
                                }
                                else {
                                    this.queue.waitingFiles.push(file);
                                }
                            }
                            return file;
                        },
                        this
                    );

                    initFileList.call(this, files);
                    operationFileQueue.call(this);
                },

                /**
                 * 获取Uploader中的文件上传组件
                 *
                 * @return {DOMElement} 容器中FileInput组件
                 * @public
                 */
                getFileInput: function () {
                    return this.getChild('fileInput');
                },

                /**
                 * 验证文件格式
                 *
                 * @param {Object} file file对象
                 * @return {boolean}
                 * @protected
                 */
                checkFileFormat: function (file) {
                    if (this.accept) {
                        // 这里就是个内置的`Rule`,走的完全是标准的验证流程,
                        // 主要问题是上传控件不能通过`getValue()`获得验证用的内容,
                        // 因此把逻辑写在控件内部了
                        var extension = file.name.split('.');
                        extension = '.' + extension[extension.length - 1].toLowerCase();

                        var isValid = false;
                        if (typeof this.accept === 'string') {
                            this.accept = lib.splitTokenList(this.accept);
                        }
                        for (var i = 0; i < this.accept.length; i++) {
                            var acceptPattern = this.accept[i].toLowerCase();
                            if (acceptPattern === extension) {
                                isValid = true;
                                break;
                            }

                            // image/*之类的,表示一个大类
                            if (acceptPattern.slice(-1)[0] === '*') {
                                var mimeType = acceptPattern.split('/')[0];
                                var targetExtensions = this.mimeTypes[mimeType];
                                if (targetExtensions && targetExtensions.hasOwnProperty(extension)) {
                                    isValid = true;
                                    break;
                                }
                            }
                        }

                        return isValid;
                    }

                    return true;
                },

                /**
                 * 验证文件大小
                 *
                 * @param {Object} file file对象
                 * @return {boolean}
                 * @protected
                 */
                checkFileSize: function (file) {
                    // IE9中filechange返回的event只有fileName以及fileId
                    // 所以如果出现这种情况就放过去,让后端做长度校验
                    if (this.maxFileSize && file.originalSize) {
                        var isValid = false;
                        if (file.originalSize) {
                            isValid = parseInt(file.originalSize, 10) <= parseInt(this.maxFileSize, 10);
                        }

                        return isValid;
                    }

                    return true;
                },

                /**
                 * 解析返回中的错误 TODO 这个的具体解析格式要跟后端商定
                 *
                 * @param {Object} response 请求返回的对象
                 * @return {string}
                 * @protected
                 */
                parseError: function (response) {
                    if (response.success === 'false') {
                        return {message: response.error};
                    }

                    return null;
                },

                /**
                 * 通知上传失败
                 *
                 * @method ui.Uploader#notifyFail
                 * @param {string} message 失败消息
                 * @protected
                 */
                notifyFail: function (message) {
                    message = message || '上传失败';
                    this.fire('fail', {message: message});
                },

                /**
                 * 通知上传完成
                 *
                 * @protected
                 * @method ui.Uploader#notifyComplete
                 */
                notifyComplete: function () {
                    this.stage = 'COMPLETE';
                    this.fire('complete', {
                        completeFiles: this.queue.completeFiles,
                        failedFiles: this.queue.failedFiles
                    });
                }
            }
        );

        /**
         * 默认属性
         *
         * @type {Object}
         * @public
         */
        Uploader.defaultProperties = {
            // 上传按钮文本
            text: '点击上传',
            // 文件上传的路径
            action: '/uploadFile',
            // 后台接收时的key名
            paramKey: 'files',
            // 接收的文件类型
            accept: '.gif,.jpg,.png,.swf,.xlsx',
            // 默认为单文件上传控件
            // 单文件上传控件一次只能选择一个文件
            multiple: false,
            // 单个文件最大大小,单位B,默认2M
            maxFileSize: 2147483648,
            // 单次最大上传文件数量
            maxFileNumber: 6,
            // 多文件上传时,请求同时开始还是逐个开始
            sequentialUploads: true,
            // 当前允许的最大连接数
            // sequentialUploads为true时该选项无效
            maxConnections: 6,
            // 提示信息
            // 目前支持提供成功提示,文件大小不符
            // 文件类型不匹配,
            message: {
                // 上传成功
                SUCCESS_INFO: '上传成功',
                // 重新上传
                RESTART_INFO: '正在重新上传',
                // http错误,一般来说就是status返回不是200
                ERROR_HTTP: '上传失败,网络连接错误或上传路径无效',
                // 文件类型错误
                ERROR_FILE_EXTENSIONS: '上传失败,安全因素不支持此类文件',
                // 文件超出最大尺寸, 标准浏览器下可以判断
                ERROR_FILE_MAX_SIEZ: '超出最大上传尺寸',
                // 文件数量超过最大限制了
                ERROR_FILE_MAX_NUMBER: '发生错误,上传文件超过限定。',
                // 格式错误
                ERROR_FILE_FORMAT: '文件格式错误'
            },
            // 是否显示进度
            showProgress: true,
            // 进度模式,seperate和total两种,seperate代表每个文件独立进度;total代表所有文件统一计算进度
            progressMode: 'seperate',
            singleProgressMode: 'detail',
            // 文件列表的容器
            // 如果没有会添加一个默认容器
            progressContainer: null
        };

        /**
         * 上传输入组件变化事件处理
         *
         * @param {mini-event.Event} e 事件对象
         */
        function inputChangeHandler(e) {
            var files = this.getFileInput().files;
            this.receiveFile(files);
        }

        /**
         * 创建上传进度队列
         *
         * @param {Array} fileList 在队列中的文件
         */
        function initFileList(fileList) {
            if (!this.showProgress) {
                return;
            }

            var files = fileList ? fileList : this.queue.waitingFiles;
            this.progressContainer = this.progressContainer || this.helper.getId('default-progress-container');
            var container;
            // 字符串处理
            if (u.isString(this.progressContainer)) {
                // 先作为DOM id寻找
                container = $('#' + this.progressContainer);
                // 如果没找到,找控件id
                if (!container[0] && this.viewContext.get(this.progressContainer)) {
                    container = $(this.viewContext.get(this.progressContainer).main);
                }
            }
            // 只能认为扔了个控件进来
            else {
                container = $(this.progressContainer.main);
            }

            if (!container[0]) {
                return;
            }

            var me = this;

            if (this.progressMode === 'seperate') {
                u.each(files, function (file, index) {
                    // 创建主容器
                    var progressContainer = $('<div></div>');
                    container.append(progressContainer);
                    var options = {
                        file: file,
                        childName: 'progress-' + file.id,
                        main: progressContainer[0],
                        progressMode: me.singleProgressMode
                    };

                    // 如果不支持进度,那就强制不展示进度详情
                    if (!supportXHR) {
                        options.singleProgressMode = 'general';
                    }

                    // 如果定义了进度模板,使用定义的
                    if (me.progressItemTemplate) {
                        options.template = me.progressItemTemplate;
                    }

                    var progress = ui.create('Progress', options);
                    progress.render();
                    me.addChild(progress);

                    progress.on('restart', function (e) {
                        // 重新上传
                        var file = e.target.file;
                        e.target.dispose();
                        // 将文件移出上传队列,然后重新进行上传
                        removeFileFromUploading.call(me, file, 'restart');
                        me.receiveFile([file]);
                    });
                    progress.on('cancel', function (e) {
                        var file = e.target.file;
                        if (file.request) {
                            file.request.abort();
                            removeFileFromUploading.call(me, file, 'cancel');
                            if (me.sequentialUploads) {
                                operationFileQueue.call(me);
                            }
                        }
                        // 从等待队列中清除
                        else {
                            removeFileFromWaiting.call(me, file);
                        }
                        e.target.dispose();
                    });
                });
            }
            // TODO 文件的总进度条,待实现
            // else {

            // }
        }

        /**
         * 执行上传队列
         *
         */
        function operationFileQueue() {
            // 当队列中
            if (this.queue.waitingFiles && this.queue.waitingFiles.length) {
                // 一个个上传
                if (this.sequentialUploads && this.queue.uploadingFiles.length < 1) {
                    chooseProgressFile.call(this);
                }
                // 多文件上传,如果当前连接数未满就继续上传
                else if ((this.maxConnections && this.queue.uploadingFiles.length < this.maxConnections)
                    || !this.maxConnections) {
                    chooseProgressFile.call(this);
                    operationFileQueue.call(this);
                }
            }
            else if (this.queue.uploadingFiles.length === 0) {
                this.notifyComplete();
            }
        }

        /**
         * 选择一个文件上传
         */
        function chooseProgressFile() {
            this.stage = 'UPLOADING';
            // 等待队列中弹出
            var file = this.queue.waitingFiles.shift();
            // 进入上传队列
            this.queue.uploadingFiles.push(file);
            // 执行上传
            uploadFile.call(this, file);
        }

        /**
         * 上传文件
         *
         * @param {ui.File} file 目标文件
         */
        function uploadFile(file) {
            // 文件对应的进度组件
            var progress = this.getChild('progress-' + file.id);
            var me = this;

            // 创建请求
            var request = getHttpRequest.call(this);
            // Need to modified
            file.request = request;
            // 修改文件状态
            file.status = File.UPLOADING;

            // 创建一个符合后端接口的数据对象
            file[this.paramKey] = file.fileData;
            delete file.fileData;

            request.send(
                {
                    container: this.main
                },
                file
            );

            // 上传中
            request.on(
                'progress',
                function (response) {
                    var loaded = response.loaded;
                    var total = response.total;
                    progress.setProperties({loaded: loaded, total: total});
                }
            );

            // 上传完成
            request.on(
                'load',
                function (event) {
                    var response = event.target.response;

                    // 解析一些校验错误
                    var error = me.parseError(response);

                    if (error) {
                        // 修改进度状态
                        progress.updateStatus('client-error', error.message);
                        me.removeFileFromUploading.call(me, file, 'error');
                        me.fire('error', {file: file});
                        addToErrorQueue.call(me, file);
                        event.preventDefault();
                        return;
                    }

                    file.status = File.DONE;

                    // 修改进度状态
                    progress.updateStatus('done', me.message.SUCCESS_INFO);
                    progress.fadeOut(
                        1000,
                        function () {

                            removeFileFromUploading.call(me, file, 'load');

                            if (me.sequentialUploads) {
                                operationFileQueue.call(me);
                            }

                            // 通知完成,供外部捕获,执行预览等操作
                            me.fire(
                                'onecomplete',
                                {
                                    file: file,
                                    data: response
                                }
                            );
                        }
                    );
                }
            );

            // 上传出错
            request.on(
                'error',
                function (event) {
                    // 修改进度状态
                    removeFileFromUploading.call(me, file, 'error');
                    progress.updateStatus('server-error', event.message || me.message.ERROR_HTTP);

                    me.fire('error', {file: file});
                    addToErrorQueue.call(me, file);
                }
            );

            // 传输终止
            request.on(
                'abort',
                function (event) {
                    removeFileFromUploading.call(me, file, 'abort');
                    me.fire('abort', {file: file});
                }
            );
        }

        /**
         * 将文件移出等待队列
         *
         * @param {ui.File} file 目标文件
         */
        function removeFileFromWaiting(file) {
            var queue = this.queue.waitingQueue;
            this.queue.waitingQueue = u.without(queue, file);
        }

        /**
         * 需要添加的错误文件
         *
         * @param {ui.File} file 目标文件
         */
        function addToErrorQueue(file) {
            var sameFile = u.filter(this.queue.failedFiles, function (rawFile) {
                    return rawFile.id === file.id;
                });
            sameFile ? '' : this.queue.failedFiles.push(file);
        }

        /**
         * 将文件移出上传队列并放入完成队列
         *
         * @param {ui.File} file 目标文件
         * @param {string} operation 操作
         */
        function removeFileFromUploading(file, operation) {
            var queue = this.queue.uploadingFiles;
            file = u.find(
                queue,
                function (rawFile) {
                    return rawFile.id === file.id;
                }
            );
            this.queue.uploadingFiles = u.without(queue, file);
            if ('load' === operation) {
                var completeFiles = this.queue.completeFiles;
                var sameFile = u.filter(completeFiles, function (rawFile) {
                    return rawFile.id === file.id;
                });
                sameFile ? '' : completeFiles.push(file);
            }

            operationFileQueue.call(this);
        }

        /**
         * 获取合适的HttpRequest,用于文件上传
         * 目前实现了Leve1 XHR、Level2 XHR以及iframe的封装
         *
         * @return {ui.HttpRequest}
         */
        function getHttpRequest() {
            var HTTPRequest;

            if (supportXHR2) {
                HTTPRequest = require('./L2XMLHttpRequest');
                // HTTPRequest = require('./XMLHttpRequest');
            }
            else if (supportXHR) {
                HTTPRequest = require('./XMLHttpRequest');
            }
            else {
                HTTPRequest = require('./IframeHttpRequest');
            }

            var httpInstance = new HTTPRequest('POST', this.action);

            // 修复Firefox上传文件的bug
            // 上传时reponse会设置concent-type为application/octet-stream
            // 但是request的accept没有这个取值
            httpInstance.setRequestHeader([
                {key: 'token', value: this.token},
                {key: 'Accept', value: '*/*'}
            ]);

            return httpInstance;
        }

        esui.register(Uploader);
        return Uploader;
    }
示例#13
0
    function (require) {
        var u = require('underscore');
        var InputControl = require('esui/InputControl');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        var esui = require('esui');
        var $ = require('jquery');

        /**
         * Filter
         *
         * @class filter.Filter
         * @extends esui.InputControl
         */
        var Filter = eoo.create(
            InputControl,
            {

                /**
                 * @override
                 */
                type: 'Filter',

                /**
                 * 初始化配置
                 *
                 * @protected
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 默认单选
                        multiple: false,
                        // 是否支持自定义
                        custom: false,
                        // 自定义按钮Label
                        customBtnLabel: '自定义',
                        datasource: [],
                        value: null
                    };
                    u.extend(properties, options);
                    this.setProperties(properties);
                },

                /**
                 * 初始化DOM结构
                 *
                 * @protected
                 * @override
                 */
                initStructure: function () {
                    var controlHelper = this.helper;
                    var template = '<div id="${filterPanelId}" class="${filterPanelStyle}">'
                        + '<label id="${labelId}"></label>'
                        + '<div id="${contentPanelId}" class="${contentPanelStyle}"></div>'
                        + '</div>';
                    var data = {
                        filterPanelStyle: controlHelper.getPartClassName('panel'),
                        filterPanelId: controlHelper.getId('items-wrapper-panel'),
                        labelId: controlHelper.getId('items-label'),
                        contentPanelId: controlHelper.getId('items-panel'),
                        contentPanelStyle: controlHelper.getPartClassName('items-panel')
                    };

                    var mainElement = this.main;
                    mainElement.innerHTML = this.helper.render(template, data);

                    // 创建控件树
                    this.initChildren(mainElement);
                },

                /**
                 * 重渲染
                 *
                 * @method
                 * @protected
                 * @override
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: ['datasource', 'rawValue'],
                        paint: function (filter, datasource, rawValue) {
                            if (!u.isArray(rawValue)) {
                                rawValue = [rawValue];
                            }

                            u.each(filter.datasource, function (item, index) {
                                if (u.indexOf(rawValue, item.value) > -1) {
                                    item.selected = true;
                                }
                            });

                            filter.buildItems();
                        }
                    },
                    {
                        name: ['label'],
                        paint: function (filter, label) {
                            $(filter.helper.getPart('items-label')).text(label);
                        }
                    }
                ),

                /**
                 * 初始化事件交互
                 *
                 * @protected
                 * @override
                 */
                initEvents: function () {
                    var itemMainClass = this.helper.getPartClasses('item')[0];
                    var itemCmdClass = this.helper.getPartClasses('item-cmd')[0];
                    this.helper.addDOMEvent(
                        this.main,
                        'click',
                        '.' + itemMainClass + ', .' + itemCmdClass,
                        function (e) {
                            e.preventDefault();
                            this.changeItemStatus(e.target);
                        }
                    );
                },

                /**
                 * 改变选择项的选中状态
                 *
                 * @protected
                 * @method filter.Filter#changeItemStatus
                 * @param {HTMLElement} target 目标元素
                 */
                changeItemStatus: function (target) {
                    var helper = this.helper;
                    var itemRemoveClass = helper.getPartClassName('item-remove');
                    var itemCommonClass = helper.getPartClasses('item')[0];
                    var itemCmdClass = this.helper.getPartClasses('item-cmd')[0];

                    var clickItem = $(target);
                    var selectedItem = clickItem.closest('.' + itemCommonClass + ', .' + itemCmdClass);
                    var itemIndex = selectedItem.data('index');
                    var item = u.clone(this.datasource[itemIndex]);

                    if (clickItem.hasClass(itemRemoveClass)) {
                        this.selectItem(item);
                        this.removeItem(item);

                        this.fire('customitemremove', {item: item});
                    }
                    else {
                        var itemClass = helper.getPartClassName('item');
                        var cmdItemClass = helper.getPartClassName('item-cmd');

                        if (selectedItem.hasClass(itemClass)) {
                            this.selectItem(item);
                        }
                        else if (selectedItem.hasClass(cmdItemClass)) {
                            this.fire('customlinkclick', {element: target});
                        }
                    }
                },

                /**
                 * 根据datasource生成选择项
                 *
                 * @param {Array} datasource 选项列表数据源
                 * @private
                 */
                buildItems: function () {
                    var helper = this.helper;
                    var htmls = u.map(
                        this.datasource,
                        function (item, index) {
                            var active = item.selected ? helper.getPartClassName('item-active') : '';
                            return buildItem.call(this, item, active, index);
                        },
                        this
                    );

                    helper.getPart('items-panel').innerHTML = htmls.join('');
                    this.custom && buildCustomItem.call(this);
                },

                /**
                 * 新增选择项
                 *
                 * @public
                 * @param {Object} item 新增的选择项
                 */
                addItem: function (item) {
                    this.datasource.push(item);
                    this.buildItems();
                },

                /**
                 * 移除选择项
                 *
                 * @param {Object} item 待移除的项
                 */
                removeItem: function (item) {
                    var removeItem = this.getItemByValue(item.value);
                    this.datasource = u.without(this.datasource, removeItem);
                    this.buildItems();
                },

                /**
                 * 设置选择项
                 *
                 * @param {Object} item 选中项的数据 格式如: {value: '', text: ''}
                 * @public
                 */
                unselectItem: function (item) {
                    if (!item || !this.getItemByValue(item.value)) {
                        return;
                    }
                    var targetItem = this.getItemByValue(item.value);
                    targetItem.selected = false;

                    this.buildItems();
                },

                /**
                 * 选择项
                 *
                 * @param {Object} item 选中项的数据 格式如: {value: '', text: ''}
                 * @param {HtmlElement} target 选中的元素
                 * @private
                 */
                selectItem: function (item) {
                    var selectedItem = this.getItemByValue(item.value);
                    var lastItem;
                    var oldSelected = selectedItem.selected;

                    // 需要移除前一个单选
                    if (!this.multiple && !oldSelected) {
                        var selectedItems = this.getSelectedItems();
                        if (selectedItems.length > 0) {
                            lastItem = selectedItems[0];
                            lastItem.selected = false;
                        }
                    }

                    selectedItem.selected = !selectedItem.selected;

                    /**
                     * @event select
                     *
                     * 选择时触发
                     */
                    this.fire('change', {
                        item: item,
                        lastItem: lastItem,
                        action: oldSelected ? 'unselect' : 'select'
                    });
                    this.buildItems();
                },

                /**
                 * 根据值获取整个选择项的数据
                 *
                 * @param {string} value 值
                 * @param {Object=} datasource 数据源
                 * @return {Object} item 选中项的数据 格式如: {value: '', text: ''}
                 * @public
                 */
                getItemByValue: function (value, datasource) {
                    datasource = datasource || this.datasource;
                    return u.find(
                        datasource,
                        function (single, index) {
                            return single.value === value;
                        }
                    );
                },

                /**
                 * 获取选中的项
                 *
                 * @return {Object} 选中项
                 */
                getSelectedItems: function () {
                    var items = [];
                    u.each(this.datasource, function (item, index) {
                        if (item.selected) {
                            items.push(item);
                        }
                    });
                    return items;
                },

                /**
                 * 获取选中的值
                 *
                 * @return {Object} 选中项
                 */
                getValue: function () {
                    var items = this.getSelectedItems();
                    var valueArr = [];
                    u.each(items, function (item, index) {
                        valueArr.push(item.value);
                    });
                    return valueArr;
                }
            }
        );

        /**
         * 根据选项数据生成选择项
         *
         * @param {Object} item 选项数据
         * @param {string} style 额外的样式
         * @param {number} index 数据源中的索引
         * @return {HtmlElement} 生成的选择项元素
         */
        function buildItem(item, style, index) {
            var template = ''
                + '<div class="${item | class} ${style}"'
                + '  data-value="${value}" data-index="${index}" data-allow-delete="${allowDelete}">'
                + '<span>${text | raw}</span>'
                + '<!-- if: ${allowDelete}-->'
                + '<span class="ui-icon ui-filter-remove ui-filter-item-remove"></span>'
                + '<!-- /if -->'
                + '</div>';
            var data = {
                style: style || '',
                value: item.value,
                index: index,
                text: item.text,
                allowDelete: item.allowDelete
            };

            return this.helper.render(template, data);
        }

        /**
         * 生成自定义项
         */
        function buildCustomItem() {
            var controlHelper = this.helper;
            var template = '<div id="${custom-link | id}" class="${item-cmd | class}"><span>${text}</span></div>';
            var data = {
                text: this.customBtnLabel
            };
            var html = this.helper.render(template, data);

            $(controlHelper.getPart('items-panel')).append(html);
        }

        esui.register(Filter);
        return Filter;
    }
示例#14
0
    function (require) {
        var Control = require('esui/Control');
        var u = require('underscore');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        var esui = require('esui');

        require('./AdvancedColorPicker');
        require('./SimpleColorPicker');

        /**
         * @class FullColorPicker
         * @extends esui.InputControl
         */
        var FullColorPicker = eoo.create(
            Control,
            {
                /**
                 * 控件类型
                 *
                 * @override
                 */
                type: 'FullColorPicker',

                /**
                 * 初始化参数
                 *
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 默认颜色
                        hex: '000000',
                        displayHex: '000000',
                        alpha: 100,
                        displayAlpha: 100,
                        // 默认拾色器模式,支持 'simple' | 'advanced' | 'full'
                        defaultMode: 'simple',
                        // 是否支持切换为'full'型
                        // mode为'simple',属性可配
                        // mode为'advanced',属性可配
                        // mode为'full',属性只能为false
                        switchable: true,
                        // 是否支持alpha选择
                        hasAlpha: true
                    };

                    u.extend(properties, FullColorPicker.defaultProperties, options);

                    // mode是full型,switchable必须是false
                    if (properties.mode === 'full' || properties.switchable === false) {
                        properties.switchable = false;
                    }

                    this.setProperties(properties);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    var html = [];
                    var advancedHTML = generateAdvancedHTML.call(this);
                    var simpleHTML = generateSimpleHTML.call(this);
                    // 根据模式创建结构
                    if (this.defaultMode === 'full' || this.switchable) {
                        html = [advancedHTML, simpleHTML];
                    }
                    else if (this.defaultMode === 'simple') {
                        html = [simpleHTML];
                    }
                    else {
                        html = [advancedHTML];
                    }
                    // 如果是可切换的,还要加上切换按钮
                    if (this.switchable) {
                        var switchClass = this.helper.getPartClassName('mode-switch');
                        html.push('<div data-ui-variants="link fluid" data-ui-type="Button" data-ui-child-name="switch" ');
                        html.push('class="' + switchClass + '">' + this.fullModeText + '</div>');
                    }

                    this.main.innerHTML = html.join('');
                    this.initChildren();
                    this.currentMode = this.defaultMode;
                    this.addState(this.defaultMode);
                },

                /**
                 * @override
                 * @fires FullColorPicker#change
                 */
                initEvents: function () {
                    var control = this;
                    // 大色盘
                    var advancedColorPicker = this.getChild('advanced');
                    if (advancedColorPicker) {
                        // 高级色盘改变引发简单色盘色彩重置
                        advancedColorPicker.on(
                            'change',
                            function () {
                                var hex = this.getDisplayHex();
                                var alpha = this.getDisplayAlpha();
                                control.displayHex = hex;
                                control.displayAlpha = alpha;
                                control.fire('change');
                            }
                        );
                    }

                    // 基本色盘变化
                    var SimpleColorPicker = this.getChild('simple');
                    if (SimpleColorPicker) {
                        SimpleColorPicker.on(
                            'change',
                            function () {
                                var color = this.getRawValue();
                                control.displayHex = color;
                                updateAdvancedColorPicker.call(control);
                                control.fire('change');
                            }
                        );
                    }

                    var switchButton = this.getChild('switch');
                    if (switchButton) {
                        switchButton.on('click', u.bind(switchState, this));
                    }
                },

                /**
                 * 渲染自身
                 *
                 * @override
                 */
                repaint: painters.createRepaint(
                    Control.prototype.repaint,
                    {
                        name: ['hex', 'alpha'],
                        paint: function (colorPicker, hex, alpha) {
                            if (hex == null && alpha == null) {
                                return;
                            }
                            // 更新显示值
                            colorPicker.displayHex = hex;
                            colorPicker.displayAlpha = alpha;

                            // 更新高级模式
                            var advanced = colorPicker.getChild('advanced');
                            if (advanced) {
                                advanced.setProperties({hex: hex, alpha: alpha});
                            }
                        }
                    }
                ),

                /**
                 * 获取色值
                 *
                 * @method FullColorPicker#getDisplayHex
                 * @return {string}
                 * @public
                 */
                getDisplayHex: function () {
                    return this.displayHex;
                },

                /**
                 * 获取透明度
                 *
                 * @method FullColorPicker#getDisplayAlpha
                 * @return {number}
                 * @public
                 */
                getDisplayAlpha: function () {
                    return this.displayAlpha;
                }
            }
        );

        FullColorPicker.defaultProperties = {
            fullModeText: '完整模式',
            advancedModeText: '高级模式',
            simpleModeText: '简单模式'
        };

        function generateAdvancedHTML() {
            return ''
                + '<div class="' + this.helper.getPartClassName('advanced-section') + '">'
                +     '<div data-ui-type="AdvancedColorPicker" data-ui-child-name="advanced"'
                +         'data-ui-no-alpha="' + (this.hasAlpha ? 'false' : 'true') + '">'
                +     '</div>'
                + '</div>';
        }

        function generateSimpleHTML() {
            return ''
                + '<div class="' + this.helper.getPartClassName('simple-section') + '">'
                +     '<div data-ui-type="SimpleColorPicker" data-ui-child-name="simple">'
                +     '</div>'
                + '</div>';
        }

        /**
         * 模式切换
         */
        function switchState() {
            var newMode;
            var switchButton = this.getChild('switch');
            // 如果当前处于full模式,切换回默认模式
            if (this.currentMode === 'full') {
                newMode = this.defaultMode;
                switchButton.setContent(this.fullModeText);
            }
            else {
                newMode = 'full';
                if (this.currentMode === 'simple') {
                    switchButton.setContent(this.simpleModeText);
                }
                else {
                    switchButton.setContent(this.advancedModeText);
                }
            }

            this.removeState(this.currentMode);
            this.addState(newMode);
            this.currentMode = newMode;
        }

        function updateAdvancedColorPicker() {
            // 更新色盘
            var colorPicker = this.getChild('advanced');
            if (colorPicker) {
                var color = this.displayHex;
                colorPicker.updateHex(color);
                var alpha = this.displayAlpha;
                colorPicker.updateAlpha(alpha);
            }
        }

        esui.register(FullColorPicker);
        return FullColorPicker;
    }
示例#15
0
    function (require) {
        require('esui/Panel');
        require('esui/TextBox');
        require('esui/Select');

        var esui = require('esui');
        var u = require('underscore');
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var eoo = require('eoo');
        var painters = require('esui/painters');

        require('esui/behavior/mouseproxy');
        var $ = require('jquery');

        var Slider = eoo.create(
            InputControl,
            {
                /**
                 * 控件的类型
                 *
                 * @override
                 * @type {String}
                 */
                type: 'Slider',

                /**
                 * 参数的初始化
                 *
                 * @protected
                 * @override
                 * @param  {Object} options [初始化的参数]
                 */
                initOptions: function (options) {

                    /**
                     * 默认的属性
                     *
                     * @type {Object}
                     * @type {string} defaults.orientation 滑块的形式 横着为'' 竖着为’vertical‘
                     * @type {number} defaults.start 起始值 默认是0
                     * @type {number} defaults.end 结束值 默认是10,
                     * @type {number} defaults.step 滑动杆值的步长默认是1
                     * @type {number | Arrary} defaults.value 滑动杆的值 默认为min或[min, max]
                     * @type {number} defaults.min 最小值 不能小于start, 无值时与start相同
                     * @type {number} defaults.max 最大值 不能大于end,无值时与end相同
                     * @type {boolean} defaults.isShowSelectedBG 滑杆已选择的部分是否加背景色显示 显示true 不显示false 默认true
                     * @type {boolean} defaults.range 滑动杆控件是否是选择区间 默认false 是true
                     */
                    var defaults = {
                        orientation: '',
                        start: 0,
                        end: 10,
                        step: 1,
                        min: null,
                        max: null,
                        isShowSelectedBG: true,
                        range: false
                    };

                    var properties = {};

                    u.extend(properties, defaults, options);

                    // 处理min和max
                    properties.min = properties.min || properties.start;
                    properties.max = properties.max || properties.end;

                    // min和max只能在start和end的中间
                    properties.min = Math.max(properties.min, properties.start);
                    properties.max = Math.min(properties.max, properties.end);

                    // 水平、竖直滑动杆时的设置
                    if (properties.orientation === 'vertical') {
                        // 竖直滑动杆时参数的设置
                        properties.leftTop = 'top';
                        properties.rightBottom = 'bottom';
                        properties.widthHeight = 'height';
                        properties.pageXY = 'pageY';
                    }
                    else {
                        // 水平时参数的设置
                        properties.leftTop = 'left';
                        properties.rightBottom = 'right';
                        properties.widthHeight = 'width';
                        properties.pageXY = 'pageX';
                    }

                    // 适配value的数据
                    properties = adaptValue.call(this, properties);

                    this.$super([properties]);
                },

                /**
                 * 将字符串类型的值转换成原始格式,复杂类型的输入控件需要重写此接口
                 *
                 * @param {string} value 要转换的string
                 * @param {Object} properties 参数对象
                 * @return {Mixed}
                 * @protected
                 */
                parseValue: function (value, properties) {
                    if ((properties && properties.range) || this.range) {
                        if (typeof value === 'string') {
                            var arr = value.split(',');
                            return [+arr[0], +arr[1]];
                        }
                    }

                    return value;
                },

                /**
                 * 批量设置控件的属性值
                 *
                 * @param {Object} properties 属性值集合
                 * @override
                 */
                setProperties: function (properties) {

                    // 给控件设值的时候适配数据用
                    if (properties.hasOwnProperty('rawValue')) {
                        properties = adaptValue.call(this, properties);

                    }

                    this.$super([properties]);
                },

                /**
                 * 创建滑动杆体
                 * 有滑块的范围和滑块,
                 * 滑块的范围分为显示的范围、已选的范围
                 * 滑动杆可能有一个滑块或两个滑块,类型是区间时可能有两个滑块,最大值和最小值
                 * 任意一个是显示的起始值时显示一个滑块
                 * 放在原型里是为了可重写
                 *
                 * @protected
                 */
                createBody: function () {
                    var bodyElement = this.bodyElement = this.helper.createPart('body');
                    var cursorElement = this.cursorElement = this.helper.createPart('body-cursor');

                    bodyElement.appendChild(cursorElement);

                    // 区间时需要两个滑块
                    if (this.range) {
                        var cursorElementTwo
                            = this.cursorElementTwo
                            = this.helper.createPart('body-cursortwo');

                        $(this.cursorElementTwo).addClass(this.helper.getPartClassName('body-cursor'));

                        bodyElement.appendChild(cursorElementTwo);
                    }

                    // 已选择的范围加个背景色
                    if (this.isShowSelectedBG) {
                        // 已选择的区间元素
                        var bodySelectedElement
                            = this.bodySelectedElement
                            = this.helper.createPart('body-selected');

                        bodyElement.appendChild(bodySelectedElement);
                    }

                    this.main.appendChild(bodyElement);

                    // 初始化body内元素的宽度和位置
                    initBodyElements(this);
                },

                /**
                 * 初始化dom结构,仅在第一次渲染的时候使用
                 *
                 * @protected
                 * @override
                 */
                initStructure: function () {
                    // 竖直滑动杆时增加样式
                    if (this.orientation === 'vertical') {
                        $(this.main).addClass(this.helper.getPartClassName('vertical'));
                    }

                    /\d+/.test(this.size) && (this.main.style[this.widthHeight] = this.size + 'px');
                    this.createBody();
                },

                /**
                 * 初始化事件的交互
                 *
                 * @protected
                 * @override
                 */
                initEvents: function () {
                    // 绑定滑块的事件
                    bindCursorEvents.call(this);
                },

                /**
                 * 获取滑动杆的值
                 *
                 * @return {*} 滑动杆的值
                 */
                getValue: function () {
                    var value;

                    if (this.range) {
                        value = [this.minRangeValue, this.maxRangeValue];
                    }
                    else {
                        value = this.getRawValue();
                    }

                    return value;
                },

                /**
                 * 重新渲染
                 *
                 * @protected
                 * @override
                 * @type {Function} 重新渲染时要执行的函数
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: 'rawValue',
                        paint: function (slider, value) {
                            setByValue(slider, value, true);
                        }
                    }
                ),

                /**
                 * 销毁控件
                 *
                 * @protected
                 * @override
                 */
                dispose: function () {
                    this.bodyElement = null;
                    this.cursorElement = null;
                    this.bodySelectedElement = null;
                    this.activeCursorElement = null;

                    if (this.range) {
                        this.cursorElementTwo = null;
                    }


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

        /**
         * 适配控件的value
         *
         * @param  {Object} properties 参数
         * @return {Object} 适配后的参数
         */
        function adaptValue(properties) {

            var value = properties.value;
            delete properties.value;

            if (value != null && properties.rawValue == null) {
                properties.rawValue = this.parseValue(value, properties);
            }

            properties.min = typeof properties.min !== 'undefined' ? properties.min : this.min;
            properties.max = typeof properties.max !== 'undefined' ? properties.max : this.max;

            if (properties.range || this.range) {
                // 值类型是区间时
                properties.rawValue = typeof properties.rawValue === 'undefined'
                    ? [properties.min, properties.max] : properties.rawValue;

                // 结果是区间时
                properties.minRangeValue = properties.rawValue[0];
                properties.maxRangeValue = properties.rawValue[1];

                properties.minRangeValue = Math.max(properties.minRangeValue, properties.min);
                properties.maxRangeValue = Math.min(properties.maxRangeValue, properties.max);

                // value只能在[min, max]之间
                properties.rawValue = [
                    properties.minRangeValue,
                    properties.maxRangeValue
                ];
            }
            else {
                // 值类型是单个值时
                properties.rawValue = typeof properties.rawValue === 'undefined'
                    ? properties.min : properties.rawValue;

                // value只能在min 和 max中间
                properties.rawValue = Math.max(properties.rawValue, properties.min);
                properties.rawValue = Math.min(properties.rawValue, properties.max);
            }

            return properties;
        }

        /**
         * 绑定滑块拖拽的事件
         *
         * @private
         */
        function bindCursorEvents() {
            var body = this.helper.getPart('body');

            // 给滑块绑定事件
            if (body) {
                $(body).mouseproxy(
                    {
                        start: u.bind(mousedownHandler, this),
                        drag: u.bind(mousemoveHandler, this),
                        stop: u.bind(mouseupHandler, this)
                    }
                );

                // 点在其他空白处,滑块也要移动到这里
                this.helper.addDOMEvent(body, 'click', mousedownHandler);
            }
        }

        /**
         * 根据滑块left或top的值来计算value
         *
         * @param {number} cursorLeftTop 滑块位置left或top的值
         * @return {number} 滑块的值
         * @private
         */
        function getValueByLeftTop(cursorLeftTop) {
            var widthHeight = this.widthHeight;

            // 滑块容器的宽度
            var tmpWidthHeight = this[widthHeight];
            // 选择的宽度
            var selectedWidthHeight = cursorLeftTop;
            var similarValue = (selectedWidthHeight / tmpWidthHeight) * (this.end - this.start);

            // 根据步长算值
            similarValue = similarValue - similarValue % this.step;

            var value = this.start + Math.round(similarValue);

            return value;
        }

        /**
         * 根据值获取滑块的位置
         *
         * @param {number} value 滑块的值
         * @return {number} 滑块的左侧位置
         * @private
         */
        function getLeftTopByValue(value) {
            var widthHeight = this.widthHeight;

            var bodyElement = this.bodyElement;
            // 获取滑块容器的位置
            var bodyPos = lib.getOffset(bodyElement);
            var tmpwidthHeight = bodyPos[widthHeight];
            var start = this.start;
            var end = this.end;

            var cursorLeftTop = (value - start) / (end - start) * tmpwidthHeight;

            return cursorLeftTop;
        }

        /**
         * 根据值去做相应的调整,包括head里显示、赋值和微调滑块的位置
         * 为啥要微调位置,因为你不知道鼠标会停在哪,比如1,2之间跨度太大时 要落到值上
         *
         * @param {Slider} slider 滑动杆控件
         * @param {number} value  滑动杆的值
         */
        function setByValue(slider, value) {
            var cursorElement = slider.cursorElement;
            var cursorLeftTop;

            var leftTop = slider.leftTop;
            var widthHeight = slider.widthHeight;

            if (slider.range) {
                var cursorElementTwo = slider.cursorElementTwo;
                var cursorLeftTopTwo = getLeftTopByValue.call(slider, value[1]);

                cursorElementTwo.style[leftTop] = cursorLeftTopTwo + 'px';
                cursorLeftTop = getLeftTopByValue.call(slider, value[0]);

                // hack: 默认第一个滑块的z-index是2 第二个滑块的z-index的是3
                // 因为区间的值可以是2,2这种,当两个滑块值是这种切最大值时,这时只能滑块1可拖动
                // 这时要把它放在第二个滑块上面
                if (value[0] === value[1] && value[0] === slider.max) {
                    cursorElement.style.zIndex = 3;
                    cursorElementTwo.style.zIndex = 2;
                }
                else {
                    cursorElement.style.zIndex = 2;
                    cursorElementTwo.style.zIndex = 3;
                }
            }
            else {
                cursorLeftTop = getLeftTopByValue.call(slider, value);
            }

            // 调整滑块的位置
            cursorElement.style[leftTop] = cursorLeftTop + 'px';

            // 已选择的部分加个背景色显示
            if (slider.isShowSelectedBG) {

                if (slider.range) {
                    slider.bodySelectedElement.style[leftTop] = cursorLeftTop + 'px';

                    slider.bodySelectedElement.style[widthHeight] = cursorLeftTopTwo - cursorLeftTop + 'px';
                }
                else {
                    slider.bodySelectedElement.style[widthHeight] = cursorLeftTop + 'px';
                }
            }
        }

        /**
         * 鼠标移动的事件
         *
         * @param {Event} e 事件对象
         * @param {boolean=} isMouseUp 是否是鼠标松开的触发 是为true 不是为false
         * @param {Object} data 事件fire时的data
         * @return {number} 返回value 让mouseup用
         * @private
         */
        function mousemoveHandler(e, isMouseUp, data) {

            if (this.disabled === true) {
                return false;
            }
            if (!u.isBoolean(isMouseUp)) {
                data = isMouseUp;
                isMouseUp = false;
            }

            var target = this.activeCursorElement;
            var cursorElement = this.cursorElement;

            var pageXY = this.pageXY;
            var leftTop = this.leftTop;
            var widthHeight = this.widthHeight;

            // 拖动的滑块距left的值
            var cursorLeftTop;

            // 滑块区间的时候
            if (this.range) {
                // 拖动的是否是第一个滑块
                var isFirst = false;
                // 另外一个滑块的left
                var otherLeftTop;
                // 另一个滑块的值
                var otherValue;

                // 滑块是第一个时
                if (target.id === cursorElement.id) {
                    otherLeftTop = getLeftTopByValue.call(this, this.maxRangeValue);
                    otherValue = this.maxRangeValue;
                    isFirst = true;

                    cursorLeftTop = Math.max(
                        this.minStartPos - this.startPos,
                        e[pageXY] - this.startPos
                    );

                    cursorLeftTop = Math.min(cursorLeftTop, otherLeftTop);
                }
                else {
                    // 滑块是第二个时
                    otherLeftTop = getLeftTopByValue.call(this, this.minRangeValue);
                    otherValue = this.minRangeValue;

                    cursorLeftTop = Math.max(otherLeftTop, e[pageXY] - this.startPos);

                    cursorLeftTop = Math.min(cursorLeftTop, this.maxEndPos - this.startPos);
                }
            }
            else {
                target = cursorElement;

                cursorLeftTop = Math.max(
                    this.minStartPos - this.startPos,
                    e[pageXY] - this.startPos
                );

                cursorLeftTop = Math.min(
                    cursorLeftTop,
                    this.maxEndPos - this.startPos
                );
            }

            // 根据left来计算值
            var value;
            var curValue = getValueByLeftTop.call(this, cursorLeftTop);

            if (this.range) {
                if (isFirst) {
                    value = [curValue, otherValue];
                }
                else {
                    value = [otherValue, curValue];
                }
            }
            else {
                value = curValue;
            }

            if (!isMouseUp) {
                // 避免抖动,这里根据value值重新计算出leftTop
                cursorLeftTop = getLeftTopByValue.call(this, curValue);
                target.style[this.leftTop] = cursorLeftTop + 'px';

                // 已选择的部分加个背景色显示
                if (this.isShowSelectedBG) {
                    if (this.range) {

                        var tmpWidthHeight;

                        if (isFirst) {
                            this.bodySelectedElement.style[leftTop] = cursorLeftTop + 'px';
                            tmpWidthHeight = otherLeftTop - cursorLeftTop;
                        }
                        else {
                            this.bodySelectedElement.style[leftTop] = otherLeftTop + 'px';
                            tmpWidthHeight = cursorLeftTop - otherLeftTop;
                        }

                        this.bodySelectedElement.style[widthHeight] = tmpWidthHeight + 'px';

                    }
                    else {
                        this.bodySelectedElement.style[widthHeight]
                            = cursorLeftTop + 'px';
                    }
                }

                // 滑动的时候触发move事件
                this.fire('move', value);
            }

            return value;
        }

        /**
         * 鼠标的松开事件
         *
         * @param {Event} e 事件的对象
         * @private
         */
        function mouseupHandler(e) {
            if (this.disabled === true) {
                return false;
            }
            // 去掉active的样式
            $(this.activeCursorElement).removeClass(
                this.helper.getPartClassName('body-cursor-active')
            );

            // 放开和mousemove时做得事是一样的,再做一遍
            var value = mousemoveHandler.call(this, e, true);

            // 设置控件的值,因为是内部设值不涉及重绘,所以不调set*方法了
            this.rawValue = value;
            this.minRangeValue = value[0];
            this.maxRangeValue = value[1];

            setByValue(this, value, true);

            // 放开鼠标的时候触发change事件
            this.fire('change', value);
        }

        /**
         * 初始化body内元素的坐标和宽度
         *
         * @param  {Slider}  slider 滑动杆控件
         */
        function initBodyElements(slider) {
            var bodyElement = slider.bodyElement;
            // 获取滑块容器的位置
            var bodyPos = lib.getOffset(bodyElement);

            var leftTop = slider.leftTop;
            var rightBottom = slider.rightBottom;
            var widthHeight = slider.widthHeight;

            // 获取滑块容器的宽度,用来计算值用
            slider[widthHeight] = bodyPos[widthHeight];

            // 滑块能去的最左边
            if (typeof slider.min !== 'undefined') {
                var minLeftTop = getLeftTopByValue.call(slider, slider.min);
                // 滑块所能去的最左边
                slider.minStartPos = bodyPos[leftTop] + minLeftTop;
                // 滑竿范围的最左边
                slider.startPos = bodyPos[leftTop];
            }

            // 滑块能去的最右侧
            if (typeof slider.max !== 'undefined') {
                var maxLeftTop = getLeftTopByValue.call(slider, slider.max);

                slider.maxEndPos = bodyPos[leftTop] + maxLeftTop;
                slider.endPos = bodyPos[rightBottom];
            }
        }

        /**
         * 根据鼠标位置,寻找离鼠标位置最近的handle
         *
         * @private
         * @param {Event} e 事件对象
         * @return {Element}
         */
        function findNearestCursorElement(e) {
            var pageXY = this.pageXY;
            var leftTop = this.leftTop;
            var bodyElement = this.helper.getPart('body');
            var bodyPos = lib.getOffset(bodyElement);

            var mouseLeftTop = e[pageXY] - bodyPos[leftTop];

            // 有两个滑块
            if (this.range) {
                var firstLeftTop = getLeftTopByValue.call(this, this.minRangeValue);
                var secondLeftTop = getLeftTopByValue.call(this, this.maxRangeValue);
                var middleLeftTop = firstLeftTop + (secondLeftTop - firstLeftTop) / 2;
                if (mouseLeftTop > middleLeftTop && this.cursorElementTwo) {
                    return this.cursorElementTwo;
                }
            }
            return this.cursorElement;
        }

        /**
         * 鼠标的按下事件
         *
         * @private
         * @param {Event} e 事件对象
         * @return {boolean}
         */
        function mousedownHandler(e) {

            if (this.disabled === true) {
                return false;
            }

            var cursorElement = findNearestCursorElement.call(this, e);

            // 存住活动的对象
            this.activeCursorElement = cursorElement;

            // 增加active的样式
            $(cursorElement).addClass(this.helper.getPartClassName('body-cursor-active'));

            // 点击的时候再初始化各种坐标 为了一些初始化时不在屏幕内的控件
            initBodyElements(this);

            // 滑块首先移动到鼠标点击位置
            mousemoveHandler.call(this, e);

            return true;
        }

        esui.register(Slider);
        return Slider;
    }
示例#16
0
    function (require) {
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var u = require('underscore');
        var eoo = require('eoo');
        var esui = require('esui');
        var painters = require('esui/painters');
        var $ = require('jquery');

        /**
         * 简单版拾色器
         *
         * @class SimpleColorPicker
         * @extends esui.InputControl
         */
        var SimpleColorPicker = eoo.create(
            InputControl,
            {
                /**
                 * 控件类型
                 *
                 * @override
                 */
                type: 'SimpleColorPicker',

                /**
                 * 初始化参数
                 *
                 * @override
                 * @protected
                 */
                initOptions: function (options) {
                    var properties = {};
                    u.extend(properties, SimpleColorPicker.defaultProperties, options);
                    this.setProperties(properties);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    this.main.innerHTML = createColorBlocks(this);
                },

                /**
                 * @override
                 */
                initEvents: function () {
                    this.$super(arguments);

                    this.helper.addDOMEvent(this.main, 'click', chooseColor);
                },

                /**
                 * 渲染自身
                 *
                 * @override
                 * @protected
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: 'rawValue',
                        paint: function (colorPicker, rawValue) {
                            syncValue(colorPicker);
                        }
                    }
                ),

                /**
                 * 批量更新属性并重绘
                 *
                 * @fires SimpleColorPicker#change
                 * @override
                 * @public
                 */
                setProperties: function (properties) {
                    var changes = this.$super(arguments);

                    if (changes.hasOwnProperty('rawValue')) {
                        this.fire('change');
                    }

                    return changes;
                }
            }
        );

        /**
         * 默认属性
         *
         * @static
         * @type {Object}
         * @public
         */
        SimpleColorPicker.defaultProperties = {
            colors: [
                // row1
                {text: '#ffffff', value: 'ffffff'},
                {text: '#ededed', value: 'ededed'},
                {text: '#d2d2d2', value: 'd2d2d2'},
                {text: '#bfbfbf', value: 'bfbfbf'},
                {text: '#a0a0a0', value: 'a0a0a0'},
                {text: '#898989', value: '898989'},
                {text: '#6f6f6f', value: '6f6f6f'},
                {text: '#626262', value: '626262'},
                {text: '#434343', value: '434343'},
                {text: '#333333', value: '333333'},
                {text: '#1b1b1b', value: '1b1b1b'},
                {text: '#000000', value: '000000'},
                // row2
                {text: '#50a7f9', value: '50a7f9'},
                {text: '#6ebf40', value: '6ebf40'},
                {text: '#fff45c', value: 'fff45c'},
                {text: '#f39017', value: 'f39017'},
                {text: '#ec5d57', value: 'ec5d57'},
                {text: '#b36ae2', value: 'b36ae2'},
                {text: '#0065c0', value: '0065c0'},
                {text: '#92d500', value: '92d500'},
                {text: '#f5d327', value: 'f5d327'},
                {text: '#c82503', value: 'c82503'},
                {text: '#f39017', value: 'f39017'},
                {text: '#ec5d57', value: 'ec5d57'},
                // row3
                {text: '#86ccc8', value: '86ccc8'},
                {text: '#acd599', value: 'acd599'},
                {text: '#7fcdf4', value: '7fcdf4'},
                {text: '#8c97cb', value: '8c97cb'},
                {text: '#aa8abd', value: 'aa8abd'},
                {text: '#f19fc2', value: 'f19fc2'},
                {text: '#f26071', value: 'f26071'},
                {text: '#e60013', value: 'e60013'},
                {text: '#eb6102', value: 'eb6102'},
                {text: '#f8b551', value: 'f8b551'},
                {text: '#7fc169', value: '7fc169'},
                {text: '#009d97', value: '009d97'},
                // row4
                {text: '#0068b7', value: '0068b7'},
                {text: '#1e2087', value: '1e2087'},
                {text: '#611986', value: '611986'},
                {text: '#920783', value: '920783'},
                {text: '#e5007f', value: 'e5007f'},
                {text: '#a40000', value: 'a40000'},
                {text: '#a84300', value: 'a84300'},
                {text: '#cea973', value: 'cea973'},
                {text: '#996b34', value: '996b34'},
                {text: '#81511c', value: '81511c'},
                {text: '#372f2c', value: '372f2c'},
                {text: '#a6927d', value: 'a6927d'}
            ],
            mode: 'block'
        };

        function syncValue(colorPicker) {
            var blocks = colorPicker.main.getElementsByTagName('span');
            var blockClass = colorPicker.helper.getPartClassName('block');

            u.each(
                blocks,
                function (block) {
                    var $block = $(block);
                    if ($block.hasClass(blockClass)) {
                        var color = $block.attr('data-value');
                        if (color === this.rawValue) {
                            this.helper.addPartClasses('selected', block);
                        }
                        else {
                            this.helper.removePartClasses('selected', block);
                        }
                    }
                },
                colorPicker
            );
        }

        /**
         * 创建候选颜色块
         *
         * @param {SimpleColorPicker} colorPicker 控件实例
         * @return {string} html
         */
        function createColorBlocks(colorPicker) {
            var blockTemplate = ''
                + '<span class="' + colorPicker.helper.getPartClassName('block') + '" '
                +     'title="${text}" '
                +     'data-value="${value}" '
                +     'style="background-color: ${diplayValue}">'
                +     '${text}'
                + '</span>';

            var html = '<div>';
            u.each(
                colorPicker.colors,
                function (color, index) {
                    color.diplayValue = color.value;
                    if (color.value.indexOf('#') < 0) {
                        color.diplayValue = '#' + color.value;
                    }
                    html += lib.format(blockTemplate, color);
                }
            );
            html += '</div>';

            return html;
        }

        /**
         * 选择颜色
         *
         * @param {Event} e DOM事件对象
         */
        function chooseColor(e) {
            var blockClass = this.helper.getPartClassName('block');
            var $t = $(e.target);
            if ($t.hasClass(blockClass)) {
                var color = $t.attr('data-value');
                this.setRawValue(color);
            }
        }

        esui.register(SimpleColorPicker);
        return SimpleColorPicker;
    }
示例#17
0
    function (require) {
        var esui = require('esui');
        var $ = require('jquery');
        var u = require('underscore');
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var eoo = require('eoo');
        var painters = require('esui/painters');

        require('esui/TextBox');

        /**
         * TokenField
         *
         * @class
         * @extends esui.InputControl
         */
        var TokenField = eoo.create(
            InputControl,
            {

                /**
                 * 控件类型,始终为`"TokenField"`
                 *
                 * @type {string}
                 * @readonly
                 * @override
                 */
                type: 'TokenField',

                /**
                 * 初始化配置
                 *
                 * @protected
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {

                        /**
                         * 控件宽度
                         *
                         * @type {number}
                        */
                        width: 300,

                        /**
                         * 输入框最小宽度,剩余宽度不够就换行了
                         *
                         * @type {number}
                        */
                        inputWidth: 90,

                        /**
                         * token最小字符串长度,低于该长度不创建, 默认不限制
                         *
                         * @type {number}
                        */
                        minLength: 0,

                        /**
                         * token最大数量,默认不限制
                         *
                         * @type {number}
                        */
                        limit: 0,

                        /**
                         * 是否允许重复
                         *
                         * @type {bool}
                        */
                        allowRepeat: false,

                        /**
                         * 默认空数组
                         */
                        rawValue: []
                    };
                    u.extend(properties, options);

                    properties.name = properties.name || this.main.getAttribute('name');
                    this.setProperties(properties);
                },

                /**
                 * 初始化DOM结构
                 *
                 * @protected
                 * @override
                 */
                initStructure: function () {

                    // 如果用的是一个`<input>`,替换成`<div>`
                    if (this.main.nodeName.toLowerCase() === 'input') {
                        this.helper.replaceMain();
                        this.main.id = this.helper.getId();
                    }

                    var html = [
                        '<input type="text" autocomplete="off"',
                        ' class="${inputClasses}"',
                        ' data-ui-type="TextBox"',
                        ' data-ui-width="${width}"',
                        ' data-ui-id="${inputId}">'
                    ].join('');

                    this.main.innerHTML = lib.format(
                        html,
                        {
                            inputId: this.helper.getId('input'),
                            inputClasses: this.helper.getPartClasses('input'),
                            width: this.inputWidth
                        }
                    );
                    // 创建控件树
                    this.initChildren();
                },

                /**
                 * 初始化事件交互
                 *
                 * @protected
                 * @override
                 */
                initEvents: function () {
                    var controlHelper = this.helper;

                    controlHelper.addDOMEvent(this.main, 'click', this.focusInput);
                    var itemClass = this.helper.getPartClassName('item');
                    controlHelper.addDOMEvent(this.main, 'click', '.' + itemClass, this.remove);

                    var input = this.getInput();
                    input.on('focus', this.focus, this);
                    input.on('blur', this.blur, this);
                    input.on('enter', this.enter, this);

                    var inputElem = input.getFocusTarget();
                    controlHelper.addDOMEvent(inputElem, 'keydown', this.keydown);
                    controlHelper.addDOMEvent(inputElem, 'keyup', this.keyup);
                },

                /**
                 * 获取真实输入框控件
                 *
                 * @return {esui.TextBox}
                 */
                getInput: function () {
                    var inputId = this.helper.getId('input');
                    return this.viewContext.get(inputId);
                },

                /**
                 * 输入框focus
                 *
                 * @param {Event} e 事件对象
                 */
                focusInput: function (e) {
                    var input = this.getInput();
                    input.getFocusTarget().focus();
                },

                /**
                 * 响应focus, 输入框获取焦点时,控件整体相应的focus
                 *
                 * @param {Event} e 事件对象
                 */
                focus: function (e) {
                    this.focused = true;
                    this.helper.addStateClasses('focus');
                },

                /**
                 * 响应blur
                 *
                 * @param {Event} e 事件对象
                 */
                blur: function (e) {
                    this.focused = false;
                    this.helper.removeStateClasses('focus');
                },

                /**
                 * 响应keydown,keydown较keyup早触发,keydown时可记录输入框变化前的值
                 *
                 * @param {Event} e 事件对象
                 */
                keydown: function (e) {
                    var input = this.getInput();
                    switch (e.keyCode) {
                        // backspace
                        case 8:
                            if (input.getFocusTarget() === document.activeElement) {
                                // keydown触发早于keyup,keydown时记下当前输入框的字符
                                // 用于keyup时判断是否应删除token
                                this.lastInputValue = input.getRawValue();
                            }
                            break;
                        default:
                            break;
                    }
                },

                /**
                 * 响应keyup
                 *
                 * @param {Event} e 事件对象
                 */
                keyup: function (e) {
                    if (!this.focused) {
                        return;
                    }
                    var input = this.getInput();
                    var inputValue = input.getRawValue();
                    switch (e.keyCode) {
                        // backspace
                        case 8:
                        // delete
                        case 46:
                            if (input.getFocusTarget() === document.activeElement) {
                                if (inputValue.length || this.lastInputValue) {
                                    break;
                                }
                                this.remove();
                            }
                            break;
                    }
                },

                /**
                 * 用户按下回车或者预设置的triggerKey,则触发token创建
                 *
                 * @param {Event} e 事件对象
                 */
                enter: function (e) {
                    var input = e.target;
                    var inputValue = input.getRawValue();
                    if (input.getFocusTarget() === document.activeElement && inputValue.length) {
                        createTokensFromInput.call(this);
                    }
                },

                /**
                 * 删除token,有两种情况:
                 * 1. 用户点击删除按钮
                 * 2. 用户在真实输入框按下backspace / delete键,且输入框中无字符
                 *
                 * @param {Event=} e 事件对象
                 */
                remove: function (e) {
                    var deleteIndex = -1;
                    var rawValue = this.rawValue.slice(0);

                    var target = e && e.currentTarget;
                    if (!target) {
                        // 通过回车删除, 则删除最后一个
                        deleteIndex = rawValue.length - 1;
                    }
                    else {
                        // 找到用户点击了哪个item上的删除按钮
                        var itemClass = this.helper.getPartClassName('item');
                        deleteIndex = $(this.main).find('.' + itemClass).index(target);
                    }

                    if (deleteIndex >= 0) {
                        var removedValue = rawValue.splice(deleteIndex, 1);
                        this.fire('removetoken', {token: removedValue[0]});
                        this.setProperties({rawValue: rawValue});
                    }
                },

                /**
                 * 将字符串类型的值转换成数组格式
                 *
                 * @param {string} value 字符串值
                 * @return {Array}
                 * @protected
                 */
                parseValue: function (value) {
                    if (u.isString(value)) {
                        return value.split(',');
                    }
                    return value || [];
                },

                /**
                 * 将值从原始格式转换成字符串
                 *
                 * @param {Array} rawValue 原始值
                 * @return {string}
                 * @protected
                 */
                stringifyValue: function (rawValue) {
                    if (u.isArray(rawValue)) {
                        return rawValue.join(',');
                    }
                    return '';
                },


                /**
                 * 重绘
                 *
                 * @protected
                 * @override
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    painters.style('width'),
                    {
                        name: ['disabled', 'readOnly'],
                        paint: function (textbox, disabled, readOnly) {
                            var input = textbox.getInput();
                            input.setProperties(
                                {
                                    disabled: disabled,
                                    readOnly: readOnly
                                }
                            );
                        }
                    },
                    {
                        name: ['rawValue'],
                        paint: function (textbox, rawValue) {
                            renderTokens.call(textbox, rawValue);
                        }
                    }
                ),

                /**
                 * 清空所有token
                 */
                clearAllTokens: function () {
                    var itemClass = this.helper.getPartClassName('item');
                    $(this.main).find('.' + itemClass).remove();
                },

                /**
                 * 销毁
                 *
                 * @protected
                 * @override
                 */
                dispose: function () {
                    var controlHelper = this.helper;

                    controlHelper.removeDOMEvent(this.main, 'click', this.focusInput);
                    var itemClass = this.helper.getPartClassName('item');
                    controlHelper.removeDOMEvent(this.main, 'click', '.' + itemClass, this.remove);

                    var input = this.getInput();
                    input.un('focus', this.focus, this);
                    input.un('blur', this.blur, this);
                    input.un('enter', this.enter, this);

                    var inputElem = input.getFocusTarget();
                    controlHelper.removeDOMEvent(inputElem, 'keydown', this.keydown);
                    controlHelper.removeDOMEvent(inputElem, 'keyup', this.keyup);

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

        /**
         * 检测token是否合法
         *
         * @param {Object} token 要检测的token
         * @return {boolean} 是否合法
         */
        function isTokenValid(token) {
            // token长度要大于最小长度限制
            if (!token || token.length <= this.minLength) {
                return false;
            }

            return true;
        }

        /**
         * 查找重复token
         *
         * @param {Object} token 要检测的token
         * @return {Object|null} 如果找到重复token,则返回,否则返回null
         */
        function findRepeatToken(token) {
            if (!this.allowRepeat) {
                var repeatIndex = u.indexOf(this.rawValue, token);
                if (repeatIndex > -1) {
                    var itemClass = this.helper.getPartClassName('item');
                    return {
                        index: repeatIndex,
                        element: $(this.main).find('.' + itemClass).get(repeatIndex),
                        token: this.rawValue[repeatIndex]
                    };
                }
            }
            return null;
        }

        /**
         * 闪动指定元素
         *
         * @param {Object} repeatToken 重复的token对象
         */
        function flashToken(repeatToken) {
            this.helper.addPartClasses('flash', repeatToken.element);

            var me = this;
            setTimeout(
                function () {
                    me.helper.removePartClasses('flash', repeatToken.element);
                },
                300
            );
        }

        /**
         * 创建token,添加到dom中
         *
         * @param {string|Object} token 要创建的token定义
         */
        function renderToken(token) {
            token = lib.trim(token);

            var event = this.fire('beforecreate', {token: token});
            if (event.preventDefault()) {
                return;
            }

            var $tokenElem = $('<div></div>');
            $tokenElem.addClass(this.helper.getPartClassName('item'));
            $tokenElem.html(
                this.helper.getPartHTML('label', 'span')
                + this.helper.getPartHTML('close', 'span')
            );

            // token标签值
            var $tokenLabel = $tokenElem.children(':first-child');
            $tokenLabel.html(token);
            // 关闭按钮
            var $closeButton = $tokenElem.children(':last-child');
            $closeButton.addClass(this.helper.getIconClass());

            var input = this.getInput();
            var inputElem = input.main;
            $tokenElem.insertBefore(inputElem);

            this.fire('aftercreate', {token: token});
        }

        /**
         * 对tokens进行预处理
         *
         * @param {Array=} rawValue 要设置的token数组
         */
        function renderTokens(rawValue) {
            if (u.isArray(this.rawValue)) {
                // 因为renderTokens是对rawValue进行全量渲染,所以这里要全部清空
                this.clearAllTokens();
                // 合法性校验
                this.rawValue = u.filter(this.rawValue, isTokenValid, this);

                // token数量要小于最大限制
                this.rawValue = u.filter(
                    this.rawValue,
                    function (item, index) {
                        return index < this.limit
                    },
                    this
                );

                // 重复检测,分为两个场景
                // 1. 根据初始rawValue值生成tokens,仅去重;
                // 2. 在用户输入值时,如果与已存在列表重复,需提示用户,
                //  派发事件等,则在用户输入时进行处理;
                // 因此,这里仅对this.rawValue进行重复过滤
                if (!this.allowRepeat) {
                    this.rawValue = u.uniq(this.rawValue);
                }
                // 根据rawValue值进行全量更新
                u.each(this.rawValue, renderToken, this);
            }
        }

        /**
         * 根据input输入创建tokens列表
         */
        function createTokensFromInput() {
            var beforeValue = this.getValue();

            var input = this.getInput();
            var inputValue = input.getRawValue();
            // 检测重复元素
            var repeatToken = findRepeatToken.call(this, inputValue);
            if (repeatToken) {
                flashToken.call(this, repeatToken);
                return;
            }
            // 这里要保证setProperties时rawValue前后值不同,这里复制一份
            if (!this.rawValue) {
                // 第一次没有设置的时候设置一个空值进来
                this.rawValue = [];
            }
            var rawValue = this.rawValue.slice(0);
            rawValue.push(inputValue);
            this.setProperties({rawValue: rawValue});

            if (beforeValue === this.getValue() && inputValue.length) {
                return;
            }
            // token创建成功,清空输入框
            input.setRawValue('');
        }

        esui.register(TokenField);
        return TokenField;
    }
示例#18
0
    function (require) {
        require('esui/Tree');

        var esui = require('esui');
        var eoo = require('eoo');

        var u = require('underscore');
        var util = require('../helper/util');
        var lib = require('esui/lib');
        var RichSelector = require('./RichSelector');
        var TreeStrategy = require('./SelectorTreeStrategy');
        var painters = require('esui/painters');
        var $ = require('jquery');

        /**
         * 控件类
         *
         * @constructor
         * @param {Object} options 初始化参数
         */
        var TreeRichSelector = eoo.create(
            RichSelector,
            {

                /**
                 * 控件类型,始终为`"TreeRichSelector"`
                 *
                 * @type {string}
                 * @override
                 */
                type: 'TreeRichSelector',

                /**
                 * @override
                 */
                styleType: 'RichSelector',

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 数据源
                        datasource: null,
                        // 定向展开配置开关,true则可以根据需要默认展开指定的节点
                        orientExpand: false,
                        // 全节点点击触发展开的配置开关
                        wideToggleArea: false,
                        // 是否只允许选择叶子节点
                        onlyLeafSelect: true,
                        // 是否隐藏根节点
                        hideRoot: true,
                        // 节点状态切换时,父子节点是否需要同步状态
                        // 有些需求场景是,父子节点除了概念上的从属关系外,交互上没有任何关联
                        // 选择父节点不代表其下的子节点全被选中;选择全部子节点也不代表父节点选中
                        needSyncParentChild: true,
                        // 树样式
                        treeVariants: 'icon angle hoverable',
                        // 大小写是否敏感。默认无视大小写
                        caseSensitive: false
                    };

                    u.extend(properties, options);
                    this.$super([properties]);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    this.$super(arguments);

                    $(this.main).addClass(
                        this.helper.getPrefixClass('treerichselector')
                    );

                    if (this.onlyLeafSelect) {
                        this.addState('only-leaf-selectable');
                    }
                },

                /**
                 * 重新渲染视图
                 * 仅当生命周期处于RENDER时,该方法才重新渲染
                 *
                 * @param {Array=} 变更过的属性的集合
                 * @override
                 */
                repaint: painters.createRepaint(
                    RichSelector.prototype.repaint,
                    {
                        name: 'datasource',
                        paint: function (me, datasource) {
                            me.refresh();
                            toggleTreeState(me, me.disabled);
                        }
                    },
                    {
                        name: 'selectedData',
                        paint: function (control, selectedData) {
                            // 如果没有传selectedData,就别取消了。
                            if (u.isEmpty(selectedData)) {
                                return;
                            }

                            // 先取消选择
                            var allData = control.allData;

                            // 对于根节点隐藏的情况
                            // 因为取值也不会取根节点,赋值如果等于根节点
                            // 避免所有子孙都被选中,这里无视
                            if (this.hideRoot && allData && selectedData.length === 1) {
                                var selectedItem = selectedData[0];
                                selectedItem = selectedItem.id || selectedItem.value || selectedItem;
                                if (selectedItem === allData.id) {
                                    return;
                                }
                            }

                            if (allData && allData.children) {
                                var oldSelectedData = control.getSelectedItems();
                                control.selectItems(oldSelectedData, false);
                                control.selectItems(selectedData, true);
                                control.fire('add');
                                control.fire('change');
                            }
                        }
                    },
                    {
                        name: 'disabled',
                        paint: function (me, disabled) {
                            toggleTreeState(me, disabled);
                        }
                    }
                ),

                /**
                 * 适配数据,创建一个全集扁平索引
                 *
                 * @param {ui.TreeRichSelector} treeForSelector 类实例
                 * @return {Object} 包含`indexData`和`selectedData`两个属性
                 * @ignore
                 */
                adaptData: function () {
                    var selectedData = [];
                    /**
                     * datasource的数据结构:
                     * {
                     *     id: -1,
                     *     text: '全部',
                     *     children: [
                     *         {
                     *             id: 1,
                     *             text: '节点1',
                     *             children: [],
                     *             // 以下属性都可以自定义
                     *             isSelected: true,
                     *             ...
                     *         }
                     *         ...
                     *     ]
                     * }
                     */
                    this.allData = lib.deepClone(this.datasource);
                    // 一个扁平化的索引
                    // 其中包含父节点信息,以及节点选择状态
                    var indexData = {};
                    var allData = this.allData;
                    if (allData && allData.children) {
                        this.walkTree(
                            allData,
                            allData.children,
                            function (parent, child) {
                                parent.id = parent.id || parent.value;
                                child.id = child.id || child.value;
                                indexData[child.id] = {
                                    parentId: parent.id,
                                    node: child,
                                    isSelected: false
                                };
                                if (child.hasOwnProperty('isSelected')) {
                                    indexData[child.id].isSelected = child.isSelected;
                                }
                                if (indexData[child.id].isSelected === true) {
                                    selectedData.push(child);
                                }
                            }
                        );
                        // 把根节点也加上
                        indexData[allData.id] = {
                            parentId: null,
                            node: allData,
                            isSelected: false
                        };
                    }
                    this.indexData = indexData;

                    return {
                        indexData: indexData,
                        selectedData: selectedData
                    };
                },

                /**
                 * @override
                 */
                processDataAfterRefresh: function (adaptedData) {
                    // 用这个数据结构更新选择状态
                    if (this.mode !== 'delete') {
                        this.selectItems(adaptedData.selectedData, true);
                    }
                },

                /**
                 * 刷新备选区
                 *
                 * @override
                 */
                refreshContent: function () {
                    var treeData = this.isQuery() ? this.queriedData : this.allData;
                    if (!treeData || !treeData.children || !treeData.children.length) {
                        this.addState('empty');
                    }
                    else {
                        this.removeState('empty');
                    }

                    if (!treeData || !treeData.children) {
                        return;
                    }

                    var queryList = this.getQueryList();
                    var tree = queryList.getChild('tree');
                    if (!tree) {
                        var options = {
                            childName: 'tree',
                            datasource: treeData,
                            allowUnselectNode: this.allowUnselectNode,
                            strategy:
                                new TreeStrategy(
                                    {
                                        mode: this.mode,
                                        onlyLeafSelect: this.onlyLeafSelect,
                                        orientExpand: this.orientExpand
                                    }
                                ),
                            wideToggleArea: this.wideToggleArea,
                            hideRoot: this.hideRoot,
                            selectMode: this.multi ? 'multiple' : 'single',
                            variants: this.treeVariants
                        };
                        if (this.getItemHTML) {
                            options.getItemHTML = this.getItemHTML;
                        }
                        if (this.itemTemplate) {
                            options.itemTemplate = this.itemTemplate;
                        }
                        tree = esui.create('Tree', options);
                        queryList.addChild(tree);
                        tree.appendTo(queryList.main);

                        var control = this;
                        tree.on(
                            'selectnode',
                            function (e) {
                                var node = e.node;
                                control.handlerAfterClickNode(node);
                            }
                        );

                        tree.on(
                            'unselectnode',
                            function (e) {
                                // control.setItemState(e.node.id, 'isSelected', false);
                                control.handlerAfterClickNode(e.node);
                            }
                        );
                    }
                    else {
                        tree.setProperties(
                            {
                                datasource: lib.deepClone(treeData),
                                keyword: this.getKeyword()
                            }
                        );
                    }
                },

                getStateNode: function (id) {
                    return this.indexData[id];
                },

                getItemState: function (id, stateName) {
                    if (this.indexData[id]) {
                        var stateNode = this.getStateNode(id);
                        return stateNode[stateName];
                    }
                    return null;
                },

                setItemState: function (id, stateName, stateValue) {
                    if (this.indexData[id]) {
                        var stateNode = this.getStateNode(id);
                        stateNode[stateName] = stateValue;
                    }
                },

                getDatasourceWithState: function () {
                    var datasource = lib.deepClone(this.datasource);
                    var indexData = this.indexData;
                    this.walkTree(datasource, datasource.children, function (parent, child) {
                        child.isSelected = indexData[child.id].isSelected;
                    });

                    return datasource;
                },

                /**
                 * 点击触发,选择或删除节点
                 *
                 * @param {Object} node 节点对象
                 * @ignore
                 */
                handlerAfterClickNode: function (node) {
                    // 这个item不一定是源数据元,为了连锁同步,再取一遍
                    var item = this.indexData[node.id];
                    if (!item) {
                        return;
                    }

                    if (this.mode === 'add') {
                        this.actionForAdd(item);
                    }
                    else if (this.mode === 'delete') {
                        this.actionForDelete(item);
                    }
                    else if (this.mode === 'load') {
                        this.actionForLoad(item);
                    }
                },

                /**
                 * 添加动作
                 *
                 * @param {Object} item 保存在indexData中的item
                 *
                 */
                actionForAdd: function (item) {
                    var stateNode = this.getStateNode(item.node.id);
                    var toBeSelected = true;
                    if (stateNode.isSelected && this.allowUnselectNode) {
                        toBeSelected = false;
                    }
                    // 如果是单选,需要将其他的已选项置为未选
                    if (!this.multi) {
                        // 如果以前选中了一个,要取消选择
                        // 节点的状态切换Tree控件会完成,因此无需这里手动unselect
                        if (this.currentSeletedId != null) {
                            this.setItemState(this.currentSeletedId, 'isSelected', !toBeSelected);
                        }
                        // 赋予新值
                        if (toBeSelected) {
                            this.currentSeletedId = item.node.id;
                        }
                    }

                    this.setItemState(item.node.id, 'isSelected', toBeSelected);

                    // 多选同步父子状态
                    // 要先更新当前节点状态,再同步祖先与子孙
                    if (this.multi) {
                        trySyncParentAndChildrenStates(this, item, toBeSelected);
                    }

                    this.fire('add', {item: item.node});
                    this.fire('change');
                },

                /**
                 * 添加全部
                 *
                 * @override
                 */
                selectAll: function () {
                    var data = this.isQuery() ? this.queriedData : this.allData;
                    var root = this.getStateNode(data.id);
                    selectItem(this, root.node.id, true);
                    trySyncChildrenStates(this, root, true);
                    this.fire('add');
                    this.fire('change');
                },

                /**
                 * 批量选择或取消选择,供外部调用,不提供fire事件
                 *
                 * @param {Array} nodes 要改变状态的节点集合
                 * @param {boolean} toBeSelected 目标状态 true是选择,false是取消
                 * @override
                 */
                selectItems: function (nodes, toBeSelected) {
                    var indexData = this.indexData;
                    if (!indexData) {
                        return;
                    }
                    var control = this;
                    u.each(
                        nodes,
                        function (node) {
                            var id = node.id !== undefined ? node.id : node;
                            var item = indexData[id];
                            if (item != null && item !== undefined) {
                                // 更新状态,但不触发事件
                                selectItem(control, id, toBeSelected);
                                trySyncParentAndChildrenStates(control, item, toBeSelected);
                            }
                        }
                    );
                },

                /**
                 * 删除动作
                 *
                 * @param {Object} item 保存在indexData中的item
                 *
                 */
                actionForDelete: function (item) {
                    // 外部需要知道什么数据被删除了
                    var event = this.fire('delete', {items: [item.node]});
                    // 如果外面阻止了默认行为(比如自己控制了Tree的删除),就不自己删除了
                    if (!event.isDefaultPrevented()) {
                        deleteItem(this, item.node.id);
                        this.fire('change');
                    }
                },

                /**
                 * 删除全部
                 *
                 * @FIXME 删除全部要区分搜索和非搜索状态么
                 * @override
                 */
                deleteAll: function () {
                    var event = this.fire('delete', {items: this.getSelectedItems()});
                    // 如果外面阻止了默认行为(比如自己控制了Tree的删除),就不自己删除了
                    if (!event.isDefaultPrevented()) {
                        this.set('datasource', null);
                        this.fire('change');
                    }
                },

                /**
                 * 加载动作
                 *
                 * @param {Object} item 保存在indexData中的item
                 */
                actionForLoad: function (item) {
                    this.setItemState(item.node.id, 'isActive', true);
                    // 如果以前选中了一个,要取消选择
                    if (this.currentActiveId) {
                        this.setItemState(this.currentActiveId, 'isActive', false);

                        // load型树节点状态不是简单的“已选择”和“未选择”,还包含已激活和未激活
                        // -- 选择状态中的节点不可以激活
                        // -- 未选择状态的节点可以激活,激活后变成“已激活”状态,而不是“已选择”
                        // -- 激活某节点时,其余“已激活”节点要变成“未激活”状态
                        // 说了这么多,就是想说,我要自己把“已激活”节点要变成“未激活”状态。。。
                        // 然后如果这个节点恰好是isSelected状态的,那则不许执行unselect操作
                        if (!this.getStateNode(this.currentActiveId).isSelected) {
                            var tree = this.getQueryList().getChild('tree');
                            tree.unselectNode(this.currentActiveId, true);
                        }
                    }
                    // 赋予新值
                    this.currentActiveId = item.node.id;

                    this.fire('load', {item: item.node});
                    this.fire('change');
                },


                /**
                 * 获取指定状态的叶子节点,递归
                 *
                 * @param {Array} data 检测的数据源
                 * @param {boolean} isSelected 选择状态还是未选状态
                 * @return {Array} 叶子节点
                 * @ignore
                 */
                getLeafItems: function (data, isSelected) {
                    data = data || (this.allData && this.allData.children) || [];
                    var leafItems = [];
                    var me = this;
                    u.each(
                        data,
                        function (item) {
                            if (isLeaf(item)) {
                                var valid = (isSelected === this.getItemState(item.id, 'isSelected'));
                                // delete型的树没有“选择”和“未选择”的状态区别,所以特殊处理
                                if (me.mode === 'delete' || valid) {
                                    leafItems.push(item);
                                }
                            }
                            else {
                                leafItems = u.union(
                                    leafItems,
                                    me.getLeafItems(item.children, isSelected)
                                );
                            }
                        },
                        this
                    );

                    return leafItems;
                },

                /**
                 * 获取当前已选择数据的扁平数组结构
                 *
                 * @return {Array}
                 * @public
                 */
                getSelectedItems: function () {
                    if (!this.allData) {
                        return [];
                    }
                    var selectedItems = [];
                    var control = this;
                    this.walkTree(
                        this.allData,
                        this.allData.children,
                        function (parent, child) {
                            if (control.mode === 'delete' || control.getStateNode(child.id).isSelected) {
                                selectedItems.push(child);
                            }
                        }
                    );

                    return selectedItems;
                },


                /**
                 * 获取当前已选择的数据的树形结构
                 *
                 * @return {Object}
                 * @public
                 */
                getSelectedTree: function () {
                    var control = this;
                    // clone完整数据,这个数据是原始的,不带最新选择状态的
                    var copyData = lib.deepClone(this.allData);
                    // 遍历树,把各个节点的children更新成只包含已选状态节点的
                    this.walkTree(
                        copyData,
                        copyData.children,
                        function (parent, child) {
                            // 找出所有选中节点或父节点状态为`isSomeSelected`, 即该节点存在选中的子孙节点
                            var selectedChildren = getSelectedNodesUnder(child, control);
                            if (selectedChildren.length) {
                                child.children = selectedChildren;
                            }
                            else {
                                child.children = null;
                            }
                        }
                    );
                    // 最外层再处理一下
                    copyData.children = u.filter(copyData.children, function (node) {
                        // 可能是叶子节点
                        return node.children || control.indexData[node.id].isSelected;
                    });
                    return copyData;
                },


                /**
                 * @override
                 */
                getSelectedItemsFullStructure: function () {
                    return this.getSelectedTree();
                },

                /**
                 * 清除搜索结果
                 *
                 * @return {boolean}
                 * @ignore
                 */
                clearQuery: function () {
                    this.$super(arguments);

                    if (this.mode !== 'delete') {
                        var selectedData = this.getSelectedItems();
                        this.selectItems(selectedData, true);
                    }
                    return false;
                },

                /**
                 * 清空搜索的结果
                 *
                 */
                clearData: function () {
                    // 清空数据
                    this.queriedData = {};
                },

                /**
                 * 搜索含有关键字的结果
                 *
                 * @param {Array} filters 过滤参数
                 */
                queryItem: function (filters) {
                    // Tree就只定位一个关键词字段
                    var keyword = filters[0].value;
                    var filteredTreeData = [];
                    filteredTreeData = queryFromNode.call(this, keyword, this.allData);
                    // 更新状态
                    this.queriedData = {
                        id: getTopId(this), text: '符合条件的结果', children: filteredTreeData
                    };
                    this.addState('queried');
                    this.refreshContent();
                    var selectedData = this.getSelectedItems();
                    // 删除型的不用设置
                    if (this.mode !== 'delete') {
                        this.selectItems(selectedData, true);
                    }
                },

                /**
                 * 一个遍历树的方法
                 *
                 * @param {Object} parent 父节点
                 * @param {Array} children 需要遍历的树的孩子节点
                 * @param {Function} callback 遍历时执行的函数
                 */
                walkTree: function (parent, children, callback) {
                    u.each(
                        children,
                        function (child, key) {
                            callback(parent, child);
                            this.walkTree(child, child.children, callback);
                        },
                        this
                    );
                },

                /**
                 * 获取当前列表的结果个数
                 *
                 * @return {number}
                 * @public
                 */
                getFilteredItemsCount: function () {
                    var node = this.isQuery() ? this.queriedData : this.allData;
                    var count = getChildrenCount(this, node, true);
                    return count;
                },


                /**
                 * 获取当前状态的显示个数
                 *
                 * @return {number}
                 * @override
                 */
                getCurrentStateItemsCount: function () {
                    var node = this.isQuery() ? this.queriedData : this.allData;
                    if (!node) {
                        return 0;
                    }
                    var count = getChildrenCount(this, node, true);
                    return count;
                },

                /**
                 * 判断是否根节点
                 *
                 * @param {Object} node 节点对象
                 * @return {boolean}
                 */
                isRoot: function (node) {
                    return node.id === getTopId(this);
                }
            }
        );

        /**
         * 选择或取消选择
         *   如果控件是单选的,则将自己置灰且将其他节点恢复可选
         *   如果控件是多选的,则仅将自己置灰
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @param {Object} id 结点对象id
         * @param {boolean} toBeSelected 置为选择还是取消选择
         *
         * @ignore
         */
        function selectItem(control, id, toBeSelected) {
            var tree = control.getQueryList().getChild('tree');
            // 完整数据
            var indexData = control.indexData;
            var item = indexData[id];

            if (!item) {
                return;
            }

            // 如果是单选,需要将其他的已选项置为未选
            if (!control.multi && toBeSelected) {
                unselectCurrent(control);
                // 赋予新值
                control.currentSeletedId = id;
            }

            control.setItemState(id, 'isSelected', toBeSelected);

            if (toBeSelected) {
                tree.selectNode(id, true);
            }
            else {
                tree.unselectNode(id, true);
            }
        }

        /**
         * 撤销选择当前项
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @ignore
         */
        function unselectCurrent(control) {
            var curId = control.currentSeletedId;
            // 撤销当前选中项
            var treeList = control.getQueryList().getChild('tree');
            treeList.unselectNode(curId);
            control.currentSeletedId = null;
        }

        /**
         * 同步一个节点的父节点和子节点选择状态
         * 比如:父节点选中与子节点全部选中的状态同步
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @param {Object} item 保存在indexData中的item
         * @param {boolean} toBeSelected 目标状态 true是选择,false是取消
         */
        function trySyncParentAndChildrenStates(control, item, toBeSelected) {
            if (!control.needSyncParentChild) {
                return;
            }
            trySyncParentStates(control, item, toBeSelected);
            trySyncChildrenStates(control, item, toBeSelected);
        }

        /**
         * 同步一个节点的父节点选择状态
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @param {Object} item 保存在indexData中的item
         * @param {boolean} toBeSelected 目标状态 true是选择,false是取消
         */
        function trySyncChildrenStates(control, item, toBeSelected) {
            // 如果当前节点确定选中或者未选中状态
            // 就不可能处于半选状态了
            control.setItemState(item.node.id, 'isSomeSelected', false);

            var indexData = control.indexData;
            var node = item.node;
            // 如果选的是父节点,子节点也要连带选上
            var children = node.children || [];
            u.each(children, function (child) {
                selectItem(control, child.id, toBeSelected);
                trySyncChildrenStates(control, indexData[child.id], toBeSelected);
            });
        }

        /**
         * 同步一个节点的子节点选择状态
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @param {Object} item 保存在indexData中的item
         * @param {boolean} toBeSelected 目标状态 true是选择,false是取消
         */
        function trySyncParentStates(control, item, toBeSelected) {
            var indexData = control.indexData;
            // 选的是子节点,判断一下是不是全部选择了,全部选择了,也要勾上父节点
            var parentId = item.parentId;
            var parentItem = indexData[parentId];

            if (parentItem) {
                var brothers = parentItem.node.children || [];
                var allSelected = !u.find(
                    brothers,
                    function (brother) {
                        return !control.getItemState(brother.id, 'isSelected');
                    }
                );
                // 如果子节点部分选中,则标记父节点`isSomeSelected`为true
                var someSelected = u.some(
                    brothers,
                    function (brother) {
                        return control.getItemState(brother.id, 'isSelected')
                            || control.getItemState(brother.id, 'isSomeSelected');
                    }
                );
                if (!allSelected && someSelected) {
                    control.setItemState(parentId, 'isSomeSelected', true);
                }
                else {
                    control.setItemState(parentId, 'isSomeSelected', false);
                }
                selectItem(control, parentId, allSelected);
                trySyncParentStates(control, parentItem, allSelected);
            }
        }

        /**
         * 删除选择的节点
         *
         * @param {ui.TreeRichSelector} control 类实例
         * @param {number} id 结点数据id
         *
         * @ignore
         */
        function deleteItem(control, id) {
            // 完整数据
            var indexData = control.indexData;
            var topId = getTopId(control);
            // 根节点
            if (topId === id) {
                control.setProperties({datasource: {}});
            }
            else {
                var item = indexData[id];

                var parentId = item.parentId;
                var parentItem = indexData[parentId];
                var node = parentItem.node;
                var children = node.children || [];
                // 从parentNode的children里删除
                var newChildren = u.without(children, item.node);
                // 没有孩子了,父节点也删了吧,遇到顶级父节点就不要往上走了,直接删掉
                if (newChildren.length === 0 && parentId !== topId) {
                    deleteItem(control, parentId);
                }
                else {
                    node.children = newChildren;
                    control.setProperties({datasource: control.allData});
                }
            }
        }


        function getSelectedNodesUnder(parentNode, control) {
            var children = parentNode.children;
            return u.filter(
                children,
                function (node) {
                    return this.getItemState(node.id, 'isSelected')
                        || this.getItemState(node.id, 'isSomeSelected');
                },
                control
            );

        }

        /**
         * 供递归调用的搜索方法
         *
         * @param {string} keyword 关键字
         * @param {Object} node 节点对象
         * @return {Array} 结果集
         */
        function queryFromNode(keyword, node) {
            var filteredTreeData = [];
            var treeData = node.children;
            u.each(
                treeData,
                function (data, key) {
                    var filteredData;
                    // 命中节点,先保存副本,之后要修改children

                    var config = {
                        caseSensitive: this.caseSensitive,
                        isPartial: true
                    };

                    if (util.compare(data.text, keyword, config)) {
                        filteredData = u.clone(data);
                    }

                    if (data.children && data.children.length) {
                        var filteredChildren = queryFromNode.call(this, keyword, data);
                        // 如果子节点有符合条件的,那么只把符合条件的子结点放进去
                        if (filteredChildren.length > 0) {
                            if (!filteredData) {
                                filteredData = u.clone(data);
                            }
                            filteredData.children = filteredChildren;
                        }
                        // 这段逻辑我还是留着吧,以防哪天又改回来。。。
                        // else {
                        //     // 如果命中了父节点,又没有命中子节点,则只展示父节点
                        //     if (filteredData) {
                        //         filteredData.children = [];
                        //     }
                        // }
                    }

                    if (filteredData) {
                        filteredTreeData.push(filteredData);
                    }
                },
                this
            );
            return filteredTreeData;
        }

        function isLeaf(node) {
            return !node.children;
        }

        function getChildrenCount(control, node, onlyLeaf) {
            var count = 1;
            // 是叶子节点,但不是root节点
            if (onlyLeaf) {
                if (isLeaf(node)) {
                    // FIXME: 这里感觉不应该hardcode,后期想想办法
                    if (!node.id || node.id === getTopId(control)) {
                        return 0;
                    }
                    return 1;
                }
                // 如果只算叶子节点,父节点那一个不算数,从0计数
                count = 0;
            }
            else {
                // 顶级节点不算
                if (node.id === getTopId(control)) {
                    count = 0;
                }
            }

            count += u.reduce(
                node.children,
                function (sum, child) {
                    return sum + getChildrenCount(control, child, onlyLeaf);
                },
                0
            );
            return count;
        }

        /**
         * 获取顶级节点id
         *
         * @param {ui.TreeRichSelector} control 当前的控件实例
         * @return {number}
         */
        function getTopId(control) {
            return control.allData.id;
        }

        function toggleTreeState(selector, disabled) {
            var queryList = selector.getQueryList();
            var tree = queryList.getChild('tree');

            if (!tree) {
                return;
            }
            disabled ? tree.disable() : tree.enable();
        }

        esui.register(TreeRichSelector);
        return TreeRichSelector;
    }
示例#19
0
    function (require) {
        require('esui/Button');
        require('esui/Link');
        require('./ProgressQueue');
        require('./FileInput');

        var eoo = require('eoo');
        var esui = require('esui');
        var ui = require('esui/main');
        var Control = require('esui/Control');
        var painters = require('esui/painters');

        var File = require('./File');

        var $ = require('jquery');
        var u = require('underscore');
        var lib = require('esui/lib');

        var NORMAL = 'normal';
        var LIST = 'list';
        var CARD = 'card';
        var ALL = 'all';

        var supportXHR = (window.File && window.XMLHttpRequest);
        var supportXHR2 = (supportXHR && new window.XMLHttpRequest().upload);

        var defaultQueue = {
            // 所有文件队列
            queueList: [],
            // 队列中所有文件的大小
            queueSize: 0,
            // 正在上传的文件
            uploadingFiles: [],
            // 等待开始的文件
            waitingFiles: [],
            // 出错的文件
            failedFiles: [],
            // 遗弃的文件
            // 当文件过长时会遗弃多余的文件
            abandonedFiles: [],
            // 上传完成的文件
            completeFiles: []
        };

        // card视图下模板
        var cardTemplate = [
            '<div class="${fileClass}">',
            '  <span class="${nameClass}">${fileName}</span>',
            '  <span class="${sizeClass}">${fileSize}</span>',
            '</div>',
            '<div class="${statusClass}">',
            '  <div class="${statusInfoClass}">',
            '    <div class="${barContainerClass}">',
            '      <div class="${barClass}" id="${barId}"></div>',
            '    </div>',
            '  </div>',
            '  <div class="${operationClass}">',
            '    <esui-button class="${startButtonClass} ui-button-link" data-ui-child-name="start">',
            '        开始',
            '    </esui-button>',
            '    <div class="${resultClass}" id="${resultId}"></div>',
            '    <esui-button class="${cancelButtonClass} ui-button-link" data-ui-child-name="cancel">',
            '        <span class="ui-icon-remove ui-eicons-fw"></span>',
            '    </esui-button>',
            '  </div>',
            '</div>'
        ].join('');

        // list视图下模板
        var listTemplate = [
            '<div class="${fileClass}">',
            '  <span class="${nameClass}">${fileName}</span>',
            '  <span class="${sizeClass}">${fileSize}</span>',
            '</div>',
            '<div class="${statusClass}">',
            '  <div class="${statusInfoClass}">',
            '    <div class="${barContainerClass}">',
            '      <div class="${barClass}" id="${barId}"></div>',
            '    </div>',
            '  </div>',
            '  <div class="${operationClass}">',
            '    <esui-button class="${startButtonClass} ui-button-link" data-ui-child-name="start">',
            '        开始',
            '    </esui-button>',
            '    <div class="${resultClass}" id="${resultId}"></div>',
            '    <esui-button class="${cancelButtonClass} ui-button-link" data-ui-child-name="cancel">',
            '        <span class="ui-icon-remove ui-eicons-fw"></span>',
            '    </esui-button>',
            '  </div>',
            '</div>'
        ].join('');

        /**
         * 控件类
         *
         * 上传控件有如下结构特点:
         *
         * - 上传组件          (必须,至少一个)
         *   -- 文件上传控件
         * - 上传列表        (可选,可自定义容器)
         *   -- 由一个或多个进度条组件构成
         *
         * 上传控件有两种模式:
         *
         * - 单文件上传
         * - 多文件上传
         *
         * @class ui.Uploader
         * @extends esui.Control
         */
        var Uploader = eoo.create(
            Control,
            {

                /**
                 * 控件类型,始终为`"Uploader"`
                 *
                 * @type {string}
                 * @readonly
                 * @override
                 */
                type: 'Uploader',

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 文件上传队列
                        queue: lib.deepClone(defaultQueue)
                    };
                    u.extend(properties, this.$self.defaultProperties, options);

                    var adaptProperties = ['sequentialUploads', 'multiple'];

                    u.each(
                        adaptProperties,
                        function (propertyName) {
                            if (properties[propertyName] === 'false') {
                                properties[propertyName] = false;
                            }
                        }
                    );

                    this.$super([properties]);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    var containerHtml = getContainerHtml.call(this, this.mode);
                    this.main.innerHTML = lib.format(
                        containerHtml,
                        {
                            uploadComboxClass: this.helper.getPartClassName('combox'),
                            uploadComboxHeaderClass: this.helper.getPartClassName('combox-header'),
                            uploadHeaderTag: this.helper.getPartClassName('header-tag'),
                            uploadHeaderTagOperation: this.helper.getPartClassName('tag-operation'),
                            uploadHeaderOutline: this.helper.getPartClassName('header-outline'),
                            totalCount: 0,
                            uploadTotalCountId: this.helper.getId('total-count'),
                            totalSize: 0,
                            uploadTotalSizeId: this.helper.getId('total-size'),
                            uploadRemoveAll: this.helper.getPartClassName('remove-all'),
                            uploadComboxBodyClass: this.helper.getPartClassName('combox-body'),
                            defaultProgressContainerId: this.helper.getId('default-progress-container'),
                            uploadComboxFooterClass: this.helper.getPartClassName('combox-footer'),
                            uploadComboxFooterId: this.helper.getId('combox-footer')
                        }
                    );

                    // 创建控件树
                    this.helper.initChildren();

                    // 创建主容器
                    if (this.mode !== NORMAL) {
                        var queueMode = this.mode === LIST ? LIST : CARD;
                        var progressContainer = $('#' + this.helper.getId('default-progress-container'))[0];
                        var progressQueue = ui.create('ProgressQueue', {
                            main: progressContainer,
                            progressMode: this.mode,
                            progressTemplate: this.itemTemplate,
                            showMode: queueMode
                        });

                        progressQueue.render();
                        this.progressQueue = progressQueue;

                        var cardTag = this.getChild(queueMode + 'Tag');
                        var tagClassName = this.helper.getPartClassName('tag-operation');
                        addedTagClass($(this), tagClassName, cardTag.main);
                    }
                },

                /**
                 * @override
                 */
                initEvents: function () {
                    var fileInput = this.getChild('fileInput');

                    // 伪装button的点击事件
                    var submitButton = this.getChild('submitButton');
                    submitButton.on(
                        'click',
                        function (e) {
                            fileInput.triggerUploadOutside();
                            e.preventDefault();
                        }
                    );

                    // 监听上传input的变化
                    fileInput.on('change', u.bind(inputChangeHandler, this));

                    var startAllBtn = this.getChild('startAll');
                    startAllBtn && startAllBtn.on('click', operationFileQueue, this);

                    var cancelAllBtn = this.getChild('cancelAll');
                    cancelAllBtn && cancelAllBtn.on('click', this.clear, this);

                    var $main = $(this.main);
                    var cardTag = this.getChild('cardTag');
                    var tagClassName = this.helper.getPartClassName('tag-operation');
                    cardTag && cardTag.on('click', function () {
                        addedTagClass($main, tagClassName, cardTag.main);
                        this.progressQueue.setProperties({
                            showMode: 'card'
                        });
                    }, this);

                    var listTag = this.getChild('listTag');
                    listTag && listTag.on('click', function () {
                        addedTagClass($main, tagClassName, listTag.main);
                        this.progressQueue.setProperties({
                            showMode: 'list'
                        });
                    }, this);

                    var progressQueue = this.progressQueue;
                    if (progressQueue) {
                        var me = this;

                        progressQueue.on('start', function (e) {
                            // 点击开始上传
                            chooseProgressFile.call(me, e.file, true);
                        });

                        progressQueue.on('restart', function (e) {
                            // 重新上传
                            var file = e.file;
                            e.target.dispose();
                            // 将文件移出上传队列,然后重新进行上传
                            removeFileFromUploading.call(me, file);
                            me.receiveFile([file]);
                        });

                        progressQueue.on('cancel', function (e) {
                            var file = e.file;
                            if (file.request) {
                                file.request.abort();
                                if (me.sequentialUploads) {
                                    operationFileQueue.call(me);
                                }
                            }
                            // 从等待队列中清除
                            removeFileFromWaiting.call(me, file);
                            progressQueue.removeProgress(file);
                            refreshStutas.call(me);
                        });

                        $(this.main).on('click', '.state-selector', {
                            progressQueue: progressQueue
                        }, switchProgressByState);
                    }
                },

                /**
                 * @override
                 */
                repaint: painters.createRepaint(
                    Control.prototype.repaint,
                    {
                        name: ['text'],
                        paint: function (uploader, text) {
                            var button = uploader.getChild('submitButton');
                            button.setContent(text);
                        }
                    },
                    {
                        name: ['disabled', 'readOnly'],
                        paint: function (uploader, disabled, readOnly) {
                            var input = uploader.getChild('fileInput');
                            var button = uploader.getChild('submitButton');
                            input.setProperties({disabled: disabled});
                            input.setProperties({readOnly: readOnly});
                            button.setProperties({disabled: disabled});
                            button.setProperties({readOnly: readOnly});
                        }
                    },
                    painters.style('width'),
                    painters.style('height')
                ),

                /**
                 * 接收文件并做上传前的校验
                 *
                 * @param {Array} files 接收到的文件,可以是input中取到的,也可以是drag外部传入的
                 * @public
                 */
                receiveFile: function (files) {
                    var event = this.fire('beforeupload', {files: files});
                    if (event.isDefaultPrevented()) {
                        return;
                    }

                    this.doUpload(files);
                },

                /**
                 * 开始上传
                 *
                 * @param {Array} files 接收到的文件
                 * @protected
                 */
                doUpload: function (files) {
                    // 超出最大限制,直接返回
                    if (files.length > this.maxFileNumber) {
                        this.notifyFail(this.message.ERROR_FILE_MAX_NUMBER);
                        return;
                    }

                    files = u.map(
                        files,
                        function (file, index) {
                            // 文件格式检查
                            if (!checkFileFormat.call(this, file)) {
                                file.status = 'fail';
                                file.message = this.message.ERROR_FILE_EXTENSIONS;
                                if (this.multiple === false) {
                                    this.queue.failedFiles = [file];
                                }
                                else {
                                    this.queue.failedFiles.push(file);
                                }
                            }
                            else if (!checkFileSize.call(this, file)) {
                                file.status = 'fail';
                                file.message = this.message.ERROR_FILE_MAX_SIEZ;
                                if (this.multiple === false) {
                                    this.queue.failedFiles = [file];
                                }
                                else {
                                    this.queue.failedFiles.push(file);
                                }
                            }
                            else {
                                file.status = 'waiting';
                                // 单文件上传要覆盖之前的文件
                                if (this.multiple === false) {
                                    this.queue.waitingFiles = [file];
                                }
                                else {
                                    this.queue.waitingFiles.push(file);
                                }
                            }
                            return file;
                        },
                        this
                    );

                    initFileList.call(this, files);
                },

                /**
                 * 获取Uploader中的文件上传组件
                 *
                 * @return {DOMElement} 容器中FileInput组件
                 * @public
                 */
                getFileInput: function () {
                    return this.getChild('fileInput');
                },

                /**
                 * 解析返回中的错误 TODO 这个的具体解析格式要跟后端商定
                 *
                 * @param {Object} response 请求返回的对象
                 * @return {string}
                 * @protected
                 */
                parseError: function (response) {
                    if (response.success === 'false') {
                        return {message: response.error};
                    }

                    return null;
                },

                /**
                 * 通知上传失败
                 *
                 * @method ui.Uploader#notifyFail
                 * @param {string} message 失败消息
                 * @protected
                 */
                notifyFail: function (message) {
                    message = message || '上传失败';
                    this.fire('fail', {message: message});
                },

                /**
                 * 通知上传完成
                 *
                 * @protected
                 * @method ui.Uploader#notifyComplete
                 */
                notifyComplete: function () {
                    this.stage = 'COMPLETE';
                    this.fire('complete', {
                        completeFiles: this.queue.completeFiles,
                        failedFiles: this.queue.failedFiles
                    });
                },

                /**
                 * 通知上传完成
                 *
                 * @public
                 * @method ui.Uploader#notifyComplete
                 */
                clear: function () {
                    // 等待队列先置空
                    this.queue.waitingFiles = [];
                    // 上传队列中都取消
                    u.each(this.queue.uploadingFiles, function (file) {
                        if (file.request) {
                            file.request.abort();
                        }
                    });
                    this.progressQueue.clearAllProgress();
                    this.queue = lib.deepClone(defaultQueue);
                    this.stage = 'COMPLETE';
                    refreshStutas.call(this);
                }
            }
        );

        /**
         * 默认属性
         *
         * @type {Object}
         * @public
         */
        Uploader.defaultProperties = {
            // 上传按钮文本
            text: '点击上传',
            // 文件上传的路径
            action: '/uploadFile',
            // 后台接收时的key名
            paramKey: 'files',
            // 接收的文件类型
            accept: '.gif,.jpg,.png,.swf,.flv',
            // 默认为单文件上传控件
            // 单文件上传控件一次只能选择一个文件
            multiple: false,
            // 单个文件最大大小,单位B,默认2M
            maxFileSize: 2147483648,
            // 单次最大上传文件数量
            maxFileNumber: 20,
            // 多文件上传时,请求同时开始还是逐个开始
            sequentialUploads: false,
            // 提示信息
            // 目前支持提供成功提示,文件大小不符
            // 文件类型不匹配,
            message: {
                // 上传成功
                SUCCESS_INFO: '已完成',
                // 重新上传
                RESTART_INFO: '正在重新上传',
                // http错误,一般来说就是status返回不是200
                ERROR_HTTP: '上传失败,网络连接错误或上传路径无效',
                // 文件类型错误
                ERROR_FILE_EXTENSIONS: '上传失败,安全因素不支持此类文件',
                // 文件超出最大尺寸, 标准浏览器下可以判断
                ERROR_FILE_MAX_SIEZ: '超出最大上传尺寸',
                // 文件数量超过最大限制了
                ERROR_FILE_MAX_NUMBER: '发生错误,上传文件超过限定。',
                // 格式错误
                ERROR_FILE_FORMAT: '文件格式错误'
            },
            // 是否显示进度
            showProgress: true,

            // removed
            // 进度模式,seperate和total两种,seperate代表每个文件独立进度;total代表所有文件统一计算进度
            // progressMode: 'seperate',
            // singleProgressMode: 'detail',

            // 文件列表的容器
            // 如果没有会添加一个默认容器
            progressContainer: null,
            // 是否自己开始
            autoStart: false,

            /**
             * @property {string} [mode="all"]
             *
             * 指定文本框模式,可以有以下值:
             *
             * - `normal`:表示仅仅只有一个上传按钮,不自带任何预览容器
             * - `card`:表示使用card视图进行显示
             * - `list`:表示使用list列表进行显示
             * - `all`: 表示使用card和list两种视图同时进行展示
             *
             * 此属性仅能在初始化时设置,运行期不能修改
             *
             */
            mode: 'all',

            // 容器模板
            containerTemplate: '',

            itemTemplate: {
                // card视图下的item的展示模板
                card: cardTemplate,
                /**
                 * @property {string|Object} [itemTemplate.list=""]
                 *
                 * 指定文本框模式,可以有以下值:
                 *
                 * - string:表示仅仅只有一个上传按钮,不自带任何预览容器
                 * - Object:表示使用card视图进行显示
                 *     - header 表示list需要一个默认的title
                 *     - content 表示content的模板,一般指item的模板
                 *
                 * 此属性仅能在初始化时设置,运行期不能修改
                 *
                 */
                list: listTemplate
            },

            // 是否可拖拽
            dragable: true,
            // 是否分片
            chunk: true,
            // 默认4M
            chunkSize: 4194304
        };


        /**
         * 上传输入组件变化事件处理
         *
         * @param {mini-event.Event} e 事件对象
         */
        function inputChangeHandler(e) {
            var files = this.getFileInput().files;
            this.receiveFile(files);
        }

        /**
         * 验证文件格式
         *
         * @param {Object} file file对象
         * @return {boolean}
         * @protected
         */
        function checkFileFormat(file) {
            if (this.accept) {
                // 这里就是个内置的`Rule`,走的完全是标准的验证流程,
                // 主要问题是上传控件不能通过`getValue()`获得验证用的内容,
                // 因此把逻辑写在控件内部了
                var extension = file.name.split('.');
                extension = '.' + extension[extension.length - 1].toLowerCase();

                var isValid = false;
                if (typeof this.accept === 'string') {
                    this.accept = lib.splitTokenList(this.accept);
                }
                for (var i = 0; i < this.accept.length; i++) {
                    var acceptPattern = this.accept[i].toLowerCase();
                    if (acceptPattern === extension) {
                        isValid = true;
                        break;
                    }

                    // image/*之类的,表示一个大类
                    if (acceptPattern.slice(-1)[0] === '*') {
                        var mimeType = acceptPattern.split('/')[0];
                        var targetExtensions = this.mimeTypes[mimeType];
                        if (targetExtensions && targetExtensions.hasOwnProperty(extension)) {
                            isValid = true;
                            break;
                        }
                    }
                }

                return isValid;
            }

            return true;
        }

        /**
         * 验证文件大小
         *
         * @param {Object} file file对象
         * @return {boolean}
         * @protected
         */
        function checkFileSize(file) {
            // IE9中filechange返回的event只有fileName以及fileId
            // 所以如果出现这种情况就放过去,让后端做长度校验
            if (this.maxFileSize && file.originalSize) {
                var isValid = false;
                if (file.originalSize) {
                    isValid = parseInt(file.originalSize, 10) <= parseInt(this.maxFileSize, 10);
                }

                return isValid;
            }

            return true;
        }

        /**
         * 创建上传进度队列
         *
         * @param {Array} fileList 在队列中的文件
         */
        function initFileList(fileList) {
            if (this.mode === NORMAL) {
                // 不显示进度条的时候自动开始
                // TODO 流程
                operationFileQueue.call(this);
                return;
            }

            var files = fileList ? fileList : this.queue.waitingFiles;

            u.each(files, function (file, index) {
                this.progressQueue.addProgress(file);
                this.queue.queueList.push(file);
            }, this);

            refreshStutas.call(this);

        }

        /**
         * 执行上传队列
         *
         */
        function operationFileQueue() {
            // 当队列中
            if (this.queue.waitingFiles && this.queue.waitingFiles.length) {
                // 一个个上传
                if (this.sequentialUploads && this.queue.uploadingFiles.length < 1) {
                    chooseProgressFile.call(this);
                }
                else {
                    chooseProgressFile.call(this);
                    operationFileQueue.call(this);
                }
            }
            else if (this.queue.uploadingFiles.length === 0) {
                this.notifyComplete();
            }
        }

        /**
         * 选择一个文件上传
         *
         * @param {Object|undefined} file 待上传的文件
         * @param {boolean} singleFlag 是否单线上传
         */
        function chooseProgressFile(file, singleFlag) {
            if (!file) {
                file = this.queue.waitingFiles.shift();
            }
            else {
                this.queue.waitingFiles = u.filter(this.queue.waitingFiles, function (wFile) {
                    return file.id !== wFile.id;
                });
            }
            // 等待队列中弹出
            // 进入上传队列
            this.queue.uploadingFiles.push(file);
            // 执行上传
            uploadFile.call(this, file, singleFlag);

            refreshStutas.call(this);

            if (!singleFlag && !this.sequentialUploads) {
                operationFileQueue.call(this);
            }
        }

        // function chunkFiles(file, singleFlag) {
        //     if (file.originalSize) {
        //         var chunkCount = Math.ceil(file.originalSize);
        //         if (chunkCount >= 2) {
        //             for (var i = 0; i < chunkCount; i++) {
        //                 var chunFile;
        //             }
        //         }
        //         else {

        //         }
        //     }
        //     else {
        //         uploadFile.call(this, file, singleFlag);
        //     }
        // }

        /**
         * 上传文件
         *
         * @param {ui.File} file 目标文件
         * @param {boolean} singleFlag 是否是one by one上传
         */
        function uploadFile(file, singleFlag) {
            var me = this;

            // 创建请求
            var request = getHttpRequest.call(this);
            file.request = request;
            // 修改文件状态
            file.status = File.UPLOADING;

            // 创建一个符合后端接口的数据对象
            var sendFile = {};
            sendFile[this.paramKey] = file.sourceFile;

            request.send(
                {container: this.main},
                sendFile
            );

            // 正常模式中不包含以下功能
            if (this.mode !== NORMAL) {
                // 上传中
                request.on(
                    'progress',
                    function (response) {
                        var loaded = response.loaded;
                        var total = response.total;
                        me.progressQueue.setProgressDetail(file, total, loaded);
                    }
                );

                // 传输终止
                request.on(
                    'abort',
                    function (event) {
                        removeFileFromUploading.call(me, file, 'abort');
                        me.fire('abort', {file: file});
                    }
                );
            }

            // 上传完成
            request.on(
                'load',
                function (event) {
                    var response = event.target.response;

                    // 解析一些校验错误
                    var error = me.parseError(response);

                    if (error) {
                        me.fire('error', {file: file});
                        if (me.mode !== NORMAL) {
                            // 修改进度状态
                            me.progressQueue.notifyError({
                                file: file,
                                status: 'fail',
                                message: error.message
                            });
                            me.removeFileFromUploading.call(me, file, 'error');
                            addToErrorQueue.call(me, file);
                        }
                        refreshStutas.call(me);
                        event.preventDefault();
                        return;
                    }

                    file.status = File.COMPLETE;

                    me.fire(
                        'onecomplete',
                        {
                            file: file,
                            data: response
                        }
                    );
                    if (me.mode !== NORMAL) {
                        // 修改进度状态
                        removeFileFromUploading.call(me, file, 'load');

                        if ((me.autoStart && me.sequentialUploads) || !singleFlag) {
                            operationFileQueue.call(me);
                        }
                        me.progressQueue.notifyError(file, 'complete', me.message.SUCCESS_INFO);
                        refreshStutas.call(me);
                    }
                }
            );

            // 上传出错
            request.on(
                'error',
                function (event) {
                    me.fire('error', {file: file});
                    if (me.mode !== NORMAL) {
                        // 修改进度状态
                        removeFileFromUploading.call(me, file, 'error');
                        me.progressQueue.notifyError(file, 'fail', event.message || me.message.ERROR_HTTP);

                        addToErrorQueue.call(me, file);

                        if ((me.autoStart && me.sequentialUploads) || !singleFlag) {
                            operationFileQueue.call(me);
                        }
                        refreshStutas.call(me);
                    }
                }
            );
        }

        /**
         * 将文件移出等待队列
         *
         * @param {ui.File} file 目标文件
         */
        function removeFileFromWaiting(file) {
            // TDDO 优化,移出队列方法
            this.queue.waitingFiles = u.without(this.queue.waitingFiles, file);
            this.queue.queueList = u.without(this.queue.queueList, file);
            this.queue.failedFiles = u.without(this.queue.failedFiles, file);
            this.queue.uploadingFiles = u.without(this.queue.uploadingFiles, file);
            this.queue.completeFiles = u.without(this.queue.completeFiles, file);
        }

        /**
         * 需要添加的错误文件
         *
         * @param {ui.File} file 目标文件
         */
        function addToErrorQueue(file) {
            var sameFile = u.filter(this.queue.failedFiles, function (rawFile) {
                return rawFile.id === file.id;
            });
            sameFile.length ? '' : this.queue.failedFiles.push(file);
        }

        /**
         * 将文件移出上传队列并放入完成队列
         *
         * @param {ui.File} file 目标文件
         * @param {string} operation 操作
         * [operation = 'load'] 将该操作放入到完成队列当中
         * [operation = 'error'] 将该操作放入到错误队列当中
         */
        function removeFileFromUploading(file, operation) {
            var queue = this.queue.uploadingFiles;
            file = u.find(
                queue,
                function (rawFile) {
                    return rawFile.id === file.id;
                }
            );
            this.queue.uploadingFiles = u.without(queue, file);

            // 放入到完成队列当中
            if ('load' === operation) {
                var completeFiles = this.queue.completeFiles;
                var sameFile = u.filter(completeFiles, function (rawFile) {
                    return rawFile.id === file.id;
                });
                sameFile.length ? '' : completeFiles.push(file);
            }
        }

        /**
         * 获取合适的HttpRequest,用于文件上传
         * 目前实现了Leve1 XHR、Level2 XHR以及iframe的封装
         *
         * @return {ui.HttpRequest}
         */
        function getHttpRequest() {
            var HTTPRequest;

            if (supportXHR2) {
                HTTPRequest = require('./L2XMLHttpRequest');
            }
            else if (supportXHR) {
                HTTPRequest = require('./XMLHttpRequest');
            }
            else {
                HTTPRequest = require('./IframeHttpRequest');
            }

            var httpInstance = new HTTPRequest('POST', this.action);
            return httpInstance;
        }

        /**
         * 获取容器的string
         *
         * @param {string} uploaderMode 当前的视图模式
         * @return {string}
         */
        function getContainerHtml(uploaderMode) {
            switch (uploaderMode) {
                case NORMAL:
                    return getNormalTemplate.call(this);
                default:
                    return getContainerTemplate.call(this);
            }
        }

        function getNormalTemplate() {
            if (this.containerTemplate) {
                return this.containerTemplate;
            }
            var tpl = [
                '<div class="${uploadComboxClass}">',
                // 上传input
                '   <div data-ui-child-name="fileInput"',
                '      data-ui="type:FileInput;accept:${accept};multiple:${multiple};name:${paramKey};"></div>',
                // 伪装ge按钮
                '   <div data-ui-child-name="submitButton" ',
                '      data-ui="type:Button;">${text}</div>',
                '</div>'
            ].join('');
            return tpl;
        }

        function getContainerTemplate() {
            var cardTagTemplate = '<a class="${uploadHeaderTagOperation}" '
                + 'data-ui="type:Link;childName:cardTag;href:javascript:void(0);">卡片</a>';
            var listTagTemplate = '<a class="${uploadHeaderTagOperation}" '
                + 'data-ui="type:Link;childName:listTag;href:javascript:void(0);">列表</a>';
            var opTemplate = '';

            if (this.mode === ALL) {
                opTemplate = cardTagTemplate + listTagTemplate;
            }
            else if (this.mode === CARD) {
                opTemplate = cardTagTemplate;
            }
            else if (this.mode === LIST) {
                opTemplate = listTagTemplate;
            }

            var tpl = [
                '<div class="${uploadComboxClass}">',
                '   <div class="${uploadComboxHeaderClass}">',
                '       <div class="${uploadHeaderTag}">',
                '       ' + opTemplate,
                '       </div>',
                '       <div class="${uploadHeaderOutline}">',
                '           <span id="${uploadTotalCountId}">总个数:${totalCount}</span>',
                '           <span id="${uploadTotalSizeId}">总大小:${totalSize}KB</span>',
                '           <esui-button data-ui-child-name="cancelAll" ',
                '           class="ui-button ui-button-link ${uploadRemoveAll}">',
                '           删除全部</esui-button>',
                '       </div>',
                '   </div>',
                '   <div class="${uploadComboxBodyClass}">',
                '       <div id="${defaultProgressContainerId}"></div>',
                '   </div>',
                '   <div class="${uploadComboxFooterClass}" id="${uploadComboxFooterId}">',
                '   ' + getStatusOperationsHtml.call(this),
                '   ' + getOperationHtml.call(this),
                '   </div>',
                '</div>'
            ].join('');

            return tpl;
        }

        // 获取四种状态操作结构
        function getStatusOperationsHtml() {
            // 完成,上传中,等待中,出错
            var statusTpl = [
                '<div class="ui-button-group" id="${uploadStatusList}">',
                '   <button type="button" data-state="complete" class="state-selector ui-button-group-first ',
                '   ui-button ui-button-primary-inverted">完成: ${completeFiles}个</button>',
                '   <button type="button" data-state="uploading" class="state-selector ui-button ',
                '   ui-button-primary-inverted">上传中: ${uploadingFiles}个</button>',
                '   <button type="button" data-state="waiting" class="state-selector ui-button ',
                '   ui-button-primary-inverted">等待中: ${waitingFiles}个</button>',
                '   <button type="button" data-state="fail" class="state-selector ui-button-group-last',
                '   ui-button ui-button-primary-inverted">出错: ${failedFiles}个</button>',
                '</div>'
            ].join('');

            return lib.format(statusTpl, {
                uploadStatusList: this.helper.getId('status-list'),
                completeFiles: this.queue.completeFiles.length,
                uploadingFiles: this.queue.uploadingFiles.length,
                waitingFiles: this.queue.waitingFiles.length,
                failedFiles: this.queue.failedFiles.length,
                totalCount: this.queue.queueList.length,
                totalSize: this.queue.queueSize
            });
        }

        // 获取操作项模板
        function getOperationHtml() {
            var operationHtml = [
                '<div class="operation-list">',
                '    <esui-button data-ui-child-name="startAll" class="ui-button ui-button-link">',
                '       全部开始',
                '    </esui-button>',
                // 断点续传没有实现,取消功能先注释掉
                // '     <esui-button data-ui-child-name="cancelAll" class="ui-button ui-button-link">',
                // '       全部取消',
                // '    </esui-button>',
                '    <esui-button data-ui-child-name="submitButton" class="ui-button ui-button-primary">',
                '       <span class="ui-icon-upload"></span>${text}', // 伪装ge按钮
                '    </esui-button>',
                '    <div data-ui-child-name="fileInput"', // 上传input
                '    data-ui="type:FileInput;accept:${accept};multiple:${multiple};name:${paramKey};"></div>',
                '</div>'
            ].join('');

            return lib.format(operationHtml, {
                text: this.text,
                accept: this.accept,
                multiple: this.multiple,
                paramKey: this.paramKey
            });
        }

        function refreshStutas() {
            var totalSize = u.reduce(this.queue.queueList, function (memo, file) {
                var size = 0;
                if (file.originalSize && u.isNumber(file.originalSize)) {
                    size = file.originalSize;
                }
                return memo + size;
            }, 0);

            this.queue.queueSize = File.formatSize(totalSize);

            $('#' + this.helper.getId('total-size')).html('总大小:' + this.queue.queueSize);
            $('#' + this.helper.getId('total-count')).html('总个数:' + this.queue.queueList.length);
            $('#' + this.helper.getId('status-list')).replaceWith(getStatusOperationsHtml.call(this));
        }

        function switchProgressByState(e) {
            var $this = $(this);
            var progressQueue = e.data.progressQueue;
            var state = $this.data('state');
            $('.state-selector').not($this).removeClass('state-selector-active');
            $this.toggleClass('state-selector-active');
            progressQueue.showSelectPrgress($this.hasClass('state-selector-active') ? state : 'all');
        }

        function addedTagClass($container, className, target) {
            $container.find('.' + className).removeClass('tag-selected');
            $(target).addClass('tag-selected');
        }

        esui.register(Uploader);
        return Uploader;
    }
示例#20
0
    function (require) {
        require('esui/Label');
        require('esui/Panel');
        require('esui/SearchBox');

        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var u = require('underscore');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        var esui = require('esui');

        /**
         * 控件类
         *
         * 富选择控件有如下结构特点:
         *
         * - 一个标题栏          (可选)
         *   - 标题栏总数显示    (可选)
         *   - 标题栏批量操作    (可选)
         * - 一个选择区          (必须)
         *   - 选择区头部搜索框  (可选)
         * - 一个底部状态栏      (可选)
         *
         * 富选择控件有三种交互模式:
         *
         * - add      点击节点,执行选择行为,并触发'add'事件
         * - load     点击节点,执行选择行为,并触发'load'事件
         * - del      点击节点,执行删除行为,并触发'delete'事件
         *
         * 富选择控件有两种选择模式:
         *
         * - 单选
         * - 多选
         *
         * @class ui.RichSelector
         * @extends esui.InputControl
         */

        var RichSelector = eoo.create(
            InputControl,
            {

                /**
                 * 控件类型,始终为`"RichSelector"`
                 *
                 * @type {string}
                 * @override
                 */
                type: 'RichSelector',

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 是否需要标题栏
                        hasHead: true,
                        // 标题栏是否需要统计数
                        needHeadCount: true,
                        // 这个名字出现在标题栏
                        title: '标题名',
                        // 是否需要批量操作
                        needBatchAction: false,
                        // 批量操作文字
                        batchActionLabel: '批量操作',
                        // 是否有搜索功能
                        hasSearchBox: true,
                        // 是否有腿部信息
                        hasFoot: true,
                        // 这个字段是对腿部信息的填充
                        itemName: '结果',
                        // 搜索为空的提示
                        emptyText: '没有相应的搜索结果',
                        // 是否刷新数据时保持搜索状态
                        holdState: false,
                        // 选择器类型 'load', 'add', 'delete'
                        // load: 点击某个节点,加载  出一堆别的数据,主要用于样式区分
                        // add: 点击一个节点,把这个节点加到另一个容器里
                        // delete: 点击一个节点,删
                        mode: 'add',
                        multi: true,
                        // 是否允许反选
                        allowUnselectNode: false
                    };

                    u.extend(properties, options);
                    this.$super([properties]);
                },

                /**
                 * 创建标题栏HTML
                 * @return {string}
                 */
                getHeadHTML: function () {
                    var helper = this.helper;
                    var actionLink = '';
                    var headCount = '';

                    if (this.needBatchAction) {
                        var linkClassName = helper.getPartClassName('batch-action-link');
                        var linkId = this.helper.getId('batch-action');
                        actionLink = ''
                            + '<a class="' + linkClassName
                            + '" id="' + linkId + '" >'
                            + this.batchActionLabel
                            + '</a>';
                    }

                    if (this.hasHead && this.needHeadCount) {
                        var countClass = helper.getPartClassName('head-count');
                        headCount = '<span class="'
                            + countClass
                            + '" data-ui="type:Label;childName:headTotalCount;title:;"></span>';
                    }

                    var head = [
                        '<div data-ui="type:Panel;childName:head;"',
                        ' class="${headClass}">',
                        '<span class="${headTitleClass}" data-ui="type:Label;childName:title;title:;">',
                        '${title}</span>${totalCount}',
                        '${actionLink}',
                        '</div>'
                    ].join('\n');

                    head = lib.format(
                        head,
                        {
                            headClass: helper.getPartClassName('head'),
                            headTitleClass: helper.getPartClassName('head-title'),
                            title: this.title,
                            actionLink: actionLink,
                            totalCount: headCount
                        }
                    );

                    return head;
                },

                /**
                 * 创建底部状态栏HTML
                 * @return {string}
                 */
                getFootHTML: function () {
                    var tpl = [
                        '<div data-ui="type:Panel;childName:foot;" class="${classes}">',
                        '   <span data-ui="type:Label;childName:totalCount"></span>',
                        '   ${footButton}',
                        '</div>'
                    ].join('\n');

                    var footButton = '';

                    if (this.footButtonText) {
                        footButton = '<button data-ui="type:Button;childName:button;variants:link;">'
                            + this.footButtonText
                            + '</button>';
                    }

                    return lib.format(
                        tpl,
                        {
                            classes: this.helper.getPartClassName('foot'),
                            footButton: footButton
                        }
                    );
                },

                /**
                 * 创建搜索框HTML
                 * @return {string}
                 */
                getSearchBoxHTML: function () {
                    return [
                        // 搜索区
                        '<div data-ui="type:Panel;childName:searchBoxArea"',
                        ' class="' + this.helper.getPartClassName('search-wrapper') + '">',
                        '   <div',
                        '   data-ui="buttonPosition:right;buttonVariants:bordered icon;',
                        '   type:SearchBox;childName:itemSearch;variants:clear-border',
                        '   hide-searched-button;searchMode:instant;">',
                        '   </div>',
                        '</div>'
                    ].join('');
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    var tpl = [
                        // 标题栏
                        '${head}',
                        // 内容
                        '<div data-ui="type:Panel;childName:body;" class="${bodyClass}">',
                             // 搜索框
                        '    ${searchInput}',
                             // 列表区
                        '    <div data-ui="type:Panel;childName:content" class="${contentClass}">',
                                 // 结果为空提示
                        '        <div data-ui="type:Label;childName:emptyText" ',
                        '           class="${emptyTextClass}">${emptyText}</div>',
                                 // 结果列表
                        '        <div data-ui="type:Panel;childName:queryList" class="${queryListClass}"></div>',
                        '    </div>',
                        '</div>',
                        // 底部概要信息
                        '${footInfo}'
                    ];

                    this.main.innerHTML = lib.format(
                        tpl.join('\n'),
                        {
                            head: this.hasHead ? this.getHeadHTML() : '',
                            bodyClass: this.helper.getPartClassName('body'),
                            searchInput: this.hasSearchBox ? this.getSearchBoxHTML() : '',
                            contentClass: this.helper.getPartClassName('content-wrapper'),
                            emptyTextClass: this.helper.getPartClassName('empty-text'),
                            emptyText: this.emptyText,
                            queryListClass: this.helper.getPartClassName('query-list'),
                            footInfo: this.hasFoot ? this.getFootHTML() : ''
                        }
                    );
                    this.initChildren();

                    // 初始化模式状态
                    this.addState(this.mode || 'delete');

                    // 绑事件

                    // 批量操作
                    if (this.needBatchAction) {
                        this.helper.addDOMEvent(
                            this.helper.getPart('batch-action'),
                            'click',
                            u.bind(this.batchAction, this)
                        );
                    }

                    // 搜索框
                    if (this.hasSearchBox) {
                        this.addState('has-search');
                        var searchBox = this.getSearchBox();
                        searchBox.on('search', u.bind(search, this));
                        searchBox.on('clear', u.bind(this.clearQuery, this));
                    }

                    // 为备选区绑定点击事件
                    var queryList = this.getQueryList().main;
                    this.helper.addDOMEvent(
                        queryList,
                        'click',
                        u.bind(this.eventDispatcher, this)
                    );
                },

                /**
                 * @override
                 */
                initEvents: function () {
                    this.$super(arguments);

                    var foot = this.getChild('foot');
                    var button = foot && foot.getChild('button');
                    if (button) {
                        button.on(
                            'click',
                            function (e) {
                                e.preventDefault();
                                this.fire('footclick');
                            },
                            this
                        );
                    }
                },

                /**
                 * 点击行为分发器
                 * @param {Event} e 事件对象
                 * @return {boolean}
                 * @ignore
                 */
                eventDispatcher: function (e) {
                    return false;
                },

                /**
                 * 按条件搜索
                 * @param {string | Object} args 搜索参数
                 */
                search: function (args) {
                    // filterData中的元素要满足一个标准结构: { keys: [], value: '' }
                    // 其中数组型的keys代表一种“并集”关系,也可以不提供
                    // filterData的各个元素代表“交集”关系。
                    var filterData = [];
                    var event = this.fire('search', filterData);
                    // 如果没有外部提供搜索条件
                    if (!event.isDefaultPrevented()) {
                        // 取自带搜索框的值
                        var searchBox = this.getSearchBox();
                        if (searchBox) {
                            var defaultFilter = {
                                value: lib.trim(searchBox.getValue())
                            };
                            filterData.push(defaultFilter);
                        }
                    }

                    if (filterData.length) {
                        // 查询,更新数据源
                        this.queryItem(filterData);
                        // 更新腿部总结果
                        this.refreshFoot();
                        // 更新头部总结果
                        this.refreshHead();
                        // 更新状态
                        this.addState('queried');
                    }
                    // 相当于执行清空操作
                    else {
                        this.clearQuery();
                    }
                },

                /**
                 * 清除搜索结果
                 *
                 * @param {ui.RichSelector} richSelector 类实例
                 * @return {boolean}
                 * @ignore
                 */
                clearQuery: function () {
                    // 重置搜索
                    resetSearchState(this);

                    // 清空数据
                    this.clearData();

                    // 更新备选区
                    this.refreshContent();

                    // 更新腿部总结果
                    this.refreshFoot();

                    // 更新头部总结果
                    this.refreshHead();

                    this.fire('clearquery');

                    return false;
                },

                /**
                 * 获取结果列表承载容器控件,列表在它里面
                 * @param {ui.RichSelector} richSelector 类实例
                 * @return {ui.Panel}
                 * @ignore
                 */
                getContent: function () {
                    var body = this.getChild('body');
                    if (body) {
                        return body.getChild('content');
                    }
                    return null;
                },

                getKeyword: function () {
                    var searchBox = this.getSearchBox();
                    var isQuery = this.isQuery();
                    if (searchBox && isQuery) {
                        return lib.trim(searchBox.getValue());
                    }
                    return null;
                },

                /**
                 * 获取结果列表控件
                 * @return {ui.TreeForSelector | ui.ListForSelector}
                 * @ignore
                 */
                getQueryList: function () {
                    var content = this.getContent();
                    if (content) {
                        return content.getChild('queryList');
                    }
                    return null;
                },

                /**
                 * 获取搜索控件
                 * @return {ui.Panel}
                 * @ignore
                 */
                getSearchBox: function () {
                    var searchBoxArea = this.getChild('body').getChild('searchBoxArea');
                    if (searchBoxArea) {
                        return searchBoxArea.getChild('itemSearch');
                    }
                },

                /**
                 * 获取腿部总个数容器
                 * @param {ui.RichSelector} richSelector 类实例
                 * @return {ui.Panel}
                 * @ignore
                 */
                getTotalCountPanel: function () {
                    var foot = this.getChild('foot');
                    if (!foot) {
                        return null;
                    }
                    return foot.getChild('totalCount');
                },

                /**
                 * 获取头部总个数容器
                 * @param {ui.RichSelector} richSelector 类实例
                 * @return {ui.Panel}
                 * @ignore
                 */
                getHeadTotalCountPanel: function () {
                    var head = this.getChild('head');
                    if (!head) {
                        return null;
                    }
                    return head.getChild('headTotalCount');
                },

                /**
                 * 判断是否处于query状态
                 * @return {boolean}
                 */
                isQuery: function () {
                    return this.hasState('queried');
                },

                /**
                 * 批量操作事件处理
                 * 可重写
                 *
                 * @return {boolean}
                 */
                batchAction: function () {
                    if (this.mode === 'delete') {
                        this.deleteAll();
                        this.refreshFoot();
                        this.refreshHead();
                    }
                    else if (this.mode === 'add') {
                        this.selectAll();
                    }
                    return false;
                },

                deleteAll: function () {
                    return false;
                },

                addAll: function () {
                    return false;
                },

                /**
                 * 数据适配
                 *
                 * @public
                 */
                adaptData: function () {},

                /**
                 * 手动刷新
                 *
                 * @param {ui.RichSelector} richSelector 类实例
                 * @ignore
                 */
                refresh: function () {
                    // 重建数据,包括索引数据的创建
                    var adaptedData = this.adaptData();

                    var needRefreshContent = true;
                    // 刷新搜索区
                    if (this.hasSearchBox && this.isQuery()) {
                        // 有一种场景(一般在删除型控件里)
                        // 在搜索状态下,删除了某个节点之后,希望还保留在搜索状态下
                        if (this.holdState) {
                            // 根据关键字获取结果
                            this.search(this.getKeyword());
                            // search方法里面已经执行过了
                            needRefreshContent = false;
                        }
                        // 清空搜索区
                        else {
                            resetSearchState(this);
                        }
                    }

                    // 刷新主体
                    if (needRefreshContent) {
                        // 重绘视图
                        this.refreshContent();
                        // 视图重绘后的一些额外数据处理
                        this.processDataAfterRefresh(adaptedData);
                        // 更新底部信息
                        this.refreshFoot();
                        // 更新头部总结果
                        this.refreshHead();
                    }
                },

                /**
                 * 视图刷新后的一些额外处理
                 *
                 * @param {Object} adaptedData 适配后的数据
                 */
                processDataAfterRefresh: function (adaptedData) {},

                /**
                 * 更新腿部信息
                 *
                 * @param {ui.RichSelector} richSelector 类实例
                 * @ignore
                 */
                refreshFoot: function () {
                    if (!this.hasFoot) {
                        return;
                    }
                    var count = this.getCurrentStateItemsCount();

                    // 更新腿部总结果
                    var totalCountPanel = this.getTotalCountPanel();
                    if (totalCountPanel) {
                        var itemName = u.escape(this.itemName);
                        totalCountPanel.setText('共 ' + count + ' 个' + itemName);
                    }
                },

                /**
                 * 更新头部信息
                 *
                 * @ignore
                 */
                refreshHead: function () {
                    if (!this.hasHead || !this.needHeadCount) {
                        return;
                    }
                    var count = this.getCurrentStateItemsCount();

                    // 更新头部总结果
                    var totalCountPanel = this.getHeadTotalCountPanel();
                    if (totalCountPanel) {
                        totalCountPanel.setText('(' + count + ')');
                    }
                },

                getCurrentStateItemsCount: function () {
                    return 0;
                },

                /**
                 * 重新渲染视图
                 * 仅当生命周期处于RENDER时,该方法才重新渲染
                 *
                 * @param {Array=} 变更过的属性的集合
                 * @override
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: 'title',
                        paint: function (me, title) {
                            var head = me.getChild('head');
                            var titleLabel = head && head.getChild('title');
                            titleLabel && titleLabel.setText(title);
                        }
                    },
                    {
                        name: 'disabled',
                        paint: function (me, disabled) {
                            var serachbox = me.getSearchBox();
                            if (disabled) {
                                serachbox && serachbox.disable();
                            }
                            else {
                                serachbox && serachbox.enable();
                            }
                        }
                    },
                    painters.style('width')
                ),

                /**
                 * 获取已经选择的数据项
                 * 就是一个代理,最后从结果列表控件里获取
                 * @return {Array}
                 * @public
                 */
                getSelectedItems: function () {
                    return [];
                },

                /**
                 * 获取已经选择的数据的完整数据结构
                 * @return {*}
                 * @public
                 */
                getSelectedItemsFullStructure: function () {
                    return {};
                },

                /**
                 * 批量更新状态
                 * @param {Array} items 需要更新的对象集合
                 * @param {boolean} toBeSelected 要选择还是取消选择
                 * @public
                 */

                /**
                 * 批量更新选择状态
                 * @param {Array} items 需要更新的对象集合
                 * @param {boolean} toBeSelected 要选择还是取消选择
                 * @public
                 */
                selectItems: function (items, toBeSelected) {},

                /**
                 * 设置元数据
                 *
                 * @param {Array | Object} value 置为选择的项.
                 */
                setRawValue: function (value) {
                    if (!u.isArray(value)) {
                        value = [value];
                    }

                    if (!this.multi && value.length > 1) {
                        value = value.slice(0);
                    }

                    this.selectItems(value, true);
                },

                /**
                 * 获取已经选择的数据项
                 *
                 * @return {Array | Object} 多选模式返回数组,单选模式返回单值
                 */
                getRawValue: function () {
                    var selectedItems = this.getSelectedItems();
                    if (!this.multi) {
                        return selectedItems[0];
                    }
                    return selectedItems;
                },

                /**
                 * 将value从原始格式转换成string
                 *
                 * @param {*} rawValue 原始值
                 * @return {string}
                 */
                stringifyValue: function (rawValue) {
                    var selectedIds = [];
                    if (!u.isArray(rawValue)) {
                        selectedIds = [rawValue];
                    }
                    else {
                        u.each(rawValue, function (item) {
                            selectedIds.push(item.id);
                        });
                    }
                    return selectedIds.join(',');
                }
            }
        );

        /**
         * 根据关键词搜索结果
         * @param {event} e SearchBox的点击事件对象
         * @ignore
         */
        function search(e) {
            this.search();
        }

        function resetSearchState(control) {
            // 删除搜索状态
            control.removeState('queried');

            // 清空搜索框
            var searchBox = control.getSearchBox();
            if (searchBox) {
                searchBox.set('text', '');
            }
        }

        esui.register(RichSelector);
        return RichSelector;
    }
示例#21
0
    function (require) {
        var eoo = require('eoo');
        var echarts = require('echarts');
        var painters = require('esui/painters');
        var util = require('../helper/util');
        var Control = require('esui/Control');
        var $ = require('jquery');
        var esui = require('esui');
        var u = require('underscore');

        /**
         * 图表基类控件
         *
         * @class ui.BaseChart
         * @extends esui.Control
         */
        var BaseChart = eoo.create(Control, {
            /**
             * @override
             */
            type: 'Chart',

            /**
             * @override
             */
            initOptions: function (options) {
                var properties = {
                    width: 'auto',
                    height: 300
                };
                u.extend(properties, BaseChart.defaultProperties, options);

                this.setProperties(properties);
            },

            /**
             * 初始化图表数据
             *
             * @protected
             * @method ui.BaseChart#initChartOptions
             * @return {Object}
             */
            initChartOptions: function () {
                return {};
            },

            /**
             * 获得提示层的title
             *
             * @protected
             * @method BaseChart#getTipTitleHtml
             * @param {Object} params 相关参数
             * @param {boolean} params.isContrast 是否是对比数据
             * @param {number} params.pointIndex 坐标点的横坐标索引
             * @param {Array} params.pointInfo 坐标点相关信息
             * @return {string} 构建完成的title
             */
            getTipTitleHtml: function (params) {
                return '';
            },

            /**
             * 提示层格式器
             *
             * @protected
             * @method ui.BaseChart#tipFormatter
             * @param {Array.<Object>} params 坐标点中的信息,具体数据参见ECHARTS文档
             * @param {string} axisIndex 坐标点在x轴上的索引位置信息
             * @return {string} 格式化后的弹出层html
             * =========================
             * 2013-12-04 星期三
             * <图例色块> 展现量:42,000
             * <图例色块> 点击量:12,000
             * 2012-12-04 星期二
             * <图例色块> 展现量:42,000
             * <图例色块> 点击量:12,000
             * =========================
             */
            tipFormatter: function (params, axisIndex) {
                // 要截取一下才是真实的index
                axisIndex = +axisIndex.slice(5);

                params = u.map(
                    params,
                    function (param, index) {
                        param.index = index;
                        return param;
                    }
                );

                // 如果是对比图表,那么一个节点中的几个指标值,隔位分成两组
                // 按照规定,对比数据,基线数据在前,对比数据在后
                // 对应的groups也一样
                var paramsGroup;
                if (this.isContrast) {
                    paramsGroup = [[], []];
                    u.each(
                        params,
                        function (param, index) {
                            paramsGroup[index % 2].push(param);
                        }
                    );
                }
                else {
                    paramsGroup = [params];
                }

                var html = [];
                u.each(
                    paramsGroup,
                    function (params, index) {
                        var title = this.getTipTitleHtml({
                            pointInfo: params,
                            pointIndex: axisIndex,
                            isContrast: index % 2 !== 0
                        });
                        html.push(title);
                        u.each(
                            params,
                            function (param, paramIndex) {
                                var itemHTML = buildTipItem.call(this, param);
                                html.push(itemHTML);
                            },
                            this
                        );
                    },
                    this
                );

                return html.join('<br>');
            },

            /**
             * 格式化y轴刻度数据
             *
             * @protected
             * @method ui.BaseChart#formatYAxisData
             * @param {Object} serie y轴数据
             * @param {Object} index 坐标索引
             * @return {Object} 返回格式化后的y轴构建所需数据对象
             */
            formatYAxisData: function (serie, index) {
                var data = serie.data;
                // 如果包含对比数据,要把对比数据也加进来
                if (this.isContrast) {
                    data = data.concat(this.ySeries[index + 1].data);
                }
                var splitNumber = this.splitNumber;
                // index 只可能为0, 1   0为左边坐标  1为右边坐标
                var labelAlign = index === 0 ? 'left' : 'right';

                // 自动计算比例尺的逻辑先删掉,PM有反应再加上。。。
                // 按从小到大排下序
                var sortedData = u.sortBy(
                    [].slice.call(data),
                    function (item) {
                        return +item;
                    }
                );

                // 把最大刻度转换成符合比例尺规范的值
                var maxData = sortedData[data.length - 1];
                var average = Math.ceil(maxData / splitNumber);
                // 取最接近average的刻度
                var scale = getNearestScale(this, average);
                var max = scale * splitNumber;

                var formatter = function (serie) {
                    return function (value) {
                        if (serie.format === 'percent') {
                            value = value + '%';
                        }
                        else if (serie.format === 'money') {
                            value = util.formatNumber(value, 2);
                        }
                        else if (serie.format === 'int') {
                            value = util.formatNumber(value);
                        }
                        return value;
                    };
                };

                return {
                    type: 'value',
                    name: '',
                    axisLabel: {
                        show: true,
                        interval: 'auto',
                        // margin和align默认处理的挺好的,如果需要的话,再加上,不需要后续删掉这两个
                        // margin: this.get('yAxisTextMargin') || 8, // 默认值是8
                        formatter: formatter(serie)
                    },
                    min: 0,
                    max: max,
                    splitNumber: splitNumber,
                    scale: true
                };
            },

            /**
             * 绘制图表
             *
             * @public
             * @method ui.BaseChart#draw
             */
            draw: function () {
                if (!this.chartOptions.series) {
                    return;
                }

                // 画图表
                var chart = this.chart;

                chart.showLoading(
                    {
                        text: this.loadingText
                    }
                );
                chart.setOption(this.chartOptions);

                chart.hideLoading();
            },

            /**
             * 格式化x轴数据
             *
             * @protected
             * @method ui.BaseChart#formatXSeries
             * @param {Array.<Object>} xSeries x轴数据系列
             * @return {Array.<Object>}
             */
            formatXSeries: function (xSeries) {
                return xSeries;
            },

            /**
             * 创建Y轴数据
             *
             * @protected
             * @method ui.BaseChart#buildYAxis
             * @param {Array} ySeries Y轴数据集合
             * @return {Array}
             */
            buildYAxis: function (ySeries) {
                var yAxis = [];
                var gap = 1;
                if (this.isContrast) {
                    gap = 2;
                }
                for (var i = 0; i < ySeries.length; i += gap) {
                    var serie = ySeries[i];
                    // 格式化y轴刻度数据
                    var formattedYAxisData = this.formatYAxisData(serie, i);
                    yAxis.push(formattedYAxisData);
                }

                return yAxis;
            },

            /**
             * 格式化y轴某条连续数据
             *
             * @protected
             * @method BaseChart#formatYSeriesData
             * @param {Object} serie y轴数据
             * @param {number} index y轴数据索引
             * @return {Object} 返回格式化后的y轴显示所需数据对象
             */
            formatYSeriesData: function (serie, index) {
                return {};
            },

            /**
             * @override
             */
            initStructure: function () {
                /**
                 * 这里将chart单独封装在一个层里是考虑,
                 * 未来可能会在控件中封装其它图表外的操作按钮。
                 */
                var $main = $(this.main);
                this.main.innerHTML = ''
                    + this.helper.getPartBeginTag('frame', 'div')
                    +     this.helper.getPartBeginTag('main', 'div')
                    +     this.helper.getPartEndTag('main', 'div')
                    + this.helper.getPartEndTag('frame', 'div');

                $main.find('.' + this.helper.getPartClassName('main')).css({
                    width: this.width,
                    height: this.height
                });

                var chart = echarts.init(this.helper.getPart('main'), this.theme);
                this.chart = chart;

                // 绑定resize事件
                this.helper.addDOMEvent(
                    window,
                    'resize',
                    function () {
                        chart.resize();
                    }
                );
            },

            /**
             * @override
             */
            repaint: painters.createRepaint(
                Control.prototype.repaint,
                {
                    name: ['xSeries', 'ySeries'],
                    paint: function (chart, xSeries, ySeries) {
                        if (!ySeries) {
                            return;
                        }
                        // 如果chart的option还没初始化,先初始化
                        if (!chart.chartOptions) {
                            chart.chartOptions = chart.initChartOptions();
                        }

                        // 更新X轴数据
                        chart.chartOptions.xAxis[0].data = chart.formatXSeries(xSeries);
                        // 为了美观,当x轴数据只有1条时,数据不顶格,当X轴数据大于1条时,顶格
                        chart.chartOptions.xAxis[0].boundaryGap = (xSeries.length === 1);

                        // 跟新Y轴数据
                        // 1. 构建数据
                        var formattedYSeries = [];
                        chart.legend = [];
                        u.each(
                            ySeries,
                            function (serie, index) {
                                // 格式化y轴坐标数据
                                var formattedYSeriesData = chart.formatYSeriesData(serie, index);
                                formattedYSeries.push(formattedYSeriesData);

                                // 更新图例数据
                                chart.legend[index] = {
                                    color: serie.color || this.chart._themeConfig.color[index],
                                    format: serie.format
                                };
                            },
                            chart
                        );

                        // 2. 构建坐标
                        var yAxis = chart.buildYAxis(ySeries);

                        // 开画
                        chart.chartOptions.yAxis = yAxis;
                        chart.chartOptions.series = formattedYSeries;

                        chart.draw();
                    }
                }
            ),

            /**
             * @override
             */
            dispose: function () {
                if (this.helper.isInStage('DISPOSED')) {
                    return;
                }
                if (this.chart) {
                    this.chart.dispose();
                    this.chart = null;
                }
                this.$super(arguments);
            }
        });

        BaseChart.defaultProperties = {
            scale: '1, 1.5, 2, 5, 10',
            splitNumber: 4,
            loadingText: '正在努力渲染...',
            // 是否是对比图表
            isContrast: false,
            /**
             * @property {string|Object} [theme]
             *
             * 设置Chart的Theme,可以自行定制。
             * http://echarts.baidu.com/doc/example/themeDesigner.html
             *
             */
            theme: 'macarons'
        };

        /**
         * 创建单行提示信息
         *
         * @param {Object} param 坐标点中的信息
         * @return {string}
         */
        function buildTipItem(param) {
            var legend = this.legend;
            if (legend.length === 0) {
                return '';
            }
            // 图例和节点的对应,通过param的第四个参数
            var format = legend[param.index].format;
            var legendColor = legend[param.index].color;
            var value = param.data;
            if (format === 'money') {
                value = (value === '-') ? '- -' : util.formatNumber(value, 2, '', '&yen;');
            }
            else if (format === 'int') {
                value = util.formatNumber(value);
            }
            else if (format === 'percent') {
                value = value + '%';
            }

            // 有时需要自定义tip信息 所以需要允许自定义
            var returnedEvent = this.fire('beforebuildtipitem', {param: param, value: value});

            var styles = 'margin-right:5px;display:inline-block;width:10px;height:10px;';
            var defaultTip = '<b style="' + styles + 'background:' + legendColor + ';"></b>'
                            + param.seriesName + ':' + value;

            return returnedEvent.itemValue || defaultTip;
        }

        /**
         * 获取单位刻度值
         *
         * 控件设置有默认五个基础比例尺 '1, 1.5, 2, 5, 10'
         * 根据目标数据的位数对基础比例尺做加权
         * 比如 215 是三位数,权值是100,加权后的比例尺是
         * '100, 150, 200, 500, 1000'
         * 可以涵盖目标数字。
         * 然后通过比较,获取目标数字所落比例尺区间的最大值,即为最后的单位刻度
         *
         * @param {BaseChart} chart 类实例
         * @param {Object} average 实际平均刻度
         *
         * @return {number} scale 获得的单位刻度
         */
        function getNearestScale(chart, average) {
            var averageStr = average.toString();
            // 位数
            var bits = averageStr.length;
            // 通过平均值的位数计算加权值,比如215的加权就是100
            var power = Math.pow(10, bits - 1);

            // 基础比例尺格式化为数组,默认[1, 1.5, 2, 5, 10]
            var baseScale = chart.scale.split(',');
            var scale;
            for (var i = 0; i < baseScale.length; i++) {
                // 实际比例尺选择范围是基础比例尺乘以加权值
                baseScale[i] = parseFloat(baseScale[i]) * power;
                // 向上取值
                if (average <= baseScale[i]) {
                    scale = baseScale[i];
                    break;
                }
            }

            return scale;
        }

        esui.register(BaseChart);
        return BaseChart;
    }
示例#22
0
    function (require) {
        var lib = require('esui/lib');
        var InputControl = require('esui/InputControl');
        var eoo = require('eoo');
        var esui = require('esui');
        var painters = require('esui/painters');
        var u = require('underscore');

        require('./FullColorPicker');
        require('esui/Overlay');
        require('esui/TextBox');
        require('esui/Button');

        var ColorPicker = eoo.create(
            InputControl,
            {
                /**
                 * 控件类型
                 *
                 * @override
                 */
                type: 'ColorPicker',

                /**
                 * 初始化参数
                 *
                 * @override
                 * @protected
                 */
                initOptions: function (options) {
                    var properties = {
                        // 展示模式 'attached' | 'dialog'
                        displayMode: 'attached',
                        // 默认拾色器模式,支持 'simple' | 'advanced' | 'full'
                        featureMode: 'simple',
                        switchable: false,
                        hex: '000000',
                        alpha: 100,
                        hasAlpha: true
                    };
                    u.extend(properties, ColorPicker.defaultProperties, options);
                    this.setProperties(properties);
                },

                /**
                 * 初始化DOM结构
                 *
                 * @override
                 * @protected
                 */
                initStructure: function () {
                    var controlHelper = this.helper;
                    var mainTpl = ''
                        + '<div class="${colorBlockFrameClass}" id="${colorBlockFrameId}">'
                        +     '<div class="${colorBlockClass}" id="${colorBlockId}"></div>'
                        + '</div>'
                        + '<div class="${colorInputClass}" id="${colorInputId}" data-ui-type="TextBox"'
                        +     'data-ui-child-name="colorInput" data-ui-hint="#" data-ui-hint-type="prefix"'
                        +     'data-ui-width="auto"></div>';
                    if (this.hasAlpha) {
                        mainTpl += ''
                            + '<div class="${alphaInputClass}" id="${alphaInputId}" data-ui-type="TextBox"'
                            +     'data-ui-child-name="alphaInput" data-ui-hint="%" data-ui-hint-type="suffix"'
                            +     'data-ui-width="auto"></div>';
                    }
                    this.main.innerHTML = lib.format(
                        mainTpl,
                        {
                            colorBlockFrameClass: controlHelper.getPartClassName('color-block-frame'),
                            colorBlockFrameId: controlHelper.getId('color-block-frame'),
                            colorBlockClass: controlHelper.getPartClassName('color-block'),
                            colorBlockId: controlHelper.getId('color-block'),
                            colorInputClass: controlHelper.getPartClassName('color-input'),
                            colorInputId: controlHelper.getId('color-input'),
                            alphaInputClass: controlHelper.getPartClassName('alpha-input'),
                            alphaInputId: controlHelper.getId('alpha-input')
                        }
                    );

                    this.initChildren();
                },

                /**
                 * @override
                 */
                initEvents: function () {
                    this.$super(arguments);

                    var colorBlock = this.helper.getPart('color-block');
                    // 点色块走的是toggle,所以阻止冒泡到`document`
                    this.helper.addDOMEvent(
                        colorBlock,
                        'click',
                        function (e) {
                            e.stopPropagation();
                        }
                    );
                    this.helper.addDOMEvent(colorBlock, 'click', toggleLayer);

                    var colorInput = this.getChild('colorInput');
                    colorInput.on('input', u.bind(onColorInput, this));

                    var alphaInput = this.getChild('alphaInput');
                    if (alphaInput) {
                        alphaInput.on('input', u.bind(onAlphaInput, this));
                    }
                },

                /**
                 * 渲染自身
                 *
                 * @override
                 * @protected
                 * @fires ColorPicker#change
                 */
                repaint: painters.createRepaint(
                    InputControl.prototype.repaint,
                    {
                        name: ['hex', 'alpha'],
                        paint: function (colorPicker, hex, alpha) {
                            updateColorDisplay.call(colorPicker);
                            syncValue.call(colorPicker);
                            colorPicker.fire('change');
                        }
                    },
                    {
                        name: ['disabled'],
                        paint: function (colorPicker, disabled) {
                            if (disabled) {
                                colorPicker.helper.disableChildren();
                            }
                            else {
                                colorPicker.helper.enableChildren();
                            }
                        }
                    },
                    {
                        name: ['readOnly'],
                        paint: function (colorPicker, readOnly) {
                            u.each(
                                colorPicker.children,
                                function (child) {
                                    if (readOnly) {
                                        child.addState('readOnly');
                                    }
                                    else {
                                        child.removeState('readOnly');
                                    }
                                }
                            );
                        }
                    }
                ),

                /**
                 * 批量更新属性并重绘
                 *
                 * @override
                 * @fires ColorPicker#change
                 */
                setProperties: function (properties) {
                    var changes = this.$super(arguments);

                    if (changes.hasOwnProperty('rawValue')) {
                        this.fire('change');
                    }

                    return changes;
                },

                /**
                 * @override
                 * @return {Object}
                 */
                getRawValue: function () {
                    var result = {};
                    result.hex = this.hex;
                    if (this.hasAlpha) {
                        result.alpha = this.alpha;
                    }
                    return result;
                }
            }
        );

        ColorPicker.defaultProperties = {
            chooseColorText: '颜色选择',
            closeText: '关闭',
            okText: '确定',
            cancelText: '取消'
        };

        /**
         * 颜色输入框输入事件处理
         *
         * @param {mini-event.Event} e 事件参数
         */
        function onColorInput(e) {
            var colorInput = e.target;
            var hex = colorInput.getValue();
            // 只取输入的前6位
            if (hex.length > 6) {
                hex = hex.slice(0, 6);
            }
            hex = new Array(6 - hex.length + 1).join('0') + hex;
            // 更新主元素显示
            var colorBlock = this.helper.getPart('color-block');
            colorBlock.style.background = '#' + hex;

            this.hex = hex;

            // 修改后需要fire一个事件出去
            this.fire('change');
        }

        /**
         * 透明度输入框输入事件处理
         *
         * @param {mini-event.Event} e 事件参数
         */
        function onAlphaInput(e) {
            var alpha = e.target.getValue();
            this.alpha = alpha;
            // 修改后需要fire一个事件出去
            this.fire('change');
        }

        function updateColorDisplay() {
            var colorBlock = this.helper.getPart('color-block');
            colorBlock.style.backgroundColor = '#' + this.hex;

            var colorInput = this.getChild('colorInput');
            colorInput.setValue(this.hex);

            if (this.hasAlpha) {
                var alphaInput = this.getChild('alphaInput');
                alphaInput.setValue(this.alpha);
            }
        }

        function syncValue() {
            var overlay = this.getChild('layer');
            if (overlay) {
                var properties = {};
                properties.hex = this.getChild('colorInput').getValue();

                if (this.hasAlpha) {
                    properties.alpha = this.getChild('alphaInput').getValue();
                }
                var colorPicker = overlay.getChild('colorPicker');
                if (colorPicker) {
                    colorPicker.setProperties(properties);
                }
            }
        }

        function createLayer() {
            var overlayMain = this.helper.createPart('layer', 'div');
            lib.addClass(overlayMain, this.helper.getPartClassName('layer'));

            var pickerContent = ''
                + this.helper.getPartBeginTag('head', 'div')
                +     this.helper.getPartBeginTag('title', 'div') + this.chooseColorText
                +     this.helper.getPartEndTag('title', 'div')
                +     this.helper.getPartBeginTag('close-btn', 'div') + this.closeText
                +     this.helper.getPartEndTag('close-btn', 'div')
                + this.helper.getPartEndTag('head', 'div')
                + '<div data-ui-type="FullColorPicker" data-ui-child-name="colorPicker"'
                +     'data-ui-default-mode="' + this.featureMode + '"'
                +     'data-ui-switchable="' + this.switchable + '"'
                +     'data-ui-has-alpha="' + this.hasAlpha + '">'
                + '</div>'
                + this.helper.getPartBeginTag('foot-frame', 'div')
                +     this.helper.getPartBeginTag('foot', 'div')
                +         '<div class="' + this.helper.getPartClassName('ok-btn') + '"'
                +             'data-ui="type:Button;childName:btnOk;variants:primary wide">' + this.okText + '</div>'
                +         '<div class="' + this.helper.getPartClassName('cancel-btn') + '"'
                +             'data-ui="type:Button;childName:btnCancel;variants:link wide">' + this.cancelText + '</div>'
                +     this.helper.getPartEndTag('foot', 'div')
                + this.helper.getPartEndTag('foot-frame', 'div');

            var colorPickerOverLay = esui.create(
                'Overlay',
                {
                    main: overlayMain,
                    childName: 'layer',
                    content: pickerContent
                }
            );

            this.addChild(colorPickerOverLay);
            colorPickerOverLay.appendTo(this.main);
            colorPickerOverLay.addState(this.displayMode);

            var colorPicker = colorPickerOverLay.getChild('colorPicker');

            // displayMode决定弹层展示模式
            if (this.displayMode === 'attached') {
                // 变了即时更新
                var control = this;
                colorPicker.on(
                    'change',
                    function (e) {
                        var hex = this.getDisplayHex();
                        var alpha = this.getDisplayAlpha();
                        if (hex !== control.hex || alpha !== control.alpha) {
                            control.setProperties({hex: hex, alpha: alpha});
                        }
                    }
                );
            }
            else {
                // 初始化关闭按钮
                var closeBtn = this.helper.getPart('close-btn');
                if (closeBtn) {
                    this.helper.addDOMEvent(closeBtn, 'click', hideOverlay);
                }

                // 初始化提交和取消按钮
                var btnOk = colorPickerOverLay.getChild('btnOk');
                var btnCancel = colorPickerOverLay.getChild('btnCancel');

                btnOk.on('click', u.bind(submit, this));
                btnCancel.on('click', u.bind(hideOverlay, this));
            }

            return this.getChild('layer');
        }

        /**
         * 确认颜色选择
         *
         * @fires ColorPicker#submit
         */
        function submit() {
            var pickerOverlay = this.getChild('layer');
            var fullColorPicker = pickerOverlay.getChild('colorPicker');
            this.setProperties({hex: fullColorPicker.getDisplayHex(), alpha: fullColorPicker.getDisplayAlpha()});
            pickerOverlay.hide();
            this.fire('submit');
        }

        function hideOverlay() {
            var pickerOverlay = this.getChild('layer');
            pickerOverlay.hide();
        }

        /**
         * 显示下拉弹层
         */
        function showLayer() {
            var colorPickerOverLay = this.getChild('layer');
            // displayMode决定弹层展示模式
            var properties = {
                hasMask: true,
                fixed: false,
                autoClose: false
            };
            if (this.displayMode === 'attached') {
                properties = {
                    attachedDOM: this.helper.getId('color-block-frame'),
                    attachedLayout: 'bottom,left'
                };
            }
            colorPickerOverLay.setProperties(properties);
            colorPickerOverLay.show();
            colorPickerOverLay.resize();
        }

        /**
         * 根据当前状态显示或隐藏浮层
         */
        function toggleLayer() {
            var layer = this.getChild('layer');
            if (!layer) {
                layer = createLayer.call(this);
            }

            if (layer.isHidden()) {
                syncValue.call(this);
                showLayer.call(this);
            }
            else {
                layer.hide();
            }
        }

        esui.register(ColorPicker);
        return ColorPicker;
    }
示例#23
0
    function (require) {
        var lib = require('./lib');
        var ui = require('esui');
        var Control = require('./Control');

        require('./TextBox');
        require('./Button');

        /**
         * 搜索框控件,由一个文本框和一个搜索按钮组成
         *
         * @extends Control
         * @param {Object} [options] 初始化参数
         * @constructor
         */
        function SearchBox(options) {
            Control.apply(this, arguments);
        }

        /**
         * @override
         */
        SearchBox.prototype.type = 'SearchBox';

        /**
         * 初始化参数
         *
         * @param {Object} [options] 构造函数传入的参数
         * @protected
         * @override
         */
        SearchBox.prototype.initOptions = function (options) {
            var properties = {
                // 搜索模式:`instant`、`normal`
                searchMode: 'normal',
                // 搜索框内容为空,默认为search图标,使用时不转义
                buttonContent: '',
                // 搜索button默认的样式为primary
                buttonVariants: 'primary icon',
                // 搜索button默认的位置为left
                buttonPosition: 'left',
                // 默认值为''
                text: '',
                // 控件内部使用的状态,外部MUST NOT设置该属性
                searched: false,
                width: ''
            };
            lib.extend(properties, options);

            if (properties.disabled === 'false') {
                properties.disabled = false;
            }

            if (lib.isInput(this.main)) {
                if (!properties.placeholder) {
                    properties.placeholder =
                        lib.getAttribute(this.main, 'placeholder');
                }

                if (!properties.text) {
                    properties.text = this.main.value;
                }

                if (!properties.maxLength && (lib.hasAttribute(this.main, 'maxlength') || this.main.maxLength > 0)) {
                    properties.maxLength = this.main.maxLength;
                }
            }
            // TODO: custom elments 的兼容
            else {
                if (!properties.text) {
                    properties.text = lib.getText(this.main);
                }
            }

            if (!properties.title) {
                properties.title = this.main.title;
            }

            if (properties.text) {
                properties.searched = true;
            }

            Control.prototype.initOptions.call(this, properties);
        };

        /**
         * 初始化DOM结构
         *
         * @protected
         * @override
         */
        SearchBox.prototype.initStructure = function () {
            var tpl = ''
                + '<esui-text-box data-ui-mode="text" data-ui-child-name="text"'
                +     'data-ui-placeholder="${placeholder}" data-ui-icon="${clearClasses}"'
                +     'data-ui-variants="icon-right" data-ui-width="auto">'
                + '</esui-text-box>';
            var addonTPL = getAddonHTML.apply(this);

            // instant模式下搜索图标在textbox前
            if (this.buttonPosition === 'left') {
                tpl = addonTPL + tpl;
            }
            // normal模式下搜索图标在textbox之后
            else if (this.buttonPosition === 'right') {
                tpl += addonTPL;
            }

            var html = lib.format(
                tpl,
                {
                    placeholder: this.placeholder,
                    clearClasses: this.helper.getIconClass('times-circle')
                }
            );

            if (lib.isInput(this.main)) {
                this.helper.replaceMain();
            }
            if (this.buttonPosition) {
                lib.addClass(this.main, this.helper.getPrefixClass('textbox-wrapper'));
            }

            this.main.innerHTML = html;
            this.helper.initChildren(this.main);
        };

        /**
         * 获取搜索按钮或搜索图标HTML
         *
         * @return {string}
         */
        function getAddonHTML() {
            // 即时搜索不需要搜索按钮
            var addonContent = '<span class="${searchIconClasses}"></span>';
            var notInstant = this.searchMode !== 'instant';
            // normal模式下有搜索按钮
            addonContent = ''
            + '<button data-ui="type:Button;childName:search;variants:${buttonVariants}"'
            +     'class="${searchClasses}">'
            +     (this.buttonContent ? this.buttonContent : addonContent)
            + '</button>';
            var tpl = ''
                + '<div class="${addonClasses}">'
                +     addonContent
                + '</div>';
            var helper = this.helper;

            return lib.format(
                tpl,
                {
                    searchIconClasses: helper.getIconClass('search'),
                    buttonVariants: this.buttonVariants,
                    searchClasses: helper.getPartClassName('search'),
                    addonClasses: helper.getPrefixClass('textbox-addon')
                }
            );
        }

        /**
         * 初始化事件交互
         *
         * @protected
         * @override
         */
        SearchBox.prototype.initEvents = function () {
            var textbox = this.getChild('text');
            var delegate = require('mini-event').delegate;

            // 处理输入事件
            textbox.on('input', onInput, this);
            // 即时模式下输入触发搜索
            if (this.searchMode === 'instant') {
                delegate(textbox, 'input', this, 'search');
            }

            // 代理回车键事件
            delegate(textbox, 'enter', this, 'search');
            // 回车时要取消掉默认行为,否则会把所在的表单给提交了
            textbox.on(
                'keypress',
                function (e) {
                    if (e.keyCode === 13) {
                        e.preventDefault();
                    }
                }
            );

            // 清除搜索按钮
            textbox.on('iconclick', clear, this);
            textbox.on('focus', lib.bind(this.addState, this, 'focus'));
            textbox.on('blur', lib.bind(this.removeState, this, 'focus'));

            var searchButton = this.getChild('search');
            if (searchButton) {
                delegate(searchButton, 'click', this, 'search');
            }
        };

        /**
         * 处理输入框的input事件,根据输入框是否有内容增加/移除`searched`状态
         */
        function onInput() {
            var textbox = this.getChild('text');
            var method = textbox.getValue() ? 'addState' : 'removeState';
            this[method]('searched');
        }

        /**
         * 清除搜索关键词,`instant`模式下触发搜索
         */
        function clear() {
            var textbox = this.getChild('text');
            textbox.setValue('');
            this.removeState('searched');
            if (this.searchMode === 'instant') {
                this.fire('search');
            }
            else {
                this.fire('clear');
            }
        }

        /**
         * 获取输入值
         *
         * @return {string}
         * @override
         */
        SearchBox.prototype.getValue = function () {
            var text = this.getChild('text');
            return text.getValue();
        };

        var paint = require('./painters');

        /**
         * 渲染自身
         *
         * @protected
         * @override
         */
        SearchBox.prototype.repaint = paint.createRepaint(
            Control.prototype.repaint,
            paint.attribute('title'),
            {
                name: [
                    'maxLength', 'placeholder', 'text',
                    'height', 'disabled', 'readOnly'
                ],
                /* eslint-disable max-params */
                paint: function (box, maxLength, placeholder, text, height, disabled, readOnly) {
                    var properties = {
                        /**
                         * @property {number} maxLength
                         *
                         * 最大长度,参考{@link TextBox#maxLength}
                         */
                        maxLength: maxLength,
                        /**
                         * @property {string} placeholder
                         *
                         * 无内容时的提示文字,参考{@link TextBox#placeholder}
                         */
                        placeholder: placeholder,
                        /**
                         * @property {string} text
                         *
                         * 文字内容
                         */
                        value: text,
                        height: height,
                        disabled: disabled,
                        readOnly: readOnly
                    };
                    box.getChild('text').setProperties(properties);
                }
                /* eslint-enable max-params */
            },
            {
                name: 'disabled',
                paint: function (box, disabled) {
                    var searchButton = box.getChild('search');
                    searchButton && searchButton.set('disabled', disabled);
                }
            },
            {
                /**
                 * @property {number} width
                 *
                 * 搜索框的宽度
                 */
                name: 'width',
                paint: function (box, width) {
                    box.main.style.width = width + 'px';
                }
            },
            {
                /**
                 * @property {boolean} searched
                 *
                 * 设定SearchBox是否已有搜素关键词
                 */
                name: 'searched',
                paint: function (box, searched) {
                    var method = searched ? 'addState' : 'removeState';
                    box[method]('searched');
                }
            },
            {
                /**
                 * @property {boolean} searched
                 *
                 * 设定SearchBox的工作模式:`instant` | `normal`
                 */
                name: 'searchMode',
                paint: function (box, searchMode) {
                    box.addState(searchMode);
                }
            }
        );

        lib.inherits(SearchBox, Control);
        ui.register(SearchBox);
        return SearchBox;
    }
示例#24
0
    function (require) {
        var $ = require('jquery');
        var Control = require('esui/Control');
        var esui = require('esui');
        var eoo = require('eoo');
        var painters = require('esui/painters');
        var u = require('underscore');

        /**
         * Drawer类定义。
         *
         * @class
         * @extends Control
         *
         * @constructor
         *
         * 创建新的Drawer实例
         *
         * @param {Object} [options] 组件参数
         */
        var Drawer = eoo.create(
            Control,
            {
                /**
                 * Drawer类型用于注册到ESUI库
                 *
                 * @override
                 */
                type: 'Drawer',

                /**
                 * 初始化传入参数。
                 *
                 * @param {Object} options 初始化参数
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        /**
                         * @property {Number|string}
                         *
                         * Drawer的宽度。可以为百分比或者像素。
                         */
                        width: null
                    };
                    u.extend(properties, options);

                    this.setProperties(properties);
                },

                /**
                 * 初始化组件结构
                 *
                 * @override
                 */
                initStructure: function () {
                    var me = this;
                    var main = me.main;

                    me.$super(arguments);

                    if (main.parentNode
                        && main.parentNode.nodeName.toLowerCase() !== 'body') {
                        document.body.appendChild(main);
                    }
                    var roles = parseMainElement(main);
                    var tempEle;
                    for (var key in roles) {
                        if (roles.hasOwnProperty(key)) {
                            tempEle = roles[key];
                            tempEle.id = me.helper.getId(key);
                            me.helper.addPartClasses(key, tempEle);
                        }
                    }

                    this.helper.initChildren();
                },

                /**
                 * 组件的重绘属性处理函数。
                 *
                 * @override
                 */
                repaint: painters.createRepaint(
                    Control.prototype.repaint,
                    {
                        name: ['width'],
                        paint: function (drawer, width) {
                            var helper = drawer.helper;
                            var w = 'width';
                            if (width) {
                                $(helper.getPart('header')).css(w, width);
                                $(helper.getPart('content')).css(w, width);
                                $(helper.getPart('footer')).css(w, width);
                            }
                        }
                    },
                    {
                        name: ['title'],
                        paint: function (drawer, title) {
                            var titleId = drawer.helper.getId('title');

                            $('#' + titleId).html(title);
                        }
                    }
                ),

                /**
                 * 事件绑定
                 *
                 * @override
                 */
                initEvents: function () {
                    this.$super(arguments);

                    var helper = this.helper;
                    // 监听main中具有data-role="close"的节点 点击就关闭自己
                    helper.addDOMEvent(this.main, 'click', '[data-role="close"]', close);
                },

                /**
                 * 显示Drawer
                 */
                show: function () {
                    $(this.main).addClass(this.helper.getPrimaryClassName('visible'));
                    $(document.body).addClass(this.helper.getPrimaryClassName('hide-overflow'));
                    this.fire('show');
                },

                /**
                 * 隐藏Drawer
                 */
                hide: function () {
                    // 如果直接显示页面滚动,可能会出现content和页面双滚动
                    // 因此延迟一会儿
                    var hiddenFlowClass = this.helper.getPrimaryClassName('hide-overflow');
                    setTimeout(function () {
                        $(document.body).removeClass(hiddenFlowClass);
                    }, 200);
                    $(this.main).removeClass(this.helper.getPrimaryClassName('visible'));
                    this.fire('hide');
                },

                /**
                 * 关闭drawer, 和hide区别是close会fire`hide`事件
                 */
                close: function () {
                    close.call(this);
                },

                /**
                 * 销毁组件
                 *
                 * @override
                 */
                dispose: function () {
                    // drawer里面可能存在link触发global redirect,这里手动隐藏一次
                    $(document.body).removeClass(this.helper.getPrimaryClassName('hide-overflow'));
                    $(this.main).remove();
                    this.$super(arguments);
                }
            }
        );

        /**
         * 关闭按钮的处理函数
         *
         * @param {Object} e 事件参数
         * @return {boolean} 是否关闭成功
         * @private
         */
        function close(e) {
            e && e.preventDefault();

            var beforecloseEvent = this.fire('beforeclose');
            // 阻止事件,则不继续运行
            if (beforecloseEvent.isDefaultPrevented()) {
                return false;
            }

            this.hide();
            this.fire('close');
            return true;
        }

        /**
         * 递归一个DOM元素,取到组件相关的DOM元素
         *
         * @param {DOMElement} element 要进行parse的元素
         * @return {DOMElement} 需要拷贝到剪切板的内容
         * @private
         */
        function parseMainElement(element) {
            var roles = {};
            $(element).find('[data-role]').each(function () {
                roles[$(this).data('role')] = this;
            });

            return roles;
        }

        esui.register(Drawer);
        return Drawer;
    }
示例#25
0
define(function (require) {
    var u = require('underscore');
    var esui = require('esui');
    var lib = require('esui/lib');
    var paint = require('esui/painters');
    var Control = require('esui/Control');
    var eoo = require('eoo');
    var $ = require('jquery');

    var MAIN_TPL = [
        '<div class="${typeSelector}-main" id="${contentId}">',
            '<div class="${typeSelector}-content">',
                '<span class="${typeSelector}-pointer ${typeSelector}-pointer-active-l ${iconLeftArrow}"',
                ' id="${leftId}"></span>',
                '<div class="ui-carouse-list-wrap">',
                    '<ul class="${typeSelector}-list" id="${listId}"></ul>',
                '</div>',
                '<span class="${typeSelector}-pointer ${typeSelector}-pointer-active-r ${iconRightArrow}"',
                ' id="${rightId}"></span>',
            '</div>',
            '<div class="${typeSelector}-toolbar">',
                '<ul id="${toolbarId}"></ul>',
            '</div>',
        '</div>'
    ].join('');

    var ITEM_TPL = [
        '<li class="${typeSelector}-item ${itemSelector}" index="${index}" ',
            'style="width:${width}px;height:${height}px;margin-right:${spacing}px;">',
            '<img class="${typeSelector}-item-img" src="${imgSrc}"/>',
            '<span class="${typeSelector}-check ${iconCheck}"></span>',
        '</li>'
    ].join('');

    var PAGE_TPL = '<li index="${index}" class="${typeSelector}-page"></li>';

    var Carousel = eoo.create(
        Control,
        {
            /**
             * 控件类型
             *
             * @type {string}
             * @readonly
             * @override
             */
            type: 'Carousel',

            /**
             * 初始化参数
             *
             * @param {Object} [options] 构造函数传入的参数
             * @param {number} option.pageSize 每页显示的个数
             * @param {number} option.spacing 每个图片的间隔
             * @param {number} option.itemWidth 每项的宽度
             * @param {number} option.itemHeight 每项的高度
             * @param {Array} option.datasource 所有项的数据数组
             * @param {number} option.value 选中的项的id值
             * @param {boolean} option.disabled 是否禁用
             * @param {boolean} option.emphasizeSelectedItem 是否高亮被选择的
             * @protected
             * @override
             */
            initOptions: function (options) {
                var properties = {
                    pageSize: 8,
                    spacing: 15,
                    itemWidth: 80,
                    itemHeight: 50,
                    datasource: [],
                    value: null,
                    disabled: false,
                    emphasizeSelectedItem: true
                };
                u.extend(properties, options);
                this.setProperties(properties);
            },

            /**
             * 初始化DOM结构
             *
             * @protected
             */
            initStructure: function () {
                this.main.innerHTML = getMainHtml.call(this);
            },

            /**
             * 初始化事件交互
             *
             * @protected
             * @override
             */
            initEvents: function () {
                // 左右的把手事件绑定
                this.helper.addDOMEvent('left-handler', 'click', u.bind(pointerClick, this, -1));
                this.helper.addDOMEvent('right-handler', 'click', u.bind(pointerClick, this, 1));
                // 列表项切换
                this.helper.addDOMEvent('list', 'click', 'li', itemChangeHandler);
                // 最下面翻页
                this.helper.addDOMEvent('toolbar', 'click', 'li', toolbarHandler);
            },

            /**
             * 重新渲染视图
             * 仅当生命周期处于RENDER时,该方法才重新渲染
             *
             * @param {Array=} 变更过的属性的集合
             * @override
             */
            repaint: paint.createRepaint(
                Control.prototype.repaint,
                {
                    name: ['datasource', 'itemWidth', 'itemHeight'],
                    paint: function (carousel, datasource, itemWidth, itemHeight) {
                        var list = carousel.helper.getPart('list');
                        var toolbar = carousel.helper.getPart('toolbar');
                        var pageSize = carousel.pageSize;
                        var spacing = carousel.spacing;
                        list.innerHTML = getItemHtml.call(
                            carousel,
                            datasource,
                            itemWidth,
                            itemHeight,
                            spacing,
                            pageSize
                        );
                        toolbar.innerHTML = getToolbarHtml.call(carousel, datasource);

                        var wrapWidth = itemWidth * carousel.pageSize + (pageSize - 1) * spacing;
                        var wrapHeight = itemHeight;
                        carousel.wrapWidth = wrapWidth;
                        var wrap = list.parentNode;
                        wrap.style.width = wrapWidth + 'px';
                        wrap.style.height = wrapHeight + 'px';
                    }
                },
                {
                    name: 'value',
                    paint: function (carousel, value) {
                        carousel.setValue(value);
                    }
                }
            ),

            /**
             * 设置选中项
             *
             * @param {number} value 选中项的值
             * @public
             */
            setValue: function (value) {
                if (u.isNull(value) || u.isUndefined(value)) {
                    this.setPage();
                    return;
                }
                this.value = value;
                this.selectedIndex = -1;
                u.each(this.datasource, function (item, index) {
                    if (item.id === this.value) {
                        this.selectedIndex = index;
                        return false;
                    }
                }, this);
                this.selectedItem = this.getSelectedItem();

                if (this.selectedIndex !== -1 && this.emphasizeSelectedItem) {
                    var selector = this.helper.getPart('list');
                    var $lis = $(selector).children('li');
                    var selectedClass = this.helper.getPrimaryClassName('selected-item');
                    $lis.parent().find('.' + selectedClass).removeClass(selectedClass);
                    var $li = $lis.eq(this.selectedIndex);
                    $li.addClass(selectedClass);
                }
                var page = getPageByIndex.call(this);
                this.setPage(page);
            },

            /**
             * 设置page
             *
             * @param {number} page 获取page的序号
             * @public
             */
            setPage: function (page) {
                page = page || 0;
                page = parseInt(page, 10);
                var currentPageClass = this.helper.getPrimaryClassName('current-page');
                if (this.currentPage === null) {
                    this.currentPage = 0;
                }
                if (this.currentPage !== page) {
                    this.currentPage = page;
                }
                var $allDom = $(this.helper.getPart('toolbar')).children();
                u.each($allDom, function (dom, i) {
                    var $dom = $(dom);
                    $dom.removeClass(currentPageClass);
                    var index = +$dom.attr('index');
                    if (this.currentPage === index) {
                        $dom.addClass(currentPageClass);
                    }
                }, this);
                setPointerStyle.call(this);
                setCarouseListPosition.call(this);
            },

            /**
             * 获取选择项的完整数据
             *
             * @return {Object}
             * @public
             */
            getSelectedItem: function () {
                return this.datasource[this.selectedIndex];
            }
        }
    );

    /**
     * 拼接main的dom结构
     *
     * @return {string} html片段
     * @inner
     */
    function getMainHtml() {
        var controlHelper = this.helper;
        return lib.format(
            MAIN_TPL,
            {
                typeSelector: controlHelper.getPrimaryClassName(),
                contentId: controlHelper.getId('main'),
                leftId: controlHelper.getId('left-handler'),
                listId: controlHelper.getId('list'),
                rightId: controlHelper.getId('right-handler'),
                toolbarId: controlHelper.getId('toolbar'),
                iconLeftArrow: controlHelper.getIconClass(),
                iconRightArrow: controlHelper.getIconClass()
            }
        );
    }

    /**
     * 拼接内容项的dom结构
     *
     * @param {Array} data 渲染数据
     * @param {number} itemWidth 单项的宽
     * @param {number} itemHeight 单项的高
     * @param {number} spacing 图片间距
     * @param {number} pageSize 图片间距
     * @return {string} html片段
     * @inner
     */
    function getItemHtml(data, itemWidth, itemHeight, spacing, pageSize) {
        var html = [];
        u.each(data, function (item, index) {
            var index1 = index + 1;
            var str = '';
            if (this.onRenderItem) {
                str = this.onRenderItem(item);
            }
            else {
                str = lib.format(
                    ITEM_TPL,
                    {
                        imgSrc: item.url,
                        width: itemWidth,
                        height: itemHeight,
                        index: index,
                        typeSelector: this.helper.getPrimaryClassName(),
                        itemSelector: this.isDisabled() ? this.helper.getPartClassName('disabled') : '',
                        iconCheck: this.helper.getIconClass(),
                        spacing: (index1 > 0 && index1 % pageSize === 0) ? 0 : spacing
                    }
                );
            }
            html.push(str);
        }, this);
        return html.join('');
    }

    /**
     * 拼接底部分页条的dom结构
     *
     * @param {Array} data 渲染所需的数据
     * @return {string} html片段
     * @inner
     */
    function getToolbarHtml(data) {
        var html = [];
        var len = data.length;
        var divided = Math.ceil(len / this.pageSize);
        this.pageLength = divided;
        for (var i = 0; i < divided; i++) {
            var str = lib.format(
                PAGE_TPL,
                {
                    index: i,
                    typeSelector: this.helper.getPrimaryClassName()
                }
            );
            html.push(str);
        }
        return html.join('');
    }

    /**
     * 获取page的序号根据选中项的index
     *
     * @return {number} page的序号
     * @inner
     */
    function getPageByIndex() {
        if (this.selectedIndex === -1) {
            return 0;
        }
        return Math.floor(this.selectedIndex / this.pageSize);
    }

    /**
     * 设置左右箭头的样式
     *
     * @inner
     */
    function setPointerStyle() {
        var controlHelper = this.helper;
        var disableClass = controlHelper.getPartClassName('pointer-disable');
        var $left = $(controlHelper.getPart('left-handler'));
        var $right = $(controlHelper.getPart('right-handler'));
        var currentPage = this.currentPage;

        $left.removeClass(disableClass);
        $right.removeClass(disableClass);
        if (currentPage === 0) {
            $left.addClass(disableClass);
        }
        if (currentPage === this.pageLength - 1) {
            $right.addClass(disableClass);
        }
    }

    /**
     * 设置翻页的滚动位置
     *
     * @inner
     */
    function setCarouseListPosition() {
        var pageOffset = -this.wrapWidth;
        var left = (pageOffset * this.currentPage) + 'px';
        this.helper.getPart('list').style.left = left;
    }

    /**
     * 左右箭头点击后的处理函数
     *
     * @param {number} n 区别方向  -1=left 1=right
     * @inner
     */
    function pointerClick(n) {
        var nextPage = this.currentPage + n;
        if (nextPage >= this.pageLength || nextPage <  0) {
        }
        else {
            this.setPage(nextPage);
        }
    }

    /**
     * 单个选项处理handler
     *
     * @param {number} index 选项的序号
     * @param {HTMLElement} $el dom对象
     * @inner
     */
    function itemClick(index, $el) {
        if (this.selectedIndex === index) {
            return;
        }
        if (this.emphasizeSelectedItem) {
            var $selector = $(this.helper.getPart('list'));
            var selectedClass = this.helper.getPrimaryClassName('selected-item');
            $selector.children('.' + selectedClass).removeClass(selectedClass);
            $el.addClass(selectedClass);
        }
        this.selectedIndex = index;
        this.selectedItem = this.getSelectedItem();
        this.value = this.selectedItem.id;

        /**
         * @event change
         *
         * 值发生变化时触发
         *
         * `Carousel`控件的值变化是以{@link Carousel#selectedIndex}属性为基准
         */
        this.fire('change');
    }

    /**
     * 翻页按钮点击处理函数
     *
     * @param {number} nextPage 目标页的序号
     * @inner
     */
    function pageClick(nextPage) {
        if (this.currentPage !== nextPage) {
            this.setPage(nextPage);
        }
    }

    /**
     * 选择项切换处理函数,采用事件委托的方式
     *
     * @param {Event} e 事件对象
     * @inner
     */
    function itemChangeHandler(e) {
        var $target = $(e.currentTarget);

        itemClick.call(this, +$target.attr('index'), $target);
    }

    /**
     * 底部工具条处理函数,采用事件委托的方式
     *
     * @param {Event} e 事件对象
     * @inner
     */
    function toolbarHandler(e) {
        var $target = $(e.currentTarget);

        pageClick.call(this, +$target.attr('index'));
    }

    esui.register(Carousel);
    return Carousel;
});
示例#26
0
    function (require) {
        var esui = require('esui');
        var lib = require('esui/lib');
        var util = require('../helper/util');
        var u = require('underscore');
        var eoo = require('eoo');
        var painters = require('esui/painters');

        var RichSelector = require('./RichSelector');

        /**
         * 控件类
         *
         * @class ui.TableRichSelector
         * @extends ui.RichSelector
         */
        var TableRichSelector = eoo.create(
            RichSelector,
            {

                /**
                 * 控件类型,始终为`"TableRichSelector"`
                 *
                 * @type {string}
                 * @override
                 */
                type: 'TableRichSelector',

                /**
                 * @override
                 */
                styleType: 'RichSelector',

                /**
                 * @override
                 */
                initOptions: function (options) {
                    var properties = {
                        // 事件是否只触发在图标上
                        firedOnIcon: false,
                        // 数据源,全集数据
                        datasource: [],
                        // 已选的数据
                        selectedData: [],
                        // 字段,含义与Table相同,searchScope表示这个字段对搜索关键词是全击中还是部分击中
                        // caseSensitive表示大小写敏感,默认不敏感
                        fields: [
                            {
                                field: 'name',
                                title: '名称',
                                content: 'name',
                                searchScope: 'partial',
                                caseSensitive: false,
                                isDefaultSearchField: true
                            }
                        ],
                        // 是否展示表格属性栏
                        hasRowHead: true
                    };

                    u.extend(properties, options);
                    this.$super([properties]);
                },

                /**
                 * @override
                 */
                initStructure: function () {
                    this.$super(arguments);

                    lib.addClass(
                        this.main,
                        this.helper.getPrefixClass('tablerichselector')
                    );
                },

                /**
                 * 重新渲染视图
                 * 仅当生命周期处于RENDER时,该方法才重新渲染
                 *
                 * @param {Array=} 变更过的属性的集合
                 * @override
                 */
                repaint: painters.createRepaint(
                    RichSelector.prototype.repaint,
                    {
                        name: ['datasource', 'selectedData', 'disabledData', 'fields'],
                        paint: function (control, datasource, selectedData, disabledData, fields) {
                            control.refresh();
                            control.fire('change');
                        }
                    }
                ),

                /**
                 * 构建List可以使用的数据结构
                 * 把用户传入数据制作成一个副本allData:
                 * —— allData
                 * [{id: 1, name: xxx}, {id: 2, name: yyy}...]
                 *
                 * 把allData转换成索引表,方便查找,并附加状态信息
                 * —— indexData
                 * {1: {index: 1, isSelected: true}, 2: {index: 2, isDisabled: true}}
                 *
                 * @override
                 */
                adaptData: function () {
                    // 适配id/value的值
                    u.each(
                        this.datasource,
                        function (item) {
                            item.id = item.id || item.value;
                        }
                    );
                    this.allData = lib.deepClone(this.datasource);
                    // 先构建indexData,把数据源里的选择状态清除
                    var indexData = {};
                    u.each(this.allData, function (item, index) {
                        indexData[item.id] = {index: index, isSelected: item.isSelected};
                    });

                    // 把选择状态merge进indexData的数据项中
                    var selectedData = this.selectedData || [];
                    // 单选模式
                    if (!this.multi) {
                        // 如果不是数组,这个值就是id
                        if (!u.isArray(selectedData)) {
                            this.currentSelectedId = selectedData;
                            selectedData = [{id: selectedData}];
                        }
                        // 如果是数组,保存第一个值为当前选值
                        else if (selectedData.length) {
                            this.currentSelectedId = selectedData[0].id || selectedData[0];
                        }
                    }

                    u.each(selectedData, function (item, index) {
                        var id = item.id !== undefined ? item.id : item;
                        // 有可能出现已选的数据在备选中已经被删除的情况
                        if (indexData[id] !== undefined) {
                            indexData[id].isSelected = true;
                        }
                    });

                    var disabledData = this.disabledData || [];
                    u.each(disabledData, function (item, index) {
                        var id = item.id !== undefined ? item.id : item;
                        if (indexData[id] !== undefined) {
                            indexData[id].isDisabled = true;
                        }
                    });

                    this.indexData = indexData;

                    // 处理fields,把fields也保存到一个索引中
                    this.fieldsIndex = {};
                    this.defaultSearchFields = [];
                    u.each(
                        this.fields,
                        function (field) {
                            this.fieldsIndex[field.field] = field;
                            if (field.isDefaultSearchField) {
                                this.defaultSearchFields.push(field.field);
                            }
                        },
                        this
                    );

                    return {
                        allData: this.allData,
                        indexData: this.indexData
                    };
                },

                /**
                 * 更新备选区
                 *
                 * @override
                 */
                refreshContent: function () {
                    var data = this.isQuery() ? this.queriedData : this.allData;
                    if (!data || data.length === 0) {
                        this.addState('empty');
                    }
                    else {
                        this.removeState('empty');
                    }

                    // 开始构建
                    var htmlArray = [];
                    if (this.hasRowHead) {
                        htmlArray.push(this.createTableHead());
                    }
                    htmlArray.push(this.createTableContent(data));

                    var queryList = this.getQueryList();
                    // 选择状态当使用手动触发时,panel的content属性并不会同步更改,
                    // 如果此时新的content恰好与初始的content相同,则panel不会重置
                    // 因此现执行一下置空
                    queryList.setContent('');
                    queryList.setContent(htmlArray.join(''));
                },

                /**
                 * 创建表头
                 *
                 * @public
                 * @return {string} 表头html
                 */
                createTableHead: function () {
                    var tableClass = this.helper.getPartClassName('head-table');
                    var tableId = this.helper.getId('head-table');
                    var tpl = ['<table border=0 class="' + tableClass + '" id="' + tableId + '"><tr>'];
                    var colmNum = this.fields.length;
                    // 绘制表头th
                    for (var i = 0; i < colmNum; i++) {
                        var field = this.fields[i];
                        tpl.push(''
                            + '<th class="th' + i + '"'
                            + ' style="width:' + field.width + 'px;">'
                            + field.title || ''
                            + '</th>'
                        );
                    }
                    // 最后一列用来装箭头
                    tpl.push('<th></th>');
                    tpl.push('</tr></table>');
                    return tpl.join(' ');
                },

                rowTpl: '<tr id="${rowId}" class="${rowClass}" index="${index}">${content}</tr>',

                /**
                 * 创建表格体
                 *
                 * @param {Object} data 绘制的内容
                 * @return {string}
                 * @ignore
                 */
                createTableContent: function (data) {
                    var indexData = this.indexData;
                    var helper = this.helper;

                    var tableClasses = helper.getPartClassName('content-table');
                    var tableId = helper.getId('content-table');
                    var tpl = ['<table border=0 class="' + tableClasses + '" id="' + tableId + '">'];
                    var baseRowClasses = helper.getPartClassName('row');
                    var selectedRowClasses = helper.getPartClassName('row-selected');
                    var disabledRowClasses = helper.getPartClassName('row-disabled');

                    // 绘制内容
                    u.each(
                        data,
                        function (item, index) {
                            var rowClasses = [baseRowClasses];
                            var indexItem = indexData[item.id];
                            if (indexItem.isSelected) {
                                rowClasses.push(selectedRowClasses);
                            }
                            if (indexItem.isDisabled) {
                                rowClasses.push(disabledRowClasses);
                            }
                            tpl.push(
                                lib.format(
                                    this.rowTpl,
                                    {
                                        rowId: this.helper.getId('row-' + item.id),
                                        rowClass: rowClasses.join(' '),
                                        index: indexItem.index,
                                        content: createRow(this, item, index)
                                    }
                                )
                            );
                        },
                        this
                    );
                    tpl.push('</table>');
                    return tpl.join(' ');
                },

                /**
                 * 点击行为分发器
                 * @param {Event} e 事件对象
                 * @ignore
                 */
                eventDispatcher: function (e) {
                    var tar = e.target;
                    var helper = this.helper;
                    var rowClasses = helper.getPartClassName('row');
                    var actionClasses = helper.getPartClassName('row-action-icon');

                    while (tar && tar !== document.body) {
                        var rowDOM;
                        // 有图标的事件触发在图标上
                        if (this.hasIcon
                            && this.fireOnIcon
                            && lib.hasClass(tar, actionClasses)) {
                            rowDOM = tar.parentNode;
                        }
                        else {
                            if (lib.hasClass(tar, rowClasses)) {
                                rowDOM = tar;
                            }
                        }
                        if (rowDOM) {
                            this.operateRow(rowDOM);
                            return;
                        }

                        tar = tar.parentNode;
                    }
                },

                // 可重写
                operateRow: function (row) {
                    var disabledClasses = this.helper.getPartClassName('row-disabled');
                    if (lib.hasClass(row, disabledClasses)) {
                        return;
                    }
                    var index = parseInt(row.getAttribute('index'), 10);
                    var item = this.allData[index];
                    if (!item) {
                        return;
                    }

                    if (this.mode === 'add') {
                        actionForAdd(this, row, item);
                    }
                    else if (this.mode === 'delete') {
                        actionForDelete(this, row, item);
                    }
                    else if (this.mode === 'load') {
                        actionForLoad(this, row, item);
                    }
                },

                /**
                 * 选择全部
                 * 如果当前处于搜索状态,那么只把搜索结果中未选择的选过去
                 * @public
                 */
                selectAll: function () {
                    var data = this.isQuery() ? this.queriedData : this.allData;
                    var control = this;
                    u.each(data, function (item) {
                        selectItem(control, item.id, true);
                    });
                    this.fire('add');
                    this.fire('change');
                },

                /**
                 * 批量更新选择状态,
                 * 用于外部调用,因此不触发事件
                 *
                 * @param {Array} items 需要更新的对象集合或id集合
                 * @param {boolean} toBeSelected 要选择还是取消选择
                 * @override
                 */
                selectItems: function (items, toBeSelected) {
                    var indexData = this.indexData;
                    var control = this;
                    u.each(items, function (item) {
                        var id = item.id !== undefined ? item.id : item;
                        var itemIndex = indexData[id];
                        if (itemIndex !== null && itemIndex !== undefined) {
                            // 更新状态,但不触发事件
                            selectItem(control, id, toBeSelected);
                        }
                    });
                },

                /**
                 * 删除全部
                 *
                 * @FIXME 删除全部要区分搜索和非搜索状态么
                 * @override
                 */
                deleteAll: function () {
                    var items = u.clone(this.datasource);
                    this.set('datasource', []);
                    this.fire('delete', {items: items});
                    this.fire('change');
                },

                /**
                 * 搜索含有关键字的结果,默认以name为目标搜索
                 *
                 * 可重写
                 *
                 * @param {Array} filters 过滤参数
                 * @public
                 */
                queryItem: function (filters) {
                    // 查找过滤 [{ keys: ['xxx', 'yyy'], value: 'xxx' }]
                    filters = filters || [];
                    // 判断数据的某个field是命中
                    function checkHitByFilterItem(field, expectValue, data) {
                        var hit = false;
                        // 只有字符串类去空格
                        if (typeof expectValue === 'string') {
                            expectValue = lib.trim(expectValue);
                        }

                        var config = {
                            isPartial: this.fieldsIndex[field].searchScope === 'partial',
                            caseSensitive: this.fieldsIndex[field].caseSensitive
                        };

                        if (util.compare(data[field], expectValue, config)) {
                            hit = true;
                        }
                        return hit;
                    }

                    // 判断行数据是否符合过滤要求
                    function checkRowHit(data, index) {
                        return !u.any(
                            filters,
                            function (filter) {
                                var searchFields = [];
                                // keys未定义,则默认选择通过field指定的并集
                                if (filter.keys === undefined) {
                                    searchFields = this.defaultSearchFields;
                                }
                                else {
                                    searchFields = filter.keys;
                                }
                                return !u.any(
                                    searchFields,
                                    function (searchField) {
                                        // 命中一个就算命中
                                        return checkHitByFilterItem.call(this, searchField, filter.value, data);
                                    },
                                    this
                                );
                            },
                            this
                        );
                    }

                    this.queriedData = u.filter(
                        this.allData,
                        checkRowHit,
                        this
                    );

                    this.afterQueryHandler();

                },

                afterQueryHandler: function () {
                    // 更新状态
                    this.addState('queried');
                    this.refreshContent();
                },

                /**
                 * 清空搜索的结果
                 *
                 */
                clearData: function () {
                    // 清空数据
                    this.queriedData = [];
                },

                /**
                 * add(load)型:或许当前选择状态的数据
                 * delete型:获取全部数据
                 *
                 * @return {Object}
                 * @public
                 */
                getSelectedItems: function () {
                    var rawData = this.datasource;
                    var indexData = this.indexData;
                    var mode = this.mode;
                    if (mode === 'delete') {
                        return this.allData;
                    }
                    var selectedData = u.filter(rawData, function (item, index) {
                        return indexData[item.id].isSelected;
                    });
                    return selectedData;
                },

                /**
                 * @override
                 */
                getSelectedItemsFullStructure: function () {
                    return this.getSelectedItems();
                },

                /**
                 * 获取当前状态的显示个数
                 *
                 * @return {number}
                 * @override
                 */
                getCurrentStateItemsCount: function () {
                    var data = this.isQuery() ? this.queriedData : this.allData;
                    data = data || [];
                    return data.length;
                }
            }
        );

        /**
         * 创建Table的每行
         *
         * @param {ui.TableForSelector} control 类实例
         * @param {Object} item 每行的数据
         * @param {number} index 行索引
         * @param {HTMLElement} tr 容器节点
         * @return {string}
         * @ignore
         */
        function createRow(control, item, index, tr) {
            var fields = control.fields;
            var html = [];
            var fieldClasses = control.helper.getPartClassName('row-field');
            var cursor = 0;
            u.each(fields, function (field, i) {
                var content = field.content;
                var innerHTML = ('function' === typeof content
                    ? content.call(control, item, index, i)
                    : u.escape(item[content]));

                var textContent = field.textContent ? field.textContent.call(control, item, index, i) : innerHTML;

                // IE不支持tr.innerHTML,所以这里要使用insertCell
                if (tr) {
                    var td = tr.insertCell(i);
                    td.style.width = field.width + 'px';
                    td.title = textContent;
                    td.innerHTML = innerHTML;
                }
                else {
                    var contentHtml = ''
                        + '<td class="' + fieldClasses
                        + '" title="' + textContent
                        + '" style="width:' + field.width + 'px;">'
                        + innerHTML
                        + '</td>';
                    html.push(contentHtml);
                }
                cursor++;
            });

            // 最后一列添加箭头
            var arrowClasses = control.helper.getPartClassName('row-action-icon')
                + ' ' + control.helper.getIconClass();
            var arrowHTML = '<span class="' + arrowClasses + '"></span>';
            if (tr) {
                var td = tr.insertCell(cursor);
                td.innerHTML = arrowHTML;
            }
            else {
                html.push('<td>' + arrowHTML + '</td>');
                return html.join(' ');
            }
        }

        function actionForAdd(control, row, item) {
            var selectedClasses = control.helper.getPartClassName('row-selected');
            var fire = false;
            var eventArgs = {item: item, status: 1};
            // 点击已选中的,在单选模式下,执行取消选择
            if (lib.hasClass(row, selectedClasses)) {
                if (control.allowUnselectNode) {
                    selectItem(control, item.id, false);
                    fire = true;
                    eventArgs.status = -1;
                }
            }
            else {
                selectItem(control, item.id, true);
                fire = true;
            }

            if (fire) {
                // 需要增加上一个参数,因为有的时候需要了解当前操作的对象是什么
                control.fire('add', eventArgs);
                control.fire('change');
            }
        }

        /**
         * 选择或取消选择
         *   如果控件是单选的,则将自己置灰且将其他节点恢复可选
         *   如果控件是多选的,则仅将自己置灰
         *
         * @param {ui.TableRichSelector} control 类实例
         * @param {Object} id 结点对象id
         * @param {boolean} toBeSelected 置为选择还是取消选择
         *
         * @ignore
         */
        function selectItem(control, id, toBeSelected) {
            // 如果是单选,需要将其他的已选项置为未选
            if (!control.multi) {
                // 移除原有选项
                unselectCurrent(control);
                // 赋予新值
                control.currentSelectedId = toBeSelected ? id : null;
            }
            updateSingleItemStatus(control, id, toBeSelected);
        }

        // 撤销选择当前项
        function unselectCurrent(control) {
            var curId = control.currentSelectedId;
            // 撤销当前选中项
            updateSingleItemStatus(control, curId, false);
            control.currentSelectedId = null;
        }

        /**
         * 更新单个结点状态
         *
         * @param {ui.TableRichSelector} control 类实例
         * @param {string} id 结点数据id
         * @param {boolean} toBeSelected 置为选择还是取消选择
         *
         * @ignore
         */
        function updateSingleItemStatus(control, id, toBeSelected) {
            var indexItem = control.indexData[id];
            if (!indexItem) {
                return;
            }
            indexItem.isSelected = toBeSelected;
            var itemDOM = control.helper.getPart('row-' + id);
            var changeClass = toBeSelected ? lib.addClass : lib.removeClass;
            changeClass(
                itemDOM,
                control.helper.getPartClassName('row-selected')
            );
        }

        /**
         * 下面的方法专属delete型table
         * @param {Object} control table
         * @param {DOMElement} row 行DOM
         * @param {Object} item 要删除的item
         */
        function actionForDelete(control, row, item) {
            deleteItem(control, item.id);
            // 外部需要知道什么数据被删除了
            control.fire('delete', {items: [item]});
            control.fire('change');
        }

        /**
         * 删除选择的节点
         *
         * @param {ui.TableRichSelector} control 类实例
         * @param {number} id 结点数据id
         *
         * @ignore
         */
        function deleteItem(control, id) {
            // 完整数据
            var indexData = control.indexData;
            var index = indexData[id].index;

            var newData = [].slice.call(control.datasource, 0);
            newData.splice(index, 1);

            control.set('datasource', newData);
        }

        function actionForLoad(control, row, item) {
            var selectedClasses = control.helper.getPartClassName('row-selected');
            // 点击未选中的,执行
            if (!lib.hasClass(row, selectedClasses)) {
                selectItem(control, item.id, true);
                control.fire('load', {item: item});
                control.fire('change');
            }
        }

        esui.register(TableRichSelector);
        return TableRichSelector;
    }