function (require) { var esui = require('esui'); var lib = require('esui/lib'); var u = require('underscore'); var Layer = require('esui/Layer'); var Extension = require('esui/Extension'); var eoo = require('eoo'); var CursorPositionHelper = require('./helper/CursorPositionHelper'); var keyboard = require('esui/behavior/keyboard'); require('esui/behavior/jquery-ui'); var TEXT_LINE = 'TextLine'; var TEXT_BOX = 'TextBox'; var INPUT = 'input'; var $ = require('jquery'); /** * @class AutoCompleteLayer * @exports esui.Layer */ var AutoCompleteLayer = eoo.create( Layer, { /** * 自动提示层构造器 * * @param {Object} [control] TextBox控件 */ constructor: function (control) { this.$super(arguments); // 对于input单行,要求layer宽度不大于input宽度 var controlType = control.type; if (controlType === TEXT_BOX) { var ele = lib.g(control.inputId); if (ele.tagName.toLowerCase() === INPUT) { this.dock = { strictWidth: true }; } } this.initStructure(); this.initEvents(); }, type: 'AutoCompleteLayer', initStructure: function () { var helper = this.control.helper; this.addCustomClasses( [ helper.getPrefixClass('autocomplete'), helper.getPrefixClass('dropdown') ] ); var element = this.getElement(); $(this.control.main).after(element); }, initEvents: function () { var me = this; var target = me.control; var helper = target.helper; var inputElement = this.inputElement = this.getInput(); var layerElement = me.getElement(false); helper.addDOMEvent( layerElement, 'click', 'li', function (e) { var clickedTarget = e.currentTarget; me.hide(); var firstChild = $(clickedTarget.firstChild); var text = firstChild.text(); var id = firstChild.closest('li').data('id'); // 事件参数 var args = {value: text}; // 如果选择项存在id属性,则在事件参数中加上 if (id !== undefined) { args.id = id; } // 为了与 `select` 事件区分,用了 `targetselect` var selectEvent = me.control.fire('targetselect', args); if (selectEvent.isDefaultPrevented()) { return; } /** * @deprecated */ var deprecatedEvent = me.control.fire('select', text); if (deprecatedEvent.isDefaultPrevented()) { return; } setTargetValue.call(me, text); } ); helper.addDOMEvent( inputElement, 'keydown', function (e) { if (me.isHidden()) { return; } switch (e.keyCode) { // up case keyboard.UP: e.preventDefault(); moveTo.call(me, 'up'); break; // down case keyboard.DOWN: e.preventDefault(); moveTo.call(me, 'down'); break; // esc case keyboard.ESC: me.hide(); break; // enter case keyboard.RETURN: e.preventDefault(); var selectedItem = me.getSelectedItem(); if (!selectedItem) { return; } me.hide(); var firstChild = $(selectedItem.firstChild); var text = firstChild.text(); var id = firstChild.closest('li').data('id'); // 事件参数 var args = {value: text}; // 如果选择项存在id属性,则在事件参数中加上 if (id !== undefined) { args.id = id; } // 为了与 `select` 事件区分,用了 `targetselect` var selectEvent = me.control.fire('targetselect', args); if (selectEvent.isDefaultPrevented()) { return; } /** * @deprecated */ var deprecatedEvent = me.control.fire('select', text); if (deprecatedEvent.isDefaultPrevented()) { return; } setTimeout( function () { setTargetValue.call(me, text); }, 0 ); break; } } ); this.control.on('input', onInput); /** * 用户输入时触发,根据输入下拉提示 * * @param {Event} event 事件对象 */ function onInput(event) { var elementValue = inputElement.value; // 空格或逗号结尾都忽略 if (!elementValue || /(?:\s|\,)$/.test(elementValue)) { repaintSuggest.call(me, ''); me.hide(); return; } if (u.isFunction(target.extractWord)) { elementValue = target.extractWord(elementValue); } else { elementValue = extractMatchingWord(elementValue); } if (!elementValue) { return; } if (target.search && target.search(elementValue) === false) { return; } repaintSuggest.call(me, elementValue); } }, repaint: function (value) { var element = this.getElement(false); if (element) { this.render(element, value); } }, render: function (element, value) { if (value != null) { element.innerHTML = value; } }, getSelectedItemIndex: function () { var element = this.getElement(false); var items = element.children; var selectedItemIndex = -1; for (var i = 0, len = items.length; i < len; i++) { if ($(items[i]).hasClass( this.control.helper.getPrefixClass('autocomplete-item-hover') )) { selectedItemIndex = i; break; } } return selectedItemIndex; }, getSelectedItem: function () { var element = this.getElement(false); var selectedItem; var selectedItemIndex = this.getSelectedItemIndex(); if (selectedItemIndex !== -1) { selectedItem = element.children[selectedItemIndex]; } return selectedItem; }, /** * @override * 对textarea自行实现position */ position: function () { var input = this.inputElement; if (input.nodeName.toLowerCase() !== 'textarea') { this.$super(arguments); } }, show: function () { this.$super(arguments); var input = this.inputElement; var $ele = $(this.getElement(false)); if (input.nodeName.toLowerCase() === 'textarea') { var cursorInstance = CursorPositionHelper.getInstance(input); var pos = cursorInstance.getCaretPosition(); $ele.position( { of: input, at: 'left+' + pos.left + ' top+' + pos.top, my: 'left top' } ); } }, isHidden: function () { var element = this.getElement(); return $(element).is(':hidden'); }, /** * 获取内部输入元素 * * @return {Element} */ getInput: function () { var control = this.control; if (control.type === TEXT_BOX) { return lib.g(control.inputId); } else if (control.type === TEXT_LINE) { return control.getTextArea(); } return null; }, nodeName: 'ol' } ); /** * 匹配已输入值的算法 * * @param {string} value 当前用户输入 * @param {Array} datasource 数据源 * @return {Array} */ function filter(value, datasource) { return u.filter( datasource, function (data) { var text = u.isObject(data) ? data.text : data; return (new RegExp(escapeRegex(value), 'i')).test(text); } ); } /** * 特殊字符处理,这些字符排除在匹配算法外 * * @param {string} value 用户输入 * @return {string} */ function escapeRegex(value) { return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); } /** * 根据用户输入绘制下拉选择列表 * * @param {sttring} value 用户输入 */ function repaintSuggest(value) { if (!value) { renderSuggest.call(this); return; } var me = this; var datasource = this.control.datasource; if (typeof datasource === 'function') { datasource.call( this, value, function (data) { renderSuggest.call(me, data, value); } ); } else if (datasource && datasource.length) { renderSuggest.call(me, filter(value, datasource), value); } } function renderSuggest(data, inputValue) { var helper = this.control.helper; /** * 将text中匹配到的搜索词高亮 * * @param {string} word 要高亮的关键字 * @return {string} */ function highlightWord(word) { return '<i class="' + helper.getPrefixClass('autocomplete-item-char-selected') + '">' + word + '</i>'; } var ret = []; if (data && data.length) { for (var i = 0, len = data.length; i < len; i++) { var item = data[i]; var text = u.isObject(item) && item.text || item; var desc = u.isObject(item) && item.desc || undefined; var html = lib.format( '<li tabindex="-1" ${dataId} class="${lineClasses}">' + '<span class="${itemClasses}">${text}</span>${desc}</li>', { dataId: u.isObject(item) && item.id ? ' data-id="' + item.id + '"' : '', lineClasses: helper.getPrefixClass('autocomplete-item') + (i === 0 ? ' ' + helper.getPrefixClass('autocomplete-item-hover') : ''), itemClasses: helper.getPrefixClass('autocomplete-item-text'), text: text.replace( new RegExp(escapeRegex(inputValue), 'i'), highlightWord ), desc: desc ? '<span class="' + helper.getPrefixClass('autocomplete-item-desc') + '">' + item.desc + '</span>' : '' } ); ret.push(html); } } ret = ret.join(''); this.repaint(ret); ret ? this.show() : this.hide(); } /** * 将用户选中值回填到input输入框 * * @param {string} value 用户选择值 */ function setTargetValue(value) { var input = this.getInput(); var targetValue = input.value; targetValue = lib.trim(targetValue); var items = []; if (/\n/.test(targetValue)) { items = targetValue.split(/\n/); targetValue = items && items.pop(); } var words = targetValue.split(','); words.pop(); words.push(value); if (items) { items.push(words.join(',')); value = items.join('\n'); } this.control.setValue(value); } function extractMatchingWord(value) { var lines = value.split(/\n/); var line = lines.pop(); var words = line.split(','); var word = words && words.pop(); return lib.trim(word); } /** * 下拉建议列表中上下选择 * * @param {string} updown up / down */ function moveTo(updown) { var element = this.getElement(false); var items = element.children; var selectedItemIndex = this.getSelectedItemIndex(); if (selectedItemIndex !== -1) { var selectedItem = items[selectedItemIndex]; if (selectedItem) { lib.removeClass( selectedItem, this.control.helper.getPrefixClass('autocomplete-item-hover') ); } } if (updown === 'up') { if (selectedItemIndex === -1 || selectedItemIndex === 0) { selectedItemIndex = items.length - 1; } else { selectedItemIndex--; } } else if (updown === 'down') { if (selectedItemIndex === -1 || selectedItemIndex === items.length - 1) { selectedItemIndex = 0; } else { selectedItemIndex++; } } selectedItem = items[selectedItemIndex]; $(selectedItem).addClass(this.control.helper.getPrefixClass('autocomplete-item-hover')); selectedItem && selectedItem.focus(); this.inputElement.focus(); } var AutoComplete = eoo.create( Extension, { /** * 输入控件自动提示扩展 * * 当输入控件加上此扩展后,其自动提示功能将由扩展自动提供 * * @class extension.AutoComplete * @extends Extension * @constructor */ constructor: function () { this.$super(arguments); }, /** * 指定扩展类型,始终为`"AutoComplete"` * * @type {string} */ type: 'AutoComplete', attachTo: function () { this.$super(arguments); var me = this; setTimeout(function () { me.layer = new AutoCompleteLayer(me.target); }, 0); }, /** * 激活扩展 * * @override */ activate: function () { // 只对`TextBox` 和 `TextLine`控件生效 var type = this.target.type; if (!(type === TEXT_LINE || type === TEXT_BOX)) { return; } this.$super(arguments); }, /** * 取消扩展的激活状态 * * @override */ inactivate: function () { var helper = this.target.helper; var inputEle = this.inputElement; helper.removeDOMEvent(inputEle, INPUT); var layerMain = this.layer.getElement(false); if (layerMain) { helper.removeDOMEvent(layerMain, 'click'); $(layerMain).remove(); } helper.removeDOMEvent(inputEle, 'keydown'); this.$super(arguments); } } ); esui.registerExtension(AutoComplete); return AutoComplete; }
function (require) { var u = require('underscore'); var Table = require('../Table'); var Extension = require('../Extension'); var eoo = require('eoo'); var esui = require('esui'); /** * 表格自动排序扩展 * * 当表格加上此扩展后,其排序功能将由扩展自动提供 * * 扩展默认使用简单的两值相减(字符串用`localeCompare`)的方法判断大小, * 也可以在表格具体列的配置中给出`comparer`属性来提供自定义的排序算法 * * @class extension.AutoSort * @extends Extension * @constructor */ var AutoSort = eoo.create( Extension, { /** * 指定扩展类型,始终为`"AutoSort"` * * @type {string} */ type: 'AutoSort', /** * 激活扩展 * * @override */ activate: function () { // 只对`Table`控件生效 if (!(this.target instanceof Table)) { return; } this.target.on('sort', sort); this.$super(arguments); }, /** * 取消扩展的激活状态 * * @override */ inactivate: function () { // 只对`Table`控件生效 if (!(this.target instanceof Table)) { return; } this.target.un('sort', sort); this.$super(arguments); } } ); function sort(e) { var computeDiff = e.field.comparer; if (!computeDiff) { var fieldName = e.field.field; computeDiff = function (x, y) { if (fieldName) { x = x[fieldName]; y = y[fieldName]; } return u.isString(x) && u.isString(y) ? x.localeCompare(y) : x - y; }; } function compare(x, y) { var diff = computeDiff(x, y); return e.order === 'asc' ? diff : -diff; } var datasource = this.datasource; datasource.sort(compare); this.setDatasource(datasource); } esui.registerExtension(AutoSort); return AutoSort; }
function (require) { var esui = require('esui'); var lib = require('esui/lib'); var u = require('underscore'); var Layer = require('esui/Layer'); var Extension = require('esui/Extension'); var eoo = require('eoo'); var textCursorHelper= require('./helper/TextCursorHelper'); var keyboard = require('esui/behavior/keyboard'); require('esui/behavior/jquery-ui'); var TEXT_LINE = 'TextLine'; var TEXT_BOX = 'TextBox'; var INPUT = 'input'; var $ = require('jquery'); /** * @class AutoCompleteLayer * @exports esui.Layer */ var AutoCompleteLayer = eoo.create( Layer, { /** * 自动提示层构造器 * * @param {Object} [control] TextBox控件 */ constructor: function (control) { this.$super(arguments); // 对于input单行,要求layer宽度不大于input宽度 var controlType = control.type; if (controlType === TEXT_BOX) { var ele = lib.g(control.inputId); if (ele.tagName.toLowerCase() === INPUT) { this.dock = { strictWidth: true }; } } this.initStructure(); this.initEvents(); }, type: 'AutoCompleteLayer', initStructure: function () { var helper = this.control.helper; this.addCustomClasses( [ helper.getPrefixClass('autocomplete'), helper.getPrefixClass('dropdown') ] ); var element = this.getElement(); $(this.control.main).after(element); }, initEvents: function () { var me = this; var target = me.control; var helper = target.helper; var inputElement = this.inputElement = this.getInput(); var layerElement = me.getElement(false); helper.addDOMEvent( layerElement, 'click', 'li', function (e) { var clickedTarget = e.currentTarget; me.hide(); var firstChild = $(clickedTarget.firstChild); var text = firstChild.text(); var id = firstChild.closest('li').data('id'); // 事件参数 var args = {value: text}; // 如果选择项存在id属性,则在事件参数中加上 if (id !== undefined) { args.id = id; } // 为了与 `select` 事件区分,用了 `targetselect` var selectEvent = me.control.fire('targetselect', args); if (selectEvent.isDefaultPrevented()) { return; } /** * @deprecated */ var deprecatedEvent = me.control.fire('select', text); if (deprecatedEvent.isDefaultPrevented()) { return; } setTargetValue.call(me, text); } ); helper.addDOMEvent( inputElement, 'keydown', function (e) { if (me.isHidden()) { return; } switch (e.keyCode) { // up case keyboard.UP: e.preventDefault(); moveTo.call(me, 'up'); break; // down case keyboard.DOWN: e.preventDefault(); moveTo.call(me, 'down'); break; // esc case keyboard.ESC: me.hide(); break; // enter case keyboard.RETURN: e.preventDefault(); var selectedItem = me.getSelectedItem(); if (!selectedItem) { return; } me.hide(); var firstChild = $(selectedItem.firstChild); var text = firstChild.text(); var id = firstChild.closest('li').data('id'); // 事件参数 var args = {value: text}; // 如果选择项存在id属性,则在事件参数中加上 if (id !== undefined) { args.id = id; } // 为了与 `select` 事件区分,用了 `targetselect` var selectEvent = me.control.fire('targetselect', args); if (selectEvent.isDefaultPrevented()) { return; } /** * @deprecated */ var deprecatedEvent = me.control.fire('select', text); if (deprecatedEvent.isDefaultPrevented()) { return; } setTimeout( function () { setTargetValue.call(me, text); }, 0 ); break; } } ); this.control.on('input', onInput); /** * 用户输入时触发,根据输入下拉提示 * * @param {Event} event 事件对象 */ function onInput(event) { // 保留光标位置,待会儿插入时需要知道在哪儿插入 me.caretPos = textCursorHelper.getCaretPosition(inputElement); // 获取光标前的字符 var val = textCursorHelper.getTextBeforeCaret(inputElement); // 进入数据面板触发逻辑 repaintHelperSlector.call(me, val); this.fire('change', {args: val}); } }, repaint: function (value) { var element = this.getElement(false); if (element) { this.render(element, value); } }, render: function (element, value) { if (value != null) { element.innerHTML = value; } }, getSelectedItemIndex: function () { var element = this.getElement(false); var items = element.children; var selectedItemIndex = -1; for (var i = 0, len = items.length; i < len; i++) { if ($(items[i]).hasClass( this.control.helper.getPrefixClass('autocomplete-item-hover') )) { selectedItemIndex = i; break; } } return selectedItemIndex; }, getSelectedItem: function () { var element = this.getElement(false); var selectedItem; var selectedItemIndex = this.getSelectedItemIndex(); if (selectedItemIndex !== -1) { selectedItem = element.children[selectedItemIndex]; } return selectedItem; }, /** * @override * 对textarea自行实现position */ position: function () { var input = this.inputElement; if (input.nodeName.toLowerCase() !== 'textarea') { this.$super(arguments); } }, show: function () { this.$super(arguments); var input = this.inputElement; var $ele = $(this.getElement(false)); if (input.nodeName.toLowerCase() === 'textarea') { var pos = textCursorHelper.getCaretPositionStyle(input); $ele.position( { of: input, at: 'left+' + pos.left + ' top+' + pos.top, my: 'left top' } ); } }, isHidden: function () { var element = this.getElement(); return $(element).is(':hidden'); }, /** * 获取内部输入元素 * * @return {Element} */ getInput: function () { var control = this.control; if (control.type === TEXT_BOX) { return lib.g(control.inputId); } else if (control.type === TEXT_LINE) { return control.getTextArea(); } return null; }, /** * 获取查询词,也即 at 符号后边的词 * * @param {string} val 截取字符 * @return {string} 查询词 */ getQuery: function (val) { var lastAtIndex = val.lastIndexOf(this.control.openAt); return val.slice(lastAtIndex + 1); }, nodeName: 'ol' } ); /** * 匹配已输入值的算法 * * @param {string} value 当前用户输入 * @param {Array} datasource 数据源 * @return {Array} */ function filter(value, datasource) { return u.filter( datasource, function (data) { var text = u.isObject(data) ? data.text : data; return (new RegExp(escapeRegex(value), 'i')).test(text); } ); } /** * 特殊字符处理,这些字符排除在匹配算法外 * * @param {string} value 用户输入 * @return {string} */ function escapeRegex(value) { return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); } /** * 根据用户输入绘制下拉选择列表 * * @param {string} value 用户输入 */ function repaintHelperSlector(value) { // 判断是否需要弹出帮助面板 if (canShowSelector.call(this, value)) { // 获取搜索词 this.query = this.getQuery(value); var me = this; var datasource = this.control.datasource; if (typeof datasource === 'function') { datasource.call( this, value, function (data) { renderSlector.call(me, data, value); } ); } else if (datasource && datasource.length) { // 根据搜索词,获取过滤后的优选词数据列表 var list = filter(this.query, datasource); // 渲染帮助面板 renderSlector.call(me, list, value); } } else { this.hide(); } } /** * 绘制下拉选择列表 * * @param {Array} data 需要渲染的数据源 * @param {string} inputValue 需要检索的字符串 */ function renderSlector(data, inputValue) { var helper = this.control.helper; /** * 将text中匹配到的搜索词高亮 * * @param {string} word 要高亮的关键字 * @return {string} */ function highlightWord(word) { return '<i class="' + helper.getPrefixClass('autocomplete-item-char-selected') + '">' + word + '</i>'; } var ret = []; if (data && data.length) { for (var i = 0, len = data.length; i < len; i++) { var item = data[i]; var text = u.isObject(item) && item.text || item; var desc = u.isObject(item) && item.desc || undefined; var html = lib.format( '<li tabindex="-1" ${dataId} class="${lineClasses}">' + '<span class="${itemClasses}">${text}</span>${desc}</li>', { dataId: u.isObject(item) && item.id ? ' data-id="' + item.id + '"' : '', lineClasses: helper.getPrefixClass('autocomplete-item') + (i === 0 ? ' ' + helper.getPrefixClass('autocomplete-item-hover') : ''), itemClasses: helper.getPrefixClass('autocomplete-item-text'), text: text.replace( new RegExp(escapeRegex(inputValue), 'i'), highlightWord ), desc: desc ? '<span class="' + helper.getPrefixClass('autocomplete-item-desc') + '">' + item.desc + '</span>' : '' } ); ret.push(html); } } ret = ret.join(''); this.repaint(ret); ret ? this.show() : this.hide(); } /** * 将用户选中值回填到input输入框 * * @param {string} value 用户选择值 */ function setTargetValue(value) { var input = this.getInput(); var closeTag = this.control.closeAt ? this.control.closeAt : ''; // 执行插入操作,先删除查询词然后插入提示词 textCursorHelper.del(input, -this.query.length, this.caretPos); textCursorHelper.add(input, value + closeTag, this.caretPos - this.query.length); } function extractMatchingWord(value) { var lines = value.split(/\n/); var line = lines.pop(); var words = line.split(','); var word = words && words.pop(); return lib.trim(word); } /** * 检测是否需要显示数据面板,检测逻辑如下: * * 一:局部替换,开闭符号都包含 * - 包含触发符号 `{` * - 最后一个触发符号 `{` 需要出现在最后一个触发结束符号 `}` 后边 * * 二:全部替换,只有开合(openAt)没有闭合(closeAt) * - 这时候会在开始输入的时候就给提示并进行替换 * * @param {string} val 光标前的数据 * @return {boolean} 需要显示返回 true,否则返回 false */ function canShowSelector (val) { var openIndex = -1; var closeIndex = -1; var openTag = this.control.openAt; var closeTag = this.control.closeAt; if (openTag) { openIndex = val.lastIndexOf(openTag); } if (closeTag) { closeIndex = val.lastIndexOf(closeTag); } if (openIndex >= 0 && openIndex > closeIndex) { return true; } else if (openTag && !closeTag) { return true; } return false; }; /** * 下拉建议列表中上下选择 * * @param {string} updown up / down */ function moveTo(updown) { var element = this.getElement(false); var items = element.children; var selectedItemIndex = this.getSelectedItemIndex(); if (selectedItemIndex !== -1) { var selectedItem = items[selectedItemIndex]; if (selectedItem) { lib.removeClass( selectedItem, this.control.helper.getPrefixClass('autocomplete-item-hover') ); } } if (updown === 'up') { if (selectedItemIndex === -1 || selectedItemIndex === 0) { selectedItemIndex = items.length - 1; } else { selectedItemIndex--; } } else if (updown === 'down') { if (selectedItemIndex === -1 || selectedItemIndex === items.length - 1) { selectedItemIndex = 0; } else { selectedItemIndex++; } } selectedItem = items[selectedItemIndex]; $(selectedItem).addClass(this.control.helper.getPrefixClass('autocomplete-item-hover')); selectedItem && selectedItem.focus(); this.inputElement.focus(); } var AutoComplete = eoo.create( Extension, { /** * 输入控件自动提示扩展 * * 当输入控件加上此扩展后,其自动提示功能将由扩展自动提供 * * @class extension.AutoComplete * @extends Extension * @constructor */ constructor: function () { this.$super(arguments); }, /** * 指定扩展类型,始终为`"AutoComplete"` * * @type {string} */ type: 'AutoComplete', attachTo: function () { this.$super(arguments); var me = this; setTimeout(function () { me.layer = new AutoCompleteLayer(me.target); }, 0); }, /** * 激活扩展 * * @override */ activate: function () { // 只对`TextBox` 和 `TextLine`控件生效 var type = this.target.type; if (!(type === TEXT_LINE || type === TEXT_BOX)) { return; } this.$super(arguments); }, /** * 取消扩展的激活状态 * * @override */ inactivate: function () { var helper = this.target.helper; var inputEle = this.inputElement; helper.removeDOMEvent(inputEle, INPUT); var layerMain = this.layer.getElement(false); if (layerMain) { helper.removeDOMEvent(layerMain, 'click'); $(layerMain).remove(); } helper.removeDOMEvent(inputEle, 'keydown'); this.$super(arguments); } } ); esui.registerExtension(AutoComplete); return AutoComplete; }
function (require) { var lib = require('esui/lib'); var ui = require('esui'); var u = require('underscore'); var Extension = require('esui/Extension'); require('esui/TipLayer'); /** * 用于表格的Tip扩展,为操作列图标添加Tip * * 使用此扩展,只要操作列图标包含`"table-operation"`这一class, * 同时有`"table-operation-{type}"`的class, * 即可在鼠标移到图标上时,使用`Tip`控件显示该图标对应的操作 * * 如以下HTML: * * <span class="table-operation table-operation-edit">编辑</span> * * 即会在鼠标移到元素上时,出现一个Tip控件,提示内容为“编辑” * * **注意,此扩展不支持IE7** * * @extends esui.Extension * @constructor */ function TableTip() { Extension.apply(this, arguments); } /** * 扩展的类型,始终为`"TableTip"` * * @type {string} * @override */ TableTip.prototype.type = 'TableTip'; var typeRule = /table-operation-(\w+)/; function getTipType(element) { return typeRule.exec(element.className)[1]; } /** * 创建`Tip`控件并附加到相应元素上 * * @param {HTMLElement[]} elements 需要`Tip`的元素 * @param {string} type 操作类型 */ TableTip.prototype.createAndAttachTip = function (elements, type) { var options = { id: 'table-operation-tip-' + u.escape(type), viewContext: this.target.viewContext, content: elements[0].innerHTML, arrow: true, skin: 'table-tip' }; var tip = ui.create('TipLayer', options); tip.appendTo(document.body); u.each( elements, function (element) { var options = { targetDOM: element, showMode: 'over', delayTime: 200, positionOpt: { bottom: 'bottom', left: 'left' } }; tip.attachTo(options); } ); }; /** * 初始化操作列的`Tip`控件 */ TableTip.prototype.initTips = function () { if (!document.querySelectorAll) { return; } var elements = document.querySelectorAll('.table-operation'); u.chain(elements) .groupBy(getTipType) .each(u.bind(this.createAndAttachTip, this)); }; /** * 激活扩展 * * @override */ TableTip.prototype.activate = function () { Extension.prototype.activate.apply(this, arguments); this.target.on('afterrender', this.initTips, this); }; /** * 取消扩展的激活状态 * * @override */ TableTip.prototype.inactivate = function () { this.target.un('afterrender', this.initTips, this); Extension.prototype.inactivate.apply(this, arguments); }; lib.inherits(TableTip, Extension); ui.registerExtension(TableTip); return TableTip; }
define(function (require) { var util = require('er/util'); var esui = require('esui'); var Extension = require('esui/Extension'); var aop = require('library/base/aop'); var us = require('underscore'); var localStorage = require('library/framework/storage/localStorage'); /** * 凤巢定制版TipLayer控件的关闭扩展功能。 * * @param {Object} * { * targetKey: '', // bubble所对应的唯一key值。 * closeTimes: 1 // 关闭次数 * } * * @constructor * @extends Extension */ function FcBubbleLayerClosable(options) { options = options || {}; options.closeTimes = +options.closeTimes || 0; Extension.apply(this, arguments); } util.inherits(FcBubbleLayerClosable, Extension); /** * 指定扩展类型,始终为`"FcBubbleLayerClosable"` * * @type {string} */ FcBubbleLayerClosable.prototype.type = 'FcBubbleLayerClosable'; /** * 气泡的宽度。 * * @const * @type {number} */ var BUBBLE_LAYER_WIDTH = 260; /** * 渲染关闭按钮。 * * @protected */ FcBubbleLayerClosable.prototype.renderCloseButton = function () { var me = this; var tipLayer = me.target; var closeButton = $( '' + '<a href="javascript:;" class="' + tipLayer.helper.getPartClasses('close-button').join(' ') + '">' + '<i class="font-icon font-icon-times"></i>' +'</a>' ); closeButton.prependTo(tipLayer.main); tipLayer.helper.addDOMEvent( closeButton[0], 'click', function () { tipLayer.fire('close'); tipLayer.hide(); } ); tipLayer.helper.addPartClasses('body-panel-extra', tipLayer.getBody().main); }; /** * 激活扩展 * * @override */ FcBubbleLayerClosable.prototype.activate = function () { var me = this; var tipLayer = me.target; $(tipLayer.main).width(BUBBLE_LAYER_WIDTH); if (tipLayer.helper.isInStage('RENDERED')) { me.renderCloseButton(); } else { tipLayer.on('afterrender', me.renderCloseButton, me); } Extension.prototype.activate.apply(this, arguments); tipLayer.on('close', me.recordCloseTimes, me); aop.after(tipLayer, 'attachTo', function (options) { me.extraShow(); }); tipLayer.on('show', me.extraShow, me); }; /** * 显示时,需要执行的额外的方法。 * * @protected */ FcBubbleLayerClosable.prototype.extraShow = function () { var me = this; me.cancelClickBodyEvent(); me.computeCloseTimes(); }; /** * 取消tip layer的点击任意地方消失事件。 * * @protected */ FcBubbleLayerClosable.prototype.cancelClickBodyEvent = function () { var me = this; var tipLayer = me.target; var delayTime = tipLayer.delayTime; if (!+delayTime) { delayTime = 0; } // 由于TipLayer中,添加body点击事件是通过setTimeout,并等待delayTime再注册, // 所以这里,移除注册就要在delayTime + 1之后。 setTimeout(function () { // 移除鼠标点击别处事件。 tipLayer.helper.removeDOMEvent($('body')[0], 'click'); }, delayTime + 1); }; /** * 记录关闭次数。 * * @protected */ FcBubbleLayerClosable.prototype.recordCloseTimes = function () { var me = this; if (me.closeTimes) { var targetKey = this.targetKey; var bubbleRecord = me.getBubbleRecord(); var closedTimes = bubbleRecord[targetKey]; if (closedTimes < me.closeTimes) { ++bubbleRecord[targetKey]; localStorage.updateItem('bubbleRecord', bubbleRecord); } } }; /** * 当bubble关闭的次数超过了指定的关闭次数,就将其隐藏。 * * @protected */ FcBubbleLayerClosable.prototype.computeCloseTimes = function () { var me = this; var tipLayer = me.target; var targetKey = this.targetKey; var bubbleRecord = me.getBubbleRecord(); var closedTimes = bubbleRecord[targetKey]; if (me.closeTimes) { if (closedTimes >= me.closeTimes) { tipLayer.hide(); } } }; /** * 从storage中获取bubble record对象 * * @return {Object} */ FcBubbleLayerClosable.prototype.getBubbleRecord = function () { var bubbleRecord = localStorage.getItem('bubbleRecord'); if (!bubbleRecord) { localStorage.setItem('bubbleRecord', {}); bubbleRecord = {}; } var targetKey = this.targetKey; if (!bubbleRecord[targetKey]) { bubbleRecord[targetKey] = 0; } return bubbleRecord; }; /** * 取消扩展的激活状态 * * @override */ FcBubbleLayerClosable.prototype.inactivate = function () { var me = this; var tipLayer = me.target; tipLayer.un('afterrender', me.renderCloseButton, me); tipLayer.un('close', me.recordCloseTimes, me); tipLayer.un('show', me.extraShow, me); Extension.prototype.activate.apply(me, arguments); }; esui.registerExtension(FcBubbleLayerClosable); return FcBubbleLayerClosable; });
function (require) { var lib = require('../lib'); var ui = require('esui'); var u = require('underscore'); var Extension = require('esui/Extension'); require('esui/TipLayer'); var exports = {}; // BIG BROTHER IS WATCHING YOU var tipId = 0x1984; /** * 为控件快速添加Tip显示 * * 使用此扩展,鼠标移入控件后如果悬浮在带有`"data-title"`属性的元素内,会按其 * 内容显示相应Tip。内容可以通过引用的方式读取扩展对象中的`data`属性。 * * 例如控件`tip`属性为: * * { del: '点击进行<strong>删除(不可恢复)</strong>' } * * 书写HTML模板时需添加: * * data-ui-extension-tip-type="QuickTip" * data-ui-extension-tip-data="@tipData" <- 当使用引用方式读取时必传 * * 控件内有元素: * * <button data-title="点击进行编辑">编辑</button> * <button data-title="@del">删除</button> * * 即会在鼠标移到元素上时,出现一个Tip控件,提示内容按`data-title`设置给出。 * * @extends esui.Extension * @constructor */ exports.constructor = function () { Extension.apply(this, arguments); }; /** * 扩展的类型,始终为`"QuickTip"` * * @type {string} * @override */ exports.type = 'QuickTip'; function getProperty(target, path) { var value = target; for (var i = 0; i < path.length; i++) { value = value[path[i]]; } return value; } exports.getTipContent = function (element) { var content = element.getAttribute('data-title'); // 引用目标控件的属性值 if (content.charAt(0) === '@') { var path = content.substring(1).split('.'); var data = this.data; if (!data) { return ''; } var value = data[path[0]]; return path.length > 1 ? getProperty(value, path.slice(1)) : value; } // 字符串直接量 return content; }; /** * 创建`Tip`控件并附加到相应元素上 * * @param {HTMLElement} element 需要`Tip`的元素 */ exports.showTip = function (element) { var content = this.getTipContent(element); content = typeof content === 'string' ? {content: content} : content; var tip = this.current; // 每个扩展只支持同时显示一个Tip if (!tip) { var options = { id: 'ui-tip-' + tipId++, viewContext: this.target.viewContext, arrow: true }; u.extend(options, content); tip = ui.create('TipLayer', options); tip.appendTo(document.body); this.current = tip; } else { tip.setProperties(content); } var attachOptions = { targetDOM: element, showMode: 'over', delayTime: 200, positionOpt: { top: 'top', right: 'left' } }; tip.attachTo(attachOptions); tip.show(element, attachOptions.positionOpt); }; /** * 鼠标移入的逻辑 * * @param {Event} evt 事件对象 */ function mouseEnterHandler(evt) { var target = evt.target; this.showTip(target); } /** * 鼠标移出的逻辑 * * @param {Event} evt 事件对象 */ function mouseLeaveHandler(evt) { if (this.current) { this.current.hide(); } } /** * 激活扩展 * * @override */ exports.activate = function () { Extension.prototype.activate.apply(this, arguments); this.mouseEnterHandler = u.bind(mouseEnterHandler, this); this.mouseLeaveHandler = u.bind(mouseLeaveHandler, this); lib.on(this.target.main, '[data-title]', 'mouseenter', this.mouseEnterHandler); lib.on(this.target.main, '[data-title]', 'mouseleave', this.mouseLeaveHandler); }; /** * 取消扩展的激活状态 * * @override */ exports.inactivate = function () { if (this.current) { this.current.dispose(); } lib.un(this.target.main, 'mouseenter', this.mouseOverHandler); lib.un(this.target.main, 'mouseleave', this.mouseLeaveHandler); Extension.prototype.inactivate.apply(this, arguments); }; var QuickTip = require('eoo').create(Extension, exports); ui.registerExtension(QuickTip); return QuickTip; }