UNPKG

@syncfusion/ej2-treemap

Version:
1,453 lines (1,450 loc) 317 kB
import { ChildProperty, Property, Complex, Collection, createElement, SanitizeHtmlHelper, isNullOrUndefined, compile, merge, remove, extend, print, Browser, Component, Fetch, EventHandler, Internationalization, Event, NotifyPropertyChanges } from '@syncfusion/ej2-base'; import { SvgRenderer, Tooltip } from '@syncfusion/ej2-svg-base'; import { DataManager, Query } from '@syncfusion/ej2-data'; import { PdfPageOrientation, PdfDocument, PdfBitmap } from '@syncfusion/ej2-pdf-export'; /** * TreeMap constants doc */ /** * Triggers when the treemap is on load. * * @private */ const load = 'load'; /** * Triggers after treemap rendered. * * @private */ const loaded = 'loaded'; /** * Trigger before call the print method. * * @private */ const beforePrint = 'beforePrint'; /** * Trigger before each treemap item rendered. * * @private */ const itemRendering = 'itemRendering'; /** * Trigger after click on treemap item. * * @private */ const drillStart = 'drillStart'; /** * Trigger after drill start event completed. * * @private */ const drillEnd = 'drillEnd'; /** * Trigger after select the treemap item. * * @private */ const itemSelected = 'itemSelected'; /** * Trigger after hover on the treemap item. * * @private */ const itemHighlight = 'itemHighlight'; /** * Trigger after mouse hover on the treemap item. * * @private */ const tooltipRendering = 'tooltipRendering'; /** * Trigger after click on the treemap item. * * @private */ const itemClick = 'itemClick'; /** * Trigger after mouse hover on the treemap item. * * @private */ const itemMove = 'itemMove'; /** * Trigger after click on the treemap item. * * @private */ const click = 'click'; /** * Trigger after double click on the treemap item. * * @private */ const doubleClick = 'doubleClick'; /** * Trigger after right click on the treemap item. * * @private */ const rightClick = 'rightClick'; /** * Trigger after mouse hover on the treemap item. * * @private */ const mouseMove = 'mouseMove'; /** * Trigger before each treemap item. * * @private */ const legendItemRendering = 'legendItemRendering'; /** * Trigger before legend items. * * @private */ const legendRendering = 'legendRendering'; /** * Trigger after resize the treemap. * * @private */ const resize = 'resize'; /** * Define the font family in treemap. * * @private */ const defaultFont = 'Roboto, Segoe UI, Noto, Sans-serif'; var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; /** * Sets and gets the options for customizing the color and width of the border in treemap. */ class Border extends ChildProperty { } __decorate([ Property('#808080') ], Border.prototype, "color", void 0); __decorate([ Property(0) ], Border.prototype, "width", void 0); /** * Sets and gets the margin for the treemap. */ class Margin extends ChildProperty { } __decorate([ Property(10) ], Margin.prototype, "left", void 0); __decorate([ Property(10) ], Margin.prototype, "right", void 0); __decorate([ Property(10) ], Margin.prototype, "top", void 0); __decorate([ Property(10) ], Margin.prototype, "bottom", void 0); /** * Sets and gets the options to customize the style of the text contents in the treemap. */ class Font extends ChildProperty { } __decorate([ Property(null) ], Font.prototype, "size", void 0); __decorate([ Property(null) ], Font.prototype, "color", void 0); __decorate([ Property(defaultFont) ], Font.prototype, "fontFamily", void 0); __decorate([ Property('') ], Font.prototype, "fontWeight", void 0); __decorate([ Property('Normal') ], Font.prototype, "fontStyle", void 0); __decorate([ Property(1) ], Font.prototype, "opacity", void 0); /** * Sets and gets the options for customizing the title of the treemap. */ class CommonTitleSettings extends ChildProperty { } __decorate([ Property('') ], CommonTitleSettings.prototype, "text", void 0); __decorate([ Property('') ], CommonTitleSettings.prototype, "description", void 0); /** * Sets and gets the options for customizing the subtitle of the treemap. */ class SubTitleSettings extends CommonTitleSettings { } __decorate([ Complex({ fontFamily: null, fontWeight: null }, Font) ], SubTitleSettings.prototype, "textStyle", void 0); __decorate([ Property('Center') ], SubTitleSettings.prototype, "alignment", void 0); /** * Sets and gets the options for customizing the title of the treemap. */ class TitleSettings extends CommonTitleSettings { } __decorate([ Complex({ fontFamily: null, fontWeight: null }, Font) ], TitleSettings.prototype, "textStyle", void 0); __decorate([ Property('Center') ], TitleSettings.prototype, "alignment", void 0); __decorate([ Complex({}, SubTitleSettings) ], TitleSettings.prototype, "subtitleSettings", void 0); /** * Sets and gets the options to customize the color-mapping in treemap. */ class ColorMapping extends ChildProperty { } __decorate([ Property(null) ], ColorMapping.prototype, "from", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "to", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "color", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "label", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "value", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "minOpacity", void 0); __decorate([ Property(null) ], ColorMapping.prototype, "maxOpacity", void 0); __decorate([ Property(true) ], ColorMapping.prototype, "showLegend", void 0); /** * Sets and gets the options for customizing the legend of the treemap. */ class LegendSettings extends ChildProperty { } __decorate([ Property(false) ], LegendSettings.prototype, "visible", void 0); __decorate([ Property('Default') ], LegendSettings.prototype, "mode", void 0); __decorate([ Property('transparent') ], LegendSettings.prototype, "background", void 0); __decorate([ Property('Circle') ], LegendSettings.prototype, "shape", void 0); __decorate([ Property('') ], LegendSettings.prototype, "width", void 0); __decorate([ Property('') ], LegendSettings.prototype, "height", void 0); __decorate([ Complex({ size: null, fontFamily: null, fontWeight: null }, Font) ], LegendSettings.prototype, "textStyle", void 0); __decorate([ Property(null) ], LegendSettings.prototype, "fill", void 0); __decorate([ Property(1) ], LegendSettings.prototype, "opacity", void 0); __decorate([ Property(15) ], LegendSettings.prototype, "shapeWidth", void 0); __decorate([ Property(15) ], LegendSettings.prototype, "shapeHeight", void 0); __decorate([ Property(10) ], LegendSettings.prototype, "shapePadding", void 0); __decorate([ Property(null) ], LegendSettings.prototype, "imageUrl", void 0); __decorate([ Complex({ color: null, width: null }, Border) ], LegendSettings.prototype, "border", void 0); __decorate([ Complex({ color: '#000000', width: 0 }, Border) ], LegendSettings.prototype, "shapeBorder", void 0); __decorate([ Complex({}, CommonTitleSettings) ], LegendSettings.prototype, "title", void 0); __decorate([ Complex({ size: null, fontFamily: null, fontWeight: null }, Font) ], LegendSettings.prototype, "titleStyle", void 0); __decorate([ Property('Bottom') ], LegendSettings.prototype, "position", void 0); __decorate([ Property('None') ], LegendSettings.prototype, "orientation", void 0); __decorate([ Property(false) ], LegendSettings.prototype, "invertedPointer", void 0); __decorate([ Property('After') ], LegendSettings.prototype, "labelPosition", void 0); __decorate([ Property('None') ], LegendSettings.prototype, "labelDisplayMode", void 0); __decorate([ Property('Center') ], LegendSettings.prototype, "alignment", void 0); __decorate([ Property({ x: 0, y: 0 }) ], LegendSettings.prototype, "location", void 0); __decorate([ Property(null) ], LegendSettings.prototype, "showLegendPath", void 0); __decorate([ Property(null) ], LegendSettings.prototype, "valuePath", void 0); __decorate([ Property(false) ], LegendSettings.prototype, "removeDuplicateLegend", void 0); /** * Sets and gets the settings for drill down to visualize the treemap rendered in the initial state. */ class InitialDrillSettings extends ChildProperty { } __decorate([ Property(null) ], InitialDrillSettings.prototype, "groupIndex", void 0); __decorate([ Property(null) ], InitialDrillSettings.prototype, "groupName", void 0); /** * Sets and gets the options for customizing the leaf item of the treemap. */ class LeafItemSettings extends ChildProperty { } __decorate([ Property(null) ], LeafItemSettings.prototype, "fill", void 0); __decorate([ Property(false) ], LeafItemSettings.prototype, "autoFill", void 0); __decorate([ Complex({}, Border) ], LeafItemSettings.prototype, "border", void 0); __decorate([ Property(0) ], LeafItemSettings.prototype, "gap", void 0); __decorate([ Property(10) ], LeafItemSettings.prototype, "padding", void 0); __decorate([ Property(1) ], LeafItemSettings.prototype, "opacity", void 0); __decorate([ Property(true) ], LeafItemSettings.prototype, "showLabels", void 0); __decorate([ Property(null) ], LeafItemSettings.prototype, "labelPath", void 0); __decorate([ Property(null) ], LeafItemSettings.prototype, "labelFormat", void 0); __decorate([ Property('TopLeft') ], LeafItemSettings.prototype, "labelPosition", void 0); __decorate([ Complex({ color: null, size: null, fontFamily: null }, Font) ], LeafItemSettings.prototype, "labelStyle", void 0); __decorate([ Property(null) ], LeafItemSettings.prototype, "labelTemplate", void 0); __decorate([ Property('Center') ], LeafItemSettings.prototype, "templatePosition", void 0); __decorate([ Property('Trim') ], LeafItemSettings.prototype, "interSectAction", void 0); __decorate([ Collection([], ColorMapping) ], LeafItemSettings.prototype, "colorMapping", void 0); /** * Sets and gets the options for customizing the tooltip of the treemap. */ class TooltipSettings extends ChildProperty { } __decorate([ Property(false) ], TooltipSettings.prototype, "visible", void 0); __decorate([ Property('') ], TooltipSettings.prototype, "template", void 0); __decorate([ Property(null) ], TooltipSettings.prototype, "format", void 0); __decorate([ Property(null) ], TooltipSettings.prototype, "fill", void 0); __decorate([ Property(0.75) ], TooltipSettings.prototype, "opacity", void 0); __decorate([ Property(['Circle']) ], TooltipSettings.prototype, "markerShapes", void 0); __decorate([ Complex({ width: null, color: null }, Border) ], TooltipSettings.prototype, "border", void 0); __decorate([ Complex({ fontFamily: null, size: null, fontWeight: null, opacity: null }, Font) ], TooltipSettings.prototype, "textStyle", void 0); /** * Sets and gets the options for customizing the selection of the leaf items in treemap. */ class SelectionSettings extends ChildProperty { } __decorate([ Property(false) ], SelectionSettings.prototype, "enable", void 0); __decorate([ Property(null) ], SelectionSettings.prototype, "fill", void 0); __decorate([ Property('0.5') ], SelectionSettings.prototype, "opacity", void 0); __decorate([ Complex({}, Border) ], SelectionSettings.prototype, "border", void 0); __decorate([ Property('Item') ], SelectionSettings.prototype, "mode", void 0); /** * Sets and gets the options for customizing the highlighting of the treemap item, * when the mouse hover is performed in it. */ class HighlightSettings extends ChildProperty { } __decorate([ Property(false) ], HighlightSettings.prototype, "enable", void 0); __decorate([ Property('#808080') ], HighlightSettings.prototype, "fill", void 0); __decorate([ Property('0.5') ], HighlightSettings.prototype, "opacity", void 0); __decorate([ Complex({}, Border) ], HighlightSettings.prototype, "border", void 0); __decorate([ Property('Item') ], HighlightSettings.prototype, "mode", void 0); /** * Sets and gets the options for customizing the level leaf items of the treemap. */ class LevelSettings extends ChildProperty { } __decorate([ Property(null) ], LevelSettings.prototype, "groupPath", void 0); __decorate([ Property(0) ], LevelSettings.prototype, "groupGap", void 0); __decorate([ Property(10) ], LevelSettings.prototype, "groupPadding", void 0); __decorate([ Complex({}, Border) ], LevelSettings.prototype, "border", void 0); __decorate([ Property(null) ], LevelSettings.prototype, "fill", void 0); __decorate([ Property(false) ], LevelSettings.prototype, "autoFill", void 0); __decorate([ Property(1) ], LevelSettings.prototype, "opacity", void 0); __decorate([ Property(true) ], LevelSettings.prototype, "showHeader", void 0); __decorate([ Property(20) ], LevelSettings.prototype, "headerHeight", void 0); __decorate([ Property(null) ], LevelSettings.prototype, "headerTemplate", void 0); __decorate([ Property(null) ], LevelSettings.prototype, "headerFormat", void 0); __decorate([ Property('Near') ], LevelSettings.prototype, "headerAlignment", void 0); __decorate([ Complex({ color: null, size: '13px', fontFamily: null }, Font) ], LevelSettings.prototype, "headerStyle", void 0); __decorate([ Property('TopLeft') ], LevelSettings.prototype, "templatePosition", void 0); __decorate([ Collection([], ColorMapping) ], LevelSettings.prototype, "colorMapping", void 0); /** * Specifies the size parameters. */ class Size { constructor(width, height) { this.width = width; this.height = height; } } /** * * @param {string} value - specifies the text. * @param {number} containerSize - specifies the container size value. * @returns {number} - Returns the number value which is converted from string. */ function stringToNumber(value, containerSize) { if (value !== null && value !== undefined) { return value.indexOf('%') !== -1 ? (containerSize / 100) * parseInt(value, 10) : parseInt(value, 10); } return null; } /** * Internal use of type rect * * @private */ class Rect { constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } } /** * Internal use of rectangle options * * @private */ class RectOption { constructor(id, fill, border, opacity, rect, dashArray) { this.y = rect.y; this.x = rect.x; this.height = rect.height; this.width = rect.width; this.id = id; this.fill = fill; this.opacity = opacity; this.stroke = border.color; this['stroke-width'] = border.width; this['stroke-dasharray'] = dashArray; } } class PathOption { constructor(id, fill, width, color, opacity, dashArray, d) { this.id = id; this.opacity = opacity; this.fill = fill; this.stroke = color; this['stroke-width'] = width; this['stroke-dasharray'] = dashArray; this.d = d; } } /** * Function to measure the height and width of the text. * * @param {string} text - Specifies the text. * @param {FontModel} font - Specifies the font. * @returns {Size} - Returns the size. * @private */ function measureText(text, font) { let measureObject = document.getElementById('treeMapMeasureText'); if (measureObject === null) { measureObject = createElement('text', { id: 'treeMapMeasureText' }); document.body.appendChild(measureObject); } measureObject.innerHTML = SanitizeHtmlHelper.sanitize(text); measureObject.style.position = 'absolute'; measureObject.style.fontSize = font.size; measureObject.style.fontWeight = font.fontWeight; measureObject.style.fontStyle = font.fontStyle; measureObject.style.fontFamily = font.fontFamily; measureObject.style.visibility = 'hidden'; measureObject.style.top = '-100'; measureObject.style.left = '0'; measureObject.style.whiteSpace = 'nowrap'; // For bootstrap line height issue measureObject.style.lineHeight = 'normal'; return new Size(measureObject.clientWidth, measureObject.clientHeight); } /** * Internal use of text options * * @private */ class TextOption { constructor(id, x, y, anchor, text, transform = '', baseLine, connectorText) { this.transform = ''; this.baseLine = 'auto'; this.id = id; this.text = text; this.transform = transform; this.anchor = anchor; this.x = x; this.y = y; this.baseLine = baseLine; this.connectorText = connectorText; } } /** * Trim the title text * * @param {number} maxWidth - Specifies the maximum width * @param {string} text - Specifies the text * @param {FontModel} font - Specifies the font * @returns {string} - Returns the string * @private */ function textTrim(maxWidth, text, font) { let label = text; let size = measureText(text, font).width; if (size > maxWidth) { const textLength = text.length; for (let i = textLength - 1; i >= 0; --i) { label = text.substring(0, i) + '...'; size = measureText(label, font).width; if (size <= maxWidth || label.length < 4) { if (label.length < 4) { label = ' '; } return label; } } } return label; } /** * Specifies the location parameters. */ class Location { constructor(x, y) { this.x = x; this.y = y; } } /** * Method to calculate x position of title * * @param {Rect} location - Specifies the location of text. * @param {Alignment} alignment - Specifies the alignment of the text. * @param {Size} textSize - Specifies the size of the text. * @param {type} type - Specifies whether the provided text is title or subtitle. * @returns {Location} - Returns the location of text. * @private */ function findPosition(location, alignment, textSize, type) { let x; switch (alignment) { case 'Near': x = location.x; break; case 'Center': x = (type === 'title') ? (location.width / 2 - textSize.width / 2) : ((location.x + (location.width / 2)) - textSize.width / 2); break; case 'Far': x = (type === 'title') ? (location.width - location.y - textSize.width) : ((location.x + location.width) - textSize.width); break; } const y = (type === 'title') ? location.y + (textSize.height / 2) : ((location.y + location.height / 2) + textSize.height / 2); return new Location(x, y); } /** * * @param {SvgRenderer} renderer - Specifies the rendering element of the SVG. * @param {any} renderOptions - Specifies the settings of the text. * @param {string} text - Specifies the text. * @returns {HTMLElement} - Returns the HTML element for the text. */ function createTextStyle(renderer, renderOptions, text) { const htmlObject = renderer.createText(renderOptions, text); htmlObject.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); htmlObject.style['user-select'] = 'none'; htmlObject.style['-moz-user-select'] = 'none'; htmlObject.style['-webkit-touch-callout'] = 'none'; htmlObject.style['-webkit-user-select'] = 'none'; htmlObject.style['-khtml-user-select'] = 'none'; htmlObject.style['-ms-user-select'] = 'none'; htmlObject.style['-o-user-select'] = 'none'; return htmlObject; } /** * Internal rendering of text * * @param {TextOption} options - Specifies the text option * @param {FontModel} font - Specifies the font model * @param {string} color - Specifies the color * @param {HTMLElement | Element} parent - Specifies the parent element of the text * @param {boolean} isMinus - Specifies the boolean value * @returns {Element} - Returns the element * @private */ function renderTextElement(options, font, color, parent, isMinus = false) { const renderOptions = { 'font-size': font.size, 'font-style': font.fontStyle, 'font-family': font.fontFamily, 'font-weight': font.fontWeight, 'text-anchor': options.anchor, 'transform': options.transform, 'opacity': font.opacity, 'dominant-baseline': options.baseLine, 'id': options.id, 'x': options.x, 'y': options.y, 'fill': color }; const text = typeof options.text === 'string' ? options.text : isMinus ? options.text[options.text.length - 1] : options.text[0]; let tspanElement; const renderer = new SvgRenderer(''); let height; let htmlObject; const breadCrumbText = !isNullOrUndefined(text) && !isNullOrUndefined(options.connectorText) ? (text.search(options.connectorText[1]) >= 0) : false; if (breadCrumbText) { const drilledLabel = text; const spacing = 5; const drillLevelText = drilledLabel.split('#'); for (let z = 0; z < drillLevelText.length; z++) { let drillText = (drillLevelText[z].search(options.connectorText) !== -1 && !isNullOrUndefined(options.connectorText)) ? options.connectorText : drillLevelText[z]; renderOptions['id'] = options.id + '_' + z; htmlObject = createTextStyle(renderer, renderOptions, drillText); if (z % 2 === 0 && z !== 0) { const re = /\s+/g; drillText = drillText.replace(re, '&nbsp'); } const size = measureText(drillText, font); renderOptions['x'] = z !== 0 ? renderOptions['x'] + size.width : renderOptions['x'] + size.width + spacing; parent.appendChild(htmlObject); } } else { htmlObject = createTextStyle(renderer, renderOptions, text); parent.appendChild(htmlObject); } if (typeof options.text !== 'string' && options.text.length > 1) { for (let i = 1, len = options.text.length; i < len; i++) { height = (measureText(options.text[i], font).height); tspanElement = renderer.createTSpan({ 'x': options.x, 'id': options.id, 'y': (options.y) + (i * height) }, options.text[i]); htmlObject.appendChild(tspanElement); } parent.appendChild(htmlObject); } return htmlObject; } /** * * @param {string} targetId - Specifies the id of the element to which template is to be appended. * @param {Element} targetElement - Specifies the element to which template is to be appended. * @param {string} contentItemTemplate - Specifies the content to be appended as template. * @returns {void} */ function setItemTemplateContent(targetId, targetElement, contentItemTemplate) { const itemSelect = targetId.split('_RectPath')[0]; let itemTemplate; if (targetId.indexOf('_LabelTemplate') > -1) { itemTemplate = targetElement; } else { itemTemplate = document.querySelector('#' + itemSelect + '_LabelTemplate'); } if (!isNullOrUndefined(itemTemplate)) { itemTemplate.innerHTML = contentItemTemplate; } } /** * * @param {string} id - Specifies the id of the element. * @returns {Element} - Returns the element. */ function getElement(id) { return document.getElementById(id); } /** * * @param {any} a - Specifies the first order of TreeMap leaf elements. * @param {any} b - Specifies the second order of TreeMap leaf elements. * @returns {number} - Returns the order of the TreeMap leaf element. */ function itemsToOrder(a, b) { return a['weight'] === b['weight'] ? 0 : a['weight'] < b['weight'] ? 1 : -1; } /** * * @param {string[]} source - Specifies the data from the data source. * @param {string} pathName - Specifies the path name in the data source. * @param {any} processData - Specifies the data source object. * @param {TreeMap} treemap - Specifies the treemap instance. * @returns {boolean} - Specifies whether data is available in the data source or not. */ function isContainsData(source, pathName, processData, treemap) { let isExist = false; let name = ''; let path; const leaf = treemap.leafItemSettings; for (let i = 0; i < source.length; i++) { path = treemap.levels[i] ? treemap.levels[i].groupPath : leaf.labelPath ? leaf.labelPath : treemap.weightValuePath; const data = processData[path] || 'undefined'; if (source[i] === data) { name += data + (i === source.length - 1 ? '' : '#'); if (name === pathName) { isExist = true; break; } } } return isExist; } /** * * @param {any} data - Specifies the data to which the children elements to be found. * @returns {any} - Returns the children elements of the TreeMap leaf element. */ function findChildren(data) { let children; if (data) { const keys = Object.keys(data); children = {}; for (let i = 0; i < keys.length; i++) { if (data[keys[i]] instanceof Array) { children['values'] = data[keys[i]]; children['key'] = keys[i]; break; } } } return children; } /** * * @param {any} data - Specifies the data to which highlight must be done. * @param {items} items - Specifies the data source items. * @param {string} mode - Specifies the mode of highlight. * @param {TreeMap} treeMap - Specifies the treemap instance. * @returns {string[]} - Returns the highlighted items. */ function findHightLightItems(data, items, mode, treeMap) { if (mode === 'Child') { items.push(data['levelOrderName']); const children = findChildren(data)['values']; if (children && children.length > 0) { for (let i = 0; i < children.length; i++) { if (items.indexOf(children[i]['levelOrderName']) === -1) { items.push(children[i]['levelOrderName']); } } for (let j = 0; j < children.length; j++) { findHightLightItems(children[j], items, mode, treeMap); } } } else if (mode === 'Parent') { if (typeof data['levelOrderName'] === 'string' && items.indexOf(data['levelOrderName']) === -1) { items.push(data['levelOrderName']); findHightLightItems(data['parent'], items, mode, treeMap); } } else if (mode === 'All') { const parentName = data['levelOrderName'].split('#')[0]; let currentItem; for (let i = 0; i < treeMap.layout.renderItems.length; i++) { currentItem = treeMap.layout.renderItems[i]; if ((currentItem['levelOrderName']).indexOf(parentName) > -1 && items.indexOf(currentItem['levelOrderName']) === -1) { items.push(currentItem['levelOrderName']); } } } else { items.push(data['levelOrderName']); } return items; } /** * Function to compile the template function for maps. * * @param {string} template - Specifies the template * @returns {Function} - Returns the template function * @private */ function getTemplateFunction(template) { let templateFn = null; try { if (typeof template !== 'function' && document.querySelectorAll(template).length) { templateFn = compile(document.querySelector(template).innerHTML.trim()); } else { templateFn = compile(template); } } catch (e) { templateFn = compile(template); } return templateFn; } /** * @private * @param {HTMLCollection} element - Specifies the element * @param {string} labelId - Specifies the label id * @param {Object} data - Specifies the data * @returns {HTMLElement} - Returns the element */ function convertElement(element, labelId, data) { const childElement = createElement('div', { id: labelId }); childElement.style.cssText = 'position: absolute;pointer-events: auto;'; let elementLength = element.length; while (elementLength > 0) { childElement.appendChild(element[0]); elementLength--; } let templateHtml = childElement.innerHTML; const keys = Object.keys(data); for (let i = 0; i < keys.length; i++) { const regExp = RegExp; templateHtml = templateHtml.replace(new regExp('{{:' + keys[i] + '}}', 'g'), data[keys[i].toString()]); } childElement.innerHTML = templateHtml; return childElement; } /** * * @param {Rect} rect - Specifies the area. * @param {LabelPosition} position - Specifies the position * @param {Size} labelSize - Specifies the label size. * @param {string} type - Specifies the type. * @param {TreeMap} treemap - Specifies the treemap instance. * @returns {Location} - Returns the text location. */ function findLabelLocation(rect, position, labelSize, type, treemap) { const location = new Location(0, 0); const padding = 5; const paddings = 2; const x = (type === 'Template') ? treemap.areaRect.x : 0; const y = (type === 'Template') ? treemap.areaRect.y : 0; location.x = (Math.abs(x - ((position.indexOf('Left') > -1) ? rect.x + padding : !(position.indexOf('Right') > -1) ? rect.x + ((rect.width / 2) - (labelSize.width / 2)) : (rect.x + rect.width) - labelSize.width))) - paddings; if (treemap.enableDrillDown && (treemap.renderDirection === 'BottomLeftTopRight' || treemap.renderDirection === 'BottomRightTopLeft')) { location.y = Math.abs((rect.y + rect.height) - labelSize.height + padding); } else { location.y = Math.abs(y - ((position.indexOf('Top') > -1) ? (type === 'Template' ? rect.y : rect.y + labelSize.height) : !(position.indexOf('Bottom') > -1) ? type === 'Template' ? (rect.y + ((rect.height / 2) - (labelSize.height / 2))) : (rect.y + (rect.height / 2) + labelSize.height / 4) : (rect.y + rect.height) - labelSize.height)); } return location; } /** * * @param {HTMLElement} element - Specifies the element to be measured. * @param {HTMLElement} parentElement - Specifies the parent element of the element to be measured. * @returns {Size} - Returns the element size. */ function measureElement(element, parentElement) { const size = new Size(0, 0); parentElement.appendChild(element); size.height = element.offsetHeight; size.width = element.offsetWidth; const measureElementId = document.getElementById(element.id); measureElementId.parentNode.removeChild(measureElementId); return size; } /** * * @param {Rect} rect - Specifies the area. * @returns {number} - Returns the area width. */ function getArea(rect) { return (rect.width - rect.x) * (rect.height - rect.y); } /** * * @param {Rect} input - Specifies input for the calculation. * @returns {number} - Returns the shortest edge. */ function getShortestEdge(input) { const container = convertToContainer(input); const width = container.width; const height = container.height; const result = Math.min(width, height); return result; } /** * * @param {Rect} rect - Specifies the rectangle bounds of the container. * @returns {Rect} - Returns the rectangle bounds. */ function convertToContainer(rect) { const x = rect.x; const y = rect.y; const width = rect.width; const height = rect.height; return { x: x, y: y, width: width - x, height: height - y }; } /** * * @param {Rect} container - Specifies the rectangle bounds of the container. * @returns {Rect} - Returns the rectangle bounds. */ function convertToRect(container) { const xOffset = container.x; const yOffset = container.y; const width = container.width; const height = container.height; return { x: xOffset, y: yOffset, width: xOffset + width, height: yOffset + height }; } /** * * @param {number} pageX - Specifies the horizontal position of the mouse location. * @param {number} pageY - Specifies the vertical position of the mouse location. * @param {Element} element - Specifies the element to which the click is done. * @returns {Location} - Returns the clicked location. */ function getMousePosition(pageX, pageY, element) { const elementRect = element.getBoundingClientRect(); const pageXOffset = element.ownerDocument.defaultView.pageXOffset; const pageYOffset = element.ownerDocument.defaultView.pageYOffset; const clientTop = element.ownerDocument.documentElement.clientTop; const clientLeft = element.ownerDocument.documentElement.clientLeft; const positionX = elementRect.left + pageXOffset - clientLeft; const positionY = elementRect.top + pageYOffset - clientTop; return new Location((pageX - positionX), (pageY - positionY)); } /** * * @param {ColorMappingModel[]} colorMapping - Specifies the color mapping instance. * @param {string} equalValue - Specifies the equal value. * @param {number | string} value - Specifies the range value. * @returns {any} - Returns the color mapping object. * @private */ function colorMap(colorMapping, equalValue, value) { let fill; const paths = []; let opacity; if (isNullOrUndefined(equalValue) && (isNullOrUndefined(value) && isNaN(value))) { return null; } for (let i = 0; i < colorMapping.length; i++) { let isEqualColor = false; const dataValue = value; if (!isNullOrUndefined(colorMapping[i].from) && !isNullOrUndefined(colorMapping[i].to) && !isNullOrUndefined(colorMapping[i].value)) { if ((value >= colorMapping[i].from && colorMapping[i].to >= value) && (colorMapping[i].value === equalValue)) { isEqualColor = true; if (Object.prototype.toString.call(colorMapping[i].color) === '[object Array]') { fill = !isEqualColor ? colorCollections(colorMapping[i], dataValue) : colorMapping[i].color[0]; } else { fill = colorMapping[i].color; } } } else if ((!isNullOrUndefined(colorMapping[i].from) && !isNullOrUndefined(colorMapping[i].to)) || !isNullOrUndefined((colorMapping[i].value))) { if ((value >= colorMapping[i].from && colorMapping[i].to >= value) || (colorMapping[i].value === equalValue)) { if (colorMapping[i].value === equalValue) { isEqualColor = true; } if (Object.prototype.toString.call(colorMapping[i].color) === '[object Array]') { fill = !isEqualColor ? colorCollections(colorMapping[i], dataValue) : colorMapping[i].color[0]; } else { fill = colorMapping[i].color; } } } if (((value >= colorMapping[i].from && value <= colorMapping[i].to) || (colorMapping[i].value === equalValue)) && !isNullOrUndefined(colorMapping[i].minOpacity) && !isNullOrUndefined(colorMapping[i].maxOpacity) && fill) { opacity = deSaturationColor(colorMapping[i], value); } if ((fill === '' || isNullOrUndefined(fill)) && isNullOrUndefined(colorMapping[i].from) && isNullOrUndefined(colorMapping[i].to) && isNullOrUndefined(colorMapping[i].minOpacity) && isNullOrUndefined(colorMapping[i].maxOpacity) && isNullOrUndefined(colorMapping[i].value)) { fill = (Object.prototype.toString.call(colorMapping[i].color) === '[object Array]') ? colorMapping[i].color[0] : colorMapping[i].color; } opacity = !isNullOrUndefined(opacity) ? opacity : '1'; paths.push(fill); } for (let j = paths.length - 1; j >= 0; j--) { fill = paths[j]; j = (fill) ? -1 : j; } return { fill: fill, opacity: opacity }; } /** * * @param {ColorMappingModel} colorMapping - Specifies the color mapping object. * @param {number} rangeValue - Specifies the range value. * @returns {string} - Returns the opacity for the color mapping. * @private */ function deSaturationColor(colorMapping, rangeValue) { let opacity = 1; if ((rangeValue >= colorMapping.from && rangeValue <= colorMapping.to)) { const ratio = (rangeValue - colorMapping.from) / (colorMapping.to - colorMapping.from); opacity = (ratio * (colorMapping.maxOpacity - colorMapping.minOpacity)) + colorMapping.minOpacity; } return opacity.toString(); } /** * * @param {ColorMappingModel} colorMap - Specifies the color mapping object. * @param {number} value - Specifies the range value. * @returns {string} - Returns the fill color. */ function colorCollections(colorMap, value) { const gradientFill = getColorByValue(colorMap, value); return gradientFill; } /** * * @param {number} r - Specifies the red color value. * @param {number} g - Specifies the green color value. * @param {number} b - Specifies the blue color value. * @returns {string} - Returns the fill color. */ function rgbToHex(r, g, b) { return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b); } /** * * @param {ColorMappingModel} colorMap - Specifies the color mapping. * @param {number} value - Specifies the range value. * @returns {string} - Returns the fill color. */ function getColorByValue(colorMap, value) { let color = ''; let rbg; if (Number(value) === colorMap.from) { color = colorMap.color[0]; } else if (Number(value) === colorMap.to) { color = colorMap.color[colorMap.color.length - 1]; } else { rbg = getGradientColor(Number(value), colorMap); color = rgbToHex(rbg.r, rbg.g, rbg.b); } return color; } /** * * @param {number} value - Specifies the range value. * @param {ColorMappingModel} colorMap - Specifies the color mapping. * @returns {ColorValue} - Returns the color value object. */ function getGradientColor(value, colorMap) { const previousOffset = colorMap.from; const nextOffset = colorMap.to; let percent = 0; const full = nextOffset - previousOffset; let midColor; percent = (value - previousOffset) / full; let previousColor; let nextColor; if (colorMap.color.length <= 2) { previousColor = colorMap.color[0].charAt(0) === '#' ? colorMap.color[0] : colorNameToHex(colorMap.color[0]); nextColor = colorMap.color[colorMap.color.length - 1].charAt(0) === '#' ? colorMap.color[colorMap.color.length - 1] : colorNameToHex(colorMap.color[colorMap.color.length - 1]); } else { previousColor = colorMap.color[0].charAt(0) === '#' ? colorMap.color[0] : colorNameToHex(colorMap.color[0]); nextColor = colorMap.color[colorMap.color.length - 1].charAt(0) === '#' ? colorMap.color[colorMap.color.length - 1] : colorNameToHex(colorMap.color[colorMap.color.length - 1]); const a = full / (colorMap.color.length - 1); let b; let c; const length = colorMap.color.length - 1; const splitColorValueOffset = []; let splitColor = {}; for (let j = 1; j < length; j++) { c = j * a; b = previousOffset + c; splitColor = { b: b, color: colorMap.color[j] }; splitColorValueOffset.push(splitColor); } for (let i = 0; i < splitColorValueOffset.length; i++) { if (previousOffset <= value && value <= splitColorValueOffset[i]['b'] && i === 0) { midColor = splitColorValueOffset[i]['color'].charAt(0) === '#' ? splitColorValueOffset[i]['color'] : colorNameToHex(splitColorValueOffset[i]['color']); nextColor = midColor; percent = value < splitColorValueOffset[i]['b'] ? 1 - Math.abs((value - splitColorValueOffset[i]['b']) / a) : (value - splitColorValueOffset[i]['b']) / a; } else if (splitColorValueOffset[i]['b'] <= value && value <= nextOffset && i === (splitColorValueOffset.length - 1)) { midColor = splitColorValueOffset[i]['color'].charAt(0) === '#' ? splitColorValueOffset[i]['color'] : colorNameToHex(splitColorValueOffset[i]['color']); previousColor = midColor; percent = value < splitColorValueOffset[i]['b'] ? 1 - Math.abs((value - splitColorValueOffset[i]['b']) / a) : (value - splitColorValueOffset[i]['b']) / a; } if (i !== splitColorValueOffset.length - 1 && i < splitColorValueOffset.length) { if (splitColorValueOffset[i]['b'] <= value && value <= splitColorValueOffset[i + 1]['b']) { midColor = splitColorValueOffset[i]['color'].charAt(0) === '#' ? splitColorValueOffset[i]['color'] : colorNameToHex(splitColorValueOffset[i]['color']); previousColor = midColor; nextColor = splitColorValueOffset[i + 1]['color'].charAt(0) === '#' ? splitColorValueOffset[i + 1]['color'] : colorNameToHex(splitColorValueOffset[i + 1]['color']); percent = Math.abs((value - splitColorValueOffset[i + 1]['b'])) / a; } } } } return getPercentageColor(percent, previousColor, nextColor); } /** * * @param {number} percent - Specifies the percentage of the color. * @param {number} previous - Specifies the previous color. * @param {number} next - Specifies the next color. * @returns {ColorValue} - Returns the color value object. */ function getPercentageColor(percent, previous, next) { const nextColor = next.split('#')[1]; const prevColor = previous.split('#')[1]; const r = getPercentage(percent, parseInt(prevColor.substr(0, 2), 16), parseInt(nextColor.substr(0, 2), 16)); const g = getPercentage(percent, parseInt(prevColor.substr(2, 2), 16), parseInt(nextColor.substr(2, 2), 16)); const b = getPercentage(percent, parseInt(prevColor.substr(4, 2), 16), parseInt(nextColor.substr(4, 2), 16)); return new ColorValue(r, g, b); } /** * * @param {number} percent - Specifies the percentage of the color. * @param {number} previous - Specifies the previous color. * @param {number} next - Specifies the next color. * @returns {number} - Returns the color value. */ function getPercentage(percent, previous, next) { const full = next - previous; return Math.round((previous + (full * percent))); } /** * * @param {number} maximumWidth - Specifies the length of the text. * @param {string} dataLabel - Specifies the label. * @param {FontModel} font - Specifies the font of the label. * @returns {string[]} - Returns the labels. */ function wordWrap(maximumWidth, dataLabel, font) { const textCollection = dataLabel.split(' '); let label = ''; const labelCollection = []; let text; for (let i = 0, len = textCollection.length; i < len; i++) { text = textCollection[i]; if (measureText(label.concat(text), font).width < maximumWidth) { label = label.concat((label === '' ? '' : ' ') + text); } else { if (label !== '') { labelCollection.push(textTrim(maximumWidth, label, font)); label = text; } else { labelCollection.push(textTrim(maximumWidth, text, font)); text = ''; } } if (label && i === len - 1) { labelCollection.push(textTrim(maximumWidth, label, font)); } } return labelCollection; } /** * * @param {number} maxWidth - Specifies the length of the text. * @param {string} label - Specifies the label. * @param {FontModel} font - Specifies the font of the label. * @returns {string[]} - Returns the labels. */ function textWrap(maxWidth, label, font) { const resultText = []; let currentLength = 0; let totalWidth = measureText(label, font).width; const totalLength = label.length; if (maxWidth >= totalWidth) { resultText.push(label); return resultText; } else { for (let i = label.length; i > currentLength; i--) { const sliceString = label.slice(currentLength, i); totalWidth = measureText(sliceString, font).width; if (totalWidth <= maxWidth) { resultText.push(sliceString); currentLength += sliceString.length; if (totalLength === currentLength) { return resultText; } i = totalLength + 1; } } } return resultText; } /** * hide function * * @param {number} maxWidth - Specifies the maximum width. * @param {number} maxHeight - Specifies the maximum height. * @param {string} text - Specifies the text. * @param {FontModel} font - Specifies the font. * @returns {string} - Returns the hidden text. * @private */ function hide(maxWidth, maxHeight, text, font) { let hideText = text; const textSize = measureText(text, font); hideText = (textSize.width > maxWidth || textSize.height > maxHeight) ? ' ' : text; return hideText; } /** * * @param {number} a - Specifies the first value of the leaf. * @param {number} b - Specifies the second value of the leaf. * @returns {number} - Returns whether values are equal or not. */ function orderByArea(a, b) { if (a['itemArea'] === b['itemArea']) { return 0; } else if (a['itemArea'] < b['itemArea']) { return 1; } return -1; } /** * * @param {TreeMap} treemap - Specifies the treemap instance. * @param {Element} element - Specifies the selected TreeMap leaf item. * @param {string} className -Specifies the selected class name. * @returns {void} */ function maintainSelection(treemap, element, className) { const elementId = treemap.levelSelection; if (elementId) { for (let index = 0; index < elementId.length; index++) { if (element.getAttribute('id') === elementId[index] || element.children[0].id === elementId[index]) { if (element.childElementCount > 0 && element.children[0].id.indexOf('_Group') === -1) { element.children[0].setAttribute('class', className); applyOptions(element.childNodes[0], { border: treemap.selectionSettings.border, fill: treemap.selectionSettings.fill, opacity: treemap.selectionSettings.opacity }); } } else { element.setAttribute('class', ''); } } } } /** * * @param {TreeMap} treemap - Specifies the treemap instance. * @param {Element} legendGroup - Specifies the selected element. * @returns {void} */ function legendMaintain(treemap, legendGroup) { const elementId = treemap.legendId; if (elementId) { for (let i = 0; i < elementId.length; i++) { if (treemap.legendSettings.mode === 'Interactive') { for (let j = 0; j < legendGroup.childElementCount; j++) { if (legendGroup.childNodes[j]['id'] === elementId[i] || parseFloat(legendGroup.childNodes[j]['id'].split('Index_')[1]) === parseFloat(elementId[i].split('Index_')[1])) { const treemapSVGRectElement = legendGroup.childNodes[j]; treemapSVGRectElement.setAttribute('fill', treemap.selectionSettings.fill); treemapSVGRectElement.setAttribute('opacity', treemap.selectionSettings.opacity); if (treemapSVGRectElement.id.indexOf('Text') === -1) { treemapSVGRectElement.setAttribute('stroke-width', (treemap.selectionSettings.border.width).toString()); treemapSVGRectElement.setAttribute('stroke', treemap.selectionSettings.border.color); } else { treemapSVGRectElement.setAttribute('stroke', null); treemapSVGRectElement.setAttribute('stroke-width', null); } } } } else { const legendItem = document.getElementById(elementId[i]); if (!isNullOrUndefined(legendItem)) { legendItem.setAttribute('fill', treemap.selectionSettings.fill); legendItem.setAttribute('opacity', treemap.selectionSettings.opacity); if (legendItem.id.indexOf('Text') === -1) { legendItem.setAttribute('stroke', treemap.selectionSettings.border.color); legendItem.setAttribute('stroke-width', (treemap.selectionSettings.border.width).toString()); } else { legendItem.setAttribute('stroke', null); legendItem.setAttribute('stroke-width', null); } } } } } } /** * * @