/*!
* =====================================================
* V0.4.3 - powered by chinieer
* =====================================================
*/
function loadScript(url, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
document.head.appendChild(script);
}
/* global $:true */
+function ($) {
"use strict";
//全局配置
var defaults = {
autoInit: false, //自动初始化页面
showPageLoadingIndicator: true, //push.js加载页面的时候显示一个加载提示
router: true, //默认使用router
swipePanel: "left", //滑动打开侧栏
swipePanelOnlyClose: true, //只允许滑动关闭,不允许滑动打开侧栏
pushAnimationDuration: 400 //不要动这个,这是解决安卓 animationEnd 事件无法触发的bug
};
$.smConfig = $.extend(defaults, $.config);
}($);
/*===========================
Device/OS Detection
===========================*/
/* global $:true */
;(function ($) {
"use strict";
var device = {};
var ua = navigator.userAgent;
var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
// Android
if (android) {
device.os = 'android';
device.osVersion = android[2];
device.android = true;
device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
}
if (ipad || iphone || ipod) {
device.os = 'ios';
device.ios = true;
}
// iOS
if (iphone && !ipod) {
device.osVersion = iphone[2].replace(/_/g, '.');
device.iphone = true;
}
if (ipad) {
device.osVersion = ipad[2].replace(/_/g, '.');
device.ipad = true;
}
if (ipod) {
device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
device.iphone = true;
}
// iOS 8+ changed UA
if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
if (device.osVersion.split('.')[0] === '10') {
device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
}
}
// Webview
device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
// Minimal UI
if (device.os && device.os === 'ios') {
var osVersionArr = device.osVersion.split('.');
device.minimalUi = !device.webView &&
(ipod || iphone) &&
(osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) &&
$('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0;
}
// Check for status bar and fullscreen app mode
var windowWidth = $(window).width();
var windowHeight = $(window).height();
device.statusBar = false;
if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) {
device.statusBar = true;
}
else {
device.statusBar = false;
}
// Classes
var classNames = [];
// Pixel Ratio
device.pixelRatio = window.devicePixelRatio || 1;
classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio));
if (device.pixelRatio >= 2) {
classNames.push('retina');
}
// OS classes
if (device.os) {
classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-'));
if (device.os === 'ios') {
var major = parseInt(device.osVersion.split('.')[0], 10);
for (var i = major - 1; i >= 6; i--) {
classNames.push('ios-gt-' + i);
}
}
}
// Status bar classes
if (device.statusBar) {
classNames.push('with-statusbar-overlay');
}
else {
$('html').removeClass('with-statusbar-overlay');
}
// Add html classes
if (classNames.length > 0) $('html').addClass(classNames.join(' '));
$.device = device;
})($);
/* global $:true */
+ function($) {
"use strict";
//比较一个字符串版本号
//a > b === 1
//a = b === 0
//a < b === -1
$.compareVersion = function(a, b) {
var as = a.split('.');
var bs = b.split('.');
if (a === b) return 0;
for (var i = 0; i < as.length; i++) {
var x = parseInt(as[i]);
if (!bs[i]) return 1;
var y = parseInt(bs[i]);
if (x < y) return -1;
if (x > y) return 1;
}
return 1;
};
$.getTouchPosition = function(e) {
e = e.originalEvent || e; //jquery wrap the originevent
if(e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend') {
return {
x: e.targetTouches[0].pageX,
y: e.targetTouches[0].pageY
};
} else {
return {
x: e.pageX,
y: e.pageY
};
}
};
}($);
// Zepto.js
// (c) 2010-2015 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.
+ function($) {
"use strict";
function detect(ua, platform) {
var os = this.os = {}, // jshint ignore:line
browser = this.browser = {},// jshint ignore:line
webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/),
android = ua.match(/(Android);?[\s\/]+([\d.]+)?/),
osx = !!ua.match(/\(Macintosh\; Intel /),
ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/),
iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
win = /Win\d{2}|Windows/.test(platform),
wp = ua.match(/Windows Phone ([\d.]+)/),
touchpad = webos && ua.match(/TouchPad/),
kindle = ua.match(/Kindle\/([\d.]+)/),
silk = ua.match(/Silk\/([\d._]+)/),
blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/),
bb10 = ua.match(/(BB10).*Version\/([\d.]+)/),
rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/),
playbook = ua.match(/PlayBook/),
chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/),
firefox = ua.match(/Firefox\/([\d.]+)/),
firefoxos = ua.match(/\((?:Mobile|Tablet); rv:([\d.]+)\).*Firefox\/[\d.]+/),
ie = ua.match(/MSIE\s([\d.]+)/) || ua.match(/Trident\/[\d](?=[^\?]+).*rv:([0-9.].)/),
webview = !chrome && ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/),
safari = webview || ua.match(/Version\/([\d.]+)([^S](Safari)|[^M]*(Mobile)[^S]*(Safari))/);
// Todo: clean this up with a better OS/browser seperation:
// - discern (more) between multiple browsers on android
// - decide if kindle fire in silk mode is android or not
// - Firefox on Android doesn't specify the Android version
// - possibly devide in os, device and browser hashes
if (browser.webkit = !!webkit) browser.version = webkit[1]; // jshint ignore:line
if (android) os.android = true, os.version = android[2];
if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
if (wp) os.wp = true, os.version = wp[1];
if (webos) os.webos = true, os.version = webos[2];
if (touchpad) os.touchpad = true;
if (blackberry) os.blackberry = true, os.version = blackberry[2];
if (bb10) os.bb10 = true, os.version = bb10[2];
if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
if (playbook) browser.playbook = true;
if (kindle) os.kindle = true, os.version = kindle[1];
if (silk) browser.silk = true, browser.version = silk[1];
if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
if (chrome) browser.chrome = true, browser.version = chrome[1];
if (firefox) browser.firefox = true, browser.version = firefox[1];
if (firefoxos) os.firefoxos = true, os.version = firefoxos[1];
if (ie) browser.ie = true, browser.version = ie[1];
if (safari && (osx || os.ios || win)) {
browser.safari = true;
if (!os.ios) browser.version = safari[1];
}
if (webview) browser.webview = true;
os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
(firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || blackberry || bb10 ||
(chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
(firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
}
detect.call($, navigator.userAgent, navigator.platform);
// make available to unit tests
$.__detect = detect;
}($); // jshint ignore:line
/* global $:true */
/* global WebKitCSSMatrix:true */
(function($) {
"use strict";
['width', 'height'].forEach(function(dimension) {
var Dimension = dimension.replace(/./, function(m) {
return m[0].toUpperCase();
});
$.fn['outer' + Dimension] = function(margin) {
var elem = this;
if (elem) {
var size = elem[dimension]();
var sides = {
'width': ['left', 'right'],
'height': ['top', 'bottom']
};
sides[dimension].forEach(function(side) {
if (margin) size += parseInt(elem.css('margin-' + side), 10);
});
return size;
} else {
return null;
}
};
});
$.noop = function() {};
//support
$.support = (function() {
var support = {
touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch)
};
return support;
})();
$.touchEvents = {
start: $.support.touch ? 'touchstart' : 'mousedown',
move: $.support.touch ? 'touchmove' : 'mousemove',
end: $.support.touch ? 'touchend' : 'mouseup'
};
$.getTranslate = function (el, axis) {
var matrix, curTransform, curStyle, transformMatrix;
// automatic axis detection
if (typeof axis === 'undefined') {
axis = 'x';
}
curStyle = window.getComputedStyle(el, null);
if (window.WebKitCSSMatrix) {
// Some old versions of Webkit choke when 'none' is passed; pass
// empty string instead in this case
transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
}
else {
transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
matrix = transformMatrix.toString().split(',');
}
if (axis === 'x') {
//Latest Chrome and webkits Fix
if (window.WebKitCSSMatrix)
curTransform = transformMatrix.m41;
//Crazy IE10 Matrix
else if (matrix.length === 16)
curTransform = parseFloat(matrix[12]);
//Normal Browsers
else
curTransform = parseFloat(matrix[4]);
}
if (axis === 'y') {
//Latest Chrome and webkits Fix
if (window.WebKitCSSMatrix)
curTransform = transformMatrix.m42;
//Crazy IE10 Matrix
else if (matrix.length === 16)
curTransform = parseFloat(matrix[13]);
//Normal Browsers
else
curTransform = parseFloat(matrix[5]);
}
return curTransform || 0;
};
$.requestAnimationFrame = function (callback) {
if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
else {
return window.setTimeout(callback, 1000 / 60);
}
};
$.cancelAnimationFrame = function (id) {
if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id);
else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id);
else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id);
else {
return window.clearTimeout(id);
}
};
$.fn.transitionEnd = function(callback) {
var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],
i, dom = this;
function fireCallBack(e) {
/*jshint validthis:true */
if (e.target !== this) return;
callback.call(this, e);
for (i = 0; i < events.length; i++) {
dom.off(events[i], fireCallBack);
}
}
if (callback) {
for (i = 0; i < events.length; i++) {
dom.on(events[i], fireCallBack);
}
}
return this;
};
$.fn.dataset = function() {
var el = this[0];
if (el) {
var dataset = {};
if (el.dataset) {
for (var dataKey in el.dataset) { // jshint ignore:line
dataset[dataKey] = el.dataset[dataKey];
}
} else {
for (var i = 0; i < el.attributes.length; i++) {
var attr = el.attributes[i];
if (attr.name.indexOf('data-') >= 0) {
dataset[$.toCamelCase(attr.name.split('data-')[1])] = attr.value;
}
}
}
for (var key in dataset) {
if (dataset[key] === 'false') dataset[key] = false;
else if (dataset[key] === 'true') dataset[key] = true;
else if (parseFloat(dataset[key]) === dataset[key] * 1) dataset[key] = dataset[key] * 1;
}
return dataset;
} else return undefined;
};
$.fn.data = function(key, value) {
if (typeof value === 'undefined') {
// Get value
if (this[0] && this[0].getAttribute) {
var dataKey = this[0].getAttribute('data-' + key);
if (dataKey) {
return dataKey;
} else if (this[0].smElementDataStorage && (key in this[0].smElementDataStorage)) {
return this[0].smElementDataStorage[key];
} else {
return undefined;
}
} else return undefined;
} else {
// Set value
for (var i = 0; i < this.length; i++) {
var el = this[i];
if (!el.smElementDataStorage) el.smElementDataStorage = {};
el.smElementDataStorage[key] = value;
}
return this;
}
};
$.fn.animationEnd = function(callback) {
var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'],
i, dom = this;
function fireCallBack(e) {
callback(e);
for (i = 0; i < events.length; i++) {
dom.off(events[i], fireCallBack);
}
}
if (callback) {
for (i = 0; i < events.length; i++) {
dom.on(events[i], fireCallBack);
}
}
return this;
};
$.fn.transition = function(duration) {
if (typeof duration !== 'string') {
duration = duration + 'ms';
}
for (var i = 0; i < this.length; i++) {
var elStyle = this[i].style;
elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;
}
return this;
};
$.fn.transform = function(transform) {
for (var i = 0; i < this.length; i++) {
var elStyle = this[i].style;
elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
}
return this;
};
$.fn.prevAll = function (selector) {
var prevEls = [];
var el = this[0];
if (!el) return $([]);
while (el.previousElementSibling) {
var prev = el.previousElementSibling;
if (selector) {
if($(prev).is(selector)) prevEls.push(prev);
}
else prevEls.push(prev);
el = prev;
}
return $(prevEls);
};
$.fn.nextAll = function (selector) {
var nextEls = [];
var el = this[0];
if (!el) return $([]);
while (el.nextElementSibling) {
var next = el.nextElementSibling;
if (selector) {
if($(next).is(selector)) nextEls.push(next);
}
else nextEls.push(next);
el = next;
}
return $(nextEls);
};
//重置zepto的show方法,防止有些人引用的版本中 show 方法操作 opacity 属性影响动画执行
$.fn.show = function(){
var elementDisplay = {};
function defaultDisplay(nodeName) {
var element, display;
if (!elementDisplay[nodeName]) {
element = document.createElement(nodeName);
document.body.appendChild(element);
display = getComputedStyle(element, '').getPropertyValue("display");
element.parentNode.removeChild(element);
display === "none" && (display = "block");
elementDisplay[nodeName] = display;
}
return elementDisplay[nodeName];
}
return this.each(function(){
this.style.display === "none" && (this.style.display = '');
if (getComputedStyle(this, '').getPropertyValue("display") === "none");
this.style.display = defaultDisplay(this.nodeName);
});
};
$.fn.scrollHeight = function() {
return this[0].scrollHeight;
};
})($);
/* global $:true */
;(function ($) {
'use strict';
var passiveSupported = false;
try {
var _options = Object.defineProperty({}, "passive", {
get: function() {
passiveSupported = true;
}
});
window.addEventListener("test", null, _options);
} catch(err) {}
/**
* @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
*
* @codingstandard ftlabs-jsv2
* @copyright The Financial Times Limited [All Rights Reserved]
* @license MIT License (see LICENSE.txt)
*/
/*jslint browser:true, node:true, elision:true*/
/*global Event, Node*/
/**
* Instantiate fast-clicking listeners on the specified layer.
*
* @constructor
* @param {Element} layer The layer to listen on
* @param {Object} [options={}] The options to override the defaults
*/
function FastClick(layer, options) {
var oldOnClick;
options = options || {};
/**
* Whether a click is currently being tracked.
*
* @type boolean
*/
this.trackingClick = false;
/**
* Timestamp for when click tracking started.
*
* @type number
*/
this.trackingClickStart = 0;
/**
* The element being tracked for a click.
*
* @type EventTarget
*/
this.targetElement = null;
/**
* X-coordinate of touch start event.
*
* @type number
*/
this.touchStartX = 0;
/**
* Y-coordinate of touch start event.
*
* @type number
*/
this.touchStartY = 0;
/**
* ID of the last touch, retrieved from Touch.identifier.
*
* @type number
*/
this.lastTouchIdentifier = 0;
/**
* Touchmove boundary, beyond which a click will be cancelled.
*
* @type number
*/
this.touchBoundary = options.touchBoundary || 10;
/**
* The FastClick layer.
*
* @type Element
*/
this.layer = layer;
/**
* The minimum time between tap(touchstart and touchend) events
*
* @type number
*/
this.tapDelay = options.tapDelay || 200;
/**
* The maximum time for a tap
*
* @type number
*/
this.tapTimeout = options.tapTimeout || 700;
if (FastClick.notNeeded(layer)) {
return;
}
// Some old versions of Android don't have Function.prototype.bind
function bind(method, context) {
return function() { return method.apply(context, arguments); };
}
var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
var context = this;
for (var i = 0, l = methods.length; i < l; i++) {
context[methods[i]] = bind(context[methods[i]], context);
}
// Set up event handlers as required
if (deviceIsAndroid) {
layer.addEventListener('mouseover', this.onMouse, true);
layer.addEventListener('mousedown', this.onMouse, true);
layer.addEventListener('mouseup', this.onMouse, true);
}
layer.addEventListener('click', this.onClick, true);
layer.addEventListener('touchstart', this.onTouchStart, false);
layer.addEventListener('touchmove', this.onTouchMove,passiveSupported ? { passive: true } : false);
layer.addEventListener('touchend', this.onTouchEnd, false);
layer.addEventListener('touchcancel', this.onTouchCancel,false);
// Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
// which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
// layer when they are cancelled.
if (!Event.prototype.stopImmediatePropagation) {
layer.removeEventListener = function(type, callback, capture) {
var rmv = Node.prototype.removeEventListener;
if (type === 'click') {
rmv.call(layer, type, callback.hijacked || callback, capture);
} else {
rmv.call(layer, type, callback, capture);
}
};
layer.addEventListener = function(type, callback, capture) {
var adv = Node.prototype.addEventListener;
if (type === 'click') {
adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
if (!event.propagationStopped) {
callback(event);
}
}), capture);
} else {
adv.call(layer, type, callback, capture);
}
};
}
// If a handler is already declared in the element's onclick attribute, it will be fired before
// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
// adding it as listener.
if (typeof layer.onclick === 'function') {
// Android browser on at least 3.2 requires a new reference to the function in layer.onclick
// - the old one won't work if passed to addEventListener directly.
oldOnClick = layer.onclick;
layer.addEventListener('click', function(event) {
oldOnClick(event);
}, false);
layer.onclick = null;
}
}
/**
* Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
*
* @type boolean
*/
var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
/**
* Android requires exceptions.
*
* @type boolean
*/
var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
/**
* iOS requires exceptions.
*
* @type boolean
*/
var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
/**
* iOS 4 requires an exception for select elements.
*
* @type boolean
*/
var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
/**
* compareVersion
* returns:
* == 0: v1 = v2
* > 0 : v1 > v2
* < 0 : v1 < v2
*
* @type number
*/
var compareVersion = function (v1, v2) {
var arr1 = v1.split('_');
var arr2 = v2.split('_');
var minLength = Math.min(arr1.length, arr2.length);
var position = 0;
var diff = 0;
while (position < minLength) {
diff = parseInt(arr1[position]) - parseInt(arr2[position]);
if (diff !== 0) {
break;
}
position++;
}
diff = (diff !== 0) ? diff : (arr1.length - arr2.length);
return diff;
};
/**
* above iOS 11_3
*
* @type boolean
*/
var deviceAboveIOS11_3 = (function aboveIOS11_3 () {
if (!deviceIsIOS) {
return false;
}
var version = '11_3';
var matches = navigator.userAgent.match(/CPU iPhone OS ([0-9_]+)/);
if (matches && matches[1]) {
return compareVersion(matches[1], version) >= 0;
} else {
return false;
}
})();
/**
* iOS 6.0-7.* requires the target element to be manually derived
*
* @type boolean
*/
var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
/**
* BlackBerry requires exceptions.
*
* @type boolean
*/
var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
/**
* Determine whether a given element requires a native click.
*
* @param {EventTarget|Element} target Target DOM element
* @returns {boolean} Returns true if the element needs a native click
*/
FastClick.prototype.needsClick = function(target) {
switch (target.nodeName.toLowerCase()) {
// Don't send a synthetic click to disabled inputs (issue #62)
case 'button':
case 'select':
case 'textarea':
if (target.disabled) {
return true;
}
break;
case 'input':
// File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
if ((deviceIsIOS && target.type === 'file') || target.disabled) {
return true;
}
break;
case 'label':
case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
case 'video':
return true;
default:
}
return (/\bneedsclick\b/).test(target.className);
};
/**
* Determine whether a given element requires a call to focus to simulate click into element.
*
* @param {EventTarget|Element} target Target DOM element
* @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
*/
FastClick.prototype.needsFocus = function(target) {
switch (target.nodeName.toLowerCase()) {
case 'textarea':
return true;
case 'select':
return !deviceIsAndroid;
case 'input':
switch (target.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
}
// No point in attempting to focus disabled inputs
return !target.disabled && !target.readOnly;
default:
return (/\bneedsfocus\b/).test(target.className);
}
};
/**
* Send a click event to the specified element.
*
* @param {EventTarget|Element} targetElement
* @param {Event} event
*/
FastClick.prototype.sendClick = function(targetElement, event) {
var clickEvent, touch;
// On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
if (document.activeElement && document.activeElement !== targetElement) {
document.activeElement.blur();
}
touch = event.changedTouches[0];
// Synthesise a click event, with an extra attribute so it can be tracked
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true;
targetElement.dispatchEvent(clickEvent);
};
FastClick.prototype.determineEventType = function(targetElement) {
//Issue #159: Android Chrome Select Box does not open with a synthetic click event
if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
return 'mousedown';
}
return 'click';
};
/**
* @param {EventTarget|Element} targetElement
*/
FastClick.prototype.focus = function(targetElement) {
var length;
// Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
if(deviceAboveIOS11_3){
targetElement.focus();
}
length = targetElement.value.length;
targetElement.setSelectionRange(length, length);
} else {
targetElement.focus();
}
};
/**
* Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
*
* @param {EventTarget|Element} targetElement
*/
FastClick.prototype.updateScrollParent = function(targetElement) {
var scrollParent, parentElement;
scrollParent = targetElement.fastClickScrollParent;
// Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
// target element was moved to another parent.
if (!scrollParent || !scrollParent.contains(targetElement)) {
parentElement = targetElement;
do {
if (parentElement.scrollHeight > parentElement.offsetHeight) {
scrollParent = parentElement;
targetElement.fastClickScrollParent = parentElement;
break;
}
parentElement = parentElement.parentElement;
} while (parentElement);
}
// Always update the scroll top tracker if possible.
if (scrollParent) {
scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
}
};
/**
* @param {EventTarget} targetElement
* @returns {Element|EventTarget}
*/
FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
// On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
if (eventTarget.nodeType === Node.TEXT_NODE) {
return eventTarget.parentNode;
}
return eventTarget;
};
/**
* On touch start, record the position and scroll offset.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchStart = function(event) {
var targetElement, touch, selection, touchStartTime;
touchStartTime = (new Date()).getTime();
// Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
if (event.targetTouches.length > 1) {
return true;
}
targetElement = this.getTargetElementFromEventTarget(event.target);
touch = event.targetTouches[0];
if (deviceIsIOS) {
// Only trusted events will deselect text on iOS (issue #49)
selection = window.getSelection();
if (selection.rangeCount && !selection.isCollapsed) {
return true;
}
if (!deviceIsIOS4) {
// Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
// when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
// with the same identifier as the touch event that previously triggered the click that triggered the alert.
// Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
// immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
// Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
// which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
// random integers, it's safe to to continue if the identifier is 0 here.
if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
event.preventDefault();
return false;
}
this.lastTouchIdentifier = touch.identifier;
// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
// 1) the user does a fling scroll on the scrollable layer
// 2) the user stops the fling scroll with another tap
// then the event.target of the last 'touchend' event will be the element that was under the user's finger
// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
this.updateScrollParent(targetElement);
}
}
this.trackingClick = true;
this.trackingClickStart = touchStartTime;
this.targetElement = targetElement;
this.touchStartX = touch.pageX;
this.touchStartY = touch.pageY;
// Prevent phantom clicks on fast double-tap (issue #36)
if ((touchStartTime - this.lastClickTime) < this.tapDelay) {
event.preventDefault();
}
return true;
};
/**
* Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.touchHasMoved = function(event) {
var touch = event.changedTouches[0], boundary = this.touchBoundary;
if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
return true;
}
return false;
};
/**
* Update the last position.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchMove = function(event) {
if (!this.trackingClick) {
return true;
}
// If the touch has moved, cancel the click tracking
if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
this.trackingClick = false;
this.targetElement = null;
}
return true;
};
/**
* Attempt to find the labelled control for the given label element.
*
* @param {EventTarget|HTMLLabelElement} labelElement
* @returns {Element|null}
*/
FastClick.prototype.findControl = function(labelElement) {
// Fast path for newer browsers supporting the HTML5 control attribute
if (labelElement.control !== undefined) {
return labelElement.control;
}
// All browsers under test that support touch events also support the HTML5 htmlFor attribute
if (labelElement.htmlFor) {
return document.getElementById(labelElement.htmlFor);
}
// If no for attribute exists, attempt to retrieve the first labellable descendant element
// the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
};
/**
* On touch end, determine whether to send a click event at once.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchEnd = function(event) {
var forElement, trackingClickStart, targetTagName, scrollParent, touch, touchEndTime,targetElement = this.targetElement;
touchEndTime = (new Date()).getTime();
if (!this.trackingClick) {
return true;
}
// Prevent phantom clicks on fast double-tap (issue #36)
// Prevent phantom clicks on fast double-tap (issue #36)
if ((touchEndTime - this.lastClickTime) < this.tapDelay) {
this.cancelNextClick = true;
return true;
}
if ((touchEndTime - this.trackingClickStart) > this.tapTimeout) {
return true;
}
// Reset to prevent wrong click cancel on input (issue #156).
this.cancelNextClick = false;
this.lastClickTime = touchEndTime;
trackingClickStart = this.trackingClickStart;
this.trackingClick = false;
this.trackingClickStart = 0;
// On some iOS devices, the targetElement supplied with the event is invalid if the layer
// is performing a transition or scroll, and has to be re-detected manually. Note that
// for this to function correctly, it must be called *after* the event target is checked!
// See issue #57; also filed as rdar://13048589 .
if (deviceIsIOSWithBadTarget) {
touch = event.changedTouches[0];
// In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
}
targetTagName = targetElement.tagName.toLowerCase();
if (targetTagName === 'label') {
forElement = this.findControl(targetElement);
if (forElement) {
this.focus(targetElement);
if (deviceIsAndroid) {
return false;
}
targetElement = forElement;
}
} else if (this.needsFocus(targetElement)) {
// Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
// Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
if ((touchEndTime - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
this.targetElement = null;
return false;
}
this.focus(targetElement);
this.sendClick(targetElement, event);
// Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
// Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
if (!deviceIsIOS || targetTagName !== 'select') {
this.targetElement = null;
event.preventDefault();
}
return false;
} else {
var parent = targetElement;
while(parent && (parent.tagName.toUpperCase() !== "BODY")) {
if(parent.tagName.toUpperCase() === "LABEL") {
$(parent).find("input").click();
}
parent = parent.parentNode;
}
}
if (deviceIsIOS && !deviceIsIOS4) {
// Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
scrollParent = targetElement.fastClickScrollParent;
if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
return true;
}
}
// Prevent the actual click from going though - unless the target node is marked as requiring
// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
if (!this.needsClick(targetElement)) {
event.preventDefault();
this.sendClick(targetElement, event);
}
return false;
};
/**
* On touch cancel, stop tracking the click.
*
* @returns {void}
*/
FastClick.prototype.onTouchCancel = function() {
this.trackingClick = false;
this.targetElement = null;
};
/**
* Determine mouse events which should be permitted.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onMouse = function(event) {
// If a target element was never set (because a touch event was never fired) allow the event
if (!this.targetElement) {
return true;
}
if (event.forwardedTouchEvent) {
return true;
}
// Programmatically generated events targeting a specific element should be permitted
if (!event.cancelable) {
return true;
}
// Derive and check the target element to see whether the mouse event needs to be permitted;
// unless explicitly enabled, prevent non-touch click events from triggering actions,
// to prevent ghost/doubleclicks.
if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
// Prevent any user-added listeners declared on FastClick element from being fired.
if (event.stopImmediatePropagation) {
event.stopImmediatePropagation();
} else {
// Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
event.propagationStopped = true;
}
// Cancel the event
event.stopPropagation();
event.preventDefault();
return false;
}
// If the mouse event is permitted, return true for the action to go through.
return true;
};
/**
* On actual clicks, determine whether this is a touch-generated click, a click action occurring
* naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
* an actual click which should be permitted.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onClick = function(event) {
var permitted;
// It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
if (this.trackingClick) {
this.targetElement = null;
this.trackingClick = false;
return true;
}
// Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
if (event.target.type === 'submit' && event.detail === 0) {
return true;
}
permitted = this.onMouse(event);
// Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
if (!permitted) {
this.targetElement = null;
}
// If clicks are permitted, return true for the action to go through.
return permitted;
};
/**
* Remove all FastClick's event listeners.
*
* @returns {void}
*/
FastClick.prototype.destroy = function() {
var layer = this.layer;
if (deviceIsAndroid) {
layer.removeEventListener('mouseover', this.onMouse, true);
layer.removeEventListener('mousedown', this.onMouse, true);
layer.removeEventListener('mouseup', this.onMouse, true);
}
layer.removeEventListener('click', this.onClick, true);
layer.removeEventListener('touchstart', this.onTouchStart, false);
layer.removeEventListener('touchmove', this.onTouchMove, passiveSupported ? { passive: true } : false);
layer.removeEventListener('touchend', this.onTouchEnd,false);
layer.removeEventListener('touchcancel', this.onTouchCancel, false);
};
/**
* Check whether FastClick is needed.
*
* @param {Element} layer The layer to listen on
*/
FastClick.notNeeded = function(layer) {
var metaViewport;
var chromeVersion;
var blackberryVersion;
var firefoxVersion;
// Devices that don't support touch don't need FastClick
if (typeof window.ontouchstart === 'undefined') {
return true;
}
// Chrome version - zero for other browsers
chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
if (chromeVersion) {
if (deviceIsAndroid) {
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport) {
// Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
return true;
}
// Chrome 32 and above with width=device-width or less don't need FastClick
if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
return true;
}
}
// Chrome desktop doesn't need FastClick (issue #15)
} else {
return true;
}
}
if (deviceIsBlackBerry10) {
blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
// BlackBerry 10.3+ does not require Fastclick library.
// https://github.com/ftlabs/fastclick/issues/251
if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport) {
// user-scalable=no eliminates click delay.
if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
return true;
}
// width=device-width (or less than device-width) eliminates click delay.
if (document.documentElement.scrollWidth <= window.outerWidth) {
return true;
}
}
}
}
// IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
return true;
}
// Firefox version - zero for other browsers
firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
if (firefoxVersion >= 27) {
// Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
return true;
}
}
// IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
// http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
return true;
}
return false;
};
/**
* Factory method for creating a FastClick object
*
* @param {Element} layer The layer to listen on
* @param {Object} [options={}] The options to override the defaults
*/
FastClick.attach = function(layer, options) {
return new FastClick(layer, options);
};
//直接绑定
$(function() {
FastClick.attach(document.body);
});
}($));
/*===========================
Template7 Template engine
===========================*/
/* global $:true */
/* jshint unused:false */
/* jshint forin:false */
+function ($) {
"use strict";
$.Template7 = $.t7 = (function () {
function isArray(arr) {
return Object.prototype.toString.apply(arr) === '[object Array]';
}
function isObject(obj) {
return obj instanceof Object;
}
function isFunction(func) {
return typeof func === 'function';
}
var cache = {};
function helperToSlices(string) {
var helperParts = string.replace(/[{}#}]/g, '').split(' ');
var slices = [];
var shiftIndex, i, j;
for (i = 0; i < helperParts.length; i++) {
var part = helperParts[i];
if (i === 0) slices.push(part);
else {
if (part.indexOf('"') === 0) {
// Plain String
if (part.match(/"/g).length === 2) {
// One word string
slices.push(part);
}
else {
// Find closed Index
shiftIndex = 0;
for (j = i + 1; j < helperParts.length; j++) {
part += ' ' + helperParts[j];
if (helperParts[j].indexOf('"') >= 0) {
shiftIndex = j;
slices.push(part);
break;
}
}
if (shiftIndex) i = shiftIndex;
}
}
else {
if (part.indexOf('=') > 0) {
// Hash
var hashParts = part.split('=');
var hashName = hashParts[0];
var hashContent = hashParts[1];
if (hashContent.match(/"/g).length !== 2) {
shiftIndex = 0;
for (j = i + 1; j < helperParts.length; j++) {
hashContent += ' ' + helperParts[j];
if (helperParts[j].indexOf('"') >= 0) {
shiftIndex = j;
break;
}
}
if (shiftIndex) i = shiftIndex;
}
var hash = [hashName, hashContent.replace(/"/g,'')];
slices.push(hash);
}
else {
// Plain variable
slices.push(part);
}
}
}
}
return slices;
}
function stringToBlocks(string) {
var blocks = [], i, j, k;
if (!string) return [];
var _blocks = string.split(/({{[^{^}]*}})/);
for (i = 0; i < _blocks.length; i++) {
var block = _blocks[i];
if (block === '') continue;
if (block.indexOf('{{') < 0) {
blocks.push({
type: 'plain',
content: block
});
}
else {
if (block.indexOf('{/') >= 0) {
continue;
}
if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) {
// Simple variable
blocks.push({
type: 'variable',
contextName: block.replace(/[{}]/g, '')
});
continue;
}
// Helpers
var helperSlices = helperToSlices(block);
var helperName = helperSlices[0];
var helperContext = [];
var helperHash = {};
for (j = 1; j < helperSlices.length; j++) {
var slice = helperSlices[j];
if (isArray(slice)) {
// Hash
helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1];
}
else {
helperContext.push(slice);
}
}
if (block.indexOf('{#') >= 0) {
// Condition/Helper
var helperStartIndex = i;
var helperContent = '';
var elseContent = '';
var toSkip = 0;
var shiftIndex;
var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0;
for (j = i + 1; j < _blocks.length; j++) {
if (_blocks[j].indexOf('{{#') >= 0) {
depth ++;
}
if (_blocks[j].indexOf('{{/') >= 0) {
depth --;
}
if (_blocks[j].indexOf('{{#' + helperName) >= 0) {
helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
toSkip ++;
}
else if (_blocks[j].indexOf('{{/' + helperName) >= 0) {
if (toSkip > 0) {
toSkip--;
helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
}
else {
shiftIndex = j;
foundClosed = true;
break;
}
}
else if (_blocks[j].indexOf('else') >= 0 && depth === 0) {
foundElse = true;
}
else {
if (!foundElse) helperContent += _blocks[j];
if (foundElse) elseContent += _blocks[j];
}
}
if (foundClosed) {
if (shiftIndex) i = shiftIndex;
blocks.push({
type: 'helper',
helperName: helperName,
contextName: helperContext,
content: helperContent,
inverseContent: elseContent,
hash: helperHash
});
}
}
else if (block.indexOf(' ') > 0) {
blocks.push({
type: 'helper',
helperName: helperName,
contextName: helperContext,
hash: helperHash
});
}
}
}
return blocks;
}
var Template7 = function (template) {
var t = this;
t.template = template;
function getCompileFn(block, depth) {
if (block.content) return compile(block.content, depth);
else return function () {return ''; };
}
function getCompileInverse(block, depth) {
if (block.inverseContent) return compile(block.inverseContent, depth);
else return function () {return ''; };
}
function getCompileVar(name, ctx) {
var variable, parts, levelsUp = 0, initialCtx = ctx;
if (name.indexOf('../') === 0) {
levelsUp = name.split('../').length - 1;
var newDepth = ctx.split('_')[1] - levelsUp;
ctx = 'ctx_' + (newDepth >= 1 ? newDepth : 1);
parts = name.split('../')[levelsUp].split('.');
}
else if (name.indexOf('@global') === 0) {
ctx = '$.Template7.global';
parts = name.split('@global.')[1].split('.');
}
else if (name.indexOf('@root') === 0) {
ctx = 'ctx_1';
parts = name.split('@root.')[1].split('.');
}
else {
parts = name.split('.');
}
variable = ctx;
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (part.indexOf('@') === 0) {
if (i > 0) {
variable += '[(data && data.' + part.replace('@', '') + ')]';
}
else {
variable = '(data && data.' + name.replace('@', '') + ')';
}
}
else {
if (isFinite(part)) {
variable += '[' + part + ']';
}
else {
if (part.indexOf('this') === 0) {
variable = part.replace('this', ctx);
}
else {
variable += '.' + part;
}
}
}
}
return variable;
}
function getCompiledArguments(contextArray, ctx) {
var arr = [];
for (var i = 0; i < contextArray.length; i++) {
if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]);
else {
arr.push(getCompileVar(contextArray[i], ctx));
}
}
return arr.join(', ');
}
function compile(template, depth) {
depth = depth || 1;
template = template || t.template;
if (typeof template !== 'string') {
throw new Error('Template7: Template must be a string');
}
var blocks = stringToBlocks(template);
if (blocks.length === 0) {
return function () { return ''; };
}
var ctx = 'ctx_' + depth;
var resultString = '(function (' + ctx + ', data) {\n';
if (depth === 1) {
resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n';
resultString += 'function isFunction(func){return (typeof func === \'function\');}\n';
resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n';
}
resultString += 'var r = \'\';\n';
var i, j, context;
for (i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Plain block
if (block.type === 'plain') {
resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';';
continue;
}
var variable, compiledArguments;
// Variable block
if (block.type === 'variable') {
variable = getCompileVar(block.contextName, ctx);
resultString += 'r += c(' + variable + ', ' + ctx + ');';
}
// Helpers block
if (block.type === 'helper') {
if (block.helperName in t.helpers) {
compiledArguments = getCompiledArguments(block.contextName, ctx);
resultString += 'r += ($.Template7.helpers.' + block.helperName + ').call(' + ctx + ', ' + (compiledArguments && (compiledArguments + ', ')) +'{hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
}
else {
if (block.contextName.length > 0) {
throw new Error('Template7: Missing helper: "' + block.helperName + '"');
}
else {
variable = getCompileVar(block.helperName, ctx);
resultString += 'if (' + variable + ') {';
resultString += 'if (isArray(' + variable + ')) {';
resultString += 'r += ($.Template7.helpers.each).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
resultString += '}else {';
resultString += 'r += ($.Template7.helpers.with).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
resultString += '}}';
}
}
}
}
resultString += '\nreturn r;})';
return eval.call(window, resultString);
}
t.compile = function (template) {
if (!t.compiled) {
t.compiled = compile(template);
}
return t.compiled;
};
};
Template7.prototype = {
options: {},
helpers: {
'if': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
if (context) {
return options.fn(this, options.data);
}
else {
return options.inverse(this, options.data);
}
},
'unless': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
if (!context) {
return options.fn(this, options.data);
}
else {
return options.inverse(this, options.data);
}
},
'each': function (context, options) {
var ret = '', i = 0;
if (isFunction(context)) { context = context.call(this); }
if (isArray(context)) {
if (options.hash.reverse) {
context = context.reverse();
}
for (i = 0; i < context.length; i++) {
ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i});
}
if (options.hash.reverse) {
context = context.reverse();
}
}
else {
for (var key in context) {
i++;
ret += options.fn(context[key], {key: key});
}
}
if (i > 0) return ret;
else return options.inverse(this);
},
'with': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
return options.fn(context);
},
'join': function (context, options) {
if (isFunction(context)) { context = context.call(this); }
return context.join(options.hash.delimiter || options.hash.delimeter);
},
'js': function (expression, options) {
var func;
if (expression.indexOf('return')>=0) {
func = '(function(){'+expression+'})';
}
else {
func = '(function(){return ('+expression+')})';
}
return eval.call(this, func).call(this);
},
'js_compare': function (expression, options) {
var func;
if (expression.indexOf('return')>=0) {
func = '(function(){'+expression+'})';
}
else {
func = '(function(){return ('+expression+')})';
}
var condition = eval.call(this, func).call(this);
if (condition) {
return options.fn(this, options.data);
}
else {
return options.inverse(this, options.data);
}
}
}
};
var t7 = function (template, data) {
if (arguments.length === 2) {
var instance = new Template7(template);
var rendered = instance.compile()(data);
instance = null;
return (rendered);
}
else return new Template7(template);
};
t7.registerHelper = function (name, fn) {
Template7.prototype.helpers[name] = fn;
};
t7.unregisterHelper = function (name) {
Template7.prototype.helpers[name] = undefined;
delete Template7.prototype.helpers[name];
};
t7.compile = function (template, options) {
var instance = new Template7(template, options);
return instance.compile();
};
t7.options = Template7.prototype.options;
t7.helpers = Template7.prototype.helpers;
return t7;
})();
}($);
/* global $:true */
+ function($) {
"use strict";
$.getCurrentPage = function() {
return $(".page")[0] || document.body;
};
}($);
/* ===============================================================================
************ Tabs ************
=============================================================================== */
/* global $:true */
+function ($) {
"use strict";
var showTab = function (tab, tabLink, force) {
var newTab = $(tab);
var activeClass = newTab.hasClass("page") ? "page-current" : "active";
if (arguments.length === 2) {
if (typeof tabLink === 'boolean') {
force = tabLink;
}
}
if (newTab.length === 0) return false;
if (newTab.hasClass(activeClass)) {
if (force) newTab.trigger('show');
return false;
}
var tabs = newTab.parent('.tabs');
if (tabs.length === 0) return false;
// Animated tabs
/*var isAnimatedTabs = tabs.parent().hasClass('tabs-animated-wrap');
if (isAnimatedTabs) {
tabs.transform('translate3d(' + -newTab.index() * 100 + '%,0,0)');
}*/
// Remove active class from old tabs
var oldTab = tabs.children('.tab.'+activeClass).removeClass(activeClass);
// Add active class to new tab
newTab.addClass(activeClass);
// Trigger 'show' event on new tab
newTab.trigger('show');
// Update navbars in new tab
/*if (!isAnimatedTabs && newTab.find('.navbar').length > 0) {
// Find tab's view
var viewContainer;
if (newTab.hasClass(app.params.viewClass)) viewContainer = newTab[0];
else viewContainer = newTab.parents('.' + app.params.viewClass)[0];
app.sizeNavbars(viewContainer);
}*/
// Find related link for new tab
if (tabLink) tabLink = $(tabLink);
else {
// Search by id
if (typeof tab === 'string') tabLink = $('.tab-link[href="' + tab + '"]');
else tabLink = $('.tab-link[href="#' + newTab.attr('id') + '"]');
// Search by data-tab
if (!tabLink || tabLink && tabLink.length === 0) {
$('[data-tab]').each(function () {
if (newTab.is($(this).attr('data-tab'))) tabLink = $(this);
});
}
}
if (tabLink.length === 0) return;
// Find related link for old tab
var oldTabLink;
if (oldTab && oldTab.length > 0) {
// Search by id
var oldTabId = oldTab.attr('id');
if (oldTabId) oldTabLink = $('.tab-link[href="#' + oldTabId + '"]');
// Search by data-tab
if (!oldTabLink || oldTabLink && oldTabLink.length === 0) {
$('[data-tab]').each(function () {
if (oldTab.is($(this).attr('data-tab'))) oldTabLink = $(this);
});
}
}
// Update links' classes
if (tabLink && tabLink.length > 0) tabLink.addClass('active');
if (oldTabLink && oldTabLink.length > 0) oldTabLink.removeClass('active');
return true;
};
var old = $.showTab;
$.showTab = showTab;
$.showTab.noConflict = function () {
$.showTab = old;
return this;
};
$(document).on("click", ".tab-link", function(e) {
e.preventDefault();
var clicked = $(this);
showTab(clicked.data("tab") || clicked.attr('href'), clicked);
});
}($);
/* global $:true */
+function ($) {
"use strict";
$(document).on("click", ".tab-item", function(e) {
var $target = $(e.currentTarget);
if(!$target.hasClass("tab-link")) {
$target.parent().find(".active").removeClass("active");
$target.addClass("active");
}
});
var highlight = function(e, pageId) {
var $tab = $(".bar-tab .tab-item[href='#"+pageId+"']");
$tab.parent().find(".active").removeClass("active");
$tab.addClass("active");
};
$(document).on("pageInit", highlight);
$(document).on("pageReinit", highlight);
$.showToolbar = function(show) {
$(document.body)[show ? "removeClass" : "addClass"]("tabbar-hidden");
};
}($);
/*======================================================
************ Modals ************
======================================================*/
/*jshint unused: false*/
/* global $:true */
+(function ($) {
'use strict';
var _modalTemplateTempDiv = document.createElement('div');
$.modalStack = [];
var t7 = $.Template7;
$.modalStackClearQueue = function () {
if ($.modalStack.length) {
$.modalStack.shift()();
}
};
$.modal = function (params) {
params = params || {};
var modalHTML = '';
if (defaults.modalTemplate) {
if (!$._compiledTemplates.modal)
$._compiledTemplates.modal = t7.compile(defaults.modalTemplate);
modalHTML = $._compiledTemplates.modal(params);
} else {
var buttonsHTML = '';
if (params.buttons && params.buttons.length > 0) {
for (var i = 0; i < params.buttons.length; i++) {
buttonsHTML +=
'' +
params.buttons[i].text +
' ';
}
}
var titleHTML = params.title
? '
' + params.title + '
'
: '';
var textHTML = params.text
? '' + params.text + '
'
: '';
var afterTextHTML = params.afterText ? params.afterText : '';
var noButtons =
!params.buttons || params.buttons.length === 0
? 'modal-no-buttons'
: '';
var verticalButtons = params.verticalButtons
? 'modal-buttons-vertical'
: '';
modalHTML =
'';
}
_modalTemplateTempDiv.innerHTML = modalHTML;
var modal = $(_modalTemplateTempDiv).children();
$(defaults.modalContainer).append(modal[0]);
// Add events on buttons
modal.find('.modal-button').each(function (index, el) {
$(el).on('click', function (e) {
if (params.buttons[index].close !== false) $.closeModal(modal);
if (params.buttons[index].onClick)
params.buttons[index].onClick(modal, e);
if (params.onClick) params.onClick(modal, index);
});
});
$.openModal(modal);
return modal[0];
};
$.alert = function (text, title, callbackOk) {
if (typeof title === 'function') {
callbackOk = arguments[1];
title = undefined;
}
return $.modal({
text: text || '',
title: typeof title === 'undefined' ? defaults.modalTitle : title,
buttons: [
{ text: defaults.modalButtonOk, bold: true, onClick: callbackOk },
],
});
};
$.confirm = function (text, title, callbackOk, callbackCancel) {
if (typeof title === 'function') {
callbackCancel = arguments[2];
callbackOk = arguments[1];
title = undefined;
}
return $.modal({
text: text || '',
title: typeof title === 'undefined' ? defaults.modalTitle : title,
buttons: [
{ text: defaults.modalButtonCancel, onClick: callbackCancel },
{ text: defaults.modalButtonOk, bold: true, onClick: callbackOk },
],
});
};
$.prompt = function (text, title, callbackOk, callbackCancel) {
if (typeof title === 'function') {
callbackCancel = arguments[2];
callbackOk = arguments[1];
title = undefined;
}
return $.modal({
text: text || '',
title: typeof title === 'undefined' ? defaults.modalTitle : title,
afterText: ' ',
buttons: [
{
text: defaults.modalButtonCancel,
},
{
text: defaults.modalButtonOk,
bold: true,
},
],
onClick: function (modal, index) {
if (index === 0 && callbackCancel)
callbackCancel($(modal).find('.modal-text-input').val());
if (index === 1 && callbackOk)
callbackOk($(modal).find('.modal-text-input').val());
},
});
};
$.modalLogin = function (text, title, callbackOk, callbackCancel) {
if (typeof title === 'function') {
callbackCancel = arguments[2];
callbackOk = arguments[1];
title = undefined;
}
return $.modal({
text: text || '',
title: typeof title === 'undefined' ? defaults.modalTitle : title,
afterText:
' ',
buttons: [
{
text: defaults.modalButtonCancel,
},
{
text: defaults.modalButtonOk,
bold: true,
},
],
onClick: function (modal, index) {
var username = $(modal)
.find('.modal-text-input[name="modal-username"]')
.val();
var password = $(modal)
.find('.modal-text-input[name="modal-password"]')
.val();
if (index === 0 && callbackCancel) callbackCancel(username, password);
if (index === 1 && callbackOk) callbackOk(username, password);
},
});
};
$.modalPassword = function (text, title, callbackOk, callbackCancel) {
if (typeof title === 'function') {
callbackCancel = arguments[2];
callbackOk = arguments[1];
title = undefined;
}
return $.modal({
text: text || '',
title: typeof title === 'undefined' ? defaults.modalTitle : title,
afterText:
' ',
buttons: [
{
text: defaults.modalButtonCancel,
},
{
text: defaults.modalButtonOk,
bold: true,
},
],
onClick: function (modal, index) {
var password = $(modal)
.find('.modal-text-input[name="modal-password"]')
.val();
if (index === 0 && callbackCancel) callbackCancel(password);
if (index === 1 && callbackOk) callbackOk(password);
},
});
};
$.showPreloader = function (title) {
return $.modal({
title: title || defaults.modalPreloaderTitle,
text: '
',
});
};
$.hidePreloader = function () {
$.closeModal('.modal.modal-in');
};
$.showIndicator = function () {
$(defaults.modalContainer).append(
'
'
);
};
$.hideIndicator = function () {
$('.preloader-indicator-overlay, .preloader-indicator-modal').remove();
};
// Action Sheet
$.actions = function (target, params) {
var toPopover = false,
modal,
groupSelector,
buttonSelector;
if (arguments.length === 1) {
// Actions
params = target;
} else {
// Popover
if ($.device.ios) {
if ($.device.ipad) toPopover = true;
} else {
if ($(window).width() >= 768) toPopover = true;
}
}
params = params || [];
if (params.length > 0 && !$.isArray(params[0])) {
params = [params];
}
var modalHTML;
if (toPopover) {
var actionsToPopoverTemplate =
defaults.modalActionsToPopoverTemplate ||
'' +
'
' +
'{{#each this}}' +
'
' +
'
' +
'{{#each this}}' +
'{{#if label}}' +
'{{text}} ' +
'{{else}}' +
'{{text}} ' +
'{{/if}}' +
'{{/each}}' +
' ' +
'
' +
'{{/each}}' +
'
' +
'
';
if (!$._compiledTemplates.actionsToPopover) {
$._compiledTemplates.actionsToPopover = t7.compile(
actionsToPopoverTemplate
);
}
var popoverHTML = $._compiledTemplates.actionsToPopover(params);
modal = $($.popover(popoverHTML, target, true));
groupSelector = '.list-block ul';
buttonSelector = '.list-button';
} else {
if (defaults.modalActionsTemplate) {
if (!$._compiledTemplates.actions)
$._compiledTemplates.actions = t7.compile(
defaults.modalActionsTemplate
);
modalHTML = $._compiledTemplates.actions(params);
} else {
var buttonsHTML = '';
for (var i = 0; i < params.length; i++) {
for (var j = 0; j < params[i].length; j++) {
if (j === 0) buttonsHTML += '';
var button = params[i][j];
var buttonClass = button.label
? 'actions-modal-label'
: 'actions-modal-button';
if (button.bold) buttonClass += ' actions-modal-button-bold';
if (button.color) buttonClass += ' color-' + button.color;
if (button.bg) buttonClass += ' bg-' + button.bg;
if (button.disabled) buttonClass += ' disabled';
buttonsHTML +=
'' + button.text + ' ';
if (j === params[i].length - 1) buttonsHTML += '
';
}
}
modalHTML = '' + buttonsHTML + '
';
}
_modalTemplateTempDiv.innerHTML = modalHTML;
modal = $(_modalTemplateTempDiv).children();
$(defaults.modalContainer).append(modal[0]);
groupSelector = '.actions-modal-group';
buttonSelector = '.actions-modal-button';
}
var groups = modal.find(groupSelector);
groups.each(function (index, el) {
var groupIndex = index;
$(el)
.children()
.each(function (index, el) {
var buttonIndex = index;
var buttonParams = params[groupIndex][buttonIndex];
var clickTarget;
if (!toPopover && $(el).is(buttonSelector)) clickTarget = $(el);
if (toPopover && $(el).find(buttonSelector).length > 0)
clickTarget = $(el).find(buttonSelector);
if (clickTarget) {
clickTarget.on('click', function (e) {
if (buttonParams.close !== false) $.closeModal(modal);
if (buttonParams.onClick) buttonParams.onClick(modal, e);
});
}
});
});
if (!toPopover) $.openModal(modal);
return modal[0];
};
$.popover = function (modal, target, removeOnClose) {
if (typeof removeOnClose === 'undefined') removeOnClose = true;
if (typeof modal === 'string' && modal.indexOf('<') >= 0) {
var _modal = document.createElement('div');
_modal.innerHTML = modal.trim();
if (_modal.childNodes.length > 0) {
modal = _modal.childNodes[0];
if (removeOnClose) modal.classList.add('remove-on-close');
$(defaults.modalContainer).append(modal);
} else return false; //nothing found
}
modal = $(modal);
target = $(target);
if (modal.length === 0 || target.length === 0) return false;
if (modal.find('.popover-angle').length === 0) {
modal.append('
');
}
modal.show();
function sizePopover() {
modal.css({ left: '', top: '' });
var modalWidth = modal.width();
var modalHeight = modal.height(); // 13 - height of angle
var modalAngle = modal.find('.popover-angle');
var modalAngleSize = modalAngle.width() / 2;
var modalAngleLeft, modalAngleTop;
modalAngle
.removeClass('on-left on-right on-top on-bottom')
.css({ left: '', top: '' });
var targetWidth = target.outerWidth();
var targetHeight = target.outerHeight();
var targetOffset = target.offset();
var targetParentPage = target.parents('.page');
if (targetParentPage.length > 0) {
targetOffset.top = targetOffset.top - targetParentPage[0].scrollTop;
}
var windowHeight = $(window).height();
var windowWidth = $(window).width();
var modalTop = 0;
var modalLeft = 0;
var diff = 0;
// Top Position
var modalPosition = 'top';
if (modalHeight + modalAngleSize < targetOffset.top) {
// On top
modalTop = targetOffset.top - modalHeight - modalAngleSize;
} else if (
modalHeight + modalAngleSize <
windowHeight - targetOffset.top - targetHeight
) {
// On bottom
modalPosition = 'bottom';
modalTop = targetOffset.top + targetHeight + modalAngleSize;
} else {
// On middle
modalPosition = 'middle';
modalTop = targetHeight / 2 + targetOffset.top - modalHeight / 2;
diff = modalTop;
if (modalTop < 0) {
modalTop = 5;
} else if (modalTop + modalHeight > windowHeight) {
modalTop = windowHeight - modalHeight - 5;
}
diff = diff - modalTop;
}
// Horizontal Position
if (modalPosition === 'top' || modalPosition === 'bottom') {
modalLeft = targetWidth / 2 + targetOffset.left - modalWidth / 2;
diff = modalLeft;
if (modalLeft < 5) modalLeft = 5;
if (modalLeft + modalWidth > windowWidth)
modalLeft = windowWidth - modalWidth - 5;
if (modalPosition === 'top') modalAngle.addClass('on-bottom');
if (modalPosition === 'bottom') modalAngle.addClass('on-top');
diff = diff - modalLeft;
modalAngleLeft = modalWidth / 2 - modalAngleSize + diff;
modalAngleLeft = Math.max(
Math.min(modalAngleLeft, modalWidth - modalAngleSize * 2 - 6),
6
);
modalAngle.css({ left: modalAngleLeft + 'px' });
} else if (modalPosition === 'middle') {
modalLeft = targetOffset.left - modalWidth - modalAngleSize;
modalAngle.addClass('on-right');
if (modalLeft < 5) {
modalLeft = targetOffset.left + targetWidth + modalAngleSize;
modalAngle.removeClass('on-right').addClass('on-left');
}
if (modalLeft + modalWidth > windowWidth) {
modalLeft = windowWidth - modalWidth - 5;
modalAngle.removeClass('on-right').addClass('on-left');
}
modalAngleTop = modalHeight / 2 - modalAngleSize + diff;
modalAngleTop = Math.max(
Math.min(modalAngleTop, modalHeight - modalAngleSize * 2 - 6),
6
);
modalAngle.css({ top: modalAngleTop + 'px' });
}
// Apply Styles
modal.css({ top: modalTop + 'px', left: modalLeft + 'px' });
}
sizePopover();
$(window).on('resize', sizePopover);
modal.on('close', function () {
$(window).off('resize', sizePopover);
});
if (modal.find('.' + defaults.viewClass).length > 0) {
$.sizeNavbars(modal.find('.' + defaults.viewClass)[0]);
}
$.openModal(modal);
return modal[0];
};
$.popup = function (modal, removeOnClose) {
if (typeof removeOnClose === 'undefined') removeOnClose = true;
if (typeof modal === 'string' && modal.indexOf('<') >= 0) {
var _modal = document.createElement('div');
_modal.innerHTML = modal.trim();
if (_modal.childNodes.length > 0) {
modal = _modal.childNodes[0];
if (removeOnClose) modal.classList.add('remove-on-close');
$(defaults.modalContainer).append(modal);
} else return false; //nothing found
}
modal = $(modal);
if (modal.length === 0) return false;
modal.show();
if (modal.find('.' + defaults.viewClass).length > 0) {
$.sizeNavbars(modal.find('.' + defaults.viewClass)[0]);
}
$.openModal(modal);
return modal[0];
};
$.pickerModal = function (pickerModal, removeOnClose) {
if (typeof removeOnClose === 'undefined') removeOnClose = true;
if (typeof pickerModal === 'string' && pickerModal.indexOf('<') >= 0) {
pickerModal = $(pickerModal);
if (pickerModal.length > 0) {
if (removeOnClose) pickerModal.addClass('remove-on-close');
$(defaults.modalContainer).append(pickerModal[0]);
} else return false; //nothing found
}
pickerModal = $(pickerModal);
if (pickerModal.length === 0) return false;
pickerModal.show();
$.openModal(pickerModal);
return pickerModal[0];
};
$.loginScreen = function (modal) {
if (!modal) modal = '.login-screen';
modal = $(modal);
if (modal.length === 0) return false;
modal.show();
if (modal.find('.' + defaults.viewClass).length > 0) {
$.sizeNavbars(modal.find('.' + defaults.viewClass)[0]);
}
$.openModal(modal);
return modal[0];
};
//显示一个消息,会在2秒钟后自动消失
$.toast = function (msg, time) {
var $toast = $("" + msg + '
').appendTo(
document.body
);
$.openModal($toast);
setTimeout(function () {
$.closeModal($toast);
}, time || 2000);
};
$.openModal = function (modal) {
if (defaults.closePrevious) $.closeModal();
modal = $(modal);
var isModal = modal.hasClass('modal');
if (
$('.modal.modal-in:not(.modal-out)').length &&
defaults.modalStack &&
isModal
) {
$.modalStack.push(function () {
$.openModal(modal);
});
return;
}
var isPopover = modal.hasClass('popover');
var isPopup = modal.hasClass('popup');
var isLoginScreen = modal.hasClass('login-screen');
var isPickerModal = modal.hasClass('picker-modal');
var isToast = modal.hasClass('toast');
if (isModal) {
modal.show();
modal.css({
marginTop: -Math.round(modal.outerHeight() / 2) + 'px',
});
}
if (isToast) {
modal.show();
modal.css({
marginLeft:
-Math.round(parseInt(window.getComputedStyle(modal[0]).width) / 2) +
'px', //
});
}
var overlay;
if (!isLoginScreen && !isPickerModal && !isToast) {
if ($('.modal-overlay').length === 0 && !isPopup) {
$(defaults.modalContainer).append('
');
}
if ($('.popup-overlay').length === 0 && isPopup) {
$(defaults.modalContainer).append('');
}
overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay');
}
//Make sure that styles are applied, trigger relayout;
var clientLeft = modal[0].clientLeft;
// Trugger open event
modal.trigger('open');
// Picker modal body class
if (isPickerModal) {
$(defaults.modalContainer).addClass('with-picker-modal');
}
// Classes for transition in
if (!isLoginScreen && !isPickerModal && !isToast)
overlay.addClass('modal-overlay-visible');
modal
.removeClass('modal-out')
.addClass('modal-in')
.transitionEnd(function (e) {
if (modal.hasClass('modal-out')) modal.trigger('closed');
else modal.trigger('opened');
});
return true;
};
$.closeModal = function (modal) {
modal = $(modal || '.modal-in');
if (typeof modal !== 'undefined' && modal.length === 0) {
return;
}
var isModal = modal.hasClass('modal');
var isPopover = modal.hasClass('popover');
var isPopup = modal.hasClass('popup');
var isLoginScreen = modal.hasClass('login-screen');
var isPickerModal = modal.hasClass('picker-modal');
var removeOnClose = modal.hasClass('remove-on-close');
var overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay');
if (isPopup) {
if (modal.length === $('.popup.modal-in').length) {
overlay.removeClass('modal-overlay-visible');
}
} else if (!isPickerModal) {
overlay.removeClass('modal-overlay-visible');
}
modal.trigger('close');
// Picker modal body class
if (isPickerModal) {
$(defaults.modalContainer).removeClass('with-picker-modal');
$(defaults.modalContainer).addClass('picker-modal-closing');
}
if (!isPopover) {
modal
.removeClass('modal-in')
.addClass('modal-out')
.transitionEnd(function (e) {
if (modal.hasClass('modal-out')) modal.trigger('closed');
else modal.trigger('opened');
if (isPickerModal) {
$(defaults.modalContainer).removeClass('picker-modal-closing');
}
if (isPopup || isLoginScreen || isPickerModal) {
modal.removeClass('modal-out').hide();
if (removeOnClose && modal.length > 0) {
modal.remove();
}
} else {
modal.remove();
}
});
if (isModal && defaults.modalStack) {
$.modalStackClearQueue();
}
} else {
modal.removeClass('modal-in modal-out').trigger('closed').hide();
if (removeOnClose) {
modal.remove();
}
}
return true;
};
function handleClicks(e) {
/*jshint validthis:true */
var clicked = $(this);
var url = clicked.attr('href');
//Collect Clicked data- attributes
var clickedData = clicked.dataset();
// Popover
if (clicked.hasClass('open-popover')) {
var popover;
if (clickedData.popover) {
popover = clickedData.popover;
} else popover = '.popover';
$.popover(popover, clicked);
}
if (clicked.hasClass('close-popover')) {
$.closeModal('.popover.modal-in');
}
// Popup
var popup;
if (clicked.hasClass('open-popup')) {
if (clickedData.popup) {
popup = clickedData.popup;
} else popup = '.popup';
$.popup(popup);
}
if (clicked.hasClass('close-popup')) {
if (clickedData.popup) {
popup = clickedData.popup;
} else popup = '.popup.modal-in';
$.closeModal(popup);
}
// Close Modal
if (clicked.hasClass('modal-overlay')) {
if ($('.modal.modal-in').length > 0 && defaults.modalCloseByOutside)
$.closeModal('.modal.modal-in');
if (
$('.actions-modal.modal-in').length > 0 &&
defaults.actionsCloseByOutside
)
$.closeModal('.actions-modal.modal-in');
if ($('.popover.modal-in').length > 0) $.closeModal('.popover.modal-in');
}
if (clicked.hasClass('popup-overlay')) {
if ($('.popup.modal-in').length > 0 && defaults.popupCloseByOutside)
$.closeModal('.popup.modal-in');
}
}
var defaults = ($.modal.prototype.defaults = {
modalButtonOk: '确定',
modalButtonCancel: '关闭',
modalPreloaderTitle: '加载中...',
modalContainer: document.body,
modalCloseByOutside: true,
actionsCloseByOutside: false,
popupCloseByOutside: true,
closePrevious: true, //close all previous modal before open
});
$(function () {
$(document).on(
'click',
' .modal-overlay, .popup-overlay, .close-popup, .open-popup, .open-popover, .close-popover, .close-picker',
handleClicks
);
defaults.modalContainer = defaults.modalContainer || document.body; //incase some one include js in head
});
})($);
/*======================================================
************ Calendar ************
======================================================*/
/* global $:true */
/*jshint unused: false*/
+(function ($) {
'use strict';
var rtl = false;
var defaults;
var Calendar = function (params) {
var p = this;
params = params || {};
for (var def in defaults) {
if (typeof params[def] === 'undefined') {
params[def] = defaults[def];
}
}
p.params = params;
p.initialized = false;
// Inline flag
p.inline = p.params.container ? true : false;
// Is horizontal
p.isH = p.params.direction === 'horizontal';
// RTL inverter
var inverter = p.isH ? (rtl ? -1 : 1) : 1;
// Animating flag
p.animating = false;
// Should be converted to popover
function isPopover() {
var toPopover = false;
if (!p.params.convertToPopover && !p.params.onlyInPopover)
return toPopover;
if (!p.inline && p.params.input) {
if (p.params.onlyInPopover) toPopover = true;
else {
if ($.device.ios) {
toPopover = $.device.ipad ? true : false;
} else {
if ($(window).width() >= 768) toPopover = true;
}
}
}
return toPopover;
}
function inPopover() {
if (
p.opened &&
p.container &&
p.container.length > 0 &&
p.container.parents('.popover').length > 0
)
return true;
else return false;
}
// Format date
function formatDate(date) {
date = new Date(date);
var year = date.getFullYear();
var month = date.getMonth();
var month1 = month + 1;
var day = date.getDate();
var weekDay = date.getDay();
return p.params.dateFormat
.replace(/yyyy/g, year)
.replace(/yy/g, (year + '').substring(2))
.replace(/mm/g, month1 < 10 ? '0' + month1 : month1)
.replace(/m/g, month1)
.replace(/MM/g, p.params.monthNames[month])
.replace(/M/g, p.params.monthNamesShort[month])
.replace(/dd/g, day < 10 ? '0' + day : day)
.replace(/d/g, day)
.replace(/DD/g, p.params.dayNames[weekDay])
.replace(/D/g, p.params.dayNamesShort[weekDay]);
}
// Value
p.addValue = function (value) {
if (p.params.multiple) {
if (!p.value) p.value = [];
var inValuesIndex;
for (var i = 0; i < p.value.length; i++) {
if (new Date(value).getTime() === new Date(p.value[i]).getTime()) {
inValuesIndex = i;
}
}
if (typeof inValuesIndex === 'undefined') {
p.value.push(value);
} else {
p.value.splice(inValuesIndex, 1);
}
p.updateValue();
} else {
p.value = [value];
p.updateValue();
}
};
p.setValue = function (arrValues) {
p.value = arrValues;
p.updateValue();
};
p.updateValue = function () {
p.wrapper
.find('.picker-calendar-day-selected')
.removeClass('picker-calendar-day-selected');
var i, inputValue;
for (i = 0; i < p.value.length; i++) {
var valueDate = new Date(p.value[i]);
p.wrapper
.find(
'.picker-calendar-day[data-date="' +
valueDate.getFullYear() +
'-' +
valueDate.getMonth() +
'-' +
valueDate.getDate() +
'"]'
)
.addClass('picker-calendar-day-selected');
}
if (p.params.onChange) {
p.params.onChange(p, p.value, p.value.map(formatDate));
}
if (p.input && p.input.length > 0) {
if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value);
else {
inputValue = [];
for (i = 0; i < p.value.length; i++) {
inputValue.push(formatDate(p.value[i]));
}
inputValue = inputValue.join(', ');
}
$(p.input).val(inputValue);
$(p.input).trigger('change');
}
};
// Columns Handlers
p.initCalendarEvents = function () {
var col;
var allowItemClick = true;
var isTouched,
isMoved,
touchStartX,
touchStartY,
touchCurrentX,
touchCurrentY,
touchStartTime,
touchEndTime,
startTranslate,
currentTranslate,
wrapperWidth,
wrapperHeight,
percentage,
touchesDiff,
isScrolling;
function handleTouchStart(e) {
if (isMoved || isTouched) return;
// e.preventDefault();
isTouched = true;
var position = $.getTouchPosition(e);
touchStartX = touchCurrentY = position.x;
touchStartY = touchCurrentY = position.y;
touchStartTime = new Date().getTime();
percentage = 0;
allowItemClick = true;
isScrolling = undefined;
startTranslate = currentTranslate = p.monthsTranslate;
}
function handleTouchMove(e) {
if (!isTouched) return;
var position = $.getTouchPosition(e);
touchCurrentX = position.x;
touchCurrentY = position.y;
if (typeof isScrolling === 'undefined') {
isScrolling = !!(
isScrolling ||
Math.abs(touchCurrentY - touchStartY) >
Math.abs(touchCurrentX - touchStartX)
);
}
if (p.isH && isScrolling) {
isTouched = false;
return;
}
e.preventDefault();
if (p.animating) {
isTouched = false;
return;
}
allowItemClick = false;
if (!isMoved) {
// First move
isMoved = true;
wrapperWidth = p.wrapper[0].offsetWidth;
wrapperHeight = p.wrapper[0].offsetHeight;
p.wrapper.transition(0);
}
e.preventDefault();
touchesDiff = p.isH
? touchCurrentX - touchStartX
: touchCurrentY - touchStartY;
percentage = touchesDiff / (p.isH ? wrapperWidth : wrapperHeight);
currentTranslate = (p.monthsTranslate * inverter + percentage) * 100;
// Transform wrapper
p.wrapper.transform(
'translate3d(' +
(p.isH ? currentTranslate : 0) +
'%, ' +
(p.isH ? 0 : currentTranslate) +
'%, 0)'
);
}
function handleTouchEnd(e) {
if (!isTouched || !isMoved) {
isTouched = isMoved = false;
return;
}
isTouched = isMoved = false;
touchEndTime = new Date().getTime();
if (touchEndTime - touchStartTime < 300) {
if (Math.abs(touchesDiff) < 10) {
p.resetMonth();
} else if (touchesDiff >= 10) {
if (rtl) p.nextMonth();
else p.prevMonth();
} else {
if (rtl) p.prevMonth();
else p.nextMonth();
}
} else {
if (percentage <= -0.5) {
if (rtl) p.prevMonth();
else p.nextMonth();
} else if (percentage >= 0.5) {
if (rtl) p.nextMonth();
else p.prevMonth();
} else {
p.resetMonth();
}
}
// Allow click
setTimeout(function () {
allowItemClick = true;
}, 100);
}
function handleDayClick(e) {
if (!allowItemClick) return;
var day = $(e.target).parents('.picker-calendar-day');
if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) {
day = $(e.target);
}
if (day.length === 0) return;
if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple)
return;
if (day.hasClass('picker-calendar-day-disabled')) return;
if (day.hasClass('picker-calendar-day-next')) p.nextMonth();
if (day.hasClass('picker-calendar-day-prev')) p.prevMonth();
var dateYear = day.attr('data-year');
var dateMonth = day.attr('data-month');
var dateDay = day.attr('data-day');
if (p.params.onDayClick) {
p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay);
}
p.addValue(new Date(dateYear, dateMonth, dateDay).getTime());
if (p.params.closeOnSelect) p.close();
}
p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth);
p.container.find('.picker-calendar-next-month').on('click', p.nextMonth);
p.container.find('.picker-calendar-prev-year').on('click', p.prevYear);
p.container.find('.picker-calendar-next-year').on('click', p.nextYear);
p.wrapper.on('click', handleDayClick);
if (p.params.touchMove) {
p.wrapper.on($.touchEvents.start, handleTouchStart);
p.wrapper.on($.touchEvents.move, handleTouchMove);
p.wrapper.on($.touchEvents.end, handleTouchEnd);
}
p.container[0].f7DestroyCalendarEvents = function () {
p.container
.find('.picker-calendar-prev-month')
.off('click', p.prevMonth);
p.container
.find('.picker-calendar-next-month')
.off('click', p.nextMonth);
p.container.find('.picker-calendar-prev-year').off('click', p.prevYear);
p.container.find('.picker-calendar-next-year').off('click', p.nextYear);
p.wrapper.off('click', handleDayClick);
if (p.params.touchMove) {
p.wrapper.off($.touchEvents.start, handleTouchStart);
p.wrapper.off($.touchEvents.move, handleTouchMove);
p.wrapper.off($.touchEvents.end, handleTouchEnd);
}
};
};
p.destroyCalendarEvents = function (colContainer) {
if ('f7DestroyCalendarEvents' in p.container[0])
p.container[0].f7DestroyCalendarEvents();
};
// Calendar Methods
p.daysInMonth = function (date) {
var d = new Date(date);
return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
};
p.monthHTML = function (date, offset) {
date = new Date(date);
var year = date.getFullYear(),
month = date.getMonth(),
day = date.getDate();
if (offset === 'next') {
if (month === 11) date = new Date(year + 1, 0);
else date = new Date(year, month + 1, 1);
}
if (offset === 'prev') {
if (month === 0) date = new Date(year - 1, 11);
else date = new Date(year, month - 1, 1);
}
if (offset === 'next' || offset === 'prev') {
month = date.getMonth();
year = date.getFullYear();
}
var daysInPrevMonth = p.daysInMonth(
new Date(date.getFullYear(), date.getMonth()).getTime() -
10 * 24 * 60 * 60 * 1000
),
daysInMonth = p.daysInMonth(date),
firstDayOfMonthIndex = new Date(
date.getFullYear(),
date.getMonth()
).getDay();
if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7;
var dayDate,
currentValues = [],
i,
j,
rows = 6,
cols = 7,
monthHTML = '',
dayIndex = 0 + (p.params.firstDay - 1),
today = new Date().setHours(0, 0, 0, 0),
minDate = p.params.minDate
? new Date(p.params.minDate).getTime()
: null,
maxDate = p.params.maxDate
? new Date(p.params.maxDate).getTime()
: null;
if (p.value && p.value.length) {
for (i = 0; i < p.value.length; i++) {
currentValues.push(new Date(p.value[i]).setHours(0, 0, 0, 0));
}
}
for (i = 1; i <= rows; i++) {
var rowHTML = '';
var row = i;
for (j = 1; j <= cols; j++) {
var col = j;
dayIndex++;
var dayNumber = dayIndex - firstDayOfMonthIndex;
var addClass = '';
if (dayNumber < 0) {
dayNumber = daysInPrevMonth + dayNumber + 1;
addClass += ' picker-calendar-day-prev';
dayDate = new Date(
month - 1 < 0 ? year - 1 : year,
month - 1 < 0 ? 11 : month - 1,
dayNumber
).getTime();
} else {
dayNumber = dayNumber + 1;
if (dayNumber > daysInMonth) {
dayNumber = dayNumber - daysInMonth;
addClass += ' picker-calendar-day-next';
dayDate = new Date(
month + 1 > 11 ? year + 1 : year,
month + 1 > 11 ? 0 : month + 1,
dayNumber
).getTime();
} else {
dayDate = new Date(year, month, dayNumber).getTime();
}
}
// Today
if (dayDate === today) addClass += ' picker-calendar-day-today';
// Selected
if (currentValues.indexOf(dayDate) >= 0)
addClass += ' picker-calendar-day-selected';
// Weekend
if (p.params.weekendDays.indexOf(col - 1) >= 0) {
addClass += ' picker-calendar-day-weekend';
}
// Disabled
if (
(minDate && dayDate < minDate) ||
(maxDate && dayDate > maxDate)
) {
addClass += ' picker-calendar-day-disabled';
}
dayDate = new Date(dayDate);
var dayYear = dayDate.getFullYear();
var dayMonth = dayDate.getMonth();
rowHTML +=
'' +
dayNumber +
'
';
}
monthHTML += '' + rowHTML + '
';
}
monthHTML =
'' +
monthHTML +
'
';
return monthHTML;
};
p.animating = false;
p.updateCurrentMonthYear = function (dir) {
if (typeof dir === 'undefined') {
p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10);
p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10);
} else {
p.currentMonth = parseInt(
p.months
.eq(dir === 'next' ? p.months.length - 1 : 0)
.attr('data-month'),
10
);
p.currentYear = parseInt(
p.months
.eq(dir === 'next' ? p.months.length - 1 : 0)
.attr('data-year'),
10
);
}
p.container
.find('.current-month-value')
.text(p.params.monthNames[p.currentMonth]);
p.container.find('.current-year-value').text(p.currentYear);
};
p.onMonthChangeStart = function (dir) {
p.updateCurrentMonthYear(dir);
p.months.removeClass(
'picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'
);
var currentIndex = dir === 'next' ? p.months.length - 1 : 0;
p.months.eq(currentIndex).addClass('picker-calendar-month-current');
p.months
.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1)
.addClass(
dir === 'next'
? 'picker-calendar-month-prev'
: 'picker-calendar-month-next'
);
if (p.params.onMonthYearChangeStart) {
p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth);
}
};
p.onMonthChangeEnd = function (dir, rebuildBoth) {
p.animating = false;
var nextMonthHTML, prevMonthHTML, newMonthHTML;
p.wrapper
.find(
'.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)'
)
.remove();
if (typeof dir === 'undefined') {
dir = 'next';
rebuildBoth = true;
}
if (!rebuildBoth) {
newMonthHTML = p.monthHTML(
new Date(p.currentYear, p.currentMonth),
dir
);
} else {
p.wrapper
.find('.picker-calendar-month-next, .picker-calendar-month-prev')
.remove();
prevMonthHTML = p.monthHTML(
new Date(p.currentYear, p.currentMonth),
'prev'
);
nextMonthHTML = p.monthHTML(
new Date(p.currentYear, p.currentMonth),
'next'
);
}
if (dir === 'next' || rebuildBoth) {
p.wrapper.append(newMonthHTML || nextMonthHTML);
}
if (dir === 'prev' || rebuildBoth) {
p.wrapper.prepend(newMonthHTML || prevMonthHTML);
}
p.months = p.wrapper.find('.picker-calendar-month');
p.setMonthsTranslate(p.monthsTranslate);
if (p.params.onMonthAdd) {
p.params.onMonthAdd(
p,
dir === 'next'
? p.months.eq(p.months.length - 1)[0]
: p.months.eq(0)[0]
);
}
if (p.params.onMonthYearChangeEnd) {
p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth);
}
};
p.setMonthsTranslate = function (translate) {
translate = translate || p.monthsTranslate || 0;
if (typeof p.monthsTranslate === 'undefined')
p.monthsTranslate = translate;
p.months.removeClass(
'picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'
);
var prevMonthTranslate = -(translate + 1) * 100 * inverter;
var currentMonthTranslate = -translate * 100 * inverter;
var nextMonthTranslate = -(translate - 1) * 100 * inverter;
p.months
.eq(0)
.transform(
'translate3d(' +
(p.isH ? prevMonthTranslate : 0) +
'%, ' +
(p.isH ? 0 : prevMonthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-prev');
p.months
.eq(1)
.transform(
'translate3d(' +
(p.isH ? currentMonthTranslate : 0) +
'%, ' +
(p.isH ? 0 : currentMonthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-current');
p.months
.eq(2)
.transform(
'translate3d(' +
(p.isH ? nextMonthTranslate : 0) +
'%, ' +
(p.isH ? 0 : nextMonthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-next');
};
p.nextMonth = function (transition) {
if (typeof transition === 'undefined' || typeof transition === 'object') {
transition = '';
if (!p.params.animate) transition = 0;
}
var nextMonth = parseInt(
p.months.eq(p.months.length - 1).attr('data-month'),
10
);
var nextYear = parseInt(
p.months.eq(p.months.length - 1).attr('data-year'),
10
);
var nextDate = new Date(nextYear, nextMonth);
var nextDateTime = nextDate.getTime();
var transitionEndCallback = p.animating ? false : true;
if (p.params.maxDate) {
if (nextDateTime > new Date(p.params.maxDate).getTime()) {
return p.resetMonth();
}
}
p.monthsTranslate--;
if (nextMonth === p.currentMonth) {
var nextMonthTranslate = -p.monthsTranslate * 100 * inverter;
var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next'))
.transform(
'translate3d(' +
(p.isH ? nextMonthTranslate : 0) +
'%, ' +
(p.isH ? 0 : nextMonthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-next');
p.wrapper.append(nextMonthHTML[0]);
p.months = p.wrapper.find('.picker-calendar-month');
if (p.params.onMonthAdd) {
p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]);
}
}
p.animating = true;
p.onMonthChangeStart('next');
var translate = p.monthsTranslate * 100 * inverter;
p.wrapper
.transition(transition)
.transform(
'translate3d(' +
(p.isH ? translate : 0) +
'%, ' +
(p.isH ? 0 : translate) +
'%, 0)'
);
if (transitionEndCallback) {
p.wrapper.transitionEnd(function () {
p.onMonthChangeEnd('next');
});
}
if (!p.params.animate) {
p.onMonthChangeEnd('next');
}
};
p.prevMonth = function (transition) {
if (typeof transition === 'undefined' || typeof transition === 'object') {
transition = '';
if (!p.params.animate) transition = 0;
}
var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10);
var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10);
var prevDate = new Date(prevYear, prevMonth + 1, -1);
var prevDateTime = prevDate.getTime();
var transitionEndCallback = p.animating ? false : true;
if (p.params.minDate) {
if (prevDateTime < new Date(p.params.minDate).getTime()) {
return p.resetMonth();
}
}
p.monthsTranslate++;
if (prevMonth === p.currentMonth) {
var prevMonthTranslate = -p.monthsTranslate * 100 * inverter;
var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev'))
.transform(
'translate3d(' +
(p.isH ? prevMonthTranslate : 0) +
'%, ' +
(p.isH ? 0 : prevMonthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-prev');
p.wrapper.prepend(prevMonthHTML[0]);
p.months = p.wrapper.find('.picker-calendar-month');
if (p.params.onMonthAdd) {
p.params.onMonthAdd(p, p.months.eq(0)[0]);
}
}
p.animating = true;
p.onMonthChangeStart('prev');
var translate = p.monthsTranslate * 100 * inverter;
p.wrapper
.transition(transition)
.transform(
'translate3d(' +
(p.isH ? translate : 0) +
'%, ' +
(p.isH ? 0 : translate) +
'%, 0)'
);
if (transitionEndCallback) {
p.wrapper.transitionEnd(function () {
p.onMonthChangeEnd('prev');
});
}
if (!p.params.animate) {
p.onMonthChangeEnd('prev');
}
};
p.resetMonth = function (transition) {
if (typeof transition === 'undefined') transition = '';
var translate = p.monthsTranslate * 100 * inverter;
p.wrapper
.transition(transition)
.transform(
'translate3d(' +
(p.isH ? translate : 0) +
'%, ' +
(p.isH ? 0 : translate) +
'%, 0)'
);
};
p.setYearMonth = function (year, month, transition) {
if (typeof year === 'undefined') year = p.currentYear;
if (typeof month === 'undefined') month = p.currentMonth;
if (typeof transition === 'undefined' || typeof transition === 'object') {
transition = '';
if (!p.params.animate) transition = 0;
}
var targetDate;
if (year < p.currentYear) {
targetDate = new Date(year, month + 1, -1).getTime();
} else {
targetDate = new Date(year, month).getTime();
}
if (
p.params.maxDate &&
targetDate > new Date(p.params.maxDate).getTime()
) {
return false;
}
if (
p.params.minDate &&
targetDate < new Date(p.params.minDate).getTime()
) {
return false;
}
var currentDate = new Date(p.currentYear, p.currentMonth).getTime();
var dir = targetDate > currentDate ? 'next' : 'prev';
var newMonthHTML = p.monthHTML(new Date(year, month));
p.monthsTranslate = p.monthsTranslate || 0;
var prevTranslate = p.monthsTranslate;
var monthTranslate, wrapperTranslate;
var transitionEndCallback = p.animating ? false : true;
if (targetDate > currentDate) {
// To next
p.monthsTranslate--;
if (!p.animating) p.months.eq(p.months.length - 1).remove();
p.wrapper.append(newMonthHTML);
p.months = p.wrapper.find('.picker-calendar-month');
monthTranslate = -(prevTranslate - 1) * 100 * inverter;
p.months
.eq(p.months.length - 1)
.transform(
'translate3d(' +
(p.isH ? monthTranslate : 0) +
'%, ' +
(p.isH ? 0 : monthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-next');
} else {
// To prev
p.monthsTranslate++;
if (!p.animating) p.months.eq(0).remove();
p.wrapper.prepend(newMonthHTML);
p.months = p.wrapper.find('.picker-calendar-month');
monthTranslate = -(prevTranslate + 1) * 100 * inverter;
p.months
.eq(0)
.transform(
'translate3d(' +
(p.isH ? monthTranslate : 0) +
'%, ' +
(p.isH ? 0 : monthTranslate) +
'%, 0)'
)
.addClass('picker-calendar-month-prev');
}
if (p.params.onMonthAdd) {
p.params.onMonthAdd(
p,
dir === 'next'
? p.months.eq(p.months.length - 1)[0]
: p.months.eq(0)[0]
);
}
p.animating = true;
p.onMonthChangeStart(dir);
wrapperTranslate = p.monthsTranslate * 100 * inverter;
p.wrapper
.transition(transition)
.transform(
'translate3d(' +
(p.isH ? wrapperTranslate : 0) +
'%, ' +
(p.isH ? 0 : wrapperTranslate) +
'%, 0)'
);
if (transitionEndCallback) {
p.wrapper.transitionEnd(function () {
p.onMonthChangeEnd(dir, true);
});
}
if (!p.params.animate) {
p.onMonthChangeEnd(dir);
}
};
p.nextYear = function () {
p.setYearMonth(p.currentYear + 1);
};
p.prevYear = function () {
p.setYearMonth(p.currentYear - 1);
};
// HTML Layout
p.layout = function () {
var pickerHTML = '';
var pickerClass = '';
var i;
var layoutDate =
p.value && p.value.length
? p.value[0]
: new Date().setHours(0, 0, 0, 0);
var prevMonthHTML = p.monthHTML(layoutDate, 'prev');
var currentMonthHTML = p.monthHTML(layoutDate);
var nextMonthHTML = p.monthHTML(layoutDate, 'next');
var monthsHTML =
'' +
(prevMonthHTML + currentMonthHTML + nextMonthHTML) +
'
';
// Week days header
var weekHeaderHTML = '';
if (p.params.weekHeader) {
for (i = 0; i < 7; i++) {
var weekDayIndex =
i + p.params.firstDay > 6
? i - 7 + p.params.firstDay
: i + p.params.firstDay;
var dayName = p.params.dayNamesShort[weekDayIndex];
weekHeaderHTML +=
' ' +
dayName +
'
';
}
weekHeaderHTML =
'' + weekHeaderHTML + '
';
}
pickerClass = 'picker-modal picker-calendar ' + (p.params.cssClass || '');
var toolbarHTML = p.params.toolbar
? p.params.toolbarTemplate.replace(
/{{closeText}}/g,
p.params.toolbarCloseText
)
: '';
if (p.params.toolbar) {
toolbarHTML = p.params.toolbarTemplate
.replace(/{{closeText}}/g, p.params.toolbarCloseText)
.replace(
/{{monthPicker}}/g,
p.params.monthPicker ? p.params.monthPickerTemplate : ''
)
.replace(
/{{yearPicker}}/g,
p.params.yearPicker ? p.params.yearPickerTemplate : ''
);
}
pickerHTML =
'' +
toolbarHTML +
'
' +
weekHeaderHTML +
monthsHTML +
'
' +
'
';
p.pickerHTML = pickerHTML;
};
// Input Events
function openOnInput(e) {
e.preventDefault();
if (p.opened) return;
p.open();
if (p.params.scrollToInput && !isPopover()) {
var pageContent = p.input.parents('.page-content');
if (pageContent.length === 0) return;
var paddingTop = parseInt(pageContent.css('padding-top'), 10),
paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
pageHeight =
pageContent[0].offsetHeight - paddingTop - p.container.height(),
pageScrollHeight =
pageContent[0].scrollHeight - paddingTop - p.container.height(),
newPaddingBottom;
var inputTop =
p.input.offset().top - paddingTop + p.input[0].offsetHeight;
if (inputTop > pageHeight) {
var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
if (scrollTop + pageHeight > pageScrollHeight) {
newPaddingBottom =
scrollTop + pageHeight - pageScrollHeight + paddingBottom;
if (pageHeight === pageScrollHeight) {
newPaddingBottom = p.container.height();
}
pageContent.css({ 'padding-bottom': newPaddingBottom + 'px' });
}
pageContent.scrollTop(scrollTop, 300);
}
}
}
function closeOnHTMLClick(e) {
if (inPopover()) return;
if (p.input && p.input.length > 0) {
if (
e.target !== p.input[0] &&
$(e.target).parents('.picker-modal').length === 0
)
p.close();
} else {
if ($(e.target).parents('.picker-modal').length === 0) p.close();
}
}
if (p.params.input) {
p.input = $(p.params.input);
if (p.input.length > 0) {
if (p.params.inputReadOnly) p.input.prop('readOnly', true);
if (!p.inline) {
p.input.on('click', function (e) {
openOnInput(e);
//修复部分安卓系统下,即使设置了readonly依然会弹出系统键盘的bug
if (p.params.inputReadOnly) {
this.focus();
this.blur();
}
});
}
if (p.params.inputReadOnly) {
p.input.on('focus mousedown', function (e) {
e.preventDefault();
});
}
}
}
if (!p.inline) $('html').on('click', closeOnHTMLClick);
// Open
function onPickerClose() {
p.opened = false;
if (p.input && p.input.length > 0)
p.input.parents('.page-content').css({ 'padding-bottom': '' });
if (p.params.onClose) p.params.onClose(p);
// Destroy events
p.destroyCalendarEvents();
}
p.opened = false;
p.open = function () {
if (p.params.beforeOpen) {
p.params.beforeOpen(p);
}
var toPopover = isPopover();
var updateValue = false;
if (!p.opened) {
// Set date value
if (!p.value) {
if (p.params.value) {
p.value = p.params.value;
updateValue = true;
}
}
// Layout
p.layout();
// Append
if (toPopover) {
p.pickerHTML =
'';
p.popover = $.popover(p.pickerHTML, p.params.input, true);
p.container = $(p.popover).find('.picker-modal');
$(p.popover).on('close', function () {
onPickerClose();
});
} else if (p.inline) {
p.container = $(p.pickerHTML);
p.container.addClass('picker-modal-inline');
$(p.params.container).append(p.container);
} else {
p.container = $($.pickerModal(p.pickerHTML));
$(p.container).on('close', function () {
onPickerClose();
});
}
// Store calendar instance
p.container[0].f7Calendar = p;
p.wrapper = p.container.find('.picker-calendar-months-wrapper');
// Months
p.months = p.wrapper.find('.picker-calendar-month');
// Update current month and year
p.updateCurrentMonthYear();
// Set initial translate
p.monthsTranslate = 0;
p.setMonthsTranslate();
// Init events
p.initCalendarEvents();
// Update input value
if (updateValue) p.updateValue();
}
// Set flag
p.opened = true;
p.initialized = true;
if (p.params.onMonthAdd) {
p.months.each(function () {
p.params.onMonthAdd(p, this);
});
}
if (p.params.onOpen) p.params.onOpen(p);
};
// Close
p.close = function () {
if (!p.opened || p.inline) return;
if (inPopover()) {
$.closeModal(p.popover);
return;
} else {
$.closeModal(p.container);
return;
}
};
// Destroy
p.destroy = function () {
p.close();
if (p.params.input && p.input.length > 0) {
p.input.off('click focus', openOnInput);
}
$('html').off('click', closeOnHTMLClick);
};
if (p.inline) {
p.open();
}
return p;
};
$.fn.calendar = function (params) {
return this.each(function () {
var $this = $(this);
if (!$this[0]) return;
var calendar = $this.data('calendar');
if (!calendar) {
var p = {};
if ($this[0].tagName.toUpperCase() === 'INPUT') {
p.input = $this;
} else {
p.container = $this;
}
$this.data('calendar', new Calendar($.extend(p, params)));
}
});
};
defaults = $.fn.calendar.prototype.defaults = {
monthNames: [
'一月',
'二月',
'三月',
'四月',
'五月',
'六月',
'七月',
'八月',
'九月',
'十月',
'十一月',
'十二月',
],
monthNamesShort: [
'一月',
'二月',
'三月',
'四月',
'五月',
'六月',
'七月',
'八月',
'九月',
'十月',
'十一月',
'十二月',
],
dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
firstDay: 1, // First day of the week, Monday
weekendDays: [0, 6], // Sunday and Saturday
multiple: false,
dateFormat: 'yyyy-mm-dd',
direction: 'horizontal', // or 'vertical'
minDate: null,
maxDate: null,
touchMove: true,
animate: true,
closeOnSelect: true,
monthPicker: true,
monthPickerTemplate:
'',
yearPicker: true,
yearPickerTemplate:
'',
weekHeader: true,
// Common settings
scrollToInput: true,
inputReadOnly: true,
convertToPopover: true,
onlyInPopover: false,
toolbar: true,
toolbarCloseText: 'Done',
toolbarTemplate:
'',
/* Callbacks
onMonthAdd
onChange
onOpen
onClose
onDayClick
onMonthYearChangeStart
onMonthYearChangeEnd
*/
};
$.initCalendar = function (content) {
var $content = content ? $(content) : $(document.body);
$content.find("[data-toggle='date']").each(function () {
$(this).calendar();
});
};
})($);
/*======================================================
************ Picker ************
======================================================*/
/* global $:true */
/* jshint unused:false */
/* jshint multistr:true */
+(function ($) {
'use strict';
var Picker = function (params) {
var p = this;
var defaults = {
updateValuesOnMomentum: false,
updateValuesOnTouchmove: true,
rotateEffect: false,
momentumRatio: 7,
freeMode: false,
// Common settings
scrollToInput: true,
inputReadOnly: true,
convertToPopover: true,
onlyInPopover: false,
toolbar: true,
toolbarCloseText: 'OK',
toolbarTemplate:
'',
};
params = params || {};
for (var def in defaults) {
if (typeof params[def] === 'undefined') {
params[def] = defaults[def];
}
}
p.params = params;
p.cols = [];
p.initialized = false;
// Inline flag
p.inline = p.params.container ? true : false;
// 3D Transforms origin bug, only on safari
var originBug =
$.device.ios ||
(navigator.userAgent.toLowerCase().indexOf('safari') >= 0 &&
navigator.userAgent.toLowerCase().indexOf('chrome') < 0 &&
!$.device.android);
// Should be converted to popover
function isPopover() {
var toPopover = false;
if (!p.params.convertToPopover && !p.params.onlyInPopover)
return toPopover;
if (!p.inline && p.params.input) {
if (p.params.onlyInPopover) toPopover = true;
else {
if ($.device.ios) {
toPopover = $.device.ipad ? true : false;
} else {
if ($(window).width() >= 768) toPopover = true;
}
}
}
return toPopover;
}
function inPopover() {
if (
p.opened &&
p.container &&
p.container.length > 0 &&
p.container.parents('.popover').length > 0
)
return true;
else return false;
}
// Value
p.setValue = function (arrValues, transition) {
var valueIndex = 0;
for (var i = 0; i < p.cols.length; i++) {
if (p.cols[i] && !p.cols[i].divider) {
p.cols[i].setValue(arrValues[valueIndex], transition);
valueIndex++;
}
}
};
p.updateValue = function () {
var newValue = [];
var newDisplayValue = [];
for (var i = 0; i < p.cols.length; i++) {
if (!p.cols[i].divider) {
newValue.push(p.cols[i].value);
newDisplayValue.push(p.cols[i].displayValue);
}
}
if (newValue.indexOf(undefined) >= 0) {
return;
}
p.value = newValue;
p.displayValue = newDisplayValue;
if (p.params.onChange) {
p.params.onChange(p, p.value, p.displayValue);
}
if (p.input && p.input.length > 0) {
$(p.input).val(
p.params.formatValue
? p.params.formatValue(p, p.value, p.displayValue)
: p.value.join(' ')
);
$(p.input).trigger('change');
}
};
// Columns Handlers
p.initPickerCol = function (colElement, updateItems) {
var colContainer = $(colElement);
var colIndex = colContainer.index();
var col = p.cols[colIndex];
if (col.divider) return;
col.container = colContainer;
col.wrapper = col.container.find('.picker-items-col-wrapper');
col.items = col.wrapper.find('.picker-item');
var i, j;
var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate;
col.replaceValues = function (values, displayValues) {
col.destroyEvents();
col.values = values;
col.displayValues = displayValues;
var newItemsHTML = p.columnHTML(col, true);
col.wrapper.html(newItemsHTML);
col.items = col.wrapper.find('.picker-item');
col.calcSize();
col.setValue(col.values[0], 0, true);
col.initEvents();
};
col.calcSize = function () {
if (p.params.rotateEffect) {
col.container.removeClass('picker-items-col-absolute');
if (!col.width) col.container.css({ width: '' });
}
var colWidth, colHeight;
colWidth = 0;
colHeight = col.container[0].offsetHeight;
wrapperHeight = col.wrapper[0].offsetHeight;
itemHeight = col.items[0].offsetHeight;
itemsHeight = itemHeight * col.items.length;
minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2;
maxTranslate = colHeight / 2 - itemHeight / 2;
if (col.width) {
colWidth = col.width;
if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px';
col.container.css({ width: colWidth });
}
if (p.params.rotateEffect) {
if (!col.width) {
col.items.each(function () {
var item = $(this);
item.css({ width: 'auto' });
colWidth = Math.max(colWidth, item[0].offsetWidth);
item.css({ width: '' });
});
col.container.css({ width: colWidth + 2 + 'px' });
}
col.container.addClass('picker-items-col-absolute');
}
};
col.calcSize();
col.wrapper
.transform('translate3d(0,' + maxTranslate + 'px,0)')
.transition(0);
var activeIndex = 0;
var animationFrameId;
// Set Value Function
col.setValue = function (newValue, transition, valueCallbacks) {
if (typeof transition === 'undefined') transition = '';
var newActiveIndex = col.wrapper
.find('.picker-item[data-picker-value="' + newValue + '"]')
.index();
if (typeof newActiveIndex === 'undefined' || newActiveIndex === -1) {
return;
}
var newTranslate = -newActiveIndex * itemHeight + maxTranslate;
// Update wrapper
col.wrapper.transition(transition);
col.wrapper.transform('translate3d(0,' + newTranslate + 'px,0)');
// Watch items
if (
p.params.updateValuesOnMomentum &&
col.activeIndex &&
col.activeIndex !== newActiveIndex
) {
$.cancelAnimationFrame(animationFrameId);
col.wrapper.transitionEnd(function () {
$.cancelAnimationFrame(animationFrameId);
});
updateDuringScroll();
}
// Update items
col.updateItems(
newActiveIndex,
newTranslate,
transition,
valueCallbacks
);
};
col.updateItems = function (
activeIndex,
translate,
transition,
valueCallbacks
) {
if (typeof translate === 'undefined') {
translate = $.getTranslate(col.wrapper[0], 'y');
}
if (typeof activeIndex === 'undefined')
activeIndex = -Math.round((translate - maxTranslate) / itemHeight);
if (activeIndex < 0) activeIndex = 0;
if (activeIndex >= col.items.length) activeIndex = col.items.length - 1;
var previousActiveIndex = col.activeIndex;
col.activeIndex = activeIndex;
/*
col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected');
col.items.transition(transition);
var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
var prevItems = selectedItem.prevAll().addClass('picker-before-selected');
var nextItems = selectedItem.nextAll().addClass('picker-after-selected');
*/
//去掉 .picker-after-selected, .picker-before-selected 以提高性能
col.wrapper.find('.picker-selected').removeClass('picker-selected');
if (p.params.rotateEffect) {
col.items.transition(transition);
}
var selectedItem = col.items
.eq(activeIndex)
.addClass('picker-selected')
.transform('');
if (valueCallbacks || typeof valueCallbacks === 'undefined') {
// Update values
col.value = selectedItem.attr('data-picker-value');
col.displayValue = col.displayValues
? col.displayValues[activeIndex]
: col.value;
// On change callback
if (previousActiveIndex !== activeIndex) {
if (col.onChange) {
col.onChange(p, col.value, col.displayValue);
}
p.updateValue();
}
}
// Set 3D rotate effect
if (!p.params.rotateEffect) {
return;
}
var percentage =
(translate -
(Math.floor((translate - maxTranslate) / itemHeight) * itemHeight +
maxTranslate)) /
itemHeight;
col.items.each(function () {
var item = $(this);
var itemOffsetTop = item.index() * itemHeight;
var translateOffset = maxTranslate - translate;
var itemOffset = itemOffsetTop - translateOffset;
var percentage = itemOffset / itemHeight;
var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1;
var angle = -18 * percentage;
if (angle > 180) angle = 180;
if (angle < -180) angle = -180;
// Far class
if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far');
else item.removeClass('picker-item-far');
// Set transform
item.transform(
'translate3d(0, ' +
(-translate + maxTranslate) +
'px, ' +
(originBug ? -110 : 0) +
'px) rotateX(' +
angle +
'deg)'
);
});
};
function updateDuringScroll() {
animationFrameId = $.requestAnimationFrame(function () {
col.updateItems(undefined, undefined, 0);
updateDuringScroll();
});
}
// Update items on init
if (updateItems) col.updateItems(0, maxTranslate, 0);
var allowItemClick = true;
var isTouched,
isMoved,
touchStartY,
touchCurrentY,
touchStartTime,
touchEndTime,
startTranslate,
returnTo,
currentTranslate,
prevTranslate,
velocityTranslate,
velocityTime;
function handleTouchStart(e) {
if (isMoved || isTouched) return;
e.preventDefault();
isTouched = true;
var position = $.getTouchPosition(e);
touchStartY = touchCurrentY = position.y;
touchStartTime = new Date().getTime();
allowItemClick = true;
startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
}
function handleTouchMove(e) {
if (!isTouched) return;
e.preventDefault();
allowItemClick = false;
var position = $.getTouchPosition(e);
touchCurrentY = position.y;
if (!isMoved) {
// First move
$.cancelAnimationFrame(animationFrameId);
isMoved = true;
startTranslate = currentTranslate = $.getTranslate(
col.wrapper[0],
'y'
);
col.wrapper.transition(0);
}
e.preventDefault();
var diff = touchCurrentY - touchStartY;
currentTranslate = startTranslate + diff;
returnTo = undefined;
// Normalize translate
if (currentTranslate < minTranslate) {
currentTranslate =
minTranslate - Math.pow(minTranslate - currentTranslate, 0.8);
returnTo = 'min';
}
if (currentTranslate > maxTranslate) {
currentTranslate =
maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8);
returnTo = 'max';
}
// Transform wrapper
col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)');
// Update items
col.updateItems(
undefined,
currentTranslate,
0,
p.params.updateValuesOnTouchmove
);
// Calc velocity
velocityTranslate =
currentTranslate - prevTranslate || currentTranslate;
velocityTime = new Date().getTime();
prevTranslate = currentTranslate;
}
function handleTouchEnd(e) {
if (!isTouched || !isMoved) {
isTouched = isMoved = false;
return;
}
isTouched = isMoved = false;
col.wrapper.transition('');
if (returnTo) {
if (returnTo === 'min') {
col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)');
} else
col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)');
}
touchEndTime = new Date().getTime();
var velocity, newTranslate;
if (touchEndTime - touchStartTime > 300) {
newTranslate = currentTranslate;
} else {
velocity = Math.abs(
velocityTranslate / (touchEndTime - velocityTime)
);
newTranslate =
currentTranslate + velocityTranslate * p.params.momentumRatio;
}
newTranslate = Math.max(
Math.min(newTranslate, maxTranslate),
minTranslate
);
// Active Index
var activeIndex = -Math.floor(
(newTranslate - maxTranslate) / itemHeight
);
// Normalize translate
if (!p.params.freeMode)
newTranslate = -activeIndex * itemHeight + maxTranslate;
// Transform wrapper
col.wrapper.transform(
'translate3d(0,' + parseInt(newTranslate, 10) + 'px,0)'
);
// Update items
col.updateItems(activeIndex, newTranslate, '', true);
// Watch items
if (p.params.updateValuesOnMomentum) {
updateDuringScroll();
col.wrapper.transitionEnd(function () {
$.cancelAnimationFrame(animationFrameId);
});
}
// Allow click
setTimeout(function () {
allowItemClick = true;
}, 100);
}
function handleClick(e) {
if (!allowItemClick) return;
$.cancelAnimationFrame(animationFrameId);
/*jshint validthis:true */
var value = $(this).attr('data-picker-value');
col.setValue(value);
}
col.initEvents = function (detach) {
var method = detach ? 'off' : 'on';
col.container[method]($.touchEvents.start, handleTouchStart);
col.container[method]($.touchEvents.move, handleTouchMove);
col.container[method]($.touchEvents.end, handleTouchEnd);
col.items[method]('click', handleClick);
};
col.destroyEvents = function () {
col.initEvents(true);
};
col.container[0].f7DestroyPickerCol = function () {
col.destroyEvents();
};
col.initEvents();
};
p.destroyPickerCol = function (colContainer) {
colContainer = $(colContainer);
if ('f7DestroyPickerCol' in colContainer[0])
colContainer[0].f7DestroyPickerCol();
};
// Resize cols
function resizeCols() {
if (!p.opened) return;
for (var i = 0; i < p.cols.length; i++) {
if (!p.cols[i].divider) {
p.cols[i].calcSize();
p.cols[i].setValue(p.cols[i].value, 0, false);
}
}
}
$(window).on('resize', resizeCols);
// HTML Layout
p.columnHTML = function (col, onlyItems) {
var columnItemsHTML = '';
var columnHTML = '';
if (col.divider) {
columnHTML +=
'' +
col.content +
'
';
} else {
for (var j = 0; j < col.values.length; j++) {
columnItemsHTML +=
'' +
(col.displayValues ? col.displayValues[j] : col.values[j]) +
'
';
}
columnHTML +=
'';
}
return onlyItems ? columnItemsHTML : columnHTML;
};
p.layout = function () {
var pickerHTML = '';
var pickerClass = '';
var i;
p.cols = [];
var colsHTML = '';
for (i = 0; i < p.params.cols.length; i++) {
var col = p.params.cols[i];
colsHTML += p.columnHTML(p.params.cols[i]);
p.cols.push(col);
}
pickerClass =
'picker-modal picker-columns ' +
(p.params.cssClass || '') +
(p.params.rotateEffect ? ' picker-3d' : '');
pickerHTML =
'' +
(p.params.toolbar
? p.params.toolbarTemplate.replace(
/{{closeText}}/g,
p.params.toolbarCloseText
)
: '') +
'
' +
'
';
p.pickerHTML = pickerHTML;
};
// Input Events
function openOnInput(e) {
e.preventDefault();
if (p.opened) return;
p.open();
if (p.params.scrollToInput && !isPopover()) {
var pageContent = p.input.parents('.content');
if (pageContent.length === 0) return;
var paddingTop = parseInt(pageContent.css('padding-top'), 10),
paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
pageHeight =
pageContent[0].offsetHeight - paddingTop - p.container.height(),
pageScrollHeight =
pageContent[0].scrollHeight - paddingTop - p.container.height(),
newPaddingBottom;
var inputTop =
p.input.offset().top - paddingTop + p.input[0].offsetHeight;
if (inputTop > pageHeight) {
var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
if (scrollTop + pageHeight > pageScrollHeight) {
newPaddingBottom =
scrollTop + pageHeight - pageScrollHeight + paddingBottom;
if (pageHeight === pageScrollHeight) {
newPaddingBottom = p.container.height();
}
pageContent.css({ 'padding-bottom': newPaddingBottom + 'px' });
}
pageContent.scrollTop(scrollTop, 300);
}
}
}
function closeOnHTMLClick(e) {
if (inPopover()) return;
if (p.input && p.input.length > 0) {
if (
e.target !== p.input[0] &&
$(e.target).parents('.picker-modal').length === 0
)
p.close();
} else {
if ($(e.target).parents('.picker-modal').length === 0) p.close();
}
}
if (p.params.input) {
p.input = $(p.params.input);
if (p.input.length > 0) {
if (p.params.inputReadOnly) p.input.prop('readOnly', true);
if (!p.inline) {
p.input.on('click', function (e) {
openOnInput(e);
//修复部分安卓系统下,即使设置了readonly依然会弹出系统键盘的bug
if (p.params.inputReadOnly) {
this.focus();
this.blur();
}
});
}
if (p.params.inputReadOnly) {
p.input.on('focus mousedown', function (e) {
e.preventDefault();
});
}
}
}
if (!p.inline) $('html').on('click', closeOnHTMLClick);
// Open
function onPickerClose() {
p.opened = false;
if (p.input && p.input.length > 0)
p.input.parents('.page-content').css({ 'padding-bottom': '' });
if (p.params.onClose) p.params.onClose(p);
// Destroy events
p.container.find('.picker-items-col').each(function () {
p.destroyPickerCol(this);
});
}
p.opened = false;
p.open = function () {
if (p.params.beforeOpen) {
p.params.beforeOpen(p);
}
var toPopover = isPopover();
console.log('open');
if (!p.opened) {
// Layout
p.layout();
// Append
if (toPopover) {
p.pickerHTML =
'';
p.popover = $.popover(p.pickerHTML, p.params.input, true);
p.container = $(p.popover).find('.picker-modal');
$(p.popover).on('close', function () {
onPickerClose();
});
} else if (p.inline) {
p.container = $(p.pickerHTML);
p.container.addClass('picker-modal-inline');
$(p.params.container).append(p.container);
} else {
p.container = $($.pickerModal(p.pickerHTML));
$(p.container).on('close', function () {
onPickerClose();
});
}
// Store picker instance
p.container[0].f7Picker = p;
// Init Events
p.container.find('.picker-items-col').each(function () {
var updateItems = true;
if ((!p.initialized && p.params.value) || (p.initialized && p.value))
updateItems = false;
p.initPickerCol(this, updateItems);
});
// Set value
if (!p.initialized) {
if (p.params.value) {
p.setValue(p.params.value, 0);
}
} else {
if (p.value) p.setValue(p.value, 0);
}
}
// Set flag
p.opened = true;
p.initialized = true;
if (p.params.onOpen) p.params.onOpen(p);
};
// Close
p.close = function () {
if (!p.opened || p.inline) return;
if (inPopover()) {
$.closeModal(p.popover);
return;
} else {
$.closeModal(p.container);
return;
}
};
// Destroy
p.destroy = function () {
p.close();
if (p.params.input && p.input.length > 0) {
p.input.off('click focus', openOnInput);
}
$('html').off('click', closeOnHTMLClick);
$(window).off('resize', resizeCols);
};
if (p.inline) {
p.open();
}
return p;
};
$(document).on('click', '.close-picker', function () {
var pickerToClose = $('.picker-modal.modal-in');
if (pickerToClose.length > 0) {
$.closeModal(pickerToClose);
} else {
pickerToClose = $('.popover.modal-in .picker-modal');
if (pickerToClose.length > 0) {
$.closeModal(pickerToClose.parents('.popover'));
}
}
});
//修复picker会滚动页面的bug
$(document).on($.touchEvents.move, '.picker-modal-inner', function (e) {
e.preventDefault();
});
$.openPicker = function (tpl, className, callback) {
if (typeof className === 'function') {
callback = className;
className = undefined;
}
$.closePicker();
var container = $(
"
"
).appendTo(document.body);
container.show();
//container.addClass("modal-in");
//关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。
var dialog = $(tpl).appendTo(container);
dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。
//dialog.addClass("modal-in");
container.addClass('modal-in');
callback && container.on('close', callback);
return dialog;
};
$.updatePicker = function (tpl) {
var container = $('.modal-in');
if (!container[0]) return false;
container.html('');
var dialog = $(tpl).appendTo(container);
dialog.addClass('modal-in');
return dialog;
};
$.closePicker = function (container, callback) {
if (typeof container === 'function') callback = container;
$('.modal-in')
.addClass('modal-out')
.transitionEnd(function () {
$(this).remove();
callback && callback();
})
.trigger('close');
};
$.fn.picker = function (params) {
var args = arguments;
return this.each(function () {
if (!this) return;
var $this = $(this);
var picker = $this.data('picker');
if (!picker) {
params = params || {};
var inputValue = $this.val();
if (params.value === undefined && inputValue !== '') {
params.value =
params.cols.length > 1 ? inputValue.split(' ') : [inputValue];
}
var p = $.extend({ input: this }, params);
picker = new Picker(p);
$this.data('picker', picker);
}
if (typeof params === typeof 'a') {
picker[params].apply(picker, Array.prototype.slice.call(args, 1));
}
});
};
})($);
+ function($) {
'use strict';
$.initPullToRefresh = function(pageContainer) {
var eventsTarget = $(pageContainer);
if (!eventsTarget.hasClass('pull-to-refresh-content')) {
eventsTarget = eventsTarget.find('.pull-to-refresh-content');
}
if (!eventsTarget || eventsTarget.length === 0) return;
var isTouched, isMoved, touchesStart = {},
isScrolling, touchesDiff, touchStartTime, container, refresh = false,
useTranslate = false,
startTranslate = 0,
translate, scrollTop, wasScrolled, triggerDistance, dynamicTriggerDistance;
container = eventsTarget;
// Define trigger distance
if (container.attr('data-ptr-distance')) {
dynamicTriggerDistance = true;
} else {
triggerDistance = 44;
}
function handleTouchStart(e) {
if (isTouched) {
if ($.os.android) {
if ('targetTouches' in e && e.targetTouches.length > 1) return;
} else return;
}
isMoved = false;
isTouched = true;
isScrolling = undefined;
wasScrolled = undefined;
var position = $.getTouchPosition(e);
touchesStart.x = position.x;
touchesStart.y = position.y;
touchStartTime = (new Date()).getTime();
/*jshint validthis:true */
container = $(this);
}
function handleTouchMove(e) {
if (!isTouched) return;
var position = $.getTouchPosition(e);
var pageX = position.x;
var pageY = position.y;
if (typeof isScrolling === 'undefined') {
isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
}
if (!isScrolling) {
isTouched = false;
return;
}
scrollTop = container[0].scrollTop;
if (typeof wasScrolled === 'undefined' && scrollTop !== 0) wasScrolled = true;
if (!isMoved) {
/*jshint validthis:true */
container.removeClass('transitioning');
if (scrollTop > container[0].offsetHeight) {
isTouched = false;
return;
}
if (dynamicTriggerDistance) {
triggerDistance = container.attr('data-ptr-distance');
if (triggerDistance.indexOf('%') >= 0) triggerDistance = container[0].offsetHeight * parseInt(triggerDistance, 10) / 100;
}
startTranslate = container.hasClass('refreshing') ? triggerDistance : 0;
if (container[0].scrollHeight === container[0].offsetHeight || !$.os.ios) {
useTranslate = true;
} else {
useTranslate = false;
}
useTranslate = true;
}
isMoved = true;
touchesDiff = pageY - touchesStart.y;
if (touchesDiff > 0 && scrollTop <= 0 || scrollTop < 0) {
// iOS 8 fix
if ($.os.ios && parseInt($.os.version.split('.')[0], 10) > 7 && scrollTop === 0 && !wasScrolled) useTranslate = true;
if (useTranslate) {
e.preventDefault();
translate = (Math.pow(touchesDiff, 0.85) + startTranslate);
container.transform('translate3d(0,' + translate + 'px,0)');
} else {}
if ((useTranslate && Math.pow(touchesDiff, 0.85) > triggerDistance) || (!useTranslate && touchesDiff >= triggerDistance * 2)) {
refresh = true;
container.addClass('pull-up').removeClass('pull-down');
} else {
refresh = false;
container.removeClass('pull-up').addClass('pull-down');
}
} else {
container.removeClass('pull-up pull-down');
refresh = false;
return;
}
}
function handleTouchEnd() {
if (!isTouched || !isMoved) {
isTouched = false;
isMoved = false;
return;
}
if (translate) {
container.addClass('transitioning');
translate = 0;
}
container.transform('');
if (refresh) {
container.addClass('refreshing');
container.trigger('refresh', {
done: function() {
$.pullToRefreshDone(container);
}
});
} else {
container.removeClass('pull-down');
}
isTouched = false;
isMoved = false;
}
// Attach Events
eventsTarget.on($.touchEvents.start, handleTouchStart);
eventsTarget.on($.touchEvents.move, handleTouchMove);
eventsTarget.on($.touchEvents.end, handleTouchEnd);
function destroyPullToRefresh() {
eventsTarget.off($.touchEvents.start, handleTouchStart);
eventsTarget.off($.touchEvents.move, handleTouchMove);
eventsTarget.off($.touchEvents.end, handleTouchEnd);
}
eventsTarget[0].destroyPullToRefresh = destroyPullToRefresh;
};
$.pullToRefreshDone = function(container) {
container = $(container);
if (container.length === 0) container = $('.pull-to-refresh-content.refreshing');
container.removeClass('refreshing').addClass('transitioning');
container.transitionEnd(function() {
container.removeClass('transitioning pull-up pull-down');
});
};
$.pullToRefreshTrigger = function(container) {
container = $(container);
if (container.length === 0) container = $('.pull-to-refresh-content');
if (container.hasClass('refreshing')) return;
container.addClass('transitioning refreshing');
container.trigger('refresh', {
done: function() {
$.pullToRefreshDone(container);
}
});
};
$.destroyPullToRefresh = function(pageContainer) {
pageContainer = $(pageContainer);
var pullToRefreshContent = pageContainer.hasClass('pull-to-refresh-content') ? pageContainer : pageContainer.find('.pull-to-refresh-content');
if (pullToRefreshContent.length === 0) return;
if (pullToRefreshContent[0].destroyPullToRefresh) pullToRefreshContent[0].destroyPullToRefresh();
};
}($); //jshint ignore:line
/* global $:true */
+ function($) {
'use strict';
function handleInfiniteScroll() {
/*jshint validthis:true */
var inf = $(this);
var scrollTop = inf.scrollTop();
var scrollHeight = inf.scrollHeight();
var height = inf[0].offsetHeight;
var distance = inf[0].getAttribute('data-distance');
var virtualListContainer = inf.find('.virtual-list');
var virtualList;
var onTop = inf.hasClass('infinite-scroll-top');
if (!distance) distance = 50;
if (typeof distance === 'string' && distance.indexOf('%') >= 0) {
distance = parseInt(distance, 10) / 100 * height;
}
if (distance > height) distance = height;
if (onTop) {
if (scrollTop < distance) {
inf.trigger('infinite');
}
} else {
if (scrollTop + height >= scrollHeight - distance) {
if (virtualListContainer.length > 0) {
virtualList = virtualListContainer[0].f7VirtualList;
if (virtualList && !virtualList.reachEnd) return;
}
inf.trigger('infinite');
}
}
}
$.attachInfiniteScroll = function(infiniteContent) {
$(infiniteContent).on('scroll', handleInfiniteScroll);
};
$.detachInfiniteScroll = function(infiniteContent) {
$(infiniteContent).off('scroll', handleInfiniteScroll);
};
$.initInfiniteScroll = function(pageContainer) {
pageContainer = $(pageContainer);
var infiniteContent = pageContainer.hasClass('infinite-scroll')?pageContainer:pageContainer.find('.infinite-scroll');
if (infiniteContent.length === 0) return;
$.attachInfiniteScroll(infiniteContent);
function detachEvents() {
$.detachInfiniteScroll(infiniteContent);
pageContainer.off('pageBeforeRemove', detachEvents);
}
pageContainer.on('pageBeforeRemove', detachEvents);
};
}($);
/* global $:true */
+function ($) {
"use strict";
var Index = function(params) {
this.params = params;
this.tpl = $.t7(this.params.indexListTemplate).compile();
};
Index.prototype.render = function(list) {
this.list = $(list || ".list");
this.draw();
};
Index.prototype.draw = function() {
if(this.indexList) this.indexList.remove();
this.titles = this.list.find(this.params.titleSelector);
var titleTexts = this.titles.map(function(i, t) {
return $(t).data("index") || $(t).text();
}).toArray();
this.indexList = $("").appendTo(this.list.parents(".page"));
this.indexList.html(this.tpl({indexes: titleTexts}));
this.indexList.on($.touchEvents.start, $.proxy(this.touchStart, this));
this.indexList.on($.touchEvents.start + " " + $.touchEvents.move, $.proxy(this.touchMove, this));
this.indexList.on($.touchEvents.end, $.proxy(this.touchEnd, this));
this.content = this.list.parents(".content");
};
Index.prototype.touchStart = function() {
this.pageOffsetTop = this.content.offset().top;
this.touching = true;
};
Index.prototype.touchMove = function(e) {
if(!this.touching) return;
e.preventDefault();
var li = this.getElementOnTouch($.getTouchPosition(e));
if(!li) return;
var title = this.titles.eq(li.data("index"));
var titleTop = title.parent().offset().top; // if a element has class list-group-title, it will be sticky in safari, so it's offset is not correct
var top = titleTop - this.pageOffsetTop + this.content.scrollTop();
this.content.scrollTop(top);
};
Index.prototype.touchEnd = function() {
this.touching = false;
};
Index.prototype.getElementOnTouch = function(position) {
var result = null;
this.indexList.find("li").each(function() {
if(result) return;
var $this = $(this);
var offset = $this.offset();
if(offset.top < position.y && offset.top + $this.outerHeight() > position.y) {
result = $this;
}
});
return result;
};
$.fn.indexList = function(params) {
return this.each(function() {
if(!this) return;
var list = $(this);
var index = list.data("index");
if(!index) {
params = $.extend({}, $.fn.indexList.prototype.defaults, params);
index = new Index(params).render(list);
list.data("index", index);
}
return index;
});
};
$.fn.indexList.prototype.defaults = {
titleSelector: ".list-group-title",
indexListTemplate: "{{#indexes}}{{this}} {{/indexes}}"
};
$.initIndexList = function(selector) {
var container = $(selector);
if(container.hasClass(".contacts-block")) {
container.indexList();
} else {
container.find(".contacts-block").indexList();
}
};
}($);
/* global $:true */
+ function($) {
"use strict";
$(document).on("click", ".search-bar label", function(e) {
console.log('a');
$(e.target).parents(".search-bar").addClass("search-focusing");
})
.on("blur", ".search-input", function(e) {
var $input = $(e.target);
if(!$input.val()) $input.parents(".search-bar").removeClass("search-focusing");
})
.on("click", ".search-cancel", function(e) {
var $input = $(e.target).parents(".search-bar").removeClass("search-focusing").find(".search-input").val("").blur();
})
.on("click", ".icon-clear", function(e) {
var $input = $(e.target).parents(".search-bar").find(".search-input").val("").focus();
});
}($);
/*======================================================
************ Panels ************
======================================================*/
/*jshint unused: false*/
+function ($) {
"use strict";
$.allowPanelOpen = true;
$.openPanel = function (panel) {
if (!$.allowPanelOpen) return false;
if(panel === 'left' || panel === 'right') panel = ".panel-" + panel; //可以传入一个方向
panel = panel ? $(panel) : $(".panel").eq(0);
var direction = panel.hasClass("panel-right") ? "right" : "left";
if (panel.length === 0 || panel.hasClass('active')) return false;
$.closePanel(); // Close if some panel is opened
$.allowPanelOpen = false;
var effect = panel.hasClass('panel-reveal') ? 'reveal' : 'cover';
panel.css({display: 'block'}).addClass('active');
panel.trigger('open');
// Trigger reLayout
var clientLeft = panel[0].clientLeft;
// Transition End;
var transitionEndTarget = effect === 'reveal' ? $($.getCurrentPage()) : panel;
var openedTriggered = false;
function panelTransitionEnd() {
transitionEndTarget.transitionEnd(function (e) {
if (e.target === transitionEndTarget[0]) {
if (panel.hasClass('active')) {
panel.trigger('opened');
}
else {
panel.trigger('closed');
}
$.allowPanelOpen = true;
}
else panelTransitionEnd();
});
}
panelTransitionEnd();
$(document.body).addClass('with-panel-' + direction + '-' + effect);
return true;
};
$.closePanel = function () {
var activePanel = $('.panel.active');
if (activePanel.length === 0) return false;
var effect = activePanel.hasClass('panel-reveal') ? 'reveal' : 'cover';
var panelPosition = activePanel.hasClass('panel-left') ? 'left' : 'right';
activePanel.removeClass('active');
var transitionEndTarget = effect === 'reveal' ? $('.page') : activePanel;
activePanel.trigger('close');
$.allowPanelOpen = false;
transitionEndTarget.transitionEnd(function () {
if (activePanel.hasClass('active')) return;
activePanel.css({display: ''});
activePanel.trigger('closed');
$('body').removeClass('panel-closing');
$.allowPanelOpen = true;
});
$('body').addClass('panel-closing').removeClass('with-panel-' + panelPosition + '-' + effect);
};
$(document).on("click", ".open-panel", function(e) {
var panel = $(e.target).data(panel);
$.openPanel(panel);
});
$(document).on("click", ".close-panel, .panel-overlay", function(e) {
$.closePanel();
});
/*======================================================
************ Swipe panels ************
======================================================*/
$.initSwipePanels = function () {
var panel, side;
var swipePanel = $.smConfig.swipePanel;
var swipePanelOnlyClose = $.smConfig.swipePanelOnlyClose;
var swipePanelCloseOpposite = true;
var swipePanelActiveArea = false;
var swipePanelThreshold = 2;
var swipePanelNoFollow = false;
if(!(swipePanel || swipePanelOnlyClose)) return;
var panelOverlay = $('.panel-overlay');
var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, translate, opened, panelWidth, effect, direction;
var currentPage = $($.getCurrentPage());
function handleTouchStart(e) {
currentPage = $($.getCurrentPage()); //page may changed
if (!$.allowPanelOpen || (!swipePanel && !swipePanelOnlyClose) || isTouched) return;
if ($('.modal-in, .photo-browser-in').length > 0) return;
if (!(swipePanelCloseOpposite || swipePanelOnlyClose)) {
if ($('.panel.active').length > 0 && !panel.hasClass('active')) return;
}
var position = $.getTouchPosition(e);
touchesStart.x = position.x;
touchesStart.y = position.y;
if (swipePanelCloseOpposite || swipePanelOnlyClose) {
if ($('.panel.active').length > 0) {
side = $('.panel.active').hasClass('panel-left') ? 'left' : 'right';
}
else {
if (swipePanelOnlyClose) return;
side = swipePanel;
}
if (!side) return;
}
panel = $('.panel.panel-' + side);
if(!panel[0]) return;
opened = panel.hasClass('active');
if (swipePanelActiveArea && !opened) {
if (side === 'left') {
if (touchesStart.x > swipePanelActiveArea) return;
}
if (side === 'right') {
if (touchesStart.x < window.innerWidth - swipePanelActiveArea) return;
}
}
isMoved = false;
isTouched = true;
isScrolling = undefined;
touchStartTime = (new Date()).getTime();
direction = undefined;
}
function handleTouchMove(e) {
if (!isTouched) return;
if(!panel[0]) return;
if (e.f7PreventPanelSwipe) return;
var position = $.getTouchPosition(e);
var pageX = position.x;
var pageY = position.y;
if (typeof isScrolling === 'undefined') {
isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
}
if (isScrolling) {
isTouched = false;
return;
}
if (!direction) {
if (pageX > touchesStart.x) {
direction = 'to-right';
}
else {
direction = 'to-left';
}
if (
side === 'left' &&
(
direction === 'to-left' && !panel.hasClass('active')
) ||
side === 'right' &&
(
direction === 'to-right' && !panel.hasClass('active')
)
)
{
isTouched = false;
return;
}
}
if (swipePanelNoFollow) {
var timeDiff = (new Date()).getTime() - touchStartTime;
if (timeDiff < 300) {
if (direction === 'to-left') {
if (side === 'right') $.openPanel(side);
if (side === 'left' && panel.hasClass('active')) $.closePanel();
}
if (direction === 'to-right') {
if (side === 'left') $.openPanel(side);
if (side === 'right' && panel.hasClass('active')) $.closePanel();
}
}
isTouched = false;
isMoved = false;
return;
}
if (!isMoved) {
effect = panel.hasClass('panel-cover') ? 'cover' : 'reveal';
if (!opened) {
panel.show();
panelOverlay.show();
}
panelWidth = panel[0].offsetWidth;
panel.transition(0);
/*
if (panel.find('.' + app.params.viewClass).length > 0) {
if (app.sizeNavbars) app.sizeNavbars(panel.find('.' + app.params.viewClass)[0]);
}
*/
}
isMoved = true;
e.preventDefault();
var threshold = opened ? 0 : -swipePanelThreshold;
if (side === 'right') threshold = -threshold;
touchesDiff = pageX - touchesStart.x + threshold;
if (side === 'right') {
translate = touchesDiff - (opened ? panelWidth : 0);
if (translate > 0) translate = 0;
if (translate < -panelWidth) {
translate = -panelWidth;
}
}
else {
translate = touchesDiff + (opened ? panelWidth : 0);
if (translate < 0) translate = 0;
if (translate > panelWidth) {
translate = panelWidth;
}
}
if (effect === 'reveal') {
currentPage.transform('translate3d(' + translate + 'px,0,0)').transition(0);
panelOverlay.transform('translate3d(' + translate + 'px,0,0)');
//app.pluginHook('swipePanelSetTransform', currentPage[0], panel[0], Math.abs(translate / panelWidth));
}
else {
panel.transform('translate3d(' + translate + 'px,0,0)').transition(0);
//app.pluginHook('swipePanelSetTransform', currentPage[0], panel[0], Math.abs(translate / panelWidth));
}
}
function handleTouchEnd(e) {
if (!isTouched || !isMoved) {
isTouched = false;
isMoved = false;
return;
}
isTouched = false;
isMoved = false;
var timeDiff = (new Date()).getTime() - touchStartTime;
var action;
var edge = (translate === 0 || Math.abs(translate) === panelWidth);
if (!opened) {
if (translate === 0) {
action = 'reset';
}
else if (
timeDiff < 300 && Math.abs(translate) > 0 ||
timeDiff >= 300 && (Math.abs(translate) >= panelWidth / 2)
) {
action = 'swap';
}
else {
action = 'reset';
}
}
else {
if (translate === -panelWidth) {
action = 'reset';
}
else if (
timeDiff < 300 && Math.abs(translate) >= 0 ||
timeDiff >= 300 && (Math.abs(translate) <= panelWidth / 2)
) {
if (side === 'left' && translate === panelWidth) action = 'reset';
else action = 'swap';
}
else {
action = 'reset';
}
}
if (action === 'swap') {
$.allowPanelOpen = true;
if (opened) {
$.closePanel();
if (edge) {
panel.css({display: ''});
$('body').removeClass('panel-closing');
}
}
else {
$.openPanel(side);
}
if (edge) $.allowPanelOpen = true;
}
if (action === 'reset') {
if (opened) {
$.allowPanelOpen = true;
$.openPanel(side);
}
else {
$.closePanel();
if (edge) {
$.allowPanelOpen = true;
panel.css({display: ''});
}
else {
var target = effect === 'reveal' ? currentPage : panel;
$('body').addClass('panel-closing');
target.transitionEnd(function () {
$.allowPanelOpen = true;
panel.css({display: ''});
$('body').removeClass('panel-closing');
});
}
}
}
if (effect === 'reveal') {
currentPage.transition('');
currentPage.transform('');
}
panel.transition('').transform('');
panelOverlay.css({display: ''}).transform('');
}
$(document).on($.touchEvents.start, handleTouchStart);
$(document).on($.touchEvents.move, handleTouchMove);
$(document).on($.touchEvents.end, handleTouchEnd);
};
$.initSwipePanels();
}($);
// jshint ignore: start
/*
* 路由器
*/
+function ($) {
"use strict";
if (!window.CustomEvent) {
window.CustomEvent = function (type, config) {
var e = document.createEvent('CustomEvent');
e.initCustomEvent(type, config.bubbles, config.cancelable, config.detail, config.id);
return e;
};
}
var Router = function() {
this.state = sessionStorage;
this.state.setItem("stateid", parseInt(this.state.getItem("stateid") || 1)+1);
this.state.setItem("currentStateID", this.state.getItem("stateid"));
this.stack = sessionStorage;
this.stack.setItem("back", "[]"); //返回栈, {url, pageid, stateid}
this.stack.setItem("forward", "[]"); //前进栈, {url, pageid, stateid}
this.extras = {}; //page extra: popup, panel...
this.init();
this.xhr = null;
}
Router.prototype.defaults = {
transition: true
};
Router.prototype.init = function() {
var currentPage = this.getCurrentPage();
if(!currentPage[0]) currentPage = $(".page").eq(0).addClass("page-current");
var hash = location.hash;
if(currentPage[0] && !currentPage[0].id) currentPage[0].id = (hash ? hash.slice(1) : this.genRandomID());
if(!currentPage[0]) throw new Error("can't find .page element");
var newCurrentPage = $(hash);
if(newCurrentPage[0] && (!currentPage[0] || hash.slice(1) !== currentPage[0].id)) {
currentPage.removeClass("page-current");
newCurrentPage.addClass("page-current");
currentPage = newCurrentPage;
}
//第一次加载的时候,初识话当前页面的state
var state = history.state;
if(!state) {
var id = this.genStateID();
this.replaceState(location.href, id);
this.setCurrentStateID(id);
}
var self = this;
window.addEventListener('load', function() {
//解决safari的一个bug,safari会在首次加载页面的时候触发 popstate 事件,通过setTimeout 做延迟来忽略这个错误的事件。
//参考 https://github.com/visionmedia/page.js/pull/239/files
setTimeout(function() {
window.addEventListener('popstate', $.proxy(self.onpopstate, self));
}, 0);
}, false);
}
//load new page, and push to history
Router.prototype.loadPage = function(url, noAnimation, replace, reload) {
var param = url;
if(noAnimation === undefined) {
noAnimation = !this.defaults.transition;
}
if(typeof url === typeof "a") {
param = {
url: url,
noAnimation: noAnimation,
replace: replace
}
}
var url = param.url, noAnimation = param.noAnimation, replace = param.replace;
this.getPage(url, function(page, extra) {
var currentPage = this.getCurrentPage();
var pageid = currentPage[0].id;
var action = "pushBack";
if(replace) action = "replaceBack";
if(reload) action = "reloadBack";
this[action]({
url: location.href,
pageid: "#"+ pageid,
id: this.getCurrentStateID(),
animation: !noAnimation
});
//remove all forward page
var forward = JSON.parse(this.state.getItem("forward") || "[]");
var self = this;
for(var i=0;i this.getCurrentStateID();
if(forward) this._forward(this._forwardStep);
else this._back(this._backStep);
this._backStep = 1;
this._forwardStep = 1;
//this._printStack();
}
//根据url获取页面的DOM,如果是一个内联页面,则直接返回,否则用ajax加载
Router.prototype.getPage = function(url, callback) {
if(url[0] === "#") return callback.apply(this, [$(url)]);
this.dispatch("pageLoadStart");
if(this.xhr && this.xhr.readyState < 4) {
this.xhr.onreadystatechange = $.noop;
this.xhr.abort();
this.dispatch("pageLoadCancel");
}
var self = this;
this.xhr = $.ajax({
url: url,
success: $.proxy(function(data, s, xhr) {
var html = this.parseXHR(xhr);
var $page = html[0];
var $extra = html[1];
if(!$page[0].id) $page[0].id = this.genRandomID();
$page.data("page-remote", 1);
callback.apply(this, [$page, $extra]);
}, this),
error: function() {
self.dispatch("pageLoadError");
},
complete: function() {
self.dispatch("pageLoadComplete");
}
});
}
Router.prototype.parseXHR = function(xhr) {
var response = xhr.responseText;
var body = response.match(/]*>([\s\S.]*)<\/body>/i);
var html = body ? body[1] : response;
html = ""+html+"
";
var tmp = $(html);
//chinieer 修复使用路由时候重复生成bug
if($('.panel').length===0){
var $extra = tmp.find(".popup, .popover, .panel, .panel-overlay");
}else{
var $extra = tmp.find(".popup, .popover");
}
var $page = tmp.find(".page");
if(!$page[0]) $page = tmp.addClass("page");
return [$page, $extra];
}
Router.prototype.genStateID = function() {
var id = parseInt(this.state.getItem("stateid")) + 1;
this.state.setItem("stateid", id);
return id;
}
Router.prototype.getCurrentStateID = function() {
return parseInt(this.state.getItem("currentStateID"));
}
Router.prototype.setCurrentStateID = function(id) {
this.state.setItem("currentStateID", id);
}
Router.prototype.genRandomID = function() {
return "page-"+(+new Date());
}
Router.prototype.popBack = function(step) {
step = step || 1;
var stack = JSON.parse(this.stack.getItem("back"));
if(!stack.length || stack.length < step) return null;
/**
* 注意这个逻辑: back, current, forward
* 所以back里面pop出来的应该进入current,而不是直接进入forward
*/
var h, last;
while(step-- > 0) {
h = stack.pop();
if(!last) {
var currentPage = this.getCurrentPage();
this.pushForward(this.currentState);
} else {
this.pushForward(last);
}
last = h;
}
this.stack.setItem("back", JSON.stringify(stack));
return h;
}
Router.prototype.pushBack = function(h) {
var stack = JSON.parse(this.stack.getItem("back"));
stack.push(h);
this.stack.setItem("back", JSON.stringify(stack));
}
Router.prototype.replaceBack = function(h) {
var stack = JSON.parse(this.stack.getItem("back"));
stack.pop();
stack.push(h);
this.stack.setItem("back", JSON.stringify(stack));
}
Router.prototype.reloadBack = function(h) {
//do nothing;
return;
}
Router.prototype.popForward = function(step) {
step = step || 1;
var stack = JSON.parse(this.stack.getItem("forward"));
if(!stack.length || stack.length < step) return null;
var h, last;
while(step-- > 0) {
h = stack.pop();
if(!last) {
var currentPage = this.getCurrentPage();
this.pushBack(this.currentState);
} else {
this.pushBack(last);
}
last = h;
}
this.stack.setItem("forward", JSON.stringify(stack));
return h;
}
Router.prototype.pushForward = function(h) {
var stack = JSON.parse(this.stack.getItem("forward"));
stack.push(h);
this.stack.setItem("forward", JSON.stringify(stack));
}
Router.prototype.dispatch = function (event) {
var e = new CustomEvent(event, {
bubbles: true,
cancelable: true
});
window.dispatchEvent(e);
};
Router.prototype._printStack = function () {
console.log(this.stack.getItem("back"));
console.log(this.stack.getItem("forward"));
}
$(function() {
if(!$.smConfig.router) return;
var router = $.router = new Router();
router.defaults = Router.prototype.defaults;
$(document).on("click", "a", function(e) {
var $target = $(e.currentTarget);
if($target.hasClass("external") ||
$target[0].hasAttribute("external") ||
$target.hasClass("tab-link") ||
$target.hasClass("open-popup") ||
$target.hasClass("open-panel")
) return;
e.preventDefault();
var url = $target.attr("href");
if($target.hasClass("back")) {
var step = $target.attr("data-back-step");
if(step) step = parseInt(step);
step = step || 1;
router.back(url, step);
return;
}
if(!url || url === "#" || /^(javascript:|mailto:|tel:).*$/.test(url)) return;
var noTransition = undefined;
if($target.hasClass("no-transition")) noTransition = true;
if($target.hasClass("with-transition")) noTransition = false;
router.loadPage(url, noTransition, $target.hasClass("replace") ? true : undefined); //undefined is different to false
})
});
}($);
// jshint ignore: end
/* global $:true */
/*jshint unused: false*/
+function ($) {
"use strict";
var getPage = function() {
var $page = $(".page-current");
if(!$page[0]) $page = $(".page").addClass("page-current");
return $page;
};
//初始化页面中的JS组件
$.initPage = function(page) {
var $page = page ? $(page) : getPage();
if(!$page[0]) $page = $(document.body);
var $content = $page.hasClass("content") ? $page : $page.find(".content");
$.initPullToRefresh($content);
$.initInfiniteScroll($content);
//$.initCalendar($content);
$.initIndexList($content);
//extend
if($.initSwiper) $.initSwiper($content);
if($.initSwipeout) $.initSwipeout(); // don't pass $content because the swipeout element is not $content
};
if($.smConfig.showPageLoadingIndicator) {
//这里的 以 push 开头的是私有事件,不要用
$(window).on("pageLoadStart", function() {
$.showIndicator();
});
$(document).on("pageAnimationStart", function() {
$.hideIndicator();
});
$(window).on("pageLoadCancel", function() {
$.hideIndicator();
});
$(window).on("pageLoadError", function() {
$.hideIndicator();
$.toast("加载失败");
});
}
$.init = function() {
var $page = getPage();
var id = $page[0].id;
if($page.hasClass("page-inited")) {
$page.trigger("pageReinit", [id, $page]);
} else {
$.initPage();
$page.addClass("page-inited");
$page.trigger("pageInit", [id, $page]);
}
};
$(function() {
if($.smConfig.autoInit) {
$.init();
}
$(document).on("pageInitInternal", function(e, id, $page) {
$.init();
});
});
}($);
/*
* jquery.fly
*
* 抛物线动画
* @github https://github.com/amibug/fly
* Copyright (c) 2014 wuyuedong
* copy from tmall.com
*/
(function ($) {
$.fly = function (element, options) {
// 默认值
var defaults = {
version: '1.0.0',
autoPlay: true,
vertex_Rtop: 20, // 默认顶点高度top值
speed: 1.2,
start: {}, // top, left, width, height
end: {},
onEnd: $.noop
};
var self = this,
$element = $(element);
/**
* 初始化组件,new的时候即调用
*/
self.init = function (options) {
this.setOptions(options);
!!this.settings.autoPlay && this.play();
};
/**
* 设置组件参数
*/
self.setOptions = function (options) {
this.settings = $.extend(true, {}, defaults, options);
var settings = this.settings,
start = settings.start,
end = settings.end;
$element.css({marginTop: '0px', marginLeft: '0px', position: 'fixed'}).appendTo('body');
// 运动过程中有改变大小
if (end.width != null && end.height != null) {
$.extend(true, start, {
width: $element.width(),
height: $element.height()
});
}
// 运动轨迹最高点top值
var vertex_top = Math.min(start.top, end.top) - Math.abs(start.left - end.left) / 3;
if (vertex_top < settings.vertex_Rtop) {
// 可能出现起点或者终点就是运动曲线顶点的情况
vertex_top = Math.min(settings.vertex_Rtop, Math.min(start.top, end.top));
}
/**
* ======================================================
* 运动轨迹在页面中的top值可以抽象成函数 y = a * x*x + b;
* a = curvature
* b = vertex_top
* ======================================================
*/
var distance = Math.sqrt(Math.pow(start.top - end.top, 2) + Math.pow(start.left - end.left, 2)),
// 元素移动次数
steps = Math.ceil(Math.min(Math.max(Math.log(distance) / 0.05 - 75, 30), 100) / settings.speed),
ratio = start.top == vertex_top ? 0 : -Math.sqrt((end.top - vertex_top) / (start.top - vertex_top)),
vertex_left = (ratio * start.left - end.left) / (ratio - 1),
// 特殊情况,出现顶点left==终点left,将曲率设置为0,做直线运动。
curvature = end.left == vertex_left ? 0 : (end.top - vertex_top) / Math.pow(end.left - vertex_left, 2);
$.extend(true, settings, {
count: -1, // 每次重置为-1
steps: steps,
vertex_left: vertex_left,
vertex_top: vertex_top,
curvature: curvature
});
};
/**
* 开始运动,可自己调用
*/
self.play = function () {
this.move();
};
/**
* 按step运动
*/
self.move = function () {
var settings = this.settings,
start = settings.start,
count = settings.count,
steps = settings.steps,
end = settings.end;
// 计算left top值
var left = start.left + (end.left - start.left) * count / steps,
top = settings.curvature == 0 ? start.top + (end.top - start.top) * count / steps : settings.curvature * Math.pow(left - settings.vertex_left, 2) + settings.vertex_top;
// 运动过程中有改变大小
if (end.width != null && end.height != null) {
var i = steps / 2,
width = end.width - (end.width - start.width) * Math.cos(count < i ? 0 : (count - i) / (steps - i) * Math.PI / 2),
height = end.height - (end.height - start.height) * Math.cos(count < i ? 0 : (count - i) / (steps - i) * Math.PI / 2);
$element.css({width: width + "px", height: height + "px", "font-size": Math.min(width, height) + "px"});
}
$element.css({
left: left + "px",
top: top + "px"
});
settings.count++;
// 定时任务
var time = window.requestAnimationFrame($.proxy(this.move, this));
if (count == steps) {
window.cancelAnimationFrame(time);
// fire callback
settings.onEnd.apply(this);
}
};
/**
* 销毁
*/
self.destroy = function(){
$element.remove();
};
self.init(options);
};
// add the plugin to the jQuery.fn object
$.fn.fly = function (options) {
return this.each(function () {
if (undefined == $(this).data('fly')) {
$(this).data('fly', new $.fly(this, options));
}
});
};
})(jQuery);
+(function ($) {
'use strict';
var defaults;
var selects = [];
var Select = function (input, config) {
var self = this;
this.config = config;
//init empty data
this.data = {
values: '',
titles: '',
origins: [],
length: 0,
};
this.$input = $(input);
this.$input.prop('readOnly', true);
this.initConfig();
config = this.config;
this.$input.click($.proxy(this.open, this));
selects.push(this);
};
Select.prototype.initConfig = function () {
this.config = $.extend({}, defaults, this.config);
var config = this.config;
if (!config.items || !config.items.length) return;
config.items = config.items.map(function (d, i) {
if (typeof d == typeof 'a') {
return {
title: d,
value: d,
};
}
return d;
});
this.tpl = $.t7.compile(
"" +
config.toolbarTemplate +
(config.multi ? config.checkboxTemplate : config.radioTemplate) +
'
'
);
if (config.input !== undefined) this.$input.val(config.input);
this.parseInitValue();
this._init = true;
};
Select.prototype.updateInputValue = function (values, titles) {
var v, t;
if (this.config.multi) {
v = values.join(this.config.split);
t = titles.join(this.config.split);
} else {
v = values[0];
t = titles[0];
}
//caculate origin data
var origins = [];
this.config.items.forEach(function (d) {
values.each(function (i, dd) {
if (d.value == dd) origins.push(d);
});
});
this.$input.val(t).data('values', v);
this.$input.attr('value', t).attr('data-values', v);
var data = {
values: v,
titles: t,
valuesArray: values,
titlesArray: titles,
origins: origins,
length: origins.length,
};
this.data = data;
this.$input.trigger('change', data);
this.config.onChange && this.config.onChange.call(this, data);
};
Select.prototype.parseInitValue = function () {
var value = this.$input.val();
var items = this.config.items;
//如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。
if (!this._init && (value === undefined || value == null || value === ''))
return;
var titles = this.config.multi ? value.split(this.config.split) : [value];
for (var i = 0; i < items.length; i++) {
items[i].checked = false;
for (var j = 0; j < titles.length; j++) {
if (items[i].title === titles[j]) {
items[i].checked = true;
}
}
}
};
Select.prototype._bind = function (dialog) {
var self = this,
config = this.config;
dialog
.on('change', function (e) {
var checked = dialog.find('input:checked');
var values = checked.map(function () {
return $(this).val();
});
var titles = checked.map(function () {
return $(this).data('title');
});
self.updateInputValue(values, titles);
if (config.autoClose && !config.multi) self.close();
})
.on('click', '.close-select', function () {
self.close();
});
};
//更新数据
Select.prototype.update = function (config) {
this.config = $.extend({}, this.config, config);
this.initConfig();
if (this._open) {
this._bind($.updatePicker(this.getHTML()));
}
};
Select.prototype.open = function (values, titles) {
if (this._open) return;
// open picker 会默认关掉其他的,但是 onClose 不会被调用,所以这里先关掉其他select
for (var i = 0; i < selects.length; i++) {
var s = selects[i];
if (s === this) continue;
if (s._open) {
if (!s.close()) return false; // 其他的select由于某些条件限制关闭失败。
}
}
this.parseInitValue();
var config = this.config;
var dialog = (this.dialog = $.openPicker(this.getHTML()));
this._bind(dialog);
this._open = true;
if (config.onOpen) config.onOpen(this);
};
Select.prototype.close = function (callback, force) {
if (!this._open) return false;
var self = this,
beforeClose = this.config.beforeClose;
if (typeof callback === typeof true) {
force === callback;
}
if (!force) {
if (
beforeClose &&
typeof beforeClose === 'function' &&
beforeClose.call(this, this.data.values, this.data.titles) === false
) {
return false;
}
if (this.config.multi) {
if (
this.config.min !== undefined &&
this.data.length < this.config.min
) {
$.toast('请至少选择' + this.config.min + '个', 'text');
return false;
}
if (
this.config.max !== undefined &&
this.data.length > this.config.max
) {
$.toast('最多只能选择' + this.config.max + '个', 'text');
return false;
}
}
}
$.closePicker(function () {
self.onClose();
callback && callback();
});
return true;
};
Select.prototype.onClose = function () {
this._open = false;
if (this.config.onClose) this.config.onClose(this);
};
Select.prototype.getHTML = function (callback) {
var config = this.config;
return this.tpl({
items: config.items,
title: config.title,
closeText: config.closeText,
});
};
$.fn.select = function (params, args) {
return this.each(function () {
var $this = $(this);
if (!$this.data('weui-select'))
$this.data('weui-select', new Select(this, params));
var select = $this.data('weui-select');
if (typeof params === typeof 'a') select[params].call(select, args);
return select;
});
};
function handleClicks(e) {
var clicked = $(this);
if (clicked.hasClass('modal-overlay')) {
if ($('.select-modal').length > 0) {
$('.select-modal').close();
}
}
}
defaults = $.fn.select.prototype.defaults = {
items: [],
input: undefined, //输入框的初始值
title: '请选择',
multi: false,
closeText: '确定',
autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效
onChange: undefined, //function
beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭
onClose: undefined, //function
onOpen: undefined, //function
split: ',', //多选模式下的分隔符
min: undefined, //多选模式下可用,最少选择数
max: undefined, //单选模式下可用,最多选择数
toolbarTemplate:
'\
{{closeText}} \
{{title}} \
',
radioTemplate:
'\
{{#items}}\
\
\
\
\
\
\
\
{{/items}}\
',
checkboxTemplate:
'\
{{#items}}\
\
\
\
\
\
\
\
{{/items}}\
',
};
$(function () {
$(document).on('click', ' .modal-overlay', handleClicks);
});
})($);
/*
* JavaScript MD5
* https://github.com/blueimp/JavaScript-MD5
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*
* Based on
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*global unescape, define, module */
;(function ($) {
'use strict'
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF)
var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
return (msw << 16) | (lsw & 0xFFFF)
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol (num, cnt) {
return (num << cnt) | (num >>> (32 - cnt))
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn (q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
}
function md5_ff (a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function md5_gg (a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function md5_hh (a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t)
}
function md5_ii (a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t)
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5 (x, len) {
/* append padding */
x[len >> 5] |= 0x80 << (len % 32)
x[(((len + 64) >>> 9) << 4) + 14] = len
var i
var olda
var oldb
var oldc
var oldd
var a = 1732584193
var b = -271733879
var c = -1732584194
var d = 271733878
for (i = 0; i < x.length; i += 16) {
olda = a
oldb = b
oldc = c
oldd = d
a = md5_ff(a, b, c, d, x[i], 7, -680876936)
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586)
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819)
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330)
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897)
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426)
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341)
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983)
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416)
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417)
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063)
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162)
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682)
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101)
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290)
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329)
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510)
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632)
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713)
b = md5_gg(b, c, d, a, x[i], 20, -373897302)
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691)
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083)
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335)
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848)
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438)
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690)
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961)
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501)
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467)
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784)
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473)
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734)
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558)
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463)
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562)
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556)
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060)
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353)
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632)
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640)
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174)
d = md5_hh(d, a, b, c, x[i], 11, -358537222)
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979)
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189)
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487)
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835)
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520)
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651)
a = md5_ii(a, b, c, d, x[i], 6, -198630844)
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415)
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905)
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055)
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571)
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606)
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523)
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799)
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359)
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744)
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380)
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649)
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070)
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379)
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259)
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551)
a = safe_add(a, olda)
b = safe_add(b, oldb)
c = safe_add(c, oldc)
d = safe_add(d, oldd)
}
return [a, b, c, d]
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr (input) {
var i
var output = ''
for (i = 0; i < input.length * 32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF)
}
return output
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl (input) {
var i
var output = []
output[(input.length >> 2) - 1] = undefined
for (i = 0; i < output.length; i += 1) {
output[i] = 0
}
for (i = 0; i < input.length * 8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32)
}
return output
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5 (s) {
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8))
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5 (key, data) {
var i
var bkey = rstr2binl(key)
var ipad = []
var opad = []
var hash
ipad[15] = opad[15] = undefined
if (bkey.length > 16) {
bkey = binl_md5(bkey, key.length * 8)
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636
opad[i] = bkey[i] ^ 0x5C5C5C5C
}
hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8)
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128))
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex (input) {
var hex_tab = '0123456789abcdef'
var output = ''
var x
var i
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i)
output += hex_tab.charAt((x >>> 4) & 0x0F) +
hex_tab.charAt(x & 0x0F)
}
return output
}
/*
* Encode a string as utf-8
*/
function str2rstr_utf8 (input) {
return unescape(encodeURIComponent(input))
}
/*
* Take string arguments and return either raw or hex encoded strings
*/
function raw_md5 (s) {
return rstr_md5(str2rstr_utf8(s))
}
function hex_md5 (s) {
return rstr2hex(raw_md5(s))
}
function raw_hmac_md5 (k, d) {
return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))
}
function hex_hmac_md5 (k, d) {
return rstr2hex(raw_hmac_md5(k, d))
}
function md5 (string, key, raw) {
if (!key) {
if (!raw) {
return hex_md5(string)
}
return raw_md5(string)
}
if (!raw) {
return hex_hmac_md5(key, string)
}
return raw_hmac_md5(key, string)
}
if (typeof define === 'function' && define.amd) {
define(function () {
return md5
})
} else if (typeof module === 'object' && module.exports) {
module.exports = md5
} else {
$.md5 = md5
}
}(this))
/* global $:true */
+(function ($) {
'use strict';
//全局通用函数;
$.getURLParameter = function (name) {
return (
decodeURIComponent(
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(
location.search
) || [, ''])[1].replace(/\+/g, '%20')
) || null
);
};
$.ajax2 = function (data) {
var successFunc = data['success'];
var errorFunc = null;
if (typeof data['error'] != 'undefined') {
errorFunc = data['error'];
}
var url = data['url'];
var time1 = new Date().getTime();
function checkTime() {
var time2 = new Date().getTime();
var t = time2 - time1;
if (t >= 2000) {
var token = '';
var uid = '';
try {
var user = eval('(' + $.caesarCache('user') + ')');
token = user.token;
uid = user.id;
} catch (e) {}
$.ajax({
type: 'POST',
url: '../show/get.php?act=recordLongTime',
dataType: '',
data: { url: url, token: token, uid: uid, rtime: t },
success: function (ret) {},
error: function () {},
});
}
}
data['success'] = function (ret) {
checkTime();
successFunc(ret);
};
if (errorFunc != null) {
data['error'] = function (ret) {
checkTime();
errorFunc(ret);
};
}
$.ajax(data);
};
$.Subtr = function (arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
n = r1 >= r2 ? r1 : r2;
return (arg1 * m - arg2 * m) / m;
};
$.accAdd = function (arg1, arg2) {
var r1, r2, m;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
};
$.fn.textSlider = function (settings) {
settings = $.extend(
{
speed: 'normal',
line: 2,
timer: 3000,
},
settings
);
return this.each(function () {
$.fn.textSlider.scllor($(this), settings);
});
};
$.fn.textSlider.scllor = function ($this, settings) {
var ul = $('ul:eq(0)', $this);
var timerID;
var li = ul.children();
var liHight = $(li[0]).height();
var upHeight = 0 - settings.line * liHight; //滚动的高度;
var scrollUp = function () {
ul.animate({ marginTop: upHeight }, settings.speed, function () {
for (var i = 0; i < settings.line; i++) {
ul.find('li:first', $this).appendTo(ul);
}
ul.css({ marginTop: 0 });
});
};
var autoPlay = function () {
timerID = window.setInterval(scrollUp, settings.timer);
};
var autoStop = function () {
window.clearInterval(timerID);
};
//事件绑定
ul.hover(autoStop, autoPlay).mouseout();
};
})($);
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) {
return (lum1 + 0.05) / (lum2 + 0.05)
};
return (lum2 + 0.05) / (lum1 + 0.05);
},
level: function(color2) {
var contrastRatio = this.contrast(color2);
return (contrastRatio >= 7.1) ? 'AAA' : (contrastRatio >= 4.5) ? 'AA' : '';
},
dark: function() {
// YIQ equation from http://24ways.org/2010/calculating-color-contrast
var rgb = this.values.rgb,
yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
return yiq < 128;
},
light: function() {
return !this.dark();
},
negate: function() {
var rgb = []
for (var i = 0; i < 3; i++) {
rgb[i] = 255 - this.values.rgb[i];
}
this.setValues("rgb", rgb);
return this;
},
lighten: function(ratio) {
this.values.hsl[2] += this.values.hsl[2] * ratio;
this.setValues("hsl", this.values.hsl);
return this;
},
darken: function(ratio) {
this.values.hsl[2] -= this.values.hsl[2] * ratio;
this.setValues("hsl", this.values.hsl);
return this;
},
saturate: function(ratio) {
this.values.hsl[1] += this.values.hsl[1] * ratio;
this.setValues("hsl", this.values.hsl);
return this;
},
desaturate: function(ratio) {
this.values.hsl[1] -= this.values.hsl[1] * ratio;
this.setValues("hsl", this.values.hsl);
return this;
},
whiten: function(ratio) {
this.values.hwb[1] += this.values.hwb[1] * ratio;
this.setValues("hwb", this.values.hwb);
return this;
},
blacken: function(ratio) {
this.values.hwb[2] += this.values.hwb[2] * ratio;
this.setValues("hwb", this.values.hwb);
return this;
},
greyscale: function() {
var rgb = this.values.rgb;
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
this.setValues("rgb", [val, val, val]);
return this;
},
clearer: function(ratio) {
this.setValues("alpha", this.values.alpha - (this.values.alpha * ratio));
return this;
},
opaquer: function(ratio) {
this.setValues("alpha", this.values.alpha + (this.values.alpha * ratio));
return this;
},
rotate: function(degrees) {
var hue = this.values.hsl[0];
hue = (hue + degrees) % 360;
hue = hue < 0 ? 360 + hue : hue;
this.values.hsl[0] = hue;
this.setValues("hsl", this.values.hsl);
return this;
},
mix: function(color2, weight) {
weight = 1 - (weight == null ? 0.5 : weight);
// algorithm from Sass's mix(). Ratio of first color in mix is
// determined by the alphas of both colors and the weight
var t1 = weight * 2 - 1,
d = this.alpha() - color2.alpha();
var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2;
var weight2 = 1 - weight1;
var rgb = this.rgbArray();
var rgb2 = color2.rgbArray();
for (var i = 0; i < rgb.length; i++) {
rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2;
}
this.setValues("rgb", rgb);
var alpha = this.alpha() * weight + color2.alpha() * (1 - weight);
this.setValues("alpha", alpha);
return this;
},
toJSON: function() {
return this.rgb();
},
clone: function() {
return new Color(this.rgb());
}
}
Color.prototype.getValues = function(space) {
var vals = {};
for (var i = 0; i < space.length; i++) {
vals[space.charAt(i)] = this.values[space][i];
}
if (this.values.alpha != 1) {
vals["a"] = this.values.alpha;
}
// {r: 255, g: 255, b: 255, a: 0.4}
return vals;
}
Color.prototype.setValues = function(space, vals) {
var spaces = {
"rgb": ["red", "green", "blue"],
"hsl": ["hue", "saturation", "lightness"],
"hsv": ["hue", "saturation", "value"],
"hwb": ["hue", "whiteness", "blackness"],
"cmyk": ["cyan", "magenta", "yellow", "black"]
};
var maxes = {
"rgb": [255, 255, 255],
"hsl": [360, 100, 100],
"hsv": [360, 100, 100],
"hwb": [360, 100, 100],
"cmyk": [100, 100, 100, 100]
};
var alpha = 1;
if (space == "alpha") {
alpha = vals;
} else if (vals.length) {
// [10, 10, 10]
this.values[space] = vals.slice(0, space.length);
alpha = vals[space.length];
} else if (vals[space.charAt(0)] !== undefined) {
// {r: 10, g: 10, b: 10}
for (var i = 0; i < space.length; i++) {
this.values[space][i] = vals[space.charAt(i)];
}
alpha = vals.a;
} else if (vals[spaces[space][0]] !== undefined) {
// {red: 10, green: 10, blue: 10}
var chans = spaces[space];
for (var i = 0; i < space.length; i++) {
this.values[space][i] = vals[chans[i]];
}
alpha = vals.alpha;
}
this.values.alpha = Math.max(0, Math.min(1, (alpha !== undefined ? alpha : this.values.alpha)));
if (space == "alpha") {
return;
}
// cap values of the space prior converting all values
for (var i = 0; i < space.length; i++) {
var capped = Math.max(0, Math.min(maxes[space][i], this.values[space][i]));
this.values[space][i] = Math.round(capped);
}
// convert to all the other color spaces
for (var sname in spaces) {
if (sname != space) {
this.values[sname] = convert[space][sname](this.values[space])
}
// cap values
for (var i = 0; i < sname.length; i++) {
var capped = Math.max(0, Math.min(maxes[sname][i], this.values[sname][i]));
this.values[sname][i] = Math.round(capped);
}
}
return true;
}
Color.prototype.setSpace = function(space, args) {
var vals = args[0];
if (vals === undefined) {
// color.rgb()
return this.getValues(space);
}
// color.rgb(10, 10, 10)
if (typeof vals == "number") {
vals = Array.prototype.slice.call(args);
}
this.setValues(space, vals);
return this;
}
Color.prototype.setChannel = function(space, index, val) {
if (val === undefined) {
// color.red()
return this.values[space][index];
}
// color.red(100)
this.values[space][index] = val;
this.setValues(space, this.values[space]);
return this;
}
window.Color = module.exports = Color
},{"color-convert":4,"color-string":6}],3:[function(require,module,exports){
/* MIT license */
module.exports = {
rgb2hsl: rgb2hsl,
rgb2hsv: rgb2hsv,
rgb2hwb: rgb2hwb,
rgb2cmyk: rgb2cmyk,
rgb2keyword: rgb2keyword,
rgb2xyz: rgb2xyz,
rgb2lab: rgb2lab,
rgb2lch: rgb2lch,
hsl2rgb: hsl2rgb,
hsl2hsv: hsl2hsv,
hsl2hwb: hsl2hwb,
hsl2cmyk: hsl2cmyk,
hsl2keyword: hsl2keyword,
hsv2rgb: hsv2rgb,
hsv2hsl: hsv2hsl,
hsv2hwb: hsv2hwb,
hsv2cmyk: hsv2cmyk,
hsv2keyword: hsv2keyword,
hwb2rgb: hwb2rgb,
hwb2hsl: hwb2hsl,
hwb2hsv: hwb2hsv,
hwb2cmyk: hwb2cmyk,
hwb2keyword: hwb2keyword,
cmyk2rgb: cmyk2rgb,
cmyk2hsl: cmyk2hsl,
cmyk2hsv: cmyk2hsv,
cmyk2hwb: cmyk2hwb,
cmyk2keyword: cmyk2keyword,
keyword2rgb: keyword2rgb,
keyword2hsl: keyword2hsl,
keyword2hsv: keyword2hsv,
keyword2hwb: keyword2hwb,
keyword2cmyk: keyword2cmyk,
keyword2lab: keyword2lab,
keyword2xyz: keyword2xyz,
xyz2rgb: xyz2rgb,
xyz2lab: xyz2lab,
xyz2lch: xyz2lch,
lab2xyz: lab2xyz,
lab2rgb: lab2rgb,
lab2lch: lab2lch,
lch2lab: lch2lab,
lch2xyz: lch2xyz,
lch2rgb: lch2rgb
}
function rgb2hsl(rgb) {
var r = rgb[0]/255,
g = rgb[1]/255,
b = rgb[2]/255,
min = Math.min(r, g, b),
max = Math.max(r, g, b),
delta = max - min,
h, s, l;
if (max == min)
h = 0;
else if (r == max)
h = (g - b) / delta;
else if (g == max)
h = 2 + (b - r) / delta;
else if (b == max)
h = 4 + (r - g)/ delta;
h = Math.min(h * 60, 360);
if (h < 0)
h += 360;
l = (min + max) / 2;
if (max == min)
s = 0;
else if (l <= 0.5)
s = delta / (max + min);
else
s = delta / (2 - max - min);
return [h, s * 100, l * 100];
}
function rgb2hsv(rgb) {
var r = rgb[0],
g = rgb[1],
b = rgb[2],
min = Math.min(r, g, b),
max = Math.max(r, g, b),
delta = max - min,
h, s, v;
if (max == 0)
s = 0;
else
s = (delta/max * 1000)/10;
if (max == min)
h = 0;
else if (r == max)
h = (g - b) / delta;
else if (g == max)
h = 2 + (b - r) / delta;
else if (b == max)
h = 4 + (r - g) / delta;
h = Math.min(h * 60, 360);
if (h < 0)
h += 360;
v = ((max / 255) * 1000) / 10;
return [h, s, v];
}
function rgb2hwb(rgb) {
var r = rgb[0],
g = rgb[1],
b = rgb[2],
h = rgb2hsl(rgb)[0],
w = 1/255 * Math.min(r, Math.min(g, b)),
b = 1 - 1/255 * Math.max(r, Math.max(g, b));
return [h, w * 100, b * 100];
}
function rgb2cmyk(rgb) {
var r = rgb[0] / 255,
g = rgb[1] / 255,
b = rgb[2] / 255,
c, m, y, k;
k = Math.min(1 - r, 1 - g, 1 - b);
c = (1 - r - k) / (1 - k) || 0;
m = (1 - g - k) / (1 - k) || 0;
y = (1 - b - k) / (1 - k) || 0;
return [c * 100, m * 100, y * 100, k * 100];
}
function rgb2keyword(rgb) {
return reverseKeywords[JSON.stringify(rgb)];
}
function rgb2xyz(rgb) {
var r = rgb[0] / 255,
g = rgb[1] / 255,
b = rgb[2] / 255;
// assume sRGB
r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
return [x * 100, y *100, z * 100];
}
function rgb2lab(rgb) {
var xyz = rgb2xyz(rgb),
x = xyz[0],
y = xyz[1],
z = xyz[2],
l, a, b;
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
l = (116 * y) - 16;
a = 500 * (x - y);
b = 200 * (y - z);
return [l, a, b];
}
function rgb2lch(args) {
return lab2lch(rgb2lab(args));
}
function hsl2rgb(hsl) {
var h = hsl[0] / 360,
s = hsl[1] / 100,
l = hsl[2] / 100,
t1, t2, t3, rgb, val;
if (s == 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5)
t2 = l * (1 + s);
else
t2 = l + s - l * s;
t1 = 2 * l - t2;
rgb = [0, 0, 0];
for (var i = 0; i < 3; i++) {
t3 = h + 1 / 3 * - (i - 1);
t3 < 0 && t3++;
t3 > 1 && t3--;
if (6 * t3 < 1)
val = t1 + (t2 - t1) * 6 * t3;
else if (2 * t3 < 1)
val = t2;
else if (3 * t3 < 2)
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
else
val = t1;
rgb[i] = val * 255;
}
return rgb;
}
function hsl2hsv(hsl) {
var h = hsl[0],
s = hsl[1] / 100,
l = hsl[2] / 100,
sv, v;
if(l === 0) {
// no need to do calc on black
// also avoids divide by 0 error
return [0, 0, 0];
}
l *= 2;
s *= (l <= 1) ? l : 2 - l;
v = (l + s) / 2;
sv = (2 * s) / (l + s);
return [h, sv * 100, v * 100];
}
function hsl2hwb(args) {
return rgb2hwb(hsl2rgb(args));
}
function hsl2cmyk(args) {
return rgb2cmyk(hsl2rgb(args));
}
function hsl2keyword(args) {
return rgb2keyword(hsl2rgb(args));
}
function hsv2rgb(hsv) {
var h = hsv[0] / 60,
s = hsv[1] / 100,
v = hsv[2] / 100,
hi = Math.floor(h) % 6;
var f = h - Math.floor(h),
p = 255 * v * (1 - s),
q = 255 * v * (1 - (s * f)),
t = 255 * v * (1 - (s * (1 - f))),
v = 255 * v;
switch(hi) {
case 0:
return [v, t, p];
case 1:
return [q, v, p];
case 2:
return [p, v, t];
case 3:
return [p, q, v];
case 4:
return [t, p, v];
case 5:
return [v, p, q];
}
}
function hsv2hsl(hsv) {
var h = hsv[0],
s = hsv[1] / 100,
v = hsv[2] / 100,
sl, l;
l = (2 - s) * v;
sl = s * v;
sl /= (l <= 1) ? l : 2 - l;
sl = sl || 0;
l /= 2;
return [h, sl * 100, l * 100];
}
function hsv2hwb(args) {
return rgb2hwb(hsv2rgb(args))
}
function hsv2cmyk(args) {
return rgb2cmyk(hsv2rgb(args));
}
function hsv2keyword(args) {
return rgb2keyword(hsv2rgb(args));
}
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
function hwb2rgb(hwb) {
var h = hwb[0] / 360,
wh = hwb[1] / 100,
bl = hwb[2] / 100,
ratio = wh + bl,
i, v, f, n;
// wh + bl cant be > 1
if (ratio > 1) {
wh /= ratio;
bl /= ratio;
}
i = Math.floor(6 * h);
v = 1 - bl;
f = 6 * h - i;
if ((i & 0x01) != 0) {
f = 1 - f;
}
n = wh + f * (v - wh); // linear interpolation
switch (i) {
default:
case 6:
case 0: r = v; g = n; b = wh; break;
case 1: r = n; g = v; b = wh; break;
case 2: r = wh; g = v; b = n; break;
case 3: r = wh; g = n; b = v; break;
case 4: r = n; g = wh; b = v; break;
case 5: r = v; g = wh; b = n; break;
}
return [r * 255, g * 255, b * 255];
}
function hwb2hsl(args) {
return rgb2hsl(hwb2rgb(args));
}
function hwb2hsv(args) {
return rgb2hsv(hwb2rgb(args));
}
function hwb2cmyk(args) {
return rgb2cmyk(hwb2rgb(args));
}
function hwb2keyword(args) {
return rgb2keyword(hwb2rgb(args));
}
function cmyk2rgb(cmyk) {
var c = cmyk[0] / 100,
m = cmyk[1] / 100,
y = cmyk[2] / 100,
k = cmyk[3] / 100,
r, g, b;
r = 1 - Math.min(1, c * (1 - k) + k);
g = 1 - Math.min(1, m * (1 - k) + k);
b = 1 - Math.min(1, y * (1 - k) + k);
return [r * 255, g * 255, b * 255];
}
function cmyk2hsl(args) {
return rgb2hsl(cmyk2rgb(args));
}
function cmyk2hsv(args) {
return rgb2hsv(cmyk2rgb(args));
}
function cmyk2hwb(args) {
return rgb2hwb(cmyk2rgb(args));
}
function cmyk2keyword(args) {
return rgb2keyword(cmyk2rgb(args));
}
function xyz2rgb(xyz) {
var x = xyz[0] / 100,
y = xyz[1] / 100,
z = xyz[2] / 100,
r, g, b;
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
// assume sRGB
r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
: r = (r * 12.92);
g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
: g = (g * 12.92);
b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
: b = (b * 12.92);
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
}
function xyz2lab(xyz) {
var x = xyz[0],
y = xyz[1],
z = xyz[2],
l, a, b;
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
l = (116 * y) - 16;
a = 500 * (x - y);
b = 200 * (y - z);
return [l, a, b];
}
function xyz2lch(args) {
return lab2lch(xyz2lab(args));
}
function lab2xyz(lab) {
var l = lab[0],
a = lab[1],
b = lab[2],
x, y, z, y2;
if (l <= 8) {
y = (l * 100) / 903.3;
y2 = (7.787 * (y / 100)) + (16 / 116);
} else {
y = 100 * Math.pow((l + 16) / 116, 3);
y2 = Math.pow(y / 100, 1/3);
}
x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
return [x, y, z];
}
function lab2lch(lab) {
var l = lab[0],
a = lab[1],
b = lab[2],
hr, h, c;
hr = Math.atan2(b, a);
h = hr * 360 / 2 / Math.PI;
if (h < 0) {
h += 360;
}
c = Math.sqrt(a * a + b * b);
return [l, c, h];
}
function lab2rgb(args) {
return xyz2rgb(lab2xyz(args));
}
function lch2lab(lch) {
var l = lch[0],
c = lch[1],
h = lch[2],
a, b, hr;
hr = h / 360 * 2 * Math.PI;
a = c * Math.cos(hr);
b = c * Math.sin(hr);
return [l, a, b];
}
function lch2xyz(args) {
return lab2xyz(lch2lab(args));
}
function lch2rgb(args) {
return lab2rgb(lch2lab(args));
}
function keyword2rgb(keyword) {
return cssKeywords[keyword];
}
function keyword2hsl(args) {
return rgb2hsl(keyword2rgb(args));
}
function keyword2hsv(args) {
return rgb2hsv(keyword2rgb(args));
}
function keyword2hwb(args) {
return rgb2hwb(keyword2rgb(args));
}
function keyword2cmyk(args) {
return rgb2cmyk(keyword2rgb(args));
}
function keyword2lab(args) {
return rgb2lab(keyword2rgb(args));
}
function keyword2xyz(args) {
return rgb2xyz(keyword2rgb(args));
}
var cssKeywords = {
aliceblue: [240,248,255],
antiquewhite: [250,235,215],
aqua: [0,255,255],
aquamarine: [127,255,212],
azure: [240,255,255],
beige: [245,245,220],
bisque: [255,228,196],
black: [0,0,0],
blanchedalmond: [255,235,205],
blue: [0,0,255],
blueviolet: [138,43,226],
brown: [165,42,42],
burlywood: [222,184,135],
cadetblue: [95,158,160],
chartreuse: [127,255,0],
chocolate: [210,105,30],
coral: [255,127,80],
cornflowerblue: [100,149,237],
cornsilk: [255,248,220],
crimson: [220,20,60],
cyan: [0,255,255],
darkblue: [0,0,139],
darkcyan: [0,139,139],
darkgoldenrod: [184,134,11],
darkgray: [169,169,169],
darkgreen: [0,100,0],
darkgrey: [169,169,169],
darkkhaki: [189,183,107],
darkmagenta: [139,0,139],
darkolivegreen: [85,107,47],
darkorange: [255,140,0],
darkorchid: [153,50,204],
darkred: [139,0,0],
darksalmon: [233,150,122],
darkseagreen: [143,188,143],
darkslateblue: [72,61,139],
darkslategray: [47,79,79],
darkslategrey: [47,79,79],
darkturquoise: [0,206,209],
darkviolet: [148,0,211],
deeppink: [255,20,147],
deepskyblue: [0,191,255],
dimgray: [105,105,105],
dimgrey: [105,105,105],
dodgerblue: [30,144,255],
firebrick: [178,34,34],
floralwhite: [255,250,240],
forestgreen: [34,139,34],
fuchsia: [255,0,255],
gainsboro: [220,220,220],
ghostwhite: [248,248,255],
gold: [255,215,0],
goldenrod: [218,165,32],
gray: [128,128,128],
green: [0,128,0],
greenyellow: [173,255,47],
grey: [128,128,128],
honeydew: [240,255,240],
hotpink: [255,105,180],
indianred: [205,92,92],
indigo: [75,0,130],
ivory: [255,255,240],
khaki: [240,230,140],
lavender: [230,230,250],
lavenderblush: [255,240,245],
lawngreen: [124,252,0],
lemonchiffon: [255,250,205],
lightblue: [173,216,230],
lightcoral: [240,128,128],
lightcyan: [224,255,255],
lightgoldenrodyellow: [250,250,210],
lightgray: [211,211,211],
lightgreen: [144,238,144],
lightgrey: [211,211,211],
lightpink: [255,182,193],
lightsalmon: [255,160,122],
lightseagreen: [32,178,170],
lightskyblue: [135,206,250],
lightslategray: [119,136,153],
lightslategrey: [119,136,153],
lightsteelblue: [176,196,222],
lightyellow: [255,255,224],
lime: [0,255,0],
limegreen: [50,205,50],
linen: [250,240,230],
magenta: [255,0,255],
maroon: [128,0,0],
mediumaquamarine: [102,205,170],
mediumblue: [0,0,205],
mediumorchid: [186,85,211],
mediumpurple: [147,112,219],
mediumseagreen: [60,179,113],
mediumslateblue: [123,104,238],
mediumspringgreen: [0,250,154],
mediumturquoise: [72,209,204],
mediumvioletred: [199,21,133],
midnightblue: [25,25,112],
mintcream: [245,255,250],
mistyrose: [255,228,225],
moccasin: [255,228,181],
navajowhite: [255,222,173],
navy: [0,0,128],
oldlace: [253,245,230],
olive: [128,128,0],
olivedrab: [107,142,35],
orange: [255,165,0],
orangered: [255,69,0],
orchid: [218,112,214],
palegoldenrod: [238,232,170],
palegreen: [152,251,152],
paleturquoise: [175,238,238],
palevioletred: [219,112,147],
papayawhip: [255,239,213],
peachpuff: [255,218,185],
peru: [205,133,63],
pink: [255,192,203],
plum: [221,160,221],
powderblue: [176,224,230],
purple: [128,0,128],
rebeccapurple: [102, 51, 153],
red: [255,0,0],
rosybrown: [188,143,143],
royalblue: [65,105,225],
saddlebrown: [139,69,19],
salmon: [250,128,114],
sandybrown: [244,164,96],
seagreen: [46,139,87],
seashell: [255,245,238],
sienna: [160,82,45],
silver: [192,192,192],
skyblue: [135,206,235],
slateblue: [106,90,205],
slategray: [112,128,144],
slategrey: [112,128,144],
snow: [255,250,250],
springgreen: [0,255,127],
steelblue: [70,130,180],
tan: [210,180,140],
teal: [0,128,128],
thistle: [216,191,216],
tomato: [255,99,71],
turquoise: [64,224,208],
violet: [238,130,238],
wheat: [245,222,179],
white: [255,255,255],
whitesmoke: [245,245,245],
yellow: [255,255,0],
yellowgreen: [154,205,50]
};
var reverseKeywords = {};
for (var key in cssKeywords) {
reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
}
},{}],4:[function(require,module,exports){
var conversions = require("./conversions");
var convert = function() {
return new Converter();
}
for (var func in conversions) {
// export Raw versions
convert[func + "Raw"] = (function(func) {
// accept array or plain args
return function(arg) {
if (typeof arg == "number")
arg = Array.prototype.slice.call(arguments);
return conversions[func](arg);
}
})(func);
var pair = /(\w+)2(\w+)/.exec(func),
from = pair[1],
to = pair[2];
// export rgb2hsl and ["rgb"]["hsl"]
convert[from] = convert[from] || {};
convert[from][to] = convert[func] = (function(func) {
return function(arg) {
if (typeof arg == "number")
arg = Array.prototype.slice.call(arguments);
var val = conversions[func](arg);
if (typeof val == "string" || val === undefined)
return val; // keyword
for (var i = 0; i < val.length; i++)
val[i] = Math.round(val[i]);
return val;
}
})(func);
}
/* Converter does lazy conversion and caching */
var Converter = function() {
this.convs = {};
};
/* Either get the values for a space or
set the values for a space, depending on args */
Converter.prototype.routeSpace = function(space, args) {
var values = args[0];
if (values === undefined) {
// color.rgb()
return this.getValues(space);
}
// color.rgb(10, 10, 10)
if (typeof values == "number") {
values = Array.prototype.slice.call(args);
}
return this.setValues(space, values);
};
/* Set the values for a space, invalidating cache */
Converter.prototype.setValues = function(space, values) {
this.space = space;
this.convs = {};
this.convs[space] = values;
return this;
};
/* Get the values for a space. If there's already
a conversion for the space, fetch it, otherwise
compute it */
Converter.prototype.getValues = function(space) {
var vals = this.convs[space];
if (!vals) {
var fspace = this.space,
from = this.convs[fspace];
vals = convert[fspace][space](from);
this.convs[space] = vals;
}
return vals;
};
["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
Converter.prototype[space] = function(vals) {
return this.routeSpace(space, arguments);
}
});
module.exports = convert;
},{"./conversions":3}],5:[function(require,module,exports){
module.exports = {
"aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255],
"aquamarine": [127, 255, 212],
"azure": [240, 255, 255],
"beige": [245, 245, 220],
"bisque": [255, 228, 196],
"black": [0, 0, 0],
"blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255],
"blueviolet": [138, 43, 226],
"brown": [165, 42, 42],
"burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30],
"coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220],
"crimson": [220, 20, 60],
"cyan": [0, 255, 255],
"darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204],
"darkred": [139, 0, 0],
"darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255],
"gold": [255, 215, 0],
"goldenrod": [218, 165, 32],
"gray": [128, 128, 128],
"green": [0, 128, 0],
"greenyellow": [173, 255, 47],
"grey": [128, 128, 128],
"honeydew": [240, 255, 240],
"hotpink": [255, 105, 180],
"indianred": [205, 92, 92],
"indigo": [75, 0, 130],
"ivory": [255, 255, 240],
"khaki": [240, 230, 140],
"lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224],
"lime": [0, 255, 0],
"limegreen": [50, 205, 50],
"linen": [250, 240, 230],
"magenta": [255, 0, 255],
"maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173],
"navy": [0, 0, 128],
"oldlace": [253, 245, 230],
"olive": [128, 128, 0],
"olivedrab": [107, 142, 35],
"orange": [255, 165, 0],
"orangered": [255, 69, 0],
"orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185],
"peru": [205, 133, 63],
"pink": [255, 192, 203],
"plum": [221, 160, 221],
"powderblue": [176, 224, 230],
"purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153],
"red": [255, 0, 0],
"rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114],
"sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87],
"seashell": [255, 245, 238],
"sienna": [160, 82, 45],
"silver": [192, 192, 192],
"skyblue": [135, 206, 235],
"slateblue": [106, 90, 205],
"slategray": [112, 128, 144],
"slategrey": [112, 128, 144],
"snow": [255, 250, 250],
"springgreen": [0, 255, 127],
"steelblue": [70, 130, 180],
"tan": [210, 180, 140],
"teal": [0, 128, 128],
"thistle": [216, 191, 216],
"tomato": [255, 99, 71],
"turquoise": [64, 224, 208],
"violet": [238, 130, 238],
"wheat": [245, 222, 179],
"white": [255, 255, 255],
"whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};
},{}],6:[function(require,module,exports){
/* MIT license */
var colorNames = require('color-name');
module.exports = {
getRgba: getRgba,
getHsla: getHsla,
getRgb: getRgb,
getHsl: getHsl,
getHwb: getHwb,
getAlpha: getAlpha,
hexString: hexString,
rgbString: rgbString,
rgbaString: rgbaString,
percentString: percentString,
percentaString: percentaString,
hslString: hslString,
hslaString: hslaString,
hwbString: hwbString,
keyword: keyword
}
function getRgba(string) {
if (!string) {
return;
}
var abbr = /^#([a-fA-F0-9]{3})$/,
hex = /^#([a-fA-F0-9]{6})$/,
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
keyword = /(\w+)/;
var rgb = [0, 0, 0],
a = 1,
match = string.match(abbr);
if (match) {
match = match[1];
for (var i = 0; i < rgb.length; i++) {
rgb[i] = parseInt(match[i] + match[i], 16);
}
}
else if (match = string.match(hex)) {
match = match[1];
for (var i = 0; i < rgb.length; i++) {
rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
}
}
else if (match = string.match(rgba)) {
for (var i = 0; i < rgb.length; i++) {
rgb[i] = parseInt(match[i + 1]);
}
a = parseFloat(match[4]);
}
else if (match = string.match(per)) {
for (var i = 0; i < rgb.length; i++) {
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
}
a = parseFloat(match[4]);
}
else if (match = string.match(keyword)) {
if (match[1] == "transparent") {
return [0, 0, 0, 0];
}
rgb = colorNames[match[1]];
if (!rgb) {
return;
}
}
for (var i = 0; i < rgb.length; i++) {
rgb[i] = scale(rgb[i], 0, 255);
}
if (!a && a != 0) {
a = 1;
}
else {
a = scale(a, 0, 1);
}
rgb[3] = a;
return rgb;
}
function getHsla(string) {
if (!string) {
return;
}
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
var match = string.match(hsl);
if (match) {
var alpha = parseFloat(match[4]);
var h = scale(parseInt(match[1]), 0, 360),
s = scale(parseFloat(match[2]), 0, 100),
l = scale(parseFloat(match[3]), 0, 100),
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, s, l, a];
}
}
function getHwb(string) {
if (!string) {
return;
}
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
var match = string.match(hwb);
if (match) {
var alpha = parseFloat(match[4]);
var h = scale(parseInt(match[1]), 0, 360),
w = scale(parseFloat(match[2]), 0, 100),
b = scale(parseFloat(match[3]), 0, 100),
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, w, b, a];
}
}
function getRgb(string) {
var rgba = getRgba(string);
return rgba && rgba.slice(0, 3);
}
function getHsl(string) {
var hsla = getHsla(string);
return hsla && hsla.slice(0, 3);
}
function getAlpha(string) {
var vals = getRgba(string);
if (vals) {
return vals[3];
}
else if (vals = getHsla(string)) {
return vals[3];
}
else if (vals = getHwb(string)) {
return vals[3];
}
}
// generators
function hexString(rgb) {
return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
+ hexDouble(rgb[2]);
}
function rgbString(rgba, alpha) {
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
return rgbaString(rgba, alpha);
}
return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
}
function rgbaString(rgba, alpha) {
if (alpha === undefined) {
alpha = (rgba[3] !== undefined ? rgba[3] : 1);
}
return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
+ ", " + alpha + ")";
}
function percentString(rgba, alpha) {
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
return percentaString(rgba, alpha);
}
var r = Math.round(rgba[0]/255 * 100),
g = Math.round(rgba[1]/255 * 100),
b = Math.round(rgba[2]/255 * 100);
return "rgb(" + r + "%, " + g + "%, " + b + "%)";
}
function percentaString(rgba, alpha) {
var r = Math.round(rgba[0]/255 * 100),
g = Math.round(rgba[1]/255 * 100),
b = Math.round(rgba[2]/255 * 100);
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
}
function hslString(hsla, alpha) {
if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
return hslaString(hsla, alpha);
}
return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
}
function hslaString(hsla, alpha) {
if (alpha === undefined) {
alpha = (hsla[3] !== undefined ? hsla[3] : 1);
}
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
+ alpha + ")";
}
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
// (hwb have alpha optional & 1 is default value)
function hwbString(hwb, alpha) {
if (alpha === undefined) {
alpha = (hwb[3] !== undefined ? hwb[3] : 1);
}
return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
+ (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
}
function keyword(rgb) {
return reverseNames[rgb.slice(0, 3)];
}
// helpers
function scale(num, min, max) {
return Math.min(Math.max(min, num), max);
}
function hexDouble(num) {
var str = num.toString(16).toUpperCase();
return (str.length < 2) ? "0" + str : str;
}
//create a list of reverse color names
var reverseNames = {};
for (var name in colorNames) {
reverseNames[colorNames[name]] = name;
}
},{"color-name":5}],7:[function(require,module,exports){
/*!
* Chart.js
* http://chartjs.org/
* Version: 2.0.2
*
* Copyright 2015 Nick Downie
* Released under the MIT license
* https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
*/
var Chart = require('./core/core.js')();
require('./core/core.helpers')(Chart);
require('./core/core.element')(Chart);
require('./core/core.animation')(Chart);
require('./core/core.controller')(Chart);
require('./core/core.datasetController')(Chart);
require('./core/core.layoutService')(Chart);
require('./core/core.legend')(Chart);
require('./core/core.plugin.js')(Chart);
require('./core/core.scale')(Chart);
require('./core/core.scaleService')(Chart);
require('./core/core.title')(Chart);
require('./core/core.tooltip')(Chart);
require('./controllers/controller.bar')(Chart);
require('./controllers/controller.bubble')(Chart);
require('./controllers/controller.doughnut')(Chart);
require('./controllers/controller.line')(Chart);
require('./controllers/controller.polarArea')(Chart);
require('./controllers/controller.radar')(Chart);
require('./scales/scale.category')(Chart);
require('./scales/scale.linear')(Chart);
require('./scales/scale.logarithmic')(Chart);
require('./scales/scale.radialLinear')(Chart);
require('./scales/scale.time')(Chart);
require('./elements/element.arc')(Chart);
require('./elements/element.line')(Chart);
require('./elements/element.point')(Chart);
require('./elements/element.rectangle')(Chart);
require('./charts/Chart.Bar')(Chart);
require('./charts/Chart.Bubble')(Chart);
require('./charts/Chart.Doughnut')(Chart);
require('./charts/Chart.Line')(Chart);
require('./charts/Chart.PolarArea')(Chart);
require('./charts/Chart.Radar')(Chart);
require('./charts/Chart.Scatter')(Chart);
window.Chart = module.exports = Chart;
},{"./charts/Chart.Bar":8,"./charts/Chart.Bubble":9,"./charts/Chart.Doughnut":10,"./charts/Chart.Line":11,"./charts/Chart.PolarArea":12,"./charts/Chart.Radar":13,"./charts/Chart.Scatter":14,"./controllers/controller.bar":15,"./controllers/controller.bubble":16,"./controllers/controller.doughnut":17,"./controllers/controller.line":18,"./controllers/controller.polarArea":19,"./controllers/controller.radar":20,"./core/core.animation":21,"./core/core.controller":22,"./core/core.datasetController":23,"./core/core.element":24,"./core/core.helpers":25,"./core/core.js":26,"./core/core.layoutService":27,"./core/core.legend":28,"./core/core.plugin.js":29,"./core/core.scale":30,"./core/core.scaleService":31,"./core/core.title":32,"./core/core.tooltip":33,"./elements/element.arc":34,"./elements/element.line":35,"./elements/element.point":36,"./elements/element.rectangle":37,"./scales/scale.category":38,"./scales/scale.linear":39,"./scales/scale.logarithmic":40,"./scales/scale.radialLinear":41,"./scales/scale.time":42}],8:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
Chart.Bar = function(context, config) {
config.type = 'bar';
return new Chart(context, config);
};
};
},{}],9:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
Chart.Bubble = function(context, config) {
config.type = 'bubble';
return new Chart(context, config);
};
};
},{}],10:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
Chart.Doughnut = function(context, config) {
config.type = 'doughnut';
return new Chart(context, config);
};
};
},{}],11:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
Chart.Line = function(context, config) {
config.type = 'line';
return new Chart(context, config);
};
};
},{}],12:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
Chart.PolarArea = function(context, config) {
config.type = 'polarArea';
return new Chart(context, config);
};
};
},{}],13:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
aspectRatio: 1
};
Chart.Radar = function(context, config) {
config.options = helpers.configMerge(defaultConfig, config.options);
config.type = 'radar';
return new Chart(context, config);
};
};
},{}],14:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var defaultConfig = {
hover: {
mode: 'single'
},
scales: {
xAxes: [{
type: "linear", // scatter should not use a category axis
position: "bottom",
id: "x-axis-1" // need an ID so datasets can reference the scale
}],
yAxes: [{
type: "linear",
position: "left",
id: "y-axis-1"
}]
},
tooltips: {
callbacks: {
title: function(tooltipItems, data) {
// Title doesn't make sense for scatter since we format the data as a point
return '';
},
label: function(tooltipItem, data) {
return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
}
}
}
};
// Register the default config for this type
Chart.defaults.scatter = defaultConfig;
// Scatter charts use line controllers
Chart.controllers.scatter = Chart.controllers.line;
Chart.Scatter = function(context, config) {
config.type = 'scatter';
return new Chart(context, config);
};
};
},{}],15:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.bar = {
hover: {
mode: "label"
},
scales: {
xAxes: [{
type: "category",
// Specific to Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
type: "linear"
}]
}
};
Chart.controllers.bar = Chart.DatasetController.extend({
initialize: function(chart, datasetIndex) {
Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
// Use this to indicate that this is a bar dataset.
this.getDataset().bar = true;
},
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
getBarCount: function getBarCount() {
var barCount = 0;
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset) && dataset.bar) {
++barCount;
}
});
return barCount;
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var rectangle = new Chart.elements.Rectangle({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
var numBars = this.getBarCount();
this.updateElement(rectangle, index, true, numBars);
this.getDataset().metaData.splice(index, 0, rectangle);
},
update: function update(reset) {
var numBars = this.getBarCount();
helpers.each(this.getDataset().metaData, function(rectangle, index) {
this.updateElement(rectangle, index, reset, numBars);
}, this);
},
updateElement: function updateElement(rectangle, index, reset, numBars) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// all less than 0. use the top
yScalePoint = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
yScalePoint = yScale.getPixelForValue(yScale.min);
} else {
yScalePoint = yScale.getPixelForValue(0);
}
helpers.extend(rectangle, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: this.calculateBarX(index, this.index),
y: reset ? yScalePoint : this.calculateBarY(index, this.index),
// Tooltip
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
// Appearance
base: reset ? yScalePoint : this.calculateBarBase(this.index, index),
width: this.calculateBarWidth(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderSkipped: rectangle.custom && rectangle.custom.borderSkipped ? rectangle.custom.borderSkipped : this.chart.options.elements.rectangle.borderSkipped,
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth)
}
});
rectangle.pivot();
},
calculateBarBase: function(datasetIndex, index) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var base = 0;
if (yScale.options.stacked) {
var value = this.chart.data.datasets[datasetIndex].data[index];
if (value < 0) {
for (var i = 0; i < datasetIndex; i++) {
var negDS = this.chart.data.datasets[i];
if (helpers.isDatasetVisible(negDS) && negDS.yAxisID === yScale.id && negDS.bar) {
base += negDS.data[index] < 0 ? negDS.data[index] : 0;
}
}
} else {
for (var j = 0; j < datasetIndex; j++) {
var posDS = this.chart.data.datasets[j];
if (helpers.isDatasetVisible(posDS) && posDS.yAxisID === yScale.id && posDS.bar) {
base += posDS.data[index] > 0 ? posDS.data[index] : 0;
}
}
}
return yScale.getPixelForValue(base);
}
base = yScale.getPixelForValue(yScale.min);
if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) {
base = yScale.getPixelForValue(0, 0);
//base += yScale.options.gridLines.lineWidth;
} else if (yScale.min < 0 && yScale.max < 0) {
// All values are negative. Use the top as the base
base = yScale.getPixelForValue(yScale.max);
}
return base;
},
getRuler: function() {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var datasetCount = this.getBarCount();
var tickWidth = (function() {
var min = xScale.getPixelForTick(1) - xScale.getPixelForTick(0);
for (var i = 2; i < this.getDataset().data.length; i++) {
min = Math.min(xScale.getPixelForTick(i) - xScale.getPixelForTick(i - 1), min);
}
return min;
}).call(this);
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
var fullBarWidth = categoryWidth / datasetCount;
var barWidth = fullBarWidth * xScale.options.barPercentage;
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
return {
datasetCount: datasetCount,
tickWidth: tickWidth,
categoryWidth: categoryWidth,
categorySpacing: categorySpacing,
fullBarWidth: fullBarWidth,
barWidth: barWidth,
barSpacing: barSpacing
};
},
calculateBarWidth: function() {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var ruler = this.getRuler();
return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
},
// Get bar index from the given dataset index accounting for the fact that not all bars are visible
getBarIndex: function(datasetIndex) {
var barIndex = 0;
for (var j = 0; j < datasetIndex; ++j) {
if (helpers.isDatasetVisible(this.chart.data.datasets[j]) && this.chart.data.datasets[j].bar) {
++barIndex;
}
}
return barIndex;
},
calculateBarX: function(index, datasetIndex) {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var barIndex = this.getBarIndex(datasetIndex);
var ruler = this.getRuler();
var leftTick = xScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo);
leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0;
if (xScale.options.stacked) {
return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
}
return leftTick +
(ruler.barWidth / 2) +
ruler.categorySpacing +
(ruler.barWidth * barIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * barIndex);
},
calculateBarY: function(index, datasetIndex) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var value = this.getDataset().data[index];
if (yScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
var ds = this.chart.data.datasets[i];
if (helpers.isDatasetVisible(ds) && ds.bar && ds.yAxisID === yScale.id) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
sumPos += ds.data[index] || 0;
}
}
}
if (value < 0) {
return yScale.getPixelForValue(sumNeg + value);
} else {
return yScale.getPixelForValue(sumPos + value);
}
return yScale.getPixelForValue(value);
}
return yScale.getPixelForValue(value);
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(rectangle, index) {
var d = this.getDataset().data[index];
if (d !== null && d !== undefined && !isNaN(d)) {
rectangle.transition(easingDecimal).draw();
}
}, this);
},
setHoverStyle: function(rectangle) {
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
var index = rectangle._index;
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, rectangle._model.borderWidth);
},
removeHoverStyle: function(rectangle) {
var dataset = this.chart.data.datasets[rectangle._datasetIndex];
var index = rectangle._index;
rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor);
rectangle._model.borderColor = rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor);
rectangle._model.borderWidth = rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth);
}
});
};
},{}],16:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.bubble = {
hover: {
mode: "single"
},
scales: {
xAxes: [{
type: "linear", // bubble should probably use a linear scale by default
position: "bottom",
id: "x-axis-0" // need an ID so datasets can reference the scale
}],
yAxes: [{
type: "linear",
position: "left",
id: "y-axis-0"
}]
},
tooltips: {
callbacks: {
title: function(tooltipItems, data) {
// Title doesn't make sense for scatter since we format the data as a point
return '';
},
label: function(tooltipItem, data) {
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')';
}
}
}
};
Chart.controllers.bubble = Chart.DatasetController.extend({
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Reset the point
this.updateElement(point, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, point);
},
update: function update(reset) {
var points = this.getDataset().metaData;
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
// Update Points
helpers.each(points, function(point, index) {
this.updateElement(point, index, reset);
}, this);
},
updateElement: function(point, index, reset) {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
helpers.extend(point, {
// Utility
_chart: this.chart.chart,
_xScale: xScale,
_yScale: yScale,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo),
y: reset ? scaleBase : yScale.getPixelForValue(this.getDataset().data[index], index, this.index),
// Appearance
radius: reset ? 0 : point.custom && point.custom.radius ? point.custom.radius : this.getRadius(this.getDataset().data[index]),
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.point.backgroundColor),
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.point.borderColor),
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.point.borderWidth),
// Tooltip
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius)
}
});
point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
point.pivot();
},
getRadius: function(value) {
return value.r || this.chart.options.elements.point.radius;
},
draw: function(ease) {
var easingDecimal = ease || 1;
// Transition and Draw the Points
helpers.each(this.getDataset().metaData, function(point, index) {
point.transition(easingDecimal);
point.draw();
});
},
setHoverStyle: function(point) {
// Point
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, this.chart.options.elements.point.hoverRadius)) + this.getRadius(this.getDataset().data[point._index]);
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, point._model.borderWidth);
},
removeHoverStyle: function(point) {
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : this.getRadius(this.getDataset().data[point._index]);
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.point.backgroundColor);
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.point.borderColor);
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.point.borderWidth);
}
});
};
},{}],17:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.doughnut = {
animation: {
//Boolean - Whether we animate the rotation of the Doughnut
animateRotate: true,
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale: false
},
aspectRatio: 1,
hover: {
mode: 'single'
},
legendCallback: function(chart) {
var text = [];
text.push('');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push(' ');
}
}
text.push(' ');
return text.join("");
},
legend: {
labels: {
generateLabels: function(data) {
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var ds = data.datasets[0];
var arc = ds.metaData[i];
var fill = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(ds.backgroundColor, i, this.chart.options.elements.arc.backgroundColor);
var stroke = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(ds.borderColor, i, this.chart.options.elements.arc.borderColor);
var bw = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(ds.borderWidth, i, this.chart.options.elements.arc.borderWidth);
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(data.datasets[0].data[i]),
// Extra data used for toggling the correct item
index: i
};
}, this);
} else {
return [];
}
}
},
onClick: function(e, legendItem) {
helpers.each(this.chart.data.datasets, function(dataset) {
dataset.metaHiddenData = dataset.metaHiddenData || [];
var idx = legendItem.index;
if (!isNaN(dataset.data[idx])) {
dataset.metaHiddenData[idx] = dataset.data[idx];
dataset.data[idx] = NaN;
} else if (!isNaN(dataset.metaHiddenData[idx])) {
dataset.data[idx] = dataset.metaHiddenData[idx];
}
});
this.chart.update();
}
},
//The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
//The rotation of the chart, where the first data arc begins.
rotation: Math.PI * -0.5,
//The total circumference of the chart.
circumference: Math.PI * 2.0,
// Need to override these to give a nice default
tooltips: {
callbacks: {
title: function() {
return '';
},
label: function(tooltipItem, data) {
return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
}
}
}
};
Chart.defaults.pie = helpers.clone(Chart.defaults.doughnut);
helpers.extend(Chart.defaults.pie, {
cutoutPercentage: 0
});
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
linkScales: function() {
// no scales for doughnut
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index, colorForNewElement) {
this.getDataset().metaData = this.getDataset().metaData || [];
var arc = new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
if (colorForNewElement && helpers.isArray(this.getDataset().backgroundColor)) {
this.getDataset().backgroundColor.splice(index, 0, colorForNewElement);
}
// Reset the point
this.updateElement(arc, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, arc);
},
getVisibleDatasetCount: function getVisibleDatasetCount() {
return helpers.where(this.chart.data.datasets, function(ds) {
return helpers.isDatasetVisible(ds);
}).length;
},
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
getRingIndex: function getRingIndex(datasetIndex) {
var ringIndex = 0;
for (var j = 0; j < datasetIndex; ++j) {
if (helpers.isDatasetVisible(this.chart.data.datasets[j])) {
++ringIndex;
}
}
return ringIndex;
},
update: function update(reset) {
var availableWidth = this.chart.chartArea.right - this.chart.chartArea.left - this.chart.options.elements.arc.borderWidth;
var availableHeight = this.chart.chartArea.bottom - this.chart.chartArea.top - this.chart.options.elements.arc.borderWidth;
var minSize = Math.min(availableWidth, availableHeight);
var offset = {x: 0, y: 0};
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
if (this.chart.options.circumference < Math.PI * 2.0) {
var startAngle = this.chart.options.rotation % (Math.PI * 2.0);
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
var endAngle = startAngle + this.chart.options.circumference;
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
var cutout = this.chart.options.cutoutPercentage / 100.0;
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
}
this.chart.outerRadius = Math.max(minSize / 2, 0);
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount();
this.chart.offsetX = offset.x * this.chart.outerRadius;
this.chart.offsetY = offset.y * this.chart.outerRadius;
this.getDataset().total = 0;
helpers.each(this.getDataset().data, function(value) {
if (!isNaN(value)) {
this.getDataset().total += Math.abs(value);
}
}, this);
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.getRingIndex(this.index));
this.innerRadius = this.outerRadius - this.chart.radiusLength;
helpers.each(this.getDataset().metaData, function(arc, index) {
this.updateElement(arc, index, reset);
}, this);
},
updateElement: function(arc, index, reset) {
var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2;
var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2;
var startAngle = this.chart.options.rotation; // non reset case handled later
var endAngle = this.chart.options.rotation; // non reset case handled later
var circumference = reset && this.chart.options.animation.animateRotate ? 0 : this.calculateCircumference(this.getDataset().data[index]) * (this.chart.options.circumference / (2.0 * Math.PI));
var innerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.innerRadius;
var outerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.outerRadius;
helpers.extend(arc, {
// Utility
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: centerX + this.chart.offsetX,
y: centerY + this.chart.offsetY,
startAngle: startAngle,
endAngle: endAngle,
circumference: circumference,
outerRadius: outerRadius,
innerRadius: innerRadius,
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index])
}
});
// Set correct angles if not resetting
if (!reset) {
if (index === 0) {
arc._model.startAngle = this.chart.options.rotation;
} else {
arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle;
}
arc._model.endAngle = arc._model.startAngle + arc._model.circumference;
}
arc.pivot();
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(arc, index) {
arc.transition(easingDecimal).draw();
});
},
setHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth);
},
removeHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
},
calculateCircumference: function(value) {
if (this.getDataset().total > 0 && !isNaN(value)) {
return (Math.PI * 2.0) * (value / this.getDataset().total);
} else {
return 0;
}
}
});
};
},{}],18:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.line = {
showLines: true,
hover: {
mode: "label"
},
scales: {
xAxes: [{
type: "category",
id: 'x-axis-0'
}],
yAxes: [{
type: "linear",
id: 'y-axis-0'
}]
}
};
Chart.controllers.line = Chart.DatasetController.extend({
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
_chart: this.chart.chart,
_datasetIndex: this.index,
_points: this.getDataset().metaData
});
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Reset the point
this.updateElement(point, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, point);
// Make sure bezier control points are updated
if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0)
this.updateBezierControlPoints();
},
update: function update(reset) {
var line = this.getDataset().metaDataset;
var points = this.getDataset().metaData;
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
// Update Line
if (this.chart.options.showLines) {
// Utility
line._scale = yScale;
line._datasetIndex = this.index;
// Data
line._children = points;
// Model
line._model = {
// Appearance
tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension),
backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor),
borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth),
borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor),
borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle),
borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash),
borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset),
borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle),
fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill),
// Scale
scaleTop: yScale.top,
scaleBottom: yScale.bottom,
scaleZero: scaleBase
};
line.pivot();
}
// Update Points
helpers.each(points, function(point, index) {
this.updateElement(point, index, reset);
}, this);
if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0)
this.updateBezierControlPoints();
},
getPointBackgroundColor: function(point, index) {
var backgroundColor = this.chart.options.elements.point.backgroundColor;
var dataset = this.getDataset();
if (point.custom && point.custom.backgroundColor) {
backgroundColor = point.custom.backgroundColor;
} else if (dataset.pointBackgroundColor) {
backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
} else if (dataset.backgroundColor) {
backgroundColor = dataset.backgroundColor;
}
return backgroundColor;
},
getPointBorderColor: function(point, index) {
var borderColor = this.chart.options.elements.point.borderColor;
var dataset = this.getDataset();
if (point.custom && point.custom.borderColor) {
borderColor = point.custom.borderColor;
} else if (dataset.pointBorderColor) {
borderColor = helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, borderColor);
} else if (dataset.borderColor) {
borderColor = dataset.borderColor;
}
return borderColor;
},
getPointBorderWidth: function(point, index) {
var borderWidth = this.chart.options.elements.point.borderWidth;
var dataset = this.getDataset();
if (point.custom && point.custom.borderWidth !== undefined) {
borderWidth = point.custom.borderWidth;
} else if (dataset.pointBorderWidth !== undefined) {
borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
} else if (dataset.borderWidth !== undefined) {
borderWidth = dataset.borderWidth;
}
return borderWidth;
},
updateElement: function(point, index, reset) {
var yScale = this.getScaleForId(this.getDataset().yAxisID);
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var scaleBase;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
} else if (yScale.min > 0 && yScale.max > 0) {
scaleBase = yScale.getPixelForValue(yScale.min);
} else {
scaleBase = yScale.getPixelForValue(0);
}
// Utility
point._chart = this.chart.chart;
point._xScale = xScale;
point._yScale = yScale;
point._datasetIndex = this.index;
point._index = index;
// Desired view properties
point._model = {
x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo),
y: reset ? scaleBase : this.calculatePointY(this.getDataset().data[index], index, this.index, this.chart.isCombo),
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension),
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
pointStyle: point.custom && point.custom.pointStyle ? point.custom.pointStyle : helpers.getValueAtIndexOrDefault(this.getDataset().pointStyle, index, this.chart.options.elements.point.pointStyle),
backgroundColor: this.getPointBackgroundColor(point, index),
borderColor: this.getPointBorderColor(point, index),
borderWidth: this.getPointBorderWidth(point, index),
// Tooltip
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius)
};
point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
},
calculatePointY: function(value, index, datasetIndex, isCombo) {
var xScale = this.getScaleForId(this.getDataset().xAxisID);
var yScale = this.getScaleForId(this.getDataset().yAxisID);
if (yScale.options.stacked) {
var sumPos = 0,
sumNeg = 0;
for (var i = 0; i < datasetIndex; i++) {
var ds = this.chart.data.datasets[i];
if (ds.type === 'line' && helpers.isDatasetVisible(ds)) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
sumPos += ds.data[index] || 0;
}
}
}
if (value < 0) {
return yScale.getPixelForValue(sumNeg + value);
} else {
return yScale.getPixelForValue(sumPos + value);
}
}
return yScale.getPixelForValue(value);
},
updateBezierControlPoints: function() {
// Update bezier control points
helpers.each(this.getDataset().metaData, function(point, index) {
var controlPoints = helpers.splineCurve(
helpers.previousItem(this.getDataset().metaData, index)._model,
point._model,
helpers.nextItem(this.getDataset().metaData, index)._model,
point._model.tension
);
// Prevent the bezier going outside of the bounds of the graph
point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Now pivot the point for animation
point.pivot();
}, this);
},
draw: function(ease) {
var easingDecimal = ease || 1;
// Transition Point Locations
helpers.each(this.getDataset().metaData, function(point) {
point.transition(easingDecimal);
});
// Transition and Draw the line
if (this.chart.options.showLines)
this.getDataset().metaDataset.transition(easingDecimal).draw();
// Draw the points
helpers.each(this.getDataset().metaData, function(point) {
point.draw();
});
},
setHoverStyle: function(point) {
// Point
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, point._model.borderWidth);
},
removeHoverStyle: function(point) {
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
point._model.backgroundColor = this.getPointBackgroundColor(point, index);
point._model.borderColor = this.getPointBorderColor(point, index);
point._model.borderWidth = this.getPointBorderWidth(point, index);
}
});
};
},{}],19:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.polarArea = {
scale: {
type: "radialLinear",
lineArc: true // so that lines are circular
},
//Boolean - Whether to animate the rotation of the chart
animateRotate: true,
animateScale: true,
aspectRatio: 1,
legendCallback: function(chart) {
var text = [];
text.push('');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push(' ');
}
}
text.push(' ');
return text.join("");
},
legend: {
labels: {
generateLabels: function(data) {
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var ds = data.datasets[0];
var arc = ds.metaData[i];
var fill = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(ds.backgroundColor, i, this.chart.options.elements.arc.backgroundColor);
var stroke = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(ds.borderColor, i, this.chart.options.elements.arc.borderColor);
var bw = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(ds.borderWidth, i, this.chart.options.elements.arc.borderWidth);
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(data.datasets[0].data[i]),
// Extra data used for toggling the correct item
index: i
};
}, this);
} else {
return [];
}
}
},
onClick: function(e, legendItem) {
helpers.each(this.chart.data.datasets, function(dataset) {
dataset.metaHiddenData = dataset.metaHiddenData || [];
var idx = legendItem.index;
if (!isNaN(dataset.data[idx])) {
dataset.metaHiddenData[idx] = dataset.data[idx];
dataset.data[idx] = NaN;
} else if (!isNaN(dataset.metaHiddenData[idx])) {
dataset.data[idx] = dataset.metaHiddenData[idx];
}
});
this.chart.update();
}
},
// Need to override these to give a nice default
tooltips: {
callbacks: {
title: function() {
return '';
},
label: function(tooltipItem, data) {
return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel;
}
}
}
};
Chart.controllers.polarArea = Chart.DatasetController.extend({
linkScales: function() {
// no scales for doughnut
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var arc = new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Reset the point
this.updateElement(arc, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, arc);
},
getVisibleDatasetCount: function getVisibleDatasetCount() {
return helpers.where(this.chart.data.datasets, function(ds) {
return helpers.isDatasetVisible(ds);
}).length;
},
update: function update(reset) {
var minSize = Math.min(this.chart.chartArea.right - this.chart.chartArea.left, this.chart.chartArea.bottom - this.chart.chartArea.top);
this.chart.outerRadius = Math.max((minSize - this.chart.options.elements.arc.borderWidth / 2) / 2, 0);
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount();
this.getDataset().total = 0;
helpers.each(this.getDataset().data, function(value) {
this.getDataset().total += Math.abs(value);
}, this);
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
this.innerRadius = this.outerRadius - this.chart.radiusLength;
helpers.each(this.getDataset().metaData, function(arc, index) {
this.updateElement(arc, index, reset);
}, this);
},
updateElement: function(arc, index, reset) {
var circumference = this.calculateCircumference(this.getDataset().data[index]);
var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2;
var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2;
// If there is NaN data before us, we need to calculate the starting angle correctly.
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
var notNullIndex = 0;
for (var i = 0; i < index; ++i) {
if (!isNaN(this.getDataset().data[i])) {
++notNullIndex;
}
}
var startAngle = (-0.5 * Math.PI) + (circumference * notNullIndex);
var endAngle = startAngle + circumference;
var resetModel = {
x: centerX,
y: centerY,
innerRadius: 0,
outerRadius: this.chart.options.animateScale ? 0 : this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
startAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : startAngle,
endAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : endAngle,
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
};
helpers.extend(arc, {
// Utility
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
_scale: this.chart.scale,
// Desired view properties
_model: reset ? resetModel : {
x: centerX,
y: centerY,
innerRadius: 0,
outerRadius: this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]),
startAngle: startAngle,
endAngle: endAngle,
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index])
}
});
arc.pivot();
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(arc, index) {
arc.transition(easingDecimal).draw();
});
},
setHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth);
},
removeHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
},
calculateCircumference: function(value) {
if (isNaN(value)) {
return 0;
} else {
// Count the number of NaN values
var numNaN = helpers.where(this.getDataset().data, function(data) {
return isNaN(data);
}).length;
return (2 * Math.PI) / (this.getDataset().data.length - numNaN);
}
}
});
};
},{}],20:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.radar = {
scale: {
type: "radialLinear"
},
elements: {
line: {
tension: 0 // no bezier in radar
}
}
};
Chart.controllers.radar = Chart.DatasetController.extend({
linkScales: function() {
// No need. Single scale only
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({
_chart: this.chart.chart,
_datasetIndex: this.index,
_points: this.getDataset().metaData,
_loop: true
});
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
_model: {
x: 0, //xScale.getPixelForValue(null, index, true),
y: 0 //this.chartArea.bottom,
}
});
}, this);
},
addElementAndReset: function(index) {
this.getDataset().metaData = this.getDataset().metaData || [];
var point = new Chart.elements.Point({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
// Reset the point
this.updateElement(point, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, point);
// Make sure bezier control points are updated
this.updateBezierControlPoints();
},
update: function update(reset) {
var line = this.getDataset().metaDataset;
var points = this.getDataset().metaData;
var scale = this.chart.scale;
var scaleBase;
if (scale.min < 0 && scale.max < 0) {
scaleBase = scale.getPointPositionForValue(0, scale.max);
} else if (scale.min > 0 && scale.max > 0) {
scaleBase = scale.getPointPositionForValue(0, scale.min);
} else {
scaleBase = scale.getPointPositionForValue(0, 0);
}
helpers.extend(this.getDataset().metaDataset, {
// Utility
_datasetIndex: this.index,
// Data
_children: this.getDataset().metaData,
// Model
_model: {
// Appearance
tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension),
backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor),
borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth),
borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor),
fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill),
borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle),
borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash),
borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset),
borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle),
// Scale
scaleTop: scale.top,
scaleBottom: scale.bottom,
scaleZero: scaleBase
}
});
this.getDataset().metaDataset.pivot();
// Update Points
helpers.each(points, function(point, index) {
this.updateElement(point, index, reset);
}, this);
// Update bezier control points
this.updateBezierControlPoints();
},
updateElement: function(point, index, reset) {
var pointPosition = this.chart.scale.getPointPositionForValue(index, this.getDataset().data[index]);
helpers.extend(point, {
// Utility
_datasetIndex: this.index,
_index: index,
_scale: this.chart.scale,
// Desired view properties
_model: {
x: reset ? this.chart.scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
y: reset ? this.chart.scale.yCenter : pointPosition.y,
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension),
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius),
backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor),
borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor),
borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth),
pointStyle: point.custom && point.custom.pointStyle ? point.custom.pointStyle : helpers.getValueAtIndexOrDefault(this.getDataset().pointStyle, index, this.chart.options.elements.point.pointStyle),
// Tooltip
hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius)
}
});
point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
},
updateBezierControlPoints: function() {
helpers.each(this.getDataset().metaData, function(point, index) {
var controlPoints = helpers.splineCurve(
helpers.previousItem(this.getDataset().metaData, index, true)._model,
point._model,
helpers.nextItem(this.getDataset().metaData, index, true)._model,
point._model.tension
);
// Prevent the bezier going outside of the bounds of the graph
point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Now pivot the point for animation
point.pivot();
}, this);
},
draw: function(ease) {
var easingDecimal = ease || 1;
// Transition Point Locations
helpers.each(this.getDataset().metaData, function(point, index) {
point.transition(easingDecimal);
});
// Transition and Draw the line
this.getDataset().metaDataset.transition(easingDecimal).draw();
// Draw the points
helpers.each(this.getDataset().metaData, function(point) {
point.draw();
});
},
setHoverStyle: function(point) {
// Point
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString());
point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, point._model.borderWidth);
},
removeHoverStyle: function(point) {
var dataset = this.chart.data.datasets[point._datasetIndex];
var index = point._index;
point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius);
point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor);
point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor);
point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth);
}
});
};
},{}],21:[function(require,module,exports){
/*global window: false */
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.animation = {
duration: 1000,
easing: "easeOutQuart",
onProgress: helpers.noop,
onComplete: helpers.noop
};
Chart.Animation = Chart.Element.extend({
currentStep: null, // the current animation step
numSteps: 60, // default number of steps
easing: "", // the easing to use for this animation
render: null, // render function used by the animation service
onAnimationProgress: null, // user specified callback to fire on each step of the animation
onAnimationComplete: null // user specified callback to fire when the animation finishes
});
Chart.animationService = {
frameDuration: 17,
animations: [],
dropFrames: 0,
request: null,
addAnimation: function(chartInstance, animationObject, duration, lazy) {
if (!lazy) {
chartInstance.animating = true;
}
for (var index = 0; index < this.animations.length; ++index) {
if (this.animations[index].chartInstance === chartInstance) {
// replacing an in progress animation
this.animations[index].animationObject = animationObject;
return;
}
}
this.animations.push({
chartInstance: chartInstance,
animationObject: animationObject
});
// If there are no animations queued, manually kickstart a digest, for lack of a better word
if (this.animations.length === 1) {
this.requestAnimationFrame();
}
},
// Cancel the animation for a given chart instance
cancelAnimation: function(chartInstance) {
var index = helpers.findIndex(this.animations, function(animationWrapper) {
return animationWrapper.chartInstance === chartInstance;
});
if (index !== -1) {
this.animations.splice(index, 1);
chartInstance.animating = false;
}
},
requestAnimationFrame: function() {
var me = this;
if (me.request === null) {
// Skip animation frame requests until the active one is executed.
// This can happen when processing mouse events, e.g. 'mousemove'
// and 'mouseout' events will trigger multiple renders.
me.request = helpers.requestAnimFrame.call(window, function() {
me.request = null;
me.startDigest();
});
}
},
startDigest: function() {
var startTime = Date.now();
var framesToDrop = 0;
if (this.dropFrames > 1) {
framesToDrop = Math.floor(this.dropFrames);
this.dropFrames = this.dropFrames % 1;
}
var i = 0;
while (i < this.animations.length) {
if (this.animations[i].animationObject.currentStep === null) {
this.animations[i].animationObject.currentStep = 0;
}
this.animations[i].animationObject.currentStep += 1 + framesToDrop;
if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) {
this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
}
this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
if (this.animations[i].animationObject.onAnimationProgress && this.animations[i].animationObject.onAnimationProgress.call) {
this.animations[i].animationObject.onAnimationProgress.call(this.animations[i].chartInstance, this.animations[i]);
}
if (this.animations[i].animationObject.currentStep === this.animations[i].animationObject.numSteps) {
if (this.animations[i].animationObject.onAnimationComplete && this.animations[i].animationObject.onAnimationComplete.call) {
this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance, this.animations[i]);
}
// executed the last frame. Remove the animation.
this.animations[i].chartInstance.animating = false;
this.animations.splice(i, 1);
} else {
++i;
}
}
var endTime = Date.now();
var dropFrames = (endTime - startTime) / this.frameDuration;
this.dropFrames += dropFrames;
// Do we have more stuff to animate?
if (this.animations.length > 0) {
this.requestAnimationFrame();
}
}
};
};
},{}],22:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
//Create a dictionary of chart types, to allow for extension of existing types
Chart.types = {};
//Store a reference to each instance - allowing us to globally resize chart instances on window resize.
//Destroy method on the chart will remove the instance of the chart from this reference.
Chart.instances = {};
// Controllers available for dataset visualization eg. bar, line, slice, etc.
Chart.controllers = {};
// The main controller of a chart
Chart.Controller = function(instance) {
this.chart = instance;
this.config = instance.config;
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
this.id = helpers.uid();
Object.defineProperty(this, 'data', {
get: function() {
return this.config.data;
}
});
//Add the chart instance to the global namespace
Chart.instances[this.id] = this;
if (this.options.responsive) {
// Silent resize before chart draws
this.resize(true);
}
this.initialize();
return this;
};
helpers.extend(Chart.Controller.prototype, {
initialize: function initialize() {
// Before init plugin notification
Chart.pluginService.notifyPlugins('beforeInit', [this]);
this.bindEvents();
// Make sure controllers are built first so that each dataset is bound to an axis before the scales
// are built
this.ensureScalesHaveIDs();
this.buildOrUpdateControllers();
this.buildScales();
this.buildSurroundingItems();
this.updateLayout();
this.resetElements();
this.initToolTip();
this.update();
// After init plugin notification
Chart.pluginService.notifyPlugins('afterInit', [this]);
return this;
},
clear: function clear() {
helpers.clear(this.chart);
return this;
},
stop: function stop() {
// Stops any current animation loop occuring
Chart.animationService.cancelAnimation(this);
return this;
},
resize: function resize(silent) {
var canvas = this.chart.canvas;
var newWidth = helpers.getMaximumWidth(this.chart.canvas);
var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas);
var sizeChanged = this.chart.width !== newWidth || this.chart.height !== newHeight;
if (!sizeChanged)
return this;
canvas.width = this.chart.width = newWidth;
canvas.height = this.chart.height = newHeight;
helpers.retinaScale(this.chart);
if (!silent) {
this.stop();
this.update(this.options.responsiveAnimationDuration);
}
return this;
},
ensureScalesHaveIDs: function ensureScalesHaveIDs() {
var defaultXAxisID = 'x-axis-';
var defaultYAxisID = 'y-axis-';
if (this.options.scales) {
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index);
});
}
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
// Build the y axes
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index);
});
}
}
},
buildScales: function buildScales() {
// Map of scale ID to scale object so we can lookup later
this.scales = {};
// Build the x axes
if (this.options.scales) {
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
var xType = helpers.getValueOrDefault(xAxisOptions.type, 'category');
var ScaleClass = Chart.scaleService.getScaleConstructor(xType);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: xAxisOptions,
chart: this,
id: xAxisOptions.id
});
this.scales[scale.id] = scale;
}
}, this);
}
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
// Build the y axes
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
var yType = helpers.getValueOrDefault(yAxisOptions.type, 'linear');
var ScaleClass = Chart.scaleService.getScaleConstructor(yType);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: yAxisOptions,
chart: this,
id: yAxisOptions.id
});
this.scales[scale.id] = scale;
}
}, this);
}
}
if (this.options.scale) {
// Build radial axes
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type);
if (ScaleClass) {
var scale = new ScaleClass({
ctx: this.chart.ctx,
options: this.options.scale,
chart: this
});
this.scale = scale;
this.scales.radialScale = scale;
}
}
Chart.scaleService.addScalesToLayout(this);
},
buildSurroundingItems: function() {
if (this.options.title) {
this.titleBlock = new Chart.Title({
ctx: this.chart.ctx,
options: this.options.title,
chart: this
});
Chart.layoutService.addBox(this, this.titleBlock);
}
if (this.options.legend) {
this.legend = new Chart.Legend({
ctx: this.chart.ctx,
options: this.options.legend,
chart: this
});
Chart.layoutService.addBox(this, this.legend);
}
},
updateLayout: function() {
Chart.layoutService.update(this, this.chart.width, this.chart.height);
},
buildOrUpdateControllers: function buildOrUpdateControllers() {
var types = [];
var newControllers = [];
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
if (!dataset.type) {
dataset.type = this.config.type;
}
var type = dataset.type;
types.push(type);
if (dataset.controller) {
dataset.controller.updateIndex(datasetIndex);
} else {
dataset.controller = new Chart.controllers[type](this, datasetIndex);
newControllers.push(dataset.controller);
}
}, this);
if (types.length > 1) {
for (var i = 1; i < types.length; i++) {
if (types[i] !== types[i - 1]) {
this.isCombo = true;
break;
}
}
}
return newControllers;
},
resetElements: function resetElements() {
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.controller.reset();
});
},
update: function update(animationDuration, lazy) {
Chart.pluginService.notifyPlugins('beforeUpdate', [this]);
// In case the entire data object changed
this.tooltip._data = this.data;
// Make sure dataset controllers are updated and new controllers are reset
var newControllers = this.buildOrUpdateControllers();
Chart.layoutService.update(this, this.chart.width, this.chart.height);
// Can only reset the new controllers after the scales have been updated
helpers.each(newControllers, function(controller) {
controller.reset();
});
// Make sure all dataset controllers have correct meta data counts
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.controller.buildOrUpdateElements();
});
// This will loop through any data and do the appropriate element update for the type
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
dataset.controller.update();
});
this.render(animationDuration, lazy);
Chart.pluginService.notifyPlugins('afterUpdate', [this]);
},
render: function render(duration, lazy) {
if (this.options.animation && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && this.options.animation.duration !== 0))) {
var animation = new Chart.Animation();
animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps
animation.easing = this.options.animation.easing;
// render function
animation.render = function(chartInstance, animationObject) {
var easingFunction = helpers.easingEffects[animationObject.easing];
var stepDecimal = animationObject.currentStep / animationObject.numSteps;
var easeDecimal = easingFunction(stepDecimal);
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
};
// user events
animation.onAnimationProgress = this.options.animation.onProgress;
animation.onAnimationComplete = this.options.animation.onComplete;
Chart.animationService.addAnimation(this, animation, duration, lazy);
} else {
this.draw();
if (this.options.animation && this.options.animation.onComplete && this.options.animation.onComplete.call) {
this.options.animation.onComplete.call(this);
}
}
return this;
},
draw: function(ease) {
var easingDecimal = ease || 1;
this.clear();
Chart.pluginService.notifyPlugins('beforeDraw', [this, easingDecimal]);
// Draw all the scales
helpers.each(this.boxes, function(box) {
box.draw(this.chartArea);
}, this);
if (this.scale) {
this.scale.draw();
}
// Clip out the chart area so that anything outside does not draw. This is necessary for zoom and pan to function
this.chart.ctx.save();
this.chart.ctx.beginPath();
this.chart.ctx.rect(this.chartArea.left, this.chartArea.top, this.chartArea.right - this.chartArea.left, this.chartArea.bottom - this.chartArea.top);
this.chart.ctx.clip();
// Draw each dataset via its respective controller (reversed to support proper line stacking)
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
if (helpers.isDatasetVisible(dataset)) {
dataset.controller.draw(ease);
}
}, null, true);
// Restore from the clipping operation
this.chart.ctx.restore();
// Finally draw the tooltip
this.tooltip.transition(easingDecimal).draw();
Chart.pluginService.notifyPlugins('afterDraw', [this, easingDecimal]);
},
// Get the single element that was clicked on
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
getElementAtEvent: function(e) {
var eventPosition = helpers.getRelativePosition(e, this.chart);
var elementsArray = [];
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
if (helpers.isDatasetVisible(dataset)) {
helpers.each(dataset.metaData, function(element, index) {
if (element.inRange(eventPosition.x, eventPosition.y)) {
elementsArray.push(element);
return elementsArray;
}
});
}
});
return elementsArray;
},
getElementsAtEvent: function(e) {
var eventPosition = helpers.getRelativePosition(e, this.chart);
var elementsArray = [];
var found = (function() {
if (this.data.datasets) {
for (var i = 0; i < this.data.datasets.length; i++) {
if (helpers.isDatasetVisible(this.data.datasets[i])) {
for (var j = 0; j < this.data.datasets[i].metaData.length; j++) {
if (this.data.datasets[i].metaData[j].inRange(eventPosition.x, eventPosition.y)) {
return this.data.datasets[i].metaData[j];
}
}
}
}
}
}).call(this);
if (!found) {
return elementsArray;
}
helpers.each(this.data.datasets, function(dataset, dsIndex) {
if (helpers.isDatasetVisible(dataset)) {
elementsArray.push(dataset.metaData[found._index]);
}
});
return elementsArray;
},
getDatasetAtEvent: function(e) {
var elementsArray = this.getElementAtEvent(e);
if (elementsArray.length > 0) {
elementsArray = this.data.datasets[elementsArray[0]._datasetIndex].metaData;
}
return elementsArray;
},
generateLegend: function generateLegend() {
return this.options.legendCallback(this);
},
destroy: function destroy() {
this.clear();
helpers.unbindEvents(this, this.events);
helpers.removeResizeListener(this.chart.canvas.parentNode);
// Reset canvas height/width attributes
var canvas = this.chart.canvas;
canvas.width = this.chart.width;
canvas.height = this.chart.height;
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
if (this.chart.originalDevicePixelRatio !== undefined) {
this.chart.ctx.scale(1 / this.chart.originalDevicePixelRatio, 1 / this.chart.originalDevicePixelRatio);
}
// Reset to the old style since it may have been changed by the device pixel ratio changes
canvas.style.width = this.chart.originalCanvasStyleWidth;
canvas.style.height = this.chart.originalCanvasStyleHeight;
delete Chart.instances[this.id];
},
toBase64Image: function toBase64Image() {
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
},
initToolTip: function initToolTip() {
this.tooltip = new Chart.Tooltip({
_chart: this.chart,
_chartInstance: this,
_data: this.data,
_options: this.options
}, this);
},
bindEvents: function bindEvents() {
helpers.bindEvents(this, this.options.events, function(evt) {
this.eventHandler(evt);
});
},
eventHandler: function eventHandler(e) {
this.lastActive = this.lastActive || [];
this.lastTooltipActive = this.lastTooltipActive || [];
// Find Active Elements for hover and tooltips
if (e.type === 'mouseout') {
this.active = [];
this.tooltipActive = [];
} else {
var _this = this;
var getItemsForMode = function(mode) {
switch (mode) {
case 'single':
return _this.getElementAtEvent(e);
case 'label':
return _this.getElementsAtEvent(e);
case 'dataset':
return _this.getDatasetAtEvent(e);
default:
return e;
}
};
this.active = getItemsForMode(this.options.hover.mode);
this.tooltipActive = getItemsForMode(this.options.tooltips.mode);
}
// On Hover hook
if (this.options.hover.onHover) {
this.options.hover.onHover.call(this, this.active);
}
if (e.type === 'mouseup' || e.type === 'click') {
if (this.options.onClick) {
this.options.onClick.call(this, e, this.active);
}
if (this.legend && this.legend.handleEvent) {
this.legend.handleEvent(e);
}
}
var dataset;
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
switch (this.options.hover.mode) {
case 'single':
this.data.datasets[this.lastActive[0]._datasetIndex].controller.removeHoverStyle(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
break;
case 'label':
case 'dataset':
for (var i = 0; i < this.lastActive.length; i++) {
if (this.lastActive[i])
this.data.datasets[this.lastActive[i]._datasetIndex].controller.removeHoverStyle(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
}
break;
default:
// Don't change anything
}
}
// Built in hover styling
if (this.active.length && this.options.hover.mode) {
switch (this.options.hover.mode) {
case 'single':
this.data.datasets[this.active[0]._datasetIndex].controller.setHoverStyle(this.active[0]);
break;
case 'label':
case 'dataset':
for (var j = 0; j < this.active.length; j++) {
if (this.active[j])
this.data.datasets[this.active[j]._datasetIndex].controller.setHoverStyle(this.active[j]);
}
break;
default:
// Don't change anything
}
}
// Built in Tooltips
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
// The usual updates
this.tooltip.initialize();
this.tooltip._active = this.tooltipActive;
this.tooltip.update();
}
// Hover animations
this.tooltip.pivot();
if (!this.animating) {
var changed;
helpers.each(this.active, function(element, index) {
if (element !== this.lastActive[index]) {
changed = true;
}
}, this);
helpers.each(this.tooltipActive, function(element, index) {
if (element !== this.lastTooltipActive[index]) {
changed = true;
}
}, this);
// If entering, leaving, or changing elements, animate the change via pivot
if ((this.lastActive.length !== this.active.length) ||
(this.lastTooltipActive.length !== this.tooltipActive.length) ||
changed) {
this.stop();
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
this.tooltip.update(true);
}
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
// memory than necessary.
this.render(this.options.hover.animationDuration, true);
}
}
// Remember Last Actives
this.lastActive = this.active;
this.lastTooltipActive = this.tooltipActive;
return this;
}
});
};
},{}],23:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
// Base class for all dataset controllers (line, bar, etc)
Chart.DatasetController = function(chart, datasetIndex) {
this.initialize.call(this, chart, datasetIndex);
};
helpers.extend(Chart.DatasetController.prototype, {
initialize: function(chart, datasetIndex) {
this.chart = chart;
this.index = datasetIndex;
this.linkScales();
this.addElements();
},
updateIndex: function(datasetIndex) {
this.index = datasetIndex;
},
linkScales: function() {
if (!this.getDataset().xAxisID) {
this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id;
}
if (!this.getDataset().yAxisID) {
this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id;
}
},
getDataset: function() {
return this.chart.data.datasets[this.index];
},
getScaleForId: function(scaleID) {
return this.chart.scales[scaleID];
},
reset: function() {
this.update(true);
},
buildOrUpdateElements: function buildOrUpdateElements() {
// Handle the number of data points changing
var numData = this.getDataset().data.length;
var numMetaData = this.getDataset().metaData.length;
// Make sure that we handle number of datapoints changing
if (numData < numMetaData) {
// Remove excess bars for data points that have been removed
this.getDataset().metaData.splice(numData, numMetaData - numData);
} else if (numData > numMetaData) {
// Add new elements
for (var index = numMetaData; index < numData; ++index) {
this.addElementAndReset(index);
}
}
},
// Controllers should implement the following
addElements: helpers.noop,
addElementAndReset: helpers.noop,
draw: helpers.noop,
removeHoverStyle: helpers.noop,
setHoverStyle: helpers.noop,
update: helpers.noop
});
Chart.DatasetController.extend = helpers.inherits;
};
},{}],24:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.elements = {};
Chart.Element = function(configuration) {
helpers.extend(this, configuration);
this.initialize.apply(this, arguments);
};
helpers.extend(Chart.Element.prototype, {
initialize: function() {},
pivot: function() {
if (!this._view) {
this._view = helpers.clone(this._model);
}
this._start = helpers.clone(this._view);
return this;
},
transition: function(ease) {
if (!this._view) {
this._view = helpers.clone(this._model);
}
// No animation -> No Transition
if (ease === 1) {
this._view = this._model;
this._start = null;
return this;
}
if (!this._start) {
this.pivot();
}
helpers.each(this._model, function(value, key) {
if (key[0] === '_' || !this._model.hasOwnProperty(key)) {
// Only non-underscored properties
}
// Init if doesn't exist
else if (!this._view.hasOwnProperty(key)) {
if (typeof value === 'number' && !isNaN(this._view[key])) {
this._view[key] = value * ease;
} else {
this._view[key] = value;
}
}
// No unnecessary computations
else if (value === this._view[key]) {
// It's the same! Woohoo!
}
// Color transitions if possible
else if (typeof value === 'string') {
try {
var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease);
this._view[key] = color.rgbString();
} catch (err) {
this._view[key] = value;
}
}
// Number transitions
else if (typeof value === 'number') {
var startVal = this._start[key] !== undefined && isNaN(this._start[key]) === false ? this._start[key] : 0;
this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
}
// Everything else
else {
this._view[key] = value;
}
}, this);
return this;
},
tooltipPosition: function() {
return {
x: this._model.x,
y: this._model.y
};
},
hasValue: function() {
return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
}
});
Chart.Element.extend = helpers.inherits;
};
},{}],25:[function(require,module,exports){
/*global window: false */
/*global document: false */
"use strict";
var color = require('chartjs-color');
module.exports = function(Chart) {
//Global Chart helpers object for utility methods and classes
var helpers = Chart.helpers = {};
//-- Basic js utility methods
helpers.each = function(loopable, callback, self, reverse) {
// Check to see if null or undefined firstly.
var i, len;
if (helpers.isArray(loopable)) {
len = loopable.length;
if (reverse) {
for (i = len - 1; i >= 0; i--) {
callback.call(self, loopable[i], i);
}
} else {
for (i = 0; i < len; i++) {
callback.call(self, loopable[i], i);
}
}
} else if (typeof loopable === 'object') {
var keys = Object.keys(loopable);
len = keys.length;
for (i = 0; i < len; i++) {
callback.call(self, loopable[keys[i]], keys[i]);
}
}
};
helpers.clone = function(obj) {
var objClone = {};
helpers.each(obj, function(value, key) {
if (obj.hasOwnProperty(key)) {
if (helpers.isArray(value)) {
objClone[key] = value.slice(0);
} else if (typeof value === 'object' && value !== null) {
objClone[key] = helpers.clone(value);
} else {
objClone[key] = value;
}
}
});
return objClone;
};
helpers.extend = function(base) {
var len = arguments.length;
var additionalArgs = [];
for (var i = 1; i < len; i++) {
additionalArgs.push(arguments[i]);
}
helpers.each(additionalArgs, function(extensionObject) {
helpers.each(extensionObject, function(value, key) {
if (extensionObject.hasOwnProperty(key)) {
base[key] = value;
}
});
});
return base;
};
// Need a special merge function to chart configs since they are now grouped
helpers.configMerge = function(_base) {
var base = helpers.clone(_base);
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
helpers.each(extension, function(value, key) {
if (extension.hasOwnProperty(key)) {
if (key === 'scales') {
// Scale config merging is complex. Add out own function here for that
base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value);
} else if (key === 'scale') {
// Used in polar area & radar charts since there is only one scale
base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value);
} else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
// In this case we have an array of objects replacing another array. Rather than doing a strict replace,
// merge. This allows easy scale option merging
var baseArray = base[key];
helpers.each(value, function(valueObj, index) {
if (index < baseArray.length) {
if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) {
// Two objects are coming together. Do a merge of them.
baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
} else {
// Just overwrite in this case since there is nothing to merge
baseArray[index] = valueObj;
}
} else {
baseArray.push(valueObj); // nothing to merge
}
});
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
// If we are overwriting an object with an object, do a merge of the properties.
base[key] = helpers.configMerge(base[key], value);
} else {
// can just overwrite the value in this case
base[key] = value;
}
}
});
});
return base;
};
helpers.extendDeep = function(_base) {
return _extendDeep.apply(this, arguments);
function _extendDeep(dst) {
helpers.each(arguments, function(obj) {
if (obj !== dst) {
helpers.each(obj, function(value, key) {
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
_extendDeep(dst[key], value);
} else {
dst[key] = value;
}
});
}
});
return dst;
}
};
helpers.scaleMerge = function(_base, extension) {
var base = helpers.clone(_base);
helpers.each(extension, function(value, key) {
if (extension.hasOwnProperty(key)) {
if (key === 'xAxes' || key === 'yAxes') {
// These properties are arrays of items
if (base.hasOwnProperty(key)) {
helpers.each(value, function(valueObj, index) {
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
var axisDefaults = Chart.scaleService.getScaleDefaults(axisType);
if (index >= base[key].length || !base[key][index].type) {
base[key].push(helpers.configMerge(axisDefaults, valueObj));
} else if (valueObj.type && valueObj.type !== base[key][index].type) {
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj);
} else {
// Type is the same
base[key][index] = helpers.configMerge(base[key][index], valueObj);
}
});
} else {
base[key] = [];
helpers.each(value, function(valueObj) {
var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
});
}
} else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") {
// If we are overwriting an object with an object, do a merge of the properties.
base[key] = helpers.configMerge(base[key], value);
} else {
// can just overwrite the value in this case
base[key] = value;
}
}
});
return base;
};
helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
if (value === undefined || value === null) {
return defaultValue;
}
if (helpers.isArray(value)) {
return index < value.length ? value[index] : defaultValue;
}
return value;
};
helpers.getValueOrDefault = function(value, defaultValue) {
return value === undefined ? defaultValue : value;
};
helpers.indexOf = function(arrayToSearch, item) {
if (Array.prototype.indexOf) {
return arrayToSearch.indexOf(item);
} else {
for (var i = 0; i < arrayToSearch.length; i++) {
if (arrayToSearch[i] === item)
return i;
}
return -1;
}
};
helpers.where = function(collection, filterCallback) {
var filtered = [];
helpers.each(collection, function(item) {
if (filterCallback(item)) {
filtered.push(item);
}
});
return filtered;
};
helpers.findIndex = function(arrayToSearch, callback, thisArg) {
var index = -1;
if (Array.prototype.findIndex) {
index = arrayToSearch.findIndex(callback, thisArg);
} else {
for (var i = 0; i < arrayToSearch.length; ++i) {
thisArg = thisArg !== undefined ? thisArg : arrayToSearch;
if (callback.call(thisArg, arrayToSearch[i], i, arrayToSearch)) {
index = i;
break;
}
}
}
return index;
};
helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
// Default to start of the array
if (startIndex === undefined || startIndex === null) {
startIndex = -1;
}
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
var currentItem = arrayToSearch[i];
if (filterCallback(currentItem)) {
return currentItem;
}
}
};
helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
// Default to end of the array
if (startIndex === undefined || startIndex === null) {
startIndex = arrayToSearch.length;
}
for (var i = startIndex - 1; i >= 0; i--) {
var currentItem = arrayToSearch[i];
if (filterCallback(currentItem)) {
return currentItem;
}
}
};
helpers.inherits = function(extensions) {
//Basic javascript inheritance based on the model created in Backbone.js
var parent = this;
var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() {
return parent.apply(this, arguments);
};
var Surrogate = function() {
this.constructor = ChartElement;
};
Surrogate.prototype = parent.prototype;
ChartElement.prototype = new Surrogate();
ChartElement.extend = helpers.inherits;
if (extensions) {
helpers.extend(ChartElement.prototype, extensions);
}
ChartElement.__super__ = parent.prototype;
return ChartElement;
};
helpers.noop = function() {};
helpers.uid = (function() {
var id = 0;
return function() {
return "chart-" + id++;
};
})();
helpers.warn = function(str) {
//Method for warning of errors
if (console && typeof console.warn === "function") {
console.warn(str);
}
};
//-- Math methods
helpers.isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
helpers.almostEquals = function(x, y, epsilon) {
return Math.abs(x - y) < epsilon;
};
helpers.max = function(array) {
return array.reduce(function(max, value) {
if (!isNaN(value)) {
return Math.max(max, value);
} else {
return max;
}
}, Number.NEGATIVE_INFINITY);
};
helpers.min = function(array) {
return array.reduce(function(min, value) {
if (!isNaN(value)) {
return Math.min(min, value);
} else {
return min;
}
}, Number.POSITIVE_INFINITY);
};
helpers.sign = function(x) {
if (Math.sign) {
return Math.sign(x);
} else {
x = +x; // convert to a number
if (x === 0 || isNaN(x)) {
return x;
}
return x > 0 ? 1 : -1;
}
};
helpers.log10 = function(x) {
if (Math.log10) {
return Math.log10(x);
} else {
return Math.log(x) / Math.LN10;
}
};
helpers.toRadians = function(degrees) {
return degrees * (Math.PI / 180);
};
helpers.toDegrees = function(radians) {
return radians * (180 / Math.PI);
};
// Gets the angle from vertical upright to the point about a centre.
helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
var distanceFromXCenter = anglePoint.x - centrePoint.x,
distanceFromYCenter = anglePoint.y - centrePoint.y,
radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
if (angle < (-0.5 * Math.PI)) {
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
}
return {
angle: angle,
distance: radialDistanceFromCenter
};
};
helpers.aliasPixel = function(pixelWidth) {
return (pixelWidth % 2 === 0) ? 0 : 0.5;
};
helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
//Props to Rob Spencer at scaled innovation for his post on splining between points
//http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
var previous = firstPoint.skip ? middlePoint : firstPoint,
current = middlePoint,
next = afterPoint.skip ? middlePoint : afterPoint;
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
var s01 = d01 / (d01 + d12);
var s12 = d12 / (d01 + d12);
// If all points are the same, s01 & s02 will be inf
s01 = isNaN(s01) ? 0 : s01;
s12 = isNaN(s12) ? 0 : s12;
var fa = t * s01; // scaling factor for triangle Ta
var fb = t * s12;
return {
previous: {
x: current.x - fa * (next.x - previous.x),
y: current.y - fa * (next.y - previous.y)
},
next: {
x: current.x + fb * (next.x - previous.x),
y: current.y + fb * (next.y - previous.y)
}
};
};
helpers.nextItem = function(collection, index, loop) {
if (loop) {
return index >= collection.length - 1 ? collection[0] : collection[index + 1];
}
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
};
helpers.previousItem = function(collection, index, loop) {
if (loop) {
return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
}
return index <= 0 ? collection[0] : collection[index - 1];
};
// Implementation of the nice number algorithm used in determining where axis labels will go
helpers.niceNum = function(range, round) {
var exponent = Math.floor(helpers.log10(range));
var fraction = range / Math.pow(10, exponent);
var niceFraction;
if (round) {
if (fraction < 1.5) {
niceFraction = 1;
} else if (fraction < 3) {
niceFraction = 2;
} else if (fraction < 7) {
niceFraction = 5;
} else {
niceFraction = 10;
}
} else {
if (fraction <= 1.0) {
niceFraction = 1;
} else if (fraction <= 2) {
niceFraction = 2;
} else if (fraction <= 5) {
niceFraction = 5;
} else {
niceFraction = 10;
}
}
return niceFraction * Math.pow(10, exponent);
};
//Easing functions adapted from Robert Penner's easing equations
//http://www.robertpenner.com/easing/
var easingEffects = helpers.easingEffects = {
linear: function(t) {
return t;
},
easeInQuad: function(t) {
return t * t;
},
easeOutQuad: function(t) {
return -1 * t * (t - 2);
},
easeInOutQuad: function(t) {
if ((t /= 1 / 2) < 1) {
return 1 / 2 * t * t;
}
return -1 / 2 * ((--t) * (t - 2) - 1);
},
easeInCubic: function(t) {
return t * t * t;
},
easeOutCubic: function(t) {
return 1 * ((t = t / 1 - 1) * t * t + 1);
},
easeInOutCubic: function(t) {
if ((t /= 1 / 2) < 1) {
return 1 / 2 * t * t * t;
}
return 1 / 2 * ((t -= 2) * t * t + 2);
},
easeInQuart: function(t) {
return t * t * t * t;
},
easeOutQuart: function(t) {
return -1 * ((t = t / 1 - 1) * t * t * t - 1);
},
easeInOutQuart: function(t) {
if ((t /= 1 / 2) < 1) {
return 1 / 2 * t * t * t * t;
}
return -1 / 2 * ((t -= 2) * t * t * t - 2);
},
easeInQuint: function(t) {
return 1 * (t /= 1) * t * t * t * t;
},
easeOutQuint: function(t) {
return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
},
easeInOutQuint: function(t) {
if ((t /= 1 / 2) < 1) {
return 1 / 2 * t * t * t * t * t;
}
return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
},
easeInSine: function(t) {
return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
},
easeOutSine: function(t) {
return 1 * Math.sin(t / 1 * (Math.PI / 2));
},
easeInOutSine: function(t) {
return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
},
easeInExpo: function(t) {
return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
},
easeOutExpo: function(t) {
return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
},
easeInOutExpo: function(t) {
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
if ((t /= 1 / 2) < 1) {
return 1 / 2 * Math.pow(2, 10 * (t - 1));
}
return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
},
easeInCirc: function(t) {
if (t >= 1) {
return t;
}
return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
},
easeOutCirc: function(t) {
return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
},
easeInOutCirc: function(t) {
if ((t /= 1 / 2) < 1) {
return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
}
return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
},
easeInElastic: function(t) {
var s = 1.70158;
var p = 0;
var a = 1;
if (t === 0) {
return 0;
}
if ((t /= 1) === 1) {
return 1;
}
if (!p) {
p = 1 * 0.3;
}
if (a < Math.abs(1)) {
a = 1;
s = p / 4;
} else {
s = p / (2 * Math.PI) * Math.asin(1 / a);
}
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
},
easeOutElastic: function(t) {
var s = 1.70158;
var p = 0;
var a = 1;
if (t === 0) {
return 0;
}
if ((t /= 1) === 1) {
return 1;
}
if (!p) {
p = 1 * 0.3;
}
if (a < Math.abs(1)) {
a = 1;
s = p / 4;
} else {
s = p / (2 * Math.PI) * Math.asin(1 / a);
}
return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
},
easeInOutElastic: function(t) {
var s = 1.70158;
var p = 0;
var a = 1;
if (t === 0) {
return 0;
}
if ((t /= 1 / 2) === 2) {
return 1;
}
if (!p) {
p = 1 * (0.3 * 1.5);
}
if (a < Math.abs(1)) {
a = 1;
s = p / 4;
} else {
s = p / (2 * Math.PI) * Math.asin(1 / a);
}
if (t < 1) {
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
}
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
},
easeInBack: function(t) {
var s = 1.70158;
return 1 * (t /= 1) * t * ((s + 1) * t - s);
},
easeOutBack: function(t) {
var s = 1.70158;
return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
},
easeInOutBack: function(t) {
var s = 1.70158;
if ((t /= 1 / 2) < 1) {
return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
}
return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
},
easeInBounce: function(t) {
return 1 - easingEffects.easeOutBounce(1 - t);
},
easeOutBounce: function(t) {
if ((t /= 1) < (1 / 2.75)) {
return 1 * (7.5625 * t * t);
} else if (t < (2 / 2.75)) {
return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
} else if (t < (2.5 / 2.75)) {
return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
} else {
return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
}
},
easeInOutBounce: function(t) {
if (t < 1 / 2) {
return easingEffects.easeInBounce(t * 2) * 0.5;
}
return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
}
};
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
helpers.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
return window.setTimeout(callback, 1000 / 60);
};
})();
helpers.cancelAnimFrame = (function() {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function(callback) {
return window.clearTimeout(callback, 1000 / 60);
};
})();
//-- DOM methods
helpers.getRelativePosition = function(evt, chart) {
var mouseX, mouseY;
var e = evt.originalEvent || evt,
canvas = evt.currentTarget || evt.srcElement,
boundingRect = canvas.getBoundingClientRect();
if (e.touches && e.touches.length > 0) {
mouseX = e.touches[0].clientX;
mouseY = e.touches[0].clientY;
} else {
mouseX = e.clientX;
mouseY = e.clientY;
}
// Scale mouse coordinates into canvas coordinates
// by following the pattern laid out by 'jerryj' in the comments of
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
return {
x: mouseX,
y: mouseY
};
};
helpers.addEvent = function(node, eventType, method) {
if (node.addEventListener) {
node.addEventListener(eventType, method);
} else if (node.attachEvent) {
node.attachEvent("on" + eventType, method);
} else {
node["on" + eventType] = method;
}
};
helpers.removeEvent = function(node, eventType, handler) {
if (node.removeEventListener) {
node.removeEventListener(eventType, handler, false);
} else if (node.detachEvent) {
node.detachEvent("on" + eventType, handler);
} else {
node["on" + eventType] = helpers.noop;
}
};
helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
// Create the events object if it's not already present
if (!chartInstance.events)
chartInstance.events = {};
helpers.each(arrayOfEvents, function(eventName) {
chartInstance.events[eventName] = function() {
handler.apply(chartInstance, arguments);
};
helpers.addEvent(chartInstance.chart.canvas, eventName, chartInstance.events[eventName]);
});
};
helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
helpers.each(arrayOfEvents, function(handler, eventName) {
helpers.removeEvent(chartInstance.chart.canvas, eventName, handler);
});
};
// Private helper function to convert max-width/max-height values that may be percentages into a number
function parseMaxStyle(styleValue, node, parentProperty) {
var valueInPixels;
if (typeof(styleValue) === 'string') {
valueInPixels = parseInt(styleValue, 10);
if (styleValue.indexOf('%') != -1) {
// percentage * size in dimension
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
}
} else {
valueInPixels = styleValue;
}
return valueInPixels;
}
// Private helper to get a constraint dimension
// @param domNode : the node to check the constraint on
// @param maxStyle : the style that defines the maximum for the direction we are using (max-width / max-height)
// @param percentageProperty : property of parent to use when calculating width as a percentage
function getConstraintDimension(domNode, maxStyle, percentageProperty) {
var constrainedDimension;
var constrainedNode = document.defaultView.getComputedStyle(domNode)[maxStyle];
var constrainedContainer = document.defaultView.getComputedStyle(domNode.parentNode)[maxStyle];
var hasCNode = constrainedNode !== null && constrainedNode !== "none";
var hasCContainer = constrainedContainer !== null && constrainedContainer !== "none";
if (hasCNode || hasCContainer) {
constrainedDimension = Math.min((hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : Number.POSITIVE_INFINITY), (hasCContainer ? parseMaxStyle(constrainedContainer, domNode.parentNode, percentageProperty) : Number.POSITIVE_INFINITY));
}
return constrainedDimension;
}
// returns Number or undefined if no constraint
helpers.getConstraintWidth = function(domNode) {
return getConstraintDimension(domNode, 'max-width', 'clientWidth');
};
// returns Number or undefined if no constraint
helpers.getConstraintHeight = function(domNode) {
return getConstraintDimension(domNode, 'max-height', 'clientHeight');
};
helpers.getMaximumWidth = function(domNode) {
var container = domNode.parentNode;
var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right'));
var w = container.clientWidth - padding;
var cw = helpers.getConstraintWidth(domNode);
if (cw !== undefined) {
w = Math.min(w, cw);
}
return w;
};
helpers.getMaximumHeight = function(domNode) {
var container = domNode.parentNode;
var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom'));
var h = container.clientHeight - padding;
var ch = helpers.getConstraintHeight(domNode);
if (ch !== undefined) {
h = Math.min(h, ch);
}
return h;
};
helpers.getStyle = function(el, property) {
return el.currentStyle ?
el.currentStyle[property] :
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
};
helpers.retinaScale = function(chart) {
var ctx = chart.ctx;
var width = chart.canvas.width;
var height = chart.canvas.height;
var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
if (pixelRatio !== 1) {
ctx.canvas.height = height * pixelRatio;
ctx.canvas.width = width * pixelRatio;
ctx.scale(pixelRatio, pixelRatio);
// Store the device pixel ratio so that we can go backwards in `destroy`.
// The devicePixelRatio changes with zoom, so there are no guarantees that it is the same
// when destroy is called
chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio;
}
ctx.canvas.style.width = width + 'px';
ctx.canvas.style.height = height + 'px';
};
//-- Canvas methods
helpers.clear = function(chart) {
chart.ctx.clearRect(0, 0, chart.width, chart.height);
};
helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
return fontStyle + " " + pixelSize + "px " + fontFamily;
};
helpers.longestText = function(ctx, font, arrayOfStrings, cache) {
cache = cache || {};
cache.data = cache.data || {};
cache.garbageCollect = cache.garbageCollect || [];
if (cache.font !== font) {
cache.data = {};
cache.garbageCollect = [];
cache.font = font;
}
ctx.font = font;
var longest = 0;
helpers.each(arrayOfStrings, function(string) {
// Undefined strings should not be measured
if (string !== undefined && string !== null) {
var textWidth = cache.data[string];
if (!textWidth) {
textWidth = cache.data[string] = ctx.measureText(string).width;
cache.garbageCollect.push(string);
}
if (textWidth > longest) {
longest = textWidth;
}
}
});
var gcLen = cache.garbageCollect.length / 2;
if (gcLen > arrayOfStrings.length) {
for (var i = 0; i < gcLen; i++) {
delete cache.data[cache.garbageCollect[i]];
}
cache.garbageCollect.splice(0, gcLen);
}
return longest;
};
helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
};
helpers.color = function(c) {
if (!color) {
console.log('Color.js not found!');
return c;
}
/* global CanvasGradient */
if (c instanceof CanvasGradient) {
return color(Chart.defaults.global.defaultColor);
}
return color(c);
};
helpers.addResizeListener = function(node, callback) {
// Hide an iframe before the node
var hiddenIframe = document.createElement('iframe');
var hiddenIframeClass = 'chartjs-hidden-iframe';
if (hiddenIframe.classlist) {
// can use classlist
hiddenIframe.classlist.add(hiddenIframeClass);
} else {
hiddenIframe.setAttribute('class', hiddenIframeClass);
}
// Set the style
hiddenIframe.style.width = '100%';
hiddenIframe.style.display = 'block';
hiddenIframe.style.border = 0;
hiddenIframe.style.height = 0;
hiddenIframe.style.margin = 0;
hiddenIframe.style.position = 'absolute';
hiddenIframe.style.left = 0;
hiddenIframe.style.right = 0;
hiddenIframe.style.top = 0;
hiddenIframe.style.bottom = 0;
// Insert the iframe so that contentWindow is available
node.insertBefore(hiddenIframe, node.firstChild);
(hiddenIframe.contentWindow || hiddenIframe).onresize = function() {
if (callback) {
callback();
}
};
};
helpers.removeResizeListener = function(node) {
var hiddenIframe = node.querySelector('.chartjs-hidden-iframe');
// Remove the resize detect iframe
if (hiddenIframe) {
hiddenIframe.parentNode.removeChild(hiddenIframe);
}
};
helpers.isArray = function(obj) {
if (!Array.isArray) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
return Array.isArray(obj);
};
helpers.pushAllIfDefined = function(element, array) {
if (typeof element === "undefined") {
return;
}
if (helpers.isArray(element)) {
array.push.apply(array, element);
} else {
array.push(element);
}
};
helpers.isDatasetVisible = function(dataset) {
return !dataset.hidden;
};
helpers.callCallback = function(fn, args, _tArg) {
if (fn && typeof fn.call === 'function') {
fn.apply(_tArg, args);
}
};
};
},{"chartjs-color":2}],26:[function(require,module,exports){
"use strict";
module.exports = function() {
//Occupy the global variable of Chart, and create a simple base class
var Chart = function(context, config) {
this.config = config;
// Support a jQuery'd canvas element
if (context.length && context[0].getContext) {
context = context[0];
}
// Support a canvas domnode
if (context.getContext) {
context = context.getContext("2d");
}
this.ctx = context;
this.canvas = context.canvas;
// Figure out what the size of the chart will be.
// If the canvas has a specified width and height, we use those else
// we look to see if the canvas node has a CSS width and height.
// If there is still no height, fill the parent container
this.width = context.canvas.width || parseInt(Chart.helpers.getStyle(context.canvas, 'width')) || Chart.helpers.getMaximumWidth(context.canvas);
this.height = context.canvas.height || parseInt(Chart.helpers.getStyle(context.canvas, 'height')) || Chart.helpers.getMaximumHeight(context.canvas);
this.aspectRatio = this.width / this.height;
if (isNaN(this.aspectRatio) || isFinite(this.aspectRatio) === false) {
// If the canvas has no size, try and figure out what the aspect ratio will be.
// Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
// else use the canvas default ratio of 2
this.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2;
}
// Store the original style of the element so we can set it back
this.originalCanvasStyleWidth = context.canvas.style.width;
this.originalCanvasStyleHeight = context.canvas.style.height;
// High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
Chart.helpers.retinaScale(this);
if (config) {
this.controller = new Chart.Controller(this);
}
// Always bind this so that if the responsive state changes we still work
var _this = this;
Chart.helpers.addResizeListener(context.canvas.parentNode, function() {
if (_this.controller && _this.controller.config.options.responsive) {
_this.controller.resize();
}
});
return this.controller ? this.controller : this;
};
//Globally expose the defaults to allow for user updating/changing
Chart.defaults = {
global: {
responsive: true,
responsiveAnimationDuration: 0,
maintainAspectRatio: true,
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"],
hover: {
onHover: null,
mode: 'single',
animationDuration: 400
},
onClick: null,
defaultColor: 'rgba(0,0,0,0.1)',
defaultFontColor: '#666',
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
defaultFontSize: 12,
defaultFontStyle: 'normal',
showLines: true,
// Element defaults defined in element extensions
elements: {},
// Legend callback string
legendCallback: function(chart) {
var text = [];
text.push('');
for (var i = 0; i < chart.data.datasets.length; i++) {
text.push(' ');
if (chart.data.datasets[i].label) {
text.push(chart.data.datasets[i].label);
}
text.push(' ');
}
text.push(' ');
return text.join("");
}
}
};
return Chart;
};
},{}],27:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
// The layout service is very self explanatory. It's responsible for the layout within a chart.
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
// It is this service's responsibility of carrying out that layout.
Chart.layoutService = {
defaults: {},
// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
addBox: function(chartInstance, box) {
if (!chartInstance.boxes) {
chartInstance.boxes = [];
}
chartInstance.boxes.push(box);
},
removeBox: function(chartInstance, box) {
if (!chartInstance.boxes) {
return;
}
chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
},
// The most important function
update: function(chartInstance, width, height) {
if (!chartInstance) {
return;
}
var xPadding = 0;
var yPadding = 0;
var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position === "left";
});
var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position === "right";
});
var topBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position === "top";
});
var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position === "bottom";
});
// Boxes that overlay the chartarea such as the radialLinear scale
var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
return box.options.position === "chartArea";
});
function fullWidthSorter(a, b) {
}
// Ensure that full width boxes are at the very top / bottom
topBoxes.sort(function(a, b) {
return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
});
bottomBoxes.sort(function(a, b) {
return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
});
// Essentially we now have any number of boxes on each of the 4 sides.
// Our canvas looks like the following.
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
// B1 is the bottom axis
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
// an error will be thrown.
//
// |----------------------------------------------------|
// | T1 (Full Width) |
// |----------------------------------------------------|
// | | | T2 | |
// | |----|-------------------------------------|----|
// | | | C1 | | C2 | |
// | | |----| |----| |
// | | | | |
// | L1 | L2 | ChartArea (C0) | R1 |
// | | | | |
// | | |----| |----| |
// | | | C3 | | C4 | |
// | |----|-------------------------------------|----|
// | | | B1 | |
// |----------------------------------------------------|
// | B2 (Full Width) |
// |----------------------------------------------------|
//
// What we do to find the best sizing, we do the following
// 1. Determine the minimum size of the chart area.
// 2. Split the remaining width equally between each vertical axis
// 3. Split the remaining height equally between each horizontal axis
// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
// 5. Adjust the sizes of each axis based on it's minimum reported size.
// 6. Refit each axis
// 7. Position each axis in the final location
// 8. Tell the chart the final location of the chart area
// 9. Tell any axes that overlay the chart area the positions of the chart area
// Step 1
var chartWidth = width - (2 * xPadding);
var chartHeight = height - (2 * yPadding);
var chartAreaWidth = chartWidth / 2; // min 50%
var chartAreaHeight = chartHeight / 2; // min 50%
// Step 2
var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
// Step 3
var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
// Step 4
var maxChartAreaWidth = chartWidth;
var maxChartAreaHeight = chartHeight;
var minBoxSizes = [];
helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
function getMinimumBoxSize(box) {
var minSize;
var isHorizontal = box.isHorizontal();
if (isHorizontal) {
minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
maxChartAreaHeight -= minSize.height;
} else {
minSize = box.update(verticalBoxWidth, chartAreaHeight);
maxChartAreaWidth -= minSize.width;
}
minBoxSizes.push({
horizontal: isHorizontal,
minSize: minSize,
box: box
});
}
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
// be if the axes are drawn at their minimum sizes.
// Steps 5 & 6
var totalLeftBoxesWidth = xPadding;
var totalRightBoxesWidth = xPadding;
var totalTopBoxesHeight = yPadding;
var totalBottomBoxesHeight = yPadding;
// Update, and calculate the left and right margins for the horizontal boxes
helpers.each(leftBoxes.concat(rightBoxes), fitBox);
helpers.each(leftBoxes, function(box) {
totalLeftBoxesWidth += box.width;
});
helpers.each(rightBoxes, function(box) {
totalRightBoxesWidth += box.width;
});
// Set the Left and Right margins for the horizontal boxes
helpers.each(topBoxes.concat(bottomBoxes), fitBox);
// Function to fit a box
function fitBox(box) {
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
return minBoxSize.box === box;
});
if (minBoxSize) {
if (box.isHorizontal()) {
var scaleMargin = {
left: totalLeftBoxesWidth,
right: totalRightBoxesWidth,
top: 0,
bottom: 0
};
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
// on the margin. Sometimes they need to increase in size slightly
box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
} else {
box.update(minBoxSize.minSize.width, maxChartAreaHeight);
}
}
}
// Figure out how much margin is on the top and bottom of the vertical boxes
helpers.each(topBoxes, function(box) {
totalTopBoxesHeight += box.height;
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
// Let the left layout know the final margin
helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
function finalFitVerticalBox(box) {
var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
return minBoxSize.box === box;
});
var scaleMargin = {
left: 0,
right: 0,
top: totalTopBoxesHeight,
bottom: totalBottomBoxesHeight
};
if (minBoxSize) {
box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
}
}
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
totalLeftBoxesWidth = xPadding;
totalRightBoxesWidth = xPadding;
totalTopBoxesHeight = yPadding;
totalBottomBoxesHeight = yPadding;
helpers.each(leftBoxes, function(box) {
totalLeftBoxesWidth += box.width;
});
helpers.each(rightBoxes, function(box) {
totalRightBoxesWidth += box.width;
});
helpers.each(topBoxes, function(box) {
totalTopBoxesHeight += box.height;
});
helpers.each(bottomBoxes, function(box) {
totalBottomBoxesHeight += box.height;
});
// Figure out if our chart area changed. This would occur if the dataset layout label rotation
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
// without calling `fit` again
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
helpers.each(leftBoxes, function(box) {
box.height = newMaxChartAreaHeight;
});
helpers.each(rightBoxes, function(box) {
box.height = newMaxChartAreaHeight;
});
helpers.each(topBoxes, function(box) {
box.width = newMaxChartAreaWidth;
});
helpers.each(bottomBoxes, function(box) {
box.width = newMaxChartAreaWidth;
});
maxChartAreaHeight = newMaxChartAreaHeight;
maxChartAreaWidth = newMaxChartAreaWidth;
}
// Step 7 - Position the boxes
var left = xPadding;
var top = yPadding;
var right = 0;
var bottom = 0;
helpers.each(leftBoxes.concat(topBoxes), placeBox);
// Account for chart width and height
left += maxChartAreaWidth;
top += maxChartAreaHeight;
helpers.each(rightBoxes, placeBox);
helpers.each(bottomBoxes, placeBox);
function placeBox(box) {
if (box.isHorizontal()) {
box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
box.top = top;
box.bottom = top + box.height;
// Move to next point
top = box.bottom;
} else {
box.left = left;
box.right = left + box.width;
box.top = totalTopBoxesHeight;
box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
// Move to next point
left = box.right;
}
}
// Step 8
chartInstance.chartArea = {
left: totalLeftBoxesWidth,
top: totalTopBoxesHeight,
right: totalLeftBoxesWidth + maxChartAreaWidth,
bottom: totalTopBoxesHeight + maxChartAreaHeight
};
// Step 9
helpers.each(chartAreaBoxes, function(box) {
box.left = chartInstance.chartArea.left;
box.top = chartInstance.chartArea.top;
box.right = chartInstance.chartArea.right;
box.bottom = chartInstance.chartArea.bottom;
box.update(maxChartAreaWidth, maxChartAreaHeight);
});
}
};
};
},{}],28:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.legend = {
display: true,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
reverse: false,
// a callback that will handle
onClick: function(e, legendItem) {
var dataset = this.chart.data.datasets[legendItem.datasetIndex];
dataset.hidden = !dataset.hidden;
// We hid a dataset ... rerender the chart
this.chart.update();
},
labels: {
boxWidth: 40,
padding: 10,
// Generates labels shown in the legend
// Valid properties to return:
// text : text to display
// fillStyle : fill of coloured box
// strokeStyle: stroke of coloured box
// hidden : if this legend item refers to a hidden item
// lineCap : cap style for line
// lineDash
// lineDashOffset :
// lineJoin :
// lineWidth :
generateLabels: function(data) {
return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
return {
text: dataset.label,
fillStyle: dataset.backgroundColor,
hidden: dataset.hidden,
lineCap: dataset.borderCapStyle,
lineDash: dataset.borderDash,
lineDashOffset: dataset.borderDashOffset,
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
// Below is extra data used for toggling the datasets
datasetIndex: i
};
}, this) : [];
}
}
};
Chart.Legend = Chart.Element.extend({
initialize: function(config) {
helpers.extend(this, config);
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
},
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all legend types.
// Any function can be extended by the legend type
beforeUpdate: helpers.noop,
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeUpdate();
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = margins;
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Labels
this.beforeBuildLabels();
this.buildLabels();
this.afterBuildLabels();
// Fit
this.beforeFit();
this.fit();
this.afterFit();
//
this.afterUpdate();
return this.minSize;
},
afterUpdate: helpers.noop,
//
beforeSetDimensions: helpers.noop,
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
// Reset position before calculating rotation
this.width = this.maxWidth;
this.left = 0;
this.right = this.width;
} else {
this.height = this.maxHeight;
// Reset position before calculating rotation
this.top = 0;
this.bottom = this.height;
}
// Reset padding
this.paddingLeft = 0;
this.paddingTop = 0;
this.paddingRight = 0;
this.paddingBottom = 0;
// Reset minSize
this.minSize = {
width: 0,
height: 0
};
},
afterSetDimensions: helpers.noop,
//
beforeBuildLabels: helpers.noop,
buildLabels: function() {
this.legendItems = this.options.labels.generateLabels.call(this, this.chart.data);
if(this.options.reverse){
this.legendItems.reverse();
}
},
afterBuildLabels: helpers.noop,
//
beforeFit: helpers.noop,
fit: function() {
var ctx = this.ctx;
var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize);
var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle);
var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily);
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
// Reset hit boxes
this.legendHitBoxes = [];
// Width
if (this.isHorizontal()) {
this.minSize.width = this.maxWidth; // fill all the width
} else {
this.minSize.width = this.options.display ? 10 : 0;
}
// height
if (this.isHorizontal()) {
this.minSize.height = this.options.display ? 10 : 0;
} else {
this.minSize.height = this.maxHeight; // fill all the height
}
// Increase sizes here
if (this.options.display) {
if (this.isHorizontal()) {
// Labels
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
this.lineWidths = [0];
var totalHeight = this.legendItems.length ? fontSize + (this.options.labels.padding) : 0;
ctx.textAlign = "left";
ctx.textBaseline = 'top';
ctx.font = labelFont;
helpers.each(this.legendItems, function(legendItem, i) {
var width = this.options.labels.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
if (this.lineWidths[this.lineWidths.length - 1] + width + this.options.labels.padding >= this.width) {
totalHeight += fontSize + (this.options.labels.padding);
this.lineWidths[this.lineWidths.length] = this.left;
}
// Store the hitbox width and height here. Final position will be updated in `draw`
this.legendHitBoxes[i] = {
left: 0,
top: 0,
width: width,
height: fontSize
};
this.lineWidths[this.lineWidths.length - 1] += width + this.options.labels.padding;
}, this);
this.minSize.height += totalHeight;
} else {
// TODO vertical
}
}
this.width = this.minSize.width;
this.height = this.minSize.height;
},
afterFit: helpers.noop,
// Shared Methods
isHorizontal: function() {
return this.options.position === "top" || this.options.position === "bottom";
},
// Actualy draw the legend on the canvas
draw: function() {
if (this.options.display) {
var ctx = this.ctx;
var cursor = {
x: this.left + ((this.width - this.lineWidths[0]) / 2),
y: this.top + this.options.labels.padding,
line: 0
};
var fontColor = helpers.getValueOrDefault(this.options.labels.fontColor, Chart.defaults.global.defaultFontColor);
var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize);
var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle);
var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily);
var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
// Horizontal
if (this.isHorizontal()) {
// Labels
ctx.textAlign = "left";
ctx.textBaseline = 'top';
ctx.lineWidth = 0.5;
ctx.strokeStyle = fontColor; // for strikethrough effect
ctx.fillStyle = fontColor; // render in correct colour
ctx.font = labelFont;
helpers.each(this.legendItems, function(legendItem, i) {
var textWidth = ctx.measureText(legendItem.text).width;
var width = this.options.labels.boxWidth + (fontSize / 2) + textWidth;
if (cursor.x + width >= this.width) {
cursor.y += fontSize + (this.options.labels.padding);
cursor.line++;
cursor.x = this.left + ((this.width - this.lineWidths[cursor.line]) / 2);
}
// Set the ctx for the box
ctx.save();
var itemOrDefault = function(item, defaulVal) {
return item !== undefined ? item : defaulVal;
};
ctx.fillStyle = itemOrDefault(legendItem.fillStyle, Chart.defaults.global.defaultColor);
ctx.lineCap = itemOrDefault(legendItem.lineCap, Chart.defaults.global.elements.line.borderCapStyle);
ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, Chart.defaults.global.elements.line.borderDashOffset);
ctx.lineJoin = itemOrDefault(legendItem.lineJoin, Chart.defaults.global.elements.line.borderJoinStyle);
ctx.lineWidth = itemOrDefault(legendItem.lineWidth, Chart.defaults.global.elements.line.borderWidth);
ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, Chart.defaults.global.defaultColor);
if (ctx.setLineDash) {
// IE 9 and 10 do not support line dash
ctx.setLineDash(itemOrDefault(legendItem.lineDash, Chart.defaults.global.elements.line.borderDash));
}
// Draw the box
ctx.strokeRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize);
ctx.fillRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize);
ctx.restore();
this.legendHitBoxes[i].left = cursor.x;
this.legendHitBoxes[i].top = cursor.y;
// Fill the actual label
ctx.fillText(legendItem.text, this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y);
if (legendItem.hidden) {
// Strikethrough the text if hidden
ctx.beginPath();
ctx.lineWidth = 2;
ctx.moveTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y + (fontSize / 2));
ctx.lineTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x + textWidth, cursor.y + (fontSize / 2));
ctx.stroke();
}
cursor.x += width + (this.options.labels.padding);
}, this);
} else {
}
}
},
// Handle an event
handleEvent: function(e) {
var position = helpers.getRelativePosition(e, this.chart.chart);
if (position.x >= this.left && position.x <= this.right && position.y >= this.top && position.y <= this.bottom) {
// See if we are touching one of the dataset boxes
for (var i = 0; i < this.legendHitBoxes.length; ++i) {
var hitBox = this.legendHitBoxes[i];
if (position.x >= hitBox.left && position.x <= hitBox.left + hitBox.width && position.y >= hitBox.top && position.y <= hitBox.top + hitBox.height) {
// Touching an element
if (this.options.onClick) {
this.options.onClick.call(this, e, this.legendItems[i]);
}
break;
}
}
}
}
});
};
},{}],29:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
// Plugins are stored here
Chart.plugins = [];
Chart.pluginService = {
// Register a new plugin
register: function(plugin) {
if (Chart.plugins.indexOf(plugin) === -1) {
Chart.plugins.push(plugin);
}
},
// Remove a registered plugin
remove: function(plugin) {
var idx = Chart.plugins.indexOf(plugin);
if (idx !== -1) {
Chart.plugins.splice(idx, 1);
}
},
// Iterate over all plugins
notifyPlugins: function(method, args, scope) {
helpers.each(Chart.plugins, function(plugin) {
if (plugin[method] && typeof plugin[method] === 'function') {
plugin[method].apply(scope, args);
}
}, scope);
}
};
Chart.PluginBase = Chart.Element.extend({
// Plugin methods. All functions are passed the chart instance
// Called at start of chart init
beforeInit: helpers.noop,
// Called at end of chart init
afterInit: helpers.noop,
// Called at start of update
beforeUpdate: helpers.noop,
// Called at end of update
afterUpdate: helpers.noop,
// Called at start of draw
beforeDraw: helpers.noop,
// Called at end of draw
afterDraw: helpers.noop,
});
};
},{}],30:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.scale = {
display: true,
// grid line settings
gridLines: {
display: true,
color: "rgba(0, 0, 0, 0.1)",
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true,
tickMarkLength: 10,
zeroLineWidth: 1,
zeroLineColor: "rgba(0,0,0,0.25)",
offsetGridLines: false
},
// scale label
scaleLabel: {
// actual label
labelString: '',
// display property
display: false
},
// label settings
ticks: {
beginAtZero: false,
maxRotation: 50,
mirror: false,
padding: 10,
reverse: false,
display: true,
autoSkip: true,
autoSkipPadding: 0,
callback: function(value) {
return '' + value;
}
}
};
Chart.Scale = Chart.Element.extend({
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all scale types.
// Any function can be extended by the scale type
beforeUpdate: function() {
helpers.callCallback(this.options.beforeUpdate, [this]);
},
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeUpdate();
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = helpers.extend({
left: 0,
right: 0,
top: 0,
bottom: 0
}, margins);
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Data min/max
this.beforeDataLimits();
this.determineDataLimits();
this.afterDataLimits();
// Ticks
this.beforeBuildTicks();
this.buildTicks();
this.afterBuildTicks();
this.beforeTickToLabelConversion();
this.convertTicksToLabels();
this.afterTickToLabelConversion();
// Tick Rotation
this.beforeCalculateTickRotation();
this.calculateTickRotation();
this.afterCalculateTickRotation();
// Fit
this.beforeFit();
this.fit();
this.afterFit();
//
this.afterUpdate();
return this.minSize;
},
afterUpdate: function() {
helpers.callCallback(this.options.afterUpdate, [this]);
},
//
beforeSetDimensions: function() {
helpers.callCallback(this.options.beforeSetDimensions, [this]);
},
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
// Reset position before calculating rotation
this.width = this.maxWidth;
this.left = 0;
this.right = this.width;
} else {
this.height = this.maxHeight;
// Reset position before calculating rotation
this.top = 0;
this.bottom = this.height;
}
// Reset padding
this.paddingLeft = 0;
this.paddingTop = 0;
this.paddingRight = 0;
this.paddingBottom = 0;
},
afterSetDimensions: function() {
helpers.callCallback(this.options.afterSetDimensions, [this]);
},
// Data limits
beforeDataLimits: function() {
helpers.callCallback(this.options.beforeDataLimits, [this]);
},
determineDataLimits: helpers.noop,
afterDataLimits: function() {
helpers.callCallback(this.options.afterDataLimits, [this]);
},
//
beforeBuildTicks: function() {
helpers.callCallback(this.options.beforeBuildTicks, [this]);
},
buildTicks: helpers.noop,
afterBuildTicks: function() {
helpers.callCallback(this.options.afterBuildTicks, [this]);
},
beforeTickToLabelConversion: function() {
helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
},
convertTicksToLabels: function() {
// Convert ticks to strings
this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
if (this.options.ticks.userCallback) {
return this.options.ticks.userCallback(numericalTick, index, ticks);
}
return this.options.ticks.callback(numericalTick, index, ticks);
},
this);
},
afterTickToLabelConversion: function() {
helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
},
//
beforeCalculateTickRotation: function() {
helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
},
calculateTickRotation: function() {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
this.ctx.font = tickLabelFont;
var firstWidth = this.ctx.measureText(this.ticks[0]).width;
var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
var firstRotated;
this.labelRotation = 0;
this.paddingRight = 0;
this.paddingLeft = 0;
if (this.options.display) {
if (this.isHorizontal()) {
this.paddingRight = lastWidth / 2 + 3;
this.paddingLeft = firstWidth / 2 + 3;
if (!this.longestTextCache) {
this.longestTextCache = {};
}
var originalLabelWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache);
var labelWidth = originalLabelWidth;
var cosRotation;
var sinRotation;
// Allow 3 pixels x2 padding either side for label readability
// only the index matters for a dataset scale, but we want a consistent interface between scales
var tickWidth = this.getPixelForTick(1) - this.getPixelForTick(0) - 6;
//Max label rotation can be set or default to 90 - also act as a loop counter
while (labelWidth > tickWidth && this.labelRotation < this.options.ticks.maxRotation) {
cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
firstRotated = cosRotation * firstWidth;
// We're right aligning the text now.
if (firstRotated + tickFontSize / 2 > this.yLabelWidth) {
this.paddingLeft = firstRotated + tickFontSize / 2;
}
this.paddingRight = tickFontSize / 2;
if (sinRotation * originalLabelWidth > this.maxHeight) {
// go back one step
this.labelRotation--;
break;
}
this.labelRotation++;
labelWidth = cosRotation * originalLabelWidth;
}
}
}
if (this.margins) {
this.paddingLeft = Math.max(this.paddingLeft - this.margins.left, 0);
this.paddingRight = Math.max(this.paddingRight - this.margins.right, 0);
}
},
afterCalculateTickRotation: function() {
helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
},
//
beforeFit: function() {
helpers.callCallback(this.options.beforeFit, [this]);
},
fit: function() {
this.minSize = {
width: 0,
height: 0
};
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
var scaleLabelFontSize = helpers.getValueOrDefault(this.options.scaleLabel.fontSize, Chart.defaults.global.defaultFontSize);
var scaleLabelFontStyle = helpers.getValueOrDefault(this.options.scaleLabel.fontStyle, Chart.defaults.global.defaultFontStyle);
var scaleLabelFontFamily = helpers.getValueOrDefault(this.options.scaleLabel.fontFamily, Chart.defaults.global.defaultFontFamily);
var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
// Width
if (this.isHorizontal()) {
// subtract the margins to line up with the chartArea if we are a full width scale
this.minSize.width = this.isFullWidth() ? this.maxWidth - this.margins.left - this.margins.right : this.maxWidth;
} else {
this.minSize.width = this.options.gridLines.tickMarkLength;
}
// height
if (this.isHorizontal()) {
this.minSize.height = this.options.gridLines.tickMarkLength;
} else {
this.minSize.height = this.maxHeight; // fill all the height
}
// Are we showing a title for the scale?
if (this.options.scaleLabel.display) {
if (this.isHorizontal()) {
this.minSize.height += (scaleLabelFontSize * 1.5);
} else {
this.minSize.width += (scaleLabelFontSize * 1.5);
}
}
if (this.options.ticks.display && this.options.display) {
// Don't bother fitting the ticks if we are not showing them
if (!this.longestTextCache) {
this.longestTextCache = {};
}
var largestTextWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache);
if (this.isHorizontal()) {
// A horizontal axis is more constrained by the height.
this.longestLabelWidth = largestTextWidth;
// TODO - improve this calculation
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + 1.5 * tickFontSize;
this.minSize.height = Math.min(this.maxHeight, this.minSize.height + labelHeight);
this.ctx.font = tickLabelFont;
var firstLabelWidth = this.ctx.measureText(this.ticks[0]).width;
var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
// by the font height
var cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
var sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
this.paddingLeft = this.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
this.paddingRight = this.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated
} else {
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
var maxLabelWidth = this.maxWidth - this.minSize.width;
// Account for padding
if (!this.options.ticks.mirror) {
largestTextWidth += this.options.ticks.padding;
}
if (largestTextWidth < maxLabelWidth) {
// We don't need all the room
this.minSize.width += largestTextWidth;
} else {
// Expand to max size
this.minSize.width = this.maxWidth;
}
this.paddingTop = tickFontSize / 2;
this.paddingBottom = tickFontSize / 2;
}
}
if (this.margins) {
this.paddingLeft = Math.max(this.paddingLeft - this.margins.left, 0);
this.paddingTop = Math.max(this.paddingTop - this.margins.top, 0);
this.paddingRight = Math.max(this.paddingRight - this.margins.right, 0);
this.paddingBottom = Math.max(this.paddingBottom - this.margins.bottom, 0);
}
this.width = this.minSize.width;
this.height = this.minSize.height;
},
afterFit: function() {
helpers.callCallback(this.options.afterFit, [this]);
},
// Shared Methods
isHorizontal: function() {
return this.options.position === "top" || this.options.position === "bottom";
},
isFullWidth: function() {
return (this.options.fullWidth);
},
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
getRightValue: function getRightValue(rawValue) {
// Null and undefined values first
if (rawValue === null || typeof(rawValue) === 'undefined') {
return NaN;
}
// isNaN(object) returns true, so make sure NaN is checking for a number
if (typeof(rawValue) === 'number' && isNaN(rawValue)) {
return NaN;
}
// If it is in fact an object, dive in one more level
if (typeof(rawValue) === "object") {
if (rawValue instanceof Date) {
return rawValue;
} else {
return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
}
}
// Value is good, return it
return rawValue;
},
// Used to get the value to display in the tooltip for the data at the given index
// function getLabelForIndex(index, datasetIndex)
getLabelForIndex: helpers.noop,
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: helpers.noop,
// Used for tick location, should
getPixelForTick: function(index, includeOffset) {
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var tickWidth = innerWidth / Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
var pixel = (tickWidth * index) + this.paddingLeft;
if (includeOffset) {
pixel += tickWidth / 2;
}
var finalVal = this.left + Math.round(pixel);
finalVal += this.isFullWidth() ? this.margins.left : 0;
return finalVal;
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
return this.top + (index * (innerHeight / (this.ticks.length - 1)));
}
},
// Utility for getting the pixel location of a percentage of scale
getPixelForDecimal: function(decimal /*, includeOffset*/ ) {
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
var finalVal = this.left + Math.round(valueOffset);
finalVal += this.isFullWidth() ? this.margins.left : 0;
return finalVal;
} else {
return this.top + (decimal * this.height);
}
},
// Actualy draw the scale on the canvas
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
draw: function(chartArea) {
if (this.options.display) {
var setContextLineSettings;
var isRotated = this.labelRotation !== 0;
var skipRatio;
var scaleLabelX;
var scaleLabelY;
var useAutoskipper = this.options.ticks.autoSkip;
// figure out the maximum number of gridlines to show
var maxTicks;
if (this.options.ticks.maxTicksLimit) {
maxTicks = this.options.ticks.maxTicksLimit;
}
var tickFontColor = helpers.getValueOrDefault(this.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
var tl = this.options.gridLines.tickMarkLength;
var scaleLabelFontColor = helpers.getValueOrDefault(this.options.scaleLabel.fontColor, Chart.defaults.global.defaultFontColor);
var scaleLabelFontSize = helpers.getValueOrDefault(this.options.scaleLabel.fontSize, Chart.defaults.global.defaultFontSize);
var scaleLabelFontStyle = helpers.getValueOrDefault(this.options.scaleLabel.fontStyle, Chart.defaults.global.defaultFontStyle);
var scaleLabelFontFamily = helpers.getValueOrDefault(this.options.scaleLabel.fontFamily, Chart.defaults.global.defaultFontFamily);
var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
var cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
var sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
var longestRotatedLabel = this.longestLabelWidth * cosRotation;
var rotatedLabelHeight = tickFontSize * sinRotation;
// Make sure we draw text in the correct color and font
this.ctx.fillStyle = tickFontColor;
if (this.isHorizontal()) {
setContextLineSettings = true;
var yTickStart = this.options.position === "bottom" ? this.top : this.bottom - tl;
var yTickEnd = this.options.position === "bottom" ? this.top + tl : this.bottom;
skipRatio = false;
if (((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
skipRatio = 1 + Math.floor((((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
}
// if they defined a max number of ticks,
// increase skipRatio until that number is met
if (maxTicks && this.ticks.length > maxTicks) {
while (!skipRatio || this.ticks.length / (skipRatio || 1) > maxTicks) {
if (!skipRatio) {
skipRatio = 1;
}
skipRatio += 1;
}
}
if (!useAutoskipper) {
skipRatio = false;
}
helpers.each(this.ticks, function(label, index) {
// Blank ticks
var isLastTick = this.ticks.length === index + 1;
// Since we always show the last tick,we need may need to hide the last shown one before
var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio > this.ticks.length);
if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
return;
}
var xLineValue = this.getPixelForTick(index); // xvalues for grid lines
var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
if (this.options.gridLines.display) {
if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) {
// Draw the first index specially
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
setContextLineSettings = true; // reset next time
} else if (setContextLineSettings) {
this.ctx.lineWidth = this.options.gridLines.lineWidth;
this.ctx.strokeStyle = this.options.gridLines.color;
setContextLineSettings = false;
}
xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
// Draw the label area
this.ctx.beginPath();
if (this.options.gridLines.drawTicks) {
this.ctx.moveTo(xLineValue, yTickStart);
this.ctx.lineTo(xLineValue, yTickEnd);
}
// Draw the chart area
if (this.options.gridLines.drawOnChartArea) {
this.ctx.moveTo(xLineValue, chartArea.top);
this.ctx.lineTo(xLineValue, chartArea.bottom);
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this.ctx.stroke();
}
if (this.options.ticks.display) {
this.ctx.save();
this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - tl : this.top + tl);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.font = tickLabelFont;
this.ctx.textAlign = (isRotated) ? "right" : "center";
this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top";
this.ctx.fillText(label, 0, 0);
this.ctx.restore();
}
}, this);
if (this.options.scaleLabel.display) {
// Draw the scale label
this.ctx.textAlign = "center";
this.ctx.textBaseline = 'middle';
this.ctx.fillStyle = scaleLabelFontColor; // render in correct colour
this.ctx.font = scaleLabelFont;
scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
scaleLabelY = this.options.position === 'bottom' ? this.bottom - (scaleLabelFontSize / 2) : this.top + (scaleLabelFontSize / 2);
this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
}
} else {
setContextLineSettings = true;
var xTickStart = this.options.position === "right" ? this.left : this.right - 5;
var xTickEnd = this.options.position === "right" ? this.left + 5 : this.right;
helpers.each(this.ticks, function(label, index) {
// If the callback returned a null or undefined value, do not draw this line
if (label === undefined || label === null) {
return;
}
var yLineValue = this.getPixelForTick(index); // xvalues for grid lines
if (this.options.gridLines.display) {
if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) {
// Draw the first index specially
this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
setContextLineSettings = true; // reset next time
} else if (setContextLineSettings) {
this.ctx.lineWidth = this.options.gridLines.lineWidth;
this.ctx.strokeStyle = this.options.gridLines.color;
setContextLineSettings = false;
}
yLineValue += helpers.aliasPixel(this.ctx.lineWidth);
// Draw the label area
this.ctx.beginPath();
if (this.options.gridLines.drawTicks) {
this.ctx.moveTo(xTickStart, yLineValue);
this.ctx.lineTo(xTickEnd, yLineValue);
}
// Draw the chart area
if (this.options.gridLines.drawOnChartArea) {
this.ctx.moveTo(chartArea.left, yLineValue);
this.ctx.lineTo(chartArea.right, yLineValue);
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this.ctx.stroke();
}
if (this.options.ticks.display) {
var xLabelValue;
var yLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
this.ctx.save();
if (this.options.position === "left") {
if (this.options.ticks.mirror) {
xLabelValue = this.right + this.options.ticks.padding;
this.ctx.textAlign = "left";
} else {
xLabelValue = this.right - this.options.ticks.padding;
this.ctx.textAlign = "right";
}
} else {
// right side
if (this.options.ticks.mirror) {
xLabelValue = this.left - this.options.ticks.padding;
this.ctx.textAlign = "right";
} else {
xLabelValue = this.left + this.options.ticks.padding;
this.ctx.textAlign = "left";
}
}
this.ctx.translate(xLabelValue, yLabelValue);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.font = tickLabelFont;
this.ctx.textBaseline = "middle";
this.ctx.fillText(label, 0, 0);
this.ctx.restore();
}
}, this);
if (this.options.scaleLabel.display) {
// Draw the scale label
scaleLabelX = this.options.position === 'left' ? this.left + (scaleLabelFontSize / 2) : this.right - (scaleLabelFontSize / 2);
scaleLabelY = this.top + ((this.bottom - this.top) / 2);
var rotation = this.options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
this.ctx.save();
this.ctx.translate(scaleLabelX, scaleLabelY);
this.ctx.rotate(rotation);
this.ctx.textAlign = "center";
this.ctx.fillStyle =scaleLabelFontColor; // render in correct colour
this.ctx.font = scaleLabelFont;
this.ctx.textBaseline = 'middle';
this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
this.ctx.restore();
}
}
// Draw the line at the edge of the axis
this.ctx.lineWidth = this.options.gridLines.lineWidth;
this.ctx.strokeStyle = this.options.gridLines.color;
var x1 = this.left,
x2 = this.right,
y1 = this.top,
y2 = this.bottom;
if (this.isHorizontal()) {
y1 = y2 = this.options.position === 'top' ? this.bottom : this.top;
y1 += helpers.aliasPixel(this.ctx.lineWidth);
y2 += helpers.aliasPixel(this.ctx.lineWidth);
} else {
x1 = x2 = this.options.position === 'left' ? this.right : this.left;
x1 += helpers.aliasPixel(this.ctx.lineWidth);
x2 += helpers.aliasPixel(this.ctx.lineWidth);
}
this.ctx.beginPath();
this.ctx.moveTo(x1, y1);
this.ctx.lineTo(x2, y2);
this.ctx.stroke();
}
}
});
};
},{}],31:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.scaleService = {
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
// use the new chart options to grab the correct scale
constructors: {},
// Use a registration function so that we can move to an ES6 map when we no longer need to support
// old browsers
// Scale config defaults
defaults: {},
registerScaleType: function(type, scaleConstructor, defaults) {
this.constructors[type] = scaleConstructor;
this.defaults[type] = helpers.clone(defaults);
},
getScaleConstructor: function(type) {
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
},
getScaleDefaults: function(type) {
// Return the scale defaults merged with the global settings so that we always use the latest ones
return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
},
addScalesToLayout: function(chartInstance) {
// Adds each scale to the chart.boxes array to be sized accordingly
helpers.each(chartInstance.scales, function(scale) {
Chart.layoutService.addBox(chartInstance, scale);
});
}
};
};
},{}],32:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.title = {
display: false,
position: 'top',
fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
fontStyle: 'bold',
padding: 10,
// actual title
text: ''
};
Chart.Title = Chart.Element.extend({
initialize: function(config) {
helpers.extend(this, config);
this.options = helpers.configMerge(Chart.defaults.global.title, config.options);
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
},
// These methods are ordered by lifecyle. Utilities then follow.
beforeUpdate: helpers.noop,
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeUpdate();
// Absorb the master measurements
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.margins = margins;
// Dimensions
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
// Labels
this.beforeBuildLabels();
this.buildLabels();
this.afterBuildLabels();
// Fit
this.beforeFit();
this.fit();
this.afterFit();
//
this.afterUpdate();
return this.minSize;
},
afterUpdate: helpers.noop,
//
beforeSetDimensions: helpers.noop,
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
// Reset position before calculating rotation
this.width = this.maxWidth;
this.left = 0;
this.right = this.width;
} else {
this.height = this.maxHeight;
// Reset position before calculating rotation
this.top = 0;
this.bottom = this.height;
}
// Reset padding
this.paddingLeft = 0;
this.paddingTop = 0;
this.paddingRight = 0;
this.paddingBottom = 0;
// Reset minSize
this.minSize = {
width: 0,
height: 0
};
},
afterSetDimensions: helpers.noop,
//
beforeBuildLabels: helpers.noop,
buildLabels: helpers.noop,
afterBuildLabels: helpers.noop,
//
beforeFit: helpers.noop,
fit: function() {
var ctx = this.ctx;
var fontSize = helpers.getValueOrDefault(this.options.fontSize, Chart.defaults.global.defaultFontSize);
var fontStyle = helpers.getValueOrDefault(this.options.fontStyle, Chart.defaults.global.defaultFontStyle);
var fontFamily = helpers.getValueOrDefault(this.options.fontFamily, Chart.defaults.global.defaultFontFamily);
var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
// Width
if (this.isHorizontal()) {
this.minSize.width = this.maxWidth; // fill all the width
} else {
this.minSize.width = 0;
}
// height
if (this.isHorizontal()) {
this.minSize.height = 0;
} else {
this.minSize.height = this.maxHeight; // fill all the height
}
// Increase sizes here
if (this.isHorizontal()) {
// Title
if (this.options.display) {
this.minSize.height += fontSize + (this.options.padding * 2);
}
} else {
if (this.options.display) {
this.minSize.width += fontSize + (this.options.padding * 2);
}
}
this.width = this.minSize.width;
this.height = this.minSize.height;
},
afterFit: helpers.noop,
// Shared Methods
isHorizontal: function() {
return this.options.position === "top" || this.options.position === "bottom";
},
// Actualy draw the title block on the canvas
draw: function() {
if (this.options.display) {
var ctx = this.ctx;
var titleX, titleY;
var fontColor = helpers.getValueOrDefault(this.options.fontColor, Chart.defaults.global.defaultFontColor);
var fontSize = helpers.getValueOrDefault(this.options.fontSize, Chart.defaults.global.defaultFontSize);
var fontStyle = helpers.getValueOrDefault(this.options.fontStyle, Chart.defaults.global.defaultFontStyle);
var fontFamily = helpers.getValueOrDefault(this.options.fontFamily, Chart.defaults.global.defaultFontFamily);
var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillStyle = fontColor; // render in correct colour
ctx.font = titleFont;
// Horizontal
if (this.isHorizontal()) {
// Title
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
titleX = this.left + ((this.right - this.left) / 2); // midpoint of the width
titleY = this.top + ((this.bottom - this.top) / 2); // midpoint of the height
ctx.fillText(this.options.text, titleX, titleY);
} else {
// Title
titleX = this.options.position === 'left' ? this.left + (fontSize / 2) : this.right - (fontSize / 2);
titleY = this.top + ((this.bottom - this.top) / 2);
var rotation = this.options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
ctx.save();
ctx.translate(titleX, titleY);
ctx.rotate(rotation);
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillText(this.options.text, 0, 0);
ctx.restore();
}
}
}
});
};
},{}],33:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.tooltips = {
enabled: true,
custom: null,
mode: 'single',
backgroundColor: "rgba(0,0,0,0.8)",
titleFontStyle: "bold",
titleSpacing: 2,
titleMarginBottom: 6,
titleColor: "#fff",
titleAlign: "left",
bodySpacing: 2,
bodyColor: "#fff",
bodyAlign: "left",
footerFontStyle: "bold",
footerSpacing: 2,
footerMarginTop: 6,
footerColor: "#fff",
footerAlign: "left",
yPadding: 6,
xPadding: 6,
yAlign : 'center',
xAlign : 'center',
caretSize: 5,
cornerRadius: 6,
multiKeyBackground: '#fff',
callbacks: {
// Args are: (tooltipItems, data)
beforeTitle: helpers.noop,
title: function(tooltipItems, data) {
// Pick first xLabel for now
var title = '';
if (tooltipItems.length > 0) {
if (tooltipItems[0].xLabel) {
title = tooltipItems[0].xLabel;
} else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) {
title = data.labels[tooltipItems[0].index];
}
}
return title;
},
afterTitle: helpers.noop,
// Args are: (tooltipItems, data)
beforeBody: helpers.noop,
// Args are: (tooltipItem, data)
beforeLabel: helpers.noop,
label: function(tooltipItem, data) {
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
return datasetLabel + ': ' + tooltipItem.yLabel;
},
afterLabel: helpers.noop,
// Args are: (tooltipItems, data)
afterBody: helpers.noop,
// Args are: (tooltipItems, data)
beforeFooter: helpers.noop,
footer: helpers.noop,
afterFooter: helpers.noop
}
};
// Helper to push or concat based on if the 2nd parameter is an array or not
function pushOrConcat(base, toPush) {
if (toPush) {
if (helpers.isArray(toPush)) {
base = base.concat(toPush);
} else {
base.push(toPush);
}
}
return base;
}
Chart.Tooltip = Chart.Element.extend({
initialize: function() {
var options = this._options;
helpers.extend(this, {
_model: {
// Positioning
xPadding: options.tooltips.xPadding,
yPadding: options.tooltips.yPadding,
xAlign : options.tooltips.yAlign,
yAlign : options.tooltips.xAlign,
// Body
bodyColor: options.tooltips.bodyColor,
_bodyFontFamily: helpers.getValueOrDefault(options.tooltips.bodyFontFamily, Chart.defaults.global.defaultFontFamily),
_bodyFontStyle: helpers.getValueOrDefault(options.tooltips.bodyFontStyle, Chart.defaults.global.defaultFontStyle),
_bodyAlign: options.tooltips.bodyAlign,
bodyFontSize: helpers.getValueOrDefault(options.tooltips.bodyFontSize, Chart.defaults.global.defaultFontSize),
bodySpacing: options.tooltips.bodySpacing,
// Title
titleColor: options.tooltips.titleColor,
_titleFontFamily: helpers.getValueOrDefault(options.tooltips.titleFontFamily, Chart.defaults.global.defaultFontFamily),
_titleFontStyle: helpers.getValueOrDefault(options.tooltips.titleFontStyle, Chart.defaults.global.defaultFontStyle),
titleFontSize: helpers.getValueOrDefault(options.tooltips.titleFontSize, Chart.defaults.global.defaultFontSize),
_titleAlign: options.tooltips.titleAlign,
titleSpacing: options.tooltips.titleSpacing,
titleMarginBottom: options.tooltips.titleMarginBottom,
// Footer
footerColor: options.tooltips.footerColor,
_footerFontFamily: helpers.getValueOrDefault(options.tooltips.footerFontFamily, Chart.defaults.global.defaultFontFamily),
_footerFontStyle: helpers.getValueOrDefault(options.tooltips.footerFontStyle, Chart.defaults.global.defaultFontStyle),
footerFontSize: helpers.getValueOrDefault(options.tooltips.footerFontSize, Chart.defaults.global.defaultFontSize),
_footerAlign: options.tooltips.footerAlign,
footerSpacing: options.tooltips.footerSpacing,
footerMarginTop: options.tooltips.footerMarginTop,
// Appearance
caretSize: options.tooltips.caretSize,
cornerRadius: options.tooltips.cornerRadius,
backgroundColor: options.tooltips.backgroundColor,
opacity: 0,
legendColorBackground: options.tooltips.multiKeyBackground
}
});
},
// Get the title
// Args are: (tooltipItem, data)
getTitle: function() {
var beforeTitle = this._options.tooltips.callbacks.beforeTitle.apply(this, arguments),
title = this._options.tooltips.callbacks.title.apply(this, arguments),
afterTitle = this._options.tooltips.callbacks.afterTitle.apply(this, arguments);
var lines = [];
lines = pushOrConcat(lines, beforeTitle);
lines = pushOrConcat(lines, title);
lines = pushOrConcat(lines, afterTitle);
return lines;
},
// Args are: (tooltipItem, data)
getBeforeBody: function() {
var lines = this._options.tooltips.callbacks.beforeBody.apply(this, arguments);
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
},
// Args are: (tooltipItem, data)
getBody: function(tooltipItems, data) {
var lines = [];
helpers.each(tooltipItems, function(bodyItem) {
helpers.pushAllIfDefined(this._options.tooltips.callbacks.beforeLabel.call(this, bodyItem, data), lines);
helpers.pushAllIfDefined(this._options.tooltips.callbacks.label.call(this, bodyItem, data), lines);
helpers.pushAllIfDefined(this._options.tooltips.callbacks.afterLabel.call(this, bodyItem, data), lines);
}, this);
return lines;
},
// Args are: (tooltipItem, data)
getAfterBody: function() {
var lines = this._options.tooltips.callbacks.afterBody.apply(this, arguments);
return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
},
// Get the footer and beforeFooter and afterFooter lines
// Args are: (tooltipItem, data)
getFooter: function() {
var beforeFooter = this._options.tooltips.callbacks.beforeFooter.apply(this, arguments);
var footer = this._options.tooltips.callbacks.footer.apply(this, arguments);
var afterFooter = this._options.tooltips.callbacks.afterFooter.apply(this, arguments);
var lines = [];
lines = pushOrConcat(lines, beforeFooter);
lines = pushOrConcat(lines, footer);
lines = pushOrConcat(lines, afterFooter);
return lines;
},
getAveragePosition: function(elements) {
if (!elements.length) {
return false;
}
var xPositions = [];
var yPositions = [];
helpers.each(elements, function(el) {
if (el) {
var pos = el.tooltipPosition();
xPositions.push(pos.x);
yPositions.push(pos.y);
}
});
var x = 0,
y = 0;
for (var i = 0; i < xPositions.length; i++) {
x += xPositions[i];
y += yPositions[i];
}
return {
x: Math.round(x / xPositions.length),
y: Math.round(y / xPositions.length)
};
},
update: function(changed) {
if (this._active.length) {
this._model.opacity = 1;
var element = this._active[0],
labelColors = [],
tooltipPosition;
var tooltipItems = [];
if (this._options.tooltips.mode === 'single') {
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
tooltipItems.push({
xLabel: element._xScale ? element._xScale.getLabelForIndex(element._index, element._datasetIndex) : '',
yLabel: yScale ? yScale.getLabelForIndex(element._index, element._datasetIndex) : '',
index: element._index,
datasetIndex: element._datasetIndex
});
tooltipPosition = this.getAveragePosition(this._active);
} else {
helpers.each(this._data.datasets, function(dataset, datasetIndex) {
if (!helpers.isDatasetVisible(dataset)) {
return;
}
var currentElement = dataset.metaData[element._index];
if (currentElement) {
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
tooltipItems.push({
xLabel: currentElement._xScale ? currentElement._xScale.getLabelForIndex(currentElement._index, currentElement._datasetIndex) : '',
yLabel: yScale ? yScale.getLabelForIndex(currentElement._index, currentElement._datasetIndex) : '',
index: element._index,
datasetIndex: datasetIndex
});
}
}, null, element._yScale.options.stacked);
helpers.each(this._active, function(active) {
if (active) {
labelColors.push({
borderColor: active._view.borderColor,
backgroundColor: active._view.backgroundColor
});
}
}, null, element._yScale.options.stacked);
tooltipPosition = this.getAveragePosition(this._active);
tooltipPosition.y = this._active[0]._yScale.getPixelForDecimal(0.5);
}
// Build the Text Lines
helpers.extend(this._model, {
title: this.getTitle(tooltipItems, this._data),
beforeBody: this.getBeforeBody(tooltipItems, this._data),
body: this.getBody(tooltipItems, this._data),
afterBody: this.getAfterBody(tooltipItems, this._data),
footer: this.getFooter(tooltipItems, this._data)
});
helpers.extend(this._model, {
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2),
labelColors: labelColors
});
// We need to determine alignment of
var tooltipSize = this.getTooltipSize(this._model);
this.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas
helpers.extend(this._model, this.getBackgroundPoint(this._model, tooltipSize));
} else {
this._model.opacity = 0;
}
if (changed && this._options.tooltips.custom) {
this._options.tooltips.custom.call(this, this._model);
}
return this;
},
getTooltipSize: function getTooltipSize(vm) {
var ctx = this._chart.ctx;
var size = {
height: vm.yPadding * 2, // Tooltip Padding
width: 0
};
var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length;
size.height += vm.title.length * vm.titleFontSize; // Title Lines
size.height += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing
size.height += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin
size.height += combinedBodyLength * vm.bodyFontSize; // Body Lines
size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing
size.height += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin
size.height += vm.footer.length * (vm.footerFontSize); // Footer Lines
size.height += vm.footer.length ? (vm.footer.length - 1) * vm.footerSpacing : 0; // Footer Line Spacing
// Width
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
});
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
helpers.each(vm.beforeBody.concat(vm.afterBody), function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
});
helpers.each(vm.body, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0));
}, this);
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(line) {
size.width = Math.max(size.width, ctx.measureText(line).width);
});
size.width += 2 * vm.xPadding;
return size;
},
determineAlignment: function determineAlignment(size) {
if (this._model.y < size.height) {
this._model.yAlign = 'top';
} else if (this._model.y > (this._chart.height - size.height)) {
this._model.yAlign = 'bottom';
}
var lf, rf; // functions to determine left, right alignment
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
var _this = this;
var midX = (this._chartInstance.chartArea.left + this._chartInstance.chartArea.right) / 2;
var midY = (this._chartInstance.chartArea.top + this._chartInstance.chartArea.bottom) / 2;
if (this._model.yAlign === 'center') {
lf = function(x) {
return x <= midX;
};
rf = function(x) {
return x > midX;
};
} else {
lf = function(x) {
return x <= (size.width / 2);
};
rf = function(x) {
return x >= (_this._chart.width - (size.width / 2));
};
}
olf = function(x) {
return x + size.width > _this._chart.width;
};
orf = function(x) {
return x - size.width < 0;
};
yf = function(y) {
return y <= midY ? 'top' : 'bottom';
};
if (lf(this._model.x)) {
this._model.xAlign = 'left';
// Is tooltip too wide and goes over the right side of the chart.?
if (olf(this._model.x)) {
this._model.xAlign = 'center';
this._model.yAlign = yf(this._model.y);
}
} else if (rf(this._model.x)) {
this._model.xAlign = 'right';
// Is tooltip too wide and goes outside left edge of canvas?
if (orf(this._model.x)) {
this._model.xAlign = 'center';
this._model.yAlign = yf(this._model.y);
}
}
},
getBackgroundPoint: function getBackgroundPoint(vm, size) {
// Background Position
var pt = {
x: vm.x,
y: vm.y
};
if (vm.xAlign === 'right') {
pt.x -= size.width;
} else if (vm.xAlign === 'center') {
pt.x -= (size.width / 2);
}
if (vm.yAlign === 'top') {
pt.y += vm.caretPadding + vm.caretSize;
} else if (vm.yAlign === 'bottom') {
pt.y -= size.height + vm.caretPadding + vm.caretSize;
} else {
pt.y -= (size.height / 2);
}
if (vm.yAlign === 'center') {
if (vm.xAlign === 'left') {
pt.x += vm.caretPadding + vm.caretSize;
} else if (vm.xAlign === 'right') {
pt.x -= vm.caretPadding + vm.caretSize;
}
} else {
if (vm.xAlign === 'left') {
pt.x -= vm.cornerRadius + vm.caretPadding;
} else if (vm.xAlign === 'right') {
pt.x += vm.cornerRadius + vm.caretPadding;
}
}
return pt;
},
drawCaret: function drawCaret(tooltipPoint, size, opacity, caretPadding) {
var vm = this._view;
var ctx = this._chart.ctx;
var x1, x2, x3;
var y1, y2, y3;
if (vm.yAlign === 'center') {
// Left or right side
if (vm.xAlign === 'left') {
x1 = tooltipPoint.x;
x2 = x1 - vm.caretSize;
x3 = x1;
} else {
x1 = tooltipPoint.x + size.width;
x2 = x1 + vm.caretSize;
x3 = x1;
}
y2 = tooltipPoint.y + (size.height / 2);
y1 = y2 - vm.caretSize;
y3 = y2 + vm.caretSize;
} else {
if (vm.xAlign === 'left') {
x1 = tooltipPoint.x + vm.cornerRadius;
x2 = x1 + vm.caretSize;
x3 = x2 + vm.caretSize;
} else if (vm.xAlign === 'right') {
x1 = tooltipPoint.x + size.width - vm.cornerRadius;
x2 = x1 - vm.caretSize;
x3 = x2 - vm.caretSize;
} else {
x2 = tooltipPoint.x + (size.width / 2);
x1 = x2 - vm.caretSize;
x3 = x2 + vm.caretSize;
}
if (vm.yAlign === 'top') {
y1 = tooltipPoint.y;
y2 = y1 - vm.caretSize;
y3 = y1;
} else {
y1 = tooltipPoint.y + size.height;
y2 = y1 + vm.caretSize;
y3 = y1;
}
}
var bgColor = helpers.color(vm.backgroundColor);
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fill();
},
drawTitle: function drawTitle(pt, vm, ctx, opacity) {
if (vm.title.length) {
ctx.textAlign = vm._titleAlign;
ctx.textBaseline = "top";
var titleColor = helpers.color(vm.titleColor);
ctx.fillStyle = titleColor.alpha(opacity * titleColor.alpha()).rgbString();
ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
helpers.each(vm.title, function(title, i) {
ctx.fillText(title, pt.x, pt.y);
pt.y += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing
if (i + 1 === vm.title.length) {
pt.y += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing
}
});
}
},
drawBody: function drawBody(pt, vm, ctx, opacity) {
ctx.textAlign = vm._bodyAlign;
ctx.textBaseline = "top";
var bodyColor = helpers.color(vm.bodyColor);
ctx.fillStyle = bodyColor.alpha(opacity * bodyColor.alpha()).rgbString();
ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
// Before Body
helpers.each(vm.beforeBody, function(beforeBody) {
ctx.fillText(beforeBody, pt.x, pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
});
helpers.each(vm.body, function(body, i) {
// Draw Legend-like boxes if needed
if (this._options.tooltips.mode !== 'single') {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString();
ctx.fillRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Border
ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString();
ctx.strokeRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize);
// Inner square
ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString();
ctx.fillRect(pt.x + 1, pt.y + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2);
ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text
}
// Body Line
ctx.fillText(body, pt.x + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), pt.y);
pt.y += vm.bodyFontSize + vm.bodySpacing;
}, this);
// After Body
helpers.each(vm.afterBody, function(afterBody) {
ctx.fillText(afterBody, pt.x, pt.y);
pt.y += vm.bodyFontSize;
});
pt.y -= vm.bodySpacing; // Remove last body spacing
},
drawFooter: function drawFooter(pt, vm, ctx, opacity) {
if (vm.footer.length) {
pt.y += vm.footerMarginTop;
ctx.textAlign = vm._footerAlign;
ctx.textBaseline = "top";
var footerColor = helpers.color(vm.footerColor);
ctx.fillStyle = footerColor.alpha(opacity * footerColor.alpha()).rgbString();
ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
helpers.each(vm.footer, function(footer) {
ctx.fillText(footer, pt.x, pt.y);
pt.y += vm.footerFontSize + vm.footerSpacing;
});
}
},
draw: function draw() {
var ctx = this._chart.ctx;
var vm = this._view;
if (vm.opacity === 0) {
return;
}
var caretPadding = vm.caretPadding;
var tooltipSize = this.getTooltipSize(vm);
var pt = {
x: vm.x,
y: vm.y
};
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
if (this._options.tooltips.enabled) {
// Draw Background
var bgColor = helpers.color(vm.backgroundColor);
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
ctx.fill();
// Draw Caret
this.drawCaret(pt, tooltipSize, opacity, caretPadding);
// Draw Title, Body, and Footer
pt.x += vm.xPadding;
pt.y += vm.yPadding;
// Titles
this.drawTitle(pt, vm, ctx, opacity);
// Body
this.drawBody(pt, vm, ctx, opacity);
// Footer
this.drawFooter(pt, vm, ctx, opacity);
}
}
});
};
},{}],34:[function(require,module,exports){
"use strict";
module.exports = function(Chart, moment) {
var helpers = Chart.helpers;
Chart.defaults.global.elements.arc = {
backgroundColor: Chart.defaults.global.defaultColor,
borderColor: "#fff",
borderWidth: 2
};
Chart.elements.Arc = Chart.Element.extend({
inLabelRange: function(mouseX) {
var vm = this._view;
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
} else {
return false;
}
},
inRange: function(chartX, chartY) {
var vm = this._view;
if (vm) {
var pointRelativePosition = helpers.getAngleFromPoint(vm, {
x: chartX,
y: chartY
});
//Sanitise angle range
var startAngle = vm.startAngle;
var endAngle = vm.endAngle;
while (endAngle < startAngle) {
endAngle += 2.0 * Math.PI;
}
while (pointRelativePosition.angle > endAngle) {
pointRelativePosition.angle -= 2.0 * Math.PI;
}
while (pointRelativePosition.angle < startAngle) {
pointRelativePosition.angle += 2.0 * Math.PI;
}
//Check if within the range of the open/close angle
var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle),
withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius);
return (betweenAngles && withinRadius);
} else {
return false;
}
},
tooltipPosition: function() {
var vm = this._view;
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
return {
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
};
},
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
ctx.beginPath();
ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle);
ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true);
ctx.closePath();
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.fillStyle = vm.backgroundColor;
ctx.fill();
ctx.lineJoin = 'bevel';
if (vm.borderWidth) {
ctx.stroke();
}
}
});
};
},{}],35:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.elements.line = {
tension: 0.4,
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 3,
borderColor: Chart.defaults.global.defaultColor,
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
fill: true // do we fill in the area between the line and its base axis
};
Chart.elements.Line = Chart.Element.extend({
lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) {
var ctx = this._chart.ctx;
if (point._view.skip) {
skipHandler.call(this, previousPoint, point, nextPoint);
} else if (previousPoint._view.skip) {
previousSkipHandler.call(this, previousPoint, point, nextPoint);
} else if (point._view.tension === 0) {
ctx.lineTo(point._view.x, point._view.y);
} else {
// Line between points
ctx.bezierCurveTo(
previousPoint._view.controlPointNextX,
previousPoint._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
}
},
draw: function() {
var _this = this;
var vm = this._view;
var ctx = this._chart.ctx;
var first = this._children[0];
var last = this._children[this._children.length - 1];
function loopBackToStart(drawLineToCenter) {
if (!first._view.skip && !last._view.skip) {
// Draw a bezier line from last to first
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
} else if (drawLineToCenter) {
// Go to center
ctx.lineTo(_this._view.scaleZero.x, _this._view.scaleZero.y);
}
}
ctx.save();
// If we had points and want to fill this line, do so.
if (this._children.length > 0 && vm.fill) {
// Draw the background first (so the border is always on top)
ctx.beginPath();
helpers.each(this._children, function(point, index) {
var previous = helpers.previousItem(this._children, index);
var next = helpers.nextItem(this._children, index);
// First point moves to it's starting position no matter what
if (index === 0) {
if (this._loop) {
ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y);
} else {
ctx.moveTo(point._view.x, vm.scaleZero);
}
if (point._view.skip) {
if (!this._loop) {
ctx.moveTo(next._view.x, this._view.scaleZero);
}
} else {
ctx.lineTo(point._view.x, point._view.y);
}
} else {
this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
if (this._loop) {
// Go to center
ctx.lineTo(this._view.scaleZero.x, this._view.scaleZero.y);
} else {
ctx.lineTo(previousPoint._view.x, this._view.scaleZero);
ctx.moveTo(nextPoint._view.x, this._view.scaleZero);
}
}, function(previousPoint, point) {
// If we skipped the last point, draw a line to ourselves so that the fill is nice
ctx.lineTo(point._view.x, point._view.y);
});
}
}, this);
// For radial scales, loop back around to the first point
if (this._loop) {
loopBackToStart(true);
} else {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
}
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.closePath();
ctx.fill();
}
// Now draw the line between all the points with any borders
ctx.lineCap = vm.borderCapStyle || Chart.defaults.global.elements.line.borderCapStyle;
// IE 9 and 10 do not support line dash
if (ctx.setLineDash) {
ctx.setLineDash(vm.borderDash || Chart.defaults.global.elements.line.borderDash);
}
ctx.lineDashOffset = vm.borderDashOffset || Chart.defaults.global.elements.line.borderDashOffset;
ctx.lineJoin = vm.borderJoinStyle || Chart.defaults.global.elements.line.borderJoinStyle;
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.line.borderWidth;
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.beginPath();
helpers.each(this._children, function(point, index) {
var previous = helpers.previousItem(this._children, index);
var next = helpers.nextItem(this._children, index);
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
} else {
this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
ctx.moveTo(nextPoint._view.x, nextPoint._view.y);
}, function(previousPoint, point) {
// If we skipped the last point, move up to our point preventing a line from being drawn
ctx.moveTo(point._view.x, point._view.y);
});
}
}, this);
if (this._loop && this._children.length > 0) {
loopBackToStart();
}
ctx.stroke();
ctx.restore();
}
});
};
},{}],36:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.elements.point = {
radius: 3,
pointStyle: 'circle',
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 1,
borderColor: Chart.defaults.global.defaultColor,
// Hover
hitRadius: 1,
hoverRadius: 4,
hoverBorderWidth: 1
};
Chart.elements.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
if (vm) {
var hoverRange = vm.hitRadius + vm.radius;
return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2));
} else {
return false;
}
},
inLabelRange: function(mouseX) {
var vm = this._view;
if (vm) {
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2));
} else {
return false;
}
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: vm.y,
padding: vm.radius + vm.borderWidth
};
},
draw: function() {
var vm = this._view;
var ctx = this._chart.ctx;
if (vm.skip) {
return;
}
if (typeof vm.pointStyle === 'object' && ((vm.pointStyle.toString() === '[object HTMLImageElement]') || (vm.pointStyle.toString() === '[object HTMLCanvasElement]'))) {
ctx.drawImage(vm.pointStyle, vm.x - vm.pointStyle.width / 2, vm.y - vm.pointStyle.height / 2);
return;
}
if (!isNaN(vm.radius) && vm.radius > 0) {
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, Chart.defaults.global.elements.point.borderWidth);
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
var radius = vm.radius;
var xOffset;
var yOffset;
switch (vm.pointStyle) {
// Default includes circle
default: ctx.beginPath();
ctx.arc(vm.x, vm.y, radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
break;
case 'triangle':
ctx.beginPath();
var edgeLength = 3 * radius / Math.sqrt(3);
var height = edgeLength * Math.sqrt(3) / 2;
ctx.moveTo(vm.x - edgeLength / 2, vm.y + height / 3);
ctx.lineTo(vm.x + edgeLength / 2, vm.y + height / 3);
ctx.lineTo(vm.x, vm.y - 2 * height / 3);
ctx.closePath();
ctx.fill();
break;
case 'rect':
ctx.fillRect(vm.x - 1 / Math.SQRT2 * radius, vm.y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
ctx.strokeRect(vm.x - 1 / Math.SQRT2 * radius, vm.y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
break;
case 'rectRot':
ctx.translate(vm.x, vm.y);
ctx.rotate(Math.PI / 4);
ctx.fillRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
ctx.strokeRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
ctx.setTransform(1, 0, 0, 1, 0, 0);
break;
case 'cross':
ctx.beginPath();
ctx.moveTo(vm.x, vm.y + radius);
ctx.lineTo(vm.x, vm.y - radius);
ctx.moveTo(vm.x - radius, vm.y);
ctx.lineTo(vm.x + radius, vm.y);
ctx.closePath();
break;
case 'crossRot':
ctx.beginPath();
xOffset = Math.cos(Math.PI / 4) * radius;
yOffset = Math.sin(Math.PI / 4) * radius;
ctx.moveTo(vm.x - xOffset, vm.y - yOffset);
ctx.lineTo(vm.x + xOffset, vm.y + yOffset);
ctx.moveTo(vm.x - xOffset, vm.y + yOffset);
ctx.lineTo(vm.x + xOffset, vm.y - yOffset);
ctx.closePath();
break;
case 'star':
ctx.beginPath();
ctx.moveTo(vm.x, vm.y + radius);
ctx.lineTo(vm.x, vm.y - radius);
ctx.moveTo(vm.x - radius, vm.y);
ctx.lineTo(vm.x + radius, vm.y);
xOffset = Math.cos(Math.PI / 4) * radius;
yOffset = Math.sin(Math.PI / 4) * radius;
ctx.moveTo(vm.x - xOffset, vm.y - yOffset);
ctx.lineTo(vm.x + xOffset, vm.y + yOffset);
ctx.moveTo(vm.x - xOffset, vm.y + yOffset);
ctx.lineTo(vm.x + xOffset, vm.y - yOffset);
ctx.closePath();
break;
case 'line':
ctx.beginPath();
ctx.moveTo(vm.x - radius, vm.y);
ctx.lineTo(vm.x + radius, vm.y);
ctx.closePath();
break;
case 'dash':
ctx.beginPath();
ctx.moveTo(vm.x, vm.y);
ctx.lineTo(vm.x + radius, vm.y);
ctx.closePath();
break;
}
ctx.stroke();
}
}
});
};
},{}],37:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.global.elements.rectangle = {
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 0,
borderColor: Chart.defaults.global.defaultColor,
borderSkipped: 'bottom'
};
Chart.elements.Rectangle = Chart.Element.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var halfWidth = vm.width / 2,
leftX = vm.x - halfWidth,
rightX = vm.x + halfWidth,
top = vm.base - (vm.base - vm.y),
halfStroke = vm.borderWidth / 2;
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (vm.borderWidth) {
leftX += halfStroke;
rightX -= halfStroke;
top += halfStroke;
}
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
// | 0 3 |
var corners = [
[leftX, vm.base],
[leftX, top],
[rightX, top],
[rightX, vm.base]
];
// Find first (starting) corner with fallback to 'bottom'
var borders = ['bottom', 'left', 'top', 'right'];
var startCorner = borders.indexOf(vm.borderSkipped, 0);
if (startCorner === -1)
startCorner = 0;
function cornerAt(index) {
return corners[(startCorner + index) % 4];
}
// Draw rectangle from 'startCorner'
ctx.moveTo.apply(ctx, cornerAt(0));
for (var i = 1; i < 4; i++)
ctx.lineTo.apply(ctx, cornerAt(i));
ctx.fill();
if (vm.borderWidth) {
ctx.stroke();
}
},
height: function() {
var vm = this._view;
return vm.base - vm.y;
},
inRange: function(mouseX, mouseY) {
var vm = this._view;
var inRange = false;
if (vm) {
if (vm.y < vm.base) {
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base);
} else {
inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y);
}
}
return inRange;
},
inLabelRange: function(mouseX) {
var vm = this._view;
if (vm) {
return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2);
} else {
return false;
}
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: vm.y
};
}
});
};
},{}],38:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
// Default config for a category scale
var defaultConfig = {
position: "bottom"
};
var DatasetScale = Chart.Scale.extend({
// Implement this so that
determineDataLimits: function() {
this.minIndex = 0;
this.maxIndex = this.chart.data.labels.length;
var findIndex;
if (this.options.ticks.min !== undefined) {
// user specified min value
findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.min);
this.minIndex = findIndex !== -1 ? findIndex : this.minIndex;
}
if (this.options.ticks.max !== undefined) {
// user specified max value
findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.max);
this.maxIndex = findIndex !== -1 ? findIndex : this.maxIndex;
}
this.min = this.chart.data.labels[this.minIndex];
this.max = this.chart.data.labels[this.maxIndex];
},
buildTicks: function(index) {
// If we are viewing some subset of labels, slice the original array
this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
},
getLabelForIndex: function(index, datasetIndex) {
return this.ticks[index];
},
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
// 1 is added because we need the length but we have the indexes
var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var valueWidth = innerWidth / offsetAmt;
var widthOffset = (valueWidth * (index - this.minIndex)) + this.paddingLeft;
if (this.options.gridLines.offsetGridLines && includeOffset) {
widthOffset += (valueWidth / 2);
}
return this.left + Math.round(widthOffset);
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
var valueHeight = innerHeight / offsetAmt;
var heightOffset = (valueHeight * (index - this.minIndex)) + this.paddingTop;
if (this.options.gridLines.offsetGridLines && includeOffset) {
heightOffset += (valueHeight / 2);
}
return this.top + Math.round(heightOffset);
}
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
}
});
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
};
},{}],39:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
position: "left",
ticks: {
callback: function(tickValue, index, ticks) {
// If we have lots of ticks, don't use the ones
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
if (Math.abs(delta) > 1) {
if (tickValue !== Math.floor(tickValue)) {
// not an integer
delta = tickValue - Math.floor(tickValue);
}
}
var logDelta = helpers.log10(Math.abs(delta));
var tickString = '';
if (tickValue !== 0) {
var numDecimal = -1 * Math.floor(logDelta);
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
tickString = tickValue.toFixed(numDecimal);
} else {
tickString = '0'; // never show decimal places for 0
}
return tickString;
}
}
};
var LinearScale = Chart.Scale.extend({
determineDataLimits: function() {
// First Calculate the range
this.min = null;
this.max = null;
if (this.options.stacked) {
var valuesPerType = {};
var hasPositiveValues = false;
var hasNegativeValues = false;
helpers.each(this.chart.data.datasets, function(dataset) {
if (valuesPerType[dataset.type] === undefined) {
valuesPerType[dataset.type] = {
positiveValues: [],
negativeValues: []
};
}
// Store these per type
var positiveValues = valuesPerType[dataset.type].positiveValues;
var negativeValues = valuesPerType[dataset.type].negativeValues;
if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) {
helpers.each(dataset.data, function(rawValue, index) {
var value = +this.getRightValue(rawValue);
if (isNaN(value)) {
return;
}
positiveValues[index] = positiveValues[index] || 0;
negativeValues[index] = negativeValues[index] || 0;
if (this.options.relativePoints) {
positiveValues[index] = 100;
} else {
if (value < 0) {
hasNegativeValues = true;
negativeValues[index] += value;
} else {
hasPositiveValues = true;
positiveValues[index] += value;
}
}
}, this);
}
}, this);
helpers.each(valuesPerType, function(valuesForType) {
var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
var minVal = helpers.min(values);
var maxVal = helpers.max(values);
this.min = this.min === null ? minVal : Math.min(this.min, minVal);
this.max = this.max === null ? maxVal : Math.max(this.max, maxVal);
}, this);
} else {
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) {
helpers.each(dataset.data, function(rawValue, index) {
var value = +this.getRightValue(rawValue);
if (isNaN(value)) {
return;
}
if (this.min === null) {
this.min = value;
} else if (value < this.min) {
this.min = value;
}
if (this.max === null) {
this.max = value;
} else if (value > this.max) {
this.max = value;
}
}, this);
}
}, this);
}
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
// do nothing since that would make the chart weird. If the user really wants a weird chart
// axis, they can manually override it
if (this.options.ticks.beginAtZero) {
var minSign = helpers.sign(this.min);
var maxSign = helpers.sign(this.max);
if (minSign < 0 && maxSign < 0) {
// move the top up to 0
this.max = 0;
} else if (minSign > 0 && maxSign > 0) {
// move the botttom down to 0
this.min = 0;
}
}
if (this.options.ticks.min !== undefined) {
this.min = this.options.ticks.min;
} else if (this.options.ticks.suggestedMin !== undefined) {
this.min = Math.min(this.min, this.options.ticks.suggestedMin);
}
if (this.options.ticks.max !== undefined) {
this.max = this.options.ticks.max;
} else if (this.options.ticks.suggestedMax !== undefined) {
this.max = Math.max(this.max, this.options.ticks.suggestedMax);
}
if (this.min === this.max) {
this.min--;
this.max++;
}
},
buildTicks: function() {
// Then calulate the ticks
this.ticks = [];
// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var maxTicks;
if (this.isHorizontal()) {
maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.width / 50));
} else {
// The factor of 2 used to scale the font size has been experimentally determined.
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.height / (2 * tickFontSize)));
}
// Make sure we always have at least 2 ticks
maxTicks = Math.max(2, maxTicks);
// To get a "nice" value for the tick spacing, we will use the appropriately named
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
// for details.
var spacing;
var fixedStepSizeSet = (this.options.ticks.fixedStepSize && this.options.ticks.fixedStepSize > 0) || (this.options.ticks.stepSize && this.options.ticks.stepSize > 0);
if (fixedStepSizeSet) {
spacing = helpers.getValueOrDefault(this.options.ticks.fixedStepSize, this.options.ticks.stepSize);
} else {
var niceRange = helpers.niceNum(this.max - this.min, false);
spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
}
var niceMin = Math.floor(this.min / spacing) * spacing;
var niceMax = Math.ceil(this.max / spacing) * spacing;
var numSpaces = (niceMax - niceMin) / spacing;
// If very close to our rounded value, use it.
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
numSpaces = Math.round(numSpaces);
} else {
numSpaces = Math.ceil(numSpaces);
}
// Put the values into the ticks array
this.ticks.push(this.options.ticks.min !== undefined ? this.options.ticks.min : niceMin);
for (var j = 1; j < numSpaces; ++j) {
this.ticks.push(niceMin + (j * spacing));
}
this.ticks.push(this.options.ticks.max !== undefined ? this.options.ticks.max : niceMax);
if (this.options.position === "left" || this.options.position === "right") {
// We are in a vertical orientation. The top value is the highest. So reverse the array
this.ticks.reverse();
}
// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
this.max = helpers.max(this.ticks);
this.min = helpers.min(this.ticks);
if (this.options.ticks.reverse) {
this.ticks.reverse();
this.start = this.max;
this.end = this.min;
} else {
this.start = this.min;
this.end = this.max;
}
},
getLabelForIndex: function(index, datasetIndex) {
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
},
convertTicksToLabels: function() {
this.ticksAsNumbers = this.ticks.slice();
this.zeroLineIndex = this.ticks.indexOf(0);
Chart.Scale.prototype.convertTicksToLabels.call(this);
},
// Utils
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
// This must be called after fit has been run so that
// this.left, this.top, this.right, and this.bottom have been defined
var rightValue = +this.getRightValue(value);
var pixel;
var range = this.end - this.start;
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
pixel = this.left + (innerWidth / range * (rightValue - this.start));
return Math.round(pixel + this.paddingLeft);
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (rightValue - this.start));
return Math.round(pixel);
}
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset);
}
});
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
};
},{}],40:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
position: "left",
// label settings
ticks: {
callback: function(value, index, arr) {
var remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
return value.toExponential();
} else {
return '';
}
}
}
};
var LogarithmicScale = Chart.Scale.extend({
determineDataLimits: function() {
// Calculate Range
this.min = null;
this.max = null;
if (this.options.stacked) {
var valuesPerType = {};
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) {
if (valuesPerType[dataset.type] === undefined) {
valuesPerType[dataset.type] = [];
}
helpers.each(dataset.data, function(rawValue, index) {
var values = valuesPerType[dataset.type];
var value = +this.getRightValue(rawValue);
if (isNaN(value)) {
return;
}
values[index] = values[index] || 0;
if (this.options.relativePoints) {
values[index] = 100;
} else {
// Don't need to split positive and negative since the log scale can't handle a 0 crossing
values[index] += value;
}
}, this);
}
}, this);
helpers.each(valuesPerType, function(valuesForType) {
var minVal = helpers.min(valuesForType);
var maxVal = helpers.max(valuesForType);
this.min = this.min === null ? minVal : Math.min(this.min, minVal);
this.max = this.max === null ? maxVal : Math.max(this.max, maxVal);
}, this);
} else {
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) {
helpers.each(dataset.data, function(rawValue, index) {
var value = +this.getRightValue(rawValue);
if (isNaN(value)) {
return;
}
if (this.min === null) {
this.min = value;
} else if (value < this.min) {
this.min = value;
}
if (this.max === null) {
this.max = value;
} else if (value > this.max) {
this.max = value;
}
}, this);
}
}, this);
}
this.min = this.options.ticks.min !== undefined ? this.options.ticks.min : this.min;
this.max = this.options.ticks.max !== undefined ? this.options.ticks.max : this.max;
if (this.min === this.max) {
if (this.min !== 0 && this.min !== null) {
this.min = Math.pow(10, Math.floor(helpers.log10(this.min)) - 1);
this.max = Math.pow(10, Math.floor(helpers.log10(this.max)) + 1);
} else {
this.min = 1;
this.max = 10;
}
}
},
buildTicks: function() {
// Reset the ticks array. Later on, we will draw a grid line at these positions
// The array simply contains the numerical value of the spots where ticks will be
this.ticks = [];
// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var tickVal = this.options.ticks.min !== undefined ? this.options.ticks.min : Math.pow(10, Math.floor(helpers.log10(this.min)));
while (tickVal < this.max) {
this.ticks.push(tickVal);
var exp = Math.floor(helpers.log10(tickVal));
var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
if (significand === 10) {
significand = 1;
++exp;
}
tickVal = significand * Math.pow(10, exp);
}
var lastTick = this.options.ticks.max !== undefined ? this.options.ticks.max : tickVal;
this.ticks.push(lastTick);
if (this.options.position === "left" || this.options.position === "right") {
// We are in a vertical orientation. The top value is the highest. So reverse the array
this.ticks.reverse();
}
// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
this.max = helpers.max(this.ticks);
this.min = helpers.min(this.ticks);
if (this.options.ticks.reverse) {
this.ticks.reverse();
this.start = this.max;
this.end = this.min;
} else {
this.start = this.min;
this.end = this.max;
}
},
convertTicksToLabels: function() {
this.tickValues = this.ticks.slice();
Chart.Scale.prototype.convertTicksToLabels.call(this);
},
// Get the correct tooltip label
getLabelForIndex: function(index, datasetIndex) {
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
},
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.tickValues[index], null, null, includeOffset);
},
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
var pixel;
var newVal = +this.getRightValue(value);
var range = helpers.log10(this.end) - helpers.log10(this.start);
if (this.isHorizontal()) {
if (newVal === 0) {
pixel = this.left + this.paddingLeft;
} else {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
pixel = this.left + (innerWidth / range * (helpers.log10(newVal) - helpers.log10(this.start)));
pixel += this.paddingLeft;
}
} else {
// Bottom - top since pixels increase downard on a screen
if (newVal === 0) {
pixel = this.top + this.paddingTop;
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (helpers.log10(newVal) - helpers.log10(this.start)));
}
}
return pixel;
}
});
Chart.scaleService.registerScaleType("logarithmic", LogarithmicScale, defaultConfig);
};
},{}],41:[function(require,module,exports){
"use strict";
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
display: true,
//Boolean - Whether to animate scaling the chart from the centre
animate: true,
lineArc: false,
position: "chartArea",
angleLines: {
display: true,
color: "rgba(0, 0, 0, 0.1)",
lineWidth: 1
},
// label settings
ticks: {
//Boolean - Show a backdrop to the scale label
showLabelBackdrop: true,
//String - The colour of the label backdrop
backdropColor: "rgba(255,255,255,0.75)",
//Number - The backdrop padding above & below the label in pixels
backdropPaddingY: 2,
//Number - The backdrop padding to the side of the label in pixels
backdropPaddingX: 2
},
pointLabels: {
//Number - Point label font size in pixels
fontSize: 10,
//Function - Used to convert point labels
callback: function(label) {
return label;
}
}
};
var LinearRadialScale = Chart.Scale.extend({
getValueCount: function() {
return this.chart.data.labels.length;
},
setDimensions: function() {
// Set the unconstrained dimension before label rotation
this.width = this.maxWidth;
this.height = this.maxHeight;
this.xCenter = Math.round(this.width / 2);
this.yCenter = Math.round(this.height / 2);
var minSize = helpers.min([this.height, this.width]);
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
this.drawingArea = (this.options.display) ? (minSize / 2) - (tickFontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2);
},
determineDataLimits: function() {
this.min = null;
this.max = null;
helpers.each(this.chart.data.datasets, function(dataset) {
if (helpers.isDatasetVisible(dataset)) {
helpers.each(dataset.data, function(rawValue, index) {
var value = +this.getRightValue(rawValue);
if (isNaN(value)) {
return;
}
if (this.min === null) {
this.min = value;
} else if (value < this.min) {
this.min = value;
}
if (this.max === null) {
this.max = value;
} else if (value > this.max) {
this.max = value;
}
}, this);
}
}, this);
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
// do nothing since that would make the chart weird. If the user really wants a weird chart
// axis, they can manually override it
if (this.options.ticks.beginAtZero) {
var minSign = helpers.sign(this.min);
var maxSign = helpers.sign(this.max);
if (minSign < 0 && maxSign < 0) {
// move the top up to 0
this.max = 0;
} else if (minSign > 0 && maxSign > 0) {
// move the botttom down to 0
this.min = 0;
}
}
if (this.options.ticks.min !== undefined) {
this.min = this.options.ticks.min;
} else if (this.options.ticks.suggestedMin !== undefined) {
this.min = Math.min(this.min, this.options.ticks.suggestedMin);
}
if (this.options.ticks.max !== undefined) {
this.max = this.options.ticks.max;
} else if (this.options.ticks.suggestedMax !== undefined) {
this.max = Math.max(this.max, this.options.ticks.suggestedMax);
}
if (this.min === this.max) {
this.min--;
this.max++;
}
},
buildTicks: function() {
this.ticks = [];
// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
// To get a "nice" value for the tick spacing, we will use the appropriately named
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
// for details.
var niceRange = helpers.niceNum(this.max - this.min, false);
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
var niceMin = Math.floor(this.min / spacing) * spacing;
var niceMax = Math.ceil(this.max / spacing) * spacing;
var numSpaces = Math.ceil((niceMax - niceMin) / spacing);
// Put the values into the ticks array
this.ticks.push(this.options.ticks.min !== undefined ? this.options.ticks.min : niceMin);
for (var j = 1; j < numSpaces; ++j) {
this.ticks.push(niceMin + (j * spacing));
}
this.ticks.push(this.options.ticks.max !== undefined ? this.options.ticks.max : niceMax);
// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
this.max = helpers.max(this.ticks);
this.min = helpers.min(this.ticks);
if (this.options.ticks.reverse) {
this.ticks.reverse();
this.start = this.max;
this.end = this.min;
} else {
this.start = this.min;
this.end = this.max;
}
this.zeroLineIndex = this.ticks.indexOf(0);
},
convertTicksToLabels: function() {
Chart.Scale.prototype.convertTicksToLabels.call(this);
// Point labels
this.pointLabels = this.chart.data.labels.map(this.options.pointLabels.callback, this);
},
getLabelForIndex: function(index, datasetIndex) {
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
},
fit: function() {
/*
* Right, this is really confusing and there is a lot of maths going on here
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
*
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
*
* Solution:
*
* We assume the radius of the polygon is half the size of the canvas at first
* at each index we check if the text overlaps.
*
* Where it does, we store that angle and that index.
*
* After finding the largest index and angle we calculate how much we need to remove
* from the shape radius to move the point inwards by that x.
*
* We average the left and right distances to get the maximum shape radius that can fit in the box
* along with labels.
*
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion
* on each side, removing that from the size, halving it and adding the left x protrusion width.
*
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels
* and position it in the most space efficient manner
*
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
*/
var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, Chart.defaults.global.defaultFontSize);
var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, Chart.defaults.global.defaultFontStyle);
var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, Chart.defaults.global.defaultFontFamily);
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]),
pointPosition,
i,
textWidth,
halfTextWidth,
furthestRight = this.width,
furthestRightIndex,
furthestRightAngle,
furthestLeft = 0,
furthestLeftIndex,
furthestLeftAngle,
xProtrusionLeft,
xProtrusionRight,
radiusReductionRight,
radiusReductionLeft,
maxWidthRadius;
this.ctx.font = pointLabeFont;
for (i = 0; i < this.getValueCount(); i++) {
// 5px to space the text slightly out - similar to what we do in the draw function.
pointPosition = this.getPointPosition(i, largestPossibleRadius);
textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5;
if (i === 0 || i === this.getValueCount() / 2) {
// If we're at index zero, or exactly the middle, we're at exactly the top/bottom
// of the radar chart, so text will be aligned centrally, so we'll half it and compare
// w/left and right text sizes
halfTextWidth = textWidth / 2;
if (pointPosition.x + halfTextWidth > furthestRight) {
furthestRight = pointPosition.x + halfTextWidth;
furthestRightIndex = i;
}
if (pointPosition.x - halfTextWidth < furthestLeft) {
furthestLeft = pointPosition.x - halfTextWidth;
furthestLeftIndex = i;
}
} else if (i < this.getValueCount() / 2) {
// Less than half the values means we'll left align the text
if (pointPosition.x + textWidth > furthestRight) {
furthestRight = pointPosition.x + textWidth;
furthestRightIndex = i;
}
} else if (i > this.getValueCount() / 2) {
// More than half the values means we'll right align the text
if (pointPosition.x - textWidth < furthestLeft) {
furthestLeft = pointPosition.x - textWidth;
furthestLeftIndex = i;
}
}
}
xProtrusionLeft = furthestLeft;
xProtrusionRight = Math.ceil(furthestRight - this.width);
furthestRightAngle = this.getIndexAngle(furthestRightIndex);
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
// Ensure we actually need to reduce the size of the chart
radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
},
setCenterPoint: function(leftMovement, rightMovement) {
var maxRight = this.width - rightMovement - this.drawingArea,
maxLeft = leftMovement + this.drawingArea;
this.xCenter = Math.round(((maxLeft + maxRight) / 2) + this.left);
// Always vertically in the centre as the text height doesn't change
this.yCenter = Math.round((this.height / 2) + this.top);
},
getIndexAngle: function(index) {
var angleMultiplier = (Math.PI * 2) / this.getValueCount();
// Start from the top instead of right, so remove a quarter of the circle
return index * angleMultiplier - (Math.PI / 2);
},
getDistanceFromCenterForValue: function(value) {
if (value === null) {
return 0; // null always in center
}
// Take into account half font size + the yPadding of the top value
var scalingFactor = this.drawingArea / (this.max - this.min);
if (this.options.reverse) {
return (this.max - value) * scalingFactor;
} else {
return (value - this.min) * scalingFactor;
}
},
getPointPosition: function(index, distanceFromCenter) {
var thisAngle = this.getIndexAngle(index);
return {
x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
};
},
getPointPositionForValue: function(index, value) {
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
},
draw: function() {
if (this.options.display) {
var ctx = this.ctx;
helpers.each(this.ticks, function(label, index) {
// Don't draw a centre value (if it is minimum)
if (index > 0 || this.options.reverse) {
var yCenterOffset = this.getDistanceFromCenterForValue(this.ticks[index]);
var yHeight = this.yCenter - yCenterOffset;
// Draw circular lines around the scale
if (this.options.gridLines.display) {
ctx.strokeStyle = this.options.gridLines.color;
ctx.lineWidth = this.options.gridLines.lineWidth;
if (this.options.lineArc) {
// Draw circular arcs between the points
ctx.beginPath();
ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();
} else {
// Draw straight lines connecting each index
ctx.beginPath();
for (var i = 0; i < this.getValueCount(); i++) {
var pointPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.ticks[index]));
if (i === 0) {
ctx.moveTo(pointPosition.x, pointPosition.y);
} else {
ctx.lineTo(pointPosition.x, pointPosition.y);
}
}
ctx.closePath();
ctx.stroke();
}
}
if (this.options.ticks.display) {
var tickFontColor = helpers.getValueOrDefault(this.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
ctx.font = tickLabelFont;
if (this.options.ticks.showLabelBackdrop) {
var labelWidth = ctx.measureText(label).width;
ctx.fillStyle = this.options.ticks.backdropColor;
ctx.fillRect(
this.xCenter - labelWidth / 2 - this.options.ticks.backdropPaddingX,
yHeight - tickFontSize / 2 - this.options.ticks.backdropPaddingY,
labelWidth + this.options.ticks.backdropPaddingX * 2,
tickFontSize + this.options.ticks.backdropPaddingY * 2
);
}
ctx.textAlign = 'center';
ctx.textBaseline = "middle";
ctx.fillStyle = tickFontColor;
ctx.fillText(label, this.xCenter, yHeight);
}
}
}, this);
if (!this.options.lineArc) {
ctx.lineWidth = this.options.angleLines.lineWidth;
ctx.strokeStyle = this.options.angleLines.color;
for (var i = this.getValueCount() - 1; i >= 0; i--) {
if (this.options.angleLines.display) {
var outerPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max));
ctx.beginPath();
ctx.moveTo(this.xCenter, this.yCenter);
ctx.lineTo(outerPosition.x, outerPosition.y);
ctx.stroke();
ctx.closePath();
}
// Extra 3px out for some label spacing
var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5);
var pointLabelFontColor = helpers.getValueOrDefault(this.options.pointLabels.fontColor, Chart.defaults.global.defaultFontColor);
var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, Chart.defaults.global.defaultFontSize);
var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, Chart.defaults.global.defaultFontStyle);
var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, Chart.defaults.global.defaultFontFamily);
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
ctx.font = pointLabeFont;
ctx.fillStyle = pointLabelFontColor;
var labelsCount = this.pointLabels.length,
halfLabelsCount = this.pointLabels.length / 2,
quarterLabelsCount = halfLabelsCount / 2,
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
if (i === 0) {
ctx.textAlign = 'center';
} else if (i === halfLabelsCount) {
ctx.textAlign = 'center';
} else if (i < halfLabelsCount) {
ctx.textAlign = 'left';
} else {
ctx.textAlign = 'right';
}
// Set the correct text baseline based on outer positioning
if (exactQuarter) {
ctx.textBaseline = 'middle';
} else if (upperHalf) {
ctx.textBaseline = 'bottom';
} else {
ctx.textBaseline = 'top';
}
ctx.fillText(this.pointLabels[i] ? this.pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y);
}
}
}
}
});
Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig);
};
},{}],42:[function(require,module,exports){
/*global window: false */
"use strict";
var moment = require('moment');
moment = typeof(moment) === 'function' ? moment : window.moment;
module.exports = function(Chart) {
var helpers = Chart.helpers;
var time = {
units: [{
name: 'millisecond',
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
}, {
name: 'second',
steps: [1, 2, 5, 10, 30]
}, {
name: 'minute',
steps: [1, 2, 5, 10, 30]
}, {
name: 'hour',
steps: [1, 2, 3, 6, 12]
}, {
name: 'day',
steps: [1, 2, 5]
}, {
name: 'week',
maxStep: 4
}, {
name: 'month',
maxStep: 3
}, {
name: 'quarter',
maxStep: 4
}, {
name: 'year',
maxStep: false
}]
};
var defaultConfig = {
position: "bottom",
time: {
parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
unit: false, // false == automatic or override with week, month, year, etc.
round: false, // none, or override with week, month, year, etc.
displayFormat: false, // DEPRECATED
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
displayFormats: {
'millisecond': 'h:mm:ss.SSS a', // 11:20:01.123 AM,
'second': 'h:mm:ss a', // 11:20:01 AM
'minute': 'h:mm:ss a', // 11:20:01 AM
'hour': 'MMM D, hA', // Sept 4, 5PM
'day': 'll', // Sep 4 2015
'week': 'll', // Week 46, or maybe "[W]WW - YYYY" ?
'month': 'MMM YYYY', // Sept 2015
'quarter': '[Q]Q - YYYY', // Q3
'year': 'YYYY' // 2015
}
},
ticks: {
autoSkip: false
}
};
var TimeScale = Chart.Scale.extend({
initialize: function() {
if (!moment) {
throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
}
Chart.Scale.prototype.initialize.call(this);
},
getLabelMoment: function(datasetIndex, index) {
return this.labelMoments[datasetIndex][index];
},
determineDataLimits: function() {
this.labelMoments = [];
// Only parse these once. If the dataset does not have data as x,y pairs, we will use
// these
var scaleLabelMoments = [];
if (this.chart.data.labels && this.chart.data.labels.length > 0) {
helpers.each(this.chart.data.labels, function(label, index) {
var labelMoment = this.parseTime(label);
if (this.options.time.round) {
labelMoment.startOf(this.options.time.round);
}
scaleLabelMoments.push(labelMoment);
}, this);
this.firstTick = moment.min.call(this, scaleLabelMoments);
this.lastTick = moment.max.call(this, scaleLabelMoments);
} else {
this.firstTick = null;
this.lastTick = null;
}
helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
var momentsForDataset = [];
if (typeof dataset.data[0] === 'object') {
helpers.each(dataset.data, function(value, index) {
var labelMoment = this.parseTime(this.getRightValue(value));
if (this.options.time.round) {
labelMoment.startOf(this.options.time.round);
}
momentsForDataset.push(labelMoment);
// May have gone outside the scale ranges, make sure we keep the first and last ticks updated
this.firstTick = this.firstTick !== null ? moment.min(this.firstTick, labelMoment) : labelMoment;
this.lastTick = this.lastTick !== null ? moment.max(this.lastTick, labelMoment) : labelMoment;
}, this);
} else {
// We have no labels. Use the ones from the scale
momentsForDataset = scaleLabelMoments;
}
this.labelMoments.push(momentsForDataset);
}, this);
// Set these after we've done all the data
if (this.options.time.min) {
this.firstTick = this.parseTime(this.options.time.min);
}
if (this.options.time.max) {
this.lastTick = this.parseTime(this.options.time.max);
}
// We will modify these, so clone for later
this.firstTick = (this.firstTick || moment()).clone();
this.lastTick = (this.lastTick || moment()).clone();
},
buildTicks: function(index) {
this.ctx.save();
var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
this.ctx.font = tickLabelFont;
this.ticks = [];
this.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
this.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc)
// Set unit override if applicable
if (this.options.time.unit) {
this.tickUnit = this.options.time.unit || 'day';
this.displayFormat = this.options.time.displayFormats[this.tickUnit];
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true);
this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, 1);
} else {
// Determine the smallest needed unit of the time
var innerWidth = this.isHorizontal() ? this.width - (this.paddingLeft + this.paddingRight) : this.height - (this.paddingTop + this.paddingBottom);
// Crude approximation of what the label length might be
var tempFirstLabel = this.tickFormatFunction(this.firstTick, 0, []);
var tickLabelWidth = this.ctx.measureText(tempFirstLabel).width;
var cosRotation = Math.cos(helpers.toRadians(this.options.ticks.maxRotation));
var sinRotation = Math.sin(helpers.toRadians(this.options.ticks.maxRotation));
tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
var labelCapacity = innerWidth / (tickLabelWidth);
// Start as small as possible
this.tickUnit = 'millisecond';
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true);
this.displayFormat = this.options.time.displayFormats[this.tickUnit];
var unitDefinitionIndex = 0;
var unitDefinition = time.units[unitDefinitionIndex];
// While we aren't ideal and we don't have units left
while (unitDefinitionIndex < time.units.length) {
// Can we scale this unit. If `false` we can scale infinitely
this.unitScale = 1;
if (helpers.isArray(unitDefinition.steps) && Math.ceil(this.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) {
// Use one of the prefedined steps
for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
if (unitDefinition.steps[idx] >= Math.ceil(this.scaleSizeInUnits / labelCapacity)) {
this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, unitDefinition.steps[idx]);
break;
}
}
break;
} else if ((unitDefinition.maxStep === false) || (Math.ceil(this.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) {
// We have a max step. Scale this unit
this.unitScale = helpers.getValueOrDefault(this.options.time.unitStepSize, Math.ceil(this.scaleSizeInUnits / labelCapacity));
break;
} else {
// Move to the next unit up
++unitDefinitionIndex;
unitDefinition = time.units[unitDefinitionIndex];
this.tickUnit = unitDefinition.name;
this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true);
this.displayFormat = this.options.time.displayFormats[unitDefinition.name];
}
}
}
var roundedStart;
// Only round the first tick if we have no hard minimum
if (!this.options.time.min) {
this.firstTick.startOf(this.tickUnit);
roundedStart = this.firstTick;
} else {
roundedStart = this.firstTick.clone().startOf(this.tickUnit);
}
// Only round the last tick if we have no hard maximum
if (!this.options.time.max) {
this.lastTick.endOf(this.tickUnit);
}
this.smallestLabelSeparation = this.width;
helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) {
for (var i = 1; i < this.labelMoments[datasetIndex].length; i++) {
this.smallestLabelSeparation = Math.min(this.smallestLabelSeparation, this.labelMoments[datasetIndex][i].diff(this.labelMoments[datasetIndex][i - 1], this.tickUnit, true));
}
}, this);
// Tick displayFormat override
if (this.options.time.displayFormat) {
this.displayFormat = this.options.time.displayFormat;
}
// first tick. will have been rounded correctly if options.time.min is not specified
this.ticks.push(this.firstTick.clone());
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
for (var i = 1; i < this.scaleSizeInUnits; ++i) {
var newTick = roundedStart.clone().add(i, this.tickUnit);
// Are we greater than the max time
if (this.options.time.max && newTick.diff(this.lastTick, this.tickUnit, true) >= 0) {
break;
}
if (i % this.unitScale === 0) {
this.ticks.push(newTick);
}
}
// Always show the right tick
if (this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit) !== 0 || this.scaleSizeInUnits === 0) {
// this is a weird case. If the option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
// but the last tick was not rounded.
if (this.options.time.max) {
this.ticks.push(this.lastTick.clone());
this.scaleSizeInUnits = this.lastTick.diff(this.ticks[0], this.tickUnit, true);
} else {
this.scaleSizeInUnits = Math.ceil(this.scaleSizeInUnits / this.unitScale) * this.unitScale;
this.ticks.push(this.firstTick.clone().add(this.scaleSizeInUnits, this.tickUnit));
this.lastTick = this.ticks[this.ticks.length - 1].clone();
}
}
this.ctx.restore();
},
// Get tooltip label
getLabelForIndex: function(index, datasetIndex) {
var label = this.chart.data.labels && index < this.chart.data.labels.length ? this.chart.data.labels[index] : '';
if (typeof this.chart.data.datasets[datasetIndex].data[0] === 'object') {
label = this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
}
// Format nicely
if (this.options.time.tooltipFormat) {
label = this.parseTime(label).format(this.options.time.tooltipFormat);
}
return label;
},
// Function to format an individual tick mark
tickFormatFunction: function tickFormatFunction(tick, index, ticks) {
var formattedTick = tick.format(this.displayFormat);
if (this.options.ticks.userCallback) {
return this.options.ticks.userCallback(formattedTick, index, ticks);
} else {
return formattedTick;
}
},
convertTicksToLabels: function() {
this.ticks = this.ticks.map(this.tickFormatFunction, this);
},
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
var labelMoment = this.getLabelMoment(datasetIndex, index);
if (labelMoment) {
var offset = labelMoment.diff(this.firstTick, this.tickUnit, true);
var decimal = offset / this.scaleSizeInUnits;
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
var valueWidth = innerWidth / Math.max(this.ticks.length - 1, 1);
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
return this.left + Math.round(valueOffset);
} else {
var innerHeight = this.height - (this.paddingTop + this.paddingBottom);
var valueHeight = innerHeight / Math.max(this.ticks.length - 1, 1);
var heightOffset = (innerHeight * decimal) + this.paddingTop;
return this.top + Math.round(heightOffset);
}
}
},
parseTime: function(label) {
if (typeof this.options.time.parser === 'string') {
return moment(label, this.options.time.parser);
}
if (typeof this.options.time.parser === 'function') {
return this.options.time.parser(label);
}
// Date objects
if (typeof label.getMonth === 'function' || typeof label === 'number') {
return moment(label);
}
// Moment support
if (label.isValid && label.isValid()) {
return label;
}
// Custom parsing (return an instance of moment)
if (typeof this.options.time.format !== 'string' && this.options.time.format.call) {
console.warn("options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale");
return this.options.time.format(label);
}
// Moment format parsing
return moment(label, this.options.time.format);
}
});
Chart.scaleService.registerScaleType("time", TimeScale, defaultConfig);
};
},{"moment":1}]},{},[7]);
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2006, 2014 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD (Register as an anonymous module)
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (arguments.length > 1 && !$.isFunction(value)) {
if(typeof androidJs != 'undefined') {
var value2 = value;
if(!value2) value2 = "";
value2 = value2 + "";
if(value2 == 'undefined') value2 = '';
try {
androidJs.setCookie(key, value2);
} catch (e){}
}
if(typeof iosJs != 'undefined') {
var value2 = value;
if(!value2) value2 = "";
value2 = value2 + "";
if(value2 == 'undefined') value2 = '';
try {
iosJs.setValue(key, value2);
} catch (e){}
}
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
if(typeof androidJs != 'undefined') {
try {
var value2 = androidJs.getCookie(key);
if(value2 != '') return value2;
} catch (e){}
}
if(typeof iosJs != 'undefined') {
try {
var value2 = iosJs.getValue(key);
if(value2 != '') return value2;
} catch (e){}
}
var result = key ? undefined : {},
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
cookies = document.cookie ? document.cookie.split('; ') : [],
i = 0,
l = cookies.length;
for (; i < l; i++) {
var parts = cookies[i].split('='),
name = decode(parts.shift()),
cookie = parts.join('=');
if (key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));
/* global $:true */
/* jshint unused:false*/
+ function($) {
"use strict";
$.fn.cityPicker = function(params) {
return this.each(function() {
if(!this) return;
var format = function(data) {
var result = [];
for(var i=0;i/?'; // 定义符号字符集
var result = '';
for (var i = 0; i < str.length; i++) {
var char = str[i];
var code = char.charCodeAt(0);
if (alphabet.indexOf(char.toLowerCase()) !== -1) {
var isUpperCase = char === char.toUpperCase();
var index = alphabet.indexOf(char.toLowerCase());
var newIndex = (index - shift + 26) % 26;
var newChar = alphabet[newIndex];
result += isUpperCase ? newChar.toUpperCase() : newChar;
} else if (digits.indexOf(char) !== -1) {
var index = digits.indexOf(char);
var newIndex = (index - shift + 10) % 10;
result += digits[newIndex];
} else if (symbols.indexOf(char) !== -1) {
var index = symbols.indexOf(char);
var newIndex = (index - shift + symbols.length) % symbols.length;
result += symbols[newIndex];
} else if (code >= 0x4e00 && code <= 0x9fff) { // 常用汉字的Unicode编码范围
var newCode = ((code - 0x4e00 + shift) % 0x5200) + 0x4e00;
result += String.fromCharCode(newCode);
} else {
result += char;
}
}
return result;
}
function caesarCache(name, str = '', exports = ''){
if(!str){
// 获取
var data = localStorage.getItem(name);
if(data){
return caesar(data, -3);
}else{
return null;
}
}else{
// 储存
var data = caesar(str, 3);
localStorage.setItem(name, data);
return true;
}
}
$.caesarCache = caesarCache;
$.caesar = caesar;
}($));
// jshint ignore: start
+function($){
$.smConfig.rawCitiesData = [
{
"name":"广东",
"sub":[
{
"name":"请选择",
"sub":[
]
},
{
"name":"广州",
"sub":[
{
"name":"请选择"
},
{
"name":"越秀区"
},
{
"name":"荔湾区"
},
{
"name":"海珠区"
},
{
"name":"天河区"
},
{
"name":"白云区"
},
{
"name":"黄埔区"
},
{
"name":"番禺区"
},
{
"name":"花都区"
},
{
"name":"南沙区"
},
{
"name":"萝岗区"
},
{
"name":"增城市"
},
{
"name":"从化市"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"深圳",
"sub":[
{
"name":"请选择"
},
{
"name":"福田区"
},
{
"name":"罗湖区"
},
{
"name":"南山区"
},
{
"name":"宝安区"
},
{
"name":"龙岗区"
},
{
"name":"盐田区"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"珠海",
"sub":[
{
"name":"请选择"
},
{
"name":"香洲区"
},
{
"name":"斗门区"
},
{
"name":"金湾区"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"汕头",
"sub":[
{
"name":"请选择"
},
{
"name":"金平区"
},
{
"name":"濠江区"
},
{
"name":"龙湖区"
},
{
"name":"潮阳区"
},
{
"name":"潮南区"
},
{
"name":"澄海区"
},
{
"name":"南澳县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"韶关",
"sub":[
{
"name":"请选择"
},
{
"name":"浈江区"
},
{
"name":"武江区"
},
{
"name":"曲江区"
},
{
"name":"乐昌市"
},
{
"name":"南雄市"
},
{
"name":"始兴县"
},
{
"name":"仁化县"
},
{
"name":"翁源县"
},
{
"name":"新丰县"
},
{
"name":"乳源瑶族自治县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"佛山",
"sub":[
{
"name":"请选择"
},
{
"name":"禅城区"
},
{
"name":"南海区"
},
{
"name":"顺德区"
},
{
"name":"三水区"
},
{
"name":"高明区"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"江门",
"sub":[
{
"name":"请选择"
},
{
"name":"蓬江区"
},
{
"name":"江海区"
},
{
"name":"新会区"
},
{
"name":"恩平市"
},
{
"name":"台山市"
},
{
"name":"开平市"
},
{
"name":"鹤山市"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"湛江",
"sub":[
{
"name":"请选择"
},
{
"name":"赤坎区"
},
{
"name":"霞山区"
},
{
"name":"坡头区"
},
{
"name":"麻章区"
},
{
"name":"吴川市"
},
{
"name":"廉江市"
},
{
"name":"雷州市"
},
{
"name":"遂溪县"
},
{
"name":"徐闻县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"茂名",
"sub":[
{
"name":"请选择"
},
{
"name":"茂南区"
},
{
"name":"茂港区"
},
{
"name":"化州市"
},
{
"name":"信宜市"
},
{
"name":"高州市"
},
{
"name":"电白县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"肇庆",
"sub":[
{
"name":"请选择"
},
{
"name":"端州区"
},
{
"name":"鼎湖区"
},
{
"name":"高要市"
},
{
"name":"四会市"
},
{
"name":"广宁县"
},
{
"name":"怀集县"
},
{
"name":"封开县"
},
{
"name":"德庆县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"惠州",
"sub":[
{
"name":"请选择"
},
{
"name":"惠城区"
},
{
"name":"惠阳区"
},
{
"name":"博罗县"
},
{
"name":"惠东县"
},
{
"name":"龙门县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"梅州",
"sub":[
{
"name":"请选择"
},
{
"name":"梅江区"
},
{
"name":"兴宁市"
},
{
"name":"梅县"
},
{
"name":"大埔县"
},
{
"name":"丰顺县"
},
{
"name":"五华县"
},
{
"name":"平远县"
},
{
"name":"蕉岭县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"汕尾",
"sub":[
{
"name":"请选择"
},
{
"name":"城区"
},
{
"name":"陆丰市"
},
{
"name":"海丰县"
},
{
"name":"陆河县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"河源",
"sub":[
{
"name":"请选择"
},
{
"name":"源城区"
},
{
"name":"紫金县"
},
{
"name":"龙川县"
},
{
"name":"连平县"
},
{
"name":"和平县"
},
{
"name":"东源县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"阳江",
"sub":[
{
"name":"请选择"
},
{
"name":"江城区"
},
{
"name":"阳春市"
},
{
"name":"阳西县"
},
{
"name":"阳东县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"清远",
"sub":[
{
"name":"请选择"
},
{
"name":"清城区"
},
{
"name":"英德市"
},
{
"name":"连州市"
},
{
"name":"佛冈县"
},
{
"name":"阳山县"
},
{
"name":"清新县"
},
{
"name":"连山壮族瑶族自治县"
},
{
"name":"连南瑶族自治县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"东莞",
"sub":[
{
"name":"请选择"
},
{
"name": "东城街道"
},
{
"name": "南城街道"
},
{
"name": "万江街道"
},
{
"name": "莞城街道"
},
{
"name": "石碣镇"
},
{
"name": "石龙镇"
},
{
"name": "茶山镇"
},
{
"name": "石排镇"
},
{
"name": "企石镇"
},
{
"name": "横沥镇"
},
{
"name": "桥头镇"
},
{
"name": "谢岗镇"
},
{
"name": "东坑镇"
},
{
"name": "常平镇"
},
{
"name": "寮步镇"
},
{
"name": "樟木头镇"
},
{
"name": "大朗镇"
},
{
"name": "黄江镇"
},
{
"name": "清溪镇"
},
{
"name": "塘厦镇"
},
{
"name": "凤岗镇"
},
{
"name": "大岭山镇"
},
{
"name": "长安镇"
},
{
"name": "虎门镇"
},
{
"name": "厚街镇"
},
{
"name": "沙田镇"
},
{
"name": "道滘镇"
},
{
"name": "洪梅镇"
},
{
"name": "麻涌镇"
},
{
"name": "望牛墩镇"
},
{
"name": "中堂镇"
},
{
"name": "高埗镇"
},
{
"name": "松山湖"
},
{
"name": "东莞港"
},
{
"name": "东莞生态园"
}
],
"type":0
},
{
"name":"中山",
"sub":[
{
"name":"请选择"
},
{
"name":"南头镇"
},
{
"name":"神湾镇"
},
{
"name":"东凤镇"
},
{
"name":"五桂山街道办事处"
},
{
"name":"黄圃镇"
},
{
"name":"小榄镇"
},
{
"name":"石岐区街道办事处"
},
{
"name":"横栏镇"
},
{
"name":"三角镇"
},
{
"name":"三乡镇"
},
{
"name":"港口镇"
},
{
"name":"沙溪镇"
},
{
"name":"板芙镇"
},
{
"name":"东升镇"
},
{
"name":"阜沙镇"
},
{
"name":"民众镇"
},
{
"name":"东区街道办事处"
},
{
"name":"火炬开发区街道办事处"
},
{
"name":"西区街道办事处"
},
{
"name":"南区街道办事处"
},
{
"name":"古镇镇"
},
{
"name":"坦洲镇"
},
{
"name":"大涌镇"
},
{
"name":"南朗镇"
},
],
"type":0
},
{
"name":"潮州",
"sub":[
{
"name":"请选择"
},
{
"name":"湘桥区"
},
{
"name":"潮安县"
},
{
"name":"饶平县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"揭阳",
"sub":[
{
"name":"请选择"
},
{
"name":"榕城区"
},
{
"name":"普宁市"
},
{
"name":"揭东县"
},
{
"name":"揭西县"
},
{
"name":"惠来县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"云浮",
"sub":[
{
"name":"请选择"
},
{
"name":"云城区"
},
{
"name":"罗定市"
},
{
"name":"云安县"
},
{
"name":"新兴县"
},
{
"name":"郁南县"
},
{
"name":"其他"
}
],
"type":0
},
{
"name":"其他",
"sub":[
{
"name":"省外区域"
},
{
"name":"其他"
}
],
"type":0
}
],
"type":1
}
];
}($);
// jshint ignore: end
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame){
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
},
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame){
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
}());
function getPostValue() {
try {
if (typeof iosJs !== 'undefined') {
var value = iosJs.getPostValue();
routerPage(value);
} else if (typeof androidJs !== 'undefined') {
var value = androidJs.getPostValue();
routerPage(value);
}
} catch (e) {}
}
function routerPage(value) {
value = JSON.parse(value);
var url = window.location.href;
if (url.indexOf('login.php') !== -1) {
return 0;
}
if (parseInt(value.type) === 1) {
$.confirm(
'收到一条公告推送',
'是否前往查看?',
function () {
setTimeout(function () {
$.router.replacePage('noticeList.html');
}, 900);
},
function () {}
);
////change
} else if (parseInt(value.type) === 2) {
$.confirm(
'收到一条账户余额变更消息',
'是否前往查看?',
function () {
setTimeout(function () {
$.router.replacePage('bills.html');
}, 900);
},
function () {}
);
}
clearPostValue();
}
function clearPostValue() {
try {
if (typeof iosJs !== 'undefined') {
var value = iosJs.clearPostValue();
} else if (typeof androidJs !== 'undefined') {
var value = androidJs.clearPostValue();
}
} catch (e) {}
}
function debugAlert() {
$.alert('ios- debug');
}
// 设置
function promptSetValue(key, value) {
var ua = navigator.userAgent.toLowerCase();
if (
typeof window.webkit !== 'undefined' &&
ua.match(/MicroMessenger/i) != 'micromessenger'
) {
console.log('开始执行promptSetValue');
var set_value_data = {
type: 'JSbridge',
functionName: 'setValue',
arguments: {
key: key,
value: value,
},
};
prompt(JSON.stringify(set_value_data));
}
}
// 获取
function promptGetValue(key) {
var ua = navigator.userAgent.toLowerCase();
if (
typeof window.webkit !== 'undefined' &&
ua.match(/MicroMessenger/i) != 'micromessenger'
) {
console.log('开始执行promptGetValue');
var get_value_data = {
type: 'JSbridge',
functionName: 'getValue',
arguments: {
key: key,
},
};
var res = prompt(JSON.stringify(get_value_data));
return res;
}
}
// 打开摄像头
function QRScannerOpen() {
var ua = navigator.userAgent.toLowerCase();
var cameraAuth = getCameraAuth();
// if(cameraAuth != 2){
// $.alert('未能获取摄像头权限');
// return false;
// }
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
console.log('开始执行QRScannerOpen');
var qr_data = {
type: 'JSbridge',
functionName: 'QRScannerOpen',
arguments: {title:"授权提示", message:"该服务需要申请相册权限:用于扫码时使用。"},
};
prompt(JSON.stringify(qr_data));
promptSetValue('qrcode', '');
// setValue("qrcode", "");
getQrCode(10, '');
}
}
function getCameraAuth() {
var arguments = {};
var type = "JSbridge";
var functionName = "getCameraAuth";
var data = {"type": type, "functionName": functionName, "arguments": arguments};
return prompt(JSON.stringify(data));
}
function getPhotoAuth() {
var arguments = {};
var type = "JSbridge";
var functionName = "getPhotoAuth";
var data = {"type": type, "functionName": functionName, "arguments": arguments};
return prompt(JSON.stringify(data));
}
// 扫码
function getQrCode(seconds, qrcode) {
console.log('运行中:' + seconds);
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
if (seconds-- > 0) {
qrcode = promptGetValue('qrcode');
if (qrcode != '') {
console.log('获取二维码成功:' + qrcode);
// alert(qrcode);
// window.webkit.messageHandlers.testH5.postMessage(
// '获取二维码成功:' + qrcode
// );
pay_page(qrcode);
seconds = 0;
} else {
// setTimeout("getQrCode("+seconds+",'" + qrcode + "')", 5000);
setTimeout(function () {
getQrCode(seconds, qrcode);
}, 1000);
}
} else {
console.log('运行结束, 获取失败');
}
}
}
// bindAccount
function promptBindAccount(account) {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var prompt_data = {
type: 'JSbridge',
functionName: 'bindAccount',
arguments: { account: account },
};
prompt(JSON.stringify(prompt_data));
}
}
// openInSafari 浏览器打开
function promptOpenInSafari(url) {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var prompt_data = {
type: 'JSbridge',
functionName: 'openInSafari',
arguments: { url: url },
};
prompt(JSON.stringify(prompt_data));
}
}
// openNewWeb 打开新浏览器
function promptOpenNewWeb(url, title, showBack) {
showBack = showBack || false;
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var prompt_data = {
type: 'JSbridge',
functionName: 'openNewWeb',
arguments: {
title: title,
url: url,
showBack: showBack,
},
};
prompt(JSON.stringify(prompt_data));
}
}
// screenBrightnessUp 调亮屏幕
function screenBrightnessUp() {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var prompt_data = {
type: 'JSbridge',
functionName: 'screenBrightnessUp',
arguments: {},
};
prompt(JSON.stringify(prompt_data));
}
}
// screenBrightnessBack 恢复亮度
function screenBrightnessBack() {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var prompt_data = {
type: 'JSbridge',
functionName: 'screenBrightnessBack',
arguments: {},
};
prompt(JSON.stringify(prompt_data));
}
}
function allowsBackFrowardGesture(status) {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) != 'micromessenger') {
var type = 'JSbridge';
var functionName = 'allowsBackFrowardGesture';
var arguments = { status: status };
var data = { type: type, functionName: functionName, arguments: arguments };
prompt(JSON.stringify(data));
}
}
// 安卓扫码后获取的内容
function getScanValue(val){
pay_page(val)
}
function isUrl(str) {
var v = new RegExp('^(?!mailto:)(?:(?:http|https)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', 'i');
return v.test(str);
}
// 支付页面
function pay_page(val){
// $.alert('请使用App操作');
// var user_str = $.cookie('user');
var user_str = $.caesarCache('user');
var user_json = JSON.parse(user_str)
var token = user_json.token
$.closePanel();
// 判断val 是否为url,则跳转url
// if(isUrl(val)){
if(false){
// 验证域名
var lead_slashes = val.indexOf("://");
var domain_start = lead_slashes + 3;
var without_resource = val.substring(domain_start, val.length);
var next_slash = without_resource.indexOf("/");
if(next_slash == -1){
next_slash = without_resource.indexOf("?");
}
var domain = without_resource.substring(0, next_slash);
if(domain != 'yzms.fsecity.com'){
return false;
}
location.href = val;
return false;
}
$.showIndicator();
$.ajax({
url: "/api/user/get_ft_code_info?token="+token,
type: 'post',
data:{
params: val
},
dataType:'json',
success: function (j){
if(j.status != 1){
$.alert(j.message);
}
$.hideIndicator();
var ft_name = j.data.ftInfo.name
var ft_id = j.data.ftInfo.id
var cid = j.data.channelInfo.id
var cname = j.data.channelInfo.name
var ftqr_id = j.data.ftqr_id
var popupHTML =
'';
$.popup(popupHTML);
},
error: function() {
$.alert("获取信息失败");
}
});
}
// pay_page('/hnak2KHHi6VIbILI1ViQeWP8WjnL+CUOCpJyS8dqd3eq6yrlTGWhSmC35zU/9gb')
function scan_pay_btn(_this){
// var user_str = $.cookie('user');
var user_str = $.caesarCache('user');
var user_json = JSON.parse(user_str)
var token = user_json.token
var scan_price = $.trim($(".scan_price").val());
if(scan_price == ''){
alert('金额不能为空')
// $.alert("金额不能为空");
return false;
}else if(scan_price <= 0){
alert('金额不能小于0')
// $.alert("金额不能小于0");
return false;
}
var ftid = $(_this).attr('ftid');
var channelid = $(_this).attr('cid')
var ftqr_id = $(_this).attr('ftqr_id')
$.showIndicator();
$.ajax({
url: "/api/user/scan_pay?token="+token,
type: 'post',
data: {
ftid: ftid,
channelid: channelid,
fee: scan_price,
ftqr_id: ftqr_id
},
dataType:'json',
success:function (j){
if(j.status == 1){
$.alert(j.message);
}else{
alert(j.message);
}
},
error:function (){
alert('网络异常')
},
complete: function (){
$.hideIndicator();
}
})
}