diff --git a/demos/snap-ad/Gruntfile.js b/demos/snap-ad/Gruntfile.js
new file mode 100644
index 0000000..a0b5124
--- /dev/null
+++ b/demos/snap-ad/Gruntfile.js
@@ -0,0 +1,75 @@
+module.exports = function(grunt) {
+
+ var pkg = grunt.file.readJSON("package.json");
+
+ // Project configuration.
+ grunt.initConfig({
+ copy: {
+ main: {
+ files: [
+ {
+ expand: true,
+ cwd: './src/',
+ src: [
+ '*',
+ '!config.rb',
+ 'js/vendor/require.min.js',
+ 'js/vendor/modernizr.min.js'
+ ],
+ dest: './site/',
+ filter: 'isFile'
+ },
+ {
+ expand: true,
+ cwd: './src',
+ src: ['assets/**'],
+ dest: './site/'
+ }
+ ]
+ }
+ },
+ requirejs: {
+ compile: {
+ options: {
+ name: "main",
+ baseUrl: "./src/js/",
+ mainConfigFile: "./src/js/main.js",
+ out: "./site/js/main.js"
+ }
+ }
+ },
+ compass: {
+ dist: {
+ options: {
+ config: './src/config.rb',
+ sassDir: './src/sass',
+ cssDir: './site/css'
+ }
+ }
+ },
+ processhtml: {
+ options: {
+ // Task-specific options go here.
+ },
+ dist: {
+ files: {
+ './site/index.html': ['./site/index.html']
+ }
+ }
+ },
+ inline: {
+ dist: {
+ src: ['./site/index.html'],
+ dest: ['./site/index.min.html']
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-contrib-requirejs');
+ grunt.loadNpmTasks("grunt-contrib-compass");
+ grunt.loadNpmTasks("grunt-processhtml");
+ grunt.loadNpmTasks("grunt-inline");
+
+ grunt.registerTask("default", ["copy", "requirejs", "compass", "processhtml", "inline"]);
+};
\ No newline at end of file
diff --git a/demos/snap-ad/README.md b/demos/snap-ad/README.md
new file mode 100644
index 0000000..06a67b0
--- /dev/null
+++ b/demos/snap-ad/README.md
@@ -0,0 +1,13 @@
+snap-ad
+=======
+
+Snap.svg banner ad
+
+run
+`npm install`
+to add node modules
+
+
+run
+`grunt`
+to build
\ No newline at end of file
diff --git a/demos/snap-ad/SnapAd.tmproj b/demos/snap-ad/SnapAd.tmproj
new file mode 100644
index 0000000..d5d2d56
--- /dev/null
+++ b/demos/snap-ad/SnapAd.tmproj
@@ -0,0 +1,109 @@
+
+
+
+
+ currentDocument
+ src/js/app/app.js
+ documents
+
+
+ expanded
+
+ name
+ src
+ regexFolderFilter
+ !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$
+ sourceDirectory
+ src
+
+
+ fileHierarchyDrawerWidth
+ 185
+ metaData
+
+ src/index.html
+
+ caret
+
+ column
+ 0
+ line
+ 17
+
+ columnSelection
+
+ firstVisibleColumn
+ 0
+ firstVisibleLine
+ 3
+ selectFrom
+
+ column
+ 7
+ line
+ 19
+
+ selectTo
+
+ column
+ 0
+ line
+ 17
+
+
+ src/js/app/app.js
+
+ caret
+
+ column
+ 0
+ line
+ 222
+
+ firstVisibleColumn
+ 0
+ firstVisibleLine
+ 199
+
+ src/js/main.js
+
+ caret
+
+ column
+ 4
+ line
+ 46
+
+ firstVisibleColumn
+ 0
+ firstVisibleLine
+ 47
+
+ src/sass/screen.scss
+
+ caret
+
+ column
+ 2
+ line
+ 81
+
+ firstVisibleColumn
+ 0
+ firstVisibleLine
+ 0
+
+
+ openDocuments
+
+ src/index.html
+ src/js/main.js
+ src/sass/screen.scss
+ src/js/app/app.js
+
+ showFileHierarchyDrawer
+
+ windowFrame
+ {{642, 51}, {651, 811}}
+
+
diff --git a/demos/snap-ad/package.json b/demos/snap-ad/package.json
new file mode 100644
index 0000000..1718196
--- /dev/null
+++ b/demos/snap-ad/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "snapad",
+ "version": "0.0.1",
+ "description": "snap ad",
+ "main": "Gruntfile.js",
+ "repository": {
+ "type": "git"
+ },
+ "author": "CJ Gammon",
+ "license": "Apache License v2",
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-contrib-copy": "*",
+ "grunt-contrib-compass": "*",
+ "grunt-contrib-requirejs": "*",
+ "grunt-processhtml": "*",
+ "grunt-inline": "*"
+ }
+}
diff --git a/demos/snap-ad/site/assets/computer.svg b/demos/snap-ad/site/assets/computer.svg
new file mode 100644
index 0000000..05c5c57
--- /dev/null
+++ b/demos/snap-ad/site/assets/computer.svg
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/demos/snap-ad/site/assets/fonts/OpenSans-Light.ttf b/demos/snap-ad/site/assets/fonts/OpenSans-Light.ttf
new file mode 100644
index 0000000..0d38189
Binary files /dev/null and b/demos/snap-ad/site/assets/fonts/OpenSans-Light.ttf differ
diff --git a/demos/snap-ad/site/assets/fonts/SourceSansPro-Semibold.ttf b/demos/snap-ad/site/assets/fonts/SourceSansPro-Semibold.ttf
new file mode 100644
index 0000000..5020594
Binary files /dev/null and b/demos/snap-ad/site/assets/fonts/SourceSansPro-Semibold.ttf differ
diff --git a/demos/snap-ad/site/assets/heart.svg b/demos/snap-ad/site/assets/heart.svg
new file mode 100644
index 0000000..87fd258
--- /dev/null
+++ b/demos/snap-ad/site/assets/heart.svg
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/demos/snap-ad/site/assets/phone.svg b/demos/snap-ad/site/assets/phone.svg
new file mode 100644
index 0000000..dbd26ca
--- /dev/null
+++ b/demos/snap-ad/site/assets/phone.svg
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/demos/snap-ad/site/assets/tablet.svg b/demos/snap-ad/site/assets/tablet.svg
new file mode 100644
index 0000000..1783bb0
--- /dev/null
+++ b/demos/snap-ad/site/assets/tablet.svg
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/demos/snap-ad/site/backup.jpg b/demos/snap-ad/site/backup.jpg
new file mode 100644
index 0000000..6e05cec
Binary files /dev/null and b/demos/snap-ad/site/backup.jpg differ
diff --git a/demos/snap-ad/site/css/screen.css b/demos/snap-ad/site/css/screen.css
new file mode 100644
index 0000000..f959e6b
--- /dev/null
+++ b/demos/snap-ad/site/css/screen.css
@@ -0,0 +1,93 @@
+/* line 3, ../../src/sass/screen.scss */
+.base-font {
+ font-family: 'Open Sans', serif;
+}
+
+/* line 7, ../../src/sass/screen.scss */
+.snap-font {
+ font-family: 'Source Sans Pro', serif;
+}
+
+/* line 11, ../../src/sass/screen.scss */
+body {
+ margin: 0;
+ cursor: pointer;
+}
+
+/* line 16, ../../src/sass/screen.scss */
+text {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/* line 25, ../../src/sass/screen.scss */
+svg {
+ -webkit-transform-origin: top left;
+ -moz-transform-origin: top left;
+ -ms-transform-origin: top left;
+ -o-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-transform: scale(1);
+ -moz-transform: scale(1);
+ -ms-transform: scale(1);
+ -o-transform: scale(1);
+ transform: scale(1);
+}
+
+/* line 30, ../../src/sass/screen.scss */
+#learn-btn {
+ cursor: pointer;
+}
+/* line 33, ../../src/sass/screen.scss */
+#learn-btn path, #learn-btn text {
+ -webkit-transform: translateY(0);
+ -moz-transform: translateY(0);
+ -ms-transform: translateY(0);
+ -o-transform: translateY(0);
+ transform: translateY(0);
+}
+/* line 39, ../../src/sass/screen.scss */
+#learn-btn:hover path:first-child {
+ fill: #076656;
+}
+/* line 46, ../../src/sass/screen.scss */
+#learn-btn:hover path:nth-child(2) {
+ fill: #7cd1c2;
+}
+/* line 51, ../../src/sass/screen.scss */
+#learn-btn:hover text {
+ fill: #0a9a87;
+}
+/* line 59, ../../src/sass/screen.scss */
+#learn-btn:active path:nth-child(2) {
+ -webkit-transform: translateY(2px);
+ -moz-transform: translateY(2px);
+ -ms-transform: translateY(2px);
+ -o-transform: translateY(2px);
+ transform: translateY(2px);
+}
+/* line 64, ../../src/sass/screen.scss */
+#learn-btn:active text {
+ -webkit-transform: translateY(2px);
+ -moz-transform: translateY(2px);
+ -ms-transform: translateY(2px);
+ -o-transform: translateY(2px);
+ transform: translateY(2px);
+}
+
+/* line 71, ../../src/sass/screen.scss */
+#replay-btn:hover {
+ opacity: 0.5;
+}
+
+/*
+@media (max-width: 400px) {
+ svg{
+ @include transform(scale(0.375));
+ }
+}
+*/
diff --git a/demos/snap-ad/site/index.html b/demos/snap-ad/site/index.html
new file mode 100644
index 0000000..cca41c6
--- /dev/null
+++ b/demos/snap-ad/site/index.html
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+ Snap! SVG
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/snap-ad/site/index.min.html b/demos/snap-ad/site/index.min.html
new file mode 100644
index 0000000..150dd28
--- /dev/null
+++ b/demos/snap-ad/site/index.min.html
@@ -0,0 +1,8947 @@
+
+
+
+
+
+
+
+
+ Snap! SVG
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/snap-ad/site/js/main.js b/demos/snap-ad/site/js/main.js
new file mode 100644
index 0000000..833e2cb
--- /dev/null
+++ b/demos/snap-ad/site/js/main.js
@@ -0,0 +1,8628 @@
+
+// Snap.svg 0.2.0
+//
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// build: 2013-12-23
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ┌────────────────────────────────────────────────────────────┐ \\
+// │ Eve 0.4.2 - JavaScript Events Library │ \\
+// ├────────────────────────────────────────────────────────────┤ \\
+// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
+// └────────────────────────────────────────────────────────────┘ \\
+
+(function (glob) {
+ var version = "0.4.2",
+ has = "hasOwnProperty",
+ separator = /[\.\/]/,
+ wildcard = "*",
+ fun = function () {},
+ numsort = function (a, b) {
+ return a - b;
+ },
+ current_event,
+ stop,
+ events = {n: {}},
+ /*\
+ * eve
+ [ method ]
+
+ * Fires event with given `name`, given scope and other parameters.
+
+ > Arguments
+
+ - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
+ - scope (object) context for the event handlers
+ - varargs (...) the rest of arguments will be sent to event handlers
+
+ = (object) array of returned values from the listeners
+ \*/
+ eve = function (name, scope) {
+ name = String(name);
+ var e = events,
+ oldstop = stop,
+ args = Array.prototype.slice.call(arguments, 2),
+ listeners = eve.listeners(name),
+ z = 0,
+ f = false,
+ l,
+ indexed = [],
+ queue = {},
+ out = [],
+ ce = current_event,
+ errors = [];
+ current_event = name;
+ stop = 0;
+ for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
+ indexed.push(listeners[i].zIndex);
+ if (listeners[i].zIndex < 0) {
+ queue[listeners[i].zIndex] = listeners[i];
+ }
+ }
+ indexed.sort(numsort);
+ while (indexed[z] < 0) {
+ l = queue[indexed[z++]];
+ out.push(l.apply(scope, args));
+ if (stop) {
+ stop = oldstop;
+ return out;
+ }
+ }
+ for (i = 0; i < ii; i++) {
+ l = listeners[i];
+ if ("zIndex" in l) {
+ if (l.zIndex == indexed[z]) {
+ out.push(l.apply(scope, args));
+ if (stop) {
+ break;
+ }
+ do {
+ z++;
+ l = queue[indexed[z]];
+ l && out.push(l.apply(scope, args));
+ if (stop) {
+ break;
+ }
+ } while (l)
+ } else {
+ queue[l.zIndex] = l;
+ }
+ } else {
+ out.push(l.apply(scope, args));
+ if (stop) {
+ break;
+ }
+ }
+ }
+ stop = oldstop;
+ current_event = ce;
+ return out.length ? out : null;
+ };
+ // Undocumented. Debug only.
+ eve._events = events;
+ /*\
+ * eve.listeners
+ [ method ]
+
+ * Internal method which gives you array of all event handlers that will be triggered by the given `name`.
+
+ > Arguments
+
+ - name (string) name of the event, dot (`.`) or slash (`/`) separated
+
+ = (array) array of event handlers
+ \*/
+ eve.listeners = function (name) {
+ var names = name.split(separator),
+ e = events,
+ item,
+ items,
+ k,
+ i,
+ ii,
+ j,
+ jj,
+ nes,
+ es = [e],
+ out = [];
+ for (i = 0, ii = names.length; i < ii; i++) {
+ nes = [];
+ for (j = 0, jj = es.length; j < jj; j++) {
+ e = es[j].n;
+ items = [e[names[i]], e[wildcard]];
+ k = 2;
+ while (k--) {
+ item = items[k];
+ if (item) {
+ nes.push(item);
+ out = out.concat(item.f || []);
+ }
+ }
+ }
+ es = nes;
+ }
+ return out;
+ };
+
+ /*\
+ * eve.on
+ [ method ]
+ **
+ * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
+ | eve.on("*.under.*", f);
+ | eve("mouse.under.floor"); // triggers f
+ * Use @eve to trigger the listener.
+ **
+ > Arguments
+ **
+ - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+ - f (function) event handler function
+ **
+ = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment.
+ > Example:
+ | eve.on("mouse", eatIt)(2);
+ | eve.on("mouse", scream);
+ | eve.on("mouse", catchIt)(1);
+ * This will ensure that `catchIt()` function will be called before `eatIt()`.
+ *
+ * If you want to put your handler before non-indexed handlers, specify a negative value.
+ * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
+ \*/
+ eve.on = function (name, f) {
+ name = String(name);
+ if (typeof f != "function") {
+ return function () {};
+ }
+ var names = name.split(separator),
+ e = events;
+ for (var i = 0, ii = names.length; i < ii; i++) {
+ e = e.n;
+ e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
+ }
+ e.f = e.f || [];
+ for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
+ return fun;
+ }
+ e.f.push(f);
+ return function (zIndex) {
+ if (+zIndex == +zIndex) {
+ f.zIndex = +zIndex;
+ }
+ };
+ };
+ /*\
+ * eve.f
+ [ method ]
+ **
+ * Returns function that will fire given event with optional arguments.
+ * Arguments that will be passed to the result function will be also
+ * concated to the list of final arguments.
+ | el.onclick = eve.f("click", 1, 2);
+ | eve.on("click", function (a, b, c) {
+ | console.log(a, b, c); // 1, 2, [event object]
+ | });
+ > Arguments
+ - event (string) event name
+ - varargs (…) and any other arguments
+ = (function) possible event handler function
+ \*/
+ eve.f = function (event) {
+ var attrs = [].slice.call(arguments, 1);
+ return function () {
+ eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
+ };
+ };
+ /*\
+ * eve.stop
+ [ method ]
+ **
+ * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
+ \*/
+ eve.stop = function () {
+ stop = 1;
+ };
+ /*\
+ * eve.nt
+ [ method ]
+ **
+ * Could be used inside event handler to figure out actual name of the event.
+ **
+ > Arguments
+ **
+ - subname (string) #optional subname of the event
+ **
+ = (string) name of the event, if `subname` is not specified
+ * or
+ = (boolean) `true`, if current event’s name contains `subname`
+ \*/
+ eve.nt = function (subname) {
+ if (subname) {
+ return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
+ }
+ return current_event;
+ };
+ /*\
+ * eve.nts
+ [ method ]
+ **
+ * Could be used inside event handler to figure out actual name of the event.
+ **
+ **
+ = (array) names of the event
+ \*/
+ eve.nts = function () {
+ return current_event.split(separator);
+ };
+ /*\
+ * eve.off
+ [ method ]
+ **
+ * Removes given function from the list of event listeners assigned to given name.
+ * If no arguments specified all the events will be cleared.
+ **
+ > Arguments
+ **
+ - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+ - f (function) event handler function
+ \*/
+ /*\
+ * eve.unbind
+ [ method ]
+ **
+ * See @eve.off
+ \*/
+ eve.off = eve.unbind = function (name, f) {
+ if (!name) {
+ eve._events = events = {n: {}};
+ return;
+ }
+ var names = name.split(separator),
+ e,
+ key,
+ splice,
+ i, ii, j, jj,
+ cur = [events];
+ for (i = 0, ii = names.length; i < ii; i++) {
+ for (j = 0; j < cur.length; j += splice.length - 2) {
+ splice = [j, 1];
+ e = cur[j].n;
+ if (names[i] != wildcard) {
+ if (e[names[i]]) {
+ splice.push(e[names[i]]);
+ }
+ } else {
+ for (key in e) if (e[has](key)) {
+ splice.push(e[key]);
+ }
+ }
+ cur.splice.apply(cur, splice);
+ }
+ }
+ for (i = 0, ii = cur.length; i < ii; i++) {
+ e = cur[i];
+ while (e.n) {
+ if (f) {
+ if (e.f) {
+ for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
+ e.f.splice(j, 1);
+ break;
+ }
+ !e.f.length && delete e.f;
+ }
+ for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+ var funcs = e.n[key].f;
+ for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
+ funcs.splice(j, 1);
+ break;
+ }
+ !funcs.length && delete e.n[key].f;
+ }
+ } else {
+ delete e.f;
+ for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+ delete e.n[key].f;
+ }
+ }
+ e = e.n;
+ }
+ }
+ };
+ /*\
+ * eve.once
+ [ method ]
+ **
+ * Binds given event handler with a given name to only run once then unbind itself.
+ | eve.once("login", f);
+ | eve("login"); // triggers f
+ | eve("login"); // no listeners
+ * Use @eve to trigger the listener.
+ **
+ > Arguments
+ **
+ - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+ - f (function) event handler function
+ **
+ = (function) same return function as @eve.on
+ \*/
+ eve.once = function (name, f) {
+ var f2 = function () {
+ eve.unbind(name, f2);
+ return f.apply(this, arguments);
+ };
+ return eve.on(name, f2);
+ };
+ /*\
+ * eve.version
+ [ property (string) ]
+ **
+ * Current version of the library.
+ \*/
+ eve.version = version;
+ eve.toString = function () {
+ return "You are running Eve " + version;
+ };
+ (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
+})(this);
+
+(function (glob, factory) {
+ // AMD support
+ if (typeof define === "function" && define.amd) {
+ // Define as an anonymous module
+ define('snap',["eve"], function( eve ) {
+ return factory(glob, eve);
+ });
+ } else {
+ // Browser globals (glob is window)
+ // Snap adds itself to window
+ factory(glob, glob.eve);
+ }
+}(this, function (window, eve) {
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+var mina = (function (eve) {
+ var animations = {},
+ requestAnimFrame = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function (callback) {
+ setTimeout(callback, 16);
+ },
+ isArray = Array.isArray || function (a) {
+ return a instanceof Array ||
+ Object.prototype.toString.call(a) == "[object Array]";
+ },
+ idgen = 0,
+ idprefix = "M" + (+new Date).toString(36),
+ ID = function () {
+ return idprefix + (idgen++).toString(36);
+ },
+ diff = function (a, b, A, B) {
+ if (isArray(a)) {
+ res = [];
+ for (var i = 0, ii = a.length; i < ii; i++) {
+ res[i] = diff(a[i], b, A[i], B);
+ }
+ return res;
+ }
+ var dif = (A - a) / (B - b);
+ return function (bb) {
+ return a + dif * (bb - b);
+ };
+ },
+ timer = Date.now || function () {
+ return +new Date;
+ },
+ sta = function (val) {
+ var a = this;
+ if (val == null) {
+ return a.s;
+ }
+ var ds = a.s - val;
+ a.b += a.dur * ds;
+ a.B += a.dur * ds;
+ a.s = val;
+ },
+ speed = function (val) {
+ var a = this;
+ if (val == null) {
+ return a.spd;
+ }
+ a.spd = val;
+ },
+ duration = function (val) {
+ var a = this;
+ if (val == null) {
+ return a.dur;
+ }
+ a.s = a.s * val / a.dur;
+ a.dur = val;
+ },
+ stopit = function () {
+ var a = this;
+ delete animations[a.id];
+ eve("mina.stop." + a.id, a);
+ },
+ pause = function () {
+ var a = this;
+ if (a.pdif) {
+ return;
+ }
+ delete animations[a.id];
+ a.pdif = a.get() - a.b;
+ },
+ resume = function () {
+ var a = this;
+ if (!a.pdif) {
+ return;
+ }
+ a.b = a.get() - a.pdif;
+ delete a.pdif;
+ animations[a.id] = a;
+ },
+ frame = function () {
+ var len = 0;
+ for (var i in animations) if (animations.hasOwnProperty(i)) {
+ var a = animations[i],
+ b = a.get(),
+ res;
+ len++;
+ a.s = (b - a.b) / (a.dur / a.spd);
+ if (a.s >= 1) {
+ delete animations[i];
+ a.s = 1;
+ len--;
+ (function (a) {
+ setTimeout(function () {
+ eve("mina.finish." + a.id, a);
+ });
+ }(a));
+ }
+ if (isArray(a.start)) {
+ res = [];
+ for (var j = 0, jj = a.start.length; j < jj; j++) {
+ res[j] = +a.start[j] +
+ (a.end[j] - a.start[j]) * a.easing(a.s);
+ }
+ } else {
+ res = +a.start + (a.end - a.start) * a.easing(a.s);
+ }
+ a.set(res);
+ }
+ len && requestAnimFrame(frame);
+ },
+ // SIERRA Unfamiliar with the word _slave_ in this context. Also, I don't know what _gereal_ means. Do you mean _general_?
+ /*\
+ * mina
+ [ method ]
+ **
+ * Generic animation of numbers
+ **
+ - a (number) start _slave_ number
+ - A (number) end _slave_ number
+ - b (number) start _master_ number (start time in general case)
+ - B (number) end _master_ number (end time in gereal case)
+ - get (function) getter of _master_ number (see @mina.time)
+ - set (function) setter of _slave_ number
+ - easing (function) #optional easing function, default is @mina.linear
+ = (object) animation descriptor
+ o {
+ o id (string) animation id,
+ o start (number) start _slave_ number,
+ o end (number) end _slave_ number,
+ o b (number) start _master_ number,
+ o s (number) animation status (0..1),
+ o dur (number) animation duration,
+ o spd (number) animation speed,
+ o get (function) getter of _master_ number (see @mina.time),
+ o set (function) setter of _slave_ number,
+ o easing (function) easing function, default is @mina.linear,
+ o status (function) status getter/setter,
+ o speed (function) speed getter/setter,
+ o duration (function) duration getter/setter,
+ o stop (function) animation stopper
+ o }
+ \*/
+ mina = function (a, A, b, B, get, set, easing) {
+ var anim = {
+ id: ID(),
+ start: a,
+ end: A,
+ b: b,
+ s: 0,
+ dur: B - b,
+ spd: 1,
+ get: get,
+ set: set,
+ easing: easing || mina.linear,
+ status: sta,
+ speed: speed,
+ duration: duration,
+ stop: stopit,
+ pause: pause,
+ resume: resume
+ };
+ animations[anim.id] = anim;
+ var len = 0, i;
+ for (i in animations) if (animations.hasOwnProperty(i)) {
+ len++;
+ if (len == 2) {
+ break;
+ }
+ }
+ len == 1 && requestAnimFrame(frame);
+ return anim;
+ };
+ /*\
+ * mina.time
+ [ method ]
+ **
+ * Returns the current time. Equivalent to:
+ | function () {
+ | return (new Date).getTime();
+ | }
+ \*/
+ mina.time = timer;
+ /*\
+ * mina.getById
+ [ method ]
+ **
+ * Returns an animation by its id
+ - id (string) animation's id
+ = (object) See @mina
+ \*/
+ mina.getById = function (id) {
+ return animations[id] || null;
+ };
+
+ /*\
+ * mina.linear
+ [ method ]
+ **
+ * Default linear easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.linear = function (n) {
+ return n;
+ };
+ /*\
+ * mina.easeout
+ [ method ]
+ **
+ * Easeout easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.easeout = function (n) {
+ return Math.pow(n, 1.7);
+ };
+ /*\
+ * mina.easein
+ [ method ]
+ **
+ * Easein easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.easein = function (n) {
+ return Math.pow(n, .48);
+ };
+ /*\
+ * mina.easeinout
+ [ method ]
+ **
+ * Easeinout easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.easeinout = function (n) {
+ if (n == 1) {
+ return 1;
+ }
+ if (n == 0) {
+ return 0;
+ }
+ var q = .48 - n / 1.04,
+ Q = Math.sqrt(.1734 + q * q),
+ x = Q - q,
+ X = Math.pow(Math.abs(x), 1 / 3) * (x < 0 ? -1 : 1),
+ y = -Q - q,
+ Y = Math.pow(Math.abs(y), 1 / 3) * (y < 0 ? -1 : 1),
+ t = X + Y + .5;
+ return (1 - t) * 3 * t * t + t * t * t;
+ };
+ /*\
+ * mina.backin
+ [ method ]
+ **
+ * Backin easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.backin = function (n) {
+ if (n == 1) {
+ return 1;
+ }
+ var s = 1.70158;
+ return n * n * ((s + 1) * n - s);
+ };
+ /*\
+ * mina.backout
+ [ method ]
+ **
+ * Backout easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.backout = function (n) {
+ if (n == 0) {
+ return 0;
+ }
+ n = n - 1;
+ var s = 1.70158;
+ return n * n * ((s + 1) * n + s) + 1;
+ };
+ /*\
+ * mina.elastic
+ [ method ]
+ **
+ * Elastic easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.elastic = function (n) {
+ if (n == !!n) {
+ return n;
+ }
+ return Math.pow(2, -10 * n) * Math.sin((n - .075) *
+ (2 * Math.PI) / .3) + 1;
+ };
+ /*\
+ * mina.bounce
+ [ method ]
+ **
+ * Bounce easing
+ - n (number) input 0..1
+ = (number) output 0..1
+ \*/
+ mina.bounce = function (n) {
+ var s = 7.5625,
+ p = 2.75,
+ l;
+ if (n < (1 / p)) {
+ l = s * n * n;
+ } else {
+ if (n < (2 / p)) {
+ n -= (1.5 / p);
+ l = s * n * n + .75;
+ } else {
+ if (n < (2.5 / p)) {
+ n -= (2.25 / p);
+ l = s * n * n + .9375;
+ } else {
+ n -= (2.625 / p);
+ l = s * n * n + .984375;
+ }
+ }
+ }
+ return l;
+ };
+ window.mina = mina;
+ return mina;
+})(typeof eve == "undefined" ? function () {} : eve);
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var Snap = (function() {
+Snap.version = "0.2.0";
+/*\
+ * Snap
+ [ method ]
+ **
+ * Creates a drawing surface or wraps existing SVG element.
+ **
+ - width (number|string) width of surface
+ - height (number|string) height of surface
+ * or
+ - DOM (SVGElement) element to be wrapped into Snap structure
+ * or
+ - query (string) CSS query selector
+ = (object) @Element
+\*/
+function Snap(w, h) {
+ if (w) {
+ if (w.tagName) {
+ return wrap(w);
+ }
+ if (w instanceof Element) {
+ return w;
+ }
+ if (h == null) {
+ w = glob.doc.querySelector(w);
+ return wrap(w);
+ }
+ }
+ w = w == null ? "100%" : w;
+ h = h == null ? "100%" : h;
+ return new Paper(w, h);
+}
+Snap.toString = function () {
+ return "Snap v" + this.version;
+};
+Snap._ = {};
+var glob = {
+ win: window,
+ doc: window.document
+};
+Snap._.glob = glob;
+var has = "hasOwnProperty",
+ Str = String,
+ toFloat = parseFloat,
+ toInt = parseInt,
+ math = Math,
+ mmax = math.max,
+ mmin = math.min,
+ abs = math.abs,
+ pow = math.pow,
+ PI = math.PI,
+ round = math.round,
+ E = "",
+ S = " ",
+ objectToString = Object.prototype.toString,
+ ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
+ colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,
+ bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
+ reURLValue = /^url\(#?([^)]+)\)$/,
+ spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
+ separator = new RegExp("[," + spaces + "]+"),
+ whitespace = new RegExp("[" + spaces + "]", "g"),
+ commaSpaces = new RegExp("[" + spaces + "]*,[" + spaces + "]*"),
+ hsrg = {hs: 1, rg: 1},
+ pathCommand = new RegExp("([a-z])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)", "ig"),
+ tCommand = new RegExp("([rstm])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)", "ig"),
+ pathValues = new RegExp("(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[" + spaces + "]*,?[" + spaces + "]*", "ig"),
+ idgen = 0,
+ idprefix = "S" + (+new Date).toString(36),
+ ID = function () {
+ return idprefix + (idgen++).toString(36);
+ },
+ xlink = "http://www.w3.org/1999/xlink",
+ xmlns = "http://www.w3.org/2000/svg",
+ hub = {},
+ URL = Snap.url = function (url) {
+ return "url('#" + url + "')";
+ };
+
+function $(el, attr) {
+ if (attr) {
+ if (typeof el == "string") {
+ el = $(el);
+ }
+ if (typeof attr == "string") {
+ if (attr.substring(0, 6) == "xlink:") {
+ return el.getAttributeNS(xlink, attr.substring(6));
+ }
+ if (attr.substring(0, 4) == "xml:") {
+ return el.getAttributeNS(xmlns, attr.substring(4));
+ }
+ return el.getAttribute(attr);
+ }
+ for (var key in attr) if (attr[has](key)) {
+ var val = Str(attr[key]);
+ if (val) {
+ if (key.substring(0, 6) == "xlink:") {
+ el.setAttributeNS(xlink, key.substring(6), val);
+ } else if (key.substring(0, 4) == "xml:") {
+ el.setAttributeNS(xmlns, key.substring(4), val);
+ } else {
+ el.setAttribute(key, val);
+ }
+ } else {
+ el.removeAttribute(key);
+ }
+ }
+ } else {
+ el = glob.doc.createElementNS(xmlns, el);
+ // el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
+ }
+ return el;
+}
+Snap._.$ = $;
+Snap._.id = ID;
+function getAttrs(el) {
+ var attrs = el.attributes,
+ name,
+ out = {};
+ for (var i = 0; i < attrs.length; i++) {
+ if (attrs[i].namespaceURI == xlink) {
+ name = "xlink:";
+ } else {
+ name = "";
+ }
+ name += attrs[i].name;
+ out[name] = attrs[i].textContent;
+ }
+ return out;
+}
+function is(o, type) {
+ type = Str.prototype.toLowerCase.call(type);
+ if (type == "finite") {
+ return isFinite(o);
+ }
+ if (type == "array" &&
+ (o instanceof Array || Array.isArray && Array.isArray(o))) {
+ return true;
+ }
+ return (type == "null" && o === null) ||
+ (type == typeof o && o !== null) ||
+ (type == "object" && o === Object(o)) ||
+ objectToString.call(o).slice(8, -1).toLowerCase() == type;
+}
+/*\
+ * Snap.format
+ [ method ]
+ **
+ * Replaces construction of type `{}` to the corresponding argument
+ **
+ - token (string) string to format
+ - json (object) object which properties are used as a replacement
+ = (string) formatted string
+ > Usage
+ | // this draws a rectangular shape equivalent to "M10,20h40v50h-40z"
+ | paper.path(Snap.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
+ | x: 10,
+ | y: 20,
+ | dim: {
+ | width: 40,
+ | height: 50,
+ | "negative width": -40
+ | }
+ | }));
+\*/
+Snap.format = (function () {
+ var tokenRegex = /\{([^\}]+)\}/g,
+ objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
+ replacer = function (all, key, obj) {
+ var res = obj;
+ key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
+ name = name || quotedName;
+ if (res) {
+ if (name in res) {
+ res = res[name];
+ }
+ typeof res == "function" && isFunc && (res = res());
+ }
+ });
+ res = (res == null || res == obj ? all : res) + "";
+ return res;
+ };
+ return function (str, obj) {
+ return Str(str).replace(tokenRegex, function (all, key) {
+ return replacer(all, key, obj);
+ });
+ };
+})();
+var preload = (function () {
+ function onerror() {
+ this.parentNode.removeChild(this);
+ }
+ return function (src, f) {
+ var img = glob.doc.createElement("img"),
+ body = glob.doc.body;
+ img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
+ img.onload = function () {
+ f.call(img);
+ img.onload = img.onerror = null;
+ body.removeChild(img);
+ };
+ img.onerror = onerror;
+ body.appendChild(img);
+ img.src = src;
+ };
+}());
+function clone(obj) {
+ if (typeof obj == "function" || Object(obj) !== obj) {
+ return obj;
+ }
+ var res = new obj.constructor;
+ for (var key in obj) if (obj[has](key)) {
+ res[key] = clone(obj[key]);
+ }
+ return res;
+}
+Snap._.clone = clone;
+function repush(array, item) {
+ for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
+ return array.push(array.splice(i, 1)[0]);
+ }
+}
+function cacher(f, scope, postprocessor) {
+ function newf() {
+ var arg = Array.prototype.slice.call(arguments, 0),
+ args = arg.join("\u2400"),
+ cache = newf.cache = newf.cache || {},
+ count = newf.count = newf.count || [];
+ if (cache[has](args)) {
+ repush(count, args);
+ return postprocessor ? postprocessor(cache[args]) : cache[args];
+ }
+ count.length >= 1e3 && delete cache[count.shift()];
+ count.push(args);
+ cache[args] = f.apply(scope, arg);
+ return postprocessor ? postprocessor(cache[args]) : cache[args];
+ }
+ return newf;
+}
+Snap._.cacher = cacher;
+function angle(x1, y1, x2, y2, x3, y3) {
+ if (x3 == null) {
+ var x = x1 - x2,
+ y = y1 - y2;
+ if (!x && !y) {
+ return 0;
+ }
+ return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
+ } else {
+ return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3);
+ }
+}
+function rad(deg) {
+ return deg % 360 * PI / 180;
+}
+function deg(rad) {
+ return rad * 180 / PI % 360;
+}
+function x_y() {
+ return this.x + S + this.y;
+}
+function x_y_w_h() {
+ return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
+}
+
+/*\
+ * Snap.rad
+ [ method ]
+ **
+ * Transform angle to radians
+ - deg (number) angle in degrees
+ = (number) angle in radians
+\*/
+Snap.rad = rad;
+/*\
+ * Snap.deg
+ [ method ]
+ **
+ * Transform angle to degrees
+ - rad (number) angle in radians
+ = (number) angle in degrees
+\*/
+Snap.deg = deg;
+// SIERRA for which point is the angle calculated?
+/*\
+ * Snap.angle
+ [ method ]
+ **
+ * Returns an angle between two or three points
+ > Parameters
+ - x1 (number) x coord of first point
+ - y1 (number) y coord of first point
+ - x2 (number) x coord of second point
+ - y2 (number) y coord of second point
+ - x3 (number) #optional x coord of third point
+ - y3 (number) #optional y coord of third point
+ = (number) angle in degrees
+\*/
+Snap.angle = angle;
+/*\
+ * Snap.is
+ [ method ]
+ **
+ * Handy replacement for the `typeof` operator
+ - o (…) any object or primitive
+ - type (string) name of the type, e.g., `string`, `function`, `number`, etc.
+ = (boolean) `true` if given value is of given type
+\*/
+Snap.is = is;
+/*\
+ * Snap.snapTo
+ [ method ]
+ **
+ * Snaps given value to given grid
+ - values (array|number) given array of values or step of the grid
+ - value (number) value to adjust
+ - tolerance (number) #optional maximum distance to the target value that would trigger the snap. Default is `10`.
+ = (number) adjusted value
+\*/
+Snap.snapTo = function (values, value, tolerance) {
+ tolerance = is(tolerance, "finite") ? tolerance : 10;
+ if (is(values, "array")) {
+ var i = values.length;
+ while (i--) if (abs(values[i] - value) <= tolerance) {
+ return values[i];
+ }
+ } else {
+ values = +values;
+ var rem = value % values;
+ if (rem < tolerance) {
+ return value - rem;
+ }
+ if (rem > values - tolerance) {
+ return value - rem + values;
+ }
+ }
+ return value;
+};
+
+// MATRIX
+function Matrix(a, b, c, d, e, f) {
+ if (b == null && objectToString.call(a) == "[object SVGMatrix]") {
+ this.a = a.a;
+ this.b = a.b;
+ this.c = a.c;
+ this.d = a.d;
+ this.e = a.e;
+ this.f = a.f;
+ return;
+ }
+ if (a != null) {
+ this.a = +a;
+ this.b = +b;
+ this.c = +c;
+ this.d = +d;
+ this.e = +e;
+ this.f = +f;
+ } else {
+ this.a = 1;
+ this.b = 0;
+ this.c = 0;
+ this.d = 1;
+ this.e = 0;
+ this.f = 0;
+ }
+}
+(function (matrixproto) {
+ /*\
+ * Matrix.add
+ [ method ]
+ **
+ * Adds the given matrix to existing one
+ - a (number)
+ - b (number)
+ - c (number)
+ - d (number)
+ - e (number)
+ - f (number)
+ * or
+ - matrix (object) @Matrix
+ \*/
+ matrixproto.add = function (a, b, c, d, e, f) {
+ var out = [[], [], []],
+ m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
+ matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
+ x, y, z, res;
+
+ if (a && a instanceof Matrix) {
+ matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
+ }
+
+ for (x = 0; x < 3; x++) {
+ for (y = 0; y < 3; y++) {
+ res = 0;
+ for (z = 0; z < 3; z++) {
+ res += m[x][z] * matrix[z][y];
+ }
+ out[x][y] = res;
+ }
+ }
+ this.a = out[0][0];
+ this.b = out[1][0];
+ this.c = out[0][1];
+ this.d = out[1][1];
+ this.e = out[0][2];
+ this.f = out[1][2];
+ return this;
+ };
+ /*\
+ * Matrix.invert
+ [ method ]
+ **
+ * Returns an inverted version of the matrix
+ = (object) @Matrix
+ \*/
+ matrixproto.invert = function () {
+ var me = this,
+ x = me.a * me.d - me.b * me.c;
+ return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
+ };
+ /*\
+ * Matrix.clone
+ [ method ]
+ **
+ * Returns a copy of the matrix
+ = (object) @Matrix
+ \*/
+ matrixproto.clone = function () {
+ return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
+ };
+ /*\
+ * Matrix.translate
+ [ method ]
+ **
+ * Translate the matrix
+ - x (number) horizontal offset distance
+ - y (number) vertical offset distance
+ \*/
+ matrixproto.translate = function (x, y) {
+ return this.add(1, 0, 0, 1, x, y);
+ };
+ /*\
+ * Matrix.scale
+ [ method ]
+ **
+ * Scales the matrix
+ - x (number) amount to be scaled, with `1` resulting in no change
+ - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.)
+ - cx (number) #optional horizontal origin point from which to scale
+ - cy (number) #optional vertical origin point from which to scale
+ * Default cx, cy is the middle point of the element.
+ \*/
+ matrixproto.scale = function (x, y, cx, cy) {
+ y == null && (y = x);
+ (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
+ this.add(x, 0, 0, y, 0, 0);
+ (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
+ return this;
+ };
+ /*\
+ * Matrix.rotate
+ [ method ]
+ **
+ * Rotates the matrix
+ - a (number) angle of rotation, in degrees
+ - x (number) horizontal origin point from which to rotate
+ - y (number) vertical origin point from which to rotate
+ \*/
+ matrixproto.rotate = function (a, x, y) {
+ a = rad(a);
+ x = x || 0;
+ y = y || 0;
+ var cos = +math.cos(a).toFixed(9),
+ sin = +math.sin(a).toFixed(9);
+ this.add(cos, sin, -sin, cos, x, y);
+ return this.add(1, 0, 0, 1, -x, -y);
+ };
+ /*\
+ * Matrix.x
+ [ method ]
+ **
+ * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y
+ - x (number)
+ - y (number)
+ = (number) x
+ \*/
+ matrixproto.x = function (x, y) {
+ return x * this.a + y * this.c + this.e;
+ };
+ /*\
+ * Matrix.y
+ [ method ]
+ **
+ * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x
+ - x (number)
+ - y (number)
+ = (number) y
+ \*/
+ matrixproto.y = function (x, y) {
+ return x * this.b + y * this.d + this.f;
+ };
+ matrixproto.get = function (i) {
+ return +this[Str.fromCharCode(97 + i)].toFixed(4);
+ };
+ matrixproto.toString = function () {
+ return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")";
+ };
+ matrixproto.offset = function () {
+ return [this.e.toFixed(4), this.f.toFixed(4)];
+ };
+ function norm(a) {
+ return a[0] * a[0] + a[1] * a[1];
+ }
+ function normalize(a) {
+ var mag = math.sqrt(norm(a));
+ a[0] && (a[0] /= mag);
+ a[1] && (a[1] /= mag);
+ }
+ /*\
+ * Matrix.split
+ [ method ]
+ **
+ * Splits matrix into primitive transformations
+ = (object) in format:
+ o dx (number) translation by x
+ o dy (number) translation by y
+ o scalex (number) scale by x
+ o scaley (number) scale by y
+ o shear (number) shear
+ o rotate (number) rotation in deg
+ o isSimple (boolean) could it be represented via simple transformations
+ \*/
+ matrixproto.split = function () {
+ var out = {};
+ // translation
+ out.dx = this.e;
+ out.dy = this.f;
+
+ // scale and shear
+ var row = [[this.a, this.c], [this.b, this.d]];
+ out.scalex = math.sqrt(norm(row[0]));
+ normalize(row[0]);
+
+ out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
+ row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
+
+ out.scaley = math.sqrt(norm(row[1]));
+ normalize(row[1]);
+ out.shear /= out.scaley;
+
+ // rotation
+ var sin = -row[0][1],
+ cos = row[1][1];
+ if (cos < 0) {
+ out.rotate = deg(math.acos(cos));
+ if (sin < 0) {
+ out.rotate = 360 - out.rotate;
+ }
+ } else {
+ out.rotate = deg(math.asin(sin));
+ }
+
+ out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
+ out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
+ out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
+ return out;
+ };
+ /*\
+ * Matrix.toTransformString
+ [ method ]
+ **
+ * Returns transform string that represents given matrix
+ = (string) transform string
+ \*/
+ matrixproto.toTransformString = function (shorter) {
+ var s = shorter || this.split();
+ if (s.isSimple) {
+ s.scalex = +s.scalex.toFixed(4);
+ s.scaley = +s.scaley.toFixed(4);
+ s.rotate = +s.rotate.toFixed(4);
+ return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) +
+ (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
+ (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E);
+ } else {
+ return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
+ }
+ };
+})(Matrix.prototype);
+/*\
+ * Snap.Matrix
+ [ method ]
+ **
+ * Utility method
+ **
+ * Returns a matrix based on the given parameters
+ - a (number)
+ - b (number)
+ - c (number)
+ - d (number)
+ - e (number)
+ - f (number)
+ * or
+ - svgMatrix (SVGMatrix)
+ = (object) @Matrix
+\*/
+Snap.Matrix = Matrix;
+// Colour
+/*\
+ * Snap.getRGB
+ [ method ]
+ **
+ * Parses color string as RGB object
+ - color (string) color string in one of the following formats:
+ #
+ # - Color name (
red
, green
, cornflowerblue
, etc)
+ # - #••• — shortened HTML color: (
#000
, #fc0
, etc.)
+ # - #•••••• — full length HTML color: (
#000000
, #bd2300
)
+ # - rgb(•••, •••, •••) — red, green and blue channels values: (
rgb(200, 100, 0)
)
+ # - rgba(•••, •••, •••, •••) — also with opacity
+ # - rgb(•••%, •••%, •••%) — same as above, but in %: (
rgb(100%, 175%, 0%)
)
+ # - rgba(•••%, •••%, •••%, •••%) — also with opacity
+ # - hsb(•••, •••, •••) — hue, saturation and brightness values: (
hsb(0.5, 0.25, 1)
)
+ # - hsba(•••, •••, •••, •••) — also with opacity
+ # - hsb(•••%, •••%, •••%) — same as above, but in %
+ # - hsba(•••%, •••%, •••%, •••%) — also with opacity
+ # - hsl(•••, •••, •••) — hue, saturation and luminosity values: (
hsb(0.5, 0.25, 0.5)
)
+ # - hsla(•••, •••, •••, •••) — also with opacity
+ # - hsl(•••%, •••%, •••%) — same as above, but in %
+ # - hsla(•••%, •••%, •••%, •••%) — also with opacity
+ #
+ * Note that `%` can be used any time: `rgb(20%, 255, 50%)`.
+ = (object) RGB object in the following format:
+ o {
+ o r (number) red,
+ o g (number) green,
+ o b (number) blue,
+ o hex (string) color in HTML/CSS format: #••••••,
+ o error (boolean) true if string can't be parsed
+ o }
+\*/
+Snap.getRGB = cacher(function (colour) {
+ if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
+ return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString};
+ }
+ if (colour == "none") {
+ return {r: -1, g: -1, b: -1, hex: "none", toString: rgbtoString};
+ }
+ !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
+ if (!colour) {
+ return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString};
+ }
+ var res,
+ red,
+ green,
+ blue,
+ opacity,
+ t,
+ values,
+ rgb = colour.match(colourRegExp);
+ if (rgb) {
+ if (rgb[2]) {
+ blue = toInt(rgb[2].substring(5), 16);
+ green = toInt(rgb[2].substring(3, 5), 16);
+ red = toInt(rgb[2].substring(1, 3), 16);
+ }
+ if (rgb[3]) {
+ blue = toInt((t = rgb[3].charAt(3)) + t, 16);
+ green = toInt((t = rgb[3].charAt(2)) + t, 16);
+ red = toInt((t = rgb[3].charAt(1)) + t, 16);
+ }
+ if (rgb[4]) {
+ values = rgb[4].split(commaSpaces);
+ red = toFloat(values[0]);
+ values[0].slice(-1) == "%" && (red *= 2.55);
+ green = toFloat(values[1]);
+ values[1].slice(-1) == "%" && (green *= 2.55);
+ blue = toFloat(values[2]);
+ values[2].slice(-1) == "%" && (blue *= 2.55);
+ rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
+ values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+ }
+ if (rgb[5]) {
+ values = rgb[5].split(commaSpaces);
+ red = toFloat(values[0]);
+ values[0].slice(-1) == "%" && (red /= 100);
+ green = toFloat(values[1]);
+ values[1].slice(-1) == "%" && (green /= 100);
+ blue = toFloat(values[2]);
+ values[2].slice(-1) == "%" && (blue /= 100);
+ (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+ rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
+ values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+ return Snap.hsb2rgb(red, green, blue, opacity);
+ }
+ if (rgb[6]) {
+ values = rgb[6].split(commaSpaces);
+ red = toFloat(values[0]);
+ values[0].slice(-1) == "%" && (red /= 100);
+ green = toFloat(values[1]);
+ values[1].slice(-1) == "%" && (green /= 100);
+ blue = toFloat(values[2]);
+ values[2].slice(-1) == "%" && (blue /= 100);
+ (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+ rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
+ values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+ return Snap.hsl2rgb(red, green, blue, opacity);
+ }
+ red = mmin(math.round(red), 255);
+ green = mmin(math.round(green), 255);
+ blue = mmin(math.round(blue), 255);
+ opacity = mmin(mmax(opacity, 0), 1);
+ rgb = {r: red, g: green, b: blue, toString: rgbtoString};
+ rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
+ rgb.opacity = is(opacity, "finite") ? opacity : 1;
+ return rgb;
+ }
+ return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString};
+}, Snap);
+// SIERRA It seems odd that the following 3 conversion methods are not expressed as .this2that(), like the others.
+/*\
+ * Snap.hsb
+ [ method ]
+ **
+ * Converts HSB values to a hex representation of the color
+ - h (number) hue
+ - s (number) saturation
+ - b (number) value or brightness
+ = (string) hex representation of the color
+\*/
+Snap.hsb = cacher(function (h, s, b) {
+ return Snap.hsb2rgb(h, s, b).hex;
+});
+/*\
+ * Snap.hsl
+ [ method ]
+ **
+ * Converts HSL values to a hex representation of the color
+ - h (number) hue
+ - s (number) saturation
+ - l (number) luminosity
+ = (string) hex representation of the color
+\*/
+Snap.hsl = cacher(function (h, s, l) {
+ return Snap.hsl2rgb(h, s, l).hex;
+});
+/*\
+ * Snap.rgb
+ [ method ]
+ **
+ * Converts RGB values to a hex representation of the color
+ - r (number) red
+ - g (number) green
+ - b (number) blue
+ = (string) hex representation of the color
+\*/
+Snap.rgb = cacher(function (r, g, b, o) {
+ if (is(o, "finite")) {
+ var round = math.round;
+ return "rgba(" + [round(r), round(g), round(b), +o.toFixed(2)] + ")";
+ }
+ return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
+});
+var toHex = function (color) {
+ var i = glob.doc.getElementsByTagName("head")[0],
+ red = "rgb(255, 0, 0)";
+ toHex = cacher(function (color) {
+ if (color.toLowerCase() == "red") {
+ return red;
+ }
+ i.style.color = red;
+ i.style.color = color;
+ var out = glob.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
+ return out == red ? null : out;
+ });
+ return toHex(color);
+},
+hsbtoString = function () {
+ return "hsb(" + [this.h, this.s, this.b] + ")";
+},
+hsltoString = function () {
+ return "hsl(" + [this.h, this.s, this.l] + ")";
+},
+rgbtoString = function () {
+ return this.opacity == 1 || this.opacity == null ?
+ this.hex :
+ "rgba(" + [this.r, this.g, this.b, this.opacity] + ")";
+},
+prepareRGB = function (r, g, b) {
+ if (g == null && is(r, "object") && "r" in r && "g" in r && "b" in r) {
+ b = r.b;
+ g = r.g;
+ r = r.r;
+ }
+ if (g == null && is(r, string)) {
+ var clr = Snap.getRGB(r);
+ r = clr.r;
+ g = clr.g;
+ b = clr.b;
+ }
+ if (r > 1 || g > 1 || b > 1) {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ }
+
+ return [r, g, b];
+},
+packageRGB = function (r, g, b, o) {
+ r = math.round(r * 255);
+ g = math.round(g * 255);
+ b = math.round(b * 255);
+ var rgb = {
+ r: r,
+ g: g,
+ b: b,
+ opacity: is(o, "finite") ? o : 1,
+ hex: Snap.rgb(r, g, b),
+ toString: rgbtoString
+ };
+ is(o, "finite") && (rgb.opacity = o);
+ return rgb;
+};
+// SIERRA Clarify if Snap does not support consolidated HSLA/RGBA colors. E.g., can you specify a semi-transparent value for Snap.filter.shadow()?
+/*\
+ * Snap.color
+ [ method ]
+ **
+ * Parses the color string and returns an object featuring the color's component values
+ - clr (string) color string in one of the supported formats (see @Snap.getRGB)
+ = (object) Combined RGB/HSB object in the following format:
+ o {
+ o r (number) red,
+ o g (number) green,
+ o b (number) blue,
+ o hex (string) color in HTML/CSS format: #••••••,
+ o error (boolean) `true` if string can't be parsed,
+ o h (number) hue,
+ o s (number) saturation,
+ o v (number) value (brightness),
+ o l (number) lightness
+ o }
+\*/
+Snap.color = function (clr) {
+ var rgb;
+ if (is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
+ rgb = Snap.hsb2rgb(clr);
+ clr.r = rgb.r;
+ clr.g = rgb.g;
+ clr.b = rgb.b;
+ clr.opacity = 1;
+ clr.hex = rgb.hex;
+ } else if (is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
+ rgb = Snap.hsl2rgb(clr);
+ clr.r = rgb.r;
+ clr.g = rgb.g;
+ clr.b = rgb.b;
+ clr.opacity = 1;
+ clr.hex = rgb.hex;
+ } else {
+ if (is(clr, "string")) {
+ clr = Snap.getRGB(clr);
+ }
+ if (is(clr, "object") && "r" in clr && "g" in clr && "b" in clr && !("error" in clr)) {
+ rgb = Snap.rgb2hsl(clr);
+ clr.h = rgb.h;
+ clr.s = rgb.s;
+ clr.l = rgb.l;
+ rgb = Snap.rgb2hsb(clr);
+ clr.v = rgb.b;
+ } else {
+ clr = {hex: "none"};
+ clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
+ clr.error = 1;
+ }
+ }
+ clr.toString = rgbtoString;
+ return clr;
+};
+/*\
+ * Snap.hsb2rgb
+ [ method ]
+ **
+ * Converts HSB values to an RGB object
+ - h (number) hue
+ - s (number) saturation
+ - v (number) value or brightness
+ = (object) RGB object in the following format:
+ o {
+ o r (number) red,
+ o g (number) green,
+ o b (number) blue,
+ o hex (string) color in HTML/CSS format: #••••••
+ o }
+\*/
+Snap.hsb2rgb = function (h, s, v, o) {
+ if (is(h, "object") && "h" in h && "s" in h && "b" in h) {
+ v = h.b;
+ s = h.s;
+ h = h.h;
+ o = h.o;
+ }
+ h *= 360;
+ var R, G, B, X, C;
+ h = (h % 360) / 60;
+ C = v * s;
+ X = C * (1 - abs(h % 2 - 1));
+ R = G = B = v - C;
+
+ h = ~~h;
+ R += [C, X, 0, 0, X, C][h];
+ G += [X, C, C, X, 0, 0][h];
+ B += [0, 0, X, C, C, X][h];
+ return packageRGB(R, G, B, o);
+};
+/*\
+ * Snap.hsl2rgb
+ [ method ]
+ **
+ * Converts HSL values to an RGB object
+ - h (number) hue
+ - s (number) saturation
+ - l (number) luminosity
+ = (object) RGB object in the following format:
+ o {
+ o r (number) red,
+ o g (number) green,
+ o b (number) blue,
+ o hex (string) color in HTML/CSS format: #••••••
+ o }
+\*/
+Snap.hsl2rgb = function (h, s, l, o) {
+ if (is(h, "object") && "h" in h && "s" in h && "l" in h) {
+ l = h.l;
+ s = h.s;
+ h = h.h;
+ }
+ if (h > 1 || s > 1 || l > 1) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ }
+ h *= 360;
+ var R, G, B, X, C;
+ h = (h % 360) / 60;
+ C = 2 * s * (l < .5 ? l : 1 - l);
+ X = C * (1 - abs(h % 2 - 1));
+ R = G = B = l - C / 2;
+
+ h = ~~h;
+ R += [C, X, 0, 0, X, C][h];
+ G += [X, C, C, X, 0, 0][h];
+ B += [0, 0, X, C, C, X][h];
+ return packageRGB(R, G, B, o);
+};
+/*\
+ * Snap.rgb2hsb
+ [ method ]
+ **
+ * Converts RGB values to an HSB object
+ - r (number) red
+ - g (number) green
+ - b (number) blue
+ = (object) HSB object in the following format:
+ o {
+ o h (number) hue,
+ o s (number) saturation,
+ o b (number) brightness
+ o }
+\*/
+Snap.rgb2hsb = function (r, g, b) {
+ b = prepareRGB(r, g, b);
+ r = b[0];
+ g = b[1];
+ b = b[2];
+
+ var H, S, V, C;
+ V = mmax(r, g, b);
+ C = V - mmin(r, g, b);
+ H = (C == 0 ? null :
+ V == r ? (g - b) / C :
+ V == g ? (b - r) / C + 2 :
+ (r - g) / C + 4
+ );
+ H = ((H + 360) % 6) * 60 / 360;
+ S = C == 0 ? 0 : C / V;
+ return {h: H, s: S, b: V, toString: hsbtoString};
+};
+/*\
+ * Snap.rgb2hsl
+ [ method ]
+ **
+ * Converts RGB values to an HSL object
+ - r (number) red
+ - g (number) green
+ - b (number) blue
+ = (object) HSL object in the following format:
+ o {
+ o h (number) hue,
+ o s (number) saturation,
+ o l (number) luminosity
+ o }
+\*/
+Snap.rgb2hsl = function (r, g, b) {
+ b = prepareRGB(r, g, b);
+ r = b[0];
+ g = b[1];
+ b = b[2];
+
+ var H, S, L, M, m, C;
+ M = mmax(r, g, b);
+ m = mmin(r, g, b);
+ C = M - m;
+ H = (C == 0 ? null :
+ M == r ? (g - b) / C :
+ M == g ? (b - r) / C + 2 :
+ (r - g) / C + 4);
+ H = ((H + 360) % 6) * 60 / 360;
+ L = (M + m) / 2;
+ S = (C == 0 ? 0 :
+ L < .5 ? C / (2 * L) :
+ C / (2 - 2 * L));
+ return {h: H, s: S, l: L, toString: hsltoString};
+};
+
+// Transformations
+// SIERRA Snap.parsePathString(): By _array of arrays,_ I assume you mean a format like this for two separate segments? [ ["M10,10","L90,90"], ["M90,10","L10,90"] ] Otherwise how is each command structured?
+/*\
+ * Snap.parsePathString
+ [ method ]
+ **
+ * Utility method
+ **
+ * Parses given path string into an array of arrays of path segments
+ - pathString (string|array) path string or array of segments (in the last case it is returned straight away)
+ = (array) array of segments
+\*/
+Snap.parsePathString = function (pathString) {
+ if (!pathString) {
+ return null;
+ }
+ var pth = Snap.path(pathString);
+ if (pth.arr) {
+ return Snap.path.clone(pth.arr);
+ }
+
+ var paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0},
+ data = [];
+ if (is(pathString, "array") && is(pathString[0], "array")) { // rough assumption
+ data = Snap.path.clone(pathString);
+ }
+ if (!data.length) {
+ Str(pathString).replace(pathCommand, function (a, b, c) {
+ var params = [],
+ name = b.toLowerCase();
+ c.replace(pathValues, function (a, b) {
+ b && params.push(+b);
+ });
+ if (name == "m" && params.length > 2) {
+ data.push([b].concat(params.splice(0, 2)));
+ name = "l";
+ b = b == "m" ? "l" : "L";
+ }
+ if (name == "o" && params.length == 1) {
+ data.push([b, params[0]]);
+ }
+ if (name == "r") {
+ data.push([b].concat(params));
+ } else while (params.length >= paramCounts[name]) {
+ data.push([b].concat(params.splice(0, paramCounts[name])));
+ if (!paramCounts[name]) {
+ break;
+ }
+ }
+ });
+ }
+ data.toString = Snap.path.toString;
+ pth.arr = Snap.path.clone(data);
+ return data;
+};
+/*\
+ * Snap.parseTransformString
+ [ method ]
+ **
+ * Utility method
+ **
+ * Parses given transform string into an array of transformations
+ - TString (string|array) transform string or array of transformations (in the last case it is returned straight away)
+ = (array) array of transformations
+\*/
+var parseTransformString = Snap.parseTransformString = function (TString) {
+ if (!TString) {
+ return null;
+ }
+ var paramCounts = {r: 3, s: 4, t: 2, m: 6},
+ data = [];
+ if (is(TString, "array") && is(TString[0], "array")) { // rough assumption
+ data = Snap.path.clone(TString);
+ }
+ if (!data.length) {
+ Str(TString).replace(tCommand, function (a, b, c) {
+ var params = [],
+ name = b.toLowerCase();
+ c.replace(pathValues, function (a, b) {
+ b && params.push(+b);
+ });
+ data.push([b].concat(params));
+ });
+ }
+ data.toString = Snap.path.toString;
+ return data;
+};
+function svgTransform2string(tstr) {
+ var res = [];
+ tstr = tstr.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g, function (all, name, params) {
+ params = params.split(/\s*,\s*|\s+/);
+ if (name == "rotate" && params.length == 1) {
+ params.push(0, 0);
+ }
+ if (name == "scale") {
+ if (params.length == 2) {
+ params.push(0, 0);
+ }
+ if (params.length == 1) {
+ params.push(params[0], 0, 0);
+ }
+ }
+ if (name == "skewX") {
+ res.push(["m", 1, 0, math.tan(rad(params[0])), 1, 0, 0]);
+ } else if (name == "skewY") {
+ res.push(["m", 1, math.tan(rad(params[0])), 0, 1, 0, 0]);
+ } else {
+ res.push([name.charAt(0)].concat(params));
+ }
+ return all;
+ });
+ return res;
+}
+Snap._.svgTransform2string = svgTransform2string;
+Snap._.rgTransform = new RegExp("^[a-z][" + spaces + "]*-?\\.?\\d", "i");
+function transform2matrix(tstr, bbox) {
+ var tdata = parseTransformString(tstr),
+ m = new Matrix;
+ if (tdata) {
+ for (var i = 0, ii = tdata.length; i < ii; i++) {
+ var t = tdata[i],
+ tlen = t.length,
+ command = Str(t[0]).toLowerCase(),
+ absolute = t[0] != command,
+ inver = absolute ? m.invert() : 0,
+ x1,
+ y1,
+ x2,
+ y2,
+ bb;
+ if (command == "t" && tlen == 2){
+ m.translate(t[1], 0);
+ } else if (command == "t" && tlen == 3) {
+ if (absolute) {
+ x1 = inver.x(0, 0);
+ y1 = inver.y(0, 0);
+ x2 = inver.x(t[1], t[2]);
+ y2 = inver.y(t[1], t[2]);
+ m.translate(x2 - x1, y2 - y1);
+ } else {
+ m.translate(t[1], t[2]);
+ }
+ } else if (command == "r") {
+ if (tlen == 2) {
+ bb = bb || bbox;
+ m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+ } else if (tlen == 4) {
+ if (absolute) {
+ x2 = inver.x(t[2], t[3]);
+ y2 = inver.y(t[2], t[3]);
+ m.rotate(t[1], x2, y2);
+ } else {
+ m.rotate(t[1], t[2], t[3]);
+ }
+ }
+ } else if (command == "s") {
+ if (tlen == 2 || tlen == 3) {
+ bb = bb || bbox;
+ m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+ } else if (tlen == 4) {
+ if (absolute) {
+ x2 = inver.x(t[2], t[3]);
+ y2 = inver.y(t[2], t[3]);
+ m.scale(t[1], t[1], x2, y2);
+ } else {
+ m.scale(t[1], t[1], t[2], t[3]);
+ }
+ } else if (tlen == 5) {
+ if (absolute) {
+ x2 = inver.x(t[3], t[4]);
+ y2 = inver.y(t[3], t[4]);
+ m.scale(t[1], t[2], x2, y2);
+ } else {
+ m.scale(t[1], t[2], t[3], t[4]);
+ }
+ }
+ } else if (command == "m" && tlen == 7) {
+ m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
+ }
+ }
+ }
+ return m;
+}
+Snap._.transform2matrix = transform2matrix;
+function extractTransform(el, tstr) {
+ if (tstr == null) {
+ var doReturn = true;
+ if (el.type == "linearGradient" || el.type == "radialGradient") {
+ tstr = el.node.getAttribute("gradientTransform");
+ } else if (el.type == "pattern") {
+ tstr = el.node.getAttribute("patternTransform");
+ } else {
+ tstr = el.node.getAttribute("transform");
+ }
+ if (!tstr) {
+ return new Matrix;
+ }
+ tstr = svgTransform2string(tstr);
+ } else {
+ if (!Snap._.rgTransform.test(tstr)) {
+ tstr = svgTransform2string(tstr);
+ } else {
+ tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
+ }
+ if (is(tstr, "array")) {
+ tstr = Snap.path ? Snap.path.toString.call(tstr) : Str(tstr);
+ }
+ el._.transform = tstr;
+ }
+ var m = transform2matrix(tstr, el.getBBox(1));
+ if (doReturn) {
+ return m;
+ } else {
+ el.matrix = m;
+ }
+}
+Snap._unit2px = unit2px;
+var contains = glob.doc.contains || glob.doc.compareDocumentPosition ?
+ function (a, b) {
+ var adown = a.nodeType == 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a == bup || !!(bup && bup.nodeType == 1 && (
+ adown.contains ?
+ adown.contains(bup) :
+ a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
+ ));
+ } :
+ function (a, b) {
+ if (b) {
+ while (b) {
+ b = b.parentNode;
+ if (b == a) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+function getSomeDefs(el) {
+ var cache = Snap._.someDefs;
+ if (cache && contains(cache.ownerDocument.documentElement, cache)) {
+ return cache;
+ }
+ var p = (el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) ||
+ (el.node.parentNode && wrap(el.node.parentNode)) ||
+ Snap.select("svg") ||
+ Snap(0, 0),
+ pdefs = p.select("defs"),
+ defs = pdefs == null ? false : pdefs.node;
+ if (!defs) {
+ defs = make("defs", p.node).node;
+ }
+ Snap._.someDefs = defs;
+ return defs;
+}
+Snap._.getSomeDefs = getSomeDefs;
+function unit2px(el, name, value) {
+ var defs = getSomeDefs(el),
+ out = {},
+ mgr = defs.querySelector(".svg---mgr");
+ if (!mgr) {
+ mgr = $("rect");
+ $(mgr, {width: 10, height: 10, "class": "svg---mgr"});
+ defs.appendChild(mgr);
+ }
+ function getW(val) {
+ if (val == null) {
+ return E;
+ }
+ if (val == +val) {
+ return val;
+ }
+ $(mgr, {width: val});
+ return mgr.getBBox().width;
+ }
+ function getH(val) {
+ if (val == null) {
+ return E;
+ }
+ if (val == +val) {
+ return val;
+ }
+ $(mgr, {height: val});
+ return mgr.getBBox().height;
+ }
+ function set(nam, f) {
+ if (name == null) {
+ out[nam] = f(el.attr(nam));
+ } else if (nam == name) {
+ out = f(value == null ? el.attr(nam) : value);
+ }
+ }
+ switch (el.type) {
+ case "rect":
+ set("rx", getW);
+ set("ry", getH);
+ case "image":
+ set("width", getW);
+ set("height", getH);
+ case "text":
+ set("x", getW);
+ set("y", getH);
+ break;
+ case "circle":
+ set("cx", getW);
+ set("cy", getH);
+ set("r", getW);
+ break;
+ case "ellipse":
+ set("cx", getW);
+ set("cy", getH);
+ set("rx", getW);
+ set("ry", getH);
+ break;
+ case "line":
+ set("x1", getW);
+ set("x2", getW);
+ set("y1", getH);
+ set("y2", getH);
+ break;
+ case "marker":
+ set("refX", getW);
+ set("markerWidth", getW);
+ set("refY", getH);
+ set("markerHeight", getH);
+ break;
+ case "radialGradient":
+ set("fx", getW);
+ set("fy", getH);
+ break;
+ case "tspan":
+ set("dx", getW);
+ set("dy", getH);
+ break;
+ default:
+ set(name, getW);
+ }
+ return out;
+}
+/*\
+ * Snap.select
+ [ method ]
+ **
+ * Wraps a DOM element specified by CSS selector as @Element
+ - query (string) CSS selector of the element
+ = (Element) the current element
+\*/
+Snap.select = function (query) {
+ return wrap(glob.doc.querySelector(query));
+};
+/*\
+ * Snap.selectAll
+ [ method ]
+ **
+ * Wraps DOM elements specified by CSS selector as set or array of @Element
+ - query (string) CSS selector of the element
+ = (Element) the current element
+\*/
+Snap.selectAll = function (query) {
+ var nodelist = glob.doc.querySelectorAll(query),
+ set = (Snap.set || Array)();
+ for (var i = 0; i < nodelist.length; i++) {
+ set.push(wrap(nodelist[i]));
+ }
+ return set;
+};
+
+function add2group(list) {
+ if (!is(list, "array")) {
+ list = Array.prototype.slice.call(arguments, 0);
+ }
+ var i = 0,
+ j = 0,
+ node = this.node;
+ while (this[i]) delete this[i++];
+ for (i = 0; i < list.length; i++) {
+ if (list[i].type == "set") {
+ list[i].forEach(function (el) {
+ node.appendChild(el.node);
+ });
+ } else {
+ node.appendChild(list[i].node);
+ }
+ }
+ var children = node.childNodes;
+ for (i = 0; i < children.length; i++) {
+ this[j++] = wrap(children[i]);
+ }
+ return this;
+}
+function Element(el) {
+ if (el.snap in hub) {
+ return hub[el.snap];
+ }
+ var id = this.id = ID(),
+ svg;
+ try {
+ svg = el.ownerSVGElement;
+ } catch(e) {}
+ this.node = el;
+ if (svg) {
+ this.paper = new Paper(svg);
+ }
+ this.type = el.tagName;
+ this.anims = {};
+ this._ = {
+ transform: []
+ };
+ el.snap = id;
+ hub[id] = this;
+ if (this.type == "g") {
+ this.add = add2group;
+ for (var method in Paper.prototype) if (Paper.prototype[has](method)) {
+ this[method] = Paper.prototype[method];
+ }
+ }
+}
+function arrayFirstValue(arr) {
+ var res;
+ for (var i = 0, ii = arr.length; i < ii; i++) {
+ res = res || arr[i];
+ if (res) {
+ return res;
+ }
+ }
+}
+(function (elproto) {
+ /*\
+ * Element.attr
+ [ method ]
+ **
+ * Gets or sets given attributes of the element
+ **
+ - params (object) contains key-value pairs of attributes you want to set
+ * or
+ - param (string) name of the attribute
+ = (Element) the current element
+ * or
+ = (string) value of attribute
+ > Usage
+ | el.attr({
+ | fill: "#fc0",
+ | stroke: "#000",
+ | strokeWidth: 2, // CamelCase...
+ | "fill-opacity": 0.5 // or dash-separated names
+ | });
+ | console.log(el.attr("fill")); // #fc0
+ \*/
+ elproto.attr = function (params, value) {
+ var el = this,
+ node = el.node;
+ if (!params) {
+ return el;
+ }
+ if (is(params, "string")) {
+ if (arguments.length > 1) {
+ var json = {};
+ json[params] = value;
+ params = json;
+ } else {
+ return arrayFirstValue(eve("snap.util.getattr."+params, el));
+ }
+ }
+ for (var att in params) {
+ if (params[has](att)) {
+ eve("snap.util.attr." + att, el, params[att]);
+ }
+ }
+ return el;
+ };
+// SIERRA Element.getBBox(): Unclear why you would want to express the dimension of the box as a path.
+// SIERRA Element.getBBox(): Unclear why you would want to use r0/r1/r2. Also, basic definitions: wouldn't the _smallest circle that can be enclosed_ be a zero-radius point?
+ /*\
+ * Element.getBBox
+ [ method ]
+ **
+ * Returns the bounding box descriptor for the given element
+ **
+ = (object) bounding box descriptor:
+ o {
+ o cx: (number) x of the center,
+ o cy: (number) x of the center,
+ o h: (number) height,
+ o height: (number) height,
+ o path: (string) path command for the box,
+ o r0: (number) radius of a circle that fully encloses the box,
+ o r1: (number) radius of the smallest circle that can be enclosed,
+ o r2: (number) radius of the largest circle that can be enclosed,
+ o vb: (string) box as a viewbox command,
+ o w: (number) width,
+ o width: (number) width,
+ o x2: (number) x of the right side,
+ o x: (number) x of the left side,
+ o y2: (number) y of the bottom edge,
+ o y: (number) y of the top edge
+ o }
+ \*/
+ elproto.getBBox = function (isWithoutTransform) {
+ var el = this;
+ if (el.type == "use") {
+ el = el.original;
+ }
+ if (el.removed) {
+ return {};
+ }
+ var _ = el._;
+ if (isWithoutTransform) {
+ _.bboxwt = Snap.path.get[el.type] ? Snap.path.getBBox(el.realPath = Snap.path.get[el.type](el)) : Snap._.box(el.node.getBBox());
+ return Snap._.box(_.bboxwt);
+ } else {
+ el.realPath = (Snap.path.get[el.type] || Snap.path.get.deflt)(el);
+ _.bbox = Snap.path.getBBox(Snap.path.map(el.realPath, el.matrix));
+ }
+ return Snap._.box(_.bbox);
+ };
+ var propString = function () {
+ return this.string;
+ };
+// SIERRA Element.transform(): seems to allow two return values, one of which (_Element_) is undefined.
+// SIERRA Element.transform(): if this only accepts one argument, it's unclear how it can both _get_ and _set_ a transform.
+// SIERRA Element.transform(): Unclear how Snap transform string format differs from SVG's.
+ /*\
+ * Element.transform
+ [ method ]
+ **
+ * Gets or sets transformation of the element
+ **
+ - tstr (string) transform string in Snap or SVG format
+ = (Element) the current element
+ * or
+ = (object) transformation descriptor:
+ o {
+ o string (string) transform string,
+ o globalMatrix (Matrix) matrix of all transformations applied to element or its parents,
+ o localMatrix (Matrix) matrix of transformations applied only to the element,
+ o diffMatrix (Matrix) matrix of difference between global and local transformations,
+ o global (string) global transformation as string,
+ o local (string) local transformation as string,
+ o toString (function) returns `string` property
+ o }
+ \*/
+ elproto.transform = function (tstr) {
+ var _ = this._;
+ if (tstr == null) {
+ var global = new Matrix(this.node.getCTM()),
+ local = extractTransform(this),
+ localString = local.toTransformString(),
+ string = Str(local) == Str(this.matrix) ?
+ _.transform : localString;
+ return {
+ string: string,
+ globalMatrix: global,
+ localMatrix: local,
+ diffMatrix: global.clone().add(local.invert()),
+ global: global.toTransformString(),
+ local: localString,
+ toString: propString
+ };
+ }
+ if (tstr instanceof Matrix) {
+ // may be need to apply it directly
+ // TODO: investigate
+ tstr = tstr.toTransformString();
+ }
+ extractTransform(this, tstr);
+
+ if (this.node) {
+ if (this.type == "linearGradient" || this.type == "radialGradient") {
+ $(this.node, {gradientTransform: this.matrix});
+ } else if (this.type == "pattern") {
+ $(this.node, {patternTransform: this.matrix});
+ } else {
+ $(this.node, {transform: this.matrix});
+ }
+ }
+
+ return this;
+ };
+ /*\
+ * Element.parent
+ [ method ]
+ **
+ * Returns the element's parent
+ **
+ = (Element) the parent element
+ \*/
+ elproto.parent = function () {
+ return wrap(this.node.parentNode);
+ };
+ /*\
+ * Element.append
+ [ method ]
+ **
+ * Appends the given element to current one
+ **
+ - el (Element|Set) element to append
+ = (Element) the parent element
+ \*/
+ /*\
+ * Element.add
+ [ method ]
+ **
+ * See @Element.append
+ \*/
+ elproto.append = elproto.add = function (el) {
+ if (el) {
+ if (el.type == "set") {
+ var it = this;
+ el.forEach(function (el) {
+ it.add(el);
+ });
+ return this;
+ }
+ el = wrap(el);
+ this.node.appendChild(el.node);
+ el.paper = this.paper;
+ }
+ return this;
+ };
+ /*\
+ * Element.appendTo
+ [ method ]
+ **
+ * Appends the current element to the given one
+ **
+ - el (Element) parent element to append to
+ = (Element) the child element
+ \*/
+ elproto.appendTo = function (el) {
+ if (el) {
+ el = wrap(el);
+ el.append(this);
+ }
+ return this;
+ };
+ /*\
+ * Element.prepend
+ [ method ]
+ **
+ * Prepends the given element to the current one
+ **
+ - el (Element) element to prepend
+ = (Element) the parent element
+ \*/
+ elproto.prepend = function (el) {
+ if (el) {
+ el = wrap(el);
+ var parent = el.parent();
+ this.node.insertBefore(el.node, this.node.firstChild);
+ this.add && this.add();
+ el.paper = this.paper;
+ this.parent() && this.parent().add();
+ parent && parent.add();
+ }
+ return this;
+ };
+ /*\
+ * Element.prependTo
+ [ method ]
+ **
+ * Prepends the current element to the given one
+ **
+ - el (Element) parent element to prepend to
+ = (Element) the child element
+ \*/
+ elproto.prependTo = function (el) {
+ el = wrap(el);
+ el.prepend(this);
+ return this;
+ };
+ /*\
+ * Element.before
+ [ method ]
+ **
+ * Inserts given element before the current one
+ **
+ - el (Element) element to insert
+ = (Element) the parent element
+ \*/
+ elproto.before = function (el) {
+ if (el.type == "set") {
+ var it = this;
+ el.forEach(function (el) {
+ var parent = el.parent();
+ it.node.parentNode.insertBefore(el.node, it.node);
+ parent && parent.add();
+ });
+ this.parent().add();
+ return this;
+ }
+ el = wrap(el);
+ var parent = el.parent();
+ this.node.parentNode.insertBefore(el.node, this.node);
+ this.parent() && this.parent().add();
+ parent && parent.add();
+ el.paper = this.paper;
+ return this;
+ };
+ /*\
+ * Element.after
+ [ method ]
+ **
+ * Inserts given element after the current one
+ **
+ - el (Element) element to insert
+ = (Element) the parent element
+ \*/
+ elproto.after = function (el) {
+ el = wrap(el);
+ var parent = el.parent();
+ if (this.node.nextSibling) {
+ this.node.parentNode.insertBefore(el.node, this.node.nextSibling);
+ } else {
+ this.node.parentNode.appendChild(el.node);
+ }
+ this.parent() && this.parent().add();
+ parent && parent.add();
+ el.paper = this.paper;
+ return this;
+ };
+ /*\
+ * Element.insertBefore
+ [ method ]
+ **
+ * Inserts the element after the given one
+ **
+ - el (Element) element next to whom insert to
+ = (Element) the parent element
+ \*/
+ elproto.insertBefore = function (el) {
+ el = wrap(el);
+ var parent = this.parent();
+ el.node.parentNode.insertBefore(this.node, el.node);
+ this.paper = el.paper;
+ parent && parent.add();
+ el.parent() && el.parent().add();
+ return this;
+ };
+ /*\
+ * Element.insertAfter
+ [ method ]
+ **
+ * Inserts the element after the given one
+ **
+ - el (Element) element next to whom insert to
+ = (Element) the parent element
+ \*/
+ elproto.insertAfter = function (el) {
+ el = wrap(el);
+ var parent = this.parent();
+ el.node.parentNode.insertBefore(this.node, el.node.nextSibling);
+ this.paper = el.paper;
+ parent && parent.add();
+ el.parent() && el.parent().add();
+ return this;
+ };
+ /*\
+ * Element.remove
+ [ method ]
+ **
+ * Removes element from the DOM
+ = (Element) the detached element
+ \*/
+ elproto.remove = function () {
+ var parent = this.parent();
+ this.node.parentNode && this.node.parentNode.removeChild(this.node);
+ delete this.paper;
+ this.removed = true;
+ parent && parent.add();
+ return this;
+ };
+ /*\
+ * Element.select
+ [ method ]
+ **
+ * Gathers the nested @Element matching the given set of CSS selectors
+ **
+ - query (string) CSS selector
+ = (Element) result of query selection
+ \*/
+ elproto.select = function (query) {
+ return wrap(this.node.querySelector(query));
+ };
+ /*\
+ * Element.selectAll
+ [ method ]
+ **
+ * Gathers nested @Element objects matching the given set of CSS selectors
+ **
+ - query (string) CSS selector
+ = (Set|array) result of query selection
+ \*/
+ elproto.selectAll = function (query) {
+ var nodelist = this.node.querySelectorAll(query),
+ set = (Snap.set || Array)();
+ for (var i = 0; i < nodelist.length; i++) {
+ set.push(wrap(nodelist[i]));
+ }
+ return set;
+ };
+ /*\
+ * Element.asPX
+ [ method ]
+ **
+ * Returns given attribute of the element as a `px` value (not %, em, etc.)
+ **
+ - attr (string) attribute name
+ - value (string) #optional attribute value
+ = (Element) result of query selection
+ \*/
+ elproto.asPX = function (attr, value) {
+ if (value == null) {
+ value = this.attr(attr);
+ }
+ return +unit2px(this, attr, value);
+ };
+ // SIERRA Element.use(): I suggest adding a note about how to access the original element the returned