722 lines
22 KiB
TypeScript
722 lines
22 KiB
TypeScript
import {
|
|
Component,
|
|
ElementRef,
|
|
Input,
|
|
OnChanges,
|
|
OnDestroy,
|
|
OnInit,
|
|
SimpleChanges,
|
|
ViewEncapsulation
|
|
} from '@angular/core';
|
|
|
|
import * as Chartist from '@mempool/chartist';
|
|
|
|
/**
|
|
* Possible chart types
|
|
* @type {String}
|
|
*/
|
|
export type ChartType = 'Pie' | 'Bar' | 'Line';
|
|
|
|
export type ChartInterfaces =
|
|
| Chartist.IChartistPieChart
|
|
| Chartist.IChartistBarChart
|
|
| Chartist.IChartistLineChart;
|
|
export type ChartOptions =
|
|
| Chartist.IBarChartOptions
|
|
| Chartist.ILineChartOptions
|
|
| Chartist.IPieChartOptions;
|
|
export type ResponsiveOptionTuple = Chartist.IResponsiveOptionTuple<
|
|
ChartOptions
|
|
>;
|
|
export type ResponsiveOptions = ResponsiveOptionTuple[];
|
|
|
|
/**
|
|
* Represent a chart event.
|
|
* For possible values, check the Chartist docs.
|
|
*/
|
|
export interface ChartEvent {
|
|
[eventName: string]: (data: any) => void;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-chartist',
|
|
template: '<ng-content></ng-content>',
|
|
styleUrls: ['./chartist.component.scss'],
|
|
encapsulation: ViewEncapsulation.None,
|
|
})
|
|
export class ChartistComponent implements OnInit, OnChanges, OnDestroy {
|
|
@Input()
|
|
// @ts-ignore
|
|
public data: Promise<Chartist.IChartistData> | Chartist.IChartistData;
|
|
|
|
// @ts-ignore
|
|
@Input() public type: Promise<ChartType> | ChartType;
|
|
|
|
@Input()
|
|
// @ts-ignore
|
|
public options: Promise<Chartist.IChartOptions> | Chartist.IChartOptions;
|
|
|
|
@Input()
|
|
// @ts-ignore
|
|
public responsiveOptions: Promise<ResponsiveOptions> | ResponsiveOptions;
|
|
|
|
// @ts-ignore
|
|
@Input() public events: ChartEvent;
|
|
|
|
// @ts-ignore
|
|
public chart: ChartInterfaces;
|
|
|
|
private element: HTMLElement;
|
|
|
|
constructor(element: ElementRef) {
|
|
this.element = element.nativeElement;
|
|
}
|
|
|
|
public ngOnInit(): Promise<ChartInterfaces> {
|
|
if (!this.type || !this.data) {
|
|
Promise.reject('Expected at least type and data.');
|
|
}
|
|
|
|
return this.renderChart().then((chart) => {
|
|
if (this.events !== undefined) {
|
|
this.bindEvents(chart);
|
|
}
|
|
|
|
return chart;
|
|
});
|
|
}
|
|
|
|
public ngOnChanges(changes: SimpleChanges): void {
|
|
this.update(changes);
|
|
}
|
|
|
|
public ngOnDestroy(): void {
|
|
if (this.chart) {
|
|
this.chart.detach();
|
|
}
|
|
}
|
|
|
|
public renderChart(): Promise<ChartInterfaces> {
|
|
const promises: any[] = [
|
|
this.type,
|
|
this.element,
|
|
this.data,
|
|
this.options,
|
|
this.responsiveOptions
|
|
];
|
|
|
|
return Promise.all(promises).then((values) => {
|
|
const [type, ...args]: any = values;
|
|
|
|
if (!(type in Chartist)) {
|
|
throw new Error(`${type} is not a valid chart type`);
|
|
}
|
|
|
|
this.chart = (Chartist as any)[type](...args);
|
|
|
|
return this.chart;
|
|
});
|
|
}
|
|
|
|
public update(changes: SimpleChanges): void {
|
|
if (!this.chart || 'type' in changes) {
|
|
this.renderChart();
|
|
} else {
|
|
if (changes.data) {
|
|
this.data = changes.data.currentValue;
|
|
}
|
|
|
|
if (changes.options) {
|
|
this.options = changes.options.currentValue;
|
|
}
|
|
|
|
(this.chart as any).update(this.data, this.options);
|
|
}
|
|
}
|
|
|
|
public bindEvents(chart: any): void {
|
|
for (const event of Object.keys(this.events)) {
|
|
chart.on(event, this.events[event]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Chartist.js plugin to display a "target" or "goal" line across the chart.
|
|
* Only tested with bar charts. Works for horizontal and vertical bars.
|
|
*/
|
|
(function(window, document, Chartist) {
|
|
'use strict';
|
|
|
|
const defaultOptions = {
|
|
// The class name so you can style the text
|
|
className: 'ct-target-line',
|
|
// The axis to draw the line. y == vertical bars, x == horizontal
|
|
axis: 'y',
|
|
// What value the target line should be drawn at
|
|
value: null
|
|
};
|
|
|
|
Chartist.plugins = Chartist.plugins || {};
|
|
|
|
Chartist.plugins.ctTargetLine = function (options: any) {
|
|
options = Chartist.extend({}, defaultOptions, options);
|
|
return function ctTargetLine (chart: any) {
|
|
|
|
chart.on('created', function(context: any) {
|
|
const projectTarget = {
|
|
y: function (chartRect: any, bounds: any, value: any) {
|
|
const targetLineY = chartRect.y1 - (chartRect.height() / bounds.max * value);
|
|
|
|
return {
|
|
x1: chartRect.x1,
|
|
x2: chartRect.x2,
|
|
y1: targetLineY,
|
|
y2: targetLineY
|
|
};
|
|
},
|
|
x: function (chartRect: any, bounds: any, value: any) {
|
|
const targetLineX = chartRect.x1 + (chartRect.width() / bounds.max * value);
|
|
|
|
return {
|
|
x1: targetLineX,
|
|
x2: targetLineX,
|
|
y1: chartRect.y1,
|
|
y2: chartRect.y2
|
|
};
|
|
}
|
|
};
|
|
// @ts-ignore
|
|
const targetLine = projectTarget[options.axis](context.chartRect, context.bounds, options.value);
|
|
|
|
context.svg.elem('line', targetLine, options.className);
|
|
});
|
|
};
|
|
};
|
|
|
|
}(window, document, Chartist));
|
|
|
|
|
|
/**
|
|
* Chartist.js plugin to display a data label on top of the points in a line chart.
|
|
*
|
|
*/
|
|
/* global Chartist */
|
|
(function(window, document, Chartist) {
|
|
'use strict';
|
|
|
|
const defaultOptions = {
|
|
labelClass: 'ct-label',
|
|
labelOffset: {
|
|
x: 0,
|
|
y: -10
|
|
},
|
|
textAnchor: 'middle',
|
|
align: 'center',
|
|
labelInterpolationFnc: Chartist.noop
|
|
};
|
|
|
|
const labelPositionCalculation = {
|
|
point: function(data: any) {
|
|
return {
|
|
x: data.x,
|
|
y: data.y
|
|
};
|
|
},
|
|
bar: {
|
|
left: function(data: any) {
|
|
return {
|
|
x: data.x1,
|
|
y: data.y1
|
|
};
|
|
},
|
|
center: function(data: any) {
|
|
return {
|
|
x: data.x1 + (data.x2 - data.x1) / 2,
|
|
y: data.y1
|
|
};
|
|
},
|
|
right: function(data: any) {
|
|
return {
|
|
x: data.x2,
|
|
y: data.y1
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
Chartist.plugins = Chartist.plugins || {};
|
|
Chartist.plugins.ctPointLabels = function(options: any) {
|
|
|
|
options = Chartist.extend({}, defaultOptions, options);
|
|
|
|
function addLabel(position: any, data: any) {
|
|
// if x and y exist concat them otherwise output only the existing value
|
|
const value = data.value.x !== undefined && data.value.y ?
|
|
(data.value.x + ', ' + data.value.y) :
|
|
data.value.y || data.value.x;
|
|
|
|
data.group.elem('text', {
|
|
x: position.x + options.labelOffset.x,
|
|
y: position.y + options.labelOffset.y,
|
|
style: 'text-anchor: ' + options.textAnchor
|
|
}, options.labelClass).text(options.labelInterpolationFnc(value));
|
|
}
|
|
|
|
return function ctPointLabels(chart: any) {
|
|
if (chart instanceof Chartist.Line || chart instanceof Chartist.Bar) {
|
|
chart.on('draw', function(data: any) {
|
|
// @ts-ignore
|
|
const positonCalculator = labelPositionCalculation[data.type]
|
|
// @ts-ignore
|
|
&& labelPositionCalculation[data.type][options.align] || labelPositionCalculation[data.type];
|
|
if (positonCalculator) {
|
|
addLabel(positonCalculator(data), data);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
}(window, document, Chartist));
|
|
|
|
const defaultOptions = {
|
|
className: '',
|
|
classNames: false,
|
|
removeAll: false,
|
|
legendNames: false,
|
|
clickable: true,
|
|
onClick: null,
|
|
position: 'top'
|
|
};
|
|
|
|
Chartist.plugins.legend = function (options: any) {
|
|
let cachedDOMPosition;
|
|
let cacheInactiveLegends: any = [];
|
|
// Catch invalid options
|
|
if (options && options.position) {
|
|
if (!(options.position === 'top' || options.position === 'bottom' || options.position instanceof HTMLElement)) {
|
|
throw Error('The position you entered is not a valid position');
|
|
}
|
|
if (options.position instanceof HTMLElement) {
|
|
// Detatch DOM element from options object, because Chartist.extend
|
|
// currently chokes on circular references present in HTMLElements
|
|
cachedDOMPosition = options.position;
|
|
delete options.position;
|
|
}
|
|
}
|
|
|
|
options = Chartist.extend({}, defaultOptions, options);
|
|
|
|
if (cachedDOMPosition) {
|
|
// Reattatch the DOM Element position if it was removed before
|
|
options.position = cachedDOMPosition;
|
|
}
|
|
|
|
return function legend(chart: any) {
|
|
|
|
var isSelfUpdate = false;
|
|
|
|
chart.on('created', function (data: any) {
|
|
|
|
const useLabels = chart instanceof Chartist.Pie && chart.data.labels && chart.data.labels.length;
|
|
const legendNames = getLegendNames(useLabels);
|
|
var dirtyChartData = (chart.data.series.length < legendNames.length);
|
|
|
|
if (isSelfUpdate || dirtyChartData)
|
|
return;
|
|
|
|
function removeLegendElement() {
|
|
const legendElement = chart.container.querySelector('.ct-legend');
|
|
if (legendElement) {
|
|
legendElement.parentNode.removeChild(legendElement);
|
|
}
|
|
}
|
|
|
|
// Set a unique className for each series so that when a series is removed,
|
|
// the other series still have the same color.
|
|
function setSeriesClassNames() {
|
|
chart.data.series = chart.data.series.map(function (series: any, seriesIndex: any) {
|
|
if (typeof series !== 'object') {
|
|
series = {
|
|
value: series
|
|
};
|
|
}
|
|
series.className = series.className || chart.options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex);
|
|
return series;
|
|
});
|
|
}
|
|
|
|
function createLegendElement() {
|
|
const legendElement = document.createElement('ul');
|
|
legendElement.className = 'ct-legend';
|
|
if (chart instanceof Chartist.Pie) {
|
|
legendElement.classList.add('ct-legend-inside');
|
|
}
|
|
if (typeof options.className === 'string' && options.className.length > 0) {
|
|
legendElement.classList.add(options.className);
|
|
}
|
|
if (chart.options.width) {
|
|
legendElement.style.cssText = 'width: ' + chart.options.width + 'px;margin: 0 auto;';
|
|
}
|
|
return legendElement;
|
|
}
|
|
|
|
// Get the right array to use for generating the legend.
|
|
function getLegendNames(useLabels: any) {
|
|
return options.legendNames || (useLabels ? chart.data.labels : chart.data.series);
|
|
}
|
|
|
|
// Initialize the array that associates series with legends.
|
|
// -1 indicates that there is no legend associated with it.
|
|
function initSeriesMetadata(useLabels: any) {
|
|
const seriesMetadata = new Array(chart.data.series.length);
|
|
for (let i = 0; i < chart.data.series.length; i++) {
|
|
seriesMetadata[i] = {
|
|
data: chart.data.series[i],
|
|
label: useLabels ? chart.data.labels[i] : null,
|
|
legend: -1
|
|
};
|
|
}
|
|
return seriesMetadata;
|
|
}
|
|
|
|
function createNameElement(i: any, legendText: any, classNamesViable: any) {
|
|
const li = document.createElement('li');
|
|
li.classList.add('ct-series-' + i);
|
|
// Append specific class to a legend element, if viable classes are given
|
|
if (classNamesViable) {
|
|
li.classList.add(options.classNames[i]);
|
|
}
|
|
li.setAttribute('data-legend', i);
|
|
li.textContent = legendText;
|
|
return li;
|
|
}
|
|
|
|
// Append the legend element to the DOM
|
|
function appendLegendToDOM(legendElement: any) {
|
|
if (!(options.position instanceof HTMLElement)) {
|
|
switch (options.position) {
|
|
case 'top':
|
|
chart.container.insertBefore(legendElement, chart.container.childNodes[0]);
|
|
break;
|
|
|
|
case 'bottom':
|
|
chart.container.insertBefore(legendElement, null);
|
|
break;
|
|
}
|
|
} else {
|
|
// Appends the legend element as the last child of a given HTMLElement
|
|
options.position.insertBefore(legendElement, null);
|
|
}
|
|
}
|
|
|
|
function updateChart(newSeries: any, newLabels:any, useLabels: any) {
|
|
chart.data.series = newSeries;
|
|
if (useLabels) {
|
|
chart.data.labels = newLabels;
|
|
}
|
|
|
|
isSelfUpdate = true;
|
|
chart.update();
|
|
isSelfUpdate = false;
|
|
}
|
|
|
|
function addClickHandler(legendElement: any, legends: any, seriesMetadata: any, useLabels: any) {
|
|
legendElement.addEventListener('click', function(e: any) {
|
|
const li = e.target;
|
|
if (li.parentNode !== legendElement || !li.hasAttribute('data-legend'))
|
|
return;
|
|
e.preventDefault();
|
|
|
|
const legendIndex = parseInt(li.getAttribute('data-legend'));
|
|
const legend = legends[legendIndex];
|
|
|
|
if (!legend.active) {
|
|
legend.active = true;
|
|
li.classList.remove('inactive');
|
|
|
|
var indexOfInactiveLegend = cacheInactiveLegends.indexOf(legendIndex, 0)
|
|
if (indexOfInactiveLegend > -1) {
|
|
cacheInactiveLegends.splice(indexOfInactiveLegend, 1);
|
|
}
|
|
|
|
} else {
|
|
legend.active = false;
|
|
li.classList.add('inactive');
|
|
cacheInactiveLegends.push(legendIndex);
|
|
|
|
const activeCount = legends.filter(function(legend: any) { return legend.active; }).length;
|
|
if (!options.removeAll && activeCount == 0) {
|
|
// If we can't disable all series at the same time, let's
|
|
// reenable all of them:
|
|
for (let i = 0; i < legends.length; i++) {
|
|
legends[i].active = true;
|
|
legendElement.childNodes[i].classList.remove('inactive');
|
|
}
|
|
|
|
cacheInactiveLegends = [];
|
|
}
|
|
}
|
|
|
|
const newSeries = [];
|
|
const newLabels = [];
|
|
|
|
for (let i = 0; i < seriesMetadata.length; i++) {
|
|
if (seriesMetadata[i].legend !== -1 && legends[seriesMetadata[i].legend].active) {
|
|
newSeries.push(seriesMetadata[i].data);
|
|
newLabels.push(seriesMetadata[i].label);
|
|
}
|
|
}
|
|
|
|
updateChart(newSeries, newLabels, useLabels);
|
|
|
|
if (options.onClick) {
|
|
options.onClick(chart, e);
|
|
}
|
|
});
|
|
}
|
|
|
|
removeLegendElement();
|
|
|
|
const legendElement = createLegendElement();
|
|
const seriesMetadata = initSeriesMetadata(useLabels);
|
|
const legends: any = [];
|
|
|
|
// Check if given class names are viable to append to legends
|
|
const classNamesViable = Array.isArray(options.classNames) && options.classNames.length === legendNames.length;
|
|
|
|
var activeSeries = [];
|
|
var activeLabels = [];
|
|
|
|
// Loop through all legends to set each name in a list item.
|
|
legendNames.forEach(function (legend: any, i: any) {
|
|
const legendText = legend.name || legend;
|
|
const legendSeries = legend.series || [i];
|
|
|
|
const li = createNameElement(i, legendText, classNamesViable);
|
|
const isActive: boolean = !(cacheInactiveLegends.indexOf(i) > -1);
|
|
if (isActive) {
|
|
activeSeries.push(seriesMetadata[i].data);
|
|
activeLabels.push(seriesMetadata[i].label);
|
|
} else {
|
|
li.classList.add('inactive');
|
|
}
|
|
legendElement.appendChild(li);
|
|
|
|
legendSeries.forEach(function(seriesIndex: any) {
|
|
seriesMetadata[seriesIndex].legend = i;
|
|
});
|
|
|
|
legends.push({
|
|
text: legendText,
|
|
series: legendSeries,
|
|
active: isActive
|
|
});
|
|
|
|
});
|
|
|
|
appendLegendToDOM(legendElement);
|
|
|
|
if (options.clickable) {
|
|
setSeriesClassNames();
|
|
addClickHandler(legendElement, legends, seriesMetadata, useLabels);
|
|
}
|
|
|
|
updateChart(activeSeries, activeLabels, useLabels);
|
|
|
|
});
|
|
};
|
|
};
|
|
|
|
Chartist.plugins.tooltip = function (options: any) {
|
|
options = Chartist.extend({}, defaultOptions, options);
|
|
|
|
return function tooltip(chart: any) {
|
|
let tooltipSelector = options.pointClass;
|
|
if (chart instanceof Chartist.Bar) {
|
|
tooltipSelector = 'ct-bar';
|
|
} else if (chart instanceof Chartist.Pie) {
|
|
// Added support for donut graph
|
|
if (chart.options.donut) {
|
|
tooltipSelector = 'ct-slice-donut';
|
|
} else {
|
|
tooltipSelector = 'ct-slice-pie';
|
|
}
|
|
}
|
|
|
|
const $chart = chart.container;
|
|
let $toolTip = $chart.querySelector('.chartist-tooltip');
|
|
if (!$toolTip) {
|
|
$toolTip = document.createElement('div');
|
|
$toolTip.className = (!options.class) ? 'chartist-tooltip' : 'chartist-tooltip ' + options.class;
|
|
if (!options.appendToBody) {
|
|
$chart.appendChild($toolTip);
|
|
} else {
|
|
document.body.appendChild($toolTip);
|
|
}
|
|
}
|
|
let height = $toolTip.offsetHeight;
|
|
let width = $toolTip.offsetWidth;
|
|
|
|
hide($toolTip);
|
|
|
|
function on(event: any, selector: any, callback: any) {
|
|
$chart.addEventListener(event, function (e: any) {
|
|
if (!selector || hasClass(e.target, selector)) {
|
|
callback(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
on('mouseover', tooltipSelector, function (event: any) {
|
|
const $point = event.target;
|
|
let tooltipText = '';
|
|
|
|
const isPieChart = (chart instanceof Chartist.Pie) ? $point : $point.parentNode;
|
|
const seriesName = (isPieChart) ? $point.parentNode.getAttribute('ct:meta') || $point.parentNode.getAttribute('ct:series-name') : '';
|
|
let meta = $point.getAttribute('ct:meta') || seriesName || '';
|
|
const hasMeta = !!meta;
|
|
let value = $point.getAttribute('ct:value');
|
|
|
|
if (options.transformTooltipTextFnc && typeof options.transformTooltipTextFnc === 'function') {
|
|
value = options.transformTooltipTextFnc(value, $point.parentNode.getAttribute('class'));
|
|
}
|
|
|
|
if (options.tooltipFnc && typeof options.tooltipFnc === 'function') {
|
|
tooltipText = options.tooltipFnc(meta, value);
|
|
} else {
|
|
if (options.metaIsHTML) {
|
|
const txt = document.createElement('textarea');
|
|
txt.innerHTML = meta;
|
|
meta = txt.value;
|
|
}
|
|
|
|
meta = '<span class="chartist-tooltip-meta">' + meta + '</span>';
|
|
|
|
if (hasMeta) {
|
|
tooltipText += meta + '<br>';
|
|
} else {
|
|
// For Pie Charts also take the labels into account
|
|
// Could add support for more charts here as well!
|
|
if (chart instanceof Chartist.Pie) {
|
|
const label = next($point, 'ct-label');
|
|
if (label) {
|
|
tooltipText += text(label) + '<br>';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (value) {
|
|
if (options.currency) {
|
|
if (options.currencyFormatCallback != undefined) {
|
|
value = options.currencyFormatCallback(value, options);
|
|
} else {
|
|
value = options.currency + value.replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, '$1,');
|
|
}
|
|
}
|
|
value = '<span class="chartist-tooltip-value">' + value + '</span>';
|
|
tooltipText += value;
|
|
}
|
|
}
|
|
|
|
if (tooltipText) {
|
|
$toolTip.innerHTML = tooltipText;
|
|
setPosition(event);
|
|
show($toolTip);
|
|
|
|
// Remember height and width to avoid wrong position in IE
|
|
height = $toolTip.offsetHeight;
|
|
width = $toolTip.offsetWidth;
|
|
}
|
|
});
|
|
|
|
on('mouseout', tooltipSelector, function () {
|
|
hide($toolTip);
|
|
});
|
|
|
|
on('mousemove', null, function (event: any) {
|
|
if (false === options.anchorToPoint) {
|
|
setPosition(event);
|
|
}
|
|
});
|
|
|
|
function setPosition(event: any) {
|
|
height = height || $toolTip.offsetHeight;
|
|
width = width || $toolTip.offsetWidth;
|
|
const offsetX = - width / 2 + options.tooltipOffset.x
|
|
const offsetY = - height + options.tooltipOffset.y;
|
|
let anchorX, anchorY;
|
|
|
|
if (!options.appendToBody) {
|
|
const box = $chart.getBoundingClientRect();
|
|
const left = event.pageX - box.left - window.pageXOffset ;
|
|
const top = event.pageY - box.top - window.pageYOffset ;
|
|
|
|
if (true === options.anchorToPoint && event.target.x2 && event.target.y2) {
|
|
anchorX = parseInt(event.target.x2.baseVal.value);
|
|
anchorY = parseInt(event.target.y2.baseVal.value);
|
|
}
|
|
|
|
$toolTip.style.top = (anchorY || top) + offsetY + 'px';
|
|
$toolTip.style.left = (anchorX || left) + offsetX + 'px';
|
|
} else {
|
|
$toolTip.style.top = event.pageY + offsetY + 'px';
|
|
$toolTip.style.left = event.pageX + offsetX + 'px';
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Chartist.plugins.ctPointLabels = (options) => {
|
|
return function ctPointLabels(chart) {
|
|
const defaultOptions2 = {
|
|
labelClass: 'ct-point-label',
|
|
labelOffset: {
|
|
x: 0,
|
|
y: -7
|
|
},
|
|
textAnchor: 'middle'
|
|
};
|
|
options = Chartist.extend({}, defaultOptions2, options);
|
|
|
|
if (chart instanceof Chartist.Line) {
|
|
chart.on('draw', (data) => {
|
|
if (data.type === 'point') {
|
|
data.group.elem('text', {
|
|
x: data.x + options.labelOffset.x,
|
|
y: data.y + options.labelOffset.y,
|
|
style: 'text-anchor: ' + options.textAnchor
|
|
}, options.labelClass).text(options.labelInterpolationFnc(data.value.y)); // 07.11.17 added ".y"
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
function show(element: any) {
|
|
if (!hasClass(element, 'tooltip-show')) {
|
|
element.className = element.className + ' tooltip-show';
|
|
}
|
|
}
|
|
|
|
function hide(element: any) {
|
|
const regex = new RegExp('tooltip-show' + '\\s*', 'gi');
|
|
element.className = element.className.replace(regex, '').trim();
|
|
}
|
|
|
|
function hasClass(element: any, className: any) {
|
|
return (' ' + element.getAttribute('class') + ' ').indexOf(' ' + className + ' ') > -1;
|
|
}
|
|
|
|
function next(element: any, className: any) {
|
|
do {
|
|
element = element.nextSibling;
|
|
} while (element && !hasClass(element, className));
|
|
return element;
|
|
}
|
|
|
|
function text(element: any) {
|
|
return element.innerText || element.textContent;
|
|
}
|