// 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.bindings = {}; 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; }; /*\ * Set.animate [ method ] ** * Animates each element in set in sync. * ** - 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 * or - animation (array) array of animation parameter for each element in set in format `[attrs, duration, easing, callback]` > Usage | // animate all elements in set to radius 10 | set.animate({r: 10}, 500, mina.easein); | // or | // animate first element to radius 10, but second to radius 20 and in different time | set.animate([{r: 10}, 500, mina.easein], [{r: 20}, 1500, mina.easein]); = (Element) the current element \*/ setproto.animate = function (attrs, ms, easing, callback) { if (typeof easing == "function" && !easing.length) { callback = easing; easing = mina.linear; } if (attrs instanceof Snap._.Animation) { callback = attrs.callback; easing = attrs.easing; ms = easing.dur; attrs = attrs.attr; } var args = arguments; if (Snap.is(attrs, "array") && Snap.is(args[args.length - 1], "array")) { var each = true; } var begin, handler = function () { if (begin) { this.b = begin; } else { begin = this.b; } }, cb = 0, set = this, callbacker = callback && function () { if (++cb == set.length) { callback.call(this); } }; return this.forEach(function (el, i) { eve.once("snap.animcreated." + el.id, handler); if (each) { args[i] && el.animate.apply(el, args[i]); } else { el.animate(attrs, ms, easing, callbacker); } }); }; setproto.remove = function () { while (this.length) { this.pop().remove(); } return this; }; /*\ * Set.bind [ method ] ** * Specifies how to handle a specific attribute when applied * to a set. * ** - attr (string) attribute name - callback (function) function to run * or - attr (string) attribute name - element (Element) specific element in the set to apply the attribute to * or - attr (string) attribute name - element (Element) specific element in the set to apply the attribute to - eattr (string) attribute on the element to bind the attribute to = (object) Set object \*/ setproto.bind = function (attr, a, b) { var data = {}; if (typeof a == "function") { this.bindings[attr] = a; } else { var aname = b || attr; this.bindings[attr] = function (v) { data[aname] = v; a.attr(data); }; } return this; }; setproto.attr = function (value) { var unbound = {}; for (var k in value) { if (this.bindings[k]) { this.bindings[k](value[k]); } else { unbound[k] = value[k]; } } for (var i = 0, ii = this.items.length; i < ii; i++) { this.items[i].attr(unbound); } 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 = Set; Snap.set = function () { var set = new Set; if (arguments.length) { set.push.apply(set, Array.prototype.slice.call(arguments, 0)); } return set; }; });