diff options
author | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2018-11-08 10:05:28 +0100 |
---|---|---|
committer | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2018-11-08 10:05:28 +0100 |
commit | 4702522137dac17a6312c521f3c1974eb839c5eb (patch) | |
tree | 97c158c2c4e8005d3e56e037eab16dfffcf3da42 | |
parent | dec70426b32ccf3979046e1637174b66bfdd1a8d (diff) | |
download | svg.js-4702522137dac17a6312c521f3c1974eb839c5eb.tar.gz svg.js-4702522137dac17a6312c521f3c1974eb839c5eb.zip |
added insertAfter/Before, introduce attrHooks, move few methods, SVG.Text.textPath returns first textPath child now
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | dist/svg.js | 740 | ||||
-rw-r--r-- | spec/spec/textpath.js | 5 | ||||
-rw-r--r-- | src/elements/Image.js | 21 | ||||
-rw-r--r-- | src/elements/TextPath.js | 17 | ||||
-rw-r--r-- | src/modules/core/attr.js | 31 | ||||
-rw-r--r-- | src/modules/core/textable.js | 1 | ||||
-rw-r--r-- | src/modules/optional/arrange.js | 13 | ||||
-rw-r--r-- | src/modules/optional/sugar.js | 32 | ||||
-rw-r--r-- | src/types/EventTarget.js | 32 | ||||
-rw-r--r-- | todo.md | 14 |
11 files changed, 483 insertions, 425 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6013e0e..fd8efd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http: - added a linter during the npm build process - added `npm build:dev` to let you develop without getting too annoyed - added `beziere()` and `steps()` to generate easing functions +- added `insertAfter()` and `insertBefore` ### Removed - removed `SVG.Array.split()` function @@ -72,6 +73,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http: - `Element.svg()` now can can replace the current node, can export the children of a node and can take an export modifier to change/replace the exported nodes - `ungroup()` now breaks off one container and not more - `clone()` does not add the clone to the dom anymore +- `SVG.Text.textPath()` returns only the first textpath child ### Fixed - fixed a bug in clipping and masking where empty nodes persists after removal -> __TODO!__ diff --git a/dist/svg.js b/dist/svg.js index 7a9a297..5f91b01 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -6,7 +6,7 @@ * @copyright Wout Fierens <wout@mick-wout.com> * @license MIT * -* BUILT: Wed Nov 07 2018 22:39:24 GMT+0100 (GMT+01:00) +* BUILT: Thu Nov 08 2018 09:25:46 GMT+0100 (GMT+01:00) */; var SVG = (function () { 'use strict'; @@ -216,6 +216,207 @@ var SVG = (function () { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } + // 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; + } // Convert dash-separated-string to camelCase + + function camelCase(s) { + return s.toLowerCase().replace(/-(.)/g, function (m, g) { + return g.toUpperCase(); + }); + } // Capitalize first letter of a string + + function capitalize(s) { + return s.charAt(0).toUpperCase() + s.slice(1); + } // Calculate proportional width and height values when necessary + + function proportionalSize(element, width, height) { + if (width == null || height == null) { + var box = element.bbox(); + + if (width == null) { + width = box.width / box.height * height; + } else if (height == null) { + height = box.height / box.width * width; + } + } + + return { + width: width, + height: height + }; + } + function getOrigin(o, element) { + // Allow origin or around as the names + var origin = o.origin; // o.around == null ? o.origin : o.around + + var ox, oy; // Allow the user to pass a string to rotate around a given point + + if (typeof origin === 'string' || origin == null) { + // Get the bounding box of the element with no transformations applied + var string = (origin || 'center').toLowerCase().trim(); + + var _element$bbox = element.bbox(), + height = _element$bbox.height, + width = _element$bbox.width, + x = _element$bbox.x, + y = _element$bbox.y; // Calculate the transformed x and y coordinates + + + var bx = string.includes('left') ? x : string.includes('right') ? x + width : x + width / 2; + var by = string.includes('top') ? y : string.includes('bottom') ? y + height : y + height / 2; // Set the bounds eg : "bottom-left", "Top right", "middle" etc... + + ox = o.ox != null ? o.ox : bx; + oy = o.oy != null ? o.oy : by; + } else { + ox = origin[0]; + oy = origin[1]; + } // Return the origin as it is if it wasn't a string + + + return [ox, oy]; + } + + // Default namespaces + var ns = 'http://www.w3.org/2000/svg'; + var xmlns = 'http://www.w3.org/2000/xmlns/'; + var xlink = 'http://www.w3.org/1999/xlink'; + var svgjs = 'http://svgjs.com/svgjs'; + + var Base = function Base() { + _classCallCheck(this, Base); + }; + + var elements = {}; + var root = Symbol('root'); // Method for element creation + + function makeNode(name) { + // create element + return document.createElementNS(ns, name); + } + function makeInstance(element) { + if (element instanceof Base) return element; + + if (_typeof(element) === 'object') { + return adopt(element); + } + + if (element == null) { + return new elements[root](); + } + + if (typeof element === 'string' && element.charAt(0) !== '<') { + return adopt(document.querySelector(element)); + } + + var node = makeNode('svg'); + node.innerHTML = element; // We can use firstChild here because we know, + // that the first char is < and thus an element + + element = adopt(node.firstChild); + return element; + } + function nodeOrNew(name, node) { + return node || makeNode(name); + } // Adopt existing svg elements + + function adopt(node) { + // check for presence of node + if (!node) return null; // make sure a node isn't already adopted + + if (node.instance instanceof Base) return node.instance; + + if (!(node instanceof window.SVGElement)) { + return new elements.HtmlNode(node); + } // initialize variables + + + var element; // adopt with element-specific settings + + if (node.nodeName === 'svg') { + element = new elements[root](node); + } else if (node.nodeName === 'linearGradient' || node.nodeName === 'radialGradient') { + element = new elements.Gradient(node); + } else if (elements[capitalize(node.nodeName)]) { + element = new elements[capitalize(node.nodeName)](node); + } else { + element = new elements.Bare(node); + } + + return element; + } + function register(element) { + var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : element.name; + var asRoot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + elements[name] = element; + if (asRoot) elements[root] = element; + return element; + } + function getClass(name) { + return elements[name]; + } // Element id sequence + + var did = 1000; // Get next named element id + + function eid(name) { + return 'Svgjs' + capitalize(name) + did++; + } // Deep new id assignment + + function assignNewId(node) { + // do the same for SVG child nodes as well + for (var i = node.children.length - 1; i >= 0; i--) { + assignNewId(node.children[i]); + } + + if (node.id) { + return adopt(node).id(eid(node.nodeName)); + } + + return adopt(node); + } // Method for extending objects + + function extend(modules, methods) { + var key, i; + modules = Array.isArray(modules) ? modules : [modules]; + + for (i = modules.length - 1; i >= 0; i--) { + for (key in methods) { + modules[i].prototype[key] = methods[key]; + } + } + } + var methods = {}; function registerMethods(name, m) { if (Array.isArray(name)) { @@ -326,6 +527,7 @@ var SVG = (function () { } // Inserts a given element before the targeted element function before(element) { + element = makeInstance(element); element.remove(); var i = this.position(); this.parent().add(element, i); @@ -333,6 +535,7 @@ var SVG = (function () { } // Inserts a given element after the targeted element function after(element) { + element = makeInstance(element); element.remove(); var i = this.position(); this.parent().add(element, i + 1); @@ -458,98 +661,6 @@ var SVG = (function () { toggleClass: toggleClass }); - // 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; - } // Convert dash-separated-string to camelCase - - function camelCase(s) { - return s.toLowerCase().replace(/-(.)/g, function (m, g) { - return g.toUpperCase(); - }); - } // Capitalize first letter of a string - - function capitalize(s) { - return s.charAt(0).toUpperCase() + s.slice(1); - } // Calculate proportional width and height values when necessary - - function proportionalSize(element, width, height) { - if (width == null || height == null) { - var box = element.bbox(); - - if (width == null) { - width = box.width / box.height * height; - } else if (height == null) { - height = box.height / box.width * width; - } - } - - return { - width: width, - height: height - }; - } - function getOrigin(o, element) { - // Allow origin or around as the names - var origin = o.origin; // o.around == null ? o.origin : o.around - - var ox, oy; // Allow the user to pass a string to rotate around a given point - - if (typeof origin === 'string' || origin == null) { - // Get the bounding box of the element with no transformations applied - var string = (origin || 'center').toLowerCase().trim(); - - var _element$bbox = element.bbox(), - height = _element$bbox.height, - width = _element$bbox.width, - x = _element$bbox.x, - y = _element$bbox.y; // Calculate the transformed x and y coordinates - - - var bx = string.includes('left') ? x : string.includes('right') ? x + width : x + width / 2; - var by = string.includes('top') ? y : string.includes('bottom') ? y + height : y + height / 2; // Set the bounds eg : "bottom-left", "Top right", "middle" etc... - - ox = o.ox != null ? o.ox : bx; - oy = o.oy != null ? o.oy : by; - } else { - ox = origin[0]; - oy = origin[1]; - } // Return the origin as it is if it wasn't a string - - - return [ox, oy]; - } - function css(style, val) { var ret = {}; @@ -696,228 +807,6 @@ var SVG = (function () { memory: memory }); - function fullHex(hex$$1) { - return hex$$1.length === 4 ? ['#', hex$$1.substring(1, 2), hex$$1.substring(1, 2), hex$$1.substring(2, 3), hex$$1.substring(2, 3), hex$$1.substring(3, 4), hex$$1.substring(3, 4)].join('') : hex$$1; - } // Component to hex value - - - function compToHex(comp) { - var hex$$1 = comp.toString(16); - return hex$$1.length === 1 ? '0' + hex$$1 : hex$$1; - } - - 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; - }(); - - // Default namespaces - var ns = 'http://www.w3.org/2000/svg'; - var xmlns = 'http://www.w3.org/2000/xmlns/'; - var xlink = 'http://www.w3.org/1999/xlink'; - var svgjs = 'http://svgjs.com/svgjs'; - - var Base = function Base() { - _classCallCheck(this, Base); - }; - - var elements = {}; - var root = Symbol('root'); // Method for element creation - - function makeNode(name) { - // create element - return document.createElementNS(ns, name); - } - function makeInstance(element) { - if (element instanceof Base) return element; - - if (_typeof(element) === 'object') { - return adopt(element); - } - - if (element == null) { - return new elements[root](); - } - - if (typeof element === 'string' && element.charAt(0) !== '<') { - return adopt(document.querySelector(element)); - } - - var node = makeNode('svg'); - node.innerHTML = element; // We can use firstChild here because we know, - // that the first char is < and thus an element - - element = adopt(node.firstChild); - return element; - } - function nodeOrNew(name, node) { - return node || makeNode(name); - } // Adopt existing svg elements - - function adopt(node) { - // check for presence of node - if (!node) return null; // make sure a node isn't already adopted - - if (node.instance instanceof Base) return node.instance; - - if (!(node instanceof window.SVGElement)) { - return new elements.HtmlNode(node); - } // initialize variables - - - var element; // adopt with element-specific settings - - if (node.nodeName === 'svg') { - element = new elements[root](node); - } else if (node.nodeName === 'linearGradient' || node.nodeName === 'radialGradient') { - element = new elements.Gradient(node); - } else if (elements[capitalize(node.nodeName)]) { - element = new elements[capitalize(node.nodeName)](node); - } else { - element = new elements.Bare(node); - } - - return element; - } - function register(element) { - var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : element.name; - var asRoot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - elements[name] = element; - if (asRoot) elements[root] = element; - return element; - } - function getClass(name) { - return elements[name]; - } // Element id sequence - - var did = 1000; // Get next named element id - - function eid(name) { - return 'Svgjs' + capitalize(name) + did++; - } // Deep new id assignment - - function assignNewId(node) { - // do the same for SVG child nodes as well - for (var i = node.children.length - 1; i >= 0; i--) { - assignNewId(node.children[i]); - } - - if (node.id) { - return adopt(node).id(eid(node.nodeName)); - } - - return adopt(node); - } // Method for extending objects - - function extend(modules, methods) { - var key, i; - modules = Array.isArray(modules) ? modules : [modules]; - - for (i = modules.length - 1; i >= 0; i--) { - for (key in methods) { - modules[i].prototype[key] = methods[key]; - } - } - } - var listenerId = 0; function getEvents(node) { @@ -1036,6 +925,119 @@ var SVG = (function () { return event; } + function fullHex(hex$$1) { + return hex$$1.length === 4 ? ['#', hex$$1.substring(1, 2), hex$$1.substring(1, 2), hex$$1.substring(2, 3), hex$$1.substring(2, 3), hex$$1.substring(3, 4), hex$$1.substring(3, 4)].join('') : hex$$1; + } // Component to hex value + + + function compToHex(comp) { + var hex$$1 = comp.toString(16); + return hex$$1.length === 1 ? '0' + hex$$1 : hex$$1; + } + + 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; + }(); + var EventTarget = /*#__PURE__*/ function (_Base) { @@ -1117,23 +1119,7 @@ var SVG = (function () { }]); return EventTarget; - }(Base); // Add events to elements - var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) { - // add event to Element - var fn = function fn(f) { - if (f === null) { - off(this, event); - } else { - on(this, event, f); - } - - return this; - }; - - last[event] = fn; - return last; - }, {}); - registerMethods('Element', methods$1); + }(Base); function noop() {} // Default animation values @@ -1338,7 +1324,14 @@ var SVG = (function () { return SVGNumber; }(); + var hooks = []; + function registerAttrHook(fn) { + hooks.push(fn); + } // Set svg element attribute + function attr(attr, val, ns) { + var _this = this; + // act as full getter if (attr == null) { // get an object of attributes @@ -1369,7 +1362,13 @@ var SVG = (function () { } return attr; - } else if (Array.isArray(attr)) ; else if (_typeof(attr) === 'object') { + } else if (attr instanceof Array) { + // loop through array and get all values + return attr.reduce(function (last, curr) { + last[curr] = _this.attr(curr); + return last; + }, {}); + } else if (_typeof(attr) === 'object') { // apply every attribute individually if an object is passed for (val in attr) { this.attr(val, attr[val]); @@ -1382,19 +1381,10 @@ var SVG = (function () { val = this.node.getAttribute(attr); return val == null ? attrs[attr] : 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) - + // Loop through hooks and execute them to convert value + val = hooks.reduce(function (_val, hook) { + return hook(attr, _val, _this); + }, val); // ensure correct numeric values (also accepts NaN and Infinity) if (typeof val === 'number') { val = new SVGNumber(val); @@ -5130,7 +5120,24 @@ var SVG = (function () { return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v); } - }); + }); // Add events to elements + + var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) { + // add event to Element + var fn = function fn(f) { + if (f === null) { + off(this, event); + } else { + on(this, event, f); + } + + return this; + }; + + last[event] = fn; + return last; + }, {}); + registerMethods('Element', methods$1); function untransform() { return this.attr('transform', null); @@ -5610,19 +5617,26 @@ var SVG = (function () { }); return this.attr('href', img.src = url, xlink); } - }, { - key: "attrHook", - value: function attrHook(obj) { - var _this = this; - - return obj.doc().defs().pattern(0, 0, function (pattern) { - pattern.add(_this); - }); - } }]); return Image; }(Shape); + registerAttrHook(function (attr$$1, val, _this) { + // convert image fill and stroke to patterns + if (attr$$1 === 'fill' || attr$$1 === 'stroke') { + if (isImage.test(val)) { + val = _this.doc().defs().image(val); + } + } + + if (val instanceof Image) { + val = _this.doc().defs().pattern(0, 0, function (pattern) { + pattern.add(val); + }); + } + + return val; + }); registerMethods({ Container: { // create image element, load image and set its size @@ -6142,8 +6156,7 @@ var SVG = (function () { this.node.appendChild(document.createTextNode(text)); return this; - } // FIXME: Does this also work for textpath? - // Get length of text element + } // Get length of text element function length() { return this.node.getComputedTextLength(); @@ -6717,7 +6730,7 @@ var SVG = (function () { Text: { // Create path for text to run on path: function path(track) { - var path = new TextPath(); // if d is a path, reuse it + var path = new TextPath(); // if track is a path, reuse it if (!(track instanceof Path)) { // create path element @@ -6729,10 +6742,9 @@ var SVG = (function () { return this.put(path); }, - // FIXME: make this plural? // Get the textPath children textPath: function textPath() { - return this.find('textPath'); + return this.find('textPath')[0]; } }, Path: { @@ -6745,8 +6757,10 @@ var SVG = (function () { } return this.parent().put(new Text()).path(this).text(_text); - } // FIXME: Maybe add `targets` to get all textPaths associated with this path - + }, + targets: function targets() { + return baseFind('svg [href*="' + this.id() + '"]'); + } } }); TextPath.prototype.MorphArray = PathArray; diff --git a/spec/spec/textpath.js b/spec/spec/textpath.js index e64f721..8a3d4c2 100644 --- a/spec/spec/textpath.js +++ b/spec/spec/textpath.js @@ -35,10 +35,9 @@ describe('TextPath', function() { }) describe('textPath()', function() { - it('returns all textPath elements in a text', function() { + it('returns only the first textPath element in a text', function() { text.path(data) - expect(text.textPath().length).toBe(1) - expect(text.textPath()[0] instanceof SVG.TextPath).toBe(true) + expect(text.textPath() instanceof SVG.TextPath).toBe(true) }) }) diff --git a/src/elements/Image.js b/src/elements/Image.js index 5e672f4..ec9459f 100644 --- a/src/elements/Image.js +++ b/src/elements/Image.js @@ -1,5 +1,7 @@ +import { isImage } from '../modules/core/regex.js' import { nodeOrNew, register } from '../utils/adopter.js' import { off, on } from '../modules/core/event.js' +import { registerAttrHook } from '../modules/core/attr.js' import { registerMethods } from '../utils/methods.js' import { xlink } from '../modules/core/namespaces.js' import Pattern from './Pattern.js' @@ -48,13 +50,24 @@ export default class Image extends Shape { return this.attr('href', (img.src = url), xlink) } +} + +registerAttrHook(function (attr, val, _this) { + // convert image fill and stroke to patterns + if (attr === 'fill' || attr === 'stroke') { + if (isImage.test(val)) { + val = _this.doc().defs().image(val) + } + } - attrHook (obj) { - return obj.doc().defs().pattern(0, 0, (pattern) => { - pattern.add(this) + if (val instanceof Image) { + val = _this.doc().defs().pattern(0, 0, (pattern) => { + pattern.add(val) }) } -} + + return val +}) registerMethods({ Container: { diff --git a/src/elements/TextPath.js b/src/elements/TextPath.js index 04146bc..480eca2 100644 --- a/src/elements/TextPath.js +++ b/src/elements/TextPath.js @@ -1,6 +1,7 @@ import { nodeOrNew, register } from '../utils/adopter.js' import { registerMethods } from '../utils/methods.js' import { xlink } from '../modules/core/namespaces.js' +import baseFind from '../modules/core/selector.js' import Path from './Path.js' import PathArray from '../types/PathArray.js' import Text from './Text.js' @@ -44,10 +45,10 @@ registerMethods({ }, Text: { // Create path for text to run on - path: function (track) { + path (track) { var path = new TextPath() - // if d is a path, reuse it + // if track is a path, reuse it if (!(track instanceof Path)) { // create path element track = this.doc().defs().path(track) @@ -60,22 +61,24 @@ registerMethods({ return this.put(path) }, - // FIXME: make this plural? // Get the textPath children - textPath: function () { - return this.find('textPath') + textPath () { + return this.find('textPath')[0] } }, Path: { // creates a textPath from this path - text: function (text) { + text (text) { if (text instanceof Text) { var txt = text.text() return text.clear().path(this).text(txt) } return this.parent().put(new Text()).path(this).text(text) + }, + + targets () { + return baseFind('svg [href*="' + this.id() + '"]') } - // FIXME: Maybe add `targets` to get all textPaths associated with this path } }) diff --git a/src/modules/core/attr.js b/src/modules/core/attr.js index 7c9e2c1..f90dcb9 100644 --- a/src/modules/core/attr.js +++ b/src/modules/core/attr.js @@ -1,9 +1,14 @@ -import { isImage, isNumber } from './regex.js' import { attrs as defaults } from './defaults.js' +import { isNumber } from './regex.js' import Color from '../../types/Color.js' import SVGArray from '../../types/SVGArray.js' import SVGNumber from '../../types/SVGNumber.js' +const hooks = [] +export function registerAttrHook (fn) { + hooks.push(fn) +} + // Set svg element attribute export default function attr (attr, val, ns) { // act as full getter @@ -19,8 +24,12 @@ export default function attr (attr, val, ns) { } return attr - } else if (Array.isArray(attr)) { - // FIXME: implement + } else if (attr instanceof Array) { + // loop through array and get all values + return attr.reduce((last, curr) => { + last[curr] = this.attr(curr) + return last + }, {}) } else if (typeof attr === 'object') { // apply every attribute individually if an object is passed for (val in attr) this.attr(val, attr[val]) @@ -34,18 +43,10 @@ export default function attr (attr, val, ns) { : 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) - } + // Loop through hooks and execute them to convert value + val = hooks.reduce((_val, hook) => { + return hook(attr, _val, this) + }, val) // ensure correct numeric values (also accepts NaN and Infinity) if (typeof val === 'number') { diff --git a/src/modules/core/textable.js b/src/modules/core/textable.js index c9a90db..139d056 100644 --- a/src/modules/core/textable.js +++ b/src/modules/core/textable.js @@ -11,7 +11,6 @@ export function plain (text) { return this } -// FIXME: Does this also work for textpath? // Get length of text element export function length () { return this.node.getComputedTextLength() diff --git a/src/modules/optional/arrange.js b/src/modules/optional/arrange.js index ca0e074..6ce2eea 100644 --- a/src/modules/optional/arrange.js +++ b/src/modules/optional/arrange.js @@ -1,3 +1,4 @@ +import { makeInstance } from '../../utils/adopter.js' import { registerMethods } from '../../utils/methods.js' // Get all siblings, including myself @@ -73,6 +74,7 @@ export function back () { // Inserts a given element before the targeted element export function before (element) { + element = makeInstance(element) element.remove() var i = this.position() @@ -84,6 +86,7 @@ export function before (element) { // Inserts a given element after the targeted element export function after (element) { + element = makeInstance(element) element.remove() var i = this.position() @@ -93,6 +96,16 @@ export function after (element) { return this } +export function insertBefore (element) { + element = makeInstance(element) + element.before(this) +} + +export function insertAfter (element) { + element = makeInstance(element) + element.after(this) +} + registerMethods('Dom', { siblings, position, next, prev, forward, backward, front, back, before, after }) diff --git a/src/modules/optional/sugar.js b/src/modules/optional/sugar.js index 904e353..9cdc662 100644 --- a/src/modules/optional/sugar.js +++ b/src/modules/optional/sugar.js @@ -1,3 +1,4 @@ +import { on, off } from '../core/event.js' import { registerMethods } from '../../utils/methods.js' import Color from '../../types/Color.js' import Element from '../../elements/Element.js' @@ -157,3 +158,34 @@ registerMethods(['Element', 'Runner'], { : this.attr(a, v) } }) + +// Add events to elements +const methods = [ 'click', + 'dblclick', + 'mousedown', + 'mouseup', + 'mouseover', + 'mouseout', + 'mousemove', + 'mouseenter', + 'mouseleave', + 'touchstart', + 'touchmove', + 'touchleave', + 'touchend', + 'touchcancel' ].reduce(function (last, event) { + // add event to Element + const fn = function (f) { + if (f === null) { + off(this, event) + } else { + on(this, event, f) + } + return this + } + + last[event] = fn + return last +}, {}) + +registerMethods('Element', methods) diff --git a/src/types/EventTarget.js b/src/types/EventTarget.js index a32a1f1..31d01df 100644 --- a/src/types/EventTarget.js +++ b/src/types/EventTarget.js @@ -1,5 +1,4 @@ import { dispatch, off, on } from '../modules/core/event.js' -import { registerMethods } from '../utils/methods.js' import Base from './Base.js' export default class EventTarget extends Base { @@ -57,34 +56,3 @@ export default class EventTarget extends Base { removeEventListener () {} } - -// Add events to elements -const methods = [ 'click', - 'dblclick', - 'mousedown', - 'mouseup', - 'mouseover', - 'mouseout', - 'mousemove', - 'mouseenter', - 'mouseleave', - 'touchstart', - 'touchmove', - 'touchleave', - 'touchend', - 'touchcancel' ].reduce(function (last, event) { - // add event to Element - const fn = function (f) { - if (f === null) { - off(this, event) - } else { - on(this, event, f) - } - return this - } - - last[event] = fn - return last -}, {}) - -registerMethods('Element', methods) @@ -1,3 +1,17 @@ +# tests to write +- sugar.js + - insertBefore + - insertAfter + - after + - before +- Path.js + - targets +- Element.js + - svg +- attr.js + + + # Where We Left Off |