prototype 源码解读 之 prototype.js

  1/*
  2 * 定义一个全局对象, 属性 Version 在发布的时候会替换为当前版本号 
  3 */
 
  4var Prototype = 
  5  Version: '@@VERSION@@' 
  6}
 
  7
  8/*
  9 * 创建一种类型,注意其属性 create 是一个方法,返回一个构造函数。 
 10 * 一般使用如下  
 11 *     var X = Class.create();  返回一个类型,类似于 java 的一个Class实例。 
 12 * 要使用 X 类型,需继续用 new X()来获取一个实例,如同 java 的 Class.newInstance()方法。 
 13 * 
 14 * 返回的构造函数会执行名为 initialize 的方法, initialize 是 Ruby 对象的构造器方法名字。 
 15 * 此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。 
 16 * 
 17 * 如果一定要从java上去理解。你可以理解为用Class.create()创建一个继承java.lang.Class类的类。当然java不允许这样做,因为Class类是final的 
 18 * 
 19 */
 
 20var Class = 
 21  create: function() 
 22    return function() 
 23      this.initialize.apply(this, arguments); 
 24    }
 
 25  }
 
 26}
 
 27
 28/*
 29 * 创建一个对象,从变量名来思考,本意也许是定义一个抽象类,以后创建新对象都 extend 它。 
 30 * 但从其后代码的应用来看, Abstract 更多是为了保持命名空间清晰的考虑。 
 31 * 也就是说,我们可以给 Abstract 这个对象实例添加新的对象定义。 
 32 * 
 33 * 从java去理解,就是动态给一个对象创建内部类。 
 34 */
 
 35var Abstract = new Object(); 
 36
 37/*
 38 * 获取参数对象的所有属性和方法,有点象多重继承。但是这种继承是动态获得的。 
 39 * 如: 
 40 *     var a = new ObjectA(), b = new ObjectB(); 
 41 *     var c = a.extend(b); 
 42 * 此时 c 对象同时拥有 a 和 b 对象的属性和方法。但是与多重继承不同的是,c instanceof ObjectB 将返回false。 
 43 */
 
 44Object.prototype.extend = function(object) 
 45  for (property in object) 
 46    this[property] = object[property]; 
 47  }
 
 48  return this
 49}
 
 50
 51/*
 52 * 这个方法很有趣,它封装一个javascript函数对象,返回一个新函数对象,新函数对象的主体和原对象相同,但是bind()方法参数将被用作当前对象的对象。 
 53 * 也就是说新函数中的 this 引用被改变为参数提供的对象。 
 54 * 比如: 
 55 *     <input type="text" id="aaa" value="aaa"> 
 56 *     <input type="text" id="bbb" value="bbb"> 
 57 *     .. 
 58 *     <script> 
 59 *         var aaa = document.getElementById("aaa"); 
 60 *         var bbb = document.getElementById("bbb"); 
 61 *         aaa.showValue = function() {alert(this.value);} 
 62 *         aaa.showValue2 = aaa.showValue.bind(bbb); 
 63 *     </script> 
 64 *  那么,调用aaa.showValue 将返回"aaa", 但调用aaa.showValue2 将返回"bbb"。 
 65 * 
 66 * apply 是ie5.5后才出现的新方法(Netscape好像很早就支持了)。 
 67 * 该方法更多的资料参考MSDN http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp 
 68 * 还有一个 call 方法,应用起来和 apply 类似。可以一起研究下。 
 69 */
 
 70Function.prototype.bind = function(object) 
 71  var method = this
 72  return function() 
 73    method.apply(object, arguments); 
 74  }
 
 75}
 
 76
 77/*
 78 * 和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象 
 79 * 注意这时候,用到了 Function.call。它与 Function.apply 的不同好像仅仅是对参数形式的定义。 
 80 * 如同 java 两个过载的方法。 
 81 */
 
 82Function.prototype.bindAsEventListener = function(object) 
 83  var method = this
 84  return function(event) 
 85    method.call(object, event || window.event); 
 86  }
 
 87}
 
 88
 89/*
 90 * 将整数形式RGB颜色值转换为HEX形式 
 91 */
 
 92Number.prototype.toColorPart = function() 
 93  var digits = this.toString(16); 
 94  if (this < 16return '0+ digits; 
 95  return digits; 
 96}
 
 97
 98/*
 99 * 典型 Ruby 风格的函数,将参数中的方法逐个调用,返回第一个成功执行的方法的返回值 
100 */
 
