OSDN Git Service

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