OSDN Git Service

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