diff --git a/Gruntfile.js b/Gruntfile.js index e7af270ff6..0e8a8345c4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -133,7 +133,7 @@ module.exports = function (grunt) { dest: "dist/demo.css" }, fineuiJs: { - src: ["dist/polyfill.js", "dist/core.js", "dist/router.js", "dist/fix/fix.js", "dist/fix/fix.compact.js", "public/js/index.js", "dist/base.js", "dist/case.js", "dist/widget.js"], + src: ["dist/polyfill.js", "dist/core.js", "dist/fix/fix.js", "dist/fix/fix.compact.js", "public/js/index.js", "dist/base.js", "dist/case.js", "dist/widget.js", "dist/router.js"], dest: "dist/fineui.js" }, fineuiCss: { diff --git a/dist/fineui.js b/dist/fineui.js index 23d7263b3c..bf8045e257 100644 --- a/dist/fineui.js +++ b/dist/fineui.js @@ -33892,1078 +33892,494 @@ Data.Constant = BICst = {}; }; Data.Source = BISource = { -};(function () { - var Events = { +};function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - on: function (name, callback, context) { - if (!eventsApi(this, "on", name, [callback, context]) || !callback) return this; - this._events || (this._events = {}); - var events = this._events[name] || (this._events[name] = []); - events.push({callback: callback, context: context, ctx: context || this}); - return this; - }, +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : factory(global.Fix = global.Fix || {}); +})(this, function (exports) { + 'use strict'; - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. - once: function (name, callback, context) { - if (!eventsApi(this, "once", name, [callback, context]) || !callback) return this; - var self = this; - var once = _.once(function () { - self.off(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.on(name, once, context); - }, + function noop(a, b, c) {} - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `name` is null, removes all bound - // callbacks for all events. - off: function (name, callback, context) { - if (!this._events || !eventsApi(this, "off", name, [callback, context])) return this; + function isNative(Ctor) { + return typeof Ctor === 'function' && /native code/.test(Ctor.toString()); + } - // Remove all callbacks for all events. - if (!name && !callback && !context) { - this._events = void 0; - return this; - } + var rhashcode = /\d\.\d{4}/; - var names = name ? [name] : _.keys(this._events); - for (var i = 0, length = names.length; i < length; i++) { - name = names[i]; + //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript + function makeHashCode(prefix) { + /* istanbul ignore next*/ + prefix = prefix || 'bi'; + /* istanbul ignore next*/ + return String(Math.random() + Math.random()).replace(rhashcode, prefix); + } - // Bail out if there are no events stored. - var events = this._events[name]; - if (!events) continue; + var hasProto = '__proto__' in {}; - // Remove all callbacks for this event. - if (!callback && !context) { - delete this._events[name]; - continue; - } + var isIE = function isIE() { + return (/(msie|trident)/i.test(navigator.userAgent.toLowerCase()) + ); + }; - // Find any remaining events. - var remaining = []; - for (var j = 0, k = events.length; j < k; j++) { - var event = events[j]; - if ( - callback && callback !== event.callback && - callback !== event.callback._callback || - context && context !== event.context - ) { - remaining.push(event); - } - } + var getIEVersion = function getIEVersion() { + var version = 0; + var agent = navigator.userAgent.toLowerCase(); + var v1 = agent.match(/(?:msie\s([\w.]+))/); + var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); + if (v1 && v2 && v1[1] && v2[1]) { + version = Math.max(v1[1] * 1, v2[1] * 1); + } else if (v1 && v1[1]) { + version = v1[1] * 1; + } else if (v2 && v2[1]) { + version = v2[1] * 1; + } else { + version = 0; + } + return version; + }; + var isIE9Below = isIE() && getIEVersion() < 9; - // Replace events if there are any remaining. Otherwise, clean up. - if (remaining.length) { - this._events[name] = remaining; - } else { - delete this._events[name]; - } - } + var _toString = Object.prototype.toString; - return this; - }, + function isPlainObject(obj) { + return _toString.call(obj) === '[object Object]'; + } - un: function () { - this.off.apply(this, arguments); - }, + function remove(arr, item) { + if (arr && arr.length) { + var _index = arr.indexOf(item); + if (_index > -1) { + return arr.splice(_index, 1); + } + } + } - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function (name) { - if (!this._events) return this; - var args = slice.call(arguments, 1); - if (!eventsApi(this, "trigger", name, args)) return this; - var events = this._events[name]; - var allEvents = this._events.all; - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, arguments); - return this; - }, + var bailRE = /[^\w.$]/; - fireEvent: function () { - this.trigger.apply(this, arguments); - }, + function parsePath(path) { + if (bailRE.test(path)) { + return; + } + var segments = path.split('.'); + return function (obj) { + for (var i = 0; i < segments.length; i++) { + if (!obj) return; + obj = obj[segments[i]]; + } + return obj; + }; + } - // Inversion-of-control versions of `on` and `once`. Tell *this* object to - // listen to an event in another object ... keeping track of what it's - // listening to. - listenTo: function (obj, name, callback) { - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var id = obj._listenId || (obj._listenId = _.uniqueId("l")); - listeningTo[id] = obj; - if (!callback && typeof name === "object") callback = this; - obj.on(name, callback, this); - return this; - }, + var nextTick = function () { + var callbacks = []; + var pending = false; + var timerFunc = void 0; - listenToOnce: function (obj, name, callback) { - if (typeof name === "object") { - for (var event in name) this.listenToOnce(obj, event, name[event]); - return this; + function nextTickHandler() { + pending = false; + var copies = callbacks.slice(0); + callbacks.length = 0; + for (var i = 0; i < copies.length; i++) { + copies[i](); } - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, length = names.length; i < length; i++) { - this.listenToOnce(obj, names[i], callback); - } - return this; + } + + // An asynchronous deferring mechanism. + // In pre 2.4, we used to use microtasks (Promise/MutationObserver) + // but microtasks actually has too high a priority and fires in between + // supposedly sequential events (e.g. #4521, #6690) or even between + // bubbling of the same event (#6566). Technically setImmediate should be + // the ideal choice, but it's not available everywhere; and the only polyfill + // that consistently queues the callback after all DOM events triggered in the + // same loop is by using MessageChannel. + /* istanbul ignore if */ + if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { + timerFunc = function timerFunc() { + setImmediate(nextTickHandler); + }; + } else if (typeof MessageChannel !== 'undefined' && (isNative(MessageChannel) || + // PhantomJS + MessageChannel.toString() === '[object MessageChannelConstructor]')) { + var channel = new MessageChannel(); + var port = channel.port2; + channel.port1.onmessage = nextTickHandler; + timerFunc = function timerFunc() { + port.postMessage(1); + }; + } else + /* istanbul ignore next */ + if (typeof Promise !== 'undefined' && isNative(Promise)) { + // use microtask in non-DOM environments, e.g. Weex + var p = Promise.resolve(); + timerFunc = function timerFunc() { + p.then(nextTickHandler); + }; + } else { + // fallback to setTimeout + timerFunc = function timerFunc() { + setTimeout(nextTickHandler, 0); + }; } - if (!callback) return this; - var once = _.once(function () { - this.stopListening(obj, name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.listenTo(obj, name, once); - }, - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - stopListening: function (obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - var remove = !name && !callback; - if (!callback && typeof name === "object") callback = this; - if (obj) (listeningTo = {})[obj._listenId] = obj; - for (var id in listeningTo) { - obj = listeningTo[id]; - obj.off(name, callback, this); - if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; + return function queueNextTick(cb, ctx) { + var _resolve = void 0; + callbacks.push(function () { + if (cb) { + // try { + cb.call(ctx); + // } catch (e) { + // } + } else if (_resolve) { + _resolve(ctx); + } + }); + if (!pending) { + pending = true; + timerFunc(); } - return this; - } + // $flow-disable-line + if (!cb && typeof Promise !== 'undefined') { + return new Promise(function (resolve, reject) { + _resolve = resolve; + }); + } + }; + }(); + var falsy; + var $$skipArray = { + __ob__: falsy, + $accessors: falsy, + $vbthis: falsy, + $vbsetter: falsy }; - // Regular expression used to split event strings. - var eventSplitter = /\s+/; + var uid = 0; - // Implement fancy features of the Events API such as multiple event - // names `"change blur"` and jQuery-style event maps `{change: action}` - // in terms of the existing API. - var eventsApi = function (obj, action, name, rest) { - if (!name) return true; + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ - // Handle event maps. - if (typeof name === "object") { - for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); - } - return false; - } + var Dep = function () { + function Dep() { + _classCallCheck(this, Dep); - // Handle space separated event names. - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, length = names.length; i < length; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); - } - return false; + this.id = uid++; + this.subs = []; } - return true; - }; + Dep.prototype.addSub = function addSub(sub) { + this.subs.push(sub); + }; - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // BI events have 3 arguments). - var triggerEvents = function (events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: - while (++i < l) (ev = events[i]).callback.call(ev.ctx); - return; - case 1: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); - return; - case 2: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); - return; - case 3: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); - return; - default: - while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); - return; - } - }; + Dep.prototype.removeSub = function removeSub(sub) { + remove(this.subs, sub); + }; - // BI.Router - // --------------- + Dep.prototype.depend = function depend() { + if (Dep.target) { + Dep.target.addDep(this); + } + }; - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = BI.Router = function (options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this._init.apply(this, arguments); - }; + Dep.prototype.notify = function notify() { + // stabilize the subscriber list first + var subs = this.subs.slice(); + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /(\(\?)?:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + return Dep; + }(); - // Set up all inheritable **BI.Router** properties and methods. - _.extend(Router.prototype, Events, { + // the current target watcher being evaluated. + // this is globally unique because there could be only one + // watcher being evaluated at any time. - // _init is an empty function by default. Override it with your own - // initialization logic. - _init: function () { - }, - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function (route, name, callback) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (_.isFunction(name)) { - callback = name; - name = ""; - } - if (!callback) callback = this[name]; - var router = this; - BI.history.route(route, function (fragment) { - var args = router._extractParameters(route, fragment); - if (router.execute(callback, args, name) !== false) { - router.trigger.apply(router, ["route:" + name].concat(args)); - router.trigger("route", name, args); - BI.history.trigger("route", router, name, args); - } - }); - return this; - }, + Dep.target = null; + var targetStack = []; - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function (callback, args, name) { - if (callback) callback.apply(this, args); - }, + function pushTarget(_target) { + if (Dep.target) targetStack.push(Dep.target); + Dep.target = _target; + } - // Simple proxy to `BI.history` to save a fragment into the history. - navigate: function (fragment, options) { - BI.history.navigate(fragment, options); - return this; - }, + function popTarget() { + Dep.target = targetStack.pop(); + } - // Bind all defined routes to `BI.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function () { - if (!this.routes) return; - this.routes = _.result(this, "routes"); - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); + var arrayProto = Array.prototype; + var arrayMethods = []; + _.each(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'], function (method) { + var original = arrayProto[method]; + arrayMethods[method] = function mutator() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function (route) { - route = route.replace(escapeRegExp, "\\$&") - .replace(optionalParam, "(?:$1)?") - .replace(namedParam, function (match, optional) { - return optional ? match : "([^/?]+)"; - }) - .replace(splatParam, "([^?]*?)"); - return new RegExp("^" + route + "(?:\\?([\\s\\S]*))?$"); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function (route, fragment) { - var params = route.exec(fragment).slice(1); - return _.map(params, function (param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - return param ? decodeURIComponent(param) : null; - }); - } + var ob = this.__ob__; + var inserted = void 0; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break; + case 'splice': + inserted = args.slice(2); + break; + } + if (inserted) inserted = ob.observeArray(inserted); + switch (method) { + case 'push': + case 'unshift': + args = inserted; + break; + case 'splice': + args = [args[0], args[1]].concat(inserted ? inserted : []); + break; + } + var result = original.apply(this, args); + notify(ob.parent, ob.parentKey, ob.dep); + return result; + }; }); - // History - // ---------------- - - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = function () { - this.handlers = []; - this.checkUrl = _.bind(this.checkUrl, this); + //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 + //标准浏览器使用__defineGetter__, __defineSetter__实现 + var canHideProperty = true; + try { + Object.defineProperty({}, '_', { + value: 'x' + }); + delete $$skipArray.$vbsetter; + delete $$skipArray.$vbthis; + } catch (e) { + /* istanbul ignore next*/ + canHideProperty = false; + } - // Ensure that `History` can be used outside of the browser. - if (typeof window !== "undefined") { - this.location = window.location; - this.history = window.history; - } - }; + var createViewModel = Object.defineProperties; + var defineProperty = void 0; - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **BI.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Are we at the app root? - atRoot: function () { - var path = this.location.pathname.replace(/[^\/]$/, "$&/"); - return path === this.root && !this.getSearch(); - }, - - // In IE6, the hash fragment and search params are incorrect if the - // fragment contains `?`. - getSearch: function () { - var match = this.location.href.replace(/#.*/, "").match(/\?.+/); - return match ? match[0] : ""; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function (window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ""; - }, - - // Get the pathname and search params, without the root. - getPath: function () { - var path = decodeURI(this.location.pathname + this.getSearch()); - var root = this.root.slice(0, -1); - if (!path.indexOf(root)) path = path.slice(root.length); - return path.charAt(0) === "/" ? path.slice(1) : path; - }, - - // Get the cross-browser normalized URL fragment from the path or hash. - getFragment: function (fragment) { - if (fragment == null) { - if (this._hasPushState || !this._wantsHashChange) { - fragment = this.getPath(); - } else { - fragment = this.getHash(); + var timeBucket = new Date() - 0; + /* istanbul ignore if*/ + if (!canHideProperty) { + if ('__defineGetter__' in {}) { + defineProperty = function defineProperty(obj, prop, desc) { + if ('value' in desc) { + obj[prop] = desc.value; } - } - return fragment.replace(routeStripper, ""); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function (options) { - if (History.started) throw new Error("BI.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({root: "/"}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._hasHashChange = "onhashchange" in window; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); - this.fragment = this.getFragment(); - - // Normalize root to always include a leading and trailing slash. - this.root = ("/" + this.root + "/").replace(rootStripper, "/"); - - // Transition from hashChange to pushState or vice versa if both are - // requested. - if (this._wantsHashChange && this._wantsPushState) { - - // If we've started off with a route from a `pushState`-enabled - // browser, but we're currently in a browser that doesn't support it... - if (!this._hasPushState && !this.atRoot()) { - var root = this.root.slice(0, -1) || "/"; - this.location.replace(root + "#" + this.getPath()); - // Return immediately as browser will do redirect to new url - return true; - - // Or if we've started out with a hash-based route, but we're currently - // in a browser where it could be `pushState`-based instead... - } else if (this._hasPushState && this.atRoot()) { - this.navigate(this.getHash(), {replace: true}); + if ('get' in desc) { + obj.__defineGetter__(prop, desc.get); } - - } - - // Proxy an iframe to handle location events if the browser doesn't - // support the `hashchange` event, HTML5 history, or the user wants - // `hashChange` but not `pushState`. - if (!this._hasHashChange && this._wantsHashChange && (!this._wantsPushState || !this._hasPushState)) { - var iframe = document.createElement("iframe"); - iframe.src = "javascript:0"; - iframe.style.display = "none"; - iframe.tabIndex = -1; - var body = document.body; - // Using `appendChild` will throw on IE < 9 if the document is not ready. - this.iframe = body.insertBefore(iframe, body.firstChild).contentWindow; - this.iframe.document.open().close(); - this.iframe.location.hash = "#" + this.fragment; - } - - // Add a cross-platform `addEventListener` shim for older browsers. - var addEventListener = window.addEventListener || function (eventName, listener) { - return attachEvent("on" + eventName, listener); + if ('set' in desc) { + obj.__defineSetter__(prop, desc.set); + } + return obj; }; - - // Depending on whether we're using pushState or hashes, and whether - // 'onhashchange' is supported, determine how we check the URL state. - if (this._hasPushState) { - addEventListener("popstate", this.checkUrl, false); - } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { - addEventListener("hashchange", this.checkUrl, false); - } else if (this._wantsHashChange) { - this._checkUrlInterval = setInterval(this.checkUrl, this.interval); - } - - if (!this.options.silent) return this.loadUrl(); - }, - - // Disable BI.history, perhaps temporarily. Not useful in a real app, - // but possibly useful for unit testing Routers. - stop: function () { - // Add a cross-platform `removeEventListener` shim for older browsers. - var removeEventListener = window.removeEventListener || function (eventName, listener) { - return detachEvent("on" + eventName, listener); + createViewModel = function createViewModel(obj, descs) { + for (var prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]); + } + } + return obj; }; + } + /* istanbul ignore if*/ + if (isIE9Below) { + var VBClassPool = {}; + window.execScript([// jshint ignore:line + 'Function parseVB(code)', '\tExecuteGlobal(code)', 'End Function' //转换一段文本为VB代码 + ].join('\n'), 'VBScript'); - // Remove window listeners. - if (this._hasPushState) { - removeEventListener("popstate", this.checkUrl, false); - } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { - removeEventListener("hashchange", this.checkUrl, false); - } - - // Clean up the iframe if necessary. - if (this.iframe) { - document.body.removeChild(this.iframe.frameElement); - this.iframe = null; - } - - // Some environments will throw when clearing an undefined interval. - if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); - History.started = false; - }, - - // Add a route to be tested when the fragment changes. Routes added later - // may override previous routes. - route: function (route, callback) { - this.handlers.unshift({route: route, callback: callback}); - }, - - // Checks the current URL to see if it has changed, and if it has, - // calls `loadUrl`, normalizing across the hidden iframe. - checkUrl: function (e) { - var current = this.getFragment(); - - // If the user pressed the back button, the iframe's hash will have - // changed and we should use that for comparison. - if (current === this.fragment && this.iframe) { - current = this.getHash(this.iframe); - } - - if (current === this.fragment) return false; - if (this.iframe) this.navigate(current); - this.loadUrl(); - }, + var VBMediator = function VBMediator(instance, accessors, name, value) { + // jshint ignore:line + var accessor = accessors[name]; + if (arguments.length === 4) { + accessor.set.call(instance, value); + } else { + return accessor.get.call(instance); + } + }; + createViewModel = function createViewModel(name, accessors, properties) { + // jshint ignore:line + var buffer = []; + buffer.push('\tPrivate [$vbsetter]', '\tPublic [$accessors]', '\tPublic Default Function [$vbthis](ac' + timeBucket + ', s' + timeBucket + ')', '\t\tSet [$accessors] = ac' + timeBucket + ': set [$vbsetter] = s' + timeBucket, '\t\tSet [$vbthis] = Me', //链式调用 + '\tEnd Function'); + //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 + var uniq = { + $vbthis: true, + $vbsetter: true, + $accessors: true + }; + for (name in $$skipArray) { + if (!uniq[name]) { + buffer.push('\tPublic [' + name + ']'); + uniq[name] = true; + } + } + //添加访问器属性 + for (name in accessors) { + if (uniq[name]) { + continue; + } + uniq[name] = true; + buffer.push( + //由于不知对方会传入什么,因此set, let都用上 + '\tPublic Property Let [' + name + '](val' + timeBucket + ')', //setter + '\t\tCall [$vbsetter](Me, [$accessors], "' + name + '", val' + timeBucket + ')', '\tEnd Property', '\tPublic Property Set [' + name + '](val' + timeBucket + ')', //setter + '\t\tCall [$vbsetter](Me, [$accessors], "' + name + '", val' + timeBucket + ')', '\tEnd Property', '\tPublic Property Get [' + name + ']', //getter + '\tOn Error Resume Next', //必须优先使用set语句,否则它会误将数组当字符串返回 + '\t\tSet[' + name + '] = [$vbsetter](Me, [$accessors],"' + name + '")', '\tIf Err.Number <> 0 Then', '\t\t[' + name + '] = [$vbsetter](Me, [$accessors],"' + name + '")', '\tEnd If', '\tOn Error Goto 0', '\tEnd Property'); + } - // Attempt to load the current URL fragment. If a route succeeds with a - // match, returns `true`. If no defined routes matches the fragment, - // returns `false`. - loadUrl: function (fragment) { - fragment = this.fragment = this.getFragment(fragment); - return _.some(this.handlers, function (handler) { - if (handler.route.test(fragment)) { - handler.callback(fragment); - return true; + for (name in properties) { + if (!uniq[name]) { + uniq[name] = true; + buffer.push('\tPublic [' + name + ']'); + } } - }); - }, - // Save a fragment into the hash history, or replace the URL state if the - // 'replace' option is passed. You are responsible for properly URL-encoding - // the fragment in advance. - // - // The options object can contain `trigger: true` if you wish to have the - // route callback be fired (not usually desirable), or `replace: true`, if - // you wish to modify the current URL without adding an entry to the history. - navigate: function (fragment, options) { - if (!History.started) return false; - if (!options || options === true) options = {trigger: !!options}; + buffer.push('\tPublic [hasOwnProperty]'); + buffer.push('End Class'); + var body = buffer.join('\r\n'); + var className = VBClassPool[body]; + if (!className) { + className = makeHashCode('VBClass'); + window.parseVB('Class ' + className + body); + window.parseVB(['Function ' + className + 'Factory(acc, vbm)', //创建实例并传入两个关键的参数 + '\tDim o', '\tSet o = (New ' + className + ')(acc, vbm)', '\tSet ' + className + 'Factory = o', 'End Function'].join('\r\n')); + VBClassPool[body] = className; + } + var ret = window[className + 'Factory'](accessors, VBMediator); //得到其产品 + return ret; //得到其产品 + }; + } + } - // Normalize the fragment. - fragment = this.getFragment(fragment || ""); + var createViewModel$1 = createViewModel; - // Don't include a trailing slash on the root. - var root = this.root; - if (fragment === "" || fragment.charAt(0) === "?") { - root = root.slice(0, -1) || "/"; - } - var url = root + fragment; + var arrayKeys = _.keys(arrayMethods); - // Strip the hash and decode for matching. - fragment = decodeURI(fragment.replace(pathStripper, "")); + var observerState = { + shouldConvert: true + }; - if (this.fragment === fragment) return; - this.fragment = fragment; + function def(obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } - // If pushState is available, we use it to set the fragment as a real URL. - if (this._hasPushState) { - this.history[options.replace ? "replaceState" : "pushState"]({}, document.title, url); + /** + * Observer class that are attached to each observed + * object. Once attached, the observer converts target + * object's property keys into getter/setters that + * collect dependencies and dispatches updates. + */ - // If hash changes haven't been explicitly disabled, update the hash - // fragment to store history. - } else if (this._wantsHashChange) { - this._updateHash(this.location, fragment, options.replace); - if (this.iframe && (fragment !== this.getHash(this.iframe))) { - // Opening and closing the iframe tricks IE7 and earlier to push a - // history entry on hash-tag change. When replace is true, we don't - // want this. - if (!options.replace) this.iframe.document.open().close(); - this._updateHash(this.iframe.location, fragment, options.replace); - } + var Observer = function () { + function Observer(value) { + _classCallCheck(this, Observer); - // If you've told us that you explicitly don't want fallback hashchange- - // based history, then `navigate` becomes a page refresh. + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + if (_.isArray(value)) { + var augment = hasProto ? protoAugment : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.model = this.observeArray(value); } else { - return this.location.assign(url); + this.model = this.walk(value); } - if (options.trigger) return this.loadUrl(fragment); - }, - - // Update the hash location, either replacing the current entry, or adding - // a new one to the browser history. - _updateHash: function (location, fragment, replace) { - if (replace) { - var href = location.href.replace(/(javascript:|#).*$/, ""); - location.replace(href + "#" + fragment); + if (isIE9Below) { + this.model['__ob__'] = this; } else { - // Some browsers require that `hash` contains a leading #. - location.hash = "#" + fragment; + def(this.model, "__ob__", this); } } - }); - - // Create the default BI.history. - BI.history = new History; -}());function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : factory(global.Fix = global.Fix || {}); -})(this, function (exports) { - 'use strict'; - - function noop(a, b, c) {} - - function isNative(Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()); - } - - var rhashcode = /\d\.\d{4}/; - - //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript - function makeHashCode(prefix) { - /* istanbul ignore next*/ - prefix = prefix || 'bi'; - /* istanbul ignore next*/ - return String(Math.random() + Math.random()).replace(rhashcode, prefix); - } - - var hasProto = '__proto__' in {}; - - var isIE = function isIE() { - return (/(msie|trident)/i.test(navigator.userAgent.toLowerCase()) - ); - }; + Observer.prototype.walk = function walk(obj) { + return defineReactive(obj, this); + }; - var getIEVersion = function getIEVersion() { - var version = 0; - var agent = navigator.userAgent.toLowerCase(); - var v1 = agent.match(/(?:msie\s([\w.]+))/); - var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); - if (v1 && v2 && v1[1] && v2[1]) { - version = Math.max(v1[1] * 1, v2[1] * 1); - } else if (v1 && v1[1]) { - version = v1[1] * 1; - } else if (v2 && v2[1]) { - version = v2[1] * 1; - } else { - version = 0; - } - return version; - }; - var isIE9Below = isIE() && getIEVersion() < 9; + Observer.prototype.observeArray = function observeArray(items) { + for (var i = 0, l = items.length; i < l; i++) { + var ob = observe(items[i], this, i); + items[i] = ob ? ob.model : items[i]; + } + return items; + }; - var _toString = Object.prototype.toString; + return Observer; + }(); - function isPlainObject(obj) { - return _toString.call(obj) === '[object Object]'; + function protoAugment(target, src, keys) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ } - function remove(arr, item) { - if (arr && arr.length) { - var _index = arr.indexOf(item); - if (_index > -1) { - return arr.splice(_index, 1); - } + /* istanbul ignore next */ + function copyAugment(target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + target[key] = src[key]; } } - var bailRE = /[^\w.$]/; - - function parsePath(path) { - if (bailRE.test(path)) { + function observe(value, parentObserver, parentKey) { + if (!_.isObject(value)) { return; } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) return; - obj = obj[segments[i]]; - } - return obj; - }; + var ob = void 0; + if (value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if (observerState.shouldConvert && (_.isArray(value) || isPlainObject(value))) { + ob = new Observer(value); + } + ob.parent = parentObserver || ob.parent; + ob.parentKey = parentKey; + return ob; } - var nextTick = function () { - var callbacks = []; - var pending = false; - var timerFunc = void 0; - - function nextTickHandler() { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); + function notify(observer, key, dep) { + dep.notify(); + if (observer) { + //触发a.*绑定的依赖 + _.each(observer._deps, function (dep) { + dep.notify(); + }); + //触发a.**绑定的依赖 + var parent = observer, + root = observer, + route = key || ""; + while (parent) { + _.each(parent._scopeDeps, function (dep) { + dep.notify(); + }); + if (parent.parentKey != null) { + route = parent.parentKey + '.' + route; + } + root = parent; + parent = parent.parent; + } + for (var _key2 in root._globalDeps) { + var reg = new RegExp(_key2); + if (reg.test(route)) { + root._globalDeps[_key2].notify(); + } } } - - // An asynchronous deferring mechanism. - // In pre 2.4, we used to use microtasks (Promise/MutationObserver) - // but microtasks actually has too high a priority and fires in between - // supposedly sequential events (e.g. #4521, #6690) or even between - // bubbling of the same event (#6566). Technically setImmediate should be - // the ideal choice, but it's not available everywhere; and the only polyfill - // that consistently queues the callback after all DOM events triggered in the - // same loop is by using MessageChannel. - /* istanbul ignore if */ - if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - timerFunc = function timerFunc() { - setImmediate(nextTickHandler); - }; - } else if (typeof MessageChannel !== 'undefined' && (isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]')) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = nextTickHandler; - timerFunc = function timerFunc() { - port.postMessage(1); - }; - } else - /* istanbul ignore next */ - if (typeof Promise !== 'undefined' && isNative(Promise)) { - // use microtask in non-DOM environments, e.g. Weex - var p = Promise.resolve(); - timerFunc = function timerFunc() { - p.then(nextTickHandler); - }; - } else { - // fallback to setTimeout - timerFunc = function timerFunc() { - setTimeout(nextTickHandler, 0); - }; - } - - return function queueNextTick(cb, ctx) { - var _resolve = void 0; - callbacks.push(function () { - if (cb) { - // try { - cb.call(ctx); - // } catch (e) { - // } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - timerFunc(); - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve, reject) { - _resolve = resolve; - }); - } - }; - }(); - - var falsy; - var $$skipArray = { - __ob__: falsy, - $accessors: falsy, - $vbthis: falsy, - $vbsetter: falsy - }; - - var uid = 0; - - /** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ - - var Dep = function () { - function Dep() { - _classCallCheck(this, Dep); - - this.id = uid++; - this.subs = []; - } - - Dep.prototype.addSub = function addSub(sub) { - this.subs.push(sub); - }; - - Dep.prototype.removeSub = function removeSub(sub) { - remove(this.subs, sub); - }; - - Dep.prototype.depend = function depend() { - if (Dep.target) { - Dep.target.addDep(this); - } - }; - - Dep.prototype.notify = function notify() { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } - }; - - return Dep; - }(); - - // the current target watcher being evaluated. - // this is globally unique because there could be only one - // watcher being evaluated at any time. - - - Dep.target = null; - var targetStack = []; - - function pushTarget(_target) { - if (Dep.target) targetStack.push(Dep.target); - Dep.target = _target; - } - - function popTarget() { - Dep.target = targetStack.pop(); - } - - var arrayProto = Array.prototype; - var arrayMethods = []; - _.each(['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'], function (method) { - var original = arrayProto[method]; - arrayMethods[method] = function mutator() { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - var ob = this.__ob__; - var inserted = void 0; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break; - case 'splice': - inserted = args.slice(2); - break; - } - if (inserted) inserted = ob.observeArray(inserted); - switch (method) { - case 'push': - case 'unshift': - args = inserted; - break; - case 'splice': - args = [args[0], args[1]].concat(inserted ? inserted : []); - break; - } - var result = original.apply(this, args); - notify(ob.parent, ob.parentKey, ob.dep); - return result; - }; - }); - - //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 - //标准浏览器使用__defineGetter__, __defineSetter__实现 - var canHideProperty = true; - try { - Object.defineProperty({}, '_', { - value: 'x' - }); - delete $$skipArray.$vbsetter; - delete $$skipArray.$vbthis; - } catch (e) { - /* istanbul ignore next*/ - canHideProperty = false; - } - - var createViewModel = Object.defineProperties; - var defineProperty = void 0; - - var timeBucket = new Date() - 0; - /* istanbul ignore if*/ - if (!canHideProperty) { - if ('__defineGetter__' in {}) { - defineProperty = function defineProperty(obj, prop, desc) { - if ('value' in desc) { - obj[prop] = desc.value; - } - if ('get' in desc) { - obj.__defineGetter__(prop, desc.get); - } - if ('set' in desc) { - obj.__defineSetter__(prop, desc.set); - } - return obj; - }; - createViewModel = function createViewModel(obj, descs) { - for (var prop in descs) { - if (descs.hasOwnProperty(prop)) { - defineProperty(obj, prop, descs[prop]); - } - } - return obj; - }; - } - /* istanbul ignore if*/ - if (isIE9Below) { - var VBClassPool = {}; - window.execScript([// jshint ignore:line - 'Function parseVB(code)', '\tExecuteGlobal(code)', 'End Function' //转换一段文本为VB代码 - ].join('\n'), 'VBScript'); - - var VBMediator = function VBMediator(instance, accessors, name, value) { - // jshint ignore:line - var accessor = accessors[name]; - if (arguments.length === 4) { - accessor.set.call(instance, value); - } else { - return accessor.get.call(instance); - } - }; - createViewModel = function createViewModel(name, accessors, properties) { - // jshint ignore:line - var buffer = []; - buffer.push('\tPrivate [$vbsetter]', '\tPublic [$accessors]', '\tPublic Default Function [$vbthis](ac' + timeBucket + ', s' + timeBucket + ')', '\t\tSet [$accessors] = ac' + timeBucket + ': set [$vbsetter] = s' + timeBucket, '\t\tSet [$vbthis] = Me', //链式调用 - '\tEnd Function'); - //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 - var uniq = { - $vbthis: true, - $vbsetter: true, - $accessors: true - }; - for (name in $$skipArray) { - if (!uniq[name]) { - buffer.push('\tPublic [' + name + ']'); - uniq[name] = true; - } - } - //添加访问器属性 - for (name in accessors) { - if (uniq[name]) { - continue; - } - uniq[name] = true; - buffer.push( - //由于不知对方会传入什么,因此set, let都用上 - '\tPublic Property Let [' + name + '](val' + timeBucket + ')', //setter - '\t\tCall [$vbsetter](Me, [$accessors], "' + name + '", val' + timeBucket + ')', '\tEnd Property', '\tPublic Property Set [' + name + '](val' + timeBucket + ')', //setter - '\t\tCall [$vbsetter](Me, [$accessors], "' + name + '", val' + timeBucket + ')', '\tEnd Property', '\tPublic Property Get [' + name + ']', //getter - '\tOn Error Resume Next', //必须优先使用set语句,否则它会误将数组当字符串返回 - '\t\tSet[' + name + '] = [$vbsetter](Me, [$accessors],"' + name + '")', '\tIf Err.Number <> 0 Then', '\t\t[' + name + '] = [$vbsetter](Me, [$accessors],"' + name + '")', '\tEnd If', '\tOn Error Goto 0', '\tEnd Property'); - } - - for (name in properties) { - if (!uniq[name]) { - uniq[name] = true; - buffer.push('\tPublic [' + name + ']'); - } - } - - buffer.push('\tPublic [hasOwnProperty]'); - buffer.push('End Class'); - var body = buffer.join('\r\n'); - var className = VBClassPool[body]; - if (!className) { - className = makeHashCode('VBClass'); - window.parseVB('Class ' + className + body); - window.parseVB(['Function ' + className + 'Factory(acc, vbm)', //创建实例并传入两个关键的参数 - '\tDim o', '\tSet o = (New ' + className + ')(acc, vbm)', '\tSet ' + className + 'Factory = o', 'End Function'].join('\r\n')); - VBClassPool[body] = className; - } - var ret = window[className + 'Factory'](accessors, VBMediator); //得到其产品 - return ret; //得到其产品 - }; - } - } - - var createViewModel$1 = createViewModel; - - var arrayKeys = _.keys(arrayMethods); - - var observerState = { - shouldConvert: true - }; - - function def(obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); - } - - /** - * Observer class that are attached to each observed - * object. Once attached, the observer converts target - * object's property keys into getter/setters that - * collect dependencies and dispatches updates. - */ - - var Observer = function () { - function Observer(value) { - _classCallCheck(this, Observer); - - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - if (_.isArray(value)) { - var augment = hasProto ? protoAugment : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.model = this.observeArray(value); - } else { - this.model = this.walk(value); - } - if (isIE9Below) { - this.model['__ob__'] = this; - } else { - def(this.model, "__ob__", this); - } - } - - Observer.prototype.walk = function walk(obj) { - return defineReactive(obj, this); - }; - - Observer.prototype.observeArray = function observeArray(items) { - for (var i = 0, l = items.length; i < l; i++) { - var ob = observe(items[i], this, i); - items[i] = ob ? ob.model : items[i]; - } - return items; - }; - - return Observer; - }(); - - function protoAugment(target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ - } - - /* istanbul ignore next */ - function copyAugment(target, src, keys) { - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - target[key] = src[key]; - } - } - - function observe(value, parentObserver, parentKey) { - if (!_.isObject(value)) { - return; - } - var ob = void 0; - if (value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if (observerState.shouldConvert && (_.isArray(value) || isPlainObject(value))) { - ob = new Observer(value); - } - ob.parent = parentObserver || ob.parent; - ob.parentKey = parentKey; - return ob; - } - - function notify(observer, key, dep) { - dep.notify(); - if (observer) { - //触发a.*绑定的依赖 - _.each(observer._deps, function (dep) { - dep.notify(); - }); - //触发a.**绑定的依赖 - var parent = observer, - root = observer, - route = key || ""; - while (parent) { - _.each(parent._scopeDeps, function (dep) { - dep.notify(); - }); - if (parent.parentKey != null) { - route = parent.parentKey + '.' + route; - } - root = parent; - parent = parent.parent; - } - for (var _key2 in root._globalDeps) { - var reg = new RegExp(_key2); - if (reg.test(route)) { - root._globalDeps[_key2].notify(); - } - } - } - } + } function defineReactive(obj, observer, shallow) { var props = {}; @@ -104704,359 +104120,1321 @@ BI.YearPopup = BI.inherit(BI.Widget, { _createYearCalendar: function (v) { var o = this.options, y = this._year; - var calendar = BI.createWidget({ - type: "bi.year_calendar", - behaviors: o.behaviors, - min: o.min, - max: o.max, - logic: { - dynamic: true - }, - year: y + v * 12 - }); - calendar.setValue(this._year); - return calendar; - }, + var calendar = BI.createWidget({ + type: "bi.year_calendar", + behaviors: o.behaviors, + min: o.min, + max: o.max, + logic: { + dynamic: true + }, + year: y + v * 12 + }); + calendar.setValue(this._year); + return calendar; + }, + + _init: function () { + BI.YearPopup.superclass._init.apply(this, arguments); + var self = this, o = this.options; + + this.selectedYear = this._year = BI.getDate().getFullYear(); + + var backBtn = BI.createWidget({ + type: "bi.icon_button", + cls: "pre-page-h-font", + width: 25, + height: 25, + value: -1 + }); + + var preBtn = BI.createWidget({ + type: "bi.icon_button", + cls: "next-page-h-font", + width: 25, + height: 25, + value: 1 + }); + + this.navigation = BI.createWidget({ + type: "bi.navigation", + element: this, + single: true, + logic: { + dynamic: true + }, + tab: { + cls: "year-popup-navigation bi-high-light bi-border-top", + height: 25, + items: [backBtn, preBtn] + }, + cardCreator: BI.bind(this._createYearCalendar, this), + + afterCardShow: function () { + this.setValue(self.selectedYear); + var calendar = this.getSelectedCard(); + backBtn.setEnable(!calendar.isFrontYear()); + preBtn.setEnable(!calendar.isFinalYear()); + } + }); + + this.navigation.on(BI.Navigation.EVENT_CHANGE, function () { + self.selectedYear = this.getValue(); + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + self.fireEvent(BI.YearPopup.EVENT_CHANGE, self.selectedYear); + }); + + if(BI.isKey(o.value)){ + this.setValue(o.value); + } + }, + + getValue: function () { + return this.selectedYear; + }, + + setValue: function (v) { + var o = this.options; + if (BI.checkDateVoid(v, 1, 1, o.min, o.max)[0]) { + v = BI.getDate().getFullYear(); + this.selectedYear = ""; + this.navigation.setSelect(BI.YearCalendar.getPageByYear(v)); + this.navigation.setValue(""); + } else { + this.selectedYear = v; + this.navigation.setSelect(BI.YearCalendar.getPageByYear(v)); + this.navigation.setValue(v); + } + } +}); +BI.YearPopup.EVENT_CHANGE = "EVENT_CHANGE"; +BI.shortcut("bi.year_popup", BI.YearPopup);/** + * 年份trigger + * + * Created by GUY on 2015/8/21. + * @class BI.YearTrigger + * @extends BI.Trigger + */ +BI.YearTrigger = BI.inherit(BI.Trigger, { + _const: { + hgap: 4, + vgap: 2, + errorText: BI.i18nText("BI-Please_Input_Positive_Integer"), + errorTextInvalid: BI.i18nText("BI-Year_Trigger_Invalid_Text") + }, + + _defaultConfig: function () { + return BI.extend(BI.YearTrigger.superclass._defaultConfig.apply(this, arguments), { + extraCls: "bi-year-trigger bi-border", + min: "1900-01-01", // 最小日期 + max: "2099-12-31", // 最大日期 + height: 24 + }); + }, + _init: function () { + BI.YearTrigger.superclass._init.apply(this, arguments); + var self = this, o = this.options, c = this._const; + this.editor = BI.createWidget({ + type: "bi.sign_editor", + height: o.height, + validationChecker: function (v) { + self.editor.setErrorText(!BI.isPositiveInteger(v) ? c.errorText : c.errorTextInvalid); + return v === "" || (BI.isPositiveInteger(v) && !BI.checkDateVoid(v, 1, 1, o.min, o.max)[0]); + }, + quitChecker: function (v) { + return false; + }, + hgap: c.hgap, + vgap: c.vgap, + allowBlank: true, + errorText: c.errorText, + value: o.value + }); + this.editor.on(BI.SignEditor.EVENT_FOCUS, function () { + self.fireEvent(BI.YearTrigger.EVENT_FOCUS); + }); + this.editor.on(BI.SignEditor.EVENT_STOP, function () { + self.fireEvent(BI.YearTrigger.EVENT_STOP); + }); + this.editor.on(BI.SignEditor.EVENT_CONFIRM, function () { + var value = self.editor.getValue(); + if (BI.isNotNull(value)) { + self.editor.setValue(value); + self.editor.setTitle(value); + } + self.fireEvent(BI.YearTrigger.EVENT_CONFIRM); + }); + this.editor.on(BI.SignEditor.EVENT_SPACE, function () { + if (self.editor.isValid()) { + self.editor.blur(); + } + }); + this.editor.on(BI.SignEditor.EVENT_START, function () { + self.fireEvent(BI.YearTrigger.EVENT_START); + }); + this.editor.on(BI.SignEditor.EVENT_ERROR, function () { + self.fireEvent(BI.YearTrigger.EVENT_ERROR); + }); + BI.createWidget({ + element: this, + type: "bi.htape", + items: [ + { + el: this.editor + }, { + el: { + type: "bi.text_button", + baseCls: "bi-trigger-year-text", + text: BI.i18nText("BI-Multi_Date_Year"), + width: o.height + }, + width: o.height + }, { + el: { + type: "bi.trigger_icon_button", + width: o.height + }, + width: o.height + } + ] + }); + }, + setValue: function (v) { + this.editor.setState(v); + this.editor.setValue(v); + this.editor.setTitle(v); + }, + getKey: function () { + return this.editor.getValue() | 0; + } +}); +BI.YearTrigger.EVENT_FOCUS = "EVENT_FOCUS"; +BI.YearTrigger.EVENT_ERROR = "EVENT_ERROR"; +BI.YearTrigger.EVENT_START = "EVENT_START"; +BI.YearTrigger.EVENT_CONFIRM = "EVENT_CONFIRM"; +BI.YearTrigger.EVENT_STOP = "EVENT_STOP"; +BI.shortcut("bi.year_trigger", BI.YearTrigger);/** + * 年份 + 月份下拉框 + * + * @class BI.YearMonthCombo + * @extends BI.Widget + */ +BI.YearMonthCombo = BI.inherit(BI.Widget, { + _defaultConfig: function () { + return BI.extend(BI.YearMonthCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-year-month-combo", + yearBehaviors: {}, + monthBehaviors: {}, + height: 25 + }); + }, + _init: function () { + BI.YearMonthCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + + o.value = o.value || {}; + + this.year = BI.createWidget({ + type: "bi.year_combo", + behaviors: o.yearBehaviors, + value: o.value.year + }); + + this.month = BI.createWidget({ + type: "bi.month_combo", + behaviors: o.monthBehaviors, + value: o.value.month + }); + + this.year.on(BI.YearCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.YearMonthCombo.EVENT_CONFIRM); + }); + this.year.on(BI.YearCombo.EVENT_BEFORE_POPUPVIEW, function () { + self.fireEvent(BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW); + }); + + this.month.on(BI.MonthCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.YearMonthCombo.EVENT_CONFIRM); + }); + this.month.on(BI.MonthCombo.EVENT_BEFORE_POPUPVIEW, function () { + self.fireEvent(BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW); + }); + + BI.createWidget({ + type: "bi.center", + element: this, + hgap: 5, + items: [this.year, this.month] + }); + + }, + + setValue: function (v) { + v = v || {}; + this.month.setValue(v.month); + this.year.setValue(v.year); + }, + + getValue: function () { + return { + year: this.year.getValue(), + month: this.month.getValue() + }; + } +}); +BI.YearMonthCombo.EVENT_CONFIRM = "EVENT_CONFIRM"; +BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW"; +BI.shortcut("bi.year_month_combo", BI.YearMonthCombo);/** + * 年份 + 月份下拉框 + * + * @class BI.YearQuarterCombo + * @extends BI.Widget + */ +BI.YearQuarterCombo = BI.inherit(BI.Widget, { + _defaultConfig: function () { + return BI.extend(BI.YearQuarterCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-year-quarter-combo", + yearBehaviors: {}, + quarterBehaviors: {}, + height: 25 + }); + }, + _init: function () { + BI.YearQuarterCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + + o.value = o.value || {}; + + this.year = BI.createWidget({ + type: "bi.year_combo", + behaviors: o.yearBehaviors, + value: o.value.year + }); + + this.quarter = BI.createWidget({ + type: "bi.quarter_combo", + behaviors: o.quarterBehaviors, + value: o.value.quarter + }); + + this.year.on(BI.YearCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.YearQuarterCombo.EVENT_CONFIRM); + }); + this.year.on(BI.YearCombo.EVENT_BEFORE_POPUPVIEW, function () { + self.fireEvent(BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW); + }); + + this.quarter.on(BI.QuarterCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.YearQuarterCombo.EVENT_CONFIRM); + }); + this.quarter.on(BI.QuarterCombo.EVENT_BEFORE_POPUPVIEW, function () { + self.fireEvent(BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW); + }); + + BI.createWidget({ + type: "bi.center", + element: this, + hgap: 5, + items: [this.year, this.quarter] + }); + + }, + + setValue: function (v) { + v = v || {}; + this.quarter.setValue(v.quarter); + this.year.setValue(v.year); + }, + + getValue: function () { + return { + year: this.year.getValue(), + quarter: this.quarter.getValue() + }; + } +}); +BI.YearQuarterCombo.EVENT_CONFIRM = "EVENT_CONFIRM"; +BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW"; +BI.shortcut("bi.year_quarter_combo", BI.YearQuarterCombo);/** + * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 + * 封装了字段处理逻辑 + * + * Created by GUY on 2015/10/29. + * @class BI.AbstractAllValueChooser + * @extends BI.Widget + */ +BI.AbstractAllValueChooser = BI.inherit(BI.Widget, { + + _const: { + perPage: 100 + }, + + _defaultConfig: function () { + return BI.extend(BI.AbstractAllValueChooser.superclass._defaultConfig.apply(this, arguments), { + width: 200, + height: 30, + items: null, + itemsCreator: BI.emptyFn, + cache: true + }); + }, + + _valueFormatter: function (v) { + var text = v; + if (BI.isNotNull(this.items)) { + BI.some(this.items, function (i, item) { + if (item.value === v) { + text = item.text; + return true; + } + }); + } + return text; + }, + + _itemsCreator: function (options, callback) { + var self = this, o = this.options; + if (!o.cache || !this.items) { + o.itemsCreator({}, function (items) { + self.items = items; + call(items); + }); + } else { + call(this.items); + } + function call (items) { + var keywords = (options.keywords || []).slice(); + if (options.keyword) { + keywords.push(options.keyword); + } + BI.each(keywords, function (i, kw) { + var search = BI.Func.getSearchResult(items, kw); + items = search.match.concat(search.find); + }); + if (options.selectedValues) {// 过滤 + var filter = BI.makeObject(options.selectedValues, true); + items = BI.filter(items, function (i, ob) { + return !filter[ob.value]; + }); + } + if (options.type === BI.MultiSelectCombo.REQ_GET_ALL_DATA) { + callback({ + items: items + }); + return; + } + if (options.type === BI.MultiSelectCombo.REQ_GET_DATA_LENGTH) { + callback({count: items.length}); + return; + } + callback({ + items: items, + hasNext: false + }); + } + } +});/** + * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 + * 封装了字段处理逻辑 + * + * Created by GUY on 2015/10/29. + * @class BI.AllValueChooserCombo + * @extends BI.AbstractAllValueChooser + */ +BI.AllValueChooserCombo = BI.inherit(BI.AbstractAllValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.AllValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-all-value-chooser-combo", + width: 200, + height: 30, + items: null, + itemsCreator: BI.emptyFn, + cache: true + }); + }, + + _init: function () { + BI.AllValueChooserCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this.items = o.items; + } + this.combo = BI.createWidget({ + type: "bi.multi_select_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height, + value: { + type: BI.Selection.Multi, + value: o.value || [] + } + }); + + this.combo.on(BI.MultiSelectCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.AllValueChooserCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue({ + type: BI.Selection.Multi, + value: v || [] + }); + }, + + getValue: function () { + var val = this.combo.getValue() || {}; + if (val.type === BI.Selection.All) { + return val.assist; + } + return val.value || []; + }, + + populate: function () { + this.combo.populate.apply(this, arguments); + } +}); +BI.AllValueChooserCombo.EVENT_CONFIRM = "AllValueChooserCombo.EVENT_CONFIRM"; +BI.shortcut("bi.all_value_chooser_combo", BI.AllValueChooserCombo);/** + * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 + * 封装了字段处理逻辑 + * + * Created by GUY on 2015/10/29. + * @class BI.AllValueChooserPane + * @extends BI.AbstractAllValueChooser + */ +BI.AllValueChooserPane = BI.inherit(BI.AbstractAllValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.AllValueChooserPane.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-all-value-chooser-pane", + width: 200, + height: 30, + items: null, + itemsCreator: BI.emptyFn, + cache: true + }); + }, + + _init: function () { + BI.AllValueChooserPane.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this.items = o.items; + } + this.list = BI.createWidget({ + type: "bi.multi_select_list", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.list.on(BI.MultiSelectList.EVENT_CHANGE, function () { + self.fireEvent(BI.AllValueChooserPane.EVENT_CHANGE); + }); + }, + + setValue: function (v) { + this.list.setValue({ + type: BI.Selection.Multi, + value: v || [] + }); + }, + + getValue: function () { + var val = this.list.getValue() || {}; + if (val.type === BI.Selection.All) { + return val.assist; + } + return val.value || []; + }, + + populate: function () { + this.list.populate.apply(this.list, arguments); + } +}); +BI.AllValueChooserPane.EVENT_CHANGE = "AllValueChooserPane.EVENT_CHANGE"; +BI.shortcut("bi.all_value_chooser_pane", BI.AllValueChooserPane);BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { + + _const: { + perPage: 100 + }, + + _defaultConfig: function () { + return BI.extend(BI.AbstractTreeValueChooser.superclass._defaultConfig.apply(this, arguments), { + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _initData: function (items) { + this.items = items; + var nodes = BI.Tree.treeFormat(items); + this.tree = new BI.Tree(); + this.tree.initTree(nodes); + }, + + _itemsCreator: function (options, callback) { + var self = this, o = this.options; + if (!this.items) { + o.itemsCreator({}, function (items) { + self._initData(items); + call(); + }); + } else { + call(); + } + function call () { + switch (options.type) { + case BI.TreeView.REQ_TYPE_INIT_DATA: + self._reqInitTreeNode(options, callback); + break; + case BI.TreeView.REQ_TYPE_ADJUST_DATA: + self._reqAdjustTreeNode(options, callback); + break; + case BI.TreeView.REQ_TYPE_SELECT_DATA: + self._reqSelectedTreeNode(options, callback); + break; + case BI.TreeView.REQ_TYPE_GET_SELECTED_DATA: + self._reqDisplayTreeNode(options, callback); + break; + default : + self._reqTreeNode(options, callback); + break; + } + } + }, + + _reqDisplayTreeNode: function (op, callback) { + var self = this; + var result = []; + var selectedValues = op.selectedValues; + + if (selectedValues == null || BI.isEmpty(selectedValues)) { + callback({}); + return; + } + + doCheck([], this.tree.getRoot(), selectedValues); + + callback({ + items: result + }); + + function doCheck (parentValues, node, selected) { + if (selected == null || BI.isEmpty(selected)) { + BI.each(node.getChildren(), function (i, child) { + var newParents = BI.clone(parentValues); + newParents.push(child.value); + var llen = self._getChildCount(newParents); + createOneJson(child, node.id, llen); + doCheck(newParents, child, {}); + }); + return; + } + BI.each(selected, function (k) { + var node = self._getTreeNode(parentValues, k); + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + }); + } + + function getCount (jo, parentValues) { + if (jo == null) { + return 0; + } + if (BI.isEmpty(jo)) { + return self._getChildCount(parentValues); + } + + return BI.size(jo); + } + + function createOneJson (node, pId, llen) { + result.push({ + id: node.id, + pId: pId, + text: node.text + (llen > 0 ? ("(" + BI.i18nText("BI-Basic_Altogether") + llen + BI.i18nText("BI-Basic_Count") + ")") : ""), + value: node.value, + open: true + }); + } + }, + + _reqSelectedTreeNode: function (op, callback) { + var self = this; + var selectedValues = BI.deepClone(op.selectedValues); + var notSelectedValue = op.notSelectedValue || {}; + var keyword = op.keyword || ""; + var parentValues = op.parentValues || []; + + if (selectedValues == null || BI.isEmpty(selectedValues)) { + callback({}); + return; + } + + dealWithSelectedValues(selectedValues); + callback(selectedValues); + + + function dealWithSelectedValues (selectedValues) { + var p = parentValues.concat(notSelectedValue); + // 存储的值中存在这个值就把它删掉 + // 例如选中了中国-江苏-南京, 取消中国或江苏或南京 + if (canFindKey(selectedValues, p)) { + // 如果搜索的值在父亲链中 + if (isSearchValueInParent(p)) { + // 例如选中了 中国-江苏, 搜索江苏, 取消江苏 + // 例如选中了 中国-江苏, 搜索江苏, 取消中国 + self._deleteNode(selectedValues, p); + } else { + var searched = []; + var find = search(parentValues, notSelectedValue, [], searched); + if (find && BI.isNotEmptyArray(searched)) { + BI.each(searched, function (i, arr) { + var node = self._getNode(selectedValues, arr); + if (node) { + // 例如选中了 中国-江苏-南京,搜索南京,取消中国 + self._deleteNode(selectedValues, arr); + } else { + // 例如选中了 中国-江苏,搜索南京,取消中国 + expandSelectedValue(selectedValues, arr, BI.last(arr)); + } + }); + } + } + } + + // 存储的值中不存在这个值,但父亲节点是全选的情况 + // 例如选中了中国-江苏,取消南京 + // important 选中了中国-江苏,取消了江苏,但是搜索的是南京 + if (isChild(selectedValues, p)) { + var result = [], find = false; + // 如果parentValues中有匹配的值,说明搜索结果不在当前值下 + if (isSearchValueInParent(p)) { + find = true; + } else { + // 从当前值开始搜 + find = search(parentValues, notSelectedValue, result); + p = parentValues; + } + + if (find === true) { + // 去掉点击的节点之后的结果集 + expandSelectedValue(selectedValues, p, notSelectedValue); + // 添加去掉搜索的结果集 + if (result.length > 0) { + BI.each(result, function (i, strs) { + self._buildTree(selectedValues, strs); + }); + } + } + } + + } + + function expandSelectedValue (selectedValues, parents, notSelectedValue) { + var next = selectedValues; + var childrenCount = []; + var path = []; + // 去掉点击的节点之后的结果集 + BI.some(parents, function (i, v) { + var t = next[v]; + if (t == null) { + if (i === 0) { + return true; + } + if (BI.isEmpty(next)) { + var split = parents.slice(0, i); + var expanded = self._getChildren(split); + path.push(split); + childrenCount.push(expanded.length); + // 如果只有一个值且取消的就是这个值 + if (i === parents.length - 1 && expanded.length === 1 && expanded[0] === notSelectedValue) { + for (var j = childrenCount.length - 1; j >= 0; j--) { + if (childrenCount[j] === 1) { + self._deleteNode(selectedValues, path[j]); + } else { + break; + } + } + } else { + BI.each(expanded, function (m, child) { + if (i === parents.length - 1 && child.value === notSelectedValue) { + return true; + } + next[child.value] = {}; + }); + } + next = next[v]; + } else { + return true; + // next = {}; + // next[v] = {}; + } + } else { + next = t; + } + }); + } + + function search (parents, current, result, searched) { + var newParents = BI.clone(parents); + newParents.push(current); + if (self._isMatch(parents, current, keyword)) { + searched && searched.push(newParents); + return true; + } + + var children = self._getChildren(newParents); + + var notSearch = []; + var can = false; + + BI.each(children, function (i, child) { + if (search(newParents, child.value, result, searched)) { + can = true; + } else { + notSearch.push(child.value); + } + }); + if (can === true) { + BI.each(notSearch, function (i, v) { + var next = BI.clone(newParents); + next.push(v); + result.push(next); + }); + } + return can; + } + + function isSearchValueInParent (parentValues) { + for (var i = 0, len = parentValues.length; i < len; i++) { + if (self._isMatch(parentValues.slice(0, parentValues.length - 1), parentValues[i], keyword)) { + return true; + } + } + return false; + } + + function canFindKey (selectedValues, parents) { + var t = selectedValues; + for (var i = 0; i < parents.length; i++) { + var v = parents[i]; + t = t[v]; + if (t == null) { + return false; + } + } + return true; + } + + function isChild (selectedValues, parents) { + var t = selectedValues; + for (var i = 0; i < parents.length; i++) { + var v = parents[i]; + if (!BI.has(t, v)) { + return false; + } + t = t[v]; + if (BI.isEmpty(t)) { + return true; + } + } + return false; + } + }, + + _reqAdjustTreeNode: function (op, callback) { + var self = this; + var result = []; + var selectedValues = op.selectedValues; + if (selectedValues == null || BI.isEmpty(selectedValues)) { + callback({}); + return; + } + BI.each(selectedValues, function (k, v) { + result.push([k]); + }); + + dealWithSelectedValues(selectedValues, []); + + var jo = {}; + BI.each(result, function (i, strs) { + self._buildTree(jo, strs); + }); + callback(jo); + + function dealWithSelectedValues (selected, parents) { + if (selected == null || BI.isEmpty(selected)) { + return true; + } + var can = true; + BI.each(selected, function (k, v) { + var p = BI.clone(parents); + p.push(k); + if (!dealWithSelectedValues(selected[k], p)) { + BI.each(selected[k], function (nk, nv) { + var t = BI.clone(p); + t.push(nk); + result.push(t); + }); + can = false; + } + }); + return can && isAllSelected(selected, parents); + } + + function isAllSelected (selected, parents) { + return BI.isEmpty(selected) || self._getChildCount(parents) === BI.size(selected); + } + }, + + _reqInitTreeNode: function (op, callback) { + var self = this; + var result = []; + var keyword = op.keyword || ""; + var selectedValues = op.selectedValues; + var lastSearchValue = op.lastSearchValue || ""; + var output = search(); + BI.nextTick(function () { + callback({ + hasNext: output.length > self._const.perPage, + items: result, + lastSearchValue: BI.last(output) + }); + }); + + function search () { + var children = self._getChildren([]); + var start = children.length; + if (lastSearchValue !== "") { + for (var j = 0, len = start; j < len; j++) { + if (children[j].value === lastSearchValue) { + start = j + 1; + break; + } + } + } else { + start = 0; + } + var output = []; + for (var i = start, len = children.length; i < len; i++) { + if (output.length < self._const.perPage) { + var find = nodeSearch(1, [], children[i].value, false, result); + } else if (output.length === self._const.perPage) { + var find = nodeSearch(1, [], children[i].value, false, []); + } + if (find[0] === true) { + output.push(children[i].value); + } + if (output.length > self._const.perPage) { + break; + } + } + return output; + } + + function nodeSearch (deep, parentValues, current, isAllSelect, result) { + if (self._isMatch(parentValues, current, keyword)) { + var checked = isAllSelect || isSelected(parentValues, current); + createOneJson(parentValues, current, false, checked, !isAllSelect && isHalf(parentValues, current), true, result); + return [true, checked]; + } + var newParents = BI.clone(parentValues); + newParents.push(current); + var children = self._getChildren(newParents); + + var can = false, checked = false; + + var isCurAllSelected = isAllSelect || isAllSelected(parentValues, current); + BI.each(children, function (i, child) { + var state = nodeSearch(deep + 1, newParents, child.value, isCurAllSelected, result); + if (state[1] === true) { + checked = true; + } + if (state[0] === true) { + can = true; + } + }); + if (can === true) { + checked = isCurAllSelected || (isSelected(parentValues, current) && checked); + createOneJson(parentValues, current, true, checked, false, false, result); + } + return [can, checked]; + } + + function createOneJson (parentValues, value, isOpen, checked, half, flag, result) { + var node = self._getTreeNode(parentValues, value); + result.push({ + id: node.id, + pId: node.pId, + text: node.text, + value: node.value, + title: node.title, + isParent: node.getChildrenLength() > 0, + open: isOpen, + checked: checked, + halfCheck: half, + flag: flag + }); + } + + function isHalf (parentValues, value) { + var find = findSelectedObj(parentValues); + if (find == null) { + return null; + } + return BI.any(find, function (v, ob) { + if (v === value) { + if (ob != null && !BI.isEmpty(ob)) { + return true; + } + } + }); + } - _init: function () { - BI.YearPopup.superclass._init.apply(this, arguments); - var self = this, o = this.options; + function isAllSelected (parentValues, value) { + var find = findSelectedObj(parentValues); + if (find == null) { + return null; + } + return BI.any(find, function (v, ob) { + if (v === value) { + if (ob != null && BI.isEmpty(ob)) { + return true; + } + } + }); + } - this.selectedYear = this._year = BI.getDate().getFullYear(); + function isSelected (parentValues, value) { + var find = findSelectedObj(parentValues); + if (find == null) { + return false; + } + return BI.any(find, function (v) { + if (v === value) { + return true; + } + }); + } - var backBtn = BI.createWidget({ - type: "bi.icon_button", - cls: "pre-page-h-font", - width: 25, - height: 25, - value: -1 - }); + function findSelectedObj (parentValues) { + var find = selectedValues; + if (find == null) { + return null; + } + BI.every(parentValues, function (i, v) { + find = find[v]; + if (find == null) { + return false; + } + return true; + }); + return find; + } + }, - var preBtn = BI.createWidget({ - type: "bi.icon_button", - cls: "next-page-h-font", - width: 25, - height: 25, - value: 1 + _reqTreeNode: function (op, callback) { + var self = this; + var result = []; + var times = op.times; + var checkState = op.checkState || {}; + var parentValues = op.parentValues || []; + var selectedValues = op.selectedValues || {}; + var valueMap = {}; + // if (judgeState(parentValues, selectedValues, checkState)) { + valueMap = dealWidthSelectedValue(parentValues, selectedValues); + // } + var nodes = this._getChildren(parentValues); + for (var i = (times - 1) * this._const.perPage; nodes[i] && i < times * this._const.perPage; i++) { + var state = getCheckState(nodes[i].value, parentValues, valueMap, checkState); + result.push({ + id: nodes[i].id, + pId: nodes[i].pId, + value: nodes[i].value, + text: nodes[i].text, + times: 1, + isParent: nodes[i].getChildrenLength() > 0, + checked: state[0], + halfCheck: state[1] + }); + } + BI.nextTick(function () { + callback({ + items: result, + hasNext: nodes.length > times * self._const.perPage + }); }); - this.navigation = BI.createWidget({ - type: "bi.navigation", - element: this, - single: true, - logic: { - dynamic: true - }, - tab: { - cls: "year-popup-navigation bi-high-light bi-border-top", - height: 25, - items: [backBtn, preBtn] - }, - cardCreator: BI.bind(this._createYearCalendar, this), - - afterCardShow: function () { - this.setValue(self.selectedYear); - var calendar = this.getSelectedCard(); - backBtn.setEnable(!calendar.isFrontYear()); - preBtn.setEnable(!calendar.isFinalYear()); + function judgeState (parentValues, selected_value, checkState) { + var checked = checkState.checked, half = checkState.half; + if (parentValues.length > 0 && !checked) { + return false; } - }); + return (parentValues.length === 0 || (checked && half) && !BI.isEmpty(selected_value)); + } - this.navigation.on(BI.Navigation.EVENT_CHANGE, function () { - self.selectedYear = this.getValue(); - self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); - self.fireEvent(BI.YearPopup.EVENT_CHANGE, self.selectedYear); - }); + function dealWidthSelectedValue (parentValues, selectedValues) { + var valueMap = {}; + BI.each(parentValues, function (i, v) { + selectedValues = selectedValues[v] || {}; + }); + BI.each(selectedValues, function (value, obj) { + if (BI.isNull(obj)) { + valueMap[value] = [0, 0]; + return; + } + if (BI.isEmpty(obj)) { + valueMap[value] = [2, 0]; + return; + } + var nextNames = {}; + BI.each(obj, function (t, o) { + if (BI.isNull(o) || BI.isEmpty(o)) { + nextNames[t] = true; + } + }); + valueMap[value] = [1, BI.size(nextNames)]; + }); + return valueMap; + } - if(BI.isKey(o.value)){ - this.setValue(o.value); + function getCheckState (current, parentValues, valueMap, checkState) { + var checked = checkState.checked, half = checkState.half; + var tempCheck = false, halfCheck = false; + if (BI.has(valueMap, current)) { + // 可能是半选 + if (valueMap[current][0] === 1) { + var values = BI.clone(parentValues); + values.push(current); + var childCount = self._getChildCount(values); + if (childCount > 0 && childCount !== valueMap[current][1]) { + halfCheck = true; + } + } else if (valueMap[current][0] === 2) { + tempCheck = true; + } + } + var check; + if (!checked && !halfCheck && !tempCheck) { + check = BI.has(valueMap, current); + } else { + check = ((tempCheck || checked) && !half) || BI.has(valueMap, current); + } + return [check, halfCheck]; } }, - getValue: function () { - return this.selectedYear; + _getNode: function (selectedValues, parentValues) { + var pNode = selectedValues; + for (var i = 0, len = parentValues.length; i < len; i++) { + if (pNode == null) { + return null; + } + pNode = pNode[parentValues[i]]; + } + return pNode; }, - setValue: function (v) { - var o = this.options; - if (BI.checkDateVoid(v, 1, 1, o.min, o.max)[0]) { - v = BI.getDate().getFullYear(); - this.selectedYear = ""; - this.navigation.setSelect(BI.YearCalendar.getPageByYear(v)); - this.navigation.setValue(""); - } else { - this.selectedYear = v; - this.navigation.setSelect(BI.YearCalendar.getPageByYear(v)); - this.navigation.setValue(v); + _deleteNode: function (selectedValues, values) { + var name = values[values.length - 1]; + var p = values.slice(0, values.length - 1); + var pNode = this._getNode(selectedValues, p); + if (pNode != null && pNode[name]) { + delete pNode[name]; + // 递归删掉空父节点 + while (p.length > 0 && BI.isEmpty(pNode)) { + name = p[p.length - 1]; + p = p.slice(0, p.length - 1); + pNode = this._getNode(selectedValues, p); + if (pNode != null) { + delete pNode[name]; + } + } } - } -}); -BI.YearPopup.EVENT_CHANGE = "EVENT_CHANGE"; -BI.shortcut("bi.year_popup", BI.YearPopup);/** - * 年份trigger - * - * Created by GUY on 2015/8/21. - * @class BI.YearTrigger - * @extends BI.Trigger - */ -BI.YearTrigger = BI.inherit(BI.Trigger, { - _const: { - hgap: 4, - vgap: 2, - errorText: BI.i18nText("BI-Please_Input_Positive_Integer"), - errorTextInvalid: BI.i18nText("BI-Year_Trigger_Invalid_Text") }, - _defaultConfig: function () { - return BI.extend(BI.YearTrigger.superclass._defaultConfig.apply(this, arguments), { - extraCls: "bi-year-trigger bi-border", - min: "1900-01-01", // 最小日期 - max: "2099-12-31", // 最大日期 - height: 24 + _buildTree: function (jo, values) { + var t = jo; + BI.each(values, function (i, v) { + if (!BI.has(t, v)) { + t[v] = {}; + } + t = t[v]; }); }, - _init: function () { - BI.YearTrigger.superclass._init.apply(this, arguments); - var self = this, o = this.options, c = this._const; - this.editor = BI.createWidget({ - type: "bi.sign_editor", - height: o.height, - validationChecker: function (v) { - self.editor.setErrorText(!BI.isPositiveInteger(v) ? c.errorText : c.errorTextInvalid); - return v === "" || (BI.isPositiveInteger(v) && !BI.checkDateVoid(v, 1, 1, o.min, o.max)[0]); - }, - quitChecker: function (v) { + + _isMatch: function (parentValues, value, keyword) { + var node = this._getTreeNode(parentValues, value); + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }, + + _getTreeNode: function (parentValues, v) { + var self = this; + var findParentNode; + var index = 0; + this.tree.traverse(function (node) { + if (self.tree.isRoot(node)) { + return; + } + if (index > parentValues.length) { return false; - }, - hgap: c.hgap, - vgap: c.vgap, - allowBlank: true, - errorText: c.errorText, - value: o.value - }); - this.editor.on(BI.SignEditor.EVENT_FOCUS, function () { - self.fireEvent(BI.YearTrigger.EVENT_FOCUS); - }); - this.editor.on(BI.SignEditor.EVENT_STOP, function () { - self.fireEvent(BI.YearTrigger.EVENT_STOP); - }); - this.editor.on(BI.SignEditor.EVENT_CONFIRM, function () { - var value = self.editor.getValue(); - if (BI.isNotNull(value)) { - self.editor.setValue(value); - self.editor.setTitle(value); } - self.fireEvent(BI.YearTrigger.EVENT_CONFIRM); - }); - this.editor.on(BI.SignEditor.EVENT_SPACE, function () { - if (self.editor.isValid()) { - self.editor.blur(); + if (index === parentValues.length && node.value === v) { + findParentNode = node; + return false; } + if (node.value === parentValues[index]) { + index++; + return; + } + return true; }); - this.editor.on(BI.SignEditor.EVENT_START, function () { - self.fireEvent(BI.YearTrigger.EVENT_START); - }); - this.editor.on(BI.SignEditor.EVENT_ERROR, function () { - self.fireEvent(BI.YearTrigger.EVENT_ERROR); - }); - BI.createWidget({ - element: this, - type: "bi.htape", - items: [ - { - el: this.editor - }, { - el: { - type: "bi.text_button", - baseCls: "bi-trigger-year-text", - text: BI.i18nText("BI-Multi_Date_Year"), - width: o.height - }, - width: o.height - }, { - el: { - type: "bi.trigger_icon_button", - width: o.height - }, - width: o.height - } - ] - }); + return findParentNode; }, - setValue: function (v) { - this.editor.setState(v); - this.editor.setValue(v); - this.editor.setTitle(v); + + _getChildren: function (parentValues) { + if (parentValues.length > 0) { + var value = BI.last(parentValues); + var parent = this._getTreeNode(parentValues.slice(0, parentValues.length - 1), value); + } else { + var parent = this.tree.getRoot(); + } + return parent.getChildren(); }, - getKey: function () { - return this.editor.getValue() | 0; + + _getChildCount: function (parentValues) { + return this._getChildren(parentValues).length; } -}); -BI.YearTrigger.EVENT_FOCUS = "EVENT_FOCUS"; -BI.YearTrigger.EVENT_ERROR = "EVENT_ERROR"; -BI.YearTrigger.EVENT_START = "EVENT_START"; -BI.YearTrigger.EVENT_CONFIRM = "EVENT_CONFIRM"; -BI.YearTrigger.EVENT_STOP = "EVENT_STOP"; -BI.shortcut("bi.year_trigger", BI.YearTrigger);/** - * 年份 + 月份下拉框 +});/** + * 简单的复选下拉树控件, 适用于数据量少的情况 * - * @class BI.YearMonthCombo + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserCombo * @extends BI.Widget */ -BI.YearMonthCombo = BI.inherit(BI.Widget, { +BI.TreeValueChooserCombo = BI.inherit(BI.AbstractTreeValueChooser, { + _defaultConfig: function () { - return BI.extend(BI.YearMonthCombo.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-year-month-combo", - yearBehaviors: {}, - monthBehaviors: {}, - height: 25 + return BI.extend(BI.TreeValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-combo", + width: 200, + height: 30, + items: null, + itemsCreator: BI.emptyFn }); }, + _init: function () { - BI.YearMonthCombo.superclass._init.apply(this, arguments); + BI.TreeValueChooserCombo.superclass._init.apply(this, arguments); var self = this, o = this.options; - - o.value = o.value || {}; - - this.year = BI.createWidget({ - type: "bi.year_combo", - behaviors: o.yearBehaviors, - value: o.value.year - }); - - this.month = BI.createWidget({ - type: "bi.month_combo", - behaviors: o.monthBehaviors, - value: o.value.month - }); - - this.year.on(BI.YearCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.YearMonthCombo.EVENT_CONFIRM); - }); - this.year.on(BI.YearCombo.EVENT_BEFORE_POPUPVIEW, function () { - self.fireEvent(BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW); - }); - - this.month.on(BI.MonthCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.YearMonthCombo.EVENT_CONFIRM); - }); - this.month.on(BI.MonthCombo.EVENT_BEFORE_POPUPVIEW, function () { - self.fireEvent(BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW); - }); - - BI.createWidget({ - type: "bi.center", + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_combo", element: this, - hgap: 5, - items: [this.year, this.month] + itemsCreator: BI.bind(this._itemsCreator, this), + width: o.width, + height: o.height }); + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserCombo.EVENT_CONFIRM); + }); }, setValue: function (v) { - v = v || {}; - this.month.setValue(v.month); - this.year.setValue(v.year); + this.combo.setValue(v); }, getValue: function () { - return { - year: this.year.getValue(), - month: this.month.getValue() - }; + return this.combo.getValue(); + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); } }); -BI.YearMonthCombo.EVENT_CONFIRM = "EVENT_CONFIRM"; -BI.YearMonthCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW"; -BI.shortcut("bi.year_month_combo", BI.YearMonthCombo);/** - * 年份 + 月份下拉框 +BI.TreeValueChooserCombo.EVENT_CONFIRM = "TreeValueChooserCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_combo", BI.TreeValueChooserCombo);/** + * 简单的复选下拉树控件, 适用于数据量少的情况 * - * @class BI.YearQuarterCombo - * @extends BI.Widget + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserPane + * @extends BI.AbstractTreeValueChooser */ -BI.YearQuarterCombo = BI.inherit(BI.Widget, { - _defaultConfig: function () { - return BI.extend(BI.YearQuarterCombo.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-year-quarter-combo", - yearBehaviors: {}, - quarterBehaviors: {}, - height: 25 - }); - }, - _init: function () { - BI.YearQuarterCombo.superclass._init.apply(this, arguments); - var self = this, o = this.options; - - o.value = o.value || {}; - - this.year = BI.createWidget({ - type: "bi.year_combo", - behaviors: o.yearBehaviors, - value: o.value.year - }); - - this.quarter = BI.createWidget({ - type: "bi.quarter_combo", - behaviors: o.quarterBehaviors, - value: o.value.quarter - }); - - this.year.on(BI.YearCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.YearQuarterCombo.EVENT_CONFIRM); - }); - this.year.on(BI.YearCombo.EVENT_BEFORE_POPUPVIEW, function () { - self.fireEvent(BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW); - }); +BI.TreeValueChooserPane = BI.inherit(BI.AbstractTreeValueChooser, { - this.quarter.on(BI.QuarterCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.YearQuarterCombo.EVENT_CONFIRM); - }); - this.quarter.on(BI.QuarterCombo.EVENT_BEFORE_POPUPVIEW, function () { - self.fireEvent(BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW); + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserPane.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-pane", + items: null, + itemsCreator: BI.emptyFn }); + }, - BI.createWidget({ - type: "bi.center", + _init: function () { + BI.TreeValueChooserPane.superclass._init.apply(this, arguments); + var self = this, o = this.options; + this.pane = BI.createWidget({ + type: "bi.multi_select_tree", element: this, - hgap: 5, - items: [this.year, this.quarter] + itemsCreator: BI.bind(this._itemsCreator, this) + }); + + this.pane.on(BI.MultiSelectTree.EVENT_CHANGE, function () { + self.fireEvent(BI.TreeValueChooserPane.EVENT_CHANGE); }); + if (BI.isNotNull(o.items)) { + this._initData(o.items); + this.populate(); + } + }, + setSelectedValue: function (v) { + this.pane.setSelectedValue(v); }, setValue: function (v) { - v = v || {}; - this.quarter.setValue(v.quarter); - this.year.setValue(v.year); + this.pane.setValue(v); }, getValue: function () { - return { - year: this.year.getValue(), - quarter: this.quarter.getValue() - }; + return this.pane.getValue(); + }, + + populate: function () { + this.pane.populate.apply(this.pane, arguments); } }); -BI.YearQuarterCombo.EVENT_CONFIRM = "EVENT_CONFIRM"; -BI.YearQuarterCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW"; -BI.shortcut("bi.year_quarter_combo", BI.YearQuarterCombo);/** - * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 +BI.TreeValueChooserPane.EVENT_CHANGE = "TreeValueChooserPane.EVENT_CHANGE"; +BI.shortcut("bi.tree_value_chooser_pane", BI.TreeValueChooserPane);/** + * 简单的复选下拉框控件, 适用于数据量少的情况 * 封装了字段处理逻辑 * * Created by GUY on 2015/10/29. - * @class BI.AbstractAllValueChooser + * @class BI.AbstractValueChooser * @extends BI.Widget */ -BI.AbstractAllValueChooser = BI.inherit(BI.Widget, { +BI.AbstractValueChooser = BI.inherit(BI.Widget, { _const: { perPage: 100 }, _defaultConfig: function () { - return BI.extend(BI.AbstractAllValueChooser.superclass._defaultConfig.apply(this, arguments), { - width: 200, - height: 30, + return BI.extend(BI.AbstractValueChooser.superclass._defaultConfig.apply(this, arguments), { items: null, itemsCreator: BI.emptyFn, cache: true @@ -105076,6 +105454,18 @@ BI.AbstractAllValueChooser = BI.inherit(BI.Widget, { return text; }, + _getItemsByTimes: function (items, times) { + var res = []; + for (var i = (times - 1) * this._const.perPage; items[i] && i < times * this._const.perPage; i++) { + res.push(items[i]); + } + return res; + }, + + _hasNextByTimes: function (items, times) { + return times * this._const.perPage < items.length; + }, + _itemsCreator: function (options, callback) { var self = this, o = this.options; if (!o.cache || !this.items) { @@ -105112,24 +105502,24 @@ BI.AbstractAllValueChooser = BI.inherit(BI.Widget, { return; } callback({ - items: items, - hasNext: false + items: self._getItemsByTimes(items, options.times), + hasNext: self._hasNextByTimes(items, options.times) }); } } });/** - * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 + * 简单的复选下拉框控件, 适用于数据量少的情况 * 封装了字段处理逻辑 * * Created by GUY on 2015/10/29. - * @class BI.AllValueChooserCombo - * @extends BI.AbstractAllValueChooser + * @class BI.ValueChooserCombo + * @extends BI.Widget */ -BI.AllValueChooserCombo = BI.inherit(BI.AbstractAllValueChooser, { +BI.ValueChooserCombo = BI.inherit(BI.AbstractValueChooser, { _defaultConfig: function () { - return BI.extend(BI.AllValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-all-value-chooser-combo", + return BI.extend(BI.ValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-value-chooser-combo", width: 200, height: 30, items: null, @@ -105139,7 +105529,7 @@ BI.AllValueChooserCombo = BI.inherit(BI.AbstractAllValueChooser, { }, _init: function () { - BI.AllValueChooserCombo.superclass._init.apply(this, arguments); + BI.ValueChooserCombo.superclass._init.apply(this, arguments); var self = this, o = this.options; if (BI.isNotNull(o.items)) { this.items = o.items; @@ -105150,53 +105540,44 @@ BI.AllValueChooserCombo = BI.inherit(BI.AbstractAllValueChooser, { itemsCreator: BI.bind(this._itemsCreator, this), valueFormatter: BI.bind(this._valueFormatter, this), width: o.width, - height: o.height, - value: { - type: BI.Selection.Multi, - value: o.value || [] - } + height: o.height }); this.combo.on(BI.MultiSelectCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.AllValueChooserCombo.EVENT_CONFIRM); + self.fireEvent(BI.ValueChooserCombo.EVENT_CONFIRM); }); }, setValue: function (v) { - this.combo.setValue({ - type: BI.Selection.Multi, - value: v || [] - }); + this.combo.setValue(v); }, getValue: function () { var val = this.combo.getValue() || {}; - if (val.type === BI.Selection.All) { - return val.assist; - } - return val.value || []; + return { + type: val.type, + value: val.value + }; }, populate: function () { this.combo.populate.apply(this, arguments); } }); -BI.AllValueChooserCombo.EVENT_CONFIRM = "AllValueChooserCombo.EVENT_CONFIRM"; -BI.shortcut("bi.all_value_chooser_combo", BI.AllValueChooserCombo);/** - * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值 +BI.ValueChooserCombo.EVENT_CONFIRM = "ValueChooserCombo.EVENT_CONFIRM"; +BI.shortcut("bi.value_chooser_combo", BI.ValueChooserCombo);/** + * 简单的复选下拉框控件, 适用于数据量少的情况 * 封装了字段处理逻辑 * * Created by GUY on 2015/10/29. - * @class BI.AllValueChooserPane - * @extends BI.AbstractAllValueChooser + * @class BI.ValueChooserPane + * @extends BI.Widget */ -BI.AllValueChooserPane = BI.inherit(BI.AbstractAllValueChooser, { +BI.ValueChooserPane = BI.inherit(BI.AbstractValueChooser, { _defaultConfig: function () { - return BI.extend(BI.AllValueChooserPane.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-all-value-chooser-pane", - width: 200, - height: 30, + return BI.extend(BI.ValueChooserPane.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-value-chooser-pane", items: null, itemsCreator: BI.emptyFn, cache: true @@ -105204,1004 +105585,623 @@ BI.AllValueChooserPane = BI.inherit(BI.AbstractAllValueChooser, { }, _init: function () { - BI.AllValueChooserPane.superclass._init.apply(this, arguments); + BI.ValueChooserPane.superclass._init.apply(this, arguments); var self = this, o = this.options; - if (BI.isNotNull(o.items)) { - this.items = o.items; - } this.list = BI.createWidget({ type: "bi.multi_select_list", element: this, itemsCreator: BI.bind(this._itemsCreator, this), - valueFormatter: BI.bind(this._valueFormatter, this), - width: o.width, - height: o.height + valueFormatter: BI.bind(this._valueFormatter, this) }); this.list.on(BI.MultiSelectList.EVENT_CHANGE, function () { - self.fireEvent(BI.AllValueChooserPane.EVENT_CHANGE); + self.fireEvent(BI.ValueChooserPane.EVENT_CHANGE); }); + if (BI.isNotNull(o.items)) { + this.items = o.items; + this.populate(); + } }, setValue: function (v) { - this.list.setValue({ - type: BI.Selection.Multi, - value: v || [] - }); + this.list.setValue(v); }, getValue: function () { var val = this.list.getValue() || {}; - if (val.type === BI.Selection.All) { - return val.assist; - } - return val.value || []; + return { + type: val.type, + value: val.value + }; }, populate: function () { this.list.populate.apply(this.list, arguments); } }); -BI.AllValueChooserPane.EVENT_CHANGE = "AllValueChooserPane.EVENT_CHANGE"; -BI.shortcut("bi.all_value_chooser_pane", BI.AllValueChooserPane);BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { - - _const: { - perPage: 100 - }, - - _defaultConfig: function () { - return BI.extend(BI.AbstractTreeValueChooser.superclass._defaultConfig.apply(this, arguments), { - items: null, - itemsCreator: BI.emptyFn - }); - }, - - _initData: function (items) { - this.items = items; - var nodes = BI.Tree.treeFormat(items); - this.tree = new BI.Tree(); - this.tree.initTree(nodes); - }, - - _itemsCreator: function (options, callback) { - var self = this, o = this.options; - if (!this.items) { - o.itemsCreator({}, function (items) { - self._initData(items); - call(); - }); - } else { - call(); - } - function call () { - switch (options.type) { - case BI.TreeView.REQ_TYPE_INIT_DATA: - self._reqInitTreeNode(options, callback); - break; - case BI.TreeView.REQ_TYPE_ADJUST_DATA: - self._reqAdjustTreeNode(options, callback); - break; - case BI.TreeView.REQ_TYPE_SELECT_DATA: - self._reqSelectedTreeNode(options, callback); - break; - case BI.TreeView.REQ_TYPE_GET_SELECTED_DATA: - self._reqDisplayTreeNode(options, callback); - break; - default : - self._reqTreeNode(options, callback); - break; - } - } - }, - - _reqDisplayTreeNode: function (op, callback) { - var self = this; - var result = []; - var selectedValues = op.selectedValues; - - if (selectedValues == null || BI.isEmpty(selectedValues)) { - callback({}); - return; - } - - doCheck([], this.tree.getRoot(), selectedValues); - - callback({ - items: result - }); - - function doCheck (parentValues, node, selected) { - if (selected == null || BI.isEmpty(selected)) { - BI.each(node.getChildren(), function (i, child) { - var newParents = BI.clone(parentValues); - newParents.push(child.value); - var llen = self._getChildCount(newParents); - createOneJson(child, node.id, llen); - doCheck(newParents, child, {}); - }); - return; - } - BI.each(selected, function (k) { - var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); - }); - } - - function getCount (jo, parentValues) { - if (jo == null) { - return 0; - } - if (BI.isEmpty(jo)) { - return self._getChildCount(parentValues); - } - - return BI.size(jo); - } - - function createOneJson (node, pId, llen) { - result.push({ - id: node.id, - pId: pId, - text: node.text + (llen > 0 ? ("(" + BI.i18nText("BI-Basic_Altogether") + llen + BI.i18nText("BI-Basic_Count") + ")") : ""), - value: node.value, - open: true - }); - } - }, - - _reqSelectedTreeNode: function (op, callback) { - var self = this; - var selectedValues = BI.deepClone(op.selectedValues); - var notSelectedValue = op.notSelectedValue || {}; - var keyword = op.keyword || ""; - var parentValues = op.parentValues || []; - - if (selectedValues == null || BI.isEmpty(selectedValues)) { - callback({}); - return; - } - - dealWithSelectedValues(selectedValues); - callback(selectedValues); - - - function dealWithSelectedValues (selectedValues) { - var p = parentValues.concat(notSelectedValue); - // 存储的值中存在这个值就把它删掉 - // 例如选中了中国-江苏-南京, 取消中国或江苏或南京 - if (canFindKey(selectedValues, p)) { - // 如果搜索的值在父亲链中 - if (isSearchValueInParent(p)) { - // 例如选中了 中国-江苏, 搜索江苏, 取消江苏 - // 例如选中了 中国-江苏, 搜索江苏, 取消中国 - self._deleteNode(selectedValues, p); - } else { - var searched = []; - var find = search(parentValues, notSelectedValue, [], searched); - if (find && BI.isNotEmptyArray(searched)) { - BI.each(searched, function (i, arr) { - var node = self._getNode(selectedValues, arr); - if (node) { - // 例如选中了 中国-江苏-南京,搜索南京,取消中国 - self._deleteNode(selectedValues, arr); - } else { - // 例如选中了 中国-江苏,搜索南京,取消中国 - expandSelectedValue(selectedValues, arr, BI.last(arr)); - } - }); - } - } - } - - // 存储的值中不存在这个值,但父亲节点是全选的情况 - // 例如选中了中国-江苏,取消南京 - // important 选中了中国-江苏,取消了江苏,但是搜索的是南京 - if (isChild(selectedValues, p)) { - var result = [], find = false; - // 如果parentValues中有匹配的值,说明搜索结果不在当前值下 - if (isSearchValueInParent(p)) { - find = true; - } else { - // 从当前值开始搜 - find = search(parentValues, notSelectedValue, result); - p = parentValues; - } - - if (find === true) { - // 去掉点击的节点之后的结果集 - expandSelectedValue(selectedValues, p, notSelectedValue); - // 添加去掉搜索的结果集 - if (result.length > 0) { - BI.each(result, function (i, strs) { - self._buildTree(selectedValues, strs); - }); - } - } - } - - } - - function expandSelectedValue (selectedValues, parents, notSelectedValue) { - var next = selectedValues; - var childrenCount = []; - var path = []; - // 去掉点击的节点之后的结果集 - BI.some(parents, function (i, v) { - var t = next[v]; - if (t == null) { - if (i === 0) { - return true; - } - if (BI.isEmpty(next)) { - var split = parents.slice(0, i); - var expanded = self._getChildren(split); - path.push(split); - childrenCount.push(expanded.length); - // 如果只有一个值且取消的就是这个值 - if (i === parents.length - 1 && expanded.length === 1 && expanded[0] === notSelectedValue) { - for (var j = childrenCount.length - 1; j >= 0; j--) { - if (childrenCount[j] === 1) { - self._deleteNode(selectedValues, path[j]); - } else { - break; - } - } - } else { - BI.each(expanded, function (m, child) { - if (i === parents.length - 1 && child.value === notSelectedValue) { - return true; - } - next[child.value] = {}; - }); - } - next = next[v]; - } else { - return true; - // next = {}; - // next[v] = {}; - } - } else { - next = t; - } - }); - } - - function search (parents, current, result, searched) { - var newParents = BI.clone(parents); - newParents.push(current); - if (self._isMatch(parents, current, keyword)) { - searched && searched.push(newParents); - return true; - } - - var children = self._getChildren(newParents); - - var notSearch = []; - var can = false; - - BI.each(children, function (i, child) { - if (search(newParents, child.value, result, searched)) { - can = true; - } else { - notSearch.push(child.value); - } - }); - if (can === true) { - BI.each(notSearch, function (i, v) { - var next = BI.clone(newParents); - next.push(v); - result.push(next); - }); - } - return can; - } - - function isSearchValueInParent (parentValues) { - for (var i = 0, len = parentValues.length; i < len; i++) { - if (self._isMatch(parentValues.slice(0, parentValues.length - 1), parentValues[i], keyword)) { - return true; - } - } - return false; - } - - function canFindKey (selectedValues, parents) { - var t = selectedValues; - for (var i = 0; i < parents.length; i++) { - var v = parents[i]; - t = t[v]; - if (t == null) { - return false; - } - } - return true; - } - - function isChild (selectedValues, parents) { - var t = selectedValues; - for (var i = 0; i < parents.length; i++) { - var v = parents[i]; - if (!BI.has(t, v)) { - return false; - } - t = t[v]; - if (BI.isEmpty(t)) { - return true; - } - } - return false; - } - }, - - _reqAdjustTreeNode: function (op, callback) { - var self = this; - var result = []; - var selectedValues = op.selectedValues; - if (selectedValues == null || BI.isEmpty(selectedValues)) { - callback({}); - return; - } - BI.each(selectedValues, function (k, v) { - result.push([k]); - }); - - dealWithSelectedValues(selectedValues, []); - - var jo = {}; - BI.each(result, function (i, strs) { - self._buildTree(jo, strs); - }); - callback(jo); - - function dealWithSelectedValues (selected, parents) { - if (selected == null || BI.isEmpty(selected)) { - return true; - } - var can = true; - BI.each(selected, function (k, v) { - var p = BI.clone(parents); - p.push(k); - if (!dealWithSelectedValues(selected[k], p)) { - BI.each(selected[k], function (nk, nv) { - var t = BI.clone(p); - t.push(nk); - result.push(t); - }); - can = false; - } - }); - return can && isAllSelected(selected, parents); - } +BI.ValueChooserPane.EVENT_CHANGE = "ValueChooserPane.EVENT_CHANGE"; +BI.shortcut("bi.value_chooser_pane", BI.ValueChooserPane);(function () { + var Events = { - function isAllSelected (selected, parents) { - return BI.isEmpty(selected) || self._getChildCount(parents) === BI.size(selected); - } - }, + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on: function (name, callback, context) { + if (!eventsApi(this, "on", name, [callback, context]) || !callback) return this; + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({callback: callback, context: context, ctx: context || this}); + return this; + }, - _reqInitTreeNode: function (op, callback) { - var self = this; - var result = []; - var keyword = op.keyword || ""; - var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; - var output = search(); - BI.nextTick(function () { - callback({ - hasNext: output.length > self._const.perPage, - items: result, - lastSearchValue: BI.last(output) + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function (name, callback, context) { + if (!eventsApi(this, "once", name, [callback, context]) || !callback) return this; + var self = this; + var once = _.once(function () { + self.off(name, once); + callback.apply(this, arguments); }); - }); + once._callback = callback; + return this.on(name, once, context); + }, - function search () { - var children = self._getChildren([]); - var start = children.length; - if (lastSearchValue !== "") { - for (var j = 0, len = start; j < len; j++) { - if (children[j].value === lastSearchValue) { - start = j + 1; - break; - } - } - } else { - start = 0; + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off: function (name, callback, context) { + if (!this._events || !eventsApi(this, "off", name, [callback, context])) return this; + + // Remove all callbacks for all events. + if (!name && !callback && !context) { + this._events = void 0; + return this; } - var output = []; - for (var i = start, len = children.length; i < len; i++) { - if (output.length < self._const.perPage) { - var find = nodeSearch(1, [], children[i].value, false, result); - } else if (output.length === self._const.perPage) { - var find = nodeSearch(1, [], children[i].value, false, []); + + var names = name ? [name] : _.keys(this._events); + for (var i = 0, length = names.length; i < length; i++) { + name = names[i]; + + // Bail out if there are no events stored. + var events = this._events[name]; + if (!events) continue; + + // Remove all callbacks for this event. + if (!callback && !context) { + delete this._events[name]; + continue; } - if (find[0] === true) { - output.push(children[i].value); + + // Find any remaining events. + var remaining = []; + for (var j = 0, k = events.length; j < k; j++) { + var event = events[j]; + if ( + callback && callback !== event.callback && + callback !== event.callback._callback || + context && context !== event.context + ) { + remaining.push(event); + } } - if (output.length > self._const.perPage) { - break; + + // Replace events if there are any remaining. Otherwise, clean up. + if (remaining.length) { + this._events[name] = remaining; + } else { + delete this._events[name]; } } - return output; - } - function nodeSearch (deep, parentValues, current, isAllSelect, result) { - if (self._isMatch(parentValues, current, keyword)) { - var checked = isAllSelect || isSelected(parentValues, current); - createOneJson(parentValues, current, false, checked, !isAllSelect && isHalf(parentValues, current), true, result); - return [true, checked]; - } - var newParents = BI.clone(parentValues); - newParents.push(current); - var children = self._getChildren(newParents); + return this; + }, - var can = false, checked = false; + un: function () { + this.off.apply(this, arguments); + }, - var isCurAllSelected = isAllSelect || isAllSelected(parentValues, current); - BI.each(children, function (i, child) { - var state = nodeSearch(deep + 1, newParents, child.value, isCurAllSelected, result); - if (state[1] === true) { - checked = true; - } - if (state[0] === true) { - can = true; - } - }); - if (can === true) { - checked = isCurAllSelected || (isSelected(parentValues, current) && checked); - createOneJson(parentValues, current, true, checked, false, false, result); - } - return [can, checked]; - } + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger: function (name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, "trigger", name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + return this; + }, - function createOneJson (parentValues, value, isOpen, checked, half, flag, result) { - var node = self._getTreeNode(parentValues, value); - result.push({ - id: node.id, - pId: node.pId, - text: node.text, - value: node.value, - title: node.title, - isParent: node.getChildrenLength() > 0, - open: isOpen, - checked: checked, - halfCheck: half, - flag: flag - }); - } + fireEvent: function () { + this.trigger.apply(this, arguments); + }, - function isHalf (parentValues, value) { - var find = findSelectedObj(parentValues); - if (find == null) { - return null; - } - return BI.any(find, function (v, ob) { - if (v === value) { - if (ob != null && !BI.isEmpty(ob)) { - return true; - } - } - }); - } + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + listenTo: function (obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = _.uniqueId("l")); + listeningTo[id] = obj; + if (!callback && typeof name === "object") callback = this; + obj.on(name, callback, this); + return this; + }, - function isAllSelected (parentValues, value) { - var find = findSelectedObj(parentValues); - if (find == null) { - return null; + listenToOnce: function (obj, name, callback) { + if (typeof name === "object") { + for (var event in name) this.listenToOnce(obj, event, name[event]); + return this; } - return BI.any(find, function (v, ob) { - if (v === value) { - if (ob != null && BI.isEmpty(ob)) { - return true; - } + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, length = names.length; i < length; i++) { + this.listenToOnce(obj, names[i], callback); } - }); - } - - function isSelected (parentValues, value) { - var find = findSelectedObj(parentValues); - if (find == null) { - return false; + return this; } - return BI.any(find, function (v) { - if (v === value) { - return true; - } + if (!callback) return this; + var once = _.once(function () { + this.stopListening(obj, name, once); + callback.apply(this, arguments); }); - } + once._callback = callback; + return this.listenTo(obj, name, once); + }, - function findSelectedObj (parentValues) { - var find = selectedValues; - if (find == null) { - return null; + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function (obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + var remove = !name && !callback; + if (!callback && typeof name === "object") callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; } - BI.every(parentValues, function (i, v) { - find = find[v]; - if (find == null) { - return false; - } - return true; - }); - return find; + return this; } - }, - _reqTreeNode: function (op, callback) { - var self = this; - var result = []; - var times = op.times; - var checkState = op.checkState || {}; - var parentValues = op.parentValues || []; - var selectedValues = op.selectedValues || {}; - var valueMap = {}; - // if (judgeState(parentValues, selectedValues, checkState)) { - valueMap = dealWidthSelectedValue(parentValues, selectedValues); - // } - var nodes = this._getChildren(parentValues); - for (var i = (times - 1) * this._const.perPage; nodes[i] && i < times * this._const.perPage; i++) { - var state = getCheckState(nodes[i].value, parentValues, valueMap, checkState); - result.push({ - id: nodes[i].id, - pId: nodes[i].pId, - value: nodes[i].value, - text: nodes[i].text, - times: 1, - isParent: nodes[i].getChildrenLength() > 0, - checked: state[0], - halfCheck: state[1] - }); - } - BI.nextTick(function () { - callback({ - items: result, - hasNext: nodes.length > times * self._const.perPage - }); - }); + }; - function judgeState (parentValues, selected_value, checkState) { - var checked = checkState.checked, half = checkState.half; - if (parentValues.length > 0 && !checked) { - return false; - } - return (parentValues.length === 0 || (checked && half) && !BI.isEmpty(selected_value)); - } + // Regular expression used to split event strings. + var eventSplitter = /\s+/; - function dealWidthSelectedValue (parentValues, selectedValues) { - var valueMap = {}; - BI.each(parentValues, function (i, v) { - selectedValues = selectedValues[v] || {}; - }); - BI.each(selectedValues, function (value, obj) { - if (BI.isNull(obj)) { - valueMap[value] = [0, 0]; - return; - } - if (BI.isEmpty(obj)) { - valueMap[value] = [2, 0]; - return; - } - var nextNames = {}; - BI.each(obj, function (t, o) { - if (BI.isNull(o) || BI.isEmpty(o)) { - nextNames[t] = true; - } - }); - valueMap[value] = [1, BI.size(nextNames)]; - }); - return valueMap; - } + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function (obj, action, name, rest) { + if (!name) return true; - function getCheckState (current, parentValues, valueMap, checkState) { - var checked = checkState.checked, half = checkState.half; - var tempCheck = false, halfCheck = false; - if (BI.has(valueMap, current)) { - // 可能是半选 - if (valueMap[current][0] === 1) { - var values = BI.clone(parentValues); - values.push(current); - var childCount = self._getChildCount(values); - if (childCount > 0 && childCount !== valueMap[current][1]) { - halfCheck = true; - } - } else if (valueMap[current][0] === 2) { - tempCheck = true; - } - } - var check; - if (!checked && !halfCheck && !tempCheck) { - check = BI.has(valueMap, current); - } else { - check = ((tempCheck || checked) && !half) || BI.has(valueMap, current); + // Handle event maps. + if (typeof name === "object") { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); } - return [check, halfCheck]; + return false; } - }, - _getNode: function (selectedValues, parentValues) { - var pNode = selectedValues; - for (var i = 0, len = parentValues.length; i < len; i++) { - if (pNode == null) { - return null; + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, length = names.length; i < length; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); } - pNode = pNode[parentValues[i]]; + return false; } - return pNode; - }, - _deleteNode: function (selectedValues, values) { - var name = values[values.length - 1]; - var p = values.slice(0, values.length - 1); - var pNode = this._getNode(selectedValues, p); - if (pNode != null && pNode[name]) { - delete pNode[name]; - // 递归删掉空父节点 - while (p.length > 0 && BI.isEmpty(pNode)) { - name = p[p.length - 1]; - p = p.slice(0, p.length - 1); - pNode = this._getNode(selectedValues, p); - if (pNode != null) { - delete pNode[name]; - } - } + return true; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // BI events have 3 arguments). + var triggerEvents = function (events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: + while (++i < l) (ev = events[i]).callback.call(ev.ctx); + return; + case 1: + while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); + return; + case 2: + while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); + return; + case 3: + while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); + return; + default: + while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); + return; } - }, + }; - _buildTree: function (jo, values) { - var t = jo; - BI.each(values, function (i, v) { - if (!BI.has(t, v)) { - t[v] = {}; - } - t = t[v]; - }); - }, + // BI.Router + // --------------- - _isMatch: function (parentValues, value, keyword) { - var node = this._getTreeNode(parentValues, value); - var find = BI.Func.getSearchResult([node.text || node.value], keyword); - return find.find.length > 0 || find.match.length > 0; - }, + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = BI.Router = function (options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this._init.apply(this, arguments); + }; - _getTreeNode: function (parentValues, v) { - var self = this; - var findParentNode; - var index = 0; - this.tree.traverse(function (node) { - if (self.tree.isRoot(node)) { - return; - } - if (index > parentValues.length) { - return false; - } - if (index === parentValues.length && node.value === v) { - findParentNode = node; - return false; + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **BI.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // _init is an empty function by default. Override it with your own + // initialization logic. + _init: function () { + }, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function (route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ""; } - if (node.value === parentValues[index]) { - index++; - return; + if (!callback) callback = this[name]; + var router = this; + BI.history.route(route, function (fragment) { + var args = router._extractParameters(route, fragment); + if (router.execute(callback, args, name) !== false) { + router.trigger.apply(router, ["route:" + name].concat(args)); + router.trigger("route", name, args); + BI.history.trigger("route", router, name, args); + } + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function (callback, args, name) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `BI.history` to save a fragment into the history. + navigate: function (fragment, options) { + BI.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `BI.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function () { + if (!this.routes) return; + this.routes = _.result(this, "routes"); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); } - return true; - }); - return findParentNode; - }, + }, - _getChildren: function (parentValues) { - if (parentValues.length > 0) { - var value = BI.last(parentValues); - var parent = this._getTreeNode(parentValues.slice(0, parentValues.length - 1), value); - } else { - var parent = this.tree.getRoot(); + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function (route) { + route = route.replace(escapeRegExp, "\\$&") + .replace(optionalParam, "(?:$1)?") + .replace(namedParam, function (match, optional) { + return optional ? match : "([^/?]+)"; + }) + .replace(splatParam, "([^?]*?)"); + return new RegExp("^" + route + "(?:\\?([\\s\\S]*))?$"); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function (route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function (param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); } - return parent.getChildren(); - }, - _getChildCount: function (parentValues) { - return this._getChildren(parentValues).length; - } -});/** - * 简单的复选下拉树控件, 适用于数据量少的情况 - * - * Created by GUY on 2015/10/29. - * @class BI.TreeValueChooserCombo - * @extends BI.Widget - */ -BI.TreeValueChooserCombo = BI.inherit(BI.AbstractTreeValueChooser, { + }); - _defaultConfig: function () { - return BI.extend(BI.TreeValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-tree-value-chooser-combo", - width: 200, - height: 30, - items: null, - itemsCreator: BI.emptyFn - }); - }, + // History + // ---------------- - _init: function () { - BI.TreeValueChooserCombo.superclass._init.apply(this, arguments); - var self = this, o = this.options; - if (BI.isNotNull(o.items)) { - this._initData(o.items); + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = function () { + this.handlers = []; + this.checkUrl = _.bind(this.checkUrl, this); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== "undefined") { + this.location = window.location; + this.history = window.history; } - this.combo = BI.createWidget({ - type: "bi.multi_tree_combo", - element: this, - itemsCreator: BI.bind(this._itemsCreator, this), - width: o.width, - height: o.height - }); + }; - this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.TreeValueChooserCombo.EVENT_CONFIRM); - }); - }, + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; - setValue: function (v) { - this.combo.setValue(v); - }, + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; - getValue: function () { - return this.combo.getValue(); - }, + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; - populate: function () { - this.combo.populate.apply(this.combo, arguments); - } -}); -BI.TreeValueChooserCombo.EVENT_CONFIRM = "TreeValueChooserCombo.EVENT_CONFIRM"; -BI.shortcut("bi.tree_value_chooser_combo", BI.TreeValueChooserCombo);/** - * 简单的复选下拉树控件, 适用于数据量少的情况 - * - * Created by GUY on 2015/10/29. - * @class BI.TreeValueChooserPane - * @extends BI.AbstractTreeValueChooser - */ -BI.TreeValueChooserPane = BI.inherit(BI.AbstractTreeValueChooser, { + // Has the history handling already been started? + History.started = false; - _defaultConfig: function () { - return BI.extend(BI.TreeValueChooserPane.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-tree-value-chooser-pane", - items: null, - itemsCreator: BI.emptyFn - }); - }, + // Set up all inheritable **BI.History** properties and methods. + _.extend(History.prototype, Events, { - _init: function () { - BI.TreeValueChooserPane.superclass._init.apply(this, arguments); - var self = this, o = this.options; - this.pane = BI.createWidget({ - type: "bi.multi_select_tree", - element: this, - itemsCreator: BI.bind(this._itemsCreator, this) - }); + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, - this.pane.on(BI.MultiSelectTree.EVENT_CHANGE, function () { - self.fireEvent(BI.TreeValueChooserPane.EVENT_CHANGE); - }); - if (BI.isNotNull(o.items)) { - this._initData(o.items); - this.populate(); - } - }, + // Are we at the app root? + atRoot: function () { + var path = this.location.pathname.replace(/[^\/]$/, "$&/"); + return path === this.root && !this.getSearch(); + }, - setSelectedValue: function (v) { - this.pane.setSelectedValue(v); - }, + // In IE6, the hash fragment and search params are incorrect if the + // fragment contains `?`. + getSearch: function () { + var match = this.location.href.replace(/#.*/, "").match(/\?.+/); + return match ? match[0] : ""; + }, - setValue: function (v) { - this.pane.setValue(v); - }, + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function (window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ""; + }, - getValue: function () { - return this.pane.getValue(); - }, + // Get the pathname and search params, without the root. + getPath: function () { + var path = decodeURI(this.location.pathname + this.getSearch()); + var root = this.root.slice(0, -1); + if (!path.indexOf(root)) path = path.slice(root.length); + return path.charAt(0) === "/" ? path.slice(1) : path; + }, - populate: function () { - this.pane.populate.apply(this.pane, arguments); - } -}); -BI.TreeValueChooserPane.EVENT_CHANGE = "TreeValueChooserPane.EVENT_CHANGE"; -BI.shortcut("bi.tree_value_chooser_pane", BI.TreeValueChooserPane);/** - * 简单的复选下拉框控件, 适用于数据量少的情况 - * 封装了字段处理逻辑 - * - * Created by GUY on 2015/10/29. - * @class BI.AbstractValueChooser - * @extends BI.Widget - */ -BI.AbstractValueChooser = BI.inherit(BI.Widget, { + // Get the cross-browser normalized URL fragment from the path or hash. + getFragment: function (fragment) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange) { + fragment = this.getPath(); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ""); + }, - _const: { - perPage: 100 - }, + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function (options) { + if (History.started) throw new Error("BI.history has already been started"); + History.started = true; - _defaultConfig: function () { - return BI.extend(BI.AbstractValueChooser.superclass._defaultConfig.apply(this, arguments), { - items: null, - itemsCreator: BI.emptyFn, - cache: true - }); - }, + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: "/"}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._hasHashChange = "onhashchange" in window; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); + this.fragment = this.getFragment(); - _valueFormatter: function (v) { - var text = v; - if (BI.isNotNull(this.items)) { - BI.some(this.items, function (i, item) { - if (item.value === v) { - text = item.text; + // Normalize root to always include a leading and trailing slash. + this.root = ("/" + this.root + "/").replace(rootStripper, "/"); + + // Transition from hashChange to pushState or vice versa if both are + // requested. + if (this._wantsHashChange && this._wantsPushState) { + + // If we've started off with a route from a `pushState`-enabled + // browser, but we're currently in a browser that doesn't support it... + if (!this._hasPushState && !this.atRoot()) { + var root = this.root.slice(0, -1) || "/"; + this.location.replace(root + "#" + this.getPath()); + // Return immediately as browser will do redirect to new url return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._hasPushState && this.atRoot()) { + this.navigate(this.getHash(), {replace: true}); } - }); - } - return text; - }, - _getItemsByTimes: function (items, times) { - var res = []; - for (var i = (times - 1) * this._const.perPage; items[i] && i < times * this._const.perPage; i++) { - res.push(items[i]); - } - return res; - }, + } - _hasNextByTimes: function (items, times) { - return times * this._const.perPage < items.length; - }, + // Proxy an iframe to handle location events if the browser doesn't + // support the `hashchange` event, HTML5 history, or the user wants + // `hashChange` but not `pushState`. + if (!this._hasHashChange && this._wantsHashChange && (!this._wantsPushState || !this._hasPushState)) { + var iframe = document.createElement("iframe"); + iframe.src = "javascript:0"; + iframe.style.display = "none"; + iframe.tabIndex = -1; + var body = document.body; + // Using `appendChild` will throw on IE < 9 if the document is not ready. + this.iframe = body.insertBefore(iframe, body.firstChild).contentWindow; + this.iframe.document.open().close(); + this.iframe.location.hash = "#" + this.fragment; + } - _itemsCreator: function (options, callback) { - var self = this, o = this.options; - if (!o.cache || !this.items) { - o.itemsCreator({}, function (items) { - self.items = items; - call(items); - }); - } else { - call(this.items); - } - function call (items) { - var keywords = (options.keywords || []).slice(); - if (options.keyword) { - keywords.push(options.keyword); + // Add a cross-platform `addEventListener` shim for older browsers. + var addEventListener = window.addEventListener || function (eventName, listener) { + return attachEvent("on" + eventName, listener); + }; + + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._hasPushState) { + addEventListener("popstate", this.checkUrl, false); + } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { + addEventListener("hashchange", this.checkUrl, false); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } - BI.each(keywords, function (i, kw) { - var search = BI.Func.getSearchResult(items, kw); - items = search.match.concat(search.find); - }); - if (options.selectedValues) {// 过滤 - var filter = BI.makeObject(options.selectedValues, true); - items = BI.filter(items, function (i, ob) { - return !filter[ob.value]; - }); + + if (!this.options.silent) return this.loadUrl(); + }, + + // Disable BI.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop: function () { + // Add a cross-platform `removeEventListener` shim for older browsers. + var removeEventListener = window.removeEventListener || function (eventName, listener) { + return detachEvent("on" + eventName, listener); + }; + + // Remove window listeners. + if (this._hasPushState) { + removeEventListener("popstate", this.checkUrl, false); + } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { + removeEventListener("hashchange", this.checkUrl, false); } - if (options.type === BI.MultiSelectCombo.REQ_GET_ALL_DATA) { - callback({ - items: items - }); - return; + + // Clean up the iframe if necessary. + if (this.iframe) { + document.body.removeChild(this.iframe.frameElement); + this.iframe = null; } - if (options.type === BI.MultiSelectCombo.REQ_GET_DATA_LENGTH) { - callback({count: items.length}); - return; + + // Some environments will throw when clearing an undefined interval. + if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); + History.started = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route: function (route, callback) { + this.handlers.unshift({route: route, callback: callback}); + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl: function (e) { + var current = this.getFragment(); + + // If the user pressed the back button, the iframe's hash will have + // changed and we should use that for comparison. + if (current === this.fragment && this.iframe) { + current = this.getHash(this.iframe); } - callback({ - items: self._getItemsByTimes(items, options.times), - hasNext: self._hasNextByTimes(items, options.times) + + if (current === this.fragment) return false; + if (this.iframe) this.navigate(current); + this.loadUrl(); + }, + + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl: function (fragment) { + fragment = this.fragment = this.getFragment(fragment); + return _.some(this.handlers, function (handler) { + if (handler.route.test(fragment)) { + handler.callback(fragment); + return true; + } }); - } - } -});/** - * 简单的复选下拉框控件, 适用于数据量少的情况 - * 封装了字段处理逻辑 - * - * Created by GUY on 2015/10/29. - * @class BI.ValueChooserCombo - * @extends BI.Widget - */ -BI.ValueChooserCombo = BI.inherit(BI.AbstractValueChooser, { + }, - _defaultConfig: function () { - return BI.extend(BI.ValueChooserCombo.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-value-chooser-combo", - width: 200, - height: 30, - items: null, - itemsCreator: BI.emptyFn, - cache: true - }); - }, + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you wish to modify the current URL without adding an entry to the history. + navigate: function (fragment, options) { + if (!History.started) return false; + if (!options || options === true) options = {trigger: !!options}; - _init: function () { - BI.ValueChooserCombo.superclass._init.apply(this, arguments); - var self = this, o = this.options; - if (BI.isNotNull(o.items)) { - this.items = o.items; - } - this.combo = BI.createWidget({ - type: "bi.multi_select_combo", - element: this, - itemsCreator: BI.bind(this._itemsCreator, this), - valueFormatter: BI.bind(this._valueFormatter, this), - width: o.width, - height: o.height - }); + // Normalize the fragment. + fragment = this.getFragment(fragment || ""); - this.combo.on(BI.MultiSelectCombo.EVENT_CONFIRM, function () { - self.fireEvent(BI.ValueChooserCombo.EVENT_CONFIRM); - }); - }, + // Don't include a trailing slash on the root. + var root = this.root; + if (fragment === "" || fragment.charAt(0) === "?") { + root = root.slice(0, -1) || "/"; + } + var url = root + fragment; - setValue: function (v) { - this.combo.setValue(v); - }, + // Strip the hash and decode for matching. + fragment = decodeURI(fragment.replace(pathStripper, "")); - getValue: function () { - var val = this.combo.getValue() || {}; - return { - type: val.type, - value: val.value - }; - }, + if (this.fragment === fragment) return; + this.fragment = fragment; - populate: function () { - this.combo.populate.apply(this, arguments); - } -}); -BI.ValueChooserCombo.EVENT_CONFIRM = "ValueChooserCombo.EVENT_CONFIRM"; -BI.shortcut("bi.value_chooser_combo", BI.ValueChooserCombo);/** - * 简单的复选下拉框控件, 适用于数据量少的情况 - * 封装了字段处理逻辑 - * - * Created by GUY on 2015/10/29. - * @class BI.ValueChooserPane - * @extends BI.Widget - */ -BI.ValueChooserPane = BI.inherit(BI.AbstractValueChooser, { + // If pushState is available, we use it to set the fragment as a real URL. + if (this._hasPushState) { + this.history[options.replace ? "replaceState" : "pushState"]({}, document.title, url); - _defaultConfig: function () { - return BI.extend(BI.ValueChooserPane.superclass._defaultConfig.apply(this, arguments), { - baseCls: "bi-value-chooser-pane", - items: null, - itemsCreator: BI.emptyFn, - cache: true - }); - }, + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && (fragment !== this.getHash(this.iframe))) { + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. + if (!options.replace) this.iframe.document.open().close(); + this._updateHash(this.iframe.location, fragment, options.replace); + } - _init: function () { - BI.ValueChooserPane.superclass._init.apply(this, arguments); - var self = this, o = this.options; - this.list = BI.createWidget({ - type: "bi.multi_select_list", - element: this, - itemsCreator: BI.bind(this._itemsCreator, this), - valueFormatter: BI.bind(this._valueFormatter, this) - }); + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + return this.location.assign(url); + } + if (options.trigger) return this.loadUrl(fragment); + }, - this.list.on(BI.MultiSelectList.EVENT_CHANGE, function () { - self.fireEvent(BI.ValueChooserPane.EVENT_CHANGE); - }); - if (BI.isNotNull(o.items)) { - this.items = o.items; - this.populate(); + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash: function (location, fragment, replace) { + if (replace) { + var href = location.href.replace(/(javascript:|#).*$/, ""); + location.replace(href + "#" + fragment); + } else { + // Some browsers require that `hash` contains a leading #. + location.hash = "#" + fragment; + } } - }, - - setValue: function (v) { - this.list.setValue(v); - }, - getValue: function () { - var val = this.list.getValue() || {}; - return { - type: val.type, - value: val.value - }; - }, + }); - populate: function () { - this.list.populate.apply(this.list, arguments); - } -}); -BI.ValueChooserPane.EVENT_CHANGE = "ValueChooserPane.EVENT_CHANGE"; -BI.shortcut("bi.value_chooser_pane", BI.ValueChooserPane); \ No newline at end of file + // Create the default BI.history. + BI.history = new History; +}()); \ No newline at end of file