【Prototype 1.4.0】源码解读----全文注释版

/*   Prototype JavaScript framework, version 1.4.0
 *  (c) 2005 Sam Stephenson <[email protected]>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 
 *  这是一个JavaScript的框架,致力于简化动态的Web开发,完全按照面对对象的思想
 *  进行Javascript开发,添加了迭代器的概念增强Javascript的设计能力。
 *  Prototype框架构思独特充满了技巧性的代码,方便易用的工具类!
 *
/*--------------------------------------------------------------------------
*/
 
  
/*  【Prototype】定义一个全局对象,提供一个全局的基本信息和工具  */
var  Prototype  =  {
  Version: 
' 1.4.0 ' // 可以作为版本检测用
//
用于脚本检测的正则表达式,经常使用所以放在这里起到全局常量的作用
  ScriptFragment:  ' (?:<script.*?>)(( | |.)*?)(?:</script>) ' ,

  emptyFunction: 
function () {}, // 空函数
  K:  function (x) { return  x} // K方法返回参数本身,在后面经常用到
}

/* ======================================================================================== */

/*
 *【Class】对象的作用只有一个就是提供了一个定义类的模式
 * 仅含有create 一个方法,返回一个构造函数。 
 * 一般使用如下  
 *     var X = Class.create();  返回一个类型  
 * 要使用 X 类型,需继续用 new X()来获取一个实例 这与C# JAVA类似
 * 返回的构造函数会执行名为 initialize 的方法, initialize 是 Ruby 对象的构造器方法名字。 
 * 此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法,可以看作是一个抽象方法。 
 * 从C#角度讲可以理解为用Class.create()创建一个继承Object基类的类。 
 * 
 
*/  

var  Class  =  {
  create: 
function () {
    
return   function () {   // 下面使用Apply方法传递参数是一个常用的技巧
       this .initialize.apply( this , arguments);
    }
  }
}

/* ======================================================================================== */


// Abstract是一个空对象,它的作用仅仅是作为一个抽象的命名空间,所有在该对象下定义的类都是抽象类
//
从而从形式上与实体类分开
var  Abstract  =   new  Object();

