/**
* @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);