/_src/core/zepto.ui.js

https://github.com/leaven/gmu · JavaScript · 360 lines · 181 code · 24 blank · 155 comment · 39 complexity · cfc82b3e473e7c9dfd3b7466f042f4b8 MD5 · raw file

  1. /**
  2. * @file
  3. * @name UI 基类
  4. * @short Zepto UI
  5. * @desc 所有UI组件的基类,通过它可以简单的快速的创建新的组件。
  6. * @import core/zepto.js, core/zepto.extend.js
  7. */
  8. (function($, undefined) {
  9. $.ui = $.ui || {
  10. version: '2.0',
  11. guid: _guid,
  12. /**
  13. * @name $.ui.define
  14. * @grammar $.ui.define(name, data[, superClass]) ⇒ undefined
  15. * @desc 定义组件,
  16. * - ''name'' 组件名称
  17. * - ''data'' 对象,设置此组件的prototype。可以添加属性或方法
  18. * - ''superClass'' 基类,指定此组件基于哪个现有组件,默认为Widget基类
  19. * **示例:**
  20. * <code type="javascript">
  21. * $.ui.define('helloworld', {
  22. * _data: {
  23. * opt1: null
  24. * },
  25. * enable: function(){
  26. * //...
  27. * }
  28. * });
  29. * </code>
  30. *
  31. * **定义完后,就可以通过以下方式使用了**
  32. *<code type="javascript">
  33. * var instance = $.ui.helloworld({opt1: true});
  34. * instance.enable();
  35. *
  36. * //或者
  37. * $('#id').helloworld({opt1:true});
  38. * //...later
  39. * $('#id').helloworld('enable');
  40. * </code>
  41. *
  42. * **Tips**
  43. * 1. 通过Zepto对象上的组件方法,可以直接实例话组件, 如: $('#btn').button({label: 'abc'});
  44. * 2. 通过Zepto对象上的组件方法,传入字符串this, 可以获得组件实例,如:var btn = $('#btn').button('this');
  45. * 3. 通过Zepto对象上的组件方法,可以直接调用组件方法,第一个参数用来指定方法名,之后的参数作为方法参数,如: $('#btn').button('setIcon', 'home');
  46. * 4. 在子类中,如覆写了某个方法,可以在方法中通过this.$super()方法调用父级方法。如:this.$super('enable');
  47. */
  48. define: function(name, data, superClass) {
  49. if(superClass) data.inherit = superClass;
  50. var Class = $.ui[name] = _createClass(function(el, options) {
  51. var obj = _createObject(Class.prototype, {
  52. _id: $.parseTpl(tpl, {
  53. name: name,
  54. id: _guid()
  55. })
  56. });
  57. obj._createWidget.call(obj, el, options,Class.plugins);
  58. return obj;
  59. }, data);
  60. return _zeptoLize(name, Class);
  61. }
  62. };
  63. var id = 1,
  64. tpl = '<%=name%>-<%=id%>',
  65. uikey = 'gmu-widget';
  66. /**
  67. * generate guid
  68. */
  69. function _guid() {
  70. return id++;
  71. };
  72. function _createObject(proto, data) {
  73. var obj = {};
  74. Object.create ? obj = Object.create(proto) : obj.__proto__ = proto;
  75. return $.extend(obj, data || {});
  76. }
  77. function _createClass(Class, data) {
  78. if (data) {
  79. _process(Class, data);
  80. $.extend(Class.prototype, data);
  81. }
  82. return $.extend(Class, {
  83. plugins: [],
  84. register: function(fn) {
  85. if ($.isObject(fn)) {
  86. $.extend(this.prototype,fn);
  87. return;
  88. }
  89. this.plugins.push(fn);
  90. }
  91. });
  92. }
  93. /**
  94. * handle inherit & _data
  95. */
  96. function _process(Class, data) {
  97. var superClass = data.inherit || _widget,
  98. proto = superClass.prototype,
  99. obj;
  100. obj = Class.prototype = _createObject(proto, {
  101. $factory: Class,
  102. $super: function(key) {
  103. var fn = proto[key];
  104. return $.isFunction(fn) ? fn.apply(this, $.slice(arguments, 1)) : fn;
  105. }
  106. });
  107. obj._data = $.extend({}, proto._data, data._data);
  108. delete data._data;
  109. return Class;
  110. }
  111. /**
  112. * 强制setup模式
  113. * @grammar $(selector).dialog(opts);
  114. */
  115. function _zeptoLize(name) {
  116. $.fn[name] = function(opts) {
  117. var ret, obj,args = $.slice(arguments, 1);
  118. $.each(this,function(i,el){
  119. obj = $(el).data(uikey + name) || $.ui[name](el, $.extend($.isPlainObject(opts) ? opts : {},{
  120. setup: true
  121. }));
  122. if ($.isString(opts)) {
  123. ret = $.isFunction(obj[opts]) && obj[opts].apply(obj, args);
  124. if (opts == 'this' || ret !== obj && ret !== undefined) {
  125. return false;
  126. }
  127. ret = null;
  128. }
  129. });
  130. //ret 为真就是要返回ui实例之外的内容
  131. //obj 'this'时返回
  132. //其他都是返回zepto实例
  133. return ret || (opts == 'this' ? obj : this);
  134. };
  135. }
  136. /**
  137. * @name widget基类
  138. * @desc GMU所有的组件都是此类的子类,即以下此类里面的方法都可在其他组建中调用。
  139. */
  140. var _widget = function() {};
  141. $.extend(_widget.prototype, {
  142. _data: {
  143. status: true
  144. },
  145. /**
  146. * @name data
  147. * @grammar data(key) ⇒ value
  148. * @grammar data(key, value) ⇒ value
  149. * @desc 设置或者获取options, 所有组件中的配置项都可以通过此方法得到。
  150. * @example
  151. * $('a#btn').button({label: '按钮'});
  152. * console.log($('a#btn').button('data', 'label'));// => 按钮
  153. */
  154. data: function(key, val) {
  155. var _data = this._data;
  156. if ($.isObject(key)) return $.extend(_data, key);
  157. else return !$.isUndefined(val) ? _data[key] = val : _data[key];
  158. },
  159. /**
  160. * common constructor
  161. */
  162. _createWidget: function(el, opts,plugins) {
  163. if ($.isObject(el)) {
  164. opts = el || {};
  165. el = undefined;
  166. }
  167. var data = $.extend({}, this._data, opts);
  168. $.extend(this, {
  169. _el: el ? $(el) : undefined,
  170. _data: data
  171. });
  172. //触发plugins
  173. var me = this;
  174. $.each(plugins,function(i,fn){
  175. var result = fn.apply(me);
  176. if(result && $.isPlainObject(result)){
  177. var plugins = me._data.disablePlugin;
  178. if(!plugins || $.isString(plugins) && plugins.indexOf(result.pluginName) == -1){
  179. delete result.pluginName
  180. $.each(result,function(key,val){
  181. var orgFn;
  182. if((orgFn = me[key]) && $.isFunction(val)){
  183. me[key] = function(){
  184. me[key + 'Org'] = orgFn;
  185. return val.apply(me,arguments);
  186. }
  187. }else
  188. me[key] = val;
  189. })
  190. }
  191. }
  192. });
  193. // use setup or render
  194. if(data.setup) this._setup(el && el.getAttribute('data-mode'));
  195. else this._create();
  196. this._init();
  197. var me = this,
  198. $el = this.trigger('init').root();
  199. $el.on('tap', function(e) {
  200. (e['bubblesList'] || (e['bubblesList'] = [])).push(me);
  201. });
  202. // record this
  203. $el.data(uikey + this._id.split('-')[0],this);
  204. },
  205. /**
  206. * @interface: use in render mod
  207. * @name _create
  208. * @desc 接口定义,子类中需要重新实现此方法,此方法在render模式时被调用。
  209. *
  210. * 所谓的render方式,即,通过以下方式初始化组件
  211. * <code>
  212. * $.ui.widgetName(options);
  213. * </code>
  214. */
  215. _create: function() {},
  216. /**
  217. * @interface: use in setup mod
  218. * @param {Boolean} data-mode use tpl mode
  219. * @name _setup
  220. * @desc 接口定义,子类中需要重新实现此方法,此方法在setup模式时被调用。第一个行参用来分辨时fullsetup,还是setup
  221. *
  222. * <code>
  223. * $.ui.define('helloworld', {
  224. * _setup: function(mode){
  225. * if(mode){
  226. * //为fullsetup模式
  227. * } else {
  228. * //为setup模式
  229. * }
  230. * }
  231. * });
  232. * </code>
  233. *
  234. * 所谓的setup方式,即,先有dom,然后通过选择器,初始化Zepto后,在Zepto对象直接调用组件名方法实例化组件,如
  235. * <code>
  236. * //<div id="widget"></div>
  237. * $('#widget').widgetName(options);
  238. * </code>
  239. *
  240. * 如果用来初始化的element,设置了data-mode="true",组件将以fullsetup模式初始化
  241. */
  242. _setup: function(mode) {},
  243. /**
  244. * @name root
  245. * @grammar root() ⇒ value
  246. * @grammar root(el) ⇒ value
  247. * @desc 设置或者获取根节点
  248. * @example
  249. * $('a#btn').button({label: '按钮'});
  250. * console.log($('a#btn').button('root'));// => a#btn
  251. */
  252. root: function(el) {
  253. return this._el = el || this._el;
  254. },
  255. /**
  256. * @name id
  257. * @grammar id() ⇒ value
  258. * @grammar id(id) ⇒ value
  259. * @desc 设置或者获取组件id
  260. */
  261. id: function(id) {
  262. return this._id = id || this._id;
  263. },
  264. /**
  265. * @name destroy
  266. * @grammar destroy() ⇒ undefined
  267. * @desc 注销组件
  268. */
  269. destroy: function() {
  270. var That = this,
  271. $el;
  272. $.each(this.data('components') || [], function(id, obj) {
  273. obj.destroy();
  274. });
  275. $el = this.trigger('destroy').off().root();
  276. $el.find('*').off();
  277. $el.removeData(uikey).off().remove();
  278. this.__proto__ = null;
  279. $.each(this, function(key, val) {
  280. delete That[key];
  281. });
  282. },
  283. /**
  284. * @name component
  285. * @grammar component() ⇒ array
  286. * @grammar component(subInstance) ⇒ instance
  287. * @grammar component(createFn) ⇒ instance
  288. * @desc 获取或者设置子组件, createFn为组件构造器,必须返回组件的实例。
  289. */
  290. component: function(createFn) {
  291. var list = this.data('components') || this.data('components', []);
  292. try {
  293. list.push($.isFunction(createFn) ? createFn.apply(this) : createFn);
  294. } catch(e) {}
  295. return this;
  296. },
  297. /**
  298. * @name on
  299. * @grammar on(type, handler) ⇒ instance
  300. * @desc 绑定事件,此事件绑定不同于zepto上绑定事件,此On的this只想组件实例,而非zepto实例
  301. */
  302. on: function(ev, callback) {
  303. this.root().on(ev, $.proxy(callback, this));
  304. return this;
  305. },
  306. /**
  307. * @name off
  308. * @grammar off(type) ⇒ instance
  309. * @grammar off(type, handler) ⇒ instance
  310. * @desc 解绑事件
  311. */
  312. off: function(ev, callback) {
  313. this.root().off(ev, callback);
  314. return this;
  315. },
  316. /**
  317. * @name trigger
  318. * @grammar trigger(type[, data]) ⇒ instance
  319. * @desc 触发事件, 此trigger会优先把options上的事件回调函数先执行,然后给根DOM派送事件。
  320. * options上回调函数可以通过e.preventDefaualt()来组织事件派发。
  321. */
  322. trigger: function(event, data) {
  323. event = $.isString(event) ? $.Event(event) : event;
  324. var onEvent = this.data(event.type),result;
  325. if( onEvent && $.isFunction(onEvent) ){
  326. event.data = data;
  327. result = onEvent.apply(this, [event].concat(data));
  328. if(result === false || event.defaultPrevented){
  329. return this;
  330. }
  331. }
  332. this.root().trigger(event, data);
  333. return this;
  334. }
  335. });
  336. })(Zepto);