/** * Sea.js 2.2.1 | seajs.org/LICENSE.md */ (function(global, undefined) { // Avoid conflicting when `sea.js` is loaded multiple times if (global.seajs) { return } var seajs = global.seajs = { // The current version of Sea.js being used version: "2.2.1" } var data = seajs.data = {} /** * util-lang.js - The minimal language enhancement */ function isType(type) { return function(obj) { return {}.toString.call(obj) == "[object " + type + "]" } } var isObject = isType("Object") var isString = isType("String") var isArray = Array.isArray || isType("Array") var isFunction = isType("Function") var _cid = 0 function cid() { return _cid++ } /** * util-events.js - The minimal events support */ var events = data.events = {} // Bind event seajs.on = function(name, callback) { var list = events[name] || (events[name] = []) list.push(callback) return seajs } // Remove event. If `callback` is undefined, remove all callbacks for the // event. If `event` and `callback` are both undefined, remove all callbacks // for all events seajs.off = function(name, callback) { // Remove *all* events if (!(name || callback)) { events = data.events = {} return seajs } var list = events[name] if (list) { if (callback) { for (var i = list.length - 1; i >= 0; i--) { if (list[i] === callback) { list.splice(i, 1) } } } else { delete events[name] } } return seajs } // Emit event, firing all bound callbacks. Callbacks receive the same // arguments as `emit` does, apart from the event name var emit = seajs.emit = function(name, data) { var list = events[name], fn if (list) { // Copy callback lists to prevent modification list = list.slice() // Execute event callbacks while ((fn = list.shift())) { fn(data) } } return seajs } /** * util-path.js - The utilities for operating path such as id, uri */ var DIRNAME_RE = /[^?#]*\// var DOT_RE = /\/\.\//g var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\// var DOUBLE_SLASH_RE = /([^:/])\/\//g // Extract the directory portion of a path // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/" // ref: http://jsperf.com/regex-vs-split/2 function dirname(path) { return path.match(DIRNAME_RE)[0] } // Canonicalize a path // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c" function realpath(path) { // /a/b/./c/./d ==> /a/b/c/d path = path.replace(DOT_RE, "/") // a/b/c/../../d ==> a/b/../d ==> a/d while (path.match(DOUBLE_DOT_RE)) { path = path.replace(DOUBLE_DOT_RE, "/") } // a//b/c ==> a/b/c path = path.replace(DOUBLE_SLASH_RE, "$1/") return path } // Normalize an id // normalize("path/to/a") ==> "path/to/a.js" // NOTICE: substring is faster than negative slice and RegExp function normalize(path) { var last = path.length - 1 var lastC = path.charAt(last) // If the uri ends with `#`, just return it without '#' if (lastC === "#") { return path.substring(0, last) } return (path.substring(last - 2) === ".js" || path.indexOf("?") > 0 || path.substring(last - 3) === ".css" || lastC === "/") ? path : path + ".js" } var PATHS_RE = /^([^/:]+)(\/.+)$/ var VARS_RE = /{([^{]+)}/g function parseAlias(id) { var alias = data.alias return alias && isString(alias[id]) ? alias[id] : id } function parsePaths(id) { var paths = data.paths var m if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) { id = paths[m[1]] + m[2] } return id } function parseVars(id) { var vars = data.vars if (vars && id.indexOf("{") > -1) { id = id.replace(VARS_RE, function(m, key) { return isString(vars[key]) ? vars[key] : m }) } return id } function parseMap(uri) { var map = data.map var ret = uri if (map) { for (var i = 0, len = map.length; i < len; i++) { var rule = map[i] ret = isFunction(rule) ? (rule(uri) || uri) : uri.replace(rule[0], rule[1]) // Only apply the first matched rule if (ret !== uri) break } } return ret } var ABSOLUTE_RE = /^\/\/.|:\// var ROOT_DIR_RE = /^.*?\/\/.*?\// function addBase(id, refUri) { var ret var first = id.charAt(0) // Absolute if (ABSOLUTE_RE.test(id)) { ret = id } // Relative else if (first === ".") { ret = realpath((refUri ? dirname(refUri) : data.cwd) + id) } // Root else if (first === "/") { var m = data.cwd.match(ROOT_DIR_RE) ret = m ? m[0] + id.substring(1) : id } // Top-level else { ret = data.base + id } // Add default protocol when uri begins with "//" if (ret.indexOf("//") === 0) { ret = location.protocol + ret } return ret } function id2Uri(id, refUri) { if (!id) return "" id = parseAlias(id) id = parsePaths(id) id = parseVars(id) id = normalize(id) var uri = addBase(id, refUri) uri = parseMap(uri) return uri } var doc = document var cwd = dirname(doc.URL) var scripts = doc.scripts // Recommend to add `seajsnode` id for the `sea.js` script element var loaderScript = doc.getElementById("seajsnode") || scripts[scripts.length - 1] // When `sea.js` is inline, set loaderDir to current working directory var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd) function getScriptAbsoluteSrc(node) { return node.hasAttribute ? // non-IE6/7 node.src : // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx node.getAttribute("src", 4) } // For Developers seajs.resolve = id2Uri /** * util-request.js - The utilities for requesting script and style files * ref: tests/research/load-js-css/test.html */ var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement var baseElement = head.getElementsByTagName("base")[0] var IS_CSS_RE = /\.css(?:\?|$)/i var currentlyAddingScript var interactiveScript // `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0 // ref: // - https://bugs.webkit.org/show_activity.cgi?id=38995 // - https://bugzilla.mozilla.org/show_bug.cgi?id=185236 // - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events var isOldWebKit = +navigator.userAgent .replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+).*/, "$1") < 536 function request(url, callback, charset) { var isCSS = IS_CSS_RE.test(url) var node = doc.createElement(isCSS ? "link" : "script") if (charset) { var cs = isFunction(charset) ? charset(url) : charset if (cs) { node.charset = cs } } addOnload(node, callback, isCSS, url) if (isCSS) { node.rel = "stylesheet" node.href = url } else { node.async = true node.src = url } // For some cache cases in IE 6-8, the script executes IMMEDIATELY after // the end of the insert execution, so use `currentlyAddingScript` to // hold current node, for deriving url in `define` call currentlyAddingScript = node // ref: #185 & http://dev.jquery.com/ticket/2709 baseElement ? head.insertBefore(node, baseElement) : head.appendChild(node) currentlyAddingScript = null } function addOnload(node, callback, isCSS, url) { var supportOnload = "onload" in node // for Old WebKit and Old Firefox if (isCSS && (isOldWebKit || !supportOnload)) { setTimeout(function() { pollCss(node, callback) }, 1) // Begin after node insertion return } if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) onload() } } else { node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } } function onload() { // Ensure only run once and handle memory leak in IE node.onload = node.onerror = node.onreadystatechange = null // Remove the script to reduce memory leak if (!isCSS && !data.debug) { head.removeChild(node) } // Dereference the node node = null callback() } } function pollCss(node, callback) { var sheet = node.sheet var isLoaded // for WebKit < 536 if (isOldWebKit) { if (sheet) { isLoaded = true } } // for Firefox < 9.0 else if (sheet) { try { if (sheet.cssRules) { isLoaded = true } } catch (ex) { // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR" // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0 // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR" if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") { isLoaded = true } } } setTimeout(function() { if (isLoaded) { // Place callback here to give time for style rendering callback() } else { pollCss(node, callback) } }, 20) } function getCurrentScript() { if (currentlyAddingScript) { return currentlyAddingScript } // For IE6-9 browsers, the script onload event may not fire right // after the script is evaluated. Kris Zyp found that it // could query the script nodes and the one that is in "interactive" // mode indicates the current script // ref: http://goo.gl/JHfFW if (interactiveScript && interactiveScript.readyState === "interactive") { return interactiveScript } var scripts = head.getElementsByTagName("script") for (var i = scripts.length - 1; i >= 0; i--) { var script = scripts[i] if (script.readyState === "interactive") { interactiveScript = script return interactiveScript } } } // For Developers seajs.request = request /** * util-deps.js - The parser for dependencies * ref: tests/research/parse-dependencies/test.html */ var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g var SLASH_RE = /\\\\/g function parseDependencies(code) { var ret = [] code.replace(SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { ret.push(m2) } }) return ret } /** * module.js - The core of module loader */ var cachedMods = seajs.cache = {} var anonymousMeta var fetchingList = {} var fetchedList = {} var callbackList = {} var STATUS = Module.STATUS = { // 1 - The `module.uri` is being fetched FETCHING: 1, // 2 - The meta data has been saved to cachedMods SAVED: 2, // 3 - The `module.dependencies` are being loaded LOADING: 3, // 4 - The module are ready to execute LOADED: 4, // 5 - The module is being executed EXECUTING: 5, // 6 - The `module.exports` is available EXECUTED: 6 } function Module(uri, deps) { this.uri = uri this.dependencies = deps || [] this.exports = null this.status = 0 // Who depends on me this._waitings = {} // The number of unloaded dependencies this._remain = 0 } // Resolve module.dependencies Module.prototype.resolve = function() { var mod = this var ids = mod.dependencies var uris = [] for (var i = 0, len = ids.length; i < len; i++) { uris[i] = Module.resolve(ids[i], mod.uri) } return uris } // Load module.dependencies and fire onload when all done Module.prototype.load = function() { var mod = this // If the module is being loaded, just wait it onload call if (mod.status >= STATUS.LOADING) { return } mod.status = STATUS.LOADING // Emit `load` event for plugins such as combo plugin var uris = mod.resolve() emit("load", uris) var len = mod._remain = uris.length var m // Initialize modules and register waitings for (var i = 0; i < len; i++) { m = Module.get(uris[i]) if (m.status < STATUS.LOADED) { // Maybe duplicate: When module has dupliate dependency, it should be it's count, not 1 m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1 } else { mod._remain-- } } if (mod._remain === 0) { mod.onload() return } // Begin parallel loading var requestCache = {} for (i = 0; i < len; i++) { m = cachedMods[uris[i]] if (m.status < STATUS.FETCHING) { m.fetch(requestCache) } else if (m.status === STATUS.SAVED) { m.load() } } // Send all requests at last to avoid cache bug in IE6-9. Issues#808 for (var requestUri in requestCache) { if (requestCache.hasOwnProperty(requestUri)) { requestCache[requestUri]() } } } // Call this method when module is loaded Module.prototype.onload = function() { var mod = this mod.status = STATUS.LOADED if (mod.callback) { mod.callback() } // Notify waiting modules to fire onload var waitings = mod._waitings var uri, m for (uri in waitings) { if (waitings.hasOwnProperty(uri)) { m = cachedMods[uri] m._remain -= waitings[uri] if (m._remain === 0) { m.onload() } } } // Reduce memory taken delete mod._waitings delete mod._remain } // Fetch a module Module.prototype.fetch = function(requestCache) { var mod = this var uri = mod.uri mod.status = STATUS.FETCHING // Emit `fetch` event for plugins such as combo plugin var emitData = { uri: uri } emit("fetch", emitData) var requestUri = emitData.requestUri || uri // Empty uri or a non-CMD module if (!requestUri || fetchedList[requestUri]) { mod.load() return } if (fetchingList[requestUri]) { callbackList[requestUri].push(mod) return } fetchingList[requestUri] = true callbackList[requestUri] = [mod] // Emit `request` event for plugins such as text plugin emit("request", emitData = { uri: uri, requestUri: requestUri, onRequest: onRequest, charset: data.charset }) if (!emitData.requested) { requestCache ? requestCache[emitData.requestUri] = sendRequest : sendRequest() } function sendRequest() { seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset) } function onRequest() { delete fetchingList[requestUri] fetchedList[requestUri] = true // Save meta data of anonymous module if (anonymousMeta) { Module.save(uri, anonymousMeta) anonymousMeta = null } // Call callbacks var m, mods = callbackList[requestUri] delete callbackList[requestUri] while ((m = mods.shift())) m.load() } } // Execute a module Module.prototype.exec = function () { var mod = this // When module is executed, DO NOT execute it again. When module // is being executed, just return `module.exports` too, for avoiding // circularly calling if (mod.status >= STATUS.EXECUTING) { return mod.exports } mod.status = STATUS.EXECUTING // Create require var uri = mod.uri function require(id) { return Module.get(require.resolve(id)).exec() } require.resolve = function(id) { return Module.resolve(id, uri) } require.async = function(ids, callback) { Module.use(ids, callback, uri + "_async_" + cid()) return require } // Exec factory var factory = mod.factory var exports = isFunction(factory) ? factory(require, mod.exports = {}, mod) : factory if (exports === undefined) { exports = mod.exports } // Reduce memory leak delete mod.factory mod.exports = exports mod.status = STATUS.EXECUTED // Emit `exec` event emit("exec", mod) return exports } // Resolve id to uri Module.resolve = function(id, refUri) { // Emit `resolve` event for plugins such as text plugin var emitData = { id: id, refUri: refUri } emit("resolve", emitData) return emitData.uri || seajs.resolve(emitData.id, refUri) } // Define a module Module.define = function (id, deps, factory) { var argsLen = arguments.length // define(factory) if (argsLen === 1) { factory = id id = undefined } else if (argsLen === 2) { factory = deps // define(deps, factory) if (isArray(id)) { deps = id id = undefined } // define(id, factory) else { deps = undefined } } // Parse dependencies according to the module factory code if (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(factory.toString()) } var meta = { id: id, uri: Module.resolve(id), deps: deps, factory: factory } // Try to derive uri in IE6-9 for anonymous modules if (!meta.uri && doc.attachEvent) { var script = getCurrentScript() if (script) { meta.uri = script.src } // NOTE: If the id-deriving methods above is failed, then falls back // to use onload event to get the uri } // Emit `define` event, used in nocache plugin, seajs node version etc emit("define", meta) meta.uri ? Module.save(meta.uri, meta) : // Save information for "saving" work in the script onload event anonymousMeta = meta } // Save meta data to cachedMods Module.save = function(uri, meta) { var mod = Module.get(uri) // Do NOT override already saved modules if (mod.status < STATUS.SAVED) { mod.id = meta.id || uri mod.dependencies = meta.deps || [] mod.factory = meta.factory mod.status = STATUS.SAVED } } // Get an existed module or create a new one Module.get = function(uri, deps) { return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps)) } // Use function is equal to load a anonymous module Module.use = function (ids, callback, uri) { var mod = Module.get(uri, isArray(ids) ? ids : [ids]) mod.callback = function() { var exports = [] var uris = mod.resolve() for (var i = 0, len = uris.length; i < len; i++) { exports[i] = cachedMods[uris[i]].exec() } if (callback) { callback.apply(global, exports) } delete mod.callback } mod.load() } // Load preload modules before all other modules Module.preload = function(callback) { var preloadMods = data.preload var len = preloadMods.length if (len) { Module.use(preloadMods, function() { // Remove the loaded preload modules preloadMods.splice(0, len) // Allow preload modules to add new preload modules Module.preload(callback) }, data.cwd + "_preload_" + cid()) } else { callback() } } // Public API seajs.use = function(ids, callback) { Module.preload(function() { Module.use(ids, callback, data.cwd + "_use_" + cid()) }) return seajs } Module.define.cmd = {} global.define = Module.define // For Developers seajs.Module = Module data.fetchedList = fetchedList data.cid = cid seajs.require = function(id) { var mod = Module.get(Module.resolve(id)) if (mod.status < STATUS.EXECUTING) { mod.onload() mod.exec() } return mod.exports } /** * config.js - The configuration for the loader */ var BASE_RE = /^(.+?\/)(\?\?)?(seajs\/)+/ // The root path to use for id2uri parsing // If loaderUri is `http://test.com/libs/seajs/[??][seajs/1.2.3/]sea.js`, the // baseUri should be `http://test.com/libs/` data.base = (loaderDir.match(BASE_RE) || ["", loaderDir])[1] // The loader directory data.dir = loaderDir // The current working directory data.cwd = cwd // The charset for requesting files data.charset = "utf-8" // Modules that are needed to load before all other modules data.preload = (function() { var plugins = [] // Convert `seajs-xxx` to `seajs-xxx=1` // NOTE: use `seajs-xxx=1` flag in uri or cookie to preload `seajs-xxx` var str = location.search.replace(/(seajs-\w+)(&|$)/g, "$1=1$2") // Add cookie string str += " " + doc.cookie // Exclude seajs-xxx=0 str.replace(/(seajs-\w+)=1/g, function(m, name) { plugins.push(name) }) return plugins })() // data.alias - An object containing shorthands of module id // data.paths - An object containing path shorthands in module id // data.vars - The {xxx} variables in module id // data.map - An array containing rules to map module uri // data.debug - Debug mode. The default value is false seajs.config = function(configData) { for (var key in configData) { var curr = configData[key] var prev = data[key] // Merge object config such as alias, vars if (prev && isObject(prev)) { for (var k in curr) { prev[k] = curr[k] } } else { // Concat array config such as map, preload if (isArray(prev)) { curr = prev.concat(curr) } // Make sure that `data.base` is an absolute path else if (key === "base") { // Make sure end with "/" if (curr.slice(-1) !== "/") { curr += "/" } curr = addBase(curr) } // Set config data[key] = curr } } emit("config", configData) return seajs } })(this); ;(function() { var bui_config_110_config_debug; bui_config_110_config_debug = function () { //from seajs function getScriptAbsoluteSrc(node) { return node.hasAttribute ? // non-IE6/7 node.src : // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx node.getAttribute('src', 4); } var BUI = window.BUI = window.BUI || {}; BUI.use = seajs.use; BUI.config = seajs.config; var scripts = document.getElementsByTagName('script'), loaderScript = scripts[scripts.length - 1], src = getScriptAbsoluteSrc(loaderScript), loaderPath = src.substring(0, src.lastIndexOf('/')), // 不能用data 因为在把包的时候会把data替换成data debug = loaderScript.getAttribute('debug') === 'true' ? true : false; BUI.loaderScript = loaderScript; //配置bui的路径 seajs.config({ paths: { 'bui': loaderPath } }); BUI.setDebug = function (debug) { BUI.debug = debug; //只有bui目录下面的文件使用-min.js var regexp = new RegExp('^(' + loaderPath + '\\S*).js$'); if (!debug) { seajs.config({ map: [[ regexp, '$1-min.js' ]] }); } else { var map = seajs.data.map; var mapReg; if (!map) { return; } for (var i = map.length - 1; i >= 0; i--) { mapReg = map[i][0]; if (Object.prototype.toString.call(mapReg) === '[object RegExp]' && mapReg.toString() === regexp.toString()) { map.splice(i, 1); } } } }; BUI.setDebug(debug); // 所有的模块都是依赖于jquery, 所以定义一个jquery的模块,并直接返回 if (window.jQuery) { window.define('jquery', [], function () { return window.jQuery; }); } }(); }()); define("bui/common", ["jquery"], function(require, exports, module){ var BUI = require("bui/common/util"); BUI.mix(BUI, { UA: require("bui/common/ua"), JSON: require("bui/common/json"), Date: require("bui/common/date"), Array: require("bui/common/array"), KeyCode: require("bui/common/keycode"), Observable: require("bui/common/observable"), Base: require("bui/common/base"), Component: require("bui/common/component/component") }); module.exports = BUI; }); define("bui/common/util", ["jquery"], function(require, exports, module){ /** * @class BUI * 控件库的工具方法,这些工具方法直接绑定到BUI对象上 *
* BUI.isString(str);
*
* BUI.extend(A,B);
*
* BUI.mix(A,{a:'a'});
*
* @singleton
*/
var $ = require("jquery");
//兼容jquery 1.6以下
(function($) {
if ($.fn) {
$.fn.on = $.fn.on || $.fn.bind;
$.fn.off = $.fn.off || $.fn.unbind;
}
})($);
/**
* @ignore
* 处于效率的目的,复制属性
*/
function mixAttrs(to, from) {
for (var c in from) {
if (from.hasOwnProperty(c)) {
to[c] = to[c] || {};
mixAttr(to[c], from[c]);
}
}
}
//合并属性
function mixAttr(attr, attrConfig) {
for (var p in attrConfig) {
if (attrConfig.hasOwnProperty(p)) {
if (p == 'value') {
if (BUI.isObject(attrConfig[p])) {
attr[p] = attr[p] || {};
BUI.mix( /*true,*/ attr[p], attrConfig[p]);
} else if (BUI.isArray(attrConfig[p])) {
attr[p] = attr[p] || [];
//BUI.mix(/*true,*/attr[p], attrConfig[p]);
attr[p] = attr[p].concat(attrConfig[p]);
} else {
attr[p] = attrConfig[p];
}
} else {
attr[p] = attrConfig[p];
}
}
};
}
var win = window,
doc = document,
objectPrototype = Object.prototype,
toString = objectPrototype.toString,
BODY = 'body',
DOC_ELEMENT = 'documentElement',
SCROLL = 'scroll',
SCROLL_WIDTH = SCROLL + 'Width',
SCROLL_HEIGHT = SCROLL + 'Height',
ATTRS = 'ATTRS',
PARSER = 'PARSER',
GUID_DEFAULT = 'guid';
window.BUI = window.BUI || {};
$.extend(BUI, {
/**
* 版本号
* @memberOf BUI
* @type {Number}
*/
version: '1.1.0',
/**
* 是否为函数
* @param {*} fn 对象
* @return {Boolean} 是否函数
*/
isFunction: function(fn) {
return typeof(fn) === 'function';
},
/**
* 是否数组
* @method
* @param {*} obj 是否数组
* @return {Boolean} 是否数组
*/
isArray: ('isArray' in Array) ? Array.isArray : function(value) {
return toString.call(value) === '[object Array]';
},
/**
* 是否日期
* @param {*} value 对象
* @return {Boolean} 是否日期
*/
isDate: function(value) {
return toString.call(value) === '[object Date]';
},
/**
* 是否是javascript对象
* @param {Object} value The value to test
* @return {Boolean}
* @method
*/
isObject: (toString.call(null) === '[object Object]') ?
function(value) {
// check ownerDocument here as well to exclude DOM nodes
return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
} : function(value) {
return toString.call(value) === '[object Object]';
},
/**
* 是否是数字或者数字字符串
* @param {String} value 数字字符串
* @return {Boolean} 是否是数字或者数字字符串
*/
isNumeric: function(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
},
/**
* 将指定的方法或属性放到构造函数的原型链上,
* 函数支持多于2个变量,后面的变量同s1一样将其成员复制到构造函数的原型链上。
* @param {Function} r 构造函数
* @param {Object} s1 将s1 的成员复制到构造函数的原型链上
* @example
* BUI.augment(class1,{
* method1: function(){
*
* }
* });
*/
augment: function(r, s1) {
if (!BUI.isFunction(r)) {
return r;
}
for (var i = 1; i < arguments.length; i++) {
BUI.mix(r.prototype, arguments[i].prototype || arguments[i]);
};
return r;
},
/**
* 拷贝对象
* @param {Object} obj 要拷贝的对象
* @return {Object} 拷贝生成的对象
*/
cloneObject: function(obj) {
var result = BUI.isArray(obj) ? [] : {};
return BUI.mix(true, result, obj);
},
/**
* 抛出错误
*/
error: function(msg) {
if (BUI.debug) {
throw msg;
}
},
/**
* 实现类的继承,通过父类生成子类
* @param {Function} subclass
* @param {Function} superclass 父类构造函数
* @param {Object} overrides 子类的属性或者方法
* @return {Function} 返回的子类构造函数
* 示例:
* @example
* //父类
* function base(){
*
* }
*
* function sub(){
*
* }
* //子类
* BUI.extend(sub,base,{
* method : function(){
*
* }
* });
*
* //或者
* var sub = BUI.extend(base,{});
*/
extend: function(subclass, superclass, overrides, staticOverrides) {
//如果只提供父类构造函数,则自动生成子类构造函数
if (!BUI.isFunction(superclass)) {
overrides = superclass;
superclass = subclass;
subclass = function() {};
}
var create = Object.create ?
function(proto, c) {
return Object.create(proto, {
constructor: {
value: c
}
});
} :
function(proto, c) {
function F() {}
F.prototype = proto;
var o = new F();
o.constructor = c;
return o;
};
var superObj = create(superclass.prototype, subclass); //new superclass(),//实例化父类作为子类的prototype
subclass.prototype = BUI.mix(superObj, subclass.prototype); //指定子类的prototype
subclass.superclass = create(superclass.prototype, superclass);
BUI.mix(superObj, overrides);
BUI.mix(subclass, staticOverrides);
return subclass;
},
/**
* 生成唯一的Id
* @method
* @param {String} prefix 前缀
* @default 'bui-guid'
* @return {String} 唯一的编号
*/
guid: (function() {
var map = {};
return function(prefix) {
prefix = prefix || BUI.prefix + GUID_DEFAULT;
if (!map[prefix]) {
map[prefix] = 1;
} else {
map[prefix] += 1;
}
return prefix + map[prefix];
};
})(),
/**
* 判断是否是字符串
* @return {Boolean} 是否是字符串
*/
isString: function(value) {
return typeof value === 'string';
},
/**
* 判断是否数字,由于$.isNumberic方法会把 '123'认为数字
* @return {Boolean} 是否数字
*/
isNumber: function(value) {
return typeof value === 'number';
},
/**
* 是否是布尔类型
*
* @param {Object} value 测试的值
* @return {Boolean}
*/
isBoolean: function(value) {
return typeof value === 'boolean';
},
/**
* 控制台输出日志
* @param {Object} obj 输出的数据
*/
log: function(obj) {
if (BUI.debug && win.console && win.console.log) {
win.console.log(obj);
}
},
/**
* 将多个对象的属性复制到一个新的对象
*/
merge: function() {
var args = $.makeArray(arguments),
first = args[0];
if (BUI.isBoolean(first)) {
args.shift();
args.unshift({});
args.unshift(first);
} else {
args.unshift({});
}
return BUI.mix.apply(null, args);
},
/**
* 封装 jQuery.extend 方法,将多个对象的属性merge到第一个对象中
* @return {Object}
*/
mix: function() {
return $.extend.apply(null, arguments);
},
/**
* 创造顶层的命名空间,附加到window对象上,
* 包含namespace方法
*/
app: function(name) {
if (!window[name]) {
window[name] = {
namespace: function(nsName) {
return BUI.namespace(nsName, window[name]);
}
};
}
return window[name];
},
mixAttrs: mixAttrs,
mixAttr: mixAttr,
/**
* 将其他类作为mixin集成到指定类上面
* @param {Function} c 构造函数
* @param {Array} mixins 扩展类
* @param {Array} attrs 扩展的静态属性,默认为['ATTRS']
* @return {Function} 传入的构造函数
*/
mixin: function(c, mixins, attrs) {
attrs = attrs || [ATTRS, PARSER];
var extensions = mixins;
if (extensions) {
c.mixins = extensions;
var desc = {
// ATTRS:
// HTML_PARSER:
},
constructors = extensions['concat'](c);
// [ex1,ex2],扩展类后面的优先,ex2 定义的覆盖 ex1 定义的
// 主类最优先
BUI.each(constructors, function(ext) {
if (ext) {
// 合并 ATTRS/HTML_PARSER 到主类
BUI.each(attrs, function(K) {
if (ext[K]) {
desc[K] = desc[K] || {};
// 不覆盖主类上的定义,因为继承层次上扩展类比主类层次高
// 但是值是对象的话会深度合并
// 注意:最好值是简单对象,自定义 new 出来的对象就会有问题(用 function return 出来)!
if (K == 'ATTRS') {
//BUI.mix(true,desc[K], ext[K]);
mixAttrs(desc[K], ext[K]);
} else {
BUI.mix(desc[K], ext[K]);
}
}
});
}
});
BUI.each(desc, function(v, k) {
c[k] = v;
});
var prototype = {};
// 主类最优先
BUI.each(constructors, function(ext) {
if (ext) {
var proto = ext.prototype;
// 合并功能代码到主类,不覆盖
for (var p in proto) {
// 不覆盖主类,但是主类的父类还是覆盖吧
if (proto.hasOwnProperty(p)) {
prototype[p] = proto[p];
}
}
}
});
BUI.each(prototype, function(v, k) {
c.prototype[k] = v;
});
}
return c;
},
/**
* 生成命名空间
* @param {String} name 命名空间的名称
* @param {Object} baseNS 在已有的命名空间上创建命名空间,默认“BUI”
* @return {Object} 返回的命名空间对象
* @example
* BUI.namespace("Grid"); // BUI.Grid
*/
namespace: function(name, baseNS) {
baseNS = baseNS || BUI;
if (!name) {
return baseNS;
}
var list = name.split('.'),
//firstNS = win[list[0]],
curNS = baseNS;
for (var i = 0; i < list.length; i++) {
var nsName = list[i];
if (!curNS[nsName]) {
curNS[nsName] = {};
}
curNS = curNS[nsName];
};
return curNS;
},
/**
* BUI 控件的公用前缀
* @type {String}
*/
prefix: 'bui-',
/**
* 替换字符串中的字段.
* @param {String} str 模版字符串
* @param {Object} o json data
* @param {RegExp} [regexp] 匹配字符串的正则表达式
*/
substitute: function(str, o, regexp) {
if (!BUI.isString(str) || (!BUI.isObject(o)) && !BUI.isArray(o)) {
return str;
}
return str.replace(regexp || /\\?\{([^{}]+)\}/g, function(match, name) {
if (match.charAt(0) === '\\') {
return match.slice(1);
}
return (o[name] === undefined) ? '' : o[name];
});
},
/**
* 将$.param的反操作
* jquery只提供param方法
* @return {[type]} [description]
*/
unparam: function(str){
if (typeof str != 'string' || !(str = $.trim(str))) {
return {};
}
var pairs = str.split('&'),
pairsArr,
rst = {};
for(var i = pairs.length - 1; i >= 0; i--) {
pairsArr = pairs[i].split('=');
rst[pairsArr[0]] = decodeURIComponent(pairsArr[1]);
}
return rst;
},
/**
* 使第一个字母变成大写
* @param {String} s 字符串
* @return {String} 首字母大写后的字符串
*/
ucfirst: function(s) {
s += '';
return s.charAt(0).toUpperCase() + s.substring(1);
},
/**
* 页面上的一点是否在用户的视图内
* @param {Object} offset 坐标,left,top
* @return {Boolean} 是否在视图内
*/
isInView: function(offset) {
var left = offset.left,
top = offset.top,
viewWidth = BUI.viewportWidth(),
wiewHeight = BUI.viewportHeight(),
scrollTop = BUI.scrollTop(),
scrollLeft = BUI.scrollLeft();
//判断横坐标
if (left < scrollLeft || left > scrollLeft + viewWidth) {
return false;
}
//判断纵坐标
if (top < scrollTop || top > scrollTop + wiewHeight) {
return false;
}
return true;
},
/**
* 页面上的一点纵向坐标是否在用户的视图内
* @param {Object} top 纵坐标
* @return {Boolean} 是否在视图内
*/
isInVerticalView: function(top) {
var wiewHeight = BUI.viewportHeight(),
scrollTop = BUI.scrollTop();
//判断纵坐标
if (top < scrollTop || top > scrollTop + wiewHeight) {
return false;
}
return true;
},
/**
* 页面上的一点横向坐标是否在用户的视图内
* @param {Object} left 横坐标
* @return {Boolean} 是否在视图内
*/
isInHorizontalView: function(left) {
var viewWidth = BUI.viewportWidth(),
scrollLeft = BUI.scrollLeft();
//判断横坐标
if (left < scrollLeft || left > scrollLeft + viewWidth) {
return false;
}
return true;
},
/**
* 获取窗口可视范围宽度
* @return {Number} 可视区宽度
*/
viewportWidth: function() {
return $(window).width();
},
/**
* 获取窗口可视范围高度
* @return {Number} 可视区高度
*/
viewportHeight: function() {
return $(window).height();
},
/**
* 滚动到窗口的left位置
*/
scrollLeft: function() {
return $(window).scrollLeft();
},
/**
* 滚动到横向位置
*/
scrollTop: function() {
return $(window).scrollTop();
},
/**
* 窗口宽度
* @return {Number} 窗口宽度
*/
docWidth: function() {
return Math.max(this.viewportWidth(), doc[DOC_ELEMENT][SCROLL_WIDTH], doc[BODY][SCROLL_WIDTH]);
},
/**
* 窗口高度
* @return {Number} 窗口高度
*/
docHeight: function() {
return Math.max(this.viewportHeight(), doc[DOC_ELEMENT][SCROLL_HEIGHT], doc[BODY][SCROLL_HEIGHT]);
},
/**
* 遍历数组或者对象
* @param {Object|Array} element/Object 数组中的元素或者对象的值
* @param {Function} func 遍历的函数 function(elememt,index){} 或者 function(value,key){}
*/
each: function(elements, func) {
if (!elements) {
return;
}
$.each(elements, function(k, v) {
return func(v, k);
});
},
/**
* 封装事件,便于使用上下文this,和便于解除事件时使用
* @protected
* @param {Object} self 对象
* @param {String} action 事件名称
*/
wrapBehavior: function(self, action) {
return self['__bui_wrap_' + action] = function(e) {
if (!self.get('disabled')) {
self[action](e);
}
};
},
/**
* 获取封装的事件
* @protected
* @param {Object} self 对象
* @param {String} action 事件名称
*/
getWrapBehavior: function(self, action) {
return self['__bui_wrap_' + action];
},
/**
* 获取页面上使用了此id的控件
* @param {String} id 控件id
* @return {BUI.Component.Controller} 查找的控件
*/
getControl: function(id) {
return BUI.Component.Manager.getComponent(id);
},
/**
* 设置对象的属性,支持深度设置属性值
*
* @example
* BUI.setValue(obj,'a.b.c',value) //obj.a.b.c = value;
* @param {Object} obj 对象
* @param {String} name 名称
* @param {String} value 值
*/
setValue: function(obj,name,value){
if(!obj && !name){
return obj;
}
var arr = name.split('.'),
curObj = obj,
len = arr.length;
for (var i = 0; i < len; i++){
if(!curObj || !BUI.isObject(curObj)){
break;
}
var subName = arr[i];
if (i === len - 1){
curObj[subName] = value;
break;
}
if (!curObj[subName]) {
curObj[subName] = {};
}
curObj = curObj[subName];
}
return obj;
},
/**
* 设置对象的属性,支持深度设置属性值
*
* @example
* BUI.getValue(obj,'a.b.c') //return obj.a.b.c;
* @param {Object} obj 对象
* @param {String} name 名称
* @param {String} value 值
*/
getValue: function(obj,name){
if(!obj && !name){
return null;
}
var arr = name.split('.'),
curObj = obj,
len = arr.length,
value = null;
for (var i = 0; i < len; i++){
if(!curObj || !BUI.isObject(curObj)){
break;
}
var subName = arr[i];
if (i === len - 1){
value = curObj[subName];
break;
}
if (!curObj[subName]) {
break;
}
curObj = curObj[subName];
}
return value;
}
});
/**
* 表单帮助类,序列化、反序列化,设置值
* @class BUI.FormHelper
* @singleton
*/
var FormHelper = {
/**
* 将表单格式化成键值对形式
* @param {HTMLElement} form 表单
* @return {Object} 键值对的对象
*/
serializeToObject: function(form) {
var array = $(form).serializeArray(),
result = {};
BUI.each(array, function(item) {
var name = item.name;
if (!result[name]) { //如果是单个值,直接赋值
result[name] = item.value;
} else { //多值使用数组
if (!BUI.isArray(result[name])) {
result[name] = [result[name]];
}
result[name].push(item.value);
}
});
return result;
},
/**
* 设置表单的值
* @param {HTMLElement} form 表单
* @param {Object} obj 键值对
*/
setFields: function(form, obj) {
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
BUI.FormHelper.setField(form, name, obj[name]);
}
}
},
/**
* 清空表单
* @param {HTMLElement} form 表单元素
*/
clear: function(form) {
var elements = $.makeArray(form.elements);
BUI.each(elements, function(element) {
if (element.type === 'checkbox' || element.type === 'radio') {
$(element).attr('checked', false);
} else {
$(element).val('');
}
$(element).change();
});
},
/**
* 设置表单字段
* @param {HTMLElement} form 表单元素
* @param {string} field 字段名
* @param {string} value 字段值
*/
setField: function(form, fieldName, value) {
var fields = form.elements[fieldName];
if (fields && fields.type) {
FormHelper._setFieldValue(fields, value);
} else if (BUI.isArray(fields) || (fields && fields.length)) {
BUI.each(fields, function(field) {
FormHelper._setFieldValue(field, value);
});
}
},
//设置字段的值
_setFieldValue: function(field, value) {
if (field.type === 'checkbox') {
if (field.value == '' + value || (BUI.isArray(value) && BUI.Array.indexOf(field.value, value) !== -1)) {
$(field).attr('checked', true);
} else {
$(field).attr('checked', false);
}
} else if (field.type === 'radio') {
if (field.value == '' + value) {
$(field).attr('checked', true);
} else {
$(field).attr('checked', false);
}
} else {
$(field).val(value);
}
},
/**
* 获取表单字段值
* @param {HTMLElement} form 表单元素
* @param {string} field 字段名
* @return {String} 字段值
*/
getField: function(form, fieldName) {
return BUI.FormHelper.serializeToObject(form)[fieldName];
}
};
BUI.FormHelper = FormHelper;
module.exports = BUI;
});
define("bui/common/ua", ["jquery"], function(require, exports, module){
/**
* @fileOverview UA,jQuery的 $.browser 对象非常难使用
* @ignore
* @author dxq613@gmail.com
*/
var $ = require("jquery");
function numberify(s) {
var c = 0;
// convert '1.2.3.4' to 1.234
return parseFloat(s.replace(/\./g, function() {
return (c++ === 0) ? '.' : '';
}));
};
function uaMatch(s) {
s = s.toLowerCase();
var r = /(chrome)[ \/]([\w.]+)/.exec(s) || /(webkit)[ \/]([\w.]+)/.exec(s) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(s) || /(msie) ([\w.]+)/.exec(s) || s.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(s) || [],
a = {
browser: r[1] || "",
version: r[2] || "0"
},
b = {};
a.browser && (b[a.browser] = !0, b.version = a.version),
b.chrome ? b.webkit = !0 : b.webkit && (b.safari = !0);
return b;
}
var UA = $.UA || (function() {
var browser = $.browser || uaMatch(navigator.userAgent),
versionNumber = numberify(browser.version),
/**
* 浏览器版本检测
* @class BUI.UA
* @singleton
*/
ua = {
/**
* ie 版本
* @type {Number}
*/
ie: browser.msie && versionNumber,
/**
* webkit 版本
* @type {Number}
*/
webkit: browser.webkit && versionNumber,
/**
* opera 版本
* @type {Number}
*/
opera: browser.opera && versionNumber,
/**
* mozilla 火狐版本
* @type {Number}
*/
mozilla: browser.mozilla && versionNumber
};
return ua;
})();
module.exports = UA;
});
define("bui/common/json", ["jquery"], function(require, exports, module){
/**
* @fileOverview 由于jQuery只有 parseJSON ,没有stringify所以使用过程不方便
* @ignore
*/
var $ = require("jquery"),
UA = require("bui/common/ua"),
win = window,
JSON = win.JSON;
// ie 8.0.7600.16315@win7 json 有问题
if (!JSON || UA['ie'] < 9) {
JSON = win.JSON = {};
}
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function(key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function(key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"': '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable['lastIndex'] = 0;
return escapable.test(string) ?
'"' + string.replace(escapable, function(a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function(value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {
'': value
});
};
}
function looseParse(data) {
try {
return new Function('return ' + data + ';')();
} catch (e) {
throw 'Json parse error!';
}
}
/**
* JSON 格式化
* @class BUI.JSON
* @singleton
*/
var JSON = {
/**
* 转成json 等同于$.parseJSON
* @method
* @param {String} jsonstring 合法的json 字符串
*/
parse: $.parseJSON,
/**
* 业务中有些字符串组成的json数据不是严格的json数据,如使用单引号,或者属性名不是字符串
* 如 : {a:'abc'}
* @method
* @param {String} jsonstring
*/
looseParse: looseParse,
/**
* 将Json转成字符串
* @method
* @param {Object} json json 对象
*/
stringify: JSON.stringify
}
module.exports = JSON;
});
define("bui/common/date", [], function(require, exports, module){
/*
* @fileOverview Date Format 1.2.3
* @ignore
* (c) 2007-2009 Steven Levithan
* var control = new Control();
* control.on('click',function(ev){
*
* });
*
* control.off(); //移除所有事件
*
* @class BUI.Observable
* @abstract
* @param {Object} config 配置项键值对
*/
var Observable = function(config) {
this._events = [];
this._eventMap = {};
this._bubblesEvents = [];
this._initEvents(config);
};
BUI.augment(Observable, {
/**
* @cfg {Object} listeners
* 初始化事件,快速注册事件
*
* var list = new BUI.List.SimpleList({
* listeners : {
* itemclick : function(ev){},
* itemrendered : function(ev){}
* },
* items : []
* });
* list.render();
*
*/
/**
* @cfg {Function} handler
* 点击事件的处理函数,快速配置点击事件而不需要写listeners属性
*
* var list = new BUI.List.SimpleList({
* handler : function(ev){} //click 事件
* });
* list.render();
*
*/
/**
* 支持的事件名列表
* @private
*/
_events: [],
/**
* 绑定的事件
* @private
*/
_eventMap: {},
_bubblesEvents: [],
_bubbleTarget: null,
//获取回调集合
_getCallbacks: function(eventType) {
var _self = this,
eventMap = _self._eventMap;
return eventMap[eventType];
},
//初始化事件列表
_initEvents: function(config) {
var _self = this,
listeners = null;
if (!config) {
return;
}
listeners = config.listeners || {};
if (config.handler) {
listeners.click = config.handler;
}
if (listeners) {
for (var name in listeners) {
if (listeners.hasOwnProperty(name)) {
_self.on(name, listeners[name]);
}
};
}
},
//事件是否支持冒泡
_isBubbles: function(eventType) {
return ArrayUtil.indexOf(eventType, this._bubblesEvents) >= 0;
},
/**
* 添加冒泡的对象
* @protected
* @param {Object} target 冒泡的事件源
*/
addTarget: function(target) {
this._bubbleTarget = target;
},
/**
* 添加支持的事件
* @protected
* @param {String|String[]} events 事件
*/
addEvents: function(events) {
var _self = this,
existEvents = _self._events,
eventMap = _self._eventMap;
function addEvent(eventType) {
if (ArrayUtil.indexOf(eventType, existEvents) === -1) {
eventMap[eventType] = getCallbacks();
existEvents.push(eventType);
}
}
if (BUI.isArray(events)) {
BUI.each(events, function(eventType) {
addEvent(eventType);
});
} else {
addEvent(events);
}
},
/**
* 移除所有绑定的事件
* @protected
*/
clearListeners: function() {
var _self = this,
eventMap = _self._eventMap;
for (var name in eventMap) {
if (eventMap.hasOwnProperty(name)) {
eventMap[name].empty();
}
}
},
/**
* 触发事件
*
* //绑定事件
* list.on('itemclick',function(ev){
* alert('21');
* });
* //触发事件
* list.fire('itemclick');
*
* @param {String} eventType 事件类型
* @param {Object} eventData 事件触发时传递的数据
* @return {Boolean|undefined} 如果其中一个事件处理器返回 false , 则返回 false, 否则返回最后一个事件处理器的返回值
*/
fire: function(eventType, eventData) {
var _self = this,
callbacks = _self._getCallbacks(eventType),
args = $.makeArray(arguments),
result;
if (!eventData) {
eventData = {};
args.push(eventData);
}
if (!eventData.target) {
eventData.target = _self;
}
if (callbacks) {
result = callbacks.fireWith(_self, Array.prototype.slice.call(args, 1));
}
if (_self._isBubbles(eventType)) {
var bubbleTarget = _self._bubbleTarget;
if (bubbleTarget && bubbleTarget.fire) {
bubbleTarget.fire(eventType, eventData);
}
}
return result;
},
/**
* 暂停事件的执行
*
* list.pauseEvent('itemclick');
*
* @param {String} eventType 事件类型
*/
pauseEvent: function(eventType) {
var _self = this,
callbacks = _self._getCallbacks(eventType);
callbacks && callbacks.pause();
},
/**
* 唤醒事件
*
* list.resumeEvent('itemclick');
*
* @param {String} eventType 事件类型
*/
resumeEvent: function(eventType) {
var _self = this,
callbacks = _self._getCallbacks(eventType);
callbacks && callbacks.resume();
},
/**
* 添加绑定事件
*
* //绑定单个事件
* list.on('itemclick',function(ev){
* alert('21');
* });
* //绑定多个事件
* list.on('itemrendered itemupdated',function(){
* //列表项创建、更新时触发操作
* });
*
* @param {String} eventType 事件类型
* @param {Function} fn 回调函数
*/
on: function(eventType, fn) {
//一次监听多个事件
var arr = eventType.split(' '),
_self = this,
callbacks = null;
if (arr.length > 1) {
BUI.each(arr, function(name) {
_self.on(name, fn);
});
} else {
callbacks = _self._getCallbacks(eventType);
if (callbacks) {
callbacks.add(fn);
} else {
_self.addEvents(eventType);
_self.on(eventType, fn);
}
}
return _self;
},
/**
* 移除绑定的事件
*
* //移除所有事件
* list.off();
*
* //移除特定事件
* function callback(ev){}
* list.on('click',callback);
*
* list.off('click',callback);//需要保存回调函数的引用
*
*
* @param {String} eventType 事件类型
* @param {Function} fn 回调函数
*/
off: function(eventType, fn) {
if (!eventType && !fn) {
this.clearListeners();
return this;
}
var _self = this,
callbacks = _self._getCallbacks(eventType);
if (callbacks) {
if (fn) {
callbacks.remove(fn);
} else {
callbacks.empty();
}
}
return _self;
},
/**
* 配置事件是否允许冒泡
* @protected
* @param {String} eventType 支持冒泡的事件
* @param {Object} cfg 配置项
* @param {Boolean} cfg.bubbles 是否支持冒泡
*/
publish: function(eventType, cfg) {
var _self = this,
bubblesEvents = _self._bubblesEvents;
if (cfg.bubbles) {
if (BUI.Array.indexOf(eventType, bubblesEvents) === -1) {
bubblesEvents.push(eventType);
}
} else {
var index = BUI.Array.indexOf(eventType, bubblesEvents);
if (index !== -1) {
bubblesEvents.splice(index, 1);
}
}
}
});
module.exports = Observable;
});
define("bui/common/base", ["jquery"], function(require, exports, module){
/**
* @fileOverview Base UI控件的最基础的类
* @author yiminghe@gmail.com
* copied by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery");
var INVALID = {},
Observable = require("bui/common/observable");
function ensureNonEmpty(obj, name, create) {
var ret = obj[name] || {};
if (create) {
obj[name] = ret;
}
return ret;
}
function normalFn(host, method) {
if (BUI.isString(method)) {
return host[method];
}
return method;
}
function __fireAttrChange(self, when, name, prevVal, newVal) {
var attrName = name;
return self.fire(when + BUI.ucfirst(name) + 'Change', {
attrName: attrName,
prevVal: prevVal,
newVal: newVal
});
}
function setInternal(self, name, value, opts, attrs) {
opts = opts || {};
var ret,
subVal,
prevVal;
prevVal = self.get(name);
//如果未改变值不进行修改
if (!$.isPlainObject(value) && !BUI.isArray(value) && prevVal === value) {
return undefined;
}
// check before event
if (!opts['silent']) {
if (false === __fireAttrChange(self, 'before', name, prevVal, value)) {
return false;
}
}
// set it
ret = self._set(name, value, opts);
if (ret === false) {
return ret;
}
// fire after event
if (!opts['silent']) {
value = self.__attrVals[name];
__fireAttrChange(self, 'after', name, prevVal, value);
}
return self;
}
function initClassAttrs(c) {
if (c._attrs || c == Base) {
return;
}
var superCon = c.superclass.constructor;
if (superCon && !superCon._attrs) {
initClassAttrs(superCon);
}
c._attrs = {};
BUI.mixAttrs(c._attrs, superCon._attrs);
BUI.mixAttrs(c._attrs, c.ATTRS);
}
/**
* 基础类,此类提供以下功能
* - 提供设置获取属性
* - 提供事件支持
* - 属性变化时会触发对应的事件
* - 将配置项自动转换成属性
*
* ** 创建类,继承BUI.Base类 **
*
* var Control = function(cfg){
* Control.superclass.constructor.call(this,cfg); //调用BUI.Base的构造方法,将配置项变成属性
* };
*
* BUI.extend(Control,BUI.Base);
*
*
* ** 声明默认属性 **
*
* Control.ATTRS = {
* id : {
* value : 'id' //value 是此属性的默认值
* },
* renderTo : {
*
* },
* el : {
* valueFn : function(){ //第一次调用的时候将renderTo的DOM转换成el属性
* return $(this.get('renderTo'));
* }
* },
* text : {
* getter : function(){ //getter 用于获取值,而不是设置的值
* return this.get('el').val();
* },
* setter : function(v){ //不仅仅是设置值,可以进行相应的操作
* this.get('el').val(v);
* }
* }
* };
*
*
* ** 声明类的方法 **
*
* BUI.augment(Control,{
* getText : function(){
* return this.get('text'); //可以用get方法获取属性值
* },
* setText : function(txt){
* this.set('text',txt); //使用set 设置属性值
* }
* });
*
*
* ** 创建对象 **
*
* var c = new Control({
* id : 'oldId',
* text : '测试文本',
* renderTo : '#t1'
* });
*
* var el = c.get(el); //$(#t1);
* el.val(); //text的值 : '测试文本'
* c.set('text','修改的值');
* el.val(); //'修改的值'
*
* c.set('id','newId') //会触发2个事件: beforeIdChange,afterIdChange 2个事件 ev.newVal 和ev.prevVal标示新旧值
*
* @class BUI.Base
* @abstract
* @extends BUI.Observable
* @param {Object} config 配置项
*/
var Base = function(config) {
var _self = this,
c = _self.constructor,
constructors = [];
this.__attrs = {};
this.__attrVals = {};
Observable.apply(this, arguments);
// define
while (c) {
constructors.push(c);
if (c.extensions) { //延迟执行mixin
BUI.mixin(c, c.extensions);
delete c.extensions;
}
//_self.addAttrs(c['ATTRS']);
c = c.superclass ? c.superclass.constructor : null;
}
//以当前对象的属性最终添加到属性中,覆盖之前的属性
/*for (var i = constructors.length - 1; i >= 0; i--) {
_self.addAttrs(constructors[i]['ATTRS'],true);
};*/
var con = _self.constructor;
initClassAttrs(con);
_self._initStaticAttrs(con._attrs);
_self._initAttrs(config);
};
Base.INVALID = INVALID;
BUI.extend(Base, Observable);
BUI.augment(Base, {
_initStaticAttrs: function(attrs) {
var _self = this,
__attrs;
__attrs = _self.__attrs = {};
for (var p in attrs) {
if (attrs.hasOwnProperty(p)) {
var attr = attrs[p];
/*if(BUI.isObject(attr.value) || BUI.isArray(attr.value) || attr.valueFn){*/
if (attr.shared === false || attr.valueFn) {
__attrs[p] = {};
BUI.mixAttr(__attrs[p], attrs[p]);
} else {
__attrs[p] = attrs[p];
}
}
};
},
/**
* 添加属性定义
* @protected
* @param {String} name 属性名
* @param {Object} attrConfig 属性定义
* @param {Boolean} overrides 是否覆盖字段
*/
addAttr: function(name, attrConfig, overrides) {
var _self = this,
attrs = _self.__attrs,
attr = attrs[name];
if (!attr) {
attr = attrs[name] = {};
}
for (var p in attrConfig) {
if (attrConfig.hasOwnProperty(p)) {
if (p == 'value') {
if (BUI.isObject(attrConfig[p])) {
attr[p] = attr[p] || {};
BUI.mix( /*true,*/ attr[p], attrConfig[p]);
} else if (BUI.isArray(attrConfig[p])) {
attr[p] = attr[p] || [];
BUI.mix( /*true,*/ attr[p], attrConfig[p]);
} else {
attr[p] = attrConfig[p];
}
} else {
attr[p] = attrConfig[p];
}
}
};
return _self;
},
/**
* 添加属性定义
* @protected
* @param {Object} attrConfigs An object with attribute name/configuration pairs.
* @param {Object} initialValues user defined initial values
* @param {Boolean} overrides 是否覆盖字段
*/
addAttrs: function(attrConfigs, initialValues, overrides) {
var _self = this;
if (!attrConfigs) {
return _self;
}
if (typeof(initialValues) === 'boolean') {
overrides = initialValues;
initialValues = null;
}
BUI.each(attrConfigs, function(attrConfig, name) {
_self.addAttr(name, attrConfig, overrides);
});
if (initialValues) {
_self.set(initialValues);
}
return _self;
},
/**
* 是否包含此属性
* @protected
* @param {String} name 值
* @return {Boolean} 是否包含
*/
hasAttr: function(name) {
return name && this.__attrs.hasOwnProperty(name);
},
/**
* 获取默认的属性值
* @protected
* @return {Object} 属性值的键值对
*/
getAttrs: function() {
return this.__attrs; //ensureNonEmpty(this, '__attrs', true);
},
/**
* 获取属性名/属性值键值对
* @protected
* @return {Object} 属性对象
*/
getAttrVals: function() {
return this.__attrVals; //ensureNonEmpty(this, '__attrVals', true);
},
/**
* 获取属性值,所有的配置项和属性都可以通过get方法获取
*
* var control = new Control({
* text : 'control text'
* });
* control.get('text'); //control text
*
* control.set('customValue','value'); //临时变量
* control.get('customValue'); //value
*
* ** 属性值/配置项 **
*
* Control.ATTRS = { //声明属性值
* text : {
* valueFn : function(){},
* value : 'value',
* getter : function(v){}
* }
* };
* var c = new Control({
* text : 'text value'
* });
* //get 函数取的顺序为:是否有修改值(配置项、set)、默认值(第一次调用执行valueFn),如果有getter,则将值传入getter返回
*
* c.get('text') //text value
* c.set('text','new text');//修改值
* c.get('text');//new text
*
* @param {String} name 属性名
* @return {Object} 属性值
*/
get: function(name) {
var _self = this,
//declared = _self.hasAttr(name),
attrVals = _self.__attrVals,
attrConfig,
getter,
ret;
attrConfig = ensureNonEmpty(_self.__attrs, name);
getter = attrConfig['getter'];
// get user-set value or default value
//user-set value takes privilege
ret = name in attrVals ?
attrVals[name] :
_self._getDefAttrVal(name);
// invoke getter for this attribute
if (getter && (getter = normalFn(_self, getter))) {
ret = getter.call(_self, ret, name);
}
return ret;
},
/**
* @清理所有属性值
* @protected
*/
clearAttrVals: function() {
this.__attrVals = {};
},
/**
* 移除属性定义
* @protected
*/
removeAttr: function(name) {
var _self = this;
if (_self.hasAttr(name)) {
delete _self.__attrs[name];
delete _self.__attrVals[name];
}
return _self;
},
/**
* 设置属性值,会触发before+Name+Change,和 after+Name+Change事件
*
* control.on('beforeTextChange',function(ev){
* var newVal = ev.newVal,
* attrName = ev.attrName,
* preVal = ev.prevVal;
*
* //TO DO
* });
* control.set('text','new text'); //此时触发 beforeTextChange,afterTextChange
* control.set('text','modify text',{silent : true}); //此时不触发事件
*
* @param {String|Object} name 属性名
* @param {Object} value 值
* @param {Object} opts 配置项
* @param {Boolean} opts.silent 配置属性时,是否不触发事件
*/
set: function(name, value, opts) {
var _self = this;
if ($.isPlainObject(name)) {
opts = value;
var all = Object(name),
attrs = [];
for (name in all) {
if (all.hasOwnProperty(name)) {
setInternal(_self, name, all[name], opts);
}
}
return _self;
}
return setInternal(_self, name, value, opts);
},
/**
* 设置属性,不触发事件
*
* control.setInternal('text','text');//此时不触发事件
*
* @param {String} name 属性名
* @param {Object} value 属性值
* @return {Boolean|undefined} 如果值无效则返回false,否则返回undefined
*/
setInternal: function(name, value, opts) {
return this._set(name, value, opts);
},
//获取属性默认值
_getDefAttrVal: function(name) {
var _self = this,
attrs = _self.__attrs,
attrConfig = ensureNonEmpty(attrs, name),
valFn = attrConfig.valueFn,
val;
if (valFn && (valFn = normalFn(_self, valFn))) {
val = valFn.call(_self);
if (val !== undefined) {
attrConfig.value = val;
}
delete attrConfig.valueFn;
attrs[name] = attrConfig;
}
return attrConfig.value;
},
//仅仅设置属性值
_set: function(name, value, opts) {
var _self = this,
setValue,
// if host does not have meta info corresponding to (name,value)
// then register on demand in order to collect all data meta info
// 一定要注册属性元数据,否则其他模块通过 _attrs 不能枚举到所有有效属性
// 因为属性在声明注册前可以直接设置值
attrConfig = ensureNonEmpty(_self.__attrs, name, true),
setter = attrConfig['setter'];
// if setter has effect
if (setter && (setter = normalFn(_self, setter))) {
setValue = setter.call(_self, value, name);
}
if (setValue === INVALID) {
return false;
}
if (setValue !== undefined) {
value = setValue;
}
// finally set
_self.__attrVals[name] = value;
return _self;
},
//初始化属性
_initAttrs: function(config) {
var _self = this;
if (config) {
for (var attr in config) {
if (config.hasOwnProperty(attr)) {
// 用户设置会调用 setter/validator 的,但不会触发属性变化事件
_self._set(attr, config[attr]);
}
}
}
}
});
module.exports = Base;
});
define("bui/common/component/component", ["jquery"], function(require, exports, module){
/**
* @fileOverview Component命名空间的入口文件
* @ignore
*/
/**
* @class BUI.Component
*
*
*
*
*
* //默认状态下创建对象,并没有进行render
* var control = new Control();
* control.render(); //需要调用render方法
*
* //设置autoRender后,不需要调用render方法
* var control = new Control({
* autoRender : true
* });
*
* @cfg {Boolean} autoRender
*/
/**
* 是否自动渲染,如果不自动渲染,需要用户调用 render()方法
* @type {Boolean}
* @ignore
*/
autoRender: {
value: false
},
/**
* @type {Object}
* 事件处理函数:
* {
* 'click':function(e){}
* }
* @ignore
*/
listeners: {
value: {}
},
/**
* 插件集合
*
* var grid = new Grid({
* columns : [{},{}],
* plugins : [Grid.Plugins.RadioSelection]
* });
*
* @cfg {Array} plugins
*/
/**
* 插件集合
* @type {Array}
* @readOnly
*/
plugins: {
//value : []
},
/**
* 是否已经渲染完成
* @type {Boolean}
* @default false
* @readOnly
*/
rendered: {
value: false
},
/**
* 获取控件的 xclass
* @readOnly
* @type {String}
* @protected
*/
xclass: {
valueFn: function() {
return Manager.getXClassByConstructor(this.constructor);
}
}
};
BUI.extend(UIBase, Base);
BUI.augment(UIBase, {
/**
* 创建DOM结构
* @protected
*/
create: function() {
var self = this;
// 是否生成过节点
if (!self.get('created')) {
/**
* @event beforeCreateDom
* fired before root node is created
* @param e
*/
self.fire('beforeCreateDom');
callMethodByHierarchy(self, 'createDom', '__createDom');
self._set('created', true);
/**
* @event afterCreateDom
* fired when root node is created
* @param e
*/
self.fire('afterCreateDom');
actionPlugins(self, self.get('plugins'), 'createDom');
}
return self;
},
/**
* 渲染
*/
render: function() {
var _self = this;
// 是否已经渲染过
if (!_self.get('rendered')) {
var plugins = _self.get('plugins');
_self.create(undefined);
_self.set('created', true);
/**
* @event beforeRenderUI
* fired when root node is ready
* @param e
*/
_self.fire('beforeRenderUI');
callMethodByHierarchy(_self, 'renderUI', '__renderUI');
/**
* @event afterRenderUI
* fired after root node is rendered into dom
* @param e
*/
_self.fire('afterRenderUI');
actionPlugins(_self, plugins, 'renderUI');
/**
* @event beforeBindUI
* fired before UIBase 's internal event is bind.
* @param e
*/
_self.fire('beforeBindUI');
bindUI(_self);
callMethodByHierarchy(_self, 'bindUI', '__bindUI');
_self.set('binded', true);
/**
* @event afterBindUI
* fired when UIBase 's internal event is bind.
* @param e
*/
_self.fire('afterBindUI');
actionPlugins(_self, plugins, 'bindUI');
/**
* @event beforeSyncUI
* fired before UIBase 's internal state is synchronized.
* @param e
*/
_self.fire('beforeSyncUI');
syncUI(_self);
callMethodByHierarchy(_self, 'syncUI', '__syncUI');
/**
* @event afterSyncUI
* fired after UIBase 's internal state is synchronized.
* @param e
*/
_self.fire('afterSyncUI');
actionPlugins(_self, plugins, 'syncUI');
_self._set('rendered', true);
}
return _self;
},
/**
* 子类可继承此方法,当DOM创建时调用
* @protected
* @method
*/
createDom: noop,
/**
* 子类可继承此方法,渲染UI时调用
* @protected
* @method
*/
renderUI: noop,
/**
* 子类可继承此方法,绑定事件时调用
* @protected
* @method
*/
bindUI: noop,
/**
* 同步属性值到UI上
* @protected
* @method
*/
syncUI: noop,
/**
* 析构函数
*/
destroy: function() {
var _self = this;
if (_self.destroyed) { //防止返回销毁
return _self;
}
/**
* @event beforeDestroy
* fired before UIBase 's destroy.
* @param e
*/
_self.fire('beforeDestroy');
actionPlugins(_self, _self.get('plugins'), 'destructor');
destroyHierarchy(_self);
/**
* @event afterDestroy
* fired before UIBase 's destroy.
* @param e
*/
_self.fire('afterDestroy');
_self.off();
_self.clearAttrVals();
_self.destroyed = true;
return _self;
}
});
//延时处理构造函数
function initConstuctor(c) {
var constructors = [];
while (c.base) {
constructors.push(c);
c = c.base;
}
for (var i = constructors.length - 1; i >= 0; i--) {
var C = constructors[i];
//BUI.extend(C,C.base,C.px,C.sx);
BUI.mix(C.prototype, C.px);
BUI.mix(C, C.sx);
C.base = null;
C.px = null;
C.sx = null;
}
}
BUI.mix(UIBase, {
/**
* 定义一个类
* @static
* @param {Function} base 基类构造函数
* @param {Array} extensions 扩展
* @param {Object} px 原型链上的扩展
* @param {Object} sx
* @return {Function} 继承与基类的构造函数
*/
define: function(base, extensions, px, sx) {
if ($.isPlainObject(extensions)) {
sx = px;
px = extensions;
extensions = [];
}
function C() {
var c = this.constructor;
if (c.base) {
initConstuctor(c);
}
UIBase.apply(this, arguments);
}
BUI.extend(C, base); //无法延迟
C.base = base;
C.px = px; //延迟复制原型链上的函数
C.sx = sx; //延迟复制静态属性
//BUI.mixin(C,extensions);
if (extensions.length) { //延迟执行mixin
C.extensions = extensions;
}
return C;
},
/**
* 扩展一个类,基类就是类本身
* @static
* @param {Array} extensions 扩展
* @param {Object} px 原型链上的扩展
* @param {Object} sx
* @return {Function} 继承与基类的构造函数
*/
extend: function extend(extensions, px, sx) {
var args = $.makeArray(arguments),
ret,
last = args[args.length - 1];
args.unshift(this);
if (last.xclass) {
args.pop();
args.push(last.xclass);
}
ret = UIBase.define.apply(UIBase, args);
if (last.xclass) {
var priority = last.priority || (this.priority ? (this.priority + 1) : 1);
Manager.setConstructorByXClass(last.xclass, {
constructor: ret,
priority: priority
});
//方便调试
ret.__xclass = last.xclass;
ret.priority = priority;
ret.toString = function() {
return last.xclass;
}
}
ret.extend = extend;
return ret;
}
});
module.exports = UIBase;
});
define("bui/common/component/uibase/align", ["jquery"], function(require, exports, module){
/**
* @fileOverview 跟指定的元素项对齐的方式
* @author yiminghe@gmail.com
* copied by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
UA = require("bui/common/ua"),
CLS_ALIGN_PREFIX ='x-align-',
win = window;
// var ieMode = document.documentMode || UA.ie;
/*
inspired by closure library by Google
see http://yiminghe.iteye.com/blog/1124720
*/
/**
* 得到会导致元素显示不全的祖先元素
* @ignore
*/
function getOffsetParent(element) {
// ie 这个也不是完全可行
/**
@ignore
**/
// element.offsetParent does the right thing in ie7 and below. Return parent with layout!
// In other browsers it only includes elements with position absolute, relative or
// fixed, not elements with overflow set to auto or scroll.
// if (UA.ie && ieMode < 8) {
// return element.offsetParent;
// }
// 统一的 offsetParent 方法
var doc = element.ownerDocument,
body = doc.body,
parent,
positionStyle = $(element).css('position'),
skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
if (!skipStatic) {
return element.nodeName.toLowerCase() == 'html' ? null : element.parentNode;
}
for (parent = element.parentNode; parent && parent != body; parent = parent.parentNode) {
positionStyle = $(parent).css('position');
if (positionStyle != 'static') {
return parent;
}
}
return null;
}
/**
* 获得元素的显示部分的区域
* @private
* @ignore
*/
function getVisibleRectForElement(element) {
var visibleRect = {
left:0,
right:Infinity,
top:0,
bottom:Infinity
},
el,
scrollX,
scrollY,
winSize,
doc = element.ownerDocument,
body = doc.body,
documentElement = doc.documentElement;
// Determine the size of the visible rect by climbing the dom accounting for
// all scrollable containers.
for (el = element; el = getOffsetParent(el);) {
// clientWidth is zero for inline block elements in ie.
if ((!UA.ie || el.clientWidth != 0) &&
// body may have overflow set on it, yet we still get the entire
// viewport. In some browsers, el.offsetParent may be
// document.documentElement, so check for that too.
(el != body && el != documentElement && $(el).css('overflow') != 'visible')) {
var pos = $(el).offset();
// add border
pos.left += el.clientLeft;
pos.top += el.clientTop;
visibleRect.top = Math.max(visibleRect.top, pos.top);
visibleRect.right = Math.min(visibleRect.right,
// consider area without scrollBar
pos.left + el.clientWidth);
visibleRect.bottom = Math.min(visibleRect.bottom,
pos.top + el.clientHeight);
visibleRect.left = Math.max(visibleRect.left, pos.left);
}
}
// Clip by window's viewport.
scrollX = $(win).scrollLeft();
scrollY = $(win).scrollTop();
visibleRect.left = Math.max(visibleRect.left, scrollX);
visibleRect.top = Math.max(visibleRect.top, scrollY);
winSize = {
width:BUI.viewportWidth(),
height:BUI.viewportHeight()
};
visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
return visibleRect.top >= 0 && visibleRect.left >= 0 &&
visibleRect.bottom > visibleRect.top &&
visibleRect.right > visibleRect.left ?
visibleRect : null;
}
function getElFuturePos(elRegion, refNodeRegion, points, offset) {
var xy,
diff,
p1,
p2;
xy = {
left:elRegion.left,
top:elRegion.top
};
p1 = getAlignOffset(refNodeRegion, points[0]);
p2 = getAlignOffset(elRegion, points[1]);
diff = [p2.left - p1.left, p2.top - p1.top];
return {
left:xy.left - diff[0] + (+offset[0]),
top:xy.top - diff[1] + (+offset[1])
};
}
function isFailX(elFuturePos, elRegion, visibleRect) {
return elFuturePos.left < visibleRect.left ||
elFuturePos.left + elRegion.width > visibleRect.right;
}
function isFailY(elFuturePos, elRegion, visibleRect) {
return elFuturePos.top < visibleRect.top ||
elFuturePos.top + elRegion.height > visibleRect.bottom;
}
function adjustForViewport(elFuturePos, elRegion, visibleRect, overflow) {
var pos = BUI.cloneObject(elFuturePos),
size = {
width:elRegion.width,
height:elRegion.height
};
if (overflow.adjustX && pos.left < visibleRect.left) {
pos.left = visibleRect.left;
}
// Left edge inside and right edge outside viewport, try to resize it.
if (overflow['resizeWidth'] &&
pos.left >= visibleRect.left &&
pos.left + size.width > visibleRect.right) {
size.width -= (pos.left + size.width) - visibleRect.right;
}
// Right edge outside viewport, try to move it.
if (overflow.adjustX && pos.left + size.width > visibleRect.right) {
// 保证左边界和可视区域左边界对齐
pos.left = Math.max(visibleRect.right - size.width, visibleRect.left);
}
// Top edge outside viewport, try to move it.
if (overflow.adjustY && pos.top < visibleRect.top) {
pos.top = visibleRect.top;
}
// Top edge inside and bottom edge outside viewport, try to resize it.
if (overflow['resizeHeight'] &&
pos.top >= visibleRect.top &&
pos.top + size.height > visibleRect.bottom) {
size.height -= (pos.top + size.height) - visibleRect.bottom;
}
// Bottom edge outside viewport, try to move it.
if (overflow.adjustY && pos.top + size.height > visibleRect.bottom) {
// 保证上边界和可视区域上边界对齐
pos.top = Math.max(visibleRect.bottom - size.height, visibleRect.top);
}
return BUI.mix(pos, size);
}
function flip(points, reg, map) {
var ret = [];
$.each(points, function (index,p) {
ret.push(p.replace(reg, function (m) {
return map[m];
}));
});
return ret;
}
function flipOffset(offset, index) {
offset[index] = -offset[index];
return offset;
}
/**
* @class BUI.Component.UIBase.Align
* Align extension class.
* Align component with specified element.
*
* var overlay = new Overlay( {
* align :{
* node: null, // 参考元素, falsy 或 window 为可视区域, 'trigger' 为触发元素, 其他为指定元素
* points: ['cc','cc'], // ['tr', 'tl'] 表示 overlay 的 tl 与参考节点的 tr 对齐
* offset: [0, 0] // 有效值为 [n, m]
* }
* });
*
*/
/**
* 设置对齐属性
* @type {Object}
* @field
*
* var align = {
* node: null, // 参考元素, falsy 或 window 为可视区域, 'trigger' 为触发元素, 其他为指定元素
* points: ['cc','cc'], // ['tr', 'tl'] 表示 overlay 的 tl 与参考节点的 tr 对齐
* offset: [0, 0] // 有效值为 [n, m]
* };
* overlay.set('align',align);
*
*/
align:{
shared : false,
value:{}
}
};
function getRegion(node) {
var offset, w, h;
if (node.length && !$.isWindow(node[0])) {
offset = node.offset();
w = node.outerWidth();
h = node.outerHeight();
} else {
offset = { left:BUI.scrollLeft(), top:BUI.scrollTop() };
w = BUI.viewportWidth();
h = BUI.viewportHeight();
}
offset.width = w;
offset.height = h;
return offset;
}
/**
* 获取 node 上的 align 对齐点 相对于页面的坐标
* @param region
* @param align
*/
function getAlignOffset(region, align) {
var V = align.charAt(0),
H = align.charAt(1),
w = region.width,
h = region.height,
x, y;
x = region.left;
y = region.top;
if (V === 'c') {
y += h / 2;
} else if (V === 'b') {
y += h;
}
if (H === 'c') {
x += w / 2;
} else if (H === 'r') {
x += w;
}
return { left:x, top:y };
}
//清除对齐的css样式
function clearAlignCls(el){
var cls = el.attr('class'),
regex = new RegExp('\s?'+CLS_ALIGN_PREFIX+'[a-z]{2}-[a-z]{2}','ig'),
arr = regex.exec(cls);
if(arr){
el.removeClass(arr.join(' '));
}
}
Align.prototype =
{
_uiSetAlign:function (v,ev) {
var alignCls = '',
el,
selfAlign; //points 的第二个参数,是自己对齐于其他节点的的方式
if (v && v.points) {
this.align(v.node, v.points, v.offset, v.overflow);
this.set('cachePosition',null);
el = this.get('el');
clearAlignCls(el);
selfAlign = v.points.join('-');
alignCls = CLS_ALIGN_PREFIX + selfAlign;
el.addClass(alignCls);
/**/
}
},
__bindUI : function(){
var _self = this;
var fn = BUI.wrapBehavior(_self,'handleWindowResize');
_self.on('show',function(){
$(window).on('resize',fn);
});
_self.on('hide',function(){
$(window).off('resize',fn);
});
},
//处理window resize事件
handleWindowResize : function(){
var _self = this,
align = _self.get('align');
_self.set('align',align);
},
/*
对齐 Overlay 到 node 的 points 点, 偏移 offset 处
@method
@ignore
@param {Element} node 参照元素, 可取配置选项中的设置, 也可是一元素
@param {String[]} points 对齐方式
@param {Number[]} [offset] 偏移
*/
align:function (refNode, points, offset, overflow) {
refNode = $(refNode || win);
offset = offset && [].concat(offset) || [0, 0];
overflow = overflow || {};
var self = this,
el = self.get('el'),
fail = 0,
// 当前节点可以被放置的显示区域
visibleRect = getVisibleRectForElement(el[0]),
// 当前节点所占的区域, left/top/width/height
elRegion = getRegion(el),
// 参照节点所占的区域, left/top/width/height
refNodeRegion = getRegion(refNode),
// 当前节点将要被放置的位置
elFuturePos = getElFuturePos(elRegion, refNodeRegion, points, offset),
// 当前节点将要所处的区域
newElRegion = BUI.merge(elRegion, elFuturePos);
// 如果可视区域不能完全放置当前节点时允许调整
if (visibleRect && (overflow.adjustX || overflow.adjustY)) {
// 如果横向不能放下
if (isFailX(elFuturePos, elRegion, visibleRect)) {
fail = 1;
// 对齐位置反下
points = flip(points, /[lr]/ig, {
l:'r',
r:'l'
});
// 偏移量也反下
offset = flipOffset(offset, 0);
}
// 如果纵向不能放下
if (isFailY(elFuturePos, elRegion, visibleRect)) {
fail = 1;
// 对齐位置反下
points = flip(points, /[tb]/ig, {
t:'b',
b:'t'
});
// 偏移量也反下
offset = flipOffset(offset, 1);
}
// 如果失败,重新计算当前节点将要被放置的位置
if (fail) {
elFuturePos = getElFuturePos(elRegion, refNodeRegion, points, offset);
BUI.mix(newElRegion, elFuturePos);
}
var newOverflowCfg = {};
// 检查反下后的位置是否可以放下了
// 如果仍然放不下只有指定了可以调整当前方向才调整
newOverflowCfg.adjustX = overflow.adjustX &&
isFailX(elFuturePos, elRegion, visibleRect);
newOverflowCfg.adjustY = overflow.adjustY &&
isFailY(elFuturePos, elRegion, visibleRect);
// 确实要调整,甚至可能会调整高度宽度
if (newOverflowCfg.adjustX || newOverflowCfg.adjustY) {
newElRegion = adjustForViewport(elFuturePos, elRegion,
visibleRect, newOverflowCfg);
}
}
// 新区域位置发生了变化
if (newElRegion.left != elRegion.left) {
self.setInternal('x', null);
self.get('view').setInternal('x', null);
self.set('x', newElRegion.left);
}
if (newElRegion.top != elRegion.top) {
// https://github.com/kissyteam/kissy/issues/190
// 相对于屏幕位置没变,而 left/top 变了
// 例如
* control.center('#t1'); //控件处于容器#t1的中间位置
*
* @param {undefined|String|HTMLElement|jQuery} node
*
*/
center:function (node) {
var self = this;
self.set('align', {
node:node,
points:['cc', 'cc'],
offset:[0, 0]
});
return self;
}
};
module.exports = Align;
});
define("bui/common/component/uibase/autoshow", ["jquery"], function(require, exports, module){
/**
* @fileOverview click,focus,hover等引起控件显示,并且定位
* @ignore
*/
var $ = require("jquery");
/**
* 处理自动显示控件的扩展,一般用于显示menu,picker,tip等
* @class BUI.Component.UIBase.AutoShow
*/
function autoShow() {
}
autoShow.ATTRS = {
/**
* 触发显示控件的DOM选择器
*
* var overlay = new Overlay({ //点击#t1时显示,点击#t1,overlay之外的元素隐藏
* trigger : '#t1',
* autoHide : true,
* content : '悬浮内容'
* });
* overlay.render();
*
* @cfg {HTMLElement|String|jQuery} trigger
*/
/**
* 触发显示控件的DOM选择器
* @type {HTMLElement|String|jQuery}
*/
trigger: {
},
delegateTigger: {
getter: function() {
this.get('delegateTrigger'); //兼容之前的版本
},
setter: function(v) {
this.set('delegateTrigger', v);
}
},
/**
* 是否使用代理的方式触发显示控件,如果tigger不是字符串,此属性无效
*
* var overlay = new Overlay({ //点击.t1(无论创建控件时.t1是否存在)时显示,点击.t1,overlay之外的元素隐藏
* trigger : '.t1',
* autoHide : true,
* delegateTrigger : true, //使用委托的方式触发显示控件
* content : '悬浮内容'
* });
* overlay.render();
*
* @cfg {Boolean} [delegateTrigger = false]
*/
/**
* 是否使用代理的方式触发显示控件,如果tigger不是字符串,此属性无效
* @type {Boolean}
* @ignore
*/
delegateTrigger: {
value: false
},
/**
* 选择器是否始终跟随触发器对齐
* @cfg {Boolean} autoAlign
* @ignore
*/
/**
* 选择器是否始终跟随触发器对齐
* @type {Boolean}
* @protected
*/
autoAlign: {
value: true
},
/**
* 显示时是否默认获取焦点
* @type {Boolean}
*/
autoFocused: {
value: true
},
/**
* 如果设置了这个样式,那么触发显示(overlay)时trigger会添加此样式
* @type {Object}
*/
triggerActiveCls: {
},
/**
* 控件显示时由此trigger触发,当配置项 trigger 选择器代表多个DOM 对象时,
* 控件可由多个DOM对象触发显示。
*
* overlay.on('show',function(){
* var curTrigger = overlay.get('curTrigger');
* //TO DO
* });
*
* @type {jQuery}
* @readOnly
*/
curTrigger: {
},
/**
* 触发显示时的回调函数
* @cfg {Function} triggerCallback
* @ignore
*/
/**
* 触发显示时的回调函数
* @type {Function}
* @ignore
*/
triggerCallback: {
},
/**
* 显示菜单的事件
*
* var overlay = new Overlay({ //移动到#t1时显示,移动出#t1,overlay之外控件隐藏
* trigger : '#t1',
* autoHide : true,
* triggerEvent :'mouseover',
* autoHideType : 'leave',
* content : '悬浮内容'
* });
* overlay.render();
*
*
* @cfg {String} [triggerEvent='click']
* @default 'click'
*/
/**
* 显示菜单的事件
* @type {String}
* @default 'click'
* @ignore
*/
triggerEvent: {
value: 'click'
},
/**
* 因为触发元素发生改变而导致控件隐藏
* @cfg {String} triggerHideEvent
* @ignore
*/
/**
* 因为触发元素发生改变而导致控件隐藏
* @type {String}
* @ignore
*/
triggerHideEvent: {
},
events: {
value: {
/**
* 当触发器(触发选择器出现)发生改变时,经常用于一个选择器对应多个触发器的情况
*
* overlay.on('triggerchange',function(ev){
* var curTrigger = ev.curTrigger;
* overlay.set('content',curTrigger.html());
* });
*
* @event
* @param {Object} e 事件对象
* @param {jQuery} e.prevTrigger 之前触发器,可能为null
* @param {jQuery} e.curTrigger 当前的触发器
*/
'triggerchange': false
}
}
};
autoShow.prototype = {
__createDom: function() {
this._setTrigger();
},
__bindUI: function() {
var _self = this,
triggerActiveCls = _self.get('triggerActiveCls');
if (triggerActiveCls) {
_self.on('hide', function() {
var curTrigger = _self.get('curTrigger');
if (curTrigger) {
curTrigger.removeClass(triggerActiveCls);
}
});
}
},
_setTrigger: function() {
var _self = this,
triggerEvent = _self.get('triggerEvent'),
triggerHideEvent = _self.get('triggerHideEvent'),
triggerCallback = _self.get('triggerCallback'),
triggerActiveCls = _self.get('triggerActiveCls') || '',
trigger = _self.get('trigger'),
isDelegate = _self.get('delegateTrigger'),
triggerEl = $(trigger);
//触发显示
function tiggerShow(ev) {
if (_self.get('disabled')) { //如果禁用则中断
return;
}
var prevTrigger = _self.get('curTrigger'),
curTrigger = isDelegate ? $(ev.currentTarget) : $(this),
align = _self.get('align');
if (!prevTrigger || prevTrigger[0] != curTrigger[0]) {
if (prevTrigger) {
prevTrigger.removeClass(triggerActiveCls);
}
_self.set('curTrigger', curTrigger);
_self.fire('triggerchange', {
prevTrigger: prevTrigger,
curTrigger: curTrigger
});
}
curTrigger.addClass(triggerActiveCls);
if (_self.get('autoAlign')) {
align.node = curTrigger;
}
_self.set('align', align);
_self.show();
triggerCallback && triggerCallback(ev);
}
//触发隐藏
function tiggerHide(ev) {
var toElement = ev.toElement || ev.relatedTarget;
if (!toElement || !_self.containsElement(toElement)) { //mouseleave时,如果移动到当前控件上,取消消失
_self.hide();
}
}
if (triggerEvent) {
if (isDelegate && BUI.isString(trigger)) {
$(document).delegate(trigger, triggerEvent, tiggerShow);
} else {
triggerEl.on(triggerEvent, tiggerShow);
}
}
if (triggerHideEvent) {
if (isDelegate && BUI.isString(trigger)) {
$(document).delegate(trigger, triggerHideEvent, tiggerHide);
} else {
triggerEl.on(triggerHideEvent, tiggerHide);
}
}
},
__renderUI: function() {
var _self = this,
align = _self.get('align');
//如果控件显示时不是由trigger触发,则同父元素对齐
if (align && !align.node) {
align.node = _self.get('render') || _self.get('trigger');
}
}
};
module.exports = autoShow;
});
define("bui/common/component/uibase/autohide", ["jquery"], function(require, exports, module){
/**
* @fileOverview 点击或移出控件外部,控件隐藏
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
wrapBehavior = BUI.wrapBehavior,
getWrapBehavior = BUI.getWrapBehavior;
function isExcept(self, elem) {
var hideExceptNode = self.get('hideExceptNode');
if (hideExceptNode && hideExceptNode.length) {
return $.contains(hideExceptNode[0], elem);
}
return false;
}
/**
* 点击隐藏控件的扩展
* @class BUI.Component.UIBase.AutoHide
*/
function autoHide() {
}
autoHide.ATTRS = {
/**
* 控件自动隐藏的事件,这里支持2种:
* - 'click'
* - 'leave'
*
* var overlay = new Overlay({ //点击#t1时显示,点击#t1之外的元素隐藏
* trigger : '#t1',
* autoHide : true,
* content : '悬浮内容'
* });
* overlay.render();
*
* var overlay = new Overlay({ //移动到#t1时显示,移动出#t1,overlay之外控件隐藏
* trigger : '#t1',
* autoHide : true,
* triggerEvent :'mouseover',
* autoHideType : 'leave',
* content : '悬浮内容'
* });
* overlay.render();
*
*
* @cfg {String} [autoHideType = 'click']
*/
/**
* 控件自动隐藏的事件,这里支持2种:
* 'click',和'leave',默认为'click'
* @type {String}
*/
autoHideType: {
value: 'click'
},
/**
* 是否自动隐藏
*
*
* var overlay = new Overlay({ //点击#t1时显示,点击#t1,overlay之外的元素隐藏
* trigger : '#t1',
* autoHide : true,
* content : '悬浮内容'
* });
* overlay.render();
*
* @cfg {Object} autoHide
*/
/**
* 是否自动隐藏
* @type {Object}
* @ignore
*/
autoHide: {
value: false
},
/**
* 点击或者移动到此节点时不触发自动隐藏
*
*
* var overlay = new Overlay({ //点击#t1时显示,点击#t1,#t2,overlay之外的元素隐藏
* trigger : '#t1',
* autoHide : true,
* hideExceptNode : '#t2',
* content : '悬浮内容'
* });
* overlay.render();
*
* @cfg {Object} hideExceptNode
*/
hideExceptNode: {
},
events: {
value: {
/**
* @event autohide
* 点击控件外部时触发,只有在控件设置自动隐藏(autoHide = true)有效
* 可以阻止控件隐藏,通过在事件监听函数中 return false
*
* overlay.on('autohide',function(){
* var curTrigger = overlay.curTrigger; //当前触发的项
* if(condtion){
* return false; //阻止隐藏
* }
* });
*
*/
autohide: false
}
}
};
autoHide.prototype = {
__bindUI: function() {
var _self = this;
_self.on('afterVisibleChange', function(ev) {
var visible = ev.newVal;
if (_self.get('autoHide')) {
if (visible) {
_self._bindHideEvent();
} else {
_self._clearHideEvent();
}
}
});
},
/**
* 处理鼠标移出事件,不影响{BUI.Component.Controller#handleMouseLeave}事件
* @param {jQuery.Event} ev 事件对象
*/
handleMoveOuter: function(ev) {
var _self = this,
target = ev.toElement || ev.relatedTarget;
if (!_self.containsElement(target) && !isExcept(_self, target)) {
if (_self.fire('autohide') !== false) {
_self.hide();
}
}
},
/**
* 点击页面时的处理函数
* @param {jQuery.Event} ev 事件对象
* @protected
*/
handleDocumentClick: function(ev) {
var _self = this,
target = ev.target;
if (!_self.containsElement(target) && !isExcept(_self, target)) {
if (_self.fire('autohide') !== false) {
_self.hide();
}
}
},
_bindHideEvent: function() {
var _self = this,
trigger = _self.get('curTrigger'),
autoHideType = _self.get('autoHideType');
if (autoHideType === 'click') {
$(document).on('mousedown', wrapBehavior(_self, 'handleDocumentClick'));
} else {
_self.get('el').on('mouseleave', wrapBehavior(_self, 'handleMoveOuter'));
if (trigger) {
$(trigger).on('mouseleave', wrapBehavior(_self, 'handleMoveOuter'))
}
}
},
//清除绑定的隐藏事件
_clearHideEvent: function() {
var _self = this,
trigger = _self.get('curTrigger'),
autoHideType = _self.get('autoHideType');
if (autoHideType === 'click') {
$(document).off('mousedown', getWrapBehavior(_self, 'handleDocumentClick'));
} else {
_self.get('el').off('mouseleave', getWrapBehavior(_self, 'handleMoveOuter'));
if (trigger) {
$(trigger).off('mouseleave', getWrapBehavior(_self, 'handleMoveOuter'))
}
}
}
};
module.exports = autoHide;
});
define("bui/common/component/uibase/close", ["jquery"], function(require, exports, module){
/**
* @fileOverview close 关闭或隐藏控件
* @author yiminghe@gmail.com
* copied and modified by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery");
var CLS_PREFIX = BUI.prefix + 'ext-';
function getCloseRenderBtn(self) {
return $(self.get('closeTpl'));
}
/**
* 关闭按钮的视图类
* @class BUI.Component.UIBase.CloseView
* @private
*/
function CloseView() {}
CloseView.ATTRS = {
closeTpl: {
value: '' +
'关闭<' + '/span>' +
'<' + '/a>'
},
closeable: {
value: true
},
closeBtn: {}
};
CloseView.prototype = {
_uiSetCloseable: function(v) {
var self = this,
btn = self.get('closeBtn');
if (v) {
if (!btn) {
self.setInternal('closeBtn', btn = getCloseRenderBtn(self));
}
btn.appendTo(self.get('el'), undefined);
} else {
if (btn) {
btn.remove();
}
}
}
};
/**
* @class BUI.Component.UIBase.Close
* Close extension class.
* Represent a close button.
*/
function Close() {}
var HIDE = 'hide';
Close.ATTRS = {
/**
* 关闭按钮的默认模版
*
* var overlay = new Overlay({
* closeTpl : 'x',
* closeable : true,
* trigger : '#t1'
* });
* overlay.render();
*
* @cfg {String} closeTpl
*/
/**
* 关闭按钮的默认模版
* @type {String}
* @protected
*/
closeTpl: {
view: true
},
/**
* 是否出现关闭按钮
* @cfg {Boolean} [closeable = false]
*/
/**
* 是否出现关闭按钮
* @type {Boolean}
*/
closeable: {
view: 1
},
/**
* 关闭按钮.
* @protected
* @type {jQuery}
*/
closeBtn: {
view: 1
},
/**
* 关闭时隐藏还是移除DOM结构
* var Control = Overlay.extend([UIBase.Drag],{
*
* });
*
* var c = new Contol({ //拖动控件时,在#t2内
* content : '',
* dragNode : '#header',
* constraint : '#t2'
* });
*
* @class BUI.Component.UIBase.Drag
*/
var drag = function() {
};
drag.ATTRS = {
/**
* 点击拖动的节点
*
* var Control = Overlay.extend([UIBase.Drag],{
*
* });
*
* var c = new Contol({ //拖动控件时,在#t2内
* content : '',
* dragNode : '#header',
* constraint : '#t2'
* });
*
* @cfg {jQuery} dragNode
*/
/**
* 点击拖动的节点
* @type {jQuery}
* @ignore
*/
dragNode: {
},
/**
* 是否正在拖动
* @type {Boolean}
* @protected
*/
draging: {
setter: function(v) {
if (v === true) {
return {};
}
},
value: null
},
/**
* 拖动的限制范围
*
* var Control = Overlay.extend([UIBase.Drag],{
*
* });
*
* var c = new Contol({ //拖动控件时,在#t2内
* content : '',
* dragNode : '#header',
* constraint : '#t2'
* });
*
* @cfg {jQuery} constraint
*/
/**
* 拖动的限制范围
* @type {jQuery}
* @ignore
*/
constraint: {
},
/**
* @private
* @type {jQuery}
*/
dragBackEl: {
/** @private **/
getter: function() {
return $('#' + dragBackId);
}
}
};
var dragTpl = '';
function initBack() {
var el = $(dragTpl).css('opacity', 0).prependTo('body');
return el;
}
drag.prototype = {
__bindUI: function() {
var _self = this,
constraint = _self.get('constraint'),
dragNode = _self.get('dragNode');
if (!dragNode) {
return;
}
dragNode.on('mousedown', function(e) {
if (e.which == 1) {
e.preventDefault();
_self.set('draging', {
elX: _self.get('x'),
elY: _self.get('y'),
startX: e.pageX,
startY: e.pageY
});
registEvent();
}
});
/**
* @private
*/
function mouseMove(e) {
var draging = _self.get('draging');
if (draging) {
e.preventDefault();
_self._dragMoveTo(e.pageX, e.pageY, draging, constraint);
}
}
/**
* @private
*/
function mouseUp(e) {
if (e.which == 1) {
_self.set('draging', false);
var dragBackEl = _self.get('dragBackEl');
if (dragBackEl) {
dragBackEl.hide();
}
unregistEvent();
}
}
/**
* @private
*/
function registEvent() {
$(document).on('mousemove', mouseMove);
$(document).on('mouseup', mouseUp);
}
/**
* @private
*/
function unregistEvent() {
$(document).off('mousemove', mouseMove);
$(document).off('mouseup', mouseUp);
}
},
_dragMoveTo: function(x, y, draging, constraint) {
var _self = this,
dragBackEl = _self.get('dragBackEl'),
draging = draging || _self.get('draging'),
offsetX = draging.startX - x,
offsetY = draging.startY - y;
if (!dragBackEl.length) {
dragBackEl = initBack();
}
dragBackEl.css({
cursor: 'move',
display: 'block'
});
_self.set('xy', [_self._getConstrainX(draging.elX - offsetX, constraint),
_self._getConstrainY(draging.elY - offsetY, constraint)
]);
},
_getConstrainX: function(x, constraint) {
var _self = this,
width = _self.get('el').outerWidth(),
endX = x + width,
curX = _self.get('x');
//如果存在约束
if (constraint) {
var constraintOffset = constraint.offset();
if (constraintOffset.left >= x) {
return constraintOffset.left;
}
if (constraintOffset.left + constraint.width() < endX) {
return constraintOffset.left + constraint.width() - width;
}
return x;
}
//当左右顶点都在视图内,移动到此点
if (BUI.isInHorizontalView(x) && BUI.isInHorizontalView(endX)) {
return x;
}
return curX;
},
_getConstrainY: function(y, constraint) {
var _self = this,
height = _self.get('el').outerHeight(),
endY = y + height,
curY = _self.get('y');
//如果存在约束
if (constraint) {
var constraintOffset = constraint.offset();
if (constraintOffset.top > y) {
return constraintOffset.top;
}
if (constraintOffset.top + constraint.height() < endY) {
return constraintOffset.top + constraint.height() - height;
}
return y;
}
//当左右顶点都在视图内,移动到此点
if (BUI.isInVerticalView(y) && BUI.isInVerticalView(endY)) {
return y;
}
return curY;
}
};
module.exports = drag;
});
define("bui/common/component/uibase/keynav", ["jquery"], function(require, exports, module){
/**
* @fileOverview 使用键盘导航
* @ignore
*/
var $ = require("jquery"),
KeyCode = require("bui/common/keycode"),
wrapBehavior = BUI.wrapBehavior,
getWrapBehavior = BUI.getWrapBehavior;
/**
* 键盘导航
* @class BUI.Component.UIBase.KeyNav
*/
var keyNav = function() {
};
keyNav.ATTRS = {
/**
* 是否允许键盘导航
* @cfg {Boolean} [allowKeyNav = true]
*/
allowKeyNav: {
value: true
},
/**
* 导航使用的事件
* @cfg {String} [navEvent = 'keydown']
*/
navEvent: {
value: 'keydown'
},
/**
* 当获取事件的DOM是 input,textarea,select等时,不处理键盘导航
* @cfg {Object} [ignoreInputFields='true']
*/
ignoreInputFields: {
value: true
}
};
keyNav.prototype = {
__bindUI: function() {
},
_uiSetAllowKeyNav: function(v) {
var _self = this,
eventName = _self.get('navEvent'),
el = _self.get('el');
if (v) {
el.on(eventName, wrapBehavior(_self, '_handleKeyDown'));
} else {
el.off(eventName, getWrapBehavior(_self, '_handleKeyDown'));
}
},
/**
* 处理键盘导航
* @private
*/
_handleKeyDown: function(ev) {
var _self = this,
ignoreInputFields = _self.get('ignoreInputFields'),
code = ev.which;
if (ignoreInputFields && $(ev.target).is('input,select,textarea')) {
return;
}
switch (code) {
case KeyCode.UP:
//ev.preventDefault();
_self.handleNavUp(ev);
break;
case KeyCode.DOWN:
//ev.preventDefault();
_self.handleNavDown(ev);
break;
case KeyCode.RIGHT:
// ev.preventDefault();
_self.handleNavRight(ev);
break;
case KeyCode.LEFT:
//ev.preventDefault();
_self.handleNavLeft(ev);
break;
case KeyCode.ENTER:
_self.handleNavEnter(ev);
break;
case KeyCode.ESC:
_self.handleNavEsc(ev);
break;
case KeyCode.TAB:
_self.handleNavTab(ev);
break;
default:
break;
}
},
/**
* 处理向上导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavUp: function(ev) {
// body...
},
/**
* 处理向下导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavDown: function(ev) {
// body...
},
/**
* 处理向左导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavLeft: function(ev) {
// body...
},
/**
* 处理向右导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavRight: function(ev) {
// body...
},
/**
* 处理确认键
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavEnter: function(ev) {
// body...
},
/**
* 处理 esc 键
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavEsc: function(ev) {
// body...
},
/**
* 处理Tab键
* @param {jQuery.Event} ev 事件对象
*/
handleNavTab: function(ev) {
}
};
module.exports = keyNav;
});
define("bui/common/component/uibase/list", ["jquery"], function(require, exports, module){
/**
* @fileOverview 所有子元素都是同一类的集合
* @ignore
*/
var $ = require("jquery"),
Selection = require("bui/common/component/uibase/selection");
/**
* 列表一类的控件的扩展,list,menu,grid都是可以从此类扩展
* @class BUI.Component.UIBase.List
*/
var list = function() {
};
list.ATTRS = {
/**
* 选择的数据集合
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>',
* idField : 'value',
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
* list.render();
*
* @cfg {Array} items
*/
/**
* 选择的数据集合
*
* list.set('items',items); //列表会直接替换内容
* //等同于
* list.clearItems();
* list.addItems(items);
*
* @type {Array}
*/
items: {
shared: false,
view: true
},
/**
* 选项的默认key值
* @cfg {String} [idField = 'id']
*/
idField: {
value: 'id'
},
/**
* 列表项的默认模板,仅在初始化时传入。
* @type {String}
* @ignore
*/
itemTpl: {
view: true
},
/**
* 列表项的渲染函数,应对列表项之间有很多差异时
*
* var list = new List.SimpleList({
* itemTplRender : function(item){
* if(item.type == '1'){
* return '<li><img src="xxx.jpg"/>'+item.text+'</li>'
* }else{
* return '<li>item.text</li>'
* }
* },
* idField : 'value',
* render : '#t1',
* items : [{value : '1',text : '1',type : '0'},{value : '2',text : '2',type : '1'}]
* });
* list.render();
*
* @type {Function}
*/
itemTplRender: {
view: true
},
/**
* 子控件各个状态默认采用的样式
*
* var list = new List.SimpleList({
* render : '#t1',
* itemStatusCls : {
* selected : 'active', //默认样式为list-item-selected,现在变成'active'
* hover : 'hover' //默认样式为list-item-hover,现在变成'hover'
* },
* items : [{id : '1',text : '1',type : '0'},{id : '2',text : '2',type : '1'}]
* });
* list.render();
*
* see {@link BUI.Component.Controller#property-statusCls}
* @type {Object}
*/
itemStatusCls: {
view: true,
value: {}
},
events: {
value: {
/**
* 选项点击事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.UIBase.ListItem} e.item 点击的选项
* @param {HTMLElement} e.element 选项代表的DOM对象
* @param {HTMLElement} e.domTarget 点击的DOM对象
* @param {HTMLElement} e.domEvent 点击的原生事件对象
*/
'itemclick': true
}
}
};
list.prototype = {
/**
* 获取选项的数量
*
* var count = list.getItemCount();
*
* @return {Number} 选项数量
*/
getItemCount: function() {
return this.getItems().length;
},
/**
* 获取字段的值
* @param {*} item 字段名
* @param {String} field 字段名
* @return {*} 字段的值
* @protected
*/
getValueByField: function(item, field) {
},
/**
* 获取所有选项值,如果选项是子控件,则是所有子控件
*
* var items = list.getItems();
* //等同
* list.get(items);
*
* @return {Array} 选项值集合
*/
getItems: function() {
},
/**
* 获取第一项
*
* var item = list.getFirstItem();
* //等同
* list.getItemAt(0);
*
* @return {Object|BUI.Component.Controller} 选项值(子控件)
*/
getFirstItem: function() {
return this.getItemAt(0);
},
/**
* 获取最后一项
*
* var item = list.getLastItem();
* //等同
* list.getItemAt(list.getItemCount()-1);
*
* @return {Object|BUI.Component.Controller} 选项值(子控件)
*/
getLastItem: function() {
return this.getItemAt(this.getItemCount() - 1);
},
/**
* 通过索引获取选项值(子控件)
*
* var item = list.getItemAt(0); //获取第1个
* var item = list.getItemAt(2); //获取第3个
*
* @param {Number} index 索引值
* @return {Object|BUI.Component.Controller} 选项(子控件)
*/
getItemAt: function(index) {
return this.getItems()[index] || null;
},
/**
* 通过Id获取选项,如果是改变了idField则通过改变的idField来查找选项
*
* //如果idField = 'id'
* var item = list.getItem('2');
* //等同于
* list.findItemByField('id','2');
*
* //如果idField = 'value'
* var item = list.getItem('2');
* //等同于
* list.findItemByField('value','2');
*
* @param {String} id 编号
* @return {Object|BUI.Component.Controller} 选项(子控件)
*/
getItem: function(id) {
var field = this.get('idField');
return this.findItemByField(field, id);
},
/**
* 返回指定项的索引
*
* var index = list.indexOf(item); //返回索引,不存在则返回-1
*
* @param {Object|BUI.Component.Controller} item 选项
* @return {Number} 项的索引值
*/
indexOfItem: function(item) {
return BUI.Array.indexOf(item, this.getItems());
},
/**
* 添加多条选项
*
* var items = [{id : '1',text : '1'},{id : '2',text : '2'}];
* list.addItems(items);
*
* @param {Array} items 记录集合(子控件配置项)
*/
addItems: function(items) {
var _self = this;
BUI.each(items, function(item) {
_self.addItem(item);
});
},
/**
* 插入多条记录
*
* var items = [{id : '1',text : '1'},{id : '2',text : '2'}];
* list.addItemsAt(items,0); // 在最前面插入
* list.addItemsAt(items,2); //第三个位置插入
*
* @param {Array} items 多条记录
* @param {Number} start 起始位置
*/
addItemsAt: function(items, start) {
var _self = this;
BUI.each(items, function(item, index) {
_self.addItemAt(item, start + index);
});
},
/**
* 更新列表项,修改选项值后,DOM跟随变化
*
* var item = list.getItem('2');
* list.text = '新内容'; //此时对应的DOM不会变化
* list.updateItem(item); //DOM进行相应的变化
*
* @param {Object} item 选项值
*/
updateItem: function(item) {
},
/**
* 添加选项,添加在控件最后
*
*
* list.addItem({id : '3',text : '3',type : '0'});
*
*
* @param {Object|BUI.Component.Controller} item 选项,子控件配置项、子控件
* @return {Object|BUI.Component.Controller} 子控件或者选项记录
*/
addItem: function(item) {
return this.addItemAt(item, this.getItemCount());
},
/**
* 在指定位置添加选项
*
* list.addItemAt({id : '3',text : '3',type : '0'},0); //第一个位置
*
* @param {Object|BUI.Component.Controller} item 选项,子控件配置项、子控件
* @param {Number} index 索引
* @return {Object|BUI.Component.Controller} 子控件或者选项记录
*/
addItemAt: function(item, index) {
},
/**
* 根据字段查找指定的项
* @param {String} field 字段名
* @param {Object} value 字段值
* @return {Object} 查询出来的项(传入的记录或者子控件)
* @protected
*/
findItemByField: function(field, value) {
},
/**
*
* 获取此项显示的文本
* @param {Object} item 获取记录显示的文本
* @protected
*/
getItemText: function(item) {
},
/**
* 清除所有选项,不等同于删除全部,此时不会触发删除事件
*
* list.clearItems();
* //等同于
* list.set('items',items);
*
*/
clearItems: function() {
var _self = this,
items = _self.getItems();
items.splice(0);
_self.clearControl();
},
/**
* 删除选项
*
* var item = list.getItem('1');
* list.removeItem(item);
*
* @param {Object|BUI.Component.Controller} item 选项(子控件)
*/
removeItem: function(item) {
},
/**
* 移除选项集合
*
* var items = list.getSelection();
* list.removeItems(items);
*
* @param {Array} items 选项集合
*/
removeItems: function(items) {
var _self = this;
BUI.each(items, function(item) {
_self.removeItem(item);
});
},
/**
* 通过索引删除选项
*
* list.removeItemAt(0); //删除第一个
*
* @param {Number} index 索引
*/
removeItemAt: function(index) {
this.removeItem(this.getItemAt(index));
},
/**
* @protected
* @template
* 清除所有的子控件或者列表项的DOM
*/
clearControl: function() {
}
}
function clearSelected(item) {
if (item.selected) {
item.selected = false;
}
if (item.set) {
item.set('selected', false);
}
}
function beforeAddItem(self, item) {
var c = item.isController ? item.getAttrVals() : item,
defaultTpl = self.get('itemTpl'),
defaultStatusCls = self.get('itemStatusCls'),
defaultTplRender = self.get('itemTplRender');
//配置默认模板
if (defaultTpl && !c.tpl) {
setItemAttr(item, 'tpl', defaultTpl);
// c.tpl = defaultTpl;
}
//配置默认渲染函数
if (defaultTplRender && !c.tplRender) {
setItemAttr(item, 'tplRender', defaultTplRender);
//c.tplRender = defaultTplRender;
}
//配置默认状态样式
if (defaultStatusCls) {
var statusCls = c.statusCls || item.isController ? item.get('statusCls') : {};
BUI.each(defaultStatusCls, function(v, k) {
if (v && !statusCls[k]) {
statusCls[k] = v;
}
});
setItemAttr(item, 'statusCls', statusCls)
//item.statusCls = statusCls;
}
// clearSelected(item);
}
function setItemAttr(item, name, val) {
if (item.isController) {
item.set(name, val);
} else {
item[name] = val;
}
}
/**
* @class BUI.Component.UIBase.ChildList
* 选中其中的DOM结构
* @extends BUI.Component.UIBase.List
* @mixins BUI.Component.UIBase.Selection
*/
var childList = function() {
this.__init();
};
childList.ATTRS = BUI.merge(true, list.ATTRS, Selection.ATTRS, {
items: {
sync: false
},
/**
* 配置的items 项是在初始化时作为children
* @protected
* @type {Boolean}
*/
autoInitItems: {
value: true
},
/**
* 使用srcNode时,是否将内部的DOM转换成子控件
* @type {Boolean}
*/
isDecorateChild: {
value: true
},
/**
* 默认的加载控件内容的配置,默认值:
* * { * property : 'children', * dataType : 'json' * } ** @type {Object} */ defaultLoaderCfg: { value: { property: 'children', dataType: 'json' } } }); BUI.augment(childList, list, Selection, { //初始化,将items转换成children __init: function() { var _self = this, items = _self.get('items'); if (items && _self.get('autoInitItems')) { _self.addItems(items); } _self.on('beforeRenderUI', function() { _self._beforeRenderUI(); }); }, _uiSetItems: function(items) { var _self = this; //清理子控件 _self.clearControl(); _self.addItems(items); }, //渲染子控件 _beforeRenderUI: function() { var _self = this, children = _self.get('children'), items = _self.get('items'); BUI.each(children, function(item) { beforeAddItem(_self, item); }); }, //绑定事件 __bindUI: function() { var _self = this, selectedEvent = _self.get('selectedEvent'); _self.on(selectedEvent, function(e) { var item = e.target; if (item.get('selectable')) { if (!item.get('selected')) { _self.setSelected(item); } else if (_self.get('multipleSelect')) { _self.clearSelected(item); } } }); _self.on('click', function(e) { if (e.target !== _self) { _self.fire('itemclick', { item: e.target, domTarget: e.domTarget, domEvent: e }); } }); _self.on('beforeAddChild', function(ev) { beforeAddItem(_self, ev.child); }); _self.on('beforeRemoveChild', function(ev) { var item = ev.child, selected = item.get('selected'); //清理选中状态 if (selected) { if (_self.get('multipleSelect')) { _self.clearSelected(item); } else { _self.setSelected(null); } } item.set('selected', false); }); }, /** * @protected * @override * 清除者列表项的DOM */ clearControl: function() { this.removeChildren(true); }, /** * 获取所有子控件 * @return {Array} 子控件集合 * @override */ getItems: function() { return this.get('children'); }, /** * 更新列表项 * @param {Object} item 选项值 */ updateItem: function(item) { var _self = this, idField = _self.get('idField'), element = _self.findItemByField(idField, item[idField]); if (element) { element.setTplContent(); } return element; }, /** * 删除项,子控件作为选项 * @param {Object} element 子控件 */ removeItem: function(item) { var _self = this, idField = _self.get('idField'); if (!(item instanceof BUI.Component.Controller)) { item = _self.findItemByField(idField, item[idField]); } this.removeChild(item, true); }, /** * 在指定位置添加选项,此处选项指子控件 * @param {Object|BUI.Component.Controller} item 子控件配置项、子控件 * @param {Number} index 索引 * @return {Object|BUI.Component.Controller} 子控件 */ addItemAt: function(item, index) { return this.addChild(item, index); }, findItemByField: function(field, value, root) { root = root || this; var _self = this, children = root.get('children'), result = null; $(children).each(function(index, item) { if (item.get(field) == value) { result = item; } else if (item.get('children').length) { result = _self.findItemByField(field, value, item); } if (result) { return false; } }); return result; }, getItemText: function(item) { return item.get('el').text(); }, getValueByField: function(item, field) { return item && item.get(field); }, /** * @protected * @ignore */ setItemSelectedStatus: function(item, selected) { var _self = this, method = selected ? 'addClass' : 'removeClass', element = null; if (item) { item.set('selected', selected); element = item.get('el'); } _self.afterSelected(item, selected, element); }, /** * 选项是否被选中 * @override * @param {*} item 选项 * @return {Boolean} 是否选中 */ isItemSelected: function(item) { return item ? item.get('selected') : false; }, /** * 设置所有选项选中 * @override */ setAllSelection: function() { var _self = this, items = _self.getItems(); _self.setSelection(items); }, /** * 获取选中的项的值 * @return {Array} * @override * @ignore */ getSelection: function() { var _self = this, items = _self.getItems(), rst = []; BUI.each(items, function(item) { if (_self.isItemSelected(item)) { rst.push(item); } }); return rst; } }); list.ChildList = childList; module.exports = list; /** * @ignore * 2013-1-22 * 更改显示数据的方式,使用 _uiSetItems */ }); define("bui/common/component/uibase/selection", ["jquery"], function(require, exports, module){ /** * @fileOverview 单选或者多选 * @author dxq613@gmail.com * @ignore */ var $ = require("jquery"); var SINGLE_SELECTED = 'single'; /** * @class BUI.Component.UIBase.Selection * 选中控件中的项(子元素或者DOM),此类选择的内容有2种 *
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>',
* idField : 'value',
* selectedEvent : 'mouseenter',
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
*
* @cfg {String} [selectedEvent = 'click']
*/
selectedEvent: {
value: 'click'
},
events: {
value: {
/**
* 选中的菜单改变时发生,
* 多选时,选中,取消选中都触发此事件,单选时,只有选中时触发此事件
* @name BUI.Component.UIBase.Selection#selectedchange
* @event
* @param {Object} e 事件对象
* @param {Object} e.item 当前选中的项
* @param {HTMLElement} e.domTarget 当前选中的项的DOM结构
* @param {Boolean} e.selected 是否选中
*/
'selectedchange': false,
/**
* 选择改变前触发,可以通过return false,阻止selectedchange事件
* @event
* @param {Object} e 事件对象
* @param {Object} e.item 当前选中的项
* @param {Boolean} e.selected 是否选中
*/
'beforeselectedchange': false,
/**
* 菜单选中
* @event
* @param {Object} e 事件对象
* @param {Object} e.item 当前选中的项
* @param {HTMLElement} e.domTarget 当前选中的项的DOM结构
*/
'itemselected': false,
/**
* 菜单取消选中
* @event
* @param {Object} e 事件对象
* @param {Object} e.item 当前选中的项
* @param {HTMLElement} e.domTarget 当前选中的项的DOM结构
*/
'itemunselected': false
}
},
/**
* 数据的id字段名称,通过此字段查找对应的数据
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>',
* idField : 'value',
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
*
* @cfg {String} [idField = 'id']
*/
/**
* 数据的id字段名称,通过此字段查找对应的数据
* @type {String}
* @ignore
*/
idField: {
value: 'id'
},
/**
* 是否多选
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>',
* idField : 'value',
* render : '#t1',
* multipleSelect : true,
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
*
* @cfg {Boolean} [multipleSelect=false]
*/
/**
* 是否多选
* @type {Boolean}
* @default false
*/
multipleSelect: {
value: false
}
};
selection.prototype =
{
/**
* 清理选中的项
*
* list.clearSelection();
*
*
*/
clearSelection: function() {
var _self = this,
selection = _self.getSelection();
BUI.each(selection, function(item) {
_self.clearSelected(item);
});
},
/**
* 获取选中的项的值
* @template
* @return {Array}
*/
getSelection: function() {
},
/**
* 获取选中的第一项
*
* var item = list.getSelected(); //多选模式下第一条
*
* @return {Object} 选中的第一项或者为undefined
*/
getSelected: function() {
return this.getSelection()[0];
},
/**
* 根据 idField 获取到的值
* @protected
* @return {Object} 选中的值
*/
getSelectedValue: function() {
var _self = this,
field = _self.get('idField'),
item = _self.getSelected();
return _self.getValueByField(item, field);
},
/**
* 获取选中的值集合
* @protected
* @return {Array} 选中值得集合
*/
getSelectionValues: function() {
var _self = this,
field = _self.get('idField'),
items = _self.getSelection();
return $.map(items, function(item) {
return _self.getValueByField(item, field);
});
},
/**
* 获取选中的文本
* @protected
* @return {Array} 选中的文本集合
*/
getSelectionText: function() {
var _self = this,
items = _self.getSelection();
return $.map(items, function(item) {
return _self.getItemText(item);
});
},
/**
* 移除选中
*
* var item = list.getItem('id'); //通过id 获取选项
* list.setSelected(item); //选中
*
* list.clearSelected();//单选模式下清除所选,多选模式下清除选中的第一项
* list.clearSelected(item); //清除选项的选中状态
*
* @param {Object} [item] 清除选项的选中状态,如果未指定则清除选中的第一个选项的选中状态
*/
clearSelected: function(item) {
var _self = this;
item = item || _self.getSelected();
if (item) {
_self.setItemSelected(item, false);
}
},
/**
* 获取选项显示的文本
* @protected
*/
getSelectedText: function() {
var _self = this,
item = _self.getSelected();
return _self.getItemText(item);
},
/**
* 设置选中的项
*
* var items = list.getItemsByStatus('active'); //获取某种状态的选项
* list.setSelection(items);
*
* @param {Array} items 项的集合
*/
setSelection: function(items) {
var _self = this;
items = BUI.isArray(items) ? items : [items];
BUI.each(items, function(item) {
_self.setSelected(item);
});
},
/**
* 设置选中的项
*
* var item = list.getItem('id');
* list.setSelected(item);
*
* @param {Object} item 记录或者子控件
*/
setSelected: function(item) {
var _self = this,
multipleSelect = _self.get('multipleSelect');
if (!_self.isItemSelectable(item)) {
return;
}
if (!multipleSelect) {
var selectedItem = _self.getSelected();
if (item != selectedItem) {
//如果是单选,清除已经选中的项
_self.clearSelected(selectedItem);
}
}
_self.setItemSelected(item, true);
},
/**
* 选项是否被选中
* @template
* @param {*} item 选项
* @return {Boolean} 是否选中
*/
isItemSelected: function(item) {
},
/**
* 选项是否可以选中
* @protected
* @param {*} item 选项
* @return {Boolean} 选项是否可以选中
*/
isItemSelectable: function(item) {
return true;
},
/**
* 设置选项的选中状态
* @param {*} item 选项
* @param {Boolean} selected 选中或者取消选中
* @protected
*/
setItemSelected: function(item, selected) {
var _self = this,
isSelected;
//当前状态等于要设置的状态时,不触发改变事件
if (item) {
isSelected = _self.isItemSelected(item);
if (isSelected == selected) {
return;
}
}
if (_self.fire('beforeselectedchange', {
item: item,
selected: selected
}) !== false) {
_self.setItemSelectedStatus(item, selected);
}
},
/**
* 设置选项的选中状态
* @template
* @param {*} item 选项
* @param {Boolean} selected 选中或者取消选中
* @protected
*/
setItemSelectedStatus: function(item, selected) {
},
/**
* 设置所有选项选中
*
* list.setAllSelection(); //选中全部,多选状态下有效
*
* @template
*/
setAllSelection: function() {
},
/**
* 设置项选中,通过字段和值
* @param {String} field 字段名,默认为配置项'idField',所以此字段可以不填写,仅填写值
* @param {Object} value 值
* @example
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{id}">{text}</li>',
* idField : 'id', //id 字段作为key
* render : '#t1',
* items : [{id : '1',text : '1'},{id : '2',text : '2'}]
* });
*
* list.setSelectedByField('123'); //默认按照id字段查找
* //或者
* list.setSelectedByField('id','123');
*
* list.setSelectedByField('value','123');
*
*/
setSelectedByField: function(field, value) {
if (!value) {
value = field;
field = this.get('idField');
}
var _self = this,
item = _self.findItemByField(field, value);
_self.setSelected(item);
},
/**
* 设置多个选中,根据字段和值
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>',
* idField : 'value', //value 字段作为key
* render : '#t1',
* multipleSelect : true,
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
* var values = ['1','2','3'];
* list.setSelectionByField(values);//
*
* //等于
* list.setSelectionByField('value',values);
*
* @param {String} field 默认为idField
* @param {Array} values 值得集合
*/
setSelectionByField: function(field, values) {
if (!values) {
values = field;
field = this.get('idField');
}
var _self = this;
BUI.each(values, function(value) {
_self.setSelectedByField(field, value);
});
},
/**
* 选中完成后,触发事件
* @protected
* @param {*} item 选项
* @param {Boolean} selected 是否选中
* @param {jQuery} element
*/
afterSelected: function(item, selected, element) {
var _self = this;
if (selected) {
_self.fire('itemselected', {
item: item,
domTarget: element
});
_self.fire('selectedchange', {
item: item,
domTarget: element,
selected: selected
});
} else {
_self.fire('itemunselected', {
item: item,
domTarget: element
});
if (_self.get('multipleSelect')) { //只有当多选时,取消选中才触发selectedchange
_self.fire('selectedchange', {
item: item,
domTarget: element,
selected: selected
});
}
}
}
}
module.exports = selection;
});
define("bui/common/component/uibase/listitem", [], function(require, exports, module){
/**
* @fileOverview 可选中的控件,父控件支持selection扩展
* @ignore
*/
/**
* 列表项控件的视图层
* @class BUI.Component.UIBase.ListItemView
* @private
*/
function listItemView() {
// body...
}
listItemView.ATTRS = {
/**
* 是否选中
* @type {Boolean}
*/
selected: {
}
};
listItemView.prototype = {
_uiSetSelected: function(v) {
var _self = this,
cls = _self.getStatusCls('selected'),
el = _self.get('el');
if (v) {
el.addClass(cls);
} else {
el.removeClass(cls);
}
}
};
/**
* 列表项的扩展
* @class BUI.Component.UIBase.ListItem
*/
function listItem() {
}
listItem.ATTRS = {
/**
* 是否可以被选中
* @cfg {Boolean} [selectable=true]
*/
/**
* 是否可以被选中
* @type {Boolean}
*/
selectable: {
value: true
},
/**
* 是否选中,只能通过设置父类的选中方法来实现选中
* @type {Boolean}
* @readOnly
*/
selected: {
view: true,
sync: false,
value: false
}
};
listItem.prototype = {
};
listItem.View = listItemView;
module.exports = listItem;
});
define("bui/common/component/uibase/mask", ["jquery"], function(require, exports, module){
/**
* @fileOverview mask 遮罩层
* @author yiminghe@gmail.com
* copied and modified by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
UA = require("bui/common/ua"),
/**
* 每组相同 prefixCls 的 position 共享一个遮罩
* @ignore
*/
maskMap = {
/**
* @ignore
* {
* node:
* num:
* }
*/
},
ie6 = UA.ie == 6;
function getMaskCls(self) {
return self.get('prefixCls') + 'ext-mask';
}
function docWidth() {
return ie6 ? BUI.docWidth() + 'px' : '100%';
}
function docHeight() {
return ie6 ? BUI.docHeight() + 'px' : '100%';
}
function initMask(maskCls) {
var mask = $('
* var overlay = new Overlay({ //显示overlay时,屏蔽body
* mask : true,
* maskNode : 'body',
* trigger : '#t1'
* });
* overlay.render();
*
* @cfg {Boolean} [mask = false]
*/
/**
* 控件显示时,是否显示屏蔽层
* @type {Boolean}
* @protected
*/
mask: {
value: false
},
/**
* 屏蔽的内容
*
* var overlay = new Overlay({ //显示overlay时,屏蔽body
* mask : true,
* maskNode : 'body',
* trigger : '#t1'
* });
* overlay.render();
*
* @cfg {jQuery} maskNode
*/
/**
* 屏蔽的内容
* @type {jQuery}
* @protected
*/
maskNode: {
view: 1
},
/**
* Whether to share mask with other overlays.
* @default true.
* @type {Boolean}
* @protected
*/
maskShared: {
view: 1
}
};
Mask.prototype = {
__bindUI: function() {
var self = this,
view = self.get('view'),
_maskExtShow = view._maskExtShow,
_maskExtHide = view._maskExtHide;
if (self.get('mask')) {
self.on('show', function() {
view._maskExtShow();
});
self.on('hide', function() {
view._maskExtHide();
});
}
}
};
Mask = Mask;
Mask.View = MaskView;
module.exports = Mask;
});
define("bui/common/component/uibase/position", ["jquery"], function(require, exports, module){
/**
* @fileOverview 位置,控件绝对定位
* @author yiminghe@gmail.com
* copied by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery");
/**
* 对齐的视图类
* @class BUI.Component.UIBase.PositionView
* @private
*/
function PositionView() {
}
PositionView.ATTRS = {
x: {
/**
* 水平方向绝对位置
* @private
* @ignore
*/
valueFn: function() {
var self = this;
// 读到这里时,el 一定是已经加到 dom 树中了,否则报未知错误
// el 不在 dom 树中 offset 报错的
// 最早读就是在 syncUI 中,一点重复设置(读取自身 X 再调用 _uiSetX)无所谓了
return self.get('el') && self.get('el').offset().left;
}
},
y: {
/**
* 垂直方向绝对位置
* @private
* @ignore
*/
valueFn: function() {
var self = this;
return self.get('el') && self.get('el').offset().top;
}
},
zIndex: {},
/**
* @private
* see {@link BUI.Component.UIBase.Box#visibleMode}.
* @default "visibility"
* @ignore
*/
visibleMode: {
value: 'visibility'
}
};
PositionView.prototype = {
__createDom: function() {
this.get('el').addClass(BUI.prefix + 'ext-position');
},
_uiSetZIndex: function(x) {
this.get('el').css('z-index', x);
},
_uiSetX: function(x) {
if (x != null) {
this.get('el').offset({
left: x
});
}
},
_uiSetY: function(y) {
if (y != null) {
this.get('el').offset({
top: y
});
}
},
_uiSetLeft: function(left) {
if (left != null) {
this.get('el').css({
left: left
});
}
},
_uiSetTop: function(top) {
if (top != null) {
this.get('el').css({
top: top
});
}
}
};
/**
* @class BUI.Component.UIBase.Position
* Position extension class.
* Make component positionable
*/
function Position() {}
Position.ATTRS = {
/**
* 水平坐标
* @cfg {Number} x
*/
/**
* 水平坐标
*
* overlay.set('x',100);
*
* @type {Number}
*/
x: {
view: 1
},
/**
* 垂直坐标
* @cfg {Number} y
*/
/**
* 垂直坐标
*
* overlay.set('y',100);
*
* @type {Number}
*/
y: {
view: 1
},
/**
* 相对于父元素的水平位置
* @type {Number}
* @protected
*/
left: {
view: 1
},
/**
* 相对于父元素的垂直位置
* @type {Number}
* @protected
*/
top: {
view: 1
},
/**
* 水平和垂直坐标
*
* var overlay = new Overlay({
* xy : [100,100],
* trigger : '#t1',
* srcNode : '#c1'
* });
*
* @cfg {Number[]} xy
*/
/**
* 水平和垂直坐标
*
* overlay.set('xy',[100,100]);
*
* @type {Number[]}
*/
xy: {
// 相对 page 定位, 有效值为 [n, m], 为 null 时, 选 align 设置
setter: function(v) {
var self = this,
xy = $.makeArray(v);
/*
属性内分发特别注意:
xy -> x,y
*/
if (xy.length) {
xy[0] && self.set('x', xy[0]);
xy[1] && self.set('y', xy[1]);
}
return v;
},
/**
* xy 纯中转作用
* @ignore
*/
getter: function() {
return [this.get('x'), this.get('y')];
}
},
/**
* z-index value.
*
* var overlay = new Overlay({
* zIndex : '1000'
* });
*
* @cfg {Number} zIndex
*/
/**
* z-index value.
*
* overlay.set('zIndex','1200');
*
* @type {Number}
*/
zIndex: {
view: 1
},
/**
* Positionable element is by default visible false.
* For compatibility in overlay and PopupMenu.
* @default false
* @ignore
*/
visible: {
view: true,
value: true
}
};
Position.prototype = {
/**
* Move to absolute position.
* @param {Number|Number[]} x
* @param {Number} [y]
* @example
*
* move(x, y);
* move(x);
* move([x,y])
*
*/
move: function(x, y) {
var self = this;
if (BUI.isArray(x)) {
y = x[1];
x = x[0];
}
self.set('xy', [x, y]);
return self;
},
//设置 x 坐标时,重置 left
_uiSetX: function(v) {
if (v != null) {
var _self = this,
el = _self.get('el');
_self.setInternal('left', el.position().left);
if (v != -999) {
this.set('cachePosition', null);
}
}
},
//设置 y 坐标时,重置 top
_uiSetY: function(v) {
if (v != null) {
var _self = this,
el = _self.get('el');
_self.setInternal('top', el.position().top);
if (v != -999) {
this.set('cachePosition', null);
}
}
},
//设置 left时,重置 x
_uiSetLeft: function(v) {
var _self = this,
el = _self.get('el');
if (v != null) {
_self.setInternal('x', el.offset().left);
}
/*else{ //如果lef 为null,同时设置过left和top,那么取对应的值
_self.setInternal('left',el.position().left);
}*/
},
//设置top 时,重置y
_uiSetTop: function(v) {
var _self = this,
el = _self.get('el');
if (v != null) {
_self.setInternal('y', el.offset().top);
}
/*else{ //如果lef 为null,同时设置过left和top,那么取对应的值
_self.setInternal('top',el.position().top);
}*/
}
};
Position.View = PositionView;
module.exports = Position;
});
define("bui/common/component/uibase/stdmod", ["jquery"], function(require, exports, module){
/**
* @fileOverview
* 控件包含头部(head)、内容(content)和尾部(foot)
* @ignore
*/
var $ = require("jquery"),
CLS_PREFIX = BUI.prefix + 'stdmod-';
/**
* 标准模块组织的视图类
* @class BUI.Component.UIBase.StdModView
* @private
*/
function StdModView() {}
StdModView.ATTRS = {
header: {},
body: {},
footer: {},
bodyStyle: {},
footerStyle: {},
headerStyle: {},
headerContent: {},
bodyContent: {},
footerContent: {}
};
StdModView.PARSER = {
header: function(el) {
return el.one("." + CLS_PREFIX + "header");
},
body: function(el) {
return el.one("." + CLS_PREFIX + "body");
},
footer: function(el) {
return el.one("." + CLS_PREFIX + "footer");
}
}; /**/
function createUI(self, part) {
var el = self.get('contentEl'),
partEl = self.get(part);
if (!partEl) {
partEl = $('
* var dialog = new Dialog({
* headerContent: '<div class="header"></div>',
* bodyContent : '#c1',
* footerContent : '<div class="footer"></div>'
* });
* dialog.show();
*
* @cfg {jQuery|String} headerContent
*/
/**
* 控件头部的html
* @type {jQuery|String}
*/
headerContent: {
view: 1
},
/**
* 控件内容的html
*
* var dialog = new Dialog({
* headerContent: '<div class="header"></div>',
* bodyContent : '#c1',
* footerContent : '<div class="footer"></div>'
* });
* dialog.show();
*
* @cfg {jQuery|String} bodyContent
*/
/**
* 控件内容的html
* @type {jQuery|String}
*/
bodyContent: {
view: 1
},
/**
* 控件底部的html
*
* var dialog = new Dialog({
* headerContent: '<div class="header"></div>',
* bodyContent : '#c1',
* footerContent : '<div class="footer"></div>'
* });
* dialog.show();
*
* @cfg {jQuery|String} footerContent
*/
/**
* 控件底部的html
* @type {jQuery|String}
*/
footerContent: {
view: 1
}
};
StdMod.View = StdModView;
module.exports = StdMod;
});
define("bui/common/component/uibase/decorate", ["jquery"], function(require, exports, module){
/**
* @fileOverview 使用wrapper
* @ignore
*/
var $ = require("jquery"),
ArrayUtil = require("bui/common/array"),
JSON = require("bui/common/json"),
prefixCls = BUI.prefix,
FIELD_PREFIX = 'data-',
FIELD_CFG = FIELD_PREFIX + 'cfg',
PARSER = 'PARSER',
Manager = require("bui/common/component/manage"),
RE_DASH_WORD = /-([a-z])/g,
regx = /^[\{\[]/;
function isConfigField(name, cfgFields) {
if (cfgFields[name]) {
return true;
}
var reg = new RegExp("^" + FIELD_PREFIX);
if (name !== FIELD_CFG && reg.test(name)) {
return true;
}
return false;
}
// 收集单继承链,子类在前,父类在后
function collectConstructorChains(self) {
var constructorChains = [],
c = self.constructor;
while (c) {
constructorChains.push(c);
c = c.superclass && c.superclass.constructor;
}
return constructorChains;
}
function camelCase(str) {
return str.toLowerCase().replace(RE_DASH_WORD, function(all, letter) {
return (letter + '').toUpperCase()
})
}
//如果属性为对象或者数组,则进行转换
function parseFieldValue(value) {
value = $.trim(value);
if (value.toLowerCase() === 'false') {
value = false
} else if (value.toLowerCase() === 'true') {
value = true
} else if (regx.test(value)) {
value = JSON.looseParse(value);
} else if (/\d/.test(value) && /[^a-z]/i.test(value)) {
var number = parseFloat(value)
if (number + '' === value) {
value = number
}
}
return value;
}
function setConfigFields(self, cfg) {
var userConfig = self.userConfig || {};
for (var p in cfg) {
// 用户设置过那么这里不从 dom 节点取
// 用户设置 > html parser > default value
if (!(p in userConfig)) {
self.setInternal(p, cfg[p]);
}
}
}
function applyParser(srcNode, parser) {
var self = this,
p, v,
userConfig = self.userConfig || {};
// 从 parser 中,默默设置属性,不触发事件
for (p in parser) {
// 用户设置过那么这里不从 dom 节点取
// 用户设置 > html parser > default value
if (!(p in userConfig)) {
v = parser[p];
// 函数
if (BUI.isFunction(v)) {
self.setInternal(p, v.call(self, srcNode));
}
// 单选选择器
else if (typeof v == 'string') {
self.setInternal(p, srcNode.find(v));
}
// 多选选择器
else if (BUI.isArray(v) && v[0]) {
self.setInternal(p, srcNode.find(v[0]))
}
}
}
}
function initParser(self, srcNode) {
var c = self.constructor,
len,
p,
constructorChains;
constructorChains = collectConstructorChains(self);
// 从父类到子类开始从 html 读取属性
for (len = constructorChains.length - 1; len >= 0; len--) {
c = constructorChains[len];
if (p = c[PARSER]) {
applyParser.call(self, srcNode, p);
}
}
}
function initDecorate(self) {
var _self = self,
srcNode = _self.get('srcNode'),
userConfig,
decorateCfg;
if (srcNode) {
srcNode = $(srcNode);
_self.setInternal('el', srcNode);
_self.setInternal('srcNode', srcNode);
userConfig = _self.get('userConfig');
decorateCfg = _self.getDecorateConfig(srcNode);
setConfigFields(self, decorateCfg);
//如果从DOM中读取子控件
if (_self.get('isDecorateChild') && _self.decorateInternal) {
_self.decorateInternal(srcNode);
}
initParser(self, srcNode);
}
}
/**
* @class BUI.Component.UIBase.Decorate
* 将DOM对象封装成控件
*/
function decorate() {
initDecorate(this);
}
decorate.ATTRS = {
/**
* 配置控件的根节点的DOM
*
* new Form.Form({
* srcNode : '#J_Form'
* }).render();
*
* @cfg {jQuery} srcNode
*/
/**
* 配置控件的根节点的DOM
* @type {jQuery}
*/
srcNode: {
view: true
},
/**
* 是否根据DOM生成子控件
* @type {Boolean}
* @protected
*/
isDecorateChild: {
value: false
},
/**
* 此配置项配置使用那些srcNode上的节点作为配置项
* - 当时用 decorate 时,取 srcNode上的节点的属性作为控件的配置信息
* - 默认id,name,value,title 都会作为属性传入
* - 使用 'data-cfg' 作为整体的配置属性
*
*
* //会生成以下配置项:
* {
* name : 'txtName',
* id : 'id',
* allowBlank:false
* }
* new Form.Field({
* src:'#c1'
* }).render();
*
* @type {Object}
* @protected
*/
decorateCfgFields: {
value: {
'id': true,
'name': true,
'value': true,
'title': true
}
}
};
decorate.prototype = {
/**
* 获取控件的配置信息
* @protected
*/
getDecorateConfig: function(el) {
if (!el.length) {
return null;
}
var _self = this,
dom = el[0],
attributes = dom.attributes,
decorateCfgFields = _self.get('decorateCfgFields'),
config = {},
statusCfg = _self._getStautsCfg(el);
BUI.each(attributes, function(attr) {
var name = attr.nodeName;
try {
if (name === FIELD_CFG) {
var cfg = parseFieldValue(attr.nodeValue);
BUI.mix(config, cfg);
} else if (isConfigField(name, decorateCfgFields)) {
var value = attr.nodeValue;
if (name.indexOf(FIELD_PREFIX) !== -1) {
name = name.replace(FIELD_PREFIX, '');
name = camelCase(name);
value = parseFieldValue(value);
}
if (config[name] && BUI.isObject(value)) {
BUI.mix(config[name], value);
} else {
config[name] = value;
}
}
} catch (e) {
BUI.log('parse field error,the attribute is:' + name);
}
});
return BUI.mix(config, statusCfg);
},
//根据css class获取状态属性
//如: selected,disabled等属性
_getStautsCfg: function(el) {
var _self = this,
rst = {},
statusCls = _self.get('statusCls');
BUI.each(statusCls, function(v, k) {
if (el.hasClass(v)) {
rst[k] = true;
}
});
return rst;
},
/**
* 获取封装成子控件的节点集合
* @protected
* @return {Array} 节点集合
*/
getDecorateElments: function() {
var _self = this,
el = _self.get('el'),
contentContainer = _self.get('childContainer');
if (contentContainer) {
return el.find(contentContainer).children();
} else {
return el.children();
}
},
/**
* 封装所有的子控件
* @protected
* @param {jQuery} el Root element of current component.
*/
decorateInternal: function(el) {
var self = this;
self.decorateChildren(el);
},
/**
* 获取子控件的xclass类型
* @protected
* @param {jQuery} childNode 子控件的根节点
*/
findXClassByNode: function(childNode, ignoreError) {
var _self = this,
cls = childNode.attr("class") || '',
childClass = _self.get('defaultChildClass'); //如果没有样式或者查找不到对应的类,使用默认的子控件类型
// 过滤掉特定前缀
cls = cls.replace(new RegExp("\\b" + prefixCls, "ig"), "");
var UI = Manager.getConstructorByXClass(cls) || Manager.getConstructorByXClass(childClass);
if (!UI && !ignoreError) {
BUI.log(childNode);
BUI.error("can not find ui " + cls + " from this markup");
}
return Manager.getXClassByConstructor(UI);
},
// 生成一个组件
decorateChildrenInternal: function(xclass, c) {
var _self = this,
children = _self.get('children');
children.push({
xclass: xclass,
srcNode: c
});
},
/**
* 封装子控件
* @private
* @param {jQuery} el component's root element.
*/
decorateChildren: function(el) {
var _self = this,
children = _self.getDecorateElments();
BUI.each(children, function(c) {
var xclass = _self.findXClassByNode($(c));
_self.decorateChildrenInternal(xclass, $(c));
});
}
};
module.exports = decorate;
});
define("bui/common/component/uibase/tpl", ["jquery"], function(require, exports, module){
/**
* @fileOverview 控件模板
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery");
/**
* @private
* 控件模板扩展类的渲染类(view)
* @class BUI.Component.UIBase.TplView
*/
function tplView() {
}
tplView.ATTRS = {
/**
* 模板
* @protected
* @type {String}
*/
tpl: {
},
tplEl: {
}
};
tplView.prototype = {
__renderUI: function() {
var _self = this,
contentContainer = _self.get('childContainer'),
contentEl;
if (contentContainer) {
contentEl = _self.get('el').find(contentContainer);
if (contentEl.length) {
_self.set('contentEl', contentEl);
}
}
},
/**
* 获取生成控件的模板
* @protected
* @param {Object} attrs 属性值
* @return {String} 模板
*/
getTpl: function(attrs) {
var _self = this,
tpl = _self.get('tpl'),
tplRender = _self.get('tplRender');
attrs = attrs || _self.getAttrVals();
if (tplRender) {
return tplRender(attrs);
}
if (tpl) {
return BUI.substitute(tpl, attrs);
}
return '';
},
/**
* 如果控件设置了模板,则根据模板和属性值生成DOM
* 如果设置了content属性,此模板不应用
* @protected
* @param {Object} attrs 属性值,默认为初始化时传入的值
*/
setTplContent: function(attrs) {
var _self = this,
el = _self.get('el'),
content = _self.get('content'),
tplEl = _self.get('tplEl'),
tpl = _self.getTpl(attrs);
//tplEl.remove();
if (!content && tpl) { //替换掉原先的内容
el.empty();
el.html(tpl);
/*if(tplEl){
var node = $(tpl).insertBefore(tplEl);
tplEl.remove();
tplEl = node;
}else{
tplEl = $(tpl).appendTo(el);
}
_self.set('tplEl',tplEl)
*/
}
}
}
/**
* 控件的模板扩展
* @class BUI.Component.UIBase.Tpl
*/
function tpl() {
}
tpl.ATTRS = {
/**
* 控件的模版,用于初始化
*
* var list = new List.List({
* tpl : '<div class="toolbar"></div><ul></ul>',
* childContainer : 'ul'
* });
* //用于统一子控件模板
* var list = new List.List({
* defaultChildCfg : {
* tpl : '<span>{text}</span>'
* }
* });
* list.render();
*
* @cfg {String} tpl
*/
/**
* 控件的模板
*
* list.set('tpl','<div class="toolbar"></div><ul></ul><div class="bottom"></div>')
*
* @type {String}
*/
tpl: {
view: true,
sync: false
},
/**
* 控件的渲染函数,应对一些简单模板解决不了的问题,例如有if,else逻辑,有循环逻辑, * 函数原型是function(data){},其中data是控件的属性值
*控件模板的加强模式,此属性会覆盖@see {BUI.Component.UIBase.Tpl#property-tpl}属性
* //用于统一子控件模板 * var list = new List.List({ * defaultChildCfg : { * tplRender : funciton(item){ * if(item.type == '1'){ * return 'type1 html'; * }else{ * return 'type2 html'; * } * } * } * }); * list.render(); * @cfg {Function} tplRender */ tplRender: { view: true, value: null }, /** * 这是一个选择器,使用了模板后,子控件可能会添加到模板对应的位置, * - 默认为null,此时子控件会将控件最外层 el 作为容器 *
* var list = new List.List({
* tpl : '<div class="toolbar"></div><ul></ul>',
* childContainer : 'ul'
* });
*
* @cfg {String} childContainer
*/
childContainer: {
view: true
}
};
tpl.prototype = {
__renderUI: function() {
//使用srcNode时,不使用模板
if (!this.get('srcNode')) {
this.setTplContent();
}
},
/**
* 控件信息发生改变时,控件内容跟模板相关时需要调用这个函数,
* 重新通过模板和控件信息构造内容
*/
updateContent: function() {
this.setTplContent();
},
/**
* 根据控件的属性和模板生成控件内容
* @protected
*/
setTplContent: function() {
var _self = this,
attrs = _self.getAttrVals();
_self.get('view').setTplContent(attrs);
},
//模板发生改变
_uiSetTpl: function() {
this.setTplContent();
}
};
tpl.View = tplView;
module.exports = tpl;
});
define("bui/common/component/uibase/childCfg", ["jquery"], function(require, exports, module){
/**
* @fileOverview 子控件的默认配置项
* @ignore
*/
var $ = require("jquery");
/**
* @class BUI.Component.UIBase.ChildCfg
* 子控件默认配置项的扩展类
*/
var childCfg = function(config) {
this._init();
};
childCfg.ATTRS = {
/**
* 默认的子控件配置项,在初始化控件时配置
*
* - 如果控件已经渲染过,此配置项无效,
* - 控件生成后,修改此配置项无效。
*
* var control = new Control({
* defaultChildCfg : {
* tpl : '<li>{text}</li>',
* xclass : 'a-b'
* }
* });
*
* @cfg {Object} defaultChildCfg
*/
/**
* @ignore
*/
defaultChildCfg: {
}
};
childCfg.prototype = {
_init: function() {
var _self = this,
defaultChildCfg = _self.get('defaultChildCfg');
if (defaultChildCfg) {
_self.on('beforeAddChild', function(ev) {
var child = ev.child;
if ($.isPlainObject(child)) {
BUI.each(defaultChildCfg, function(v, k) {
if (child[k] == null) { //如果未在配置项中设置,则使用默认值
child[k] = v;
}
});
}
});
}
}
};
module.exports = childCfg;
});
define("bui/common/component/uibase/bindable", [], function(require, exports, module){
/**
* @fileOverview bindable extension class.
* @author dxq613@gmail.com
* @ignore
*/
/**
* bindable extension class.
*
* BUI.use(['bui/list','bui/data','bui/mask'],function(List,Data,Mask){
* var store = new Data.Store({
* url : 'data/xx.json'
* });
* var list = new List.SimpleList({
* render : '#l1',
* store : store,
* loadMask : new Mask.LoadMask({el : '#t1'})
* });
*
* list.render();
* store.load();
* });
*
* 使控件绑定store,处理store的事件 {@link BUI.Data.Store}
* @class BUI.Component.UIBase.Bindable
*/
function bindable() {
}
bindable.ATTRS = {
/**
* 绑定 {@link BUI.Data.Store}的事件
*
* var store = new Data.Store({
* url : 'data/xx.json',
* autoLoad : true
* });
*
* var list = new List.SimpleList({
* render : '#l1',
* store : store
* });
*
* list.render();
*
* @cfg {BUI.Data.Store} store
*/
/**
* 绑定 {@link BUI.Data.Store}的事件
*
* var store = list.get('store');
*
* @type {BUI.Data.Store}
*/
store: {
},
/**
* 加载数据时,是否显示等待加载的屏蔽层
*
* BUI.use(['bui/list','bui/data','bui/mask'],function(List,Data,Mask){
* var store = new Data.Store({
* url : 'data/xx.json'
* });
* var list = new List.SimpleList({
* render : '#l1',
* store : store,
* loadMask : new Mask.LoadMask({el : '#t1'})
* });
*
* list.render();
* store.load();
* });
*
* @cfg {Boolean|Object} loadMask
*/
/**
* 加载数据时,是否显示等待加载的屏蔽层
* @type {Boolean|Object}
* @ignore
*/
loadMask: {
value: false
}
};
BUI.augment(bindable, {
__bindUI: function() {
var _self = this,
store = _self.get('store'),
loadMask = _self.get('loadMask');
if (!store) {
return;
}
store.on('beforeload', function(e) {
_self.onBeforeLoad(e);
if (loadMask && loadMask.show) {
loadMask.show();
}
});
store.on('load', function(e) {
_self.onLoad(e);
if (loadMask && loadMask.hide) {
loadMask.hide();
}
});
store.on('exception', function(e) {
_self.onException(e);
if (loadMask && loadMask.hide) {
loadMask.hide();
}
});
store.on('add', function(e) {
_self.onAdd(e);
});
store.on('remove', function(e) {
_self.onRemove(e);
});
store.on('update', function(e) {
_self.onUpdate(e);
});
store.on('localsort', function(e) {
_self.onLocalSort(e);
});
store.on('filtered', function(e) {
_self.onFiltered(e);
});
},
__syncUI: function() {
var _self = this,
store = _self.get('store');
if (!store) {
return;
}
if (store.hasData()) {
_self.onLoad();
}
},
/**
* @protected
* @template
* before store load data
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-beforeload}
*/
onBeforeLoad: function(e) {
},
/**
* @protected
* @template
* after store load data
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-load}
*/
onLoad: function(e) {
},
/**
* @protected
* @template
* occurred exception when store is loading data
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-exception}
*/
onException: function(e) {
},
/**
* @protected
* @template
* after added data to store
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-add}
*/
onAdd: function(e) {
},
/**
* @protected
* @template
* after remvoed data to store
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-remove}
*/
onRemove: function(e) {
},
/**
* @protected
* @template
* after updated data to store
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-update}
*/
onUpdate: function(e) {
},
/**
* @protected
* @template
* after local sorted data to store
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-localsort}
*/
onLocalSort: function(e) {
},
/**
* @protected
* @template
* after filter data to store
* @param {Object} e The event object
* @see {@link BUI.Data.Store#event-filtered}
*/
onFiltered: function(e) {}
});
module.exports = bindable;
});
define("bui/common/component/uibase/depends", ["jquery"], function(require, exports, module){
/**
* @fileOverview 依赖扩展,用于观察者模式中的观察者
* @ignore
*/
var $ = require("jquery"),
regexp = /^#(.*):(.*)$/,
Manager = require("bui/common/component/manage");
//获取依赖信息
function getDepend(name) {
var arr = regexp.exec(name),
id = arr[1],
eventType = arr[2],
source = getSource(id);
return {
source: source,
eventType: eventType
};
}
//绑定依赖
function bindDepend(self, name, action) {
var depend = getDepend(name),
source = depend.source,
eventType = depend.eventType,
callbak;
if (source && action && eventType) {
if (BUI.isFunction(action)) { //如果action是一个函数
callbak = action;
} else if (BUI.isArray(action)) { //如果是一个数组,构建一个回调函数
callbak = function() {
BUI.each(action, function(methodName) {
if (self[methodName]) {
self[methodName]();
}
});
}
}
}
if (callbak) {
depend.callbak = callbak;
source.on(eventType, callbak);
return depend;
}
return null;
}
//去除依赖
function offDepend(depend) {
var source = depend.source,
eventType = depend.eventType,
callbak = depend.callbak;
source.off(eventType, callbak);
}
//获取绑定的事件源
function getSource(id) {
var control = Manager.getComponent(id);
if (!control) {
control = $('#' + id);
if (!control.length) {
control = null;
}
}
return control;
}
/**
* @class BUI.Component.UIBase.Depends
* 依赖事件源的扩展
*
* var control = new Control({
* depends : {
* '#btn:click':['toggle'],//当点击id为'btn'的按钮时,执行 control 的toggle方法
* '#checkbox1:checked':['show'],//当勾选checkbox时,显示控件
* '#menu:click',function(){}
* }
* });
*
*/
function Depends() {
};
Depends.ATTRS = {
/**
* 控件的依赖事件,是一个数组集合,每一条记录是一个依赖关系
* var control = new Control({
* depends : {
* '#btn:click':['toggle'],//当点击id为'btn'的按钮时,执行 control 的toggle方法
* '#checkbox1:checked':['show'],//当勾选checkbox时,显示控件
* '#menu:click',function(){}
* }
* });
*
* ** 注意:** 这些依赖项是在控件渲染(render)后进行的。
* @type {Object}
*/
depends: {
},
/**
* @private
* 依赖的映射集合
* @type {Object}
*/
dependencesMap: {
shared: false,
value: {}
}
};
Depends.prototype = {
__syncUI: function() {
this.initDependences();
},
/**
* 初始化依赖项
* @protected
*/
initDependences: function() {
var _self = this,
depends = _self.get('depends');
BUI.each(depends, function(action, name) {
_self.addDependence(name, action);
});
},
/**
* 添加依赖,如果已经有同名的事件,则移除,再添加
*
* form.addDependence('#btn:click',['toggle']); //当按钮#btn点击时,表单交替显示隐藏
*
* form.addDependence('#btn:click',function(){//当按钮#btn点击时,表单交替显示隐藏
* //TO DO
* });
*
* @param {String} name 依赖项的名称
* @param {Array|Function} action 依赖项的事件
*/
addDependence: function(name, action) {
var _self = this,
dependencesMap = _self.get('dependencesMap'),
depend;
_self.removeDependence(name);
depend = bindDepend(_self, name, action)
if (depend) {
dependencesMap[name] = depend;
}
},
/**
* 移除依赖
*
* form.removeDependence('#btn:click'); //当按钮#btn点击时,表单不在监听
*
* @param {String} name 依赖名称
*/
removeDependence: function(name) {
var _self = this,
dependencesMap = _self.get('dependencesMap'),
depend = dependencesMap[name];
if (depend) {
offDepend(depend);
delete dependencesMap[name];
}
},
/**
* 清除所有的依赖
*
* form.clearDependences();
*
*/
clearDependences: function() {
var _self = this,
map = _self.get('dependencesMap');
BUI.each(map, function(depend, name) {
offDepend(depend);
});
_self.set('dependencesMap', {});
},
__destructor: function() {
this.clearDependences();
}
};
module.exports = Depends;
});
define("bui/common/component/view", ["jquery"], function(require, exports, module){
/**
* @fileOverview 控件的视图层
* @author yiminghe@gmail.com
* copied by dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
win = window,
Manager = require("bui/common/component/manage"),
UIBase = require("bui/common/component/uibase/uibase"), //BUI.Component.UIBase,
doc = document;
/**
* 控件的视图层基类
* @class BUI.Component.View
* @protected
* @extends BUI.Component.UIBase
* @mixins BUI.Component.UIBase.TplView
*/
var View = UIBase.extend([UIBase.TplView], {
/**
* Get all css class name to be applied to the root element of this component for given state.
* the css class names are prefixed with component name.
* @param {String} [state] This component's state info.
*/
getComponentCssClassWithState: function(state) {
var self = this,
componentCls = self.get('ksComponentCss');
state = state || '';
return self.getCssClassWithPrefix(componentCls.split(/\s+/).join(state + ' ') + state);
},
/**
* Get full class name (with prefix) for current component
* @param classes {String} class names without prefixCls. Separated by space.
* @method
* @return {String} class name with prefixCls
* @private
*/
getCssClassWithPrefix: Manager.getCssClassWithPrefix,
/**
* Returns the dom element which is responsible for listening keyboard events.
* @return {jQuery}
*/
getKeyEventTarget: function() {
return this.get('el');
},
/**
* Return the dom element into which child component to be rendered.
* @return {jQuery}
*/
getContentElement: function() {
return this.get('contentEl') || this.get('el');
},
/**
* 获取状态对应的css样式
* @param {String} name 状态名称 例如:hover,disabled等等
* @return {String} 状态样式
*/
getStatusCls: function(name) {
var self = this,
statusCls = self.get('statusCls'),
cls = statusCls[name];
if (!cls) {
cls = self.getComponentCssClassWithState('-' + name);
}
return cls;
},
/**
* 渲染控件
* @protected
*/
renderUI: function() {
var self = this;
// 新建的节点才需要摆放定位,不支持srcNode模式
if (!self.get('srcNode')) {
var render = self.get('render'),
el = self.get('el'),
renderBefore = self.get('elBefore');
if (renderBefore) {
el.insertBefore(renderBefore, undefined);
} else if (render) {
el.appendTo(render, undefined);
} else {
el.appendTo(doc.body, undefined);
}
}
},
/**
* 只负责建立节点,如果是 decorate 过来的,甚至内容会丢失
* @protected
* 通过 render 来重建原有的内容
*/
createDom: function() {
var self = this,
contentEl = self.get('contentEl'),
el = self.get('el');
if (!self.get('srcNode')) {
el = $('<' + self.get('elTagName') + '>');
if (contentEl) {
el.append(contentEl);
}
self.setInternal('el', el);
}
el.addClass(self.getComponentCssClassWithState());
if (!contentEl) {
// 没取到,这里设下值, uiSet 时可以 set('content') 取到
self.setInternal('contentEl', el);
}
},
/**
* 设置高亮显示
* @protected
*/
_uiSetHighlighted: function(v) {
var self = this,
componentCls = self.getStatusCls('hover'),
el = self.get('el');
el[v ? 'addClass' : 'removeClass'](componentCls);
},
/**
* 设置禁用
* @protected
*/
_uiSetDisabled: function(v) {
var self = this,
componentCls = self.getStatusCls('disabled'),
el = self.get('el');
el[v ? 'addClass' : 'removeClass'](componentCls)
.attr('aria-disabled', v);
//如果禁用控件时,处于hover状态,则清除
if (v && self.get('highlighted')) {
self.set('highlighted', false);
}
if (self.get('focusable')) {
//不能被 tab focus 到
self.getKeyEventTarget().attr('tabIndex', v ? -1 : 0);
}
},
/**
* 设置激活状态
* @protected
*/
_uiSetActive: function(v) {
var self = this,
componentCls = self.getStatusCls('active');
self.get('el')[v ? 'addClass' : 'removeClass'](componentCls)
.attr('aria-pressed', !!v);
},
/**
* 设置获得焦点
* @protected
*/
_uiSetFocused: function(v) {
var self = this,
el = self.get('el'),
componentCls = self.getStatusCls('focused');
el[v ? 'addClass' : 'removeClass'](componentCls);
},
/**
* 设置控件最外层DOM的属性
* @protected
*/
_uiSetElAttrs: function(attrs) {
this.get('el').attr(attrs);
},
/**
* 设置应用到控件最外层DOM的css class
* @protected
*/
_uiSetElCls: function(cls) {
this.get('el').addClass(cls);
},
/**
* 设置应用到控件最外层DOM的css style
* @protected
*/
_uiSetElStyle: function(style) {
this.get('el').css(style);
},
//设置role
_uiSetRole: function(role) {
if (role) {
this.get('el').attr('role', role);
}
},
/**
* 设置应用到控件宽度
* @protected
*/
_uiSetWidth: function(w) {
this.get('el').width(w);
},
/**
* 设置应用到控件高度
* @protected
*/
_uiSetHeight: function(h) {
var self = this;
self.get('el').height(h);
},
/**
* 设置应用到控件的内容
* @protected
*/
_uiSetContent: function(c) {
var self = this,
el;
// srcNode 时不重新渲染 content
// 防止内部有改变,而 content 则是老的 html 内容
if (self.get('srcNode') && !self.get('rendered')) {} else {
el = self.get('contentEl');
if (typeof c == 'string') {
el.html(c);
} else if (c) {
el.empty().append(c);
}
}
},
/**
* 设置应用到控件是否可见
* @protected
*/
_uiSetVisible: function(isVisible) {
var self = this,
el = self.get('el'),
visibleMode = self.get('visibleMode');
if (visibleMode === 'visibility') {
el.css('visibility', isVisible ? 'visible' : 'hidden');
} else {
el.css('display', isVisible ? '' : 'none');
}
},
set: function(name, value) {
var _self = this,
attr = _self.__attrs[name],
ev,
ucName,
m;
if (!attr || !_self.get('binded')) { //未初始化view或者没用定义属性
View.superclass.set.call(this, name, value);
return _self;
}
var prevVal = View.superclass.get.call(this, name);
//如果未改变值不进行修改
if (!$.isPlainObject(value) && !BUI.isArray(value) && prevVal === value) {
return _self;
}
View.superclass.set.call(this, name, value);
value = _self.__attrVals[name];
ev = {
attrName: name,
prevVal: prevVal,
newVal: value
};
ucName = BUI.ucfirst(name);
m = '_uiSet' + ucName;
if (_self[m]) {
_self[m](value, ev);
}
return _self;
},
/**
* 析构函数
* @protected
*/
destructor: function() {
var el = this.get('el');
if (el) {
el.remove();
}
}
}, {
xclass: 'view',
priority: 0
});
View.ATTRS = {
/**
* 控件根节点
* @readOnly
* see {@link BUI.Component.Controller#property-el}
*/
el: {
/**
* @private
*/
setter: function(v) {
return $(v);
}
},
/**
* 控件根节点样式
* see {@link BUI.Component.Controller#property-elCls}
*/
elCls: {},
/**
* 控件根节点样式属性
* see {@link BUI.Component.Controller#property-elStyle}
*/
elStyle: {},
/**
* ARIA 标准中的role
* @type {String}
*/
role: {
},
/**
* 控件宽度
* see {@link BUI.Component.Controller#property-width}
*/
width: {},
/**
* 控件高度
* see {@link BUI.Component.Controller#property-height}
*/
height: {},
/**
* 状态相关的样式,默认情况下会使用 前缀名 + xclass + '-' + 状态名
* see {@link BUI.Component.Controller#property-statusCls}
* @type {Object}
*/
statusCls: {
value: {}
},
/**
* 控件根节点使用的标签
* @type {String}
*/
elTagName: {
// 生成标签名字
value: 'div'
},
/**
* 控件根节点属性
* see {@link BUI.Component.Controller#property-elAttrs}
* @ignore
*/
elAttrs: {},
/**
* 控件内容,html,文本等
* see {@link BUI.Component.Controller#property-content}
*/
content: {},
/**
* 控件插入到指定元素前
* see {@link BUI.Component.Controller#property-tpl}
*/
elBefore: {
// better named to renderBefore, too late !
},
/**
* 控件在指定元素内部渲染
* see {@link BUI.Component.Controller#property-render}
* @ignore
*/
render: {},
/**
* 是否可见
* see {@link BUI.Component.Controller#property-visible}
*/
visible: {
value: true
},
/**
* 可视模式
* see {@link BUI.Component.Controller#property-visibleMode}
*/
visibleMode: {
value: 'display'
},
/**
* @private
* 缓存隐藏时的位置,对应visibleMode = 'visiblity' 的场景
* @type {Object}
*/
cachePosition: {
},
/**
* content 设置的内容节点,默认根节点
* @type {jQuery}
* @default el
*/
contentEl: {
valueFn: function() {
return this.get('el');
}
},
/**
* 样式前缀
* see {@link BUI.Component.Controller#property-prefixCls}
*/
prefixCls: {
value: BUI.prefix
},
/**
* 可以获取焦点
* @protected
* see {@link BUI.Component.Controller#property-focusable}
*/
focusable: {
value: true
},
/**
* 获取焦点
* see {@link BUI.Component.Controller#property-focused}
*/
focused: {},
/**
* 激活
* see {@link BUI.Component.Controller#property-active}
*/
active: {},
/**
* 禁用
* see {@link BUI.Component.Controller#property-disabled}
*/
disabled: {},
/**
* 高亮显示
* see {@link BUI.Component.Controller#property-highlighted}
*/
highlighted: {}
};
module.exports = View;
});
define("bui/common/component/controller", ["jquery"], function(require, exports, module){
/**
* @fileOverview 控件可以实例化的基类
* @ignore
* @author yiminghe@gmail.com
* copied by dxq613@gmail.com
*/
/**
* jQuery 事件
* @class jQuery.Event
* @private
*/
'use strict';
var $ = require("jquery"),
UIBase = require("bui/common/component/uibase/uibase"),
Manager = require("bui/common/component/manage"),
View = require("bui/common/component/view"),
Loader = require("bui/common/component/loader"),
wrapBehavior = BUI.wrapBehavior,
getWrapBehavior = BUI.getWrapBehavior;
/**
* @ignore
*/
function wrapperViewSetter(attrName) {
return function(ev) {
var self = this;
// in case bubbled from sub component
if (self === ev.target) {
var value = ev.newVal,
view = self.get('view');
if (view) {
view.set(attrName, value);
}
}
};
}
/**
* @ignore
*/
function wrapperViewGetter(attrName) {
return function(v) {
var self = this,
view = self.get('view');
return v === undefined ? view.get(attrName) : v;
};
}
/**
* @ignore
*/
function initChild(self, c, renderBefore) {
// 生成父组件的 dom 结构
self.create();
var contentEl = self.getContentElement(),
defaultCls = self.get('defaultChildClass');
//配置默认 xclass
if (!c.xclass && !(c instanceof Controller)) {
if (!c.xtype) {
c.xclass = defaultCls;
} else {
c.xclass = defaultCls + '-' + c.xtype;
}
}
c = BUI.Component.create(c, self);
c.setInternal('parent', self);
// set 通知 view 也更新对应属性
c.set('render', contentEl);
c.set('elBefore', renderBefore);
// 如果 parent 也没渲染,子组件 create 出来和 parent 节点关联
// 子组件和 parent 组件一起渲染
// 之前设好属性,view ,logic 同步还没 bind ,create 不是 render ,还没有 bindUI
c.create(undefined);
return c;
}
/**
* 不使用 valueFn,
* 只有 render 时需要找到默认,其他时候不需要,防止莫名其妙初始化
* @ignore
*/
function constructView(self) {
// 逐层找默认渲染器
var attrs,
attrCfg,
attrName,
cfg = {},
v,
Render = self.get('xview');
//将渲染层初始化所需要的属性,直接构造器设置过去
attrs = self.getAttrs();
// 整理属性,对纯属于 view 的属性,添加 getter setter 直接到 view
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
attrCfg = attrs[attrName];
if (attrCfg.view) {
// 先取后 getter
// 防止死循环
if ((v = self.get(attrName)) !== undefined) {
cfg[attrName] = v;
}
// setter 不应该有实际操作,仅用于正规化比较好
// attrCfg.setter = wrapperViewSetter(attrName);
// 不更改attrCfg的定义,可以多个实例公用一份attrCfg
/*self.on('after' + BUI.ucfirst(attrName) + 'Change',
wrapperViewSetter(attrName));
*/
// 逻辑层读值直接从 view 层读
// 那么如果存在默认值也设置在 view 层
// 逻辑层不要设置 getter
//attrCfg.getter = wrapperViewGetter(attrName);
}
}
}
// does not autoRender for view
delete cfg.autoRender;
cfg.ksComponentCss = getComponentCss(self);
return new Render(cfg);
}
function getComponentCss(self) {
var constructor = self.constructor,
cls,
re = [];
while (constructor && constructor !== Controller) {
cls = Manager.getXClassByConstructor(constructor);
if (cls) {
re.push(cls);
}
constructor = constructor.superclass && constructor.superclass.constructor;
}
return re.join(' ');
}
function isMouseEventWithinElement(e, elem) {
var relatedTarget = e.relatedTarget;
// 在里面或等于自身都不算 mouseenter/leave
return relatedTarget &&
(relatedTarget === elem[0] || $.contains(elem, relatedTarget));
}
/**
* 可以实例化的控件,作为最顶层的控件类,一切用户控件都继承此控件
* xclass: 'controller'.
* ** 创建子控件 **
*
* var Control = Controller.extend([mixin1,mixin2],{ //原型链上的函数
* renderUI : function(){ //创建DOM
*
* },
* bindUI : function(){ //绑定事件
*
* },
* destructor : funciton(){ //析构函数
*
* }
* },{
* ATTRS : { //默认的属性
* text : {
*
* }
* }
* },{
* xclass : 'a' //用于把对象解析成类
* });
*
*
* ** 创建对象 **
*
* var c1 = new Control({
* render : '#t1', //在t1上创建
* text : 'text1',
* children : [{xclass : 'a',text : 'a1'},{xclass : 'b',text : 'b1'}]
* });
*
* c1.render();
*
* @extends BUI.Component.UIBase
* @mixins BUI.Component.UIBase.Tpl
* @mixins BUI.Component.UIBase.Decorate
* @mixins BUI.Component.UIBase.Depends
* @mixins BUI.Component.UIBase.ChildCfg
* @class BUI.Component.Controller
*/
var Controller = UIBase.extend([UIBase.Decorate, UIBase.Tpl, UIBase.ChildCfg, UIBase.KeyNav, UIBase.Depends], {
/**
* 是否是控件,标示对象是否是一个UI 控件
* @type {Boolean}
*/
isController: true,
/**
* 使用前缀获取类的名字
* @param classes {String} class names without prefixCls. Separated by space.
* @method
* @protected
* @return {String} class name with prefixCls
*/
getCssClassWithPrefix: Manager.getCssClassWithPrefix,
/**
* From UIBase, Initialize this component. *
* @protected
*/
initializer: function() {
var self = this;
if (!self.get('id')) {
self.set('id', self.getNextUniqueId());
}
Manager.addComponent(self.get('id'), self);
// initialize view
var view = constructView(self);
self.setInternal('view', view);
self.__view = view;
},
/**
* 返回新的唯一的Id,结果是 'xclass' + number
* @protected
* @return {String} 唯一id
*/
getNextUniqueId: function() {
var self = this,
xclass = Manager.getXClassByConstructor(self.constructor);
return BUI.guid(xclass);
},
/**
* From UIBase. Constructor(or get) view object to create ui elements.
* @protected
*
*/
createDom: function() {
var self = this,
//el,
view = self.get('view');
view.create(undefined);
//el = view.getKeyEventTarget();
/*if (!self.get('allowTextSelection')) {
//el.unselectable(undefined);
}*/
},
/**
* From UIBase. Call view object to render ui elements.
* @protected
*
*/
renderUI: function() {
var self = this,
loader = self.get('loader');
self.get('view').render();
self._initChildren();
if (loader) {
self.setInternal('loader', loader);
}
/**/
},
_initChildren: function(children) {
var self = this,
i,
children,
child;
// then render my children
children = children || self.get('children').concat();
self.get('children').length = 0;
for (i = 0; i < children.length; i++) {
child = self.addChild(children[i]);
child.render();
}
},
/**
* bind ui for box
* @private
*/
bindUI: function() {
var self = this,
events = self.get('events');
this.on('afterVisibleChange', function(e) {
this.fire(e.newVal ? 'show' : 'hide');
});
//处理控件事件,设置事件是否冒泡
BUI.each(events, function(v, k) {
self.publish(k, {
bubbles: v
});
});
},
/**
* 控件是否包含指定的DOM元素,包括根节点
*
* var control = new Control();
* $(document).on('click',function(ev){
* var target = ev.target;
*
* if(!control.containsElement(elem)){ //未点击在控件内部
* control.hide();
* }
* });
*
* @param {HTMLElement} elem DOM 元素
* @return {Boolean} 是否包含
*/
containsElement: function(elem) {
var _self = this,
el = _self.get('el'),
children = _self.get('children'),
result = false;
if (!_self.get('rendered')) {
return false;
}
if ($.contains(el[0], elem) || el[0] === elem) {
result = true;
} else {
BUI.each(children, function(item) {
if (item.containsElement(elem)) {
result = true;
return false;
}
});
}
return result;
},
/**
* 是否是子控件的DOM元素
* @protected
* @return {Boolean} 是否子控件的DOM元素
*/
isChildrenElement: function(elem) {
var _self = this,
children = _self.get('children'),
rst = false;
BUI.each(children, function(child) {
if (child.containsElement(elem)) {
rst = true;
return false;
}
});
return rst;
},
/**
* 显示控件
*/
show: function() {
var self = this;
self.render();
self.set('visible', true);
return self;
},
/**
* 隐藏控件
*/
hide: function() {
var self = this;
self.set('visible', false);
return self;
},
/**
* 交替显示或者隐藏
*
* control.show(); //显示
* control.toggle(); //隐藏
* control.toggle(); //显示
*
*/
toggle: function() {
this.set('visible', !this.get('visible'));
return this;
},
_uiSetFocusable: function(focusable) {
var self = this,
t,
el = self.getKeyEventTarget();
if (focusable) {
el.attr('tabIndex', 0)
// remove smart outline in ie
// set outline in style for other standard browser
.attr('hideFocus', true)
.on('focus', wrapBehavior(self, 'handleFocus'))
.on('blur', wrapBehavior(self, 'handleBlur'))
.on('keydown', wrapBehavior(self, 'handleKeydown'))
.on('keyup', wrapBehavior(self, 'handleKeyUp'));
} else {
el.removeAttr('tabIndex');
if (t = getWrapBehavior(self, 'handleFocus')) {
el.off('focus', t);
}
if (t = getWrapBehavior(self, 'handleBlur')) {
el.off('blur', t);
}
if (t = getWrapBehavior(self, 'handleKeydown')) {
el.off('keydown', t);
}
if (t = getWrapBehavior(self, 'handleKeyUp')) {
el.off('keyup', t);
}
}
},
_uiSetHandleMouseEvents: function(handleMouseEvents) {
var self = this,
el = self.get('el'),
t;
if (handleMouseEvents) {
el.on('mouseenter', wrapBehavior(self, 'handleMouseEnter'))
.on('mouseleave', wrapBehavior(self, 'handleMouseLeave'))
.on('contextmenu', wrapBehavior(self, 'handleContextMenu'))
.on('mousedown', wrapBehavior(self, 'handleMouseDown'))
.on('mouseup', wrapBehavior(self, 'handleMouseUp'))
.on('dblclick', wrapBehavior(self, 'handleDblClick'));
} else {
t = getWrapBehavior(self, 'handleMouseEnter') &&
el.off('mouseenter', t);
t = getWrapBehavior(self, 'handleMouseLeave') &&
el.off('mouseleave', t);
t = getWrapBehavior(self, 'handleContextMenu') &&
el.off('contextmenu', t);
t = getWrapBehavior(self, 'handleMouseDown') &&
el.off('mousedown', t);
t = getWrapBehavior(self, 'handleMouseUp') &&
el.off('mouseup', t);
t = getWrapBehavior(self, 'handleDblClick') &&
el.off('dblclick', t);
}
},
_uiSetFocused: function(v) {
if (v) {
this.getKeyEventTarget()[0].focus();
}
},
//当使用visiblity显示隐藏时,隐藏时把DOM移除出视图内,显示时回复原位置
_uiSetVisible: function(isVisible) {
var self = this,
el = self.get('el'),
visibleMode = self.get('visibleMode');
if (visibleMode === 'visibility') {
if (isVisible) {
var position = self.get('cachePosition');
if (position) {
self.set('xy', position);
}
}
if (!isVisible) {
var position = [
self.get('x'), self.get('y')
];
self.set('cachePosition', position);
self.set('xy', [-999, -999]);
}
}
},
//设置children时
_uiSetChildren: function(v) {
var self = this,
children = BUI.cloneObject(v);
//self.removeChildren(true);
self._initChildren(children);
},
/**
* 使控件可用
*/
enable: function() {
this.set('disabled', false);
return this;
},
/**
* 使控件不可用,控件不可用时,点击等事件不会触发
*
* control.disable(); //禁用
* control.enable(); //解除禁用
*
*/
disable: function() {
this.set('disabled', true);
return this;
},
/**
* 控件获取焦点
*/
focus: function() {
if (this.get('focusable')) {
this.set('focused', true);
}
},
/**
* 子组件将要渲染到的节点,在 render 类上覆盖对应方法
* @protected
* @ignore
*/
getContentElement: function() {
return this.get('view').getContentElement();
},
/**
* 焦点所在元素即键盘事件处理元素,在 render 类上覆盖对应方法
* @protected
* @ignore
*/
getKeyEventTarget: function() {
return this.get('view').getKeyEventTarget();
},
/**
* 添加控件的子控件,索引值为 0-based
*
* control.add(new Control());//添加controller对象
* control.add({xclass : 'a'});//添加xclass 为a 的一个对象
* control.add({xclass : 'b'},2);//插入到第三个位置
*
* @param {BUI.Component.Controller|Object} c 子控件的实例或者配置项
* @param {String} [c.xclass] 如果c为配置项,设置c的xclass
* @param {Number} [index] 0-based 如果未指定索引值,则插在控件的最后
*/
addChild: function(c, index) {
var self = this,
children = self.get('children'),
renderBefore;
if (index === undefined) {
index = children.length;
}
/**
* 添加子控件前触发
* @event beforeAddChild
* @param {Object} e
* @param {Object} e.child 添加子控件时传入的配置项或者子控件
* @param {Number} e.index 添加的位置
*/
self.fire('beforeAddChild', {
child: c,
index: index
});
renderBefore = children[index] && children[index].get('el') || null;
c = initChild(self, c, renderBefore);
children.splice(index, 0, c);
// 先 create 占位 再 render
// 防止 render 逻辑里读 parent.get('children') 不同步
// 如果 parent 已经渲染好了子组件也要立即渲染,就 创建 dom ,绑定事件
if (self.get('rendered')) {
c.render();
}
/**
* 添加子控件后触发
* @event afterAddChild
* @param {Object} e
* @param {Object} e.child 添加子控件
* @param {Number} e.index 添加的位置
*/
self.fire('afterAddChild', {
child: c,
index: index
});
return c;
},
/**
* 将自己从父控件中移除
*
* control.remove(); //将控件从父控件中移除,并未删除
* parent.addChild(control); //还可以添加回父控件
*
* control.remove(true); //从控件中移除并调用控件的析构函数
*
* @param {Boolean} destroy 是否删除DON节点
* @return {BUI.Component.Controller} 删除的子对象.
*/
remove: function(destroy) {
var self = this,
parent = self.get('parent');
if (parent) {
parent.removeChild(self, destroy);
} else if (destroy) {
self.destroy();
}
return self;
},
/**
* 移除子控件,并返回移除的控件
*
* ** 如果 destroy=true,调用移除控件的 {@link BUI.Component.UIBase#destroy} 方法,
* 同时删除对应的DOM **
*
* var child = control.getChild(id);
* control.removeChild(child); //仅仅移除
*
* control.removeChild(child,true); //移除,并调用析构函数
*
* @param {BUI.Component.Controller} c 要移除的子控件.
* @param {Boolean} [destroy=false] 如果是true,
* 调用控件的方法 {@link BUI.Component.UIBase#destroy} .
* @return {BUI.Component.Controller} 移除的子控件.
*/
removeChild: function(c, destroy) {
var self = this,
children = self.get('children'),
index = BUI.Array.indexOf(c, children);
if (index === -1) {
return;
}
/**
* 删除子控件前触发
* @event beforeRemoveChild
* @param {Object} e
* @param {Object} e.child 子控件
* @param {Boolean} e.destroy 是否清除DOM
*/
self.fire('beforeRemoveChild', {
child: c,
destroy: destroy
});
if (index !== -1) {
children.splice(index, 1);
}
if (destroy &&
// c is still json
c.destroy) {
c.destroy();
}
/**
* 删除子控件前触发
* @event afterRemoveChild
* @param {Object} e
* @param {Object} e.child 子控件
* @param {Boolean} e.destroy 是否清除DOM
*/
self.fire('afterRemoveChild', {
child: c,
destroy: destroy
});
return c;
},
/**
* 删除当前控件的子控件
*
* control.removeChildren();//删除所有子控件
* control.removeChildren(true);//删除所有子控件,并调用子控件的析构函数
*
* @see Component.Controller#removeChild
* @param {Boolean} [destroy] 如果设置 true,
* 调用子控件的 {@link BUI.Component.UIBase#destroy}方法.
*/
removeChildren: function(destroy) {
var self = this,
i,
t = [].concat(self.get('children'));
for (i = 0; i < t.length; i++) {
self.removeChild(t[i], destroy);
}
},
/**
* 根据索引获取子控件
*
* control.getChildAt(0);//获取第一个子控件
* control.getChildAt(2); //获取第三个子控件
*
* @param {Number} index 0-based 索引值.
* @return {BUI.Component.Controller} 子控件或者null
*/
getChildAt: function(index) {
var children = this.get('children');
return children[index] || null;
},
/**
* 根据Id获取子控件
*
* control.getChild('id'); //从控件的直接子控件中查找
* control.getChild('id',true);//递归查找所有子控件,包含子控件的子控件
*
* @param {String} id 控件编号
* @param {Boolean} deep 是否继续查找在子控件中查找
* @return {BUI.Component.Controller} 子控件或者null
*/
getChild: function(id, deep) {
return this.getChildBy(function(item) {
return item.get('id') === id;
}, deep);
},
/**
* 通过匹配函数查找子控件,返回第一个匹配的对象
*
* control.getChildBy(function(child){//从控件的直接子控件中查找
* return child.get('id') = '1243';
* });
*
* control.getChild(function(child){//递归查找所有子控件,包含子控件的子控件
* return child.get('id') = '1243';
* },true);
*
* @param {Function} math 查找的匹配函数
* @param {Boolean} deep 是否继续查找在子控件中查找
* @return {BUI.Component.Controller} 子控件或者null
*/
getChildBy: function(math, deep) {
return this.getChildrenBy(math, deep)[0] || null;
},
/**
* 获取控件的附加高度 = control.get('el').outerHeight() - control.get('el').height()
* @protected
* @return {Number} 附加宽度
*/
getAppendHeight: function() {
var el = this.get('el');
return el.outerHeight() - el.height();
},
/**
* 获取控件的附加宽度 = control.get('el').outerWidth() - control.get('el').width()
* @protected
* @return {Number} 附加宽度
*/
getAppendWidth: function() {
var el = this.get('el');
return el.outerWidth() - el.width();
},
/**
* 查找符合条件的子控件
*
* control.getChildrenBy(function(child){//从控件的直接子控件中查找
* return child.get('type') = '1';
* });
*
* control.getChildrenBy(function(child){//递归查找所有子控件,包含子控件的子控件
* return child.get('type') = '1';
* },true);
*
* @param {Function} math 查找的匹配函数
* @param {Boolean} deep 是否继续查找在子控件中查找,如果符合上面的匹配函数,则不再往下查找
* @return {BUI.Component.Controller[]} 子控件数组
*/
getChildrenBy: function(math, deep) {
var self = this,
results = [];
if (!math) {
return results;
}
self.eachChild(function(child) {
if (math(child)) {
results.push(child);
} else if (deep) {
results = results.concat(child.getChildrenBy(math, deep));
}
});
return results;
},
/**
* 遍历子元素
*
* control.eachChild(function(child,index){ //遍历子控件
*
* });
*
* @param {Function} func 迭代函数,函数原型function(child,index)
*/
eachChild: function(func) {
BUI.each(this.get('children'), func);
},
/**
* Handle dblclick events. By default, this performs its associated action by calling
* {@link BUI.Component.Controller#performActionInternal}.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleDblClick: function(ev) {
this.performActionInternal(ev);
if (!this.isChildrenElement(ev.target)) {
this.fire('dblclick', {
domTarget: ev.target,
domEvent: ev
});
}
},
/**
* Called by it's container component to dispatch mouseenter event.
* @private
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseOver: function(ev) {
var self = this,
el = self.get('el');
if (!isMouseEventWithinElement(ev, el)) {
self.handleMouseEnter(ev);
}
},
/**
* Called by it's container component to dispatch mouseleave event.
* @private
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseOut: function(ev) {
var self = this,
el = self.get('el');
if (!isMouseEventWithinElement(ev, el)) {
self.handleMouseLeave(ev);
}
},
/**
* Handle mouseenter events. If the component is not disabled, highlights it.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseEnter: function(ev) {
var self = this;
this.set('highlighted', !!ev);
self.fire('mouseenter', {
domTarget: ev.target,
domEvent: ev
});
},
/**
* Handle mouseleave events. If the component is not disabled, de-highlights it.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseLeave: function(ev) {
var self = this;
self.set('active', false);
self.set('highlighted', !ev);
self.fire('mouseleave', {
domTarget: ev.target,
domEvent: ev
});
},
/**
* Handles mousedown events. If the component is not disabled,
* If the component is activeable, then activate it.
* If the component is focusable, then focus it,
* else prevent it from receiving keyboard focus.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseDown: function(ev) {
var self = this,
n,
target = $(ev.target),
isMouseActionButton = ev['which'] === 1,
el;
if (isMouseActionButton) {
el = self.getKeyEventTarget();
if (self.get('activeable')) {
self.set('active', true);
}
if (self.get('focusable')) {
//如果不是input,select,area等可以获取焦点的控件,那么设置此控件的focus
/*if(target[0] == el[0] || (!target.is('input,select,area') && !target.attr('tabindex'))){
el[0].focus();
}*/
self.setInternal('focused', true);
}
if (!self.get('allowTextSelection')) {
// firefox /chrome 不会引起焦点转移
n = ev.target.nodeName;
n = n && n.toLowerCase();
// do not prevent focus when click on editable element
if (n !== 'input' && n !== 'textarea') {
ev.preventDefault();
}
}
if (!self.isChildrenElement(ev.target)) {
self.fire('mousedown', {
domTarget: ev.target,
domEvent: ev
});
}
}
},
/**
* Handles mouseup events.
* If this component is not disabled, performs its associated action by calling
* {@link BUI.Component.Controller#performActionInternal}, then deactivates it.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleMouseUp: function(ev) {
var self = this,
isChildrenElement = self.isChildrenElement(ev.target);
// 左键
if (self.get('active') && ev.which === 1) {
self.performActionInternal(ev);
self.set('active', false);
if (!isChildrenElement) {
self.fire('click', {
domTarget: ev.target,
domEvent: ev
});
}
}
if (!isChildrenElement) {
self.fire('mouseup', {
domTarget: ev.target,
domEvent: ev
});
}
},
/**
* Handles context menu.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleContextMenu: function(ev) {},
/**
* Handles focus events. Style focused class.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleFocus: function(ev) {
this.set('focused', !!ev);
this.fire('focus', {
domEvent: ev,
domTarget: ev.target
});
},
/**
* Handles blur events. Remove focused class.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleBlur: function(ev) {
this.set('focused', !ev);
this.fire('blur', {
domEvent: ev,
domTarget: ev.target
});
},
/**
* Handle enter keydown event to {@link BUI.Component.Controller#performActionInternal}.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleKeyEventInternal: function(ev) {
var self = this,
isChildrenElement = self.isChildrenElement(ev.target);
if (ev.which === 13) {
if (!isChildrenElement) {
self.fire('click', {
domTarget: ev.target,
domEvent: ev
});
}
return this.performActionInternal(ev);
}
if (!isChildrenElement) {
self.fire('keydown', {
domTarget: ev.target,
domEvent: ev
});
}
},
/**
* Handle keydown events.
* If the component is not disabled, call {@link BUI.Component.Controller#handleKeyEventInternal}
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
handleKeydown: function(ev) {
var self = this;
if (self.handleKeyEventInternal(ev)) {
ev.halt();
return true;
}
},
handleKeyUp: function(ev) {
var self = this;
if (!self.isChildrenElement(ev.target)) {
self.fire('keyup', {
domTarget: ev.target,
domEvent: ev
});
}
},
/**
* Performs the appropriate action when this component is activated by the user.
* @protected
* @param {jQuery.Event} ev DOM event to handle.
*/
performActionInternal: function(ev) {},
/**
* 析构函数
* @protected
*/
destructor: function() {
var self = this,
id,
i,
view,
children = self.get('children');
id = self.get('id');
for (i = 0; i < children.length; i++) {
children[i].destroy && children[i].destroy();
}
self.get('view').destroy();
Manager.removeComponent(id);
},
//覆写set方法
set: function(name, value, opt) {
var _self = this,
view = _self.__view,
attr = _self.__attrs[name],
ucName,
ev,
m;
if (BUI.isObject(name)) {
opt = value;
BUI.each(name, function(v, k) {
_self.set(k, v, opt);
});
}
if (!view || !attr || (opt && opt.silent)) { //未初始化view或者没用定义属性
Controller.superclass.set.call(this, name, value, opt);
return _self;
}
var prevVal = Controller.superclass.get.call(this, name);
//如果未改变值不进行修改
if (!$.isPlainObject(value) && !BUI.isArray(value) && prevVal === value) {
return _self;
}
ucName = BUI.ucfirst(name);
m = '_uiSet' + ucName;
//触发before事件
_self.fire('before' + ucName + 'Change', {
attrName: name,
prevVal: prevVal,
newVal: value
});
_self.setInternal(name, value);
value = _self.__attrVals[name];
if (view && attr.view) {
view.set(name, value);
//return _self;
}
ev = {
attrName: name,
prevVal: prevVal,
newVal: value
};
//触发before事件
_self.fire('after' + ucName + 'Change', ev);
if (_self.get('binded') && _self[m]) {
_self[m](value, ev);
}
return _self;
},
//覆写get方法,改变时同时改变view的值
get: function(name) {
var _self = this,
view = _self.__view,
attr = _self.__attrs[name],
value = Controller.superclass.get.call(this, name);
if (value !== undefined) {
return value;
}
if (view && attr && attr.view) {
return view.get(name);
}
return value;
}
}, {
ATTRS: {
/**
* 控件的Html 内容
*
* new Control({
* content : '内容',
* render : '#c1'
* });
*
* @cfg {String|jQuery} content
*/
/**
* 控件的Html 内容
* @type {String|jQuery}
*/
content: {
view: 1
},
/**
* 控件根节点使用的标签
*
* new Control({
* elTagName : 'ul',
* content : '内容 ', //控件的DOM <ul><li>内容</li></ul>
* render : '#c1'
* });
*
* @cfg {String} elTagName
*/
elTagName: {
// 生成标签名字
view: true,
value: 'div'
},
/**
* 子元素的默认 xclass,配置child的时候没必要每次都填写xclass
* @type {String}
*/
defaultChildClass: {
},
/**
* 如果控件未设置 xclass,同时父元素设置了 defaultChildClass,那么
* xclass = defaultChildClass + '-' + xtype
*
* A.ATTRS = {
* defaultChildClass : {
* value : 'b'
* }
* }
* //类B 的xclass = 'b'类 B1的xclass = 'b-1',类 B2的xclass = 'b-2',那么
* var a = new A({
* children : [
* {content : 'b'}, //B类
* {content : 'b1',xtype:'1'}, //B1类
* {content : 'b2',xtype:'2'}, //B2类
* ]
* });
*
* @type {String}
*/
xtype: {
},
/**
* 标示控件的唯一编号,默认会自动生成
* @cfg {String} id
*/
/**
* 标示控件的唯一编号,默认会自动生成
* @type {String}
*/
id: {
view: true
},
/**
* 控件宽度
*
* new Control({
* width : 200 // 200,'200px','20%'
* });
*
* @cfg {Number|String} width
*/
/**
* 控件宽度
*
* control.set('width',200);
* control.set('width','200px');
* control.set('width','20%');
*
* @type {Number|String}
*/
width: {
view: 1
},
/**
* 控件宽度
*
* new Control({
* height : 200 // 200,'200px','20%'
* });
*
* @cfg {Number|String} height
*/
/**
* 控件宽度
*
* control.set('height',200);
* control.set('height','200px');
* control.set('height','20%');
*
* @type {Number|String}
*/
height: {
view: 1
},
/**
* 控件根节点应用的样式
*
* new Control({
* elCls : 'test',
* content : '内容',
* render : '#t1' //<div id='t1'><div class="test">内容</div></div>
* });
*
* @cfg {String} elCls
*/
/**
* 控件根节点应用的样式 css class
* @type {String}
*/
elCls: {
view: 1
},
/**
* @cfg {Object} elStyle
* 控件根节点应用的css属性
*
* var cfg = {elStyle : {width:'100px', height:'200px'}};
*
*/
/**
* 控件根节点应用的css属性,以键值对形式
* @type {Object}
*
* control.set('elStyle', {
* width:'100px',
* height:'200px'
* });
*
*/
elStyle: {
view: 1
},
/**
* @cfg {Object} elAttrs
* 控件根节点应用的属性,以键值对形式:
*
* new Control({
* elAttrs :{title : 'tips'}
* });
*
*/
/**
* @type {Object}
* 控件根节点应用的属性,以键值对形式:
* { title : 'tips'}
* @ignore
*/
elAttrs: {
view: 1
},
/**
* 将控件插入到指定元素前
*
* new Control({
* elBefore : '#t1'
* });
*
* @cfg {jQuery} elBefore
*/
/**
* 将控件插入到指定元素前
* @type {jQuery}
* @ignore
*/
elBefore: {
// better named to renderBefore, too late !
view: 1
},
/**
* 只读属性,根节点DOM
* @type {jQuery}
*/
el: {
view: 1
},
/**
* 控件支持的事件
* @type {Object}
* @protected
*/
events: {
value: {
/**
* 点击事件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'click': true,
/**
* 双击事件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'dblclick': true,
/**
* 鼠标移入控件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'mouseenter': true,
/**
* 鼠标移出控件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'mouseleave': true,
/**
* 键盘按下按键事件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'keydown': true,
/**
* 键盘按键抬起控件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'keyup': true,
/**
* 控件获取焦点事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'focus': false,
/**
* 控件丢失焦点事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'blur': false,
/**
* 鼠标按下控件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'mousedown': true,
/**
* 鼠标抬起控件,此事件会冒泡,所以可以在父元素上监听所有子元素的此事件
* @event
* @param {Object} e 事件对象
* @param {BUI.Component.Controller} e.target 触发事件的对象
* @param {jQuery.Event} e.domEvent DOM触发的事件
* @param {HTMLElement} e.domTarget 触发事件的DOM节点
*/
'mouseup': true,
/**
* 控件显示
* @event
*/
'show': false,
/**
* 控件隐藏
* @event
*/
'hide': false
}
},
/**
* 指定控件的容器
*
* new Control({
* render : '#t1',
* elCls : 'test',
* content : '123' //<div id="t1"><div class="test bui-xclass"><span>123</span></div></div>
* });
*
* @cfg {jQuery} render
*/
/**
* 指定控件的容器
* @type {jQuery}
* @ignore
*/
render: {
view: 1
},
/**
* ARIA 标准中的role,不要更改此属性
* @type {String}
* @protected
*/
role: {
view: 1
},
/**
* 状态相关的样式,默认情况下会使用 前缀名 + xclass + '-' + 状态名
*
* new Control({
* visibleMode: 'visibility'
* });
*
* @cfg {String} [visibleMode = 'display']
*/
/**
* 控件的可视方式,使用 css
* - 'display' 或者
* - 'visibility'
*
* control.set('visibleMode','display')
*
* @type {String}
*/
visibleMode: {
view: 1,
value: 'display'
},
/**
* 控件是否可见
*
* new Control({
* visible : false //隐藏
* });
*
* @cfg {Boolean} [visible = true]
*/
/**
* 控件是否可见
*
* control.set('visible',true); //control.show();
* control.set('visible',false); //control.hide();
*
* @type {Boolean}
* @default true
*/
visible: {
value: true,
view: 1
},
/**
* 是否允许处理鼠标事件
* @default true.
* @type {Boolean}
* @protected
*/
handleMouseEvents: {
value: true
},
/**
* 控件是否可以获取焦点
* @default true.
* @protected
* @type {Boolean}
*/
focusable: {
value: false,
view: 1
},
/**
* 一旦使用loader的默认配置
* @protected
* @type {Object}
*/
defaultLoaderCfg: {
value: {
property: 'content',
autoLoad: true
}
},
/**
* 控件内容的加载器
* @type {BUI.Component.Loader}
*/
loader: {
getter: function(v) {
var _self = this,
defaultCfg;
if (v && !v.isLoader) {
v.target = _self;
defaultCfg = _self.get('defaultLoaderCfg')
v = new Loader(BUI.merge(defaultCfg, v));
_self.setInternal('loader', v);
}
return v;
}
},
/**
* 1. Whether allow select this component's text.
* control.set('disabled',true); //== control.disable();
* control.set('disabled',false); //== control.enable();
*
* @type {Boolean}
* @default false
*/
disabled: {
view: 1,
value: false
},
/**
* 渲染控件的View类.
* @protected
* @cfg {BUI.Component.View} [xview = BUI.Component.View]
*/
/**
* 渲染控件的View类.
* @protected
* @type {BUI.Component.View}
*/
xview: {
value: View
}
},
PARSER: {
visible: function(el) {
var _self = this,
display = el.css('display'),
visibility = el.css('visibility'),
visibleMode = _self.get('visibleMode');
if ((display == 'none' && visibleMode == 'display') || (visibility == 'hidden' && visibleMode == 'visibility')) {
return false;
}
return true;
},
disabled: function(el){
var _self = this,
cls = _self.get('prefixCls') + _self.get('xclass') + '-disabled';
return el.hasClass(cls);
}
}
}, {
xclass: 'controller',
priority: 0
});
module.exports = Controller;
});
define("bui/common/component/loader", ["jquery"], function(require, exports, module){
/**
* @fileOverview 加载控件内容
* @ignore
*/
'use strict';
var $ = require("jquery"),
BUI = require("bui/common/util"),
Base = require("bui/common/base"),
/**
* @class BUI.Component.Loader
* @extends BUI.Base
* ** 控件的默认Loader属性是:**
*
*
* defaultLoader : {
* value : {
* property : 'content',
* autoLoad : true
* }
* }
*
* ** 一般的控件默认读取html,作为控件的content值 **
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json'
* }
* });
*
* control.render();
*
*
* ** 可以修改Loader的默认属性,加载children **
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/children.json',
* property : 'children',
* dataType : 'json'
* }
* });
*
* control.render();
*
* 加载控件内容的类,一般不进行实例化
*/
Loader = function(config) {
Loader.superclass.constructor.call(this, config);
this._init();
};
Loader.ATTRS = {
/**
* 加载内容的地址
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json'
* }
* });
*
* control.render();
*
* @cfg {String} url
*/
url: {
},
/**
* 对应的控件,加载完成后设置属性到对应的控件
* @readOnly
* @type {BUI.Component.Controller}
*/
target: {
},
/**
* @private
* 是否load 过
*/
hasLoad: {
value: false
},
/**
* 是否自动加载数据
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* autoLoad : false
* }
* });
*
* control.render();
*
* @cfg {Boolean} [autoLoad = true]
*/
autoLoad: {
},
/**
* 延迟加载
*
* - event : 触发加载的事件
* - repeat :是否重复加载
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* lazyLoad : {
* event : 'show',
* repeat : true
* }
* }
* });
*
* control.render();
*
* @cfg {Object} [lazyLoad = null]
*/
lazyLoad: {
},
/**
* 加载返回的数据作为控件的那个属性
*
* var control = new BUI.List.SimpleList({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* dataType : 'json',
* property : 'items'
* }
* });
*
* control.render();
*
* @cfg {String} property
*/
property: {
},
/**
* 格式化返回的数据
* @cfg {Function} renderer
*/
renderer: {
value: function(value) {
return value;
}
},
/**
* 加载数据时是否显示屏蔽层和加载提示 {@link BUI.Mask.LoadMask}
*
* - loadMask : true时使用loadMask 默认的配置信息
* - loadMask : {msg : '正在加载,请稍后。。'} LoadMask的配置信息
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* loadMask : true
* }
* });
*
* control.render();
*
* @cfg {Boolean|Object} [loadMask = false]
*/
loadMask: {
value: false
},
/**
* ajax 请求返回数据的类型
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* dataType : 'json',
* property : 'items'
* }
* });
*
* control.render();
*
* @cfg {String} [dataType = 'text']
*/
dataType: {
value: 'text'
},
/**
* Ajax请求的配置项,会覆盖 url,dataType数据
* @cfg {Object} ajaxOptions
*/
ajaxOptions: {
//shared : false,
value: {
type: 'get',
cache: false
}
},
/**
* 初始化的请求参数
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* params : {
* a : 'a',
* b : 'b'
* }
* }
* });
*
* control.render();
*
* @cfg {Object} params
* @default null
*/
params: {
},
/**
* 附加参数,每次请求都带的参数
* @cfg {Object} appendParams
*/
appendParams: {
},
/**
* 最后一次请求的参数
* @readOnly
* @private
* @type {Object}
*/
lastParams: {
shared: false,
value: {}
},
/**
* 加载数据,并添加属性到控件后的回调函数
* - data : 加载的数据
* - params : 加载的参数
*
* var control = new BUI.Component.Controller({
* render : '#c1',
* loader : {
* url : 'data/text.json',
* callback : function(text){
* var target = this.get('target');//control
* //TO DO
* }
* }
* });
*
* control.render();
*
* @cfg {Function} callback
*/
callback: {
},
/**
* 失败的回调函数
* - response : 返回的错误对象
* - params : 加载的参数
* @cfg {Function} failure
*/
failure: {
}
};
BUI.extend(Loader, Base);
BUI.augment(Loader, {
/**
* @protected
* 是否是Loader
* @type {Boolean}
*/
isLoader: true,
//初始化
_init: function() {
var _self = this,
autoLoad = _self.get('autoLoad'),
params = _self.get('params');
_self._initMask();
if (autoLoad) {
_self.load(params);
} else {
_self._initParams();
_self._initLazyLoad();
}
},
//初始化延迟加载
_initLazyLoad: function() {
var _self = this,
target = _self.get('target'),
lazyLoad = _self.get('lazyLoad');
if (target && lazyLoad && lazyLoad.event) {
target.on(lazyLoad.event, function() {
if (!_self.get('hasLoad') || lazyLoad.repeat) {
_self.load();
}
});
}
},
/**
* 初始化mask
* @private
*/
_initMask: function() {
var _self = this,
target = _self.get('target'),
loadMask = _self.get('loadMask');
if (target && loadMask) {
require.async('bui/mask', function(Mask) {
var cfg = $.isPlainObject(loadMask) ? loadMask : {};
loadMask = new Mask.LoadMask(BUI.mix({
el: target.get('el')
}, cfg));
_self.set('loadMask', loadMask);
});
}
},
//初始化查询参数
_initParams: function() {
var _self = this,
lastParams = _self.get('lastParams'),
params = _self.get('params');
//初始化 参数
BUI.mix(lastParams, params);
},
/**
* 加载内容
* @param {Object} params 加载数据的参数
*/
load: function(params) {
var _self = this,
url = _self.get('url'),
ajaxOptions = _self.get('ajaxOptions'),
lastParams = _self.get('lastParams'),
appendParams = _self.get('appendParams');
//BUI.mix(true,lastParams,appendParams,params);
params = params || lastParams;
params = BUI.merge(appendParams, params); //BUI.cloneObject(lastParams);
_self.set('lastParams', params);
//未提供加载地址,阻止加载
if (!url) {
return;
}
_self.onBeforeLoad();
_self.set('hasLoad', true);
$.ajax(BUI.mix({
dataType: _self.get('dataType'),
data: params,
url: url,
success: function(data) {
_self.onload(data, params);
},
error: function(jqXHR, textStatus, errorThrown) {
_self.onException({
jqXHR: jqXHR,
textStatus: textStatus,
errorThrown: errorThrown
}, params);
}
}, ajaxOptions));
},
/**
* @private
* 加载前
*/
onBeforeLoad: function() {
var _self = this,
loadMask = _self.get('loadMask');
if (loadMask && loadMask.show) {
loadMask.show();
}
},
/**
* @private
* 加载完毕
*/
onload: function(data, params) {
var _self = this,
loadMask = _self.get('loadMask'),
property = _self.get('property'),
callback = _self.get('callback'),
renderer = _self.get('renderer'),
target = _self.get('target');
if (BUI.isString(data)) {
target.set(property, ''); //防止2次返回的数据一样
}
target.set(property, renderer.call(_self, data));
/**/
if (loadMask && loadMask.hide) {
loadMask.hide();
}
if (callback) {
callback.call(this, data, params);
}
},
/**
* @private
* 加载出错
*/
onException: function(response, params) {
var _self = this,
failure = _self.get('failure');
if (failure) {
failure.call(this, response, params);
}
}
});
module.exports = Loader;
});
define("bui/data", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview Data 命名空间的入口文件
* @ignore
*/
var BUI = require("bui/common"),
Data = BUI.namespace('Data');
BUI.mix(Data, {
Sortable: require("bui/data/sortable"),
Proxy: require("bui/data/proxy"),
AbstractStore: require("bui/data/abstractstore"),
Store: require("bui/data/store"),
Node: require("bui/data/node"),
TreeStore: require("bui/data/treestore")
});
module.exports = Data;
});
define("bui/data/sortable", [], function(require, exports, module){
/**
* @fileOverview 可排序扩展类
* @ignore
*/
var ASC = 'ASC',
DESC = 'DESC';
/**
* 排序扩展方法,无法直接使用
* 请在继承了 {@link BUI.Base}的类上使用
* @class BUI.Data.Sortable
* @extends BUI.Base
*/
var sortable = function(){
};
sortable.ATTRS =
{
/**
* 比较函数
* @cfg {Function} compareFunction
* 函数原型 function(v1,v2),比较2个字段是否相等
* 如果是字符串则按照本地比较算法,否则使用 > ,== 验证
*/
compareFunction:{
value : function(v1,v2){
if(v1 === undefined){
v1 = '';
}
if(v2 === undefined){
v2 = '';
}
if(BUI.isString(v1)){
return v1.localeCompare(v2);
}
if(v1 > v2){
return 1;
}else if(v1 === v2){
return 0;
}else{
return -1;
}
}
},
/**
* 排序字段
* @cfg {String} sortField
*/
/**
* 排序字段
* @type {String}
*/
sortField : {
},
/**
* 排序方向,'ASC'、'DESC'
* @cfg {String} [sortDirection = 'ASC']
*/
/**
* 排序方向,'ASC'、'DESC'
* @type {String}
*/
sortDirection : {
value : 'ASC'
},
/**
* 排序信息
*
* var store = new Data.Store({
* url : 'data.php', //设置加载数据的URL
* autoLoad : true //创建Store时自动加载数据
* });
*
* @cfg {Boolean} [autoLoad=false]
*/
autoLoad: {
value :false
},
/**
* 是否服务器端过滤数据,如果设置此属性,当调用filter()函数时发送请求
* @type {Object}
*/
remoteFilter: {
value : false
},
/**
* 上次查询的参数
* @type {Object}
* @readOnly
*/
lastParams : {
shared : false,
value : {}
},
/**
* 初始化时查询的参数,在初始化时有效
*
* var store = new Data.Store({
* url : 'data.php', //设置加载数据的URL
* autoLoad : true, //创建Store时自动加载数据
* params : { //设置请求时的参数
* id : '1',
* type : '1'
* }
* });
*
* @cfg {Object} params
*/
params : {
},
/**
* 错误字段,包含在返回信息中表示错误信息的字段
*
* //可以修改接收的后台参数的含义
* var store = new Store({
* url : 'data.json',
* errorProperty : 'errorMsg', //存放错误信息的字段(error)
* hasErrorProperty : 'isError', //是否错误的字段(hasError)
* root : 'data', //存放数据的字段名(rows)
* totalProperty : 'total' //存放记录总数的字段名(results)
* });
*
* @cfg {String} [errorProperty='error']
*/
/**
* 错误字段
* @type {String}
* @ignore
*/
errorProperty : {
value : 'error'
},
/**
* 是否存在错误,加载数据时如果返回错误,此字段表示有错误发生
*
* //可以修改接收的后台参数的含义
* var store = new Store({
* url : 'data.json',
* errorProperty : 'errorMsg', //存放错误信息的字段(error)
* hasErrorProperty : 'isError', //是否错误的字段(hasError)
* root : 'data', //存放数据的字段名(rows)
* totalProperty : 'total' //存放记录总数的字段名(results)
* });
*
* @cfg {String} [hasErrorProperty='hasError']
*/
/**
* 是否存在错误
* @type {String}
* @default 'hasError'
* @ignore
*/
hasErrorProperty : {
value : 'hasError'
},
/**
* 数据代理对象,用于加载数据的ajax配置,{@link BUI.Data.Proxy}
*
* var store = new Data.Store({
* url : 'data.php', //设置加载数据的URL
* autoLoad : true, //创建Store时自动加载数据
* proxy : {
* method : 'post',
* dataType : 'jsonp'
* }
* });
*
* @cfg {Object|BUI.Data.Proxy} proxy
*/
proxy : {
shared : false,
value : {
}
},
/**
* 请求数据的地址,通过ajax加载数据,
* 此参数设置则加载远程数据
* ** 你可以设置在proxy外部 **
*
* var store = new Data.Store({
* url : 'data.php', //设置加载数据的URL
* autoLoad : true, //创建Store时自动加载数据
* proxy : {
* method : 'post',
* dataType : 'jsonp'
* }
* });
*
* ** 你也可以设置在proxy上 **
*
* var store = new Data.Store({
* autoLoad : true, //创建Store时自动加载数据
* proxy : {
* url : 'data.php', //设置加载数据的URL
* method : 'post',
* dataType : 'jsonp'
* }
* });
*
* 否则把 {BUI.Data.Store#cfg-data}作为本地缓存数据加载
* @cfg {String} url
*/
/**
* 请求数据的url
*
* //更改url
* store.get('proxy').set('url',url);
*
* @type {String}
*/
url : {
},
events : {
value : [
/**
* 数据接受改变,所有增加、删除、修改的数据记录清空
* @name BUI.Data.Store#acceptchanges
* @event
*/
'acceptchanges',
/**
* 当数据加载完成后
* @name BUI.Data.Store#load
* @event
* @param {jQuery.Event} e 事件对象,包含加载数据时的参数
*/
'load',
/**
* 当数据加载前
* @name BUI.Data.Store#beforeload
* @event
*/
'beforeload',
/**
* 发生在,beforeload和load中间,数据已经获取完成,但是还未触发load事件,用于获取返回的原始数据
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.data 从服务器端返回的数据
*/
'beforeprocessload',
/**
* 当添加数据时触发该事件
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.record 添加的数据
*/
'add',
/**
* 加载数据发生异常时触发
* @event
* @name BUI.Data.Store#exception
* @param {jQuery.Event} e 事件对象
* @param {String|Object} e.error 加载数据时返回的错误信息或者加载数据失败,浏览器返回的信息(httpResponse 对象 的textStatus)
* @param {String} e.responseText 网络或者浏览器加载数据发生错误是返回的httpResponse 对象的responseText
*/
'exception',
/**
* 当删除数据是触发该事件
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.data 删除的数据
*/
'remove',
/**
* 当更新数据指定字段时触发该事件
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.record 更新的数据
* @param {Object} e.field 更新的字段
* @param {Object} e.value 更新的值
*/
'update',
/**
* 前端发生排序时触发
* @name BUI.Data.Store#localsort
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.field 排序的字段
* @param {Object} e.direction 排序的方向 'ASC','DESC'
*/
'localsort',
/**
* 前端发生过滤时触发
* @event
* @param {jQuery.Event} e 事件对象
* @param {Array} e.data 过滤完成的数据
* @param {Function} e.filter 过滤器
*/
'filtered'
]
},
/**
* 本地数据源,使用本地数据源时会使用{@link BUI.Data.Proxy.Memery}
* @cfg {Array} data
*/
/**
* 本地数据源
* @type {Array}
*/
data : {
setter : function(data){
var _self = this,
proxy = _self.get('proxy');
if(proxy.set){
proxy.set('data',data);
}else{
proxy.data = data;
}
//设置本地数据时,把autoLoad置为true
_self.set('autoLoad',true);
}
}
};
BUI.extend(AbstractStore,BUI.Base);
BUI.augment(AbstractStore,{
/**
* 是否是数据缓冲对象,用于判断对象
* @type {Boolean}
*/
isStore : true,
/**
* @private
* 初始化
*/
_init : function(){
var _self = this;
_self.beforeInit();
//初始化结果集
_self._initParams();
_self._initProxy();
_self._initData();
},
/**
* @protected
* 初始化之前
*/
beforeInit : function(){
},
//初始化数据,如果默认加载数据,则加载数据
_initData : function(){
var _self = this,
autoLoad = _self.get('autoLoad');
if(autoLoad){
_self.load();
}
},
//初始化查询参数
_initParams : function(){
var _self = this,
lastParams = _self.get('lastParams'),
params = _self.get('params');
//初始化 参数
BUI.mix(lastParams,params);
},
/**
* @protected
* 初始化数据代理类
*/
_initProxy : function(){
var _self = this,
url = _self.get('url'),
proxy = _self.get('proxy');
if(!(proxy instanceof Proxy)){
if(url){
proxy.url = url;
}
//异步请求的代理类
if(proxy.type === 'ajax' || proxy.url){
proxy = new Proxy.Ajax(proxy);
}else{
proxy = new Proxy.Memery(proxy);
}
_self.set('proxy',proxy);
}
},
/**
* 加载数据
*
* //一般调用
* store.load(params);
*
* //使用回调函数
* store.load(params,function(data){
*
* });
*
* //load有记忆参数的功能
* store.load({id : '123',type="1"});
* //下一次调用
* store.load();默认使用上次的参数,可以对对应的参数进行覆盖
*
* @param {Object} params 参数键值对
* @param {Function} fn 回调函数,默认为空
*/
load : function(params,callback){
var _self = this,
proxy = _self.get('proxy'),
lastParams = _self.get('lastParams');
BUI.mix(lastParams,_self.getAppendParams(),params);
_self.fire('beforeload',{params:lastParams});
//防止异步请求未结束,又发送新请求回调参数错误
params = BUI.cloneObject(lastParams);
proxy.read(lastParams,function(data){
_self.onLoad(data,params);
if(callback){
callback(data,params);
}
},_self);
},
/**
* 触发过滤
* @protected
*/
onFiltered : function(data,filter){
var _self = this;
_self.fire('filtered',{data : data,filter : filter});
},
/**
* 加载完数据
* @protected
* @template
*/
onLoad : function(data,params){
var _self = this;
var processResult = _self.processLoad(data,params);
//如果处理成功,返回错误时,不进行后面的处理
if(processResult){
_self.afterProcessLoad(data,params);
}
},
/**
* 获取当前缓存的纪录
*/
getResult : function(){
},
/**
* 过滤数据,此函数的执行同属性 remoteFilter关联密切
*
* - remoteFilter == true时:此函数只接受字符串类型的过滤参数,将{filter : filterStr}参数传输到服务器端
* - remoteFilter == false时:此函数接受比对函数,只有当函数返回true时生效
*
* @param {Function|String} fn 过滤函数
* @return {Array} 过滤结果
*/
filter : function(filter){
var _self = this,
remoteFilter = _self.get('remoteFilter'),
result;
filter = filter || _self.get('filter');
if(remoteFilter){
_self.load({filter : filter});
}else if(filter){
_self.set('filter',filter);
//如果result有值时才会进行filter
if(_self.getResult().length > 0){
result = _self._filterLocal(filter);
_self.onFiltered(result,filter);
}
}
},
/**
* @protected
* 过滤缓存的数据
* @param {Function} fn 过滤函数
* @return {Array} 过滤结果
*/
_filterLocal : function(fn){
},
/**
* 获取过滤后的数据,仅当本地过滤(remoteFilter = false)时有效
* @return {Array} 过滤过的数据
*/
getFilterResult: function(){
var filter = this.get('filter');
if(filter) {
return this._filterLocal(filter);
}
else {
return this.getResult();
}
},
_clearLocalFilter : function(){
this.set('filter', null);
},
/**
* 清理过滤
*/
clearFilter : function(){
var _self = this,
remoteFilter = _self.get('remoteFilter'),
result;
if(remoteFilter){
_self.load({filter : ''});
}else{
_self._clearLocalFilter();
result = _self.getFilterResult();
_self.onFiltered(result, null);
}
},
/**
* @private
* 加载完数据处理数据
*/
processLoad : function(data,params){
var _self = this,
hasErrorField = _self.get('hasErrorProperty');
_self.fire('beforeprocessload',{data : data});
//获取的原始数据
_self.fire('beforeProcessLoad',data);
if(BUI.getValue(data,hasErrorField) || data.exception){
_self.onException(data);
return false;
}
return true;
},
/**
* @protected
* @template
* 处理数据后
*/
afterProcessLoad : function(data,params){
},
/**
* @protected
* 处理错误函数
* @param {*} data 出错对象
*/
onException : function(data){
var _self = this,
errorProperty = _self.get('errorProperty'),
obj = {};
//网络异常、转码错误之类,发生在json获取或转变时
if(data.exception){
obj.type = 'exception';
obj.error = data.exception;
}else{//用户定义的错误
obj.type = 'error';
obj.error = BUI.getValue(data,errorProperty);
}
_self.fire('exception',obj);
},
/**
* 是否包含数据
* @return {Boolean}
*/
hasData : function(){
},
/**
* 获取附加的参数
* @template
* @protected
* @return {Object} 附加的参数
*/
getAppendParams : function(){
return {};
}
});
module.exports = AbstractStore;
});
define("bui/data/store", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 数据缓冲对象
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
Proxy = require("bui/data/proxy"),
AbstractStore = require("bui/data/abstractstore"),
Sortable = require("bui/data/sortable");
//移除数据
function removeAt(index,array){
if(index < 0){
return;
}
var records = array,
record = records[index];
records.splice(index,1);
return record;
}
function removeFrom(record,array){
var index = BUI.Array.indexOf(record,array);
if(index >= 0){
removeAt(index,array);
}
}
function contains(record,array){
return BUI.Array.indexOf(record,array) !== -1;
}
/**
* 用于加载数据,缓冲数据的类
*
*
*
* var store = new Store({
* data : [{},{}]
* });
*
* ** 异步加载数据 **
*
* var store = new Store({
* url : 'data.json',
* autoLoad : true,
* params : {id : '123'},
* sortInfo : {
* field : 'id',
* direction : 'ASC' //ASC,DESC
* }
* });
*
*
* @class BUI.Data.Store
* @extends BUI.Data.AbstractStore
* @mixins BUI.Data.Sortable
*/
var store = function(config){
store.superclass.constructor.call(this,config);
//this._init();
};
store.ATTRS =
{
/**
* 保存数据时,是否自动更新数据源的数据,常用于添加、删除、更改数据后重新加载数据。
* @cfg {Boolean} autoSync
*/
autoSync : {
value : false
},
/**
* 当前页码
* @cfg {Number} [currentPage=0]
* @ignore
*/
/**
* 当前页码
* @type {Number}
* @ignore
* @readOnly
*/
currentPage:{
value : 0
},
/**
* 删除掉的纪录
* @readOnly
* @private
* @type {Array}
*/
deletedRecords : {
shared : false,
value:[]
},
/**
* 对比2个对象是否相当,在去重、更新、删除,查找数据时使用此函数
* @default
* function(obj1,obj2){
* return obj1 == obj2;
* }
* @type {Object}
* @example
* function(obj1 ,obj2){
* //如果id相等,就认为2个数据相等,可以在添加对象时去重
* //更新对象时,仅提供改变的字段
* return obj1.id == obj2.id;
* }
*
*/
matchFunction : {
value : function(obj1,obj2){
return obj1 == obj2;
}
},
/**
* 更改的纪录集合
* @type {Array}
* @private
* @readOnly
*/
modifiedRecords : {
shared : false,
value:[]
},
/**
* 新添加的纪录集合,只读
* @type {Array}
* @private
* @readOnly
*/
newRecords : {
shared : false,
value : []
},
/**
* 是否远程排序,默认状态下内存排序
* - 由于当前Store存储的不一定是数据源的全集,所以此配置项需要重新读取数据
* - 在分页状态下,进行远程排序,会进行全集数据的排序,并返回首页的数据
* - remoteSort为 false的情况下,仅对当前页的数据进行排序
* @cfg {Boolean} [remoteSort=false]
*/
remoteSort : {
value : false
},
/**
* 缓存的数据,包含以下几个字段
*
* //默认返回数据类型:
* '{"rows":[{"name":"abc"},{"name":"bcd"}],"results":100}'
* //可以修改接收的后台参数的含义
* var store = new Store({
* url : 'data.json',
* errorProperty : 'errorMsg', //存放错误信息的字段(error)
* hasErrorProperty : 'isError', //是否错误的字段(hasError)
* root : 'data', //存放数据的字段名(rows)
* totalProperty : 'total' //存放记录总数的字段名(results)
* });
*
*
*/
root: { value : 'rows'},
/**
* 当前Store缓存的数据条数
* @type {Number}
* @private
* @readOnly
*/
rowCount :{
value : 0
},
/**
* 加载数据时,返回记录的总数的字段,用于分页
* @cfg {String} [totalProperty='results']
*
* //默认返回数据类型:
* '{"rows":[{"name":"abc"},{"name":"bcd"}],"results":100}'
* //可以修改接收的后台参数的含义
* var store = new Store({
* url : 'data.json',
* errorProperty : 'errorMsg', //存放错误信息的字段(error)
* hasErrorProperty : 'isError', //是否错误的字段(hasError)
* root : 'data', //存放数据的字段名(rows)
* totalProperty : 'total' //存放记录总数的字段名(results)
* });
*
*/
totalProperty: {value :'results'},
/**
* 加载数据的起始位置
*
* //初始化时,可以在params中配置
* var store = new Store({
* url : 'data.json',
* params : {
* start : 100
* }
* });
*
* @type {Object}
*/
start:{
value : 0
},
/**
* 每页多少条记录,默认为null,此时不分页,当指定了此值时分页
*
* //当请求的数据分页时
* var store = new Store({
* url : 'data.json',
* pageSize : 30
* });
*
* @cfg {Number} pageSize
*/
pageSize : {
}
};
BUI.extend(store,AbstractStore);
BUI.mixin(store,[Sortable]);
BUI.augment(store,
{
/**
* 添加记录,默认添加在后面
*
* //添加记录
* store.add({id : '2',text: 'new data'});
* //是否去重,重复数据不能添加
* store.add(obj,true); //不能添加重复数据,此时用obj1 === obj2判断
* //使用匹配函去重
* store.add(obj,true,function(obj1,obj2){
* return obj1.id == obj2.id;
* });
*
*
* @param {Array|Object} data 添加的数据,可以是数组,可以是单条记录
* @param {Boolean} [noRepeat = false] 是否去重,可以为空,默认: false
* @param {Function} [match] 匹配函数,可以为空,
* @default 配置项中 matchFunction 属性传入的函数,默认是:
* //使用方式跟类似于add,增加了index参数
* store.add(obj,0);//添加在最前面
*
* @param {Array|Object} data 添加的数据,可以是数组,可以是单条记录
* @param {Number} index 开始添加数据的位置
* @param {Boolean} [noRepeat = false] 是否去重,可以为空,默认: false
* @param {Function} [match] 匹配函数,可以为空,
*/
addAt : function(data,index,noRepeat,match){
var _self = this;
match = match || _self._getDefaultMatch();
if(!BUI.isArray(data)){
data = [data];
}
$.each(data,function(pos,element){
if(!noRepeat || !_self.contains(element,match)){
_self._addRecord(element,pos + index);
_self.get('newRecords').push(element);
removeFrom(element,_self.get('deletedRecords'));
removeFrom(element,_self.get('modifiedRecords'));
}
});
},
/**
* 验证是否存在指定记录
*
* store.contains(obj); //是否包含指定的记录
*
* store.contains(obj,function(obj1,obj2){ //使用匹配函数
* return obj1.id == obj2.id;
* });
*
* @param {Object} record 指定的记录
* @param {Function} [match = function(obj1,obj2){return obj1 == obj2}] 默认为比较2个对象是否相同
* @return {Boolean}
*/
contains :function(record,match){
return this.findIndexBy(record,match)!==-1;
},
/**
* 查找记录,仅返回第一条
*
* var record = store.find('id','123');
*
* @param {String} field 字段名
* @param {String} value 字段值
* @return {Object|null}
*/
find : function(field,value){
var _self = this,
result = null,
records = _self.getResult();
$.each(records,function(index,record){
if(record[field] === value){
result = record;
return false;
}
});
return result;
},
/**
* 查找记录,返回所有符合查询条件的记录
*
* var records = store.findAll('type','0');
*
* @param {String} field 字段名
* @param {String} value 字段值
* @return {Array}
*/
findAll : function(field,value){
var _self = this,
result = [],
records = _self.getResult();
$.each(records,function(index,record){
if(record[field] === value){
result.push(record);
}
});
return result;
},
/**
* 根据索引查找记录
*
* var record = store.findByIndex(1);
*
* @param {Number} index 索引
* @return {Object} 查找的记录
*/
findByIndex : function(index){
return this.getResult()[index];
},
/**
* 查找数据所在的索引位置,若不存在返回-1
*
* var index = store.findIndexBy(obj);
*
* var index = store.findIndexBy(obj,function(obj1,obj2){
* return obj1.id == obj2.id;
* });
*
* @param {Object} target 指定的记录
* @param {Function} [match = matchFunction] @see {BUI.Data.Store#matchFunction}默认为比较2个对象是否相同
* @return {Number}
*/
findIndexBy :function(target,match){
var _self = this,
position = -1,
records = _self.getResult();
match = match || _self._getDefaultMatch();
if(target === null || target === undefined){
return -1;
}
$.each(records,function(index,record){
if(match(target,record)){
position = index;
return false;
}
});
return position;
},
/**
* 获取下一条记录
*
* var record = store.findNextRecord(obj);
*
* @param {Object} record 当前记录
* @return {Object} 下一条记录
*/
findNextRecord : function(record){
var _self = this,
index = _self.findIndexBy(record);
if(index >= 0){
return _self.findByIndex(index + 1);
}
return;
},
/**
* 获取缓存的记录数
*
* var count = store.getCount(); //缓存的数据数量
*
* var totalCount = store.getTotalCount(); //数据的总数,如果有分页时,totalCount != count
*
* @return {Number} 记录数
*/
getCount : function(){
return this.getResult().length;
},
/**
* 获取数据源的数据总数,分页时,当前仅缓存当前页数据
*
* var count = store.getCount(); //缓存的数据数量
*
* var totalCount = store.getTotalCount(); //数据的总数,如果有分页时,totalCount != count
*
* @return {Number} 记录的总数
*/
getTotalCount : function(){
var _self = this,
resultMap = _self.get('resultMap'),
total = _self.get('totalProperty'),
totalVal = BUI.getValue(resultMap,total);
return parseInt(totalVal,10) || 0;
},
/**
* 获取当前缓存的纪录
*
* var records = store.getResult();
*
* @return {Array} 纪录集合
*/
getResult : function(){
var _self = this,
resultMap = _self.get('resultMap'),
root = _self.get('root');
return BUI.getValue(resultMap,root);
},
/**
* 是否包含数据
* @return {Boolean}
*/
hasData : function(){
return this.getCount() !== 0;
},
/**
* 设置数据源,非异步加载时,设置缓存的数据
*
* store.setResult([]); //清空数据
*
* var data = [{},{}];
* store.setResult(data); //重设数据
*
*/
setResult : function(data){
var _self = this,
proxy = _self.get('proxy');
if(proxy instanceof Proxy.Memery){
_self.set('data',data);
_self.load({start:0});
}else{
_self._setResult(data);
//如果有filter则进行过滤
if(_self.get('filter')){
_self.filter();
}
}
},
/**
* 删除一条或多条记录触发 remove 事件.
*
* store.remove(obj); //删除一条记录
*
* store.remove([obj1,obj2...]); //删除多个条记录
*
* store.remvoe(obj,funciton(obj1,obj2){ //使用匹配函数
* return obj1.id == obj2.id;
* });
*
* @param {Array|Object} data 添加的数据,可以是数组,可以是单条记录
* @param {Function} [match = function(obj1,obj2){return obj1 == obj2}] 匹配函数,可以为空
*/
remove :function(data,match){
var _self =this,
delData=[];
match = match || _self._getDefaultMatch();
if(!BUI.isArray(data)){
data = [data];
}
$.each(data,function(index,element){
var index = _self.findIndexBy(element,match),
record = removeAt(index,_self.getResult());
//添加到已删除队列中,如果是新添加的数据,不计入删除的数据集合中
if(!contains(record,_self.get('newRecords')) && !contains(record,_self.get('deletedRecords'))){
_self.get('deletedRecords').push(record);
}
removeFrom(record,_self.get('newRecords'));
removeFrom(record,_self.get('modifiedRecords'));
_self.fire('remove',{record:record});
});
},
/**
* 保存数据,有几种类型:
*
* - add 保存添加的记录,
* - remove 保存删除,
* - update 保存更新,
* - all 保存store从上次加载到目前更改的记录
*
*
* @param {String} type 保存的类型
* @param {Object} saveData 数据
* @param {Function} callback
*/
save : function(type,saveData,callback){
var _self = this,
proxy = _self.get('proxy');
if(BUI.isFunction(type)){ //只有回调函数
callback = type;
type = undefined;
}
if(BUI.isObject(type)){ //未指定类型
callback = saveData;
saveData = type;
type = undefined;
}
if(!type){
type = _self._getSaveType(saveData);
}
if(type == 'all' && !saveData){//如果保存全部,同时未提供保存的数据,自动获取
saveData = _self._getDirtyData();
}
_self.fire('beforesave',{type : type,saveData : saveData});
proxy.save(type,saveData,function(data){
_self.onSave(type,saveData,data);
if(callback){
callback(data,saveData);
}
},_self);
},
//根据保存的数据获取保存的类型
_getSaveType :function(saveData){
var _self = this;
if(!saveData){
return 'all';
}
if(BUI.Array.contains(saveData,_self.get('newRecords'))){
return 'add';
}
if(BUI.Array.contains(saveData,_self.get('modifiedRecords'))){
return 'update';
}
if(BUI.Array.contains(saveData,_self.get('deletedRecords'))){
return 'remove';
}
return 'custom';
},
//获取未保存的数据
_getDirtyData : function(){
var _self = this,
proxy = _self.get('proxy');
if(proxy.get('url')){
return {
add : BUI.JSON.stringify(_self.get('newRecords')),
update : BUI.JSON.stringify(_self.get('modifiedRecords')),
remove : BUI.JSON.stringify(_self.get('deletedRecords'))
};
}else{
return {
add : _self.get('newRecords'),
update : _self.get('modifiedRecords'),
remove : _self.get('deletedRecords')
};
}
},
/**
* 保存完成后
* @private
*/
onSave : function(type,saveData,data){
var _self = this,
hasErrorField = _self.get('hasErrorProperty');
if (BUI.getValue(data,hasErrorField) || data.exception){ //如果失败
_self.onException(data);
return;
}
_self._clearDirty(type,saveData);
_self.fire('saved',{type : type,saveData : saveData,data : data});
if(_self.get('autoSync')){
_self.load();
}
},
//清除脏数据
_clearDirty : function(type,saveData){
var _self = this;
switch(type){
case 'all' :
_self._clearChanges();
break;
case 'add' :
removeFrom(saveData,'newRecords');
break;
case 'update' :
removeFrom(saveData,'modifiedRecords');
break;
case 'remove' :
removeFrom(saveData,'deletedRecords');
break;
default :
break;
}
function removeFrom(obj,name){
BUI.Array.remove(_self.get(name),obj);
}
},
/**
* 排序,如果remoteSort = true,发送请求,后端排序
*
* store.sort('id','DESC'); //以id为排序字段,倒序排序
*
* @param {String} field 排序字段
* @param {String} direction 排序方向
*/
sort : function(field,direction){
var _self = this,
remoteSort = _self.get('remoteSort');
if(!remoteSort){
_self._localSort(field,direction);
}else{
_self.set('sortField',field);
_self.set('sortDirection',direction);
_self.load(_self.get('sortInfo'));
}
},
/**
* 计算指定字段的和
*
* var sum = store.sum('number');
*
* @param {String} field 字段名
* @param {Array} [data] 计算的集合,默认为Store中的数据集合
* @return {Number} 汇总和
*/
sum : function(field,data){
var _self = this,
records = data || _self.getResult(),
sum = 0;
BUI.each(records,function(record){
var val = record[field];
if(!isNaN(val)){
sum += parseFloat(val);
}
});
return sum;
},
/**
* 设置记录的值 ,触发 update 事件
*
* store.setValue(obj,'value','new value');
*
* @param {Object} obj 修改的记录
* @param {String} field 修改的字段名
* @param {Object} value 修改的值
*/
setValue : function(obj,field,value){
var record = obj,
_self = this;
record[field]=value;
if(!contains(record,_self.get('newRecords')) && !contains(record,_self.get('modifiedRecords'))){
_self.get('modifiedRecords').push(record);
}
_self.fire('update',{record:record,field:field,value:value});
},
/**
* 更新记录 ,触发 update事件
*
* var record = store.find('id','12');
* record.value = 'new value';
* record.text = 'new text';
* store.update(record); //触发update事件,引起绑定了store的控件更新
*
* @param {Object} obj 修改的记录
* @param {Boolean} [isMatch = false] 是否需要进行匹配,检测指定的记录是否在集合中
* @param {Function} [match = matchFunction] 匹配函数
*/
update : function(obj,isMatch,match){
var record = obj,
_self = this,
match = null,
index = null;
if(isMatch){
match = match || _self._getDefaultMatch();
index = _self.findIndexBy(obj,match);
if(index >=0){
record = _self.getResult()[index];
}
}
record = BUI.mix(record,obj);
if(!contains(record,_self.get('newRecords')) && !contains(record,_self.get('modifiedRecords'))){
_self.get('modifiedRecords').push(record);
}
_self.fire('update',{record:record});
},
//添加纪录
_addRecord :function(record,index){
var records = this.getResult();
if(index == undefined){
index = records.length;
}
records.splice(index,0,record);
this.fire('add',{record:record,index:index});
},
//清除改变的数据记录
_clearChanges : function(){
var _self = this;
BUI.Array.empty(_self.get('newRecords'));
BUI.Array.empty(_self.get('modifiedRecords'));
BUI.Array.empty(_self.get('deletedRecords'));
},
/**
* @protected
* 过滤缓存的数据
* @param {Function} fn 过滤函数
* @return {Array} 过滤结果
*/
_filterLocal : function(fn,data){
var _self = this,
rst = [];
data = data || _self.getResult();
if(!fn){ //没有过滤器时直接返回
return data;
}
BUI.each(data,function(record){
if(fn(record)){
rst.push(record);
}
});
return rst;
},
//获取默认的匹配函数
_getDefaultMatch :function(){
return this.get('matchFunction');
},
//获取分页相关的信息
_getPageParams : function(){
var _self = this,
sortInfo = _self.get('sortInfo'),
start = _self.get('start'),
limit = _self.get('pageSize'),
pageIndex = _self.get('pageIndex') || (limit ? start/limit : 0);
params = {
start : start,
limit : limit,
pageIndex : pageIndex //一般而言,pageIndex = start/limit
};
if(_self.get('remoteSort')){
BUI.mix(params,sortInfo);
}
return params;
},
/**
* 获取附加的参数,分页信息,排序信息
* @override
* @protected
* @return {Object} 附加的参数
*/
getAppendParams : function(){
return this._getPageParams();
},
/**
* @protected
* 初始化之前
*/
beforeInit : function(){
//初始化结果集
this._setResult([]);
},
//本地排序
_localSort : function(field,direction){
var _self = this;
_self._sortData(field,direction);
_self.fire('localsort',{field:field,direction:direction});
},
_sortData : function(field,direction,data){
var _self = this;
data = data || _self.getResult();
_self.sortData(field,direction,data);
},
//处理数据
afterProcessLoad : function(data,params){
var _self = this,
root = _self.get('root'),
start = params.start,
limit = params.limit,
totalProperty = _self.get('totalProperty');
if(BUI.isArray(data)){
_self._setResult(data);
}else{
_self._setResult(BUI.getValue(data,root), BUI.getValue(data,totalProperty));
}
_self.set('start',start);
if(limit){
_self.set('pageIndex',start/limit);
}
//如果本地排序,则排序
if(!_self.get('remoteSort')){
_self._sortData();
}
_self.fire('load',{ params : params });
//如果有本地过滤,则本地过滤
if(!_self.get('remoteFilter') && _self.get('filter')){
_self.filter(_self.get('filter'));
}
},
//设置结果集
_setResult : function(rows,totalCount){
var _self = this,
resultMap = _self.get('resultMap');
totalCount = totalCount || rows.length;
BUI.setValue(resultMap,_self.get('root'),rows);
BUI.setValue(resultMap,_self.get('totalProperty'),totalCount);
//清理之前发生的改变
_self._clearChanges();
}
});
module.exports = store;
});
define("bui/data/node", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview 树形数据结构的节点类,无法直接使用数据作为节点,所以进行一层封装
* 可以直接作为TreeNode控件的配置项
* @ignore
*/
var BUI = require("bui/common");
function mapNode(cfg,map){
var rst = {};
if(map){
BUI.each(cfg,function(v,k){
var name = map[k] || k;
rst[name] = v;
});
rst.record = cfg;
}else{
rst = cfg;
}
return rst;
}
/**
* @class BUI.Data.Node
* 树形数据结构的节点类
*/
function Node (cfg,map) {
var _self = this;
cfg = mapNode(cfg,map);
BUI.mix(this,cfg);
}
BUI.augment(Node,{
/**
* 是否根节点
* @type {Boolean}
*/
root : false,
/**
* 是否叶子节点
* @type {Boolean}
*/
leaf : null,
/**
* 显示节点时显示的文本
* @type {Object}
*/
text : '',
/**
* 代表节点的编号
* @type {String}
*/
id : null,
/**
* 子节点是否已经加载过
* @type {Boolean}
*/
loaded : false,
/**
* 从根节点到此节点的路径,id的集合如: ['0','1','12'],
* 便于快速定位节点
* @type {Array}
*/
path : null,
/**
* 父节点
* @type {BUI.Data.Node}
*/
parent : null,
/**
* 树节点的等级
* @type {Number}
*/
level : 0,
/**
* 节点是否由一条记录封装而成
* @type {Object}
*/
record : null,
/**
* 子节点集合
* @type {BUI.Data.Node[]}
*/
children : null,
/**
* 是否是Node对象
* @type {Object}
*/
isNode : true
});
module.exports = Node;
});
define("bui/data/treestore", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview 树形对象缓冲类
* @ignore
*/
var BUI = require("bui/common"),
Node = require("bui/data/node"),
Proxy = require("bui/data/proxy"),
AbstractStore = require("bui/data/abstractstore");
/**
* @class BUI.Data.TreeStore
* 树形数据缓冲类
*
*
*
* //加载静态数据
* var store = new TreeStore({
* root : {
* text : '根节点',
* id : 'root'
* },
* data : [{id : '1',text : 1},{id : '2',text : 2}] //会加载成root的children
* });
* //异步加载数据,自动加载数据时,会调用store.load({id : 'root'}); //root为根节点的id
* var store = new TreeStore({
* root : {
* text : '根节点',
* id : 'root'
* },
* url : 'data/nodes.php',
* autoLoad : true //设置自动加载,初始化后自动加载数据
* });
*
* //加载指定节点
* var node = store.findNode('1');
* store.loadNode(node);
* //或者
* store.load({id : '1'});//可以配置自定义参数,返回值附加到指定id的节点上
*
* @extends BUI.Data.AbstractStore
*/
function TreeStore(config){
TreeStore.superclass.constructor.call(this,config);
}
TreeStore.ATTRS = {
/**
* 根节点
*
* var store = new TreeStore({
* root : {text : '根节点',id : 'rootId',children : [{id : '1',text : '1'}]}
* });
*
* @cfg {Object} root
*/
/**
* 根节点,初始化后不要更改对象,可以更改属性值
*
* var root = store.get('root');
* root.text = '修改的文本';
* store.update(root);
*
* @type {Object}
* @readOnly
*/
root : {
},
/**
* 数据映射,用于设置的数据跟@see {BUI.Data.Node} 不一致时,进行匹配。
* 如果此属性为null,那么假设设置的对象是Node对象
*
* //例如原始数据为 {name : '123',value : '文本123',isLeaf: false,nodes : []}
* var store = new TreeStore({
* map : {
* 'name' : 'id',
* 'value' : 'text',
* 'isLeaf' : 'leaf' ,
* 'nodes' : 'children'
* }
* });
* //映射后,记录会变成 {id : '123',text : '文本123',leaf: false,children : []};
* //此时原始记录会作为对象的 record属性
* var node = store.findNode('123'),
* record = node.record;
*
* **Notes:**
* 使用数据映射的记录仅做于展示数据,不作为可更改的数据,add,update不会更改数据的原始数据
* @cfg {Object} map
*/
map : {
},
/**
* 标示父元素id的字段名称
* @type {String}
*/
pidField : {
},
/**
* 返回数据标示数据的字段
* //返回数据为数组 [{},{}],会直接附加到加载的节点后面
*
* var node = store.loadNode('123');
* store.loadNode(node);
*
*
* @cfg {Object} [dataProperty = 'nodes']
*/
dataProperty : {
value : 'nodes'
},
events : {
value : [
/**
* 当添加数据时触发该事件
* @event
*
* store.on('add',function(ev){
* list.addItem(e.node,index);
* });
*
* @param {jQuery.Event} e 事件对象
* @param {Object} e.node 添加的节点
* @param {Number} index 添加的位置
*/
'add',
/**
* 当更新数据指定字段时触发该事件
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.node 更新的节点
*/
'update',
/**
* 当删除数据时触发该事件
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.node 删除的节点
* @param {Number} index 删除节点的索引
*/
'remove',
/**
* 节点加载完毕触发该事件
*
* //异步加载节点,此时节点已经附加到加载节点的后面
* store.on('load',function(ev){
* var params = ev.params,
* id = params.id,
* node = store.findNode(id),
* children = node.children; //节点的id
* //TO DO
* });
*
*
* @event
* @param {jQuery.Event} e 事件对象
* @param {Object} e.node 加载的节点
* @param {Object} e.params 加载节点时的参数
*/
'load'
]
}
}
BUI.extend(TreeStore,AbstractStore);
BUI.augment(TreeStore,{
/**
* @protected
* @override
* 初始化前
*/
beforeInit:function(){
this.initRoot();
},
//初始化数据,如果默认加载数据,则加载数据
_initData : function(){
var _self = this,
autoLoad = _self.get('autoLoad'),
pidField = _self.get('pidField'),
proxy = _self.get('proxy'),
root = _self.get('root');
//添加默认的匹配父元素的字段
if(!proxy.get('url') && pidField){
proxy.get('matchFields').push(pidField);
}
if(autoLoad && !root.children){
//params = root.id ? {id : root.id}: {};
_self.loadNode(root);
}
},
/**
* @protected
* 初始化根节点
*/
initRoot : function(){
var _self = this,
map = _self.get('map'),
root = _self.get('root');
if(!root){
root = {};
}
if(!root.isNode){
root = new Node(root,map);
//root.children= [];
}
root.path = [root.id];
root.level = 0;
if(root.children){
_self.setChildren(root,root.children);
}
_self.set('root',root);
},
/**
* 添加节点,触发{@link BUI.Data.TreeStore#event-add} 事件
*
* //添加到根节点下
* store.add({id : '1',text : '1'});
* //添加到指定节点
* var node = store.findNode('1'),
* subNode = store.add({id : '11',text : '11'},node);
* //插入到节点的指定位置
* var node = store.findNode('1'),
* subNode = store.add({id : '12',text : '12'},node,0);
*
* @param {BUI.Data.Node|Object} node 节点或者数据对象
* @param {BUI.Data.Node} [parent] 父节点,如果未指定则为根节点
* @param {Number} [index] 添加节点的位置
* @return {BUI.Data.Node} 添加完成的节点
*/
add : function(node,parent,index){
var _self = this;
node = _self._add(node,parent,index);
_self.fire('add',{node : node,record : node,index : index});
return node;
},
//
_add : function(node,parent,index){
parent = parent || this.get('root'); //如果未指定父元素,添加到跟节点
var _self = this,
map = _self.get('map'),
nodes = parent.children,
nodeChildren;
if(!node.isNode){
node = new Node(node,map);
}
nodeChildren = node.children || []
if(nodeChildren.length == 0 && node.leaf == null){
node.leaf = true;
}
if(parent){
parent.leaf = false;
}
node.parent = parent;
node.level = parent.level + 1;
node.path = parent.path.concat(node.id);
index = index == null ? parent.children.length : index;
BUI.Array.addAt(nodes,node,index);
_self.setChildren(node,nodeChildren);
return node;
},
/**
* 移除节点,触发{@link BUI.Data.TreeStore#event-remove} 事件
*
*
* var node = store.findNode('1'); //根据节点id 获取节点
* store.remove(node);
*
*
* @param {BUI.Data.Node} node 节点或者数据对象
* @return {BUI.Data.Node} 删除的节点
*/
remove : function(node){
var parent = node.parent || _self.get('root'),
index = BUI.Array.indexOf(node,parent.children) ;
BUI.Array.remove(parent.children,node);
if(parent.children.length === 0){
parent.leaf = true;
}
this.fire('remove',{node : node ,record : node , index : index});
node.parent = null;
return node;
},
/**
* 设置记录的值 ,触发 update 事件
*
* store.setValue(obj,'value','new value');
*
* @param {Object} obj 修改的记录
* @param {String} field 修改的字段名
* @param {Object} value 修改的值
*/
setValue : function(node,field,value){
var
_self = this;
node[field] = value;
_self.fire('update',{node:node,record : node,field:field,value:value});
},
/**
* 更新节点
*
* var node = store.findNode('1'); //根据节点id 获取节点
* node.text = 'modify text'; //修改文本
* store.update(node); //此时会触发update事件,绑定了store的控件会更新对应的DOM
*
* @return {BUI.Data.Node} 更新节点
*/
update : function(node){
this.fire('update',{node : node,record : node});
},
/**
* 返回缓存的数据,根节点的直接子节点集合
*
* //获取根节点的所有子节点
* var data = store.getResult();
* //获取根节点
* var root = store.get('root');
*
* @return {Array} 根节点下面的数据
*/
getResult : function(){
return this.get('root').children;
},
/**
* 设置缓存的数据,设置为根节点的数据
*
* var data = [
* {id : '1',text : '文本1'},
* {id : '2',text : '文本2',children:[
* {id : '21',text : '文本21'}
* ]},
* {id : '3',text : '文本3'}
* ];
* store.setResult(data); //会对数据进行格式化,添加leaf等字段:
* //[{id : '1',text : '文本1',leaf : true},{id : '2',text : '文本2',leaf : false,children:[...]}....]
*
* @param {Array} data 缓存的数据
*/
setResult : function(data){
var _self = this,
proxy = _self.get('proxy'),
root = _self.get('root');
if(proxy instanceof Proxy.Memery){
_self.set('data',data);
_self.load({id : root.id});
}else{
_self.setChildren(root,data);
}
},
/**
* 设置子节点
* @protected
* @param {BUI.Data.Node} node 节点
* @param {Array} children 子节点
*/
setChildren : function(node,children){
var _self = this;
node.children = [];
if(!children.length){
return;
}
BUI.each(children,function(item){
_self._add(item,node);
});
},
/**
* 查找节点
*
* var node = store.findNode('1');//从根节点开始查找节点
*
* var subNode = store.findNode('123',node); //从指定节点开始查找
*
* @param {String} id 节点Id
* @param {BUI.Data.Node} [parent] 父节点
* @param {Boolean} [deep = true] 是否递归查找
* @return {BUI.Data.Node} 节点
*/
findNode : function(id,parent,deep){
return this.findNodeBy(function(node){
return node.id === id;
},parent,deep);
},
/**
* 根据匹配函数查找节点
* @param {Function} fn 匹配函数
* @param {BUI.Data.Node} [parent] 父节点
* @param {Boolean} [deep = true] 是否递归查找
* @return {BUI.Data.Node} 节点
*/
findNodeBy : function(fn,parent,deep){
var _self = this;
deep = deep == null ? true : deep;
if(!parent){
var root = _self.get('root');
if(fn(root)){
return root;
}
return _self.findNodeBy(fn,root);
}
var children = parent.children,
rst = null;
BUI.each(children,function(item){
if(fn(item)){
rst = item;
}else if(deep){
rst = _self.findNodeBy(fn,item);
}
if(rst){
return false;
}
});
return rst;
},
/**
* 查找节点,根据匹配函数查找
*
* var nodes = store.findNodesBy(function(node){
* if(node.status == '0'){
* return true;
* }
* return false;
* });
*
* @param {Function} func 匹配函数
* @param {BUI.Data.Node} [parent] 父元素,如果不存在,则从根节点查找
* @return {Array} 节点数组
*/
findNodesBy : function(func,parent){
var _self = this,
root,
rst = [];
if(!parent){
parent = _self.get('root');
}
BUI.each(parent.children,function(item){
if(func(item)){
rst.push(item);
}
rst = rst.concat(_self.findNodesBy(func,item));
});
return rst;
},
/**
* 根据path查找节点
* @return {BUI.Data.Node} 节点
* @ignore
*/
findNodeByPath : function(path){
if(!path){
return null;
}
var _self = this,
root = _self.get('root'),
pathArr = path.split(','),
node,
i,
tempId = pathArr[0];
if(!tempId){
return null;
}
if(root.id == tempId){
node = root;
}else{
node = _self.findNode(tempId,root,false);
}
if(!node){
return;
}
for(i = 1 ; i < pathArr.length ; i = i + 1){
var tempId = pathArr[i];
node = _self.findNode(tempId,node,false);
if(!node){
break;
}
}
return node;
},
/**
* 是否包含指定节点,如果未指定父节点,从根节点开始搜索
*
* store.contains(node); //是否存在节点
*
* store.contains(subNode,node); //节点是否存在指定子节点
*
* @param {BUI.Data.Node} node 节点
* @param {BUI.Data.Node} parent 父节点
* @return {Boolean} 是否包含指定节点
*/
contains : function(node,parent){
var _self = this,
findNode = _self.findNode(node.id,parent);
return !!findNode;
},
/**
* 加载完数据
* @protected
* @override
*/
afterProcessLoad : function(data,params){
var _self = this,
pidField = _self.get('pidField'),
id = params.id || params[pidField],
dataProperty = _self.get('dataProperty'),
node = _self.findNode(id) || _self.get('root');//如果找不到父元素,则放置在跟节点
if(BUI.isArray(data)){
_self.setChildren(node,data);
}else{
_self.setChildren(node, BUI.getValue(data, dataProperty));
}
node.loaded = true; //标识已经加载过
_self.fire('load',{node : node,params : params});
},
/**
* 是否包含数据
* @return {Boolean}
*/
hasData : function(){
//return true;
return this.get('root').children && this.get('root').children.length !== 0;
},
/**
* 是否已经加载过,叶子节点或者存在字节点的节点
* @param {BUI.Data.Node} node 节点
* @return {Boolean} 是否加载过
*/
isLoaded : function(node){
var root = this.get('root');
if(node == root && !root.children){
return false;
}
if(!this.get('url') && !this.get('pidField')){ //如果不从远程加载数据,默认已经加载
return true;
}
return node.loaded || node.leaf || !!(node.children && node.children.length);
},
/**
* 加载节点的子节点
* @param {BUI.Data.Node} node 节点
* @param {Boolean} forceLoad 是否强迫重新加载节点,如果设置成true,不判断是否加载过
*/
loadNode : function(node,forceLoad){
var _self = this,
pidField = _self.get('pidField'),
params;
//如果已经加载过,或者节点是叶子节点
if(!forceLoad && _self.isLoaded(node)){
return ;
}
params = {id : node.id};
if(pidField){
params[pidField] = node.id;
}
_self.load(params);
},
/**
* 重新加载节点
* @param {BUI.Data.Node} node node节点
*/
reloadNode : function(node){
var _self = this;
node = node || _self.get('root');
node.loaded = false;
//node.children = [];
_self.loadNode(node,true);
},
/**
* 加载节点,根据path
* @param {String} path 加载路径
* @ignore
*/
loadPath : function(path){
var _self = this,
arr = path.split(','),
id = arr[0];
if(_self.findNodeByPath(path)){ //加载过
return;
}
_self.load({id : id,path : path});
}
});
module.exports = TreeStore;
});
define("bui/list", ["bui/common","jquery","bui/data"], function(require, exports, module){
/**
* @fileOverview 列表模块入口文件
* @ignore
*/
var BUI = require("bui/common"),
List = BUI.namespace('List');
BUI.mix(List, {
List : require("bui/list/list"),
ListItem : require("bui/list/listitem"),
SimpleList : require("bui/list/simplelist"),
Listbox : require("bui/list/listbox")
});
BUI.mix(List, {
ListItemView : List.ListItem.View,
SimpleListView : List.SimpleList.View
});
module.exports = List;
});
define("bui/list/list", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 列表
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
UIBase = Component.UIBase;
/**
* 列表
*
*
*
*
*
*
* BUI.use('bui/list',function(List){
* var list = new List.SimpleList({
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
* list.render();
* });
*
*
*
* ** 自定义模板的列表 **
*
*
* BUI.use('bui/list',function(List){
* var list = new List.SimpleList({
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
* list.render();
* });
*
*
*
* @class BUI.List.SimpleList
* @extends BUI.Component.Controller
* @mixins BUI.List.DomList
* @mixins BUI.List.KeyNav
* @mixins BUI.Component.UIBase.Bindable
*/
var simpleList = BUI.Component.Controller.extend([DomList,UIBase.Bindable,KeyNav,Sortable],
{
/**
* @protected
* @ignore
*/
bindUI : function(){
var _self = this,
itemCls = _self.get('itemCls'),
itemContainer = _self.get('view').getItemContainer();
itemContainer.delegate('.'+itemCls,'mouseover',function(ev){
if(_self.get('disabled')){ //控件禁用后,阻止事件
return;
}
var element = ev.currentTarget,
item = _self.getItemByElement(element);
if(_self.isItemDisabled(ev.item,ev.currentTarget)){ //如果禁用
return;
}
if(!(UA.ie && UA.ie < 8) && _self.get('focusable') && _self.get('highlightedStatus') === 'hover'){
_self.setHighlighted(item,element)
}else{
_self.setItemStatus(item,'hover',true,element);
}
/*_self.get('view').setElementHover(element,true);*/
}).delegate('.'+itemCls,'mouseout',function(ev){
if(_self.get('disabled')){ //控件禁用后,阻止事件
return;
}
var sender = $(ev.currentTarget);
_self.get('view').setElementHover(sender,false);
});
},
/**
* 添加
* @protected
*/
onAdd : function(e){
var _self = this,
store = _self.get('store'),
item = e.record;
if(_self.getCount() == 0){ //初始为空时,列表跟Store不同步
_self.setItems(store.getResult());
}else{
_self.addItemToView(item,e.index);
}
},
handleContextMenu: function(ev) {
var _self = this,
target = ev.target,
itemCls = _self.get('itemCls'),
element = $(target).closest('.' + itemCls),
item = _self.getItemByElement(element);
var result = _self.fire('itemcontextmenu',{
element : element,
item : item,
pageX : ev.pageX,
pageY : ev.pageY,
domTarget : ev.target,
domEvent : ev
});
if(result === false){
ev.preventDefault();
}
},
/**
* 删除
* @protected
*/
onRemove : function(e){
var _self = this,
item = e.record;
_self.removeItem(item);
},
/**
* 更新
* @protected
*/
onUpdate : function(e){
this.updateItem(e.record);
},
/**
* 本地排序
* @protected
*/
onLocalSort : function(e){
if(this.get('frontSortable')){
this.sort(e.field ,e.direction);
}else{
this.onLoad(e);
}
},
/**
* 加载数据
* @protected
*/
onLoad:function(){
var _self = this,
store = _self.get('store'),
items = store.getResult();
_self.set('items',items);
},
/**
* 过滤数据
* @protected
*/
onFiltered: function(e){
var _self = this,
items = e.data;
_self.set('items', items);
}
},{
ATTRS :
{
/**
* 排序的时候是否直接进行DOM的排序,不重新生成DOM,
* var list = new List.SimpleList({
* render : '#t1',
* itemCls : 'my-item', //自定义样式名称
* items : [{id : '1',text : '1',type : '0'},{id : '2',text : '2',type : '1'}]
* });
* list.render();
*
* @cfg {Object} [itemCl='list-item']
*/
itemCls : {
view:true,
value : CLS_ITEM
},
/**
* 选项的默认id字段
*
* var list = new List.SimpleList({
* render : '#t1',
* idField : 'id', //自定义选项 id 字段
* items : [{id : '1',text : '1',type : '0'},{id : '2',text : '2',type : '1'}]
* });
* list.render();
*
* list.getItem('1'); //使用idField指定的字段进行查找
*
* @cfg {String} [idField = 'value']
*/
idField : {
value : 'value'
},
/**
* 列表的选择器,将列表项附加到此节点
* @protected
* @type {Object}
*/
listSelector:{
view:true,
value:'ul'
},
/**
* 列表项的默认模板。
*
* var list = new List.SimpleList({
* itemTpl : '<li id="{value}">{text}</li>', //列表项的模板
* idField : 'value',
* render : '#t1',
* items : [{value : '1',text : '1'},{value : '2',text : '2'}]
* });
* list.render();
*
* @cfg {String} [itemTpl ='<li role="option" class="bui-list-item" data-value="{value}">{text}</li>']
*/
itemTpl :{
view : true,
value : ' checked : true
,可以使得此记录对应的DOM上应用对应的状态(默认为 'list-item-checked')
* {id : '1',text : 1,checked : true}
* - 当更改DOM的状态时,记录中对应的字段属性也会跟着变化
*
* var list = new List.SimpleList({
* render : '#t1',
* idField : 'id', //自定义样式名称
* itemStatusFields : {
* checked : 'checked',
* disabled : 'disabled'
* },
* items : [{id : '1',text : '1',checked : true},{id : '2',text : '2',disabled : true}]
* });
* list.render(); //列表渲染后,会自动带有checked,和disabled对应的样式
*
* var item = list.getItem('1');
* list.hasStatus(item,'checked'); //true
*
* list.setItemStatus(item,'checked',false);
* list.hasStatus(item,'checked'); //false
* item.checked; //false
*
*
* ** 注意 **
* 此字段跟 {@link #itemStatusCls} 一起使用效果更好,可以自定义对应状态的样式
* @cfg {Object} itemStatusFields
*/
itemStatusFields : {
value : {}
},
/**
* 项的样式,用来获取子项
* @cfg {Object} itemCls
*/
itemCls : {
view : true
},
/**
* 是否允许取消选中,在多选情况下默认允许取消,单选情况下不允许取消,注意此属性只有单选情况下生效
* @type {Boolean}
*/
cancelSelected : {
value : false
},
/**
* 获取项的文本,默认获取显示的文本
* @type {Object}
* @protected
*/
textGetter : {
},
/**
* 默认的加载控件内容的配置,默认值:
* * { * property : 'items', * dataType : 'json' * } ** @type {Object} */ defaultLoaderCfg : { value : { property : 'items', dataType : 'json' } }, events : { value : { /** * 选项对应的DOM创建完毕 * @event * @param {Object} e 事件对象 * @param {Object} e.item 渲染DOM对应的选项 * @param {HTMLElement} e.element 渲染的DOM对象 */ 'itemrendered' : true, /** * @event * 删除选项 * @param {Object} e 事件对象 * @param {Object} e.item 删除DOM对应的选项 * @param {HTMLElement} e.element 删除的DOM对象 */ 'itemremoved' : true, /** * @event * 更新选项 * @param {Object} e 事件对象 * @param {Object} e.item 更新DOM对应的选项 * @param {HTMLElement} e.element 更新的DOM对象 */ 'itemupdated' : true, /** * 设置记录时,所有的记录显示完毕后触发 * @event */ 'itemsshow' : false, /** * 设置记录后,所有的记录显示前触发 * @event: */ 'beforeitemsshow' : false, /** * 清空所有记录,DOM清理完成后 * @event */ 'itemsclear' : false, /** * 双击是触发 * @event * @param {Object} e 事件对象 * @param {Object} e.item DOM对应的选项 * @param {HTMLElement} e.element 选项的DOM对象 * @param {HTMLElement} e.domTarget 点击的元素 */ 'itemdblclick' : false, /** * 清空所有Dom前触发 * @event */ 'beforeitemsclear' : false } } }); domList.PARSER = { items : function(el){ var _self = this, rst = [], itemCls = _self.get('itemCls'), dataField = _self.get('dataField'), elements = el.find('.' + itemCls); if(!elements.length){ elements = el.children(); elements.addClass(itemCls); } BUI.each(elements,function(element){ var item = parseItem(element,_self); rst.push(item); $(element).data(dataField,item); }); //_self.setInternal('items',rst); return rst; } }; BUI.augment(domList,List,Selection,{ //设置记录 _uiSetItems : function (items) { var _self = this; //使用srcNode 的方式,不同步 if(_self.get('srcNode') && !_self.get('rendered')){ return; } this.setItems(items); }, __bindUI : function(){ var _self = this, selectedEvent = _self.get('selectedEvent'), itemCls = _self.get('itemCls'), itemContainer = _self.get('view').getItemContainer(); itemContainer.delegate('.'+itemCls,'click',function(ev){ if(_self.get('disabled')){ //控件禁用后,阻止事件 return; } var itemEl = $(ev.currentTarget), item = _self.getItemByElement(itemEl); if(_self.isItemDisabled(item,itemEl)){ //禁用状态下阻止选中 return; } var rst = _self.fire('itemclick',{item:item,element : itemEl[0],domTarget:ev.target,domEvent : ev}); if(rst !== false && selectedEvent == 'click' && _self.isItemSelectable(item)){ setItemSelectedStatus(item,itemEl); } }); if(selectedEvent !== 'click'){ //如果选中事件不等于click,则进行监听选中 itemContainer.delegate('.'+itemCls,selectedEvent,function(ev){ if(_self.get('disabled')){ //控件禁用后,阻止事件 return; } var itemEl = $(ev.currentTarget), item = _self.getItemByElement(itemEl); if(_self.isItemDisabled(item,itemEl)){ //禁用状态下阻止选中 return; } if(_self.isItemSelectable(item)){ setItemSelectedStatus(item,itemEl); } }); } itemContainer.delegate('.' + itemCls,'dblclick',function(ev){ if(_self.get('disabled')){ //控件禁用后,阻止事件 return; } var itemEl = $(ev.currentTarget), item = _self.getItemByElement(itemEl); if(_self.isItemDisabled(item,itemEl)){ //禁用状态下阻止选中 return; } _self.fire('itemdblclick',{item:item,element : itemEl[0],domTarget:ev.target}); }); function setItemSelectedStatus(item,itemEl){ var multipleSelect = _self.get('multipleSelect'), isSelected; isSelected = _self.isItemSelected(item,itemEl); if(!isSelected){ if(!multipleSelect){ _self.clearSelected(); } _self.setItemSelected(item,true,itemEl); }else if(multipleSelect){ _self.setItemSelected(item,false,itemEl); }else if(_self.get('cancelSelected')){ _self.setSelected(null); //选中空记录 } } _self.on('itemrendered itemupdated',function(ev){ var item = ev.item, element = ev.element; _self._syncItemStatus(item,element); }); }, //获取值,通过字段 getValueByField : function(item,field){ return item && item[field]; }, //同步选项状态 _syncItemStatus : function(item,element){ var _self = this, itemStatusFields = _self.get('itemStatusFields'); BUI.each(itemStatusFields,function(v,k){ if(item[v] != null){ _self.get('view').setItemStatusCls(k,element,item[v]); } }); }, /** * @protected * 获取记录中的状态值,未定义则为undefined * @param {Object} item 记录 * @param {String} status 状态名 * @return {Boolean|undefined} */ getStatusValue : function(item,status){ var _self = this, itemStatusFields = _self.get('itemStatusFields'), field = itemStatusFields[status]; return item[field]; }, /** * 获取选项数量 * @return {Number} 选项数量 */ getCount : function(){ var items = this.getItems(); return items ? items.length : 0; }, /** * 更改状态值对应的字段 * @protected * @param {String} status 状态名 * @return {String} 状态对应的字段 */ getStatusField : function(status){ var _self = this, itemStatusFields = _self.get('itemStatusFields'); return itemStatusFields[status]; }, /** * 设置记录状态值 * @protected * @param {Object} item 记录 * @param {String} status 状态名 * @param {Boolean} value 状态值 */ setStatusValue : function(item,status,value){ var _self = this, itemStatusFields = _self.get('itemStatusFields'), field = itemStatusFields[status]; if(field){ item[field] = value; } }, /** * @ignore * 获取选项文本 */ getItemText : function(item){ var _self = this, textGetter = _self.get('textGetter'); if(!item) { return ''; } if(textGetter){ return textGetter(item); }else{ return $(_self.findElement(item)).text(); } }, /** * 删除项 * @param {Object} item 选项记录 * @ignore */ removeItem : function (item) { var _self = this, items = _self.get('items'), element = _self.findElement(item), index; index = BUI.Array.indexOf(item,items); if(index !== -1){ items.splice(index, 1); } _self.get('view').removeItem(item,element); _self.fire('itemremoved',{item:item,domTarget: $(element)[0],element : element}); }, /** * 在指定位置添加选项,选项值为一个对象 * @param {Object} item 选项 * @param {Number} index 索引 * @ignore */ addItemAt : function(item,index) { var _self = this, items = _self.get('items'); if(index === undefined) { index = items.length; } items.splice(index, 0, item); _self.addItemToView(item,index); return item; }, /** * @protected * 直接在View上显示 * @param {Object} item 选项 * @param {Number} index 索引 * */ addItemToView : function(item,index){ var _self = this, element = _self.get('view').addItem(item,index); _self.fire('itemrendered',{item:item,domTarget : $(element)[0],element : element}); return element; }, /** * 更新列表项 * @param {Object} item 选项值 * @ignore */ updateItem : function(item){ var _self = this, element = _self.get('view').updateItem(item); _self.fire('itemupdated',{item : item,domTarget : $(element)[0],element : element}); }, /** * 设置列表记录 *
* list.setItems(items);
* //等同
* list.set('items',items);
*
* @param {Array} items 列表记录
*/
setItems : function(items){
var _self = this;
if(items != _self.getItems()){
_self.setInternal('items',items);
}
//清理子控件
_self.clearControl();
_self.fire('beforeitemsshow');
BUI.each(items,function(item,index){
_self.addItemToView(item,index);
});
_self.fire('itemsshow');
},
/**
* 获取所有选项
* @return {Array} 选项集合
* @override
* @ignore
*/
getItems : function () {
return this.get('items');
},
/**
* 获取DOM结构中的数据
* @protected
* @param {HTMLElement} element DOM 结构
* @return {Object} 该项对应的值
*/
getItemByElement : function(element){
return this.get('view').getItemByElement(element);
},
/**
* 获取选中的第一项,
*
* var item = list.getSelected(); //多选模式下第一条
*
* @return {Object} 选中的第一项或者为null
*/
getSelected : function(){ //this.getSelection()[0] 的方式效率太低
var _self = this,
element = _self.get('view').getFirstElementByStatus('selected');
return _self.getItemByElement(element) || null;
},
/**
* 根据状态获取选项
*
* //设置状态
* list.setItemStatus(item,'active');
*
* //获取'active'状态的选项
* list.getItemsByStatus('active');
*
* @param {String} status 状态名
* @return {Array} 选项组集合
*/
getItemsByStatus : function(status){
var _self = this,
elements = _self.get('view').getElementsByStatus(status),
rst = [];
BUI.each(elements,function(element){
rst.push(_self.getItemByElement(element));
});
return rst;
},
/**
* 查找指定的项的DOM结构
*
* var item = list.getItem('2'); //获取选项
* var element = list.findElement(item);
* $(element).addClass('xxx');
*
* @param {Object} item
* @return {HTMLElement} element
*/
findElement : function(item){
var _self = this;
if(BUI.isString(item)){
item = _self.getItem(item);
}
return this.get('view').findElement(item);
},
findItemByField : function(field,value){
var _self = this,
items = _self.get('items'),
result = null;
BUI.each(items,function(item){
if(item[field] != null && item[field] == value){//会出现false == '','0' == false的情况
result = item;
return false;
}
});
return result;
},
/**
* @override
* @ignore
*/
setItemSelectedStatus : function(item,selected,element){
var _self = this;
element = element || _self.findElement(item);
//_self.get('view').setItemSelected(item,selected,element);
_self.setItemStatus(item,'selected',selected,element);
//_self.afterSelected(item,selected,element);
},
/**
* 设置所有选项选中
* @ignore
*/
setAllSelection : function(){
var _self = this,
items = _self.getItems();
_self.setSelection(items);
},
/**
* 选项是否被选中
*
* var item = list.getItem('2');
* if(list.isItemSelected(item)){
* //do something
* }
*
* @override
* @param {Object} item 选项
* @return {Boolean} 是否选中
*/
isItemSelected : function(item,element){
var _self = this;
element = element || _self.findElement(item);
return _self.get('view').isElementSelected(element);
},
/**
* 是否选项被禁用
*
* var item = list.getItem('2');
* if(list.isItemDisabled(item)){ //如果选项禁用
* //do something
* }
*
* @param {Object} item 选项
* @return {Boolean} 选项是否禁用
*/
isItemDisabled : function(item,element){
return this.hasStatus(item,'disabled',element);
},
/**
* 设置选项禁用
*
* var item = list.getItem('2');
* list.setItemDisabled(item,true);//设置选项禁用,会在DOM上添加 itemCls + 'disabled'的样式
* list.setItemDisabled(item,false); //取消禁用,可以用{@link #itemStatusCls} 来替换样式
*
* @param {Object} item 选项
*/
setItemDisabled : function(item,disabled){
var _self = this;
/*if(disabled){
//清除选择
_self.setItemSelected(item,false);
}*/
_self.setItemStatus(item,'disabled',disabled);
},
/**
* 获取选中的项的值
* @override
* @return {Array}
* @ignore
*/
getSelection : function(){
var _self = this,
elements = _self.get('view').getSelectedElements(),
rst = [];
BUI.each(elements,function(elem){
rst.push(_self.getItemByElement(elem));
});
return rst;
},
/**
* @protected
* @override
* 清除者列表项的DOM
*/
clearControl : function(){
this.fire('beforeitemsclear');
this.get('view').clearControl();
this.fire('itemsclear');
},
/**
* 选项是否存在某种状态
*
* var item = list.getItem('2');
* list.setItemStatus(item,'active',true);
* list.hasStatus(item,'active'); //true
*
* list.setItemStatus(item,'active',false);
* list.hasStatus(item,'false'); //true
*
* @param {*} item 选项
* @param {String} status 状态名称,如selected,hover,open等等
* @param {HTMLElement} [element] 选项对应的Dom,放置反复查找
* @return {Boolean} 是否具有某种状态
*/
hasStatus : function(item,status,element){
if(!item){
return false;
}
var _self = this,
field = _self.getStatusField(status);
/*if(field){
return _self.getStatusValue(item,status);
}*/
element = element || _self.findElement(item);
return _self.get('view').hasStatus(status,element);
},
/**
* 设置选项状态,可以设置任何自定义状态
*
* var item = list.getItem('2');
* list.setItemStatus(item,'active',true);
* list.hasStatus(item,'active'); //true
*
* list.setItemStatus(item,'active',false);
* list.hasStatus(item,'false'); //true
*
* @param {*} item 选项
* @param {String} status 状态名称
* @param {Boolean} value 状态值,true,false
* @param {HTMLElement} [element] 选项对应的Dom,放置反复查找
*/
setItemStatus : function(item,status,value,element){
var _self = this;
if(item){
element = element || _self.findElement(item);
}
if(!_self.isItemDisabled(item,element) || status === 'disabled'){ //禁用后,阻止添加任何状态变化
if(item){
if(status === 'disabled' && value){ //禁用,同时清理其他状态
_self.clearItemStatus(item);
}
_self.setStatusValue(item,status,value);
_self.get('view').setItemStatusCls(status,element,value);
_self.fire('itemstatuschange',{item : item,status : status,value : value,element : element});
}
if(status === 'selected'){ //处理选中
_self.afterSelected(item,value,element);
}
}
},
/**
* 清除所有选项状态,如果指定清除的状态名,则清除指定的,否则清除所有状态
* @param {Object} item 选项
*/
clearItemStatus : function(item,status,element){
var _self = this,
itemStatusFields = _self.get('itemStatusFields');
element = element || _self.findElement(item);
if(status){
_self.setItemStatus(item,status,false,element);
}else{
BUI.each(itemStatusFields,function(v,k){
_self.setItemStatus(item,k,false,element);
});
if(!itemStatusFields['selected']){
_self.setItemSelected(item,false);
}
//移除hover状态
_self.setItemStatus(item,'hover',false);
}
}
});
domList.View = domListView;
module.exports = domList;
});
define("bui/list/keynav", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 列表选项,使用键盘导航
* @author dxq613@gmail.com
* @ignore
*/
'use strict';
/**
* @class BUI.List.KeyNav
* 列表导航扩展类
*/
var $ = require("jquery"),
BUI = require("bui/common"),
KeyNav = function(){};
KeyNav.ATTRS = {
/**
* 选项高亮使用的状态,有些场景下,使用selected更合适
* @cfg {String} [highlightedStatus='hover']
*/
highlightedStatus : {
value : 'hover'
}
};
BUI.augment(KeyNav,{
/**
* 设置选项高亮,默认使用 'hover' 状态
* @param {Object} item 选项
* @param {Boolean} value 状态值,true,false
* @protected
*/
setHighlighted : function(item,element){
if(this.hasStatus(item,'hover',element)){
return;
}
var _self = this,
highlightedStatus = _self.get('highlightedStatus'),
lightedElement = _self._getHighLightedElement(),
lightedItem = lightedElement ? _self.getItemByElement(lightedElement) : null;
if(lightedItem !== item){
if(lightedItem){
this.setItemStatus(lightedItem,highlightedStatus,false,lightedElement);
}
this.setItemStatus(item,highlightedStatus,true,element);
_self._scrollToItem(item,element);
}
},
_getHighLightedElement : function(){
var _self = this,
highlightedStatus = _self.get('highlightedStatus'),
element = _self.get('view').getFirstElementByStatus(highlightedStatus);
return element;
},
/**
* 获取高亮的选项
* @return {Object} item
* @protected
*/
getHighlighted : function(){
var _self = this,
highlightedStatus = _self.get('highlightedStatus'),
element = _self.get('view').getFirstElementByStatus(highlightedStatus);
return _self.getItemByElement(element) || null;
},
/**
* 获取列数
* @return {Number} 选项的列数,默认为1列
* @protected
*/
getColumnCount : function(){
var _self = this,
firstItem = _self.getFirstItem(),
element = _self.findElement(firstItem),
node = $(element);
if(element){
return parseInt(node.parent().width() / node.outerWidth(),10);
}
return 1;
},
/**
* 获取选项的行数 ,总数/列数 = list.getCount / column
* @protected
* @return {Number} 选项行数
*/
getRowCount : function(columns){
var _self = this;
columns = columns || _self.getColumnCount();
return (this.getCount() + columns - 1) / columns;
},
_getNextItem : function(forward,skip,count){
var _self = this,
currentIndx = _self._getCurrentIndex(),//默认第一行
itemCount = _self.getCount(),
factor = forward ? 1 : -1,
nextIndex;
if(currentIndx === -1){
return forward ? _self.getFirstItem() : _self.getLastItem();
}
if(!forward){
skip = skip * factor;
}
nextIndex = (currentIndx + skip + count) % count;
if(nextIndex > itemCount - 1){ //如果位置超出索引位置
if(forward){
nextIndex = nextIndex - (itemCount - 1);
}else{
nextIndex = nextIndex + skip;
}
}
return _self.getItemAt(nextIndex);
},
//获取左边一项
_getLeftItem : function(){
var _self = this,
count = _self.getCount(),
column = _self.getColumnCount();
if(!count || column <= 1){ //单列时,或者为0时
return null;
}
return _self._getNextItem(false,1,count);
},
//获取当前项
_getCurrentItem : function(){
return this.getHighlighted();
},
//获取当前项
_getCurrentIndex : function(){
var _self = this,
item = _self._getCurrentItem();
return this.indexOfItem(item);
},
//获取右边一项
_getRightItem : function(){
var _self = this,
count = _self.getCount(),
column = _self.getColumnCount();
if(!count || column <= 1){ //单列时,或者为0时
return null;
}
return this._getNextItem(true,1,count);
},
//获取下面一项
_getDownItem : function(){
var _self = this,
columns = _self.getColumnCount(),
rows = _self.getRowCount(columns);
if(rows <= 1){ //单行或者为0时
return null;
}
return this._getNextItem(true,columns,columns * rows);
},
getScrollContainer : function(){
return this.get('el');
},
/**
* @protected
* 只处理上下滚动,不处理左右滚动
* @return {Boolean} 是否可以上下滚动
*/
isScrollVertical : function(){
var _self = this,
el = _self.get('el'),
container = _self.get('view').getItemContainer();
return el.height() < container.height();
},
_scrollToItem : function(item,element){
var _self = this;
if(_self.isScrollVertical()){
element = element || _self.findElement(item);
var container = _self.getScrollContainer(),
top = $(element).position().top,
ctop = container.position().top,
cHeight = container.height(),
distance = top - ctop,
height = $(element).height(),
scrollTop = container.scrollTop();
if(distance < 0 || distance > cHeight - height){
container.scrollTop(scrollTop + distance);
}
}
},
//获取上面一项
_getUpperItem : function(){
var _self = this,
columns = _self.getColumnCount(),
rows = _self.getRowCount(columns);
if(rows <= 1){ //单行或者为0时
return null;
}
return this._getNextItem(false,columns,columns * rows);
},
/**
* 处理向上导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavUp : function (ev) {
var _self = this,
upperItem = _self._getUpperItem();
_self.setHighlighted(upperItem);
},
/**
* 处理向下导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavDown : function (ev) {
this.setHighlighted(this._getDownItem());
},
/**
* 处理向左导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavLeft : function (ev) {
this.setHighlighted(this._getLeftItem());
},
/**
* 处理向右导航
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavRight : function (ev) {
this.setHighlighted(this._getRightItem());
},
/**
* 处理确认键
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavEnter : function (ev) {
var _self = this,
current = _self._getCurrentItem(),
element;
if(current){
element = _self.findElement(current);
//_self.setSelected(current);
$(element).trigger('click');
}
},
/**
* 处理 esc 键
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavEsc : function (ev) {
this.setHighlighted(null); //移除
},
/**
* 处理Tab键
* @param {jQuery.Event} ev 事件对象
*/
handleNavTab : function(ev){
this.setHighlighted(this._getRightItem());
}
});
module.exports = KeyNav;
});
define("bui/list/sortable", ["jquery","bui/common","bui/data"], function(require, exports, module){
/**
* @fileOverview 列表排序
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
DataSortable = require("bui/data").Sortable;
/**
* @class BUI.List.Sortable
* 列表排序的扩展
* @extends BUI.Data.Sortable
*/
var Sortable = function(){
};
Sortable.ATTRS = BUI.merge(true,DataSortable.ATTRS, {
});
BUI.augment(Sortable,DataSortable,{
/**
* @protected
* @override
* @ignore
* 覆写比较方法
*/
compare : function(obj1,obj2,field,direction){
var _self = this,
dir;
field = field || _self.get('sortField');
direction = direction || _self.get('sortDirection');
//如果未指定排序字段,或方向,则按照默认顺序
if(!field || !direction){
return 1;
}
dir = direction === 'ASC' ? 1 : -1;
if(!$.isPlainObject(obj1)){
obj1 = _self.getItemByElement(obj1);
}
if(!$.isPlainObject(obj2)){
obj2 = _self.getItemByElement(obj2);
}
return _self.get('compareFunction')(obj1[field],obj2[field]) * dir;
},
/**
* 获取排序的集合
* @protected
* @return {Array} 排序集合
*/
getSortData : function(){
return $.makeArray(this.get('view').getAllElements());
},
/**
* 列表排序
* @param {string} field 字段名
* @param {string} direction 排序方向 ASC,DESC
*/
sort : function(field,direction){
var _self = this,
sortedElements = _self.sortData(field,direction),
itemContainer = _self.get('view').getItemContainer();
if(!_self.get('store')){
_self.sortData(field,direction,_self.get('items'));
}
BUI.each(sortedElements,function(el){
$(el).appendTo(itemContainer);
});
}
});
module.exports = Sortable;
});
define("bui/list/listbox", ["jquery","bui/common","bui/data"], function(require, exports, module){
/**
* @fileOverview 可选择的列表
* @author dengbin
* @ignore
*/
var $ = require("jquery"),
SimpleList = require("bui/list/simplelist");
/**
* 列表选择框
* @extends BUI.List.SimpleList
* @class BUI.List.Listbox
*/
var listbox = SimpleList.extend({
bindUI : function(){
var _self = this;
_self.on('selectedchange',function(e){
var item = e.item,
sender = $(e.domTarget),
checkbox =sender.find('input');
if(item){
checkbox.attr('checked',e.selected);
}
});
}
},{
ATTRS : {
/**
* 选项模板
* @override
* @type {String}
*/
itemTpl : {
value : '
* BUI.use('bui/tab',function(Tab){
*
* var tab = new Tab.Tab({
* render : '#tab',
* elCls : 'nav-tabs',
* autoRender: true,
* children:[
* {text:'标签一',value:'1'},
* {text:'标签二',value:'2'},
* {text:'标签三',value:'3'}
* ]
* });
* tab.on('selectedchange',function (ev) {
* var item = ev.item;
* $('#log').text(item.get('text') + ' ' + item.get('value'));
* });
* tab.setSelected(tab.getItemAt(0)); //设置选中第一个
*
* });
*
* @class BUI.Tab.Tab
* @extends BUI.Component.Controller
* @mixins BUI.Component.UIBase.ChildList
*/
var tab = Component.Controller.extend([UIBase.ChildList],{
},{
ATTRS : {
elTagName:{
view:true,
value:'ul'
},
/**
* 子类的默认类名,即类的 xclass
* @type {String}
* @override
* @default 'tab-item'
*/
defaultChildClass : {
value : 'tab-item'
}
}
},{
xclass : 'tab'
});
module.exports = tab;
});
define("bui/tab/tabitem", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview
* @ignore
*/
var BUI = require("bui/common"),
Component = BUI.Component,
UIBase = Component.UIBase;
/**
* @private
* @class BUI.Tab.TabItemView
* @extends BUI.Component.View
* @mixins BUI.Component.UIBase.ListItemView
* 标签项的视图层对象
*/
var itemView = Component.View.extend([UIBase.ListItemView],{
},{
xclass:'tab-item-view'
});
/**
* 标签项
* @class BUI.Tab.TabItem
* @extends BUI.Component.Controller
* @mixins BUI.Component.UIBase.ListItem
*/
var item = Component.Controller.extend([UIBase.ListItem],{
},{
ATTRS :
{
elTagName:{
view:true,
value:'li'
},
xview:{
value:itemView
},
tpl:{
view:true,
value:'{text}'
}
}
},{
xclass:'tab-item'
});
item.View = itemView;
module.exports = item;
});
define("bui/tab/navtabitem", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 导航项
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
CLS_ITEM_TITLE = 'tab-item-title',
CLS_ITEM_CLOSE = 'tab-item-close',
CLS_ITEM_INNER = 'tab-item-inner',
CLS_NAV_ACTIVED = 'tab-nav-actived',
CLS_CONTENT = 'tab-content';
/**
* 导航标签项的视图类
* @class BUI.Tab.NavTabItemView
* @extends BUI.Component.View
* @private
*/
var navTabItemView = Component.View.extend({
renderUI : function(){
var _self = this,
contentContainer = _self.get('tabContentContainer'),
contentTpl = _self.get('tabContentTpl');
if(contentContainer){
var tabContentEl = $(contentTpl).appendTo(contentContainer);
_self.set('tabContentEl',tabContentEl);
}
},
//设置链接地址
_uiSetHref : function(v){
this._setHref(v);
},
_setHref : function(href){
var _self = this,
tabContentEl = _self.get('tabContentEl');
href = href || _self.get('href');
if(tabContentEl){
$('iframe',tabContentEl).attr('src',href);
}
},
resetHref : function(){
this._setHref();
},
//设置标题
_uiSetTitle : function(v){
var _self = this,
el = _self.get('el');
//el.attr('title',v);
$('.' + CLS_ITEM_TITLE,el).html(v);
},
_uiSetActived : function(v){
var _self = this,
el = _self.get('el');
_self.setTabContentVisible(v);
if(v){
el.addClass(CLS_NAV_ACTIVED);
}else{
el.removeClass(CLS_NAV_ACTIVED);
}
},
//析构函数
destructor : function(){
var _self = this,
tabContentEl = _self.get('tabContentEl');
if(tabContentEl){
tabContentEl.remove();
}
},
//设置标签内容是否可见
setTabContentVisible : function(v){
var _self = this,
tabContentEl = _self.get('tabContentEl');
if(tabContentEl){
if(v){
tabContentEl.show();
}else{
tabContentEl.hide();
}
}
}
},{
ATTRS : {
tabContentContainer:{
},
tabContentEl: {
},
title:{
},
href:{
}
}
});
/**
* 导航标签项
* xclass : 'nav-tab-item'
* @class BUI.Tab.NavTabItem
* @extends BUI.Component.Controller
*/
var navTabItem = Component.Controller.extend(
{
/**
* 创建DOM
* @protected
*/
createDom : function(){
var _self = this,
parent = _self.get('parent');
if(parent){
_self.set('tabContentContainer',parent.getTabContentContainer());
}
},
/**
* 绑定事件
* @protected
*/
bindUI : function(){
var _self = this,
el = _self.get('el'),
events = _self.get('events');
el.on('click',function(ev){
var sender = $(ev.target);
if(sender.hasClass(CLS_ITEM_CLOSE)){
if(_self.fire('closing')!== false){
_self.close();
}
}
});
},
/**
* 处理双击
* @protected
*/
handleDblClick:function(ev){
var _self = this;
if(_self.get('closeable') && _self.fire('closing')!== false){
_self.close();
}
_self.fire('dblclick',{domTarget : ev.target,domEvent : ev});
},
/**
* 处理右键
* @protected
*/
handleContextMenu:function(ev){
ev.preventDefault();
this.fire('showmenu',{position:{x:ev.pageX,y:ev.pageY}});
},
/**
* 设置标题
* @param {String} title 标题
*/
setTitle : function(title){
this.set('title',title);
},
/**
* 关闭
*/
close:function(){
this.fire('closed');
},
/**
* 重新加载页面
*/
reload : function(){
this.get('view').resetHref();
},
/**
* @protected
* @ignore
*/
show : function(){
var _self = this;
_self.get('el').show(500,function(){
_self.set('visible',true);
});
},
/**
* @protected
* @ignore
*/
hide : function(callback){
var _self = this;
this.get('el').hide(500,function(){
_self.set('visible',false);
callback && callback();
});
},
_uiSetActived : function(v){
var _self = this,
parent = _self.get('parent');
if(parent && v){
parent._setItemActived(_self);
}
},
_uiSetCloseable : function(v){
var _self = this,
el = _self.get('el'),
closeEl = el.find('.' + CLS_ITEM_CLOSE);
if(v){
closeEl.show();
}else{
closeEl.hide();
}
}
},{
ATTRS :
{
elTagName : {
value: 'li'
},
/**
* 标签是否选中
* @type {Boolean}
*/
actived : {
view:true,
value : false
},
/**
* 是否可关闭
* @type {Boolean}
*/
closeable : {
value : true
},
allowTextSelection:{
view:false,
value:false
},
events:{
value : {
/**
* 点击菜单项
* @name BUI.Tab.NavTabItem#click
* @event
* @param {Object} e 事件对象
* @param {BUI.Tab.NavTabItem} e.target 正在点击的标签
*/
'click' : true,
/**
* 正在关闭,返回false可以阻止关闭事件发生
* @name BUI.Tab.NavTabItem#closing
* @event
* @param {Object} e 事件对象
* @param {BUI.Tab.NavTabItem} e.target 正在关闭的标签
*/
'closing' : true,
/**
* 关闭事件
* @name BUI.Tab.NavTabItem#closed
* @event
* @param {Object} e 事件对象
* @param {BUI.Tab.NavTabItem} e.target 关闭的标签
*/
'closed' : true,
/**
* @name BUI.Tab.NavTabItem#showmenu
* @event
* @param {Object} e 事件对象
* @param {BUI.Tab.NavTabItem} e.target 显示菜单的标签
*/
'showmenu' : true,
'afterVisibleChange' : true
}
},
/**
* @private
* @type {Object}
*/
tabContentContainer:{
view : true
},
/**
* @private
* @type {Object}
*/
tabContentTpl : {
view : true,
value : ''
},
/**
* 标签页指定的URL
* @cfg {String} href
*/
/**
* 标签页指定的URL
* @type {String}
*/
href : {
view : true,
value:''
},
visible:{
view:true,
value:true
},
/**
* 标签文本
* @cfg {String} title
*/
/**
* 标签文本
* tab.getItem('id').set('title','new title');
* @type {String}
* @default ''
*/
title : {
view:true,
value : ''
},
tpl : {
view:true,
value :'
* BUI.use('bui/tab',function(Tab){
*
* var tab = new Tab.TabPanel({
* render : '#tab',
* elCls : 'nav-tabs',
* panelContainer : '#panel',
* autoRender: true,
* children:[
* {text:'源代码',value:'1'},
* {text:'HTML',value:'2'},
* {text:'JS',value:'3'}
* ]
* });
* tab.setSelected(tab.getItemAt(0));
* });
*
* @class BUI.Tab.TabPanel
* @extends BUI.Tab.Tab
* @mixins BUI.Tab.Panels
*/
var tabPanel = Tab.extend([Panels],{
bindUI : function(){
var _self = this;
//关闭标签
_self.on('beforeclosed',function(ev){
var item = ev.target;
_self._beforeClosedItem(item);
});
},
//关闭标签选项前
_beforeClosedItem : function(item){
if(!item.get('selected')){ //如果未选中不执行下面的选中操作
return;
}
var _self = this,
index = _self.indexOfItem(item),
count = _self.getItemCount(),
preItem,
nextItem;
if(index !== count - 1){ //不是最后一个,则激活最后一个
nextItem = _self.getItemAt(index + 1);
_self.setSelected(nextItem);
}else if(index !== 0){
preItem = _self.getItemAt(index - 1);
_self.setSelected(preItem);
}
}
},{
ATTRS : {
elTagName : {
value : 'div'
},
childContainer : {
value : 'ul'
},
tpl : {
value : '
* tab.getItem('id').set('title','new title');
*
*/
title : {
view : true,
sync : false
},
/**
* 标签项的模板,因为之前没有title属性,所以默认用text,所以也兼容text,但是在最好直接使用title,方便更改
* @type {String}
*/
tpl : {
value : '{text}{title}'
},
closeable : {
value : false
},
events : {
value : {
beforeclosed : true
}
},
xview:{
value:itemView
}
}
},{
xclass:'tab-panel-item'
});
item.View = itemView;
module.exports = item;
});
define("bui/tab/panelitem", ["jquery"], function(require, exports, module){
/**
* @fileOverview 拥有内容的标签项的扩展类,每个标签项都有一个分离的容器作为面板
* @ignore
*/
var $ = require("jquery");
/**
* @class BUI.Tab.PanelItem
* 包含面板的标签项的扩展
*/
var PanelItem = function(){
};
PanelItem.ATTRS = {
/**
* 标签项对应的面板容器,当标签选中时,面板显示
* @cfg {String|HTMLElement|jQuery} panel
* @internal 面板属性一般由 tabPanel设置而不应该由用户手工设置
*/
/**
* 标签项对应的面板容器,当标签选中时,面板显示
* @type {String|HTMLElement|jQuery}
* @readOnly
*/
panel : {
},
/**
* 面板的内容
* @type {String}
*/
panelContent : {
},
/**
* 关联面板显示隐藏的属性名
* @protected
* @type {string}
*/
panelVisibleStatus : {
value : 'selected'
},
/**
* 默认的加载控件内容的配置,默认值:
* * { * property : 'panelContent', * lazyLoad : { * event : 'active' * }, * loadMask : { * el : _self.get('panel') * } * } ** @type {Object} */ defaultLoaderCfg : { valueFn :function(){ var _self = this, eventName = _self._getVisibleEvent(); return { property : 'panelContent', autoLoad : false, lazyLoad : { event : eventName }, loadMask : { el : _self.get('panel') } } } }, /** * 面板是否跟随标签一起释放 * @type {Boolean} */ panelDestroyable : { value : true } } BUI.augment(PanelItem,{ __renderUI : function(){ this._resetPanelVisible(); }, __bindUI : function(){ var _self = this, eventName = _self._getVisibleEvent(); _self.on(eventName,function(ev){ _self._setPanelVisible(ev.newVal); }); }, _resetPanelVisible : function(){ var _self = this, status = _self.get('panelVisibleStatus'), visible = _self.get(status); _self._setPanelVisible(visible); }, //获取显示隐藏的事件 _getVisibleEvent : function(){ var _self = this, status = _self.get('panelVisibleStatus'); return 'after' + BUI.ucfirst(status) + 'Change';; }, /** * @private * 设置面板的可见 * @param {Boolean} visible 显示或者隐藏 */ _setPanelVisible : function(visible){ var _self = this, panel = _self.get('panel'), method = visible ? 'show' : 'hide'; if(panel){ $(panel)[method](); } }, __destructor : function(){ var _self = this, panel = _self.get('panel'); if(panel && _self.get('panelDestroyable')){ $(panel).remove(); } }, _setPanelContent : function(panel,content){ var panelEl = $(panel); $(panel).html(content); }, _uiSetPanelContent : function(v){ var _self = this, panel = _self.get('panel'); //$(panel).html(v); _self._setPanelContent(panel,v); }, //设置panel _uiSetPanel : function(v){ var _self = this, content = _self.get('panelContent'); if(content){ _self._setPanelContent(v,content); } _self._resetPanelVisible(); } }); module.exports = PanelItem; }); define("bui/mask", ["bui/common","jquery"], function(require, exports, module){ /** * @fileOverview Mask的入口文件 * @ignore */ var BUI = require("bui/common"), Mask = require("bui/mask/mask"); Mask.LoadMask = require("bui/mask/loadMask"); module.exports = Mask; }); define("bui/mask/mask", ["jquery","bui/common"], function(require, exports, module){ /** * @fileOverview Mask屏蔽层 * @author dxq613@gmail.com * @ignore */ var $ = require("jquery"), BUI = require("bui/common"), Mask = BUI.namespace('Mask'), UA = BUI.UA, CLS_MASK = BUI.prefix + 'ext-mask', CLS_MASK_MSG = CLS_MASK + '-msg'; BUI.mix(Mask, /** * 屏蔽层 *
* BUI.use('bui/mask',function(Mask){
* Mask.maskElement('#domId'); //屏蔽dom
* Mask.unmaskElement('#domId'); //解除DOM屏蔽
* });
*
* @class BUI.Mask
* @singleton
*/
{
/**
* @description 屏蔽指定元素
* @param {String|HTMLElement} element 被屏蔽的元素
* @param {String} [msg] 屏蔽元素时显示的文本
* @param {String} [msgCls] 显示文本应用的样式
*
* BUI.Mask.maskElement('#domId');
* BUI.Mask.maskElement('body'); //屏蔽整个窗口
*
*/
maskElement:function (element, msg, msgCls) {
var maskedEl = $(element),
maskDiv = maskedEl.children('.' + CLS_MASK),
tpl = null,
msgDiv = null,
top = null,
left = null;
if (!maskDiv.length) {
maskDiv = $('').appendTo(maskedEl);
maskedEl.addClass('x-masked-relative x-masked');
//屏蔽整个窗口
if(element == 'body'){
if(UA.ie == 6){
maskDiv.height(BUI.docHeight());
}else{
maskDiv.css('position','fixed');
}
}else{
if (UA.ie === 6) {
maskDiv.height(maskedEl.height());
}
}
if (msg) {
tpl = ['
* BUI.Mask.unmaskElement('#domId');
*
*/
unmaskElement:function (element) {
var maskedEl = $(element),
msgEl = maskedEl.children('.' + CLS_MASK_MSG),
maskDiv = maskedEl.children('.' + CLS_MASK);
if (msgEl) {
msgEl.remove();
}
if (maskDiv) {
maskDiv.remove();
}
maskedEl.removeClass('x-masked-relative x-masked');
}
});
module.exports = Mask;
});
define("bui/mask/loadMask", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 加载数据时屏蔽层
* @ignore
*/
var $ = require("jquery"),
Mask = require("bui/mask/mask");
/**
* 屏蔽指定元素,并显示加载信息
*
* BUI.use('bui/mask',function(Mask){
* var loadMask = new Mask.LoadMask({
* el : '#domId',
* msg : 'loading ....'
* });
*
* $('#btn').on('click',function(){
* loadMask.show();
* });
*
* $('#btn1').on('click',function(){
* loadMask.hide();
* });
* });
*
* @class BUI.Mask.LoadMask
* @extends BUI.Base
*/
function LoadMask(config) {
var _self = this;
LoadMask.superclass.constructor.call(_self, config);
}
BUI.extend(LoadMask, BUI.Base);
LoadMask.ATTRS = {
/**
* 屏蔽的元素
*
* var loadMask = new Mask.LoadMask({
* el : '#domId'
* });
*
* @cfg {jQuery} el
*/
el : {
},
/**
* 加载时显示的加载信息
*
* var loadMask = new Mask.LoadMask({
* el : '#domId',
* msg : '正在加载,请稍后。。。'
* });
*
* @cfg {String} msg [msg = 'Loading...']
*/
msg:{
value : 'Loading...'
},
/**
* 加载时显示的加载信息的样式
*
* var loadMask = new Mask.LoadMask({
* el : '#domId',
* msgCls : 'custom-cls'
* });
*
* @cfg {String} [msgCls = 'x-mask-loading']
*/
msgCls:{
value : 'x-mask-loading'
},
/**
* 加载控件是否禁用
* @type {Boolean}
* @field
* @default false
* @ignore
*/
disabled:{
value : false
}
};
//对象原型
BUI.augment(LoadMask,
{
/**
* 设置控件不可用
*/
disable:function () {
this.set('disabled',true);
},
/**
* @private 加载已经完毕,解除屏蔽
*/
onLoad:function () {
Mask.unmaskElement(this.get('el'));
},
/**
* @private 开始加载,屏蔽当前元素
*/
onBeforeLoad:function () {
var _self = this;
if (!_self.get('disabled')) {
Mask.maskElement(_self.get('el'), _self.get('msg'), this.get('msgCls'));
}
},
/**
* 显示加载条,并遮盖元素
*/
show:function () {
this.onBeforeLoad();
},
/**
* 隐藏加载条,并解除遮盖元素
*/
hide:function () {
this.onLoad();
},
/*
* 清理资源
*/
destroy:function () {
this.hide();
this.clearAttrVals();
this.off();
}
});
module.exports = LoadMask;
});
define("bui/overlay", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview Overlay 模块的入口
* @ignore
*/
var BUI = require("bui/common"),
Overlay = BUI.namespace('Overlay');
BUI.mix(Overlay, {
Overlay : require("bui/overlay/overlay"),
Dialog : require("bui/overlay/dialog"),
Message : require("bui/overlay/message")
});
BUI.mix(Overlay,{
OverlayView : Overlay.Overlay.View,
DialogView : Overlay.Dialog.View
});
BUI.Message = BUI.Overlay.Message;
module.exports = Overlay;
});
define("bui/overlay/overlay", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 悬浮层
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
CLS_ARROW = 'x-align-arrow',
UIBase = Component.UIBase;
/**
* 悬浮层的视图类
* @class BUI.Overlay.OverlayView
* @extends BUI.Component.View
* @mixins BUI.Component.UIBase.PositionView
* @mixins BUI.Component.UIBase.CloseView
* @private
*/
var overlayView = Component.View.extend([
UIBase.PositionView,
UIBase.CloseView
]);
/**
* 悬浮层,显示悬浮信息,Message、Dialog的基类
*
*
*
* BUI.use('bui/overlay',function(Overlay){
* //点击#btn,显示overlay
* var overlay = new Overlay.Overlay({
* trigger : '#btn',
* content : '这是内容',
* align : {
* points : ['bl','tl']
* }, //对齐方式
* elCls : 'custom-cls', //自定义样式
* autoHide : true //点击overlay外面,overlay 会自动隐藏
* });
*
* overlay.render();
* });
*
*
*
* @class BUI.Overlay.Overlay
* @extends BUI.Component.Controller
* @mixins BUI.Component.UIBase.Position
* @mixins BUI.Component.UIBase.Align
* @mixins BUI.Component.UIBase.Close
* @mixins BUI.Component.UIBase.AutoShow
* @mixins BUI.Component.UIBase.AutoHide
*/
var overlay = Component.Controller.extend([UIBase.Position,UIBase.Align,UIBase.Close,UIBase.AutoShow,UIBase.AutoHide],{
renderUI : function(){
var _self = this,
el = _self.get('el'),
arrowContainer = _self.get('arrowContainer'),
container = arrowContainer ? el.one(arrowContainer) : el;
if(_self.get('showArrow')){
$(_self.get('arrowTpl')).appendTo(container);
}
},
show : function(){
var _self = this,
effectCfg = _self.get('effect'),
el = _self.get('el'),
visibleMode = _self.get('visibleMode'),
effect = effectCfg.effect,
duration = effectCfg.duration;
//如果还未渲染,则先渲染控件
if(!_self.get('rendered')){
_self.set('visible',true);
_self.render();
_self.set('visible',false);
el = _self.get('el');
}
if(visibleMode === 'visibility'){
_self.set('visible',true);
el.css({display : 'none'});
}
switch(effect){
case 'linear' :
el.show(duration,callback);
break;
case 'fade' :
el.fadeIn(duration,callback);
break;
case 'slide' :
el.slideDown(duration,callback);
break;
default:
callback();
break;
}
function callback(){
if(visibleMode === 'visibility'){
el.css({display : 'block'});
}else{
_self.set('visible',true);
}
if(effectCfg.callback){
effectCfg.callback.call(_self);
}
//自动隐藏
var delay = _self.get('autoHideDelay'),
delayHandler = _self.get('delayHandler');
if(delay){
delayHandler && clearTimeout(delayHandler);
delayHandler = setTimeout(function(){
_self.hide();
_self.set('delayHandler',null);
},delay);
_self.set('delayHandler',delayHandler);
}
}
},
hide : function(){
var _self = this,
effectCfg = _self.get('effect'),
el = _self.get('el'),
effect = effectCfg.effect,
duration = effectCfg.duration;
switch(effect){
case 'linear':
el.hide(duration,callback);
break;
case 'fade' :
el.fadeOut(duration,callback);
break;
case 'slide' :
el.slideUp(duration,callback);
break;
default:
callback();
break;
}
function callback(){
if(_self.get('visibleMode') === 'visibility'){
el.css({display : 'block'});
}
_self.set('visible',false);
if(effectCfg.callback){
effectCfg.callback.call(_self);
}
}
}
},{
ATTRS :
{
/**
* {Object} - 可选, 显示或隐藏时的特效支持, 对象包含以下配置
*
*
*
* BUI.use('bui/overlay',function(Overlay){
* var dialog = new Overlay.Dialog({
* title:'非模态窗口',
* width:500,
* height:300,
* mask:false, //设置是否模态
* buttons:[],
* bodyContent:'这是一个非模态窗口,并且不带按钮
'
* });
* dialog.show();
* $('#btnShow').on('click',function () {
* dialog.show();
* });
* });
*
*
* ** 使用现有的html结构 **
*
* BUI.use('bui/overlay',function(Overlay){
* var dialog = new Overlay.Dialog({
* title:'配置DOM',
* width:500,
* height:250,
* contentId:'content',//配置DOM容器的编号
* success:function () {
* alert('确认');
* this.hide();
* }
* });
* dialog.show();
* $('#btnShow').on('click',function () {
* dialog.show();
* });
* });
*
* @class BUI.Overlay.Dialog
* @extends BUI.Overlay.Overlay
* @mixins BUI.Component.UIBase.StdMod
* @mixins BUI.Component.UIBase.Mask
* @mixins BUI.Component.UIBase.Drag
*/
var dialog = Overlay.extend([UIBase.StdMod,UIBase.Mask,UIBase.Drag],{
show:function(){
var _self = this;
align = _self.get('align');
dialog.superclass.show.call(this);
_self.set('align',align);
},/**/
//绑定事件
bindUI : function(){
var _self = this;
_self.on('closeclick',function(){
return _self.onCancel();
});
},
/**
* @protected
* 取消
*/
onCancel : function(){
var _self = this,
cancel = _self.get('cancel');
return cancel.call(this);
},
//设置按钮
_uiSetButtons:function(buttons){
var _self = this,
footer = _self.get('footer');
footer.children().remove();
BUI.each(buttons,function(conf){
_self._createButton(conf,footer);
});
},
//创建按钮
_createButton : function(conf,parent){
var _self = this,
temp = '',
btn = $(temp).appendTo(parent);
btn.on('click',function(){
conf.handler.call(_self,_self,this);
});
},
destructor : function(){
var _self = this,
contentId = _self.get('contentId'),
body = _self.get('body'),
closeAction = _self.get('closeAction');
if(closeAction == 'destroy'){
_self.hide();
if(contentId){
body.children().appendTo('#'+contentId);
}
}
}
},{
ATTRS :
{
closeTpl:{
view:true,
value : ' '
},
/**
* 弹出库的按钮,可以有多个,有3个参数
* var dialog = new Overlay.Dialog({
* title:'自定义按钮',
* width:500,
* height:300,
* mask:false,
* buttons:[
* {
* text:'自定义',
* elCls : 'button button-primary',
* handler : function(){
* //do some thing
* this.hide();
* }
* },{
* text:'关闭',
* elCls : 'button',
* handler : function(){
* this.hide();
* }
* }
* ],
*
* bodyContent:'这是一个自定义按钮窗口,可以配置事件和文本样式
' * }); * dialog.show(); ** { * property : 'bodyContent', * autoLoad : false, * lazyLoad : { * event : 'show' * }, * loadMask : { * el : _self.get('body') * } * } ** @type {Object} */ defaultLoaderCfg : { valueFn :function(){ var _self = this; return { property : 'bodyContent', autoLoad : false, lazyLoad : { event : 'show' }, loadMask : { el : _self.get('body') } } } }, /** * 弹出框标题 * @cfg {String} title */ /** * 弹出框标题 *
* dialog.set('title','new title');
*
* @type {String}
*/
title : {
view:true,
value : ''
},
align : {
value : {
node : window,
points : ['cc','cc']
}
},
mask : {
value:true
},
maskShared:{
value:false
},
headerContent:{
value:'
** BUI.use('bui/overlay',function(overlay){
*
* BUI.Message.Alert('这只是简单的提示信息','info');
* BUI.Message.Alert('这只是简单的成功信息','success');
* BUI.Message.Alert('这只是简单的警告信息','warning');
* BUI.Message.Alert('这只是简单的错误信息','error');
* BUI.Message.Alert('这只是简单的询问信息','question');
*
* //回调函数
* BUI.Message.Alert('点击触发回调函数',function() {
* alert('执行回调');
* },'error');
*
* //复杂的提示信息
* var msg = '<h2>上传失败,请上传10M以内的文件</h2>'+
* '<p class="auxiliary-text">如连续上传失败,请及时联系客服热线:0511-23883767834</p>'+
* '<p><a href="#">返回list页面</a> <a href="#">查看详情</a></p>';
* BUI.Message.Alert(msg,'error');
* //确认信息
* BUI.Message.Confirm('确认要更改么?',function(){
* alert('确认');
* },'question');
* });
*
* @class BUI.Overlay.Message
* @private
* @extends BUI.Overlay.Dialog
*/
var message = Dialog.extend({
/**
* @protected
* @ignore
*/
renderUI : function(){
this._setContent();
},
bindUI : function(){
var _self = this,
body = _self.get('body');
_self.on('afterVisibleChange',function(ev){
if(ev.newVal){
if(BUI.UA.ie < 8){
/**
* fix ie6,7 bug
* @ignore
*/
var outerWidth = body.outerWidth();
if(BUI.UA.ie == 6){
outerWidth = outerWidth > 350 ? 350 : outerWidth;
}
_self.get('header').width(outerWidth - 20);
_self.get('footer').width(outerWidth);
}
}
});
},
//根据模版设置内容
_setContent : function(){
var _self = this,
body = _self.get('body'),
contentTpl = BUI.substitute(_self.get('contentTpl'),{
msg : _self.get('msg'),
iconTpl : _self.get('iconTpl')
});
body.empty();
$(contentTpl).appendTo(body);
},
//设置类型
_uiSetIcon : function(v){
if (!this.get('rendered')) {
return;
}
this._setContent();
},
//设置文本
_uiSetMsg : function(v){
if (!this.get('rendered')) {
return;
}
this._setContent();
}
},{
ATTRS :
{
/**
* 图标类型
* info
success
warning
error
question
* BUI.Message.Confirm('确认要更改么?',function(){
* alert('确认');
* },'question');
*
* @static
* @method
* @member BUI.Message
* @param {String} msg 提示信息
* @param {Function} callback 确定的回调函数
* @param {String} icon 图标,提供以下几种图标:info,error,success,question,warning
*/
message.Confirm = Confirm;
/**
* 自定义消息框,传入配置信息 {@link BUI.Overlay.Dialog} 和 {@link BUI.Overlay.Message}
* @static
* @method
* @member BUI.Message
* @param {Object} config 配置信息
*/
message.Show = showMessage;
module.exports = message;
});
define("bui/picker", ["bui/common","jquery","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview Picker的入口
* @author dxq613@gmail.com
* @ignore
*/
var BUI = require("bui/common"),
Picker = BUI.namespace('Picker');
BUI.mix(Picker, {
Mixin : require("bui/picker/mixin"),
Picker : require("bui/picker/picker"),
ListPicker : require("bui/picker/listpicker")
});
module.exports = Picker;
});
define("bui/picker/mixin", ["jquery"], function(require, exports, module){
/**
* @fileOverview picker的扩展
* @ignore
*/
var $ = require("jquery");
/**
* @class BUI.Picker.Mixin
*/
var Mixin = function () {
};
Mixin.ATTRS = {
/**
* 用于选择的控件,默认为第一个子元素,此控件实现 @see {BUI.Component.UIBase.Selection} 接口
* @protected
* @type {Object|BUI.Component.Controller}
*/
innerControl : {
getter:function(){
return this.get('children')[0];
}
},
/**
* 显示选择器的事件
* @cfg {String} [triggerEvent='click']
*/
/**
* 显示选择器的事件
* @type {String}
* @default 'click'
*/
triggerEvent:{
value:'click'
},
/**
* 选择器选中的项,是否随着触发器改变
* @cfg {Boolean} [autoSetValue=true]
*/
/**
* 选择器选中的项,是否随着触发器改变
* @type {Boolean}
*/
autoSetValue : {
value : true
},
/**
* 选择发生改变的事件
* @cfg {String} [changeEvent='selectedchange']
*/
/**
* 选择发生改变的事件
* @type {String}
*/
changeEvent : {
value:'selectedchange'
},
/**
* 自动隐藏
* @type {Boolean}
* @override
*/
autoHide:{
value : true
},
/**
* 隐藏选择器的事件
* @protected
* @type {String}
*/
hideEvent:{
value:'itemclick'
},
/**
* 返回的文本放在的DOM,一般是input
* @cfg {String|HTMLElement|jQuery} textField
*/
/**
* 返回的文本放在的DOM,一般是input
* @type {String|HTMLElement|jQuery}
*/
textField : {
},
align : {
value : {
points: ['bl','tl'], // ['tr', 'tl'] 表示 overlay 的 tl 与参考节点的 tr 对齐
offset: [0, 0] // 有效值为 [n, m]
}
},
/**
* 返回的值放置DOM ,一般是input
* @cfg {String|HTMLElement|jQuery} valueField
*/
/**
* 返回的值放置DOM ,一般是input
* @type {String|HTMLElement|jQuery}
*/
valueField:{
}
/**
* @event selectedchange
* 选中值改变事件
* @param {Object} e 事件对象
* @param {String} text 选中的文本
* @param {string} value 选中的值
* @param {jQuery} curTrigger 当前触发picker的元素
*/
}
Mixin.prototype = {
__bindUI : function(){
var _self = this,
//innerControl = _self.get('innerControl'),
hideEvent = _self.get('hideEvent'),
trigger = $(_self.get('trigger'));
_self.on('show',function(ev){
//trigger.on(_self.get('triggerEvent'),function(e){
if(!_self.get('isInit')){
_self._initControl();
}
if(_self.get('autoSetValue')){
var valueField = _self.get('valueField') || _self.get('textField') || _self.get('curTrigger'),
val = $(valueField).val();
_self.setSelectedValue(val);
}
});
//_self.initControlEvent();
},
_initControl : function(){
var _self = this;
if(_self.get('isInit')){ //已经初始化过
return ;
}
if(!_self.get('innerControl')){
var control = _self.createControl();
_self.get('children').push(control);
}
_self.initControlEvent();
_self.set('isInit',true);
},
/**
* 初始化内部控件,绑定事件
*/
initControl : function(){
this._initControl();
},
/**
* @protected
* 初始化内部控件
*/
createControl : function(){
},
//初始化内部控件的事件
initControlEvent : function(){
var _self = this,
innerControl = _self.get('innerControl'),
trigger = $(_self.get('trigger')),
hideEvent = _self.get('hideEvent');
innerControl.on(_self.get('changeEvent'),function(e){
var curTrigger = _self.get('curTrigger'),
textField = _self.get('textField') || curTrigger || trigger,
valueField = _self.get('valueField'),
selValue = _self.getSelectedValue(),
isChange = false;
if(textField){
var selText = _self.getSelectedText(),
preText = $(textField).val();
if(selText != preText){
$(textField).val(selText);
isChange = true;
$(textField).trigger('change');
}
}
if(valueField && _self.get('autoSetValue')){
var preValue = $(valueField).val();
if(valueField != preValue){
$(valueField).val(selValue);
isChange = true;
$(valueField).trigger('change');
}
}
if(isChange){
_self.onChange(selText,selValue,e);
}
});
if(hideEvent){
innerControl.on(_self.get('hideEvent'),function(){
var curTrigger = _self.get('curTrigger');
try{ //隐藏时,在ie6,7下会报错
if(curTrigger){
curTrigger.focus();
}
}catch(e){
BUI.log(e);
}
_self.hide();
});
}
},
/**
* 设置选中的值
* @template
* @protected
* @param {String} val 设置值
*/
setSelectedValue : function(val){
},
/**
* 获取选中的值,多选状态下,值以','分割
* @template
* @protected
* @return {String} 选中的值
*/
getSelectedValue : function(){
},
/**
* 获取选中项的文本,多选状态下,文本以','分割
* @template
* @protected
* @return {String} 选中的文本
*/
getSelectedText : function(){
},
/**
* 选择器获取焦点时,默认选中内部控件
*/
focus : function(){
this.get('innerControl').focus();
},
/**
* @protected
* 发生改变
*/
onChange : function(selText,selValue,ev){
var _self = this,
curTrigger = _self.get('curTrigger');
//curTrigger && curTrigger.trigger('change'); //触发改变事件
_self.fire('selectedchange',{value : selValue,text : selText,curTrigger : curTrigger});
},
/**
* 处理 esc 键
* @protected
* @param {jQuery.Event} ev 事件对象
*/
handleNavEsc : function (ev) {
this.hide();
},
_uiSetValueField : function(v){
var _self = this;
if(v != null && v !== '' && _self.get('autoSetValue')){ //if(v)问题太多
_self.setSelectedValue($(v).val());
}
},
_getTextField : function(){
var _self = this;
return _self.get('textField') || _self.get('curTrigger');
}
}
module.exports = Mixin;
});
define("bui/picker/picker", ["jquery","bui/overlay","bui/common"], function(require, exports, module){
/**
* @fileOverview 选择器
* @ignore
*/
var $ = require("jquery"),
Overlay = require("bui/overlay").Overlay,
Mixin = require("bui/picker/mixin");
/**
* 选择器控件的基类,弹出一个层来选择数据,不要使用此类创建控件,仅用于继承实现控件
* xclass : 'picker'
*
* BUI.use(['bui/picker','bui/list'],function(Picker,List){
*
* var items = [
* {text:'选项1',value:'a'},
* {text:'选项2',value:'b'},
* {text:'选项3',value:'c'}
* ],
* list = new List.SimpleList({
* elCls:'bui-select-list',
* items : items
* }),
* picker = new Picker.ListPicker({
* trigger : '#show',
* valueField : '#hide', //如果需要列表返回的value,放在隐藏域,那么指定隐藏域
* width:100, //指定宽度
* children : [list] //配置picker内的列表
* });
* picker.render();
* });
*
* @abstract
* @class BUI.Picker.Picker
* @mixins BUI.Picker.Mixin
* @extends BUI.Overlay.Overlay
*/
var picker = Overlay.extend([Mixin], {
},{
ATTRS : {
}
},{
xclass:'picker'
});
module.exports = picker;
});
define("bui/picker/listpicker", ["jquery","bui/list","bui/common","bui/data","bui/overlay"], function(require, exports, module){
/**
* @fileOverview 列表项的选择器
* @ignore
*/
var $ = require("jquery"),
List = require("bui/list"),
Picker = require("bui/picker/picker"),
/**
* 列表选择器,xclass = 'list-picker'
*
* BUI.use(['bui/picker'],function(Picker){
*
* var items = [
* {text:'选项1',value:'a'},
* {text:'选项2',value:'b'},
* {text:'选项3',value:'c'}
* ],
* picker = new Picker.ListPicker({
* trigger : '#show',
* valueField : '#hide', //如果需要列表返回的value,放在隐藏域,那么指定隐藏域
* width:100, //指定宽度
* children : [{
* elCls:'bui-select-list',
* items : items
* }] //配置picker内的列表
* });
* picker.render();
* });
*
* @class BUI.Picker.ListPicker
* @extends BUI.Picker.Picker
*/
listPicker = Picker.extend({
initializer : function(){
var _self = this,
children = _self.get('children'),
list = _self.get('list');
if(!list){
children.push({
});
}
},
/**
* 设置选中的值
* @override
* @param {String} val 设置值
*/
setSelectedValue : function(val){
val = val ? val.toString() : '';
if(!this.get('isInit')){
this._initControl();
}
var _self = this,
list = _self.get('list'),
selectedValue = _self.getSelectedValue();
if(val !== selectedValue && list.getCount()){
if(list.get('multipleSelect')){
list.clearSelection();
}
list.setSelectionByField(val.split(','));
}
},
/**
* @protected
* @ignore
*/
onChange : function(selText,selValue,ev){
var _self = this,
curTrigger = _self.get('curTrigger');
//curTrigger && curTrigger.trigger('change'); //触发改变事件
_self.fire('selectedchange',{value : selValue,text : selText,curTrigger : curTrigger,item : ev.item});
},
/**
* 获取选中的值,多选状态下,值以','分割
* @return {String} 选中的值
*/
getSelectedValue : function(){
if(!this.get('isInit')){
this._initControl();
}
return this.get('list').getSelectionValues().join(',');
},
/**
* 获取选中项的文本,多选状态下,文本以','分割
* @return {String} 选中的文本
*/
getSelectedText : function(){
if(!this.get('isInit')){
this._initControl();
}
return this.get('list').getSelectionText().join(',');
}
},{
ATTRS : {
/**
* 默认子控件的样式,默认为'simple-list'
* @type {String}
* @override
*/
defaultChildClass:{
value : 'simple-list'
},
/**
* 选择的列表
*
* var list = picker.get('list');
* list.getSelected();
*
* @type {BUI.List.SimpleList}
* @readOnly
*/
list : {
getter:function(){
return this.get('children')[0];
}
}
/**
* @event selectedchange
* 选择发生改变事件
* @param {Object} e 事件对象
* @param {String} e.text 选中的文本
* @param {string} e.value 选中的值
* @param {Object} e.item 发生改变的选项
* @param {jQuery} e.curTrigger 当前触发picker的元素
*/
}
},{
xclass : 'list-picker'
});
module.exports = listPicker;
});
define("bui/toolbar", ["bui/common","jquery"], function(require, exports, module){
/**
* @fileOverview 工具栏命名空间入口
* @ignore
*/
var BUI = require("bui/common"),
Toolbar = BUI.namespace('Toolbar');
BUI.mix(Toolbar,{
BarItem : require("bui/toolbar/baritem"),
Bar : require("bui/toolbar/bar"),
PagingBar : require("bui/toolbar/pagingbar"),
NumberPagingBar : require("bui/toolbar/numberpagingbar")
});
module.exports = Toolbar;
});
define("bui/toolbar/baritem", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview buttons or controls of toolbar
* @author dxq613@gmail.com, yiminghe@gmail.com
* @ignore
*/
/**
* @name BUI.Toolbar
* @namespace 工具栏命名空间
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
PREFIX = BUI.prefix,
Component = BUI.Component,
UIBase = Component.UIBase;
/**
* barItem的视图类
* @class BUI.Toolbar.BarItemView
* @extends BUI.Component.View
* @mixins BUI.Component.UIBase.ListItemView
* @private
*/
var BarItemView = Component.View.extend([UIBase.ListItemView]);
/**
* 工具栏的子项,包括按钮、文本、链接和分隔符等
* @class BUI.Toolbar.BarItem
* @extends BUI.Component.Controller
*/
var BarItem = Component.Controller.extend([UIBase.ListItem],{
/**
* render baritem 's dom
* @protected
*/
renderUI:function() {
var el = this.get('el');
el.addClass(PREFIX + 'inline-block');
if (!el.attr('id')) {
el.attr('id', this.get('id'));
}
}
},{
ATTRS:
{
elTagName :{
view : true,
value : 'li'
},
/**
* 是否可选择
*
*
*
* @cfg {Object} [selectable = false]
*/
selectable : {
value : false
},
/**
* 是否获取焦点
* @default {boolean} false
*/
focusable : {
value : false
},
xview: {
value : BarItemView
}
}
},{
xclass : 'bar-item',
priority : 1
});
/**
* 工具栏的子项,添加按钮
* xclass : 'bar-item-button'
* @extends BUI.Toolbar.BarItem
* @class BUI.Toolbar.BarItem.Button
*/
var ButtonBarItem = BarItem.extend({
_uiSetDisabled : function(value){
var _self = this,
el = _self.get('el'),
method = value ? 'addClass' : 'removeClass';
el.find('button').attr('disabled',value)[method](PREFIX + 'button-disabled');
},
_uiSetChecked: function(value){
var _self = this,
el = _self.get('el'),
method = value ? 'addClass' : 'removeClass';
el.find('button')[method](PREFIX + 'button-checked');
},
_uiSetText : function(v){
var _self = this,
el = _self.get('el');
el.find('button').text(v);
},
_uiSetbtnCls : function(v){
var _self = this,
el = _self.get('el');
el.find('button').addClass(v);
}
},{
ATTRS:
{
/**
* 是否选中
* @type {Boolean}
*/
checked : {
value :false
},
/**
* 模板
* @type {String}
*/
tpl : {
view : true,
value : ''
},
/**
* 按钮的样式
* @cfg {String} btnCls
*/
/**
* 按钮的样式
* @type {String}
*/
btnCls:{
sync:false
},
/**
* The text to be used as innerHTML (html tags are accepted).
* @cfg {String} text
*/
/**
* The text to be used as innerHTML (html tags are accepted).
* @type {String}
*/
text : {
sync:false,
value : ''
}
}
},{
xclass : 'bar-item-button',
priority : 2
});
/**
* 工具栏项之间的分隔符
* xclass:'bar-item-separator'
* @extends BUI.Toolbar.BarItem
* @class BUI.Toolbar.BarItem.Separator
*/
var SeparatorBarItem = BarItem.extend({
/* render separator's dom
* @protected
*
*/
renderUI:function() {
var el = this.get('el');
el .attr('role', 'separator');
}
},
{
xclass : 'bar-item-separator',
priority : 2
});
/**
* 工具栏项之间的空白
* xclass:'bar-item-spacer'
* @extends BUI.Toolbar.BarItem
* @class BUI.Toolbar.BarItem.Spacer
*/
var SpacerBarItem = BarItem.extend({
},{
ATTRS:
{
/**
* 空白宽度
* @type {Number}
*/
width : {
view:true,
value : 2
}
}
},{
xclass : 'bar-item-spacer',
priority : 2
});
/**
* 显示文本的工具栏项
* xclass:'bar-item-text'
* @extends BUI.Toolbar.BarItem
* @class BUI.Toolbar.BarItem.Text
*/
var TextBarItem = BarItem.extend({
_uiSetText : function(text){
var _self = this,
el = _self.get('el');
el.html(text);
}
},{
ATTRS:
{
/**
* 文本用作 innerHTML (html tags are accepted).
* @cfg {String} text
*/
/**
* 文本用作 innerHTML (html tags are accepted).
* @default {String} ""
*/
text : {
value : ''
}
}
},{
xclass : 'bar-item-text',
priority : 2
});
BarItem.types = {
'button' : ButtonBarItem,
'separator' : SeparatorBarItem,
'spacer' : SpacerBarItem,
'text' : TextBarItem
};
module.exports = BarItem;
});
define("bui/toolbar/bar", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview A collection of commonly used function buttons or controls represented in compact visual form.
* @author dxq613@gmail.com, yiminghe@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
UIBase = Component.UIBase;
/**
* bar的视图类
* @class BUI.Toolbar.BarView
* @extends BUI.Component.View
* @private
*/
var barView = Component.View.extend({
renderUI:function() {
var el = this.get('el');
el.attr('role', 'toolbar');
if (!el.attr('id')) {
el.attr('id', BUI.guid('bar'));
}
}
});
/**
* 工具栏
* 可以放置按钮、文本、链接等,是分页栏的基类
* xclass : 'bar'
*
*
*
* BUI.use('bui/toolbar',function(Toolbar){
* var buttonGroup = new Toolbar.Bar({
* elCls : 'button-group',
* defaultChildCfg : {
* elCls : 'button button-small'
* },
* children : [{content : '增加'},{content : '修改'},{content : '删除'}],
*
* render : '#b1'
* });
*
* buttonGroup.render();
* });
*
* @class BUI.Toolbar.Bar
* @extends BUI.Component.Controller
* @mixins BUI.Component.UIBase.ChildList
*/
var Bar = Component.Controller.extend([UIBase.ChildList],
{
/**
* 通过id 获取项
* @param {String|Number} id the id of item
* @return {BUI.Toolbar.BarItem}
*/
getItem : function(id){
return this.getChild(id);
}
},{
ATTRS:
{
elTagName :{
view : true,
value : 'ul'
},
/**
* 默认子项的样式
* @type {String}
* @override
*/
defaultChildClass: {
value : 'bar-item'
},
/**
* 获取焦点
* @protected
* @ignore
*/
focusable : {
value : false
},
/**
* @private
* @ignore
*/
xview : {
value : barView
}
}
},{
xclass : 'bar',
priority : 1
});
module.exports = Bar;
});
define("bui/toolbar/pagingbar", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview a specialized toolbar that is bound to a Grid.Store and provides automatic paging control.
* @author dxq613@gmail.com, yiminghe@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Bar = require("bui/toolbar/bar"),
Component = BUI.Component,
Bindable = Component.UIBase.Bindable;
var PREFIX = BUI.prefix,
ID_FIRST = 'first',
ID_PREV = 'prev',
ID_NEXT = 'next',
ID_LAST = 'last',
ID_SKIP = 'skip',
ID_REFRESH = 'refresh',
ID_TOTAL_PAGE = 'totalPage',
ID_CURRENT_PAGE = 'curPage',
ID_TOTAL_COUNT = 'totalCount',
ID_BUTTONS = [ID_FIRST,ID_PREV,ID_NEXT,ID_LAST,ID_SKIP,ID_REFRESH],
ID_TEXTS = [ID_TOTAL_PAGE,ID_CURRENT_PAGE,ID_TOTAL_COUNT];
/**
* 分页栏
* xclass:'pagingbar'
* @extends BUI.Toolbar.Bar
* @mixins BUI.Component.UIBase.Bindable
* @class BUI.Toolbar.PagingBar
*/
var PagingBar = Bar.extend([Bindable],
{
/**
* From Bar, Initialize this paging bar items.
*
* @protected
*/
initializer:function () {
var _self = this,
children = _self.get('children'),
items = _self.get('items'),
store = _self.get('store');
if(!items){
items = _self._getItems();
BUI.each(items, function (item) {
children.push(item);//item
});
}else{
BUI.each(items, function (item,index) { //转换对应的分页栏
if(BUI.isString(item)){
if(BUI.Array.contains(item,ID_BUTTONS)){
item = _self._getButtonItem(item);
}else if(BUI.Array.contains(item,ID_TEXTS)){
item = _self._getTextItem(item);
}else{
item = {xtype : item};
}
}
children.push(item);
});
}
if (store && store.get('pageSize')) {
_self.set('pageSize', store.get('pageSize'));
}
},
/**
* bind page change and store events
*
* @protected
*/
bindUI:function () {
var _self = this;
_self._bindButtonEvent();
//_self._bindStoreEvents();
},
/**
* skip to page
* this method can fire "beforepagechange" event,
* if you return false in the handler the action will be canceled
* @param {Number} page target page
*/
jumpToPage:function (page) {
if (page <= 0 || page > this.get('totalPage')) {
return;
}
var _self = this,
store = _self.get('store'),
pageSize = _self.get('pageSize'),
index = page - 1,
start = index * pageSize;
var result = _self.fire('beforepagechange', {from:_self.get('curPage'), to:page});
if (store && result !== false) {
store.load({ start:start, limit:pageSize, pageIndex:index });
}
},
//after store loaded data,reset the information of paging bar and buttons state
_afterStoreLoad:function (store, params) {
var _self = this,
pageSize = _self.get('pageSize'),
start = 0, //页面的起始记录
end, //页面的结束记录
totalCount, //记录的总数
curPage, //当前页
totalPage;//总页数;
start = store.get('start');
//设置加载数据后翻页栏的状态
totalCount = store.getTotalCount();
end = totalCount - start > pageSize ? start + store.getCount() - 1: totalCount;
totalPage = parseInt((totalCount + pageSize - 1) / pageSize, 10);
totalPage = totalPage > 0 ? totalPage : 1;
curPage = parseInt(start / pageSize, 10) + 1;
_self.set('start', start);
_self.set('end', end);
_self.set('totalCount', totalCount);
_self.set('curPage', curPage);
_self.set('totalPage', totalPage);
//设置按钮状态
_self._setAllButtonsState();
_self._setNumberPages();
},
//bind page change events
_bindButtonEvent:function () {
var _self = this;
//first page handler
_self._bindButtonItemEvent(ID_FIRST, function () {
_self.jumpToPage(1);
});
//previous page handler
_self._bindButtonItemEvent(ID_PREV, function () {
_self.jumpToPage(_self.get('curPage') - 1);
});
//previous page next
_self._bindButtonItemEvent(ID_NEXT, function () {
_self.jumpToPage(_self.get('curPage') + 1);
});
//previous page next
_self._bindButtonItemEvent(ID_LAST, function () {
_self.jumpToPage(_self.get('totalPage'));
});
//skip to one page
_self._bindButtonItemEvent(ID_SKIP, function () {
handleSkip();
});
//refresh
_self._bindButtonItemEvent(ID_REFRESH, function () {
_self.jumpToPage(_self.get('curPage'));
});
//input page number and press key "enter"
var curPage = _self.getItem(ID_CURRENT_PAGE);
if(curPage){
curPage.get('el').on('keyup', function (event) {
event.stopPropagation();
if (event.keyCode === 13) {
handleSkip();
}
});
}
//when click skip button or press key "enter",cause an action of skipping page
/**
* @private
* @ignore
*/
function handleSkip() {
var value = parseInt(_self._getCurrentPageValue(), 10);
if (_self._isPageAllowRedirect(value)) {
_self.jumpToPage(value);
} else {
_self._setCurrentPageValue(_self.get('curPage'));
}
}
},
// bind button item event
_bindButtonItemEvent:function (id, func) {
var _self = this,
item = _self.getItem(id);
if (item) {
item.on('click', func);
}
},
onLoad:function (params) {
var _self = this,
store = _self.get('store');
_self._afterStoreLoad(store, params);
},
//get the items of paging bar
_getItems:function () {
var _self = this,
items = _self.get('items');
if (items && items.length) {
return items;
}
//default items
items = [];
//first item
items.push(_self._getButtonItem(ID_FIRST));
//previous item
items.push(_self._getButtonItem(ID_PREV));
//separator item
items.push(_self._getSeparator());
//total page of store
items.push(_self._getTextItem(ID_TOTAL_PAGE));
//current page of store
items.push(_self._getTextItem(ID_CURRENT_PAGE));
//button for skip to
items.push(_self._getButtonItem(ID_SKIP));
//separator item
items.push(_self._getSeparator());
//next item
items.push(_self._getButtonItem(ID_NEXT));
//last item
items.push(_self._getButtonItem(ID_LAST));
//separator item
items.push(_self._getSeparator());
//current page of store
items.push(_self._getTextItem(ID_TOTAL_COUNT));
return items;
},
//get item which the xclass is button
_getButtonItem:function (id) {
var _self = this;
return {
id:id,
xclass:'bar-item-button',
text:_self.get(id + 'Text'),
disabled:true,
elCls:_self.get(id + 'Cls')
};
},
//get separator item
_getSeparator:function () {
return {xclass:'bar-item-separator'};
},
//get text item
_getTextItem:function (id) {
var _self = this;
return {
id:id,
xclass:'bar-item-text',
text:_self._getTextItemTpl(id)
};
},
//get text item's template
_getTextItemTpl:function (id) {
var _self = this,
obj = _self.getAttrVals();
return BUI.substitute(this.get(id + 'Tpl'), obj);
},
//Whether to allow jump, if it had been in the current page or not within the scope of effective page, not allowed to jump
_isPageAllowRedirect:function (value) {
var _self = this;
return value && value > 0 && value <= _self.get('totalPage') && value !== _self.get('curPage');
},
//when page changed, reset all buttons state
_setAllButtonsState:function () {
var _self = this,
store = _self.get('store');
if (store) {
_self._setButtonsState([ID_PREV, ID_NEXT, ID_FIRST, ID_LAST, ID_SKIP], true);
}
if (_self.get('curPage') === 1) {
_self._setButtonsState([ID_PREV, ID_FIRST], false);
}
if (_self.get('curPage') === _self.get('totalPage')) {
_self._setButtonsState([ID_NEXT, ID_LAST], false);
}
},
//if button id in the param buttons,set the button state
_setButtonsState:function (buttons, enable) {
var _self = this,
children = _self.get('children');
BUI.each(children, function (child) {
if (BUI.Array.indexOf(child.get('id'), buttons) !== -1) {
child.set('disabled', !enable);
}
});
},
//show the information of current page , total count of pages and total count of records
_setNumberPages:function () {
var _self = this,
items = _self.getItems();/*,
totalPageItem = _self.getItem(ID_TOTAL_PAGE),
totalCountItem = _self.getItem(ID_TOTAL_COUNT);
if (totalPageItem) {
totalPageItem.set('content', _self._getTextItemTpl(ID_TOTAL_PAGE));
}
_self._setCurrentPageValue(_self.get(ID_CURRENT_PAGE));
if (totalCountItem) {
totalCountItem.set('content', _self._getTextItemTpl(ID_TOTAL_COUNT));
}*/
BUI.each(items,function(item){
if(item.__xclass === 'bar-item-text'){
item.set('content', _self._getTextItemTpl(item.get('id')));
}
});
},
_getCurrentPageValue:function (curItem) {
var _self = this;
curItem = curItem || _self.getItem(ID_CURRENT_PAGE);
if(curItem){
var textEl = curItem.get('el').find('input');
return textEl.val();
}
},
//show current page in textbox
_setCurrentPageValue:function (value, curItem) {
var _self = this;
curItem = curItem || _self.getItem(ID_CURRENT_PAGE);
if(curItem){
var textEl = curItem.get('el').find('input');
textEl.val(value);
}
}
}, {
ATTRS:
{
/**
* the text of button for first page
* @default {String} "首 页"
*/
firstText:{
value:'首 页'
},
/**
* the cls of button for first page
* @default {String} "bui-pb-first"
*/
firstCls:{
value:PREFIX + 'pb-first'
},
/**
* the text for previous page button
* @default {String} "前一页"
*/
prevText:{
value:'上一页'
},
/**
* the cls for previous page button
* @default {String} "bui-pb-prev"
*/
prevCls:{
value: PREFIX + 'pb-prev'
},
/**
* the text for next page button
* @default {String} "下一页"
*/
nextText:{
value:'下一页'
},
/**
* the cls for next page button
* @default {String} "bui-pb-next"
*/
nextCls:{
value: PREFIX + 'pb-next'
},
/**
* the text for last page button
* @default {String} "末 页"
*/
lastText:{
value:'末 页'
},
/**
* the cls for last page button
* @default {String} "bui-pb-last"
*/
lastCls:{
value:PREFIX + 'pb-last'
},
/**
* the text for skip page button
* @default {String} "跳 转"
*/
skipText:{
value:'确定'
},
/**
* the cls for skip page button
* @default {String} "bui-pb-last"
*/
skipCls:{
value:PREFIX + 'pb-skip'
},
refreshText : {
value : '刷新'
},
refreshCls : {
value:PREFIX + 'pb-refresh'
},
/**
* the template of total page info
* @default {String} '共 {totalPage} 页'
*/
totalPageTpl:{
value:'共 {totalPage} 页'
},
/**
* the template of current page info
* @default {String} '第 <input type="text" autocomplete="off" class="bui-pb-page" size="20" name="inputItem"> 页'
*/
curPageTpl:{
value:'第 页'
},
/**
* the template of total count info
* @default {String} '共{totalCount}条记录'
*/
totalCountTpl:{
value:'共{totalCount}条记录'
},
autoInitItems : {
value : false
},
/**
* current page of the paging bar
* @private
* @default {Number} 0
*/
curPage:{
value:0
},
/**
* total page of the paging bar
* @private
* @default {Number} 0
*/
totalPage:{
value:0
},
/**
* total count of the store that the paging bar bind to
* @private
* @default {Number} 0
*/
totalCount:{
value:0
},
/**
* The number of records considered to form a 'page'.
* if store set the property ,override this value by store's pageSize
* @private
*/
pageSize:{
value:30
},
/**
* The {@link BUI.Data.Store} the paging toolbar should use as its data source.
* @protected
*/
store:{
}
},
ID_FIRST:ID_FIRST,
ID_PREV:ID_PREV,
ID_NEXT:ID_NEXT,
ID_LAST:ID_LAST,
ID_SKIP:ID_SKIP,
ID_REFRESH: ID_REFRESH,
ID_TOTAL_PAGE:ID_TOTAL_PAGE,
ID_CURRENT_PAGE:ID_CURRENT_PAGE,
ID_TOTAL_COUNT:ID_TOTAL_COUNT
}, {
xclass:'pagingbar',
priority:2
});
module.exports = PagingBar;
});
define("bui/toolbar/numberpagingbar", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview a specialized toolbar that is bound to a Grid.Store and provides automatic paging control.
* @author
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
PBar = require("bui/toolbar/pagingbar");
var PREFIX = BUI.prefix,
NUMBER_CONTAINER = 'numberContainer',
CLS_NUMBER_BUTTON = PREFIX + 'button-number';
/**
* 数字分页栏
* xclass:'pagingbar-number'
* @extends BUI.Toolbar.PagingBar
* @class BUI.Toolbar.NumberPagingBar
*/
var NumberPagingBar = PBar.extend(
{
/**
* get the initial items of paging bar
* @protected
*
*/
_getItems : function(){
var _self = this,
items = _self.get('items');
if(items){
return items;
}
//default items
items = [];
//previous item
items.push(_self._getButtonItem(PBar.ID_PREV));
//next item
items.push(_self._getButtonItem(PBar.ID_NEXT));
return items;
},
_getButtonItem : function(id){
var _self = this;
return {
id:id,
content:''+_self.get(id + 'Text')+'',
disabled:true
};
},
/**
* bind buttons event
* @protected
*
*/
_bindButtonEvent : function(){
var _self = this,
cls = _self.get('numberButtonCls');
NumberPagingBar.superclass._bindButtonEvent.call(this);
_self.get('el').delegate('a','click',function(ev){
ev.preventDefault();
});
_self.on('click',function(ev){
var item = ev.target;
if(item && item.get('el').hasClass(cls)){
var page = item.get('id');
_self.jumpToPage(page);
}
});
},
//设置页码信息,设置 页数 按钮
_setNumberPages : function(){
var _self = this;
_self._setNumberButtons();
},
//设置 页数 按钮
_setNumberButtons : function(){
var _self = this,
curPage = _self.get('curPage'),
totalPage = _self.get('totalPage'),
numberItems = _self._getNumberItems(curPage,totalPage),
curItem;
_self._clearNumberButtons();
BUI.each(numberItems,function(item){
_self._appendNumberButton(item);
});
curItem = _self.getItem(curPage);
if(curItem){
curItem.set('selected',true);
}
},
_appendNumberButton : function(cfg){
var _self = this,
count = _self.getItemCount();
var item = _self.addItemAt(cfg,count - 1);
},
_clearNumberButtons : function(){
var _self = this,
items = _self.getItems(),
count = _self.getItemCount();
while(count > 2){
_self.removeItemAt(count-2);
count = _self.getItemCount();
}
},
//获取所有页码按钮的配置项
_getNumberItems : function(curPage, totalPage){
var _self = this,
result = [],
maxLimitCount = _self.get('maxLimitCount'),
showRangeCount = _self.get('showRangeCount'),
maxPage;
function addNumberItem(from,to){
for(var i = from ;i<=to;i++){
result.push(_self._getNumberItem(i));
}
}
function addEllipsis(){
result.push(_self._getEllipsisItem());
}
if(totalPage < maxLimitCount){
maxPage = totalPage;
addNumberItem(1,totalPage);
}else{
var startNum = (curPage <= maxLimitCount) ? 1 : (curPage - showRangeCount),
lastLimit = curPage + showRangeCount,
endNum = lastLimit < totalPage ? (lastLimit > maxLimitCount ? lastLimit : maxLimitCount) : totalPage;
if (startNum > 1) {
addNumberItem(1, 1);
if(startNum > 2){
addEllipsis();
}
}
maxPage = endNum;
addNumberItem(startNum, endNum);
}
if (maxPage < totalPage) {
if(maxPage < totalPage -1){
addEllipsis();
}
addNumberItem(totalPage, totalPage);
}
return result;
},
//获取省略号
_getEllipsisItem : function(){
var _self = this;
return {
disabled: true,
content : _self.get('ellipsisTpl')
};
},
//生成页面按钮配置项
_getNumberItem : function(page){
var _self = this;
return {
id : page,
elCls : _self.get('numberButtonCls')
};
}
},{
ATTRS:{
itemStatusCls : {
value : {
selected : 'active',
disabled : 'disabled'
}
},
itemTpl : {
value : '{id}'
},
prevText : {
value : '<<'
},
nextText : {
value : '>>'
},
/**
* 当页码超过该设置页码时候显示省略号
* @default {Number} 4
*/
maxLimitCount : {
value : 4
},
showRangeCount : {
value : 1
},
/**
* the css used on number button
*/
numberButtonCls:{
value : CLS_NUMBER_BUTTON
},
/**
* the template of ellipsis which represent the omitted pages number
*/
ellipsisTpl : {
value : '...'
}
}
},{
xclass : 'pagingbar-number',
priority : 3
});
module.exports = NumberPagingBar;
});
define("bui/calendar", ["bui/common","jquery","bui/picker","bui/overlay","bui/list","bui/data","bui/toolbar"], function(require, exports, module){
/**
* @fileOverview 日历命名空间入口
* @ignore
*/
var BUI = require("bui/common"),
Calendar = BUI.namespace('Calendar');
BUI.mix(Calendar, {
Calendar: require("bui/calendar/calendar"),
MonthPicker: require("bui/calendar/monthpicker"),
DatePicker: require("bui/calendar/datepicker"),
Resource : require("bui/calendar/resource")
});
module.exports = Calendar;
});
define("bui/calendar/calendar", ["bui/common","jquery","bui/picker","bui/overlay","bui/list","bui/data","bui/toolbar"], function(require, exports, module){
/**
* @fileOverview 日期控件
* @author dxq613@gmail.com
* @ignore
*/
var BUI = require("bui/common"),
PREFIX = BUI.prefix,
CLS_PICKER_TIME = 'x-datepicker-time',
CLS_PICKER_HOUR = 'x-datepicker-hour',
CLS_PICKER_MINUTE = 'x-datepicker-minute',
CLS_PICKER_SECOND = 'x-datepicker-second',
CLS_TIME_PICKER = 'x-timepicker',
Picker = require("bui/picker").ListPicker,
MonthPicker = require("bui/calendar/monthpicker"),
Header = require("bui/calendar/header"),
Panel = require("bui/calendar/panel"),
Toolbar = require("bui/toolbar"),
Component = BUI.Component,
DateUtil = BUI.Date,
Resource = require("bui/calendar/resource");
function today(){
var now = new Date();
return new Date(now.getFullYear(),now.getMonth(),now.getDate());
}
function fixedNumber(n){
if( n< 10 ){
return '0'+n;
}
return n.toString();
}
function getNumberItems(end){
var items = [];
for (var i = 0; i < end; i++) {
items.push({text:fixedNumber(i),value:fixedNumber(i)});
}
return items;
}
function getTimeUnit (self,cls){
var inputEl = self.get('el').find('.' + cls);
return parseInt(inputEl.val(),10);
}
function setTimeUnit (self,cls,val){
var inputEl = self.get('el').find('.' + cls);
if(BUI.isNumber(val)){
val = fixedNumber(val);
}
inputEl.val(val);
}
/**
* 日期控件
*
*
*
* BUI.use('bui/calendar',function(Calendar){
* var calendar = new Calendar.Calendar({
* render:'#calendar'
* });
* calendar.render();
* calendar.on('selectedchange',function (ev) {
* alert(ev.date);
* });
* });
*
* @class BUI.Calendar.Calendar
* @extends BUI.Component.Controller
*/
var calendar = Component.Controller.extend({
//设置内容
initializer: function(){
var _self = this,
children = _self.get('children'),
header = new Header(),
panel = new Panel(),
footer = _self.get('footer') || _self._createFooter();/*,
monthPicker = _self.get('monthPicker') || _self._createMonthPicker();*/
//添加头
children.push(header);
//添加panel
children.push(panel);
children.push(footer);
//children.push(monthPicker);
_self.set('header',header);
_self.set('panel',panel);
_self.set('footer',footer);
//_self.set('monthPicker',monthPicker);
},
renderUI : function(){
var _self = this,
children = _self.get('children');
if(_self.get('showTime')){
var timepicker = _self.get('timepicker') || _self._initTimePicker();
children.push(timepicker);
_self.set('timepicker',timepicker);
}
},
//绑定事件
bindUI : function(){
var _self = this,
header = _self.get('header'),
panel = _self.get('panel');
panel.on('selectedchange',function(e){
var date = e.date;
if(!DateUtil.isDateEquals(date,_self.get('selectedDate'))){
_self.set('selectedDate',date);
}
});
if(!_self.get('showTime')){
panel.on('click',function(){
_self.fire('accept');
});
}else{
_self._initTimePickerEvent();
}
header.on('monthchange',function(e){
//_self.get('header').setMonth(e.year,e.month);
_self._setYearMonth(e.year,e.month);
});
header.on('headerclick',function(){
var monthPicker = _self.get('monthpicker') || _self._createMonthPicker();
monthPicker.set('year',header.get('year'));
monthPicker.set('month',header.get('month'));
monthPicker.show();
});
},
_initTimePicker : function(){
var _self = this,
lockTime = _self.get('lockTime'),
_timePickerEnum={hour:CLS_PICKER_HOUR,minute:CLS_PICKER_MINUTE,second:CLS_PICKER_SECOND};
if(lockTime){
for(var key in lockTime){
var noCls = _timePickerEnum[key.toLowerCase()];
_self.set(key,lockTime[key]);
if(!lockTime.editable){
_self.get('el').find("."+noCls).attr("disabled","");
}
}
}
var picker = new Picker({
elCls : CLS_TIME_PICKER,
children:[{
itemTpl : '
* calendar.set('maxDate','2013-07-29');
*
* @type {Date}
*/
maxDate : {
},
/**
* 最小日期
*
* calendar.set('minDate','2013-07-29');
*
* @type {Date}
*/
minDate : {
},
/**
* 选择月份控件
* @private
* @type {Object}
*/
monthPicker : {
},
/**
* 选择时间控件
* @private
* @type {Object}
*/
timepicker:{
},
width:{
value:180
},
events:{
value:{
/**
* @event
* @name BUI.Calendar.Calendar#click
* @param {Object} e 点击事件
* @param {Date} e.date
*/
'click' : false,
/**
* 确认日期更改,如果不显示日期则当点击日期或者点击今天按钮时触发,如果显示日期,则当点击确认按钮时触发。
* @event
*/
'accept' : false,
/**
* @event
* @name BUI.Calendar.Calendar#datechange
* @param {Object} e 选中的日期发生改变
* @param {Date} e.date
*/
'datechange' : false,
/**
* @event
* @name BUI.Calendar.Calendar#monthchange
* @param {Object} e 月份发生改变
* @param {Number} e.year
* @param {Number} e.month
*/
'monthchange' : false
}
},
/**
* 是否选择时间,此选项决定是否可以选择时间
*
* @cfg {Boolean} showTime
*/
showTime : {
value : false
},
/**
* 锁定时间选择
*
* var calendar = new Calendar.Calendar({
* render:'#calendar',
* lockTime : {hour:00,minute:30} //表示锁定时为00,分为30分,秒无锁用户可选择
* });
*
*
* @type {Object}
*/
lockTime :{
},
timeTpl : {
value : '::'
},
/**
* 选择的日期,默认为当天
*
* var calendar = new Calendar.Calendar({
* render:'#calendar',
* selectedDate : new Date('2013/07/01') //不能使用字符串
* });
*
* @cfg {Date} selectedDate
*/
/**
* 选择的日期
*
* calendar.set('selectedDate',new Date('2013-9-01'));
*
* @type {Date}
* @default today
*/
selectedDate : {
value : today()
},
/**
* 小时,默认为当前小时
* @type {Number}
*/
hour : {
value : new Date().getHours()
},
/**
* 分,默认为当前分
* @type {Number}
*/
minute:{
value : new Date().getMinutes()
},
/**
* 秒,默认为当前秒
* @type {Number}
*/
second : {
value : 0
}
}
},{
xclass : 'calendar',
priority : 0
});
module.exports = calendar;
});
define("bui/calendar/monthpicker", ["jquery","bui/common","bui/overlay","bui/list","bui/data","bui/toolbar"], function(require, exports, module){
/**
* @fileOverview 选择年月
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Component = BUI.Component,
Overlay = require("bui/overlay").Overlay,
List = require("bui/list").SimpleList,
Toolbar = require("bui/toolbar"),
PREFIX = BUI.prefix,
CLS_MONTH = 'x-monthpicker-month',
DATA_MONTH = 'data-month',
DATA_YEAR = 'data-year',
CLS_YEAR = 'x-monthpicker-year',
CLS_YEAR_NAV = 'x-monthpicker-yearnav',
CLS_SELECTED = 'x-monthpicker-selected',
CLS_ITEM = 'x-monthpicker-item',
Resource = require("bui/calendar/resource");
function getMonths(){
return $.map(Resource.months,function(month,index){
return {text:month,value:index};
});
}
var MonthPanel = List.extend({
bindUI : function(){
var _self = this;
_self.get('el').delegate('a','click',function(ev){
ev.preventDefault();
}).delegate('.' + CLS_MONTH,'dblclick',function(){
_self.fire('monthdblclick');
});
}
},{
ATTRS:{
itemTpl:{
view:true,
value : ''+resource.weekDays[0]+' | ' + ''+resource.weekDays[1]+' | ' + ''+resource.weekDays[2]+' | ' + ''+resource.weekDays[3]+' | ' + ''+resource.weekDays[4]+' | ' + ''+resource.weekDays[5]+' | ' + ''+resource.weekDays[6]+' | ' + '
---|
*
*
* BUI.use('bui/calendar',function(Calendar){
* var datepicker = new Calendar.DatePicker({
* trigger:'.calendar',
* //delegateTrigger : true, //如果设置此参数,那么新增加的.calendar元素也会支持日历选择
* autoRender : true
* });
* });
*
* @class BUI.Calendar.DatePicker
* @extends BUI.Picker.Picker
*/
var datepicker = Picker.extend({
initializer:function(){
},
/**
* @protected
* 初始化内部控件
*/
createControl : function(){
var _self = this,
children = _self.get('children'),
calendar = new Calendar({
render : _self.get('el'),
showTime : _self.get('showTime'),
lockTime : _self.get('lockTime'),
minDate: _self.get('minDate'),
maxDate: _self.get('maxDate'),
autoRender : true
});
calendar.on('clear', function(){
var curTrigger = _self.get('curTrigger'),
oldValue = curTrigger.val();
if(oldValue){
curTrigger.val('');
curTrigger.trigger('change');
}
});
if (!_self.get('dateMask')) {
if (_self.get('showTime')) {
_self.set('dateMask', 'yyyy-mm-dd HH:MM:ss');
} else {
_self.set('dateMask', 'yyyy-mm-dd');
}
}
children.push(calendar);
_self.set('calendar',calendar);
return calendar;
},
/**
* 设置选中的值
*
* datePicker.setSelectedValue('2012-01-1');
*
* @param {String} val 设置值
* @protected
*/
setSelectedValue : function(val){
if(!this.get('calendar')){
return;
}
var _self = this,
calendar = this.get('calendar'),
date = DateUtil.parse(val,_self.get("dateMask"));
date = date || _self.get('selectedDate');
calendar.set('selectedDate',DateUtil.getDate(date));
if(_self.get('showTime')){
var lockTime = this.get("lockTime"),
hour = date.getHours(),
minute = date.getMinutes(),
second = date.getSeconds();
if(lockTime){
if(!val || !lockTime.editable){
hour = lockTime['hour'] != null ?lockTime['hour']:hour;
minute = lockTime['minute'] != null ?lockTime['minute']:minute;
second = lockTime['second'] != null ?lockTime['second']:second;
}
}
calendar.set('hour',hour);
calendar.set('minute',minute);
calendar.set('second',second);
}
},
/**
* 获取选中的值
* @protected
* @return {String} 选中的值
*/
getSelectedValue : function(){
if(!this.get('calendar')){
return null;
}
var _self = this,
calendar = _self.get('calendar'),
date = DateUtil.getDate(calendar.get('selectedDate'));
if(_self.get('showTime')){
date = DateUtil.addHour(calendar.get('hour'),date);
date = DateUtil.addMinute(calendar.get('minute'),date);
date = DateUtil.addSecond(calendar.get('second'),date);
}
return date;
},
/**
* 获取选中项的文本,多选状态下,文本以','分割
* @protected
* @return {String} 选中的文本
*/
getSelectedText : function(){
if(!this.get('calendar')){
return '';
}
return DateUtil.format(this.getSelectedValue(),this._getFormatType());
},
_getFormatType : function(){
return this.get('dateMask');
},
//设置最大值
_uiSetMaxDate : function(v){
if(!this.get('calendar')){
return null;
}
var _self = this;
_self.get('calendar').set('maxDate',v);
},
//设置最小值
_uiSetMinDate : function(v){
if(!this.get('calendar')){
return null;
}
var _self = this;
_self.get('calendar').set('minDate',v);
}
},{
ATTRS :
{
/**
* 是否显示日期
*
* var datepicker = new Calendar.DatePicker({
* trigger:'.calendar',
* showTime : true, //可以选择日期
* autoRender : true
* });
*
* @type {Boolean}
*/
showTime : {
value:false
},
/**
* 锁定时间选择,默认锁定的时间不能修改可以通过 editable : true 来允许修改锁定的时间
*
* var calendar = new Calendar.Calendar({
* render:'#calendar',
* lockTime : {hour:00,minute:30} //表示锁定时为00,分为30分,秒无锁用户可选择
* });
*
*
* @type {Object}
*/
lockTime :{
},
/**
* 最大日期
*
* var datepicker = new Calendar.DatePicker({
* trigger:'.calendar',
* maxDate : '2014-01-01',
* minDate : '2013-7-25',
* autoRender : true
* });
*
* @type {Date}
*/
maxDate : {
},
/**
* 最小日期
*
* var datepicker = new Calendar.DatePicker({
* trigger:'.calendar',
* maxDate : '2014-01-01',
* minDate : '2013-7-25',
* autoRender : true
* });
*
* @type {Date}
*/
minDate : {
},
/**
* 返回日期格式,如果不设置默认为 yyyy-mm-dd,时间选择为true时为 yyyy-mm-dd HH:MM:ss
*
* calendar.set('dateMask','yyyy-mm-dd');
*
* @type {String}
*/
dateMask: {
},
changeEvent:{
value:'accept'
},
hideEvent:{
value:'accept clear'
},
/**
* 日历对象,可以进行更多的操作,参看{@link BUI.Calendar.Calendar}
* @type {BUI.Calendar.Calendar}
*/
calendar:{
},
/**
* 默认选中的日期
* @type {Date}
*/
selectedDate: {
value: new Date(new Date().setSeconds(0))
}
}
},{
xclass : 'datepicker',
priority : 0
});
module.exports = datepicker;
});
define("bui/select", ["bui/common","jquery","bui/picker","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 选择框命名空间入口文件
* @ignore
*/
var BUI = require("bui/common"),
Select = BUI.namespace('Select');
BUI.mix(Select,{
Select : require("bui/select/select"),
Combox : require("bui/select/combox"),
Suggest: require("bui/select/suggest")
});
module.exports = Select;
});
define("bui/select/select", ["jquery","bui/common","bui/picker","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 选择控件
* @author dxq613@gmail.com
* @ignore
*/
'use strict';
var $ = require("jquery"),
BUI = require("bui/common"),
ListPicker = require("bui/picker").ListPicker,
PREFIX = BUI.prefix;
function formatItems(items){
if($.isPlainObject(items)){
var tmp = [];
BUI.each(items,function(v,n){
tmp.push({value : n,text : v});
});
return tmp;
}
var rst = [];
BUI.each(items,function(item,index){
if(BUI.isString(item)){
rst.push({value : item,text:item});
}else{
rst.push(item);
}
});
return rst;
}
var Component = BUI.Component,
Picker = ListPicker,
CLS_INPUT = PREFIX + 'select-input',
/**
* 选择控件
* xclass:'select'
*
* BUI.use('bui/select',function(Select){
*
* var items = [
* {text:'选项1',value:'a'},
* {text:'选项2',value:'b'},
* {text:'选项3',value:'c'}
* ],
* select = new Select.Select({
* render:'#s1',
* valueField:'#hide',
* //multipleSelect: true, //是否多选
* items:items
* });
* select.render();
* select.on('change', function(ev){
* //ev.text,ev.value,ev.item
* });
*
* });
*
* @class BUI.Select.Select
* @extends BUI.Component.Controller
*/
select = Component.Controller.extend({
//初始化
initializer:function(){
var _self = this,
multipleSelect = _self.get('multipleSelect'),
xclass,
picker = _self.get('picker'),
list;
if(!picker){
xclass = multipleSelect ? 'listbox' : 'simple-list';
list = _self.get('list') || {};
list = BUI.mix(list,{
xclass : xclass,
elCls:PREFIX + 'select-list',
store : _self.get('store'),
items : formatItems(_self.get('items'))/**/
});
picker = new Picker({
children:[
list
],
valueField : _self.get('valueField')
});
_self.set('picker',picker);
}else{
if(_self.get('valueField')){
picker.set('valueField',_self.get('valueField'));
}
}
if(multipleSelect){
picker.set('hideEvent','');
}
},
//渲染DOM以及选择器
renderUI : function(){
var _self = this,
picker = _self.get('picker'),
textEl = _self._getTextEl();
picker.set('trigger',_self.getTrigger());
picker.set('triggerEvent', _self.get('triggerEvent'));
picker.set('autoSetValue', _self.get('autoSetValue'));
picker.set('textField',textEl);
picker.render();
_self.set('list',picker.get('list'));
},
//绑定事件
bindUI : function(){
var _self = this,
picker = _self.get('picker'),
list = picker.get('list'),
store = list.get('store');
//选项发生改变时
picker.on('selectedchange',function(ev){
if(ev.item){
_self.fire('change',{text : ev.text,value : ev.value,item : ev.item});
}
});
if(_self.get('autoSetValue')){
list.on('itemsshow',function(){
_self._syncValue();
});
}
picker.on('show',function(){
if(_self.get('forceFit')){
picker.set('width',_self.get('el').outerWidth());
}
});
},
/**
* 是否包含元素
* @override
*/
containsElement : function(elem){
var _self = this,
picker = _self.get('picker');
return Component.Controller.prototype.containsElement.call(this,elem) || picker.containsElement(elem);
},
/**
* @protected
* 获取触发点
*/
getTrigger : function(){
return this.get('el');
},
//设置子项
_uiSetItems : function(items){
if(!items){
return;
}
var _self = this,
picker = _self.get('picker'),
list = picker.get('list');
list.set('items',formatItems(items));
_self._syncValue();
},
_syncValue : function(){
var _self = this,
picker = _self.get('picker'),
valueField = _self.get('valueField');
if(valueField){
picker.setSelectedValue($(valueField).val());
}
},
//设置Form表单中的名称
_uiSetName:function(v){
var _self = this,
textEl = _self._getTextEl();
if(v){
textEl.attr('name',v);
}
},
_uiSetWidth : function(v){
var _self = this;
if(v != null){
if(_self.get('inputForceFit')){
var textEl = _self._getTextEl(),
iconEl = _self.get('el').find('.x-icon'),
appendWidth = textEl.outerWidth() - textEl.width(),
width = v - iconEl.outerWidth() - appendWidth;
textEl.width(width);
}
if(_self.get('forceFit')){
var picker = _self.get('picker');
picker.set('width',v);
}
}
},
//禁用
_uiSetDisabled : function(v){
var _self = this,
picker = _self.get('picker'),
textEl = _self._getTextEl();
picker.set('disabled',v);
textEl && textEl.attr('disabled',v);
},
_getTextEl : function(){
var _self = this,
el = _self.get('el');
return el.is('input') ? el : el.find('input');
},
/**
* 析构函数
*/
destructor:function(){
var _self = this,
picker = _self.get('picker');
if(picker){
picker.destroy();
}
},
//获取List控件
_getList:function(){
var _self = this,
picker = _self.get('picker'),
list = picker.get('list');
return list;
},
/**
* 获取选中项的值,如果是多选则,返回的'1,2,3'形式的字符串
*
* var value = select.getSelectedValue();
*
* @return {String} 选中项的值
*/
getSelectedValue:function(){
return this.get('picker').getSelectedValue();
},
/**
* 设置选中的值
*
* select.setSelectedValue('1'); //单选模式下
* select.setSelectedValue('1,2,3'); //多选模式下
*
* @param {String} value 选中的值
*/
setSelectedValue : function(value){
var _self = this,
picker = _self.get('picker');
picker.setSelectedValue(value);
},
/**
* 获取选中项的文本,如果是多选则,返回的'text1,text2,text3'形式的字符串
*
* var value = select.getSelectedText();
*
* @return {String} 选中项的文本
*/
getSelectedText:function(){
return this.get('picker').getSelectedText();
}
},{
ATTRS :
{
/**
* 选择器,浮动出现,供用户选择
* @cfg {BUI.Picker.ListPicker} picker
*
* var columns = [
* {title : '表头1(30%)',dataIndex :'a', width:'30%'},
* {id: '123',title : '表头2(30%)',dataIndex :'b', width:'30%'},
* {title : '表头3(40%)',dataIndex : 'c',width:'40%'}
* ],
* data = [{a:'123',b:'选择文本1'},{a:'cdd',b:'选择文本2'},{a:'1333',b:'选择文本3',c:'eee',d:2}],
* grid = new Grid.SimpleGrid({
* idField : 'a', //设置作为key 的字段,放到valueField中
* columns : columns,
* textGetter: function(item){ //返回选中的文本
* return item.b;
* }
* }),
* picker = new Picker.ListPicker({
* width:300, //指定宽度
* children : [grid] //配置picker内的列表
* }),
* select = new Select.Select({
* render:'#s1',
* picker : picker,
* forceFit:false, //不强迫列表跟选择器宽度一致
* valueField:'#hide',
* items : data
* });
* select.render();
*
*/
/**
* 选择器,浮动出现,供用户选择
* @readOnly
* @type {BUI.Picker.ListPicker}
*/
picker:{
},
/**
* Picker中的列表
* * var list = select.get('list'); ** @readOnly * @type {BUI.List.SimpleList} */ list : { }, /** * 存放值得字段,一般是一个input[type='hidden'] ,用于存放选择框的值 * @cfg {Object} valueField */ /** * @ignore */ valueField : { }, /** * 数据缓冲类 *
* var store = new Store({
* url : 'data.json',
* autoLoad : true
* });
* var select = new Select({
* render : '#s',
* store : store//设置了store后,不要再设置items,会进行覆盖
* });
* select.render();
*
* @cfg {BUI.Data.Store} Store
*/
store : {
},
focusable:{
value:true
},
/**
* 是否跟valueField自动同步
* @type {Boolean}
*/
autoSetValue : {
value : true
},
/**
* 是否可以多选
* @cfg {Boolean} [multipleSelect=false]
*/
/**
* 是否可以多选
* @type {Boolean}
*/
multipleSelect:{
value:false
},
/**
* 内部的input是否跟随宽度的变化而变化
* @type {Object}
*/
inputForceFit : {
value : true
},
/**
* 控件的name,用于存放选中的文本,便于表单提交
* @cfg {Object} name
*/
/**
* 控件的name,便于表单提交
* @type {Object}
*/
name:{
},
/**
* 选项
* @cfg {Array} items
*
* BUI.use('bui/select',function(Select){
*
* var items = [
* {text:'选项1',value:'a'},
* {text:'选项2',value:'b'},
* {text:'选项3',value:'c'}
* ],
* select = new Select.Select({
* render:'#s1',
* valueField:'#hide',
* //multipleSelect: true, //是否多选
* items:items
* });
* select.render();
*
* });
*
*/
/**
* 选项
* @type {Array}
*/
items:{
sync:false
},
/**
* 标示选择完成后,显示文本的DOM节点的样式
* @type {String}
* @protected
* @default 'bui-select-input'
*/
inputCls:{
value:CLS_INPUT
},
/**
* 是否使选择列表跟选择框同等宽度
*
* picker = new Picker.ListPicker({
* width:300, //指定宽度
* children : [grid] //配置picker内的列表
* }),
* select = new Select.Select({
* render:'#s1',
* picker : picker,
* forceFit:false, //不强迫列表跟选择器宽度一致
* valueField:'#hide',
* items : data
* });
* select.render();
*
* @cfg {Boolean} [forceFit=true]
*/
forceFit : {
value : true
},
events : {
value : {
/**
* 选择值发生改变时
* @event
* @param {Object} e 事件对象
* @param {String} e.text 选中的文本
* @param {String} e.value 选中的value
* @param {Object} e.item 发生改变的选项
*/
'change' : false
}
},
/**
* 控件的默认模版
* @type {String}
* @default
* '<input type="text" readonly="readonly" class="bui-select-input"/><span class="x-icon x-icon-normal"><span class="bui-caret bui-caret-down"></span></span>'
*/
tpl : {
view:true,
value : ' '
},
/**
* 触发的事件
* @cfg {String} triggerEvent
* @default 'click'
*/
triggerEvent:{
value:'click'
}
}
},{
xclass : 'select'
});
module.exports = select;
});
define("bui/select/combox", ["jquery","bui/common","bui/picker","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 组合框可用于选择输入文本
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Select = require("bui/select/select"),
Tag = require("bui/select/tag"),
CLS_INPUT = BUI.prefix + 'combox-input';
/**
* 组合框 用于提示输入
* xclass:'combox'
*
* BUI.use('bui/select',function(Select){
*
* var select = new Select.Combox({
* render:'#c1',
* name:'combox',
* items:['选项1','选项2','选项3','选项4']
* });
* select.render();
* });
*
* @class BUI.Select.Combox
* @extends BUI.Select.Select
*/
var combox = Select.extend([Tag],{
renderUI : function(){
var _self = this,
picker = _self.get('picker');
picker.set('autoFocused',false);
},
_uiSetItems : function(v){
var _self = this;
for(var i = 0 ; i < v.length ; i++){
var item = v[i];
if(BUI.isString(item)){
v[i] = {value:item,text:item};
}
}
combox.superclass._uiSetItems.call(_self,v);
},
bindUI: function(){
var _self = this,
picker = _self.get('picker'),
list = picker.get('list'),
textField = picker.get('textField');
//修复手动清空textField里面的值,再选时不填充的bug
$(textField).on('keyup', function(ev){
var item = list.getSelected();
if(item){
list.clearItemStatus(item);
}
});
picker.on('show',function(){
list.clearSelected();
});
},
//覆写此方法
_uiSetValueField : function(){
},
/**
* @protected
* 获取触发点
*/
getTrigger : function(){
return this._getTextEl();
}
},{
ATTRS :
{
/*focusable : {
value : false
},*/
/**
* 控件的模版
* @type {String}
* @default
* '<input type="text" class="'+CLS_INPUT+'"/>'
*/
tpl:{
view:true,
value:''
},
/**
* 显示选择回的文本DOM节点的样式
* @type {String}
* @protected
* @default 'bui-combox-input'
*/
inputCls:{
value:CLS_INPUT
},
autoSetValue : {
value : false
}
}
},{
xclass:'combox'
});
module.exports = combox;
});
define("bui/select/tag", ["jquery","bui/common","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 输入、选择完毕后显示tag
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
List = require("bui/list"),
KeyCode = BUI.KeyCode,
WARN = 'warn';
function html_decode(str)
{
var s = "";
if (str.length == 0) return "";
s = str.replace(/>/g, ">");
s = s.replace(/{text}'
},
/**
* @private
* tag 的列表
* @type {Object}
*/
tagList : {
value : null
},
limit : {
value : null
},
forbitInput : {
value : false
},
tagPlaceholder : {
value : '输入标签'
},
tagFormatter : {
value : null
},
/**
* 默认的value分隔符,将值分割显示成tag
* @type {String}
*/
separator : {
value : ';'
}
};
BUI.augment(Tag,{
__renderUI : function(){
var _self = this,
showTag = _self.get('showTag'),
tagPlaceholder = _self.get('tagPlaceholder'),
tagInput = _self.getTagInput();
if(showTag && !tagInput.attr('placeholder')){
tagInput.attr('placeholder',tagPlaceholder);
_self.set('inputForceFit',false);
}
},
__bindUI : function(){
var _self = this,
showTag = _self.get('showTag'),
tagInput = _self.getTagInput();
if(showTag){
tagInput.on('keydown',function(ev){
if(!tagInput.val()){
var tagList = _self.get('tagList'),
last = tagList.getLastItem(),
picker = _self.get('picker');
if(ev.which == KeyCode.DELETE || ev.which == KeyCode.BACKSPACE){
if(tagList.hasStatus(last,WARN)){
_self._delTag(last);
}else{
tagList.setItemStatus(last,WARN,true);
}
picker.hide();
}else{
tagList.setItemStatus(last,WARN,false);
}
}
});
var handler;
function setTag(){
var tagList = _self.get('tagList'),
last = tagList.getLastItem();
if(last && tagList.hasStatus(last,WARN)){ //如果最后一项处于警告状态
tagList.setItemStatus(last,WARN,false);
}
var val = tagInput.val();
if(val){
_self._addTag(val);
}
}
if(!_self.get('forbitInput')){
tagInput.on('change',function(){
handler = setTimeout(function(){
setTag();
handler = null;
},50);
});
}
_self.on('change',function(ev){
setTimeout(function(){
if(handler){
clearTimeout(handler);
}
setTag();
});
});
}
},
__syncUI : function(){
var _self = this,
showTag = _self.get('showTag'),
valueField = _self.get('valueField');
if(showTag && valueField){
_self._setTags($(valueField).val());
}
},
//设置tags,初始化时处理
_setTags : function(value){
var _self = this,
tagList = _self.get('tagList'),
separator = _self.get('separator'),
formatter = _self.get('tagFormatter'),
values = value.split(separator);
if(!tagList){
tagList = _self._initTagList();
}
if(value){
BUI.each(values,function(val){
var text = val;
if(formatter){
text = formatter(text);
}
tagList.addItem({value : val,text : text});
});
}
},
//添加tag
_addTag : function(value){
value = html_decode(value);
var _self = this,
tagList = _self.get('tagList'),
tagInput = _self.getTagInput(),
limit = _self.get('limit'),
formatter = _self.get('tagFormatter'),
preItem = tagList.getItem(value);
if(limit){
if(tagList.getItemCount() >= limit){
return;
}
}
if(!preItem){
var text = value;
if(formatter){
text = formatter(text);
}
tagList.addItem({value : value,text : text});
_self._synTagsValue();
}else{
_self._blurItem(tagList,preItem);
}
tagInput.val('');
},
//提示用户选项已经存在
_blurItem : function(list,item){
list.setItemStatus(item,'active',true);
setTimeout(function(){
list.setItemStatus(item,'active',false);
},400);
},
//删除tag
_delTag : function(item){
var _self = this,
tagList = _self.get('tagList');
tagList.removeItem(item);
_self._synTagsValue();
},
/**
* 获取tag 列表的值
* @return {String} 列表对应的值
*/
getTagsValue : function(){
var _self = this,
tagList = _self.get('tagList'),
items = tagList.getItems(),
vals = [];
BUI.each(items,function(item){
vals.push(item.value);
});
return vals.join(_self.get('separator'));
},
//初始化tagList
_initTagList : function(){
var _self = this,
tagInput = _self.getTagInput(),
tagList = new List.SimpleList({
elBefore : tagInput,
itemTpl : _self.get('tagItemTpl'),
idField : 'value'
});
tagList.render();
_self._initTagEvent(tagList);
_self.set('tagList',tagList);
return tagList;
},
//初始化tag删除事件
_initTagEvent : function(list){
var _self = this;
list.on('itemclick',function(ev){
var sender = $(ev.domTarget);
if(sender.is('button')){
_self._delTag(ev.item);
}
});
},
/**
* 获取输入的文本框
* @protected
* @return {jQuery} 输入框
*/
getTagInput : function(){
var _self = this,
el = _self.get('el');
return el.is('input') ? el : el.find('input');
},
_synTagsValue : function(){
var _self = this,
valueEl = _self.get('valueField');
valueEl && $(valueEl).val(_self.getTagsValue());
}
});
module.exports = Tag;
});
define("bui/select/suggest", ["jquery","bui/common","bui/picker","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 组合框可用于选择输入文本
* @ignore
*/
'use strict';
var $ = require("jquery"),
BUI = require("bui/common"),
Combox = require("bui/select/combox"),
TIMER_DELAY = 200,
EMPTY = '';
/**
* 组合框 用于提示输入
* xclass:'suggest'
* ** 简单使用静态数据 **
*
* BUI.use('bui/select',function (Select) {
*
* var suggest = new Select.Suggest({
* render:'#c2',
* name:'suggest', //形成输入框的name
* data:['1222224','234445','122','1111111']
* });
* suggest.render();
*
* });
*
* ** 查询服务器数据 **
*
* BUI.use('bui/select',function(Select){
*
* var suggest = new Select.Suggest({
* render:'#s1',
* name:'suggest',
* url:'server-data.php'
* });
* suggest.render();
*
* });
*
* @class BUI.Select.Suggest
* @extends BUI.Select.Combox
*/
var suggest = Combox.extend({
bindUI : function(){
var _self = this,
textEl = _self.get('el').find('input'),
triggerEvent = (_self.get('triggerEvent') === 'keyup') ? 'keyup' : 'keyup click';
//监听 keyup 事件
textEl.on(triggerEvent, function(){
_self._start();
});
},
//启动计时器,开始监听用户输入
_start:function(){
var _self = this;
_self._timer = _self.later(function(){
_self._updateContent();
// _self._timer = _self.later(arguments.callee, TIMER_DELAY);
}, TIMER_DELAY);
},
//更新提示层的数据
_updateContent:function(){
var _self = this,
isStatic = _self.get('data'),
textEl = _self.get('el').find('input'),
text;
//检测是否需要更新。注意:加入空格也算有变化
if (!isStatic && (textEl.val() === _self.get('query'))) {
return;
}
_self.set('query', textEl.val());
text = textEl.val();
//输入为空时,直接返回
if (!isStatic && !text) {
/* _self.set('items',EMPTY_ARRAY);
picker.hide();*/
return;
}
//3种加载方式选择
var cacheable = _self.get('cacheable'),
store = _self.get('store'),
url = _self.get('url'),
data = _self.get('data');
if (cacheable && (url || store)) {
var dataCache = _self.get('dataCache');
if (dataCache[text] !== undefined) {
//从缓存读取
//BUI.log('use cache');
_self._handleResponse(dataCache[text]);
}else{
//请求服务器数据
//BUI.log('no cache, data from server');
_self._requestData();
}
}else if (url || store) {
//从服务器获取数据
//BUI.log('no cache, data always from server');
_self._requestData();
}else if (data) {
//使用静态数据源
//BUI.log('use static datasource');
_self._handleResponse(data,true);
}
},
//如果存在数据源
_getStore : function(){
var _self = this,
picker = _self.get('picker'),
list = picker.get('list');
if(list){
return list.get('store');
}
},
//通过 script 元素异步加载数据
_requestData:function(){
var _self = this,
textEl = _self.get('el').find('input'),
callback = _self.get('callback'),
store = _self.get('store'),
param = {};
param[textEl.attr('name')] = textEl.val();
if(store){
param.start = 0; //回滚到第一页
store.load(param,callback);
}else{
$.ajax({
url:_self.get('url'),
type:'post',
dataType:_self.get('dataType'),
data:param,
success:function(data){
_self._handleResponse(data);
if(callback){
callback(data);
}
}
});
}
},
//处理获取的数据
_handleResponse:function(data,filter){
var _self = this,
items = filter ? _self._getFilterItems(data) : data;
_self.set('items',items);
if(_self.get('cacheable')){
_self.get('dataCache')[_self.get('query')] = items;
}
},
//如果列表记录是对象获取显示的文本
_getItemText : function(item){
var _self = this,
picker = _self.get('picker'),
list = picker.get('list');
if(list){
return list.getItemText(item);
}
return '';
},
//获取过滤的文本
_getFilterItems:function(data){
var _self = this,
result = [],
textEl = _self.get('el').find('input'),
text = textEl.val(),
isStatic = _self.get('data');
data = data || [];
/**
* @private
* @ignore
*/
function push(str,item){
if(BUI.isString(item)){
result.push(str);
}else{
result.push(item);
}
}
BUI.each(data, function(item){
var str = BUI.isString(item) ? item : _self._getItemText(item);
if(isStatic){
if(str.indexOf($.trim(text)) !== -1){
push(str,item);
}
}else{
push(str,item);
}
});
return result;
},
/**
* 延迟执行指定函数 fn
* @protected
* @return {Object} 操作定时器的对象
*/
later:function (fn, when, periodic) {
when = when || 0;
var r = periodic ? setInterval(fn, when) : setTimeout(fn, when);
return {
id:r,
interval:periodic,
cancel:function () {
if (this.interval) {
clearInterval(r);
} else {
clearTimeout(r);
}
}
};
}
},{
ATTRS :
{
/**
* 用于显示提示的数据源
*
* var suggest = new Select.Suggest({
* render:'#c2',
* name:'suggest', //形成输入框的name
* data:['1222224','234445','122','1111111']
* });
*
* @cfg {Array} data
*/
/**
* 用于显示提示的数据源
* @type {Array}
*/
data:{
value : null
},
/**
* 输入框的值
* @type {String}
* @private
*/
query:{
value : EMPTY
},
/**
* 是否允许缓存
* @cfg {Boolean} cacheable
*/
/**
* 是否允许缓存
* @type {Boolean}
*/
cacheable:{
value:false
},
/**
* 缓存的数据
* @private
*/
dataCache:{
shared:false,
value:{}
},
/**
* 请求返回的数据格式默认为'jsonp'
*
* var suggest = new Select.Suggest({
* render:'#s1',
* name:'suggest',
* dataType : 'json',
* url:'server-data.php'
* });
*
* @cfg {Object} [dataType = 'jsonp']
*/
dataType : {
value : 'jsonp'
},
/**
* 请求数据的url
*
* var suggest = new Select.Suggest({
* render:'#s1',
* name:'suggest',
* dataType : 'json',
* url:'server-data.php'
* });
*
* @cfg {String} url
*/
url : {
},
/**
* 请求完数据的回调函数
*
* var suggest = new Select.Suggest({
* render:'#s1',
* name:'suggest',
* dataType : 'json',
* callback : function(data){
* //do something
* },
* url:'server-data.php'
* });
*
* @type {Function}
*/
callback : {
},
/**
* 触发的事件
* @cfg {String} triggerEvent
* @default 'click'
*/
triggerEvent:{
valueFn:function(){
if(this.get('data')){
return 'click';
}
return 'keyup';
}
},
/**
* suggest不提供自动设置选中文本功能
* @type {Boolean}
*/
autoSetValue:{
value:false
}
}
},{
xclass:'suggest'
});
module.exports = suggest;
});
define("bui/form", ["bui/common","jquery","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview form 命名空间入口
* @ignore
*/
var BUI = require("bui/common"),
Form = BUI.namespace('Form'),
Tips = require("bui/form/tips");
BUI.mix(Form, {
Tips : Tips,
TipItem : Tips.Item,
FieldContainer : require("bui/form/fieldcontainer"),
Form : require("bui/form/form"),
Row : require("bui/form/row"),
Group : require("bui/form/fieldgroup"),
HForm : require("bui/form/hform"),
Rules : require("bui/form/rules"),
Field : require("bui/form/field"),
FieldGroup : require("bui/form/fieldgroup")
});
module.exports = Form;
});
define("bui/form/tips", ["jquery","bui/common","bui/overlay"], function(require, exports, module){
/**
* @fileOverview 输入提示信息
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
prefix = BUI.prefix,
Overlay = require("bui/overlay").Overlay,
FIELD_TIP = 'data-tip',
CLS_TIP_CONTAINER = prefix + 'form-tip-container';
/**
* 表单提示信息类
* xclass:'form-tip'
* @class BUI.Form.TipItem
* @extends BUI.Overlay.Overlay
*/
var tipItem = Overlay.extend(
{
initializer : function(){
var _self = this,
render = _self.get('render');
if(!render){
var parent = $(_self.get('trigger')).parent();
_self.set('render',parent);
}
},
renderUI : function(){
var _self = this;
_self.resetVisible();
},
/**
* 重置是否显示
*/
resetVisible : function(){
var _self = this,
triggerEl = $(_self.get('trigger'));
if(triggerEl.val()){//如果默认有文本则不显示,否则显示
_self.set('visible',false);
}else{
_self.set('align',{
node:$(_self.get('trigger')),
points: ['cl','cl']
});
_self.set('visible',true);
}
},
bindUI : function(){
var _self = this,
triggerEl = $(_self.get('trigger'));
_self.get('el').on('click',function(){
_self.hide();
triggerEl.focus();
});
triggerEl.on('click focus',function(){
_self.hide();
});
triggerEl.on('blur',function(){
_self.resetVisible();
});
}
},{
ATTRS :
{
/**
* 提示的输入框
* @cfg {String|HTMLElement|jQuery} trigger
*/
/**
* 提示的输入框
* @type {String|HTMLElement|jQuery}
*/
trigger:{
},
/**
* 提示文本
* @cfg {String} text
*/
/**
* 提示文本
* @type {String}
*/
text : {
},
/**
* 提示文本上显示的icon样式
* @cfg {String} iconCls
* iconCls : icon-ok
*/
/**
* 提示文本上显示的icon样式
* @type {String}
* iconCls : icon-ok
*/
iconCls:{
},
/**
* 默认的模版
* @type {String}
* @default '{text}'
*/
tpl:{
value:'{text}'
}
}
},{
xclass : 'form-tip'
});
/**
* 表单提示信息的管理类
* @class BUI.Form.Tips
* @extends BUI.Base
*/
var Tips = function(config){
if (this.constructor !== Tips){
return new Tips(config);
}
Tips.superclass.constructor.call(this,config);
this._init();
};
Tips.ATTRS =
{
/**
* 表单的选择器
* @cfg {String|HTMLElement|jQuery} form
*/
/**
* 表单的选择器
* @type {String|HTMLElement|jQuery}
*/
form : {
},
/**
* 表单提示项对象 {@link BUI.Form.TipItem}
* @readOnly
* @type {Array}
*/
items : {
valueFn:function(){
return [];
}
}
};
BUI.extend(Tips,BUI.Base);
BUI.augment(Tips,{
_init : function(){
var _self = this,
form = $(_self.get('form'));
if(form.length){
BUI.each($.makeArray(form[0].elements),function(elem){
var tipConfig = $(elem).attr(FIELD_TIP);
if(tipConfig){
_self._initFormElement(elem,$.parseJSON(tipConfig));
}
});
form.addClass(CLS_TIP_CONTAINER);
}
},
_initFormElement : function(element,config){
if(config){
config.trigger = element;
//config.render = this.get('form');
}
var _self = this,
items = _self.get('items'),
item = new tipItem(config);
items.push(item);
},
/**
* 获取提示项
* @param {String} name 字段的名称
* @return {BUI.Form.TipItem} 提示项
*/
getItem : function(name){
var _self = this,
items = _self.get('items'),
result = null;
BUI.each(items,function(item){
if($(item.get('trigger')).attr('name') === name){
result = item;
return false;
}
});
return result;
},
/**
* 重置所有提示的可视状态
*/
resetVisible : function(){
var _self = this,
items = _self.get('items');
BUI.each(items,function(item){
item.resetVisible();
});
},
/**
* 生成 表单提示
*/
render:function(){
var _self = this,
items = _self.get('items');
BUI.each(items,function(item){
item.render();
});
},
/**
* 删除所有提示
*/
destroy:function(){
var _self = this,
items = _self.get(items);
BUI.each(items,function(item){
item.destroy();
});
}
});
Tips.Item = tipItem;
module.exports = Tips;
});
define("bui/form/fieldcontainer", ["jquery","bui/common","bui/overlay","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 表单字段的容器扩展
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Field = require("bui/form/field"),
GroupValid = require("bui/form/groupvalid"),
PREFIX = BUI.prefix;
var FIELD_XCLASS = 'form-field',
CLS_FIELD = PREFIX + FIELD_XCLASS,
CLS_GROUP = PREFIX + 'form-group',
FIELD_TAGS = 'input,select,textarea';
function isField(node){
return node.is(FIELD_TAGS);
}
/**
* 获取节点需要封装的子节点
* @ignore
*/
function getDecorateChilds(node,srcNode){
if(node != srcNode){
if(isField(node)){
return [node];
}
var cls = node.attr('class');
if(cls && (cls.indexOf(CLS_GROUP) !== -1 || cls.indexOf(CLS_FIELD) !== -1)){
return [node];
}
}
var rst = [],
children = node.children();
BUI.each(children,function(subNode){
rst = rst.concat(getDecorateChilds($(subNode),srcNode));
});
return rst;
}
var containerView = BUI.Component.View.extend([GroupValid.View]);
/**
* 表单字段容器的扩展类
* @class BUI.Form.FieldContainer
* @extends BUI.Component.Controller
* @mixins BUI.Form.GroupValid
*/
var container = BUI.Component.Controller.extend([GroupValid],
{
//同步数据
syncUI : function(){
var _self = this,
fields = _self.getFields(),
validators = _self.get('validators');
BUI.each(fields,function(field){
var name = field.get('name');
if(validators[name]){
field.set('validator',validators[name]);
}
});
BUI.each(validators,function(item,key){
//按照ID查找
if(key.indexOf('#') == 0){
var id = key.replace('#',''),
child = _self.getChild(id,true);
if(child){
child.set('validator',item);
}
}
});
},
/**
* 获取封装的子控件节点
* @protected
* @override
*/
getDecorateElments : function(){
var _self = this,
el = _self.get('el');
var items = getDecorateChilds(el,el);
return items;
},
/**
* 根据子节点获取对应的子控件 xclass
* @protected
* @override
*/
findXClassByNode : function(childNode, ignoreError){
if(childNode.attr('type') === 'checkbox'){
return FIELD_XCLASS + '-checkbox';
}
if(childNode.attr('type') === 'radio'){
return FIELD_XCLASS + '-radio';
}
if(childNode.attr('type') === 'number'){
return FIELD_XCLASS + '-number';
}
if(childNode.hasClass('calendar')){
return FIELD_XCLASS + '-date';
}
if(childNode[0].tagName == "SELECT"){
return FIELD_XCLASS + '-select';
}
if(isField(childNode)){
return FIELD_XCLASS;
}
return BUI.Component.Controller.prototype.findXClassByNode.call(this,childNode, ignoreError);
},
/**
* 获取表单编辑的对象
* @return {Object} 编辑的对象
*/
getRecord : function(){
var _self = this,
rst = {},
fields = _self.getFields();
BUI.each(fields,function(field){
var name = field.get('name'),
value = _self._getFieldValue(field);
if(!rst[name]){//没有值,直接赋值
rst[name] = value;
}else if(BUI.isArray(rst[name]) && value != null){//已经存在值,并且是数组,加入数组
rst[name].push(value);
}else if(value != null){ //否则封装成数组,并加入数组
var arr = [rst[name]]
arr.push(value);
rst[name] = arr;
}
});
return rst;
},
/**
* 获取表单字段
* @return {Array} 表单字段
*/
getFields : function(name){
var _self = this,
rst = [],
children = _self.get('children');
BUI.each(children,function(item){
if(item instanceof Field){
if(!name || item.get('name') == name){
rst.push(item);
}
}else if(item.getFields){
rst = rst.concat(item.getFields(name));
}
});
return rst;
},
/**
* 根据name 获取表单字段
* @param {String} name 字段名
* @return {BUI.Form.Field} 表单字段或者 null
*/
getField : function(name){
var _self = this,
fields = _self.getFields(),
rst = null;
BUI.each(fields,function(field){
if(field.get('name') === name){
rst = field;
return false;
}
});
return rst;
},
/**
* 根据索引获取字段的name
* @param {Number} index 字段的索引
* @return {String} 字段名称
*/
getFieldAt : function (index) {
return this.getFields()[index];
},
/**
* 根据字段名
* @param {String} name 字段名
* @param {*} value 字段值
*/
setFieldValue : function(name,value){
var _self = this,
fields = _self.getFields(name);
BUI.each(fields,function(field){
_self._setFieldValue(field,value);
});
},
//设置字段域的值
_setFieldValue : function(field,value){
//如果字段不可用,则不能设置值
if(field.get('disabled')){
return;
}
//如果是可勾选的
if(field instanceof Field.Check){
var fieldValue = field.get('value');
if(value && (fieldValue === value || (BUI.isArray(value) && BUI.Array.contains(fieldValue,value)))){
field.set('checked',true);
}else{
field.set('checked',false);
}
}else{
if(value == null){
value = '';
}
field.clearErrors(true);//清理错误
field.set('value',value);
}
},
/**
* 获取字段值,不存在字段时返回null,多个同名字段时,checkbox返回一个数组
* @param {String} name 字段名
* @return {*} 字段值
*/
getFieldValue : function(name){
var _self = this,
fields = _self.getFields(name),
rst = [];
BUI.each(fields,function(field){
var value = _self._getFieldValue(field);
if(value){
rst.push(value);
}
});
if(rst.length === 0){
return null;
}
if(rst.length === 1){
return rst[0]
}
return rst;
},
//获取字段域的值
_getFieldValue : function(field){
if(!(field instanceof Field.Check) || field.get('checked')){
return field.get('value');
}
return null;
},
/**
* 清除所有表单域的值
*/
clearFields : function(){
this.clearErrors(true);
this.setRecord({})
},
/**
* 设置表单编辑的对象
* @param {Object} record 编辑的对象
*/
setRecord : function(record){
var _self = this,
fields = _self.getFields();
BUI.each(fields,function(field){
var name = field.get('name');
_self._setFieldValue(field,record[name]);
});
},
/**
* 更新表单编辑的对象
* @param {Object} record 编辑的对象
*/
updateRecord : function(record){
var _self = this,
fields = _self.getFields();
BUI.each(fields,function(field){
var name = field.get('name');
if(record.hasOwnProperty(name)){
_self._setFieldValue(field,record[name]);
}
});
},
/**
* 设置控件获取焦点,设置第一个子控件获取焦点
*/
focus : function(){
var _self = this,
fields = _self.getFields(),
firstField = fields[0];
if(firstField){
firstField.focus();
}
},
//禁用控件
_uiSetDisabled : function(v){
var _self = this,
children = _self.get('children');
BUI.each(children,function(item){
item.set('disabled',v);
});
}
},
{
ATTRS : {
/**
* 表单的数据记录,以键值对的形式存在
* @type {Object}
*/
record : {
setter : function(v){
this.setRecord(v);
},
getter : function(){
return this.getRecord();
}
},
/**
* 内部元素的验证函数,可以使用2中选择器
* * { * property : 'children', * dataType : 'json' * } ** @type {Object} */ defaultLoaderCfg : { value : { property : 'children', dataType : 'json' } }, disabled : { sync : false }, isDecorateChild : { value : true }, xview : { value : containerView } } },{ xclass : 'form-field-container' } ); container.View = containerView; module.exports = container; }); define("bui/form/field", ["bui/common","jquery","bui/overlay","bui/list","bui/data"], function(require, exports, module){ /** * @fileOverview 表单域的入口文件 * @ignore */ var BUI = require("bui/common"), Field = require("bui/form/fields/base"); BUI.mix(Field, { Text : require("bui/form/fields/text"), Date : require("bui/form/fields/date"), Select : require("bui/form/fields/select"), Hidden : require("bui/form/fields/hidden"), Number : require("bui/form/fields/number"), Check : require("bui/form/fields/check"), Radio : require("bui/form/fields/radio"), Checkbox : require("bui/form/fields/checkbox"), Plain : require("bui/form/fields/plain"), List : require("bui/form/fields/list"), TextArea : require("bui/form/fields/textarea"), Uploader : require("bui/form/fields/uploader"), CheckList : require("bui/form/fields/checklist"), RadioList : require("bui/form/fields/radiolist") }); module.exports = Field; }); define("bui/form/fields/base", ["jquery","bui/common","bui/overlay"], function(require, exports, module){ /** * @fileOverview 表单元素 * @ignore */ var $ = require("jquery"), BUI = require("bui/common"), Component = BUI.Component, TipItem = require("bui/form/tips").Item, Valid = require("bui/form/valid"), Remote = require("bui/form/remote"), CLS_FIELD_ERROR = BUI.prefix + 'form-field-error', CLS_TIP_CONTAINER = 'bui-form-tip-container', DATA_ERROR = 'data-error'; /** * 字段视图类 * @class BUI.Form.FieldView * @private */ var fieldView = Component.View.extend([Remote.View,Valid.View],{ //渲染DOM renderUI : function(){ var _self = this, control = _self.get('control'); if(!control){ var controlTpl = _self.get('controlTpl'), container = _self.getControlContainer(); if(controlTpl){ var control = $(controlTpl).appendTo(container); _self.set('control',control); } }else{ //var controlContainer = control.parent(); _self.set('controlContainer',control.parent()); } }, /** * 清理显示的错误信息 * @protected */ clearErrors : function(){ var _self = this, msgEl = _self.get('msgEl'); if(msgEl){ msgEl.remove(); _self.set('msgEl',null); } _self.get('el').removeClass(CLS_FIELD_ERROR); }, /** * 显示错误信息 * @param {String} msg 错误信息 * @protected */ showError : function(msg,errorTpl){ var _self = this, control = _self.get('control'), errorMsg = BUI.substitute(errorTpl,{error : msg}), el = $(errorMsg); //_self.clearErrorMsg(); el.appendTo(control.parent()); _self.set('msgEl',el); _self.get('el').addClass(CLS_FIELD_ERROR); }, /** * @internal 获取控件的容器 * @return {jQuery} 控件容器 */ getControlContainer : function(){ var _self = this, el = _self.get('el'), controlContainer = _self.get('controlContainer'); if(controlContainer){ if(BUI.isString(controlContainer)){ controlContainer = el.find(controlContainer); } } return (controlContainer && controlContainer.length) ? controlContainer : el; }, /** * 获取显示加载状态的容器 * @protected * @override * @return {jQuery} 加载状态的容器 */ getLoadingContainer : function () { return this.getControlContainer(); }, //设置名称 _uiSetName : function(v){ var _self = this; _self.get('control').attr('name',v); } }, { ATTRS : { error:{}, controlContainer : {}, msgEl: {}, control : {} } }); /** * 表单字段基类 * @class BUI.Form.Field * @mixins BUI.Form.Remote * @mixins BUI.Form.Valid * @extends BUI.Component.Controller */ var field = Component.Controller.extend([Remote,Valid],{ isField : true, initializer : function(){ var _self = this; _self.on('afterRenderUI',function(){ var tip = _self.get('tip'); if(tip){ var trigger = _self.getTipTigger(); trigger && trigger.parent().addClass(CLS_TIP_CONTAINER); tip.trigger = trigger; tip.autoRender = true; tip = new TipItem(tip); _self.set('tip',tip); } }); }, //绑定事件 bindUI : function(){ var _self = this, validEvent = _self.get('validEvent'), changeEvent = _self.get('changeEvent'), firstValidEvent = _self.get('firstValidEvent'), innerControl = _self.getInnerControl(); //选择框只使用 select事件 if(innerControl.is('select')){ validEvent = 'change'; } //验证事件 innerControl.on(validEvent,function(){ var value = _self.getControlValue(innerControl); _self.validControl(value); }); if(firstValidEvent){ //未发生验证时,首次获取焦点/丢失焦点/点击,进行验证 innerControl.on(firstValidEvent,function(){ if(!_self.get('hasValid')){ var value = _self.getControlValue(innerControl); _self.validControl(value); } }); } //本来是监听控件的change事件,但是,如果控件还未触发change,但是通过get('value')来取值,则会出现错误, //所以当通过验证时,即触发改变事件 _self.on(changeEvent,function(){ _self.onValid(); }); _self.on('remotecomplete',function (ev) { _self._setError(ev.error); }); }, /** * 验证成功后执行的操作 * @protected */ onValid : function(){ var _self = this, value = _self.getControlValue(); value = _self.parseValue(value); if(!_self.isCurrentValue(value)){ _self.setInternal('value',value); _self.onChange(); } }, onChange : function () { this.fire('change'); }, /** * @protected * 是否当前值,主要用于日期等特殊值的比较,不能用 == 进行比较 * @param {*} value 进行比较的值 * @return {Boolean} 是否当前值 */ isCurrentValue : function (value) { return value == this.get('value'); }, //清理错误信息 _clearError : function(){ this.set('error',null); this.get('view').clearErrors(); }, //设置错误信息 _setError : function(msg){ this.set('error',msg); this.showErrors(); }, /** * 获取内部表单元素的值 * @protected * @param {jQuery} [innerControl] 内部表单元素 * @return {String|Boolean} 表单元素的值,checkbox,radio的返回值为 true,false */ getControlValue : function(innerControl){ var _self = this; innerControl = innerControl || _self.getInnerControl(); return innerControl.val(); }, /** * @protected * 获取内部控件的容器 */ getControlContainer : function(){ return this.get('view').getControlContainer(); }, /** * 获取异步验证的参数,对于表单字段域而言,是{[name] : [value]} * @protected * @override * @return {Object} 参数键值对 */ getRemoteParams : function () { var _self = this, rst = {}; rst[_self.get('name')] = _self.getControlValue(); return rst; }, /** * 设置字段的值 * @protected * @param {*} value 字段值 */ setControlValue : function(value){ var _self = this, innerControl = _self.getInnerControl(); innerControl.val(value); }, /** * 将字符串等格式转换成 * @protected * @param {String} value 原始数据 * @return {*} 该字段指定的类型 */ parseValue : function(value){ return value; }, valid : function(){ var _self = this; _self.validControl(); }, /** * 验证控件内容 * @return {Boolean} 是否通过验证 */ validControl : function(value){ var _self = this, errorMsg; value = value || _self.getControlValue(), preError = _self.get('error'); errorMsg = _self.getValidError(value); _self.setInternal('hasValid',true); if (errorMsg) { _self._setError(errorMsg); _self.fire('error', {msg:errorMsg, value:value}); if(preError !== errorMsg){//验证错误信息改变,说明验证改变 _self.fire('validchange',{ valid : false }); } } else { _self._clearError(); _self.fire('valid'); if(preError){//如果以前存在错误,那么验证结果改变 _self.fire('validchange',{ valid : true }); } } return !errorMsg; }, /** * 字段获得焦点 */ focus : function(){ this.getInnerControl().focus(); }, /** * 字段发生改变 */ change : function(){ var control = this.getInnerControl(); control.change(); }, /** * 字段丢失焦点 */ blur : function(){ this.getInnerControl().blur(); }, /** * 是否通过验证,如果未发生过校验,则进行校验,否则不进行校验,直接根据已校验的结果判断。 * @return {Boolean} 是否通过验证 */ isValid : function(){ var _self = this; if(!_self.get('hasValid')){ _self.validControl(); } return !_self.get('error'); }, /** * 获取验证出错信息 * @return {String} 出错信息 */ getError : function(){ return this.get('error'); }, /** * 获取验证出错信息集合 * @return {Array} 出错信息集合 */ getErrors : function(){ var error = this.getError(); if(error){ return [error]; } return []; }, /** * 清理出错信息,回滚到未出错状态 * @param {Boolean} reset 清除错误时,是否回滚上次正确的值 */ clearErrors : function(reset){ var _self = this; _self._clearError(); if(reset && _self.getControlValue()!= _self.get('value')){ _self.setControlValue(_self.get('value')); } }, /** * 获取内部的表单元素或者内部控件 * @protected * @return {jQuery|BUI.Component.Controller} */ getInnerControl : function(){ return this.get('view').get('control'); }, /** * 提示信息按照此元素对齐 * @protected * @return {HTMLElement} */ getTipTigger : function(){ return this.getInnerControl(); }, //析构函数 destructor : function(){ var _self = this, tip = _self.get('tip'); if(tip && tip.destroy){ tip.destroy(); } }, /** * @protected * 设置内部元素宽度 */ setInnerWidth : function(width){ var _self = this, innerControl = _self.getInnerControl(), siblings = innerControl.siblings(), appendWidth = innerControl.outerWidth() - innerControl.width(); BUI.each(siblings,function(dom){ appendWidth += $(dom).outerWidth(); }); innerControl.width(width - appendWidth); }, //重置 提示信息是否可见 _resetTip :function(){ var _self = this, tip = _self.get('tip'); if(tip){ tip.resetVisible(); } }, /** * 重置显示提示信息 * field.resetTip(); */ resetTip : function(){ this._resetTip(); }, //设置值 _uiSetValue : function(v){ var _self = this; //v = v ? v.toString() : ''; _self.setControlValue(v); if(_self.get('rendered')){ _self.validControl(); _self.onChange(); } _self._resetTip(); }, //禁用控件 _uiSetDisabled : function(v){ var _self = this, innerControl = _self.getInnerControl(), children = _self.get('children'); innerControl.attr('disabled',v); if(_self.get('rendered')){ if(v){//控件不可用,清除错误 _self.clearErrors(); } if(!v){//控件可用,执行重新验证 _self.valid(); } } BUI.each(children,function(child){ child.set('disabled',v); }); }, _uiSetWidth : function(v){ var _self = this; if(v != null && _self.get('forceFit')){ _self.setInnerWidth(v); } } },{ ATTRS : { /** * 是否发生过校验,初始值为空时,未进行赋值,不进行校验 * @type {Boolean} */ hasValid : { value : false }, /** * 内部元素是否根据控件宽度调整宽度 * @type {Boolean} */ forceFit : { value : false }, /** * 是否显示提示信息 * @type {Object} */ tip : { }, /** * 表单元素或者控件内容改变的事件 * @type {String} */ changeEvent : { value : 'valid' }, /** * 未发生验证时,首次获取/丢失焦点,进行验证 */ firstValidEvent : { value : 'blur' }, /** * 表单元素或者控件触发此事件时,触发验证 * @type {String} */ validEvent : { value : 'keyup change' }, /** * 字段的name值 * @type {Object} */ name : { view :true }, /** * 是否显示错误 * @type {Boolean} */ showError : { view : true, value : true }, /** * 字段的值,类型根据字段类型决定 * @cfg {*} value */ value : { view : true }, /** * 标题 * @type {String} */ label : { }, /** * 控件容器,如果为空直接添加在控件容器上 * @type {String|HTMLElement} */ controlContainer : { view : true }, /** * 内部表单元素的控件 * @protected * @type {jQuery} */ control : { view : true }, /** * 内部表单元素的容器 * @type {String} */ controlTpl : { view : true, value : '' }, events: { value : { /** * 未通过验证 * @event */ error : false, /** * 通过验证 * @event */ valid : false, /** * @event * 值改变,仅当通过验证时触发 */ change : true, /** * @event * 验证改变 * @param {Object} e 事件对象 * @param {Object} e.target 触发事件的对象 * @param {Boolean} e.valid 是否通过验证 */ validchange : true } }, tpl: { value : '' }, xview : { value : fieldView } }, PARSER : { control : function(el){ var control = el.find('input,select,textarea'); if(control.length){ return control; } return el; }, disabled : function(el){ return !!el.attr('disabled'); }, value : function(el){ var _self = this, selector = 'select,input,textarea', value = _self.get('value'); if(!value){ if(el.is(selector)){ value = el.val(); if(!value && el.is('select')){ value = el.attr('value'); } }else{ value = el.find(selector).val(); } } return value; }, name : function(el){ var _self = this, selector = 'select,input,textarea', name = _self.get('name'); if(!name){ if(el.is(selector)){ name = el.attr('name'); }else{ name = el.find(selector).attr('name'); } } return name; } } },{ xclass:'form-field' }); field.View = fieldView; module.exports = field; }); define("bui/form/valid", ["bui/common","jquery"], function(require, exports, module){ /** * @fileOverview 表单验证 * @ignore */ var BUI = require("bui/common"), Rules = require("bui/form/rules"); /** * @class BUI.Form.ValidView * @private * 对控件内的字段域进行验证的视图 */ var ValidView = function(){ }; ValidView.prototype = { /** * 获取错误信息的容器 * @protected * @return {jQuery} */ getErrorsContainer : function(){ var _self = this, errorContainer = _self.get('errorContainer'); if(errorContainer){ if(BUI.isString(errorContainer)){ return _self.get('el').find(errorContainer); } return errorContainer; } return _self.getContentElement(); }, /** * 显示错误 */ showErrors : function(errors){ var _self = this, errorsContainer = _self.getErrorsContainer(), errorTpl = _self.get('errorTpl'); _self.clearErrors(); if(!_self.get('showError')){ return ; } //如果仅显示第一条错误记录 if(_self.get('showOneError')){ if(errors && errors.length){ _self.showError(errors[0],errorTpl,errorsContainer); } return ; } BUI.each(errors,function(error){ if(error){ _self.showError(error,errorTpl,errorsContainer); } }); }, /** * 显示一条错误 * @protected * @template * @param {String} msg 错误信息 */ showError : function(msg,errorTpl,container){ }, /** * @protected * @template * 清除错误 */ clearErrors : function(){ } }; /** * 对控件内的字段域进行验证 * @class BUI.Form.Valid */ var Valid = function(){ }; Valid.ATTRS = { /** * 控件固有的验证规则,例如,日期字段域,有的date类型的验证 * @protected * @type {Object} */ defaultRules : { value : {} }, /** * 控件固有的验证出错信息,例如,日期字段域,不是有效日期的验证字段 * @protected * @type {Object} */ defaultMessages : { value : {} }, /** * 验证规则 * @type {Object} */ rules : { shared : false, value : {} }, /** * 验证信息集合 * @type {Object} */ messages : { shared : false, value : {} }, /** * 验证器 验证容器内的表单字段是否通过验证 * @type {Function} */ validator : { }, /** * 存放错误信息容器的选择器,如果未提供则默认显示在控件中 * @private * @type {String} */ errorContainer : { view : true }, /** * 显示错误信息的模板 * @type {Object} */ errorTpl : { view : true, value : '' }, /** * 显示错误 * @type {Boolean} */ showError : { view : true, value : true }, /** * 是否仅显示一个错误 * @type {Boolean} */ showOneError: { }, /** * 错误信息,这个验证错误不包含子控件的验证错误 * @type {String} */ error : { }, /** * 暂停验证 *
* field.set('pauseValid',true); //可以调用field.clearErrors()
* field.set('pauseValid',false); //可以同时调用field.valid()
*
* @type {Boolean}
*/
pauseValid : {
value : false
}
};
Valid.prototype = {
__bindUI : function(){
var _self = this;
//监听是否禁用
_self.on('afterDisabledChange',function(ev){
var disabled = ev.newVal;
if(disabled){
_self.clearErrors(false,false);
}else{
_self.valid();
}
});
},
/**
* 是否通过验证
* @template
* @return {Boolean} 是否通过验证
*/
isValid : function(){
},
/**
* 进行验证
*/
valid : function(){
},
/**
* @protected
* @template
* 验证自身的规则和验证器
*/
validControl : function(){
},
//验证规则
validRules : function(rules,value){
if(!rules){
return null;
}
if(this.get('pauseValid')){
return null;
}
var _self = this,
messages = _self._getValidMessages(),
error = null;
for(var name in rules){
if(rules.hasOwnProperty(name)){
var baseValue = rules[name];
error = Rules.valid(name,value,baseValue,messages[name],_self);
if(error){
break;
}
}
}
return error;
},
//获取验证错误信息
_getValidMessages : function(){
var _self = this,
defaultMessages = _self.get('defaultMessages'),
messages = _self.get('messages');
return BUI.merge(defaultMessages,messages);
},
/**
* @template
* @protected
* 控件本身是否通过验证,不考虑子控件
* @return {String} 验证的错误
*/
getValidError : function(value){
var _self = this,
validator = _self.get('validator'),
error = null;
error = _self.validRules(_self.get('defaultRules'),value) || _self.validRules(_self.get('rules'),value);
if(!error && !this.get('pauseValid')){
if(_self.parseValue){
value = _self.parseValue(value);
}
error = validator ? validator.call(this,value) : '';
}
return error;
},
/**
* 获取验证出错信息,包括自身和子控件的验证错误信息
* @return {Array} 出错信息
*/
getErrors : function(){
},
/**
* 显示错误
* @param {Array} errors 显示错误
*/
showErrors : function(errors){
var _self = this,
errors = errors || _self.getErrors();
_self.get('view').showErrors(errors);
},
/**
* 清除错误
* @param {Boolean} reset 清除错误时是否重置
* @param {Boolean} [deep = true] 是否清理子控件的错误
*/
clearErrors : function(reset,deep){
deep = deep == null ? true : deep;
var _self = this,
children = _self.get('children');
if(deep){
BUI.each(children,function(item){
if(item.clearErrors){
if(item.field){
item.clearErrors(reset);
}else{
item.clearErrors(reset,deep);
}
}
});
}
_self.set('error',null);
_self.get('view').clearErrors();
},
/**
* 添加验证规则
* @param {String} name 规则名称
* @param {*} [value] 规则进行校验的进行对比的值,如max : 10
* @param {String} [message] 出错信息,可以使模板
*
* dialog.on('show',function(){
* form.resetTips();
* });
*
*
*/
resetTips : function(){
var _self = this,
fields = _self.getFields();
BUI.each(fields,function(field){
field.resetTip();
});
},
/**
* @protected
* @ignore
*/
destructor : function(){
var _self = this,
buttonBar = _self.get('buttonBar'),
submitMask = _self.get('submitMask');
if(buttonBar && buttonBar.destroy){
buttonBar.destroy();
}
if(submitMask && submitMask.destroy){
submitMask.destroy();
}
},
//设置表单的初始数据
_uiSetInitRecord : function(v){
//if(v){
this.setRecord(v);
//}
}
},{
ATTRS : {
/**
* 提交的路径
* @type {String}
*/
action : {
view : true,
value : ''
},
allowTextSelection:{
value : true
},
events : {
value : {
/**
* @event
* 表单提交前触发,如果返回false会阻止表单提交
*/
beforesubmit : false
}
},
/**
* 提交的方式
* @type {String}
*/
method : {
view : true,
value : 'get'
},
/**
* 默认的loader配置
* * { * autoLoad : true, * property : 'record', * dataType : 'json' * } ** @type {Object} */ defaultLoaderCfg : { value : { autoLoad : true, property : 'record', dataType : 'json' } }, /** * 异步提交表单时的屏蔽 * @type {BUI.Mask.LoadMask|Object} */ submitMask : { value : { msg : '正在提交。。。' } }, /** * 提交表单的方式 * * - normal 普通方式,直接提交表单 * - ajax 异步提交方式,在submit指定参数 * - iframe 使用iframe提交,开发中。。。 * @cfg {String} [submitType='normal'] */ submitType : { value : 'normal' }, /** * 表单提交前,如果存在错误,是否将焦点定位到第一个错误 * @type {Object} */ focusError : { value : true }, /** * 表单提交成功后的回调函数,普通提交方式 submitType = 'normal',不会调用 * @type {Object} */ callback : { }, decorateCfgFields : { value : { 'method' : true, 'action' : true } }, /** * 默认的子控件时文本域 * @type {String} */ defaultChildClass : { value : 'form-field' }, /** * 使用的标签,为form * @type {String} */ elTagName : { value : 'form' }, /** * 表单按钮 * @type {Array} */ buttons : { }, /** * 按钮栏 * @type {BUI.Toolbar.Bar} */ buttonBar : { shared : false, value : {} }, childContainer : { value : '.x-form-fields' }, /** * 表单初始化的数据,用于初始化或者表单回滚 * @type {Object} */ initRecord : { }, /** * 表单默认不显示错误,不影响表单分组和表单字段 * @type {Boolean} */ showError : { value : false }, xview : { value : FormView }, tpl : { value : '' } } },{ xclass : 'form' }); Form.View = FormView; module.exports = Form; }); define("bui/form/row", ["bui/common","jquery","bui/overlay","bui/list","bui/data"], function(require, exports, module){ /** * @fileOverview 表单里的一行元素 * @ignore */ var BUI = require("bui/common"), FieldContainer = require("bui/form/fieldcontainer"); /** * @class BUI.Form.Row * 表单行 * @extends BUI.Form.FieldContainer */ var Row = FieldContainer.extend({ },{ ATTRS : { elCls : { value : 'row' }, defaultChildCfg:{ value : { tpl : ' \
*
*
* var editor = new Editor.Editor({
* trigger : '.edit-text',
* field : {
* rules : {
* required : true
* }
* }
* });
*
*/
var editor = Overlay.extend([Mixin],{
bindUI : function(){
var _self = this,
innerControl = _self.getInnerControl();
_self.on('validchange',function(ev){
if(!_self.isValid() && _self.get('visible')){
_self._showError(_self.getErrors());
}else{
_self._hideError();
}
});
_self.on('hide',function(){
_self._hideError();
});
_self.on('show',function(){
if(!_self.isValid()){
_self._showError(_self.getErrors());
}
});
},
_initOverlay : function(){
var _self = this,
tooltip = _self.get('tooltip'),
overlay = new Overlay(tooltip);
overlay.render();
_self.set('overlay',overlay);
return overlay;
},
//获取显示错误列表
_getErrorList : function(){
var _self = this,
overlay = _self.get('overlay');
return overlay && overlay.get('children')[0];
},
_showError : function(errors){
var _self = this,
overlay = _self.get('overlay') || _self._initOverlay(),
list = _self._getErrorList(),
align = _self.get('errorAlign'),
items = BUI.Array.map(errors,function(text){
return {error : text};
});
list.set('items',items);
align.node = _self.get('el');
overlay.set('align',align);
overlay.show();
},
//隐藏错误
_hideError : function(){
var _self = this,
overlay = _self.get('overlay');
overlay && overlay.hide();
},
/**
* 清除错误
*/
clearErrors : function(){
var _self = this,
innerControl = _self.getInnerControl();
innerControl.clearErrors();
_self._hideError();
},
/**
* @protected
* @override
* 获取编辑的源数据
* @return {String} 返回需要编辑的文本
*/
getSourceValue : function(){
var _self = this,
trigger = _self.get('curTrigger'),
parser = _self.get('parser'),
text = trigger.text();
if(parser){
text = parser.call(this,text,trigger);
}
return text;
},
/**
* @protected
* 更新文本
* @param {String} text 编辑器的值
*/
updateSource : function(text){
var _self = this,
trigger = _self.get('curTrigger');
if(trigger && trigger.length){
text = _self._formatText(text);
trigger.text(text);
}
},
//格式化文本
_formatText : function(text){
var _self = this,
formatter = _self.get('formatter');
if(formatter){
text = formatter.call(_self,text);
}
return text;
},
_uiSetWidth : function(v){
var _self = this;
if(v != null){
var innerControl = _self.getInnerControl();
if(innerControl.set){
innerControl.set('width',v);
}
}
}
},{
ATTRS : {
/**
* 内部控件的代表Value的字段
* @protected
* @override
* @type {String}
*/
innerValueField : {
value : 'value'
},
/**
* 空值的数据,清空编辑器时使用
* @protected
* @type {*}
*/
emptyValue : {
value : ''
},
/**
* 是否自动隐藏
* @override
* @type {Boolean}
*/
autoHide : {
value : true
},
/**
* 内部控件配置项的字段
* @protected
* @type {String}
*/
controlCfgField : {
value : 'field'
},
/**
* 默认的字段域配置项
* @type {Object}
*/
defaultChildCfg : {
value : {
tpl : '',
forceFit : true,
errorTpl : ''//
}
},
/**
* 错误提示信息的配置信息
* @cfg {Object} tooltip
*/
tooltip : {
valueFn : function(){
return {
children : [{
xclass : 'simple-list',
itemTpl : '
* BUI.use('bui/tooltip',function (Tooltip) {
* //不使用模板的,左侧显示
* var t1 = new Tooltip.Tip({
* trigger : '#t1',
* alignType : 'left', //方向
* showArrow : false, //不显示箭头
* offset : 5, //距离左边的距离
* title : '无任何样式,
左边的提示信息'
* });
* t1.render();
* });
*
*
* ** 也可以配置模板 **
*
* BUI.use('bui/tooltip',function (Tooltip) {
* //使用模板的,左侧显示
* var t1 = new Tooltip.Tip({
* trigger : '#t1',
* alignType : 'left', //方向
* titleTpl : '<span class="x-icon x-icon-small x-icon-success"><i class="icon icon-white icon-question"></i></span>\
* <div class="tips-content">{title}</div>',
* offset : 5, //距离左边的距离
* title : '无任何样式,<br>左边的提示信息'
* });
* t1.render();
* });
*
*/
var Tip = Overlay.Overlay.extend({
//设置对齐方式
_uiSetAlignType : function(type){
var _self = this,
offset = _self.get('offset'),
align = _self.get('align') || {},
points = MAP_TYPES[type];
if(points){
align.points = points;
if(offset){
align.offset = getOffset(type,offset);
}
_self.set('align',align);
}
}
},{
ATTRS : {
//使用委托的方式显示提示信息
delegateTrigger : {
value : true
},
/**
* 对齐类型,包括: top,left,right,bottom四种常用方式,其他对齐方式,可以使用@see{BUI.Tooltip.Tip#property-align}属性
*
* @type {String}
*/
alignType : {
view : true
},
/**
* 显示的内容,文本或者键值对
*
* var tip = new Tip({
* title : {a : 'text a',b:'text b'}, //属性是对象
* titleTpl : 'this is {a},because {b}
' // this is text a,because text b
* });
*
* @cfg {String|Object} title
*/
/**
* 显示的内容
*
* //设置文本
* tip.set('title','new title');
*
* //设置对象
* tip.set('title',{a : 'a',b : 'b'})
*
* @type {Object}
*/
title : {
view : true
},
/**
* 显示对齐箭头
* @override
* @default true
* @cfg {Boolean} [showArrow = true]
*/
showArrow : {
value : true
},
/**
* 箭头放置在的位置,是一个选择器,例如 .arrow-wraper
*
* new Tip({ //可以设置整个控件的模板
* arrowContainer : '.arrow-wraper',
* tpl : ''
* });
*
* new Tip({ //也可以设置title的模板
* arrowContainer : '.arrow-wraper',
* titleTpl : '{title}'
* });
*
* @cfg {String} arrowContainer
*/
arrowContainer : {
view : true
},
//自动显示
autoHide : {
value : true
},
//覆盖自动隐藏类型
autoHideType : {
value : 'leave'
},
/**
* 显示的tip 距离触发器Dom的距离
*
* var tip = new Tip({
* title : {a : 'text a',b:'text b'}, //属性是对象
* offset : 10, //距离
* titleTpl : 'this is {a},because {b}
' // this is text a,because text b
* });
*
* @cfg {Number} offset
*/
offset : {
value : 0
},
/**
* 触发显示tip的事件名称,默认为mouseover
* @type {String}
* @protected
*/
triggerEvent : {
value : 'mouseover'
},
/**
* 显示文本的模板
*
* var tip = new Tip({
* title : {a : 'text a',b:'text b'}, //属性是对象
* offset : 10, //距离
* titleTpl : 'this is {a},because {b}
' // this is text a,because text b
* });
*
* @type {String}
*/
titleTpl : {
view : true,
value : '{title}'
},
xview : {
value : TipView
}
}
},{
xclass : 'tooltip'
});
Tip.View = TipView;
module.exports = Tip;
});
define("bui/tooltip/tips", ["bui/common","jquery","bui/overlay"], function(require, exports, module){
/**
* @fileOverview 批量显示提示信息
* @ignore
*/
//是否json对象构成的字符串
function isObjectString(str){
return /^{.*}$/.test(str);
}
var BUI = require("bui/common"),
Tip = require("bui/tooltip/tip"),
/**
* @class BUI.Tooltip.Tips
* 批量显示提示信息
*
* BUI.use('bui/tooltip',function(){
* var tips = new Tooltip.Tips({
* tip : {
* trigger : '#t1 a', //出现此样式的元素显示tip
* alignType : 'top', //默认方向
* elCls : 'tips tips-no-icon tip1',
* titleTpl : '<span class="x-icon x-icon-small x-icon-success"><i class="icon icon-white icon-question"></i></span>\
* <div class="tips-content">{title}</div>',
* offset : 10 //距离左边的距离
* }
* });
* tips.render();
* })
*
*
*/
Tips = function(config){
Tips.superclass.constructor.call(this,config);
};
Tips.ATTRS = {
/**
* 使用的提示控件或者配置信息 @see {BUI.Tooltip.Tip}
*
* //不使用模板的,左侧显示
* var tips = new Tooltip.Tips({
* tip : {
* trigger : '#t1 a', //出现此样式的元素显示tip
* alignType : 'top', //默认方向
* elCls : 'tips tips-no-icon tip1',
* offset : 10 //距离左边的距离
* }
* });
* tips.render();
*
* @cfg {BUI.Tooltip.Tip|Object} tip
*/
/**
* 使用的提示控件 @see {BUI.Tooltip.Tip}
*
* var tip = tips.get('tip');
*
* @type {BUI.Tooltip.Tip}
* @readOnly
*/
tip : {
},
/**
* 默认的对齐方式,如果不指定tip的对齐方式,那么使用此属性
*
* //不使用模板的,左侧显示
* var tips = new Tooltip.Tips({
* tip : {
* trigger : '#t1 a', //出现此样式的元素显示tip
* defaultAlignType : 'top', //默认方向
* elCls : 'tips tips-no-icon tip1',
* offset : 10 //距离左边的距离
* }
* });
* tips.render();
*
* @cfg {Object} defaultAlignType
*/
defaultAlignType : {
}
};
BUI.extend(Tips,BUI.Base);
BUI.augment(Tips,{
//初始化
_init : function(){
this._initDom();
this._initEvent();
},
//初始化DOM
_initDom : function(){
var _self = this,
tip = _self.get('tip'),
defaultAlignType;
if(tip && !tip.isController){
defaultAlignType = tip.alignType; //设置默认的对齐方式
tip = new Tip(tip);
tip.render();
_self.set('tip',tip);
if(defaultAlignType){
_self.set('defaultAlignType',defaultAlignType);
}
}
},
//初始化事件
_initEvent : function(){
var _self = this,
tip = _self.get('tip');
tip.on('triggerchange',function(ev){
var curTrigger = ev.curTrigger;
_self._replaceTitle(curTrigger);
_self._setTitle(curTrigger,tip);
});
},
//替换掉title
_replaceTitle : function(triggerEl){
var title = triggerEl.attr('title');
if(title){
triggerEl.attr('data-title',title);
triggerEl[0].removeAttribute('title');
}
},
//设置title
_setTitle : function(triggerEl,tip){
var _self = this,
title = triggerEl.attr('data-title'),
alignType = triggerEl.attr('data-align') || _self.get('defaultAlignType');
if(isObjectString(title)){
title = BUI.JSON.looseParse(title);
}
tip.set('title',title);
if(alignType){
tip.set('alignType',alignType);
}
},
/**
* 渲染提示信息
* @chainable
*/
render : function(){
this._init();
return this;
}
});
module.exports = Tips;
});
define("bui/grid", ["bui/common","jquery","bui/list","bui/data","bui/mask","bui/toolbar","bui/menu"], function(require, exports, module){
/**
* @fileOverview 表格命名空间入口
* @ignore
*/
var BUI = require("bui/common"),
Grid = BUI.namespace('Grid');
BUI.mix(Grid, {
SimpleGrid : require("bui/grid/simplegrid"),
Grid : require("bui/grid/grid"),
Column : require("bui/grid/column"),
Header : require("bui/grid/header"),
Format : require("bui/grid/format"),
Plugins : require("bui/grid/plugins/base")
});
module.exports = Grid;
});
define("bui/grid/simplegrid", ["jquery","bui/common","bui/list","bui/data"], function(require, exports, module){
/**
* @fileOverview 简单表格,仅用于展示数据
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
List = require("bui/list"),
Component = BUI.Component,
UIBase = Component.UIBase,
PREFIX = BUI.prefix,
CLS_GRID = PREFIX + 'grid',
CLS_GRID_ROW = CLS_GRID + '-row',
CLS_ROW_ODD = PREFIX + 'grid-row-odd',
CLS_ROW_EVEN = PREFIX + 'grid-row-even',
CLS_GRID_BORDER = PREFIX + 'grid-border',
CLS_ROW_FIRST = PREFIX + 'grid-row-first';
/**
* 简单表格的视图类
* @class BUI.Grid.SimpleGridView
* @extends BUI.List.SimpleListView
* @private
*/
var simpleGridView = List.SimpleListView.extend({
/**
* 设置列
* @internal
* @param {Array} columns 列集合
*/
setColumns : function(columns){
var _self = this,
headerRowEl = _self.get('headerRowEl');
columns = columns || _self.get('columns');
//清空表头
headerRowEl.empty();
BUI.each(columns,function(column){
_self._createColumn(column,headerRowEl);
});
},
//创建列
_createColumn : function(column,parent){
var _self = this,
columnTpl = BUI.substitute(_self.get('columnTpl'),column);
$(columnTpl).appendTo(parent);
},
/**
* 获取行模板
* @ignore
*/
getItemTpl : function (record,index) {
var _self = this,
columns = _self.get('columns'),
rowTpl = _self.get('rowTpl'),
oddCls = index % 2 === 0 ? CLS_ROW_ODD : CLS_ROW_EVEN,
cellsTpl = [],
rowEl;
BUI.each(columns, function (column) {
var dataIndex = column['dataIndex'];
cellsTpl.push(_self._getCellTpl(column, dataIndex, record));
});
rowTpl = BUI.substitute(rowTpl,{cellsTpl:cellsTpl.join(''), oddCls:oddCls});
return rowTpl;
},
//get cell template by config and record
_getCellTpl:function (column, dataIndex, record) {
var _self = this,
renderer = column.renderer,
text = renderer ? renderer(record[dataIndex], record) : record[dataIndex],
cellTpl = _self.get('cellTpl');
return BUI.substitute(cellTpl,{elCls : column.elCls,text:text});
},
/**
* 清除数据
* @ignore
*/
clearData : function(){
var _self = this,
tbodyEl = _self.get('itemContainer');
tbodyEl.empty();
},
showData : function(data){
var _self = this;
BUI.each(data,function(record,index){
_self._createRow(record,index);
});
},
//设置单元格边框
_uiSetInnerBorder : function(v){
var _self = this,
el = _self.get('el');
if(v){
el.addClass(CLS_GRID_BORDER);
}else{
el.removeClass(CLS_GRID_BORDER);
}
},
_uiSetTableCls : function(v){
var _self = this,
tableEl = _self.get('el').find('table');
tableEl.attr('class',v);
}
},{
ATTRS : {
/**
* @private
* @ignore
*/
headerRowEl : {
valueFn :function(){
var _self = this,
thead = _self.get('el').find('thead');
return thead.children('tr');
}
},
/**
* @private
* @ignore
* @type {Object}
*/
itemContainer :{
valueFn :function(){
return this.get('el').find('tbody');
}
},
tableCls : {
}
}
},{
xclass:'simple-grid-veiw'
});
/**
* 简单表格
* xclass:'simple-grid'
*
* BUI.use('bui/grid',function(Grid){
*
* var columns = [{
* title : '表头1(10%)',
* dataIndex :'a',
* width:'10%'
* },{
* id: '123',
* title : '表头2(20%)',
* dataIndex :'b',
* width:'20%'
* },{
* title : '表头3(70%)',
* dataIndex : 'c',
* width:'70%'
* }],
* data = [{a:'123'},{a:'cdd',b:'edd'},{a:'1333',c:'eee',d:2}];
*
* var grid = new Grid.SimpleGrid({
* render:'#grid',
* columns : columns,
* items : data,
* idField : 'a'
* });
*
* grid.render();
* });
*
* @class BUI.Grid.SimpleGrid
* @extends BUI.List.SimpleList
*/
var simpleGrid = BUI.List.SimpleList.extend(
{
renderUI : function(){
this.get('view').setColumns();
},
/**
* 绑定事件
* @protected
*/
bindUI : function(){
var _self = this,
itemCls = _self.get('itemCls'),
hoverCls = itemCls + '-hover',
el = _self.get('el');
el.delegate('.'+itemCls,'mouseover',function(ev){
var sender = $(ev.currentTarget);
sender.addClass(hoverCls);
}).delegate('.'+itemCls,'mouseout',function(ev){
var sender = $(ev.currentTarget);
sender.removeClass(hoverCls);
});
},
/**
* 显示数据
*
* var data = [{},{}];
* grid.showData(data);
*
* //等同
* grid.set('items',data);
*
* @param {Array} data 要显示的数据
*/
showData : function(data){
this.clearData();
//this.get('view').showData(data);
this.set('items',data);
},
/**
* 清除数据
*/
clearData : function(){
this.get('view').clearData();
},
_uiSetColumns : function(columns){
var _self = this;
//重置列,先清空数据
_self.clearData();
_self.get('view').setColumns(columns);
}
},{
ATTRS :
{
/**
* 表格可点击项的样式
* @protected
* @type {String}
*/
itemCls : {
view:true,
value : CLS_GRID_ROW
},
/**
* 表格应用的样式,更改此值,则不应用默认表格样式
*
* grid = new Grid.SimpleGrid({
* render:'#grid',
* columns : columns,
* innerBorder : false,
* tableCls:'table table-bordered table-striped',
* store : store
* });
*
* @type {Object}
*/
tableCls : {
view : true,
value : CLS_GRID + '-table'
},
/**
* 列信息
* @cfg {Array} columns
*/
/**
* 列信息,仅支持以下配置项:
*
*
* grid = new Grid.SimpleGrid({
* render:'#grid',
* columns : columns,
* innerBorder : false,
* store : store
* });
*
*
* @cfg {Boolean} [innerBorder=true]
*/
/**
* 单元格左右之间是否出现边框
* @type {Boolean}
* @default true
*/
innerBorder : {
view:true,
value : true
},
/**
* 行模版
* @type {Object}
*/
rowTpl:{
view : true,
value:'
*
*
表格插件的类图:
*
*
*
* BUI.use(['bui/grid','bui/data'],function(Grid,Data){
* var Grid = Grid,
* Store = Data.Store,
* columns = [{ //声明列模型
* title : '表头1(20%)',
* dataIndex :'a',
* width:'20%'
* },{
* id: '123',
* title : '表头2(30%)',
* dataIndex :'b',
* width:'30%'
* },{
* title : '表头3(50%)',
* dataIndex : 'c',
* width:'50%'
* }],
* data = [{a:'123'},{a:'cdd',b:'edd'},{a:'1333',c:'eee',d:2}]; //显示的数据
*
* var store = new Store({
* data : data,
* autoLoad:true
* }),
* grid = new Grid.Grid({
* render:'#grid',
* width:'100%',//这个属性一定要设置
* columns : columns,
* idField : 'a',
* store : store
* });
*
* grid.render();
* });
*
* @extends BUI.List.SimpleList
*/
var grid = List.SimpleList.extend({
/**
* @protected
* @ignore
*/
createDom:function () {
var _self = this,
render = _self.get('render'),
outerWidth = $(render).width(),
width = _self.get('width');
if(!width && outerWidth){
var appendWidth = _self.getAppendWidth();
_self.set('width',outerWidth - appendWidth);
}
// 提前,中途设置宽度时会失败!!
if (_self.get('width')) {
_self.get('el').addClass(CLS_GRID_WITH);
}
if (_self.get('height')) {
_self.get('el').addClass(CLS_GRID_HEIGHT);
}
//因为内部的边距影响header的forceFit计算,所以必须在header计算forceFit前置此项
if(_self.get('innerBorder')){
_self.get('el').addClass(CLS_GRID_BORDER);
}
},
/**
* @protected
* @ignore
*/
renderUI : function(){
var _self = this;
_self._initHeader();
_self._initBars();
_self._initLoadMask();
_self.get('view').resetHeaderRow();
},
/**
* @private
*/
bindUI:function () {
var _self = this;
_self._bindHeaderEvent();
_self._bindBodyEvent();
_self._bindItemsEvent();
},
/**
* 添加列
*
* //添加到最后
* grid.addColumn({title : 'new column',dataIndex : 'new',width:100});
* //添加到最前
* grid.addColumn({title : 'new column',dataIndex : 'new',width:100},0);
*
* @param {Object|BUI.Grid.Column} column 列的配置,列类的定义 {@link BUI.Grid.Column}
* @param {Number} index 添加到的位置
* @return {BUI.Grid.Column}
*/
addColumn : function(column, index){
var _self = this,
header = _self.get('header');
if(header){
column = header.addColumn(column, index);
}else{
column = new Column(column);
_self.get('columns').splice(index,0,column);
}
return column;
},
/**
* 清除显示的数据
*
* grid.clearData();
*
*/
clearData : function(){
this.clearItems();
},
/**
* 当前显示在表格中的数据
* @return {Array} 纪录集合
* @private
*/
getRecords : function(){
return this.getItems();
},
/**
* 使用索引或者id查找列
*
* //设置列的id,否则会自动生成
* {id : '1',title : '表头',dataIndex : 'a'}
* //获取列
* var column = grid.findColumn('id');
* //操作列
* column.set('visible',false);
*
* @param {String|Number} id|index 文本值代表编号,数字代表索引
*/
findColumn : function(id){
var _self = this,
header = _self.get('header');
if(BUI.isNumber(id)){
return header.getColumnByIndex(id);
}else{
return header.getColumnById(id);
}
},
/**
* 使用字段名查找列
*
* //设置列dataIndex
* {id : '1',title : '表头',dataIndex : 'a'}
* //获取列
* var column = grid.findColumnByField('a');
* //操作列
* column.set('visible',false);
*
* @param {String} field 列的字段名 dataIndex
*/
findColumnByField : function(field){
var _self = this,
header = _self.get('header');
return header.getColumn(function(column){
return column.get('dataIndex') === field;
});
},
/**
* 根据列的Id查找对应的单元格
* @param {String|Number} id 列id
* @param {Object|jQuery} record 本行对应的记录,或者是本行的DOM对象
* @protected
* @return {jQuery}
*/
findCell:function (id, record) {
var _self = this,
rowEl = null;
if (record instanceof $) {
rowEl = record;
} else {
rowEl = _self.findRow(record);
}
if (rowEl) {
return _self.get('view').findCell(id, rowEl);
}
return null;
},
/**
* find the dom by the record in this component
* @param {Object} record the record used to find row dom
* @protected
* @return jQuery
*/
findRow:function (record) {
var _self = this;
return _self.get('view').findRow(record);
},
/**
* 移除列
*
* var column = grid.findColumn('id');
* grid.removeColumn(column);
*
* @param {BUI.Grid.Column} column 要移除的列
*/
removeColumn:function (column) {
var _self = this;
_self.get('header').removeColumn(column);
},
/**
* 显示数据,当不使用store时,可以单独显示数据
*
* var data = [{},{}];
* grid.showData(data);
*
* @param {Array} data 显示的数据集合
*/
showData : function(data){
var _self = this;
_self.set('items',data);
},
/**
* 重置列,当列发生改变时同步DOM和数据
* @protected
*/
resetColumns:function () {
var _self = this,
store = _self.get('store');
//recreate the header row
_self.get('view').resetHeaderRow();
//show data
if (store) {
_self.onLoad();
}
},
//when body scrolled,the other component of grid also scrolled
_bindScrollEvent:function () {
var _self = this,
el = _self.get('el'),
bodyEl = el.find('.' + CLS_GRID_BODY),
header = _self.get('header');
bodyEl.on('scroll', function () {
var left = bodyEl.scrollLeft(),
top = bodyEl.scrollTop();
header.scrollTo({left:left, top:top});
_self.fire('scroll', {scrollLeft:left, scrollTop:top,bodyWidth : bodyEl.width(),bodyHeight : bodyEl.height()});
});
},
//bind header event,when column changed,followed this component
_bindHeaderEvent:function () {
var _self = this,
header = _self.get('header'),
view = _self.get('view'),
store = _self.get('store');
header.on('afterWidthChange', function (e) {
var sender = e.target;
if (sender !== header) {
view.resetColumnsWidth(sender);
}
});
header.on('afterSortStateChange', function (e) {
var column = e.target,
val = e.newVal;
if (val && store) {
store.sort(column.get('dataIndex'), column.get('sortState'));
}
});
header.on('afterVisibleChange', function (e) {
var sender = e.target;
if (sender !== header) {
view.setColumnVisible(sender);
_self.fire('columnvisiblechange',{column:sender});
}
});
header.on('click', function (e) {
var sender = e.target;
if (sender !== header) {
_self.fire('columnclick',{column:sender,domTarget:e.domTarget});
}
});
header.on('forceFitWidth', function () {
if (_self.get('rendered')) {
_self.resetColumns();
}
});
header.on('add', function (e) {
if (_self.get('rendered')) {
_self.fire('columnadd',{column:e.column,index:e.index});
_self.resetColumns();
}
});
header.on('remove', function (e) {
if (_self.get('rendered')) {
_self.resetColumns();
_self.fire('columnremoved',{column:e.column,index:e.index});
}
});
},
//when body scrolled, header can followed
_bindBodyEvent:function () {
var _self = this;
_self._bindScrollEvent();
},
//绑定记录DOM相关的事件
_bindItemsEvent : function(){
var _self = this,
store = _self.get('store');
_self.on('itemsshow',function(){
_self.fire('aftershow');
});
_self.on('itemsclear',function(){
_self.fire('clear');
});
_self.on('itemclick',function(ev){
var target = ev.domTarget,
record = ev.item,
cell = $(target).closest('.' + CLS_GRID_CELL),
rowEl = $(target).closest('.' + CLS_GRID_ROW),
rst; //用于是否阻止事件触发
if(cell.length){
rst = _self.fire('cellclick', {record:record, row:rowEl[0], cell:cell[0], field:cell.attr(ATTR_COLUMN_FIELD), domTarget:target,domEvent:ev.domEvent});
}
if(rst === false){
return rst;
}
return _self.fire('rowclick', {record:record, row:rowEl[0], domTarget:target});
});
_self.on('itemunselected',function(ev){
_self.fire('rowunselected',getEventObj(ev));
});
_self.on('itemselected',function(ev){
_self.fire('rowselected',getEventObj(ev));
});
_self.on('itemrendered',function(ev){
_self.fire('rowcreated',getEventObj(ev));
});
_self.on('itemremoved',function(ev){
_self.fire('rowremoved',getEventObj(ev));
});
_self.on('itemupdated',function(ev){
_self.fire('rowupdated',getEventObj(ev));
});
function getEventObj(ev){
return {record : ev.item, row : ev.domTarget, domTarget : ev.domTarget};
}
},
//获取表格内部的宽度,受边框的影响,
//内部的宽度不能等于表格宽度
_getInnerWidth : function(width){
width = width || this.get('width');
return getInnerWidth(width);
},
//init header,if there is not a header property in config,instance it
_initHeader:function () {
var _self = this,
header = _self.get('header'),
container = _self.get('el').find('.'+ CLS_GRID_HEADER_CONTAINER);
if (!header) {
header = new Header({
columns:_self.get('columns'),
tableCls:_self.get('tableCls'),
forceFit:_self.get('forceFit'),
width:_self._getInnerWidth(),
render: container,
parent : _self
}).render();
//_self.addChild(header);
_self.set('header', header);
}
},
//初始化 上下工具栏
_initBars:function () {
var _self = this,
bbar = _self.get('bbar'),
tbar = _self.get('tbar');
_self._initBar(bbar, CLS_GRID_BBAR, 'bbar');
_self._initBar(tbar, CLS_GRID_TBAR, 'tbar');
},
//set bar's elCls to identify top bar or bottom bar
_initBar:function (bar, cls, name) {
var _self = this,
store = null,
pagingBarCfg = null;
if (bar) {
//未指定xclass,同时不是Controller时
if(!bar.xclass && !(bar instanceof Component.Controller)){
bar.xclass = 'bar';
bar.children = bar.children || [];
if(bar.items){
bar.children.push({
xclass : 'bar',
defaultChildClass : "bar-item-button",
elCls : CLS_BUTTON_BAR,
children : bar.items
});
bar.items=null;
}
// modify by fuzheng
if(bar.pagingBar){
store = _self.get('store');
pagingBarCfg = {
xclass : 'pagingbar',
store : store,
pageSize : store.pageSize
};
if(bar.pagingBar !== true){
pagingBarCfg = BUI.merge(pagingBarCfg, bar.pagingBar);
}
bar.children.push(pagingBarCfg);
}
}
if (bar.xclass) {
var barContainer = _self.get('el').find('.' + cls);
barContainer.show();
bar.render = barContainer;
//bar.parent=_self;
bar.elTagName = 'div';
bar.autoRender = true;
bar = _self.addChild(bar); //Component.create(bar).create();
}
_self.set(name, bar);
}
return bar;
},
//when set 'loadMask = true' ,create a loadMask instance
_initLoadMask:function () {
var _self = this,
loadMask = _self.get('loadMask');
if (loadMask && !loadMask.show) {
loadMask = new BUI.Mask.LoadMask({el:_self.get('el')});
_self.set('loadMask', loadMask);
}
},
//调整宽度时,调整内部控件宽度
_uiSetWidth:function (w) {
var _self = this;
if (_self.get('rendered')) {
if(!isPercent(w)){
_self.get('header').set('width', _self._getInnerWidth(w));
}else{
_self.get('header').set('width','100%');
}
}
_self.get('view').setTableWidth();
},
//设置自适应宽度
_uiSetForceFit:function (v) {
var _self = this;
_self.get('header').set('forceFit', v);
},
//when set grid's height,the scroll can effect the width of its body and header
_uiSetHeight:function (h,obj) {
var _self = this,
header = _self.get('header');
_self.get('view').setBodyHeight(h);
if (_self.get('rendered')) {
if (_self.get('forceFit') && !obj.prevVal) {
header.forceFitColumns();
//强迫对齐时,由未设置高度改成设置高度,增加了17像素的滚动条宽度,所以重置表格宽度
_self.get('view').setTableWidth();
}
header.setTableWidth();
}
},
/**
* 加载数据
* @protected
*/
onLoad : function(){
var _self = this,
store = _self.get('store');
grid.superclass.onLoad.call(this);
if(_self.get('emptyDataTpl')){ //初始化的时候不显示空白数据的文本
if(store && store.getCount() == 0){
_self.get('view').showEmptyText();
}else{
_self.get('view').clearEmptyText();
}
}
}
},{
ATTRS : {
/**
* 表头对象
* @type {BUI.Grid.Header}
* @protected
*/
header:{
},
/**
* @see {BUI.Grid.Grid#tbar}
*
* grid = new Grid.Grid({
* render:'#grid',
* columns : columns,
* width : 700,
* forceFit : true,
* tbar:{ //添加、删除
* items : [{
* btnCls : 'button button-small',
* text : '添加',
* listeners : {
* 'click' : addFunction
* }
* },
* {
* btnCls : 'button button-small',
* text : '删除',
* listeners : {
* 'click' : delFunction
* }
* }]
* },
* store : store
* });
*
* grid.render();
*
* @cfg {Object|BUI.Toolbar.Bar} bbar
*/
/**
* @see {BUI.Grid.Grid#tbar}
* @type {Object}
* @ignore
*/
bbar:{
},
itemCls : {
value : CLS_GRID_ROW
},
/**
* 列的配置 用来配置 表头 和 表内容。{@link BUI.Grid.Column}
* @cfg {Array} columns
*/
columns:{
view : true,
value:[]
},
/**
* 强迫列自适应宽度,如果列宽度大于Grid整体宽度,等比例缩减,否则等比例增加
*
* var grid = new Grid.Grid({
* render:'#grid',
* columns : columns,
* width : 700,
* forceFit : true, //自适应宽度
* store : store
* });
*
* @cfg {Boolean} [forceFit= false]
*/
/**
* 强迫列自适应宽度,如果列宽度大于Grid整体宽度,等比例缩减,否则等比例增加
*
* grid.set('forceFit',true);
*
* @type {Boolean}
* @default 'false'
*/
forceFit:{
sync:false,
view : true,
value:false
},
/**
* 数据为空时,显示的提示内容
*
* var grid = new Grid({
* render:'#J_Grid4',
* columns : columns,
* store : store,
* emptyDataTpl : '<div class="centered"><img alt="Crying" src="http://img03.taobaocdn.com/tps/i3/T1amCdXhXqXXXXXXXX-60-67.png"><h2>查询的数据不存在</h2></div>',
* width:'100%'
*
* });
*
* grid.render();
*
** @cfg {Object} emptyDataTpl
*/
emptyDataTpl : {
view : true
},
/**
* 表格首行记录模板,首行记录,隐藏显示,用于确定表格各列的宽度
* @type {String}
* @protected
*/
headerRowTpl:{
view:true,
value:''<tr class="' + CLS_GRID_ROW + ' {{oddCls}}">{{cellsTpl}}</tr>'*/ rowTpl:{ view:true, value:'
* '<td class="' + CLS_GRID_CELL + ' grid-td-{{id}}" data-column-id="{{id}}" data-column-field = {{dataIndex}}>'+ * '<div class="' + CLS_GRID_CELL_INNER + '" >{{cellText}}</div>'+ * '</td>' **/ cellTpl:{ view:true, value:'
* var grid = new Grid.Grid({
* render:'#grid',
* innerBorder: false, // 默认为true
* columns : columns,
* store : store
* });
*
* @type {Boolean}
* @default true
*/
innerBorder : {
sync:false,
value : true
},
/**
* 是否使用空白单元格用于占位,使列宽等于设置的宽度
* @type {Boolean}
* @private
*/
useEmptyCell : {
view : true,
value : true
},
/**
* 是否首行使用空白行,用以确定表格列的宽度
* @type {Boolean}
* @private
*/
useHeaderRow : {
view : true,
value : true
},
/**
* Grid 的视图类型
* @type {BUI.Grid.GridView}
*/
xview : {
value : gridView
}
}
},{
xclass : 'grid'
});
grid.View = gridView;
module.exports = grid;
/**
* @ignore
* 2013.1.18
* 这是一个重构的版本,将Body取消掉了,目的是为了可以将Grid和SimpleGrid联系起来,
* 同时将selection 统一
*/
});
define("bui/grid/header", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 表格的头部
* @author dxq613@gmail.com, yiminghe@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
PREFIX = BUI.prefix,
Grid = BUI.namespace('Grid'),
Column = require("bui/grid/column"),
View = BUI.Component.View,
Controller = BUI.Component.Controller,
CLS_SCROLL_WITH = 17,
UA = BUI.UA;
/**
* 表格控件中表头的视图类
* @class BUI.Grid.HeaderView
* @extends BUI.Component.View
* @private
*/
var headerView = View.extend({
/**
* @see {Component.Render#getContentElement}
* @ignore
*/
getContentElement:function () {
return this.get('el').find('tr');
},
scrollTo:function (obj) {
var _self = this,
el = _self.get('el');
if (obj.top !== undefined) {
el.scrollTop(obj.top);
}
if (obj.left !== undefined) {
el.scrollLeft(obj.left);
}
},
_uiSetTableCls : function(v){
var _self = this,
tableEl = _self.get('el').find('table');
tableEl.attr('class',v);
}
}, {
ATTRS:{
emptyCellEl:{},
tableCls : {
}
}
},{
xclass : 'header-view'
});
/**
* Container which holds headers and is docked at the top or bottom of a Grid.
* The HeaderContainer drives resizing/moving/hiding of columns within the GridView.
* As headers are hidden, moved or resized,
* the header container is responsible for triggering changes within the view.
* If you are not in the writing plugins, don't direct manipulation this control.
* @class BUI.Grid.Header
* @protected
* xclass:'grid-header'
* @extends BUI.Component.Controller
*/
var header = Controller.extend(
{
/**
* add a columns to header
* @param {Object|BUI.Grid.Column} c The column object or column config.
* @index {Number} index The position of the column in a header,0 based.
*/
addColumn:function (c, index) {
var _self = this,
insertIndex = index,
columns = _self.get('columns');
c = _self._createColumn(c);
if (index === undefined) {
index = columns.length;
insertIndex = _self.get('children').length - 1;
}
columns.splice(index, 0, c);
_self.addChild(c, insertIndex);
_self.fire('add', {column:c, index:index});
return c;
},
/**
* remove a columns from header
* @param {BUI.Grid.Column|Number} c is The column object or The position of the column in a header,0 based.
*/
removeColumn:function (c) {
var _self = this,
columns = _self.get('columns'),
index;
c = BUI.isNumber(c) ? columns[c] : c;
index = BUI.Array.indexOf(c, columns);
columns.splice(index, 1);
_self.fire('remove', {column:c, index:index});
return _self.removeChild(c, true);
},
/**
* For overridden.
* @see Component.Controller#bindUI
*/
bindUI:function () {
var _self = this;
_self._bindColumnsEvent();
},
/*
* For overridden.
* @protected
*
*/
initializer:function () {
var _self = this,
children = _self.get('children'),
columns = _self.get('columns'),
emptyColumn;
$.each(columns, function (index,item) {
var columnControl = _self._createColumn(item);
children[index] = columnControl;
columns[index] = columnControl;
});
//if(!_self.get('forceFit')){
emptyColumn = _self._createEmptyColumn();
children.push(emptyColumn);
_self.set('emptyColumn',emptyColumn);
//}
},
/**
* get the columns of this header,the result equals the 'children' property .
* @return {Array} columns
* @example var columns = header.getColumns();
*
* columns = [{
* title : '表头1',
* dataIndex :'a',
* width:100
* },{
* title : '表头2',
* dataIndex :'b',
* visible : false, //隐藏
* width:200
* },{
* title : '表头3',
* dataIndex : 'c',
* width:200
* }];
*
* @class BUI.Grid.Column
* @extends BUI.Component.Controller
*/
var column = BUI.Component.Controller.extend(
{ //toggle sort state of this column ,if no sort state set 'ASC',else toggle 'ASC' and 'DESC'
_toggleSortState:function () {
var _self = this,
sortState = _self.get('sortState'),
v = sortState ? (sortState === SORT_ASC ? SORT_DESC : SORT_ASC) : SORT_ASC;
_self.set('sortState', v);
},
/**
* {BUI.Component.Controller#performActionInternal}
* @ignore
*/
performActionInternal:function (ev) {
var _self = this,
sender = $(ev.target),
prefix = _self.get('prefixCls');
if (sender.hasClass(prefix + CLS_HD_TRIGGER)) {
} else {
if (_self.get('sortable')) {
_self._toggleSortState();
}
}
//_self.fire('click',{domTarget:ev.target});
},
_uiSetWidth : function(v){
if(v){
this.set('originWidth',v);
}
}
}, {
ATTRS:
{
/**
* The tag name of the rendered column
* @private
*/
elTagName:{
value:'th'
},
/**
* 表头展开显示菜单,
* @type {Boolean}
* @protected
*/
open : {
view : true,
value : false
},
/**
* 此列对应显示数据的字段名称
*
* {
* title : '表头1',
* dataIndex :'a', //对应的数据的字段名称,如 : {a:'123',b:'456'}
* width:100
* }
*
* @cfg {String} dataIndex
*/
/**
* 此列对应显示数据的字段名称
* @type {String}
* @default {String} empty string
*/
dataIndex:{
view:true,
value:''
},
/**
* 是否可拖拽,暂时未支持
* @private
* @type {Boolean}
* @defalut true
*/
draggable:{
sync:false,
view:true,
value:true
},
/**
* 编辑器,用于可编辑表格中
* columns = [
* {title : '文本',dataIndex :'a',editor : {xtype : 'text'}},
* {title : '数字', dataIndex :'b',editor : {xtype : 'number',rules : {required : true}}},
* {title : '日期',dataIndex :'c', editor : {xtype : 'date'},renderer : Grid.Format.dateRenderer},
* {title : '单选',dataIndex : 'd', editor : {xtype :'select',items : enumObj},renderer : Grid.Format.enumRenderer(enumObj)},
* {title : '多选',dataIndex : 'e', editor : {xtype :'select',select:{multipleSelect : true},items : enumObj},
* renderer : Grid.Format.multipleItemsRenderer(enumObj)
* }
* ]
*
* @type {Object}
*/
editor:{
},
/**
* 是否可以获取焦点
* @protected
*/
focusable:{
value:false
},
/**
* 固定列,主要用于在首行显示一些特殊内容,如单选框,复选框,序号等。插件不能对此列进行特殊操作,如:移动位置,隐藏等
* @cfg {Boolean} fixed
*/
fixed : {
value : false
},
/**
* 控件的编号
* @cfg {String} id
*/
id:{
},
/**
* 渲染表格单元格的格式化函数
* "function(value,obj,index){return value;}"
*
* {title : '操作',renderer : function(){
* return '编辑'
* }}
*
* @cfg {Function} renderer
*/
renderer:{
},
/**
* 是否可以调整宽度,应用于拖拽或者自适应宽度时
* @type {Boolean}
* @protected
* @default true
*/
resizable:{
value:true
},
/**
* 是否可以按照此列排序,如果设置true,那么点击列头时
*
* {title : '数字', dataIndex :'b',sortable : false},
*
* @cfg {Boolean} [sortable=true]
*/
sortable:{
sync:false,
view:true,
value:true
},
/**
* 排序状态,当前排序是按照升序、降序。有3种值 null, 'ASC','DESC'
* @type {String}
* @protected
* @default null
*/
sortState:{
view:true,
value:null
},
/**
* 列标题
* @cfg {String} [title= ]
*/
/**
* 列标题
*
* var column = grid.findColumn('id');
* column.get('title');
*
* Note: to have a clickable header with no text displayed you can use the default of aka .
* @type {String}
* @default {String}
*/
title:{
sync:false,
view:true,
value:' '
},
/**
* 列的宽度,可以使数字或者百分比,不要使用 width : '100'或者width : '100px'
*
* {title : '文本',width:100,dataIndex :'a',editor : {xtype : 'text'}}
*
* {title : '文本',width:'10%',dataIndex :'a',editor : {xtype : 'text'}}
*
* @cfg {Number} [width = 80]
*/
/**
* 列宽度
*
* grid.findColumn(id).set('width',200);
*
*
* @type {Number}
*/
width:{
value:100
},
/**
* 是否显示菜单
* @cfg {Boolean} [showMenu=false]
*/
/**
* 是否显示菜单
* @type {Boolean}
* @default false
*/
showMenu:{
view:true,
value:false
},
/**
* @private
* @type {Object}
*/
triggerTpl:{
view:true,
value:''
},
/**
* An template used to create the internal structure inside this Component's encapsulating Element.
* User can use the syntax of KISSY 's template component.
* Only in the configuration of the column can set this property.
* @type {String}
*/
tpl:{
sync:false,
view:true,
value:'
* //Grid 的列定义
* {title:"状态",dataIndex:"status",renderer:BUI.Grid.Format.multipleItemsRenderer({"1":"入库","2":"出库","3":"退货"})}
* //数据源是[1,2] 时,则返回 "入库,出库"
*
*/
multipleItemsRenderer:function (enumObj) {
var enumFun = Format.enumRenderer(enumObj);
return function (values) {
var result = [];
if (!values) {
return '';
}
if (!BUI.isArray(values)) {
values = values.toString().split(',');
}
$.each(values, function (index,value) {
result.push(enumFun(value));
});
return result.join(',');
};
},
/**
* 将财务数据分转换成元
* @param {Number|String} enumObj 键值对的枚举对象 {"1":"大","2":"小"}
* @return {Number} 返回将分转换成元的数字
*/
moneyCentRenderer:function (v) {
if (BUI.isString(v)) {
v = parseFloat(v);
}
if ($.isNumberic(v)) {
return (v * 0.01).toFixed(2);
}
return v;
}
};
module.exports = Format;
});
define("bui/grid/plugins/base", ["bui/common","jquery","bui/menu"], function(require, exports, module){
/**
* @fileOverview 表格插件的入口
* @author dxq613@gmail.com, yiminghe@gmail.com
* @ignore
*/
var BUI = require("bui/common"),
Selection = require("bui/grid/plugins/selection"),
Plugins = {};
BUI.mix(Plugins,{
CheckSelection : Selection.CheckSelection,
RadioSelection : Selection.RadioSelection,
Cascade : require("bui/grid/plugins/cascade"),
CellEditing : require("bui/grid/plugins/cellediting"),
RowEditing : require("bui/grid/plugins/rowediting"),
DialogEditing : require("bui/grid/plugins/dialog"),
AutoFit : require("bui/grid/plugins/autofit"),
GridMenu : require("bui/grid/plugins/gridmenu"),
Summary : require("bui/grid/plugins/summary"),
RowNumber : require("bui/grid/plugins/rownumber"),
ColumnGroup : require("bui/grid/plugins/columngroup"),
RowGroup : require("bui/grid/plugins/rowgroup"),
ColumnResize : require("bui/grid/plugins/columnresize"),
ColumnChecked : require("bui/grid/plugins/columnchecked")
});
module.exports = Plugins;
});
define("bui/grid/plugins/selection", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 选择的插件
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
PREFIX = BUI.prefix,
CLS_CHECKBOX = PREFIX + 'grid-checkBox',
CLS_CHECK_ICON = 'x-grid-checkbox',
CLS_RADIO = PREFIX + 'grid-radio';
/**
* 选择行插件
*
** var store = new Store({
* data : data,
* autoLoad:true
* }),
* grid = new Grid.Grid({
* render:'#grid',
* columns : columns,
* itemStatusFields : { //设置数据跟状态的对应关系
* selected : 'selected',
* disabled : 'disabled'
* },
* store : store,
* plugins : [Grid.Plugins.CheckSelection] // 插件形式引入多选表格
* //multiSelect: true // 控制表格是否可以多选,但是这种方式没有前面的复选框 默认为false
* });
*
* grid.render();
*
* @class BUI.Grid.Plugins.CheckSelection
* @extends BUI.Base
*/
function checkSelection(config){
checkSelection.superclass.constructor.call(this, config);
}
BUI.extend(checkSelection,BUI.Base);
checkSelection.ATTRS =
{
/**
* column's width which contains the checkbox
*/
width : {
value : 40
},
/**
* @private
*/
column : {
},
/**
* @private
*
*/
cellInner : {
value : '
* // 实例化 Grid.Plugins.Cascade 插件
* var cascade = new Grid.Plugins.Cascade({
* renderer : function(record){
* return '详情信息
' + record.detail + '
';
* }
* });
* var store = new Store({
* data : data,
* autoLoad:true
* }),
* grid = new Grid.Grid({
* render:'#grid',
* columns : columns,
* store: store,
* plugins: [cascade] // Grid.Plugins.Cascade 插件
* });
*
* grid.render();
*
* cascade.expandAll();//展开所有
*
* @class BUI.Grid.Plugins.Cascade
* @extends BUI.Base
*/
var cascade = function(config){
cascade.superclass.constructor.call(this, config);
};
BUI.extend(cascade,BUI.Base);
cascade.ATTRS =
{
/**
* 显示展开按钮列的宽度
* @cfg {Number} width
*/
/**
* 显示展开按钮列的宽度
* @type {Number}
* @default 40
*/
width:{
value:40
},
/**
* 展开列的默认内容
* @type {String}
* @protected
*/
cellInner:{
value:''
},
/**
* 展开行的模版
* @protected
* @type {String}
*/
rowTpl : {
value:'
* cascade.expandAll();
*
*/
expandAll : function(){
var _self = this,
grid = _self.get('grid'),
records = grid.getRecords();
$.each(records,function(index,record){
_self.expand(record);
});
},
/**
* 展开某条纪录
*
* var record = grid.getItem('a');
* cascade.expand(record);
*
* @param {Object} record 纪录
*/
expand : function(record){
var _self = this,
grid = _self.get('grid');
var row = grid.findRow(record);
if(row){
_self._onExpand(record,row);
}
},
/**
* 折叠某条纪录
*
* var record = grid.getItem('a');
* cascade.collapse(record);
*
* @param {Object} record 纪录
*/
collapse : function(record){
var _self = this,
grid = _self.get('grid');
var row = grid.findRow(record);
if(row){
_self._onCollapse(record,row);
}
},
/**
* 移除所有级联数据的DOM
* @protected
*/
removeAll : function(){
var _self = this,
rows = _self._getAllCascadeRows();
rows.each(function(index,row){
_self._removeCascadeRow(row);
});
},
/**
* 根据纪录删除级联信息
* @protected
* @param {Object} record 级联信息对应的纪录
*/
remove : function(record){
var _self = this,
cascadeRow = _self._findCascadeRow(record);
if(cascadeRow){
_self._removeCascadeRow(cascadeRow);
}
},
/**
* 折叠所有级联数据
*
* cascade.collapseAll();
*
*/
collapseAll : function(){
var _self = this,
grid = _self.get('grid'),
records = grid.getRecords();
$.each(records,function(index,record){
_self.collapse(record);
});
},
//获取级联数据
_getRowRecord : function(cascadeRow){
return $(cascadeRow).data(DATA_RECORD);
},
//移除级联行
_removeCascadeRow : function(row){
this.fire('removed',{record: $(row).data(DATA_RECORD),row : row});
$(row).remove();
},
//通过纪录查找
_findCascadeRow: function(record){
var _self = this,
rows = _self._getAllCascadeRows(),
result = null;
$.each(rows,function(index,row){
if(_self._getRowRecord(row) === record){
result = row;
return false;
}
});
return result;
},
_getAllCascadeRows : function(){
var _self = this,
grid = _self.get('grid');
return grid.get('el').find('.' + CLS_CASCADE_ROW);
},
//获取生成的级联行
_getCascadeRow : function(gridRow){
var nextRow = $(gridRow).next();
if((nextRow).hasClass(CLS_CASCADE_ROW)){
return nextRow;
}
return null;
//return $(gridRow).next('.' + CLS_CASCADE_ROW);
},
//获取级联内容
_getRowContent : function(record){
var _self = this,
renderer = _self.get('renderer'),
content = renderer ? renderer(record) : '';
return content;
},
//创建级联行
_createCascadeRow : function(record,gridRow){
var _self = this,
rowTpl = _self.get('rowTpl'),
content = _self._getRowContent(record),
rowEl = $(rowTpl).insertAfter(gridRow);
rowEl.find('.' + CLS_CASCADE_CELL).append($(content));
rowEl.data(DATA_RECORD,record);
return rowEl;
},
//展开
_onExpand : function(record,row,cascadeEl){
var _self = this,
cascadeRow = _self._getCascadeRow(row),
colspan = _self._getColumnCount(row);
cascadeEl = cascadeEl || $(row).find('.'+CLS_CASCADE);
cascadeEl.addClass(CLS_CASCADE_EXPAND);
if(!cascadeRow || !cascadeRow.length){
cascadeRow = _self._createCascadeRow(record,row);
}
$(cascadeRow).removeClass(CLS_CASCADE_ROW_COLLAPSE);
_self._setColSpan(cascadeRow,row);
_self.fire('expand',{record : record,row : cascadeRow[0]});
},
//折叠
_onCollapse : function(record,row,cascadeEl){
var _self = this,
cascadeRow = _self._getCascadeRow(row);
cascadeEl = cascadeEl || $(row).find('.'+CLS_CASCADE);
cascadeEl.removeClass(CLS_CASCADE_EXPAND);
if(cascadeRow && cascadeRow.length){
$(cascadeRow).addClass(CLS_CASCADE_ROW_COLLAPSE);
_self.fire('collapse',{record : record,row : cascadeRow[0]});
}
},
//获取显示的列数
_getColumnCount : function(row){
return $(row).children().filter(function(){
return $(this).css('display') !== 'none';
}).length;
},
//设置colspan
_setColSpan : function(cascadeRow,gridRow){
gridRow = gridRow || $(cascadeRow).prev();
var _self = this,
colspan = _self._getColumnCount(gridRow);
$(cascadeRow).find('.' + CLS_CASCADE_CELL).attr('colspan',colspan)
},
//重置所有的colspan
_resetColspan : function(){
var _self = this,
cascadeRows = _self._getAllCascadeRows();
$.each(cascadeRows,function(index,cascadeRow){
_self._setColSpan(cascadeRow);
});
},
/**
* 析构函数
*/
destructor : function(){
var _self = this;
_self.removeAll();
_self.off();
_self.clearAttrVals();
}
});
module.exports = cascade;
});
define("bui/grid/plugins/cellediting", ["jquery"], function(require, exports, module){
/**
* @fileOverview 表格单元格编辑
* @ignore
*/
var $ = require("jquery"),
Editing = require("bui/grid/plugins/editing"),
CLS_BODY = BUI.prefix + 'grid-body',
CLS_CELL = BUI.prefix + 'grid-cell';
/**
* @class BUI.Grid.Plugins.CellEditing
* @extends BUI.Grid.Plugins.Editing
* 单元格编辑插件
*/
var CellEditing = function(config){
CellEditing.superclass.constructor.call(this, config);
};
CellEditing.ATTRS = {
/**
* 触发编辑样式,为空时默认点击整行都会触发编辑
* @cfg {String} [triggerCls = 'bui-grid-cell']
*/
triggerCls : {
value : CLS_CELL
}
};
BUI.extend(CellEditing,Editing);
BUI.augment(CellEditing,{
/**
* @protected
* 获取编辑器的配置项
* @param {Array} fields 字段配置
*/
getEditorCfgs : function(fields){
var _self = this,
grid = _self.get('grid'),
bodyNode = grid.get('el').find('.' + CLS_BODY),
rst = [];
BUI.each(fields,function(field){
var cfg = {field : field,changeSourceEvent : null,hideExceptNode : bodyNode,autoUpdate : false,preventHide : false,editableFn : field.editableFn};
if(field.xtype === 'checkbox'){
cfg.innerValueField = 'checked';
}
rst.push(cfg);
});
return rst;
},
/**
* 获取编辑器
* @protected
* @param {String} field 字段值
* @return {BUI.Editor.Editor} 编辑器
*/
getEditor : function(field){
if(!field){
return null;
}
var _self = this,
editors = _self.get('editors'),
editor = null;
BUI.each(editors,function(item){
if(item.get('field').get('name') === field){
editor = item;
return false;
}
});
return editor;
},
/**
* 显示编辑器前
* @protected
* @param {BUI.Editor.Editor} editor
* @param {Object} options
*/
beforeShowEditor : function(editor,options){
var _self = this,
cell = $(options.cell);
_self.resetWidth(editor,cell.outerWidth());
_self._makeEnable(editor,options);
},
_makeEnable : function(editor,options){
var editableFn = editor.get('editableFn'),
field,
enable,
record;
if(BUI.isFunction(editableFn)){
field = options.field;
record = options.record;
if(record && field){
enable = editableFn(record[field],record);
if(enable){
editor.get('field').enable();
}else{
editor.get('field').disable();
}
}
}
},
resetWidth : function(editor,width){
editor.set('width',width);
},
/**
* 更新数据
* @protected
* @param {Object} record 编辑的数据
* @param {*} value 编辑值
*/
updateRecord : function(store,record,editor){
var _self = this,
value = editor.getValue(),
fieldName = editor.get('field').get('name'),
preValue = record[fieldName];
value = BUI.isDate(value) ? value.getTime() : value;
if(preValue !== value){
store.setValue(record,fieldName,value);
}
},
/**
* @protected
* 获取对齐的节点
* @override
* @param {Object} options 点击单元格的事件对象
* @return {jQuery}
*/
getAlignNode : function(options){
return $(options.cell);
},
/**
* 获取编辑的字段
* @protected
* @return {Array} 字段集合
*/
getFields : function(){
var rst = [],
_self = this,
editors = _self.get('editors');
BUI.each(editors,function(editor){
rst.push(editor.get('field'));
});
return rst;
},
/**
* @protected
* 获取要编辑的值
* @param {Object} options 点击单元格的事件对象
* @return {*} 编辑的值
*/
getEditValue : function(options){
if(options.record && options.field){
var value = options.record[options.field];
return value == null ? '' : value;
}
return '';
}
});
module.exports = CellEditing;
});
define("bui/grid/plugins/editing", ["jquery"], function(require, exports, module){
/**
* @fileOverview 表格编辑插件
* @ignore
*/
var $ = require("jquery"),
CLS_CELL_INNER = BUI.prefix + 'grid-cell-inner',
CLS_CELL_ERROR = BUI.prefix + 'grid-cell-error';
/**
* 表格的编辑插件
* @class BUI.Grid.Plugins.Editing
*/
function Editing(config){
Editing.superclass.constructor.call(this, config);
}
BUI.extend(Editing,BUI.Base);
Editing.ATTRS = {
/**
* @protected
* 编辑器的对齐设置
* @type {Object}
*/
align : {
value : {
points: ['cl','cl']
}
},
/**
* 是否直接在表格上显示错误信息
* @type {Boolean}
*/
showError : {
value : true
},
errorTpl : {
value : ' '
},
/**
* 是否初始化过编辑器
* @protected
* @type {Boolean}
*/
isInitEditors : {
value : false
},
/**
* 正在编辑的记录
* @type {Object}
*/
record : {
},
/**
* 当前编辑的编辑器
* @type {Object}
*/
curEditor : {
},
/**
* 是否发生过验证
* @type {Boolean}
*/
hasValid : {
},
/**
* 编辑器
* @protected
* @type {Object}
*/
editors : {
shared:false,
value : []
},
/**
* 触发编辑样式,为空时默认点击整行都会触发编辑
* @type {String}
*/
triggerCls : {
},
/**
* 进行编辑时是否触发选中
* @type {Boolean}
*/
triggerSelected : {
value : true
}
/**
* @event accept
* 确认编辑
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event cancel
* 取消编辑
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event editorshow
* editor 显示
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event editorready
* editor 创建完成,因为editor延迟创建,所以创建完成grid,等待editor创建成功
*/
/**
* @event beforeeditorshow
* editor显示前,可以更改editor的一些属性
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
};
BUI.augment(Editing,{
/**
* 初始化
* @protected
*/
initializer : function (grid) {
var _self = this;
_self.set('grid',grid);
_self.initEditing(grid);
},
renderUI : function(){
var _self = this,
grid = _self.get('grid');
//延迟加载 editor模块
require.async('bui/editor',function(Editor){
_self.initEditors(Editor);
_self._initGridEvent(grid);
_self.set('isInitEditors',true);
_self.fire('editorready');
});
},
/**
* 初始化插件
* @protected
*/
initEditing : function(grid){
},
_getCurEditor : function(){
return this.get('curEditor');
},
_initGridEvent : function(grid){
var _self = this,
header = grid.get('header');
grid.on('cellclick',function(ev){
var editor = null,
domTarget = ev.domTarget,
triggerCls = _self.get('triggerCls'),
curEditor = _self._getCurEditor();
if(curEditor && curEditor.get('acceptEvent')){
curEditor.accept();
curEditor.hide();
}else{
curEditor && curEditor.cancel();
}
//if(ev.field){
editor = _self.getEditor(ev.field);
//}
if(editor && $(domTarget).closest('.' + triggerCls).length){
_self.showEditor(editor,ev);
//if(curEditor && curEditor.get('acceptEvent')){
if(!_self.get('triggerSelected')){
return false; //此时不触发选中事件
}
//}
}
});
grid.on('rowcreated',function(ev){
validRow(ev.record,ev.row);
});
grid.on('rowremoved',function(ev){
if(_self.get('record') == ev.record){
_self.cancel();
}
});
grid.on('rowupdated',function(ev){
validRow(ev.record,ev.row);
});
grid.on('scroll',function(ev){
var editor = _self._getCurEditor();
if(editor){
var align = editor.get('align'),
node = align.node,
pos = node.position();
if(pos.top < 0 || pos.top > ev.bodyHeight){
editor.hide();
}else{
editor.set('align',align);
editor.show();
}
}
});
header.on('afterVisibleChange',function(ev){
if(ev.target && ev.target != header){
var column = ev.target;
_self.onColumnVisibleChange(column);
}
});
function validRow(record,row){
if(_self.get('hasValid')){
_self.validRecord(record,_self.getFields(),$(row));
}
}
},
/**
* 初始化所有
* @protected
*/
initEditors : function(Editor){
var _self = this,
grid = _self.get('grid'),
fields = [],
columns = grid.get('columns');
BUI.each(columns,function(column){
var field = _self.getFieldConfig(column);
if(field){
field.name = column.get('dataIndex');
field.colId = column.get('id');
if(field.validator){
field.validator = _self.wrapValidator(field.validator);
}
fields.push(field);
}
});
var cfgs = _self.getEditorCfgs(fields);
BUI.each(cfgs,function(cfg){
_self.initEidtor(cfg,Editor);
});
},
/**
* @protected
* 获取列定义中的字段定义信息
* @param {BUI.Grid.Column} column 列定义
* @return {Object} 字段定义
*/
getFieldConfig : function(column){
return column.get('editor');
},
/**
* 封装验证方法
* @protected
*/
wrapValidator : function(validator){
var _self = this;
return function(value){
var record = _self.get('record');
return validator(value,record);
};
},
/**
* @protected
* 列显示隐藏时
*/
onColumnVisibleChange : function(column){
},
/**
* @protected
* 获取编辑器的配置
* @template
* @param {Array} fields 字段配置
* @return {Array} 编辑器的配置项
*/
getEditorCfgs : function(fields){
},
/**
* 获取编辑器的构造函数
* @param {Object} Editor 命名空间
* @return {Function} 构造函数
*/
getEditorConstructor : function(Editor){
return Editor.Editor;
},
/**
* 初始化编辑器
* @private
*/
initEidtor : function(cfg,Editor){
var _self = this,
con = _self.getEditorConstructor(Editor),
editor = new con(cfg);
editor.render();
_self.get('editors').push(editor);
_self.bindEidtor(editor);
return editor;
},
/**
* @protected
* 绑定编辑器事件
* @param {BUI.Editor.Editor} editor 编辑器
*/
bindEidtor : function(editor){
var _self = this,
grid = _self.get('grid'),
store = grid.get('store');
editor.on('accept',function(){
var record = _self.get('record');
_self.updateRecord(store,record,editor);
_self.fire('accept',{editor : editor,record : record});
_self.set('curEditor',null);
});
editor.on('cancel',function(){
_self.fire('cancel',{editor : editor,record : _self.get('record')});
_self.set('curEditor',null);
});
},
/**
* 获取编辑器
* @protected
* @param {String} field 字段值
* @return {BUI.Editor.Editor} 编辑器
*/
getEditor : function(options){
},
/**
* @protected
* 获取对齐的节点
* @template
* @param {Object} options 点击单元格的事件对象
* @return {jQuery}
*/
getAlignNode : function(options){
},
/**
* @protected
* 获取编辑的值
* @param {Object} options 点击单元格的事件对象
* @return {*} 编辑的值
*/
getEditValue : function(options){
},
/**
* 显示编辑器
* @protected
* @param {BUI.Editor.Editor} editor
*/
showEditor : function(editor,options){
var _self = this,
value = _self.getEditValue(options),
alignNode = _self.getAlignNode(options);
_self.beforeShowEditor(editor,options);
_self.set('record',options.record);
_self.fire('beforeeditorshow',{editor : editor,record : options.record});
editor.setValue(value);
if(alignNode){
var align = _self.get('align');
align.node = alignNode;
editor.set('align',align);
}
editor.show();
_self.focusEditor(editor,options.field);
_self.set('curEditor',editor);
_self.fire('editorshow',{editor : editor,record : options.record});
},
/**
* @protected
* 编辑器字段定位
*/
focusEditor : function(editor,field){
editor.focus();
},
/**
* 显示编辑器前
* @protected
* @template
* @param {BUI.Editor.Editor} editor
* @param {Object} options
*/
beforeShowEditor : function(editor,options){
},
//创建编辑的配置项
_createEditOptions : function(record,field){
var _self = this,
grid = _self.get('grid'),
rowEl = grid.findRow(record),
column = grid.findColumnByField(field),
cellEl = grid.findCell(column.get('id'),rowEl);
return {
record : record,
field : field,
cell : cellEl[0],
row : rowEl[0]
};
},
/**
* 验证表格是否通过验证
*/
valid : function(){
var _self = this,
grid = _self.get('grid'),
store = grid.get('store');
if(store){
var records = store.getResult();
BUI.each(records,function(record){
_self.validRecord(record,_self.getFields());
});
}
_self.set('hasValid',true);
},
isValid : function(){
var _self = this,
grid = _self.get('grid');
if(!_self.get('hasValid')){
_self.valid();
}
return !grid.get('el').find('.' + CLS_CELL_ERROR).length;
},
/**
* 清理错误
*/
clearErrors : function(){
var _self = this,
grid = _self.get('grid');
grid.get('el').find('.' + CLS_CELL_ERROR).remove();
},
/**
* 获取编辑的字段
* @protected
* @param {Array} editors 编辑器
* @return {Array} 字段集合
*/
getFields : function(editors){
},
/**
* 校验记录
* @protected
* @param {Object} record 校验的记录
* @param {Array} fields 字段的集合
*/
validRecord : function(record,fields,row){
var _self = this,
errors = [];
_self.setInternal('record',record);
fields = fields || _self.getFields();
BUI.each(fields,function(field){
var name = field.get('name'),
value = record[name] || '',
error = field.getValidError(value);
if(error){
errors.push({name : name,error : error,id : field.get('colId')});
}
});
_self.showRecordError(record,errors,row);
},
showRecordError : function(record,errors,row){
var _self = this,
grid = _self.get('grid');
row = row || grid.findRow(record);
if(row){
_self._clearRowError(row);
BUI.each(errors,function(item){
var cell = grid.findCell(item.id,row);
_self._showCellError(cell,item.error);
});
}
},
/**
* 更新数据
* @protected
* @param {Object} record 编辑的数据
* @param {*} value 编辑值
*/
updateRecord : function(store,record,editor){
},
_clearRowError : function(row){
row.find('.' + CLS_CELL_ERROR).remove();
},
_showCellError : function(cell,error){
var _self = this,
errorTpl = BUI.substitute(_self.get('errorTpl'),{error : error}),
innerEl = cell.find('.' + CLS_CELL_INNER);
$(errorTpl).appendTo(innerEl);
},
/**
* 编辑记录
* @param {Object} record 需要编辑的记录
* @param {String} field 编辑的字段
*/
edit : function(record,field){
var _self = this,
options = _self._createEditOptions(record,field),
editor = _self.getEditor(field);
_self.showEditor(editor,options);
},
/**
* 取消编辑
*/
cancel : function(){
var _self = this,
editors = _self.get('editors');
BUI.each(editors,function(editor){
if(editor.get('visible')){
editor.cancel();
}
});
_self.set('curEditor',null);
_self.set('record',null);
},
/**
* 析构函数
* @protected
*/
destructor:function () {
var _self = this,
editors = _self.get('editors');
BUI.each(editors,function(editor){
editor.destroy && editor.destroy();
});
_self.off();
_self.clearAttrVals();
}
});
module.exports = Editing;
});
define("bui/grid/plugins/rowediting", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 表格行编辑
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Editing = require("bui/grid/plugins/editing"),
CLS_ROW = BUI.prefix + 'grid-row';
/**
* @class BUI.Grid.Plugins.RowEditing
* @extends BUI.Grid.Plugins.Editing
* 单元格编辑插件
*
* ** 注意 **
*
* - 编辑器的定义在columns中,editor属性
* - editor里面的定义对应form-field的定义,xtype代表 form-field + xtype
* - validator 函数的函数原型 function(value,newRecord,originRecord){} //编辑的当前值,正在编辑的记录,原始记录
*/
var RowEditing = function(config){
RowEditing.superclass.constructor.call(this, config);
};
RowEditing.ATTRS = {
/**
* 是否自动保存数据到数据源,通过store的save方法实现
* @cfg {Object} [autoSave=false]
*/
autoSave : {
value : false
},
/**
* @protected
* 编辑器的对齐设置
* @type {Object}
*/
align : {
value : {
points: ['tl','tl'],
offset : [-2,0]
}
},
/**
* 触发编辑样式,为空时默认点击整行都会触发编辑
* @cfg {String} [triggerCls = 'bui-grid-row']
*/
triggerCls : {
value : CLS_ROW
},
/**
* 编辑器的默认配置信息
* @type {Object}
*/
editor : {
}
};
BUI.extend(RowEditing,Editing);
BUI.augment(RowEditing,{
/**
* @protected
* 获取编辑器的配置项
* @param {Array} fields 字段配置
*/
getEditorCfgs : function(fields){
var _self = this,
editor = _self.get('editor'),
rst = [],
cfg = BUI.mix(true,{
changeSourceEvent : null,
autoUpdate : false,
form : {
children : fields,
buttonBar : {
elCls : 'centered toolbar'
}
}
},editor);
rst.push(cfg);
return rst;
},
/**
* 封装验证方法
* @protected
*/
wrapValidator : function(validator){
var _self = this;
return function(value){
var editor = _self.get('curEditor'),
origin = _self.get('record'),
record = editor ? editor.getValue() : origin;
if(record){
return validator(value,record,origin);
}
};
},
/**
* @protected
* 编辑器字段定位
*/
focusEditor : function(editor,field){
var form = editor.get('form'),
control = form.getField(field);
if(control){
control.focus();
}
},
/**
* @protected
* 获取列定义中的字段定义信息
* @param {BUI.Grid.Column} column 列定义
* @return {Object} 字段定义
*/
getFieldConfig : function(column){
var editor = column.get('editor');
if(editor){
if(editor.xtype === 'checkbox'){
editor.innerValueField = 'checked';
}
return editor;
}
var cfg = {xtype : 'plain'};
if(column.get('dataIndex') && column.get('renderer')){
cfg.renderer = column.get('renderer');
//cfg.id = column.get('id');
}
return cfg;
},
/**
* 更新数据
* @protected
* @param {Object} record 编辑的数据
* @param {*} value 编辑值
*/
updateRecord : function(store,record,editor){
var _self = this,
value = editor.getValue();
BUI.each(value,function(v,k){
if(BUI.isDate(v)){
value[k] = v.getTime();
}
});
BUI.mix(record,value);
store.update(record);
if(_self.get('autoSave')){
store.save(record);
}
},
/**
* 获取编辑此行的编辑器
* @protected
* @param {String} field 点击单元格的字段
* @return {BUI.Editor.Editor} 编辑器
*/
getEditor : function(field){
var _self = this,
editors = _self.get('editors');
return editors[0];
},
/**
* @override
* 列发生改变
*/
onColumnVisibleChange : function(column){
var _self = this,
id = column.get('id'),
editor = _self.getEditor(),
field = editor.getChild(id,true);
if(field){
field.set('visible',column.get('visible'));
}
},
/**
* 显示编辑器前
* @protected
* @template
* @param {BUI.Editor.Editor} editor
* @param {Object} options
*/
beforeShowEditor : function(editor,options){
var _self = this,
grid = _self.get('grid'),
columns = grid.get('columns'),
form = editor.get('form'),
row = $(options.row);
editor.set('width',row.width());
BUI.each(columns,function(column){
var fieldName = column.get('dataIndex'),
field = form.getField(fieldName)
if(!column.get('visible')){
field && field.set('visible',false);
}else{
var
width = column.get('el').outerWidth() - field.getAppendWidth();
field.set('width',width);
}
});
},
/**
* @protected
* 获取要编辑的值
* @param {Object} options 点击单元格的事件对象
* @return {*} 编辑的值
*/
getEditValue : function(options){
return options.record;
},
/**
* 获取编辑器的构造函数
* @param {Object} Editor 命名空间
* @return {Function} 构造函数
*/
getEditorConstructor : function(Editor){
return Editor.RecordEditor;
},
/**
* @protected
* 获取对齐的节点
* @override
* @param {Object} options 点击单元格的事件对象
* @return {jQuery}
*/
getAlignNode : function(options){
return $(options.row);
},
/**
* 获取编辑的字段
* @protected
* @return {Array} 字段集合
*/
getFields : function(){
var _self = this,
editors = _self.get('editors');
return editors[0].get('form').get('children');
}
});
module.exports = RowEditing;
});
define("bui/grid/plugins/dialog", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 表格跟表单联用
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
TYPE_ADD = 'add',
TYPE_EDIT = 'edit';
/**
* 表格的编辑插件
* @class BUI.Grid.Plugins.DialogEditing
*/
function Dialog(config){
Dialog.superclass.constructor.call(this, config);
}
Dialog.ATTRS = {
/**
* 是否自动保存数据到数据源,通过store的save方法实现
* @cfg {Object} [autoSave=false]
*/
autoSave : {
value : false
},
/**
* 编辑的记录
* @type {Object}
* @readOnly
*/
record : {
},
/**
* @private
* 编辑记录的index
* @type {Object}
*/
curIndex : {
},
/**
* Dialog的内容,内部包含表单(form)
* @cfg {String} contentId
*/
/**
* Dialog的内容,内部包含表单(form)
* @type {String}
*/
contentId:{
},
/**
* 编辑器
* @type {BUI.Editor.DialogEditor}
* @readOnly
*/
editor : {
},
/**
* Dialog中的表单
* @type {BUI.Form.Form}
* @readOnly
*/
form : {
},
events : {
value : {
/**
* @event
* 编辑的记录发生更改
* @param {Object} e 事件对象
* @param {Object} e.record 记录
* @param {Object} e.editType 编辑的类型 add 或者 edit
*/
recordchange : false
/**
* @event accept
* 确认编辑
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Form.Form} form 表单
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event cancel
* 取消编辑
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Form.Form} form 表单
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event editorshow
* editor 显示
* @param {Object} ev 事件对象
* @param {Object} ev.record 编辑的数据
* @param {BUI.Editor.Editor} ev.editor 编辑器
*/
/**
* @event editorready
* editor 创建完成,因为editor延迟创建,所以创建完成grid,等待editor创建成功
*/
}
},
editType : {
}
};
BUI.extend(Dialog,BUI.Base);
BUI.augment(Dialog,{
/**
* 初始化
* @protected
*/
initializer : function (grid) {
var _self = this;
_self.set('grid',grid);
//延迟加载 editor模块
require.async('bui/editor',function(Editor){
_self._initEditor(Editor);
_self.fire('editorready');
});
},
bindUI : function(grid){
var _self = this,
triggerCls = _self.get('triggerCls');
if(triggerCls){
grid.on('cellclick',function(ev){
var sender = $(ev.domTarget),
editor = _self.get('editor');
if(sender.hasClass(triggerCls) && editor){
_self.edit(ev.record);
if(grid.get('multipleSelect')){
return false;
}
}
});
}
},
//初始化编辑器
_initEditor : function(Editor){
var _self = this,
contentId = _self.get('contentId'),
formNode = $('#' + contentId).find('form'),
editor = _self.get('editor'),
cfg = BUI.merge(editor,{
contentId : contentId,
form : {
srcNode : formNode
}
});
editor = new Editor.DialogEditor(cfg);
_self._bindEditor(editor);
_self.set('editor',editor);
_self.set('form',editor.get('form'));
},
//绑定编辑器事件
_bindEditor : function(editor){
var _self = this;
editor.on('accept',function(){
var form = editor.get('form'),
record = form.serializeToObject();
_self.saveRecord(record);
_self.fire('accept',{editor : editor,record : _self.get('record'),form : form});
});
editor.on('cancel',function(){
_self.fire('cancel',{editor : editor,record : _self.get('record'),form : editor.get('form')});
});
},
/**
* 编辑记录
* @param {Object} record 记录
*/
edit : function(record){
var _self = this;
_self.set('editType',TYPE_EDIT);
_self.showEditor(record);
},
/**
* 添加记录
* @param {Object} record 记录
* @param {Number} [index] 添加到的位置,默认添加在最后
*/
add : function(record,index){
var _self = this;
_self.set('editType',TYPE_ADD);
_self.set('curIndex',index);
_self.showEditor(record);
},
/**
* @private
* 保存记录
*/
saveRecord : function(record){
var _self = this,
grid = _self.get('grid'),
editType = _self.get('editType'),
curIndex = _self.get('curIndex'),
store = grid.get('store'),
curRecord = _self.get('record');
BUI.mix(curRecord,record);
if(editType == TYPE_ADD){
if(curIndex != null){
store.addAt(curRecord,curIndex);
}else{
store.add(curRecord);
}
}else{
store.update(curRecord);
}
if(_self.get('autoSave')){
store.save(curRecord);
}
},
/**
* @private
* 显示编辑器
*/
showEditor : function(record){
var _self = this,
editor = _self.get('editor');
_self.set('record',record);
editor.show();
editor.setValue(record,true); //设置值,并且隐藏错误
_self.fire('recordchange',{record : record,editType : _self.get('editType')});
_self.fire('editorshow',{eidtor : editor,editType : _self.get('editType')});
},
/**
* 取消编辑
*/
cancel : function(){
var _self = this,
editor = _self.get('editor');
editor.cancel();
},
destructor : function(){
var _self = this,
editor = _self.get('editor');
editor && editor.destroy();
_self.off();
_self.clearAttrVals();
}
});
module.exports = Dialog;
});
define("bui/grid/plugins/autofit", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 自动适应表格宽度的扩展
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
UA = BUI.UA;
/**
* 表格自适应宽度
* @class BUI.Grid.Plugins.AutoFit
* @extends BUI.Base
*/
var AutoFit = function(cfg){
AutoFit.superclass.constructor.call(this,cfg);
};
BUI.extend(AutoFit,BUI.Base);
AutoFit.ATTRS = {
};
BUI.augment(AutoFit,{
//绑定事件
bindUI : function(grid){
var _self = this,
handler;
$(window).on('resize',function(){
function autoFit(){
clearTimeout(handler); //防止resize短时间内反复调用
handler = setTimeout(function(){
_self._autoFit(grid);
},100);
_self.set('handler',handler);
}
autoFit();
});
},
//自适应宽度
_autoFit : function(grid){
var _self = this,
render = $(grid.get('render')),
docWidth = $(window).width(),//窗口宽度
width,
appendWidth = 0,
parent = grid.get('el').parent();
while(parent[0] && parent[0] != $('body')[0]){
appendWidth += parent.outerWidth() - parent.width();
parent = parent.parent();
}
grid.set('width',docWidth - appendWidth);
}
});
module.exports = AutoFit;
});
define("bui/grid/plugins/gridmenu", ["jquery","bui/common","bui/menu"], function(require, exports, module){
/**
* @fileOverview Grid 菜单
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
Menu = require("bui/menu"),
PREFIX = BUI.prefix,
ID_SORT_ASC = 'sort-asc',
ID_SORT_DESC = 'sort-desc',
ID_COLUMNS_SET = 'column-setting',
CLS_COLUMN_CHECKED = 'icon-check';
/**
* @class BUI.Grid.Plugins.GridMenu
* @extends BUI.Base
* 表格菜单插件
*/
var gridMenu = function (config) {
gridMenu.superclass.constructor.call(this,config);
};
BUI.extend(gridMenu,BUI.Base);
gridMenu.ATTRS =
{
/**
* 弹出菜单
* @type {BUI.Menu.ContextMenu}
*/
menu : {
},
/**
* @private
*/
activedColumn : {
},
triggerCls : {
value : PREFIX + 'grid-hd-menu-trigger'
},
/**
* 菜单的配置项
* @type {Array}
*/
items : {
value : [
{
id:ID_SORT_ASC,
text:'升序',
iconCls:'icon-arrow-up'
},
{
id:ID_SORT_DESC,
text:'降序',
iconCls : 'icon-arrow-down'
},
{
xclass:'menu-item-sparator'
},
{
id : ID_COLUMNS_SET,
text:'设置列',
iconCls:'icon-list-alt'
}
]
}
};
BUI.augment(gridMenu,{
/**
* 初始化
* @protected
*/
initializer : function (grid) {
var _self = this;
_self.set('grid',grid);
},
/**
* 渲染DOM
*/
renderUI : function(grid){
var _self = this,
columns = grid.get('columns');
BUI.each(columns,function(column){
_self._addShowMenu(column);
});
},
/**
* 绑定表格
* @protected
*/
bindUI : function (grid){
var _self = this;
grid.on('columnadd',function(ev){
_self._addShowMenu(ev.column);
});
grid.on('columnclick',function (ev) {
var sender = $(ev.domTarget),
column = ev.column,
menu;
_self.set('activedColumn',column);
if(sender.hasClass(_self.get('triggerCls'))){
menu = _self.get('menu') || _self._initMenu();
menu.set('align',{
node: sender, // 参考元素, falsy 或 window 为可视区域, 'trigger' 为触发元素, 其他为指定元素
points: ['bl','tl'], // ['tr', 'tl'] 表示 overlay 的 tl 与参考节点的 tr 对齐
offset: [0, 0]
});
menu.show();
_self._afterShow(column,menu);
}
});
},
_addShowMenu : function(column){
if(!column.get('fixed')){
column.set('showMenu',true);
}
},
//菜单显示后
_afterShow : function (column,menu) {
var _self = this,
grid = _self.get('grid');
menu = menu || _self.get('menu');
_self._resetSortMenuItems(column,menu);
_self._resetColumnsVisible(menu);
},
//设置菜单项是否选中
_resetColumnsVisible : function (menu) {
var _self = this,
settingItem = menu.findItemById(ID_COLUMNS_SET),
subMenu = settingItem.get('subMenu') || _self._initColumnsMenu(settingItem),
columns = _self.get('grid').get('columns');
subMenu.removeChildren(true);
$.each(columns,function (index,column) {
if(!column.get('fixed')){
var config = {
xclass : 'context-menu-item',
text : column.get('title'),
column : column,
iconCls : 'icon'
},
menuItem = subMenu.addChild(config);
if(column.get('visible')){
menuItem.set('selected',true);
}
}
});
},
//设置排序菜单项是否可用
_resetSortMenuItems : function(column,menu) {
var ascItem = menu.findItemById(ID_SORT_ASC),
descItem = menu.findItemById(ID_SORT_DESC);
if(column.get('sortable')){
ascItem.set('disabled',false);
descItem.set('disabled',false);
}else{
ascItem.set('disabled',true);
descItem.set('disabled',true);
}
},
//初始化菜单
_initMenu : function () {
var _self = this,
menu = _self.get('menu'),
menuItems;
if(!menu){
menuItems = _self.get('items');
$.each(menuItems,function (index,item) {
if(!item.xclass){
item.xclass = 'context-menu-item'
}
});
menu = new Menu.ContextMenu({
children : menuItems,
elCls : 'grid-menu'
});
_self._initMenuEvent(menu);
_self.set('menu',menu)
}
return menu;
},
_initMenuEvent : function (menu) {
var _self = this;
menu.on('itemclick',function(ev) {
var item = ev.item,
id = item.get('id'),
activedColumn = _self.get('activedColumn');
if(id === ID_SORT_ASC){
activedColumn.set('sortState','ASC');
}else if(id === ID_SORT_DESC){
activedColumn.set('sortState','DESC');
}
});
menu.on('afterVisibleChange',function (ev) {
var visible = ev.newVal,
activedColumn = _self.get('activedColumn');
if(visible && activedColumn){
activedColumn.set('open',true);
}else{
activedColumn.set('open',false);
}
});
},
_initColumnsMenu : function (settingItem) {
var subMenu = new Menu.ContextMenu({
multipleSelect : true,
elCls : 'grid-column-menu'
});
settingItem.set('subMenu',subMenu);
subMenu.on('itemclick',function (ev) {
var item = ev.item,
column = item.get('column'),
selected = item.get('selected');
if(selected){
column.set('visible',true);
}else{
column.set('visible',false);
}
});
return subMenu;
},
destructor:function () {
var _self = this,
menu = _self.get('menu');
if(menu){
menu.destroy();
}
_self.off();
_self.clearAttrVals();
}
});
module.exports = gridMenu;
});
define("bui/grid/plugins/summary", ["jquery","bui/common"], function(require, exports, module){
/**
* @fileOverview 表格数据汇总
* @author dxq613@gmail.com
* @ignore
*/
var $ = require("jquery"),
BUI = require("bui/common"),
PREFIX = BUI.prefix,
CLS_GRID_ROW = PREFIX + 'grid-row',
CLS_GRID_BODY = PREFIX + 'grid-body',
CLS_SUMMARY_ROW = PREFIX + 'grid-summary-row',
CLS_GRID_CELL_INNER = PREFIX + 'grid-cell-inner',
CLS_COLUMN_PREFIX = 'grid-td-',
CLS_GRID_CELL_TEXT = PREFIX + 'grid-cell-text',
CLS_GRID_CELL = PREFIX + 'grid-cell';
/**
* @private
* @ignore
*/
function getEmptyCellTemplate(colspan){
if(colspan > 0) {
return '
* var store = new Store({
* url : 'data/summary.json',
* pageSize : 10,
* autoLoad:true
* }),
* grid = new Grid.Grid({
* render:'#grid',
* columns : columns,
* store: store,
* bbar : {pagingBar : true},
* plugins : [Grid.Plugins.Summary] // 插件形式引入单选表格
* });
*
* grid.render();
*
* @class BUI.Grid.Plugins.Summary
*/
var summary = function (config) {
summary.superclass.constructor.call(this,config);
};
summary.ATTRS =
{
footerTpl : {
value : ''
},
footerEl : {
},
/**
* 总汇总行的标题
* @type {String}
* @default '总汇总'
*/
summaryTitle : {
value : '查询合计'
},
/**
* 本页汇总的标题
* @type {String}
*/
pageSummaryTitle : {
value : '本页合计'
},
/**
* 在列对象中配置的字段
* @type {String}
* @default 'summary'
*/
field : {
value : 'summary'
},
/**
* 本页汇总值的记录
* @type {String}
*/
pageSummaryField: {
value : 'pageSummary'
},
/**
* 总汇总值的记录
* @type {String}
*/
summaryField : {
value : 'summary'
},
/**
* @private
* 本页汇总值
* @type {Object}
*/
pageSummary : {
},
/**
* @private
* 总汇总
* @type {Object}
*/
summary : {
}
};
BUI.extend(summary,BUI.Base);
BUI.augment(summary,{
//初始化
initializer : function (grid) {
var _self = this;
_self.set('grid',grid);
},
//添加DOM结构
renderUI : function(grid){
var _self = this,
bodyEl = grid.get('el').find('.' + CLS_GRID_BODY),
bodyTable = bodyEl.find('table'),
footerEl = $(_self.get('footerTpl')).appendTo(bodyTable);
_self.set('footerEl',footerEl);
},
//绑定事件
bindUI : function(grid){
//绑定获取数据
var _self = this,
store = grid.get('store');
if(store){
store.on('beforeprocessload',function(ev){
_self._processSummary(ev.data);
});
store.on('add',function(){
_self.resetPageSummary();
});
store.on('remove',function(){
_self.resetPageSummary();
});
store.on('update',function(){
_self.resetPageSummary();
});
}
grid.on('aftershow',function(){
_self.resetSummary();
});
grid.get('header').on('afterVisibleChange',function(){
_self.resetSummary();
});
},
//处理汇总数据
_processSummary : function(data){
var _self = this,
footerEl = _self.get('footerEl');
footerEl.empty();
if(!data){
return;
}
var pageSummary = data[_self.get('pageSummaryField')],
summary = data[_self.get('summaryField')];
_self.set('pageSummary',pageSummary);
_self.set('summary',summary);
},
/**
* 重新设置本页汇总
*/
resetPageSummary : function(){
var _self = this,
grid = _self.get('grid'),
columns = grid.get('columns'),
pageSummary = _self._calculatePageSummary(),
pageEl = _self.get('pageEl');
_self.set('pageSummary',pageSummary);
if(pageEl){
BUI.each(columns,function(column){
if(column.get('summary') && column.get('visible')){
var id = column.get('id'),
cellEl = pageEl.find('.' + CLS_COLUMN_PREFIX + id),
text = _self._getSummaryCellText(column,pageSummary);
cellEl.find('.' + CLS_GRID_CELL_TEXT).text(text);
}
});
_self._updateFirstRow(pageEl,_self.get('pageSummaryTitle'));
}
},
//重置汇总数据
resetSummary : function(pageSummary,summary){
var _self = this,
footerEl = _self.get('footerEl'),
pageEl = null;
footerEl.empty();
pageSummary = pageSummary || _self.get('pageSummary');
if(!pageSummary){
pageSummary = _self._calculatePageSummary();
_self.set('pageSummary',pageSummary);
}
summary = summary || _self.get('summary');
pageEl = _self._creatSummaryRow(pageSummary,_self.get('pageSummaryTitle'));
_self.set('pageEl',pageEl);
_self._creatSummaryRow(summary,_self.get('summaryTitle'));
},
//创建汇总
_creatSummaryRow : function(summary,title){
if(!summary){
return null;
}
var _self = this,
footerEl = _self.get('footerEl'),
tpl = _self._getSummaryTpl(summary),
rowEl = $(tpl).appendTo(footerEl);
_self._updateFirstRow(rowEl,title);
return rowEl;
},
_updateFirstRow : function(rowEl,title){
var firstCell = rowEl.find('td').first(),
textEl = firstCell.find('.' + CLS_GRID_CELL_INNER);
if(textEl.length){
var textPrefix = title + ': ';
text = textEl.text();
if(text.indexOf(textPrefix) === -1){
text = textPrefix + text;
}
firstCell.html(getInnerTemplate(text));
}else{
firstCell.html(getInnerTemplate(title + ': '));
}
},
//获取汇总模板
_getSummaryTpl : function(summary){
var _self = this,
grid = _self.get('grid'),
columns = grid.get('columns'),
cellTempArray = [],
prePosition = -1, //上次汇总列的位置
currentPosition = -1,//当前位置
rowTemplate = null;
$.each(columns, function (colindex,column) {
if(column.get('visible')){
currentPosition += 1;
if(column.get('summary')){
cellTempArray.push(getEmptyCellTemplate(currentPosition-prePosition - 1));
var text = _self._getSummaryCellText(column,summary),
temp = getCellTemplate(text,column.get('id'));
cellTempArray.push(temp);
prePosition = currentPosition;
}
}
});
if(prePosition !== currentPosition){
cellTempArray.push(getEmptyCellTemplate(currentPosition-prePosition));
}
rowTemplate = ['