我思故我强

prototype学习资料(三)


prototype学习资料(三)

js 代码
  1. /**   
  2. * Form.Element.getValue 会经常用到,所以做了一个快捷引用   
  3. * 取得某个表单控件的值,可以简化调用为 $F("username"),真是方便啊   
  4. */     
  5. var  $F = Form.Element.getValue;    
  6. /**   
  7. * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样   
  8. * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素,   
  9. * 当表单元素的值发生变化的时候,就执行回调函数   
  10.  
  11. * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。   
  12. * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。   
  13.  
  14. * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。   
  15. * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等   
  16.  
  17. */     
  18. Abstract.TimedObserver =  function () {}    
  19. /**   
  20. * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心   
  21. */     
  22. Abstract.TimedObserver.prototype = {    
  23. initialize:  function (element, frequency, callback) {    
  24. this .frequency = frequency;    
  25. this .element = $(element);    
  26. this .callback = callback;    
  27.   
  28. this .lastValue =  this .getValue();    
  29. this .registerCallback();    
  30. },    
  31. registerCallback:  function () {    
  32. setInterval( this .onTimerEvent.bind( this ),  this .frequency * 1000);    
  33. },    
  34. onTimerEvent:  function () {    
  35. var  value =  this .getValue();    
  36. if  ( this .lastValue != value) {    
  37. this .callback( this .element, value);    
  38. this .lastValue = value;    
  39. }    
  40. }    
  41. }    
  42. /**   
  43. * Form.Element.Observer 监视指定表单域的值是否变化   
  44. */     
  45. Form.Element.Observer = Class.create();    
  46. Form.Element.Observer.prototype = ( new  Abstract.TimedObserver()).extend({    
  47. getValue:  function () {    
  48. return  Form.Element.getValue( this .element);    
  49. }    
  50. });    
  51. /**   
  52. * Form.Element.Observer 监视指定表单所有控件的值是否有变化   
  53. */     
  54. Form.Observer = Class.create();    
  55. Form.Observer.prototype = ( new  Abstract.TimedObserver()).extend({    
  56. getValue:  function () {    
  57. return  Form.serialize( this .element);    
  58. }    
  59. });    
  60. /*--------------------------------------------------------------------------*/     
  61. /**   
  62. * EventObserver 相比上面的 TimedObserver,是更具主动性的一种监测   
  63. * 它直接为表单控件(根据 type 的不同) 注册相应的事件处理, 只要发现某个控件值发生改变,就执行回调函数   
  64. */     
  65. Abstract.EventObserver =  function () {}    
  66. Abstract.EventObserver.prototype = {    
  67. initialize:  function (element, callback) {    
  68. this .element = $(element);    
  69. this .callback = callback;    
  70.   
  71. this .lastValue =  this .getValue();    
  72. if  ( this .element.tagName.toLowerCase() == 'form')    
  73. this .registerFormCallbacks();    
  74. else     
  75. this .registerCallback( this .element);    
  76. },    
  77. onElementEvent:  function () {    
  78. var  value =  this .getValue();    
  79. if  ( this .lastValue != value) {    
  80. this .callback( this .element, value);    
  81. this .lastValue = value;    
  82. }    
  83. },    
  84. registerFormCallbacks:  function () {    
  85. var  elements = Form.getElements( this .element);    
  86. for  ( var  i = 0; i < elements.length; i++)    
  87. this .registerCallback(elements);    
  88. },    
  89. registerCallback:  function (element) {    
  90. if  (element.type) {    
  91. switch  (element.type.toLowerCase()) {    
  92. /**   
  93. * checkbox 和 radio 类型的控件注册 onclick 事件处理   
  94. */     
  95. case  'checkbox':    
  96. case  'radio':    
  97. element.target =  this ;    
  98. element.prev_onclick = element.onclick || Prototype.emptyFunction;    
  99. /**   
  100. * 相信这里有改进的空间,应该使用其后的 Event对象提供的注册管理功能来统一注册   
  101. */     
  102. element.onclick =  function () {    
  103. this .prev_onclick();    
  104. this .target.onElementEvent();    
  105. }    
  106. break ;    
  107. /**   
  108. * 其他类型的控件注册 onchange 事件处理   
  109. */     
  110. case  'password':    
  111. case  'text':    
  112. case  'textarea':    
  113. case  'select-one':    
  114. case  'select-multiple':    
  115. element.target =  this ;    
  116. element.prev_onchange = element.onchange || Prototype.emptyFunction;    
  117. element.onchange =  function () {    
  118. this .prev_onchange();    
  119. this .target.onElementEvent();    
  120. }    
  121. break ;    
  122. }    
  123. }    
  124. }    
  125. }    
  126. /**   
  127. * 监视指定表单控件   
  128. */     
  129. Form.Element.EventObserver = Class.create();    
  130. Form.Element.EventObserver.prototype = ( new  Abstract.EventObserver()).extend({    
  131. getValue:  function () {    
  132. return  Form.Element.getValue( this .element);    
  133. }    
  134. });    
  135. /**   
  136. * 监视指定表单所有控件   
  137. */     
  138. Form.EventObserver = Class.create();    
  139. Form.EventObserver.prototype = ( new  Abstract.EventObserver()).extend({    
  140. getValue:  function () {    
  141. return  Form.serialize( this .element);    
  142. }    
  143. });    
  144. /**   
  145. * 封装事件处理的静态工具对象   
  146. */     
  147. if  (!window.Event) {    
  148. var  Event =  new  Object();    
  149. }    
  150. Object.extend(Event, {    
  151. KEY_BACKSPACE: 8,    
  152. KEY_TAB: 9,    
  153. KEY_RETURN: 13,    
  154. KEY_ESC: 27,    
  155. KEY_LEFT: 37,    
  156. KEY_UP: 38,    
  157. KEY_RIGHT: 39,    
  158. KEY_DOWN: 40,    
  159. KEY_DELETE: 46,    
  160. element:  function (event) {    
  161. return  event.target || event.srcElement;    
  162. },    
  163. isLeftClick:  function (event) {    
  164. return  (((event.which) && (event.which == 1)) ||    
  165. ((event.button) && (event.button == 1)));    
  166. },    
  167. /**   
  168. * click事件时鼠标以页面为基准的x坐标值, 考虑到了滚动条导致的位移差   
  169. */     
  170. pointerX:  function (event) {    
  171. return  event.pageX || (event.clientX +    
  172. (document.documentElement.scrollLeft || document.body.scrollLeft));    
  173. },    
  174. /**   
  175. * click事件时鼠标以页面为基准的y坐标值, 考虑到了滚动条导致的位移差   
  176. */     
  177. pointerY:  function (event) {    
  178. return  event.pageY || (event.clientY +    
  179. (document.documentElement.scrollTop || document.body.scrollTop));    
  180. },    
  181. /**   
  182. * 停止冒泡(参见 http://www.quirksmode.org/js/events_order.html) 和阻止浏览器执行与事件相关的默认动作   
  183. * 比如   
  184. google   
  185. * 那么点击该连接,页面并不会执行转向   
  186. */     
  187. stop:  function (event) {    
  188. if  (event.preventDefault) {    
  189. event.preventDefault();    
  190. event.stopPropagation();    
  191. else  {    
  192. event.returnValue =  false ;    
  193. }    
  194. },    
  195. // find the first node with the given tagName, starting from the    
  196. // node the event was triggered on; traverses the DOM upwards    
  197. /**   
  198. * 找到事件元素的父级元素中,最接近事件元素且等同于指定标签名的父元素。   
  199. * 如果到达顶级元素(HTML),那么就返回顶级元素   
  200. */     
  201. findElement:  function (event, tagName) {    
  202. var  element = Event.element(event);    
  203. while  (element.parentNode && (!element.tagName ||    
  204. (element.tagName.toUpperCase() != tagName.toUpperCase())))    
  205. element = element.parentNode;    
  206. return  element;    
  207. },    
  208. /**   
  209. * 其后的代码封装了事件的注册和反注册,避免ie的内存泄露的bug   
  210. * 参见 http://javascript.weblogsinc.com/en...34000267034921/   
  211. */     
  212. observers:  false ,    
  213. /**   
  214. * this.observers 的数据格式是一个二维数组,二维的数组分别四个元素分别是   
  215. * [注册事件对象,事件名,事件处理函数,事件处理模式布尔值]   
  216. */     
  217. _observeAndCache:  function (element, name, observer, useCapture) {    
  218. if  (! this .observers)  this .observers = ;    
  219. if  (element.addEventListener) {    
  220. this .observers.push([element, name, observer, useCapture]);    
  221. element.addEventListener(name, observer, useCapture);    
  222. else   if  (element.attachEvent) {    
  223. this .observers.push([element, name, observer, useCapture]);    
  224. element.attachEvent('on' + name, observer);    
  225. }    
  226. },    
  227. unloadCache:  function () {    
  228. if  (!Event.observers)  return ;    
  229. for  ( var  i = 0; i < Event.observers.length; i++) {    
  230. /**   
  231. * 这里与 Ajax.Request 对象设置 request header 的代码异曲同工   
  232. */     
  233. Event.stopObserving.apply( this , Event.observers);    
  234. Event.observers[0] =  null ;    
  235. }    
  236. Event.observers =  false ;    
  237. },    
  238. /**   
  239. * 注册对象的事件处理,并记录到cache中   
  240. */     
  241. observe:  function (element, name, observer, useCapture) {    
  242. var  element = $(element);    
  243. useCapture = useCapture ||  false ;    
  244.   
  245. if  (name == 'keypress' &&    
  246. ((navigator.appVersion.indexOf('AppleWebKit') > 0)    
  247. || element.attachEvent))    
  248. name = 'keydown';    
  249.   
  250. this ._observeAndCache(element, name, observer, useCapture);    
  251. },    
  252. /**   
  253. * 取消对象已注册的事件处理   
  254. */     
  255. stopObserving:  function (element, name, observer, useCapture) {    
  256. var  element = $(element);    
  257. useCapture = useCapture ||  false ;    
  258.   
  259. if  (name == 'keypress' &&    
  260. ((navigator.appVersion.indexOf('AppleWebKit') > 0)    
  261. || element.detachEvent))    
  262. name = 'keydown';    
  263.   
  264. if  (element.removeEventListener) {    
  265. element.removeEventListener(name, observer, useCapture);    
  266. else   if  (element.detachEvent) {    
  267. element.detachEvent('on' + name, observer);    
  268. }    
  269. }    
  270. });    
  271. /* prevent memory leaks in IE */     
  272. /**   
  273. * 页面onload 的时候取消所有事件注册,避免ie内存泄漏的bug   
  274. */     
  275. Event.observe(window, 'unload', Event.unloadCache,  false );    
  276. /**   
  277. * Position 对象也是常用的工具类,提供了获取元素在页面上位置的函数,Drag&Drop的效果一定常会用到   
  278. * 具体的应用参考 script.aculo.us 基于prototype 的实现,尤其是dragdrop.js。   
  279. */     
  280. var  Position = {    
  281. // set to true if needed, warning: firefox performance problems    
  282. // NOT neeeded for page scrolling, only if draggable contained in    
  283. // scrollable elements    
  284. includeScrollOffsets:  false ,    
  285. // must be called before calling withinIncludingScrolloffset, every time the    
  286. // page is scrolled    
  287. prepare:  function () {    
  288. this .deltaX = window.pageXOffset    
  289. || document.documentElement.scrollLeft    
  290. || document.body.scrollLeft    
  291. || 0;    
  292. this .deltaY = window.pageYOffset    
  293. || document.documentElement.scrollTop    
  294. || document.body.scrollTop    
  295. || 0;    
  296. },    
  297. /**   
  298. * 当对象所处的页面有滚动条是,计算位移   
  299. */     
  300. realOffset:  function (element) {    
  301. var  valueT = 0, valueL = 0;    
  302. do  {    
  303. valueT += element.scrollTop || 0;    
  304. valueL += element.scrollLeft || 0;    
  305. element = element.parentNode;    
  306. while  (element);    
  307. return  [valueL, valueT];    
  308. },    
  309. /**   
  310. * 计算出对象在页面上的位置   
  311. */     
  312. cumulativeOffset:  function (element) {    
  313. var  valueT = 0, valueL = 0;    
  314. do  {    
  315. valueT += element.offsetTop || 0;    
  316. valueL += element.offsetLeft || 0;    
  317. element = element.offsetParent;    
  318. while  (element);    
  319. return  [valueL, valueT];    
  320. },    
  321. // caches x/y coordinate pair to use with overlap    
  322. /**   
  323. * 判断一个坐标是否在指定元素的空间范围中   
  324. * 比如你想判断鼠标点击点的坐标是否在某个层或窗口   
  325. */     
  326. within:  function (element, x, y) {    
  327. if  ( this .includeScrollOffsets)    
  328. return   this .withinIncludingScrolloffsets(element, x, y);    
  329. this .xcomp = x;    
  330. this .ycomp = y;    
  331. this .offset =  this .cumulativeOffset(element);    
  332. return  (y >=  this .offset[1] &&    
  333. y <  this .offset[1] + element.offsetHeight &&    
  334. x >=  this .offset[0] &&    
  335. x <  this .offset[0] + element.offsetWidth);    
  336. },    
  337. withinIncludingScrolloffsets:  function (element, x, y) {    
  338. var  offsetcache =  this .realOffset(element);    
  339. this .xcomp = x + offsetcache[0] -  this .deltaX;    
  340. this .ycomp = y + offsetcache[1] -  this .deltaY;    
  341. this .offset =  this .cumulativeOffset(element);    
  342. return  ( this .ycomp >=  this .offset[1] &&    
  343. this .ycomp <  this .offset[1] + element.offsetHeight &&    
  344. this .xcomp >=  this .offset[0] &&    
  345. this .xcomp <  this .offset[0] + element.offsetWidth);    
  346. },    
  347. // within must be called directly before    
  348. /**   
  349. * 调用该方法时,确保首先调用了within方法   
  350. * 如果x,y坐标位于element的空间范围中,那么返回一个小于1的标示位置的值,比如0.5标示该坐标位于element空间的中线上   
  351. */     
  352. overlap:  function (mode, element) {    
  353. if  (!mode)  return  0;    
  354. if  (mode == 'vertical')    
  355. return  (( this .offset[1] + element.offsetHeight) -  this .ycomp) /    
  356. element.offsetHeight;    
  357. if  (mode == 'horizontal')    
  358. return  (( this .offset[0] + element.offsetWidth) -  this .xcomp) /    
  359. element.offsetWidth;    
  360. },    
  361. /**   
  362. * 复制源对象的空间数据到目的对象。   
  363. * 常用的地方:拖缀一个层到新地方时,常常动态构造和该层同样大小的虚层。   
  364. */     
  365. clone:  function (source, target) {    
  366. source = $(source);    
  367. target = $(target);    
  368. target.style.position = 'absolute';    
  369. var  offsets =  this .cumulativeOffset(source);    
  370. target.style.top = offsets[1] + 'px';    
  371. target.style.left = offsets[0] + 'px';    
  372. target.style.width = source.offsetWidth + 'px';    
  373. target.style.height = source.offsetHeight + 'px';    
  374. }    
  375. }   

posted on 2007-09-20 19:40 李云泽 阅读(262) 评论(0)  编辑  收藏 所属分类: Prototype(Ajax)


只有注册用户登录后才能发表评论。


网站导航: