/** * @file 所有UI组件的基类,通过它可以简单的快速的创建新的组件。 * @name zepto.ui * @short zepto.ui * @desc 所有UI组件的基类,通过它可以简单的快速的创建新的组件。 * @import core/zepto.js, core/zepto.extend.js */ (function($, undefined) { var id = 1, _blankFn = function(){}, tpl = '<%=name%>-<%=id%>', record = (function(){ var data = {}, id = 0, iKey = "GMUWidget"+(+ new Date()); //internal key. return function( obj, key, val){ var dkey = obj[ iKey ] || ( obj[ iKey ] = ++id ), store = data[dkey] || (data[dkey] = {}); !$.isUndefined(val) && (store[key] = val); $.isNull(val) && delete store[key]; return store[ key ]; } })(); $.ui = $.ui || { version: '2.0.5', guid: _guid, /** * @name $.ui.define * @grammar $.ui.define(name, data[, superClass]) ⇒ undefined * @desc 定义组件, * - ''name'' 组件名称 * - ''data'' 对象,设置此组件的prototype。可以添加属性或方法 * - ''superClass'' 基类,指定此组件基于哪个现有组件,默认为Widget基类 * **示例:** * * $.ui.define('helloworld', { * _data: { * opt1: null * }, * enable: function(){ * //... * } * }); * * * **定义完后,就可以通过以下方式使用了** * * var instance = $.ui.helloworld({opt1: true}); * instance.enable(); * * //或者 * $('#id').helloworld({opt1:true}); * //...later * $('#id').helloworld('enable'); * * * **Tips** * 1. 通过Zepto对象上的组件方法,可以直接实例话组件, 如: $('#btn').button({label: 'abc'}); * 2. 通过Zepto对象上的组件方法,传入字符串this, 可以获得组件实例,如:var btn = $('#btn').button('this'); * 3. 通过Zepto对象上的组件方法,可以直接调用组件方法,第一个参数用来指定方法名,之后的参数作为方法参数,如: $('#btn').button('setIcon', 'home'); * 4. 在子类中,如覆写了某个方法,可以在方法中通过this.$super()方法调用父级方法。如:this.$super('enable'); */ define: function(name, data, superClass) { if(superClass) data.inherit = superClass; var Class = $.ui[name] = _createClass(function(el, options) { var obj = _createObject(Class.prototype, { _id: $.parseTpl(tpl, { name: name, id: _guid() }) }); obj._createWidget.call(obj, el, options,Class.plugins); return obj; }, data); return _zeptoLize(name, Class); }, /** * @name $.ui.isWidget() * @grammar $.ui.isWidget(obj) ⇒ boolean * @grammar $.ui.isWidget(obj, name) ⇒ boolean * @desc 判断obj是不是widget实例 * * **参数** * - ''obj'' 用于检测的对象 * - ''name'' 可选,默认监测是不是''widget''(基类)的实例,可以传入组件名字如''button''。作用将变为obj是不是button组件实例。 * @param obj * @param name * @example * * var btn = $.ui.button(), * dialog = $.ui.dialog(); * * console.log($.isWidget(btn)); // => true * console.log($.isWidget(dialog)); // => true * console.log($.isWidget(btn, 'button')); // => true * console.log($.isWidget(dialog, 'button')); // => false * console.log($.isWidget(btn, 'noexist')); // => false */ isWidget: function(obj, name){ return obj instanceof (name===undefined ? _widget: $.ui[name] || _blankFn); } }; /** * generate guid */ function _guid() { return id++; }; function _createObject(proto, data) { var obj = {}; Object.create ? obj = Object.create(proto) : obj.__proto__ = proto; return $.extend(obj, data || {}); } function _createClass(Class, data) { if (data) { _process(Class, data); $.extend(Class.prototype, data); } return $.extend(Class, { plugins: [], register: function(fn) { if ($.isObject(fn)) { $.extend(this.prototype,fn); return; } this.plugins.push(fn); } }); } /** * handle inherit & _data */ function _process(Class, data) { var superClass = data.inherit || _widget, proto = superClass.prototype, obj; obj = Class.prototype = _createObject(proto, { $factory: Class, $super: function(key) { var fn = proto[key]; return $.isFunction(fn) ? fn.apply(this, $.slice(arguments, 1)) : fn; } }); obj._data = $.extend({}, proto._data, data._data); delete data._data; return Class; } /** * 强制setup模式 * @grammar $(selector).dialog(opts); */ function _zeptoLize( name ) { $.fn[ name ] = function(opts) { var ret, obj, args = $.slice(arguments, 1); $.each( this, function( i, el ){ obj = record( el, name ) || $.ui[name]( el, $.extend( $.isPlainObject(opts) ? opts : {}, { setup: true } ) ); if ($.isString( opts )) { if (!$.isFunction( obj[ opts ] ) && opts !== 'this') { throw new Error(name + '组件没有此方法'); //当不是取方法是,抛出错误信息 } ret = $.isFunction( obj[ opts ] ) ? obj[opts].apply(obj, args) : undefined; } if( ret !== undefined && ret !== obj || opts === "this" && ( ret = obj ) ) { return false; } ret = undefined; }); //ret 为真就是要返回ui实例之外的内容 //obj 'this'时返回 //其他都是返回zepto实例 //修改返回值为空的时,返回值不对的问题 return ret !== undefined ? ret : this; }; } /** * @name widget * @desc GMU所有的组件都是此类的子类,即以下此类里面的方法都可在其他组建中调用。 */ var _widget = function() {}; $.extend(_widget.prototype, { _data: { status: true }, /** * @name data * @grammar data(key) ⇒ value * @grammar data(key, value) ⇒ value * @desc 设置或者获取options, 所有组件中的配置项都可以通过此方法得到。 * @example * $('a#btn').button({label: '按钮'}); * console.log($('a#btn').button('data', 'label'));// => 按钮 */ data: function(key, val) { var _data = this._data; if ($.isObject(key)) return $.extend(_data, key); else return !$.isUndefined(val) ? _data[key] = val : _data[key]; }, /** * common constructor */ _createWidget: function(el, opts,plugins) { if ($.isObject(el)) { opts = el || {}; el = undefined; } var data = $.extend({}, this._data, opts); $.extend(this, { _el: el ? $(el) : undefined, _data: data }); //触发plugins var me = this; $.each( plugins, function( i, fn ){ var result = fn.apply( me ); if(result && $.isPlainObject(result) ){ var plugins = me._data.disablePlugin; if( !plugins || $.isString(plugins) && !~plugins.indexOf(result.pluginName) ){ delete result.pluginName; $.each(result,function( key, val ){ var orgFn; if((orgFn = me[key]) && $.isFunction( val ) ){ me[key] = function(){ me[key + 'Org'] = orgFn; return val.apply(me,arguments); } }else me[key] = val; }); } } }); // use setup or render if(data.setup) this._setup(el && el.getAttribute('data-mode')); else this._create(); this._init(); var me = this, $el = this.trigger('init').root(); $el.on('tap', function(e) { (e['bubblesList'] || (e['bubblesList'] = [])).push(me); }); record( $el[0], me._id.split('-')[0], me ); }, /** * @interface: use in render mod * @name _create * @desc 接口定义,子类中需要重新实现此方法,此方法在render模式时被调用。 * * 所谓的render方式,即,通过以下方式初始化组件 * * $.ui.widgetName(options); * */ _create: function() {}, /** * @interface: use in setup mod * @name _setup * @desc 接口定义,子类中需要重新实现此方法,此方法在setup模式时被调用。第一个行参用来分辨时fullsetup,还是setup * * * $.ui.define('helloworld', { * _setup: function(mode){ * if(mode){ * //为fullsetup模式 * } else { * //为setup模式 * } * } * }); * * * 所谓的setup方式,即,先有dom,然后通过选择器,初始化Zepto后,在Zepto对象直接调用组件名方法实例化组件,如 * * //
* $('#widget').widgetName(options); *
* * 如果用来初始化的element,设置了data-mode="true",组件将以fullsetup模式初始化 */ _setup: function(mode) {}, /** * @name root * @grammar root() ⇒ value * @grammar root(el) ⇒ value * @desc 设置或者获取根节点 * @example * $('a#btn').button({label: '按钮'}); * console.log($('a#btn').button('root'));// => a#btn */ root: function(el) { return this._el = el || this._el; }, /** * @name id * @grammar id() ⇒ value * @grammar id(id) ⇒ value * @desc 设置或者获取组件id */ id: function(id) { return this._id = id || this._id; }, /** * @name destroy * @grammar destroy() ⇒ undefined * @desc 注销组件 */ destroy: function() { var me = this, $el; $el = this.trigger('destroy').off().root(); $el.find('*').off(); record( $el[0], me._id.split('-')[0], null); $el.off().remove(); this.__proto__ = null; $.each(this, function(key) { delete me[key]; }); }, /** * @name on * @grammar on(type, handler) ⇒ instance * @desc 绑定事件,此事件绑定不同于zepto上绑定事件,此On的this只想组件实例,而非zepto实例 */ on: function(ev, callback) { this.root().on(ev, $.proxy(callback, this)); return this; }, /** * @name off * @grammar off(type) ⇒ instance * @grammar off(type, handler) ⇒ instance * @desc 解绑事件 */ off: function(ev, callback) { this.root().off(ev, callback); return this; }, /** * @name trigger * @grammar trigger(type[, data]) ⇒ instance * @desc 触发事件, 此trigger会优先把options上的事件回调函数先执行,然后给根DOM派送事件。 * options上回调函数可以通过e.preventDefaualt()来组织事件派发。 */ trigger: function(event, data) { event = $.isString(event) ? $.Event(event) : event; var onEvent = this.data(event.type),result; if( onEvent && $.isFunction(onEvent) ){ event.data = data; result = onEvent.apply(this, [event].concat(data)); if(result === false || event.defaultPrevented){ return this; } } this.root().trigger(event, data); return this; } }); })(Zepto);