/*
 *Object是所有对象的基类,这个基类有两个静态方法
 
*/  
// 把Source的所有属性和方法传递给Destination,实现了继承的效果
Object.extend  =   function (destination, source) {
  
for  (property  in  source) {
    destination[property] 
=  source[property];
  }
  
return  destination;
}
// 观察Object的组成,需要参数中的object实现自己的inspect方法
Object.inspect  =   function (object) {
  
try  {
    
if  (object  ==  undefined)  return   ' undefined ' ;
    
if  (object  ==   null return   ' null ' ;
    
return  object.inspect  ?  object.inspect() : object.toString();
  } 
catch  (e) {
    
if  (e  instanceof  RangeError)  return   '' ;
    
throw  e;
  }
}

/* ======================================================================================== */


// 【Function】是所有函数对象的基类,可以通过对它的扩展实现对所有函数对象添加功能
/*
 这里在绑定的时候旧可以传递参数而不是调用的时候才指定参数,bind方法接收多个参数
  将函数绑定到第一个参数指定的对象上,并返回该方法的调用句柄
*/
Function.prototype.bind 
=   function () {
  
//  $A()方法的作用是把参数专为数组 
   // 数组的shift方法作用是删除数组的第一个元素
   var  __method  =   this , args  =  $A(arguments), object  =  args.shift();
  
return   function () {
    
return  __method.apply(object, args.concat($A(arguments)));
    
// 这里的$A(arguments)是条用返回函数句柄时传递的参数,不是bind方法的参数
  }
}
/*
 * 和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象 
 * 好像是重载了_method方法
 
*/  

Function.prototype.bindAsEventListener 
=   function (object) {
  
var  __method  =   this ;
  
return   function (event) {
    
return  __method.call(object, event  ||  window.event);
  }
}

/* ======================================================================================== */

// 【Function】是所有数值类型的基类

Object.extend(Number.prototype, {
  toColorPart: 
function () {            // RGB-》16进制颜色
     var  digits  =   this .toString( 16 );
    
if  ( this   <   16 return   ' 0 '   +  digits;
    
return  digits;
  },

  succ: 
function () {  // 数值加一 var a=1; var b=a.succ; 那么b=2
     return   this   +   1 ;
  },

  times: 
function (iterator) { 
  
// 这里的参数iterator是一个迭代器作用就是循环调用指定次数的iterator函数
   // $R(start,end,exclusive)是创建ObjectRnge对象的快捷方式
    $R( 0 this true ).each(iterator);
    
return   this ;
  }
});

/* ======================================================================================== */

// 提供了一个方法these,要求参数是函数的句柄,可以有多个。作用就是返回第一个成功执行的函数的返回值
var  Try  =  {
  these: 
function () {
    
var  returnValue;

    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ ) {
      
var  lambda  =  arguments[i];
      
try  {
        returnValue 
=  lambda();
        
break ;
      } 
catch  (e) {}
    }

    
return  returnValue;
  }
}

/* ======================================================================================== */

// 定时器类用来实现Window.setInterval的效果,在给定时间间隔执行某一个函数,增加了对重复执行的控制S
var  PeriodicalExecuter  =  Class.create();
PeriodicalExecuter.prototype 
=  {
  initialize: 
function (callback, frequency) {
  
// 构造函数指定回调函数和执行频率,单位是秒
     this .callback  =  callback;
    
this .frequency  =  frequency;
    
this .currentlyExecuting  =   false ;

    
this .registerCallback();
  },
  
/*  开始调用定时器,无需显示调用,在构造函数中就实现了自动调用,这一下面的
this.onTimerEvent.bind(this)如果写成this.onTimerEvent则this指针就会指向widows对象即setInterval的默认对象
从而不能正确的引用到下面两个函数上,也就失去了对正在执行函数的控制
*/

  registerCallback: 
function () {
    setInterval(
this .onTimerEvent.bind( this ),  this .frequency  *   1000 );
  },
// 下面的函数相当于是回调函数的一个代理, setInterval是到了指定的时间就会强制执行而这里
//
加入了一个判断如果callback函数执行的时间超过了一个时间片,则阻止其被重复执行
  onTimerEvent:  function () {
    
if  ( ! this .currentlyExecuting) {
      
try  {
        
this .currentlyExecuting  =   true ;
        
this .callback();
      } 
finally  {
        
this .currentlyExecuting  =   false ;
      }
    }
  }
}
/* 使用举例:
function GetOnlineCount()
{//获得在线人数
}
new PeriodicalExecuter(GetOnlineCount,10)每10秒获取一次
*/

/* ======================================================================================== */
/*  框架核心内容--------【基础工具类】
/*========================================================================================
*/

 
/* document.getElementById(id)获取一个指定ID的结点,是这个方法的快捷方式和扩展
 可以指定多个参数返回一个对象数组。参数也不一定是ID也可以是对象本身的引用,例如
 $('id')等价于$($('id'))
  
*/
function  $() {
  
var  elements  =   new  Array();

  
for  ( var  i  =   0 ; i  <  arguments.length; i ++ ) {
    
var  element  =  arguments[i];
    
if  ( typeof  element  ==   ' string ' )
      element 
=  document.getElementById(element);

    
if  (arguments.length  ==   1 )
      
return  element;

    elements.push(element);
  }

  
return  elements;
}

/* ======================================================================================== */
// 【String】类的扩展

// 删除字符串中的HTML标记
Object.extend(String.prototype, {
  stripTags: 
function () {
    
return   this .replace( /</? [ ^> ] +>/ gi,  '' );
  },
// 删除字符串中的脚本块
  stripScripts:  function () {
    
return   this .replace( new  RegExp(Prototype.ScriptFragment,  ' img ' ),  '' );
  },
// 提取字符串中的所有脚本块,作为数组返回,每一个脚本作为一个数组元素
  extractScripts:  function () {
    
var  matchAll  =   new  RegExp(Prototype.ScriptFragment,  ' img ' );
    
var  matchOne  =   new  RegExp(Prototype.ScriptFragment,  ' im ' );
    
return  ( this .match(matchAll)  ||  []).map( function (scriptTag) {
      
return  (scriptTag.match(matchOne)  ||  [ '' '' ])[ 1 ];
    });
  },
// 提取字符串中的脚本并执行
  evalScripts:  function () {
    
return   this .extractScripts().map(eval);
  },
// 将字符串进行html编码例如"<" --》"&lt"
  escapeHTML:  function () {
    
var  div  =  document.createElement( ' div ' );
    
var  text  =  document.createTextNode( this );
    div.appendChild(text);
    
return  div.innerHTML;
  },
// 对字符串进行html解码例如"&lt"--》"<"
  unescapeHTML:  function () {
    
var  div  =  document.createElement( ' div ' );
    div.innerHTML 
=   this .stripTags();
    
return  div.childNodes[ 0 ?  div.childNodes[ 0 ].nodeValue :  '' ;
  },
// 将查询字符串格式的字符串转换为键值对数组
//
例如var s="a=1&b=2&c=3"; var s2=s.toQueryParams(); s2为{a:1,b:2,c:3}
  toQueryParams:  function () {
    
var  pairs  =   this .match( /^?? (. * )$ / )[ 1 ].split( ' & ' );
    
return  pairs.inject({},  function (params, pairString) {
      
var  pair  =  pairString.split( ' = ' );
      params[pair[
0 ]]  =  pair[ 1 ];
      
return  params;
    });
  },
// 字符串转换为字符数组 "abc"-->['a','b','c']
  toArray:  function () {
    
return   this .split( '' );
  },
// 连字符--》骆驼样式 'good-man'-->'goodMan'
  camelize:  function () {
    
var  oStringList  =   this .split( ' - ' );
    
if  (oStringList.length  ==   1 return  oStringList[ 0 ];

    
var  camelizedString  =   this .indexOf( ' - ' ==   0
      
?  oStringList[ 0 ].charAt( 0 ).toUpperCase()  +  oStringList[ 0 ].substring( 1 )
      : oStringList[
0 ];

    
for  ( var  i  =   1 , len  =  oStringList.length; i  <  len; i ++ ) {
      
var  s  =  oStringList[i];
      camelizedString 
+=  s.charAt( 0 ).toUpperCase()  +  s.substring( 1 );
    }

    
return  camelizedString;
  },
// 得到字符串的组成结构
  inspect:  function () {
/* 这个加注释为了让下面的显示好看点
    return "'" + this.replace('', '\').replace("'", ''') + "'";
*/   }
});
// 定义了一个等价的函数
String.prototype.parseQuery  =  String.prototype.toQueryParams;

/* ======================================================================================== */

// 【Enumerable】可枚举接口 是整个1.4.0框架的核心工具,所有实现此接口的类必须要实现_each(iterator)方法


var  $ break      =   new  Object();  // 首先定义了两个异常对象,用于进行迭代计算的控制
var  $ continue   =   new  Object();

var  Enumerable  =  {
// 用于对对象的每一个元素遍历执行iterator迭代器函数
  each:  function (iterator) {
    
var  index  =   0 ;                 // 可选参数表示元素在枚举对象的次序
     try  {
      
this ._each( function (value) { // value是枚举元素的值
         try  {
          iterator(value, index
++ );
        } 
catch  (e) {
          
if  (e  !=  $ continue throw  e;
        }
      });
    } 
catch  (e) {
      
if  (e  !=  $ break throw  e;
    }
  },
// 判断是否所有的枚举元素都能使iterator返回true
  all:  function (iterator) {
    
var  result  =   true ;
    
this .each( function (value, index) {
      result 
=  result  &&   !! (iterator  ||  Prototype.K)(value, index);
      
if  ( ! result)  throw  $ break ;
    });
    
return  result;
  },
// 判断是否有枚举元素能使iterator返回true,有一个就是True
  any:  function (iterator) {
    
var  result  =   true ;
    
this .each( function (value, index) {
      
if  (result  =   !! (iterator  ||  Prototype.K)(value, index))
        
throw  $ break ;
    });
    
return  result;
  },
// 对所有的枚举元素执行iterator迭代器函数 结果作为一个数组返回
  collect:  function (iterator) {
    
var  results  =  [];
    
this .each( function (value, index) {
      results.push(iterator(value, index));
    });
    
return  results;
  },
// 第一个素能使iterator返回true的枚举元素的值,没有返回undefined
  detect:  function  (iterator) {
    
var  result;
    
this .each( function (value, index) {
      
if  (iterator(value, index)) {
        result 
=  value;
        
throw  $ break ;
      }
    });
    
return  result;
  },
// 找到所有的能使iterator迭代器函数返回true的枚举元素 作为一个数组返回
  findAll:  function (iterator) {
    
var  results  =  [];
    
this .each( function (value, index) {
      
if  (iterator(value, index))
        results.push(value);
    });
    
return  results;
  },
// 找到素有匹配pattern的枚举元素,结果作为数组返回,iterator可选,如果不指定旧返回素有匹配pattern的枚举元素
  grep:  function (pattern, iterator) { // 正则模式 迭代器
     var  results  =  [];
    
this .each( function (value, index) {
      
var  stringValue  =  value.toString();
      
if  (stringValue.match(pattern))
        results.push((iterator 
||  Prototype.K)(value, index));
    })
    
return  results;
  },
// 判断枚举对象中是否含有参数Object指定的值
  include:  function (object) {
    
var  found  =   false ;
    
this .each( function (value) {
      
if  (value  ==  object) {
        found 
=   true ;
        
throw  $ break ;
      }
    });
    
return  found;
  },
// 将memo作为iterator的第一个参数,枚举元素作为iterator的第二个参数,枚举元素的次序作为第三个
//
参数每次迭代器的返回值将作为下一个iterator的memo参数,从而所有的迭代执行都通过memo联系起来了

  inject: 
function (memo, iterator) {
    
this .each( function (value, index) {
      memo 
=  iterator(memo, value, index);
    });
    
return  memo;
  },
// 对所有的枚举元素执行method方法 后面是要传递的参数
  invoke:  function (method) {
    
var  args  =  $A(arguments).slice( 1 );
    
return   this .collect( function (value) {
      
return  value[method].apply(value, args);
    });
  },
// 返回的最大的迭代器执行结果
  max:  function (iterator) {
    
var  result;
    
this .each( function (value, index) {
      value 
=  (iterator  ||  Prototype.K)(value, index);
      
if  (value  >=  (result  ||  value))
        result 
=  value;
    });
    
return  result;
  },
// 反之
  min:  function (iterator) {
    
var  result;
    
this .each( function (value, index) {
      value 
=  (iterator  ||  Prototype.K)(value, index);
      
if  (value  <=  (result  ||  value))
        result 
=  value;
    });
    
return  result;
  },
// 返回两个数组一组能使iterator返回true 另一组返回false
  partition:  function (iterator) {
    
var  trues  =  [], falses  =  [];
    
this .each( function (value, index) {
      ((iterator 
||  Prototype.K)(value, index)  ?
        trues : falses).push(value);
    });
    
return  [trues, falses];
  },
// 获取所有枚举元素的property属性值作为数组的返回
  pluck:  function (property) {
    
var  results  =  [];
    
this .each( function (value, index) {
      results.push(value[property]);
    });
    
return  results;
  },
// 与findall相反 
  reject:  function (iterator) {
    
var  results  =  [];
    
this .each( function (value, index) {
      
if  ( ! iterator(value, index))
        results.push(value);
    });
    
return  results;
  },
// 根据iterator的结果排序 最小的在前面 作为数组返回
  sortBy:  function (iterator) {
    
return   this .collect( function (value, index) {
      
return  {value: value, criteria: iterator(value, index)};
    }).sort(
function (left, right) {
      
var  a  =  left.criteria, b  =  right.criteria;
      
return  a  <  b  ?   - 1  : a  >  b  ?   1  :  0 ;
    }).pluck(
' value ' );
  },
// 枚举对象--》数组
  toArray:  function () {
    
return   this .collect(Prototype.K);
  },
// 接收多个枚举对象参数,最后一个可以是迭代器用于阵列转换
  zip:  function () {
    
var  iterator  =  Prototype.K, args  =  $A(arguments);
    
if  ( typeof  args.last()  ==   ' function ' )
      iterator 
=  args.pop();

    
var  collections  =  [ this ].concat(args).map($A);
    
return   this .map( function (value, index) {
      iterator(value 
=  collections.pluck(index));
      
return  value;
    });
  },
// 返回枚举对象的字符串描述
  inspect:  function () {
    
return   ' #<Enumerable: '   +   this .toArray().inspect()  +   ' > ' ;
  }
}
// 等价函数定义
Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});

/* ======================================================================================== */
// 【Array】数组对象

// 参数转换为数组,如果参数定义了toarray则直接调用,否则枚举获得数组
var  $A  =  Array.from  =   function (iterable) {
  
if  ( ! iterable)  return  [];
  
if  (iterable.toArray) {
    
return  iterable.toArray();
  } 
else  {
    
var  results  =  [];
    
for  ( var  i  =   0 ; i  <  iterable.length; i ++ )
      results.push(iterable[i]);
    
return  results;
  }
}
// 数组对象继承了Enumerable接口
Object.extend(Array.prototype, Enumerable);

Array.prototype._reverse 
=  Array.prototype.reverse;

Object.extend(Array.prototype, {
// 实现了枚举接口方法,用于对数组内的元素执行迭代器
  _each:  function (iterator) {
    
for  ( var  i  =   0 ; i  <   this .length; i ++ )
      iterator(
this [i]);
  },
// 清空数组
  clear:  function () {
    
this .length  =   0 ;
    
return   this ;
  },
// 取得第一个元素
  first:  function () {
    
return   this [ 0 ];
  },
// 取得最后一个元素
  last:  function () {
    
return   this [ this .length  -   1 ];
  },
// 删除数组元素中所有的null undefined值 作为新数组返回,原数组不受影响
  compact:  function () {
    
return   this .select( function (value) {
      
return  value  !=  undefined  ||  value  !=   null ;
    });
  },
// 展开所有数组元素不再嵌套
  flatten:  function () {
    
return   this .inject([],  function (array, value) {
      
return  array.concat(value.constructor  ==  Array  ?
        value.flatten() : [value]);
    });
  },
// 数组中删除指定的元素,返回删除后的结果,原数组不受影响
  without:  function () {
    
var  values  =  $A(arguments);
    
return   this .select( function (value) {
      
return   ! values.include(value);
    });
  },
// Value元素在数组中的索引值
  indexOf:  function (object) {
    
for  ( var  i  =   0 ; i  <   this .length; i ++ )
      
if  ( this [i]  ==  object)  return  i;
    
return   - 1 ;
  },
// 反转数组
  reverse:  function (inline) {
    
return  (inline  !==   false   ?   this  :  this .toArray())._reverse();
  },
// 删除数组中第一个元素并返回这个元素的值
  shift:  function () {
    
var  result  =   this [ 0 ];
    
for  ( var  i  =   0 ; i  <   this .length  -   1 ; i ++ )
      
this [i]  =   this [i  +   1 ];
    
this .length -- ;
    
return  result;
  },
// 得到数组的字符串描述例如arr=[1,2,3]-----> "[1,2,3]"
  inspect:  function () {
    
return   ' [ '   +   this .map(Object.inspect).join( ' ' +   ' ] ' ;
  }
});

/* ======================================================================================== */
// 【Hash】哈希对象


var  Hash  =  {
// 实现枚举接口方法从而使Hash对象也是枚举对象
  _each:  function (iterator) {
    
for  (key  in   this ) {
      
var  value  =   this [key];
      
if  ( typeof  value  ==   ' function ' continue ;

      
var  pair  =  [key, value];
      pair.key 
=  key;
      pair.value 
=  value;
      iterator(pair);
    }
  },
// 所有键数组
  keys:  function () {
    
return   this .pluck( ' key ' );
  },
// 所有值数组
  values:  function () {
    
return   this .pluck( ' value ' );
  },
// 合并哈希表,键相同就覆盖调用者
  merge:  function (hash) {
    
return  $H(hash).inject($H( this ),  function (mergedHash, pair) {
      mergedHash[pair.key] 
=  pair.value;
      
return  mergedHash;
    });
  },
// 键值对组成查询字符串的形式
  toQueryString:  function () {
    
return   this .map( function (pair) {
      
return  pair.map(encodeURIComponent).join( ' = ' );
    }).join(
' & ' );
  },
// 获取Hash对象的字符串描述
  inspect:  function () {
    
return   ' #<Hash:{ '   +   this .map( function (pair) {
      
return  pair.map(Object.inspect).join( ' ' );
    }).join(
' ' +   ' }> ' ;
  }
}
/* 哈希表不是类而是对象所以不能用new的方法创建,而是用$H()函数
  这个方法吧一个对象转换为哈希对象,对象的属性名为key 值为value
  可枚举!!
*/
function  $H(object) {
  
var  hash  =  Object.extend({}, object  ||  {});
  Object.extend(hash, Enumerable);
  Object.extend(hash, Hash);
  
return  hash;
}

/* ======================================================================================== */
// 【ObjectRange】用于进行指定次数的循环运算,同样继承于枚举接口并实现_each方法
//
有了这个方法基本上Javascript里面就不用for循环了


ObjectRange 
=  Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
// 构造函数同事指定了3个参数的值
  initialize:  function (start, end, exclusive) {
    
this .start  =  start;  // 其实索引
     this .end  =  end; // 结束索引
     this .exclusive  =  exclusive; // 表示是否包含结束索引
  },

  _each: 
function (iterator) {
    
var  value  =   this .start;
    
do  {
      iterator(value);
      value 
=  value.succ();
    } 
while  ( this .include(value));
  },
// 重写枚举接口的include方法,判断改循环的索引是否包含参数value的指定的值
  include:  function (value) {
    
if  (value  <   this .start)
      
return   false ;
    
if  ( this .exclusive)
      
return  value  <   this .end;
    
return  value  <=   this .end;
  }
});
// 使用$R()方法快速创建objectRange对象
var  $R  =   function (start, end, exclusive) {
  
return   new  ObjectRange(start, end, exclusive);
}

/* ======================================================================================== */
/*  【Ajax模块】
/*========================================================================================
*/
// 【ajax对象】

var  Ajax  =  { // 创建浏览器兼容的XMLHttpRequest对象
  getTransport:  function () {
    
return  Try.these(
      
function () { return   new  ActiveXObject( ' Msxml2.XMLHTTP ' )},
      
function () { return   new  ActiveXObject( ' Microsoft.XMLHTTP ' )},
      
function () { return   new  XMLHttpRequest()}
    ) 
||   false ;
  },
// 当前激活的请求数目
  activeRequestCount:  0
}
// 【Ajax.Responders对象】
Ajax.Responders  =  {
  responders: [],
// 表示所有响应处理代理

  _each: 
function (iterator) {
    
this .responders._each(iterator);
  },
// 注册一个响应代理
  register:  function (responderToAdd) {
    
if  ( ! this .include(responderToAdd))
      
this .responders.push(responderToAdd);
  },
// 删除一个响应代理
  unregister:  function (responderToRemove) {
    
this .responders  =   this .responders.without(responderToRemove);
  },
// 分发一个回调函数,让所有半酣callback回调函数事件标识符的响应代理都被调用
//
传递三个参数最后一个可选表示Json对象
  dispatch:  function (callback, request, transport, json) {
    
this .each( function (responder) {
      
if  (responder[callback]  &&   typeof  responder[callback]  ==   ' function ' ) {
        
try  {
          responder[callback].apply(responder, [request, transport, json]);
        } 
catch  (e) {}
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);
// 统计当前活动请求的数目
Ajax.Responders.register({
// 开始创建了一个请求
  onCreate:  function () {
    Ajax.activeRequestCount
++ ;
  },
// 请求结束
  onComplete:  function () {
    Ajax.activeRequestCount
-- ;
  }
});

// 【Ajax.Base类】进行服务器通信的基类
Ajax.Base  =   function () {};
Ajax.Base.prototype 
=  {
  setOptions: 
function (options) {
    
this .options  =  {
      method:       
' post ' ,
      asynchronous: 
true ,
      parameters:   
''
    }
    Object.extend(
this .options, options  ||  {});
  },

  responseIsSuccess: 
function () {
    
return   this .transport.status  ==  undefined
        
||   this .transport.status  ==   0
        
||  ( this .transport.status  >=   200   &&   this .transport.status  <   300 );
  },

  responseIsFailure: 
function () {
    
return   ! this .responseIsSuccess();
  }
}
// 【Ajax.Request类】用于向服务器端发送请求,封装了XmlHttpRequest
Ajax.Request  =  Class.create();
Ajax.Request.Events 
=
  [
' Uninitialized ' ' Loading ' ' Loaded ' ' Interactive ' ' Complete ' ];

Ajax.Request.prototype 
=  Object.extend( new  Ajax.Base(), {
  initialize: 
function (url, options) {
    
this .transport  =  Ajax.getTransport();
    
this .setOptions(options); // 使用基类方法设置
     this .request(url);
  },

// 向指定url发送请求 一般不会显示调用
  request:  function (url) {
    
var  parameters  =   this .options.parameters  ||   '' ;
    
if  (parameters.length  >   0 ) parameters  +=   ' &_= ' ;

    
try  {
      
this .url  =  url;
      
if  ( this .options.method  ==   ' get '   &&  parameters.length  >   0 )
        
this .url  +=  ( this .url.match( /?/ ?   ' & '  :  ' ? ' +  parameters;

      Ajax.Responders.dispatch(
' onCreate ' this this .transport);

      
this .transport.open( this .options.method,  this .url,
        
this .options.asynchronous);

      
if  ( this .options.asynchronous) {
        
this .transport.onreadystatechange  =   this .onStateChange.bind( this );
        setTimeout((
function () { this .respondToReadyState( 1 )}).bind( this ),  10 );
      }

      
this .setRequestHeaders();

      
var  body  =   this .options.postBody  ?   this .options.postBody : parameters;
      
this .transport.send( this .options.method  ==   ' post '   ?  body :  null );

    } 
catch  (e) {
      
this .dispatchException(e);
    }
  },
// 设置请求的Http头 类内部使用 一般无需显示调用
  setRequestHeaders:  function () {
    
var  requestHeaders  =
      [
' X-Requested-With ' ' XMLHttpRequest ' ,
       
' X-Prototype-Version ' , Prototype.Version];

    
if  ( this .options.method  ==   ' post ' ) {
      requestHeaders.push(
' Content-type ' ,
        
' application/x-www-form-urlencoded ' );

      
/*  Force "Connection: close" for Mozilla browsers to work around
       * a bug where XMLHttpReqeuest sends an incorrect Content-length
       * header. See Mozilla Bugzilla #246651.
       
*/
      
if  ( this .transport.overrideMimeType)
        requestHeaders.push(
' Connection ' ' close ' );
    }

    
if  ( this .options.requestHeaders)
      requestHeaders.push.apply(requestHeaders, 
this .options.requestHeaders);

    
for  ( var  i  =   0 ; i  <  requestHeaders.length; i  +=   2 )
      
this .transport.setRequestHeader(requestHeaders[i], requestHeaders[i + 1 ]);
  },
// 检测XMLHttpRquest对象的onstatechange事件类内部使用 一般无需显示调用
  onStateChange:  function () {
    
var  readyState  =   this .transport.readyState;
    
if  (readyState  !=   1 )
      
this .respondToReadyState( this .transport.readyState);
  },
// 获取指定的Http头的内容
  header:  function (name) {
    
try  {
      
return   this .transport.getResponseHeader(name);
    } 
catch  (e) {}
  },
// 如果服务器返回了Http头"X-JOSN"则将其作为js执行并返回执行结果
  evalJSON:  function () {
    
try  {
      
return  eval( this .header( ' X-JSON ' ));
    } 
catch  (e) {}
  },
// 将XMLHttpRequest返回的responseText作为JS语句执行并返回执行结果
  evalResponse:  function () {
    
try  {
      
return  eval( this .transport.responseText);
    } 
catch  (e) {
      
this .dispatchException(e);
    }
  },
// 处理XMLHttpRequest的readystate属性根据成功或失败调用Options对象中相应的回调函数
//
同时通知Ajax.Responders中注册的响应处理句柄
  respondToReadyState:  function (readyState) {
    
var  event  =  Ajax.Request.Events[readyState];
    
var  transport  =   this .transport, json  =   this .evalJSON();

    
if  (event  ==   ' Complete ' ) {
      
try  {
        (
this .options[ ' on '   +   this .transport.status]
         
||   this .options[ ' on '   +  ( this .responseIsSuccess()  ?   ' Success '  :  ' Failure ' )]
         
||  Prototype.emptyFunction)(transport, json);
      } 
catch  (e) {
        
this .dispatchException(e);
      }

      
if  (( this .header( ' Content-type ' ||   '' ).match( /^ text / javascript / i))
        
this .evalResponse();
    }

    
try  {
      (
this .options[ ' on '   +  event]  ||  Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch(
' on '   +  event,  this , transport, json);
    } 
catch  (e) {
      
this .dispatchException(e);
    }

    
/*  Avoid memory leak in MSIE: clean up the oncomplete event handler  */
    
if  (event  ==   ' Complete ' )
      
this .transport.onreadystatechange  =  Prototype.emptyFunction;
  },

  dispatchException: 
function (exception) {
    (
this .options.onException  ||  Prototype.emptyFunction)( this , exception);
    Ajax.Responders.dispatch(
' onException ' this , exception);
  }
});


/* ======================================================================================== */
// 【Ajax.Updater类】用于将获取的内容填充到指定的容器中去
Ajax.Updater  =  Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
// 重写了构造函数
  initialize:  function (container, url, options) {
    
this .containers  =  {
      success: container.success 
?  $(container.success) : $(container),
      failure: container.failure 
?  $(container.failure) :
        (container.success 
?   null  : $(container))
    }

    
this .transport  =  Ajax.getTransport();
    
this .setOptions(options); // 可以指定evalscripts属性和insertion属性

    
var  onComplete  =   this .options.onComplete  ||  Prototype.emptyFunction;
    
this .options.onComplete  =  ( function (transport, object) {
      
this .updateContent();
      onComplete(transport, object);
    }).bind(
this );

    
this .request(url);
  },

  updateContent: 
function () {
    
var  receiver  =   this .responseIsSuccess()  ?
      
this .containers.success :  this .containers.failure;
    
var  response  =   this .transport.responseText;

    
if  ( ! this .options.evalScripts)
      response 
=  response.stripScripts();

    
if  (receiver) {
      
if  ( this .options.insertion) {
        
new   this .options.insertion(receiver, response);
      } 
else  {
        Element.update(receiver, response);
      }
    }

    
if  ( this .responseIsSuccess()) {
      
if  ( this .onComplete)
        setTimeout(
this .onComplete.bind( this ),  10 );
    }
  }
});

/* ======================================================================================== */
// 【Ajax.PeriodicalUpdater类】用于定时执行服务器异步调用,功能与Updater相同只是有了定时功能

Ajax.PeriodicalUpdater 
=  Class.create();
Ajax.PeriodicalUpdater.prototype 
=  Object.extend( new  Ajax.Base(), {
  initialize: 
function (container, url, options) {
    
this .setOptions(options);
    
this .onComplete  =   this .options.onComplete;
// 区别就在下面两个属性 频率 衰减参数 指数增长!只要有一次不同旧回复原有频率
     this .frequency  =  ( this .options.frequency  ||   2 );
    
this .decay  =  ( this .options.decay  ||   1 );

    
this .updater  =  {};
    
this .container  =  container;
    
this .url  =  url;

    
this .start();
  },

  start: 
function () {
    
this .options.onComplete  =   this .updateComplete.bind( this );
    
this .onTimerEvent();
  },

  stop: 
function () {
    
this .updater.onComplete  =  undefined;
    clearTimeout(
this .timer);
    (
this .onComplete  ||  Prototype.emptyFunction).apply( this , arguments);
  },

  updateComplete: 
function (request) {
    
if  ( this .options.decay) {
      
this .decay  =  (request.responseText  ==   this .lastText  ?
        
this .decay  *   this .options.decay :  1 );

      
this .lastText  =  request.responseText;
    }
    
this .timer  =  setTimeout( this .onTimerEvent.bind( this ),
      
this .decay  *   this .frequency  *   1000 );
  },

  onTimerEvent: 
function () {
    
this .updater  =   new  Ajax.Updater( this .container,  this .url,  this .options);
  }
});


/* ======================================================================================== */
/* 【文档操作的封装】
/*========================================================================================
*/

// 返回了一个数组包含了所有符合条件的结点的引用
document.getElementsByClassName  =   function (className, parentElement) {  // parentElement不指定就在全局查找
   var  children  =  ($(parentElement)  ||  document.body).getElementsByTagName( ' * ' );
  
return  $A(children).inject([],  function (elements, child) {
    
if  (child.className.match( new  RegExp( " (^|s) "   +  className  +   " (s|$) " )))
      elements.push(child);
    
return  elements;
  });
}

/* 【Element对象】用于对文档结点做一些统一的操作

*/

if  ( ! window.Element) {
  
var  Element  =   new  Object();
}

Object.extend(Element, {
  visible: 
function (element) {
    
return  $(element).style.display  !=   ' none ' ;
  },
// 指定结点可见性的切换
  toggle:  function () {
    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ ) {
      
var  element  =  $(arguments[i]);
      Element[Element.visible(element) 
?   ' hide '  :  ' show ' ](element);
    }
  },

  hide: 
function () {
    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ ) {
      
var  element  =  $(arguments[i]);
      element.style.display 
=   ' none ' ;
    }
  },

  show: 
function () {
    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ ) {
      
var  element  =  $(arguments[i]);
      element.style.display 
=   '' ;
    }
  },

  remove: 
function (element) {
    element 
=  $(element);
    element.parentNode.removeChild(element);
  },
// 将html片段填充到element指定的结点中
  update:  function (element, html) {
    $(element).innerHTML 
=  html.stripScripts();
    setTimeout(
function () {html.evalScripts()},  10 );
  },
// 获得element的绝对高度
  getHeight:  function (element) {
    element 
=  $(element);
    
return  element.offsetHeight;
  },

  classNames: 
function (element) {
    
return   new  Element.ClassNames(element);
  },

  hasClassName: 
function (element, className) {
    
if  ( ! (element  =  $(element)))  return ;
    
return  Element.classNames(element).include(className);
  },

  addClassName: 
function (element, className) {
    
if  ( ! (element  =  $(element)))  return ;
    
return  Element.classNames(element).add(className);
  },

  removeClassName: 
function (element, className) {
    
if  ( ! (element  =  $(element)))  return ;
    
return  Element.classNames(element).remove(className);
  },

  
//  removes whitespace-only text node children
  cleanWhitespace:  function (element) {
    element 
=  $(element);
    
for  ( var  i  =   0 ; i  <  element.childNodes.length; i ++ ) {
      
var  node  =  element.childNodes[i];
      
if  (node.nodeType  ==   3   &&   !/ S / .test(node.nodeValue))
        Element.remove(node);
    }
  },

  empty: 
function (element) {
    
return  $(element).innerHTML.match( /^ s * $ / );
  },
// 将浏览器的滚动条滚动到指定的结点的位置
  scrollTo:  function (element) {
    element 
=  $(element);
    
var  x  =  element.x  ?  element.x : element.offsetLeft,
        y 
=  element.y  ?  element.y : element.offsetTop;
    window.scrollTo(x, y);
  },
// 得到element的结点的绝对样式
  getStyle:  function (element, style) {
    element 
=  $(element);
    
var  value  =  element.style[style.camelize()];
    
if  ( ! value) {
      
if  (document.defaultView  &&  document.defaultView.getComputedStyle) {
        
var  css  =  document.defaultView.getComputedStyle(element,  null );
        value 
=  css  ?  css.getPropertyValue(style) :  null ;
      } 
else   if  (element.currentStyle) {
        value 
=  element.currentStyle[style.camelize()];
      }
    }

    
if  (window.opera  &&  [ ' left ' ' top ' ' right ' ' bottom ' ].include(style))
      
if  (Element.getStyle(element,  ' position ' ==   ' static ' ) value  =   ' auto ' ;

    
return  value  ==   ' auto '   ?   null  : value;
  },
// 设置结点样式 var style={background-color:'black',color:'red'} Element.setStyle($('div1'),style)
  setStyle:  function (element, style) {
    element 
=  $(element);
    
for  (name  in  style)
      element.style[name.camelize()] 
=  style[name];
  },
  
  
// 获得结点的宽度高度 该方法无论结点是否可见 都能获得其大小
  getDimensions:  function (element) {
    element 
=  $(element);
    
if  (Element.getStyle(element,  ' display ' !=   ' none ' )
      
return  {width: element.offsetWidth, height: element.offsetHeight};

    
//  All *Width and *Height properties give 0 on elements with display none,
     //  so enable the element temporarily
     var  els  =  element.style;
    
var  originalVisibility  =  els.visibility;
    
var  originalPosition  =  els.position;
    els.visibility 
=   ' hidden ' ;
    els.position 
=   ' absolute ' ;
    els.display 
=   '' ;
    
var  originalWidth  =  element.clientWidth;
    
var  originalHeight  =  element.clientHeight;
    els.display 
=   ' none ' ;
    els.position 
=  originalPosition;
    els.visibility 
=  originalVisibility;
    
return  {width: originalWidth, height: originalHeight};
  },
// 相对定位
  makePositioned:  function (element) {
    element 
=  $(element);
    
var  pos  =  Element.getStyle(element,  ' position ' );
    
if  (pos  ==   ' static '   ||   ! pos) {
      element._madePositioned 
=   true ;
      element.style.position 
=   ' relative ' ;
      
//  Opera returns the offset relative to the positioning context, when an
       //  element is position relative but top and left have not been defined
       if  (window.opera) {
        element.style.top 
=   0 ;
        element.style.left 
=   0 ;
      }
    }
  },
// 取消相对定位
  undoPositioned:  function (element) {
    element 
=  $(element);
    
if  (element._madePositioned) {
      element._madePositioned 
=  undefined;
      element.style.position 
=
        element.style.top 
=
        element.style.left 
=
        element.style.bottom 
=
        element.style.right 
=   '' ;
    }
  },
// 使结点隐藏超出的部分 overflow=relative
  makeClipping:  function (element) {
    element 
=  $(element);
    
if  (element._overflow)  return ;
    element._overflow 
=  element.style.overflow;
    
if  ((Element.getStyle(element,  ' overflow ' ||   ' visible ' !=   ' hidden ' )
      element.style.overflow 
=   ' hidden ' ;
  },

  undoClipping: 
function (element) {
    element 
=  $(element);
    
if  (element._overflow)  return ;
    element.style.overflow 
=  element._overflow;
    element._overflow 
=  undefined;
  }
});


/* ======================================================================================== */
// Toggle对象是Element.toggle方法的一个快捷用法
var  Toggle  =   new  Object();
Toggle.display 
=  Element.toggle;

/* ======================================================================================== */
// 【Insertion命名空间】包含了4个类把指定的html片段插入到指定的结点的相应位置上
Abstract.Insertion  =   function (adjacency) {
  
this .adjacency  =  adjacency;
}

Abstract.Insertion.prototype 
=  {
  initialize: 
function (element, content) {
    
this .element  =  $(element);
    
this .content  =  content.stripScripts();

    
if  ( this .adjacency  &&   this .element.insertAdjacentHTML) {
      
try  {
        
this .element.insertAdjacentHTML( this .adjacency,  this .content);
      } 
catch  (e) {
        
if  ( this .element.tagName.toLowerCase()  ==   ' tbody ' ) {
          
this .insertContent( this .contentFromAnonymousTable());
        } 
else  {
          
throw  e;
        }
      }
    } 
else  {
      
this .range  =   this .element.ownerDocument.createRange();
      
if  ( this .initializeRange)  this .initializeRange();
      
this .insertContent([ this .range.createContextualFragment( this .content)]);
    }

    setTimeout(
function () {content.evalScripts()},  10 );
  },

  contentFromAnonymousTable: 
function () {
    
var  div  =  document.createElement( ' div ' );
    div.innerHTML 
=   ' <table><tbody> '   +   this .content  +   ' </tbody></table> ' ;
    
return  $A(div.childNodes[ 0 ].childNodes[ 0 ].childNodes);
  }
}

var  Insertion  =   new  Object();

Insertion.Before 
=  Class.create();
Insertion.Before.prototype 
=  Object.extend( new  Abstract.Insertion( ' beforeBegin ' ), {
  initializeRange: 
function () {
    
this .range.setStartBefore( this .element);
  },

  insertContent: 
function (fragments) {
    fragments.each((
function (fragment) {
      
this .element.parentNode.insertBefore(fragment,  this .element);
    }).bind(
this ));
  }
});

Insertion.Top 
=  Class.create();
Insertion.Top.prototype 
=  Object.extend( new  Abstract.Insertion( ' afterBegin ' ), {
  initializeRange: 
function () {
    
this .range.selectNodeContents( this .element);
    
this .range.collapse( true );
  },

  insertContent: 
function (fragments) {
    fragments.reverse(
false ).each(( function (fragment) {
      
this .element.insertBefore(fragment,  this .element.firstChild);
    }).bind(
this ));
  }
});

Insertion.Bottom 
=  Class.create();
Insertion.Bottom.prototype 
=  Object.extend( new  Abstract.Insertion( ' beforeEnd ' ), {
  initializeRange: 
function () {
    
this .range.selectNodeContents( this .element);
    
this .range.collapse( this .element);
  },

  insertContent: 
function (fragments) {
    fragments.each((
function (fragment) {
      
this .element.appendChild(fragment);
    }).bind(
this ));
  }
});

Insertion.After 
=  Class.create();
Insertion.After.prototype 
=  Object.extend( new  Abstract.Insertion( ' afterEnd ' ), {
  initializeRange: 
function () {
    
this .range.setStartAfter( this .element);
  },

  insertContent: 
function (fragments) {
    fragments.each((
function (fragment) {
      
this .element.parentNode.insertBefore(fragment,
        
this .element.nextSibling);
    }).bind(
this ));
  }
});

/* ======================================================================================== */
// 【Element.ClassNames】获得一个结点的所有的class名称组成的对象,实现了枚举接口

Element.ClassNames 
=  Class.create();
Element.ClassNames.prototype 
=  {
  initialize: 
function (element) {
    
this .element  =  $(element);
  },

  _each: 
function (iterator) {
    
this .element.className.split( / s +/ ).select( function (name) {
      
return  name.length  >   0 ;
    })._each(iterator);
  },

  set: 
function (className) {
    
this .element.className  =  className;
  },

  add: 
function (classNameToAdd) {
    
if  ( this .include(classNameToAdd))  return ;
    
this .set( this .toArray().concat(classNameToAdd).join( '   ' ));
  },

  remove: 
function (classNameToRemove) {
    
if  ( ! this .include(classNameToRemove))  return ;
    
this .set( this .select( function (className) {
      
return  className  !=  classNameToRemove;
    }).join(
'   ' ));
  },

  toString: 
function () { // 获得所有class的字符串表示空格隔开
     return   this .toArray().join( '   ' );
  }
}
// 表单域对象工具类
Object.extend(Element.ClassNames.prototype, Enumerable);
var  Field  =  {
  clear: 
function () {
    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ )
      $(arguments[i]).value 
=   '' ;
  },

  focus: 
function (element) {
    $(element).focus();
  },

  present: 
function () {
    
for  ( var  i  =   0 ; i  <  arguments.length; i ++ )
      
if  ($(arguments[i]).value  ==   '' return   false ;
    
return   true ;
  },
// 选中表单域
  select:  function (element) {
    $(element).select();
  },
// focus + select
  activate:  function (element) {
    element 
=  $(element);
    element.focus();
    
if  (element.select)
      element.select();
  }
}

/* 【Form】表单工具对象 */

var  Form  =  {
// 表单数据序列化
  serialize:  function (form) {
    
var  elements  =  Form.getElements($(form));
    
var  queryComponents  =   new  Array();

    
for  ( var  i  =   0 ; i  <  elements.length; i ++ ) {
      
var  queryComponent  =  Form.Element.serialize(elements[i]);
      
if  (queryComponent)
        queryComponents.push(queryComponent);
    }

    
return  queryComponents.join( ' & ' );
  },
// 获取所有表单域按照标记名排列
  getElements:  function (form) {
    form 
=  $(form);
    
var  elements  =   new  Array();

    
for  (tagName  in  Form.Element.Serializers) {
      
var  tagElements  =  form.getElementsByTagName(tagName);
      
for  ( var  j  =   0 ; j  <  tagElements.length; j ++ )
        elements.push(tagElements[j]);
    }
    
return  elements;
  },
// 有一个满足就返回
  getInputs:  function (form, typeName, name) {
    form 
=  $(form);
    
var  inputs  =  form.getElementsByTagName( ' input ' );

    
if  ( ! typeName  &&   ! name)
      
return  inputs;

    
var  matchingInputs  =   new  Array();
    
for  ( var  i  =   0 ; i  <  inputs.length; i ++ ) {
      
var  input  =  inputs[i];
      
if  ((typeName  &&  input.type  !=  typeName)  ||
          (name 
&&  input.name  !=  name))
        
continue ;
      matchingInputs.push(input);
    }

    
return  matchingInputs;
  },
// 整个表单域禁用
  disable:  function (form) {
    
var  elements  =  Form.getElements(form);
    
for  ( var  i  =   0 ; i  <  elements.length; i ++ ) {
      
var  element  =  elements[i];
      element.blur();
      element.disabled 
=   ' true ' ;
    }
  },
// 启用
  enable:  function (form) {
    
var  elements  =  Form.getElements(form);
    
for  ( var  i  =   0 ; i  <  elements.length; i ++ ) {
      
var  element  =  elements[i];
      element.disabled 
=   '' ;
    }
  },
// 获取第一个可用的表单域
  findFirstElement:  function (form) {
    
return  Form.getElements(form).find( function (element) {
      
return  element.type  !=   ' hidden '   &&   ! element.disabled  &&
        [
' input ' ' select ' ' textarea ' ].include(element.tagName.toLowerCase());
    });
  },
// 激活第一个表单域
  focusFirstElement:  function (form) {
    Field.activate(Form.findFirstElement(form));
  },

  reset: 
function (form) {
    $(form).reset();
  }
}

// 【Form.Element】 对特定的表单域进行操作
Form.Element  =  {
// 序列化形成"&name=value"
  serialize:  function (element) {
    element 
=  $(element);
    
var  method  =  element.tagName.toLowerCase();
    
var  parameter  =  Form.Element.Serializers[method](element);

    
if  (parameter) {
      
var  key  =  encodeURIComponent(parameter[ 0 ]);
      
if  (key.length  ==   0 return ;

      
if  (parameter[ 1 ].constructor  !=  Array)
        parameter[
1 =  [parameter[ 1 ]];

      
return  parameter[ 1 ].map( function (value) {
        
return  key  +   ' = '   +  encodeURIComponent(value);
      }).join(
' & ' );
    }
  },
// 获得指定表单域的值
  getValue:  function (element) {
    element 
=  $(element);
    
var  method  =  element.tagName.toLowerCase();
    
var  parameter  =  Form.Element.Serializers[method](element);

    
if  (parameter)
      
return  parameter[ 1 ];
  }
}
// 【Form.Element.Serializers】针对不同的表单域进行序列化
Form.Element.Serializers  =  {
// 一般用所有的<input>标记的序列化都可以
  input:  function (element) {
    
switch  (element.type.toLowerCase()) {
      
case   ' submit ' :
      
case   ' hidden ' :
      
case   ' password ' :
      
case   ' text ' :
        
return  Form.Element.Serializers.textarea(element);
      
case   ' checkbox ' :
      
case   ' radio ' :
        
return  Form.Element.Serializers.inputSelector(element);
    }
    
return   false ;
  },

  inputSelector: 
function (element) {
    
if  (element.checked)
      
return  [element.name, element.value];
  },

  textarea: 
function (element) {
    
return  [element.name, element.value];
  },

  select: 
function (element) {
    
return  Form.Element.Serializers[element.type  ==   ' select-one '   ?
      
' selectOne '  :  ' selectMany ' ](element);
  },

  selectOne: 
function (element) {
    
var  value  =   '' , opt, index  =  element.selectedIndex;
    
if  (index  >=   0 ) {
      opt 
=  element.options[index];
      value 
=  opt.value;
      
if  ( ! value  &&   ! ( ' value '   in  opt))
        value 
=  opt.text;
    }
    
return  [element.name, value];
  },

  selectMany: 
function (element) {
    
var  value  =   new  Array();
    
for  ( var  i  =   0 ; i  <  element.length; i ++ ) {
      
var  opt  =  element.options[i];
      
if  (opt.selected) {
        
var  optValue  =  opt.value;
        
if  ( ! optValue  &&   ! ( ' value '   in  opt))
          optValue 
=  opt.text;
        value.push(optValue);
      }
    }
    
return  [element.name, value];
  }
}

// 【获得表单域的值】
var  $F  =  Form.Element.getValue;

/* ======================================================================================== */
/* 【Observer模式框架】 观察对象仅仅针对表单和表单域 */
/* ======================================================================================== */

Abstract.TimedObserver 
=   function () {} // 定义了基于定时器的观察模式基类后面的类都继承了这个类
Abstract.TimedObserver.prototype  =  {
  initialize: 
function (element, frequency, callback) {
    
this .frequency  =  frequency; // 观察频率
     this .element    =  $(element); // 被观察的对象
     this .callback   =  callback; // 回调函数接受参数 被观察对象自身和触发事件时的值

    
this .lastValue  =   this .getValue(); // 这是观察的值!!
     this .registerCallback();
  },
// 注册回调函数
  registerCallback:  function () {
    setInterval(
this .onTimerEvent.bind( this ),  this .frequency  *   1000 );
  },
// 每一个时间片触发一次该事件
  onTimerEvent:  function () {
    
var  value  =   this .getValue();
    
if  ( this .lastValue  !=  value) {
      
this .callback( this .element, value);
      
this .lastValue  =  value;
    }
  }
}

// 【Form.Element.Observer类】实现了getvalue方法
Form.Element.Observer  =  Class.create();
Form.Element.Observer.prototype 
=  Object.extend( new  Abstract.TimedObserver(), {
  getValue: 
function () {
    
return  Form.Element.getValue( this .element);
  }
});

// 【Form.Observer类】实现了getvalue方法,只要有一个表单域发生变化就会触发回调
Form.Observer  =  Class.create();
Form.Observer.prototype 
=  Object.extend( new  Abstract.TimedObserver(), {
  getValue: 
function () {
    
return  Form.serialize( this .element);
  }
});

// 【Abstract.EventObserver 类】基于事件的Oberver模式的基类 后面的具体类都继承这个类

Abstract.EventObserver 
=   function () {}
Abstract.EventObserver.prototype 
=  {
  initialize: 
function (element, callback) {
    
this .element   =  $(element);
    
this .callback  =  callback;

    
this .lastValue  =   this .getValue();
    
if  ( this .element.tagName.toLowerCase()  ==   ' form ' )
      
this .registerFormCallbacks();
    
else
      
this .registerCallback( this .element);
  },
// 每次发生事件时被调用 内部调用 无需显示调用
  onElementEvent:  function () {
    
var  value  =   this .getValue();
    
if  ( this .lastValue  !=  value) {
      
this .callback( this .element, value);
      
this .lastValue  =  value;
    }
  },
// 注册表表单域事件
  registerFormCallbacks:  function () {
    
var  elements  =  Form.getElements( this .element);
    
for  ( var  i  =   0 ; i  <  elements.length; i ++ )
      
this .registerCallback(elements[i]);
  },

  registerCallback: 
function (element) {
    
if  (element.type) {
      
switch  (element.type.toLowerCase()) {
        
case   ' checkbox ' :
        
case   ' radio ' :
          Event.observe(element, 
' click ' this .onElementEvent.bind( this ));
          
break ;
        
case   ' password ' :
        
case   ' text ' :
        
case   ' textarea ' :
        
case   ' select-one ' :
        
case   ' select-multiple ' :
          Event.observe(element, 
' change ' this .onElementEvent.bind( this ));
          
break ;
      }
    }
  }
}
// 【Form.Element.EventObserver】
Form.Element.EventObserver  =  Class.create();
Form.Element.EventObserver.prototype 
=  Object.extend( new  Abstract.EventObserver(), {
  getValue: 
function () {
    
return  Form.Element.getValue( this .element);
  }
});
// 【Form.EventObserver】
Form.EventObserver  =  Class.create();
Form.EventObserver.prototype 
=  Object.extend( new  Abstract.EventObserver(), {
  getValue: 
function () {
    
return  Form.serialize( this .element);
  }
});



/* ======================================================================================== */
/* 【事件处理:对Event对象的扩展】对javascript事件处理机制进行封装 */
/* ======================================================================================== */
if  ( ! window.Event) {
  
var  Event  =   new  Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 
8 ,
  KEY_TAB:       
9 ,
  KEY_RETURN:   
13 ,
  KEY_ESC:      
27 ,
  KEY_LEFT:     
37 ,
  KEY_UP:       
38 ,
  KEY_RIGHT:    
39 ,
  KEY_DOWN:     
40 ,
  KEY_DELETE:   
46 ,
// 获取触发事件的对象
  element:  function (event) {
    
return  event.target  ||  event.srcElement;
  },

  isLeftClick: 
function (event) {
    
return  (((event.which)  &&  (event.which  ==   1 ))  ||
            ((event.button) 
&&  (event.button  ==   1 )));
  },

  pointerX: 
function (event) {
    
return  event.pageX  ||  (event.clientX  +
      (document.documentElement.scrollLeft 
||  document.body.scrollLeft));
  },

  pointerY: 
function (event) {
    
return  event.pageY  ||  (event.clientY  +
      (document.documentElement.scrollTop 
||  document.body.scrollTop));
  },
// 停止事件的传递
  stop:  function (event) {
    
if  (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } 
else  {
      event.returnValue 
=   false ;
      event.cancelBubble 
=   true ;
    }
  },

  
//  find the first node with the given tagName, starting from the
   //  node the event was triggered on; traverses the DOM upwards
  findElement:  function (event, tagName) {
    
var  element  =  Event.element(event);
    
while  (element.parentNode  &&  ( ! element.tagName  ||
        (element.tagName.toUpperCase() 
!=  tagName.toUpperCase())))
      element 
=  element.parentNode;
    
return  element;
  },

  observers: 
false ,

  _observeAndCache: 
function (element, name, observer, useCapture) {
    
if  ( ! this .observers)  this .observers  =  [];
    
if  (element.addEventListener) {
      
this .observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } 
else   if  (element.attachEvent) {
      
this .observers.push([element, name, observer, useCapture]);
      element.attachEvent(
' on '   +  name, observer);
    }
  },
// 删除所有通过Event.observers绑定的事件
  unloadCache:  function () {
    
if  ( ! Event.observers)  return ;
    
for  ( var  i  =   0 ; i  <  Event.observers.length; i ++ ) {
      Event.stopObserving.apply(
this , Event.observers[i]);
      Event.observers[i][
0 =   null ;
    }
    Event.observers 
=   false ;
  },
// 绑定一个事件
  observe:  function (element, name, observer, useCapture) {
    
var  element  =  $(element);
    useCapture 
=  useCapture  ||   false ;

    
if  (name  ==   ' keypress '   &&
        (navigator.appVersion.match(
/ Konqueror | Safari | KHTML / )
        
||  element.attachEvent))
      name 
=   ' keydown ' ;

    
this ._observeAndCache(element, name, observer, useCapture);
  },
// 取消事件绑定
  stopObserving:  function (element, name, observer, useCapture) {
    
var  element  =  $(element);
    useCapture 
=  useCapture  ||   false ;

    
if  (name  ==   ' keypress '   &&
        (navigator.appVersion.match(
/ Konqueror | Safari | KHTML / )
        
||  element.detachEvent))
      name 
=   ' keydown ' ;

    
if  (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } 
else   if  (element.detachEvent) {
      element.detachEvent(
' on '   +  name, observer);
    }
  }
});

// 【结点的位置处理:Position对象】
/*
 prevent memory leaks in IE  */
Event.observe(window, 
' unload ' , Event.unloadCache,  false );
var  Position  =  {
  
//  set to true if needed, warning: firefox performance problems
   //  NOT neeeded for page scrolling, only if draggable contained in
   //  scrollable elements
  includeScrollOffsets:  false ,

  
//  must be called before calling withinIncludingScrolloffset, every time the
   //  page is scrolled用于计算滚动条的位置信息
  prepare:  function () {
    
this .deltaX  =   window.pageXOffset
                
||  document.documentElement.scrollLeft
                
||  document.body.scrollLeft
                
||   0 ;
    
this .deltaY  =   window.pageYOffset
                
||  document.documentElement.scrollTop
                
||  document.body.scrollTop
                
||   0 ;
  },
// 计算节点的绝对滚动位置返回包含两个元素的数组到文档左侧 顶部的距离
  realOffset:  function (element) {
    
var  valueT  =   0 , valueL  =   0 ;
    
do  {
      valueT 
+=  element.scrollTop   ||   0 ;
      valueL 
+=  element.scrollLeft  ||   0 ;
      element 
=  element.parentNode;
    } 
while  (element);
    
return  [valueL, valueT];
  },
// 计算结点相对于文档的绝对滚动位置,返回包含两个元素的数组到文档左侧 顶部的距离
  cumulativeOffset:  function (element) {
    
var  valueT  =   0 , valueL  =   0 ;
    
do  {
      valueT 
+=  element.offsetTop   ||   0 ;
      valueL 
+=  element.offsetLeft  ||   0 ;
      element 
=  element.offsetParent;
    } 
while  (element);
    
return  [valueL, valueT];
  },
// 相对位置
  positionedOffset:  function (element) {
    
var  valueT  =   0 , valueL  =   0 ;
    
do  {
      valueT 
+=  element.offsetTop   ||   0 ;
      valueL 
+=  element.offsetLeft  ||   0 ;
      element 
=  element.offsetParent;
      
if  (element) {
        p 
=  Element.getStyle(element,  ' position ' );
        
if  (p  ==   ' relative '   ||  p  ==   ' absolute ' break ;
      }
    } 
while  (element);
    
return  [valueL, valueT];
  },

  offsetParent: 
function (element) {
    
if  (element.offsetParent)  return  element.offsetParent;
    
if  (element  ==  document.body)  return  element;

    
while  ((element  =  element.parentNode)  &&  element  !=  document.body)
      
if  (Element.getStyle(element,  ' position ' !=   ' static ' )
        
return  element;

    
return  document.body;
  },

  
//  caches x/y coordinate pair to use with overlap是否在element内
  within:  function (element, x, y) {
    
if  ( this .includeScrollOffsets)
      
return   this .withinIncludingScrolloffsets(element, x, y);
    
this .xcomp  =  x;
    
this .ycomp  =  y;
    
this .offset  =   this .cumulativeOffset(element);

    
return  (y  >=   this .offset[ 1 &&
            y 
<    this .offset[ 1 +  element.offsetHeight  &&
            x 
>=   this .offset[ 0 &&
            x 
<    this .offset[ 0 +  element.offsetWidth);
  },

  withinIncludingScrolloffsets: 
function (element, x, y) {
    
var  offsetcache  =   this .realOffset(element);

    
this .xcomp  =  x  +  offsetcache[ 0 -   this .deltaX;
    
this .ycomp  =  y  +  offsetcache[ 1 -   this .deltaY;
    
this .offset  =   this .cumulativeOffset(element);

    
return  ( this .ycomp  >=   this .offset[ 1 &&
            
this .ycomp  <    this .offset[ 1 +  element.offsetHeight  &&
            
this .xcomp  >=   this .offset[ 0 &&
            
this .xcomp  <    this .offset[ 0 +  element.offsetWidth);
  },

  
//  within must be called directly before
  overlap:  function (mode, element) {
    
if  ( ! mode)  return   0 ;
    
if  (mode  ==   ' vertical ' )
      
return  (( this .offset[ 1 +  element.offsetHeight)  -   this .ycomp)  /
        element.offsetHeight;
    
if  (mode  ==   ' horizontal ' )
      
return  (( this .offset[ 0 +  element.offsetWidth)  -   this .xcomp)  /
        element.offsetWidth;
  },
// 克隆结点 常用于拖放
  clone:  function (source, target) {
    source 
=  $(source);
    target 
=  $(target);
    target.style.position 
=   ' absolute ' ;
    
var  offsets  =   this .cumulativeOffset(source);
    target.style.top    
=  offsets[ 1 +   ' px ' ;
    target.style.left   
=  offsets[ 0 +   ' px ' ;
    target.style.width  
=  source.offsetWidth  +   ' px ' ;
    target.style.height 
=  source.offsetHeight  +   ' px ' ;
  },

  page: 
function (forElement) {
    
var  valueT  =   0 , valueL  =   0 ;

    
var  element  =  forElement;
    
do  {
      valueT 
+=  element.offsetTop   ||   0 ;
      valueL 
+=  element.offsetLeft  ||   0 ;

      
//  Safari fix
       if  (element.offsetParent == document.body)
        
if  (Element.getStyle(element, ' position ' ) == ' absolute ' break ;

    } 
while  (element  =  element.offsetParent);

    element 
=  forElement;
    
do  {
      valueT 
-=  element.scrollTop   ||   0 ;
      valueL 
-=  element.scrollLeft  ||   0 ;
    } 
while  (element  =  element.parentNode);

    
return  [valueL, valueT];
  },

  clone: 
function (source, target) {
    
var  options  =  Object.extend({
      setLeft:    
true ,
      setTop:     
true ,
      setWidth:   
true ,
      setHeight:  
true ,
      offsetTop:  
0 ,
      offsetLeft: 
0
    }, arguments[
2 ||  {})

    
//  find page position of source
    source  =  $(source);
    
var  p  =  Position.page(source);

    
//  find coordinate system to use
    target  =  $(target);
    
var  delta  =  [ 0 0 ];
    
var  parent  =   null ;
    
//  delta [0,0] will do fine with position: fixed elements,
     //  position:absolute needs offsetParent deltas
     if  (Element.getStyle(target, ' position ' ==   ' absolute ' ) {
      parent 
=  Position.offsetParent(target);
      delta 
=  Position.page(parent);
    }

    
//  correct by body offsets (fixes Safari)
     if  (parent  ==  document.body) {
      delta[
0 -=  document.body.offsetLeft;
      delta[
1 -=  document.body.offsetTop;
    }

    
//  set position
     if (options.setLeft)   target.style.left   =  (p[ 0 -  delta[ 0 +  options.offsetLeft)  +   ' px ' ;
    
if (options.setTop)    target.style.top    =  (p[ 1 -  delta[ 1 +  options.offsetTop)  +   ' px ' ;
    
if (options.setWidth)  target.style.width  =  source.offsetWidth  +   ' px ' ;
    
if (options.setHeight) target.style.height  =  source.offsetHeight  +   ' px ' ;
  },
// 绝对定位
  absolutize:  function (element) {
    element 
=  $(element);
    
if  (element.style.position  ==   ' absolute ' return ;
    Position.prepare();

    
var  offsets  =  Position.positionedOffset(element);
    
var  top      =  offsets[ 1 ];
    
var  left     =  offsets[ 0 ];
    
var  width    =  element.clientWidth;
    
var  height   =  element.clientHeight;

    element._originalLeft   
=  left  -  parseFloat(element.style.left   ||   0 );
    element._originalTop    
=  top   -  parseFloat(element.style.top  ||   0 );
    element._originalWidth  
=  element.style.width;
    element._originalHeight 
=  element.style.height;

    element.style.position 
=   ' absolute ' ;
    element.style.top    
=  top  +   ' px ' ;;
    element.style.left   
=  left  +   ' px ' ;;
    element.style.width  
=  width  +   ' px ' ;;
    element.style.height 
=  height  +   ' px ' ;;
  },
// 相对定位
  relativize:  function (element) {
    element 
=  $(element);
    
if  (element.style.position  ==   ' relative ' return ;
    Position.prepare();

    element.style.position 
=   ' relative ' ;
    
var  top   =  parseFloat(element.style.top   ||   0 -  (element._originalTop  ||   0 );
    
var  left  =  parseFloat(element.style.left  ||   0 -  (element._originalLeft  ||   0 );

    element.style.top    
=  top  +   ' px ' ;
    element.style.left   
=  left  +   ' px ' ;
    element.style.height 
=  element._originalHeight;
    element.style.width  
=  element._originalWidth;
  }
}

//  Safari returns margins on body which is incorrect if the child is absolutely
//
 positioned.  For performance reasons, redefine Position.cumulativeOffset for
//
 KHTML/WebKit only.
if  ( / Konqueror | Safari | KHTML / .test(navigator.userAgent)) {
  Position.cumulativeOffset 
=   function (element) {
    
var  valueT  =   0 , valueL  =   0 ;
    
do  {
      valueT 
+=  element.offsetTop   ||   0 ;
      valueL 
+=  element.offsetLeft  ||   0 ;
      
if  (element.offsetParent  ==  document.body)
        
if  (Element.getStyle(element,  ' position ' ==   ' absolute ' break ;

      element 
=  element.offsetParent;
    } 
while  (element);

    
return  [valueL, valueT];
  }
}

// --------->完成

你可能感兴趣的:(【Prototype 1.4.0】源码解读----全文注释版)