101var Try = 
102  these: function() 
103    var returnValue; 
104    
105    for (var i = 0; i < arguments.length; i++
106      var lambda = arguments[i]; 
107      try 
108        returnValue = lambda(); 
109        break
110      }
 catch (e) {} 
111    }
 
112    
113    return returnValue; 
114  }
 
115}
 
116
117/*--------------------------------------------------------------------------*/ 
118
119/*
120 * 一个设计精巧的定时执行器 
121 * 首先由 Class.create() 创建一个 PeriodicalExecuter 类型, 
122 * 然后用对象直接量的语法形式设置原型。 
123 * 
124 * 需要特别说明的是 rgisterCallback 方法,它调用上面定义的函数原型方法bind, 并传递自己为参数。 
125 * 之所以这样做,是因为 setTimeout 默认总以 window 对象为当前对象,也就是说,如果 registerCallback 方法定义如下的话: 
126 *     registerCallback: function() { 
127 *         setTimeout(this.onTimerEvent, this.frequency * 1000); 
128 *     } 
129 * 那么,this.onTimeoutEvent 方法执行失败,因为它无法访问 this.currentlyExecuting 属性。 
130 * 而使用了bind以后,该方法才能正确的找到this,也就是PeriodicalExecuter的当前实例。 
131 */
 
132var PeriodicalExecuter = Class.create(); 
133PeriodicalExecuter.prototype = 
134  initialize: function(callback, frequency) 
135    this.callback = callback; 
136    this.frequency = frequency; 
137    this.currentlyExecuting = false
138    
139    this.registerCallback(); 
140  }

141  
142  registerCallback: function() 
143    setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000); 
144  }

145  
146  onTimerEvent: function() 
147    if (!this.currentlyExecuting) 
148      try 
149        this.currentlyExecuting = true
150        this.callback(); 
151      }
 finally 
152        this.currentlyExecuting = false
153      }
 
154    }
 
155    
156    this.registerCallback(); 
157  }
 
158}
 
159
160/*--------------------------------------------------------------------------*/ 
161
162/*
163 * 这个函数就 Ruby 了。我觉得它的作用主要有两个 
164 * 1.  大概是 document.getElementById(id) 的最简化调用。 
165 * 比如:$("aaa") 将返回上 aaa 对象 
166 * 2.  得到对象数组 
167 * 比如: $("aaa","bbb") 返回一个包括id为"aaa"和"bbb"两个input控件对象的数组。 
168 */
 
169function $() 
170  var elements = new Array(); 
171  
172  for (var i = 0; i < arguments.length; i++
173    var element = arguments[i]; 
174    if (typeof element == 'string') 
175      element = document.getElementById(element); 
176
177    if (arguments.length == 1
178      return element; 
179      
180    elements.push(element); 
181  }
 
182  
183  return elements; 
184}
 
185
186
187/*
188 * 定义 Ajax 对象, 静态方法 getTransport 方法返回一个 XMLHttp 对象 
189 */
 
190var Ajax = 
191  getTransport: function() 
192    return Try.these( 
193      function() {return new ActiveXObject('Msxml2.XMLHTTP')}
194      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
195      function() {return new XMLHttpRequest()} 
196    ) || false
197  }

198  
199  emptyFunction: function() {} 
200}
 
201
202/*
203 * 我以为此时的Ajax对象起到命名空间的作用。 
204 * Ajax.Base 声明为一个基础对象类型 
205 * 注意 Ajax.Base 并没有使用 Class.create() 的方式来创建,我想是因为作者并不希望 Ajax.Base 被库使用者实例化。 
206 * 作者在其他对象类型的声明中,将会继承于它。 
207 * 就好像 java 中的私有抽象类 
208 */
 
