[转]prototype 源码解读 超强推荐第2/3页
更新时间:2007年02月13日 00:00:00 作者:
ajax.js 代码:
复制代码 代码如下:
/**
2
3 * 定义 Ajax 对象, 静态方法 getTransport 方法返回一个 XMLHttp 对象
4
5 */
6
7 var Ajax = {
8
9 getTransport: function() {
10
11 return Try.these(
12
13 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
14
15 function() {return new ActiveXObject('Microsoft.XMLHTTP')},
16
17 function() {return new XMLHttpRequest()}
18
19 ) || false;
20
21 },
22
23
24 emptyFunction: function() {}
25
26 }
27
28
29 /**
30
31 * 我以为此时的Ajax对象起到命名空间的作用。
32
33 * Ajax.Base 声明为一个基础对象类型
34
35 * 注意 Ajax.Base 并没有使用 Class.create() 的方式来创建,我想是因为作者并不
36
37 * 希望 Ajax.Base 被库使用者实例化。
38
39 * 作者在其他对象类型的声明中,将会继承于它。
40
41 * 就好像 java 中的私有抽象类
42
43 */
44
45 Ajax.Base = function() {};
46
47 Ajax.Base.prototype = {
48
49 /**
50
51 * extend (见prototype.js中的定义) 的用法真是让人耳目一新
52
53 * options 首先设置默认属性,然后再 extend 参数对象,那么参数对象中也有同名
54
55 * 的属性,那么就覆盖默认属性值。
56
57 * 想想如果我写这样的实现,应该类似如下:
58
59 setOptions: function(options) {
60
61 this.options.methed = options.methed? options.methed : 'post';
62
63 ..........
64
65 }
66
67 我想很多时候,java 限制了 js 的创意。
68
69 */
70
71 setOptions: function(options) {
72
73 this.options = {
74
75 method: 'post',
76
77 asynchronous: true,
78
79 parameters: ''
80
81 }.extend(options || {});
82
83 }
84
85 }
86
87
88
89 /**
90
91 * Ajax.Request 封装 XmlHttp
92
93 */
94
95 Ajax.Request = Class.create();
96
97
98 /**
99
100 * 定义四种事件(状态), 参考
101
102 * http://msdn.microsoft.com/workshop/
103
104 * author/dhtml/reference/properties/readystate_1.asp
105
106 */
107
108 Ajax.Request.Events =
109
110 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
111
112
113 /**
114
115 *
116
117 */
118
119 Ajax.Request.prototype = (new Ajax.Base()).extend({
120
121 initialize: function(url, options) {
122
123 this.transport = Ajax.getTransport();
124
125 this.setOptions(options);
126
127
128 try {
129
130 if (this.options.method == 'get')
131
132 url += '?' + this.options.parameters + '&_=';
133
134
135 /**
136
137 * 此处好像强制使用了异步方式,而不是依照 this.options.asynchronous 的值
138
139 */
140
141 this.transport.open(this.options.method, url, true);
142
143
144 /**
145
146 * 这里提供了 XmlHttp 传输过程中每个步骤的回调函数
147
148 */
149
150 if (this.options.asynchronous) {
151
152 this.transport.onreadystatechange = this.onStateChange.bind(this);
153
154 setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
155
156 }
157
158
159 this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
160
161 this.transport.setRequestHeader('X-Prototype-Version', Prototype.Version);
162
163
164 if (this.options.method == 'post') {
165
166 this.transport.setRequestHeader('Connection', 'close');
167
168 this.transport.setRequestHeader('Content-type',
169
170 'application/x-www-form-urlencoded');
171
172 }
173
174
175 this.transport.send(this.options.method == 'post' ?
176
177 this.options.parameters + '&_=' : null);
178
179
180 } catch (e) {
181
182 }
183
184 },
185
186
187 onStateChange: function() {
188
189 var readyState = this.transport.readyState;
190
191 /**
192
193 * 如果不是 Loading 状态,就调用回调函数
194
195 */
196
197 if (readyState != 1)
198
199 this.respondToReadyState(this.transport.readyState);
200
201 },
202
203
204 /**
205
206 * 回调函数定义在 this.options 属性中,比如:
207
208 var option = {
209
210 onLoaded : function(req) {...};
211
212 ......
213
214 }
215
216 new Ajax.Request(url, option);
217
218 */
219
220 respondToReadyState: function(readyState) {
221
222 var event = Ajax.Request.Events[readyState];
223
224 (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
225
226 }
227
228 });
229
230
231 /**
232
233 * Ajax.Updater 用于绑定一个html元素与 XmlHttp调用的返回值。
234
235 * 类似与 buffalo 的 bind。
236
237 * 如果 options 中有 insertion(from dom.js) 对象的话,
238
239 * insertion 能提供更多的插入控制。
240
241 */
242
243 Ajax.Updater = Class.create();
244
245 Ajax.Updater.prototype = (new Ajax.Base()).extend({
246
247 initialize: function(container, url, options) {
248
249 this.container = $(container);
250
251 this.setOptions(options);
252
253
254 if (this.options.asynchronous) {
255
256 this.onComplete = this.options.onComplete;
257
258 this.options.onComplete = this.updateContent.bind(this);
259
260 }
261
262
263 this.request = new Ajax.Request(url, this.options);
264
265
266 if (!this.options.asynchronous)
267
268 this.updateContent();
269
270 },
271
272
273 updateContent: function() {
274
275 if (this.options.insertion) {
276
277 new this.options.insertion(this.container,
278
279 this.request.transport.responseText);
280
281 } else {
282
283 this.container.innerHTML = this.request.transport.responseText;
284
285 }
286
287
288 if (this.onComplete) {
289
290 setTimeout((function() {this.onComplete(this.request)}).bind(this), 10);
291
292 }
293
294 }
295
296 });
form.js 代码:
复制代码 代码如下:
/**
2
3 * 针对 页面元素对象 的工具类,提供一些简单静态方法
4
5 */
6
7 var Field = {
8
9 /**
10
11 * 清除参数引用对象的值
12
13 */
14
15 clear: function() {
16
17 for (var i = 0; i < arguments.length; i++)
18
19 $(arguments[i]).value = '';
20
21 },
22
23
24 /**
25
26 * 使参数引用对象获取焦点
27
28 */
29
30 focus: function(element) {
31
32 $(element).focus();
33
34 },
35
36
37 /**
38
39 * 判断参数引用对象值是否为空,如为空,返回false, 反之true
40
41 */
42
43 present: function() {
44
45 for (var i = 0; i < arguments.length; i++)
46
47 if ($(arguments[i]).value == '') return false;
48
49 return true;
50
51 },
52
53
54 /**
55
56 * 使选中参数引用对象
57
58 */
59
60 select: function(element) {
61
62 $(element).select();
63
64 },
65
66
67 /**
68
69 * 使参数引用对象处于可编辑状态
70
71 */
72
73 activate: function(element) {
74
75 $(element).focus();
76
77 $(element).select();
78
79 }
80
81 }
82
83
84 /*-----------------------------------------------------------------*/
85
86
87 /**
88
89 * 表单工具类
90
91 */
92
93 var Form = {
94
95 /**
96
97 * 将表单元素序列化后的值组合成 QueryString 的形式
98
99 */
100
101 serialize: function(form) {
102
103 var elements = Form.getElements($(form));
104
105 var queryComponents = new Array();
106
107
108 for (var i = 0; i < elements.length; i++) {
109
110 var queryComponent = Form.Element.serialize(elements[i]);
111
112 if (queryComponent)
113
114 queryComponents.push(queryComponent);
115
116 }
117
118
119 return queryComponents.join('&');
120
121 },
122
123
124 /**
125
126 * 得到表单的所有元素对象
127
128 */
129
130 getElements: function(form) {
131
132 form = $(form);
133
134 var elements = new Array();
135
136
137 for (tagName in Form.Element.Serializers) {
138
139 var tagElements = form.getElementsByTagName(tagName);
140
141 for (var j = 0; j < tagElements.length; j++)
142
143 elements.push(tagElements[j]);
144
145 }
146
147 return elements;
148
149 },
150
151
152 /**
153
154 * 将指定表单的元素置于不可用状态
155
156 */
157
158 disable: function(form) {
159
160 var elements = Form.getElements(form);
161
162 for (var i = 0; i < elements.length; i++) {
163
164 var element = elements[i];
165
166 element.blur();
167
168 element.disable = 'true';
169
170 }
171
172 },
173
174
175 /**
176
177 * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点
178
179 */
180
181 focusFirstElement: function(form) {
182
183 form = $(form);
184
185 var elements = Form.getElements(form);
186
187 for (var i = 0; i < elements.length; i++) {
188
189 var element = elements[i];
190
191 if (element.type != 'hidden' && !element.disabled) {
192
193 Field.activate(element);
194
195 break;
196
197 }
198
199 }
200
201 },
202
203
204 /*
205
206 * 重置表单
207
208 */
209
210 reset: function(form) {
211
212 $(form).reset();
213
214 }
215
216 }
217
218
219 /**
220
221 * 表单元素工具类
222
223 */
224
225 Form.Element = {
226
227 /**
228
229 * 返回表单元素的值先序列化再进行 URL 编码后的值
230
231 */
232
233 serialize: function(element) {
234
235 element = $(element);
236
237 var method = element.tagName.toLowerCase();
238
239 var parameter = Form.Element.Serializers[method](element);
240
241
242 if (parameter)
243
244 return encodeURIComponent(parameter[0]) + '=' +
245
246 encodeURIComponent(parameter[1]);
247
248 },
249
250
251 /**
252
253 * 返回表单元素序列化后的值
254
255 */
256
257 getValue: function(element) {
258
259 element = $(element);
260
261 var method = element.tagName.toLowerCase();
262
263 var parameter = Form.Element.Serializers[method](element);
264
265
266 if (parameter)
267
268 return parameter[1];
269
270 }
271
272 }
273
274
275 /**
276
277 * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组
278
279 */
280
281 Form.Element.Serializers = {
282
283 input: function(element) {
284
285 switch (element.type.toLowerCase()) {
286
287 case 'hidden':
288
289 case 'password':
290
291 case 'text':
292
293 return Form.Element.Serializers.textarea(element);
294
295 case 'checkbox':
296
297 case 'radio':
298
299 return Form.Element.Serializers.inputSelector(element);
300
301 }
302
303 return false;
304
305 },
306
307
308 inputSelector: function(element) {
309
310 if (element.checked)
311
312 return [element.name, element.value];
313
314 },
315
316
317 textarea: function(element) {
318
319 return [element.name, element.value];
320
321 },
322
323
324 /**
325
326 * 看样子,也不支持多选框(select-multiple)
327
328 */
329
330 select: function(element) {
331
332 var index = element.selectedIndex;
333
334 var value = element.options[index].value || element.options[index].text;
335
336 return [element.name, (index >= 0) ? value : ''];
337
338 }
339
340 }
341
342
343 /*--------------------------------------------------------------------------*/
344
345
346 /**
347
348 * Form.Element.getValue 也许会经常用到,所以做了一个快捷引用
349
350 */
351
352 var $F = Form.Element.getValue;
353
354
355 /*--------------------------------------------------------------------------*/
356
357
358 /**
359
360 * Abstract.TimedObserver 也没有用 Class.create() 来创建,
361
362 * 和Ajax.Base 意图应该一样
363
364 * Abstract.TimedObserver 顾名思义,
365
366 * 是套用Observer设计模式来跟踪指定表单元素,
367
368 * 当表单元素的值发生变化的时候,就执行回调函数
369
370 *
371
372 * 我想 Observer 与注册onchange事件相似,
373
374 * 不同点在于 onchange 事件是在元素失去焦点
375
376 * 的时候才激发。
377
378 * 同样的与 onpropertychange 事件也相似,
379
380 * 不过它只关注表单元素的值的变化,而且提供timeout的控制。
381
382 *
383
384 * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,
385
386 * 这就比注册事件要灵活一些。
387
388 * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等
389
390 *
391
392 */
393
394 Abstract.TimedObserver = function() {}
395
396
397 /**
398
399 * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心
400
401 */
402
403 Abstract.TimedObserver.prototype = {
404
405 initialize: function(element, frequency, callback) {
406
407 this.frequency = frequency;
408
409 this.element = $(element);
410
411 this.callback = callback;
412
413
414 this.lastValue = this.getValue();
415
416 this.registerCallback();
417
418 },
419
420
421 registerCallback: function() {
422
423 setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
424
425 },
426
427
428 onTimerEvent: function() {
429
430 var value = this.getValue();
431
432 if (this.lastValue != value) {
433
434 this.callback(this.element, value);
435
436 this.lastValue = value;
437
438 }
439
440
441 this.registerCallback();
442
443 }
444
445 }
446
447
448 /**
449
450 * Form.Element.Observer 和 Form.Observer 其实是一样的
451
452 * 注意 Form.Observer 并不是用来跟踪整个表单的,我想大概只是
453
454 * 为了减少书写(这是Ruby的一个设计原则)
455
456 */
457
458 Form.Element.Observer = Class.create();
459
460 Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
461
462 getValue: function() {
463
464 return Form.Element.getValue(this.element);
465
466 }
467
468 });
469
470
471 Form.Observer = Class.create();
472
473 Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
474
475 getValue: function() {
476
477 return Form.serialize(this.element);
478
479 }
480
481 });
最新评论