OSDN Git Service

4f1f136713184b55082e8a5c2d2d34e03e94ef4e
[pettanr/clientJs.git] / 0.6.x / js / 00_core / 06_XEventDispatcher.js
1 /**\r
2  * X.EventDispatcher\r
3  * \r
4  *  1. as3 の EventDispatcher ライクなクラス。そのまま使ったり、継承したり。コールバック中にイベントを追加したら?削除したら?にも対処している。\r
5  *  2. _rawObject メンバがいる場合、addEventListener, attachEvent, on 等で生のブラウザオブジェクトにリスナを登録する。\r
6  *     window, document, HTMLElement, Image, XHR などが _rawObject\r
7  * \r
8  * use X.Callback\r
9  * \r
10  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.addEventListener\r
11  * イベント発送中のリスナーの追加\r
12  * EventListener がイベント処理中に EventTarget に追加された場合、それが現在のアクションによって実行されることはありませんが、浮上フェーズのように、後の段階のイベントフローで実行されるかもしれません。\r
13  * \r
14  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.removeEventListener\r
15  * イベントリスナーが イベントを処理中であるイベントターゲットから削除された場合、現在のアクションによってそのイベントリスナーが実行されることはありません。\r
16  * イベントリスナーは、決して削除された後に実行されることはありません。\r
17  * イベントターゲット上にある現在のどのイベントリスナーも指定していない引数付きの removeEventListener は、何の効果もありません。\r
18  */\r
19 \r
20 X.Event = {\r
21         COMPLETE               :  1,\r
22         SUCCESS                :  2,\r
23         ERROR                  :  3,\r
24         PROGRESS               :  4,\r
25         BEFORE_CANCEL          :  5,\r
26         CANCELED               :  6,\r
27         TIMEOUT                :  7,\r
28         BEFORE_KILL_INSTANCE   :  8,\r
29         KILL_INSTANCE          :  9,\r
30         KILL_INSTANCE_CANCELED : 10,\r
31         _LAST_EVENT            : 10\r
32 };\r
33 \r
34 // ------------------------------------------------------------------------- //\r
35 // ------------ local variables -------------------------------------------- //\r
36 // ------------------------------------------------------------------------- //\r
37 var X_EventDispatcher_once       = false,\r
38         X_EventDispatcher_needsIndex = false,\r
39         X_EventDispatcher_temp       = {},\r
40         \r
41         X_EventDispatcher_safariPreventDefault = false; //  // Safari3-\r
42 \r
43 if( X.UA.MacIE ){\r
44         X_EventDispatcher_temp.DOM_W3C    = true;\r
45         X_EventDispatcher_temp.EVENT_DOM0 = true;\r
46 } else\r
47 if( X.UA.IE4 ){ // ie4 & iemobi4\r
48         X_EventDispatcher_temp.DOM_IE4    = true;\r
49         X_EventDispatcher_temp.EVENT_DOM0 = true;\r
50 } else\r
51 if( document.getElementById ){\r
52         X_EventDispatcher_temp.DOM_W3C = true;\r
53         if( document.addEventListener ){\r
54                 X_EventDispatcher_temp.EVENT_W3C = true;\r
55         } else\r
56         if( document.attachEvent ){\r
57                 X_EventDispatcher_temp.EVENT_IE = true;\r
58         } else {\r
59                 X_EventDispatcher_temp.EVENT_DOM0 = true;\r
60         };\r
61 } else\r
62 if( document.all ){\r
63         X_EventDispatcher_temp.DOM_IE4    = true;\r
64         X_EventDispatcher_temp.EVENT_DOM0 = true;\r
65 } else\r
66 if( document.layers ){\r
67         \r
68 } else {\r
69         \r
70 };\r
71 \r
72 // ------------------------------------------------------------------------- //\r
73 // --- interface ----------------------------------------------------------- //\r
74 // ------------------------------------------------------------------------- //\r
75 \r
76 /**\r
77  * イベントターゲット(widnow, document, Image, XHR 等)をラップする場合、通常は new 時に渡します。参照:コンストラクタ実体 {@link X.EventDispatcher.Constructor}\r
78  * アプリケーション独自のイベントをやり取りしたいだけ、という場合、イベントターゲットは不要です。\r
79  * @class\r
80  * @classdesc EventTarget オブジェクトをラップしたり、アプリケーションで独自に定義したイベントを発信するためのクラスです。\r
81  *     listen, unlisten, dispatch という addEventListener, removeEventListener, dispatchEvent に対応する関数を持ちます。\r
82  *     さらに listening という as3 の hasEventListener に相当する関数を持ちます。\r
83  *     イベントターゲットオブジェクト(widnow, document, HTMLElement, XHR 等)が _rawObject に設定されていた場合に、それらへ実際のイベント登録・解除も行います。\r
84  *     このイベントの登録・解除はクロスブラウザで、IE5~8 の独自イベントの差異を吸収し、DOM0 に対しても複数のコールバックを登録することができます。\r
85  *     またコールバックに対して、this コンテキストや、追加の引数を指定もできます。 this コンテキストを指定しなかった場合、EventDispatcher インスタンスがコールバックの this になります。 \r
86  * @param {object=} opt_rawObject\r
87  */\r
88 X.EventDispatcher =\r
89         X.Class.create(\r
90                 'EventDispatcher',\r
91                 \r
92         /** @lends {X.EventDispatcher.prototype} */\r
93                 {\r
94 \r
95             /**\r
96              * @namespace\r
97              * @memberof X.EventDispatcher\r
98              */\r
99 \r
100                 /**\r
101                  * イベントリスナをイベント名(string)や数値(1~,フレームワーク内で定義)をキーとするArrayで記憶します。\r
102                  * Arrayには、{k:種類,x:コンテキスト(thisObject),f:関数,s:サプリメントする引数の配列} というハッシュ、または関数が蓄えられています。\r
103                  * @private\r
104                  * @type {Object.<(number|string), Array.<(callbackHash|function)>>}\r
105                  */\r
106                         '_listeners'    : null,\r
107 \r
108                 /**\r
109                  * _rawObject には HTMLElement, window, document, XHR といったイベントターゲットオブジェクトを設定します。\r
110                  * _rawObject が設定されていると on(), off() 時に addEventListener(DOM Level2) や detachEvent(ie5~8), on~(DOM0) 等を操作します。\r
111                  * _rawObject は最初の on() 前に設定しておかないと addEventListener 等が意図したように行われません。\r
112                  * X.Dom.Node では非同期に HTMLElement を生成していて、要素生成以前に on, off を呼び出すことができます。これは適宜に migrateEvent, restoreEvent を呼んで解決しているためです。\r
113                  * @private\r
114                  * @type {Object}\r
115                  */\r
116                         '_rawObject'    : null,\r
117                 \r
118                 /**\r
119                  * _rawObject がある場合、イベントターゲットオブジェクトに対して addEventListener, attachEvent, onXX 時に実際に渡される関数。\r
120                  * イベントタイプが変わっても、単一の オブジェクトを使いまわす。(但し例外は、IE8以下の XHR|MSXML に対して。)最初の listen で生成されて、最後の unlisten で削除される。\r
121                  * @private\r
122                  * @type {X.Callback}\r
123                  */\r
124                         '_handleEvent'  : null,\r
125                         \r
126                 /*\r
127                  * dispatch 中に dispatch が呼ばれた際に、そのネストの深さを保存する。\r
128                  * dispatch() 終了時に _dispatching が 0 の場合に、現在のインスタンスの dispatch がすべて終わったことになる。\r
129                  * @private\r
130                  * @type {number}\r
131                  */\r
132                         '_dispatching'  : 0,     // dispatch 中の unlisten で使用\r
133 \r
134                 /*\r
135                  * dispatch 中に listen が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。\r
136                  * _dispatching が 0 のときに _reserves を使って listen() を呼び出す。\r
137                  * @private\r
138                  * @type {Object.<(number|string), Array.<(X.Callback|function)>>}\r
139                  */\r
140                         '_reserves'     : null,\r
141                 \r
142                 /*\r
143                  * dispatch 中に unlisten が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。\r
144                  * _dispatching が 0 のときに _unlistens を使って unlisten() を呼び出す。\r
145                  * @private\r
146                  * @type {Object.<(number|string), Array.<(X.Callback|function)>>}\r
147                  */\r
148                         '_unlistens'    : null,\r
149                         \r
150                 /*\r
151                  * dispatch 中に kill が呼ばれた場合に、X.EventDispatcher インスタンスの削除を dispatch 後にずらすために立てるフラグ。\r
152                  * @private\r
153                  * @type {boolean}\r
154                  */\r
155                         '_killReserved' : false,\r
156                         \r
157             /**\r
158              * X.EventDispatcher のコンストラクタの実体。\r
159              * @constructs\r
160              * @this {X.EventDispatcher}\r
161              * @params {object=} opt_rawObject\r
162              */\r
163                         Constructor : function( opt_rawObject ){\r
164                                 if( opt_rawObject ){\r
165                                         this._rawObject = opt_rawObject;\r
166                                 };\r
167                         },\r
168 \r
169                 /**\r
170                  * 登録されたイベントリスナを呼び出す。イベントリスナの返り値(数値)を OR したものを返す。\r
171                  * @this {X.EventDispatcher}\r
172                  * @return {number}\r
173                  * @param {(eventHash|string|number)} e\r
174                  */     \r
175                         dispatch : X_EventDispatcher_dispatch,\r
176 \r
177             /**\r
178              * \r
179              * @this {X.EventDispatcher}\r
180              */\r
181                         on     : X_EventDispatcher_listen,\r
182                         listen : X_EventDispatcher_listen,\r
183                 \r
184                 /**\r
185                  * 一度 dispatch された時に自動で unlisten されるフラグを立てて listen する。\r
186                  * @this {X.EventDispatcher}\r
187                  * @return {X.EventDispatcher}\r
188                  * @param {(string|number|Array.<(string,number)>)} type\r
189                  * @param {(listener|function|Array)=} opt_arg1\r
190                  * @param {(function|Array=} opt_arg2\r
191                  * @param {Array=} opt_arg3\r
192                  */\r
193                         listenOnce : function( type, opt_arg1, opt_arg2, opt_arg3 ){\r
194                                 X_EventDispatcher_once = true;\r
195                                 this.listen( type, opt_arg1, opt_arg2, opt_arg3 );\r
196                                 X_EventDispatcher_once = false;\r
197                                 return this;\r
198                         },\r
199                         \r
200                         off      : X_EventDispatcher_unlisten,\r
201                         unlisten : X_EventDispatcher_unlisten,\r
202 \r
203                 /**\r
204                  * イベントリスナの登録状況を真偽値で返す。戻り値が数値(index)の場合もあるが、これは内部のみで使用。\r
205                  * listening() と type を省略した場合、一つでも登録があれば true を返す。\r
206                  * listening( type ) と type だけを与えた場合、その type に登録があれば true を返す。\r
207                  * type と イベントリスナの組み合わせが登録されているかを調べる場合は、listen 時の thisObject や args(Array) も一致させて渡す必要がある。\r
208                  *  this.listen( [ 'myevent', 'yourevent' ], this, onMyEvent, args = [ 1, 'a' ] );\r
209                  *  this.listening( 'myevent', this, onMyEvent, args ) === true;\r
210                  * @this {X.EventDispatcher}\r
211                  * @return {(number|boolean)}\r
212                  * @param {(string|number)=} opt_type\r
213                  * @param {(listener|function|Array)=} opt_arg1\r
214                  * @param {(function|Array=} opt_arg2\r
215                  * @param {Array=} opt_arg3\r
216                  */                     \r
217                         listening : function( opt_type, opt_arg1, opt_arg2, opt_arg3 ){\r
218                                 var list = this._listeners, unlistens, i, f, hash;\r
219                                 if( opt_type === undefined ) return !!list;\r
220                                 if( !list || !( list = list[ opt_type ] ) ) return false;\r
221                                 if( opt_arg1 === undefined ) return true;\r
222                                 \r
223                                 if( opt_arg1.k ){\r
224                                         hash = opt_arg1;\r
225                                 } else {\r
226                                         hash = X.Callback._classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
227                                 };\r
228                                 \r
229                                 if( ( unlistens = this._unlistens ) && ( unlistens = unlistens[ opt_type ] ) ){\r
230                                         for( i = unlistens.length; i; ){\r
231                                                 f = unlistens[ --i ];\r
232                                                 if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ) return false;\r
233                                         };\r
234                                 };\r
235                                 for( i = list.length; i; ){\r
236                                         f = list[ --i ];\r
237                                         if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ) return X_EventDispatcher_needsIndex ? i : true;\r
238                                 };\r
239                                 return false;\r
240                         },\r
241 \r
242                 /**\r
243                  * delay(ミリ秒)後にイベントを dispatch する。戻り値は uid = X.Timer.add() のタイマーID(数値)。X.Timer.remove(uid) でタイマーを解除して dispatch を中止できる。\r
244                  * @this {X.EventDispatcher}\r
245                  * @return {number}\r
246                  * @param {number=} delay\r
247                  * @param {(eventHash|string|number)=} e\r
248                  */                     \r
249                         asyncDispatch : function( delay, e ){\r
250                                 return X.Timer.add( delay, 1, this, this.dispatch, [ e ] );\r
251                         }\r
252                 }\r
253         );\r
254 \r
255 // ------------------------------------------------------------------------- //\r
256 // --- implements ---------------------------------------------------------- //\r
257 // ------------------------------------------------------------------------- //\r
258 \r
259 /**\r
260  * 登録されたイベントリスナを呼び出す。イベントリスナの返り値(数値)を OR したものを返す。イベントハッシュでなく、string|number を渡すと内部でイベントハッシュを作る\r
261  * @this {X.EventDispatcher}\r
262  * @return {number} X.Callback で定義された数値\r
263  * @param {(eventHash|string|number)} e\r
264  */\r
265 function X_EventDispatcher_dispatch( e ){\r
266         var list  = this[ '_listeners' ],\r
267                 ret   = X.Callback.NONE,\r
268                 type  = e[ 'type' ],\r
269                 unlistens, i, l, f, r, sysOnly;\r
270         \r
271         if( !list || !( list = list[ type || e ] ) ) return ret;\r
272         \r
273         // 数値, 文字が渡された場合\r
274         if( !type ){\r
275                 e = { type : type = e };\r
276         };\r
277         e.target = e.target || this;\r
278         \r
279         ++this._dispatching;\r
280         \r
281         // todo:\r
282         // type も保存\r
283         this._unlistens = this._unlistens || {};\r
284         unlistens = this._unlistens[ type ];\r
285         \r
286         for( i = 0; i < list.length; ++i ){\r
287                 f = list[ i ];\r
288                 if( !unlistens ){\r
289                         unlistens = this._unlistens[ type ];\r
290                 };\r
291                 if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
292                 \r
293                 r = X.Callback.NONE;\r
294                 if( f.k ){\r
295                         f.a = [ e ];\r
296                         r = X.Callback._proxyCallback( f );\r
297                 } else {\r
298                         r = f.call( this, e );\r
299                 };\r
300                 \r
301                 if( f.once || r & X.Callback.UN_LISTEN ){\r
302                         // dispatch 中に unlisten が作られることがある\r
303                         if( !unlistens ){\r
304                                 unlistens = this._unlistens || ( this._unlistens = {} );\r
305                                 unlistens = unlistens[ type ] || ( unlistens[ type ] = [] );\r
306                         };\r
307                         unlistens.indexOf( f ) === -1 && ( unlistens[ unlistens.length ] = f );\r
308                 };\r
309 \r
310                 if( r & X.Callback.STOP_NOW ){\r
311                         sysOnly = true;\r
312                 };\r
313                 ret |= r;\r
314         };\r
315         \r
316         if( ( --this._dispatching ) === 0 ){\r
317                 // dispatch 中に unlisten された要素の削除\r
318                 unlistens = this._unlistens;\r
319                 delete this._dispatching;\r
320                 delete this._unlistens;                                 \r
321                 \r
322                 for( type in unlistens ){\r
323                         list = unlistens[ type ];\r
324                         for( i = list.length; i; ){\r
325                                 this.unlisten( type, list[ --i ] );\r
326                         };\r
327                         list.length = 0;\r
328                         delete unlistens[ type ];\r
329                 };\r
330                 \r
331                 if( this._killReserved ){\r
332                         this.kill();\r
333                 } else\r
334                 if( list = this._reserves ){\r
335                         for( i = 0, l = list.length; i < l; ++i ){\r
336                                 f = list[ i ];\r
337                                 X_EventDispatcher_once = f[ 4 ];\r
338                                 this.listen( f[ 0 ], f[ 1 ], f[ 2 ], f[ 3 ] );\r
339                                 X_EventDispatcher_once = false;\r
340                                 f.length = 0;\r
341                         };\r
342                         list.length = 0;\r
343                         delete this._reserves;\r
344                 };\r
345         };\r
346         \r
347         return ret;\r
348 };\r
349 \r
350 /**\r
351  * \r
352  * @this {X.EventDispatcher}\r
353  * @return {X.EventDispatcher}\r
354  * @param {(string|number|Array.<(string,number)>)} type\r
355  * @param {(listener|function|Array)=} opt_arg1\r
356  * @param {(function|Array=} opt_arg2\r
357  * @param {Array=} opt_arg3\r
358  */\r
359 function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){\r
360         var list = this._listeners,\r
361                 i, r, f;\r
362         \r
363         if( this._dispatching ){\r
364                 if( !this._reserves ) this._reserves = [];\r
365                 this._reserves[ this._reserves.length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once ];\r
366                 return this;\r
367         };\r
368         \r
369         if( X.Type.isArray( type ) ){\r
370                 for( i = type.length; i; ){\r
371                         this.listen( type[ --i ], opt_arg1, opt_arg2, opt_arg3 );\r
372                 };\r
373                 return this;\r
374         };\r
375         \r
376         ( !list || !list[ type ] ) && X.Type.isString( type ) && X_EventDispatcher_actualAddEvent( this, type );\r
377         \r
378         if( this.listening( type, opt_arg1, opt_arg2, opt_arg3 ) ) return this;\r
379 \r
380         if( !list ) list = this._listeners = {};\r
381         if( !( list = list[ type ] ) ) list = this._listeners[ type ] = [];\r
382         \r
383         f = X.Callback._classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
384         list[ list.length ] = f;\r
385         f.once = X_EventDispatcher_once;\r
386         \r
387         return this;\r
388 };\r
389 \r
390 /**\r
391  * \r
392  * @this {X.EventDispatcher}\r
393  * @return {X.EventDispatcher}\r
394  * @param {(string|number|Array.<(string,number)>)=} opt_type\r
395  * @param {(listener|function|Array)=} opt_arg1\r
396  * @param {(function|Array=} opt_arg2\r
397  * @param {Array=} opt_arg3\r
398  */\r
399 function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){\r
400         var list = this._listeners,\r
401                 _list, reserves, unlistens, i, f;\r
402         if( !list ) return this;\r
403         \r
404         if( X.Type.isArray( opt_type ) ){\r
405                 for( i = opt_type.length; i; ){\r
406                         this.unlisten( opt_type[ --i ], opt_arg1, opt_arg2, opt_arg3 );\r
407                 };\r
408                 return this;\r
409         };\r
410         \r
411         if( opt_type === undefined ){\r
412                 // 全て削除\r
413                 for( opt_type in list ){\r
414                         _list = list[ opt_type ];\r
415                         for( i = _list.length; i; ){\r
416                                 this.unlisten( opt_type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
417                         };\r
418                         // this.unlisten( opt_type ); これは無茶!\r
419                 };\r
420                 return this;\r
421         } else\r
422         if( opt_arg1 === undefined ){\r
423                 // 同一タイプを全て削除\r
424                 if( _list = list[ opt_type ] ){\r
425                         for( i = _list.length; i; ){\r
426                                 this.unlisten( opt_type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
427                         };\r
428                 };\r
429                 return this;\r
430         } else\r
431         if( reserves = this._reserves ){\r
432                 for( i = reserves.length; i; ){\r
433                         f = reserves[ --i ];\r
434                         if( f[ 0 ] === opt_type && f[ 1 ] === opt_arg1 && f[ 2 ] === opt_arg2 && f[ 3 ] === opt_arg3 ){\r
435                                 reserves.splice( i, 1 );\r
436                                 if( !reserves.legth ) delete this._reserves;\r
437                                 return this;\r
438                         };\r
439                 };\r
440         };\r
441         \r
442         X_EventDispatcher_needsIndex = true;\r
443         i = this.listening( opt_type, opt_arg1, opt_arg2, opt_arg3 );\r
444         X_EventDispatcher_needsIndex = false;\r
445         if( i === false ) return this;\r
446 \r
447         f = ( _list = list[ opt_type ] )[ i ];\r
448         if( unlistens = this._unlistens ){\r
449                 ( unlistens = unlistens[ opt_type ] ) ?\r
450                         ( unlistens[ unlistens.length ] = f ) :\r
451                         ( this._unlistens[ opt_type ] = [ f ] );\r
452         } else {\r
453                 delete f.once;\r
454                 // f.kill === X.Callback._kill && f.kill();\r
455                 _list.splice( i, 1 );\r
456                 if( !_list.length ){\r
457                         delete this._listeners[ opt_type ];\r
458                         X.Type.isString( opt_type ) && X_EventDispatcher_actualRemoveEvent( this, opt_type );\r
459                         if( X.isEmptyObject( this._listeners ) ) delete this._listeners;\r
460                 };\r
461         };\r
462         return this;\r
463 };\r
464 \r
465 \r
466 var X_EventDispatcher_actualAddEvent =\r
467         // Days on the Moon DOM Events とブラウザの実装 \r
468         // http://nanto.asablo.jp/blog/2007/03/23/1339502\r
469         // Safari 2 では関数オブジェクトしか EventListener として使えませんが、Safari のナイトリービルドでは handleEvent メソッドを持つオブジェクトも EventListener として使えるようです。\r
470         X_EventDispatcher_temp.EVENT_W3C && ( X.UA.WebKit < 525.13 || X.UA.Opera7 || X.UA.NetFront < 4 ) ? // Safari3-\r
471                 (function( that, type ){\r
472                         var raw = that._rawObject;\r
473                         if( !raw ) return; \r
474                         that._handleEvent = that._handleEvent || X.Callback.create( that, X_EventDispatcher_actualHandleEvent );\r
475                         type = X.Dom.Event.Rename[ type] || type;\r
476                         if( raw.addEventListener ){\r
477                                 raw.addEventListener( type, that._handleEvent, false );\r
478                         } else {\r
479                                 // Safari は Image, Opera7 は window\r
480                                 raw[ 'on' + type ] = that._handleEvent;\r
481                         };\r
482                 }) :\r
483         X_EventDispatcher_temp.EVENT_W3C ?\r
484                 (function( that, type ){\r
485                         if( that._rawObject ){\r
486                                 that._handleEvent = that._handleEvent || X.Callback.create( that, X_EventDispatcher_actualHandleEvent );\r
487                                 that._rawObject.addEventListener( X.Dom.Event.Rename[ type ] || type, that._handleEvent, false );\r
488                         };\r
489                 }) :\r
490         X_EventDispatcher_temp.EVENT_IE ?\r
491                 (function( that, type ){\r
492                         var raw = that._rawObject;\r
493                         if( !raw ) return;\r
494                         type = X.Dom.Event.Rename[ type ] || type;\r
495                         //if( type === 'load' && that._tag && X.Dom.Event._LOAD_FIX_TAGS[ that._tag ] ){\r
496                         //      type = 'readystatechange';\r
497                         //};\r
498                         if( that._isXHR ){\r
499                                 // ie8- の XHR は window.event が更新されないため, eventType 毎に callback を指定する\r
500                                 raw[ 'on' + type ] = X.Callback.create( that, X_EventDispatcher_dispatch, [ type ] );\r
501                         } else {\r
502                                 that._handleEvent = that._handleEvent || X.Callback.create( that, X_EventDispatcher_actualHandleEvent );\r
503                                 \r
504                                 if( raw.attachEvent ){\r
505                                         raw.attachEvent( 'on' + type, that._handleEvent );\r
506                                 } else {\r
507                                         raw[ 'on' + type ] = that._handleEvent;\r
508                                 };                              \r
509                         };\r
510 \r
511                 }) :\r
512                 (function( that, type ){\r
513                         var raw = that._rawObject || ( that._ie4getRawNode && that._ie4getRawNode() );\r
514                         if( !raw ) return;\r
515                         raw[ 'on' + ( X.Dom.Event.Rename[ type ] || type ) ] = that._handleEvent = that._handleEvent || X.Callback.create( that, X_EventDispatcher_actualHandleEvent );\r
516                 });\r
517 \r
518 \r
519 var X_EventDispatcher_actualRemoveEvent =\r
520         X_EventDispatcher_temp.EVENT_W3C && ( X.UA.WebKit < 525.13 || X.UA.Opera7 || X.UA.NetFront < 4 ) ? // Safari3-\r
521                 (function( that, type ){\r
522                         var raw = that._rawObject;\r
523                         if( !raw ) return;\r
524                         type = X.Dom.Event.Rename[ type ] || type;\r
525                         \r
526                         if( raw.addEventListener ){ // Image\r
527                                 raw.removeEventListener( type, that._handleEvent, false );\r
528                         } else {\r
529                                 raw[ 'on' + type ] = null;\r
530                         };\r
531                         if( !that._listeners ){\r
532                                 X.Callback._correct( that._handleEvent );\r
533                                 delete that._handleEvent;\r
534                         };\r
535                 }) :\r
536         X_EventDispatcher_temp.EVENT_W3C ?\r
537                 (function( that, type ){\r
538                         var raw = that._rawObject;\r
539                         if( !raw ) return;\r
540                         raw.removeEventListener( X.Dom.Event.Rename[ type ] || type, that._handleEvent, false );\r
541                         if( !that._listeners ){\r
542                                 X.Callback._correct( that._handleEvent );\r
543                                 delete that._handleEvent;\r
544                         };\r
545                 }) :\r
546         X_EventDispatcher_temp.EVENT_IE ?\r
547                 (function( that, type ){\r
548                         var raw = that._rawObject;\r
549                         if( !raw ) return;\r
550                         type = X.Dom.Event.Rename[ type ] || type;\r
551                         //if( type === 'load' && that._tag && X.Dom.Event._LOAD_FIX_TAGS[ that._tag ] ){\r
552                         //      type = 'readystatechange';\r
553                         //};\r
554                         if( that._isXHR ){\r
555                                 X.Callback._correct( raw[ 'on' + type ] );\r
556                                 raw[ 'on' + type ] = X.emptyFunction;\r
557                                 raw[ 'on' + type ] = '';\r
558                         } else {\r
559                                 if( raw.attachEvent ){\r
560                                         raw.detachEvent( 'on' + type, that._handleEvent );\r
561                                 } else {\r
562                                         raw[ 'on' + type ] = X.emptyFunction;\r
563                                         raw[ 'on' + type ] = '';\r
564                                 };\r
565                                 \r
566                                 if( !that._listeners ){\r
567                                         X.Callback._correct( that._handleEvent );\r
568                                         delete that._handleEvent;\r
569                                 };                              \r
570                         };\r
571                 }) :\r
572                 (function( that, type ){\r
573                         var raw = that._rawObject || ( that._ie4getRawNode && that._ie4getRawNode() );\r
574                         if( !raw ) return;\r
575                         type = X.Dom.Event.Rename[ type ] || type;\r
576                         raw[ 'on' + type ] = X.emptyFunction;\r
577                         raw[ 'on' + type ] = '';\r
578                         if( !that._listeners ){\r
579                                 X.Callback._correct( that._handleEvent );\r
580                                 delete that._handleEvent;\r
581                         };\r
582                 });\r
583 \r
584 \r
585 // handleEvent を拡張可能にするために、クロージャに移動した\r
586 // Is this in regard to the Safari 1.x preventDefault bug on click/dblclick?\r
587 // https://groups.google.com/forum/#!msg/comp.lang.javascript/uYEuCHjHxnw/yKoHtZJPa1QJ\r
588 var X_EventDispatcher_actualHandleEvent =\r
589         X.UA.IE4 || X.UA.IE5678 ? // ie45678 EVENT_IE & EVENT_DOM0 for ie4\r
590                 (function(){\r
591                         var ret;\r
592                         \r
593                         if( event.type === 'readystatechange' && this._tag && X.Dom.Event._LOAD_FIX_TAGS[ this._tag ] ){\r
594                                 //type = 'readystatechange';\r
595                         };\r
596                         \r
597                         ret = X_EventDispatcher_dispatch.call( this, new X.Dom.Event( event, this, this._rawObject ) );\r
598 \r
599                         if( ret & X.Callback.STOP_PROPAGATION ){\r
600                                 event.cancelBubble = true;\r
601                         };\r
602                         if( ret & X.Callback.PREVENT_DEFAULT ){\r
603                                 this._tag === 'A' && this._rawObject.blur();\r
604                                 return event.returnValue = false;\r
605                         };\r
606                 }) :\r
607         //X.Dom.EVENT_W3C & EVENT_DOM0\r
608                 (function( e ){\r
609                         var ret = X_EventDispatcher_dispatch.call( this, new X.Dom.Event( e, this ) );\r
610                         \r
611                         if( ret & X.Callback.STOP_PROPAGATION ){\r
612                                 e.stopPropagation();\r
613                         };\r
614                         if( ret & X.Callback.PREVENT_DEFAULT ){\r
615                                 this._tag === 'A' && this._rawObject.blur();\r
616                                 e.preventDefault();\r
617                                 if( X.UA.WebKit < 525.13 ){ // Safari3-\r
618                                         if( e.type === 'click' || e.type === 'dbclick' ){\r
619                                                 X_EventDispatcher_safariPreventDefault = true;\r
620                                         };\r
621                                 };\r
622                                 return false;\r
623                         };\r
624                 });\r
625 \r
626 if( X.UA.WebKit < 525.13 ){ // Safari3-\r
627         document.documentElement.onclick =\r
628         document.documentElement.ondbclick = function( e ){\r
629                         if( X_EventDispatcher_safariPreventDefault ){\r
630                                 X_EventDispatcher_safariPreventDefault = false;\r
631                                 e.preventDefault();\r
632                                 return false;\r
633                         };\r
634                 };\r
635 };\r
636 \r
637 // イベントの退避、dom が画面から抜かれる場合に実施しておく\r
638 function X_EventDispatcher_migrateEvent( that ){\r
639         var hash = that._listeners,\r
640                 type;\r
641         if( !hash ) return;\r
642         for( type in hash ){\r
643                 // 数字イベントの除外\r
644                 '' + parseFloat( type ) !== type && X_EventDispatcher_actualRemoveEvent( that, type );\r
645         };\r
646 };\r
647 \r
648 // 退避したイベントの復帰\r
649 function X_EventDispatcher_restoreEvent( that ){\r
650         var hash = that._listeners,\r
651                 type;\r
652         if( !hash ) return;\r
653         for( type in hash ){\r
654                 // 数字イベントの除外\r
655                 '' + parseFloat( type ) !== type && X_EventDispatcher_actualAddEvent( that, type );\r
656         };\r
657 };\r
658 \r
659 \r
660 console.log( 'X.Core.EventDispatcher' );\r