OSDN Git Service

Version 0.6.39, bugfix for listenOnce().
[pettanr/clientJs.git] / 0.6.x / js / core / 06_XEventDispatcher.js
1 /**\r
2  * use X.Callback\r
3  * \r
4  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.addEventListener\r
5  * イベント発送中のリスナーの追加\r
6  * EventListener がイベント処理中に EventTarget に追加された場合、それが現在のアクションによって実行されることはありませんが、浮上フェーズのように、後の段階のイベントフローで実行されるかもしれません。\r
7  * \r
8  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.removeEventListener\r
9  * イベントリスナーが イベントを処理中であるイベントターゲットから削除された場合、現在のアクションによってそのイベントリスナーが実行されることはありません。\r
10  * イベントリスナーは、決して削除された後に実行されることはありません。\r
11  * イベントターゲット上にある現在のどのイベントリスナーも指定していない引数付きの removeEventListener は、何の効果もありません。\r
12  */\r
13 \r
14 X.Event = {\r
15         COMPLETE    : 1,\r
16         SUCCESS     : 2,\r
17         ERROR       : 3,\r
18         PROGRESS    : 4,\r
19         _LAST_EVENT : 4\r
20 };\r
21 \r
22 \r
23 X.EventDispatcher =\r
24         X.Class.create(\r
25                 'EventDispatcher',\r
26                 {\r
27                         _listeners    : null,\r
28                         _dispatching  : 0, // dispatch 中の unlisten で使用\r
29                         _unlistens    : null, // dispatch 中の unlisten で使用\r
30                         _needsIndex   : false, // listening で index を返す\r
31                         _reserves     : null,\r
32                         _killReserved : false,\r
33                         \r
34                         listen : function( type, arg1, arg2, arg3 ){\r
35                                 var list = this._listeners,\r
36                                         r, f;\r
37                                 if( this._dispatching ){\r
38                                         // todo\r
39                                         // reserve\r
40                                         if( !this._reserves ) this._reserves = [];\r
41                                         this._reserves[ this._reserves.length ] = [ type, arg1, arg2, arg3, X.EventDispatcher._once ];\r
42                                         return this;\r
43                                 } else\r
44                                 if( this.listening( type, arg1, arg2, arg3 ) ) return this;\r
45 \r
46                                 if( !list ) list = this._listeners = {};\r
47                                 if( !( list = list[ type ] ) ) list = this._listeners[ type ] = [];\r
48                                 list[ list.length ] = f =\r
49                                         ( arg1 && !arg2 ) ?\r
50                                                 arg1 :\r
51                                                 X.Callback.create( arg1, arg2, arg3 );\r
52                                 f.once = X.EventDispatcher._once;\r
53                                 return this;\r
54                         },\r
55                         listenOnce : function( type, arg1, arg2, arg3 ){\r
56                                 X.EventDispatcher._once = true;\r
57                                 this.listen( type, arg1, arg2, arg3 );\r
58                                 X.EventDispatcher._once = false;\r
59                                 return this;\r
60                         },\r
61                         unlisten : function( type, arg1, arg2, arg3 ){\r
62                                 var list = this._listeners,\r
63                                         _list, reserves, unlistens, i, f;\r
64                                 if( !list ) return this;\r
65                                 if( type === undefined ){\r
66                                         // 全て削除\r
67                                         for( type in list ){\r
68                                                 _list = list[ type ];\r
69                                                 if( !( i = _list.length ) ) continue;\r
70                                                 for( ; i; ){\r
71                                                         this.unlisten( type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
72                                                 };\r
73                                                 // this.unlisten( type ); これは無茶!\r
74                                         };\r
75                                         return this;\r
76                                 };\r
77                                 if( arg1 === undefined ){\r
78                                         // 同一タイプを全て削除\r
79                                         if( !( list = list[ type ] ) || !( i = list.length ) ) return this;\r
80                                         for( ; i; ){\r
81                                                 this.unlisten( type, list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
82                                         };\r
83                                         return this;\r
84                                 };\r
85                                 \r
86                                 if( reserves = this._reserves ){\r
87                                         for( i = reserves.length; i; ){\r
88                                                 f = reserves[ --i ];\r
89                                                 if( f[ 0 ] === type && f[ 1 ] === arg1 && f[ 2 ] === arg2 && f[ 3 ] === arg3 ){\r
90                                                         reserves.splice( i, 1 );\r
91                                                         if( !reserves.legth ) delete this._reserves;\r
92                                                         return this;\r
93                                                 };\r
94                                         };\r
95                                 };\r
96                                 \r
97                                 this._needsIndex = true;\r
98                                 i = this.listening( type, arg1, arg2, arg3 );\r
99                                 delete this._needsIndex;\r
100                                 if( i === false ) return this;\r
101 \r
102                                 f = ( list = list[ type ] )[ i ];\r
103                                 if( unlistens = this._unlistens ){\r
104                                         ( unlistens = unlistens[ type ] ) ?\r
105                                                 ( unlistens[ unlistens.length ] = f ) :\r
106                                                 ( this._unlistens[ type ] = [ f ] );\r
107                                 } else {\r
108                                         delete f.once;\r
109                                         f.kill === X.Callback._kill && f.kill();\r
110                                         list.splice( i, 1 );\r
111                                         if( !list.length ){\r
112                                                 delete this._listeners[ type ];\r
113                                                 if( X.isEmptyObject( this._listeners ) ) delete this._listeners;\r
114                                         };\r
115                                 };\r
116                                 return this;\r
117                         },\r
118                         listening : function( type, arg1, arg2, arg3 ){\r
119                                 var list = this._listeners, unlistens, i, f;\r
120                                 if( type === undefined ) return !!list;\r
121                                 if( !list || !( list = list[ type ] ) ) return false;\r
122                                 if( arg1 === undefined ) return true;\r
123                                 if( ( unlistens = this._unlistens ) && ( unlistens = unlistens[ type ] ) ){\r
124                                         for( i = unlistens.length; i; ){\r
125                                                 f = unlistens[ --i ];\r
126                                                 if( f === arg1 || ( f.same && f.same( arg1, arg2, arg3 ) ) ) return false;\r
127                                         };\r
128                                 };\r
129                                 for( i = list.length; i; ){\r
130                                         f = list[ --i ];\r
131                                         if( f === arg1 || ( f.same && f.same( arg1, arg2, arg3 ) ) ) return this._needsIndex ? i : true;\r
132                                 };\r
133                                 return false;\r
134                         },\r
135                         /*\r
136                          * dispatch 中に dispatch が呼ばれるケースがあるため、\r
137                          * _dispatching では その深さを保存する\r
138                          * _dispatching が 0 のときに unlistens を削除する\r
139                          *\r
140                          */\r
141                         dispatch : function( e ){\r
142                                 // dispatch 中の listen は?\r
143                                 var list  = this._listeners,\r
144                                         ret   = X.Callback.NONE,\r
145                                         type  = e.type,\r
146                                         unlistens, i, l, f, r, sysOnly;\r
147 \r
148                                 if( !list || !( list = list[ type ] ) ) return ret;\r
149                                 \r
150                                 ++this._dispatching;\r
151                                 \r
152                                 // todo:\r
153                                 // type も保存\r
154                                 this._unlistens = this._unlistens || {};\r
155                                 unlistens = this._unlistens[ type ];\r
156                                 \r
157                                 for( i = 0; i < list.length; ++i ){\r
158                                         f = list[ i ];\r
159                                         if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
160 \r
161                                         r = typeof f === 'function' ? f( e ) : f.handleEvent( e );\r
162                                         \r
163                                         if( f.once === true || r & X.Callback.UN_LISTEN ){\r
164                                                 unlistens ?\r
165                                                         ( unlistens[ unlistens.length ] = f ) :\r
166                                                         ( unlistens = this._unlistens[ type ] = [ f ] );\r
167                                         };\r
168 \r
169                                         if( r & X.Callback.STOP_NOW ){\r
170                                                 sysOnly = true;\r
171                                         };\r
172                                         ret |= r;\r
173                                 };\r
174                                 \r
175                                 if( ( --this._dispatching ) === 0 ){\r
176                                         // dispatch 中に unlisten された要素の削除\r
177                                         unlistens = this._unlistens;\r
178                                         delete this._dispatching;\r
179                                         delete this._unlistens;                                 \r
180                                         \r
181                                         for( type in unlistens ){\r
182                                                 list = unlistens[ type ];\r
183                                                 for( i = list.length; i; ){\r
184                                                         this.unlisten( type, list[ --i ] );\r
185                                                 };\r
186                                                 unlistens.length = 0;\r
187                                         };\r
188                                         \r
189                                         if( this._killReserved ){\r
190                                                 this.kill();\r
191                                         } else\r
192                                         if( list = this._reserves ){\r
193                                                 for( i = 0, l = list.length; i < l; ++i ){\r
194                                                         f = list[ i ];\r
195                                                         X.EventDispatcher._once = f[ 4 ];\r
196                                                         this.listen( f[ 0 ], f[ 1 ], f[ 2 ], f[ 3 ] );\r
197                                                         X.EventDispatcher._once = false;\r
198                                                         f.length = 0;\r
199                                                 };\r
200                                                 list.length = 0;\r
201                                                 delete this._reserves;\r
202                                         };\r
203                                 };\r
204                                 \r
205                                 return ret;\r
206                         },\r
207                         \r
208                         onKill : function(){\r
209                                 if( this._dispatching ){\r
210                                         this._killReserved = true;\r
211                                         return false;\r
212                                 };\r
213                                 this._listeners && this.unlisten();\r
214                         },\r
215                         \r
216                         asyncDispatch : function( delay, e ){\r
217                                 return X.Timer.once( delay, this, this.dispatch, [ e ] );\r
218                         }\r
219                 }\r
220         );\r
221 \r
222 X.EventDispatcher._once = false;\r