OSDN Git Service

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