209Ajax.Base = function() {}
210Ajax.Base.prototype = 
211  /*
212   * extend (见prototype.js中的定义) 的用法真是让人耳目一新 
213   * options 首先设置默认属性,然后再 extend 参数对象,那么参数对象中也有同名的属性,那么就覆盖默认属性值。 
214   * 想想如果我写这样的实现,应该类似如下: 
215     setOptions: function(options) { 
216      this.options.methed = options.methed? options.methed : 'post'; 
217      
218     } 
219     我想很多时候,java 限制了 js 的创意。 
220   */
 
221  setOptions: function(options) 
222    this.options = 
223      method:       'post', 
224      asynchronous: true
225      parameters:   '' 
226    }
.extend(options || {}); 
227  }
 
228}
 
229
230
231/*
232 * Ajax.Request 封装 XmlHttp 
233 */
 
234Ajax.Request = Class.create(); 
235
236/*
237 * 定义四种事件(状态), 参考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp 
238 */
 
239Ajax.Request.Events = 
240  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
241
242/*
243 * 
244 */
 
245Ajax.Request.prototype = (new Ajax.Base()).extend(
246  initialize: function(url, options) 
247    this.transport = Ajax.getTransport(); 
248    this.setOptions(options); 
249  
250    try 
251      if (this.options.method == 'get') 
252        url += '?+ this.options.parameters + '&_='; 
253    
254     /*
255      * 此处好像强制使用了异步方式,而不是依照 this.options.asynchronous 的值 
256      */
 
257      this.transport.open(this.options.method, url, true); 
258      
259     /*
260      * 这里提供了 XmlHttp 传输过程中每个步骤的回调函数 
261      */
 
262      if (this.options.asynchronous) 
263        this.transport.onreadystatechange = this.onStateChange.bind(this); 
264        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); 
265      }
 
266              
267      this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 
268      this.transport.setRequestHeader('X-Prototype-Version', Prototype.Version); 
269
270      if (this.options.method == 'post') 
271        this.transport.setRequestHeader('Connection', 'close'); 
272        this.transport.setRequestHeader('Content-type', 
273          'application/x-www-form-urlencoded'); 
274      }
 
275      
276      this.transport.send(this.options.method == 'post' ? 
277        this.options.parameters + '&_=' : null); 
278                      
279    }
 catch (e) 
280    }
    
281  }

282      
283  onStateChange: function() 
284    var readyState = this.transport.readyState; 
285   /*
286    * 如果不是 Loading 状态,就调用回调函数 
287     */
 
288    if (readyState != 1
289      this.respondToReadyState(this.transport.readyState); 
290  }

291  
292  /*
293   * 回调函数定义在 this.options 属性中,比如: 
294      var option = { 
295         onLoaded : function(req) {}; 
296          
297      } 
298      new Ajax.Request(url, option); 
299   */
 
300  respondToReadyState: function(readyState) 
301    var event = Ajax.Request.Events[readyState]; 
302    (this.options['on' + event] || Ajax.emptyFunction)(this.transport); 
303  }
 
304}
); 
305
306/*
307 * Ajax.Updater 用于绑定一个html元素与 XmlHttp调用的返回值。类似与 buffalo 的 bind。 
308 * 如果 options 中有 insertion(from dom.js) 对象的话, insertion 能提供更多的插入控制。 
309 */
 
310Ajax.Updater = Class.create(); 
311Ajax.Updater.prototype = (new Ajax.Base()).extend(
312  initialize: function(container, url, options) 
313    this.container = $(container); 
314    this.setOptions(options); 
315  
316    if (this.options.asynchronous) 
317      this.onComplete = this.options.onComplete; 
318      this.options.onComplete = this.updateContent.bind(this); 
319    }
 
320    
321    this.request = new Ajax.Request(url, this.options); 
322    
323    if (!this.options.asynchronous) 
324      this.updateContent(); 
325  }

326  
327  updateContent: function() 
328    if (this.options.insertion) 
329      new this.options.insertion(this.container, 
330        this.request.transport.responseText); 
331    }
 else 
332      this.container.innerHTML = this.request.transport.responseText; 
333    }
 
