UNPKG

snapsvg

Version:
1,598 lines (1,570 loc) 235 kB
// Snap.svg 0.0.1 // // 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-10-15 // 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(["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 = 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--; } 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); if (a.s == 1) { eve("mina.finish." + a.id, a); } } 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 gereal 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); /* * Elemental 0.2.4 - Simple JavaScript Tag Parser * * Copyright (c) 2010 - 2013 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. */ (function () { function parse(s) { s = s || Object(s); var pos = 1, len = s.length + 1, p, c, n = at(s, 0); for (;pos < len; pos++) { p = c; c = n; n = at(s, pos); this.raw += c; step.call(this, c, n, p); } this._beforeEnd = function () { step.call(this, "", "", c); }; return this; } function at(s, i) { return s && (s.charAt ? s.charAt(i) : s[i]); } function on(name, f) { this.events = this.events || {}; this.events[name] = this.events[name] || []; this.events[name].push(f); } function event(name, data, extra) { if (typeof eve == "function") { eve("elemental." + name + (data ? "." + data : ""), null, data, extra || "", this.raw); } var a = this.events && this.events[name], i = a && a.length; while (i--) try { this.events[name][i](data, extra || "", this.raw); } catch (e) {} this.raw = ""; } function end() { step.call(this, "eof"); this.event("eof"); } var entities = { "lt": 60, "lt;": 60, "AMP;": 38, "AMP": 38, "GT;": 62, "GT": 62, "QUOT;": 34, "QUOT": 34, "apos;": 39, "bull;": 8226, "bullet;": 8226, "copy;": 169, "copy": 169, "deg;": 176, "deg": 176 }, whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]/, notEntity = /[#\da-z]/i, entity2text = function (entity) { var code; if (entity.charAt() == "#") { if (entity.charAt(1).toLowerCase() == "x") { code = parseInt(entity.substring(2), 16); } else { code = parseInt(entity.substring(1), 10); } } code = entities[entity]; return code ? String.fromCharCode(code) : ("&" + entity); }, fireAttrEvent = function () { for (var key in this.attr) if (this.attr.hasOwnProperty(key)) { this.event("attr", key, { value: this.attr[key], tagname: this.tagname, attr: this.attr }); } }, act = { text: function (c, n, p) { switch (c) { case "<": case "eof": this.nodename = ""; this.attr = {}; this.mode = "tag name start"; this.raw = this.raw.slice(0, -1); this.textchunk && this.event("text", this.textchunk); this.raw += c; this.textchunk = ""; break; case "&": this.mode = "entity"; this.entity = ""; break; default: this.textchunk += c; break; } }, entity: function (c, n, p) { if (whitespace.test(c)) { this.textchunk += entity2text(this.entity); this.mode = "text"; } else if (c == ";") { this.textchunk += entity2text(this.entity + c); this.mode = "text"; } else { this.entity += c; } }, special: function (c, n, p) { if (p == "!" && c == "-" && n == "-") { this.mode = "comment start"; return; } if (this.textchunk == "[CDATA" && c == "[") { this.mode = "cdata"; this.textchunk = ""; return; } if (c == ">" || c == "eof") { this.event("special", this.textchunk); this.mode = "text"; this.textchunk = ""; return; } this.textchunk += c; }, cdata: function (c, n, p) { if (p == "]" && c == "]" && n == ">") { this.mode = "cdata end"; this.textchunk = this.textchunk.slice(0, -1); return; } if (c == "eof") { act["cdata end"].call(this); } this.textchunk += c; }, "cdata end": function (c, n, p) { this.event("cdata", this.textchunk); this.textchunk = ""; this.mode = "text"; }, "comment start": function (c, n, p) { if (n == ">" || c == "eof") { this.event("comment", ""); this.mode = "skip"; } else { this.mode = "comment"; } }, "skip": function (c, n, p) { this.mode = "text"; }, comment: function (c, n, p) { if (c == "-" && p == "-" && n == ">") { this.mode = "comment end"; this.textchunk = this.textchunk.slice(0, -1); } else if (c == "eof") { this.event("comment", this.textchunk); } else { this.textchunk += c; } }, "comment end": function (c, n, p) { this.event("comment", this.textchunk); this.textchunk = ""; this.mode = "text"; }, declaration: function (c, n, p) { if (c == "?" && n == ">") { this.mode = "declaration end"; return; } if (c == "eof") { this.event("comment", this.textchunk); } this.textchunk += c; }, "declaration end": function (c, n, p) { this.event("comment", this.textchunk); this.textchunk = ""; this.mode = "text"; }, "tag name start": function (c, n, p) { if (c == "eof") { this.event("text", "<"); return; } if (!whitespace.test(c)) { this.mode = "tag name"; if (c == "/") { this.mode = "close tag name start"; return; } else if (c == "!") { this.mode = "special"; this.textchunk = ""; return; } else if (c == "?") { this.mode = "declaration"; return; } act[this.mode].call(this, c, n, p); } }, "close tag name start": function (c, n, p) { if (!whitespace.test(c)) { this.mode = "close tag name"; this.tagname = ""; this.nodename = ""; act[this.mode].call(this, c, n, p); } }, "close tag name": function (c, n, p) { if (whitespace.test(c)) { this.tagname = this.nodename; } else switch (c) { case ">": this.event("/tag", (this.tagname || this.nodename)); this.mode = "text"; break; default: !this.tagname && (this.nodename += c); break; } }, "tag name": function (c, n, p) { if (whitespace.test(c)) { this.tagname = this.nodename; this.nodename = ""; this.mode = "attr start"; } else switch (c) { case ">": this.event("tag", this.nodename); this.mode = "text"; break; case "/": this.raw += n; this.event("tag", this.nodename); this.event("/tag", this.nodename); this.mode = "skip"; break; default: this.nodename += c; break; } }, "attr start": function (c, n, p) { if (!whitespace.test(c)) { this.mode = "attr"; this.nodename = ""; act[this.mode].call(this, c, n, p); } }, attr: function (c, n, p) { if (whitespace.test(c) || c == "=") { this.attr[this.nodename] = ""; this.mode = "attr value start"; } else switch (c) { case ">": if (this.nodename == "/") { delete this.attr["/"]; this.event("tag", this.tagname, this.attr); fireAttrEvent.call(this); this.event("/tag", this.tagname, true); } else { this.nodename && (this.attr[this.nodename] = ""); this.event("tag", this.tagname, this.attr); fireAttrEvent.call(this); } this.mode = "text"; break; default: this.nodename += c; break; } }, "attr value start": function (c, n, p) { if (!whitespace.test(c)) { this.mode = "attr value"; this.quote = false; if (c == "'" || c == '"') { this.quote = c; return; } act[this.mode].call(this, c, n, p); } }, "attr value": function (c, n, p) { if (whitespace.test(c) && !this.quote) { this.mode = "attr start"; } else if (c == ">" && !this.quote) { this.event("tag", this.tagname, this.attr); this.mode = "text"; } else switch (c) { case '"': case "'": if (this.quote == c && p != "\\") { this.mode = "attr start"; } break; default: this.attr[this.nodename] += c; break; } } }; function step(c, n, p) { c == "\n" && this.event("newline"); act[this.mode].call(this, c, n, p); } function elemental(type, ent) { var out = function (s) { out.parse(s); }; out.mode = "text"; out.type = String(type || "html").toLowerCase(); out.textchunk = ""; out.raw = ""; out.parse = parse; out.on = on; out.event = event; out.end = end; if (ent) { entities = ent; } return out; } elemental.version = "0.2.4"; (typeof exports == "undefined" ? this : exports).elemental = elemental; })(); // 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.1.0"; // SIERRA: this method appears to be missing from HTML output /*\ * 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, isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1}, 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", hub = {}; 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)); } 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 { el.setAttribute(key, val); } } else { el.removeAttribute(key); } } } else { el = glob.doc.createElementNS("http://www.w3.org/2000/svg", 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 !isnan[has](+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 `{<name>}` 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; }; // SIERRA Matrix.invert(): Unclear what it means to invert a matrix. /*\ * 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); }; // SIERRA: do cx/cy default to the center point, as in CSS? If so, Snap appears to resolve important discrepancies between how transforms behave in SVG & CSS. /*\ * 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 \*/ 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