OSDN Git Service

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