zepto.ui.js 13 KB

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