OSDN Git Service

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