334
335    if (this.onComplete) 
336      setTimeout((function() {this.onComplete(this.request)}).bind(this), 10); 
337    }
 
338  }
 
339}
); 
340
341
342/*
343 * 针对 页面元素对象 的工具类,提供一些简单静态方法 
344 */
 
345var Field = 
346  /*
347   * 清除参数引用对象的值 
348   */
 
349  clear: function() 
350    for (var i = 0; i < arguments.length; i++
351      $(arguments[i]).value = ''; 
352  }

353
354  /*
355   * 使参数引用对象获取焦点 
356   */
 
357  focus: function(element) 
358    $(element).focus(); 
359  }

360  
361  /*
362   * 判断参数引用对象值是否为空,如为空,返回false, 反之true 
363   */
 
364  present: function() 
365    for (var i = 0; i < arguments.length; i++
366      if ($(arguments[i]).value == '') return false
367    return true
368  }

369  
370  /*
371   * 使选中参数引用对象 
372   */
 
373  select: function(element) 
374    $(element).select(); 
375  }

376
377  /*
378   * 使参数引用对象处于可编辑状态 
379   */
 
380  activate: function(element) 
381    $(element).focus(); 
382    $(element).select(); 
383  }
 
384}
 
385
386/*--------------------------------------------------------------------------*/ 
387
388/*
389 * 表单工具类 
390 */
 
391var Form = 
392  /*
393   * 将表单元素序列化后的值组合成 QueryString 的形式 
394   */
 
395  serialize: function(form) 
396    var elements = Form.getElements($(form)); 
397    var queryComponents = new Array(); 
398    
399    for (var i = 0; i < elements.length; i++
400      var queryComponent = Form.Element.serialize(elements[i]); 
401      if (queryComponent) 
402        queryComponents.push(queryComponent); 
403    }
 
404    
405    return queryComponents.join('&'); 
406  }

407  
408  /*
409   * 得到表单的所有元素对象 
410   */
 
411  getElements: function(form) 
412    form = $(form); 
413    var elements = new Array(); 
414
415    for (tagName in Form.Element.Serializers) 
416      var tagElements = form.getElementsByTagName(tagName); 
417      for (var j = 0; j < tagElements.length; j++
418        elements.push(tagElements[j]); 
419    }
 
420    return elements; 
421  }

422  
423  /*
424   * 将指定表单的元素置于不可用状态 
425   */
 
426  disable: function(form) 
427    var elements = Form.getElements(form); 
428    for (var i = 0; i < elements.length; i++
429      var element = elements[i]; 
430      element.blur(); 
431      element.disable = 'true'; 
432    }
 
433  }

434
435  /*
436   * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点 
437   */
 
438  focusFirstElement: function(form) 
439    form = $(form); 
440    var elements = Form.getElements(form); 
441    for (var i = 0; i < elements.length; i++
442      var element = elements[i]; 
443      if (element.type != 'hidden' && !element.disabled) 
444        Field.activate(element); 
445        break
446      }
 
447    }
 
448  }

449
450  /* 
451   * 重置表单 
452   */
 
453  reset: function(form) 
454    $(form).reset(); 
455  }
 
456}
 
457
458/*
459 * 表单元素工具类 
460 */
 
461Form.Element = 
462  /*
463   * 返回表单元素的值先序列化再进行 URL 编码后的值 
464   */
 
465  serialize: function(element) 
466    element = $(element); 
467    var method = element.tagName.toLowerCase(); 
468    var parameter = Form.Element.Serializers[method](element); 
469    
470    if (parameter) 
471      return encodeURIComponent(parameter[0]) + '=+ 
472        encodeURIComponent(parameter[1]);                    
473  }

474  
475  /*
476   *  返回表单元素序列化后的值 
477   */
 
478  getValue: function(element) 
479    element = $(element); 
480    var method = element.tagName.toLowerCase(); 
481    var parameter = Form.Element.Serializers[method](element); 
482    
483    if (parameter) 
484      return parameter[1]; 
485  }
 
486}
 
487
488/*
489 * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组 
490 */
 
