OSDN Git Service

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