ecui
Version:
Enterprise Classic User Interface.
1,544 lines (1,422 loc) • 391 kB
JavaScript
/*Copyright 2014 Baidu Inc. All rights reserved.*/var ecui;
(function() {
//{assign var="phases" value="define,body" delimiter=","}//
//{foreach item="item" from=$phases}//
//{assign var="phase" value=$item}//
//{include file="adapter.js"}//
//{include file="core.js"}//
//{include file="control.js"}//
//{include file="input-control.js"}//
//{include file="button.js"}//
//{include file="scrollbar.js"}//
//{include file="panel.js"}//
//{include file="items.js"}//
//{include file="checkbox.js"}//
//{include file="radio.js"}//
//{include file="select.js"}//
//{include file="combox.js"}//
//{include file="form.js"}//
//{include file="tree-view.js"}//
//{include file="month-view.js"}//
//{include file="table.js"}//
//{include file="locked-table.js"}//
//{include file="popup-menu.js"}//
//{include file="listbox.js"}//
//{include file="tab.js"}//
//{include file="decorate.js"}//
//{include file="combine.js"}//
//*{include file="label.js"}//
//*{include file="progress.js"}//
//*{include file="collection.js"}//
//*{include file="calendar.js"}//
//*{include file="format-edit.js"}//
//*{include file="radio-tree.js"}//
//*{include file="check-tree.js"}//
//*{include file="color.js"}//
//*{include file="palette.js"}//
//*{include file="multi-select.js"}//
//*{include file="locked-table.js"}//
//*{include file="messagebox.js"}//
//*{include file="shield.js"}//
//*{include file="tween.js"}//
//{/foreach}//
})();
//{if 0}//
(function() {
//{/if}//
//{if $phase == "define"}//
//__gzip_unitize__i
//__gzip_unitize__list
//__gzip_unitize__o
//__gzip_unitize__el
//__gzip_unitize__params
var core = ecui = {},
array = core.array = {},
dom = core.dom = {},
ext = core.ext = {},
json = core.json = {},
string = core.string = {},
ui = core.ui = {},
util = core.util = {};
//__gzip_original__WINDOW
///__gzip_original__DOCUMENT
//__gzip_original__DATE
//__gzip_original__FUNCTION
//__gzip_original__MATH
//__gzip_original__REGEXP
//__gzip_original__ABS
//__gzip_original__CEIL
///__gzip_original__FLOOR
///__gzip_original__MAX
///__gzip_original__MIN
//__gzip_original__POW
///__gzip_original__ROUND
///__gzip_original__PARSEINT
//__gzip_original__ISNAN
var undefined,
WINDOW = window,
DOCUMENT = document,
DATE = Date,
FUNCTION = Function,
MATH = Math,
REGEXP = RegExp,
ABS = MATH.abs,
CEIL = MATH.ceil,
FLOOR = MATH.floor,
MAX = MATH.max,
MIN = MATH.min,
POW = MATH.pow,
ROUND = MATH.round,
PARSEINT = parseInt,
ISNAN = isNaN;
var USER_AGENT = navigator.userAgent,
isStrict = DOCUMENT.compatMode == 'CSS1Compat',
ieVersion = /msie (\d+\.\d)/i.test(USER_AGENT) ? DOCUMENT.documentMode || (REGEXP.$1 - 0) : undefined,
firefoxVersion = /firefox\/(\d+\.\d)/i.test(USER_AGENT) ? REGEXP.$1 - 0 : undefined,
operaVersion = /opera\/(\d+\.\d)/i.test(USER_AGENT) ? REGEXP.$1 - 0 : undefined,
safariVersion =
/(\d+\.\d)(\.\d)?\s+safari/i.test(USER_AGENT) && !/chrome/i.test(USER_AGENT) ? REGEXP.$1 - 0 : undefined;
// 字符集基本操作定义
var charset = {
utf8: {
byteLength: function(source) {
return source.replace(/[\x80-\u07ff]/g, ' ').replace(/[\u0800-\uffff]/g, ' ').length;
},
codeLength: function(code) {
return code > 2047 ? 3 : code > 127 ? 2 : 1;
}
},
gbk: {
byteLength: function(source) {
return source.replace(/[\x80-\uffff]/g, ' ').length;
},
codeLength: function(code) {
return code > 127 ? 2 : 1;
}
},
'': {
byteLength: function(source) {
return source.length;
},
codeLength: function(code) {
return 1;
}
}
};
// 读写特殊的 css 属性操作
var styleFixer = {
display: ieVersion < 8 ? {
get: function(el, style) {
return style.display == 'inline' && style.zoom == 1 ? 'inline-block' : style.display;
},
set: function(el, value) {
if (value == 'inline-block') {
value = 'inline';
el.style.zoom = 1;
}
el.style.display = value;
}
} : firefoxVersion < 3 ? {
get: function(el, style) {
return style.display == '-moz-inline-box' ? 'inline-block' : style.display;
},
set: function(el, value) {
el.style.display = value == 'inline-block' ? '-moz-inline-box' : value;
}
} : undefined,
opacity: ieVersion ? {
get: function(el, style) {
return /alpha\(opacity=(\d+)/.test(style.filter) ? ((REGEXP.$1 - 0) / 100) + '' : '1';
},
set: function(el, value) {
el.style.filter =
el.style.filter.replace(/alpha\([^\)]*\)/gi, '') + 'alpha(opacity=' + value * 100 + ')';
}
} : undefined,
'float': ieVersion ? 'styleFloat' : 'cssFloat'
};
/**
* 查询数组中指定对象的位置序号。
* indexOf 方法返回完全匹配的对象在数组中的序号,如果在数组中找不到指定的对象,返回 -1。
* @public
*
* @param {Array} list 数组对象
* @param {Object} obj 需要查询的对象
* @return {number} 位置序号,不存在返回 -1
*/
var indexOf = array.indexOf = function(list, obj) {
for (var i = list.length; i--;) {
if (list[i] === obj) {
break;
}
}
return i;
},
/**
* 从数组中移除对象。
* @public
*
* @param {Array} list 数组对象
* @param {Object} obj 需要移除的对象
*/
remove = array.remove = function(list, obj) {
for (var i = list.length; i--;) {
if (list[i] === obj) {
list.splice(i, 1);
}
}
},
/**
* 为 Element 对象添加新的样式。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} className 样式名,可以是多个,中间使用空白符分隔
*/
addClass = dom.addClass = function(el, className) {
// 这里直接添加是为了提高效率,因此对于可能重复添加的属性,请使用标志位判断是否已经存在,
// 或者先使用 removeClass 方法删除之前的样式
el.className += ' ' + className;
},
/**
* 获取所有 parentNode 为指定 Element 的子 Element 集合。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {Array} Element 对象数组
*/
children = dom.children = function(el) {
for (var result = [], o = el.firstChild; o; o = o.nextSibling) {
if (o.nodeType == 1) {
result.push(o);
}
}
return result;
},
/**
* 判断一个 Element 对象是否包含另一个 Element 对象。
* contain 方法认为两个相同的 Element 对象相互包含。
* @public
*
* @param {HTMLElement} container 包含的 Element 对象
* @param {HTMLElement} contained 被包含的 Element 对象
* @return {boolean} contained 对象是否被包含于 container 对象的 DOM 节点上
*/
contain = dom.contain = firefoxVersion ? function(container, contained) {
return container == contained || !!(container.compareDocumentPosition(contained) & 16);
} : function(container, contained) {
return container.contains(contained);
},
/**
* 创建 Element 对象。
* @public
*
* @param {string} className 样式名称
* @param {string} cssText 样式文本
* @param {string} tagName 标签名称,默认创建一个空的 div 对象
* @return {HTMLElement} 创建的 Element 对象
*/
createDom = dom.create = function(className, cssText, tagName) {
tagName = DOCUMENT.createElement(tagName || 'DIV');
if (className) {
tagName.className = className;
}
if (cssText) {
tagName.style.cssText = cssText;
}
return tagName;
},
/**
* 获取 Element 对象的第一个子 Element 对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {HTMLElement} 子 Element 对象
*/
first = dom.first = function(el) {
return matchNode(el.firstChild, 'nextSibling');
},
/**
* 获取 Element 对象的属性值。
* 在 IE 下,Element 对象的属性可以通过名称直接访问,效率是 getAttribute 方式的两倍。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} name 属性名称
* @return {string} 属性值
*/
getAttribute = dom.getAttribute = ieVersion < 8 ? function(el, name) {
return el[name];
} : function(el, name) {
return el.getAttribute(name);
},
/**
* 获取 Element 对象的父 Element 对象。
* 在 IE 下,Element 对象被 removeChild 方法移除时,parentNode 仍然指向原来的父 Element 对象,与 W3C 标准兼容的属性应该是 parentElement。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {HTMLElement} 父 Element 对象,如果没有,返回 null
*/
getParent = dom.getParent = ieVersion ? function(el) {
return el.parentElement;
} : function(el) {
return el.parentNode;
},
/**
* 获取 Element 对象的页面位置。
* getPosition 方法将返回指定 Element 对象的位置信息。属性如下:
* left {number} X轴坐标
* top {number} Y轴坐标
* @public
*
* @param {HTMLElement} el Element 对象
* @return {Object} 位置信息
*/
getPosition = dom.getPosition = function(el) {
var top = 0,
left = 0,
body = DOCUMENT.body,
html = getParent(body);
if (ieVersion) {
if (!isStrict) {
// 在怪异模式下,IE 将 body 的边框也算在了偏移值中,需要先纠正
o = getStyle(body);
if (ISNAN(top = PARSEINT(o.borderTopWidth))) {
top = -2;
}
if (ISNAN(left = PARSEINT(o.borderLeftWidth))) {
left = -2;
}
}
o = el.getBoundingClientRect();
top += html.scrollTop + body.scrollTop - html.clientTop + FLOOR(o.top);
left += html.scrollLeft + body.scrollLeft - html.clientLeft + FLOOR(o.left);
} else if (el == body) {
top = html.scrollTop + body.scrollTop;
left = html.scrollLeft + body.scrollLeft;
} else if (el != html) {
for (o = el; o; o = o.offsetParent) {
top += o.offsetTop;
left += o.offsetLeft;
}
if (operaVersion || (/webkit/i.test(USER_AGENT) && getStyle(el, 'position') == 'absolute')) {
top -= body.offsetTop;
}
for (var o = getParent(el), style = getStyle(el); o != body; o = getParent(o), style = el) {
left -= o.scrollLeft;
if (!operaVersion) {
el = getStyle(o);
// 以下使用 html 作为临时变量
html = firefoxVersion && el.overflow != 'visible' && style.position == 'absolute' ? 2 : 1;
top += toNumber(el.borderTopWidth) * html - o.scrollTop;
left += toNumber(el.borderLeftWidth) * html;
} else if (o.tagName != 'TR') {
top -= o.scrollTop;
}
}
}
return {
top: top,
left: left
};
},
/**
* 获取 Element 对象的 CssStyle 对象或者是指定的样式值。
* getStyle 方法如果不指定样式名称,将返回 Element 对象的当前 CssStyle 对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} name 样式名称
* @return {CssStyle|Object} CssStyle 对象或样式值
*/
getStyle = dom.getStyle = function(el, name) {
var fixer = styleFixer[name],
style = el.currentStyle || (ieVersion ? el.style : getComputedStyle(el, null));
return name ? fixer && fixer.get ? fixer.get(el, style) : style[fixer || name] : style;
},
/**
* 获取 Element 对象的文本。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {string} Element 对象的文本
*/
getText = dom.getText = firefoxVersion ? function(el) {
return el.textContent;
} : function(el) {
return el.innerText;
},
/**
* 将 Element 对象插入指定的 Element 对象之后。
* 如果指定的 Element 对象没有父 Element 对象,相当于 remove 操作。
* @public
*
* @param {HTMLElement} el 被插入的 Element 对象
* @param {HTMLElement} target 目标 Element 对象
* @return {HTMLElement} 被插入的 Element 对象
*/
insertAfter = dom.insertAfter = function(el, target) {
var parent = getParent(target);
return parent ? parent.insertBefore(el, target.nextSibling) : removeDom(el);
},
/**
* 将 Element 对象插入指定的 Element 对象之前。
* 如果指定的 Element 对象没有父 Element 对象,相当于 remove 操作。
* @public
*
* @param {HTMLElement} el 被插入的 Element 对象
* @param {HTMLElement} target 目标 Element 对象
* @return {HTMLElement} 被插入的 Element 对象
*/
insertBefore = dom.insertBefore = function(el, target) {
var parent = getParent(target);
return parent ? parent.insertBefore(el, target) : removeDom(el);
},
/**
* 向指定的 Element 对象内插入一段 html 代码。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} position 插入 html 的位置信息,取值为 beforeBegin,afterBegin,beforeEnd,afterEnd
* @param {string} html 要插入的 html 代码
*/
insertHTML = dom.insertHTML = firefoxVersion ? function(el, position, html) {
var name = {
AFTERBEGIN: 'selectNodeContents',
BEFOREEND: 'selectNodeContents',
BEFOREBEGIN: 'setStartBefore',
AFTEREND: 'setEndAfter'
}[position.toUpperCase()],
range = DOCUMENT.createRange();
range[name](el);
range.collapse(position.length > 9);
range.insertNode(range.createContextualFragment(html));
} : function(el, position, html) {
el.insertAdjacentHTML(position, html);
},
/**
* 获取 Element 对象的最后一个子 Element 对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {HTMLElement} 子 Element 对象
*/
last = dom.last = function(el) {
return matchNode(el.lastChild, 'previousSibling');
},
/**
* 删除一个节点下面的所有子节点。
* @name ecui.dom.empty
* @function
* @param {HTMLElement|string} element 目标元素或目标元素的id
*
* @returns {HTMLElement} 目标元素
*/
empty = dom.empty = function(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
//todo:删除元素上绑定的事件等?
return element;
},
/**
* 将指定的 Element 对象的内容移动到目标 Element 对象中。
* @public
*
* @param {HTMLElement} source 指定的 Element 对象
* @param {HTMLElement} target 目标 Element 对象
* @param {boolean} all 是否移动所有的 DOM 对象,默认仅移动 ElementNode 类型的对象
*/
moveElements = dom.moveElements = function(source, target, all) {
//__transform__el_o
for (var el = source.firstChild; el; el = source) {
source = el.nextSibling;
if (all || el.nodeType == 1) {
target.appendChild(el);
}
}
},
/**
* 获取 Element 对象的下一个 Element 对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {HTMLElement} Element 对象
*/
next = dom.next = function(el) {
return matchNode(el.nextSibling, 'nextSibling');
},
/**
* 从页面中移除 Element 对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {HTMLElement} 被移除的 Element 对象
*/
removeDom = dom.remove = function(el) {
var parent = getParent(el);
if (parent) {
parent.removeChild(el);
}
return el;
},
/**
* 删除 Element 对象中的样式。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} className 样式名,可以是多个,中间用空白符分隔
*/
removeClass = dom.removeClass = function(el, className) {
var oldClasses = el.className.split(/\s+/).sort(),
newClasses = className.split(/\s+/).sort(),
i = oldClasses.length,
j = newClasses.length;
for (; i && j;) {
if (oldClasses[i - 1] == newClasses[j - 1]) {
oldClasses.splice(--i, 1);
} else if (oldClasses[i - 1] < newClasses[j - 1]) {
j--;
} else {
i--;
}
}
el.className = oldClasses.join(' ');
},
/**
* 设置输入框的表单项属性。
* 如果没有指定一个表单项,setInput 方法将创建一个表单项。
* @public
*
* @param {HTMLElement} el InputElement 对象
* @param {string} name 新的表单项名称,默认与 el 相同
* @param {string} type 新的表单项类型,默认为 el 相同
* @return {HTMLElement} 设置后的 InputElement 对象
*/
setInput = dom.setInput = function(el, name, type) {
if (!el) {
if (type == 'textarea') {
el = createDom('', '', 'textarea');
} else {
if (ieVersion < 9) {
return createDom('', '', '<input type="' + (type || '') + '" name="' + (name || '') + '">');
}
el = createDom('', '', 'input');
}
}
name = name === undefined ? el.name : name;
type = type === undefined ? el.type : type;
if (el.name != name || el.type != type) {
if ((ieVersion && type != 'textarea') ||
el.type != type && (el.type == 'textarea' || type == 'textarea')) {
insertHTML(
el,
'AFTEREND',
'<' + (type == 'textarea' ? 'textarea' : 'input type="' + type + '"') +
' name="' + name + '" class="' + el.className +
'" style="' + el.style.cssText + '" ' + (el.disabled ? 'disabled' : '') +
(el.readOnly ? ' readOnly' : '') + '>'
);
name = el;
(el = el.nextSibling).value = name.value;
if (type == 'radio') {
el.checked = name.checked;
}
removeDom(name);
} else {
el.type = type;
el.name = name;
}
}
return el;
},
/**
* 设置 Element 对象的样式值。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} name 样式名称
* @param {string} value 样式值
*/
setStyle = dom.setStyle = function(el, name, value) {
var fixer = styleFixer[name];
if (fixer && fixer.set) {
fixer.set(el, value);
} else {
el.style[fixer || name] = value;
}
},
/**
* 设置 Element 对象的样式值。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} css 样式名称
*/
setStyles = dom.setStyles = function(el, styles) {
for (var key in styles || {}) {
setStyle(el, key, styles[key]);
}
return el;
},
/**
* 设置 Element 对象的文本。
* @public
*
* @param {HTMLElement} el Element 对象
* @param {string} text Element 对象的文本
*/
setText = dom.setText = firefoxVersion ? function(el, text) {
el.textContent = text;
} : function(el, text) {
el.innerText = text;
},
/**
* JSON字串解析,将JSON字符串解析为JSON对象。
* @public
*
* @param {string} text json字符串
* @return {Object} json字符串描述的对象
*/
parse = json.parse = function(text) {
return new Function('return (' + text + ')')();
},
/**
* JSON对象序列化。
* @public
*
* @param {Object} source 需要序列化的对象
* @return {string} json字符串
*/
stringify = json.stringify = (function() {
//__gzip_unitize__result
//__gzip_unitize__source
var escapeMap = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"': '\\"',
'\\': '\\\\'
};
/**
* 字符串序列化。
* @private
*
* @param {string} source 需要序列化的字符串
*/
function encodeString(source) {
if (/["\\\x00-\x1f]/.test(source)) {
source = source.replace(
/["\\\x00-\x1f]/g,
function(match) {
var o = escapeMap[match];
if (o) {
return o;
}
o = match.charCodeAt();
return '\\u00' + FLOOR(o / 16) + (o % 16).toString(16);
}
);
}
return '"' + source + '"';
}
/**
* 数组序列化。
* @private
*
* @param {Array} source 需要序列化的数组
*/
function encodeArray(source) {
var i = 0,
result = [],
o,
l = source.length;
for (var i = 0, result = [], o, l = source.length; i < l; i++) {
if ((o = stringify(source[i])) !== undefined) {
result.push(o);
}
}
return '[' + result.join(',') + ']';
}
/**
* 处理日期序列化时的补零。
* @private
*
* @param {number} source 数值,小于10需要补零
*/
function pad(source) {
return source < 10 ? '0' + source : source;
}
/**
* 日期序列化。
* @private
*
* @param {Date} source 需要序列化的日期
*/
function encodeDate(source) {
return '"' + source.getFullYear() + '-' + pad(source.getMonth() + 1) + '-' +
pad(source.getDate()) + 'T' + pad(source.getHours()) + ':' +
pad(source.getMinutes()) + ':' + pad(source.getSeconds()) + '"';
}
return function(source) {
switch (typeof source) {
case 'undefined':
case 'function':
case 'unknown':
return undefined;
case 'number':
if (!isFinite(source)) {
return 'null';
}
// 对于有意义的数值与布尔类型直接输出
case 'boolean':
return source.toString();
case 'string':
return encodeString(source);
default:
if (source === null) {
return 'null';
} else if (source instanceof Array) {
return encodeArray(source);
} else if (source instanceof Date) {
return encodeDate(source);
} else {
var result = [],
o;
for (var i in source) {
if ((o = stringify(source[i])) !== undefined) {
result.push(encodeString(i) + ':' + o);
}
}
return '{' + result.join(',') + '}';
}
}
};
})(),
/**
* 对目标字符串进行 html 解码。
* @public
*
* @param {string} source 目标字符串
* @return {string} 结果字符串
*/
decodeHTML = string.decodeHTML = (function() {
var codeTable = {
quot: '"',
lt: '<',
gt: '>',
amp: '&'
};
return function(source) {
//处理转义的中文和实体字符
return source.replace(/&(quot|lt|gt|amp|#([\d]+));/g, function(match, $1, $2) {
return codeTable[$1] || String.fromCharCode(+$2);
});
};
})(),
/**
* 对目标字符串进行格式化
* @name baidu.string.format
* @function
* @grammar baidu.string.format(source, opts)
* @param {string} source 目标字符串
* @param {Object|string...} opts 提供相应数据的对象或多个字符串
* @remark
*
opts参数为“Object”时,替换目标字符串中的#{property name}部分。<br>
opts为“string...”时,替换目标字符串中的#{0}、#{1}...部分。
* @shortcut format
* @meta standard
*
* @returns {string} 格式化后的字符串
*/
format = string.format = function(source, opts) {
source = String(source);
var data = Array.prototype.slice.call(arguments, 1),
toString = Object.prototype.toString;
if (data.length) {
data = data.length == 1 ?
/* ie 下 Object.prototype.toString.call(null) == '[object Object]' */
(opts !== null && (/\[object Array\]|\[object Object\]/.test(toString.call(opts))) ? opts : data) : data;
return source.replace(/#\{(.+?)\}/g, function(match, key) {
var replacer = data[key];
// chrome 下 typeof /a/ == 'function'
if ('[object Function]' == toString.call(replacer)) {
replacer = replacer(key);
}
return ('undefined' == typeof replacer ? '' : replacer);
});
}
return source;
},
/**
* 对目标字符串按gbk编码截取字节长度
* @name ecui.string.subByte
* @function
* @param {string} source 目标字符串
* @param {number} length 需要截取的字节长度
* @param {string} [tail] 追加字符串,可选.
* @remark
* 截取过程中,遇到半个汉字时,向下取整。
* @see ecui.string.getByteLength
*
* @returns {string} 字符串截取结果
*/
subByte = string.subByte = function(source, length, tail) {
source = String(source);
tail = tail || '';
if (length < 0 || string.getByteLength(source) <= length) {
return source + tail;
}
//thanks 加宽提供优化方法
source = source.substr(0, length).replace(/([^\x00-\xff])/g, "\x241 ") //双字节字符替换成两个
.substr(0, length) //截取长度
.replace(/[^\x00-\xff]$/, "") //去掉临界双字节字符
.replace(/([^\x00-\xff]) /g, "\x241"); //还原
return source + tail;
}.
/**
* 对目标字符串进行 html 编码。
* encodeHTML 方法对四个字符进行编码,分别是 &<>"
* @public
*
* @param {string} source 目标字符串
* @return {string} 结果字符串
*/
encodeHTML = string.encodeHTML = function(source) {
return source.replace(/[&<>"']/g, function(c) {
return '&#' + c.charCodeAt(0) + ';';
});
},
/**
* 计算字符串的字节长度。
* 如果没有指定编码集,相当于获取字符串属性 length 的值。
*
* @param {string} source 目标字符串
* @param {string} charsetName 字符对应的编码集
* @return {number} 字节长度
*/
getByteLength = string.getByteLength = function(source, charsetName) {
return charset[charsetName || ''].byteLength(source);
},
/**
* 根据字节长度截取字符串。
* 如果没有指定编码集,相当于字符串的 slice 方法。
*
* @param {string} source 目标字符串
* @param {number} length 需要截取的字节长度
* @param {string} charsetName 字符对应的编码集
* @return {string} 结果字符串
*/
sliceByte = string.sliceByte = function(source, length, charsetName) {
for (var i = 0, func = charset[charsetName || ''].codeLength; i < source.length; i++) {
length -= func(source.charCodeAt(i));
if (length < 0) {
return source.slice(0, i);
}
}
return source;
},
/**
* 驼峰命名法转换。
* toCamelCase 方法将 xxx-xxx 字符串转换成 xxxXxx。
* @public
*
* @param {string} source 目标字符串
* @return {string} 结果字符串
*/
toCamelCase = string.toCamelCase = function(source) {
if (source.indexOf('-') < 0) {
return source;
}
return source.replace(/\-./g, function(match) {
return match.charAt(1).toUpperCase();
});
},
/**
* 将目标字符串中常见全角字符转换成半角字符。
*
* @param {string} source 目标字符串
* @return {string} 结果字符串
*/
toHalfWidth = string.toHalfWidth = function(source) {
return source.replace(/[\u3000\uFF01-\uFF5E]/g, function(c) {
return String.fromCharCode(MAX(c.charCodeAt(0) - 65248, 32));
});
},
/**
* 过滤字符串两端的空白字符。
* @public
*
* @param {string} source 目标字符串
* @return {string} 结果字符串
*/
trim = string.trim = function(source) {
return source && source.replace(/^\s+|\s+$/g, '');
},
/**
* 解析日期字符串
* @public
*
* @param {string} source 目标字符串
* @return {Date} 结果日期
*/
parseDate = string.parseDate = function(subject) {
if (subject instanceof Date) {
return subject;
}
var subject = subject ? new DATE(typeof subject == "number" ? subject : String(subject).replace(/-/g, "/")) : new DATE;
return subject.toString() == 'Invalid Date' ? new DATE : subject;
},
//@todo: liuronghan
addDate = string.addDate = function(subject, addFlag) {
//addDate(_oDate, "+10d");
},
/**
* 日期格式化。
* @public
*
* @param {Date} source 日期对象
* @param {string} pattern 日期格式描述字符串
* @return {string} 结果字符串
*/
formatDate = string.formatDate = function(source, pattern) {
var year = source.getFullYear(),
month = source.getMonth() + 1,
date = source.getDate(),
hours = source.getHours(),
minutes = source.getMinutes(),
seconds = source.getSeconds();
return pattern.replace(/(y+|M+|d+|H+|h+|m+|s+)/g, function(match) {
var length = match.length;
switch (match.charAt()) {
case 'y':
return length > 2 ? year : year.toString().slice(2);
case 'M':
match = month;
break;
case 'd':
match = date;
break;
case 'H':
match = hours;
break;
case 'h':
match = hours % 12;
break;
case 'm':
match = minutes;
break;
case 's':
match = seconds;
}
return length > 1 && match < 10 ? '0' + match : match;
});
},
/**
* 挂载事件。
* @public
*
* @param {Object} obj 响应事件的对象
* @param {string} type 事件类型
* @param {Function} func 事件处理函数
*/
attachEvent = util.attachEvent = ieVersion ? function(obj, type, func) {
obj.attachEvent('on' + type, func);
} : function(obj, type, func) {
obj.addEventListener(type, func, false);
},
/*
* 空函数。
* blank 方法不应该被执行,也不进行任何处理,它用于提供给不需要执行操作的事件方法进行赋值,与 blank 类似的用于给事件方法进行赋值,而不直接被执行的方法还有 cancel。
* @public
*/
blank = util.blank = function() {},
/**
* 调用指定对象超类的指定方法。
* callSuper 用于不确定超类类型时的访问,例如接口内定义的方法。请注意,接口不允许被子类实现两次,否则将会引发死循环。
* @public
*
* @param {Object} object 需要操作的对象
* @param {string} name 方法名称
* @return {Object} 超类方法的返回值
*/
callSuper = util.callSuper = function(object, name) {
/**
* 查找指定的方法对应的超类方法。
* @private
*
* @param {Object} clazz 查找的起始类对象
* @param {Function} caller 基准方法,即查找 caller 对应的超类方法
* @return {Function} 基准方法对应的超类方法,没有找到基准方法返回 undefined,基准方法没有超类方法返回 null
*/
function findPrototype(clazz, caller) {
for (; clazz; clazz = clazz.constructor.superClass) {
if (clazz[name] == caller) {
for (; clazz = clazz.constructor.superClass;) {
if (clazz[name] != caller) {
return clazz[name];
}
}
return null;
}
}
}
//__gzip_original__clazz
var clazz = object.constructor.prototype,
caller = callSuper.caller,
func = findPrototype(clazz, caller);
if (func === undefined) {
// 如果Items的方法直接位于prototype链上,是caller,如果是间接被别的方法调用Items.xxx.call,是caller.caller
func = findPrototype(clazz, caller.caller);
}
if (func) {
return func.apply(object, caller.arguments);
}
},
/*
* 返回 false。
* cancel 方法不应该被执行,它每次返回 false,用于提供给需要返回逻辑假操作的事件方法进行赋值,例如需要取消默认事件操作的情况,与 cancel 类似的用于给事件方法进行赋值,而不直接被执行的方法还有 blank。
* @public
*
* @return {boolean} false
*/
cancel = util.cancel = function() {
return false;
},
/**
* 卸载事件。
* @public
*
* @param {Object} obj 响应事件的对象
* @param {string} type 事件类型
* @param {Function} func 事件处理函数
*/
detachEvent = util.detachEvent = ieVersion ? function(obj, type, func) {
obj.detachEvent('on' + type, func);
} : function(obj, type, func) {
obj.removeEventListener(type, func, false);
},
/**
* 对象属性复制。
* @public
*
* @param {Object} target 目标对象
* @param {Object} source 源对象
* @return {Object} 目标对象
*/
extend = util.extend = function(target, source) {
for (var key in source) {
target[key] = source[key];
}
return target;
},
/**
* 获取浏览器可视区域的相关信息。
* getView 方法将返回浏览器可视区域的信息。属性如下:
* top {number} 可视区域最小X轴坐标
* right {number} 可视区域最大Y轴坐标
* bottom {number} 可视区域最大X轴坐标
* left {number} 可视区域最小Y轴坐标
* width {number} 可视区域的宽度
* height {number} 可视区域的高度
* pageWidth {number} 页面的宽度
* pageHeight {number} 页面的高度
* @public
*
* @return {Object} 浏览器可视区域信息
*/
getView = util.getView = function() {
//__gzip_original__clientWidth
//__gzip_original__clientHeight
var body = DOCUMENT.body,
html = getParent(body),
client = isStrict ? html : body,
scrollTop = html.scrollTop + body.scrollTop,
scrollLeft = html.scrollLeft + body.scrollLeft,
clientWidth = client.clientWidth,
clientHeight = client.clientHeight;
return {
top: scrollTop,
right: scrollLeft + clientWidth,
bottom: scrollTop + clientHeight,
left: scrollLeft,
width: clientWidth,
height: clientHeight,
pageWidth: MAX(html.scrollWidth, body.scrollWidth, clientWidth),
pageHeight: MAX(html.scrollHeight, body.scrollHeight, clientHeight)
};
},
/**
* 类继承。
* @public
*
* @param {Function} subClass 子类
* @param {Function} superClass 父类
* @return {Object} subClass 的 prototype 属性
*/
inherits = util.inherits = function(subClass, superClass) {
var oldPrototype = subClass.prototype,
clazz = new FUNCTION();
clazz.prototype = superClass.prototype;
extend(subClass.prototype = new clazz(), oldPrototype);
subClass.prototype.constructor = subClass;
subClass.superClass = superClass.prototype;
return subClass.prototype;
},
/**
* 设置缺省的属性值。
* 如果对象的属性已经被设置,setDefault 方法不进行任何处理,否则将默认值设置到指定的属性上。
* @public
*
* @param {Object} obj 被设置的对象
* @param {string} key 属性名
* @param {Object} value 属性的默认值
*/
setDefault = util.setDefault = function(obj, key, value) {
if (!obj.hasOwnProperty(key)) {
obj[key] = value;
}
},
/**
* 创建一个定时器对象。
* @public
*
* @param {Function} func 定时器需要调用的函数
* @param {number} delay 定时器延迟调用的毫秒数,如果为负数表示需要连续触发
* @param {Object} caller 调用者,在 func 被执行时,this 指针指向的对象,可以为空
* @param {Object} ... 向 func 传递的参数
* @return {Function} 用于关闭定时器的方法
*/
timer = util.timer = function(func, delay, caller) {
function build() {
return (delay < 0 ? setInterval : setTimeout)(function() {
func.apply(caller, args);
// 使用delay<0而不是delay>=0,是防止delay没有值的时候,不进入分支
if (!(delay < 0)) {
func = caller = args = null;
}
}, ABS(delay));
}
var args = Array.prototype.slice.call(arguments, 3),
handle = build(),
pausing;
/**
* 中止定时调用。
* @public
*
* @param {boolean} pause 是否暂时停止定时器,如果参数是 true,再次调用函数并传入参数 true 恢复运行。
*/
return function(pause) {
(delay < 0 ? clearInterval : clearTimeout)(handle);
if (pause) {
if (pausing) {
handle = build();
}
pausing = !pausing;
} else {
func = caller = args = null;
}
};
},
/**
* 将对象转换成数值。
* toNumber 方法会省略数值的符号,例如字符串 9px 将当成数值的 9,不能识别的数值将默认为 0。
* @public
*
* @param {Object} obj 需要转换的对象
* @return {number} 对象的数值
*/
toNumber = util.toNumber = function(obj) {
return PARSEINT(obj) || 0;
},
/**
* 设置页面加载完毕后自动执行的方法。
* @public
*
* @param {Function} func 需要自动执行的方法
*/
ready = dom.ready = (function() {
var hasReady = false,
list = [],
check,
numStyles;
function ready() {
if (!hasReady) {
hasReady = true;
for (var i = 0, o; o = list[i++];) {
o();
}
}
}
if (DOCUMENT.addEventListener && !operaVersion) {
DOCUMENT.addEventListener('DOMContentLoaded', ready, false);
} else if (ieVersion && WINDOW == top) {
check = function() {
try {
DOCUMENT.documentElement.doScroll('left');
ready();
} catch (e) {
timer(check, 0);
}
};
} else if (safariVersion) {
check = function() {
var i = 0,
list,
o = DOCUMENT.readyState;
if (o != 'loaded' && o != 'complete') {
timer(check, 0);
} else {
if (numStyles === undefined) {
numStyles = 0;
if (list = DOCUMENT.getElementsByTagName('style')) {
numStyles += list.length;
}
if (list = DOCUMENT.getElementsByTagName('link')) {
for (; o = list[i++];) {
if (getAttribute(o, 'rel') == 'stylesheet') {
numStyles++;
}
}
}
}
if (DOCUMENT.styleSheets.length != numStyles) {
timer(check, 0);
} else {
ready();
}
}
};
}
if (check) {
check();
}
attachEvent(WINDOW, 'load', ready);
return function(func) {
if (hasReady) {
func();
} else {
list.push(func);
}
};
})();
//{else}//
/**
* 获取 Element 对象指定位置的 Element 对象。
* @private
*
* @param {HTMLElement} el Element 对象
* @param {string} direction Element 对象遍历的属性
* @return {HTMLElement} 指定位置的 Element 对象
*/
function matchNode(el, direction) {
for (; el; el = el[direction]) {
if (el.nodeType == 1) {
break;
}
}
return el;
}
try {
DOCUMENT.execCommand("BackgroundImageCache", false, true);
} catch (e) {}
//{/if}//
//{if 0}//
})();
//{/if}//
//{if 0}//
(function() {
var core = ecui,
array = core.array,
dom = core.dom,
ext = core.ext,
string = core.string,
util = core.util,
ui = core.ui,
undefined,
WINDOW = window,
DOCUMENT = document,
DATE = Date,
MATH = Math,
REGEXP = RegExp,
ABS = MATH.abs,
MAX = MATH.max,
MIN = MATH.min,
ISNAN = isNaN,
USER_AGENT = navigator.userAgent,
isStrict = DOCUMENT.compatMode == 'CSS1Compat',
ieVersion = /msie (\d+\.\d)/i.test(USER_AGENT) ? DOCUMENT.documentMode || (REGEXP.$1 - 0) : undefined,
firefoxVersion = /firefox\/(\d+\.\d)/i.test(USER_AGENT) ? REGEXP.$1 - 0 : undefined,
indexOf = array.indexOf,
remove = array.remove,
addClass = dom.addClass,
contain = dom.contain,
createDom = dom.create,
getAttribute = dom.getAttribute,
getParent = dom.getParent,
getPosition = dom.getPosition,
getStyle = dom.getStyle,
insertHTML = dom.insertHTML,
ready = dom.ready,
removeDom = dom.remove,
removeClass = dom.removeClass,
setStyle = dom.setStyle,
toCamelCase = string.toCamelCase,
attachEvent = util.attachEvent,
blank = util.blank,
detachEvent = util.detachEvent,
extend = util.extend,
getView = util.getView,
inherits = util.inherits,
timer = util.timer,
toNumber = util.toNumber;
//{/if}//
//{if $phase == "define"}//
var NORMAL = core.NORMAL = 0,
LOADING = core.LOADING = 1,
REPAINT = core.REPAINT = 2;
//__gzip_unitize__event
var $bind,
$connect,
$clearState,
$create,
$fastCreate,
calcHeightRevise,
calcLeftRevise,
calcTopRevise,
calcWidthRevise,
createControl,
disposeControl,
drag,
/**
* 从指定的 Element 对象开始,依次向它的父节点查找绑定的 ECUI 控件。
* findControl 方法,会返回从当前 Element 对象开始,依次向它的父 Element 查找到的第一个绑定(参见 $bind 方法)的 ECUI 控件。findControl 方法一般在控件创建时使用,用于查找父控件对象。
* @public
*
* @param {HTMLElement} el Element 对象
* @return {ecui.ui.Control} ECUI 控件对象,如果不能找到,返回 null
*/
findControl = core.findControl = function(el) {
for (; el; el = getParent(el)) {
if (el.getControl) {
return el.getControl();
}
}
return null;
},
getActived,
getAttributeName,
getFocused,
getHovered,
getKey,
getMouseX,
getMouseY,
getOptions,
getScrollNarrow,
getStatus,
inheritsControl,
intercept,
isContentBox,
loseFocus,
mask,
needInitClass,
query,
restore,
setFocused,
triggerEvent,
wrapEvent,
eventNames = [
'mousedown', 'mouseover', 'mousemove', 'mouseout', 'mouseup',
'click', 'dblclick', 'focus', 'blur', 'activate', 'deactivate',
'keydown', 'keypress', 'keyup', 'mousewheel'
];
(function() {
/**
* 创建 ECUI 事件对象。
* @public
*
* @param {string} type 事件类型
* @param {Event} event 浏览器原生事件对象,忽略将自动填充
*/
///__gzip_original__UI_EVENT_CLASS
var UI_EVENT = ui.Event = function(type, event) {
this.type = type;
if (event) {
this.pageX = event.pageX;
this.pageY = event.pageY;
this.which = event.which;
this.target = event.target;
this._oNative = event;
} else {
this.pageX = mouseX;
this.pageY = mouseY;
this.which = keyCode;
this.target = DOCUMENT;
}
},
UI_EVENT_CLASS = UI_EVENT.prototype,
ecuiName = 'ecui', // Element 中用于自动渲染的 ecui 属性名称
isGlobalId, // 是否自动将 ecui 的标识符全局化
structural, // DOM结构生成的方式,0表示填充所有内容,1表示不填充控件的class,2表示完全不填充
flgContentBox, // 在计算宽度与高度时,是否需要修正内填充与边框样式的影响
flgFixedOffset, // 在计算相对位置时,是否需要修正边框样式的影响
scrollNarrow, // 浏览器滚动条相对窄的一边的长度
initRecursion = 0, // init 操作的递归次数
lastClientWidth, // 浏览器之前的宽度
plugins = {}, // 扩展组件列表
maskElements = [], // 遮罩层组
mouseX, // 当前鼠标光标的X轴坐标
mouseY, // 当前鼠标光标的Y轴坐标
keyCode = 0, // 当前键盘按下的键值,解决keypress与keyup中得不到特殊按键的keyCode的问题
lastClick, // 上一次产生点击事件的信息
status, // 框架当前状态
allControls = [], // 全部生成的控件,供释放控件占用的内存使用
independentControls = [], // 独立的控件,即使用create($create)方法创建的控件
namedControls, // 所有被命名的控件的集合
uniqueIndex = 0, // 控件的唯一序号
connectedControls = {}, // 等待关联的控件集合
activedControl, // 当前环境下被激活的控件,即鼠标左键按下时对应的控件,直到左键松开后失去激活状态
hoveredControl, // 当前环境下鼠标悬停的控件
focusedControl, // 当前环境下拥有焦点的控件
eventListeners = {}, // 控件事件监听描述对象
envStack = [], // 高优先级事件调用时,保存上一个事件环境的栈
currEnv = { // 当前操作的环境
// 鼠标点击时控件如果被屏弊需要取消点击事件的默认处理,此时链接将不能提交
click: function(event) {
event = wrapEvent(event);
//__transform__control_o
var control = findControl(event.target);
if (control && control.isDisabled()) {
event.preventDefault();
}
},
// 鼠标左键按下需要改变框架中拥有焦点的控件
mousedown: function(event) {
if (activedControl) {
// 如果按下鼠标左键后,使用ALT+TAB使浏览器失去焦点然后松开鼠标左键,
// 需要恢复激活控件状态,第一次点击失效
bubble(activedControl, 'deactivate