491Form.Element.Serializers = 
492  input: function(element) 
493    switch (element.type.toLowerCase()) 
494      case 'hidden': 
495      case 'password': 
496      case 'text': 
497        return Form.Element.Serializers.textarea(element); 
498      case 'checkbox':  
499      case 'radio': 
500        return Form.Element.Serializers.inputSelector(element); 
501    }
 
502    return false
503  }

504  
505  inputSelector: function(element) 
506    if (element.checked) 
507      return [element.name, element.value]; 
508  }

509
510  textarea: function(element) 
511    return [element.name, element.value]; 
512  }

513
514  /*
515   * 看样子,也不支持多选框(select-multiple) 
516   */
 
517  select: function(element) 
518    var index = element.selectedIndex; 
519    var value = element.options[index].value || element.options[index].text; 
520    return [element.name, (index >= 0? value : '']; 
521  }
 
522}
 
523
524/*--------------------------------------------------------------------------*/ 
525
526/*
527 * Form.Element.getValue 也许会经常用到,所以做了一个快捷引用 
528 */
 
529var $F = Form.Element.getValue; 
530
531/*--------------------------------------------------------------------------*/ 
532
533/*
534 * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样 
535 * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素, 
536 * 当表单元素的值发生变化的时候,就执行回调函数 
537 * 
538 * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。 
539 * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。 
540 * 
541 * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。 
542 * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等 
543 * 
544 */
 
545Abstract.TimedObserver = function() {} 
546
547/*
548 * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心 
549 */
 
550Abstract.TimedObserver.prototype = 
551  initialize: function(element, frequency, callback) 
552    this.frequency = frequency; 
553    this.element   = $(element); 
554    this.callback  = callback; 
555    
556    this.lastValue = this.getValue(); 
557    this.registerCallback(); 
558  }

559  
560  registerCallback: function() 
561    setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000); 
562  }

563  
564  onTimerEvent: function() 
565    var value = this.getValue(); 
566    if (this.lastValue != value) 
567      this.callback(this.element, value); 
568      this.lastValue = value; 
569    }
 
570    
571    this.registerCallback(); 
572  }
 
573}
 
574
575/*
576 * Form.Element.Observer 和 Form.Observer 其实是一样的 
577 * 注意 Form.Observer 并不是用来跟踪整个表单的,我想大概只是为了减少书写(这是Ruby的一个设计原则) 
578 */
 
579Form.Element.Observer = Class.create(); 
580Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend(
581  getValue: function() 
582    return Form.Element.getValue(this.element); 
583  }
 
584}
); 
585
586Form.Observer = Class.create(); 
587Form.Observer.prototype = (new Abstract.TimedObserver()).extend(
588  getValue: function() 
589    return Form.serialize(this.element); 
590  }
 
591}
); 
592
593
594/*
595 * 根据 class attribute 的名字得到对象数组,支持 multiple class 
596 * 
597 */
 
598document.getElementsByClassName = function(className) 
599  var children = document.getElementsByTagName('*') || document.all; 
600  var elements = new Array(); 
601  
602  for (var i = 0; i < children.length; i++
603    var child = children[i]; 
604    var classNames = child.className.split(' '); 
605    for (var j = 0; j < classNames.length; j++
606      if (classNames[j] == className) 
607        elements.push(child); 
608        break
609      }
 
610    }
 
611  }
 
612  
613  return elements; 
614}
 
615
616/*--------------------------------------------------------------------------*/ 
617
618/*
619 * Element 就象一个 java 的工具类,主要用来 隐藏/显示/销除 对象,以及获取对象的简单属性。 
620 * 
621 */
 
622var Element = 
623  toggle: function() 
624    for (var i = 0; i < arguments.length; i++
625      var element = $(arguments[i]); 
626      element.style.display = 
627        (element.style.display == 'none' ? '' : 'none'); 
628    }
 
629  }

630
631  hide: function() 
632    for (var i = 0; i < arguments.length; i++
633      var element = $(arguments[i]); 
634      element.style.display = 'none'; 
635    }
 
636  }

637
638  show: function() 
639    for (var i = 0; i < arguments.length; i++
640      var element = $(arguments[i]); 
641      element.style.display = ''; 
642    }
 
