From 4049e2e6361d5ed9120f1edd02ef96ecc138fa6d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ulrich-Matthias=20Sch=C3=A4fer?= Date: Mon, 5 Nov 2018 15:12:58 +0100 Subject: [PATCH] rework of classes, make events on every object possible --- dirty.html | 3 + dist/svg.js | 1857 ++++++++++++++++++++-------------------- dist/svg.min.js | 2 +- spec/spec/container.js | 6 +- src/Bare.js | 4 +- src/Box.js | 1 - src/Container.js | 28 +- src/Dom.js | 237 +++++ src/Element.js | 232 ++--- src/EventTarget.js | 31 +- src/HtmlNode.js | 34 +- src/Parent.js | 169 ---- src/Runner.js | 15 +- src/Shape.js | 4 +- src/Text.js | 4 +- src/Tspan.js | 5 +- src/arrange.js | 2 +- src/attr.js | 4 +- src/classHandling.js | 44 + src/classes.js | 3 +- src/css.js | 2 +- src/data.js | 2 +- src/elemnts-svg.js | 4 - src/event.js | 28 +- src/memory.js | 2 +- src/selector.js | 2 +- src/sugar.js | 7 +- src/svg.js | 13 +- 28 files changed, 1388 insertions(+), 1357 deletions(-) create mode 100644 src/Dom.js delete mode 100644 src/Parent.js create mode 100644 src/classHandling.js diff --git a/dirty.html b/dirty.html index 9792d5e..23b2d6d 100644 --- a/dirty.html +++ b/dirty.html @@ -267,6 +267,9 @@ let moon = canvas.circle(50).center(1200, 300).attr({fill: '#ffa'}) earth.animate(10000).loop().ease('-') .transform({rotate: 360, origin: [500, 300]}, true) .transform({rotate: 720, origin: 'center'}, true) + .on('step', (e) => { + // console.log(e) + }) moon.animate(10000).loop().ease('-') .transform({rotate: 360, origin: [500, 300]}, true) diff --git a/dist/svg.js b/dist/svg.js index ba5a1d8..1d465b9 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -669,22 +669,32 @@ var SVG = (function () { var listenerId = 0; + function getEvents(node) { + var n = makeInstance(node).getEventHolder(); + if (!n.events) n.events = {}; + return n.events; + } + function getEventTarget(node) { - return typeof node.getEventTarget === 'function' ? node.getEventTarget() : node; + return makeInstance(node).getEventTarget(); + } + + function clearEvents(node) { + var n = makeInstance(node).getEventHolder(); + if (n.events) n.events = {}; } // Add event binder in the SVG namespace function on(node, events, listener, binding, options) { var l = listener.bind(binding || node); + var bag = getEvents(node); var n = getEventTarget(node); // events can be an array of events or a string of events events = Array.isArray(events) ? events : events.split(delimiter); // ensure instance object for nodes which are not adopted - - n.instance = n.instance || { - events: {} // pull event handlers from the element - - }; - var bag = n.instance.events; // add id to listener + // n.instance = n.instance || {events: {}} + // pull event handlers from the element + // var bag = n.instance.events + // add id to listener if (!listener._svgjsListenerId) { listener._svgjsListenerId = ++listenerId; @@ -704,18 +714,19 @@ var SVG = (function () { } // Add event unbinder in the SVG namespace function off(node, events, listener, options) { + var bag = getEvents(node); var n = getEventTarget(node); // we cannot remove an event if its not an svg.js instance - - if (!n.instance) return; // listener can be a function or a number + // if (!n.instance) return + // listener can be a function or a number if (typeof listener === 'function') { listener = listener._svgjsListenerId; if (!listener) return; } // pull event handlers from the element + // var bag = n.instance.events + // events can be an array of events or a string or undefined - var bag = n.instance.events; // events can be an array of events or a string or undefined - events = Array.isArray(events) ? events : (events || '').split(delimiter); events.forEach(function (event) { var ev = event && event.split('.')[0]; @@ -762,7 +773,7 @@ var SVG = (function () { off(n, event); } - n.instance.events = {}; + clearEvents(node); } }); } @@ -796,17 +807,22 @@ var SVG = (function () { function EventTarget() { var _this; - var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + _ref$events = _ref.events, + events = _ref$events === void 0 ? {} : _ref$events; _classCallCheck(this, EventTarget); _this = _possibleConstructorReturn(this, _getPrototypeOf(EventTarget).call(this)); - _this.events = node.events || {}; + _this.events = events; return _this; - } // Bind given event to listener - + } _createClass(EventTarget, [{ + key: "addEventListener", + value: function addEventListener() {} // Bind given event to listener + + }, { key: "on", value: function on$$1(event, listener, binding, options) { on(this, event, listener, binding, options); @@ -825,6 +841,21 @@ var SVG = (function () { key: "dispatch", value: function dispatch$$1(event, data) { return dispatch(this, event, data); + } + }, { + key: "dispatchEvent", + value: function dispatchEvent(event) { + var bag = this.getEventHolder().events; + if (!bag) return true; + var events = bag[event.type]; + + for (var i in events) { + for (var j in events[i]) { + events[i][j](event); + } + } + + return !event.defaultPrevented; } // Fire given event }, { @@ -833,6 +864,19 @@ var SVG = (function () { this.dispatch(event, data); return this; } + }, { + key: "getEventHolder", + value: function getEventHolder() { + return this; + } + }, { + key: "getEventTarget", + value: function getEventTarget() { + return this; + } + }, { + key: "removeEventListener", + value: function removeEventListener() {} }]); return EventTarget; @@ -858,530 +902,682 @@ var SVG = (function () { // // registerConstructor('EventTarget', setup) - var SVGNumber = + // Map function + function map(array, block) { + var i; + var il = array.length; + var result = []; + + for (i = 0; i < il; i++) { + result.push(block(array[i])); + } + + return result; + } // Filter function + + function filter(array, block) { + var i; + var il = array.length; + var result = []; + + for (i = 0; i < il; i++) { + if (block(array[i])) { + result.push(array[i]); + } + } + + return result; + } // Degrees to radians + + function radians(d) { + return d % 360 * Math.PI / 180; + } // Radians to degrees + + function degrees(r) { + return r * 180 / Math.PI % 360; + } + function filterSVGElements(nodes) { + return this.filter(nodes, function (el) { + return el instanceof window.SVGElement; + }); + } + + var utils = /*#__PURE__*/Object.freeze({ + map: map, + filter: filter, + radians: radians, + degrees: degrees, + filterSVGElements: filterSVGElements + }); + + function noop() {} // Default animation values + + var timeline = { + duration: 400, + ease: '>', + delay: 0 // Default attribute values + + }; + var attrs = { + // fill and stroke + 'fill-opacity': 1, + 'stroke-opacity': 1, + 'stroke-width': 0, + 'stroke-linejoin': 'miter', + 'stroke-linecap': 'butt', + fill: '#000000', + stroke: '#000000', + opacity: 1, + // position + x: 0, + y: 0, + cx: 0, + cy: 0, + // size + width: 0, + height: 0, + // radius + r: 0, + rx: 0, + ry: 0, + // gradient + offset: 0, + 'stop-opacity': 1, + 'stop-color': '#000000', + // text + 'font-size': 16, + 'font-family': 'Helvetica, Arial, sans-serif', + 'text-anchor': 'start' + }; + + var defaults = /*#__PURE__*/Object.freeze({ + noop: noop, + timeline: timeline, + attrs: attrs + }); + + var Color = /*#__PURE__*/ function () { - // Initialize - function SVGNumber() { - _classCallCheck(this, SVGNumber); + function Color() { + _classCallCheck(this, Color); this.init.apply(this, arguments); } - _createClass(SVGNumber, [{ + _createClass(Color, [{ key: "init", - value: function init(value, unit) { - unit = Array.isArray(value) ? value[1] : unit; - value = Array.isArray(value) ? value[0] : value; // initialize defaults - - this.value = 0; - this.unit = unit || ''; // parse value - - if (typeof value === 'number') { - // ensure a valid numeric value - this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value; - } else if (typeof value === 'string') { - unit = value.match(numberAndUnit); + value: function init(color, g, b) { + var match; // initialize defaults - if (unit) { - // make value numeric - this.value = parseFloat(unit[1]); // normalize + this.r = 0; + this.g = 0; + this.b = 0; + if (!color) return; // parse color - if (unit[5] === '%') { - this.value /= 100; - } else if (unit[5] === 's') { - this.value *= 1000; - } // store unit + if (typeof color === 'string') { + if (isRgb.test(color)) { + // get rgb values + match = rgb.exec(color.replace(whitespace, '')); // parse numeric values + this.r = parseInt(match[1]); + this.g = parseInt(match[2]); + this.b = parseInt(match[3]); + } else if (isHex.test(color)) { + // get hex values + match = hex.exec(fullHex(color)); // parse numeric values - this.unit = unit[5]; - } - } else { - if (value instanceof SVGNumber) { - this.value = value.valueOf(); - this.unit = value.unit; + this.r = parseInt(match[1], 16); + this.g = parseInt(match[2], 16); + this.b = parseInt(match[3], 16); } + } else if (Array.isArray(color)) { + this.r = color[0]; + this.g = color[1]; + this.b = color[2]; + } else if (_typeof(color) === 'object') { + this.r = color.r; + this.g = color.g; + this.b = color.b; + } else if (arguments.length === 3) { + this.r = color; + this.g = g; + this.b = b; } - } + } // Default to hex conversion + }, { key: "toString", value: function toString() { - return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit; - } - }, { - key: "toJSON", - value: function toJSON() { - return this.toString(); + return this.toHex(); } }, { key: "toArray", value: function toArray() { - return [this.value, this.unit]; - } + return [this.r, this.g, this.b]; + } // Build hex value + }, { - key: "valueOf", - value: function valueOf() { - return this.value; - } // Add number + key: "toHex", + value: function toHex() { + return '#' + compToHex(Math.round(this.r)) + compToHex(Math.round(this.g)) + compToHex(Math.round(this.b)); + } // Build rgb value }, { - key: "plus", - value: function plus(number) { - number = new SVGNumber(number); - return new SVGNumber(this + number, this.unit || number.unit); - } // Subtract number + key: "toRgb", + value: function toRgb() { + return 'rgb(' + [this.r, this.g, this.b].join() + ')'; + } // Calculate true brightness }, { - key: "minus", - value: function minus(number) { - number = new SVGNumber(number); - return new SVGNumber(this - number, this.unit || number.unit); - } // Multiply number + key: "brightness", + value: function brightness() { + return this.r / 255 * 0.30 + this.g / 255 * 0.59 + this.b / 255 * 0.11; + } // Testers + // Test if given value is a color string + + }], [{ + key: "test", + value: function test(color) { + color += ''; + return isHex.test(color) || isRgb.test(color); + } // Test if given value is a rgb object }, { - key: "times", - value: function times(number) { - number = new SVGNumber(number); - return new SVGNumber(this * number, this.unit || number.unit); - } // Divide number + key: "isRgb", + value: function isRgb$$1(color) { + return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number'; + } // Test if given value is a color }, { - key: "divide", - value: function divide(number) { - number = new SVGNumber(number); - return new SVGNumber(this / number, this.unit || number.unit); + key: "isColor", + value: function isColor(color) { + return this.isRgb(color) || this.test(color); } }]); - return SVGNumber; + return Color; }(); - var Doc = getClass(root); //export const name = 'Element' - - var Element = - /*#__PURE__*/ - function (_EventTarget) { - _inherits(Element, _EventTarget); - - function Element(node) { - var _this; - - _classCallCheck(this, Element); - - _this = _possibleConstructorReturn(this, _getPrototypeOf(Element).call(this)); // initialize data object - - _this.dom = {}; // create circular reference - - _this.node = node; - _this.type = node.nodeName; - _this.node.instance = _assertThisInitialized(_assertThisInitialized(_this)); - - if (node.hasAttribute('svgjs:data')) { - // pull svgjs data from the dom (getAttributeNS doesn't work in html5) - _this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {}); - } - - return _this; - } // Move over x-axis - + var subClassArray = function () { + try { + // try es6 subclassing + return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', '[name]: class extends baseClass {', 'constructor (...args) {', 'super(...args)', '_constructor && _constructor.apply(this, args)', '}', '}', '}[name]'].join('\n')); + } catch (e) { + // Use es5 approach + return function (name) { + var baseClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Array; - _createClass(Element, [{ - key: "x", - value: function x(_x) { - return this.attr('x', _x); - } // Move over y-axis + var _constructor = arguments.length > 2 ? arguments[2] : undefined; - }, { - key: "y", - value: function y(_y) { - return this.attr('y', _y); - } // Move by center over x-axis + var Arr = function Arr() { + baseClass.apply(this, arguments); + _constructor && _constructor.apply(this, arguments); + }; - }, { - key: "cx", - value: function cx(x) { - return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2); - } // Move by center over y-axis + Arr.prototype = Object.create(baseClass.prototype); + Arr.prototype.constructor = Arr; + return Arr; + }; + } + }(); - }, { - key: "cy", - value: function cy(y) { - return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2); - } // Move element to given x and y values + var SVGArray = subClassArray('SVGArray', Array, function () { + this.init.apply(this, arguments); + }); + extend(SVGArray, { + init: function init() { + //this.splice(0, this.length) + this.length = 0; + this.push.apply(this, _toConsumableArray(this.parse.apply(this, arguments))); + }, + toArray: function toArray() { + // const ret = [] + // ret.push(...this) + // return ret + return Array.prototype.concat.apply([], this); + }, + toString: function toString() { + return this.join(' '); + }, + // Flattens the array if needed + valueOf: function valueOf() { + var ret = []; + ret.push.apply(ret, _toConsumableArray(this)); + return ret; // return this.toArray() + }, + // Parse whitespace separated string + parse: function parse() { + var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + //array = array.valueOf() + // If already is an array, no need to parse it + if (array instanceof Array) return array; + return array.trim().split(delimiter).map(parseFloat); + }, + clone: function clone() { + return new this.constructor(this); + }, + toSet: function toSet() { + return new Set(this); + } + }); // export default class SVGArray extends BaseArray { + // constructor (...args) { + // super() + // this.init(...args) + // } + // + // init (array, fallback = []) { + // //this.splice(0, this.length) + // this.length = 0 + // this.push(...this.parse(array || fallback)) + // } + // + // toArray () { + // return [].concat(this) + // } + // + // toString () { + // return this.join(' ') + // } + // + // valueOf () { + // return this.toArray() + // } + // + // // Parse whitespace separated string + // parse (array) { + // array = array.valueOf() + // + // // if already is an array, no need to parse it + // if (Array.isArray(array)) return array + // + // return array.trim().split(delimiter).map(parseFloat) + // } + // + // clone () { + // return new this.constructor(this) + // } + // + // toSet () { + // return new Set(this) + // } + // } - }, { - key: "move", - value: function move(x, y) { - return this.x(x).y(y); - } // Move element by its center + var SVGNumber = + /*#__PURE__*/ + function () { + // Initialize + function SVGNumber() { + _classCallCheck(this, SVGNumber); - }, { - key: "center", - value: function center(x, y) { - return this.cx(x).cy(y); - } // Set width of element + this.init.apply(this, arguments); + } - }, { - key: "width", - value: function width(_width) { - return this.attr('width', _width); - } // Set height of element + _createClass(SVGNumber, [{ + key: "init", + value: function init(value, unit) { + unit = Array.isArray(value) ? value[1] : unit; + value = Array.isArray(value) ? value[0] : value; // initialize defaults - }, { - key: "height", - value: function height(_height) { - return this.attr('height', _height); - } // Set element size to given width and height + this.value = 0; + this.unit = unit || ''; // parse value - }, { - key: "size", - value: function size(width, height) { - var p = proportionalSize(this, width, height); - return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height)); - } // Clone element + if (typeof value === 'number') { + // ensure a valid numeric value + this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value; + } else if (typeof value === 'string') { + unit = value.match(numberAndUnit); - }, { - key: "clone", - value: function clone(parent) { - // write dom data to the dom so the clone can pickup the data - this.writeDataToDom(); // clone element and assign new id + if (unit) { + // make value numeric + this.value = parseFloat(unit[1]); // normalize - var clone = assignNewId(this.node.cloneNode(true)); // insert the clone in the given parent or after myself + if (unit[5] === '%') { + this.value /= 100; + } else if (unit[5] === 's') { + this.value *= 1000; + } // store unit - if (parent) parent.add(clone);else this.after(clone); - return clone; - } // Remove element - }, { - key: "remove", - value: function remove() { - if (this.parent()) { - this.parent().removeElement(this); + this.unit = unit[5]; + } + } else { + if (value instanceof SVGNumber) { + this.value = value.valueOf(); + this.unit = value.unit; + } } - - return this; - } // Replace element - - }, { - key: "replace", - value: function replace(element) { - this.after(element).remove(); - return element; - } // Add element to given container and return self - - }, { - key: "addTo", - value: function addTo(parent) { - return makeInstance(parent).put(this); - } // Add element to given container and return container - - }, { - key: "putIn", - value: function putIn(parent) { - return makeInstance(parent).add(this); - } // Get / set id - - }, { - key: "id", - value: function id(_id) { - // generate new id if no id set - if (typeof _id === 'undefined' && !this.node.id) { - this.node.id = eid(this.type); - } // dont't set directly width this.node.id to make `null` work correctly - - - return this.attr('id', _id); - } // Checks whether the given point inside the bounding box of the element - - }, { - key: "inside", - value: function inside(x, y) { - var box = this.bbox(); - return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height; - } // Return id on string conversion - + } }, { key: "toString", value: function toString() { - return this.id(); - } // Return array of classes on the node - - }, { - key: "classes", - value: function classes() { - var attr = this.attr('class'); - return attr == null ? [] : attr.trim().split(delimiter); - } // Return true if class exists on the node, false otherwise - - }, { - key: "hasClass", - value: function hasClass(name) { - return this.classes().indexOf(name) !== -1; - } // Add class to the node - - }, { - key: "addClass", - value: function addClass(name) { - if (!this.hasClass(name)) { - var array = this.classes(); - array.push(name); - this.attr('class', array.join(' ')); - } - - return this; - } // Remove class from the node - - }, { - key: "removeClass", - value: function removeClass(name) { - if (this.hasClass(name)) { - this.attr('class', this.classes().filter(function (c) { - return c !== name; - }).join(' ')); - } - - return this; - } // Toggle the presence of a class on the node - - }, { - key: "toggleClass", - value: function toggleClass(name) { - return this.hasClass(name) ? this.removeClass(name) : this.addClass(name); - } // Get referenced element form attribute value - - }, { - key: "reference", - value: function reference$$1(attr) { - var id = idFromReference(this.attr(attr)); - return id ? makeInstance(id) : null; - } // Returns the parent element instance - + return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit; + } }, { - key: "parent", - value: function parent(type) { - var parent = this; // check for parent - - if (!parent.node.parentNode) return null; // get parent element - - parent = adopt(parent.node.parentNode); - if (!type) return parent; // loop trough ancestors if type is given - - while (parent && parent.node instanceof window.SVGElement) { - if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent; - parent = adopt(parent.node.parentNode); - } - } // Get parent document - + key: "toJSON", + value: function toJSON() { + return this.toString(); + } }, { - key: "doc", - value: function doc() { - var p = this.parent(Doc); - return p && p.doc(); - } // Get defs - + key: "toArray", + value: function toArray() { + return [this.value, this.unit]; + } }, { - key: "defs", - value: function defs() { - return this.doc().defs(); - } // return array of all ancestors of given type up to the root svg + key: "valueOf", + value: function valueOf() { + return this.value; + } // Add number }, { - key: "parents", - value: function parents(type) { - var parents = []; - var parent = this; - - do { - parent = parent.parent(type); - if (!parent || parent instanceof getClass('HtmlNode')) break; - parents.push(parent); - } while (parent.parent); - - return parents; - } // matches the element vs a css selector + key: "plus", + value: function plus(number) { + number = new SVGNumber(number); + return new SVGNumber(this + number, this.unit || number.unit); + } // Subtract number }, { - key: "matches", - value: function matches(selector) { - return matcher(this.node, selector); - } // Returns the svg node to call native svg methods on it + key: "minus", + value: function minus(number) { + number = new SVGNumber(number); + return new SVGNumber(this - number, this.unit || number.unit); + } // Multiply number }, { - key: "native", - value: function native() { - return this.node; - } // Import raw svg + key: "times", + value: function times(number) { + number = new SVGNumber(number); + return new SVGNumber(this * number, this.unit || number.unit); + } // Divide number }, { - key: "svg", - value: function svg() { - // write svgjs data to the dom - this.writeDataToDom(); - return this.node.outerHTML; - } // write svgjs data to the dom + key: "divide", + value: function divide(number) { + number = new SVGNumber(number); + return new SVGNumber(this / number, this.unit || number.unit); + } + }]); - }, { - key: "writeDataToDom", - value: function writeDataToDom() { - // remove previously set data - this.node.removeAttribute('svgjs:data'); + return SVGNumber; + }(); - if (Object.keys(this.dom).length) { - this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428 - } + // Set svg element attribute - return this; - } // set given data to the elements data property + function attr(attr, val, ns) { + // act as full getter + if (attr == null) { + // get an object of attributes + attr = {}; + val = this.node.attributes; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; - }, { - key: "setData", - value: function setData(o) { - this.dom = o; - return this; + try { + for (var _iterator = val[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var node = _step.value; + attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return != null) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } } - }, { - key: "getEventTarget", - value: function getEventTarget() { - return this.node; + + return attr; + } else if (Array.isArray(attr)) ; else if (_typeof(attr) === 'object') { + // apply every attribute individually if an object is passed + for (val in attr) { + this.attr(val, attr[val]); } - }]); + } else if (val === null) { + // remove value + this.node.removeAttribute(attr); + } else if (val == null) { + // act as a getter if the first and only argument is not an object + val = this.node.getAttribute(attr); + return val == null ? attrs[attr] // FIXME: do we need to return defaults? + : isNumber.test(val) ? parseFloat(val) : val; + } else { + // convert image fill and stroke to patterns + if (attr === 'fill' || attr === 'stroke') { + if (isImage.test(val)) { + val = this.doc().defs().image(val); + } + } // FIXME: This is fine, but what about the lines above? + // How does attr know about image()? - return Element; - }(EventTarget); // registerMethods('Element', { - // Map function - function map(array, block) { - var i; - var il = array.length; - var result = []; + while (typeof val.attrHook == 'function') { + val = val.attrHook(this, attr); + } // ensure correct numeric values (also accepts NaN and Infinity) - for (i = 0; i < il; i++) { - result.push(block(array[i])); - } - return result; - } // Filter function + if (typeof val === 'number') { + val = new SVGNumber(val); + } else if (Color.isColor(val)) { + // ensure full hex color + val = new Color(val); + } else if (val.constructor === Array) { + // Check for plain arrays and parse array values + val = new SVGArray(val); + } // if the passed attribute is leading... - function filter(array, block) { - var i; - var il = array.length; - var result = []; - for (i = 0; i < il; i++) { - if (block(array[i])) { - result.push(array[i]); + if (attr === 'leading') { + // ... call the leading method instead + if (this.leading) { + this.leading(val); + } + } else { + // set given attribute on node + typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString()); + } // rebuild if required + + + if (this.rebuild && (attr === 'font-size' || attr === 'x')) { + this.rebuild(); } } - return result; - } // Degrees to radians + return this; + } //registerMethods('Element', {attr}) - function radians(d) { - return d % 360 * Math.PI / 180; - } // Radians to degrees + var Dom = + /*#__PURE__*/ + function (_EventTarget) { + _inherits(Dom, _EventTarget); - function degrees(r) { - return r * 180 / Math.PI % 360; - } - function filterSVGElements(nodes) { - return this.filter(nodes, function (el) { - return el instanceof window.SVGElement; - }); - } + function Dom(node) { + var _this; - var utils = /*#__PURE__*/Object.freeze({ - map: map, - filter: filter, - radians: radians, - degrees: degrees, - filterSVGElements: filterSVGElements - }); + _classCallCheck(this, Dom); - var Parent = - /*#__PURE__*/ - function (_Element) { - _inherits(Parent, _Element); + _this = _possibleConstructorReturn(this, _getPrototypeOf(Dom).call(this, node)); + _this.node = node; + _this.type = node.nodeName; + return _this; + } // Add given element at a position - function Parent() { - _classCallCheck(this, Parent); - return _possibleConstructorReturn(this, _getPrototypeOf(Parent).apply(this, arguments)); - } + _createClass(Dom, [{ + key: "add", + value: function add(element, i) { + element = makeInstance(element); + + if (i == null) { + this.node.appendChild(element.node); + } else if (element.node !== this.node.childNodes[i]) { + this.node.insertBefore(element.node, this.node.childNodes[i]); + } + + return this; + } // Add element to given container and return self + + }, { + key: "addTo", + value: function addTo(parent) { + return makeInstance(parent).put(this); + } // Returns all child elements - _createClass(Parent, [{ + }, { key: "children", - // Returns all child elements value: function children() { return map(this.node.children, function (node) { return adopt(node); }); - } // Add given element at a position + } // Remove all elements in this container }, { - key: "add", - value: function add(element, i) { - element = makeInstance(element); + key: "clear", + value: function clear() { + // remove children + while (this.node.hasChildNodes()) { + this.node.removeChild(this.node.lastChild); + } // remove defs reference + + + delete this._defs; + return this; + } // Clone element + + }, { + key: "clone", + value: function clone(parent) { + // write dom data to the dom so the clone can pickup the data + this.writeDataToDom(); // clone element and assign new id + + var clone = assignNewId(this.node.cloneNode(true)); // insert the clone in the given parent or after myself + + if (parent) parent.add(clone); // FIXME: after might not be available here + else this.after(clone); + return clone; + } // Iterates over all children and invokes a given block + + }, { + key: "each", + value: function each(block, deep) { + var children = this.children(); + var i, il; - if (i == null) { - this.node.appendChild(element.node); - } else if (element.node !== this.node.childNodes[i]) { - this.node.insertBefore(element.node, this.node.childNodes[i]); + for (i = 0, il = children.length; i < il; i++) { + block.apply(children[i], [i, children]); + + if (deep) { + children[i].each(block, deep); + } } return this; - } // Basically does the same as `add()` but returns the added element instead + } // Get first child }, { - key: "put", - value: function put(element, i) { - this.add(element, i); - return element.instance || element; + key: "first", + value: function first() { + return adopt(this.node.firstChild); + } // Get a element at the given index + + }, { + key: "get", + value: function get(i) { + return adopt(this.node.childNodes[i]); + } + }, { + key: "getEventHolder", + value: function getEventHolder() { + return this.node; + } + }, { + key: "getEventTarget", + value: function getEventTarget() { + return this.node; } // Checks if the given element is a child }, { key: "has", value: function has(element) { return this.index(element) >= 0; + } // Get / set id + + }, { + key: "id", + value: function id(_id) { + // generate new id if no id set + if (typeof _id === 'undefined' && !this.node.id) { + this.node.id = eid(this.type); + } // dont't set directly width this.node.id to make `null` work correctly + + + return this.attr('id', _id); } // Gets index of given element }, { key: "index", value: function index(element) { return [].slice.call(this.node.childNodes).indexOf(element.node); - } // Get a element at the given index - - }, { - key: "get", - value: function get(i) { - return adopt(this.node.childNodes[i]); - } // Get first child - - }, { - key: "first", - value: function first() { - return adopt(this.node.firstChild); } // Get the last child }, { key: "last", value: function last() { return adopt(this.node.lastChild); - } // Iterates over all children and invokes a given block + } // matches the element vs a css selector }, { - key: "each", - value: function each(block, deep) { - var children = this.children(); - var i, il; + key: "matches", + value: function matches(selector) { + return matcher(this.node, selector); + } // Returns the svg node to call native svg methods on it - for (i = 0, il = children.length; i < il; i++) { - if (children[i] instanceof Element) { - block.apply(children[i], [i, children]); - } + }, { + key: "native", + value: function native() { + return this.node; + } // Returns the parent element instance - if (deep && children[i] instanceof Parent) { - children[i].each(block, deep); - } + }, { + key: "parent", + value: function parent(type) { + var parent = this; // check for parent + + if (!parent.node.parentNode) return null; // get parent element + + parent = adopt(parent.node.parentNode); + if (!type) return parent; // loop trough ancestors if type is given + + while (parent && parent.node instanceof window.SVGElement) { + if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent; + parent = adopt(parent.node.parentNode); + } + } // Basically does the same as `add()` but returns the added element instead + + }, { + key: "put", + value: function put(element, i) { + this.add(element, i); + return element; + } // Add element to given container and return container + + }, { + key: "putIn", + value: function putIn(parent) { + return makeInstance(parent).add(this); + } // Remove element + + }, { + key: "remove", + value: function remove() { + if (this.parent()) { + this.parent().removeElement(this); } return this; @@ -1392,19 +1588,20 @@ var SVG = (function () { value: function removeElement(element) { this.node.removeChild(element.node); return this; - } // Remove all elements in this container + } // Replace element }, { - key: "clear", - value: function clear() { - // remove children - while (this.node.hasChildNodes()) { - this.node.removeChild(this.node.lastChild); - } // remove defs reference - + key: "replace", + value: function replace(element) { + // FIXME: after might not be available here + this.after(element).remove(); + return element; + } // Return id on string conversion - delete this._defs; - return this; + }, { + key: "toString", + value: function toString() { + return this.id(); } // Import raw svg }, { @@ -1434,49 +1631,177 @@ var SVG = (function () { }, { key: "writeDataToDom", value: function writeDataToDom() { - // dump variables recursively - this.each(function () { - this.writeDataToDom(); - }); // remove previously set data - + // dump variables recursively + this.each(function () { + this.writeDataToDom(); + }); + return this; + } + }]); + + return Dom; + }(EventTarget); + extend(Dom, { + attr: attr + }); + + var Doc = getClass(root); + + var Element = + /*#__PURE__*/ + function (_Dom) { + _inherits(Element, _Dom); + + function Element(node) { + var _this; + + _classCallCheck(this, Element); + + _this = _possibleConstructorReturn(this, _getPrototypeOf(Element).call(this, node)); // initialize data object + + _this.dom = {}; // create circular reference + + _this.node.instance = _assertThisInitialized(_assertThisInitialized(_this)); + + if (node.hasAttribute('svgjs:data')) { + // pull svgjs data from the dom (getAttributeNS doesn't work in html5) + _this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {}); + } + + return _this; + } // Move element by its center + + + _createClass(Element, [{ + key: "center", + value: function center(x, y) { + return this.cx(x).cy(y); + } // Move by center over x-axis + + }, { + key: "cx", + value: function cx(x) { + return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2); + } // Move by center over y-axis + + }, { + key: "cy", + value: function cy(y) { + return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2); + } // Get defs + + }, { + key: "defs", + value: function defs() { + return this.doc().defs(); + } // Get parent document + + }, { + key: "doc", + value: function doc() { + var p = this.parent(Doc); + return p && p.doc(); + } + }, { + key: "getEventHolder", + value: function getEventHolder() { + return this; + } // Set height of element + + }, { + key: "height", + value: function height(_height) { + return this.attr('height', _height); + } // Checks whether the given point inside the bounding box of the element + + }, { + key: "inside", + value: function inside(x, y) { + var box = this.bbox(); + return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height; + } // Move element to given x and y values + + }, { + key: "move", + value: function move(x, y) { + return this.x(x).y(y); + } // return array of all ancestors of given type up to the root svg + + }, { + key: "parents", + value: function parents(type) { + var parents = []; + var parent = this; + + do { + parent = parent.parent(type); + if (!parent || parent instanceof getClass('HtmlNode')) break; + parents.push(parent); + } while (parent.parent); + + return parents; + } // Get referenced element form attribute value + + }, { + key: "reference", + value: function reference$$1(attr) { + var id = idFromReference(this.attr(attr)); + return id ? makeInstance(id) : null; + } // set given data to the elements data property + + }, { + key: "setData", + value: function setData(o) { + this.dom = o; + return this; + } // Set element size to given width and height + + }, { + key: "size", + value: function size(width, height) { + var p = proportionalSize(this, width, height); + return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height)); + } // Set width of element + + }, { + key: "width", + value: function width(_width) { + return this.attr('width', _width); + } // write svgjs data to the dom + + }, { + key: "writeDataToDom", + value: function writeDataToDom() { + // remove previously set data this.node.removeAttribute('svgjs:data'); if (Object.keys(this.dom).length) { this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428 } - return this; - } + return _get(_getPrototypeOf(Element.prototype), "writeDataToDom", this).call(this); + } // Move over x-axis + }, { - key: "flatten", - value: function flatten(parent) { - this.each(function () { - if (this instanceof Parent) return this.flatten(parent).ungroup(parent); - return this.toParent(parent); - }); // we need this so that Doc does not get removed + key: "x", + value: function x(_x) { + return this.attr('x', _x); + } // Move over y-axis - this.node.firstElementChild || this.remove(); - return this; - } }, { - key: "ungroup", - value: function ungroup(parent) { - parent = parent || this.parent(); - this.each(function () { - return this.toParent(parent); - }); - this.remove(); - return this; + key: "y", + value: function y(_y) { + return this.attr('y', _y); } }]); - return Parent; - }(Element); // registerMethods('Container', { + return Element; + }(Dom); // registerMethods('Element', { var Shape = /*#__PURE__*/ - function (_Parent) { - _inherits(Shape, _Parent); + function (_Element) { + _inherits(Shape, _Element); function Shape() { _classCallCheck(this, Shape); @@ -1485,12 +1810,12 @@ var SVG = (function () { } return Shape; - }(Parent); + }(Element); var Container$1 = /*#__PURE__*/ - function (_Parent) { - _inherits(Container, _Parent); + function (_Element) { + _inherits(Container, _Element); function Container() { _classCallCheck(this, Container); @@ -1498,56 +1823,45 @@ var SVG = (function () { return _possibleConstructorReturn(this, _getPrototypeOf(Container).apply(this, arguments)); } + _createClass(Container, [{ + key: "flatten", + value: function flatten(parent) { + this.each(function () { + if (this instanceof Container) return this.flatten(parent).ungroup(parent); + return this.toParent(parent); + }); // we need this so that Doc does not get removed + + this.node.firstElementChild || this.remove(); + return this; + } + }, { + key: "ungroup", + value: function ungroup(parent) { + parent = parent || this.parent(); + this.each(function () { + return this.toParent(parent); + }); + this.remove(); + return this; + } + }]); + return Container; - }(Parent); + }(Element); var HtmlNode = /*#__PURE__*/ - function (_Parent) { - _inherits(HtmlNode, _Parent); - - function HtmlNode(element) { - var _this; + function (_Dom) { + _inherits(HtmlNode, _Dom); + function HtmlNode(node) { _classCallCheck(this, HtmlNode); - _this = _possibleConstructorReturn(this, _getPrototypeOf(HtmlNode).call(this, element, HtmlNode)); - _this.node = element; - return _this; + return _possibleConstructorReturn(this, _getPrototypeOf(HtmlNode).call(this, node, HtmlNode)); } - _createClass(HtmlNode, [{ - key: "add", - value: function add(element, i) { - element = makeInstance(element); - - if (element.node !== this.node.children[i]) { - this.node.insertBefore(element.node, this.node.children[i] || null); - } - - return this; - } - }, { - key: "put", - value: function put(element, i) { - this.add(element, i); - return element; - } - }, { - key: "removeElement", - value: function removeElement(element) { - this.node.removeChild(element.node); - return this; - } - }, { - key: "getEventTarget", - value: function getEventTarget() { - return this.node; - } - }]); - return HtmlNode; - }(Parent); + }(Dom); register(HtmlNode); var Defs = @@ -1848,8 +2162,8 @@ var SVG = (function () { var Bare = /*#__PURE__*/ - function (_Parent) { - _inherits(Bare, _Parent); + function (_Container) { + _inherits(Bare, _Container); function Bare(node) { _classCallCheck(this, Bare); @@ -1872,7 +2186,7 @@ var SVG = (function () { }]); return Bare; - }(Parent); + }(Container$1); register(Bare); registerMethods('Container', { // Create an element that is not described by SVG.js @@ -2014,7 +2328,7 @@ var SVG = (function () { function find$1(query) { return baseFind(query, this.node); } - registerMethods('Element', { + registerMethods('Dom', { find: find$1 }); @@ -2648,110 +2962,6 @@ var SVG = (function () { }); register(Image); - var subClassArray = function () { - try { - // try es6 subclassing - return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', '[name]: class extends baseClass {', 'constructor (...args) {', 'super(...args)', '_constructor && _constructor.apply(this, args)', '}', '}', '}[name]'].join('\n')); - } catch (e) { - // Use es5 approach - return function (name) { - var baseClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Array; - - var _constructor = arguments.length > 2 ? arguments[2] : undefined; - - var Arr = function Arr() { - baseClass.apply(this, arguments); - _constructor && _constructor.apply(this, arguments); - }; - - Arr.prototype = Object.create(baseClass.prototype); - Arr.prototype.constructor = Arr; - return Arr; - }; - } - }(); - - var SVGArray = subClassArray('SVGArray', Array, function () { - this.init.apply(this, arguments); - }); - extend(SVGArray, { - init: function init() { - //this.splice(0, this.length) - this.length = 0; - this.push.apply(this, _toConsumableArray(this.parse.apply(this, arguments))); - }, - toArray: function toArray() { - // const ret = [] - // ret.push(...this) - // return ret - return Array.prototype.concat.apply([], this); - }, - toString: function toString() { - return this.join(' '); - }, - // Flattens the array if needed - valueOf: function valueOf() { - var ret = []; - ret.push.apply(ret, _toConsumableArray(this)); - return ret; // return this.toArray() - }, - // Parse whitespace separated string - parse: function parse() { - var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - //array = array.valueOf() - // If already is an array, no need to parse it - if (array instanceof Array) return array; - return array.trim().split(delimiter).map(parseFloat); - }, - clone: function clone() { - return new this.constructor(this); - }, - toSet: function toSet() { - return new Set(this); - } - }); // export default class SVGArray extends BaseArray { - // constructor (...args) { - // super() - // this.init(...args) - // } - // - // init (array, fallback = []) { - // //this.splice(0, this.length) - // this.length = 0 - // this.push(...this.parse(array || fallback)) - // } - // - // toArray () { - // return [].concat(this) - // } - // - // toString () { - // return this.join(' ') - // } - // - // valueOf () { - // return this.toArray() - // } - // - // // Parse whitespace separated string - // parse (array) { - // array = array.valueOf() - // - // // if already is an array, no need to parse it - // if (Array.isArray(array)) return array - // - // return array.trim().split(delimiter).map(parseFloat) - // } - // - // clone () { - // return new this.constructor(this) - // } - // - // toSet () { - // return new Set(this) - // } - // } - var PointArray = subClassArray('PointArray', SVGArray); extend(PointArray, { // Convert array to string @@ -3922,69 +4132,23 @@ var SVG = (function () { function (_Container) { _inherits(_Symbol, _Container); - // Initialize node - function _Symbol(node) { - _classCallCheck(this, _Symbol); - - return _possibleConstructorReturn(this, _getPrototypeOf(_Symbol).call(this, nodeOrNew('symbol', node), _Symbol)); - } - - return _Symbol; - }(Container$1); - registerMethods({ - Container: { - symbol: function symbol() { - return this.put(new _Symbol()); - } - } - }); - register(_Symbol); - - function noop() {} // Default animation values - - var timeline = { - duration: 400, - ease: '>', - delay: 0 // Default attribute values - - }; - var attrs = { - // fill and stroke - 'fill-opacity': 1, - 'stroke-opacity': 1, - 'stroke-width': 0, - 'stroke-linejoin': 'miter', - 'stroke-linecap': 'butt', - fill: '#000000', - stroke: '#000000', - opacity: 1, - // position - x: 0, - y: 0, - cx: 0, - cy: 0, - // size - width: 0, - height: 0, - // radius - r: 0, - rx: 0, - ry: 0, - // gradient - offset: 0, - 'stop-opacity': 1, - 'stop-color': '#000000', - // text - 'font-size': 16, - 'font-family': 'Helvetica, Arial, sans-serif', - 'text-anchor': 'start' - }; - - var defaults = /*#__PURE__*/Object.freeze({ - noop: noop, - timeline: timeline, - attrs: attrs + // Initialize node + function _Symbol(node) { + _classCallCheck(this, _Symbol); + + return _possibleConstructorReturn(this, _getPrototypeOf(_Symbol).call(this, nodeOrNew('symbol', node), _Symbol)); + } + + return _Symbol; + }(Container$1); + registerMethods({ + Container: { + symbol: function symbol() { + return this.put(new _Symbol()); + } + } }); + register(_Symbol); // Create plain text node function plain(text) { @@ -4010,8 +4174,8 @@ var SVG = (function () { var Text = /*#__PURE__*/ - function (_Parent) { - _inherits(Text, _Parent); + function (_Shape) { + _inherits(Text, _Shape); // Initialize node function Text(node) { @@ -4178,7 +4342,7 @@ var SVG = (function () { }]); return Text; - }(Parent); + }(Shape); extend(Text, textable); registerMethods({ Container: { @@ -4282,8 +4446,8 @@ var SVG = (function () { var Tspan = /*#__PURE__*/ - function (_Parent) { - _inherits(Tspan, _Parent); + function (_Text) { + _inherits(Tspan, _Text); // Initialize node function Tspan(node) { @@ -4326,7 +4490,7 @@ var SVG = (function () { }]); return Tspan; - }(Parent); + }(Text); extend(Tspan, textable); registerMethods({ Tspan: { @@ -4895,109 +5059,6 @@ var SVG = (function () { } }); - var Color = - /*#__PURE__*/ - function () { - function Color() { - _classCallCheck(this, Color); - - this.init.apply(this, arguments); - } - - _createClass(Color, [{ - key: "init", - value: function init(color, g, b) { - var match; // initialize defaults - - this.r = 0; - this.g = 0; - this.b = 0; - if (!color) return; // parse color - - if (typeof color === 'string') { - if (isRgb.test(color)) { - // get rgb values - match = rgb.exec(color.replace(whitespace, '')); // parse numeric values - - this.r = parseInt(match[1]); - this.g = parseInt(match[2]); - this.b = parseInt(match[3]); - } else if (isHex.test(color)) { - // get hex values - match = hex.exec(fullHex(color)); // parse numeric values - - this.r = parseInt(match[1], 16); - this.g = parseInt(match[2], 16); - this.b = parseInt(match[3], 16); - } - } else if (Array.isArray(color)) { - this.r = color[0]; - this.g = color[1]; - this.b = color[2]; - } else if (_typeof(color) === 'object') { - this.r = color.r; - this.g = color.g; - this.b = color.b; - } else if (arguments.length === 3) { - this.r = color; - this.g = g; - this.b = b; - } - } // Default to hex conversion - - }, { - key: "toString", - value: function toString() { - return this.toHex(); - } - }, { - key: "toArray", - value: function toArray() { - return [this.r, this.g, this.b]; - } // Build hex value - - }, { - key: "toHex", - value: function toHex() { - return '#' + compToHex(Math.round(this.r)) + compToHex(Math.round(this.g)) + compToHex(Math.round(this.b)); - } // Build rgb value - - }, { - key: "toRgb", - value: function toRgb() { - return 'rgb(' + [this.r, this.g, this.b].join() + ')'; - } // Calculate true brightness - - }, { - key: "brightness", - value: function brightness() { - return this.r / 255 * 0.30 + this.g / 255 * 0.59 + this.b / 255 * 0.11; - } // Testers - // Test if given value is a color string - - }], [{ - key: "test", - value: function test(color) { - color += ''; - return isHex.test(color) || isRgb.test(color); - } // Test if given value is a rgb object - - }, { - key: "isRgb", - value: function isRgb$$1(color) { - return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number'; - } // Test if given value is a color - - }, { - key: "isColor", - value: function isColor(color) { - return this.isRgb(color) || this.test(color); - } - }]); - - return Color; - }(); - /*** Base Class ========== @@ -5781,41 +5842,47 @@ var SVG = (function () { var Runner = /*#__PURE__*/ - function () { + function (_EventTarget) { + _inherits(Runner, _EventTarget); + function Runner(options) { + var _this; + _classCallCheck(this, Runner); - // Store a unique id on the runner, so that we can identify it later - this.id = Runner.id++; // Ensure a default value + _this = _possibleConstructorReturn(this, _getPrototypeOf(Runner).call(this)); // Store a unique id on the runner, so that we can identify it later + + _this.id = Runner.id++; // Ensure a default value options = options == null ? timeline.duration : options; // Ensure that we get a controller options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables - this._element = null; - this._timeline = null; - this.done = false; - this._queue = []; // Work out the stepper and the duration + _this._element = null; + _this._timeline = null; + _this.done = false; + _this._queue = []; // Work out the stepper and the duration - this._duration = typeof options === 'number' && options; - this._isDeclarative = options instanceof Controller; - this._stepper = this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change + _this._duration = typeof options === 'number' && options; + _this._isDeclarative = options instanceof Controller; + _this._stepper = _this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change - this._history = {}; // Store the state of the runner + _this._history = {}; // Store the state of the runner - this.enabled = true; - this._time = 0; - this._last = 0; // Save transforms applied to this runner + _this.enabled = true; + _this._time = 0; + _this._last = 0; // Save transforms applied to this runner - this.transforms = new Matrix(); - this.transformId = 1; // Looping variables + _this.transforms = new Matrix(); + _this.transformId = 1; // Looping variables - this._haveReversed = false; - this._reverse = false; - this._loopsDone = 0; - this._swing = false; - this._wait = 0; - this._times = 1; + _this._haveReversed = false; + _this._reverse = false; + _this._loopsDone = 0; + _this._swing = false; + _this._wait = 0; + _this._times = 1; + return _this; } /* Runner Definitions @@ -6036,7 +6103,10 @@ var SVG = (function () { var justStarted = this._lastTime < 0 && this._time > 0; var justFinished = this._lastTime < this._time && this.time > duration; this._lastTime = this._time; - // Work out if the runner is finished set the done flag here so animations + + if (justStarted) { + this.fire('start', this); + } // Work out if the runner is finished set the done flag here so animations // know, that they are running in the last step (this is good for // transformations which can be merged) @@ -6050,15 +6120,18 @@ var SVG = (function () { this.transforms = new Matrix(); - var converged = this._run(declarative ? dt : position); // this.fire('step', this) + var converged = this._run(declarative ? dt : position); + this.fire('step', this); } // correct the done flag here // declaritive animations itself know when they converged - this.done = this.done || converged && declarative; // if (this.done) { - // this.fire('finish', this) - // } + this.done = this.done || converged && declarative; + + if (this.done) { + this.fire('finish', this); + } return this; } @@ -6217,7 +6290,7 @@ var SVG = (function () { }]); return Runner; - }(); + }(EventTarget); Runner.id = 0; var FakeRunner = function FakeRunner() { @@ -6299,14 +6372,14 @@ var SVG = (function () { }, { key: "merge", value: function merge() { - var _this = this; + var _this2 = this; var lastRunner = null; this.runners.forEach(function (runner, i) { if (lastRunner && runner.done && lastRunner.done) { - _this.remove(runner.id); + _this2.remove(runner.id); - _this.edit(lastRunner.id, runner.mergeWith(lastRunner)); + _this2.edit(lastRunner.id, runner.mergeWith(lastRunner)); } lastRunner = runner; @@ -6691,7 +6764,6 @@ var SVG = (function () { // export {default as Matrix} from './Matrix.js' // export {default as Morphable} from './Morphable.js' // export {default as SVGNumber} from './SVGNumber.js' - // export {default as Parent} from './Parent.js' // export {default as Path} from './Path.js' // export {default as PathArray} from './PathArray.js' // export {default as Pattern} from './Pattern.js' @@ -6712,9 +6784,9 @@ var SVG = (function () { var Classes = /*#__PURE__*/Object.freeze({ EventTarget: EventTarget, + Dom: Dom, Element: Element, Shape: Shape, - Parent: Parent, Container: Container$1, HtmlNode: HtmlNode, Doc: Doc$1, @@ -6760,98 +6832,6 @@ var SVG = (function () { Spring: Spring }); - function attr(attr, val, ns) { - // act as full getter - if (attr == null) { - // get an object of attributes - attr = {}; - val = this.node.attributes; - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = val[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var node = _step.value; - attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return != null) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - return attr; - } else if (Array.isArray(attr)) ; else if (_typeof(attr) === 'object') { - // apply every attribute individually if an object is passed - for (val in attr) { - this.attr(val, attr[val]); - } - } else if (val === null) { - // remove value - this.node.removeAttribute(attr); - } else if (val == null) { - // act as a getter if the first and only argument is not an object - val = this.node.getAttribute(attr); - return val == null ? attrs[attr] // FIXME: do we need to return defaults? - : isNumber.test(val) ? parseFloat(val) : val; - } else { - // convert image fill and stroke to patterns - if (attr === 'fill' || attr === 'stroke') { - if (isImage.test(val)) { - val = this.doc().defs().image(val); - } - } // FIXME: This is fine, but what about the lines above? - // How does attr know about image()? - - - while (typeof val.attrHook == 'function') { - val = val.attrHook(this, attr); - } // ensure correct numeric values (also accepts NaN and Infinity) - - - if (typeof val === 'number') { - val = new SVGNumber(val); - } else if (Color.isColor(val)) { - // ensure full hex color - val = new Color(val); - } else if (val.constructor === Array) { - // Check for plain arrays and parse array values - val = new SVGArray(val); - } // if the passed attribute is leading... - - - if (attr === 'leading') { - // ... call the leading method instead - if (this.leading) { - this.leading(val); - } - } else { - // set given attribute on node - typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString()); - } // rebuild if required - - - if (this.rebuild && (attr === 'font-size' || attr === 'x')) { - this.rebuild(); - } - } - - return this; - } - registerMethods('Element', { - attr: attr - }); - // ### This module adds backward / forward functionality to elements. function siblings() { @@ -6926,7 +6906,7 @@ var SVG = (function () { this.parent().add(element, i + 1); return this; } - registerMethods('Element', { + registerMethods('Dom', { siblings: siblings, position: position, next: next, @@ -6956,10 +6936,55 @@ var SVG = (function () { return this; } - registerMethods('Element', { + registerMethods('Dom', { data: data }); + function classes() { + var attr = this.attr('class'); + return attr == null ? [] : attr.trim().split(delimiter); + } // Return true if class exists on the node, false otherwise + + + function hasClass(name) { + return this.classes().indexOf(name) !== -1; + } // Add class to the node + + + function addClass(name) { + if (!this.hasClass(name)) { + var array = this.classes(); + array.push(name); + this.attr('class', array.join(' ')); + } + + return this; + } // Remove class from the node + + + function removeClass(name) { + if (this.hasClass(name)) { + this.attr('class', this.classes().filter(function (c) { + return c !== name; + }).join(' ')); + } + + return this; + } // Toggle the presence of a class on the node + + + function toggleClass(name) { + return this.hasClass(name) ? this.removeClass(name) : this.addClass(name); + } + + registerMethods('Dom', { + classes: classes, + hasClass: hasClass, + addClass: addClass, + removeClass: removeClass, + toggleClass: toggleClass + }); + // Dynamic style generator function css(style, val) { @@ -7040,7 +7065,7 @@ var SVG = (function () { function visible() { return this.css('display') !== 'none'; } - registerMethods('Element', { + registerMethods('Dom', { css: css, show: show, hide: hide, @@ -7147,7 +7172,7 @@ var SVG = (function () { function memory() { return this._memory = this._memory || {}; } - registerMethods('Element', { + registerMethods('Dom', { remember: remember, forget: forget, memory: memory @@ -7170,7 +7195,7 @@ var SVG = (function () { return this; } - if (typeof o === 'string' || Color.isRgb(o) || o && typeof o.fill === 'function') { + if (typeof o === 'string' || Color.isRgb(o) || o instanceof Element) { this.attr(m, o); } else { // set all attributes from sugar.fill and sugar.stroke list @@ -7184,7 +7209,7 @@ var SVG = (function () { return this; }; - registerMethods(['Element', 'Runner'], extension); + registerMethods(['Shape', 'Runner'], extension); }); registerMethods(['Element', 'Runner'], { // Let the user set the matrix directly @@ -7291,7 +7316,7 @@ var SVG = (function () { return new Point(this.node.getPointAtLength(length)); } }); - registerMethods(['Parent', 'Runner'], { + registerMethods(['Element', 'Runner'], { // Set font font: function font(a, v) { if (_typeof(a) === 'object') { @@ -7304,7 +7329,6 @@ var SVG = (function () { } }); - // import {extend} from './tools.js' var extend$1 = extend; extend$1([Doc$1, _Symbol, Image, Pattern, Marker], getMethodsFor('viewbox')); extend$1([Line, Polyline, Polygon, Path], getMethodsFor('marker')); @@ -7314,8 +7338,9 @@ var SVG = (function () { extend$1([Text, Tspan], getMethodsFor('Tspan')); extend$1([Rect, Ellipse, Circle, Gradient], getMethodsFor('radius')); extend$1(EventTarget, getMethodsFor('EventTarget')); + extend$1(Dom, getMethodsFor('Dom')); extend$1(Element, getMethodsFor('Element')); - extend$1(Element, getMethodsFor('Parent')); //extend(Classes.Element, getConstructor('Memory')) + extend$1(Shape, getMethodsFor('Shape')); //extend(Classes.Element, getConstructor('Memory')) extend$1(Container$1, getMethodsFor('Container')); registerMorphableType([SVGNumber, Color, Box$1, Matrix, SVGArray, PointArray, PathArray]); @@ -7331,7 +7356,7 @@ var SVG = (function () { SVG.regex = regex; // satisfy tests, fix later SVG.get = SVG; SVG.find = baseFind; - Object.assign(SVG, ns$1); // import Base from './Base.js' + Object.assign(SVG, ns$1); SVG.easing = easing; Object.assign(SVG, events); SVG.TransformBag = TransformBag; diff --git a/dist/svg.min.js b/dist/svg.min.js index 27a2e20..ed14bb2 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1 +1 @@ -var SVG=function(){"use strict";function l(t){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var n=0;nn.x&&e>n.y&&t=e.time?e.run():Et.timeouts.push(e),e!==n););for(var i=null,r=Et.frames.last();i!==r&&(i=Et.frames.shift());)i.run();Et.transforms.forEach(function(t){t()}),Et.nextDraw=Et.timeouts.first()||Et.frames.first()?window.requestAnimationFrame(Et._draw):null}},Nt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q(t,"string"==typeof t?null:t),e))}return r(e,_t),a(e,[{key:"words",value:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(document.createTextNode(t)),this}}]),e}();function Pt(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())}function Dt(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())}function zt(t){return null==t?this.attr("cx"):this.attr("cx",t)}function Rt(t){return null==t?this.attr("cy"):this.attr("cy",t)}function qt(t){return null==t?2*this.rx():this.rx(new mt(t).divide(2))}function Lt(t){return null==t?2*this.ry():this.ry(new mt(t).divide(2))}function Ft(t,e){var n=R(this,t,e);return this.rx(new mt(n.width).divide(2)).ry(new mt(n.height).divide(2))}et(Nt),at("Container",{element:function(t,e){return this.put(new Nt(t,e))}});var It=Object.freeze({rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)},x:Pt,y:Dt,cx:zt,cy:Rt,width:qt,height:Lt,size:Ft}),Xt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("circle",t),e))}return r(e,Ot),a(e,[{key:"radius",value:function(t){return this.attr("r",t)}},{key:"rx",value:function(t){return this.attr("r",t)}},{key:"ry",value:function(t){return this.rx(t)}}]),e}();function Yt(t,e){return kt((e||document).querySelectorAll(t),function(t){return tt(t)})}$(Xt,{x:Pt,y:Dt,cx:zt,cy:Rt,width:qt,height:Lt,size:Ft}),at({Element:{circle:function(t){return this.put(new Xt).radius(new mt(t).divide(2)).move(0,0)}}}),et(Xt),at("Element",{find:function(t){return Yt(t,this.node)}});var Ht=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("clipPath",t),e))}return r(e,At),a(e,[{key:"remove",value:function(){return this.targets().forEach(function(t){t.unclip()}),c(u(e.prototype),"remove",this).call(this)}},{key:"targets",value:function(){return Yt('svg [clip-path*="'+this.id()+'"]')}}]),e}();at({Container:{clip:function(){return this.defs().put(new Ht)}},Element:{clipWith:function(t){var e=t instanceof Ht?t:this.parent().clip().add(t);return this.attr("clip-path",'url("#'+e.id()+'")')},unclip:function(){return this.attr("clip-path",null)},clipper:function(){return this.reference("clip-path")}}}),et(Ht);var Bt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("a",t),e))}return r(e,At),a(e,[{key:"to",value:function(t){return this.attr("href",t,B)}},{key:"target",value:function(t){return this.attr("target",t)}}]),e}();at({Container:{link:function(t){return this.put(new Bt).to(t)}},Element:{linkTo:function(t){var e=new Bt;return"function"==typeof t?t.call(e,e):e.to(t),this.parent().put(e).put(this)}}}),et(Bt);var Gt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("ellipse",t),e))}return r(e,Ot),e}();$(Gt,It),at("Container",{ellipse:function(t,e){return this.put(new Gt).size(t,e).move(0,0)}}),et(Gt);var Vt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("stop",t),e))}return r(e,wt),a(e,[{key:"update",value:function(t){return("number"==typeof t||t instanceof mt)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new mt(t.offset)),this}}]),e}();et(Vt);var Qt=Object.freeze({from:function(t,e){return"radialGradient"===(this._element||this).type?this.attr({fx:new mt(t),fy:new mt(e)}):this.attr({x1:new mt(t),y1:new mt(e)})},to:function(t,e){return"radialGradient"===(this._element||this).type?this.attr({cx:new mt(t),cy:new mt(e)}):this.attr({x2:new mt(t),y2:new mt(e)})}});function Ut(){if(!Ut.nodes){var t=(new Mt).size(2,0).css({opacity:0,position:"absolute",left:"-100%",top:"-100%",overflow:"hidden"}),e=t.path().node;Ut.nodes={svg:t,path:e}}if(!Ut.nodes.svg.node.parentNode){var n=document.body||document.documentElement;Ut.nodes.svg.addTo(n)}return Ut.nodes}var $t=function(){function r(t,e,n){var i;o(this,r),n=n||{x:0,y:0},i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"===l(t)?{x:t.x,y:t.y}:{x:t,y:e},this.x=null==i.x?n.x:i.x,this.y=null==i.y?n.y:i.y}return a(r,[{key:"clone",value:function(){return new r(this)}},{key:"native",value:function(){var t=Ut().svg.node.createSVGPoint();return t.x=this.x,t.y=this.y,t}},{key:"transform",value:function(t){return new r(t.a*this.x+t.c*this.y+t.e,t.b*this.x+t.d*this.y+t.f)}}]),r}();at({Element:{point:function(t,e){return new $t(t,e).transform(this.screenCTM().inverse())}}});var Jt=function(){function u(){o(this,u),this.init.apply(this,arguments)}return a(u,[{key:"init",value:function(t){var e;t="string"==typeof t?t.split(j).map(parseFloat):Array.isArray(t)?t:"object"===l(t)?[null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height]:4===arguments.length?[].slice.call(arguments):[0,0,0,0],this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3],null==(e=this).x&&(e.x=0,e.y=0,e.width=0,e.height=0),e.w=e.width,e.h=e.height,e.x2=e.x+e.width,e.y2=e.y+e.height,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2}},{key:"merge",value:function(t){var e=Math.min(this.x,t.x),n=Math.min(this.y,t.y);return new u(e,n,Math.max(this.x+this.width,t.x+t.width)-e,Math.max(this.y+this.height,t.y+t.height)-n)}},{key:"transform",value:function(e){var n=1/0,i=-1/0,r=1/0,s=-1/0;return[new $t(this.x,this.y),new $t(this.x2,this.y),new $t(this.x,this.y2),new $t(this.x2,this.y2)].forEach(function(t){t=t.transform(e),n=Math.min(n,t.x),i=Math.max(i,t.x),r=Math.min(r,t.y),s=Math.max(s,t.y)}),new u(n,r,i-n,s-r)}},{key:"addOffset",value:function(){return this.x+=window.pageXOffset,this.y+=window.pageYOffset,this}},{key:"toString",value:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}},{key:"toArray",value:function(){return[this.x,this.y,this.width,this.height]}}]),u}();function Wt(e){var n,t,i;try{if(n=e(this.node),!((i=n).w||i.h||i.x||i.y||(t=this.node,(document.documentElement.contains||function(t){for(;t.parentNode;)t=t.parentNode;return t===document}).call(document.documentElement,t))))throw new Error("Element not in the dom")}catch(t){try{var r=this.clone(Ut().svg).show();n=e(r.node),r.remove()}catch(t){console.warn("Getting a bounding box of this element is not possible")}}return n}at({Element:{bbox:function(){return new Jt(Wt.call(this,function(t){return t.getBBox()}))},rbox:function(t){var e=new Jt(Wt.call(this,function(t){return t.getBoundingClientRect()}));return t?e.transform(t.screenCTM().inverse()):e.addOffset()}},viewbox:{viewbox:function(t,e,n,i){return null==t?new Jt(this.attr("viewBox")):this.attr("viewBox",new Jt(t,e,n,i))}}});var Zt=function(t){function i(t){return o(this,i),h(this,u(i).call(this,Q(t+"Gradient","string"==typeof t?null:t),i))}return r(i,At),a(i,[{key:"stop",value:function(t,e,n){return this.put(new Vt).update(t,e,n)}},{key:"update",value:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this}},{key:"url",value:function(){return"url(#"+this.id()+")"}},{key:"toString",value:function(){return this.url()}},{key:"attr",value:function(t,e,n){return"transform"===t&&(t="gradientTransform"),c(u(i.prototype),"attr",this).call(this,t,e,n)}},{key:"targets",value:function(){return find('svg [fill*="'+this.id()+'"]')}},{key:"bbox",value:function(){return new Jt}}]),i}();$(Zt,Qt),at({Container:{gradient:function(t,e){return this.defs().gradient(t,e)}},Defs:{gradient:function(t,e){return this.put(new Zt(t)).update(e)}}}),et(Zt);var Kt=function(t){function i(t){return o(this,i),h(this,u(i).call(this,Q("pattern",t),i))}return r(i,At),a(i,[{key:"url",value:function(){return"url(#"+this.id()+")"}},{key:"update",value:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this}},{key:"toString",value:function(){return this.url()}},{key:"attr",value:function(t,e,n){return"transform"===t&&(t="patternTransform"),c(u(i.prototype),"attr",this).call(this,t,e,n)}},{key:"targets",value:function(){return find('svg [fill*="'+this.id()+'"]')}},{key:"bbox",value:function(){return new Jt}}]),i}();at({Container:{pattern:function(t,e,n){return this.defs().pattern(t,e,n)}},Defs:{pattern:function(t,e,n){return this.put(new Kt).update(n).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}}),et(Kt);var te=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("image",t),e))}return r(e,Ot),a(e,[{key:"load",value:function(n,i){if(!n)return this;var r=new window.Image;return ft(r,"load",function(t){var e=this.parent(Kt);0===this.width()&&0===this.height()&&this.size(r.width,r.height),e instanceof Kt&&0===e.width()&&0===e.height()&&e.size(this.width(),this.height()),"function"==typeof i&&i.call(this,{width:r.width,height:r.height,ratio:r.width/r.height,url:n})},this),ft(r,"load error",function(){dt(r)}),this.attr("href",r.src=n,B)}},{key:"attrHook",value:function(t){var e=this;return t.doc().defs().pattern(0,0,function(t){t.add(e)})}}]),e}();at({Container:{image:function(t,e){return this.put(new te).size(0,0).load(t,e)}}}),et(te);var ee=function(){try{return Function("name","baseClass","_constructor",["baseClass = baseClass || Array","return {","[name]: class extends baseClass {","constructor (...args) {","super(...args)","_constructor && _constructor.apply(this, args)","}","}","}[name]"].join("\n"))}catch(t){return function(t){var e=1",delay:0},xe={"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},_e=Object.freeze({noop:ke,timeline:be,attrs:xe});var Oe=Object.freeze({plain:function(t){return!1===this._build&&this.clear(),this.node.appendChild(document.createTextNode(t)),this},length:function(){return this.node.getComputedTextLength()}}),Ae=function(t){function n(t){var e;return o(this,n),(e=h(this,u(n).call(this,Q("text",t),n))).dom.leading=new mt(1.3),e._rebuild=!0,e._build=!1,e.attr("font-family",xe["font-family"]),e}return r(n,_t),a(n,[{key:"x",value:function(t){return null==t?this.attr("x"):this.attr("x",t)}},{key:"y",value:function(t){var e=this.attr("y"),n="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-n:e:this.attr("y","number"==typeof t?t+n:t)}},{key:"cx",value:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)}},{key:"cy",value:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)}},{key:"text",value:function(t){if(void 0===t){var e=this.node.childNodes,n=0;t="";for(var i=0,r=e.length;i":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return 1-Math.cos(t*Math.PI/2)},bezier:function(t,e,n,i){return function(t){}}},Pe=function(){function t(){o(this,t)}return a(t,[{key:"done",value:function(){return!1}}]),t}(),De=function(t){function n(t){var e;return o(this,n),(e=h(this,u(n).call(this))).ease=Ne[t||be.ease]||t,e}return r(n,Pe),a(n,[{key:"step",value:function(t,e,n){return"number"!=typeof t?n<1?t:e:t+(e-t)*this.ease(n)}}]),n}(),ze=function(t){function n(t){var e;return o(this,n),(e=h(this,u(n).call(this))).stepper=t,e}return r(n,Pe),a(n,[{key:"step",value:function(t,e,n,i){return this.stepper(t,e,n,i)}},{key:"done",value:function(t){return t.done}}]),n}();function Re(){var t=(this._duration||500)/1e3,e=this._overshoot||0,n=Math.PI,i=Math.log(e/100+1e-10),r=-i/Math.sqrt(n*n+i*i),s=3.9/(r*t);this.d=2*r*s,this.k=s*s}var qe=function(t){function i(t,e){var n;return o(this,i),(n=h(this,u(i).call(this))).duration(t||500).overshoot(e||0),n}return r(i,ze),a(i,[{key:"step",value:function(t,e,n,i){if("string"==typeof t)return t;if(i.done=n===1/0,n===1/0)return e;if(0===n)return t;100i);this._lastTime=this._time;var s=this._isDeclarative;if(this.done=!s&&!r&&this._time>=i,n||s){this._initialise(n),this.transforms=new Se;var u=this._run(s?t:e)}return this.done=this.done||u&&s,this}},{key:"finish",value:function(){return this.step(1/0)}},{key:"reverse",value:function(t){return this._reverse=null==t?!this._reverse:t,this}},{key:"ease",value:function(t){return this._stepper=new De(t),this}},{key:"active",value:function(t){return null==t?this.enabled:(this.enabled=t,this)}},{key:"_rememberMorpher",value:function(t,e){this._history[t]={morpher:e,caller:this._queue[this._queue.length-1]}}},{key:"_tryRetarget",value:function(t,e){if(this._history[t]){if(!this._history[t].caller.initialised){var n=this._queue.indexOf(this._history[t].caller);return this._queue.splice(n,1),!1}this._history[t].caller.isTransform?this._history[t].caller.isTransform(e):this._history[t].morpher.to(e),this._history[t].caller.finished=!1;var i=this.timeline();return i&&i._continue(),!0}return!1}},{key:"_initialise",value:function(t){if(t||this._isDeclarative)for(var e=0,n=this._queue.length;e",delay:0},_t={"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},Ot=Object.freeze({noop:bt,timeline:xt,attrs:_t}),At=function(){function t(){o(this,t),this.init.apply(this,arguments)}return a(t,[{key:"init",value:function(t,e,n){var i,r;(this.r=0,this.g=0,this.b=0,t)&&("string"==typeof t?k.test(t)?(i=p.exec(t.replace(g,"")),this.r=parseInt(i[1]),this.g=parseInt(i[2]),this.b=parseInt(i[3])):w.test(t)&&(i=y.exec(4===(r=t).length?["#",r.substring(1,2),r.substring(1,2),r.substring(2,3),r.substring(2,3),r.substring(3,4),r.substring(3,4)].join(""):r),this.r=parseInt(i[1],16),this.g=parseInt(i[2],16),this.b=parseInt(i[3],16)):Array.isArray(t)?(this.r=t[0],this.g=t[1],this.b=t[2]):"object"===l(t)?(this.r=t.r,this.g=t.g,this.b=t.b):3===arguments.length&&(this.r=t,this.g=e,this.b=n))}},{key:"toString",value:function(){return this.toHex()}},{key:"toArray",value:function(){return[this.r,this.g,this.b]}},{key:"toHex",value:function(){return"#"+z(Math.round(this.r))+z(Math.round(this.g))+z(Math.round(this.b))}},{key:"toRgb",value:function(){return"rgb("+[this.r,this.g,this.b].join()+")"}},{key:"brightness",value:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11}}],[{key:"test",value:function(t){return t+="",w.test(t)||k.test(t)}},{key:"isRgb",value:function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b}},{key:"isColor",value:function(t){return this.isRgb(t)||this.test(t)}}]),t}(),Ct=function(){try{return Function("name","baseClass","_constructor",["baseClass = baseClass || Array","return {","[name]: class extends baseClass {","constructor (...args) {","super(...args)","_constructor && _constructor.apply(this, args)","}","}","}[name]"].join("\n"))}catch(t){return function(t){var e=1n.x&&e>n.y&&t=e.time?e.run():Ft.timeouts.push(e),e!==n););for(var i=null,r=Ft.frames.last();i!==r&&(i=Ft.frames.shift());)i.run();Ft.transforms.forEach(function(t){t()}),Ft.nextDraw=Ft.timeouts.first()||Ft.frames.first()?window.requestAnimationFrame(Ft._draw):null}},It=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q(t,"string"==typeof t?null:t),e))}return r(e,Dt),a(e,[{key:"words",value:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(document.createTextNode(t)),this}}]),e}();function Xt(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())}function Yt(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())}function Ht(t){return null==t?this.attr("cx"):this.attr("cx",t)}function Gt(t){return null==t?this.attr("cy"):this.attr("cy",t)}function Vt(t){return null==t?2*this.rx():this.rx(new Mt(t).divide(2))}function Bt(t){return null==t?2*this.ry():this.ry(new Mt(t).divide(2))}function Qt(t,e){var n=R(this,t,e);return this.rx(new Mt(n.width).divide(2)).ry(new Mt(n.height).divide(2))}et(It),at("Container",{element:function(t,e){return this.put(new It(t,e))}});var Ut=Object.freeze({rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)},x:Xt,y:Yt,cx:Ht,cy:Gt,width:Vt,height:Bt,size:Qt}),$t=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("circle",t),e))}return r(e,Nt),a(e,[{key:"radius",value:function(t){return this.attr("r",t)}},{key:"rx",value:function(t){return this.attr("r",t)}},{key:"ry",value:function(t){return this.rx(t)}}]),e}();function Wt(t,e){return gt((e||document).querySelectorAll(t),function(t){return tt(t)})}$($t,{x:Xt,y:Yt,cx:Ht,cy:Gt,width:Vt,height:Bt,size:Qt}),at({Element:{circle:function(t){return this.put(new $t).radius(new Mt(t).divide(2)).move(0,0)}}}),et($t),at("Dom",{find:function(t){return Wt(t,this.node)}});var Jt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("clipPath",t),e))}return r(e,Dt),a(e,[{key:"remove",value:function(){return this.targets().forEach(function(t){t.unclip()}),c(u(e.prototype),"remove",this).call(this)}},{key:"targets",value:function(){return Wt('svg [clip-path*="'+this.id()+'"]')}}]),e}();at({Container:{clip:function(){return this.defs().put(new Jt)}},Element:{clipWith:function(t){var e=t instanceof Jt?t:this.parent().clip().add(t);return this.attr("clip-path",'url("#'+e.id()+'")')},unclip:function(){return this.attr("clip-path",null)},clipper:function(){return this.reference("clip-path")}}}),et(Jt);var Zt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("a",t),e))}return r(e,Dt),a(e,[{key:"to",value:function(t){return this.attr("href",t,G)}},{key:"target",value:function(t){return this.attr("target",t)}}]),e}();at({Container:{link:function(t){return this.put(new Zt).to(t)}},Element:{linkTo:function(t){var e=new Zt;return"function"==typeof t?t.call(e,e):e.to(t),this.parent().put(e).put(this)}}}),et(Zt);var Kt=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("ellipse",t),e))}return r(e,Nt),e}();$(Kt,Ut),at("Container",{ellipse:function(t,e){return this.put(new Kt).size(t,e).move(0,0)}}),et(Kt);var te=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("stop",t),e))}return r(e,Et),a(e,[{key:"update",value:function(t){return("number"==typeof t||t instanceof Mt)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new Mt(t.offset)),this}}]),e}();et(te);var ee=Object.freeze({from:function(t,e){return"radialGradient"===(this._element||this).type?this.attr({fx:new Mt(t),fy:new Mt(e)}):this.attr({x1:new Mt(t),y1:new Mt(e)})},to:function(t,e){return"radialGradient"===(this._element||this).type?this.attr({cx:new Mt(t),cy:new Mt(e)}):this.attr({x2:new Mt(t),y2:new Mt(e)})}});function ne(){if(!ne.nodes){var t=(new Rt).size(2,0).css({opacity:0,position:"absolute",left:"-100%",top:"-100%",overflow:"hidden"}),e=t.path().node;ne.nodes={svg:t,path:e}}if(!ne.nodes.svg.node.parentNode){var n=document.body||document.documentElement;ne.nodes.svg.addTo(n)}return ne.nodes}var ie=function(){function r(t,e,n){var i;o(this,r),n=n||{x:0,y:0},i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"===l(t)?{x:t.x,y:t.y}:{x:t,y:e},this.x=null==i.x?n.x:i.x,this.y=null==i.y?n.y:i.y}return a(r,[{key:"clone",value:function(){return new r(this)}},{key:"native",value:function(){var t=ne().svg.node.createSVGPoint();return t.x=this.x,t.y=this.y,t}},{key:"transform",value:function(t){return new r(t.a*this.x+t.c*this.y+t.e,t.b*this.x+t.d*this.y+t.f)}}]),r}();at({Element:{point:function(t,e){return new ie(t,e).transform(this.screenCTM().inverse())}}});var re=function(){function u(){o(this,u),this.init.apply(this,arguments)}return a(u,[{key:"init",value:function(t){var e;t="string"==typeof t?t.split(C).map(parseFloat):Array.isArray(t)?t:"object"===l(t)?[null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height]:4===arguments.length?[].slice.call(arguments):[0,0,0,0],this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3],null==(e=this).x&&(e.x=0,e.y=0,e.width=0,e.height=0),e.w=e.width,e.h=e.height,e.x2=e.x+e.width,e.y2=e.y+e.height,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2}},{key:"merge",value:function(t){var e=Math.min(this.x,t.x),n=Math.min(this.y,t.y);return new u(e,n,Math.max(this.x+this.width,t.x+t.width)-e,Math.max(this.y+this.height,t.y+t.height)-n)}},{key:"transform",value:function(e){var n=1/0,i=-1/0,r=1/0,s=-1/0;return[new ie(this.x,this.y),new ie(this.x2,this.y),new ie(this.x,this.y2),new ie(this.x2,this.y2)].forEach(function(t){t=t.transform(e),n=Math.min(n,t.x),i=Math.max(i,t.x),r=Math.min(r,t.y),s=Math.max(s,t.y)}),new u(n,r,i-n,s-r)}},{key:"addOffset",value:function(){return this.x+=window.pageXOffset,this.y+=window.pageYOffset,this}},{key:"toString",value:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}},{key:"toArray",value:function(){return[this.x,this.y,this.width,this.height]}}]),u}();function se(e){var n,t,i;try{if(n=e(this.node),!((i=n).w||i.h||i.x||i.y||(t=this.node,(document.documentElement.contains||function(t){for(;t.parentNode;)t=t.parentNode;return t===document}).call(document.documentElement,t))))throw new Error("Element not in the dom")}catch(t){try{var r=this.clone(ne().svg).show();n=e(r.node),r.remove()}catch(t){console.warn("Getting a bounding box of this element is not possible")}}return n}at({Element:{bbox:function(){return new re(se.call(this,function(t){return t.getBBox()}))},rbox:function(t){var e=new re(se.call(this,function(t){return t.getBoundingClientRect()}));return t?e.transform(t.screenCTM().inverse()):e.addOffset()}},viewbox:{viewbox:function(t,e,n,i){return null==t?new re(this.attr("viewBox")):this.attr("viewBox",new re(t,e,n,i))}}});var ue=function(t){function i(t){return o(this,i),h(this,u(i).call(this,Q(t+"Gradient","string"==typeof t?null:t),i))}return r(i,Dt),a(i,[{key:"stop",value:function(t,e,n){return this.put(new te).update(t,e,n)}},{key:"update",value:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this}},{key:"url",value:function(){return"url(#"+this.id()+")"}},{key:"toString",value:function(){return this.url()}},{key:"attr",value:function(t,e,n){return"transform"===t&&(t="gradientTransform"),c(u(i.prototype),"attr",this).call(this,t,e,n)}},{key:"targets",value:function(){return find('svg [fill*="'+this.id()+'"]')}},{key:"bbox",value:function(){return new re}}]),i}();$(ue,ee),at({Container:{gradient:function(t,e){return this.defs().gradient(t,e)}},Defs:{gradient:function(t,e){return this.put(new ue(t)).update(e)}}}),et(ue);var oe=function(t){function i(t){return o(this,i),h(this,u(i).call(this,Q("pattern",t),i))}return r(i,Dt),a(i,[{key:"url",value:function(){return"url(#"+this.id()+")"}},{key:"update",value:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this}},{key:"toString",value:function(){return this.url()}},{key:"attr",value:function(t,e,n){return"transform"===t&&(t="patternTransform"),c(u(i.prototype),"attr",this).call(this,t,e,n)}},{key:"targets",value:function(){return find('svg [fill*="'+this.id()+'"]')}},{key:"bbox",value:function(){return new re}}]),i}();at({Container:{pattern:function(t,e,n){return this.defs().pattern(t,e,n)}},Defs:{pattern:function(t,e,n){return this.put(new oe).update(n).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}}),et(oe);var ae=function(t){function e(t){return o(this,e),h(this,u(e).call(this,Q("image",t),e))}return r(e,Nt),a(e,[{key:"load",value:function(n,i){if(!n)return this;var r=new window.Image;return dt(r,"load",function(t){var e=this.parent(oe);0===this.width()&&0===this.height()&&this.size(r.width,r.height),e instanceof oe&&0===e.width()&&0===e.height()&&e.size(this.width(),this.height()),"function"==typeof i&&i.call(this,{width:r.width,height:r.height,ratio:r.width/r.height,url:n})},this),dt(r,"load error",function(){vt(r)}),this.attr("href",r.src=n,G)}},{key:"attrHook",value:function(t){var e=this;return t.doc().defs().pattern(0,0,function(t){t.add(e)})}}]),e}();at({Container:{image:function(t,e){return this.put(new ae).size(0,0).load(t,e)}}}),et(ae);var he=Ct("PointArray",jt);$(he,{toString:function(){for(var t=0,e=this.length,n=[];t":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return 1-Math.cos(t*Math.PI/2)},bezier:function(t,e,n,i){return function(t){}}},Pe=function(){function t(){o(this,t)}return a(t,[{key:"done",value:function(){return!1}}]),t}(),ze=function(t){function n(t){var e;return o(this,n),(e=h(this,u(n).call(this))).ease=De[t||xt.ease]||t,e}return r(n,Pe),a(n,[{key:"step",value:function(t,e,n){return"number"!=typeof t?n<1?t:e:t+(e-t)*this.ease(n)}}]),n}(),Re=function(t){function n(t){var e;return o(this,n),(e=h(this,u(n).call(this))).stepper=t,e}return r(n,Pe),a(n,[{key:"step",value:function(t,e,n,i){return this.stepper(t,e,n,i)}},{key:"done",value:function(t){return t.done}}]),n}();function qe(){var t=(this._duration||500)/1e3,e=this._overshoot||0,n=Math.PI,i=Math.log(e/100+1e-10),r=-i/Math.sqrt(n*n+i*i),s=3.9/(r*t);this.d=2*r*s,this.k=s*s}var Le=function(t){function i(t,e){var n;return o(this,i),(n=h(this,u(i).call(this))).duration(t||500).overshoot(e||0),n}return r(i,Re),a(i,[{key:"step",value:function(t,e,n,i){if("string"==typeof t)return t;if(i.done=n===1/0,n===1/0)return e;if(0===n)return t;100i;this._lastTime=this._time,r&&this.fire("start",this);var u=this._isDeclarative;if(this.done=!u&&!s&&this._time>=i,n||u){this._initialise(n),this.transforms=new Ee;var o=this._run(u?t:e);this.fire("step",this)}return this.done=this.done||o&&u,this.done&&this.fire("finish",this),this}},{key:"finish",value:function(){return this.step(1/0)}},{key:"reverse",value:function(t){return this._reverse=null==t?!this._reverse:t,this}},{key:"ease",value:function(t){return this._stepper=new ze(t),this}},{key:"active",value:function(t){return null==t?this.enabled:(this.enabled=t,this)}},{key:"_rememberMorpher",value:function(t,e){this._history[t]={morpher:e,caller:this._queue[this._queue.length-1]}}},{key:"_tryRetarget",value:function(t,e){if(this._history[t]){if(!this._history[t].caller.initialised){var n=this._queue.indexOf(this._history[t].caller);return this._queue.splice(n,1),!1}this._history[t].caller.isTransform?this._history[t].caller.isTransform(e):this._history[t].morpher.to(e),this._history[t].caller.finished=!1;var i=this.timeline();return i&&i._continue(),!0}return!1}},{key:"_initialise",value:function(t){if(t||this._isDeclarative)for(var e=0,n=this._queue.length;e= 0 + } + + // Get / set id + id (id) { + // generate new id if no id set + if (typeof id === 'undefined' && !this.node.id) { + this.node.id = eid(this.type) + } + + // dont't set directly width this.node.id to make `null` work correctly + return this.attr('id', id) + } + + // Gets index of given element + index (element) { + return [].slice.call(this.node.childNodes).indexOf(element.node) + } + + // Get the last child + last () { + return adopt(this.node.lastChild) + } + + // matches the element vs a css selector + matches (selector) { + return matcher(this.node, selector) + } + + // Returns the svg node to call native svg methods on it + native () { + return this.node + } + + // Returns the parent element instance + parent (type) { + var parent = this + + // check for parent + if (!parent.node.parentNode) return null + + // get parent element + parent = adopt(parent.node.parentNode) + + if (!type) return parent + + // loop trough ancestors if type is given + while (parent && parent.node instanceof window.SVGElement) { + if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent + parent = adopt(parent.node.parentNode) + } + } + + // Basically does the same as `add()` but returns the added element instead + put (element, i) { + this.add(element, i) + return element + } + + // Add element to given container and return container + putIn (parent) { + return makeInstance(parent).add(this) + } + + // Remove element + remove () { + if (this.parent()) { + this.parent().removeElement(this) + } + + return this + } + + // Remove a given child + removeElement (element) { + this.node.removeChild(element.node) + + return this + } + + // Replace element + replace (element) { + // FIXME: after might not be available here + this.after(element).remove() + + return element + } + + // Return id on string conversion + toString () { + return this.id() + } + + // Import raw svg + svg (svg) { + var well, len + + // act as a setter if svg is given + if (svg) { + // create temporary holder + well = document.createElementNS(ns, 'svg') + // dump raw svg + well.innerHTML = svg + + // transplant nodes + for (len = well.children.length; len--;) { + this.node.appendChild(well.firstElementChild) + } + + // otherwise act as a getter + } else { + // write svgjs data to the dom + this.writeDataToDom() + + return this.node.outerHTML + } + + return this + } + + // write svgjs data to the dom + writeDataToDom () { + // dump variables recursively + this.each(function () { + this.writeDataToDom() + }) + + return this + } +} + +import {extend} from './tools.js' +import attr from './attr.js' +extend(Dom, {attr}) diff --git a/src/Element.js b/src/Element.js index 5e798ff..e7944fc 100644 --- a/src/Element.js +++ b/src/Element.js @@ -1,27 +1,20 @@ import {proportionalSize, matcher, idFromReference} from './helpers.js' -import {makeInstance, adopt, assignNewId, eid, root, getClass} from './adopter.js' +import {makeInstance, root, getClass} from './adopter.js' import {delimiter} from './regex.js' import {ns} from './namespaces.js' import SVGNumber from './SVGNumber.js' -import {registerMethods} from './methods.js' -import {registerConstructor} from './methods.js' -import EventTarget from './EventTarget.js' +import Dom from './Dom.js' const Doc = getClass(root) -//export const name = 'Element' - -export default class Element extends EventTarget { +export default class Element extends Dom { constructor (node) { - super() + super(node) - // initialize data object + // initialize data object this.dom = {} - // create circular reference - this.node = node - - this.type = node.nodeName + // create circular reference this.node.instance = this if (node.hasAttribute('svgjs:data')) { @@ -30,14 +23,9 @@ export default class Element extends EventTarget { } } - // Move over x-axis - x (x) { - return this.attr('x', x) - } - - // Move over y-axis - y (y) { - return this.attr('y', y) + // Move element by its center + center (x, y) { + return this.cx(x).cy(y) } // Move by center over x-axis @@ -52,19 +40,19 @@ export default class Element extends EventTarget { : this.y(y - this.height() / 2) } - // Move element to given x and y values - move (x, y) { - return this.x(x).y(y) + // Get defs + defs () { + return this.doc().defs() } - // Move element by its center - center (x, y) { - return this.cx(x).cy(y) + // Get parent document + doc () { + let p = this.parent(Doc) + return p && p.doc() } - // Set width of element - width (width) { - return this.attr('width', width) + getEventHolder () { + return this } // Set height of element @@ -72,65 +60,6 @@ export default class Element extends EventTarget { return this.attr('height', height) } - // Set element size to given width and height - size (width, height) { - let p = proportionalSize(this, width, height) - - return this - .width(new SVGNumber(p.width)) - .height(new SVGNumber(p.height)) - } - - // Clone element - clone (parent) { - // write dom data to the dom so the clone can pickup the data - this.writeDataToDom() - - // clone element and assign new id - let clone = assignNewId(this.node.cloneNode(true)) - - // insert the clone in the given parent or after myself - if (parent) parent.add(clone) - else this.after(clone) - - return clone - } - - // Remove element - remove () { - if (this.parent()) { this.parent().removeElement(this) } - - return this - } - - // Replace element - replace (element) { - this.after(element).remove() - - return element - } - - // Add element to given container and return self - addTo (parent) { - return makeInstance(parent).put(this) - } - - // Add element to given container and return container - putIn (parent) { - return makeInstance(parent).add(this) - } - - // Get / set id - id (id) { - // generate new id if no id set - if (typeof id === 'undefined' && !this.node.id) { - this.node.id = eid(this.type) - } - - // dont't set directly width this.node.id to make `null` work correctly - return this.attr('id', id) - } - // Checks whether the given point inside the bounding box of the element inside (x, y) { let box = this.bbox() @@ -141,83 +70,9 @@ export default class Element extends EventTarget { y < box.y + box.height } - // Return id on string conversion - toString () { - return this.id() - } - - // Return array of classes on the node - classes () { - var attr = this.attr('class') - return attr == null ? [] : attr.trim().split(delimiter) - } - - // Return true if class exists on the node, false otherwise - hasClass (name) { - return this.classes().indexOf(name) !== -1 - } - - // Add class to the node - addClass (name) { - if (!this.hasClass(name)) { - var array = this.classes() - array.push(name) - this.attr('class', array.join(' ')) - } - - return this - } - - // Remove class from the node - removeClass (name) { - if (this.hasClass(name)) { - this.attr('class', this.classes().filter(function (c) { - return c !== name - }).join(' ')) - } - - return this - } - - // Toggle the presence of a class on the node - toggleClass (name) { - return this.hasClass(name) ? this.removeClass(name) : this.addClass(name) - } - - // Get referenced element form attribute value - reference (attr) { - let id = idFromReference(this.attr(attr)) - return id ? makeInstance(id) : null - } - - // Returns the parent element instance - parent (type) { - var parent = this - - // check for parent - if (!parent.node.parentNode) return null - - // get parent element - parent = adopt(parent.node.parentNode) - - if (!type) return parent - - // loop trough ancestors if type is given - while (parent && parent.node instanceof window.SVGElement) { - if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent - parent = adopt(parent.node.parentNode) - } - } - - // Get parent document - doc () { - let p = this.parent(Doc) - return p && p.doc() - } - - // Get defs - defs () { - return this.doc().defs() + // Move element to given x and y values + move (x, y) { + return this.x(x).y(y) } // return array of all ancestors of given type up to the root svg @@ -235,22 +90,30 @@ export default class Element extends EventTarget { return parents } - // matches the element vs a css selector - matches (selector) { - return matcher(this.node, selector) + // Get referenced element form attribute value + reference (attr) { + let id = idFromReference(this.attr(attr)) + return id ? makeInstance(id) : null } - // Returns the svg node to call native svg methods on it - native () { - return this.node + // set given data to the elements data property + setData (o) { + this.dom = o + return this } - // Import raw svg - svg () { - // write svgjs data to the dom - this.writeDataToDom() + // Set element size to given width and height + size (width, height) { + let p = proportionalSize(this, width, height) - return this.node.outerHTML + return this + .width(new SVGNumber(p.width)) + .height(new SVGNumber(p.height)) + } + + // Set width of element + width (width) { + return this.attr('width', width) } // write svgjs data to the dom @@ -261,17 +124,18 @@ export default class Element extends EventTarget { if (Object.keys(this.dom).length) { this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428 } - return this + + return super.writeDataToDom() } - // set given data to the elements data property - setData (o) { - this.dom = o - return this + // Move over x-axis + x (x) { + return this.attr('x', x) } - getEventTarget () { - return this.node + // Move over y-axis + y (y) { + return this.attr('y', y) } } diff --git a/src/EventTarget.js b/src/EventTarget.js index a72cafd..637f7f8 100644 --- a/src/EventTarget.js +++ b/src/EventTarget.js @@ -3,11 +3,13 @@ import {on, off, dispatch} from './event.js' import {extend} from './tools.js' export default class EventTarget extends Base{ - constructor (node = {}) { + constructor ({events = {}} = {}) { super() - this.events = node.events || {} + this.events = events } + addEventListener () {} + // Bind given event to listener on (event, listener, binding, options) { on(this, event, listener, binding, options) @@ -24,11 +26,36 @@ export default class EventTarget extends Base{ return dispatch(this, event, data) } + dispatchEvent (event) { + const bag = this.getEventHolder().events + if (!bag) return true + + const events = bag[event.type] + + for (let i in events) { + for (let j in events[i]) { + events[i][j](event) + } + } + + return !event.defaultPrevented + } + // Fire given event fire (event, data) { this.dispatch(event, data) return this } + + getEventHolder () { + return this + } + + getEventTarget () { + return this + } + + removeEventListener () {} } diff --git a/src/HtmlNode.js b/src/HtmlNode.js index 258c0ec..ff45984 100644 --- a/src/HtmlNode.js +++ b/src/HtmlNode.js @@ -1,35 +1,9 @@ -import {makeInstance} from './adopter.js' -import Parent from './Parent.js' +import Dom from './Dom.js' import {register} from './adopter.js' -export default class HtmlNode extends Parent { - constructor (element) { - super(element, HtmlNode) - this.node = element - } - - add (element, i) { - element = makeInstance(element) - - if (element.node !== this.node.children[i]) { - this.node.insertBefore(element.node, this.node.children[i] || null) - } - - return this - } - - put (element, i) { - this.add(element, i) - return element - } - - removeElement (element) { - this.node.removeChild(element.node) - return this - } - - getEventTarget () { - return this.node +export default class HtmlNode extends Dom { + constructor (node) { + super(node, HtmlNode) } } diff --git a/src/Parent.js b/src/Parent.js deleted file mode 100644 index 6786329..0000000 --- a/src/Parent.js +++ /dev/null @@ -1,169 +0,0 @@ -import {makeInstance, adopt} from './adopter.js' -import {map} from './utils.js' -import {registerMethods} from './methods.js' -import Element from './Element.js' -import {ns} from './namespaces.js' - -export default class Parent extends Element { - // Returns all child elements - children () { - return map(this.node.children, function (node) { - return adopt(node) - }) - } - - // Add given element at a position - add (element, i) { - element = makeInstance(element) - - if (i == null) { - this.node.appendChild(element.node) - } else if (element.node !== this.node.childNodes[i]) { - this.node.insertBefore(element.node, this.node.childNodes[i]) - } - - return this - } - - // Basically does the same as `add()` but returns the added element instead - put (element, i) { - this.add(element, i) - return element.instance || element - } - - // Checks if the given element is a child - has (element) { - return this.index(element) >= 0 - } - - // Gets index of given element - index (element) { - return [].slice.call(this.node.childNodes).indexOf(element.node) - } - - // Get a element at the given index - get (i) { - return adopt(this.node.childNodes[i]) - } - - // Get first child - first () { - return adopt(this.node.firstChild) - } - - // Get the last child - last () { - return adopt(this.node.lastChild) - } - - // Iterates over all children and invokes a given block - each (block, deep) { - var children = this.children() - var i, il - - for (i = 0, il = children.length; i < il; i++) { - if (children[i] instanceof Element) { - block.apply(children[i], [i, children]) - } - - if (deep && (children[i] instanceof Parent)) { - children[i].each(block, deep) - } - } - - return this - } - - // Remove a given child - removeElement (element) { - this.node.removeChild(element.node) - - return this - } - - // Remove all elements in this container - clear () { - // remove children - while (this.node.hasChildNodes()) { - this.node.removeChild(this.node.lastChild) - } - - // remove defs reference - delete this._defs - - return this - } - - // Import raw svg - svg (svg) { - var well, len - - // act as a setter if svg is given - if (svg) { - // create temporary holder - well = document.createElementNS(ns, 'svg') - // dump raw svg - well.innerHTML = svg - - // transplant nodes - for (len = well.children.length; len--;) { - this.node.appendChild(well.firstElementChild) - } - - // otherwise act as a getter - } else { - // write svgjs data to the dom - this.writeDataToDom() - - return this.node.outerHTML - } - - return this - } - - // write svgjs data to the dom - writeDataToDom () { - // dump variables recursively - this.each(function () { - this.writeDataToDom() - }) - - // remove previously set data - this.node.removeAttribute('svgjs:data') - - if (Object.keys(this.dom).length) { - this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428 - } - return this - } - - flatten (parent) { - this.each(function () { - if (this instanceof Parent) return this.flatten(parent).ungroup(parent) - return this.toParent(parent) - }) - - // we need this so that Doc does not get removed - this.node.firstElementChild || this.remove() - - return this - } - - ungroup (parent) { - parent = parent || this.parent() - - this.each(function () { - return this.toParent(parent) - }) - - this.remove() - - return this - } -} - - -// registerMethods('Container', { -// children, add, put, has, index, get, first, last, each, -// removeElement, clear, svg, writeDataToDom, flatten, ungroup -// }) diff --git a/src/Runner.js b/src/Runner.js index 52731e0..b8bafa6 100644 --- a/src/Runner.js +++ b/src/Runner.js @@ -9,6 +9,7 @@ import {extend} from './tools.js' import Animator from './Animator.js' import Point from './Point.js' import {registerMethods} from './methods.js' +import EventTarget from './EventTarget.js' // FIXME: What is this doing here? // easing = { @@ -18,8 +19,10 @@ import {registerMethods} from './methods.js' // '<': function (pos) { return -Math.cos(pos * Math.PI / 2) + 1 } // } -export default class Runner { +export default class Runner extends EventTarget { constructor (options) { + super() + // Store a unique id on the runner, so that we can identify it later this.id = Runner.id++ @@ -266,7 +269,7 @@ export default class Runner { var justFinished = this._lastTime < this._time && this.time > duration this._lastTime = this._time if (justStarted) { - // this.fire('start', this) + this.fire('start', this) } // Work out if the runner is finished set the done flag here so animations @@ -282,14 +285,14 @@ export default class Runner { // clear the transforms on this runner so they dont get added again and again this.transforms = new Matrix() var converged = this._run(declarative ? dt : position) - // this.fire('step', this) + this.fire('step', this) } // correct the done flag here // declaritive animations itself know when they converged this.done = this.done || (converged && declarative) - // if (this.done) { - // this.fire('finish', this) - // } + if (this.done) { + this.fire('finish', this) + } return this } diff --git a/src/Shape.js b/src/Shape.js index bf4ae8f..f02fec2 100644 --- a/src/Shape.js +++ b/src/Shape.js @@ -1,2 +1,2 @@ -import Parent from './Parent.js' -export default class Shape extends Parent {} +import Element from './Element.js' +export default class Shape extends Element {} diff --git a/src/Text.js b/src/Text.js index 239b429..55fed22 100644 --- a/src/Text.js +++ b/src/Text.js @@ -1,4 +1,4 @@ -import Parent from './Parent.js' +import Shape from './Shape.js' import SVGNumber from './SVGNumber.js' import {nodeOrNew, extend} from './tools.js' import {attrs} from './defaults.js' @@ -6,7 +6,7 @@ import * as textable from './textable.js' import {register, adopt} from './adopter.js' import {registerMethods} from './methods.js' -export default class Text extends Parent { +export default class Text extends Shape { // Initialize node constructor (node) { super(nodeOrNew('text', node), Text) diff --git a/src/Tspan.js b/src/Tspan.js index 677adf4..148fb16 100644 --- a/src/Tspan.js +++ b/src/Tspan.js @@ -1,11 +1,10 @@ -import Parent from './Parent.js' +import Text from './Text.js' import {nodeOrNew, extend} from './tools.js' import * as textable from './textable.js' import {register} from './adopter.js' import {registerMethods} from './methods.js' -import Text from './Text.js' -export default class Tspan extends Parent { +export default class Tspan extends Text { // Initialize node constructor (node) { super(nodeOrNew('tspan', node), Tspan) diff --git a/src/arrange.js b/src/arrange.js index 4d4ec1c..d0a5b23 100644 --- a/src/arrange.js +++ b/src/arrange.js @@ -94,6 +94,6 @@ export function after (element) { return this } -registerMethods('Element', { +registerMethods('Dom', { siblings, position, next, prev, forward, backward, front, back, before, after }) diff --git a/src/attr.js b/src/attr.js index 23baf51..c44fa68 100644 --- a/src/attr.js +++ b/src/attr.js @@ -3,7 +3,7 @@ import {attrs as defaults} from './defaults.js' import Color from './Color.js' import SVGArray from './SVGArray.js' import SVGNumber from './SVGNumber.js' -import {registerMethods} from './methods.js' +//import {registerMethods} from './methods.js' // Set svg element attribute export default function attr (attr, val, ns) { @@ -80,4 +80,4 @@ export default function attr (attr, val, ns) { return this } -registerMethods('Element', {attr}) +//registerMethods('Element', {attr}) diff --git a/src/classHandling.js b/src/classHandling.js new file mode 100644 index 0000000..27bf11a --- /dev/null +++ b/src/classHandling.js @@ -0,0 +1,44 @@ +import {registerMethods} from './methods.js' +import {delimiter} from './regex.js' + +// Return array of classes on the node +function classes () { + var attr = this.attr('class') + return attr == null ? [] : attr.trim().split(delimiter) +} + +// Return true if class exists on the node, false otherwise +function hasClass (name) { + return this.classes().indexOf(name) !== -1 +} + +// Add class to the node +function addClass (name) { + if (!this.hasClass(name)) { + var array = this.classes() + array.push(name) + this.attr('class', array.join(' ')) + } + + return this +} + +// Remove class from the node +function removeClass (name) { + if (this.hasClass(name)) { + this.attr('class', this.classes().filter(function (c) { + return c !== name + }).join(' ')) + } + + return this +} + +// Toggle the presence of a class on the node +function toggleClass (name) { + return this.hasClass(name) ? this.removeClass(name) : this.addClass(name) +} + +registerMethods('Dom', { + classes, hasClass, addClass, removeClass, toggleClass +}) diff --git a/src/classes.js b/src/classes.js index 8385c20..283baaa 100644 --- a/src/classes.js +++ b/src/classes.js @@ -1,7 +1,7 @@ export {default as EventTarget} from './EventTarget.js' +export {default as Dom} from './Dom.js' export {default as Element} from './Element.js' export {default as Shape} from './Shape.js' -export {default as Parent} from './Parent.js' export {default as Container} from './Container.js' export {default as HtmlNode} from './HtmlNode.js' export {default as Doc} from './Doc.js' @@ -68,7 +68,6 @@ export {Controller, Ease, PID, Spring} from './Controller.js' // export {default as Matrix} from './Matrix.js' // export {default as Morphable} from './Morphable.js' // export {default as SVGNumber} from './SVGNumber.js' -// export {default as Parent} from './Parent.js' // export {default as Path} from './Path.js' // export {default as PathArray} from './PathArray.js' // export {default as Pattern} from './Pattern.js' diff --git a/src/css.js b/src/css.js index 795be71..b581d71 100644 --- a/src/css.js +++ b/src/css.js @@ -68,6 +68,6 @@ export function visible () { return this.css('display') !== 'none' } -registerMethods('Element', { +registerMethods('Dom', { css, show, hide, visible }) diff --git a/src/data.js b/src/data.js index c49f6a9..6374987 100644 --- a/src/data.js +++ b/src/data.js @@ -23,4 +23,4 @@ export function data (a, v, r) { return this } -registerMethods('Element', {data}) +registerMethods('Dom', {data}) diff --git a/src/elemnts-svg.js b/src/elemnts-svg.js index 5e51034..5ee97b7 100644 --- a/src/elemnts-svg.js +++ b/src/elemnts-svg.js @@ -44,10 +44,6 @@ // Act as setter if we got a string - // Make sure we are on a current when trying to import - if(!(this instanceof SVG.Parent)) - throw Error('Cannot import svg into non-current element') - // Create temporary holder well = document.createElementNS(SVG.ns, 'svg') fragment = document.createDocumentFragment() diff --git a/src/event.js b/src/event.js index acc4dd6..2aa9daf 100644 --- a/src/event.js +++ b/src/event.js @@ -1,27 +1,38 @@ import {delimiter} from './regex.js' import {registerMethods} from './methods.js' +import {makeInstance} from './adopter.js' let listenerId = 0 +function getEvents (node) { + const n = makeInstance(node).getEventHolder() + if (!n.events) n.events = {} + return n.events +} + function getEventTarget (node) { - return typeof node.getEventTarget === 'function' - ? node.getEventTarget() - : node + return makeInstance(node).getEventTarget() +} + +function clearEvents (node) { + const n = makeInstance(node).getEventHolder() + if (n.events) n.events = {} } // Add event binder in the SVG namespace export function on (node, events, listener, binding, options) { var l = listener.bind(binding || node) + var bag = getEvents(node) var n = getEventTarget(node) // events can be an array of events or a string of events events = Array.isArray(events) ? events : events.split(delimiter) // ensure instance object for nodes which are not adopted - n.instance = n.instance || {events: {}} + // n.instance = n.instance || {events: {}} // pull event handlers from the element - var bag = n.instance.events + // var bag = n.instance.events // add id to listener if (!listener._svgjsListenerId) { @@ -46,10 +57,11 @@ export function on (node, events, listener, binding, options) { // Add event unbinder in the SVG namespace export function off (node, events, listener, options) { + var bag = getEvents(node) var n = getEventTarget(node) // we cannot remove an event if its not an svg.js instance - if (!n.instance) return + // if (!n.instance) return // listener can be a function or a number if (typeof listener === 'function') { @@ -58,7 +70,7 @@ export function off (node, events, listener, options) { } // pull event handlers from the element - var bag = n.instance.events + // var bag = n.instance.events // events can be an array of events or a string or undefined events = Array.isArray(events) ? events : (events || '').split(delimiter) @@ -101,7 +113,7 @@ export function off (node, events, listener, options) { // remove all listeners on a given node for (event in bag) { off(n, event) } - n.instance.events = {} + clearEvents(node) } }) } diff --git a/src/memory.js b/src/memory.js index 77d3518..a94f0e2 100644 --- a/src/memory.js +++ b/src/memory.js @@ -42,5 +42,5 @@ export function memory () { return (this._memory = this._memory || {}) } -registerMethods('Element', {remember, forget, memory}) +registerMethods('Dom', {remember, forget, memory}) //registerConstructor('Memory', setup) diff --git a/src/selector.js b/src/selector.js index 973787d..c6717fb 100644 --- a/src/selector.js +++ b/src/selector.js @@ -37,4 +37,4 @@ export function find (query) { return baseFind(query, this.node) } -registerMethods('Element', {find}) +registerMethods('Dom', {find}) diff --git a/src/sugar.js b/src/sugar.js index e5d6b61..6465985 100644 --- a/src/sugar.js +++ b/src/sugar.js @@ -3,6 +3,7 @@ import Runner from './Runner.js' import SVGNumber from './SVGNumber.js' import Matrix from './Matrix.js' import Point from './Point.js' +import Element from './Element.js' import {registerMethods} from './methods.js' // Define list of available attributes for stroke and fill @@ -23,7 +24,7 @@ var sugar = { if (typeof o === 'undefined') { return this } - if (typeof o === 'string' || Color.isRgb(o) || (o && typeof o.fill === 'function')) { + if (typeof o === 'string' || Color.isRgb(o) || (o instanceof Element)) { this.attr(m, o) } else { // set all attributes from sugar.fill and sugar.stroke list @@ -37,7 +38,7 @@ var sugar = { return this } - registerMethods(['Element', 'Runner'], extension) + registerMethods(['Shape', 'Runner'], extension) }) registerMethods(['Element', 'Runner'], { @@ -140,7 +141,7 @@ registerMethods('Path', { } }) -registerMethods(['Parent', 'Runner'], { +registerMethods(['Element', 'Runner'], { // Set font font: function (a, v) { if (typeof a === 'object') { diff --git a/src/svg.js b/src/svg.js index 4206060..7cfbbc8 100644 --- a/src/svg.js +++ b/src/svg.js @@ -1,9 +1,3 @@ -// import {extend} from './tools.js' -// import * as Element from './Element.js' -// import Defs from './Defs.js' -// -// extend(Defs, [EventTarget, Element, Parent]) - import {makeInstance} from './adopter.js' import * as Classes from './classes.js' import * as adopter from './adopter.js' @@ -13,6 +7,7 @@ import * as elements from './elements.js' import './attr.js' import './arrange.js' import './data.js' +import './classHandling.js' import find from './selector.js' import './css.js' import './transform.js' @@ -24,7 +19,6 @@ const extend = tools.extend import './EventTarget.js' import './Element.js' -import './Parent.js' extend([ Classes.Doc, @@ -59,8 +53,9 @@ extend([ ], getMethodsFor('radius')) extend(Classes.EventTarget, getMethodsFor('EventTarget')) +extend(Classes.Dom, getMethodsFor('Dom')) extend(Classes.Element, getMethodsFor('Element')) -extend(Classes.Element, getMethodsFor('Parent')) +extend(Classes.Shape, getMethodsFor('Shape')) //extend(Classes.Element, getConstructor('Memory')) extend(Classes.Container, getMethodsFor('Container')) @@ -97,8 +92,6 @@ import * as ns from './namespaces.js' SVG.get = SVG SVG.find = find Object.assign(SVG, ns) -// import Base from './Base.js' -// SVG.Element = SVG.Parent = SVG.Shape = SVG.Container = Base import {easing} from './Controller.js' SVG.easing = easing import * as events from './event.js' -- 2.39.5