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: + # + * 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 instantiates. It's a part of SVG with which ordinary web developers may be least familiar. + /*\ + * Element.use + [ method ] + ** + * Creates a `` element linked to the current element + ** + = (Element) the `` element + \*/ + elproto.use = function () { + var use, + id = this.node.id; + if (!id) { + id = this.id; + $(this.node, { + id: id + }); + } + if (this.type == "linearGradient" || this.type == "radialGradient" || + this.type == "pattern") { + use = make(this.type, this.node.parentNode); + } else { + use = make("use", this.node.parentNode); + } + $(use.node, { + "xlink:href": "#" + id + }); + use.original = this; + return use; + }; + /*\ + * Element.clone + [ method ] + ** + * Creates a clone of the element and inserts it after the element + ** + = (Element) the clone + \*/ + function fixids(el) { + var els = el.selectAll("*"), + it, + url = /^\s*url\(("|'|)(.*)\1\)\s*$/, + ids = [], + uses = {}; + function urltest(it, name) { + var val = $(it.node, name); + val = val && val.match(url); + val = val && val[2]; + if (val && val.charAt() == "#") { + val = val.substring(1); + } else { + return; + } + if (val) { + uses[val] = (uses[val] || []).concat(function (id) { + var attr = {}; + attr[name] = URL(id); + $(it.node, attr); + }); + } + } + function linktest(it) { + var val = $(it.node, "xlink:href"); + if (val && val.charAt() == "#") { + val = val.substring(1); + } else { + return; + } + if (val) { + uses[val] = (uses[val] || []).concat(function (id) { + it.attr("xlink:href", "#" + id); + }); + } + } + for (var i = 0, ii = els.length; i < ii; i++) { + it = els[i]; + urltest(it, "fill"); + urltest(it, "stroke"); + urltest(it, "filter"); + urltest(it, "mask"); + urltest(it, "clip-path"); + linktest(it); + var oldid = $(it.node, "id"); + if (oldid) { + $(it.node, {id: it.id}); + ids.push({ + old: oldid, + id: it.id + }); + } + } + for (i = 0, ii = ids.length; i < ii; i++) { + var fs = uses[ids[i].old]; + if (fs) { + for (var j = 0, jj = fs.length; j < jj; j++) { + fs[j](ids[i].id); + } + } + } + } + elproto.clone = function () { + var clone = wrap(this.node.cloneNode(true)); + if ($(clone.node, "id")) { + $(clone.node, {id: clone.id}); + } + fixids(clone); + clone.insertAfter(this); + return clone; + }; +// SIERRA Element.toDefs(): If this _moves_ an element to the region, why is the return value a _clone_? Also unclear why it's called the _relative_ section. Perhaps _shared_? + /*\ + * Element.toDefs + [ method ] + ** + * Moves element to the shared `` area + ** + = (Element) the clone + \*/ + elproto.toDefs = function () { + var defs = getSomeDefs(this); + defs.appendChild(this.node); + return this; + }; +// SIERRA Element.pattern(): x/y/width/height data types are listed as both String and Number. Is that an error, or does it mean strings are coerced? +// SIERRA Element.pattern(): clarify that x/y are offsets that e.g., may add gutters between the tiles. + /*\ + * Element.pattern + [ method ] + ** + * Creates a `` element from the current element + ** + * To create a pattern you have to specify the pattern rect: + - x (string|number) + - y (string|number) + - width (string|number) + - height (string|number) + = (Element) the `` element + * You can use pattern later on as an argument for `fill` attribute: + | var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({ + | fill: "none", + | stroke: "#bada55", + | strokeWidth: 5 + | }).pattern(0, 0, 10, 10), + | c = paper.circle(200, 200, 100); + | c.attr({ + | fill: p + | }); + \*/ + elproto.pattern = function (x, y, width, height) { + var p = make("pattern", getSomeDefs(this)); + if (x == null) { + x = this.getBBox(); + } + if (is(x, "object") && "x" in x) { + y = x.y; + width = x.width; + height = x.height; + x = x.x; + } + $(p.node, { + x: x, + y: y, + width: width, + height: height, + patternUnits: "userSpaceOnUse", + id: p.id, + viewBox: [x, y, width, height].join(" ") + }); + p.node.appendChild(this.node); + return p; + }; +// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path. +// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values? + /*\ + * Element.marker + [ method ] + ** + * Creates a `` element from the current element + ** + * To create a marker you have to specify the bounding rect and reference point: + - x (number) + - y (number) + - width (number) + - height (number) + - refX (number) + - refY (number) + = (Element) the `` element + * You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end. + \*/ + // TODO add usage for markers + elproto.marker = function (x, y, width, height, refX, refY) { + var p = make("marker", getSomeDefs(this)); + if (x == null) { + x = this.getBBox(); + } + if (is(x, "object") && "x" in x) { + y = x.y; + width = x.width; + height = x.height; + refX = x.refX || x.cx; + refY = x.refY || x.cy; + x = x.x; + } + $(p.node, { + viewBox: [x, y, width, height].join(S), + markerWidth: width, + markerHeight: height, + orient: "auto", + refX: refX || 0, + refY: refY || 0, + id: p.id + }); + p.node.appendChild(this.node); + return p; + }; + // animation + function slice(from, to, f) { + return function (arr) { + var res = arr.slice(from, to); + if (res.length == 1) { + res = res[0]; + } + return f ? f(res) : res; + }; + } + var Animation = function (attr, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + this.attr = attr; + this.dur = ms; + easing && (this.easing = easing); + callback && (this.callback = callback); + }; + // SIERRA All object methods should feature sample code. This is just one instance. + /*\ + * Snap.animation + [ method ] + ** + * Creates an animation object + ** + - attr (object) attributes of final destination + - duration (number) duration of the animation, in milliseconds + - easing (function) #optional one of easing functions of @mina or custom one + - callback (function) #optional callback function that fires when animation ends + = (object) animation object + \*/ + Snap.animation = function (attr, ms, easing, callback) { + return new Animation(attr, ms, easing, callback); + }; + /*\ + * Element.inAnim + [ method ] + ** + * Returns a set of animations that may be able to manipulate the current element + ** + = (object) in format: + o { + o anim (object) animation object, + o curStatus (number) 0..1 — status of the animation: 0 — just started, 1 — just finished, + o status (function) gets or sets the status of the animation, + o stop (function) stops the animation + o } + \*/ + elproto.inAnim = function () { + var el = this, + res = []; + for (var id in el.anims) if (el.anims[has](id)) { + (function (a) { + res.push({ + anim: new Animation(a._attrs, a.dur, a.easing, a._callback), + curStatus: a.status(), + status: function (val) { + return a.status(val); + }, + stop: function () { + a.stop(); + } + }); + }(el.anims[id])); + } + return res; + }; + /*\ + * Snap.animate + [ method ] + ** + * Runs generic animation of one number into another with a caring function + ** + - from (number|array) number or array of numbers + - to (number|array) number or array of numbers + - setter (function) caring function that accepts one number argument + - duration (number) duration, in milliseconds + - easing (function) #optional easing function from @mina or custom + - callback (function) #optional callback function to execute when animation ends + = (object) animation object in @mina format + o { + o id (string) animation id, consider it read-only, + o duration (function) gets or sets the duration of the animation, + o easing (function) easing, + o speed (function) gets or sets the speed of the animation, + o status (function) gets or sets the status of the animation, + o stop (function) stops the animation + o } + | var rect = Snap().rect(0, 0, 10, 10); + | Snap.animate(0, 10, function (val) { + | rect.attr({ + | x: val + | }); + | }, 1000); + | // in given context is equivalent to + | rect.animate({x: 10}, 1000); + \*/ + Snap.animate = function (from, to, setter, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + var now = mina.time(), + anim = mina(from, to, now, now + ms, mina.time, setter, easing); + callback && eve.once("mina.finish." + anim.id, callback); + return anim; + }; + /*\ + * Element.stop + [ method ] + ** + * Stops all the animations for the current element + ** + = (Element) the current element + \*/ + elproto.stop = function () { + var anims = this.inAnim(); + for (var i = 0, ii = anims.length; i < ii; i++) { + anims[i].stop(); + } + return this; + }; + // SIERRA Element.animate(): For _attrs_, clarify if they represent the destination values, and if the animation executes relative to the element's current attribute values. + // SIERRA would a _custom_ animation function be an SVG keySplines value? + /*\ + * Element.animate + [ method ] + ** + * Animates the given attributes of the element + ** + - attrs (object) key-value pairs of destination attributes + - duration (number) duration of the animation in milliseconds + - easing (function) #optional easing function from @mina or custom + - callback (function) #optional callback function that executes when the animation ends + = (Element) the current element + \*/ + elproto.animate = function (attrs, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + if (attrs instanceof Animation) { + callback = attrs.callback; + easing = attrs.easing; + ms = easing.dur; + attrs = attrs.attr; + } + var fkeys = [], tkeys = [], keys = {}, from, to, f, eq, + el = this; + for (var key in attrs) if (attrs[has](key)) { + if (el.equal) { + eq = el.equal(key, Str(attrs[key])); + from = eq.from; + to = eq.to; + f = eq.f; + } else { + from = +el.attr(key); + to = +attrs[key]; + } + var len = is(from, "array") ? from.length : 1; + keys[key] = slice(fkeys.length, fkeys.length + len, f); + fkeys = fkeys.concat(from); + tkeys = tkeys.concat(to); + } + var now = mina.time(), + anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) { + var attr = {}; + for (var key in keys) if (keys[has](key)) { + attr[key] = keys[key](val); + } + el.attr(attr); + }, easing); + el.anims[anim.id] = anim; + anim._attrs = attrs; + anim._callback = callback; + eve.once("mina.finish." + anim.id, function () { + delete el.anims[anim.id]; + callback && callback.call(el); + }); + eve.once("mina.stop." + anim.id, function () { + delete el.anims[anim.id]; + }); + return el; + }; + var eldata = {}; + /*\ + * Element.data + [ method ] + ** + * Adds or retrieves given value associated with given key. (Don’t confuse + * with `data-` attributes) + * + * See also @Element.removeData + - key (string) key to store data + - value (any) #optional value to store + = (object) @Element + * or, if value is not specified: + = (any) value + > Usage + | for (var i = 0, i < 5, i++) { + | paper.circle(10 + 15 * i, 10, 10) + | .attr({fill: "#000"}) + | .data("i", i) + | .click(function () { + | alert(this.data("i")); + | }); + | } + \*/ + elproto.data = function (key, value) { + var data = eldata[this.id] = eldata[this.id] || {}; + if (arguments.length == 0){ + eve("snap.data.get." + this.id, this, data, null); + return data; + } + if (arguments.length == 1) { + if (Snap.is(key, "object")) { + for (var i in key) if (key[has](i)) { + this.data(i, key[i]); + } + return this; + } + eve("snap.data.get." + this.id, this, data[key], key); + return data[key]; + } + data[key] = value; + eve("snap.data.set." + this.id, this, value, key); + return this; + }; + /*\ + * Element.removeData + [ method ] + ** + * Removes value associated with an element by given key. + * If key is not provided, removes all the data of the element. + - key (string) #optional key + = (object) @Element + \*/ + elproto.removeData = function (key) { + if (key == null) { + eldata[this.id] = {}; + } else { + eldata[this.id] && delete eldata[this.id][key]; + } + return this; + }; + /*\ + * Element.outerSVG + [ method ] + ** + * Returns SVG code for the element, equivalent to HTML's `outerHTML`. + * + * See also @Element.innerSVG + = (string) SVG code for the element + \*/ + /*\ + * Element.toString + [ method ] + ** + * See @Element.outerSVG + \*/ + elproto.outerSVG = elproto.toString = toString(1); + /*\ + * Element.innerSVG + [ method ] + ** + * Returns SVG code for the element's contents, equivalent to HTML's `innerHTML` + = (string) SVG code for the element + \*/ + elproto.innerSVG = toString(); + function toString(type) { + return function () { + var res = type ? "<" + this.type : "", + attr = this.node.attributes, + chld = this.node.childNodes; + if (type) { + for (var i = 0, ii = attr.length; i < ii; i++) { + res += " " + attr[i].name + '="' + + attr[i].value.replace(/"/g, '\\"') + '"'; + } + } + if (chld.length) { + type && (res += ">"); + for (i = 0, ii = chld.length; i < ii; i++) { + if (chld[i].nodeType == 3) { + res += chld[i].nodeValue; + } else if (chld[i].nodeType == 1) { + res += wrap(chld[i]).toString(); + } + } + type && (res += ""); + } else { + type && (res += "/>"); + } + return res; + }; + } +}(Element.prototype)); +// SIERRA Snap.parse() accepts & returns a fragment, but there's no info on what it does in between. What if it doesn't parse? +/*\ + * Snap.parse + [ method ] + ** + * Parses SVG fragment and converts it into a @Fragment + ** + - svg (string) SVG string + = (Fragment) the @Fragment +\*/ +Snap.parse = function (svg) { + var f = glob.doc.createDocumentFragment(), + full = true, + div = glob.doc.createElement("div"); + svg = Str(svg); + if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) { + svg = "" + svg + ""; + full = false; + } + div.innerHTML = svg; + svg = div.getElementsByTagName("svg")[0]; + if (svg) { + if (full) { + f = svg; + } else { + while (svg.firstChild) { + f.appendChild(svg.firstChild); + } + } + } + div.innerHTML = E; + return new Fragment(f); +}; +function Fragment(frag) { + this.node = frag; +} +/*\ + * Fragment.select + [ method ] + ** + * See @Element.select +\*/ +Fragment.prototype.select = Element.prototype.select; +/*\ + * Fragment.selectAll + [ method ] + ** + * See @Element.selectAll +\*/ +Fragment.prototype.selectAll = Element.prototype.selectAll; +// SIERRA Snap.fragment() could especially use a code example +/*\ + * Snap.fragment + [ method ] + ** + * Creates a DOM fragment from a given list of elements or strings + ** + - varargs (…) SVG string + = (Fragment) the @Fragment +\*/ +Snap.fragment = function () { + var args = Array.prototype.slice.call(arguments, 0), + f = glob.doc.createDocumentFragment(); + for (var i = 0, ii = args.length; i < ii; i++) { + var item = args[i]; + if (item.node && item.node.nodeType) { + f.appendChild(item.node); + } + if (item.nodeType) { + f.appendChild(item); + } + if (typeof item == "string") { + f.appendChild(Snap.parse(item).node); + } + } + return new Fragment(f); +}; + +function make(name, parent) { + var res = $(name); + parent.appendChild(res); + var el = wrap(res); + el.type = name; + return el; +} +function Paper(w, h) { + var res, + desc, + defs, + proto = Paper.prototype; + if (w && w.tagName == "svg") { + if (w.snap in hub) { + return hub[w.snap]; + } + res = new Element(w); + desc = w.getElementsByTagName("desc")[0]; + defs = w.getElementsByTagName("defs")[0]; + if (!desc) { + desc = $("desc"); + desc.appendChild(glob.doc.createTextNode("Created with Snap")); + res.node.appendChild(desc); + } + if (!defs) { + defs = $("defs"); + res.node.appendChild(defs); + } + res.defs = defs; + for (var key in proto) if (proto[has](key)) { + res[key] = proto[key]; + } + res.paper = res.root = res; + } else { + res = make("svg", glob.doc.body); + $(res.node, { + height: h, + version: 1.1, + width: w, + xmlns: xmlns + }); + } + return res; +} +function wrap(dom) { + if (!dom) { + return dom; + } + if (dom instanceof Element || dom instanceof Fragment) { + return dom; + } + if (dom.tagName == "svg") { + return new Paper(dom); + } + return new Element(dom); +} +// gradients' helpers +function Gstops() { + return this.selectAll("stop"); +} +function GaddStop(color, offset) { + var stop = $("stop"), + attr = { + offset: +offset + "%" + }; + color = Snap.color(color); + attr["stop-color"] = color.hex; + if (color.opacity < 1) { + attr["stop-opacity"] = color.opacity; + } + $(stop, attr); + this.node.appendChild(stop); + return this; +} +function GgetBBox() { + if (this.type == "linearGradient") { + var x1 = $(this.node, "x1") || 0, + x2 = $(this.node, "x2") || 1, + y1 = $(this.node, "y1") || 0, + y2 = $(this.node, "y2") || 0; + return Snap._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1)); + } else { + var cx = this.node.cx || .5, + cy = this.node.cy || .5, + r = this.node.r || 0; + return Snap._.box(cx - r, cy - r, r * 2, r * 2); + } +} +function gradient(defs, str) { + var grad = arrayFirstValue(eve("snap.util.grad.parse", null, str)), + el; + if (!grad) { + return null; + } + grad.params.unshift(defs); + if (grad.type.toLowerCase() == "l") { + el = gradientLinear.apply(0, grad.params); + } else { + el = gradientRadial.apply(0, grad.params); + } + if (grad.type != grad.type.toLowerCase()) { + $(el.node, { + gradientUnits: "userSpaceOnUse" + }); + } + var stops = grad.stops, + len = stops.length, + start = 0, + j = 0; + function seed(i, end) { + var step = (end - start) / (i - j); + for (var k = j; k < i; k++) { + stops[k].offset = +(+start + step * (k - j)).toFixed(2); + } + j = i; + start = end; + } + len--; + for (var i = 0; i < len; i++) if ("offset" in stops[i]) { + seed(i, stops[i].offset); + } + stops[len].offset = stops[len].offset || 100; + seed(len, stops[len].offset); + for (i = 0; i <= len; i++) { + var stop = stops[i]; + el.addStop(stop.color, stop.offset); + } + return el; +} +function gradientLinear(defs, x1, y1, x2, y2) { + var el = make("linearGradient", defs); + el.stops = Gstops; + el.addStop = GaddStop; + el.getBBox = GgetBBox; + if (x1 != null) { + $(el.node, { + x1: x1, + y1: y1, + x2: x2, + y2: y2 + }); + } + return el; +} +function gradientRadial(defs, cx, cy, r, fx, fy) { + var el = make("radialGradient", defs); + el.stops = Gstops; + el.addStop = GaddStop; + el.getBBox = GgetBBox; + if (cx != null) { + $(el.node, { + cx: cx, + cy: cy, + r: r + }); + } + if (fx != null && fy != null) { + $(el.node, { + fx: fx, + fy: fy + }); + } + return el; +} +// Paper prototype methods +(function (proto) { + /*\ + * Paper.el + [ method ] + ** + * Creates an element on paper with a given name and no attributes + ** + - name (string) tag name + - attr (object) attributes + = (Element) the current element + > Usage + | var c = paper.circle(10, 10, 10); // is the same as... + | var c = paper.el("circle").attr({ + | cx: 10, + | cy: 10, + | r: 10 + | }); + | // and the same as + | var c = paper.el("circle", { + | cx: 10, + | cy: 10, + | r: 10 + | }); + \*/ + proto.el = function (name, attr) { + return make(name, this.node).attr(attr); + }; + /*\ + * Paper.rect + [ method ] + * + * Draws a rectangle + ** + - x (number) x coordinate of the top left corner + - y (number) y coordinate of the top left corner + - width (number) width + - height (number) height + - rx (number) #optional horizontal radius for rounded corners, default is 0 + - ry (number) #optional vertical radius for rounded corners, default is rx or 0 + = (object) the `rect` element + ** + > Usage + | // regular rectangle + | var c = paper.rect(10, 10, 50, 50); + | // rectangle with rounded corners + | var c = paper.rect(40, 40, 50, 50, 10); + \*/ + proto.rect = function (x, y, w, h, rx, ry) { + var attr; + if (ry == null) { + ry = rx; + } + if (is(x, "object") && "x" in x) { + attr = x; + } else if (x != null) { + attr = { + x: x, + y: y, + width: w, + height: h + }; + if (rx != null) { + attr.rx = rx; + attr.ry = ry; + } + } + return this.el("rect", attr); + }; + /*\ + * Paper.circle + [ method ] + ** + * Draws a circle + ** + - x (number) x coordinate of the centre + - y (number) y coordinate of the centre + - r (number) radius + = (object) the `circle` element + ** + > Usage + | var c = paper.circle(50, 50, 40); + \*/ + proto.circle = function (cx, cy, r) { + var attr; + if (is(cx, "object") && "cx" in cx) { + attr = cx; + } else if (cx != null) { + attr = { + cx: cx, + cy: cy, + r: r + }; + } + return this.el("circle", attr); + }; + + /*\ + * Paper.image + [ method ] + ** + * Places an image on the surface + ** + - src (string) URI of the source image + - x (number) x offset position + - y (number) y offset position + - width (number) width of the image + - height (number) height of the image + = (object) the `image` element + * or + = (object) Snap element object with type `image` + ** + > Usage + | var c = paper.image("apple.png", 10, 10, 80, 80); + \*/ + proto.image = function (src, x, y, width, height) { + var el = make("image", this.node); + if (is(src, "object") && "src" in src) { + el.attr(src); + } else if (src != null) { + var set = { + "xlink:href": src, + preserveAspectRatio: "none" + }; + if (x != null && y != null) { + set.x = x; + set.y = y; + } + if (width != null && height != null) { + set.width = width; + set.height = height; + } else { + preload(src, function () { + $(el.node, { + width: this.offsetWidth, + height: this.offsetHeight + }); + }); + } + $(el.node, set); + } + return el; + }; + /*\ + * Paper.ellipse + [ method ] + ** + * Draws an ellipse + ** + - x (number) x coordinate of the centre + - y (number) y coordinate of the centre + - rx (number) horizontal radius + - ry (number) vertical radius + = (object) the `ellipse` element + ** + > Usage + | var c = paper.ellipse(50, 50, 40, 20); + \*/ + proto.ellipse = function (cx, cy, rx, ry) { + var el = make("ellipse", this.node); + if (is(cx, "object") && "cx" in cx) { + el.attr(cx); + } else if (cx != null) { + el.attr({ + cx: cx, + cy: cy, + rx: rx, + ry: ry + }); + } + return el; + }; + // SIERRA Paper.path(): Unclear from the link what a Catmull-Rom curveto is, and why it would make life any easier. + /*\ + * Paper.path + [ method ] + ** + * Creates a `` element using the given string as the path's definition + - pathString (string) #optional path string in SVG format + * Path string consists of one-letter commands, followed by comma seprarated arguments in numerical form. Example: + | "M10,20L30,40" + * This example features two commands: `M`, with arguments `(10, 20)` and `L` with arguments `(30, 40)`. Uppercase letter commands express coordinates in absolute terms, while lowercase commands express them in relative terms from the most recently declared coordinates. + * + #

Here is short list of commands available, for more details see SVG path string format or article about path strings at MDN.

+ # + # + # + # + # + # + # + # + # + # + # + #
CommandNameParameters
Mmoveto(x y)+
Zclosepath(none)
Llineto(x y)+
Hhorizontal linetox+
Vvertical linetoy+
Ccurveto(x1 y1 x2 y2 x y)+
Ssmooth curveto(x2 y2 x y)+
Qquadratic Bézier curveto(x1 y1 x y)+
Tsmooth quadratic Bézier curveto(x y)+
Aelliptical arc(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
RCatmull-Rom curveto*x1 y1 (x y)+
+ * * _Catmull-Rom curveto_ is a not standard SVG command and added to make life easier. + * Note: there is a special case when a path consists of only three commands: `M10,10R…z`. In this case the path connects back to its starting point. + > Usage + | var c = paper.path("M10 10L90 90"); + | // draw a diagonal line: + | // move to 10,10, line to 90,90 + \*/ + proto.path = function (d) { + var el = make("path", this.node); + if (is(d, "object") && !is(d, "array")) { + el.attr(d); + } else if (d) { + el.attr({ + d: d + }); + } + return el; + }; +// SIERRA Paper.g(): Don't understand the code comment about the order being _different._ Wouldn't it be a rect followed by a circle? + /*\ + * Paper.g + [ method ] + ** + * Creates a group element + ** + - varargs (…) #optional elements to nest within the group + = (object) the `g` element + ** + > Usage + | var c1 = paper.circle(), + | c2 = paper.rect(), + | g = paper.g(c2, c1); // note that the order of elements is different + * or + | var c1 = paper.circle(), + | c2 = paper.rect(), + | g = paper.g(); + | g.add(c2, c1); + \*/ + /*\ + * Paper.group + [ method ] + ** + * See @Paper.g + \*/ + proto.group = proto.g = function (first) { + var el = make("g", this.node); + el.add = add2group; + for (var method in proto) if (proto[has](method)) { + el[method] = proto[method]; + } + if (arguments.length == 1 && first && !first.type) { + el.attr(first); + } else if (arguments.length) { + el.add(Array.prototype.slice.call(arguments, 0)); + } + return el; + }; + /*\ + * Paper.text + [ method ] + ** + * Draws a text string + ** + - x (number) x coordinate position + - y (number) y coordinate position + - text (string|array) The text string to draw or array of strings to nest within separate `` elements + = (object) the `text` element + ** + > Usage + | var t1 = paper.text(50, 50, "Snap"); + | var t2 = paper.text(50, 50, ["S","n","a","p"]); + | // Text path usage + | t1.attr({textpath: "M10,10L100,100"}); + | // or + | var pth = paper.path("M10,10L100,100"); + | t1.attr({textpath: pth}); + \*/ + proto.text = function (x, y, text) { + var el = make("text", this.node); + if (is(x, "object")) { + el.attr(x); + } else if (x != null) { + el.attr({ + x: x, + y: y, + text: text || "" + }); + } + return el; + }; + /*\ + * Paper.line + [ method ] + ** + * Draws a line + ** + - x1 (number) x coordinate position of the start + - y1 (number) y coordinate position of the start + - x2 (number) x coordinate position of the end + - y2 (number) y coordinate position of the end + = (object) the `line` element + ** + > Usage + | var t1 = paper.line(50, 50, 100, 100); + \*/ + proto.line = function (x1, y1, x2, y2) { + var el = make("line", this.node); + if (is(x1, "object")) { + el.attr(x1); + } else if (x1 != null) { + el.attr({ + x1: x1, + x2: x2, + y1: y1, + y2: y2 + }); + } + return el; + }; + /*\ + * Paper.polyline + [ method ] + ** + * Draws a polyline + ** + - points (array) array of points + * or + - varargs (…) points + = (object) the `polyline` element + ** + > Usage + | var p1 = paper.polyline([10, 10, 100, 100]); + | var p2 = paper.polyline(10, 10, 100, 100); + \*/ + proto.polyline = function (points) { + if (arguments.length > 1) { + points = Array.prototype.slice.call(arguments, 0); + } + var el = make("polyline", this.node); + if (is(points, "object") && !is(points, "array")) { + el.attr(points); + } else if (points != null) { + el.attr({ + points: points + }); + } + return el; + }; + /*\ + * Paper.polygon + [ method ] + ** + * Draws a polygon. See @Paper.polyline + \*/ + proto.polygon = function (points) { + if (arguments.length > 1) { + points = Array.prototype.slice.call(arguments, 0); + } + var el = make("polygon", this.node); + if (is(points, "object") && !is(points, "array")) { + el.attr(points); + } else if (points != null) { + el.attr({ + points: points + }); + } + return el; + }; + // gradients + (function () { + /*\ + * Paper.gradient + [ method ] + ** + * Creates a gradient element + ** + - gradient (string) gradient descriptor + > Gradient Descriptor + * The gradient descriptor is an expression formatted as + * follows: `()`. The `` can be + * either linear or radial. The uppercase `L` or `R` letters + * indicate absolute coordinates offset from the SVG surface. + * Lowercase `l` or `r` letters indicate coordinates + * calculated relative to the element to which the gradient is + * applied. Coordinates specify a linear gradient vector as + * `x1`, `y1`, `x2`, `y2`, or a radial gradient as `cx`, `cy`, + * `r` and optional `fx`, `fy` specifying a focal point away + * from the center of the circle. Specify `` as a list + * of dash-separated CSS color values. Each color may be + * followed by a custom offset value, separated with a colon + * character. + > Examples + * Linear gradient, relative from top-left corner to bottom-right + * corner, from black through red to white: + | var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff"); + * Linear gradient, absolute from (0, 0) to (100, 100), from black + * through red at 25% to white: + | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25%-#fff"); + * Radial gradient, relative from the center of the element with radius + * half the width, from black to white: + | var g = paper.gradient("r(0.5, 0.5, 0.5)#000-#fff"); + * To apply the gradient: + | paper.circle(50, 50, 40).attr({ + | fill: g + | }); + = (object) the `gradient` element + \*/ + proto.gradient = function (str) { + return gradient(this.defs, str); + }; + proto.gradientLinear = function (x1, y1, x2, y2) { + return gradientLinear(this.defs, x1, y1, x2, y2); + }; + proto.gradientRadial = function (cx, cy, r, fx, fy) { + return gradientRadial(this.defs, cx, cy, r, fx, fy); + }; + /*\ + * Paper.toString + [ method ] + ** + * Returns SVG code for the @Paper + = (string) SVG code for the @Paper + \*/ + proto.toString = function () { + var f = glob.doc.createDocumentFragment(), + d = glob.doc.createElement("div"), + svg = this.node.cloneNode(true), + res; + f.appendChild(d); + d.appendChild(svg); + $(svg, {xmlns: xmlns}); + res = d.innerHTML; + f.removeChild(f.firstChild); + return res; + }; + /*\ + * Paper.clear + [ method ] + ** + * Removes all child nodes of the paper, except . + \*/ + proto.clear = function () { + var node = this.node.firstChild, + next; + while (node) { + next = node.nextSibling; + if (node.tagName != "defs") { + node.parentNode.removeChild(node); + } + node = next; + } + }; + }()); +}(Paper.prototype)); + +// simple ajax +/*\ + * Snap.ajax + [ method ] + ** + * Simple implementation of Ajax + ** + - url (string) URL + - postData (object|string) data for post request + - callback (function) callback + - scope (object) #optional scope of callback + * or + - url (string) URL + - callback (function) callback + - scope (object) #optional scope of callback + = (XMLHttpRequest) the XMLHttpRequest object, just in case +\*/ +Snap.ajax = function (url, postData, callback, scope){ + var req = new XMLHttpRequest, + id = ID(); + if (req) { + if (is(postData, "function")) { + scope = callback; + callback = postData; + postData = null; + } else if (is(postData, "object")) { + var pd = []; + for (var key in postData) if (postData.hasOwnProperty(key)) { + pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key])); + } + postData = pd.join("&"); + } + req.open((postData ? "POST" : "GET"), url, true); + req.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + if (postData) { + req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + } + if (callback) { + eve.once("snap.ajax." + id + ".0", callback); + eve.once("snap.ajax." + id + ".200", callback); + eve.once("snap.ajax." + id + ".304", callback); + } + req.onreadystatechange = function() { + if (req.readyState != 4) return; + eve("snap.ajax." + id + "." + req.status, scope, req); + }; + if (req.readyState == 4) { + return req; + } + req.send(postData); + return req; + } +}; +/*\ + * Snap.load + [ method ] + ** + * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX) + ** + - url (string) URL + - callback (function) callback + - scope (object) #optional scope of callback +\*/ +Snap.load = function (url, callback, scope) { + Snap.ajax(url, function (req) { + var f = Snap.parse(req.responseText); + scope ? callback.call(scope, f) : callback(f); + }); +}; + +// Attributes event handlers +eve.on("snap.util.attr.mask", function (value) { + if (value instanceof Element || value instanceof Fragment) { + eve.stop(); + if (value instanceof Fragment && value.node.childNodes.length == 1) { + value = value.node.firstChild; + getSomeDefs(this).appendChild(value); + value = wrap(value); + } + if (value.type == "mask") { + var mask = value; + } else { + mask = make("mask", getSomeDefs(this)); + mask.node.appendChild(value.node); + !mask.node.id && $(mask.node, { + id: mask.id + }); + } + $(this.node, { + mask: URL(mask.id) + }); + } +}); +(function (clipIt) { + eve.on("snap.util.attr.clip", clipIt); + eve.on("snap.util.attr.clip-path", clipIt); + eve.on("snap.util.attr.clipPath", clipIt); +}(function (value) { + if (value instanceof Element || value instanceof Fragment) { + eve.stop(); + if (value.type == "clipPath") { + var clip = value; + } else { + clip = make("clipPath", getSomeDefs(this)); + clip.node.appendChild(value.node); + !clip.node.id && $(clip.node, { + id: clip.id + }); + } + $(this.node, { + "clip-path": URL(clip.id) + }); + } +})); +function fillStroke(name) { + return function (value) { + eve.stop(); + if (value instanceof Fragment && value.node.childNodes.length == 1 && + (value.node.firstChild.tagName == "radialGradient" || + value.node.firstChild.tagName == "linearGradient" || + value.node.firstChild.tagName == "pattern")) { + value = value.node.firstChild; + getSomeDefs(this).appendChild(value); + value = wrap(value); + } + if (value instanceof Element) { + if (value.type == "radialGradient" || value.type == "linearGradient" + || value.type == "pattern") { + if (!value.node.id) { + $(value.node, { + id: value.id + }); + } + var fill = URL(value.node.id); + } else { + fill = value.attr(name); + } + } else { + fill = Snap.color(value); + if (fill.error) { + var grad = gradient(getSomeDefs(this), value); + if (grad) { + if (!grad.node.id) { + $(grad.node, { + id: grad.id + }); + } + fill = URL(grad.node.id); + } else { + fill = value; + } + } else { + fill = Str(fill); + } + } + var attrs = {}; + attrs[name] = fill; + $(this.node, attrs); + this.node.style[name] = E; + }; +} +eve.on("snap.util.attr.fill", fillStroke("fill")); +eve.on("snap.util.attr.stroke", fillStroke("stroke")); +var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i; +eve.on("snap.util.grad.parse", function parseGrad(string) { + string = Str(string); + var tokens = string.match(gradrg); + if (!tokens) { + return null; + } + var type = tokens[1], + params = tokens[2], + stops = tokens[3]; + params = params.split(/\s*,\s*/).map(function (el) { + return +el == el ? +el : el; + }); + if (params.length == 1 && params[0] == 0) { + params = []; + } + stops = stops.split("-"); + stops = stops.map(function (el) { + el = el.split(":"); + var out = { + color: el[0] + }; + if (el[1]) { + out.offset = el[1]; + } + return out; + }); + return { + type: type, + params: params, + stops: stops + }; +}); + +eve.on("snap.util.attr.d", function (value) { + eve.stop(); + if (is(value, "array") && is(value[0], "array")) { + value = Snap.path.toString.call(value); + } + value = Str(value); + if (value.match(/[ruo]/i)) { + value = Snap.path.toAbsolute(value); + } + $(this.node, {d: value}); +})(-1); +eve.on("snap.util.attr.#text", function (value) { + eve.stop(); + value = Str(value); + var txt = glob.doc.createTextNode(value); + while (this.node.firstChild) { + this.node.removeChild(this.node.firstChild); + } + this.node.appendChild(txt); +})(-1); +eve.on("snap.util.attr.path", function (value) { + eve.stop(); + this.attr({d: value}); +})(-1); +eve.on("snap.util.attr.viewBox", function (value) { + var vb; + if (is(value, "object") && "x" in value) { + vb = [value.x, value.y, value.width, value.height].join(" "); + } else if (is(value, "array")) { + vb = value.join(" "); + } else { + vb = value; + } + $(this.node, { + viewBox: vb + }); + eve.stop(); +})(-1); +eve.on("snap.util.attr.transform", function (value) { + this.transform(value); + eve.stop(); +})(-1); +eve.on("snap.util.attr.r", function (value) { + if (this.type == "rect") { + eve.stop(); + $(this.node, { + rx: value, + ry: value + }); + } +})(-1); +eve.on("snap.util.attr.textpath", function (value) { + eve.stop(); + if (this.type == "text") { + var id, tp, node; + if (!value && this.textPath) { + tp = this.textPath; + while (tp.node.firstChild) { + this.node.appendChild(tp.node.firstChild); + } + tp.remove(); + delete this.textPath; + return; + } + if (is(value, "string")) { + var defs = getSomeDefs(this), + path = wrap(defs.parentNode).path(value); + defs.appendChild(path.node); + id = path.id; + path.attr({id: id}); + } else { + value = wrap(value); + if (value instanceof Element) { + id = value.attr("id"); + if (!id) { + id = value.id; + value.attr({id: id}); + } + } + } + if (id) { + tp = this.textPath; + node = this.node; + if (tp) { + tp.attr({"xlink:href": "#" + id}); + } else { + tp = $("textPath", { + "xlink:href": "#" + id + }); + while (node.firstChild) { + tp.appendChild(node.firstChild); + } + node.appendChild(tp); + this.textPath = wrap(tp); + } + } + } +})(-1); +eve.on("snap.util.attr.text", function (value) { + if (this.type == "text") { + var i = 0, + node = this.node, + tuner = function (chunk) { + var out = $("tspan"); + if (is(chunk, "array")) { + for (var i = 0; i < chunk.length; i++) { + out.appendChild(tuner(chunk[i])); + } + } else { + out.appendChild(glob.doc.createTextNode(chunk)); + } + out.normalize && out.normalize(); + return out; + }; + while (node.firstChild) { + node.removeChild(node.firstChild); + } + var tuned = tuner(value); + while (tuned.firstChild) { + node.appendChild(tuned.firstChild); + } + } + eve.stop(); +})(-1); +// default +var cssAttr = { + "alignment-baseline": 0, + "baseline-shift": 0, + "clip": 0, + "clip-path": 0, + "clip-rule": 0, + "color": 0, + "color-interpolation": 0, + "color-interpolation-filters": 0, + "color-profile": 0, + "color-rendering": 0, + "cursor": 0, + "direction": 0, + "display": 0, + "dominant-baseline": 0, + "enable-background": 0, + "fill": 0, + "fill-opacity": 0, + "fill-rule": 0, + "filter": 0, + "flood-color": 0, + "flood-opacity": 0, + "font": 0, + "font-family": 0, + "font-size": 0, + "font-size-adjust": 0, + "font-stretch": 0, + "font-style": 0, + "font-variant": 0, + "font-weight": 0, + "glyph-orientation-horizontal": 0, + "glyph-orientation-vertical": 0, + "image-rendering": 0, + "kerning": 0, + "letter-spacing": 0, + "lighting-color": 0, + "marker": 0, + "marker-end": 0, + "marker-mid": 0, + "marker-start": 0, + "mask": 0, + "opacity": 0, + "overflow": 0, + "pointer-events": 0, + "shape-rendering": 0, + "stop-color": 0, + "stop-opacity": 0, + "stroke": 0, + "stroke-dasharray": 0, + "stroke-dashoffset": 0, + "stroke-linecap": 0, + "stroke-linejoin": 0, + "stroke-miterlimit": 0, + "stroke-opacity": 0, + "stroke-width": 0, + "text-anchor": 0, + "text-decoration": 0, + "text-rendering": 0, + "unicode-bidi": 0, + "visibility": 0, + "word-spacing": 0, + "writing-mode": 0 +}; + +eve.on("snap.util.attr", function (value) { + var att = eve.nt(), + attr = {}; + att = att.substring(att.lastIndexOf(".") + 1); + attr[att] = value; + var style = att.replace(/-(\w)/gi, function (all, letter) { + return letter.toUpperCase(); + }), + css = att.replace(/[A-Z]/g, function (letter) { + return "-" + letter.toLowerCase(); + }); + if (cssAttr[has](css)) { + this.node.style[style] = value == null ? E : value; + } else { + $(this.node, attr); + } +}); +eve.on("snap.util.getattr.transform", function () { + eve.stop(); + return this.transform(); +})(-1); +eve.on("snap.util.getattr.textpath", function () { + eve.stop(); + return this.textPath; +})(-1); +// Markers +(function () { + function getter(end) { + return function () { + eve.stop(); + var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end); + if (style == "none") { + return style; + } else { + return Snap(glob.doc.getElementById(style.match(reURLValue)[1])); + } + }; + } + function setter(end) { + return function (value) { + eve.stop(); + var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1); + if (value == "" || !value) { + this.node.style[name] = "none"; + return; + } + if (value.type == "marker") { + var id = value.node.id; + if (!id) { + $(value.node, {id: value.id}); + } + this.node.style[name] = URL(id); + return; + } + }; + } + eve.on("snap.util.getattr.marker-end", getter("end"))(-1); + eve.on("snap.util.getattr.markerEnd", getter("end"))(-1); + eve.on("snap.util.getattr.marker-start", getter("start"))(-1); + eve.on("snap.util.getattr.markerStart", getter("start"))(-1); + eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1); + eve.on("snap.util.getattr.markerMid", getter("mid"))(-1); + eve.on("snap.util.attr.marker-end", setter("end"))(-1); + eve.on("snap.util.attr.markerEnd", setter("end"))(-1); + eve.on("snap.util.attr.marker-start", setter("start"))(-1); + eve.on("snap.util.attr.markerStart", setter("start"))(-1); + eve.on("snap.util.attr.marker-mid", setter("mid"))(-1); + eve.on("snap.util.attr.markerMid", setter("mid"))(-1); +}()); +eve.on("snap.util.getattr.r", function () { + if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) { + eve.stop(); + return $(this.node, "rx"); + } +})(-1); +function textExtract(node) { + var out = []; + var children = node.childNodes; + for (var i = 0, ii = children.length; i < ii; i++) { + var chi = children[i]; + if (chi.nodeType == 3) { + out.push(chi.nodeValue); + } + if (chi.tagName == "tspan") { + if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) { + out.push(chi.firstChild.nodeValue); + } else { + out.push(textExtract(chi)); + } + } + } + return out; +} +eve.on("snap.util.getattr.text", function () { + if (this.type == "text" || this.type == "tspan") { + eve.stop(); + var out = textExtract(this.node); + return out.length == 1 ? out[0] : out; + } +})(-1); +eve.on("snap.util.getattr.#text", function () { + return this.node.textContent; +})(-1); +eve.on("snap.util.getattr.viewBox", function () { + eve.stop(); + var vb = $(this.node, "viewBox").split(separator); + return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]); + // TODO: investigate why I need to z-index it +})(-1); +eve.on("snap.util.getattr.points", function () { + var p = $(this.node, "points"); + eve.stop(); + return p.split(separator); +}); +eve.on("snap.util.getattr.path", function () { + var p = $(this.node, "d"); + eve.stop(); + return p; +}); +// default +eve.on("snap.util.getattr", function () { + var att = eve.nt(); + att = att.substring(att.lastIndexOf(".") + 1); + var css = att.replace(/[A-Z]/g, function (letter) { + return "-" + letter.toLowerCase(); + }); + if (cssAttr[has](css)) { + return glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue(css); + } else { + return $(this.node, att); + } +}); +var getOffset = function (elem) { + var box = elem.getBoundingClientRect(), + doc = elem.ownerDocument, + body = doc.body, + docElem = doc.documentElement, + clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, + top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, + left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; + return { + y: top, + x: left + }; +}; +/*\ + * Snap.getElementByPoint + [ method ] + ** + * Returns you topmost element under given point. + ** + = (object) Snap element object + - x (number) x coordinate from the top left corner of the window + - y (number) y coordinate from the top left corner of the window + > Usage + | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"}); +\*/ +Snap.getElementByPoint = function (x, y) { + var paper = this, + svg = paper.canvas, + target = glob.doc.elementFromPoint(x, y); + if (glob.win.opera && target.tagName == "svg") { + var so = getOffset(target), + sr = target.createSVGRect(); + sr.x = x - so.x; + sr.y = y - so.y; + sr.width = sr.height = 1; + var hits = target.getIntersectionList(sr, null); + if (hits.length) { + target = hits[hits.length - 1]; + } + } + if (!target) { + return null; + } + return wrap(target); +}; +/*\ + * Snap.plugin + [ method ] + ** + * Let you write plugins. You pass in a function with four arguments, like this: + | Snap.plugin(function (Snap, Element, Paper, global) { + | Snap.newmethod = function () {}; + | Element.prototype.newmethod = function () {}; + | Paper.prototype.newmethod = function () {}; + | }); + * Inside the function you have access to all main objects (and their + * prototypes). This allow you to extend anything you want. + ** + - f (function) your plugin body +\*/ +Snap.plugin = function (f) { + f(Snap, Element, Paper, glob); +}; +glob.win.Snap = Snap; +return Snap; +}()); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + is = Snap.is, + clone = Snap._.clone, + has = "hasOwnProperty", + p2s = /,?([a-z]),?/gi, + toFloat = parseFloat, + math = Math, + PI = math.PI, + mmin = math.min, + mmax = math.max, + pow = math.pow, + abs = math.abs; + function paths(ps) { + var p = paths.ps = paths.ps || {}; + if (p[ps]) { + p[ps].sleep = 100; + } else { + p[ps] = { + sleep: 100 + }; + } + setTimeout(function () { + for (var key in p) if (p[has](key) && key != ps) { + p[key].sleep--; + !p[key].sleep && delete p[key]; + } + }); + return p[ps]; + } + function box(x, y, width, height) { + if (x == null) { + x = y = width = height = 0; + } + if (y == null) { + y = x.y; + width = x.width; + height = x.height; + x = x.x; + } + return { + x: x, + y: y, + width: width, + w: width, + height: height, + h: height, + x2: x + width, + y2: y + height, + cx: x + width / 2, + cy: y + height / 2, + r1: math.min(width, height) / 2, + r2: math.max(width, height) / 2, + r0: math.sqrt(width * width + height * height) / 2, + path: rectPath(x, y, width, height), + vb: [x, y, width, height].join(" ") + }; + } + function toString() { + return this.join(",").replace(p2s, "$1"); + } + function pathClone(pathArray) { + var res = clone(pathArray); + res.toString = toString; + return res; + } + function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { + if (length == null) { + return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); + } else { + return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, + getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length)); + } + } + function getLengthFactory(istotal, subpath) { + function O(val) { + return +(+val).toFixed(3); + } + return Snap._.cacher(function (path, length, onlystart) { + if (path instanceof Element) { + path = path.attr("d"); + } + path = path2curve(path); + var x, y, p, l, sp = "", subpaths = {}, point, + len = 0; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = +p[1]; + y = +p[2]; + } else { + l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + if (len + l > length) { + if (subpath && !subpaths.start) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + sp += [ + "C" + O(point.start.x), + O(point.start.y), + O(point.m.x), + O(point.m.y), + O(point.x), + O(point.y) + ]; + if (onlystart) {return sp;} + subpaths.start = sp; + sp = [ + "M" + O(point.x), + O(point.y) + "C" + O(point.n.x), + O(point.n.y), + O(point.end.x), + O(point.end.y), + O(p[5]), + O(p[6]) + ].join(); + len += l; + x = +p[5]; + y = +p[6]; + continue; + } + if (!istotal && !subpath) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + return point; + } + } + len += l; + x = +p[5]; + y = +p[6]; + } + sp += p.shift() + p; + } + subpaths.end = sp; + point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); + return point; + }, null, Snap._.clone); + } + var getTotalLength = getLengthFactory(1), + getPointAtLength = getLengthFactory(), + getSubpathsAtLength = getLengthFactory(0, 1); + function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t, + t13 = pow(t1, 3), + t12 = pow(t1, 2), + t2 = t * t, + t3 = t2 * t, + x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, + y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, + mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), + ax = t1 * p1x + t * c1x, + ay = t1 * p1y + t * c1y, + cx = t1 * c2x + t * p2x, + cy = t1 * c2y + t * p2y, + alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI); + // (mx > nx || my < ny) && (alpha += 180); + return { + x: x, + y: y, + m: {x: mx, y: my}, + n: {x: nx, y: ny}, + start: {x: ax, y: ay}, + end: {x: cx, y: cy}, + alpha: alpha + }; + } + function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + if (!Snap.is(p1x, "array")) { + p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y]; + } + var bbox = curveDim.apply(null, p1x); + return box( + bbox.min.x, + bbox.min.y, + bbox.max.x - bbox.min.x, + bbox.max.y - bbox.min.y + ); + } + function isPointInsideBBox(bbox, x, y) { + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + function isBBoxIntersect(bbox1, bbox2) { + bbox1 = box(bbox1); + bbox2 = box(bbox2); + return isPointInsideBBox(bbox2, bbox1.x, bbox1.y) + || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y) + || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2) + || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2) + || isPointInsideBBox(bbox1, bbox2.x, bbox2.y) + || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y) + || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2) + || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2) + || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x + || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) + && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y + || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); + } + function base3(t, p1, p2, p3, p4) { + var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, + t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; + return t * t2 - 3 * p1 + 3 * p2; + } + function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { + if (z == null) { + z = 1; + } + z = z > 1 ? 1 : z < 0 ? 0 : z; + var z2 = z / 2, + n = 12, + Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], + Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472], + sum = 0; + for (var i = 0; i < n; i++) { + var ct = z2 * Tvalues[i] + z2, + xbase = base3(ct, x1, x2, x3, x4), + ybase = base3(ct, y1, y2, y3, y4), + comb = xbase * xbase + ybase * ybase; + sum += Cvalues[i] * math.sqrt(comb); + } + return z2 * sum; + } + function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) { + if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) { + return; + } + var t = 1, + step = t / 2, + t2 = t - step, + l, + e = .01; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + while (abs(l - ll) > e) { + step /= 2; + t2 += (l < ll ? 1 : -1) * step; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + } + return t2; + } + function intersect(x1, y1, x2, y2, x3, y3, x4, y4) { + if ( + mmax(x1, x2) < mmin(x3, x4) || + mmin(x1, x2) > mmax(x3, x4) || + mmax(y1, y2) < mmin(y3, y4) || + mmin(y1, y2) > mmax(y3, y4) + ) { + return; + } + var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), + ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), + denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + + if (!denominator) { + return; + } + var px = nx / denominator, + py = ny / denominator, + px2 = +px.toFixed(2), + py2 = +py.toFixed(2); + if ( + px2 < +mmin(x1, x2).toFixed(2) || + px2 > +mmax(x1, x2).toFixed(2) || + px2 < +mmin(x3, x4).toFixed(2) || + px2 > +mmax(x3, x4).toFixed(2) || + py2 < +mmin(y1, y2).toFixed(2) || + py2 > +mmax(y1, y2).toFixed(2) || + py2 < +mmin(y3, y4).toFixed(2) || + py2 > +mmax(y3, y4).toFixed(2) + ) { + return; + } + return {x: px, y: py}; + } + function inter(bez1, bez2) { + return interHelper(bez1, bez2); + } + function interCount(bez1, bez2) { + return interHelper(bez1, bez2, 1); + } + function interHelper(bez1, bez2, justCount) { + var bbox1 = bezierBBox(bez1), + bbox2 = bezierBBox(bez2); + if (!isBBoxIntersect(bbox1, bbox2)) { + return justCount ? 0 : []; + } + var l1 = bezlen.apply(0, bez1), + l2 = bezlen.apply(0, bez2), + n1 = ~~(l1 / 5), + n2 = ~~(l2 / 5), + dots1 = [], + dots2 = [], + xy = {}, + res = justCount ? 0 : []; + for (var i = 0; i < n1 + 1; i++) { + var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); + dots1.push({x: p.x, y: p.y, t: i / n1}); + } + for (i = 0; i < n2 + 1; i++) { + p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); + dots2.push({x: p.x, y: p.y, t: i / n2}); + } + for (i = 0; i < n1; i++) { + for (var j = 0; j < n2; j++) { + var di = dots1[i], + di1 = dots1[i + 1], + dj = dots2[j], + dj1 = dots2[j + 1], + ci = abs(di1.x - di.x) < .001 ? "y" : "x", + cj = abs(dj1.x - dj.x) < .001 ? "y" : "x", + is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y); + if (is) { + if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) { + continue; + } + xy[is.x.toFixed(4)] = is.y.toFixed(4); + var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), + t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); + if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { + if (justCount) { + res++; + } else { + res.push({ + x: is.x, + y: is.y, + t1: t1, + t2: t2 + }); + } + } + } + } + } + return res; + } + function pathIntersection(path1, path2) { + return interPathHelper(path1, path2); + } + function pathIntersectionNumber(path1, path2) { + return interPathHelper(path1, path2, 1); + } + function interPathHelper(path1, path2, justCount) { + path1 = path2curve(path1); + path2 = path2curve(path2); + var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, + res = justCount ? 0 : []; + for (var i = 0, ii = path1.length; i < ii; i++) { + var pi = path1[i]; + if (pi[0] == "M") { + x1 = x1m = pi[1]; + y1 = y1m = pi[2]; + } else { + if (pi[0] == "C") { + bez1 = [x1, y1].concat(pi.slice(1)); + x1 = bez1[6]; + y1 = bez1[7]; + } else { + bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; + x1 = x1m; + y1 = y1m; + } + for (var j = 0, jj = path2.length; j < jj; j++) { + var pj = path2[j]; + if (pj[0] == "M") { + x2 = x2m = pj[1]; + y2 = y2m = pj[2]; + } else { + if (pj[0] == "C") { + bez2 = [x2, y2].concat(pj.slice(1)); + x2 = bez2[6]; + y2 = bez2[7]; + } else { + bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; + x2 = x2m; + y2 = y2m; + } + var intr = interHelper(bez1, bez2, justCount); + if (justCount) { + res += intr; + } else { + for (var k = 0, kk = intr.length; k < kk; k++) { + intr[k].segment1 = i; + intr[k].segment2 = j; + intr[k].bez1 = bez1; + intr[k].bez2 = bez2; + } + res = res.concat(intr); + } + } + } + } + } + return res; + } + function isPointInsidePath(path, x, y) { + var bbox = pathBBox(path); + return isPointInsideBBox(bbox, x, y) && + interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1; + } + function pathBBox(path) { + var pth = paths(path); + if (pth.bbox) { + return clone(pth.bbox); + } + if (!path) { + return box(); + } + path = path2curve(path); + var x = 0, + y = 0, + X = [], + Y = [], + p; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = p[1]; + y = p[2]; + X.push(x); + Y.push(y); + } else { + var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + X = X.concat(dim.min.x, dim.max.x); + Y = Y.concat(dim.min.y, dim.max.y); + x = p[5]; + y = p[6]; + } + } + var xmin = mmin.apply(0, X), + ymin = mmin.apply(0, Y), + xmax = mmax.apply(0, X), + ymax = mmax.apply(0, Y), + bb = box(xmin, ymin, xmax - xmin, ymax - ymin); + pth.bbox = clone(bb); + return bb; + } + function rectPath(x, y, w, h, r) { + if (r) { + return [ + ["M", x + r, y], + ["l", w - r * 2, 0], + ["a", r, r, 0, 0, 1, r, r], + ["l", 0, h - r * 2], + ["a", r, r, 0, 0, 1, -r, r], + ["l", r * 2 - w, 0], + ["a", r, r, 0, 0, 1, -r, -r], + ["l", 0, r * 2 - h], + ["a", r, r, 0, 0, 1, r, -r], + ["z"] + ]; + } + var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; + res.toString = toString; + return res; + } + function ellipsePath(x, y, rx, ry, a) { + if (a == null && ry == null) { + ry = rx; + } + if (a != null) { + var rad = Math.PI / 180, + x1 = x + rx * Math.cos(-ry * rad), + x2 = x + rx * Math.cos(-a * rad), + y1 = y + rx * Math.sin(-ry * rad), + y2 = y + rx * Math.sin(-a * rad), + res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]]; + } else { + res = [ + ["M", x, y], + ["m", 0, -ry], + ["a", rx, ry, 0, 1, 1, 0, 2 * ry], + ["a", rx, ry, 0, 1, 1, 0, -2 * ry], + ["z"] + ]; + } + res.toString = toString; + return res; + } + var unit2px = Snap._unit2px, + getPath = { + path: function (el) { + return el.attr("path"); + }, + circle: function (el) { + var attr = unit2px(el); + return ellipsePath(attr.cx, attr.cy, attr.r); + }, + ellipse: function (el) { + var attr = unit2px(el); + return ellipsePath(attr.cx, attr.cy, attr.rx, attr.ry); + }, + rect: function (el) { + var attr = unit2px(el); + return rectPath(attr.x, attr.y, attr.width, attr.height, attr.rx, attr.ry); + }, + image: function (el) { + var attr = unit2px(el); + return rectPath(attr.x, attr.y, attr.width, attr.height); + }, + text: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + g: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + symbol: function (el) { + var bbox = el.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + line: function (el) { + return "M" + [el.attr("x1"), el.attr("y1"), el.attr("x2"), el.attr("y2")]; + }, + polyline: function (el) { + return "M" + el.attr("points"); + }, + polygon: function (el) { + return "M" + el.attr("points") + "z"; + }, + svg: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + deflt: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + } + }; + function pathToRelative(pathArray) { + var pth = paths(pathArray), + lowerCase = String.prototype.toLowerCase; + if (pth.rel) { + return pathClone(pth.rel); + } + if (!Snap.is(pathArray, "array") || !Snap.is(pathArray && pathArray[0], "array")) { + pathArray = Snap.parsePathString(pathArray); + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0; + if (pathArray[0][0] == "M") { + x = pathArray[0][1]; + y = pathArray[0][2]; + mx = x; + my = y; + start++; + res.push(["M", x, y]); + } + for (var i = start, ii = pathArray.length; i < ii; i++) { + var r = res[i] = [], + pa = pathArray[i]; + if (pa[0] != lowerCase.call(pa[0])) { + r[0] = lowerCase.call(pa[0]); + switch (r[0]) { + case "a": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] - x).toFixed(3); + r[7] = +(pa[7] - y).toFixed(3); + break; + case "v": + r[1] = +(pa[1] - y).toFixed(3); + break; + case "m": + mx = pa[1]; + my = pa[2]; + default: + for (var j = 1, jj = pa.length; j < jj; j++) { + r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); + } + } + } else { + r = res[i] = []; + if (pa[0] == "m") { + mx = pa[1] + x; + my = pa[2] + y; + } + for (var k = 0, kk = pa.length; k < kk; k++) { + res[i][k] = pa[k]; + } + } + var len = res[i].length; + switch (res[i][0]) { + case "z": + x = mx; + y = my; + break; + case "h": + x += +res[i][len - 1]; + break; + case "v": + y += +res[i][len - 1]; + break; + default: + x += +res[i][len - 2]; + y += +res[i][len - 1]; + } + } + res.toString = toString; + pth.rel = pathClone(res); + return res; + } + function pathToAbsolute(pathArray) { + var pth = paths(pathArray); + if (pth.abs) { + return pathClone(pth.abs); + } + if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption + pathArray = Snap.parsePathString(pathArray); + } + if (!pathArray || !pathArray.length) { + return [["M", 0, 0]]; + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0, + pa0; + if (pathArray[0][0] == "M") { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + start++; + res[0] = ["M", x, y]; + } + var crz = pathArray.length == 3 && + pathArray[0][0] == "M" && + pathArray[1][0].toUpperCase() == "R" && + pathArray[2][0].toUpperCase() == "Z"; + for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { + res.push(r = []); + pa = pathArray[i]; + pa0 = pa[0]; + if (pa0 != pa0.toUpperCase()) { + r[0] = pa0.toUpperCase(); + switch (r[0]) { + case "A": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] + x); + r[7] = +(pa[7] + y); + break; + case "V": + r[1] = +pa[1] + y; + break; + case "H": + r[1] = +pa[1] + x; + break; + case "R": + var dots = [x, y].concat(pa.slice(1)); + for (var j = 2, jj = dots.length; j < jj; j++) { + dots[j] = +dots[j] + x; + dots[++j] = +dots[j] + y; + } + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + break; + case "O": + res.pop(); + dots = ellipsePath(x, y, pa[1], pa[2]); + dots.push(dots[0]); + res = res.concat(dots); + break; + case "U": + res.pop(); + res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); + r = ["U"].concat(res[res.length - 1].slice(-2)); + break; + case "M": + mx = +pa[1] + x; + my = +pa[2] + y; + default: + for (j = 1, jj = pa.length; j < jj; j++) { + r[j] = +pa[j] + ((j % 2) ? x : y); + } + } + } else if (pa0 == "R") { + dots = [x, y].concat(pa.slice(1)); + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + r = ["R"].concat(pa.slice(-2)); + } else if (pa0 == "O") { + res.pop(); + dots = ellipsePath(x, y, pa[1], pa[2]); + dots.push(dots[0]); + res = res.concat(dots); + } else if (pa0 == "U") { + res.pop(); + res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); + r = ["U"].concat(res[res.length - 1].slice(-2)); + } else { + for (var k = 0, kk = pa.length; k < kk; k++) { + r[k] = pa[k]; + } + } + pa0 = pa0.toUpperCase(); + if (pa0 != "O") { + switch (r[0]) { + case "Z": + x = mx; + y = my; + break; + case "H": + x = r[1]; + break; + case "V": + y = r[1]; + break; + case "M": + mx = r[r.length - 2]; + my = r[r.length - 1]; + default: + x = r[r.length - 2]; + y = r[r.length - 1]; + } + } + } + res.toString = toString; + pth.abs = pathClone(res); + return res; + } + function l2c(x1, y1, x2, y2) { + return [x1, y1, x2, y2, x2, y2]; + } + function q2c(x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, + _23 = 2 / 3; + return [ + _13 * x1 + _23 * ax, + _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, + _13 * y2 + _23 * ay, + x2, + y2 + ]; + } + function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { + // for more information of where this math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var _120 = PI * 120 / 180, + rad = PI / 180 * (+angle || 0), + res = [], + xy, + rotate = Snap._.cacher(function (x, y, rad) { + var X = x * math.cos(rad) - y * math.sin(rad), + Y = x * math.sin(rad) + y * math.cos(rad); + return {x: X, y: Y}; + }); + if (!recursive) { + xy = rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + var cos = math.cos(PI / 180 * angle), + sin = math.sin(PI / 180 * angle), + x = (x1 - x2) / 2, + y = (y1 - y2) / 2; + var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = math.sqrt(h); + rx = h * rx; + ry = h * ry; + } + var rx2 = rx * rx, + ry2 = ry * ry, + k = (large_arc_flag == sweep_flag ? -1 : 1) * + math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), + cx = k * rx * y / ry + (x1 + x2) / 2, + cy = k * -ry * x / rx + (y1 + y2) / 2, + f1 = math.asin(((y1 - cy) / ry).toFixed(9)), + f2 = math.asin(((y2 - cy) / ry).toFixed(9)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + f1 < 0 && (f1 = PI * 2 + f1); + f2 < 0 && (f2 = PI * 2 + f2); + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + var df = f2 - f1; + if (abs(df) > _120) { + var f2old = f2, + x2old = x2, + y2old = y2; + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * math.cos(f2); + y2 = cy + ry * math.sin(f2); + res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + df = f2 - f1; + var c1 = math.cos(f1), + s1 = math.sin(f1), + c2 = math.cos(f2), + s2 = math.sin(f2), + t = math.tan(df / 4), + hx = 4 / 3 * rx * t, + hy = 4 / 3 * ry * t, + m1 = [x1, y1], + m2 = [x1 + hx * s1, y1 - hy * c1], + m3 = [x2 + hx * s2, y2 - hy * c2], + m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4].concat(res); + } else { + res = [m2, m3, m4].concat(res).join().split(","); + var newres = []; + for (var i = 0, ii = res.length; i < ii; i++) { + newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; + } + return newres; + } + } + function findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t; + return { + x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, + y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y + }; + } + function curveDim(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x), + b = 2 * (c1x - p1x) - 2 * (c2x - c1x), + c = p1x - c1x, + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a, + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a, + y = [p1y, p2y], + x = [p1x, p2x], + dot; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y); + b = 2 * (c1y - p1y) - 2 * (c2y - c1y); + c = p1y - c1y; + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a; + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + return { + min: {x: mmin.apply(0, x), y: mmin.apply(0, y)}, + max: {x: mmax.apply(0, x), y: mmax.apply(0, y)} + }; + } + function path2curve(path, path2) { + var pth = !path2 && paths(path); + if (!path2 && pth.curve) { + return pathClone(pth.curve); + } + var p = pathToAbsolute(path), + p2 = path2 && pathToAbsolute(path2), + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + processPath = function (path, d) { + var nx, ny; + if (!path) { + return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; + } + !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null); + switch (path[0]) { + case "M": + d.X = path[1]; + d.Y = path[2]; + break; + case "A": + path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1)))); + break; + case "S": + nx = d.x + (d.x - (d.bx || d.x)); + ny = d.y + (d.y - (d.by || d.y)); + path = ["C", nx, ny].concat(path.slice(1)); + break; + case "T": + d.qx = d.x + (d.x - (d.qx || d.x)); + d.qy = d.y + (d.y - (d.qy || d.y)); + path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); + break; + case "Q": + d.qx = path[1]; + d.qy = path[2]; + path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4])); + break; + case "L": + path = ["C"].concat(l2c(d.x, d.y, path[1], path[2])); + break; + case "H": + path = ["C"].concat(l2c(d.x, d.y, path[1], d.y)); + break; + case "V": + path = ["C"].concat(l2c(d.x, d.y, d.x, path[1])); + break; + case "Z": + path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); + break; + } + return path; + }, + fixArc = function (pp, i) { + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + while (pi.length) { + pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6))); + } + pp.splice(i, 1); + ii = mmax(p.length, p2 && p2.length || 0); + } + }, + fixM = function (path1, path2, a1, a2, i) { + if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { + path2.splice(i, 0, ["M", a2.x, a2.y]); + a1.bx = 0; + a1.by = 0; + a1.x = path1[i][1]; + a1.y = path1[i][2]; + ii = mmax(p.length, p2 && p2.length || 0); + } + }; + for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) { + p[i] = processPath(p[i], attrs); + fixArc(p, i); + p2 && (p2[i] = processPath(p2[i], attrs2)); + p2 && fixArc(p2, i); + fixM(p, p2, attrs, attrs2, i); + fixM(p2, p, attrs2, attrs, i); + var seg = p[i], + seg2 = p2 && p2[i], + seglen = seg.length, + seg2len = p2 && seg2.length; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; + attrs.by = toFloat(seg[seglen - 3]) || attrs.y; + attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); + attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); + attrs2.x = p2 && seg2[seg2len - 2]; + attrs2.y = p2 && seg2[seg2len - 1]; + } + if (!p2) { + pth.curve = pathClone(p); + } + return p2 ? [p, p2] : p; + } + function mapPath(path, matrix) { + if (!matrix) { + return path; + } + var x, y, i, j, ii, jj, pathi; + path = path2curve(path); + for (i = 0, ii = path.length; i < ii; i++) { + pathi = path[i]; + for (j = 1, jj = pathi.length; j < jj; j += 2) { + x = matrix.x(pathi[j], pathi[j + 1]); + y = matrix.y(pathi[j], pathi[j + 1]); + pathi[j] = x; + pathi[j + 1] = y; + } + } + return path; + } + + // http://schepers.cc/getting-to-the-point + function catmullRom2bezier(crp, z) { + var d = []; + for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { + var p = [ + {x: +crp[i - 2], y: +crp[i - 1]}, + {x: +crp[i], y: +crp[i + 1]}, + {x: +crp[i + 2], y: +crp[i + 3]}, + {x: +crp[i + 4], y: +crp[i + 5]} + ]; + if (z) { + if (!i) { + p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}; + } else if (iLen - 4 == i) { + p[3] = {x: +crp[0], y: +crp[1]}; + } else if (iLen - 2 == i) { + p[2] = {x: +crp[0], y: +crp[1]}; + p[3] = {x: +crp[2], y: +crp[3]}; + } + } else { + if (iLen - 4 == i) { + p[3] = p[2]; + } else if (!i) { + p[0] = {x: +crp[i], y: +crp[i + 1]}; + } + } + d.push(["C", + (-p[0].x + 6 * p[1].x + p[2].x) / 6, + (-p[0].y + 6 * p[1].y + p[2].y) / 6, + (p[1].x + 6 * p[2].x - p[3].x) / 6, + (p[1].y + 6*p[2].y - p[3].y) / 6, + p[2].x, + p[2].y + ]); + } + + return d; + } + + // export + Snap.path = paths; + + /*\ + * Snap.path.getTotalLength + [ method ] + ** + * Returns the length of the given path in pixels + ** + - path (string) SVG path string + ** + = (number) length + \*/ + Snap.path.getTotalLength = getTotalLength; + /*\ + * Snap.path.getPointAtLength + [ method ] + ** + * Returns the coordinates of the point located at the given length along the given path + ** + - path (string) SVG path string + - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps + ** + = (object) representation of the point: + o { + o x: (number) x coordinate, + o y: (number) y coordinate, + o alpha: (number) angle of derivative + o } + \*/ + Snap.path.getPointAtLength = getPointAtLength; + /*\ + * Snap.path.getSubpath + [ method ] + ** + * Returns the subpath of a given path between given start and end lengths + ** + - path (string) SVG path string + - from (number) length, in pixels, from the start of the path to the start of the segment + - to (number) length, in pixels, from the start of the path to the end of the segment + ** + = (string) path string definition for the segment + \*/ + Snap.path.getSubpath = function (path, from, to) { + if (this.getTotalLength(path) - to < 1e-6) { + return getSubpathsAtLength(path, from).end; + } + var a = getSubpathsAtLength(path, to, 1); + return from ? getSubpathsAtLength(a, from).end : a; + }; + /*\ + * Element.getTotalLength + [ method ] + ** + * Returns the length of the path in pixels (only works for `path` elements) + = (number) length + \*/ + elproto.getTotalLength = function () { + if (this.node.getTotalLength) { + return this.node.getTotalLength(); + } + }; + // SIERRA Element.getPointAtLength()/Element.getTotalLength(): If a is broken into different segments, is the jump distance to the new coordinates set by the _M_ or _m_ commands calculated as part of the path's total length? + /*\ + * Element.getPointAtLength + [ method ] + ** + * Returns coordinates of the point located at the given length on the given path (only works for `path` elements) + ** + - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps + ** + = (object) representation of the point: + o { + o x: (number) x coordinate, + o y: (number) y coordinate, + o alpha: (number) angle of derivative + o } + \*/ + elproto.getPointAtLength = function (length) { + return getPointAtLength(this.attr("d"), length); + }; + // SIERRA Element.getSubpath(): Similar to the problem for Element.getPointAtLength(). Unclear how this would work for a segmented path. Overall, the concept of _subpath_ and what I'm calling a _segment_ (series of non-_M_ or _Z_ commands) is unclear. + /*\ + * Element.getSubpath + [ method ] + ** + * Returns subpath of a given element from given start and end lengths (only works for `path` elements) + ** + - from (number) length, in pixels, from the start of the path to the start of the segment + - to (number) length, in pixels, from the start of the path to the end of the segment + ** + = (string) path string definition for the segment + \*/ + elproto.getSubpath = function (from, to) { + return Snap.path.getSubpath(this.attr("d"), from, to); + }; + Snap._.box = box; + /*\ + * Snap.path.findDotsAtSegment + [ method ] + ** + * Utility method + ** + * Finds dot coordinates on the given cubic beziér curve at the given t + - p1x (number) x of the first point of the curve + - p1y (number) y of the first point of the curve + - c1x (number) x of the first anchor of the curve + - c1y (number) y of the first anchor of the curve + - c2x (number) x of the second anchor of the curve + - c2y (number) y of the second anchor of the curve + - p2x (number) x of the second point of the curve + - p2y (number) y of the second point of the curve + - t (number) position on the curve (0..1) + = (object) point information in format: + o { + o x: (number) x coordinate of the point, + o y: (number) y coordinate of the point, + o m: { + o x: (number) x coordinate of the left anchor, + o y: (number) y coordinate of the left anchor + o }, + o n: { + o x: (number) x coordinate of the right anchor, + o y: (number) y coordinate of the right anchor + o }, + o start: { + o x: (number) x coordinate of the start of the curve, + o y: (number) y coordinate of the start of the curve + o }, + o end: { + o x: (number) x coordinate of the end of the curve, + o y: (number) y coordinate of the end of the curve + o }, + o alpha: (number) angle of the curve derivative at the point + o } + \*/ + Snap.path.findDotsAtSegment = findDotsAtSegment; + /*\ + * Snap.path.bezierBBox + [ method ] + ** + * Utility method + ** + * Returns the bounding box of a given cubic beziér curve + - p1x (number) x of the first point of the curve + - p1y (number) y of the first point of the curve + - c1x (number) x of the first anchor of the curve + - c1y (number) y of the first anchor of the curve + - c2x (number) x of the second anchor of the curve + - c2y (number) y of the second anchor of the curve + - p2x (number) x of the second point of the curve + - p2y (number) y of the second point of the curve + * or + - bez (array) array of six points for beziér curve + = (object) bounding box + o { + o x: (number) x coordinate of the left top point of the box, + o y: (number) y coordinate of the left top point of the box, + o x2: (number) x coordinate of the right bottom point of the box, + o y2: (number) y coordinate of the right bottom point of the box, + o width: (number) width of the box, + o height: (number) height of the box + o } + \*/ + Snap.path.bezierBBox = bezierBBox; + /*\ + * Snap.path.isPointInsideBBox + [ method ] + ** + * Utility method + ** + * Returns `true` if given point is inside bounding box + - bbox (string) bounding box + - x (string) x coordinate of the point + - y (string) y coordinate of the point + = (boolean) `true` if point is inside + \*/ + Snap.path.isPointInsideBBox = isPointInsideBBox; + /*\ + * Snap.path.isBBoxIntersect + [ method ] + ** + * Utility method + ** + * Returns `true` if two bounding boxes intersect + - bbox1 (string) first bounding box + - bbox2 (string) second bounding box + = (boolean) `true` if bounding boxes intersect + \*/ + Snap.path.isBBoxIntersect = isBBoxIntersect; + /*\ + * Snap.path.intersection + [ method ] + ** + * Utility method + ** + * Finds intersections of two paths + - path1 (string) path string + - path2 (string) path string + = (array) dots of intersection + o [ + o { + o x: (number) x coordinate of the point, + o y: (number) y coordinate of the point, + o t1: (number) t value for segment of path1, + o t2: (number) t value for segment of path2, + o segment1: (number) order number for segment of path1, + o segment2: (number) order number for segment of path2, + o bez1: (array) eight coordinates representing beziér curve for the segment of path1, + o bez2: (array) eight coordinates representing beziér curve for the segment of path2 + o } + o ] + \*/ + Snap.path.intersection = pathIntersection; + Snap.path.intersectionNumber = pathIntersectionNumber; + /*\ + * Snap.path.isPointInside + [ method ] + ** + * Utility method + ** + * Returns `true` if given point is inside a given closed path. + * + * Note: fill mode doesn’t affect the result of this method. + - path (string) path string + - x (number) x of the point + - y (number) y of the point + = (boolean) `true` if point is inside the path + \*/ + Snap.path.isPointInside = isPointInsidePath; + /*\ + * Snap.path.getBBox + [ method ] + ** + * Utility method + ** + * Returns the bounding box of a given path + - path (string) path string + = (object) bounding box + o { + o x: (number) x coordinate of the left top point of the box, + o y: (number) y coordinate of the left top point of the box, + o x2: (number) x coordinate of the right bottom point of the box, + o y2: (number) y coordinate of the right bottom point of the box, + o width: (number) width of the box, + o height: (number) height of the box + o } + \*/ + Snap.path.getBBox = pathBBox; + Snap.path.get = getPath; + /*\ + * Snap.path.toRelative + [ method ] + ** + * Utility method + ** + * Converts path coordinates into relative values + - path (string) path string + = (array) path string + \*/ + Snap.path.toRelative = pathToRelative; + /*\ + * Snap.path.toAbsolute + [ method ] + ** + * Utility method + ** + * Converts path coordinates into absolute values + - path (string) path string + = (array) path string + \*/ + Snap.path.toAbsolute = pathToAbsolute; + /*\ + * Snap.path.toCubic + [ method ] + ** + * Utility method + ** + * Converts path to a new path where all segments are cubic beziér curves + - pathString (string|array) path string or array of segments + = (array) array of segments + \*/ + Snap.path.toCubic = path2curve; + /*\ + * Snap.path.map + [ method ] + ** + * Transform the path string with the given matrix + - path (string) path string + - matrix (object) see @Matrix + = (string) transformed path string + \*/ + Snap.path.map = mapPath; + Snap.path.toString = toString; + Snap.path.clone = pathClone; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var mmax = Math.max, + mmin = Math.min; + + // Set + var Set = function (items) { + this.items = []; + this.length = 0; + this.type = "set"; + if (items) { + for (var i = 0, ii = items.length; i < ii; i++) { + if (items[i]) { + this[this.items.length] = this.items[this.items.length] = items[i]; + this.length++; + } + } + } + }, + setproto = Set.prototype; + /*\ + * Set.push + [ method ] + ** + * Adds each argument to the current set + = (object) original element + \*/ + setproto.push = function () { + var item, + len; + for (var i = 0, ii = arguments.length; i < ii; i++) { + item = arguments[i]; + if (item) { + len = this.items.length; + this[len] = this.items[len] = item; + this.length++; + } + } + return this; + }; + /*\ + * Set.pop + [ method ] + ** + * Removes last element and returns it + = (object) element + \*/ + setproto.pop = function () { + this.length && delete this[this.length--]; + return this.items.pop(); + }; + /*\ + * Set.forEach + [ method ] + ** + * Executes given function for each element in the set + * + * If the function returns `false`, the loop stops running. + ** + - callback (function) function to run + - thisArg (object) context object for the callback + = (object) Set object + \*/ + setproto.forEach = function (callback, thisArg) { + for (var i = 0, ii = this.items.length; i < ii; i++) { + if (callback.call(thisArg, this.items[i], i) === false) { + return this; + } + } + return this; + }; + setproto.remove = function () { + while (this.length) { + this.pop().remove(); + } + return this; + }; + setproto.attr = function (value) { + for (var i = 0, ii = this.items.length; i < ii; i++) { + this.items[i].attr(value); + } + return this; + }; + /*\ + * Set.clear + [ method ] + ** + * Removes all elements from the set + \*/ + setproto.clear = function () { + while (this.length) { + this.pop(); + } + }; + /*\ + * Set.splice + [ method ] + ** + * Removes range of elements from the set + ** + - index (number) position of the deletion + - count (number) number of element to remove + - insertion… (object) #optional elements to insert + = (object) set elements that were deleted + \*/ + setproto.splice = function (index, count, insertion) { + index = index < 0 ? mmax(this.length + index, 0) : index; + count = mmax(0, mmin(this.length - index, count)); + var tail = [], + todel = [], + args = [], + i; + for (i = 2; i < arguments.length; i++) { + args.push(arguments[i]); + } + for (i = 0; i < count; i++) { + todel.push(this[index + i]); + } + for (; i < this.length - index; i++) { + tail.push(this[index + i]); + } + var arglen = args.length; + for (i = 0; i < arglen + tail.length; i++) { + this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen]; + } + i = this.items.length = this.length -= count - arglen; + while (this[i]) { + delete this[i++]; + } + return new Set(todel); + }; + /*\ + * Set.exclude + [ method ] + ** + * Removes given element from the set + ** + - element (object) element to remove + = (boolean) `true` if object was found and removed from the set + \*/ + setproto.exclude = function (el) { + for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) { + this.splice(i, 1); + return true; + } + return false; + }; + setproto.insertAfter = function (el) { + var i = this.items.length; + while (i--) { + this.items[i].insertAfter(el); + } + return this; + }; + setproto.getBBox = function () { + var x = [], + y = [], + x2 = [], + y2 = []; + for (var i = this.items.length; i--;) if (!this.items[i].removed) { + var box = this.items[i].getBBox(); + x.push(box.x); + y.push(box.y); + x2.push(box.x + box.width); + y2.push(box.y + box.height); + } + x = mmin.apply(0, x); + y = mmin.apply(0, y); + x2 = mmax.apply(0, x2); + y2 = mmax.apply(0, y2); + return { + x: x, + y: y, + x2: x2, + y2: y2, + width: x2 - x, + height: y2 - y, + cx: x + (x2 - x) / 2, + cy: y + (y2 - y) / 2 + }; + }; + setproto.clone = function (s) { + s = new Set; + for (var i = 0, ii = this.items.length; i < ii; i++) { + s.push(this.items[i].clone()); + } + return s; + }; + setproto.toString = function () { + return "Snap\u2018s set"; + }; + setproto.type = "set"; + // export + Snap.set = function () { + var set = new Set; + if (arguments.length) { + set.push.apply(set, Array.prototype.slice.call(arguments, 0)); + } + return set; + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var names = {}, + reUnit = /[a-z]+$/i, + Str = String; + names.stroke = names.fill = "colour"; + function getEmpty(item) { + var l = item[0]; + switch (l.toLowerCase()) { + case "t": return [l, 0, 0]; + case "m": return [l, 1, 0, 0, 1, 0, 0]; + case "r": if (item.length == 4) { + return [l, 0, item[2], item[3]]; + } else { + return [l, 0]; + } + case "s": if (item.length == 5) { + return [l, 1, 1, item[3], item[4]]; + } else if (item.length == 3) { + return [l, 1, 1]; + } else { + return [l, 1]; + } + } + } + function equaliseTransform(t1, t2, getBBox) { + t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); + t1 = Snap.parseTransformString(t1) || []; + t2 = Snap.parseTransformString(t2) || []; + var maxlength = Math.max(t1.length, t2.length), + from = [], + to = [], + i = 0, j, jj, + tt1, tt2; + for (; i < maxlength; i++) { + tt1 = t1[i] || getEmpty(t2[i]); + tt2 = t2[i] || getEmpty(tt1); + if ((tt1[0] != tt2[0]) || + (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || + (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) + ) { + t1 = Snap._.transform2matrix(t1, getBBox()); + t2 = Snap._.transform2matrix(t2, getBBox()); + from = [["m", t1.a, t1.b, t1.c, t1.d, t1.e, t1.f]]; + to = [["m", t2.a, t2.b, t2.c, t2.d, t2.e, t2.f]]; + break; + } + from[i] = []; + to[i] = []; + for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) { + j in tt1 && (from[i][j] = tt1[j]); + j in tt2 && (to[i][j] = tt2[j]); + } + } + return { + from: path2array(from), + to: path2array(to), + f: getPath(from) + }; + } + function getNumber(val) { + return val; + } + function getUnit(unit) { + return function (val) { + return +val.toFixed(3) + unit; + }; + } + function getColour(clr) { + return Snap.rgb(clr[0], clr[1], clr[2]); + } + function getPath(path) { + var k = 0, i, ii, j, jj, out, a, b = []; + for (i = 0, ii = path.length; i < ii; i++) { + out = "["; + a = ['"' + path[i][0] + '"']; + for (j = 1, jj = path[i].length; j < jj; j++) { + a[j] = "val[" + (k++) + "]"; + } + out += a + "]"; + b[i] = out; + } + return Function("val", "return Snap.path.toString.call([" + b + "])"); + } + function path2array(path) { + var out = []; + for (var i = 0, ii = path.length; i < ii; i++) { + for (var j = 1, jj = path[i].length; j < jj; j++) { + out.push(path[i][j]); + } + } + return out; + } + Element.prototype.equal = function (name, b) { + var A, B, a = Str(this.attr(name) || ""), + el = this; + if (a == +a && b == +b) { + return { + from: +a, + to: +b, + f: getNumber + }; + } + if (names[name] == "colour") { + A = Snap.color(a); + B = Snap.color(b); + return { + from: [A.r, A.g, A.b, A.opacity], + to: [B.r, B.g, B.b, B.opacity], + f: getColour + }; + } + if (name == "transform" || name == "gradientTransform" || name == "patternTransform") { + if (b instanceof Snap.Matrix) { + b = b.toTransformString(); + } + if (!Snap._.rgTransform.test(b)) { + b = Snap._.svgTransform2string(b); + } + return equaliseTransform(a, b, function () { + return el.getBBox(1); + }); + } + if (name == "d" || name == "path") { + A = Snap.path.toCubic(a, b); + return { + from: path2array(A[0]), + to: path2array(A[1]), + f: getPath(A[0]) + }; + } + if (name == "points") { + A = Str(a).split(","); + B = Str(b).split(","); + return { + from: A, + to: B, + f: function (val) { return val; } + }; + } + var aUnit = a.match(reUnit), + bUnit = Str(b).match(reUnit); + if (aUnit && aUnit == bUnit) { + return { + from: parseFloat(a), + to: parseFloat(b), + f: getUnit(aUnit) + }; + } else { + return { + from: this.asPX(name), + to: this.asPX(name, b), + f: getNumber + }; + } + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + has = "hasOwnProperty", + supportsTouch = "createTouch" in glob.doc, + events = [ + "click", "dblclick", "mousedown", "mousemove", "mouseout", + "mouseover", "mouseup", "touchstart", "touchmove", "touchend", + "touchcancel" + ], + touchMap = { + mousedown: "touchstart", + mousemove: "touchmove", + mouseup: "touchend" + }, + getScroll = function (xy) { + var name = xy == "y" ? "scrollTop" : "scrollLeft"; + return glob.doc.documentElement[name] || glob.doc.body[name]; + }, + preventDefault = function () { + this.returnValue = false; + }, + preventTouch = function () { + return this.originalEvent.preventDefault(); + }, + stopPropagation = function () { + this.cancelBubble = true; + }, + stopTouch = function () { + return this.originalEvent.stopPropagation(); + }, + addEvent = (function () { + if (glob.doc.addEventListener) { + return function (obj, type, fn, element) { + var realName = supportsTouch && touchMap[type] ? touchMap[type] : type, + f = function (e) { + var scrollY = getScroll("y"), + scrollX = getScroll("x"); + if (supportsTouch && touchMap[has](type)) { + for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { + if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) { + var olde = e; + e = e.targetTouches[i]; + e.originalEvent = olde; + e.preventDefault = preventTouch; + e.stopPropagation = stopTouch; + break; + } + } + } + var x = e.clientX + scrollX, + y = e.clientY + scrollY; + return fn.call(element, e, x, y); + }; + + if (type !== realName) { + obj.addEventListener(type, f, false); + } + + obj.addEventListener(realName, f, false); + + return function () { + if (type !== realName) { + obj.removeEventListener(type, f, false); + } + + obj.removeEventListener(realName, f, false); + return true; + }; + }; + } else if (glob.doc.attachEvent) { + return function (obj, type, fn, element) { + var f = function (e) { + e = e || glob.win.event; + var scrollY = getScroll("y"), + scrollX = getScroll("x"), + x = e.clientX + scrollX, + y = e.clientY + scrollY; + e.preventDefault = e.preventDefault || preventDefault; + e.stopPropagation = e.stopPropagation || stopPropagation; + return fn.call(element, e, x, y); + }; + obj.attachEvent("on" + type, f); + var detacher = function () { + obj.detachEvent("on" + type, f); + return true; + }; + return detacher; + }; + } + })(), + drag = [], + dragMove = function (e) { + var x = e.clientX, + y = e.clientY, + scrollY = getScroll("y"), + scrollX = getScroll("x"), + dragi, + j = drag.length; + while (j--) { + dragi = drag[j]; + if (supportsTouch) { + var i = e.touches && e.touches.length, + touch; + while (i--) { + touch = e.touches[i]; + if (touch.identifier == dragi.el._drag.id || dragi.el.node.contains(touch.target)) { + x = touch.clientX; + y = touch.clientY; + (e.originalEvent ? e.originalEvent : e).preventDefault(); + break; + } + } + } else { + e.preventDefault(); + } + var node = dragi.el.node, + o, + glob = Snap._.glob, + next = node.nextSibling, + parent = node.parentNode, + display = node.style.display; + // glob.win.opera && parent.removeChild(node); + // node.style.display = "none"; + // o = dragi.el.paper.getElementByPoint(x, y); + // node.style.display = display; + // glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); + // o && eve("snap.drag.over." + dragi.el.id, dragi.el, o); + x += scrollX; + y += scrollY; + eve("snap.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); + } + }, + dragUp = function (e) { + Snap.unmousemove(dragMove).unmouseup(dragUp); + var i = drag.length, + dragi; + while (i--) { + dragi = drag[i]; + dragi.el._drag = {}; + eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); + } + drag = []; + }; + /*\ + * Element.click + [ method ] + ** + * Adds a click event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unclick + [ method ] + ** + * Removes a click event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.dblclick + [ method ] + ** + * Adds a double click event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.undblclick + [ method ] + ** + * Removes a double click event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mousedown + [ method ] + ** + * Adds a mousedown event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmousedown + [ method ] + ** + * Removes a mousedown event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mousemove + [ method ] + ** + * Adds a mousemove event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmousemove + [ method ] + ** + * Removes a mousemove event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseout + [ method ] + ** + * Adds a mouseout event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseout + [ method ] + ** + * Removes a mouseout event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseover + [ method ] + ** + * Adds a mouseover event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseover + [ method ] + ** + * Removes a mouseover event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseup + [ method ] + ** + * Adds a mouseup event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseup + [ method ] + ** + * Removes a mouseup event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchstart + [ method ] + ** + * Adds a touchstart event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchstart + [ method ] + ** + * Removes a touchstart event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchmove + [ method ] + ** + * Adds a touchmove event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchmove + [ method ] + ** + * Removes a touchmove event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchend + [ method ] + ** + * Adds a touchend event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchend + [ method ] + ** + * Removes a touchend event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchcancel + [ method ] + ** + * Adds a touchcancel event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchcancel + [ method ] + ** + * Removes a touchcancel event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + for (var i = events.length; i--;) { + (function (eventName) { + Snap[eventName] = elproto[eventName] = function (fn, scope) { + if (Snap.is(fn, "function")) { + this.events = this.events || []; + this.events.push({ + name: eventName, + f: fn, + unbind: addEvent(this.shape || this.node || glob.doc, eventName, fn, scope || this) + }); + } + return this; + }; + Snap["un" + eventName] = + elproto["un" + eventName] = function (fn) { + var events = this.events || [], + l = events.length; + while (l--) if (events[l].name == eventName && + (events[l].f == fn || !fn)) { + events[l].unbind(); + events.splice(l, 1); + !events.length && delete this.events; + return this; + } + return this; + }; + })(events[i]); + } + /*\ + * Element.hover + [ method ] + ** + * Adds hover event handlers to the element + - f_in (function) handler for hover in + - f_out (function) handler for hover out + - icontext (object) #optional context for hover in handler + - ocontext (object) #optional context for hover out handler + = (object) @Element + \*/ + elproto.hover = function (f_in, f_out, scope_in, scope_out) { + return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); + }; + /*\ + * Element.unhover + [ method ] + ** + * Removes hover event handlers from the element + - f_in (function) handler for hover in + - f_out (function) handler for hover out + = (object) @Element + \*/ + elproto.unhover = function (f_in, f_out) { + return this.unmouseover(f_in).unmouseout(f_out); + }; + var draggable = []; + // SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture. + // SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from? + // SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason. + // SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start. on start, drag.end. on end and drag.move. on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID? + /*\ + * Element.drag + [ method ] + ** + * Adds event handlers for an element's drag gesture + ** + - onmove (function) handler for moving + - onstart (function) handler for drag start + - onend (function) handler for drag end + - mcontext (object) #optional context for moving handler + - scontext (object) #optional context for drag start handler + - econtext (object) #optional context for drag end handler + * Additionaly following `drag` events are triggered: `drag.start.` on start, + * `drag.end.` on end and `drag.move.` on every move. When element is dragged over another element + * `drag.over.` fires as well. + * + * Start event and start handler are called in specified context or in context of the element with following parameters: + o x (number) x position of the mouse + o y (number) y position of the mouse + o event (object) DOM event object + * Move event and move handler are called in specified context or in context of the element with following parameters: + o dx (number) shift by x from the start point + o dy (number) shift by y from the start point + o x (number) x position of the mouse + o y (number) y position of the mouse + o event (object) DOM event object + * End event and end handler are called in specified context or in context of the element with following parameters: + o event (object) DOM event object + = (object) @Element + \*/ + elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { + if (!arguments.length) { + var origTransform; + return this.drag(function (dx, dy) { + this.attr({ + transform: origTransform + (origTransform ? "T" : "t") + [dx, dy] + }); + }, function () { + origTransform = this.transform().local; + }); + } + function start(e, x, y) { + (e.originalEvent || e).preventDefault(); + this._drag.x = x; + this._drag.y = y; + this._drag.id = e.identifier; + !drag.length && Snap.mousemove(dragMove).mouseup(dragUp); + drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); + onstart && eve.on("snap.drag.start." + this.id, onstart); + onmove && eve.on("snap.drag.move." + this.id, onmove); + onend && eve.on("snap.drag.end." + this.id, onend); + eve("snap.drag.start." + this.id, start_scope || move_scope || this, x, y, e); + } + this._drag = {}; + draggable.push({el: this, start: start}); + this.mousedown(start); + return this; + }; + /* + * Element.onDragOver + [ method ] + ** + * Shortcut to assign event handler for `drag.over.` event, where `id` is the element's `id` (see @Element.id) + - f (function) handler for event, first argument would be the element you are dragging over + \*/ + // elproto.onDragOver = function (f) { + // f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id); + // }; + /*\ + * Element.undrag + [ method ] + ** + * Removes all drag event handlers from the given element + \*/ + elproto.undrag = function () { + var i = draggable.length; + while (i--) if (draggable[i].el == this) { + this.unmousedown(draggable[i].start); + draggable.splice(i, 1); + eve.unbind("snap.drag.*." + this.id); + } + !draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp); + return this; + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + pproto = Paper.prototype, + rgurl = /^\s*url\((.+)\)/, + Str = String, + $ = Snap._.$; + Snap.filter = {}; +// SIERRA Paper.filter(): I don't understand the note. Does that mean an HTML should dedicate a separate SVG region for a filter definition? What's the advantage over a DEFS? + /*\ + * Paper.filter + [ method ] + ** + * Creates a `` element + ** + - filstr (string) SVG fragment of filter provided as a string + = (object) @Element + * Note: It is recommended to use filters embedded into the page inside an empty SVG element. + > Usage + | var f = paper.filter(''), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + pproto.filter = function (filstr) { + var paper = this; + if (paper.type != "svg") { + paper = paper.paper; + } + var f = Snap.parse(Str(filstr)), + id = Snap._.id(), + width = paper.node.offsetWidth, + height = paper.node.offsetHeight, + filter = $("filter"); + $(filter, { + id: id, + filterUnits: "userSpaceOnUse" + }); + filter.appendChild(f.node); + paper.defs.appendChild(filter); + return new Element(filter); + }; + + eve.on("snap.util.getattr.filter", function () { + eve.stop(); + var p = $(this.node, "filter"); + if (p) { + var match = Str(p).match(rgurl); + return match && Snap.select(match[1]); + } + }); + eve.on("snap.util.attr.filter", function (value) { + if (value instanceof Element && value.type == "filter") { + eve.stop(); + var id = value.node.id; + if (!id) { + $(value.node, {id: value.id}); + id = value.id; + } + $(this.node, { + filter: Snap.url(id) + }); + } + if (!value || value == "none") { + eve.stop(); + this.node.removeAttribute("filter"); + } + }); + /*\ + * Snap.filter.blur + [ method ] + ** + * Returns an SVG markup string for the blur filter + ** + - x (number) amount of horizontal blur, in pixels + - y (number) #optional amount of vertical blur, in pixels + = (string) filter representation + > Usage + | var f = paper.filter(Snap.filter.blur(5, 10)), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + Snap.filter.blur = function (x, y) { + if (x == null) { + x = 2; + } + var def = y == null ? x : [x, y]; + return Snap.format('\', { + def: def + }); + }; + Snap.filter.blur.toString = function () { + return this(); + }; + /*\ + * Snap.filter.shadow + [ method ] + ** + * Returns an SVG markup string for the shadow filter + ** + - dx (number) horizontal shift of the shadow, in pixels + - dy (number) vertical shift of the shadow, in pixels + - blur (number) #optional amount of blur + - color (string) #optional color of the shadow + = (string) filter representation + > Usage + | var f = paper.filter(Snap.filter.shadow(0, 2, 3)), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + Snap.filter.shadow = function (dx, dy, blur, color) { + color = color || "#000"; + if (blur == null) { + blur = 4; + } + if (typeof blur == "string") { + color = blur; + blur = 4; + } + if (dx == null) { + dx = 0; + dy = 2; + } + if (dy == null) { + dy = dx; + } + color = Snap.color(color); + return Snap.format('', { + color: color, + dx: dx, + dy: dy, + blur: blur + }); + }; + Snap.filter.shadow.toString = function () { + return this(); + }; + /*\ + * Snap.filter.grayscale + [ method ] + ** + * Returns an SVG markup string for the grayscale filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.grayscale = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + a: 0.2126 + 0.7874 * (1 - amount), + b: 0.7152 - 0.7152 * (1 - amount), + c: 0.0722 - 0.0722 * (1 - amount), + d: 0.2126 - 0.2126 * (1 - amount), + e: 0.7152 + 0.2848 * (1 - amount), + f: 0.0722 - 0.0722 * (1 - amount), + g: 0.2126 - 0.2126 * (1 - amount), + h: 0.0722 + 0.9278 * (1 - amount) + }); + }; + Snap.filter.grayscale.toString = function () { + return this(); + }; + /*\ + * Snap.filter.sepia + [ method ] + ** + * Returns an SVG markup string for the sepia filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.sepia = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + a: 0.393 + 0.607 * (1 - amount), + b: 0.769 - 0.769 * (1 - amount), + c: 0.189 - 0.189 * (1 - amount), + d: 0.349 - 0.349 * (1 - amount), + e: 0.686 + 0.314 * (1 - amount), + f: 0.168 - 0.168 * (1 - amount), + g: 0.272 - 0.272 * (1 - amount), + h: 0.534 - 0.534 * (1 - amount), + i: 0.131 + 0.869 * (1 - amount) + }); + }; + Snap.filter.sepia.toString = function () { + return this(); + }; + /*\ + * Snap.filter.saturate + [ method ] + ** + * Returns an SVG markup string for the saturate filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.saturate = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: 1 - amount + }); + }; + Snap.filter.saturate.toString = function () { + return this(); + }; + /*\ + * Snap.filter.hueRotate + [ method ] + ** + * Returns an SVG markup string for the hue-rotate filter + ** + - angle (number) angle of rotation + = (string) filter representation + \*/ + Snap.filter.hueRotate = function (angle) { + angle = angle || 0; + return Snap.format('', { + angle: angle + }); + }; + Snap.filter.hueRotate.toString = function () { + return this(); + }; + /*\ + * Snap.filter.invert + [ method ] + ** + * Returns an SVG markup string for the invert filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.invert = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount, + amount2: 1 - amount + }); + }; + Snap.filter.invert.toString = function () { + return this(); + }; + /*\ + * Snap.filter.brightness + [ method ] + ** + * Returns an SVG markup string for the brightness filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.brightness = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount + }); + }; + Snap.filter.brightness.toString = function () { + return this(); + }; + /*\ + * Snap.filter.contrast + [ method ] + ** + * Returns an SVG markup string for the contrast filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.contrast = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount, + amount2: .5 - amount / 2 + }); + }; + Snap.filter.contrast.toString = function () { + return this(); + }; +}); +return Snap; +})); +define('app/heart',['require'],function (require) { + + var Heart = function (s, x, y) { + var instance = this, + heart, + heartMatrix, + totalMaskVertices = 50; + + this.el = s.select("#heart"); + heart = this.el.select('#heart-shape'); + + instance.maskElement = s.path(getPath(totalMaskVertices)); + instance.el.attr({ + clipPath: instance.maskElement + }); + + this.animFill = function (f, dur) { + heart.animate({ + fill: f + }, 200); + } + + this.setFill = function (f, dur) { + heart.attr({ + fill: f + }, 200); + } + + this.animScale = function (scale, dur) { + dur = dur ? dur : 300; + + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.matrix.scale(scale); + this.el.animate({ + transform: this.matrix.toTransformString() + }, dur, mina.bounce); + } + + this.setScale = function (scale, dur) { + dur = dur ? dur : 300; + + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.matrix.scale(scale, scale, 0, 0); + this.el.attr({ + transform: this.matrix.toTransformString() + }); + + } + + this.mask = function () { + var n = totalMaskVertices; + + instance.maskElement.attr({ + d: getPath(n) + }); + + function updatePath() { + n -= 1; + instance.maskElement.attr({ + d: getPath(n) + }); + + if (n > 0) { + setTimeout(updatePath, 10); + } + } + + setTimeout(updatePath, 10); + } + + this.unmask = function () { + instance.maskElement.attr({ + d: getPath(totalMaskVertices) + }); + } + + function getPath(n) { + var pathString, + i, + _x, + _y; + + pathString = "M0 0"; + + for (i = 0; i < n + 1; i += 1) { + a = 2 * Math.PI * i / totalMaskVertices; + a += Math.PI; + + _x = Math.sin(a) * 50; + _y = Math.cos(a) * 50; + + pathString += "L" + _x + " " + _y; + } + + pathString += "Z"; + return pathString; + } + } + + return Heart; +}); +define('app/device',['require'],function (require) { + + var Device = function (s, x, y) { + var instance = this; + + this.el = s.g(); + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.el.transform(this.matrix.toTransformString()); + + this.keyboardMatrix = new Snap.Matrix(); + this.keyboardMatrix.translate(0, 70); + + instance.maskElement = s.polygon(); + instance.maskElement.toDefs(); + + addBack(); + addScreen(); + //addGloss(); + addKeyboard(); + + function addBack() { + instance.back = s.rect(0, 0, 0, 0); + instance.back.attr({ + fill: '#696969' + }); + + instance.el.append(instance.back); + } + + function addScreen() { + instance.scr = s.rect(0, 0, 0, 0); + instance.scr.attr({ + fill: "#09ae8a" + }); + + instance.el.append(instance.scr); + } + + function addKeyboard() { + var p1, + p2; + + instance.keyboard = s.g(); + instance.keyboard.transform(instance.keyboardMatrix.toTransformString()); + + p1 = s.polygon('-103.324,0 -135.324,32 136.676,32 104.676,0 '); + p1.attr({ + fill: '#818181' + }); + + p2 = s.polygon('127.774,40 -128.226,40 -136.226,32 135.774,32 '); + p2.attr({ + fill: '#676767' + }); + + instance.keyboard.append(p1); + instance.keyboard.append(p2); + instance.el.append(instance.keyboard); + } + + function addGloss() { + instance.gl = s.rect(0, 0, 0, 0); + instance.gl.attr({ + opacity: 0.2, + fill: "white", + clipPath: instance.maskElement + }); + + instance.el.append(instance.gl); + } + + this.hideKeyboard = function () { + this.keyboardMatrix = new Snap.Matrix(); + instance.keyboardMatrix.translate(0, 50); + instance.keyboardMatrix.scale(0.01, 0.01, 0, 0); + instance.keyboard.animate({ + opacity: 0, + transform: instance.keyboardMatrix.toTransformString() + }, 100); + } + + this.showKeyboard = function () { + this.keyboardMatrix = new Snap.Matrix(); + instance.keyboardMatrix.translate(0, 70); + instance.keyboardMatrix.scale(1, 1, 0, 0); + instance.keyboard.attr({ + opacity: 1, + transform: instance.keyboardMatrix.toTransformString() + }); + } + + this.setScreen = function(w, h) { + this.scr.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + } + + this.setBack = function (w, h) { + this.back.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + + /* + this.gl.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + + var pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + */ + } + + this.setScale = function (scale) { + this.matrix.scale(scale, scale, 0, 0); + this.el.transform(this.matrix.toTransformString()); + } + + this.animScreen = function(w, h) { + this.scr.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + } + + this.animBack = function (w, h) { + this.back.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + + /* + this.gl.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + + var pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + */ + } + + this.animRotation = function (r) { + instance.matrix.rotate(r, 0, 0); + + instance.el.animate({ + transform: instance.matrix.toTransformString() + }, 100, mina.easeIn); + + /* + if (r == 90) { + var w = 100, + h = 56, + pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + + instance.maskElement.animate({ + transform: 'rotate(-90)' + }, 100, mina.easeIn); + } else { + instance.maskElement.animate({ + transform: 'rotate(0)' + }, 100, mina.easeIn); + } + */ + } + + this.animScale = function (scale, dur, ease) { + dur = dur ? dur : 100; + ease = ease ? ease : mina.easeout; + + this.matrix.scale(scale, scale, 0, 0); + this.el.animate({ + transform: this.matrix.toTransformString() + }, dur, ease); + } + + this.animOpacity = function (opacity, dur) { + dur = dur ? dur : 200; + + this.el.animate({ + opacity: opacity + }, dur); + } + + this.setOpacity = function (opacity) { + this.el.attr({ + opacity: opacity + }); + } + } + + return Device; +}); +define('app/burst',['require'],function (require) { + + var Burst = function (s, x, y) { + var instance = this, + polygons, + mask, + maskCircle, + maskBg; + + this.el = s.select("#burst"); + + mask = s.g(); + mask.toDefs(); + + maskBg = s.rect(-200, -200, 400, 400); + maskBg.attr({ + fill: 'white' + }); + mask.append(maskBg); + + maskCircle = s.circle(0, 0, 30); + mask.append(maskCircle); + + this.el.attr({ + mask: mask + }); + + this.anim = function () { + this.el.animate({ + opacity: 1 + }, 100); + + maskCircle.animate({ + transform: 'scale(6)' + }, 300); + + setTimeout(function () { + instance.el.animate({ + opacity: 0 + }, 100); + }, 300) + } + + this.reset = function () { + maskCircle.attr({ + transform: 'scale(1)' + }); + } + } + + return Burst; +}); +//============================================================ +// +// Copyright (C) 2013 Matthew Wagerfield +// +// Twitter: https://twitter.com/mwagerfield +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY +// OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//============================================================ + +/** + * Defines the Flat Surface Shader namespace for all the awesomeness to exist upon. + * @author Matthew Wagerfield + */ +FSS = { + FRONT : 0, + BACK : 1, + DOUBLE : 2, + SVGNS : 'http://www.w3.org/2000/svg' +}; + +/** + * @class Array + * @author Matthew Wagerfield + */ +FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; + +/** + * @class Utils + * @author Matthew Wagerfield + */ +FSS.Utils = { + isNumber: function(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + } +}; + +/** + * Request Animation Frame Polyfill. + * @author Paul Irish + * @see https://gist.github.com/paulirish/1579671 + */ +(function() { + + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currentTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currentTime - lastTime)); + var id = window.setTimeout(function() { + callback(currentTime + timeToCall); + }, timeToCall); + lastTime = currentTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + +}()); + +/** + * @object Math Augmentation + * @author Matthew Wagerfield + */ +Math.PIM2 = Math.PI*2; +Math.PID2 = Math.PI/2; +Math.randomInRange = function(min, max) { + return min + (max - min) * Math.random(); +}; +Math.clamp = function(value, min, max) { + value = Math.max(value, min); + value = Math.min(value, max); + return value; +}; + +/** + * @object Vector3 + * @author Matthew Wagerfield + */ +FSS.Vector3 = { + create: function(x, y, z) { + var vector = new FSS.Array(3); + this.set(vector, x, y, z); + return vector; + }, + clone: function(a) { + var vector = this.create(); + this.copy(vector, a); + return vector; + }, + set: function(target, x, y, z) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + copy: function(target, a) { + target[0] = a[0]; + target[1] = a[1]; + target[2] = a[2]; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + return this; + }, + addVectors: function(target, a, b) { + target[0] = a[0] + b[0]; + target[1] = a[1] + b[1]; + target[2] = a[2] + b[2]; + return this; + }, + addScalar: function(target, s) { + target[0] += s; + target[1] += s; + target[2] += s; + return this; + }, + subtract: function(target, a) { + target[0] -= a[0]; + target[1] -= a[1]; + target[2] -= a[2]; + return this; + }, + subtractVectors: function(target, a, b) { + target[0] = a[0] - b[0]; + target[1] = a[1] - b[1]; + target[2] = a[2] - b[2]; + return this; + }, + subtractScalar: function(target, s) { + target[0] -= s; + target[1] -= s; + target[2] -= s; + return this; + }, + multiply: function(target, a) { + target[0] *= a[0]; + target[1] *= a[1]; + target[2] *= a[2]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + return this; + }, + divide: function(target, a) { + target[0] /= a[0]; + target[1] /= a[1]; + target[2] /= a[2]; + return this; + }, + divideVectors: function(target, a, b) { + target[0] = a[0] / b[0]; + target[1] = a[1] / b[1]; + target[2] = a[2] / b[2]; + return this; + }, + divideScalar: function(target, s) { + if (s !== 0) { + target[0] /= s; + target[1] /= s; + target[2] /= s; + } else { + target[0] = 0; + target[1] = 0; + target[2] = 0; + } + return this; + }, + cross: function(target, a) { + var x = target[0]; + var y = target[1]; + var z = target[2]; + target[0] = y*a[2] - z*a[1]; + target[1] = z*a[0] - x*a[2]; + target[2] = x*a[1] - y*a[0]; + return this; + }, + crossVectors: function(target, a, b) { + target[0] = a[1]*b[2] - a[2]*b[1]; + target[1] = a[2]*b[0] - a[0]*b[2]; + target[2] = a[0]*b[1] - a[1]*b[0]; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + }, + limit: function(target, min, max) { + var length = this.length(target); + if (min !== null && length < min) { + this.setLength(target, min); + } else if (max !== null && length > max) { + this.setLength(target, max); + } + return this; + }, + dot: function(a, b) { + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; + }, + normalise: function(target) { + return this.divideScalar(target, this.length(target)); + }, + negate: function(target) { + return this.multiplyScalar(target, -1); + }, + distanceSquared: function(a, b) { + var dx = a[0] - b[0]; + var dy = a[1] - b[1]; + var dz = a[2] - b[2]; + return dx*dx + dy*dy + dz*dz; + }, + distance: function(a, b) { + return Math.sqrt(this.distanceSquared(a, b)); + }, + lengthSquared: function(a) { + return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; + }, + length: function(a) { + return Math.sqrt(this.lengthSquared(a)); + }, + setLength: function(target, l) { + var length = this.length(target); + if (length !== 0 && l !== length) { + this.multiplyScalar(target, l / length); + } + return this; + } +}; + +/** + * @object Vector4 + * @author Matthew Wagerfield + */ +FSS.Vector4 = { + create: function(x, y, z, w) { + var vector = new FSS.Array(4); + this.set(vector, x, y, z); + return vector; + }, + set: function(target, x, y, z, w) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + target[3] = w || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + setW: function(target, w) { + target[3] = w || 0; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + target[3] += a[3]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + target[3] = a[3] * b[3]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + target[3] *= s; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + if (target[3] < value) { target[3] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + if (target[3] > value) { target[3] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + } +}; + +/** + * @class Color + * @author Matthew Wagerfield + */ +FSS.Color = function(hex, opacity) { + this.rgba = FSS.Vector4.create(); + this.hex = hex || '#000000'; + this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; + this.set(this.hex, this.opacity); +}; + +FSS.Color.prototype = { + set: function(hex, opacity) { + hex = hex.replace('#', ''); + var size = hex.length / 3; + this.rgba[0] = parseInt(hex.substring(size*0, size*1), 16) / 255; + this.rgba[1] = parseInt(hex.substring(size*1, size*2), 16) / 255; + this.rgba[2] = parseInt(hex.substring(size*2, size*3), 16) / 255; + this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; + return this; + }, + hexify: function(channel) { + var hex = Math.ceil(channel*255).toString(16); + if (hex.length === 1) { hex = '0' + hex; } + return hex; + }, + format: function() { + var r = this.hexify(this.rgba[0]); + var g = this.hexify(this.rgba[1]); + var b = this.hexify(this.rgba[2]); + this.hex = '#' + r + g + b; + return this.hex; + } +}; + +/** + * @class Object + * @author Matthew Wagerfield + */ +FSS.Object = function() { + this.position = FSS.Vector3.create(); +}; + +FSS.Object.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; + +/** + * @class Light + * @author Matthew Wagerfield + */ +FSS.Light = function(ambient, diffuse) { + FSS.Object.call(this); + this.ambient = new FSS.Color(ambient || '#FFFFFF'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.ray = FSS.Vector3.create(); +}; + +FSS.Light.prototype = Object.create(FSS.Object.prototype); + +/** + * @class Vertex + * @author Matthew Wagerfield + */ +FSS.Vertex = function(x, y, z) { + this.position = FSS.Vector3.create(x, y, z); +}; + +FSS.Vertex.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; + +/** + * @class Triangle + * @author Matthew Wagerfield + */ +FSS.Triangle = function(a, b, c, s, material) { + this.a = a || new FSS.Vertex(); + this.b = b || new FSS.Vertex(); + this.c = c || new FSS.Vertex(); + this.vertices = [this.a, this.b, this.c]; + this.u = FSS.Vector3.create(); + this.v = FSS.Vector3.create(); + this.centroid = FSS.Vector3.create(); + this.normal = FSS.Vector3.create(); + this.material = material || new FSS.Material(); + this.color = new FSS.Color(); + this.polygon = s.polygon(); + this.polygon.attr({ + 'stroke-linejoin': 'round', + 'stroke-miterlimit': 1, + 'stroke-width': 1 + }); + + this.computeCentroid(); + this.computeNormal(); +}; + +FSS.Triangle.prototype = { + computeCentroid: function() { + this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; + this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; + this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; + FSS.Vector3.divideScalar(this.centroid, 3); + return this; + }, + computeNormal: function() { + FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); + FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); + FSS.Vector3.crossVectors(this.normal, this.u, this.v); + FSS.Vector3.normalise(this.normal); + return this; + } +}; + +/** + * @class Geometry + * @author Matthew Wagerfield + */ +FSS.Geometry = function() { + this.vertices = []; + this.triangles = []; + this.dirty = false; +}; + +FSS.Geometry.prototype = { + update: function() { + if (this.dirty) { + var t,triangle; + for (t = this.triangles.length - 1; t >= 0; t--) { + triangle = this.triangles[t]; + triangle.computeCentroid(); + triangle.computeNormal(); + } + this.dirty = false; + } + return this; + } +}; + +/** + * @class Plane + * @author Matthew Wagerfield + */ +FSS.Plane = function(width, height, segments, slices, s, material) { + FSS.Geometry.call(this); + this.width = width || 100; + this.height = height || 100; + this.segments = segments || 4; + this.slices = slices || 4; + this.segmentWidth = this.width / this.segments; + this.sliceHeight = this.height / this.slices; + + // Cache Variables + var x, y, v0, v1, v2, v3, + vertex, triangle, vertices = [], + offsetX = this.width * -0.5, + offsetY = this.height * 0.5; + + // Add Vertices + for (x = 0; x <= this.segments; x++) { + vertices.push([]); + for (y = 0; y <= this.slices; y++) { + vertex = new FSS.Vertex(offsetX + x*this.segmentWidth, offsetY - y*this.sliceHeight); + vertices[x].push(vertex); + this.vertices.push(vertex); + } + } + + // Add Triangles + for (x = 0; x < this.segments; x++) { + for (y = 0; y < this.slices; y++) { + v0 = vertices[x+0][y+0]; + v1 = vertices[x+0][y+1]; + v2 = vertices[x+1][y+0]; + v3 = vertices[x+1][y+1]; + t0 = new FSS.Triangle(v0, v1, v2, s, material); + t1 = new FSS.Triangle(v2, v1, v3, s, material); + this.triangles.push(t0, t1); + } + } +}; + +FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); + +/** + * @class Material + * @author Matthew Wagerfield + */ +FSS.Material = function(ambient, diffuse) { + this.ambient = new FSS.Color(ambient || '#444444'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.slave = new FSS.Color(); +}; + +/** + * @class Mesh + * @author Matthew Wagerfield + */ +FSS.Mesh = function(geometry, material) { + FSS.Object.call(this); + this.geometry = geometry || new FSS.Geometry(); + this.material = material || new FSS.Material(); + this.side = FSS.FRONT; + this.visible = true; +}; + +FSS.Mesh.prototype = Object.create(FSS.Object.prototype); + +FSS.Mesh.prototype.update = function(lights, calculate) { + var t,triangle, l,light, illuminance; + + // Update Geometry + this.geometry.update(); + + // Calculate the triangle colors + if (calculate) { + + // Iterate through Triangles + for (t = this.geometry.triangles.length - 1; t >= 0; t--) { + triangle = this.geometry.triangles[t]; + + // Reset Triangle Color + FSS.Vector4.set(triangle.color.rgba); + + // Iterate through Lights + for (l = lights.length - 1; l >= 0; l--) { + light = lights[l]; + + // Calculate Illuminance + FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); + FSS.Vector3.normalise(light.ray); + illuminance = FSS.Vector3.dot(triangle.normal, light.ray); + if (this.side === FSS.FRONT) { + illuminance = Math.max(illuminance, 0); + } else if (this.side === FSS.BACK) { + illuminance = Math.abs(Math.min(illuminance, 0)); + } else if (this.side === FSS.DOUBLE) { + illuminance = Math.max(Math.abs(illuminance), 0); + } + + // Calculate Ambient Light + FSS.Vector4.multiplyVectors(triangle.material.slave.rgba, triangle.material.ambient.rgba, light.ambient.rgba); + FSS.Vector4.add(triangle.color.rgba, triangle.material.slave.rgba); + + // Calculate Diffuse Light + FSS.Vector4.multiplyVectors(triangle.material.slave.rgba, triangle.material.diffuse.rgba, light.diffuse.rgba); + FSS.Vector4.multiplyScalar(triangle.material.slave.rgba, illuminance); + FSS.Vector4.add(triangle.color.rgba, triangle.material.slave.rgba); + } + + // Clamp & Format Color + FSS.Vector4.clamp(triangle.color.rgba, 0, 1); + } + } + return this; +}; + +/** + * @class Scene + * @author Matthew Wagerfield + */ +FSS.Scene = function() { + this.meshes = []; + this.lights = []; +}; + +FSS.Scene.prototype = { + add: function(object) { + if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { + this.meshes.push(object); + } else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { + this.lights.push(object); + } + return this; + }, + remove: function(object) { + if (object instanceof FSS.Mesh && ~this.meshes.indexOf(object)) { + this.meshes.splice(this.meshes.indexOf(object), 1); + } else if (object instanceof FSS.Light && ~this.lights.indexOf(object)) { + this.lights.splice(this.lights.indexOf(object), 1); + } + return this; + } +}; + +/** + * @class Renderer + * @author Matthew Wagerfield + */ +FSS.Renderer = function() { + this.width = 0; + this.height = 0; + this.halfWidth = 0; + this.halfHeight = 0; +}; + +FSS.Renderer.prototype = { + setSize: function(width, height) { + if (this.width === width && this.height === height) return; + this.width = width; + this.height = height; + this.halfWidth = this.width * 0.5; + this.halfHeight = this.height * 0.5; + return this; + }, + clear: function() { + return this; + }, + render: function(scene) { + return this; + } +}; + +/** + * @class SVG Renderer + * @author Matthew Wagerfield + */ +FSS.SVGRenderer = function(s) { + FSS.Renderer.call(this); + this.element = s.g(); +}; + +FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); + +FSS.SVGRenderer.prototype.setSize = function(width, height) { + FSS.Renderer.prototype.setSize.call(this, width, height); + return this; +}; + +FSS.SVGRenderer.prototype.clear = function() { + FSS.Renderer.prototype.clear.call(this); + for (var i = this.element.childNodes.length - 1; i >= 0; i--) { + this.element.removeChild(this.element.childNodes[i]); + } + return this; +}; + +FSS.SVGRenderer.prototype.render = function(scene) { + FSS.Renderer.prototype.render.call(this, scene); + var m,mesh, t,triangle, points, style; + + // Update Meshes + for (m = scene.meshes.length - 1; m >= 0; m--) { + mesh = scene.meshes[m]; + if (mesh.visible) { + mesh.update(scene.lights, true); + + // Render Triangles + for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { + triangle = mesh.geometry.triangles[t]; + + if (triangle.polygon.parent() !== this.element) { + this.element.append(triangle.polygon); + } + + points = this.formatPoint(triangle.a)+' '; + points += this.formatPoint(triangle.b)+' '; + points += this.formatPoint(triangle.c); + style = this.formatStyle(triangle.color.format()); + + triangle.polygon.attr({ + points: points, + style: style + }); + } + } + } + return this; +}; + +FSS.SVGRenderer.prototype.formatPoint = function(vertex) { + return (this.halfWidth+vertex.position[0])+','+(this.halfHeight-vertex.position[1]); +}; + +FSS.SVGRenderer.prototype.formatStyle = function(color) { + var style = 'fill:'+color+';'; + style += 'stroke:'+color+';'; + return style; +}; + +define("vendor/fss", function(){}); + +define('app/mesh',['require','vendor/fss'],function (require) { + + require('vendor/fss'); + + var Mesh = function (s, container, colorA, colorB) { + var instance = this, + _width = 440, + _height = 440, + now, + start = Date.now(), + renderer, + scene, + geometry, + material, + mesh, + light; + + instance.animating = false; + + instance.init = function () { + renderer = new FSS.SVGRenderer(s); + renderer.setSize(_width, _height); + renderer.element.transform('translate(-20, -20)'); //keep edges from showing + + instance.el = renderer.element; + + container.append(renderer.element); + + scene = new FSS.Scene(); + material = new FSS.Material(colorA, colorB); + geometry = new FSS.Plane(_width, _height, 10, 10, s, material); + mesh = new FSS.Mesh(geometry, material); + scene.add(mesh); + + light = new FSS.Light('#eeeeee', '#eeeeee'); + light.setPosition(300*Math.sin(0.001), 200*Math.cos(0.0005), 100); + scene.add(light); + + now = Date.now() - start; + + tweakMesh(); + distortMesh(); + renderer.render(scene); + } + + instance.start = function () { + instance.animating = true; + animate(); + } + + instance.stop = function () { + instance.animating = false; + } + + instance.setColor = function (colorA, colorB) { + var i; + + material = new FSS.Material(colorA, colorB); + + for (i = geometry.triangles.length - 1; i > -1; i -= 1) { + geometry.triangles[i].material = material; + } + + animate(); + } + + instance.rippleColor = function (colorA, colorB) { + var i; + + material = new FSS.Material(colorA, colorB); + + function colorTriangle(j) { + geometry.triangles[j].material = material; + + if (j == 0) { + setTimeout(function () { + animate(); + }, 10); //force clear + } + } + + for (i = geometry.triangles.length - 1; i > -1; i -= 1) { + var speed = 200 + Math.sin(0.1 + Math.abs(geometry.triangles[i].centroid[0] / geometry.triangles[i].centroid[1])) * 100; + setTimeout(colorTriangle, speed * 2, i); + } + } + + + function tweakMesh() { + var v, vertex; + + for (v = geometry.vertices.length - 1; v >= 0; v--) { + vertex = geometry.vertices[v]; + vertex.anchor = FSS.Vector3.clone(vertex.position); + vertex.step = FSS.Vector3.create( + Math.randomInRange(0.2, 1.0), + Math.randomInRange(0.2, 1.0), + Math.randomInRange(0.2, 1.0) + ); + vertex.time = Math.randomInRange(0, Math.PIM2); + } + } + + function distortMesh() { + var v, + vertex, + ox, oy, oz, + offset = 10 / 2; + + for (v = geometry.vertices.length - 1; v >= 0; v--) { + vertex = geometry.vertices[v]; + ox = Math.sin(vertex.time + vertex.step[0] * now * 0.002); + oy = Math.cos(vertex.time + vertex.step[1] * now * 0.002); + oz = Math.sin(vertex.time + vertex.step[2] * now * 0.002); + FSS.Vector3.set(vertex.position, + 0.2 * geometry.segmentWidth * ox, + 0.1 * geometry.sliceHeight * oy, + 0.7 * offset * oz - offset); + FSS.Vector3.add(vertex.position, vertex.anchor); + } + + geometry.dirty = true; + } + + function animate() { + now = Date.now() - start; + + if (mobilecheck() !== true) { + distortMesh(); + } + + renderer.render(scene); + + if (instance.animating !== false) { + requestAnimationFrame(animate); + } + } + + instance.init(); + } + + return Mesh; +}); +define('app/logo',['require'],function (require) { + + var Logo = function (s) { + var instance = this, + logo, + parts = [ + ['top', 0, 20], + ['left', 20, 0], + ['bottom', 0, -20], + ['right', -20, 0] + ], + components = [], + i = 0, + k = 0, + isLogoAnimated = false, + isCrocAnimated = false, + isCroc2Animated = false; + + logo = s.select("#snap-logo"); + + for (i = 0; i < parts.length; i++) { + var el = parts[i] + elid = el[0]; + element = logo.select("#snap-logo-" + elid); + element.attr({opacity:0, transform: "t" + (el[1]) + "," + (el[2])}); + components.push(element); + } + + function animateEach() { + if (!components[k]) { + return; + } + components[k].animate({ + transform: "t" + (0) + "," + (0), + opacity: 1 + }, 250, mina.easeout); + setTimeout(animateEach, 150); + k++; + }; + + this.animate = function () { + setTimeout(animateEach, 150); + } + + this.show = function () { + var i; + + for (i = 0; i < components.length; i += 1) { + components[i].attr({ + transform: "t" + (0) + "," + (0), + opacity: 1 + }); + } + + } + } + + return Logo; +}); +define('app/app',['require','snap','app/heart','app/device','app/burst','app/mesh','app/logo'],function (require) { + + require('snap'); + + var App, + Heart = require('app/heart'), + Device = require('app/device'), + Burst = require('app/burst'); + Mesh = require('app/mesh'); + Logo = require('app/logo'); + + App = function () { + var instance = this; + + this.started = false; + + this.init = function () { + var timeline, + ad, + s, + cover, + device, + heart, + burst, + screen1, + text1, + text2, + text3, + text4, + resolveScreen, + logo, + meshA, + meshAContainer, + replaycount = 0, + WIDTH = 400, + HEIGHT = 400, + WHITE = "#ffffff", + GREEN = "#09ae8a"; + + this.started = true; + + ad = document.getElementById('ad'); + s = new Snap("#ad"); + cover = s.select('#cover'); + meshAContainer = s.select('#meshAContainer'); + screen1 = s.select('#screen1'); + text1 = s.select('#text1'); + text2 = s.select('#text2'); + text3 = s.select('#text3'); + text4 = s.select('#text4'); + replayBtn = s.select('#replay-btn'); + resolveScreen = s.select('#resolve'); + logo = new Logo(s); + + handle_RESIZE(); + window.addEventListener('resize', handle_RESIZE); + ad.addEventListener('click', handle_CLICK); + replayBtn.click(replay_CLICK); + + addMeshA(); + cover.remove(); + addHeart(); + addComputer(); + addBurst(); + + function addHeart() { + heart = new Heart(s, WIDTH / 2, HEIGHT / 2); + } + + function addComputer() { + device = new Device(s, WIDTH / 2, HEIGHT / 2); + device.setScreen(192, 112); + device.setBack(208, 148); + device.setScale(0.1); + text2.after(device.el); + } + + function addBurst() { + burst = new Burst(s); + } + + function addMeshA() { + meshA = new Mesh(s, meshAContainer, '#afafaf', '#afafaf'); + } + + function addMeshB() { + meshA.el.remove(); + meshB = new Mesh(s, meshBContainer, '#09ae8a', '#777777'); + } + + function showMeshB() { + if (mobilecheck() !== true) { + meshB.start(); + } + } + + function showComputer() { + text1.animate({y: 80}, 100); + text2.animate({y: 350}, 100); + device.animScale(10, 300, mina.bounce); + heart.animFill(WHITE); + } + + function toTablet() { + device.animRotation(-90); + device.animScreen(76, 100); + device.animBack(92, 132); + heart.animScale(0.5, 200); + } + + function toPhone() { + device.hideKeyboard(); + device.animScreen(48, 76); + device.animBack(56, 100); + heart.animScale(0.3); + } + + function rotate() { + device.animRotation(90); + heart.animScale(0.4); + } + + function zoom() { + meshA.stop(); + device.animScale(10, 600); + heart.animScale(4, 600); + burst.anim(); + } + + function greenMesh() { + + meshA.setColor('#09ae8a', '#777777'); + + if (mobilecheck() !== true) { + meshA.start(); + } + + device.animOpacity(0, 200); + screen1.animate({ + opacity: 0 + }, 100); + } + + function maskReveal() { + heart.mask(); + } + + function showText3() { + device.setScale(0.01); + text3.animate({ + opacity: 1 + }, 200); + } + + function hideText3() { + text3.animate({ + opacity: 0 + }, 200); + + if (mobilecheck() !== true) { + meshA.rippleColor('#afafaf', '#afafaf'); + } else { + meshA.setColor('#afafaf', '#afafaf'); + } + } + + function resolve() { + resolveScreen.animate({ + opacity: 1 + }, 200); + } + + function stop() { + logo.animate(); + meshA.stop(); + } + + function reset() { + resolveScreen.attr({ + opacity: 0 + }); + + screen1.attr({ + opacity: 1 + }); + + text1.attr({y: 130}); + text2.attr({y: 300}); + + heart.setScale(1); + heart.setFill('#0DAE8A'); + heart.unmask(); + device.setOpacity(1); + device.setScreen(192, 112); + device.setBack(208, 148); + device.showKeyboard(); + meshA.start(); + burst.reset(); + } + + function replay_CLICK(e) { + e.stopPropagation(); + + replaycount += 1; + ga('send', 'event', 'button', 'click', 'replay', replaycount); + reset(); + run(); + } + + function handle_CLICK(e) { + ga('send', 'event', 'button', 'click', 'ad'); + top.window.location.href = 'http://snapsvg.io/'; + } + + function handle_RESIZE() { + var _w = window.innerWidth, + scale = _w / 400; + + ad.style.webkitTransform = 'scale(' + scale + ')'; + ad.style.MozTransform = 'scale(' + scale + ')'; + ad.style.msTransform = 'scale(' + scale + ')'; + ad.style.oTransform = 'scale(' + scale + ')'; + ad.style.transform = 'scale(' + scale + ')'; + + } + + function run() { + if (mobilecheck() !== true) { + meshA.start(); + } + setTimeout(showComputer, 2000); + setTimeout(toPhone, 3000); + setTimeout(rotate, 4000); + setTimeout(toTablet, 5000); + setTimeout(zoom, 6000); + setTimeout(greenMesh, 6300); + setTimeout(maskReveal, 6700); + setTimeout(showText3, 7000); + setTimeout(hideText3, 10000); + setTimeout(resolve, 10500); + setTimeout(stop, 10900); + } + + function basic() { + screen1.attr({opacity: 0}); + heart.el.attr({opacity: 0}); + replayBtn.attr({opacity: 0}); + logo.show(); + text4.select('tspan').attr({opacity: 0}); + text4.select('tspan:nth-child(2)').attr({y: 120}); + resolveScreen.attr({opacity: 1}); + } + + if (window.replay !== true) { + replayBtn.attr({opacity: 0}); + } + + if (window.supported !== false) { + run(); + } else { + basic(); + } + + } + + } + + return App; +}); +/*global require*/ + +require.config({ + shim: { + + }, + paths: { + snap: 'vendor/snap.svg', + tweenlite: 'vendor/greensock/TweenLite', + timelinelite: 'vendor/greensock/TimelineLite' + } +}); + +/** +* check support +*/ +window.ua = navigator.userAgent.toLowerCase(); +window.isAndroid = ua.indexOf("android") > -1; +window.isSafari = ua.indexOf("safari") > -1 && ua.indexOf("chrome") === -1; + +function checkSVG() { + return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect; +} + +/** +* check mobile +*/ +window.mobilecheck = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +} + +function addFallback() { + var link, + img; + + link = document.createElement('a'); + link.href = "http://snapsvg.io/"; + document.body.appendChild(link); + + img = new Image(); + img.src = "backup.jpg"; + img.style.position = "absolute"; + img.style.top = "0"; + img.style.left = "0"; + img.width = "100%"; + link.appendChild(img); +} + +window.supported = true; +window.replay = true; + +require(['app/app'], function (App) { + + if (checkSVG() !== true) { + addFallback(); + return; + } + + if (isAndroid === true) { + window.supported = false; + } + + if (isSafari === true) { + window.replay = false; + } + + window.app = new App(); + //app.init(); //fired from font loading +}); + + + +/** +* font loading +*/ +WebFontConfig = { + google: { + families: ['Open+Sans:300:latin', 'Source+Sans+Pro::latin'] + }, + inactive: function () { + if (typeof(app) !== 'undefined' && app.started !== true) { + app.init(); + } + }, + fontactive: function(familyName, fvd) { + if (typeof(app) !== 'undefined' && familyName == 'Open Sans') { + if (app.started !== true) { + app.init(); + } + } + } +}; +define("main", function(){}); diff --git a/demos/snap-ad/site/js/vendor/modernizr.min.js b/demos/snap-ad/site/js/vendor/modernizr.min.js new file mode 100644 index 0000000..02fb48a --- /dev/null +++ b/demos/snap-ad/site/js/vendor/modernizr.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.7.1 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-shiv-cssclasses-load + */ +;window.Modernizr=function(a,b,c){function u(a){j.cssText=a}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function w(a,b){return typeof a===b}function x(a,b){return!!~(""+a).indexOf(b)}function y(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:w(f,"function")?f.bind(d||b):f}return!1}var d="2.7.1",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m={},n={},o={},p=[],q=p.slice,r,s={}.hasOwnProperty,t;!w(s,"undefined")&&!w(s.call,"undefined")?t=function(a,b){return s.call(a,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=q.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(q.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(q.call(arguments)))};return e});for(var z in m)t(m,z)&&(r=z.toLowerCase(),e[r]=m[z](),p.push((e[r]?"":"no-")+r));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)t(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},u(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+p.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value !== 'string') { + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + //Allow getting a global that expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof define !== 'undefined') { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof requirejs !== 'undefined') { + if (isFunction(requirejs)) { + //Do not overwrite and existing requirejs instance. + return; + } + cfg = requirejs; + requirejs = undefined; + } + + //Allow for a require config object + if (typeof require !== 'undefined' && !isFunction(require)) { + //assume it is a config object. + cfg = require; + require = undefined; + } + + function newContext(contextName) { + var inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId, + config = { + //Defaults. Do not set a default for map + //config to speed up normalize(), which + //will run faster if there is no default. + waitSeconds: 7, + baseUrl: './', + paths: {}, + pkgs: {}, + shim: {}, + config: {} + }, + registry = {}, + //registry of just enabled modules, to speed + //cycle breaking code when lots of modules + //are registered, but not activated. + enabledRegistry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + requireCounter = 1, + unnormalizedCounter = 1; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; ary[i]; i += 1) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, + foundMap, foundI, foundStarMap, starI, + baseParts = baseName && baseName.split('/'), + normalizedBaseParts = baseParts, + map = config.map, + starMap = map && map['*']; + + //Adjust any relative paths. + if (name && name.charAt(0) === '.') { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + if (getOwn(config.pkgs, baseName)) { + //If the baseName is a package name, then just treat it as one + //name to concat the name with. + normalizedBaseParts = baseParts = [baseName]; + } else { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + } + + name = normalizedBaseParts.concat(name.split('/')); + trimDots(name); + + //Some use of packages may use a . path to reference the + //'main' module name, so normalize for that. + pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); + name = name.join('/'); + if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { + name = pkgName; + } + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if (applyMap && map && (baseParts || starMap)) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = getOwn(mapValue, nameSegment); + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = getOwn(config.paths, id); + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + removeScript(id); + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.require.undef(id); + context.require([id]); + return true; + } + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via require.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a define() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var url, pluginModule, suffix, nameParts, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = ''; + + //If no name, then it means it is a require call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = getOwn(defined, prefix); + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + normalizedName = normalize(name, parentName, applyMap); + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + + url = context.nameToUrl(normalizedName); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + getModule(depMap).on(name, fn); + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = getOwn(registry, id); + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + //Array splice in the values since the context code has a + //local var ref to defQueue, so cannot just reassign the one + //on context. + apsp.apply(defQueue, + [defQueue.length - 1, 0].concat(globalDefQueue)); + globalDefQueue = []; + } + } + + handlers = { + 'require': function (mod) { + if (mod.require) { + return mod.require; + } else { + return (mod.require = context.makeRequire(mod.map)); + } + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + if (mod.exports) { + return mod.exports; + } else { + return (mod.exports = defined[mod.map.id] = {}); + } + } + }, + 'module': function (mod) { + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return (config.config && getOwn(config.config, mod.map.id)) || {}; + }, + exports: defined[mod.map.id] + }); + } + } + }; + + function cleanRegistry(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + delete enabledRegistry[id]; + } + + function breakCycle(mod, traced, processed) { + var id = mod.map.id; + + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } + } + }); + processed[id] = true; + } + } + + function checkLoaded() { + var map, modId, err, usingPathFallback, + waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + reqCalls = [], + stillLoading = false, + needCycleCheck = true; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(enabledRegistry, function (mod) { + map = mod.map; + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!map.isDefine) { + reqCalls.push(mod); + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); + }); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = getOwn(undefEvents, map.id) || {}; + this.map = map; + this.shim = getOwn(config.shim, map.id); + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function (depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple define calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function () { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks if the module is ready to define itself, and if so, + * define it. + */ + check: function () { + if (!this.enabled || this.enabling) { + return; + } + + var err, cjsModule, + id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory; + + if (!this.inited) { + this.fetch(); + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another require call + //that would result in checking this module to + //define itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. + if (this.events.error) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + if (this.map.isDefine) { + //If setting exports via 'module' is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + cjsModule = this.module; + if (cjsModule && + cjsModule.exports !== undefined && + //Make sure it is not already the exports value + cjsModule.exports !== this.exports) { + exports = cjsModule.exports; + } else if (exports === undefined && this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = [this.map.id]; + err.requireType = 'define'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + req.onResourceLoad(context, this.map, this.depMaps); + } + } + + //Clean up + cleanRegistry(id); + + this.defined = true; + } + + //Finished the define stage. Allow calling check again + //to allow define notifications below in the case of a + //cycle. + this.defining = false; + + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + + } + }, + + callPlugin: function () { + var map = this.map, + id = map.id, + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var load, normalizedMap, normalizedMod, + name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true + }); + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + //prefix and name should already be normalized, no need + //for applying map config again either. + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + + normalizedMod = getOwn(registry, normalizedMap.id); + if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + cleanRegistry(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = bind(this, function (text, textAlt) { + /*jslint evil: true */ + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } + + //Turn off interactive script matching for IE for any define + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } + + try { + req.exec(text); + } catch (e) { + return onError(makeError('fromtexteval', + 'fromText eval for ' + id + + ' failed: ' + e, + e, + [id])); + } + + if (hasInteractive) { + useInteractive = true; + } + + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + + //Support anonymous modules. + context.completeLoad(moduleName); + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, localRequire, load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + enabledRegistry[this.map.id] = this; + this.enabled = true; + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.skipMap); + this.depMaps[i] = depMap; + + handler = getOwn(handlers, depMap.id); + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', this.errback); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'require', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!hasProp(handlers, id) && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = getOwn(registry, pluginMap.id); + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function (name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the requirejs info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + } + + context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + defQueue: defQueue, + Module: Module, + makeModuleMap: makeModuleMap, + nextTick: req.nextTick, + onError: onError, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + //Save off the paths and packages since they require special processing, + //they are additive. + var pkgs = config.pkgs, + shim = config.shim, + objs = { + paths: true, + config: true, + map: true + }; + + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (prop === 'map') { + if (!config.map) { + config.map = {}; + } + mixin(config[prop], value, true, true); + } else { + mixin(config[prop], value, true); + } + } else { + config[prop] = value; + } + }); + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location; + + pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + location = pkgObj.location; + + //Create a brand new object on pkgs, since currentPackages can + //be passed in again, and config.pkgs is the internal transformed + //state for all package configs. + pkgs[pkgObj.name] = { + name: pkgObj.name, + location: location || pkgObj.name, + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + main: (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, '') + }; + }); + + //Done with modifications, assing packages back to context config + config.pkgs = pkgs; + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + //If module already has init called, since it is too + //late to modify them, and ignore unnormalized ones + //since they are transient. + if (!mod.inited && !mod.map.unnormalized) { + mod.map = makeModuleMap(id); + } + }); + + //If a deps array or a config callback is specified, then call + //require with those args. This is useful when require is defined as a + //config object before require.js is loaded. + if (cfg.deps || cfg.callback) { + context.require(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); + } + return fn; + }, + + makeRequire: function (relMap, options) { + options = options || {}; + + function localRequire(deps, callback, errback) { + var id, map, requireMod; + + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; + } + + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } + + //If require|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } + + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap, localRequire); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use require([])'))); + } + return defined[id]; + } + + //Grab defines waiting in the global queue. + intakeDefines(); + + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //require call, collect them. + intakeDefines(); + + requireMod = getModule(makeModuleMap(null, relMap)); + + //Store if map config should be applied to this require + //call for dependencies. + requireMod.skipMap = options.skipMap; + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + }); + + return localRequire; + } + + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var ext, + index = moduleNamePlusExt.lastIndexOf('.'), + segment = moduleNamePlusExt.split('/')[0], + isRelative = segment === '.' || segment === '..'; + + //Have a file extension alias, and it is not the + //dots from a relative path. + if (index !== -1 && (!isRelative || index > 1)) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext, true); + }, + + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + } + }); + + //Only allow undef on top level require calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting define() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + cleanRegistry(id); + } + }; + } + + return localRequire; + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. A second arg, parent, the parent module, + * is passed in for context, when this method is overriden by + * the optimizer. Not shown here to keep code compact. + */ + enable: function (depMap) { + var mod = getOwn(registry, depMap.id); + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var found, args, mod, + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching define call for this script! + found = true; + } + + callGetModule(args); + } + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = getOwn(registry, moduleName); + + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No define call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call define(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + * Note that it **does not** call normalize on the moduleName, + * it is assumed to have already been normalized. This is an + * internal API, not a public one. Use toUrl for the public API. + */ + nameToUrl: function (moduleName, ext, skipExt) { + var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, + parentPath; + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + pkgs = config.pkgs; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } else if (pkg) { + //If module name is just the package name, then looking + //for the main module. + if (moduleName === pkg.name) { + pkgPath = pkg.location + '/' + pkg.main; + } else { + pkgPath = pkg.location; + } + syms.splice(0, i, pkgPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/'); + url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs ? url + + ((url.indexOf('?') === -1 ? '?' : '&') + + config.urlArgs) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callack function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layer in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + return onError(makeError('scripterror', 'Script error', evt, [data.id])); + } + } + }; + + context.require = context.makeRequire(); + return context; + } + + /** + * Main entry point. + * + * If the only argument to require is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a require that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = requirejs = function (deps, callback, errback, optional) { + + //Find the right context, use default + var context, config, + contextName = defContextName; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = getOwn(contexts, contextName); + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.require(deps, callback, errback); + }; + + /** + * Support require.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global require. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.require[prop].apply(ctx, arguments); + }; + }); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that require explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = function (err) { + throw err; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the require context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/jrburke/requirejs/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/jrburke/requirejs/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous define call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the define call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEventListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous define + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + try { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation that a build has been done so that + //only one script needs to be loaded anyway. This may need to be + //reevaluated if other use cases become common. + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } catch (e) { + context.onError(makeError('importscripts', + 'importScripts failed for ' + + moduleName + ' at ' + url, + e, + [moduleName])); + } + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser) { + //Figure out baseUrl. Get it from the script tag with require.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + //Set final baseUrl if there is not already an explicit one. + if (!cfg.baseUrl) { + //Pull off the directory of data-main for use as the + //baseUrl. + src = dataMain.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + cfg.baseUrl = subPath; + dataMain = mainScript; + } + + //Strip off any trailing .js since dataMain is now + //like a module name. + dataMain = dataMain.replace(jsSuffixRegExp, ''); + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * require() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to define the module corresponding to the first argument's + * name. + */ + define = function (name, deps, callback) { + var node, context; + + //Allow for anonymous modules + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = []; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps.length && isFunction(callback)) { + //Remove comments from the callback string, + //look for require calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, '') + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without require calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs require. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous define() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + }; + + define.amd = { + jQuery: true + }; + + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this)); diff --git a/demos/snap-ad/site/js/vendor/require.min.js b/demos/snap-ad/site/js/vendor/require.min.js new file mode 100644 index 0000000..84d1d67 --- /dev/null +++ b/demos/snap-ad/site/js/vendor/require.min.js @@ -0,0 +1,36 @@ +/* + RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + Available via the MIT or new BSD license. + see: http://github.com/jrburke/requirejs for details +*/ +var requirejs,require,define; +(function(ca){function G(b){return"[object Function]"===N.call(b)}function H(b){return"[object Array]"===N.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&& +(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= +this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f); +if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval", +"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(K,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b, +a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(K,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m, +nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b, +a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild= +!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(K,f))return K[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}M();i.nextTick(function(){M();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!== +d&&(!("."===g||".."===g)||1g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): +(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,M=g,D?y.insertBefore(g,D):y.appendChild(g),M=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(L=b.getAttribute("data-main"))return q=L,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl= +Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=L),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=M))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b|| +(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this); diff --git a/demos/snap-ad/src/.sass-cache/7eb2e4ecfecd432261de95f74b5302c6e0956ac9/screen.scssc b/demos/snap-ad/src/.sass-cache/7eb2e4ecfecd432261de95f74b5302c6e0956ac9/screen.scssc new file mode 100644 index 0000000..7992a72 Binary files /dev/null and b/demos/snap-ad/src/.sass-cache/7eb2e4ecfecd432261de95f74b5302c6e0956ac9/screen.scssc differ diff --git a/demos/snap-ad/src/assets/computer.svg b/demos/snap-ad/src/assets/computer.svg new file mode 100644 index 0000000..05c5c57 --- /dev/null +++ b/demos/snap-ad/src/assets/computer.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/snap-ad/src/assets/fonts/OpenSans-Light.ttf b/demos/snap-ad/src/assets/fonts/OpenSans-Light.ttf new file mode 100755 index 0000000..0d38189 Binary files /dev/null and b/demos/snap-ad/src/assets/fonts/OpenSans-Light.ttf differ diff --git a/demos/snap-ad/src/assets/fonts/SourceSansPro-Semibold.ttf b/demos/snap-ad/src/assets/fonts/SourceSansPro-Semibold.ttf new file mode 100755 index 0000000..5020594 Binary files /dev/null and b/demos/snap-ad/src/assets/fonts/SourceSansPro-Semibold.ttf differ diff --git a/demos/snap-ad/src/assets/heart.svg b/demos/snap-ad/src/assets/heart.svg new file mode 100644 index 0000000..87fd258 --- /dev/null +++ b/demos/snap-ad/src/assets/heart.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/demos/snap-ad/src/assets/phone.svg b/demos/snap-ad/src/assets/phone.svg new file mode 100644 index 0000000..dbd26ca --- /dev/null +++ b/demos/snap-ad/src/assets/phone.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/demos/snap-ad/src/assets/tablet.svg b/demos/snap-ad/src/assets/tablet.svg new file mode 100644 index 0000000..1783bb0 --- /dev/null +++ b/demos/snap-ad/src/assets/tablet.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/demos/snap-ad/src/backup.jpg b/demos/snap-ad/src/backup.jpg new file mode 100644 index 0000000..6e05cec Binary files /dev/null and b/demos/snap-ad/src/backup.jpg differ diff --git a/demos/snap-ad/src/config.rb b/demos/snap-ad/src/config.rb new file mode 100644 index 0000000..541a2aa --- /dev/null +++ b/demos/snap-ad/src/config.rb @@ -0,0 +1,24 @@ +# Require any additional compass plugins here. + +# Set this to the root of your project when deployed: +http_path = "/" +css_dir = "css" +sass_dir = "sass" +images_dir = "assets/images" +javascripts_dir = "js" + +# You can select your preferred output style here (can be overridden via the command line): +# output_style = :expanded or :nested or :compact or :compressed + +# To enable relative paths to assets via compass helper functions. Uncomment: +# relative_assets = true + +# To disable debugging comments that display the original location of your selectors. Uncomment: +# line_comments = false + + +# If you prefer the indented syntax, you might want to regenerate this +# project again passing --syntax sass, or you can uncomment this: +# preferred_syntax = :sass +# and then run: +# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass diff --git a/demos/snap-ad/src/css/screen.css b/demos/snap-ad/src/css/screen.css new file mode 100644 index 0000000..2e03e58 --- /dev/null +++ b/demos/snap-ad/src/css/screen.css @@ -0,0 +1,93 @@ +/* line 3, ../sass/screen.scss */ +.base-font { + font-family: 'Open Sans', serif; +} + +/* line 7, ../sass/screen.scss */ +.snap-font { + font-family: 'Source Sans Pro', serif; +} + +/* line 11, ../sass/screen.scss */ +body { + margin: 0; + cursor: pointer; +} + +/* line 16, ../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, ../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, ../sass/screen.scss */ +#learn-btn { + cursor: pointer; +} +/* line 33, ../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, ../sass/screen.scss */ +#learn-btn:hover path:first-child { + fill: #076656; +} +/* line 46, ../sass/screen.scss */ +#learn-btn:hover path:nth-child(2) { + fill: #7cd1c2; +} +/* line 51, ../sass/screen.scss */ +#learn-btn:hover text { + fill: #0a9a87; +} +/* line 59, ../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, ../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, ../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/src/index.html b/demos/snap-ad/src/index.html new file mode 100755 index 0000000..77f7077 --- /dev/null +++ b/demos/snap-ad/src/index.html @@ -0,0 +1,187 @@ + + + + + + + + + Snap! SVG + + + + + + + + + + + + + + + + + + diff --git a/demos/snap-ad/src/js/app/app.js b/demos/snap-ad/src/js/app/app.js new file mode 100644 index 0000000..051c939 --- /dev/null +++ b/demos/snap-ad/src/js/app/app.js @@ -0,0 +1,269 @@ +define(function (require) { + + require('snap'); + + var App, + Heart = require('app/heart'), + Device = require('app/device'), + Burst = require('app/burst'); + Mesh = require('app/mesh'); + Logo = require('app/logo'); + + App = function () { + var instance = this; + + this.started = false; + + this.init = function () { + var timeline, + ad, + s, + cover, + device, + heart, + burst, + screen1, + text1, + text2, + text3, + text4, + resolveScreen, + logo, + meshA, + meshAContainer, + replaycount = 0, + WIDTH = 400, + HEIGHT = 400, + WHITE = "#ffffff", + GREEN = "#09ae8a"; + + this.started = true; + + ad = document.getElementById('ad'); + s = new Snap("#ad"); + cover = s.select('#cover'); + meshAContainer = s.select('#meshAContainer'); + screen1 = s.select('#screen1'); + text1 = s.select('#text1'); + text2 = s.select('#text2'); + text3 = s.select('#text3'); + text4 = s.select('#text4'); + replayBtn = s.select('#replay-btn'); + resolveScreen = s.select('#resolve'); + logo = new Logo(s); + + handle_RESIZE(); + window.addEventListener('resize', handle_RESIZE); + ad.addEventListener('click', handle_CLICK); + replayBtn.click(replay_CLICK); + + addMeshA(); + cover.remove(); + addHeart(); + addComputer(); + addBurst(); + + function addHeart() { + heart = new Heart(s, WIDTH / 2, HEIGHT / 2); + } + + function addComputer() { + device = new Device(s, WIDTH / 2, HEIGHT / 2); + device.setScreen(192, 112); + device.setBack(208, 148); + device.setScale(0.1); + text2.after(device.el); + } + + function addBurst() { + burst = new Burst(s); + } + + function addMeshA() { + meshA = new Mesh(s, meshAContainer, '#afafaf', '#afafaf'); + } + + function addMeshB() { + meshA.el.remove(); + meshB = new Mesh(s, meshBContainer, '#09ae8a', '#777777'); + } + + function showMeshB() { + if (mobilecheck() !== true) { + meshB.start(); + } + } + + function showComputer() { + text1.animate({y: 80}, 100); + text2.animate({y: 350}, 100); + device.animScale(10, 300, mina.bounce); + heart.animFill(WHITE); + } + + function toTablet() { + device.animRotation(-90); + device.animScreen(76, 100); + device.animBack(92, 132); + heart.animScale(0.5, 200); + } + + function toPhone() { + device.hideKeyboard(); + device.animScreen(48, 76); + device.animBack(56, 100); + heart.animScale(0.3); + } + + function rotate() { + device.animRotation(90); + heart.animScale(0.4); + } + + function zoom() { + meshA.stop(); + device.animScale(10, 600); + heart.animScale(4, 600); + burst.anim(); + } + + function greenMesh() { + + meshA.setColor('#09ae8a', '#777777'); + + if (mobilecheck() !== true) { + meshA.start(); + } + + device.animOpacity(0, 200); + screen1.animate({ + opacity: 0 + }, 100); + } + + function maskReveal() { + heart.mask(); + } + + function showText3() { + device.setScale(0.01); + text3.animate({ + opacity: 1 + }, 200); + } + + function hideText3() { + text3.animate({ + opacity: 0 + }, 200); + + if (mobilecheck() !== true) { + meshA.rippleColor('#afafaf', '#afafaf'); + } else { + meshA.setColor('#afafaf', '#afafaf'); + } + } + + function resolve() { + resolveScreen.animate({ + opacity: 1 + }, 200); + } + + function stop() { + logo.animate(); + meshA.stop(); + } + + function reset() { + resolveScreen.attr({ + opacity: 0 + }); + + screen1.attr({ + opacity: 1 + }); + + text1.attr({y: 130}); + text2.attr({y: 300}); + + heart.setScale(1); + heart.setFill('#0DAE8A'); + heart.unmask(); + device.setOpacity(1); + device.setScreen(192, 112); + device.setBack(208, 148); + device.showKeyboard(); + meshA.start(); + burst.reset(); + } + + function replay_CLICK(e) { + e.stopPropagation(); + + replaycount += 1; + ga('send', 'event', 'button', 'click', 'replay', replaycount); + reset(); + run(); + } + + function handle_CLICK(e) { + ga('send', 'event', 'button', 'click', 'ad'); + top.window.location.href = 'http://snapsvg.io/'; + } + + function handle_RESIZE() { + var _w = window.innerWidth, + scale = _w / 400; + + ad.style.webkitTransform = 'scale(' + scale + ')'; + ad.style.MozTransform = 'scale(' + scale + ')'; + ad.style.msTransform = 'scale(' + scale + ')'; + ad.style.oTransform = 'scale(' + scale + ')'; + ad.style.transform = 'scale(' + scale + ')'; + + } + + function run() { + if (mobilecheck() !== true) { + meshA.start(); + } + setTimeout(showComputer, 2000); + setTimeout(toPhone, 3000); + setTimeout(rotate, 4000); + setTimeout(toTablet, 5000); + setTimeout(zoom, 6000); + setTimeout(greenMesh, 6300); + setTimeout(maskReveal, 6700); + setTimeout(showText3, 7000); + setTimeout(hideText3, 10000); + setTimeout(resolve, 10500); + setTimeout(stop, 10900); + } + + function basic() { + screen1.attr({opacity: 0}); + heart.el.attr({opacity: 0}); + replayBtn.attr({opacity: 0}); + logo.show(); + text4.select('tspan').attr({opacity: 0}); + text4.select('tspan:nth-child(2)').attr({y: 120}); + resolveScreen.attr({opacity: 1}); + } + + if (window.replay !== true) { + replayBtn.attr({opacity: 0}); + } + + if (window.supported !== false) { + run(); + } else { + basic(); + } + + } + + } + + return App; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/app/burst.js b/demos/snap-ad/src/js/app/burst.js new file mode 100644 index 0000000..ae912cf --- /dev/null +++ b/demos/snap-ad/src/js/app/burst.js @@ -0,0 +1,52 @@ +define(function (require) { + + var Burst = function (s, x, y) { + var instance = this, + polygons, + mask, + maskCircle, + maskBg; + + this.el = s.select("#burst"); + + mask = s.g(); + mask.toDefs(); + + maskBg = s.rect(-200, -200, 400, 400); + maskBg.attr({ + fill: 'white' + }); + mask.append(maskBg); + + maskCircle = s.circle(0, 0, 30); + mask.append(maskCircle); + + this.el.attr({ + mask: mask + }); + + this.anim = function () { + this.el.animate({ + opacity: 1 + }, 100); + + maskCircle.animate({ + transform: 'scale(6)' + }, 300); + + setTimeout(function () { + instance.el.animate({ + opacity: 0 + }, 100); + }, 300) + } + + this.reset = function () { + maskCircle.attr({ + transform: 'scale(1)' + }); + } + } + + return Burst; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/app/device.js b/demos/snap-ad/src/js/app/device.js new file mode 100644 index 0000000..3d33883 --- /dev/null +++ b/demos/snap-ad/src/js/app/device.js @@ -0,0 +1,218 @@ +define(function (require) { + + var Device = function (s, x, y) { + var instance = this; + + this.el = s.g(); + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.el.transform(this.matrix.toTransformString()); + + this.keyboardMatrix = new Snap.Matrix(); + this.keyboardMatrix.translate(0, 70); + + instance.maskElement = s.polygon(); + instance.maskElement.toDefs(); + + addBack(); + addScreen(); + //addGloss(); + addKeyboard(); + + function addBack() { + instance.back = s.rect(0, 0, 0, 0); + instance.back.attr({ + fill: '#696969' + }); + + instance.el.append(instance.back); + } + + function addScreen() { + instance.scr = s.rect(0, 0, 0, 0); + instance.scr.attr({ + fill: "#09ae8a" + }); + + instance.el.append(instance.scr); + } + + function addKeyboard() { + var p1, + p2; + + instance.keyboard = s.g(); + instance.keyboard.transform(instance.keyboardMatrix.toTransformString()); + + p1 = s.polygon('-103.324,0 -135.324,32 136.676,32 104.676,0 '); + p1.attr({ + fill: '#818181' + }); + + p2 = s.polygon('127.774,40 -128.226,40 -136.226,32 135.774,32 '); + p2.attr({ + fill: '#676767' + }); + + instance.keyboard.append(p1); + instance.keyboard.append(p2); + instance.el.append(instance.keyboard); + } + + function addGloss() { + instance.gl = s.rect(0, 0, 0, 0); + instance.gl.attr({ + opacity: 0.2, + fill: "white", + clipPath: instance.maskElement + }); + + instance.el.append(instance.gl); + } + + this.hideKeyboard = function () { + this.keyboardMatrix = new Snap.Matrix(); + instance.keyboardMatrix.translate(0, 50); + instance.keyboardMatrix.scale(0.01, 0.01, 0, 0); + instance.keyboard.animate({ + opacity: 0, + transform: instance.keyboardMatrix.toTransformString() + }, 100); + } + + this.showKeyboard = function () { + this.keyboardMatrix = new Snap.Matrix(); + instance.keyboardMatrix.translate(0, 70); + instance.keyboardMatrix.scale(1, 1, 0, 0); + instance.keyboard.attr({ + opacity: 1, + transform: instance.keyboardMatrix.toTransformString() + }); + } + + this.setScreen = function(w, h) { + this.scr.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + } + + this.setBack = function (w, h) { + this.back.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + + /* + this.gl.attr({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }); + + var pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + */ + } + + this.setScale = function (scale) { + this.matrix.scale(scale, scale, 0, 0); + this.el.transform(this.matrix.toTransformString()); + } + + this.animScreen = function(w, h) { + this.scr.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + } + + this.animBack = function (w, h) { + this.back.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + + /* + this.gl.animate({ + x: -w / 2, + y: -h / 2, + width: w, + height: h + }, 100); + + var pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + */ + } + + this.animRotation = function (r) { + instance.matrix.rotate(r, 0, 0); + + instance.el.animate({ + transform: instance.matrix.toTransformString() + }, 100, mina.easeIn); + + /* + if (r == 90) { + var w = 100, + h = 56, + pointString = -w / 2 + ' ' + -h / 2 + ',' + w / 2 + ' ' + -h / 2 + ',' + -w / 2 + ' ' + h / 2; + + instance.maskElement.attr({ + points: pointString + }); + + instance.maskElement.animate({ + transform: 'rotate(-90)' + }, 100, mina.easeIn); + } else { + instance.maskElement.animate({ + transform: 'rotate(0)' + }, 100, mina.easeIn); + } + */ + } + + this.animScale = function (scale, dur, ease) { + dur = dur ? dur : 100; + ease = ease ? ease : mina.easeout; + + this.matrix.scale(scale, scale, 0, 0); + this.el.animate({ + transform: this.matrix.toTransformString() + }, dur, ease); + } + + this.animOpacity = function (opacity, dur) { + dur = dur ? dur : 200; + + this.el.animate({ + opacity: opacity + }, dur); + } + + this.setOpacity = function (opacity) { + this.el.attr({ + opacity: opacity + }); + } + } + + return Device; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/app/heart.js b/demos/snap-ad/src/js/app/heart.js new file mode 100644 index 0000000..a5f9f1e --- /dev/null +++ b/demos/snap-ad/src/js/app/heart.js @@ -0,0 +1,103 @@ +define(function (require) { + + var Heart = function (s, x, y) { + var instance = this, + heart, + heartMatrix, + totalMaskVertices = 50; + + this.el = s.select("#heart"); + heart = this.el.select('#heart-shape'); + + instance.maskElement = s.path(getPath(totalMaskVertices)); + instance.el.attr({ + clipPath: instance.maskElement + }); + + this.animFill = function (f, dur) { + heart.animate({ + fill: f + }, 200); + } + + this.setFill = function (f, dur) { + heart.attr({ + fill: f + }, 200); + } + + this.animScale = function (scale, dur) { + dur = dur ? dur : 300; + + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.matrix.scale(scale); + this.el.animate({ + transform: this.matrix.toTransformString() + }, dur, mina.bounce); + } + + this.setScale = function (scale, dur) { + dur = dur ? dur : 300; + + this.matrix = new Snap.Matrix(); + this.matrix.translate(x, y); + this.matrix.scale(scale, scale, 0, 0); + this.el.attr({ + transform: this.matrix.toTransformString() + }); + + } + + this.mask = function () { + var n = totalMaskVertices; + + instance.maskElement.attr({ + d: getPath(n) + }); + + function updatePath() { + n -= 1; + instance.maskElement.attr({ + d: getPath(n) + }); + + if (n > 0) { + setTimeout(updatePath, 10); + } + } + + setTimeout(updatePath, 10); + } + + this.unmask = function () { + instance.maskElement.attr({ + d: getPath(totalMaskVertices) + }); + } + + function getPath(n) { + var pathString, + i, + _x, + _y; + + pathString = "M0 0"; + + for (i = 0; i < n + 1; i += 1) { + a = 2 * Math.PI * i / totalMaskVertices; + a += Math.PI; + + _x = Math.sin(a) * 50; + _y = Math.cos(a) * 50; + + pathString += "L" + _x + " " + _y; + } + + pathString += "Z"; + return pathString; + } + } + + return Heart; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/app/logo.js b/demos/snap-ad/src/js/app/logo.js new file mode 100644 index 0000000..b592e83 --- /dev/null +++ b/demos/snap-ad/src/js/app/logo.js @@ -0,0 +1,59 @@ +define(function (require) { + + var Logo = function (s) { + var instance = this, + logo, + parts = [ + ['top', 0, 20], + ['left', 20, 0], + ['bottom', 0, -20], + ['right', -20, 0] + ], + components = [], + i = 0, + k = 0, + isLogoAnimated = false, + isCrocAnimated = false, + isCroc2Animated = false; + + logo = s.select("#snap-logo"); + + for (i = 0; i < parts.length; i++) { + var el = parts[i] + elid = el[0]; + element = logo.select("#snap-logo-" + elid); + element.attr({opacity:0, transform: "t" + (el[1]) + "," + (el[2])}); + components.push(element); + } + + function animateEach() { + if (!components[k]) { + return; + } + components[k].animate({ + transform: "t" + (0) + "," + (0), + opacity: 1 + }, 250, mina.easeout); + setTimeout(animateEach, 150); + k++; + }; + + this.animate = function () { + setTimeout(animateEach, 150); + } + + this.show = function () { + var i; + + for (i = 0; i < components.length; i += 1) { + components[i].attr({ + transform: "t" + (0) + "," + (0), + opacity: 1 + }); + } + + } + } + + return Logo; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/app/mesh.js b/demos/snap-ad/src/js/app/mesh.js new file mode 100644 index 0000000..f8b90af --- /dev/null +++ b/demos/snap-ad/src/js/app/mesh.js @@ -0,0 +1,143 @@ +define(function (require) { + + require('vendor/fss'); + + var Mesh = function (s, container, colorA, colorB) { + var instance = this, + _width = 440, + _height = 440, + now, + start = Date.now(), + renderer, + scene, + geometry, + material, + mesh, + light; + + instance.animating = false; + + instance.init = function () { + renderer = new FSS.SVGRenderer(s); + renderer.setSize(_width, _height); + renderer.element.transform('translate(-20, -20)'); //keep edges from showing + + instance.el = renderer.element; + + container.append(renderer.element); + + scene = new FSS.Scene(); + material = new FSS.Material(colorA, colorB); + geometry = new FSS.Plane(_width, _height, 10, 10, s, material); + mesh = new FSS.Mesh(geometry, material); + scene.add(mesh); + + light = new FSS.Light('#eeeeee', '#eeeeee'); + light.setPosition(300*Math.sin(0.001), 200*Math.cos(0.0005), 100); + scene.add(light); + + now = Date.now() - start; + + tweakMesh(); + distortMesh(); + renderer.render(scene); + } + + instance.start = function () { + instance.animating = true; + animate(); + } + + instance.stop = function () { + instance.animating = false; + } + + instance.setColor = function (colorA, colorB) { + var i; + + material = new FSS.Material(colorA, colorB); + + for (i = geometry.triangles.length - 1; i > -1; i -= 1) { + geometry.triangles[i].material = material; + } + + animate(); + } + + instance.rippleColor = function (colorA, colorB) { + var i; + + material = new FSS.Material(colorA, colorB); + + function colorTriangle(j) { + geometry.triangles[j].material = material; + + if (j == 0) { + setTimeout(function () { + animate(); + }, 10); //force clear + } + } + + for (i = geometry.triangles.length - 1; i > -1; i -= 1) { + var speed = 200 + Math.sin(0.1 + Math.abs(geometry.triangles[i].centroid[0] / geometry.triangles[i].centroid[1])) * 100; + setTimeout(colorTriangle, speed * 2, i); + } + } + + + function tweakMesh() { + var v, vertex; + + for (v = geometry.vertices.length - 1; v >= 0; v--) { + vertex = geometry.vertices[v]; + vertex.anchor = FSS.Vector3.clone(vertex.position); + vertex.step = FSS.Vector3.create( + Math.randomInRange(0.2, 1.0), + Math.randomInRange(0.2, 1.0), + Math.randomInRange(0.2, 1.0) + ); + vertex.time = Math.randomInRange(0, Math.PIM2); + } + } + + function distortMesh() { + var v, + vertex, + ox, oy, oz, + offset = 10 / 2; + + for (v = geometry.vertices.length - 1; v >= 0; v--) { + vertex = geometry.vertices[v]; + ox = Math.sin(vertex.time + vertex.step[0] * now * 0.002); + oy = Math.cos(vertex.time + vertex.step[1] * now * 0.002); + oz = Math.sin(vertex.time + vertex.step[2] * now * 0.002); + FSS.Vector3.set(vertex.position, + 0.2 * geometry.segmentWidth * ox, + 0.1 * geometry.sliceHeight * oy, + 0.7 * offset * oz - offset); + FSS.Vector3.add(vertex.position, vertex.anchor); + } + + geometry.dirty = true; + } + + function animate() { + now = Date.now() - start; + + if (mobilecheck() !== true) { + distortMesh(); + } + + renderer.render(scene); + + if (instance.animating !== false) { + requestAnimationFrame(animate); + } + } + + instance.init(); + } + + return Mesh; +}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/main.js b/demos/snap-ad/src/js/main.js new file mode 100644 index 0000000..434a475 --- /dev/null +++ b/demos/snap-ad/src/js/main.js @@ -0,0 +1,94 @@ +/*global require*/ + +require.config({ + shim: { + + }, + paths: { + snap: 'vendor/snap.svg', + tweenlite: 'vendor/greensock/TweenLite', + timelinelite: 'vendor/greensock/TimelineLite' + } +}); + +/** +* check support +*/ +window.ua = navigator.userAgent.toLowerCase(); +window.isAndroid = ua.indexOf("android") > -1; +window.isSafari = ua.indexOf("safari") > -1 && ua.indexOf("chrome") === -1; + +function checkSVG() { + return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect; +} + +/** +* check mobile +*/ +window.mobilecheck = function() { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); + return check; +} + +function addFallback() { + var link, + img; + + link = document.createElement('a'); + link.href = "http://snapsvg.io/"; + document.body.appendChild(link); + + img = new Image(); + img.src = "backup.jpg"; + img.style.position = "absolute"; + img.style.top = "0"; + img.style.left = "0"; + img.width = "100%"; + link.appendChild(img); +} + +window.supported = true; +window.replay = true; + +require(['app/app'], function (App) { + + if (checkSVG() !== true) { + addFallback(); + return; + } + + if (isAndroid === true) { + window.supported = false; + } + + if (isSafari === true) { + window.replay = false; + } + + window.app = new App(); + //app.init(); //fired from font loading +}); + + + +/** +* font loading +*/ +WebFontConfig = { + google: { + families: ['Open+Sans:300:latin', 'Source+Sans+Pro::latin'] + }, + inactive: function () { + if (typeof(app) !== 'undefined' && app.started !== true) { + app.init(); + } + }, + fontactive: function(familyName, fvd) { + if (typeof(app) !== 'undefined' && familyName == 'Open Sans') { + if (app.started !== true) { + app.init(); + } + } + } +} \ No newline at end of file diff --git a/demos/snap-ad/src/js/vendor/fss.js b/demos/snap-ad/src/js/vendor/fss.js new file mode 100755 index 0000000..cfe5bf6 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss.js @@ -0,0 +1,760 @@ +//============================================================ +// +// Copyright (C) 2013 Matthew Wagerfield +// +// Twitter: https://twitter.com/mwagerfield +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY +// OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//============================================================ + +/** + * Defines the Flat Surface Shader namespace for all the awesomeness to exist upon. + * @author Matthew Wagerfield + */ +FSS = { + FRONT : 0, + BACK : 1, + DOUBLE : 2, + SVGNS : 'http://www.w3.org/2000/svg' +}; + +/** + * @class Array + * @author Matthew Wagerfield + */ +FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; + +/** + * @class Utils + * @author Matthew Wagerfield + */ +FSS.Utils = { + isNumber: function(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + } +}; + +/** + * Request Animation Frame Polyfill. + * @author Paul Irish + * @see https://gist.github.com/paulirish/1579671 + */ +(function() { + + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currentTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currentTime - lastTime)); + var id = window.setTimeout(function() { + callback(currentTime + timeToCall); + }, timeToCall); + lastTime = currentTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + +}()); + +/** + * @object Math Augmentation + * @author Matthew Wagerfield + */ +Math.PIM2 = Math.PI*2; +Math.PID2 = Math.PI/2; +Math.randomInRange = function(min, max) { + return min + (max - min) * Math.random(); +}; +Math.clamp = function(value, min, max) { + value = Math.max(value, min); + value = Math.min(value, max); + return value; +}; + +/** + * @object Vector3 + * @author Matthew Wagerfield + */ +FSS.Vector3 = { + create: function(x, y, z) { + var vector = new FSS.Array(3); + this.set(vector, x, y, z); + return vector; + }, + clone: function(a) { + var vector = this.create(); + this.copy(vector, a); + return vector; + }, + set: function(target, x, y, z) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + copy: function(target, a) { + target[0] = a[0]; + target[1] = a[1]; + target[2] = a[2]; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + return this; + }, + addVectors: function(target, a, b) { + target[0] = a[0] + b[0]; + target[1] = a[1] + b[1]; + target[2] = a[2] + b[2]; + return this; + }, + addScalar: function(target, s) { + target[0] += s; + target[1] += s; + target[2] += s; + return this; + }, + subtract: function(target, a) { + target[0] -= a[0]; + target[1] -= a[1]; + target[2] -= a[2]; + return this; + }, + subtractVectors: function(target, a, b) { + target[0] = a[0] - b[0]; + target[1] = a[1] - b[1]; + target[2] = a[2] - b[2]; + return this; + }, + subtractScalar: function(target, s) { + target[0] -= s; + target[1] -= s; + target[2] -= s; + return this; + }, + multiply: function(target, a) { + target[0] *= a[0]; + target[1] *= a[1]; + target[2] *= a[2]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + return this; + }, + divide: function(target, a) { + target[0] /= a[0]; + target[1] /= a[1]; + target[2] /= a[2]; + return this; + }, + divideVectors: function(target, a, b) { + target[0] = a[0] / b[0]; + target[1] = a[1] / b[1]; + target[2] = a[2] / b[2]; + return this; + }, + divideScalar: function(target, s) { + if (s !== 0) { + target[0] /= s; + target[1] /= s; + target[2] /= s; + } else { + target[0] = 0; + target[1] = 0; + target[2] = 0; + } + return this; + }, + cross: function(target, a) { + var x = target[0]; + var y = target[1]; + var z = target[2]; + target[0] = y*a[2] - z*a[1]; + target[1] = z*a[0] - x*a[2]; + target[2] = x*a[1] - y*a[0]; + return this; + }, + crossVectors: function(target, a, b) { + target[0] = a[1]*b[2] - a[2]*b[1]; + target[1] = a[2]*b[0] - a[0]*b[2]; + target[2] = a[0]*b[1] - a[1]*b[0]; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + }, + limit: function(target, min, max) { + var length = this.length(target); + if (min !== null && length < min) { + this.setLength(target, min); + } else if (max !== null && length > max) { + this.setLength(target, max); + } + return this; + }, + dot: function(a, b) { + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; + }, + normalise: function(target) { + return this.divideScalar(target, this.length(target)); + }, + negate: function(target) { + return this.multiplyScalar(target, -1); + }, + distanceSquared: function(a, b) { + var dx = a[0] - b[0]; + var dy = a[1] - b[1]; + var dz = a[2] - b[2]; + return dx*dx + dy*dy + dz*dz; + }, + distance: function(a, b) { + return Math.sqrt(this.distanceSquared(a, b)); + }, + lengthSquared: function(a) { + return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; + }, + length: function(a) { + return Math.sqrt(this.lengthSquared(a)); + }, + setLength: function(target, l) { + var length = this.length(target); + if (length !== 0 && l !== length) { + this.multiplyScalar(target, l / length); + } + return this; + } +}; + +/** + * @object Vector4 + * @author Matthew Wagerfield + */ +FSS.Vector4 = { + create: function(x, y, z, w) { + var vector = new FSS.Array(4); + this.set(vector, x, y, z); + return vector; + }, + set: function(target, x, y, z, w) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + target[3] = w || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + setW: function(target, w) { + target[3] = w || 0; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + target[3] += a[3]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + target[3] = a[3] * b[3]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + target[3] *= s; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + if (target[3] < value) { target[3] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + if (target[3] > value) { target[3] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + } +}; + +/** + * @class Color + * @author Matthew Wagerfield + */ +FSS.Color = function(hex, opacity) { + this.rgba = FSS.Vector4.create(); + this.hex = hex || '#000000'; + this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; + this.set(this.hex, this.opacity); +}; + +FSS.Color.prototype = { + set: function(hex, opacity) { + hex = hex.replace('#', ''); + var size = hex.length / 3; + this.rgba[0] = parseInt(hex.substring(size*0, size*1), 16) / 255; + this.rgba[1] = parseInt(hex.substring(size*1, size*2), 16) / 255; + this.rgba[2] = parseInt(hex.substring(size*2, size*3), 16) / 255; + this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; + return this; + }, + hexify: function(channel) { + var hex = Math.ceil(channel*255).toString(16); + if (hex.length === 1) { hex = '0' + hex; } + return hex; + }, + format: function() { + var r = this.hexify(this.rgba[0]); + var g = this.hexify(this.rgba[1]); + var b = this.hexify(this.rgba[2]); + this.hex = '#' + r + g + b; + return this.hex; + } +}; + +/** + * @class Object + * @author Matthew Wagerfield + */ +FSS.Object = function() { + this.position = FSS.Vector3.create(); +}; + +FSS.Object.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; + +/** + * @class Light + * @author Matthew Wagerfield + */ +FSS.Light = function(ambient, diffuse) { + FSS.Object.call(this); + this.ambient = new FSS.Color(ambient || '#FFFFFF'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.ray = FSS.Vector3.create(); +}; + +FSS.Light.prototype = Object.create(FSS.Object.prototype); + +/** + * @class Vertex + * @author Matthew Wagerfield + */ +FSS.Vertex = function(x, y, z) { + this.position = FSS.Vector3.create(x, y, z); +}; + +FSS.Vertex.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; + +/** + * @class Triangle + * @author Matthew Wagerfield + */ +FSS.Triangle = function(a, b, c, s, material) { + this.a = a || new FSS.Vertex(); + this.b = b || new FSS.Vertex(); + this.c = c || new FSS.Vertex(); + this.vertices = [this.a, this.b, this.c]; + this.u = FSS.Vector3.create(); + this.v = FSS.Vector3.create(); + this.centroid = FSS.Vector3.create(); + this.normal = FSS.Vector3.create(); + this.material = material || new FSS.Material(); + this.color = new FSS.Color(); + this.polygon = s.polygon(); + this.polygon.attr({ + 'stroke-linejoin': 'round', + 'stroke-miterlimit': 1, + 'stroke-width': 1 + }); + + this.computeCentroid(); + this.computeNormal(); +}; + +FSS.Triangle.prototype = { + computeCentroid: function() { + this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; + this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; + this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; + FSS.Vector3.divideScalar(this.centroid, 3); + return this; + }, + computeNormal: function() { + FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); + FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); + FSS.Vector3.crossVectors(this.normal, this.u, this.v); + FSS.Vector3.normalise(this.normal); + return this; + } +}; + +/** + * @class Geometry + * @author Matthew Wagerfield + */ +FSS.Geometry = function() { + this.vertices = []; + this.triangles = []; + this.dirty = false; +}; + +FSS.Geometry.prototype = { + update: function() { + if (this.dirty) { + var t,triangle; + for (t = this.triangles.length - 1; t >= 0; t--) { + triangle = this.triangles[t]; + triangle.computeCentroid(); + triangle.computeNormal(); + } + this.dirty = false; + } + return this; + } +}; + +/** + * @class Plane + * @author Matthew Wagerfield + */ +FSS.Plane = function(width, height, segments, slices, s, material) { + FSS.Geometry.call(this); + this.width = width || 100; + this.height = height || 100; + this.segments = segments || 4; + this.slices = slices || 4; + this.segmentWidth = this.width / this.segments; + this.sliceHeight = this.height / this.slices; + + // Cache Variables + var x, y, v0, v1, v2, v3, + vertex, triangle, vertices = [], + offsetX = this.width * -0.5, + offsetY = this.height * 0.5; + + // Add Vertices + for (x = 0; x <= this.segments; x++) { + vertices.push([]); + for (y = 0; y <= this.slices; y++) { + vertex = new FSS.Vertex(offsetX + x*this.segmentWidth, offsetY - y*this.sliceHeight); + vertices[x].push(vertex); + this.vertices.push(vertex); + } + } + + // Add Triangles + for (x = 0; x < this.segments; x++) { + for (y = 0; y < this.slices; y++) { + v0 = vertices[x+0][y+0]; + v1 = vertices[x+0][y+1]; + v2 = vertices[x+1][y+0]; + v3 = vertices[x+1][y+1]; + t0 = new FSS.Triangle(v0, v1, v2, s, material); + t1 = new FSS.Triangle(v2, v1, v3, s, material); + this.triangles.push(t0, t1); + } + } +}; + +FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); + +/** + * @class Material + * @author Matthew Wagerfield + */ +FSS.Material = function(ambient, diffuse) { + this.ambient = new FSS.Color(ambient || '#444444'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.slave = new FSS.Color(); +}; + +/** + * @class Mesh + * @author Matthew Wagerfield + */ +FSS.Mesh = function(geometry, material) { + FSS.Object.call(this); + this.geometry = geometry || new FSS.Geometry(); + this.material = material || new FSS.Material(); + this.side = FSS.FRONT; + this.visible = true; +}; + +FSS.Mesh.prototype = Object.create(FSS.Object.prototype); + +FSS.Mesh.prototype.update = function(lights, calculate) { + var t,triangle, l,light, illuminance; + + // Update Geometry + this.geometry.update(); + + // Calculate the triangle colors + if (calculate) { + + // Iterate through Triangles + for (t = this.geometry.triangles.length - 1; t >= 0; t--) { + triangle = this.geometry.triangles[t]; + + // Reset Triangle Color + FSS.Vector4.set(triangle.color.rgba); + + // Iterate through Lights + for (l = lights.length - 1; l >= 0; l--) { + light = lights[l]; + + // Calculate Illuminance + FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); + FSS.Vector3.normalise(light.ray); + illuminance = FSS.Vector3.dot(triangle.normal, light.ray); + if (this.side === FSS.FRONT) { + illuminance = Math.max(illuminance, 0); + } else if (this.side === FSS.BACK) { + illuminance = Math.abs(Math.min(illuminance, 0)); + } else if (this.side === FSS.DOUBLE) { + illuminance = Math.max(Math.abs(illuminance), 0); + } + + // Calculate Ambient Light + FSS.Vector4.multiplyVectors(triangle.material.slave.rgba, triangle.material.ambient.rgba, light.ambient.rgba); + FSS.Vector4.add(triangle.color.rgba, triangle.material.slave.rgba); + + // Calculate Diffuse Light + FSS.Vector4.multiplyVectors(triangle.material.slave.rgba, triangle.material.diffuse.rgba, light.diffuse.rgba); + FSS.Vector4.multiplyScalar(triangle.material.slave.rgba, illuminance); + FSS.Vector4.add(triangle.color.rgba, triangle.material.slave.rgba); + } + + // Clamp & Format Color + FSS.Vector4.clamp(triangle.color.rgba, 0, 1); + } + } + return this; +}; + +/** + * @class Scene + * @author Matthew Wagerfield + */ +FSS.Scene = function() { + this.meshes = []; + this.lights = []; +}; + +FSS.Scene.prototype = { + add: function(object) { + if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { + this.meshes.push(object); + } else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { + this.lights.push(object); + } + return this; + }, + remove: function(object) { + if (object instanceof FSS.Mesh && ~this.meshes.indexOf(object)) { + this.meshes.splice(this.meshes.indexOf(object), 1); + } else if (object instanceof FSS.Light && ~this.lights.indexOf(object)) { + this.lights.splice(this.lights.indexOf(object), 1); + } + return this; + } +}; + +/** + * @class Renderer + * @author Matthew Wagerfield + */ +FSS.Renderer = function() { + this.width = 0; + this.height = 0; + this.halfWidth = 0; + this.halfHeight = 0; +}; + +FSS.Renderer.prototype = { + setSize: function(width, height) { + if (this.width === width && this.height === height) return; + this.width = width; + this.height = height; + this.halfWidth = this.width * 0.5; + this.halfHeight = this.height * 0.5; + return this; + }, + clear: function() { + return this; + }, + render: function(scene) { + return this; + } +}; + +/** + * @class SVG Renderer + * @author Matthew Wagerfield + */ +FSS.SVGRenderer = function(s) { + FSS.Renderer.call(this); + this.element = s.g(); +}; + +FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); + +FSS.SVGRenderer.prototype.setSize = function(width, height) { + FSS.Renderer.prototype.setSize.call(this, width, height); + return this; +}; + +FSS.SVGRenderer.prototype.clear = function() { + FSS.Renderer.prototype.clear.call(this); + for (var i = this.element.childNodes.length - 1; i >= 0; i--) { + this.element.removeChild(this.element.childNodes[i]); + } + return this; +}; + +FSS.SVGRenderer.prototype.render = function(scene) { + FSS.Renderer.prototype.render.call(this, scene); + var m,mesh, t,triangle, points, style; + + // Update Meshes + for (m = scene.meshes.length - 1; m >= 0; m--) { + mesh = scene.meshes[m]; + if (mesh.visible) { + mesh.update(scene.lights, true); + + // Render Triangles + for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { + triangle = mesh.geometry.triangles[t]; + + if (triangle.polygon.parent() !== this.element) { + this.element.append(triangle.polygon); + } + + points = this.formatPoint(triangle.a)+' '; + points += this.formatPoint(triangle.b)+' '; + points += this.formatPoint(triangle.c); + style = this.formatStyle(triangle.color.format()); + + triangle.polygon.attr({ + points: points, + style: style + }); + } + } + } + return this; +}; + +FSS.SVGRenderer.prototype.formatPoint = function(vertex) { + return (this.halfWidth+vertex.position[0])+','+(this.halfHeight-vertex.position[1]); +}; + +FSS.SVGRenderer.prototype.formatStyle = function(color) { + var style = 'fill:'+color+';'; + style += 'stroke:'+color+';'; + return style; +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Color.js b/demos/snap-ad/src/js/vendor/fss/Color.js new file mode 100755 index 0000000..2f0649f --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Color.js @@ -0,0 +1,34 @@ +/** + * @class Color + * @author Matthew Wagerfield + */ +FSS.Color = function(hex, opacity) { + this.rgba = FSS.Vector4.create(); + this.hex = hex || '#000000'; + this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; + this.set(this.hex, this.opacity); +}; + +FSS.Color.prototype = { + set: function(hex, opacity) { + hex = hex.replace('#', ''); + var size = hex.length / 3; + this.rgba[0] = parseInt(hex.substring(size*0, size*1), 16) / 255; + this.rgba[1] = parseInt(hex.substring(size*1, size*2), 16) / 255; + this.rgba[2] = parseInt(hex.substring(size*2, size*3), 16) / 255; + this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; + return this; + }, + hexify: function(channel) { + var hex = Math.ceil(channel*255).toString(16); + if (hex.length === 1) { hex = '0' + hex; } + return hex; + }, + format: function() { + var r = this.hexify(this.rgba[0]); + var g = this.hexify(this.rgba[1]); + var b = this.hexify(this.rgba[2]); + this.hex = '#' + r + g + b; + return this.hex; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Core.js b/demos/snap-ad/src/js/vendor/fss/Core.js new file mode 100755 index 0000000..2c26e1c --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Core.js @@ -0,0 +1,61 @@ +/** + * Defines the Flat Surface Shader namespace for all the awesomeness to exist upon. + * @author Matthew Wagerfield + */ +FSS = { + FRONT : 0, + BACK : 1, + DOUBLE : 2, + SVGNS : 'http://www.w3.org/2000/svg' +}; + +/** + * @class Array + * @author Matthew Wagerfield + */ +FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; + +/** + * @class Utils + * @author Matthew Wagerfield + */ +FSS.Utils = { + isNumber: function(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + } +}; + +/** + * Request Animation Frame Polyfill. + * @author Paul Irish + * @see https://gist.github.com/paulirish/1579671 + */ +(function() { + + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currentTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currentTime - lastTime)); + var id = window.setTimeout(function() { + callback(currentTime + timeToCall); + }, timeToCall); + lastTime = currentTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + +}()); diff --git a/demos/snap-ad/src/js/vendor/fss/Geometry.js b/demos/snap-ad/src/js/vendor/fss/Geometry.js new file mode 100755 index 0000000..f7bf62e --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Geometry.js @@ -0,0 +1,24 @@ +/** + * @class Geometry + * @author Matthew Wagerfield + */ +FSS.Geometry = function() { + this.vertices = []; + this.triangles = []; + this.dirty = false; +}; + +FSS.Geometry.prototype = { + update: function() { + if (this.dirty) { + var t,triangle; + for (t = this.triangles.length - 1; t >= 0; t--) { + triangle = this.triangles[t]; + triangle.computeCentroid(); + triangle.computeNormal(); + } + this.dirty = false; + } + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Light.js b/demos/snap-ad/src/js/vendor/fss/Light.js new file mode 100755 index 0000000..8c7e27f --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Light.js @@ -0,0 +1,12 @@ +/** + * @class Light + * @author Matthew Wagerfield + */ +FSS.Light = function(ambient, diffuse) { + FSS.Object.call(this); + this.ambient = new FSS.Color(ambient || '#FFFFFF'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.ray = FSS.Vector3.create(); +}; + +FSS.Light.prototype = Object.create(FSS.Object.prototype); diff --git a/demos/snap-ad/src/js/vendor/fss/Material.js b/demos/snap-ad/src/js/vendor/fss/Material.js new file mode 100755 index 0000000..a6fcb08 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Material.js @@ -0,0 +1,9 @@ +/** + * @class Material + * @author Matthew Wagerfield + */ +FSS.Material = function(ambient, diffuse) { + this.ambient = new FSS.Color(ambient || '#444444'); + this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); + this.slave = new FSS.Color(); +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Math.js b/demos/snap-ad/src/js/vendor/fss/Math.js new file mode 100755 index 0000000..fae7798 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Math.js @@ -0,0 +1,14 @@ +/** + * @object Math Augmentation + * @author Matthew Wagerfield + */ +Math.PIM2 = Math.PI*2; +Math.PID2 = Math.PI/2; +Math.randomInRange = function(min, max) { + return min + (max - min) * Math.random(); +}; +Math.clamp = function(value, min, max) { + value = Math.max(value, min); + value = Math.min(value, max); + return value; +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Mesh.js b/demos/snap-ad/src/js/vendor/fss/Mesh.js new file mode 100755 index 0000000..a02ef65 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Mesh.js @@ -0,0 +1,62 @@ +/** + * @class Mesh + * @author Matthew Wagerfield + */ +FSS.Mesh = function(geometry, material) { + FSS.Object.call(this); + this.geometry = geometry || new FSS.Geometry(); + this.material = material || new FSS.Material(); + this.side = FSS.FRONT; + this.visible = true; +}; + +FSS.Mesh.prototype = Object.create(FSS.Object.prototype); + +FSS.Mesh.prototype.update = function(lights, calculate) { + var t,triangle, l,light, illuminance; + + // Update Geometry + this.geometry.update(); + + // Calculate the triangle colors + if (calculate) { + + // Iterate through Triangles + for (t = this.geometry.triangles.length - 1; t >= 0; t--) { + triangle = this.geometry.triangles[t]; + + // Reset Triangle Color + FSS.Vector4.set(triangle.color.rgba); + + // Iterate through Lights + for (l = lights.length - 1; l >= 0; l--) { + light = lights[l]; + + // Calculate Illuminance + FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); + FSS.Vector3.normalise(light.ray); + illuminance = FSS.Vector3.dot(triangle.normal, light.ray); + if (this.side === FSS.FRONT) { + illuminance = Math.max(illuminance, 0); + } else if (this.side === FSS.BACK) { + illuminance = Math.abs(Math.min(illuminance, 0)); + } else if (this.side === FSS.DOUBLE) { + illuminance = Math.max(Math.abs(illuminance), 0); + } + + // Calculate Ambient Light + FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.ambient.rgba, light.ambient.rgba); + FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); + + // Calculate Diffuse Light + FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, light.diffuse.rgba); + FSS.Vector4.multiplyScalar(this.material.slave.rgba, illuminance); + FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); + } + + // Clamp & Format Color + FSS.Vector4.clamp(triangle.color.rgba, 0, 1); + } + } + return this; +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Object.js b/demos/snap-ad/src/js/vendor/fss/Object.js new file mode 100755 index 0000000..19255d3 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Object.js @@ -0,0 +1,14 @@ +/** + * @class Object + * @author Matthew Wagerfield + */ +FSS.Object = function() { + this.position = FSS.Vector3.create(); +}; + +FSS.Object.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Plane.js b/demos/snap-ad/src/js/vendor/fss/Plane.js new file mode 100755 index 0000000..c7598c0 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Plane.js @@ -0,0 +1,44 @@ +/** + * @class Plane + * @author Matthew Wagerfield + */ +FSS.Plane = function(width, height, segments, slices, s) { + FSS.Geometry.call(this); + this.width = width || 100; + this.height = height || 100; + this.segments = segments || 4; + this.slices = slices || 4; + this.segmentWidth = this.width / this.segments; + this.sliceHeight = this.height / this.slices; + + // Cache Variables + var x, y, v0, v1, v2, v3, + vertex, triangle, vertices = [], + offsetX = this.width * -0.5, + offsetY = this.height * 0.5; + + // Add Vertices + for (x = 0; x <= this.segments; x++) { + vertices.push([]); + for (y = 0; y <= this.slices; y++) { + vertex = new FSS.Vertex(offsetX + x*this.segmentWidth, offsetY - y*this.sliceHeight); + vertices[x].push(vertex); + this.vertices.push(vertex); + } + } + + // Add Triangles + for (x = 0; x < this.segments; x++) { + for (y = 0; y < this.slices; y++) { + v0 = vertices[x+0][y+0]; + v1 = vertices[x+0][y+1]; + v2 = vertices[x+1][y+0]; + v3 = vertices[x+1][y+1]; + t0 = new FSS.Triangle(v0, v1, v2, s); + t1 = new FSS.Triangle(v2, v1, v3, s); + this.triangles.push(t0, t1); + } + } +}; + +FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); diff --git a/demos/snap-ad/src/js/vendor/fss/Renderer.js b/demos/snap-ad/src/js/vendor/fss/Renderer.js new file mode 100755 index 0000000..aebac17 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Renderer.js @@ -0,0 +1,27 @@ +/** + * @class Renderer + * @author Matthew Wagerfield + */ +FSS.Renderer = function() { + this.width = 0; + this.height = 0; + this.halfWidth = 0; + this.halfHeight = 0; +}; + +FSS.Renderer.prototype = { + setSize: function(width, height) { + if (this.width === width && this.height === height) return; + this.width = width; + this.height = height; + this.halfWidth = this.width * 0.5; + this.halfHeight = this.height * 0.5; + return this; + }, + clear: function() { + return this; + }, + render: function(scene) { + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/SVGRenderer.js b/demos/snap-ad/src/js/vendor/fss/SVGRenderer.js new file mode 100755 index 0000000..aa226e9 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/SVGRenderer.js @@ -0,0 +1,66 @@ +/** + * @class SVG Renderer + * @author Matthew Wagerfield + */ +FSS.SVGRenderer = function(s) { + FSS.Renderer.call(this); + this.element = s.g(); +}; + +FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); + +FSS.SVGRenderer.prototype.setSize = function(width, height) { + FSS.Renderer.prototype.setSize.call(this, width, height); + return this; +}; + +FSS.SVGRenderer.prototype.clear = function() { + FSS.Renderer.prototype.clear.call(this); + for (var i = this.element.childNodes.length - 1; i >= 0; i--) { + this.element.removeChild(this.element.childNodes[i]); + } + return this; +}; + +FSS.SVGRenderer.prototype.render = function(scene) { + FSS.Renderer.prototype.render.call(this, scene); + var m,mesh, t,triangle, points, style; + + // Update Meshes + for (m = scene.meshes.length - 1; m >= 0; m--) { + mesh = scene.meshes[m]; + if (mesh.visible) { + mesh.update(scene.lights, true); + + // Render Triangles + for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { + triangle = mesh.geometry.triangles[t]; + + if (triangle.polygon.parent() !== this.element) { + this.element.append(triangle.polygon); + } + + points = this.formatPoint(triangle.a)+' '; + points += this.formatPoint(triangle.b)+' '; + points += this.formatPoint(triangle.c); + style = this.formatStyle(triangle.color.format()); + + triangle.polygon.attr({ + points: points, + style: style + }); + } + } + } + return this; +}; + +FSS.SVGRenderer.prototype.formatPoint = function(vertex) { + return (this.halfWidth+vertex.position[0])+','+(this.halfHeight-vertex.position[1]); +}; + +FSS.SVGRenderer.prototype.formatStyle = function(color) { + var style = 'fill:'+color+';'; + style += 'stroke:'+color+';'; + return style; +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Scene.js b/demos/snap-ad/src/js/vendor/fss/Scene.js new file mode 100755 index 0000000..50f1dc6 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Scene.js @@ -0,0 +1,27 @@ +/** + * @class Scene + * @author Matthew Wagerfield + */ +FSS.Scene = function() { + this.meshes = []; + this.lights = []; +}; + +FSS.Scene.prototype = { + add: function(object) { + if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { + this.meshes.push(object); + } else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { + this.lights.push(object); + } + return this; + }, + remove: function(object) { + if (object instanceof FSS.Mesh && ~this.meshes.indexOf(object)) { + this.meshes.splice(this.meshes.indexOf(object), 1); + } else if (object instanceof FSS.Light && ~this.lights.indexOf(object)) { + this.lights.splice(this.lights.indexOf(object), 1); + } + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Triangle.js b/demos/snap-ad/src/js/vendor/fss/Triangle.js new file mode 100755 index 0000000..23cf4da --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Triangle.js @@ -0,0 +1,41 @@ +/** + * @class Triangle + * @author Matthew Wagerfield + */ +FSS.Triangle = function(a, b, c, s) { + this.a = a || new FSS.Vertex(); + this.b = b || new FSS.Vertex(); + this.c = c || new FSS.Vertex(); + this.vertices = [this.a, this.b, this.c]; + this.u = FSS.Vector3.create(); + this.v = FSS.Vector3.create(); + this.centroid = FSS.Vector3.create(); + this.normal = FSS.Vector3.create(); + this.color = new FSS.Color(); + this.polygon = s.polygon(); + this.polygon.attr({ + 'stroke-linejoin': 'round', + 'stroke-miterlimit': 1, + 'stroke-width': 1 + }); + + this.computeCentroid(); + this.computeNormal(); +}; + +FSS.Triangle.prototype = { + computeCentroid: function() { + this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; + this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; + this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; + FSS.Vector3.divideScalar(this.centroid, 3); + return this; + }, + computeNormal: function() { + FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); + FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); + FSS.Vector3.crossVectors(this.normal, this.u, this.v); + FSS.Vector3.normalise(this.normal); + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Vector3.js b/demos/snap-ad/src/js/vendor/fss/Vector3.js new file mode 100755 index 0000000..93787ac --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Vector3.js @@ -0,0 +1,190 @@ +/** + * @object Vector3 + * @author Matthew Wagerfield + */ +FSS.Vector3 = { + create: function(x, y, z) { + var vector = new FSS.Array(3); + this.set(vector, x, y, z); + return vector; + }, + clone: function(a) { + var vector = this.create(); + this.copy(vector, a); + return vector; + }, + set: function(target, x, y, z) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + copy: function(target, a) { + target[0] = a[0]; + target[1] = a[1]; + target[2] = a[2]; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + return this; + }, + addVectors: function(target, a, b) { + target[0] = a[0] + b[0]; + target[1] = a[1] + b[1]; + target[2] = a[2] + b[2]; + return this; + }, + addScalar: function(target, s) { + target[0] += s; + target[1] += s; + target[2] += s; + return this; + }, + subtract: function(target, a) { + target[0] -= a[0]; + target[1] -= a[1]; + target[2] -= a[2]; + return this; + }, + subtractVectors: function(target, a, b) { + target[0] = a[0] - b[0]; + target[1] = a[1] - b[1]; + target[2] = a[2] - b[2]; + return this; + }, + subtractScalar: function(target, s) { + target[0] -= s; + target[1] -= s; + target[2] -= s; + return this; + }, + multiply: function(target, a) { + target[0] *= a[0]; + target[1] *= a[1]; + target[2] *= a[2]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + return this; + }, + divide: function(target, a) { + target[0] /= a[0]; + target[1] /= a[1]; + target[2] /= a[2]; + return this; + }, + divideVectors: function(target, a, b) { + target[0] = a[0] / b[0]; + target[1] = a[1] / b[1]; + target[2] = a[2] / b[2]; + return this; + }, + divideScalar: function(target, s) { + if (s !== 0) { + target[0] /= s; + target[1] /= s; + target[2] /= s; + } else { + target[0] = 0; + target[1] = 0; + target[2] = 0; + } + return this; + }, + cross: function(target, a) { + var x = target[0]; + var y = target[1]; + var z = target[2]; + target[0] = y*a[2] - z*a[1]; + target[1] = z*a[0] - x*a[2]; + target[2] = x*a[1] - y*a[0]; + return this; + }, + crossVectors: function(target, a, b) { + target[0] = a[1]*b[2] - a[2]*b[1]; + target[1] = a[2]*b[0] - a[0]*b[2]; + target[2] = a[0]*b[1] - a[1]*b[0]; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + }, + limit: function(target, min, max) { + var length = this.length(target); + if (min !== null && length < min) { + this.setLength(target, min); + } else if (max !== null && length > max) { + this.setLength(target, max); + } + return this; + }, + dot: function(a, b) { + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; + }, + normalise: function(target) { + return this.divideScalar(target, this.length(target)); + }, + negate: function(target) { + return this.multiplyScalar(target, -1); + }, + distanceSquared: function(a, b) { + var dx = a[0] - b[0]; + var dy = a[1] - b[1]; + var dz = a[2] - b[2]; + return dx*dx + dy*dy + dz*dz; + }, + distance: function(a, b) { + return Math.sqrt(this.distanceSquared(a, b)); + }, + lengthSquared: function(a) { + return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; + }, + length: function(a) { + return Math.sqrt(this.lengthSquared(a)); + }, + setLength: function(target, l) { + var length = this.length(target); + if (length !== 0 && l !== length) { + this.multiplyScalar(target, l / length); + } + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Vector4.js b/demos/snap-ad/src/js/vendor/fss/Vector4.js new file mode 100755 index 0000000..1004cb2 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Vector4.js @@ -0,0 +1,74 @@ +/** + * @object Vector4 + * @author Matthew Wagerfield + */ +FSS.Vector4 = { + create: function(x, y, z, w) { + var vector = new FSS.Array(4); + this.set(vector, x, y, z); + return vector; + }, + set: function(target, x, y, z, w) { + target[0] = x || 0; + target[1] = y || 0; + target[2] = z || 0; + target[3] = w || 0; + return this; + }, + setX: function(target, x) { + target[0] = x || 0; + return this; + }, + setY: function(target, y) { + target[1] = y || 0; + return this; + }, + setZ: function(target, z) { + target[2] = z || 0; + return this; + }, + setW: function(target, w) { + target[3] = w || 0; + return this; + }, + add: function(target, a) { + target[0] += a[0]; + target[1] += a[1]; + target[2] += a[2]; + target[3] += a[3]; + return this; + }, + multiplyVectors: function(target, a, b) { + target[0] = a[0] * b[0]; + target[1] = a[1] * b[1]; + target[2] = a[2] * b[2]; + target[3] = a[3] * b[3]; + return this; + }, + multiplyScalar: function(target, s) { + target[0] *= s; + target[1] *= s; + target[2] *= s; + target[3] *= s; + return this; + }, + min: function(target, value) { + if (target[0] < value) { target[0] = value; } + if (target[1] < value) { target[1] = value; } + if (target[2] < value) { target[2] = value; } + if (target[3] < value) { target[3] = value; } + return this; + }, + max: function(target, value) { + if (target[0] > value) { target[0] = value; } + if (target[1] > value) { target[1] = value; } + if (target[2] > value) { target[2] = value; } + if (target[3] > value) { target[3] = value; } + return this; + }, + clamp: function(target, min, max) { + this.min(target, min); + this.max(target, max); + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/fss/Vertex.js b/demos/snap-ad/src/js/vendor/fss/Vertex.js new file mode 100755 index 0000000..58b9c58 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/fss/Vertex.js @@ -0,0 +1,14 @@ +/** + * @class Vertex + * @author Matthew Wagerfield + */ +FSS.Vertex = function(x, y, z) { + this.position = FSS.Vector3.create(x, y, z); +}; + +FSS.Vertex.prototype = { + setPosition: function(x, y, z) { + FSS.Vector3.set(this.position, x, y, z); + return this; + } +}; diff --git a/demos/snap-ad/src/js/vendor/modernizr.min.js b/demos/snap-ad/src/js/vendor/modernizr.min.js new file mode 100644 index 0000000..02fb48a --- /dev/null +++ b/demos/snap-ad/src/js/vendor/modernizr.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.7.1 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-shiv-cssclasses-load + */ +;window.Modernizr=function(a,b,c){function u(a){j.cssText=a}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function w(a,b){return typeof a===b}function x(a,b){return!!~(""+a).indexOf(b)}function y(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:w(f,"function")?f.bind(d||b):f}return!1}var d="2.7.1",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m={},n={},o={},p=[],q=p.slice,r,s={}.hasOwnProperty,t;!w(s,"undefined")&&!w(s.call,"undefined")?t=function(a,b){return s.call(a,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=q.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(q.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(q.call(arguments)))};return e});for(var z in m)t(m,z)&&(r=z.toLowerCase(),e[r]=m[z](),p.push((e[r]?"":"no-")+r));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)t(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},u(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+p.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value !== 'string') { + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + //Allow getting a global that expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof define !== 'undefined') { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof requirejs !== 'undefined') { + if (isFunction(requirejs)) { + //Do not overwrite and existing requirejs instance. + return; + } + cfg = requirejs; + requirejs = undefined; + } + + //Allow for a require config object + if (typeof require !== 'undefined' && !isFunction(require)) { + //assume it is a config object. + cfg = require; + require = undefined; + } + + function newContext(contextName) { + var inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId, + config = { + //Defaults. Do not set a default for map + //config to speed up normalize(), which + //will run faster if there is no default. + waitSeconds: 7, + baseUrl: './', + paths: {}, + pkgs: {}, + shim: {}, + config: {} + }, + registry = {}, + //registry of just enabled modules, to speed + //cycle breaking code when lots of modules + //are registered, but not activated. + enabledRegistry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + requireCounter = 1, + unnormalizedCounter = 1; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; ary[i]; i += 1) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, + foundMap, foundI, foundStarMap, starI, + baseParts = baseName && baseName.split('/'), + normalizedBaseParts = baseParts, + map = config.map, + starMap = map && map['*']; + + //Adjust any relative paths. + if (name && name.charAt(0) === '.') { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + if (getOwn(config.pkgs, baseName)) { + //If the baseName is a package name, then just treat it as one + //name to concat the name with. + normalizedBaseParts = baseParts = [baseName]; + } else { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + } + + name = normalizedBaseParts.concat(name.split('/')); + trimDots(name); + + //Some use of packages may use a . path to reference the + //'main' module name, so normalize for that. + pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); + name = name.join('/'); + if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { + name = pkgName; + } + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if (applyMap && map && (baseParts || starMap)) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = getOwn(mapValue, nameSegment); + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = getOwn(config.paths, id); + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + removeScript(id); + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.require.undef(id); + context.require([id]); + return true; + } + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via require.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a define() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var url, pluginModule, suffix, nameParts, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = ''; + + //If no name, then it means it is a require call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = getOwn(defined, prefix); + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + normalizedName = normalize(name, parentName, applyMap); + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + + url = context.nameToUrl(normalizedName); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + getModule(depMap).on(name, fn); + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = getOwn(registry, id); + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + //Array splice in the values since the context code has a + //local var ref to defQueue, so cannot just reassign the one + //on context. + apsp.apply(defQueue, + [defQueue.length - 1, 0].concat(globalDefQueue)); + globalDefQueue = []; + } + } + + handlers = { + 'require': function (mod) { + if (mod.require) { + return mod.require; + } else { + return (mod.require = context.makeRequire(mod.map)); + } + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + if (mod.exports) { + return mod.exports; + } else { + return (mod.exports = defined[mod.map.id] = {}); + } + } + }, + 'module': function (mod) { + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return (config.config && getOwn(config.config, mod.map.id)) || {}; + }, + exports: defined[mod.map.id] + }); + } + } + }; + + function cleanRegistry(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + delete enabledRegistry[id]; + } + + function breakCycle(mod, traced, processed) { + var id = mod.map.id; + + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } + } + }); + processed[id] = true; + } + } + + function checkLoaded() { + var map, modId, err, usingPathFallback, + waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + reqCalls = [], + stillLoading = false, + needCycleCheck = true; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(enabledRegistry, function (mod) { + map = mod.map; + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!map.isDefine) { + reqCalls.push(mod); + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); + }); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = getOwn(undefEvents, map.id) || {}; + this.map = map; + this.shim = getOwn(config.shim, map.id); + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function (depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple define calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function () { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks if the module is ready to define itself, and if so, + * define it. + */ + check: function () { + if (!this.enabled || this.enabling) { + return; + } + + var err, cjsModule, + id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory; + + if (!this.inited) { + this.fetch(); + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another require call + //that would result in checking this module to + //define itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. + if (this.events.error) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + if (this.map.isDefine) { + //If setting exports via 'module' is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + cjsModule = this.module; + if (cjsModule && + cjsModule.exports !== undefined && + //Make sure it is not already the exports value + cjsModule.exports !== this.exports) { + exports = cjsModule.exports; + } else if (exports === undefined && this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = [this.map.id]; + err.requireType = 'define'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + req.onResourceLoad(context, this.map, this.depMaps); + } + } + + //Clean up + cleanRegistry(id); + + this.defined = true; + } + + //Finished the define stage. Allow calling check again + //to allow define notifications below in the case of a + //cycle. + this.defining = false; + + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + + } + }, + + callPlugin: function () { + var map = this.map, + id = map.id, + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var load, normalizedMap, normalizedMod, + name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true + }); + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + //prefix and name should already be normalized, no need + //for applying map config again either. + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + + normalizedMod = getOwn(registry, normalizedMap.id); + if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + cleanRegistry(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = bind(this, function (text, textAlt) { + /*jslint evil: true */ + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } + + //Turn off interactive script matching for IE for any define + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } + + try { + req.exec(text); + } catch (e) { + return onError(makeError('fromtexteval', + 'fromText eval for ' + id + + ' failed: ' + e, + e, + [id])); + } + + if (hasInteractive) { + useInteractive = true; + } + + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + + //Support anonymous modules. + context.completeLoad(moduleName); + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, localRequire, load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + enabledRegistry[this.map.id] = this; + this.enabled = true; + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.skipMap); + this.depMaps[i] = depMap; + + handler = getOwn(handlers, depMap.id); + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', this.errback); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'require', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!hasProp(handlers, id) && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = getOwn(registry, pluginMap.id); + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function (name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the requirejs info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + } + + context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + defQueue: defQueue, + Module: Module, + makeModuleMap: makeModuleMap, + nextTick: req.nextTick, + onError: onError, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + //Save off the paths and packages since they require special processing, + //they are additive. + var pkgs = config.pkgs, + shim = config.shim, + objs = { + paths: true, + config: true, + map: true + }; + + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (prop === 'map') { + if (!config.map) { + config.map = {}; + } + mixin(config[prop], value, true, true); + } else { + mixin(config[prop], value, true); + } + } else { + config[prop] = value; + } + }); + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location; + + pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + location = pkgObj.location; + + //Create a brand new object on pkgs, since currentPackages can + //be passed in again, and config.pkgs is the internal transformed + //state for all package configs. + pkgs[pkgObj.name] = { + name: pkgObj.name, + location: location || pkgObj.name, + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + main: (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, '') + }; + }); + + //Done with modifications, assing packages back to context config + config.pkgs = pkgs; + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + //If module already has init called, since it is too + //late to modify them, and ignore unnormalized ones + //since they are transient. + if (!mod.inited && !mod.map.unnormalized) { + mod.map = makeModuleMap(id); + } + }); + + //If a deps array or a config callback is specified, then call + //require with those args. This is useful when require is defined as a + //config object before require.js is loaded. + if (cfg.deps || cfg.callback) { + context.require(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); + } + return fn; + }, + + makeRequire: function (relMap, options) { + options = options || {}; + + function localRequire(deps, callback, errback) { + var id, map, requireMod; + + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; + } + + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } + + //If require|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } + + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap, localRequire); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use require([])'))); + } + return defined[id]; + } + + //Grab defines waiting in the global queue. + intakeDefines(); + + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //require call, collect them. + intakeDefines(); + + requireMod = getModule(makeModuleMap(null, relMap)); + + //Store if map config should be applied to this require + //call for dependencies. + requireMod.skipMap = options.skipMap; + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + }); + + return localRequire; + } + + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var ext, + index = moduleNamePlusExt.lastIndexOf('.'), + segment = moduleNamePlusExt.split('/')[0], + isRelative = segment === '.' || segment === '..'; + + //Have a file extension alias, and it is not the + //dots from a relative path. + if (index !== -1 && (!isRelative || index > 1)) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext, true); + }, + + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + } + }); + + //Only allow undef on top level require calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting define() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + cleanRegistry(id); + } + }; + } + + return localRequire; + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. A second arg, parent, the parent module, + * is passed in for context, when this method is overriden by + * the optimizer. Not shown here to keep code compact. + */ + enable: function (depMap) { + var mod = getOwn(registry, depMap.id); + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var found, args, mod, + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching define call for this script! + found = true; + } + + callGetModule(args); + } + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = getOwn(registry, moduleName); + + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No define call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call define(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + * Note that it **does not** call normalize on the moduleName, + * it is assumed to have already been normalized. This is an + * internal API, not a public one. Use toUrl for the public API. + */ + nameToUrl: function (moduleName, ext, skipExt) { + var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, + parentPath; + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + pkgs = config.pkgs; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } else if (pkg) { + //If module name is just the package name, then looking + //for the main module. + if (moduleName === pkg.name) { + pkgPath = pkg.location + '/' + pkg.main; + } else { + pkgPath = pkg.location; + } + syms.splice(0, i, pkgPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/'); + url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs ? url + + ((url.indexOf('?') === -1 ? '?' : '&') + + config.urlArgs) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callack function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layer in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + return onError(makeError('scripterror', 'Script error', evt, [data.id])); + } + } + }; + + context.require = context.makeRequire(); + return context; + } + + /** + * Main entry point. + * + * If the only argument to require is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a require that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = requirejs = function (deps, callback, errback, optional) { + + //Find the right context, use default + var context, config, + contextName = defContextName; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = getOwn(contexts, contextName); + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.require(deps, callback, errback); + }; + + /** + * Support require.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global require. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.require[prop].apply(ctx, arguments); + }; + }); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that require explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = function (err) { + throw err; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the require context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/jrburke/requirejs/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/jrburke/requirejs/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous define call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the define call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEventListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous define + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + try { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation that a build has been done so that + //only one script needs to be loaded anyway. This may need to be + //reevaluated if other use cases become common. + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } catch (e) { + context.onError(makeError('importscripts', + 'importScripts failed for ' + + moduleName + ' at ' + url, + e, + [moduleName])); + } + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser) { + //Figure out baseUrl. Get it from the script tag with require.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + //Set final baseUrl if there is not already an explicit one. + if (!cfg.baseUrl) { + //Pull off the directory of data-main for use as the + //baseUrl. + src = dataMain.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + cfg.baseUrl = subPath; + dataMain = mainScript; + } + + //Strip off any trailing .js since dataMain is now + //like a module name. + dataMain = dataMain.replace(jsSuffixRegExp, ''); + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * require() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to define the module corresponding to the first argument's + * name. + */ + define = function (name, deps, callback) { + var node, context; + + //Allow for anonymous modules + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = []; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps.length && isFunction(callback)) { + //Remove comments from the callback string, + //look for require calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, '') + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without require calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs require. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous define() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + }; + + define.amd = { + jQuery: true + }; + + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this)); diff --git a/demos/snap-ad/src/js/vendor/require.min.js b/demos/snap-ad/src/js/vendor/require.min.js new file mode 100644 index 0000000..84d1d67 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/require.min.js @@ -0,0 +1,36 @@ +/* + RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + Available via the MIT or new BSD license. + see: http://github.com/jrburke/requirejs for details +*/ +var requirejs,require,define; +(function(ca){function G(b){return"[object Function]"===N.call(b)}function H(b){return"[object Array]"===N.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&& +(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= +this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f); +if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval", +"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(K,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b, +a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(K,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m, +nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b, +a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild= +!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(K,f))return K[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}M();i.nextTick(function(){M();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!== +d&&(!("."===g||".."===g)||1g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): +(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,M=g,D?y.insertBefore(g,D):y.appendChild(g),M=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(L=b.getAttribute("data-main"))return q=L,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl= +Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=L),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=M))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b|| +(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this); diff --git a/demos/snap-ad/src/js/vendor/snap.svg-min.js b/demos/snap-ad/src/js/vendor/snap.svg-min.js new file mode 100755 index 0000000..cfc6ef1 --- /dev/null +++ b/demos/snap-ad/src/js/vendor/snap.svg-min.js @@ -0,0 +1,20 @@ +// 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: 2014-01-23 +!function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g="*",h=function(){},i=function(a,b){return a-b},j={n:{}},k=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=k.listeners(a),j=0,l=[],m={},n=[],o=b;b=a,c=0;for(var p=0,q=h.length;q>p;p++)"zIndex"in h[p]&&(l.push(h[p].zIndex),h[p].zIndex<0&&(m[h[p].zIndex]=h[p]));for(l.sort(i);l[j]<0;)if(e=m[l[j++]],n.push(e.apply(d,g)),c)return c=f,n;for(p=0;q>p;p++)if(e=h[p],"zIndex"in e)if(e.zIndex==l[j]){if(n.push(e.apply(d,g)),c)break;do if(j++,e=m[l[j]],e&&n.push(e.apply(d,g)),c)break;while(e)}else m[e.zIndex]=e;else if(n.push(e.apply(d,g)),c)break;return c=f,b=o,n.length?n:null};k._events=j,k.listeners=function(a){var b,c,d,e,h,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,h=m.length;h>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[g]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},k.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(f),d=j,e=0,g=c.length;g>e;e++)d=d.n,d=d.hasOwnProperty(c[e])&&d[c[e]]||(d[c[e]]={n:{}});for(d.f=d.f||[],e=0,g=d.f.length;g>e;e++)if(d.f[e]==b)return h;return d.f.push(b),function(a){+a==+a&&(b.zIndex=+a)}},k.f=function(a){var b=[].slice.call(arguments,1);return function(){k.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},k.stop=function(){c=1},k.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},k.nts=function(){return b.split(f)},k.off=k.unbind=function(a,b){if(!a)return void(k._events=j={n:{}});var c,d,h,i,l,m,n,o=a.split(f),p=[j];for(i=0,l=o.length;l>i;i++)for(m=0;mi;i++)for(c=p[i];c.n;){if(b){if(c.f){for(m=0,n=c.f.length;n>m;m++)if(c.f[m]==b){c.f.splice(m,1);break}!c.f.length&&delete c.f}for(d in c.n)if(c.n[e](d)&&c.n[d].f){var q=c.n[d].f;for(m=0,n=q.length;n>m;m++)if(q[m]==b){q.splice(m,1);break}!q.length&&delete c.n[d].f}}else{delete c.f;for(d in c.n)c.n[e](d)&&c.n[d].f&&delete c.n[d].f}c=c.n}},k.once=function(a,b){var c=function(){return k.unbind(a,c),b.apply(this,arguments)};return k.on(a,c)},k.version=d,k.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=k:"undefined"!=typeof define?define("eve",[],function(){return k}):a.eve=k}(this),function(a,b){"function"==typeof define&&define.amd?define(["eve"],function(c){return b(a,c)}):b(a,a.eve)}(this,function(a,b){var c=function(b){var c={},d=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},e=Array.isArray||function(a){return a instanceof Array||"[object Array]"==Object.prototype.toString.call(a)},f=0,g="M"+(+new Date).toString(36),h=function(){return g+(f++).toString(36)},i=Date.now||function(){return+new Date},j=function(a){var b=this;if(null==a)return b.s;var c=b.s-a;b.b+=b.dur*c,b.B+=b.dur*c,b.s=a},k=function(a){var b=this;return null==a?b.spd:void(b.spd=a)},l=function(a){var b=this;return null==a?b.dur:(b.s=b.s*a/b.dur,void(b.dur=a))},m=function(){var a=this;delete c[a.id],b("mina.stop."+a.id,a)},n=function(){var a=this;a.pdif||(delete c[a.id],a.pdif=a.get()-a.b)},o=function(){var a=this;a.pdif&&(a.b=a.get()-a.pdif,delete a.pdif,c[a.id]=a)},p=function(){var a=0;for(var f in c)if(c.hasOwnProperty(f)){var g,h=c[f],i=h.get();if(a++,h.s=(i-h.b)/(h.dur/h.spd),h.s>=1&&(delete c[f],h.s=1,a--,function(a){setTimeout(function(){b("mina.finish."+a.id,a)})}(h)),e(h.start)){g=[];for(var j=0,k=h.start.length;k>j;j++)g[j]=+h.start[j]+(h.end[j]-h.start[j])*h.easing(h.s)}else g=+h.start+(h.end-h.start)*h.easing(h.s);h.set(g)}a&&d(p)},q=function(a,b,e,f,g,i,r){var s={id:h(),start:a,end:b,b:e,s:0,dur:f-e,spd:1,get:g,set:i,easing:r||q.linear,status:j,speed:k,duration:l,stop:m,pause:n,resume:o};c[s.id]=s;var t,u=0;for(t in c)if(c.hasOwnProperty(t)&&(u++,2==u))break;return 1==u&&d(p),s};return q.time=i,q.getById=function(a){return c[a]||null},q.linear=function(a){return a},q.easeout=function(a){return Math.pow(a,1.7)},q.easein=function(a){return Math.pow(a,.48)},q.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=.48-a/1.04,c=Math.sqrt(.1734+b*b),d=c-b,e=Math.pow(Math.abs(d),1/3)*(0>d?-1:1),f=-c-b,g=Math.pow(Math.abs(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},q.backin=function(a){if(1==a)return 1;var b=1.70158;return a*a*((b+1)*a-b)},q.backout=function(a){if(0==a)return 0;a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},q.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-.075)*Math.PI/.3)+1},q.bounce=function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b},a.mina=q,q}("undefined"==typeof b?function(){}:b),d=function(){function d(a,b){if(a){if(a.tagName)return z(a);if(a instanceof u)return a;if(null==b)return a=I.doc.querySelector(a),z(a)}return a=null==a?"100%":a,b=null==b?"100%":b,new y(a,b)}function e(a,b){if(b){if("string"==typeof a&&(a=e(a)),"string"==typeof b)return"xlink:"==b.substring(0,6)?a.getAttributeNS(fb,b.substring(6)):"xml:"==b.substring(0,4)?a.getAttributeNS(gb,b.substring(4)):a.getAttribute(b);for(var c in b)if(b[J](c)){var d=K(b[c]);d?"xlink:"==c.substring(0,6)?a.setAttributeNS(fb,c.substring(6),d):"xml:"==c.substring(0,4)?a.setAttributeNS(gb,c.substring(4),d):a.setAttribute(c,d):a.removeAttribute(c)}}else a=I.doc.createElementNS(gb,a);return a}function f(a,b){return b=K.prototype.toLowerCase.call(b),"finite"==b?isFinite(a):"array"==b&&(a instanceof Array||Array.isArray&&Array.isArray(a))?!0:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||U.call(a).slice(8,-1).toLowerCase()==b}function h(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[J](c)&&(b[c]=h(a[c]));return b}function i(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function j(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),g=d.cache=d.cache||{},h=d.count=d.count||[];return g[J](f)?(i(h,f),c?c(g[f]):g[f]):(h.length>=1e3&&delete g[h.shift()],h.push(f),g[f]=a.apply(b,e),c?c(g[f]):g[f])}return d}function k(a,b,c,d,e,f){if(null==e){var g=a-c,h=b-d;return g||h?(180+180*N.atan2(-h,-g)/R+360)%360:0}return k(a,b,e,f)-k(c,d,e,f)}function l(a){return a%360*R/180}function m(a){return 180*a/R%360}function n(a,b,c,d,e,f){return null==b&&"[object SVGMatrix]"==U.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,void(this.f=a.f)):void(null!=a?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0))}function o(a){var b=[];return a=a.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g,function(a,c,d){return d=d.split(/\s*,\s*|\s+/),"rotate"==c&&1==d.length&&d.push(0,0),"scale"==c&&(2==d.length&&d.push(0,0),1==d.length&&d.push(d[0],0,0)),b.push("skewX"==c?["m",1,0,N.tan(l(d[0])),1,0,0]:"skewY"==c?["m",1,N.tan(l(d[0])),0,1,0,0]:[c.charAt(0)].concat(d)),a}),b}function p(a,b){var c=qb(a),d=new n;if(c)for(var e=0,f=c.length;f>e;e++){var g,h,i,j,k,l=c[e],m=l.length,o=K(l[0]).toLowerCase(),p=l[0]!=o,q=p?d.invert():0;"t"==o&&2==m?d.translate(l[1],0):"t"==o&&3==m?p?(g=q.x(0,0),h=q.y(0,0),i=q.x(l[1],l[2]),j=q.y(l[1],l[2]),d.translate(i-g,j-h)):d.translate(l[1],l[2]):"r"==o?2==m?(k=k||b,d.rotate(l[1],k.x+k.width/2,k.y+k.height/2)):4==m&&(p?(i=q.x(l[2],l[3]),j=q.y(l[2],l[3]),d.rotate(l[1],i,j)):d.rotate(l[1],l[2],l[3])):"s"==o?2==m||3==m?(k=k||b,d.scale(l[1],l[m-1],k.x+k.width/2,k.y+k.height/2)):4==m?p?(i=q.x(l[2],l[3]),j=q.y(l[2],l[3]),d.scale(l[1],l[1],i,j)):d.scale(l[1],l[1],l[2],l[3]):5==m&&(p?(i=q.x(l[3],l[4]),j=q.y(l[3],l[4]),d.scale(l[1],l[2],i,j)):d.scale(l[1],l[2],l[3],l[4])):"m"==o&&7==m&&d.add(l[1],l[2],l[3],l[4],l[5],l[6])}return d}function q(a,b){if(null==b){var c=!0;if(b=a.node.getAttribute("linearGradient"==a.type||"radialGradient"==a.type?"gradientTransform":"pattern"==a.type?"patternTransform":"transform"),!b)return new n;b=o(b)}else b=d._.rgTransform.test(b)?K(b).replace(/\.{3}|…/g,a._.transform||S):o(b),f(b,"array")&&(b=d.path?d.path.toString.call(b):K(b)),a._.transform=b;var e=p(b,a.getBBox(1));return c?e:void(a.matrix=e)}function r(a){var b=d._.someDefs;if(b&&rb(b.ownerDocument.documentElement,b))return b;var c=a.node.ownerSVGElement&&z(a.node.ownerSVGElement)||a.node.parentNode&&z(a.node.parentNode)||d.select("svg")||d(0,0),e=c.select("defs"),f=null==e?!1:e.node;return f||(f=x("defs",c.node).node),d._.someDefs=f,f}function s(a,b,c){function d(a){return null==a?S:a==+a?a:(e(j,{width:a}),j.getBBox().width)}function f(a){return null==a?S:a==+a?a:(e(j,{height:a}),j.getBBox().height)}function g(d,e){null==b?i[d]=e(a.attr(d)):d==b&&(i=e(null==c?a.attr(d):c))}var h=r(a),i={},j=h.querySelector(".svg---mgr");switch(j||(j=e("rect"),e(j,{width:10,height:10,"class":"svg---mgr"}),h.appendChild(j)),a.type){case"rect":g("rx",d),g("ry",f);case"image":g("width",d),g("height",f);case"text":g("x",d),g("y",f);break;case"circle":g("cx",d),g("cy",f),g("r",d);break;case"ellipse":g("cx",d),g("cy",f),g("rx",d),g("ry",f);break;case"line":g("x1",d),g("x2",d),g("y1",f),g("y2",f);break;case"marker":g("refX",d),g("markerWidth",d),g("refY",f),g("markerHeight",f);break;case"radialGradient":g("fx",d),g("fy",f);break;case"tspan":g("dx",d),g("dy",f);break;default:g(b,d)}return i}function t(a){f(a,"array")||(a=Array.prototype.slice.call(arguments,0));for(var b=0,c=0,d=this.node;this[b];)delete this[b++];for(b=0;bc;c++)if(b=b||a[c])return b}function w(a){this.node=a}function x(a,b){var c=e(a);b.appendChild(c);var d=z(c);return d.type=a,d}function y(a,b){var c,d,f,g=y.prototype;if(a&&"svg"==a.tagName){if(a.snap in hb)return hb[a.snap];c=new u(a),d=a.getElementsByTagName("desc")[0],f=a.getElementsByTagName("defs")[0],d||(d=e("desc"),d.appendChild(I.doc.createTextNode("Created with Snap")),c.node.appendChild(d)),f||(f=e("defs"),c.node.appendChild(f)),c.defs=f;for(var h in g)g[J](h)&&(c[h]=g[h]);c.paper=c.root=c}else c=x("svg",I.doc.body),e(c.node,{height:b,version:1.1,width:a,xmlns:gb});return c}function z(a){return a?a instanceof u||a instanceof w?a:"svg"==a.tagName?new y(a):new u(a):a}function A(){return this.selectAll("stop")}function B(a,b){var c=e("stop"),f={offset:+b+"%"};return a=d.color(a),f["stop-color"]=a.hex,a.opacity<1&&(f["stop-opacity"]=a.opacity),e(c,f),this.node.appendChild(c),this}function C(){if("linearGradient"==this.type){var a=e(this.node,"x1")||0,b=e(this.node,"x2")||1,c=e(this.node,"y1")||0,f=e(this.node,"y2")||0;return d._.box(a,c,N.abs(b-a),N.abs(f-c))}var g=this.node.cx||.5,h=this.node.cy||.5,i=this.node.r||0;return d._.box(g-i,h-i,2*i,2*i)}function D(a,c){function d(a,b){for(var c=(b-j)/(a-k),d=k;a>d;d++)h[d].offset=+(+j+c*(d-k)).toFixed(2);k=a,j=b}var f,g=v(b("snap.util.grad.parse",null,c));if(!g)return null;g.params.unshift(a),f="l"==g.type.toLowerCase()?E.apply(0,g.params):F.apply(0,g.params),g.type!=g.type.toLowerCase()&&e(f.node,{gradientUnits:"userSpaceOnUse"});var h=g.stops,i=h.length,j=0,k=0;i--;for(var l=0;i>l;l++)"offset"in h[l]&&d(l,h[l].offset);for(h[i].offset=h[i].offset||100,d(i,h[i].offset),l=0;i>=l;l++){var m=h[l];f.addStop(m.color,m.offset)}return f}function E(a,b,c,d,f){var g=x("linearGradient",a);return g.stops=A,g.addStop=B,g.getBBox=C,null!=b&&e(g.node,{x1:b,y1:c,x2:d,y2:f}),g}function F(a,b,c,d,f,g){var h=x("radialGradient",a);return h.stops=A,h.addStop=B,h.getBBox=C,null!=b&&e(h.node,{cx:b,cy:c,r:d}),null!=f&&null!=g&&e(h.node,{fx:f,fy:g}),h}function G(a){return function(c){if(b.stop(),c instanceof w&&1==c.node.childNodes.length&&("radialGradient"==c.node.firstChild.tagName||"linearGradient"==c.node.firstChild.tagName||"pattern"==c.node.firstChild.tagName)&&(c=c.node.firstChild,r(this).appendChild(c),c=z(c)),c instanceof u)if("radialGradient"==c.type||"linearGradient"==c.type||"pattern"==c.type){c.node.id||e(c.node,{id:c.id});var f=ib(c.node.id)}else f=c.attr(a);else if(f=d.color(c),f.error){var g=D(r(this),c);g?(g.node.id||e(g.node,{id:g.id}),f=ib(g.node.id)):f=c}else f=K(f);var h={};h[a]=f,e(this.node,h),this.node.style[a]=S}}function H(a){for(var b=[],c=a.childNodes,d=0,e=c.length;e>d;d++){var f=c[d];3==f.nodeType&&b.push(f.nodeValue),"tspan"==f.tagName&&b.push(1==f.childNodes.length&&3==f.firstChild.nodeType?f.firstChild.nodeValue:H(f))}return b}d.version="0.2.0",d.toString=function(){return"Snap v"+this.version},d._={};var I={win:a,doc:a.document};d._.glob=I;var J="hasOwnProperty",K=String,L=parseFloat,M=parseInt,N=Math,O=N.max,P=N.min,Q=N.abs,R=(N.pow,N.PI),S=(N.round,""),T=" ",U=Object.prototype.toString,V=/^\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|°|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|°|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,W=/^url\(#?([^)]+)\)$/,X=" \n \f\r   ᠎              \u2028\u2029",Y=new RegExp("[,"+X+"]+"),Z=(new RegExp("["+X+"]","g"),new RegExp("["+X+"]*,["+X+"]*")),$={hs:1,rg:1},_=new RegExp("([a-z])["+X+",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?["+X+"]*,?["+X+"]*)+)","ig"),ab=new RegExp("([rstm])["+X+",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?["+X+"]*,?["+X+"]*)+)","ig"),bb=new RegExp("(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)["+X+"]*,?["+X+"]*","ig"),cb=0,db="S"+(+new Date).toString(36),eb=function(){return db+(cb++).toString(36)},fb="http://www.w3.org/1999/xlink",gb="http://www.w3.org/2000/svg",hb={},ib=d.url=function(a){return"url('#"+a+"')"};d._.$=e,d._.id=eb,d.format=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return K(b).replace(a,function(a,b){return c(a,b,d)})}}();var jb=function(){function a(){this.parentNode.removeChild(this)}return function(b,c){var d=I.doc.createElement("img"),e=I.doc.body;d.style.cssText="position:absolute;left:-9999em;top:-9999em",d.onload=function(){c.call(d),d.onload=d.onerror=null,e.removeChild(d)},d.onerror=a,e.appendChild(d),d.src=b}}();d._.clone=h,d._.cacher=j,d.rad=l,d.deg=m,d.angle=k,d.is=f,d.snapTo=function(a,b,c){if(c=f(c,"finite")?c:10,f(a,"array")){for(var d=a.length;d--;)if(Q(a[d]-b)<=c)return a[d]}else{a=+a;var e=b%a;if(c>e)return b-e;if(e>a-c)return b-e+a}return b},function(a){function b(a){return a[0]*a[0]+a[1]*a[1]}function c(a){var c=N.sqrt(b(a));a[0]&&(a[0]/=c),a[1]&&(a[1]/=c)}a.add=function(a,b,c,d,e,f){var g,h,i,j,k=[[],[],[]],l=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],m=[[a,c,e],[b,d,f],[0,0,1]];for(a&&a instanceof n&&(m=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),g=0;3>g;g++)for(h=0;3>h;h++){for(j=0,i=0;3>i;i++)j+=l[g][i]*m[i][h];k[g][h]=j}return this.a=k[0][0],this.b=k[1][0],this.c=k[0][1],this.d=k[1][1],this.e=k[0][2],this.f=k[1][2],this},a.invert=function(){var a=this,b=a.a*a.d-a.b*a.c;return new n(a.d/b,-a.b/b,-a.c/b,a.a/b,(a.c*a.f-a.d*a.e)/b,(a.b*a.e-a.a*a.f)/b)},a.clone=function(){return new n(this.a,this.b,this.c,this.d,this.e,this.f)},a.translate=function(a,b){return this.add(1,0,0,1,a,b)},a.scale=function(a,b,c,d){return null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d),this},a.rotate=function(a,b,c){a=l(a),b=b||0,c=c||0;var d=+N.cos(a).toFixed(9),e=+N.sin(a).toFixed(9);return this.add(d,e,-e,d,b,c),this.add(1,0,0,1,-b,-c)},a.x=function(a,b){return a*this.a+b*this.c+this.e},a.y=function(a,b){return a*this.b+b*this.d+this.f},a.get=function(a){return+this[K.fromCharCode(97+a)].toFixed(4)},a.toString=function(){return"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")"},a.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},a.split=function(){var a={};a.dx=this.e,a.dy=this.f;var d=[[this.a,this.c],[this.b,this.d]];a.scalex=N.sqrt(b(d[0])),c(d[0]),a.shear=d[0][0]*d[1][0]+d[0][1]*d[1][1],d[1]=[d[1][0]-d[0][0]*a.shear,d[1][1]-d[0][1]*a.shear],a.scaley=N.sqrt(b(d[1])),c(d[1]),a.shear/=a.scaley;var e=-d[0][1],f=d[1][1];return 0>f?(a.rotate=m(N.acos(f)),0>e&&(a.rotate=360-a.rotate)):a.rotate=m(N.asin(e)),a.isSimple=!(+a.shear.toFixed(9)||a.scalex.toFixed(9)!=a.scaley.toFixed(9)&&a.rotate),a.isSuperSimple=!+a.shear.toFixed(9)&&a.scalex.toFixed(9)==a.scaley.toFixed(9)&&!a.rotate,a.noRotation=!+a.shear.toFixed(9)&&!a.rotate,a},a.toTransformString=function(a){var b=a||this.split();return b.isSimple?(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[+b.dx.toFixed(4),+b.dy.toFixed(4)]:S)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:S)+(b.rotate?"r"+[+b.rotate.toFixed(4),0,0]:S)):"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}(n.prototype),d.Matrix=n,d.getRGB=j(function(a){if(!a||(a=K(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:nb};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:nb};if(!($[J](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=kb(a)),!a)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:nb};var b,c,e,g,h,i,j=a.match(V);return j?(j[2]&&(e=M(j[2].substring(5),16),c=M(j[2].substring(3,5),16),b=M(j[2].substring(1,3),16)),j[3]&&(e=M((h=j[3].charAt(3))+h,16),c=M((h=j[3].charAt(2))+h,16),b=M((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4].split(Z),b=L(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),c=L(i[1]),"%"==i[1].slice(-1)&&(c*=2.55),e=L(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=L(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5].split(Z),b=L(i[0]),"%"==i[0].slice(-1)&&(b/=100),c=L(i[1]),"%"==i[1].slice(-1)&&(c/=100),e=L(i[2]),"%"==i[2].slice(-1)&&(e/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=L(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),d.hsb2rgb(b,c,e,g)):j[6]?(i=j[6].split(Z),b=L(i[0]),"%"==i[0].slice(-1)&&(b/=100),c=L(i[1]),"%"==i[1].slice(-1)&&(c/=100),e=L(i[2]),"%"==i[2].slice(-1)&&(e/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=L(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),d.hsl2rgb(b,c,e,g)):(b=P(N.round(b),255),c=P(N.round(c),255),e=P(N.round(e),255),g=P(O(g,0),1),j={r:b,g:c,b:e,toString:nb},j.hex="#"+(16777216|e|c<<8|b<<16).toString(16).slice(1),j.opacity=f(g,"finite")?g:1,j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:nb}},d),d.hsb=j(function(a,b,c){return d.hsb2rgb(a,b,c).hex}),d.hsl=j(function(a,b,c){return d.hsl2rgb(a,b,c).hex}),d.rgb=j(function(a,b,c,d){if(f(d,"finite")){var e=N.round;return"rgba("+[e(a),e(b),e(c),+d.toFixed(2)]+")"}return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)});var kb=function(a){var b=I.doc.getElementsByTagName("head")[0],c="rgb(255, 0, 0)";return(kb=j(function(a){if("red"==a.toLowerCase())return c;b.style.color=c,b.style.color=a;var d=I.doc.defaultView.getComputedStyle(b,S).getPropertyValue("color");return d==c?null:d}))(a)},lb=function(){return"hsb("+[this.h,this.s,this.b]+")"},mb=function(){return"hsl("+[this.h,this.s,this.l]+")"},nb=function(){return 1==this.opacity||null==this.opacity?this.hex:"rgba("+[this.r,this.g,this.b,this.opacity]+")"},ob=function(a,b,c){if(null==b&&f(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(c=a.b,b=a.g,a=a.r),null==b&&f(a,string)){var e=d.getRGB(a);a=e.r,b=e.g,c=e.b}return(a>1||b>1||c>1)&&(a/=255,b/=255,c/=255),[a,b,c]},pb=function(a,b,c,e){a=N.round(255*a),b=N.round(255*b),c=N.round(255*c);var g={r:a,g:b,b:c,opacity:f(e,"finite")?e:1,hex:d.rgb(a,b,c),toString:nb};return f(e,"finite")&&(g.opacity=e),g};d.color=function(a){var b;return f(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=d.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):f(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=d.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):(f(a,"string")&&(a=d.getRGB(a)),f(a,"object")&&"r"in a&&"g"in a&&"b"in a&&!("error"in a)?(b=d.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=d.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1,a.error=1)),a.toString=nb,a},d.hsb2rgb=function(a,b,c,d){f(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,g,h,i,j;return a=a%360/60,j=c*b,i=j*(1-Q(a%2-1)),e=g=h=c-j,a=~~a,e+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],pb(e,g,h,d)},d.hsl2rgb=function(a,b,c,d){f(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var e,g,h,i,j;return a=a%360/60,j=2*b*(.5>c?c:1-c),i=j*(1-Q(a%2-1)),e=g=h=c-j/2,a=~~a,e+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],pb(e,g,h,d)},d.rgb2hsb=function(a,b,c){c=ob(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=O(a,b,c),g=f-P(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:lb}},d.rgb2hsl=function(a,b,c){c=ob(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=O(a,b,c),h=P(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:mb}},d.parsePathString=function(a){if(!a)return null;var b=d.path(a);if(b.arr)return d.path.clone(b.arr);var c={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},e=[];return f(a,"array")&&f(a[0],"array")&&(e=d.path.clone(a)),e.length||K(a).replace(_,function(a,b,d){var f=[],g=b.toLowerCase();if(d.replace(bb,function(a,b){b&&f.push(+b)}),"m"==g&&f.length>2&&(e.push([b].concat(f.splice(0,2))),g="l",b="m"==b?"l":"L"),"o"==g&&1==f.length&&e.push([b,f[0]]),"r"==g)e.push([b].concat(f));else for(;f.length>=c[g]&&(e.push([b].concat(f.splice(0,c[g]))),c[g]););}),e.toString=d.path.toString,b.arr=d.path.clone(e),e};var qb=d.parseTransformString=function(a){if(!a)return null;var b=[];return f(a,"array")&&f(a[0],"array")&&(b=d.path.clone(a)),b.length||K(a).replace(ab,function(a,c,d){{var e=[];c.toLowerCase()}d.replace(bb,function(a,b){b&&e.push(+b)}),b.push([c].concat(e))}),b.toString=d.path.toString,b};d._.svgTransform2string=o,d._.rgTransform=new RegExp("^[a-z]["+X+"]*-?\\.?\\d","i"),d._.transform2matrix=p,d._unit2px=s;var rb=I.doc.contains||I.doc.compareDocumentPosition?function(a,b){var c=9==a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a==d||!(!d||1!=d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b;)if(b=b.parentNode,b==a)return!0;return!1};d._.getSomeDefs=r,d.select=function(a){return z(I.doc.querySelector(a))},d.selectAll=function(a){for(var b=I.doc.querySelectorAll(a),c=(d.set||Array)(),e=0;ej;j++){d=f[j],b(d,"fill"),b(d,"stroke"),b(d,"filter"),b(d,"mask"),b(d,"clip-path"),c(d);var l=e(d.node,"id");l&&(e(d.node,{id:d.id}),h.push({old:l,id:d.id}))}for(j=0,k=h.length;k>j;j++){var m=i[h[j].old];if(m)for(var n=0,o=m.length;o>n;n++)m[n](h[j].id)}}function h(a,b,c){return function(d){var e=d.slice(a,b);return 1==e.length&&(e=e[0]),c?c(e):e}}function i(a){return function(){var b=a?"<"+this.type:"",c=this.node.attributes,d=this.node.childNodes;if(a)for(var e=0,f=c.length;f>e;e++)b+=" "+c[e].name+'="'+c[e].value.replace(/"/g,'\\"')+'"';if(d.length){for(a&&(b+=">"),e=0,f=d.length;f>e;e++)3==d[e].nodeType?b+=d[e].nodeValue:1==d[e].nodeType&&(b+=z(d[e]).toString());a&&(b+="")}else a&&(b+="/>");return b}}a.attr=function(a,c){{var d=this;d.node}if(!a)return d;if(f(a,"string")){if(!(arguments.length>1))return v(b("snap.util.getattr."+a,d));var e={};e[a]=c,a=e}for(var g in a)a[J](g)&&b("snap.util.attr."+g,d,a[g]);return d},a.getBBox=function(a){var b=this;if("use"==b.type&&(b=b.original),b.removed)return{};var c=b._;return a?(c.bboxwt=d.path.get[b.type]?d.path.getBBox(b.realPath=d.path.get[b.type](b)):d._.box(b.node.getBBox()),d._.box(c.bboxwt)):(b.realPath=(d.path.get[b.type]||d.path.get.deflt)(b),c.bbox=d.path.getBBox(d.path.map(b.realPath,b.matrix)),d._.box(c.bbox))};var j=function(){return this.string};a.transform=function(a){var b=this._;if(null==a){var c=new n(this.node.getCTM()),d=q(this),f=d.toTransformString(),g=K(d)==K(this.matrix)?b.transform:f;return{string:g,globalMatrix:c,localMatrix:d,diffMatrix:c.clone().add(d.invert()),global:c.toTransformString(),local:f,toString:j}}return a instanceof n&&(a=a.toTransformString()),q(this,a),this.node&&("linearGradient"==this.type||"radialGradient"==this.type?e(this.node,{gradientTransform:this.matrix}):"pattern"==this.type?e(this.node,{patternTransform:this.matrix}):e(this.node,{transform:this.matrix})),this},a.parent=function(){return z(this.node.parentNode)},a.append=a.add=function(a){if(a){if("set"==a.type){var b=this;return a.forEach(function(a){b.add(a)}),this}a=z(a),this.node.appendChild(a.node),a.paper=this.paper}return this},a.appendTo=function(a){return a&&(a=z(a),a.append(this)),this},a.prepend=function(a){if(a){a=z(a);var b=a.parent();this.node.insertBefore(a.node,this.node.firstChild),this.add&&this.add(),a.paper=this.paper,this.parent()&&this.parent().add(),b&&b.add()}return this},a.prependTo=function(a){return a=z(a),a.prepend(this),this},a.before=function(a){if("set"==a.type){var b=this;return a.forEach(function(a){var c=a.parent();b.node.parentNode.insertBefore(a.node,b.node),c&&c.add()}),this.parent().add(),this}a=z(a);var c=a.parent();return this.node.parentNode.insertBefore(a.node,this.node),this.parent()&&this.parent().add(),c&&c.add(),a.paper=this.paper,this},a.after=function(a){a=z(a);var b=a.parent();return this.node.nextSibling?this.node.parentNode.insertBefore(a.node,this.node.nextSibling):this.node.parentNode.appendChild(a.node),this.parent()&&this.parent().add(),b&&b.add(),a.paper=this.paper,this},a.insertBefore=function(a){a=z(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},a.insertAfter=function(a){a=z(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node.nextSibling),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},a.remove=function(){var a=this.parent();return this.node.parentNode&&this.node.parentNode.removeChild(this.node),delete this.paper,this.removed=!0,a&&a.add(),this},a.select=function(a){return z(this.node.querySelector(a))},a.selectAll=function(a){for(var b=this.node.querySelectorAll(a),c=(d.set||Array)(),e=0;eb;b++)a[b].stop();return this},a.animate=function(a,d,e,g){"function"!=typeof e||e.length||(g=e,e=c.linear),a instanceof k&&(g=a.callback,e=a.easing,d=e.dur,a=a.attr);var i,j,l,m,n=[],o=[],p={},q=this;for(var r in a)if(a[J](r)){q.equal?(m=q.equal(r,K(a[r])),i=m.from,j=m.to,l=m.f):(i=+q.attr(r),j=+a[r]);var s=f(i,"array")?i.length:1;p[r]=h(n.length,n.length+s,l),n=n.concat(i),o=o.concat(j)}var t=c.time(),u=c(n,o,t,t+d,c.time,function(a){var b={};for(var c in p)p[J](c)&&(b[c]=p[c](a));q.attr(b)},e);return q.anims[u.id]=u,u._attrs=a,u._callback=g,b.once("mina.finish."+u.id,function(){delete q.anims[u.id],g&&g.call(q)}),b.once("mina.stop."+u.id,function(){delete q.anims[u.id]}),q};var l={};a.data=function(a,c){var e=l[this.id]=l[this.id]||{};if(0==arguments.length)return b("snap.data.get."+this.id,this,e,null),e;if(1==arguments.length){if(d.is(a,"object")){for(var f in a)a[J](f)&&this.data(f,a[f]);return this}return b("snap.data.get."+this.id,this,e[a],a),e[a]}return e[a]=c,b("snap.data.set."+this.id,this,c,a),this},a.removeData=function(a){return null==a?l[this.id]={}:l[this.id]&&delete l[this.id][a],this},a.outerSVG=a.toString=i(1),a.innerSVG=i()}(u.prototype),d.parse=function(a){var b=I.doc.createDocumentFragment(),c=!0,d=I.doc.createElement("div");if(a=K(a),a.match(/^\s*<\s*svg(?:\s|>)/)||(a=""+a+"",c=!1),d.innerHTML=a,a=d.getElementsByTagName("svg")[0])if(c)b=a;else for(;a.firstChild;)b.appendChild(a.firstChild);return d.innerHTML=S,new w(b)},w.prototype.select=u.prototype.select,w.prototype.selectAll=u.prototype.selectAll,d.fragment=function(){for(var a=Array.prototype.slice.call(arguments,0),b=I.doc.createDocumentFragment(),c=0,e=a.length;e>c;c++){var f=a[c];f.node&&f.node.nodeType&&b.appendChild(f.node),f.nodeType&&b.appendChild(f),"string"==typeof f&&b.appendChild(d.parse(f).node)}return new w(b)},function(a){a.el=function(a,b){return x(a,this.node).attr(b)},a.rect=function(a,b,c,d,e,g){var h;return null==g&&(g=e),f(a,"object")&&"x"in a?h=a:null!=a&&(h={x:a,y:b,width:c,height:d},null!=e&&(h.rx=e,h.ry=g)),this.el("rect",h)},a.circle=function(a,b,c){var d;return f(a,"object")&&"cx"in a?d=a:null!=a&&(d={cx:a,cy:b,r:c}),this.el("circle",d)},a.image=function(a,b,c,d,g){var h=x("image",this.node);if(f(a,"object")&&"src"in a)h.attr(a);else if(null!=a){var i={"xlink:href":a,preserveAspectRatio:"none"};null!=b&&null!=c&&(i.x=b,i.y=c),null!=d&&null!=g?(i.width=d,i.height=g):jb(a,function(){e(h.node,{width:this.offsetWidth,height:this.offsetHeight})}),e(h.node,i)}return h},a.ellipse=function(a,b,c,d){var e=x("ellipse",this.node);return f(a,"object")&&"cx"in a?e.attr(a):null!=a&&e.attr({cx:a,cy:b,rx:c,ry:d}),e},a.path=function(a){var b=x("path",this.node); +return f(a,"object")&&!f(a,"array")?b.attr(a):a&&b.attr({d:a}),b},a.group=a.g=function(b){var c=x("g",this.node);c.add=t;for(var d in a)a[J](d)&&(c[d]=a[d]);return 1==arguments.length&&b&&!b.type?c.attr(b):arguments.length&&c.add(Array.prototype.slice.call(arguments,0)),c},a.text=function(a,b,c){var d=x("text",this.node);return f(a,"object")?d.attr(a):null!=a&&d.attr({x:a,y:b,text:c||""}),d},a.line=function(a,b,c,d){var e=x("line",this.node);return f(a,"object")?e.attr(a):null!=a&&e.attr({x1:a,x2:c,y1:b,y2:d}),e},a.polyline=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b=x("polyline",this.node);return f(a,"object")&&!f(a,"array")?b.attr(a):null!=a&&b.attr({points:a}),b},a.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b=x("polygon",this.node);return f(a,"object")&&!f(a,"array")?b.attr(a):null!=a&&b.attr({points:a}),b},function(){a.gradient=function(a){return D(this.defs,a)},a.gradientLinear=function(a,b,c,d){return E(this.defs,a,b,c,d)},a.gradientRadial=function(a,b,c,d,e){return F(this.defs,a,b,c,d,e)},a.toString=function(){var a,b=I.doc.createDocumentFragment(),c=I.doc.createElement("div"),d=this.node.cloneNode(!0);return b.appendChild(c),c.appendChild(d),e(d,{xmlns:gb}),a=c.innerHTML,b.removeChild(b.firstChild),a},a.clear=function(){for(var a,b=this.node.firstChild;b;)a=b.nextSibling,"defs"!=b.tagName&&b.parentNode.removeChild(b),b=a}}()}(y.prototype),d.ajax=function(a,c,d,e){var g=new XMLHttpRequest,h=eb();if(g){if(f(c,"function"))e=d,d=c,c=null;else if(f(c,"object")){var i=[];for(var j in c)c.hasOwnProperty(j)&&i.push(encodeURIComponent(j)+"="+encodeURIComponent(c[j]));c=i.join("&")}return g.open(c?"POST":"GET",a,!0),g.setRequestHeader("X-Requested-With","XMLHttpRequest"),c&&g.setRequestHeader("Content-type","application/x-www-form-urlencoded"),d&&(b.once("snap.ajax."+h+".0",d),b.once("snap.ajax."+h+".200",d),b.once("snap.ajax."+h+".304",d)),g.onreadystatechange=function(){4==g.readyState&&b("snap.ajax."+h+"."+g.status,e,g)},4==g.readyState?g:(g.send(c),g)}},d.load=function(a,b,c){d.ajax(a,function(a){var e=d.parse(a.responseText);c?b.call(c,e):b(e)})},b.on("snap.util.attr.mask",function(a){if(a instanceof u||a instanceof w){if(b.stop(),a instanceof w&&1==a.node.childNodes.length&&(a=a.node.firstChild,r(this).appendChild(a),a=z(a)),"mask"==a.type)var c=a;else c=x("mask",r(this)),c.node.appendChild(a.node),!c.node.id&&e(c.node,{id:c.id});e(this.node,{mask:ib(c.id)})}}),function(a){b.on("snap.util.attr.clip",a),b.on("snap.util.attr.clip-path",a),b.on("snap.util.attr.clipPath",a)}(function(a){if(a instanceof u||a instanceof w){if(b.stop(),"clipPath"==a.type)var c=a;else c=x("clipPath",r(this)),c.node.appendChild(a.node),!c.node.id&&e(c.node,{id:c.id});e(this.node,{"clip-path":ib(c.id)})}}),b.on("snap.util.attr.fill",G("fill")),b.on("snap.util.attr.stroke",G("stroke"));var sb=/^([lr])(?:\(([^)]*)\))?(.*)$/i;b.on("snap.util.grad.parse",function(a){a=K(a);var b=a.match(sb);if(!b)return null;var c=b[1],d=b[2],e=b[3];return d=d.split(/\s*,\s*/).map(function(a){return+a==a?+a:a}),1==d.length&&0==d[0]&&(d=[]),e=e.split("-"),e=e.map(function(a){a=a.split(":");var b={color:a[0]};return a[1]&&(b.offset=a[1]),b}),{type:c,params:d,stops:e}}),b.on("snap.util.attr.d",function(a){b.stop(),f(a,"array")&&f(a[0],"array")&&(a=d.path.toString.call(a)),a=K(a),a.match(/[ruo]/i)&&(a=d.path.toAbsolute(a)),e(this.node,{d:a})})(-1),b.on("snap.util.attr.#text",function(a){b.stop(),a=K(a);for(var c=I.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(c)})(-1),b.on("snap.util.attr.path",function(a){b.stop(),this.attr({d:a})})(-1),b.on("snap.util.attr.viewBox",function(a){var c;c=f(a,"object")&&"x"in a?[a.x,a.y,a.width,a.height].join(" "):f(a,"array")?a.join(" "):a,e(this.node,{viewBox:c}),b.stop()})(-1),b.on("snap.util.attr.transform",function(a){this.transform(a),b.stop()})(-1),b.on("snap.util.attr.r",function(a){"rect"==this.type&&(b.stop(),e(this.node,{rx:a,ry:a}))})(-1),b.on("snap.util.attr.textpath",function(a){if(b.stop(),"text"==this.type){var c,d,g;if(!a&&this.textPath){for(d=this.textPath;d.node.firstChild;)this.node.appendChild(d.node.firstChild);return d.remove(),void delete this.textPath}if(f(a,"string")){var h=r(this),i=z(h.parentNode).path(a);h.appendChild(i.node),c=i.id,i.attr({id:c})}else a=z(a),a instanceof u&&(c=a.attr("id"),c||(c=a.id,a.attr({id:c})));if(c)if(d=this.textPath,g=this.node,d)d.attr({"xlink:href":"#"+c});else{for(d=e("textPath",{"xlink:href":"#"+c});g.firstChild;)d.appendChild(g.firstChild);g.appendChild(d),this.textPath=z(d)}}})(-1),b.on("snap.util.attr.text",function(a){if("text"==this.type){for(var c=this.node,d=function(a){var b=e("tspan");if(f(a,"array"))for(var c=0;cr;r++){if(l=a[r],"M"==l[0])j=+l[1],k=+l[2];else{if(m=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6]),q+m>f){if(d&&!p.start){if(n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q),o+=["C"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)],h)return o;p.start=o,o=["M"+e(n.x),e(n.y)+"C"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(l[5]),e(l[6])].join(),q+=m,j=+l[5],k=+l[6];continue}if(!c&&!d)return n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q)}q+=m,j=+l[5],k=+l[6]}o+=l.shift()+l}return p.end=o,n=c?q:d?p:i(j,k,l[0],l[1],l[2],l[3],l[4],l[5],1)},null,a._.clone)}function i(a,b,c,d,e,f,g,h,i){var j=1-i,k=S(j,3),l=S(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*O.atan2(q-s,r-t)/P;return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function j(b,c,e,f,g,h,i,j){a.is(b,"array")||(b=[b,c,e,f,g,h,i,j]);var k=E.apply(null,b);return d(k.min.x,k.min.y,k.max.x-k.min.x,k.max.y-k.min.y)}function k(a,b,c){return b>=a.x&&b<=a.x+a.width&&c>=a.y&&c<=a.y+a.height}function l(a,b){return a=d(a),b=d(b),k(b,a.x,a.y)||k(b,a.x2,a.y)||k(b,a.x,a.y2)||k(b,a.x2,a.y2)||k(a,b.x,b.y)||k(a,b.x2,b.y)||k(a,b.x,b.y2)||k(a,b.x2,b.y2)||(a.xb.x||b.xa.x)&&(a.yb.y||b.ya.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*O.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(R(a,c)R(e,g)||R(b,d)R(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+Q(a,c).toFixed(2)||n>+R(a,c).toFixed(2)||n<+Q(e,g).toFixed(2)||n>+R(e,g).toFixed(2)||o<+Q(b,d).toFixed(2)||o>+R(b,d).toFixed(2)||o<+Q(f,h).toFixed(2)||o>+R(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/5),k=~~(g/5),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=T(w.x-v.x)<.001?"y":"x",A=T(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+T((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+T((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=F(a),b=F(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return K(b.bbox);if(!a)return d();a=F(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=E(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=Q.apply(0,h),n=Q.apply(0,i),o=R.apply(0,h),p=R.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=K(q),q}function w(a,b,c,d,f){if(f)return[["M",a+f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(J(b,"array")&&J(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+(o[6]+i),n[7]=+(o[7]+j);break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(H(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(H(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=k,j=l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*P/180,n=P/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*O.cos(c)-b*O.sin(c),e=a*O.sin(c)+b*O.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(O.cos(P/180*f),O.sin(P/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=O.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*O.sqrt(T((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=O.asin(((c-x)/e).toFixed(9)),z=O.asin(((j-x)/e).toFixed(9));y=w>b?P-y:y,z=w>i?P-z:z,0>y&&(y=2*P+y),0>z&&(z=2*P+z),h&&y>z&&(y-=2*P),!h&&z>y&&(z-=2*P)}var A=z-y;if(T(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*O.cos(z),j=x+e*O.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=O.cos(y),G=O.sin(y),H=O.cos(z),I=O.sin(z),J=O.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],N=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(N[0]=2*M[0]-N[0],N[1]=2*M[1]-N[1],k)return[N,Q,R].concat(o);o=[N,Q,R].concat(o).join().split(",");for(var S=[],U=0,V=o.length;V>U;U++)S[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return S}function D(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:S(j,3)*a+3*S(j,2)*i*c+3*j*i*i*e+S(i,3)*g,y:S(j,3)*b+3*S(j,2)*i*d+3*j*i*i*f+S(i,3)*h}}function E(a,b,c,d,e,f,g,h){var i,j=e-2*c+a-(g-2*e+c),k=2*(c-a)-2*(e-c),l=a-c,m=(-k+O.sqrt(k*k-4*j*l))/2/j,n=(-k-O.sqrt(k*k-4*j*l))/2/j,o=[b,h],p=[a,g];return T(m)>"1e12"&&(m=.5),T(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=D(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=D(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),j=f-2*d+b-(h-2*f+d),k=2*(d-b)-2*(f-d),l=b-d,m=(-k+O.sqrt(k*k-4*j*l))/2/j,n=(-k-O.sqrt(k*k-4*j*l))/2/j,T(m)>"1e12"&&(m=.5),T(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=D(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=D(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),{min:{x:Q.apply(0,p),y:Q.apply(0,o)},max:{x:R.apply(0,p),y:R.apply(0,o)}}}function F(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d].concat(a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),n=R(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],n=R(e.length,g&&g.length||0))},m=0,n=R(e.length,g&&g.length||0);n>m;m++){e[m]=j(e[m],h),k(e,m),g&&(g[m]=j(g[m],i)),g&&k(g,m),l(e,g,h,i,m),l(g,e,i,h,m);var o=e[m],p=g&&g[m],q=o.length,r=g&&p.length;h.x=o[q-2],h.y=o[q-1],h.bx=N(o[q-4])||h.x,h.by=N(o[q-3])||h.y,i.bx=g&&(N(p[r-4])||i.x),i.by=g&&(N(p[r-3])||i.y),i.x=g&&p[r-2],i.y=g&&p[r-1]}return g||(d.curve=f(e)),g?[e,g]:e}function G(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=F(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function H(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var I=b.prototype,J=a.is,K=a._.clone,L="hasOwnProperty",M=/,?([a-z]),?/gi,N=parseFloat,O=Math,P=O.PI,Q=O.min,R=O.max,S=O.pow,T=O.abs,U=h(1),V=h(),W=h(0,1),X=a._unit2px,Y={path:function(a){return a.attr("path")},circle:function(a){var b=X(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=X(a);return x(b.cx,b.cy,b.rx,b.ry)},rect:function(a){var b=X(a);return w(b.x,b.y,b.width,b.height,b.rx,b.ry)},image:function(a){var b=X(a);return w(b.x,b.y,b.width,b.height)},text:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)},g:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)},symbol:function(a){var b=a.getBBox();return w(b.x,b.y,b.width,b.height)},line:function(a){return"M"+[a.attr("x1"),a.attr("y1"),a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},svg:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=U,a.path.getPointAtLength=V,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return W(a,b).end;var d=W(a,c,1);return b?W(d,b).end:d},I.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},I.getPointAtLength=function(a){return V(this.attr("d"),a)},I.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=Y,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=F,a.path.map=G,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var b=Math.max,c=Math.min,d=function(a){if(this.items=[],this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},e=d.prototype;e.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},e.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},e.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},e.remove=function(){for(;this.length;)this.pop().remove();return this},e.attr=function(a){for(var b=0,c=this.items.length;c>b;b++)this.items[b].attr(a);return this},e.clear=function(){for(;this.length;)this.pop()},e.splice=function(a,e){a=0>a?b(this.length+a,0):a,e=b(0,c(this.length-a,e));var f,g=[],h=[],i=[];for(f=2;ff;f++)h.push(this[a+f]);for(;ff?i[f]:g[f-j];for(f=this.items.length=this.length-=e-j;this[f];)delete this[f++];return new d(h)},e.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},e.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},e.getBBox=function(){for(var a=[],d=[],e=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),d.push(h.y),e.push(h.x+h.width),f.push(h.y+h.height)}return a=c.apply(0,a),d=c.apply(0,d),e=b.apply(0,e),f=b.apply(0,f),{x:a,y:d,x2:e,y2:f,width:e-a,height:f-d,cx:a+(e-a)/2,cy:d+(f-d)/2}},e.clone=function(a){a=new d;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},e.toString=function(){return"Snap‘s set"},e.type="set",a.set=function(){var a=new d;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,b){function c(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function d(b,d,e){d=l(d).replace(/\.{3}|…/g,b),b=a.parseTransformString(b)||[],d=a.parseTransformString(d)||[];for(var f,g,j,k,m=Math.max(b.length,d.length),n=[],o=[],p=0;m>p;p++){if(j=b[p]||c(d[p]),k=d[p]||c(j),j[0]!=k[0]||"r"==j[0].toLowerCase()&&(j[2]!=k[2]||j[3]!=k[3])||"s"==j[0].toLowerCase()&&(j[3]!=k[3]||j[4]!=k[4])){b=a._.transform2matrix(b,e()),d=a._.transform2matrix(d,e()),n=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],o=[["m",d.a,d.b,d.c,d.d,d.e,d.f]];break}for(n[p]=[],o[p]=[],f=0,g=Math.max(j.length,k.length);g>f;f++)f in j&&(n[p][f]=j[f]),f in k&&(o[p][f]=k[f])}return{from:i(n),to:i(o),f:h(n)}}function e(a){return a}function f(a){return function(b){return+b.toFixed(3)+a}}function g(b){return a.rgb(b[0],b[1],b[2])}function h(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function i(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}var j={},k=/[a-z]+$/i,l=String;j.stroke=j.fill="colour",b.prototype.equal=function(b,c){var m,n,o=l(this.attr(b)||""),p=this;if(o==+o&&c==+c)return{from:+o,to:+c,f:e};if("colour"==j[b])return m=a.color(o),n=a.color(c),{from:[m.r,m.g,m.b,m.opacity],to:[n.r,n.g,n.b,n.opacity],f:g};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),d(o,c,function(){return p.getBBox(1)});if("d"==b||"path"==b)return m=a.path.toCubic(o,c),{from:i(m[0]),to:i(m[1]),f:h(m[0])};if("points"==b)return m=l(o).split(","),n=l(c).split(","),{from:m,to:n,f:function(a){return a}};var q=o.match(k),r=l(c).match(k);return q&&q==r?{from:parseFloat(o),to:parseFloat(c),f:f(q)}:{from:this.asPX(b),to:this.asPX(b,c),f:e}}}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=function(a){var b="y"==a?"scrollTop":"scrollLeft";return e.doc.documentElement[b]||e.doc.body[b]},l=function(){this.returnValue=!1},m=function(){return this.originalEvent.preventDefault()},n=function(){this.cancelBubble=!0},o=function(){return this.originalEvent.stopPropagation()},p=function(){return e.doc.addEventListener?function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y"),i=k("x");if(h&&j[g](b))for(var l=0,n=e.targetTouches&&e.targetTouches.length;n>l;l++)if(e.targetTouches[l].target==a||a.contains(e.targetTouches[l].target)){var p=e;e=e.targetTouches[l],e.originalEvent=p,e.preventDefault=m,e.stopPropagation=o;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}}:e.doc.attachEvent?function(a,b,c,d){var f=function(a){a=a||e.win.event;var b=k("y"),f=k("x"),g=a.clientX+f,h=a.clientY+b;return a.preventDefault=a.preventDefault||l,a.stopPropagation=a.stopPropagation||n,c.call(d,a,g,h)};a.attachEvent("on"+b,f);var g=function(){return a.detachEvent("on"+b,f),!0};return g}:void 0}(),q=[],r=function(c){for(var d,e=c.clientX,f=c.clientY,g=k("y"),i=k("x"),j=q.length;j--;){if(d=q[j],h){for(var l,m=c.touches&&c.touches.length;m--;)if(l=c.touches[m],l.identifier==d.el._drag.id||d.el.node.contains(l.target)){e=l.clientX,f=l.clientY,(c.originalEvent?c.originalEvent:c).preventDefault();break}}else c.preventDefault();{var n=d.el.node;a._.glob,n.nextSibling,n.parentNode,n.style.display}e+=i,f+=g,b("snap.drag.move."+d.el.id,d.move_scope||d.el,e-d.el._drag.x,f-d.el._drag.y,e,f,c)}},s=function(c){a.unmousemove(r).unmouseup(s);for(var d,e=q.length;e--;)d=q[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c);q=[]},t=i.length;t--;)!function(b){a[b]=f[b]=function(c,d){return a.is(c,"function")&&(this.events=this.events||[],this.events.push({name:b,f:c,unbind:p(this.shape||this.node||e.doc,b,c,d||this)})),this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[t]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var u=[];f.drag=function(c,d,e,f,g,h){function i(i,j,k){(i.originalEvent||i).preventDefault(),this._drag.x=j,this._drag.y=k,this._drag.id=i.identifier,!q.length&&a.mousemove(r).mouseup(s),q.push({el:this,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+this.id,d),c&&b.on("snap.drag.move."+this.id,c),e&&b.on("snap.drag.end."+this.id,e),b("snap.drag.start."+this.id,g||f||this,j,k,i)}if(!arguments.length){var j;return this.drag(function(a,b){this.attr({transform:j+(j?"T":"t")+[a,b]})},function(){j=this.transform().local})}return this._drag={},u.push({el:this,start:i}),this.mousedown(i),this},f.undrag=function(){for(var c=u.length;c--;)u[c].el==this&&(this.unmousedown(u[c].start),u.splice(c,1),b.unbind("snap.drag.*."+this.id));return!u.length&&a.unmousemove(r).unmouseup(s),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format('',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e){return e=e||"#000",null==d&&(d=4),"string"==typeof d&&(e=d,d=4),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format('',{color:e,dx:b,dy:c,blur:d})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format('',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)}) +},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format('',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format('',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format('',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format('',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d}); \ No newline at end of file diff --git a/demos/snap-ad/src/js/vendor/snap.svg.js b/demos/snap-ad/src/js/vendor/snap.svg.js new file mode 100755 index 0000000..6c9065a --- /dev/null +++ b/demos/snap-ad/src/js/vendor/snap.svg.js @@ -0,0 +1,6925 @@ +// 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(["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 instantiates. It's a part of SVG with which ordinary web developers may be least familiar. + /*\ + * Element.use + [ method ] + ** + * Creates a `` element linked to the current element + ** + = (Element) the `` element + \*/ + elproto.use = function () { + var use, + id = this.node.id; + if (!id) { + id = this.id; + $(this.node, { + id: id + }); + } + if (this.type == "linearGradient" || this.type == "radialGradient" || + this.type == "pattern") { + use = make(this.type, this.node.parentNode); + } else { + use = make("use", this.node.parentNode); + } + $(use.node, { + "xlink:href": "#" + id + }); + use.original = this; + return use; + }; + /*\ + * Element.clone + [ method ] + ** + * Creates a clone of the element and inserts it after the element + ** + = (Element) the clone + \*/ + function fixids(el) { + var els = el.selectAll("*"), + it, + url = /^\s*url\(("|'|)(.*)\1\)\s*$/, + ids = [], + uses = {}; + function urltest(it, name) { + var val = $(it.node, name); + val = val && val.match(url); + val = val && val[2]; + if (val && val.charAt() == "#") { + val = val.substring(1); + } else { + return; + } + if (val) { + uses[val] = (uses[val] || []).concat(function (id) { + var attr = {}; + attr[name] = URL(id); + $(it.node, attr); + }); + } + } + function linktest(it) { + var val = $(it.node, "xlink:href"); + if (val && val.charAt() == "#") { + val = val.substring(1); + } else { + return; + } + if (val) { + uses[val] = (uses[val] || []).concat(function (id) { + it.attr("xlink:href", "#" + id); + }); + } + } + for (var i = 0, ii = els.length; i < ii; i++) { + it = els[i]; + urltest(it, "fill"); + urltest(it, "stroke"); + urltest(it, "filter"); + urltest(it, "mask"); + urltest(it, "clip-path"); + linktest(it); + var oldid = $(it.node, "id"); + if (oldid) { + $(it.node, {id: it.id}); + ids.push({ + old: oldid, + id: it.id + }); + } + } + for (i = 0, ii = ids.length; i < ii; i++) { + var fs = uses[ids[i].old]; + if (fs) { + for (var j = 0, jj = fs.length; j < jj; j++) { + fs[j](ids[i].id); + } + } + } + } + elproto.clone = function () { + var clone = wrap(this.node.cloneNode(true)); + if ($(clone.node, "id")) { + $(clone.node, {id: clone.id}); + } + fixids(clone); + clone.insertAfter(this); + return clone; + }; +// SIERRA Element.toDefs(): If this _moves_ an element to the region, why is the return value a _clone_? Also unclear why it's called the _relative_ section. Perhaps _shared_? + /*\ + * Element.toDefs + [ method ] + ** + * Moves element to the shared `` area + ** + = (Element) the clone + \*/ + elproto.toDefs = function () { + var defs = getSomeDefs(this); + defs.appendChild(this.node); + return this; + }; +// SIERRA Element.pattern(): x/y/width/height data types are listed as both String and Number. Is that an error, or does it mean strings are coerced? +// SIERRA Element.pattern(): clarify that x/y are offsets that e.g., may add gutters between the tiles. + /*\ + * Element.pattern + [ method ] + ** + * Creates a `` element from the current element + ** + * To create a pattern you have to specify the pattern rect: + - x (string|number) + - y (string|number) + - width (string|number) + - height (string|number) + = (Element) the `` element + * You can use pattern later on as an argument for `fill` attribute: + | var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({ + | fill: "none", + | stroke: "#bada55", + | strokeWidth: 5 + | }).pattern(0, 0, 10, 10), + | c = paper.circle(200, 200, 100); + | c.attr({ + | fill: p + | }); + \*/ + elproto.pattern = function (x, y, width, height) { + var p = make("pattern", getSomeDefs(this)); + if (x == null) { + x = this.getBBox(); + } + if (is(x, "object") && "x" in x) { + y = x.y; + width = x.width; + height = x.height; + x = x.x; + } + $(p.node, { + x: x, + y: y, + width: width, + height: height, + patternUnits: "userSpaceOnUse", + id: p.id, + viewBox: [x, y, width, height].join(" ") + }); + p.node.appendChild(this.node); + return p; + }; +// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path. +// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values? + /*\ + * Element.marker + [ method ] + ** + * Creates a `` element from the current element + ** + * To create a marker you have to specify the bounding rect and reference point: + - x (number) + - y (number) + - width (number) + - height (number) + - refX (number) + - refY (number) + = (Element) the `` element + * You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end. + \*/ + // TODO add usage for markers + elproto.marker = function (x, y, width, height, refX, refY) { + var p = make("marker", getSomeDefs(this)); + if (x == null) { + x = this.getBBox(); + } + if (is(x, "object") && "x" in x) { + y = x.y; + width = x.width; + height = x.height; + refX = x.refX || x.cx; + refY = x.refY || x.cy; + x = x.x; + } + $(p.node, { + viewBox: [x, y, width, height].join(S), + markerWidth: width, + markerHeight: height, + orient: "auto", + refX: refX || 0, + refY: refY || 0, + id: p.id + }); + p.node.appendChild(this.node); + return p; + }; + // animation + function slice(from, to, f) { + return function (arr) { + var res = arr.slice(from, to); + if (res.length == 1) { + res = res[0]; + } + return f ? f(res) : res; + }; + } + var Animation = function (attr, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + this.attr = attr; + this.dur = ms; + easing && (this.easing = easing); + callback && (this.callback = callback); + }; + // SIERRA All object methods should feature sample code. This is just one instance. + /*\ + * Snap.animation + [ method ] + ** + * Creates an animation object + ** + - attr (object) attributes of final destination + - duration (number) duration of the animation, in milliseconds + - easing (function) #optional one of easing functions of @mina or custom one + - callback (function) #optional callback function that fires when animation ends + = (object) animation object + \*/ + Snap.animation = function (attr, ms, easing, callback) { + return new Animation(attr, ms, easing, callback); + }; + /*\ + * Element.inAnim + [ method ] + ** + * Returns a set of animations that may be able to manipulate the current element + ** + = (object) in format: + o { + o anim (object) animation object, + o curStatus (number) 0..1 — status of the animation: 0 — just started, 1 — just finished, + o status (function) gets or sets the status of the animation, + o stop (function) stops the animation + o } + \*/ + elproto.inAnim = function () { + var el = this, + res = []; + for (var id in el.anims) if (el.anims[has](id)) { + (function (a) { + res.push({ + anim: new Animation(a._attrs, a.dur, a.easing, a._callback), + curStatus: a.status(), + status: function (val) { + return a.status(val); + }, + stop: function () { + a.stop(); + } + }); + }(el.anims[id])); + } + return res; + }; + /*\ + * Snap.animate + [ method ] + ** + * Runs generic animation of one number into another with a caring function + ** + - from (number|array) number or array of numbers + - to (number|array) number or array of numbers + - setter (function) caring function that accepts one number argument + - duration (number) duration, in milliseconds + - easing (function) #optional easing function from @mina or custom + - callback (function) #optional callback function to execute when animation ends + = (object) animation object in @mina format + o { + o id (string) animation id, consider it read-only, + o duration (function) gets or sets the duration of the animation, + o easing (function) easing, + o speed (function) gets or sets the speed of the animation, + o status (function) gets or sets the status of the animation, + o stop (function) stops the animation + o } + | var rect = Snap().rect(0, 0, 10, 10); + | Snap.animate(0, 10, function (val) { + | rect.attr({ + | x: val + | }); + | }, 1000); + | // in given context is equivalent to + | rect.animate({x: 10}, 1000); + \*/ + Snap.animate = function (from, to, setter, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + var now = mina.time(), + anim = mina(from, to, now, now + ms, mina.time, setter, easing); + callback && eve.once("mina.finish." + anim.id, callback); + return anim; + }; + /*\ + * Element.stop + [ method ] + ** + * Stops all the animations for the current element + ** + = (Element) the current element + \*/ + elproto.stop = function () { + var anims = this.inAnim(); + for (var i = 0, ii = anims.length; i < ii; i++) { + anims[i].stop(); + } + return this; + }; + // SIERRA Element.animate(): For _attrs_, clarify if they represent the destination values, and if the animation executes relative to the element's current attribute values. + // SIERRA would a _custom_ animation function be an SVG keySplines value? + /*\ + * Element.animate + [ method ] + ** + * Animates the given attributes of the element + ** + - attrs (object) key-value pairs of destination attributes + - duration (number) duration of the animation in milliseconds + - easing (function) #optional easing function from @mina or custom + - callback (function) #optional callback function that executes when the animation ends + = (Element) the current element + \*/ + elproto.animate = function (attrs, ms, easing, callback) { + if (typeof easing == "function" && !easing.length) { + callback = easing; + easing = mina.linear; + } + if (attrs instanceof Animation) { + callback = attrs.callback; + easing = attrs.easing; + ms = easing.dur; + attrs = attrs.attr; + } + var fkeys = [], tkeys = [], keys = {}, from, to, f, eq, + el = this; + for (var key in attrs) if (attrs[has](key)) { + if (el.equal) { + eq = el.equal(key, Str(attrs[key])); + from = eq.from; + to = eq.to; + f = eq.f; + } else { + from = +el.attr(key); + to = +attrs[key]; + } + var len = is(from, "array") ? from.length : 1; + keys[key] = slice(fkeys.length, fkeys.length + len, f); + fkeys = fkeys.concat(from); + tkeys = tkeys.concat(to); + } + var now = mina.time(), + anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) { + var attr = {}; + for (var key in keys) if (keys[has](key)) { + attr[key] = keys[key](val); + } + el.attr(attr); + }, easing); + el.anims[anim.id] = anim; + anim._attrs = attrs; + anim._callback = callback; + eve.once("mina.finish." + anim.id, function () { + delete el.anims[anim.id]; + callback && callback.call(el); + }); + eve.once("mina.stop." + anim.id, function () { + delete el.anims[anim.id]; + }); + return el; + }; + var eldata = {}; + /*\ + * Element.data + [ method ] + ** + * Adds or retrieves given value associated with given key. (Don’t confuse + * with `data-` attributes) + * + * See also @Element.removeData + - key (string) key to store data + - value (any) #optional value to store + = (object) @Element + * or, if value is not specified: + = (any) value + > Usage + | for (var i = 0, i < 5, i++) { + | paper.circle(10 + 15 * i, 10, 10) + | .attr({fill: "#000"}) + | .data("i", i) + | .click(function () { + | alert(this.data("i")); + | }); + | } + \*/ + elproto.data = function (key, value) { + var data = eldata[this.id] = eldata[this.id] || {}; + if (arguments.length == 0){ + eve("snap.data.get." + this.id, this, data, null); + return data; + } + if (arguments.length == 1) { + if (Snap.is(key, "object")) { + for (var i in key) if (key[has](i)) { + this.data(i, key[i]); + } + return this; + } + eve("snap.data.get." + this.id, this, data[key], key); + return data[key]; + } + data[key] = value; + eve("snap.data.set." + this.id, this, value, key); + return this; + }; + /*\ + * Element.removeData + [ method ] + ** + * Removes value associated with an element by given key. + * If key is not provided, removes all the data of the element. + - key (string) #optional key + = (object) @Element + \*/ + elproto.removeData = function (key) { + if (key == null) { + eldata[this.id] = {}; + } else { + eldata[this.id] && delete eldata[this.id][key]; + } + return this; + }; + /*\ + * Element.outerSVG + [ method ] + ** + * Returns SVG code for the element, equivalent to HTML's `outerHTML`. + * + * See also @Element.innerSVG + = (string) SVG code for the element + \*/ + /*\ + * Element.toString + [ method ] + ** + * See @Element.outerSVG + \*/ + elproto.outerSVG = elproto.toString = toString(1); + /*\ + * Element.innerSVG + [ method ] + ** + * Returns SVG code for the element's contents, equivalent to HTML's `innerHTML` + = (string) SVG code for the element + \*/ + elproto.innerSVG = toString(); + function toString(type) { + return function () { + var res = type ? "<" + this.type : "", + attr = this.node.attributes, + chld = this.node.childNodes; + if (type) { + for (var i = 0, ii = attr.length; i < ii; i++) { + res += " " + attr[i].name + '="' + + attr[i].value.replace(/"/g, '\\"') + '"'; + } + } + if (chld.length) { + type && (res += ">"); + for (i = 0, ii = chld.length; i < ii; i++) { + if (chld[i].nodeType == 3) { + res += chld[i].nodeValue; + } else if (chld[i].nodeType == 1) { + res += wrap(chld[i]).toString(); + } + } + type && (res += ""); + } else { + type && (res += "/>"); + } + return res; + }; + } +}(Element.prototype)); +// SIERRA Snap.parse() accepts & returns a fragment, but there's no info on what it does in between. What if it doesn't parse? +/*\ + * Snap.parse + [ method ] + ** + * Parses SVG fragment and converts it into a @Fragment + ** + - svg (string) SVG string + = (Fragment) the @Fragment +\*/ +Snap.parse = function (svg) { + var f = glob.doc.createDocumentFragment(), + full = true, + div = glob.doc.createElement("div"); + svg = Str(svg); + if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) { + svg = "" + svg + ""; + full = false; + } + div.innerHTML = svg; + svg = div.getElementsByTagName("svg")[0]; + if (svg) { + if (full) { + f = svg; + } else { + while (svg.firstChild) { + f.appendChild(svg.firstChild); + } + } + } + div.innerHTML = E; + return new Fragment(f); +}; +function Fragment(frag) { + this.node = frag; +} +/*\ + * Fragment.select + [ method ] + ** + * See @Element.select +\*/ +Fragment.prototype.select = Element.prototype.select; +/*\ + * Fragment.selectAll + [ method ] + ** + * See @Element.selectAll +\*/ +Fragment.prototype.selectAll = Element.prototype.selectAll; +// SIERRA Snap.fragment() could especially use a code example +/*\ + * Snap.fragment + [ method ] + ** + * Creates a DOM fragment from a given list of elements or strings + ** + - varargs (…) SVG string + = (Fragment) the @Fragment +\*/ +Snap.fragment = function () { + var args = Array.prototype.slice.call(arguments, 0), + f = glob.doc.createDocumentFragment(); + for (var i = 0, ii = args.length; i < ii; i++) { + var item = args[i]; + if (item.node && item.node.nodeType) { + f.appendChild(item.node); + } + if (item.nodeType) { + f.appendChild(item); + } + if (typeof item == "string") { + f.appendChild(Snap.parse(item).node); + } + } + return new Fragment(f); +}; + +function make(name, parent) { + var res = $(name); + parent.appendChild(res); + var el = wrap(res); + el.type = name; + return el; +} +function Paper(w, h) { + var res, + desc, + defs, + proto = Paper.prototype; + if (w && w.tagName == "svg") { + if (w.snap in hub) { + return hub[w.snap]; + } + res = new Element(w); + desc = w.getElementsByTagName("desc")[0]; + defs = w.getElementsByTagName("defs")[0]; + if (!desc) { + desc = $("desc"); + desc.appendChild(glob.doc.createTextNode("Created with Snap")); + res.node.appendChild(desc); + } + if (!defs) { + defs = $("defs"); + res.node.appendChild(defs); + } + res.defs = defs; + for (var key in proto) if (proto[has](key)) { + res[key] = proto[key]; + } + res.paper = res.root = res; + } else { + res = make("svg", glob.doc.body); + $(res.node, { + height: h, + version: 1.1, + width: w, + xmlns: xmlns + }); + } + return res; +} +function wrap(dom) { + if (!dom) { + return dom; + } + if (dom instanceof Element || dom instanceof Fragment) { + return dom; + } + if (dom.tagName == "svg") { + return new Paper(dom); + } + return new Element(dom); +} +// gradients' helpers +function Gstops() { + return this.selectAll("stop"); +} +function GaddStop(color, offset) { + var stop = $("stop"), + attr = { + offset: +offset + "%" + }; + color = Snap.color(color); + attr["stop-color"] = color.hex; + if (color.opacity < 1) { + attr["stop-opacity"] = color.opacity; + } + $(stop, attr); + this.node.appendChild(stop); + return this; +} +function GgetBBox() { + if (this.type == "linearGradient") { + var x1 = $(this.node, "x1") || 0, + x2 = $(this.node, "x2") || 1, + y1 = $(this.node, "y1") || 0, + y2 = $(this.node, "y2") || 0; + return Snap._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1)); + } else { + var cx = this.node.cx || .5, + cy = this.node.cy || .5, + r = this.node.r || 0; + return Snap._.box(cx - r, cy - r, r * 2, r * 2); + } +} +function gradient(defs, str) { + var grad = arrayFirstValue(eve("snap.util.grad.parse", null, str)), + el; + if (!grad) { + return null; + } + grad.params.unshift(defs); + if (grad.type.toLowerCase() == "l") { + el = gradientLinear.apply(0, grad.params); + } else { + el = gradientRadial.apply(0, grad.params); + } + if (grad.type != grad.type.toLowerCase()) { + $(el.node, { + gradientUnits: "userSpaceOnUse" + }); + } + var stops = grad.stops, + len = stops.length, + start = 0, + j = 0; + function seed(i, end) { + var step = (end - start) / (i - j); + for (var k = j; k < i; k++) { + stops[k].offset = +(+start + step * (k - j)).toFixed(2); + } + j = i; + start = end; + } + len--; + for (var i = 0; i < len; i++) if ("offset" in stops[i]) { + seed(i, stops[i].offset); + } + stops[len].offset = stops[len].offset || 100; + seed(len, stops[len].offset); + for (i = 0; i <= len; i++) { + var stop = stops[i]; + el.addStop(stop.color, stop.offset); + } + return el; +} +function gradientLinear(defs, x1, y1, x2, y2) { + var el = make("linearGradient", defs); + el.stops = Gstops; + el.addStop = GaddStop; + el.getBBox = GgetBBox; + if (x1 != null) { + $(el.node, { + x1: x1, + y1: y1, + x2: x2, + y2: y2 + }); + } + return el; +} +function gradientRadial(defs, cx, cy, r, fx, fy) { + var el = make("radialGradient", defs); + el.stops = Gstops; + el.addStop = GaddStop; + el.getBBox = GgetBBox; + if (cx != null) { + $(el.node, { + cx: cx, + cy: cy, + r: r + }); + } + if (fx != null && fy != null) { + $(el.node, { + fx: fx, + fy: fy + }); + } + return el; +} +// Paper prototype methods +(function (proto) { + /*\ + * Paper.el + [ method ] + ** + * Creates an element on paper with a given name and no attributes + ** + - name (string) tag name + - attr (object) attributes + = (Element) the current element + > Usage + | var c = paper.circle(10, 10, 10); // is the same as... + | var c = paper.el("circle").attr({ + | cx: 10, + | cy: 10, + | r: 10 + | }); + | // and the same as + | var c = paper.el("circle", { + | cx: 10, + | cy: 10, + | r: 10 + | }); + \*/ + proto.el = function (name, attr) { + return make(name, this.node).attr(attr); + }; + /*\ + * Paper.rect + [ method ] + * + * Draws a rectangle + ** + - x (number) x coordinate of the top left corner + - y (number) y coordinate of the top left corner + - width (number) width + - height (number) height + - rx (number) #optional horizontal radius for rounded corners, default is 0 + - ry (number) #optional vertical radius for rounded corners, default is rx or 0 + = (object) the `rect` element + ** + > Usage + | // regular rectangle + | var c = paper.rect(10, 10, 50, 50); + | // rectangle with rounded corners + | var c = paper.rect(40, 40, 50, 50, 10); + \*/ + proto.rect = function (x, y, w, h, rx, ry) { + var attr; + if (ry == null) { + ry = rx; + } + if (is(x, "object") && "x" in x) { + attr = x; + } else if (x != null) { + attr = { + x: x, + y: y, + width: w, + height: h + }; + if (rx != null) { + attr.rx = rx; + attr.ry = ry; + } + } + return this.el("rect", attr); + }; + /*\ + * Paper.circle + [ method ] + ** + * Draws a circle + ** + - x (number) x coordinate of the centre + - y (number) y coordinate of the centre + - r (number) radius + = (object) the `circle` element + ** + > Usage + | var c = paper.circle(50, 50, 40); + \*/ + proto.circle = function (cx, cy, r) { + var attr; + if (is(cx, "object") && "cx" in cx) { + attr = cx; + } else if (cx != null) { + attr = { + cx: cx, + cy: cy, + r: r + }; + } + return this.el("circle", attr); + }; + + /*\ + * Paper.image + [ method ] + ** + * Places an image on the surface + ** + - src (string) URI of the source image + - x (number) x offset position + - y (number) y offset position + - width (number) width of the image + - height (number) height of the image + = (object) the `image` element + * or + = (object) Snap element object with type `image` + ** + > Usage + | var c = paper.image("apple.png", 10, 10, 80, 80); + \*/ + proto.image = function (src, x, y, width, height) { + var el = make("image", this.node); + if (is(src, "object") && "src" in src) { + el.attr(src); + } else if (src != null) { + var set = { + "xlink:href": src, + preserveAspectRatio: "none" + }; + if (x != null && y != null) { + set.x = x; + set.y = y; + } + if (width != null && height != null) { + set.width = width; + set.height = height; + } else { + preload(src, function () { + $(el.node, { + width: this.offsetWidth, + height: this.offsetHeight + }); + }); + } + $(el.node, set); + } + return el; + }; + /*\ + * Paper.ellipse + [ method ] + ** + * Draws an ellipse + ** + - x (number) x coordinate of the centre + - y (number) y coordinate of the centre + - rx (number) horizontal radius + - ry (number) vertical radius + = (object) the `ellipse` element + ** + > Usage + | var c = paper.ellipse(50, 50, 40, 20); + \*/ + proto.ellipse = function (cx, cy, rx, ry) { + var el = make("ellipse", this.node); + if (is(cx, "object") && "cx" in cx) { + el.attr(cx); + } else if (cx != null) { + el.attr({ + cx: cx, + cy: cy, + rx: rx, + ry: ry + }); + } + return el; + }; + // SIERRA Paper.path(): Unclear from the link what a Catmull-Rom curveto is, and why it would make life any easier. + /*\ + * Paper.path + [ method ] + ** + * Creates a `` element using the given string as the path's definition + - pathString (string) #optional path string in SVG format + * Path string consists of one-letter commands, followed by comma seprarated arguments in numerical form. Example: + | "M10,20L30,40" + * This example features two commands: `M`, with arguments `(10, 20)` and `L` with arguments `(30, 40)`. Uppercase letter commands express coordinates in absolute terms, while lowercase commands express them in relative terms from the most recently declared coordinates. + * + #

Here is short list of commands available, for more details see SVG path string format or article about path strings at MDN.

+ # + # + # + # + # + # + # + # + # + # + # + #
CommandNameParameters
Mmoveto(x y)+
Zclosepath(none)
Llineto(x y)+
Hhorizontal linetox+
Vvertical linetoy+
Ccurveto(x1 y1 x2 y2 x y)+
Ssmooth curveto(x2 y2 x y)+
Qquadratic Bézier curveto(x1 y1 x y)+
Tsmooth quadratic Bézier curveto(x y)+
Aelliptical arc(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
RCatmull-Rom curveto*x1 y1 (x y)+
+ * * _Catmull-Rom curveto_ is a not standard SVG command and added to make life easier. + * Note: there is a special case when a path consists of only three commands: `M10,10R…z`. In this case the path connects back to its starting point. + > Usage + | var c = paper.path("M10 10L90 90"); + | // draw a diagonal line: + | // move to 10,10, line to 90,90 + \*/ + proto.path = function (d) { + var el = make("path", this.node); + if (is(d, "object") && !is(d, "array")) { + el.attr(d); + } else if (d) { + el.attr({ + d: d + }); + } + return el; + }; +// SIERRA Paper.g(): Don't understand the code comment about the order being _different._ Wouldn't it be a rect followed by a circle? + /*\ + * Paper.g + [ method ] + ** + * Creates a group element + ** + - varargs (…) #optional elements to nest within the group + = (object) the `g` element + ** + > Usage + | var c1 = paper.circle(), + | c2 = paper.rect(), + | g = paper.g(c2, c1); // note that the order of elements is different + * or + | var c1 = paper.circle(), + | c2 = paper.rect(), + | g = paper.g(); + | g.add(c2, c1); + \*/ + /*\ + * Paper.group + [ method ] + ** + * See @Paper.g + \*/ + proto.group = proto.g = function (first) { + var el = make("g", this.node); + el.add = add2group; + for (var method in proto) if (proto[has](method)) { + el[method] = proto[method]; + } + if (arguments.length == 1 && first && !first.type) { + el.attr(first); + } else if (arguments.length) { + el.add(Array.prototype.slice.call(arguments, 0)); + } + return el; + }; + /*\ + * Paper.text + [ method ] + ** + * Draws a text string + ** + - x (number) x coordinate position + - y (number) y coordinate position + - text (string|array) The text string to draw or array of strings to nest within separate `` elements + = (object) the `text` element + ** + > Usage + | var t1 = paper.text(50, 50, "Snap"); + | var t2 = paper.text(50, 50, ["S","n","a","p"]); + | // Text path usage + | t1.attr({textpath: "M10,10L100,100"}); + | // or + | var pth = paper.path("M10,10L100,100"); + | t1.attr({textpath: pth}); + \*/ + proto.text = function (x, y, text) { + var el = make("text", this.node); + if (is(x, "object")) { + el.attr(x); + } else if (x != null) { + el.attr({ + x: x, + y: y, + text: text || "" + }); + } + return el; + }; + /*\ + * Paper.line + [ method ] + ** + * Draws a line + ** + - x1 (number) x coordinate position of the start + - y1 (number) y coordinate position of the start + - x2 (number) x coordinate position of the end + - y2 (number) y coordinate position of the end + = (object) the `line` element + ** + > Usage + | var t1 = paper.line(50, 50, 100, 100); + \*/ + proto.line = function (x1, y1, x2, y2) { + var el = make("line", this.node); + if (is(x1, "object")) { + el.attr(x1); + } else if (x1 != null) { + el.attr({ + x1: x1, + x2: x2, + y1: y1, + y2: y2 + }); + } + return el; + }; + /*\ + * Paper.polyline + [ method ] + ** + * Draws a polyline + ** + - points (array) array of points + * or + - varargs (…) points + = (object) the `polyline` element + ** + > Usage + | var p1 = paper.polyline([10, 10, 100, 100]); + | var p2 = paper.polyline(10, 10, 100, 100); + \*/ + proto.polyline = function (points) { + if (arguments.length > 1) { + points = Array.prototype.slice.call(arguments, 0); + } + var el = make("polyline", this.node); + if (is(points, "object") && !is(points, "array")) { + el.attr(points); + } else if (points != null) { + el.attr({ + points: points + }); + } + return el; + }; + /*\ + * Paper.polygon + [ method ] + ** + * Draws a polygon. See @Paper.polyline + \*/ + proto.polygon = function (points) { + if (arguments.length > 1) { + points = Array.prototype.slice.call(arguments, 0); + } + var el = make("polygon", this.node); + if (is(points, "object") && !is(points, "array")) { + el.attr(points); + } else if (points != null) { + el.attr({ + points: points + }); + } + return el; + }; + // gradients + (function () { + /*\ + * Paper.gradient + [ method ] + ** + * Creates a gradient element + ** + - gradient (string) gradient descriptor + > Gradient Descriptor + * The gradient descriptor is an expression formatted as + * follows: `()`. The `` can be + * either linear or radial. The uppercase `L` or `R` letters + * indicate absolute coordinates offset from the SVG surface. + * Lowercase `l` or `r` letters indicate coordinates + * calculated relative to the element to which the gradient is + * applied. Coordinates specify a linear gradient vector as + * `x1`, `y1`, `x2`, `y2`, or a radial gradient as `cx`, `cy`, + * `r` and optional `fx`, `fy` specifying a focal point away + * from the center of the circle. Specify `` as a list + * of dash-separated CSS color values. Each color may be + * followed by a custom offset value, separated with a colon + * character. + > Examples + * Linear gradient, relative from top-left corner to bottom-right + * corner, from black through red to white: + | var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff"); + * Linear gradient, absolute from (0, 0) to (100, 100), from black + * through red at 25% to white: + | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25%-#fff"); + * Radial gradient, relative from the center of the element with radius + * half the width, from black to white: + | var g = paper.gradient("r(0.5, 0.5, 0.5)#000-#fff"); + * To apply the gradient: + | paper.circle(50, 50, 40).attr({ + | fill: g + | }); + = (object) the `gradient` element + \*/ + proto.gradient = function (str) { + return gradient(this.defs, str); + }; + proto.gradientLinear = function (x1, y1, x2, y2) { + return gradientLinear(this.defs, x1, y1, x2, y2); + }; + proto.gradientRadial = function (cx, cy, r, fx, fy) { + return gradientRadial(this.defs, cx, cy, r, fx, fy); + }; + /*\ + * Paper.toString + [ method ] + ** + * Returns SVG code for the @Paper + = (string) SVG code for the @Paper + \*/ + proto.toString = function () { + var f = glob.doc.createDocumentFragment(), + d = glob.doc.createElement("div"), + svg = this.node.cloneNode(true), + res; + f.appendChild(d); + d.appendChild(svg); + $(svg, {xmlns: xmlns}); + res = d.innerHTML; + f.removeChild(f.firstChild); + return res; + }; + /*\ + * Paper.clear + [ method ] + ** + * Removes all child nodes of the paper, except . + \*/ + proto.clear = function () { + var node = this.node.firstChild, + next; + while (node) { + next = node.nextSibling; + if (node.tagName != "defs") { + node.parentNode.removeChild(node); + } + node = next; + } + }; + }()); +}(Paper.prototype)); + +// simple ajax +/*\ + * Snap.ajax + [ method ] + ** + * Simple implementation of Ajax + ** + - url (string) URL + - postData (object|string) data for post request + - callback (function) callback + - scope (object) #optional scope of callback + * or + - url (string) URL + - callback (function) callback + - scope (object) #optional scope of callback + = (XMLHttpRequest) the XMLHttpRequest object, just in case +\*/ +Snap.ajax = function (url, postData, callback, scope){ + var req = new XMLHttpRequest, + id = ID(); + if (req) { + if (is(postData, "function")) { + scope = callback; + callback = postData; + postData = null; + } else if (is(postData, "object")) { + var pd = []; + for (var key in postData) if (postData.hasOwnProperty(key)) { + pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key])); + } + postData = pd.join("&"); + } + req.open((postData ? "POST" : "GET"), url, true); + req.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + if (postData) { + req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + } + if (callback) { + eve.once("snap.ajax." + id + ".0", callback); + eve.once("snap.ajax." + id + ".200", callback); + eve.once("snap.ajax." + id + ".304", callback); + } + req.onreadystatechange = function() { + if (req.readyState != 4) return; + eve("snap.ajax." + id + "." + req.status, scope, req); + }; + if (req.readyState == 4) { + return req; + } + req.send(postData); + return req; + } +}; +/*\ + * Snap.load + [ method ] + ** + * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX) + ** + - url (string) URL + - callback (function) callback + - scope (object) #optional scope of callback +\*/ +Snap.load = function (url, callback, scope) { + Snap.ajax(url, function (req) { + var f = Snap.parse(req.responseText); + scope ? callback.call(scope, f) : callback(f); + }); +}; + +// Attributes event handlers +eve.on("snap.util.attr.mask", function (value) { + if (value instanceof Element || value instanceof Fragment) { + eve.stop(); + if (value instanceof Fragment && value.node.childNodes.length == 1) { + value = value.node.firstChild; + getSomeDefs(this).appendChild(value); + value = wrap(value); + } + if (value.type == "mask") { + var mask = value; + } else { + mask = make("mask", getSomeDefs(this)); + mask.node.appendChild(value.node); + !mask.node.id && $(mask.node, { + id: mask.id + }); + } + $(this.node, { + mask: URL(mask.id) + }); + } +}); +(function (clipIt) { + eve.on("snap.util.attr.clip", clipIt); + eve.on("snap.util.attr.clip-path", clipIt); + eve.on("snap.util.attr.clipPath", clipIt); +}(function (value) { + if (value instanceof Element || value instanceof Fragment) { + eve.stop(); + if (value.type == "clipPath") { + var clip = value; + } else { + clip = make("clipPath", getSomeDefs(this)); + clip.node.appendChild(value.node); + !clip.node.id && $(clip.node, { + id: clip.id + }); + } + $(this.node, { + "clip-path": URL(clip.id) + }); + } +})); +function fillStroke(name) { + return function (value) { + eve.stop(); + if (value instanceof Fragment && value.node.childNodes.length == 1 && + (value.node.firstChild.tagName == "radialGradient" || + value.node.firstChild.tagName == "linearGradient" || + value.node.firstChild.tagName == "pattern")) { + value = value.node.firstChild; + getSomeDefs(this).appendChild(value); + value = wrap(value); + } + if (value instanceof Element) { + if (value.type == "radialGradient" || value.type == "linearGradient" + || value.type == "pattern") { + if (!value.node.id) { + $(value.node, { + id: value.id + }); + } + var fill = URL(value.node.id); + } else { + fill = value.attr(name); + } + } else { + fill = Snap.color(value); + if (fill.error) { + var grad = gradient(getSomeDefs(this), value); + if (grad) { + if (!grad.node.id) { + $(grad.node, { + id: grad.id + }); + } + fill = URL(grad.node.id); + } else { + fill = value; + } + } else { + fill = Str(fill); + } + } + var attrs = {}; + attrs[name] = fill; + $(this.node, attrs); + this.node.style[name] = E; + }; +} +eve.on("snap.util.attr.fill", fillStroke("fill")); +eve.on("snap.util.attr.stroke", fillStroke("stroke")); +var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i; +eve.on("snap.util.grad.parse", function parseGrad(string) { + string = Str(string); + var tokens = string.match(gradrg); + if (!tokens) { + return null; + } + var type = tokens[1], + params = tokens[2], + stops = tokens[3]; + params = params.split(/\s*,\s*/).map(function (el) { + return +el == el ? +el : el; + }); + if (params.length == 1 && params[0] == 0) { + params = []; + } + stops = stops.split("-"); + stops = stops.map(function (el) { + el = el.split(":"); + var out = { + color: el[0] + }; + if (el[1]) { + out.offset = el[1]; + } + return out; + }); + return { + type: type, + params: params, + stops: stops + }; +}); + +eve.on("snap.util.attr.d", function (value) { + eve.stop(); + if (is(value, "array") && is(value[0], "array")) { + value = Snap.path.toString.call(value); + } + value = Str(value); + if (value.match(/[ruo]/i)) { + value = Snap.path.toAbsolute(value); + } + $(this.node, {d: value}); +})(-1); +eve.on("snap.util.attr.#text", function (value) { + eve.stop(); + value = Str(value); + var txt = glob.doc.createTextNode(value); + while (this.node.firstChild) { + this.node.removeChild(this.node.firstChild); + } + this.node.appendChild(txt); +})(-1); +eve.on("snap.util.attr.path", function (value) { + eve.stop(); + this.attr({d: value}); +})(-1); +eve.on("snap.util.attr.viewBox", function (value) { + var vb; + if (is(value, "object") && "x" in value) { + vb = [value.x, value.y, value.width, value.height].join(" "); + } else if (is(value, "array")) { + vb = value.join(" "); + } else { + vb = value; + } + $(this.node, { + viewBox: vb + }); + eve.stop(); +})(-1); +eve.on("snap.util.attr.transform", function (value) { + this.transform(value); + eve.stop(); +})(-1); +eve.on("snap.util.attr.r", function (value) { + if (this.type == "rect") { + eve.stop(); + $(this.node, { + rx: value, + ry: value + }); + } +})(-1); +eve.on("snap.util.attr.textpath", function (value) { + eve.stop(); + if (this.type == "text") { + var id, tp, node; + if (!value && this.textPath) { + tp = this.textPath; + while (tp.node.firstChild) { + this.node.appendChild(tp.node.firstChild); + } + tp.remove(); + delete this.textPath; + return; + } + if (is(value, "string")) { + var defs = getSomeDefs(this), + path = wrap(defs.parentNode).path(value); + defs.appendChild(path.node); + id = path.id; + path.attr({id: id}); + } else { + value = wrap(value); + if (value instanceof Element) { + id = value.attr("id"); + if (!id) { + id = value.id; + value.attr({id: id}); + } + } + } + if (id) { + tp = this.textPath; + node = this.node; + if (tp) { + tp.attr({"xlink:href": "#" + id}); + } else { + tp = $("textPath", { + "xlink:href": "#" + id + }); + while (node.firstChild) { + tp.appendChild(node.firstChild); + } + node.appendChild(tp); + this.textPath = wrap(tp); + } + } + } +})(-1); +eve.on("snap.util.attr.text", function (value) { + if (this.type == "text") { + var i = 0, + node = this.node, + tuner = function (chunk) { + var out = $("tspan"); + if (is(chunk, "array")) { + for (var i = 0; i < chunk.length; i++) { + out.appendChild(tuner(chunk[i])); + } + } else { + out.appendChild(glob.doc.createTextNode(chunk)); + } + out.normalize && out.normalize(); + return out; + }; + while (node.firstChild) { + node.removeChild(node.firstChild); + } + var tuned = tuner(value); + while (tuned.firstChild) { + node.appendChild(tuned.firstChild); + } + } + eve.stop(); +})(-1); +// default +var cssAttr = { + "alignment-baseline": 0, + "baseline-shift": 0, + "clip": 0, + "clip-path": 0, + "clip-rule": 0, + "color": 0, + "color-interpolation": 0, + "color-interpolation-filters": 0, + "color-profile": 0, + "color-rendering": 0, + "cursor": 0, + "direction": 0, + "display": 0, + "dominant-baseline": 0, + "enable-background": 0, + "fill": 0, + "fill-opacity": 0, + "fill-rule": 0, + "filter": 0, + "flood-color": 0, + "flood-opacity": 0, + "font": 0, + "font-family": 0, + "font-size": 0, + "font-size-adjust": 0, + "font-stretch": 0, + "font-style": 0, + "font-variant": 0, + "font-weight": 0, + "glyph-orientation-horizontal": 0, + "glyph-orientation-vertical": 0, + "image-rendering": 0, + "kerning": 0, + "letter-spacing": 0, + "lighting-color": 0, + "marker": 0, + "marker-end": 0, + "marker-mid": 0, + "marker-start": 0, + "mask": 0, + "opacity": 0, + "overflow": 0, + "pointer-events": 0, + "shape-rendering": 0, + "stop-color": 0, + "stop-opacity": 0, + "stroke": 0, + "stroke-dasharray": 0, + "stroke-dashoffset": 0, + "stroke-linecap": 0, + "stroke-linejoin": 0, + "stroke-miterlimit": 0, + "stroke-opacity": 0, + "stroke-width": 0, + "text-anchor": 0, + "text-decoration": 0, + "text-rendering": 0, + "unicode-bidi": 0, + "visibility": 0, + "word-spacing": 0, + "writing-mode": 0 +}; + +eve.on("snap.util.attr", function (value) { + var att = eve.nt(), + attr = {}; + att = att.substring(att.lastIndexOf(".") + 1); + attr[att] = value; + var style = att.replace(/-(\w)/gi, function (all, letter) { + return letter.toUpperCase(); + }), + css = att.replace(/[A-Z]/g, function (letter) { + return "-" + letter.toLowerCase(); + }); + if (cssAttr[has](css)) { + this.node.style[style] = value == null ? E : value; + } else { + $(this.node, attr); + } +}); +eve.on("snap.util.getattr.transform", function () { + eve.stop(); + return this.transform(); +})(-1); +eve.on("snap.util.getattr.textpath", function () { + eve.stop(); + return this.textPath; +})(-1); +// Markers +(function () { + function getter(end) { + return function () { + eve.stop(); + var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end); + if (style == "none") { + return style; + } else { + return Snap(glob.doc.getElementById(style.match(reURLValue)[1])); + } + }; + } + function setter(end) { + return function (value) { + eve.stop(); + var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1); + if (value == "" || !value) { + this.node.style[name] = "none"; + return; + } + if (value.type == "marker") { + var id = value.node.id; + if (!id) { + $(value.node, {id: value.id}); + } + this.node.style[name] = URL(id); + return; + } + }; + } + eve.on("snap.util.getattr.marker-end", getter("end"))(-1); + eve.on("snap.util.getattr.markerEnd", getter("end"))(-1); + eve.on("snap.util.getattr.marker-start", getter("start"))(-1); + eve.on("snap.util.getattr.markerStart", getter("start"))(-1); + eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1); + eve.on("snap.util.getattr.markerMid", getter("mid"))(-1); + eve.on("snap.util.attr.marker-end", setter("end"))(-1); + eve.on("snap.util.attr.markerEnd", setter("end"))(-1); + eve.on("snap.util.attr.marker-start", setter("start"))(-1); + eve.on("snap.util.attr.markerStart", setter("start"))(-1); + eve.on("snap.util.attr.marker-mid", setter("mid"))(-1); + eve.on("snap.util.attr.markerMid", setter("mid"))(-1); +}()); +eve.on("snap.util.getattr.r", function () { + if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) { + eve.stop(); + return $(this.node, "rx"); + } +})(-1); +function textExtract(node) { + var out = []; + var children = node.childNodes; + for (var i = 0, ii = children.length; i < ii; i++) { + var chi = children[i]; + if (chi.nodeType == 3) { + out.push(chi.nodeValue); + } + if (chi.tagName == "tspan") { + if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) { + out.push(chi.firstChild.nodeValue); + } else { + out.push(textExtract(chi)); + } + } + } + return out; +} +eve.on("snap.util.getattr.text", function () { + if (this.type == "text" || this.type == "tspan") { + eve.stop(); + var out = textExtract(this.node); + return out.length == 1 ? out[0] : out; + } +})(-1); +eve.on("snap.util.getattr.#text", function () { + return this.node.textContent; +})(-1); +eve.on("snap.util.getattr.viewBox", function () { + eve.stop(); + var vb = $(this.node, "viewBox").split(separator); + return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]); + // TODO: investigate why I need to z-index it +})(-1); +eve.on("snap.util.getattr.points", function () { + var p = $(this.node, "points"); + eve.stop(); + return p.split(separator); +}); +eve.on("snap.util.getattr.path", function () { + var p = $(this.node, "d"); + eve.stop(); + return p; +}); +// default +eve.on("snap.util.getattr", function () { + var att = eve.nt(); + att = att.substring(att.lastIndexOf(".") + 1); + var css = att.replace(/[A-Z]/g, function (letter) { + return "-" + letter.toLowerCase(); + }); + if (cssAttr[has](css)) { + return glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue(css); + } else { + return $(this.node, att); + } +}); +var getOffset = function (elem) { + var box = elem.getBoundingClientRect(), + doc = elem.ownerDocument, + body = doc.body, + docElem = doc.documentElement, + clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, + top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, + left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; + return { + y: top, + x: left + }; +}; +/*\ + * Snap.getElementByPoint + [ method ] + ** + * Returns you topmost element under given point. + ** + = (object) Snap element object + - x (number) x coordinate from the top left corner of the window + - y (number) y coordinate from the top left corner of the window + > Usage + | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"}); +\*/ +Snap.getElementByPoint = function (x, y) { + var paper = this, + svg = paper.canvas, + target = glob.doc.elementFromPoint(x, y); + if (glob.win.opera && target.tagName == "svg") { + var so = getOffset(target), + sr = target.createSVGRect(); + sr.x = x - so.x; + sr.y = y - so.y; + sr.width = sr.height = 1; + var hits = target.getIntersectionList(sr, null); + if (hits.length) { + target = hits[hits.length - 1]; + } + } + if (!target) { + return null; + } + return wrap(target); +}; +/*\ + * Snap.plugin + [ method ] + ** + * Let you write plugins. You pass in a function with four arguments, like this: + | Snap.plugin(function (Snap, Element, Paper, global) { + | Snap.newmethod = function () {}; + | Element.prototype.newmethod = function () {}; + | Paper.prototype.newmethod = function () {}; + | }); + * Inside the function you have access to all main objects (and their + * prototypes). This allow you to extend anything you want. + ** + - f (function) your plugin body +\*/ +Snap.plugin = function (f) { + f(Snap, Element, Paper, glob); +}; +glob.win.Snap = Snap; +return Snap; +}()); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + is = Snap.is, + clone = Snap._.clone, + has = "hasOwnProperty", + p2s = /,?([a-z]),?/gi, + toFloat = parseFloat, + math = Math, + PI = math.PI, + mmin = math.min, + mmax = math.max, + pow = math.pow, + abs = math.abs; + function paths(ps) { + var p = paths.ps = paths.ps || {}; + if (p[ps]) { + p[ps].sleep = 100; + } else { + p[ps] = { + sleep: 100 + }; + } + setTimeout(function () { + for (var key in p) if (p[has](key) && key != ps) { + p[key].sleep--; + !p[key].sleep && delete p[key]; + } + }); + return p[ps]; + } + function box(x, y, width, height) { + if (x == null) { + x = y = width = height = 0; + } + if (y == null) { + y = x.y; + width = x.width; + height = x.height; + x = x.x; + } + return { + x: x, + y: y, + width: width, + w: width, + height: height, + h: height, + x2: x + width, + y2: y + height, + cx: x + width / 2, + cy: y + height / 2, + r1: math.min(width, height) / 2, + r2: math.max(width, height) / 2, + r0: math.sqrt(width * width + height * height) / 2, + path: rectPath(x, y, width, height), + vb: [x, y, width, height].join(" ") + }; + } + function toString() { + return this.join(",").replace(p2s, "$1"); + } + function pathClone(pathArray) { + var res = clone(pathArray); + res.toString = toString; + return res; + } + function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { + if (length == null) { + return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); + } else { + return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, + getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length)); + } + } + function getLengthFactory(istotal, subpath) { + function O(val) { + return +(+val).toFixed(3); + } + return Snap._.cacher(function (path, length, onlystart) { + if (path instanceof Element) { + path = path.attr("d"); + } + path = path2curve(path); + var x, y, p, l, sp = "", subpaths = {}, point, + len = 0; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = +p[1]; + y = +p[2]; + } else { + l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + if (len + l > length) { + if (subpath && !subpaths.start) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + sp += [ + "C" + O(point.start.x), + O(point.start.y), + O(point.m.x), + O(point.m.y), + O(point.x), + O(point.y) + ]; + if (onlystart) {return sp;} + subpaths.start = sp; + sp = [ + "M" + O(point.x), + O(point.y) + "C" + O(point.n.x), + O(point.n.y), + O(point.end.x), + O(point.end.y), + O(p[5]), + O(p[6]) + ].join(); + len += l; + x = +p[5]; + y = +p[6]; + continue; + } + if (!istotal && !subpath) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + return point; + } + } + len += l; + x = +p[5]; + y = +p[6]; + } + sp += p.shift() + p; + } + subpaths.end = sp; + point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); + return point; + }, null, Snap._.clone); + } + var getTotalLength = getLengthFactory(1), + getPointAtLength = getLengthFactory(), + getSubpathsAtLength = getLengthFactory(0, 1); + function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t, + t13 = pow(t1, 3), + t12 = pow(t1, 2), + t2 = t * t, + t3 = t2 * t, + x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, + y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, + mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), + ax = t1 * p1x + t * c1x, + ay = t1 * p1y + t * c1y, + cx = t1 * c2x + t * p2x, + cy = t1 * c2y + t * p2y, + alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI); + // (mx > nx || my < ny) && (alpha += 180); + return { + x: x, + y: y, + m: {x: mx, y: my}, + n: {x: nx, y: ny}, + start: {x: ax, y: ay}, + end: {x: cx, y: cy}, + alpha: alpha + }; + } + function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + if (!Snap.is(p1x, "array")) { + p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y]; + } + var bbox = curveDim.apply(null, p1x); + return box( + bbox.min.x, + bbox.min.y, + bbox.max.x - bbox.min.x, + bbox.max.y - bbox.min.y + ); + } + function isPointInsideBBox(bbox, x, y) { + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + function isBBoxIntersect(bbox1, bbox2) { + bbox1 = box(bbox1); + bbox2 = box(bbox2); + return isPointInsideBBox(bbox2, bbox1.x, bbox1.y) + || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y) + || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2) + || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2) + || isPointInsideBBox(bbox1, bbox2.x, bbox2.y) + || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y) + || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2) + || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2) + || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x + || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) + && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y + || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); + } + function base3(t, p1, p2, p3, p4) { + var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, + t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; + return t * t2 - 3 * p1 + 3 * p2; + } + function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { + if (z == null) { + z = 1; + } + z = z > 1 ? 1 : z < 0 ? 0 : z; + var z2 = z / 2, + n = 12, + Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], + Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472], + sum = 0; + for (var i = 0; i < n; i++) { + var ct = z2 * Tvalues[i] + z2, + xbase = base3(ct, x1, x2, x3, x4), + ybase = base3(ct, y1, y2, y3, y4), + comb = xbase * xbase + ybase * ybase; + sum += Cvalues[i] * math.sqrt(comb); + } + return z2 * sum; + } + function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) { + if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) { + return; + } + var t = 1, + step = t / 2, + t2 = t - step, + l, + e = .01; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + while (abs(l - ll) > e) { + step /= 2; + t2 += (l < ll ? 1 : -1) * step; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + } + return t2; + } + function intersect(x1, y1, x2, y2, x3, y3, x4, y4) { + if ( + mmax(x1, x2) < mmin(x3, x4) || + mmin(x1, x2) > mmax(x3, x4) || + mmax(y1, y2) < mmin(y3, y4) || + mmin(y1, y2) > mmax(y3, y4) + ) { + return; + } + var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), + ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), + denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + + if (!denominator) { + return; + } + var px = nx / denominator, + py = ny / denominator, + px2 = +px.toFixed(2), + py2 = +py.toFixed(2); + if ( + px2 < +mmin(x1, x2).toFixed(2) || + px2 > +mmax(x1, x2).toFixed(2) || + px2 < +mmin(x3, x4).toFixed(2) || + px2 > +mmax(x3, x4).toFixed(2) || + py2 < +mmin(y1, y2).toFixed(2) || + py2 > +mmax(y1, y2).toFixed(2) || + py2 < +mmin(y3, y4).toFixed(2) || + py2 > +mmax(y3, y4).toFixed(2) + ) { + return; + } + return {x: px, y: py}; + } + function inter(bez1, bez2) { + return interHelper(bez1, bez2); + } + function interCount(bez1, bez2) { + return interHelper(bez1, bez2, 1); + } + function interHelper(bez1, bez2, justCount) { + var bbox1 = bezierBBox(bez1), + bbox2 = bezierBBox(bez2); + if (!isBBoxIntersect(bbox1, bbox2)) { + return justCount ? 0 : []; + } + var l1 = bezlen.apply(0, bez1), + l2 = bezlen.apply(0, bez2), + n1 = ~~(l1 / 5), + n2 = ~~(l2 / 5), + dots1 = [], + dots2 = [], + xy = {}, + res = justCount ? 0 : []; + for (var i = 0; i < n1 + 1; i++) { + var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); + dots1.push({x: p.x, y: p.y, t: i / n1}); + } + for (i = 0; i < n2 + 1; i++) { + p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); + dots2.push({x: p.x, y: p.y, t: i / n2}); + } + for (i = 0; i < n1; i++) { + for (var j = 0; j < n2; j++) { + var di = dots1[i], + di1 = dots1[i + 1], + dj = dots2[j], + dj1 = dots2[j + 1], + ci = abs(di1.x - di.x) < .001 ? "y" : "x", + cj = abs(dj1.x - dj.x) < .001 ? "y" : "x", + is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y); + if (is) { + if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) { + continue; + } + xy[is.x.toFixed(4)] = is.y.toFixed(4); + var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), + t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); + if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { + if (justCount) { + res++; + } else { + res.push({ + x: is.x, + y: is.y, + t1: t1, + t2: t2 + }); + } + } + } + } + } + return res; + } + function pathIntersection(path1, path2) { + return interPathHelper(path1, path2); + } + function pathIntersectionNumber(path1, path2) { + return interPathHelper(path1, path2, 1); + } + function interPathHelper(path1, path2, justCount) { + path1 = path2curve(path1); + path2 = path2curve(path2); + var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, + res = justCount ? 0 : []; + for (var i = 0, ii = path1.length; i < ii; i++) { + var pi = path1[i]; + if (pi[0] == "M") { + x1 = x1m = pi[1]; + y1 = y1m = pi[2]; + } else { + if (pi[0] == "C") { + bez1 = [x1, y1].concat(pi.slice(1)); + x1 = bez1[6]; + y1 = bez1[7]; + } else { + bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; + x1 = x1m; + y1 = y1m; + } + for (var j = 0, jj = path2.length; j < jj; j++) { + var pj = path2[j]; + if (pj[0] == "M") { + x2 = x2m = pj[1]; + y2 = y2m = pj[2]; + } else { + if (pj[0] == "C") { + bez2 = [x2, y2].concat(pj.slice(1)); + x2 = bez2[6]; + y2 = bez2[7]; + } else { + bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; + x2 = x2m; + y2 = y2m; + } + var intr = interHelper(bez1, bez2, justCount); + if (justCount) { + res += intr; + } else { + for (var k = 0, kk = intr.length; k < kk; k++) { + intr[k].segment1 = i; + intr[k].segment2 = j; + intr[k].bez1 = bez1; + intr[k].bez2 = bez2; + } + res = res.concat(intr); + } + } + } + } + } + return res; + } + function isPointInsidePath(path, x, y) { + var bbox = pathBBox(path); + return isPointInsideBBox(bbox, x, y) && + interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1; + } + function pathBBox(path) { + var pth = paths(path); + if (pth.bbox) { + return clone(pth.bbox); + } + if (!path) { + return box(); + } + path = path2curve(path); + var x = 0, + y = 0, + X = [], + Y = [], + p; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = p[1]; + y = p[2]; + X.push(x); + Y.push(y); + } else { + var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + X = X.concat(dim.min.x, dim.max.x); + Y = Y.concat(dim.min.y, dim.max.y); + x = p[5]; + y = p[6]; + } + } + var xmin = mmin.apply(0, X), + ymin = mmin.apply(0, Y), + xmax = mmax.apply(0, X), + ymax = mmax.apply(0, Y), + bb = box(xmin, ymin, xmax - xmin, ymax - ymin); + pth.bbox = clone(bb); + return bb; + } + function rectPath(x, y, w, h, r) { + if (r) { + return [ + ["M", x + r, y], + ["l", w - r * 2, 0], + ["a", r, r, 0, 0, 1, r, r], + ["l", 0, h - r * 2], + ["a", r, r, 0, 0, 1, -r, r], + ["l", r * 2 - w, 0], + ["a", r, r, 0, 0, 1, -r, -r], + ["l", 0, r * 2 - h], + ["a", r, r, 0, 0, 1, r, -r], + ["z"] + ]; + } + var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; + res.toString = toString; + return res; + } + function ellipsePath(x, y, rx, ry, a) { + if (a == null && ry == null) { + ry = rx; + } + if (a != null) { + var rad = Math.PI / 180, + x1 = x + rx * Math.cos(-ry * rad), + x2 = x + rx * Math.cos(-a * rad), + y1 = y + rx * Math.sin(-ry * rad), + y2 = y + rx * Math.sin(-a * rad), + res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]]; + } else { + res = [ + ["M", x, y], + ["m", 0, -ry], + ["a", rx, ry, 0, 1, 1, 0, 2 * ry], + ["a", rx, ry, 0, 1, 1, 0, -2 * ry], + ["z"] + ]; + } + res.toString = toString; + return res; + } + var unit2px = Snap._unit2px, + getPath = { + path: function (el) { + return el.attr("path"); + }, + circle: function (el) { + var attr = unit2px(el); + return ellipsePath(attr.cx, attr.cy, attr.r); + }, + ellipse: function (el) { + var attr = unit2px(el); + return ellipsePath(attr.cx, attr.cy, attr.rx, attr.ry); + }, + rect: function (el) { + var attr = unit2px(el); + return rectPath(attr.x, attr.y, attr.width, attr.height, attr.rx, attr.ry); + }, + image: function (el) { + var attr = unit2px(el); + return rectPath(attr.x, attr.y, attr.width, attr.height); + }, + text: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + g: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + symbol: function (el) { + var bbox = el.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + line: function (el) { + return "M" + [el.attr("x1"), el.attr("y1"), el.attr("x2"), el.attr("y2")]; + }, + polyline: function (el) { + return "M" + el.attr("points"); + }, + polygon: function (el) { + return "M" + el.attr("points") + "z"; + }, + svg: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + }, + deflt: function (el) { + var bbox = el.node.getBBox(); + return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); + } + }; + function pathToRelative(pathArray) { + var pth = paths(pathArray), + lowerCase = String.prototype.toLowerCase; + if (pth.rel) { + return pathClone(pth.rel); + } + if (!Snap.is(pathArray, "array") || !Snap.is(pathArray && pathArray[0], "array")) { + pathArray = Snap.parsePathString(pathArray); + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0; + if (pathArray[0][0] == "M") { + x = pathArray[0][1]; + y = pathArray[0][2]; + mx = x; + my = y; + start++; + res.push(["M", x, y]); + } + for (var i = start, ii = pathArray.length; i < ii; i++) { + var r = res[i] = [], + pa = pathArray[i]; + if (pa[0] != lowerCase.call(pa[0])) { + r[0] = lowerCase.call(pa[0]); + switch (r[0]) { + case "a": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] - x).toFixed(3); + r[7] = +(pa[7] - y).toFixed(3); + break; + case "v": + r[1] = +(pa[1] - y).toFixed(3); + break; + case "m": + mx = pa[1]; + my = pa[2]; + default: + for (var j = 1, jj = pa.length; j < jj; j++) { + r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); + } + } + } else { + r = res[i] = []; + if (pa[0] == "m") { + mx = pa[1] + x; + my = pa[2] + y; + } + for (var k = 0, kk = pa.length; k < kk; k++) { + res[i][k] = pa[k]; + } + } + var len = res[i].length; + switch (res[i][0]) { + case "z": + x = mx; + y = my; + break; + case "h": + x += +res[i][len - 1]; + break; + case "v": + y += +res[i][len - 1]; + break; + default: + x += +res[i][len - 2]; + y += +res[i][len - 1]; + } + } + res.toString = toString; + pth.rel = pathClone(res); + return res; + } + function pathToAbsolute(pathArray) { + var pth = paths(pathArray); + if (pth.abs) { + return pathClone(pth.abs); + } + if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption + pathArray = Snap.parsePathString(pathArray); + } + if (!pathArray || !pathArray.length) { + return [["M", 0, 0]]; + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0, + pa0; + if (pathArray[0][0] == "M") { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + start++; + res[0] = ["M", x, y]; + } + var crz = pathArray.length == 3 && + pathArray[0][0] == "M" && + pathArray[1][0].toUpperCase() == "R" && + pathArray[2][0].toUpperCase() == "Z"; + for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { + res.push(r = []); + pa = pathArray[i]; + pa0 = pa[0]; + if (pa0 != pa0.toUpperCase()) { + r[0] = pa0.toUpperCase(); + switch (r[0]) { + case "A": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] + x); + r[7] = +(pa[7] + y); + break; + case "V": + r[1] = +pa[1] + y; + break; + case "H": + r[1] = +pa[1] + x; + break; + case "R": + var dots = [x, y].concat(pa.slice(1)); + for (var j = 2, jj = dots.length; j < jj; j++) { + dots[j] = +dots[j] + x; + dots[++j] = +dots[j] + y; + } + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + break; + case "O": + res.pop(); + dots = ellipsePath(x, y, pa[1], pa[2]); + dots.push(dots[0]); + res = res.concat(dots); + break; + case "U": + res.pop(); + res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); + r = ["U"].concat(res[res.length - 1].slice(-2)); + break; + case "M": + mx = +pa[1] + x; + my = +pa[2] + y; + default: + for (j = 1, jj = pa.length; j < jj; j++) { + r[j] = +pa[j] + ((j % 2) ? x : y); + } + } + } else if (pa0 == "R") { + dots = [x, y].concat(pa.slice(1)); + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + r = ["R"].concat(pa.slice(-2)); + } else if (pa0 == "O") { + res.pop(); + dots = ellipsePath(x, y, pa[1], pa[2]); + dots.push(dots[0]); + res = res.concat(dots); + } else if (pa0 == "U") { + res.pop(); + res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); + r = ["U"].concat(res[res.length - 1].slice(-2)); + } else { + for (var k = 0, kk = pa.length; k < kk; k++) { + r[k] = pa[k]; + } + } + pa0 = pa0.toUpperCase(); + if (pa0 != "O") { + switch (r[0]) { + case "Z": + x = mx; + y = my; + break; + case "H": + x = r[1]; + break; + case "V": + y = r[1]; + break; + case "M": + mx = r[r.length - 2]; + my = r[r.length - 1]; + default: + x = r[r.length - 2]; + y = r[r.length - 1]; + } + } + } + res.toString = toString; + pth.abs = pathClone(res); + return res; + } + function l2c(x1, y1, x2, y2) { + return [x1, y1, x2, y2, x2, y2]; + } + function q2c(x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, + _23 = 2 / 3; + return [ + _13 * x1 + _23 * ax, + _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, + _13 * y2 + _23 * ay, + x2, + y2 + ]; + } + function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { + // for more information of where this math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var _120 = PI * 120 / 180, + rad = PI / 180 * (+angle || 0), + res = [], + xy, + rotate = Snap._.cacher(function (x, y, rad) { + var X = x * math.cos(rad) - y * math.sin(rad), + Y = x * math.sin(rad) + y * math.cos(rad); + return {x: X, y: Y}; + }); + if (!recursive) { + xy = rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + var cos = math.cos(PI / 180 * angle), + sin = math.sin(PI / 180 * angle), + x = (x1 - x2) / 2, + y = (y1 - y2) / 2; + var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = math.sqrt(h); + rx = h * rx; + ry = h * ry; + } + var rx2 = rx * rx, + ry2 = ry * ry, + k = (large_arc_flag == sweep_flag ? -1 : 1) * + math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), + cx = k * rx * y / ry + (x1 + x2) / 2, + cy = k * -ry * x / rx + (y1 + y2) / 2, + f1 = math.asin(((y1 - cy) / ry).toFixed(9)), + f2 = math.asin(((y2 - cy) / ry).toFixed(9)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + f1 < 0 && (f1 = PI * 2 + f1); + f2 < 0 && (f2 = PI * 2 + f2); + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + var df = f2 - f1; + if (abs(df) > _120) { + var f2old = f2, + x2old = x2, + y2old = y2; + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * math.cos(f2); + y2 = cy + ry * math.sin(f2); + res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + df = f2 - f1; + var c1 = math.cos(f1), + s1 = math.sin(f1), + c2 = math.cos(f2), + s2 = math.sin(f2), + t = math.tan(df / 4), + hx = 4 / 3 * rx * t, + hy = 4 / 3 * ry * t, + m1 = [x1, y1], + m2 = [x1 + hx * s1, y1 - hy * c1], + m3 = [x2 + hx * s2, y2 - hy * c2], + m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4].concat(res); + } else { + res = [m2, m3, m4].concat(res).join().split(","); + var newres = []; + for (var i = 0, ii = res.length; i < ii; i++) { + newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; + } + return newres; + } + } + function findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t; + return { + x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, + y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y + }; + } + function curveDim(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x), + b = 2 * (c1x - p1x) - 2 * (c2x - c1x), + c = p1x - c1x, + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a, + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a, + y = [p1y, p2y], + x = [p1x, p2x], + dot; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y); + b = 2 * (c1y - p1y) - 2 * (c2y - c1y); + c = p1y - c1y; + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a; + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + return { + min: {x: mmin.apply(0, x), y: mmin.apply(0, y)}, + max: {x: mmax.apply(0, x), y: mmax.apply(0, y)} + }; + } + function path2curve(path, path2) { + var pth = !path2 && paths(path); + if (!path2 && pth.curve) { + return pathClone(pth.curve); + } + var p = pathToAbsolute(path), + p2 = path2 && pathToAbsolute(path2), + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + processPath = function (path, d) { + var nx, ny; + if (!path) { + return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; + } + !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null); + switch (path[0]) { + case "M": + d.X = path[1]; + d.Y = path[2]; + break; + case "A": + path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1)))); + break; + case "S": + nx = d.x + (d.x - (d.bx || d.x)); + ny = d.y + (d.y - (d.by || d.y)); + path = ["C", nx, ny].concat(path.slice(1)); + break; + case "T": + d.qx = d.x + (d.x - (d.qx || d.x)); + d.qy = d.y + (d.y - (d.qy || d.y)); + path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); + break; + case "Q": + d.qx = path[1]; + d.qy = path[2]; + path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4])); + break; + case "L": + path = ["C"].concat(l2c(d.x, d.y, path[1], path[2])); + break; + case "H": + path = ["C"].concat(l2c(d.x, d.y, path[1], d.y)); + break; + case "V": + path = ["C"].concat(l2c(d.x, d.y, d.x, path[1])); + break; + case "Z": + path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); + break; + } + return path; + }, + fixArc = function (pp, i) { + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + while (pi.length) { + pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6))); + } + pp.splice(i, 1); + ii = mmax(p.length, p2 && p2.length || 0); + } + }, + fixM = function (path1, path2, a1, a2, i) { + if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { + path2.splice(i, 0, ["M", a2.x, a2.y]); + a1.bx = 0; + a1.by = 0; + a1.x = path1[i][1]; + a1.y = path1[i][2]; + ii = mmax(p.length, p2 && p2.length || 0); + } + }; + for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) { + p[i] = processPath(p[i], attrs); + fixArc(p, i); + p2 && (p2[i] = processPath(p2[i], attrs2)); + p2 && fixArc(p2, i); + fixM(p, p2, attrs, attrs2, i); + fixM(p2, p, attrs2, attrs, i); + var seg = p[i], + seg2 = p2 && p2[i], + seglen = seg.length, + seg2len = p2 && seg2.length; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; + attrs.by = toFloat(seg[seglen - 3]) || attrs.y; + attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); + attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); + attrs2.x = p2 && seg2[seg2len - 2]; + attrs2.y = p2 && seg2[seg2len - 1]; + } + if (!p2) { + pth.curve = pathClone(p); + } + return p2 ? [p, p2] : p; + } + function mapPath(path, matrix) { + if (!matrix) { + return path; + } + var x, y, i, j, ii, jj, pathi; + path = path2curve(path); + for (i = 0, ii = path.length; i < ii; i++) { + pathi = path[i]; + for (j = 1, jj = pathi.length; j < jj; j += 2) { + x = matrix.x(pathi[j], pathi[j + 1]); + y = matrix.y(pathi[j], pathi[j + 1]); + pathi[j] = x; + pathi[j + 1] = y; + } + } + return path; + } + + // http://schepers.cc/getting-to-the-point + function catmullRom2bezier(crp, z) { + var d = []; + for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { + var p = [ + {x: +crp[i - 2], y: +crp[i - 1]}, + {x: +crp[i], y: +crp[i + 1]}, + {x: +crp[i + 2], y: +crp[i + 3]}, + {x: +crp[i + 4], y: +crp[i + 5]} + ]; + if (z) { + if (!i) { + p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}; + } else if (iLen - 4 == i) { + p[3] = {x: +crp[0], y: +crp[1]}; + } else if (iLen - 2 == i) { + p[2] = {x: +crp[0], y: +crp[1]}; + p[3] = {x: +crp[2], y: +crp[3]}; + } + } else { + if (iLen - 4 == i) { + p[3] = p[2]; + } else if (!i) { + p[0] = {x: +crp[i], y: +crp[i + 1]}; + } + } + d.push(["C", + (-p[0].x + 6 * p[1].x + p[2].x) / 6, + (-p[0].y + 6 * p[1].y + p[2].y) / 6, + (p[1].x + 6 * p[2].x - p[3].x) / 6, + (p[1].y + 6*p[2].y - p[3].y) / 6, + p[2].x, + p[2].y + ]); + } + + return d; + } + + // export + Snap.path = paths; + + /*\ + * Snap.path.getTotalLength + [ method ] + ** + * Returns the length of the given path in pixels + ** + - path (string) SVG path string + ** + = (number) length + \*/ + Snap.path.getTotalLength = getTotalLength; + /*\ + * Snap.path.getPointAtLength + [ method ] + ** + * Returns the coordinates of the point located at the given length along the given path + ** + - path (string) SVG path string + - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps + ** + = (object) representation of the point: + o { + o x: (number) x coordinate, + o y: (number) y coordinate, + o alpha: (number) angle of derivative + o } + \*/ + Snap.path.getPointAtLength = getPointAtLength; + /*\ + * Snap.path.getSubpath + [ method ] + ** + * Returns the subpath of a given path between given start and end lengths + ** + - path (string) SVG path string + - from (number) length, in pixels, from the start of the path to the start of the segment + - to (number) length, in pixels, from the start of the path to the end of the segment + ** + = (string) path string definition for the segment + \*/ + Snap.path.getSubpath = function (path, from, to) { + if (this.getTotalLength(path) - to < 1e-6) { + return getSubpathsAtLength(path, from).end; + } + var a = getSubpathsAtLength(path, to, 1); + return from ? getSubpathsAtLength(a, from).end : a; + }; + /*\ + * Element.getTotalLength + [ method ] + ** + * Returns the length of the path in pixels (only works for `path` elements) + = (number) length + \*/ + elproto.getTotalLength = function () { + if (this.node.getTotalLength) { + return this.node.getTotalLength(); + } + }; + // SIERRA Element.getPointAtLength()/Element.getTotalLength(): If a is broken into different segments, is the jump distance to the new coordinates set by the _M_ or _m_ commands calculated as part of the path's total length? + /*\ + * Element.getPointAtLength + [ method ] + ** + * Returns coordinates of the point located at the given length on the given path (only works for `path` elements) + ** + - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps + ** + = (object) representation of the point: + o { + o x: (number) x coordinate, + o y: (number) y coordinate, + o alpha: (number) angle of derivative + o } + \*/ + elproto.getPointAtLength = function (length) { + return getPointAtLength(this.attr("d"), length); + }; + // SIERRA Element.getSubpath(): Similar to the problem for Element.getPointAtLength(). Unclear how this would work for a segmented path. Overall, the concept of _subpath_ and what I'm calling a _segment_ (series of non-_M_ or _Z_ commands) is unclear. + /*\ + * Element.getSubpath + [ method ] + ** + * Returns subpath of a given element from given start and end lengths (only works for `path` elements) + ** + - from (number) length, in pixels, from the start of the path to the start of the segment + - to (number) length, in pixels, from the start of the path to the end of the segment + ** + = (string) path string definition for the segment + \*/ + elproto.getSubpath = function (from, to) { + return Snap.path.getSubpath(this.attr("d"), from, to); + }; + Snap._.box = box; + /*\ + * Snap.path.findDotsAtSegment + [ method ] + ** + * Utility method + ** + * Finds dot coordinates on the given cubic beziér curve at the given t + - p1x (number) x of the first point of the curve + - p1y (number) y of the first point of the curve + - c1x (number) x of the first anchor of the curve + - c1y (number) y of the first anchor of the curve + - c2x (number) x of the second anchor of the curve + - c2y (number) y of the second anchor of the curve + - p2x (number) x of the second point of the curve + - p2y (number) y of the second point of the curve + - t (number) position on the curve (0..1) + = (object) point information in format: + o { + o x: (number) x coordinate of the point, + o y: (number) y coordinate of the point, + o m: { + o x: (number) x coordinate of the left anchor, + o y: (number) y coordinate of the left anchor + o }, + o n: { + o x: (number) x coordinate of the right anchor, + o y: (number) y coordinate of the right anchor + o }, + o start: { + o x: (number) x coordinate of the start of the curve, + o y: (number) y coordinate of the start of the curve + o }, + o end: { + o x: (number) x coordinate of the end of the curve, + o y: (number) y coordinate of the end of the curve + o }, + o alpha: (number) angle of the curve derivative at the point + o } + \*/ + Snap.path.findDotsAtSegment = findDotsAtSegment; + /*\ + * Snap.path.bezierBBox + [ method ] + ** + * Utility method + ** + * Returns the bounding box of a given cubic beziér curve + - p1x (number) x of the first point of the curve + - p1y (number) y of the first point of the curve + - c1x (number) x of the first anchor of the curve + - c1y (number) y of the first anchor of the curve + - c2x (number) x of the second anchor of the curve + - c2y (number) y of the second anchor of the curve + - p2x (number) x of the second point of the curve + - p2y (number) y of the second point of the curve + * or + - bez (array) array of six points for beziér curve + = (object) bounding box + o { + o x: (number) x coordinate of the left top point of the box, + o y: (number) y coordinate of the left top point of the box, + o x2: (number) x coordinate of the right bottom point of the box, + o y2: (number) y coordinate of the right bottom point of the box, + o width: (number) width of the box, + o height: (number) height of the box + o } + \*/ + Snap.path.bezierBBox = bezierBBox; + /*\ + * Snap.path.isPointInsideBBox + [ method ] + ** + * Utility method + ** + * Returns `true` if given point is inside bounding box + - bbox (string) bounding box + - x (string) x coordinate of the point + - y (string) y coordinate of the point + = (boolean) `true` if point is inside + \*/ + Snap.path.isPointInsideBBox = isPointInsideBBox; + /*\ + * Snap.path.isBBoxIntersect + [ method ] + ** + * Utility method + ** + * Returns `true` if two bounding boxes intersect + - bbox1 (string) first bounding box + - bbox2 (string) second bounding box + = (boolean) `true` if bounding boxes intersect + \*/ + Snap.path.isBBoxIntersect = isBBoxIntersect; + /*\ + * Snap.path.intersection + [ method ] + ** + * Utility method + ** + * Finds intersections of two paths + - path1 (string) path string + - path2 (string) path string + = (array) dots of intersection + o [ + o { + o x: (number) x coordinate of the point, + o y: (number) y coordinate of the point, + o t1: (number) t value for segment of path1, + o t2: (number) t value for segment of path2, + o segment1: (number) order number for segment of path1, + o segment2: (number) order number for segment of path2, + o bez1: (array) eight coordinates representing beziér curve for the segment of path1, + o bez2: (array) eight coordinates representing beziér curve for the segment of path2 + o } + o ] + \*/ + Snap.path.intersection = pathIntersection; + Snap.path.intersectionNumber = pathIntersectionNumber; + /*\ + * Snap.path.isPointInside + [ method ] + ** + * Utility method + ** + * Returns `true` if given point is inside a given closed path. + * + * Note: fill mode doesn’t affect the result of this method. + - path (string) path string + - x (number) x of the point + - y (number) y of the point + = (boolean) `true` if point is inside the path + \*/ + Snap.path.isPointInside = isPointInsidePath; + /*\ + * Snap.path.getBBox + [ method ] + ** + * Utility method + ** + * Returns the bounding box of a given path + - path (string) path string + = (object) bounding box + o { + o x: (number) x coordinate of the left top point of the box, + o y: (number) y coordinate of the left top point of the box, + o x2: (number) x coordinate of the right bottom point of the box, + o y2: (number) y coordinate of the right bottom point of the box, + o width: (number) width of the box, + o height: (number) height of the box + o } + \*/ + Snap.path.getBBox = pathBBox; + Snap.path.get = getPath; + /*\ + * Snap.path.toRelative + [ method ] + ** + * Utility method + ** + * Converts path coordinates into relative values + - path (string) path string + = (array) path string + \*/ + Snap.path.toRelative = pathToRelative; + /*\ + * Snap.path.toAbsolute + [ method ] + ** + * Utility method + ** + * Converts path coordinates into absolute values + - path (string) path string + = (array) path string + \*/ + Snap.path.toAbsolute = pathToAbsolute; + /*\ + * Snap.path.toCubic + [ method ] + ** + * Utility method + ** + * Converts path to a new path where all segments are cubic beziér curves + - pathString (string|array) path string or array of segments + = (array) array of segments + \*/ + Snap.path.toCubic = path2curve; + /*\ + * Snap.path.map + [ method ] + ** + * Transform the path string with the given matrix + - path (string) path string + - matrix (object) see @Matrix + = (string) transformed path string + \*/ + Snap.path.map = mapPath; + Snap.path.toString = toString; + Snap.path.clone = pathClone; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var mmax = Math.max, + mmin = Math.min; + + // Set + var Set = function (items) { + this.items = []; + this.length = 0; + this.type = "set"; + if (items) { + for (var i = 0, ii = items.length; i < ii; i++) { + if (items[i]) { + this[this.items.length] = this.items[this.items.length] = items[i]; + this.length++; + } + } + } + }, + setproto = Set.prototype; + /*\ + * Set.push + [ method ] + ** + * Adds each argument to the current set + = (object) original element + \*/ + setproto.push = function () { + var item, + len; + for (var i = 0, ii = arguments.length; i < ii; i++) { + item = arguments[i]; + if (item) { + len = this.items.length; + this[len] = this.items[len] = item; + this.length++; + } + } + return this; + }; + /*\ + * Set.pop + [ method ] + ** + * Removes last element and returns it + = (object) element + \*/ + setproto.pop = function () { + this.length && delete this[this.length--]; + return this.items.pop(); + }; + /*\ + * Set.forEach + [ method ] + ** + * Executes given function for each element in the set + * + * If the function returns `false`, the loop stops running. + ** + - callback (function) function to run + - thisArg (object) context object for the callback + = (object) Set object + \*/ + setproto.forEach = function (callback, thisArg) { + for (var i = 0, ii = this.items.length; i < ii; i++) { + if (callback.call(thisArg, this.items[i], i) === false) { + return this; + } + } + return this; + }; + setproto.remove = function () { + while (this.length) { + this.pop().remove(); + } + return this; + }; + setproto.attr = function (value) { + for (var i = 0, ii = this.items.length; i < ii; i++) { + this.items[i].attr(value); + } + return this; + }; + /*\ + * Set.clear + [ method ] + ** + * Removes all elements from the set + \*/ + setproto.clear = function () { + while (this.length) { + this.pop(); + } + }; + /*\ + * Set.splice + [ method ] + ** + * Removes range of elements from the set + ** + - index (number) position of the deletion + - count (number) number of element to remove + - insertion… (object) #optional elements to insert + = (object) set elements that were deleted + \*/ + setproto.splice = function (index, count, insertion) { + index = index < 0 ? mmax(this.length + index, 0) : index; + count = mmax(0, mmin(this.length - index, count)); + var tail = [], + todel = [], + args = [], + i; + for (i = 2; i < arguments.length; i++) { + args.push(arguments[i]); + } + for (i = 0; i < count; i++) { + todel.push(this[index + i]); + } + for (; i < this.length - index; i++) { + tail.push(this[index + i]); + } + var arglen = args.length; + for (i = 0; i < arglen + tail.length; i++) { + this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen]; + } + i = this.items.length = this.length -= count - arglen; + while (this[i]) { + delete this[i++]; + } + return new Set(todel); + }; + /*\ + * Set.exclude + [ method ] + ** + * Removes given element from the set + ** + - element (object) element to remove + = (boolean) `true` if object was found and removed from the set + \*/ + setproto.exclude = function (el) { + for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) { + this.splice(i, 1); + return true; + } + return false; + }; + setproto.insertAfter = function (el) { + var i = this.items.length; + while (i--) { + this.items[i].insertAfter(el); + } + return this; + }; + setproto.getBBox = function () { + var x = [], + y = [], + x2 = [], + y2 = []; + for (var i = this.items.length; i--;) if (!this.items[i].removed) { + var box = this.items[i].getBBox(); + x.push(box.x); + y.push(box.y); + x2.push(box.x + box.width); + y2.push(box.y + box.height); + } + x = mmin.apply(0, x); + y = mmin.apply(0, y); + x2 = mmax.apply(0, x2); + y2 = mmax.apply(0, y2); + return { + x: x, + y: y, + x2: x2, + y2: y2, + width: x2 - x, + height: y2 - y, + cx: x + (x2 - x) / 2, + cy: y + (y2 - y) / 2 + }; + }; + setproto.clone = function (s) { + s = new Set; + for (var i = 0, ii = this.items.length; i < ii; i++) { + s.push(this.items[i].clone()); + } + return s; + }; + setproto.toString = function () { + return "Snap\u2018s set"; + }; + setproto.type = "set"; + // export + Snap.set = function () { + var set = new Set; + if (arguments.length) { + set.push.apply(set, Array.prototype.slice.call(arguments, 0)); + } + return set; + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var names = {}, + reUnit = /[a-z]+$/i, + Str = String; + names.stroke = names.fill = "colour"; + function getEmpty(item) { + var l = item[0]; + switch (l.toLowerCase()) { + case "t": return [l, 0, 0]; + case "m": return [l, 1, 0, 0, 1, 0, 0]; + case "r": if (item.length == 4) { + return [l, 0, item[2], item[3]]; + } else { + return [l, 0]; + } + case "s": if (item.length == 5) { + return [l, 1, 1, item[3], item[4]]; + } else if (item.length == 3) { + return [l, 1, 1]; + } else { + return [l, 1]; + } + } + } + function equaliseTransform(t1, t2, getBBox) { + t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); + t1 = Snap.parseTransformString(t1) || []; + t2 = Snap.parseTransformString(t2) || []; + var maxlength = Math.max(t1.length, t2.length), + from = [], + to = [], + i = 0, j, jj, + tt1, tt2; + for (; i < maxlength; i++) { + tt1 = t1[i] || getEmpty(t2[i]); + tt2 = t2[i] || getEmpty(tt1); + if ((tt1[0] != tt2[0]) || + (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || + (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) + ) { + t1 = Snap._.transform2matrix(t1, getBBox()); + t2 = Snap._.transform2matrix(t2, getBBox()); + from = [["m", t1.a, t1.b, t1.c, t1.d, t1.e, t1.f]]; + to = [["m", t2.a, t2.b, t2.c, t2.d, t2.e, t2.f]]; + break; + } + from[i] = []; + to[i] = []; + for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) { + j in tt1 && (from[i][j] = tt1[j]); + j in tt2 && (to[i][j] = tt2[j]); + } + } + return { + from: path2array(from), + to: path2array(to), + f: getPath(from) + }; + } + function getNumber(val) { + return val; + } + function getUnit(unit) { + return function (val) { + return +val.toFixed(3) + unit; + }; + } + function getColour(clr) { + return Snap.rgb(clr[0], clr[1], clr[2]); + } + function getPath(path) { + var k = 0, i, ii, j, jj, out, a, b = []; + for (i = 0, ii = path.length; i < ii; i++) { + out = "["; + a = ['"' + path[i][0] + '"']; + for (j = 1, jj = path[i].length; j < jj; j++) { + a[j] = "val[" + (k++) + "]"; + } + out += a + "]"; + b[i] = out; + } + return Function("val", "return Snap.path.toString.call([" + b + "])"); + } + function path2array(path) { + var out = []; + for (var i = 0, ii = path.length; i < ii; i++) { + for (var j = 1, jj = path[i].length; j < jj; j++) { + out.push(path[i][j]); + } + } + return out; + } + Element.prototype.equal = function (name, b) { + var A, B, a = Str(this.attr(name) || ""), + el = this; + if (a == +a && b == +b) { + return { + from: +a, + to: +b, + f: getNumber + }; + } + if (names[name] == "colour") { + A = Snap.color(a); + B = Snap.color(b); + return { + from: [A.r, A.g, A.b, A.opacity], + to: [B.r, B.g, B.b, B.opacity], + f: getColour + }; + } + if (name == "transform" || name == "gradientTransform" || name == "patternTransform") { + if (b instanceof Snap.Matrix) { + b = b.toTransformString(); + } + if (!Snap._.rgTransform.test(b)) { + b = Snap._.svgTransform2string(b); + } + return equaliseTransform(a, b, function () { + return el.getBBox(1); + }); + } + if (name == "d" || name == "path") { + A = Snap.path.toCubic(a, b); + return { + from: path2array(A[0]), + to: path2array(A[1]), + f: getPath(A[0]) + }; + } + if (name == "points") { + A = Str(a).split(","); + B = Str(b).split(","); + return { + from: A, + to: B, + f: function (val) { return val; } + }; + } + var aUnit = a.match(reUnit), + bUnit = Str(b).match(reUnit); + if (aUnit && aUnit == bUnit) { + return { + from: parseFloat(a), + to: parseFloat(b), + f: getUnit(aUnit) + }; + } else { + return { + from: this.asPX(name), + to: this.asPX(name, b), + f: getNumber + }; + } + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + has = "hasOwnProperty", + supportsTouch = "createTouch" in glob.doc, + events = [ + "click", "dblclick", "mousedown", "mousemove", "mouseout", + "mouseover", "mouseup", "touchstart", "touchmove", "touchend", + "touchcancel" + ], + touchMap = { + mousedown: "touchstart", + mousemove: "touchmove", + mouseup: "touchend" + }, + getScroll = function (xy) { + var name = xy == "y" ? "scrollTop" : "scrollLeft"; + return glob.doc.documentElement[name] || glob.doc.body[name]; + }, + preventDefault = function () { + this.returnValue = false; + }, + preventTouch = function () { + return this.originalEvent.preventDefault(); + }, + stopPropagation = function () { + this.cancelBubble = true; + }, + stopTouch = function () { + return this.originalEvent.stopPropagation(); + }, + addEvent = (function () { + if (glob.doc.addEventListener) { + return function (obj, type, fn, element) { + var realName = supportsTouch && touchMap[type] ? touchMap[type] : type, + f = function (e) { + var scrollY = getScroll("y"), + scrollX = getScroll("x"); + if (supportsTouch && touchMap[has](type)) { + for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { + if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) { + var olde = e; + e = e.targetTouches[i]; + e.originalEvent = olde; + e.preventDefault = preventTouch; + e.stopPropagation = stopTouch; + break; + } + } + } + var x = e.clientX + scrollX, + y = e.clientY + scrollY; + return fn.call(element, e, x, y); + }; + + if (type !== realName) { + obj.addEventListener(type, f, false); + } + + obj.addEventListener(realName, f, false); + + return function () { + if (type !== realName) { + obj.removeEventListener(type, f, false); + } + + obj.removeEventListener(realName, f, false); + return true; + }; + }; + } else if (glob.doc.attachEvent) { + return function (obj, type, fn, element) { + var f = function (e) { + e = e || glob.win.event; + var scrollY = getScroll("y"), + scrollX = getScroll("x"), + x = e.clientX + scrollX, + y = e.clientY + scrollY; + e.preventDefault = e.preventDefault || preventDefault; + e.stopPropagation = e.stopPropagation || stopPropagation; + return fn.call(element, e, x, y); + }; + obj.attachEvent("on" + type, f); + var detacher = function () { + obj.detachEvent("on" + type, f); + return true; + }; + return detacher; + }; + } + })(), + drag = [], + dragMove = function (e) { + var x = e.clientX, + y = e.clientY, + scrollY = getScroll("y"), + scrollX = getScroll("x"), + dragi, + j = drag.length; + while (j--) { + dragi = drag[j]; + if (supportsTouch) { + var i = e.touches && e.touches.length, + touch; + while (i--) { + touch = e.touches[i]; + if (touch.identifier == dragi.el._drag.id || dragi.el.node.contains(touch.target)) { + x = touch.clientX; + y = touch.clientY; + (e.originalEvent ? e.originalEvent : e).preventDefault(); + break; + } + } + } else { + e.preventDefault(); + } + var node = dragi.el.node, + o, + glob = Snap._.glob, + next = node.nextSibling, + parent = node.parentNode, + display = node.style.display; + // glob.win.opera && parent.removeChild(node); + // node.style.display = "none"; + // o = dragi.el.paper.getElementByPoint(x, y); + // node.style.display = display; + // glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); + // o && eve("snap.drag.over." + dragi.el.id, dragi.el, o); + x += scrollX; + y += scrollY; + eve("snap.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); + } + }, + dragUp = function (e) { + Snap.unmousemove(dragMove).unmouseup(dragUp); + var i = drag.length, + dragi; + while (i--) { + dragi = drag[i]; + dragi.el._drag = {}; + eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); + } + drag = []; + }; + /*\ + * Element.click + [ method ] + ** + * Adds a click event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unclick + [ method ] + ** + * Removes a click event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.dblclick + [ method ] + ** + * Adds a double click event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.undblclick + [ method ] + ** + * Removes a double click event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mousedown + [ method ] + ** + * Adds a mousedown event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmousedown + [ method ] + ** + * Removes a mousedown event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mousemove + [ method ] + ** + * Adds a mousemove event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmousemove + [ method ] + ** + * Removes a mousemove event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseout + [ method ] + ** + * Adds a mouseout event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseout + [ method ] + ** + * Removes a mouseout event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseover + [ method ] + ** + * Adds a mouseover event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseover + [ method ] + ** + * Removes a mouseover event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.mouseup + [ method ] + ** + * Adds a mouseup event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.unmouseup + [ method ] + ** + * Removes a mouseup event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchstart + [ method ] + ** + * Adds a touchstart event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchstart + [ method ] + ** + * Removes a touchstart event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchmove + [ method ] + ** + * Adds a touchmove event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchmove + [ method ] + ** + * Removes a touchmove event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchend + [ method ] + ** + * Adds a touchend event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchend + [ method ] + ** + * Removes a touchend event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + + /*\ + * Element.touchcancel + [ method ] + ** + * Adds a touchcancel event handler to the element + - handler (function) handler for the event + = (object) @Element + \*/ + /*\ + * Element.untouchcancel + [ method ] + ** + * Removes a touchcancel event handler from the element + - handler (function) handler for the event + = (object) @Element + \*/ + for (var i = events.length; i--;) { + (function (eventName) { + Snap[eventName] = elproto[eventName] = function (fn, scope) { + if (Snap.is(fn, "function")) { + this.events = this.events || []; + this.events.push({ + name: eventName, + f: fn, + unbind: addEvent(this.shape || this.node || glob.doc, eventName, fn, scope || this) + }); + } + return this; + }; + Snap["un" + eventName] = + elproto["un" + eventName] = function (fn) { + var events = this.events || [], + l = events.length; + while (l--) if (events[l].name == eventName && + (events[l].f == fn || !fn)) { + events[l].unbind(); + events.splice(l, 1); + !events.length && delete this.events; + return this; + } + return this; + }; + })(events[i]); + } + /*\ + * Element.hover + [ method ] + ** + * Adds hover event handlers to the element + - f_in (function) handler for hover in + - f_out (function) handler for hover out + - icontext (object) #optional context for hover in handler + - ocontext (object) #optional context for hover out handler + = (object) @Element + \*/ + elproto.hover = function (f_in, f_out, scope_in, scope_out) { + return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); + }; + /*\ + * Element.unhover + [ method ] + ** + * Removes hover event handlers from the element + - f_in (function) handler for hover in + - f_out (function) handler for hover out + = (object) @Element + \*/ + elproto.unhover = function (f_in, f_out) { + return this.unmouseover(f_in).unmouseout(f_out); + }; + var draggable = []; + // SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture. + // SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from? + // SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason. + // SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start. on start, drag.end. on end and drag.move. on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID? + /*\ + * Element.drag + [ method ] + ** + * Adds event handlers for an element's drag gesture + ** + - onmove (function) handler for moving + - onstart (function) handler for drag start + - onend (function) handler for drag end + - mcontext (object) #optional context for moving handler + - scontext (object) #optional context for drag start handler + - econtext (object) #optional context for drag end handler + * Additionaly following `drag` events are triggered: `drag.start.` on start, + * `drag.end.` on end and `drag.move.` on every move. When element is dragged over another element + * `drag.over.` fires as well. + * + * Start event and start handler are called in specified context or in context of the element with following parameters: + o x (number) x position of the mouse + o y (number) y position of the mouse + o event (object) DOM event object + * Move event and move handler are called in specified context or in context of the element with following parameters: + o dx (number) shift by x from the start point + o dy (number) shift by y from the start point + o x (number) x position of the mouse + o y (number) y position of the mouse + o event (object) DOM event object + * End event and end handler are called in specified context or in context of the element with following parameters: + o event (object) DOM event object + = (object) @Element + \*/ + elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { + if (!arguments.length) { + var origTransform; + return this.drag(function (dx, dy) { + this.attr({ + transform: origTransform + (origTransform ? "T" : "t") + [dx, dy] + }); + }, function () { + origTransform = this.transform().local; + }); + } + function start(e, x, y) { + (e.originalEvent || e).preventDefault(); + this._drag.x = x; + this._drag.y = y; + this._drag.id = e.identifier; + !drag.length && Snap.mousemove(dragMove).mouseup(dragUp); + drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); + onstart && eve.on("snap.drag.start." + this.id, onstart); + onmove && eve.on("snap.drag.move." + this.id, onmove); + onend && eve.on("snap.drag.end." + this.id, onend); + eve("snap.drag.start." + this.id, start_scope || move_scope || this, x, y, e); + } + this._drag = {}; + draggable.push({el: this, start: start}); + this.mousedown(start); + return this; + }; + /* + * Element.onDragOver + [ method ] + ** + * Shortcut to assign event handler for `drag.over.` event, where `id` is the element's `id` (see @Element.id) + - f (function) handler for event, first argument would be the element you are dragging over + \*/ + // elproto.onDragOver = function (f) { + // f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id); + // }; + /*\ + * Element.undrag + [ method ] + ** + * Removes all drag event handlers from the given element + \*/ + elproto.undrag = function () { + var i = draggable.length; + while (i--) if (draggable[i].el == this) { + this.unmousedown(draggable[i].start); + draggable.splice(i, 1); + eve.unbind("snap.drag.*." + this.id); + } + !draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp); + return this; + }; +}); +// 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. +Snap.plugin(function (Snap, Element, Paper, glob) { + var elproto = Element.prototype, + pproto = Paper.prototype, + rgurl = /^\s*url\((.+)\)/, + Str = String, + $ = Snap._.$; + Snap.filter = {}; +// SIERRA Paper.filter(): I don't understand the note. Does that mean an HTML should dedicate a separate SVG region for a filter definition? What's the advantage over a DEFS? + /*\ + * Paper.filter + [ method ] + ** + * Creates a `` element + ** + - filstr (string) SVG fragment of filter provided as a string + = (object) @Element + * Note: It is recommended to use filters embedded into the page inside an empty SVG element. + > Usage + | var f = paper.filter(''), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + pproto.filter = function (filstr) { + var paper = this; + if (paper.type != "svg") { + paper = paper.paper; + } + var f = Snap.parse(Str(filstr)), + id = Snap._.id(), + width = paper.node.offsetWidth, + height = paper.node.offsetHeight, + filter = $("filter"); + $(filter, { + id: id, + filterUnits: "userSpaceOnUse" + }); + filter.appendChild(f.node); + paper.defs.appendChild(filter); + return new Element(filter); + }; + + eve.on("snap.util.getattr.filter", function () { + eve.stop(); + var p = $(this.node, "filter"); + if (p) { + var match = Str(p).match(rgurl); + return match && Snap.select(match[1]); + } + }); + eve.on("snap.util.attr.filter", function (value) { + if (value instanceof Element && value.type == "filter") { + eve.stop(); + var id = value.node.id; + if (!id) { + $(value.node, {id: value.id}); + id = value.id; + } + $(this.node, { + filter: Snap.url(id) + }); + } + if (!value || value == "none") { + eve.stop(); + this.node.removeAttribute("filter"); + } + }); + /*\ + * Snap.filter.blur + [ method ] + ** + * Returns an SVG markup string for the blur filter + ** + - x (number) amount of horizontal blur, in pixels + - y (number) #optional amount of vertical blur, in pixels + = (string) filter representation + > Usage + | var f = paper.filter(Snap.filter.blur(5, 10)), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + Snap.filter.blur = function (x, y) { + if (x == null) { + x = 2; + } + var def = y == null ? x : [x, y]; + return Snap.format('\', { + def: def + }); + }; + Snap.filter.blur.toString = function () { + return this(); + }; + /*\ + * Snap.filter.shadow + [ method ] + ** + * Returns an SVG markup string for the shadow filter + ** + - dx (number) horizontal shift of the shadow, in pixels + - dy (number) vertical shift of the shadow, in pixels + - blur (number) #optional amount of blur + - color (string) #optional color of the shadow + = (string) filter representation + > Usage + | var f = paper.filter(Snap.filter.shadow(0, 2, 3)), + | c = paper.circle(10, 10, 10).attr({ + | filter: f + | }); + \*/ + Snap.filter.shadow = function (dx, dy, blur, color) { + color = color || "#000"; + if (blur == null) { + blur = 4; + } + if (typeof blur == "string") { + color = blur; + blur = 4; + } + if (dx == null) { + dx = 0; + dy = 2; + } + if (dy == null) { + dy = dx; + } + color = Snap.color(color); + return Snap.format('', { + color: color, + dx: dx, + dy: dy, + blur: blur + }); + }; + Snap.filter.shadow.toString = function () { + return this(); + }; + /*\ + * Snap.filter.grayscale + [ method ] + ** + * Returns an SVG markup string for the grayscale filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.grayscale = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + a: 0.2126 + 0.7874 * (1 - amount), + b: 0.7152 - 0.7152 * (1 - amount), + c: 0.0722 - 0.0722 * (1 - amount), + d: 0.2126 - 0.2126 * (1 - amount), + e: 0.7152 + 0.2848 * (1 - amount), + f: 0.0722 - 0.0722 * (1 - amount), + g: 0.2126 - 0.2126 * (1 - amount), + h: 0.0722 + 0.9278 * (1 - amount) + }); + }; + Snap.filter.grayscale.toString = function () { + return this(); + }; + /*\ + * Snap.filter.sepia + [ method ] + ** + * Returns an SVG markup string for the sepia filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.sepia = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + a: 0.393 + 0.607 * (1 - amount), + b: 0.769 - 0.769 * (1 - amount), + c: 0.189 - 0.189 * (1 - amount), + d: 0.349 - 0.349 * (1 - amount), + e: 0.686 + 0.314 * (1 - amount), + f: 0.168 - 0.168 * (1 - amount), + g: 0.272 - 0.272 * (1 - amount), + h: 0.534 - 0.534 * (1 - amount), + i: 0.131 + 0.869 * (1 - amount) + }); + }; + Snap.filter.sepia.toString = function () { + return this(); + }; + /*\ + * Snap.filter.saturate + [ method ] + ** + * Returns an SVG markup string for the saturate filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.saturate = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: 1 - amount + }); + }; + Snap.filter.saturate.toString = function () { + return this(); + }; + /*\ + * Snap.filter.hueRotate + [ method ] + ** + * Returns an SVG markup string for the hue-rotate filter + ** + - angle (number) angle of rotation + = (string) filter representation + \*/ + Snap.filter.hueRotate = function (angle) { + angle = angle || 0; + return Snap.format('', { + angle: angle + }); + }; + Snap.filter.hueRotate.toString = function () { + return this(); + }; + /*\ + * Snap.filter.invert + [ method ] + ** + * Returns an SVG markup string for the invert filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.invert = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount, + amount2: 1 - amount + }); + }; + Snap.filter.invert.toString = function () { + return this(); + }; + /*\ + * Snap.filter.brightness + [ method ] + ** + * Returns an SVG markup string for the brightness filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.brightness = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount + }); + }; + Snap.filter.brightness.toString = function () { + return this(); + }; + /*\ + * Snap.filter.contrast + [ method ] + ** + * Returns an SVG markup string for the contrast filter + ** + - amount (number) amount of filter (`0..1`) + = (string) filter representation + \*/ + Snap.filter.contrast = function (amount) { + if (amount == null) { + amount = 1; + } + return Snap.format('', { + amount: amount, + amount2: .5 - amount / 2 + }); + }; + Snap.filter.contrast.toString = function () { + return this(); + }; +}); +return Snap; +})); \ No newline at end of file diff --git a/demos/snap-ad/src/sass/screen.scss b/demos/snap-ad/src/sass/screen.scss new file mode 100644 index 0000000..e0a5ee4 --- /dev/null +++ b/demos/snap-ad/src/sass/screen.scss @@ -0,0 +1,82 @@ +@import 'compass/css3'; + +.base-font{ + font-family: 'Open Sans', serif; +} + +.snap-font{ + font-family: 'Source Sans Pro', serif; +} + +body{ + margin: 0; + cursor: pointer; +} + +text{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +svg{ + @include transform-origin(top, left); + @include transform(scale(1)); +} + +#learn-btn{ + cursor: pointer; + + path, text{ + @include transform(translateY(0)); + } + + &:hover{ + path{ + &:first-child{ + fill: #076656; + + } + } + + path{ + &:nth-child(2){ + fill: #7cd1c2; + } + } + + text{ + fill: #0a9a87; + + } + }; + + &:active{ + path{ + &:nth-child(2){ + @include transform(translateY(2px)); + } + } + + text{ + @include transform(translateY(2px)); + } + } +} + +#replay-btn{ + &:hover{ + opacity: 0.5; + }; +} + +/* +@media (max-width: 400px) { + svg{ + @include transform(scale(0.375)); + } +} +*/ \ No newline at end of file