mirror of https://github.com/ColorlibHQ/AdminLTE
216 lines
5.1 KiB
JavaScript
216 lines
5.1 KiB
JavaScript
/*!
|
|
* chartjs-plugin-deferred
|
|
* http://chartjs.org/
|
|
* Version: 0.2.0
|
|
*
|
|
* Copyright 2016 Simon Brunel
|
|
* Released under the MIT license
|
|
* https://github.com/chartjs/chartjs-plugin-deferred/blob/master/LICENSE.md
|
|
*/
|
|
/* global window: false */
|
|
'use strict';
|
|
|
|
(function() {
|
|
|
|
var Chart = window.Chart;
|
|
var helpers = Chart.helpers;
|
|
var STUB_KEY = '_chartjs_deferred';
|
|
var MODEL_KEY = '_deferred_model';
|
|
|
|
/**
|
|
* Plugin based on discussion from Chart.js issue #2745.
|
|
* @see https://github.com/chartjs/Chart.js/issues/2745
|
|
*/
|
|
Chart.Deferred = Chart.Deferred || {};
|
|
Chart.Deferred.defaults = {
|
|
enabled: true,
|
|
xOffset: 0,
|
|
yOffset: 0,
|
|
delay: 0
|
|
};
|
|
|
|
// DOM implementation
|
|
// @TODO move it in Chart.js: src/core/core.platform.js
|
|
Chart.platform = helpers.extend(Chart.platform || {}, {
|
|
defer: function(fn, delay, scope) {
|
|
var callback = function() {
|
|
fn.call(scope);
|
|
};
|
|
if (!delay) {
|
|
helpers.requestAnimFrame.call(window, callback);
|
|
} else {
|
|
window.setTimeout(callback, delay);
|
|
}
|
|
}
|
|
});
|
|
|
|
function computeOffset(value, base) {
|
|
var number = parseInt(value, 10);
|
|
if (isNaN(number)) {
|
|
return 0;
|
|
} else if (typeof value === 'string' && value.indexOf('%') !== -1) {
|
|
return number / 100 * base;
|
|
}
|
|
return number;
|
|
}
|
|
|
|
function chartInViewport(instance) {
|
|
var model = instance[MODEL_KEY];
|
|
var canvas = instance.chart.canvas;
|
|
|
|
// http://stackoverflow.com/a/21696585
|
|
if (canvas.offsetParent === null) {
|
|
return false;
|
|
}
|
|
|
|
var rect = canvas.getBoundingClientRect();
|
|
var dy = computeOffset(model.yOffset || 0, rect.height);
|
|
var dx = computeOffset(model.xOffset || 0, rect.width);
|
|
|
|
return rect.right - dx >= 0
|
|
&& rect.bottom - dy >= 0
|
|
&& rect.left + dx <= window.innerWidth
|
|
&& rect.top + dy <= window.innerHeight;
|
|
}
|
|
|
|
function buildDeferredModel(instance) {
|
|
var defaults = Chart.Deferred.defaults;
|
|
var options = instance.options.deferred;
|
|
var getValue = helpers.getValueOrDefault;
|
|
|
|
if (options === undefined) {
|
|
options = {};
|
|
} else if (typeof options === 'boolean') {
|
|
// accepting { options: { deferred: true } }
|
|
options = {enabled: options};
|
|
}
|
|
|
|
return {
|
|
enabled: getValue(options.enabled, defaults.enabled),
|
|
xOffset: getValue(options.xOffset, defaults.xOffset),
|
|
yOffset: getValue(options.yOffset, defaults.yOffset),
|
|
delay: getValue(options.delay, defaults.delay),
|
|
appeared: false,
|
|
delayed: false,
|
|
loaded: false,
|
|
elements: []
|
|
};
|
|
}
|
|
|
|
function onScroll(event) {
|
|
var node = event.target;
|
|
var stub = node[STUB_KEY];
|
|
if (stub.ticking) {
|
|
return;
|
|
}
|
|
|
|
stub.ticking = true;
|
|
Chart.platform.defer(function() {
|
|
var instances = stub.instances.slice();
|
|
var ilen = instances.length;
|
|
var instance, i;
|
|
|
|
for (i=0; i<ilen; ++i) {
|
|
instance = instances[i];
|
|
if (chartInViewport(instance)) {
|
|
unwatch(instance); // eslint-disable-line
|
|
instance[MODEL_KEY].appeared = true;
|
|
instance.update();
|
|
}
|
|
}
|
|
|
|
stub.ticking = false;
|
|
});
|
|
}
|
|
|
|
function isScrollable(node) {
|
|
var type = node.nodeType;
|
|
if (type === Node.ELEMENT_NODE) {
|
|
var overflowX = helpers.getStyle(node, 'overflow-x');
|
|
var overflowY = helpers.getStyle(node, 'overflow-y');
|
|
return overflowX === 'auto' || overflowX === 'scroll'
|
|
|| overflowY === 'auto' || overflowY === 'scroll';
|
|
}
|
|
|
|
return node.nodeType === Node.DOCUMENT_NODE;
|
|
}
|
|
|
|
function watch(instance) {
|
|
var canvas = instance.chart.canvas;
|
|
var parent = canvas.parentElement;
|
|
var stub, instances;
|
|
|
|
while (parent) {
|
|
if (isScrollable(parent)) {
|
|
stub = parent[STUB_KEY] || (parent[STUB_KEY] = {});
|
|
instances = stub.instances || (stub.instances = []);
|
|
if (instances.length === 0) {
|
|
parent.addEventListener('scroll', onScroll, {passive: true});
|
|
}
|
|
|
|
instances.push(instance);
|
|
instance[MODEL_KEY].elements.push(parent);
|
|
}
|
|
|
|
parent = parent.parentElement || parent.ownerDocument;
|
|
}
|
|
}
|
|
|
|
function unwatch(instance) {
|
|
instance[MODEL_KEY].elements.forEach(function(element) {
|
|
var instances = element[STUB_KEY].instances;
|
|
instances.splice(instances.indexOf(instance), 1);
|
|
if (!instances.length) {
|
|
helpers.removeEvent(element, 'scroll', onScroll);
|
|
delete element[STUB_KEY];
|
|
}
|
|
});
|
|
|
|
instance[MODEL_KEY].elements = [];
|
|
}
|
|
|
|
Chart.plugins.register({
|
|
beforeInit: function(instance) {
|
|
var model = instance[MODEL_KEY] = buildDeferredModel(instance);
|
|
if (model.enabled) {
|
|
watch(instance);
|
|
}
|
|
},
|
|
|
|
beforeDatasetsUpdate: function(instance) {
|
|
var model = instance[MODEL_KEY];
|
|
if (!model.enabled) {
|
|
return true;
|
|
}
|
|
|
|
if (!model.loaded) {
|
|
if (!model.appeared && !chartInViewport(instance)) {
|
|
// cancel the datasets update
|
|
return false;
|
|
}
|
|
|
|
model.appeared = true;
|
|
model.loaded = true;
|
|
unwatch(instance);
|
|
|
|
if (model.delay > 0) {
|
|
model.delayed = true;
|
|
Chart.platform.defer(function() {
|
|
model.delayed = false;
|
|
instance.update();
|
|
}, model.delay);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (model.delayed) {
|
|
// in case of delayed update, ensure to block external requests, such
|
|
// as interacting with the legend label, or direct calls to update()
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
|
|
}());
|