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

【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. *?> )((\n | \r | .) *? )( ? : < \ / 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];
  }

}


// --------->完成
转自: http://www.cnblogs.com/me-sa/articles/605263.html

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