1 /**
  2  *
  3  * 定义widget类
  4  * @fileOverView core/widget
  5  * @author  <a href="mailto:zhang.gd@foxmail.com">Zhang Guangda</a>
  6  * @date    2012-10-25
  7  */
  8 define("core/widget", function() {
  9 
 10 	/**
 11 	 * 获取默认的业务流
 12 	 * @return {object} 业务流默认的一些方法
 13 	 */
 14 	var _getDefaultProcess = function() {
 15 		return {
 16 			start: $we.emptyFunction,
 17 			end: $we.emptyFunction,
 18 			checkSucc: $we.emptyFunction,
 19 			succ: $we.emptyFunction,
 20 			fail: $we.emptyFunction,
 21 			next: $we.emptyFunction,
 22 			prev: $we.emptyFunction,
 23 			options: $we.emptyFunction
 24 		};
 25 	};
 26 
 27 	/**
 28 	 * 作为widget的初始化的类
 29 	 * @param  {String} name       Widget的名称
 30 	 * @param  {Function} init     初始化函数
 31 	 * @param  {Function} run      初始化widget完毕后运行
 32 	 * @param  {Object} params     参数
 33 	 * @param  {Object} interfaces 对外的接口
 34 	 * @param  {Object} events     需要绑定的事件
 35 	 * @param  {Object} notifies   提供给Children使用的方法
 36 	 * @param  {Object} notifyTo   传递通知的对象
 37 	 * @param  {Object} inherit    传递继承自的对象
 38 	 * @return {Object}            返回一个Widget类
 39 	 */
 40 	var widget = function(name, init, run, params, interfaces, events, notifies, process, notifyTo, inherit) {
 41 		// 管理所有内部的elements
 42 		this.node = {};
 43 		// 管理内部的事件响应
 44 		this.events = {};
 45 		// 管理内部提供给children的方法
 46 		this.notifies = {};
 47 		// 业务流需要的方法
 48 		this.process = _getDefaultProcess();
 49 		// 继承
 50 		this._super = {};
 51 
 52 		// 设置notifyTo
 53 		if (notifyTo) this.setNotifyTo(notifyTo);
 54 
 55 		this.extend(inherit);
 56 		widget.extendMethod.call(this, interfaces, events, notifies, process, init, params);
 57 		if (typeof run == "function") {
 58 			run.apply(this, params);
 59 		}
 60 	};
 61 
 62 	// 自定义的一些events储存在这里
 63 	widget.customizedEvents = {};
 64 
 65 	// 注册好了的widget储存在这里
 66 	widget.store = {};
 67 
 68 	widget.tmpDiv = document.createElement("div");
 69 
 70 
 71 	widget.extendMethod = function(interfaces, events, notifies, process, init, params, bExtend) {
 72 		for (var s in events) {
 73 			this.events[s] = events[s];
 74 		}
 75 		var _makeFunction = function(collection, funName) {
 76 				var me = this;
 77 				return function() {
 78 					return collection[funName].apply(me, arguments);
 79 				};
 80 			};
 81 		for (var funName in interfaces) {
 82 			if (typeof interfaces[funName] == "function") {
 83 				if (!this[funName] || !bExtend)
 84 					this[funName] = _makeFunction.call(this, interfaces, funName);
 85 				if (bExtend)
 86 					this._super[funName] = _makeFunction.call(this, interfaces, funName);
 87 			}
 88 		}
 89 		for (var funName in notifies) {
 90 			if (typeof notifies[funName] == "function") {
 91 				this.notifies[funName] = _makeFunction.call(this, notifies, funName);
 92 			}
 93 		}
 94 
 95 		if (!bExtend) {
 96 			for (var funName in process) {
 97 				if (typeof process[funName] == "function") {
 98 					this.process[funName] = _makeFunction.call(this, process, funName);
 99 				}
100 			}	
101 		}
102 
103 		if (typeof init == "function") {
104 			init.apply(this, params);
105 		}
106 	};
107 
108 	widget.getEvents = function(action, el, nodes) {
109 		var me = this;
110 		if (!nodes) {
111 			nodes = this.node;
112 		}
113 		if (typeof this.events[action] == "function") {
114 			return function(e) {
115 				return !(me.events[action].call(me, e, el, nodes) === false);
116 			}
117 		}
118 		return null;
119 	};
120 
121 	widget.bindEvents = function(el, evt, action, nodes) {
122 		evt = evt.toLowerCase();
123 		if ($.inArray(evt, 
124 			("blur focus focusin focusout load resize scroll unload click dblclick " +
125 	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
126 	"change select submit keydown keypress keyup error contextmenu").split(" ")) == -1) return;
127 
128 		var callback = widget.getEvents.call(this, action, el, nodes);
129 		if (callback) {
130 			$(el).on(evt, callback);
131 		}
132 	};
133 
134 	widget.bindCustomizedEvents = function(el, evt, param, nodes) {
135 		if (typeof widget.customizedEvents[evt] == "function") {
136 			if (!nodes) nodes = this.node;
137 			widget.customizedEvents[evt].call(this, el, param, nodes);
138 		}
139 	};
140 
141 	
142 	widget.getWidgetConfig = function(name) {
143 		var ret = widget.store[name];
144 		if (!ret) {
145 			$we.Exception(name + " is not registered");
146 			return false;
147 		}
148 		if (!ret.interfaces) ret.interfaces = {};
149 		if (!ret.events) ret.events = {};
150 		if (!ret.notifies) ret.notifies = {};
151 		if (!ret.process) ret.process = _getDefaultProcess();
152 		return ret;
153 	};
154 
155 	widget.emNode = function(node, nodes, group) {
156 		if (group) {
157 			group.node = {};
158 		} else {
159 			group = this;
160 		}
161 		if (!nodes) {
162 			try {
163 				nodes = node.getElementsByTagName("em");
164 			} catch (e) {
165 				$we.Exception("no nodes specified!");
166 				return;
167 			}
168 		}
169 		var toBind = [];
170 		for (var i = 0; nodes && i < nodes.length; ++i) {
171 			var text = $(nodes[i]).attr("attr");
172 			if (text && typeof text == "string") {
173 				var arr = text.split(";"),
174 					el = nodes[i];
175 				for (var j = 0; j < arr.length; ++j) {
176 					var t = arr[j].split(":");
177 					if (1 == t.length && t[0]) {
178 						group.node[t[0]] = el;
179 					}
180 					if (2 == t.length) {
181 						if ("inner" == t[0]) {
182 							group.node[t[1]] = el;
183 						} else {
184 							// 处理完元素element相关内容,开始处理events和customizedEvents
185 							toBind.push({
186 								el: el,
187 								key: t[0],
188 								value: t[1]
189 							});
190 						}
191 					}
192 				}
193 			}
194 		}
195 		for (var i = 0; i < toBind.length; ++i) {
196 			widget.bindEvents.call(this, toBind[i].el, toBind[i].key, toBind[i].value, group);
197 			widget.bindCustomizedEvents.call(this, toBind[i].el, toBind[i].key, toBind[i].value, group);
198 		}
199 	};
200 
201 	widget.setNotifyTo = function(obj) {
202 		if (!obj._notifyMembers) obj._notifyMembers = [];
203 		obj._notifyMembers.push(this);
204 		this._notifyTo = obj;
205 		return this;
206 	};
207 
208 	widget.notify = function(type) {
209 		var param = [];
210 		for (var i = 1; i < arguments.length; ++i) {
211 			param.push(arguments[i]);
212 		}
213 		var notifyTo = this._notifyTo;
214 		while (notifyTo) {
215 			if (notifyTo.notifies && typeof notifyTo.notifies[type] == "function") {
216 				return notifyTo.notifies[type].apply(notifyTo, param);
217 			}
218 			notifyTo = notifyTo._notifyTo;
219 		}
220 	};
221 
222 	widget.extend = function(p, params) {
223 		if (typeof p == "string") p = [p];
224 		if (!$.isArray(p)) return this;
225 		if (!$.isArray(params)) params = [params];
226 
227 		for (var i = 0; i < p.length; ++i) {
228 			var c = widget.getWidgetConfig(p[i]);
229 			if (!c) continue;
230 			widget.extendMethod.call(this, c.interfaces, c.events, c.notifies, c.process, c.init, params, true);
231 		}
232 		return this;
233 	};
234 
235 	widget.enableNode = function(node, group){
236 		// node最好还是element,否则可能会有问题
237 		if (typeof node == "string") node = $("#" + node)[0];
238 		if (!node) return;
239 		
240 		var attrs = $(node).find("[attr]").toArray();
241 		attrs.push(node);
242 		this.emNode(node, attrs, group);
243 	};
244 
245 	widget.domOperation = function(type, node, str, conf, group, bNoEnable) {
246 		if (typeof node == "string") node = $("#" + node)[0];
247 		if (!node) return;
248 
249 		str = (""+str).replace(/\$(\w+)\$/g, function(a, b) {
250 			return typeof conf[b] != "undefined" ? conf[b] : "$" + b + "$"
251 		});
252 
253 		$(widget.tmpDiv).empty();
254 		$(widget.tmpDiv).append(str);
255 		if (!bNoEnable) {
256 			this.enableNode(widget.tmpDiv, group);
257 		}
258 
259 		var childNodes = widget.tmpDiv.childNodes;
260 		if(node){
261 			switch (type) {
262 				case "append":
263 					while(childNodes.length){
264 						node.appendChild(childNodes[0]);	
265 					}
266 				break;
267 				case "after":
268 					while(childNodes.length){
269 						$(node).after(childNodes[0]);
270 					}
271 				break;
272 				case "before":
273 					while(childNodes.length){
274 						$(node).before(childNodes[0]);
275 					}
276 				break;
277 			}
278 			
279 		}
280 	};
281 
282 	widget.append = function(node, str, conf, group, bNoEnable) {
283 		widget.domOperation.call(this, "append", node, str, conf, group, bNoEnable);
284 	};
285 
286 	widget.after = function(node, str, conf, group, bNoEnable) {
287 		widget.domOperation.call(this, "after", node, str, conf, group, bNoEnable);
288 	};
289 
290 	widget.before = function(node, str, conf, group, bNoEnable) {
291 		widget.domOperation.call(this, "before", node, str, conf, group, bNoEnable);
292 	};
293 
294 	widget.prototype = {
295 		// 这4个方法是最最基础的方法
296 		setNotifyTo: widget.setNotifyTo,
297 		extend: widget.extend,
298 		notify: widget.notify,
299 		emNode: widget.emNode,
300 		// 以下的方法都是基于上面的方法实现出来的
301 		enableNode: widget.enableNode,
302 		append: widget.append,
303 		after: widget.after,
304 		before: widget.before
305 	};
306 
307 	/**
308 	 * widget类
309 	 * @class widget类
310 	 */
311 	$we.widget = {
312 		/**
313 		 * 新增一个widget类的方法
314 		 * @param {String} name 方法名称
315 		 * @param {Function} func 函数
316 		 */
317 		addWidgetFunc: function(name, func) {
318 			widget.prototype[name] = func;
319 		},
320 		/**
321 		 * 新增一个widget类的事件
322 		 * @param {String} name 事件名称
323 		 * @param {Function} func 函数
324 		 */
325 		addCustomizedEvents: function(name, func) {
326 			widget.customizedEvents[name] = func;
327 		},
328 		/**
329 		 * 使用一个组件
330 		 * @param {Object} param 参数
331 		 */
332 		add: function(param) {
333 			if (typeof param == "string") {
334 				param = {
335 					name: param
336 				};
337 			}
338 			var obj = widget.getWidgetConfig(param.name),
339 				params = [];
340 			if (!obj) {
341 				$we.Exception(param.name + " is not registered");
342 				return;
343 			}
344 			for (var i = 1; i < arguments.length; ++i) {
345 				params.push(arguments[i]);
346 			}
347 
348 			var el = new widget(
349 				param.name,
350 				obj.init,
351 				obj.run,
352 				params,
353 				obj.interfaces,
354 				obj.events,
355 				obj.notifies,
356 				obj.process,
357 				param && param.notifyTo,
358 				param && param.extend
359 			);
360 			return el;
361 		},
362 		/**
363 		 * 注册一个组件
364 		 * @param  {String} name 组件名称
365 		 * @param  {Object} obj  组件配置
366 		 */
367 		reg: function(name, obj) {
368 			widget.store[name] = obj;
369 		},
370 		/**
371 		 * 支持AMD规范的组件返回值
372 		 * @param  {String} name 需要返回的组件名称
373 		 * @return {Function}      可以实例化一个组件的函数
374 		 */
375 		amd: function(name) {
376 			return function() {
377 				var args = [name].concat($(arguments).toArray());
378 				return $we.widget.add.apply(this, args);
379 			};
380 		}
381 	};
382 
383 	$we.widget.addCustomizedEvents("hover", function(el, param) {
384 		$(el).mouseover(function(){
385 			$(el).addClass(param);
386 		}).mouseout(function(){
387 			$(el).removeClass(param);
388 		});
389 	});
390 
391 	$we.widget.addWidgetFunc("appendHTML", function(str, node, conf, group) {
392 		this.append(node, str, conf, group);
393 	});
394 
395 	// add as jq plugin
396 	$.fn.addWidget = function() {
397 		var params = [];
398 		if (this.length && this[0].name) {
399 			params.push(this[0]);		
400 		} else if (this.selector) {
401 			params.push(this.selector);
402 		} else {
403 			$we.Exception("invalid params of using addWidget");
404 		}
405 
406 		for (var i=0, l=arguments.length; i<l; ++i) {
407 			params.push(arguments[i]);
408 		}
409 
410 		return $we.widget.add.apply(window, params);
411 	};
412 
413 	return $we.widget;
414 
415 });