OSDN Git Service

Build SIE 31 beta
[sie/sie.git] / org / sie-uncompressed.js
1 /*SIE under the MIT Lisence
2  */
3 /* Copyright 2016 dhrname and other contributors\r
4  * http://sie.osdn.jp/\r
5  * \r
6  * Permission is hereby granted, free of charge, to any person obtaining\r
7  * a copy of this software and associated documentation files (the\r
8  * "Software"), to deal in the Software without restriction, including\r
9  * without limitation the rights to use, copy, modify, merge, publish,\r
10  * distribute, sublicense, and/or sell copies of the Software, and to\r
11  * permit persons to whom the Software is furnished to do so, subject to\r
12  * the following conditions:\r
13  * \r
14  * The above copyright notice and this permission notice shall be\r
15  * included in all copies or substantial portions of the Software.\r
16  * \r
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
24  */\r
25 \r
26 \r
27 (function(){\r
28 \r
29 /*本体オブジェクト。base関数の裏に隠蔽されている*/\r
30 var _base = {\r
31     \r
32     /*_base.objはbase関数やupメソッドで呼び出されるオブジェクトの始祖となるオブジェクト*/\r
33     obj: {\r
34       /*upメソッド\r
35        * 自身をプロトタイプとして、新たにオブジェクトを生成する\r
36        */\r
37       up: function(name) {\r
38         var s = Object.create(this);\r
39         if (name) {\r
40           this[name] = s;\r
41           s.up = this.up;\r
42         } else {\r
43           /*既定値を$1としておく*/\r
44           this.$1 = s;\r
45         }\r
46         return s;\r
47       },\r
48     \r
49       /*mixメソッド\r
50        * 別のオブジェクトと合成ができるメソッド\r
51        */\r
52       mix: function(obj) {\r
53         if (!obj) {\r
54           throw new Error("No arguments error");\r
55         }\r
56         if (typeof obj !== "function") {\r
57           var alias = _base.__ng_;\r
58           for (var i in obj) {\r
59             if (!alias[i]) {\r
60               /*hasOwnPropertyメソッドを使わないのは、プロトタイプチェーンをたどるようにするため\r
61                *なお、Object.prototypeのプロパティなどは外した方がエラーがおきにくい\r
62                */\r
63               this[i] = obj[i];\r
64             }\r
65           }\r
66           i = alias = void 0;\r
67         } else {\r
68           obj.call(this, this);\r
69         }\r
70         return this;\r
71       },\r
72     \r
73       /*onメソッド\r
74        * メソッドの合成ができるメソッド。\r
75        * 指定した名前nameのメソッドが呼び出された場合、便乗して指定関数funcをメソッドとして実行することができる\r
76        */\r
77       on: function(name, func) {\r
78         if (!name) {\r
79           throw new Error("No arguments error");\r
80         } else if (/^(?:up|on|mix|of)$/.test(name)) {\r
81           throw new Error("Invalid method name error");\r
82         } else if (typeof func !== "function") {\r
83           throw new Error("Not support arguments type");\r
84         }\r
85         var tev = this._eventList__,\r
86             tn = this[name];\r
87         if (!this._eventList__) {\r
88           tev = this._eventList__ = [];\r
89         } else if (!this.hasOwnProperty("_eventList__")) { //祖先がすでにonメソッドを呼び出していれば\r
90           var s = [];\r
91           s._parent = tev;\r
92           tev = this._eventList__ = s;\r
93           s = void 0;\r
94         }\r
95         if (!this[name] || !tn.isOn) { //まだ、onメソッドが呼び出されていなければ\r
96           /*nameで指定されたメソッドの初期化*/\r
97           if (typeof tn === "function") {\r
98             /*nameで指定されたメソッドがすでにある場合は、配列tevの親をたどれないようにしておく*/\r
99             tev.push({\r
100                 name: name,\r
101                 func: tn\r
102               });\r
103             tev._parent = null;\r
104           }\r
105           this[name] = function() {\r
106             var te = this._eventList__,\r
107                  _name = name,    //スコープチェーンのエイリアス\r
108                  ts = null,\r
109                  s = null,\r
110                  tp,\r
111                  isCalled = false;//返り値の制御で使う\r
112             te._child = null;\r
113             while (tp = te._parent) { //親をさかのぼっていく\r
114               tp._child = te;\r
115               te = tp;\r
116             }\r
117             while (te) {            //子をたどっていく\r
118               /*最初の返り値の結果はsとして記録して、後で返す*/\r
119               for (var i=0, tli=te.length;i<tli;++i) {\r
120                 if(te[i].name === _name) {\r
121                   ts = te[i].func.apply(this, arguments);\r
122                   if (!isCalled) {\r
123                     s = ts;\r
124                     isCalled = true;\r
125                   }\r
126                 }\r
127               }\r
128               te = te._child;\r
129             }\r
130             te = ts = _name = isCalled = void 0;\r
131             return s;\r
132           };\r
133           this[name].isOn = true;\r
134         }\r
135         tev.push({\r
136                 name: name,\r
137                 func: func\r
138               });\r
139         tev = tn = func= void 0;\r
140         return this;\r
141       },\r
142 \r
143       /*__argsと__appプロパティは後のofメソッドで使う*/\r
144       __args: null,\r
145       __app: null,\r
146 \r
147       /*ofメソッド\r
148        * 指定されたオブジェクトのプロパティを遅延処理で実行できるメソッド\r
149        * 後述のcallメソッドと組み合わせて使う\r
150        */\r
151        of: function(obj) {\r
152         if (!obj) {\r
153           throw new Error("No arguments error");\r
154         } else if(this.hasOwnProperty("__of")) {\r
155           /*再代入禁止*/\r
156           throw new Error("Reset error");\r
157         }\r
158         /*__appと__argsプロパティに、指定されたプロパティを記録しておく*/\r
159         var args = this.__args || [];\r
160         for (var i in obj) {\r
161           if(obj.hasOwnProperty(i) && (i !== "call")) {\r
162             /*一度登録されたプロパティは二度書きしないようにする*/\r
163             args[i] || args.push(i);\r
164             args[i] = this[i] = obj[i];\r
165           }\r
166         }\r
167         obj.call && (this.__app = { call: obj.call });\r
168         this.__args = args;\r
169         this.__of = 1;\r
170         args = i = obj = void 0;\r
171         return this;\r
172        },\r
173 \r
174       /*callメソッド\r
175        * ofメソッドで指定されているオブジェクトのcallメソッドを実行できるメソッド\r
176        * そのさい、オブジェクトのプロパティとメソッドは、自動で実行展開される\r
177        */\r
178        call: function() {\r
179          if (!this.__app) { //ofメソッドが呼び出されていないか、callメソッドが一度も設定されていない場合\r
180            return this;\r
181          }\r
182          var args = this.__args,\r
183              call = this.call; //callメソッドの一時的なキャッシュ\r
184          /*循環参照を避けるためcallメソッドの入れ替え*/\r
185          this.call = callFunc;\r
186          for (var i=0, ali=args.length;i<ali;++i) {\r
187            /*callメソッドがあるオブジェクトは展開*/\r
188            var ai = args[i],\r
189                argi = this[ai];\r
190            if (argi && argi.call) {\r
191              this[ai] = argi.call(this);\r
192            }\r
193          }\r
194          this.call = call;\r
195          args = ai = argi = call = void 0;\r
196          return this.__app.call.apply(this, arguments);\r
197        },\r
198     }\r
199 };\r
200 /*callメソッドで使われる関数*/\r
201 var callFunc = function() { return this };\r
202 \r
203 /*base関数でキャッシュとして使うオブジェクト*/\r
204 var baseCache = {};\r
205 \r
206 base = function (name) {\r
207     var __base = _base,\r
208          _cache = baseCache; //エイリアス作成\r
209     if (!name) {\r
210       throw new Error("No arguments error");\r
211     } else if (_cache[name]) {\r
212       /*キャッシュに登録されている場合は、登録されたオブジェクトを返す*/\r
213       return _cache[name];\r
214     } else {\r
215       var s = Object.create(__base.obj);\r
216       this[name] = _cache[name] = s;\r
217       /*自身が値であるようなプロパティを設定する*/\r
218       s[name] = s;\r
219       return s;\r
220     }\r
221 };\r
222 \r
223 \r
224 /*mixメソッドで使うNGハッシュを作成*/\r
225 var hash = {},\r
226     proto = Object.prototype;\r
227 for (var i in proto) {\r
228   hash[i] = true;\r
229   /*上記のキャッシュについて、すべてのプロパティをnullかundefinedにしておく*/\r
230   baseCache[i] = null;\r
231 }\r
232 hash.constructor = false; //constructorはNGハッシュに追加しない\r
233 _base.__ng_ = hash;\r
234 hash = proto = void 0;\r
235 \r
236 /*base.free関数\r
237  *  即時関数の内部で作っていおいたオブジェクトを解放させるための関数\r
238  */\r
239 base.free = function() {\r
240   delete _base.obj;\r
241   _base = baseCache = callFunc = void 0;\r
242 };\r
243 \r
244 /*IE8などObject.createをサポートしていないブラウザ用*/\r
245 Object.create || (Object.create = function(obj) {\r
246   var F = function() {};\r
247   F.prototype = obj;\r
248   return new F()\r
249 } );\r
250 })();\r
251 /*SIE under the MIT Lisence\r
252  */\r
253 /* Copyright 2016 dhrname and other contributors\r
254  * http://sie.osdn.jp/\r
255  *\r
256  * Permission is hereby granted, free of charge, to any person obtaining\r
257  * a copy of this software and associated documentation files (the\r
258  * "Software"), to deal in the Software without restriction, including\r
259  * without limitation the rights to use, copy, modify, merge, publish,\r
260  * distribute, sublicense, and/or sell copies of the Software, and to\r
261  * permit persons to whom the Software is furnished to do so, subject to\r
262  * the following conditions:\r
263  * \r
264  * The above copyright notice and this permission notice shall be\r
265  * included in all copies or substantial portions of the Software.\r
266  * \r
267  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
268  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
269  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
270  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
271  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
272  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
273  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
274  */\r
275 \r
276 /*$frame オブジェクト\r
277  * 全体のフレームの管理を行う\r
278  */\r
279 base("$frame").mix ( {\r
280   /*フレームレート。1ミリ秒何フレームか。計算を省略するためミリ秒使用*/\r
281   fpms: 0.024,\r
282 \r
283   /*タイムラインのリスト (時間区間の設定ができる)*/\r
284   timelines: [],\r
285 \r
286   /*開始フレーム数の候補。アニメーションの開始条件となる\r
287    * 単位はフレーム数であって、秒数ではない\r
288    * なお、初期値については、開始フレームが負の値を考慮しているため*/\r
289   begin: -Number.MAX_VALUE,\r
290 \r
291   /*活動継続時間 (Active Duration)のフレーム数。アニメーションの継続条件となる\r
292    * 単位はフレーム数であって、秒数ではない*/\r
293   activeTime: Number.MAX_VALUE,\r
294   \r
295   /*現在のフレーム数*/\r
296   currentFrame: 0,\r
297   \r
298   /*アニメーションを開始させるメソッド*/\r
299   startAnimation: function() {\r
300     /*$getDocument.step関数は最後に書く*/\r
301     base("$getDocument").step();\r
302   },\r
303   \r
304   /*アニメーションが停止した状態かどうか、停止しているならばtrue*/\r
305   isPaused: false,\r
306 \r
307   /*アニメーションを停止させるメソッド*/\r
308   pauseAnimation: function() {\r
309     this.isPaused = true;\r
310   },\r
311   \r
312   /*後述のinitializeメソッドで使うオブジェクトリスト*/\r
313   objList: [],\r
314   \r
315   /*オブジェクトの初期化処理*/\r
316   initialize: function() {\r
317     var list = this.objList;\r
318     for (var i=0;i<list.length;++i) {\r
319       list[i].initialize();\r
320     }\r
321   },\r
322 \r
323   /*setFrame メソッド\r
324    * フレーム数を数値num まで進めるか、戻す*/\r
325   setFrame: function( /*number*/ num) {\r
326     this.currentFrame = num;\r
327     var timelines = this.timelines;\r
328     for (var i=0;i<timelines.length;++i) {\r
329       if (timelines[i] === this) {\r
330         /*自分自身が含まれていると、永久に再帰を繰り返して、「スタック領域が不足」してしまう*/\r
331         continue;\r
332       }\r
333       timelines[i].setFrame(num);\r
334     }\r
335   },\r
336   \r
337   /*addLineメソッド\r
338    * タイムラインを追加したあと、trueを返す\r
339    * ただし、引数objのobj.beginとobj.activeTimeが定まっていない場合はfalseを返す*/\r
340   addLine: function( /*$frame*/ obj ) {\r
341     if(!obj || (!obj.begin && (obj.begin !== 0))\r
342     || (!obj.activeTime && (obj.activeTime !== 0)) ) {\r
343       /*どちらのプロパティも未確認の場合、タイムラインは追加されない*/\r
344       return false;\r
345     }\r
346     var timelines = this.timelines;\r
347     if ( timelines.indexOf(obj) >= 0 ) {\r
348       this.removeLine(obj);\r
349     }\r
350     timelines.push(obj);\r
351     timelines = void 0;
352     return true;\r
353   },\r
354   \r
355   /*removeLine メソッド\r
356    * 指定されたタイムラインのオブジェクトを、リストから削除する*/\r
357   removeLine: function( /*$frame*/ timeline ) {\r
358     var list = this.timelines,\r
359         j = list.indexOf(timeline);\r
360     if (j > -1) {\r
361       list.splice(j, 1);      //Arrayのspliceを利用して、リストからtimelineを排除\r
362     }\r
363     list = j = void 0;\r
364   }\r
365 } ).mix( function($frame) {\r
366   $frame.up("$list").mix( {\r
367     /*終了時刻(単位フレーム数)のキャッシュとして使う*/\r
368     end: 0,\r
369     \r
370     /*開始時刻から終了時刻までのフレーム数\r
371      * これがactiveTimeより短ければ、活動継続時間とみなす*/\r
372     beginEnd: Number.MAX_VALUE,\r
373     \r
374     /*開始時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/\r
375     beginList: { \r
376       next: null,\r
377       value: Number.MAX_VALUE\r
378     },\r
379     \r
380     /*終了時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/\r
381     endList: {\r
382        next: null,\r
383        value: Number.MAX_VALUE\r
384     },\r
385     \r
386     addBeginList: function (num) {\r
387       return ( this.beginList = {\r
388                   value: num,\r
389                   next: this.beginList\r
390                 } );\r
391     },\r
392     \r
393     addEndList: function (num) {\r
394       return ( this.endList = {\r
395                   value: num,\r
396                   next: this.endList\r
397                 } );\r
398     },\r
399     \r
400     /*引数に渡された時刻リストの中から、現在フレームf以下の、最大値を求めるメソッド\r
401      * Number.MIN_VALUEの値を返したときはリストの中にf以下の値がないことを示す*/\r
402     getMaxList: function (f, list) {\r
403       var maxTime = -Number.MAX_VALUE; /*時刻の最大値*/\r
404       while( list ) {\r
405         var v = list.value;\r
406         /*f以下の開始リスト値のうち、最大値をmaxTimeに代入*/\r
407         if ( (v <= f)\r
408              && (maxTime <= v) ) {\r
409           maxTime = v;\r
410         }\r
411         list = list.next;\r
412       }\r
413       return maxTime;\r
414     },\r
415       \r
416     /*現在の要素の状態を数値で示す(マジックナンバーは後述の大文字プロパティを使う)*/\r
417     state: 0,\r
418     \r
419     /*アニメーション待機状態を示す定数*/\r
420     WAITING: 0,\r
421     \r
422     /*アニメーション開始状態を示す定数*/\r
423     BEGINNING: 1,\r
424     \r
425     /*アニメーション再生中の状態を示す定数*/\r
426     PLAYING: 2,\r
427     \r
428     /*アニメーション終了状態を示す定数*/\r
429     ENDING: 3,\r
430     \r
431     /*終了処理をした後の待機状態を示す定数*/\r
432     POSTWAITING: 4,\r
433     \r
434     /*初期化用メソッド*/\r
435     init: function() {\r
436       this.state = this.WAITING;\r
437       this.begin = 0;\r
438       return this;\r
439     },\r
440     \r
441     /*引数で指定されたフレーム数に応じて、stateプロパティを更新するメソッド*/\r
442     updateState: function( /*number*/ f) {\r
443       if (f === void 0) {\r
444         /*引数fが指定されないときには状態を更新しない*/\r
445         return this;\r
446       }\r
447       var state = this.state,\r
448           wait = /*this.WAITING*/ 0,\r
449           begin = /*this.BEGINNING*/ 1,\r
450           play = /*this.PLAYING*/ 2,\r
451           end = /*this.ENDING*/ 3,\r
452           post = /*this.POSTWAITING*/ 4;\r
453       /*beginListプロパティと、endListプロパティの中で、\r
454        * 現在フレーム数 f より大きい数値は、更新できる条件と無関係なので、除外しておく\r
455        * だから、f以下の値の中から、最大値を探して、\r
456        * それをbeginプロパティの値cacheBeginと比較する*/\r
457       var startTime = this.getMaxList(f, this.beginList);\r
458       if (startTime === -Number.MAX_VALUE) {\r
459         (state > post) && (this.state = begin);\r
460         return this;\r
461       }\r
462       var endTime = this.getMaxList(f, this.endList),\r
463           isWait = (state === wait),\r
464           cacheBegin = this.begin;\r
465       if ( !startTime && isWait) {\r
466           /*開始時刻が0ならば、アニメーションを開始\r
467            * ただし、アニメが終了した後のPOSTWAITING状態の時には作動させない*/\r
468           this.begin = 0;\r
469           this.state = begin;\r
470       } else if ( isWait || (state === post) ) {\r
471         if (startTime > cacheBegin) {\r
472           this.state = begin;\r
473           /*beginプロパティに開始時刻をキャッシュ用に保存*/\r
474           this.begin = startTime;\r
475         }\r
476       } else if (state === begin) {\r
477         if (endTime >= cacheBegin) {\r
478           /*終了時刻にもう到達したときは、直接BEGINNING状態からENDING状態へ移行*/\r
479           this.state = end;\r
480           /*endTimeを終了時刻とみなす*/\r
481           (endTime > 0) && (this.end = endTime);\r
482           /*activeTimeプロパティは、begin属性とend属性が反映されていないため、\r
483            * beginEndプロパティに別に設定しておく*/\r
484           this.beginEnd = 0;\r
485         } else {\r
486           this.state = play;\r
487         }\r
488       } else if (state === play) {\r
489         /*activeTimeプロパティを比較して、変数endTimeを書き換える*/\r
490         var act = cacheBegin + this.activeTime;\r
491         endTime = (endTime > act) ? act\r
492                                   : endTime;\r
493         if ( (f >= act) || (endTime >= cacheBegin) || (startTime > cacheBegin) ) {\r
494           /*終了時刻に到達したか、再び開始イベントが発火されたとき*/\r
495           this.state = end;\r
496           if (endTime > 0) {\r
497             /*endTimeを終了時刻とみなす*/\r
498             this.end = endTime;\r
499             /*activeTimeプロパティは、begin属性とend属性が反映されていないため、\r
500              * beginEndプロパティに別に設定しておく*/\r
501             this.beginEnd = endTime - cacheBegin;\r
502           }\r
503         }\r
504       } else if (state === end) {\r
505         if (startTime > cacheBegin) {\r
506           /*再生中に開始イベントが発火されて、終了状態となったとき*/\r
507           this.state = begin;\r
508           this.begin = startTime;\r
509         } else {\r
510           this.state = post;\r
511         }\r
512       } else {\r
513         this.state = begin;\r
514       }\r
515       cacheBegin = startTime = endTime = isWait = state = void 0;\r
516       return this;\r
517     },\r
518     \r
519     /*addEventメソッドで使われるイベントリスト(開始時に登録されたリスナー関数が呼び出される)*/\r
520     _beginListenerList: [],\r
521     \r
522     /*addEventメソッドで使われるイベントリスト(終了時に登録されたリスナー関数が呼び出される)*/\r
523     _endListenerList: [],\r
524     \r
525     /*addEventメソッドで使われるイベントリスト(再生時に登録されたリスナー関数が呼び出される)*/\r
526     _playListenerList: [],\r
527     \r
528     /*開始と再生と終了時に発火されるイベントリスナーを登録するメソッド*/\r
529     addEvent: function ( /*string*/ eventName, /*fnction*/ listener) {\r
530       var evtName = "_" +eventName+ "ListenerList";\r
531       /*プロトタイプ継承していた場合は新しく配列を作成*/\r
532       if (!this.hasOwnProperty(evtName)) {\r
533         this[evtName] = [];\r
534       }\r
535       this[evtName].push(listener);\r
536     },\r
537     \r
538     /*入力されたフレーム数fの場面に切り替えるメソッド*/\r
539     setFrame: function( /*number*/ f) {\r
540       this.currentFrame = f;\r
541       var state = this.updateState(f).state;\r
542       /*アニメーション開始と再生と、終了状態のときに、beginとplayとendイベントを呼び出しておいて、\r
543        * 次の状態(再生状態)に遷移する*/\r
544       if (state === /*this.PLAYING*/ 2) {\r
545         var list = this._playListenerList;\r
546         for (var i=0;i<list.length;++i) {\r
547           list[i](this);\r
548         }\r
549       } else if (state === /*this.BEGINNING*/ 1) {\r
550         list = this._beginListenerList;\r
551         for (var i=0;i<list.length;++i) {\r
552           list[i](this);\r
553         }\r
554         /*開始時刻と終了時刻が一致した場合はstateはENDING状態\r
555          * それ以外はPLAYING状態*/\r
556         state = this.updateState(f).state;\r
557       }\r
558       if (state === /*this.ENDING*/ 3) {\r
559         list = this._endListenerList;\r
560         for (var i=0;i<list.length;++i) {\r
561           list[i](this);\r
562         }\r
563         if (this.updateState(f).state === /*this.BEGINNING*/ 1) {\r
564           /*再生中にbeginイベントが呼び出された場合*/\r
565           this.updateState(f);\r
566         }\r
567       }\r
568       state = list = void 0;\r
569     }\r
570   } ).mix( function() {\r
571     /*後述の$beginや$endで使うメソッド*/\r
572     this.addList = this.addBeginList;\r
573   } );\r
574    \r
575   /*$begin オブジェクト\r
576    * 開始のタイミングを計算する*/\r
577   $frame.up("$begin").mix( {\r
578 \r
579     /*開始時刻やタイミングが書かれた文字列*/\r
580     string: "",\r
581 \r
582     /*イベントやindefinteで未解決かどうか*/\r
583     isResolved: false,\r
584     \r
585     /*イベントが適用される要素*/\r
586     eventTarget: document.documentElement,\r
587     \r
588     /*現在のフレーム数を改めて初期化*/\r
589     currentFrame: 0,\r
590     \r
591     /*イベント同期で使う時間差のフレーム数*/\r
592     eventOffset: 0,\r
593     \r
594     /*repeat(1)など文字列内に書かれたリピート回数*/\r
595     repeat: 0,\r
596     \r
597     /*accessKey(a)の"a"などキーイベントの対象となる文字列*/\r
598     accessKey: "",\r
599 \r
600     /*trim メソッド\r
601      * 文字列中の空白を除去*/\r
602     trim: function(str) {\r
603       /*strがString型以外のときは必ずエラーを出す*/\r
604       return str.replace(/[\s\n]+/g, "");\r
605     },\r
606 \r
607     /*offset メソッド\r
608      * 引数に渡された文字列から、ミリ秒単位に変換した時間を、解析して返す*/\r
609     offset: function(str) {\r
610       str = str || "0";\r
611       var plusminus = str.charAt(0),\r
612           /*parseFloatのエイリアス*/\r
613           _float = parseFloat,\r
614           s = _float( str.match(/[\d.]+ms$/) || "0") + sec() + min() + h();\r
615       if (plusminus === "-") {\r
616         s *= -1;\r
617       }\r
618       plusminus = _float = sec = min = h = void 0;\r
619       return s;\r
620       \r
621       /*00:00:0と00:0と、0sなどの文字列をミリ秒へ変換*/\r
622       function sec() {\r
623         return str2num( 1000, /[\d.]+s$/, /[\d.]+$/ );\r
624       };\r
625       function min() {\r
626         return str2num( 60000, /[\d.]+min$/, /\d\d:[^:]+$/ );\r
627       };\r
628       function h() {\r
629         return str2num( 3600000, /\d+:\d\d:/, /[\d.]+h$/ );\r
630       };\r
631       function str2num(s, /*RegExp*/ a, /*RegExp*/ b) {\r
632         return s*( _float(str.match(a) || "0") || _float(str.match(b) || "0") );\r
633       };\r
634     },\r
635 \r
636     /*event メソッド\r
637      * 引数の文字列から、idとイベントに相当する文字列のプロパティを持ったオブジェクトを返す\r
638      * idがない場合や、イベントがない場合は空文字列を該当のプロパティに入れる*/\r
639     event: function(str) {\r
640       str = str || "";\r
641       if (/[\+\-]/.test(str)) {\r
642         /*数値がある場合は切り取っておく*/\r
643         str = str.slice(0, str.search(/[\+\-]/));\r
644       }\r
645       if (str.indexOf(".") > -1) {\r
646         /*ドットが見つかった場合、IDとイベントに分けておく*/\r
647         var ide = str.split(".");\r
648         /* エラーが起きて、idが空文字列ならば、evtも空文字列。逆も然り*/\r
649         return {\r
650           id: (ide[1] && ide[0]),\r
651           event: (ide[0] && ide[1])\r
652         };\r
653       } else {\r
654         return {\r
655           id: "",\r
656           event: str\r
657         };\r
658       }\r
659     }, \r
660     \r
661     /*_parse メソッド\r
662      * 引数の文字列を解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力\r
663      * また、イベントリスナーに登録をしておく*/\r
664     _parse: function (str) {\r
665       var plusminus = str.search(/[\+\-]/),\r
666           event = null,\r
667           ele,\r
668           /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/\r
669           endList = this.$list.addEndList(Number.MAX_VALUE);\r
670       if (str === "indefinite") {\r
671         this.begin = Number.MAX_VALUE;\r
672       } else if (plusminus > 0) {\r
673         /*Event-Value +/- Clock-Value の場合*/\r
674         this.begin = this.offset( str.slice(plusminus) );\r
675         event = this.event(str);\r
676       } else if ( /[^\+\-\d]/.test(str.charAt(0)) ) {\r
677         /*Event-Valuen のみの場合*/\r
678         event = this.event(str);\r
679       } else {\r
680         /*+/- Clock-Value のみの場合*/\r
681         this.begin = this.offset( str );\r
682         /*イベントもindefiniteもないので、解決済みと考える*/\r
683         this.isResolved = true;\r
684       }\r
685       /*もしもあれば、リピートの回数を求める*/\r
686       this.repeat = /repeat\((\d+)\)/.test(str) ? +RegExp.$1 : 0;\r
687       /*もしもあれば、押されるはずのキーを求める*/\r
688       this.accessKey = /accessKey\(([^\)]+?)\)/.test(str) ? RegExp.$1 : "";\r
689       this.begin = Math.floor( this.begin * this.fpms);\r
690       if (str === "indefinite") {\r
691         /*begin属性の値がindefiniteの場合は、何もしない。\r
692          * 開始時刻はbeginElementメソッドに依存*/\r
693       } else if (event) {\r
694         ele = event.id ? this.eventTarget.ownerDocument.getElementById(event.id)\r
695                         : this.eventTarget;\r
696         /*イベントの時間差を設定しておく\r
697          * eventOffsetとobjListの変数はクロージャとしてlistener関数で使われる*/\r
698         var eventOffset = this.begin,\r
699             /*objListのvalueプロパティはあとで書き換えられる(イベントの場合のみ)*/\r
700             objList = this.$list.addList(Number.MAX_VALUE),\r
701         /*イベントのリスナーとして使う*/\r
702             listener = function(evt) {\r
703               objList.value = this.begin = eventOffset + base("$frame").currentFrame;\r
704               this.isResolved = true;\r
705             };\r
706         this.eventOffset = eventOffset;\r
707         if (this.repeat > 0) {\r
708           ele && ele.addEventListener("repeatEvent", (function(evt) {\r
709             if (evt.detail === this.repeat) {\r
710               listener.call(this, evt);\r
711             } }).bind(this), true);\r
712         } else if (this.accessKey) {\r
713           document.documentElement.addEventListener("keydown", (function(evt) {\r
714             if (evt.char === this.accessKey) {\r
715                 listener.call(this, evt);\r
716               } }).bind(this), false);\r
717         } else {\r
718           var evtName = /^(?:begin|end|repeat)$/.test(event.event) ? event.event + "Event"\r
719                           : event.event;\r
720           ele && ele.addEventListener(evtName, listener.bind(this), false);\r
721         }\r
722       } else {\r
723         /*開始リストに登録しておく($endの場合は終了リストに登録)*/\r
724         this.$list.addList(this.begin);\r
725       }\r
726       s = event = str = plusminus = ele = void 0;\r
727     },\r
728     \r
729     /*stringプロパティを解析して、\r
730      * 開始フレーム数の算出や、イベントリスナーの登録をするメソッド*/\r
731     parse: function() {\r
732       /*初期値を設定*/\r
733       this.begin = 0;\r
734       this.isResolved = false;\r
735       var str = this.trim(this.string);\r
736       if (str.indexOf(";") > -1){\r
737         /*;で区切られたリストを一つずつ解析*/\r
738         var list = str.split(";");\r
739         for (var i=0;i<list.length;++i) {\r
740           this._parse(list[i]);\r
741         }\r
742       } else {\r
743         this._parse(str);\r
744       }\r
745       s = str = void 0;\r
746       return this;\r
747     },\r
748     \r
749     /*$listと$activateオブジェクトを更新して、活動継続時間を求めるメソッド*/\r
750     updateList: function() {\r
751       this.$list = this.$list.up();\r
752       /*beginとend属性を考慮に入れないで、活動継続時間を求める*/\r
753       var s = ( this.$activate = this.$activate.up() );\r
754       /*$endオブジェクトに付属している$listプロパティを更新したものと一致させておく*/\r
755       s.end && (s.end.$list = this.$list);\r
756       this.activeTime = this.$list.activeTime = s.call() || Number.MAX_VALUE;\r
757       this.simpleDuration = s.simpleDur;\r
758       return this;\r
759     }\r
760     \r
761   /*$activate オブジェクト\r
762    * 活動継続時間などを計算するための計算実体\r
763    * $begin オブジェクトからの継承*/\r
764   } ).up("$activate").of( {\r
765     \r
766     /*単純継続時間のパースされる前の文字列*/\r
767     dur: "indefinite",\r
768     \r
769     /*活動をストップさせるためのオブジェクト*/\r
770     end: $frame.$begin.up("$end"),\r
771 \r
772     /*リピート回数*/\r
773     repeatCount: null,\r
774     \r
775     /*繰り返し時間*/\r
776     repeatDur: null,\r
777 \r
778     /*単純継続時間 (単位はフレーム数)*/\r
779     simpleDur: function() {\r
780       return ( (this.dur === "indefinite") || !this.dur ) ?\r
781                 null\r
782               : Math.floor(this.offset(this.dur) * this.fpms) ;\r
783     },\r
784 \r
785     /*最小値に制限される\r
786      * 最小値 <= 活動継続時間 とならなければならない*/\r
787      min: "0",\r
788      \r
789     /*最大値に制限される\r
790      * 活動継続時間 <= 最大値 とならなければならない*/\r
791      max: "indefinite",\r
792     \r
793     /*解決した(計算する)ときの時間*/\r
794     resolvedTime: function() {\r
795       return Date.now();\r
796     },\r
797     \r
798     /*関数型の呼び出しメソッド\r
799      * base.jsのofメソッドを活用して活動継続時間を算出\r
800      * ただし、begin属性とend属性については、別途、$frame.$listで定める\r
801      * 計算方法はSMILアニメーション 3.3.4節を参照\r
802      * http://www.w3.org/TR/smil-animation/#ComputingActiveDur\r
803      */\r
804     call: function() {\r
805       var ind = "indefinite",\r
806           dur = this.simpleDur,\r
807           isIndefRepeatCount = (this.repeatCount === ind),\r
808           isIndefRepeatDur = (this.repeatDur === ind),\r
809           isDur = dur || (dur === 0),\r
810           isRepeatCount = this.repeatCount || (this.repeatCount === 0),\r
811           isRepeatDur = this.repeatDur || (this.repeatDur === 0),\r
812           actList = [],\r
813           min = Math.floor(this.offset(this.min) * this.fpms),\r
814           max = (this.max === ind) ? null : Math.floor(this.offset(this.max) * this.fpms),\r
815           s;\r
816       if (indef()) {\r
817         /*注意点として、活動継続時間が不定かつ、min属性とmax属性が指定されたときの記述が仕様にないため、\r
818          * W3Cのテストスイート(animate-elem-66-t.svg)に従い、max属性の値を返す*/\r
819         if (max) {\r
820           return max;\r
821         }\r
822         return null;\r
823       }\r
824       if (isDur && this.repeatCount && !isIndefRepeatCount) {\r
825         actList.push( dur * this.repeatCount );\r
826       }\r
827       if (isRepeatDur && !isIndefRepeatDur) {\r
828         actList.push( Math.floor( this.offset(this.repeatDur) * this.fpms) );\r
829       }\r
830       if (isDur && !isRepeatCount && !isRepeatDur) {\r
831         /*repeatCountやrepeatDur属性が指定されていない場合*/\r
832         actList.push( dur );\r
833       }\r
834 \r
835       /*長くなるため、インライン関数を活用\r
836        * indef関数はbeginとend属性を考慮せずに、\r
837        * 活動継続時間が不定かどうか、もし、不定なら真を返す*/\r
838       function indef() {\r
839         return !!( (!isDur && !isRepeatDur)\r
840                    || (isIndefRepeatCount && !isRepeatDur)\r
841                    || (isIndefRepeatDur && !isRepeatCount)\r
842                    || (isIndefRepeatCount && isIndefRepeatDur)\r
843                  );\r
844       };\r
845       \r
846       ind = dur = isIndefRepeatCount = isIndefRepeatDurindef = isDur = isEnd = isRepeatDur = isRepeatCount = indef = void 0;\r
847 \r
848       if (actList.length === 1) {\r
849         s = actList[0];\r
850       } else if(actList.length > 1) {\r
851         /*属性が競合するときは、最小値をとること (SMILアニメーション 3.3.4節)*/\r
852         s = Math.min.apply(Math, actList);\r
853       } else {\r
854         return null;\r
855       }\r
856       if ( max && (min > max) ) {\r
857         return s;\r
858       }\r
859       min && (min > s) && (s = min);\r
860       max && (max < s) && (s = max);\r
861       return s;\r
862     }\r
863   } );\r
864   $frame.$begin.$end.of( {\r
865     call: function() {\r
866       if (!this.string) {\r
867         return null;\r
868       }\r
869       /*addListメソッドには、addBeginList関数が入っているはずなので、それを一時的に変更する*/\r
870       this.$list.addList = this.$list.addEndList;\r
871       this.parse(this.string);\r
872       this.$list.addList = this.$list.addBeginList;\r
873       return this.isResolved ? this.begin\r
874                              : "indefinite";\r
875     }\r
876   } ).mix( {\r
877     $list: $frame.$begin.$list.up()\r
878   } );\r
879 } );\r
880 /*$from オブジェクト\r
881  * 呈示値 (presentation value)の計算をする。値そのものを返すための計算実体*/\r
882 base("$from").of( {\r
883   /*呈示値が書かれた文字列*/\r
884   string: "",\r
885   \r
886   /*呈示値の数値の部分だけを抜き出した配列を返す*/\r
887   numList: function() {\r
888     var s  = this.string.match(/[\-\+]?[\d\.]+(?:[eE][\-\+]?[\d\.]+)?/g)\r
889              || [];\r
890     if (s) {\r
891       /*mapメソッドで代用してもよい*/\r
892       for (var i=0;i<s.length;++i) {\r
893         s[i] = parseFloat(s[i]);\r
894       }\r
895     }\r
896     return s;\r
897   },\r
898   \r
899   /*呈示値の文字部分だけを抜き出した配列を返す*/\r
900   strList: function() {\r
901     /*replaceメソッドで1E-10などの対策*/\r
902     return this.string.replace(/\d[eE][\-\+\d]/g, "")\r
903                       .match(/[^\d\-\+\.]+/g);\r
904   },\r
905   \r
906   from: base("$from").up().mix( {\r
907           from: null\r
908         } ),\r
909   \r
910   /*$toオブジェクトにこのオブジェクトを適用させる関数*/\r
911   call: function() {\r
912     /*advanceメソッドで使うために、stringを保持*/\r
913     this.numList.string = this.string;\r
914     if (this.numList.length\r
915           && (this.additive[0] === 0)\r
916           && (this.accumulate[0] === 0)\r
917           ) {\r
918       /*配列の項目がundefinedだと困るので、配列を初期化する*/\r
919       var additive = [],\r
920           accumulate = [];\r
921       for (var i=0;i<this.numList.length;++i) {\r
922         /*0で配列を初期化しておく*/\r
923         additive[i] = accumulate[i] = 0;\r
924       }\r
925       this.additive = additive;\r
926       this.accumulate = accumulate;\r
927     }\r
928     /*文字部分の配置パターンは4通りあるので、ここでstrListを処理\r
929      * (1) a 0 の場合\r
930      * (2) 0 a\r
931      * (3) a 0 a (ノーマルパターン)\r
932      * (4) 0 a 0\r
933      * これらのパターンのうち、(1)(2)(4)を(3)のパターンに統一したのが以下の処理*/\r
934     /*文字列が1aのように、数値で始まるかどうか。始まったら真*/\r
935     if (!this.string || !this.numList.length || !this.strList) {\r
936       return this.numList;\r
937     }\r
938     var isNormal = (this.numList.length < this.strList.length);\r
939     if (/^[\-\+]?[\d\.]/.test(this.string) && !isNormal) {\r
940       /*文字列が1aのように、数値で始まる場合*/\r
941       this.strList.unshift("");\r
942     }\r
943     if (/\d$/.test(this.string) && !isNormal) {\r
944       /*文字列がa1のように、数値で終わる場合*/\r
945       this.strList.push("");\r
946     }\r
947     return this.numList;\r
948   }\r
949     \r
950 } )\r
951  .mix( {\r
952    /*advanceメソッドの返り値で使われる小数点以下の桁数*/\r
953    degit: 0,\r
954    \r
955    /*additve属性やaccumulate属性が設定された、累積アニメーションか、加法アニメーションで使われる*/\r
956    additive: [0],\r
957    accumulate: [0],\r
958    \r
959    /*advance メソッド\r
960     * アニメーションの進行具合を示す進捗率 t (0 <= t <= 1)をもとに、現在の呈示値を算出するためのもの\r
961     * callメソッドが前もって呼び出されていることが前提となる*/\r
962     advance: function(t) {\r
963       if ( (t < 0) || (1 < t)) {\r
964         throw new Error("An Invalid Number Error");\r
965       }\r
966       if (!this.string) {\r
967         return "";\r
968       } else if (!this.from.length) {\r
969         /*discreteのために、this.stringに数値が入っていない場合の対処*/\r
970         if (t === 1) {\r
971           return this.string.trim();\r
972         }\r
973         return this.from.string.trim();\r
974       }\r
975       var str = "",\r
976           numList = this.numList,\r
977           strList = this.strList,\r
978           fromNumList = this.from,\r
979           deg = this.degit,\r
980           additive = this.additive,\r
981           accumulate = this.accumulate;\r
982       for (var i=0,nuli=numList.length;i<nuli;++i) {\r
983         /*原点Oを(0,0,...0)とおく\r
984          *$fromと$toを、原点Oからの二つのベクトル (n次空間のベクトル)、ベクトルOFとベクトルOTと考える\r
985          *$fromと$toの二つの端の点FとTを結ぶ線分を、t : 1-t で内分する点をPとおく\r
986          * このときのベクトルOPを求めたのが以下の式*/\r
987         str += ( t * numList[i] + (1 - t) * fromNumList[i] + additive[i] + accumulate[i]).toFixed(deg);\r
988         strList && ( str += strList[i+1] );\r
989       }\r
990       /*文字列はcallメソッドにより、a0aのパターンになっているので、aの部分を追加*/\r
991       str = (strList ? strList[0] : "") + str;\r
992       numList = strList = fromNumList = i = nuli = deg = additive = accumulate = void 0;\r
993       return str.trim();\r
994     },\r
995     \r
996     /*distanceメソッド\r
997      * fromベクトルから自分自身のベクトルへの距離 (ノルム)の数値を返す。callメソッドを使うので注意すること*/\r
998      distance: function(from) {\r
999        if (!from) {\r
1000           return 0;\r
1001        }\r
1002        var toList = this.call(),\r
1003            fromList = from.call ? from.call() : from,\r
1004            s = 0;\r
1005        if (!toList || !fromList) {\r
1006          return 0;\r
1007        }\r
1008        for (var i=0, tli=toList.length; i<tli; ++i) {\r
1009          s += (toList[i] - fromList[i])*(toList[i] - fromList[i]);\r
1010        }\r
1011        return Math.sqrt(s);\r
1012      },\r
1013      \r
1014      /*setAdditive メソッド\r
1015       * additve属性がsumのときに使われるメソッド\r
1016       * 引数は親要素の、現在の属性値*/\r
1017       setAdditive: function(str) {\r
1018         if (!str) {\r
1019           return 0;\r
1020         }\r
1021         var from = this.$from.up();\r
1022         from.string = str;\r
1023         return ( this.additive = from.call() );\r
1024       },\r
1025      \r
1026      /*setAccumulate メソッド\r
1027       * accumulate属性がsumのときに使われるメソッド\r
1028       * 引数は現在のリピート回数*/\r
1029       setAccumulate: function(num) {\r
1030         if (!num || isNaN(num)) {\r
1031           return 0;\r
1032         }\r
1033         return ( this.accumulate = this.numList.map( function(d) {\r
1034           return d * num;\r
1035         } ) );\r
1036       }\r
1037   } )\r
1038   /*fromプロパティの初期化*/\r
1039  .up("$to").from = null;\r
1040  \r
1041  /*計算モードを定めるための計算実体\r
1042   *補間の細かい制御などを行う*/\r
1043  base("$calcMode").mix({\r
1044    /*計算モード (calcMode属性の値)*/\r
1045    mode: "linear",\r
1046 \r
1047    /*keyTimesの区間\r
1048     * たとえば、"0, 0.5, 0.7, 1"の場合、時間の区間はそれぞれ、0.5 (=0.5-0)  0.2 (=0.7-0.5)  0.3 (=1-0.7)である\r
1049     * このうち、どれか一つが値として入力される*/\r
1050    keyTime: 1,\r
1051    \r
1052    /*keySpline属性の値を設定*/\r
1053    keySplines: null,\r
1054 \r
1055    /*callメソッドで使う関数\r
1056     * 進捗率を時間の圧縮率( = keyTimeプロパティ)で割ることで、現在、どこまで進んでいるのかを求めることができる*/\r
1057    _f: function (t) {\r
1058          /*tは進捗率*/\r
1059          var tkey = this.keyTime;\r
1060          if ( (tkey === 0) && t) {\r
1061            t = 0; \r
1062          } else if (!tkey || !isFinite(tkey) ) {\r
1063            return this.string;\r
1064          } else {\r
1065            t = t / tkey;\r
1066            t = (t > 1) ? Math.floor(t) : t;\r
1067          }\r
1068          tkey = void 0;\r
1069          return isNaN(t) ? this.string\r
1070                          : this.to.advance(t);\r
1071      },\r
1072      \r
1073      /*discreteモードのときには、上記の_f関数の代わりに、以下の関数を用いる*/\r
1074      funcForDiscrete: function (t) {\r
1075         if (isNaN(t)) {\r
1076           return this.string;\r
1077         } else if (t === 1) {\r
1078           return this.to.string;\r
1079         } else {\r
1080           return this.to.advance(0);\r
1081         }\r
1082       }\r
1083  }).of( {\r
1084    \r
1085    /*全体の行列ノルム(距離)*/\r
1086    norm: 1,\r
1087 \r
1088    /*無効だった場合の呈示値*/\r
1089    string: "",\r
1090    \r
1091    /*与えられたアニメーションの進捗率を使った時間の圧縮率を計算して呈示値を返すための関数を作る*/\r
1092    call: function() {\r
1093      var f = this._f.bind(this);\r
1094      if (this.mode === "linear") {\r
1095        this.to.call();\r
1096        return f;\r
1097      } else if (this.mode === "paced") {\r
1098        /*keyTimes属性は無視され、ベクトルの距離の割合から計算される*/\r
1099        this.keyTime = this.to.distance(this.to.from) / this.norm;\r
1100        return f;\r
1101      } else if (this.mode === "spline") {\r
1102        var tk = this.keySplines,\r
1103            /*必ず関数を返すようにするため、円周率を返す関数tfを返して、nullの代わりとする*/\r
1104            tf = function(x) {\r
1105                  return Math.PI;\r
1106            };\r
1107       tf.isNotAnimate = true;\r
1108       if (!tk) {\r
1109          return tf;\r
1110        }\r
1111       for (var i=0,tki = NaN;i<tk.length;++i) {\r
1112        tki = tk[i];\r
1113        if (isNaN(tki)) {\r
1114          return tf;\r
1115        }\r
1116        if ( (tki < 0) || (1 < tki)) {\r
1117          return tf;\r
1118        }\r
1119      }\r
1120      this.to.call();\r
1121      var x2 = tk[0],\r
1122          y2 = tk[1],\r
1123          x3 = tk[2],\r
1124          y3 = tk[3],\r
1125          x4 = 1,\r
1126          y4 = 1,\r
1127          Ax = x4-3*(x3-x2),\r
1128          Bx = 3*(x3-2*x2),\r
1129          Cx = 3*x2,\r
1130          Ay = y4-3*(y3-y2),\r
1131          By = 3*(y3-2*y2),\r
1132          Cy = 3*y2,\r
1133          _newton = Math.qubicnewton; //高速化のためのエイリアス\r
1134      if ( ( (x2 === 0) || (x2 === 1) )\r
1135           && (y2 === 0)\r
1136           && ( (x3 === 1) || (x3 === 0) )\r
1137           && (y3 === 1) ) {\r
1138             /*linearモードと同じ効果 (収束ではない可能性を考慮)*/\r
1139             this.to.call();\r
1140             return f;\r
1141      }\r
1142      var tkey = this.keyTime;\r
1143      if (tkey || isFinite(tkey) ) {\r
1144        /*keyTimeから時間の収縮率を3次ベジェ曲線に適用しておく*/\r
1145        Ax *= tkey;\r
1146        Bx *= tkey;\r
1147        Cx *= tkey;\r
1148        Ay *= tkey;\r
1149        By *= tkey;\r
1150        Cy *= tkey;\r
1151      }\r
1152      tkey = tk = x2 = y2 = x3 = y3 = x4 = y4 = void 0;\r
1153      return function (x) {\r
1154         /*3次ベジェ曲線は媒介曲線\r
1155          *x = (x4-3*(x3-x2)-x1)*t*t*t + 3*(x3-2*x2+x1)*t*t + 3*(x2-x1)*t + x1\r
1156          *y = (y4-3*(y3-y2)-y1)*t*t*t + 3*(y3-2*y2+y1)*t*t + 3*(y2-y1)*t + y1\r
1157          * ただし、0 <= t <= 1\r
1158          * スプラインモードの場合、x1 = y1 = 0, x4 = y4 = 1\r
1159          * ベジェ曲線のxの式が三次方程式であるため、その解 t から、ベジェ曲線の y を求める\r
1160          * なお、ニュートン法の初期値はxとする\r
1161          * なぜなら、xの式をみると、xが増加傾向となるスプラインモードでは、係数が負となる可能性が低いため*/\r
1162         var t = _newton(Ax, Bx, Cx, -x, x);\r
1163         return f(Ay*t*t*t + By*t*t + Cy*t);\r
1164       };\r
1165     } else if (this.mode === "discrete") {\r
1166       this.to.call();\r
1167       return this.funcForDiscrete.bind(this);\r
1168     }\r
1169   }\r
1170 } ).to = base("$from").$to;\r
1171 \r
1172 \r
1173 /*ニュートン法により、三次方程式 a0x^3 + a1x^2 + a2x + a3 の解を求める\r
1174  * 引数bは初期値*/\r
1175 Math.qubicnewton = function(a0, a1, a2, a3, b) {\r
1176   var eps = 1e-15,                          //収束誤差\r
1177       fb = a0 *b*b*b + a1 *b*b + a2*b + a3; //方程式の結果\r
1178   if (fb === 0) {\r
1179     return b;\r
1180   }\r
1181   /*限界の収束回数は100回*/\r
1182   for (var i=0;i<100;i=(i+1)|0) {\r
1183     /*数値nは与えられた三次方程式を微分したもの*/\r
1184     var n = 3* a0 *b*b + 2 * a1 *b + a2;\r
1185     if (!n || ( (fb < eps) && (fb > -eps) )) {\r
1186       fb = eps = void 0;\r
1187       return b;\r
1188     } else {\r
1189       /*以下は収束の漸化式*/\r
1190       b =  b - fb / n;\r
1191       fb = a0 *b*b*b + a1 *b*b + a2*b + a3;\r
1192     }\r
1193   }\r
1194   return b; //収束しなかった結果\r
1195 };\r
1196 \r
1197 /*$attribute オブジェクト\r
1198  * アニメーションの時間調整と、呈示値の調整を一つのオブジェクトにまとめて行うことで、\r
1199  * アニメーションサンドイッチの実装をする\r
1200  * $calcModeオブジェクトから継承*/\r
1201 base("$calcMode").up("$attribute").mix( {\r
1202   \r
1203   /*アニメーションの対象となる要素。たとえば、animate要素の親要素*/\r
1204   element: null,\r
1205   \r
1206   /*$fromオブジェクトを作るためのひな形となるオブジェクト*/\r
1207   $from: base("$from").up(),\r
1208   \r
1209   /*attributeName属性の値*/\r
1210   attrName: "",\r
1211   \r
1212   /*属性の名前空間*/\r
1213   attrNameSpace: null,\r
1214   \r
1215     /*指定された属性の規定値*/\r
1216   defaultValue: "",\r
1217   \r
1218   /*もともと属性がターゲットの要素につけられていたかどうか*/\r
1219   isDefault: false,\r
1220   \r
1221   /*attributeType属性がCSSだったときや、autoで属性名がCSSプロパティ名だったときには、true*/\r
1222   isCSS: false,\r
1223   \r
1224   /*計算モードdicreteやsplineなど*/\r
1225   mode: "linear",\r
1226   \r
1227   /*指定した要素の属性値を取得するメソッド*/\r
1228   getAttr: function(/*string*/ name, def) {\r
1229     var nameSpace = null;\r
1230     if (name.indexOf("xlink:") > -1) {\r
1231       nameSpace = "http://www.w3.org/1999/xlink";\r
1232     }\r
1233     var s = this._ele.getAttributeNS(nameSpace, name);\r
1234     if (this.element) {\r
1235         var view = this.element.ownerDocument.defaultView;\r
1236       if (s === "inherit") {\r
1237         return view.getComputedStyle(this.element.parentNode, "").getPropertyValue(this.attrName);\r
1238       } else if (s === "currentColor") {\r
1239         return view.getComputedStyle(this._ele, "").getPropertyValue("color");\r
1240       }\r
1241     }\r
1242     /*DOM Level2やIE11では、getAttributeNSメソッドは空文字列を返す。他のブラウザではnullを返すことが多い\r
1243      * \r
1244      * >the empty string if that attribute does not have a specified or default value\r
1245      * http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-ElGetAttrNS*/\r
1246     return (s || def);\r
1247   },\r
1248   \r
1249   _ele: document.documentElement,\r
1250   \r
1251   /*指定された要素にvalues属性が付いているかどうかのチェックできるメソッド*/\r
1252   hasAttrValues: function () {\r
1253     var ele = this._ele;\r
1254     if (!ele) {\r
1255       return false;\r
1256     } else {\r
1257       return ( ele.hasAttribute("from") || ele.hasAttribute("to")\r
1258          || ele.hasAttribute("by") || ele.hasAttribute("values") );\r
1259     }\r
1260   },\r
1261   \r
1262   /*後述のsetAttributeメソッドで使うキャッシュ\r
1263    * これを使うことで、二度同じ値を設定せずにすむので、高速化できる*/\r
1264   __cacheAttr: "",\r
1265   \r
1266   /*アニメーションするために、対象の属性やプロパティを変化させるメソッド*/\r
1267   setAttribute: function (value) {\r
1268     var attrName = this.attrName;\r
1269     if (!attrName) {\r
1270       return;\r
1271     }\r
1272     if (this.__cacheAttr === value) {\r
1273       /*同じ値であれば、再設定しない*/\r
1274       return;\r
1275     } else {\r
1276       this.__cacheAttr = value;\r
1277     }\r
1278     var ele = this.element;\r
1279     if (this.isCSS) {\r
1280       /*スタイルシートのプロパティを上書きしておく*/\r
1281       ele.style.setProperty(attrName, value, "");\r
1282     } else {\r
1283       ele.setAttributeNS(this.attrNameSpace, attrName, value);\r
1284     }\r
1285     value = attrName = ele = void 0;\r
1286   },\r
1287   \r
1288   /*setAttributeメソッドとは逆の効果で、無効化させるメソッド*/\r
1289   removeAttribute: function () {\r
1290     var attrName = this.attrName;\r
1291     if (!attrName) {\r
1292       return;\r
1293     }\r
1294     var ele = this.element;\r
1295     if (this.isDefault) {\r
1296       this.setAttribute(this.defaultValue);\r
1297     } else {\r
1298       /*初期段階でターゲットの要素に属性が指定されていない場合は、\r
1299        * 現在の属性値を削除するだけでよい*/\r
1300       ele.removeAttributeNS(this.attrNameSpace, attrName);\r
1301       /*スタイルシートのプロパティも削除しておく。removePropertyでないことに注意*/\r
1302       this.isCSS && ele.style.setProperty(attrName, this.defaultValue, "");\r
1303     }\r
1304     this.__cacheAttr = "";\r
1305     value = attrName = ele = void 0;\r
1306   },\r
1307   \r
1308   /*アニメーションの対象となる要素を値として返すメソッド\r
1309    * もっぱら、pushメソッドで使われる*/\r
1310   initTargetElement: function() {\r
1311     var ele = this._ele;\r
1312     var s = ele.parentNode || null;\r
1313     var id = ele.getAttribute("xlink:href");\r
1314     /*getAttributeNSメソッドでうまくいかなかったため、NSなしで代用*/\r
1315     if (id) {\r
1316       return ele.ownerDocument.getElementById(id.slice(1));\r
1317     }\r
1318     if ( id = ele.getAttributeNS(null, "targetElement") ) {\r
1319       return ele.ownerDocument.getElementById(id);\r
1320     }\r
1321     return s;\r
1322   },\r
1323   \r
1324   /*repeatイベントの発火時刻リスト\r
1325    * setSmilEventメソッドを見よ*/\r
1326   _repeatList: [],\r
1327   \r
1328   /*リピート回数を示すプロパティ\r
1329    * setSmilEventメソッドを見よ*/\r
1330   _repeatCount: 0,\r
1331   \r
1332   /*SMILイベント関連を発火させるためのメソッド\r
1333    * もっぱら、$attributeオブジェクトのpush メソッドで使われる*/\r
1334    setSmilEvent: function($list) {\r
1335     $list.addEvent("begin", function($list) {\r
1336         var target = this._ele,\r
1337             detail = 0;\r
1338         /*IE11のために、MouseEventsでSMILEventsの代用をする*/\r
1339         var evt = target.ownerDocument.createEvent("MouseEvents");\r
1340         evt.initMouseEvent("beginEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
1341         target.dispatchEvent(evt);\r
1342         /*repeatイベントのために、_repeatListプロパティを初期化する*/\r
1343         var list = this._repeatList = [],\r
1344             active = $list.activeTime,\r
1345             begin = $list.begin,\r
1346             simpleDuration = this.timeline.simpleDuration;\r
1347             if (simpleDuration && (simpleDuration !== active)\r
1348                   && (active !== Number.MAX_VALUE) ) {\r
1349               /*活動継続時間と単純持続時間が異なるとき、repeatイベントを設定*/\r
1350               for (var m= simpleDuration, n=1;m < active ; m+=simpleDuration) {\r
1351                 list.push( {\r
1352                   frame: begin + m,\r
1353                   /*リピートの回数 (n >= 1)*/\r
1354                   count: n\r
1355                 } );\r
1356                 ++n;\r
1357               }\r
1358             }\r
1359     }.bind(this) );\r
1360     \r
1361     $list.addEvent("play", function($list) {\r
1362       var target = this._ele,\r
1363           detail = 0,\r
1364           frame = $list.currentFrame,\r
1365           list = this._repeatList;\r
1366       if (!list.length) {\r
1367         return;\r
1368       }\r
1369       for (var i=0;i<list.length;++i) {\r
1370         if ( (this._repaetCount >= i+1)\r
1371             || (list[i].frame >= frame) ) {\r
1372           this._repeatCount = detail;\r
1373           break;\r
1374         } \r
1375         detail = list[i].count;\r
1376         var evt = target.ownerDocument.createEvent("MouseEvents");\r
1377         evt.initMouseEvent("repeatEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
1378         target.dispatchEvent(evt);\r
1379       }\r
1380     }.bind(this) );\r
1381     \r
1382     $list.addEvent("end", function() {\r
1383         var target = this._ele,\r
1384             detail = 0;\r
1385         var evt = target.ownerDocument.createEvent("MouseEvents");\r
1386         evt.initMouseEvent("endEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
1387         target.dispatchEvent(evt);\r
1388     }.bind(this) );\r
1389    },\r
1390 \r
1391   /*引数で指定した要素 ele の属性を解析して、フレームに追加する*/\r
1392   push: function(/*Element Node*/ ele) {\r
1393     if (!ele || !ele.hasAttribute) {\r
1394       return null;\r
1395     }\r
1396     /*キャッシュを初期化しておく*/\r
1397     this.__cacheAttr = "";\r
1398     \r
1399 \r
1400    /*getAttrメソッドとhasAttrValuesメソッドで必要*/\r
1401     this._ele = ele;\r
1402     \r
1403     /*initTargetElementメソッドを使って、elementプロパティの初期化*/\r
1404     this.element = this.initTargetElement();\r
1405     \r
1406     if (!this.hasAttrValues()) {\r
1407       /*from属性、to、by、values属性が指定されていない場合、アニメーションの効果が出ないように調整する\r
1408        *SMILアニメーションの仕様を参照\r
1409        *\r
1410        *>if none of the from, to, by or values attributes are specified, the animation will have no effect\r
1411        *「3.2.2. Animation function values」より引用\r
1412        *http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues\r
1413       */\r
1414       return null;\r
1415     }\r
1416  \r
1417     /*属性値の設定*/\r
1418     this.attrName = this.getAttr("attributeName", "");\r
1419     var attrName = this.attrName;\r
1420     \r
1421     /*属性タイプの設定*/\r
1422     var attrType = this.getAttr("attributeType", "auto");\r
1423     var computedStyle = this.element && this.element.ownerDocument.defaultView.getComputedStyle(this.element, "");\r
1424     if ( (attrType === "CSS")\r
1425          || ( (attrType === "auto") && this.element \r
1426              && computedStyle.getPropertyValue(attrName)\r
1427              && !/^(width|height|transform)$/.test(attrName)\r
1428             )\r
1429         ) {\r
1430         this.isCSS = true;\r
1431     }\r
1432     \r
1433     /*xlink関連の属性のときは名前空間を変更しておく*/\r
1434     if (attrName.indexOf("xlink") > -1) {\r
1435       this.attrNameSpace = "http://www.w3.org/1999/xlink";\r
1436     }\r
1437     /*thiseleはターゲットとなる要素(たとえば、親要素)*/\r
1438     var thisele = this.element;\r
1439     if (thisele) {\r
1440       /*規定値を設定しておく。属性を初期化するときに使われる*/\r
1441       this._ele = thisele;\r
1442       this.isDefault = thisele.hasAttributeNS(this.attrNameSpace, attrName);\r
1443       this.defaultValue = this.getAttr(attrName,\r
1444        computedStyle.getPropertyValue(attrName) );\r
1445       this._ele = ele; /*_eleプロパティを元に戻しておく*/\r
1446     }\r
1447     /*eleの属性の値を、それぞれオブジェクトに割り当て*/\r
1448     var $frame = base("$frame"),\r
1449         begin = $frame.$begin,\r
1450         frame = begin.up().mix( {\r
1451                   /*targetプロパティはbeginEventなどの発火で使う*/\r
1452                   target: ele,\r
1453                   eventTarget: (this.element || begin.eventTarget),\r
1454                   string: this.getAttr("begin", "0"),\r
1455                   $activate: begin.$activate.up().mix( {\r
1456                     dur: this.getAttr("dur", null),\r
1457                     end: begin.$end.up().mix( {\r
1458                           eventTarget: (this.element || begin.eventTarget),\r
1459                           string: this.getAttr("end", null)\r
1460                         } ),\r
1461                     repeatCount: this.getAttr("repeatCount", null),\r
1462                     repeatDur: this.getAttr("repeatDur", null),\r
1463                     min: this.getAttr("min", "0"),\r
1464                     max: this.getAttr("max", "indefinite")\r
1465                   } )\r
1466                 } ).updateList().parse();\r
1467     $frame.addLine(frame.$list.init());\r
1468     \r
1469     /*beginElementメソッドを追加*/\r
1470     var objList = frame.$list.addList(Number.MAX_VALUE),\r
1471         /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/\r
1472         endList = frame.$list.addEndList(Number.MAX_VALUE);\r
1473     ele.beginElement = (frame.string !== "indefinite") ? function(){}\r
1474                         : function() {\r
1475                             objList.value = frame.begin = base("$frame").currentFrame;\r
1476                             frame.isResolved = true;\r
1477                             var evt = this.ownerDocument.createEvent("MouseEvents");\r
1478                             evt.initMouseEvent("beginEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);\r
1479                             this.dispatchEvent(evt);\r
1480                           };\r
1481     /*endElementメソッドを追加*/\r
1482     var endFrame = frame.$activate.end || {};\r
1483     ele.endElement = (endFrame.string !== "indefinite") ? function(){}\r
1484                         : function() {\r
1485                             if (frame.isResolved) {\r
1486                               endFrame.isResolved = true;\r
1487                               endList.value  = base("$frame").currentFrame;\r
1488                               var evt = this.ownerDocument.createEvent("MouseEvents");\r
1489                               evt.initMouseEvent("endEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);\r
1490                               this.dispatchEvent(evt);\r
1491                             }\r
1492                           };\r
1493     /*setFrameメソッドを使ったときの、再帰スタックの使いすぎを防ぐため*/\r
1494     frame.timelines = [];\r
1495     this.setSmilEvent(frame.$list);\r
1496     begin = ele = void 0;\r
1497     return frame;\r
1498   },\r
1499   \r
1500   /*setValuesメソッド\r
1501    * values属性やfrom属性やto属性を処理するためのメソッド\r
1502    * valuesは配列、それ以外の引数は文字列\r
1503    * 返り値は、values属性は配列、それ以外の属性のときは、\r
1504    * 自分自身となる$attributeオブジェクトのコピーを返す*/\r
1505    setValues: function(values, from, to, by) {\r
1506      var $from = this.$from,\r
1507          s = [this.up().mix( {\r
1508                to: $from.up().mix( {\r
1509                  from: $from.up()\r
1510                } )\r
1511              } )],\r
1512          sto = s[0].to;\r
1513      values = values && values.split(";");\r
1514      /*from属性はオプションなので、条件には付け加えない*/\r
1515      if (values && values.length) {\r
1516        /*values属性が指定された場合、他の属性は無視される\r
1517         * W3C仕様 SMIL アニメーション 3.2.2. アニメーション関数の値*/\r
1518         s = [];\r
1519        for (var i=1;i<values.length;++i) {\r
1520          s.push( this.up().mix( {\r
1521                to: $from.up().mix( {\r
1522                  from: $from.up()\r
1523                } )\r
1524              } ) );\r
1525          sto = s[s.length-1].to;\r
1526          sto.string = values[i];\r
1527          sto.from.string = values[i-1];\r
1528        }\r
1529      } else if (to) {\r
1530        sto.string = to;\r
1531        sto.from.string = from || "0";\r
1532      } else if (by) {\r
1533        sto.string = by;\r
1534        sto.from.string = from || "0";\r
1535        var toNumList = sto.call(),\r
1536            fromNumList = sto.from;\r
1537        for (var i=0;i<toNumList.length;++i) {\r
1538          /*初期値と差分を足していく*/\r
1539          toNumList[i] += fromNumList[i];\r
1540        }\r
1541      } else {\r
1542        return null;\r
1543      }\r
1544      $from = sto = toNumList = fromNumList = void 0;\r
1545      return s;\r
1546    },\r
1547    \r
1548   /*isKeyErrorメソッド\r
1549    * 後述のsetKeyメソッドで使われる。keyTimes属性のエラーをチェックするメソッド\r
1550    * 属性値にエラーがあれば、trueを返し、なければ、falseを返す*/\r
1551   isKeyError: function(/*number*/ keyLength, /*number*/toLength) {\r
1552     return !!(keyLength && (keyLength !== (toLength+1)) );\r
1553   },\r
1554    \r
1555    /*setKeyメソッド\r
1556     * 引数の要素のkeyTimes属性やkeySplines属性を処理するためのメソッド\r
1557     * 必要な他の属性処理はsetValuesメソッドに任せている*/\r
1558    setKey: function(ele) {\r
1559      this._ele = ele;\r
1560      var to = this.setValues(this.getAttr("values", null),\r
1561           this.getAttr("from", null),\r
1562           this.getAttr("to", null),\r
1563           this.getAttr("by", null) ),\r
1564          toLength = to ? to.length : 0,\r
1565          keyTimes = this.getAttr("keyTimes", null),\r
1566          keySplines = this.getAttr("keySplines", null),\r
1567          keys,\r
1568          splines = keySplines && keySplines.split(";"),\r
1569          isDiscrete = (this.mode === "discrete"),\r
1570          toiKeySplines;\r
1571     if (!isDiscrete && keyTimes && to) {\r
1572       keys = this.$from.numList.call( {\r
1573         string: keyTimes\r
1574       } );\r
1575       /*keysTime属性の最初は0でなければならない。そうでなければエラー(SVG 1.1 2ndの仕様を参照)*/\r
1576       if (keys.length && (keys[0] !== 0)) {\r
1577         return null;\r
1578       }\r
1579       /*toオブジェクトはtoとfromで一組となっているのでlengthが加算される*/\r
1580       if (this.isKeyError(keys.length, toLength)) {\r
1581         /*keyTimes属性とvalues属性のリストの個数が合致しない場合、アニメーションの効果がない\r
1582          * 仕様を参照 SMIL Animation 3.2.3. Animation function calculation modes\r
1583          * http://www.w3.org/TR/smil-animation/#AnimFuncCalcMode*/\r
1584          \r
1585         /*ただし、animateMotion要素においては、keyPoints属性が\r
1586          * values属性のリストよりも優先されるため、\r
1587          * keyPoints属性があるときは、アニメーションの効果がある\r
1588          * \r
1589          * >Regarding determining the points which correspond to the ‘keyTimes’ attributes, the ‘keyPoints’ attribute overrides ‘path’, which overrides ‘values’\r
1590          * \r
1591          * http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement\r
1592          */\r
1593         return null;\r
1594       }\r
1595       for (var i=0;i<toLength;++i) {\r
1596         to[i].keyTime = keys[i+1] - keys[i];\r
1597         if (splines) {\r
1598           toiKeySplines = this.$from.numList.call( {\r
1599             string: splines[i]\r
1600           } );\r
1601           /*空配列を返すため、nullに変えておく*/\r
1602           to[i].keySplines = toiKeySplines.length ? toiKeySplines : null;\r
1603         }\r
1604       }\r
1605     } else if (!isDiscrete && to) {\r
1606       var per = 1 / toLength;\r
1607       for (var i=0;i<toLength;++i) {\r
1608         to[i].keyTime = per;\r
1609         if (splines) {\r
1610           toiKeySplines = this.$from.numList.call( {\r
1611             string: splines[i]\r
1612           } );\r
1613           to[i].keySplines = toiKeySplines.length ? toiKeySplines : null;\r
1614         }\r
1615       }\r
1616     } else if (to) {\r
1617         /*discreteモードの処理*/\r
1618       if (keyTimes) {\r
1619         keys = this.$from.numList.call( {\r
1620           string: keyTimes\r
1621         } );\r
1622       /*keysTime属性の最初は0でなければならない。そうでなければエラー(SVG 1.1 2ndの仕様を参照)*/\r
1623         if (keys.length && (keys[0] !== 0)) {\r
1624           return null;\r
1625         }\r
1626         if (this.isKeyError(keys.length, toLength)) {\r
1627           return null;\r
1628         }\r
1629         for (var i=0;i<toLength;++i) {\r
1630           to[i].keyTime = keys[i+1] - keys[i];\r
1631         }\r
1632       } else {\r
1633         var per = 1 / (toLength+1);\r
1634         for (var i=0;i<toLength;++i) {\r
1635           to[i].keyTime = per;\r
1636         }\r
1637       }\r
1638       /*toオブジェクトが足らないので、一つ追加しておく*/\r
1639       to.push( to[toLength-1].up().mix( function(){\r
1640           if (!keys) {\r
1641             return;\r
1642           }\r
1643           this.keyTime = 1-keys[keys.length-1];\r
1644         } ).of( {\r
1645             call: function() {\r
1646               return function (t) {\r
1647                  return isNaN(t) ? this.string\r
1648                                  : this.to.advance(1);\r
1649               }.bind(this);\r
1650             }\r
1651           } \r
1652         ) );\r
1653     }\r
1654     if (this.mode === "paced") {\r
1655       /*ベクトル全体の距離を算出*/\r
1656       var norm = 0;\r
1657       to.forEach( function(x) {\r
1658         norm += x.to.distance(x.to.from);\r
1659       } );\r
1660       to.forEach( function(x) {\r
1661          x.norm = norm;\r
1662       } );\r
1663     }\r
1664     ele = keyTimes = keys = per = splines = void 0;\r
1665     return to;\r
1666    }\r
1667 } ).up("$setElement").mix( {\r
1668   /*to属性の値、文字列*/\r
1669   to: "",\r
1670   \r
1671   \r
1672   /*後述のinitializeメソッドで使う要素リスト\r
1673    * getElementsByTagNameNSメソッドの返り値をArray化したことを想定*/\r
1674   elementList: [],\r
1675   \r
1676   /*何番目からelementListを処理しているかの数値*/\r
1677   numberOfElemList: 0,\r
1678   \r
1679   /*initialize メソッド\r
1680    * 要素リストを初期化させる\r
1681    * 初期化処理を分散させるために使う*/\r
1682   initialize: function() {\r
1683     var eles = this.elementList;\r
1684     if (!eles || !eles.length) {\r
1685       return;\r
1686     }\r
1687     var length = this.numberOfElemList+50;\r
1688     for (var i=length-50; i<length; ++i) {\r
1689       if (eles.length <= i) {\r
1690         this.elementList = null;\r
1691         return;\r
1692       }\r
1693       this.up().init(eles[i]);\r
1694     }\r
1695     this.numberOfElemList += 50;\r
1696     eles = length = void 0;\r
1697   },\r
1698    \r
1699   /*initメソッドで使われるアニメーション関数*/\r
1700   _setFrame: function ($list) {\r
1701     this.setAttribute(this.to);\r
1702   },\r
1703   \r
1704   /*開始を設定されたタイムライン ($beginオブジェクト)*/\r
1705   timeline: base("$frame").$begin,\r
1706   \r
1707   /*アニメが終了した際の後処理*/\r
1708   _setEndFrame: function ($list) {\r
1709     /*removeの場合、アニメーションを凍結せずに、もとに戻す*/\r
1710     if (this.fill === "remove") {\r
1711       this.removeAttribute();\r
1712     }\r
1713   },\r
1714   \r
1715   /*アニメーションの呈示値を呼び出す関数*/\r
1716   tocall: function() {},\r
1717   \r
1718   init: function(ele) {\r
1719     var line = this.push(ele);\r
1720     if (ele && ele.getAttributeNS) {\r
1721       this._ele = ele;\r
1722       this.to = this.getAttr("to", "");\r
1723       this.fill = this.getAttr("fill", "remove");\r
1724       if ( (this.getAttr("attributeName", "") === "font-size") \r
1725           && /\d\s*$/.test(this.to) ) {\r
1726         this.to += "px";\r
1727       }\r
1728     }\r
1729     var thisele = this.element;\r
1730     if (line && thisele) {\r
1731       this.timeline = line;\r
1732       /*$begin.$listのイベントに属性処理を追加*/\r
1733       line.$list.addEvent("begin", this._setFrame.bind(this));\r
1734       line.$list.addEvent("play", this._setFrame.bind(this));\r
1735       line.$list.addEvent("end", this._setEndFrame.bind(this));\r
1736       /*アニメーションが再起動する可能性もあるため、$listのstateプロパティはここで初期化*/\r
1737       line.$list.state = line.$list.WAITING;\r
1738     }\r
1739     line = thisele = void 0;\r
1740   }\r
1741 }).up("$animateElement").mix( {\r
1742   /*アニメ関数の配列*/\r
1743   funcs: [],\r
1744 \r
1745   /*進捗率advanceから、呈示値を求める*/\r
1746   tocall: function(advance) {\r
1747     var tf = this.funcs;\r
1748     if (this.mode !== "discrete") {\r
1749       for (var i=0;i<tf.length;++i) {\r
1750         var tfi = tf[i];\r
1751         /*keyTime(keyTimes属性で指定されたような値)で実行するかどうかを判別*/\r
1752         if (tfi.endKeyTime >= advance) {\r
1753           return tfi(advance - tfi.startKeyTime);\r
1754         }\r
1755       }\r
1756     } else {\r
1757       var result = "";\r
1758       for (var i=0;i<tf.length;++i) {\r
1759         var tfi = tf[i];\r
1760         if (advance >= tfi.startKeyTime) {\r
1761           result = tfi(advance);\r
1762         }\r
1763       }\r
1764       advance = tf = tfi = void 0;\r
1765       return result;\r
1766     }\r
1767     tf = i = tfi = void 0;\r
1768     return "";\r
1769   },\r
1770   \r
1771   _setFrame: function($list) {\r
1772     var currentFrame = $list.currentFrame;\r
1773     /*durationは単純継続時間\r
1774      *advanceは継続時間内での、進捗率\r
1775      *  仕様を参照 http://www.w3.org/TR/smil-animation/#AnimFuncValues\r
1776      *進捗率advanceは、durationと進捗フレーム数とを割った余り(REMAINDER)で算出する\r
1777      * 仕様を参照 SMIL Animation 3.6.2 Interval timing\r
1778      * http://www.w3.org/TR/2001/REC-smil-animation-20010904/#IntervalTiming*/\r
1779     var line = this.timeline,\r
1780         duration = line.simpleDuration,\r
1781         /*単純継続時間が不定の場合、補間はせずに初期値が採用されるため、advanceは0となる\r
1782          * 仕様を参照 SMIL Animation 3.2.2. Animation function values のInterpolation and indefinite simple durations\r
1783          * http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues*/\r
1784         advance = duration ? ( (currentFrame - $list.begin) % duration ) / duration\r
1785                     : 0;\r
1786     this.setAttribute(this.tocall(advance));\r
1787     line = duration = advance = void 0;\r
1788   },\r
1789   \r
1790   /*凍結処理をするために、進捗率を最後まで進めて調整するメソッド\r
1791    * 返り値は調整された進捗率\r
1792    * もっぱら、_setEndFrameメソッドで使われる*/\r
1793   getAdvanceEnd: function($list) {\r
1794     var line = this.timeline,\r
1795         duration = line.simpleDuration;\r
1796     if (duration) {\r
1797       var time = (line.activeTime > $list.beginEnd) ? $list.beginEnd\r
1798                   : line.activeTime;\r
1799       var advance = ( time % duration ) / duration;\r
1800       /*例外が発生するため、進捗率が1を超えないように処理*/\r
1801       advance = (advance > 1) ? 1 : advance;\r
1802        /*活動継続時間と単純継続時間が一致すると、余りは0となるため以下の処理*/\r
1803       advance = advance || 1;\r
1804     } else {\r
1805       advance = 0;\r
1806     }\r
1807     return advance;\r
1808   },\r
1809   \r
1810   /*_setEndFrameメソッドは、終了処理と凍結作業をする*/\r
1811   _setEndFrame: function($list) {\r
1812     /*上書きされたメソッドを呼び出してアニメーションの凍結作業をする*/\r
1813     if (this.fill === "freeze") {\r
1814       this.setAttribute(this.tocall( this.getAdvanceEnd($list) ));\r
1815       line = duration = advance = void 0;\r
1816     } else {\r
1817       this.removeAttribute();\r
1818     }\r
1819   },\r
1820   \r
1821   /*getAttrメソッドをオーバライド*/\r
1822   getAttr: function (name, def) {\r
1823     var s= this.$attribute.getAttr.apply(this, arguments);\r
1824     if ((name === "from") && !s && this.defaultValue) {\r
1825       /*from属性がない場合、対象要素の既定値を返す*/\r
1826       return this.defaultValue;\r
1827     }\r
1828     return s;\r
1829   },\r
1830   \r
1831   _keywords: {\r
1832     aliceblue:    [240,248,255],\r
1833     antiquewhite: [250,235,215],\r
1834     aqua:         [0,255,255],\r
1835     aquamarine:   [127,255,212],\r
1836     azure:        [240,255,255],\r
1837     beige:        [245,245,220],\r
1838     bisque:       [255,228,196],\r
1839     black:        [0,0,0],\r
1840     blanchedalmond:[255,235,205],\r
1841     blue:         [0,0,255],\r
1842     blueviolet:   [138,43,226],\r
1843     brown:        [165,42,42],\r
1844     burlywood:    [222,184,135],\r
1845     cadetblue:    [95,158,160],\r
1846     chartreuse:   [127,255,0],\r
1847     chocolate:    [210,105,30],\r
1848     coral:        [255,127,80],\r
1849     cornflowerblue:[100,149,237],\r
1850     cornsilk:     [255,248,220],\r
1851     crimson:      [220,20,60],\r
1852     cyan:         [0,255,255],\r
1853     darkblue:     [0,0,139],\r
1854     darkcyan:     [0,139,139],\r
1855     darkgoldenrod:[184,134,11],\r
1856     darkgray:     [169,169,169],\r
1857     darkgreen:    [0,100,0],\r
1858     darkgrey:     [169,169,169],\r
1859     darkkhaki:    [189,183,107],\r
1860     darkmagenta:  [139,0,139],\r
1861     darkolivegreen:[85,107,47],\r
1862     darkorange:    [255,140,0],\r
1863     darkorchid:   [153,50,204],\r
1864     darkred:      [139,0,0],\r
1865     darksalmon:   [233,150,122],\r
1866     darkseagreen: [143,188,143],\r
1867     darkslateblue:[72,61,139],\r
1868     darkslategray:[47,79,79],\r
1869     darkslategrey:[47,79,79],\r
1870     darkturquoise:[0,206,209],\r
1871     darkviolet:   [148,0,211],\r
1872     deeppink:     [255,20,147],\r
1873     deepskyblue:  [0,191,255],\r
1874     dimgray:      [105,105,105],\r
1875     dimgrey:      [105,105,105],\r
1876     dodgerblue:   [30,144,255],\r
1877     firebrick:    [178,34,34],\r
1878     floralwhite:  [255,250,240],\r
1879     forestgreen:  [34,139,34],\r
1880     fuchsia:      [255,0,255],\r
1881     gainsboro:    [220,220,220],\r
1882     ghostwhite:   [248,248,255],\r
1883     gold:         [255,215,0],\r
1884     goldenrod:    [218,165,32],\r
1885     gray:         [128,128,128],\r
1886     grey:         [128,128,128],\r
1887     green:        [0,128,0],\r
1888     greenyellow:  [173,255,47],\r
1889     honeydew:     [240,255,240],\r
1890     hotpink:      [255,105,180],\r
1891     indianred:    [205,92,92],\r
1892     indigo:       [75,0,130],\r
1893     ivory:        [255,255,240],\r
1894     khaki:        [240,230,140],\r
1895     lavender:     [230,230,250],\r
1896     lavenderblush:[255,240,245],\r
1897     lawngreen:    [124,252,0],\r
1898     lemonchiffon: [255,250,205],\r
1899     lightblue:    [173,216,230],\r
1900     lightcoral:   [240,128,128],\r
1901     lightcyan:    [224,255,255],\r
1902     lightgoldenrodyellow:[250,250,210],\r
1903     lightgray:    [211,211,211],\r
1904     lightgreen:   [144,238,144],\r
1905     lightgrey:    [211,211,211],\r
1906     lightpink:    [255,182,193],\r
1907     lightsalmon:  [255,160,122],\r
1908     lightseagree: [32,178,170],\r
1909     lightskyblue: [135,206,250],\r
1910     lightslategray:[119,136,153],\r
1911     lightslategrey:[119,136,153],\r
1912     lightsteelblue:[176,196,222],\r
1913     lightyellow:  [255,255,224],\r
1914     lime:         [0,255,0],\r
1915     limegreen:    [50,205,50],\r
1916     linen:        [250,240,230],\r
1917     magenta:      [255,0,255],\r
1918     maroon:       [128,0,0],\r
1919     mediumaquamarine:[102,205,170],\r
1920     mediumblue:    [0,0,205],\r
1921     mediumorchid:  [186,85,211],\r
1922     mediumpurple:  [147,112,219],\r
1923     mediumseagreen:[60,179,113],\r
1924     mediumslateblue:[123,104,238],\r
1925     mediumspringgreen:[0,250,154],\r
1926     mediumturquoise:[72,209,204],\r
1927     mediumvioletred:[199,21,133],\r
1928     midnightblue:  [25,25,112],\r
1929     mintcream:     [245,255,250],\r
1930     mistyrose:     [255,228,225],\r
1931     moccasin:      [255,228,181],\r
1932     navajowhite:   [255,222,173],\r
1933     navy:          [0,0,128],\r
1934     oldlace:       [253,245,230],\r
1935     olive:         [128,128,0],\r
1936     olivedrab:     [107,142,35],\r
1937     orange:        [255,165,0],\r
1938     orangered:     [255,69,0],\r
1939     orchid:        [218,112,214],\r
1940     palegoldenrod: [238,232,170],\r
1941     palegreen:     [152,251,152],\r
1942     paleturquoise: [175,238,238],\r
1943     palevioletred: [219,112,147],\r
1944     papayawhip:    [255,239,213],\r
1945     peachpuff:     [255,218,185],\r
1946     peru:          [205,133,63],\r
1947     pink:          [255,192,203],\r
1948     plum:          [221,160,221],\r
1949     powderblue:    [176,224,230],\r
1950     purple:        [128,0,128],\r
1951     red:           [255,0,0],\r
1952     rosybrown:     [188,143,143],\r
1953     royalblue:     [65,105,225],\r
1954     saddlebrown:   [139,69,19],\r
1955     salmon:        [250,128,114],\r
1956     sandybrown:    [244,164,96],\r
1957     seagreen:      [46,139,87],\r
1958     seashell:      [255,245,238],\r
1959     sienna:        [160,82,45],\r
1960     silver:        [192,192,192],\r
1961     skyblue:       [135,206,235],\r
1962     slateblue:     [106,90,205],\r
1963     slategray:     [112,128,144],\r
1964     slategrey:     [112,128,144],\r
1965     snow:          [255,250,250],\r
1966     springgreen:   [0,255,127],\r
1967     steelblue:     [70,130,180],\r
1968     tan:           [210,180,140],\r
1969     teal:          [0,128,128],\r
1970     thistle:       [216,191,216],\r
1971     tomato:        [255,99,71],\r
1972     turquoise:     [64,224,208],\r
1973     violet:        [238,130,238],\r
1974     wheat:         [245,222,179],\r
1975     white:         [255,255,255],\r
1976     whitesmoke:    [245,245,245],\r
1977     yellow:        [255,255,0],\r
1978     yellowgreen:   [154,205,50]\r
1979   },\r
1980   \r
1981   setAdd: function (ele, to) {\r
1982     /*additive属性がsum (加法アニメーション)の場合*/\r
1983     if (ele.getAttributeNS(null, "additive") === "sum") {\r
1984       var attrValue = ele.parentNode.getAttributeNS(null, this.attrName);\r
1985       ele.addEventListener("beginEvent", function(evt) {\r
1986         to.forEach( function(x) {\r
1987           x.to.setAdditive(attrValue);\r
1988         } )\r
1989       }, false);\r
1990     }\r
1991   },\r
1992   setAccum: function (ele, to) {\r
1993     /*accumulate属性がsum (蓄積アニメーション)の場合*/\r
1994     if (ele.getAttributeNS(null, "accumulate") === "sum") {\r
1995       ele.addEventListener("repeatEvent", function(evt) {\r
1996         to.forEach( function(x) {\r
1997           x.to.call();\r
1998           x.to.setAccumulate(evt.detail);\r
1999         } )\r
2000       }, false);\r
2001     }\r
2002   },\r
2003   \r
2004   /*displayなど補間をしなくてもよい属性への対処するためのメソッド*/\r
2005   setString: function() {\r
2006     if (/^(?:display|class|edgeMode|(gradient|marker|pattern|maskContent|mask|patternContent|primitive)Units|in|in2|method|mode|operator|preserveAspectRatio|result|spacing|spreadMethod|stitchTiles|target|type|xlink:href|yChannelSelector|color-interpolation|(clip|fill)-rule|cursor|filter|font-(family|stretch|style|variant)|image-rendering|marker-(end|mid|start)|mask|overflow|pointer-events|shape-rendering|stroke-(linecap|linejoin)|text-(anchor|decoration|rendering)|visibility)$/.test(this.attrName) ) {\r
2007       this.mode = "discrete";\r
2008     }\r
2009   },\r
2010   \r
2011   /*小数点以下の桁数を指定するため、setValuesメソッドをオーバライドする*/\r
2012   degits: 1,\r
2013   setValues: function() {\r
2014     var s = this.$attribute.setValues.apply(this, arguments),\r
2015         degits = this.degits;\r
2016     s && s.forEach(function(x) {\r
2017       x.to.degit = degits;\r
2018     } );\r
2019     degits = void 0;\r
2020     return s;\r
2021   },\r
2022   \r
2023   /*#から始まる文字列#aabbcc、例えばを、rgb(.., .., ..,)形式へと変換するためのメソッド\r
2024    * initメソッドで使われる*/\r
2025   toRGB: function(rgbColor) {\r
2026            var keyword = this._keywords[rgbColor];\r
2027            if (keyword) {\r
2028              return "rgb(" + keyword.join(", ") + ")";\r
2029            }\r
2030            if (rgbColor && (rgbColor[0] === "#")) {  //#を含む場合\r
2031               var s = "rgb(",\r
2032                   _parseInt = parseInt;\r
2033               if (rgbColor.length < 5) {\r
2034                 var r = rgbColor[1],\r
2035                     g = rgbColor[2],\r
2036                     b = rgbColor[3],\r
2037                 rgbColor = "#" + r + r + g + g + b + b;\r
2038               }\r
2039               rgbColor.match(/\#(\w{2})(\w{2})(\w{2})/);\r
2040               s += _parseInt(RegExp.$1, 16)\r
2041                 + ", "\r
2042                 + _parseInt(RegExp.$2, 16)\r
2043                 + ", "\r
2044                 + _parseInt(RegExp.$3, 16)\r
2045                 + ")";\r
2046               r = g = b = void 0;\r
2047               return s;\r
2048            }\r
2049            return rgbColor;\r
2050         }\r
2051 \r
2052 /*initメソッドに追加処理\r
2053  * onメソッドについては、base.jsを参照のこと*/\r
2054 } ).on ("init", function(ele) {\r
2055   var to, \r
2056       keyTime = 0,\r
2057       /*関数toRGBはrgbColor形式への変換処理で使う*/\r
2058       toRGB = function(x) { return x; },\r
2059       isColor = /^(?:fill|stroke|stop-color|color)$/.test(this.attrName);\r
2060   if (isColor) {\r
2061     /*通常は、小数点以下の桁数を既定値の1桁とする\r
2062      *RGB形式では補間に、小数を使わないため、0桁に設定\r
2063      * (なお、この作業は、setKeyメソッドの前に済ませておく必要がある)*/\r
2064     this.degits = 0;\r
2065     /*たとえば、fill属性などである場合には、rgbColor形式への変換処理をする*/\r
2066     toRGB = this.toRGB.bind(this);\r
2067   }\r
2068   if (ele) {\r
2069     this.mode = ele.getAttributeNS(null, "calcMode") || "linear";\r
2070     this.setString();\r
2071     to = this.setKey(ele);\r
2072   }\r
2073   if (to) {\r
2074     var aa = new Array(to.length);\r
2075     for (var i=0;i<to.length;++i) {\r
2076       var x = to[i];\r
2077       x.to.string = toRGB(x.to.string);\r
2078       x.to.from.string = toRGB(x.to.from.string);\r
2079       var s = x.call();\r
2080       /*x.keyTimeプロパティは区間を示しているため、区切り時刻に変換しておく\r
2081        * startKeyTimeプロパティは区間のスタート時点\r
2082        * endKeyTimeプロパティは区間のエンド地点*/\r
2083       s.startKeyTime = keyTime;\r
2084       keyTime = s.endKeyTime = keyTime + x.keyTime;\r
2085       aa[i] = s;\r
2086     }\r
2087     this.funcs = aa.filter( function(s) {\r
2088        if (!this.timeline.isResolved) {\r
2089          /*begin属性などにイベントを設定していた(未解決の)場合、後のs(0.1)がうまく作動せず、\r
2090           * 例外を出してしまうため、ここで返しておく*/\r
2091          return true;\r
2092        }\r
2093        /*splineモードで、かつ、アニメーションが無効な関数の場合は配列から外しておく\r
2094         * 無効な場合に関しては、$calcModeオブジェクトのcallメソッドを参照*/\r
2095        return (this.mode !== "spline") || !s.isNotAnimate;\r
2096     }, this );\r
2097     this.setAdd(ele, to);\r
2098     this.setAccum(ele, to);\r
2099   }\r
2100   keyTime = keywords = toRGB = isColor = void 0;\r
2101 } )\r
2102 /*$animateTranformElementオブジェクト\r
2103  * animateTransform要素に関連するオブジェクト*/\r
2104  .up("$animateTransformElement")\r
2105  .mix({\r
2106    /*__transformListで何番目のアイテムかを示すプロパティ*/\r
2107    numberOfList: -1,\r
2108    \r
2109    /*type属性の値*/\r
2110    type: "translate",\r
2111    \r
2112    /*attributeName属性の値はtransformで固定*/\r
2113    attrName: "transform",\r
2114    \r
2115    /*明確にisCSSプロパティを設定しておくことで、プロトタイプチェーンを使わせず最適化*/\r
2116    isCSS: false,\r
2117    \r
2118    /*additive属性の値がsumのときにtrue*/\r
2119    isSum: false,\r
2120    \r
2121    /*transform属性の値に使われる数値は精密であることが求められるため、\r
2122     *小数点以下の桁数を決めるdegitsプロパティの値も大きくしておく*/\r
2123    degits: 15,\r
2124    \r
2125    /*tocallメソッド(後述の$motionElementオブジェクトも含む)で使うメソッド\r
2126     * __transformListの値を結合して、文字列として返す*/\r
2127    joinList: function(s) {\r
2128      var playList = this.element.__transformList;\r
2129      for (var i=0;i<playList.length;++i) {\r
2130        var item = playList[i],\r
2131            value = item.value;\r
2132        if (item.isSum) {\r
2133          s += " " + value;\r
2134        } else if (item.isPlaying) {\r
2135          /*他のanimateTransform要素がadditive属性の値にreplaceをすでに設定していた、\r
2136           *かつ、その要素がアニメーション再生中の場合はsを初期化*/\r
2137          s = value;\r
2138        }\r
2139      }\r
2140      return s.trim();\r
2141    },\r
2142    \r
2143    /*$animateElementオブジェクトのtocallメソッドをオーバライド*/\r
2144    tocall: function (advance) {\r
2145      if (this.numberOfList < 0) {\r
2146        throw new Error("Number of The List Error");\r
2147      }\r
2148      var currentItem = this.element.__transformList[this.numberOfList];\r
2149      currentItem.value = this.type+ "(" +this.$animateElement.tocall.call(this, advance)+ ")";\r
2150      currentItem.isPlaying = true;\r
2151      currentItem.isSum = this.isSum;\r
2152      return this.joinList(this.defaultValue || "");\r
2153    },\r
2154    \r
2155    /*後の_setFrameメソッドで使うダミー*/\r
2156    __setAttribute: function(){},\r
2157    \r
2158    _setFrame: function($list) {\r
2159     var currentFrame = $list.currentFrame;\r
2160      /*__transformListの中で、自分より後の項目に再生中のものがあれば、\r
2161       *アニメーションを再生させないで、後に続く項目に任せる*/\r
2162      var list = this.element.__transformList,\r
2163          isPostActive = false,\r
2164          length = list.length;\r
2165      if ( (length !== 1) && (this.numberOfList < (length - 1) ) ) {\r
2166        /*リストの項目が一つだけであったり、自分自身が最後尾であれば、アニメーションを再生する\r
2167         * また、後に続く項目で再生中のものがあったら、今回は再生しない*/\r
2168        for (var i=this.numberOfList+1;i<length;++i) {\r
2169          if (list[i].isPlaying) {\r
2170            isPostActive = true;\r
2171          }\r
2172        }\r
2173      }\r
2174      /*__setAttributeはダミーなので、アニメーションが再生されない*/\r
2175      this.setAttribute = isPostActive ? this.__setAttribute\r
2176                                     : this.$animateElement.setAttribute;\r
2177      /*上書きされたメソッドを呼び出す*/\r
2178      this.$animateElement._setFrame.call(this, $list);\r
2179    },\r
2180    \r
2181    _setEndFrame: function($list) {\r
2182      var list = this.element.__transformList;\r
2183      if (!list) {\r
2184        return;\r
2185      }\r
2186      var item = list[this.numberOfList];\r
2187      if (this.fill === "remove") {\r
2188        if (!item) {\r
2189          return;\r
2190        } else if (!this.isSum) {\r
2191          /*凍結処理をしないで、かつ、元の状態に戻して、効果が出ないようにする*/\r
2192          item.isPlaying = false;\r
2193        } else {\r
2194          /*凍結処理をしないで、かつ、効果を出すが、変形させないようにする*/\r
2195          item.value = "translate(0)";\r
2196        }\r
2197      } else {\r
2198        /*凍結処理をする\r
2199         * 自前のtocallメソッドはvalueプロパティを書きかえてしまうため、\r
2200         * 上書きメソッドを呼び出す*/\r
2201        item.value = this.type+ "(" \r
2202                                       +this.$animateElement.tocall.call( this, this.getAdvanceEnd($list) )+ ")";\r
2203      }\r
2204      this.setAttribute( this.joinList(this.defaultValue || "") );\r
2205      item = void 0;\r
2206    },\r
2207    \r
2208     /*setAddメソッドのオーバライド\r
2209      * additive属性のsumに対する振る舞いが異なるため*/\r
2210     setAdd: function() {},\r
2211   })\r
2212  .on("init", function (ele) {\r
2213    if (!ele || !ele.parentNode) {\r
2214      return;\r
2215    }\r
2216    this.getAttr = this.$attribute.getAttr;\r
2217    this.type = this.getAttr("type", "translate");\r
2218    this.attrName = "transform";\r
2219    var parent = this.element;\r
2220    this.isDefault = parent.hasAttributeNS(null, "transform");\r
2221    this.defaultValue = parent.getAttributeNS(null, "transform") || "";\r
2222    this.isSum = (this.getAttr("additive", "replace") === "sum");\r
2223    if (!parent.__transformList) {\r
2224      parent.__transformList = [];\r
2225      this.numberOfList = -1;\r
2226    }\r
2227    if (this.hasAttrValues()\r
2228     && (this.numberOfList < 0) ) {\r
2229      /*もし、今まで、このオブジェクトで、initメソッドを実行していなければ*/\r
2230      this.numberOfList = parent.__transformList.length;\r
2231      /*isPlayingプロパティはアニメーション再生終了後でもtrueとなるので注意*/\r
2232      parent.__transformList.push( {isPlaying: false,\r
2233                                    value: "translate(0)",\r
2234                                    isSum: this.isSum\r
2235                                   } );\r
2236    }\r
2237   } )\r
2238   .up("$motionElement")\r
2239   .mix( function() {\r
2240     /*$animateTransformElementオブジェクトのでは、うまくいかないため、\r
2241      * setRFrameとsetEndFrameメソッドを再定義*/\r
2242     this._setFrame = this.$animateElement._setFrame;\r
2243     this._setEndFrame = this.$animateElement._setEndFrame;\r
2244   })\r
2245   .mix( {\r
2246     numberOfList: -1,\r
2247     mode: "paced",\r
2248         \r
2249     /*hasAttrValuesメソッドのオーバライド\r
2250      * path属性などに対応させるため*/\r
2251     hasAttrValues: function () {\r
2252       if (this.$attribute.hasAttrValues.call(this)) {\r
2253         return true;\r
2254       } else {\r
2255         return (this._ele.hasAttribute("keyPoints") || this._ele.hasAttribute("path")\r
2256                  || this._ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath").length);\r
2257       }\r
2258     },\r
2259     \r
2260     path: document.createElementNS("http://www.w3.org/2000/svg", "path"),\r
2261     \r
2262     rotate: "0",\r
2263     \r
2264     /*tocallメソッドのオーバライド*/\r
2265     tocall: function(advance) {\r
2266       /*モーションは仕様の関係上、かならず、CTMの一番初めに置かれ、前置積となる\r
2267      * なお、__transformListプロパティはtransform属性の値であり、CTMを表現する。\r
2268      * 必ず、CTMの一番目に設定する*/\r
2269       return ("translate(" +this.$animateElement.tocall.call(this, advance)+ ") "\r
2270               + this.joinList(this.defaultValue || "")).trim();\r
2271     },\r
2272     \r
2273     /*図の現在の角度rを求めて、rotate(rの文字列(最後に括弧はいらない)で返すメソッド*/\r
2274     getRotate: function(path, advanceLength, rotate) {\r
2275       /*パスセグメントの数値を求めてから、動いている図形の傾き角度r(ラジアンではなく度数)を算出する*/\r
2276       var length = path.getPathSegAtLength(advanceLength),\r
2277           seg = path.pathSegList.getItem(length),\r
2278           command = seg.pathSegTypeAsLetter,\r
2279           /*pi*180ではEdgeでうまく作動しない(原因は不明)*/\r
2280           pi = Math.PI;\r
2281       if (command === "M") {\r
2282         /*次のセグメントがどのコマンドによるかで、計算方式が違う*/\r
2283         var nextSeg = path.pathSegList.getItem(length+1),\r
2284             nextCommand = nextSeg.pathSegTypeAsLetter;\r
2285         if (nextCommand === "M") {\r
2286           return "";\r
2287         } else if (nextCommand === "L") {\r
2288           return ") rotate(" +(Math.atan2(nextSeg.y-seg.y, nextSeg.x-seg.x)/pi*180 + rotate)+ "";\r
2289         } else if (nextCommand === "C") {\r
2290           return ") rotate(" +(Math.atan2(nextSeg.y1-seg.y, nextSeg.x1-seg.x)/pi*180 + rotate)+ "";\r
2291         }\r
2292       } else if ((command === "L") && (length-1 >= 0)) {\r
2293         var preSeg = path.pathSegList.getItem(length-1);\r
2294         return ") rotate(" +(Math.atan2(seg.y-preSeg.y, seg.x-preSeg.x)/pi*180 + rotate)+ "";\r
2295       } else if (command === "C") {\r
2296         /*3次ベジェ曲線を微分する方法はニュートン法など数値解析の必要があるので、\r
2297          * 以下の通り、別の方法を採用する。\r
2298          * 現在位置から一歩進んだ曲線上の点Bをとり、それを、現在の点Aと結んで線分ABとしたとき、\r
2299          * その直線の傾きからおおよその角度を求める*/\r
2300          var point = path.getPointAtLength(advanceLength),\r
2301              x = point.x,\r
2302              y = point.y;\r
2303          /*一歩進んだ点*/\r
2304          point = path.getPointAtLength(advanceLength+1);\r
2305          return ") rotate(" +(Math.atan2(point.y-y, point.x-x)/pi*180 + rotate)+ "";\r
2306       }\r
2307     },\r
2308     \r
2309     /*this.$animateElement.tocallメソッドを置き換えるためのメソッド\r
2310      * mpath要素が指定されたときか、path属性のときにのみ使われる*/\r
2311     _tocallForPath: function(advance) {\r
2312       if (this.isKeyPoints) {\r
2313         /*keyPoints属性の値に従って、advance値を決定する。\r
2314          * なお、$animateElementはinitメソッドで書き換えているので、二重に呼び出す必要がある*/\r
2315         advance = +this.$animateElement.$animateElement.tocall.call(this, advance);\r
2316       }\r
2317       var path = this.path,\r
2318           advanceLength = advance * path.getTotalLength();\r
2319       /*全体の距離から、現在進めている距離を算出して、そこから、現在点を導き出す*/\r
2320       var point = path.getPointAtLength(advanceLength),\r
2321           rotate = 0; //追加すべき角度\r
2322       if (this.rotate === "0") {\r
2323         return point.x+ "," +point.y;\r
2324       } else if (this.rotate === "auto") {\r
2325         rotate = 0;\r
2326       } else if (this.rotate === "auto-reverse") {\r
2327         rotate = 180;\r
2328       } else {\r
2329         rotate = +this.rotate;\r
2330       }\r
2331       return point.x+ "," +point.y + this.getRotate(path, advanceLength, rotate);\r
2332     },\r
2333     \r
2334     /*setValuesメソッドのオーバライド*/\r
2335     setValues: function() {\r
2336       var keyPoints = this.getAttr("keyPoints", null),\r
2337           /*$animateElementプロパティは下記のinitメソッドで上書きされているため、\r
2338            * $animateElementを別方法で呼び出す必要がある*/\r
2339           superSetValues = this.$animateElement.$animateElement.setValues;\r
2340       if (keyPoints) {\r
2341         return superSetValues.call(this, keyPoints, null, null, null);\r
2342       } else {\r
2343         return superSetValues.apply(this, arguments);\r
2344       }\r
2345     }\r
2346   } )\r
2347   .on("init", function (ele) {\r
2348     if (!ele || !ele.parentNode) {\r
2349      return;\r
2350     }\r
2351     /*type属性で変更されないように($animateTransformElementのinitメソッドを参照のこと)*/\r
2352     this.type = "translate";\r
2353     /*isSumプロパティは常にtrueにしておく。animateTransform要素とは挙動が違うため\r
2354      * また、$aniamteTransformElementのtocallメソッド参照*/\r
2355     this.isSum = true;\r
2356     this.mode = this.getAttr("mode", "paced");\r
2357     this.rotate = this.getAttr("rotate", "0");\r
2358     /*isKeyPointsプロパティはkeyPoints属性が設定されていたら真*/\r
2359     this.isKeyPoints = ele.hasAttributeNS(null, "keyPoints");\r
2360     if (this.isKeyPoints && !ele.hasAttributeNS(null, "path")) {\r
2361       /*keyPoints属性がある場合は、path属性に指定がなければ、\r
2362        * values属性などの値をpath属性に書いておく*/\r
2363        var values = this.getAttr( "values", this.getAttr("from", "")+" L "+this.getAttr("to", "") );\r
2364        ele.setAttributeNS( null, "path", "M " +values.replace(/;/g, " L ") );\r
2365     }\r
2366     this.path = this.path.cloneNode(true);\r
2367     var mpath = ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath");\r
2368     /*$animateは後で、プロパティを書き換えるために使う。tocallメソッドも参照*/\r
2369     var $animate = this.$animateElement;\r
2370     if (mpath.length) {\r
2371       var p = ele.ownerDocument.getElementById(\r
2372         mpath[0].getAttributeNS("http://www.w3.org/1999/xlink", "href").slice(1)\r
2373       );\r
2374       p && this.path.setAttributeNS(null, "d", p.getAttributeNS(null, "d"));\r
2375       this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );\r
2376     } else if (ele.hasAttributeNS(null, "path")) {\r
2377       this.path.setAttributeNS(null, "d", ele.getAttributeNS(null, "path"));\r
2378       this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );\r
2379     }\r
2380   } );\r
2381 \r
2382 base("$getDocument").mix ( function() {\r
2383 \r
2384   function getDocument() \r
2385   {\r
2386     var svg = document.getElementsByTagName("object"),\r
2387         svgns = "http://www.w3.org/2000/svg";\r
2388     if (svg) {\r
2389       for (var i=0;i<svg.length;++i) {\r
2390         getElement( svg[i].getSVGDocument() );\r
2391       }\r
2392     }\r
2393     /*SVG文書から呼び出されたときも処理する*/\r
2394     getElement(document);\r
2395     /*idはアニメの中止ハンドル*/\r
2396     var id = __step(),\r
2397         idstop = function() {\r
2398           /*アニメーションを中止する関数*/\r
2399           window.cancelAnimationFrame && cancelAnimationFrame(id);\r
2400         };\r
2401     base("$frame").on("pauseAnimation", idstop);\r
2402     window.addEventListener("unload", idstop);\r
2403     \r
2404     /*文書からアニメーション関連要素を取り出して、オブジェクトを初期化*/\r
2405     function getElement (svgDoc) {\r
2406         var $set = base("$calcMode").$attribute.$setElement,\r
2407             $animate = $set.$animateElement,\r
2408             frame = base("$frame");\r
2409         init($set, "set");\r
2410         init($animate, "animate");\r
2411         init($animate.up(), "animateColor");\r
2412         init($animate.$animateTransformElement, "animateTransform");\r
2413         init($animate.$animateTransformElement.$motionElement, "animateMotion");\r
2414           /*リンクのハッシュ読み取りで、ハイパーリンクのイベント処理\r
2415          * たとえば、a要素のxlink:href="#hoge"で、<animate id="hoge"のとき、\r
2416          * animate要素がハイパーリンク作動と同時に動くようになる\r
2417          * \r
2418          * ただし、SMIL アニメーションの仕様では、\r
2419          * animate要素の開始時刻まで、時を進める操作をするだけ*/\r
2420          svgDoc.defaultView.addEventListener("hashchange", function() {\r
2421              var hash = svgDoc.defaultView.location.hash.slice(1);\r
2422              svgDoc.getElementById(hash).beginElement();\r
2423            });\r
2424         function init (obj, name) {\r
2425           /*あとでframe.initializeメソッドで呼び出すために準備しておく*/\r
2426           var elist = svgDoc.getElementsByTagNameNS(svgns, name);\r
2427           obj.numberOfElemList = 0;\r
2428           if (elist.length > 0) {\r
2429             obj.elementList = elist;\r
2430             frame.objList.push(obj);\r
2431           }\r
2432           elist = obj = void 0;\r
2433         };\r
2434     };\r
2435   }\r
2436   \r
2437   window.addEventListener && window.addEventListener("load", getDocument);\r
2438 \r
2439   this.step = __step;\r
2440   function __step() {\r
2441     /*EdgeはhasFeatureメソッドでtrueを返す*/\r
2442     if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Animation", "1.1")\r
2443         || (window.navigator.userAgent.toLowerCase().indexOf("edge") > 0)) {\r
2444       if (window.requestAnimationFrame && requestAnimationFrame) {\r
2445         /*IE11やEdgeなどSMILアニメーションに対応していないブラウザ用*/\r
2446         /*cancelはアニメーションの中止ハンドル*/\r
2447         var cancel = {\r
2448            handle: null\r
2449           };\r
2450         (function(frame) {\r
2451           var _cancel = cancel; /*cancelのエイリアス*/\r
2452           var step = function () {\r
2453             if (!this.isPaused) {\r
2454               frame++;\r
2455               try {\r
2456                 this.initialize();\r
2457                 this.setFrame(frame);\r
2458               } catch(e) {\r
2459               }\r
2460               _cancel.handle = requestAnimationFrame(step);\r
2461             }\r
2462           }.bind(base("$frame"));\r
2463           _cancel.handle = requestAnimationFrame(step);\r
2464         })(-1);\r
2465         return cancel;\r
2466       } else {\r
2467         setInterval( (function(frame) {\r
2468           var $f = base("$frame");\r
2469           return function () {\r
2470             frame++;\r
2471             $f.initialize();\r
2472             $f.setFrame(frame);\r
2473           };\r
2474         })(-1), 1 );\r
2475       }\r
2476     }\r
2477   }\r
2478 } );\r
2479 //#endif // _SMIL_IDL_\r