/** * @private */Ext.define('Ext.d3.Helpers', { singleton: true, makeScale: function (config) { var type = config.type, Type, scale; //<debug> if (!type) { Ext.raise('The type of scale is not specified.'); } //</debug> Type = type.charAt(0).toUpperCase() + type.substr(1); scale = this.make('scale' + Type, config); if ('nice' in config && scale.nice) { // mbostock: @Vitalyx Yes; scale.nice rounds the current domain. // So it has no effect if you subsequently set a new domain. scale.nice(this.eval(config.nice)); } scale._type = type; return scale; }, makeAxis: function (config) { var type = config.orient, Type, axis; //<debug> if (!type) { Ext.raise('The position of the axis ("orient" property) is not specified.'); } //</debug> Type = type.charAt(0).toUpperCase() + type.substr(1); axis = this.make('axis' + Type, config); axis._type = type; return axis; }, make: function (name, config) { return this.configure(d3[name](), config); }, /** * @param {Function} thing A D3 entity instance, such as a scale or an axis. * @param {Object} config The configs to be set on the instance. * @return {Function} Configured `thing`. */ configure: function (thing, config) { // Examples: // bandScale.round(true).align(0.7) <-> configure(bandScale, {round: true, align: 0.7}) // axis.ticks(d3.timeMinute.every(15)) <-> configure(axis, {ticks: 'd3.timeMinute.every(15)'}) // scale.domain([0, 20]) <-> configure(scale, {domain: [0, 20]}) // timeScale.nice(d3.timeSecond, 10) <-> configure(timeScale, {$nice: ['d3.timeSecond', 10]}) var key, arrayArgs, param; for (key in config) { arrayArgs = key.charAt(0) === '$'; if (arrayArgs) { key = key.substr(1); } if (typeof thing[key] === 'function') { if (arrayArgs) { param = config['$' + key].map(function (param) { if (typeof param === 'string' && !param.search('d3.')) { param = (new Function('return ' + param))(); } return param; }); thing[key].apply(thing, param); arrayArgs = false; } else { param = this.eval(config[key]); thing[key](param); } } } return thing; }, eval: function (param) { if (typeof param === 'string' && !param.search('d3.')) { param = (new Function('return ' + param))(); } return param; }, /** * See https://bugzilla.mozilla.org/show_bug.cgi?id=612118 */ isBBoxable: function (selection) { if (Ext.isFirefox) { if (selection) { if (selection instanceof Element) { selection = d3.select(selection); } return document.contains(selection.node()) && selection.style('display') !== 'none' && selection.attr('visibility') !== 'hidden'; } } return true; }, getBBox: function (selection) { var display = selection.style('display'); if (display !== 'none') { return selection.node().getBBox(); } }, setDominantBaseline: function (element, baseline) { element.setAttribute('dominant-baseline', baseline); this.fakeDominantBaseline(element, baseline); if (Ext.isSafari && baseline === 'text-after-edge') { // dominant-baseline: text-after-edge doesn't work properly in Safari element.setAttribute('baseline-shift', 'super'); } }, noDominantBaseline: function () { // 'dominant-baseline' and 'alignment-baseline' don't work in IE and Edge. return Ext.isIE || Ext.isEdge; }, fakeDominantBaselineMap: { 'alphabetic': '0em', 'ideographic': '-.24em', 'hanging': '.72em', 'mathematical': '.46em', 'middle': '.22em', 'central': '.33em', 'text-after-edge': '-.26em', 'text-before-edge': '.91em' }, fakeDominantBaseline: function (element, baseline, force) { if (force || this.noDominantBaseline()) { var dy = this.fakeDominantBaselineMap[baseline]; dy && element.setAttribute('dy', dy); } }, fakeDominantBaselines: function (config) { var map = this.fakeDominantBaselineMap, selector, baseline, dy, nodeList, i, ln; // `config` is a map of the {selector: baseline} format. // Alternatively, the method takes two arguments: selector and baseline. if (this.noDominantBaseline()) { if (arguments.length > 1) { selector = arguments[0]; baseline = arguments[1]; nodeList = document.querySelectorAll(selector); dy = map[baseline]; if (dy) { for (i = 0, ln = nodeList.length; i < ln; i++) { nodeList[i].setAttribute('dy', dy); } } } else { for (selector in config) { baseline = config[selector]; dy = map[baseline]; if (dy) { nodeList = document.querySelectorAll(selector); for (i = 0, ln = nodeList.length; i < ln; i++) { nodeList[i].setAttribute('dy', dy); } } } } } }, unitMath: function (string, operation, number) { var value = parseFloat(string), unit = string.substr(value.toString().length); switch (operation) { case '*': value *= number; break; case '+': value += number; break; case '/': value /= number; break; case '-': value -= number; break; } return value.toString() + unit; }, getLinkId: function (link) { var pos = link.search('#'), // e.g. url(#path) id = link.substr(pos, link.length - pos - 1); return id; }, alignRect: function (x, y, inner, outer, selection) { var tx, ty, translation; if (outer && inner) { switch (x) { case 'center': tx = outer.width / 2 - (inner.x + inner.width / 2); break; case 'left': tx = -inner.x; break; case 'right': tx = outer.width - (inner.x + inner.width); break; default: Ext.raise('Invalid value. Valid `x` values are: center, left, right.'); } switch (y) { case 'center': ty = outer.height / 2 - (inner.y + inner.height / 2); break; case 'top': ty = -inner.y; break; case 'bottom': ty = outer.height - (inner.y + inner.height); break; default: Ext.raise('Invalid value. Valid `y` values are: center, top, bottom.'); } } if (Ext.isNumber(tx) && Ext.isNumber(ty)) { tx += outer.x; ty += outer.y; translation = [tx, ty]; selection.attr('transform', 'translate(' + translation + ')'); } }, commasRe: /,+/g, getTransformComponent: function (transform, name) { var pos = transform.indexOf(name), start, end, str, values, x, y; if (pos >= 0) { start = transform.indexOf('(', pos) + 1; end = transform.indexOf(')', start); if (start >= 0 && end >= 0) { str = transform.substr(start, end - start); str = str.replace(' ', ','); str = str.replace(this.commasRe, ','); } } if (str) { values = str.split(','); if (values.length > 1) { x = parseFloat(values[0]); y = parseFloat(values[1]); return [x, y]; } else { x = parseFloat(values[0]); return [x]; } } }, parseTransform: function (str) { var scale = this.getTransformComponent(str, 'scale'), rotate = this.getTransformComponent(str, 'rotate'), translate = this.getTransformComponent(str, 'translate'); if (scale) { if (scale.length < 2) { scale.push(scale[0]); } } else { scale = [0, 0]; } rotate = rotate ? rotate[0] : 0; if (translate) { if (translate.length < 2) { translate.push(0); } } else { translate = [0, 0]; } return { scale: scale, rotate: rotate, translate: translate } } });