OSDN Git Service

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