-/*SIE-SVG without Plugin under LGPL2.1 & GPL2.0 & Mozilla Public License\r
- *公式ページは http://sie.sourceforge.jp/\r
- *利用方法は <script defer="defer" type="text/javascript" src="sie.js"></script>\r
- *http://sie.sourceforge.jp/\r
- *Usage: <script defer="defer" type="text/javascript" src="sie.js"></script>\r
+/*SIE under the MIT Lisence\r
*/\r
-/* ***** BEGIN LICENSE BLOCK *****\r
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1\r
+/* Copyright 2016 dhrname and other contributors\r
+ * http://sie.osdn.jp/\r
*\r
- * The contents of this file are subject to the Mozilla Public License Version\r
- * 1.1 (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * http://www.mozilla.org/MPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS IS" basis,\r
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r
- * for the specific language governing rights and limitations under the\r
- * License.\r
- *\r
- * The Original Code is the Mozilla SVG Cairo Renderer project.\r
- *\r
- * The Initial Developer of the Original Code is IBM Corporation.\r
- * Portions created by the Initial Developer are Copyright (C) 2004\r
- * the Initial Developer. All Rights Reserved.\r
- *\r
- * Parts of this file contain code derived from the following files(s)\r
- * of the Mozilla SVG project (these parts are Copyright (C) by their\r
- * respective copyright-holders):\r
- * layout/svg/renderer/src/libart/nsSVGLibartBPathBuilder.cpp\r
- *\r
- * Contributor(s):DHRNAME revulo\r
- *\r
- * Alternatively, the contents of this file may be used under the terms of\r
- * either of the GNU General Public License Version 2 or later (the "GPL"),\r
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),\r
- * in which case the provisions of the GPL or the LGPL are applicable instead\r
- * of those above. If you wish to allow use of your version of this file only\r
- * under the terms of either the GPL or the LGPL, and not to allow others to\r
- * use your version of this file under the terms of the MPL, indicate your\r
- * decision by deleting the provisions above and replace them with the notice\r
- * and other provisions required by the GPL or the LGPL. If you do not delete\r
- * the provisions above, a recipient may use your version of this file under\r
- * the terms of any one of the MPL, the GPL or the LGPL.\r
- *\r
- * ***** END LICENSE BLOCK ***** */\r
-/*\r
- * Copyright (c) 2000 World Wide Web Consortium,\r
- * (Massachusetts Institute of Technology, Institut National de\r
- * Recherche en Informatique et en Automatique, Keio University). All\r
- * Rights Reserved. This program is distributed under the W3C's Software\r
- * Intellectual Property License. This program is distributed in the\r
- * hope that it will be useful, but WITHOUT ANY WARRANTY; without even\r
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
- * PURPOSE.\r
- * See W3C License http://www.w3.org/Consortium/Legal/ for more details.\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files (the\r
+ * "Software"), to deal in the Software without restriction, including\r
+ * without limitation the rights to use, copy, modify, merge, publish,\r
+ * distribute, sublicense, and/or sell copies of the Software, and to\r
+ * permit persons to whom the Software is furnished to do so, subject to\r
+ * the following conditions:\r
+ * \r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ * \r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
*/\r
\r
-\r
/*$frame オブジェクト\r
* 全体のフレームの管理を行う\r
*/\r
/*タイムラインのリスト (時間区間の設定ができる)*/\r
timelines: [],\r
\r
- /*開始フレーム数。アニメーションの開始条件となる\r
+ /*é\96\8bå§\8bã\83\95ã\83¬ã\83¼ã\83 æ\95°ã\81®å\80\99è£\9cã\80\82ã\82¢ã\83\8bã\83¡ã\83¼ã\82·ã\83§ã\83³ã\81®é\96\8bå§\8bæ\9d¡ä»¶ã\81¨ã\81ªã\82\8b\r
* 単位はフレーム数であって、秒数ではない*/\r
begin: 0,\r
\r
list = j = void 0;\r
}\r
} ).mix( function($frame) {\r
- /*$endFrame オブジェクト\r
- * 終了時の処理をするためのフレームを集める*/\r
- $frame.up("$endFrame").mix( {\r
- timelines: []\r
+ $frame.up("$list").mix( {\r
+ /*終了時刻(単位フレーム数)のキャッシュとして使う*/\r
+ end: 0,\r
+ \r
+ /*開始時刻から終了時刻までのフレーム数\r
+ * これがactiveTimeより短ければ、活動継続時間とみなす*/\r
+ beginEnd: Number.MAX_VALUE,\r
+ \r
+ /*開始時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/\r
+ beginList: { \r
+ next: null,\r
+ value: Number.MAX_VALUE\r
+ },\r
+ \r
+ /*終了時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/\r
+ endList: {\r
+ next: null,\r
+ value: Number.MAX_VALUE\r
+ },\r
+ \r
+ addBeginList: function (num) {\r
+ return ( this.beginList = {\r
+ value: num,\r
+ next: this.beginList\r
+ } );\r
+ },\r
+ \r
+ addEndList: function (num) {\r
+ return ( this.endList = {\r
+ value: num,\r
+ next: this.endList\r
+ } );\r
+ },\r
+ \r
+ /*引数に渡された時刻リストの中から、現在フレームf以下の、最大値を求めるメソッド\r
+ * -1を返したときはリストの中にf以下の値がないことを示す*/\r
+ getMaxList: function (f, list) {\r
+ var maxTime = -1; /*時刻の最大値*/\r
+ while( list ) {\r
+ var v = list.value;\r
+ /*f以下の開始リスト値のうち、最大値をstartTimeに代入*/\r
+ if ( (v <= f)\r
+ && (maxTime <= v) ) {\r
+ maxTime = v;\r
+ }\r
+ list = list.next;\r
+ }\r
+ return maxTime;\r
+ },\r
+ \r
+ /*現在の要素の状態を数値で示す(マジックナンバーは後述の大文字プロパティを使う)*/\r
+ state: 0,\r
+ \r
+ /*アニメーション待機状態を示す定数*/\r
+ WAITING: 0,\r
+ \r
+ /*アニメーション開始状態を示す定数*/\r
+ BEGINNING: 1,\r
+ \r
+ /*アニメーション再生中の状態を示す定数*/\r
+ PLAYING: 2,\r
+ \r
+ /*アニメーション終了状態を示す定数*/\r
+ ENDING: 3,\r
+ \r
+ /*終了処理をした後の待機状態を示す定数*/\r
+ POSTWAITING: 4,\r
+ \r
+ /*初期化用メソッド*/\r
+ init: function() {\r
+ this.state = this.WAITING;\r
+ this.begin = 0;\r
+ return this;\r
+ },\r
+ \r
+ /*引数で指定されたフレーム数に応じて、stateプロパティを更新するメソッド*/\r
+ updateState: function( /*number*/ f) {\r
+ if (f === void 0) {\r
+ /*引数fが指定されないときには状態を更新しない*/\r
+ return this;\r
+ }\r
+ var state = this.state,\r
+ wait = /*this.WAITING*/ 0,\r
+ begin = /*this.BEGINNING*/ 1,\r
+ play = /*this.PLAYING*/ 2,\r
+ end = /*this.ENDING*/ 3,\r
+ post = /*this.POSTWAITING*/ 4;\r
+ /*beginListプロパティと、endListプロパティの中で、\r
+ * 現在フレーム数 f より大きい数値は、更新できる条件と無関係なので、除外しておく\r
+ * だから、f以下の値の中から、最大値を探して、\r
+ * それをbeginプロパティの値cacheBeginと比較する*/\r
+ var startTime = this.getMaxList(f, this.beginList),\r
+ endTime = this.getMaxList(f, this.endList),\r
+ isWait = (state === wait),\r
+ cacheBegin = this.begin;\r
+ if ( !startTime && isWait) {\r
+ /*開始時刻が0ならば、アニメーションを開始*/\r
+ this.begin = 0;\r
+ this.state = begin;\r
+ } else if ( isWait || (state === post) ) {\r
+ if (startTime > cacheBegin) {\r
+ this.state = begin;\r
+ /*beginプロパティに開始時刻をキャッシュ用に保存*/\r
+ this.begin = startTime;\r
+ }\r
+ } else if (state === begin) {\r
+ if (endTime >= cacheBegin) {\r
+ /*終了時刻にもう到達したときは、直接BEGINNING状態からENDING状態へ移行*/\r
+ this.state = end;\r
+ /*endTimeを終了時刻とみなす*/\r
+ (endTime > 0) && (this.end = endTime);\r
+ /*activeTimeプロパティは、begin属性とend属性が反映されていないため、\r
+ * beginEndプロパティに別に設定しておく*/\r
+ this.beginEnd = 0;\r
+ } else {\r
+ this.state = play;\r
+ }\r
+ } else if (state === play) {\r
+ /*activeTimeプロパティを比較して、変数endTimeを書き換える*/\r
+ var act = cacheBegin + this.activeTime;\r
+ endTime = (endTime > act) ? act\r
+ : endTime;\r
+ if ( (f >= act) || (endTime >= cacheBegin) || (startTime > cacheBegin) ) {\r
+ /*終了時刻に到達したか、再び開始イベントが発火されたとき*/\r
+ this.state = end;\r
+ if (endTime > 0) {\r
+ /*endTimeを終了時刻とみなす*/\r
+ this.end = endTime;\r
+ /*activeTimeプロパティは、begin属性とend属性が反映されていないため、\r
+ * beginEndプロパティに別に設定しておく*/\r
+ this.beginEnd = endTime - cacheBegin;\r
+ }\r
+ }\r
+ } else if (state === end) {\r
+ if (startTime > cacheBegin) {\r
+ /*再生中に開始イベントが発火されて、終了状態となったとき*/\r
+ this.state = begin;\r
+ this.begin = startTime;\r
+ } else {\r
+ this.state = post;\r
+ }\r
+ } else {\r
+ this.state = begin;\r
+ }\r
+ cacheBegin = startTime = endTime = isWait = state = void 0;\r
+ return this;\r
+ },\r
+ \r
+ /*addEventメソッドで使われるイベントリスト(開始時に登録されたリスナー関数が呼び出される)*/\r
+ _beginListenerList: [],\r
+ \r
+ /*addEventメソッドで使われるイベントリスト(終了時に登録されたリスナー関数が呼び出される)*/\r
+ _endListenerList: [],\r
+ \r
+ /*addEventメソッドで使われるイベントリスト(再生時に登録されたリスナー関数が呼び出される)*/\r
+ _playListenerList: [],\r
+ \r
+ /*開始と再生と終了時に発火されるイベントリスナーを登録するメソッド*/\r
+ addEvent: function ( /*string*/ eventName, /*fnction*/ listener) {\r
+ var evtName = "_" +eventName+ "ListenerList";\r
+ /*プロトタイプ継承していた場合は新しく配列を作成*/\r
+ if (!this.hasOwnProperty(evtName)) {\r
+ this[evtName] = [];\r
+ }\r
+ this[evtName].push(listener);\r
+ },\r
+ \r
+ /*入力されたフレーム数fの場面に切り替えるメソッド*/\r
+ setFrame: function( /*number*/ f) {\r
+ this.currentFrame = f;\r
+ var state = this.updateState(f).state;\r
+ /*アニメーション開始と再生と、終了状態のときに、beginとplayとendイベントを呼び出しておいて、\r
+ * 次の状態(再生状態)に遷移する*/\r
+ if (state === /*this.PLAYING*/ 2) {\r
+ var list = this._playListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
+ } else if (state === /*this.BEGINNING*/ 1) {\r
+ list = this._beginListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
+ /*開始時刻と終了時刻が一致した場合はstateはENDING状態\r
+ * それ以外はPLAYING状態*/\r
+ state = this.updateState(f).state;\r
+ }\r
+ if (state === /*this.ENDING*/ 3) {\r
+ list = this._endListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
+ if (this.updateState(f).state === /*this.BEGINNING*/ 1) {\r
+ /*再生中にbeginイベントが呼び出された場合*/\r
+ this.updateState(f);\r
+ }\r
+ }\r
+ state = list = void 0;\r
+ }\r
+ } ).mix( function() {\r
+ /*後述の$beginや$endで使うメソッド*/\r
+ this.addList = this.addBeginList;\r
} );\r
\r
/*$begin オブジェクト\r
event: str\r
};\r
}\r
- },\r
+ }, \r
\r
- /*parse メソッド\r
- * stringプロパティを解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力\r
+ /*_parse メソッド\r
+ * 引数の文字列を解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力\r
* また、イベントリスナーに登録をしておく*/\r
- parse: function() {\r
- /*初期値を設定*/\r
- this.begin = 0;\r
- this.isResolved = false;\r
- var str = this.trim(this.string),\r
- plusminus = str.search(/[\+\-]/),\r
+ _parse: function (str) {\r
+ var plusminus = str.search(/[\+\-]/),\r
event = null,\r
- ele;\r
+ ele,\r
+ /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/\r
+ endList = this.$list.addEndList(Number.MAX_VALUE);\r
if (str === "indefinite") {\r
this.begin = Number.MAX_VALUE;\r
} else if (plusminus > 0) {\r
/*もしもあれば、押されるはずのキーを求める*/\r
this.accessKey = /accessKey\(([^\)]+?)\)/.test(str) ? RegExp.$1 : "";\r
this.begin = Math.floor( this.begin * this.fpms);\r
- if (event) {\r
+ if (str === "indefinite") {\r
+ /*begin属性の値がindefiniteの場合は、何もしない。\r
+ * 開始時刻はbeginElementメソッドに依存*/\r
+ } else if (event) {\r
ele = event.id ? this.eventTarget.ownerDocument.getElementById(event.id)\r
: this.eventTarget;\r
- /*イベントの時間差を設定しておく*/\r
- this.eventOffset = this.begin;\r
+ /*イベントの時間差を設定しておく\r
+ * eventOffsetとobjListの変数はクロージャとしてlistener関数で使われる*/\r
+ var eventOffset = this.begin,\r
+ /*objListのvalueプロパティはあとで書き換えられる(イベントの場合のみ)*/\r
+ objList = this.$list.addList(Number.MAX_VALUE),\r
+ /*イベントのリスナーとして使う*/\r
+ listener = function(evt) {\r
+ objList.value = this.begin = eventOffset + base("$frame").currentFrame;\r
+ this.isResolved = true;\r
+ };\r
+ this.eventOffset = eventOffset;\r
if (this.repeat > 0) {\r
ele && ele.addEventListener("repeatEvent", (function(evt) {\r
if (evt.detail === this.repeat) {\r
- this.listener(evt);\r
+ listener.call(this, evt);\r
} }).bind(this), true);\r
} else if (this.accessKey) {\r
document.documentElement.addEventListener("keydown", (function(evt) {\r
if (evt.char === this.accessKey) {\r
- this.listener(evt);\r
+ listener.call(this, evt);\r
} }).bind(this), false);\r
} else {\r
var evtName = /^(?:begin|end|repeat)$/.test(event.event) ? event.event + "Event"\r
: event.event;\r
- ele && ele.addEventListener(evtName, this.listener.bind(this), false);\r
+ ele && ele.addEventListener(evtName, listener.bind(this), false);\r
}\r
} else {\r
- /*イベントの影響を防ぐため\r
- * すでに、フレームオブジェクトのタイムラインには登録済みなので、\r
- * フレームではなく、自分独自のタイムラインに登録しておけばよい*/\r
- this.$frame = this;\r
+ /*開始リストに登録しておく($endの場合は終了リストに登録)*/\r
+ this.$list.addList(this.begin);\r
}\r
s = event = str = plusminus = ele = void 0;\r
+ },\r
+ \r
+ /*stringプロパティを解析して、\r
+ * 開始フレーム数の算出や、イベントリスナーの登録をするメソッド*/\r
+ parse: function() {\r
+ /*初期値を設定*/\r
+ this.begin = 0;\r
+ this.isResolved = false;\r
+ var str = this.trim(this.string);\r
+ if (str.indexOf(";") > -1){\r
+ /*;で区切られたリストを一つずつ解析*/\r
+ var list = str.split(";");\r
+ for (var i=0;i<list.length;++i) {\r
+ this._parse(list[i]);\r
+ }\r
+ } else {\r
+ this._parse(str);\r
+ }\r
+ s = str = void 0;\r
return this;\r
},\r
\r
- /*イベントのリスナーとして、parseメソッドで使う*/\r
- listener: function(evt) {\r
- this.begin = this.eventOffset + this.$frame.currentFrame;\r
- var s = this.$activate;\r
- s.begin = this.begin;\r
- this.activeTime = s.call() || Number.MAX_VALUE;\r
+ /*$listと$activateオブジェクトを更新して、活動継続時間を求めるメソッド*/\r
+ updateList: function() {\r
+ this.$list = this.$list.up();\r
+ /*beginとend属性を考慮に入れないで、活動継続時間を求める*/\r
+ var s = ( this.$activate = this.$activate.up() );\r
+ /*$endオブジェクトに付属している$listプロパティを更新したものと一致させておく*/\r
+ s.end && (s.end.$list = this.$list);\r
+ this.activeTime = this.$list.activeTime = s.call() || Number.MAX_VALUE;\r
this.simpleDuration = s.simpleDur;\r
- s = void 0;\r
- this.$frame.addLine(this);\r
- this.isResolved = true;\r
+ return this;\r
}\r
\r
/*$activate オブジェクト\r
},\r
\r
/*関数型の呼び出しメソッド\r
- * base.jsのofメソッドを活用して、関数型っぽい処理をする\r
- * 以下では、活動継続時間を算出\r
+ * base.jsのofメソッドを活用して活動継続時間を算出\r
+ * ただし、begin属性とend属性については、別途、$frame.$listで定める\r
* 計算方法はSMILアニメーション 3.3.4節を参照\r
* http://www.w3.org/TR/smil-animation/#ComputingActiveDur\r
*/\r
dur = this.simpleDur,\r
isIndefRepeatCount = (this.repeatCount === ind),\r
isIndefRepeatDur = (this.repeatDur === ind),\r
- isIndefEnd = (this.end === ind),\r
isDur = dur || (dur === 0),\r
- isEnd = this.end || (this.end === 0),\r
isRepeatCount = this.repeatCount || (this.repeatCount === 0),\r
isRepeatDur = this.repeatDur || (this.repeatDur === 0),\r
actList = [],\r
max = (this.max === ind) ? null : Math.floor(this.offset(this.max) * this.fpms),\r
s;\r
if (indef()) {\r
+ /*注意点として、活動継続時間が不定かつ、min属性とmax属性が指定されたときの記述が仕様にないため、\r
+ * W3Cのテストスイート(animate-elem-66-t.svg)に従い、max属性の値を返す*/\r
+ if (max) {\r
+ return max;\r
+ }\r
return null;\r
}\r
if (isDur && this.repeatCount && !isIndefRepeatCount) {\r
if (isRepeatDur && !isIndefRepeatDur) {\r
actList.push( Math.floor( this.offset(this.repeatDur) * this.fpms) );\r
}\r
- if (isEnd && !isIndefEnd) {\r
- actList.push( this.end - this.begin );\r
- }\r
if (isDur && !isRepeatCount && !isRepeatDur) {\r
/*repeatCountやrepeatDur属性が指定されていない場合*/\r
actList.push( dur );\r
}\r
\r
/*長くなるため、インライン関数を活用\r
- * indef関数は活動継続時間が不定かどうか、もし、不定なら真を返す*/\r
+ * indef関数はbeginとend属性を考慮せずに、\r
+ * 活動継続時間が不定かどうか、もし、不定なら真を返す*/\r
function indef() {\r
- if(isIndefEnd) {\r
- return true;\r
- } else if (isEnd) {\r
- return false;\r
- }\r
return !!( (!isDur && !isRepeatDur)\r
|| (isIndefRepeatCount && !isRepeatDur)\r
|| (isIndefRepeatDur && !isRepeatCount)\r
if (!this.string) {\r
return null;\r
}\r
+ /*addListメソッドには、addBeginList関数が入っているはずなので、それを一時的に変更する*/\r
+ this.$list.addList = this.$list.addEndList;\r
this.parse(this.string);\r
+ this.$list.addList = this.$list.addBeginList;\r
return this.isResolved ? this.begin\r
: "indefinite";\r
}\r
} ).mix( {\r
- /*イベントリスナー用の関数*/\r
- listener: function(evt) {\r
- if (this.begin <= 0) {\r
- /*強制的に終了させる*/\r
- this.removeLine(this.$begin);\r
- }\r
- this.begin = this.eventOffset + this.$frame.currentFrame;\r
- var s = this.$begin.$activate;\r
- s.end = this.begin;\r
- /*未解決だったendの値が、イベントの発生により、解決して再定義されたとき、\r
- * $activateオブジェクトを使って活動継続時間を再計算する*/\r
- this.$begin.activeTime = s.call();\r
- this.isResolved = true;\r
- s = void 0;\r
- }\r
+ $list: $frame.$begin.$list.up()\r
} );\r
} );\r
/*$from オブジェクト\r
this.__cacheAttr = "";\r
value = attrName = ele = void 0;\r
},\r
+ \r
+ /*アニメーションの対象となる要素を値として返すメソッド\r
+ * もっぱら、pushメソッドで使われる*/\r
+ initTargetElement: function() {\r
+ var ele = this._ele;\r
+ var s = ele.parentNode || null;\r
+ var id = ele.getAttribute("xlink:href");\r
+ /*getAttributeNSメソッドでうまくいかなかったため、NSなしで代用*/\r
+ if (id) {\r
+ return ele.ownerDocument.getElementById(id.slice(1));\r
+ }\r
+ if ( id = ele.getAttributeNS(null, "targetElement") ) {\r
+ return ele.ownerDocument.getElementById(id);\r
+ }\r
+ return s;\r
+ },\r
+ \r
+ /*repeatイベントの発火時刻リスト\r
+ * setSmilEventメソッドを見よ*/\r
+ _repeatList: [],\r
+ \r
+ /*リピート回数を示すプロパティ\r
+ * setSmilEventメソッドを見よ*/\r
+ _repeatCount: 0,\r
+ \r
+ /*SMILイベント関連を発火させるためのメソッド\r
+ * もっぱら、$attributeオブジェクトのpush メソッドで使われる*/\r
+ setSmilEvent: function($list) {\r
+ $list.addEvent("begin", function($list) {\r
+ var target = this._ele,\r
+ detail = 0;\r
+ /*IE11のために、MouseEventsでSMILEventsの代用をする*/\r
+ var evt = target.ownerDocument.createEvent("MouseEvents");\r
+ evt.initMouseEvent("beginEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
+ target.dispatchEvent(evt);\r
+ /*repeatイベントのために、_repeatListプロパティを初期化する*/\r
+ var list = this._repeatList = [],\r
+ active = $list.activeTime,\r
+ begin = $list.begin,\r
+ simpleDuration = this.timeline.simpleDuration;\r
+ if (simpleDuration && (simpleDuration !== active)\r
+ && (active !== Number.MAX_VALUE) ) {\r
+ /*活動継続時間と単純持続時間が異なるとき、repeatイベントを設定*/\r
+ for (var m= simpleDuration, n=1;m < active ; m+=simpleDuration) {\r
+ list.push( {\r
+ frame: begin + m,\r
+ /*リピートの回数 (n >= 1)*/\r
+ count: n\r
+ } );\r
+ ++n;\r
+ }\r
+ }\r
+ }.bind(this) );\r
+ \r
+ $list.addEvent("play", function($list) {\r
+ var target = this._ele,\r
+ detail = 0,\r
+ frame = $list.currentFrame,\r
+ list = this._repeatList;\r
+ if (!list.length) {\r
+ return;\r
+ }\r
+ for (var i=0;i<list.length;++i) {\r
+ if ( (this._repaetCount >= i+1)\r
+ || (list[i].frame >= frame) ) {\r
+ this._repeatCount = detail;\r
+ break;\r
+ } \r
+ detail = list[i].count;\r
+ var evt = target.ownerDocument.createEvent("MouseEvents");\r
+ evt.initMouseEvent("repeatEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
+ target.dispatchEvent(evt);\r
+ }\r
+ }.bind(this) );\r
+ \r
+ $list.addEvent("end", function() {\r
+ var target = this._ele,\r
+ detail = 0;\r
+ var evt = target.ownerDocument.createEvent("MouseEvents");\r
+ evt.initMouseEvent("endEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);\r
+ target.dispatchEvent(evt);\r
+ }.bind(this) );\r
+ },\r
\r
/*引数で指定した要素 ele の属性を解析して、フレームに追加する*/\r
push: function(/*Element Node*/ ele) {\r
}\r
/*キャッシュを初期化しておく*/\r
this.__cacheAttr = "";\r
- this.element = ele.parentNode || null;\r
- var id;\r
- if ( id = ele.getAttributeNS(null, "targetElement") ) {\r
- this.element = ele.ownerDocument.getElementById(id);\r
- }\r
- /*getAttributeNSメソッドでうまくいかなかったため、NSなしで代用*/\r
- if ( id = ele.getAttribute("xlink:href") ) {\r
- this.element = ele.ownerDocument.getElementById(id.slice(1));\r
- }\r
+ \r
+\r
/*getAttrメソッドとhasAttrValuesメソッドで必要*/\r
this._ele = ele;\r
+ \r
+ /*initTargetElementメソッドを使って、elementプロパティの初期化*/\r
+ this.element = this.initTargetElement();\r
+ \r
if (!this.hasAttrValues()) {\r
/*from属性、to、by、values属性が指定されていない場合、アニメーションの効果が出ないように調整する\r
*SMILアニメーションの仕様を参照\r
*/\r
return null;\r
}\r
- \r
+ \r
/*属性値の設定*/\r
this.attrName = this.getAttr("attributeName", "");\r
var attrName = this.attrName;\r
min: this.getAttr("min", "0"),\r
max: this.getAttr("max", "indefinite")\r
} )\r
- } ).parse();\r
- frame.$activate.end.$begin = frame;\r
+ } ).updateList().parse();\r
+ $frame.addLine(frame.$list.init());\r
+ \r
/*beginElementメソッドを追加*/\r
- function eleMethod (obj, eventName) {\r
- return (obj.string !== "indefinite") ? function(){}\r
+ var objList = frame.$list.addList(Number.MAX_VALUE),\r
+ /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/\r
+ endList = frame.$list.addEndList(Number.MAX_VALUE);\r
+ ele.beginElement = (frame.string !== "indefinite") ? function(){}\r
: function() {\r
- obj.listener( {\r
- /*アニメーションの開始をこのメソッドが呼ばれた時点とする*/\r
- timeStamp: Date.now()\r
- } );\r
+ objList.value = frame.begin = base("$frame").currentFrame;\r
+ frame.isResolved = true;\r
var evt = this.ownerDocument.createEvent("MouseEvents");\r
- evt.initMouseEvent(eventName + "Event" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);\r
+ evt.initMouseEvent("beginEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);\r
this.dispatchEvent(evt);\r
};\r
- };\r
- ele.beginElement = eleMethod(frame, "begin");\r
/*endElementメソッドを追加*/\r
- ele.endElement = eleMethod(frame.$activate.end, "end");\r
- if (frame.isResolved) {\r
- /*開始時間が初期化されてしまうのを防ぐ*/\r
- var cacheBegin = frame.begin;\r
- frame.listener( {\r
- /*アニメーションの開始をこのメソッドが呼ばれた時点とする*/\r
- timeStamp: Date.now()\r
- } );\r
- frame.begin = cacheBegin;\r
- }\r
+ var endFrame = frame.$activate.end || {};\r
+ ele.endElement = (endFrame.string !== "indefinite") ? function(){}\r
+ : function() {\r
+ if (frame.isResolved) {\r
+ endFrame.isResolved = true;\r
+ endList.value = base("$frame").currentFrame;\r
+ var evt = this.ownerDocument.createEvent("MouseEvents");\r
+ evt.initMouseEvent("endEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);\r
+ this.dispatchEvent(evt);\r
+ }\r
+ };\r
/*setFrameメソッドを使ったときの、再帰スタックの使いすぎを防ぐため*/\r
frame.timelines = [];\r
- begin = ele = id = void 0;\r
+ this.setSmilEvent(frame.$list);\r
+ begin = ele = void 0;\r
return frame;\r
},\r
\r
to: "",\r
\r
/*initメソッドで使われるアニメーション関数*/\r
- _setFrame: function (frame) {\r
- this.state = "playing";\r
+ _setFrame: function ($list) {\r
this.setAttribute(this.to);\r
},\r
\r
- /*アニメーション中かどうかの判別\r
- * 1, idling アニメがまだ始まっていない待機状態 (規定値)\r
- * 2, playing アニメーションの再生中\r
- */\r
- state: "idling",\r
- \r
/*開始を設定されたタイムライン ($beginオブジェクト)*/\r
timeline: base("$frame").$begin,\r
\r
- /*_setEndFrameメソッドで終了処理をさせたいときに、呼び出されるメソッド\r
- * 終了処理が必要なときだけ、trueを返す*/\r
- checkEnd: function (frame) {\r
- var line = this.timeline,\r
- end = line.begin + line.activeTime;\r
- if (!line.isResolved || isNaN(end)) {\r
- /*未解決など問題が発生したとき*/\r
- line = frame = void 0;\r
- return false;\r
- } else if (\r
- ( ( frame < line.begin ) || ( end <= frame )\r
- ) && (this.state === "playing") ) {\r
- /*stateプロパティを書き換えることで、一度のみ、終了処理させる*/\r
- this.state = "idling";\r
- line = frame = void 0;\r
- return true;\r
- } else {\r
- line = frame = void 0;\r
- return false;\r
- }\r
- },\r
- \r
/*アニメが終了した際の後処理*/\r
- _setEndFrame: function (frame) {\r
+ _setEndFrame: function ($list) {\r
/*removeの場合、アニメーションを凍結せずに、もとに戻す*/\r
- if (this.checkEnd(frame) && (this.fill === "remove")) {\r
+ if (this.fill === "remove") {\r
this.removeAttribute();\r
}\r
},\r
var thisele = this.element;\r
if (line && thisele) {\r
this.timeline = line;\r
- /*ラインの中に、属性処理をするためのラインを追加*/\r
- line.addLine(\r
- { setFrame: this._setFrame.bind(this),\r
- begin: 1,\r
- activeTime: 1\r
- }\r
- );\r
- base("$frame").$endFrame.addLine(\r
- { setFrame: this._setEndFrame.bind(this),\r
- begin: 1,\r
- activeTime: 1\r
- }\r
- );\r
+ /*$begin.$listのイベントに属性処理を追加*/\r
+ line.$list.addEvent("begin", this._setFrame.bind(this));\r
+ line.$list.addEvent("play", this._setFrame.bind(this));\r
+ line.$list.addEvent("end", this._setEndFrame.bind(this));\r
+ /*アニメーションが再起動する可能性もあるため、$listのstateプロパティはここで初期化*/\r
+ line.$list.state = line.$list.WAITING;\r
}\r
- /*アニメーションが再起動する可能性もあるため、stateプロパティはここで初期化*/\r
- this.state = "idling";\r
line = thisele = void 0;\r
}\r
}).up("$animateElement").mix( {\r
return "";\r
},\r
\r
- _setFrame: function(currentTime) {\r
+ _setFrame: function($list) {\r
+ var currentFrame = $list.currentFrame;\r
/*durationは単純継続時間\r
*advanceは継続時間内での、進捗率\r
* 仕様を参照 http://www.w3.org/TR/smil-animation/#AnimFuncValues\r
/*単純継続時間が不定の場合、補間はせずに初期値が採用されるため、advanceは0となる\r
* 仕様を参照 SMIL Animation 3.2.2. Animation function values のInterpolation and indefinite simple durations\r
* http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues*/\r
- advance = duration ? ( (currentTime - line.begin) % duration ) / duration\r
+ advance = duration ? ( (currentFrame - $list.begin) % duration ) / duration\r
: 0;\r
this.setAttribute(this.tocall(advance));\r
- this.state = "playing";\r
line = duration = advance = void 0;\r
},\r
\r
- /*_setEndFrameメソッドは、終了処理と凍結作業をするときに、falseを返す*/\r
- _setEndFrame: function(frame) {\r
- /*上書きされたメソッドを呼び出してアニメーションの凍結作業をする*/\r
- if (!this.checkEnd(frame)) {\r
- return;\r
+ /*凍結処理をするために、進捗率を最後まで進めて調整するメソッド\r
+ * もっぱら、_setEndFrameメソッドで使われる*/\r
+ freeze: function($list) {\r
+ var line = this.timeline,\r
+ duration = line.simpleDuration;\r
+ if (duration) {\r
+ var time = (line.activeTime > $list.beginEnd) ? $list.beginEnd\r
+ : line.activeTime;\r
+ var advance = ( time % duration ) / duration;\r
+ /*例外が発生するため、進捗率が1を超えないように処理*/\r
+ advance = (advance > 1) ? 1 : advance;\r
+ /*活動継続時間と単純継続時間が一致すると、余りは0となるため以下の処理*/\r
+ advance = advance || 1;\r
+ } else {\r
+ advance = 0;\r
}\r
+ return this.tocall(advance);\r
+ },\r
+ \r
+ /*_setEndFrameメソッドは、終了処理と凍結作業をする*/\r
+ _setEndFrame: function($list) {\r
+ /*上書きされたメソッドを呼び出してアニメーションの凍結作業をする*/\r
if (this.fill === "freeze") {\r
- var line = this.timeline,\r
- duration = line.simpleDuration;\r
- if (duration) {\r
- var advance = ( line.activeTime % duration ) / duration;\r
- /*例外が発生するため、進捗率が1を超えないように処理*/\r
- advance = (advance > 1) ? 1 : advance;\r
- /*活動継続時間と単純継続時間が一致すると、余りは0となるため以下の処理*/\r
- advance = advance || 1;\r
- } else {\r
- advance = 0;\r
- }\r
- this.setAttribute(this.tocall(advance));\r
+ this.setAttribute(this.freeze($list));\r
line = duration = advance = void 0;\r
} else {\r
this.removeAttribute();\r
} );\r
degits = void 0;\r
return s;\r
- }\r
-\r
-/*initメソッドに追加処理\r
- * onメソッドについては、base.jsを参照のこと*/\r
-} ).on ("init", function(ele) {\r
- var isColor = /^(?:fill|stroke|stop-color|color)$/.test(this.attrName);\r
- if (isColor) {\r
- /*通常は、小数点以下の桁数を既定値の1桁とする\r
- *RGB形式では補間に、小数を使わないため、0桁に設定\r
- * (なお、この作業は、setKeyメソッドの前に済ませておく必要がある)*/\r
- this.degits = 0;\r
- }\r
- var to, \r
- keyTime = 0,\r
- /*関数toRGBはrgbColor形式への変換処理で使う*/\r
- toRGB = function(x) { return x; };\r
- if (ele) {\r
- this.mode = ele.getAttributeNS(null, "calcMode") || "linear";\r
- this.setString();\r
- to = this.setKey(ele);\r
- }\r
- if (isColor) {\r
- /*#から始まる文字列を、rgb(.., .., ..,)形式へと変換するための関数*/\r
- var keywords = this._keywords;\r
- toRGB = function(rgbColor) {\r
- var keyword = keywords[rgbColor];\r
+ },\r
+ \r
+ /*#から始まる文字列#aabbcc、例えばを、rgb(.., .., ..,)形式へと変換するためのメソッド\r
+ * initメソッドで使われる*/\r
+ toRGB: function(rgbColor) {\r
+ var keyword = this._keywords[rgbColor];\r
if (keyword) {\r
return "rgb(" + keyword.join(", ") + ")";\r
}\r
return s;\r
}\r
return rgbColor;\r
- };\r
+ }\r
+\r
+/*initメソッドに追加処理\r
+ * onメソッドについては、base.jsを参照のこと*/\r
+} ).on ("init", function(ele) {\r
+ var to, \r
+ keyTime = 0,\r
+ /*関数toRGBはrgbColor形式への変換処理で使う*/\r
+ toRGB = function(x) { return x; },\r
+ isColor = /^(?:fill|stroke|stop-color|color)$/.test(this.attrName);\r
+ if (isColor) {\r
+ /*通常は、小数点以下の桁数を既定値の1桁とする\r
+ *RGB形式では補間に、小数を使わないため、0桁に設定\r
+ * (なお、この作業は、setKeyメソッドの前に済ませておく必要がある)*/\r
+ this.degits = 0;\r
+ /*たとえば、fill属性などである場合には、rgbColor形式への変換処理をする*/\r
+ toRGB = this.toRGB.bind(this);\r
+ }\r
+ if (ele) {\r
+ this.mode = ele.getAttributeNS(null, "calcMode") || "linear";\r
+ this.setString();\r
+ to = this.setKey(ele);\r
}\r
if (to) {\r
this.funcs = to.map( function(x) {\r
*小数点以下の桁数を決めるdegitsプロパティの値も大きくしておく*/\r
degits: 15,\r
\r
- /*$animateElementオブジェクトのtocallメソッドをオーバライド*/\r
- tocall: function (advance) {\r
- if (this.numberOfList < 0) {\r
- throw new Error("Number of The List Error");\r
- }\r
- var playList = this.element.__transformList,\r
- currentItem = playList[this.numberOfList];\r
- currentItem.value = this.type+ "(" +this.$animateElement.tocall.call(this, advance)+ ")";\r
- currentItem.isPlaying = true;\r
- var s = this.defaultValue || "";\r
+ /*tocallメソッド(後述の$motionElementオブジェクトも含む)で使うメソッド\r
+ * __transformListの値を結合して、文字列として返す*/\r
+ joinList: function(s) {\r
+ var playList = this.element.__transformList;\r
for (var i=0;i<playList.length;++i) {\r
var item = playList[i],\r
value = item.value;\r
s = value;\r
}\r
}\r
- return s;\r
+ return s.trim();\r
+ },\r
+ \r
+ /*$animateElementオブジェクトのtocallメソッドをオーバライド*/\r
+ tocall: function (advance) {\r
+ if (this.numberOfList < 0) {\r
+ throw new Error("Number of The List Error");\r
+ }\r
+ var currentItem = this.element.__transformList[this.numberOfList];\r
+ currentItem.value = this.type+ "(" +this.$animateElement.tocall.call(this, advance)+ ")";\r
+ currentItem.isPlaying = true;\r
+ currentItem.isSum = this.isSum;\r
+ return this.joinList(this.defaultValue || "");\r
},\r
\r
/*後の_setFrameメソッドで使うダミー*/\r
__setAttribute: function(){},\r
\r
- _setFrame: function(currentFrame) {\r
+ _setFrame: function($list) {\r
+ var currentFrame = $list.currentFrame;\r
/*__transformListの中で、自分より後の項目に再生中のものがあれば、\r
*アニメーションを再生させないで、後に続く項目に任せる*/\r
var list = this.element.__transformList,\r
this.$animateElement._setFrame.call(this, currentFrame);\r
},\r
\r
- _setEndFrame: function(currentFrame) {\r
+ _setEndFrame: function($list) {\r
var list = this.element.__transformList;\r
- if (!this.checkEnd(currentFrame) || (this.fill !== "remove") || !list) {\r
+ if (!list) {\r
return;\r
}\r
- if (!this.isSum) {\r
- /*凍結処理をしないで、かつ、元の状態に戻して、効果が出ないようにする*/\r
- list[this.numberOfList]\r
- && (list[this.numberOfList].isPlaying = false);\r
- for (var i = 0;i<list.length;++i) {\r
- var listi = list[i];\r
- if (listi.isPlaying || !listi.isRemove) {\r
- /*他のanimateTransform要素が活性化しているか、凍結処理が必要ならば、\r
- * 属性の初期化はしない*/\r
- return;\r
- }\r
- }\r
- this.removeAttribute();\r
+ if (this.fill === "remove") {\r
+ if (!list[this.numberOfList]) {\r
+ return;\r
+ } else if (!this.isSum) {\r
+ /*凍結処理をしないで、かつ、元の状態に戻して、効果が出ないようにする*/\r
+ list[this.numberOfList].isPlaying = false;\r
+ } else {\r
+ /*凍結処理をしないで、かつ、効果を出すが、変形させないようにする*/\r
+ list[this.numberOfList].value = "translate(0)";\r
+ }\r
} else {\r
- /*凍結処理をしないで、かつ、効果を出すが、変形させないようにする*/\r
- list[this.numberOfList]\r
- && (list[this.numberOfList].value = "translate(0)");\r
+ /*凍結処理をする*/\r
+ list[this.numberOfList].value = this.freeze($list);\r
}\r
},\r
\r
}\r
} )\r
.up("$motionElement")\r
+ .mix( function() {\r
+ /*setRFrameメソッドを再定義*/\r
+ this._setFrame = this.$animateElement._setFrame;\r
+ })\r
.mix( {\r
numberOfList: -1,\r
- mode: "paced"\r
+ mode: "paced",\r
+ \r
+ /*hasAttrValuesメソッドのオーバライド\r
+ * path属性に対応させるため*/\r
+ hasAttrValues: function () {\r
+ if (this.$attribute.hasAttrValues.call(this)) {\r
+ return true;\r
+ } else {\r
+ return (this._ele.hasAttribute("path")\r
+ || this._ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath").length);\r
+ }\r
+ },\r
+ \r
+ path: document.createElementNS("http://www.w3.org/2000/svg", "path"),\r
+ \r
+ rotate: "0",\r
+ \r
+ /*tocallメソッドのオーバライド*/\r
+ tocall: function(advance) {\r
+ /*モーションは仕様の関係上、かならず、CTMの一番初めに置かれ、前置積となる\r
+ * なお、__transformListプロパティはtransform属性の値であり、CTMを表現する。\r
+ * 必ず、CTMの一番目に設定する*/\r
+ return ("translate(" +this.$animateElement.tocall.call(this, advance)+ ") "\r
+ + this.joinList(this.defaultValue || "")).trim();\r
+ },\r
+ \r
+ /*図の現在の角度rを求めて、rotate(rの文字列(最後に括弧はいらない)で返すメソッド*/\r
+ getRotate: function(path, advanceLength, rotate) {\r
+ /*パスセグメントの数値を求めてから、動いている図形の傾き角度r(ラジアンではなく度数)を算出する*/\r
+ var length = path.getPathSegAtLength(advanceLength),\r
+ seg = path.pathSegList.getItem(length),\r
+ command = seg.pathSegTypeAsLetter,\r
+ /*pi*180ではEdgeでうまく作動しない(原因は不明)*/\r
+ pi = Math.PI;\r
+ if (command === "M") {\r
+ /*次のセグメントがどのコマンドによるかで、計算方式が違う*/\r
+ var nextSeg = path.pathSegList.getItem(length+1),\r
+ nextCommand = nextSeg.pathSegTypeAsLetter;\r
+ if (nextCommand === "M") {\r
+ return "";\r
+ } else if (nextCommand === "L") {\r
+ return ") rotate(" +(Math.atan2(nextSeg.y-seg.y, nextSeg.x-seg.x)/pi*180 + rotate)+ "";\r
+ } else if (nextCommand === "C") {\r
+ return ") rotate(" +(Math.atan2(nextSeg.y1-seg.y, nextSeg.x1-seg.x)/pi*180 + rotate)+ "";\r
+ }\r
+ } else if ((command === "L") && (length-1 >= 0)) {\r
+ var preSeg = path.pathSegList.getItem(length-1);\r
+ return ") rotate(" +(Math.atan2(seg.y-preSeg.y, seg.x-preSeg.x)/pi*180 + rotate)+ "";\r
+ } else if (command === "C") {\r
+ /*3次ベジェ曲線を微分する方法はニュートン法など数値解析の必要があるので、\r
+ * 以下の通り、別の方法を採用する。\r
+ * 現在位置から一歩進んだ曲線上の点Bをとり、それを、現在の点Aと結んで線分ABとしたとき、\r
+ * その直線の傾きからおおよその角度を求める*/\r
+ var point = path.getPointAtLength(advanceLength),\r
+ x = point.x,\r
+ y = point.y;\r
+ /*一歩進んだ点*/\r
+ point = path.getPointAtLength(advanceLength+1);\r
+ return ") rotate(" +(Math.atan2(point.y-y, point.x-x)/pi*180 + rotate)+ "";\r
+ }\r
+ },\r
+ \r
+ /*$animateElement.tocallメソッドを置き換えるためのメソッド\r
+ * mpath要素が指定されたときか、path属性のときにのみ使われる*/\r
+ _tocallForPath: function(advance) {\r
+ var path = this.path,\r
+ advanceLength = advance * path.getTotalLength();\r
+ /*全体の距離から、現在進めている距離を算出して、そこから、現在点を導き出す*/\r
+ var point = path.getPointAtLength(advanceLength),\r
+ rotate = 0; //追加すべき角度\r
+ if (this.rotate === "0") {\r
+ return point.x+ "," +point.y;\r
+ } else if (this.rotate === "auto") {\r
+ rotate = 0;\r
+ } else if (this.rotate === "auto-reverse") {\r
+ rotate = 180;\r
+ } else {\r
+ rotate = +this.rotate;\r
+ }\r
+ return point.x+ "," +point.y + this.getRotate(path, advanceLength, rotate);\r
+ }\r
} )\r
.on("init", function (ele) {\r
+ if (!ele || !ele.parentNode) {\r
+ return;\r
+ }\r
/*type属性で変更されないように($animateTransformElementのinitメソッドを参照のこと)*/\r
this.type = "translate";\r
+ /*isSumプロパティは常にtrueにしておく。animateTransform要素とは挙動が違うため\r
+ * また、$aniamteTransformElementのtocallメソッド参照*/\r
+ this.isSum = true;\r
this.mode = this.getAttr("mode", "paced");\r
+ this.rotate = this.getAttr("rotate", "0");\r
+ this.path = this.path.cloneNode(true);\r
+ var mpath = ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath");\r
+ /*$animateは後で、プロパティを書き換えるために使う。tocallメソッドも参照*/\r
+ var $animate = this.$animateElement;\r
+ if (mpath.length) {\r
+ var p = ele.ownerDocument.getElementById(\r
+ mpath[0].getAttributeNS("http://www.w3.org/1999/xlink", "href").slice(1)\r
+ );\r
+ p && this.path.setAttributeNS(null, "d", p.getAttributeNS(null, "d"));\r
+ this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );\r
+ } else if (ele.hasAttributeNS(null, "path")) {\r
+ this.path.setAttributeNS(null, "d", ele.getAttributeNS(null, "path"));\r
+ this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );\r
+ }\r
} );\r
\r
/*$svgEventオブジェクトは、SVGEvent発火を監視するためのオブジェクト*/\r
floor = Math.floor;\r
while(obj) {\r
var frame = obj.frame,\r
- target = obj.target\r
+ target = obj.target,\r
detail = 0;\r
if (frame <= num) {\r
/*IE11ではSVGEventsやDOMEventsを使うと問題が起きるため、MouseEventsで代用する*/\r
window.addEventListener && window.addEventListener("load", getDocument);\r
\r
function __step() {\r
-if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Animation", "1.1")) {\r
+/*EdgeはhasFeatureメソッドでtrueを返す*/\r
+if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Animation", "1.1")\r
+ || window.navigator.userAgent.toLowerCase().indexOf("edge")) {\r
if (window.requestAnimationFrame && requestAnimationFrame) {\r
/*IE11などSMILアニメーションに対応していないブラウザ用*/\r
/*cancelはアニメーションの中止ハンドル*/\r
frame++;\r
try {\r
$f.setFrame(frame);\r
- $f.$endFrame.setFrame(frame);\r
} catch(e) {\r
}\r
_cancel.handle = requestAnimationFrame(step);\r