643  }

644
645  remove: function(element) 
646    element = $(element); 
647    element.parentNode.removeChild(element); 
648  }

649    
650  getHeight: function(element) 
651    element = $(element); 
652    return element.offsetHeight; 
653  }
 
654}
 
655
656/*
657 * 为 Element.toggle 做了一个符号连接,大概是兼容性的考虑 
658 */
 
659var Toggle = new Object(); 
660Toggle.display = Element.toggle; 
661
662/*--------------------------------------------------------------------------*/ 
663
664/*
665 * 动态插入内容的实现,MS的Jscript实现中对象有一个 insertAdjacentHTML 方法(http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp) 
666 * 这里算是一个对象形式的封装。 
667 */
 
668Abstract.Insertion = function(adjacency) 
669  this.adjacency = adjacency; 
670}
 
671
672Abstract.Insertion.prototype = 
673  initialize: function(element, content) 
674    this.element = $(element); 
675    this.content = content; 
676    
677    if (this.adjacency && this.element.insertAdjacentHTML) 
678      this.element.insertAdjacentHTML(this.adjacency, this.content); 
679    }
 else 
680     /*
681      * gecko 不支持 insertAdjacentHTML 方法,但可以用如下代码代替 
682      */
 
683      this.range = this.element.ownerDocument.createRange(); 
684     /*
685      * 如果定义了 initializeRange 方法,则实行,这里相当与定义了一个抽象的 initializeRange 方法 
686      */
 
687      if (this.initializeRange) this.initializeRange(); 
688      this.fragment = this.range.createContextualFragment(this.content); 
689
690     /*
691      * insertContent 也是一个抽象方法,子类必须实现 
692      */
 
693      this.insertContent(); 
694    }
 
695  }
 
696}
 
697
698/*
699 * prototype 加深了我的体会,就是写js 如何去遵循 Don’t Repeat Yourself (DRY) 原则 
700 * 上文中 Abstract.Insertion 算是一个抽象类,定义了名为 initializeRange 的一个抽象方法 
701 * var Insertion = new Object() 建立一个命名空间 
702 * Insertion.Before|Top|Bottom|After 就象是四个java中的四个静态内部类,而它们分别继承于Abstract.Insertion,并实现了initializeRange方法。 
703 */
 
704var Insertion = new Object(); 
705
706Insertion.Before = Class.create(); 
707Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend(
708  initializeRange: function() 
709    this.range.setStartBefore(this.element); 
710  }

711  
712  /*
713   * 将内容插入到指定节点的前面, 与指定节点同级 
714   */
 
715  insertContent: function() 
716    this.element.parentNode.insertBefore(this.fragment, this.element); 
717  }
 
718}
); 
719
720Insertion.Top = Class.create(); 
721Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend(
722  initializeRange: function() 
723    this.range.selectNodeContents(this.element); 
724    this.range.collapse(true); 
725  }

726  
727  /*
728   * 将内容插入到指定节点的第一个子节点前,于是内容变为该节点的第一个子节点 
729   */
 
730  insertContent: function() {  
731    this.element.insertBefore(this.fragment, this.element.firstChild); 
732  }
 
733}
); 
734
735Insertion.Bottom = Class.create(); 
736Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend(
737  initializeRange: function() 
738    this.range.selectNodeContents(this.element); 
739    this.range.collapse(this.element); 
740  }

741  
742  /*
743   * 将内容插入到指定节点的最后,于是内容变为该节点的最后一个子节点 
744   */
 
745  insertContent: function() 
746    this.element.appendChild(this.fragment); 
747  }
 
748}
); 
749
750
751Insertion.After = Class.create(); 
752Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend(
753  initializeRange: function() 
754    this.range.setStartAfter(this.element); 
755  }

756
757  /*
758   * 将内容插入到指定节点的后面, 与指定节点同级 
759   */
 
760  insertContent: function() 
761    this.element.parentNode.insertBefore(this.fragment, 
762      this.element.nextSibling); 
763  }
 
764}
); 
765


posted on 2006-09-13 14:24  xing  阅读(214)  评论(0)    收藏  举报