163447 lines
6.8 MiB
163447 lines
6.8 MiB
/**
|
|
* Kendo UI v2016.1.420 (http://www.telerik.com/kendo-ui)
|
|
* Copyright 2016 Telerik AD. All rights reserved.
|
|
*
|
|
* Kendo UI commercial licenses may be obtained at
|
|
* http://www.telerik.com/purchase/license-agreement/kendo-ui-complete
|
|
* If you do not own a commercial license, this file shall be governed by the trial license terms.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
(function (f, define) {
|
|
define('kendo.core', ['jquery'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'core',
|
|
name: 'Core',
|
|
category: 'framework',
|
|
description: 'The core of the Kendo framework.'
|
|
};
|
|
(function ($, window, undefined) {
|
|
var kendo = window.kendo = window.kendo || { cultures: {} }, extend = $.extend, each = $.each, isArray = $.isArray, proxy = $.proxy, noop = $.noop, math = Math, Template, JSON = window.JSON || {}, support = {}, percentRegExp = /%/, formatRegExp = /\{(\d+)(:[^\}]+)?\}/g, boxShadowRegExp = /(\d+(?:\.?)\d*)px\s*(\d+(?:\.?)\d*)px\s*(\d+(?:\.?)\d*)px\s*(\d+)?/i, numberRegExp = /^(\+|-?)\d+(\.?)\d*$/, FUNCTION = 'function', STRING = 'string', NUMBER = 'number', OBJECT = 'object', NULL = 'null', BOOLEAN = 'boolean', UNDEFINED = 'undefined', getterCache = {}, setterCache = {}, slice = [].slice;
|
|
kendo.version = '2016.1.420'.replace(/^\s+|\s+$/g, '');
|
|
function Class() {
|
|
}
|
|
Class.extend = function (proto) {
|
|
var base = function () {
|
|
}, member, that = this, subclass = proto && proto.init ? proto.init : function () {
|
|
that.apply(this, arguments);
|
|
}, fn;
|
|
base.prototype = that.prototype;
|
|
fn = subclass.fn = subclass.prototype = new base();
|
|
for (member in proto) {
|
|
if (proto[member] != null && proto[member].constructor === Object) {
|
|
fn[member] = extend(true, {}, base.prototype[member], proto[member]);
|
|
} else {
|
|
fn[member] = proto[member];
|
|
}
|
|
}
|
|
fn.constructor = subclass;
|
|
subclass.extend = that.extend;
|
|
return subclass;
|
|
};
|
|
Class.prototype._initOptions = function (options) {
|
|
this.options = deepExtend({}, this.options, options);
|
|
};
|
|
var isFunction = kendo.isFunction = function (fn) {
|
|
return typeof fn === 'function';
|
|
};
|
|
var preventDefault = function () {
|
|
this._defaultPrevented = true;
|
|
};
|
|
var isDefaultPrevented = function () {
|
|
return this._defaultPrevented === true;
|
|
};
|
|
var Observable = Class.extend({
|
|
init: function () {
|
|
this._events = {};
|
|
},
|
|
bind: function (eventName, handlers, one) {
|
|
var that = this, idx, eventNames = typeof eventName === STRING ? [eventName] : eventName, length, original, handler, handlersIsFunction = typeof handlers === FUNCTION, events;
|
|
if (handlers === undefined) {
|
|
for (idx in eventName) {
|
|
that.bind(idx, eventName[idx]);
|
|
}
|
|
return that;
|
|
}
|
|
for (idx = 0, length = eventNames.length; idx < length; idx++) {
|
|
eventName = eventNames[idx];
|
|
handler = handlersIsFunction ? handlers : handlers[eventName];
|
|
if (handler) {
|
|
if (one) {
|
|
original = handler;
|
|
handler = function () {
|
|
that.unbind(eventName, handler);
|
|
original.apply(that, arguments);
|
|
};
|
|
handler.original = original;
|
|
}
|
|
events = that._events[eventName] = that._events[eventName] || [];
|
|
events.push(handler);
|
|
}
|
|
}
|
|
return that;
|
|
},
|
|
one: function (eventNames, handlers) {
|
|
return this.bind(eventNames, handlers, true);
|
|
},
|
|
first: function (eventName, handlers) {
|
|
var that = this, idx, eventNames = typeof eventName === STRING ? [eventName] : eventName, length, handler, handlersIsFunction = typeof handlers === FUNCTION, events;
|
|
for (idx = 0, length = eventNames.length; idx < length; idx++) {
|
|
eventName = eventNames[idx];
|
|
handler = handlersIsFunction ? handlers : handlers[eventName];
|
|
if (handler) {
|
|
events = that._events[eventName] = that._events[eventName] || [];
|
|
events.unshift(handler);
|
|
}
|
|
}
|
|
return that;
|
|
},
|
|
trigger: function (eventName, e) {
|
|
var that = this, events = that._events[eventName], idx, length;
|
|
if (events) {
|
|
e = e || {};
|
|
e.sender = that;
|
|
e._defaultPrevented = false;
|
|
e.preventDefault = preventDefault;
|
|
e.isDefaultPrevented = isDefaultPrevented;
|
|
events = events.slice();
|
|
for (idx = 0, length = events.length; idx < length; idx++) {
|
|
events[idx].call(that, e);
|
|
}
|
|
return e._defaultPrevented === true;
|
|
}
|
|
return false;
|
|
},
|
|
unbind: function (eventName, handler) {
|
|
var that = this, events = that._events[eventName], idx;
|
|
if (eventName === undefined) {
|
|
that._events = {};
|
|
} else if (events) {
|
|
if (handler) {
|
|
for (idx = events.length - 1; idx >= 0; idx--) {
|
|
if (events[idx] === handler || events[idx].original === handler) {
|
|
events.splice(idx, 1);
|
|
}
|
|
}
|
|
} else {
|
|
that._events[eventName] = [];
|
|
}
|
|
}
|
|
return that;
|
|
}
|
|
});
|
|
function compilePart(part, stringPart) {
|
|
if (stringPart) {
|
|
return '\'' + part.split('\'').join('\\\'').split('\\"').join('\\\\\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t') + '\'';
|
|
} else {
|
|
var first = part.charAt(0), rest = part.substring(1);
|
|
if (first === '=') {
|
|
return '+(' + rest + ')+';
|
|
} else if (first === ':') {
|
|
return '+$kendoHtmlEncode(' + rest + ')+';
|
|
} else {
|
|
return ';' + part + ';$kendoOutput+=';
|
|
}
|
|
}
|
|
}
|
|
var argumentNameRegExp = /^\w+/, encodeRegExp = /\$\{([^}]*)\}/g, escapedCurlyRegExp = /\\\}/g, curlyRegExp = /__CURLY__/g, escapedSharpRegExp = /\\#/g, sharpRegExp = /__SHARP__/g, zeros = [
|
|
'',
|
|
'0',
|
|
'00',
|
|
'000',
|
|
'0000'
|
|
];
|
|
Template = {
|
|
paramName: 'data',
|
|
useWithBlock: true,
|
|
render: function (template, data) {
|
|
var idx, length, html = '';
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
html += template(data[idx]);
|
|
}
|
|
return html;
|
|
},
|
|
compile: function (template, options) {
|
|
var settings = extend({}, this, options), paramName = settings.paramName, argumentName = paramName.match(argumentNameRegExp)[0], useWithBlock = settings.useWithBlock, functionBody = 'var $kendoOutput, $kendoHtmlEncode = kendo.htmlEncode;', fn, parts, idx;
|
|
if (isFunction(template)) {
|
|
return template;
|
|
}
|
|
functionBody += useWithBlock ? 'with(' + paramName + '){' : '';
|
|
functionBody += '$kendoOutput=';
|
|
parts = template.replace(escapedCurlyRegExp, '__CURLY__').replace(encodeRegExp, '#=$kendoHtmlEncode($1)#').replace(curlyRegExp, '}').replace(escapedSharpRegExp, '__SHARP__').split('#');
|
|
for (idx = 0; idx < parts.length; idx++) {
|
|
functionBody += compilePart(parts[idx], idx % 2 === 0);
|
|
}
|
|
functionBody += useWithBlock ? ';}' : ';';
|
|
functionBody += 'return $kendoOutput;';
|
|
functionBody = functionBody.replace(sharpRegExp, '#');
|
|
try {
|
|
fn = new Function(argumentName, functionBody);
|
|
fn._slotCount = Math.floor(parts.length / 2);
|
|
return fn;
|
|
} catch (e) {
|
|
throw new Error(kendo.format('Invalid template:\'{0}\' Generated code:\'{1}\'', template, functionBody));
|
|
}
|
|
}
|
|
};
|
|
function pad(number, digits, end) {
|
|
number = number + '';
|
|
digits = digits || 2;
|
|
end = digits - number.length;
|
|
if (end) {
|
|
return zeros[digits].substring(0, end) + number;
|
|
}
|
|
return number;
|
|
}
|
|
(function () {
|
|
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = {
|
|
'\b': '\\b',
|
|
'\t': '\\t',
|
|
'\n': '\\n',
|
|
'\f': '\\f',
|
|
'\r': '\\r',
|
|
'"': '\\"',
|
|
'\\': '\\\\'
|
|
}, rep, toString = {}.toString;
|
|
if (typeof Date.prototype.toJSON !== FUNCTION) {
|
|
Date.prototype.toJSON = function () {
|
|
var that = this;
|
|
return isFinite(that.valueOf()) ? pad(that.getUTCFullYear(), 4) + '-' + pad(that.getUTCMonth() + 1) + '-' + pad(that.getUTCDate()) + 'T' + pad(that.getUTCHours()) + ':' + pad(that.getUTCMinutes()) + ':' + pad(that.getUTCSeconds()) + 'Z' : null;
|
|
};
|
|
String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function () {
|
|
return this.valueOf();
|
|
};
|
|
}
|
|
function quote(string) {
|
|
escapable.lastIndex = 0;
|
|
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
|
var c = meta[a];
|
|
return typeof c === STRING ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
}) + '"' : '"' + string + '"';
|
|
}
|
|
function str(key, holder) {
|
|
var i, k, v, length, mind = gap, partial, value = holder[key], type;
|
|
if (value && typeof value === OBJECT && typeof value.toJSON === FUNCTION) {
|
|
value = value.toJSON(key);
|
|
}
|
|
if (typeof rep === FUNCTION) {
|
|
value = rep.call(holder, key, value);
|
|
}
|
|
type = typeof value;
|
|
if (type === STRING) {
|
|
return quote(value);
|
|
} else if (type === NUMBER) {
|
|
return isFinite(value) ? String(value) : NULL;
|
|
} else if (type === BOOLEAN || type === NULL) {
|
|
return String(value);
|
|
} else if (type === OBJECT) {
|
|
if (!value) {
|
|
return NULL;
|
|
}
|
|
gap += indent;
|
|
partial = [];
|
|
if (toString.apply(value) === '[object Array]') {
|
|
length = value.length;
|
|
for (i = 0; i < length; i++) {
|
|
partial[i] = str(i, value) || NULL;
|
|
}
|
|
v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
|
|
gap = mind;
|
|
return v;
|
|
}
|
|
if (rep && typeof rep === OBJECT) {
|
|
length = rep.length;
|
|
for (i = 0; i < length; i++) {
|
|
if (typeof rep[i] === STRING) {
|
|
k = rep[i];
|
|
v = str(k, value);
|
|
if (v) {
|
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (k in value) {
|
|
if (Object.hasOwnProperty.call(value, k)) {
|
|
v = str(k, value);
|
|
if (v) {
|
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
|
|
gap = mind;
|
|
return v;
|
|
}
|
|
}
|
|
if (typeof JSON.stringify !== FUNCTION) {
|
|
JSON.stringify = function (value, replacer, space) {
|
|
var i;
|
|
gap = '';
|
|
indent = '';
|
|
if (typeof space === NUMBER) {
|
|
for (i = 0; i < space; i += 1) {
|
|
indent += ' ';
|
|
}
|
|
} else if (typeof space === STRING) {
|
|
indent = space;
|
|
}
|
|
rep = replacer;
|
|
if (replacer && typeof replacer !== FUNCTION && (typeof replacer !== OBJECT || typeof replacer.length !== NUMBER)) {
|
|
throw new Error('JSON.stringify');
|
|
}
|
|
return str('', { '': value });
|
|
};
|
|
}
|
|
}());
|
|
(function () {
|
|
var dateFormatRegExp = /dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|HH|H|hh|h|mm|m|fff|ff|f|tt|ss|s|zzz|zz|z|"[^"]*"|'[^']*'/g, standardFormatRegExp = /^(n|c|p|e)(\d*)$/i, literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g, commaRegExp = /\,/g, EMPTY = '', POINT = '.', COMMA = ',', SHARP = '#', ZERO = '0', PLACEHOLDER = '??', EN = 'en-US', objectToString = {}.toString;
|
|
kendo.cultures['en-US'] = {
|
|
name: EN,
|
|
numberFormat: {
|
|
pattern: ['-n'],
|
|
decimals: 2,
|
|
',': ',',
|
|
'.': '.',
|
|
groupSize: [3],
|
|
percent: {
|
|
pattern: [
|
|
'-n %',
|
|
'n %'
|
|
],
|
|
decimals: 2,
|
|
',': ',',
|
|
'.': '.',
|
|
groupSize: [3],
|
|
symbol: '%'
|
|
},
|
|
currency: {
|
|
name: 'US Dollar',
|
|
abbr: 'USD',
|
|
pattern: [
|
|
'($n)',
|
|
'$n'
|
|
],
|
|
decimals: 2,
|
|
',': ',',
|
|
'.': '.',
|
|
groupSize: [3],
|
|
symbol: '$'
|
|
}
|
|
},
|
|
calendars: {
|
|
standard: {
|
|
days: {
|
|
names: [
|
|
'Sunday',
|
|
'Monday',
|
|
'Tuesday',
|
|
'Wednesday',
|
|
'Thursday',
|
|
'Friday',
|
|
'Saturday'
|
|
],
|
|
namesAbbr: [
|
|
'Sun',
|
|
'Mon',
|
|
'Tue',
|
|
'Wed',
|
|
'Thu',
|
|
'Fri',
|
|
'Sat'
|
|
],
|
|
namesShort: [
|
|
'Su',
|
|
'Mo',
|
|
'Tu',
|
|
'We',
|
|
'Th',
|
|
'Fr',
|
|
'Sa'
|
|
]
|
|
},
|
|
months: {
|
|
names: [
|
|
'January',
|
|
'February',
|
|
'March',
|
|
'April',
|
|
'May',
|
|
'June',
|
|
'July',
|
|
'August',
|
|
'September',
|
|
'October',
|
|
'November',
|
|
'December'
|
|
],
|
|
namesAbbr: [
|
|
'Jan',
|
|
'Feb',
|
|
'Mar',
|
|
'Apr',
|
|
'May',
|
|
'Jun',
|
|
'Jul',
|
|
'Aug',
|
|
'Sep',
|
|
'Oct',
|
|
'Nov',
|
|
'Dec'
|
|
]
|
|
},
|
|
AM: [
|
|
'AM',
|
|
'am',
|
|
'AM'
|
|
],
|
|
PM: [
|
|
'PM',
|
|
'pm',
|
|
'PM'
|
|
],
|
|
patterns: {
|
|
d: 'M/d/yyyy',
|
|
D: 'dddd, MMMM dd, yyyy',
|
|
F: 'dddd, MMMM dd, yyyy h:mm:ss tt',
|
|
g: 'M/d/yyyy h:mm tt',
|
|
G: 'M/d/yyyy h:mm:ss tt',
|
|
m: 'MMMM dd',
|
|
M: 'MMMM dd',
|
|
s: 'yyyy\'-\'MM\'-\'ddTHH\':\'mm\':\'ss',
|
|
t: 'h:mm tt',
|
|
T: 'h:mm:ss tt',
|
|
u: 'yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'',
|
|
y: 'MMMM, yyyy',
|
|
Y: 'MMMM, yyyy'
|
|
},
|
|
'/': '/',
|
|
':': ':',
|
|
firstDay: 0,
|
|
twoDigitYearMax: 2029
|
|
}
|
|
}
|
|
};
|
|
function findCulture(culture) {
|
|
if (culture) {
|
|
if (culture.numberFormat) {
|
|
return culture;
|
|
}
|
|
if (typeof culture === STRING) {
|
|
var cultures = kendo.cultures;
|
|
return cultures[culture] || cultures[culture.split('-')[0]] || null;
|
|
}
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
function getCulture(culture) {
|
|
if (culture) {
|
|
culture = findCulture(culture);
|
|
}
|
|
return culture || kendo.cultures.current;
|
|
}
|
|
kendo.culture = function (cultureName) {
|
|
var cultures = kendo.cultures, culture;
|
|
if (cultureName !== undefined) {
|
|
culture = findCulture(cultureName) || cultures[EN];
|
|
culture.calendar = culture.calendars.standard;
|
|
cultures.current = culture;
|
|
} else {
|
|
return cultures.current;
|
|
}
|
|
};
|
|
kendo.findCulture = findCulture;
|
|
kendo.getCulture = getCulture;
|
|
kendo.culture(EN);
|
|
function formatDate(date, format, culture) {
|
|
culture = getCulture(culture);
|
|
var calendar = culture.calendars.standard, days = calendar.days, months = calendar.months;
|
|
format = calendar.patterns[format] || format;
|
|
return format.replace(dateFormatRegExp, function (match) {
|
|
var minutes;
|
|
var result;
|
|
var sign;
|
|
if (match === 'd') {
|
|
result = date.getDate();
|
|
} else if (match === 'dd') {
|
|
result = pad(date.getDate());
|
|
} else if (match === 'ddd') {
|
|
result = days.namesAbbr[date.getDay()];
|
|
} else if (match === 'dddd') {
|
|
result = days.names[date.getDay()];
|
|
} else if (match === 'M') {
|
|
result = date.getMonth() + 1;
|
|
} else if (match === 'MM') {
|
|
result = pad(date.getMonth() + 1);
|
|
} else if (match === 'MMM') {
|
|
result = months.namesAbbr[date.getMonth()];
|
|
} else if (match === 'MMMM') {
|
|
result = months.names[date.getMonth()];
|
|
} else if (match === 'yy') {
|
|
result = pad(date.getFullYear() % 100);
|
|
} else if (match === 'yyyy') {
|
|
result = pad(date.getFullYear(), 4);
|
|
} else if (match === 'h') {
|
|
result = date.getHours() % 12 || 12;
|
|
} else if (match === 'hh') {
|
|
result = pad(date.getHours() % 12 || 12);
|
|
} else if (match === 'H') {
|
|
result = date.getHours();
|
|
} else if (match === 'HH') {
|
|
result = pad(date.getHours());
|
|
} else if (match === 'm') {
|
|
result = date.getMinutes();
|
|
} else if (match === 'mm') {
|
|
result = pad(date.getMinutes());
|
|
} else if (match === 's') {
|
|
result = date.getSeconds();
|
|
} else if (match === 'ss') {
|
|
result = pad(date.getSeconds());
|
|
} else if (match === 'f') {
|
|
result = math.floor(date.getMilliseconds() / 100);
|
|
} else if (match === 'ff') {
|
|
result = date.getMilliseconds();
|
|
if (result > 99) {
|
|
result = math.floor(result / 10);
|
|
}
|
|
result = pad(result);
|
|
} else if (match === 'fff') {
|
|
result = pad(date.getMilliseconds(), 3);
|
|
} else if (match === 'tt') {
|
|
result = date.getHours() < 12 ? calendar.AM[0] : calendar.PM[0];
|
|
} else if (match === 'zzz') {
|
|
minutes = date.getTimezoneOffset();
|
|
sign = minutes < 0;
|
|
result = math.abs(minutes / 60).toString().split('.')[0];
|
|
minutes = math.abs(minutes) - result * 60;
|
|
result = (sign ? '+' : '-') + pad(result);
|
|
result += ':' + pad(minutes);
|
|
} else if (match === 'zz' || match === 'z') {
|
|
result = date.getTimezoneOffset() / 60;
|
|
sign = result < 0;
|
|
result = math.abs(result).toString().split('.')[0];
|
|
result = (sign ? '+' : '-') + (match === 'zz' ? pad(result) : result);
|
|
}
|
|
return result !== undefined ? result : match.slice(1, match.length - 1);
|
|
});
|
|
}
|
|
function formatNumber(number, format, culture) {
|
|
culture = getCulture(culture);
|
|
var numberFormat = culture.numberFormat, decimal = numberFormat[POINT], precision = numberFormat.decimals, pattern = numberFormat.pattern[0], literals = [], symbol, isCurrency, isPercent, customPrecision, formatAndPrecision, negative = number < 0, integer, fraction, integerLength, fractionLength, replacement = EMPTY, value = EMPTY, idx, length, ch, hasGroup, hasNegativeFormat, decimalIndex, sharpIndex, zeroIndex, hasZero, hasSharp, percentIndex, currencyIndex, startZeroIndex, start = -1, end;
|
|
if (number === undefined) {
|
|
return EMPTY;
|
|
}
|
|
if (!isFinite(number)) {
|
|
return number;
|
|
}
|
|
if (!format) {
|
|
return culture.name.length ? number.toLocaleString() : number.toString();
|
|
}
|
|
formatAndPrecision = standardFormatRegExp.exec(format);
|
|
if (formatAndPrecision) {
|
|
format = formatAndPrecision[1].toLowerCase();
|
|
isCurrency = format === 'c';
|
|
isPercent = format === 'p';
|
|
if (isCurrency || isPercent) {
|
|
numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
|
|
decimal = numberFormat[POINT];
|
|
precision = numberFormat.decimals;
|
|
symbol = numberFormat.symbol;
|
|
pattern = numberFormat.pattern[negative ? 0 : 1];
|
|
}
|
|
customPrecision = formatAndPrecision[2];
|
|
if (customPrecision) {
|
|
precision = +customPrecision;
|
|
}
|
|
if (format === 'e') {
|
|
return customPrecision ? number.toExponential(precision) : number.toExponential();
|
|
}
|
|
if (isPercent) {
|
|
number *= 100;
|
|
}
|
|
number = round(number, precision);
|
|
negative = number < 0;
|
|
number = number.split(POINT);
|
|
integer = number[0];
|
|
fraction = number[1];
|
|
if (negative) {
|
|
integer = integer.substring(1);
|
|
}
|
|
value = groupInteger(integer, 0, integer.length, numberFormat);
|
|
if (fraction) {
|
|
value += decimal + fraction;
|
|
}
|
|
if (format === 'n' && !negative) {
|
|
return value;
|
|
}
|
|
number = EMPTY;
|
|
for (idx = 0, length = pattern.length; idx < length; idx++) {
|
|
ch = pattern.charAt(idx);
|
|
if (ch === 'n') {
|
|
number += value;
|
|
} else if (ch === '$' || ch === '%') {
|
|
number += symbol;
|
|
} else {
|
|
number += ch;
|
|
}
|
|
}
|
|
return number;
|
|
}
|
|
if (negative) {
|
|
number = -number;
|
|
}
|
|
if (format.indexOf('\'') > -1 || format.indexOf('"') > -1 || format.indexOf('\\') > -1) {
|
|
format = format.replace(literalRegExp, function (match) {
|
|
var quoteChar = match.charAt(0).replace('\\', ''), literal = match.slice(1).replace(quoteChar, '');
|
|
literals.push(literal);
|
|
return PLACEHOLDER;
|
|
});
|
|
}
|
|
format = format.split(';');
|
|
if (negative && format[1]) {
|
|
format = format[1];
|
|
hasNegativeFormat = true;
|
|
} else if (number === 0) {
|
|
format = format[2] || format[0];
|
|
if (format.indexOf(SHARP) == -1 && format.indexOf(ZERO) == -1) {
|
|
return format;
|
|
}
|
|
} else {
|
|
format = format[0];
|
|
}
|
|
percentIndex = format.indexOf('%');
|
|
currencyIndex = format.indexOf('$');
|
|
isPercent = percentIndex != -1;
|
|
isCurrency = currencyIndex != -1;
|
|
if (isPercent) {
|
|
number *= 100;
|
|
}
|
|
if (isCurrency && format[currencyIndex - 1] === '\\') {
|
|
format = format.split('\\').join('');
|
|
isCurrency = false;
|
|
}
|
|
if (isCurrency || isPercent) {
|
|
numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
|
|
decimal = numberFormat[POINT];
|
|
precision = numberFormat.decimals;
|
|
symbol = numberFormat.symbol;
|
|
}
|
|
hasGroup = format.indexOf(COMMA) > -1;
|
|
if (hasGroup) {
|
|
format = format.replace(commaRegExp, EMPTY);
|
|
}
|
|
decimalIndex = format.indexOf(POINT);
|
|
length = format.length;
|
|
if (decimalIndex != -1) {
|
|
fraction = number.toString().split('e');
|
|
if (fraction[1]) {
|
|
fraction = round(number, Math.abs(fraction[1]));
|
|
} else {
|
|
fraction = fraction[0];
|
|
}
|
|
fraction = fraction.split(POINT)[1] || EMPTY;
|
|
zeroIndex = format.lastIndexOf(ZERO) - decimalIndex;
|
|
sharpIndex = format.lastIndexOf(SHARP) - decimalIndex;
|
|
hasZero = zeroIndex > -1;
|
|
hasSharp = sharpIndex > -1;
|
|
idx = fraction.length;
|
|
if (!hasZero && !hasSharp) {
|
|
format = format.substring(0, decimalIndex) + format.substring(decimalIndex + 1);
|
|
length = format.length;
|
|
decimalIndex = -1;
|
|
idx = 0;
|
|
}
|
|
if (hasZero && zeroIndex > sharpIndex) {
|
|
idx = zeroIndex;
|
|
} else if (sharpIndex > zeroIndex) {
|
|
if (hasSharp && idx > sharpIndex) {
|
|
idx = sharpIndex;
|
|
} else if (hasZero && idx < zeroIndex) {
|
|
idx = zeroIndex;
|
|
}
|
|
}
|
|
if (idx > -1) {
|
|
number = round(number, idx);
|
|
}
|
|
} else {
|
|
number = round(number);
|
|
}
|
|
sharpIndex = format.indexOf(SHARP);
|
|
startZeroIndex = zeroIndex = format.indexOf(ZERO);
|
|
if (sharpIndex == -1 && zeroIndex != -1) {
|
|
start = zeroIndex;
|
|
} else if (sharpIndex != -1 && zeroIndex == -1) {
|
|
start = sharpIndex;
|
|
} else {
|
|
start = sharpIndex > zeroIndex ? zeroIndex : sharpIndex;
|
|
}
|
|
sharpIndex = format.lastIndexOf(SHARP);
|
|
zeroIndex = format.lastIndexOf(ZERO);
|
|
if (sharpIndex == -1 && zeroIndex != -1) {
|
|
end = zeroIndex;
|
|
} else if (sharpIndex != -1 && zeroIndex == -1) {
|
|
end = sharpIndex;
|
|
} else {
|
|
end = sharpIndex > zeroIndex ? sharpIndex : zeroIndex;
|
|
}
|
|
if (start == length) {
|
|
end = start;
|
|
}
|
|
if (start != -1) {
|
|
value = number.toString().split(POINT);
|
|
integer = value[0];
|
|
fraction = value[1] || EMPTY;
|
|
integerLength = integer.length;
|
|
fractionLength = fraction.length;
|
|
if (negative && number * -1 >= 0) {
|
|
negative = false;
|
|
}
|
|
number = format.substring(0, start);
|
|
if (negative && !hasNegativeFormat) {
|
|
number += '-';
|
|
}
|
|
for (idx = start; idx < length; idx++) {
|
|
ch = format.charAt(idx);
|
|
if (decimalIndex == -1) {
|
|
if (end - idx < integerLength) {
|
|
number += integer;
|
|
break;
|
|
}
|
|
} else {
|
|
if (zeroIndex != -1 && zeroIndex < idx) {
|
|
replacement = EMPTY;
|
|
}
|
|
if (decimalIndex - idx <= integerLength && decimalIndex - idx > -1) {
|
|
number += integer;
|
|
idx = decimalIndex;
|
|
}
|
|
if (decimalIndex === idx) {
|
|
number += (fraction ? decimal : EMPTY) + fraction;
|
|
idx += end - decimalIndex + 1;
|
|
continue;
|
|
}
|
|
}
|
|
if (ch === ZERO) {
|
|
number += ch;
|
|
replacement = ch;
|
|
} else if (ch === SHARP) {
|
|
number += replacement;
|
|
}
|
|
}
|
|
if (hasGroup) {
|
|
number = groupInteger(number, start, end, numberFormat);
|
|
}
|
|
if (end >= start) {
|
|
number += format.substring(end + 1);
|
|
}
|
|
if (isCurrency || isPercent) {
|
|
value = EMPTY;
|
|
for (idx = 0, length = number.length; idx < length; idx++) {
|
|
ch = number.charAt(idx);
|
|
value += ch === '$' || ch === '%' ? symbol : ch;
|
|
}
|
|
number = value;
|
|
}
|
|
length = literals.length;
|
|
if (length) {
|
|
for (idx = 0; idx < length; idx++) {
|
|
number = number.replace(PLACEHOLDER, literals[idx]);
|
|
}
|
|
}
|
|
}
|
|
return number;
|
|
}
|
|
var groupInteger = function (number, start, end, numberFormat) {
|
|
var decimalIndex = number.indexOf(numberFormat[POINT]);
|
|
var groupSizes = numberFormat.groupSize.slice();
|
|
var groupSize = groupSizes.shift();
|
|
var integer, integerLength;
|
|
var idx, parts, value;
|
|
var newGroupSize;
|
|
end = decimalIndex !== -1 ? decimalIndex : end + 1;
|
|
integer = number.substring(start, end);
|
|
integerLength = integer.length;
|
|
if (integerLength >= groupSize) {
|
|
idx = integerLength;
|
|
parts = [];
|
|
while (idx > -1) {
|
|
value = integer.substring(idx - groupSize, idx);
|
|
if (value) {
|
|
parts.push(value);
|
|
}
|
|
idx -= groupSize;
|
|
newGroupSize = groupSizes.shift();
|
|
groupSize = newGroupSize !== undefined ? newGroupSize : groupSize;
|
|
if (groupSize === 0) {
|
|
parts.push(integer.substring(0, idx));
|
|
break;
|
|
}
|
|
}
|
|
integer = parts.reverse().join(numberFormat[COMMA]);
|
|
number = number.substring(0, start) + integer + number.substring(end);
|
|
}
|
|
return number;
|
|
};
|
|
var round = function (value, precision) {
|
|
precision = precision || 0;
|
|
value = value.toString().split('e');
|
|
value = Math.round(+(value[0] + 'e' + (value[1] ? +value[1] + precision : precision)));
|
|
value = value.toString().split('e');
|
|
value = +(value[0] + 'e' + (value[1] ? +value[1] - precision : -precision));
|
|
return value.toFixed(precision);
|
|
};
|
|
var toString = function (value, fmt, culture) {
|
|
if (fmt) {
|
|
if (objectToString.call(value) === '[object Date]') {
|
|
return formatDate(value, fmt, culture);
|
|
} else if (typeof value === NUMBER) {
|
|
return formatNumber(value, fmt, culture);
|
|
}
|
|
}
|
|
return value !== undefined ? value : '';
|
|
};
|
|
kendo.format = function (fmt) {
|
|
var values = arguments;
|
|
return fmt.replace(formatRegExp, function (match, index, placeholderFormat) {
|
|
var value = values[parseInt(index, 10) + 1];
|
|
return toString(value, placeholderFormat ? placeholderFormat.substring(1) : '');
|
|
});
|
|
};
|
|
kendo._extractFormat = function (format) {
|
|
if (format.slice(0, 3) === '{0:') {
|
|
format = format.slice(3, format.length - 1);
|
|
}
|
|
return format;
|
|
};
|
|
kendo._activeElement = function () {
|
|
try {
|
|
return document.activeElement;
|
|
} catch (e) {
|
|
return document.documentElement.activeElement;
|
|
}
|
|
};
|
|
kendo._round = round;
|
|
kendo.toString = toString;
|
|
}());
|
|
(function () {
|
|
var nonBreakingSpaceRegExp = /\u00A0/g, exponentRegExp = /[eE][\-+]?[0-9]+/, shortTimeZoneRegExp = /[+|\-]\d{1,2}/, longTimeZoneRegExp = /[+|\-]\d{1,2}:?\d{2}/, dateRegExp = /^\/Date\((.*?)\)\/$/, offsetRegExp = /[+-]\d*/, formatsSequence = [
|
|
'G',
|
|
'g',
|
|
'd',
|
|
'F',
|
|
'D',
|
|
'y',
|
|
'm',
|
|
'T',
|
|
't'
|
|
], numberRegExp = {
|
|
2: /^\d{1,2}/,
|
|
3: /^\d{1,3}/,
|
|
4: /^\d{4}/
|
|
}, objectToString = {}.toString;
|
|
function outOfRange(value, start, end) {
|
|
return !(value >= start && value <= end);
|
|
}
|
|
function designatorPredicate(designator) {
|
|
return designator.charAt(0);
|
|
}
|
|
function mapDesignators(designators) {
|
|
return $.map(designators, designatorPredicate);
|
|
}
|
|
function adjustDST(date, hours) {
|
|
if (!hours && date.getHours() === 23) {
|
|
date.setHours(date.getHours() + 2);
|
|
}
|
|
}
|
|
function lowerArray(data) {
|
|
var idx = 0, length = data.length, array = [];
|
|
for (; idx < length; idx++) {
|
|
array[idx] = (data[idx] + '').toLowerCase();
|
|
}
|
|
return array;
|
|
}
|
|
function lowerLocalInfo(localInfo) {
|
|
var newLocalInfo = {}, property;
|
|
for (property in localInfo) {
|
|
newLocalInfo[property] = lowerArray(localInfo[property]);
|
|
}
|
|
return newLocalInfo;
|
|
}
|
|
function parseExact(value, format, culture) {
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
var lookAhead = function (match) {
|
|
var i = 0;
|
|
while (format[idx] === match) {
|
|
i++;
|
|
idx++;
|
|
}
|
|
if (i > 0) {
|
|
idx -= 1;
|
|
}
|
|
return i;
|
|
}, getNumber = function (size) {
|
|
var rg = numberRegExp[size] || new RegExp('^\\d{1,' + size + '}'), match = value.substr(valueIdx, size).match(rg);
|
|
if (match) {
|
|
match = match[0];
|
|
valueIdx += match.length;
|
|
return parseInt(match, 10);
|
|
}
|
|
return null;
|
|
}, getIndexByName = function (names, lower) {
|
|
var i = 0, length = names.length, name, nameLength, matchLength = 0, matchIdx = 0, subValue;
|
|
for (; i < length; i++) {
|
|
name = names[i];
|
|
nameLength = name.length;
|
|
subValue = value.substr(valueIdx, nameLength);
|
|
if (lower) {
|
|
subValue = subValue.toLowerCase();
|
|
}
|
|
if (subValue == name && nameLength > matchLength) {
|
|
matchLength = nameLength;
|
|
matchIdx = i;
|
|
}
|
|
}
|
|
if (matchLength) {
|
|
valueIdx += matchLength;
|
|
return matchIdx + 1;
|
|
}
|
|
return null;
|
|
}, checkLiteral = function () {
|
|
var result = false;
|
|
if (value.charAt(valueIdx) === format[idx]) {
|
|
valueIdx++;
|
|
result = true;
|
|
}
|
|
return result;
|
|
}, calendar = culture.calendars.standard, year = null, month = null, day = null, hours = null, minutes = null, seconds = null, milliseconds = null, idx = 0, valueIdx = 0, literal = false, date = new Date(), twoDigitYearMax = calendar.twoDigitYearMax || 2029, defaultYear = date.getFullYear(), ch, count, length, pattern, pmHour, UTC, matches, amDesignators, pmDesignators, hoursOffset, minutesOffset, hasTime, match;
|
|
if (!format) {
|
|
format = 'd';
|
|
}
|
|
pattern = calendar.patterns[format];
|
|
if (pattern) {
|
|
format = pattern;
|
|
}
|
|
format = format.split('');
|
|
length = format.length;
|
|
for (; idx < length; idx++) {
|
|
ch = format[idx];
|
|
if (literal) {
|
|
if (ch === '\'') {
|
|
literal = false;
|
|
} else {
|
|
checkLiteral();
|
|
}
|
|
} else {
|
|
if (ch === 'd') {
|
|
count = lookAhead('d');
|
|
if (!calendar._lowerDays) {
|
|
calendar._lowerDays = lowerLocalInfo(calendar.days);
|
|
}
|
|
if (day !== null && count > 2) {
|
|
continue;
|
|
}
|
|
day = count < 3 ? getNumber(2) : getIndexByName(calendar._lowerDays[count == 3 ? 'namesAbbr' : 'names'], true);
|
|
if (day === null || outOfRange(day, 1, 31)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 'M') {
|
|
count = lookAhead('M');
|
|
if (!calendar._lowerMonths) {
|
|
calendar._lowerMonths = lowerLocalInfo(calendar.months);
|
|
}
|
|
month = count < 3 ? getNumber(2) : getIndexByName(calendar._lowerMonths[count == 3 ? 'namesAbbr' : 'names'], true);
|
|
if (month === null || outOfRange(month, 1, 12)) {
|
|
return null;
|
|
}
|
|
month -= 1;
|
|
} else if (ch === 'y') {
|
|
count = lookAhead('y');
|
|
year = getNumber(count);
|
|
if (year === null) {
|
|
return null;
|
|
}
|
|
if (count == 2) {
|
|
if (typeof twoDigitYearMax === 'string') {
|
|
twoDigitYearMax = defaultYear + parseInt(twoDigitYearMax, 10);
|
|
}
|
|
year = defaultYear - defaultYear % 100 + year;
|
|
if (year > twoDigitYearMax) {
|
|
year -= 100;
|
|
}
|
|
}
|
|
} else if (ch === 'h') {
|
|
lookAhead('h');
|
|
hours = getNumber(2);
|
|
if (hours == 12) {
|
|
hours = 0;
|
|
}
|
|
if (hours === null || outOfRange(hours, 0, 11)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 'H') {
|
|
lookAhead('H');
|
|
hours = getNumber(2);
|
|
if (hours === null || outOfRange(hours, 0, 23)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 'm') {
|
|
lookAhead('m');
|
|
minutes = getNumber(2);
|
|
if (minutes === null || outOfRange(minutes, 0, 59)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 's') {
|
|
lookAhead('s');
|
|
seconds = getNumber(2);
|
|
if (seconds === null || outOfRange(seconds, 0, 59)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 'f') {
|
|
count = lookAhead('f');
|
|
match = value.substr(valueIdx, count).match(numberRegExp[3]);
|
|
milliseconds = getNumber(count);
|
|
if (milliseconds !== null) {
|
|
milliseconds = parseFloat('0.' + match[0], 10);
|
|
milliseconds = kendo._round(milliseconds, 3);
|
|
milliseconds *= 1000;
|
|
}
|
|
if (milliseconds === null || outOfRange(milliseconds, 0, 999)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 't') {
|
|
count = lookAhead('t');
|
|
amDesignators = calendar.AM;
|
|
pmDesignators = calendar.PM;
|
|
if (count === 1) {
|
|
amDesignators = mapDesignators(amDesignators);
|
|
pmDesignators = mapDesignators(pmDesignators);
|
|
}
|
|
pmHour = getIndexByName(pmDesignators);
|
|
if (!pmHour && !getIndexByName(amDesignators)) {
|
|
return null;
|
|
}
|
|
} else if (ch === 'z') {
|
|
UTC = true;
|
|
count = lookAhead('z');
|
|
if (value.substr(valueIdx, 1) === 'Z') {
|
|
checkLiteral();
|
|
continue;
|
|
}
|
|
matches = value.substr(valueIdx, 6).match(count > 2 ? longTimeZoneRegExp : shortTimeZoneRegExp);
|
|
if (!matches) {
|
|
return null;
|
|
}
|
|
matches = matches[0].split(':');
|
|
hoursOffset = matches[0];
|
|
minutesOffset = matches[1];
|
|
if (!minutesOffset && hoursOffset.length > 3) {
|
|
valueIdx = hoursOffset.length - 2;
|
|
minutesOffset = hoursOffset.substring(valueIdx);
|
|
hoursOffset = hoursOffset.substring(0, valueIdx);
|
|
}
|
|
hoursOffset = parseInt(hoursOffset, 10);
|
|
if (outOfRange(hoursOffset, -12, 13)) {
|
|
return null;
|
|
}
|
|
if (count > 2) {
|
|
minutesOffset = parseInt(minutesOffset, 10);
|
|
if (isNaN(minutesOffset) || outOfRange(minutesOffset, 0, 59)) {
|
|
return null;
|
|
}
|
|
}
|
|
} else if (ch === '\'') {
|
|
literal = true;
|
|
checkLiteral();
|
|
} else if (!checkLiteral()) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
hasTime = hours !== null || minutes !== null || seconds || null;
|
|
if (year === null && month === null && day === null && hasTime) {
|
|
year = defaultYear;
|
|
month = date.getMonth();
|
|
day = date.getDate();
|
|
} else {
|
|
if (year === null) {
|
|
year = defaultYear;
|
|
}
|
|
if (day === null) {
|
|
day = 1;
|
|
}
|
|
}
|
|
if (pmHour && hours < 12) {
|
|
hours += 12;
|
|
}
|
|
if (UTC) {
|
|
if (hoursOffset) {
|
|
hours += -hoursOffset;
|
|
}
|
|
if (minutesOffset) {
|
|
minutes += -minutesOffset;
|
|
}
|
|
value = new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds));
|
|
} else {
|
|
value = new Date(year, month, day, hours, minutes, seconds, milliseconds);
|
|
adjustDST(value, hours);
|
|
}
|
|
if (year < 100) {
|
|
value.setFullYear(year);
|
|
}
|
|
if (value.getDate() !== day && UTC === undefined) {
|
|
return null;
|
|
}
|
|
return value;
|
|
}
|
|
function parseMicrosoftFormatOffset(offset) {
|
|
var sign = offset.substr(0, 1) === '-' ? -1 : 1;
|
|
offset = offset.substring(1);
|
|
offset = parseInt(offset.substr(0, 2), 10) * 60 + parseInt(offset.substring(2), 10);
|
|
return sign * offset;
|
|
}
|
|
kendo.parseDate = function (value, formats, culture) {
|
|
if (objectToString.call(value) === '[object Date]') {
|
|
return value;
|
|
}
|
|
var idx = 0;
|
|
var date = null;
|
|
var length, patterns;
|
|
var tzoffset;
|
|
if (value && value.indexOf('/D') === 0) {
|
|
date = dateRegExp.exec(value);
|
|
if (date) {
|
|
date = date[1];
|
|
tzoffset = offsetRegExp.exec(date.substring(1));
|
|
date = new Date(parseInt(date, 10));
|
|
if (tzoffset) {
|
|
tzoffset = parseMicrosoftFormatOffset(tzoffset[0]);
|
|
date = kendo.timezone.apply(date, 0);
|
|
date = kendo.timezone.convert(date, 0, -1 * tzoffset);
|
|
}
|
|
return date;
|
|
}
|
|
}
|
|
culture = kendo.getCulture(culture);
|
|
if (!formats) {
|
|
formats = [];
|
|
patterns = culture.calendar.patterns;
|
|
length = formatsSequence.length;
|
|
for (; idx < length; idx++) {
|
|
formats[idx] = patterns[formatsSequence[idx]];
|
|
}
|
|
idx = 0;
|
|
formats = [
|
|
'yyyy/MM/dd HH:mm:ss',
|
|
'yyyy/MM/dd HH:mm',
|
|
'yyyy/MM/dd',
|
|
'ddd MMM dd yyyy HH:mm:ss',
|
|
'yyyy-MM-ddTHH:mm:ss.fffffffzzz',
|
|
'yyyy-MM-ddTHH:mm:ss.fffzzz',
|
|
'yyyy-MM-ddTHH:mm:sszzz',
|
|
'yyyy-MM-ddTHH:mm:ss.fffffff',
|
|
'yyyy-MM-ddTHH:mm:ss.fff',
|
|
'yyyy-MM-ddTHH:mmzzz',
|
|
'yyyy-MM-ddTHH:mmzz',
|
|
'yyyy-MM-ddTHH:mm:ss',
|
|
'yyyy-MM-ddTHH:mm',
|
|
'yyyy-MM-dd HH:mm:ss',
|
|
'yyyy-MM-dd HH:mm',
|
|
'yyyy-MM-dd',
|
|
'HH:mm:ss',
|
|
'HH:mm'
|
|
].concat(formats);
|
|
}
|
|
formats = isArray(formats) ? formats : [formats];
|
|
length = formats.length;
|
|
for (; idx < length; idx++) {
|
|
date = parseExact(value, formats[idx], culture);
|
|
if (date) {
|
|
return date;
|
|
}
|
|
}
|
|
return date;
|
|
};
|
|
kendo.parseInt = function (value, culture) {
|
|
var result = kendo.parseFloat(value, culture);
|
|
if (result) {
|
|
result = result | 0;
|
|
}
|
|
return result;
|
|
};
|
|
kendo.parseFloat = function (value, culture, format) {
|
|
if (!value && value !== 0) {
|
|
return null;
|
|
}
|
|
if (typeof value === NUMBER) {
|
|
return value;
|
|
}
|
|
value = value.toString();
|
|
culture = kendo.getCulture(culture);
|
|
var number = culture.numberFormat, percent = number.percent, currency = number.currency, symbol = currency.symbol, percentSymbol = percent.symbol, negative = value.indexOf('-'), parts, isPercent;
|
|
if (exponentRegExp.test(value)) {
|
|
value = parseFloat(value.replace(number['.'], '.'));
|
|
if (isNaN(value)) {
|
|
value = null;
|
|
}
|
|
return value;
|
|
}
|
|
if (negative > 0) {
|
|
return null;
|
|
} else {
|
|
negative = negative > -1;
|
|
}
|
|
if (value.indexOf(symbol) > -1 || format && format.toLowerCase().indexOf('c') > -1) {
|
|
number = currency;
|
|
parts = number.pattern[0].replace('$', symbol).split('n');
|
|
if (value.indexOf(parts[0]) > -1 && value.indexOf(parts[1]) > -1) {
|
|
value = value.replace(parts[0], '').replace(parts[1], '');
|
|
negative = true;
|
|
}
|
|
} else if (value.indexOf(percentSymbol) > -1) {
|
|
isPercent = true;
|
|
number = percent;
|
|
symbol = percentSymbol;
|
|
}
|
|
value = value.replace('-', '').replace(symbol, '').replace(nonBreakingSpaceRegExp, ' ').split(number[','].replace(nonBreakingSpaceRegExp, ' ')).join('').replace(number['.'], '.');
|
|
value = parseFloat(value);
|
|
if (isNaN(value)) {
|
|
value = null;
|
|
} else if (negative) {
|
|
value *= -1;
|
|
}
|
|
if (value && isPercent) {
|
|
value /= 100;
|
|
}
|
|
return value;
|
|
};
|
|
}());
|
|
function getShadows(element) {
|
|
var shadow = element.css(kendo.support.transitions.css + 'box-shadow') || element.css('box-shadow'), radius = shadow ? shadow.match(boxShadowRegExp) || [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
] : [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
], blur = math.max(+radius[3], +(radius[4] || 0));
|
|
return {
|
|
left: -radius[1] + blur,
|
|
right: +radius[1] + blur,
|
|
bottom: +radius[2] + blur
|
|
};
|
|
}
|
|
function wrap(element, autosize) {
|
|
var browser = support.browser, percentage, isRtl = element.css('direction') == 'rtl';
|
|
if (!element.parent().hasClass('k-animation-container')) {
|
|
var shadows = getShadows(element), width = element[0].style.width, height = element[0].style.height, percentWidth = percentRegExp.test(width), percentHeight = percentRegExp.test(height);
|
|
if (browser.opera) {
|
|
shadows.left = shadows.right = shadows.bottom = 5;
|
|
}
|
|
percentage = percentWidth || percentHeight;
|
|
if (!percentWidth && (!autosize || autosize && width)) {
|
|
width = element.outerWidth();
|
|
}
|
|
if (!percentHeight && (!autosize || autosize && height)) {
|
|
height = element.outerHeight();
|
|
}
|
|
element.wrap($('<div/>').addClass('k-animation-container').css({
|
|
width: width,
|
|
height: height,
|
|
marginLeft: shadows.left * (isRtl ? 1 : -1),
|
|
paddingLeft: shadows.left,
|
|
paddingRight: shadows.right,
|
|
paddingBottom: shadows.bottom
|
|
}));
|
|
if (percentage) {
|
|
element.css({
|
|
width: '100%',
|
|
height: '100%',
|
|
boxSizing: 'border-box',
|
|
mozBoxSizing: 'border-box',
|
|
webkitBoxSizing: 'border-box'
|
|
});
|
|
}
|
|
} else {
|
|
var wrapper = element.parent('.k-animation-container'), wrapperStyle = wrapper[0].style;
|
|
if (wrapper.is(':hidden')) {
|
|
wrapper.show();
|
|
}
|
|
percentage = percentRegExp.test(wrapperStyle.width) || percentRegExp.test(wrapperStyle.height);
|
|
if (!percentage) {
|
|
wrapper.css({
|
|
width: element.outerWidth(),
|
|
height: element.outerHeight(),
|
|
boxSizing: 'content-box',
|
|
mozBoxSizing: 'content-box',
|
|
webkitBoxSizing: 'content-box'
|
|
});
|
|
}
|
|
}
|
|
if (browser.msie && math.floor(browser.version) <= 7) {
|
|
element.css({ zoom: 1 });
|
|
element.children('.k-menu').width(element.width());
|
|
}
|
|
return element.parent();
|
|
}
|
|
function deepExtend(destination) {
|
|
var i = 1, length = arguments.length;
|
|
for (i = 1; i < length; i++) {
|
|
deepExtendOne(destination, arguments[i]);
|
|
}
|
|
return destination;
|
|
}
|
|
function deepExtendOne(destination, source) {
|
|
var ObservableArray = kendo.data.ObservableArray, LazyObservableArray = kendo.data.LazyObservableArray, DataSource = kendo.data.DataSource, HierarchicalDataSource = kendo.data.HierarchicalDataSource, property, propValue, propType, propInit, destProp;
|
|
for (property in source) {
|
|
propValue = source[property];
|
|
propType = typeof propValue;
|
|
if (propType === OBJECT && propValue !== null) {
|
|
propInit = propValue.constructor;
|
|
} else {
|
|
propInit = null;
|
|
}
|
|
if (propInit && propInit !== Array && propInit !== ObservableArray && propInit !== LazyObservableArray && propInit !== DataSource && propInit !== HierarchicalDataSource) {
|
|
if (propValue instanceof Date) {
|
|
destination[property] = new Date(propValue.getTime());
|
|
} else if (isFunction(propValue.clone)) {
|
|
destination[property] = propValue.clone();
|
|
} else {
|
|
destProp = destination[property];
|
|
if (typeof destProp === OBJECT) {
|
|
destination[property] = destProp || {};
|
|
} else {
|
|
destination[property] = {};
|
|
}
|
|
deepExtendOne(destination[property], propValue);
|
|
}
|
|
} else if (propType !== UNDEFINED) {
|
|
destination[property] = propValue;
|
|
}
|
|
}
|
|
return destination;
|
|
}
|
|
function testRx(agent, rxs, dflt) {
|
|
for (var rx in rxs) {
|
|
if (rxs.hasOwnProperty(rx) && rxs[rx].test(agent)) {
|
|
return rx;
|
|
}
|
|
}
|
|
return dflt !== undefined ? dflt : agent;
|
|
}
|
|
function toHyphens(str) {
|
|
return str.replace(/([a-z][A-Z])/g, function (g) {
|
|
return g.charAt(0) + '-' + g.charAt(1).toLowerCase();
|
|
});
|
|
}
|
|
function toCamelCase(str) {
|
|
return str.replace(/\-(\w)/g, function (strMatch, g1) {
|
|
return g1.toUpperCase();
|
|
});
|
|
}
|
|
function getComputedStyles(element, properties) {
|
|
var styles = {}, computedStyle;
|
|
if (document.defaultView && document.defaultView.getComputedStyle) {
|
|
computedStyle = document.defaultView.getComputedStyle(element, '');
|
|
if (properties) {
|
|
$.each(properties, function (idx, value) {
|
|
styles[value] = computedStyle.getPropertyValue(value);
|
|
});
|
|
}
|
|
} else {
|
|
computedStyle = element.currentStyle;
|
|
if (properties) {
|
|
$.each(properties, function (idx, value) {
|
|
styles[value] = computedStyle[toCamelCase(value)];
|
|
});
|
|
}
|
|
}
|
|
if (!kendo.size(styles)) {
|
|
styles = computedStyle;
|
|
}
|
|
return styles;
|
|
}
|
|
function isScrollable(element) {
|
|
if (element && element.className && typeof element.className === 'string' && element.className.indexOf('k-auto-scrollable') > -1) {
|
|
return true;
|
|
}
|
|
var overflow = getComputedStyles(element, ['overflow']).overflow;
|
|
return overflow == 'auto' || overflow == 'scroll';
|
|
}
|
|
function scrollLeft(element, value) {
|
|
var webkit = support.browser.webkit;
|
|
var mozila = support.browser.mozilla;
|
|
var el = element instanceof $ ? element[0] : element;
|
|
var isRtl;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
isRtl = support.isRtl(element);
|
|
if (value !== undefined) {
|
|
if (isRtl && webkit) {
|
|
el.scrollLeft = el.scrollWidth - el.clientWidth - value;
|
|
} else if (isRtl && mozila) {
|
|
el.scrollLeft = -value;
|
|
} else {
|
|
el.scrollLeft = value;
|
|
}
|
|
} else {
|
|
if (isRtl && webkit) {
|
|
return el.scrollWidth - el.clientWidth - el.scrollLeft;
|
|
} else {
|
|
return Math.abs(el.scrollLeft);
|
|
}
|
|
}
|
|
}
|
|
(function () {
|
|
support._scrollbar = undefined;
|
|
support.scrollbar = function (refresh) {
|
|
if (!isNaN(support._scrollbar) && !refresh) {
|
|
return support._scrollbar;
|
|
} else {
|
|
var div = document.createElement('div'), result;
|
|
div.style.cssText = 'overflow:scroll;overflow-x:hidden;zoom:1;clear:both;display:block';
|
|
div.innerHTML = ' ';
|
|
document.body.appendChild(div);
|
|
support._scrollbar = result = div.offsetWidth - div.scrollWidth;
|
|
document.body.removeChild(div);
|
|
return result;
|
|
}
|
|
};
|
|
support.isRtl = function (element) {
|
|
return $(element).closest('.k-rtl').length > 0;
|
|
};
|
|
var table = document.createElement('table');
|
|
try {
|
|
table.innerHTML = '<tr><td></td></tr>';
|
|
support.tbodyInnerHtml = true;
|
|
} catch (e) {
|
|
support.tbodyInnerHtml = false;
|
|
}
|
|
support.touch = 'ontouchstart' in window;
|
|
support.msPointers = window.MSPointerEvent;
|
|
support.pointers = window.PointerEvent;
|
|
var transitions = support.transitions = false, transforms = support.transforms = false, elementProto = 'HTMLElement' in window ? HTMLElement.prototype : [];
|
|
support.hasHW3D = 'WebKitCSSMatrix' in window && 'm11' in new window.WebKitCSSMatrix() || 'MozPerspective' in document.documentElement.style || 'msPerspective' in document.documentElement.style;
|
|
each([
|
|
'Moz',
|
|
'webkit',
|
|
'O',
|
|
'ms'
|
|
], function () {
|
|
var prefix = this.toString(), hasTransitions = typeof table.style[prefix + 'Transition'] === STRING;
|
|
if (hasTransitions || typeof table.style[prefix + 'Transform'] === STRING) {
|
|
var lowPrefix = prefix.toLowerCase();
|
|
transforms = {
|
|
css: lowPrefix != 'ms' ? '-' + lowPrefix + '-' : '',
|
|
prefix: prefix,
|
|
event: lowPrefix === 'o' || lowPrefix === 'webkit' ? lowPrefix : ''
|
|
};
|
|
if (hasTransitions) {
|
|
transitions = transforms;
|
|
transitions.event = transitions.event ? transitions.event + 'TransitionEnd' : 'transitionend';
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
table = null;
|
|
support.transforms = transforms;
|
|
support.transitions = transitions;
|
|
support.devicePixelRatio = window.devicePixelRatio === undefined ? 1 : window.devicePixelRatio;
|
|
try {
|
|
support.screenWidth = window.outerWidth || window.screen ? window.screen.availWidth : window.innerWidth;
|
|
support.screenHeight = window.outerHeight || window.screen ? window.screen.availHeight : window.innerHeight;
|
|
} catch (e) {
|
|
support.screenWidth = window.screen.availWidth;
|
|
support.screenHeight = window.screen.availHeight;
|
|
}
|
|
support.detectOS = function (ua) {
|
|
var os = false, minorVersion, match = [], notAndroidPhone = !/mobile safari/i.test(ua), agentRxs = {
|
|
wp: /(Windows Phone(?: OS)?)\s(\d+)\.(\d+(\.\d+)?)/,
|
|
fire: /(Silk)\/(\d+)\.(\d+(\.\d+)?)/,
|
|
android: /(Android|Android.*(?:Opera|Firefox).*?\/)\s*(\d+)\.(\d+(\.\d+)?)/,
|
|
iphone: /(iPhone|iPod).*OS\s+(\d+)[\._]([\d\._]+)/,
|
|
ipad: /(iPad).*OS\s+(\d+)[\._]([\d_]+)/,
|
|
meego: /(MeeGo).+NokiaBrowser\/(\d+)\.([\d\._]+)/,
|
|
webos: /(webOS)\/(\d+)\.(\d+(\.\d+)?)/,
|
|
blackberry: /(BlackBerry|BB10).*?Version\/(\d+)\.(\d+(\.\d+)?)/,
|
|
playbook: /(PlayBook).*?Tablet\s*OS\s*(\d+)\.(\d+(\.\d+)?)/,
|
|
windows: /(MSIE)\s+(\d+)\.(\d+(\.\d+)?)/,
|
|
tizen: /(tizen).*?Version\/(\d+)\.(\d+(\.\d+)?)/i,
|
|
sailfish: /(sailfish).*rv:(\d+)\.(\d+(\.\d+)?).*firefox/i,
|
|
ffos: /(Mobile).*rv:(\d+)\.(\d+(\.\d+)?).*Firefox/
|
|
}, osRxs = {
|
|
ios: /^i(phone|pad|pod)$/i,
|
|
android: /^android|fire$/i,
|
|
blackberry: /^blackberry|playbook/i,
|
|
windows: /windows/,
|
|
wp: /wp/,
|
|
flat: /sailfish|ffos|tizen/i,
|
|
meego: /meego/
|
|
}, formFactorRxs = { tablet: /playbook|ipad|fire/i }, browserRxs = {
|
|
omini: /Opera\sMini/i,
|
|
omobile: /Opera\sMobi/i,
|
|
firefox: /Firefox|Fennec/i,
|
|
mobilesafari: /version\/.*safari/i,
|
|
ie: /MSIE|Windows\sPhone/i,
|
|
chrome: /chrome|crios/i,
|
|
webkit: /webkit/i
|
|
};
|
|
for (var agent in agentRxs) {
|
|
if (agentRxs.hasOwnProperty(agent)) {
|
|
match = ua.match(agentRxs[agent]);
|
|
if (match) {
|
|
if (agent == 'windows' && 'plugins' in navigator) {
|
|
return false;
|
|
}
|
|
os = {};
|
|
os.device = agent;
|
|
os.tablet = testRx(agent, formFactorRxs, false);
|
|
os.browser = testRx(ua, browserRxs, 'default');
|
|
os.name = testRx(agent, osRxs);
|
|
os[os.name] = true;
|
|
os.majorVersion = match[2];
|
|
os.minorVersion = match[3].replace('_', '.');
|
|
minorVersion = os.minorVersion.replace('.', '').substr(0, 2);
|
|
os.flatVersion = os.majorVersion + minorVersion + new Array(3 - (minorVersion.length < 3 ? minorVersion.length : 2)).join('0');
|
|
os.cordova = typeof window.PhoneGap !== UNDEFINED || typeof window.cordova !== UNDEFINED;
|
|
os.appMode = window.navigator.standalone || /file|local|wmapp/.test(window.location.protocol) || os.cordova;
|
|
if (os.android && (support.devicePixelRatio < 1.5 && os.flatVersion < 400 || notAndroidPhone) && (support.screenWidth > 800 || support.screenHeight > 800)) {
|
|
os.tablet = agent;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return os;
|
|
};
|
|
var mobileOS = support.mobileOS = support.detectOS(navigator.userAgent);
|
|
support.wpDevicePixelRatio = mobileOS.wp ? screen.width / 320 : 0;
|
|
support.kineticScrollNeeded = mobileOS && (support.touch || support.msPointers || support.pointers);
|
|
support.hasNativeScrolling = false;
|
|
if (mobileOS.ios || mobileOS.android && mobileOS.majorVersion > 2 || mobileOS.wp) {
|
|
support.hasNativeScrolling = mobileOS;
|
|
}
|
|
support.delayedClick = function () {
|
|
if (support.touch) {
|
|
if (mobileOS.ios) {
|
|
return true;
|
|
}
|
|
if (mobileOS.android) {
|
|
if (!support.browser.chrome) {
|
|
return true;
|
|
}
|
|
if (support.browser.version < 32) {
|
|
return false;
|
|
}
|
|
return !($('meta[name=viewport]').attr('content') || '').match(/user-scalable=no/i);
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
support.mouseAndTouchPresent = support.touch && !(support.mobileOS.ios || support.mobileOS.android);
|
|
support.detectBrowser = function (ua) {
|
|
var browser = false, match = [], browserRxs = {
|
|
edge: /(edge)[ \/]([\w.]+)/i,
|
|
webkit: /(chrome)[ \/]([\w.]+)/i,
|
|
safari: /(webkit)[ \/]([\w.]+)/i,
|
|
opera: /(opera)(?:.*version|)[ \/]([\w.]+)/i,
|
|
msie: /(msie\s|trident.*? rv:)([\w.]+)/i,
|
|
mozilla: /(mozilla)(?:.*? rv:([\w.]+)|)/i
|
|
};
|
|
for (var agent in browserRxs) {
|
|
if (browserRxs.hasOwnProperty(agent)) {
|
|
match = ua.match(browserRxs[agent]);
|
|
if (match) {
|
|
browser = {};
|
|
browser[agent] = true;
|
|
browser[match[1].toLowerCase().split(' ')[0].split('/')[0]] = true;
|
|
browser.version = parseInt(document.documentMode || match[2], 10);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return browser;
|
|
};
|
|
support.browser = support.detectBrowser(navigator.userAgent);
|
|
support.detectClipboardAccess = function () {
|
|
var commands = {
|
|
copy: document.queryCommandSupported ? document.queryCommandSupported('copy') : false,
|
|
cut: document.queryCommandSupported ? document.queryCommandSupported('cut') : false,
|
|
paste: document.queryCommandSupported ? document.queryCommandSupported('paste') : false
|
|
};
|
|
if (support.browser.chrome && support.browser.version >= 43) {
|
|
commands.copy = true;
|
|
commands.cut = true;
|
|
}
|
|
return commands;
|
|
};
|
|
support.clipboard = support.detectClipboardAccess();
|
|
support.zoomLevel = function () {
|
|
try {
|
|
var browser = support.browser;
|
|
var ie11WidthCorrection = 0;
|
|
var docEl = document.documentElement;
|
|
if (browser.msie && browser.version == 11 && docEl.scrollHeight > docEl.clientHeight && !support.touch) {
|
|
ie11WidthCorrection = support.scrollbar();
|
|
}
|
|
return support.touch ? docEl.clientWidth / window.innerWidth : browser.msie && browser.version >= 10 ? ((top || window).document.documentElement.offsetWidth + ie11WidthCorrection) / (top || window).innerWidth : 1;
|
|
} catch (e) {
|
|
return 1;
|
|
}
|
|
};
|
|
support.cssBorderSpacing = typeof document.documentElement.style.borderSpacing != 'undefined' && !(support.browser.msie && support.browser.version < 8);
|
|
(function (browser) {
|
|
var cssClass = '', docElement = $(document.documentElement), majorVersion = parseInt(browser.version, 10);
|
|
if (browser.msie) {
|
|
cssClass = 'ie';
|
|
} else if (browser.mozilla) {
|
|
cssClass = 'ff';
|
|
} else if (browser.safari) {
|
|
cssClass = 'safari';
|
|
} else if (browser.webkit) {
|
|
cssClass = 'webkit';
|
|
} else if (browser.opera) {
|
|
cssClass = 'opera';
|
|
} else if (browser.edge) {
|
|
cssClass = 'edge';
|
|
}
|
|
if (cssClass) {
|
|
cssClass = 'k-' + cssClass + ' k-' + cssClass + majorVersion;
|
|
}
|
|
if (support.mobileOS) {
|
|
cssClass += ' k-mobile';
|
|
}
|
|
docElement.addClass(cssClass);
|
|
}(support.browser));
|
|
support.eventCapture = document.documentElement.addEventListener;
|
|
var input = document.createElement('input');
|
|
support.placeholder = 'placeholder' in input;
|
|
support.propertyChangeEvent = 'onpropertychange' in input;
|
|
support.input = function () {
|
|
var types = [
|
|
'number',
|
|
'date',
|
|
'time',
|
|
'month',
|
|
'week',
|
|
'datetime',
|
|
'datetime-local'
|
|
];
|
|
var length = types.length;
|
|
var value = 'test';
|
|
var result = {};
|
|
var idx = 0;
|
|
var type;
|
|
for (; idx < length; idx++) {
|
|
type = types[idx];
|
|
input.setAttribute('type', type);
|
|
input.value = value;
|
|
result[type.replace('-', '')] = input.type !== 'text' && input.value !== value;
|
|
}
|
|
return result;
|
|
}();
|
|
input.style.cssText = 'float:left;';
|
|
support.cssFloat = !!input.style.cssFloat;
|
|
input = null;
|
|
support.stableSort = function () {
|
|
var threshold = 513;
|
|
var sorted = [{
|
|
index: 0,
|
|
field: 'b'
|
|
}];
|
|
for (var i = 1; i < threshold; i++) {
|
|
sorted.push({
|
|
index: i,
|
|
field: 'a'
|
|
});
|
|
}
|
|
sorted.sort(function (a, b) {
|
|
return a.field > b.field ? 1 : a.field < b.field ? -1 : 0;
|
|
});
|
|
return sorted[0].index === 1;
|
|
}();
|
|
support.matchesSelector = elementProto.webkitMatchesSelector || elementProto.mozMatchesSelector || elementProto.msMatchesSelector || elementProto.oMatchesSelector || elementProto.matchesSelector || elementProto.matches || function (selector) {
|
|
var nodeList = document.querySelectorAll ? (this.parentNode || document).querySelectorAll(selector) || [] : $(selector), i = nodeList.length;
|
|
while (i--) {
|
|
if (nodeList[i] == this) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
support.pushState = window.history && window.history.pushState;
|
|
var documentMode = document.documentMode;
|
|
support.hashChange = 'onhashchange' in window && !(support.browser.msie && (!documentMode || documentMode <= 8));
|
|
support.customElements = 'registerElement' in window.document;
|
|
}());
|
|
function size(obj) {
|
|
var result = 0, key;
|
|
for (key in obj) {
|
|
if (obj.hasOwnProperty(key) && key != 'toJSON') {
|
|
result++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function getOffset(element, type, positioned) {
|
|
if (!type) {
|
|
type = 'offset';
|
|
}
|
|
var result = element[type]();
|
|
if (support.browser.msie && (support.pointers || support.msPointers) && !positioned) {
|
|
result.top -= window.pageYOffset - document.documentElement.scrollTop;
|
|
result.left -= window.pageXOffset - document.documentElement.scrollLeft;
|
|
}
|
|
return result;
|
|
}
|
|
var directions = {
|
|
left: { reverse: 'right' },
|
|
right: { reverse: 'left' },
|
|
down: { reverse: 'up' },
|
|
up: { reverse: 'down' },
|
|
top: { reverse: 'bottom' },
|
|
bottom: { reverse: 'top' },
|
|
'in': { reverse: 'out' },
|
|
out: { reverse: 'in' }
|
|
};
|
|
function parseEffects(input) {
|
|
var effects = {};
|
|
each(typeof input === 'string' ? input.split(' ') : input, function (idx) {
|
|
effects[idx] = this;
|
|
});
|
|
return effects;
|
|
}
|
|
function fx(element) {
|
|
return new kendo.effects.Element(element);
|
|
}
|
|
var effects = {};
|
|
$.extend(effects, {
|
|
enabled: true,
|
|
Element: function (element) {
|
|
this.element = $(element);
|
|
},
|
|
promise: function (element, options) {
|
|
if (!element.is(':visible')) {
|
|
element.css({ display: element.data('olddisplay') || 'block' }).css('display');
|
|
}
|
|
if (options.hide) {
|
|
element.data('olddisplay', element.css('display')).hide();
|
|
}
|
|
if (options.init) {
|
|
options.init();
|
|
}
|
|
if (options.completeCallback) {
|
|
options.completeCallback(element);
|
|
}
|
|
element.dequeue();
|
|
},
|
|
disable: function () {
|
|
this.enabled = false;
|
|
this.promise = this.promiseShim;
|
|
},
|
|
enable: function () {
|
|
this.enabled = true;
|
|
this.promise = this.animatedPromise;
|
|
}
|
|
});
|
|
effects.promiseShim = effects.promise;
|
|
function prepareAnimationOptions(options, duration, reverse, complete) {
|
|
if (typeof options === STRING) {
|
|
if (isFunction(duration)) {
|
|
complete = duration;
|
|
duration = 400;
|
|
reverse = false;
|
|
}
|
|
if (isFunction(reverse)) {
|
|
complete = reverse;
|
|
reverse = false;
|
|
}
|
|
if (typeof duration === BOOLEAN) {
|
|
reverse = duration;
|
|
duration = 400;
|
|
}
|
|
options = {
|
|
effects: options,
|
|
duration: duration,
|
|
reverse: reverse,
|
|
complete: complete
|
|
};
|
|
}
|
|
return extend({
|
|
effects: {},
|
|
duration: 400,
|
|
reverse: false,
|
|
init: noop,
|
|
teardown: noop,
|
|
hide: false
|
|
}, options, {
|
|
completeCallback: options.complete,
|
|
complete: noop
|
|
});
|
|
}
|
|
function animate(element, options, duration, reverse, complete) {
|
|
var idx = 0, length = element.length, instance;
|
|
for (; idx < length; idx++) {
|
|
instance = $(element[idx]);
|
|
instance.queue(function () {
|
|
effects.promise(instance, prepareAnimationOptions(options, duration, reverse, complete));
|
|
});
|
|
}
|
|
return element;
|
|
}
|
|
function toggleClass(element, classes, options, add) {
|
|
if (classes) {
|
|
classes = classes.split(' ');
|
|
each(classes, function (idx, value) {
|
|
element.toggleClass(value, add);
|
|
});
|
|
}
|
|
return element;
|
|
}
|
|
if (!('kendoAnimate' in $.fn)) {
|
|
extend($.fn, {
|
|
kendoStop: function (clearQueue, gotoEnd) {
|
|
return this.stop(clearQueue, gotoEnd);
|
|
},
|
|
kendoAnimate: function (options, duration, reverse, complete) {
|
|
return animate(this, options, duration, reverse, complete);
|
|
},
|
|
kendoAddClass: function (classes, options) {
|
|
return kendo.toggleClass(this, classes, options, true);
|
|
},
|
|
kendoRemoveClass: function (classes, options) {
|
|
return kendo.toggleClass(this, classes, options, false);
|
|
},
|
|
kendoToggleClass: function (classes, options, toggle) {
|
|
return kendo.toggleClass(this, classes, options, toggle);
|
|
}
|
|
});
|
|
}
|
|
var ampRegExp = /&/g, ltRegExp = /</g, quoteRegExp = /"/g, aposRegExp = /'/g, gtRegExp = />/g;
|
|
function htmlEncode(value) {
|
|
return ('' + value).replace(ampRegExp, '&').replace(ltRegExp, '<').replace(gtRegExp, '>').replace(quoteRegExp, '"').replace(aposRegExp, ''');
|
|
}
|
|
var eventTarget = function (e) {
|
|
return e.target;
|
|
};
|
|
if (support.touch) {
|
|
eventTarget = function (e) {
|
|
var touches = 'originalEvent' in e ? e.originalEvent.changedTouches : 'changedTouches' in e ? e.changedTouches : null;
|
|
return touches ? document.elementFromPoint(touches[0].clientX, touches[0].clientY) : e.target;
|
|
};
|
|
each([
|
|
'swipe',
|
|
'swipeLeft',
|
|
'swipeRight',
|
|
'swipeUp',
|
|
'swipeDown',
|
|
'doubleTap',
|
|
'tap'
|
|
], function (m, value) {
|
|
$.fn[value] = function (callback) {
|
|
return this.bind(value, callback);
|
|
};
|
|
});
|
|
}
|
|
if (support.touch) {
|
|
if (!support.mobileOS) {
|
|
support.mousedown = 'mousedown touchstart';
|
|
support.mouseup = 'mouseup touchend';
|
|
support.mousemove = 'mousemove touchmove';
|
|
support.mousecancel = 'mouseleave touchcancel';
|
|
support.click = 'click';
|
|
support.resize = 'resize';
|
|
} else {
|
|
support.mousedown = 'touchstart';
|
|
support.mouseup = 'touchend';
|
|
support.mousemove = 'touchmove';
|
|
support.mousecancel = 'touchcancel';
|
|
support.click = 'touchend';
|
|
support.resize = 'orientationchange';
|
|
}
|
|
} else if (support.pointers) {
|
|
support.mousemove = 'pointermove';
|
|
support.mousedown = 'pointerdown';
|
|
support.mouseup = 'pointerup';
|
|
support.mousecancel = 'pointercancel';
|
|
support.click = 'pointerup';
|
|
support.resize = 'orientationchange resize';
|
|
} else if (support.msPointers) {
|
|
support.mousemove = 'MSPointerMove';
|
|
support.mousedown = 'MSPointerDown';
|
|
support.mouseup = 'MSPointerUp';
|
|
support.mousecancel = 'MSPointerCancel';
|
|
support.click = 'MSPointerUp';
|
|
support.resize = 'orientationchange resize';
|
|
} else {
|
|
support.mousemove = 'mousemove';
|
|
support.mousedown = 'mousedown';
|
|
support.mouseup = 'mouseup';
|
|
support.mousecancel = 'mouseleave';
|
|
support.click = 'click';
|
|
support.resize = 'resize';
|
|
}
|
|
var wrapExpression = function (members, paramName) {
|
|
var result = paramName || 'd', index, idx, length, member, count = 1;
|
|
for (idx = 0, length = members.length; idx < length; idx++) {
|
|
member = members[idx];
|
|
if (member !== '') {
|
|
index = member.indexOf('[');
|
|
if (index !== 0) {
|
|
if (index == -1) {
|
|
member = '.' + member;
|
|
} else {
|
|
count++;
|
|
member = '.' + member.substring(0, index) + ' || {})' + member.substring(index);
|
|
}
|
|
}
|
|
count++;
|
|
result += member + (idx < length - 1 ? ' || {})' : ')');
|
|
}
|
|
}
|
|
return new Array(count).join('(') + result;
|
|
}, localUrlRe = /^([a-z]+:)?\/\//i;
|
|
extend(kendo, {
|
|
widgets: [],
|
|
_widgetRegisteredCallbacks: [],
|
|
ui: kendo.ui || {},
|
|
fx: kendo.fx || fx,
|
|
effects: kendo.effects || effects,
|
|
mobile: kendo.mobile || {},
|
|
data: kendo.data || {},
|
|
dataviz: kendo.dataviz || {},
|
|
drawing: kendo.drawing || {},
|
|
spreadsheet: { messages: {} },
|
|
keys: {
|
|
INSERT: 45,
|
|
DELETE: 46,
|
|
BACKSPACE: 8,
|
|
TAB: 9,
|
|
ENTER: 13,
|
|
ESC: 27,
|
|
LEFT: 37,
|
|
UP: 38,
|
|
RIGHT: 39,
|
|
DOWN: 40,
|
|
END: 35,
|
|
HOME: 36,
|
|
SPACEBAR: 32,
|
|
PAGEUP: 33,
|
|
PAGEDOWN: 34,
|
|
F2: 113,
|
|
F10: 121,
|
|
F12: 123,
|
|
NUMPAD_PLUS: 107,
|
|
NUMPAD_MINUS: 109,
|
|
NUMPAD_DOT: 110
|
|
},
|
|
support: kendo.support || support,
|
|
animate: kendo.animate || animate,
|
|
ns: '',
|
|
attr: function (value) {
|
|
return 'data-' + kendo.ns + value;
|
|
},
|
|
getShadows: getShadows,
|
|
wrap: wrap,
|
|
deepExtend: deepExtend,
|
|
getComputedStyles: getComputedStyles,
|
|
webComponents: kendo.webComponents || [],
|
|
isScrollable: isScrollable,
|
|
scrollLeft: scrollLeft,
|
|
size: size,
|
|
toCamelCase: toCamelCase,
|
|
toHyphens: toHyphens,
|
|
getOffset: kendo.getOffset || getOffset,
|
|
parseEffects: kendo.parseEffects || parseEffects,
|
|
toggleClass: kendo.toggleClass || toggleClass,
|
|
directions: kendo.directions || directions,
|
|
Observable: Observable,
|
|
Class: Class,
|
|
Template: Template,
|
|
template: proxy(Template.compile, Template),
|
|
render: proxy(Template.render, Template),
|
|
stringify: proxy(JSON.stringify, JSON),
|
|
eventTarget: eventTarget,
|
|
htmlEncode: htmlEncode,
|
|
isLocalUrl: function (url) {
|
|
return url && !localUrlRe.test(url);
|
|
},
|
|
expr: function (expression, safe, paramName) {
|
|
expression = expression || '';
|
|
if (typeof safe == STRING) {
|
|
paramName = safe;
|
|
safe = false;
|
|
}
|
|
paramName = paramName || 'd';
|
|
if (expression && expression.charAt(0) !== '[') {
|
|
expression = '.' + expression;
|
|
}
|
|
if (safe) {
|
|
expression = expression.replace(/"([^.]*)\.([^"]*)"/g, '"$1_$DOT$_$2"');
|
|
expression = expression.replace(/'([^.]*)\.([^']*)'/g, '\'$1_$DOT$_$2\'');
|
|
expression = wrapExpression(expression.split('.'), paramName);
|
|
expression = expression.replace(/_\$DOT\$_/g, '.');
|
|
} else {
|
|
expression = paramName + expression;
|
|
}
|
|
return expression;
|
|
},
|
|
getter: function (expression, safe) {
|
|
var key = expression + safe;
|
|
return getterCache[key] = getterCache[key] || new Function('d', 'return ' + kendo.expr(expression, safe));
|
|
},
|
|
setter: function (expression) {
|
|
return setterCache[expression] = setterCache[expression] || new Function('d,value', kendo.expr(expression) + '=value');
|
|
},
|
|
accessor: function (expression) {
|
|
return {
|
|
get: kendo.getter(expression),
|
|
set: kendo.setter(expression)
|
|
};
|
|
},
|
|
guid: function () {
|
|
var id = '', i, random;
|
|
for (i = 0; i < 32; i++) {
|
|
random = math.random() * 16 | 0;
|
|
if (i == 8 || i == 12 || i == 16 || i == 20) {
|
|
id += '-';
|
|
}
|
|
id += (i == 12 ? 4 : i == 16 ? random & 3 | 8 : random).toString(16);
|
|
}
|
|
return id;
|
|
},
|
|
roleSelector: function (role) {
|
|
return role.replace(/(\S+)/g, '[' + kendo.attr('role') + '=$1],').slice(0, -1);
|
|
},
|
|
directiveSelector: function (directives) {
|
|
var selectors = directives.split(' ');
|
|
if (selectors) {
|
|
for (var i = 0; i < selectors.length; i++) {
|
|
if (selectors[i] != 'view') {
|
|
selectors[i] = selectors[i].replace(/(\w*)(view|bar|strip|over)$/, '$1-$2');
|
|
}
|
|
}
|
|
}
|
|
return selectors.join(' ').replace(/(\S+)/g, 'kendo-mobile-$1,').slice(0, -1);
|
|
},
|
|
triggeredByInput: function (e) {
|
|
return /^(label|input|textarea|select)$/i.test(e.target.tagName);
|
|
},
|
|
onWidgetRegistered: function (callback) {
|
|
for (var i = 0, len = kendo.widgets.length; i < len; i++) {
|
|
callback(kendo.widgets[i]);
|
|
}
|
|
kendo._widgetRegisteredCallbacks.push(callback);
|
|
},
|
|
logToConsole: function (message, type) {
|
|
var console = window.console;
|
|
if (!kendo.suppressLog && typeof console != 'undefined' && console.log) {
|
|
console[type || 'log'](message);
|
|
}
|
|
}
|
|
});
|
|
var Widget = Observable.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
that.element = kendo.jQuery(element).handler(that);
|
|
that.angular('init', options);
|
|
Observable.fn.init.call(that);
|
|
var dataSource = options ? options.dataSource : null;
|
|
if (dataSource) {
|
|
options = extend({}, options, { dataSource: {} });
|
|
}
|
|
options = that.options = extend(true, {}, that.options, options);
|
|
if (dataSource) {
|
|
options.dataSource = dataSource;
|
|
}
|
|
if (!that.element.attr(kendo.attr('role'))) {
|
|
that.element.attr(kendo.attr('role'), (options.name || '').toLowerCase());
|
|
}
|
|
that.element.data('kendo' + options.prefix + options.name, that);
|
|
that.bind(that.events, options);
|
|
},
|
|
events: [],
|
|
options: { prefix: '' },
|
|
_hasBindingTarget: function () {
|
|
return !!this.element[0].kendoBindingTarget;
|
|
},
|
|
_tabindex: function (target) {
|
|
target = target || this.wrapper;
|
|
var element = this.element, TABINDEX = 'tabindex', tabindex = target.attr(TABINDEX) || element.attr(TABINDEX);
|
|
element.removeAttr(TABINDEX);
|
|
target.attr(TABINDEX, !isNaN(tabindex) ? tabindex : 0);
|
|
},
|
|
setOptions: function (options) {
|
|
this._setEvents(options);
|
|
$.extend(this.options, options);
|
|
},
|
|
_setEvents: function (options) {
|
|
var that = this, idx = 0, length = that.events.length, e;
|
|
for (; idx < length; idx++) {
|
|
e = that.events[idx];
|
|
if (that.options[e] && options[e]) {
|
|
that.unbind(e, that.options[e]);
|
|
}
|
|
}
|
|
that.bind(that.events, options);
|
|
},
|
|
resize: function (force) {
|
|
var size = this.getSize(), currentSize = this._size;
|
|
if (force || (size.width > 0 || size.height > 0) && (!currentSize || size.width !== currentSize.width || size.height !== currentSize.height)) {
|
|
this._size = size;
|
|
this._resize(size, force);
|
|
this.trigger('resize', size);
|
|
}
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.element);
|
|
},
|
|
size: function (size) {
|
|
if (!size) {
|
|
return this.getSize();
|
|
} else {
|
|
this.setSize(size);
|
|
}
|
|
},
|
|
setSize: $.noop,
|
|
_resize: $.noop,
|
|
destroy: function () {
|
|
var that = this;
|
|
that.element.removeData('kendo' + that.options.prefix + that.options.name);
|
|
that.element.removeData('handler');
|
|
that.unbind();
|
|
},
|
|
_destroy: function () {
|
|
this.destroy();
|
|
},
|
|
angular: function () {
|
|
},
|
|
_muteAngularRebind: function (callback) {
|
|
this._muteRebind = true;
|
|
callback.call(this);
|
|
this._muteRebind = false;
|
|
}
|
|
});
|
|
var DataBoundWidget = Widget.extend({
|
|
dataItems: function () {
|
|
return this.dataSource.flatView();
|
|
},
|
|
_angularItems: function (cmd) {
|
|
var that = this;
|
|
that.angular(cmd, function () {
|
|
return {
|
|
elements: that.items(),
|
|
data: $.map(that.dataItems(), function (dataItem) {
|
|
return { dataItem: dataItem };
|
|
})
|
|
};
|
|
});
|
|
}
|
|
});
|
|
kendo.dimensions = function (element, dimensions) {
|
|
var domElement = element[0];
|
|
if (dimensions) {
|
|
element.css(dimensions);
|
|
}
|
|
return {
|
|
width: domElement.offsetWidth,
|
|
height: domElement.offsetHeight
|
|
};
|
|
};
|
|
kendo.notify = noop;
|
|
var templateRegExp = /template$/i, jsonRegExp = /^\s*(?:\{(?:.|\r\n|\n)*\}|\[(?:.|\r\n|\n)*\])\s*$/, jsonFormatRegExp = /^\{(\d+)(:[^\}]+)?\}|^\[[A-Za-z_]*\]$/, dashRegExp = /([A-Z])/g;
|
|
function parseOption(element, option) {
|
|
var value;
|
|
if (option.indexOf('data') === 0) {
|
|
option = option.substring(4);
|
|
option = option.charAt(0).toLowerCase() + option.substring(1);
|
|
}
|
|
option = option.replace(dashRegExp, '-$1');
|
|
value = element.getAttribute('data-' + kendo.ns + option);
|
|
if (value === null) {
|
|
value = undefined;
|
|
} else if (value === 'null') {
|
|
value = null;
|
|
} else if (value === 'true') {
|
|
value = true;
|
|
} else if (value === 'false') {
|
|
value = false;
|
|
} else if (numberRegExp.test(value)) {
|
|
value = parseFloat(value);
|
|
} else if (jsonRegExp.test(value) && !jsonFormatRegExp.test(value)) {
|
|
value = new Function('return (' + value + ')')();
|
|
}
|
|
return value;
|
|
}
|
|
function parseOptions(element, options) {
|
|
var result = {}, option, value;
|
|
for (option in options) {
|
|
value = parseOption(element, option);
|
|
if (value !== undefined) {
|
|
if (templateRegExp.test(option)) {
|
|
value = kendo.template($('#' + value).html());
|
|
}
|
|
result[option] = value;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
kendo.initWidget = function (element, options, roles) {
|
|
var result, option, widget, idx, length, role, value, dataSource, fullPath, widgetKeyRegExp;
|
|
if (!roles) {
|
|
roles = kendo.ui.roles;
|
|
} else if (roles.roles) {
|
|
roles = roles.roles;
|
|
}
|
|
element = element.nodeType ? element : element[0];
|
|
role = element.getAttribute('data-' + kendo.ns + 'role');
|
|
if (!role) {
|
|
return;
|
|
}
|
|
fullPath = role.indexOf('.') === -1;
|
|
if (fullPath) {
|
|
widget = roles[role];
|
|
} else {
|
|
widget = kendo.getter(role)(window);
|
|
}
|
|
var data = $(element).data(), widgetKey = widget ? 'kendo' + widget.fn.options.prefix + widget.fn.options.name : '';
|
|
if (fullPath) {
|
|
widgetKeyRegExp = new RegExp('^kendo.*' + role + '$', 'i');
|
|
} else {
|
|
widgetKeyRegExp = new RegExp('^' + widgetKey + '$', 'i');
|
|
}
|
|
for (var key in data) {
|
|
if (key.match(widgetKeyRegExp)) {
|
|
if (key === widgetKey) {
|
|
result = data[key];
|
|
} else {
|
|
return data[key];
|
|
}
|
|
}
|
|
}
|
|
if (!widget) {
|
|
return;
|
|
}
|
|
dataSource = parseOption(element, 'dataSource');
|
|
options = $.extend({}, parseOptions(element, widget.fn.options), options);
|
|
if (dataSource) {
|
|
if (typeof dataSource === STRING) {
|
|
options.dataSource = kendo.getter(dataSource)(window);
|
|
} else {
|
|
options.dataSource = dataSource;
|
|
}
|
|
}
|
|
for (idx = 0, length = widget.fn.events.length; idx < length; idx++) {
|
|
option = widget.fn.events[idx];
|
|
value = parseOption(element, option);
|
|
if (value !== undefined) {
|
|
options[option] = kendo.getter(value)(window);
|
|
}
|
|
}
|
|
if (!result) {
|
|
result = new widget(element, options);
|
|
} else if (!$.isEmptyObject(options)) {
|
|
result.setOptions(options);
|
|
}
|
|
return result;
|
|
};
|
|
kendo.rolesFromNamespaces = function (namespaces) {
|
|
var roles = [], idx, length;
|
|
if (!namespaces[0]) {
|
|
namespaces = [
|
|
kendo.ui,
|
|
kendo.dataviz.ui
|
|
];
|
|
}
|
|
for (idx = 0, length = namespaces.length; idx < length; idx++) {
|
|
roles[idx] = namespaces[idx].roles;
|
|
}
|
|
return extend.apply(null, [{}].concat(roles.reverse()));
|
|
};
|
|
kendo.init = function (element) {
|
|
var roles = kendo.rolesFromNamespaces(slice.call(arguments, 1));
|
|
$(element).find('[data-' + kendo.ns + 'role]').addBack().each(function () {
|
|
kendo.initWidget(this, {}, roles);
|
|
});
|
|
};
|
|
kendo.destroy = function (element) {
|
|
$(element).find('[data-' + kendo.ns + 'role]').addBack().each(function () {
|
|
var data = $(this).data();
|
|
for (var key in data) {
|
|
if (key.indexOf('kendo') === 0 && typeof data[key].destroy === FUNCTION) {
|
|
data[key].destroy();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
function containmentComparer(a, b) {
|
|
return $.contains(a, b) ? -1 : 1;
|
|
}
|
|
function resizableWidget() {
|
|
var widget = $(this);
|
|
return $.inArray(widget.attr('data-' + kendo.ns + 'role'), [
|
|
'slider',
|
|
'rangeslider'
|
|
]) > -1 || widget.is(':visible');
|
|
}
|
|
kendo.resize = function (element, force) {
|
|
var widgets = $(element).find('[data-' + kendo.ns + 'role]').addBack().filter(resizableWidget);
|
|
if (!widgets.length) {
|
|
return;
|
|
}
|
|
var widgetsArray = $.makeArray(widgets);
|
|
widgetsArray.sort(containmentComparer);
|
|
$.each(widgetsArray, function () {
|
|
var widget = kendo.widgetInstance($(this));
|
|
if (widget) {
|
|
widget.resize(force);
|
|
}
|
|
});
|
|
};
|
|
kendo.parseOptions = parseOptions;
|
|
extend(kendo.ui, {
|
|
Widget: Widget,
|
|
DataBoundWidget: DataBoundWidget,
|
|
roles: {},
|
|
progress: function (container, toggle) {
|
|
var mask = container.find('.k-loading-mask'), support = kendo.support, browser = support.browser, isRtl, leftRight, webkitCorrection, containerScrollLeft;
|
|
if (toggle) {
|
|
if (!mask.length) {
|
|
isRtl = support.isRtl(container);
|
|
leftRight = isRtl ? 'right' : 'left';
|
|
containerScrollLeft = container.scrollLeft();
|
|
webkitCorrection = browser.webkit ? !isRtl ? 0 : container[0].scrollWidth - container.width() - 2 * containerScrollLeft : 0;
|
|
mask = $('<div class=\'k-loading-mask\'><span class=\'k-loading-text\'>Loading...</span><div class=\'k-loading-image\'/><div class=\'k-loading-color\'/></div>').width('100%').height('100%').css('top', container.scrollTop()).css(leftRight, Math.abs(containerScrollLeft) + webkitCorrection).prependTo(container);
|
|
}
|
|
} else if (mask) {
|
|
mask.remove();
|
|
}
|
|
},
|
|
plugin: function (widget, register, prefix) {
|
|
var name = widget.fn.options.name, getter;
|
|
register = register || kendo.ui;
|
|
prefix = prefix || '';
|
|
register[name] = widget;
|
|
register.roles[name.toLowerCase()] = widget;
|
|
getter = 'getKendo' + prefix + name;
|
|
name = 'kendo' + prefix + name;
|
|
var widgetEntry = {
|
|
name: name,
|
|
widget: widget,
|
|
prefix: prefix || ''
|
|
};
|
|
kendo.widgets.push(widgetEntry);
|
|
for (var i = 0, len = kendo._widgetRegisteredCallbacks.length; i < len; i++) {
|
|
kendo._widgetRegisteredCallbacks[i](widgetEntry);
|
|
}
|
|
$.fn[name] = function (options) {
|
|
var value = this, args;
|
|
if (typeof options === STRING) {
|
|
args = slice.call(arguments, 1);
|
|
this.each(function () {
|
|
var widget = $.data(this, name), method, result;
|
|
if (!widget) {
|
|
throw new Error(kendo.format('Cannot call method \'{0}\' of {1} before it is initialized', options, name));
|
|
}
|
|
method = widget[options];
|
|
if (typeof method !== FUNCTION) {
|
|
throw new Error(kendo.format('Cannot find method \'{0}\' of {1}', options, name));
|
|
}
|
|
result = method.apply(widget, args);
|
|
if (result !== undefined) {
|
|
value = result;
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
this.each(function () {
|
|
return new widget(this, options);
|
|
});
|
|
}
|
|
return value;
|
|
};
|
|
$.fn[name].widget = widget;
|
|
$.fn[getter] = function () {
|
|
return this.data(name);
|
|
};
|
|
}
|
|
});
|
|
var ContainerNullObject = {
|
|
bind: function () {
|
|
return this;
|
|
},
|
|
nullObject: true,
|
|
options: {}
|
|
};
|
|
var MobileWidget = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.autoApplyNS();
|
|
this.wrapper = this.element;
|
|
this.element.addClass('km-widget');
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.element.kendoDestroy();
|
|
},
|
|
options: { prefix: 'Mobile' },
|
|
events: [],
|
|
view: function () {
|
|
var viewElement = this.element.closest(kendo.roleSelector('view splitview modalview drawer'));
|
|
return kendo.widgetInstance(viewElement, kendo.mobile.ui) || ContainerNullObject;
|
|
},
|
|
viewHasNativeScrolling: function () {
|
|
var view = this.view();
|
|
return view && view.options.useNativeScrolling;
|
|
},
|
|
container: function () {
|
|
var element = this.element.closest(kendo.roleSelector('view layout modalview drawer splitview'));
|
|
return kendo.widgetInstance(element.eq(0), kendo.mobile.ui) || ContainerNullObject;
|
|
}
|
|
});
|
|
extend(kendo.mobile, {
|
|
init: function (element) {
|
|
kendo.init(element, kendo.mobile.ui, kendo.ui, kendo.dataviz.ui);
|
|
},
|
|
appLevelNativeScrolling: function () {
|
|
return kendo.mobile.application && kendo.mobile.application.options && kendo.mobile.application.options.useNativeScrolling;
|
|
},
|
|
roles: {},
|
|
ui: {
|
|
Widget: MobileWidget,
|
|
DataBoundWidget: DataBoundWidget.extend(MobileWidget.prototype),
|
|
roles: {},
|
|
plugin: function (widget) {
|
|
kendo.ui.plugin(widget, kendo.mobile.ui, 'Mobile');
|
|
}
|
|
}
|
|
});
|
|
deepExtend(kendo.dataviz, {
|
|
init: function (element) {
|
|
kendo.init(element, kendo.dataviz.ui);
|
|
},
|
|
ui: {
|
|
roles: {},
|
|
themes: {},
|
|
views: [],
|
|
plugin: function (widget) {
|
|
kendo.ui.plugin(widget, kendo.dataviz.ui);
|
|
}
|
|
},
|
|
roles: {}
|
|
});
|
|
kendo.touchScroller = function (elements, options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
options.useNative = true;
|
|
return $(elements).map(function (idx, element) {
|
|
element = $(element);
|
|
if (support.kineticScrollNeeded && kendo.mobile.ui.Scroller && !element.data('kendoMobileScroller')) {
|
|
element.kendoMobileScroller(options);
|
|
return element.data('kendoMobileScroller');
|
|
} else {
|
|
return false;
|
|
}
|
|
})[0];
|
|
};
|
|
kendo.preventDefault = function (e) {
|
|
e.preventDefault();
|
|
};
|
|
kendo.widgetInstance = function (element, suites) {
|
|
var role = element.data(kendo.ns + 'role'), widgets = [], i, length;
|
|
if (role) {
|
|
if (role === 'content') {
|
|
role = 'scroller';
|
|
}
|
|
if (suites) {
|
|
if (suites[0]) {
|
|
for (i = 0, length = suites.length; i < length; i++) {
|
|
widgets.push(suites[i].roles[role]);
|
|
}
|
|
} else {
|
|
widgets.push(suites.roles[role]);
|
|
}
|
|
} else {
|
|
widgets = [
|
|
kendo.ui.roles[role],
|
|
kendo.dataviz.ui.roles[role],
|
|
kendo.mobile.ui.roles[role]
|
|
];
|
|
}
|
|
if (role.indexOf('.') >= 0) {
|
|
widgets = [kendo.getter(role)(window)];
|
|
}
|
|
for (i = 0, length = widgets.length; i < length; i++) {
|
|
var widget = widgets[i];
|
|
if (widget) {
|
|
var instance = element.data('kendo' + widget.fn.options.prefix + widget.fn.options.name);
|
|
if (instance) {
|
|
return instance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
kendo.onResize = function (callback) {
|
|
var handler = callback;
|
|
if (support.mobileOS.android) {
|
|
handler = function () {
|
|
setTimeout(callback, 600);
|
|
};
|
|
}
|
|
$(window).on(support.resize, handler);
|
|
return handler;
|
|
};
|
|
kendo.unbindResize = function (callback) {
|
|
$(window).off(support.resize, callback);
|
|
};
|
|
kendo.attrValue = function (element, key) {
|
|
return element.data(kendo.ns + key);
|
|
};
|
|
kendo.days = {
|
|
Sunday: 0,
|
|
Monday: 1,
|
|
Tuesday: 2,
|
|
Wednesday: 3,
|
|
Thursday: 4,
|
|
Friday: 5,
|
|
Saturday: 6
|
|
};
|
|
function focusable(element, isTabIndexNotNaN) {
|
|
var nodeName = element.nodeName.toLowerCase();
|
|
return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled : 'a' === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) && visible(element);
|
|
}
|
|
function visible(element) {
|
|
return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () {
|
|
return $.css(this, 'visibility') === 'hidden';
|
|
}).length;
|
|
}
|
|
$.extend($.expr[':'], {
|
|
kendoFocusable: function (element) {
|
|
var idx = $.attr(element, 'tabindex');
|
|
return focusable(element, !isNaN(idx) && idx > -1);
|
|
}
|
|
});
|
|
var MOUSE_EVENTS = [
|
|
'mousedown',
|
|
'mousemove',
|
|
'mouseenter',
|
|
'mouseleave',
|
|
'mouseover',
|
|
'mouseout',
|
|
'mouseup',
|
|
'click'
|
|
];
|
|
var EXCLUDE_BUST_CLICK_SELECTOR = 'label, input, [data-rel=external]';
|
|
var MouseEventNormalizer = {
|
|
setupMouseMute: function () {
|
|
var idx = 0, length = MOUSE_EVENTS.length, element = document.documentElement;
|
|
if (MouseEventNormalizer.mouseTrap || !support.eventCapture) {
|
|
return;
|
|
}
|
|
MouseEventNormalizer.mouseTrap = true;
|
|
MouseEventNormalizer.bustClick = false;
|
|
MouseEventNormalizer.captureMouse = false;
|
|
var handler = function (e) {
|
|
if (MouseEventNormalizer.captureMouse) {
|
|
if (e.type === 'click') {
|
|
if (MouseEventNormalizer.bustClick && !$(e.target).is(EXCLUDE_BUST_CLICK_SELECTOR)) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
} else {
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
};
|
|
for (; idx < length; idx++) {
|
|
element.addEventListener(MOUSE_EVENTS[idx], handler, true);
|
|
}
|
|
},
|
|
muteMouse: function (e) {
|
|
MouseEventNormalizer.captureMouse = true;
|
|
if (e.data.bustClick) {
|
|
MouseEventNormalizer.bustClick = true;
|
|
}
|
|
clearTimeout(MouseEventNormalizer.mouseTrapTimeoutID);
|
|
},
|
|
unMuteMouse: function () {
|
|
clearTimeout(MouseEventNormalizer.mouseTrapTimeoutID);
|
|
MouseEventNormalizer.mouseTrapTimeoutID = setTimeout(function () {
|
|
MouseEventNormalizer.captureMouse = false;
|
|
MouseEventNormalizer.bustClick = false;
|
|
}, 400);
|
|
}
|
|
};
|
|
var eventMap = {
|
|
down: 'touchstart mousedown',
|
|
move: 'mousemove touchmove',
|
|
up: 'mouseup touchend touchcancel',
|
|
cancel: 'mouseleave touchcancel'
|
|
};
|
|
if (support.touch && (support.mobileOS.ios || support.mobileOS.android)) {
|
|
eventMap = {
|
|
down: 'touchstart',
|
|
move: 'touchmove',
|
|
up: 'touchend touchcancel',
|
|
cancel: 'touchcancel'
|
|
};
|
|
} else if (support.pointers) {
|
|
eventMap = {
|
|
down: 'pointerdown',
|
|
move: 'pointermove',
|
|
up: 'pointerup',
|
|
cancel: 'pointercancel pointerleave'
|
|
};
|
|
} else if (support.msPointers) {
|
|
eventMap = {
|
|
down: 'MSPointerDown',
|
|
move: 'MSPointerMove',
|
|
up: 'MSPointerUp',
|
|
cancel: 'MSPointerCancel MSPointerLeave'
|
|
};
|
|
}
|
|
if (support.msPointers && !('onmspointerenter' in window)) {
|
|
$.each({
|
|
MSPointerEnter: 'MSPointerOver',
|
|
MSPointerLeave: 'MSPointerOut'
|
|
}, function (orig, fix) {
|
|
$.event.special[orig] = {
|
|
delegateType: fix,
|
|
bindType: fix,
|
|
handle: function (event) {
|
|
var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj;
|
|
if (!related || related !== target && !$.contains(target, related)) {
|
|
event.type = handleObj.origType;
|
|
ret = handleObj.handler.apply(this, arguments);
|
|
event.type = fix;
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
});
|
|
}
|
|
var getEventMap = function (e) {
|
|
return eventMap[e] || e;
|
|
}, eventRegEx = /([^ ]+)/g;
|
|
kendo.applyEventMap = function (events, ns) {
|
|
events = events.replace(eventRegEx, getEventMap);
|
|
if (ns) {
|
|
events = events.replace(eventRegEx, '$1.' + ns);
|
|
}
|
|
return events;
|
|
};
|
|
var on = $.fn.on;
|
|
function kendoJQuery(selector, context) {
|
|
return new kendoJQuery.fn.init(selector, context);
|
|
}
|
|
extend(true, kendoJQuery, $);
|
|
kendoJQuery.fn = kendoJQuery.prototype = new $();
|
|
kendoJQuery.fn.constructor = kendoJQuery;
|
|
kendoJQuery.fn.init = function (selector, context) {
|
|
if (context && context instanceof $ && !(context instanceof kendoJQuery)) {
|
|
context = kendoJQuery(context);
|
|
}
|
|
return $.fn.init.call(this, selector, context, rootjQuery);
|
|
};
|
|
kendoJQuery.fn.init.prototype = kendoJQuery.fn;
|
|
var rootjQuery = kendoJQuery(document);
|
|
extend(kendoJQuery.fn, {
|
|
handler: function (handler) {
|
|
this.data('handler', handler);
|
|
return this;
|
|
},
|
|
autoApplyNS: function (ns) {
|
|
this.data('kendoNS', ns || kendo.guid());
|
|
return this;
|
|
},
|
|
on: function () {
|
|
var that = this, ns = that.data('kendoNS');
|
|
if (arguments.length === 1) {
|
|
return on.call(that, arguments[0]);
|
|
}
|
|
var context = that, args = slice.call(arguments);
|
|
if (typeof args[args.length - 1] === UNDEFINED) {
|
|
args.pop();
|
|
}
|
|
var callback = args[args.length - 1], events = kendo.applyEventMap(args[0], ns);
|
|
if (support.mouseAndTouchPresent && events.search(/mouse|click/) > -1 && this[0] !== document.documentElement) {
|
|
MouseEventNormalizer.setupMouseMute();
|
|
var selector = args.length === 2 ? null : args[1], bustClick = events.indexOf('click') > -1 && events.indexOf('touchend') > -1;
|
|
on.call(this, {
|
|
touchstart: MouseEventNormalizer.muteMouse,
|
|
touchend: MouseEventNormalizer.unMuteMouse
|
|
}, selector, { bustClick: bustClick });
|
|
}
|
|
if (typeof callback === STRING) {
|
|
context = that.data('handler');
|
|
callback = context[callback];
|
|
args[args.length - 1] = function (e) {
|
|
callback.call(context, e);
|
|
};
|
|
}
|
|
args[0] = events;
|
|
on.apply(that, args);
|
|
return that;
|
|
},
|
|
kendoDestroy: function (ns) {
|
|
ns = ns || this.data('kendoNS');
|
|
if (ns) {
|
|
this.off('.' + ns);
|
|
}
|
|
return this;
|
|
}
|
|
});
|
|
kendo.jQuery = kendoJQuery;
|
|
kendo.eventMap = eventMap;
|
|
kendo.timezone = function () {
|
|
var months = {
|
|
Jan: 0,
|
|
Feb: 1,
|
|
Mar: 2,
|
|
Apr: 3,
|
|
May: 4,
|
|
Jun: 5,
|
|
Jul: 6,
|
|
Aug: 7,
|
|
Sep: 8,
|
|
Oct: 9,
|
|
Nov: 10,
|
|
Dec: 11
|
|
};
|
|
var days = {
|
|
Sun: 0,
|
|
Mon: 1,
|
|
Tue: 2,
|
|
Wed: 3,
|
|
Thu: 4,
|
|
Fri: 5,
|
|
Sat: 6
|
|
};
|
|
function ruleToDate(year, rule) {
|
|
var date;
|
|
var targetDay;
|
|
var ourDay;
|
|
var month = rule[3];
|
|
var on = rule[4];
|
|
var time = rule[5];
|
|
var cache = rule[8];
|
|
if (!cache) {
|
|
rule[8] = cache = {};
|
|
}
|
|
if (cache[year]) {
|
|
return cache[year];
|
|
}
|
|
if (!isNaN(on)) {
|
|
date = new Date(Date.UTC(year, months[month], on, time[0], time[1], time[2], 0));
|
|
} else if (on.indexOf('last') === 0) {
|
|
date = new Date(Date.UTC(year, months[month] + 1, 1, time[0] - 24, time[1], time[2], 0));
|
|
targetDay = days[on.substr(4, 3)];
|
|
ourDay = date.getUTCDay();
|
|
date.setUTCDate(date.getUTCDate() + targetDay - ourDay - (targetDay > ourDay ? 7 : 0));
|
|
} else if (on.indexOf('>=') >= 0) {
|
|
date = new Date(Date.UTC(year, months[month], on.substr(5), time[0], time[1], time[2], 0));
|
|
targetDay = days[on.substr(0, 3)];
|
|
ourDay = date.getUTCDay();
|
|
date.setUTCDate(date.getUTCDate() + targetDay - ourDay + (targetDay < ourDay ? 7 : 0));
|
|
}
|
|
return cache[year] = date;
|
|
}
|
|
function findRule(utcTime, rules, zone) {
|
|
rules = rules[zone];
|
|
if (!rules) {
|
|
var time = zone.split(':');
|
|
var offset = 0;
|
|
if (time.length > 1) {
|
|
offset = time[0] * 60 + Number(time[1]);
|
|
}
|
|
return [
|
|
-1000000,
|
|
'max',
|
|
'-',
|
|
'Jan',
|
|
1,
|
|
[
|
|
0,
|
|
0,
|
|
0
|
|
],
|
|
offset,
|
|
'-'
|
|
];
|
|
}
|
|
var year = new Date(utcTime).getUTCFullYear();
|
|
rules = jQuery.grep(rules, function (rule) {
|
|
var from = rule[0];
|
|
var to = rule[1];
|
|
return from <= year && (to >= year || from == year && to == 'only' || to == 'max');
|
|
});
|
|
rules.push(utcTime);
|
|
rules.sort(function (a, b) {
|
|
if (typeof a != 'number') {
|
|
a = Number(ruleToDate(year, a));
|
|
}
|
|
if (typeof b != 'number') {
|
|
b = Number(ruleToDate(year, b));
|
|
}
|
|
return a - b;
|
|
});
|
|
var rule = rules[jQuery.inArray(utcTime, rules) - 1] || rules[rules.length - 1];
|
|
return isNaN(rule) ? rule : null;
|
|
}
|
|
function findZone(utcTime, zones, timezone) {
|
|
var zoneRules = zones[timezone];
|
|
if (typeof zoneRules === 'string') {
|
|
zoneRules = zones[zoneRules];
|
|
}
|
|
if (!zoneRules) {
|
|
throw new Error('Timezone "' + timezone + '" is either incorrect, or kendo.timezones.min.js is not included.');
|
|
}
|
|
for (var idx = zoneRules.length - 1; idx >= 0; idx--) {
|
|
var until = zoneRules[idx][3];
|
|
if (until && utcTime > until) {
|
|
break;
|
|
}
|
|
}
|
|
var zone = zoneRules[idx + 1];
|
|
if (!zone) {
|
|
throw new Error('Timezone "' + timezone + '" not found on ' + utcTime + '.');
|
|
}
|
|
return zone;
|
|
}
|
|
function zoneAndRule(utcTime, zones, rules, timezone) {
|
|
if (typeof utcTime != NUMBER) {
|
|
utcTime = Date.UTC(utcTime.getFullYear(), utcTime.getMonth(), utcTime.getDate(), utcTime.getHours(), utcTime.getMinutes(), utcTime.getSeconds(), utcTime.getMilliseconds());
|
|
}
|
|
var zone = findZone(utcTime, zones, timezone);
|
|
return {
|
|
zone: zone,
|
|
rule: findRule(utcTime, rules, zone[1])
|
|
};
|
|
}
|
|
function offset(utcTime, timezone) {
|
|
if (timezone == 'Etc/UTC' || timezone == 'Etc/GMT') {
|
|
return 0;
|
|
}
|
|
var info = zoneAndRule(utcTime, this.zones, this.rules, timezone);
|
|
var zone = info.zone;
|
|
var rule = info.rule;
|
|
return kendo.parseFloat(rule ? zone[0] - rule[6] : zone[0]);
|
|
}
|
|
function abbr(utcTime, timezone) {
|
|
var info = zoneAndRule(utcTime, this.zones, this.rules, timezone);
|
|
var zone = info.zone;
|
|
var rule = info.rule;
|
|
var base = zone[2];
|
|
if (base.indexOf('/') >= 0) {
|
|
return base.split('/')[rule && +rule[6] ? 1 : 0];
|
|
} else if (base.indexOf('%s') >= 0) {
|
|
return base.replace('%s', !rule || rule[7] == '-' ? '' : rule[7]);
|
|
}
|
|
return base;
|
|
}
|
|
function convert(date, fromOffset, toOffset) {
|
|
if (typeof fromOffset == STRING) {
|
|
fromOffset = this.offset(date, fromOffset);
|
|
}
|
|
if (typeof toOffset == STRING) {
|
|
toOffset = this.offset(date, toOffset);
|
|
}
|
|
var fromLocalOffset = date.getTimezoneOffset();
|
|
date = new Date(date.getTime() + (fromOffset - toOffset) * 60000);
|
|
var toLocalOffset = date.getTimezoneOffset();
|
|
return new Date(date.getTime() + (toLocalOffset - fromLocalOffset) * 60000);
|
|
}
|
|
function apply(date, timezone) {
|
|
return this.convert(date, date.getTimezoneOffset(), timezone);
|
|
}
|
|
function remove(date, timezone) {
|
|
return this.convert(date, timezone, date.getTimezoneOffset());
|
|
}
|
|
function toLocalDate(time) {
|
|
return this.apply(new Date(time), 'Etc/UTC');
|
|
}
|
|
return {
|
|
zones: {},
|
|
rules: {},
|
|
offset: offset,
|
|
convert: convert,
|
|
apply: apply,
|
|
remove: remove,
|
|
abbr: abbr,
|
|
toLocalDate: toLocalDate
|
|
};
|
|
}();
|
|
kendo.date = function () {
|
|
var MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000;
|
|
function adjustDST(date, hours) {
|
|
if (hours === 0 && date.getHours() === 23) {
|
|
date.setHours(date.getHours() + 2);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function setDayOfWeek(date, day, dir) {
|
|
var hours = date.getHours();
|
|
dir = dir || 1;
|
|
day = (day - date.getDay() + 7 * dir) % 7;
|
|
date.setDate(date.getDate() + day);
|
|
adjustDST(date, hours);
|
|
}
|
|
function dayOfWeek(date, day, dir) {
|
|
date = new Date(date);
|
|
setDayOfWeek(date, day, dir);
|
|
return date;
|
|
}
|
|
function firstDayOfMonth(date) {
|
|
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
}
|
|
function lastDayOfMonth(date) {
|
|
var last = new Date(date.getFullYear(), date.getMonth() + 1, 0), first = firstDayOfMonth(date), timeOffset = Math.abs(last.getTimezoneOffset() - first.getTimezoneOffset());
|
|
if (timeOffset) {
|
|
last.setHours(first.getHours() + timeOffset / 60);
|
|
}
|
|
return last;
|
|
}
|
|
function getDate(date) {
|
|
date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
|
adjustDST(date, 0);
|
|
return date;
|
|
}
|
|
function toUtcTime(date) {
|
|
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
}
|
|
function getMilliseconds(date) {
|
|
return date.getTime() - getDate(date);
|
|
}
|
|
function isInTimeRange(value, min, max) {
|
|
var msMin = getMilliseconds(min), msMax = getMilliseconds(max), msValue;
|
|
if (!value || msMin == msMax) {
|
|
return true;
|
|
}
|
|
if (min >= max) {
|
|
max += MS_PER_DAY;
|
|
}
|
|
msValue = getMilliseconds(value);
|
|
if (msMin > msValue) {
|
|
msValue += MS_PER_DAY;
|
|
}
|
|
if (msMax < msMin) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
return msValue >= msMin && msValue <= msMax;
|
|
}
|
|
function isInDateRange(value, min, max) {
|
|
var msMin = min.getTime(), msMax = max.getTime(), msValue;
|
|
if (msMin >= msMax) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
msValue = value.getTime();
|
|
return msValue >= msMin && msValue <= msMax;
|
|
}
|
|
function addDays(date, offset) {
|
|
var hours = date.getHours();
|
|
date = new Date(date);
|
|
setTime(date, offset * MS_PER_DAY);
|
|
adjustDST(date, hours);
|
|
return date;
|
|
}
|
|
function setTime(date, milliseconds, ignoreDST) {
|
|
var offset = date.getTimezoneOffset();
|
|
var difference;
|
|
date.setTime(date.getTime() + milliseconds);
|
|
if (!ignoreDST) {
|
|
difference = date.getTimezoneOffset() - offset;
|
|
date.setTime(date.getTime() + difference * MS_PER_MINUTE);
|
|
}
|
|
}
|
|
function setHours(date, time) {
|
|
date = new Date(kendo.date.getDate(date).getTime() + kendo.date.getMilliseconds(time));
|
|
adjustDST(date, time.getHours());
|
|
return date;
|
|
}
|
|
function today() {
|
|
return getDate(new Date());
|
|
}
|
|
function isToday(date) {
|
|
return getDate(date).getTime() == today().getTime();
|
|
}
|
|
function toInvariantTime(date) {
|
|
var staticDate = new Date(1980, 1, 1, 0, 0, 0);
|
|
if (date) {
|
|
staticDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
}
|
|
return staticDate;
|
|
}
|
|
return {
|
|
adjustDST: adjustDST,
|
|
dayOfWeek: dayOfWeek,
|
|
setDayOfWeek: setDayOfWeek,
|
|
getDate: getDate,
|
|
isInDateRange: isInDateRange,
|
|
isInTimeRange: isInTimeRange,
|
|
isToday: isToday,
|
|
nextDay: function (date) {
|
|
return addDays(date, 1);
|
|
},
|
|
previousDay: function (date) {
|
|
return addDays(date, -1);
|
|
},
|
|
toUtcTime: toUtcTime,
|
|
MS_PER_DAY: MS_PER_DAY,
|
|
MS_PER_HOUR: 60 * MS_PER_MINUTE,
|
|
MS_PER_MINUTE: MS_PER_MINUTE,
|
|
setTime: setTime,
|
|
setHours: setHours,
|
|
addDays: addDays,
|
|
today: today,
|
|
toInvariantTime: toInvariantTime,
|
|
firstDayOfMonth: firstDayOfMonth,
|
|
lastDayOfMonth: lastDayOfMonth,
|
|
getMilliseconds: getMilliseconds
|
|
};
|
|
}();
|
|
kendo.stripWhitespace = function (element) {
|
|
if (document.createNodeIterator) {
|
|
var iterator = document.createNodeIterator(element, NodeFilter.SHOW_TEXT, function (node) {
|
|
return node.parentNode == element ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
}, false);
|
|
while (iterator.nextNode()) {
|
|
if (iterator.referenceNode && !iterator.referenceNode.textContent.trim()) {
|
|
iterator.referenceNode.parentNode.removeChild(iterator.referenceNode);
|
|
}
|
|
}
|
|
} else {
|
|
for (var i = 0; i < element.childNodes.length; i++) {
|
|
var child = element.childNodes[i];
|
|
if (child.nodeType == 3 && !/\S/.test(child.nodeValue)) {
|
|
element.removeChild(child);
|
|
i--;
|
|
}
|
|
if (child.nodeType == 1) {
|
|
kendo.stripWhitespace(child);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var animationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
|
|
setTimeout(callback, 1000 / 60);
|
|
};
|
|
kendo.animationFrame = function (callback) {
|
|
animationFrame.call(window, callback);
|
|
};
|
|
var animationQueue = [];
|
|
kendo.queueAnimation = function (callback) {
|
|
animationQueue[animationQueue.length] = callback;
|
|
if (animationQueue.length === 1) {
|
|
kendo.runNextAnimation();
|
|
}
|
|
};
|
|
kendo.runNextAnimation = function () {
|
|
kendo.animationFrame(function () {
|
|
if (animationQueue[0]) {
|
|
animationQueue.shift()();
|
|
if (animationQueue[0]) {
|
|
kendo.runNextAnimation();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
kendo.parseQueryStringParams = function (url) {
|
|
var queryString = url.split('?')[1] || '', params = {}, paramParts = queryString.split(/&|=/), length = paramParts.length, idx = 0;
|
|
for (; idx < length; idx += 2) {
|
|
if (paramParts[idx] !== '') {
|
|
params[decodeURIComponent(paramParts[idx])] = decodeURIComponent(paramParts[idx + 1]);
|
|
}
|
|
}
|
|
return params;
|
|
};
|
|
kendo.elementUnderCursor = function (e) {
|
|
if (typeof e.x.client != 'undefined') {
|
|
return document.elementFromPoint(e.x.client, e.y.client);
|
|
}
|
|
};
|
|
kendo.wheelDeltaY = function (jQueryEvent) {
|
|
var e = jQueryEvent.originalEvent, deltaY = e.wheelDeltaY, delta;
|
|
if (e.wheelDelta) {
|
|
if (deltaY === undefined || deltaY) {
|
|
delta = e.wheelDelta;
|
|
}
|
|
} else if (e.detail && e.axis === e.VERTICAL_AXIS) {
|
|
delta = -e.detail * 10;
|
|
}
|
|
return delta;
|
|
};
|
|
kendo.throttle = function (fn, delay) {
|
|
var timeout;
|
|
var lastExecTime = 0;
|
|
if (!delay || delay <= 0) {
|
|
return fn;
|
|
}
|
|
var throttled = function () {
|
|
var that = this;
|
|
var elapsed = +new Date() - lastExecTime;
|
|
var args = arguments;
|
|
function exec() {
|
|
fn.apply(that, args);
|
|
lastExecTime = +new Date();
|
|
}
|
|
if (!lastExecTime) {
|
|
return exec();
|
|
}
|
|
if (timeout) {
|
|
clearTimeout(timeout);
|
|
}
|
|
if (elapsed > delay) {
|
|
exec();
|
|
} else {
|
|
timeout = setTimeout(exec, delay - elapsed);
|
|
}
|
|
};
|
|
throttled.cancel = function () {
|
|
clearTimeout(timeout);
|
|
};
|
|
return throttled;
|
|
};
|
|
kendo.caret = function (element, start, end) {
|
|
var rangeElement;
|
|
var isPosition = start !== undefined;
|
|
if (end === undefined) {
|
|
end = start;
|
|
}
|
|
if (element[0]) {
|
|
element = element[0];
|
|
}
|
|
if (isPosition && element.disabled) {
|
|
return;
|
|
}
|
|
try {
|
|
if (element.selectionStart !== undefined) {
|
|
if (isPosition) {
|
|
element.focus();
|
|
element.setSelectionRange(start, end);
|
|
} else {
|
|
start = [
|
|
element.selectionStart,
|
|
element.selectionEnd
|
|
];
|
|
}
|
|
} else if (document.selection) {
|
|
if ($(element).is(':visible')) {
|
|
element.focus();
|
|
}
|
|
rangeElement = element.createTextRange();
|
|
if (isPosition) {
|
|
rangeElement.collapse(true);
|
|
rangeElement.moveStart('character', start);
|
|
rangeElement.moveEnd('character', end - start);
|
|
rangeElement.select();
|
|
} else {
|
|
var rangeDuplicated = rangeElement.duplicate(), selectionStart, selectionEnd;
|
|
rangeElement.moveToBookmark(document.selection.createRange().getBookmark());
|
|
rangeDuplicated.setEndPoint('EndToStart', rangeElement);
|
|
selectionStart = rangeDuplicated.text.length;
|
|
selectionEnd = selectionStart + rangeElement.text.length;
|
|
start = [
|
|
selectionStart,
|
|
selectionEnd
|
|
];
|
|
}
|
|
}
|
|
} catch (e) {
|
|
start = [];
|
|
}
|
|
return start;
|
|
};
|
|
kendo.compileMobileDirective = function (element, scope) {
|
|
var angular = window.angular;
|
|
element.attr('data-' + kendo.ns + 'role', element[0].tagName.toLowerCase().replace('kendo-mobile-', '').replace('-', ''));
|
|
angular.element(element).injector().invoke([
|
|
'$compile',
|
|
function ($compile) {
|
|
$compile(element)(scope);
|
|
if (!/^\$(digest|apply)$/.test(scope.$$phase)) {
|
|
scope.$digest();
|
|
}
|
|
}
|
|
]);
|
|
return kendo.widgetInstance(element, kendo.mobile.ui);
|
|
};
|
|
kendo.antiForgeryTokens = function () {
|
|
var tokens = {}, csrf_token = $('meta[name=csrf-token],meta[name=_csrf]').attr('content'), csrf_param = $('meta[name=csrf-param],meta[name=_csrf_header]').attr('content');
|
|
$('input[name^=\'__RequestVerificationToken\']').each(function () {
|
|
tokens[this.name] = this.value;
|
|
});
|
|
if (csrf_param !== undefined && csrf_token !== undefined) {
|
|
tokens[csrf_param] = csrf_token;
|
|
}
|
|
return tokens;
|
|
};
|
|
kendo.cycleForm = function (form) {
|
|
var firstElement = form.find('input, .k-widget').first();
|
|
var lastElement = form.find('button, .k-button').last();
|
|
function focus(el) {
|
|
var widget = kendo.widgetInstance(el);
|
|
if (widget && widget.focus) {
|
|
widget.focus();
|
|
} else {
|
|
el.focus();
|
|
}
|
|
}
|
|
lastElement.on('keydown', function (e) {
|
|
if (e.keyCode == kendo.keys.TAB && !e.shiftKey) {
|
|
e.preventDefault();
|
|
focus(firstElement);
|
|
}
|
|
});
|
|
firstElement.on('keydown', function (e) {
|
|
if (e.keyCode == kendo.keys.TAB && e.shiftKey) {
|
|
e.preventDefault();
|
|
focus(lastElement);
|
|
}
|
|
});
|
|
};
|
|
(function () {
|
|
function postToProxy(dataURI, fileName, proxyURL, proxyTarget) {
|
|
var form = $('<form>').attr({
|
|
action: proxyURL,
|
|
method: 'POST',
|
|
target: proxyTarget
|
|
});
|
|
var data = kendo.antiForgeryTokens();
|
|
data.fileName = fileName;
|
|
var parts = dataURI.split(';base64,');
|
|
data.contentType = parts[0].replace('data:', '');
|
|
data.base64 = parts[1];
|
|
for (var name in data) {
|
|
if (data.hasOwnProperty(name)) {
|
|
$('<input>').attr({
|
|
value: data[name],
|
|
name: name,
|
|
type: 'hidden'
|
|
}).appendTo(form);
|
|
}
|
|
}
|
|
form.appendTo('body').submit().remove();
|
|
}
|
|
var fileSaver = document.createElement('a');
|
|
var downloadAttribute = 'download' in fileSaver && !kendo.support.browser.edge;
|
|
function saveAsBlob(dataURI, fileName) {
|
|
var blob = dataURI;
|
|
if (typeof dataURI == 'string') {
|
|
var parts = dataURI.split(';base64,');
|
|
var contentType = parts[0];
|
|
var base64 = atob(parts[1]);
|
|
var array = new Uint8Array(base64.length);
|
|
for (var idx = 0; idx < base64.length; idx++) {
|
|
array[idx] = base64.charCodeAt(idx);
|
|
}
|
|
blob = new Blob([array.buffer], { type: contentType });
|
|
}
|
|
navigator.msSaveBlob(blob, fileName);
|
|
}
|
|
function saveAsDataURI(dataURI, fileName) {
|
|
if (window.Blob && dataURI instanceof Blob) {
|
|
dataURI = URL.createObjectURL(dataURI);
|
|
}
|
|
fileSaver.download = fileName;
|
|
fileSaver.href = dataURI;
|
|
var e = document.createEvent('MouseEvents');
|
|
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
|
fileSaver.dispatchEvent(e);
|
|
}
|
|
kendo.saveAs = function (options) {
|
|
var save = postToProxy;
|
|
if (!options.forceProxy) {
|
|
if (downloadAttribute) {
|
|
save = saveAsDataURI;
|
|
} else if (navigator.msSaveBlob) {
|
|
save = saveAsBlob;
|
|
}
|
|
}
|
|
save(options.dataURI, options.fileName, options.proxyURL, options.proxyTarget);
|
|
};
|
|
}());
|
|
kendo.proxyModelSetters = function proxyModelSetters(data) {
|
|
var observable = {};
|
|
Object.keys(data || {}).forEach(function (property) {
|
|
Object.defineProperty(observable, property, {
|
|
get: function () {
|
|
return data[property];
|
|
},
|
|
set: function (value) {
|
|
data[property] = value;
|
|
data.dirty = true;
|
|
}
|
|
});
|
|
});
|
|
return observable;
|
|
};
|
|
}(jQuery, window));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.router', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'router',
|
|
name: 'Router',
|
|
category: 'framework',
|
|
description: 'The Router class is responsible for tracking the application state and navigating between the application states.',
|
|
depends: ['core'],
|
|
hidden: false
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, CHANGE = 'change', BACK = 'back', SAME = 'same', support = kendo.support, location = window.location, history = window.history, CHECK_URL_INTERVAL = 50, BROKEN_BACK_NAV = kendo.support.browser.msie, hashStrip = /^#*/, document = window.document;
|
|
function absoluteURL(path, pathPrefix) {
|
|
if (!pathPrefix) {
|
|
return path;
|
|
}
|
|
if (path + '/' === pathPrefix) {
|
|
path = pathPrefix;
|
|
}
|
|
var regEx = new RegExp('^' + pathPrefix, 'i');
|
|
if (!regEx.test(path)) {
|
|
path = pathPrefix + '/' + path;
|
|
}
|
|
return location.protocol + '//' + (location.host + '/' + path).replace(/\/\/+/g, '/');
|
|
}
|
|
function hashDelimiter(bang) {
|
|
return bang ? '#!' : '#';
|
|
}
|
|
function locationHash(hashDelimiter) {
|
|
var href = location.href;
|
|
if (hashDelimiter === '#!' && href.indexOf('#') > -1 && href.indexOf('#!') < 0) {
|
|
return null;
|
|
}
|
|
return href.split(hashDelimiter)[1] || '';
|
|
}
|
|
function stripRoot(root, url) {
|
|
if (url.indexOf(root) === 0) {
|
|
return url.substr(root.length).replace(/\/\//g, '/');
|
|
} else {
|
|
return url;
|
|
}
|
|
}
|
|
var HistoryAdapter = kendo.Class.extend({
|
|
back: function () {
|
|
if (BROKEN_BACK_NAV) {
|
|
setTimeout(function () {
|
|
history.back();
|
|
});
|
|
} else {
|
|
history.back();
|
|
}
|
|
},
|
|
forward: function () {
|
|
if (BROKEN_BACK_NAV) {
|
|
setTimeout(function () {
|
|
history.forward();
|
|
});
|
|
} else {
|
|
history.forward();
|
|
}
|
|
},
|
|
length: function () {
|
|
return history.length;
|
|
},
|
|
replaceLocation: function (url) {
|
|
location.replace(url);
|
|
}
|
|
});
|
|
var PushStateAdapter = HistoryAdapter.extend({
|
|
init: function (root) {
|
|
this.root = root;
|
|
},
|
|
navigate: function (to) {
|
|
history.pushState({}, document.title, absoluteURL(to, this.root));
|
|
},
|
|
replace: function (to) {
|
|
history.replaceState({}, document.title, absoluteURL(to, this.root));
|
|
},
|
|
normalize: function (url) {
|
|
return stripRoot(this.root, url);
|
|
},
|
|
current: function () {
|
|
var current = location.pathname;
|
|
if (location.search) {
|
|
current += location.search;
|
|
}
|
|
return stripRoot(this.root, current);
|
|
},
|
|
change: function (callback) {
|
|
$(window).bind('popstate.kendo', callback);
|
|
},
|
|
stop: function () {
|
|
$(window).unbind('popstate.kendo');
|
|
},
|
|
normalizeCurrent: function (options) {
|
|
var fixedUrl, root = options.root, pathname = location.pathname, hash = locationHash(hashDelimiter(options.hashBang));
|
|
if (root === pathname + '/') {
|
|
fixedUrl = root;
|
|
}
|
|
if (root === pathname && hash) {
|
|
fixedUrl = absoluteURL(hash.replace(hashStrip, ''), root);
|
|
}
|
|
if (fixedUrl) {
|
|
history.pushState({}, document.title, fixedUrl);
|
|
}
|
|
}
|
|
});
|
|
function fixHash(url) {
|
|
return url.replace(/^(#)?/, '#');
|
|
}
|
|
function fixBang(url) {
|
|
return url.replace(/^(#(!)?)?/, '#!');
|
|
}
|
|
var HashAdapter = HistoryAdapter.extend({
|
|
init: function (bang) {
|
|
this._id = kendo.guid();
|
|
this.prefix = hashDelimiter(bang);
|
|
this.fix = bang ? fixBang : fixHash;
|
|
},
|
|
navigate: function (to) {
|
|
location.hash = this.fix(to);
|
|
},
|
|
replace: function (to) {
|
|
this.replaceLocation(this.fix(to));
|
|
},
|
|
normalize: function (url) {
|
|
if (url.indexOf(this.prefix) < 0) {
|
|
return url;
|
|
} else {
|
|
return url.split(this.prefix)[1];
|
|
}
|
|
},
|
|
change: function (callback) {
|
|
if (support.hashChange) {
|
|
$(window).on('hashchange.' + this._id, callback);
|
|
} else {
|
|
this._interval = setInterval(callback, CHECK_URL_INTERVAL);
|
|
}
|
|
},
|
|
stop: function () {
|
|
$(window).off('hashchange.' + this._id);
|
|
clearInterval(this._interval);
|
|
},
|
|
current: function () {
|
|
return locationHash(this.prefix);
|
|
},
|
|
normalizeCurrent: function (options) {
|
|
var pathname = location.pathname, root = options.root;
|
|
if (options.pushState && root !== pathname) {
|
|
this.replaceLocation(root + this.prefix + stripRoot(root, pathname));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var History = kendo.Observable.extend({
|
|
start: function (options) {
|
|
options = options || {};
|
|
this.bind([
|
|
CHANGE,
|
|
BACK,
|
|
SAME
|
|
], options);
|
|
if (this._started) {
|
|
return;
|
|
}
|
|
this._started = true;
|
|
options.root = options.root || '/';
|
|
var adapter = this.createAdapter(options), current;
|
|
if (adapter.normalizeCurrent(options)) {
|
|
return;
|
|
}
|
|
current = adapter.current();
|
|
$.extend(this, {
|
|
adapter: adapter,
|
|
root: options.root,
|
|
historyLength: adapter.length(),
|
|
current: current,
|
|
locations: [current]
|
|
});
|
|
adapter.change($.proxy(this, '_checkUrl'));
|
|
},
|
|
createAdapter: function (options) {
|
|
return support.pushState && options.pushState ? new PushStateAdapter(options.root) : new HashAdapter(options.hashBang);
|
|
},
|
|
stop: function () {
|
|
if (!this._started) {
|
|
return;
|
|
}
|
|
this.adapter.stop();
|
|
this.unbind(CHANGE);
|
|
this._started = false;
|
|
},
|
|
change: function (callback) {
|
|
this.bind(CHANGE, callback);
|
|
},
|
|
replace: function (to, silent) {
|
|
this._navigate(to, silent, function (adapter) {
|
|
adapter.replace(to);
|
|
this.locations[this.locations.length - 1] = this.current;
|
|
});
|
|
},
|
|
navigate: function (to, silent) {
|
|
if (to === '#:back') {
|
|
this.backCalled = true;
|
|
this.adapter.back();
|
|
return;
|
|
}
|
|
this._navigate(to, silent, function (adapter) {
|
|
adapter.navigate(to);
|
|
this.locations.push(this.current);
|
|
});
|
|
},
|
|
_navigate: function (to, silent, callback) {
|
|
var adapter = this.adapter;
|
|
to = adapter.normalize(to);
|
|
if (this.current === to || this.current === decodeURIComponent(to)) {
|
|
this.trigger(SAME);
|
|
return;
|
|
}
|
|
if (!silent) {
|
|
if (this.trigger(CHANGE, { url: to })) {
|
|
return;
|
|
}
|
|
}
|
|
this.current = to;
|
|
callback.call(this, adapter);
|
|
this.historyLength = adapter.length();
|
|
},
|
|
_checkUrl: function () {
|
|
var adapter = this.adapter, current = adapter.current(), newLength = adapter.length(), navigatingInExisting = this.historyLength === newLength, back = current === this.locations[this.locations.length - 2] && navigatingInExisting, backCalled = this.backCalled, prev = this.current;
|
|
if (current === null || this.current === current || this.current === decodeURIComponent(current)) {
|
|
return true;
|
|
}
|
|
this.historyLength = newLength;
|
|
this.backCalled = false;
|
|
this.current = current;
|
|
if (back && this.trigger('back', {
|
|
url: prev,
|
|
to: current
|
|
})) {
|
|
adapter.forward();
|
|
this.current = prev;
|
|
return;
|
|
}
|
|
if (this.trigger(CHANGE, {
|
|
url: current,
|
|
backButtonPressed: !backCalled
|
|
})) {
|
|
if (back) {
|
|
adapter.forward();
|
|
} else {
|
|
adapter.back();
|
|
this.historyLength--;
|
|
}
|
|
this.current = prev;
|
|
return;
|
|
}
|
|
if (back) {
|
|
this.locations.pop();
|
|
} else {
|
|
this.locations.push(current);
|
|
}
|
|
}
|
|
});
|
|
kendo.History = History;
|
|
kendo.History.HistoryAdapter = HistoryAdapter;
|
|
kendo.History.HashAdapter = HashAdapter;
|
|
kendo.History.PushStateAdapter = PushStateAdapter;
|
|
kendo.absoluteURL = absoluteURL;
|
|
kendo.history = new History();
|
|
}(window.kendo.jQuery));
|
|
(function () {
|
|
var kendo = window.kendo, history = kendo.history, Observable = kendo.Observable, INIT = 'init', ROUTE_MISSING = 'routeMissing', CHANGE = 'change', BACK = 'back', SAME = 'same', optionalParam = /\((.*?)\)/g, namedParam = /(\(\?)?:\w+/g, splatParam = /\*\w+/g, escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
|
|
function namedParamReplace(match, optional) {
|
|
return optional ? match : '([^/]+)';
|
|
}
|
|
function routeToRegExp(route, ignoreCase) {
|
|
return new RegExp('^' + route.replace(escapeRegExp, '\\$&').replace(optionalParam, '(?:$1)?').replace(namedParam, namedParamReplace).replace(splatParam, '(.*?)') + '$', ignoreCase ? 'i' : '');
|
|
}
|
|
function stripUrl(url) {
|
|
return url.replace(/(\?.*)|(#.*)/g, '');
|
|
}
|
|
var Route = kendo.Class.extend({
|
|
init: function (route, callback, ignoreCase) {
|
|
if (!(route instanceof RegExp)) {
|
|
route = routeToRegExp(route, ignoreCase);
|
|
}
|
|
this.route = route;
|
|
this._callback = callback;
|
|
},
|
|
callback: function (url, back) {
|
|
var params, idx = 0, length, queryStringParams = kendo.parseQueryStringParams(url);
|
|
queryStringParams._back = back;
|
|
url = stripUrl(url);
|
|
params = this.route.exec(url).slice(1);
|
|
length = params.length;
|
|
for (; idx < length; idx++) {
|
|
if (typeof params[idx] !== 'undefined') {
|
|
params[idx] = decodeURIComponent(params[idx]);
|
|
}
|
|
}
|
|
params.push(queryStringParams);
|
|
this._callback.apply(null, params);
|
|
},
|
|
worksWith: function (url, back) {
|
|
if (this.route.test(stripUrl(url))) {
|
|
this.callback(url, back);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
var Router = Observable.extend({
|
|
init: function (options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
Observable.fn.init.call(this);
|
|
this.routes = [];
|
|
this.pushState = options.pushState;
|
|
this.hashBang = options.hashBang;
|
|
this.root = options.root;
|
|
this.ignoreCase = options.ignoreCase !== false;
|
|
this.bind([
|
|
INIT,
|
|
ROUTE_MISSING,
|
|
CHANGE,
|
|
SAME
|
|
], options);
|
|
},
|
|
destroy: function () {
|
|
history.unbind(CHANGE, this._urlChangedProxy);
|
|
history.unbind(SAME, this._sameProxy);
|
|
history.unbind(BACK, this._backProxy);
|
|
this.unbind();
|
|
},
|
|
start: function () {
|
|
var that = this, sameProxy = function () {
|
|
that._same();
|
|
}, backProxy = function (e) {
|
|
that._back(e);
|
|
}, urlChangedProxy = function (e) {
|
|
that._urlChanged(e);
|
|
};
|
|
history.start({
|
|
same: sameProxy,
|
|
change: urlChangedProxy,
|
|
back: backProxy,
|
|
pushState: that.pushState,
|
|
hashBang: that.hashBang,
|
|
root: that.root
|
|
});
|
|
var initEventObject = {
|
|
url: history.current || '/',
|
|
preventDefault: $.noop
|
|
};
|
|
if (!that.trigger(INIT, initEventObject)) {
|
|
that._urlChanged(initEventObject);
|
|
}
|
|
this._urlChangedProxy = urlChangedProxy;
|
|
this._backProxy = backProxy;
|
|
},
|
|
route: function (route, callback) {
|
|
this.routes.push(new Route(route, callback, this.ignoreCase));
|
|
},
|
|
navigate: function (url, silent) {
|
|
kendo.history.navigate(url, silent);
|
|
},
|
|
replace: function (url, silent) {
|
|
kendo.history.replace(url, silent);
|
|
},
|
|
_back: function (e) {
|
|
if (this.trigger(BACK, {
|
|
url: e.url,
|
|
to: e.to
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_same: function () {
|
|
this.trigger(SAME);
|
|
},
|
|
_urlChanged: function (e) {
|
|
var url = e.url;
|
|
var back = e.backButtonPressed;
|
|
if (!url) {
|
|
url = '/';
|
|
}
|
|
if (this.trigger(CHANGE, {
|
|
url: e.url,
|
|
params: kendo.parseQueryStringParams(e.url),
|
|
backButtonPressed: back
|
|
})) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
var idx = 0, routes = this.routes, route, length = routes.length;
|
|
for (; idx < length; idx++) {
|
|
route = routes[idx];
|
|
if (route.worksWith(url, back)) {
|
|
return;
|
|
}
|
|
}
|
|
if (this.trigger(ROUTE_MISSING, {
|
|
url: url,
|
|
params: kendo.parseQueryStringParams(url),
|
|
backButtonPressed: back
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
kendo.Router = Router;
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.data.odata', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'data.odata',
|
|
name: 'OData',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, odataFilters = {
|
|
eq: 'eq',
|
|
neq: 'ne',
|
|
gt: 'gt',
|
|
gte: 'ge',
|
|
lt: 'lt',
|
|
lte: 'le',
|
|
contains: 'substringof',
|
|
doesnotcontain: 'substringof',
|
|
endswith: 'endswith',
|
|
startswith: 'startswith',
|
|
isnull: 'eq',
|
|
isnotnull: 'ne',
|
|
isempty: 'eq',
|
|
isnotempty: 'ne'
|
|
}, odataFiltersVersionFour = extend({}, odataFilters, { contains: 'contains' }), mappers = {
|
|
pageSize: $.noop,
|
|
page: $.noop,
|
|
filter: function (params, filter, useVersionFour) {
|
|
if (filter) {
|
|
filter = toOdataFilter(filter, useVersionFour);
|
|
if (filter) {
|
|
params.$filter = filter;
|
|
}
|
|
}
|
|
},
|
|
sort: function (params, orderby) {
|
|
var expr = $.map(orderby, function (value) {
|
|
var order = value.field.replace(/\./g, '/');
|
|
if (value.dir === 'desc') {
|
|
order += ' desc';
|
|
}
|
|
return order;
|
|
}).join(',');
|
|
if (expr) {
|
|
params.$orderby = expr;
|
|
}
|
|
},
|
|
skip: function (params, skip) {
|
|
if (skip) {
|
|
params.$skip = skip;
|
|
}
|
|
},
|
|
take: function (params, take) {
|
|
if (take) {
|
|
params.$top = take;
|
|
}
|
|
}
|
|
}, defaultDataType = { read: { dataType: 'jsonp' } };
|
|
function toOdataFilter(filter, useOdataFour) {
|
|
var result = [], logic = filter.logic || 'and', idx, length, field, type, format, operator, value, ignoreCase, filters = filter.filters;
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
field = filter.field;
|
|
value = filter.value;
|
|
operator = filter.operator;
|
|
if (filter.filters) {
|
|
filter = toOdataFilter(filter, useOdataFour);
|
|
} else {
|
|
ignoreCase = filter.ignoreCase;
|
|
field = field.replace(/\./g, '/');
|
|
filter = odataFilters[operator];
|
|
if (useOdataFour) {
|
|
filter = odataFiltersVersionFour[operator];
|
|
}
|
|
if (operator === 'isnull' || operator === 'isnotnull') {
|
|
filter = kendo.format('{0} {1} null', field, filter);
|
|
} else if (operator === 'isempty' || operator === 'isnotempty') {
|
|
filter = kendo.format('{0} {1} \'\'', field, filter);
|
|
} else if (filter && value !== undefined) {
|
|
type = $.type(value);
|
|
if (type === 'string') {
|
|
format = '\'{1}\'';
|
|
value = value.replace(/'/g, '\'\'');
|
|
if (ignoreCase === true) {
|
|
field = 'tolower(' + field + ')';
|
|
}
|
|
} else if (type === 'date') {
|
|
if (useOdataFour) {
|
|
format = '{1:yyyy-MM-ddTHH:mm:ss+00:00}';
|
|
} else {
|
|
format = 'datetime\'{1:yyyy-MM-ddTHH:mm:ss}\'';
|
|
}
|
|
} else {
|
|
format = '{1}';
|
|
}
|
|
if (filter.length > 3) {
|
|
if (filter !== 'substringof') {
|
|
format = '{0}({2},' + format + ')';
|
|
} else {
|
|
format = '{0}(' + format + ',{2})';
|
|
if (operator === 'doesnotcontain') {
|
|
if (useOdataFour) {
|
|
format = '{0}({2},\'{1}\') eq -1';
|
|
filter = 'indexof';
|
|
} else {
|
|
format += ' eq false';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
format = '{2} {0} ' + format;
|
|
}
|
|
filter = kendo.format(format, filter, value, field);
|
|
}
|
|
}
|
|
result.push(filter);
|
|
}
|
|
filter = result.join(' ' + logic + ' ');
|
|
if (result.length > 1) {
|
|
filter = '(' + filter + ')';
|
|
}
|
|
return filter;
|
|
}
|
|
function stripMetadata(obj) {
|
|
for (var name in obj) {
|
|
if (name.indexOf('@odata') === 0) {
|
|
delete obj[name];
|
|
}
|
|
}
|
|
}
|
|
extend(true, kendo.data, {
|
|
schemas: {
|
|
odata: {
|
|
type: 'json',
|
|
data: function (data) {
|
|
return data.d.results || [data.d];
|
|
},
|
|
total: 'd.__count'
|
|
}
|
|
},
|
|
transports: {
|
|
odata: {
|
|
read: {
|
|
cache: true,
|
|
dataType: 'jsonp',
|
|
jsonp: '$callback'
|
|
},
|
|
update: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
contentType: 'application/json',
|
|
type: 'PUT'
|
|
},
|
|
create: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
contentType: 'application/json',
|
|
type: 'POST'
|
|
},
|
|
destroy: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
type: 'DELETE'
|
|
},
|
|
parameterMap: function (options, type, useVersionFour) {
|
|
var params, value, option, dataType;
|
|
options = options || {};
|
|
type = type || 'read';
|
|
dataType = (this.options || defaultDataType)[type];
|
|
dataType = dataType ? dataType.dataType : 'json';
|
|
if (type === 'read') {
|
|
params = { $inlinecount: 'allpages' };
|
|
if (dataType != 'json') {
|
|
params.$format = 'json';
|
|
}
|
|
for (option in options) {
|
|
if (mappers[option]) {
|
|
mappers[option](params, options[option], useVersionFour);
|
|
} else {
|
|
params[option] = options[option];
|
|
}
|
|
}
|
|
} else {
|
|
if (dataType !== 'json') {
|
|
throw new Error('Only json dataType can be used for ' + type + ' operation.');
|
|
}
|
|
if (type !== 'destroy') {
|
|
for (option in options) {
|
|
value = options[option];
|
|
if (typeof value === 'number') {
|
|
options[option] = value + '';
|
|
}
|
|
}
|
|
params = kendo.stringify(options);
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
extend(true, kendo.data, {
|
|
schemas: {
|
|
'odata-v4': {
|
|
type: 'json',
|
|
data: function (data) {
|
|
data = $.extend({}, data);
|
|
stripMetadata(data);
|
|
if (data.value) {
|
|
return data.value;
|
|
}
|
|
return [data];
|
|
},
|
|
total: function (data) {
|
|
return data['@odata.count'];
|
|
}
|
|
}
|
|
},
|
|
transports: {
|
|
'odata-v4': {
|
|
read: {
|
|
cache: true,
|
|
dataType: 'json'
|
|
},
|
|
update: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
contentType: 'application/json;IEEE754Compatible=true',
|
|
type: 'PUT'
|
|
},
|
|
create: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
contentType: 'application/json;IEEE754Compatible=true',
|
|
type: 'POST'
|
|
},
|
|
destroy: {
|
|
cache: true,
|
|
dataType: 'json',
|
|
type: 'DELETE'
|
|
},
|
|
parameterMap: function (options, type) {
|
|
var result = kendo.data.transports.odata.parameterMap(options, type, true);
|
|
if (type == 'read') {
|
|
result.$count = true;
|
|
delete result.$inlinecount;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.data.xml', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'data.xml',
|
|
name: 'XML',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, isArray = $.isArray, isPlainObject = $.isPlainObject, map = $.map, each = $.each, extend = $.extend, getter = kendo.getter, Class = kendo.Class;
|
|
var XmlDataReader = Class.extend({
|
|
init: function (options) {
|
|
var that = this, total = options.total, model = options.model, parse = options.parse, errors = options.errors, serialize = options.serialize, data = options.data;
|
|
if (model) {
|
|
if (isPlainObject(model)) {
|
|
var base = options.modelBase || kendo.data.Model;
|
|
if (model.fields) {
|
|
each(model.fields, function (field, value) {
|
|
if (isPlainObject(value) && value.field) {
|
|
if (!$.isFunction(value.field)) {
|
|
value = extend(value, { field: that.getter(value.field) });
|
|
}
|
|
} else {
|
|
value = { field: that.getter(value) };
|
|
}
|
|
model.fields[field] = value;
|
|
});
|
|
}
|
|
var id = model.id;
|
|
if (id) {
|
|
var idField = {};
|
|
idField[that.xpathToMember(id, true)] = { field: that.getter(id) };
|
|
model.fields = extend(idField, model.fields);
|
|
model.id = that.xpathToMember(id);
|
|
}
|
|
model = base.define(model);
|
|
}
|
|
that.model = model;
|
|
}
|
|
if (total) {
|
|
if (typeof total == 'string') {
|
|
total = that.getter(total);
|
|
that.total = function (data) {
|
|
return parseInt(total(data), 10);
|
|
};
|
|
} else if (typeof total == 'function') {
|
|
that.total = total;
|
|
}
|
|
}
|
|
if (errors) {
|
|
if (typeof errors == 'string') {
|
|
errors = that.getter(errors);
|
|
that.errors = function (data) {
|
|
return errors(data) || null;
|
|
};
|
|
} else if (typeof errors == 'function') {
|
|
that.errors = errors;
|
|
}
|
|
}
|
|
if (data) {
|
|
if (typeof data == 'string') {
|
|
data = that.xpathToMember(data);
|
|
that.data = function (value) {
|
|
var result = that.evaluate(value, data), modelInstance;
|
|
result = isArray(result) ? result : [result];
|
|
if (that.model && model.fields) {
|
|
modelInstance = new that.model();
|
|
return map(result, function (value) {
|
|
if (value) {
|
|
var record = {}, field;
|
|
for (field in model.fields) {
|
|
record[field] = modelInstance._parse(field, model.fields[field].field(value));
|
|
}
|
|
return record;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
};
|
|
} else if (typeof data == 'function') {
|
|
that.data = data;
|
|
}
|
|
}
|
|
if (typeof parse == 'function') {
|
|
var xmlParse = that.parse;
|
|
that.parse = function (data) {
|
|
var xml = parse.call(that, data);
|
|
return xmlParse.call(that, xml);
|
|
};
|
|
}
|
|
if (typeof serialize == 'function') {
|
|
that.serialize = serialize;
|
|
}
|
|
},
|
|
total: function (result) {
|
|
return this.data(result).length;
|
|
},
|
|
errors: function (data) {
|
|
return data ? data.errors : null;
|
|
},
|
|
serialize: function (data) {
|
|
return data;
|
|
},
|
|
parseDOM: function (element) {
|
|
var result = {}, parsedNode, node, nodeType, nodeName, member, attribute, attributes = element.attributes, attributeCount = attributes.length, idx;
|
|
for (idx = 0; idx < attributeCount; idx++) {
|
|
attribute = attributes[idx];
|
|
result['@' + attribute.nodeName] = attribute.nodeValue;
|
|
}
|
|
for (node = element.firstChild; node; node = node.nextSibling) {
|
|
nodeType = node.nodeType;
|
|
if (nodeType === 3 || nodeType === 4) {
|
|
result['#text'] = node.nodeValue;
|
|
} else if (nodeType === 1) {
|
|
parsedNode = this.parseDOM(node);
|
|
nodeName = node.nodeName;
|
|
member = result[nodeName];
|
|
if (isArray(member)) {
|
|
member.push(parsedNode);
|
|
} else if (member !== undefined) {
|
|
member = [
|
|
member,
|
|
parsedNode
|
|
];
|
|
} else {
|
|
member = parsedNode;
|
|
}
|
|
result[nodeName] = member;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
evaluate: function (value, expression) {
|
|
var members = expression.split('.'), member, result, length, intermediateResult, idx;
|
|
while (member = members.shift()) {
|
|
value = value[member];
|
|
if (isArray(value)) {
|
|
result = [];
|
|
expression = members.join('.');
|
|
for (idx = 0, length = value.length; idx < length; idx++) {
|
|
intermediateResult = this.evaluate(value[idx], expression);
|
|
intermediateResult = isArray(intermediateResult) ? intermediateResult : [intermediateResult];
|
|
result.push.apply(result, intermediateResult);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
parse: function (xml) {
|
|
var documentElement, tree, result = {};
|
|
documentElement = xml.documentElement || $.parseXML(xml).documentElement;
|
|
tree = this.parseDOM(documentElement);
|
|
result[documentElement.nodeName] = tree;
|
|
return result;
|
|
},
|
|
xpathToMember: function (member, raw) {
|
|
if (!member) {
|
|
return '';
|
|
}
|
|
member = member.replace(/^\//, '').replace(/\//g, '.');
|
|
if (member.indexOf('@') >= 0) {
|
|
return member.replace(/\.?(@.*)/, raw ? '$1' : '["$1"]');
|
|
}
|
|
if (member.indexOf('text()') >= 0) {
|
|
return member.replace(/(\.?text\(\))/, raw ? '#text' : '["#text"]');
|
|
}
|
|
return member;
|
|
},
|
|
getter: function (member) {
|
|
return getter(this.xpathToMember(member), true);
|
|
}
|
|
});
|
|
$.extend(true, kendo.data, {
|
|
XmlDataReader: XmlDataReader,
|
|
readers: { xml: XmlDataReader }
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.data', [
|
|
'kendo.core',
|
|
'kendo.data.odata',
|
|
'kendo.data.xml'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'data',
|
|
name: 'Data source',
|
|
category: 'framework',
|
|
description: 'Powerful component for using local and remote data.Fully supports CRUD, Sorting, Paging, Filtering, Grouping, and Aggregates.',
|
|
depends: ['core'],
|
|
features: [
|
|
{
|
|
id: 'data-odata',
|
|
name: 'OData',
|
|
description: 'Support for accessing Open Data Protocol (OData) services.',
|
|
depends: ['data.odata']
|
|
},
|
|
{
|
|
id: 'data-signalr',
|
|
name: 'SignalR',
|
|
description: 'Support for binding to SignalR hubs.',
|
|
depends: ['data.signalr']
|
|
},
|
|
{
|
|
id: 'data-XML',
|
|
name: 'XML',
|
|
description: 'Support for binding to XML.',
|
|
depends: ['data.xml']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var extend = $.extend, proxy = $.proxy, isPlainObject = $.isPlainObject, isEmptyObject = $.isEmptyObject, isArray = $.isArray, grep = $.grep, ajax = $.ajax, map, each = $.each, noop = $.noop, kendo = window.kendo, isFunction = kendo.isFunction, Observable = kendo.Observable, Class = kendo.Class, STRING = 'string', FUNCTION = 'function', CREATE = 'create', READ = 'read', UPDATE = 'update', DESTROY = 'destroy', CHANGE = 'change', SYNC = 'sync', GET = 'get', ERROR = 'error', REQUESTSTART = 'requestStart', PROGRESS = 'progress', REQUESTEND = 'requestEnd', crud = [
|
|
CREATE,
|
|
READ,
|
|
UPDATE,
|
|
DESTROY
|
|
], identity = function (o) {
|
|
return o;
|
|
}, getter = kendo.getter, stringify = kendo.stringify, math = Math, push = [].push, join = [].join, pop = [].pop, splice = [].splice, shift = [].shift, slice = [].slice, unshift = [].unshift, toString = {}.toString, stableSort = kendo.support.stableSort, dateRegExp = /^\/Date\((.*?)\)\/$/, newLineRegExp = /(\r+|\n+)/g, quoteRegExp = /(?=['\\])/g;
|
|
var ObservableArray = Observable.extend({
|
|
init: function (array, type) {
|
|
var that = this;
|
|
that.type = type || ObservableObject;
|
|
Observable.fn.init.call(that);
|
|
that.length = array.length;
|
|
that.wrapAll(array, that);
|
|
},
|
|
at: function (index) {
|
|
return this[index];
|
|
},
|
|
toJSON: function () {
|
|
var idx, length = this.length, value, json = new Array(length);
|
|
for (idx = 0; idx < length; idx++) {
|
|
value = this[idx];
|
|
if (value instanceof ObservableObject) {
|
|
value = value.toJSON();
|
|
}
|
|
json[idx] = value;
|
|
}
|
|
return json;
|
|
},
|
|
parent: noop,
|
|
wrapAll: function (source, target) {
|
|
var that = this, idx, length, parent = function () {
|
|
return that;
|
|
};
|
|
target = target || [];
|
|
for (idx = 0, length = source.length; idx < length; idx++) {
|
|
target[idx] = that.wrap(source[idx], parent);
|
|
}
|
|
return target;
|
|
},
|
|
wrap: function (object, parent) {
|
|
var that = this, observable;
|
|
if (object !== null && toString.call(object) === '[object Object]') {
|
|
observable = object instanceof that.type || object instanceof Model;
|
|
if (!observable) {
|
|
object = object instanceof ObservableObject ? object.toJSON() : object;
|
|
object = new that.type(object);
|
|
}
|
|
object.parent = parent;
|
|
object.bind(CHANGE, function (e) {
|
|
that.trigger(CHANGE, {
|
|
field: e.field,
|
|
node: e.node,
|
|
index: e.index,
|
|
items: e.items || [this],
|
|
action: e.node ? e.action || 'itemloaded' : 'itemchange'
|
|
});
|
|
});
|
|
}
|
|
return object;
|
|
},
|
|
push: function () {
|
|
var index = this.length, items = this.wrapAll(arguments), result;
|
|
result = push.apply(this, items);
|
|
this.trigger(CHANGE, {
|
|
action: 'add',
|
|
index: index,
|
|
items: items
|
|
});
|
|
return result;
|
|
},
|
|
slice: slice,
|
|
sort: [].sort,
|
|
join: join,
|
|
pop: function () {
|
|
var length = this.length, result = pop.apply(this);
|
|
if (length) {
|
|
this.trigger(CHANGE, {
|
|
action: 'remove',
|
|
index: length - 1,
|
|
items: [result]
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
splice: function (index, howMany, item) {
|
|
var items = this.wrapAll(slice.call(arguments, 2)), result, i, len;
|
|
result = splice.apply(this, [
|
|
index,
|
|
howMany
|
|
].concat(items));
|
|
if (result.length) {
|
|
this.trigger(CHANGE, {
|
|
action: 'remove',
|
|
index: index,
|
|
items: result
|
|
});
|
|
for (i = 0, len = result.length; i < len; i++) {
|
|
if (result[i] && result[i].children) {
|
|
result[i].unbind(CHANGE);
|
|
}
|
|
}
|
|
}
|
|
if (item) {
|
|
this.trigger(CHANGE, {
|
|
action: 'add',
|
|
index: index,
|
|
items: items
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
shift: function () {
|
|
var length = this.length, result = shift.apply(this);
|
|
if (length) {
|
|
this.trigger(CHANGE, {
|
|
action: 'remove',
|
|
index: 0,
|
|
items: [result]
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
unshift: function () {
|
|
var items = this.wrapAll(arguments), result;
|
|
result = unshift.apply(this, items);
|
|
this.trigger(CHANGE, {
|
|
action: 'add',
|
|
index: 0,
|
|
items: items
|
|
});
|
|
return result;
|
|
},
|
|
indexOf: function (item) {
|
|
var that = this, idx, length;
|
|
for (idx = 0, length = that.length; idx < length; idx++) {
|
|
if (that[idx] === item) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
forEach: function (callback) {
|
|
var idx = 0, length = this.length;
|
|
for (; idx < length; idx++) {
|
|
callback(this[idx], idx, this);
|
|
}
|
|
},
|
|
map: function (callback) {
|
|
var idx = 0, result = [], length = this.length;
|
|
for (; idx < length; idx++) {
|
|
result[idx] = callback(this[idx], idx, this);
|
|
}
|
|
return result;
|
|
},
|
|
reduce: function (callback) {
|
|
var idx = 0, result, length = this.length;
|
|
if (arguments.length == 2) {
|
|
result = arguments[1];
|
|
} else if (idx < length) {
|
|
result = this[idx++];
|
|
}
|
|
for (; idx < length; idx++) {
|
|
result = callback(result, this[idx], idx, this);
|
|
}
|
|
return result;
|
|
},
|
|
reduceRight: function (callback) {
|
|
var idx = this.length - 1, result;
|
|
if (arguments.length == 2) {
|
|
result = arguments[1];
|
|
} else if (idx > 0) {
|
|
result = this[idx--];
|
|
}
|
|
for (; idx >= 0; idx--) {
|
|
result = callback(result, this[idx], idx, this);
|
|
}
|
|
return result;
|
|
},
|
|
filter: function (callback) {
|
|
var idx = 0, result = [], item, length = this.length;
|
|
for (; idx < length; idx++) {
|
|
item = this[idx];
|
|
if (callback(item, idx, this)) {
|
|
result[result.length] = item;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
find: function (callback) {
|
|
var idx = 0, item, length = this.length;
|
|
for (; idx < length; idx++) {
|
|
item = this[idx];
|
|
if (callback(item, idx, this)) {
|
|
return item;
|
|
}
|
|
}
|
|
},
|
|
every: function (callback) {
|
|
var idx = 0, item, length = this.length;
|
|
for (; idx < length; idx++) {
|
|
item = this[idx];
|
|
if (!callback(item, idx, this)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
some: function (callback) {
|
|
var idx = 0, item, length = this.length;
|
|
for (; idx < length; idx++) {
|
|
item = this[idx];
|
|
if (callback(item, idx, this)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
remove: function (item) {
|
|
var idx = this.indexOf(item);
|
|
if (idx !== -1) {
|
|
this.splice(idx, 1);
|
|
}
|
|
},
|
|
empty: function () {
|
|
this.splice(0, this.length);
|
|
}
|
|
});
|
|
var LazyObservableArray = ObservableArray.extend({
|
|
init: function (data, type) {
|
|
Observable.fn.init.call(this);
|
|
this.type = type || ObservableObject;
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
this[idx] = data[idx];
|
|
}
|
|
this.length = idx;
|
|
this._parent = proxy(function () {
|
|
return this;
|
|
}, this);
|
|
},
|
|
at: function (index) {
|
|
var item = this[index];
|
|
if (!(item instanceof this.type)) {
|
|
item = this[index] = this.wrap(item, this._parent);
|
|
} else {
|
|
item.parent = this._parent;
|
|
}
|
|
return item;
|
|
}
|
|
});
|
|
function eventHandler(context, type, field, prefix) {
|
|
return function (e) {
|
|
var event = {}, key;
|
|
for (key in e) {
|
|
event[key] = e[key];
|
|
}
|
|
if (prefix) {
|
|
event.field = field + '.' + e.field;
|
|
} else {
|
|
event.field = field;
|
|
}
|
|
if (type == CHANGE && context._notifyChange) {
|
|
context._notifyChange(event);
|
|
}
|
|
context.trigger(type, event);
|
|
};
|
|
}
|
|
var ObservableObject = Observable.extend({
|
|
init: function (value) {
|
|
var that = this, member, field, parent = function () {
|
|
return that;
|
|
};
|
|
Observable.fn.init.call(this);
|
|
this._handlers = {};
|
|
for (field in value) {
|
|
member = value[field];
|
|
if (typeof member === 'object' && member && !member.getTime && field.charAt(0) != '_') {
|
|
member = that.wrap(member, field, parent);
|
|
}
|
|
that[field] = member;
|
|
}
|
|
that.uid = kendo.guid();
|
|
},
|
|
shouldSerialize: function (field) {
|
|
return this.hasOwnProperty(field) && field !== '_handlers' && field !== '_events' && typeof this[field] !== FUNCTION && field !== 'uid';
|
|
},
|
|
forEach: function (f) {
|
|
for (var i in this) {
|
|
if (this.shouldSerialize(i)) {
|
|
f(this[i], i);
|
|
}
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
var result = {}, value, field;
|
|
for (field in this) {
|
|
if (this.shouldSerialize(field)) {
|
|
value = this[field];
|
|
if (value instanceof ObservableObject || value instanceof ObservableArray) {
|
|
value = value.toJSON();
|
|
}
|
|
result[field] = value;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
get: function (field) {
|
|
var that = this, result;
|
|
that.trigger(GET, { field: field });
|
|
if (field === 'this') {
|
|
result = that;
|
|
} else {
|
|
result = kendo.getter(field, true)(that);
|
|
}
|
|
return result;
|
|
},
|
|
_set: function (field, value) {
|
|
var that = this;
|
|
var composite = field.indexOf('.') >= 0;
|
|
if (composite) {
|
|
var paths = field.split('.'), path = '';
|
|
while (paths.length > 1) {
|
|
path += paths.shift();
|
|
var obj = kendo.getter(path, true)(that);
|
|
if (obj instanceof ObservableObject) {
|
|
obj.set(paths.join('.'), value);
|
|
return composite;
|
|
}
|
|
path += '.';
|
|
}
|
|
}
|
|
kendo.setter(field)(that, value);
|
|
return composite;
|
|
},
|
|
set: function (field, value) {
|
|
var that = this, isSetPrevented = false, composite = field.indexOf('.') >= 0, current = kendo.getter(field, true)(that);
|
|
if (current !== value) {
|
|
if (current instanceof Observable && this._handlers[field]) {
|
|
if (this._handlers[field].get) {
|
|
current.unbind(GET, this._handlers[field].get);
|
|
}
|
|
current.unbind(CHANGE, this._handlers[field].change);
|
|
}
|
|
isSetPrevented = that.trigger('set', {
|
|
field: field,
|
|
value: value
|
|
});
|
|
if (!isSetPrevented) {
|
|
if (!composite) {
|
|
value = that.wrap(value, field, function () {
|
|
return that;
|
|
});
|
|
}
|
|
if (!that._set(field, value) || field.indexOf('(') >= 0 || field.indexOf('[') >= 0) {
|
|
that.trigger(CHANGE, { field: field });
|
|
}
|
|
}
|
|
}
|
|
return isSetPrevented;
|
|
},
|
|
parent: noop,
|
|
wrap: function (object, field, parent) {
|
|
var that = this;
|
|
var get;
|
|
var change;
|
|
var type = toString.call(object);
|
|
if (object != null && (type === '[object Object]' || type === '[object Array]')) {
|
|
var isObservableArray = object instanceof ObservableArray;
|
|
var isDataSource = object instanceof DataSource;
|
|
if (type === '[object Object]' && !isDataSource && !isObservableArray) {
|
|
if (!(object instanceof ObservableObject)) {
|
|
object = new ObservableObject(object);
|
|
}
|
|
get = eventHandler(that, GET, field, true);
|
|
object.bind(GET, get);
|
|
change = eventHandler(that, CHANGE, field, true);
|
|
object.bind(CHANGE, change);
|
|
that._handlers[field] = {
|
|
get: get,
|
|
change: change
|
|
};
|
|
} else if (type === '[object Array]' || isObservableArray || isDataSource) {
|
|
if (!isObservableArray && !isDataSource) {
|
|
object = new ObservableArray(object);
|
|
}
|
|
change = eventHandler(that, CHANGE, field, false);
|
|
object.bind(CHANGE, change);
|
|
that._handlers[field] = { change: change };
|
|
}
|
|
object.parent = parent;
|
|
}
|
|
return object;
|
|
}
|
|
});
|
|
function equal(x, y) {
|
|
if (x === y) {
|
|
return true;
|
|
}
|
|
var xtype = $.type(x), ytype = $.type(y), field;
|
|
if (xtype !== ytype) {
|
|
return false;
|
|
}
|
|
if (xtype === 'date') {
|
|
return x.getTime() === y.getTime();
|
|
}
|
|
if (xtype !== 'object' && xtype !== 'array') {
|
|
return false;
|
|
}
|
|
for (field in x) {
|
|
if (!equal(x[field], y[field])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
var parsers = {
|
|
'number': function (value) {
|
|
return kendo.parseFloat(value);
|
|
},
|
|
'date': function (value) {
|
|
return kendo.parseDate(value);
|
|
},
|
|
'boolean': function (value) {
|
|
if (typeof value === STRING) {
|
|
return value.toLowerCase() === 'true';
|
|
}
|
|
return value != null ? !!value : value;
|
|
},
|
|
'string': function (value) {
|
|
return value != null ? value + '' : value;
|
|
},
|
|
'default': function (value) {
|
|
return value;
|
|
}
|
|
};
|
|
var defaultValues = {
|
|
'string': '',
|
|
'number': 0,
|
|
'date': new Date(),
|
|
'boolean': false,
|
|
'default': ''
|
|
};
|
|
function getFieldByName(obj, name) {
|
|
var field, fieldName;
|
|
for (fieldName in obj) {
|
|
field = obj[fieldName];
|
|
if (isPlainObject(field) && field.field && field.field === name) {
|
|
return field;
|
|
} else if (field === name) {
|
|
return field;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
var Model = ObservableObject.extend({
|
|
init: function (data) {
|
|
var that = this;
|
|
if (!data || $.isEmptyObject(data)) {
|
|
data = $.extend({}, that.defaults, data);
|
|
if (that._initializers) {
|
|
for (var idx = 0; idx < that._initializers.length; idx++) {
|
|
var name = that._initializers[idx];
|
|
data[name] = that.defaults[name]();
|
|
}
|
|
}
|
|
}
|
|
ObservableObject.fn.init.call(that, data);
|
|
that.dirty = false;
|
|
if (that.idField) {
|
|
that.id = that.get(that.idField);
|
|
if (that.id === undefined) {
|
|
that.id = that._defaultId;
|
|
}
|
|
}
|
|
},
|
|
shouldSerialize: function (field) {
|
|
return ObservableObject.fn.shouldSerialize.call(this, field) && field !== 'uid' && !(this.idField !== 'id' && field === 'id') && field !== 'dirty' && field !== '_accessors';
|
|
},
|
|
_parse: function (field, value) {
|
|
var that = this, fieldName = field, fields = that.fields || {}, parse;
|
|
field = fields[field];
|
|
if (!field) {
|
|
field = getFieldByName(fields, fieldName);
|
|
}
|
|
if (field) {
|
|
parse = field.parse;
|
|
if (!parse && field.type) {
|
|
parse = parsers[field.type.toLowerCase()];
|
|
}
|
|
}
|
|
return parse ? parse(value) : value;
|
|
},
|
|
_notifyChange: function (e) {
|
|
var action = e.action;
|
|
if (action == 'add' || action == 'remove') {
|
|
this.dirty = true;
|
|
}
|
|
},
|
|
editable: function (field) {
|
|
field = (this.fields || {})[field];
|
|
return field ? field.editable !== false : true;
|
|
},
|
|
set: function (field, value, initiator) {
|
|
var that = this;
|
|
var dirty = that.dirty;
|
|
if (that.editable(field)) {
|
|
value = that._parse(field, value);
|
|
if (!equal(value, that.get(field))) {
|
|
that.dirty = true;
|
|
if (ObservableObject.fn.set.call(that, field, value, initiator) && !dirty) {
|
|
that.dirty = dirty;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
accept: function (data) {
|
|
var that = this, parent = function () {
|
|
return that;
|
|
}, field;
|
|
for (field in data) {
|
|
var value = data[field];
|
|
if (field.charAt(0) != '_') {
|
|
value = that.wrap(data[field], field, parent);
|
|
}
|
|
that._set(field, value);
|
|
}
|
|
if (that.idField) {
|
|
that.id = that.get(that.idField);
|
|
}
|
|
that.dirty = false;
|
|
},
|
|
isNew: function () {
|
|
return this.id === this._defaultId;
|
|
}
|
|
});
|
|
Model.define = function (base, options) {
|
|
if (options === undefined) {
|
|
options = base;
|
|
base = Model;
|
|
}
|
|
var model, proto = extend({ defaults: {} }, options), name, field, type, value, idx, length, fields = {}, originalName, id = proto.id, functionFields = [];
|
|
if (id) {
|
|
proto.idField = id;
|
|
}
|
|
if (proto.id) {
|
|
delete proto.id;
|
|
}
|
|
if (id) {
|
|
proto.defaults[id] = proto._defaultId = '';
|
|
}
|
|
if (toString.call(proto.fields) === '[object Array]') {
|
|
for (idx = 0, length = proto.fields.length; idx < length; idx++) {
|
|
field = proto.fields[idx];
|
|
if (typeof field === STRING) {
|
|
fields[field] = {};
|
|
} else if (field.field) {
|
|
fields[field.field] = field;
|
|
}
|
|
}
|
|
proto.fields = fields;
|
|
}
|
|
for (name in proto.fields) {
|
|
field = proto.fields[name];
|
|
type = field.type || 'default';
|
|
value = null;
|
|
originalName = name;
|
|
name = typeof field.field === STRING ? field.field : name;
|
|
if (!field.nullable) {
|
|
value = proto.defaults[originalName !== name ? originalName : name] = field.defaultValue !== undefined ? field.defaultValue : defaultValues[type.toLowerCase()];
|
|
if (typeof value === 'function') {
|
|
functionFields.push(name);
|
|
}
|
|
}
|
|
if (options.id === name) {
|
|
proto._defaultId = value;
|
|
}
|
|
proto.defaults[originalName !== name ? originalName : name] = value;
|
|
field.parse = field.parse || parsers[type];
|
|
}
|
|
if (functionFields.length > 0) {
|
|
proto._initializers = functionFields;
|
|
}
|
|
model = base.extend(proto);
|
|
model.define = function (options) {
|
|
return Model.define(model, options);
|
|
};
|
|
if (proto.fields) {
|
|
model.fields = proto.fields;
|
|
model.idField = proto.idField;
|
|
}
|
|
return model;
|
|
};
|
|
var Comparer = {
|
|
selector: function (field) {
|
|
return isFunction(field) ? field : getter(field);
|
|
},
|
|
compare: function (field) {
|
|
var selector = this.selector(field);
|
|
return function (a, b) {
|
|
a = selector(a);
|
|
b = selector(b);
|
|
if (a == null && b == null) {
|
|
return 0;
|
|
}
|
|
if (a == null) {
|
|
return -1;
|
|
}
|
|
if (b == null) {
|
|
return 1;
|
|
}
|
|
if (a.localeCompare) {
|
|
return a.localeCompare(b);
|
|
}
|
|
return a > b ? 1 : a < b ? -1 : 0;
|
|
};
|
|
},
|
|
create: function (sort) {
|
|
var compare = sort.compare || this.compare(sort.field);
|
|
if (sort.dir == 'desc') {
|
|
return function (a, b) {
|
|
return compare(b, a, true);
|
|
};
|
|
}
|
|
return compare;
|
|
},
|
|
combine: function (comparers) {
|
|
return function (a, b) {
|
|
var result = comparers[0](a, b), idx, length;
|
|
for (idx = 1, length = comparers.length; idx < length; idx++) {
|
|
result = result || comparers[idx](a, b);
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
};
|
|
var StableComparer = extend({}, Comparer, {
|
|
asc: function (field) {
|
|
var selector = this.selector(field);
|
|
return function (a, b) {
|
|
var valueA = selector(a);
|
|
var valueB = selector(b);
|
|
if (valueA && valueA.getTime && valueB && valueB.getTime) {
|
|
valueA = valueA.getTime();
|
|
valueB = valueB.getTime();
|
|
}
|
|
if (valueA === valueB) {
|
|
return a.__position - b.__position;
|
|
}
|
|
if (valueA == null) {
|
|
return -1;
|
|
}
|
|
if (valueB == null) {
|
|
return 1;
|
|
}
|
|
if (valueA.localeCompare) {
|
|
return valueA.localeCompare(valueB);
|
|
}
|
|
return valueA > valueB ? 1 : -1;
|
|
};
|
|
},
|
|
desc: function (field) {
|
|
var selector = this.selector(field);
|
|
return function (a, b) {
|
|
var valueA = selector(a);
|
|
var valueB = selector(b);
|
|
if (valueA && valueA.getTime && valueB && valueB.getTime) {
|
|
valueA = valueA.getTime();
|
|
valueB = valueB.getTime();
|
|
}
|
|
if (valueA === valueB) {
|
|
return a.__position - b.__position;
|
|
}
|
|
if (valueA == null) {
|
|
return 1;
|
|
}
|
|
if (valueB == null) {
|
|
return -1;
|
|
}
|
|
if (valueB.localeCompare) {
|
|
return valueB.localeCompare(valueA);
|
|
}
|
|
return valueA < valueB ? 1 : -1;
|
|
};
|
|
},
|
|
create: function (sort) {
|
|
return this[sort.dir](sort.field);
|
|
}
|
|
});
|
|
map = function (array, callback) {
|
|
var idx, length = array.length, result = new Array(length);
|
|
for (idx = 0; idx < length; idx++) {
|
|
result[idx] = callback(array[idx], idx, array);
|
|
}
|
|
return result;
|
|
};
|
|
var operators = function () {
|
|
function quote(value) {
|
|
return value.replace(quoteRegExp, '\\').replace(newLineRegExp, '');
|
|
}
|
|
function operator(op, a, b, ignore) {
|
|
var date;
|
|
if (b != null) {
|
|
if (typeof b === STRING) {
|
|
b = quote(b);
|
|
date = dateRegExp.exec(b);
|
|
if (date) {
|
|
b = new Date(+date[1]);
|
|
} else if (ignore) {
|
|
b = '\'' + b.toLowerCase() + '\'';
|
|
a = '((' + a + ' || \'\')+\'\').toLowerCase()';
|
|
} else {
|
|
b = '\'' + b + '\'';
|
|
}
|
|
}
|
|
if (b.getTime) {
|
|
a = '(' + a + '&&' + a + '.getTime?' + a + '.getTime():' + a + ')';
|
|
b = b.getTime();
|
|
}
|
|
}
|
|
return a + ' ' + op + ' ' + b;
|
|
}
|
|
return {
|
|
quote: function (value) {
|
|
if (value && value.getTime) {
|
|
return 'new Date(' + value.getTime() + ')';
|
|
}
|
|
if (typeof value == 'string') {
|
|
return '\'' + quote(value) + '\'';
|
|
}
|
|
return '' + value;
|
|
},
|
|
eq: function (a, b, ignore) {
|
|
return operator('==', a, b, ignore);
|
|
},
|
|
neq: function (a, b, ignore) {
|
|
return operator('!=', a, b, ignore);
|
|
},
|
|
gt: function (a, b, ignore) {
|
|
return operator('>', a, b, ignore);
|
|
},
|
|
gte: function (a, b, ignore) {
|
|
return operator('>=', a, b, ignore);
|
|
},
|
|
lt: function (a, b, ignore) {
|
|
return operator('<', a, b, ignore);
|
|
},
|
|
lte: function (a, b, ignore) {
|
|
return operator('<=', a, b, ignore);
|
|
},
|
|
startswith: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.lastIndexOf(\'' + b + '\', 0) == 0';
|
|
},
|
|
doesnotstartwith: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.lastIndexOf(\'' + b + '\', 0) == -1';
|
|
},
|
|
endswith: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.indexOf(\'' + b + '\', ' + a + '.length - ' + (b || '').length + ') >= 0';
|
|
},
|
|
doesnotendwith: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.indexOf(\'' + b + '\', ' + a + '.length - ' + (b || '').length + ') < 0';
|
|
},
|
|
contains: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.indexOf(\'' + b + '\') >= 0';
|
|
},
|
|
doesnotcontain: function (a, b, ignore) {
|
|
if (ignore) {
|
|
a = '(' + a + ' || \'\').toLowerCase()';
|
|
if (b) {
|
|
b = b.toLowerCase();
|
|
}
|
|
}
|
|
if (b) {
|
|
b = quote(b);
|
|
}
|
|
return a + '.indexOf(\'' + b + '\') == -1';
|
|
},
|
|
isempty: function (a) {
|
|
return a + ' === \'\'';
|
|
},
|
|
isnotempty: function (a) {
|
|
return a + ' !== \'\'';
|
|
},
|
|
isnull: function (a) {
|
|
return '(' + a + ' === null || ' + a + ' === undefined)';
|
|
},
|
|
isnotnull: function (a) {
|
|
return '(' + a + ' !== null && ' + a + ' !== undefined)';
|
|
}
|
|
};
|
|
}();
|
|
function Query(data) {
|
|
this.data = data || [];
|
|
}
|
|
Query.filterExpr = function (expression) {
|
|
var expressions = [], logic = {
|
|
and: ' && ',
|
|
or: ' || '
|
|
}, idx, length, filter, expr, fieldFunctions = [], operatorFunctions = [], field, operator, filters = expression.filters;
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
field = filter.field;
|
|
operator = filter.operator;
|
|
if (filter.filters) {
|
|
expr = Query.filterExpr(filter);
|
|
filter = expr.expression.replace(/__o\[(\d+)\]/g, function (match, index) {
|
|
index = +index;
|
|
return '__o[' + (operatorFunctions.length + index) + ']';
|
|
}).replace(/__f\[(\d+)\]/g, function (match, index) {
|
|
index = +index;
|
|
return '__f[' + (fieldFunctions.length + index) + ']';
|
|
});
|
|
operatorFunctions.push.apply(operatorFunctions, expr.operators);
|
|
fieldFunctions.push.apply(fieldFunctions, expr.fields);
|
|
} else {
|
|
if (typeof field === FUNCTION) {
|
|
expr = '__f[' + fieldFunctions.length + '](d)';
|
|
fieldFunctions.push(field);
|
|
} else {
|
|
expr = kendo.expr(field);
|
|
}
|
|
if (typeof operator === FUNCTION) {
|
|
filter = '__o[' + operatorFunctions.length + '](' + expr + ', ' + operators.quote(filter.value) + ')';
|
|
operatorFunctions.push(operator);
|
|
} else {
|
|
filter = operators[(operator || 'eq').toLowerCase()](expr, filter.value, filter.ignoreCase !== undefined ? filter.ignoreCase : true);
|
|
}
|
|
}
|
|
expressions.push(filter);
|
|
}
|
|
return {
|
|
expression: '(' + expressions.join(logic[expression.logic]) + ')',
|
|
fields: fieldFunctions,
|
|
operators: operatorFunctions
|
|
};
|
|
};
|
|
function normalizeSort(field, dir) {
|
|
if (field) {
|
|
var descriptor = typeof field === STRING ? {
|
|
field: field,
|
|
dir: dir
|
|
} : field, descriptors = isArray(descriptor) ? descriptor : descriptor !== undefined ? [descriptor] : [];
|
|
return grep(descriptors, function (d) {
|
|
return !!d.dir;
|
|
});
|
|
}
|
|
}
|
|
var operatorMap = {
|
|
'==': 'eq',
|
|
equals: 'eq',
|
|
isequalto: 'eq',
|
|
equalto: 'eq',
|
|
equal: 'eq',
|
|
'!=': 'neq',
|
|
ne: 'neq',
|
|
notequals: 'neq',
|
|
isnotequalto: 'neq',
|
|
notequalto: 'neq',
|
|
notequal: 'neq',
|
|
'<': 'lt',
|
|
islessthan: 'lt',
|
|
lessthan: 'lt',
|
|
less: 'lt',
|
|
'<=': 'lte',
|
|
le: 'lte',
|
|
islessthanorequalto: 'lte',
|
|
lessthanequal: 'lte',
|
|
'>': 'gt',
|
|
isgreaterthan: 'gt',
|
|
greaterthan: 'gt',
|
|
greater: 'gt',
|
|
'>=': 'gte',
|
|
isgreaterthanorequalto: 'gte',
|
|
greaterthanequal: 'gte',
|
|
ge: 'gte',
|
|
notsubstringof: 'doesnotcontain',
|
|
isnull: 'isnull',
|
|
isempty: 'isempty',
|
|
isnotempty: 'isnotempty'
|
|
};
|
|
function normalizeOperator(expression) {
|
|
var idx, length, filter, operator, filters = expression.filters;
|
|
if (filters) {
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
operator = filter.operator;
|
|
if (operator && typeof operator === STRING) {
|
|
filter.operator = operatorMap[operator.toLowerCase()] || operator;
|
|
}
|
|
normalizeOperator(filter);
|
|
}
|
|
}
|
|
}
|
|
function normalizeFilter(expression) {
|
|
if (expression && !isEmptyObject(expression)) {
|
|
if (isArray(expression) || !expression.filters) {
|
|
expression = {
|
|
logic: 'and',
|
|
filters: isArray(expression) ? expression : [expression]
|
|
};
|
|
}
|
|
normalizeOperator(expression);
|
|
return expression;
|
|
}
|
|
}
|
|
Query.normalizeFilter = normalizeFilter;
|
|
function compareDescriptor(f1, f2) {
|
|
if (f1.logic || f2.logic) {
|
|
return false;
|
|
}
|
|
return f1.field === f2.field && f1.value === f2.value && f1.operator === f2.operator;
|
|
}
|
|
function normalizeDescriptor(filter) {
|
|
filter = filter || {};
|
|
if (isEmptyObject(filter)) {
|
|
return {
|
|
logic: 'and',
|
|
filters: []
|
|
};
|
|
}
|
|
return normalizeFilter(filter);
|
|
}
|
|
function fieldComparer(a, b) {
|
|
if (b.logic || a.field > b.field) {
|
|
return 1;
|
|
} else if (a.field < b.field) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
function compareFilters(expr1, expr2) {
|
|
expr1 = normalizeDescriptor(expr1);
|
|
expr2 = normalizeDescriptor(expr2);
|
|
if (expr1.logic !== expr2.logic) {
|
|
return false;
|
|
}
|
|
var f1, f2;
|
|
var filters1 = (expr1.filters || []).slice();
|
|
var filters2 = (expr2.filters || []).slice();
|
|
if (filters1.length !== filters2.length) {
|
|
return false;
|
|
}
|
|
filters1 = filters1.sort(fieldComparer);
|
|
filters2 = filters2.sort(fieldComparer);
|
|
for (var idx = 0; idx < filters1.length; idx++) {
|
|
f1 = filters1[idx];
|
|
f2 = filters2[idx];
|
|
if (f1.logic && f2.logic) {
|
|
if (!compareFilters(f1, f2)) {
|
|
return false;
|
|
}
|
|
} else if (!compareDescriptor(f1, f2)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
Query.compareFilters = compareFilters;
|
|
function normalizeAggregate(expressions) {
|
|
return isArray(expressions) ? expressions : [expressions];
|
|
}
|
|
function normalizeGroup(field, dir) {
|
|
var descriptor = typeof field === STRING ? {
|
|
field: field,
|
|
dir: dir
|
|
} : field, descriptors = isArray(descriptor) ? descriptor : descriptor !== undefined ? [descriptor] : [];
|
|
return map(descriptors, function (d) {
|
|
return {
|
|
field: d.field,
|
|
dir: d.dir || 'asc',
|
|
aggregates: d.aggregates
|
|
};
|
|
});
|
|
}
|
|
Query.prototype = {
|
|
toArray: function () {
|
|
return this.data;
|
|
},
|
|
range: function (index, count) {
|
|
return new Query(this.data.slice(index, index + count));
|
|
},
|
|
skip: function (count) {
|
|
return new Query(this.data.slice(count));
|
|
},
|
|
take: function (count) {
|
|
return new Query(this.data.slice(0, count));
|
|
},
|
|
select: function (selector) {
|
|
return new Query(map(this.data, selector));
|
|
},
|
|
order: function (selector, dir) {
|
|
var sort = { dir: dir };
|
|
if (selector) {
|
|
if (selector.compare) {
|
|
sort.compare = selector.compare;
|
|
} else {
|
|
sort.field = selector;
|
|
}
|
|
}
|
|
return new Query(this.data.slice(0).sort(Comparer.create(sort)));
|
|
},
|
|
orderBy: function (selector) {
|
|
return this.order(selector, 'asc');
|
|
},
|
|
orderByDescending: function (selector) {
|
|
return this.order(selector, 'desc');
|
|
},
|
|
sort: function (field, dir, comparer) {
|
|
var idx, length, descriptors = normalizeSort(field, dir), comparers = [];
|
|
comparer = comparer || Comparer;
|
|
if (descriptors.length) {
|
|
for (idx = 0, length = descriptors.length; idx < length; idx++) {
|
|
comparers.push(comparer.create(descriptors[idx]));
|
|
}
|
|
return this.orderBy({ compare: comparer.combine(comparers) });
|
|
}
|
|
return this;
|
|
},
|
|
filter: function (expressions) {
|
|
var idx, current, length, compiled, predicate, data = this.data, fields, operators, result = [], filter;
|
|
expressions = normalizeFilter(expressions);
|
|
if (!expressions || expressions.filters.length === 0) {
|
|
return this;
|
|
}
|
|
compiled = Query.filterExpr(expressions);
|
|
fields = compiled.fields;
|
|
operators = compiled.operators;
|
|
predicate = filter = new Function('d, __f, __o', 'return ' + compiled.expression);
|
|
if (fields.length || operators.length) {
|
|
filter = function (d) {
|
|
return predicate(d, fields, operators);
|
|
};
|
|
}
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
current = data[idx];
|
|
if (filter(current)) {
|
|
result.push(current);
|
|
}
|
|
}
|
|
return new Query(result);
|
|
},
|
|
group: function (descriptors, allData) {
|
|
descriptors = normalizeGroup(descriptors || []);
|
|
allData = allData || this.data;
|
|
var that = this, result = new Query(that.data), descriptor;
|
|
if (descriptors.length > 0) {
|
|
descriptor = descriptors[0];
|
|
result = result.groupBy(descriptor).select(function (group) {
|
|
var data = new Query(allData).filter([{
|
|
field: group.field,
|
|
operator: 'eq',
|
|
value: group.value,
|
|
ignoreCase: false
|
|
}]);
|
|
return {
|
|
field: group.field,
|
|
value: group.value,
|
|
items: descriptors.length > 1 ? new Query(group.items).group(descriptors.slice(1), data.toArray()).toArray() : group.items,
|
|
hasSubgroups: descriptors.length > 1,
|
|
aggregates: data.aggregate(descriptor.aggregates)
|
|
};
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
groupBy: function (descriptor) {
|
|
if (isEmptyObject(descriptor) || !this.data.length) {
|
|
return new Query([]);
|
|
}
|
|
var field = descriptor.field, sorted = this._sortForGrouping(field, descriptor.dir || 'asc'), accessor = kendo.accessor(field), item, groupValue = accessor.get(sorted[0], field), group = {
|
|
field: field,
|
|
value: groupValue,
|
|
items: []
|
|
}, currentValue, idx, len, result = [group];
|
|
for (idx = 0, len = sorted.length; idx < len; idx++) {
|
|
item = sorted[idx];
|
|
currentValue = accessor.get(item, field);
|
|
if (!groupValueComparer(groupValue, currentValue)) {
|
|
groupValue = currentValue;
|
|
group = {
|
|
field: field,
|
|
value: groupValue,
|
|
items: []
|
|
};
|
|
result.push(group);
|
|
}
|
|
group.items.push(item);
|
|
}
|
|
return new Query(result);
|
|
},
|
|
_sortForGrouping: function (field, dir) {
|
|
var idx, length, data = this.data;
|
|
if (!stableSort) {
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
data[idx].__position = idx;
|
|
}
|
|
data = new Query(data).sort(field, dir, StableComparer).toArray();
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
delete data[idx].__position;
|
|
}
|
|
return data;
|
|
}
|
|
return this.sort(field, dir).toArray();
|
|
},
|
|
aggregate: function (aggregates) {
|
|
var idx, len, result = {}, state = {};
|
|
if (aggregates && aggregates.length) {
|
|
for (idx = 0, len = this.data.length; idx < len; idx++) {
|
|
calculateAggregate(result, aggregates, this.data[idx], idx, len, state);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
function groupValueComparer(a, b) {
|
|
if (a && a.getTime && b && b.getTime) {
|
|
return a.getTime() === b.getTime();
|
|
}
|
|
return a === b;
|
|
}
|
|
function calculateAggregate(accumulator, aggregates, item, index, length, state) {
|
|
aggregates = aggregates || [];
|
|
var idx, aggr, functionName, len = aggregates.length;
|
|
for (idx = 0; idx < len; idx++) {
|
|
aggr = aggregates[idx];
|
|
functionName = aggr.aggregate;
|
|
var field = aggr.field;
|
|
accumulator[field] = accumulator[field] || {};
|
|
state[field] = state[field] || {};
|
|
state[field][functionName] = state[field][functionName] || {};
|
|
accumulator[field][functionName] = functions[functionName.toLowerCase()](accumulator[field][functionName], item, kendo.accessor(field), index, length, state[field][functionName]);
|
|
}
|
|
}
|
|
var functions = {
|
|
sum: function (accumulator, item, accessor) {
|
|
var value = accessor.get(item);
|
|
if (!isNumber(accumulator)) {
|
|
accumulator = value;
|
|
} else if (isNumber(value)) {
|
|
accumulator += value;
|
|
}
|
|
return accumulator;
|
|
},
|
|
count: function (accumulator) {
|
|
return (accumulator || 0) + 1;
|
|
},
|
|
average: function (accumulator, item, accessor, index, length, state) {
|
|
var value = accessor.get(item);
|
|
if (state.count === undefined) {
|
|
state.count = 0;
|
|
}
|
|
if (!isNumber(accumulator)) {
|
|
accumulator = value;
|
|
} else if (isNumber(value)) {
|
|
accumulator += value;
|
|
}
|
|
if (isNumber(value)) {
|
|
state.count++;
|
|
}
|
|
if (index == length - 1 && isNumber(accumulator)) {
|
|
accumulator = accumulator / state.count;
|
|
}
|
|
return accumulator;
|
|
},
|
|
max: function (accumulator, item, accessor) {
|
|
var value = accessor.get(item);
|
|
if (!isNumber(accumulator) && !isDate(accumulator)) {
|
|
accumulator = value;
|
|
}
|
|
if (accumulator < value && (isNumber(value) || isDate(value))) {
|
|
accumulator = value;
|
|
}
|
|
return accumulator;
|
|
},
|
|
min: function (accumulator, item, accessor) {
|
|
var value = accessor.get(item);
|
|
if (!isNumber(accumulator) && !isDate(accumulator)) {
|
|
accumulator = value;
|
|
}
|
|
if (accumulator > value && (isNumber(value) || isDate(value))) {
|
|
accumulator = value;
|
|
}
|
|
return accumulator;
|
|
}
|
|
};
|
|
function isNumber(val) {
|
|
return typeof val === 'number' && !isNaN(val);
|
|
}
|
|
function isDate(val) {
|
|
return val && val.getTime;
|
|
}
|
|
function toJSON(array) {
|
|
var idx, length = array.length, result = new Array(length);
|
|
for (idx = 0; idx < length; idx++) {
|
|
result[idx] = array[idx].toJSON();
|
|
}
|
|
return result;
|
|
}
|
|
Query.process = function (data, options) {
|
|
options = options || {};
|
|
var query = new Query(data), group = options.group, sort = normalizeGroup(group || []).concat(normalizeSort(options.sort || [])), total, filterCallback = options.filterCallback, filter = options.filter, skip = options.skip, take = options.take;
|
|
if (filter) {
|
|
query = query.filter(filter);
|
|
if (filterCallback) {
|
|
query = filterCallback(query);
|
|
}
|
|
total = query.toArray().length;
|
|
}
|
|
if (sort) {
|
|
query = query.sort(sort);
|
|
if (group) {
|
|
data = query.toArray();
|
|
}
|
|
}
|
|
if (skip !== undefined && take !== undefined) {
|
|
query = query.range(skip, take);
|
|
}
|
|
if (group) {
|
|
query = query.group(group, data);
|
|
}
|
|
return {
|
|
total: total,
|
|
data: query.toArray()
|
|
};
|
|
};
|
|
var LocalTransport = Class.extend({
|
|
init: function (options) {
|
|
this.data = options.data;
|
|
},
|
|
read: function (options) {
|
|
options.success(this.data);
|
|
},
|
|
update: function (options) {
|
|
options.success(options.data);
|
|
},
|
|
create: function (options) {
|
|
options.success(options.data);
|
|
},
|
|
destroy: function (options) {
|
|
options.success(options.data);
|
|
}
|
|
});
|
|
var RemoteTransport = Class.extend({
|
|
init: function (options) {
|
|
var that = this, parameterMap;
|
|
options = that.options = extend({}, that.options, options);
|
|
each(crud, function (index, type) {
|
|
if (typeof options[type] === STRING) {
|
|
options[type] = { url: options[type] };
|
|
}
|
|
});
|
|
that.cache = options.cache ? Cache.create(options.cache) : {
|
|
find: noop,
|
|
add: noop
|
|
};
|
|
parameterMap = options.parameterMap;
|
|
if (isFunction(options.push)) {
|
|
that.push = options.push;
|
|
}
|
|
if (!that.push) {
|
|
that.push = identity;
|
|
}
|
|
that.parameterMap = isFunction(parameterMap) ? parameterMap : function (options) {
|
|
var result = {};
|
|
each(options, function (option, value) {
|
|
if (option in parameterMap) {
|
|
option = parameterMap[option];
|
|
if (isPlainObject(option)) {
|
|
value = option.value(value);
|
|
option = option.key;
|
|
}
|
|
}
|
|
result[option] = value;
|
|
});
|
|
return result;
|
|
};
|
|
},
|
|
options: { parameterMap: identity },
|
|
create: function (options) {
|
|
return ajax(this.setup(options, CREATE));
|
|
},
|
|
read: function (options) {
|
|
var that = this, success, error, result, cache = that.cache;
|
|
options = that.setup(options, READ);
|
|
success = options.success || noop;
|
|
error = options.error || noop;
|
|
result = cache.find(options.data);
|
|
if (result !== undefined) {
|
|
success(result);
|
|
} else {
|
|
options.success = function (result) {
|
|
cache.add(options.data, result);
|
|
success(result);
|
|
};
|
|
$.ajax(options);
|
|
}
|
|
},
|
|
update: function (options) {
|
|
return ajax(this.setup(options, UPDATE));
|
|
},
|
|
destroy: function (options) {
|
|
return ajax(this.setup(options, DESTROY));
|
|
},
|
|
setup: function (options, type) {
|
|
options = options || {};
|
|
var that = this, parameters, operation = that.options[type], data = isFunction(operation.data) ? operation.data(options.data) : operation.data;
|
|
options = extend(true, {}, operation, options);
|
|
parameters = extend(true, {}, data, options.data);
|
|
options.data = that.parameterMap(parameters, type);
|
|
if (isFunction(options.url)) {
|
|
options.url = options.url(parameters);
|
|
}
|
|
return options;
|
|
}
|
|
});
|
|
var Cache = Class.extend({
|
|
init: function () {
|
|
this._store = {};
|
|
},
|
|
add: function (key, data) {
|
|
if (key !== undefined) {
|
|
this._store[stringify(key)] = data;
|
|
}
|
|
},
|
|
find: function (key) {
|
|
return this._store[stringify(key)];
|
|
},
|
|
clear: function () {
|
|
this._store = {};
|
|
},
|
|
remove: function (key) {
|
|
delete this._store[stringify(key)];
|
|
}
|
|
});
|
|
Cache.create = function (options) {
|
|
var store = {
|
|
'inmemory': function () {
|
|
return new Cache();
|
|
}
|
|
};
|
|
if (isPlainObject(options) && isFunction(options.find)) {
|
|
return options;
|
|
}
|
|
if (options === true) {
|
|
return new Cache();
|
|
}
|
|
return store[options]();
|
|
};
|
|
function serializeRecords(data, getters, modelInstance, originalFieldNames, fieldNames) {
|
|
var record, getter, originalName, idx, setters = {}, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
record = data[idx];
|
|
for (getter in getters) {
|
|
originalName = fieldNames[getter];
|
|
if (originalName && originalName !== getter) {
|
|
if (!setters[originalName]) {
|
|
setters[originalName] = kendo.setter(originalName);
|
|
}
|
|
setters[originalName](record, getters[getter](record));
|
|
delete record[getter];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function convertRecords(data, getters, modelInstance, originalFieldNames, fieldNames) {
|
|
var record, getter, originalName, idx, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
record = data[idx];
|
|
for (getter in getters) {
|
|
record[getter] = modelInstance._parse(getter, getters[getter](record));
|
|
originalName = fieldNames[getter];
|
|
if (originalName && originalName !== getter) {
|
|
delete record[originalName];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function convertGroup(data, getters, modelInstance, originalFieldNames, fieldNames) {
|
|
var record, idx, fieldName, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
record = data[idx];
|
|
fieldName = originalFieldNames[record.field];
|
|
if (fieldName && fieldName != record.field) {
|
|
record.field = fieldName;
|
|
}
|
|
record.value = modelInstance._parse(record.field, record.value);
|
|
if (record.hasSubgroups) {
|
|
convertGroup(record.items, getters, modelInstance, originalFieldNames, fieldNames);
|
|
} else {
|
|
convertRecords(record.items, getters, modelInstance, originalFieldNames, fieldNames);
|
|
}
|
|
}
|
|
}
|
|
function wrapDataAccess(originalFunction, model, converter, getters, originalFieldNames, fieldNames) {
|
|
return function (data) {
|
|
data = originalFunction(data);
|
|
if (data && !isEmptyObject(getters)) {
|
|
if (toString.call(data) !== '[object Array]' && !(data instanceof ObservableArray)) {
|
|
data = [data];
|
|
}
|
|
converter(data, getters, new model(), originalFieldNames, fieldNames);
|
|
}
|
|
return data || [];
|
|
};
|
|
}
|
|
var DataReader = Class.extend({
|
|
init: function (schema) {
|
|
var that = this, member, get, model, base;
|
|
schema = schema || {};
|
|
for (member in schema) {
|
|
get = schema[member];
|
|
that[member] = typeof get === STRING ? getter(get) : get;
|
|
}
|
|
base = schema.modelBase || Model;
|
|
if (isPlainObject(that.model)) {
|
|
that.model = model = base.define(that.model);
|
|
}
|
|
var dataFunction = proxy(that.data, that);
|
|
that._dataAccessFunction = dataFunction;
|
|
if (that.model) {
|
|
var groupsFunction = proxy(that.groups, that), serializeFunction = proxy(that.serialize, that), originalFieldNames = {}, getters = {}, serializeGetters = {}, fieldNames = {}, shouldSerialize = false, fieldName;
|
|
model = that.model;
|
|
if (model.fields) {
|
|
each(model.fields, function (field, value) {
|
|
var fromName;
|
|
fieldName = field;
|
|
if (isPlainObject(value) && value.field) {
|
|
fieldName = value.field;
|
|
} else if (typeof value === STRING) {
|
|
fieldName = value;
|
|
}
|
|
if (isPlainObject(value) && value.from) {
|
|
fromName = value.from;
|
|
}
|
|
shouldSerialize = shouldSerialize || fromName && fromName !== field || fieldName !== field;
|
|
getters[field] = getter(fromName || fieldName);
|
|
serializeGetters[field] = getter(field);
|
|
originalFieldNames[fromName || fieldName] = field;
|
|
fieldNames[field] = fromName || fieldName;
|
|
});
|
|
if (!schema.serialize && shouldSerialize) {
|
|
that.serialize = wrapDataAccess(serializeFunction, model, serializeRecords, serializeGetters, originalFieldNames, fieldNames);
|
|
}
|
|
}
|
|
that._dataAccessFunction = dataFunction;
|
|
that.data = wrapDataAccess(dataFunction, model, convertRecords, getters, originalFieldNames, fieldNames);
|
|
that.groups = wrapDataAccess(groupsFunction, model, convertGroup, getters, originalFieldNames, fieldNames);
|
|
}
|
|
},
|
|
errors: function (data) {
|
|
return data ? data.errors : null;
|
|
},
|
|
parse: identity,
|
|
data: identity,
|
|
total: function (data) {
|
|
return data.length;
|
|
},
|
|
groups: identity,
|
|
aggregates: function () {
|
|
return {};
|
|
},
|
|
serialize: function (data) {
|
|
return data;
|
|
}
|
|
});
|
|
function mergeGroups(target, dest, skip, take) {
|
|
var group, idx = 0, items;
|
|
while (dest.length && take) {
|
|
group = dest[idx];
|
|
items = group.items;
|
|
var length = items.length;
|
|
if (target && target.field === group.field && target.value === group.value) {
|
|
if (target.hasSubgroups && target.items.length) {
|
|
mergeGroups(target.items[target.items.length - 1], group.items, skip, take);
|
|
} else {
|
|
items = items.slice(skip, skip + take);
|
|
target.items = target.items.concat(items);
|
|
}
|
|
dest.splice(idx--, 1);
|
|
} else if (group.hasSubgroups && items.length) {
|
|
mergeGroups(group, items, skip, take);
|
|
if (!group.items.length) {
|
|
dest.splice(idx--, 1);
|
|
}
|
|
} else {
|
|
items = items.slice(skip, skip + take);
|
|
group.items = items;
|
|
if (!group.items.length) {
|
|
dest.splice(idx--, 1);
|
|
}
|
|
}
|
|
if (items.length === 0) {
|
|
skip -= length;
|
|
} else {
|
|
skip = 0;
|
|
take -= items.length;
|
|
}
|
|
if (++idx >= dest.length) {
|
|
break;
|
|
}
|
|
}
|
|
if (idx < dest.length) {
|
|
dest.splice(idx, dest.length - idx);
|
|
}
|
|
}
|
|
function flattenGroups(data) {
|
|
var idx, result = [], length, items, itemIndex;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
var group = data.at(idx);
|
|
if (group.hasSubgroups) {
|
|
result = result.concat(flattenGroups(group.items));
|
|
} else {
|
|
items = group.items;
|
|
for (itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
result.push(items.at(itemIndex));
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function wrapGroupItems(data, model) {
|
|
var idx, length, group;
|
|
if (model) {
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
group = data.at(idx);
|
|
if (group.hasSubgroups) {
|
|
wrapGroupItems(group.items, model);
|
|
} else {
|
|
group.items = new LazyObservableArray(group.items, model);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function eachGroupItems(data, func) {
|
|
for (var idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].hasSubgroups) {
|
|
if (eachGroupItems(data[idx].items, func)) {
|
|
return true;
|
|
}
|
|
} else if (func(data[idx].items, data[idx])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
function replaceInRanges(ranges, data, item, observable) {
|
|
for (var idx = 0; idx < ranges.length; idx++) {
|
|
if (ranges[idx].data === data) {
|
|
break;
|
|
}
|
|
if (replaceInRange(ranges[idx].data, item, observable)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function replaceInRange(items, item, observable) {
|
|
for (var idx = 0, length = items.length; idx < length; idx++) {
|
|
if (items[idx] && items[idx].hasSubgroups) {
|
|
return replaceInRange(items[idx].items, item, observable);
|
|
} else if (items[idx] === item || items[idx] === observable) {
|
|
items[idx] = observable;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
function replaceWithObservable(view, data, ranges, type, serverGrouping) {
|
|
for (var viewIndex = 0, length = view.length; viewIndex < length; viewIndex++) {
|
|
var item = view[viewIndex];
|
|
if (!item || item instanceof type) {
|
|
continue;
|
|
}
|
|
if (item.hasSubgroups !== undefined && !serverGrouping) {
|
|
replaceWithObservable(item.items, data, ranges, type, serverGrouping);
|
|
} else {
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
if (data[idx] === item) {
|
|
view[viewIndex] = data.at(idx);
|
|
replaceInRanges(ranges, data, item, view[viewIndex]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function removeModel(data, model) {
|
|
var idx, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
var dataItem = data.at(idx);
|
|
if (dataItem.uid == model.uid) {
|
|
data.splice(idx, 1);
|
|
return dataItem;
|
|
}
|
|
}
|
|
}
|
|
function indexOfPristineModel(data, model) {
|
|
if (model) {
|
|
return indexOf(data, function (item) {
|
|
return item.uid && item.uid == model.uid || item[model.idField] === model.id && model.id !== model._defaultId;
|
|
});
|
|
}
|
|
return -1;
|
|
}
|
|
function indexOfModel(data, model) {
|
|
if (model) {
|
|
return indexOf(data, function (item) {
|
|
return item.uid == model.uid;
|
|
});
|
|
}
|
|
return -1;
|
|
}
|
|
function indexOf(data, comparer) {
|
|
var idx, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (comparer(data[idx])) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function fieldNameFromModel(fields, name) {
|
|
if (fields && !isEmptyObject(fields)) {
|
|
var descriptor = fields[name];
|
|
var fieldName;
|
|
if (isPlainObject(descriptor)) {
|
|
fieldName = descriptor.from || descriptor.field || name;
|
|
} else {
|
|
fieldName = fields[name] || name;
|
|
}
|
|
if (isFunction(fieldName)) {
|
|
return name;
|
|
}
|
|
return fieldName;
|
|
}
|
|
return name;
|
|
}
|
|
function convertFilterDescriptorsField(descriptor, model) {
|
|
var idx, length, target = {};
|
|
for (var field in descriptor) {
|
|
if (field !== 'filters') {
|
|
target[field] = descriptor[field];
|
|
}
|
|
}
|
|
if (descriptor.filters) {
|
|
target.filters = [];
|
|
for (idx = 0, length = descriptor.filters.length; idx < length; idx++) {
|
|
target.filters[idx] = convertFilterDescriptorsField(descriptor.filters[idx], model);
|
|
}
|
|
} else {
|
|
target.field = fieldNameFromModel(model.fields, target.field);
|
|
}
|
|
return target;
|
|
}
|
|
function convertDescriptorsField(descriptors, model) {
|
|
var idx, length, result = [], target, descriptor;
|
|
for (idx = 0, length = descriptors.length; idx < length; idx++) {
|
|
target = {};
|
|
descriptor = descriptors[idx];
|
|
for (var field in descriptor) {
|
|
target[field] = descriptor[field];
|
|
}
|
|
target.field = fieldNameFromModel(model.fields, target.field);
|
|
if (target.aggregates && isArray(target.aggregates)) {
|
|
target.aggregates = convertDescriptorsField(target.aggregates, model);
|
|
}
|
|
result.push(target);
|
|
}
|
|
return result;
|
|
}
|
|
var DataSource = Observable.extend({
|
|
init: function (options) {
|
|
var that = this, model, data;
|
|
if (options) {
|
|
data = options.data;
|
|
}
|
|
options = that.options = extend({}, that.options, options);
|
|
that._map = {};
|
|
that._prefetch = {};
|
|
that._data = [];
|
|
that._pristineData = [];
|
|
that._ranges = [];
|
|
that._view = [];
|
|
that._pristineTotal = 0;
|
|
that._destroyed = [];
|
|
that._pageSize = options.pageSize;
|
|
that._page = options.page || (options.pageSize ? 1 : undefined);
|
|
that._sort = normalizeSort(options.sort);
|
|
that._filter = normalizeFilter(options.filter);
|
|
that._group = normalizeGroup(options.group);
|
|
that._aggregate = options.aggregate;
|
|
that._total = options.total;
|
|
that._shouldDetachObservableParents = true;
|
|
Observable.fn.init.call(that);
|
|
that.transport = Transport.create(options, data, that);
|
|
if (isFunction(that.transport.push)) {
|
|
that.transport.push({
|
|
pushCreate: proxy(that._pushCreate, that),
|
|
pushUpdate: proxy(that._pushUpdate, that),
|
|
pushDestroy: proxy(that._pushDestroy, that)
|
|
});
|
|
}
|
|
if (options.offlineStorage != null) {
|
|
if (typeof options.offlineStorage == 'string') {
|
|
var key = options.offlineStorage;
|
|
that._storage = {
|
|
getItem: function () {
|
|
return JSON.parse(localStorage.getItem(key));
|
|
},
|
|
setItem: function (item) {
|
|
localStorage.setItem(key, stringify(that.reader.serialize(item)));
|
|
}
|
|
};
|
|
} else {
|
|
that._storage = options.offlineStorage;
|
|
}
|
|
}
|
|
that.reader = new kendo.data.readers[options.schema.type || 'json'](options.schema);
|
|
model = that.reader.model || {};
|
|
that._detachObservableParents();
|
|
that._data = that._observe(that._data);
|
|
that._online = true;
|
|
that.bind([
|
|
'push',
|
|
ERROR,
|
|
CHANGE,
|
|
REQUESTSTART,
|
|
SYNC,
|
|
REQUESTEND,
|
|
PROGRESS
|
|
], options);
|
|
},
|
|
options: {
|
|
data: null,
|
|
schema: { modelBase: Model },
|
|
offlineStorage: null,
|
|
serverSorting: false,
|
|
serverPaging: false,
|
|
serverFiltering: false,
|
|
serverGrouping: false,
|
|
serverAggregates: false,
|
|
batch: false
|
|
},
|
|
clone: function () {
|
|
return this;
|
|
},
|
|
online: function (value) {
|
|
if (value !== undefined) {
|
|
if (this._online != value) {
|
|
this._online = value;
|
|
if (value) {
|
|
return this.sync();
|
|
}
|
|
}
|
|
return $.Deferred().resolve().promise();
|
|
} else {
|
|
return this._online;
|
|
}
|
|
},
|
|
offlineData: function (state) {
|
|
if (this.options.offlineStorage == null) {
|
|
return null;
|
|
}
|
|
if (state !== undefined) {
|
|
return this._storage.setItem(state);
|
|
}
|
|
return this._storage.getItem() || [];
|
|
},
|
|
_isServerGrouped: function () {
|
|
var group = this.group() || [];
|
|
return this.options.serverGrouping && group.length;
|
|
},
|
|
_pushCreate: function (result) {
|
|
this._push(result, 'pushCreate');
|
|
},
|
|
_pushUpdate: function (result) {
|
|
this._push(result, 'pushUpdate');
|
|
},
|
|
_pushDestroy: function (result) {
|
|
this._push(result, 'pushDestroy');
|
|
},
|
|
_push: function (result, operation) {
|
|
var data = this._readData(result);
|
|
if (!data) {
|
|
data = result;
|
|
}
|
|
this[operation](data);
|
|
},
|
|
_flatData: function (data, skip) {
|
|
if (data) {
|
|
if (this._isServerGrouped()) {
|
|
return flattenGroups(data);
|
|
}
|
|
if (!skip) {
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
data.at(idx);
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
},
|
|
parent: noop,
|
|
get: function (id) {
|
|
var idx, length, data = this._flatData(this._data);
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].id == id) {
|
|
return data[idx];
|
|
}
|
|
}
|
|
},
|
|
getByUid: function (id) {
|
|
var idx, length, data = this._flatData(this._data);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].uid == id) {
|
|
return data[idx];
|
|
}
|
|
}
|
|
},
|
|
indexOf: function (model) {
|
|
return indexOfModel(this._data, model);
|
|
},
|
|
at: function (index) {
|
|
return this._data.at(index);
|
|
},
|
|
data: function (value) {
|
|
var that = this;
|
|
if (value !== undefined) {
|
|
that._detachObservableParents();
|
|
that._data = this._observe(value);
|
|
that._pristineData = value.slice(0);
|
|
that._storeData();
|
|
that._ranges = [];
|
|
that.trigger('reset');
|
|
that._addRange(that._data);
|
|
that._total = that._data.length;
|
|
that._pristineTotal = that._total;
|
|
that._process(that._data);
|
|
} else {
|
|
if (that._data) {
|
|
for (var idx = 0; idx < that._data.length; idx++) {
|
|
that._data.at(idx);
|
|
}
|
|
}
|
|
return that._data;
|
|
}
|
|
},
|
|
view: function (value) {
|
|
if (value === undefined) {
|
|
return this._view;
|
|
} else {
|
|
this._view = this._observeView(value);
|
|
}
|
|
},
|
|
_observeView: function (data) {
|
|
var that = this;
|
|
replaceWithObservable(data, that._data, that._ranges, that.reader.model || ObservableObject, that._isServerGrouped());
|
|
var view = new LazyObservableArray(data, that.reader.model);
|
|
view.parent = function () {
|
|
return that.parent();
|
|
};
|
|
return view;
|
|
},
|
|
flatView: function () {
|
|
var groups = this.group() || [];
|
|
if (groups.length) {
|
|
return flattenGroups(this._view);
|
|
} else {
|
|
return this._view;
|
|
}
|
|
},
|
|
add: function (model) {
|
|
return this.insert(this._data.length, model);
|
|
},
|
|
_createNewModel: function (model) {
|
|
if (this.reader.model) {
|
|
return new this.reader.model(model);
|
|
}
|
|
if (model instanceof ObservableObject) {
|
|
return model;
|
|
}
|
|
return new ObservableObject(model);
|
|
},
|
|
insert: function (index, model) {
|
|
if (!model) {
|
|
model = index;
|
|
index = 0;
|
|
}
|
|
if (!(model instanceof Model)) {
|
|
model = this._createNewModel(model);
|
|
}
|
|
if (this._isServerGrouped()) {
|
|
this._data.splice(index, 0, this._wrapInEmptyGroup(model));
|
|
} else {
|
|
this._data.splice(index, 0, model);
|
|
}
|
|
return model;
|
|
},
|
|
pushCreate: function (items) {
|
|
if (!isArray(items)) {
|
|
items = [items];
|
|
}
|
|
var pushed = [];
|
|
var autoSync = this.options.autoSync;
|
|
this.options.autoSync = false;
|
|
try {
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
var item = items[idx];
|
|
var result = this.add(item);
|
|
pushed.push(result);
|
|
var pristine = result.toJSON();
|
|
if (this._isServerGrouped()) {
|
|
pristine = this._wrapInEmptyGroup(pristine);
|
|
}
|
|
this._pristineData.push(pristine);
|
|
}
|
|
} finally {
|
|
this.options.autoSync = autoSync;
|
|
}
|
|
if (pushed.length) {
|
|
this.trigger('push', {
|
|
type: 'create',
|
|
items: pushed
|
|
});
|
|
}
|
|
},
|
|
pushUpdate: function (items) {
|
|
if (!isArray(items)) {
|
|
items = [items];
|
|
}
|
|
var pushed = [];
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
var item = items[idx];
|
|
var model = this._createNewModel(item);
|
|
var target = this.get(model.id);
|
|
if (target) {
|
|
pushed.push(target);
|
|
target.accept(item);
|
|
target.trigger(CHANGE);
|
|
this._updatePristineForModel(target, item);
|
|
} else {
|
|
this.pushCreate(item);
|
|
}
|
|
}
|
|
if (pushed.length) {
|
|
this.trigger('push', {
|
|
type: 'update',
|
|
items: pushed
|
|
});
|
|
}
|
|
},
|
|
pushDestroy: function (items) {
|
|
var pushed = this._removeItems(items);
|
|
if (pushed.length) {
|
|
this.trigger('push', {
|
|
type: 'destroy',
|
|
items: pushed
|
|
});
|
|
}
|
|
},
|
|
_removeItems: function (items) {
|
|
if (!isArray(items)) {
|
|
items = [items];
|
|
}
|
|
var destroyed = [];
|
|
var autoSync = this.options.autoSync;
|
|
this.options.autoSync = false;
|
|
try {
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
var item = items[idx];
|
|
var model = this._createNewModel(item);
|
|
var found = false;
|
|
this._eachItem(this._data, function (items) {
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
var item = items.at(idx);
|
|
if (item.id === model.id) {
|
|
destroyed.push(item);
|
|
items.splice(idx, 1);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
if (found) {
|
|
this._removePristineForModel(model);
|
|
this._destroyed.pop();
|
|
}
|
|
}
|
|
} finally {
|
|
this.options.autoSync = autoSync;
|
|
}
|
|
return destroyed;
|
|
},
|
|
remove: function (model) {
|
|
var result, that = this, hasGroups = that._isServerGrouped();
|
|
this._eachItem(that._data, function (items) {
|
|
result = removeModel(items, model);
|
|
if (result && hasGroups) {
|
|
if (!result.isNew || !result.isNew()) {
|
|
that._destroyed.push(result);
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
this._removeModelFromRanges(model);
|
|
this._updateRangesLength();
|
|
return model;
|
|
},
|
|
destroyed: function () {
|
|
return this._destroyed;
|
|
},
|
|
created: function () {
|
|
var idx, length, result = [], data = this._flatData(this._data);
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].isNew && data[idx].isNew()) {
|
|
result.push(data[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
updated: function () {
|
|
var idx, length, result = [], data = this._flatData(this._data);
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].isNew && !data[idx].isNew() && data[idx].dirty) {
|
|
result.push(data[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
sync: function () {
|
|
var that = this, created = [], updated = [], destroyed = that._destroyed;
|
|
var promise = $.Deferred().resolve().promise();
|
|
if (that.online()) {
|
|
if (!that.reader.model) {
|
|
return promise;
|
|
}
|
|
created = that.created();
|
|
updated = that.updated();
|
|
var promises = [];
|
|
if (that.options.batch && that.transport.submit) {
|
|
promises = that._sendSubmit(created, updated, destroyed);
|
|
} else {
|
|
promises.push.apply(promises, that._send('create', created));
|
|
promises.push.apply(promises, that._send('update', updated));
|
|
promises.push.apply(promises, that._send('destroy', destroyed));
|
|
}
|
|
promise = $.when.apply(null, promises).then(function () {
|
|
var idx, length;
|
|
for (idx = 0, length = arguments.length; idx < length; idx++) {
|
|
that._accept(arguments[idx]);
|
|
}
|
|
that._storeData(true);
|
|
that._change({ action: 'sync' });
|
|
that.trigger(SYNC);
|
|
});
|
|
} else {
|
|
that._storeData(true);
|
|
that._change({ action: 'sync' });
|
|
}
|
|
return promise;
|
|
},
|
|
cancelChanges: function (model) {
|
|
var that = this;
|
|
if (model instanceof kendo.data.Model) {
|
|
that._cancelModel(model);
|
|
} else {
|
|
that._destroyed = [];
|
|
that._detachObservableParents();
|
|
that._data = that._observe(that._pristineData);
|
|
if (that.options.serverPaging) {
|
|
that._total = that._pristineTotal;
|
|
}
|
|
that._ranges = [];
|
|
that._addRange(that._data);
|
|
that._change();
|
|
that._markOfflineUpdatesAsDirty();
|
|
}
|
|
},
|
|
_markOfflineUpdatesAsDirty: function () {
|
|
var that = this;
|
|
if (that.options.offlineStorage != null) {
|
|
that._eachItem(that._data, function (items) {
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
var item = items.at(idx);
|
|
if (item.__state__ == 'update') {
|
|
item.dirty = true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
hasChanges: function () {
|
|
var idx, length, data = this._flatData(this._data);
|
|
if (this._destroyed.length) {
|
|
return true;
|
|
}
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].isNew && data[idx].isNew() || data[idx].dirty) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_accept: function (result) {
|
|
var that = this, models = result.models, response = result.response, idx = 0, serverGroup = that._isServerGrouped(), pristine = that._pristineData, type = result.type, length;
|
|
that.trigger(REQUESTEND, {
|
|
response: response,
|
|
type: type
|
|
});
|
|
if (response && !isEmptyObject(response)) {
|
|
response = that.reader.parse(response);
|
|
if (that._handleCustomErrors(response)) {
|
|
return;
|
|
}
|
|
response = that.reader.data(response);
|
|
if (!isArray(response)) {
|
|
response = [response];
|
|
}
|
|
} else {
|
|
response = $.map(models, function (model) {
|
|
return model.toJSON();
|
|
});
|
|
}
|
|
if (type === 'destroy') {
|
|
that._destroyed = [];
|
|
}
|
|
for (idx = 0, length = models.length; idx < length; idx++) {
|
|
if (type !== 'destroy') {
|
|
models[idx].accept(response[idx]);
|
|
if (type === 'create') {
|
|
pristine.push(serverGroup ? that._wrapInEmptyGroup(models[idx]) : response[idx]);
|
|
} else if (type === 'update') {
|
|
that._updatePristineForModel(models[idx], response[idx]);
|
|
}
|
|
} else {
|
|
that._removePristineForModel(models[idx]);
|
|
}
|
|
}
|
|
},
|
|
_updatePristineForModel: function (model, values) {
|
|
this._executeOnPristineForModel(model, function (index, items) {
|
|
kendo.deepExtend(items[index], values);
|
|
});
|
|
},
|
|
_executeOnPristineForModel: function (model, callback) {
|
|
this._eachPristineItem(function (items) {
|
|
var index = indexOfPristineModel(items, model);
|
|
if (index > -1) {
|
|
callback(index, items);
|
|
return true;
|
|
}
|
|
});
|
|
},
|
|
_removePristineForModel: function (model) {
|
|
this._executeOnPristineForModel(model, function (index, items) {
|
|
items.splice(index, 1);
|
|
});
|
|
},
|
|
_readData: function (data) {
|
|
var read = !this._isServerGrouped() ? this.reader.data : this.reader.groups;
|
|
return read.call(this.reader, data);
|
|
},
|
|
_eachPristineItem: function (callback) {
|
|
this._eachItem(this._pristineData, callback);
|
|
},
|
|
_eachItem: function (data, callback) {
|
|
if (data && data.length) {
|
|
if (this._isServerGrouped()) {
|
|
eachGroupItems(data, callback);
|
|
} else {
|
|
callback(data);
|
|
}
|
|
}
|
|
},
|
|
_pristineForModel: function (model) {
|
|
var pristine, idx, callback = function (items) {
|
|
idx = indexOfPristineModel(items, model);
|
|
if (idx > -1) {
|
|
pristine = items[idx];
|
|
return true;
|
|
}
|
|
};
|
|
this._eachPristineItem(callback);
|
|
return pristine;
|
|
},
|
|
_cancelModel: function (model) {
|
|
var pristine = this._pristineForModel(model);
|
|
this._eachItem(this._data, function (items) {
|
|
var idx = indexOfModel(items, model);
|
|
if (idx >= 0) {
|
|
if (pristine && (!model.isNew() || pristine.__state__)) {
|
|
items[idx].accept(pristine);
|
|
if (pristine.__state__ == 'update') {
|
|
items[idx].dirty = true;
|
|
}
|
|
} else {
|
|
items.splice(idx, 1);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_submit: function (promises, data) {
|
|
var that = this;
|
|
that.trigger(REQUESTSTART, { type: 'submit' });
|
|
that.transport.submit(extend({
|
|
success: function (response, type) {
|
|
var promise = $.grep(promises, function (x) {
|
|
return x.type == type;
|
|
})[0];
|
|
if (promise) {
|
|
promise.resolve({
|
|
response: response,
|
|
models: promise.models,
|
|
type: type
|
|
});
|
|
}
|
|
},
|
|
error: function (response, status, error) {
|
|
for (var idx = 0; idx < promises.length; idx++) {
|
|
promises[idx].reject(response);
|
|
}
|
|
that.error(response, status, error);
|
|
}
|
|
}, data));
|
|
},
|
|
_sendSubmit: function (created, updated, destroyed) {
|
|
var that = this, promises = [];
|
|
if (that.options.batch) {
|
|
if (created.length) {
|
|
promises.push($.Deferred(function (deferred) {
|
|
deferred.type = 'create';
|
|
deferred.models = created;
|
|
}));
|
|
}
|
|
if (updated.length) {
|
|
promises.push($.Deferred(function (deferred) {
|
|
deferred.type = 'update';
|
|
deferred.models = updated;
|
|
}));
|
|
}
|
|
if (destroyed.length) {
|
|
promises.push($.Deferred(function (deferred) {
|
|
deferred.type = 'destroy';
|
|
deferred.models = destroyed;
|
|
}));
|
|
}
|
|
that._submit(promises, {
|
|
data: {
|
|
created: that.reader.serialize(toJSON(created)),
|
|
updated: that.reader.serialize(toJSON(updated)),
|
|
destroyed: that.reader.serialize(toJSON(destroyed))
|
|
}
|
|
});
|
|
}
|
|
return promises;
|
|
},
|
|
_promise: function (data, models, type) {
|
|
var that = this;
|
|
return $.Deferred(function (deferred) {
|
|
that.trigger(REQUESTSTART, { type: type });
|
|
that.transport[type].call(that.transport, extend({
|
|
success: function (response) {
|
|
deferred.resolve({
|
|
response: response,
|
|
models: models,
|
|
type: type
|
|
});
|
|
},
|
|
error: function (response, status, error) {
|
|
deferred.reject(response);
|
|
that.error(response, status, error);
|
|
}
|
|
}, data));
|
|
}).promise();
|
|
},
|
|
_send: function (method, data) {
|
|
var that = this, idx, length, promises = [], converted = that.reader.serialize(toJSON(data));
|
|
if (that.options.batch) {
|
|
if (data.length) {
|
|
promises.push(that._promise({ data: { models: converted } }, data, method));
|
|
}
|
|
} else {
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
promises.push(that._promise({ data: converted[idx] }, [data[idx]], method));
|
|
}
|
|
}
|
|
return promises;
|
|
},
|
|
read: function (data) {
|
|
var that = this, params = that._params(data);
|
|
var deferred = $.Deferred();
|
|
that._queueRequest(params, function () {
|
|
var isPrevented = that.trigger(REQUESTSTART, { type: 'read' });
|
|
if (!isPrevented) {
|
|
that.trigger(PROGRESS);
|
|
that._ranges = [];
|
|
that.trigger('reset');
|
|
if (that.online()) {
|
|
that.transport.read({
|
|
data: params,
|
|
success: function (data) {
|
|
that.success(data, params);
|
|
deferred.resolve();
|
|
},
|
|
error: function () {
|
|
var args = slice.call(arguments);
|
|
that.error.apply(that, args);
|
|
deferred.reject.apply(deferred, args);
|
|
}
|
|
});
|
|
} else if (that.options.offlineStorage != null) {
|
|
that.success(that.offlineData(), params);
|
|
deferred.resolve();
|
|
}
|
|
} else {
|
|
that._dequeueRequest();
|
|
deferred.resolve(isPrevented);
|
|
}
|
|
});
|
|
return deferred.promise();
|
|
},
|
|
_readAggregates: function (data) {
|
|
return this.reader.aggregates(data);
|
|
},
|
|
success: function (data) {
|
|
var that = this, options = that.options;
|
|
that.trigger(REQUESTEND, {
|
|
response: data,
|
|
type: 'read'
|
|
});
|
|
if (that.online()) {
|
|
data = that.reader.parse(data);
|
|
if (that._handleCustomErrors(data)) {
|
|
that._dequeueRequest();
|
|
return;
|
|
}
|
|
that._total = that.reader.total(data);
|
|
if (that._aggregate && options.serverAggregates) {
|
|
that._aggregateResult = that._readAggregates(data);
|
|
}
|
|
data = that._readData(data);
|
|
} else {
|
|
data = that._readData(data);
|
|
var items = [];
|
|
var itemIds = {};
|
|
var model = that.reader.model;
|
|
var idField = model ? model.idField : 'id';
|
|
var idx;
|
|
for (idx = 0; idx < this._destroyed.length; idx++) {
|
|
var id = this._destroyed[idx][idField];
|
|
itemIds[id] = id;
|
|
}
|
|
for (idx = 0; idx < data.length; idx++) {
|
|
var item = data[idx];
|
|
var state = item.__state__;
|
|
if (state == 'destroy') {
|
|
if (!itemIds[item[idField]]) {
|
|
this._destroyed.push(this._createNewModel(item));
|
|
}
|
|
} else {
|
|
items.push(item);
|
|
}
|
|
}
|
|
data = items;
|
|
that._total = data.length;
|
|
}
|
|
that._pristineTotal = that._total;
|
|
that._pristineData = data.slice(0);
|
|
that._detachObservableParents();
|
|
that._data = that._observe(data);
|
|
that._markOfflineUpdatesAsDirty();
|
|
that._storeData();
|
|
that._addRange(that._data);
|
|
that._process(that._data);
|
|
that._dequeueRequest();
|
|
},
|
|
_detachObservableParents: function () {
|
|
if (this._data && this._shouldDetachObservableParents) {
|
|
for (var idx = 0; idx < this._data.length; idx++) {
|
|
if (this._data[idx].parent) {
|
|
this._data[idx].parent = noop;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_storeData: function (updatePristine) {
|
|
var serverGrouping = this._isServerGrouped();
|
|
var model = this.reader.model;
|
|
function items(data) {
|
|
var state = [];
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
var dataItem = data.at(idx);
|
|
var item = dataItem.toJSON();
|
|
if (serverGrouping && dataItem.items) {
|
|
item.items = items(dataItem.items);
|
|
} else {
|
|
item.uid = dataItem.uid;
|
|
if (model) {
|
|
if (dataItem.isNew()) {
|
|
item.__state__ = 'create';
|
|
} else if (dataItem.dirty) {
|
|
item.__state__ = 'update';
|
|
}
|
|
}
|
|
}
|
|
state.push(item);
|
|
}
|
|
return state;
|
|
}
|
|
if (this.options.offlineStorage != null) {
|
|
var state = items(this._data);
|
|
var destroyed = [];
|
|
for (var idx = 0; idx < this._destroyed.length; idx++) {
|
|
var item = this._destroyed[idx].toJSON();
|
|
item.__state__ = 'destroy';
|
|
destroyed.push(item);
|
|
}
|
|
this.offlineData(state.concat(destroyed));
|
|
if (updatePristine) {
|
|
this._pristineData = this._readData(state);
|
|
}
|
|
}
|
|
},
|
|
_addRange: function (data) {
|
|
var that = this, start = that._skip || 0, end = start + that._flatData(data, true).length;
|
|
that._ranges.push({
|
|
start: start,
|
|
end: end,
|
|
data: data,
|
|
timestamp: new Date().getTime()
|
|
});
|
|
that._ranges.sort(function (x, y) {
|
|
return x.start - y.start;
|
|
});
|
|
},
|
|
error: function (xhr, status, errorThrown) {
|
|
this._dequeueRequest();
|
|
this.trigger(REQUESTEND, {});
|
|
this.trigger(ERROR, {
|
|
xhr: xhr,
|
|
status: status,
|
|
errorThrown: errorThrown
|
|
});
|
|
},
|
|
_params: function (data) {
|
|
var that = this, options = extend({
|
|
take: that.take(),
|
|
skip: that.skip(),
|
|
page: that.page(),
|
|
pageSize: that.pageSize(),
|
|
sort: that._sort,
|
|
filter: that._filter,
|
|
group: that._group,
|
|
aggregate: that._aggregate
|
|
}, data);
|
|
if (!that.options.serverPaging) {
|
|
delete options.take;
|
|
delete options.skip;
|
|
delete options.page;
|
|
delete options.pageSize;
|
|
}
|
|
if (!that.options.serverGrouping) {
|
|
delete options.group;
|
|
} else if (that.reader.model && options.group) {
|
|
options.group = convertDescriptorsField(options.group, that.reader.model);
|
|
}
|
|
if (!that.options.serverFiltering) {
|
|
delete options.filter;
|
|
} else if (that.reader.model && options.filter) {
|
|
options.filter = convertFilterDescriptorsField(options.filter, that.reader.model);
|
|
}
|
|
if (!that.options.serverSorting) {
|
|
delete options.sort;
|
|
} else if (that.reader.model && options.sort) {
|
|
options.sort = convertDescriptorsField(options.sort, that.reader.model);
|
|
}
|
|
if (!that.options.serverAggregates) {
|
|
delete options.aggregate;
|
|
} else if (that.reader.model && options.aggregate) {
|
|
options.aggregate = convertDescriptorsField(options.aggregate, that.reader.model);
|
|
}
|
|
return options;
|
|
},
|
|
_queueRequest: function (options, callback) {
|
|
var that = this;
|
|
if (!that._requestInProgress) {
|
|
that._requestInProgress = true;
|
|
that._pending = undefined;
|
|
callback();
|
|
} else {
|
|
that._pending = {
|
|
callback: proxy(callback, that),
|
|
options: options
|
|
};
|
|
}
|
|
},
|
|
_dequeueRequest: function () {
|
|
var that = this;
|
|
that._requestInProgress = false;
|
|
if (that._pending) {
|
|
that._queueRequest(that._pending.options, that._pending.callback);
|
|
}
|
|
},
|
|
_handleCustomErrors: function (response) {
|
|
if (this.reader.errors) {
|
|
var errors = this.reader.errors(response);
|
|
if (errors) {
|
|
this.trigger(ERROR, {
|
|
xhr: null,
|
|
status: 'customerror',
|
|
errorThrown: 'custom error',
|
|
errors: errors
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_shouldWrap: function (data) {
|
|
var model = this.reader.model;
|
|
if (model && data.length) {
|
|
return !(data[0] instanceof model);
|
|
}
|
|
return false;
|
|
},
|
|
_observe: function (data) {
|
|
var that = this, model = that.reader.model;
|
|
that._shouldDetachObservableParents = true;
|
|
if (data instanceof ObservableArray) {
|
|
that._shouldDetachObservableParents = false;
|
|
if (that._shouldWrap(data)) {
|
|
data.type = that.reader.model;
|
|
data.wrapAll(data, data);
|
|
}
|
|
} else {
|
|
var arrayType = that.pageSize() && !that.options.serverPaging ? LazyObservableArray : ObservableArray;
|
|
data = new arrayType(data, that.reader.model);
|
|
data.parent = function () {
|
|
return that.parent();
|
|
};
|
|
}
|
|
if (that._isServerGrouped()) {
|
|
wrapGroupItems(data, model);
|
|
}
|
|
if (that._changeHandler && that._data && that._data instanceof ObservableArray) {
|
|
that._data.unbind(CHANGE, that._changeHandler);
|
|
} else {
|
|
that._changeHandler = proxy(that._change, that);
|
|
}
|
|
return data.bind(CHANGE, that._changeHandler);
|
|
},
|
|
_updateTotalForAction: function (action, items) {
|
|
var that = this;
|
|
var total = parseInt(that._total, 10);
|
|
if (!isNumber(that._total)) {
|
|
total = parseInt(that._pristineTotal, 10);
|
|
}
|
|
if (action === 'add') {
|
|
total += items.length;
|
|
} else if (action === 'remove') {
|
|
total -= items.length;
|
|
} else if (action !== 'itemchange' && action !== 'sync' && !that.options.serverPaging) {
|
|
total = that._pristineTotal;
|
|
} else if (action === 'sync') {
|
|
total = that._pristineTotal = parseInt(that._total, 10);
|
|
}
|
|
that._total = total;
|
|
},
|
|
_change: function (e) {
|
|
var that = this, idx, length, action = e ? e.action : '';
|
|
if (action === 'remove') {
|
|
for (idx = 0, length = e.items.length; idx < length; idx++) {
|
|
if (!e.items[idx].isNew || !e.items[idx].isNew()) {
|
|
that._destroyed.push(e.items[idx]);
|
|
}
|
|
}
|
|
}
|
|
if (that.options.autoSync && (action === 'add' || action === 'remove' || action === 'itemchange')) {
|
|
var handler = function (args) {
|
|
if (args.action === 'sync') {
|
|
that.unbind('change', handler);
|
|
that._updateTotalForAction(action, e.items);
|
|
}
|
|
};
|
|
that.first('change', handler);
|
|
that.sync();
|
|
} else {
|
|
that._updateTotalForAction(action, e ? e.items : []);
|
|
that._process(that._data, e);
|
|
}
|
|
},
|
|
_calculateAggregates: function (data, options) {
|
|
options = options || {};
|
|
var query = new Query(data), aggregates = options.aggregate, filter = options.filter;
|
|
if (filter) {
|
|
query = query.filter(filter);
|
|
}
|
|
return query.aggregate(aggregates);
|
|
},
|
|
_process: function (data, e) {
|
|
var that = this, options = {}, result;
|
|
if (that.options.serverPaging !== true) {
|
|
options.skip = that._skip;
|
|
options.take = that._take || that._pageSize;
|
|
if (options.skip === undefined && that._page !== undefined && that._pageSize !== undefined) {
|
|
options.skip = (that._page - 1) * that._pageSize;
|
|
}
|
|
}
|
|
if (that.options.serverSorting !== true) {
|
|
options.sort = that._sort;
|
|
}
|
|
if (that.options.serverFiltering !== true) {
|
|
options.filter = that._filter;
|
|
}
|
|
if (that.options.serverGrouping !== true) {
|
|
options.group = that._group;
|
|
}
|
|
if (that.options.serverAggregates !== true) {
|
|
options.aggregate = that._aggregate;
|
|
that._aggregateResult = that._calculateAggregates(data, options);
|
|
}
|
|
result = that._queryProcess(data, options);
|
|
that.view(result.data);
|
|
if (result.total !== undefined && !that.options.serverFiltering) {
|
|
that._total = result.total;
|
|
}
|
|
e = e || {};
|
|
e.items = e.items || that._view;
|
|
that.trigger(CHANGE, e);
|
|
},
|
|
_queryProcess: function (data, options) {
|
|
return Query.process(data, options);
|
|
},
|
|
_mergeState: function (options) {
|
|
var that = this;
|
|
if (options !== undefined) {
|
|
that._pageSize = options.pageSize;
|
|
that._page = options.page;
|
|
that._sort = options.sort;
|
|
that._filter = options.filter;
|
|
that._group = options.group;
|
|
that._aggregate = options.aggregate;
|
|
that._skip = that._currentRangeStart = options.skip;
|
|
that._take = options.take;
|
|
if (that._skip === undefined) {
|
|
that._skip = that._currentRangeStart = that.skip();
|
|
options.skip = that.skip();
|
|
}
|
|
if (that._take === undefined && that._pageSize !== undefined) {
|
|
that._take = that._pageSize;
|
|
options.take = that._take;
|
|
}
|
|
if (options.sort) {
|
|
that._sort = options.sort = normalizeSort(options.sort);
|
|
}
|
|
if (options.filter) {
|
|
that._filter = options.filter = normalizeFilter(options.filter);
|
|
}
|
|
if (options.group) {
|
|
that._group = options.group = normalizeGroup(options.group);
|
|
}
|
|
if (options.aggregate) {
|
|
that._aggregate = options.aggregate = normalizeAggregate(options.aggregate);
|
|
}
|
|
}
|
|
return options;
|
|
},
|
|
query: function (options) {
|
|
var result;
|
|
var remote = this.options.serverSorting || this.options.serverPaging || this.options.serverFiltering || this.options.serverGrouping || this.options.serverAggregates;
|
|
if (remote || (this._data === undefined || this._data.length === 0) && !this._destroyed.length) {
|
|
return this.read(this._mergeState(options));
|
|
}
|
|
var isPrevented = this.trigger(REQUESTSTART, { type: 'read' });
|
|
if (!isPrevented) {
|
|
this.trigger(PROGRESS);
|
|
result = this._queryProcess(this._data, this._mergeState(options));
|
|
if (!this.options.serverFiltering) {
|
|
if (result.total !== undefined) {
|
|
this._total = result.total;
|
|
} else {
|
|
this._total = this._data.length;
|
|
}
|
|
}
|
|
this._aggregateResult = this._calculateAggregates(this._data, options);
|
|
this.view(result.data);
|
|
this.trigger(REQUESTEND, { type: 'read' });
|
|
this.trigger(CHANGE, { items: result.data });
|
|
}
|
|
return $.Deferred().resolve(isPrevented).promise();
|
|
},
|
|
fetch: function (callback) {
|
|
var that = this;
|
|
var fn = function (isPrevented) {
|
|
if (isPrevented !== true && isFunction(callback)) {
|
|
callback.call(that);
|
|
}
|
|
};
|
|
return this._query().then(fn);
|
|
},
|
|
_query: function (options) {
|
|
var that = this;
|
|
return that.query(extend({}, {
|
|
page: that.page(),
|
|
pageSize: that.pageSize(),
|
|
sort: that.sort(),
|
|
filter: that.filter(),
|
|
group: that.group(),
|
|
aggregate: that.aggregate()
|
|
}, options));
|
|
},
|
|
next: function (options) {
|
|
var that = this, page = that.page(), total = that.total();
|
|
options = options || {};
|
|
if (!page || total && page + 1 > that.totalPages()) {
|
|
return;
|
|
}
|
|
that._skip = that._currentRangeStart = page * that.take();
|
|
page += 1;
|
|
options.page = page;
|
|
that._query(options);
|
|
return page;
|
|
},
|
|
prev: function (options) {
|
|
var that = this, page = that.page();
|
|
options = options || {};
|
|
if (!page || page === 1) {
|
|
return;
|
|
}
|
|
that._skip = that._currentRangeStart = that._skip - that.take();
|
|
page -= 1;
|
|
options.page = page;
|
|
that._query(options);
|
|
return page;
|
|
},
|
|
page: function (val) {
|
|
var that = this, skip;
|
|
if (val !== undefined) {
|
|
val = math.max(math.min(math.max(val, 1), that.totalPages()), 1);
|
|
that._query({ page: val });
|
|
return;
|
|
}
|
|
skip = that.skip();
|
|
return skip !== undefined ? math.round((skip || 0) / (that.take() || 1)) + 1 : undefined;
|
|
},
|
|
pageSize: function (val) {
|
|
var that = this;
|
|
if (val !== undefined) {
|
|
that._query({
|
|
pageSize: val,
|
|
page: 1
|
|
});
|
|
return;
|
|
}
|
|
return that.take();
|
|
},
|
|
sort: function (val) {
|
|
var that = this;
|
|
if (val !== undefined) {
|
|
that._query({ sort: val });
|
|
return;
|
|
}
|
|
return that._sort;
|
|
},
|
|
filter: function (val) {
|
|
var that = this;
|
|
if (val === undefined) {
|
|
return that._filter;
|
|
}
|
|
that.trigger('reset');
|
|
that._query({
|
|
filter: val,
|
|
page: 1
|
|
});
|
|
},
|
|
group: function (val) {
|
|
var that = this;
|
|
if (val !== undefined) {
|
|
that._query({ group: val });
|
|
return;
|
|
}
|
|
return that._group;
|
|
},
|
|
total: function () {
|
|
return parseInt(this._total || 0, 10);
|
|
},
|
|
aggregate: function (val) {
|
|
var that = this;
|
|
if (val !== undefined) {
|
|
that._query({ aggregate: val });
|
|
return;
|
|
}
|
|
return that._aggregate;
|
|
},
|
|
aggregates: function () {
|
|
var result = this._aggregateResult;
|
|
if (isEmptyObject(result)) {
|
|
result = this._emptyAggregates(this.aggregate());
|
|
}
|
|
return result;
|
|
},
|
|
_emptyAggregates: function (aggregates) {
|
|
var result = {};
|
|
if (!isEmptyObject(aggregates)) {
|
|
var aggregate = {};
|
|
if (!isArray(aggregates)) {
|
|
aggregates = [aggregates];
|
|
}
|
|
for (var idx = 0; idx < aggregates.length; idx++) {
|
|
aggregate[aggregates[idx].aggregate] = 0;
|
|
result[aggregates[idx].field] = aggregate;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_wrapInEmptyGroup: function (model) {
|
|
var groups = this.group(), parent, group, idx, length;
|
|
for (idx = groups.length - 1, length = 0; idx >= length; idx--) {
|
|
group = groups[idx];
|
|
parent = {
|
|
value: model.get(group.field),
|
|
field: group.field,
|
|
items: parent ? [parent] : [model],
|
|
hasSubgroups: !!parent,
|
|
aggregates: this._emptyAggregates(group.aggregates)
|
|
};
|
|
}
|
|
return parent;
|
|
},
|
|
totalPages: function () {
|
|
var that = this, pageSize = that.pageSize() || that.total();
|
|
return math.ceil((that.total() || 0) / pageSize);
|
|
},
|
|
inRange: function (skip, take) {
|
|
var that = this, end = math.min(skip + take, that.total());
|
|
if (!that.options.serverPaging && that._data.length > 0) {
|
|
return true;
|
|
}
|
|
return that._findRange(skip, end).length > 0;
|
|
},
|
|
lastRange: function () {
|
|
var ranges = this._ranges;
|
|
return ranges[ranges.length - 1] || {
|
|
start: 0,
|
|
end: 0,
|
|
data: []
|
|
};
|
|
},
|
|
firstItemUid: function () {
|
|
var ranges = this._ranges;
|
|
return ranges.length && ranges[0].data.length && ranges[0].data[0].uid;
|
|
},
|
|
enableRequestsInProgress: function () {
|
|
this._skipRequestsInProgress = false;
|
|
},
|
|
_timeStamp: function () {
|
|
return new Date().getTime();
|
|
},
|
|
range: function (skip, take) {
|
|
this._currentRequestTimeStamp = this._timeStamp();
|
|
this._skipRequestsInProgress = true;
|
|
skip = math.min(skip || 0, this.total());
|
|
var that = this, pageSkip = math.max(math.floor(skip / take), 0) * take, size = math.min(pageSkip + take, that.total()), data;
|
|
data = that._findRange(skip, math.min(skip + take, that.total()));
|
|
if (data.length) {
|
|
that._pending = undefined;
|
|
that._skip = skip > that.skip() ? math.min(size, (that.totalPages() - 1) * that.take()) : pageSkip;
|
|
that._currentRangeStart = skip;
|
|
that._take = take;
|
|
var paging = that.options.serverPaging;
|
|
var sorting = that.options.serverSorting;
|
|
var filtering = that.options.serverFiltering;
|
|
var aggregates = that.options.serverAggregates;
|
|
try {
|
|
that.options.serverPaging = true;
|
|
if (!that._isServerGrouped() && !(that.group() && that.group().length)) {
|
|
that.options.serverSorting = true;
|
|
}
|
|
that.options.serverFiltering = true;
|
|
that.options.serverPaging = true;
|
|
that.options.serverAggregates = true;
|
|
if (paging) {
|
|
that._detachObservableParents();
|
|
that._data = data = that._observe(data);
|
|
}
|
|
that._process(data);
|
|
} finally {
|
|
that.options.serverPaging = paging;
|
|
that.options.serverSorting = sorting;
|
|
that.options.serverFiltering = filtering;
|
|
that.options.serverAggregates = aggregates;
|
|
}
|
|
return;
|
|
}
|
|
if (take !== undefined) {
|
|
if (!that._rangeExists(pageSkip, size)) {
|
|
that.prefetch(pageSkip, take, function () {
|
|
if (skip > pageSkip && size < that.total() && !that._rangeExists(size, math.min(size + take, that.total()))) {
|
|
that.prefetch(size, take, function () {
|
|
that.range(skip, take);
|
|
});
|
|
} else {
|
|
that.range(skip, take);
|
|
}
|
|
});
|
|
} else if (pageSkip < skip) {
|
|
that.prefetch(size, take, function () {
|
|
that.range(skip, take);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_findRange: function (start, end) {
|
|
var that = this, ranges = that._ranges, range, data = [], skipIdx, takeIdx, startIndex, endIndex, rangeData, rangeEnd, processed, options = that.options, remote = options.serverSorting || options.serverPaging || options.serverFiltering || options.serverGrouping || options.serverAggregates, flatData, count, length;
|
|
for (skipIdx = 0, length = ranges.length; skipIdx < length; skipIdx++) {
|
|
range = ranges[skipIdx];
|
|
if (start >= range.start && start <= range.end) {
|
|
count = 0;
|
|
for (takeIdx = skipIdx; takeIdx < length; takeIdx++) {
|
|
range = ranges[takeIdx];
|
|
flatData = that._flatData(range.data, true);
|
|
if (flatData.length && start + count >= range.start) {
|
|
rangeData = range.data;
|
|
rangeEnd = range.end;
|
|
if (!remote) {
|
|
var sort = normalizeGroup(that.group() || []).concat(normalizeSort(that.sort() || []));
|
|
processed = that._queryProcess(range.data, {
|
|
sort: sort,
|
|
filter: that.filter()
|
|
});
|
|
flatData = rangeData = processed.data;
|
|
if (processed.total !== undefined) {
|
|
rangeEnd = processed.total;
|
|
}
|
|
}
|
|
startIndex = 0;
|
|
if (start + count > range.start) {
|
|
startIndex = start + count - range.start;
|
|
}
|
|
endIndex = flatData.length;
|
|
if (rangeEnd > end) {
|
|
endIndex = endIndex - (rangeEnd - end);
|
|
}
|
|
count += endIndex - startIndex;
|
|
data = that._mergeGroups(data, rangeData, startIndex, endIndex);
|
|
if (end <= range.end && count == end - start) {
|
|
return data;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return [];
|
|
},
|
|
_mergeGroups: function (data, range, skip, take) {
|
|
if (this._isServerGrouped()) {
|
|
var temp = range.toJSON(), prevGroup;
|
|
if (data.length) {
|
|
prevGroup = data[data.length - 1];
|
|
}
|
|
mergeGroups(prevGroup, temp, skip, take);
|
|
return data.concat(temp);
|
|
}
|
|
return data.concat(range.slice(skip, take));
|
|
},
|
|
skip: function () {
|
|
var that = this;
|
|
if (that._skip === undefined) {
|
|
return that._page !== undefined ? (that._page - 1) * (that.take() || 1) : undefined;
|
|
}
|
|
return that._skip;
|
|
},
|
|
currentRangeStart: function () {
|
|
return this._currentRangeStart || 0;
|
|
},
|
|
take: function () {
|
|
return this._take || this._pageSize;
|
|
},
|
|
_prefetchSuccessHandler: function (skip, size, callback, force) {
|
|
var that = this;
|
|
var timestamp = that._timeStamp();
|
|
return function (data) {
|
|
var found = false, range = {
|
|
start: skip,
|
|
end: size,
|
|
data: [],
|
|
timestamp: that._timeStamp()
|
|
}, idx, length, temp;
|
|
that._dequeueRequest();
|
|
that.trigger(REQUESTEND, {
|
|
response: data,
|
|
type: 'read'
|
|
});
|
|
data = that.reader.parse(data);
|
|
temp = that._readData(data);
|
|
if (temp.length) {
|
|
for (idx = 0, length = that._ranges.length; idx < length; idx++) {
|
|
if (that._ranges[idx].start === skip) {
|
|
found = true;
|
|
range = that._ranges[idx];
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
that._ranges.push(range);
|
|
}
|
|
}
|
|
range.data = that._observe(temp);
|
|
range.end = range.start + that._flatData(range.data, true).length;
|
|
that._ranges.sort(function (x, y) {
|
|
return x.start - y.start;
|
|
});
|
|
that._total = that.reader.total(data);
|
|
if (force || (timestamp >= that._currentRequestTimeStamp || !that._skipRequestsInProgress)) {
|
|
if (callback && temp.length) {
|
|
callback();
|
|
} else {
|
|
that.trigger(CHANGE, {});
|
|
}
|
|
}
|
|
};
|
|
},
|
|
prefetch: function (skip, take, callback) {
|
|
var that = this, size = math.min(skip + take, that.total()), options = {
|
|
take: take,
|
|
skip: skip,
|
|
page: skip / take + 1,
|
|
pageSize: take,
|
|
sort: that._sort,
|
|
filter: that._filter,
|
|
group: that._group,
|
|
aggregate: that._aggregate
|
|
};
|
|
if (!that._rangeExists(skip, size)) {
|
|
clearTimeout(that._timeout);
|
|
that._timeout = setTimeout(function () {
|
|
that._queueRequest(options, function () {
|
|
if (!that.trigger(REQUESTSTART, { type: 'read' })) {
|
|
that.transport.read({
|
|
data: that._params(options),
|
|
success: that._prefetchSuccessHandler(skip, size, callback),
|
|
error: function () {
|
|
var args = slice.call(arguments);
|
|
that.error.apply(that, args);
|
|
}
|
|
});
|
|
} else {
|
|
that._dequeueRequest();
|
|
}
|
|
});
|
|
}, 100);
|
|
} else if (callback) {
|
|
callback();
|
|
}
|
|
},
|
|
_multiplePrefetch: function (skip, take, callback) {
|
|
var that = this, size = math.min(skip + take, that.total()), options = {
|
|
take: take,
|
|
skip: skip,
|
|
page: skip / take + 1,
|
|
pageSize: take,
|
|
sort: that._sort,
|
|
filter: that._filter,
|
|
group: that._group,
|
|
aggregate: that._aggregate
|
|
};
|
|
if (!that._rangeExists(skip, size)) {
|
|
if (!that.trigger(REQUESTSTART, { type: 'read' })) {
|
|
that.transport.read({
|
|
data: that._params(options),
|
|
success: that._prefetchSuccessHandler(skip, size, callback, true)
|
|
});
|
|
}
|
|
} else if (callback) {
|
|
callback();
|
|
}
|
|
},
|
|
_rangeExists: function (start, end) {
|
|
var that = this, ranges = that._ranges, idx, length;
|
|
for (idx = 0, length = ranges.length; idx < length; idx++) {
|
|
if (ranges[idx].start <= start && ranges[idx].end >= end) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_removeModelFromRanges: function (model) {
|
|
var result, found, range;
|
|
for (var idx = 0, length = this._ranges.length; idx < length; idx++) {
|
|
range = this._ranges[idx];
|
|
this._eachItem(range.data, function (items) {
|
|
result = removeModel(items, model);
|
|
if (result) {
|
|
found = true;
|
|
}
|
|
});
|
|
if (found) {
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
_updateRangesLength: function () {
|
|
var startOffset = 0, range, rangeLength;
|
|
for (var idx = 0, length = this._ranges.length; idx < length; idx++) {
|
|
range = this._ranges[idx];
|
|
range.start = range.start - startOffset;
|
|
rangeLength = this._flatData(range.data, true).length;
|
|
startOffset = range.end - rangeLength;
|
|
range.end = range.start + rangeLength;
|
|
}
|
|
}
|
|
});
|
|
var Transport = {};
|
|
Transport.create = function (options, data, dataSource) {
|
|
var transport, transportOptions = options.transport ? $.extend({}, options.transport) : null;
|
|
if (transportOptions) {
|
|
transportOptions.read = typeof transportOptions.read === STRING ? { url: transportOptions.read } : transportOptions.read;
|
|
if (options.type === 'jsdo') {
|
|
transportOptions.dataSource = dataSource;
|
|
}
|
|
if (options.type) {
|
|
kendo.data.transports = kendo.data.transports || {};
|
|
kendo.data.schemas = kendo.data.schemas || {};
|
|
if (!kendo.data.transports[options.type]) {
|
|
kendo.logToConsole('Unknown DataSource transport type \'' + options.type + '\'.\nVerify that registration scripts for this type are included after Kendo UI on the page.', 'warn');
|
|
} else if (!isPlainObject(kendo.data.transports[options.type])) {
|
|
transport = new kendo.data.transports[options.type](extend(transportOptions, { data: data }));
|
|
} else {
|
|
transportOptions = extend(true, {}, kendo.data.transports[options.type], transportOptions);
|
|
}
|
|
options.schema = extend(true, {}, kendo.data.schemas[options.type], options.schema);
|
|
}
|
|
if (!transport) {
|
|
transport = isFunction(transportOptions.read) ? transportOptions : new RemoteTransport(transportOptions);
|
|
}
|
|
} else {
|
|
transport = new LocalTransport({ data: options.data || [] });
|
|
}
|
|
return transport;
|
|
};
|
|
DataSource.create = function (options) {
|
|
if (isArray(options) || options instanceof ObservableArray) {
|
|
options = { data: options };
|
|
}
|
|
var dataSource = options || {}, data = dataSource.data, fields = dataSource.fields, table = dataSource.table, select = dataSource.select, idx, length, model = {}, field;
|
|
if (!data && fields && !dataSource.transport) {
|
|
if (table) {
|
|
data = inferTable(table, fields);
|
|
} else if (select) {
|
|
data = inferSelect(select, fields);
|
|
if (dataSource.group === undefined && data[0] && data[0].optgroup !== undefined) {
|
|
dataSource.group = 'optgroup';
|
|
}
|
|
}
|
|
}
|
|
if (kendo.data.Model && fields && (!dataSource.schema || !dataSource.schema.model)) {
|
|
for (idx = 0, length = fields.length; idx < length; idx++) {
|
|
field = fields[idx];
|
|
if (field.type) {
|
|
model[field.field] = field;
|
|
}
|
|
}
|
|
if (!isEmptyObject(model)) {
|
|
dataSource.schema = extend(true, dataSource.schema, { model: { fields: model } });
|
|
}
|
|
}
|
|
dataSource.data = data;
|
|
select = null;
|
|
dataSource.select = null;
|
|
table = null;
|
|
dataSource.table = null;
|
|
return dataSource instanceof DataSource ? dataSource : new DataSource(dataSource);
|
|
};
|
|
function inferSelect(select, fields) {
|
|
select = $(select)[0];
|
|
var options = select.options;
|
|
var firstField = fields[0];
|
|
var secondField = fields[1];
|
|
var data = [];
|
|
var idx, length;
|
|
var optgroup;
|
|
var option;
|
|
var record;
|
|
var value;
|
|
for (idx = 0, length = options.length; idx < length; idx++) {
|
|
record = {};
|
|
option = options[idx];
|
|
optgroup = option.parentNode;
|
|
if (optgroup === select) {
|
|
optgroup = null;
|
|
}
|
|
if (option.disabled || optgroup && optgroup.disabled) {
|
|
continue;
|
|
}
|
|
if (optgroup) {
|
|
record.optgroup = optgroup.label;
|
|
}
|
|
record[firstField.field] = option.text;
|
|
value = option.attributes.value;
|
|
if (value && value.specified) {
|
|
value = option.value;
|
|
} else {
|
|
value = option.text;
|
|
}
|
|
record[secondField.field] = value;
|
|
data.push(record);
|
|
}
|
|
return data;
|
|
}
|
|
function inferTable(table, fields) {
|
|
var tbody = $(table)[0].tBodies[0], rows = tbody ? tbody.rows : [], idx, length, fieldIndex, fieldCount = fields.length, data = [], cells, record, cell, empty;
|
|
for (idx = 0, length = rows.length; idx < length; idx++) {
|
|
record = {};
|
|
empty = true;
|
|
cells = rows[idx].cells;
|
|
for (fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) {
|
|
cell = cells[fieldIndex];
|
|
if (cell.nodeName.toLowerCase() !== 'th') {
|
|
empty = false;
|
|
record[fields[fieldIndex].field] = cell.innerHTML;
|
|
}
|
|
}
|
|
if (!empty) {
|
|
data.push(record);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
var Node = Model.define({
|
|
idField: 'id',
|
|
init: function (value) {
|
|
var that = this, hasChildren = that.hasChildren || value && value.hasChildren, childrenField = 'items', childrenOptions = {};
|
|
kendo.data.Model.fn.init.call(that, value);
|
|
if (typeof that.children === STRING) {
|
|
childrenField = that.children;
|
|
}
|
|
childrenOptions = {
|
|
schema: {
|
|
data: childrenField,
|
|
model: {
|
|
hasChildren: hasChildren,
|
|
id: that.idField,
|
|
fields: that.fields
|
|
}
|
|
}
|
|
};
|
|
if (typeof that.children !== STRING) {
|
|
extend(childrenOptions, that.children);
|
|
}
|
|
childrenOptions.data = value;
|
|
if (!hasChildren) {
|
|
hasChildren = childrenOptions.schema.data;
|
|
}
|
|
if (typeof hasChildren === STRING) {
|
|
hasChildren = kendo.getter(hasChildren);
|
|
}
|
|
if (isFunction(hasChildren)) {
|
|
that.hasChildren = !!hasChildren.call(that, that);
|
|
}
|
|
that._childrenOptions = childrenOptions;
|
|
if (that.hasChildren) {
|
|
that._initChildren();
|
|
}
|
|
that._loaded = !!(value && value._loaded);
|
|
},
|
|
_initChildren: function () {
|
|
var that = this;
|
|
var children, transport, parameterMap;
|
|
if (!(that.children instanceof HierarchicalDataSource)) {
|
|
children = that.children = new HierarchicalDataSource(that._childrenOptions);
|
|
transport = children.transport;
|
|
parameterMap = transport.parameterMap;
|
|
transport.parameterMap = function (data, type) {
|
|
data[that.idField || 'id'] = that.id;
|
|
if (parameterMap) {
|
|
data = parameterMap(data, type);
|
|
}
|
|
return data;
|
|
};
|
|
children.parent = function () {
|
|
return that;
|
|
};
|
|
children.bind(CHANGE, function (e) {
|
|
e.node = e.node || that;
|
|
that.trigger(CHANGE, e);
|
|
});
|
|
children.bind(ERROR, function (e) {
|
|
var collection = that.parent();
|
|
if (collection) {
|
|
e.node = e.node || that;
|
|
collection.trigger(ERROR, e);
|
|
}
|
|
});
|
|
that._updateChildrenField();
|
|
}
|
|
},
|
|
append: function (model) {
|
|
this._initChildren();
|
|
this.loaded(true);
|
|
this.children.add(model);
|
|
},
|
|
hasChildren: false,
|
|
level: function () {
|
|
var parentNode = this.parentNode(), level = 0;
|
|
while (parentNode && parentNode.parentNode) {
|
|
level++;
|
|
parentNode = parentNode.parentNode ? parentNode.parentNode() : null;
|
|
}
|
|
return level;
|
|
},
|
|
_updateChildrenField: function () {
|
|
var fieldName = this._childrenOptions.schema.data;
|
|
this[fieldName || 'items'] = this.children.data();
|
|
},
|
|
_childrenLoaded: function () {
|
|
this._loaded = true;
|
|
this._updateChildrenField();
|
|
},
|
|
load: function () {
|
|
var options = {};
|
|
var method = '_query';
|
|
var children, promise;
|
|
if (this.hasChildren) {
|
|
this._initChildren();
|
|
children = this.children;
|
|
options[this.idField || 'id'] = this.id;
|
|
if (!this._loaded) {
|
|
children._data = undefined;
|
|
method = 'read';
|
|
}
|
|
children.one(CHANGE, proxy(this._childrenLoaded, this));
|
|
promise = children[method](options);
|
|
} else {
|
|
this.loaded(true);
|
|
}
|
|
return promise || $.Deferred().resolve().promise();
|
|
},
|
|
parentNode: function () {
|
|
var array = this.parent();
|
|
return array.parent();
|
|
},
|
|
loaded: function (value) {
|
|
if (value !== undefined) {
|
|
this._loaded = value;
|
|
} else {
|
|
return this._loaded;
|
|
}
|
|
},
|
|
shouldSerialize: function (field) {
|
|
return Model.fn.shouldSerialize.call(this, field) && field !== 'children' && field !== '_loaded' && field !== 'hasChildren' && field !== '_childrenOptions';
|
|
}
|
|
});
|
|
function dataMethod(name) {
|
|
return function () {
|
|
var data = this._data, result = DataSource.fn[name].apply(this, slice.call(arguments));
|
|
if (this._data != data) {
|
|
this._attachBubbleHandlers();
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
var HierarchicalDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
var node = Node.define({ children: options });
|
|
DataSource.fn.init.call(this, extend(true, {}, {
|
|
schema: {
|
|
modelBase: node,
|
|
model: node
|
|
}
|
|
}, options));
|
|
this._attachBubbleHandlers();
|
|
},
|
|
_attachBubbleHandlers: function () {
|
|
var that = this;
|
|
that._data.bind(ERROR, function (e) {
|
|
that.trigger(ERROR, e);
|
|
});
|
|
},
|
|
remove: function (node) {
|
|
var parentNode = node.parentNode(), dataSource = this, result;
|
|
if (parentNode && parentNode._initChildren) {
|
|
dataSource = parentNode.children;
|
|
}
|
|
result = DataSource.fn.remove.call(dataSource, node);
|
|
if (parentNode && !dataSource.data().length) {
|
|
parentNode.hasChildren = false;
|
|
}
|
|
return result;
|
|
},
|
|
success: dataMethod('success'),
|
|
data: dataMethod('data'),
|
|
insert: function (index, model) {
|
|
var parentNode = this.parent();
|
|
if (parentNode && parentNode._initChildren) {
|
|
parentNode.hasChildren = true;
|
|
parentNode._initChildren();
|
|
}
|
|
return DataSource.fn.insert.call(this, index, model);
|
|
},
|
|
_find: function (method, value) {
|
|
var idx, length, node, children;
|
|
var data = this._data;
|
|
if (!data) {
|
|
return;
|
|
}
|
|
node = DataSource.fn[method].call(this, value);
|
|
if (node) {
|
|
return node;
|
|
}
|
|
data = this._flatData(this._data);
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
children = data[idx].children;
|
|
if (!(children instanceof HierarchicalDataSource)) {
|
|
continue;
|
|
}
|
|
node = children[method](value);
|
|
if (node) {
|
|
return node;
|
|
}
|
|
}
|
|
},
|
|
get: function (id) {
|
|
return this._find('get', id);
|
|
},
|
|
getByUid: function (uid) {
|
|
return this._find('getByUid', uid);
|
|
}
|
|
});
|
|
function inferList(list, fields) {
|
|
var items = $(list).children(), idx, length, data = [], record, textField = fields[0].field, urlField = fields[1] && fields[1].field, spriteCssClassField = fields[2] && fields[2].field, imageUrlField = fields[3] && fields[3].field, item, id, textChild, className, children;
|
|
function elements(collection, tagName) {
|
|
return collection.filter(tagName).add(collection.find(tagName));
|
|
}
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
record = { _loaded: true };
|
|
item = items.eq(idx);
|
|
textChild = item[0].firstChild;
|
|
children = item.children();
|
|
list = children.filter('ul');
|
|
children = children.filter(':not(ul)');
|
|
id = item.attr('data-id');
|
|
if (id) {
|
|
record.id = id;
|
|
}
|
|
if (textChild) {
|
|
record[textField] = textChild.nodeType == 3 ? textChild.nodeValue : children.text();
|
|
}
|
|
if (urlField) {
|
|
record[urlField] = elements(children, 'a').attr('href');
|
|
}
|
|
if (imageUrlField) {
|
|
record[imageUrlField] = elements(children, 'img').attr('src');
|
|
}
|
|
if (spriteCssClassField) {
|
|
className = elements(children, '.k-sprite').prop('className');
|
|
record[spriteCssClassField] = className && $.trim(className.replace('k-sprite', ''));
|
|
}
|
|
if (list.length) {
|
|
record.items = inferList(list.eq(0), fields);
|
|
}
|
|
if (item.attr('data-hasChildren') == 'true') {
|
|
record.hasChildren = true;
|
|
}
|
|
data.push(record);
|
|
}
|
|
return data;
|
|
}
|
|
HierarchicalDataSource.create = function (options) {
|
|
options = options && options.push ? { data: options } : options;
|
|
var dataSource = options || {}, data = dataSource.data, fields = dataSource.fields, list = dataSource.list;
|
|
if (data && data._dataSource) {
|
|
return data._dataSource;
|
|
}
|
|
if (!data && fields && !dataSource.transport) {
|
|
if (list) {
|
|
data = inferList(list, fields);
|
|
}
|
|
}
|
|
dataSource.data = data;
|
|
return dataSource instanceof HierarchicalDataSource ? dataSource : new HierarchicalDataSource(dataSource);
|
|
};
|
|
var Buffer = kendo.Observable.extend({
|
|
init: function (dataSource, viewSize, disablePrefetch) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this._prefetching = false;
|
|
this.dataSource = dataSource;
|
|
this.prefetch = !disablePrefetch;
|
|
var buffer = this;
|
|
dataSource.bind('change', function () {
|
|
buffer._change();
|
|
});
|
|
dataSource.bind('reset', function () {
|
|
buffer._reset();
|
|
});
|
|
this._syncWithDataSource();
|
|
this.setViewSize(viewSize);
|
|
},
|
|
setViewSize: function (viewSize) {
|
|
this.viewSize = viewSize;
|
|
this._recalculate();
|
|
},
|
|
at: function (index) {
|
|
var pageSize = this.pageSize, itemPresent = true;
|
|
if (index >= this.total()) {
|
|
this.trigger('endreached', { index: index });
|
|
return null;
|
|
}
|
|
if (!this.useRanges) {
|
|
return this.dataSource.view()[index];
|
|
}
|
|
if (this.useRanges) {
|
|
if (index < this.dataOffset || index >= this.skip + pageSize) {
|
|
itemPresent = this.range(Math.floor(index / pageSize) * pageSize);
|
|
}
|
|
if (index === this.prefetchThreshold) {
|
|
this._prefetch();
|
|
}
|
|
if (index === this.midPageThreshold) {
|
|
this.range(this.nextMidRange, true);
|
|
} else if (index === this.nextPageThreshold) {
|
|
this.range(this.nextFullRange);
|
|
} else if (index === this.pullBackThreshold) {
|
|
if (this.offset === this.skip) {
|
|
this.range(this.previousMidRange);
|
|
} else {
|
|
this.range(this.previousFullRange);
|
|
}
|
|
}
|
|
if (itemPresent) {
|
|
return this.dataSource.at(index - this.dataOffset);
|
|
} else {
|
|
this.trigger('endreached', { index: index });
|
|
return null;
|
|
}
|
|
}
|
|
},
|
|
indexOf: function (item) {
|
|
return this.dataSource.data().indexOf(item) + this.dataOffset;
|
|
},
|
|
total: function () {
|
|
return parseInt(this.dataSource.total(), 10);
|
|
},
|
|
next: function () {
|
|
var buffer = this, pageSize = buffer.pageSize, offset = buffer.skip - buffer.viewSize + pageSize, pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize;
|
|
this.offset = offset;
|
|
this.dataSource.prefetch(pageSkip, pageSize, function () {
|
|
buffer._goToRange(offset, true);
|
|
});
|
|
},
|
|
range: function (offset, nextRange) {
|
|
if (this.offset === offset) {
|
|
return true;
|
|
}
|
|
var buffer = this, pageSize = this.pageSize, pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize, dataSource = this.dataSource;
|
|
if (nextRange) {
|
|
pageSkip += pageSize;
|
|
}
|
|
if (dataSource.inRange(offset, pageSize)) {
|
|
this.offset = offset;
|
|
this._recalculate();
|
|
this._goToRange(offset);
|
|
return true;
|
|
} else if (this.prefetch) {
|
|
dataSource.prefetch(pageSkip, pageSize, function () {
|
|
buffer.offset = offset;
|
|
buffer._recalculate();
|
|
buffer._goToRange(offset, true);
|
|
});
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
syncDataSource: function () {
|
|
var offset = this.offset;
|
|
this.offset = null;
|
|
this.range(offset);
|
|
},
|
|
destroy: function () {
|
|
this.unbind();
|
|
},
|
|
_prefetch: function () {
|
|
var buffer = this, pageSize = this.pageSize, prefetchOffset = this.skip + pageSize, dataSource = this.dataSource;
|
|
if (!dataSource.inRange(prefetchOffset, pageSize) && !this._prefetching && this.prefetch) {
|
|
this._prefetching = true;
|
|
this.trigger('prefetching', {
|
|
skip: prefetchOffset,
|
|
take: pageSize
|
|
});
|
|
dataSource.prefetch(prefetchOffset, pageSize, function () {
|
|
buffer._prefetching = false;
|
|
buffer.trigger('prefetched', {
|
|
skip: prefetchOffset,
|
|
take: pageSize
|
|
});
|
|
});
|
|
}
|
|
},
|
|
_goToRange: function (offset, expanding) {
|
|
if (this.offset !== offset) {
|
|
return;
|
|
}
|
|
this.dataOffset = offset;
|
|
this._expanding = expanding;
|
|
this.dataSource.range(offset, this.pageSize);
|
|
this.dataSource.enableRequestsInProgress();
|
|
},
|
|
_reset: function () {
|
|
this._syncPending = true;
|
|
},
|
|
_change: function () {
|
|
var dataSource = this.dataSource;
|
|
this.length = this.useRanges ? dataSource.lastRange().end : dataSource.view().length;
|
|
if (this._syncPending) {
|
|
this._syncWithDataSource();
|
|
this._recalculate();
|
|
this._syncPending = false;
|
|
this.trigger('reset', { offset: this.offset });
|
|
}
|
|
this.trigger('resize');
|
|
if (this._expanding) {
|
|
this.trigger('expand');
|
|
}
|
|
delete this._expanding;
|
|
},
|
|
_syncWithDataSource: function () {
|
|
var dataSource = this.dataSource;
|
|
this._firstItemUid = dataSource.firstItemUid();
|
|
this.dataOffset = this.offset = dataSource.skip() || 0;
|
|
this.pageSize = dataSource.pageSize();
|
|
this.useRanges = dataSource.options.serverPaging;
|
|
},
|
|
_recalculate: function () {
|
|
var pageSize = this.pageSize, offset = this.offset, viewSize = this.viewSize, skip = Math.ceil(offset / pageSize) * pageSize;
|
|
this.skip = skip;
|
|
this.midPageThreshold = skip + pageSize - 1;
|
|
this.nextPageThreshold = skip + viewSize - 1;
|
|
this.prefetchThreshold = skip + Math.floor(pageSize / 3 * 2);
|
|
this.pullBackThreshold = this.offset - 1;
|
|
this.nextMidRange = skip + pageSize - viewSize;
|
|
this.nextFullRange = skip;
|
|
this.previousMidRange = offset - viewSize;
|
|
this.previousFullRange = skip - pageSize;
|
|
}
|
|
});
|
|
var BatchBuffer = kendo.Observable.extend({
|
|
init: function (dataSource, batchSize) {
|
|
var batchBuffer = this;
|
|
kendo.Observable.fn.init.call(batchBuffer);
|
|
this.dataSource = dataSource;
|
|
this.batchSize = batchSize;
|
|
this._total = 0;
|
|
this.buffer = new Buffer(dataSource, batchSize * 3);
|
|
this.buffer.bind({
|
|
'endreached': function (e) {
|
|
batchBuffer.trigger('endreached', { index: e.index });
|
|
},
|
|
'prefetching': function (e) {
|
|
batchBuffer.trigger('prefetching', {
|
|
skip: e.skip,
|
|
take: e.take
|
|
});
|
|
},
|
|
'prefetched': function (e) {
|
|
batchBuffer.trigger('prefetched', {
|
|
skip: e.skip,
|
|
take: e.take
|
|
});
|
|
},
|
|
'reset': function () {
|
|
batchBuffer._total = 0;
|
|
batchBuffer.trigger('reset');
|
|
},
|
|
'resize': function () {
|
|
batchBuffer._total = Math.ceil(this.length / batchBuffer.batchSize);
|
|
batchBuffer.trigger('resize', {
|
|
total: batchBuffer.total(),
|
|
offset: this.offset
|
|
});
|
|
}
|
|
});
|
|
},
|
|
syncDataSource: function () {
|
|
this.buffer.syncDataSource();
|
|
},
|
|
at: function (index) {
|
|
var buffer = this.buffer, skip = index * this.batchSize, take = this.batchSize, view = [], item;
|
|
if (buffer.offset > skip) {
|
|
buffer.at(buffer.offset - 1);
|
|
}
|
|
for (var i = 0; i < take; i++) {
|
|
item = buffer.at(skip + i);
|
|
if (item === null) {
|
|
break;
|
|
}
|
|
view.push(item);
|
|
}
|
|
return view;
|
|
},
|
|
total: function () {
|
|
return this._total;
|
|
},
|
|
destroy: function () {
|
|
this.buffer.destroy();
|
|
this.unbind();
|
|
}
|
|
});
|
|
extend(true, kendo.data, {
|
|
readers: { json: DataReader },
|
|
Query: Query,
|
|
DataSource: DataSource,
|
|
HierarchicalDataSource: HierarchicalDataSource,
|
|
Node: Node,
|
|
ObservableObject: ObservableObject,
|
|
ObservableArray: ObservableArray,
|
|
LazyObservableArray: LazyObservableArray,
|
|
LocalTransport: LocalTransport,
|
|
RemoteTransport: RemoteTransport,
|
|
Cache: Cache,
|
|
DataReader: DataReader,
|
|
Model: Model,
|
|
Buffer: Buffer,
|
|
BatchBuffer: BatchBuffer
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.binder', [
|
|
'kendo.core',
|
|
'kendo.data'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'binder',
|
|
name: 'MVVM',
|
|
category: 'framework',
|
|
description: 'Model View ViewModel (MVVM) is a design pattern which helps developers separate the Model (the data) from the View (the UI).',
|
|
depends: [
|
|
'core',
|
|
'data'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Observable = kendo.Observable, ObservableObject = kendo.data.ObservableObject, ObservableArray = kendo.data.ObservableArray, toString = {}.toString, binders = {}, Class = kendo.Class, proxy = $.proxy, VALUE = 'value', SOURCE = 'source', EVENTS = 'events', CHECKED = 'checked', CSS = 'css', deleteExpando = true, FUNCTION = 'function', CHANGE = 'change';
|
|
(function () {
|
|
var a = document.createElement('a');
|
|
try {
|
|
delete a.test;
|
|
} catch (e) {
|
|
deleteExpando = false;
|
|
}
|
|
}());
|
|
var Binding = Observable.extend({
|
|
init: function (parents, path) {
|
|
var that = this;
|
|
Observable.fn.init.call(that);
|
|
that.source = parents[0];
|
|
that.parents = parents;
|
|
that.path = path;
|
|
that.dependencies = {};
|
|
that.dependencies[path] = true;
|
|
that.observable = that.source instanceof Observable;
|
|
that._access = function (e) {
|
|
that.dependencies[e.field] = true;
|
|
};
|
|
if (that.observable) {
|
|
that._change = function (e) {
|
|
that.change(e);
|
|
};
|
|
that.source.bind(CHANGE, that._change);
|
|
}
|
|
},
|
|
_parents: function () {
|
|
var parents = this.parents;
|
|
var value = this.get();
|
|
if (value && typeof value.parent == 'function') {
|
|
var parent = value.parent();
|
|
if ($.inArray(parent, parents) < 0) {
|
|
parents = [parent].concat(parents);
|
|
}
|
|
}
|
|
return parents;
|
|
},
|
|
change: function (e) {
|
|
var dependency, ch, field = e.field, that = this;
|
|
if (that.path === 'this') {
|
|
that.trigger(CHANGE, e);
|
|
} else {
|
|
for (dependency in that.dependencies) {
|
|
if (dependency.indexOf(field) === 0) {
|
|
ch = dependency.charAt(field.length);
|
|
if (!ch || ch === '.' || ch === '[') {
|
|
that.trigger(CHANGE, e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
start: function (source) {
|
|
source.bind('get', this._access);
|
|
},
|
|
stop: function (source) {
|
|
source.unbind('get', this._access);
|
|
},
|
|
get: function () {
|
|
var that = this, source = that.source, index = 0, path = that.path, result = source;
|
|
if (!that.observable) {
|
|
return result;
|
|
}
|
|
that.start(that.source);
|
|
result = source.get(path);
|
|
while (result === undefined && source) {
|
|
source = that.parents[++index];
|
|
if (source instanceof ObservableObject) {
|
|
result = source.get(path);
|
|
}
|
|
}
|
|
if (result === undefined) {
|
|
source = that.source;
|
|
while (result === undefined && source) {
|
|
source = source.parent();
|
|
if (source instanceof ObservableObject) {
|
|
result = source.get(path);
|
|
}
|
|
}
|
|
}
|
|
if (typeof result === 'function') {
|
|
index = path.lastIndexOf('.');
|
|
if (index > 0) {
|
|
source = source.get(path.substring(0, index));
|
|
}
|
|
that.start(source);
|
|
if (source !== that.source) {
|
|
result = result.call(source, that.source);
|
|
} else {
|
|
result = result.call(source);
|
|
}
|
|
that.stop(source);
|
|
}
|
|
if (source && source !== that.source) {
|
|
that.currentSource = source;
|
|
source.unbind(CHANGE, that._change).bind(CHANGE, that._change);
|
|
}
|
|
that.stop(that.source);
|
|
return result;
|
|
},
|
|
set: function (value) {
|
|
var source = this.currentSource || this.source;
|
|
var field = kendo.getter(this.path)(source);
|
|
if (typeof field === 'function') {
|
|
if (source !== this.source) {
|
|
field.call(source, this.source, value);
|
|
} else {
|
|
field.call(source, value);
|
|
}
|
|
} else {
|
|
source.set(this.path, value);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
if (this.observable) {
|
|
this.source.unbind(CHANGE, this._change);
|
|
if (this.currentSource) {
|
|
this.currentSource.unbind(CHANGE, this._change);
|
|
}
|
|
}
|
|
this.unbind();
|
|
}
|
|
});
|
|
var EventBinding = Binding.extend({
|
|
get: function () {
|
|
var source = this.source, path = this.path, index = 0, handler;
|
|
handler = source.get(path);
|
|
while (!handler && source) {
|
|
source = this.parents[++index];
|
|
if (source instanceof ObservableObject) {
|
|
handler = source.get(path);
|
|
}
|
|
}
|
|
return proxy(handler, source);
|
|
}
|
|
});
|
|
var TemplateBinding = Binding.extend({
|
|
init: function (source, path, template) {
|
|
var that = this;
|
|
Binding.fn.init.call(that, source, path);
|
|
that.template = template;
|
|
},
|
|
render: function (value) {
|
|
var html;
|
|
this.start(this.source);
|
|
html = kendo.render(this.template, value);
|
|
this.stop(this.source);
|
|
return html;
|
|
}
|
|
});
|
|
var Binder = Class.extend({
|
|
init: function (element, bindings, options) {
|
|
this.element = element;
|
|
this.bindings = bindings;
|
|
this.options = options;
|
|
},
|
|
bind: function (binding, attribute) {
|
|
var that = this;
|
|
binding = attribute ? binding[attribute] : binding;
|
|
binding.bind(CHANGE, function (e) {
|
|
that.refresh(attribute || e);
|
|
});
|
|
that.refresh(attribute);
|
|
},
|
|
destroy: function () {
|
|
}
|
|
});
|
|
var TypedBinder = Binder.extend({
|
|
dataType: function () {
|
|
var dataType = this.element.getAttribute('data-type') || this.element.type || 'text';
|
|
return dataType.toLowerCase();
|
|
},
|
|
parsedValue: function () {
|
|
return this._parseValue(this.element.value, this.dataType());
|
|
},
|
|
_parseValue: function (value, dataType) {
|
|
if (dataType == 'date') {
|
|
value = kendo.parseDate(value, 'yyyy-MM-dd');
|
|
} else if (dataType == 'datetime-local') {
|
|
value = kendo.parseDate(value, [
|
|
'yyyy-MM-ddTHH:mm:ss',
|
|
'yyyy-MM-ddTHH:mm'
|
|
]);
|
|
} else if (dataType == 'number') {
|
|
value = kendo.parseFloat(value);
|
|
} else if (dataType == 'boolean') {
|
|
value = value.toLowerCase();
|
|
if (kendo.parseFloat(value) !== null) {
|
|
value = Boolean(kendo.parseFloat(value));
|
|
} else {
|
|
value = value.toLowerCase() === 'true';
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
});
|
|
binders.attr = Binder.extend({
|
|
refresh: function (key) {
|
|
this.element.setAttribute(key, this.bindings.attr[key].get());
|
|
}
|
|
});
|
|
binders.css = Binder.extend({
|
|
init: function (element, bindings, options) {
|
|
Binder.fn.init.call(this, element, bindings, options);
|
|
this.classes = {};
|
|
},
|
|
refresh: function (className) {
|
|
var element = $(this.element), binding = this.bindings.css[className], hasClass = this.classes[className] = binding.get();
|
|
if (hasClass) {
|
|
element.addClass(className);
|
|
} else {
|
|
element.removeClass(className);
|
|
}
|
|
}
|
|
});
|
|
binders.style = Binder.extend({
|
|
refresh: function (key) {
|
|
this.element.style[key] = this.bindings.style[key].get() || '';
|
|
}
|
|
});
|
|
binders.enabled = Binder.extend({
|
|
refresh: function () {
|
|
if (this.bindings.enabled.get()) {
|
|
this.element.removeAttribute('disabled');
|
|
} else {
|
|
this.element.setAttribute('disabled', 'disabled');
|
|
}
|
|
}
|
|
});
|
|
binders.readonly = Binder.extend({
|
|
refresh: function () {
|
|
if (this.bindings.readonly.get()) {
|
|
this.element.setAttribute('readonly', 'readonly');
|
|
} else {
|
|
this.element.removeAttribute('readonly');
|
|
}
|
|
}
|
|
});
|
|
binders.disabled = Binder.extend({
|
|
refresh: function () {
|
|
if (this.bindings.disabled.get()) {
|
|
this.element.setAttribute('disabled', 'disabled');
|
|
} else {
|
|
this.element.removeAttribute('disabled');
|
|
}
|
|
}
|
|
});
|
|
binders.events = Binder.extend({
|
|
init: function (element, bindings, options) {
|
|
Binder.fn.init.call(this, element, bindings, options);
|
|
this.handlers = {};
|
|
},
|
|
refresh: function (key) {
|
|
var element = $(this.element), binding = this.bindings.events[key], handler = this.handlers[key];
|
|
if (handler) {
|
|
element.off(key, handler);
|
|
}
|
|
handler = this.handlers[key] = binding.get();
|
|
element.on(key, binding.source, handler);
|
|
},
|
|
destroy: function () {
|
|
var element = $(this.element), handler;
|
|
for (handler in this.handlers) {
|
|
element.off(handler, this.handlers[handler]);
|
|
}
|
|
}
|
|
});
|
|
binders.text = Binder.extend({
|
|
refresh: function () {
|
|
var text = this.bindings.text.get();
|
|
var dataFormat = this.element.getAttribute('data-format') || '';
|
|
if (text == null) {
|
|
text = '';
|
|
}
|
|
$(this.element).text(kendo.toString(text, dataFormat));
|
|
}
|
|
});
|
|
binders.visible = Binder.extend({
|
|
refresh: function () {
|
|
if (this.bindings.visible.get()) {
|
|
this.element.style.display = '';
|
|
} else {
|
|
this.element.style.display = 'none';
|
|
}
|
|
}
|
|
});
|
|
binders.invisible = Binder.extend({
|
|
refresh: function () {
|
|
if (!this.bindings.invisible.get()) {
|
|
this.element.style.display = '';
|
|
} else {
|
|
this.element.style.display = 'none';
|
|
}
|
|
}
|
|
});
|
|
binders.html = Binder.extend({
|
|
refresh: function () {
|
|
this.element.innerHTML = this.bindings.html.get();
|
|
}
|
|
});
|
|
binders.value = TypedBinder.extend({
|
|
init: function (element, bindings, options) {
|
|
TypedBinder.fn.init.call(this, element, bindings, options);
|
|
this._change = proxy(this.change, this);
|
|
this.eventName = options.valueUpdate || CHANGE;
|
|
$(this.element).on(this.eventName, this._change);
|
|
this._initChange = false;
|
|
},
|
|
change: function () {
|
|
this._initChange = this.eventName != CHANGE;
|
|
this.bindings[VALUE].set(this.parsedValue());
|
|
this._initChange = false;
|
|
},
|
|
refresh: function () {
|
|
if (!this._initChange) {
|
|
var value = this.bindings[VALUE].get();
|
|
if (value == null) {
|
|
value = '';
|
|
}
|
|
var type = this.dataType();
|
|
if (type == 'date') {
|
|
value = kendo.toString(value, 'yyyy-MM-dd');
|
|
} else if (type == 'datetime-local') {
|
|
value = kendo.toString(value, 'yyyy-MM-ddTHH:mm:ss');
|
|
}
|
|
this.element.value = value;
|
|
}
|
|
this._initChange = false;
|
|
},
|
|
destroy: function () {
|
|
$(this.element).off(this.eventName, this._change);
|
|
}
|
|
});
|
|
binders.source = Binder.extend({
|
|
init: function (element, bindings, options) {
|
|
Binder.fn.init.call(this, element, bindings, options);
|
|
var source = this.bindings.source.get();
|
|
if (source instanceof kendo.data.DataSource && options.autoBind !== false) {
|
|
source.fetch();
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, source = that.bindings.source.get();
|
|
if (source instanceof ObservableArray || source instanceof kendo.data.DataSource) {
|
|
e = e || {};
|
|
if (e.action == 'add') {
|
|
that.add(e.index, e.items);
|
|
} else if (e.action == 'remove') {
|
|
that.remove(e.index, e.items);
|
|
} else if (e.action != 'itemchange') {
|
|
that.render();
|
|
}
|
|
} else {
|
|
that.render();
|
|
}
|
|
},
|
|
container: function () {
|
|
var element = this.element;
|
|
if (element.nodeName.toLowerCase() == 'table') {
|
|
if (!element.tBodies[0]) {
|
|
element.appendChild(document.createElement('tbody'));
|
|
}
|
|
element = element.tBodies[0];
|
|
}
|
|
return element;
|
|
},
|
|
template: function () {
|
|
var options = this.options, template = options.template, nodeName = this.container().nodeName.toLowerCase();
|
|
if (!template) {
|
|
if (nodeName == 'select') {
|
|
if (options.valueField || options.textField) {
|
|
template = kendo.format('<option value="#:{0}#">#:{1}#</option>', options.valueField || options.textField, options.textField || options.valueField);
|
|
} else {
|
|
template = '<option>#:data#</option>';
|
|
}
|
|
} else if (nodeName == 'tbody') {
|
|
template = '<tr><td>#:data#</td></tr>';
|
|
} else if (nodeName == 'ul' || nodeName == 'ol') {
|
|
template = '<li>#:data#</li>';
|
|
} else {
|
|
template = '#:data#';
|
|
}
|
|
template = kendo.template(template);
|
|
}
|
|
return template;
|
|
},
|
|
add: function (index, items) {
|
|
var element = this.container(), parents, idx, length, child, clone = element.cloneNode(false), reference = element.children[index];
|
|
$(clone).html(kendo.render(this.template(), items));
|
|
if (clone.children.length) {
|
|
parents = this.bindings.source._parents();
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
child = clone.children[0];
|
|
element.insertBefore(child, reference || null);
|
|
bindElement(child, items[idx], this.options.roles, [items[idx]].concat(parents));
|
|
}
|
|
}
|
|
},
|
|
remove: function (index, items) {
|
|
var idx, element = this.container();
|
|
for (idx = 0; idx < items.length; idx++) {
|
|
var child = element.children[index];
|
|
unbindElementTree(child, true);
|
|
element.removeChild(child);
|
|
}
|
|
},
|
|
render: function () {
|
|
var source = this.bindings.source.get(), parents, idx, length, element = this.container(), template = this.template();
|
|
if (source == null) {
|
|
return;
|
|
}
|
|
if (source instanceof kendo.data.DataSource) {
|
|
source = source.view();
|
|
}
|
|
if (!(source instanceof ObservableArray) && toString.call(source) !== '[object Array]') {
|
|
source = [source];
|
|
}
|
|
if (this.bindings.template) {
|
|
unbindElementChildren(element, true);
|
|
$(element).html(this.bindings.template.render(source));
|
|
if (element.children.length) {
|
|
parents = this.bindings.source._parents();
|
|
for (idx = 0, length = source.length; idx < length; idx++) {
|
|
bindElement(element.children[idx], source[idx], this.options.roles, [source[idx]].concat(parents));
|
|
}
|
|
}
|
|
} else {
|
|
$(element).html(kendo.render(template, source));
|
|
}
|
|
}
|
|
});
|
|
binders.input = {
|
|
checked: TypedBinder.extend({
|
|
init: function (element, bindings, options) {
|
|
TypedBinder.fn.init.call(this, element, bindings, options);
|
|
this._change = proxy(this.change, this);
|
|
$(this.element).change(this._change);
|
|
},
|
|
change: function () {
|
|
var element = this.element;
|
|
var value = this.value();
|
|
if (element.type == 'radio') {
|
|
value = this.parsedValue();
|
|
this.bindings[CHECKED].set(value);
|
|
} else if (element.type == 'checkbox') {
|
|
var source = this.bindings[CHECKED].get();
|
|
var index;
|
|
if (source instanceof ObservableArray) {
|
|
value = this.parsedValue();
|
|
if (value instanceof Date) {
|
|
for (var i = 0; i < source.length; i++) {
|
|
if (source[i] instanceof Date && +source[i] === +value) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
index = source.indexOf(value);
|
|
}
|
|
if (index > -1) {
|
|
source.splice(index, 1);
|
|
} else {
|
|
source.push(value);
|
|
}
|
|
} else {
|
|
this.bindings[CHECKED].set(value);
|
|
}
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var value = this.bindings[CHECKED].get(), source = value, type = this.dataType(), element = this.element;
|
|
if (element.type == 'checkbox') {
|
|
if (source instanceof ObservableArray) {
|
|
var index = -1;
|
|
value = this.parsedValue();
|
|
if (value instanceof Date) {
|
|
for (var i = 0; i < source.length; i++) {
|
|
if (source[i] instanceof Date && +source[i] === +value) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
index = source.indexOf(value);
|
|
}
|
|
element.checked = index >= 0;
|
|
} else {
|
|
element.checked = source;
|
|
}
|
|
} else if (element.type == 'radio' && value != null) {
|
|
if (type == 'date') {
|
|
value = kendo.toString(value, 'yyyy-MM-dd');
|
|
} else if (type == 'datetime-local') {
|
|
value = kendo.toString(value, 'yyyy-MM-ddTHH:mm:ss');
|
|
}
|
|
if (element.value === value.toString()) {
|
|
element.checked = true;
|
|
} else {
|
|
element.checked = false;
|
|
}
|
|
}
|
|
},
|
|
value: function () {
|
|
var element = this.element, value = element.value;
|
|
if (element.type == 'checkbox') {
|
|
value = element.checked;
|
|
}
|
|
return value;
|
|
},
|
|
destroy: function () {
|
|
$(this.element).off(CHANGE, this._change);
|
|
}
|
|
})
|
|
};
|
|
binders.select = {
|
|
source: binders.source.extend({
|
|
refresh: function (e) {
|
|
var that = this, source = that.bindings.source.get();
|
|
if (source instanceof ObservableArray || source instanceof kendo.data.DataSource) {
|
|
e = e || {};
|
|
if (e.action == 'add') {
|
|
that.add(e.index, e.items);
|
|
} else if (e.action == 'remove') {
|
|
that.remove(e.index, e.items);
|
|
} else if (e.action == 'itemchange' || e.action === undefined) {
|
|
that.render();
|
|
if (that.bindings.value) {
|
|
if (that.bindings.value) {
|
|
var val = retrievePrimitiveValues(that.bindings.value.get(), $(that.element).data('valueField'));
|
|
if (val === null) {
|
|
that.element.selectedIndex = -1;
|
|
} else {
|
|
that.element.value = val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
that.render();
|
|
}
|
|
}
|
|
}),
|
|
value: TypedBinder.extend({
|
|
init: function (target, bindings, options) {
|
|
TypedBinder.fn.init.call(this, target, bindings, options);
|
|
this._change = proxy(this.change, this);
|
|
$(this.element).change(this._change);
|
|
},
|
|
parsedValue: function () {
|
|
var dataType = this.dataType();
|
|
var values = [];
|
|
var value, option, idx, length;
|
|
for (idx = 0, length = this.element.options.length; idx < length; idx++) {
|
|
option = this.element.options[idx];
|
|
if (option.selected) {
|
|
value = option.attributes.value;
|
|
if (value && value.specified) {
|
|
value = option.value;
|
|
} else {
|
|
value = option.text;
|
|
}
|
|
values.push(this._parseValue(value, dataType));
|
|
}
|
|
}
|
|
return values;
|
|
},
|
|
change: function () {
|
|
var values = [], element = this.element, source, field = this.options.valueField || this.options.textField, valuePrimitive = this.options.valuePrimitive, option, valueIndex, value, idx, length;
|
|
for (idx = 0, length = element.options.length; idx < length; idx++) {
|
|
option = element.options[idx];
|
|
if (option.selected) {
|
|
value = option.attributes.value;
|
|
if (value && value.specified) {
|
|
value = option.value;
|
|
} else {
|
|
value = option.text;
|
|
}
|
|
values.push(this._parseValue(value, this.dataType()));
|
|
}
|
|
}
|
|
if (field) {
|
|
source = this.bindings.source.get();
|
|
if (source instanceof kendo.data.DataSource) {
|
|
source = source.view();
|
|
}
|
|
for (valueIndex = 0; valueIndex < values.length; valueIndex++) {
|
|
for (idx = 0, length = source.length; idx < length; idx++) {
|
|
var sourceValue = this._parseValue(source[idx].get(field), this.dataType());
|
|
var match = String(sourceValue) === values[valueIndex];
|
|
if (match) {
|
|
values[valueIndex] = source[idx];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
value = this.bindings[VALUE].get();
|
|
if (value instanceof ObservableArray) {
|
|
value.splice.apply(value, [
|
|
0,
|
|
value.length
|
|
].concat(values));
|
|
} else if (!valuePrimitive && (value instanceof ObservableObject || value === null || value === undefined || !field)) {
|
|
this.bindings[VALUE].set(values[0]);
|
|
} else {
|
|
this.bindings[VALUE].set(values[0].get(field));
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var optionIndex, element = this.element, options = element.options, value = this.bindings[VALUE].get(), values = value, field = this.options.valueField || this.options.textField, found = false, type = this.dataType(), optionValue;
|
|
if (!(values instanceof ObservableArray)) {
|
|
values = new ObservableArray([value]);
|
|
}
|
|
element.selectedIndex = -1;
|
|
for (var valueIndex = 0; valueIndex < values.length; valueIndex++) {
|
|
value = values[valueIndex];
|
|
if (field && value instanceof ObservableObject) {
|
|
value = value.get(field);
|
|
}
|
|
if (type == 'date') {
|
|
value = kendo.toString(values[valueIndex], 'yyyy-MM-dd');
|
|
} else if (type == 'datetime-local') {
|
|
value = kendo.toString(values[valueIndex], 'yyyy-MM-ddTHH:mm:ss');
|
|
}
|
|
for (optionIndex = 0; optionIndex < options.length; optionIndex++) {
|
|
optionValue = options[optionIndex].value;
|
|
if (optionValue === '' && value !== '') {
|
|
optionValue = options[optionIndex].text;
|
|
}
|
|
if (value != null && optionValue == value.toString()) {
|
|
options[optionIndex].selected = true;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
$(this.element).off(CHANGE, this._change);
|
|
}
|
|
})
|
|
};
|
|
function dataSourceBinding(bindingName, fieldName, setter) {
|
|
return Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
var that = this;
|
|
Binder.fn.init.call(that, widget.element[0], bindings, options);
|
|
that.widget = widget;
|
|
that._dataBinding = proxy(that.dataBinding, that);
|
|
that._dataBound = proxy(that.dataBound, that);
|
|
that._itemChange = proxy(that.itemChange, that);
|
|
},
|
|
itemChange: function (e) {
|
|
bindElement(e.item[0], e.data, this._ns(e.ns), [e.data].concat(this.bindings[bindingName]._parents()));
|
|
},
|
|
dataBinding: function (e) {
|
|
var idx, length, widget = this.widget, items = e.removedItems || widget.items();
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
unbindElementTree(items[idx], false);
|
|
}
|
|
},
|
|
_ns: function (ns) {
|
|
ns = ns || kendo.ui;
|
|
var all = [
|
|
kendo.ui,
|
|
kendo.dataviz.ui,
|
|
kendo.mobile.ui
|
|
];
|
|
all.splice($.inArray(ns, all), 1);
|
|
all.unshift(ns);
|
|
return kendo.rolesFromNamespaces(all);
|
|
},
|
|
dataBound: function (e) {
|
|
var idx, length, widget = this.widget, items = e.addedItems || widget.items(), dataSource = widget[fieldName], view, parents, hds = kendo.data.HierarchicalDataSource;
|
|
if (hds && dataSource instanceof hds) {
|
|
return;
|
|
}
|
|
if (items.length) {
|
|
view = e.addedDataItems || dataSource.flatView();
|
|
parents = this.bindings[bindingName]._parents();
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
bindElement(items[idx], view[idx], this._ns(e.ns), [view[idx]].concat(parents));
|
|
}
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, source, widget = that.widget, select, multiselect;
|
|
e = e || {};
|
|
if (!e.action) {
|
|
that.destroy();
|
|
widget.bind('dataBinding', that._dataBinding);
|
|
widget.bind('dataBound', that._dataBound);
|
|
widget.bind('itemChange', that._itemChange);
|
|
source = that.bindings[bindingName].get();
|
|
if (widget[fieldName] instanceof kendo.data.DataSource && widget[fieldName] != source) {
|
|
if (source instanceof kendo.data.DataSource) {
|
|
widget[setter](source);
|
|
} else if (source && source._dataSource) {
|
|
widget[setter](source._dataSource);
|
|
} else {
|
|
widget[fieldName].data(source);
|
|
select = kendo.ui.Select && widget instanceof kendo.ui.Select;
|
|
multiselect = kendo.ui.MultiSelect && widget instanceof kendo.ui.MultiSelect;
|
|
if (that.bindings.value && (select || multiselect)) {
|
|
widget.value(retrievePrimitiveValues(that.bindings.value.get(), widget.options.dataValueField));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var widget = this.widget;
|
|
widget.unbind('dataBinding', this._dataBinding);
|
|
widget.unbind('dataBound', this._dataBound);
|
|
widget.unbind('itemChange', this._itemChange);
|
|
}
|
|
});
|
|
}
|
|
binders.widget = {
|
|
events: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
this.handlers = {};
|
|
},
|
|
refresh: function (key) {
|
|
var binding = this.bindings.events[key], handler = this.handlers[key];
|
|
if (handler) {
|
|
this.widget.unbind(key, handler);
|
|
}
|
|
handler = binding.get();
|
|
this.handlers[key] = function (e) {
|
|
e.data = binding.source;
|
|
handler(e);
|
|
if (e.data === binding.source) {
|
|
delete e.data;
|
|
}
|
|
};
|
|
this.widget.bind(key, this.handlers[key]);
|
|
},
|
|
destroy: function () {
|
|
var handler;
|
|
for (handler in this.handlers) {
|
|
this.widget.unbind(handler, this.handlers[handler]);
|
|
}
|
|
}
|
|
}),
|
|
checked: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
this._change = proxy(this.change, this);
|
|
this.widget.bind(CHANGE, this._change);
|
|
},
|
|
change: function () {
|
|
this.bindings[CHECKED].set(this.value());
|
|
},
|
|
refresh: function () {
|
|
this.widget.check(this.bindings[CHECKED].get() === true);
|
|
},
|
|
value: function () {
|
|
var element = this.element, value = element.value;
|
|
if (value == 'on' || value == 'off') {
|
|
value = element.checked;
|
|
}
|
|
return value;
|
|
},
|
|
destroy: function () {
|
|
this.widget.unbind(CHANGE, this._change);
|
|
}
|
|
}),
|
|
visible: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
},
|
|
refresh: function () {
|
|
var visible = this.bindings.visible.get();
|
|
this.widget.wrapper[0].style.display = visible ? '' : 'none';
|
|
}
|
|
}),
|
|
invisible: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
},
|
|
refresh: function () {
|
|
var invisible = this.bindings.invisible.get();
|
|
this.widget.wrapper[0].style.display = invisible ? 'none' : '';
|
|
}
|
|
}),
|
|
enabled: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
},
|
|
refresh: function () {
|
|
if (this.widget.enable) {
|
|
this.widget.enable(this.bindings.enabled.get());
|
|
}
|
|
}
|
|
}),
|
|
disabled: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
},
|
|
refresh: function () {
|
|
if (this.widget.enable) {
|
|
this.widget.enable(!this.bindings.disabled.get());
|
|
}
|
|
}
|
|
}),
|
|
source: dataSourceBinding('source', 'dataSource', 'setDataSource'),
|
|
value: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
this._change = $.proxy(this.change, this);
|
|
this.widget.first(CHANGE, this._change);
|
|
var value = this.bindings.value.get();
|
|
this._valueIsObservableObject = !options.valuePrimitive && (value == null || value instanceof ObservableObject);
|
|
this._valueIsObservableArray = value instanceof ObservableArray;
|
|
this._initChange = false;
|
|
},
|
|
_source: function () {
|
|
var source;
|
|
if (this.widget.dataItem) {
|
|
source = this.widget.dataItem();
|
|
if (source && source instanceof ObservableObject) {
|
|
return [source];
|
|
}
|
|
}
|
|
if (this.bindings.source) {
|
|
source = this.bindings.source.get();
|
|
}
|
|
if (!source || source instanceof kendo.data.DataSource) {
|
|
source = this.widget.dataSource.flatView();
|
|
}
|
|
return source;
|
|
},
|
|
change: function () {
|
|
var value = this.widget.value(), field = this.options.dataValueField || this.options.dataTextField, isArray = toString.call(value) === '[object Array]', isObservableObject = this._valueIsObservableObject, valueIndex, valueLength, values = [], sourceItem, sourceValue, idx, length, source;
|
|
this._initChange = true;
|
|
if (field) {
|
|
if (value === '' && (isObservableObject || this.options.valuePrimitive)) {
|
|
value = null;
|
|
} else {
|
|
source = this._source();
|
|
if (isArray) {
|
|
valueLength = value.length;
|
|
values = value.slice(0);
|
|
}
|
|
for (idx = 0, length = source.length; idx < length; idx++) {
|
|
sourceItem = source[idx];
|
|
sourceValue = sourceItem.get(field);
|
|
if (isArray) {
|
|
for (valueIndex = 0; valueIndex < valueLength; valueIndex++) {
|
|
if (sourceValue == values[valueIndex]) {
|
|
values[valueIndex] = sourceItem;
|
|
break;
|
|
}
|
|
}
|
|
} else if (sourceValue == value) {
|
|
value = isObservableObject ? sourceItem : sourceValue;
|
|
break;
|
|
}
|
|
}
|
|
if (values[0]) {
|
|
if (this._valueIsObservableArray) {
|
|
value = values;
|
|
} else if (isObservableObject || !field) {
|
|
value = values[0];
|
|
} else {
|
|
value = values[0].get(field);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.bindings.value.set(value);
|
|
this._initChange = false;
|
|
},
|
|
refresh: function () {
|
|
if (!this._initChange) {
|
|
var widget = this.widget;
|
|
var options = widget.options;
|
|
var textField = options.dataTextField;
|
|
var valueField = options.dataValueField || textField;
|
|
var value = this.bindings.value.get();
|
|
var text = options.text || '';
|
|
var idx = 0, length;
|
|
var values = [];
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
if (valueField) {
|
|
if (value instanceof ObservableArray) {
|
|
for (length = value.length; idx < length; idx++) {
|
|
values[idx] = value[idx].get(valueField);
|
|
}
|
|
value = values;
|
|
} else if (value instanceof ObservableObject) {
|
|
text = value.get(textField);
|
|
value = value.get(valueField);
|
|
}
|
|
}
|
|
if (options.autoBind === false && !options.cascadeFrom && widget.listView && !widget.listView.bound()) {
|
|
if (textField === valueField && !text) {
|
|
text = value;
|
|
}
|
|
if (!text && (value || value === 0) && options.valuePrimitive) {
|
|
widget.value(value);
|
|
} else {
|
|
widget._preselect(value, text);
|
|
}
|
|
} else {
|
|
widget.value(value);
|
|
}
|
|
}
|
|
this._initChange = false;
|
|
},
|
|
destroy: function () {
|
|
this.widget.unbind(CHANGE, this._change);
|
|
}
|
|
}),
|
|
gantt: { dependencies: dataSourceBinding('dependencies', 'dependencies', 'setDependenciesDataSource') },
|
|
multiselect: {
|
|
value: Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
this._change = $.proxy(this.change, this);
|
|
this.widget.first(CHANGE, this._change);
|
|
this._initChange = false;
|
|
},
|
|
change: function () {
|
|
var that = this, oldValues = that.bindings[VALUE].get(), valuePrimitive = that.options.valuePrimitive, newValues = valuePrimitive ? that.widget.value() : that.widget.dataItems();
|
|
var field = this.options.dataValueField || this.options.dataTextField;
|
|
newValues = newValues.slice(0);
|
|
that._initChange = true;
|
|
if (oldValues instanceof ObservableArray) {
|
|
var remove = [];
|
|
var newLength = newValues.length;
|
|
var i = 0, j = 0;
|
|
var old = oldValues[i];
|
|
var same = false;
|
|
var removeIndex;
|
|
var newValue;
|
|
var found;
|
|
while (old !== undefined) {
|
|
found = false;
|
|
for (j = 0; j < newLength; j++) {
|
|
if (valuePrimitive) {
|
|
same = newValues[j] == old;
|
|
} else {
|
|
newValue = newValues[j];
|
|
newValue = newValue.get ? newValue.get(field) : newValue;
|
|
same = newValue == (old.get ? old.get(field) : old);
|
|
}
|
|
if (same) {
|
|
newValues.splice(j, 1);
|
|
newLength -= 1;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
remove.push(old);
|
|
arraySplice(oldValues, i, 1);
|
|
removeIndex = i;
|
|
} else {
|
|
i += 1;
|
|
}
|
|
old = oldValues[i];
|
|
}
|
|
arraySplice(oldValues, oldValues.length, 0, newValues);
|
|
if (remove.length) {
|
|
oldValues.trigger('change', {
|
|
action: 'remove',
|
|
items: remove,
|
|
index: removeIndex
|
|
});
|
|
}
|
|
if (newValues.length) {
|
|
oldValues.trigger('change', {
|
|
action: 'add',
|
|
items: newValues,
|
|
index: oldValues.length - 1
|
|
});
|
|
}
|
|
} else {
|
|
that.bindings[VALUE].set(newValues);
|
|
}
|
|
that._initChange = false;
|
|
},
|
|
refresh: function () {
|
|
if (!this._initChange) {
|
|
var options = this.options, widget = this.widget, field = options.dataValueField || options.dataTextField, value = this.bindings.value.get(), data = value, idx = 0, length, values = [], selectedValue;
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
if (field) {
|
|
if (value instanceof ObservableArray) {
|
|
for (length = value.length; idx < length; idx++) {
|
|
selectedValue = value[idx];
|
|
values[idx] = selectedValue.get ? selectedValue.get(field) : selectedValue;
|
|
}
|
|
value = values;
|
|
} else if (value instanceof ObservableObject) {
|
|
value = value.get(field);
|
|
}
|
|
}
|
|
if (options.autoBind === false && options.valuePrimitive !== true && !widget._isBound()) {
|
|
widget._preselect(data, value);
|
|
} else {
|
|
widget.value(value);
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this.widget.unbind(CHANGE, this._change);
|
|
}
|
|
})
|
|
},
|
|
scheduler: {
|
|
source: dataSourceBinding('source', 'dataSource', 'setDataSource').extend({
|
|
dataBound: function (e) {
|
|
var idx;
|
|
var length;
|
|
var widget = this.widget;
|
|
var elements = e.addedItems || widget.items();
|
|
var data, parents;
|
|
if (elements.length) {
|
|
data = e.addedDataItems || widget.dataItems();
|
|
parents = this.bindings.source._parents();
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
bindElement(elements[idx], data[idx], this._ns(e.ns), [data[idx]].concat(parents));
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
};
|
|
var arraySplice = function (arr, idx, remove, add) {
|
|
add = add || [];
|
|
remove = remove || 0;
|
|
var addLength = add.length;
|
|
var oldLength = arr.length;
|
|
var shifted = [].slice.call(arr, idx + remove);
|
|
var shiftedLength = shifted.length;
|
|
var index;
|
|
if (addLength) {
|
|
addLength = idx + addLength;
|
|
index = 0;
|
|
for (; idx < addLength; idx++) {
|
|
arr[idx] = add[index];
|
|
index++;
|
|
}
|
|
arr.length = addLength;
|
|
} else if (remove) {
|
|
arr.length = idx;
|
|
remove += idx;
|
|
while (idx < remove) {
|
|
delete arr[--remove];
|
|
}
|
|
}
|
|
if (shiftedLength) {
|
|
shiftedLength = idx + shiftedLength;
|
|
index = 0;
|
|
for (; idx < shiftedLength; idx++) {
|
|
arr[idx] = shifted[index];
|
|
index++;
|
|
}
|
|
arr.length = shiftedLength;
|
|
}
|
|
idx = arr.length;
|
|
while (idx < oldLength) {
|
|
delete arr[idx];
|
|
idx++;
|
|
}
|
|
};
|
|
var BindingTarget = Class.extend({
|
|
init: function (target, options) {
|
|
this.target = target;
|
|
this.options = options;
|
|
this.toDestroy = [];
|
|
},
|
|
bind: function (bindings) {
|
|
var key, hasValue, hasSource, hasEvents, hasChecked, hasCss, widgetBinding = this instanceof WidgetBindingTarget, specificBinders = this.binders();
|
|
for (key in bindings) {
|
|
if (key == VALUE) {
|
|
hasValue = true;
|
|
} else if (key == SOURCE) {
|
|
hasSource = true;
|
|
} else if (key == EVENTS && !widgetBinding) {
|
|
hasEvents = true;
|
|
} else if (key == CHECKED) {
|
|
hasChecked = true;
|
|
} else if (key == CSS) {
|
|
hasCss = true;
|
|
} else {
|
|
this.applyBinding(key, bindings, specificBinders);
|
|
}
|
|
}
|
|
if (hasSource) {
|
|
this.applyBinding(SOURCE, bindings, specificBinders);
|
|
}
|
|
if (hasValue) {
|
|
this.applyBinding(VALUE, bindings, specificBinders);
|
|
}
|
|
if (hasChecked) {
|
|
this.applyBinding(CHECKED, bindings, specificBinders);
|
|
}
|
|
if (hasEvents && !widgetBinding) {
|
|
this.applyBinding(EVENTS, bindings, specificBinders);
|
|
}
|
|
if (hasCss && !widgetBinding) {
|
|
this.applyBinding(CSS, bindings, specificBinders);
|
|
}
|
|
},
|
|
binders: function () {
|
|
return binders[this.target.nodeName.toLowerCase()] || {};
|
|
},
|
|
applyBinding: function (name, bindings, specificBinders) {
|
|
var binder = specificBinders[name] || binders[name], toDestroy = this.toDestroy, attribute, binding = bindings[name];
|
|
if (binder) {
|
|
binder = new binder(this.target, bindings, this.options);
|
|
toDestroy.push(binder);
|
|
if (binding instanceof Binding) {
|
|
binder.bind(binding);
|
|
toDestroy.push(binding);
|
|
} else {
|
|
for (attribute in binding) {
|
|
binder.bind(binding, attribute);
|
|
toDestroy.push(binding[attribute]);
|
|
}
|
|
}
|
|
} else if (name !== 'template') {
|
|
throw new Error('The ' + name + ' binding is not supported by the ' + this.target.nodeName.toLowerCase() + ' element');
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var idx, length, toDestroy = this.toDestroy;
|
|
for (idx = 0, length = toDestroy.length; idx < length; idx++) {
|
|
toDestroy[idx].destroy();
|
|
}
|
|
}
|
|
});
|
|
var WidgetBindingTarget = BindingTarget.extend({
|
|
binders: function () {
|
|
return binders.widget[this.target.options.name.toLowerCase()] || {};
|
|
},
|
|
applyBinding: function (name, bindings, specificBinders) {
|
|
var binder = specificBinders[name] || binders.widget[name], toDestroy = this.toDestroy, attribute, binding = bindings[name];
|
|
if (binder) {
|
|
binder = new binder(this.target, bindings, this.target.options);
|
|
toDestroy.push(binder);
|
|
if (binding instanceof Binding) {
|
|
binder.bind(binding);
|
|
toDestroy.push(binding);
|
|
} else {
|
|
for (attribute in binding) {
|
|
binder.bind(binding, attribute);
|
|
toDestroy.push(binding[attribute]);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error('The ' + name + ' binding is not supported by the ' + this.target.options.name + ' widget');
|
|
}
|
|
}
|
|
});
|
|
function bindingTargetForRole(element, roles) {
|
|
var widget = kendo.initWidget(element, {}, roles);
|
|
if (widget) {
|
|
return new WidgetBindingTarget(widget);
|
|
}
|
|
}
|
|
var keyValueRegExp = /[A-Za-z0-9_\-]+:(\{([^}]*)\}|[^,}]+)/g, whiteSpaceRegExp = /\s/g;
|
|
function parseBindings(bind) {
|
|
var result = {}, idx, length, token, colonIndex, key, value, tokens;
|
|
tokens = bind.match(keyValueRegExp);
|
|
for (idx = 0, length = tokens.length; idx < length; idx++) {
|
|
token = tokens[idx];
|
|
colonIndex = token.indexOf(':');
|
|
key = token.substring(0, colonIndex);
|
|
value = token.substring(colonIndex + 1);
|
|
if (value.charAt(0) == '{') {
|
|
value = parseBindings(value);
|
|
}
|
|
result[key] = value;
|
|
}
|
|
return result;
|
|
}
|
|
function createBindings(bindings, source, type) {
|
|
var binding, result = {};
|
|
for (binding in bindings) {
|
|
result[binding] = new type(source, bindings[binding]);
|
|
}
|
|
return result;
|
|
}
|
|
function bindElement(element, source, roles, parents) {
|
|
var role = element.getAttribute('data-' + kendo.ns + 'role'), idx, bind = element.getAttribute('data-' + kendo.ns + 'bind'), children = element.children, childrenCopy = [], deep = true, bindings, options = {}, target;
|
|
parents = parents || [source];
|
|
if (role || bind) {
|
|
unbindElement(element, false);
|
|
}
|
|
if (role) {
|
|
target = bindingTargetForRole(element, roles);
|
|
}
|
|
if (bind) {
|
|
bind = parseBindings(bind.replace(whiteSpaceRegExp, ''));
|
|
if (!target) {
|
|
options = kendo.parseOptions(element, {
|
|
textField: '',
|
|
valueField: '',
|
|
template: '',
|
|
valueUpdate: CHANGE,
|
|
valuePrimitive: false,
|
|
autoBind: true
|
|
});
|
|
options.roles = roles;
|
|
target = new BindingTarget(element, options);
|
|
}
|
|
target.source = source;
|
|
bindings = createBindings(bind, parents, Binding);
|
|
if (options.template) {
|
|
bindings.template = new TemplateBinding(parents, '', options.template);
|
|
}
|
|
if (bindings.click) {
|
|
bind.events = bind.events || {};
|
|
bind.events.click = bind.click;
|
|
bindings.click.destroy();
|
|
delete bindings.click;
|
|
}
|
|
if (bindings.source) {
|
|
deep = false;
|
|
}
|
|
if (bind.attr) {
|
|
bindings.attr = createBindings(bind.attr, parents, Binding);
|
|
}
|
|
if (bind.style) {
|
|
bindings.style = createBindings(bind.style, parents, Binding);
|
|
}
|
|
if (bind.events) {
|
|
bindings.events = createBindings(bind.events, parents, EventBinding);
|
|
}
|
|
if (bind.css) {
|
|
bindings.css = createBindings(bind.css, parents, Binding);
|
|
}
|
|
target.bind(bindings);
|
|
}
|
|
if (target) {
|
|
element.kendoBindingTarget = target;
|
|
}
|
|
if (deep && children) {
|
|
for (idx = 0; idx < children.length; idx++) {
|
|
childrenCopy[idx] = children[idx];
|
|
}
|
|
for (idx = 0; idx < childrenCopy.length; idx++) {
|
|
bindElement(childrenCopy[idx], source, roles, parents);
|
|
}
|
|
}
|
|
}
|
|
function bind(dom, object) {
|
|
var idx, length, node, roles = kendo.rolesFromNamespaces([].slice.call(arguments, 2));
|
|
object = kendo.observable(object);
|
|
dom = $(dom);
|
|
for (idx = 0, length = dom.length; idx < length; idx++) {
|
|
node = dom[idx];
|
|
if (node.nodeType === 1) {
|
|
bindElement(node, object, roles);
|
|
}
|
|
}
|
|
}
|
|
function unbindElement(element, destroyWidget) {
|
|
var bindingTarget = element.kendoBindingTarget;
|
|
if (bindingTarget) {
|
|
bindingTarget.destroy();
|
|
if (deleteExpando) {
|
|
delete element.kendoBindingTarget;
|
|
} else if (element.removeAttribute) {
|
|
element.removeAttribute('kendoBindingTarget');
|
|
} else {
|
|
element.kendoBindingTarget = null;
|
|
}
|
|
}
|
|
if (destroyWidget) {
|
|
var widget = kendo.widgetInstance($(element));
|
|
if (widget && typeof widget.destroy === FUNCTION) {
|
|
widget.destroy();
|
|
}
|
|
}
|
|
}
|
|
function unbindElementTree(element, destroyWidgets) {
|
|
unbindElement(element, destroyWidgets);
|
|
unbindElementChildren(element, destroyWidgets);
|
|
}
|
|
function unbindElementChildren(element, destroyWidgets) {
|
|
var children = element.children;
|
|
if (children) {
|
|
for (var idx = 0, length = children.length; idx < length; idx++) {
|
|
unbindElementTree(children[idx], destroyWidgets);
|
|
}
|
|
}
|
|
}
|
|
function unbind(dom) {
|
|
var idx, length;
|
|
dom = $(dom);
|
|
for (idx = 0, length = dom.length; idx < length; idx++) {
|
|
unbindElementTree(dom[idx], false);
|
|
}
|
|
}
|
|
function notify(widget, namespace) {
|
|
var element = widget.element, bindingTarget = element[0].kendoBindingTarget;
|
|
if (bindingTarget) {
|
|
bind(element, bindingTarget.source, namespace);
|
|
}
|
|
}
|
|
function retrievePrimitiveValues(value, valueField) {
|
|
var values = [];
|
|
var idx = 0;
|
|
var length;
|
|
var item;
|
|
if (!valueField) {
|
|
return value;
|
|
}
|
|
if (value instanceof ObservableArray) {
|
|
for (length = value.length; idx < length; idx++) {
|
|
item = value[idx];
|
|
values[idx] = item.get ? item.get(valueField) : item[valueField];
|
|
}
|
|
value = values;
|
|
} else if (value instanceof ObservableObject) {
|
|
value = value.get(valueField);
|
|
}
|
|
return value;
|
|
}
|
|
kendo.unbind = unbind;
|
|
kendo.bind = bind;
|
|
kendo.data.binders = binders;
|
|
kendo.data.Binder = Binder;
|
|
kendo.notify = notify;
|
|
kendo.observable = function (object) {
|
|
if (!(object instanceof ObservableObject)) {
|
|
object = new ObservableObject(object);
|
|
}
|
|
return object;
|
|
};
|
|
kendo.observableHierarchy = function (array) {
|
|
var dataSource = kendo.data.HierarchicalDataSource.create(array);
|
|
function recursiveRead(data) {
|
|
var i, children;
|
|
for (i = 0; i < data.length; i++) {
|
|
data[i]._initChildren();
|
|
children = data[i].children;
|
|
children.fetch();
|
|
data[i].items = children.data();
|
|
recursiveRead(data[i].items);
|
|
}
|
|
}
|
|
dataSource.fetch();
|
|
recursiveRead(dataSource.data());
|
|
dataSource._data._dataSource = dataSource;
|
|
return dataSource._data;
|
|
};
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.fx', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'fx',
|
|
name: 'Effects',
|
|
category: 'framework',
|
|
description: 'Required for animation effects in all Kendo UI widgets.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, fx = kendo.effects, each = $.each, extend = $.extend, proxy = $.proxy, support = kendo.support, browser = support.browser, transforms = support.transforms, transitions = support.transitions, scaleProperties = {
|
|
scale: 0,
|
|
scalex: 0,
|
|
scaley: 0,
|
|
scale3d: 0
|
|
}, translateProperties = {
|
|
translate: 0,
|
|
translatex: 0,
|
|
translatey: 0,
|
|
translate3d: 0
|
|
}, hasZoom = typeof document.documentElement.style.zoom !== 'undefined' && !transforms, matrix3dRegExp = /matrix3?d?\s*\(.*,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?/i, cssParamsRegExp = /^(-?[\d\.\-]+)?[\w\s]*,?\s*(-?[\d\.\-]+)?[\w\s]*/i, translateXRegExp = /translatex?$/i, oldEffectsRegExp = /(zoom|fade|expand)(\w+)/, singleEffectRegExp = /(zoom|fade|expand)/, unitRegExp = /[xy]$/i, transformProps = [
|
|
'perspective',
|
|
'rotate',
|
|
'rotatex',
|
|
'rotatey',
|
|
'rotatez',
|
|
'rotate3d',
|
|
'scale',
|
|
'scalex',
|
|
'scaley',
|
|
'scalez',
|
|
'scale3d',
|
|
'skew',
|
|
'skewx',
|
|
'skewy',
|
|
'translate',
|
|
'translatex',
|
|
'translatey',
|
|
'translatez',
|
|
'translate3d',
|
|
'matrix',
|
|
'matrix3d'
|
|
], transform2d = [
|
|
'rotate',
|
|
'scale',
|
|
'scalex',
|
|
'scaley',
|
|
'skew',
|
|
'skewx',
|
|
'skewy',
|
|
'translate',
|
|
'translatex',
|
|
'translatey',
|
|
'matrix'
|
|
], transform2units = {
|
|
'rotate': 'deg',
|
|
scale: '',
|
|
skew: 'px',
|
|
translate: 'px'
|
|
}, cssPrefix = transforms.css, round = Math.round, BLANK = '', PX = 'px', NONE = 'none', AUTO = 'auto', WIDTH = 'width', HEIGHT = 'height', HIDDEN = 'hidden', ORIGIN = 'origin', ABORT_ID = 'abortId', OVERFLOW = 'overflow', TRANSLATE = 'translate', POSITION = 'position', COMPLETE_CALLBACK = 'completeCallback', TRANSITION = cssPrefix + 'transition', TRANSFORM = cssPrefix + 'transform', BACKFACE = cssPrefix + 'backface-visibility', PERSPECTIVE = cssPrefix + 'perspective', DEFAULT_PERSPECTIVE = '1500px', TRANSFORM_PERSPECTIVE = 'perspective(' + DEFAULT_PERSPECTIVE + ')', directions = {
|
|
left: {
|
|
reverse: 'right',
|
|
property: 'left',
|
|
transition: 'translatex',
|
|
vertical: false,
|
|
modifier: -1
|
|
},
|
|
right: {
|
|
reverse: 'left',
|
|
property: 'left',
|
|
transition: 'translatex',
|
|
vertical: false,
|
|
modifier: 1
|
|
},
|
|
down: {
|
|
reverse: 'up',
|
|
property: 'top',
|
|
transition: 'translatey',
|
|
vertical: true,
|
|
modifier: 1
|
|
},
|
|
up: {
|
|
reverse: 'down',
|
|
property: 'top',
|
|
transition: 'translatey',
|
|
vertical: true,
|
|
modifier: -1
|
|
},
|
|
top: { reverse: 'bottom' },
|
|
bottom: { reverse: 'top' },
|
|
'in': {
|
|
reverse: 'out',
|
|
modifier: -1
|
|
},
|
|
out: {
|
|
reverse: 'in',
|
|
modifier: 1
|
|
},
|
|
vertical: { reverse: 'vertical' },
|
|
horizontal: { reverse: 'horizontal' }
|
|
};
|
|
kendo.directions = directions;
|
|
extend($.fn, {
|
|
kendoStop: function (clearQueue, gotoEnd) {
|
|
if (transitions) {
|
|
return fx.stopQueue(this, clearQueue || false, gotoEnd || false);
|
|
} else {
|
|
return this.stop(clearQueue, gotoEnd);
|
|
}
|
|
}
|
|
});
|
|
if (transforms && !transitions) {
|
|
each(transform2d, function (idx, value) {
|
|
$.fn[value] = function (val) {
|
|
if (typeof val == 'undefined') {
|
|
return animationProperty(this, value);
|
|
} else {
|
|
var that = $(this)[0], transformValue = value + '(' + val + transform2units[value.replace(unitRegExp, '')] + ')';
|
|
if (that.style.cssText.indexOf(TRANSFORM) == -1) {
|
|
$(this).css(TRANSFORM, transformValue);
|
|
} else {
|
|
that.style.cssText = that.style.cssText.replace(new RegExp(value + '\\(.*?\\)', 'i'), transformValue);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
$.fx.step[value] = function (fx) {
|
|
$(fx.elem)[value](fx.now);
|
|
};
|
|
});
|
|
var curProxy = $.fx.prototype.cur;
|
|
$.fx.prototype.cur = function () {
|
|
if (transform2d.indexOf(this.prop) != -1) {
|
|
return parseFloat($(this.elem)[this.prop]());
|
|
}
|
|
return curProxy.apply(this, arguments);
|
|
};
|
|
}
|
|
kendo.toggleClass = function (element, classes, options, add) {
|
|
if (classes) {
|
|
classes = classes.split(' ');
|
|
if (transitions) {
|
|
options = extend({
|
|
exclusive: 'all',
|
|
duration: 400,
|
|
ease: 'ease-out'
|
|
}, options);
|
|
element.css(TRANSITION, options.exclusive + ' ' + options.duration + 'ms ' + options.ease);
|
|
setTimeout(function () {
|
|
element.css(TRANSITION, '').css(HEIGHT);
|
|
}, options.duration);
|
|
}
|
|
each(classes, function (idx, value) {
|
|
element.toggleClass(value, add);
|
|
});
|
|
}
|
|
return element;
|
|
};
|
|
kendo.parseEffects = function (input, mirror) {
|
|
var effects = {};
|
|
if (typeof input === 'string') {
|
|
each(input.split(' '), function (idx, value) {
|
|
var redirectedEffect = !singleEffectRegExp.test(value), resolved = value.replace(oldEffectsRegExp, function (match, $1, $2) {
|
|
return $1 + ':' + $2.toLowerCase();
|
|
}), effect = resolved.split(':'), direction = effect[1], effectBody = {};
|
|
if (effect.length > 1) {
|
|
effectBody.direction = mirror && redirectedEffect ? directions[direction].reverse : direction;
|
|
}
|
|
effects[effect[0]] = effectBody;
|
|
});
|
|
} else {
|
|
each(input, function (idx) {
|
|
var direction = this.direction;
|
|
if (direction && mirror && !singleEffectRegExp.test(idx)) {
|
|
this.direction = directions[direction].reverse;
|
|
}
|
|
effects[idx] = this;
|
|
});
|
|
}
|
|
return effects;
|
|
};
|
|
function parseInteger(value) {
|
|
return parseInt(value, 10);
|
|
}
|
|
function parseCSS(element, property) {
|
|
return parseInteger(element.css(property));
|
|
}
|
|
function keys(obj) {
|
|
var acc = [];
|
|
for (var propertyName in obj) {
|
|
acc.push(propertyName);
|
|
}
|
|
return acc;
|
|
}
|
|
function strip3DTransforms(properties) {
|
|
for (var key in properties) {
|
|
if (transformProps.indexOf(key) != -1 && transform2d.indexOf(key) == -1) {
|
|
delete properties[key];
|
|
}
|
|
}
|
|
return properties;
|
|
}
|
|
function normalizeCSS(element, properties) {
|
|
var transformation = [], cssValues = {}, lowerKey, key, value, isTransformed;
|
|
for (key in properties) {
|
|
lowerKey = key.toLowerCase();
|
|
isTransformed = transforms && transformProps.indexOf(lowerKey) != -1;
|
|
if (!support.hasHW3D && isTransformed && transform2d.indexOf(lowerKey) == -1) {
|
|
delete properties[key];
|
|
} else {
|
|
value = properties[key];
|
|
if (isTransformed) {
|
|
transformation.push(key + '(' + value + ')');
|
|
} else {
|
|
cssValues[key] = value;
|
|
}
|
|
}
|
|
}
|
|
if (transformation.length) {
|
|
cssValues[TRANSFORM] = transformation.join(' ');
|
|
}
|
|
return cssValues;
|
|
}
|
|
if (transitions) {
|
|
extend(fx, {
|
|
transition: function (element, properties, options) {
|
|
var css, delay = 0, oldKeys = element.data('keys') || [], timeoutID;
|
|
options = extend({
|
|
duration: 200,
|
|
ease: 'ease-out',
|
|
complete: null,
|
|
exclusive: 'all'
|
|
}, options);
|
|
var stopTransitionCalled = false;
|
|
var stopTransition = function () {
|
|
if (!stopTransitionCalled) {
|
|
stopTransitionCalled = true;
|
|
if (timeoutID) {
|
|
clearTimeout(timeoutID);
|
|
timeoutID = null;
|
|
}
|
|
element.removeData(ABORT_ID).dequeue().css(TRANSITION, '').css(TRANSITION);
|
|
options.complete.call(element);
|
|
}
|
|
};
|
|
options.duration = $.fx ? $.fx.speeds[options.duration] || options.duration : options.duration;
|
|
css = normalizeCSS(element, properties);
|
|
$.merge(oldKeys, keys(css));
|
|
element.data('keys', $.unique(oldKeys)).height();
|
|
element.css(TRANSITION, options.exclusive + ' ' + options.duration + 'ms ' + options.ease).css(TRANSITION);
|
|
element.css(css).css(TRANSFORM);
|
|
if (transitions.event) {
|
|
element.one(transitions.event, stopTransition);
|
|
if (options.duration !== 0) {
|
|
delay = 500;
|
|
}
|
|
}
|
|
timeoutID = setTimeout(stopTransition, options.duration + delay);
|
|
element.data(ABORT_ID, timeoutID);
|
|
element.data(COMPLETE_CALLBACK, stopTransition);
|
|
},
|
|
stopQueue: function (element, clearQueue, gotoEnd) {
|
|
var cssValues, taskKeys = element.data('keys'), retainPosition = !gotoEnd && taskKeys, completeCallback = element.data(COMPLETE_CALLBACK);
|
|
if (retainPosition) {
|
|
cssValues = kendo.getComputedStyles(element[0], taskKeys);
|
|
}
|
|
if (completeCallback) {
|
|
completeCallback();
|
|
}
|
|
if (retainPosition) {
|
|
element.css(cssValues);
|
|
}
|
|
return element.removeData('keys').stop(clearQueue);
|
|
}
|
|
});
|
|
}
|
|
function animationProperty(element, property) {
|
|
if (transforms) {
|
|
var transform = element.css(TRANSFORM);
|
|
if (transform == NONE) {
|
|
return property == 'scale' ? 1 : 0;
|
|
}
|
|
var match = transform.match(new RegExp(property + '\\s*\\(([\\d\\w\\.]+)')), computed = 0;
|
|
if (match) {
|
|
computed = parseInteger(match[1]);
|
|
} else {
|
|
match = transform.match(matrix3dRegExp) || [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
];
|
|
property = property.toLowerCase();
|
|
if (translateXRegExp.test(property)) {
|
|
computed = parseFloat(match[3] / match[2]);
|
|
} else if (property == 'translatey') {
|
|
computed = parseFloat(match[4] / match[2]);
|
|
} else if (property == 'scale') {
|
|
computed = parseFloat(match[2]);
|
|
} else if (property == 'rotate') {
|
|
computed = parseFloat(Math.atan2(match[2], match[1]));
|
|
}
|
|
}
|
|
return computed;
|
|
} else {
|
|
return parseFloat(element.css(property));
|
|
}
|
|
}
|
|
var EffectSet = kendo.Class.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
that.element = element;
|
|
that.effects = [];
|
|
that.options = options;
|
|
that.restore = [];
|
|
},
|
|
run: function (effects) {
|
|
var that = this, effect, idx, jdx, length = effects.length, element = that.element, options = that.options, deferred = $.Deferred(), start = {}, end = {}, target, children, childrenLength;
|
|
that.effects = effects;
|
|
deferred.then($.proxy(that, 'complete'));
|
|
element.data('animating', true);
|
|
for (idx = 0; idx < length; idx++) {
|
|
effect = effects[idx];
|
|
effect.setReverse(options.reverse);
|
|
effect.setOptions(options);
|
|
that.addRestoreProperties(effect.restore);
|
|
effect.prepare(start, end);
|
|
children = effect.children();
|
|
for (jdx = 0, childrenLength = children.length; jdx < childrenLength; jdx++) {
|
|
children[jdx].duration(options.duration).run();
|
|
}
|
|
}
|
|
for (var effectName in options.effects) {
|
|
extend(end, options.effects[effectName].properties);
|
|
}
|
|
if (!element.is(':visible')) {
|
|
extend(start, { display: element.data('olddisplay') || 'block' });
|
|
}
|
|
if (transforms && !options.reset) {
|
|
target = element.data('targetTransform');
|
|
if (target) {
|
|
start = extend(target, start);
|
|
}
|
|
}
|
|
start = normalizeCSS(element, start);
|
|
if (transforms && !transitions) {
|
|
start = strip3DTransforms(start);
|
|
}
|
|
element.css(start).css(TRANSFORM);
|
|
for (idx = 0; idx < length; idx++) {
|
|
effects[idx].setup();
|
|
}
|
|
if (options.init) {
|
|
options.init();
|
|
}
|
|
element.data('targetTransform', end);
|
|
fx.animate(element, end, extend({}, options, { complete: deferred.resolve }));
|
|
return deferred.promise();
|
|
},
|
|
stop: function () {
|
|
$(this.element).kendoStop(true, true);
|
|
},
|
|
addRestoreProperties: function (restore) {
|
|
var element = this.element, value, i = 0, length = restore.length;
|
|
for (; i < length; i++) {
|
|
value = restore[i];
|
|
this.restore.push(value);
|
|
if (!element.data(value)) {
|
|
element.data(value, element.css(value));
|
|
}
|
|
}
|
|
},
|
|
restoreCallback: function () {
|
|
var element = this.element;
|
|
for (var i = 0, length = this.restore.length; i < length; i++) {
|
|
var value = this.restore[i];
|
|
element.css(value, element.data(value));
|
|
}
|
|
},
|
|
complete: function () {
|
|
var that = this, idx = 0, element = that.element, options = that.options, effects = that.effects, length = effects.length;
|
|
element.removeData('animating').dequeue();
|
|
if (options.hide) {
|
|
element.data('olddisplay', element.css('display')).hide();
|
|
}
|
|
this.restoreCallback();
|
|
if (hasZoom && !transforms) {
|
|
setTimeout($.proxy(this, 'restoreCallback'), 0);
|
|
}
|
|
for (; idx < length; idx++) {
|
|
effects[idx].teardown();
|
|
}
|
|
if (options.completeCallback) {
|
|
options.completeCallback(element);
|
|
}
|
|
}
|
|
});
|
|
fx.promise = function (element, options) {
|
|
var effects = [], effectClass, effectSet = new EffectSet(element, options), parsedEffects = kendo.parseEffects(options.effects), effect;
|
|
options.effects = parsedEffects;
|
|
for (var effectName in parsedEffects) {
|
|
effectClass = fx[capitalize(effectName)];
|
|
if (effectClass) {
|
|
effect = new effectClass(element, parsedEffects[effectName].direction);
|
|
effects.push(effect);
|
|
}
|
|
}
|
|
if (effects[0]) {
|
|
effectSet.run(effects);
|
|
} else {
|
|
if (!element.is(':visible')) {
|
|
element.css({ display: element.data('olddisplay') || 'block' }).css('display');
|
|
}
|
|
if (options.init) {
|
|
options.init();
|
|
}
|
|
element.dequeue();
|
|
effectSet.complete();
|
|
}
|
|
};
|
|
extend(fx, {
|
|
animate: function (elements, properties, options) {
|
|
var useTransition = options.transition !== false;
|
|
delete options.transition;
|
|
if (transitions && 'transition' in fx && useTransition) {
|
|
fx.transition(elements, properties, options);
|
|
} else {
|
|
if (transforms) {
|
|
elements.animate(strip3DTransforms(properties), {
|
|
queue: false,
|
|
show: false,
|
|
hide: false,
|
|
duration: options.duration,
|
|
complete: options.complete
|
|
});
|
|
} else {
|
|
elements.each(function () {
|
|
var element = $(this), multiple = {};
|
|
each(transformProps, function (idx, value) {
|
|
var params, currentValue = properties ? properties[value] + ' ' : null;
|
|
if (currentValue) {
|
|
var single = properties;
|
|
if (value in scaleProperties && properties[value] !== undefined) {
|
|
params = currentValue.match(cssParamsRegExp);
|
|
if (transforms) {
|
|
extend(single, { scale: +params[0] });
|
|
}
|
|
} else {
|
|
if (value in translateProperties && properties[value] !== undefined) {
|
|
var position = element.css(POSITION), isFixed = position == 'absolute' || position == 'fixed';
|
|
if (!element.data(TRANSLATE)) {
|
|
if (isFixed) {
|
|
element.data(TRANSLATE, {
|
|
top: parseCSS(element, 'top') || 0,
|
|
left: parseCSS(element, 'left') || 0,
|
|
bottom: parseCSS(element, 'bottom'),
|
|
right: parseCSS(element, 'right')
|
|
});
|
|
} else {
|
|
element.data(TRANSLATE, {
|
|
top: parseCSS(element, 'marginTop') || 0,
|
|
left: parseCSS(element, 'marginLeft') || 0
|
|
});
|
|
}
|
|
}
|
|
var originalPosition = element.data(TRANSLATE);
|
|
params = currentValue.match(cssParamsRegExp);
|
|
if (params) {
|
|
var dX = value == TRANSLATE + 'y' ? +null : +params[1], dY = value == TRANSLATE + 'y' ? +params[1] : +params[2];
|
|
if (isFixed) {
|
|
if (!isNaN(originalPosition.right)) {
|
|
if (!isNaN(dX)) {
|
|
extend(single, { right: originalPosition.right - dX });
|
|
}
|
|
} else {
|
|
if (!isNaN(dX)) {
|
|
extend(single, { left: originalPosition.left + dX });
|
|
}
|
|
}
|
|
if (!isNaN(originalPosition.bottom)) {
|
|
if (!isNaN(dY)) {
|
|
extend(single, { bottom: originalPosition.bottom - dY });
|
|
}
|
|
} else {
|
|
if (!isNaN(dY)) {
|
|
extend(single, { top: originalPosition.top + dY });
|
|
}
|
|
}
|
|
} else {
|
|
if (!isNaN(dX)) {
|
|
extend(single, { marginLeft: originalPosition.left + dX });
|
|
}
|
|
if (!isNaN(dY)) {
|
|
extend(single, { marginTop: originalPosition.top + dY });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!transforms && value != 'scale' && value in single) {
|
|
delete single[value];
|
|
}
|
|
if (single) {
|
|
extend(multiple, single);
|
|
}
|
|
}
|
|
});
|
|
if (browser.msie) {
|
|
delete multiple.scale;
|
|
}
|
|
element.animate(multiple, {
|
|
queue: false,
|
|
show: false,
|
|
hide: false,
|
|
duration: options.duration,
|
|
complete: options.complete
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
fx.animatedPromise = fx.promise;
|
|
var Effect = kendo.Class.extend({
|
|
init: function (element, direction) {
|
|
var that = this;
|
|
that.element = element;
|
|
that._direction = direction;
|
|
that.options = {};
|
|
that._additionalEffects = [];
|
|
if (!that.restore) {
|
|
that.restore = [];
|
|
}
|
|
},
|
|
reverse: function () {
|
|
this._reverse = true;
|
|
return this.run();
|
|
},
|
|
play: function () {
|
|
this._reverse = false;
|
|
return this.run();
|
|
},
|
|
add: function (additional) {
|
|
this._additionalEffects.push(additional);
|
|
return this;
|
|
},
|
|
direction: function (value) {
|
|
this._direction = value;
|
|
return this;
|
|
},
|
|
duration: function (duration) {
|
|
this._duration = duration;
|
|
return this;
|
|
},
|
|
compositeRun: function () {
|
|
var that = this, effectSet = new EffectSet(that.element, {
|
|
reverse: that._reverse,
|
|
duration: that._duration
|
|
}), effects = that._additionalEffects.concat([that]);
|
|
return effectSet.run(effects);
|
|
},
|
|
run: function () {
|
|
if (this._additionalEffects && this._additionalEffects[0]) {
|
|
return this.compositeRun();
|
|
}
|
|
var that = this, element = that.element, idx = 0, restore = that.restore, length = restore.length, value, deferred = $.Deferred(), start = {}, end = {}, target, children = that.children(), childrenLength = children.length;
|
|
deferred.then($.proxy(that, '_complete'));
|
|
element.data('animating', true);
|
|
for (idx = 0; idx < length; idx++) {
|
|
value = restore[idx];
|
|
if (!element.data(value)) {
|
|
element.data(value, element.css(value));
|
|
}
|
|
}
|
|
for (idx = 0; idx < childrenLength; idx++) {
|
|
children[idx].duration(that._duration).run();
|
|
}
|
|
that.prepare(start, end);
|
|
if (!element.is(':visible')) {
|
|
extend(start, { display: element.data('olddisplay') || 'block' });
|
|
}
|
|
if (transforms) {
|
|
target = element.data('targetTransform');
|
|
if (target) {
|
|
start = extend(target, start);
|
|
}
|
|
}
|
|
start = normalizeCSS(element, start);
|
|
if (transforms && !transitions) {
|
|
start = strip3DTransforms(start);
|
|
}
|
|
element.css(start).css(TRANSFORM);
|
|
that.setup();
|
|
element.data('targetTransform', end);
|
|
fx.animate(element, end, {
|
|
duration: that._duration,
|
|
complete: deferred.resolve
|
|
});
|
|
return deferred.promise();
|
|
},
|
|
stop: function () {
|
|
var idx = 0, children = this.children(), childrenLength = children.length;
|
|
for (idx = 0; idx < childrenLength; idx++) {
|
|
children[idx].stop();
|
|
}
|
|
$(this.element).kendoStop(true, true);
|
|
return this;
|
|
},
|
|
restoreCallback: function () {
|
|
var element = this.element;
|
|
for (var i = 0, length = this.restore.length; i < length; i++) {
|
|
var value = this.restore[i];
|
|
element.css(value, element.data(value));
|
|
}
|
|
},
|
|
_complete: function () {
|
|
var that = this, element = that.element;
|
|
element.removeData('animating').dequeue();
|
|
that.restoreCallback();
|
|
if (that.shouldHide()) {
|
|
element.data('olddisplay', element.css('display')).hide();
|
|
}
|
|
if (hasZoom && !transforms) {
|
|
setTimeout($.proxy(that, 'restoreCallback'), 0);
|
|
}
|
|
that.teardown();
|
|
},
|
|
setOptions: function (options) {
|
|
extend(true, this.options, options);
|
|
},
|
|
children: function () {
|
|
return [];
|
|
},
|
|
shouldHide: $.noop,
|
|
setup: $.noop,
|
|
prepare: $.noop,
|
|
teardown: $.noop,
|
|
directions: [],
|
|
setReverse: function (reverse) {
|
|
this._reverse = reverse;
|
|
return this;
|
|
}
|
|
});
|
|
function capitalize(word) {
|
|
return word.charAt(0).toUpperCase() + word.substring(1);
|
|
}
|
|
function createEffect(name, definition) {
|
|
var effectClass = Effect.extend(definition), directions = effectClass.prototype.directions;
|
|
fx[capitalize(name)] = effectClass;
|
|
fx.Element.prototype[name] = function (direction, opt1, opt2, opt3) {
|
|
return new effectClass(this.element, direction, opt1, opt2, opt3);
|
|
};
|
|
each(directions, function (idx, theDirection) {
|
|
fx.Element.prototype[name + capitalize(theDirection)] = function (opt1, opt2, opt3) {
|
|
return new effectClass(this.element, theDirection, opt1, opt2, opt3);
|
|
};
|
|
});
|
|
}
|
|
var FOUR_DIRECTIONS = [
|
|
'left',
|
|
'right',
|
|
'up',
|
|
'down'
|
|
], IN_OUT = [
|
|
'in',
|
|
'out'
|
|
];
|
|
createEffect('slideIn', {
|
|
directions: FOUR_DIRECTIONS,
|
|
divisor: function (value) {
|
|
this.options.divisor = value;
|
|
return this;
|
|
},
|
|
prepare: function (start, end) {
|
|
var that = this, tmp, element = that.element, direction = directions[that._direction], offset = -direction.modifier * (direction.vertical ? element.outerHeight() : element.outerWidth()), startValue = offset / (that.options && that.options.divisor || 1) + PX, endValue = '0px';
|
|
if (that._reverse) {
|
|
tmp = start;
|
|
start = end;
|
|
end = tmp;
|
|
}
|
|
if (transforms) {
|
|
start[direction.transition] = startValue;
|
|
end[direction.transition] = endValue;
|
|
} else {
|
|
start[direction.property] = startValue;
|
|
end[direction.property] = endValue;
|
|
}
|
|
}
|
|
});
|
|
createEffect('tile', {
|
|
directions: FOUR_DIRECTIONS,
|
|
init: function (element, direction, previous) {
|
|
Effect.prototype.init.call(this, element, direction);
|
|
this.options = { previous: previous };
|
|
},
|
|
previousDivisor: function (value) {
|
|
this.options.previousDivisor = value;
|
|
return this;
|
|
},
|
|
children: function () {
|
|
var that = this, reverse = that._reverse, previous = that.options.previous, divisor = that.options.previousDivisor || 1, dir = that._direction;
|
|
var children = [kendo.fx(that.element).slideIn(dir).setReverse(reverse)];
|
|
if (previous) {
|
|
children.push(kendo.fx(previous).slideIn(directions[dir].reverse).divisor(divisor).setReverse(!reverse));
|
|
}
|
|
return children;
|
|
}
|
|
});
|
|
function createToggleEffect(name, property, defaultStart, defaultEnd) {
|
|
createEffect(name, {
|
|
directions: IN_OUT,
|
|
startValue: function (value) {
|
|
this._startValue = value;
|
|
return this;
|
|
},
|
|
endValue: function (value) {
|
|
this._endValue = value;
|
|
return this;
|
|
},
|
|
shouldHide: function () {
|
|
return this._shouldHide;
|
|
},
|
|
prepare: function (start, end) {
|
|
var that = this, startValue, endValue, out = this._direction === 'out', startDataValue = that.element.data(property), startDataValueIsSet = !(isNaN(startDataValue) || startDataValue == defaultStart);
|
|
if (startDataValueIsSet) {
|
|
startValue = startDataValue;
|
|
} else if (typeof this._startValue !== 'undefined') {
|
|
startValue = this._startValue;
|
|
} else {
|
|
startValue = out ? defaultStart : defaultEnd;
|
|
}
|
|
if (typeof this._endValue !== 'undefined') {
|
|
endValue = this._endValue;
|
|
} else {
|
|
endValue = out ? defaultEnd : defaultStart;
|
|
}
|
|
if (this._reverse) {
|
|
start[property] = endValue;
|
|
end[property] = startValue;
|
|
} else {
|
|
start[property] = startValue;
|
|
end[property] = endValue;
|
|
}
|
|
that._shouldHide = end[property] === defaultEnd;
|
|
}
|
|
});
|
|
}
|
|
createToggleEffect('fade', 'opacity', 1, 0);
|
|
createToggleEffect('zoom', 'scale', 1, 0.01);
|
|
createEffect('slideMargin', {
|
|
prepare: function (start, end) {
|
|
var that = this, element = that.element, options = that.options, origin = element.data(ORIGIN), offset = options.offset, margin, reverse = that._reverse;
|
|
if (!reverse && origin === null) {
|
|
element.data(ORIGIN, parseFloat(element.css('margin-' + options.axis)));
|
|
}
|
|
margin = element.data(ORIGIN) || 0;
|
|
end['margin-' + options.axis] = !reverse ? margin + offset : margin;
|
|
}
|
|
});
|
|
createEffect('slideTo', {
|
|
prepare: function (start, end) {
|
|
var that = this, element = that.element, options = that.options, offset = options.offset.split(','), reverse = that._reverse;
|
|
if (transforms) {
|
|
end.translatex = !reverse ? offset[0] : 0;
|
|
end.translatey = !reverse ? offset[1] : 0;
|
|
} else {
|
|
end.left = !reverse ? offset[0] : 0;
|
|
end.top = !reverse ? offset[1] : 0;
|
|
}
|
|
element.css('left');
|
|
}
|
|
});
|
|
createEffect('expand', {
|
|
directions: [
|
|
'horizontal',
|
|
'vertical'
|
|
],
|
|
restore: [OVERFLOW],
|
|
prepare: function (start, end) {
|
|
var that = this, element = that.element, options = that.options, reverse = that._reverse, property = that._direction === 'vertical' ? HEIGHT : WIDTH, setLength = element[0].style[property], oldLength = element.data(property), length = parseFloat(oldLength || setLength), realLength = round(element.css(property, AUTO)[property]());
|
|
start.overflow = HIDDEN;
|
|
length = options && options.reset ? realLength || length : length || realLength;
|
|
end[property] = (reverse ? 0 : length) + PX;
|
|
start[property] = (reverse ? length : 0) + PX;
|
|
if (oldLength === undefined) {
|
|
element.data(property, setLength);
|
|
}
|
|
},
|
|
shouldHide: function () {
|
|
return this._reverse;
|
|
},
|
|
teardown: function () {
|
|
var that = this, element = that.element, property = that._direction === 'vertical' ? HEIGHT : WIDTH, length = element.data(property);
|
|
if (length == AUTO || length === BLANK) {
|
|
setTimeout(function () {
|
|
element.css(property, AUTO).css(property);
|
|
}, 0);
|
|
}
|
|
}
|
|
});
|
|
var TRANSFER_START_STATE = {
|
|
position: 'absolute',
|
|
marginLeft: 0,
|
|
marginTop: 0,
|
|
scale: 1
|
|
};
|
|
createEffect('transfer', {
|
|
init: function (element, target) {
|
|
this.element = element;
|
|
this.options = { target: target };
|
|
this.restore = [];
|
|
},
|
|
setup: function () {
|
|
this.element.appendTo(document.body);
|
|
},
|
|
prepare: function (start, end) {
|
|
var that = this, element = that.element, outerBox = fx.box(element), innerBox = fx.box(that.options.target), currentScale = animationProperty(element, 'scale'), scale = fx.fillScale(innerBox, outerBox), transformOrigin = fx.transformOrigin(innerBox, outerBox);
|
|
extend(start, TRANSFER_START_STATE);
|
|
end.scale = 1;
|
|
element.css(TRANSFORM, 'scale(1)').css(TRANSFORM);
|
|
element.css(TRANSFORM, 'scale(' + currentScale + ')');
|
|
start.top = outerBox.top;
|
|
start.left = outerBox.left;
|
|
start.transformOrigin = transformOrigin.x + PX + ' ' + transformOrigin.y + PX;
|
|
if (that._reverse) {
|
|
start.scale = scale;
|
|
} else {
|
|
end.scale = scale;
|
|
}
|
|
}
|
|
});
|
|
var CLIPS = {
|
|
top: 'rect(auto auto $size auto)',
|
|
bottom: 'rect($size auto auto auto)',
|
|
left: 'rect(auto $size auto auto)',
|
|
right: 'rect(auto auto auto $size)'
|
|
};
|
|
var ROTATIONS = {
|
|
top: {
|
|
start: 'rotatex(0deg)',
|
|
end: 'rotatex(180deg)'
|
|
},
|
|
bottom: {
|
|
start: 'rotatex(-180deg)',
|
|
end: 'rotatex(0deg)'
|
|
},
|
|
left: {
|
|
start: 'rotatey(0deg)',
|
|
end: 'rotatey(-180deg)'
|
|
},
|
|
right: {
|
|
start: 'rotatey(180deg)',
|
|
end: 'rotatey(0deg)'
|
|
}
|
|
};
|
|
function clipInHalf(container, direction) {
|
|
var vertical = kendo.directions[direction].vertical, size = container[vertical ? HEIGHT : WIDTH]() / 2 + 'px';
|
|
return CLIPS[direction].replace('$size', size);
|
|
}
|
|
createEffect('turningPage', {
|
|
directions: FOUR_DIRECTIONS,
|
|
init: function (element, direction, container) {
|
|
Effect.prototype.init.call(this, element, direction);
|
|
this._container = container;
|
|
},
|
|
prepare: function (start, end) {
|
|
var that = this, reverse = that._reverse, direction = reverse ? directions[that._direction].reverse : that._direction, rotation = ROTATIONS[direction];
|
|
start.zIndex = 1;
|
|
if (that._clipInHalf) {
|
|
start.clip = clipInHalf(that._container, kendo.directions[direction].reverse);
|
|
}
|
|
start[BACKFACE] = HIDDEN;
|
|
end[TRANSFORM] = TRANSFORM_PERSPECTIVE + (reverse ? rotation.start : rotation.end);
|
|
start[TRANSFORM] = TRANSFORM_PERSPECTIVE + (reverse ? rotation.end : rotation.start);
|
|
},
|
|
setup: function () {
|
|
this._container.append(this.element);
|
|
},
|
|
face: function (value) {
|
|
this._face = value;
|
|
return this;
|
|
},
|
|
shouldHide: function () {
|
|
var that = this, reverse = that._reverse, face = that._face;
|
|
return reverse && !face || !reverse && face;
|
|
},
|
|
clipInHalf: function (value) {
|
|
this._clipInHalf = value;
|
|
return this;
|
|
},
|
|
temporary: function () {
|
|
this.element.addClass('temp-page');
|
|
return this;
|
|
}
|
|
});
|
|
createEffect('staticPage', {
|
|
directions: FOUR_DIRECTIONS,
|
|
init: function (element, direction, container) {
|
|
Effect.prototype.init.call(this, element, direction);
|
|
this._container = container;
|
|
},
|
|
restore: ['clip'],
|
|
prepare: function (start, end) {
|
|
var that = this, direction = that._reverse ? directions[that._direction].reverse : that._direction;
|
|
start.clip = clipInHalf(that._container, direction);
|
|
start.opacity = 0.999;
|
|
end.opacity = 1;
|
|
},
|
|
shouldHide: function () {
|
|
var that = this, reverse = that._reverse, face = that._face;
|
|
return reverse && !face || !reverse && face;
|
|
},
|
|
face: function (value) {
|
|
this._face = value;
|
|
return this;
|
|
}
|
|
});
|
|
createEffect('pageturn', {
|
|
directions: [
|
|
'horizontal',
|
|
'vertical'
|
|
],
|
|
init: function (element, direction, face, back) {
|
|
Effect.prototype.init.call(this, element, direction);
|
|
this.options = {};
|
|
this.options.face = face;
|
|
this.options.back = back;
|
|
},
|
|
children: function () {
|
|
var that = this, options = that.options, direction = that._direction === 'horizontal' ? 'left' : 'top', reverseDirection = kendo.directions[direction].reverse, reverse = that._reverse, temp, faceClone = options.face.clone(true).removeAttr('id'), backClone = options.back.clone(true).removeAttr('id'), element = that.element;
|
|
if (reverse) {
|
|
temp = direction;
|
|
direction = reverseDirection;
|
|
reverseDirection = temp;
|
|
}
|
|
return [
|
|
kendo.fx(options.face).staticPage(direction, element).face(true).setReverse(reverse),
|
|
kendo.fx(options.back).staticPage(reverseDirection, element).setReverse(reverse),
|
|
kendo.fx(faceClone).turningPage(direction, element).face(true).clipInHalf(true).temporary().setReverse(reverse),
|
|
kendo.fx(backClone).turningPage(reverseDirection, element).clipInHalf(true).temporary().setReverse(reverse)
|
|
];
|
|
},
|
|
prepare: function (start, end) {
|
|
start[PERSPECTIVE] = DEFAULT_PERSPECTIVE;
|
|
start.transformStyle = 'preserve-3d';
|
|
start.opacity = 0.999;
|
|
end.opacity = 1;
|
|
},
|
|
teardown: function () {
|
|
this.element.find('.temp-page').remove();
|
|
}
|
|
});
|
|
createEffect('flip', {
|
|
directions: [
|
|
'horizontal',
|
|
'vertical'
|
|
],
|
|
init: function (element, direction, face, back) {
|
|
Effect.prototype.init.call(this, element, direction);
|
|
this.options = {};
|
|
this.options.face = face;
|
|
this.options.back = back;
|
|
},
|
|
children: function () {
|
|
var that = this, options = that.options, direction = that._direction === 'horizontal' ? 'left' : 'top', reverseDirection = kendo.directions[direction].reverse, reverse = that._reverse, temp, element = that.element;
|
|
if (reverse) {
|
|
temp = direction;
|
|
direction = reverseDirection;
|
|
reverseDirection = temp;
|
|
}
|
|
return [
|
|
kendo.fx(options.face).turningPage(direction, element).face(true).setReverse(reverse),
|
|
kendo.fx(options.back).turningPage(reverseDirection, element).setReverse(reverse)
|
|
];
|
|
},
|
|
prepare: function (start) {
|
|
start[PERSPECTIVE] = DEFAULT_PERSPECTIVE;
|
|
start.transformStyle = 'preserve-3d';
|
|
}
|
|
});
|
|
var RESTORE_OVERFLOW = !support.mobileOS.android;
|
|
var IGNORE_TRANSITION_EVENT_SELECTOR = '.km-touch-scrollbar, .km-actionsheet-wrapper';
|
|
createEffect('replace', {
|
|
_before: $.noop,
|
|
_after: $.noop,
|
|
init: function (element, previous, transitionClass) {
|
|
Effect.prototype.init.call(this, element);
|
|
this._previous = $(previous);
|
|
this._transitionClass = transitionClass;
|
|
},
|
|
duration: function () {
|
|
throw new Error('The replace effect does not support duration setting; the effect duration may be customized through the transition class rule');
|
|
},
|
|
beforeTransition: function (callback) {
|
|
this._before = callback;
|
|
return this;
|
|
},
|
|
afterTransition: function (callback) {
|
|
this._after = callback;
|
|
return this;
|
|
},
|
|
_both: function () {
|
|
return $().add(this._element).add(this._previous);
|
|
},
|
|
_containerClass: function () {
|
|
var direction = this._direction, containerClass = 'k-fx k-fx-start k-fx-' + this._transitionClass;
|
|
if (direction) {
|
|
containerClass += ' k-fx-' + direction;
|
|
}
|
|
if (this._reverse) {
|
|
containerClass += ' k-fx-reverse';
|
|
}
|
|
return containerClass;
|
|
},
|
|
complete: function (e) {
|
|
if (!this.deferred || e && $(e.target).is(IGNORE_TRANSITION_EVENT_SELECTOR)) {
|
|
return;
|
|
}
|
|
var container = this.container;
|
|
container.removeClass('k-fx-end').removeClass(this._containerClass()).off(transitions.event, this.completeProxy);
|
|
this._previous.hide().removeClass('k-fx-current');
|
|
this.element.removeClass('k-fx-next');
|
|
if (RESTORE_OVERFLOW) {
|
|
container.css(OVERFLOW, '');
|
|
}
|
|
if (!this.isAbsolute) {
|
|
this._both().css(POSITION, '');
|
|
}
|
|
this.deferred.resolve();
|
|
delete this.deferred;
|
|
},
|
|
run: function () {
|
|
if (this._additionalEffects && this._additionalEffects[0]) {
|
|
return this.compositeRun();
|
|
}
|
|
var that = this, element = that.element, previous = that._previous, container = element.parents().filter(previous.parents()).first(), both = that._both(), deferred = $.Deferred(), originalPosition = element.css(POSITION), originalOverflow;
|
|
if (!container.length) {
|
|
container = element.parent();
|
|
}
|
|
this.container = container;
|
|
this.deferred = deferred;
|
|
this.isAbsolute = originalPosition == 'absolute';
|
|
if (!this.isAbsolute) {
|
|
both.css(POSITION, 'absolute');
|
|
}
|
|
if (RESTORE_OVERFLOW) {
|
|
originalOverflow = container.css(OVERFLOW);
|
|
container.css(OVERFLOW, 'hidden');
|
|
}
|
|
if (!transitions) {
|
|
this.complete();
|
|
} else {
|
|
element.addClass('k-fx-hidden');
|
|
container.addClass(this._containerClass());
|
|
this.completeProxy = $.proxy(this, 'complete');
|
|
container.on(transitions.event, this.completeProxy);
|
|
kendo.animationFrame(function () {
|
|
element.removeClass('k-fx-hidden').addClass('k-fx-next');
|
|
previous.css('display', '').addClass('k-fx-current');
|
|
that._before(previous, element);
|
|
kendo.animationFrame(function () {
|
|
container.removeClass('k-fx-start').addClass('k-fx-end');
|
|
that._after(previous, element);
|
|
});
|
|
});
|
|
}
|
|
return deferred.promise();
|
|
},
|
|
stop: function () {
|
|
this.complete();
|
|
}
|
|
});
|
|
var Animation = kendo.Class.extend({
|
|
init: function () {
|
|
var that = this;
|
|
that._tickProxy = proxy(that._tick, that);
|
|
that._started = false;
|
|
},
|
|
tick: $.noop,
|
|
done: $.noop,
|
|
onEnd: $.noop,
|
|
onCancel: $.noop,
|
|
start: function () {
|
|
if (!this.enabled()) {
|
|
return;
|
|
}
|
|
if (!this.done()) {
|
|
this._started = true;
|
|
kendo.animationFrame(this._tickProxy);
|
|
} else {
|
|
this.onEnd();
|
|
}
|
|
},
|
|
enabled: function () {
|
|
return true;
|
|
},
|
|
cancel: function () {
|
|
this._started = false;
|
|
this.onCancel();
|
|
},
|
|
_tick: function () {
|
|
var that = this;
|
|
if (!that._started) {
|
|
return;
|
|
}
|
|
that.tick();
|
|
if (!that.done()) {
|
|
kendo.animationFrame(that._tickProxy);
|
|
} else {
|
|
that._started = false;
|
|
that.onEnd();
|
|
}
|
|
}
|
|
});
|
|
var Transition = Animation.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
extend(that, options);
|
|
Animation.fn.init.call(that);
|
|
},
|
|
done: function () {
|
|
return this.timePassed() >= this.duration;
|
|
},
|
|
timePassed: function () {
|
|
return Math.min(this.duration, new Date() - this.startDate);
|
|
},
|
|
moveTo: function (options) {
|
|
var that = this, movable = that.movable;
|
|
that.initial = movable[that.axis];
|
|
that.delta = options.location - that.initial;
|
|
that.duration = typeof options.duration == 'number' ? options.duration : 300;
|
|
that.tick = that._easeProxy(options.ease);
|
|
that.startDate = new Date();
|
|
that.start();
|
|
},
|
|
_easeProxy: function (ease) {
|
|
var that = this;
|
|
return function () {
|
|
that.movable.moveAxis(that.axis, ease(that.timePassed(), that.initial, that.delta, that.duration));
|
|
};
|
|
}
|
|
});
|
|
extend(Transition, {
|
|
easeOutExpo: function (t, b, c, d) {
|
|
return t == d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
|
|
},
|
|
easeOutBack: function (t, b, c, d, s) {
|
|
s = 1.70158;
|
|
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
|
|
}
|
|
});
|
|
fx.Animation = Animation;
|
|
fx.Transition = Transition;
|
|
fx.createEffect = createEffect;
|
|
fx.box = function (element) {
|
|
element = $(element);
|
|
var result = element.offset();
|
|
result.width = element.outerWidth();
|
|
result.height = element.outerHeight();
|
|
return result;
|
|
};
|
|
fx.transformOrigin = function (inner, outer) {
|
|
var x = (inner.left - outer.left) * outer.width / (outer.width - inner.width), y = (inner.top - outer.top) * outer.height / (outer.height - inner.height);
|
|
return {
|
|
x: isNaN(x) ? 0 : x,
|
|
y: isNaN(y) ? 0 : y
|
|
};
|
|
};
|
|
fx.fillScale = function (inner, outer) {
|
|
return Math.min(inner.width / outer.width, inner.height / outer.height);
|
|
};
|
|
fx.fitScale = function (inner, outer) {
|
|
return Math.max(inner.width / outer.width, inner.height / outer.height);
|
|
};
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.view', [
|
|
'kendo.core',
|
|
'kendo.binder',
|
|
'kendo.fx'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'view',
|
|
name: 'View',
|
|
category: 'framework',
|
|
description: 'The View class instantiates and handles the events of a certain screen from the application.',
|
|
depends: [
|
|
'core',
|
|
'binder',
|
|
'fx'
|
|
],
|
|
hidden: false
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Observable = kendo.Observable, SCRIPT = 'SCRIPT', INIT = 'init', SHOW = 'show', HIDE = 'hide', TRANSITION_START = 'transitionStart', TRANSITION_END = 'transitionEnd', ATTACH = 'attach', DETACH = 'detach', sizzleErrorRegExp = /unrecognized expression/;
|
|
var View = Observable.extend({
|
|
init: function (content, options) {
|
|
var that = this;
|
|
options = options || {};
|
|
Observable.fn.init.call(that);
|
|
that.content = content;
|
|
that.id = kendo.guid();
|
|
that.tagName = options.tagName || 'div';
|
|
that.model = options.model;
|
|
that._wrap = options.wrap !== false;
|
|
this._evalTemplate = options.evalTemplate || false;
|
|
that._fragments = {};
|
|
that.bind([
|
|
INIT,
|
|
SHOW,
|
|
HIDE,
|
|
TRANSITION_START,
|
|
TRANSITION_END
|
|
], options);
|
|
},
|
|
render: function (container) {
|
|
var that = this, notInitialized = !that.element;
|
|
if (notInitialized) {
|
|
that.element = that._createElement();
|
|
}
|
|
if (container) {
|
|
$(container).append(that.element);
|
|
}
|
|
if (notInitialized) {
|
|
kendo.bind(that.element, that.model);
|
|
that.trigger(INIT);
|
|
}
|
|
if (container) {
|
|
that._eachFragment(ATTACH);
|
|
that.trigger(SHOW);
|
|
}
|
|
return that.element;
|
|
},
|
|
clone: function () {
|
|
return new ViewClone(this);
|
|
},
|
|
triggerBeforeShow: function () {
|
|
return true;
|
|
},
|
|
triggerBeforeHide: function () {
|
|
return true;
|
|
},
|
|
showStart: function () {
|
|
this.element.css('display', '');
|
|
},
|
|
showEnd: function () {
|
|
},
|
|
hideEnd: function () {
|
|
this.hide();
|
|
},
|
|
beforeTransition: function (type) {
|
|
this.trigger(TRANSITION_START, { type: type });
|
|
},
|
|
afterTransition: function (type) {
|
|
this.trigger(TRANSITION_END, { type: type });
|
|
},
|
|
hide: function () {
|
|
this._eachFragment(DETACH);
|
|
this.element.detach();
|
|
this.trigger(HIDE);
|
|
},
|
|
destroy: function () {
|
|
var element = this.element;
|
|
if (element) {
|
|
kendo.unbind(element);
|
|
kendo.destroy(element);
|
|
element.remove();
|
|
}
|
|
},
|
|
fragments: function (fragments) {
|
|
$.extend(this._fragments, fragments);
|
|
},
|
|
_eachFragment: function (methodName) {
|
|
for (var placeholder in this._fragments) {
|
|
this._fragments[placeholder][methodName](this, placeholder);
|
|
}
|
|
},
|
|
_createElement: function () {
|
|
var that = this, wrapper = '<' + that.tagName + ' />', element, content;
|
|
try {
|
|
content = $(document.getElementById(that.content) || that.content);
|
|
if (content[0].tagName === SCRIPT) {
|
|
content = content.html();
|
|
}
|
|
} catch (e) {
|
|
if (sizzleErrorRegExp.test(e.message)) {
|
|
content = that.content;
|
|
}
|
|
}
|
|
if (typeof content === 'string') {
|
|
content = content.replace(/^\s+|\s+$/g, '');
|
|
if (that._evalTemplate) {
|
|
content = kendo.template(content)(that.model || {});
|
|
}
|
|
element = $(wrapper).append(content);
|
|
if (!that._wrap) {
|
|
element = element.contents();
|
|
}
|
|
} else {
|
|
element = content;
|
|
if (that._evalTemplate) {
|
|
var result = $(kendo.template($('<div />').append(element.clone(true)).html())(that.model || {}));
|
|
if ($.contains(document, element[0])) {
|
|
element.replaceWith(result);
|
|
}
|
|
element = result;
|
|
}
|
|
if (that._wrap) {
|
|
element = element.wrapAll(wrapper).parent();
|
|
}
|
|
}
|
|
return element;
|
|
}
|
|
});
|
|
var ViewClone = kendo.Class.extend({
|
|
init: function (view) {
|
|
$.extend(this, {
|
|
element: view.element.clone(true),
|
|
transition: view.transition,
|
|
id: view.id
|
|
});
|
|
view.element.parent().append(this.element);
|
|
},
|
|
hideEnd: function () {
|
|
this.element.remove();
|
|
},
|
|
beforeTransition: $.noop,
|
|
afterTransition: $.noop
|
|
});
|
|
var Layout = View.extend({
|
|
init: function (content, options) {
|
|
View.fn.init.call(this, content, options);
|
|
this.containers = {};
|
|
},
|
|
container: function (selector) {
|
|
var container = this.containers[selector];
|
|
if (!container) {
|
|
container = this._createContainer(selector);
|
|
this.containers[selector] = container;
|
|
}
|
|
return container;
|
|
},
|
|
showIn: function (selector, view, transition) {
|
|
this.container(selector).show(view, transition);
|
|
},
|
|
_createContainer: function (selector) {
|
|
var root = this.render(), element = root.find(selector), container;
|
|
if (!element.length && root.is(selector)) {
|
|
if (root.is(selector)) {
|
|
element = root;
|
|
} else {
|
|
throw new Error('can\'t find a container with the specified ' + selector + ' selector');
|
|
}
|
|
}
|
|
container = new ViewContainer(element);
|
|
container.bind('accepted', function (e) {
|
|
e.view.render(element);
|
|
});
|
|
return container;
|
|
}
|
|
});
|
|
var Fragment = View.extend({
|
|
attach: function (view, placeholder) {
|
|
view.element.find(placeholder).replaceWith(this.render());
|
|
},
|
|
detach: function () {
|
|
}
|
|
});
|
|
var transitionRegExp = /^(\w+)(:(\w+))?( (\w+))?$/;
|
|
function parseTransition(transition) {
|
|
if (!transition) {
|
|
return {};
|
|
}
|
|
var matches = transition.match(transitionRegExp) || [];
|
|
return {
|
|
type: matches[1],
|
|
direction: matches[3],
|
|
reverse: matches[5] === 'reverse'
|
|
};
|
|
}
|
|
var ViewContainer = Observable.extend({
|
|
init: function (container) {
|
|
Observable.fn.init.call(this);
|
|
this.container = container;
|
|
this.history = [];
|
|
this.view = null;
|
|
this.running = false;
|
|
},
|
|
after: function () {
|
|
this.running = false;
|
|
this.trigger('complete', { view: this.view });
|
|
this.trigger('after');
|
|
},
|
|
end: function () {
|
|
this.view.showEnd();
|
|
this.previous.hideEnd();
|
|
this.after();
|
|
},
|
|
show: function (view, transition, locationID) {
|
|
if (!view.triggerBeforeShow() || this.view && !this.view.triggerBeforeHide()) {
|
|
this.trigger('after');
|
|
return false;
|
|
}
|
|
locationID = locationID || view.id;
|
|
var that = this, current = view === that.view ? view.clone() : that.view, history = that.history, previousEntry = history[history.length - 2] || {}, back = previousEntry.id === locationID, theTransition = transition || (back ? history[history.length - 1].transition : view.transition), transitionData = parseTransition(theTransition);
|
|
if (that.running) {
|
|
that.effect.stop();
|
|
}
|
|
if (theTransition === 'none') {
|
|
theTransition = null;
|
|
}
|
|
that.trigger('accepted', { view: view });
|
|
that.view = view;
|
|
that.previous = current;
|
|
that.running = true;
|
|
if (!back) {
|
|
history.push({
|
|
id: locationID,
|
|
transition: theTransition
|
|
});
|
|
} else {
|
|
history.pop();
|
|
}
|
|
if (!current) {
|
|
view.showStart();
|
|
view.showEnd();
|
|
that.after();
|
|
return true;
|
|
}
|
|
if (!theTransition || !kendo.effects.enabled) {
|
|
view.showStart();
|
|
that.end();
|
|
} else {
|
|
view.element.addClass('k-fx-hidden');
|
|
view.showStart();
|
|
if (back && !transition) {
|
|
transitionData.reverse = !transitionData.reverse;
|
|
}
|
|
that.effect = kendo.fx(view.element).replace(current.element, transitionData.type).beforeTransition(function () {
|
|
view.beforeTransition('show');
|
|
current.beforeTransition('hide');
|
|
}).afterTransition(function () {
|
|
view.afterTransition('show');
|
|
current.afterTransition('hide');
|
|
}).direction(transitionData.direction).setReverse(transitionData.reverse);
|
|
that.effect.run().then(function () {
|
|
that.end();
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
kendo.ViewContainer = ViewContainer;
|
|
kendo.Fragment = Fragment;
|
|
kendo.Layout = Layout;
|
|
kendo.View = View;
|
|
kendo.ViewClone = ViewClone;
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dom', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dom',
|
|
name: 'Virtual DOM',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
advanced: true
|
|
};
|
|
(function (kendo) {
|
|
function Node() {
|
|
this.node = null;
|
|
}
|
|
Node.prototype = {
|
|
remove: function () {
|
|
this.node.parentNode.removeChild(this.node);
|
|
this.attr = {};
|
|
},
|
|
attr: {},
|
|
text: function () {
|
|
return '';
|
|
}
|
|
};
|
|
function NullNode() {
|
|
}
|
|
NullNode.prototype = {
|
|
nodeName: '#null',
|
|
attr: { style: {} },
|
|
children: [],
|
|
remove: function () {
|
|
}
|
|
};
|
|
var NULL_NODE = new NullNode();
|
|
function Element(nodeName, attr, children) {
|
|
this.nodeName = nodeName;
|
|
this.attr = attr || {};
|
|
this.children = children || [];
|
|
}
|
|
Element.prototype = new Node();
|
|
Element.prototype.appendTo = function (parent) {
|
|
var node = document.createElement(this.nodeName);
|
|
var children = this.children;
|
|
for (var index = 0; index < children.length; index++) {
|
|
children[index].render(node, NULL_NODE);
|
|
}
|
|
parent.appendChild(node);
|
|
return node;
|
|
};
|
|
Element.prototype.render = function (parent, cached) {
|
|
var node;
|
|
if (cached.nodeName !== this.nodeName) {
|
|
cached.remove();
|
|
node = this.appendTo(parent);
|
|
} else {
|
|
node = cached.node;
|
|
var index;
|
|
var children = this.children;
|
|
var length = children.length;
|
|
var cachedChildren = cached.children;
|
|
var cachedLength = cachedChildren.length;
|
|
if (Math.abs(cachedLength - length) > 2) {
|
|
this.render({
|
|
appendChild: function (node) {
|
|
parent.replaceChild(node, cached.node);
|
|
}
|
|
}, NULL_NODE);
|
|
return;
|
|
}
|
|
for (index = 0; index < length; index++) {
|
|
children[index].render(node, cachedChildren[index] || NULL_NODE);
|
|
}
|
|
for (index = length; index < cachedLength; index++) {
|
|
cachedChildren[index].remove();
|
|
}
|
|
}
|
|
this.node = node;
|
|
this.syncAttributes(cached.attr);
|
|
this.removeAttributes(cached.attr);
|
|
};
|
|
Element.prototype.syncAttributes = function (cachedAttr) {
|
|
var attr = this.attr;
|
|
for (var name in attr) {
|
|
var value = attr[name];
|
|
var cachedValue = cachedAttr[name];
|
|
if (name === 'style') {
|
|
this.setStyle(value, cachedValue);
|
|
} else if (value !== cachedValue) {
|
|
this.setAttribute(name, value, cachedValue);
|
|
}
|
|
}
|
|
};
|
|
Element.prototype.setStyle = function (style, cachedValue) {
|
|
var node = this.node;
|
|
var key;
|
|
if (cachedValue) {
|
|
for (key in style) {
|
|
if (style[key] !== cachedValue[key]) {
|
|
node.style[key] = style[key];
|
|
}
|
|
}
|
|
} else {
|
|
for (key in style) {
|
|
node.style[key] = style[key];
|
|
}
|
|
}
|
|
};
|
|
Element.prototype.removeStyle = function (cachedStyle) {
|
|
var style = this.attr.style || {};
|
|
var node = this.node;
|
|
for (var key in cachedStyle) {
|
|
if (style[key] === undefined) {
|
|
node.style[key] = '';
|
|
}
|
|
}
|
|
};
|
|
Element.prototype.removeAttributes = function (cachedAttr) {
|
|
var attr = this.attr;
|
|
for (var name in cachedAttr) {
|
|
if (name === 'style') {
|
|
this.removeStyle(cachedAttr.style);
|
|
} else if (attr[name] === undefined) {
|
|
this.removeAttribute(name);
|
|
}
|
|
}
|
|
};
|
|
Element.prototype.removeAttribute = function (name) {
|
|
var node = this.node;
|
|
if (name === 'style') {
|
|
node.style.cssText = '';
|
|
} else if (name === 'className') {
|
|
node.className = '';
|
|
} else {
|
|
node.removeAttribute(name);
|
|
}
|
|
};
|
|
Element.prototype.setAttribute = function (name, value) {
|
|
var node = this.node;
|
|
if (node[name] !== undefined) {
|
|
node[name] = value;
|
|
} else {
|
|
node.setAttribute(name, value);
|
|
}
|
|
};
|
|
Element.prototype.text = function () {
|
|
var str = '';
|
|
for (var i = 0; i < this.children.length; ++i) {
|
|
str += this.children[i].text();
|
|
}
|
|
return str;
|
|
};
|
|
function TextNode(nodeValue) {
|
|
this.nodeValue = nodeValue;
|
|
}
|
|
TextNode.prototype = new Node();
|
|
TextNode.prototype.nodeName = '#text';
|
|
TextNode.prototype.render = function (parent, cached) {
|
|
var node;
|
|
if (cached.nodeName !== this.nodeName) {
|
|
cached.remove();
|
|
node = document.createTextNode(this.nodeValue);
|
|
parent.appendChild(node);
|
|
} else {
|
|
node = cached.node;
|
|
if (this.nodeValue !== cached.nodeValue) {
|
|
node.nodeValue = this.nodeValue;
|
|
}
|
|
}
|
|
this.node = node;
|
|
};
|
|
TextNode.prototype.text = function () {
|
|
return this.nodeValue;
|
|
};
|
|
function HtmlNode(html) {
|
|
this.html = html;
|
|
}
|
|
HtmlNode.prototype = {
|
|
nodeName: '#html',
|
|
attr: {},
|
|
remove: function () {
|
|
for (var index = 0; index < this.nodes.length; index++) {
|
|
this.nodes[index].parentNode.removeChild(this.nodes[index]);
|
|
}
|
|
},
|
|
render: function (parent, cached) {
|
|
if (cached.nodeName !== this.nodeName || cached.html !== this.html) {
|
|
cached.remove();
|
|
var lastChild = parent.lastChild;
|
|
insertHtml(parent, this.html);
|
|
this.nodes = [];
|
|
for (var child = lastChild ? lastChild.nextSibling : parent.firstChild; child; child = child.nextSibling) {
|
|
this.nodes.push(child);
|
|
}
|
|
} else {
|
|
this.nodes = cached.nodes.slice(0);
|
|
}
|
|
}
|
|
};
|
|
var HTML_CONTAINER = document.createElement('div');
|
|
function insertHtml(node, html) {
|
|
HTML_CONTAINER.innerHTML = html;
|
|
while (HTML_CONTAINER.firstChild) {
|
|
node.appendChild(HTML_CONTAINER.firstChild);
|
|
}
|
|
}
|
|
function html(value) {
|
|
return new HtmlNode(value);
|
|
}
|
|
function element(nodeName, attrs, children) {
|
|
return new Element(nodeName, attrs, children);
|
|
}
|
|
function text(value) {
|
|
return new TextNode(value);
|
|
}
|
|
function Tree(root) {
|
|
this.root = root;
|
|
this.children = [];
|
|
}
|
|
Tree.prototype = {
|
|
html: html,
|
|
element: element,
|
|
text: text,
|
|
render: function (children) {
|
|
var cachedChildren = this.children;
|
|
var index;
|
|
var length;
|
|
for (index = 0, length = children.length; index < length; index++) {
|
|
children[index].render(this.root, cachedChildren[index] || NULL_NODE);
|
|
}
|
|
for (index = length; index < cachedChildren.length; index++) {
|
|
cachedChildren[index].remove();
|
|
}
|
|
this.children = children;
|
|
}
|
|
};
|
|
kendo.dom = {
|
|
html: html,
|
|
text: text,
|
|
element: element,
|
|
Tree: Tree,
|
|
Node: Node
|
|
};
|
|
}(window.kendo));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.ooxml', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'ooxml',
|
|
name: 'XLSX generation',
|
|
category: 'framework',
|
|
advanced: true,
|
|
depends: ['core']
|
|
};
|
|
(function ($, kendo) {
|
|
var RELS = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' + '<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>' + '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>' + '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' + '</Relationships>';
|
|
var CORE = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" ' + 'xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" ' + 'xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + '<dc:creator>${creator}</dc:creator>' + '<cp:lastModifiedBy>${lastModifiedBy}</cp:lastModifiedBy>' + '<dcterms:created xsi:type="dcterms:W3CDTF">${created}</dcterms:created>' + '<dcterms:modified xsi:type="dcterms:W3CDTF">${modified}</dcterms:modified>' + '</cp:coreProperties>');
|
|
var APP = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">' + '<Application>Microsoft Excel</Application>' + '<DocSecurity>0</DocSecurity>' + '<ScaleCrop>false</ScaleCrop>' + '<HeadingPairs>' + '<vt:vector size="2" baseType="variant">' + '<vt:variant>' + '<vt:lpstr>Worksheets</vt:lpstr>' + '</vt:variant>' + '<vt:variant>' + '<vt:i4>${sheets.length}</vt:i4>' + '</vt:variant>' + '</vt:vector>' + '</HeadingPairs>' + '<TitlesOfParts>' + '<vt:vector size="${sheets.length}" baseType="lpstr">' + '# for (var idx = 0; idx < sheets.length; idx++) { #' + '# if (sheets[idx].options.title) { #' + '<vt:lpstr>${sheets[idx].options.title}</vt:lpstr>' + '# } else { #' + '<vt:lpstr>Sheet${idx+1}</vt:lpstr>' + '# } #' + '# } #' + '</vt:vector>' + '</TitlesOfParts>' + '<LinksUpToDate>false</LinksUpToDate>' + '<SharedDoc>false</SharedDoc>' + '<HyperlinksChanged>false</HyperlinksChanged>' + '<AppVersion>14.0300</AppVersion>' + '</Properties>');
|
|
var CONTENT_TYPES = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' + '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' + '<Default Extension="xml" ContentType="application/xml" />' + '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' + '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>' + '<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>' + '# for (var idx = 1; idx <= count; idx++) { #' + '<Override PartName="/xl/worksheets/sheet${idx}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' + '# } #' + '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml" />' + '<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" />' + '</Types>');
|
|
var WORKBOOK = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' + '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303" />' + '<workbookPr defaultThemeVersion="124226" />' + '<bookViews>' + '<workbookView xWindow="240" yWindow="45" windowWidth="18195" windowHeight="7995" />' + '</bookViews>' + '<sheets>' + '# for (var idx = 0; idx < sheets.length; idx++) { #' + '# var options = sheets[idx].options; #' + '# var name = options.name || options.title #' + '# if (name) { #' + '<sheet name="${name}" sheetId="${idx+1}" r:id="rId${idx+1}" />' + '# } else { #' + '<sheet name="Sheet${idx+1}" sheetId="${idx+1}" r:id="rId${idx+1}" />' + '# } #' + '# } #' + '</sheets>' + '# if (definedNames.length) { #' + '<definedNames>' + ' # for (var di = 0; di < definedNames.length; di++) { #' + '<definedName name="_xlnm._FilterDatabase" hidden="1" localSheetId="${definedNames[di].localSheetId}">' + '${definedNames[di].name}!$${definedNames[di].from}:$${definedNames[di].to}' + '</definedName>' + ' # } #' + '</definedNames>' + '# } #' + '<calcPr calcId="145621" />' + '</workbook>');
|
|
var WORKSHEET = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">' + '<dimension ref="A1" />' + '<sheetViews>' + '<sheetView #if(index==0) {# tabSelected="1" #}# workbookViewId="0">' + '# if (frozenRows || frozenColumns) { #' + '<pane state="frozen"' + '# if (frozenColumns) { #' + ' xSplit="${frozenColumns}"' + '# } #' + '# if (frozenRows) { #' + ' ySplit="${frozenRows}"' + '# } #' + ' topLeftCell="${String.fromCharCode(65 + (frozenColumns || 0))}${(frozenRows || 0)+1}"' + '/>' + '# } #' + '</sheetView>' + '</sheetViews>' + '<sheetFormatPr x14ac:dyDescent="0.25" defaultRowHeight="#= defaults.rowHeight ? defaults.rowHeight * 0.75 : 15 #" ' + '# if (defaults.columnWidth) { # defaultColWidth="#= kendo.ooxml.toWidth(defaults.columnWidth) #" # } #' + ' />' + '# if (columns && columns.length > 0) { #' + '<cols>' + '# for (var ci = 0; ci < columns.length; ci++) { #' + '# var column = columns[ci]; #' + '# var columnIndex = typeof column.index === "number" ? column.index + 1 : (ci + 1); #' + '# if (column.width) { #' + '<col min="${columnIndex}" max="${columnIndex}" customWidth="1"' + '# if (column.autoWidth) { #' + ' width="${((column.width*7+5)/7*256)/256}" bestFit="1"' + '# } else { #' + ' width="#= kendo.ooxml.toWidth(column.width) #" ' + '# } #' + '/>' + '# } #' + '# } #' + '</cols>' + '# } #' + '<sheetData>' + '# for (var ri = 0; ri < data.length; ri++) { #' + '# var row = data[ri]; #' + '# var rowIndex = typeof row.index === "number" ? row.index + 1 : (ri + 1); #' + '<row r="${rowIndex}" x14ac:dyDescent="0.25" ' + '# if (row.height) { # ht="#= kendo.ooxml.toHeight(row.height) #" customHeight="1" # } #' + ' >' + '# for (var ci = 0; ci < row.data.length; ci++) { #' + '# var cell = row.data[ci];#' + '<c r="#=cell.ref#"# if (cell.style) { # s="#=cell.style#" # } ## if (cell.type) { # t="#=cell.type#"# } #>' + '# if (cell.formula != null) { #' + '<f>${cell.formula}</f>' + '# } #' + '# if (cell.value != null) { #' + '<v>${cell.value}</v>' + '# } #' + '</c>' + '# } #' + '</row>' + '# } #' + '</sheetData>' + '# if (filter) { #' + '<autoFilter ref="${filter.from}:${filter.to}"/>' + '# } #' + '# if (mergeCells.length) { #' + '<mergeCells count="${mergeCells.length}">' + '# for (var ci = 0; ci < mergeCells.length; ci++) { #' + '<mergeCell ref="${mergeCells[ci]}"/>' + '# } #' + '</mergeCells>' + '# } #' + '<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3" />' + '</worksheet>');
|
|
var WORKBOOK_RELS = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' + '# for (var idx = 1; idx <= count; idx++) { #' + '<Relationship Id="rId${idx}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${idx}.xml" />' + '# } #' + '<Relationship Id="rId${count+1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml" />' + '<Relationship Id="rId${count+2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml" />' + '</Relationships>');
|
|
var SHARED_STRINGS = kendo.template('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n' + '<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${count}" uniqueCount="${uniqueCount}">' + '# for (var index in indexes) { #' + '<si><t>${index.substring(1)}</t></si>' + '# } #' + '</sst>');
|
|
var STYLES = kendo.template('<?xml version="1.0" encoding="UTF-8"?>' + '<styleSheet' + ' xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"' + ' xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"' + ' mc:Ignorable="x14ac"' + ' xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' + '<numFmts count="${formats.length}">' + '# for (var fi = 0; fi < formats.length; fi++) { #' + '# var format = formats[fi]; #' + '<numFmt formatCode="${format.format}" numFmtId="${165+fi}" />' + '# } #' + '</numFmts>' + '<fonts count="${fonts.length+1}" x14ac:knownFonts="1">' + '<font>' + '<sz val="11" />' + '<color theme="1" />' + '<name val="Calibri" />' + '<family val="2" />' + '<scheme val="minor" />' + '</font>' + '# for (var fi = 0; fi < fonts.length; fi++) { #' + '# var font = fonts[fi]; #' + '<font>' + '# if (font.fontSize) { #' + '<sz val="${font.fontSize}" />' + '# } else { #' + '<sz val="11" />' + '# } #' + '# if (font.bold) { #' + '<b/>' + '# } #' + '# if (font.italic) { #' + '<i/>' + '# } #' + '# if (font.underline) { #' + '<u/>' + '# } #' + '# if (font.color) { #' + '<color rgb="${font.color}" />' + '# } else { #' + '<color theme="1" />' + '# } #' + '# if (font.fontFamily) { #' + '<name val="${font.fontFamily}" />' + '<family val="2" />' + '# } else { #' + '<name val="Calibri" />' + '<family val="2" />' + '<scheme val="minor" />' + '# } #' + '</font>' + '# } #' + '</fonts>' + '<fills count="${fills.length+2}">' + '<fill><patternFill patternType="none"/></fill>' + '<fill><patternFill patternType="gray125"/></fill>' + '# for (var fi = 0; fi < fills.length; fi++) { #' + '# var fill = fills[fi]; #' + '# if (fill.background) { #' + '<fill>' + '<patternFill patternType="solid">' + '<fgColor rgb="${fill.background}"/>' + '</patternFill>' + '</fill>' + '# } #' + '# } #' + '</fills>' + '<borders count="${borders.length+1}">' + '<border><left/><right/><top/><bottom/><diagonal/></border>' + '# for (var bi = 0; bi < borders.length; bi++) { #' + '#= kendo.ooxml.borderTemplate(borders[bi]) #' + '# } #' + '</borders>' + '<cellStyleXfs count="1">' + '<xf borderId="0" fillId="0" fontId="0" />' + '</cellStyleXfs>' + '<cellXfs count="${styles.length+1}">' + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>' + '# for (var si = 0; si < styles.length; si++) { #' + '# var style = styles[si]; #' + '<xf xfId="0"' + '# if (style.fontId) { #' + ' fontId="${style.fontId}" applyFont="1"' + '# } #' + '# if (style.fillId) { #' + ' fillId="${style.fillId}" applyFill="1"' + '# } #' + '# if (style.numFmtId) { #' + ' numFmtId="${style.numFmtId}" applyNumberFormat="1"' + '# } #' + '# if (style.textAlign || style.verticalAlign || style.wrap) { #' + ' applyAlignment="1"' + '# } #' + '# if (style.borderId) { #' + ' borderId="${style.borderId}" applyBorder="1"' + '# } #' + '>' + '# if (style.textAlign || style.verticalAlign || style.wrap) { #' + '<alignment' + '# if (style.textAlign) { #' + ' horizontal="${style.textAlign}"' + '# } #' + '# if (style.verticalAlign) { #' + ' vertical="${style.verticalAlign}"' + '# } #' + '# if (style.wrap) { #' + ' wrapText="1"' + '# } #' + '/>' + '# } #' + '</xf>' + '# } #' + '</cellXfs>' + '<cellStyles count="1">' + '<cellStyle name="Normal" xfId="0" builtinId="0"/>' + '</cellStyles>' + '<dxfs count="0" />' + '<tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleMedium9" />' + '</styleSheet>');
|
|
function numChar(colIndex) {
|
|
var letter = Math.floor(colIndex / 26) - 1;
|
|
return (letter >= 0 ? numChar(letter) : '') + String.fromCharCode(65 + colIndex % 26);
|
|
}
|
|
function ref(rowIndex, colIndex) {
|
|
return numChar(colIndex) + (rowIndex + 1);
|
|
}
|
|
function $ref(rowIndex, colIndex) {
|
|
return numChar(colIndex) + '$' + (rowIndex + 1);
|
|
}
|
|
function filterRowIndex(options) {
|
|
var frozenRows = options.frozenRows || (options.freezePane || {}).rowSplit || 1;
|
|
return frozenRows - 1;
|
|
}
|
|
function toWidth(px) {
|
|
return (px / 7 * 100 + 0.5) / 100;
|
|
}
|
|
function toHeight(px) {
|
|
return px * 0.75;
|
|
}
|
|
var DATE_EPOCH = new Date(1900, 0, 0);
|
|
var Worksheet = kendo.Class.extend({
|
|
init: function (options, sharedStrings, styles, borders) {
|
|
this.options = options;
|
|
this._strings = sharedStrings;
|
|
this._styles = styles;
|
|
this._borders = borders;
|
|
},
|
|
toXML: function (index) {
|
|
this._mergeCells = this.options.mergedCells || [];
|
|
this._rowsByIndex = [];
|
|
var rows = this.options.rows || [];
|
|
for (var i = 0; i < rows.length; i++) {
|
|
var ri = rows[i].index;
|
|
if (typeof ri !== 'number') {
|
|
ri = i;
|
|
}
|
|
rows[i].index = ri;
|
|
this._rowsByIndex[ri] = rows[i];
|
|
}
|
|
var data = [];
|
|
for (i = 0; i < rows.length; i++) {
|
|
data.push(this._row(rows[i], i));
|
|
}
|
|
data.sort(function (a, b) {
|
|
return a.index - b.index;
|
|
});
|
|
var filter = this.options.filter;
|
|
if (filter && typeof filter.from === 'number' && typeof filter.to === 'number') {
|
|
filter = {
|
|
from: ref(filterRowIndex(this.options), filter.from),
|
|
to: ref(filterRowIndex(this.options), filter.to)
|
|
};
|
|
}
|
|
var freezePane = this.options.freezePane || {};
|
|
return WORKSHEET({
|
|
frozenColumns: this.options.frozenColumns || freezePane.colSplit,
|
|
frozenRows: this.options.frozenRows || freezePane.rowSplit,
|
|
columns: this.options.columns,
|
|
defaults: this.options.defaults || {},
|
|
data: data,
|
|
index: index,
|
|
mergeCells: this._mergeCells,
|
|
filter: filter
|
|
});
|
|
},
|
|
_row: function (row) {
|
|
var data = [];
|
|
var offset = 0;
|
|
var sheet = this;
|
|
var cellRefs = {};
|
|
$.each(row.cells, function (i, cell) {
|
|
if (!cell) {
|
|
return;
|
|
}
|
|
var cellIndex;
|
|
if (typeof cell.index === 'number') {
|
|
cellIndex = cell.index;
|
|
offset = cellIndex - i;
|
|
} else {
|
|
cellIndex = i + offset;
|
|
}
|
|
if (cell.colSpan) {
|
|
offset += cell.colSpan - 1;
|
|
}
|
|
var items = sheet._cell(cell, row.index, cellIndex);
|
|
$.each(items, function (i, cellData) {
|
|
if (cellRefs[cellData.ref]) {
|
|
return;
|
|
}
|
|
cellRefs[cellData.ref] = true;
|
|
data.push(cellData);
|
|
});
|
|
});
|
|
return {
|
|
data: data,
|
|
height: row.height,
|
|
index: row.index
|
|
};
|
|
},
|
|
_lookupString: function (value) {
|
|
var key = '$' + value;
|
|
var index = this._strings.indexes[key];
|
|
if (index !== undefined) {
|
|
value = index;
|
|
} else {
|
|
value = this._strings.indexes[key] = this._strings.uniqueCount;
|
|
this._strings.uniqueCount++;
|
|
}
|
|
this._strings.count++;
|
|
return value;
|
|
},
|
|
_lookupStyle: function (style) {
|
|
var json = kendo.stringify(style);
|
|
if (json == '{}') {
|
|
return 0;
|
|
}
|
|
var index = $.inArray(json, this._styles);
|
|
if (index < 0) {
|
|
index = this._styles.push(json) - 1;
|
|
}
|
|
return index + 1;
|
|
},
|
|
_lookupBorder: function (border) {
|
|
var json = kendo.stringify(border);
|
|
if (json == '{}') {
|
|
return;
|
|
}
|
|
var index = $.inArray(json, this._borders);
|
|
if (index < 0) {
|
|
index = this._borders.push(json) - 1;
|
|
}
|
|
return index + 1;
|
|
},
|
|
_cell: function (data, rowIndex, cellIndex) {
|
|
if (!data) {
|
|
return [];
|
|
}
|
|
var value = data.value;
|
|
var border = {};
|
|
if (data.borderLeft) {
|
|
border.left = data.borderLeft;
|
|
}
|
|
if (data.borderRight) {
|
|
border.right = data.borderRight;
|
|
}
|
|
if (data.borderTop) {
|
|
border.top = data.borderTop;
|
|
}
|
|
if (data.borderBottom) {
|
|
border.bottom = data.borderBottom;
|
|
}
|
|
border = this._lookupBorder(border);
|
|
var style = {
|
|
bold: data.bold,
|
|
color: data.color,
|
|
background: data.background,
|
|
italic: data.italic,
|
|
underline: data.underline,
|
|
fontFamily: data.fontFamily || data.fontName,
|
|
fontSize: data.fontSize,
|
|
format: data.format,
|
|
textAlign: data.textAlign || data.hAlign,
|
|
verticalAlign: data.verticalAlign || data.vAlign,
|
|
wrap: data.wrap,
|
|
borderId: border
|
|
};
|
|
var columns = this.options.columns || [];
|
|
var column = columns[cellIndex];
|
|
var type = typeof value;
|
|
if (column && column.autoWidth) {
|
|
var displayValue = value;
|
|
if (type === 'number') {
|
|
displayValue = kendo.toString(value, data.format);
|
|
}
|
|
column.width = Math.max(column.width || 0, (displayValue + '').length);
|
|
}
|
|
if (type === 'string') {
|
|
value = this._lookupString(value);
|
|
type = 's';
|
|
} else if (type === 'number') {
|
|
type = 'n';
|
|
} else if (type === 'boolean') {
|
|
type = 'b';
|
|
value = +value;
|
|
} else if (value && value.getTime) {
|
|
type = null;
|
|
var offset = (value.getTimezoneOffset() - DATE_EPOCH.getTimezoneOffset()) * kendo.date.MS_PER_MINUTE;
|
|
value = (value - DATE_EPOCH - offset) / kendo.date.MS_PER_DAY + 1;
|
|
if (!style.format) {
|
|
style.format = 'mm-dd-yy';
|
|
}
|
|
} else {
|
|
type = null;
|
|
value = null;
|
|
}
|
|
style = this._lookupStyle(style);
|
|
var cells = [];
|
|
var cellRef = ref(rowIndex, cellIndex);
|
|
cells.push({
|
|
value: value,
|
|
formula: data.formula,
|
|
type: type,
|
|
style: style,
|
|
ref: cellRef
|
|
});
|
|
var colSpan = data.colSpan || 1;
|
|
var rowSpan = data.rowSpan || 1;
|
|
var ci;
|
|
if (colSpan > 1 || rowSpan > 1) {
|
|
this._mergeCells.push(cellRef + ':' + ref(rowIndex + rowSpan - 1, cellIndex + colSpan - 1));
|
|
for (var ri = rowIndex + 1; ri < rowIndex + rowSpan; ri++) {
|
|
if (!this._rowsByIndex[ri]) {
|
|
this._rowsByIndex[ri] = {
|
|
index: ri,
|
|
cells: []
|
|
};
|
|
}
|
|
for (ci = cellIndex; ci < cellIndex + colSpan; ci++) {
|
|
this._rowsByIndex[ri].cells.splice(ci, 0, {});
|
|
}
|
|
}
|
|
for (ci = cellIndex + 1; ci < cellIndex + colSpan; ci++) {
|
|
cells.push({ ref: ref(rowIndex, ci) });
|
|
}
|
|
}
|
|
return cells;
|
|
}
|
|
});
|
|
var defaultFormats = {
|
|
'General': 0,
|
|
'0': 1,
|
|
'0.00': 2,
|
|
'#,##0': 3,
|
|
'#,##0.00': 4,
|
|
'0%': 9,
|
|
'0.00%': 10,
|
|
'0.00E+00': 11,
|
|
'# ?/?': 12,
|
|
'# ??/??': 13,
|
|
'mm-dd-yy': 14,
|
|
'd-mmm-yy': 15,
|
|
'd-mmm': 16,
|
|
'mmm-yy': 17,
|
|
'h:mm AM/PM': 18,
|
|
'h:mm:ss AM/PM': 19,
|
|
'h:mm': 20,
|
|
'h:mm:ss': 21,
|
|
'm/d/yy h:mm': 22,
|
|
'#,##0 ;(#,##0)': 37,
|
|
'#,##0 ;[Red](#,##0)': 38,
|
|
'#,##0.00;(#,##0.00)': 39,
|
|
'#,##0.00;[Red](#,##0.00)': 40,
|
|
'mm:ss': 45,
|
|
'[h]:mm:ss': 46,
|
|
'mmss.0': 47,
|
|
'##0.0E+0': 48,
|
|
'@': 49,
|
|
'[$-404]e/m/d': 27,
|
|
'm/d/yy': 30,
|
|
't0': 59,
|
|
't0.00': 60,
|
|
't#,##0': 61,
|
|
't#,##0.00': 62,
|
|
't0%': 67,
|
|
't0.00%': 68,
|
|
't# ?/?': 69,
|
|
't# ??/??': 70
|
|
};
|
|
function convertColor(color) {
|
|
if (color.length < 6) {
|
|
color = color.replace(/(\w)/g, function ($0, $1) {
|
|
return $1 + $1;
|
|
});
|
|
}
|
|
color = color.substring(1).toUpperCase();
|
|
if (color.length < 8) {
|
|
color = 'FF' + color;
|
|
}
|
|
return color;
|
|
}
|
|
var Workbook = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.options = options || {};
|
|
this._strings = {
|
|
indexes: {},
|
|
count: 0,
|
|
uniqueCount: 0
|
|
};
|
|
this._styles = [];
|
|
this._borders = [];
|
|
this._sheets = $.map(this.options.sheets || [], $.proxy(function (options) {
|
|
options.defaults = this.options;
|
|
return new Worksheet(options, this._strings, this._styles, this._borders);
|
|
}, this));
|
|
},
|
|
toDataURL: function () {
|
|
if (typeof JSZip === 'undefined') {
|
|
throw new Error('JSZip not found. Check http://docs.telerik.com/kendo-ui/framework/excel/introduction#requirements for more details.');
|
|
}
|
|
var zip = new JSZip();
|
|
var docProps = zip.folder('docProps');
|
|
docProps.file('core.xml', CORE({
|
|
creator: this.options.creator || 'Kendo UI',
|
|
lastModifiedBy: this.options.creator || 'Kendo UI',
|
|
created: this.options.date || new Date().toJSON(),
|
|
modified: this.options.date || new Date().toJSON()
|
|
}));
|
|
var sheetCount = this._sheets.length;
|
|
docProps.file('app.xml', APP({ sheets: this._sheets }));
|
|
var rels = zip.folder('_rels');
|
|
rels.file('.rels', RELS);
|
|
var xl = zip.folder('xl');
|
|
var xlRels = xl.folder('_rels');
|
|
xlRels.file('workbook.xml.rels', WORKBOOK_RELS({ count: sheetCount }));
|
|
xl.file('workbook.xml', WORKBOOK({
|
|
sheets: this._sheets,
|
|
definedNames: $.map(this._sheets, function (sheet, index) {
|
|
var options = sheet.options;
|
|
var filter = options.filter;
|
|
if (filter && typeof filter.from !== 'undefined' && typeof filter.to !== 'undefined') {
|
|
return {
|
|
localSheetId: index,
|
|
name: options.name || options.title || 'Sheet' + (index + 1),
|
|
from: $ref(filterRowIndex(options), filter.from),
|
|
to: $ref(filterRowIndex(options), filter.to)
|
|
};
|
|
}
|
|
})
|
|
}));
|
|
var worksheets = xl.folder('worksheets');
|
|
for (var idx = 0; idx < sheetCount; idx++) {
|
|
worksheets.file(kendo.format('sheet{0}.xml', idx + 1), this._sheets[idx].toXML(idx));
|
|
}
|
|
var borders = $.map(this._borders, $.parseJSON);
|
|
var styles = $.map(this._styles, $.parseJSON);
|
|
var hasFont = function (style) {
|
|
return style.underline || style.bold || style.italic || style.color || style.fontFamily || style.fontSize;
|
|
};
|
|
var fonts = $.map(styles, function (style) {
|
|
if (style.color) {
|
|
style.color = convertColor(style.color);
|
|
}
|
|
if (hasFont(style)) {
|
|
return style;
|
|
}
|
|
});
|
|
var formats = $.map(styles, function (style) {
|
|
if (style.format && defaultFormats[style.format] === undefined) {
|
|
return style;
|
|
}
|
|
});
|
|
var fills = $.map(styles, function (style) {
|
|
if (style.background) {
|
|
style.background = convertColor(style.background);
|
|
return style;
|
|
}
|
|
});
|
|
xl.file('styles.xml', STYLES({
|
|
fonts: fonts,
|
|
fills: fills,
|
|
formats: formats,
|
|
borders: borders,
|
|
styles: $.map(styles, function (style) {
|
|
var result = {};
|
|
if (hasFont(style)) {
|
|
result.fontId = $.inArray(style, fonts) + 1;
|
|
}
|
|
if (style.background) {
|
|
result.fillId = $.inArray(style, fills) + 2;
|
|
}
|
|
result.textAlign = style.textAlign;
|
|
result.verticalAlign = style.verticalAlign;
|
|
result.wrap = style.wrap;
|
|
result.borderId = style.borderId;
|
|
if (style.format) {
|
|
if (defaultFormats[style.format] !== undefined) {
|
|
result.numFmtId = defaultFormats[style.format];
|
|
} else {
|
|
result.numFmtId = 165 + $.inArray(style, formats);
|
|
}
|
|
}
|
|
return result;
|
|
})
|
|
}));
|
|
xl.file('sharedStrings.xml', SHARED_STRINGS(this._strings));
|
|
zip.file('[Content_Types].xml', CONTENT_TYPES({ count: sheetCount }));
|
|
return 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,' + zip.generate({ compression: 'DEFLATE' });
|
|
}
|
|
});
|
|
function borderStyle(width) {
|
|
var alias = 'thin';
|
|
if (width === 2) {
|
|
alias = 'medium';
|
|
} else if (width === 3) {
|
|
alias = 'thick';
|
|
}
|
|
return alias;
|
|
}
|
|
function borderSideTemplate(name, style) {
|
|
var result = '';
|
|
if (style && style.size) {
|
|
result += '<' + name + ' style="' + borderStyle(style.size) + '">';
|
|
if (style.color) {
|
|
result += '<color rgb="' + convertColor(style.color) + '"/>';
|
|
}
|
|
result += '</' + name + '>';
|
|
}
|
|
return result;
|
|
}
|
|
function borderTemplate(border) {
|
|
return '<border>' + borderSideTemplate('left', border.left) + borderSideTemplate('right', border.right) + borderSideTemplate('top', border.top) + borderSideTemplate('bottom', border.bottom) + '</border>';
|
|
}
|
|
kendo.ooxml = {
|
|
Workbook: Workbook,
|
|
Worksheet: Worksheet,
|
|
toWidth: toWidth,
|
|
toHeight: toHeight,
|
|
borderTemplate: borderTemplate
|
|
};
|
|
}(kendo.jQuery, kendo));
|
|
return kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.excel', [
|
|
'kendo.core',
|
|
'kendo.data',
|
|
'kendo.ooxml'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'excel',
|
|
name: 'Excel export',
|
|
category: 'framework',
|
|
advanced: true,
|
|
mixin: true,
|
|
depends: [
|
|
'data',
|
|
'ooxml'
|
|
]
|
|
};
|
|
(function ($, kendo) {
|
|
kendo.ExcelExporter = kendo.Class.extend({
|
|
init: function (options) {
|
|
options.columns = this._trimColumns(options.columns || []);
|
|
this.allColumns = $.map(this._leafColumns(options.columns || []), this._prepareColumn);
|
|
this.columns = $.grep(this.allColumns, function (column) {
|
|
return !column.hidden;
|
|
});
|
|
this.options = options;
|
|
var dataSource = options.dataSource;
|
|
if (dataSource instanceof kendo.data.DataSource) {
|
|
this.dataSource = new dataSource.constructor($.extend({}, dataSource.options, {
|
|
page: options.allPages ? 0 : dataSource.page(),
|
|
filter: dataSource.filter(),
|
|
pageSize: options.allPages ? dataSource.total() : dataSource.pageSize(),
|
|
sort: dataSource.sort(),
|
|
group: dataSource.group(),
|
|
aggregate: dataSource.aggregate()
|
|
}));
|
|
var data = dataSource.data();
|
|
if (data.length > 0) {
|
|
this.dataSource._data = data;
|
|
var transport = this.dataSource.transport;
|
|
if (dataSource._isServerGrouped() && transport.options.data) {
|
|
transport.options.data = null;
|
|
}
|
|
}
|
|
} else {
|
|
this.dataSource = kendo.data.DataSource.create(dataSource);
|
|
}
|
|
},
|
|
_trimColumns: function (columns) {
|
|
var that = this;
|
|
return $.grep(columns, function (column) {
|
|
var result = !!column.field;
|
|
if (!result && column.columns) {
|
|
result = that._trimColumns(column.columns).length > 0;
|
|
}
|
|
return result;
|
|
});
|
|
},
|
|
_leafColumns: function (columns) {
|
|
var result = [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (!columns[idx].columns) {
|
|
result.push(columns[idx]);
|
|
continue;
|
|
}
|
|
result = result.concat(this._leafColumns(columns[idx].columns));
|
|
}
|
|
return result;
|
|
},
|
|
workbook: function () {
|
|
return $.Deferred($.proxy(function (d) {
|
|
this.dataSource.fetch().then($.proxy(function () {
|
|
var workbook = {
|
|
sheets: [{
|
|
columns: this._columns(),
|
|
rows: this._rows(),
|
|
freezePane: this._freezePane(),
|
|
filter: this._filter()
|
|
}]
|
|
};
|
|
d.resolve(workbook, this.dataSource.view());
|
|
}, this));
|
|
}, this)).promise();
|
|
},
|
|
_prepareColumn: function (column) {
|
|
if (!column.field) {
|
|
return;
|
|
}
|
|
var value = function (dataItem) {
|
|
return dataItem.get(column.field);
|
|
};
|
|
var values = null;
|
|
if (column.values) {
|
|
values = {};
|
|
$.each(column.values, function () {
|
|
values[this.value] = this.text;
|
|
});
|
|
value = function (dataItem) {
|
|
return values[dataItem.get(column.field)];
|
|
};
|
|
}
|
|
return $.extend({}, column, {
|
|
value: value,
|
|
values: values,
|
|
groupHeaderTemplate: kendo.template(column.groupHeaderTemplate || '#= title #: #= value #'),
|
|
groupFooterTemplate: column.groupFooterTemplate ? kendo.template(column.groupFooterTemplate) : null,
|
|
footerTemplate: column.footerTemplate ? kendo.template(column.footerTemplate) : null
|
|
});
|
|
},
|
|
_filter: function () {
|
|
if (!this.options.filterable) {
|
|
return null;
|
|
}
|
|
var depth = this._depth();
|
|
return {
|
|
from: depth,
|
|
to: depth + this.columns.length - 1
|
|
};
|
|
},
|
|
_dataRow: function (dataItem, level, depth) {
|
|
if (this._hierarchical()) {
|
|
level = this.dataSource.level(dataItem) + 1;
|
|
}
|
|
var cells = [];
|
|
for (var li = 0; li < level; li++) {
|
|
cells[li] = {
|
|
background: '#dfdfdf',
|
|
color: '#333'
|
|
};
|
|
}
|
|
if (depth && dataItem.items) {
|
|
var column = $.grep(this.allColumns, function (column) {
|
|
return column.field == dataItem.field;
|
|
})[0];
|
|
var title = column && column.title ? column.title : dataItem.field;
|
|
var template = column ? column.groupHeaderTemplate : null;
|
|
var value = title + ': ' + dataItem.value;
|
|
var group = $.extend({
|
|
title: title,
|
|
field: dataItem.field,
|
|
value: column && column.values ? column.values[dataItem.value] : dataItem.value,
|
|
aggregates: dataItem.aggregates
|
|
}, dataItem.aggregates[dataItem.field]);
|
|
if (template) {
|
|
value = template(group);
|
|
}
|
|
cells.push({
|
|
value: value,
|
|
background: '#dfdfdf',
|
|
color: '#333',
|
|
colSpan: this.columns.length + depth - level
|
|
});
|
|
var rows = this._dataRows(dataItem.items, level + 1);
|
|
rows.unshift({
|
|
type: 'group-header',
|
|
cells: cells
|
|
});
|
|
return rows.concat(this._footer(dataItem));
|
|
} else {
|
|
var dataCells = [];
|
|
for (var ci = 0; ci < this.columns.length; ci++) {
|
|
dataCells[ci] = this._cell(dataItem, this.columns[ci]);
|
|
}
|
|
if (this._hierarchical()) {
|
|
dataCells[0].colSpan = depth - level + 1;
|
|
}
|
|
return [{
|
|
type: 'data',
|
|
cells: cells.concat(dataCells)
|
|
}];
|
|
}
|
|
},
|
|
_dataRows: function (dataItems, level) {
|
|
var depth = this._depth();
|
|
var rows = [];
|
|
for (var i = 0; i < dataItems.length; i++) {
|
|
rows.push.apply(rows, this._dataRow(dataItems[i], level, depth));
|
|
}
|
|
return rows;
|
|
},
|
|
_footer: function (dataItem) {
|
|
var rows = [];
|
|
var footer = false;
|
|
var cells = $.map(this.columns, $.proxy(function (column) {
|
|
if (column.groupFooterTemplate) {
|
|
footer = true;
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333',
|
|
value: column.groupFooterTemplate($.extend({}, this.dataSource.aggregates(), dataItem.aggregates, dataItem.aggregates[column.field]))
|
|
};
|
|
} else {
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333'
|
|
};
|
|
}
|
|
}, this));
|
|
if (footer) {
|
|
rows.push({
|
|
type: 'group-footer',
|
|
cells: $.map(new Array(this.dataSource.group().length), function () {
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333'
|
|
};
|
|
}).concat(cells)
|
|
});
|
|
}
|
|
return rows;
|
|
},
|
|
_isColumnVisible: function (column) {
|
|
return this._visibleColumns([column]).length > 0 && (column.field || column.columns);
|
|
},
|
|
_visibleColumns: function (columns) {
|
|
var that = this;
|
|
return $.grep(columns, function (column) {
|
|
var result = !column.hidden;
|
|
if (result && column.columns) {
|
|
result = that._visibleColumns(column.columns).length > 0;
|
|
}
|
|
return result;
|
|
});
|
|
},
|
|
_headerRow: function (row, groups) {
|
|
var headers = $.map(row.cells, function (cell) {
|
|
return {
|
|
background: '#7a7a7a',
|
|
color: '#fff',
|
|
value: cell.title,
|
|
colSpan: cell.colSpan > 1 ? cell.colSpan : 1,
|
|
rowSpan: row.rowSpan > 1 && !cell.colSpan ? row.rowSpan : 1
|
|
};
|
|
});
|
|
if (this._hierarchical()) {
|
|
headers[0].colSpan = this._depth() + 1;
|
|
}
|
|
return {
|
|
type: 'header',
|
|
cells: $.map(new Array(groups.length), function () {
|
|
return {
|
|
background: '#7a7a7a',
|
|
color: '#fff'
|
|
};
|
|
}).concat(headers)
|
|
};
|
|
},
|
|
_prependHeaderRows: function (rows) {
|
|
var groups = this.dataSource.group();
|
|
var headerRows = [{
|
|
rowSpan: 1,
|
|
cells: [],
|
|
index: 0
|
|
}];
|
|
this._prepareHeaderRows(headerRows, this.options.columns);
|
|
for (var idx = headerRows.length - 1; idx >= 0; idx--) {
|
|
rows.unshift(this._headerRow(headerRows[idx], groups));
|
|
}
|
|
},
|
|
_prepareHeaderRows: function (rows, columns, parentCell, parentRow) {
|
|
var row = parentRow || rows[rows.length - 1];
|
|
var childRow = rows[row.index + 1];
|
|
var totalColSpan = 0;
|
|
var column;
|
|
var cell;
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
column = columns[idx];
|
|
if (this._isColumnVisible(column)) {
|
|
cell = {
|
|
title: column.title || column.field,
|
|
colSpan: 0
|
|
};
|
|
row.cells.push(cell);
|
|
if (column.columns && column.columns.length) {
|
|
if (!childRow) {
|
|
childRow = {
|
|
rowSpan: 0,
|
|
cells: [],
|
|
index: rows.length
|
|
};
|
|
rows.push(childRow);
|
|
}
|
|
cell.colSpan = this._trimColumns(this._visibleColumns(column.columns)).length;
|
|
this._prepareHeaderRows(rows, column.columns, cell, childRow);
|
|
totalColSpan += cell.colSpan - 1;
|
|
row.rowSpan = rows.length - row.index;
|
|
}
|
|
}
|
|
}
|
|
if (parentCell) {
|
|
parentCell.colSpan += totalColSpan;
|
|
}
|
|
},
|
|
_rows: function () {
|
|
var groups = this.dataSource.group();
|
|
var rows = this._dataRows(this.dataSource.view(), 0);
|
|
if (this.columns.length) {
|
|
this._prependHeaderRows(rows);
|
|
var footer = false;
|
|
var cells = $.map(this.columns, $.proxy(function (column) {
|
|
if (column.footerTemplate) {
|
|
footer = true;
|
|
var aggregates = this.dataSource.aggregates();
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333',
|
|
value: column.footerTemplate($.extend({}, aggregates, aggregates[column.field]))
|
|
};
|
|
} else {
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333'
|
|
};
|
|
}
|
|
}, this));
|
|
if (footer) {
|
|
rows.push({
|
|
type: 'footer',
|
|
cells: $.map(new Array(groups.length), function () {
|
|
return {
|
|
background: '#dfdfdf',
|
|
color: '#333'
|
|
};
|
|
}).concat(cells)
|
|
});
|
|
}
|
|
}
|
|
return rows;
|
|
},
|
|
_headerDepth: function (columns) {
|
|
var result = 1;
|
|
var max = 0;
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (columns[idx].columns) {
|
|
var temp = this._headerDepth(columns[idx].columns);
|
|
if (temp > max) {
|
|
max = temp;
|
|
}
|
|
}
|
|
}
|
|
return result + max;
|
|
},
|
|
_freezePane: function () {
|
|
var columns = this._visibleColumns(this.options.columns || []);
|
|
var colSplit = this._visibleColumns(this._trimColumns(this._leafColumns($.grep(columns, function (column) {
|
|
return column.locked;
|
|
})))).length;
|
|
return {
|
|
rowSplit: this._headerDepth(columns),
|
|
colSplit: colSplit ? colSplit + this.dataSource.group().length : 0
|
|
};
|
|
},
|
|
_cell: function (dataItem, column) {
|
|
return { value: column.value(dataItem) };
|
|
},
|
|
_hierarchical: function () {
|
|
return this.options.hierarchy && this.dataSource.level;
|
|
},
|
|
_depth: function () {
|
|
var dataSource = this.dataSource;
|
|
var depth = 0;
|
|
var view, i, level;
|
|
if (this._hierarchical()) {
|
|
view = dataSource.view();
|
|
for (i = 0; i < view.length; i++) {
|
|
level = dataSource.level(view[i]);
|
|
if (level > depth) {
|
|
depth = level;
|
|
}
|
|
}
|
|
depth++;
|
|
} else {
|
|
depth = dataSource.group().length;
|
|
}
|
|
return depth;
|
|
},
|
|
_columns: function () {
|
|
var depth = this._depth();
|
|
var columns = $.map(new Array(depth), function () {
|
|
return { width: 20 };
|
|
});
|
|
return columns.concat($.map(this.columns, function (column) {
|
|
return {
|
|
width: parseInt(column.width, 10),
|
|
autoWidth: column.width ? false : true
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
kendo.ExcelMixin = {
|
|
extend: function (proto) {
|
|
proto.events.push('excelExport');
|
|
proto.options.excel = $.extend(proto.options.excel, this.options);
|
|
proto.saveAsExcel = this.saveAsExcel;
|
|
},
|
|
options: {
|
|
proxyURL: '',
|
|
allPages: false,
|
|
filterable: false,
|
|
fileName: 'Export.xlsx'
|
|
},
|
|
saveAsExcel: function () {
|
|
var excel = this.options.excel || {};
|
|
var exporter = new kendo.ExcelExporter({
|
|
columns: this.columns,
|
|
dataSource: this.dataSource,
|
|
allPages: excel.allPages,
|
|
filterable: excel.filterable,
|
|
hierarchy: excel.hierarchy
|
|
});
|
|
exporter.workbook().then($.proxy(function (book, data) {
|
|
if (!this.trigger('excelExport', {
|
|
workbook: book,
|
|
data: data
|
|
})) {
|
|
var workbook = new kendo.ooxml.Workbook(book);
|
|
kendo.saveAs({
|
|
dataURI: workbook.toDataURL(),
|
|
fileName: book.fileName || excel.fileName,
|
|
proxyURL: excel.proxyURL,
|
|
forceProxy: excel.forceProxy
|
|
});
|
|
}
|
|
}, this));
|
|
}
|
|
};
|
|
}(kendo.jQuery, kendo));
|
|
return kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.data.signalr', ['kendo.data'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'data.signalr',
|
|
name: 'SignalR',
|
|
category: 'framework',
|
|
depends: ['data'],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var transport = kendo.data.RemoteTransport.extend({
|
|
init: function (options) {
|
|
var signalr = options && options.signalr ? options.signalr : {};
|
|
var promise = signalr.promise;
|
|
if (!promise) {
|
|
throw new Error('The "promise" option must be set.');
|
|
}
|
|
if (typeof promise.done != 'function' || typeof promise.fail != 'function') {
|
|
throw new Error('The "promise" option must be a Promise.');
|
|
}
|
|
this.promise = promise;
|
|
var hub = signalr.hub;
|
|
if (!hub) {
|
|
throw new Error('The "hub" option must be set.');
|
|
}
|
|
if (typeof hub.on != 'function' || typeof hub.invoke != 'function') {
|
|
throw new Error('The "hub" option is not a valid SignalR hub proxy.');
|
|
}
|
|
this.hub = hub;
|
|
kendo.data.RemoteTransport.fn.init.call(this, options);
|
|
},
|
|
push: function (callbacks) {
|
|
var client = this.options.signalr.client || {};
|
|
if (client.create) {
|
|
this.hub.on(client.create, callbacks.pushCreate);
|
|
}
|
|
if (client.update) {
|
|
this.hub.on(client.update, callbacks.pushUpdate);
|
|
}
|
|
if (client.destroy) {
|
|
this.hub.on(client.destroy, callbacks.pushDestroy);
|
|
}
|
|
},
|
|
_crud: function (options, type) {
|
|
var hub = this.hub;
|
|
var server = this.options.signalr.server;
|
|
if (!server || !server[type]) {
|
|
throw new Error(kendo.format('The "server.{0}" option must be set.', type));
|
|
}
|
|
var args = [server[type]];
|
|
var data = this.parameterMap(options.data, type);
|
|
if (!$.isEmptyObject(data)) {
|
|
args.push(data);
|
|
}
|
|
this.promise.done(function () {
|
|
hub.invoke.apply(hub, args).done(options.success).fail(options.error);
|
|
});
|
|
},
|
|
read: function (options) {
|
|
this._crud(options, 'read');
|
|
},
|
|
create: function (options) {
|
|
this._crud(options, 'create');
|
|
},
|
|
update: function (options) {
|
|
this._crud(options, 'update');
|
|
},
|
|
destroy: function (options) {
|
|
this._crud(options, 'destroy');
|
|
}
|
|
});
|
|
$.extend(true, kendo.data, { transports: { signalr: transport } });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.color', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'color',
|
|
name: 'Color utils',
|
|
category: 'framework',
|
|
advanced: true,
|
|
description: 'Color utilities used across components',
|
|
depends: ['core']
|
|
};
|
|
(function ($, parseFloat, parseInt) {
|
|
var Color = function (value) {
|
|
var color = this, formats = Color.formats, re, processor, parts, i, channels;
|
|
if (arguments.length === 1) {
|
|
value = color.resolveColor(value);
|
|
for (i = 0; i < formats.length; i++) {
|
|
re = formats[i].re;
|
|
processor = formats[i].process;
|
|
parts = re.exec(value);
|
|
if (parts) {
|
|
channels = processor(parts);
|
|
color.r = channels[0];
|
|
color.g = channels[1];
|
|
color.b = channels[2];
|
|
}
|
|
}
|
|
} else {
|
|
color.r = arguments[0];
|
|
color.g = arguments[1];
|
|
color.b = arguments[2];
|
|
}
|
|
color.r = color.normalizeByte(color.r);
|
|
color.g = color.normalizeByte(color.g);
|
|
color.b = color.normalizeByte(color.b);
|
|
};
|
|
Color.prototype = {
|
|
toHex: function () {
|
|
var color = this, pad = color.padDigit, r = color.r.toString(16), g = color.g.toString(16), b = color.b.toString(16);
|
|
return '#' + pad(r) + pad(g) + pad(b);
|
|
},
|
|
resolveColor: function (value) {
|
|
value = value || 'black';
|
|
if (value.charAt(0) == '#') {
|
|
value = value.substr(1, 6);
|
|
}
|
|
value = value.replace(/ /g, '');
|
|
value = value.toLowerCase();
|
|
value = Color.namedColors[value] || value;
|
|
return value;
|
|
},
|
|
normalizeByte: function (value) {
|
|
return value < 0 || isNaN(value) ? 0 : value > 255 ? 255 : value;
|
|
},
|
|
padDigit: function (value) {
|
|
return value.length === 1 ? '0' + value : value;
|
|
},
|
|
brightness: function (value) {
|
|
var color = this, round = Math.round;
|
|
color.r = round(color.normalizeByte(color.r * value));
|
|
color.g = round(color.normalizeByte(color.g * value));
|
|
color.b = round(color.normalizeByte(color.b * value));
|
|
return color;
|
|
},
|
|
percBrightness: function () {
|
|
var color = this;
|
|
return Math.sqrt(0.241 * color.r * color.r + 0.691 * color.g * color.g + 0.068 * color.b * color.b);
|
|
}
|
|
};
|
|
Color.formats = [
|
|
{
|
|
re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1], 10),
|
|
parseInt(parts[2], 10),
|
|
parseInt(parts[3], 10)
|
|
];
|
|
}
|
|
},
|
|
{
|
|
re: /^(\w{2})(\w{2})(\w{2})$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1], 16),
|
|
parseInt(parts[2], 16),
|
|
parseInt(parts[3], 16)
|
|
];
|
|
}
|
|
},
|
|
{
|
|
re: /^(\w{1})(\w{1})(\w{1})$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1] + parts[1], 16),
|
|
parseInt(parts[2] + parts[2], 16),
|
|
parseInt(parts[3] + parts[3], 16)
|
|
];
|
|
}
|
|
}
|
|
];
|
|
Color.namedColors = {
|
|
aliceblue: 'f0f8ff',
|
|
antiquewhite: 'faebd7',
|
|
aqua: '00ffff',
|
|
aquamarine: '7fffd4',
|
|
azure: 'f0ffff',
|
|
beige: 'f5f5dc',
|
|
bisque: 'ffe4c4',
|
|
black: '000000',
|
|
blanchedalmond: 'ffebcd',
|
|
blue: '0000ff',
|
|
blueviolet: '8a2be2',
|
|
brown: 'a52a2a',
|
|
burlywood: 'deb887',
|
|
cadetblue: '5f9ea0',
|
|
chartreuse: '7fff00',
|
|
chocolate: 'd2691e',
|
|
coral: 'ff7f50',
|
|
cornflowerblue: '6495ed',
|
|
cornsilk: 'fff8dc',
|
|
crimson: 'dc143c',
|
|
cyan: '00ffff',
|
|
darkblue: '00008b',
|
|
darkcyan: '008b8b',
|
|
darkgoldenrod: 'b8860b',
|
|
darkgray: 'a9a9a9',
|
|
darkgrey: 'a9a9a9',
|
|
darkgreen: '006400',
|
|
darkkhaki: 'bdb76b',
|
|
darkmagenta: '8b008b',
|
|
darkolivegreen: '556b2f',
|
|
darkorange: 'ff8c00',
|
|
darkorchid: '9932cc',
|
|
darkred: '8b0000',
|
|
darksalmon: 'e9967a',
|
|
darkseagreen: '8fbc8f',
|
|
darkslateblue: '483d8b',
|
|
darkslategray: '2f4f4f',
|
|
darkslategrey: '2f4f4f',
|
|
darkturquoise: '00ced1',
|
|
darkviolet: '9400d3',
|
|
deeppink: 'ff1493',
|
|
deepskyblue: '00bfff',
|
|
dimgray: '696969',
|
|
dimgrey: '696969',
|
|
dodgerblue: '1e90ff',
|
|
firebrick: 'b22222',
|
|
floralwhite: 'fffaf0',
|
|
forestgreen: '228b22',
|
|
fuchsia: 'ff00ff',
|
|
gainsboro: 'dcdcdc',
|
|
ghostwhite: 'f8f8ff',
|
|
gold: 'ffd700',
|
|
goldenrod: 'daa520',
|
|
gray: '808080',
|
|
grey: '808080',
|
|
green: '008000',
|
|
greenyellow: 'adff2f',
|
|
honeydew: 'f0fff0',
|
|
hotpink: 'ff69b4',
|
|
indianred: 'cd5c5c',
|
|
indigo: '4b0082',
|
|
ivory: 'fffff0',
|
|
khaki: 'f0e68c',
|
|
lavender: 'e6e6fa',
|
|
lavenderblush: 'fff0f5',
|
|
lawngreen: '7cfc00',
|
|
lemonchiffon: 'fffacd',
|
|
lightblue: 'add8e6',
|
|
lightcoral: 'f08080',
|
|
lightcyan: 'e0ffff',
|
|
lightgoldenrodyellow: 'fafad2',
|
|
lightgray: 'd3d3d3',
|
|
lightgrey: 'd3d3d3',
|
|
lightgreen: '90ee90',
|
|
lightpink: 'ffb6c1',
|
|
lightsalmon: 'ffa07a',
|
|
lightseagreen: '20b2aa',
|
|
lightskyblue: '87cefa',
|
|
lightslategray: '778899',
|
|
lightslategrey: '778899',
|
|
lightsteelblue: 'b0c4de',
|
|
lightyellow: 'ffffe0',
|
|
lime: '00ff00',
|
|
limegreen: '32cd32',
|
|
linen: 'faf0e6',
|
|
magenta: 'ff00ff',
|
|
maroon: '800000',
|
|
mediumaquamarine: '66cdaa',
|
|
mediumblue: '0000cd',
|
|
mediumorchid: 'ba55d3',
|
|
mediumpurple: '9370d8',
|
|
mediumseagreen: '3cb371',
|
|
mediumslateblue: '7b68ee',
|
|
mediumspringgreen: '00fa9a',
|
|
mediumturquoise: '48d1cc',
|
|
mediumvioletred: 'c71585',
|
|
midnightblue: '191970',
|
|
mintcream: 'f5fffa',
|
|
mistyrose: 'ffe4e1',
|
|
moccasin: 'ffe4b5',
|
|
navajowhite: 'ffdead',
|
|
navy: '000080',
|
|
oldlace: 'fdf5e6',
|
|
olive: '808000',
|
|
olivedrab: '6b8e23',
|
|
orange: 'ffa500',
|
|
orangered: 'ff4500',
|
|
orchid: 'da70d6',
|
|
palegoldenrod: 'eee8aa',
|
|
palegreen: '98fb98',
|
|
paleturquoise: 'afeeee',
|
|
palevioletred: 'd87093',
|
|
papayawhip: 'ffefd5',
|
|
peachpuff: 'ffdab9',
|
|
peru: 'cd853f',
|
|
pink: 'ffc0cb',
|
|
plum: 'dda0dd',
|
|
powderblue: 'b0e0e6',
|
|
purple: '800080',
|
|
red: 'ff0000',
|
|
rosybrown: 'bc8f8f',
|
|
royalblue: '4169e1',
|
|
saddlebrown: '8b4513',
|
|
salmon: 'fa8072',
|
|
sandybrown: 'f4a460',
|
|
seagreen: '2e8b57',
|
|
seashell: 'fff5ee',
|
|
sienna: 'a0522d',
|
|
silver: 'c0c0c0',
|
|
skyblue: '87ceeb',
|
|
slateblue: '6a5acd',
|
|
slategray: '708090',
|
|
slategrey: '708090',
|
|
snow: 'fffafa',
|
|
springgreen: '00ff7f',
|
|
steelblue: '4682b4',
|
|
tan: 'd2b48c',
|
|
teal: '008080',
|
|
thistle: 'd8bfd8',
|
|
tomato: 'ff6347',
|
|
turquoise: '40e0d0',
|
|
violet: 'ee82ee',
|
|
wheat: 'f5deb3',
|
|
white: 'ffffff',
|
|
whitesmoke: 'f5f5f5',
|
|
yellow: 'ffff00',
|
|
yellowgreen: '9acd32'
|
|
};
|
|
var namedColorRegexp = ['transparent'];
|
|
for (var i in Color.namedColors) {
|
|
if (Color.namedColors.hasOwnProperty(i)) {
|
|
namedColorRegexp.push(i);
|
|
}
|
|
}
|
|
namedColorRegexp = new RegExp('^(' + namedColorRegexp.join('|') + ')(\\W|$)', 'i');
|
|
function parseColor(color, nothrow) {
|
|
var m, ret;
|
|
if (color == null || color == 'none') {
|
|
return null;
|
|
}
|
|
if (color instanceof _Color) {
|
|
return color;
|
|
}
|
|
color = color.toLowerCase();
|
|
if (m = namedColorRegexp.exec(color)) {
|
|
if (m[1] == 'transparent') {
|
|
color = new _RGB(1, 1, 1, 0);
|
|
} else {
|
|
color = parseColor(Color.namedColors[m[1]], nothrow);
|
|
}
|
|
color.match = [m[1]];
|
|
return color;
|
|
}
|
|
if (m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})\b/i.exec(color)) {
|
|
ret = new _Bytes(parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16), 1);
|
|
} else if (m = /^#?([0-9a-f])([0-9a-f])([0-9a-f])\b/i.exec(color)) {
|
|
ret = new _Bytes(parseInt(m[1] + m[1], 16), parseInt(m[2] + m[2], 16), parseInt(m[3] + m[3], 16), 1);
|
|
} else if (m = /^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/.exec(color)) {
|
|
ret = new _Bytes(parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10), 1);
|
|
} else if (m = /^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)/.exec(color)) {
|
|
ret = new _Bytes(parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10), parseFloat(m[4]));
|
|
} else if (m = /^rgb\(\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*\)/.exec(color)) {
|
|
ret = new _RGB(parseFloat(m[1]) / 100, parseFloat(m[2]) / 100, parseFloat(m[3]) / 100, 1);
|
|
} else if (m = /^rgba\(\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9.]+)\s*\)/.exec(color)) {
|
|
ret = new _RGB(parseFloat(m[1]) / 100, parseFloat(m[2]) / 100, parseFloat(m[3]) / 100, parseFloat(m[4]));
|
|
}
|
|
if (ret) {
|
|
ret.match = m;
|
|
} else if (!nothrow) {
|
|
throw new Error('Cannot parse color: ' + color);
|
|
}
|
|
return ret;
|
|
}
|
|
function hex(n, width, pad) {
|
|
if (!pad) {
|
|
pad = '0';
|
|
}
|
|
n = n.toString(16);
|
|
while (width > n.length) {
|
|
n = '0' + n;
|
|
}
|
|
return n;
|
|
}
|
|
function hue2rgb(p, q, t) {
|
|
if (t < 0) {
|
|
t += 1;
|
|
}
|
|
if (t > 1) {
|
|
t -= 1;
|
|
}
|
|
if (t < 1 / 6) {
|
|
return p + (q - p) * 6 * t;
|
|
}
|
|
if (t < 1 / 2) {
|
|
return q;
|
|
}
|
|
if (t < 2 / 3) {
|
|
return p + (q - p) * (2 / 3 - t) * 6;
|
|
}
|
|
return p;
|
|
}
|
|
var _Color = kendo.Class.extend({
|
|
toHSV: function () {
|
|
return this;
|
|
},
|
|
toRGB: function () {
|
|
return this;
|
|
},
|
|
toHex: function () {
|
|
return this.toBytes().toHex();
|
|
},
|
|
toBytes: function () {
|
|
return this;
|
|
},
|
|
toCss: function () {
|
|
return '#' + this.toHex();
|
|
},
|
|
toCssRgba: function () {
|
|
var rgb = this.toBytes();
|
|
return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat((+this.a).toFixed(3)) + ')';
|
|
},
|
|
toDisplay: function () {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return this.toCss();
|
|
}
|
|
return this.toCssRgba();
|
|
},
|
|
equals: function (c) {
|
|
return c === this || c !== null && this.toCssRgba() == parseColor(c).toCssRgba();
|
|
},
|
|
diff: function (c2) {
|
|
if (c2 == null) {
|
|
return NaN;
|
|
}
|
|
var c1 = this.toBytes();
|
|
c2 = c2.toBytes();
|
|
return Math.sqrt(Math.pow((c1.r - c2.r) * 0.3, 2) + Math.pow((c1.g - c2.g) * 0.59, 2) + Math.pow((c1.b - c2.b) * 0.11, 2));
|
|
},
|
|
clone: function () {
|
|
var c = this.toBytes();
|
|
if (c === this) {
|
|
c = new _Bytes(c.r, c.g, c.b, c.a);
|
|
}
|
|
return c;
|
|
}
|
|
});
|
|
var _RGB = _Color.extend({
|
|
init: function (r, g, b, a) {
|
|
this.r = r;
|
|
this.g = g;
|
|
this.b = b;
|
|
this.a = a;
|
|
},
|
|
toHSV: function () {
|
|
var min, max, delta, h, s, v;
|
|
var r = this.r, g = this.g, b = this.b;
|
|
min = Math.min(r, g, b);
|
|
max = Math.max(r, g, b);
|
|
v = max;
|
|
delta = max - min;
|
|
if (delta === 0) {
|
|
return new _HSV(0, 0, v, this.a);
|
|
}
|
|
if (max !== 0) {
|
|
s = delta / max;
|
|
if (r == max) {
|
|
h = (g - b) / delta;
|
|
} else if (g == max) {
|
|
h = 2 + (b - r) / delta;
|
|
} else {
|
|
h = 4 + (r - g) / delta;
|
|
}
|
|
h *= 60;
|
|
if (h < 0) {
|
|
h += 360;
|
|
}
|
|
} else {
|
|
s = 0;
|
|
h = -1;
|
|
}
|
|
return new _HSV(h, s, v, this.a);
|
|
},
|
|
toHSL: function () {
|
|
var r = this.r, g = this.g, b = this.b;
|
|
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
var h, s, l = (max + min) / 2;
|
|
if (max == min) {
|
|
h = s = 0;
|
|
} else {
|
|
var d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
switch (max) {
|
|
case r:
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
break;
|
|
case g:
|
|
h = (b - r) / d + 2;
|
|
break;
|
|
case b:
|
|
h = (r - g) / d + 4;
|
|
break;
|
|
}
|
|
h *= 60;
|
|
s *= 100;
|
|
l *= 100;
|
|
}
|
|
return new _HSL(h, s, l, this.a);
|
|
},
|
|
toBytes: function () {
|
|
return new _Bytes(this.r * 255, this.g * 255, this.b * 255, this.a);
|
|
}
|
|
});
|
|
var _Bytes = _RGB.extend({
|
|
init: function (r, g, b, a) {
|
|
this.r = Math.round(r);
|
|
this.g = Math.round(g);
|
|
this.b = Math.round(b);
|
|
this.a = a;
|
|
},
|
|
toRGB: function () {
|
|
return new _RGB(this.r / 255, this.g / 255, this.b / 255, this.a);
|
|
},
|
|
toHSV: function () {
|
|
return this.toRGB().toHSV();
|
|
},
|
|
toHSL: function () {
|
|
return this.toRGB().toHSL();
|
|
},
|
|
toHex: function () {
|
|
return hex(this.r, 2) + hex(this.g, 2) + hex(this.b, 2);
|
|
},
|
|
toBytes: function () {
|
|
return this;
|
|
}
|
|
});
|
|
var _HSV = _Color.extend({
|
|
init: function (h, s, v, a) {
|
|
this.h = h;
|
|
this.s = s;
|
|
this.v = v;
|
|
this.a = a;
|
|
},
|
|
toRGB: function () {
|
|
var h = this.h, s = this.s, v = this.v;
|
|
var i, r, g, b, f, p, q, t;
|
|
if (s === 0) {
|
|
r = g = b = v;
|
|
} else {
|
|
h /= 60;
|
|
i = Math.floor(h);
|
|
f = h - i;
|
|
p = v * (1 - s);
|
|
q = v * (1 - s * f);
|
|
t = v * (1 - s * (1 - f));
|
|
switch (i) {
|
|
case 0:
|
|
r = v;
|
|
g = t;
|
|
b = p;
|
|
break;
|
|
case 1:
|
|
r = q;
|
|
g = v;
|
|
b = p;
|
|
break;
|
|
case 2:
|
|
r = p;
|
|
g = v;
|
|
b = t;
|
|
break;
|
|
case 3:
|
|
r = p;
|
|
g = q;
|
|
b = v;
|
|
break;
|
|
case 4:
|
|
r = t;
|
|
g = p;
|
|
b = v;
|
|
break;
|
|
default:
|
|
r = v;
|
|
g = p;
|
|
b = q;
|
|
break;
|
|
}
|
|
}
|
|
return new _RGB(r, g, b, this.a);
|
|
},
|
|
toHSL: function () {
|
|
return this.toRGB().toHSL();
|
|
},
|
|
toBytes: function () {
|
|
return this.toRGB().toBytes();
|
|
}
|
|
});
|
|
var _HSL = _Color.extend({
|
|
init: function (h, s, l, a) {
|
|
this.h = h;
|
|
this.s = s;
|
|
this.l = l;
|
|
this.a = a;
|
|
},
|
|
toRGB: function () {
|
|
var h = this.h, s = this.s, l = this.l;
|
|
var r, g, b;
|
|
if (s === 0) {
|
|
r = g = b = l;
|
|
} else {
|
|
h /= 360;
|
|
s /= 100;
|
|
l /= 100;
|
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
var p = 2 * l - q;
|
|
r = hue2rgb(p, q, h + 1 / 3);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1 / 3);
|
|
}
|
|
return new _RGB(r, g, b, this.a);
|
|
},
|
|
toHSV: function () {
|
|
return this.toRGB().toHSV();
|
|
},
|
|
toBytes: function () {
|
|
return this.toRGB().toBytes();
|
|
}
|
|
});
|
|
Color.fromBytes = function (r, g, b, a) {
|
|
return new _Bytes(r, g, b, a != null ? a : 1);
|
|
};
|
|
Color.fromRGB = function (r, g, b, a) {
|
|
return new _RGB(r, g, b, a != null ? a : 1);
|
|
};
|
|
Color.fromHSV = function (h, s, v, a) {
|
|
return new _HSV(h, s, v, a != null ? a : 1);
|
|
};
|
|
Color.fromHSL = function (h, s, l, a) {
|
|
return new _HSL(h, s, l, a != null ? a : 1);
|
|
};
|
|
kendo.Color = Color;
|
|
kendo.parseColor = parseColor;
|
|
}(window.kendo.jQuery, parseFloat, parseInt));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/main', ['kendo.core'], f);
|
|
}(function () {
|
|
(function () {
|
|
var math = Math, kendo = window.kendo, deepExtend = kendo.deepExtend;
|
|
var DEG_TO_RAD = math.PI / 180, MAX_NUM = Number.MAX_VALUE, MIN_NUM = -Number.MAX_VALUE, UNDEFINED = 'undefined';
|
|
function defined(value) {
|
|
return typeof value !== UNDEFINED;
|
|
}
|
|
function round(value, precision) {
|
|
var power = pow(precision);
|
|
return math.round(value * power) / power;
|
|
}
|
|
function pow(p) {
|
|
if (p) {
|
|
return math.pow(10, p);
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
function limitValue(value, min, max) {
|
|
return math.max(math.min(value, max), min);
|
|
}
|
|
function rad(degrees) {
|
|
return degrees * DEG_TO_RAD;
|
|
}
|
|
function deg(radians) {
|
|
return radians / DEG_TO_RAD;
|
|
}
|
|
function isNumber(val) {
|
|
return typeof val === 'number' && !isNaN(val);
|
|
}
|
|
function valueOrDefault(value, defaultValue) {
|
|
return defined(value) ? value : defaultValue;
|
|
}
|
|
function sqr(value) {
|
|
return value * value;
|
|
}
|
|
function objectKey(object) {
|
|
var parts = [];
|
|
for (var key in object) {
|
|
parts.push(key + object[key]);
|
|
}
|
|
return parts.sort().join('');
|
|
}
|
|
function hashKey(str) {
|
|
var hash = 2166136261;
|
|
for (var i = 0; i < str.length; ++i) {
|
|
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
hash ^= str.charCodeAt(i);
|
|
}
|
|
return hash >>> 0;
|
|
}
|
|
function hashObject(object) {
|
|
return hashKey(objectKey(object));
|
|
}
|
|
var now = Date.now;
|
|
if (!now) {
|
|
now = function () {
|
|
return new Date().getTime();
|
|
};
|
|
}
|
|
function arrayLimits(arr) {
|
|
var length = arr.length, i, min = MAX_NUM, max = MIN_NUM;
|
|
for (i = 0; i < length; i++) {
|
|
max = math.max(max, arr[i]);
|
|
min = math.min(min, arr[i]);
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
function arrayMin(arr) {
|
|
return arrayLimits(arr).min;
|
|
}
|
|
function arrayMax(arr) {
|
|
return arrayLimits(arr).max;
|
|
}
|
|
function sparseArrayMin(arr) {
|
|
return sparseArrayLimits(arr).min;
|
|
}
|
|
function sparseArrayMax(arr) {
|
|
return sparseArrayLimits(arr).max;
|
|
}
|
|
function sparseArrayLimits(arr) {
|
|
var min = MAX_NUM, max = MIN_NUM;
|
|
for (var i = 0, length = arr.length; i < length; i++) {
|
|
var n = arr[i];
|
|
if (n !== null && isFinite(n)) {
|
|
min = math.min(min, n);
|
|
max = math.max(max, n);
|
|
}
|
|
}
|
|
return {
|
|
min: min === MAX_NUM ? undefined : min,
|
|
max: max === MIN_NUM ? undefined : max
|
|
};
|
|
}
|
|
function last(array) {
|
|
if (array) {
|
|
return array[array.length - 1];
|
|
}
|
|
}
|
|
function append(first, second) {
|
|
first.push.apply(first, second);
|
|
return first;
|
|
}
|
|
function renderTemplate(text) {
|
|
return kendo.template(text, {
|
|
useWithBlock: false,
|
|
paramName: 'd'
|
|
});
|
|
}
|
|
function renderAttr(name, value) {
|
|
return defined(value) && value !== null ? ' ' + name + '=\'' + value + '\' ' : '';
|
|
}
|
|
function renderAllAttr(attrs) {
|
|
var output = '';
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
output += renderAttr(attrs[i][0], attrs[i][1]);
|
|
}
|
|
return output;
|
|
}
|
|
function renderStyle(attrs) {
|
|
var output = '';
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
var value = attrs[i][1];
|
|
if (defined(value)) {
|
|
output += attrs[i][0] + ':' + value + ';';
|
|
}
|
|
}
|
|
if (output !== '') {
|
|
return output;
|
|
}
|
|
}
|
|
function renderSize(size) {
|
|
if (typeof size !== 'string') {
|
|
size += 'px';
|
|
}
|
|
return size;
|
|
}
|
|
function renderPos(pos) {
|
|
var result = [];
|
|
if (pos) {
|
|
var parts = kendo.toHyphens(pos).split('-');
|
|
for (var i = 0; i < parts.length; i++) {
|
|
result.push('k-pos-' + parts[i]);
|
|
}
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
function isTransparent(color) {
|
|
return color === '' || color === null || color === 'none' || color === 'transparent' || !defined(color);
|
|
}
|
|
function arabicToRoman(n) {
|
|
var literals = {
|
|
1: 'i',
|
|
10: 'x',
|
|
100: 'c',
|
|
2: 'ii',
|
|
20: 'xx',
|
|
200: 'cc',
|
|
3: 'iii',
|
|
30: 'xxx',
|
|
300: 'ccc',
|
|
4: 'iv',
|
|
40: 'xl',
|
|
400: 'cd',
|
|
5: 'v',
|
|
50: 'l',
|
|
500: 'd',
|
|
6: 'vi',
|
|
60: 'lx',
|
|
600: 'dc',
|
|
7: 'vii',
|
|
70: 'lxx',
|
|
700: 'dcc',
|
|
8: 'viii',
|
|
80: 'lxxx',
|
|
800: 'dccc',
|
|
9: 'ix',
|
|
90: 'xc',
|
|
900: 'cm',
|
|
1000: 'm'
|
|
};
|
|
var values = [
|
|
1000,
|
|
900,
|
|
800,
|
|
700,
|
|
600,
|
|
500,
|
|
400,
|
|
300,
|
|
200,
|
|
100,
|
|
90,
|
|
80,
|
|
70,
|
|
60,
|
|
50,
|
|
40,
|
|
30,
|
|
20,
|
|
10,
|
|
9,
|
|
8,
|
|
7,
|
|
6,
|
|
5,
|
|
4,
|
|
3,
|
|
2,
|
|
1
|
|
];
|
|
var roman = '';
|
|
while (n > 0) {
|
|
if (n < values[0]) {
|
|
values.shift();
|
|
} else {
|
|
roman += literals[values[0]];
|
|
n -= values[0];
|
|
}
|
|
}
|
|
return roman;
|
|
}
|
|
function romanToArabic(r) {
|
|
r = r.toLowerCase();
|
|
var digits = {
|
|
i: 1,
|
|
v: 5,
|
|
x: 10,
|
|
l: 50,
|
|
c: 100,
|
|
d: 500,
|
|
m: 1000
|
|
};
|
|
var value = 0, prev = 0;
|
|
for (var i = 0; i < r.length; ++i) {
|
|
var v = digits[r.charAt(i)];
|
|
if (!v) {
|
|
return null;
|
|
}
|
|
value += v;
|
|
if (v > prev) {
|
|
value -= 2 * prev;
|
|
}
|
|
prev = v;
|
|
}
|
|
return value;
|
|
}
|
|
function memoize(f) {
|
|
var cache = Object.create(null);
|
|
return function () {
|
|
var id = '';
|
|
for (var i = arguments.length; --i >= 0;) {
|
|
id += ':' + arguments[i];
|
|
}
|
|
if (id in cache) {
|
|
return cache[id];
|
|
}
|
|
return f.apply(this, arguments);
|
|
};
|
|
}
|
|
function ucs2decode(string) {
|
|
var output = [], counter = 0, length = string.length, value, extra;
|
|
while (counter < length) {
|
|
value = string.charCodeAt(counter++);
|
|
if (value >= 55296 && value <= 56319 && counter < length) {
|
|
extra = string.charCodeAt(counter++);
|
|
if ((extra & 64512) == 56320) {
|
|
output.push(((value & 1023) << 10) + (extra & 1023) + 65536);
|
|
} else {
|
|
output.push(value);
|
|
counter--;
|
|
}
|
|
} else {
|
|
output.push(value);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
function ucs2encode(array) {
|
|
return array.map(function (value) {
|
|
var output = '';
|
|
if (value > 65535) {
|
|
value -= 65536;
|
|
output += String.fromCharCode(value >>> 10 & 1023 | 55296);
|
|
value = 56320 | value & 1023;
|
|
}
|
|
output += String.fromCharCode(value);
|
|
return output;
|
|
}).join('');
|
|
}
|
|
deepExtend(kendo, {
|
|
util: {
|
|
MAX_NUM: MAX_NUM,
|
|
MIN_NUM: MIN_NUM,
|
|
append: append,
|
|
arrayLimits: arrayLimits,
|
|
arrayMin: arrayMin,
|
|
arrayMax: arrayMax,
|
|
defined: defined,
|
|
deg: deg,
|
|
hashKey: hashKey,
|
|
hashObject: hashObject,
|
|
isNumber: isNumber,
|
|
isTransparent: isTransparent,
|
|
last: last,
|
|
limitValue: limitValue,
|
|
now: now,
|
|
objectKey: objectKey,
|
|
round: round,
|
|
rad: rad,
|
|
renderAttr: renderAttr,
|
|
renderAllAttr: renderAllAttr,
|
|
renderPos: renderPos,
|
|
renderSize: renderSize,
|
|
renderStyle: renderStyle,
|
|
renderTemplate: renderTemplate,
|
|
sparseArrayLimits: sparseArrayLimits,
|
|
sparseArrayMin: sparseArrayMin,
|
|
sparseArrayMax: sparseArrayMax,
|
|
sqr: sqr,
|
|
valueOrDefault: valueOrDefault,
|
|
romanToArabic: romanToArabic,
|
|
arabicToRoman: arabicToRoman,
|
|
memoize: memoize,
|
|
ucs2encode: ucs2encode,
|
|
ucs2decode: ucs2decode
|
|
}
|
|
});
|
|
kendo.drawing.util = kendo.util;
|
|
kendo.dataviz.util = kendo.util;
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/text-metrics', [
|
|
'kendo.core',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var doc = document, kendo = window.kendo, Class = kendo.Class, util = kendo.util, defined = util.defined;
|
|
var LRUCache = Class.extend({
|
|
init: function (size) {
|
|
this._size = size;
|
|
this._length = 0;
|
|
this._map = {};
|
|
},
|
|
put: function (key, value) {
|
|
var lru = this, map = lru._map, entry = {
|
|
key: key,
|
|
value: value
|
|
};
|
|
map[key] = entry;
|
|
if (!lru._head) {
|
|
lru._head = lru._tail = entry;
|
|
} else {
|
|
lru._tail.newer = entry;
|
|
entry.older = lru._tail;
|
|
lru._tail = entry;
|
|
}
|
|
if (lru._length >= lru._size) {
|
|
map[lru._head.key] = null;
|
|
lru._head = lru._head.newer;
|
|
lru._head.older = null;
|
|
} else {
|
|
lru._length++;
|
|
}
|
|
},
|
|
get: function (key) {
|
|
var lru = this, entry = lru._map[key];
|
|
if (entry) {
|
|
if (entry === lru._head && entry !== lru._tail) {
|
|
lru._head = entry.newer;
|
|
lru._head.older = null;
|
|
}
|
|
if (entry !== lru._tail) {
|
|
if (entry.older) {
|
|
entry.older.newer = entry.newer;
|
|
entry.newer.older = entry.older;
|
|
}
|
|
entry.older = lru._tail;
|
|
entry.newer = null;
|
|
lru._tail.newer = entry;
|
|
lru._tail = entry;
|
|
}
|
|
return entry.value;
|
|
}
|
|
}
|
|
});
|
|
var defaultMeasureBox = $('<div style=\'position: absolute !important; top: -4000px !important; width: auto !important; height: auto !important;' + 'padding: 0 !important; margin: 0 !important; border: 0 !important;' + 'line-height: normal !important; visibility: hidden !important; white-space: nowrap!important;\' />')[0];
|
|
function zeroSize() {
|
|
return {
|
|
width: 0,
|
|
height: 0,
|
|
baseline: 0
|
|
};
|
|
}
|
|
var TextMetrics = Class.extend({
|
|
init: function (options) {
|
|
this._cache = new LRUCache(1000);
|
|
this._initOptions(options);
|
|
},
|
|
options: { baselineMarkerSize: 1 },
|
|
measure: function (text, style, box) {
|
|
if (!text) {
|
|
return zeroSize();
|
|
}
|
|
var styleKey = util.objectKey(style), cacheKey = util.hashKey(text + styleKey), cachedResult = this._cache.get(cacheKey);
|
|
if (cachedResult) {
|
|
return cachedResult;
|
|
}
|
|
var size = zeroSize();
|
|
var measureBox = box ? box : defaultMeasureBox;
|
|
var baselineMarker = this._baselineMarker().cloneNode(false);
|
|
for (var key in style) {
|
|
var value = style[key];
|
|
if (defined(value)) {
|
|
measureBox.style[key] = value;
|
|
}
|
|
}
|
|
$(measureBox).text(text);
|
|
measureBox.appendChild(baselineMarker);
|
|
doc.body.appendChild(measureBox);
|
|
if ((text + '').length) {
|
|
size.width = measureBox.offsetWidth - this.options.baselineMarkerSize;
|
|
size.height = measureBox.offsetHeight;
|
|
size.baseline = baselineMarker.offsetTop + this.options.baselineMarkerSize;
|
|
}
|
|
if (size.width > 0 && size.height > 0) {
|
|
this._cache.put(cacheKey, size);
|
|
}
|
|
measureBox.parentNode.removeChild(measureBox);
|
|
return size;
|
|
},
|
|
_baselineMarker: function () {
|
|
return $('<div class=\'k-baseline-marker\' ' + 'style=\'display: inline-block; vertical-align: baseline;' + 'width: ' + this.options.baselineMarkerSize + 'px; height: ' + this.options.baselineMarkerSize + 'px;' + 'overflow: hidden;\' />')[0];
|
|
}
|
|
});
|
|
TextMetrics.current = new TextMetrics();
|
|
function measureText(text, style, measureBox) {
|
|
return TextMetrics.current.measure(text, style, measureBox);
|
|
}
|
|
function loadFonts(fonts, callback) {
|
|
var promises = [];
|
|
if (fonts.length > 0 && document.fonts) {
|
|
try {
|
|
promises = fonts.map(function (font) {
|
|
return document.fonts.load(font);
|
|
});
|
|
} catch (e) {
|
|
kendo.logToConsole(e);
|
|
}
|
|
Promise.all(promises).then(callback, callback);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
kendo.util.TextMetrics = TextMetrics;
|
|
kendo.util.LRUCache = LRUCache;
|
|
kendo.util.loadFonts = loadFonts;
|
|
kendo.util.measureText = measureText;
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/base64', ['util/main'], f);
|
|
}(function () {
|
|
(function () {
|
|
var kendo = window.kendo, deepExtend = kendo.deepExtend, fromCharCode = String.fromCharCode;
|
|
var KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
function encodeBase64(input) {
|
|
var output = '';
|
|
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
|
var i = 0;
|
|
input = encodeUTF8(input);
|
|
while (i < input.length) {
|
|
chr1 = input.charCodeAt(i++);
|
|
chr2 = input.charCodeAt(i++);
|
|
chr3 = input.charCodeAt(i++);
|
|
enc1 = chr1 >> 2;
|
|
enc2 = (chr1 & 3) << 4 | chr2 >> 4;
|
|
enc3 = (chr2 & 15) << 2 | chr3 >> 6;
|
|
enc4 = chr3 & 63;
|
|
if (isNaN(chr2)) {
|
|
enc3 = enc4 = 64;
|
|
} else if (isNaN(chr3)) {
|
|
enc4 = 64;
|
|
}
|
|
output = output + KEY_STR.charAt(enc1) + KEY_STR.charAt(enc2) + KEY_STR.charAt(enc3) + KEY_STR.charAt(enc4);
|
|
}
|
|
return output;
|
|
}
|
|
function encodeUTF8(input) {
|
|
var output = '';
|
|
for (var i = 0; i < input.length; i++) {
|
|
var c = input.charCodeAt(i);
|
|
if (c < 128) {
|
|
output += fromCharCode(c);
|
|
} else if (c < 2048) {
|
|
output += fromCharCode(192 | c >>> 6);
|
|
output += fromCharCode(128 | c & 63);
|
|
} else if (c < 65536) {
|
|
output += fromCharCode(224 | c >>> 12);
|
|
output += fromCharCode(128 | c >>> 6 & 63);
|
|
output += fromCharCode(128 | c & 63);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
deepExtend(kendo.util, {
|
|
encodeBase64: encodeBase64,
|
|
encodeUTF8: encodeUTF8
|
|
});
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('mixins/observers', ['kendo.core'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var math = Math, kendo = window.kendo, deepExtend = kendo.deepExtend, inArray = $.inArray;
|
|
var ObserversMixin = {
|
|
observers: function () {
|
|
this._observers = this._observers || [];
|
|
return this._observers;
|
|
},
|
|
addObserver: function (element) {
|
|
if (!this._observers) {
|
|
this._observers = [element];
|
|
} else {
|
|
this._observers.push(element);
|
|
}
|
|
return this;
|
|
},
|
|
removeObserver: function (element) {
|
|
var observers = this.observers();
|
|
var index = inArray(element, observers);
|
|
if (index != -1) {
|
|
observers.splice(index, 1);
|
|
}
|
|
return this;
|
|
},
|
|
trigger: function (methodName, event) {
|
|
var observers = this._observers;
|
|
var observer;
|
|
var idx;
|
|
if (observers && !this._suspended) {
|
|
for (idx = 0; idx < observers.length; idx++) {
|
|
observer = observers[idx];
|
|
if (observer[methodName]) {
|
|
observer[methodName](event);
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
optionsChange: function (e) {
|
|
this.trigger('optionsChange', e);
|
|
},
|
|
geometryChange: function (e) {
|
|
this.trigger('geometryChange', e);
|
|
},
|
|
suspend: function () {
|
|
this._suspended = (this._suspended || 0) + 1;
|
|
return this;
|
|
},
|
|
resume: function () {
|
|
this._suspended = math.max((this._suspended || 0) - 1, 0);
|
|
return this;
|
|
},
|
|
_observerField: function (field, value) {
|
|
if (this[field]) {
|
|
this[field].removeObserver(this);
|
|
}
|
|
this[field] = value;
|
|
value.addObserver(this);
|
|
}
|
|
};
|
|
deepExtend(kendo, { mixins: { ObserversMixin: ObserversMixin } });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/geometry', [
|
|
'util/main',
|
|
'mixins/observers'
|
|
], f);
|
|
}(function () {
|
|
(function () {
|
|
var math = Math, pow = math.pow, kendo = window.kendo, Class = kendo.Class, deepExtend = kendo.deepExtend, ObserversMixin = kendo.mixins.ObserversMixin, util = kendo.util, defined = util.defined, rad = util.rad, deg = util.deg, round = util.round;
|
|
var PI_DIV_2 = math.PI / 2, MIN_NUM = util.MIN_NUM, MAX_NUM = util.MAX_NUM;
|
|
var Point = Class.extend({
|
|
init: function (x, y) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
},
|
|
equals: function (other) {
|
|
return other && other.x === this.x && other.y === this.y;
|
|
},
|
|
clone: function () {
|
|
return new Point(this.x, this.y);
|
|
},
|
|
rotate: function (angle, origin) {
|
|
return this.transform(transform().rotate(angle, origin));
|
|
},
|
|
translate: function (x, y) {
|
|
this.x += x;
|
|
this.y += y;
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
translateWith: function (point) {
|
|
return this.translate(point.x, point.y);
|
|
},
|
|
move: function (x, y) {
|
|
this.x = this.y = 0;
|
|
return this.translate(x, y);
|
|
},
|
|
scale: function (scaleX, scaleY) {
|
|
if (!defined(scaleY)) {
|
|
scaleY = scaleX;
|
|
}
|
|
this.x *= scaleX;
|
|
this.y *= scaleY;
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
scaleCopy: function (scaleX, scaleY) {
|
|
return this.clone().scale(scaleX, scaleY);
|
|
},
|
|
transform: function (transformation) {
|
|
var mx = toMatrix(transformation), x = this.x, y = this.y;
|
|
this.x = mx.a * x + mx.c * y + mx.e;
|
|
this.y = mx.b * x + mx.d * y + mx.f;
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
transformCopy: function (transformation) {
|
|
var point = this.clone();
|
|
if (transformation) {
|
|
point.transform(transformation);
|
|
}
|
|
return point;
|
|
},
|
|
distanceTo: function (point) {
|
|
var dx = this.x - point.x;
|
|
var dy = this.y - point.y;
|
|
return math.sqrt(dx * dx + dy * dy);
|
|
},
|
|
round: function (digits) {
|
|
this.x = round(this.x, digits);
|
|
this.y = round(this.y, digits);
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
toArray: function (digits) {
|
|
var doRound = defined(digits);
|
|
var x = doRound ? round(this.x, digits) : this.x;
|
|
var y = doRound ? round(this.y, digits) : this.y;
|
|
return [
|
|
x,
|
|
y
|
|
];
|
|
}
|
|
});
|
|
defineAccessors(Point.fn, [
|
|
'x',
|
|
'y'
|
|
]);
|
|
deepExtend(Point.fn, ObserversMixin);
|
|
Point.fn.toString = function (digits, separator) {
|
|
var x = this.x, y = this.y;
|
|
if (defined(digits)) {
|
|
x = round(x, digits);
|
|
y = round(y, digits);
|
|
}
|
|
separator = separator || ' ';
|
|
return x + separator + y;
|
|
};
|
|
Point.create = function (arg0, arg1) {
|
|
if (defined(arg0)) {
|
|
if (arg0 instanceof Point) {
|
|
return arg0;
|
|
} else if (arguments.length === 1 && arg0.length === 2) {
|
|
return new Point(arg0[0], arg0[1]);
|
|
} else {
|
|
return new Point(arg0, arg1);
|
|
}
|
|
}
|
|
};
|
|
Point.min = function () {
|
|
var minX = util.MAX_NUM;
|
|
var minY = util.MAX_NUM;
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var pt = arguments[i];
|
|
minX = math.min(pt.x, minX);
|
|
minY = math.min(pt.y, minY);
|
|
}
|
|
return new Point(minX, minY);
|
|
};
|
|
Point.max = function () {
|
|
var maxX = util.MIN_NUM;
|
|
var maxY = util.MIN_NUM;
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var pt = arguments[i];
|
|
maxX = math.max(pt.x, maxX);
|
|
maxY = math.max(pt.y, maxY);
|
|
}
|
|
return new Point(maxX, maxY);
|
|
};
|
|
Point.minPoint = function () {
|
|
return new Point(MIN_NUM, MIN_NUM);
|
|
};
|
|
Point.maxPoint = function () {
|
|
return new Point(MAX_NUM, MAX_NUM);
|
|
};
|
|
Point.ZERO = new Point(0, 0);
|
|
var Size = Class.extend({
|
|
init: function (width, height) {
|
|
this.width = width || 0;
|
|
this.height = height || 0;
|
|
},
|
|
equals: function (other) {
|
|
return other && other.width === this.width && other.height === this.height;
|
|
},
|
|
clone: function () {
|
|
return new Size(this.width, this.height);
|
|
},
|
|
toArray: function (digits) {
|
|
var doRound = defined(digits);
|
|
var width = doRound ? round(this.width, digits) : this.width;
|
|
var height = doRound ? round(this.height, digits) : this.height;
|
|
return [
|
|
width,
|
|
height
|
|
];
|
|
}
|
|
});
|
|
defineAccessors(Size.fn, [
|
|
'width',
|
|
'height'
|
|
]);
|
|
deepExtend(Size.fn, ObserversMixin);
|
|
Size.create = function (arg0, arg1) {
|
|
if (defined(arg0)) {
|
|
if (arg0 instanceof Size) {
|
|
return arg0;
|
|
} else if (arguments.length === 1 && arg0.length === 2) {
|
|
return new Size(arg0[0], arg0[1]);
|
|
} else {
|
|
return new Size(arg0, arg1);
|
|
}
|
|
}
|
|
};
|
|
Size.ZERO = new Size(0, 0);
|
|
var Rect = Class.extend({
|
|
init: function (origin, size) {
|
|
this.setOrigin(origin || new Point());
|
|
this.setSize(size || new Size());
|
|
},
|
|
clone: function () {
|
|
return new Rect(this.origin.clone(), this.size.clone());
|
|
},
|
|
equals: function (other) {
|
|
return other && other.origin.equals(this.origin) && other.size.equals(this.size);
|
|
},
|
|
setOrigin: function (value) {
|
|
this._observerField('origin', Point.create(value));
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
getOrigin: function () {
|
|
return this.origin;
|
|
},
|
|
setSize: function (value) {
|
|
this._observerField('size', Size.create(value));
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
getSize: function () {
|
|
return this.size;
|
|
},
|
|
width: function () {
|
|
return this.size.width;
|
|
},
|
|
height: function () {
|
|
return this.size.height;
|
|
},
|
|
topLeft: function () {
|
|
return this.origin.clone();
|
|
},
|
|
bottomRight: function () {
|
|
return this.origin.clone().translate(this.width(), this.height());
|
|
},
|
|
topRight: function () {
|
|
return this.origin.clone().translate(this.width(), 0);
|
|
},
|
|
bottomLeft: function () {
|
|
return this.origin.clone().translate(0, this.height());
|
|
},
|
|
center: function () {
|
|
return this.origin.clone().translate(this.width() / 2, this.height() / 2);
|
|
},
|
|
bbox: function (matrix) {
|
|
var tl = this.topLeft().transformCopy(matrix);
|
|
var tr = this.topRight().transformCopy(matrix);
|
|
var br = this.bottomRight().transformCopy(matrix);
|
|
var bl = this.bottomLeft().transformCopy(matrix);
|
|
return Rect.fromPoints(tl, tr, br, bl);
|
|
},
|
|
transformCopy: function (m) {
|
|
return Rect.fromPoints(this.topLeft().transform(m), this.bottomRight().transform(m));
|
|
}
|
|
});
|
|
deepExtend(Rect.fn, ObserversMixin);
|
|
Rect.fromPoints = function () {
|
|
var topLeft = Point.min.apply(this, arguments);
|
|
var bottomRight = Point.max.apply(this, arguments);
|
|
var size = new Size(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
|
|
return new Rect(topLeft, size);
|
|
};
|
|
Rect.union = function (a, b) {
|
|
return Rect.fromPoints(Point.min(a.topLeft(), b.topLeft()), Point.max(a.bottomRight(), b.bottomRight()));
|
|
};
|
|
Rect.intersect = function (a, b) {
|
|
a = {
|
|
left: a.topLeft().x,
|
|
top: a.topLeft().y,
|
|
right: a.bottomRight().x,
|
|
bottom: a.bottomRight().y
|
|
};
|
|
b = {
|
|
left: b.topLeft().x,
|
|
top: b.topLeft().y,
|
|
right: b.bottomRight().x,
|
|
bottom: b.bottomRight().y
|
|
};
|
|
if (a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom) {
|
|
return Rect.fromPoints(new Point(math.max(a.left, b.left), math.max(a.top, b.top)), new Point(math.min(a.right, b.right), math.min(a.bottom, b.bottom)));
|
|
}
|
|
};
|
|
var Circle = Class.extend({
|
|
init: function (center, radius) {
|
|
this.setCenter(center || new Point());
|
|
this.setRadius(radius || 0);
|
|
},
|
|
setCenter: function (value) {
|
|
this._observerField('center', Point.create(value));
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
getCenter: function () {
|
|
return this.center;
|
|
},
|
|
equals: function (other) {
|
|
return other && other.center.equals(this.center) && other.radius === this.radius;
|
|
},
|
|
clone: function () {
|
|
return new Circle(this.center.clone(), this.radius);
|
|
},
|
|
pointAt: function (angle) {
|
|
return this._pointAt(rad(angle));
|
|
},
|
|
bbox: function (matrix) {
|
|
var minPoint = Point.maxPoint();
|
|
var maxPoint = Point.minPoint();
|
|
var extremeAngles = ellipseExtremeAngles(this.center, this.radius, this.radius, matrix);
|
|
for (var i = 0; i < 4; i++) {
|
|
var currentPointX = this._pointAt(extremeAngles.x + i * PI_DIV_2).transformCopy(matrix);
|
|
var currentPointY = this._pointAt(extremeAngles.y + i * PI_DIV_2).transformCopy(matrix);
|
|
var currentPoint = new Point(currentPointX.x, currentPointY.y);
|
|
minPoint = Point.min(minPoint, currentPoint);
|
|
maxPoint = Point.max(maxPoint, currentPoint);
|
|
}
|
|
return Rect.fromPoints(minPoint, maxPoint);
|
|
},
|
|
_pointAt: function (angle) {
|
|
var c = this.center;
|
|
var r = this.radius;
|
|
return new Point(c.x - r * math.cos(angle), c.y - r * math.sin(angle));
|
|
}
|
|
});
|
|
defineAccessors(Circle.fn, ['radius']);
|
|
deepExtend(Circle.fn, ObserversMixin);
|
|
var Arc = Class.extend({
|
|
init: function (center, options) {
|
|
this.setCenter(center || new Point());
|
|
options = options || {};
|
|
this.radiusX = options.radiusX;
|
|
this.radiusY = options.radiusY || options.radiusX;
|
|
this.startAngle = options.startAngle;
|
|
this.endAngle = options.endAngle;
|
|
this.anticlockwise = options.anticlockwise || false;
|
|
},
|
|
clone: function () {
|
|
return new Arc(this.center, {
|
|
radiusX: this.radiusX,
|
|
radiusY: this.radiusY,
|
|
startAngle: this.startAngle,
|
|
endAngle: this.endAngle,
|
|
anticlockwise: this.anticlockwise
|
|
});
|
|
},
|
|
setCenter: function (value) {
|
|
this._observerField('center', Point.create(value));
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
getCenter: function () {
|
|
return this.center;
|
|
},
|
|
MAX_INTERVAL: 45,
|
|
pointAt: function (angle) {
|
|
var center = this.center;
|
|
var radian = rad(angle);
|
|
return new Point(center.x + this.radiusX * math.cos(radian), center.y + this.radiusY * math.sin(radian));
|
|
},
|
|
curvePoints: function () {
|
|
var startAngle = this.startAngle;
|
|
var dir = this.anticlockwise ? -1 : 1;
|
|
var curvePoints = [this.pointAt(startAngle)];
|
|
var currentAngle = startAngle;
|
|
var interval = this._arcInterval();
|
|
var intervalAngle = interval.endAngle - interval.startAngle;
|
|
var subIntervalsCount = math.ceil(intervalAngle / this.MAX_INTERVAL);
|
|
var subIntervalAngle = intervalAngle / subIntervalsCount;
|
|
for (var i = 1; i <= subIntervalsCount; i++) {
|
|
var nextAngle = currentAngle + dir * subIntervalAngle;
|
|
var points = this._intervalCurvePoints(currentAngle, nextAngle);
|
|
curvePoints.push(points.cp1, points.cp2, points.p2);
|
|
currentAngle = nextAngle;
|
|
}
|
|
return curvePoints;
|
|
},
|
|
bbox: function (matrix) {
|
|
var arc = this;
|
|
var interval = arc._arcInterval();
|
|
var startAngle = interval.startAngle;
|
|
var endAngle = interval.endAngle;
|
|
var extremeAngles = ellipseExtremeAngles(this.center, this.radiusX, this.radiusY, matrix);
|
|
var extremeX = deg(extremeAngles.x);
|
|
var extremeY = deg(extremeAngles.y);
|
|
var currentPoint = arc.pointAt(startAngle).transformCopy(matrix);
|
|
var endPoint = arc.pointAt(endAngle).transformCopy(matrix);
|
|
var minPoint = Point.min(currentPoint, endPoint);
|
|
var maxPoint = Point.max(currentPoint, endPoint);
|
|
var currentAngleX = bboxStartAngle(extremeX, startAngle);
|
|
var currentAngleY = bboxStartAngle(extremeY, startAngle);
|
|
while (currentAngleX < endAngle || currentAngleY < endAngle) {
|
|
var currentPointX;
|
|
if (currentAngleX < endAngle) {
|
|
currentPointX = arc.pointAt(currentAngleX).transformCopy(matrix);
|
|
currentAngleX += 90;
|
|
}
|
|
var currentPointY;
|
|
if (currentAngleY < endAngle) {
|
|
currentPointY = arc.pointAt(currentAngleY).transformCopy(matrix);
|
|
currentAngleY += 90;
|
|
}
|
|
currentPoint = new Point(currentPointX.x, currentPointY.y);
|
|
minPoint = Point.min(minPoint, currentPoint);
|
|
maxPoint = Point.max(maxPoint, currentPoint);
|
|
}
|
|
return Rect.fromPoints(minPoint, maxPoint);
|
|
},
|
|
_arcInterval: function () {
|
|
var startAngle = this.startAngle;
|
|
var endAngle = this.endAngle;
|
|
var anticlockwise = this.anticlockwise;
|
|
if (anticlockwise) {
|
|
var oldStart = startAngle;
|
|
startAngle = endAngle;
|
|
endAngle = oldStart;
|
|
}
|
|
if (startAngle > endAngle || anticlockwise && startAngle === endAngle) {
|
|
endAngle += 360;
|
|
}
|
|
return {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle
|
|
};
|
|
},
|
|
_intervalCurvePoints: function (startAngle, endAngle) {
|
|
var arc = this;
|
|
var p1 = arc.pointAt(startAngle);
|
|
var p2 = arc.pointAt(endAngle);
|
|
var p1Derivative = arc._derivativeAt(startAngle);
|
|
var p2Derivative = arc._derivativeAt(endAngle);
|
|
var t = (rad(endAngle) - rad(startAngle)) / 3;
|
|
var cp1 = new Point(p1.x + t * p1Derivative.x, p1.y + t * p1Derivative.y);
|
|
var cp2 = new Point(p2.x - t * p2Derivative.x, p2.y - t * p2Derivative.y);
|
|
return {
|
|
p1: p1,
|
|
cp1: cp1,
|
|
cp2: cp2,
|
|
p2: p2
|
|
};
|
|
},
|
|
_derivativeAt: function (angle) {
|
|
var arc = this;
|
|
var radian = rad(angle);
|
|
return new Point(-arc.radiusX * math.sin(radian), arc.radiusY * math.cos(radian));
|
|
}
|
|
});
|
|
defineAccessors(Arc.fn, [
|
|
'radiusX',
|
|
'radiusY',
|
|
'startAngle',
|
|
'endAngle',
|
|
'anticlockwise'
|
|
]);
|
|
deepExtend(Arc.fn, ObserversMixin);
|
|
Arc.fromPoints = function (start, end, rx, ry, largeArc, swipe) {
|
|
var arcParameters = normalizeArcParameters(start.x, start.y, end.x, end.y, rx, ry, largeArc, swipe);
|
|
return new Arc(arcParameters.center, {
|
|
startAngle: arcParameters.startAngle,
|
|
endAngle: arcParameters.endAngle,
|
|
radiusX: rx,
|
|
radiusY: ry,
|
|
anticlockwise: swipe === 0
|
|
});
|
|
};
|
|
var Matrix = Class.extend({
|
|
init: function (a, b, c, d, e, f) {
|
|
this.a = a || 0;
|
|
this.b = b || 0;
|
|
this.c = c || 0;
|
|
this.d = d || 0;
|
|
this.e = e || 0;
|
|
this.f = f || 0;
|
|
},
|
|
multiplyCopy: function (m) {
|
|
return new Matrix(this.a * m.a + this.c * m.b, this.b * m.a + this.d * m.b, this.a * m.c + this.c * m.d, this.b * m.c + this.d * m.d, this.a * m.e + this.c * m.f + this.e, this.b * m.e + this.d * m.f + this.f);
|
|
},
|
|
invert: function () {
|
|
var a = this.a, b = this.b;
|
|
var d = this.c, e = this.d;
|
|
var g = this.e, h = this.f;
|
|
var det = a * e - b * d;
|
|
if (det === 0) {
|
|
return null;
|
|
}
|
|
return new Matrix(e / det, -b / det, -d / det, a / det, (d * h - e * g) / det, (b * g - a * h) / det);
|
|
},
|
|
clone: function () {
|
|
return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
|
|
},
|
|
equals: function (other) {
|
|
if (!other) {
|
|
return false;
|
|
}
|
|
return this.a === other.a && this.b === other.b && this.c === other.c && this.d === other.d && this.e === other.e && this.f === other.f;
|
|
},
|
|
round: function (precision) {
|
|
this.a = round(this.a, precision);
|
|
this.b = round(this.b, precision);
|
|
this.c = round(this.c, precision);
|
|
this.d = round(this.d, precision);
|
|
this.e = round(this.e, precision);
|
|
this.f = round(this.f, precision);
|
|
return this;
|
|
},
|
|
toArray: function (precision) {
|
|
var arr = [
|
|
this.a,
|
|
this.b,
|
|
this.c,
|
|
this.d,
|
|
this.e,
|
|
this.f
|
|
];
|
|
if (defined(precision)) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
arr[i] = round(arr[i], precision);
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
});
|
|
Matrix.fn.toString = function (precision, separator) {
|
|
return this.toArray(precision).join(separator || ',');
|
|
};
|
|
Matrix.translate = function (x, y) {
|
|
return new Matrix(1, 0, 0, 1, x, y);
|
|
};
|
|
Matrix.unit = function () {
|
|
return new Matrix(1, 0, 0, 1, 0, 0);
|
|
};
|
|
Matrix.rotate = function (angle, x, y) {
|
|
var m = new Matrix();
|
|
m.a = math.cos(rad(angle));
|
|
m.b = math.sin(rad(angle));
|
|
m.c = -m.b;
|
|
m.d = m.a;
|
|
m.e = x - x * m.a + y * m.b || 0;
|
|
m.f = y - y * m.a - x * m.b || 0;
|
|
return m;
|
|
};
|
|
Matrix.scale = function (scaleX, scaleY) {
|
|
return new Matrix(scaleX, 0, 0, scaleY, 0, 0);
|
|
};
|
|
Matrix.IDENTITY = Matrix.unit();
|
|
var Transformation = Class.extend({
|
|
init: function (matrix) {
|
|
this._matrix = matrix || Matrix.unit();
|
|
},
|
|
clone: function () {
|
|
return new Transformation(this._matrix.clone());
|
|
},
|
|
equals: function (other) {
|
|
return other && other._matrix.equals(this._matrix);
|
|
},
|
|
_optionsChange: function () {
|
|
this.optionsChange({
|
|
field: 'transform',
|
|
value: this
|
|
});
|
|
},
|
|
translate: function (x, y) {
|
|
this._matrix = this._matrix.multiplyCopy(Matrix.translate(x, y));
|
|
this._optionsChange();
|
|
return this;
|
|
},
|
|
scale: function (scaleX, scaleY, origin) {
|
|
if (!defined(scaleY)) {
|
|
scaleY = scaleX;
|
|
}
|
|
if (origin) {
|
|
origin = Point.create(origin);
|
|
this._matrix = this._matrix.multiplyCopy(Matrix.translate(origin.x, origin.y));
|
|
}
|
|
this._matrix = this._matrix.multiplyCopy(Matrix.scale(scaleX, scaleY));
|
|
if (origin) {
|
|
this._matrix = this._matrix.multiplyCopy(Matrix.translate(-origin.x, -origin.y));
|
|
}
|
|
this._optionsChange();
|
|
return this;
|
|
},
|
|
rotate: function (angle, origin) {
|
|
origin = Point.create(origin) || Point.ZERO;
|
|
this._matrix = this._matrix.multiplyCopy(Matrix.rotate(angle, origin.x, origin.y));
|
|
this._optionsChange();
|
|
return this;
|
|
},
|
|
multiply: function (transformation) {
|
|
var matrix = toMatrix(transformation);
|
|
this._matrix = this._matrix.multiplyCopy(matrix);
|
|
this._optionsChange();
|
|
return this;
|
|
},
|
|
matrix: function (matrix) {
|
|
if (matrix) {
|
|
this._matrix = matrix;
|
|
this._optionsChange();
|
|
return this;
|
|
} else {
|
|
return this._matrix;
|
|
}
|
|
}
|
|
});
|
|
deepExtend(Transformation.fn, ObserversMixin);
|
|
function transform(matrix) {
|
|
if (matrix === null) {
|
|
return null;
|
|
}
|
|
if (matrix instanceof Transformation) {
|
|
return matrix;
|
|
}
|
|
return new Transformation(matrix);
|
|
}
|
|
function toMatrix(value) {
|
|
if (value && kendo.isFunction(value.matrix)) {
|
|
return value.matrix();
|
|
}
|
|
return value;
|
|
}
|
|
function ellipseExtremeAngles(center, rx, ry, matrix) {
|
|
var extremeX = 0, extremeY = 0;
|
|
if (matrix) {
|
|
extremeX = math.atan2(matrix.c * ry, matrix.a * rx);
|
|
if (matrix.b !== 0) {
|
|
extremeY = math.atan2(matrix.d * ry, matrix.b * rx);
|
|
}
|
|
}
|
|
return {
|
|
x: extremeX,
|
|
y: extremeY
|
|
};
|
|
}
|
|
function bboxStartAngle(angle, start) {
|
|
while (angle < start) {
|
|
angle += 90;
|
|
}
|
|
return angle;
|
|
}
|
|
function defineAccessors(fn, fields) {
|
|
for (var i = 0; i < fields.length; i++) {
|
|
var name = fields[i];
|
|
var capitalized = name.charAt(0).toUpperCase() + name.substring(1, name.length);
|
|
fn['set' + capitalized] = setAccessor(name);
|
|
fn['get' + capitalized] = getAccessor(name);
|
|
}
|
|
}
|
|
function setAccessor(field) {
|
|
return function (value) {
|
|
if (this[field] !== value) {
|
|
this[field] = value;
|
|
this.geometryChange();
|
|
}
|
|
return this;
|
|
};
|
|
}
|
|
function getAccessor(field) {
|
|
return function () {
|
|
return this[field];
|
|
};
|
|
}
|
|
function elipseAngle(start, end, swipe) {
|
|
if (start > end) {
|
|
end += 360;
|
|
}
|
|
var alpha = math.abs(end - start);
|
|
if (!swipe) {
|
|
alpha = 360 - alpha;
|
|
}
|
|
return alpha;
|
|
}
|
|
function calculateAngle(cx, cy, rx, ry, x, y) {
|
|
var cos = round((x - cx) / rx, 3);
|
|
var sin = round((y - cy) / ry, 3);
|
|
return round(deg(math.atan2(sin, cos)));
|
|
}
|
|
function normalizeArcParameters(x1, y1, x2, y2, rx, ry, largeArc, swipe) {
|
|
var cx, cy;
|
|
var cx1, cy1;
|
|
var a, b, c, sqrt;
|
|
if (y1 !== y2) {
|
|
var x21 = x2 - x1;
|
|
var y21 = y2 - y1;
|
|
var rx2 = pow(rx, 2), ry2 = pow(ry, 2);
|
|
var k = (ry2 * x21 * (x1 + x2) + rx2 * y21 * (y1 + y2)) / (2 * rx2 * y21);
|
|
var yk2 = k - y2;
|
|
var l = -(x21 * ry2) / (rx2 * y21);
|
|
a = 1 / rx2 + pow(l, 2) / ry2;
|
|
b = 2 * (l * yk2 / ry2 - x2 / rx2);
|
|
c = pow(x2, 2) / rx2 + pow(yk2, 2) / ry2 - 1;
|
|
sqrt = math.sqrt(pow(b, 2) - 4 * a * c);
|
|
cx = (-b - sqrt) / (2 * a);
|
|
cy = k + l * cx;
|
|
cx1 = (-b + sqrt) / (2 * a);
|
|
cy1 = k + l * cx1;
|
|
} else if (x1 !== x2) {
|
|
b = -2 * y2;
|
|
c = pow((x2 - x1) * ry / (2 * rx), 2) + pow(y2, 2) - pow(ry, 2);
|
|
sqrt = math.sqrt(pow(b, 2) - 4 * c);
|
|
cx = cx1 = (x1 + x2) / 2;
|
|
cy = (-b - sqrt) / 2;
|
|
cy1 = (-b + sqrt) / 2;
|
|
} else {
|
|
return false;
|
|
}
|
|
var start = calculateAngle(cx, cy, rx, ry, x1, y1);
|
|
var end = calculateAngle(cx, cy, rx, ry, x2, y2);
|
|
var alpha = elipseAngle(start, end, swipe);
|
|
if (largeArc && alpha <= 180 || !largeArc && alpha > 180) {
|
|
cx = cx1;
|
|
cy = cy1;
|
|
start = calculateAngle(cx, cy, rx, ry, x1, y1);
|
|
end = calculateAngle(cx, cy, rx, ry, x2, y2);
|
|
}
|
|
return {
|
|
center: new Point(cx, cy),
|
|
startAngle: start,
|
|
endAngle: end
|
|
};
|
|
}
|
|
deepExtend(kendo, {
|
|
geometry: {
|
|
Arc: Arc,
|
|
Circle: Circle,
|
|
Matrix: Matrix,
|
|
Point: Point,
|
|
Rect: Rect,
|
|
Size: Size,
|
|
Transformation: Transformation,
|
|
transform: transform,
|
|
toMatrix: toMatrix
|
|
}
|
|
});
|
|
kendo.dataviz.geometry = kendo.geometry;
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/core', ['drawing/geometry'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var noop = $.noop, toString = Object.prototype.toString, kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, deepExtend = kendo.deepExtend, util = kendo.util, defined = util.defined;
|
|
var Surface = Widget.extend({
|
|
init: function (element, options) {
|
|
this.options = deepExtend({}, this.options, options);
|
|
Widget.fn.init.call(this, element, this.options);
|
|
this._click = this._handler('click');
|
|
this._mouseenter = this._handler('mouseenter');
|
|
this._mouseleave = this._handler('mouseleave');
|
|
this._visual = new kendo.drawing.Group();
|
|
if (this.options.width) {
|
|
this.element.css('width', this.options.width);
|
|
}
|
|
if (this.options.height) {
|
|
this.element.css('height', this.options.height);
|
|
}
|
|
},
|
|
options: { name: 'Surface' },
|
|
events: [
|
|
'click',
|
|
'mouseenter',
|
|
'mouseleave',
|
|
'resize'
|
|
],
|
|
draw: function (element) {
|
|
this._visual.children.push(element);
|
|
},
|
|
clear: function () {
|
|
this._visual.children = [];
|
|
},
|
|
destroy: function () {
|
|
this._visual = null;
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
exportVisual: function () {
|
|
return this._visual;
|
|
},
|
|
getSize: function () {
|
|
return {
|
|
width: this.element.width(),
|
|
height: this.element.height()
|
|
};
|
|
},
|
|
setSize: function (size) {
|
|
this.element.css({
|
|
width: size.width,
|
|
height: size.height
|
|
});
|
|
this._size = size;
|
|
this._resize();
|
|
},
|
|
eventTarget: function (e) {
|
|
var domNode = $(e.touch ? e.touch.initialTouch : e.target);
|
|
var node;
|
|
while (!node && domNode.length > 0) {
|
|
node = domNode[0]._kendoNode;
|
|
if (domNode.is(this.element) || domNode.length === 0) {
|
|
break;
|
|
}
|
|
domNode = domNode.parent();
|
|
}
|
|
if (node) {
|
|
return node.srcElement;
|
|
}
|
|
},
|
|
_resize: noop,
|
|
_handler: function (event) {
|
|
var surface = this;
|
|
return function (e) {
|
|
var node = surface.eventTarget(e);
|
|
if (node) {
|
|
surface.trigger(event, {
|
|
element: node,
|
|
originalEvent: e
|
|
});
|
|
}
|
|
};
|
|
}
|
|
});
|
|
kendo.ui.plugin(Surface);
|
|
Surface.create = function (element, options) {
|
|
return SurfaceFactory.current.create(element, options);
|
|
};
|
|
var BaseNode = Class.extend({
|
|
init: function (srcElement) {
|
|
this.childNodes = [];
|
|
this.parent = null;
|
|
if (srcElement) {
|
|
this.srcElement = srcElement;
|
|
this.observe();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
if (this.srcElement) {
|
|
this.srcElement.removeObserver(this);
|
|
}
|
|
var children = this.childNodes;
|
|
for (var i = 0; i < children.length; i++) {
|
|
this.childNodes[i].destroy();
|
|
}
|
|
this.parent = null;
|
|
},
|
|
load: noop,
|
|
observe: function () {
|
|
if (this.srcElement) {
|
|
this.srcElement.addObserver(this);
|
|
}
|
|
},
|
|
append: function (node) {
|
|
this.childNodes.push(node);
|
|
node.parent = this;
|
|
},
|
|
insertAt: function (node, pos) {
|
|
this.childNodes.splice(pos, 0, node);
|
|
node.parent = this;
|
|
},
|
|
remove: function (index, count) {
|
|
var end = index + count;
|
|
for (var i = index; i < end; i++) {
|
|
this.childNodes[i].removeSelf();
|
|
}
|
|
this.childNodes.splice(index, count);
|
|
},
|
|
removeSelf: function () {
|
|
this.clear();
|
|
this.destroy();
|
|
},
|
|
clear: function () {
|
|
this.remove(0, this.childNodes.length);
|
|
},
|
|
invalidate: function () {
|
|
if (this.parent) {
|
|
this.parent.invalidate();
|
|
}
|
|
},
|
|
geometryChange: function () {
|
|
this.invalidate();
|
|
},
|
|
optionsChange: function () {
|
|
this.invalidate();
|
|
},
|
|
childrenChange: function (e) {
|
|
if (e.action === 'add') {
|
|
this.load(e.items, e.index);
|
|
} else if (e.action === 'remove') {
|
|
this.remove(e.index, e.items.length);
|
|
}
|
|
this.invalidate();
|
|
}
|
|
});
|
|
var OptionsStore = Class.extend({
|
|
init: function (options, prefix) {
|
|
var field, member;
|
|
this.prefix = prefix || '';
|
|
for (field in options) {
|
|
member = options[field];
|
|
member = this._wrap(member, field);
|
|
this[field] = member;
|
|
}
|
|
},
|
|
get: function (field) {
|
|
return kendo.getter(field, true)(this);
|
|
},
|
|
set: function (field, value) {
|
|
var current = kendo.getter(field, true)(this);
|
|
if (current !== value) {
|
|
var composite = this._set(field, this._wrap(value, field));
|
|
if (!composite) {
|
|
this.optionsChange({
|
|
field: this.prefix + field,
|
|
value: value
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_set: function (field, value) {
|
|
var composite = field.indexOf('.') >= 0;
|
|
if (composite) {
|
|
var parts = field.split('.'), path = '', obj;
|
|
while (parts.length > 1) {
|
|
path += parts.shift();
|
|
obj = kendo.getter(path, true)(this);
|
|
if (!obj) {
|
|
obj = new OptionsStore({}, path + '.');
|
|
obj.addObserver(this);
|
|
this[path] = obj;
|
|
}
|
|
if (obj instanceof OptionsStore) {
|
|
obj.set(parts.join('.'), value);
|
|
return composite;
|
|
}
|
|
path += '.';
|
|
}
|
|
}
|
|
this._clear(field);
|
|
kendo.setter(field)(this, value);
|
|
return composite;
|
|
},
|
|
_clear: function (field) {
|
|
var current = kendo.getter(field, true)(this);
|
|
if (current && current.removeObserver) {
|
|
current.removeObserver(this);
|
|
}
|
|
},
|
|
_wrap: function (object, field) {
|
|
var type = toString.call(object);
|
|
if (object !== null && defined(object) && type === '[object Object]') {
|
|
if (!(object instanceof OptionsStore) && !(object instanceof Class)) {
|
|
object = new OptionsStore(object, this.prefix + field + '.');
|
|
}
|
|
object.addObserver(this);
|
|
}
|
|
return object;
|
|
}
|
|
});
|
|
deepExtend(OptionsStore.fn, kendo.mixins.ObserversMixin);
|
|
var SurfaceFactory = function () {
|
|
this._items = [];
|
|
};
|
|
SurfaceFactory.prototype = {
|
|
register: function (name, type, order) {
|
|
var items = this._items, first = items[0], entry = {
|
|
name: name,
|
|
type: type,
|
|
order: order
|
|
};
|
|
if (!first || order < first.order) {
|
|
items.unshift(entry);
|
|
} else {
|
|
items.push(entry);
|
|
}
|
|
},
|
|
create: function (element, options) {
|
|
var items = this._items, match = items[0];
|
|
if (options && options.type) {
|
|
var preferred = options.type.toLowerCase();
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (items[i].name === preferred) {
|
|
match = items[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (match) {
|
|
return new match.type(element, options);
|
|
}
|
|
kendo.logToConsole('Warning: Unable to create Kendo UI Drawing Surface. Possible causes:\n' + '- The browser does not support SVG, VML and Canvas. User agent: ' + navigator.userAgent + '\n' + '- The Kendo UI scripts are not fully loaded');
|
|
}
|
|
};
|
|
SurfaceFactory.current = new SurfaceFactory();
|
|
deepExtend(kendo, {
|
|
drawing: {
|
|
DASH_ARRAYS: {
|
|
dot: [
|
|
1.5,
|
|
3.5
|
|
],
|
|
dash: [
|
|
4,
|
|
3.5
|
|
],
|
|
longdash: [
|
|
8,
|
|
3.5
|
|
],
|
|
dashdot: [
|
|
3.5,
|
|
3.5,
|
|
1.5,
|
|
3.5
|
|
],
|
|
longdashdot: [
|
|
8,
|
|
3.5,
|
|
1.5,
|
|
3.5
|
|
],
|
|
longdashdotdot: [
|
|
8,
|
|
3.5,
|
|
1.5,
|
|
3.5,
|
|
1.5,
|
|
3.5
|
|
]
|
|
},
|
|
Color: kendo.Color,
|
|
BaseNode: BaseNode,
|
|
OptionsStore: OptionsStore,
|
|
Surface: Surface,
|
|
SurfaceFactory: SurfaceFactory
|
|
}
|
|
});
|
|
kendo.dataviz.drawing = kendo.drawing;
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/mixins', ['drawing/core'], f);
|
|
}(function () {
|
|
(function () {
|
|
var kendo = window.kendo, deepExtend = kendo.deepExtend, defined = kendo.util.defined;
|
|
var GRADIENT = 'gradient';
|
|
var Paintable = {
|
|
extend: function (proto) {
|
|
proto.fill = this.fill;
|
|
proto.stroke = this.stroke;
|
|
},
|
|
fill: function (color, opacity) {
|
|
var options = this.options;
|
|
if (defined(color)) {
|
|
if (color && color.nodeType != GRADIENT) {
|
|
var newFill = { color: color };
|
|
if (defined(opacity)) {
|
|
newFill.opacity = opacity;
|
|
}
|
|
options.set('fill', newFill);
|
|
} else {
|
|
options.set('fill', color);
|
|
}
|
|
return this;
|
|
} else {
|
|
return options.get('fill');
|
|
}
|
|
},
|
|
stroke: function (color, width, opacity) {
|
|
if (defined(color)) {
|
|
this.options.set('stroke.color', color);
|
|
if (defined(width)) {
|
|
this.options.set('stroke.width', width);
|
|
}
|
|
if (defined(opacity)) {
|
|
this.options.set('stroke.opacity', opacity);
|
|
}
|
|
return this;
|
|
} else {
|
|
return this.options.get('stroke');
|
|
}
|
|
}
|
|
};
|
|
var Traversable = {
|
|
extend: function (proto, childrenField) {
|
|
proto.traverse = function (callback) {
|
|
var children = this[childrenField];
|
|
for (var i = 0; i < children.length; i++) {
|
|
var child = children[i];
|
|
if (child.traverse) {
|
|
child.traverse(callback);
|
|
} else {
|
|
callback(child);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
}
|
|
};
|
|
deepExtend(kendo.drawing, {
|
|
mixins: {
|
|
Paintable: Paintable,
|
|
Traversable: Traversable
|
|
}
|
|
});
|
|
}());
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/shapes', [
|
|
'drawing/core',
|
|
'drawing/mixins',
|
|
'util/text-metrics',
|
|
'mixins/observers'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, deepExtend = kendo.deepExtend, g = kendo.geometry, Point = g.Point, Size = g.Size, Matrix = g.Matrix, toMatrix = g.toMatrix, drawing = kendo.drawing, OptionsStore = drawing.OptionsStore, math = Math, pow = math.pow, util = kendo.util, append = util.append, arrayLimits = util.arrayLimits, defined = util.defined, last = util.last, valueOrDefault = util.valueOrDefault, ObserversMixin = kendo.mixins.ObserversMixin, inArray = $.inArray, push = [].push, pop = [].pop, splice = [].splice, shift = [].shift, slice = [].slice, unshift = [].unshift, defId = 1, START = 'start', END = 'end', HORIZONTAL = 'horizontal';
|
|
var Element = Class.extend({
|
|
nodeType: 'Element',
|
|
init: function (options) {
|
|
this._initOptions(options);
|
|
},
|
|
_initOptions: function (options) {
|
|
options = options || {};
|
|
var transform = options.transform;
|
|
var clip = options.clip;
|
|
if (transform) {
|
|
options.transform = g.transform(transform);
|
|
}
|
|
if (clip && !clip.id) {
|
|
clip.id = generateDefinitionId();
|
|
}
|
|
this.options = new OptionsStore(options);
|
|
this.options.addObserver(this);
|
|
},
|
|
transform: function (transform) {
|
|
if (defined(transform)) {
|
|
this.options.set('transform', g.transform(transform));
|
|
} else {
|
|
return this.options.get('transform');
|
|
}
|
|
},
|
|
parentTransform: function () {
|
|
var element = this, transformation, parentMatrix;
|
|
while (element.parent) {
|
|
element = element.parent;
|
|
transformation = element.transform();
|
|
if (transformation) {
|
|
parentMatrix = transformation.matrix().multiplyCopy(parentMatrix || Matrix.unit());
|
|
}
|
|
}
|
|
if (parentMatrix) {
|
|
return g.transform(parentMatrix);
|
|
}
|
|
},
|
|
currentTransform: function (parentTransform) {
|
|
var elementTransform = this.transform(), elementMatrix = toMatrix(elementTransform), parentMatrix, combinedMatrix;
|
|
if (!defined(parentTransform)) {
|
|
parentTransform = this.parentTransform();
|
|
}
|
|
parentMatrix = toMatrix(parentTransform);
|
|
if (elementMatrix && parentMatrix) {
|
|
combinedMatrix = parentMatrix.multiplyCopy(elementMatrix);
|
|
} else {
|
|
combinedMatrix = elementMatrix || parentMatrix;
|
|
}
|
|
if (combinedMatrix) {
|
|
return g.transform(combinedMatrix);
|
|
}
|
|
},
|
|
visible: function (visible) {
|
|
if (defined(visible)) {
|
|
this.options.set('visible', visible);
|
|
return this;
|
|
} else {
|
|
return this.options.get('visible') !== false;
|
|
}
|
|
},
|
|
clip: function (clip) {
|
|
var options = this.options;
|
|
if (defined(clip)) {
|
|
if (clip && !clip.id) {
|
|
clip.id = generateDefinitionId();
|
|
}
|
|
options.set('clip', clip);
|
|
return this;
|
|
} else {
|
|
return options.get('clip');
|
|
}
|
|
},
|
|
opacity: function (value) {
|
|
if (defined(value)) {
|
|
this.options.set('opacity', value);
|
|
return this;
|
|
} else {
|
|
return valueOrDefault(this.options.get('opacity'), 1);
|
|
}
|
|
},
|
|
clippedBBox: function (transformation) {
|
|
var box = this._clippedBBox(transformation);
|
|
if (box) {
|
|
var clip = this.clip();
|
|
return clip ? g.Rect.intersect(box, clip.bbox(transformation)) : box;
|
|
}
|
|
},
|
|
_clippedBBox: function (transformation) {
|
|
return this.bbox(transformation);
|
|
}
|
|
});
|
|
deepExtend(Element.fn, ObserversMixin);
|
|
var ElementsArray = Class.extend({
|
|
init: function (array) {
|
|
array = array || [];
|
|
this.length = 0;
|
|
this._splice(0, array.length, array);
|
|
},
|
|
elements: function (elements) {
|
|
if (elements) {
|
|
this._splice(0, this.length, elements);
|
|
this._change();
|
|
return this;
|
|
} else {
|
|
return this.slice(0);
|
|
}
|
|
},
|
|
push: function () {
|
|
var elements = arguments;
|
|
var result = push.apply(this, elements);
|
|
this._add(elements);
|
|
return result;
|
|
},
|
|
slice: slice,
|
|
pop: function () {
|
|
var length = this.length;
|
|
var result = pop.apply(this);
|
|
if (length) {
|
|
this._remove([result]);
|
|
}
|
|
return result;
|
|
},
|
|
splice: function (index, howMany) {
|
|
var elements = slice.call(arguments, 2);
|
|
var result = this._splice(index, howMany, elements);
|
|
this._change();
|
|
return result;
|
|
},
|
|
shift: function () {
|
|
var length = this.length;
|
|
var result = shift.apply(this);
|
|
if (length) {
|
|
this._remove([result]);
|
|
}
|
|
return result;
|
|
},
|
|
unshift: function () {
|
|
var elements = arguments;
|
|
var result = unshift.apply(this, elements);
|
|
this._add(elements);
|
|
return result;
|
|
},
|
|
indexOf: function (element) {
|
|
var that = this;
|
|
var idx;
|
|
var length;
|
|
for (idx = 0, length = that.length; idx < length; idx++) {
|
|
if (that[idx] === element) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
_splice: function (index, howMany, elements) {
|
|
var result = splice.apply(this, [
|
|
index,
|
|
howMany
|
|
].concat(elements));
|
|
this._clearObserver(result);
|
|
this._setObserver(elements);
|
|
return result;
|
|
},
|
|
_add: function (elements) {
|
|
this._setObserver(elements);
|
|
this._change();
|
|
},
|
|
_remove: function (elements) {
|
|
this._clearObserver(elements);
|
|
this._change();
|
|
},
|
|
_setObserver: function (elements) {
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
elements[idx].addObserver(this);
|
|
}
|
|
},
|
|
_clearObserver: function (elements) {
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
elements[idx].removeObserver(this);
|
|
}
|
|
},
|
|
_change: function () {
|
|
}
|
|
});
|
|
deepExtend(ElementsArray.fn, ObserversMixin);
|
|
var Group = Element.extend({
|
|
nodeType: 'Group',
|
|
init: function (options) {
|
|
Element.fn.init.call(this, options);
|
|
this.children = [];
|
|
},
|
|
childrenChange: function (action, items, index) {
|
|
this.trigger('childrenChange', {
|
|
action: action,
|
|
items: items,
|
|
index: index
|
|
});
|
|
},
|
|
append: function () {
|
|
append(this.children, arguments);
|
|
this._reparent(arguments, this);
|
|
this.childrenChange('add', arguments);
|
|
return this;
|
|
},
|
|
insert: function (index, element) {
|
|
this.children.splice(index, 0, element);
|
|
element.parent = this;
|
|
this.childrenChange('add', [element], index);
|
|
return this;
|
|
},
|
|
insertAt: function (element, index) {
|
|
return this.insert(index, element);
|
|
},
|
|
remove: function (element) {
|
|
var index = inArray(element, this.children);
|
|
if (index >= 0) {
|
|
this.children.splice(index, 1);
|
|
element.parent = null;
|
|
this.childrenChange('remove', [element], index);
|
|
}
|
|
return this;
|
|
},
|
|
removeAt: function (index) {
|
|
if (0 <= index && index < this.children.length) {
|
|
var element = this.children[index];
|
|
this.children.splice(index, 1);
|
|
element.parent = null;
|
|
this.childrenChange('remove', [element], index);
|
|
}
|
|
return this;
|
|
},
|
|
clear: function () {
|
|
var items = this.children;
|
|
this.children = [];
|
|
this._reparent(items, null);
|
|
this.childrenChange('remove', items, 0);
|
|
return this;
|
|
},
|
|
bbox: function (transformation) {
|
|
return elementsBoundingBox(this.children, true, this.currentTransform(transformation));
|
|
},
|
|
rawBBox: function () {
|
|
return elementsBoundingBox(this.children, false);
|
|
},
|
|
_clippedBBox: function (transformation) {
|
|
return elementsClippedBoundingBox(this.children, this.currentTransform(transformation));
|
|
},
|
|
currentTransform: function (transformation) {
|
|
return Element.fn.currentTransform.call(this, transformation) || null;
|
|
},
|
|
_reparent: function (elements, newParent) {
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var child = elements[i];
|
|
var parent = child.parent;
|
|
if (parent && parent != this && parent.remove) {
|
|
parent.remove(child);
|
|
}
|
|
child.parent = newParent;
|
|
}
|
|
}
|
|
});
|
|
drawing.mixins.Traversable.extend(Group.fn, 'children');
|
|
var Text = Element.extend({
|
|
nodeType: 'Text',
|
|
init: function (content, position, options) {
|
|
Element.fn.init.call(this, options);
|
|
this.content(content);
|
|
this.position(position || new g.Point());
|
|
if (!this.options.font) {
|
|
this.options.font = '12px sans-serif';
|
|
}
|
|
if (!defined(this.options.fill)) {
|
|
this.fill('#000');
|
|
}
|
|
},
|
|
content: function (value) {
|
|
if (defined(value)) {
|
|
this.options.set('content', value);
|
|
return this;
|
|
} else {
|
|
return this.options.get('content');
|
|
}
|
|
},
|
|
measure: function () {
|
|
var metrics = util.measureText(this.content(), { font: this.options.get('font') });
|
|
return metrics;
|
|
},
|
|
rect: function () {
|
|
var size = this.measure();
|
|
var pos = this.position().clone();
|
|
return new g.Rect(pos, [
|
|
size.width,
|
|
size.height
|
|
]);
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
return this.rect().bbox(combinedMatrix);
|
|
},
|
|
rawBBox: function () {
|
|
return this.rect().bbox();
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(Text.fn);
|
|
definePointAccessors(Text.fn, ['position']);
|
|
var Circle = Element.extend({
|
|
nodeType: 'Circle',
|
|
init: function (geometry, options) {
|
|
Element.fn.init.call(this, options);
|
|
this.geometry(geometry || new g.Circle());
|
|
if (!defined(this.options.stroke)) {
|
|
this.stroke('#000');
|
|
}
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
var rect = this._geometry.bbox(combinedMatrix);
|
|
var strokeWidth = this.options.get('stroke.width');
|
|
if (strokeWidth) {
|
|
expandRect(rect, strokeWidth / 2);
|
|
}
|
|
return rect;
|
|
},
|
|
rawBBox: function () {
|
|
return this._geometry.bbox();
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(Circle.fn);
|
|
defineGeometryAccessors(Circle.fn, ['geometry']);
|
|
var Arc = Element.extend({
|
|
nodeType: 'Arc',
|
|
init: function (geometry, options) {
|
|
Element.fn.init.call(this, options);
|
|
this.geometry(geometry || new g.Arc());
|
|
if (!defined(this.options.stroke)) {
|
|
this.stroke('#000');
|
|
}
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
var rect = this.geometry().bbox(combinedMatrix);
|
|
var strokeWidth = this.options.get('stroke.width');
|
|
if (strokeWidth) {
|
|
expandRect(rect, strokeWidth / 2);
|
|
}
|
|
return rect;
|
|
},
|
|
rawBBox: function () {
|
|
return this.geometry().bbox();
|
|
},
|
|
toPath: function () {
|
|
var path = new Path();
|
|
var curvePoints = this.geometry().curvePoints();
|
|
if (curvePoints.length > 0) {
|
|
path.moveTo(curvePoints[0].x, curvePoints[0].y);
|
|
for (var i = 1; i < curvePoints.length; i += 3) {
|
|
path.curveTo(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(Arc.fn);
|
|
defineGeometryAccessors(Arc.fn, ['geometry']);
|
|
var GeometryElementsArray = ElementsArray.extend({
|
|
_change: function () {
|
|
this.geometryChange();
|
|
}
|
|
});
|
|
var Segment = Class.extend({
|
|
init: function (anchor, controlIn, controlOut) {
|
|
this.anchor(anchor || new Point());
|
|
this.controlIn(controlIn);
|
|
this.controlOut(controlOut);
|
|
},
|
|
bboxTo: function (toSegment, matrix) {
|
|
var rect;
|
|
var segmentAnchor = this.anchor().transformCopy(matrix);
|
|
var toSegmentAnchor = toSegment.anchor().transformCopy(matrix);
|
|
if (this.controlOut() && toSegment.controlIn()) {
|
|
rect = this._curveBoundingBox(segmentAnchor, this.controlOut().transformCopy(matrix), toSegment.controlIn().transformCopy(matrix), toSegmentAnchor);
|
|
} else {
|
|
rect = this._lineBoundingBox(segmentAnchor, toSegmentAnchor);
|
|
}
|
|
return rect;
|
|
},
|
|
_lineBoundingBox: function (p1, p2) {
|
|
return g.Rect.fromPoints(p1, p2);
|
|
},
|
|
_curveBoundingBox: function (p1, cp1, cp2, p2) {
|
|
var points = [
|
|
p1,
|
|
cp1,
|
|
cp2,
|
|
p2
|
|
], extremesX = this._curveExtremesFor(points, 'x'), extremesY = this._curveExtremesFor(points, 'y'), xLimits = arrayLimits([
|
|
extremesX.min,
|
|
extremesX.max,
|
|
p1.x,
|
|
p2.x
|
|
]), yLimits = arrayLimits([
|
|
extremesY.min,
|
|
extremesY.max,
|
|
p1.y,
|
|
p2.y
|
|
]);
|
|
return g.Rect.fromPoints(new Point(xLimits.min, yLimits.min), new Point(xLimits.max, yLimits.max));
|
|
},
|
|
_curveExtremesFor: function (points, field) {
|
|
var extremes = this._curveExtremes(points[0][field], points[1][field], points[2][field], points[3][field]);
|
|
return {
|
|
min: this._calculateCurveAt(extremes.min, field, points),
|
|
max: this._calculateCurveAt(extremes.max, field, points)
|
|
};
|
|
},
|
|
_calculateCurveAt: function (t, field, points) {
|
|
var t1 = 1 - t;
|
|
return pow(t1, 3) * points[0][field] + 3 * pow(t1, 2) * t * points[1][field] + 3 * pow(t, 2) * t1 * points[2][field] + pow(t, 3) * points[3][field];
|
|
},
|
|
_curveExtremes: function (x1, x2, x3, x4) {
|
|
var a = x1 - 3 * x2 + 3 * x3 - x4;
|
|
var b = -2 * (x1 - 2 * x2 + x3);
|
|
var c = x1 - x2;
|
|
var sqrt = math.sqrt(b * b - 4 * a * c);
|
|
var t1 = 0;
|
|
var t2 = 1;
|
|
if (a === 0) {
|
|
if (b !== 0) {
|
|
t1 = t2 = -c / b;
|
|
}
|
|
} else if (!isNaN(sqrt)) {
|
|
t1 = (-b + sqrt) / (2 * a);
|
|
t2 = (-b - sqrt) / (2 * a);
|
|
}
|
|
var min = math.max(math.min(t1, t2), 0);
|
|
if (min < 0 || min > 1) {
|
|
min = 0;
|
|
}
|
|
var max = math.min(math.max(t1, t2), 1);
|
|
if (max > 1 || max < 0) {
|
|
max = 1;
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
});
|
|
definePointAccessors(Segment.fn, [
|
|
'anchor',
|
|
'controlIn',
|
|
'controlOut'
|
|
]);
|
|
deepExtend(Segment.fn, ObserversMixin);
|
|
var Path = Element.extend({
|
|
nodeType: 'Path',
|
|
init: function (options) {
|
|
Element.fn.init.call(this, options);
|
|
this.segments = new GeometryElementsArray();
|
|
this.segments.addObserver(this);
|
|
if (!defined(this.options.stroke)) {
|
|
this.stroke('#000');
|
|
if (!defined(this.options.stroke.lineJoin)) {
|
|
this.options.set('stroke.lineJoin', 'miter');
|
|
}
|
|
}
|
|
},
|
|
moveTo: function (x, y) {
|
|
this.suspend();
|
|
this.segments.elements([]);
|
|
this.resume();
|
|
this.lineTo(x, y);
|
|
return this;
|
|
},
|
|
lineTo: function (x, y) {
|
|
var point = defined(y) ? new Point(x, y) : x, segment = new Segment(point);
|
|
this.segments.push(segment);
|
|
return this;
|
|
},
|
|
curveTo: function (controlOut, controlIn, point) {
|
|
if (this.segments.length > 0) {
|
|
var lastSegment = last(this.segments);
|
|
var segment = new Segment(point, controlIn);
|
|
this.suspend();
|
|
lastSegment.controlOut(controlOut);
|
|
this.resume();
|
|
this.segments.push(segment);
|
|
}
|
|
return this;
|
|
},
|
|
arc: function (startAngle, endAngle, radiusX, radiusY, anticlockwise) {
|
|
if (this.segments.length > 0) {
|
|
var lastSegment = last(this.segments);
|
|
var anchor = lastSegment.anchor();
|
|
var start = util.rad(startAngle);
|
|
var center = new Point(anchor.x - radiusX * math.cos(start), anchor.y - radiusY * math.sin(start));
|
|
var arc = new g.Arc(center, {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
radiusX: radiusX,
|
|
radiusY: radiusY,
|
|
anticlockwise: anticlockwise
|
|
});
|
|
this._addArcSegments(arc);
|
|
}
|
|
return this;
|
|
},
|
|
arcTo: function (end, rx, ry, largeArc, swipe) {
|
|
if (this.segments.length > 0) {
|
|
var lastSegment = last(this.segments);
|
|
var anchor = lastSegment.anchor();
|
|
var arc = g.Arc.fromPoints(anchor, end, rx, ry, largeArc, swipe);
|
|
this._addArcSegments(arc);
|
|
}
|
|
return this;
|
|
},
|
|
_addArcSegments: function (arc) {
|
|
this.suspend();
|
|
var curvePoints = arc.curvePoints();
|
|
for (var i = 1; i < curvePoints.length; i += 3) {
|
|
this.curveTo(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
|
|
}
|
|
this.resume();
|
|
this.geometryChange();
|
|
},
|
|
close: function () {
|
|
this.options.closed = true;
|
|
this.geometryChange();
|
|
return this;
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
var boundingBox = this._bbox(combinedMatrix);
|
|
var strokeWidth = this.options.get('stroke.width');
|
|
if (strokeWidth) {
|
|
expandRect(boundingBox, strokeWidth / 2);
|
|
}
|
|
return boundingBox;
|
|
},
|
|
rawBBox: function () {
|
|
return this._bbox();
|
|
},
|
|
_bbox: function (matrix) {
|
|
var segments = this.segments;
|
|
var length = segments.length;
|
|
var boundingBox;
|
|
if (length === 1) {
|
|
var anchor = segments[0].anchor().transformCopy(matrix);
|
|
boundingBox = new g.Rect(anchor, Size.ZERO);
|
|
} else if (length > 0) {
|
|
for (var i = 1; i < length; i++) {
|
|
var segmentBox = segments[i - 1].bboxTo(segments[i], matrix);
|
|
if (boundingBox) {
|
|
boundingBox = g.Rect.union(boundingBox, segmentBox);
|
|
} else {
|
|
boundingBox = segmentBox;
|
|
}
|
|
}
|
|
}
|
|
return boundingBox;
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(Path.fn);
|
|
Path.fromRect = function (rect, options) {
|
|
return new Path(options).moveTo(rect.topLeft()).lineTo(rect.topRight()).lineTo(rect.bottomRight()).lineTo(rect.bottomLeft()).close();
|
|
};
|
|
Path.fromPoints = function (points, options) {
|
|
if (points) {
|
|
var path = new Path(options);
|
|
for (var i = 0; i < points.length; i++) {
|
|
var pt = Point.create(points[i]);
|
|
if (pt) {
|
|
if (i === 0) {
|
|
path.moveTo(pt);
|
|
} else {
|
|
path.lineTo(pt);
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
};
|
|
Path.fromArc = function (arc, options) {
|
|
var path = new Path(options);
|
|
var startAngle = arc.startAngle;
|
|
var start = arc.pointAt(startAngle);
|
|
path.moveTo(start.x, start.y);
|
|
path.arc(startAngle, arc.endAngle, arc.radiusX, arc.radiusY, arc.anticlockwise);
|
|
return path;
|
|
};
|
|
var MultiPath = Element.extend({
|
|
nodeType: 'MultiPath',
|
|
init: function (options) {
|
|
Element.fn.init.call(this, options);
|
|
this.paths = new GeometryElementsArray();
|
|
this.paths.addObserver(this);
|
|
if (!defined(this.options.stroke)) {
|
|
this.stroke('#000');
|
|
}
|
|
},
|
|
moveTo: function (x, y) {
|
|
var path = new Path();
|
|
path.moveTo(x, y);
|
|
this.paths.push(path);
|
|
return this;
|
|
},
|
|
lineTo: function (x, y) {
|
|
if (this.paths.length > 0) {
|
|
last(this.paths).lineTo(x, y);
|
|
}
|
|
return this;
|
|
},
|
|
curveTo: function (controlOut, controlIn, point) {
|
|
if (this.paths.length > 0) {
|
|
last(this.paths).curveTo(controlOut, controlIn, point);
|
|
}
|
|
return this;
|
|
},
|
|
arc: function (startAngle, endAngle, radiusX, radiusY, anticlockwise) {
|
|
if (this.paths.length > 0) {
|
|
last(this.paths).arc(startAngle, endAngle, radiusX, radiusY, anticlockwise);
|
|
}
|
|
return this;
|
|
},
|
|
arcTo: function (end, rx, ry, largeArc, swipe) {
|
|
if (this.paths.length > 0) {
|
|
last(this.paths).arcTo(end, rx, ry, largeArc, swipe);
|
|
}
|
|
return this;
|
|
},
|
|
close: function () {
|
|
if (this.paths.length > 0) {
|
|
last(this.paths).close();
|
|
}
|
|
return this;
|
|
},
|
|
bbox: function (transformation) {
|
|
return elementsBoundingBox(this.paths, true, this.currentTransform(transformation));
|
|
},
|
|
rawBBox: function () {
|
|
return elementsBoundingBox(this.paths, false);
|
|
},
|
|
_clippedBBox: function (transformation) {
|
|
return elementsClippedBoundingBox(this.paths, this.currentTransform(transformation));
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(MultiPath.fn);
|
|
var Image = Element.extend({
|
|
nodeType: 'Image',
|
|
init: function (src, rect, options) {
|
|
Element.fn.init.call(this, options);
|
|
this.src(src);
|
|
this.rect(rect || new g.Rect());
|
|
},
|
|
src: function (value) {
|
|
if (defined(value)) {
|
|
this.options.set('src', value);
|
|
return this;
|
|
} else {
|
|
return this.options.get('src');
|
|
}
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
return this._rect.bbox(combinedMatrix);
|
|
},
|
|
rawBBox: function () {
|
|
return this._rect.bbox();
|
|
}
|
|
});
|
|
defineGeometryAccessors(Image.fn, ['rect']);
|
|
var GradientStop = Class.extend({
|
|
init: function (offset, color, opacity) {
|
|
this.options = new OptionsStore({
|
|
offset: offset,
|
|
color: color,
|
|
opacity: defined(opacity) ? opacity : 1
|
|
});
|
|
this.options.addObserver(this);
|
|
}
|
|
});
|
|
defineOptionsAccessors(GradientStop.fn, [
|
|
'offset',
|
|
'color',
|
|
'opacity'
|
|
]);
|
|
deepExtend(GradientStop.fn, ObserversMixin);
|
|
GradientStop.create = function (arg) {
|
|
if (defined(arg)) {
|
|
var stop;
|
|
if (arg instanceof GradientStop) {
|
|
stop = arg;
|
|
} else if (arg.length > 1) {
|
|
stop = new GradientStop(arg[0], arg[1], arg[2]);
|
|
} else {
|
|
stop = new GradientStop(arg.offset, arg.color, arg.opacity);
|
|
}
|
|
return stop;
|
|
}
|
|
};
|
|
var StopsArray = ElementsArray.extend({
|
|
_change: function () {
|
|
this.optionsChange({ field: 'stops' });
|
|
}
|
|
});
|
|
var Gradient = Class.extend({
|
|
nodeType: 'gradient',
|
|
init: function (options) {
|
|
this.stops = new StopsArray(this._createStops(options.stops));
|
|
this.stops.addObserver(this);
|
|
this._userSpace = options.userSpace;
|
|
this.id = generateDefinitionId();
|
|
},
|
|
userSpace: function (value) {
|
|
if (defined(value)) {
|
|
this._userSpace = value;
|
|
this.optionsChange();
|
|
return this;
|
|
} else {
|
|
return this._userSpace;
|
|
}
|
|
},
|
|
_createStops: function (stops) {
|
|
var result = [];
|
|
var idx;
|
|
stops = stops || [];
|
|
for (idx = 0; idx < stops.length; idx++) {
|
|
result.push(GradientStop.create(stops[idx]));
|
|
}
|
|
return result;
|
|
},
|
|
addStop: function (offset, color, opacity) {
|
|
this.stops.push(new GradientStop(offset, color, opacity));
|
|
},
|
|
removeStop: function (stop) {
|
|
var index = this.stops.indexOf(stop);
|
|
if (index >= 0) {
|
|
this.stops.splice(index, 1);
|
|
}
|
|
}
|
|
});
|
|
deepExtend(Gradient.fn, ObserversMixin, {
|
|
optionsChange: function (e) {
|
|
this.trigger('optionsChange', {
|
|
field: 'gradient' + (e ? '.' + e.field : ''),
|
|
value: this
|
|
});
|
|
},
|
|
geometryChange: function () {
|
|
this.optionsChange();
|
|
}
|
|
});
|
|
var LinearGradient = Gradient.extend({
|
|
init: function (options) {
|
|
options = options || {};
|
|
Gradient.fn.init.call(this, options);
|
|
this.start(options.start || new Point());
|
|
this.end(options.end || new Point(1, 0));
|
|
}
|
|
});
|
|
definePointAccessors(LinearGradient.fn, [
|
|
'start',
|
|
'end'
|
|
]);
|
|
var RadialGradient = Gradient.extend({
|
|
init: function (options) {
|
|
options = options || {};
|
|
Gradient.fn.init.call(this, options);
|
|
this.center(options.center || new Point());
|
|
this._radius = defined(options.radius) ? options.radius : 1;
|
|
this._fallbackFill = options.fallbackFill;
|
|
},
|
|
radius: function (value) {
|
|
if (defined(value)) {
|
|
this._radius = value;
|
|
this.geometryChange();
|
|
return this;
|
|
} else {
|
|
return this._radius;
|
|
}
|
|
},
|
|
fallbackFill: function (value) {
|
|
if (defined(value)) {
|
|
this._fallbackFill = value;
|
|
this.optionsChange();
|
|
return this;
|
|
} else {
|
|
return this._fallbackFill;
|
|
}
|
|
}
|
|
});
|
|
definePointAccessors(RadialGradient.fn, ['center']);
|
|
var Rect = Element.extend({
|
|
nodeType: 'Rect',
|
|
init: function (geometry, options) {
|
|
Element.fn.init.call(this, options);
|
|
this.geometry(geometry || new g.Rect());
|
|
if (!defined(this.options.stroke)) {
|
|
this.stroke('#000');
|
|
}
|
|
},
|
|
bbox: function (transformation) {
|
|
var combinedMatrix = toMatrix(this.currentTransform(transformation));
|
|
var rect = this._geometry.bbox(combinedMatrix);
|
|
var strokeWidth = this.options.get('stroke.width');
|
|
if (strokeWidth) {
|
|
expandRect(rect, strokeWidth / 2);
|
|
}
|
|
return rect;
|
|
},
|
|
rawBBox: function () {
|
|
return this._geometry.bbox();
|
|
}
|
|
});
|
|
drawing.mixins.Paintable.extend(Rect.fn);
|
|
defineGeometryAccessors(Rect.fn, ['geometry']);
|
|
var Layout = Group.extend({
|
|
init: function (rect, options) {
|
|
Group.fn.init.call(this, kendo.deepExtend({}, this._defaults, options));
|
|
this._rect = rect;
|
|
this._fieldMap = {};
|
|
},
|
|
_defaults: {
|
|
alignContent: START,
|
|
justifyContent: START,
|
|
alignItems: START,
|
|
spacing: 0,
|
|
orientation: HORIZONTAL,
|
|
lineSpacing: 0,
|
|
wrap: true
|
|
},
|
|
rect: function (value) {
|
|
if (value) {
|
|
this._rect = value;
|
|
return this;
|
|
} else {
|
|
return this._rect;
|
|
}
|
|
},
|
|
_initMap: function () {
|
|
var options = this.options;
|
|
var fieldMap = this._fieldMap;
|
|
if (options.orientation == HORIZONTAL) {
|
|
fieldMap.sizeField = 'width';
|
|
fieldMap.groupsSizeField = 'height';
|
|
fieldMap.groupAxis = 'x';
|
|
fieldMap.groupsAxis = 'y';
|
|
} else {
|
|
fieldMap.sizeField = 'height';
|
|
fieldMap.groupsSizeField = 'width';
|
|
fieldMap.groupAxis = 'y';
|
|
fieldMap.groupsAxis = 'x';
|
|
}
|
|
},
|
|
reflow: function () {
|
|
if (!this._rect || this.children.length === 0) {
|
|
return;
|
|
}
|
|
this._initMap();
|
|
if (this.options.transform) {
|
|
this.transform(null);
|
|
}
|
|
var options = this.options;
|
|
var fieldMap = this._fieldMap;
|
|
var rect = this._rect;
|
|
var groupOptions = this._initGroups();
|
|
var groups = groupOptions.groups;
|
|
var groupsSize = groupOptions.groupsSize;
|
|
var sizeField = fieldMap.sizeField;
|
|
var groupsSizeField = fieldMap.groupsSizeField;
|
|
var groupAxis = fieldMap.groupAxis;
|
|
var groupsAxis = fieldMap.groupsAxis;
|
|
var groupStart = alignStart(groupsSize, rect, options.alignContent, groupsAxis, groupsSizeField);
|
|
var groupOrigin = new Point();
|
|
var elementOrigin = new Point();
|
|
var size = new g.Size();
|
|
var elementStart, bbox, element, group, groupBox;
|
|
for (var groupIdx = 0; groupIdx < groups.length; groupIdx++) {
|
|
group = groups[groupIdx];
|
|
groupOrigin[groupAxis] = elementStart = alignStart(group.size, rect, options.justifyContent, groupAxis, sizeField);
|
|
groupOrigin[groupsAxis] = groupStart;
|
|
size[sizeField] = group.size;
|
|
size[groupsSizeField] = group.lineSize;
|
|
groupBox = new g.Rect(groupOrigin, size);
|
|
for (var idx = 0; idx < group.bboxes.length; idx++) {
|
|
element = group.elements[idx];
|
|
bbox = group.bboxes[idx];
|
|
elementOrigin[groupAxis] = elementStart;
|
|
elementOrigin[groupsAxis] = alignStart(bbox.size[groupsSizeField], groupBox, options.alignItems, groupsAxis, groupsSizeField);
|
|
translateToPoint(elementOrigin, bbox, element);
|
|
elementStart += bbox.size[sizeField] + options.spacing;
|
|
}
|
|
groupStart += group.lineSize + options.lineSpacing;
|
|
}
|
|
if (!options.wrap && group.size > rect.size[sizeField]) {
|
|
var scale = rect.size[sizeField] / groupBox.size[sizeField];
|
|
var scaledStart = groupBox.topLeft().scale(scale, scale);
|
|
var scaledSize = groupBox.size[groupsSizeField] * scale;
|
|
var newStart = alignStart(scaledSize, rect, options.alignContent, groupsAxis, groupsSizeField);
|
|
var transform = g.transform();
|
|
if (groupAxis === 'x') {
|
|
transform.translate(rect.origin.x - scaledStart.x, newStart - scaledStart.y);
|
|
} else {
|
|
transform.translate(newStart - scaledStart.x, rect.origin.y - scaledStart.y);
|
|
}
|
|
transform.scale(scale, scale);
|
|
this.transform(transform);
|
|
}
|
|
},
|
|
_initGroups: function () {
|
|
var options = this.options;
|
|
var children = this.children;
|
|
var lineSpacing = options.lineSpacing;
|
|
var sizeField = this._fieldMap.sizeField;
|
|
var groupsSize = -lineSpacing;
|
|
var groups = [];
|
|
var group = this._newGroup();
|
|
var addGroup = function () {
|
|
groups.push(group);
|
|
groupsSize += group.lineSize + lineSpacing;
|
|
};
|
|
var bbox, element;
|
|
for (var idx = 0; idx < children.length; idx++) {
|
|
element = children[idx];
|
|
bbox = children[idx].clippedBBox();
|
|
if (element.visible() && bbox) {
|
|
if (options.wrap && group.size + bbox.size[sizeField] + options.spacing > this._rect.size[sizeField]) {
|
|
if (group.bboxes.length === 0) {
|
|
this._addToGroup(group, bbox, element);
|
|
addGroup();
|
|
group = this._newGroup();
|
|
} else {
|
|
addGroup();
|
|
group = this._newGroup();
|
|
this._addToGroup(group, bbox, element);
|
|
}
|
|
} else {
|
|
this._addToGroup(group, bbox, element);
|
|
}
|
|
}
|
|
}
|
|
if (group.bboxes.length) {
|
|
addGroup();
|
|
}
|
|
return {
|
|
groups: groups,
|
|
groupsSize: groupsSize
|
|
};
|
|
},
|
|
_addToGroup: function (group, bbox, element) {
|
|
group.size += bbox.size[this._fieldMap.sizeField] + this.options.spacing;
|
|
group.lineSize = Math.max(bbox.size[this._fieldMap.groupsSizeField], group.lineSize);
|
|
group.bboxes.push(bbox);
|
|
group.elements.push(element);
|
|
},
|
|
_newGroup: function () {
|
|
return {
|
|
lineSize: 0,
|
|
size: -this.options.spacing,
|
|
bboxes: [],
|
|
elements: []
|
|
};
|
|
}
|
|
});
|
|
function elementsBoundingBox(elements, applyTransform, transformation) {
|
|
var boundingBox;
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var element = elements[i];
|
|
if (element.visible()) {
|
|
var elementBoundingBox = applyTransform ? element.bbox(transformation) : element.rawBBox();
|
|
if (elementBoundingBox) {
|
|
if (boundingBox) {
|
|
boundingBox = g.Rect.union(boundingBox, elementBoundingBox);
|
|
} else {
|
|
boundingBox = elementBoundingBox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return boundingBox;
|
|
}
|
|
function elementsClippedBoundingBox(elements, transformation) {
|
|
var boundingBox;
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var element = elements[i];
|
|
if (element.visible()) {
|
|
var elementBoundingBox = element.clippedBBox(transformation);
|
|
if (elementBoundingBox) {
|
|
if (boundingBox) {
|
|
boundingBox = g.Rect.union(boundingBox, elementBoundingBox);
|
|
} else {
|
|
boundingBox = elementBoundingBox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return boundingBox;
|
|
}
|
|
function expandRect(rect, value) {
|
|
rect.origin.x -= value;
|
|
rect.origin.y -= value;
|
|
rect.size.width += value * 2;
|
|
rect.size.height += value * 2;
|
|
}
|
|
function defineGeometryAccessors(fn, names) {
|
|
for (var i = 0; i < names.length; i++) {
|
|
fn[names[i]] = geometryAccessor(names[i]);
|
|
}
|
|
}
|
|
function geometryAccessor(name) {
|
|
var fieldName = '_' + name;
|
|
return function (value) {
|
|
if (defined(value)) {
|
|
this._observerField(fieldName, value);
|
|
this.geometryChange();
|
|
return this;
|
|
} else {
|
|
return this[fieldName];
|
|
}
|
|
};
|
|
}
|
|
function definePointAccessors(fn, names) {
|
|
for (var i = 0; i < names.length; i++) {
|
|
fn[names[i]] = pointAccessor(names[i]);
|
|
}
|
|
}
|
|
function pointAccessor(name) {
|
|
var fieldName = '_' + name;
|
|
return function (value) {
|
|
if (defined(value)) {
|
|
this._observerField(fieldName, Point.create(value));
|
|
this.geometryChange();
|
|
return this;
|
|
} else {
|
|
return this[fieldName];
|
|
}
|
|
};
|
|
}
|
|
function defineOptionsAccessors(fn, names) {
|
|
for (var i = 0; i < names.length; i++) {
|
|
fn[names[i]] = optionsAccessor(names[i]);
|
|
}
|
|
}
|
|
function optionsAccessor(name) {
|
|
return function (value) {
|
|
if (defined(value)) {
|
|
this.options.set(name, value);
|
|
return this;
|
|
} else {
|
|
return this.options.get(name);
|
|
}
|
|
};
|
|
}
|
|
function generateDefinitionId() {
|
|
return 'kdef' + defId++;
|
|
}
|
|
function align(elements, rect, alignment) {
|
|
alignElements(elements, rect, alignment, 'x', 'width');
|
|
}
|
|
function vAlign(elements, rect, alignment) {
|
|
alignElements(elements, rect, alignment, 'y', 'height');
|
|
}
|
|
function stack(elements) {
|
|
stackElements(getStackElements(elements), 'x', 'y', 'width');
|
|
}
|
|
function vStack(elements) {
|
|
stackElements(getStackElements(elements), 'y', 'x', 'height');
|
|
}
|
|
function wrap(elements, rect) {
|
|
return wrapElements(elements, rect, 'x', 'y', 'width');
|
|
}
|
|
function vWrap(elements, rect) {
|
|
return wrapElements(elements, rect, 'y', 'x', 'height');
|
|
}
|
|
function wrapElements(elements, rect, axis, otherAxis, sizeField) {
|
|
var result = [];
|
|
var stacks = getStacks(elements, rect, sizeField);
|
|
var origin = rect.origin.clone();
|
|
var startElement;
|
|
var elementIdx;
|
|
var stack;
|
|
var idx;
|
|
for (idx = 0; idx < stacks.length; idx++) {
|
|
stack = stacks[idx];
|
|
startElement = stack[0];
|
|
origin[otherAxis] = startElement.bbox.origin[otherAxis];
|
|
translateToPoint(origin, startElement.bbox, startElement.element);
|
|
startElement.bbox.origin[axis] = origin[axis];
|
|
stackElements(stack, axis, otherAxis, sizeField);
|
|
result.push([]);
|
|
for (elementIdx = 0; elementIdx < stack.length; elementIdx++) {
|
|
result[idx].push(stack[elementIdx].element);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function fit(element, rect) {
|
|
var bbox = element.clippedBBox();
|
|
var elementSize = bbox.size;
|
|
var rectSize = rect.size;
|
|
if (rectSize.width < elementSize.width || rectSize.height < elementSize.height) {
|
|
var scale = math.min(rectSize.width / elementSize.width, rectSize.height / elementSize.height);
|
|
var transform = element.transform() || g.transform();
|
|
transform.scale(scale, scale);
|
|
element.transform(transform);
|
|
}
|
|
}
|
|
function getStacks(elements, rect, sizeField) {
|
|
var maxSize = rect.size[sizeField];
|
|
var stackSize = 0;
|
|
var stacks = [];
|
|
var stack = [];
|
|
var element;
|
|
var size;
|
|
var bbox;
|
|
var addElementToStack = function () {
|
|
stack.push({
|
|
element: element,
|
|
bbox: bbox
|
|
});
|
|
};
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
element = elements[idx];
|
|
bbox = element.clippedBBox();
|
|
if (bbox) {
|
|
size = bbox.size[sizeField];
|
|
if (stackSize + size > maxSize) {
|
|
if (stack.length) {
|
|
stacks.push(stack);
|
|
stack = [];
|
|
addElementToStack();
|
|
stackSize = size;
|
|
} else {
|
|
addElementToStack();
|
|
stacks.push(stack);
|
|
stack = [];
|
|
stackSize = 0;
|
|
}
|
|
} else {
|
|
addElementToStack();
|
|
stackSize += size;
|
|
}
|
|
}
|
|
}
|
|
if (stack.length) {
|
|
stacks.push(stack);
|
|
}
|
|
return stacks;
|
|
}
|
|
function getStackElements(elements) {
|
|
var stackElements = [];
|
|
var element;
|
|
var bbox;
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
element = elements[idx];
|
|
bbox = element.clippedBBox();
|
|
if (bbox) {
|
|
stackElements.push({
|
|
element: element,
|
|
bbox: bbox
|
|
});
|
|
}
|
|
}
|
|
return stackElements;
|
|
}
|
|
function stackElements(elements, stackAxis, otherAxis, sizeField) {
|
|
if (elements.length > 1) {
|
|
var previousBBox = elements[0].bbox;
|
|
var origin = new Point();
|
|
var element;
|
|
var bbox;
|
|
for (var idx = 1; idx < elements.length; idx++) {
|
|
element = elements[idx].element;
|
|
bbox = elements[idx].bbox;
|
|
origin[stackAxis] = previousBBox.origin[stackAxis] + previousBBox.size[sizeField];
|
|
origin[otherAxis] = bbox.origin[otherAxis];
|
|
translateToPoint(origin, bbox, element);
|
|
bbox.origin[stackAxis] = origin[stackAxis];
|
|
previousBBox = bbox;
|
|
}
|
|
}
|
|
}
|
|
function alignElements(elements, rect, alignment, axis, sizeField) {
|
|
var bbox, point;
|
|
alignment = alignment || 'start';
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
bbox = elements[idx].clippedBBox();
|
|
if (bbox) {
|
|
point = bbox.origin.clone();
|
|
point[axis] = alignStart(bbox.size[sizeField], rect, alignment, axis, sizeField);
|
|
translateToPoint(point, bbox, elements[idx]);
|
|
}
|
|
}
|
|
}
|
|
function alignStart(size, rect, align, axis, sizeField) {
|
|
var start;
|
|
if (align == START) {
|
|
start = rect.origin[axis];
|
|
} else if (align == END) {
|
|
start = rect.origin[axis] + rect.size[sizeField] - size;
|
|
} else {
|
|
start = rect.origin[axis] + (rect.size[sizeField] - size) / 2;
|
|
}
|
|
return start;
|
|
}
|
|
function translate(x, y, element) {
|
|
var transofrm = element.transform() || g.transform();
|
|
var matrix = transofrm.matrix();
|
|
matrix.e += x;
|
|
matrix.f += y;
|
|
transofrm.matrix(matrix);
|
|
element.transform(transofrm);
|
|
}
|
|
function translateToPoint(point, bbox, element) {
|
|
translate(point.x - bbox.origin.x, point.y - bbox.origin.y, element);
|
|
}
|
|
deepExtend(drawing, {
|
|
align: align,
|
|
Arc: Arc,
|
|
Circle: Circle,
|
|
Element: Element,
|
|
ElementsArray: ElementsArray,
|
|
fit: fit,
|
|
Gradient: Gradient,
|
|
GradientStop: GradientStop,
|
|
Group: Group,
|
|
Image: Image,
|
|
Layout: Layout,
|
|
LinearGradient: LinearGradient,
|
|
MultiPath: MultiPath,
|
|
Path: Path,
|
|
RadialGradient: RadialGradient,
|
|
Rect: Rect,
|
|
Segment: Segment,
|
|
stack: stack,
|
|
Text: Text,
|
|
vAlign: vAlign,
|
|
vStack: vStack,
|
|
vWrap: vWrap,
|
|
wrap: wrap
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/parser', ['drawing/shapes'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, drawing = kendo.drawing, geometry = kendo.geometry, Class = kendo.Class, Point = geometry.Point, deepExtend = kendo.deepExtend, trim = $.trim, util = kendo.util, last = util.last;
|
|
var SEGMENT_REGEX = /([a-df-z]{1})([^a-df-z]*)(z)?/gi, SPLIT_REGEX = /[,\s]?([+\-]?(?:\d*\.\d+|\d+)(?:[eE][+\-]?\d+)?)/g, MOVE = 'm', CLOSE = 'z';
|
|
var PathParser = Class.extend({
|
|
parse: function (str, options) {
|
|
var multiPath = new drawing.MultiPath(options);
|
|
var position = new Point();
|
|
var previousCommand;
|
|
str.replace(SEGMENT_REGEX, function (match, element, params, closePath) {
|
|
var command = element.toLowerCase();
|
|
var isRelative = command === element;
|
|
var parameters = parseParameters(trim(params));
|
|
if (command === MOVE) {
|
|
if (isRelative) {
|
|
position.x += parameters[0];
|
|
position.y += parameters[1];
|
|
} else {
|
|
position.x = parameters[0];
|
|
position.y = parameters[1];
|
|
}
|
|
multiPath.moveTo(position.x, position.y);
|
|
if (parameters.length > 2) {
|
|
command = 'l';
|
|
parameters.splice(0, 2);
|
|
}
|
|
}
|
|
if (ShapeMap[command]) {
|
|
ShapeMap[command](multiPath, {
|
|
parameters: parameters,
|
|
position: position,
|
|
isRelative: isRelative,
|
|
previousCommand: previousCommand
|
|
});
|
|
if (closePath && closePath.toLowerCase() === CLOSE) {
|
|
multiPath.close();
|
|
}
|
|
} else if (command !== MOVE) {
|
|
throw new Error('Error while parsing SVG path. Unsupported command: ' + command);
|
|
}
|
|
previousCommand = command;
|
|
});
|
|
return multiPath;
|
|
}
|
|
});
|
|
var ShapeMap = {
|
|
l: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
for (var i = 0; i < parameters.length; i += 2) {
|
|
var point = new Point(parameters[i], parameters[i + 1]);
|
|
if (options.isRelative) {
|
|
point.translateWith(position);
|
|
}
|
|
path.lineTo(point.x, point.y);
|
|
position.x = point.x;
|
|
position.y = point.y;
|
|
}
|
|
},
|
|
c: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
var controlOut, controlIn, point;
|
|
for (var i = 0; i < parameters.length; i += 6) {
|
|
controlOut = new Point(parameters[i], parameters[i + 1]);
|
|
controlIn = new Point(parameters[i + 2], parameters[i + 3]);
|
|
point = new Point(parameters[i + 4], parameters[i + 5]);
|
|
if (options.isRelative) {
|
|
controlIn.translateWith(position);
|
|
controlOut.translateWith(position);
|
|
point.translateWith(position);
|
|
}
|
|
path.curveTo(controlOut, controlIn, point);
|
|
position.x = point.x;
|
|
position.y = point.y;
|
|
}
|
|
},
|
|
v: function (path, options) {
|
|
var value = options.isRelative ? 0 : options.position.x;
|
|
toLineParamaters(options.parameters, true, value);
|
|
this.l(path, options);
|
|
},
|
|
h: function (path, options) {
|
|
var value = options.isRelative ? 0 : options.position.y;
|
|
toLineParamaters(options.parameters, false, value);
|
|
this.l(path, options);
|
|
},
|
|
a: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
for (var i = 0; i < parameters.length; i += 7) {
|
|
var radiusX = parameters[i];
|
|
var radiusY = parameters[i + 1];
|
|
var largeArc = parameters[i + 3];
|
|
var swipe = parameters[i + 4];
|
|
var endPoint = new Point(parameters[i + 5], parameters[i + 6]);
|
|
if (options.isRelative) {
|
|
endPoint.translateWith(position);
|
|
}
|
|
path.arcTo(endPoint, radiusX, radiusY, largeArc, swipe);
|
|
position.x = endPoint.x;
|
|
position.y = endPoint.y;
|
|
}
|
|
},
|
|
s: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
var previousCommand = options.previousCommand;
|
|
var controlOut, endPoint, controlIn, lastControlIn;
|
|
if (previousCommand == 's' || previousCommand == 'c') {
|
|
lastControlIn = last(last(path.paths).segments).controlIn();
|
|
}
|
|
for (var i = 0; i < parameters.length; i += 4) {
|
|
controlIn = new Point(parameters[i], parameters[i + 1]);
|
|
endPoint = new Point(parameters[i + 2], parameters[i + 3]);
|
|
if (options.isRelative) {
|
|
controlIn.translateWith(position);
|
|
endPoint.translateWith(position);
|
|
}
|
|
if (lastControlIn) {
|
|
controlOut = reflectionPoint(lastControlIn, position);
|
|
} else {
|
|
controlOut = position.clone();
|
|
}
|
|
lastControlIn = controlIn;
|
|
path.curveTo(controlOut, controlIn, endPoint);
|
|
position.x = endPoint.x;
|
|
position.y = endPoint.y;
|
|
}
|
|
},
|
|
q: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
var cubicControlPoints, endPoint, controlPoint;
|
|
for (var i = 0; i < parameters.length; i += 4) {
|
|
controlPoint = new Point(parameters[i], parameters[i + 1]);
|
|
endPoint = new Point(parameters[i + 2], parameters[i + 3]);
|
|
if (options.isRelative) {
|
|
controlPoint.translateWith(position);
|
|
endPoint.translateWith(position);
|
|
}
|
|
cubicControlPoints = quadraticToCubicControlPoints(position, controlPoint, endPoint);
|
|
path.curveTo(cubicControlPoints.controlOut, cubicControlPoints.controlIn, endPoint);
|
|
position.x = endPoint.x;
|
|
position.y = endPoint.y;
|
|
}
|
|
},
|
|
t: function (path, options) {
|
|
var parameters = options.parameters;
|
|
var position = options.position;
|
|
var previousCommand = options.previousCommand;
|
|
var cubicControlPoints, controlPoint, endPoint;
|
|
if (previousCommand == 'q' || previousCommand == 't') {
|
|
var lastSegment = last(last(path.paths).segments);
|
|
controlPoint = lastSegment.controlIn().clone().translateWith(position.scaleCopy(-1 / 3)).scale(3 / 2);
|
|
}
|
|
for (var i = 0; i < parameters.length; i += 2) {
|
|
endPoint = new Point(parameters[i], parameters[i + 1]);
|
|
if (options.isRelative) {
|
|
endPoint.translateWith(position);
|
|
}
|
|
if (controlPoint) {
|
|
controlPoint = reflectionPoint(controlPoint, position);
|
|
} else {
|
|
controlPoint = position.clone();
|
|
}
|
|
cubicControlPoints = quadraticToCubicControlPoints(position, controlPoint, endPoint);
|
|
path.curveTo(cubicControlPoints.controlOut, cubicControlPoints.controlIn, endPoint);
|
|
position.x = endPoint.x;
|
|
position.y = endPoint.y;
|
|
}
|
|
}
|
|
};
|
|
function parseParameters(str) {
|
|
var parameters = [];
|
|
str.replace(SPLIT_REGEX, function (match, number) {
|
|
parameters.push(parseFloat(number));
|
|
});
|
|
return parameters;
|
|
}
|
|
function toLineParamaters(parameters, isVertical, value) {
|
|
var insertPosition = isVertical ? 0 : 1;
|
|
for (var i = 0; i < parameters.length; i += 2) {
|
|
parameters.splice(i + insertPosition, 0, value);
|
|
}
|
|
}
|
|
function reflectionPoint(point, center) {
|
|
if (point && center) {
|
|
return center.scaleCopy(2).translate(-point.x, -point.y);
|
|
}
|
|
}
|
|
function quadraticToCubicControlPoints(position, controlPoint, endPoint) {
|
|
var third = 1 / 3;
|
|
controlPoint = controlPoint.clone().scale(2 / 3);
|
|
return {
|
|
controlOut: controlPoint.clone().translateWith(position.scaleCopy(third)),
|
|
controlIn: controlPoint.translateWith(endPoint.scaleCopy(third))
|
|
};
|
|
}
|
|
PathParser.current = new PathParser();
|
|
drawing.Path.parse = function (str, options) {
|
|
return PathParser.current.parse(str, options);
|
|
};
|
|
deepExtend(drawing, { PathParser: PathParser });
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/svg', [
|
|
'drawing/shapes',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var doc = document, kendo = window.kendo, deepExtend = kendo.deepExtend, g = kendo.geometry, d = kendo.drawing, BaseNode = d.BaseNode, util = kendo.util, defined = util.defined, isTransparent = util.isTransparent, renderAttr = util.renderAttr, renderAllAttr = util.renderAllAttr, renderTemplate = util.renderTemplate, inArray = $.inArray;
|
|
var BUTT = 'butt', DASH_ARRAYS = d.DASH_ARRAYS, GRADIENT = 'gradient', NONE = 'none', NS = '.kendo', SOLID = 'solid', SPACE = ' ', SVG_NS = 'http://www.w3.org/2000/svg', TRANSFORM = 'transform', UNDEFINED = 'undefined';
|
|
var Surface = d.Surface.extend({
|
|
init: function (element, options) {
|
|
d.Surface.fn.init.call(this, element, options);
|
|
this._root = new RootNode(this.options);
|
|
renderSVG(this.element[0], this._template(this));
|
|
this._rootElement = this.element[0].firstElementChild;
|
|
alignToScreen(this._rootElement);
|
|
this._root.attachTo(this._rootElement);
|
|
this.element.on('click' + NS, this._click);
|
|
this.element.on('mouseover' + NS, this._mouseenter);
|
|
this.element.on('mouseout' + NS, this._mouseleave);
|
|
this.resize();
|
|
},
|
|
type: 'svg',
|
|
destroy: function () {
|
|
if (this._root) {
|
|
this._root.destroy();
|
|
this._root = null;
|
|
this._rootElement = null;
|
|
this.element.off(NS);
|
|
}
|
|
d.Surface.fn.destroy.call(this);
|
|
},
|
|
translate: function (offset) {
|
|
var viewBox = kendo.format('{0} {1} {2} {3}', Math.round(offset.x), Math.round(offset.y), this._size.width, this._size.height);
|
|
this._offset = offset;
|
|
this._rootElement.setAttribute('viewBox', viewBox);
|
|
},
|
|
draw: function (element) {
|
|
d.Surface.fn.draw.call(this, element);
|
|
this._root.load([element]);
|
|
},
|
|
clear: function () {
|
|
d.Surface.fn.clear.call(this);
|
|
this._root.clear();
|
|
},
|
|
svg: function () {
|
|
return '<?xml version=\'1.0\' ?>' + this._template(this);
|
|
},
|
|
exportVisual: function () {
|
|
var visual = this._visual;
|
|
var offset = this._offset;
|
|
if (offset) {
|
|
var wrap = new d.Group();
|
|
wrap.children.push(visual);
|
|
wrap.transform(g.transform().translate(-offset.x, -offset.y));
|
|
visual = wrap;
|
|
}
|
|
return visual;
|
|
},
|
|
_resize: function () {
|
|
if (this._offset) {
|
|
this.translate(this._offset);
|
|
}
|
|
},
|
|
_template: renderTemplate('<svg style=\'width: 100%; height: 100%; overflow: hidden;\' ' + 'xmlns=\'' + SVG_NS + '\' ' + 'xmlns:xlink=\'http://www.w3.org/1999/xlink\' ' + 'version=\'1.1\'>#= d._root.render() #</svg>')
|
|
});
|
|
var Node = BaseNode.extend({
|
|
init: function (srcElement) {
|
|
BaseNode.fn.init.call(this, srcElement);
|
|
this.definitions = {};
|
|
},
|
|
destroy: function () {
|
|
if (this.element) {
|
|
this.element._kendoNode = null;
|
|
this.element = null;
|
|
}
|
|
this.clearDefinitions();
|
|
BaseNode.fn.destroy.call(this);
|
|
},
|
|
load: function (elements, pos) {
|
|
var node = this, element = node.element, childNode, srcElement, children, i;
|
|
for (i = 0; i < elements.length; i++) {
|
|
srcElement = elements[i];
|
|
children = srcElement.children;
|
|
childNode = new nodeMap[srcElement.nodeType](srcElement);
|
|
if (defined(pos)) {
|
|
node.insertAt(childNode, pos);
|
|
} else {
|
|
node.append(childNode);
|
|
}
|
|
childNode.createDefinitions();
|
|
if (children && children.length > 0) {
|
|
childNode.load(children);
|
|
}
|
|
if (element) {
|
|
childNode.attachTo(element, pos);
|
|
}
|
|
}
|
|
},
|
|
root: function () {
|
|
var root = this;
|
|
while (root.parent) {
|
|
root = root.parent;
|
|
}
|
|
return root;
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
var container = doc.createElement('div');
|
|
renderSVG(container, '<svg xmlns=\'' + SVG_NS + '\' version=\'1.1\'>' + this.render() + '</svg>');
|
|
var element = container.firstChild.firstChild;
|
|
if (element) {
|
|
if (defined(pos)) {
|
|
domElement.insertBefore(element, domElement.childNodes[pos] || null);
|
|
} else {
|
|
domElement.appendChild(element);
|
|
}
|
|
this.setElement(element);
|
|
}
|
|
},
|
|
setElement: function (element) {
|
|
var nodes = this.childNodes, childElement, i;
|
|
if (this.element) {
|
|
this.element._kendoNode = null;
|
|
}
|
|
this.element = element;
|
|
this.element._kendoNode = this;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
childElement = element.childNodes[i];
|
|
nodes[i].setElement(childElement);
|
|
}
|
|
},
|
|
clear: function () {
|
|
this.clearDefinitions();
|
|
if (this.element) {
|
|
this.element.innerHTML = '';
|
|
}
|
|
var children = this.childNodes;
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].destroy();
|
|
}
|
|
this.childNodes = [];
|
|
},
|
|
removeSelf: function () {
|
|
if (this.element) {
|
|
var parentNode = this.element.parentNode;
|
|
if (parentNode) {
|
|
parentNode.removeChild(this.element);
|
|
}
|
|
this.element = null;
|
|
}
|
|
BaseNode.fn.removeSelf.call(this);
|
|
},
|
|
template: renderTemplate('#= d.renderChildren() #'),
|
|
render: function () {
|
|
return this.template(this);
|
|
},
|
|
renderChildren: function () {
|
|
var nodes = this.childNodes, output = '', i;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
output += nodes[i].render();
|
|
}
|
|
return output;
|
|
},
|
|
optionsChange: function (e) {
|
|
var field = e.field;
|
|
var value = e.value;
|
|
if (field === 'visible') {
|
|
this.css('display', value ? '' : NONE);
|
|
} else if (DefinitionMap[field] && isDefinition(field, value)) {
|
|
this.updateDefinition(field, value);
|
|
} else if (field === 'opacity') {
|
|
this.attr('opacity', value);
|
|
}
|
|
BaseNode.fn.optionsChange.call(this, e);
|
|
},
|
|
attr: function (name, value) {
|
|
if (this.element) {
|
|
this.element.setAttribute(name, value);
|
|
}
|
|
},
|
|
allAttr: function (attrs) {
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
this.attr(attrs[i][0], attrs[i][1]);
|
|
}
|
|
},
|
|
css: function (name, value) {
|
|
if (this.element) {
|
|
this.element.style[name] = value;
|
|
}
|
|
},
|
|
allCss: function (styles) {
|
|
for (var i = 0; i < styles.length; i++) {
|
|
this.css(styles[i][0], styles[i][1]);
|
|
}
|
|
},
|
|
removeAttr: function (name) {
|
|
if (this.element) {
|
|
this.element.removeAttribute(name);
|
|
}
|
|
},
|
|
mapTransform: function (transform) {
|
|
var attrs = [];
|
|
if (transform) {
|
|
attrs.push([
|
|
TRANSFORM,
|
|
'matrix(' + transform.matrix().toString(6) + ')'
|
|
]);
|
|
}
|
|
return attrs;
|
|
},
|
|
renderTransform: function () {
|
|
return renderAllAttr(this.mapTransform(this.srcElement.transform()));
|
|
},
|
|
transformChange: function (value) {
|
|
if (value) {
|
|
this.allAttr(this.mapTransform(value));
|
|
} else {
|
|
this.removeAttr(TRANSFORM);
|
|
}
|
|
},
|
|
mapStyle: function () {
|
|
var options = this.srcElement.options;
|
|
var style = [[
|
|
'cursor',
|
|
options.cursor
|
|
]];
|
|
if (options.visible === false) {
|
|
style.push([
|
|
'display',
|
|
NONE
|
|
]);
|
|
}
|
|
return style;
|
|
},
|
|
renderStyle: function () {
|
|
return renderAttr('style', util.renderStyle(this.mapStyle(true)));
|
|
},
|
|
renderOpacity: function () {
|
|
return renderAttr('opacity', this.srcElement.options.opacity);
|
|
},
|
|
createDefinitions: function () {
|
|
var srcElement = this.srcElement;
|
|
var definitions = this.definitions;
|
|
var definition, field, options, hasDefinitions;
|
|
if (srcElement) {
|
|
options = srcElement.options;
|
|
for (field in DefinitionMap) {
|
|
definition = options.get(field);
|
|
if (definition && isDefinition(field, definition)) {
|
|
definitions[field] = definition;
|
|
hasDefinitions = true;
|
|
}
|
|
}
|
|
if (hasDefinitions) {
|
|
this.definitionChange({
|
|
action: 'add',
|
|
definitions: definitions
|
|
});
|
|
}
|
|
}
|
|
},
|
|
definitionChange: function (e) {
|
|
if (this.parent) {
|
|
this.parent.definitionChange(e);
|
|
}
|
|
},
|
|
updateDefinition: function (type, value) {
|
|
var definitions = this.definitions;
|
|
var current = definitions[type];
|
|
var attr = DefinitionMap[type];
|
|
var definition = {};
|
|
if (current) {
|
|
definition[type] = current;
|
|
this.definitionChange({
|
|
action: 'remove',
|
|
definitions: definition
|
|
});
|
|
delete definitions[type];
|
|
}
|
|
if (!value) {
|
|
if (current) {
|
|
this.removeAttr(attr);
|
|
}
|
|
} else {
|
|
definition[type] = value;
|
|
this.definitionChange({
|
|
action: 'add',
|
|
definitions: definition
|
|
});
|
|
definitions[type] = value;
|
|
this.attr(attr, refUrl(value.id));
|
|
}
|
|
},
|
|
clearDefinitions: function () {
|
|
var definitions = this.definitions;
|
|
var field;
|
|
for (field in definitions) {
|
|
this.definitionChange({
|
|
action: 'remove',
|
|
definitions: definitions
|
|
});
|
|
this.definitions = {};
|
|
break;
|
|
}
|
|
},
|
|
renderDefinitions: function () {
|
|
return renderAllAttr(this.mapDefinitions());
|
|
},
|
|
mapDefinitions: function () {
|
|
var definitions = this.definitions;
|
|
var attrs = [];
|
|
var field;
|
|
for (field in definitions) {
|
|
attrs.push([
|
|
DefinitionMap[field],
|
|
refUrl(definitions[field].id)
|
|
]);
|
|
}
|
|
return attrs;
|
|
}
|
|
});
|
|
var RootNode = Node.extend({
|
|
init: function (options) {
|
|
Node.fn.init.call(this);
|
|
this.options = options;
|
|
this.defs = new DefinitionNode();
|
|
},
|
|
attachTo: function (domElement) {
|
|
this.element = domElement;
|
|
this.defs.attachTo(domElement.firstElementChild);
|
|
},
|
|
clear: function () {
|
|
BaseNode.fn.clear.call(this);
|
|
},
|
|
template: renderTemplate('#=d.defs.render()##= d.renderChildren() #'),
|
|
definitionChange: function (e) {
|
|
this.defs.definitionChange(e);
|
|
}
|
|
});
|
|
var DefinitionNode = Node.extend({
|
|
init: function () {
|
|
Node.fn.init.call(this);
|
|
this.definitionMap = {};
|
|
},
|
|
attachTo: function (domElement) {
|
|
this.element = domElement;
|
|
},
|
|
template: renderTemplate('<defs>#= d.renderChildren()#</defs>'),
|
|
definitionChange: function (e) {
|
|
var definitions = e.definitions;
|
|
var action = e.action;
|
|
if (action == 'add') {
|
|
this.addDefinitions(definitions);
|
|
} else if (action == 'remove') {
|
|
this.removeDefinitions(definitions);
|
|
}
|
|
},
|
|
createDefinition: function (type, item) {
|
|
var nodeType;
|
|
if (type == 'clip') {
|
|
nodeType = ClipNode;
|
|
} else if (type == 'fill') {
|
|
if (item instanceof d.LinearGradient) {
|
|
nodeType = LinearGradientNode;
|
|
} else if (item instanceof d.RadialGradient) {
|
|
nodeType = RadialGradientNode;
|
|
}
|
|
}
|
|
return new nodeType(item);
|
|
},
|
|
addDefinitions: function (definitions) {
|
|
for (var field in definitions) {
|
|
this.addDefinition(field, definitions[field]);
|
|
}
|
|
},
|
|
addDefinition: function (type, srcElement) {
|
|
var definitionMap = this.definitionMap;
|
|
var id = srcElement.id;
|
|
var element = this.element;
|
|
var node, mapItem;
|
|
mapItem = definitionMap[id];
|
|
if (!mapItem) {
|
|
node = this.createDefinition(type, srcElement);
|
|
definitionMap[id] = {
|
|
element: node,
|
|
count: 1
|
|
};
|
|
this.append(node);
|
|
if (element) {
|
|
node.attachTo(this.element);
|
|
}
|
|
} else {
|
|
mapItem.count++;
|
|
}
|
|
},
|
|
removeDefinitions: function (definitions) {
|
|
for (var field in definitions) {
|
|
this.removeDefinition(definitions[field]);
|
|
}
|
|
},
|
|
removeDefinition: function (srcElement) {
|
|
var definitionMap = this.definitionMap;
|
|
var id = srcElement.id;
|
|
var mapItem;
|
|
mapItem = definitionMap[id];
|
|
if (mapItem) {
|
|
mapItem.count--;
|
|
if (mapItem.count === 0) {
|
|
this.remove(inArray(mapItem.element, this.childNodes), 1);
|
|
delete definitionMap[id];
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var ClipNode = Node.extend({
|
|
init: function (srcElement) {
|
|
Node.fn.init.call(this);
|
|
this.srcElement = srcElement;
|
|
this.id = srcElement.id;
|
|
this.load([srcElement]);
|
|
},
|
|
template: renderTemplate('<clipPath id=\'#=d.id#\'>#= d.renderChildren()#</clipPath>')
|
|
});
|
|
var GroupNode = Node.extend({
|
|
template: renderTemplate('<g#= d.renderTransform() + d.renderStyle() + d.renderOpacity() + d.renderDefinitions()#>#= d.renderChildren() #</g>'),
|
|
optionsChange: function (e) {
|
|
if (e.field == TRANSFORM) {
|
|
this.transformChange(e.value);
|
|
}
|
|
Node.fn.optionsChange.call(this, e);
|
|
}
|
|
});
|
|
var PathNode = Node.extend({
|
|
geometryChange: function () {
|
|
this.attr('d', this.renderData());
|
|
this.invalidate();
|
|
},
|
|
optionsChange: function (e) {
|
|
switch (e.field) {
|
|
case 'fill':
|
|
if (e.value) {
|
|
this.allAttr(this.mapFill(e.value));
|
|
} else {
|
|
this.removeAttr('fill');
|
|
}
|
|
break;
|
|
case 'fill.color':
|
|
this.allAttr(this.mapFill({ color: e.value }));
|
|
break;
|
|
case 'stroke':
|
|
if (e.value) {
|
|
this.allAttr(this.mapStroke(e.value));
|
|
} else {
|
|
this.removeAttr('stroke');
|
|
}
|
|
break;
|
|
case TRANSFORM:
|
|
this.transformChange(e.value);
|
|
break;
|
|
default:
|
|
var name = this.attributeMap[e.field];
|
|
if (name) {
|
|
this.attr(name, e.value);
|
|
}
|
|
break;
|
|
}
|
|
Node.fn.optionsChange.call(this, e);
|
|
},
|
|
attributeMap: {
|
|
'fill.opacity': 'fill-opacity',
|
|
'stroke.color': 'stroke',
|
|
'stroke.width': 'stroke-width',
|
|
'stroke.opacity': 'stroke-opacity'
|
|
},
|
|
content: function () {
|
|
if (this.element) {
|
|
this.element.textContent = this.srcElement.content();
|
|
}
|
|
},
|
|
renderData: function () {
|
|
return this.printPath(this.srcElement);
|
|
},
|
|
printPath: function (path) {
|
|
var segments = path.segments, length = segments.length;
|
|
if (length > 0) {
|
|
var parts = [], output, segmentType, currentType, i;
|
|
for (i = 1; i < length; i++) {
|
|
segmentType = this.segmentType(segments[i - 1], segments[i]);
|
|
if (segmentType !== currentType) {
|
|
currentType = segmentType;
|
|
parts.push(segmentType);
|
|
}
|
|
if (segmentType === 'L') {
|
|
parts.push(this.printPoints(segments[i].anchor()));
|
|
} else {
|
|
parts.push(this.printPoints(segments[i - 1].controlOut(), segments[i].controlIn(), segments[i].anchor()));
|
|
}
|
|
}
|
|
output = 'M' + this.printPoints(segments[0].anchor()) + SPACE + parts.join(SPACE);
|
|
if (path.options.closed) {
|
|
output += 'Z';
|
|
}
|
|
return output;
|
|
}
|
|
},
|
|
printPoints: function () {
|
|
var points = arguments, length = points.length, i, result = [];
|
|
for (i = 0; i < length; i++) {
|
|
result.push(points[i].toString(3));
|
|
}
|
|
return result.join(SPACE);
|
|
},
|
|
segmentType: function (segmentStart, segmentEnd) {
|
|
return segmentStart.controlOut() && segmentEnd.controlIn() ? 'C' : 'L';
|
|
},
|
|
mapStroke: function (stroke) {
|
|
var attrs = [];
|
|
if (stroke && !isTransparent(stroke.color)) {
|
|
attrs.push([
|
|
'stroke',
|
|
stroke.color
|
|
]);
|
|
attrs.push([
|
|
'stroke-width',
|
|
stroke.width
|
|
]);
|
|
attrs.push([
|
|
'stroke-linecap',
|
|
this.renderLinecap(stroke)
|
|
]);
|
|
attrs.push([
|
|
'stroke-linejoin',
|
|
stroke.lineJoin
|
|
]);
|
|
if (defined(stroke.opacity)) {
|
|
attrs.push([
|
|
'stroke-opacity',
|
|
stroke.opacity
|
|
]);
|
|
}
|
|
if (defined(stroke.dashType)) {
|
|
attrs.push([
|
|
'stroke-dasharray',
|
|
this.renderDashType(stroke)
|
|
]);
|
|
}
|
|
} else {
|
|
attrs.push([
|
|
'stroke',
|
|
NONE
|
|
]);
|
|
}
|
|
return attrs;
|
|
},
|
|
renderStroke: function () {
|
|
return renderAllAttr(this.mapStroke(this.srcElement.options.stroke));
|
|
},
|
|
renderDashType: function (stroke) {
|
|
var width = stroke.width || 1, dashType = stroke.dashType;
|
|
if (dashType && dashType != SOLID) {
|
|
var dashArray = DASH_ARRAYS[dashType.toLowerCase()], result = [], i;
|
|
for (i = 0; i < dashArray.length; i++) {
|
|
result.push(dashArray[i] * width);
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
},
|
|
renderLinecap: function (stroke) {
|
|
var dashType = stroke.dashType, lineCap = stroke.lineCap;
|
|
return dashType && dashType != SOLID ? BUTT : lineCap;
|
|
},
|
|
mapFill: function (fill) {
|
|
var attrs = [];
|
|
if (!(fill && fill.nodeType == GRADIENT)) {
|
|
if (fill && !isTransparent(fill.color)) {
|
|
attrs.push([
|
|
'fill',
|
|
fill.color
|
|
]);
|
|
if (defined(fill.opacity)) {
|
|
attrs.push([
|
|
'fill-opacity',
|
|
fill.opacity
|
|
]);
|
|
}
|
|
} else {
|
|
attrs.push([
|
|
'fill',
|
|
NONE
|
|
]);
|
|
}
|
|
}
|
|
return attrs;
|
|
},
|
|
renderFill: function () {
|
|
return renderAllAttr(this.mapFill(this.srcElement.options.fill));
|
|
},
|
|
template: renderTemplate('<path #= d.renderStyle() # #= d.renderOpacity() # ' + '#= kendo.util.renderAttr(\'d\', d.renderData()) # ' + '#= d.renderStroke() # ' + '#= d.renderFill() # ' + '#= d.renderDefinitions() # ' + '#= d.renderTransform() #></path>')
|
|
});
|
|
var ArcNode = PathNode.extend({
|
|
renderData: function () {
|
|
return this.printPath(this.srcElement.toPath());
|
|
}
|
|
});
|
|
var MultiPathNode = PathNode.extend({
|
|
renderData: function () {
|
|
var paths = this.srcElement.paths;
|
|
if (paths.length > 0) {
|
|
var result = [], i;
|
|
for (i = 0; i < paths.length; i++) {
|
|
result.push(this.printPath(paths[i]));
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
}
|
|
});
|
|
var CircleNode = PathNode.extend({
|
|
geometryChange: function () {
|
|
var center = this.center();
|
|
this.attr('cx', center.x);
|
|
this.attr('cy', center.y);
|
|
this.attr('r', this.radius());
|
|
this.invalidate();
|
|
},
|
|
center: function () {
|
|
return this.srcElement.geometry().center;
|
|
},
|
|
radius: function () {
|
|
return this.srcElement.geometry().radius;
|
|
},
|
|
template: renderTemplate('<circle #= d.renderStyle() # #= d.renderOpacity() # ' + 'cx=\'#= d.center().x #\' cy=\'#= d.center().y #\' ' + 'r=\'#= d.radius() #\' ' + '#= d.renderStroke() # ' + '#= d.renderFill() # ' + '#= d.renderDefinitions() # ' + '#= d.renderTransform() # ></circle>')
|
|
});
|
|
var TextNode = PathNode.extend({
|
|
geometryChange: function () {
|
|
var pos = this.pos();
|
|
this.attr('x', pos.x);
|
|
this.attr('y', pos.y);
|
|
this.invalidate();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'font') {
|
|
this.attr('style', util.renderStyle(this.mapStyle()));
|
|
this.geometryChange();
|
|
} else if (e.field === 'content') {
|
|
PathNode.fn.content.call(this, this.srcElement.content());
|
|
}
|
|
PathNode.fn.optionsChange.call(this, e);
|
|
},
|
|
mapStyle: function (encode) {
|
|
var style = PathNode.fn.mapStyle.call(this, encode);
|
|
var font = this.srcElement.options.font;
|
|
if (encode) {
|
|
font = kendo.htmlEncode(font);
|
|
}
|
|
style.push([
|
|
'font',
|
|
font
|
|
]);
|
|
return style;
|
|
},
|
|
pos: function () {
|
|
var pos = this.srcElement.position();
|
|
var size = this.srcElement.measure();
|
|
return pos.clone().setY(pos.y + size.baseline);
|
|
},
|
|
renderContent: function () {
|
|
var content = this.srcElement.content();
|
|
content = decodeEntities(content);
|
|
content = kendo.htmlEncode(content);
|
|
return content;
|
|
},
|
|
template: renderTemplate('<text #= d.renderStyle() # #= d.renderOpacity() # ' + 'x=\'#= this.pos().x #\' y=\'#= this.pos().y #\' ' + '#= d.renderStroke() # ' + '#= d.renderTransform() # ' + '#= d.renderDefinitions() # ' + '#= d.renderFill() #>#= d.renderContent() #</text>')
|
|
});
|
|
var ImageNode = PathNode.extend({
|
|
geometryChange: function () {
|
|
this.allAttr(this.mapPosition());
|
|
this.invalidate();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'src') {
|
|
this.allAttr(this.mapSource());
|
|
}
|
|
PathNode.fn.optionsChange.call(this, e);
|
|
},
|
|
mapPosition: function () {
|
|
var rect = this.srcElement.rect();
|
|
var tl = rect.topLeft();
|
|
return [
|
|
[
|
|
'x',
|
|
tl.x
|
|
],
|
|
[
|
|
'y',
|
|
tl.y
|
|
],
|
|
[
|
|
'width',
|
|
rect.width() + 'px'
|
|
],
|
|
[
|
|
'height',
|
|
rect.height() + 'px'
|
|
]
|
|
];
|
|
},
|
|
renderPosition: function () {
|
|
return renderAllAttr(this.mapPosition());
|
|
},
|
|
mapSource: function (encode) {
|
|
var src = this.srcElement.src();
|
|
if (encode) {
|
|
src = kendo.htmlEncode(src);
|
|
}
|
|
return [[
|
|
'xlink:href',
|
|
src
|
|
]];
|
|
},
|
|
renderSource: function () {
|
|
return renderAllAttr(this.mapSource(true));
|
|
},
|
|
template: renderTemplate('<image preserveAspectRatio=\'none\' #= d.renderStyle() # #= d.renderTransform()# #= d.renderOpacity() # ' + '#= d.renderPosition() # #= d.renderSource() # #= d.renderDefinitions()#>' + '</image>')
|
|
});
|
|
var GradientStopNode = Node.extend({
|
|
template: renderTemplate('<stop #=d.renderOffset()# #=d.renderStyle()# />'),
|
|
renderOffset: function () {
|
|
return renderAttr('offset', this.srcElement.offset());
|
|
},
|
|
mapStyle: function () {
|
|
var srcElement = this.srcElement;
|
|
return [
|
|
[
|
|
'stop-color',
|
|
srcElement.color()
|
|
],
|
|
[
|
|
'stop-opacity',
|
|
srcElement.opacity()
|
|
]
|
|
];
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field == 'offset') {
|
|
this.attr(e.field, e.value);
|
|
} else if (e.field == 'color' || e.field == 'opacity') {
|
|
this.css('stop-' + e.field, e.value);
|
|
}
|
|
}
|
|
});
|
|
var GradientNode = Node.extend({
|
|
init: function (srcElement) {
|
|
Node.fn.init.call(this, srcElement);
|
|
this.id = srcElement.id;
|
|
this.loadStops();
|
|
},
|
|
loadStops: function () {
|
|
var srcElement = this.srcElement;
|
|
var stops = srcElement.stops;
|
|
var element = this.element;
|
|
var stopNode;
|
|
var idx;
|
|
for (idx = 0; idx < stops.length; idx++) {
|
|
stopNode = new GradientStopNode(stops[idx]);
|
|
this.append(stopNode);
|
|
if (element) {
|
|
stopNode.attachTo(element);
|
|
}
|
|
}
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field == 'gradient.stops') {
|
|
BaseNode.fn.clear.call(this);
|
|
this.loadStops();
|
|
} else if (e.field == GRADIENT) {
|
|
this.allAttr(this.mapCoordinates());
|
|
}
|
|
},
|
|
renderCoordinates: function () {
|
|
return renderAllAttr(this.mapCoordinates());
|
|
},
|
|
mapSpace: function () {
|
|
return [
|
|
'gradientUnits',
|
|
this.srcElement.userSpace() ? 'userSpaceOnUse' : 'objectBoundingBox'
|
|
];
|
|
}
|
|
});
|
|
var LinearGradientNode = GradientNode.extend({
|
|
template: renderTemplate('<linearGradient id=\'#=d.id#\' #=d.renderCoordinates()#>' + '#= d.renderChildren()#' + '</linearGradient>'),
|
|
mapCoordinates: function () {
|
|
var srcElement = this.srcElement;
|
|
var start = srcElement.start();
|
|
var end = srcElement.end();
|
|
var attrs = [
|
|
[
|
|
'x1',
|
|
start.x
|
|
],
|
|
[
|
|
'y1',
|
|
start.y
|
|
],
|
|
[
|
|
'x2',
|
|
end.x
|
|
],
|
|
[
|
|
'y2',
|
|
end.y
|
|
],
|
|
this.mapSpace()
|
|
];
|
|
return attrs;
|
|
}
|
|
});
|
|
var RadialGradientNode = GradientNode.extend({
|
|
template: renderTemplate('<radialGradient id=\'#=d.id#\' #=d.renderCoordinates()#>' + '#= d.renderChildren()#' + '</radialGradient>'),
|
|
mapCoordinates: function () {
|
|
var srcElement = this.srcElement;
|
|
var center = srcElement.center();
|
|
var radius = srcElement.radius();
|
|
var attrs = [
|
|
[
|
|
'cx',
|
|
center.x
|
|
],
|
|
[
|
|
'cy',
|
|
center.y
|
|
],
|
|
[
|
|
'r',
|
|
radius
|
|
],
|
|
this.mapSpace()
|
|
];
|
|
return attrs;
|
|
}
|
|
});
|
|
var RectNode = PathNode.extend({
|
|
geometryChange: function () {
|
|
var geometry = this.srcElement.geometry();
|
|
this.attr('x', geometry.origin.x);
|
|
this.attr('y', geometry.origin.y);
|
|
this.attr('width', geometry.size.width);
|
|
this.attr('height', geometry.size.height);
|
|
this.invalidate();
|
|
},
|
|
size: function () {
|
|
return this.srcElement.geometry().size;
|
|
},
|
|
origin: function () {
|
|
return this.srcElement.geometry().origin;
|
|
},
|
|
template: renderTemplate('<rect #= d.renderStyle() # #= d.renderOpacity() # ' + 'x=\'#= d.origin().x #\' y=\'#= d.origin().y #\' ' + 'width=\'#= d.size().width #\' height=\'#= d.size().height #\'' + '#= d.renderStroke() # ' + '#= d.renderFill() # ' + '#= d.renderDefinitions() # ' + '#= d.renderTransform() # />')
|
|
});
|
|
var nodeMap = {
|
|
Group: GroupNode,
|
|
Text: TextNode,
|
|
Path: PathNode,
|
|
MultiPath: MultiPathNode,
|
|
Circle: CircleNode,
|
|
Arc: ArcNode,
|
|
Image: ImageNode,
|
|
Rect: RectNode
|
|
};
|
|
var renderSVG = function (container, svg) {
|
|
container.innerHTML = svg;
|
|
};
|
|
(function () {
|
|
var testFragment = '<svg xmlns=\'' + SVG_NS + '\'></svg>', testContainer = doc.createElement('div'), hasParser = typeof DOMParser != UNDEFINED;
|
|
testContainer.innerHTML = testFragment;
|
|
if (hasParser && testContainer.firstChild.namespaceURI != SVG_NS) {
|
|
renderSVG = function (container, svg) {
|
|
var parser = new DOMParser(), chartDoc = parser.parseFromString(svg, 'text/xml'), importedDoc = doc.adoptNode(chartDoc.documentElement);
|
|
container.innerHTML = '';
|
|
container.appendChild(importedDoc);
|
|
};
|
|
}
|
|
}());
|
|
function alignToScreen(element) {
|
|
var ctm;
|
|
try {
|
|
ctm = element.getScreenCTM ? element.getScreenCTM() : null;
|
|
} catch (e) {
|
|
}
|
|
if (ctm) {
|
|
var left = -ctm.e % 1, top = -ctm.f % 1, style = element.style;
|
|
if (left !== 0 || top !== 0) {
|
|
style.left = left + 'px';
|
|
style.top = top + 'px';
|
|
}
|
|
}
|
|
}
|
|
function baseUrl() {
|
|
var base = document.getElementsByTagName('base')[0], url = '', href = document.location.href, hashIndex = href.indexOf('#');
|
|
if (base && !kendo.support.browser.msie) {
|
|
if (hashIndex !== -1) {
|
|
href = href.substring(0, hashIndex);
|
|
}
|
|
url = href;
|
|
}
|
|
return url;
|
|
}
|
|
function refUrl(id) {
|
|
return 'url(' + baseUrl() + '#' + id + ')';
|
|
}
|
|
function exportGroup(group) {
|
|
var root = new RootNode();
|
|
var bbox = group.clippedBBox();
|
|
if (bbox) {
|
|
var origin = bbox.getOrigin();
|
|
var exportRoot = new d.Group();
|
|
exportRoot.transform(g.transform().translate(-origin.x, -origin.y));
|
|
exportRoot.children.push(group);
|
|
group = exportRoot;
|
|
}
|
|
root.load([group]);
|
|
var svg = '<?xml version=\'1.0\' ?>' + '<svg xmlns=\'' + SVG_NS + '\' ' + 'xmlns:xlink=\'http://www.w3.org/1999/xlink\' ' + 'version=\'1.1\'>' + root.render() + '</svg>';
|
|
root.destroy();
|
|
return svg;
|
|
}
|
|
function exportSVG(group, options) {
|
|
var svg = exportGroup(group);
|
|
if (!options || !options.raw) {
|
|
svg = 'data:image/svg+xml;base64,' + util.encodeBase64(svg);
|
|
}
|
|
return $.Deferred().resolve(svg).promise();
|
|
}
|
|
function isDefinition(type, value) {
|
|
return type == 'clip' || type == 'fill' && (!value || value.nodeType == GRADIENT);
|
|
}
|
|
function decodeEntities(text) {
|
|
if (!text || !text.indexOf || text.indexOf('&') < 0) {
|
|
return text;
|
|
} else {
|
|
var element = decodeEntities._element;
|
|
element.innerHTML = text;
|
|
return element.textContent || element.innerText;
|
|
}
|
|
}
|
|
decodeEntities._element = document.createElement('span');
|
|
var DefinitionMap = {
|
|
clip: 'clip-path',
|
|
fill: 'fill'
|
|
};
|
|
kendo.support.svg = function () {
|
|
return doc.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1');
|
|
}();
|
|
if (kendo.support.svg) {
|
|
d.SurfaceFactory.current.register('svg', Surface, 10);
|
|
}
|
|
deepExtend(d, {
|
|
exportSVG: exportSVG,
|
|
svg: {
|
|
ArcNode: ArcNode,
|
|
CircleNode: CircleNode,
|
|
ClipNode: ClipNode,
|
|
DefinitionNode: DefinitionNode,
|
|
GradientStopNode: GradientStopNode,
|
|
GroupNode: GroupNode,
|
|
ImageNode: ImageNode,
|
|
LinearGradientNode: LinearGradientNode,
|
|
MultiPathNode: MultiPathNode,
|
|
Node: Node,
|
|
PathNode: PathNode,
|
|
RadialGradientNode: RadialGradientNode,
|
|
RectNode: RectNode,
|
|
RootNode: RootNode,
|
|
Surface: Surface,
|
|
TextNode: TextNode,
|
|
_exportGroup: exportGroup
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/canvas', [
|
|
'drawing/shapes',
|
|
'kendo.color'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var doc = document, kendo = window.kendo, deepExtend = kendo.deepExtend, util = kendo.util, defined = util.defined, isTransparent = util.isTransparent, renderTemplate = util.renderTemplate, valueOrDefault = util.valueOrDefault, g = kendo.geometry, d = kendo.drawing, BaseNode = d.BaseNode;
|
|
var BUTT = 'butt', DASH_ARRAYS = d.DASH_ARRAYS, FRAME_DELAY = 1000 / 60, SOLID = 'solid';
|
|
var Surface = d.Surface.extend({
|
|
init: function (element, options) {
|
|
d.Surface.fn.init.call(this, element, options);
|
|
this.element[0].innerHTML = this._template(this);
|
|
var canvas = this.element[0].firstElementChild;
|
|
canvas.width = $(element).width();
|
|
canvas.height = $(element).height();
|
|
this._rootElement = canvas;
|
|
this._root = new RootNode(canvas);
|
|
},
|
|
destroy: function () {
|
|
d.Surface.fn.destroy.call(this);
|
|
if (this._root) {
|
|
this._root.destroy();
|
|
this._root = null;
|
|
}
|
|
},
|
|
type: 'canvas',
|
|
draw: function (element) {
|
|
d.Surface.fn.draw.call(this, element);
|
|
this._root.load([element], undefined, this.options.cors);
|
|
},
|
|
clear: function () {
|
|
d.Surface.fn.clear.call(this);
|
|
this._root.clear();
|
|
},
|
|
image: function () {
|
|
var root = this._root;
|
|
var rootElement = this._rootElement;
|
|
var loadingStates = [];
|
|
root.traverse(function (childNode) {
|
|
if (childNode.loading) {
|
|
loadingStates.push(childNode.loading);
|
|
}
|
|
});
|
|
var defer = $.Deferred();
|
|
$.when.apply($, loadingStates).done(function () {
|
|
root._invalidate();
|
|
try {
|
|
var data = rootElement.toDataURL();
|
|
defer.resolve(data);
|
|
} catch (e) {
|
|
defer.reject(e);
|
|
}
|
|
}).fail(function (e) {
|
|
defer.reject(e);
|
|
});
|
|
return defer.promise();
|
|
},
|
|
_resize: function () {
|
|
this._rootElement.width = this._size.width;
|
|
this._rootElement.height = this._size.height;
|
|
this._root.invalidate();
|
|
},
|
|
_template: renderTemplate('<canvas style=\'width: 100%; height: 100%;\'></canvas>')
|
|
});
|
|
var Node = BaseNode.extend({
|
|
init: function (srcElement) {
|
|
BaseNode.fn.init.call(this, srcElement);
|
|
if (srcElement) {
|
|
this.initClip();
|
|
}
|
|
},
|
|
initClip: function () {
|
|
var clip = this.srcElement.clip();
|
|
if (clip) {
|
|
this.clip = clip;
|
|
clip.addObserver(this);
|
|
}
|
|
},
|
|
clear: function () {
|
|
if (this.srcElement) {
|
|
this.srcElement.removeObserver(this);
|
|
}
|
|
this.clearClip();
|
|
BaseNode.fn.clear.call(this);
|
|
},
|
|
clearClip: function () {
|
|
if (this.clip) {
|
|
this.clip.removeObserver(this);
|
|
delete this.clip;
|
|
}
|
|
},
|
|
setClip: function (ctx) {
|
|
if (this.clip) {
|
|
ctx.beginPath();
|
|
PathNode.fn.renderPoints(ctx, this.clip);
|
|
ctx.clip();
|
|
}
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field == 'clip') {
|
|
this.clearClip();
|
|
this.initClip();
|
|
}
|
|
BaseNode.fn.optionsChange.call(this, e);
|
|
},
|
|
setTransform: function (ctx) {
|
|
if (this.srcElement) {
|
|
var transform = this.srcElement.transform();
|
|
if (transform) {
|
|
ctx.transform.apply(ctx, transform.matrix().toArray(6));
|
|
}
|
|
}
|
|
},
|
|
loadElements: function (elements, pos, cors) {
|
|
var node = this, childNode, srcElement, children, i;
|
|
for (i = 0; i < elements.length; i++) {
|
|
srcElement = elements[i];
|
|
children = srcElement.children;
|
|
childNode = new nodeMap[srcElement.nodeType](srcElement, cors);
|
|
if (children && children.length > 0) {
|
|
childNode.load(children, pos, cors);
|
|
}
|
|
if (defined(pos)) {
|
|
node.insertAt(childNode, pos);
|
|
} else {
|
|
node.append(childNode);
|
|
}
|
|
}
|
|
},
|
|
load: function (elements, pos, cors) {
|
|
this.loadElements(elements, pos, cors);
|
|
this.invalidate();
|
|
},
|
|
setOpacity: function (ctx) {
|
|
if (this.srcElement) {
|
|
var opacity = this.srcElement.opacity();
|
|
if (defined(opacity)) {
|
|
this.globalAlpha(ctx, opacity);
|
|
}
|
|
}
|
|
},
|
|
globalAlpha: function (ctx, value) {
|
|
if (value && ctx.globalAlpha) {
|
|
value *= ctx.globalAlpha;
|
|
}
|
|
ctx.globalAlpha = value;
|
|
},
|
|
visible: function () {
|
|
var src = this.srcElement;
|
|
return !src || src && src.options.visible !== false;
|
|
}
|
|
});
|
|
var GroupNode = Node.extend({
|
|
renderTo: function (ctx) {
|
|
if (!this.visible()) {
|
|
return;
|
|
}
|
|
ctx.save();
|
|
this.setTransform(ctx);
|
|
this.setClip(ctx);
|
|
this.setOpacity(ctx);
|
|
var childNodes = this.childNodes;
|
|
for (var i = 0; i < childNodes.length; i++) {
|
|
var child = childNodes[i];
|
|
if (child.visible()) {
|
|
child.renderTo(ctx);
|
|
}
|
|
}
|
|
ctx.restore();
|
|
}
|
|
});
|
|
d.mixins.Traversable.extend(GroupNode.fn, 'childNodes');
|
|
var RootNode = GroupNode.extend({
|
|
init: function (canvas) {
|
|
GroupNode.fn.init.call(this);
|
|
this.canvas = canvas;
|
|
this.ctx = canvas.getContext('2d');
|
|
var invalidateHandler = $.proxy(this._invalidate, this);
|
|
this.invalidate = kendo.throttle(function () {
|
|
kendo.animationFrame(invalidateHandler);
|
|
}, FRAME_DELAY);
|
|
},
|
|
destroy: function () {
|
|
GroupNode.fn.destroy.call(this);
|
|
this.canvas = null;
|
|
this.ctx = null;
|
|
},
|
|
load: function (elements, pos, cors) {
|
|
this.loadElements(elements, pos, cors);
|
|
this._invalidate();
|
|
},
|
|
_invalidate: function () {
|
|
if (!this.ctx) {
|
|
return;
|
|
}
|
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
this.renderTo(this.ctx);
|
|
}
|
|
});
|
|
d.mixins.Traversable.extend(RootNode.fn, 'childNodes');
|
|
var PathNode = Node.extend({
|
|
renderTo: function (ctx) {
|
|
ctx.save();
|
|
this.setTransform(ctx);
|
|
this.setClip(ctx);
|
|
this.setOpacity(ctx);
|
|
ctx.beginPath();
|
|
this.renderPoints(ctx, this.srcElement);
|
|
this.setLineDash(ctx);
|
|
this.setLineCap(ctx);
|
|
this.setLineJoin(ctx);
|
|
this.setFill(ctx);
|
|
this.setStroke(ctx);
|
|
ctx.restore();
|
|
},
|
|
setFill: function (ctx) {
|
|
var fill = this.srcElement.options.fill;
|
|
var hasFill = false;
|
|
if (fill) {
|
|
if (fill.nodeType == 'gradient') {
|
|
this.setGradientFill(ctx, fill);
|
|
hasFill = true;
|
|
} else if (!isTransparent(fill.color)) {
|
|
ctx.fillStyle = fill.color;
|
|
ctx.save();
|
|
this.globalAlpha(ctx, fill.opacity);
|
|
ctx.fill();
|
|
ctx.restore();
|
|
hasFill = true;
|
|
}
|
|
}
|
|
return hasFill;
|
|
},
|
|
setGradientFill: function (ctx, fill) {
|
|
var bbox = this.srcElement.rawBBox();
|
|
var gradient;
|
|
if (fill instanceof d.LinearGradient) {
|
|
var start = fill.start();
|
|
var end = fill.end();
|
|
gradient = ctx.createLinearGradient(start.x, start.y, end.x, end.y);
|
|
} else if (fill instanceof d.RadialGradient) {
|
|
var center = fill.center();
|
|
gradient = ctx.createRadialGradient(center.x, center.y, 0, center.x, center.y, fill.radius());
|
|
}
|
|
addGradientStops(gradient, fill.stops);
|
|
ctx.save();
|
|
if (!fill.userSpace()) {
|
|
ctx.transform(bbox.width(), 0, 0, bbox.height(), bbox.origin.x, bbox.origin.y);
|
|
}
|
|
ctx.fillStyle = gradient;
|
|
ctx.fill();
|
|
ctx.restore();
|
|
},
|
|
setStroke: function (ctx) {
|
|
var stroke = this.srcElement.options.stroke;
|
|
if (stroke && !isTransparent(stroke.color) && stroke.width > 0) {
|
|
ctx.strokeStyle = stroke.color;
|
|
ctx.lineWidth = valueOrDefault(stroke.width, 1);
|
|
ctx.save();
|
|
this.globalAlpha(ctx, stroke.opacity);
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
return true;
|
|
}
|
|
},
|
|
dashType: function () {
|
|
var stroke = this.srcElement.options.stroke;
|
|
if (stroke && stroke.dashType) {
|
|
return stroke.dashType.toLowerCase();
|
|
}
|
|
},
|
|
setLineDash: function (ctx) {
|
|
var dashType = this.dashType();
|
|
if (dashType && dashType != SOLID) {
|
|
var dashArray = DASH_ARRAYS[dashType];
|
|
if (ctx.setLineDash) {
|
|
ctx.setLineDash(dashArray);
|
|
} else {
|
|
ctx.mozDash = dashArray;
|
|
ctx.webkitLineDash = dashArray;
|
|
}
|
|
}
|
|
},
|
|
setLineCap: function (ctx) {
|
|
var dashType = this.dashType();
|
|
var stroke = this.srcElement.options.stroke;
|
|
if (dashType && dashType !== SOLID) {
|
|
ctx.lineCap = BUTT;
|
|
} else if (stroke && stroke.lineCap) {
|
|
ctx.lineCap = stroke.lineCap;
|
|
}
|
|
},
|
|
setLineJoin: function (ctx) {
|
|
var stroke = this.srcElement.options.stroke;
|
|
if (stroke && stroke.lineJoin) {
|
|
ctx.lineJoin = stroke.lineJoin;
|
|
}
|
|
},
|
|
renderPoints: function (ctx, path) {
|
|
var segments = path.segments;
|
|
if (segments.length === 0) {
|
|
return;
|
|
}
|
|
var seg = segments[0];
|
|
var anchor = seg.anchor();
|
|
ctx.moveTo(anchor.x, anchor.y);
|
|
for (var i = 1; i < segments.length; i++) {
|
|
seg = segments[i];
|
|
anchor = seg.anchor();
|
|
var prevSeg = segments[i - 1];
|
|
var prevOut = prevSeg.controlOut();
|
|
var controlIn = seg.controlIn();
|
|
if (prevOut && controlIn) {
|
|
ctx.bezierCurveTo(prevOut.x, prevOut.y, controlIn.x, controlIn.y, anchor.x, anchor.y);
|
|
} else {
|
|
ctx.lineTo(anchor.x, anchor.y);
|
|
}
|
|
}
|
|
if (path.options.closed) {
|
|
ctx.closePath();
|
|
}
|
|
}
|
|
});
|
|
var MultiPathNode = PathNode.extend({
|
|
renderPoints: function (ctx) {
|
|
var paths = this.srcElement.paths;
|
|
for (var i = 0; i < paths.length; i++) {
|
|
PathNode.fn.renderPoints(ctx, paths[i]);
|
|
}
|
|
}
|
|
});
|
|
var CircleNode = PathNode.extend({
|
|
renderPoints: function (ctx) {
|
|
var geometry = this.srcElement.geometry();
|
|
var c = geometry.center;
|
|
var r = geometry.radius;
|
|
ctx.arc(c.x, c.y, r, 0, Math.PI * 2);
|
|
}
|
|
});
|
|
var ArcNode = PathNode.extend({
|
|
renderPoints: function (ctx) {
|
|
var path = this.srcElement.toPath();
|
|
PathNode.fn.renderPoints.call(this, ctx, path);
|
|
}
|
|
});
|
|
var TextNode = PathNode.extend({
|
|
renderTo: function (ctx) {
|
|
var text = this.srcElement;
|
|
var pos = text.position();
|
|
var size = text.measure();
|
|
ctx.save();
|
|
this.setTransform(ctx);
|
|
this.setClip(ctx);
|
|
this.setOpacity(ctx);
|
|
ctx.beginPath();
|
|
ctx.font = text.options.font;
|
|
if (this.setFill(ctx)) {
|
|
ctx.fillText(text.content(), pos.x, pos.y + size.baseline);
|
|
}
|
|
if (this.setStroke(ctx)) {
|
|
this.setLineDash(ctx);
|
|
ctx.strokeText(text.content(), pos.x, pos.y + size.baseline);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
});
|
|
var ImageNode = PathNode.extend({
|
|
init: function (srcElement, cors) {
|
|
PathNode.fn.init.call(this, srcElement);
|
|
this.onLoad = $.proxy(this.onLoad, this);
|
|
this.onError = $.proxy(this.onError, this);
|
|
this.loading = $.Deferred();
|
|
var img = this.img = new Image();
|
|
if (cors && !/^data:/i.test(srcElement.src())) {
|
|
img.crossOrigin = cors;
|
|
}
|
|
img.src = srcElement.src();
|
|
if (img.complete) {
|
|
this.onLoad();
|
|
} else {
|
|
img.onload = this.onLoad;
|
|
img.onerror = this.onError;
|
|
}
|
|
},
|
|
renderTo: function (ctx) {
|
|
if (this.loading.state() === 'resolved') {
|
|
ctx.save();
|
|
this.setTransform(ctx);
|
|
this.setClip(ctx);
|
|
this.drawImage(ctx);
|
|
ctx.restore();
|
|
}
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'src') {
|
|
this.loading = $.Deferred();
|
|
this.img.src = this.srcElement.src();
|
|
} else {
|
|
PathNode.fn.optionsChange.call(this, e);
|
|
}
|
|
},
|
|
onLoad: function () {
|
|
this.loading.resolve();
|
|
this.invalidate();
|
|
},
|
|
onError: function () {
|
|
this.loading.reject(new Error('Unable to load image \'' + this.img.src + '\'. Check for connectivity and verify CORS headers.'));
|
|
},
|
|
drawImage: function (ctx) {
|
|
var rect = this.srcElement.rect();
|
|
var tl = rect.topLeft();
|
|
ctx.drawImage(this.img, tl.x, tl.y, rect.width(), rect.height());
|
|
}
|
|
});
|
|
var RectNode = PathNode.extend({
|
|
renderPoints: function (ctx) {
|
|
var geometry = this.srcElement.geometry();
|
|
var origin = geometry.origin;
|
|
var size = geometry.size;
|
|
ctx.rect(origin.x, origin.y, size.width, size.height);
|
|
}
|
|
});
|
|
function exportImage(group, options) {
|
|
var defaults = {
|
|
width: '800px',
|
|
height: '600px',
|
|
cors: 'Anonymous'
|
|
};
|
|
var bbox = group.clippedBBox();
|
|
if (bbox) {
|
|
var origin = bbox.getOrigin();
|
|
var exportRoot = new d.Group();
|
|
exportRoot.transform(g.transform().translate(-origin.x, -origin.y));
|
|
exportRoot.children.push(group);
|
|
group = exportRoot;
|
|
var size = bbox.getSize();
|
|
defaults.width = size.width + 'px';
|
|
defaults.height = size.height + 'px';
|
|
}
|
|
options = deepExtend(defaults, options);
|
|
var container = $('<div />').css({
|
|
display: 'none',
|
|
width: options.width,
|
|
height: options.height
|
|
}).appendTo(document.body);
|
|
var surface = new Surface(container, options);
|
|
surface.draw(group);
|
|
var promise = surface.image();
|
|
promise.always(function () {
|
|
surface.destroy();
|
|
container.remove();
|
|
});
|
|
return promise;
|
|
}
|
|
var nodeMap = {
|
|
Group: GroupNode,
|
|
Text: TextNode,
|
|
Path: PathNode,
|
|
MultiPath: MultiPathNode,
|
|
Circle: CircleNode,
|
|
Arc: ArcNode,
|
|
Image: ImageNode,
|
|
Rect: RectNode
|
|
};
|
|
function addGradientStops(gradient, stops) {
|
|
var color, stop, idx;
|
|
for (idx = 0; idx < stops.length; idx++) {
|
|
stop = stops[idx];
|
|
color = kendo.parseColor(stop.color());
|
|
color.a *= stop.opacity();
|
|
gradient.addColorStop(stop.offset(), color.toCssRgba());
|
|
}
|
|
}
|
|
kendo.support.canvas = function () {
|
|
return !!doc.createElement('canvas').getContext;
|
|
}();
|
|
if (kendo.support.canvas) {
|
|
d.SurfaceFactory.current.register('canvas', Surface, 20);
|
|
}
|
|
deepExtend(kendo.drawing, {
|
|
exportImage: exportImage,
|
|
canvas: {
|
|
ArcNode: ArcNode,
|
|
CircleNode: CircleNode,
|
|
GroupNode: GroupNode,
|
|
ImageNode: ImageNode,
|
|
MultiPathNode: MultiPathNode,
|
|
Node: Node,
|
|
PathNode: PathNode,
|
|
RectNode: RectNode,
|
|
RootNode: RootNode,
|
|
Surface: Surface,
|
|
TextNode: TextNode
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/vml', [
|
|
'drawing/shapes',
|
|
'kendo.color'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var doc = document, math = Math, atan2 = math.atan2, ceil = math.ceil, sqrt = math.sqrt, kendo = window.kendo, deepExtend = kendo.deepExtend, noop = $.noop, d = kendo.drawing, BaseNode = d.BaseNode, g = kendo.geometry, toMatrix = g.toMatrix, Color = kendo.Color, util = kendo.util, isTransparent = util.isTransparent, defined = util.defined, deg = util.deg, round = util.round, valueOrDefault = util.valueOrDefault;
|
|
var NONE = 'none', NS = '.kendo', COORDINATE_MULTIPLE = 100, COORDINATE_SIZE = COORDINATE_MULTIPLE * COORDINATE_MULTIPLE, GRADIENT = 'gradient', TRANSFORM_PRECISION = 4;
|
|
var Surface = d.Surface.extend({
|
|
init: function (element, options) {
|
|
d.Surface.fn.init.call(this, element, options);
|
|
enableVML();
|
|
this.element.empty();
|
|
this._root = new RootNode();
|
|
this._root.attachTo(this.element[0]);
|
|
this.element.on('click' + NS, this._click);
|
|
this.element.on('mouseover' + NS, this._mouseenter);
|
|
this.element.on('mouseout' + NS, this._mouseleave);
|
|
},
|
|
type: 'vml',
|
|
destroy: function () {
|
|
if (this._root) {
|
|
this._root.destroy();
|
|
this._root = null;
|
|
this.element.off(NS);
|
|
}
|
|
d.Surface.fn.destroy.call(this);
|
|
},
|
|
draw: function (element) {
|
|
d.Surface.fn.draw.call(this, element);
|
|
this._root.load([element], undefined, null);
|
|
},
|
|
clear: function () {
|
|
d.Surface.fn.clear.call(this);
|
|
this._root.clear();
|
|
}
|
|
});
|
|
var Node = BaseNode.extend({
|
|
init: function (srcElement) {
|
|
BaseNode.fn.init.call(this, srcElement);
|
|
this.createElement();
|
|
this.attachReference();
|
|
},
|
|
observe: noop,
|
|
destroy: function () {
|
|
if (this.element) {
|
|
this.element._kendoNode = null;
|
|
this.element = null;
|
|
}
|
|
BaseNode.fn.destroy.call(this);
|
|
},
|
|
clear: function () {
|
|
if (this.element) {
|
|
this.element.innerHTML = '';
|
|
}
|
|
var children = this.childNodes;
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].destroy();
|
|
}
|
|
this.childNodes = [];
|
|
},
|
|
removeSelf: function () {
|
|
if (this.element) {
|
|
this.element.parentNode.removeChild(this.element);
|
|
this.element = null;
|
|
}
|
|
BaseNode.fn.removeSelf.call(this);
|
|
},
|
|
createElement: function () {
|
|
this.element = doc.createElement('div');
|
|
},
|
|
attachReference: function () {
|
|
this.element._kendoNode = this;
|
|
},
|
|
load: function (elements, pos, transform, opacity) {
|
|
opacity = valueOrDefault(opacity, 1);
|
|
if (this.srcElement) {
|
|
opacity *= valueOrDefault(this.srcElement.options.opacity, 1);
|
|
}
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var srcElement = elements[i];
|
|
var children = srcElement.children;
|
|
var combinedTransform = srcElement.currentTransform(transform);
|
|
var currentOpacity = opacity * valueOrDefault(srcElement.options.opacity, 1);
|
|
var childNode = new nodeMap[srcElement.nodeType](srcElement, combinedTransform, currentOpacity);
|
|
if (children && children.length > 0) {
|
|
childNode.load(children, pos, combinedTransform, opacity);
|
|
}
|
|
if (defined(pos)) {
|
|
this.insertAt(childNode, pos);
|
|
} else {
|
|
this.append(childNode);
|
|
}
|
|
childNode.attachTo(this.element, pos);
|
|
}
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
if (defined(pos)) {
|
|
domElement.insertBefore(this.element, domElement.children[pos] || null);
|
|
} else {
|
|
domElement.appendChild(this.element);
|
|
}
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field == 'visible') {
|
|
this.css('display', e.value !== false ? '' : NONE);
|
|
}
|
|
},
|
|
setStyle: function () {
|
|
this.allCss(this.mapStyle());
|
|
},
|
|
mapStyle: function () {
|
|
var style = [];
|
|
if (this.srcElement && this.srcElement.options.visible === false) {
|
|
style.push([
|
|
'display',
|
|
NONE
|
|
]);
|
|
}
|
|
return style;
|
|
},
|
|
mapOpacityTo: function (attrs, multiplier) {
|
|
var opacity = valueOrDefault(this.opacity, 1);
|
|
opacity *= valueOrDefault(multiplier, 1);
|
|
attrs.push([
|
|
'opacity',
|
|
opacity
|
|
]);
|
|
},
|
|
attr: function (name, value) {
|
|
if (this.element) {
|
|
this.element[name] = value;
|
|
}
|
|
},
|
|
allAttr: function (attrs) {
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
this.attr(attrs[i][0], attrs[i][1]);
|
|
}
|
|
},
|
|
css: function (name, value) {
|
|
if (this.element) {
|
|
this.element.style[name] = value;
|
|
}
|
|
},
|
|
allCss: function (styles) {
|
|
for (var i = 0; i < styles.length; i++) {
|
|
this.css(styles[i][0], styles[i][1]);
|
|
}
|
|
}
|
|
});
|
|
var RootNode = Node.extend({
|
|
createElement: function () {
|
|
Node.fn.createElement.call(this);
|
|
this.allCss([
|
|
[
|
|
'width',
|
|
'100%'
|
|
],
|
|
[
|
|
'height',
|
|
'100%'
|
|
],
|
|
[
|
|
'position',
|
|
'relative'
|
|
],
|
|
[
|
|
'visibility',
|
|
'visible'
|
|
]
|
|
]);
|
|
},
|
|
attachReference: noop
|
|
});
|
|
var ClipObserver = kendo.Class.extend({
|
|
init: function (srcElement, observer) {
|
|
this.srcElement = srcElement;
|
|
this.observer = observer;
|
|
srcElement.addObserver(this);
|
|
},
|
|
geometryChange: function () {
|
|
this.observer.optionsChange({
|
|
field: 'clip',
|
|
value: this.srcElement
|
|
});
|
|
},
|
|
clear: function () {
|
|
this.srcElement.removeObserver(this);
|
|
}
|
|
});
|
|
var ObserverNode = Node.extend({
|
|
init: function (srcElement) {
|
|
Node.fn.init.call(this, srcElement);
|
|
if (srcElement) {
|
|
this.initClip();
|
|
}
|
|
},
|
|
observe: function () {
|
|
BaseNode.fn.observe.call(this);
|
|
},
|
|
mapStyle: function () {
|
|
var style = Node.fn.mapStyle.call(this);
|
|
if (this.srcElement && this.srcElement.clip()) {
|
|
style.push([
|
|
'clip',
|
|
this.clipRect()
|
|
]);
|
|
}
|
|
return style;
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field == 'clip') {
|
|
this.clearClip();
|
|
this.initClip();
|
|
this.setClip();
|
|
}
|
|
Node.fn.optionsChange.call(this, e);
|
|
},
|
|
clear: function () {
|
|
this.clearClip();
|
|
Node.fn.clear.call(this);
|
|
},
|
|
initClip: function () {
|
|
if (this.srcElement.clip()) {
|
|
this.clip = new ClipObserver(this.srcElement.clip(), this);
|
|
this.clip.observer = this;
|
|
}
|
|
},
|
|
clearClip: function () {
|
|
if (this.clip) {
|
|
this.clip.clear();
|
|
this.clip = null;
|
|
this.css('clip', this.clipRect());
|
|
}
|
|
},
|
|
setClip: function () {
|
|
if (this.clip) {
|
|
this.css('clip', this.clipRect());
|
|
}
|
|
},
|
|
clipRect: function () {
|
|
var clipRect = EMPTY_CLIP;
|
|
var clip = this.srcElement.clip();
|
|
if (clip) {
|
|
var bbox = this.clipBBox(clip);
|
|
var topLeft = bbox.topLeft();
|
|
var bottomRight = bbox.bottomRight();
|
|
clipRect = kendo.format('rect({0}px {1}px {2}px {3}px)', topLeft.y, bottomRight.x, bottomRight.y, topLeft.x);
|
|
}
|
|
return clipRect;
|
|
},
|
|
clipBBox: function (clip) {
|
|
var topLeft = this.srcElement.rawBBox().topLeft();
|
|
var clipBBox = clip.rawBBox();
|
|
clipBBox.origin.translate(-topLeft.x, -topLeft.y);
|
|
return clipBBox;
|
|
}
|
|
});
|
|
var GroupNode = ObserverNode.extend({
|
|
createElement: function () {
|
|
Node.fn.createElement.call(this);
|
|
this.setStyle();
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
this.css('display', NONE);
|
|
Node.fn.attachTo.call(this, domElement, pos);
|
|
if (this.srcElement.options.visible !== false) {
|
|
this.css('display', '');
|
|
}
|
|
},
|
|
_attachTo: function (domElement) {
|
|
var frag = document.createDocumentFragment();
|
|
frag.appendChild(this.element);
|
|
domElement.appendChild(frag);
|
|
},
|
|
mapStyle: function () {
|
|
var style = ObserverNode.fn.mapStyle.call(this);
|
|
style.push([
|
|
'position',
|
|
'absolute'
|
|
]);
|
|
style.push([
|
|
'white-space',
|
|
'nowrap'
|
|
]);
|
|
return style;
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'transform') {
|
|
this.refreshTransform();
|
|
}
|
|
if (e.field === 'opacity') {
|
|
this.refreshOpacity();
|
|
}
|
|
ObserverNode.fn.optionsChange.call(this, e);
|
|
},
|
|
refreshTransform: function (transform) {
|
|
var currentTransform = this.srcElement.currentTransform(transform), children = this.childNodes, length = children.length, i;
|
|
this.setClip();
|
|
for (i = 0; i < length; i++) {
|
|
children[i].refreshTransform(currentTransform);
|
|
}
|
|
},
|
|
currentOpacity: function () {
|
|
var opacity = valueOrDefault(this.srcElement.options.opacity, 1);
|
|
if (this.parent && this.parent.currentOpacity) {
|
|
opacity *= this.parent.currentOpacity();
|
|
}
|
|
return opacity;
|
|
},
|
|
refreshOpacity: function () {
|
|
var children = this.childNodes, length = children.length, i;
|
|
var opacity = this.currentOpacity();
|
|
for (i = 0; i < length; i++) {
|
|
children[i].refreshOpacity(opacity);
|
|
}
|
|
},
|
|
initClip: function () {
|
|
ObserverNode.fn.initClip.call(this);
|
|
if (this.clip) {
|
|
var bbox = this.clip.srcElement.bbox(this.srcElement.currentTransform());
|
|
if (bbox) {
|
|
this.css('width', bbox.width() + bbox.origin.x);
|
|
this.css('height', bbox.height() + bbox.origin.y);
|
|
}
|
|
}
|
|
},
|
|
clipBBox: function (clip) {
|
|
return clip.bbox(this.srcElement.currentTransform());
|
|
},
|
|
clearClip: function () {
|
|
ObserverNode.fn.clearClip.call(this);
|
|
}
|
|
});
|
|
var StrokeNode = Node.extend({
|
|
init: function (srcElement, opacity) {
|
|
this.opacity = opacity;
|
|
Node.fn.init.call(this, srcElement);
|
|
},
|
|
createElement: function () {
|
|
this.element = createElementVML('stroke');
|
|
this.setOpacity();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field.indexOf('stroke') === 0) {
|
|
this.setStroke();
|
|
}
|
|
},
|
|
refreshOpacity: function (opacity) {
|
|
this.opacity = opacity;
|
|
this.setStroke();
|
|
},
|
|
setStroke: function () {
|
|
this.allAttr(this.mapStroke());
|
|
},
|
|
setOpacity: function () {
|
|
this.setStroke();
|
|
},
|
|
mapStroke: function () {
|
|
var stroke = this.srcElement.options.stroke;
|
|
var attrs = [];
|
|
if (stroke && !isTransparent(stroke.color) && stroke.width !== 0) {
|
|
attrs.push([
|
|
'on',
|
|
'true'
|
|
]);
|
|
attrs.push([
|
|
'color',
|
|
stroke.color
|
|
]);
|
|
attrs.push([
|
|
'weight',
|
|
(stroke.width || 1) + 'px'
|
|
]);
|
|
this.mapOpacityTo(attrs, stroke.opacity);
|
|
if (defined(stroke.dashType)) {
|
|
attrs.push([
|
|
'dashstyle',
|
|
stroke.dashType
|
|
]);
|
|
}
|
|
if (defined(stroke.lineJoin)) {
|
|
attrs.push([
|
|
'joinstyle',
|
|
stroke.lineJoin
|
|
]);
|
|
}
|
|
if (defined(stroke.lineCap)) {
|
|
var lineCap = stroke.lineCap.toLowerCase();
|
|
if (lineCap === 'butt') {
|
|
lineCap = lineCap === 'butt' ? 'flat' : lineCap;
|
|
}
|
|
attrs.push([
|
|
'endcap',
|
|
lineCap
|
|
]);
|
|
}
|
|
} else {
|
|
attrs.push([
|
|
'on',
|
|
'false'
|
|
]);
|
|
}
|
|
return attrs;
|
|
}
|
|
});
|
|
var FillNode = Node.extend({
|
|
init: function (srcElement, transform, opacity) {
|
|
this.opacity = opacity;
|
|
Node.fn.init.call(this, srcElement);
|
|
},
|
|
createElement: function () {
|
|
this.element = createElementVML('fill');
|
|
this.setFill();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (fillField(e.field)) {
|
|
this.setFill();
|
|
}
|
|
},
|
|
refreshOpacity: function (opacity) {
|
|
this.opacity = opacity;
|
|
this.setOpacity();
|
|
},
|
|
setFill: function () {
|
|
this.allAttr(this.mapFill());
|
|
},
|
|
setOpacity: function () {
|
|
this.setFill();
|
|
},
|
|
attr: function (name, value) {
|
|
var element = this.element;
|
|
if (element) {
|
|
var fields = name.split('.');
|
|
while (fields.length > 1) {
|
|
element = element[fields.shift()];
|
|
}
|
|
element[fields[0]] = value;
|
|
}
|
|
},
|
|
mapFill: function () {
|
|
var fill = this.srcElement.fill();
|
|
var attrs = [[
|
|
'on',
|
|
'false'
|
|
]];
|
|
if (fill) {
|
|
if (fill.nodeType == GRADIENT) {
|
|
attrs = this.mapGradient(fill);
|
|
} else if (!isTransparent(fill.color)) {
|
|
attrs = this.mapFillColor(fill);
|
|
}
|
|
}
|
|
return attrs;
|
|
},
|
|
mapFillColor: function (fill) {
|
|
var attrs = [
|
|
[
|
|
'on',
|
|
'true'
|
|
],
|
|
[
|
|
'color',
|
|
fill.color
|
|
]
|
|
];
|
|
this.mapOpacityTo(attrs, fill.opacity);
|
|
return attrs;
|
|
},
|
|
mapGradient: function (fill) {
|
|
var options = this.srcElement.options;
|
|
var fallbackFill = options.fallbackFill || fill.fallbackFill && fill.fallbackFill();
|
|
var attrs;
|
|
if (fill instanceof d.LinearGradient) {
|
|
attrs = this.mapLinearGradient(fill);
|
|
} else if (fill instanceof d.RadialGradient && fill.supportVML) {
|
|
attrs = this.mapRadialGradient(fill);
|
|
} else if (fallbackFill) {
|
|
attrs = this.mapFillColor(fallbackFill);
|
|
} else {
|
|
attrs = [[
|
|
'on',
|
|
'false'
|
|
]];
|
|
}
|
|
return attrs;
|
|
},
|
|
mapLinearGradient: function (fill) {
|
|
var start = fill.start();
|
|
var end = fill.end();
|
|
var angle = util.deg(atan2(end.y - start.y, end.x - start.x));
|
|
var attrs = [
|
|
[
|
|
'on',
|
|
'true'
|
|
],
|
|
[
|
|
'type',
|
|
GRADIENT
|
|
],
|
|
[
|
|
'focus',
|
|
0
|
|
],
|
|
[
|
|
'method',
|
|
'none'
|
|
],
|
|
[
|
|
'angle',
|
|
270 - angle
|
|
]
|
|
];
|
|
this.addColors(attrs);
|
|
return attrs;
|
|
},
|
|
mapRadialGradient: function (fill) {
|
|
var bbox = this.srcElement.rawBBox();
|
|
var center = fill.center();
|
|
var focusx = (center.x - bbox.origin.x) / bbox.width();
|
|
var focusy = (center.y - bbox.origin.y) / bbox.height();
|
|
var attrs = [
|
|
[
|
|
'on',
|
|
'true'
|
|
],
|
|
[
|
|
'type',
|
|
'gradienttitle'
|
|
],
|
|
[
|
|
'focus',
|
|
'100%'
|
|
],
|
|
[
|
|
'focusposition',
|
|
focusx + ' ' + focusy
|
|
],
|
|
[
|
|
'method',
|
|
'none'
|
|
]
|
|
];
|
|
this.addColors(attrs);
|
|
return attrs;
|
|
},
|
|
addColors: function (attrs) {
|
|
var options = this.srcElement.options;
|
|
var opacity = valueOrDefault(this.opacity, 1);
|
|
var stopColors = [];
|
|
var stops = options.fill.stops;
|
|
var baseColor = options.baseColor;
|
|
var colorsField = this.element.colors ? 'colors.value' : 'colors';
|
|
var color = stopColor(baseColor, stops[0], opacity);
|
|
var color2 = stopColor(baseColor, stops[stops.length - 1], opacity);
|
|
var stop;
|
|
for (var idx = 0; idx < stops.length; idx++) {
|
|
stop = stops[idx];
|
|
stopColors.push(math.round(stop.offset() * 100) + '% ' + stopColor(baseColor, stop, opacity));
|
|
}
|
|
attrs.push([
|
|
colorsField,
|
|
stopColors.join(',')
|
|
], [
|
|
'color',
|
|
color
|
|
], [
|
|
'color2',
|
|
color2
|
|
]);
|
|
}
|
|
});
|
|
var TransformNode = Node.extend({
|
|
init: function (srcElement, transform) {
|
|
this.transform = transform;
|
|
Node.fn.init.call(this, srcElement);
|
|
},
|
|
createElement: function () {
|
|
this.element = createElementVML('skew');
|
|
this.setTransform();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'transform') {
|
|
this.refresh(this.srcElement.currentTransform());
|
|
}
|
|
},
|
|
refresh: function (transform) {
|
|
this.transform = transform;
|
|
this.setTransform();
|
|
},
|
|
transformOrigin: function () {
|
|
return '-0.5,-0.5';
|
|
},
|
|
setTransform: function () {
|
|
this.allAttr(this.mapTransform());
|
|
},
|
|
mapTransform: function () {
|
|
var transform = this.transform;
|
|
var attrs = [], matrix = toMatrix(transform);
|
|
if (matrix) {
|
|
matrix.round(TRANSFORM_PRECISION);
|
|
attrs.push([
|
|
'on',
|
|
'true'
|
|
], [
|
|
'matrix',
|
|
[
|
|
matrix.a,
|
|
matrix.c,
|
|
matrix.b,
|
|
matrix.d,
|
|
0,
|
|
0
|
|
].join(',')
|
|
], [
|
|
'offset',
|
|
matrix.e + 'px,' + matrix.f + 'px'
|
|
], [
|
|
'origin',
|
|
this.transformOrigin()
|
|
]);
|
|
} else {
|
|
attrs.push([
|
|
'on',
|
|
'false'
|
|
]);
|
|
}
|
|
return attrs;
|
|
}
|
|
});
|
|
var ShapeNode = ObserverNode.extend({
|
|
init: function (srcElement, transform, opacity) {
|
|
this.fill = this.createFillNode(srcElement, transform, opacity);
|
|
this.stroke = new StrokeNode(srcElement, opacity);
|
|
this.transform = this.createTransformNode(srcElement, transform);
|
|
ObserverNode.fn.init.call(this, srcElement);
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
this.fill.attachTo(this.element);
|
|
this.stroke.attachTo(this.element);
|
|
this.transform.attachTo(this.element);
|
|
Node.fn.attachTo.call(this, domElement, pos);
|
|
},
|
|
createFillNode: function (srcElement, transform, opacity) {
|
|
return new FillNode(srcElement, transform, opacity);
|
|
},
|
|
createTransformNode: function (srcElement, transform) {
|
|
return new TransformNode(srcElement, transform);
|
|
},
|
|
createElement: function () {
|
|
this.element = createElementVML('shape');
|
|
this.setCoordsize();
|
|
this.setStyle();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (fillField(e.field)) {
|
|
this.fill.optionsChange(e);
|
|
} else if (e.field.indexOf('stroke') === 0) {
|
|
this.stroke.optionsChange(e);
|
|
} else if (e.field === 'transform') {
|
|
this.transform.optionsChange(e);
|
|
} else if (e.field === 'opacity') {
|
|
this.fill.setOpacity();
|
|
this.stroke.setOpacity();
|
|
}
|
|
ObserverNode.fn.optionsChange.call(this, e);
|
|
},
|
|
refreshTransform: function (transform) {
|
|
this.transform.refresh(this.srcElement.currentTransform(transform));
|
|
},
|
|
refreshOpacity: function (opacity) {
|
|
opacity *= valueOrDefault(this.srcElement.options.opacity, 1);
|
|
this.fill.refreshOpacity(opacity);
|
|
this.stroke.refreshOpacity(opacity);
|
|
},
|
|
mapStyle: function (width, height) {
|
|
var styles = ObserverNode.fn.mapStyle.call(this);
|
|
if (!width || !height) {
|
|
width = height = COORDINATE_MULTIPLE;
|
|
}
|
|
styles.push([
|
|
'position',
|
|
'absolute'
|
|
], [
|
|
'width',
|
|
width + 'px'
|
|
], [
|
|
'height',
|
|
height + 'px'
|
|
]);
|
|
var cursor = this.srcElement.options.cursor;
|
|
if (cursor) {
|
|
styles.push([
|
|
'cursor',
|
|
cursor
|
|
]);
|
|
}
|
|
return styles;
|
|
},
|
|
setCoordsize: function () {
|
|
this.allAttr([
|
|
[
|
|
'coordorigin',
|
|
'0 0'
|
|
],
|
|
[
|
|
'coordsize',
|
|
COORDINATE_SIZE + ' ' + COORDINATE_SIZE
|
|
]
|
|
]);
|
|
}
|
|
});
|
|
var PathDataNode = Node.extend({
|
|
createElement: function () {
|
|
this.element = createElementVML('path');
|
|
this.setPathData();
|
|
},
|
|
geometryChange: function () {
|
|
this.setPathData();
|
|
},
|
|
setPathData: function () {
|
|
this.attr('v', this.renderData());
|
|
},
|
|
renderData: function () {
|
|
return printPath(this.srcElement);
|
|
}
|
|
});
|
|
var PathNode = ShapeNode.extend({
|
|
init: function (srcElement, transform, opacity) {
|
|
this.pathData = this.createDataNode(srcElement);
|
|
ShapeNode.fn.init.call(this, srcElement, transform, opacity);
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
this.pathData.attachTo(this.element);
|
|
ShapeNode.fn.attachTo.call(this, domElement, pos);
|
|
},
|
|
createDataNode: function (srcElement) {
|
|
return new PathDataNode(srcElement);
|
|
},
|
|
geometryChange: function () {
|
|
this.pathData.geometryChange();
|
|
ShapeNode.fn.geometryChange.call(this);
|
|
}
|
|
});
|
|
var MultiPathDataNode = PathDataNode.extend({
|
|
renderData: function () {
|
|
var paths = this.srcElement.paths;
|
|
if (paths.length > 0) {
|
|
var result = [], i, open;
|
|
for (i = 0; i < paths.length; i++) {
|
|
open = i < paths.length - 1;
|
|
result.push(printPath(paths[i], open));
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
}
|
|
});
|
|
var MultiPathNode = PathNode.extend({
|
|
createDataNode: function (srcElement) {
|
|
return new MultiPathDataNode(srcElement);
|
|
}
|
|
});
|
|
var CircleTransformNode = TransformNode.extend({
|
|
transformOrigin: function () {
|
|
var boundingBox = this.srcElement.geometry().bbox(), center = boundingBox.center(), originX = -ceil(center.x) / ceil(boundingBox.width()), originY = -ceil(center.y) / ceil(boundingBox.height());
|
|
return originX + ',' + originY;
|
|
}
|
|
});
|
|
var CircleNode = ShapeNode.extend({
|
|
createElement: function () {
|
|
this.element = createElementVML('oval');
|
|
this.setStyle();
|
|
},
|
|
createTransformNode: function (srcElement, transform) {
|
|
return new CircleTransformNode(srcElement, transform);
|
|
},
|
|
geometryChange: function () {
|
|
ShapeNode.fn.geometryChange.call(this);
|
|
this.setStyle();
|
|
this.refreshTransform();
|
|
},
|
|
mapStyle: function () {
|
|
var geometry = this.srcElement.geometry();
|
|
var radius = geometry.radius;
|
|
var center = geometry.center;
|
|
var diameter = ceil(radius * 2);
|
|
var styles = ShapeNode.fn.mapStyle.call(this, diameter, diameter);
|
|
styles.push([
|
|
'left',
|
|
ceil(center.x - radius) + 'px'
|
|
], [
|
|
'top',
|
|
ceil(center.y - radius) + 'px'
|
|
]);
|
|
return styles;
|
|
}
|
|
});
|
|
var ArcDataNode = PathDataNode.extend({
|
|
renderData: function () {
|
|
return printPath(this.srcElement.toPath());
|
|
}
|
|
});
|
|
var ArcNode = PathNode.extend({
|
|
createDataNode: function (srcElement) {
|
|
return new ArcDataNode(srcElement);
|
|
}
|
|
});
|
|
var TextPathDataNode = PathDataNode.extend({
|
|
createElement: function () {
|
|
PathDataNode.fn.createElement.call(this);
|
|
this.attr('textpathok', true);
|
|
},
|
|
renderData: function () {
|
|
var rect = this.srcElement.rect();
|
|
var center = rect.center();
|
|
return 'm ' + printPoints([new g.Point(rect.topLeft().x, center.y)]) + ' l ' + printPoints([new g.Point(rect.bottomRight().x, center.y)]);
|
|
}
|
|
});
|
|
var TextPathNode = Node.extend({
|
|
createElement: function () {
|
|
this.element = createElementVML('textpath');
|
|
this.attr('on', true);
|
|
this.attr('fitpath', false);
|
|
this.setStyle();
|
|
this.setString();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'content') {
|
|
this.setString();
|
|
} else {
|
|
this.setStyle();
|
|
}
|
|
Node.fn.optionsChange.call(this, e);
|
|
},
|
|
mapStyle: function () {
|
|
return [[
|
|
'font',
|
|
this.srcElement.options.font
|
|
]];
|
|
},
|
|
setString: function () {
|
|
this.attr('string', this.srcElement.content());
|
|
}
|
|
});
|
|
var TextNode = PathNode.extend({
|
|
init: function (srcElement, transform, opacity) {
|
|
this.path = new TextPathNode(srcElement);
|
|
PathNode.fn.init.call(this, srcElement, transform, opacity);
|
|
},
|
|
createDataNode: function (srcElement) {
|
|
return new TextPathDataNode(srcElement);
|
|
},
|
|
attachTo: function (domElement, pos) {
|
|
this.path.attachTo(this.element);
|
|
PathNode.fn.attachTo.call(this, domElement, pos);
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'font' || e.field === 'content') {
|
|
this.path.optionsChange(e);
|
|
this.pathData.geometryChange(e);
|
|
}
|
|
PathNode.fn.optionsChange.call(this, e);
|
|
}
|
|
});
|
|
var ImagePathDataNode = PathDataNode.extend({
|
|
renderData: function () {
|
|
var rect = this.srcElement.rect();
|
|
var path = new d.Path().moveTo(rect.topLeft()).lineTo(rect.topRight()).lineTo(rect.bottomRight()).lineTo(rect.bottomLeft()).close();
|
|
return printPath(path);
|
|
}
|
|
});
|
|
var ImageFillNode = TransformNode.extend({
|
|
init: function (srcElement, transform, opacity) {
|
|
this.opacity = opacity;
|
|
TransformNode.fn.init.call(this, srcElement, transform);
|
|
},
|
|
createElement: function () {
|
|
this.element = createElementVML('fill');
|
|
this.attr('type', 'frame');
|
|
this.attr('rotate', true);
|
|
this.setOpacity();
|
|
this.setSrc();
|
|
this.setTransform();
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'src') {
|
|
this.setSrc();
|
|
}
|
|
TransformNode.fn.optionsChange.call(this, e);
|
|
},
|
|
geometryChange: function () {
|
|
this.refresh();
|
|
},
|
|
refreshOpacity: function (opacity) {
|
|
this.opacity = opacity;
|
|
this.setOpacity();
|
|
},
|
|
setOpacity: function () {
|
|
var attrs = [];
|
|
this.mapOpacityTo(attrs, this.srcElement.options.opacity);
|
|
this.allAttr(attrs);
|
|
},
|
|
setSrc: function () {
|
|
this.attr('src', this.srcElement.src());
|
|
},
|
|
mapTransform: function () {
|
|
var img = this.srcElement;
|
|
var rawbbox = img.rawBBox();
|
|
var rawcenter = rawbbox.center();
|
|
var fillOrigin = COORDINATE_MULTIPLE / 2;
|
|
var fillSize = COORDINATE_MULTIPLE;
|
|
var x;
|
|
var y;
|
|
var width = rawbbox.width() / fillSize;
|
|
var height = rawbbox.height() / fillSize;
|
|
var angle = 0;
|
|
var transform = this.transform;
|
|
if (transform) {
|
|
var matrix = toMatrix(transform);
|
|
var sx = sqrt(matrix.a * matrix.a + matrix.b * matrix.b);
|
|
var sy = sqrt(matrix.c * matrix.c + matrix.d * matrix.d);
|
|
width *= sx;
|
|
height *= sy;
|
|
var ax = deg(atan2(matrix.b, matrix.d));
|
|
var ay = deg(atan2(-matrix.c, matrix.a));
|
|
angle = (ax + ay) / 2;
|
|
if (angle !== 0) {
|
|
var center = img.bbox().center();
|
|
x = (center.x - fillOrigin) / fillSize;
|
|
y = (center.y - fillOrigin) / fillSize;
|
|
} else {
|
|
x = (rawcenter.x * sx + matrix.e - fillOrigin) / fillSize;
|
|
y = (rawcenter.y * sy + matrix.f - fillOrigin) / fillSize;
|
|
}
|
|
} else {
|
|
x = (rawcenter.x - fillOrigin) / fillSize;
|
|
y = (rawcenter.y - fillOrigin) / fillSize;
|
|
}
|
|
width = round(width, TRANSFORM_PRECISION);
|
|
height = round(height, TRANSFORM_PRECISION);
|
|
x = round(x, TRANSFORM_PRECISION);
|
|
y = round(y, TRANSFORM_PRECISION);
|
|
angle = round(angle, TRANSFORM_PRECISION);
|
|
return [
|
|
[
|
|
'size',
|
|
width + ',' + height
|
|
],
|
|
[
|
|
'position',
|
|
x + ',' + y
|
|
],
|
|
[
|
|
'angle',
|
|
angle
|
|
]
|
|
];
|
|
}
|
|
});
|
|
var ImageNode = PathNode.extend({
|
|
createFillNode: function (srcElement, transform, opacity) {
|
|
return new ImageFillNode(srcElement, transform, opacity);
|
|
},
|
|
createDataNode: function (srcElement) {
|
|
return new ImagePathDataNode(srcElement);
|
|
},
|
|
optionsChange: function (e) {
|
|
if (e.field === 'src' || e.field === 'transform') {
|
|
this.fill.optionsChange(e);
|
|
}
|
|
PathNode.fn.optionsChange.call(this, e);
|
|
},
|
|
geometryChange: function () {
|
|
this.fill.geometryChange();
|
|
PathNode.fn.geometryChange.call(this);
|
|
},
|
|
refreshTransform: function (transform) {
|
|
PathNode.fn.refreshTransform.call(this, transform);
|
|
this.fill.refresh(this.srcElement.currentTransform(transform));
|
|
}
|
|
});
|
|
var RectDataNode = PathDataNode.extend({
|
|
renderData: function () {
|
|
var rect = this.srcElement.geometry();
|
|
var parts = [
|
|
'm',
|
|
printPoints([rect.topLeft()]),
|
|
'l',
|
|
printPoints([
|
|
rect.topRight(),
|
|
rect.bottomRight(),
|
|
rect.bottomLeft()
|
|
]),
|
|
'x e'
|
|
];
|
|
return parts.join(' ');
|
|
}
|
|
});
|
|
var RectNode = PathNode.extend({
|
|
createDataNode: function (srcElement) {
|
|
return new RectDataNode(srcElement);
|
|
}
|
|
});
|
|
var nodeMap = {
|
|
Group: GroupNode,
|
|
Text: TextNode,
|
|
Path: PathNode,
|
|
MultiPath: MultiPathNode,
|
|
Circle: CircleNode,
|
|
Arc: ArcNode,
|
|
Image: ImageNode,
|
|
Rect: RectNode
|
|
};
|
|
function enableVML() {
|
|
if (doc.namespaces && !doc.namespaces.kvml) {
|
|
doc.namespaces.add('kvml', 'urn:schemas-microsoft-com:vml');
|
|
var stylesheet = doc.styleSheets.length > 30 ? doc.styleSheets[0] : doc.createStyleSheet();
|
|
stylesheet.addRule('.kvml', 'behavior:url(#default#VML)');
|
|
}
|
|
}
|
|
function createElementVML(type) {
|
|
var element = doc.createElement('kvml:' + type);
|
|
element.className = 'kvml';
|
|
return element;
|
|
}
|
|
function printPoints(points) {
|
|
var length = points.length;
|
|
var result = [];
|
|
for (var i = 0; i < length; i++) {
|
|
result.push(points[i].scaleCopy(COORDINATE_MULTIPLE).toString(0, ','));
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
function printPath(path, open) {
|
|
var segments = path.segments, length = segments.length;
|
|
if (length > 0) {
|
|
var parts = [], output, type, currentType, i;
|
|
for (i = 1; i < length; i++) {
|
|
type = segmentType(segments[i - 1], segments[i]);
|
|
if (type !== currentType) {
|
|
currentType = type;
|
|
parts.push(type);
|
|
}
|
|
if (type === 'l') {
|
|
parts.push(printPoints([segments[i].anchor()]));
|
|
} else {
|
|
parts.push(printPoints([
|
|
segments[i - 1].controlOut(),
|
|
segments[i].controlIn(),
|
|
segments[i].anchor()
|
|
]));
|
|
}
|
|
}
|
|
output = 'm ' + printPoints([segments[0].anchor()]) + ' ' + parts.join(' ');
|
|
if (path.options.closed) {
|
|
output += ' x';
|
|
}
|
|
if (open !== true) {
|
|
output += ' e';
|
|
}
|
|
return output;
|
|
}
|
|
}
|
|
function segmentType(segmentStart, segmentEnd) {
|
|
return segmentStart.controlOut() && segmentEnd.controlIn() ? 'c' : 'l';
|
|
}
|
|
function fillField(field) {
|
|
return field.indexOf('fill') === 0 || field.indexOf(GRADIENT) === 0;
|
|
}
|
|
function stopColor(baseColor, stop, baseOpacity) {
|
|
var opacity = baseOpacity * valueOrDefault(stop.opacity(), 1);
|
|
var color;
|
|
if (baseColor) {
|
|
color = blendColors(baseColor, stop.color(), opacity);
|
|
} else {
|
|
color = blendColors(stop.color(), '#fff', 1 - opacity);
|
|
}
|
|
return color;
|
|
}
|
|
function blendColors(base, overlay, alpha) {
|
|
var baseColor = new Color(base), overlayColor = new Color(overlay), r = blendChannel(baseColor.r, overlayColor.r, alpha), g = blendChannel(baseColor.g, overlayColor.g, alpha), b = blendChannel(baseColor.b, overlayColor.b, alpha);
|
|
return new Color(r, g, b).toHex();
|
|
}
|
|
function blendChannel(a, b, alpha) {
|
|
return math.round(alpha * b + (1 - alpha) * a);
|
|
}
|
|
kendo.support.vml = function () {
|
|
var browser = kendo.support.browser;
|
|
return browser.msie && browser.version < 9;
|
|
}();
|
|
var EMPTY_CLIP = 'inherit';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 8) {
|
|
EMPTY_CLIP = 'rect(auto auto auto auto)';
|
|
}
|
|
if (kendo.support.vml) {
|
|
d.SurfaceFactory.current.register('vml', Surface, 30);
|
|
}
|
|
deepExtend(d, {
|
|
vml: {
|
|
ArcDataNode: ArcDataNode,
|
|
ArcNode: ArcNode,
|
|
CircleTransformNode: CircleTransformNode,
|
|
CircleNode: CircleNode,
|
|
FillNode: FillNode,
|
|
GroupNode: GroupNode,
|
|
ImageNode: ImageNode,
|
|
ImageFillNode: ImageFillNode,
|
|
ImagePathDataNode: ImagePathDataNode,
|
|
MultiPathDataNode: MultiPathDataNode,
|
|
MultiPathNode: MultiPathNode,
|
|
Node: Node,
|
|
PathDataNode: PathDataNode,
|
|
PathNode: PathNode,
|
|
RectDataNode: RectDataNode,
|
|
RectNode: RectNode,
|
|
RootNode: RootNode,
|
|
StrokeNode: StrokeNode,
|
|
Surface: Surface,
|
|
TextNode: TextNode,
|
|
TextPathNode: TextPathNode,
|
|
TextPathDataNode: TextPathDataNode,
|
|
TransformNode: TransformNode
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/html', [
|
|
'kendo.color',
|
|
'drawing/shapes',
|
|
'util/main',
|
|
'util/text-metrics'
|
|
], f);
|
|
}(function () {
|
|
(function ($, parseFloat, Math) {
|
|
'use strict';
|
|
var drawing = kendo.drawing;
|
|
var geo = kendo.geometry;
|
|
var slice = Array.prototype.slice;
|
|
var browser = kendo.support.browser;
|
|
var romanNumeral = kendo.util.arabicToRoman;
|
|
var KENDO_PSEUDO_ELEMENT = 'KENDO-PSEUDO-ELEMENT';
|
|
var IMAGE_CACHE = {};
|
|
var nodeInfo = {};
|
|
nodeInfo._root = nodeInfo;
|
|
var TextRect = drawing.Text.extend({
|
|
nodeType: 'Text',
|
|
init: function (str, rect, options) {
|
|
drawing.Text.fn.init.call(this, str, rect.getOrigin(), options);
|
|
this._pdfRect = rect;
|
|
},
|
|
rect: function () {
|
|
return this._pdfRect;
|
|
},
|
|
rawBBox: function () {
|
|
return this._pdfRect;
|
|
}
|
|
});
|
|
function drawDOM(element, options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
var defer = $.Deferred();
|
|
element = $(element)[0];
|
|
if (!element) {
|
|
return defer.reject('No element to export');
|
|
}
|
|
if (typeof window.getComputedStyle != 'function') {
|
|
throw new Error('window.getComputedStyle is missing. You are using an unsupported browser, or running in IE8 compatibility mode. Drawing HTML is supported in Chrome, Firefox, Safari and IE9+.');
|
|
}
|
|
if (kendo.pdf) {
|
|
kendo.pdf.defineFont(getFontFaces(element.ownerDocument));
|
|
}
|
|
function doOne(element) {
|
|
var group = new drawing.Group();
|
|
var pos = element.getBoundingClientRect();
|
|
setTransform(group, [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
-pos.left,
|
|
-pos.top
|
|
]);
|
|
nodeInfo._clipbox = false;
|
|
nodeInfo._matrix = geo.Matrix.unit();
|
|
nodeInfo._stackingContext = {
|
|
element: element,
|
|
group: group
|
|
};
|
|
if (options.avoidLinks === true) {
|
|
nodeInfo._avoidLinks = 'a';
|
|
} else {
|
|
nodeInfo._avoidLinks = options.avoidLinks;
|
|
}
|
|
$(element).addClass('k-pdf-export');
|
|
renderElement(element, group);
|
|
$(element).removeClass('k-pdf-export');
|
|
return group;
|
|
}
|
|
cacheImages(element, function () {
|
|
var forceBreak = options && options.forcePageBreak;
|
|
var hasPaperSize = options && options.paperSize && options.paperSize != 'auto';
|
|
var paperOptions = hasPaperSize && kendo.pdf.getPaperOptions(function (key, def) {
|
|
return key in options ? options[key] : def;
|
|
});
|
|
var pageWidth = hasPaperSize && paperOptions.paperSize[0];
|
|
var pageHeight = hasPaperSize && paperOptions.paperSize[1];
|
|
var margin = options.margin && paperOptions.margin;
|
|
if (forceBreak || pageHeight) {
|
|
if (!margin) {
|
|
margin = {
|
|
left: 0,
|
|
top: 0,
|
|
right: 0,
|
|
bottom: 0
|
|
};
|
|
}
|
|
var group = new drawing.Group({
|
|
pdf: {
|
|
multiPage: true,
|
|
paperSize: hasPaperSize ? paperOptions.paperSize : 'auto'
|
|
}
|
|
});
|
|
handlePageBreaks(function (x) {
|
|
if (options.progress) {
|
|
var canceled = false, pageNum = 0;
|
|
(function next() {
|
|
if (pageNum < x.pages.length) {
|
|
group.append(doOne(x.pages[pageNum]));
|
|
options.progress({
|
|
pageNum: ++pageNum,
|
|
totalPages: x.pages.length,
|
|
cancel: function () {
|
|
canceled = true;
|
|
}
|
|
});
|
|
if (!canceled) {
|
|
setTimeout(next);
|
|
} else {
|
|
x.container.parentNode.removeChild(x.container);
|
|
}
|
|
} else {
|
|
x.container.parentNode.removeChild(x.container);
|
|
defer.resolve(group);
|
|
}
|
|
}());
|
|
} else {
|
|
x.pages.forEach(function (page) {
|
|
group.append(doOne(page));
|
|
});
|
|
x.container.parentNode.removeChild(x.container);
|
|
defer.resolve(group);
|
|
}
|
|
}, element, forceBreak, pageWidth ? pageWidth - margin.left - margin.right : null, pageHeight ? pageHeight - margin.top - margin.bottom : null, margin, options);
|
|
} else {
|
|
defer.resolve(doOne(element));
|
|
}
|
|
});
|
|
function makeTemplate(template) {
|
|
if (template != null) {
|
|
if (typeof template == 'string') {
|
|
template = kendo.template(template.replace(/^\s+|\s+$/g, ''));
|
|
}
|
|
if (typeof template == 'function') {
|
|
return function (data) {
|
|
var el = template(data);
|
|
if (el) {
|
|
if (typeof el == 'string') {
|
|
el = el.replace(/^\s+|\s+$/g, '');
|
|
}
|
|
return $(el)[0];
|
|
}
|
|
};
|
|
}
|
|
return function () {
|
|
return $(template).clone()[0];
|
|
};
|
|
}
|
|
}
|
|
function cloneNodes(el) {
|
|
var clone = el.cloneNode(false);
|
|
if (el.nodeType == 1) {
|
|
var $el = $(el), $clone = $(clone), i;
|
|
var data = $el.data();
|
|
for (i in data) {
|
|
$clone.data(i, data[i]);
|
|
}
|
|
if (/^canvas$/i.test(el.tagName)) {
|
|
clone.getContext('2d').drawImage(el, 0, 0);
|
|
} else if (/^input$/i.test(el.tagName)) {
|
|
el.removeAttribute('name');
|
|
} else {
|
|
for (i = el.firstChild; i; i = i.nextSibling) {
|
|
clone.appendChild(cloneNodes(i));
|
|
}
|
|
}
|
|
}
|
|
return clone;
|
|
}
|
|
function handlePageBreaks(callback, element, forceBreak, pageWidth, pageHeight, margin, options) {
|
|
var template = makeTemplate(options.template);
|
|
var doc = element.ownerDocument;
|
|
var pages = [];
|
|
var copy = cloneNodes(element);
|
|
var container = doc.createElement('KENDO-PDF-DOCUMENT');
|
|
var adjust = 0;
|
|
$(copy).find('tfoot').each(function () {
|
|
this.parentNode.appendChild(this);
|
|
});
|
|
$(copy).find('ol').each(function () {
|
|
$(this).children().each(function (index) {
|
|
this.setAttribute('kendo-split-index', index);
|
|
});
|
|
});
|
|
$(container).css({
|
|
display: 'block',
|
|
position: 'absolute',
|
|
boxSizing: 'content-box',
|
|
left: '-10000px',
|
|
top: '-10000px'
|
|
});
|
|
if (pageWidth) {
|
|
$(container).css({
|
|
width: pageWidth,
|
|
paddingLeft: margin.left,
|
|
paddingRight: margin.right
|
|
});
|
|
$(copy).css({ overflow: 'hidden' });
|
|
}
|
|
container.appendChild(copy);
|
|
element.parentNode.insertBefore(container, element);
|
|
if (options.beforePageBreak) {
|
|
setTimeout(function () {
|
|
options.beforePageBreak(container, doPageBreak);
|
|
}, 15);
|
|
} else {
|
|
setTimeout(doPageBreak, 15);
|
|
}
|
|
function doPageBreak() {
|
|
if (forceBreak != '-' || pageHeight) {
|
|
splitElement(copy);
|
|
}
|
|
var page = makePage();
|
|
copy.parentNode.insertBefore(page, copy);
|
|
page.appendChild(copy);
|
|
if (template) {
|
|
var count = pages.length;
|
|
pages.forEach(function (page, i) {
|
|
var el = template({
|
|
element: page,
|
|
pageNum: i + 1,
|
|
totalPages: pages.length
|
|
});
|
|
if (el) {
|
|
page.appendChild(el);
|
|
cacheImages(el, function () {
|
|
if (--count === 0) {
|
|
next();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
next();
|
|
}
|
|
function next() {
|
|
whenImagesAreActuallyLoaded(pages, function () {
|
|
callback({
|
|
pages: pages,
|
|
container: container
|
|
});
|
|
});
|
|
}
|
|
}
|
|
function splitElement(element) {
|
|
var style = getComputedStyle(element);
|
|
var bottomPadding = parseFloat(getPropertyValue(style, 'padding-bottom'));
|
|
var bottomBorder = parseFloat(getPropertyValue(style, 'border-bottom-width'));
|
|
var saveAdjust = adjust;
|
|
adjust += bottomPadding + bottomBorder;
|
|
var isFirst = true;
|
|
for (var el = element.firstChild; el; el = el.nextSibling) {
|
|
if (el.nodeType == 1) {
|
|
isFirst = false;
|
|
var jqel = $(el);
|
|
if (jqel.is(forceBreak)) {
|
|
breakAtElement(el);
|
|
continue;
|
|
}
|
|
if (!pageHeight) {
|
|
splitElement(el);
|
|
continue;
|
|
}
|
|
if (!/^(?:static|relative)$/.test(getPropertyValue(getComputedStyle(el), 'position'))) {
|
|
continue;
|
|
}
|
|
var fall = fallsOnMargin(el);
|
|
if (fall == 1) {
|
|
breakAtElement(el);
|
|
} else if (fall) {
|
|
if (jqel.data('kendoChart') || /^(?:img|tr|iframe|svg|object|canvas|input|textarea|select|video|h[1-6])/i.test(el.tagName)) {
|
|
breakAtElement(el);
|
|
} else {
|
|
splitElement(el);
|
|
}
|
|
} else {
|
|
splitElement(el);
|
|
}
|
|
} else if (el.nodeType == 3 && pageHeight) {
|
|
splitText(el, isFirst);
|
|
isFirst = false;
|
|
}
|
|
}
|
|
adjust = saveAdjust;
|
|
}
|
|
function firstInParent(el) {
|
|
var p = el.parentNode, first = p.firstChild;
|
|
if (el === first) {
|
|
return true;
|
|
}
|
|
if (el === p.children[0]) {
|
|
if (first.nodeType == 7 || first.nodeType == 8) {
|
|
return true;
|
|
}
|
|
if (first.nodeType == 3) {
|
|
return !/\S/.test(first.data);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function breakAtElement(el) {
|
|
if (el.nodeType == 1 && el !== copy && firstInParent(el)) {
|
|
return breakAtElement(el.parentNode);
|
|
}
|
|
var colgroup = $(el).closest('table').find('colgroup');
|
|
var page = makePage();
|
|
var range = doc.createRange();
|
|
range.setStartBefore(copy);
|
|
range.setEndBefore(el);
|
|
page.appendChild(range.extractContents());
|
|
copy.parentNode.insertBefore(page, copy);
|
|
if (colgroup[0]) {
|
|
colgroup.clone().prependTo($(el).closest('table'));
|
|
}
|
|
}
|
|
function makePage() {
|
|
var page = doc.createElement('KENDO-PDF-PAGE');
|
|
$(page).css({
|
|
display: 'block',
|
|
boxSizing: 'content-box',
|
|
width: pageWidth || 'auto',
|
|
padding: margin.top + 'px ' + margin.right + 'px ' + margin.bottom + 'px ' + margin.left + 'px',
|
|
position: 'relative',
|
|
height: pageHeight || 'auto',
|
|
overflow: pageHeight || pageWidth ? 'hidden' : 'visible',
|
|
clear: 'both'
|
|
});
|
|
if (options && options.pageClassName) {
|
|
page.className = options.pageClassName;
|
|
}
|
|
pages.push(page);
|
|
return page;
|
|
}
|
|
function fallsOnMargin(thing) {
|
|
var box = thing.getBoundingClientRect();
|
|
if (box.width === 0 || box.height === 0) {
|
|
return 0;
|
|
}
|
|
var top = copy.getBoundingClientRect().top;
|
|
var available = pageHeight - adjust;
|
|
return box.height > available ? 3 : box.top - top > available ? 1 : box.bottom - top > available ? 2 : 0;
|
|
}
|
|
function splitText(node, isFirst) {
|
|
if (!/\S/.test(node.data)) {
|
|
return;
|
|
}
|
|
var len = node.data.length;
|
|
var range = doc.createRange();
|
|
range.selectNodeContents(node);
|
|
var fall = fallsOnMargin(range);
|
|
if (!fall) {
|
|
return;
|
|
}
|
|
var nextnode = node;
|
|
if (fall == 1) {
|
|
if (isFirst) {
|
|
breakAtElement(node.parentNode);
|
|
} else {
|
|
breakAtElement(node);
|
|
}
|
|
} else {
|
|
(function findEOP(min, pos, max) {
|
|
range.setEnd(node, pos);
|
|
if (min == pos || pos == max) {
|
|
return pos;
|
|
}
|
|
if (fallsOnMargin(range)) {
|
|
return findEOP(min, min + pos >> 1, pos);
|
|
} else {
|
|
return findEOP(pos, pos + max >> 1, max);
|
|
}
|
|
}(0, len >> 1, len));
|
|
if (!/\S/.test(range.toString()) && isFirst) {
|
|
breakAtElement(node.parentNode);
|
|
} else {
|
|
nextnode = node.splitText(range.endOffset);
|
|
var page = makePage();
|
|
range.setStartBefore(copy);
|
|
page.appendChild(range.extractContents());
|
|
copy.parentNode.insertBefore(page, copy);
|
|
}
|
|
}
|
|
splitText(nextnode);
|
|
}
|
|
}
|
|
return defer.promise();
|
|
}
|
|
drawing.drawDOM = drawDOM;
|
|
drawDOM.getFontFaces = getFontFaces;
|
|
var parseBackgroundImage = function () {
|
|
var tok_linear_gradient = /^((-webkit-|-moz-|-o-|-ms-)?linear-gradient\s*)\(/;
|
|
var tok_percent = /^([-0-9.]+%)/;
|
|
var tok_length = /^([-0-9.]+px)/;
|
|
var tok_keyword = /^(left|right|top|bottom|to|center)\W/;
|
|
var tok_angle = /^([-0-9.]+(deg|grad|rad|turn))/;
|
|
var tok_whitespace = /^(\s+)/;
|
|
var tok_popen = /^(\()/;
|
|
var tok_pclose = /^(\))/;
|
|
var tok_comma = /^(,)/;
|
|
var tok_url = /^(url)\(/;
|
|
var tok_content = /^(.*?)\)/;
|
|
var cache1 = {}, cache2 = {};
|
|
function parse(input) {
|
|
var orig = input;
|
|
if (hasOwnProperty(cache1, orig)) {
|
|
return cache1[orig];
|
|
}
|
|
function skip_ws() {
|
|
var m = tok_whitespace.exec(input);
|
|
if (m) {
|
|
input = input.substr(m[1].length);
|
|
}
|
|
}
|
|
function read(token) {
|
|
skip_ws();
|
|
var m = token.exec(input);
|
|
if (m) {
|
|
input = input.substr(m[1].length);
|
|
return m[1];
|
|
}
|
|
}
|
|
function read_stop() {
|
|
var color = kendo.parseColor(input, true);
|
|
var length, percent;
|
|
if (color) {
|
|
input = input.substr(color.match[0].length);
|
|
color = color.toRGB();
|
|
if (!(length = read(tok_length))) {
|
|
percent = read(tok_percent);
|
|
}
|
|
return {
|
|
color: color,
|
|
length: length,
|
|
percent: percent
|
|
};
|
|
}
|
|
}
|
|
function read_linear_gradient(propName) {
|
|
var angle;
|
|
var to1, to2;
|
|
var stops = [];
|
|
var reverse = false;
|
|
if (read(tok_popen)) {
|
|
angle = read(tok_angle);
|
|
if (angle) {
|
|
angle = parseAngle(angle);
|
|
read(tok_comma);
|
|
} else {
|
|
to1 = read(tok_keyword);
|
|
if (to1 == 'to') {
|
|
to1 = read(tok_keyword);
|
|
} else if (to1 && /^-/.test(propName)) {
|
|
reverse = true;
|
|
}
|
|
to2 = read(tok_keyword);
|
|
read(tok_comma);
|
|
}
|
|
if (/-moz-/.test(propName) && angle == null && to1 == null) {
|
|
var x = read(tok_percent), y = read(tok_percent);
|
|
reverse = true;
|
|
if (x == '0%') {
|
|
to1 = 'left';
|
|
} else if (x == '100%') {
|
|
to1 = 'right';
|
|
}
|
|
if (y == '0%') {
|
|
to2 = 'top';
|
|
} else if (y == '100%') {
|
|
to2 = 'bottom';
|
|
}
|
|
read(tok_comma);
|
|
}
|
|
while (input && !read(tok_pclose)) {
|
|
var stop = read_stop();
|
|
if (!stop) {
|
|
break;
|
|
}
|
|
stops.push(stop);
|
|
read(tok_comma);
|
|
}
|
|
return {
|
|
type: 'linear',
|
|
angle: angle,
|
|
to: to1 && to2 ? to1 + ' ' + to2 : to1 ? to1 : to2 ? to2 : null,
|
|
stops: stops,
|
|
reverse: reverse
|
|
};
|
|
}
|
|
}
|
|
function read_url() {
|
|
if (read(tok_popen)) {
|
|
var url = read(tok_content);
|
|
url = url.replace(/^['"]+|["']+$/g, '');
|
|
read(tok_pclose);
|
|
return {
|
|
type: 'url',
|
|
url: url
|
|
};
|
|
}
|
|
}
|
|
var tok;
|
|
if (tok = read(tok_linear_gradient)) {
|
|
tok = read_linear_gradient(tok);
|
|
} else if (tok = read(tok_url)) {
|
|
tok = read_url();
|
|
}
|
|
return cache1[orig] = tok || { type: 'none' };
|
|
}
|
|
return function (input) {
|
|
if (hasOwnProperty(cache2, input)) {
|
|
return cache2[input];
|
|
}
|
|
return cache2[input] = splitProperty(input).map(parse);
|
|
};
|
|
}();
|
|
var splitProperty = function () {
|
|
var cache = {};
|
|
return function (input, separator) {
|
|
if (!separator) {
|
|
separator = /^\s*,\s*/;
|
|
}
|
|
var cacheKey = input + separator;
|
|
if (hasOwnProperty(cache, cacheKey)) {
|
|
return cache[cacheKey];
|
|
}
|
|
var ret = [];
|
|
var last = 0, pos = 0;
|
|
var in_paren = 0;
|
|
var in_string = false;
|
|
var m;
|
|
function looking_at(rx) {
|
|
return m = rx.exec(input.substr(pos));
|
|
}
|
|
function trim(str) {
|
|
return str.replace(/^\s+|\s+$/g, '');
|
|
}
|
|
while (pos < input.length) {
|
|
if (!in_string && looking_at(/^[\(\[\{]/)) {
|
|
in_paren++;
|
|
pos++;
|
|
} else if (!in_string && looking_at(/^[\)\]\}]/)) {
|
|
in_paren--;
|
|
pos++;
|
|
} else if (!in_string && looking_at(/^[\"\']/)) {
|
|
in_string = m[0];
|
|
pos++;
|
|
} else if (in_string == '\'' && looking_at(/^\\\'/)) {
|
|
pos += 2;
|
|
} else if (in_string == '"' && looking_at(/^\\\"/)) {
|
|
pos += 2;
|
|
} else if (in_string == '\'' && looking_at(/^\'/)) {
|
|
in_string = false;
|
|
pos++;
|
|
} else if (in_string == '"' && looking_at(/^\"/)) {
|
|
in_string = false;
|
|
pos++;
|
|
} else if (looking_at(separator)) {
|
|
if (!in_string && !in_paren && pos > last) {
|
|
ret.push(trim(input.substring(last, pos)));
|
|
last = pos + m[0].length;
|
|
}
|
|
pos += m[0].length;
|
|
} else {
|
|
pos++;
|
|
}
|
|
}
|
|
if (last < pos) {
|
|
ret.push(trim(input.substring(last, pos)));
|
|
}
|
|
return cache[cacheKey] = ret;
|
|
};
|
|
}();
|
|
var getFontURL = function () {
|
|
var cache = {};
|
|
return function (el) {
|
|
var url = cache[el];
|
|
if (!url) {
|
|
var m;
|
|
if (m = /url\((['"]?)([^'")]*?)\1\)\s+format\((['"]?)truetype\3\)/.exec(el)) {
|
|
url = cache[el] = m[2];
|
|
} else if (m = /url\((['"]?)([^'")]*?\.ttf)\1\)/.exec(el)) {
|
|
url = cache[el] = m[2];
|
|
}
|
|
}
|
|
return url;
|
|
};
|
|
}();
|
|
function getFontFaces(doc) {
|
|
if (doc == null) {
|
|
doc = document;
|
|
}
|
|
var result = {};
|
|
for (var i = 0; i < doc.styleSheets.length; ++i) {
|
|
doStylesheet(doc.styleSheets[i]);
|
|
}
|
|
return result;
|
|
function doStylesheet(ss) {
|
|
if (ss) {
|
|
var rules = null;
|
|
try {
|
|
rules = ss.cssRules;
|
|
} catch (ex) {
|
|
}
|
|
if (rules) {
|
|
addRules(ss, rules);
|
|
}
|
|
}
|
|
}
|
|
function findFonts(rule) {
|
|
var src = getPropertyValue(rule.style, 'src');
|
|
if (src) {
|
|
return splitProperty(src).reduce(function (a, el) {
|
|
var font = getFontURL(el);
|
|
if (font) {
|
|
a.push(font);
|
|
}
|
|
return a;
|
|
}, []);
|
|
} else {
|
|
var font = getFontURL(rule.cssText);
|
|
return font ? [font] : [];
|
|
}
|
|
}
|
|
function addRules(styleSheet, rules) {
|
|
for (var i = 0; i < rules.length; ++i) {
|
|
var r = rules[i];
|
|
switch (r.type) {
|
|
case 3:
|
|
doStylesheet(r.styleSheet);
|
|
break;
|
|
case 5:
|
|
var style = r.style;
|
|
var family = splitProperty(getPropertyValue(style, 'font-family'));
|
|
var bold = /^([56789]00|bold)$/i.test(getPropertyValue(style, 'font-weight'));
|
|
var italic = 'italic' == getPropertyValue(style, 'font-style');
|
|
var src = findFonts(r);
|
|
if (src.length > 0) {
|
|
addRule(styleSheet, family, bold, italic, src[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function addRule(styleSheet, names, bold, italic, url) {
|
|
if (!/^data:/i.test(url)) {
|
|
if (!(/^[^\/:]+:\/\//.test(url) || /^\//.test(url))) {
|
|
url = String(styleSheet.href).replace(/[^\/]*$/, '') + url;
|
|
}
|
|
}
|
|
names.forEach(function (name) {
|
|
name = name.replace(/^(['"]?)(.*?)\1$/, '$2');
|
|
if (bold) {
|
|
name += '|bold';
|
|
}
|
|
if (italic) {
|
|
name += '|italic';
|
|
}
|
|
result[name] = url;
|
|
});
|
|
}
|
|
}
|
|
function hasOwnProperty(obj, key) {
|
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
}
|
|
function getCounter(name) {
|
|
name = '_counter_' + name;
|
|
return nodeInfo[name];
|
|
}
|
|
function getAllCounters(name) {
|
|
var values = [], p = nodeInfo;
|
|
name = '_counter_' + name;
|
|
while (p) {
|
|
if (hasOwnProperty(p, name)) {
|
|
values.push(p[name]);
|
|
}
|
|
p = Object.getPrototypeOf(p);
|
|
}
|
|
return values.reverse();
|
|
}
|
|
function incCounter(name, inc) {
|
|
var p = nodeInfo;
|
|
name = '_counter_' + name;
|
|
while (p && !hasOwnProperty(p, name)) {
|
|
p = Object.getPrototypeOf(p);
|
|
}
|
|
if (!p) {
|
|
p = nodeInfo._root;
|
|
}
|
|
p[name] = (p[name] || 0) + (inc == null ? 1 : inc);
|
|
}
|
|
function resetCounter(name, val) {
|
|
name = '_counter_' + name;
|
|
nodeInfo[name] = val == null ? 0 : val;
|
|
}
|
|
function doCounters(a, f, def) {
|
|
for (var i = 0; i < a.length;) {
|
|
var name = a[i++];
|
|
var val = parseFloat(a[i]);
|
|
if (isNaN(val)) {
|
|
f(name, def);
|
|
} else {
|
|
f(name, val);
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
function parseColor(str, css) {
|
|
var color = kendo.parseColor(str);
|
|
if (color) {
|
|
color = color.toRGB();
|
|
if (css) {
|
|
color = color.toCssRgba();
|
|
} else if (color.a === 0) {
|
|
color = null;
|
|
}
|
|
}
|
|
return color;
|
|
}
|
|
function whenImagesAreActuallyLoaded(elements, callback) {
|
|
var pending = 0;
|
|
elements.forEach(function (el) {
|
|
var images = el.querySelectorAll('img');
|
|
for (var i = 0; i < images.length; ++i) {
|
|
var img = images[i];
|
|
if (!img.complete) {
|
|
pending++;
|
|
img.onload = img.onerror = next;
|
|
}
|
|
}
|
|
});
|
|
if (!pending) {
|
|
next();
|
|
}
|
|
function next() {
|
|
if (--pending <= 0) {
|
|
callback();
|
|
}
|
|
}
|
|
}
|
|
function cacheImages(element, callback) {
|
|
var urls = [];
|
|
function add(url) {
|
|
if (!IMAGE_CACHE[url]) {
|
|
IMAGE_CACHE[url] = true;
|
|
urls.push(url);
|
|
}
|
|
}
|
|
(function dive(element) {
|
|
if (/^img$/i.test(element.tagName)) {
|
|
add(element.src);
|
|
}
|
|
parseBackgroundImage(getPropertyValue(getComputedStyle(element), 'background-image')).forEach(function (bg) {
|
|
if (bg.type == 'url') {
|
|
add(bg.url);
|
|
}
|
|
});
|
|
if (element.children) {
|
|
slice.call(element.children).forEach(dive);
|
|
}
|
|
}(element));
|
|
var count = urls.length;
|
|
function next() {
|
|
if (--count <= 0) {
|
|
callback();
|
|
}
|
|
}
|
|
if (count === 0) {
|
|
next();
|
|
}
|
|
urls.forEach(function (url) {
|
|
var img = IMAGE_CACHE[url] = new Image();
|
|
if (!/^data:/i.test(url)) {
|
|
img.crossOrigin = 'Anonymous';
|
|
}
|
|
img.src = url;
|
|
if (img.complete) {
|
|
next();
|
|
} else {
|
|
img.onload = next;
|
|
img.onerror = function () {
|
|
IMAGE_CACHE[url] = null;
|
|
next();
|
|
};
|
|
}
|
|
});
|
|
}
|
|
function alphaNumeral(n) {
|
|
var result = '';
|
|
do {
|
|
var r = n % 26;
|
|
result = String.fromCharCode(97 + r) + result;
|
|
n = Math.floor(n / 26);
|
|
} while (n > 0);
|
|
return result;
|
|
}
|
|
function pushNodeInfo(element, style, group) {
|
|
nodeInfo = Object.create(nodeInfo);
|
|
nodeInfo[element.tagName.toLowerCase()] = {
|
|
element: element,
|
|
style: style
|
|
};
|
|
var decoration = getPropertyValue(style, 'text-decoration');
|
|
if (decoration && decoration != 'none') {
|
|
var color = getPropertyValue(style, 'color');
|
|
decoration.split(/\s+/g).forEach(function (name) {
|
|
if (!nodeInfo[name]) {
|
|
nodeInfo[name] = color;
|
|
}
|
|
});
|
|
}
|
|
if (createsStackingContext(style)) {
|
|
nodeInfo._stackingContext = {
|
|
element: element,
|
|
group: group
|
|
};
|
|
}
|
|
}
|
|
function popNodeInfo() {
|
|
nodeInfo = Object.getPrototypeOf(nodeInfo);
|
|
}
|
|
function updateClipbox(path) {
|
|
if (nodeInfo._clipbox != null) {
|
|
var box = path.bbox(nodeInfo._matrix);
|
|
if (nodeInfo._clipbox) {
|
|
nodeInfo._clipbox = geo.Rect.intersect(nodeInfo._clipbox, box);
|
|
} else {
|
|
nodeInfo._clipbox = box;
|
|
}
|
|
}
|
|
}
|
|
function emptyClipbox() {
|
|
var cb = nodeInfo._clipbox;
|
|
if (cb == null) {
|
|
return true;
|
|
}
|
|
if (cb) {
|
|
return cb.width() === 0 || cb.height() === 0;
|
|
}
|
|
}
|
|
function createsStackingContext(style) {
|
|
function prop(name) {
|
|
return getPropertyValue(style, name);
|
|
}
|
|
if (prop('transform') != 'none' || prop('position') != 'static' && prop('z-index') != 'auto' || prop('opacity') < 1) {
|
|
return true;
|
|
}
|
|
}
|
|
function getComputedStyle(element, pseudoElt) {
|
|
return window.getComputedStyle(element, pseudoElt || null);
|
|
}
|
|
function getPropertyValue(style, prop) {
|
|
return style.getPropertyValue(prop) || browser.webkit && style.getPropertyValue('-webkit-' + prop) || browser.mozilla && style.getPropertyValue('-moz-' + prop) || browser.opera && style.getPropertyValue('-o-' + prop) || browser.msie && style.getPropertyValue('-ms-' + prop);
|
|
}
|
|
function pleaseSetPropertyValue(style, prop, value, important) {
|
|
style.setProperty(prop, value, important);
|
|
if (browser.webkit) {
|
|
style.setProperty('-webkit-' + prop, value, important);
|
|
} else if (browser.mozilla) {
|
|
style.setProperty('-moz-' + prop, value, important);
|
|
} else if (browser.opera) {
|
|
style.setProperty('-o-' + prop, value, important);
|
|
} else if (browser.msie) {
|
|
style.setProperty('-ms-' + prop, value, important);
|
|
prop = 'ms' + prop.replace(/(^|-)([a-z])/g, function (s, p1, p2) {
|
|
return p1 + p2.toUpperCase();
|
|
});
|
|
style[prop] = value;
|
|
}
|
|
}
|
|
function actuallyGetRangeBoundingRect(range) {
|
|
if (browser.msie || browser.chrome) {
|
|
var a = range.getClientRects(), box, count = 0;
|
|
if (a.length <= 3) {
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (a[i].width <= 1) {
|
|
count++;
|
|
} else {
|
|
box = a[i];
|
|
}
|
|
}
|
|
if (count == a.length - 1) {
|
|
return box;
|
|
}
|
|
}
|
|
}
|
|
return range.getBoundingClientRect();
|
|
}
|
|
function getBorder(style, side) {
|
|
side = 'border-' + side;
|
|
return {
|
|
width: parseFloat(getPropertyValue(style, side + '-width')),
|
|
style: getPropertyValue(style, side + '-style'),
|
|
color: parseColor(getPropertyValue(style, side + '-color'), true)
|
|
};
|
|
}
|
|
function saveStyle(element, func) {
|
|
var prev = element.style.cssText;
|
|
var result = func();
|
|
element.style.cssText = prev;
|
|
return result;
|
|
}
|
|
function getBorderRadius(style, side) {
|
|
var r = getPropertyValue(style, 'border-' + side + '-radius').split(/\s+/g).map(parseFloat);
|
|
if (r.length == 1) {
|
|
r.push(r[0]);
|
|
}
|
|
return sanitizeRadius({
|
|
x: r[0],
|
|
y: r[1]
|
|
});
|
|
}
|
|
function getContentBox(element) {
|
|
var box = element.getBoundingClientRect();
|
|
box = innerBox(box, 'border-*-width', element);
|
|
box = innerBox(box, 'padding-*', element);
|
|
return box;
|
|
}
|
|
function innerBox(box, prop, element) {
|
|
var style, wt, wr, wb, wl;
|
|
if (typeof prop == 'string') {
|
|
style = getComputedStyle(element);
|
|
wt = parseFloat(getPropertyValue(style, prop.replace('*', 'top')));
|
|
wr = parseFloat(getPropertyValue(style, prop.replace('*', 'right')));
|
|
wb = parseFloat(getPropertyValue(style, prop.replace('*', 'bottom')));
|
|
wl = parseFloat(getPropertyValue(style, prop.replace('*', 'left')));
|
|
} else if (typeof prop == 'number') {
|
|
wt = wr = wb = wl = prop;
|
|
}
|
|
return {
|
|
top: box.top + wt,
|
|
right: box.right - wr,
|
|
bottom: box.bottom - wb,
|
|
left: box.left + wl,
|
|
width: box.right - box.left - wr - wl,
|
|
height: box.bottom - box.top - wb - wt
|
|
};
|
|
}
|
|
function getTransform(style) {
|
|
var transform = getPropertyValue(style, 'transform');
|
|
if (transform == 'none') {
|
|
return null;
|
|
}
|
|
var matrix = /^\s*matrix\(\s*(.*?)\s*\)\s*$/.exec(transform);
|
|
if (matrix) {
|
|
var origin = getPropertyValue(style, 'transform-origin');
|
|
matrix = matrix[1].split(/\s*,\s*/g).map(parseFloat);
|
|
origin = origin.split(/\s+/g).map(parseFloat);
|
|
return {
|
|
matrix: matrix,
|
|
origin: origin
|
|
};
|
|
}
|
|
}
|
|
function radiansToDegrees(radians) {
|
|
return 180 * radians / Math.PI % 360;
|
|
}
|
|
function parseAngle(angle) {
|
|
var num = parseFloat(angle);
|
|
if (/grad$/.test(angle)) {
|
|
return Math.PI * num / 200;
|
|
} else if (/rad$/.test(angle)) {
|
|
return num;
|
|
} else if (/turn$/.test(angle)) {
|
|
return Math.PI * num * 2;
|
|
} else if (/deg$/.test(angle)) {
|
|
return Math.PI * num / 180;
|
|
}
|
|
}
|
|
function setTransform(shape, m) {
|
|
m = new geo.Matrix(m[0], m[1], m[2], m[3], m[4], m[5]);
|
|
shape.transform(m);
|
|
return m;
|
|
}
|
|
function setClipping(shape, clipPath) {
|
|
shape.clip(clipPath);
|
|
}
|
|
function addArcToPath(path, x, y, options) {
|
|
var points = new geo.Arc([
|
|
x,
|
|
y
|
|
], options).curvePoints(), i = 1;
|
|
while (i < points.length) {
|
|
path.curveTo(points[i++], points[i++], points[i++]);
|
|
}
|
|
}
|
|
function sanitizeRadius(r) {
|
|
if (r.x <= 0 || r.y <= 0) {
|
|
r.x = r.y = 0;
|
|
}
|
|
return r;
|
|
}
|
|
function adjustBorderRadiusForBox(box, rTL, rTR, rBR, rBL) {
|
|
var tl_x = Math.max(0, rTL.x), tl_y = Math.max(0, rTL.y);
|
|
var tr_x = Math.max(0, rTR.x), tr_y = Math.max(0, rTR.y);
|
|
var br_x = Math.max(0, rBR.x), br_y = Math.max(0, rBR.y);
|
|
var bl_x = Math.max(0, rBL.x), bl_y = Math.max(0, rBL.y);
|
|
var f = Math.min(box.width / (tl_x + tr_x), box.height / (tr_y + br_y), box.width / (br_x + bl_x), box.height / (bl_y + tl_y));
|
|
if (f < 1) {
|
|
tl_x *= f;
|
|
tl_y *= f;
|
|
tr_x *= f;
|
|
tr_y *= f;
|
|
br_x *= f;
|
|
br_y *= f;
|
|
bl_x *= f;
|
|
bl_y *= f;
|
|
}
|
|
return {
|
|
tl: {
|
|
x: tl_x,
|
|
y: tl_y
|
|
},
|
|
tr: {
|
|
x: tr_x,
|
|
y: tr_y
|
|
},
|
|
br: {
|
|
x: br_x,
|
|
y: br_y
|
|
},
|
|
bl: {
|
|
x: bl_x,
|
|
y: bl_y
|
|
}
|
|
};
|
|
}
|
|
function elementRoundBox(element, box, type) {
|
|
var style = getComputedStyle(element);
|
|
var rTL = getBorderRadius(style, 'top-left');
|
|
var rTR = getBorderRadius(style, 'top-right');
|
|
var rBL = getBorderRadius(style, 'bottom-left');
|
|
var rBR = getBorderRadius(style, 'bottom-right');
|
|
if (type == 'padding' || type == 'content') {
|
|
var bt = getBorder(style, 'top');
|
|
var br = getBorder(style, 'right');
|
|
var bb = getBorder(style, 'bottom');
|
|
var bl = getBorder(style, 'left');
|
|
rTL.x -= bl.width;
|
|
rTL.y -= bt.width;
|
|
rTR.x -= br.width;
|
|
rTR.y -= bt.width;
|
|
rBR.x -= br.width;
|
|
rBR.y -= bb.width;
|
|
rBL.x -= bl.width;
|
|
rBL.y -= bb.width;
|
|
if (type == 'content') {
|
|
var pt = parseFloat(getPropertyValue(style, 'padding-top'));
|
|
var pr = parseFloat(getPropertyValue(style, 'padding-right'));
|
|
var pb = parseFloat(getPropertyValue(style, 'padding-bottom'));
|
|
var pl = parseFloat(getPropertyValue(style, 'padding-left'));
|
|
rTL.x -= pl;
|
|
rTL.y -= pt;
|
|
rTR.x -= pr;
|
|
rTR.y -= pt;
|
|
rBR.x -= pr;
|
|
rBR.y -= pb;
|
|
rBL.x -= pl;
|
|
rBL.y -= pb;
|
|
}
|
|
}
|
|
if (typeof type == 'number') {
|
|
rTL.x -= type;
|
|
rTL.y -= type;
|
|
rTR.x -= type;
|
|
rTR.y -= type;
|
|
rBR.x -= type;
|
|
rBR.y -= type;
|
|
rBL.x -= type;
|
|
rBL.y -= type;
|
|
}
|
|
return roundBox(box, rTL, rTR, rBR, rBL);
|
|
}
|
|
function roundBox(box, rTL0, rTR0, rBR0, rBL0) {
|
|
var tmp = adjustBorderRadiusForBox(box, rTL0, rTR0, rBR0, rBL0);
|
|
var rTL = tmp.tl;
|
|
var rTR = tmp.tr;
|
|
var rBR = tmp.br;
|
|
var rBL = tmp.bl;
|
|
var path = new drawing.Path({
|
|
fill: null,
|
|
stroke: null
|
|
});
|
|
path.moveTo(box.left, box.top + rTL.y);
|
|
if (rTL.x) {
|
|
addArcToPath(path, box.left + rTL.x, box.top + rTL.y, {
|
|
startAngle: -180,
|
|
endAngle: -90,
|
|
radiusX: rTL.x,
|
|
radiusY: rTL.y
|
|
});
|
|
}
|
|
path.lineTo(box.right - rTR.x, box.top);
|
|
if (rTR.x) {
|
|
addArcToPath(path, box.right - rTR.x, box.top + rTR.y, {
|
|
startAngle: -90,
|
|
endAngle: 0,
|
|
radiusX: rTR.x,
|
|
radiusY: rTR.y
|
|
});
|
|
}
|
|
path.lineTo(box.right, box.bottom - rBR.y);
|
|
if (rBR.x) {
|
|
addArcToPath(path, box.right - rBR.x, box.bottom - rBR.y, {
|
|
startAngle: 0,
|
|
endAngle: 90,
|
|
radiusX: rBR.x,
|
|
radiusY: rBR.y
|
|
});
|
|
}
|
|
path.lineTo(box.left + rBL.x, box.bottom);
|
|
if (rBL.x) {
|
|
addArcToPath(path, box.left + rBL.x, box.bottom - rBL.y, {
|
|
startAngle: 90,
|
|
endAngle: 180,
|
|
radiusX: rBL.x,
|
|
radiusY: rBL.y
|
|
});
|
|
}
|
|
return path.close();
|
|
}
|
|
function formatCounter(val, style) {
|
|
var str = parseFloat(val) + '';
|
|
switch (style) {
|
|
case 'decimal-leading-zero':
|
|
if (str.length < 2) {
|
|
str = '0' + str;
|
|
}
|
|
return str;
|
|
case 'lower-roman':
|
|
return romanNumeral(val).toLowerCase();
|
|
case 'upper-roman':
|
|
return romanNumeral(val).toUpperCase();
|
|
case 'lower-latin':
|
|
case 'lower-alpha':
|
|
return alphaNumeral(val - 1);
|
|
case 'upper-latin':
|
|
case 'upper-alpha':
|
|
return alphaNumeral(val - 1).toUpperCase();
|
|
default:
|
|
return str;
|
|
}
|
|
}
|
|
function evalPseudoElementContent(element, content) {
|
|
function displayCounter(name, style, separator) {
|
|
if (!separator) {
|
|
return formatCounter(getCounter(name) || 0, style);
|
|
}
|
|
separator = separator.replace(/^\s*(["'])(.*)\1\s*$/, '$2');
|
|
return getAllCounters(name).map(function (val) {
|
|
return formatCounter(val, style);
|
|
}).join(separator);
|
|
}
|
|
var a = splitProperty(content, /^\s+/);
|
|
var result = [], m;
|
|
a.forEach(function (el) {
|
|
var tmp;
|
|
if (m = /^\s*(["'])(.*)\1\s*$/.exec(el)) {
|
|
result.push(m[2].replace(/\\([0-9a-f]{4})/gi, function (s, p) {
|
|
return String.fromCharCode(parseInt(p, 16));
|
|
}));
|
|
} else if (m = /^\s*counter\((.*?)\)\s*$/.exec(el)) {
|
|
tmp = splitProperty(m[1]);
|
|
result.push(displayCounter(tmp[0], tmp[1]));
|
|
} else if (m = /^\s*counters\((.*?)\)\s*$/.exec(el)) {
|
|
tmp = splitProperty(m[1]);
|
|
result.push(displayCounter(tmp[0], tmp[2], tmp[1]));
|
|
} else if (m = /^\s*attr\((.*?)\)\s*$/.exec(el)) {
|
|
result.push(element.getAttribute(m[1]) || '');
|
|
} else {
|
|
result.push(el);
|
|
}
|
|
});
|
|
return result.join('');
|
|
}
|
|
function getCssText(style) {
|
|
if (style.cssText) {
|
|
return style.cssText;
|
|
}
|
|
var result = [];
|
|
for (var i = 0; i < style.length; ++i) {
|
|
result.push(style[i] + ': ' + getPropertyValue(style, style[i]));
|
|
}
|
|
return result.join(';\n');
|
|
}
|
|
function _renderWithPseudoElements(element, group) {
|
|
if (element.tagName == KENDO_PSEUDO_ELEMENT) {
|
|
_renderElement(element, group);
|
|
return;
|
|
}
|
|
var fake = [];
|
|
function pseudo(kind, place) {
|
|
var style = getComputedStyle(element, kind);
|
|
if (style.content && style.content != 'normal' && style.content != 'none' && style.width != '0px') {
|
|
var psel = element.ownerDocument.createElement(KENDO_PSEUDO_ELEMENT);
|
|
psel.style.cssText = getCssText(style);
|
|
psel.textContent = evalPseudoElementContent(element, style.content);
|
|
element.insertBefore(psel, place);
|
|
fake.push(psel);
|
|
}
|
|
}
|
|
pseudo(':before', element.firstChild);
|
|
pseudo(':after', null);
|
|
var saveClass = element.className;
|
|
element.className += ' kendo-pdf-hide-pseudo-elements';
|
|
_renderElement(element, group);
|
|
element.className = saveClass;
|
|
fake.forEach(function (el) {
|
|
element.removeChild(el);
|
|
});
|
|
}
|
|
function _renderElement(element, group) {
|
|
var style = getComputedStyle(element);
|
|
var top = getBorder(style, 'top');
|
|
var right = getBorder(style, 'right');
|
|
var bottom = getBorder(style, 'bottom');
|
|
var left = getBorder(style, 'left');
|
|
var rTL0 = getBorderRadius(style, 'top-left');
|
|
var rTR0 = getBorderRadius(style, 'top-right');
|
|
var rBL0 = getBorderRadius(style, 'bottom-left');
|
|
var rBR0 = getBorderRadius(style, 'bottom-right');
|
|
var dir = getPropertyValue(style, 'direction');
|
|
var backgroundColor = getPropertyValue(style, 'background-color');
|
|
backgroundColor = parseColor(backgroundColor);
|
|
var backgroundImage = parseBackgroundImage(getPropertyValue(style, 'background-image'));
|
|
var backgroundRepeat = splitProperty(getPropertyValue(style, 'background-repeat'));
|
|
var backgroundPosition = splitProperty(getPropertyValue(style, 'background-position'));
|
|
var backgroundOrigin = splitProperty(getPropertyValue(style, 'background-origin'));
|
|
var backgroundSize = splitProperty(getPropertyValue(style, 'background-size'));
|
|
if (browser.msie && browser.version < 10) {
|
|
backgroundPosition = splitProperty(element.currentStyle.backgroundPosition);
|
|
}
|
|
var innerbox = innerBox(element.getBoundingClientRect(), 'border-*-width', element);
|
|
(function () {
|
|
var clip = getPropertyValue(style, 'clip');
|
|
var m = /^\s*rect\((.*)\)\s*$/.exec(clip);
|
|
if (m) {
|
|
var a = m[1].split(/[ ,]+/g);
|
|
var top = a[0] == 'auto' ? innerbox.top : parseFloat(a[0]) + innerbox.top;
|
|
var right = a[1] == 'auto' ? innerbox.right : parseFloat(a[1]) + innerbox.left;
|
|
var bottom = a[2] == 'auto' ? innerbox.bottom : parseFloat(a[2]) + innerbox.top;
|
|
var left = a[3] == 'auto' ? innerbox.left : parseFloat(a[3]) + innerbox.left;
|
|
var tmp = new drawing.Group();
|
|
var clipPath = new drawing.Path().moveTo(left, top).lineTo(right, top).lineTo(right, bottom).lineTo(left, bottom).close();
|
|
setClipping(tmp, clipPath);
|
|
group.append(tmp);
|
|
group = tmp;
|
|
updateClipbox(clipPath);
|
|
}
|
|
}());
|
|
var boxes, i, cells;
|
|
var display = getPropertyValue(style, 'display');
|
|
if (display == 'table-row') {
|
|
boxes = [];
|
|
for (i = 0, cells = element.children; i < cells.length; ++i) {
|
|
boxes.push(cells[i].getBoundingClientRect());
|
|
}
|
|
} else {
|
|
boxes = element.getClientRects();
|
|
if (boxes.length == 1) {
|
|
boxes = [element.getBoundingClientRect()];
|
|
}
|
|
}
|
|
boxes = adjustBoxes(boxes);
|
|
for (i = 0; i < boxes.length; ++i) {
|
|
drawOneBox(boxes[i], i === 0, i == boxes.length - 1);
|
|
}
|
|
if (boxes.length > 0 && display == 'list-item') {
|
|
drawBullet(boxes[0]);
|
|
}
|
|
(function () {
|
|
function clipit() {
|
|
var clipPath = elementRoundBox(element, innerbox, 'padding');
|
|
var tmp = new drawing.Group();
|
|
setClipping(tmp, clipPath);
|
|
group.append(tmp);
|
|
group = tmp;
|
|
updateClipbox(clipPath);
|
|
}
|
|
if (isFormField(element)) {
|
|
clipit();
|
|
} else if (/^(hidden|auto|scroll)/.test(getPropertyValue(style, 'overflow'))) {
|
|
clipit();
|
|
} else if (/^(hidden|auto|scroll)/.test(getPropertyValue(style, 'overflow-x'))) {
|
|
clipit();
|
|
} else if (/^(hidden|auto|scroll)/.test(getPropertyValue(style, 'overflow-y'))) {
|
|
clipit();
|
|
}
|
|
}());
|
|
if (!maybeRenderWidget(element, group)) {
|
|
renderContents(element, group);
|
|
}
|
|
return group;
|
|
function adjustBoxes(boxes) {
|
|
if (/^td$/i.test(element.tagName)) {
|
|
var table = nodeInfo.table;
|
|
if (table && getPropertyValue(table.style, 'border-collapse') == 'collapse') {
|
|
var tableBorderLeft = getBorder(table.style, 'left').width;
|
|
var tableBorderTop = getBorder(table.style, 'top').width;
|
|
if (tableBorderLeft === 0 && tableBorderTop === 0) {
|
|
return boxes;
|
|
}
|
|
var tableBox = table.element.getBoundingClientRect();
|
|
var firstCell = table.element.rows[0].cells[0];
|
|
var firstCellBox = firstCell.getBoundingClientRect();
|
|
if (firstCellBox.top == tableBox.top || firstCellBox.left == tableBox.left) {
|
|
return slice.call(boxes).map(function (box) {
|
|
return {
|
|
left: box.left + tableBorderLeft,
|
|
top: box.top + tableBorderTop,
|
|
right: box.right + tableBorderLeft,
|
|
bottom: box.bottom + tableBorderTop,
|
|
height: box.height,
|
|
width: box.width
|
|
};
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return boxes;
|
|
}
|
|
function drawEdge(color, len, Wtop, Wleft, Wright, rl, rr, transform) {
|
|
if (Wtop <= 0) {
|
|
return;
|
|
}
|
|
var path, edge = new drawing.Group();
|
|
setTransform(edge, transform);
|
|
group.append(edge);
|
|
sanitizeRadius(rl);
|
|
sanitizeRadius(rr);
|
|
path = new drawing.Path({
|
|
fill: { color: color },
|
|
stroke: null
|
|
});
|
|
edge.append(path);
|
|
path.moveTo(rl.x ? Math.max(rl.x, Wleft) : 0, 0).lineTo(len - (rr.x ? Math.max(rr.x, Wright) : 0), 0).lineTo(len - Math.max(rr.x, Wright), Wtop).lineTo(Math.max(rl.x, Wleft), Wtop).close();
|
|
if (rl.x) {
|
|
drawRoundCorner(Wleft, rl, [
|
|
-1,
|
|
0,
|
|
0,
|
|
1,
|
|
rl.x,
|
|
0
|
|
]);
|
|
}
|
|
if (rr.x) {
|
|
drawRoundCorner(Wright, rr, [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
len - rr.x,
|
|
0
|
|
]);
|
|
}
|
|
function drawRoundCorner(Wright, r, transform) {
|
|
var angle = Math.PI / 2 * Wright / (Wright + Wtop);
|
|
var ri = {
|
|
x: r.x - Wright,
|
|
y: r.y - Wtop
|
|
};
|
|
var path = new drawing.Path({
|
|
fill: { color: color },
|
|
stroke: null
|
|
}).moveTo(0, 0);
|
|
setTransform(path, transform);
|
|
addArcToPath(path, 0, r.y, {
|
|
startAngle: -90,
|
|
endAngle: -radiansToDegrees(angle),
|
|
radiusX: r.x,
|
|
radiusY: r.y
|
|
});
|
|
if (ri.x > 0 && ri.y > 0) {
|
|
path.lineTo(ri.x * Math.cos(angle), r.y - ri.y * Math.sin(angle));
|
|
addArcToPath(path, 0, r.y, {
|
|
startAngle: -radiansToDegrees(angle),
|
|
endAngle: -90,
|
|
radiusX: ri.x,
|
|
radiusY: ri.y,
|
|
anticlockwise: true
|
|
});
|
|
} else if (ri.x > 0) {
|
|
path.lineTo(ri.x, Wtop).lineTo(0, Wtop);
|
|
} else {
|
|
path.lineTo(ri.x, Wtop).lineTo(ri.x, 0);
|
|
}
|
|
edge.append(path.close());
|
|
}
|
|
}
|
|
function drawBackground(box) {
|
|
var background = new drawing.Group();
|
|
setClipping(background, roundBox(box, rTL0, rTR0, rBR0, rBL0));
|
|
group.append(background);
|
|
if (element.tagName == 'A' && element.href && !/^#?$/.test($(element).attr('href'))) {
|
|
if (!nodeInfo._avoidLinks || !$(element).is(nodeInfo._avoidLinks)) {
|
|
background._pdfLink = {
|
|
url: element.href,
|
|
top: box.top,
|
|
right: box.right,
|
|
bottom: box.bottom,
|
|
left: box.left
|
|
};
|
|
}
|
|
}
|
|
if (backgroundColor) {
|
|
var path = new drawing.Path({
|
|
fill: { color: backgroundColor.toCssRgba() },
|
|
stroke: null
|
|
});
|
|
path.moveTo(box.left, box.top).lineTo(box.right, box.top).lineTo(box.right, box.bottom).lineTo(box.left, box.bottom).close();
|
|
background.append(path);
|
|
}
|
|
for (var i = backgroundImage.length; --i >= 0;) {
|
|
drawOneBackground(background, box, backgroundImage[i], backgroundRepeat[i % backgroundRepeat.length], backgroundPosition[i % backgroundPosition.length], backgroundOrigin[i % backgroundOrigin.length], backgroundSize[i % backgroundSize.length]);
|
|
}
|
|
}
|
|
function drawOneBackground(group, box, background, backgroundRepeat, backgroundPosition, backgroundOrigin, backgroundSize) {
|
|
if (!background || background == 'none') {
|
|
return;
|
|
}
|
|
if (background.type == 'url') {
|
|
if (/^url\(\"data:image\/svg/i.test(background.url)) {
|
|
return;
|
|
}
|
|
var img = IMAGE_CACHE[background.url];
|
|
if (img && img.width > 0 && img.height > 0) {
|
|
drawBackgroundImage(group, box, img.width, img.height, function (group, rect) {
|
|
group.append(new drawing.Image(background.url, rect));
|
|
});
|
|
}
|
|
} else if (background.type == 'linear') {
|
|
drawBackgroundImage(group, box, box.width, box.height, gradientRenderer(background));
|
|
} else {
|
|
return;
|
|
}
|
|
function drawBackgroundImage(group, box, img_width, img_height, renderBG) {
|
|
var aspect_ratio = img_width / img_height, f;
|
|
var orgBox = box;
|
|
if (backgroundOrigin == 'content-box') {
|
|
orgBox = innerBox(orgBox, 'border-*-width', element);
|
|
orgBox = innerBox(orgBox, 'padding-*', element);
|
|
} else if (backgroundOrigin == 'padding-box') {
|
|
orgBox = innerBox(orgBox, 'border-*-width', element);
|
|
}
|
|
if (!/^\s*auto(\s+auto)?\s*$/.test(backgroundSize)) {
|
|
if (backgroundSize == 'contain') {
|
|
f = Math.min(orgBox.width / img_width, orgBox.height / img_height);
|
|
img_width *= f;
|
|
img_height *= f;
|
|
} else if (backgroundSize == 'cover') {
|
|
f = Math.max(orgBox.width / img_width, orgBox.height / img_height);
|
|
img_width *= f;
|
|
img_height *= f;
|
|
} else {
|
|
var size = backgroundSize.split(/\s+/g);
|
|
if (/%$/.test(size[0])) {
|
|
img_width = orgBox.width * parseFloat(size[0]) / 100;
|
|
} else {
|
|
img_width = parseFloat(size[0]);
|
|
}
|
|
if (size.length == 1 || size[1] == 'auto') {
|
|
img_height = img_width / aspect_ratio;
|
|
} else if (/%$/.test(size[1])) {
|
|
img_height = orgBox.height * parseFloat(size[1]) / 100;
|
|
} else {
|
|
img_height = parseFloat(size[1]);
|
|
}
|
|
}
|
|
}
|
|
var pos = (backgroundPosition + '').split(/\s+/);
|
|
if (pos.length == 1) {
|
|
pos[1] = '50%';
|
|
}
|
|
if (/%$/.test(pos[0])) {
|
|
pos[0] = parseFloat(pos[0]) / 100 * (orgBox.width - img_width);
|
|
} else {
|
|
pos[0] = parseFloat(pos[0]);
|
|
}
|
|
if (/%$/.test(pos[1])) {
|
|
pos[1] = parseFloat(pos[1]) / 100 * (orgBox.height - img_height);
|
|
} else {
|
|
pos[1] = parseFloat(pos[1]);
|
|
}
|
|
var rect = new geo.Rect([
|
|
orgBox.left + pos[0],
|
|
orgBox.top + pos[1]
|
|
], [
|
|
img_width,
|
|
img_height
|
|
]);
|
|
function rewX() {
|
|
while (rect.origin.x > box.left) {
|
|
rect.origin.x -= img_width;
|
|
}
|
|
}
|
|
function rewY() {
|
|
while (rect.origin.y > box.top) {
|
|
rect.origin.y -= img_height;
|
|
}
|
|
}
|
|
function repeatX() {
|
|
while (rect.origin.x < box.right) {
|
|
renderBG(group, rect.clone());
|
|
rect.origin.x += img_width;
|
|
}
|
|
}
|
|
if (backgroundRepeat == 'no-repeat') {
|
|
renderBG(group, rect);
|
|
} else if (backgroundRepeat == 'repeat-x') {
|
|
rewX();
|
|
repeatX();
|
|
} else if (backgroundRepeat == 'repeat-y') {
|
|
rewY();
|
|
while (rect.origin.y < box.bottom) {
|
|
renderBG(group, rect.clone());
|
|
rect.origin.y += img_height;
|
|
}
|
|
} else if (backgroundRepeat == 'repeat') {
|
|
rewX();
|
|
rewY();
|
|
var origin = rect.origin.clone();
|
|
while (rect.origin.y < box.bottom) {
|
|
rect.origin.x = origin.x;
|
|
repeatX();
|
|
rect.origin.y += img_height;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function drawBullet() {
|
|
var listStyleType = getPropertyValue(style, 'list-style-type');
|
|
if (listStyleType == 'none') {
|
|
return;
|
|
}
|
|
var listStylePosition = getPropertyValue(style, 'list-style-position');
|
|
function _drawBullet(f) {
|
|
saveStyle(element, function () {
|
|
element.style.position = 'relative';
|
|
var bullet = element.ownerDocument.createElement(KENDO_PSEUDO_ELEMENT);
|
|
bullet.style.position = 'absolute';
|
|
bullet.style.boxSizing = 'border-box';
|
|
if (listStylePosition == 'outside') {
|
|
bullet.style.width = '6em';
|
|
bullet.style.left = '-6.8em';
|
|
bullet.style.textAlign = 'right';
|
|
} else {
|
|
bullet.style.left = '0px';
|
|
}
|
|
f(bullet);
|
|
element.insertBefore(bullet, element.firstChild);
|
|
renderElement(bullet, group);
|
|
element.removeChild(bullet);
|
|
});
|
|
}
|
|
function elementIndex(f) {
|
|
var a = element.parentNode.children;
|
|
var k = element.getAttribute('kendo-split-index');
|
|
if (k != null) {
|
|
return f(k | 0, a.length);
|
|
}
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (a[i] === element) {
|
|
return f(i, a.length);
|
|
}
|
|
}
|
|
}
|
|
switch (listStyleType) {
|
|
case 'circle':
|
|
case 'disc':
|
|
case 'square':
|
|
_drawBullet(function (bullet) {
|
|
bullet.style.fontSize = '60%';
|
|
bullet.style.lineHeight = '200%';
|
|
bullet.style.paddingRight = '0.5em';
|
|
bullet.style.fontFamily = 'DejaVu Serif';
|
|
bullet.innerHTML = {
|
|
'disc': '\u25CF',
|
|
'circle': '\u25EF',
|
|
'square': '\u25A0'
|
|
}[listStyleType];
|
|
});
|
|
break;
|
|
case 'decimal':
|
|
case 'decimal-leading-zero':
|
|
_drawBullet(function (bullet) {
|
|
elementIndex(function (idx) {
|
|
++idx;
|
|
if (listStyleType == 'decimal-leading-zero' && (idx + '').length < 2) {
|
|
idx = '0' + idx;
|
|
}
|
|
bullet.innerHTML = idx + '.';
|
|
});
|
|
});
|
|
break;
|
|
case 'lower-roman':
|
|
case 'upper-roman':
|
|
_drawBullet(function (bullet) {
|
|
elementIndex(function (idx) {
|
|
idx = romanNumeral(idx + 1);
|
|
if (listStyleType == 'upper-roman') {
|
|
idx = idx.toUpperCase();
|
|
}
|
|
bullet.innerHTML = idx + '.';
|
|
});
|
|
});
|
|
break;
|
|
case 'lower-latin':
|
|
case 'lower-alpha':
|
|
case 'upper-latin':
|
|
case 'upper-alpha':
|
|
_drawBullet(function (bullet) {
|
|
elementIndex(function (idx) {
|
|
idx = alphaNumeral(idx);
|
|
if (/^upper/i.test(listStyleType)) {
|
|
idx = idx.toUpperCase();
|
|
}
|
|
bullet.innerHTML = idx + '.';
|
|
});
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
function drawOneBox(box, isFirst, isLast) {
|
|
if (box.width === 0 || box.height === 0) {
|
|
return;
|
|
}
|
|
drawBackground(box);
|
|
var shouldDrawLeft = left.width > 0 && (isFirst && dir == 'ltr' || isLast && dir == 'rtl');
|
|
var shouldDrawRight = right.width > 0 && (isLast && dir == 'ltr' || isFirst && dir == 'rtl');
|
|
if (top.width === 0 && left.width === 0 && right.width === 0 && bottom.width === 0) {
|
|
return;
|
|
}
|
|
if (true) {
|
|
if (top.color == right.color && top.color == bottom.color && top.color == left.color) {
|
|
if (top.width == right.width && top.width == bottom.width && top.width == left.width) {
|
|
if (shouldDrawLeft && shouldDrawRight) {
|
|
box = innerBox(box, top.width / 2);
|
|
var path = elementRoundBox(element, box, top.width / 2);
|
|
path.options.stroke = {
|
|
color: top.color,
|
|
width: top.width
|
|
};
|
|
group.append(path);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (rTL0.x === 0 && rTR0.x === 0 && rBR0.x === 0 && rBL0.x === 0) {
|
|
if (top.width < 2 && left.width < 2 && right.width < 2 && bottom.width < 2) {
|
|
if (top.width > 0) {
|
|
group.append(new drawing.Path({
|
|
stroke: {
|
|
width: top.width,
|
|
color: top.color
|
|
}
|
|
}).moveTo(box.left, box.top + top.width / 2).lineTo(box.right, box.top + top.width / 2));
|
|
}
|
|
if (bottom.width > 0) {
|
|
group.append(new drawing.Path({
|
|
stroke: {
|
|
width: bottom.width,
|
|
color: bottom.color
|
|
}
|
|
}).moveTo(box.left, box.bottom - bottom.width / 2).lineTo(box.right, box.bottom - bottom.width / 2));
|
|
}
|
|
if (shouldDrawLeft) {
|
|
group.append(new drawing.Path({
|
|
stroke: {
|
|
width: left.width,
|
|
color: left.color
|
|
}
|
|
}).moveTo(box.left + left.width / 2, box.top).lineTo(box.left + left.width / 2, box.bottom));
|
|
}
|
|
if (shouldDrawRight) {
|
|
group.append(new drawing.Path({
|
|
stroke: {
|
|
width: right.width,
|
|
color: right.color
|
|
}
|
|
}).moveTo(box.right - right.width / 2, box.top).lineTo(box.right - right.width / 2, box.bottom));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
var tmp = adjustBorderRadiusForBox(box, rTL0, rTR0, rBR0, rBL0);
|
|
var rTL = tmp.tl;
|
|
var rTR = tmp.tr;
|
|
var rBR = tmp.br;
|
|
var rBL = tmp.bl;
|
|
drawEdge(top.color, box.width, top.width, left.width, right.width, rTL, rTR, [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
box.left,
|
|
box.top
|
|
]);
|
|
drawEdge(bottom.color, box.width, bottom.width, right.width, left.width, rBR, rBL, [
|
|
-1,
|
|
0,
|
|
0,
|
|
-1,
|
|
box.right,
|
|
box.bottom
|
|
]);
|
|
function inv(p) {
|
|
return {
|
|
x: p.y,
|
|
y: p.x
|
|
};
|
|
}
|
|
drawEdge(left.color, box.height, left.width, bottom.width, top.width, inv(rBL), inv(rTL), [
|
|
0,
|
|
-1,
|
|
1,
|
|
0,
|
|
box.left,
|
|
box.bottom
|
|
]);
|
|
drawEdge(right.color, box.height, right.width, top.width, bottom.width, inv(rTR), inv(rBR), [
|
|
0,
|
|
1,
|
|
-1,
|
|
0,
|
|
box.right,
|
|
box.top
|
|
]);
|
|
}
|
|
}
|
|
function gradientRenderer(gradient) {
|
|
return function (group, rect) {
|
|
var width = rect.width(), height = rect.height();
|
|
switch (gradient.type) {
|
|
case 'linear':
|
|
var angle = gradient.angle != null ? gradient.angle : Math.PI;
|
|
switch (gradient.to) {
|
|
case 'top':
|
|
angle = 0;
|
|
break;
|
|
case 'left':
|
|
angle = -Math.PI / 2;
|
|
break;
|
|
case 'bottom':
|
|
angle = Math.PI;
|
|
break;
|
|
case 'right':
|
|
angle = Math.PI / 2;
|
|
break;
|
|
case 'top left':
|
|
case 'left top':
|
|
angle = -Math.atan2(height, width);
|
|
break;
|
|
case 'top right':
|
|
case 'right top':
|
|
angle = Math.atan2(height, width);
|
|
break;
|
|
case 'bottom left':
|
|
case 'left bottom':
|
|
angle = Math.PI + Math.atan2(height, width);
|
|
break;
|
|
case 'bottom right':
|
|
case 'right bottom':
|
|
angle = Math.PI - Math.atan2(height, width);
|
|
break;
|
|
}
|
|
if (gradient.reverse) {
|
|
angle -= Math.PI;
|
|
}
|
|
angle %= 2 * Math.PI;
|
|
if (angle < 0) {
|
|
angle += 2 * Math.PI;
|
|
}
|
|
var pxlen = Math.abs(width * Math.sin(angle)) + Math.abs(height * Math.cos(angle));
|
|
var scaledAngle = Math.atan(width * Math.tan(angle) / height);
|
|
var sin = Math.sin(scaledAngle), cos = Math.cos(scaledAngle);
|
|
var len = Math.abs(sin) + Math.abs(cos);
|
|
var x = len / 2 * sin;
|
|
var y = len / 2 * cos;
|
|
if (angle > Math.PI / 2 && angle <= 3 * Math.PI / 2) {
|
|
x = -x;
|
|
y = -y;
|
|
}
|
|
var implicit = [], right = 0;
|
|
var stops = gradient.stops.map(function (s, i) {
|
|
var offset = s.percent;
|
|
if (offset) {
|
|
offset = parseFloat(offset) / 100;
|
|
} else if (s.length) {
|
|
offset = parseFloat(s.length) / pxlen;
|
|
} else if (i === 0) {
|
|
offset = 0;
|
|
} else if (i == gradient.stops.length - 1) {
|
|
offset = 1;
|
|
}
|
|
var stop = {
|
|
color: s.color.toCssRgba(),
|
|
offset: offset
|
|
};
|
|
if (offset != null) {
|
|
right = offset;
|
|
implicit.forEach(function (s, i) {
|
|
var stop = s.stop;
|
|
stop.offset = s.left + (right - s.left) * (i + 1) / (implicit.length + 1);
|
|
});
|
|
implicit = [];
|
|
} else {
|
|
implicit.push({
|
|
left: right,
|
|
stop: stop
|
|
});
|
|
}
|
|
return stop;
|
|
});
|
|
var start = [
|
|
0.5 - x,
|
|
0.5 + y
|
|
];
|
|
var end = [
|
|
0.5 + x,
|
|
0.5 - y
|
|
];
|
|
group.append(drawing.Path.fromRect(rect).stroke(null).fill(new drawing.LinearGradient({
|
|
start: start,
|
|
end: end,
|
|
stops: stops,
|
|
userSpace: false
|
|
})));
|
|
break;
|
|
case 'radial':
|
|
if (window.console && window.console.log) {
|
|
window.console.log('Radial gradients are not yet supported in HTML renderer');
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
function maybeRenderWidget(element, group) {
|
|
if (element.getAttribute(kendo.attr('role'))) {
|
|
var widget = kendo.widgetInstance($(element));
|
|
if (widget && (widget.exportDOMVisual || widget.exportVisual)) {
|
|
var visual;
|
|
if (widget.exportDOMVisual) {
|
|
visual = widget.exportDOMVisual();
|
|
} else {
|
|
visual = widget.exportVisual();
|
|
}
|
|
if (!visual) {
|
|
return false;
|
|
}
|
|
var wrap = new drawing.Group();
|
|
wrap.children.push(visual);
|
|
var bbox = element.getBoundingClientRect();
|
|
wrap.transform(geo.transform().translate(bbox.left, bbox.top));
|
|
group.append(wrap);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
function renderImage(element, url, group) {
|
|
var box = getContentBox(element);
|
|
var rect = new geo.Rect([
|
|
box.left,
|
|
box.top
|
|
], [
|
|
box.width,
|
|
box.height
|
|
]);
|
|
var image = new drawing.Image(url, rect);
|
|
setClipping(image, elementRoundBox(element, box, 'content'));
|
|
group.append(image);
|
|
}
|
|
function zIndexSort(a, b) {
|
|
var sa = getComputedStyle(a);
|
|
var sb = getComputedStyle(b);
|
|
var za = parseFloat(getPropertyValue(sa, 'z-index'));
|
|
var zb = parseFloat(getPropertyValue(sb, 'z-index'));
|
|
var pa = getPropertyValue(sa, 'position');
|
|
var pb = getPropertyValue(sb, 'position');
|
|
if (isNaN(za) && isNaN(zb)) {
|
|
if (/static|absolute/.test(pa) && /static|absolute/.test(pb)) {
|
|
return 0;
|
|
}
|
|
if (pa == 'static') {
|
|
return -1;
|
|
}
|
|
if (pb == 'static') {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
if (isNaN(za)) {
|
|
return zb === 0 ? 0 : zb > 0 ? -1 : 1;
|
|
}
|
|
if (isNaN(zb)) {
|
|
return za === 0 ? 0 : za > 0 ? 1 : -1;
|
|
}
|
|
return parseFloat(za) - parseFloat(zb);
|
|
}
|
|
function isFormField(element) {
|
|
return /^(?:textarea|select|input)$/i.test(element.tagName);
|
|
}
|
|
function getSelectedOption(element) {
|
|
if (element.selectedOptions && element.selectedOptions.length > 0) {
|
|
return element.selectedOptions[0];
|
|
}
|
|
return element.options[element.selectedIndex];
|
|
}
|
|
function renderCheckbox(element, group) {
|
|
var style = getComputedStyle(element);
|
|
var color = getPropertyValue(style, 'color');
|
|
var box = element.getBoundingClientRect();
|
|
if (element.type == 'checkbox') {
|
|
group.append(drawing.Path.fromRect(new geo.Rect([
|
|
box.left + 1,
|
|
box.top + 1
|
|
], [
|
|
box.width - 2,
|
|
box.height - 2
|
|
])).stroke(color, 1));
|
|
if (element.checked) {
|
|
group.append(new drawing.Path().stroke(color, 1.2).moveTo(box.left + 0.22 * box.width, box.top + 0.55 * box.height).lineTo(box.left + 0.45 * box.width, box.top + 0.75 * box.height).lineTo(box.left + 0.78 * box.width, box.top + 0.22 * box.width));
|
|
}
|
|
} else {
|
|
group.append(new drawing.Circle(new geo.Circle([
|
|
(box.left + box.right) / 2,
|
|
(box.top + box.bottom) / 2
|
|
], Math.min(box.width - 2, box.height - 2) / 2)).stroke(color, 1));
|
|
if (element.checked) {
|
|
group.append(new drawing.Circle(new geo.Circle([
|
|
(box.left + box.right) / 2,
|
|
(box.top + box.bottom) / 2
|
|
], Math.min(box.width - 8, box.height - 8) / 2)).fill(color).stroke(null));
|
|
}
|
|
}
|
|
}
|
|
function renderFormField(element, group) {
|
|
var tag = element.tagName.toLowerCase();
|
|
if (tag == 'input' && (element.type == 'checkbox' || element.type == 'radio')) {
|
|
return renderCheckbox(element, group);
|
|
}
|
|
var p = element.parentNode;
|
|
var doc = element.ownerDocument;
|
|
var el = doc.createElement(KENDO_PSEUDO_ELEMENT);
|
|
var option;
|
|
el.style.cssText = getCssText(getComputedStyle(element));
|
|
if (tag == 'input') {
|
|
el.style.whiteSpace = 'pre';
|
|
}
|
|
if (tag == 'select' || tag == 'textarea') {
|
|
el.style.overflow = 'auto';
|
|
}
|
|
if (tag == 'select') {
|
|
if (element.multiple) {
|
|
for (var i = 0; i < element.options.length; ++i) {
|
|
option = doc.createElement(KENDO_PSEUDO_ELEMENT);
|
|
option.style.cssText = getCssText(getComputedStyle(element.options[i]));
|
|
option.style.display = 'block';
|
|
option.textContent = element.options[i].textContent;
|
|
el.appendChild(option);
|
|
}
|
|
} else {
|
|
option = getSelectedOption(element);
|
|
if (option) {
|
|
el.textContent = option.textContent;
|
|
}
|
|
}
|
|
} else {
|
|
el.textContent = element.value;
|
|
}
|
|
p.insertBefore(el, element);
|
|
el.scrollLeft = element.scrollLeft;
|
|
el.scrollTop = element.scrollTop;
|
|
renderContents(el, group);
|
|
p.removeChild(el);
|
|
}
|
|
function renderContents(element, group) {
|
|
if (nodeInfo._stackingContext.element === element) {
|
|
nodeInfo._stackingContext.group = group;
|
|
}
|
|
switch (element.tagName.toLowerCase()) {
|
|
case 'img':
|
|
renderImage(element, element.src, group);
|
|
break;
|
|
case 'canvas':
|
|
try {
|
|
renderImage(element, element.toDataURL('image/png'), group);
|
|
} catch (ex) {
|
|
}
|
|
break;
|
|
case 'textarea':
|
|
case 'input':
|
|
case 'select':
|
|
renderFormField(element, group);
|
|
break;
|
|
default:
|
|
var blocks = [], floats = [], inline = [], positioned = [];
|
|
for (var i = element.firstChild; i; i = i.nextSibling) {
|
|
switch (i.nodeType) {
|
|
case 3:
|
|
if (/\S/.test(i.data)) {
|
|
renderText(element, i, group);
|
|
}
|
|
break;
|
|
case 1:
|
|
var style = getComputedStyle(i);
|
|
var display = getPropertyValue(style, 'display');
|
|
var floating = getPropertyValue(style, 'float');
|
|
var position = getPropertyValue(style, 'position');
|
|
if (position != 'static') {
|
|
positioned.push(i);
|
|
} else if (display != 'inline') {
|
|
if (floating != 'none') {
|
|
floats.push(i);
|
|
} else {
|
|
blocks.push(i);
|
|
}
|
|
} else {
|
|
inline.push(i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
blocks.sort(zIndexSort).forEach(function (el) {
|
|
renderElement(el, group);
|
|
});
|
|
floats.sort(zIndexSort).forEach(function (el) {
|
|
renderElement(el, group);
|
|
});
|
|
inline.sort(zIndexSort).forEach(function (el) {
|
|
renderElement(el, group);
|
|
});
|
|
positioned.sort(zIndexSort).forEach(function (el) {
|
|
renderElement(el, group);
|
|
});
|
|
}
|
|
}
|
|
function renderText(element, node, group) {
|
|
if (emptyClipbox()) {
|
|
return;
|
|
}
|
|
var style = getComputedStyle(element);
|
|
if (parseFloat(getPropertyValue(style, 'text-indent')) < -500) {
|
|
return;
|
|
}
|
|
var text = node.data;
|
|
var start = 0;
|
|
var end = text.search(/\S\s*$/) + 1;
|
|
if (!end) {
|
|
return;
|
|
}
|
|
var fontSize = getPropertyValue(style, 'font-size');
|
|
var lineHeight = getPropertyValue(style, 'line-height');
|
|
var font = [
|
|
getPropertyValue(style, 'font-style'),
|
|
getPropertyValue(style, 'font-variant'),
|
|
getPropertyValue(style, 'font-weight'),
|
|
fontSize,
|
|
getPropertyValue(style, 'font-family')
|
|
].join(' ');
|
|
fontSize = parseFloat(fontSize);
|
|
lineHeight = parseFloat(lineHeight);
|
|
if (fontSize === 0) {
|
|
return;
|
|
}
|
|
var color = getPropertyValue(style, 'color');
|
|
var range = element.ownerDocument.createRange();
|
|
var align = getPropertyValue(style, 'text-align');
|
|
var isJustified = align == 'justify';
|
|
var whiteSpace = getPropertyValue(style, 'white-space');
|
|
var textOverflow, saveTextOverflow;
|
|
if (browser.msie) {
|
|
textOverflow = style.textOverflow;
|
|
if (textOverflow == 'ellipsis') {
|
|
saveTextOverflow = element.style.textOverflow;
|
|
element.style.textOverflow = 'clip';
|
|
}
|
|
}
|
|
var estimateLineLength = element.getBoundingClientRect().width / fontSize * 5;
|
|
if (estimateLineLength === 0) {
|
|
estimateLineLength = 500;
|
|
}
|
|
while (!doChunk()) {
|
|
}
|
|
if (browser.msie && textOverflow == 'ellipsis') {
|
|
element.style.textOverflow = saveTextOverflow;
|
|
}
|
|
return;
|
|
function doChunk() {
|
|
var origStart = start;
|
|
var box, pos = text.substr(start).search(/\S/);
|
|
start += pos;
|
|
if (pos < 0 || start >= end) {
|
|
return true;
|
|
}
|
|
range.setStart(node, start);
|
|
range.setEnd(node, start + 1);
|
|
box = actuallyGetRangeBoundingRect(range);
|
|
var found = false;
|
|
if (isJustified) {
|
|
pos = text.substr(start).search(/\s/);
|
|
if (pos >= 0) {
|
|
range.setEnd(node, start + pos);
|
|
var r = range.getBoundingClientRect();
|
|
if (r.bottom == box.bottom) {
|
|
box = r;
|
|
found = true;
|
|
start += pos;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
pos = function findEOL(min, eol, max) {
|
|
range.setEnd(node, eol);
|
|
var r = actuallyGetRangeBoundingRect(range);
|
|
if (r.bottom != box.bottom && min < eol) {
|
|
return findEOL(min, min + eol >> 1, eol);
|
|
} else if (r.right != box.right) {
|
|
box = r;
|
|
if (eol < max) {
|
|
return findEOL(eol, eol + max >> 1, max);
|
|
} else {
|
|
return eol;
|
|
}
|
|
} else {
|
|
return eol;
|
|
}
|
|
}(start, Math.min(end, start + estimateLineLength), end);
|
|
if (pos == start) {
|
|
return true;
|
|
}
|
|
start = pos;
|
|
pos = range.toString().search(/\s+$/);
|
|
if (pos === 0) {
|
|
return;
|
|
}
|
|
if (pos > 0) {
|
|
range.setEnd(node, range.startOffset + pos);
|
|
box = range.getBoundingClientRect();
|
|
}
|
|
}
|
|
if (browser.msie) {
|
|
box = range.getClientRects()[0];
|
|
}
|
|
var str = range.toString();
|
|
if (!/^(?:pre|pre-wrap)$/i.test(whiteSpace)) {
|
|
str = str.replace(/\s+/g, ' ');
|
|
} else if (/\t/.test(str)) {
|
|
var cc = 0;
|
|
for (pos = origStart; pos < range.startOffset; ++pos) {
|
|
var code = text.charCodeAt(pos);
|
|
if (code == 9) {
|
|
cc += 8 - cc % 8;
|
|
} else if (code == 10 || code == 13) {
|
|
cc = 0;
|
|
} else {
|
|
cc++;
|
|
}
|
|
}
|
|
while ((pos = str.search('\t')) >= 0) {
|
|
var indent = ' '.substr(0, 8 - (cc + pos) % 8);
|
|
str = str.substr(0, pos) + indent + str.substr(pos + 1);
|
|
}
|
|
}
|
|
drawText(str, box);
|
|
}
|
|
function drawText(str, box) {
|
|
if (browser.msie && !isNaN(lineHeight)) {
|
|
var size = kendo.util.measureText(str, { font: font });
|
|
var top = (box.top + box.bottom - size.height) / 2;
|
|
box = {
|
|
top: top,
|
|
right: box.right,
|
|
bottom: top + size.height,
|
|
left: box.left,
|
|
height: size.height,
|
|
width: box.right - box.left
|
|
};
|
|
}
|
|
var text = new TextRect(str, new geo.Rect([
|
|
box.left,
|
|
box.top
|
|
], [
|
|
box.width,
|
|
box.height
|
|
]), {
|
|
font: font,
|
|
fill: { color: color }
|
|
});
|
|
group.append(text);
|
|
decorate(box);
|
|
}
|
|
function decorate(box) {
|
|
line(nodeInfo['underline'], box.bottom);
|
|
line(nodeInfo['line-through'], box.bottom - box.height / 2.7);
|
|
line(nodeInfo['overline'], box.top);
|
|
function line(color, ypos) {
|
|
if (color) {
|
|
var width = fontSize / 12;
|
|
var path = new drawing.Path({
|
|
stroke: {
|
|
width: width,
|
|
color: color
|
|
}
|
|
});
|
|
ypos -= width;
|
|
path.moveTo(box.left, ypos).lineTo(box.right, ypos);
|
|
group.append(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function groupInStackingContext(element, group, zIndex) {
|
|
var main;
|
|
if (zIndex != 'auto') {
|
|
main = nodeInfo._stackingContext.group;
|
|
zIndex = parseFloat(zIndex);
|
|
} else {
|
|
main = group;
|
|
zIndex = 0;
|
|
}
|
|
var a = main.children;
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (a[i]._dom_zIndex != null && a[i]._dom_zIndex > zIndex) {
|
|
break;
|
|
}
|
|
}
|
|
var tmp = new drawing.Group();
|
|
main.insertAt(tmp, i);
|
|
tmp._dom_zIndex = zIndex;
|
|
if (main !== group) {
|
|
if (nodeInfo._clipbox) {
|
|
var m = nodeInfo._matrix.invert();
|
|
var r = nodeInfo._clipbox.transformCopy(m);
|
|
setClipping(tmp, drawing.Path.fromRect(r));
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
function renderElement(element, container) {
|
|
var style = getComputedStyle(element);
|
|
var counterReset = getPropertyValue(style, 'counter-reset');
|
|
if (counterReset) {
|
|
doCounters(splitProperty(counterReset, /^\s+/), resetCounter, 0);
|
|
}
|
|
var counterIncrement = getPropertyValue(style, 'counter-increment');
|
|
if (counterIncrement) {
|
|
doCounters(splitProperty(counterIncrement, /^\s+/), incCounter, 1);
|
|
}
|
|
if (/^(style|script|link|meta|iframe|svg|col|colgroup)$/i.test(element.tagName)) {
|
|
return;
|
|
}
|
|
if (nodeInfo._clipbox == null) {
|
|
return;
|
|
}
|
|
var opacity = parseFloat(getPropertyValue(style, 'opacity'));
|
|
var visibility = getPropertyValue(style, 'visibility');
|
|
var display = getPropertyValue(style, 'display');
|
|
if (opacity === 0 || visibility == 'hidden' || display == 'none') {
|
|
return;
|
|
}
|
|
var tr = getTransform(style);
|
|
var group;
|
|
var zIndex = getPropertyValue(style, 'z-index');
|
|
if ((tr || opacity < 1) && zIndex == 'auto') {
|
|
zIndex = 0;
|
|
}
|
|
group = groupInStackingContext(element, container, zIndex);
|
|
if (opacity < 1) {
|
|
group.opacity(opacity * group.opacity());
|
|
}
|
|
pushNodeInfo(element, style, group);
|
|
if (!tr) {
|
|
_renderWithPseudoElements(element, group);
|
|
} else {
|
|
saveStyle(element, function () {
|
|
pleaseSetPropertyValue(element.style, 'transform', 'none', 'important');
|
|
pleaseSetPropertyValue(element.style, 'transition', 'none', 'important');
|
|
if (getPropertyValue(style, 'position') == 'static') {
|
|
pleaseSetPropertyValue(element.style, 'position', 'relative', 'important');
|
|
}
|
|
var bbox = element.getBoundingClientRect();
|
|
var x = bbox.left + tr.origin[0];
|
|
var y = bbox.top + tr.origin[1];
|
|
var m = [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
-x,
|
|
-y
|
|
];
|
|
m = mmul(m, tr.matrix);
|
|
m = mmul(m, [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
x,
|
|
y
|
|
]);
|
|
m = setTransform(group, m);
|
|
nodeInfo._matrix = nodeInfo._matrix.multiplyCopy(m);
|
|
_renderWithPseudoElements(element, group);
|
|
});
|
|
}
|
|
popNodeInfo();
|
|
}
|
|
function mmul(a, b) {
|
|
var a1 = a[0], b1 = a[1], c1 = a[2], d1 = a[3], e1 = a[4], f1 = a[5];
|
|
var a2 = b[0], b2 = b[1], c2 = b[2], d2 = b[3], e2 = b[4], f2 = b[5];
|
|
return [
|
|
a1 * a2 + b1 * c2,
|
|
a1 * b2 + b1 * d2,
|
|
c1 * a2 + d1 * c2,
|
|
c1 * b2 + d1 * d2,
|
|
e1 * a2 + f1 * c2 + e2,
|
|
e1 * b2 + f1 * d2 + f2
|
|
];
|
|
}
|
|
}(window.kendo.jQuery, parseFloat, Math));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('drawing/animation', [
|
|
'drawing/geometry',
|
|
'drawing/core'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var noop = $.noop, kendo = window.kendo, Class = kendo.Class, util = kendo.util, animationFrame = kendo.animationFrame, deepExtend = kendo.deepExtend;
|
|
var Animation = Class.extend({
|
|
init: function (element, options) {
|
|
var anim = this;
|
|
anim.options = deepExtend({}, anim.options, options);
|
|
anim.element = element;
|
|
},
|
|
options: {
|
|
duration: 500,
|
|
easing: 'swing'
|
|
},
|
|
setup: noop,
|
|
step: noop,
|
|
play: function () {
|
|
var anim = this, options = anim.options, easing = $.easing[options.easing], duration = options.duration, delay = options.delay || 0, start = util.now() + delay, finish = start + duration;
|
|
if (duration === 0) {
|
|
anim.step(1);
|
|
anim.abort();
|
|
} else {
|
|
setTimeout(function () {
|
|
var loop = function () {
|
|
if (anim._stopped) {
|
|
return;
|
|
}
|
|
var wallTime = util.now();
|
|
var time = util.limitValue(wallTime - start, 0, duration);
|
|
var pos = time / duration;
|
|
var easingPos = easing(pos, time, 0, 1, duration);
|
|
anim.step(easingPos);
|
|
if (wallTime < finish) {
|
|
animationFrame(loop);
|
|
} else {
|
|
anim.abort();
|
|
}
|
|
};
|
|
loop();
|
|
}, delay);
|
|
}
|
|
},
|
|
abort: function () {
|
|
this._stopped = true;
|
|
},
|
|
destroy: function () {
|
|
this.abort();
|
|
}
|
|
});
|
|
var AnimationFactory = function () {
|
|
this._items = [];
|
|
};
|
|
AnimationFactory.prototype = {
|
|
register: function (name, type) {
|
|
this._items.push({
|
|
name: name,
|
|
type: type
|
|
});
|
|
},
|
|
create: function (element, options) {
|
|
var items = this._items;
|
|
var match;
|
|
if (options && options.type) {
|
|
var type = options.type.toLowerCase();
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (items[i].name.toLowerCase() === type) {
|
|
match = items[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (match) {
|
|
return new match.type(element, options);
|
|
}
|
|
}
|
|
};
|
|
AnimationFactory.current = new AnimationFactory();
|
|
Animation.create = function (type, element, options) {
|
|
return AnimationFactory.current.create(type, element, options);
|
|
};
|
|
deepExtend(kendo.drawing, {
|
|
Animation: Animation,
|
|
AnimationFactory: AnimationFactory
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.drawing', [
|
|
'kendo.color',
|
|
'util/main',
|
|
'util/text-metrics',
|
|
'util/base64',
|
|
'mixins/observers',
|
|
'drawing/geometry',
|
|
'drawing/core',
|
|
'drawing/mixins',
|
|
'drawing/shapes',
|
|
'drawing/parser',
|
|
'drawing/svg',
|
|
'drawing/canvas',
|
|
'drawing/vml',
|
|
'drawing/html',
|
|
'drawing/animation'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'drawing',
|
|
name: 'Drawing API',
|
|
category: 'framework',
|
|
description: 'The Kendo UI low-level drawing API',
|
|
depends: [
|
|
'core',
|
|
'color'
|
|
]
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.validator', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'validator',
|
|
name: 'Validator',
|
|
category: 'web',
|
|
description: 'The Validator offers an easy way to do a client-side form validation.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, NS = '.kendoValidator', INVALIDMSG = 'k-invalid-msg', invalidMsgRegExp = new RegExp(INVALIDMSG, 'i'), INVALIDINPUT = 'k-invalid', VALIDINPUT = 'k-valid', emailRegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, urlRegExp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, INPUTSELECTOR = ':input:not(:button,[type=submit],[type=reset],[disabled],[readonly])', CHECKBOXSELECTOR = ':checkbox:not([disabled],[readonly])', NUMBERINPUTSELECTOR = '[type=number],[type=range]', BLUR = 'blur', NAME = 'name', FORM = 'form', NOVALIDATE = 'novalidate', proxy = $.proxy, patternMatcher = function (value, pattern) {
|
|
if (typeof pattern === 'string') {
|
|
pattern = new RegExp('^(?:' + pattern + ')$');
|
|
}
|
|
return pattern.test(value);
|
|
}, matcher = function (input, selector, pattern) {
|
|
var value = input.val();
|
|
if (input.filter(selector).length && value !== '') {
|
|
return patternMatcher(value, pattern);
|
|
}
|
|
return true;
|
|
}, hasAttribute = function (input, name) {
|
|
if (input.length) {
|
|
return input[0].attributes[name] != null;
|
|
}
|
|
return false;
|
|
};
|
|
if (!kendo.ui.validator) {
|
|
kendo.ui.validator = {
|
|
rules: {},
|
|
messages: {}
|
|
};
|
|
}
|
|
function resolveRules(element) {
|
|
var resolvers = kendo.ui.validator.ruleResolvers || {}, rules = {}, name;
|
|
for (name in resolvers) {
|
|
$.extend(true, rules, resolvers[name].resolve(element));
|
|
}
|
|
return rules;
|
|
}
|
|
function decode(value) {
|
|
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, '\'').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
function numberOfDecimalDigits(value) {
|
|
value = (value + '').split('.');
|
|
if (value.length > 1) {
|
|
return value[1].length;
|
|
}
|
|
return 0;
|
|
}
|
|
function parseHtml(text) {
|
|
if ($.parseHTML) {
|
|
return $($.parseHTML(text));
|
|
}
|
|
return $(text);
|
|
}
|
|
function searchForMessageContainer(elements, fieldName) {
|
|
var containers = $(), element, attr;
|
|
for (var idx = 0, length = elements.length; idx < length; idx++) {
|
|
element = elements[idx];
|
|
if (invalidMsgRegExp.test(element.className)) {
|
|
attr = element.getAttribute(kendo.attr('for'));
|
|
if (attr === fieldName) {
|
|
containers = containers.add(element);
|
|
}
|
|
}
|
|
}
|
|
return containers;
|
|
}
|
|
var Validator = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, resolved = resolveRules(element), validateAttributeSelector = '[' + kendo.attr('validate') + '!=false]';
|
|
options = options || {};
|
|
options.rules = $.extend({}, kendo.ui.validator.rules, resolved.rules, options.rules);
|
|
options.messages = $.extend({}, kendo.ui.validator.messages, resolved.messages, options.messages);
|
|
Widget.fn.init.call(that, element, options);
|
|
that._errorTemplate = kendo.template(that.options.errorTemplate);
|
|
if (that.element.is(FORM)) {
|
|
that.element.attr(NOVALIDATE, NOVALIDATE);
|
|
}
|
|
that._inputSelector = INPUTSELECTOR + validateAttributeSelector;
|
|
that._checkboxSelector = CHECKBOXSELECTOR + validateAttributeSelector;
|
|
that._errors = {};
|
|
that._attachEvents();
|
|
that._isValidated = false;
|
|
},
|
|
events: [
|
|
'validate',
|
|
'change'
|
|
],
|
|
options: {
|
|
name: 'Validator',
|
|
errorTemplate: '<span class="k-widget k-tooltip k-tooltip-validation">' + '<span class="k-icon k-warning"> </span> #=message#</span>',
|
|
messages: {
|
|
required: '{0} is required',
|
|
pattern: '{0} is not valid',
|
|
min: '{0} should be greater than or equal to {1}',
|
|
max: '{0} should be smaller than or equal to {1}',
|
|
step: '{0} is not valid',
|
|
email: '{0} is not valid email',
|
|
url: '{0} is not valid URL',
|
|
date: '{0} is not valid date',
|
|
dateCompare: 'End date should be greater than or equal to the start date'
|
|
},
|
|
rules: {
|
|
required: function (input) {
|
|
var checkbox = input.filter('[type=checkbox]').length && !input.is(':checked'), value = input.val();
|
|
return !(hasAttribute(input, 'required') && (value === '' || !value || checkbox));
|
|
},
|
|
pattern: function (input) {
|
|
if (input.filter('[type=text],[type=email],[type=url],[type=tel],[type=search],[type=password]').filter('[pattern]').length && input.val() !== '') {
|
|
return patternMatcher(input.val(), input.attr('pattern'));
|
|
}
|
|
return true;
|
|
},
|
|
min: function (input) {
|
|
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[min]').length && input.val() !== '') {
|
|
var min = parseFloat(input.attr('min')) || 0, val = kendo.parseFloat(input.val());
|
|
return min <= val;
|
|
}
|
|
return true;
|
|
},
|
|
max: function (input) {
|
|
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[max]').length && input.val() !== '') {
|
|
var max = parseFloat(input.attr('max')) || 0, val = kendo.parseFloat(input.val());
|
|
return max >= val;
|
|
}
|
|
return true;
|
|
},
|
|
step: function (input) {
|
|
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[step]').length && input.val() !== '') {
|
|
var min = parseFloat(input.attr('min')) || 0, step = parseFloat(input.attr('step')) || 1, val = parseFloat(input.val()), decimals = numberOfDecimalDigits(step), raise;
|
|
if (decimals) {
|
|
raise = Math.pow(10, decimals);
|
|
return Math.floor((val - min) * raise) % (step * raise) / Math.pow(100, decimals) === 0;
|
|
}
|
|
return (val - min) % step === 0;
|
|
}
|
|
return true;
|
|
},
|
|
email: function (input) {
|
|
return matcher(input, '[type=email],[' + kendo.attr('type') + '=email]', emailRegExp);
|
|
},
|
|
url: function (input) {
|
|
return matcher(input, '[type=url],[' + kendo.attr('type') + '=url]', urlRegExp);
|
|
},
|
|
date: function (input) {
|
|
if (input.filter('[type^=date],[' + kendo.attr('type') + '=date]').length && input.val() !== '') {
|
|
return kendo.parseDate(input.val(), input.attr(kendo.attr('format'))) !== null;
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
validateOnBlur: true
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.element.off(NS);
|
|
},
|
|
value: function () {
|
|
if (!this._isValidated) {
|
|
return false;
|
|
}
|
|
return this.errors().length === 0;
|
|
},
|
|
_submit: function (e) {
|
|
if (!this.validate()) {
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
_checkElement: function (element) {
|
|
var state = this.value();
|
|
this.validateInput(element);
|
|
if (this.value() !== state) {
|
|
this.trigger('change');
|
|
}
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this;
|
|
if (that.element.is(FORM)) {
|
|
that.element.on('submit' + NS, proxy(that._submit, that));
|
|
}
|
|
if (that.options.validateOnBlur) {
|
|
if (!that.element.is(INPUTSELECTOR)) {
|
|
that.element.on(BLUR + NS, that._inputSelector, function () {
|
|
that._checkElement($(this));
|
|
});
|
|
that.element.on('click' + NS, that._checkboxSelector, function () {
|
|
that._checkElement($(this));
|
|
});
|
|
} else {
|
|
that.element.on(BLUR + NS, function () {
|
|
that._checkElement(that.element);
|
|
});
|
|
if (that.element.is(CHECKBOXSELECTOR)) {
|
|
that.element.on('click' + NS, function () {
|
|
that._checkElement(that.element);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
validate: function () {
|
|
var inputs;
|
|
var idx;
|
|
var result = false;
|
|
var length;
|
|
var isValid = this.value();
|
|
this._errors = {};
|
|
if (!this.element.is(INPUTSELECTOR)) {
|
|
var invalid = false;
|
|
inputs = this.element.find(this._inputSelector);
|
|
for (idx = 0, length = inputs.length; idx < length; idx++) {
|
|
if (!this.validateInput(inputs.eq(idx))) {
|
|
invalid = true;
|
|
}
|
|
}
|
|
result = !invalid;
|
|
} else {
|
|
result = this.validateInput(this.element);
|
|
}
|
|
this.trigger('validate', { valid: result });
|
|
if (isValid !== result) {
|
|
this.trigger('change');
|
|
}
|
|
return result;
|
|
},
|
|
validateInput: function (input) {
|
|
input = $(input);
|
|
this._isValidated = true;
|
|
var that = this, template = that._errorTemplate, result = that._checkValidity(input), valid = result.valid, className = '.' + INVALIDMSG, fieldName = input.attr(NAME) || '', lbl = that._findMessageContainer(fieldName).add(input.next(className).filter(function () {
|
|
var element = $(this);
|
|
if (element.filter('[' + kendo.attr('for') + ']').length) {
|
|
return element.attr(kendo.attr('for')) === fieldName;
|
|
}
|
|
return true;
|
|
})).hide(), messageText;
|
|
input.removeAttr('aria-invalid');
|
|
if (!valid) {
|
|
messageText = that._extractMessage(input, result.key);
|
|
that._errors[fieldName] = messageText;
|
|
var messageLabel = parseHtml(template({ message: decode(messageText) }));
|
|
var lblId = lbl.attr('id');
|
|
that._decorateMessageContainer(messageLabel, fieldName);
|
|
if (lblId) {
|
|
messageLabel.attr('id', lblId);
|
|
}
|
|
if (!lbl.replaceWith(messageLabel).length) {
|
|
messageLabel.insertAfter(input);
|
|
}
|
|
messageLabel.show();
|
|
input.attr('aria-invalid', true);
|
|
} else {
|
|
delete that._errors[fieldName];
|
|
}
|
|
input.toggleClass(INVALIDINPUT, !valid);
|
|
input.toggleClass(VALIDINPUT, valid);
|
|
return valid;
|
|
},
|
|
hideMessages: function () {
|
|
var that = this, className = '.' + INVALIDMSG, element = that.element;
|
|
if (!element.is(INPUTSELECTOR)) {
|
|
element.find(className).hide();
|
|
} else {
|
|
element.next(className).hide();
|
|
}
|
|
},
|
|
_findMessageContainer: function (fieldName) {
|
|
var locators = kendo.ui.validator.messageLocators, name, containers = $();
|
|
for (var idx = 0, length = this.element.length; idx < length; idx++) {
|
|
containers = containers.add(searchForMessageContainer(this.element[idx].getElementsByTagName('*'), fieldName));
|
|
}
|
|
for (name in locators) {
|
|
containers = containers.add(locators[name].locate(this.element, fieldName));
|
|
}
|
|
return containers;
|
|
},
|
|
_decorateMessageContainer: function (container, fieldName) {
|
|
var locators = kendo.ui.validator.messageLocators, name;
|
|
container.addClass(INVALIDMSG).attr(kendo.attr('for'), fieldName || '');
|
|
for (name in locators) {
|
|
locators[name].decorate(container, fieldName);
|
|
}
|
|
container.attr('role', 'alert');
|
|
},
|
|
_extractMessage: function (input, ruleKey) {
|
|
var that = this, customMessage = that.options.messages[ruleKey], fieldName = input.attr(NAME);
|
|
customMessage = kendo.isFunction(customMessage) ? customMessage(input) : customMessage;
|
|
return kendo.format(input.attr(kendo.attr(ruleKey + '-msg')) || input.attr('validationMessage') || input.attr('title') || customMessage || '', fieldName, input.attr(ruleKey) || input.attr(kendo.attr(ruleKey)));
|
|
},
|
|
_checkValidity: function (input) {
|
|
var rules = this.options.rules, rule;
|
|
for (rule in rules) {
|
|
if (!rules[rule].call(this, input)) {
|
|
return {
|
|
valid: false,
|
|
key: rule
|
|
};
|
|
}
|
|
}
|
|
return { valid: true };
|
|
},
|
|
errors: function () {
|
|
var results = [], errors = this._errors, error;
|
|
for (error in errors) {
|
|
results.push(errors[error]);
|
|
}
|
|
return results;
|
|
}
|
|
});
|
|
kendo.ui.plugin(Validator);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.userevents', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'userevents',
|
|
name: 'User Events',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, support = kendo.support, document = window.document, Class = kendo.Class, Observable = kendo.Observable, now = $.now, extend = $.extend, OS = support.mobileOS, invalidZeroEvents = OS && OS.android, DEFAULT_MIN_HOLD = 800, DEFAULT_THRESHOLD = support.browser.msie ? 5 : 0, PRESS = 'press', HOLD = 'hold', SELECT = 'select', START = 'start', MOVE = 'move', END = 'end', CANCEL = 'cancel', TAP = 'tap', RELEASE = 'release', GESTURESTART = 'gesturestart', GESTURECHANGE = 'gesturechange', GESTUREEND = 'gestureend', GESTURETAP = 'gesturetap';
|
|
var THRESHOLD = {
|
|
'api': 0,
|
|
'touch': 0,
|
|
'mouse': 9,
|
|
'pointer': 9
|
|
};
|
|
var ENABLE_GLOBAL_SURFACE = !support.touch || support.mouseAndTouchPresent;
|
|
function touchDelta(touch1, touch2) {
|
|
var x1 = touch1.x.location, y1 = touch1.y.location, x2 = touch2.x.location, y2 = touch2.y.location, dx = x1 - x2, dy = y1 - y2;
|
|
return {
|
|
center: {
|
|
x: (x1 + x2) / 2,
|
|
y: (y1 + y2) / 2
|
|
},
|
|
distance: Math.sqrt(dx * dx + dy * dy)
|
|
};
|
|
}
|
|
function getTouches(e) {
|
|
var touches = [], originalEvent = e.originalEvent, currentTarget = e.currentTarget, idx = 0, length, changedTouches, touch;
|
|
if (e.api) {
|
|
touches.push({
|
|
id: 2,
|
|
event: e,
|
|
target: e.target,
|
|
currentTarget: e.target,
|
|
location: e,
|
|
type: 'api'
|
|
});
|
|
} else if (e.type.match(/touch/)) {
|
|
changedTouches = originalEvent ? originalEvent.changedTouches : [];
|
|
for (length = changedTouches.length; idx < length; idx++) {
|
|
touch = changedTouches[idx];
|
|
touches.push({
|
|
location: touch,
|
|
event: e,
|
|
target: touch.target,
|
|
currentTarget: currentTarget,
|
|
id: touch.identifier,
|
|
type: 'touch'
|
|
});
|
|
}
|
|
} else if (support.pointers || support.msPointers) {
|
|
touches.push({
|
|
location: originalEvent,
|
|
event: e,
|
|
target: e.target,
|
|
currentTarget: currentTarget,
|
|
id: originalEvent.pointerId,
|
|
type: 'pointer'
|
|
});
|
|
} else {
|
|
touches.push({
|
|
id: 1,
|
|
event: e,
|
|
target: e.target,
|
|
currentTarget: currentTarget,
|
|
location: e,
|
|
type: 'mouse'
|
|
});
|
|
}
|
|
return touches;
|
|
}
|
|
var TouchAxis = Class.extend({
|
|
init: function (axis, location) {
|
|
var that = this;
|
|
that.axis = axis;
|
|
that._updateLocationData(location);
|
|
that.startLocation = that.location;
|
|
that.velocity = that.delta = 0;
|
|
that.timeStamp = now();
|
|
},
|
|
move: function (location) {
|
|
var that = this, offset = location['page' + that.axis], timeStamp = now(), timeDelta = timeStamp - that.timeStamp || 1;
|
|
if (!offset && invalidZeroEvents) {
|
|
return;
|
|
}
|
|
that.delta = offset - that.location;
|
|
that._updateLocationData(location);
|
|
that.initialDelta = offset - that.startLocation;
|
|
that.velocity = that.delta / timeDelta;
|
|
that.timeStamp = timeStamp;
|
|
},
|
|
_updateLocationData: function (location) {
|
|
var that = this, axis = that.axis;
|
|
that.location = location['page' + axis];
|
|
that.client = location['client' + axis];
|
|
that.screen = location['screen' + axis];
|
|
}
|
|
});
|
|
var Touch = Class.extend({
|
|
init: function (userEvents, target, touchInfo) {
|
|
extend(this, {
|
|
x: new TouchAxis('X', touchInfo.location),
|
|
y: new TouchAxis('Y', touchInfo.location),
|
|
type: touchInfo.type,
|
|
useClickAsTap: userEvents.useClickAsTap,
|
|
threshold: userEvents.threshold || THRESHOLD[touchInfo.type],
|
|
userEvents: userEvents,
|
|
target: target,
|
|
currentTarget: touchInfo.currentTarget,
|
|
initialTouch: touchInfo.target,
|
|
id: touchInfo.id,
|
|
pressEvent: touchInfo,
|
|
_moved: false,
|
|
_finished: false
|
|
});
|
|
},
|
|
press: function () {
|
|
this._holdTimeout = setTimeout($.proxy(this, '_hold'), this.userEvents.minHold);
|
|
this._trigger(PRESS, this.pressEvent);
|
|
},
|
|
_hold: function () {
|
|
this._trigger(HOLD, this.pressEvent);
|
|
},
|
|
move: function (touchInfo) {
|
|
var that = this;
|
|
if (that._finished) {
|
|
return;
|
|
}
|
|
that.x.move(touchInfo.location);
|
|
that.y.move(touchInfo.location);
|
|
if (!that._moved) {
|
|
if (that._withinIgnoreThreshold()) {
|
|
return;
|
|
}
|
|
if (!UserEvents.current || UserEvents.current === that.userEvents) {
|
|
that._start(touchInfo);
|
|
} else {
|
|
return that.dispose();
|
|
}
|
|
}
|
|
if (!that._finished) {
|
|
that._trigger(MOVE, touchInfo);
|
|
}
|
|
},
|
|
end: function (touchInfo) {
|
|
this.endTime = now();
|
|
if (this._finished) {
|
|
return;
|
|
}
|
|
this._finished = true;
|
|
this._trigger(RELEASE, touchInfo);
|
|
if (this._moved) {
|
|
this._trigger(END, touchInfo);
|
|
} else {
|
|
if (!this.useClickAsTap) {
|
|
this._trigger(TAP, touchInfo);
|
|
}
|
|
}
|
|
clearTimeout(this._holdTimeout);
|
|
this.dispose();
|
|
},
|
|
dispose: function () {
|
|
var userEvents = this.userEvents, activeTouches = userEvents.touches;
|
|
this._finished = true;
|
|
this.pressEvent = null;
|
|
clearTimeout(this._holdTimeout);
|
|
activeTouches.splice($.inArray(this, activeTouches), 1);
|
|
},
|
|
skip: function () {
|
|
this.dispose();
|
|
},
|
|
cancel: function () {
|
|
this.dispose();
|
|
},
|
|
isMoved: function () {
|
|
return this._moved;
|
|
},
|
|
_start: function (touchInfo) {
|
|
clearTimeout(this._holdTimeout);
|
|
this.startTime = now();
|
|
this._moved = true;
|
|
this._trigger(START, touchInfo);
|
|
},
|
|
_trigger: function (name, touchInfo) {
|
|
var that = this, jQueryEvent = touchInfo.event, data = {
|
|
touch: that,
|
|
x: that.x,
|
|
y: that.y,
|
|
target: that.target,
|
|
event: jQueryEvent
|
|
};
|
|
if (that.userEvents.notify(name, data)) {
|
|
jQueryEvent.preventDefault();
|
|
}
|
|
},
|
|
_withinIgnoreThreshold: function () {
|
|
var xDelta = this.x.initialDelta, yDelta = this.y.initialDelta;
|
|
return Math.sqrt(xDelta * xDelta + yDelta * yDelta) <= this.threshold;
|
|
}
|
|
});
|
|
function withEachUpEvent(callback) {
|
|
var downEvents = kendo.eventMap.up.split(' '), idx = 0, length = downEvents.length;
|
|
for (; idx < length; idx++) {
|
|
callback(downEvents[idx]);
|
|
}
|
|
}
|
|
var UserEvents = Observable.extend({
|
|
init: function (element, options) {
|
|
var that = this, filter, ns = kendo.guid();
|
|
options = options || {};
|
|
filter = that.filter = options.filter;
|
|
that.threshold = options.threshold || DEFAULT_THRESHOLD;
|
|
that.minHold = options.minHold || DEFAULT_MIN_HOLD;
|
|
that.touches = [];
|
|
that._maxTouches = options.multiTouch ? 2 : 1;
|
|
that.allowSelection = options.allowSelection;
|
|
that.captureUpIfMoved = options.captureUpIfMoved;
|
|
that.useClickAsTap = !options.fastTap && !support.delayedClick();
|
|
that.eventNS = ns;
|
|
element = $(element).handler(that);
|
|
Observable.fn.init.call(that);
|
|
extend(that, {
|
|
element: element,
|
|
surface: options.global && ENABLE_GLOBAL_SURFACE ? $(document.documentElement) : $(options.surface || element),
|
|
stopPropagation: options.stopPropagation,
|
|
pressed: false
|
|
});
|
|
that.surface.handler(that).on(kendo.applyEventMap('move', ns), '_move').on(kendo.applyEventMap('up cancel', ns), '_end');
|
|
element.on(kendo.applyEventMap('down', ns), filter, '_start');
|
|
if (that.useClickAsTap) {
|
|
element.on(kendo.applyEventMap('click', ns), filter, '_click');
|
|
}
|
|
if (support.pointers || support.msPointers) {
|
|
if (support.browser.version < 11) {
|
|
element.css('-ms-touch-action', 'pinch-zoom double-tap-zoom');
|
|
} else {
|
|
element.css('touch-action', 'pan-y');
|
|
}
|
|
}
|
|
if (options.preventDragEvent) {
|
|
element.on(kendo.applyEventMap('dragstart', ns), kendo.preventDefault);
|
|
}
|
|
element.on(kendo.applyEventMap('mousedown', ns), filter, { root: element }, '_select');
|
|
if (that.captureUpIfMoved && support.eventCapture) {
|
|
var surfaceElement = that.surface[0], preventIfMovingProxy = $.proxy(that.preventIfMoving, that);
|
|
withEachUpEvent(function (eventName) {
|
|
surfaceElement.addEventListener(eventName, preventIfMovingProxy, true);
|
|
});
|
|
}
|
|
that.bind([
|
|
PRESS,
|
|
HOLD,
|
|
TAP,
|
|
START,
|
|
MOVE,
|
|
END,
|
|
RELEASE,
|
|
CANCEL,
|
|
GESTURESTART,
|
|
GESTURECHANGE,
|
|
GESTUREEND,
|
|
GESTURETAP,
|
|
SELECT
|
|
], options);
|
|
},
|
|
preventIfMoving: function (e) {
|
|
if (this._isMoved()) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
if (that._destroyed) {
|
|
return;
|
|
}
|
|
that._destroyed = true;
|
|
if (that.captureUpIfMoved && support.eventCapture) {
|
|
var surfaceElement = that.surface[0];
|
|
withEachUpEvent(function (eventName) {
|
|
surfaceElement.removeEventListener(eventName, that.preventIfMoving);
|
|
});
|
|
}
|
|
that.element.kendoDestroy(that.eventNS);
|
|
that.surface.kendoDestroy(that.eventNS);
|
|
that.element.removeData('handler');
|
|
that.surface.removeData('handler');
|
|
that._disposeAll();
|
|
that.unbind();
|
|
delete that.surface;
|
|
delete that.element;
|
|
delete that.currentTarget;
|
|
},
|
|
capture: function () {
|
|
UserEvents.current = this;
|
|
},
|
|
cancel: function () {
|
|
this._disposeAll();
|
|
this.trigger(CANCEL);
|
|
},
|
|
notify: function (eventName, data) {
|
|
var that = this, touches = that.touches;
|
|
if (this._isMultiTouch()) {
|
|
switch (eventName) {
|
|
case MOVE:
|
|
eventName = GESTURECHANGE;
|
|
break;
|
|
case END:
|
|
eventName = GESTUREEND;
|
|
break;
|
|
case TAP:
|
|
eventName = GESTURETAP;
|
|
break;
|
|
}
|
|
extend(data, { touches: touches }, touchDelta(touches[0], touches[1]));
|
|
}
|
|
return this.trigger(eventName, extend(data, { type: eventName }));
|
|
},
|
|
press: function (x, y, target) {
|
|
this._apiCall('_start', x, y, target);
|
|
},
|
|
move: function (x, y) {
|
|
this._apiCall('_move', x, y);
|
|
},
|
|
end: function (x, y) {
|
|
this._apiCall('_end', x, y);
|
|
},
|
|
_isMultiTouch: function () {
|
|
return this.touches.length > 1;
|
|
},
|
|
_maxTouchesReached: function () {
|
|
return this.touches.length >= this._maxTouches;
|
|
},
|
|
_disposeAll: function () {
|
|
var touches = this.touches;
|
|
while (touches.length > 0) {
|
|
touches.pop().dispose();
|
|
}
|
|
},
|
|
_isMoved: function () {
|
|
return $.grep(this.touches, function (touch) {
|
|
return touch.isMoved();
|
|
}).length;
|
|
},
|
|
_select: function (e) {
|
|
if (!this.allowSelection || this.trigger(SELECT, { event: e })) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_start: function (e) {
|
|
var that = this, idx = 0, filter = that.filter, target, touches = getTouches(e), length = touches.length, touch, which = e.which;
|
|
if (which && which > 1 || that._maxTouchesReached()) {
|
|
return;
|
|
}
|
|
UserEvents.current = null;
|
|
that.currentTarget = e.currentTarget;
|
|
if (that.stopPropagation) {
|
|
e.stopPropagation();
|
|
}
|
|
for (; idx < length; idx++) {
|
|
if (that._maxTouchesReached()) {
|
|
break;
|
|
}
|
|
touch = touches[idx];
|
|
if (filter) {
|
|
target = $(touch.currentTarget);
|
|
} else {
|
|
target = that.element;
|
|
}
|
|
if (!target.length) {
|
|
continue;
|
|
}
|
|
touch = new Touch(that, target, touch);
|
|
that.touches.push(touch);
|
|
touch.press();
|
|
if (that._isMultiTouch()) {
|
|
that.notify('gesturestart', {});
|
|
}
|
|
}
|
|
},
|
|
_move: function (e) {
|
|
this._eachTouch('move', e);
|
|
},
|
|
_end: function (e) {
|
|
this._eachTouch('end', e);
|
|
},
|
|
_click: function (e) {
|
|
var data = {
|
|
touch: {
|
|
initialTouch: e.target,
|
|
target: $(e.currentTarget),
|
|
endTime: now(),
|
|
x: {
|
|
location: e.pageX,
|
|
client: e.clientX
|
|
},
|
|
y: {
|
|
location: e.pageY,
|
|
client: e.clientY
|
|
}
|
|
},
|
|
x: e.pageX,
|
|
y: e.pageY,
|
|
target: $(e.currentTarget),
|
|
event: e,
|
|
type: 'tap'
|
|
};
|
|
if (this.trigger('tap', data)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_eachTouch: function (methodName, e) {
|
|
var that = this, dict = {}, touches = getTouches(e), activeTouches = that.touches, idx, touch, touchInfo, matchingTouch;
|
|
for (idx = 0; idx < activeTouches.length; idx++) {
|
|
touch = activeTouches[idx];
|
|
dict[touch.id] = touch;
|
|
}
|
|
for (idx = 0; idx < touches.length; idx++) {
|
|
touchInfo = touches[idx];
|
|
matchingTouch = dict[touchInfo.id];
|
|
if (matchingTouch) {
|
|
matchingTouch[methodName](touchInfo);
|
|
}
|
|
}
|
|
},
|
|
_apiCall: function (type, x, y, target) {
|
|
this[type]({
|
|
api: true,
|
|
pageX: x,
|
|
pageY: y,
|
|
clientX: x,
|
|
clientY: y,
|
|
target: $(target || this.element)[0],
|
|
stopPropagation: $.noop,
|
|
preventDefault: $.noop
|
|
});
|
|
}
|
|
});
|
|
UserEvents.defaultThreshold = function (value) {
|
|
DEFAULT_THRESHOLD = value;
|
|
};
|
|
UserEvents.minHold = function (value) {
|
|
DEFAULT_MIN_HOLD = value;
|
|
};
|
|
kendo.getTouches = getTouches;
|
|
kendo.touchDelta = touchDelta;
|
|
kendo.UserEvents = UserEvents;
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.draganddrop', [
|
|
'kendo.core',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'draganddrop',
|
|
name: 'Drag & drop',
|
|
category: 'framework',
|
|
description: 'Drag & drop functionality for any DOM element.',
|
|
depends: [
|
|
'core',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, support = kendo.support, document = window.document, $window = $(window), Class = kendo.Class, Widget = kendo.ui.Widget, Observable = kendo.Observable, UserEvents = kendo.UserEvents, proxy = $.proxy, extend = $.extend, getOffset = kendo.getOffset, draggables = {}, dropTargets = {}, dropAreas = {}, lastDropTarget, elementUnderCursor = kendo.elementUnderCursor, KEYUP = 'keyup', CHANGE = 'change', DRAGSTART = 'dragstart', HOLD = 'hold', DRAG = 'drag', DRAGEND = 'dragend', DRAGCANCEL = 'dragcancel', HINTDESTROYED = 'hintDestroyed', DRAGENTER = 'dragenter', DRAGLEAVE = 'dragleave', DROP = 'drop';
|
|
function contains(parent, child) {
|
|
try {
|
|
return $.contains(parent, child) || parent == child;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
function numericCssPropery(element, property) {
|
|
return parseInt(element.css(property), 10) || 0;
|
|
}
|
|
function within(value, range) {
|
|
return Math.min(Math.max(value, range.min), range.max);
|
|
}
|
|
function containerBoundaries(container, element) {
|
|
var offset = getOffset(container), minX = offset.left + numericCssPropery(container, 'borderLeftWidth') + numericCssPropery(container, 'paddingLeft'), minY = offset.top + numericCssPropery(container, 'borderTopWidth') + numericCssPropery(container, 'paddingTop'), maxX = minX + container.width() - element.outerWidth(true), maxY = minY + container.height() - element.outerHeight(true);
|
|
return {
|
|
x: {
|
|
min: minX,
|
|
max: maxX
|
|
},
|
|
y: {
|
|
min: minY,
|
|
max: maxY
|
|
}
|
|
};
|
|
}
|
|
function checkTarget(target, targets, areas) {
|
|
var theTarget, theFilter, i = 0, targetLen = targets && targets.length, areaLen = areas && areas.length;
|
|
while (target && target.parentNode) {
|
|
for (i = 0; i < targetLen; i++) {
|
|
theTarget = targets[i];
|
|
if (theTarget.element[0] === target) {
|
|
return {
|
|
target: theTarget,
|
|
targetElement: target
|
|
};
|
|
}
|
|
}
|
|
for (i = 0; i < areaLen; i++) {
|
|
theFilter = areas[i];
|
|
if ($.contains(theFilter.element[0], target) && support.matchesSelector.call(target, theFilter.options.filter)) {
|
|
return {
|
|
target: theFilter,
|
|
targetElement: target
|
|
};
|
|
}
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
return undefined;
|
|
}
|
|
var TapCapture = Observable.extend({
|
|
init: function (element, options) {
|
|
var that = this, domElement = element[0];
|
|
that.capture = false;
|
|
if (domElement.addEventListener) {
|
|
$.each(kendo.eventMap.down.split(' '), function () {
|
|
domElement.addEventListener(this, proxy(that._press, that), true);
|
|
});
|
|
$.each(kendo.eventMap.up.split(' '), function () {
|
|
domElement.addEventListener(this, proxy(that._release, that), true);
|
|
});
|
|
} else {
|
|
$.each(kendo.eventMap.down.split(' '), function () {
|
|
domElement.attachEvent(this, proxy(that._press, that));
|
|
});
|
|
$.each(kendo.eventMap.up.split(' '), function () {
|
|
domElement.attachEvent(this, proxy(that._release, that));
|
|
});
|
|
}
|
|
Observable.fn.init.call(that);
|
|
that.bind([
|
|
'press',
|
|
'release'
|
|
], options || {});
|
|
},
|
|
captureNext: function () {
|
|
this.capture = true;
|
|
},
|
|
cancelCapture: function () {
|
|
this.capture = false;
|
|
},
|
|
_press: function (e) {
|
|
var that = this;
|
|
that.trigger('press');
|
|
if (that.capture) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_release: function (e) {
|
|
var that = this;
|
|
that.trigger('release');
|
|
if (that.capture) {
|
|
e.preventDefault();
|
|
that.cancelCapture();
|
|
}
|
|
}
|
|
});
|
|
var PaneDimension = Observable.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Observable.fn.init.call(that);
|
|
that.forcedEnabled = false;
|
|
$.extend(that, options);
|
|
that.scale = 1;
|
|
if (that.horizontal) {
|
|
that.measure = 'offsetWidth';
|
|
that.scrollSize = 'scrollWidth';
|
|
that.axis = 'x';
|
|
} else {
|
|
that.measure = 'offsetHeight';
|
|
that.scrollSize = 'scrollHeight';
|
|
that.axis = 'y';
|
|
}
|
|
},
|
|
makeVirtual: function () {
|
|
$.extend(this, {
|
|
virtual: true,
|
|
forcedEnabled: true,
|
|
_virtualMin: 0,
|
|
_virtualMax: 0
|
|
});
|
|
},
|
|
virtualSize: function (min, max) {
|
|
if (this._virtualMin !== min || this._virtualMax !== max) {
|
|
this._virtualMin = min;
|
|
this._virtualMax = max;
|
|
this.update();
|
|
}
|
|
},
|
|
outOfBounds: function (offset) {
|
|
return offset > this.max || offset < this.min;
|
|
},
|
|
forceEnabled: function () {
|
|
this.forcedEnabled = true;
|
|
},
|
|
getSize: function () {
|
|
return this.container[0][this.measure];
|
|
},
|
|
getTotal: function () {
|
|
return this.element[0][this.scrollSize];
|
|
},
|
|
rescale: function (scale) {
|
|
this.scale = scale;
|
|
},
|
|
update: function (silent) {
|
|
var that = this, total = that.virtual ? that._virtualMax : that.getTotal(), scaledTotal = total * that.scale, size = that.getSize();
|
|
if (total === 0 && !that.forcedEnabled) {
|
|
return;
|
|
}
|
|
that.max = that.virtual ? -that._virtualMin : 0;
|
|
that.size = size;
|
|
that.total = scaledTotal;
|
|
that.min = Math.min(that.max, size - scaledTotal);
|
|
that.minScale = size / total;
|
|
that.centerOffset = (scaledTotal - size) / 2;
|
|
that.enabled = that.forcedEnabled || scaledTotal > size;
|
|
if (!silent) {
|
|
that.trigger(CHANGE, that);
|
|
}
|
|
}
|
|
});
|
|
var PaneDimensions = Observable.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Observable.fn.init.call(that);
|
|
that.x = new PaneDimension(extend({ horizontal: true }, options));
|
|
that.y = new PaneDimension(extend({ horizontal: false }, options));
|
|
that.container = options.container;
|
|
that.forcedMinScale = options.minScale;
|
|
that.maxScale = options.maxScale || 100;
|
|
that.bind(CHANGE, options);
|
|
},
|
|
rescale: function (newScale) {
|
|
this.x.rescale(newScale);
|
|
this.y.rescale(newScale);
|
|
this.refresh();
|
|
},
|
|
centerCoordinates: function () {
|
|
return {
|
|
x: Math.min(0, -this.x.centerOffset),
|
|
y: Math.min(0, -this.y.centerOffset)
|
|
};
|
|
},
|
|
refresh: function () {
|
|
var that = this;
|
|
that.x.update();
|
|
that.y.update();
|
|
that.enabled = that.x.enabled || that.y.enabled;
|
|
that.minScale = that.forcedMinScale || Math.min(that.x.minScale, that.y.minScale);
|
|
that.fitScale = Math.max(that.x.minScale, that.y.minScale);
|
|
that.trigger(CHANGE);
|
|
}
|
|
});
|
|
var PaneAxis = Observable.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
extend(that, options);
|
|
Observable.fn.init.call(that);
|
|
},
|
|
outOfBounds: function () {
|
|
return this.dimension.outOfBounds(this.movable[this.axis]);
|
|
},
|
|
dragMove: function (delta) {
|
|
var that = this, dimension = that.dimension, axis = that.axis, movable = that.movable, position = movable[axis] + delta;
|
|
if (!dimension.enabled) {
|
|
return;
|
|
}
|
|
if (position < dimension.min && delta < 0 || position > dimension.max && delta > 0) {
|
|
delta *= that.resistance;
|
|
}
|
|
movable.translateAxis(axis, delta);
|
|
that.trigger(CHANGE, that);
|
|
}
|
|
});
|
|
var Pane = Class.extend({
|
|
init: function (options) {
|
|
var that = this, x, y, resistance, movable;
|
|
extend(that, { elastic: true }, options);
|
|
resistance = that.elastic ? 0.5 : 0;
|
|
movable = that.movable;
|
|
that.x = x = new PaneAxis({
|
|
axis: 'x',
|
|
dimension: that.dimensions.x,
|
|
resistance: resistance,
|
|
movable: movable
|
|
});
|
|
that.y = y = new PaneAxis({
|
|
axis: 'y',
|
|
dimension: that.dimensions.y,
|
|
resistance: resistance,
|
|
movable: movable
|
|
});
|
|
that.userEvents.bind([
|
|
'press',
|
|
'move',
|
|
'end',
|
|
'gesturestart',
|
|
'gesturechange'
|
|
], {
|
|
gesturestart: function (e) {
|
|
that.gesture = e;
|
|
that.offset = that.dimensions.container.offset();
|
|
},
|
|
press: function (e) {
|
|
if ($(e.event.target).closest('a').is('[data-navigate-on-press=true]')) {
|
|
e.sender.cancel();
|
|
}
|
|
},
|
|
gesturechange: function (e) {
|
|
var previousGesture = that.gesture, previousCenter = previousGesture.center, center = e.center, scaleDelta = e.distance / previousGesture.distance, minScale = that.dimensions.minScale, maxScale = that.dimensions.maxScale, coordinates;
|
|
if (movable.scale <= minScale && scaleDelta < 1) {
|
|
scaleDelta += (1 - scaleDelta) * 0.8;
|
|
}
|
|
if (movable.scale * scaleDelta >= maxScale) {
|
|
scaleDelta = maxScale / movable.scale;
|
|
}
|
|
var offsetX = movable.x + that.offset.left, offsetY = movable.y + that.offset.top;
|
|
coordinates = {
|
|
x: (offsetX - previousCenter.x) * scaleDelta + center.x - offsetX,
|
|
y: (offsetY - previousCenter.y) * scaleDelta + center.y - offsetY
|
|
};
|
|
movable.scaleWith(scaleDelta);
|
|
x.dragMove(coordinates.x);
|
|
y.dragMove(coordinates.y);
|
|
that.dimensions.rescale(movable.scale);
|
|
that.gesture = e;
|
|
e.preventDefault();
|
|
},
|
|
move: function (e) {
|
|
if (e.event.target.tagName.match(/textarea|input/i)) {
|
|
return;
|
|
}
|
|
if (x.dimension.enabled || y.dimension.enabled) {
|
|
x.dragMove(e.x.delta);
|
|
y.dragMove(e.y.delta);
|
|
e.preventDefault();
|
|
} else {
|
|
e.touch.skip();
|
|
}
|
|
},
|
|
end: function (e) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
var TRANSFORM_STYLE = support.transitions.prefix + 'Transform', translate;
|
|
if (support.hasHW3D) {
|
|
translate = function (x, y, scale) {
|
|
return 'translate3d(' + x + 'px,' + y + 'px,0) scale(' + scale + ')';
|
|
};
|
|
} else {
|
|
translate = function (x, y, scale) {
|
|
return 'translate(' + x + 'px,' + y + 'px) scale(' + scale + ')';
|
|
};
|
|
}
|
|
var Movable = Observable.extend({
|
|
init: function (element) {
|
|
var that = this;
|
|
Observable.fn.init.call(that);
|
|
that.element = $(element);
|
|
that.element[0].style.webkitTransformOrigin = 'left top';
|
|
that.x = 0;
|
|
that.y = 0;
|
|
that.scale = 1;
|
|
that._saveCoordinates(translate(that.x, that.y, that.scale));
|
|
},
|
|
translateAxis: function (axis, by) {
|
|
this[axis] += by;
|
|
this.refresh();
|
|
},
|
|
scaleTo: function (scale) {
|
|
this.scale = scale;
|
|
this.refresh();
|
|
},
|
|
scaleWith: function (scaleDelta) {
|
|
this.scale *= scaleDelta;
|
|
this.refresh();
|
|
},
|
|
translate: function (coordinates) {
|
|
this.x += coordinates.x;
|
|
this.y += coordinates.y;
|
|
this.refresh();
|
|
},
|
|
moveAxis: function (axis, value) {
|
|
this[axis] = value;
|
|
this.refresh();
|
|
},
|
|
moveTo: function (coordinates) {
|
|
extend(this, coordinates);
|
|
this.refresh();
|
|
},
|
|
refresh: function () {
|
|
var that = this, x = that.x, y = that.y, newCoordinates;
|
|
if (that.round) {
|
|
x = Math.round(x);
|
|
y = Math.round(y);
|
|
}
|
|
newCoordinates = translate(x, y, that.scale);
|
|
if (newCoordinates != that.coordinates) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 10) {
|
|
that.element[0].style.position = 'absolute';
|
|
that.element[0].style.left = that.x + 'px';
|
|
that.element[0].style.top = that.y + 'px';
|
|
} else {
|
|
that.element[0].style[TRANSFORM_STYLE] = newCoordinates;
|
|
}
|
|
that._saveCoordinates(newCoordinates);
|
|
that.trigger(CHANGE);
|
|
}
|
|
},
|
|
_saveCoordinates: function (coordinates) {
|
|
this.coordinates = coordinates;
|
|
}
|
|
});
|
|
function destroyDroppable(collection, widget) {
|
|
var groupName = widget.options.group, droppables = collection[groupName], i;
|
|
Widget.fn.destroy.call(widget);
|
|
if (droppables.length > 1) {
|
|
for (i = 0; i < droppables.length; i++) {
|
|
if (droppables[i] == widget) {
|
|
droppables.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
droppables.length = 0;
|
|
delete collection[groupName];
|
|
}
|
|
}
|
|
var DropTarget = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
var group = that.options.group;
|
|
if (!(group in dropTargets)) {
|
|
dropTargets[group] = [that];
|
|
} else {
|
|
dropTargets[group].push(that);
|
|
}
|
|
},
|
|
events: [
|
|
DRAGENTER,
|
|
DRAGLEAVE,
|
|
DROP
|
|
],
|
|
options: {
|
|
name: 'DropTarget',
|
|
group: 'default'
|
|
},
|
|
destroy: function () {
|
|
destroyDroppable(dropTargets, this);
|
|
},
|
|
_trigger: function (eventName, e) {
|
|
var that = this, draggable = draggables[that.options.group];
|
|
if (draggable) {
|
|
return that.trigger(eventName, extend({}, e.event, {
|
|
draggable: draggable,
|
|
dropTarget: e.dropTarget
|
|
}));
|
|
}
|
|
},
|
|
_over: function (e) {
|
|
this._trigger(DRAGENTER, e);
|
|
},
|
|
_out: function (e) {
|
|
this._trigger(DRAGLEAVE, e);
|
|
},
|
|
_drop: function (e) {
|
|
var that = this, draggable = draggables[that.options.group];
|
|
if (draggable) {
|
|
draggable.dropped = !that._trigger(DROP, e);
|
|
}
|
|
}
|
|
});
|
|
DropTarget.destroyGroup = function (groupName) {
|
|
var group = dropTargets[groupName] || dropAreas[groupName], i;
|
|
if (group) {
|
|
for (i = 0; i < group.length; i++) {
|
|
Widget.fn.destroy.call(group[i]);
|
|
}
|
|
group.length = 0;
|
|
delete dropTargets[groupName];
|
|
delete dropAreas[groupName];
|
|
}
|
|
};
|
|
DropTarget._cache = dropTargets;
|
|
var DropTargetArea = DropTarget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
var group = that.options.group;
|
|
if (!(group in dropAreas)) {
|
|
dropAreas[group] = [that];
|
|
} else {
|
|
dropAreas[group].push(that);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
destroyDroppable(dropAreas, this);
|
|
},
|
|
options: {
|
|
name: 'DropTargetArea',
|
|
group: 'default',
|
|
filter: null
|
|
}
|
|
});
|
|
var Draggable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._activated = false;
|
|
that.userEvents = new UserEvents(that.element, {
|
|
global: true,
|
|
allowSelection: true,
|
|
filter: that.options.filter,
|
|
threshold: that.options.distance,
|
|
start: proxy(that._start, that),
|
|
hold: proxy(that._hold, that),
|
|
move: proxy(that._drag, that),
|
|
end: proxy(that._end, that),
|
|
cancel: proxy(that._cancel, that),
|
|
select: proxy(that._select, that)
|
|
});
|
|
that._afterEndHandler = proxy(that._afterEnd, that);
|
|
that._captureEscape = proxy(that._captureEscape, that);
|
|
},
|
|
events: [
|
|
HOLD,
|
|
DRAGSTART,
|
|
DRAG,
|
|
DRAGEND,
|
|
DRAGCANCEL,
|
|
HINTDESTROYED
|
|
],
|
|
options: {
|
|
name: 'Draggable',
|
|
distance: kendo.support.touch ? 0 : 5,
|
|
group: 'default',
|
|
cursorOffset: null,
|
|
axis: null,
|
|
container: null,
|
|
filter: null,
|
|
ignore: null,
|
|
holdToDrag: false,
|
|
autoScroll: false,
|
|
dropped: false
|
|
},
|
|
cancelHold: function () {
|
|
this._activated = false;
|
|
},
|
|
_captureEscape: function (e) {
|
|
var that = this;
|
|
if (e.keyCode === kendo.keys.ESC) {
|
|
that._trigger(DRAGCANCEL, { event: e });
|
|
that.userEvents.cancel();
|
|
}
|
|
},
|
|
_updateHint: function (e) {
|
|
var that = this, coordinates, options = that.options, boundaries = that.boundaries, axis = options.axis, cursorOffset = that.options.cursorOffset;
|
|
if (cursorOffset) {
|
|
coordinates = {
|
|
left: e.x.location + cursorOffset.left,
|
|
top: e.y.location + cursorOffset.top
|
|
};
|
|
} else {
|
|
that.hintOffset.left += e.x.delta;
|
|
that.hintOffset.top += e.y.delta;
|
|
coordinates = $.extend({}, that.hintOffset);
|
|
}
|
|
if (boundaries) {
|
|
coordinates.top = within(coordinates.top, boundaries.y);
|
|
coordinates.left = within(coordinates.left, boundaries.x);
|
|
}
|
|
if (axis === 'x') {
|
|
delete coordinates.top;
|
|
} else if (axis === 'y') {
|
|
delete coordinates.left;
|
|
}
|
|
that.hint.css(coordinates);
|
|
},
|
|
_shouldIgnoreTarget: function (target) {
|
|
var ignoreSelector = this.options.ignore;
|
|
return ignoreSelector && $(target).is(ignoreSelector);
|
|
},
|
|
_select: function (e) {
|
|
if (!this._shouldIgnoreTarget(e.event.target)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_start: function (e) {
|
|
var that = this, options = that.options, container = options.container, hint = options.hint;
|
|
if (this._shouldIgnoreTarget(e.touch.initialTouch) || options.holdToDrag && !that._activated) {
|
|
that.userEvents.cancel();
|
|
return;
|
|
}
|
|
that.currentTarget = e.target;
|
|
that.currentTargetOffset = getOffset(that.currentTarget);
|
|
if (hint) {
|
|
if (that.hint) {
|
|
that.hint.stop(true, true).remove();
|
|
}
|
|
that.hint = kendo.isFunction(hint) ? $(hint.call(that, that.currentTarget)) : hint;
|
|
var offset = getOffset(that.currentTarget);
|
|
that.hintOffset = offset;
|
|
that.hint.css({
|
|
position: 'absolute',
|
|
zIndex: 20000,
|
|
left: offset.left,
|
|
top: offset.top
|
|
}).appendTo(document.body);
|
|
that.angular('compile', function () {
|
|
that.hint.removeAttr('ng-repeat');
|
|
var scopeTarget = $(e.target);
|
|
while (!scopeTarget.data('$$kendoScope') && scopeTarget.length) {
|
|
scopeTarget = scopeTarget.parent();
|
|
}
|
|
return {
|
|
elements: that.hint.get(),
|
|
scopeFrom: scopeTarget.data('$$kendoScope')
|
|
};
|
|
});
|
|
}
|
|
draggables[options.group] = that;
|
|
that.dropped = false;
|
|
if (container) {
|
|
that.boundaries = containerBoundaries(container, that.hint);
|
|
}
|
|
$(document).on(KEYUP, that._captureEscape);
|
|
if (that._trigger(DRAGSTART, e)) {
|
|
that.userEvents.cancel();
|
|
that._afterEnd();
|
|
}
|
|
that.userEvents.capture();
|
|
},
|
|
_hold: function (e) {
|
|
this.currentTarget = e.target;
|
|
if (this._trigger(HOLD, e)) {
|
|
this.userEvents.cancel();
|
|
} else {
|
|
this._activated = true;
|
|
}
|
|
},
|
|
_drag: function (e) {
|
|
e.preventDefault();
|
|
var cursorElement = this._elementUnderCursor(e);
|
|
this._lastEvent = e;
|
|
this._processMovement(e, cursorElement);
|
|
if (this.options.autoScroll) {
|
|
if (this._cursorElement !== cursorElement) {
|
|
this._scrollableParent = findScrollableParent(cursorElement);
|
|
this._cursorElement = cursorElement;
|
|
}
|
|
if (this._scrollableParent[0]) {
|
|
var velocity = autoScrollVelocity(e.x.location, e.y.location, scrollableViewPort(this._scrollableParent));
|
|
this._scrollCompenstation = $.extend({}, this.hintOffset);
|
|
this._scrollVelocity = velocity;
|
|
if (velocity.y === 0 && velocity.x === 0) {
|
|
clearInterval(this._scrollInterval);
|
|
this._scrollInterval = null;
|
|
} else if (!this._scrollInterval) {
|
|
this._scrollInterval = setInterval($.proxy(this, '_autoScroll'), 50);
|
|
}
|
|
}
|
|
}
|
|
if (this.hint) {
|
|
this._updateHint(e);
|
|
}
|
|
},
|
|
_processMovement: function (e, cursorElement) {
|
|
this._withDropTarget(cursorElement, function (target, targetElement) {
|
|
if (!target) {
|
|
if (lastDropTarget) {
|
|
lastDropTarget._trigger(DRAGLEAVE, extend(e, { dropTarget: $(lastDropTarget.targetElement) }));
|
|
lastDropTarget = null;
|
|
}
|
|
return;
|
|
}
|
|
if (lastDropTarget) {
|
|
if (targetElement === lastDropTarget.targetElement) {
|
|
return;
|
|
}
|
|
lastDropTarget._trigger(DRAGLEAVE, extend(e, { dropTarget: $(lastDropTarget.targetElement) }));
|
|
}
|
|
target._trigger(DRAGENTER, extend(e, { dropTarget: $(targetElement) }));
|
|
lastDropTarget = extend(target, { targetElement: targetElement });
|
|
});
|
|
this._trigger(DRAG, extend(e, {
|
|
dropTarget: lastDropTarget,
|
|
elementUnderCursor: cursorElement
|
|
}));
|
|
},
|
|
_autoScroll: function () {
|
|
var parent = this._scrollableParent[0], velocity = this._scrollVelocity, compensation = this._scrollCompenstation;
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
var cursorElement = this._elementUnderCursor(this._lastEvent);
|
|
this._processMovement(this._lastEvent, cursorElement);
|
|
var yIsScrollable, xIsScrollable;
|
|
var isRootNode = parent === scrollableRoot()[0];
|
|
if (isRootNode) {
|
|
yIsScrollable = document.body.scrollHeight > $window.height();
|
|
xIsScrollable = document.body.scrollWidth > $window.width();
|
|
} else {
|
|
yIsScrollable = parent.offsetHeight <= parent.scrollHeight;
|
|
xIsScrollable = parent.offsetWidth <= parent.scrollWidth;
|
|
}
|
|
var yDelta = parent.scrollTop + velocity.y;
|
|
var yInBounds = yIsScrollable && yDelta > 0 && yDelta < parent.scrollHeight;
|
|
var xDelta = parent.scrollLeft + velocity.x;
|
|
var xInBounds = xIsScrollable && xDelta > 0 && xDelta < parent.scrollWidth;
|
|
if (yInBounds) {
|
|
parent.scrollTop += velocity.y;
|
|
}
|
|
if (xInBounds) {
|
|
parent.scrollLeft += velocity.x;
|
|
}
|
|
if (isRootNode && (xInBounds || yInBounds)) {
|
|
if (yInBounds) {
|
|
compensation.top += velocity.y;
|
|
}
|
|
if (xInBounds) {
|
|
compensation.left += velocity.x;
|
|
}
|
|
this.hint.css(compensation);
|
|
}
|
|
},
|
|
_end: function (e) {
|
|
this._withDropTarget(this._elementUnderCursor(e), function (target, targetElement) {
|
|
if (target) {
|
|
target._drop(extend({}, e, { dropTarget: $(targetElement) }));
|
|
lastDropTarget = null;
|
|
}
|
|
});
|
|
this._cancel(this._trigger(DRAGEND, e));
|
|
},
|
|
_cancel: function (isDefaultPrevented) {
|
|
var that = this;
|
|
that._scrollableParent = null;
|
|
this._cursorElement = null;
|
|
clearInterval(this._scrollInterval);
|
|
that._activated = false;
|
|
if (that.hint && !that.dropped) {
|
|
setTimeout(function () {
|
|
that.hint.stop(true, true);
|
|
if (isDefaultPrevented) {
|
|
that._afterEndHandler();
|
|
} else {
|
|
that.hint.animate(that.currentTargetOffset, 'fast', that._afterEndHandler);
|
|
}
|
|
}, 0);
|
|
} else {
|
|
that._afterEnd();
|
|
}
|
|
},
|
|
_trigger: function (eventName, e) {
|
|
var that = this;
|
|
return that.trigger(eventName, extend({}, e.event, {
|
|
x: e.x,
|
|
y: e.y,
|
|
currentTarget: that.currentTarget,
|
|
initialTarget: e.touch ? e.touch.initialTouch : null,
|
|
dropTarget: e.dropTarget,
|
|
elementUnderCursor: e.elementUnderCursor
|
|
}));
|
|
},
|
|
_elementUnderCursor: function (e) {
|
|
var target = elementUnderCursor(e), hint = this.hint;
|
|
if (hint && contains(hint[0], target)) {
|
|
hint.hide();
|
|
target = elementUnderCursor(e);
|
|
if (!target) {
|
|
target = elementUnderCursor(e);
|
|
}
|
|
hint.show();
|
|
}
|
|
return target;
|
|
},
|
|
_withDropTarget: function (element, callback) {
|
|
var result, group = this.options.group, targets = dropTargets[group], areas = dropAreas[group];
|
|
if (targets && targets.length || areas && areas.length) {
|
|
result = checkTarget(element, targets, areas);
|
|
if (result) {
|
|
callback(result.target, result.targetElement);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that._afterEnd();
|
|
that.userEvents.destroy();
|
|
this._scrollableParent = null;
|
|
this._cursorElement = null;
|
|
clearInterval(this._scrollInterval);
|
|
that.currentTarget = null;
|
|
},
|
|
_afterEnd: function () {
|
|
var that = this;
|
|
if (that.hint) {
|
|
that.hint.remove();
|
|
}
|
|
delete draggables[that.options.group];
|
|
that.trigger('destroy');
|
|
that.trigger(HINTDESTROYED);
|
|
$(document).off(KEYUP, that._captureEscape);
|
|
}
|
|
});
|
|
kendo.ui.plugin(DropTarget);
|
|
kendo.ui.plugin(DropTargetArea);
|
|
kendo.ui.plugin(Draggable);
|
|
kendo.TapCapture = TapCapture;
|
|
kendo.containerBoundaries = containerBoundaries;
|
|
extend(kendo.ui, {
|
|
Pane: Pane,
|
|
PaneDimensions: PaneDimensions,
|
|
Movable: Movable
|
|
});
|
|
function scrollableViewPort(element) {
|
|
var root = scrollableRoot()[0], offset, top, left;
|
|
if (element[0] === root) {
|
|
top = root.scrollTop;
|
|
left = root.scrollLeft;
|
|
return {
|
|
top: top,
|
|
left: left,
|
|
bottom: top + $window.height(),
|
|
right: left + $window.width()
|
|
};
|
|
} else {
|
|
offset = element.offset();
|
|
offset.bottom = offset.top + element.height();
|
|
offset.right = offset.left + element.width();
|
|
return offset;
|
|
}
|
|
}
|
|
function scrollableRoot() {
|
|
return $(kendo.support.browser.chrome ? document.body : document.documentElement);
|
|
}
|
|
function findScrollableParent(element) {
|
|
var root = scrollableRoot();
|
|
if (!element || element === document.body || element === document.documentElement) {
|
|
return root;
|
|
}
|
|
var parent = $(element)[0];
|
|
while (parent && !kendo.isScrollable(parent) && parent !== document.body) {
|
|
parent = parent.parentNode;
|
|
}
|
|
if (parent === document.body) {
|
|
return root;
|
|
}
|
|
return $(parent);
|
|
}
|
|
function autoScrollVelocity(mouseX, mouseY, rect) {
|
|
var velocity = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
var AUTO_SCROLL_AREA = 50;
|
|
if (mouseX - rect.left < AUTO_SCROLL_AREA) {
|
|
velocity.x = -(AUTO_SCROLL_AREA - (mouseX - rect.left));
|
|
} else if (rect.right - mouseX < AUTO_SCROLL_AREA) {
|
|
velocity.x = AUTO_SCROLL_AREA - (rect.right - mouseX);
|
|
}
|
|
if (mouseY - rect.top < AUTO_SCROLL_AREA) {
|
|
velocity.y = -(AUTO_SCROLL_AREA - (mouseY - rect.top));
|
|
} else if (rect.bottom - mouseY < AUTO_SCROLL_AREA) {
|
|
velocity.y = AUTO_SCROLL_AREA - (rect.bottom - mouseY);
|
|
}
|
|
return velocity;
|
|
}
|
|
kendo.ui.Draggable.utils = {
|
|
autoScrollVelocity: autoScrollVelocity,
|
|
scrollableViewPort: scrollableViewPort,
|
|
findScrollableParent: findScrollableParent
|
|
};
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.scroller', [
|
|
'kendo.fx',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.scroller',
|
|
name: 'Scroller',
|
|
category: 'mobile',
|
|
description: 'The Kendo Mobile Scroller widget enables touch friendly kinetic scrolling for the contents of a given DOM element.',
|
|
depends: [
|
|
'fx',
|
|
'draganddrop'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, fx = kendo.effects, ui = mobile.ui, proxy = $.proxy, extend = $.extend, Widget = ui.Widget, Class = kendo.Class, Movable = kendo.ui.Movable, Pane = kendo.ui.Pane, PaneDimensions = kendo.ui.PaneDimensions, Transition = fx.Transition, Animation = fx.Animation, abs = Math.abs, SNAPBACK_DURATION = 500, SCROLLBAR_OPACITY = 0.7, FRICTION = 0.96, VELOCITY_MULTIPLIER = 10, MAX_VELOCITY = 55, OUT_OF_BOUNDS_FRICTION = 0.5, ANIMATED_SCROLLER_PRECISION = 5, RELEASECLASS = 'km-scroller-release', REFRESHCLASS = 'km-scroller-refresh', PULL = 'pull', CHANGE = 'change', RESIZE = 'resize', SCROLL = 'scroll', MOUSE_WHEEL_ID = 2;
|
|
var ZoomSnapBack = Animation.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Animation.fn.init.call(that);
|
|
extend(that, options);
|
|
that.userEvents.bind('gestureend', proxy(that.start, that));
|
|
that.tapCapture.bind('press', proxy(that.cancel, that));
|
|
},
|
|
enabled: function () {
|
|
return this.movable.scale < this.dimensions.minScale;
|
|
},
|
|
done: function () {
|
|
return this.dimensions.minScale - this.movable.scale < 0.01;
|
|
},
|
|
tick: function () {
|
|
var movable = this.movable;
|
|
movable.scaleWith(1.1);
|
|
this.dimensions.rescale(movable.scale);
|
|
},
|
|
onEnd: function () {
|
|
var movable = this.movable;
|
|
movable.scaleTo(this.dimensions.minScale);
|
|
this.dimensions.rescale(movable.scale);
|
|
}
|
|
});
|
|
var DragInertia = Animation.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Animation.fn.init.call(that);
|
|
extend(that, options, {
|
|
transition: new Transition({
|
|
axis: options.axis,
|
|
movable: options.movable,
|
|
onEnd: function () {
|
|
that._end();
|
|
}
|
|
})
|
|
});
|
|
that.tapCapture.bind('press', function () {
|
|
that.cancel();
|
|
});
|
|
that.userEvents.bind('end', proxy(that.start, that));
|
|
that.userEvents.bind('gestureend', proxy(that.start, that));
|
|
that.userEvents.bind('tap', proxy(that.onEnd, that));
|
|
},
|
|
onCancel: function () {
|
|
this.transition.cancel();
|
|
},
|
|
freeze: function (location) {
|
|
var that = this;
|
|
that.cancel();
|
|
that._moveTo(location);
|
|
},
|
|
onEnd: function () {
|
|
var that = this;
|
|
if (that.paneAxis.outOfBounds()) {
|
|
that._snapBack();
|
|
} else {
|
|
that._end();
|
|
}
|
|
},
|
|
done: function () {
|
|
return abs(this.velocity) < 1;
|
|
},
|
|
start: function (e) {
|
|
var that = this, velocity;
|
|
if (!that.dimension.enabled) {
|
|
return;
|
|
}
|
|
if (that.paneAxis.outOfBounds()) {
|
|
that._snapBack();
|
|
} else {
|
|
velocity = e.touch.id === MOUSE_WHEEL_ID ? 0 : e.touch[that.axis].velocity;
|
|
that.velocity = Math.max(Math.min(velocity * that.velocityMultiplier, MAX_VELOCITY), -MAX_VELOCITY);
|
|
that.tapCapture.captureNext();
|
|
Animation.fn.start.call(that);
|
|
}
|
|
},
|
|
tick: function () {
|
|
var that = this, dimension = that.dimension, friction = that.paneAxis.outOfBounds() ? OUT_OF_BOUNDS_FRICTION : that.friction, delta = that.velocity *= friction, location = that.movable[that.axis] + delta;
|
|
if (!that.elastic && dimension.outOfBounds(location)) {
|
|
location = Math.max(Math.min(location, dimension.max), dimension.min);
|
|
that.velocity = 0;
|
|
}
|
|
that.movable.moveAxis(that.axis, location);
|
|
},
|
|
_end: function () {
|
|
this.tapCapture.cancelCapture();
|
|
this.end();
|
|
},
|
|
_snapBack: function () {
|
|
var that = this, dimension = that.dimension, snapBack = that.movable[that.axis] > dimension.max ? dimension.max : dimension.min;
|
|
that._moveTo(snapBack);
|
|
},
|
|
_moveTo: function (location) {
|
|
this.transition.moveTo({
|
|
location: location,
|
|
duration: SNAPBACK_DURATION,
|
|
ease: Transition.easeOutExpo
|
|
});
|
|
}
|
|
});
|
|
var AnimatedScroller = Animation.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
kendo.effects.Animation.fn.init.call(this);
|
|
extend(that, options, {
|
|
origin: {},
|
|
destination: {},
|
|
offset: {}
|
|
});
|
|
},
|
|
tick: function () {
|
|
this._updateCoordinates();
|
|
this.moveTo(this.origin);
|
|
},
|
|
done: function () {
|
|
return abs(this.offset.y) < ANIMATED_SCROLLER_PRECISION && abs(this.offset.x) < ANIMATED_SCROLLER_PRECISION;
|
|
},
|
|
onEnd: function () {
|
|
this.moveTo(this.destination);
|
|
if (this.callback) {
|
|
this.callback.call();
|
|
}
|
|
},
|
|
setCoordinates: function (from, to) {
|
|
this.offset = {};
|
|
this.origin = from;
|
|
this.destination = to;
|
|
},
|
|
setCallback: function (callback) {
|
|
if (callback && kendo.isFunction(callback)) {
|
|
this.callback = callback;
|
|
} else {
|
|
callback = undefined;
|
|
}
|
|
},
|
|
_updateCoordinates: function () {
|
|
this.offset = {
|
|
x: (this.destination.x - this.origin.x) / 4,
|
|
y: (this.destination.y - this.origin.y) / 4
|
|
};
|
|
this.origin = {
|
|
y: this.origin.y + this.offset.y,
|
|
x: this.origin.x + this.offset.x
|
|
};
|
|
}
|
|
});
|
|
var ScrollBar = Class.extend({
|
|
init: function (options) {
|
|
var that = this, horizontal = options.axis === 'x', element = $('<div class="km-touch-scrollbar km-' + (horizontal ? 'horizontal' : 'vertical') + '-scrollbar" />');
|
|
extend(that, options, {
|
|
element: element,
|
|
elementSize: 0,
|
|
movable: new Movable(element),
|
|
scrollMovable: options.movable,
|
|
alwaysVisible: options.alwaysVisible,
|
|
size: horizontal ? 'width' : 'height'
|
|
});
|
|
that.scrollMovable.bind(CHANGE, proxy(that.refresh, that));
|
|
that.container.append(element);
|
|
if (options.alwaysVisible) {
|
|
that.show();
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var that = this, axis = that.axis, dimension = that.dimension, paneSize = dimension.size, scrollMovable = that.scrollMovable, sizeRatio = paneSize / dimension.total, position = Math.round(-scrollMovable[axis] * sizeRatio), size = Math.round(paneSize * sizeRatio);
|
|
if (sizeRatio >= 1) {
|
|
this.element.css('display', 'none');
|
|
} else {
|
|
this.element.css('display', '');
|
|
}
|
|
if (position + size > paneSize) {
|
|
size = paneSize - position;
|
|
} else if (position < 0) {
|
|
size += position;
|
|
position = 0;
|
|
}
|
|
if (that.elementSize != size) {
|
|
that.element.css(that.size, size + 'px');
|
|
that.elementSize = size;
|
|
}
|
|
that.movable.moveAxis(axis, position);
|
|
},
|
|
show: function () {
|
|
this.element.css({
|
|
opacity: SCROLLBAR_OPACITY,
|
|
visibility: 'visible'
|
|
});
|
|
},
|
|
hide: function () {
|
|
if (!this.alwaysVisible) {
|
|
this.element.css({ opacity: 0 });
|
|
}
|
|
}
|
|
});
|
|
var Scroller = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
that._native = that.options.useNative && kendo.support.hasNativeScrolling;
|
|
if (that._native) {
|
|
element.addClass('km-native-scroller').prepend('<div class="km-scroll-header"/>');
|
|
extend(that, {
|
|
scrollElement: element,
|
|
fixedContainer: element.children().first()
|
|
});
|
|
return;
|
|
}
|
|
element.css('overflow', 'hidden').addClass('km-scroll-wrapper').wrapInner('<div class="km-scroll-container"/>').prepend('<div class="km-scroll-header"/>');
|
|
var inner = element.children().eq(1), tapCapture = new kendo.TapCapture(element), movable = new Movable(inner), dimensions = new PaneDimensions({
|
|
element: inner,
|
|
container: element,
|
|
forcedEnabled: that.options.zoom
|
|
}), avoidScrolling = this.options.avoidScrolling, userEvents = new kendo.UserEvents(element, {
|
|
fastTap: true,
|
|
allowSelection: true,
|
|
preventDragEvent: true,
|
|
captureUpIfMoved: true,
|
|
multiTouch: that.options.zoom,
|
|
start: function (e) {
|
|
dimensions.refresh();
|
|
var velocityX = abs(e.x.velocity), velocityY = abs(e.y.velocity), horizontalSwipe = velocityX * 2 >= velocityY, originatedFromFixedContainer = $.contains(that.fixedContainer[0], e.event.target), verticalSwipe = velocityY * 2 >= velocityX;
|
|
if (!originatedFromFixedContainer && !avoidScrolling(e) && that.enabled && (dimensions.x.enabled && horizontalSwipe || dimensions.y.enabled && verticalSwipe)) {
|
|
userEvents.capture();
|
|
} else {
|
|
userEvents.cancel();
|
|
}
|
|
}
|
|
}), pane = new Pane({
|
|
movable: movable,
|
|
dimensions: dimensions,
|
|
userEvents: userEvents,
|
|
elastic: that.options.elastic
|
|
}), zoomSnapBack = new ZoomSnapBack({
|
|
movable: movable,
|
|
dimensions: dimensions,
|
|
userEvents: userEvents,
|
|
tapCapture: tapCapture
|
|
}), animatedScroller = new AnimatedScroller({
|
|
moveTo: function (coordinates) {
|
|
that.scrollTo(coordinates.x, coordinates.y);
|
|
}
|
|
});
|
|
movable.bind(CHANGE, function () {
|
|
that.scrollTop = -movable.y;
|
|
that.scrollLeft = -movable.x;
|
|
that.trigger(SCROLL, {
|
|
scrollTop: that.scrollTop,
|
|
scrollLeft: that.scrollLeft
|
|
});
|
|
});
|
|
if (that.options.mousewheelScrolling) {
|
|
element.on('DOMMouseScroll mousewheel', proxy(this, '_wheelScroll'));
|
|
}
|
|
extend(that, {
|
|
movable: movable,
|
|
dimensions: dimensions,
|
|
zoomSnapBack: zoomSnapBack,
|
|
animatedScroller: animatedScroller,
|
|
userEvents: userEvents,
|
|
pane: pane,
|
|
tapCapture: tapCapture,
|
|
pulled: false,
|
|
enabled: true,
|
|
scrollElement: inner,
|
|
scrollTop: 0,
|
|
scrollLeft: 0,
|
|
fixedContainer: element.children().first()
|
|
});
|
|
that._initAxis('x');
|
|
that._initAxis('y');
|
|
that._wheelEnd = function () {
|
|
that._wheel = false;
|
|
that.userEvents.end(0, that._wheelY);
|
|
};
|
|
dimensions.refresh();
|
|
if (that.options.pullToRefresh) {
|
|
that._initPullToRefresh();
|
|
}
|
|
},
|
|
_wheelScroll: function (e) {
|
|
if (!this._wheel) {
|
|
this._wheel = true;
|
|
this._wheelY = 0;
|
|
this.userEvents.press(0, this._wheelY);
|
|
}
|
|
clearTimeout(this._wheelTimeout);
|
|
this._wheelTimeout = setTimeout(this._wheelEnd, 50);
|
|
var delta = kendo.wheelDeltaY(e);
|
|
if (delta) {
|
|
this._wheelY += delta;
|
|
this.userEvents.move(0, this._wheelY);
|
|
}
|
|
e.preventDefault();
|
|
},
|
|
makeVirtual: function () {
|
|
this.dimensions.y.makeVirtual();
|
|
},
|
|
virtualSize: function (min, max) {
|
|
this.dimensions.y.virtualSize(min, max);
|
|
},
|
|
height: function () {
|
|
return this.dimensions.y.size;
|
|
},
|
|
scrollHeight: function () {
|
|
return this.scrollElement[0].scrollHeight;
|
|
},
|
|
scrollWidth: function () {
|
|
return this.scrollElement[0].scrollWidth;
|
|
},
|
|
options: {
|
|
name: 'Scroller',
|
|
zoom: false,
|
|
pullOffset: 140,
|
|
visibleScrollHints: false,
|
|
elastic: true,
|
|
useNative: false,
|
|
mousewheelScrolling: true,
|
|
avoidScrolling: function () {
|
|
return false;
|
|
},
|
|
pullToRefresh: false,
|
|
messages: {
|
|
pullTemplate: 'Pull to refresh',
|
|
releaseTemplate: 'Release to refresh',
|
|
refreshTemplate: 'Refreshing'
|
|
}
|
|
},
|
|
events: [
|
|
PULL,
|
|
SCROLL,
|
|
RESIZE
|
|
],
|
|
_resize: function () {
|
|
if (!this._native) {
|
|
this.contentResized();
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
Widget.fn.setOptions.call(that, options);
|
|
if (options.pullToRefresh) {
|
|
that._initPullToRefresh();
|
|
}
|
|
},
|
|
reset: function () {
|
|
if (this._native) {
|
|
this.scrollElement.scrollTop(0);
|
|
} else {
|
|
this.movable.moveTo({
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
this._scale(1);
|
|
}
|
|
},
|
|
contentResized: function () {
|
|
this.dimensions.refresh();
|
|
if (this.pane.x.outOfBounds()) {
|
|
this.movable.moveAxis('x', this.dimensions.x.min);
|
|
}
|
|
if (this.pane.y.outOfBounds()) {
|
|
this.movable.moveAxis('y', this.dimensions.y.min);
|
|
}
|
|
},
|
|
zoomOut: function () {
|
|
var dimensions = this.dimensions;
|
|
dimensions.refresh();
|
|
this._scale(dimensions.fitScale);
|
|
this.movable.moveTo(dimensions.centerCoordinates());
|
|
},
|
|
enable: function () {
|
|
this.enabled = true;
|
|
},
|
|
disable: function () {
|
|
this.enabled = false;
|
|
},
|
|
scrollTo: function (x, y) {
|
|
if (this._native) {
|
|
this.scrollElement.scrollLeft(abs(x));
|
|
this.scrollElement.scrollTop(abs(y));
|
|
} else {
|
|
this.dimensions.refresh();
|
|
this.movable.moveTo({
|
|
x: x,
|
|
y: y
|
|
});
|
|
}
|
|
},
|
|
animatedScrollTo: function (x, y, callback) {
|
|
var from, to;
|
|
if (this._native) {
|
|
this.scrollTo(x, y);
|
|
} else {
|
|
from = {
|
|
x: this.movable.x,
|
|
y: this.movable.y
|
|
};
|
|
to = {
|
|
x: x,
|
|
y: y
|
|
};
|
|
this.animatedScroller.setCoordinates(from, to);
|
|
this.animatedScroller.setCallback(callback);
|
|
this.animatedScroller.start();
|
|
}
|
|
},
|
|
pullHandled: function () {
|
|
var that = this;
|
|
that.refreshHint.removeClass(REFRESHCLASS);
|
|
that.hintContainer.html(that.pullTemplate({}));
|
|
that.yinertia.onEnd();
|
|
that.xinertia.onEnd();
|
|
that.userEvents.cancel();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
if (this.userEvents) {
|
|
this.userEvents.destroy();
|
|
}
|
|
},
|
|
_scale: function (scale) {
|
|
this.dimensions.rescale(scale);
|
|
this.movable.scaleTo(scale);
|
|
},
|
|
_initPullToRefresh: function () {
|
|
var that = this;
|
|
that.dimensions.y.forceEnabled();
|
|
that.pullTemplate = kendo.template(that.options.messages.pullTemplate);
|
|
that.releaseTemplate = kendo.template(that.options.messages.releaseTemplate);
|
|
that.refreshTemplate = kendo.template(that.options.messages.refreshTemplate);
|
|
that.scrollElement.prepend('<span class="km-scroller-pull"><span class="km-icon"></span><span class="km-loading-left"></span><span class="km-loading-right"></span><span class="km-template">' + that.pullTemplate({}) + '</span></span>');
|
|
that.refreshHint = that.scrollElement.children().first();
|
|
that.hintContainer = that.refreshHint.children('.km-template');
|
|
that.pane.y.bind('change', proxy(that._paneChange, that));
|
|
that.userEvents.bind('end', proxy(that._dragEnd, that));
|
|
},
|
|
_dragEnd: function () {
|
|
var that = this;
|
|
if (!that.pulled) {
|
|
return;
|
|
}
|
|
that.pulled = false;
|
|
that.refreshHint.removeClass(RELEASECLASS).addClass(REFRESHCLASS);
|
|
that.hintContainer.html(that.refreshTemplate({}));
|
|
that.yinertia.freeze(that.options.pullOffset / 2);
|
|
that.trigger('pull');
|
|
},
|
|
_paneChange: function () {
|
|
var that = this;
|
|
if (that.movable.y / OUT_OF_BOUNDS_FRICTION > that.options.pullOffset) {
|
|
if (!that.pulled) {
|
|
that.pulled = true;
|
|
that.refreshHint.removeClass(REFRESHCLASS).addClass(RELEASECLASS);
|
|
that.hintContainer.html(that.releaseTemplate({}));
|
|
}
|
|
} else if (that.pulled) {
|
|
that.pulled = false;
|
|
that.refreshHint.removeClass(RELEASECLASS);
|
|
that.hintContainer.html(that.pullTemplate({}));
|
|
}
|
|
},
|
|
_initAxis: function (axis) {
|
|
var that = this, movable = that.movable, dimension = that.dimensions[axis], tapCapture = that.tapCapture, paneAxis = that.pane[axis], scrollBar = new ScrollBar({
|
|
axis: axis,
|
|
movable: movable,
|
|
dimension: dimension,
|
|
container: that.element,
|
|
alwaysVisible: that.options.visibleScrollHints
|
|
});
|
|
dimension.bind(CHANGE, function () {
|
|
scrollBar.refresh();
|
|
});
|
|
paneAxis.bind(CHANGE, function () {
|
|
scrollBar.show();
|
|
});
|
|
that[axis + 'inertia'] = new DragInertia({
|
|
axis: axis,
|
|
paneAxis: paneAxis,
|
|
movable: movable,
|
|
tapCapture: tapCapture,
|
|
userEvents: that.userEvents,
|
|
dimension: dimension,
|
|
elastic: that.options.elastic,
|
|
friction: that.options.friction || FRICTION,
|
|
velocityMultiplier: that.options.velocityMultiplier || VELOCITY_MULTIPLIER,
|
|
end: function () {
|
|
scrollBar.hide();
|
|
that.trigger('scrollEnd', {
|
|
axis: axis,
|
|
scrollTop: that.scrollTop,
|
|
scrollLeft: that.scrollLeft
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
ui.plugin(Scroller);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.groupable', [
|
|
'kendo.core',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'groupable',
|
|
name: 'Groupable',
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'draganddrop'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, isRtl = false, NS = '.kendoGroupable', CHANGE = 'change', indicatorTmpl = kendo.template('<div class="k-group-indicator" data-#=data.ns#field="${data.field}" data-#=data.ns#title="${data.title || ""}" data-#=data.ns#dir="${data.dir || "asc"}">' + '<a href="\\#" class="k-link">' + '<span class="k-icon k-si-arrow-${(data.dir || "asc") == "asc" ? "n" : "s"}">(sorted ${(data.dir || "asc") == "asc" ? "ascending": "descending"})</span>' + '${data.title ? data.title: data.field}' + '</a>' + '<a class="k-button k-button-icon k-button-bare">' + '<span class="k-icon k-group-delete"></span>' + '</a>' + '</div>', { useWithBlock: false }), hint = function (target) {
|
|
return $('<div class="k-header k-drag-clue" />').css({
|
|
width: target.width(),
|
|
paddingLeft: target.css('paddingLeft'),
|
|
paddingRight: target.css('paddingRight'),
|
|
lineHeight: target.height() + 'px',
|
|
paddingTop: target.css('paddingTop'),
|
|
paddingBottom: target.css('paddingBottom')
|
|
}).html(kendo.htmlEncode(target.attr(kendo.attr('title'))) || target.attr(kendo.attr('field'))).prepend('<span class="k-icon k-drag-status k-denied" />');
|
|
}, dropCue = $('<div class="k-grouping-dropclue"/>');
|
|
function dropCueOffsetTop(element) {
|
|
return element.position().top + 3;
|
|
}
|
|
var Groupable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, group = kendo.guid(), intializePositions = proxy(that._intializePositions, that), draggable, horizontalCuePosition, dropCuePositions = that._dropCuePositions = [];
|
|
Widget.fn.init.call(that, element, options);
|
|
isRtl = kendo.support.isRtl(element);
|
|
horizontalCuePosition = isRtl ? 'right' : 'left';
|
|
that.draggable = draggable = that.options.draggable || new kendo.ui.Draggable(that.element, {
|
|
filter: that.options.draggableElements,
|
|
hint: hint,
|
|
group: group
|
|
});
|
|
that.groupContainer = $(that.options.groupContainer, that.element).kendoDropTarget({
|
|
group: draggable.options.group,
|
|
dragenter: function (e) {
|
|
if (that._canDrag(e.draggable.currentTarget)) {
|
|
e.draggable.hint.find('.k-drag-status').removeClass('k-denied').addClass('k-add');
|
|
dropCue.css('top', dropCueOffsetTop(that.groupContainer)).css(horizontalCuePosition, 0).appendTo(that.groupContainer);
|
|
}
|
|
},
|
|
dragleave: function (e) {
|
|
e.draggable.hint.find('.k-drag-status').removeClass('k-add').addClass('k-denied');
|
|
dropCue.remove();
|
|
},
|
|
drop: function (e) {
|
|
var targetElement = e.draggable.currentTarget, field = targetElement.attr(kendo.attr('field')), title = targetElement.attr(kendo.attr('title')), sourceIndicator = that.indicator(field), dropCuePositions = that._dropCuePositions, lastCuePosition = dropCuePositions[dropCuePositions.length - 1], position;
|
|
if (!targetElement.hasClass('k-group-indicator') && !that._canDrag(targetElement)) {
|
|
return;
|
|
}
|
|
if (lastCuePosition) {
|
|
position = that._dropCuePosition(kendo.getOffset(dropCue).left + parseInt(lastCuePosition.element.css('marginLeft'), 10) * (isRtl ? -1 : 1) + parseInt(lastCuePosition.element.css('marginRight'), 10));
|
|
if (position && that._canDrop($(sourceIndicator), position.element, position.left)) {
|
|
if (position.before) {
|
|
position.element.before(sourceIndicator || that.buildIndicator(field, title));
|
|
} else {
|
|
position.element.after(sourceIndicator || that.buildIndicator(field, title));
|
|
}
|
|
that._change();
|
|
}
|
|
} else {
|
|
that.groupContainer.append(that.buildIndicator(field, title));
|
|
that._change();
|
|
}
|
|
}
|
|
}).kendoDraggable({
|
|
filter: 'div.k-group-indicator',
|
|
hint: hint,
|
|
group: draggable.options.group,
|
|
dragcancel: proxy(that._dragCancel, that),
|
|
dragstart: function (e) {
|
|
var element = e.currentTarget, marginLeft = parseInt(element.css('marginLeft'), 10), elementPosition = element.position(), left = isRtl ? elementPosition.left - marginLeft : elementPosition.left + element.outerWidth();
|
|
intializePositions();
|
|
dropCue.css({
|
|
top: dropCueOffsetTop(that.groupContainer),
|
|
left: left
|
|
}).appendTo(that.groupContainer);
|
|
this.hint.find('.k-drag-status').removeClass('k-denied').addClass('k-add');
|
|
},
|
|
dragend: function () {
|
|
that._dragEnd(this);
|
|
},
|
|
drag: proxy(that._drag, that)
|
|
}).on('click' + NS, '.k-button', function (e) {
|
|
e.preventDefault();
|
|
that._removeIndicator($(this).parent());
|
|
}).on('click' + NS, '.k-link', function (e) {
|
|
var current = $(this).parent(), newIndicator = that.buildIndicator(current.attr(kendo.attr('field')), current.attr(kendo.attr('title')), current.attr(kendo.attr('dir')) == 'asc' ? 'desc' : 'asc');
|
|
current.before(newIndicator).remove();
|
|
that._change();
|
|
e.preventDefault();
|
|
});
|
|
draggable.bind([
|
|
'dragend',
|
|
'dragcancel',
|
|
'dragstart',
|
|
'drag'
|
|
], {
|
|
dragend: function () {
|
|
that._dragEnd(this);
|
|
},
|
|
dragcancel: proxy(that._dragCancel, that),
|
|
dragstart: function (e) {
|
|
var element, marginRight, left;
|
|
if (!that.options.allowDrag && !that._canDrag(e.currentTarget)) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
intializePositions();
|
|
if (dropCuePositions.length) {
|
|
element = dropCuePositions[dropCuePositions.length - 1].element;
|
|
marginRight = parseInt(element.css('marginRight'), 10);
|
|
left = element.position().left + element.outerWidth() + marginRight;
|
|
} else {
|
|
left = 0;
|
|
}
|
|
},
|
|
drag: proxy(that._drag, that)
|
|
});
|
|
that.dataSource = that.options.dataSource;
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
} else {
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
}
|
|
if (that.dataSource) {
|
|
that.dataSource.bind('change', that._refreshHandler);
|
|
that.refresh();
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var that = this, dataSource = that.dataSource;
|
|
if (that.groupContainer) {
|
|
that.groupContainer.empty().append($.map(dataSource.group() || [], function (item) {
|
|
var fieldName = item.field;
|
|
var attr = kendo.attr('field');
|
|
var element = that.element.find(that.options.filter).filter(function () {
|
|
return $(this).attr(attr) === fieldName;
|
|
});
|
|
return that.buildIndicator(item.field, element.attr(kendo.attr('title')), item.dir);
|
|
}).join(''));
|
|
}
|
|
that._invalidateGroupContainer();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.groupContainer.off(NS);
|
|
if (that.groupContainer.data('kendoDropTarget')) {
|
|
that.groupContainer.data('kendoDropTarget').destroy();
|
|
}
|
|
if (that.groupContainer.data('kendoDraggable')) {
|
|
that.groupContainer.data('kendoDraggable').destroy();
|
|
}
|
|
if (!that.options.draggable) {
|
|
that.draggable.destroy();
|
|
}
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind('change', that._refreshHandler);
|
|
that._refreshHandler = null;
|
|
}
|
|
that.groupContainer = that.element = that.draggable = null;
|
|
},
|
|
options: {
|
|
name: 'Groupable',
|
|
filter: 'th',
|
|
draggableElements: 'th',
|
|
messages: { empty: 'Drag a column header and drop it here to group by that column' }
|
|
},
|
|
indicator: function (field) {
|
|
var indicators = $('.k-group-indicator', this.groupContainer);
|
|
return $.grep(indicators, function (item) {
|
|
return $(item).attr(kendo.attr('field')) === field;
|
|
})[0];
|
|
},
|
|
buildIndicator: function (field, title, dir) {
|
|
return indicatorTmpl({
|
|
field: field.replace(/"/g, '\''),
|
|
dir: dir,
|
|
title: title,
|
|
ns: kendo.ns
|
|
});
|
|
},
|
|
descriptors: function () {
|
|
var that = this, indicators = $('.k-group-indicator', that.groupContainer), aggregates, names, field, idx, length;
|
|
aggregates = that.element.find(that.options.filter).map(function () {
|
|
var cell = $(this), aggregate = cell.attr(kendo.attr('aggregates')), member = cell.attr(kendo.attr('field'));
|
|
if (aggregate && aggregate !== '') {
|
|
names = aggregate.split(',');
|
|
aggregate = [];
|
|
for (idx = 0, length = names.length; idx < length; idx++) {
|
|
aggregate.push({
|
|
field: member,
|
|
aggregate: names[idx]
|
|
});
|
|
}
|
|
}
|
|
return aggregate;
|
|
}).toArray();
|
|
return $.map(indicators, function (item) {
|
|
item = $(item);
|
|
field = item.attr(kendo.attr('field'));
|
|
return {
|
|
field: field,
|
|
dir: item.attr(kendo.attr('dir')),
|
|
aggregates: aggregates || []
|
|
};
|
|
});
|
|
},
|
|
_removeIndicator: function (indicator) {
|
|
var that = this;
|
|
indicator.remove();
|
|
that._invalidateGroupContainer();
|
|
that._change();
|
|
},
|
|
_change: function () {
|
|
var that = this;
|
|
if (that.dataSource) {
|
|
that.dataSource.group(that.descriptors());
|
|
}
|
|
},
|
|
_dropCuePosition: function (position) {
|
|
var dropCuePositions = this._dropCuePositions;
|
|
if (!dropCue.is(':visible') || dropCuePositions.length === 0) {
|
|
return;
|
|
}
|
|
position = Math.ceil(position);
|
|
var lastCuePosition = dropCuePositions[dropCuePositions.length - 1], left = lastCuePosition.left, right = lastCuePosition.right, marginLeft = parseInt(lastCuePosition.element.css('marginLeft'), 10), marginRight = parseInt(lastCuePosition.element.css('marginRight'), 10);
|
|
if (position >= right && !isRtl || position < left && isRtl) {
|
|
position = {
|
|
left: lastCuePosition.element.position().left + (!isRtl ? lastCuePosition.element.outerWidth() + marginRight : -marginLeft),
|
|
element: lastCuePosition.element,
|
|
before: false
|
|
};
|
|
} else {
|
|
position = $.grep(dropCuePositions, function (item) {
|
|
return item.left <= position && position <= item.right || isRtl && position > item.right;
|
|
})[0];
|
|
if (position) {
|
|
position = {
|
|
left: isRtl ? position.element.position().left + position.element.outerWidth() + marginRight : position.element.position().left - marginLeft,
|
|
element: position.element,
|
|
before: true
|
|
};
|
|
}
|
|
}
|
|
return position;
|
|
},
|
|
_drag: function (event) {
|
|
var position = this._dropCuePosition(event.x.location);
|
|
if (position) {
|
|
dropCue.css({
|
|
left: position.left,
|
|
right: 'auto'
|
|
});
|
|
}
|
|
},
|
|
_canDrag: function (element) {
|
|
var field = element.attr(kendo.attr('field'));
|
|
return element.attr(kendo.attr('groupable')) != 'false' && field && (element.hasClass('k-group-indicator') || !this.indicator(field));
|
|
},
|
|
_canDrop: function (source, target, position) {
|
|
var next = source.next(), result = source[0] !== target[0] && (!next[0] || target[0] !== next[0] || (!isRtl && position > next.position().left || isRtl && position < next.position().left));
|
|
return result;
|
|
},
|
|
_dragEnd: function (draggable) {
|
|
var that = this, field = draggable.currentTarget.attr(kendo.attr('field')), sourceIndicator = that.indicator(field);
|
|
if (draggable !== that.options.draggable && !draggable.dropped && sourceIndicator) {
|
|
that._removeIndicator($(sourceIndicator));
|
|
}
|
|
that._dragCancel();
|
|
},
|
|
_dragCancel: function () {
|
|
dropCue.remove();
|
|
this._dropCuePositions = [];
|
|
},
|
|
_intializePositions: function () {
|
|
var that = this, indicators = $('.k-group-indicator', that.groupContainer), left;
|
|
that._dropCuePositions = $.map(indicators, function (item) {
|
|
item = $(item);
|
|
left = kendo.getOffset(item).left;
|
|
return {
|
|
left: parseInt(left, 10),
|
|
right: parseInt(left + item.outerWidth(), 10),
|
|
element: item
|
|
};
|
|
});
|
|
},
|
|
_invalidateGroupContainer: function () {
|
|
var groupContainer = this.groupContainer;
|
|
if (groupContainer && groupContainer.is(':empty')) {
|
|
groupContainer.html(this.options.messages.empty);
|
|
}
|
|
}
|
|
});
|
|
kendo.ui.plugin(Groupable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.reorderable', [
|
|
'kendo.core',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'reorderable',
|
|
name: 'Reorderable',
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'draganddrop'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, getOffset = kendo.getOffset, Widget = kendo.ui.Widget, CHANGE = 'change', KREORDERABLE = 'k-reorderable';
|
|
function toggleHintClass(hint, denied) {
|
|
hint = $(hint);
|
|
if (denied) {
|
|
hint.find('.k-drag-status').removeClass('k-add').addClass('k-denied');
|
|
} else {
|
|
hint.find('.k-drag-status').removeClass('k-denied').addClass('k-add');
|
|
}
|
|
}
|
|
var Reorderable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, draggable, group = kendo.guid() + '-reorderable';
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element.addClass(KREORDERABLE);
|
|
options = that.options;
|
|
that.draggable = draggable = options.draggable || new kendo.ui.Draggable(element, {
|
|
group: group,
|
|
autoScroll: true,
|
|
filter: options.filter,
|
|
hint: options.hint
|
|
});
|
|
that.reorderDropCue = $('<div class="k-reorder-cue"><div class="k-icon k-i-arrow-s"></div><div class="k-icon k-i-arrow-n"></div></div>');
|
|
element.find(draggable.options.filter).kendoDropTarget({
|
|
group: draggable.options.group,
|
|
dragenter: function (e) {
|
|
if (!that._draggable) {
|
|
return;
|
|
}
|
|
var dropTarget = this.element, offset;
|
|
var denied = !that._dropTargetAllowed(dropTarget) || that._isLastDraggable();
|
|
toggleHintClass(e.draggable.hint, denied);
|
|
if (!denied) {
|
|
offset = getOffset(dropTarget);
|
|
var left = offset.left;
|
|
if (options.inSameContainer && !options.inSameContainer({
|
|
source: dropTarget,
|
|
target: that._draggable,
|
|
sourceIndex: that._index(dropTarget),
|
|
targetIndex: that._index(that._draggable)
|
|
})) {
|
|
that._dropTarget = dropTarget;
|
|
} else {
|
|
if (that._index(dropTarget) > that._index(that._draggable)) {
|
|
left += dropTarget.outerWidth();
|
|
}
|
|
}
|
|
that.reorderDropCue.css({
|
|
height: dropTarget.outerHeight(),
|
|
top: offset.top,
|
|
left: left
|
|
}).appendTo(document.body);
|
|
}
|
|
},
|
|
dragleave: function (e) {
|
|
toggleHintClass(e.draggable.hint, true);
|
|
that.reorderDropCue.remove();
|
|
that._dropTarget = null;
|
|
},
|
|
drop: function () {
|
|
that._dropTarget = null;
|
|
if (!that._draggable) {
|
|
return;
|
|
}
|
|
var dropTarget = this.element;
|
|
var draggable = that._draggable;
|
|
if (that._dropTargetAllowed(dropTarget) && !that._isLastDraggable()) {
|
|
that.trigger(CHANGE, {
|
|
element: that._draggable,
|
|
target: dropTarget,
|
|
oldIndex: that._index(draggable),
|
|
newIndex: that._index(dropTarget),
|
|
position: getOffset(that.reorderDropCue).left > getOffset(dropTarget).left ? 'after' : 'before'
|
|
});
|
|
}
|
|
}
|
|
});
|
|
draggable.bind([
|
|
'dragcancel',
|
|
'dragend',
|
|
'dragstart',
|
|
'drag'
|
|
], {
|
|
dragcancel: function () {
|
|
that.reorderDropCue.remove();
|
|
that._draggable = null;
|
|
that._elements = null;
|
|
},
|
|
dragend: function () {
|
|
that.reorderDropCue.remove();
|
|
that._draggable = null;
|
|
that._elements = null;
|
|
},
|
|
dragstart: function (e) {
|
|
that._draggable = e.currentTarget;
|
|
that._elements = that.element.find(that.draggable.options.filter);
|
|
},
|
|
drag: function (e) {
|
|
if (!that._dropTarget || this.hint.find('.k-drag-status').hasClass('k-denied')) {
|
|
return;
|
|
}
|
|
var dropStartOffset = getOffset(that._dropTarget).left;
|
|
var width = that._dropTarget.outerWidth();
|
|
if (e.pageX > dropStartOffset + width / 2) {
|
|
that.reorderDropCue.css({ left: dropStartOffset + width });
|
|
} else {
|
|
that.reorderDropCue.css({ left: dropStartOffset });
|
|
}
|
|
}
|
|
});
|
|
},
|
|
options: {
|
|
name: 'Reorderable',
|
|
filter: '*'
|
|
},
|
|
events: [CHANGE],
|
|
_isLastDraggable: function () {
|
|
var inSameContainer = this.options.inSameContainer, draggable = this._draggable[0], elements = this._elements.get(), found = false, item;
|
|
if (!inSameContainer) {
|
|
return false;
|
|
}
|
|
while (!found && elements.length > 0) {
|
|
item = elements.pop();
|
|
found = draggable !== item && inSameContainer({
|
|
source: draggable,
|
|
target: item,
|
|
sourceIndex: this._index(draggable),
|
|
targetIndex: this._index(item)
|
|
});
|
|
}
|
|
return !found;
|
|
},
|
|
_dropTargetAllowed: function (dropTarget) {
|
|
var inSameContainer = this.options.inSameContainer, dragOverContainers = this.options.dragOverContainers, draggable = this._draggable;
|
|
if (draggable[0] === dropTarget[0]) {
|
|
return false;
|
|
}
|
|
if (!inSameContainer || !dragOverContainers) {
|
|
return true;
|
|
}
|
|
if (inSameContainer({
|
|
source: draggable,
|
|
target: dropTarget,
|
|
sourceIndex: this._index(draggable),
|
|
targetIndex: this._index(dropTarget)
|
|
})) {
|
|
return true;
|
|
}
|
|
return dragOverContainers(this._index(draggable), this._index(dropTarget));
|
|
},
|
|
_index: function (element) {
|
|
return this._elements.index(element);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.element.find(that.draggable.options.filter).each(function () {
|
|
var item = $(this);
|
|
if (item.data('kendoDropTarget')) {
|
|
item.data('kendoDropTarget').destroy();
|
|
}
|
|
});
|
|
if (that.draggable) {
|
|
that.draggable.destroy();
|
|
that.draggable.element = that.draggable = null;
|
|
}
|
|
that.elements = that.reorderDropCue = that._elements = that._draggable = null;
|
|
}
|
|
});
|
|
kendo.ui.plugin(Reorderable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.resizable', [
|
|
'kendo.core',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'resizable',
|
|
name: 'Resizable',
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'draganddrop'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, proxy = $.proxy, isFunction = kendo.isFunction, extend = $.extend, HORIZONTAL = 'horizontal', VERTICAL = 'vertical', START = 'start', RESIZE = 'resize', RESIZEEND = 'resizeend';
|
|
var Resizable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.orientation = that.options.orientation.toLowerCase() != VERTICAL ? HORIZONTAL : VERTICAL;
|
|
that._positionMouse = that.orientation == HORIZONTAL ? 'x' : 'y';
|
|
that._position = that.orientation == HORIZONTAL ? 'left' : 'top';
|
|
that._sizingDom = that.orientation == HORIZONTAL ? 'outerWidth' : 'outerHeight';
|
|
that.draggable = new ui.Draggable(element, {
|
|
distance: 1,
|
|
filter: options.handle,
|
|
drag: proxy(that._resize, that),
|
|
dragcancel: proxy(that._cancel, that),
|
|
dragstart: proxy(that._start, that),
|
|
dragend: proxy(that._stop, that)
|
|
});
|
|
that.userEvents = that.draggable.userEvents;
|
|
},
|
|
events: [
|
|
RESIZE,
|
|
RESIZEEND,
|
|
START
|
|
],
|
|
options: {
|
|
name: 'Resizable',
|
|
orientation: HORIZONTAL
|
|
},
|
|
resize: function () {
|
|
},
|
|
_max: function (e) {
|
|
var that = this, hintSize = that.hint ? that.hint[that._sizingDom]() : 0, size = that.options.max;
|
|
return isFunction(size) ? size(e) : size !== undefined ? that._initialElementPosition + size - hintSize : size;
|
|
},
|
|
_min: function (e) {
|
|
var that = this, size = that.options.min;
|
|
return isFunction(size) ? size(e) : size !== undefined ? that._initialElementPosition + size : size;
|
|
},
|
|
_start: function (e) {
|
|
var that = this, hint = that.options.hint, el = $(e.currentTarget);
|
|
that._initialElementPosition = el.position()[that._position];
|
|
that._initialMousePosition = e[that._positionMouse].startLocation;
|
|
if (hint) {
|
|
that.hint = isFunction(hint) ? $(hint(el)) : hint;
|
|
that.hint.css({ position: 'absolute' }).css(that._position, that._initialElementPosition).appendTo(that.element);
|
|
}
|
|
that.trigger(START, e);
|
|
that._maxPosition = that._max(e);
|
|
that._minPosition = that._min(e);
|
|
$(document.body).css('cursor', el.css('cursor'));
|
|
},
|
|
_resize: function (e) {
|
|
var that = this, maxPosition = that._maxPosition, minPosition = that._minPosition, currentPosition = that._initialElementPosition + (e[that._positionMouse].location - that._initialMousePosition), position;
|
|
position = minPosition !== undefined ? Math.max(minPosition, currentPosition) : currentPosition;
|
|
that.position = position = maxPosition !== undefined ? Math.min(maxPosition, position) : position;
|
|
if (that.hint) {
|
|
that.hint.toggleClass(that.options.invalidClass || '', position == maxPosition || position == minPosition).css(that._position, position);
|
|
}
|
|
that.resizing = true;
|
|
that.trigger(RESIZE, extend(e, { position: position }));
|
|
},
|
|
_stop: function (e) {
|
|
var that = this;
|
|
if (that.hint) {
|
|
that.hint.remove();
|
|
}
|
|
that.resizing = false;
|
|
that.trigger(RESIZEEND, extend(e, { position: that.position }));
|
|
$(document.body).css('cursor', '');
|
|
},
|
|
_cancel: function (e) {
|
|
var that = this;
|
|
if (that.hint) {
|
|
that.position = undefined;
|
|
that.hint.css(that._position, that._initialElementPosition);
|
|
that._stop(e);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
if (that.draggable) {
|
|
that.draggable.destroy();
|
|
}
|
|
},
|
|
press: function (target) {
|
|
if (!target) {
|
|
return;
|
|
}
|
|
var position = target.position(), that = this;
|
|
that.userEvents.press(position.left, position.top, target[0]);
|
|
that.targetPosition = position;
|
|
that.target = target;
|
|
},
|
|
move: function (delta) {
|
|
var that = this, orientation = that._position, position = that.targetPosition, current = that.position;
|
|
if (current === undefined) {
|
|
current = position[orientation];
|
|
}
|
|
position[orientation] = current + delta;
|
|
that.userEvents.move(position.left, position.top);
|
|
},
|
|
end: function () {
|
|
this.userEvents.end();
|
|
this.target = this.position = undefined;
|
|
}
|
|
});
|
|
kendo.ui.plugin(Resizable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.sortable', ['kendo.draganddrop'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'sortable',
|
|
name: 'Sortable',
|
|
category: 'framework',
|
|
depends: ['draganddrop']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, START = 'start', BEFORE_MOVE = 'beforeMove', MOVE = 'move', END = 'end', CHANGE = 'change', CANCEL = 'cancel', ACTION_SORT = 'sort', ACTION_REMOVE = 'remove', ACTION_RECEIVE = 'receive', DEFAULT_FILTER = '>*', MISSING_INDEX = -1;
|
|
function containsOrEqualTo(parent, child) {
|
|
try {
|
|
return $.contains(parent, child) || parent == child;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
function defaultHint(element) {
|
|
return element.clone();
|
|
}
|
|
function defaultPlaceholder(element) {
|
|
return element.clone().removeAttr('id').css('visibility', 'hidden');
|
|
}
|
|
var Sortable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
if (!that.options.placeholder) {
|
|
that.options.placeholder = defaultPlaceholder;
|
|
}
|
|
if (!that.options.hint) {
|
|
that.options.hint = defaultHint;
|
|
}
|
|
that.draggable = that._createDraggable();
|
|
},
|
|
events: [
|
|
START,
|
|
BEFORE_MOVE,
|
|
MOVE,
|
|
END,
|
|
CHANGE,
|
|
CANCEL
|
|
],
|
|
options: {
|
|
name: 'Sortable',
|
|
hint: null,
|
|
placeholder: null,
|
|
filter: DEFAULT_FILTER,
|
|
holdToDrag: false,
|
|
disabled: null,
|
|
container: null,
|
|
connectWith: null,
|
|
handler: null,
|
|
cursorOffset: null,
|
|
axis: null,
|
|
ignore: null,
|
|
autoScroll: false,
|
|
cursor: 'auto',
|
|
moveOnDragEnter: false
|
|
},
|
|
destroy: function () {
|
|
this.draggable.destroy();
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
_createDraggable: function () {
|
|
var that = this, element = that.element, options = that.options;
|
|
return new kendo.ui.Draggable(element, {
|
|
filter: options.filter,
|
|
hint: kendo.isFunction(options.hint) ? options.hint : $(options.hint),
|
|
holdToDrag: options.holdToDrag,
|
|
container: options.container ? $(options.container) : null,
|
|
cursorOffset: options.cursorOffset,
|
|
axis: options.axis,
|
|
ignore: options.ignore,
|
|
autoScroll: options.autoScroll,
|
|
dragstart: $.proxy(that._dragstart, that),
|
|
dragcancel: $.proxy(that._dragcancel, that),
|
|
drag: $.proxy(that._drag, that),
|
|
dragend: $.proxy(that._dragend, that)
|
|
});
|
|
},
|
|
_dragstart: function (e) {
|
|
var draggedElement = this.draggedElement = e.currentTarget, disabled = this.options.disabled, handler = this.options.handler, _placeholder = this.options.placeholder, placeholder = this.placeholder = kendo.isFunction(_placeholder) ? $(_placeholder.call(this, draggedElement)) : $(_placeholder);
|
|
if (disabled && draggedElement.is(disabled)) {
|
|
e.preventDefault();
|
|
} else if (handler && !$(e.initialTarget).is(handler)) {
|
|
e.preventDefault();
|
|
} else {
|
|
if (this.trigger(START, {
|
|
item: draggedElement,
|
|
draggableEvent: e
|
|
})) {
|
|
e.preventDefault();
|
|
} else {
|
|
draggedElement.css('display', 'none');
|
|
draggedElement.before(placeholder);
|
|
this._setCursor();
|
|
}
|
|
}
|
|
},
|
|
_dragcancel: function () {
|
|
this._cancel();
|
|
this.trigger(CANCEL, { item: this.draggedElement });
|
|
this._resetCursor();
|
|
},
|
|
_drag: function (e) {
|
|
var draggedElement = this.draggedElement, target = this._findTarget(e), targetCenter, cursorOffset = {
|
|
left: e.x.location,
|
|
top: e.y.location
|
|
}, offsetDelta, axisDelta = {
|
|
x: e.x.delta,
|
|
y: e.y.delta
|
|
}, direction, sibling, getSibling, axis = this.options.axis, moveOnDragEnter = this.options.moveOnDragEnter, eventData = {
|
|
item: draggedElement,
|
|
list: this,
|
|
draggableEvent: e
|
|
};
|
|
if (axis === 'x' || axis === 'y') {
|
|
this._movementByAxis(axis, cursorOffset, axisDelta[axis], eventData);
|
|
return;
|
|
}
|
|
if (target) {
|
|
targetCenter = this._getElementCenter(target.element);
|
|
offsetDelta = {
|
|
left: Math.round(cursorOffset.left - targetCenter.left),
|
|
top: Math.round(cursorOffset.top - targetCenter.top)
|
|
};
|
|
$.extend(eventData, { target: target.element });
|
|
if (target.appendToBottom) {
|
|
this._movePlaceholder(target, null, eventData);
|
|
return;
|
|
}
|
|
if (target.appendAfterHidden) {
|
|
this._movePlaceholder(target, 'next', eventData);
|
|
}
|
|
if (this._isFloating(target.element)) {
|
|
if (axisDelta.x < 0 && (moveOnDragEnter || offsetDelta.left < 0)) {
|
|
direction = 'prev';
|
|
} else if (axisDelta.x > 0 && (moveOnDragEnter || offsetDelta.left > 0)) {
|
|
direction = 'next';
|
|
}
|
|
} else {
|
|
if (axisDelta.y < 0 && (moveOnDragEnter || offsetDelta.top < 0)) {
|
|
direction = 'prev';
|
|
} else if (axisDelta.y > 0 && (moveOnDragEnter || offsetDelta.top > 0)) {
|
|
direction = 'next';
|
|
}
|
|
}
|
|
if (direction) {
|
|
getSibling = direction === 'prev' ? jQuery.fn.prev : jQuery.fn.next;
|
|
sibling = getSibling.call(target.element);
|
|
while (sibling.length && !sibling.is(':visible')) {
|
|
sibling = getSibling.call(sibling);
|
|
}
|
|
if (sibling[0] != this.placeholder[0]) {
|
|
this._movePlaceholder(target, direction, eventData);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_dragend: function (e) {
|
|
var placeholder = this.placeholder, draggedElement = this.draggedElement, draggedIndex = this.indexOf(draggedElement), placeholderIndex = this.indexOf(placeholder), connectWith = this.options.connectWith, connectedList, isDefaultPrevented, eventData, connectedListEventData;
|
|
this._resetCursor();
|
|
eventData = {
|
|
action: ACTION_SORT,
|
|
item: draggedElement,
|
|
oldIndex: draggedIndex,
|
|
newIndex: placeholderIndex,
|
|
draggableEvent: e
|
|
};
|
|
if (placeholderIndex >= 0) {
|
|
isDefaultPrevented = this.trigger(END, eventData);
|
|
} else {
|
|
connectedList = placeholder.parents(connectWith).getKendoSortable();
|
|
eventData.action = ACTION_REMOVE;
|
|
connectedListEventData = $.extend({}, eventData, {
|
|
action: ACTION_RECEIVE,
|
|
oldIndex: MISSING_INDEX,
|
|
newIndex: connectedList.indexOf(placeholder)
|
|
});
|
|
isDefaultPrevented = !(!this.trigger(END, eventData) && !connectedList.trigger(END, connectedListEventData));
|
|
}
|
|
if (isDefaultPrevented || placeholderIndex === draggedIndex) {
|
|
this._cancel();
|
|
return;
|
|
}
|
|
placeholder.replaceWith(draggedElement);
|
|
draggedElement.show();
|
|
this.draggable.dropped = true;
|
|
eventData = {
|
|
action: this.indexOf(draggedElement) != MISSING_INDEX ? ACTION_SORT : ACTION_REMOVE,
|
|
item: draggedElement,
|
|
oldIndex: draggedIndex,
|
|
newIndex: this.indexOf(draggedElement),
|
|
draggableEvent: e
|
|
};
|
|
this.trigger(CHANGE, eventData);
|
|
if (connectedList) {
|
|
connectedListEventData = $.extend({}, eventData, {
|
|
action: ACTION_RECEIVE,
|
|
oldIndex: MISSING_INDEX,
|
|
newIndex: connectedList.indexOf(draggedElement)
|
|
});
|
|
connectedList.trigger(CHANGE, connectedListEventData);
|
|
}
|
|
},
|
|
_findTarget: function (e) {
|
|
var element = this._findElementUnderCursor(e), items, connectWith = this.options.connectWith, node;
|
|
if ($.contains(this.element[0], element)) {
|
|
items = this.items();
|
|
node = items.filter(element)[0] || items.has(element)[0];
|
|
return node ? {
|
|
element: $(node),
|
|
sortable: this
|
|
} : null;
|
|
} else if (this.element[0] == element && this._isEmpty()) {
|
|
return {
|
|
element: this.element,
|
|
sortable: this,
|
|
appendToBottom: true
|
|
};
|
|
} else if (this.element[0] == element && this._isLastHidden()) {
|
|
node = this.items().eq(0);
|
|
return {
|
|
element: node,
|
|
sortable: this,
|
|
appendAfterHidden: true
|
|
};
|
|
} else if (connectWith) {
|
|
return this._searchConnectedTargets(element, e);
|
|
}
|
|
},
|
|
_findElementUnderCursor: function (e) {
|
|
var elementUnderCursor = kendo.elementUnderCursor(e), draggable = e.sender;
|
|
if (containsOrEqualTo(draggable.hint[0], elementUnderCursor)) {
|
|
draggable.hint.hide();
|
|
elementUnderCursor = kendo.elementUnderCursor(e);
|
|
if (!elementUnderCursor) {
|
|
elementUnderCursor = kendo.elementUnderCursor(e);
|
|
}
|
|
draggable.hint.show();
|
|
}
|
|
return elementUnderCursor;
|
|
},
|
|
_searchConnectedTargets: function (element, e) {
|
|
var connected = $(this.options.connectWith), sortableInstance, items, node;
|
|
for (var i = 0; i < connected.length; i++) {
|
|
sortableInstance = connected.eq(i).getKendoSortable();
|
|
if ($.contains(connected[i], element)) {
|
|
if (sortableInstance) {
|
|
items = sortableInstance.items();
|
|
node = items.filter(element)[0] || items.has(element)[0];
|
|
if (node) {
|
|
sortableInstance.placeholder = this.placeholder;
|
|
return {
|
|
element: $(node),
|
|
sortable: sortableInstance
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
} else if (connected[i] == element) {
|
|
if (sortableInstance && sortableInstance._isEmpty()) {
|
|
return {
|
|
element: connected.eq(i),
|
|
sortable: sortableInstance,
|
|
appendToBottom: true
|
|
};
|
|
} else if (this._isCursorAfterLast(sortableInstance, e)) {
|
|
node = sortableInstance.items().last();
|
|
return {
|
|
element: node,
|
|
sortable: sortableInstance
|
|
};
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_isCursorAfterLast: function (sortable, e) {
|
|
var lastItem = sortable.items().last(), cursorOffset = {
|
|
left: e.x.location,
|
|
top: e.y.location
|
|
}, lastItemOffset, delta;
|
|
lastItemOffset = kendo.getOffset(lastItem);
|
|
lastItemOffset.top += lastItem.outerHeight();
|
|
lastItemOffset.left += lastItem.outerWidth();
|
|
if (this._isFloating(lastItem)) {
|
|
delta = lastItemOffset.left - cursorOffset.left;
|
|
} else {
|
|
delta = lastItemOffset.top - cursorOffset.top;
|
|
}
|
|
return delta < 0 ? true : false;
|
|
},
|
|
_movementByAxis: function (axis, cursorOffset, delta, eventData) {
|
|
var cursorPosition = axis === 'x' ? cursorOffset.left : cursorOffset.top, target = delta < 0 ? this.placeholder.prev() : this.placeholder.next(), targetCenter;
|
|
if (target.length && !target.is(':visible')) {
|
|
target = delta < 0 ? target.prev() : target.next();
|
|
}
|
|
$.extend(eventData, { target: target });
|
|
targetCenter = this._getElementCenter(target);
|
|
if (targetCenter) {
|
|
targetCenter = axis === 'x' ? targetCenter.left : targetCenter.top;
|
|
}
|
|
if (target.length && delta < 0 && cursorPosition - targetCenter < 0) {
|
|
this._movePlaceholder({
|
|
element: target,
|
|
sortable: this
|
|
}, 'prev', eventData);
|
|
} else if (target.length && delta > 0 && cursorPosition - targetCenter > 0) {
|
|
this._movePlaceholder({
|
|
element: target,
|
|
sortable: this
|
|
}, 'next', eventData);
|
|
}
|
|
},
|
|
_movePlaceholder: function (target, direction, eventData) {
|
|
var placeholder = this.placeholder;
|
|
if (!target.sortable.trigger(BEFORE_MOVE, eventData)) {
|
|
if (!direction) {
|
|
target.element.append(placeholder);
|
|
} else if (direction === 'prev') {
|
|
target.element.before(placeholder);
|
|
} else if (direction === 'next') {
|
|
target.element.after(placeholder);
|
|
}
|
|
target.sortable.trigger(MOVE, eventData);
|
|
}
|
|
},
|
|
_setCursor: function () {
|
|
var cursor = this.options.cursor, body;
|
|
if (cursor && cursor !== 'auto') {
|
|
body = $(document.body);
|
|
this._originalCursorType = body.css('cursor');
|
|
body.css({ 'cursor': cursor });
|
|
if (!this._cursorStylesheet) {
|
|
this._cursorStylesheet = $('<style>* { cursor: ' + cursor + ' !important; }</style>');
|
|
}
|
|
this._cursorStylesheet.appendTo(body);
|
|
}
|
|
},
|
|
_resetCursor: function () {
|
|
if (this._originalCursorType) {
|
|
$(document.body).css('cursor', this._originalCursorType);
|
|
this._originalCursorType = null;
|
|
this._cursorStylesheet.remove();
|
|
}
|
|
},
|
|
_getElementCenter: function (element) {
|
|
var center = element.length ? kendo.getOffset(element) : null;
|
|
if (center) {
|
|
center.top += element.outerHeight() / 2;
|
|
center.left += element.outerWidth() / 2;
|
|
}
|
|
return center;
|
|
},
|
|
_isFloating: function (item) {
|
|
return /left|right/.test(item.css('float')) || /inline|table-cell/.test(item.css('display'));
|
|
},
|
|
_cancel: function () {
|
|
this.draggedElement.show();
|
|
this.placeholder.remove();
|
|
},
|
|
_items: function () {
|
|
var filter = this.options.filter, items;
|
|
if (filter) {
|
|
items = this.element.find(filter);
|
|
} else {
|
|
items = this.element.children();
|
|
}
|
|
return items;
|
|
},
|
|
indexOf: function (element) {
|
|
var items = this._items(), placeholder = this.placeholder, draggedElement = this.draggedElement;
|
|
if (placeholder && element[0] == placeholder[0]) {
|
|
return items.not(draggedElement).index(element);
|
|
} else {
|
|
return items.not(placeholder).index(element);
|
|
}
|
|
},
|
|
items: function () {
|
|
var placeholder = this.placeholder, items = this._items();
|
|
if (placeholder) {
|
|
items = items.not(placeholder);
|
|
}
|
|
return items;
|
|
},
|
|
_isEmpty: function () {
|
|
return !this.items().length;
|
|
},
|
|
_isLastHidden: function () {
|
|
return this.items().length === 1 && this.items().is(':hidden');
|
|
}
|
|
});
|
|
kendo.ui.plugin(Sortable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.selectable', [
|
|
'kendo.core',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'selectable',
|
|
name: 'Selectable',
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'userevents'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, abs = Math.abs, ARIASELECTED = 'aria-selected', SELECTED = 'k-state-selected', ACTIVE = 'k-state-selecting', SELECTABLE = 'k-selectable', CHANGE = 'change', NS = '.kendoSelectable', UNSELECTING = 'k-state-unselecting', INPUTSELECTOR = 'input,a,textarea,.k-multiselect-wrap,select,button,a.k-button>.k-icon,button.k-button>.k-icon,span.k-icon.k-i-expand,span.k-icon.k-i-collapse', msie = kendo.support.browser.msie, supportEventDelegation = false;
|
|
(function ($) {
|
|
(function () {
|
|
$('<div class="parent"><span /></div>').on('click', '>*', function () {
|
|
supportEventDelegation = true;
|
|
}).find('span').click().end().off();
|
|
}());
|
|
}($));
|
|
var Selectable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, multiple;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._marquee = $('<div class=\'k-marquee\'><div class=\'k-marquee-color\'></div></div>');
|
|
that._lastActive = null;
|
|
that.element.addClass(SELECTABLE);
|
|
that.relatedTarget = that.options.relatedTarget;
|
|
multiple = that.options.multiple;
|
|
if (this.options.aria && multiple) {
|
|
that.element.attr('aria-multiselectable', true);
|
|
}
|
|
that.userEvents = new kendo.UserEvents(that.element, {
|
|
global: true,
|
|
allowSelection: true,
|
|
filter: (!supportEventDelegation ? '.' + SELECTABLE + ' ' : '') + that.options.filter,
|
|
tap: proxy(that._tap, that)
|
|
});
|
|
if (multiple) {
|
|
that.userEvents.bind('start', proxy(that._start, that)).bind('move', proxy(that._move, that)).bind('end', proxy(that._end, that)).bind('select', proxy(that._select, that));
|
|
}
|
|
},
|
|
events: [CHANGE],
|
|
options: {
|
|
name: 'Selectable',
|
|
filter: '>*',
|
|
multiple: false,
|
|
relatedTarget: $.noop
|
|
},
|
|
_isElement: function (target) {
|
|
var elements = this.element;
|
|
var idx, length = elements.length, result = false;
|
|
target = target[0];
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (elements[idx] === target) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_tap: function (e) {
|
|
var target = $(e.target), that = this, ctrlKey = e.event.ctrlKey || e.event.metaKey, multiple = that.options.multiple, shiftKey = multiple && e.event.shiftKey, selected, whichCode = e.event.which, buttonCode = e.event.button;
|
|
if (!that._isElement(target.closest('.' + SELECTABLE)) || whichCode && whichCode == 3 || buttonCode && buttonCode == 2) {
|
|
return;
|
|
}
|
|
if (!this._allowSelection(e.event.target)) {
|
|
return;
|
|
}
|
|
selected = target.hasClass(SELECTED);
|
|
if (!multiple || !ctrlKey) {
|
|
that.clear();
|
|
}
|
|
target = target.add(that.relatedTarget(target));
|
|
if (shiftKey) {
|
|
that.selectRange(that._firstSelectee(), target);
|
|
} else {
|
|
if (selected && ctrlKey) {
|
|
that._unselect(target);
|
|
that._notify(CHANGE);
|
|
} else {
|
|
that.value(target);
|
|
}
|
|
that._lastActive = that._downTarget = target;
|
|
}
|
|
},
|
|
_start: function (e) {
|
|
var that = this, target = $(e.target), selected = target.hasClass(SELECTED), currentElement, ctrlKey = e.event.ctrlKey || e.event.metaKey;
|
|
if (!this._allowSelection(e.event.target)) {
|
|
return;
|
|
}
|
|
that._downTarget = target;
|
|
if (!that._isElement(target.closest('.' + SELECTABLE))) {
|
|
that.userEvents.cancel();
|
|
return;
|
|
}
|
|
if (that.options.useAllItems) {
|
|
that._items = that.element.find(that.options.filter);
|
|
} else {
|
|
currentElement = target.closest(that.element);
|
|
that._items = currentElement.find(that.options.filter);
|
|
}
|
|
e.sender.capture();
|
|
that._marquee.appendTo(document.body).css({
|
|
left: e.x.client + 1,
|
|
top: e.y.client + 1,
|
|
width: 0,
|
|
height: 0
|
|
});
|
|
if (!ctrlKey) {
|
|
that.clear();
|
|
}
|
|
target = target.add(that.relatedTarget(target));
|
|
if (selected) {
|
|
that._selectElement(target, true);
|
|
if (ctrlKey) {
|
|
target.addClass(UNSELECTING);
|
|
}
|
|
}
|
|
},
|
|
_move: function (e) {
|
|
var that = this, position = {
|
|
left: e.x.startLocation > e.x.location ? e.x.location : e.x.startLocation,
|
|
top: e.y.startLocation > e.y.location ? e.y.location : e.y.startLocation,
|
|
width: abs(e.x.initialDelta),
|
|
height: abs(e.y.initialDelta)
|
|
};
|
|
that._marquee.css(position);
|
|
that._invalidateSelectables(position, e.event.ctrlKey || e.event.metaKey);
|
|
e.preventDefault();
|
|
},
|
|
_end: function () {
|
|
var that = this;
|
|
that._marquee.remove();
|
|
that._unselect(that.element.find(that.options.filter + '.' + UNSELECTING)).removeClass(UNSELECTING);
|
|
var target = that.element.find(that.options.filter + '.' + ACTIVE);
|
|
target = target.add(that.relatedTarget(target));
|
|
that.value(target);
|
|
that._lastActive = that._downTarget;
|
|
that._items = null;
|
|
},
|
|
_invalidateSelectables: function (position, ctrlKey) {
|
|
var idx, length, target = this._downTarget[0], items = this._items, related, toSelect;
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
toSelect = items.eq(idx);
|
|
related = toSelect.add(this.relatedTarget(toSelect));
|
|
if (collision(toSelect, position)) {
|
|
if (toSelect.hasClass(SELECTED)) {
|
|
if (ctrlKey && target !== toSelect[0]) {
|
|
related.removeClass(SELECTED).addClass(UNSELECTING);
|
|
}
|
|
} else if (!toSelect.hasClass(ACTIVE) && !toSelect.hasClass(UNSELECTING)) {
|
|
related.addClass(ACTIVE);
|
|
}
|
|
} else {
|
|
if (toSelect.hasClass(ACTIVE)) {
|
|
related.removeClass(ACTIVE);
|
|
} else if (ctrlKey && toSelect.hasClass(UNSELECTING)) {
|
|
related.removeClass(UNSELECTING).addClass(SELECTED);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
value: function (val) {
|
|
var that = this, selectElement = proxy(that._selectElement, that);
|
|
if (val) {
|
|
val.each(function () {
|
|
selectElement(this);
|
|
});
|
|
that._notify(CHANGE);
|
|
return;
|
|
}
|
|
return that.element.find(that.options.filter + '.' + SELECTED);
|
|
},
|
|
_firstSelectee: function () {
|
|
var that = this, selected;
|
|
if (that._lastActive !== null) {
|
|
return that._lastActive;
|
|
}
|
|
selected = that.value();
|
|
return selected.length > 0 ? selected[0] : that.element.find(that.options.filter)[0];
|
|
},
|
|
_selectElement: function (element, preventNotify) {
|
|
var toSelect = $(element), isPrevented = !preventNotify && this._notify('select', { element: element });
|
|
toSelect.removeClass(ACTIVE);
|
|
if (!isPrevented) {
|
|
toSelect.addClass(SELECTED);
|
|
if (this.options.aria) {
|
|
toSelect.attr(ARIASELECTED, true);
|
|
}
|
|
}
|
|
},
|
|
_notify: function (name, args) {
|
|
args = args || {};
|
|
return this.trigger(name, args);
|
|
},
|
|
_unselect: function (element) {
|
|
element.removeClass(SELECTED);
|
|
if (this.options.aria) {
|
|
element.attr(ARIASELECTED, false);
|
|
}
|
|
return element;
|
|
},
|
|
_select: function (e) {
|
|
if (this._allowSelection(e.event.target)) {
|
|
if (!msie || msie && !$(kendo._activeElement()).is(INPUTSELECTOR)) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
_allowSelection: function (target) {
|
|
if ($(target).is(INPUTSELECTOR)) {
|
|
this.userEvents.cancel();
|
|
this._downTarget = null;
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
resetTouchEvents: function () {
|
|
this.userEvents.cancel();
|
|
},
|
|
clear: function () {
|
|
var items = this.element.find(this.options.filter + '.' + SELECTED);
|
|
this._unselect(items);
|
|
},
|
|
selectRange: function (start, end) {
|
|
var that = this, idx, tmp, items;
|
|
that.clear();
|
|
if (that.element.length > 1) {
|
|
items = that.options.continuousItems();
|
|
}
|
|
if (!items || !items.length) {
|
|
items = that.element.find(that.options.filter);
|
|
}
|
|
start = $.inArray($(start)[0], items);
|
|
end = $.inArray($(end)[0], items);
|
|
if (start > end) {
|
|
tmp = start;
|
|
start = end;
|
|
end = tmp;
|
|
}
|
|
if (!that.options.useAllItems) {
|
|
end += that.element.length - 1;
|
|
}
|
|
for (idx = start; idx <= end; idx++) {
|
|
that._selectElement(items[idx]);
|
|
}
|
|
that._notify(CHANGE);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.element.off(NS);
|
|
that.userEvents.destroy();
|
|
that._marquee = that._lastActive = that.element = that.userEvents = null;
|
|
}
|
|
});
|
|
Selectable.parseOptions = function (selectable) {
|
|
var asLowerString = typeof selectable === 'string' && selectable.toLowerCase();
|
|
return {
|
|
multiple: asLowerString && asLowerString.indexOf('multiple') > -1,
|
|
cell: asLowerString && asLowerString.indexOf('cell') > -1
|
|
};
|
|
};
|
|
function collision(element, position) {
|
|
if (!element.is(':visible')) {
|
|
return false;
|
|
}
|
|
var elementPosition = kendo.getOffset(element), right = position.left + position.width, bottom = position.top + position.height;
|
|
elementPosition.right = elementPosition.left + element.outerWidth();
|
|
elementPosition.bottom = elementPosition.top + element.outerHeight();
|
|
return !(elementPosition.left > right || elementPosition.right < position.left || elementPosition.top > bottom || elementPosition.bottom < position.top);
|
|
}
|
|
kendo.ui.plugin(Selectable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.button', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'button',
|
|
name: 'Button',
|
|
category: 'web',
|
|
description: 'The Button widget displays styled buttons.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, keys = kendo.keys, CLICK = 'click', KBUTTON = 'k-button', KBUTTONICON = 'k-button-icon', KBUTTONICONTEXT = 'k-button-icontext', NS = '.kendoButton', DISABLED = 'disabled', DISABLEDSTATE = 'k-state-disabled', FOCUSEDSTATE = 'k-state-focused', SELECTEDSTATE = 'k-state-selected';
|
|
var Button = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.wrapper = that.element;
|
|
options = that.options;
|
|
element.addClass(KBUTTON).attr('role', 'button');
|
|
options.enable = options.enable && !element.attr(DISABLED);
|
|
that.enable(options.enable);
|
|
that._tabindex();
|
|
that._graphics();
|
|
element.on(CLICK + NS, proxy(that._click, that)).on('focus' + NS, proxy(that._focus, that)).on('blur' + NS, proxy(that._blur, that)).on('keydown' + NS, proxy(that._keydown, that)).on('keyup' + NS, proxy(that._keyup, that));
|
|
kendo.notify(that);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.wrapper.off(NS);
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
events: [CLICK],
|
|
options: {
|
|
name: 'Button',
|
|
icon: '',
|
|
spriteCssClass: '',
|
|
imageUrl: '',
|
|
enable: true
|
|
},
|
|
_isNativeButton: function () {
|
|
return this.element.prop('tagName').toLowerCase() == 'button';
|
|
},
|
|
_click: function (e) {
|
|
if (this.options.enable) {
|
|
if (this.trigger(CLICK, { event: e })) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
_focus: function () {
|
|
if (this.options.enable) {
|
|
this.element.addClass(FOCUSEDSTATE);
|
|
}
|
|
},
|
|
_blur: function () {
|
|
this.element.removeClass(FOCUSEDSTATE);
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
if (!that._isNativeButton()) {
|
|
if (e.keyCode == keys.ENTER || e.keyCode == keys.SPACEBAR) {
|
|
if (e.keyCode == keys.SPACEBAR) {
|
|
e.preventDefault();
|
|
if (that.options.enable) {
|
|
that.element.addClass(SELECTEDSTATE);
|
|
}
|
|
}
|
|
that._click(e);
|
|
}
|
|
}
|
|
},
|
|
_keyup: function () {
|
|
this.element.removeClass(SELECTEDSTATE);
|
|
},
|
|
_graphics: function () {
|
|
var that = this, element = that.element, options = that.options, icon = options.icon, spriteCssClass = options.spriteCssClass, imageUrl = options.imageUrl, span, img, isEmpty;
|
|
if (spriteCssClass || imageUrl || icon) {
|
|
isEmpty = true;
|
|
element.contents().not('span.k-sprite').not('span.k-icon').not('img.k-image').each(function (idx, el) {
|
|
if (el.nodeType == 1 || el.nodeType == 3 && $.trim(el.nodeValue).length > 0) {
|
|
isEmpty = false;
|
|
}
|
|
});
|
|
if (isEmpty) {
|
|
element.addClass(KBUTTONICON);
|
|
} else {
|
|
element.addClass(KBUTTONICONTEXT);
|
|
}
|
|
}
|
|
if (icon) {
|
|
span = element.children('span.k-icon').first();
|
|
if (!span[0]) {
|
|
span = $('<span class="k-icon"></span>').prependTo(element);
|
|
}
|
|
span.addClass('k-i-' + icon);
|
|
} else if (spriteCssClass) {
|
|
span = element.children('span.k-sprite').first();
|
|
if (!span[0]) {
|
|
span = $('<span class="k-sprite"></span>').prependTo(element);
|
|
}
|
|
span.addClass(spriteCssClass);
|
|
} else if (imageUrl) {
|
|
img = element.children('img.k-image').first();
|
|
if (!img[0]) {
|
|
img = $('<img alt="icon" class="k-image" />').prependTo(element);
|
|
}
|
|
img.attr('src', imageUrl);
|
|
}
|
|
},
|
|
enable: function (enable) {
|
|
var that = this, element = that.element;
|
|
if (enable === undefined) {
|
|
enable = true;
|
|
}
|
|
enable = !!enable;
|
|
that.options.enable = enable;
|
|
element.toggleClass(DISABLEDSTATE, !enable).attr('aria-disabled', !enable).attr(DISABLED, !enable);
|
|
try {
|
|
element.blur();
|
|
} catch (err) {
|
|
}
|
|
}
|
|
});
|
|
kendo.ui.plugin(Button);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.pager', ['kendo.data'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'pager',
|
|
name: 'Pager',
|
|
category: 'framework',
|
|
depends: ['data'],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, proxy = $.proxy, FIRST = '.k-i-seek-w', LAST = '.k-i-seek-e', PREV = '.k-i-arrow-w', NEXT = '.k-i-arrow-e', CHANGE = 'change', NS = '.kendoPager', CLICK = 'click', KEYDOWN = 'keydown', DISABLED = 'disabled', iconTemplate = kendo.template('<a href="\\#" title="#=text#" class="k-link k-pager-nav #= wrapClassName #"><span class="k-icon #= className #">#=text#</span></a>');
|
|
function button(template, idx, text, numeric, title) {
|
|
return template({
|
|
idx: idx,
|
|
text: text,
|
|
ns: kendo.ns,
|
|
numeric: numeric,
|
|
title: title || ''
|
|
});
|
|
}
|
|
function icon(className, text, wrapClassName) {
|
|
return iconTemplate({
|
|
className: className.substring(1),
|
|
text: text,
|
|
wrapClassName: wrapClassName || ''
|
|
});
|
|
}
|
|
function update(element, selector, page, disabled) {
|
|
element.find(selector).parent().attr(kendo.attr('page'), page).attr('tabindex', -1).toggleClass('k-state-disabled', disabled);
|
|
}
|
|
function first(element, page) {
|
|
update(element, FIRST, 1, page <= 1);
|
|
}
|
|
function prev(element, page) {
|
|
update(element, PREV, Math.max(1, page - 1), page <= 1);
|
|
}
|
|
function next(element, page, totalPages) {
|
|
update(element, NEXT, Math.min(totalPages, page + 1), page >= totalPages);
|
|
}
|
|
function last(element, page, totalPages) {
|
|
update(element, LAST, totalPages, page >= totalPages);
|
|
}
|
|
var Pager = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, page, totalPages;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
that.dataSource = kendo.data.DataSource.create(options.dataSource);
|
|
that.linkTemplate = kendo.template(that.options.linkTemplate);
|
|
that.selectTemplate = kendo.template(that.options.selectTemplate);
|
|
that.currentPageTemplate = kendo.template(that.options.currentPageTemplate);
|
|
page = that.page();
|
|
totalPages = that.totalPages();
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
if (options.previousNext) {
|
|
if (!that.element.find(FIRST).length) {
|
|
that.element.append(icon(FIRST, options.messages.first, 'k-pager-first'));
|
|
first(that.element, page, totalPages);
|
|
}
|
|
if (!that.element.find(PREV).length) {
|
|
that.element.append(icon(PREV, options.messages.previous));
|
|
prev(that.element, page, totalPages);
|
|
}
|
|
}
|
|
if (options.numeric) {
|
|
that.list = that.element.find('.k-pager-numbers');
|
|
if (!that.list.length) {
|
|
that.list = $('<ul class="k-pager-numbers k-reset" />').appendTo(that.element);
|
|
}
|
|
}
|
|
if (options.input) {
|
|
if (!that.element.find('.k-pager-input').length) {
|
|
that.element.append('<span class="k-pager-input k-label">' + options.messages.page + '<input class="k-textbox">' + kendo.format(options.messages.of, totalPages) + '</span>');
|
|
}
|
|
that.element.on(KEYDOWN + NS, '.k-pager-input input', proxy(that._keydown, that));
|
|
}
|
|
if (options.previousNext) {
|
|
if (!that.element.find(NEXT).length) {
|
|
that.element.append(icon(NEXT, options.messages.next));
|
|
next(that.element, page, totalPages);
|
|
}
|
|
if (!that.element.find(LAST).length) {
|
|
that.element.append(icon(LAST, options.messages.last, 'k-pager-last'));
|
|
last(that.element, page, totalPages);
|
|
}
|
|
}
|
|
if (options.pageSizes) {
|
|
if (!that.element.find('.k-pager-sizes').length) {
|
|
var pageSizes = options.pageSizes.length ? options.pageSizes : [
|
|
'all',
|
|
5,
|
|
10,
|
|
20
|
|
];
|
|
var pageItems = $.map(pageSizes, function (size) {
|
|
if (size.toLowerCase && size.toLowerCase() === 'all') {
|
|
return '<option value=\'all\'>' + options.messages.allPages + '</option>';
|
|
}
|
|
return '<option>' + size + '</option>';
|
|
});
|
|
$('<span class="k-pager-sizes k-label"><select/>' + options.messages.itemsPerPage + '</span>').appendTo(that.element).find('select').html(pageItems.join('')).end().appendTo(that.element);
|
|
}
|
|
that.element.find('.k-pager-sizes select').val(that.pageSize());
|
|
if (kendo.ui.DropDownList) {
|
|
that.element.find('.k-pager-sizes select').show().kendoDropDownList();
|
|
}
|
|
that.element.on(CHANGE + NS, '.k-pager-sizes select', proxy(that._change, that));
|
|
}
|
|
if (options.refresh) {
|
|
if (!that.element.find('.k-pager-refresh').length) {
|
|
that.element.append('<a href="#" class="k-pager-refresh k-link" title="' + options.messages.refresh + '"><span class="k-icon k-i-refresh">' + options.messages.refresh + '</span></a>');
|
|
}
|
|
that.element.on(CLICK + NS, '.k-pager-refresh', proxy(that._refreshClick, that));
|
|
}
|
|
if (options.info) {
|
|
if (!that.element.find('.k-pager-info').length) {
|
|
that.element.append('<span class="k-pager-info k-label" />');
|
|
}
|
|
}
|
|
that.element.on(CLICK + NS, 'a', proxy(that._click, that)).addClass('k-pager-wrap k-widget k-floatwrap');
|
|
that.element.on(CLICK + NS, '.k-current-page', proxy(that._toggleActive, that));
|
|
if (options.autoBind) {
|
|
that.refresh();
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.element.off(NS);
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that._refreshHandler = null;
|
|
kendo.destroy(that.element);
|
|
that.element = that.list = null;
|
|
},
|
|
events: [CHANGE],
|
|
options: {
|
|
name: 'Pager',
|
|
selectTemplate: '<li><span class="k-state-selected">#=text#</span></li>',
|
|
currentPageTemplate: '<li class="k-current-page"><span class="k-link k-pager-nav">#=text#</span></li>',
|
|
linkTemplate: '<li><a tabindex="-1" href="\\#" class="k-link" data-#=ns#page="#=idx#" #if (title !== "") {# title="#=title#" #}#>#=text#</a></li>',
|
|
buttonCount: 10,
|
|
autoBind: true,
|
|
numeric: true,
|
|
info: true,
|
|
input: false,
|
|
previousNext: true,
|
|
pageSizes: false,
|
|
refresh: false,
|
|
messages: {
|
|
allPages: 'All',
|
|
display: '{0} - {1} of {2} items',
|
|
empty: 'No items to display',
|
|
page: 'Page',
|
|
of: 'of {0}',
|
|
itemsPerPage: 'items per page',
|
|
first: 'Go to the first page',
|
|
previous: 'Go to the previous page',
|
|
next: 'Go to the next page',
|
|
last: 'Go to the last page',
|
|
refresh: 'Refresh',
|
|
morePages: 'More pages'
|
|
}
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that.dataSource = that.options.dataSource = dataSource;
|
|
dataSource.bind(CHANGE, that._refreshHandler);
|
|
if (that.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, idx, end, start = 1, reminder, page = that.page(), html = '', options = that.options, pageSize = that.pageSize(), total = that.dataSource.total(), totalPages = that.totalPages(), linkTemplate = that.linkTemplate, buttonCount = options.buttonCount;
|
|
if (e && e.action == 'itemchange') {
|
|
return;
|
|
}
|
|
if (options.numeric) {
|
|
if (page > buttonCount) {
|
|
reminder = page % buttonCount;
|
|
start = reminder === 0 ? page - buttonCount + 1 : page - reminder + 1;
|
|
}
|
|
end = Math.min(start + buttonCount - 1, totalPages);
|
|
if (start > 1) {
|
|
html += button(linkTemplate, start - 1, '...', false, options.messages.morePages);
|
|
}
|
|
for (idx = start; idx <= end; idx++) {
|
|
html += button(idx == page ? that.selectTemplate : linkTemplate, idx, idx, true);
|
|
}
|
|
if (end < totalPages) {
|
|
html += button(linkTemplate, idx, '...', false, options.messages.morePages);
|
|
}
|
|
if (html === '') {
|
|
html = that.selectTemplate({ text: 0 });
|
|
}
|
|
html = this.currentPageTemplate({ text: page }) + html;
|
|
that.list.removeClass('k-state-expanded').html(html);
|
|
}
|
|
if (options.info) {
|
|
if (total > 0) {
|
|
html = kendo.format(options.messages.display, (page - 1) * pageSize + 1, Math.min(page * pageSize, total), total);
|
|
} else {
|
|
html = options.messages.empty;
|
|
}
|
|
that.element.find('.k-pager-info').html(html);
|
|
}
|
|
if (options.input) {
|
|
that.element.find('.k-pager-input').html(that.options.messages.page + '<input class="k-textbox">' + kendo.format(options.messages.of, totalPages)).find('input').val(page).attr(DISABLED, total < 1).toggleClass('k-state-disabled', total < 1);
|
|
}
|
|
if (options.previousNext) {
|
|
first(that.element, page, totalPages);
|
|
prev(that.element, page, totalPages);
|
|
next(that.element, page, totalPages);
|
|
last(that.element, page, totalPages);
|
|
}
|
|
if (options.pageSizes) {
|
|
var hasAll = that.element.find('.k-pager-sizes option[value=\'all\']').length > 0;
|
|
var selectAll = hasAll && pageSize === this.dataSource.total();
|
|
var text = pageSize;
|
|
if (selectAll) {
|
|
pageSize = 'all';
|
|
text = options.messages.allPages;
|
|
}
|
|
that.element.find('.k-pager-sizes select').val(pageSize).filter('[' + kendo.attr('role') + '=dropdownlist]').kendoDropDownList('value', pageSize).kendoDropDownList('text', text);
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
if (e.keyCode === kendo.keys.ENTER) {
|
|
var input = this.element.find('.k-pager-input').find('input'), page = parseInt(input.val(), 10);
|
|
if (isNaN(page) || page < 1 || page > this.totalPages()) {
|
|
page = this.page();
|
|
}
|
|
input.val(page);
|
|
this.page(page);
|
|
}
|
|
},
|
|
_refreshClick: function (e) {
|
|
e.preventDefault();
|
|
this.dataSource.read();
|
|
},
|
|
_change: function (e) {
|
|
var value = e.currentTarget.value;
|
|
var pageSize = parseInt(value, 10);
|
|
var dataSource = this.dataSource;
|
|
if (!isNaN(pageSize)) {
|
|
dataSource.pageSize(pageSize);
|
|
} else if ((value + '').toLowerCase() == 'all') {
|
|
dataSource.pageSize(dataSource.total());
|
|
}
|
|
},
|
|
_toggleActive: function () {
|
|
this.list.toggleClass('k-state-expanded');
|
|
},
|
|
_click: function (e) {
|
|
var target = $(e.currentTarget);
|
|
e.preventDefault();
|
|
if (!target.is('.k-state-disabled')) {
|
|
this.page(target.attr(kendo.attr('page')));
|
|
}
|
|
},
|
|
totalPages: function () {
|
|
return Math.ceil((this.dataSource.total() || 0) / (this.pageSize() || 1));
|
|
},
|
|
pageSize: function () {
|
|
return this.dataSource.pageSize() || this.dataSource.total();
|
|
},
|
|
page: function (page) {
|
|
if (page !== undefined) {
|
|
this.dataSource.page(page);
|
|
this.trigger(CHANGE, { index: page });
|
|
} else {
|
|
if (this.dataSource.total() > 0) {
|
|
return this.dataSource.page();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Pager);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.popup', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'popup',
|
|
name: 'Pop-up',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, support = kendo.support, getOffset = kendo.getOffset, OPEN = 'open', CLOSE = 'close', DEACTIVATE = 'deactivate', ACTIVATE = 'activate', CENTER = 'center', LEFT = 'left', RIGHT = 'right', TOP = 'top', BOTTOM = 'bottom', ABSOLUTE = 'absolute', HIDDEN = 'hidden', BODY = 'body', LOCATION = 'location', POSITION = 'position', VISIBLE = 'visible', EFFECTS = 'effects', ACTIVE = 'k-state-active', ACTIVEBORDER = 'k-state-border', ACTIVEBORDERREGEXP = /k-state-border-(\w+)/, ACTIVECHILDREN = '.k-picker-wrap, .k-dropdown-wrap, .k-link', MOUSEDOWN = 'down', DOCUMENT_ELEMENT = $(document.documentElement), WINDOW = $(window), SCROLL = 'scroll', cssPrefix = support.transitions.css, TRANSFORM = cssPrefix + 'transform', extend = $.extend, NS = '.kendoPopup', styles = [
|
|
'font-size',
|
|
'font-family',
|
|
'font-stretch',
|
|
'font-style',
|
|
'font-weight',
|
|
'line-height'
|
|
];
|
|
function contains(container, target) {
|
|
return container === target || $.contains(container, target);
|
|
}
|
|
var Popup = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, parentPopup;
|
|
options = options || {};
|
|
if (options.isRtl) {
|
|
options.origin = options.origin || BOTTOM + ' ' + RIGHT;
|
|
options.position = options.position || TOP + ' ' + RIGHT;
|
|
}
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
that.collisions = options.collision ? options.collision.split(' ') : [];
|
|
that.downEvent = kendo.applyEventMap(MOUSEDOWN, kendo.guid());
|
|
if (that.collisions.length === 1) {
|
|
that.collisions.push(that.collisions[0]);
|
|
}
|
|
parentPopup = $(that.options.anchor).closest('.k-popup,.k-group').filter(':not([class^=km-])');
|
|
options.appendTo = $($(options.appendTo)[0] || parentPopup[0] || BODY);
|
|
that.element.hide().addClass('k-popup k-group k-reset').toggleClass('k-rtl', !!options.isRtl).css({ position: ABSOLUTE }).appendTo(options.appendTo).on('mouseenter' + NS, function () {
|
|
that._hovered = true;
|
|
}).on('mouseleave' + NS, function () {
|
|
that._hovered = false;
|
|
});
|
|
that.wrapper = $();
|
|
if (options.animation === false) {
|
|
options.animation = {
|
|
open: { effects: {} },
|
|
close: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
}
|
|
extend(options.animation.open, {
|
|
complete: function () {
|
|
that.wrapper.css({ overflow: VISIBLE });
|
|
that._activated = true;
|
|
that._trigger(ACTIVATE);
|
|
}
|
|
});
|
|
extend(options.animation.close, {
|
|
complete: function () {
|
|
that._animationClose();
|
|
}
|
|
});
|
|
that._mousedownProxy = function (e) {
|
|
that._mousedown(e);
|
|
};
|
|
if (support.mobileOS.android) {
|
|
that._resizeProxy = function (e) {
|
|
setTimeout(function () {
|
|
that._resize(e);
|
|
}, 600);
|
|
};
|
|
} else {
|
|
that._resizeProxy = function (e) {
|
|
that._resize(e);
|
|
};
|
|
}
|
|
if (options.toggleTarget) {
|
|
$(options.toggleTarget).on(options.toggleEvent + NS, $.proxy(that.toggle, that));
|
|
}
|
|
},
|
|
events: [
|
|
OPEN,
|
|
ACTIVATE,
|
|
CLOSE,
|
|
DEACTIVATE
|
|
],
|
|
options: {
|
|
name: 'Popup',
|
|
toggleEvent: 'click',
|
|
origin: BOTTOM + ' ' + LEFT,
|
|
position: TOP + ' ' + LEFT,
|
|
anchor: BODY,
|
|
appendTo: null,
|
|
collision: 'flip fit',
|
|
viewport: window,
|
|
copyAnchorStyles: true,
|
|
autosize: false,
|
|
modal: false,
|
|
adjustSize: {
|
|
width: 0,
|
|
height: 0
|
|
},
|
|
animation: {
|
|
open: {
|
|
effects: 'slideIn:down',
|
|
transition: true,
|
|
duration: 200
|
|
},
|
|
close: {
|
|
duration: 100,
|
|
hide: true
|
|
}
|
|
}
|
|
},
|
|
_animationClose: function () {
|
|
var that = this;
|
|
var location = that.wrapper.data(LOCATION);
|
|
that.wrapper.hide();
|
|
if (location) {
|
|
that.wrapper.css(location);
|
|
}
|
|
if (that.options.anchor != BODY) {
|
|
that._hideDirClass();
|
|
}
|
|
that._closing = false;
|
|
that._trigger(DEACTIVATE);
|
|
},
|
|
destroy: function () {
|
|
var that = this, options = that.options, element = that.element.off(NS), parent;
|
|
Widget.fn.destroy.call(that);
|
|
if (options.toggleTarget) {
|
|
$(options.toggleTarget).off(NS);
|
|
}
|
|
if (!options.modal) {
|
|
DOCUMENT_ELEMENT.unbind(that.downEvent, that._mousedownProxy);
|
|
that._toggleResize(false);
|
|
}
|
|
kendo.destroy(that.element.children());
|
|
element.removeData();
|
|
if (options.appendTo[0] === document.body) {
|
|
parent = element.parent('.k-animation-container');
|
|
if (parent[0]) {
|
|
parent.remove();
|
|
} else {
|
|
element.remove();
|
|
}
|
|
}
|
|
},
|
|
open: function (x, y) {
|
|
var that = this, fixed = {
|
|
isFixed: !isNaN(parseInt(y, 10)),
|
|
x: x,
|
|
y: y
|
|
}, element = that.element, options = that.options, animation, wrapper, anchor = $(options.anchor), mobile = element[0] && element.hasClass('km-widget');
|
|
if (!that.visible()) {
|
|
if (options.copyAnchorStyles) {
|
|
if (mobile && styles[0] == 'font-size') {
|
|
styles.shift();
|
|
}
|
|
element.css(kendo.getComputedStyles(anchor[0], styles));
|
|
}
|
|
if (element.data('animating') || that._trigger(OPEN)) {
|
|
return;
|
|
}
|
|
that._activated = false;
|
|
if (!options.modal) {
|
|
DOCUMENT_ELEMENT.unbind(that.downEvent, that._mousedownProxy).bind(that.downEvent, that._mousedownProxy);
|
|
that._toggleResize(false);
|
|
that._toggleResize(true);
|
|
}
|
|
that.wrapper = wrapper = kendo.wrap(element, options.autosize).css({
|
|
overflow: HIDDEN,
|
|
display: 'block',
|
|
position: ABSOLUTE
|
|
});
|
|
if (support.mobileOS.android) {
|
|
wrapper.css(TRANSFORM, 'translatez(0)');
|
|
}
|
|
wrapper.css(POSITION);
|
|
if ($(options.appendTo)[0] == document.body) {
|
|
wrapper.css(TOP, '-10000px');
|
|
}
|
|
that.flipped = that._position(fixed);
|
|
animation = that._openAnimation();
|
|
if (options.anchor != BODY) {
|
|
that._showDirClass(animation);
|
|
}
|
|
element.data(EFFECTS, animation.effects).kendoStop(true).kendoAnimate(animation);
|
|
}
|
|
},
|
|
_openAnimation: function () {
|
|
var animation = extend(true, {}, this.options.animation.open);
|
|
animation.effects = kendo.parseEffects(animation.effects, this.flipped);
|
|
return animation;
|
|
},
|
|
_hideDirClass: function () {
|
|
var anchor = $(this.options.anchor);
|
|
var direction = ((anchor.attr('class') || '').match(ACTIVEBORDERREGEXP) || [
|
|
'',
|
|
'down'
|
|
])[1];
|
|
var dirClass = ACTIVEBORDER + '-' + direction;
|
|
anchor.removeClass(dirClass).children(ACTIVECHILDREN).removeClass(ACTIVE).removeClass(dirClass);
|
|
this.element.removeClass(ACTIVEBORDER + '-' + kendo.directions[direction].reverse);
|
|
},
|
|
_showDirClass: function (animation) {
|
|
var direction = animation.effects.slideIn ? animation.effects.slideIn.direction : 'down';
|
|
var dirClass = ACTIVEBORDER + '-' + direction;
|
|
$(this.options.anchor).addClass(dirClass).children(ACTIVECHILDREN).addClass(ACTIVE).addClass(dirClass);
|
|
this.element.addClass(ACTIVEBORDER + '-' + kendo.directions[direction].reverse);
|
|
},
|
|
position: function () {
|
|
if (this.visible()) {
|
|
this.flipped = this._position();
|
|
}
|
|
},
|
|
toggle: function () {
|
|
var that = this;
|
|
that[that.visible() ? CLOSE : OPEN]();
|
|
},
|
|
visible: function () {
|
|
return this.element.is(':' + VISIBLE);
|
|
},
|
|
close: function (skipEffects) {
|
|
var that = this, options = that.options, wrap, animation, openEffects, closeEffects;
|
|
if (that.visible()) {
|
|
wrap = that.wrapper[0] ? that.wrapper : kendo.wrap(that.element).hide();
|
|
that._toggleResize(false);
|
|
if (that._closing || that._trigger(CLOSE)) {
|
|
that._toggleResize(true);
|
|
return;
|
|
}
|
|
that.element.find('.k-popup').each(function () {
|
|
var that = $(this), popup = that.data('kendoPopup');
|
|
if (popup) {
|
|
popup.close(skipEffects);
|
|
}
|
|
});
|
|
DOCUMENT_ELEMENT.unbind(that.downEvent, that._mousedownProxy);
|
|
if (skipEffects) {
|
|
animation = {
|
|
hide: true,
|
|
effects: {}
|
|
};
|
|
} else {
|
|
animation = extend(true, {}, options.animation.close);
|
|
openEffects = that.element.data(EFFECTS);
|
|
closeEffects = animation.effects;
|
|
if (!closeEffects && !kendo.size(closeEffects) && openEffects && kendo.size(openEffects)) {
|
|
animation.effects = openEffects;
|
|
animation.reverse = true;
|
|
}
|
|
that._closing = true;
|
|
}
|
|
that.element.kendoStop(true);
|
|
wrap.css({ overflow: HIDDEN });
|
|
that.element.kendoAnimate(animation);
|
|
}
|
|
},
|
|
_trigger: function (ev) {
|
|
return this.trigger(ev, { type: ev });
|
|
},
|
|
_resize: function (e) {
|
|
var that = this;
|
|
if (support.resize.indexOf(e.type) !== -1) {
|
|
clearTimeout(that._resizeTimeout);
|
|
that._resizeTimeout = setTimeout(function () {
|
|
that._position();
|
|
that._resizeTimeout = null;
|
|
}, 50);
|
|
} else {
|
|
if (!that._hovered || that._activated && that.element.hasClass('k-list-container')) {
|
|
that.close();
|
|
}
|
|
}
|
|
},
|
|
_toggleResize: function (toggle) {
|
|
var method = toggle ? 'on' : 'off';
|
|
var eventNames = support.resize;
|
|
if (!(support.mobileOS.ios || support.mobileOS.android)) {
|
|
eventNames += ' ' + SCROLL;
|
|
}
|
|
this._scrollableParents()[method](SCROLL, this._resizeProxy);
|
|
WINDOW[method](eventNames, this._resizeProxy);
|
|
},
|
|
_mousedown: function (e) {
|
|
var that = this, container = that.element[0], options = that.options, anchor = $(options.anchor)[0], toggleTarget = options.toggleTarget, target = kendo.eventTarget(e), popup = $(target).closest('.k-popup'), mobile = popup.parent().parent('.km-shim').length;
|
|
popup = popup[0];
|
|
if (!mobile && popup && popup !== that.element[0]) {
|
|
return;
|
|
}
|
|
if ($(e.target).closest('a').data('rel') === 'popover') {
|
|
return;
|
|
}
|
|
if (!contains(container, target) && !contains(anchor, target) && !(toggleTarget && contains($(toggleTarget)[0], target))) {
|
|
that.close();
|
|
}
|
|
},
|
|
_fit: function (position, size, viewPortSize) {
|
|
var output = 0;
|
|
if (position + size > viewPortSize) {
|
|
output = viewPortSize - (position + size);
|
|
}
|
|
if (position < 0) {
|
|
output = -position;
|
|
}
|
|
return output;
|
|
},
|
|
_flip: function (offset, size, anchorSize, viewPortSize, origin, position, boxSize) {
|
|
var output = 0;
|
|
boxSize = boxSize || size;
|
|
if (position !== origin && position !== CENTER && origin !== CENTER) {
|
|
if (offset + boxSize > viewPortSize) {
|
|
output += -(anchorSize + size);
|
|
}
|
|
if (offset + output < 0) {
|
|
output += anchorSize + size;
|
|
}
|
|
}
|
|
return output;
|
|
},
|
|
_scrollableParents: function () {
|
|
return $(this.options.anchor).parentsUntil('body').filter(function (index, element) {
|
|
return kendo.isScrollable(element);
|
|
});
|
|
},
|
|
_position: function (fixed) {
|
|
var that = this, element = that.element, wrapper = that.wrapper, options = that.options, viewport = $(options.viewport), viewportOffset = viewport.offset(), anchor = $(options.anchor), origins = options.origin.toLowerCase().split(' '), positions = options.position.toLowerCase().split(' '), collisions = that.collisions, zoomLevel = support.zoomLevel(), siblingContainer, parents, parentZIndex, zIndex = 10002, isWindow = !!(viewport[0] == window && window.innerWidth && zoomLevel <= 1.02), idx = 0, docEl = document.documentElement, length, viewportWidth, viewportHeight;
|
|
viewportWidth = isWindow ? window.innerWidth : viewport.width();
|
|
viewportHeight = isWindow ? window.innerHeight : viewport.height();
|
|
if (isWindow && docEl.scrollHeight - docEl.clientHeight > 0) {
|
|
viewportWidth -= kendo.support.scrollbar();
|
|
}
|
|
siblingContainer = anchor.parents().filter(wrapper.siblings());
|
|
if (siblingContainer[0]) {
|
|
parentZIndex = Math.max(Number(siblingContainer.css('zIndex')), 0);
|
|
if (parentZIndex) {
|
|
zIndex = parentZIndex + 10;
|
|
} else {
|
|
parents = anchor.parentsUntil(siblingContainer);
|
|
for (length = parents.length; idx < length; idx++) {
|
|
parentZIndex = Number($(parents[idx]).css('zIndex'));
|
|
if (parentZIndex && zIndex < parentZIndex) {
|
|
zIndex = parentZIndex + 10;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
wrapper.css('zIndex', zIndex);
|
|
if (fixed && fixed.isFixed) {
|
|
wrapper.css({
|
|
left: fixed.x,
|
|
top: fixed.y
|
|
});
|
|
} else {
|
|
wrapper.css(that._align(origins, positions));
|
|
}
|
|
var pos = getOffset(wrapper, POSITION, anchor[0] === wrapper.offsetParent()[0]), offset = getOffset(wrapper), anchorParent = anchor.offsetParent().parent('.k-animation-container,.k-popup,.k-group');
|
|
if (anchorParent.length) {
|
|
pos = getOffset(wrapper, POSITION, true);
|
|
offset = getOffset(wrapper);
|
|
}
|
|
if (viewport[0] === window) {
|
|
offset.top -= window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
offset.left -= window.pageXOffset || document.documentElement.scrollLeft || 0;
|
|
} else {
|
|
offset.top -= viewportOffset.top;
|
|
offset.left -= viewportOffset.left;
|
|
}
|
|
if (!that.wrapper.data(LOCATION)) {
|
|
wrapper.data(LOCATION, extend({}, pos));
|
|
}
|
|
var offsets = extend({}, offset), location = extend({}, pos), adjustSize = options.adjustSize;
|
|
if (collisions[0] === 'fit') {
|
|
location.top += that._fit(offsets.top, wrapper.outerHeight() + adjustSize.height, viewportHeight / zoomLevel);
|
|
}
|
|
if (collisions[1] === 'fit') {
|
|
location.left += that._fit(offsets.left, wrapper.outerWidth() + adjustSize.width, viewportWidth / zoomLevel);
|
|
}
|
|
var flipPos = extend({}, location);
|
|
var elementHeight = element.outerHeight();
|
|
var wrapperHeight = wrapper.outerHeight();
|
|
if (!wrapper.height() && elementHeight) {
|
|
wrapperHeight = wrapperHeight + elementHeight;
|
|
}
|
|
if (collisions[0] === 'flip') {
|
|
location.top += that._flip(offsets.top, elementHeight, anchor.outerHeight(), viewportHeight / zoomLevel, origins[0], positions[0], wrapperHeight);
|
|
}
|
|
if (collisions[1] === 'flip') {
|
|
location.left += that._flip(offsets.left, element.outerWidth(), anchor.outerWidth(), viewportWidth / zoomLevel, origins[1], positions[1], wrapper.outerWidth());
|
|
}
|
|
element.css(POSITION, ABSOLUTE);
|
|
wrapper.css(location);
|
|
return location.left != flipPos.left || location.top != flipPos.top;
|
|
},
|
|
_align: function (origin, position) {
|
|
var that = this, element = that.wrapper, anchor = $(that.options.anchor), verticalOrigin = origin[0], horizontalOrigin = origin[1], verticalPosition = position[0], horizontalPosition = position[1], anchorOffset = getOffset(anchor), appendTo = $(that.options.appendTo), appendToOffset, width = element.outerWidth(), height = element.outerHeight(), anchorWidth = anchor.outerWidth(), anchorHeight = anchor.outerHeight(), top = anchorOffset.top, left = anchorOffset.left, round = Math.round;
|
|
if (appendTo[0] != document.body) {
|
|
appendToOffset = getOffset(appendTo);
|
|
top -= appendToOffset.top;
|
|
left -= appendToOffset.left;
|
|
}
|
|
if (verticalOrigin === BOTTOM) {
|
|
top += anchorHeight;
|
|
}
|
|
if (verticalOrigin === CENTER) {
|
|
top += round(anchorHeight / 2);
|
|
}
|
|
if (verticalPosition === BOTTOM) {
|
|
top -= height;
|
|
}
|
|
if (verticalPosition === CENTER) {
|
|
top -= round(height / 2);
|
|
}
|
|
if (horizontalOrigin === RIGHT) {
|
|
left += anchorWidth;
|
|
}
|
|
if (horizontalOrigin === CENTER) {
|
|
left += round(anchorWidth / 2);
|
|
}
|
|
if (horizontalPosition === RIGHT) {
|
|
left -= width;
|
|
}
|
|
if (horizontalPosition === CENTER) {
|
|
left -= round(width / 2);
|
|
}
|
|
return {
|
|
top: top,
|
|
left: left
|
|
};
|
|
}
|
|
});
|
|
ui.plugin(Popup);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.notification', [
|
|
'kendo.core',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'notification',
|
|
name: 'Notification',
|
|
category: 'web',
|
|
description: 'The Notification widget displays user alerts.',
|
|
depends: [
|
|
'core',
|
|
'popup'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, extend = $.extend, setTimeout = window.setTimeout, CLICK = 'click', SHOW = 'show', HIDE = 'hide', KNOTIFICATION = 'k-notification', KICLOSE = '.k-notification-wrap .k-i-close', INFO = 'info', SUCCESS = 'success', WARNING = 'warning', ERROR = 'error', TOP = 'top', LEFT = 'left', BOTTOM = 'bottom', RIGHT = 'right', UP = 'up', NS = '.kendoNotification', WRAPPER = '<div class="k-widget k-notification"></div>', TEMPLATE = '<div class="k-notification-wrap">' + '<span class="k-icon k-i-note">#=typeIcon#</span>' + '#=content#' + '<span class="k-icon k-i-close">Hide</span>' + '</div>', SAFE_TEMPLATE = TEMPLATE.replace('#=content#', '#:content#');
|
|
var Notification = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
if (!options.appendTo || !$(options.appendTo).is(element)) {
|
|
that.element.hide();
|
|
}
|
|
that._compileTemplates(options.templates);
|
|
that._guid = '_' + kendo.guid();
|
|
that._isRtl = kendo.support.isRtl(element);
|
|
that._compileStacking(options.stacking, options.position.top, options.position.left);
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
SHOW,
|
|
HIDE
|
|
],
|
|
options: {
|
|
name: 'Notification',
|
|
position: {
|
|
pinned: true,
|
|
top: null,
|
|
left: null,
|
|
bottom: 20,
|
|
right: 20
|
|
},
|
|
stacking: 'default',
|
|
hideOnClick: true,
|
|
button: false,
|
|
allowHideAfter: 0,
|
|
autoHideAfter: 5000,
|
|
appendTo: null,
|
|
width: null,
|
|
height: null,
|
|
templates: [],
|
|
animation: {
|
|
open: {
|
|
effects: 'fade:in',
|
|
duration: 300
|
|
},
|
|
close: {
|
|
effects: 'fade:out',
|
|
duration: 600,
|
|
hide: true
|
|
}
|
|
}
|
|
},
|
|
_compileTemplates: function (templates) {
|
|
var that = this;
|
|
var kendoTemplate = kendo.template;
|
|
that._compiled = {};
|
|
$.each(templates, function (key, value) {
|
|
that._compiled[value.type] = kendoTemplate(value.template || $('#' + value.templateId).html());
|
|
});
|
|
that._defaultCompiled = kendoTemplate(TEMPLATE);
|
|
that._safeCompiled = kendoTemplate(SAFE_TEMPLATE);
|
|
},
|
|
_getCompiled: function (type, safe) {
|
|
var defaultCompiled = safe ? this._safeCompiled : this._defaultCompiled;
|
|
return type ? this._compiled[type] || defaultCompiled : defaultCompiled;
|
|
},
|
|
_compileStacking: function (stacking, top, left) {
|
|
var that = this, paddings = {
|
|
paddingTop: 0,
|
|
paddingRight: 0,
|
|
paddingBottom: 0,
|
|
paddingLeft: 0
|
|
}, horizontalAlignment = left !== null ? LEFT : RIGHT, origin, position;
|
|
switch (stacking) {
|
|
case 'down':
|
|
origin = BOTTOM + ' ' + horizontalAlignment;
|
|
position = TOP + ' ' + horizontalAlignment;
|
|
delete paddings.paddingBottom;
|
|
break;
|
|
case RIGHT:
|
|
origin = TOP + ' ' + RIGHT;
|
|
position = TOP + ' ' + LEFT;
|
|
delete paddings.paddingRight;
|
|
break;
|
|
case LEFT:
|
|
origin = TOP + ' ' + LEFT;
|
|
position = TOP + ' ' + RIGHT;
|
|
delete paddings.paddingLeft;
|
|
break;
|
|
case UP:
|
|
origin = TOP + ' ' + horizontalAlignment;
|
|
position = BOTTOM + ' ' + horizontalAlignment;
|
|
delete paddings.paddingTop;
|
|
break;
|
|
default:
|
|
if (top !== null) {
|
|
origin = BOTTOM + ' ' + horizontalAlignment;
|
|
position = TOP + ' ' + horizontalAlignment;
|
|
delete paddings.paddingBottom;
|
|
} else {
|
|
origin = TOP + ' ' + horizontalAlignment;
|
|
position = BOTTOM + ' ' + horizontalAlignment;
|
|
delete paddings.paddingTop;
|
|
}
|
|
break;
|
|
}
|
|
that._popupOrigin = origin;
|
|
that._popupPosition = position;
|
|
that._popupPaddings = paddings;
|
|
},
|
|
_attachPopupEvents: function (options, popup) {
|
|
var that = this, allowHideAfter = options.allowHideAfter, attachDelay = !isNaN(allowHideAfter) && allowHideAfter > 0, closeIcon;
|
|
function attachClick(target) {
|
|
target.on(CLICK + NS, function () {
|
|
that._hidePopup(popup);
|
|
});
|
|
}
|
|
if (popup.options.anchor !== document.body && popup.options.origin.indexOf(RIGHT) > 0) {
|
|
popup.bind('open', function () {
|
|
var shadows = kendo.getShadows(popup.element);
|
|
setTimeout(function () {
|
|
popup.wrapper.css('left', parseFloat(popup.wrapper.css('left')) + shadows.left + shadows.right);
|
|
});
|
|
});
|
|
}
|
|
if (options.hideOnClick) {
|
|
popup.bind('activate', function () {
|
|
if (attachDelay) {
|
|
setTimeout(function () {
|
|
attachClick(popup.element);
|
|
}, allowHideAfter);
|
|
} else {
|
|
attachClick(popup.element);
|
|
}
|
|
});
|
|
} else if (options.button) {
|
|
closeIcon = popup.element.find(KICLOSE);
|
|
if (attachDelay) {
|
|
setTimeout(function () {
|
|
attachClick(closeIcon);
|
|
}, allowHideAfter);
|
|
} else {
|
|
attachClick(closeIcon);
|
|
}
|
|
}
|
|
},
|
|
_showPopup: function (wrapper, options) {
|
|
var that = this, autoHideAfter = options.autoHideAfter, x = options.position.left, y = options.position.top, popup, openPopup;
|
|
openPopup = $('.' + that._guid + ':not(.k-hiding)').last();
|
|
popup = new kendo.ui.Popup(wrapper, {
|
|
anchor: openPopup[0] ? openPopup : document.body,
|
|
origin: that._popupOrigin,
|
|
position: that._popupPosition,
|
|
animation: options.animation,
|
|
modal: true,
|
|
collision: '',
|
|
isRtl: that._isRtl,
|
|
close: function () {
|
|
that._triggerHide(this.element);
|
|
},
|
|
deactivate: function (e) {
|
|
e.sender.element.off(NS);
|
|
e.sender.element.find(KICLOSE).off(NS);
|
|
e.sender.destroy();
|
|
}
|
|
});
|
|
that._attachPopupEvents(options, popup);
|
|
if (openPopup[0]) {
|
|
popup.open();
|
|
} else {
|
|
if (x === null) {
|
|
x = $(window).width() - wrapper.width() - options.position.right;
|
|
}
|
|
if (y === null) {
|
|
y = $(window).height() - wrapper.height() - options.position.bottom;
|
|
}
|
|
popup.open(x, y);
|
|
}
|
|
popup.wrapper.addClass(that._guid).css(extend({ margin: 0 }, that._popupPaddings));
|
|
if (options.position.pinned) {
|
|
popup.wrapper.css('position', 'fixed');
|
|
if (openPopup[0]) {
|
|
that._togglePin(popup.wrapper, true);
|
|
}
|
|
} else if (!openPopup[0]) {
|
|
that._togglePin(popup.wrapper, false);
|
|
}
|
|
if (autoHideAfter > 0) {
|
|
setTimeout(function () {
|
|
that._hidePopup(popup);
|
|
}, autoHideAfter);
|
|
}
|
|
},
|
|
_hidePopup: function (popup) {
|
|
popup.wrapper.addClass('k-hiding');
|
|
popup.close();
|
|
},
|
|
_togglePin: function (wrapper, pin) {
|
|
var win = $(window), sign = pin ? -1 : 1;
|
|
wrapper.css({
|
|
top: parseInt(wrapper.css(TOP), 10) + sign * win.scrollTop(),
|
|
left: parseInt(wrapper.css(LEFT), 10) + sign * win.scrollLeft()
|
|
});
|
|
},
|
|
_attachStaticEvents: function (options, wrapper) {
|
|
var that = this, allowHideAfter = options.allowHideAfter, attachDelay = !isNaN(allowHideAfter) && allowHideAfter > 0;
|
|
function attachClick(target) {
|
|
target.on(CLICK + NS, proxy(that._hideStatic, that, wrapper));
|
|
}
|
|
if (options.hideOnClick) {
|
|
if (attachDelay) {
|
|
setTimeout(function () {
|
|
attachClick(wrapper);
|
|
}, allowHideAfter);
|
|
} else {
|
|
attachClick(wrapper);
|
|
}
|
|
} else if (options.button) {
|
|
if (attachDelay) {
|
|
setTimeout(function () {
|
|
attachClick(wrapper.find(KICLOSE));
|
|
}, allowHideAfter);
|
|
} else {
|
|
attachClick(wrapper.find(KICLOSE));
|
|
}
|
|
}
|
|
},
|
|
_showStatic: function (wrapper, options) {
|
|
var that = this, autoHideAfter = options.autoHideAfter, animation = options.animation, insertionMethod = options.stacking == UP || options.stacking == LEFT ? 'prependTo' : 'appendTo';
|
|
wrapper.addClass(that._guid)[insertionMethod](options.appendTo).hide().kendoAnimate(animation.open || false);
|
|
that._attachStaticEvents(options, wrapper);
|
|
if (autoHideAfter > 0) {
|
|
setTimeout(function () {
|
|
that._hideStatic(wrapper);
|
|
}, autoHideAfter);
|
|
}
|
|
},
|
|
_hideStatic: function (wrapper) {
|
|
wrapper.kendoAnimate(extend(this.options.animation.close || false, {
|
|
complete: function () {
|
|
wrapper.off(NS).find(KICLOSE).off(NS);
|
|
wrapper.remove();
|
|
}
|
|
}));
|
|
this._triggerHide(wrapper);
|
|
},
|
|
_triggerHide: function (element) {
|
|
this.trigger(HIDE, { element: element });
|
|
this.angular('cleanup', function () {
|
|
return { elements: element };
|
|
});
|
|
},
|
|
show: function (content, type, safe) {
|
|
var that = this, options = that.options, wrapper = $(WRAPPER), args, defaultArgs;
|
|
if (!type) {
|
|
type = INFO;
|
|
}
|
|
if (content !== null && content !== undefined && content !== '') {
|
|
if (kendo.isFunction(content)) {
|
|
content = content();
|
|
}
|
|
defaultArgs = {
|
|
typeIcon: type,
|
|
content: ''
|
|
};
|
|
if ($.isPlainObject(content)) {
|
|
args = extend(defaultArgs, content);
|
|
} else {
|
|
args = extend(defaultArgs, { content: content });
|
|
}
|
|
wrapper.addClass(KNOTIFICATION + '-' + type).toggleClass(KNOTIFICATION + '-button', options.button).attr('data-role', 'alert').css({
|
|
width: options.width,
|
|
height: options.height
|
|
}).append(that._getCompiled(type, safe)(args));
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: wrapper,
|
|
data: [{ dataItem: args }]
|
|
};
|
|
});
|
|
if ($(options.appendTo)[0]) {
|
|
that._showStatic(wrapper, options);
|
|
} else {
|
|
that._showPopup(wrapper, options);
|
|
}
|
|
that.trigger(SHOW, { element: wrapper });
|
|
}
|
|
return that;
|
|
},
|
|
showText: function (content, type) {
|
|
this.show(content, type, true);
|
|
},
|
|
info: function (content) {
|
|
return this.show(content, INFO);
|
|
},
|
|
success: function (content) {
|
|
return this.show(content, SUCCESS);
|
|
},
|
|
warning: function (content) {
|
|
return this.show(content, WARNING);
|
|
},
|
|
error: function (content) {
|
|
return this.show(content, ERROR);
|
|
},
|
|
hide: function () {
|
|
var that = this, openedNotifications = that.getNotifications();
|
|
if (that.options.appendTo) {
|
|
openedNotifications.each(function (idx, element) {
|
|
that._hideStatic($(element));
|
|
});
|
|
} else {
|
|
openedNotifications.each(function (idx, element) {
|
|
var popup = $(element).data('kendoPopup');
|
|
if (popup) {
|
|
that._hidePopup(popup);
|
|
}
|
|
});
|
|
}
|
|
return that;
|
|
},
|
|
getNotifications: function () {
|
|
var that = this, guidElements = $('.' + that._guid);
|
|
if (that.options.appendTo) {
|
|
return guidElements;
|
|
} else {
|
|
return guidElements.children('.' + KNOTIFICATION);
|
|
}
|
|
},
|
|
setOptions: function (newOptions) {
|
|
var that = this, options;
|
|
Widget.fn.setOptions.call(that, newOptions);
|
|
options = that.options;
|
|
if (newOptions.templates !== undefined) {
|
|
that._compileTemplates(options.templates);
|
|
}
|
|
if (newOptions.stacking !== undefined || newOptions.position !== undefined) {
|
|
that._compileStacking(options.stacking, options.position.top, options.position.left);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.getNotifications().off(NS).find(KICLOSE).off(NS);
|
|
}
|
|
});
|
|
kendo.ui.plugin(Notification);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.tooltip', [
|
|
'kendo.core',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'tooltip',
|
|
name: 'Tooltip',
|
|
category: 'web',
|
|
description: 'The Tooltip widget displays a popup hint for a given html element.',
|
|
depends: [
|
|
'core',
|
|
'popup'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, Popup = kendo.ui.Popup, isFunction = kendo.isFunction, isPlainObject = $.isPlainObject, extend = $.extend, proxy = $.proxy, DOCUMENT = $(document), isLocalUrl = kendo.isLocalUrl, ARIAIDSUFFIX = '_tt_active', DESCRIBEDBY = 'aria-describedby', SHOW = 'show', HIDE = 'hide', ERROR = 'error', CONTENTLOAD = 'contentLoad', REQUESTSTART = 'requestStart', KCONTENTFRAME = 'k-content-frame', TEMPLATE = '<div role="tooltip" class="k-widget k-tooltip#if (!autoHide) {# k-tooltip-closable#}#">#if (!autoHide) {# <div class="k-tooltip-button"><a href="\\#" class="k-icon k-i-close">close</a></div> #}#' + '<div class="k-tooltip-content"></div>' + '#if (callout){ #<div class="k-callout k-callout-#=dir#"></div>#}#' + '</div>', IFRAMETEMPLATE = kendo.template('<iframe frameborder=\'0\' class=\'' + KCONTENTFRAME + '\' ' + 'src=\'#= content.url #\'>' + 'This page requires frames in order to show content' + '</iframe>'), NS = '.kendoTooltip', POSITIONS = {
|
|
bottom: {
|
|
origin: 'bottom center',
|
|
position: 'top center'
|
|
},
|
|
top: {
|
|
origin: 'top center',
|
|
position: 'bottom center'
|
|
},
|
|
left: {
|
|
origin: 'center left',
|
|
position: 'center right',
|
|
collision: 'fit flip'
|
|
},
|
|
right: {
|
|
origin: 'center right',
|
|
position: 'center left',
|
|
collision: 'fit flip'
|
|
},
|
|
center: {
|
|
position: 'center center',
|
|
origin: 'center center'
|
|
}
|
|
}, REVERSE = {
|
|
'top': 'bottom',
|
|
'bottom': 'top',
|
|
'left': 'right',
|
|
'right': 'left',
|
|
'center': 'center'
|
|
}, DIRCLASSES = {
|
|
bottom: 'n',
|
|
top: 's',
|
|
left: 'e',
|
|
right: 'w',
|
|
center: 'n'
|
|
}, DIMENSIONS = {
|
|
'horizontal': {
|
|
offset: 'top',
|
|
size: 'outerHeight'
|
|
},
|
|
'vertical': {
|
|
offset: 'left',
|
|
size: 'outerWidth'
|
|
}
|
|
}, DEFAULTCONTENT = function (e) {
|
|
return e.target.data(kendo.ns + 'title');
|
|
};
|
|
function restoreTitle(element) {
|
|
while (element.length) {
|
|
restoreTitleAttributeForElement(element);
|
|
element = element.parent();
|
|
}
|
|
}
|
|
function restoreTitleAttributeForElement(element) {
|
|
var title = element.data(kendo.ns + 'title');
|
|
if (title) {
|
|
element.attr('title', title);
|
|
element.removeData(kendo.ns + 'title');
|
|
}
|
|
}
|
|
function saveTitleAttributeForElement(element) {
|
|
var title = element.attr('title');
|
|
if (title) {
|
|
element.data(kendo.ns + 'title', title);
|
|
element.attr('title', '');
|
|
}
|
|
}
|
|
function saveTitleAttributes(element) {
|
|
while (element.length && !element.is('body')) {
|
|
saveTitleAttributeForElement(element);
|
|
element = element.parent();
|
|
}
|
|
}
|
|
var Tooltip = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, axis;
|
|
Widget.fn.init.call(that, element, options);
|
|
axis = that.options.position.match(/left|right/) ? 'horizontal' : 'vertical';
|
|
that.dimensions = DIMENSIONS[axis];
|
|
that._documentKeyDownHandler = proxy(that._documentKeyDown, that);
|
|
that.element.on(that.options.showOn + NS, that.options.filter, proxy(that._showOn, that)).on('mouseenter' + NS, that.options.filter, proxy(that._mouseenter, that));
|
|
if (this.options.autoHide) {
|
|
that.element.on('mouseleave' + NS, that.options.filter, proxy(that._mouseleave, that));
|
|
}
|
|
},
|
|
options: {
|
|
name: 'Tooltip',
|
|
filter: '',
|
|
content: DEFAULTCONTENT,
|
|
showAfter: 100,
|
|
callout: true,
|
|
position: 'bottom',
|
|
showOn: 'mouseenter',
|
|
autoHide: true,
|
|
width: null,
|
|
height: null,
|
|
animation: {
|
|
open: {
|
|
effects: 'fade:in',
|
|
duration: 0
|
|
},
|
|
close: {
|
|
effects: 'fade:out',
|
|
duration: 40,
|
|
hide: true
|
|
}
|
|
}
|
|
},
|
|
events: [
|
|
SHOW,
|
|
HIDE,
|
|
CONTENTLOAD,
|
|
ERROR,
|
|
REQUESTSTART
|
|
],
|
|
_mouseenter: function (e) {
|
|
saveTitleAttributes($(e.currentTarget));
|
|
},
|
|
_showOn: function (e) {
|
|
var that = this;
|
|
var currentTarget = $(e.currentTarget);
|
|
if (that.options.showOn && that.options.showOn.match(/click|focus/)) {
|
|
that._show(currentTarget);
|
|
} else {
|
|
clearTimeout(that.timeout);
|
|
that.timeout = setTimeout(function () {
|
|
that._show(currentTarget);
|
|
}, that.options.showAfter);
|
|
}
|
|
},
|
|
_appendContent: function (target) {
|
|
var that = this, contentOptions = that.options.content, element = that.content, showIframe = that.options.iframe, iframe;
|
|
if (isPlainObject(contentOptions) && contentOptions.url) {
|
|
if (!('iframe' in that.options)) {
|
|
showIframe = !isLocalUrl(contentOptions.url);
|
|
}
|
|
that.trigger(REQUESTSTART, {
|
|
options: contentOptions,
|
|
target: target
|
|
});
|
|
if (!showIframe) {
|
|
element.empty();
|
|
kendo.ui.progress(element, true);
|
|
that._ajaxRequest(contentOptions);
|
|
} else {
|
|
element.hide();
|
|
iframe = element.find('.' + KCONTENTFRAME)[0];
|
|
if (iframe) {
|
|
iframe.src = contentOptions.url || iframe.src;
|
|
} else {
|
|
element.html(IFRAMETEMPLATE({ content: contentOptions }));
|
|
}
|
|
element.find('.' + KCONTENTFRAME).off('load' + NS).on('load' + NS, function () {
|
|
that.trigger(CONTENTLOAD);
|
|
element.show();
|
|
});
|
|
}
|
|
} else if (contentOptions && isFunction(contentOptions)) {
|
|
contentOptions = contentOptions({
|
|
sender: this,
|
|
target: target
|
|
});
|
|
element.html(contentOptions || '');
|
|
} else {
|
|
element.html(contentOptions);
|
|
}
|
|
that.angular('compile', function () {
|
|
return { elements: element };
|
|
});
|
|
},
|
|
_ajaxRequest: function (options) {
|
|
var that = this;
|
|
jQuery.ajax(extend({
|
|
type: 'GET',
|
|
dataType: 'html',
|
|
cache: false,
|
|
error: function (xhr, status) {
|
|
kendo.ui.progress(that.content, false);
|
|
that.trigger(ERROR, {
|
|
status: status,
|
|
xhr: xhr
|
|
});
|
|
},
|
|
success: proxy(function (data) {
|
|
kendo.ui.progress(that.content, false);
|
|
that.content.html(data);
|
|
that.trigger(CONTENTLOAD);
|
|
}, that)
|
|
}, options));
|
|
},
|
|
_documentKeyDown: function (e) {
|
|
if (e.keyCode === kendo.keys.ESC) {
|
|
this.hide();
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var that = this, popup = that.popup;
|
|
if (popup && popup.options.anchor) {
|
|
that._appendContent(popup.options.anchor);
|
|
}
|
|
},
|
|
hide: function () {
|
|
if (this.popup) {
|
|
this.popup.close();
|
|
}
|
|
},
|
|
show: function (target) {
|
|
target = target || this.element;
|
|
saveTitleAttributes(target);
|
|
this._show(target);
|
|
},
|
|
_show: function (target) {
|
|
var that = this, current = that.target();
|
|
if (!that.popup) {
|
|
that._initPopup();
|
|
}
|
|
if (current && current[0] != target[0]) {
|
|
that.popup.close();
|
|
that.popup.element.kendoStop(true, true);
|
|
}
|
|
if (!current || current[0] != target[0]) {
|
|
that._appendContent(target);
|
|
that.popup.options.anchor = target;
|
|
}
|
|
that.popup.one('deactivate', function () {
|
|
restoreTitle(target);
|
|
target.removeAttr(DESCRIBEDBY);
|
|
this.element.removeAttr('id').attr('aria-hidden', true);
|
|
DOCUMENT.off('keydown' + NS, that._documentKeyDownHandler);
|
|
});
|
|
that.popup.open();
|
|
},
|
|
_initPopup: function () {
|
|
var that = this, options = that.options, wrapper = $(kendo.template(TEMPLATE)({
|
|
callout: options.callout && options.position !== 'center',
|
|
dir: DIRCLASSES[options.position],
|
|
autoHide: options.autoHide
|
|
}));
|
|
that.popup = new Popup(wrapper, extend({
|
|
activate: function () {
|
|
var anchor = this.options.anchor, ariaId = anchor[0].id || that.element[0].id;
|
|
if (ariaId) {
|
|
anchor.attr(DESCRIBEDBY, ariaId + ARIAIDSUFFIX);
|
|
this.element.attr('id', ariaId + ARIAIDSUFFIX);
|
|
}
|
|
if (options.callout) {
|
|
that._positionCallout();
|
|
}
|
|
this.element.removeAttr('aria-hidden');
|
|
DOCUMENT.on('keydown' + NS, that._documentKeyDownHandler);
|
|
that.trigger(SHOW);
|
|
},
|
|
close: function () {
|
|
that.trigger(HIDE);
|
|
},
|
|
copyAnchorStyles: false,
|
|
animation: options.animation
|
|
}, POSITIONS[options.position]));
|
|
wrapper.css({
|
|
width: options.width,
|
|
height: options.height
|
|
});
|
|
that.content = wrapper.find('.k-tooltip-content');
|
|
that.arrow = wrapper.find('.k-callout');
|
|
if (options.autoHide) {
|
|
wrapper.on('mouseleave' + NS, proxy(that._mouseleave, that));
|
|
} else {
|
|
wrapper.on('click' + NS, '.k-tooltip-button', proxy(that._closeButtonClick, that));
|
|
}
|
|
},
|
|
_closeButtonClick: function (e) {
|
|
e.preventDefault();
|
|
this.hide();
|
|
},
|
|
_mouseleave: function (e) {
|
|
if (this.popup) {
|
|
var element = $(e.currentTarget), offset = element.offset(), pageX = e.pageX, pageY = e.pageY;
|
|
offset.right = offset.left + element.outerWidth();
|
|
offset.bottom = offset.top + element.outerHeight();
|
|
if (pageX > offset.left && pageX < offset.right && pageY > offset.top && pageY < offset.bottom) {
|
|
return;
|
|
}
|
|
this.popup.close();
|
|
} else {
|
|
restoreTitle($(e.currentTarget));
|
|
}
|
|
clearTimeout(this.timeout);
|
|
},
|
|
_positionCallout: function () {
|
|
var that = this, position = that.options.position, dimensions = that.dimensions, offset = dimensions.offset, popup = that.popup, anchor = popup.options.anchor, anchorOffset = $(anchor).offset(), arrowBorder = parseInt(that.arrow.css('border-top-width'), 10), elementOffset = $(popup.element).offset(), cssClass = DIRCLASSES[popup.flipped ? REVERSE[position] : position], offsetAmount = anchorOffset[offset] - elementOffset[offset] + $(anchor)[dimensions.size]() / 2 - arrowBorder;
|
|
that.arrow.removeClass('k-callout-n k-callout-s k-callout-w k-callout-e').addClass('k-callout-' + cssClass).css(offset, offsetAmount);
|
|
},
|
|
target: function () {
|
|
if (this.popup) {
|
|
return this.popup.options.anchor;
|
|
}
|
|
return null;
|
|
},
|
|
destroy: function () {
|
|
var popup = this.popup;
|
|
if (popup) {
|
|
popup.element.off(NS);
|
|
popup.destroy();
|
|
}
|
|
clearTimeout(this.timeout);
|
|
this.element.off(NS);
|
|
DOCUMENT.off('keydown' + NS, this._documentKeyDownHandler);
|
|
Widget.fn.destroy.call(this);
|
|
}
|
|
});
|
|
kendo.ui.plugin(Tooltip);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.list', [
|
|
'kendo.data',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'list',
|
|
name: 'List',
|
|
category: 'framework',
|
|
depends: [
|
|
'data',
|
|
'popup'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, support = kendo.support, htmlEncode = kendo.htmlEncode, activeElement = kendo._activeElement, ObservableArray = kendo.data.ObservableArray, ID = 'id', CHANGE = 'change', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', LOADING = 'k-loading', OPEN = 'open', CLOSE = 'close', CASCADE = 'cascade', SELECT = 'select', SELECTED = 'selected', REQUESTSTART = 'requestStart', REQUESTEND = 'requestEnd', WIDTH = 'width', extend = $.extend, proxy = $.proxy, isArray = $.isArray, browser = support.browser, isIE8 = browser.msie && browser.version < 9, quotRegExp = /"/g, alternativeNames = {
|
|
'ComboBox': 'DropDownList',
|
|
'DropDownList': 'ComboBox'
|
|
};
|
|
var List = kendo.ui.DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
var that = this, ns = that.ns, id;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
that._isSelect = element.is(SELECT);
|
|
if (that._isSelect && that.element[0].length) {
|
|
if (!options.dataSource) {
|
|
options.dataTextField = options.dataTextField || 'text';
|
|
options.dataValueField = options.dataValueField || 'value';
|
|
}
|
|
}
|
|
that.ul = $('<ul unselectable="on" class="k-list k-reset"/>').attr({
|
|
tabIndex: -1,
|
|
'aria-hidden': true
|
|
});
|
|
that.list = $('<div class=\'k-list-container\'/>').append(that.ul).on('mousedown' + ns, proxy(that._listMousedown, that));
|
|
id = element.attr(ID);
|
|
if (id) {
|
|
that.list.attr(ID, id + '-list');
|
|
that.ul.attr(ID, id + '_listbox');
|
|
}
|
|
that._header();
|
|
that._accessors();
|
|
that._initValue();
|
|
},
|
|
options: {
|
|
valuePrimitive: false,
|
|
headerTemplate: ''
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
if (options && options.enable !== undefined) {
|
|
options.enabled = options.enable;
|
|
}
|
|
},
|
|
focus: function () {
|
|
this._focused.focus();
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
_listOptions: function (options) {
|
|
var that = this;
|
|
var currentOptions = that.options;
|
|
var virtual = currentOptions.virtual;
|
|
var listBoundHandler = proxy(that._listBound, that);
|
|
virtual = typeof virtual === 'object' ? virtual : {};
|
|
options = $.extend({
|
|
autoBind: false,
|
|
selectable: true,
|
|
dataSource: that.dataSource,
|
|
click: proxy(that._click, that),
|
|
change: proxy(that._listChange, that),
|
|
activate: proxy(that._activateItem, that),
|
|
deactivate: proxy(that._deactivateItem, that),
|
|
dataBinding: function () {
|
|
that.trigger('dataBinding');
|
|
that._angularItems('cleanup');
|
|
},
|
|
dataBound: listBoundHandler,
|
|
listBound: listBoundHandler,
|
|
height: currentOptions.height,
|
|
dataValueField: currentOptions.dataValueField,
|
|
dataTextField: currentOptions.dataTextField,
|
|
groupTemplate: currentOptions.groupTemplate,
|
|
fixedGroupTemplate: currentOptions.fixedGroupTemplate,
|
|
template: currentOptions.template
|
|
}, options, virtual);
|
|
if (!options.template) {
|
|
options.template = '#:' + kendo.expr(options.dataTextField, 'data') + '#';
|
|
}
|
|
return options;
|
|
},
|
|
_initList: function () {
|
|
var that = this;
|
|
var listOptions = that._listOptions({ selectedItemChange: proxy(that._listChange, that) });
|
|
if (!that.options.virtual) {
|
|
that.listView = new kendo.ui.StaticList(that.ul, listOptions);
|
|
} else {
|
|
that.listView = new kendo.ui.VirtualList(that.ul, listOptions);
|
|
}
|
|
that._setListValue();
|
|
},
|
|
_setListValue: function (value) {
|
|
value = value || this.options.value;
|
|
if (value !== undefined) {
|
|
this.listView.value(value).done(proxy(this._updateSelectionState, this));
|
|
}
|
|
},
|
|
_updateSelectionState: $.noop,
|
|
_listMousedown: function (e) {
|
|
if (!this.filterInput || this.filterInput[0] !== e.target) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_isFilterEnabled: function () {
|
|
var filter = this.options.filter;
|
|
return filter && filter !== 'none';
|
|
},
|
|
_clearFilter: function () {
|
|
if (!this.options.virtual) {
|
|
this.listView.bound(false);
|
|
}
|
|
this._filterSource();
|
|
},
|
|
_filterSource: function (filter, force) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var dataSource = that.dataSource;
|
|
var expression = extend({}, dataSource.filter() || {});
|
|
var removed = removeFiltersForField(expression, options.dataTextField);
|
|
if ((filter || removed) && that.trigger('filtering', { filter: filter })) {
|
|
return;
|
|
}
|
|
expression = {
|
|
filters: expression.filters || [],
|
|
logic: 'and'
|
|
};
|
|
if (filter) {
|
|
expression.filters.push(filter);
|
|
}
|
|
if (that._cascading) {
|
|
this.listView.setDSFilter(expression);
|
|
}
|
|
if (!force) {
|
|
dataSource.filter(expression);
|
|
} else {
|
|
dataSource.read({ filter: expression });
|
|
}
|
|
},
|
|
_header: function () {
|
|
var that = this;
|
|
var template = that.options.headerTemplate;
|
|
var header;
|
|
if ($.isFunction(template)) {
|
|
template = template({});
|
|
}
|
|
if (template) {
|
|
that.list.prepend(template);
|
|
header = that.ul.prev();
|
|
that.header = header[0] ? header : null;
|
|
if (that.header) {
|
|
that.angular('compile', function () {
|
|
return { elements: that.header };
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_initValue: function () {
|
|
var that = this, value = that.options.value;
|
|
if (value !== null) {
|
|
that.element.val(value);
|
|
} else {
|
|
value = that._accessor();
|
|
that.options.value = value;
|
|
}
|
|
that._old = value;
|
|
},
|
|
_ignoreCase: function () {
|
|
var that = this, model = that.dataSource.reader.model, field;
|
|
if (model && model.fields) {
|
|
field = model.fields[that.options.dataTextField];
|
|
if (field && field.type && field.type !== 'string') {
|
|
that.options.ignoreCase = false;
|
|
}
|
|
}
|
|
},
|
|
_focus: function (candidate) {
|
|
return this.listView.focus(candidate);
|
|
},
|
|
current: function (candidate) {
|
|
return this._focus(candidate);
|
|
},
|
|
items: function () {
|
|
return this.ul[0].children;
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
var ns = that.ns;
|
|
Widget.fn.destroy.call(that);
|
|
that._unbindDataSource();
|
|
that.listView.destroy();
|
|
that.list.off(ns);
|
|
that.popup.destroy();
|
|
if (that._form) {
|
|
that._form.off('reset', that._resetHandler);
|
|
}
|
|
},
|
|
dataItem: function (index) {
|
|
var that = this;
|
|
if (index === undefined) {
|
|
return that.listView.selectedDataItems()[0];
|
|
}
|
|
if (typeof index !== 'number') {
|
|
if (that.options.virtual) {
|
|
return that.dataSource.getByUid($(index).data('uid'));
|
|
}
|
|
index = $(that.items()).index(index);
|
|
}
|
|
return that.dataSource.flatView()[index];
|
|
},
|
|
_activateItem: function () {
|
|
var current = this.listView.focus();
|
|
if (current) {
|
|
this._focused.add(this.filterInput).attr('aria-activedescendant', current.attr('id'));
|
|
}
|
|
},
|
|
_deactivateItem: function () {
|
|
this._focused.add(this.filterInput).removeAttr('aria-activedescendant');
|
|
},
|
|
_accessors: function () {
|
|
var that = this;
|
|
var element = that.element;
|
|
var options = that.options;
|
|
var getter = kendo.getter;
|
|
var textField = element.attr(kendo.attr('text-field'));
|
|
var valueField = element.attr(kendo.attr('value-field'));
|
|
if (!options.dataTextField && textField) {
|
|
options.dataTextField = textField;
|
|
}
|
|
if (!options.dataValueField && valueField) {
|
|
options.dataValueField = valueField;
|
|
}
|
|
that._text = getter(options.dataTextField);
|
|
that._value = getter(options.dataValueField);
|
|
},
|
|
_aria: function (id) {
|
|
var that = this, options = that.options, element = that._focused.add(that.filterInput);
|
|
if (options.suggest !== undefined) {
|
|
element.attr('aria-autocomplete', options.suggest ? 'both' : 'list');
|
|
}
|
|
id = id ? id + ' ' + that.ul[0].id : that.ul[0].id;
|
|
element.attr('aria-owns', id);
|
|
that.ul.attr('aria-live', !that._isFilterEnabled() ? 'off' : 'polite');
|
|
},
|
|
_blur: function () {
|
|
var that = this;
|
|
that._change();
|
|
that.close();
|
|
},
|
|
_change: function () {
|
|
var that = this;
|
|
var index = that.selectedIndex;
|
|
var optionValue = that.options.value;
|
|
var value = that.value();
|
|
var trigger;
|
|
if (that._isSelect && !that.listView.bound() && optionValue) {
|
|
value = optionValue;
|
|
}
|
|
if (value !== unifyType(that._old, typeof value)) {
|
|
trigger = true;
|
|
} else if (index !== undefined && index !== that._oldIndex) {
|
|
trigger = true;
|
|
}
|
|
if (trigger) {
|
|
that._old = value;
|
|
that._oldIndex = index;
|
|
if (!that._typing) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
that.trigger(CHANGE);
|
|
}
|
|
that.typing = false;
|
|
},
|
|
_data: function () {
|
|
return this.dataSource.view();
|
|
},
|
|
_enable: function () {
|
|
var that = this, options = that.options, disabled = that.element.is('[disabled]');
|
|
if (options.enable !== undefined) {
|
|
options.enabled = options.enable;
|
|
}
|
|
if (!options.enabled || disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(that.element.is('[readonly]'));
|
|
}
|
|
},
|
|
_dataValue: function (dataItem) {
|
|
var value = this._value(dataItem);
|
|
if (value === undefined) {
|
|
value = this._text(dataItem);
|
|
}
|
|
return value;
|
|
},
|
|
_offsetHeight: function () {
|
|
var offsetHeight = 0;
|
|
var siblings = this.listView.content.prevAll(':visible');
|
|
siblings.each(function () {
|
|
var element = $(this);
|
|
if (element.hasClass('k-list-filter')) {
|
|
offsetHeight += element.children().outerHeight();
|
|
} else {
|
|
offsetHeight += element.outerHeight();
|
|
}
|
|
});
|
|
return offsetHeight;
|
|
},
|
|
_height: function (length) {
|
|
var that = this;
|
|
var list = that.list;
|
|
var height = that.options.height;
|
|
var visible = that.popup.visible();
|
|
var offsetTop;
|
|
var popups;
|
|
if (length) {
|
|
popups = list.add(list.parent('.k-animation-container')).show();
|
|
if (!list.is(':visible')) {
|
|
popups.hide();
|
|
return;
|
|
}
|
|
height = that.listView.content[0].scrollHeight > height ? height : 'auto';
|
|
popups.height(height);
|
|
if (height !== 'auto') {
|
|
offsetTop = that._offsetHeight();
|
|
if (offsetTop) {
|
|
height -= offsetTop;
|
|
}
|
|
}
|
|
that.listView.content.height(height);
|
|
if (!visible) {
|
|
popups.hide();
|
|
}
|
|
}
|
|
return height;
|
|
},
|
|
_adjustListWidth: function () {
|
|
var list = this.list, width = list[0].style.width, wrapper = this.wrapper, computedStyle, computedWidth;
|
|
if (!list.data(WIDTH) && width) {
|
|
return;
|
|
}
|
|
computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0;
|
|
computedWidth = parseFloat(computedStyle && computedStyle.width) || wrapper.outerWidth();
|
|
if (computedStyle && browser.msie) {
|
|
computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth);
|
|
}
|
|
if (list.css('box-sizing') !== 'border-box') {
|
|
width = computedWidth - (list.outerWidth() - list.width());
|
|
} else {
|
|
width = computedWidth;
|
|
}
|
|
list.css({
|
|
fontFamily: wrapper.css('font-family'),
|
|
width: width
|
|
}).data(WIDTH, width);
|
|
return true;
|
|
},
|
|
_openHandler: function (e) {
|
|
this._adjustListWidth();
|
|
if (this.trigger(OPEN)) {
|
|
e.preventDefault();
|
|
} else {
|
|
this._focused.attr('aria-expanded', true);
|
|
this.ul.attr('aria-hidden', false);
|
|
}
|
|
},
|
|
_closeHandler: function (e) {
|
|
if (this.trigger(CLOSE)) {
|
|
e.preventDefault();
|
|
} else {
|
|
this._focused.attr('aria-expanded', false);
|
|
this.ul.attr('aria-hidden', true);
|
|
}
|
|
},
|
|
_focusItem: function () {
|
|
var listView = this.listView;
|
|
var focusedItem = listView.focus();
|
|
var index = listView.select();
|
|
index = index[index.length - 1];
|
|
if (index === undefined && this.options.highlightFirst && !focusedItem) {
|
|
index = 0;
|
|
}
|
|
if (index !== undefined) {
|
|
listView.focus(index);
|
|
} else {
|
|
listView.scrollToIndex(0);
|
|
}
|
|
},
|
|
_calculateGroupPadding: function (height) {
|
|
var li = this.ul.children('.k-first:first');
|
|
var groupHeader = this.listView.content.prev('.k-group-header');
|
|
var padding = 0;
|
|
if (groupHeader[0] && groupHeader[0].style.display !== 'none') {
|
|
if (height !== 'auto') {
|
|
padding = kendo.support.scrollbar();
|
|
}
|
|
padding += parseFloat(li.css('border-right-width'), 10) + parseFloat(li.children('.k-group').css('padding-right'), 10);
|
|
groupHeader.css('padding-right', padding);
|
|
}
|
|
},
|
|
_calculatePopupHeight: function (force) {
|
|
var height = this._height(this.dataSource.flatView().length || force);
|
|
this._calculateGroupPadding(height);
|
|
},
|
|
_resizePopup: function (force) {
|
|
if (this.options.virtual) {
|
|
return;
|
|
}
|
|
if (!this.popup.element.is(':visible')) {
|
|
this.popup.one('open', function (force) {
|
|
return proxy(function () {
|
|
this._calculatePopupHeight(force);
|
|
}, this);
|
|
}.call(this, force));
|
|
} else {
|
|
this._calculatePopupHeight(force);
|
|
}
|
|
},
|
|
_popup: function () {
|
|
var that = this;
|
|
that.popup = new ui.Popup(that.list, extend({}, that.options.popup, {
|
|
anchor: that.wrapper,
|
|
open: proxy(that._openHandler, that),
|
|
close: proxy(that._closeHandler, that),
|
|
animation: that.options.animation,
|
|
isRtl: support.isRtl(that.wrapper)
|
|
}));
|
|
},
|
|
_makeUnselectable: function () {
|
|
if (isIE8) {
|
|
this.list.find('*').not('.k-textbox').attr('unselectable', 'on');
|
|
}
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_toggle: function (open, preventFocus) {
|
|
var that = this;
|
|
var touchEnabled = support.mobileOS && (support.touch || support.MSPointers || support.pointers);
|
|
open = open !== undefined ? open : !that.popup.visible();
|
|
if (!preventFocus && !touchEnabled && that._focused[0] !== activeElement()) {
|
|
that._prevent = true;
|
|
that._focused.focus();
|
|
that._prevent = false;
|
|
}
|
|
that[open ? OPEN : CLOSE]();
|
|
},
|
|
_triggerCascade: function () {
|
|
var that = this;
|
|
if (!that._cascadeTriggered || that._old !== that.value() || that._oldIndex !== that.selectedIndex) {
|
|
that._cascadeTriggered = true;
|
|
that.trigger(CASCADE, { userTriggered: that._userTriggered });
|
|
}
|
|
},
|
|
_triggerChange: function () {
|
|
if (this._valueBeforeCascade !== this.value()) {
|
|
this.trigger(CHANGE);
|
|
}
|
|
},
|
|
_unbindDataSource: function () {
|
|
var that = this;
|
|
that.dataSource.unbind(REQUESTSTART, that._requestStartHandler).unbind(REQUESTEND, that._requestEndHandler).unbind('error', that._errorHandler);
|
|
}
|
|
});
|
|
function unifyType(value, type) {
|
|
if (value !== undefined && value !== '' && value !== null) {
|
|
if (type === 'boolean') {
|
|
value = Boolean(value);
|
|
} else if (type === 'number') {
|
|
value = Number(value);
|
|
} else if (type === 'string') {
|
|
value = value.toString();
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
extend(List, {
|
|
inArray: function (node, parentNode) {
|
|
var idx, length, siblings = parentNode.children;
|
|
if (!node || node.parentNode !== parentNode) {
|
|
return -1;
|
|
}
|
|
for (idx = 0, length = siblings.length; idx < length; idx++) {
|
|
if (node === siblings[idx]) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
unifyType: unifyType
|
|
});
|
|
kendo.ui.List = List;
|
|
ui.Select = List.extend({
|
|
init: function (element, options) {
|
|
List.fn.init.call(this, element, options);
|
|
this._initial = this.element.val();
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
var parent;
|
|
that.options.dataSource = dataSource;
|
|
that._dataSource();
|
|
if (that.listView.bound()) {
|
|
that._initialIndex = null;
|
|
}
|
|
that.listView.setDataSource(that.dataSource);
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
}
|
|
parent = that._parentWidget();
|
|
if (parent) {
|
|
that._cascadeSelect(parent);
|
|
}
|
|
},
|
|
close: function () {
|
|
this.popup.close();
|
|
},
|
|
select: function (candidate) {
|
|
var that = this;
|
|
if (candidate === undefined) {
|
|
return that.selectedIndex;
|
|
} else {
|
|
that._select(candidate);
|
|
that._old = that._accessor();
|
|
that._oldIndex = that.selectedIndex;
|
|
}
|
|
},
|
|
search: function (word) {
|
|
word = typeof word === 'string' ? word : this.text();
|
|
var that = this;
|
|
var length = word.length;
|
|
var options = that.options;
|
|
var ignoreCase = options.ignoreCase;
|
|
var field = options.dataTextField;
|
|
clearTimeout(that._typingTimeout);
|
|
if (!length || length >= options.minLength) {
|
|
that._state = 'filter';
|
|
if (!that._isFilterEnabled()) {
|
|
that._filter(word);
|
|
} else {
|
|
that._open = true;
|
|
that._filterSource({
|
|
value: ignoreCase ? word.toLowerCase() : word,
|
|
field: field,
|
|
operator: options.filter,
|
|
ignoreCase: ignoreCase
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_accessor: function (value, idx) {
|
|
return this[this._isSelect ? '_accessorSelect' : '_accessorInput'](value, idx);
|
|
},
|
|
_accessorInput: function (value) {
|
|
var element = this.element[0];
|
|
if (value === undefined) {
|
|
return element.value;
|
|
} else {
|
|
if (value === null) {
|
|
value = '';
|
|
}
|
|
element.value = value;
|
|
}
|
|
},
|
|
_accessorSelect: function (value, idx) {
|
|
var element = this.element[0];
|
|
var selectedIndex = element.selectedIndex;
|
|
var option;
|
|
if (value === undefined) {
|
|
if (selectedIndex > -1) {
|
|
option = element.options[selectedIndex];
|
|
}
|
|
if (option) {
|
|
value = option.value;
|
|
}
|
|
return value || '';
|
|
} else {
|
|
if (selectedIndex > -1) {
|
|
element.options[selectedIndex].removeAttribute(SELECTED);
|
|
element.options[selectedIndex].selected = false;
|
|
}
|
|
if (idx === undefined) {
|
|
idx = -1;
|
|
}
|
|
if (value !== null && value !== '' && idx == -1) {
|
|
this._custom(value);
|
|
} else {
|
|
if (value) {
|
|
element.value = value;
|
|
} else {
|
|
element.selectedIndex = idx;
|
|
}
|
|
if (element.selectedIndex > -1) {
|
|
option = element.options[element.selectedIndex];
|
|
}
|
|
if (option) {
|
|
option.setAttribute(SELECTED, SELECTED);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_custom: function (value) {
|
|
var that = this;
|
|
var element = that.element;
|
|
var custom = that._customOption;
|
|
if (!custom) {
|
|
custom = $('<option/>');
|
|
that._customOption = custom;
|
|
element.append(custom);
|
|
}
|
|
custom.text(value);
|
|
custom[0].setAttribute(SELECTED, SELECTED);
|
|
custom[0].selected = true;
|
|
},
|
|
_hideBusy: function () {
|
|
var that = this;
|
|
clearTimeout(that._busy);
|
|
that._arrow.removeClass(LOADING);
|
|
that._focused.attr('aria-busy', false);
|
|
that._busy = null;
|
|
},
|
|
_showBusy: function () {
|
|
var that = this;
|
|
that._request = true;
|
|
if (that._busy) {
|
|
return;
|
|
}
|
|
that._busy = setTimeout(function () {
|
|
if (that._arrow) {
|
|
that._focused.attr('aria-busy', true);
|
|
that._arrow.addClass(LOADING);
|
|
}
|
|
}, 100);
|
|
},
|
|
_requestEnd: function () {
|
|
this._request = false;
|
|
this._hideBusy();
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, element = that.element, options = that.options, dataSource = options.dataSource || {}, idx;
|
|
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (that._isSelect) {
|
|
idx = element[0].selectedIndex;
|
|
if (idx > -1) {
|
|
options.index = idx;
|
|
}
|
|
dataSource.select = element;
|
|
dataSource.fields = [
|
|
{ field: options.dataTextField },
|
|
{ field: options.dataValueField }
|
|
];
|
|
}
|
|
if (that.dataSource) {
|
|
that._unbindDataSource();
|
|
} else {
|
|
that._requestStartHandler = proxy(that._showBusy, that);
|
|
that._requestEndHandler = proxy(that._requestEnd, that);
|
|
that._errorHandler = proxy(that._hideBusy, that);
|
|
}
|
|
that.dataSource = kendo.data.DataSource.create(dataSource).bind(REQUESTSTART, that._requestStartHandler).bind(REQUESTEND, that._requestEndHandler).bind('error', that._errorHandler);
|
|
},
|
|
_firstItem: function () {
|
|
this.listView.focusFirst();
|
|
},
|
|
_lastItem: function () {
|
|
this.listView.focusLast();
|
|
},
|
|
_nextItem: function () {
|
|
this.listView.focusNext();
|
|
},
|
|
_prevItem: function () {
|
|
this.listView.focusPrev();
|
|
},
|
|
_move: function (e) {
|
|
var that = this;
|
|
var key = e.keyCode;
|
|
var down = key === keys.DOWN;
|
|
var dataItem;
|
|
var pressed;
|
|
var current;
|
|
if (key === keys.UP || down) {
|
|
if (e.altKey) {
|
|
that.toggle(down);
|
|
} else {
|
|
if (!that.listView.bound()) {
|
|
if (!that._fetch) {
|
|
that.dataSource.one(CHANGE, function () {
|
|
that._fetch = false;
|
|
that._move(e);
|
|
});
|
|
that._fetch = true;
|
|
that._filterSource();
|
|
}
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
current = that._focus();
|
|
if (!that._fetch && (!current || current.hasClass('k-state-selected'))) {
|
|
if (down) {
|
|
that._nextItem();
|
|
if (!that._focus()) {
|
|
that._lastItem();
|
|
}
|
|
} else {
|
|
that._prevItem();
|
|
if (!that._focus()) {
|
|
that._firstItem();
|
|
}
|
|
}
|
|
}
|
|
if (that.trigger(SELECT, { item: that._focus() })) {
|
|
that._focus(current);
|
|
return;
|
|
}
|
|
that._select(that._focus(), true);
|
|
if (!that.popup.visible()) {
|
|
that._blur();
|
|
}
|
|
}
|
|
e.preventDefault();
|
|
pressed = true;
|
|
} else if (key === keys.ENTER || key === keys.TAB) {
|
|
if (that.popup.visible()) {
|
|
e.preventDefault();
|
|
}
|
|
current = that._focus();
|
|
dataItem = that.dataItem();
|
|
if (!that.popup.visible() && (!dataItem || that.text() !== that._text(dataItem))) {
|
|
current = null;
|
|
}
|
|
var activeFilter = that.filterInput && that.filterInput[0] === activeElement();
|
|
if (current) {
|
|
if (that.trigger(SELECT, { item: current })) {
|
|
return;
|
|
}
|
|
that._select(current);
|
|
} else if (that.input) {
|
|
that._accessor(that.input.val());
|
|
that.listView.value(that.input.val());
|
|
}
|
|
if (that._focusElement) {
|
|
that._focusElement(that.wrapper);
|
|
}
|
|
if (activeFilter && key === keys.TAB) {
|
|
that.wrapper.focusout();
|
|
} else {
|
|
that._blur();
|
|
}
|
|
that.close();
|
|
pressed = true;
|
|
} else if (key === keys.ESC) {
|
|
if (that.popup.visible()) {
|
|
e.preventDefault();
|
|
}
|
|
that.close();
|
|
pressed = true;
|
|
}
|
|
return pressed;
|
|
},
|
|
_fetchData: function () {
|
|
var that = this;
|
|
var hasItems = !!that.dataSource.view().length;
|
|
if (that._request || that.options.cascadeFrom) {
|
|
return;
|
|
}
|
|
if (!that.listView.bound() && !that._fetch && !hasItems) {
|
|
that._fetch = true;
|
|
that.dataSource.fetch().done(function () {
|
|
that._fetch = false;
|
|
});
|
|
}
|
|
},
|
|
_options: function (data, optionLabel, value) {
|
|
var that = this, element = that.element, length = data.length, options = '', option, dataItem, dataText, dataValue, idx = 0;
|
|
if (optionLabel) {
|
|
options = optionLabel;
|
|
}
|
|
for (; idx < length; idx++) {
|
|
option = '<option';
|
|
dataItem = data[idx];
|
|
dataText = that._text(dataItem);
|
|
dataValue = that._value(dataItem);
|
|
if (dataValue !== undefined) {
|
|
dataValue += '';
|
|
if (dataValue.indexOf('"') !== -1) {
|
|
dataValue = dataValue.replace(quotRegExp, '"');
|
|
}
|
|
option += ' value="' + dataValue + '"';
|
|
}
|
|
option += '>';
|
|
if (dataText !== undefined) {
|
|
option += htmlEncode(dataText);
|
|
}
|
|
option += '</option>';
|
|
options += option;
|
|
}
|
|
element.html(options);
|
|
if (value !== undefined) {
|
|
element[0].value = value;
|
|
if (element[0].value && !value) {
|
|
element[0].selectedIndex = -1;
|
|
}
|
|
}
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
setTimeout(function () {
|
|
that.value(that._initial);
|
|
});
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
},
|
|
_parentWidget: function () {
|
|
var name = this.options.name;
|
|
var parentElement = $('#' + this.options.cascadeFrom);
|
|
var parent = parentElement.data('kendo' + name);
|
|
if (!parent) {
|
|
parent = parentElement.data('kendo' + alternativeNames[name]);
|
|
}
|
|
return parent;
|
|
},
|
|
_cascade: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var cascade = options.cascadeFrom;
|
|
var parent;
|
|
if (cascade) {
|
|
parent = that._parentWidget();
|
|
that._cascadeHandlerProxy = proxy(that._cascadeHandler, that);
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
options.autoBind = false;
|
|
parent.bind('set', function () {
|
|
that.one('set', function (e) {
|
|
that._selectedValue = e.value;
|
|
});
|
|
});
|
|
parent.first(CASCADE, that._cascadeHandlerProxy);
|
|
if (parent.listView.bound()) {
|
|
that._toggleCascadeOnFocus();
|
|
that._cascadeSelect(parent);
|
|
} else {
|
|
parent.one('dataBound', function () {
|
|
that._toggleCascadeOnFocus();
|
|
});
|
|
if (!parent.value()) {
|
|
that.enable(false);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_toggleCascadeOnFocus: function () {
|
|
var that = this;
|
|
var parent = that._parentWidget();
|
|
parent._focused.bind('focus', function () {
|
|
parent.unbind(CASCADE, that._cascadeHandlerProxy);
|
|
parent.first(CHANGE, that._cascadeHandlerProxy);
|
|
});
|
|
parent._focused.bind('focusout', function () {
|
|
parent.unbind(CHANGE, that._cascadeHandlerProxy);
|
|
parent.first(CASCADE, that._cascadeHandlerProxy);
|
|
});
|
|
},
|
|
_cascadeHandler: function (e) {
|
|
var parent = this._parentWidget();
|
|
var valueBeforeCascade = this.value();
|
|
this._userTriggered = e.userTriggered;
|
|
if (this.listView.bound()) {
|
|
this._clearSelection(parent, true);
|
|
}
|
|
this._cascadeSelect(parent, valueBeforeCascade);
|
|
},
|
|
_cascadeChange: function (parent) {
|
|
var that = this;
|
|
var value = that._accessor() || that._selectedValue;
|
|
that._selectedValue = null;
|
|
if (that._userTriggered) {
|
|
that._clearSelection(parent, true);
|
|
} else if (value) {
|
|
if (value !== that.listView.value()[0]) {
|
|
that.value(value);
|
|
}
|
|
if (!that.dataSource.view()[0] || that.selectedIndex === -1) {
|
|
that._clearSelection(parent, true);
|
|
}
|
|
} else if (that.dataSource.flatView().length) {
|
|
that.select(that.options.index);
|
|
}
|
|
that.enable();
|
|
that._triggerCascade();
|
|
that._triggerChange();
|
|
that._userTriggered = false;
|
|
},
|
|
_cascadeSelect: function (parent, valueBeforeCascade) {
|
|
var that = this;
|
|
var dataItem = parent.dataItem();
|
|
var filterValue = dataItem ? parent._value(dataItem) : null;
|
|
var valueField = that.options.cascadeFromField || parent.options.dataValueField;
|
|
var expressions;
|
|
that._valueBeforeCascade = valueBeforeCascade !== undefined ? valueBeforeCascade : that.value();
|
|
if (filterValue || filterValue === 0) {
|
|
expressions = that.dataSource.filter() || {};
|
|
removeFiltersForField(expressions, valueField);
|
|
var handler = function () {
|
|
that.unbind('dataBound', handler);
|
|
that._cascadeChange(parent);
|
|
};
|
|
that.first('dataBound', handler);
|
|
that._cascading = true;
|
|
that._filterSource({
|
|
field: valueField,
|
|
operator: 'eq',
|
|
value: filterValue
|
|
});
|
|
that._cascading = false;
|
|
} else {
|
|
that.enable(false);
|
|
that._clearSelection(parent);
|
|
that._triggerCascade();
|
|
that._triggerChange();
|
|
that._userTriggered = false;
|
|
}
|
|
}
|
|
});
|
|
var STATIC_LIST_NS = '.StaticList';
|
|
var StaticList = kendo.ui.DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.attr('role', 'listbox').on('click' + STATIC_LIST_NS, 'li', proxy(this._click, this)).on('mouseenter' + STATIC_LIST_NS, 'li', function () {
|
|
$(this).addClass(HOVER);
|
|
}).on('mouseleave' + STATIC_LIST_NS, 'li', function () {
|
|
$(this).removeClass(HOVER);
|
|
});
|
|
this.content = this.element.wrap('<div class=\'k-list-scroller\' unselectable=\'on\'></div>').parent();
|
|
this.header = this.content.before('<div class="k-group-header" style="display:none"></div>').prev();
|
|
this.bound(false);
|
|
this._optionID = kendo.guid();
|
|
this._selectedIndices = [];
|
|
this._view = [];
|
|
this._dataItems = [];
|
|
this._values = [];
|
|
var value = this.options.value;
|
|
if (value) {
|
|
this._values = $.isArray(value) ? value.slice(0) : [value];
|
|
}
|
|
this._getter();
|
|
this._templates();
|
|
this.setDataSource(this.options.dataSource);
|
|
this._onScroll = proxy(function () {
|
|
var that = this;
|
|
clearTimeout(that._scrollId);
|
|
that._scrollId = setTimeout(function () {
|
|
that._renderHeader();
|
|
}, 50);
|
|
}, this);
|
|
},
|
|
options: {
|
|
name: 'StaticList',
|
|
dataValueField: null,
|
|
valuePrimitive: false,
|
|
selectable: true,
|
|
template: null,
|
|
groupTemplate: null,
|
|
fixedGroupTemplate: null
|
|
},
|
|
events: [
|
|
'click',
|
|
CHANGE,
|
|
'activate',
|
|
'deactivate',
|
|
'dataBinding',
|
|
'dataBound',
|
|
'selectedItemChange'
|
|
],
|
|
setDataSource: function (source) {
|
|
var that = this;
|
|
var dataSource = source || {};
|
|
var value;
|
|
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
dataSource = kendo.data.DataSource.create(dataSource);
|
|
if (that.dataSource) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
value = that.value();
|
|
that.value([]);
|
|
that.bound(false);
|
|
that.value(value);
|
|
} else {
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
}
|
|
that.setDSFilter(dataSource.filter());
|
|
that.dataSource = dataSource.bind(CHANGE, that._refreshHandler);
|
|
that._fixedHeader();
|
|
},
|
|
skip: function () {
|
|
return this.dataSource.skip();
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
this._getter();
|
|
this._templates();
|
|
this._render();
|
|
},
|
|
destroy: function () {
|
|
this.element.off(STATIC_LIST_NS);
|
|
if (this._refreshHandler) {
|
|
this.dataSource.unbind(CHANGE, this._refreshHandler);
|
|
}
|
|
clearTimeout(this._scrollId);
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
scrollToIndex: function (index) {
|
|
var item = this.element[0].children[index];
|
|
if (item) {
|
|
this.scroll(item);
|
|
}
|
|
},
|
|
scroll: function (item) {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
if (item[0]) {
|
|
item = item[0];
|
|
}
|
|
var content = this.content[0], itemOffsetTop = item.offsetTop, itemOffsetHeight = item.offsetHeight, contentScrollTop = content.scrollTop, contentOffsetHeight = content.clientHeight, bottomDistance = itemOffsetTop + itemOffsetHeight;
|
|
if (contentScrollTop > itemOffsetTop) {
|
|
contentScrollTop = itemOffsetTop;
|
|
} else if (bottomDistance > contentScrollTop + contentOffsetHeight) {
|
|
contentScrollTop = bottomDistance - contentOffsetHeight;
|
|
}
|
|
content.scrollTop = contentScrollTop;
|
|
},
|
|
selectedDataItems: function (dataItems) {
|
|
if (dataItems === undefined) {
|
|
return this._dataItems.slice();
|
|
}
|
|
this._dataItems = dataItems;
|
|
this._values = this._getValues(dataItems);
|
|
},
|
|
_getValues: function (dataItems) {
|
|
var getter = this._valueGetter;
|
|
return $.map(dataItems, function (dataItem) {
|
|
return getter(dataItem);
|
|
});
|
|
},
|
|
focusNext: function () {
|
|
var current = this.focus();
|
|
if (!current) {
|
|
current = 0;
|
|
} else {
|
|
current = current.next();
|
|
}
|
|
this.focus(current);
|
|
},
|
|
focusPrev: function () {
|
|
var current = this.focus();
|
|
if (!current) {
|
|
current = this.element[0].children.length - 1;
|
|
} else {
|
|
current = current.prev();
|
|
}
|
|
this.focus(current);
|
|
},
|
|
focusFirst: function () {
|
|
this.focus(this.element[0].children[0]);
|
|
},
|
|
focusLast: function () {
|
|
this.focus(this.element[0].children[this.element[0].children.length - 1]);
|
|
},
|
|
focus: function (candidate) {
|
|
var that = this;
|
|
var id = that._optionID;
|
|
var hasCandidate;
|
|
if (candidate === undefined) {
|
|
return that._current;
|
|
}
|
|
candidate = that._get(candidate);
|
|
candidate = candidate[candidate.length - 1];
|
|
candidate = $(this.element[0].children[candidate]);
|
|
if (that._current) {
|
|
that._current.removeClass(FOCUSED).removeAttr('aria-selected').removeAttr(ID);
|
|
that.trigger('deactivate');
|
|
}
|
|
hasCandidate = !!candidate[0];
|
|
if (hasCandidate) {
|
|
candidate.addClass(FOCUSED);
|
|
that.scroll(candidate);
|
|
candidate.attr('id', id);
|
|
}
|
|
that._current = hasCandidate ? candidate : null;
|
|
that.trigger('activate');
|
|
},
|
|
focusIndex: function () {
|
|
return this.focus() ? this.focus().index() : undefined;
|
|
},
|
|
skipUpdate: function (skipUpdate) {
|
|
this._skipUpdate = skipUpdate;
|
|
},
|
|
select: function (indices) {
|
|
var that = this;
|
|
var selectable = that.options.selectable;
|
|
var singleSelection = selectable !== 'multiple' && selectable !== false;
|
|
var selectedIndices = that._selectedIndices;
|
|
var added = [];
|
|
var removed = [];
|
|
var result;
|
|
if (indices === undefined) {
|
|
return selectedIndices.slice();
|
|
}
|
|
indices = that._get(indices);
|
|
if (indices.length === 1 && indices[0] === -1) {
|
|
indices = [];
|
|
}
|
|
var filtered = that.isFiltered();
|
|
if (filtered && !singleSelection && that._deselectFiltered(indices)) {
|
|
return;
|
|
}
|
|
if (singleSelection && !filtered && $.inArray(indices[indices.length - 1], selectedIndices) !== -1) {
|
|
if (that._dataItems.length && that._view.length) {
|
|
that._dataItems = [that._view[selectedIndices[0]].item];
|
|
}
|
|
return;
|
|
}
|
|
result = that._deselect(indices);
|
|
removed = result.removed;
|
|
indices = result.indices;
|
|
if (indices.length) {
|
|
if (singleSelection) {
|
|
indices = [indices[indices.length - 1]];
|
|
}
|
|
added = that._select(indices);
|
|
}
|
|
if (added.length || removed.length) {
|
|
that._valueComparer = null;
|
|
that.trigger(CHANGE, {
|
|
added: added,
|
|
removed: removed
|
|
});
|
|
}
|
|
},
|
|
removeAt: function (position) {
|
|
this._selectedIndices.splice(position, 1);
|
|
this._values.splice(position, 1);
|
|
this._valueComparer = null;
|
|
return {
|
|
position: position,
|
|
dataItem: this._dataItems.splice(position, 1)[0]
|
|
};
|
|
},
|
|
setValue: function (value) {
|
|
value = $.isArray(value) || value instanceof ObservableArray ? value.slice(0) : [value];
|
|
this._values = value;
|
|
this._valueComparer = null;
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var deferred = that._valueDeferred;
|
|
var indices;
|
|
if (value === undefined) {
|
|
return that._values.slice();
|
|
}
|
|
that.setValue(value);
|
|
if (!deferred || deferred.state() === 'resolved') {
|
|
that._valueDeferred = deferred = $.Deferred();
|
|
}
|
|
if (that.bound()) {
|
|
indices = that._valueIndices(that._values);
|
|
if (that.options.selectable === 'multiple') {
|
|
that.select(-1);
|
|
}
|
|
that.select(indices);
|
|
deferred.resolve();
|
|
}
|
|
that._skipUpdate = false;
|
|
return deferred;
|
|
},
|
|
items: function () {
|
|
return this.element.children('.k-item');
|
|
},
|
|
_click: function (e) {
|
|
if (!e.isDefaultPrevented()) {
|
|
if (!this.trigger('click', { item: $(e.currentTarget) })) {
|
|
this.select(e.currentTarget);
|
|
}
|
|
}
|
|
},
|
|
_valueExpr: function (type, values) {
|
|
var that = this;
|
|
var idx = 0;
|
|
var body;
|
|
var comparer;
|
|
var normalized = [];
|
|
if (!that._valueComparer || that._valueType !== type) {
|
|
that._valueType = type;
|
|
for (; idx < values.length; idx++) {
|
|
normalized.push(unifyType(values[idx], type));
|
|
}
|
|
body = 'for (var idx = 0; idx < ' + normalized.length + '; idx++) {' + ' if (current === values[idx]) {' + ' return idx;' + ' }' + '} ' + 'return -1;';
|
|
comparer = new Function('current', 'values', body);
|
|
that._valueComparer = function (current) {
|
|
return comparer(current, normalized);
|
|
};
|
|
}
|
|
return that._valueComparer;
|
|
},
|
|
_dataItemPosition: function (dataItem, values) {
|
|
var value = this._valueGetter(dataItem);
|
|
var valueExpr = this._valueExpr(typeof value, values);
|
|
return valueExpr(value);
|
|
},
|
|
_getter: function () {
|
|
this._valueGetter = kendo.getter(this.options.dataValueField);
|
|
},
|
|
_deselect: function (indices) {
|
|
var that = this;
|
|
var children = that.element[0].children;
|
|
var selectable = that.options.selectable;
|
|
var selectedIndices = that._selectedIndices;
|
|
var dataItems = that._dataItems;
|
|
var values = that._values;
|
|
var removed = [];
|
|
var i = 0;
|
|
var j;
|
|
var index, selectedIndex;
|
|
var removedIndices = 0;
|
|
indices = indices.slice();
|
|
if (selectable === true || !indices.length) {
|
|
for (; i < selectedIndices.length; i++) {
|
|
$(children[selectedIndices[i]]).removeClass('k-state-selected');
|
|
removed.push({
|
|
position: i,
|
|
dataItem: dataItems[i]
|
|
});
|
|
}
|
|
that._values = [];
|
|
that._dataItems = [];
|
|
that._selectedIndices = [];
|
|
} else if (selectable === 'multiple') {
|
|
for (; i < indices.length; i++) {
|
|
index = indices[i];
|
|
if (!$(children[index]).hasClass('k-state-selected')) {
|
|
continue;
|
|
}
|
|
for (j = 0; j < selectedIndices.length; j++) {
|
|
selectedIndex = selectedIndices[j];
|
|
if (selectedIndex === index) {
|
|
$(children[selectedIndex]).removeClass('k-state-selected');
|
|
removed.push({
|
|
position: j + removedIndices,
|
|
dataItem: dataItems.splice(j, 1)[0]
|
|
});
|
|
selectedIndices.splice(j, 1);
|
|
indices.splice(i, 1);
|
|
values.splice(j, 1);
|
|
removedIndices += 1;
|
|
i -= 1;
|
|
j -= 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
indices: indices,
|
|
removed: removed
|
|
};
|
|
},
|
|
_deselectFiltered: function (indices) {
|
|
var children = this.element[0].children;
|
|
var dataItem, index, position;
|
|
var removed = [];
|
|
var idx = 0;
|
|
for (; idx < indices.length; idx++) {
|
|
index = indices[idx];
|
|
dataItem = this._view[index].item;
|
|
position = this._dataItemPosition(dataItem, this._values);
|
|
if (position > -1) {
|
|
removed.push(this.removeAt(position));
|
|
$(children[index]).removeClass('k-state-selected');
|
|
}
|
|
}
|
|
if (removed.length) {
|
|
this.trigger(CHANGE, {
|
|
added: [],
|
|
removed: removed
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_select: function (indices) {
|
|
var that = this;
|
|
var children = that.element[0].children;
|
|
var data = that._view;
|
|
var dataItem, index;
|
|
var added = [];
|
|
var idx = 0;
|
|
if (indices[indices.length - 1] !== -1) {
|
|
that.focus(indices);
|
|
}
|
|
for (; idx < indices.length; idx++) {
|
|
index = indices[idx];
|
|
dataItem = data[index];
|
|
if (index === -1 || !dataItem) {
|
|
continue;
|
|
}
|
|
dataItem = dataItem.item;
|
|
that._selectedIndices.push(index);
|
|
that._dataItems.push(dataItem);
|
|
that._values.push(that._valueGetter(dataItem));
|
|
$(children[index]).addClass('k-state-selected').attr('aria-selected', true);
|
|
added.push({ dataItem: dataItem });
|
|
}
|
|
return added;
|
|
},
|
|
_get: function (candidate) {
|
|
if (typeof candidate === 'number') {
|
|
candidate = [candidate];
|
|
} else if (!isArray(candidate)) {
|
|
candidate = $(candidate).data('offset-index');
|
|
if (candidate === undefined) {
|
|
candidate = -1;
|
|
}
|
|
candidate = [candidate];
|
|
}
|
|
return candidate;
|
|
},
|
|
_template: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var template = options.template;
|
|
if (!template) {
|
|
template = kendo.template('<li tabindex="-1" role="option" unselectable="on" class="k-item">${' + kendo.expr(options.dataTextField, 'data') + '}</li>', { useWithBlock: false });
|
|
} else {
|
|
template = kendo.template(template);
|
|
template = function (data) {
|
|
return '<li tabindex="-1" role="option" unselectable="on" class="k-item">' + template(data) + '</li>';
|
|
};
|
|
}
|
|
return template;
|
|
},
|
|
_templates: function () {
|
|
var template;
|
|
var templates = {
|
|
template: this.options.template,
|
|
groupTemplate: this.options.groupTemplate,
|
|
fixedGroupTemplate: this.options.fixedGroupTemplate
|
|
};
|
|
for (var key in templates) {
|
|
template = templates[key];
|
|
if (template && typeof template !== 'function') {
|
|
templates[key] = kendo.template(template);
|
|
}
|
|
}
|
|
this.templates = templates;
|
|
},
|
|
_normalizeIndices: function (indices) {
|
|
var newIndices = [];
|
|
var idx = 0;
|
|
for (; idx < indices.length; idx++) {
|
|
if (indices[idx] !== undefined) {
|
|
newIndices.push(indices[idx]);
|
|
}
|
|
}
|
|
return newIndices;
|
|
},
|
|
_valueIndices: function (values, indices) {
|
|
var data = this._view;
|
|
var idx = 0;
|
|
var index;
|
|
indices = indices ? indices.slice() : [];
|
|
if (!values.length) {
|
|
return [];
|
|
}
|
|
for (; idx < data.length; idx++) {
|
|
index = this._dataItemPosition(data[idx].item, values);
|
|
if (index !== -1) {
|
|
indices[index] = idx;
|
|
}
|
|
}
|
|
return this._normalizeIndices(indices);
|
|
},
|
|
_firstVisibleItem: function () {
|
|
var element = this.element[0];
|
|
var content = this.content[0];
|
|
var scrollTop = content.scrollTop;
|
|
var itemHeight = $(element.children[0]).height();
|
|
var itemIndex = Math.floor(scrollTop / itemHeight) || 0;
|
|
var item = element.children[itemIndex] || element.lastChild;
|
|
var forward = item.offsetTop < scrollTop;
|
|
while (item) {
|
|
if (forward) {
|
|
if (item.offsetTop + itemHeight > scrollTop || !item.nextSibling) {
|
|
break;
|
|
}
|
|
item = item.nextSibling;
|
|
} else {
|
|
if (item.offsetTop <= scrollTop || !item.previousSibling) {
|
|
break;
|
|
}
|
|
item = item.previousSibling;
|
|
}
|
|
}
|
|
return this._view[$(item).data('offset-index')];
|
|
},
|
|
_fixedHeader: function () {
|
|
if (this.isGrouped() && this.templates.fixedGroupTemplate) {
|
|
this.header.show();
|
|
this.content.scroll(this._onScroll);
|
|
} else {
|
|
this.header.hide();
|
|
this.content.off('scroll', this._onScroll);
|
|
}
|
|
},
|
|
_renderHeader: function () {
|
|
var template = this.templates.fixedGroupTemplate;
|
|
if (!template) {
|
|
return;
|
|
}
|
|
var visibleItem = this._firstVisibleItem();
|
|
if (visibleItem) {
|
|
this.header.html(template(visibleItem.group));
|
|
}
|
|
},
|
|
_renderItem: function (context) {
|
|
var item = '<li tabindex="-1" role="option" unselectable="on" class="k-item';
|
|
var dataItem = context.item;
|
|
var notFirstItem = context.index !== 0;
|
|
var selected = context.selected;
|
|
if (notFirstItem && context.newGroup) {
|
|
item += ' k-first';
|
|
}
|
|
if (selected) {
|
|
item += ' k-state-selected';
|
|
}
|
|
item += '"' + (selected ? ' aria-selected="true"' : '') + ' data-offset-index="' + context.index + '">';
|
|
item += this.templates.template(dataItem);
|
|
if (notFirstItem && context.newGroup) {
|
|
item += '<div class="k-group">' + this.templates.groupTemplate(context.group) + '</div>';
|
|
}
|
|
return item + '</li>';
|
|
},
|
|
_render: function () {
|
|
var html = '';
|
|
var i = 0;
|
|
var idx = 0;
|
|
var context;
|
|
var dataContext = [];
|
|
var view = this.dataSource.view();
|
|
var values = this.value();
|
|
var group, newGroup, j;
|
|
var isGrouped = this.isGrouped();
|
|
if (isGrouped) {
|
|
for (i = 0; i < view.length; i++) {
|
|
group = view[i];
|
|
newGroup = true;
|
|
for (j = 0; j < group.items.length; j++) {
|
|
context = {
|
|
selected: this._selected(group.items[j], values),
|
|
item: group.items[j],
|
|
group: group.value,
|
|
newGroup: newGroup,
|
|
index: idx
|
|
};
|
|
dataContext[idx] = context;
|
|
idx += 1;
|
|
html += this._renderItem(context);
|
|
newGroup = false;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < view.length; i++) {
|
|
context = {
|
|
selected: this._selected(view[i], values),
|
|
item: view[i],
|
|
index: i
|
|
};
|
|
dataContext[i] = context;
|
|
html += this._renderItem(context);
|
|
}
|
|
}
|
|
this._view = dataContext;
|
|
this.element[0].innerHTML = html;
|
|
if (isGrouped && dataContext.length) {
|
|
this._renderHeader();
|
|
}
|
|
},
|
|
_selected: function (dataItem, values) {
|
|
var select = !this.isFiltered() || this.options.selectable === 'multiple';
|
|
return select && this._dataItemPosition(dataItem, values) !== -1;
|
|
},
|
|
setDSFilter: function (filter) {
|
|
this._lastDSFilter = extend({}, filter);
|
|
},
|
|
isFiltered: function () {
|
|
if (!this._lastDSFilter) {
|
|
this.setDSFilter(this.dataSource.filter());
|
|
}
|
|
return !kendo.data.Query.compareFilters(this.dataSource.filter(), this._lastDSFilter);
|
|
},
|
|
refresh: function (e) {
|
|
var that = this;
|
|
var action = e && e.action;
|
|
var skipUpdateOnBind = that.options.skipUpdateOnBind;
|
|
var isItemChange = action === 'itemchange';
|
|
var result;
|
|
that.trigger('dataBinding');
|
|
that._fixedHeader();
|
|
that._render();
|
|
that.bound(true);
|
|
if (isItemChange || action === 'remove') {
|
|
result = mapChangedItems(that._dataItems, e.items);
|
|
if (result.changed.length) {
|
|
if (isItemChange) {
|
|
that.trigger('selectedItemChange', { items: result.changed });
|
|
} else {
|
|
that.value(that._getValues(result.unchanged));
|
|
}
|
|
}
|
|
} else if (that.isFiltered() || that._skipUpdate) {
|
|
that.focus(0);
|
|
if (that._skipUpdate) {
|
|
that._skipUpdate = false;
|
|
that._selectedIndices = that._valueIndices(that._values, that._selectedIndices);
|
|
}
|
|
} else if (!skipUpdateOnBind && (!action || action === 'add')) {
|
|
that.value(that._values);
|
|
}
|
|
if (that._valueDeferred) {
|
|
that._valueDeferred.resolve();
|
|
}
|
|
that.trigger('dataBound');
|
|
},
|
|
bound: function (bound) {
|
|
if (bound === undefined) {
|
|
return this._bound;
|
|
}
|
|
this._bound = bound;
|
|
},
|
|
isGrouped: function () {
|
|
return (this.dataSource.group() || []).length;
|
|
}
|
|
});
|
|
ui.plugin(StaticList);
|
|
function mapChangedItems(selected, itemsToMatch) {
|
|
var itemsLength = itemsToMatch.length;
|
|
var selectedLength = selected.length;
|
|
var dataItem;
|
|
var found;
|
|
var i, j;
|
|
var changed = [];
|
|
var unchanged = [];
|
|
if (selectedLength) {
|
|
for (i = 0; i < selectedLength; i++) {
|
|
dataItem = selected[i];
|
|
found = false;
|
|
for (j = 0; j < itemsLength; j++) {
|
|
if (dataItem === itemsToMatch[j]) {
|
|
found = true;
|
|
changed.push({
|
|
index: i,
|
|
item: dataItem
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
unchanged.push(dataItem);
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
changed: changed,
|
|
unchanged: unchanged
|
|
};
|
|
}
|
|
function removeFiltersForField(expression, field) {
|
|
var filters;
|
|
var found = false;
|
|
if (expression.filters) {
|
|
filters = $.grep(expression.filters, function (filter) {
|
|
found = removeFiltersForField(filter, field);
|
|
if (filter.filters) {
|
|
return filter.filters.length;
|
|
} else {
|
|
return filter.field != field;
|
|
}
|
|
});
|
|
if (!found && expression.filters.length !== filters.length) {
|
|
found = true;
|
|
}
|
|
expression.filters = filters;
|
|
}
|
|
return found;
|
|
}
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.calendar', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'calendar',
|
|
name: 'Calendar',
|
|
category: 'web',
|
|
description: 'The Calendar widget renders a graphical calendar that supports navigation and selection.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, support = kendo.support, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, parse = kendo.parseDate, adjustDST = kendo.date.adjustDST, extractFormat = kendo._extractFormat, template = kendo.template, getCulture = kendo.getCulture, transitions = kendo.support.transitions, transitionOrigin = transitions ? transitions.css + 'transform-origin' : '', cellTemplate = template('<td#=data.cssClass# role="gridcell"><a tabindex="-1" class="k-link" href="\\#" data-#=data.ns#value="#=data.dateString#">#=data.value#</a></td>', { useWithBlock: false }), emptyCellTemplate = template('<td role="gridcell"> </td>', { useWithBlock: false }), browser = kendo.support.browser, isIE8 = browser.msie && browser.version < 9, ns = '.kendoCalendar', CLICK = 'click' + ns, KEYDOWN_NS = 'keydown' + ns, ID = 'id', MIN = 'min', LEFT = 'left', SLIDE = 'slideIn', MONTH = 'month', CENTURY = 'century', CHANGE = 'change', NAVIGATE = 'navigate', VALUE = 'value', HOVER = 'k-state-hover', DISABLED = 'k-state-disabled', FOCUSED = 'k-state-focused', OTHERMONTH = 'k-other-month', OTHERMONTHCLASS = ' class="' + OTHERMONTH + '"', TODAY = 'k-nav-today', CELLSELECTOR = 'td:has(.k-link)', BLUR = 'blur' + ns, FOCUS = 'focus', FOCUS_WITH_NS = FOCUS + ns, MOUSEENTER = support.touch ? 'touchstart' : 'mouseenter', MOUSEENTER_WITH_NS = support.touch ? 'touchstart' + ns : 'mouseenter' + ns, MOUSELEAVE = support.touch ? 'touchend' + ns + ' touchmove' + ns : 'mouseleave' + ns, MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000, PREVARROW = '_prevArrow', NEXTARROW = '_nextArrow', ARIA_DISABLED = 'aria-disabled', ARIA_SELECTED = 'aria-selected', proxy = $.proxy, extend = $.extend, DATE = Date, views = {
|
|
month: 0,
|
|
year: 1,
|
|
decade: 2,
|
|
century: 3
|
|
};
|
|
var Calendar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, value, id;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.wrapper = that.element;
|
|
options = that.options;
|
|
options.url = window.unescape(options.url);
|
|
that.options.disableDates = getDisabledExpr(that.options.disableDates);
|
|
that._templates();
|
|
that._header();
|
|
that._footer(that.footer);
|
|
id = element.addClass('k-widget k-calendar').on(MOUSEENTER_WITH_NS + ' ' + MOUSELEAVE, CELLSELECTOR, mousetoggle).on(KEYDOWN_NS, 'table.k-content', proxy(that._move, that)).on(CLICK, CELLSELECTOR, function (e) {
|
|
var link = e.currentTarget.firstChild, value = that._toDateObject(link);
|
|
if (link.href.indexOf('#') != -1) {
|
|
e.preventDefault();
|
|
}
|
|
if (that.options.disableDates(value) && that._view.name == 'month') {
|
|
return;
|
|
}
|
|
that._click($(link));
|
|
}).on('mouseup' + ns, 'table.k-content, .k-footer', function () {
|
|
that._focusView(that.options.focusOnNav !== false);
|
|
}).attr(ID);
|
|
if (id) {
|
|
that._cellID = id + '_cell_selected';
|
|
}
|
|
normalize(options);
|
|
value = parse(options.value, options.format, options.culture);
|
|
that._index = views[options.start];
|
|
that._current = new DATE(+restrictValue(value, options.min, options.max));
|
|
that._addClassProxy = function () {
|
|
that._active = true;
|
|
if (that._cell.hasClass(DISABLED)) {
|
|
var todayString = that._view.toDateString(getToday());
|
|
that._cell = that._cellByDate(todayString);
|
|
}
|
|
that._cell.addClass(FOCUSED);
|
|
};
|
|
that._removeClassProxy = function () {
|
|
that._active = false;
|
|
that._cell.removeClass(FOCUSED);
|
|
};
|
|
that.value(value);
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'Calendar',
|
|
value: null,
|
|
min: new DATE(1900, 0, 1),
|
|
max: new DATE(2099, 11, 31),
|
|
dates: [],
|
|
url: '',
|
|
culture: '',
|
|
footer: '',
|
|
format: '',
|
|
month: {},
|
|
start: MONTH,
|
|
depth: MONTH,
|
|
animation: {
|
|
horizontal: {
|
|
effects: SLIDE,
|
|
reverse: true,
|
|
duration: 500,
|
|
divisor: 2
|
|
},
|
|
vertical: {
|
|
effects: 'zoomIn',
|
|
duration: 400
|
|
}
|
|
}
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
NAVIGATE
|
|
],
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
normalize(options);
|
|
if (!options.dates[0]) {
|
|
options.dates = that.options.dates;
|
|
}
|
|
options.disableDates = getDisabledExpr(options.disableDates);
|
|
Widget.fn.setOptions.call(that, options);
|
|
that._templates();
|
|
that._footer(that.footer);
|
|
that._index = views[that.options.start];
|
|
that.navigate();
|
|
},
|
|
destroy: function () {
|
|
var that = this, today = that._today;
|
|
that.element.off(ns);
|
|
that._title.off(ns);
|
|
that[PREVARROW].off(ns);
|
|
that[NEXTARROW].off(ns);
|
|
kendo.destroy(that._table);
|
|
if (today) {
|
|
kendo.destroy(today.off(ns));
|
|
}
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
current: function () {
|
|
return this._current;
|
|
},
|
|
view: function () {
|
|
return this._view;
|
|
},
|
|
focus: function (table) {
|
|
table = table || this._table;
|
|
this._bindTable(table);
|
|
table.focus();
|
|
},
|
|
min: function (value) {
|
|
return this._option(MIN, value);
|
|
},
|
|
max: function (value) {
|
|
return this._option('max', value);
|
|
},
|
|
navigateToPast: function () {
|
|
this._navigate(PREVARROW, -1);
|
|
},
|
|
navigateToFuture: function () {
|
|
this._navigate(NEXTARROW, 1);
|
|
},
|
|
navigateUp: function () {
|
|
var that = this, index = that._index;
|
|
if (that._title.hasClass(DISABLED)) {
|
|
return;
|
|
}
|
|
that.navigate(that._current, ++index);
|
|
},
|
|
navigateDown: function (value) {
|
|
var that = this, index = that._index, depth = that.options.depth;
|
|
if (!value) {
|
|
return;
|
|
}
|
|
if (index === views[depth]) {
|
|
if (!isEqualDate(that._value, that._current) || !isEqualDate(that._value, value)) {
|
|
that.value(value);
|
|
that.trigger(CHANGE);
|
|
}
|
|
return;
|
|
}
|
|
that.navigate(value, --index);
|
|
},
|
|
navigate: function (value, view) {
|
|
view = isNaN(view) ? views[view] : view;
|
|
var that = this, options = that.options, culture = options.culture, min = options.min, max = options.max, title = that._title, from = that._table, old = that._oldTable, selectedValue = that._value, currentValue = that._current, future = value && +value > +currentValue, vertical = view !== undefined && view !== that._index, to, currentView, compare, disabled;
|
|
if (!value) {
|
|
value = currentValue;
|
|
}
|
|
that._current = value = new DATE(+restrictValue(value, min, max));
|
|
if (view === undefined) {
|
|
view = that._index;
|
|
} else {
|
|
that._index = view;
|
|
}
|
|
that._view = currentView = calendar.views[view];
|
|
compare = currentView.compare;
|
|
disabled = view === views[CENTURY];
|
|
title.toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
|
|
disabled = compare(value, min) < 1;
|
|
that[PREVARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
|
|
disabled = compare(value, max) > -1;
|
|
that[NEXTARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
|
|
if (from && old && old.data('animating')) {
|
|
old.kendoStop(true, true);
|
|
from.kendoStop(true, true);
|
|
}
|
|
that._oldTable = from;
|
|
if (!from || that._changeView) {
|
|
title.html(currentView.title(value, min, max, culture));
|
|
that._table = to = $(currentView.content(extend({
|
|
min: min,
|
|
max: max,
|
|
date: value,
|
|
url: options.url,
|
|
dates: options.dates,
|
|
format: options.format,
|
|
culture: culture,
|
|
disableDates: options.disableDates
|
|
}, that[currentView.name])));
|
|
makeUnselectable(to);
|
|
var replace = from && from.data('start') === to.data('start');
|
|
that._animate({
|
|
from: from,
|
|
to: to,
|
|
vertical: vertical,
|
|
future: future,
|
|
replace: replace
|
|
});
|
|
that.trigger(NAVIGATE);
|
|
that._focus(value);
|
|
}
|
|
if (view === views[options.depth] && selectedValue && !that.options.disableDates(selectedValue)) {
|
|
that._class('k-state-selected', selectedValue);
|
|
}
|
|
that._class(FOCUSED, value);
|
|
if (!from && that._cell) {
|
|
that._cell.removeClass(FOCUSED);
|
|
}
|
|
that._changeView = true;
|
|
},
|
|
value: function (value) {
|
|
var that = this, view = that._view, options = that.options, old = that._view, min = options.min, max = options.max;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
if (value === null) {
|
|
that._current = new Date(that._current.getFullYear(), that._current.getMonth(), that._current.getDate());
|
|
}
|
|
value = parse(value, options.format, options.culture);
|
|
if (value !== null) {
|
|
value = new DATE(+value);
|
|
if (!isInRange(value, min, max)) {
|
|
value = null;
|
|
}
|
|
}
|
|
if (!that.options.disableDates(value)) {
|
|
that._value = value;
|
|
} else if (that._value === undefined) {
|
|
that._value = null;
|
|
}
|
|
if (old && value === null && that._cell) {
|
|
that._cell.removeClass('k-state-selected');
|
|
} else {
|
|
that._changeView = !value || view && view.compare(value, that._current) !== 0;
|
|
that.navigate(value);
|
|
}
|
|
},
|
|
_move: function (e) {
|
|
var that = this, options = that.options, key = e.keyCode, view = that._view, index = that._index, min = that.options.min, max = that.options.max, currentValue = new DATE(+that._current), isRtl = kendo.support.isRtl(that.wrapper), isDisabled = that.options.disableDates, value, prevent, method, temp;
|
|
if (e.target === that._table[0]) {
|
|
that._active = true;
|
|
}
|
|
if (e.ctrlKey) {
|
|
if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) {
|
|
that.navigateToFuture();
|
|
prevent = true;
|
|
} else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) {
|
|
that.navigateToPast();
|
|
prevent = true;
|
|
} else if (key == keys.UP) {
|
|
that.navigateUp();
|
|
prevent = true;
|
|
} else if (key == keys.DOWN) {
|
|
that._click($(that._cell[0].firstChild));
|
|
prevent = true;
|
|
}
|
|
} else {
|
|
if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) {
|
|
value = 1;
|
|
prevent = true;
|
|
} else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) {
|
|
value = -1;
|
|
prevent = true;
|
|
} else if (key == keys.UP) {
|
|
value = index === 0 ? -7 : -4;
|
|
prevent = true;
|
|
} else if (key == keys.DOWN) {
|
|
value = index === 0 ? 7 : 4;
|
|
prevent = true;
|
|
} else if (key == keys.ENTER) {
|
|
that._click($(that._cell[0].firstChild));
|
|
prevent = true;
|
|
} else if (key == keys.HOME || key == keys.END) {
|
|
method = key == keys.HOME ? 'first' : 'last';
|
|
temp = view[method](currentValue);
|
|
currentValue = new DATE(temp.getFullYear(), temp.getMonth(), temp.getDate(), currentValue.getHours(), currentValue.getMinutes(), currentValue.getSeconds(), currentValue.getMilliseconds());
|
|
prevent = true;
|
|
} else if (key == keys.PAGEUP) {
|
|
prevent = true;
|
|
that.navigateToPast();
|
|
} else if (key == keys.PAGEDOWN) {
|
|
prevent = true;
|
|
that.navigateToFuture();
|
|
}
|
|
if (value || method) {
|
|
if (!method) {
|
|
view.setDate(currentValue, value);
|
|
}
|
|
if (isDisabled(currentValue)) {
|
|
currentValue = that._nextNavigatable(currentValue, value);
|
|
}
|
|
if (isInRange(currentValue, min, max)) {
|
|
that._focus(restrictValue(currentValue, options.min, options.max));
|
|
}
|
|
}
|
|
}
|
|
if (prevent) {
|
|
e.preventDefault();
|
|
}
|
|
return that._current;
|
|
},
|
|
_nextNavigatable: function (currentValue, value) {
|
|
var that = this, disabled = true, view = that._view, min = that.options.min, max = that.options.max, isDisabled = that.options.disableDates, navigatableDate = new Date(currentValue.getTime());
|
|
view.setDate(navigatableDate, -value);
|
|
while (disabled) {
|
|
view.setDate(currentValue, value);
|
|
if (!isInRange(currentValue, min, max)) {
|
|
currentValue = navigatableDate;
|
|
break;
|
|
}
|
|
disabled = isDisabled(currentValue);
|
|
}
|
|
return currentValue;
|
|
},
|
|
_animate: function (options) {
|
|
var that = this, from = options.from, to = options.to, active = that._active;
|
|
if (!from) {
|
|
to.insertAfter(that.element[0].firstChild);
|
|
that._bindTable(to);
|
|
} else if (from.parent().data('animating')) {
|
|
from.off(ns);
|
|
from.parent().kendoStop(true, true).remove();
|
|
from.remove();
|
|
to.insertAfter(that.element[0].firstChild);
|
|
that._focusView(active);
|
|
} else if (!from.is(':visible') || that.options.animation === false || options.replace) {
|
|
to.insertAfter(from);
|
|
from.off(ns).remove();
|
|
that._focusView(active);
|
|
} else {
|
|
that[options.vertical ? '_vertical' : '_horizontal'](from, to, options.future);
|
|
}
|
|
},
|
|
_horizontal: function (from, to, future) {
|
|
var that = this, active = that._active, horizontal = that.options.animation.horizontal, effects = horizontal.effects, viewWidth = from.outerWidth();
|
|
if (effects && effects.indexOf(SLIDE) != -1) {
|
|
from.add(to).css({ width: viewWidth });
|
|
from.wrap('<div/>');
|
|
that._focusView(active, from);
|
|
from.parent().css({
|
|
position: 'relative',
|
|
width: viewWidth * 2,
|
|
'float': LEFT,
|
|
'margin-left': future ? 0 : -viewWidth
|
|
});
|
|
to[future ? 'insertAfter' : 'insertBefore'](from);
|
|
extend(horizontal, {
|
|
effects: SLIDE + ':' + (future ? 'right' : LEFT),
|
|
complete: function () {
|
|
from.off(ns).remove();
|
|
that._oldTable = null;
|
|
to.unwrap();
|
|
that._focusView(active);
|
|
}
|
|
});
|
|
from.parent().kendoStop(true, true).kendoAnimate(horizontal);
|
|
}
|
|
},
|
|
_vertical: function (from, to) {
|
|
var that = this, vertical = that.options.animation.vertical, effects = vertical.effects, active = that._active, cell, position;
|
|
if (effects && effects.indexOf('zoom') != -1) {
|
|
to.css({
|
|
position: 'absolute',
|
|
top: from.prev().outerHeight(),
|
|
left: 0
|
|
}).insertBefore(from);
|
|
if (transitionOrigin) {
|
|
cell = that._cellByDate(that._view.toDateString(that._current));
|
|
position = cell.position();
|
|
position = position.left + parseInt(cell.width() / 2, 10) + 'px' + ' ' + (position.top + parseInt(cell.height() / 2, 10) + 'px');
|
|
to.css(transitionOrigin, position);
|
|
}
|
|
from.kendoStop(true, true).kendoAnimate({
|
|
effects: 'fadeOut',
|
|
duration: 600,
|
|
complete: function () {
|
|
from.off(ns).remove();
|
|
that._oldTable = null;
|
|
to.css({
|
|
position: 'static',
|
|
top: 0,
|
|
left: 0
|
|
});
|
|
that._focusView(active);
|
|
}
|
|
});
|
|
to.kendoStop(true, true).kendoAnimate(vertical);
|
|
}
|
|
},
|
|
_cellByDate: function (value) {
|
|
return this._table.find('td:not(.' + OTHERMONTH + ')').filter(function () {
|
|
return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
|
|
});
|
|
},
|
|
_class: function (className, date) {
|
|
var that = this, id = that._cellID, cell = that._cell, value = that._view.toDateString(date), disabledDate;
|
|
if (cell) {
|
|
cell.removeAttr(ARIA_SELECTED).removeAttr('aria-label').removeAttr(ID);
|
|
}
|
|
if (date) {
|
|
disabledDate = that.options.disableDates(date);
|
|
}
|
|
cell = that._table.find('td:not(.' + OTHERMONTH + ')').removeClass(className).filter(function () {
|
|
return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
|
|
}).attr(ARIA_SELECTED, true);
|
|
if (className === FOCUSED && !that._active && that.options.focusOnNav !== false || disabledDate) {
|
|
className = '';
|
|
}
|
|
cell.addClass(className);
|
|
if (cell[0]) {
|
|
that._cell = cell;
|
|
}
|
|
if (id) {
|
|
cell.attr(ID, id);
|
|
that._table.removeAttr('aria-activedescendant').attr('aria-activedescendant', id);
|
|
}
|
|
},
|
|
_bindTable: function (table) {
|
|
table.on(FOCUS_WITH_NS, this._addClassProxy).on(BLUR, this._removeClassProxy);
|
|
},
|
|
_click: function (link) {
|
|
var that = this, options = that.options, currentValue = new Date(+that._current), value = that._toDateObject(link);
|
|
adjustDST(value, 0);
|
|
if (that.options.disableDates(value) && that._view.name == 'month') {
|
|
value = that._value;
|
|
}
|
|
that._view.setDate(currentValue, value);
|
|
that.navigateDown(restrictValue(currentValue, options.min, options.max));
|
|
},
|
|
_focus: function (value) {
|
|
var that = this, view = that._view;
|
|
if (view.compare(value, that._current) !== 0) {
|
|
that.navigate(value);
|
|
} else {
|
|
that._current = value;
|
|
that._class(FOCUSED, value);
|
|
}
|
|
},
|
|
_focusView: function (active, table) {
|
|
if (active) {
|
|
this.focus(table);
|
|
}
|
|
},
|
|
_footer: function (template) {
|
|
var that = this, today = getToday(), element = that.element, footer = element.find('.k-footer');
|
|
if (!template) {
|
|
that._toggle(false);
|
|
footer.hide();
|
|
return;
|
|
}
|
|
if (!footer[0]) {
|
|
footer = $('<div class="k-footer"><a href="#" class="k-link k-nav-today"></a></div>').appendTo(element);
|
|
}
|
|
that._today = footer.show().find('.k-link').html(template(today)).attr('title', kendo.toString(today, 'D', that.options.culture));
|
|
that._toggle();
|
|
},
|
|
_header: function () {
|
|
var that = this, element = that.element, links;
|
|
if (!element.find('.k-header')[0]) {
|
|
element.html('<div class="k-header">' + '<a href="#" role="button" class="k-link k-nav-prev"><span class="k-icon k-i-arrow-w"></span></a>' + '<a href="#" role="button" aria-live="assertive" aria-atomic="true" class="k-link k-nav-fast"></a>' + '<a href="#" role="button" class="k-link k-nav-next"><span class="k-icon k-i-arrow-e"></span></a>' + '</div>');
|
|
}
|
|
links = element.find('.k-link').on(MOUSEENTER_WITH_NS + ' ' + MOUSELEAVE + ' ' + FOCUS_WITH_NS + ' ' + BLUR, mousetoggle).click(false);
|
|
that._title = links.eq(1).on(CLICK, function () {
|
|
that._active = that.options.focusOnNav !== false;
|
|
that.navigateUp();
|
|
});
|
|
that[PREVARROW] = links.eq(0).on(CLICK, function () {
|
|
that._active = that.options.focusOnNav !== false;
|
|
that.navigateToPast();
|
|
});
|
|
that[NEXTARROW] = links.eq(2).on(CLICK, function () {
|
|
that._active = that.options.focusOnNav !== false;
|
|
that.navigateToFuture();
|
|
});
|
|
},
|
|
_navigate: function (arrow, modifier) {
|
|
var that = this, index = that._index + 1, currentValue = new DATE(+that._current);
|
|
arrow = that[arrow];
|
|
if (!arrow.hasClass(DISABLED)) {
|
|
if (index > 3) {
|
|
currentValue.setFullYear(currentValue.getFullYear() + 100 * modifier);
|
|
} else {
|
|
calendar.views[index].setDate(currentValue, modifier);
|
|
}
|
|
that.navigate(currentValue);
|
|
}
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this, options = that.options, currentValue = that._value || that._current, isBigger;
|
|
if (value === undefined) {
|
|
return options[option];
|
|
}
|
|
value = parse(value, options.format, options.culture);
|
|
if (!value) {
|
|
return;
|
|
}
|
|
options[option] = new DATE(+value);
|
|
if (option === MIN) {
|
|
isBigger = value > currentValue;
|
|
} else {
|
|
isBigger = currentValue > value;
|
|
}
|
|
if (isBigger || isEqualMonth(currentValue, value)) {
|
|
if (isBigger) {
|
|
that._value = null;
|
|
}
|
|
that._changeView = true;
|
|
}
|
|
if (!that._changeView) {
|
|
that._changeView = !!(options.month.content || options.month.empty);
|
|
}
|
|
that.navigate(that._value);
|
|
that._toggle();
|
|
},
|
|
_toggle: function (toggle) {
|
|
var that = this, options = that.options, isTodayDisabled = that.options.disableDates(getToday()), link = that._today;
|
|
if (toggle === undefined) {
|
|
toggle = isInRange(getToday(), options.min, options.max);
|
|
}
|
|
if (link) {
|
|
link.off(CLICK);
|
|
if (toggle && !isTodayDisabled) {
|
|
link.addClass(TODAY).removeClass(DISABLED).on(CLICK, proxy(that._todayClick, that));
|
|
} else {
|
|
link.removeClass(TODAY).addClass(DISABLED).on(CLICK, prevent);
|
|
}
|
|
}
|
|
},
|
|
_todayClick: function (e) {
|
|
var that = this, depth = views[that.options.depth], disabled = that.options.disableDates, today = getToday();
|
|
e.preventDefault();
|
|
if (disabled(today)) {
|
|
return;
|
|
}
|
|
if (that._view.compare(that._current, today) === 0 && that._index == depth) {
|
|
that._changeView = false;
|
|
}
|
|
that._value = today;
|
|
that.navigate(today, depth);
|
|
that.trigger(CHANGE);
|
|
},
|
|
_toDateObject: function (link) {
|
|
var value = $(link).attr(kendo.attr(VALUE)).split('/');
|
|
value = new DATE(value[0], value[1], value[2]);
|
|
return value;
|
|
},
|
|
_templates: function () {
|
|
var that = this, options = that.options, footer = options.footer, month = options.month, content = month.content, empty = month.empty;
|
|
that.month = {
|
|
content: template('<td#=data.cssClass# role="gridcell"><a tabindex="-1" class="k-link#=data.linkClass#" href="#=data.url#" ' + kendo.attr('value') + '="#=data.dateString#" title="#=data.title#">' + (content || '#=data.value#') + '</a></td>', { useWithBlock: !!content }),
|
|
empty: template('<td role="gridcell">' + (empty || ' ') + '</td>', { useWithBlock: !!empty })
|
|
};
|
|
that.footer = footer !== false ? template(footer || '#= kendo.toString(data,"D","' + options.culture + '") #', { useWithBlock: false }) : null;
|
|
}
|
|
});
|
|
ui.plugin(Calendar);
|
|
var calendar = {
|
|
firstDayOfMonth: function (date) {
|
|
return new DATE(date.getFullYear(), date.getMonth(), 1);
|
|
},
|
|
firstVisibleDay: function (date, calendarInfo) {
|
|
calendarInfo = calendarInfo || kendo.culture().calendar;
|
|
var firstDay = calendarInfo.firstDay, firstVisibleDay = new DATE(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
while (firstVisibleDay.getDay() != firstDay) {
|
|
calendar.setTime(firstVisibleDay, -1 * MS_PER_DAY);
|
|
}
|
|
return firstVisibleDay;
|
|
},
|
|
setTime: function (date, time) {
|
|
var tzOffsetBefore = date.getTimezoneOffset(), resultDATE = new DATE(date.getTime() + time), tzOffsetDiff = resultDATE.getTimezoneOffset() - tzOffsetBefore;
|
|
date.setTime(resultDATE.getTime() + tzOffsetDiff * MS_PER_MINUTE);
|
|
},
|
|
views: [
|
|
{
|
|
name: MONTH,
|
|
title: function (date, min, max, culture) {
|
|
return getCalendarInfo(culture).months.names[date.getMonth()] + ' ' + date.getFullYear();
|
|
},
|
|
content: function (options) {
|
|
var that = this, idx = 0, min = options.min, max = options.max, date = options.date, dates = options.dates, format = options.format, culture = options.culture, navigateUrl = options.url, hasUrl = navigateUrl && dates[0], currentCalendar = getCalendarInfo(culture), firstDayIdx = currentCalendar.firstDay, days = currentCalendar.days, names = shiftArray(days.names, firstDayIdx), shortNames = shiftArray(days.namesShort, firstDayIdx), start = calendar.firstVisibleDay(date, currentCalendar), firstDayOfMonth = that.first(date), lastDayOfMonth = that.last(date), toDateString = that.toDateString, today = new DATE(), html = '<table tabindex="0" role="grid" class="k-content" cellspacing="0" data-start="' + toDateString(start) + '"><thead><tr role="row">';
|
|
for (; idx < 7; idx++) {
|
|
html += '<th scope="col" title="' + names[idx] + '">' + shortNames[idx] + '</th>';
|
|
}
|
|
today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());
|
|
adjustDST(today, 0);
|
|
today = +today;
|
|
return view({
|
|
cells: 42,
|
|
perRow: 7,
|
|
html: html += '</tr></thead><tbody><tr role="row">',
|
|
start: start,
|
|
min: new DATE(min.getFullYear(), min.getMonth(), min.getDate()),
|
|
max: new DATE(max.getFullYear(), max.getMonth(), max.getDate()),
|
|
content: options.content,
|
|
empty: options.empty,
|
|
setter: that.setDate,
|
|
disableDates: options.disableDates,
|
|
build: function (date, idx, disableDates) {
|
|
var cssClass = [], day = date.getDay(), linkClass = '', url = '#';
|
|
if (date < firstDayOfMonth || date > lastDayOfMonth) {
|
|
cssClass.push(OTHERMONTH);
|
|
}
|
|
if (disableDates(date)) {
|
|
cssClass.push(DISABLED);
|
|
}
|
|
if (+date === today) {
|
|
cssClass.push('k-today');
|
|
}
|
|
if (day === 0 || day === 6) {
|
|
cssClass.push('k-weekend');
|
|
}
|
|
if (hasUrl && inArray(+date, dates)) {
|
|
url = navigateUrl.replace('{0}', kendo.toString(date, format, culture));
|
|
linkClass = ' k-action-link';
|
|
}
|
|
return {
|
|
date: date,
|
|
dates: dates,
|
|
ns: kendo.ns,
|
|
title: kendo.toString(date, 'D', culture),
|
|
value: date.getDate(),
|
|
dateString: toDateString(date),
|
|
cssClass: cssClass[0] ? ' class="' + cssClass.join(' ') + '"' : '',
|
|
linkClass: linkClass,
|
|
url: url
|
|
};
|
|
}
|
|
});
|
|
},
|
|
first: function (date) {
|
|
return calendar.firstDayOfMonth(date);
|
|
},
|
|
last: function (date) {
|
|
var last = new DATE(date.getFullYear(), date.getMonth() + 1, 0), first = calendar.firstDayOfMonth(date), timeOffset = Math.abs(last.getTimezoneOffset() - first.getTimezoneOffset());
|
|
if (timeOffset) {
|
|
last.setHours(first.getHours() + timeOffset / 60);
|
|
}
|
|
return last;
|
|
},
|
|
compare: function (date1, date2) {
|
|
var result, month1 = date1.getMonth(), year1 = date1.getFullYear(), month2 = date2.getMonth(), year2 = date2.getFullYear();
|
|
if (year1 > year2) {
|
|
result = 1;
|
|
} else if (year1 < year2) {
|
|
result = -1;
|
|
} else {
|
|
result = month1 == month2 ? 0 : month1 > month2 ? 1 : -1;
|
|
}
|
|
return result;
|
|
},
|
|
setDate: function (date, value) {
|
|
var hours = date.getHours();
|
|
if (value instanceof DATE) {
|
|
date.setFullYear(value.getFullYear(), value.getMonth(), value.getDate());
|
|
} else {
|
|
calendar.setTime(date, value * MS_PER_DAY);
|
|
}
|
|
adjustDST(date, hours);
|
|
},
|
|
toDateString: function (date) {
|
|
return date.getFullYear() + '/' + date.getMonth() + '/' + date.getDate();
|
|
}
|
|
},
|
|
{
|
|
name: 'year',
|
|
title: function (date) {
|
|
return date.getFullYear();
|
|
},
|
|
content: function (options) {
|
|
var namesAbbr = getCalendarInfo(options.culture).months.namesAbbr, toDateString = this.toDateString, min = options.min, max = options.max;
|
|
return view({
|
|
min: new DATE(min.getFullYear(), min.getMonth(), 1),
|
|
max: new DATE(max.getFullYear(), max.getMonth(), 1),
|
|
start: new DATE(options.date.getFullYear(), 0, 1),
|
|
setter: this.setDate,
|
|
build: function (date) {
|
|
return {
|
|
value: namesAbbr[date.getMonth()],
|
|
ns: kendo.ns,
|
|
dateString: toDateString(date),
|
|
cssClass: ''
|
|
};
|
|
}
|
|
});
|
|
},
|
|
first: function (date) {
|
|
return new DATE(date.getFullYear(), 0, date.getDate());
|
|
},
|
|
last: function (date) {
|
|
return new DATE(date.getFullYear(), 11, date.getDate());
|
|
},
|
|
compare: function (date1, date2) {
|
|
return compare(date1, date2);
|
|
},
|
|
setDate: function (date, value) {
|
|
var month, hours = date.getHours();
|
|
if (value instanceof DATE) {
|
|
month = value.getMonth();
|
|
date.setFullYear(value.getFullYear(), month, date.getDate());
|
|
if (month !== date.getMonth()) {
|
|
date.setDate(0);
|
|
}
|
|
} else {
|
|
month = date.getMonth() + value;
|
|
date.setMonth(month);
|
|
if (month > 11) {
|
|
month -= 12;
|
|
}
|
|
if (month > 0 && date.getMonth() != month) {
|
|
date.setDate(0);
|
|
}
|
|
}
|
|
adjustDST(date, hours);
|
|
},
|
|
toDateString: function (date) {
|
|
return date.getFullYear() + '/' + date.getMonth() + '/1';
|
|
}
|
|
},
|
|
{
|
|
name: 'decade',
|
|
title: function (date, min, max) {
|
|
return title(date, min, max, 10);
|
|
},
|
|
content: function (options) {
|
|
var year = options.date.getFullYear(), toDateString = this.toDateString;
|
|
return view({
|
|
start: new DATE(year - year % 10 - 1, 0, 1),
|
|
min: new DATE(options.min.getFullYear(), 0, 1),
|
|
max: new DATE(options.max.getFullYear(), 0, 1),
|
|
setter: this.setDate,
|
|
build: function (date, idx) {
|
|
return {
|
|
value: date.getFullYear(),
|
|
ns: kendo.ns,
|
|
dateString: toDateString(date),
|
|
cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : ''
|
|
};
|
|
}
|
|
});
|
|
},
|
|
first: function (date) {
|
|
var year = date.getFullYear();
|
|
return new DATE(year - year % 10, date.getMonth(), date.getDate());
|
|
},
|
|
last: function (date) {
|
|
var year = date.getFullYear();
|
|
return new DATE(year - year % 10 + 9, date.getMonth(), date.getDate());
|
|
},
|
|
compare: function (date1, date2) {
|
|
return compare(date1, date2, 10);
|
|
},
|
|
setDate: function (date, value) {
|
|
setDate(date, value, 1);
|
|
},
|
|
toDateString: function (date) {
|
|
return date.getFullYear() + '/0/1';
|
|
}
|
|
},
|
|
{
|
|
name: CENTURY,
|
|
title: function (date, min, max) {
|
|
return title(date, min, max, 100);
|
|
},
|
|
content: function (options) {
|
|
var year = options.date.getFullYear(), min = options.min.getFullYear(), max = options.max.getFullYear(), toDateString = this.toDateString, minYear = min, maxYear = max;
|
|
minYear = minYear - minYear % 10;
|
|
maxYear = maxYear - maxYear % 10;
|
|
if (maxYear - minYear < 10) {
|
|
maxYear = minYear + 9;
|
|
}
|
|
return view({
|
|
start: new DATE(year - year % 100 - 10, 0, 1),
|
|
min: new DATE(minYear, 0, 1),
|
|
max: new DATE(maxYear, 0, 1),
|
|
setter: this.setDate,
|
|
build: function (date, idx) {
|
|
var start = date.getFullYear(), end = start + 9;
|
|
if (start < min) {
|
|
start = min;
|
|
}
|
|
if (end > max) {
|
|
end = max;
|
|
}
|
|
return {
|
|
ns: kendo.ns,
|
|
value: start + ' - ' + end,
|
|
dateString: toDateString(date),
|
|
cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : ''
|
|
};
|
|
}
|
|
});
|
|
},
|
|
first: function (date) {
|
|
var year = date.getFullYear();
|
|
return new DATE(year - year % 100, date.getMonth(), date.getDate());
|
|
},
|
|
last: function (date) {
|
|
var year = date.getFullYear();
|
|
return new DATE(year - year % 100 + 99, date.getMonth(), date.getDate());
|
|
},
|
|
compare: function (date1, date2) {
|
|
return compare(date1, date2, 100);
|
|
},
|
|
setDate: function (date, value) {
|
|
setDate(date, value, 10);
|
|
},
|
|
toDateString: function (date) {
|
|
var year = date.getFullYear();
|
|
return year - year % 10 + '/0/1';
|
|
}
|
|
}
|
|
]
|
|
};
|
|
function title(date, min, max, modular) {
|
|
var start = date.getFullYear(), minYear = min.getFullYear(), maxYear = max.getFullYear(), end;
|
|
start = start - start % modular;
|
|
end = start + (modular - 1);
|
|
if (start < minYear) {
|
|
start = minYear;
|
|
}
|
|
if (end > maxYear) {
|
|
end = maxYear;
|
|
}
|
|
return start + '-' + end;
|
|
}
|
|
function view(options) {
|
|
var idx = 0, data, min = options.min, max = options.max, start = options.start, setter = options.setter, build = options.build, length = options.cells || 12, cellsPerRow = options.perRow || 4, content = options.content || cellTemplate, empty = options.empty || emptyCellTemplate, html = options.html || '<table tabindex="0" role="grid" class="k-content k-meta-view" cellspacing="0"><tbody><tr role="row">';
|
|
for (; idx < length; idx++) {
|
|
if (idx > 0 && idx % cellsPerRow === 0) {
|
|
html += '</tr><tr role="row">';
|
|
}
|
|
start = new DATE(start.getFullYear(), start.getMonth(), start.getDate(), 0, 0, 0);
|
|
adjustDST(start, 0);
|
|
data = build(start, idx, options.disableDates);
|
|
html += isInRange(start, min, max) ? content(data) : empty(data);
|
|
setter(start, 1);
|
|
}
|
|
return html + '</tr></tbody></table>';
|
|
}
|
|
function compare(date1, date2, modifier) {
|
|
var year1 = date1.getFullYear(), start = date2.getFullYear(), end = start, result = 0;
|
|
if (modifier) {
|
|
start = start - start % modifier;
|
|
end = start - start % modifier + modifier - 1;
|
|
}
|
|
if (year1 > end) {
|
|
result = 1;
|
|
} else if (year1 < start) {
|
|
result = -1;
|
|
}
|
|
return result;
|
|
}
|
|
function getToday() {
|
|
var today = new DATE();
|
|
return new DATE(today.getFullYear(), today.getMonth(), today.getDate());
|
|
}
|
|
function restrictValue(value, min, max) {
|
|
var today = getToday();
|
|
if (value) {
|
|
today = new DATE(+value);
|
|
}
|
|
if (min > today) {
|
|
today = new DATE(+min);
|
|
} else if (max < today) {
|
|
today = new DATE(+max);
|
|
}
|
|
return today;
|
|
}
|
|
function isInRange(date, min, max) {
|
|
return +date >= +min && +date <= +max;
|
|
}
|
|
function shiftArray(array, idx) {
|
|
return array.slice(idx).concat(array.slice(0, idx));
|
|
}
|
|
function setDate(date, value, multiplier) {
|
|
value = value instanceof DATE ? value.getFullYear() : date.getFullYear() + multiplier * value;
|
|
date.setFullYear(value);
|
|
}
|
|
function mousetoggle(e) {
|
|
var disabled = $(this).hasClass('k-state-disabled');
|
|
if (!disabled) {
|
|
$(this).toggleClass(HOVER, MOUSEENTER.indexOf(e.type) > -1 || e.type == FOCUS);
|
|
}
|
|
}
|
|
function prevent(e) {
|
|
e.preventDefault();
|
|
}
|
|
function getCalendarInfo(culture) {
|
|
return getCulture(culture).calendars.standard;
|
|
}
|
|
function normalize(options) {
|
|
var start = views[options.start], depth = views[options.depth], culture = getCulture(options.culture);
|
|
options.format = extractFormat(options.format || culture.calendars.standard.patterns.d);
|
|
if (isNaN(start)) {
|
|
start = 0;
|
|
options.start = MONTH;
|
|
}
|
|
if (depth === undefined || depth > start) {
|
|
options.depth = MONTH;
|
|
}
|
|
if (!options.dates) {
|
|
options.dates = [];
|
|
}
|
|
}
|
|
function makeUnselectable(element) {
|
|
if (isIE8) {
|
|
element.find('*').attr('unselectable', 'on');
|
|
}
|
|
}
|
|
function inArray(date, dates) {
|
|
for (var i = 0, length = dates.length; i < length; i++) {
|
|
if (date === +dates[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function isEqualDatePart(value1, value2) {
|
|
if (value1) {
|
|
return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth() && value1.getDate() === value2.getDate();
|
|
}
|
|
return false;
|
|
}
|
|
function isEqualMonth(value1, value2) {
|
|
if (value1) {
|
|
return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth();
|
|
}
|
|
return false;
|
|
}
|
|
function getDisabledExpr(option) {
|
|
if (kendo.isFunction(option)) {
|
|
return option;
|
|
}
|
|
if ($.isArray(option)) {
|
|
return createDisabledExpr(option);
|
|
}
|
|
return $.noop;
|
|
}
|
|
function convertDatesArray(dates) {
|
|
var result = [];
|
|
for (var i = 0; i < dates.length; i++) {
|
|
result.push(dates[i].setHours(0, 0, 0, 0));
|
|
}
|
|
return result;
|
|
}
|
|
function createDisabledExpr(dates) {
|
|
var body, callback, disabledDates = [], days = [
|
|
'su',
|
|
'mo',
|
|
'tu',
|
|
'we',
|
|
'th',
|
|
'fr',
|
|
'sa'
|
|
], searchExpression = 'if (found) {' + ' return true ' + '} else {' + 'return false' + '}';
|
|
if (dates[0] instanceof DATE) {
|
|
disabledDates = convertDatesArray(dates);
|
|
body = 'var found = date && $.inArray(date.setHours(0, 0, 0, 0),[' + disabledDates + ']) > -1;' + searchExpression;
|
|
} else {
|
|
for (var i = 0; i < dates.length; i++) {
|
|
var day = dates[i].slice(0, 2).toLowerCase();
|
|
var index = $.inArray(day, days);
|
|
if (index > -1) {
|
|
disabledDates.push(index);
|
|
}
|
|
}
|
|
body = 'var found = date && $.inArray(date.getDay(),[' + disabledDates + ']) > -1;' + searchExpression;
|
|
}
|
|
callback = new Function('date', body);
|
|
return callback;
|
|
}
|
|
function isEqualDate(oldValue, newValue) {
|
|
if (oldValue instanceof Date && newValue instanceof Date) {
|
|
oldValue = oldValue.getTime();
|
|
newValue = newValue.getTime();
|
|
}
|
|
return oldValue === newValue;
|
|
}
|
|
calendar.isEqualDatePart = isEqualDatePart;
|
|
calendar.makeUnselectable = makeUnselectable;
|
|
calendar.restrictValue = restrictValue;
|
|
calendar.isInRange = isInRange;
|
|
calendar.normalize = normalize;
|
|
calendar.viewsEnum = views;
|
|
calendar.disabled = getDisabledExpr;
|
|
kendo.calendar = calendar;
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.datepicker', [
|
|
'kendo.calendar',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'datepicker',
|
|
name: 'DatePicker',
|
|
category: 'web',
|
|
description: 'The DatePicker widget allows the user to select a date from a calendar or by direct input.',
|
|
depends: [
|
|
'calendar',
|
|
'popup'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, parse = kendo.parseDate, keys = kendo.keys, template = kendo.template, activeElement = kendo._activeElement, DIV = '<div />', SPAN = '<span />', ns = '.kendoDatePicker', CLICK = 'click' + ns, OPEN = 'open', CLOSE = 'close', CHANGE = 'change', DISABLED = 'disabled', READONLY = 'readonly', DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', HOVER = 'k-state-hover', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, MOUSEDOWN = 'mousedown' + ns, ID = 'id', MIN = 'min', MAX = 'max', MONTH = 'month', ARIA_DISABLED = 'aria-disabled', ARIA_EXPANDED = 'aria-expanded', ARIA_HIDDEN = 'aria-hidden', ARIA_READONLY = 'aria-readonly', calendar = kendo.calendar, isInRange = calendar.isInRange, restrictValue = calendar.restrictValue, isEqualDatePart = calendar.isEqualDatePart, extend = $.extend, proxy = $.proxy, DATE = Date;
|
|
function normalize(options) {
|
|
var parseFormats = options.parseFormats, format = options.format;
|
|
calendar.normalize(options);
|
|
parseFormats = $.isArray(parseFormats) ? parseFormats : [parseFormats];
|
|
if (!parseFormats.length) {
|
|
parseFormats.push('yyyy-MM-dd');
|
|
}
|
|
if ($.inArray(format, parseFormats) === -1) {
|
|
parseFormats.splice(0, 0, options.format);
|
|
}
|
|
options.parseFormats = parseFormats;
|
|
}
|
|
function preventDefault(e) {
|
|
e.preventDefault();
|
|
}
|
|
var DateView = function (options) {
|
|
var that = this, id, body = document.body, div = $(DIV).attr(ARIA_HIDDEN, 'true').addClass('k-calendar-container').appendTo(body);
|
|
that.options = options = options || {};
|
|
id = options.id;
|
|
if (id) {
|
|
id += '_dateview';
|
|
div.attr(ID, id);
|
|
that._dateViewID = id;
|
|
}
|
|
that.popup = new ui.Popup(div, extend(options.popup, options, {
|
|
name: 'Popup',
|
|
isRtl: kendo.support.isRtl(options.anchor)
|
|
}));
|
|
that.div = div;
|
|
that.value(options.value);
|
|
};
|
|
DateView.prototype = {
|
|
_calendar: function () {
|
|
var that = this;
|
|
var calendar = that.calendar;
|
|
var options = that.options;
|
|
var div;
|
|
if (!calendar) {
|
|
div = $(DIV).attr(ID, kendo.guid()).appendTo(that.popup.element).on(MOUSEDOWN, preventDefault).on(CLICK, 'td:has(.k-link)', proxy(that._click, that));
|
|
that.calendar = calendar = new ui.Calendar(div);
|
|
that._setOptions(options);
|
|
kendo.calendar.makeUnselectable(calendar.element);
|
|
calendar.navigate(that._value || that._current, options.start);
|
|
that.value(that._value);
|
|
}
|
|
},
|
|
_setOptions: function (options) {
|
|
this.calendar.setOptions({
|
|
focusOnNav: false,
|
|
change: options.change,
|
|
culture: options.culture,
|
|
dates: options.dates,
|
|
depth: options.depth,
|
|
footer: options.footer,
|
|
format: options.format,
|
|
max: options.max,
|
|
min: options.min,
|
|
month: options.month,
|
|
start: options.start,
|
|
disableDates: options.disableDates
|
|
});
|
|
},
|
|
setOptions: function (options) {
|
|
var old = this.options;
|
|
var disableDates = options.disableDates;
|
|
if (disableDates) {
|
|
options.disableDates = calendar.disabled(disableDates);
|
|
}
|
|
this.options = extend(old, options, {
|
|
change: old.change,
|
|
close: old.close,
|
|
open: old.open
|
|
});
|
|
if (this.calendar) {
|
|
this._setOptions(this.options);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this.popup.destroy();
|
|
},
|
|
open: function () {
|
|
var that = this;
|
|
that._calendar();
|
|
that.popup.open();
|
|
},
|
|
close: function () {
|
|
this.popup.close();
|
|
},
|
|
min: function (value) {
|
|
this._option(MIN, value);
|
|
},
|
|
max: function (value) {
|
|
this._option(MAX, value);
|
|
},
|
|
toggle: function () {
|
|
var that = this;
|
|
that[that.popup.visible() ? CLOSE : OPEN]();
|
|
},
|
|
move: function (e) {
|
|
var that = this, key = e.keyCode, calendar = that.calendar, selectIsClicked = e.ctrlKey && key == keys.DOWN || key == keys.ENTER, handled = false;
|
|
if (e.altKey) {
|
|
if (key == keys.DOWN) {
|
|
that.open();
|
|
e.preventDefault();
|
|
handled = true;
|
|
} else if (key == keys.UP) {
|
|
that.close();
|
|
e.preventDefault();
|
|
handled = true;
|
|
}
|
|
} else if (that.popup.visible()) {
|
|
if (key == keys.ESC || selectIsClicked && calendar._cell.hasClass(SELECTED)) {
|
|
that.close();
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
that._current = calendar._move(e);
|
|
handled = true;
|
|
}
|
|
return handled;
|
|
},
|
|
current: function (date) {
|
|
this._current = date;
|
|
this.calendar._focus(date);
|
|
},
|
|
value: function (value) {
|
|
var that = this, calendar = that.calendar, options = that.options, disabledDate = options.disableDates;
|
|
if (disabledDate && disabledDate(value)) {
|
|
value = null;
|
|
}
|
|
that._value = value;
|
|
that._current = new DATE(+restrictValue(value, options.min, options.max));
|
|
if (calendar) {
|
|
calendar.value(value);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
if (e.currentTarget.className.indexOf(SELECTED) !== -1) {
|
|
this.close();
|
|
}
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this;
|
|
var calendar = that.calendar;
|
|
that.options[option] = value;
|
|
if (calendar) {
|
|
calendar[option](value);
|
|
}
|
|
}
|
|
};
|
|
DateView.normalize = normalize;
|
|
kendo.DateView = DateView;
|
|
var DatePicker = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, disabled, div;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
options.disableDates = kendo.calendar.disabled(options.disableDates);
|
|
options.min = parse(element.attr('min')) || parse(options.min);
|
|
options.max = parse(element.attr('max')) || parse(options.max);
|
|
normalize(options);
|
|
that._initialOptions = extend({}, options);
|
|
that._wrapper();
|
|
that.dateView = new DateView(extend({}, options, {
|
|
id: element.attr(ID),
|
|
anchor: that.wrapper,
|
|
change: function () {
|
|
that._change(this.value());
|
|
that.close();
|
|
},
|
|
close: function (e) {
|
|
if (that.trigger(CLOSE)) {
|
|
e.preventDefault();
|
|
} else {
|
|
element.attr(ARIA_EXPANDED, false);
|
|
div.attr(ARIA_HIDDEN, true);
|
|
}
|
|
},
|
|
open: function (e) {
|
|
var options = that.options, date;
|
|
if (that.trigger(OPEN)) {
|
|
e.preventDefault();
|
|
} else {
|
|
if (that.element.val() !== that._oldText) {
|
|
date = parse(element.val(), options.parseFormats, options.culture);
|
|
that.dateView[date ? 'current' : 'value'](date);
|
|
}
|
|
element.attr(ARIA_EXPANDED, true);
|
|
div.attr(ARIA_HIDDEN, false);
|
|
that._updateARIA(date);
|
|
}
|
|
}
|
|
}));
|
|
div = that.dateView.div;
|
|
that._icon();
|
|
try {
|
|
element[0].setAttribute('type', 'text');
|
|
} catch (e) {
|
|
element[0].type = 'text';
|
|
}
|
|
element.addClass('k-input').attr({
|
|
role: 'combobox',
|
|
'aria-expanded': false,
|
|
'aria-owns': that.dateView._dateViewID
|
|
});
|
|
that._reset();
|
|
that._template();
|
|
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(element.is('[readonly]'));
|
|
}
|
|
that._old = that._update(options.value || that.element.val());
|
|
that._oldText = element.val();
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
CHANGE
|
|
],
|
|
options: {
|
|
name: 'DatePicker',
|
|
value: null,
|
|
footer: '',
|
|
format: '',
|
|
culture: '',
|
|
parseFormats: [],
|
|
min: new Date(1900, 0, 1),
|
|
max: new Date(2099, 11, 31),
|
|
start: MONTH,
|
|
depth: MONTH,
|
|
animation: {},
|
|
month: {},
|
|
dates: [],
|
|
ARIATemplate: 'Current focused date is #=kendo.toString(data.current, "D")#'
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
var value = that._value;
|
|
Widget.fn.setOptions.call(that, options);
|
|
options = that.options;
|
|
options.min = parse(options.min);
|
|
options.max = parse(options.max);
|
|
normalize(options);
|
|
that.dateView.setOptions(options);
|
|
if (value) {
|
|
that.element.val(kendo.toString(value, options.format, options.culture));
|
|
that._updateARIA(value);
|
|
}
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, icon = that._dateIcon.off(ns), element = that.element.off(ns), wrapper = that._inputWrapper.off(ns), readonly = options.readonly, disable = options.disable;
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
element.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, proxy(that._keydown, that)).on('focusout' + ns, proxy(that._blur, that)).on('focus' + ns, function () {
|
|
that._inputWrapper.addClass(FOCUSED);
|
|
});
|
|
icon.on(CLICK, proxy(that._click, that)).on(MOUSEDOWN, preventDefault);
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.dateView.destroy();
|
|
that.element.off(ns);
|
|
that._dateIcon.off(ns);
|
|
that._inputWrapper.off(ns);
|
|
if (that._form) {
|
|
that._form.off('reset', that._resetHandler);
|
|
}
|
|
},
|
|
open: function () {
|
|
this.dateView.open();
|
|
},
|
|
close: function () {
|
|
this.dateView.close();
|
|
},
|
|
min: function (value) {
|
|
return this._option(MIN, value);
|
|
},
|
|
max: function (value) {
|
|
return this._option(MAX, value);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
that._old = that._update(value);
|
|
if (that._old === null) {
|
|
that.element.val('');
|
|
}
|
|
that._oldText = that.element.val();
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_blur: function () {
|
|
var that = this, value = that.element.val();
|
|
that.close();
|
|
if (value !== that._oldText) {
|
|
that._change(value);
|
|
}
|
|
that._inputWrapper.removeClass(FOCUSED);
|
|
},
|
|
_click: function () {
|
|
var that = this, element = that.element;
|
|
that.dateView.toggle();
|
|
if (!kendo.support.touch && element[0] !== activeElement()) {
|
|
element.focus();
|
|
}
|
|
},
|
|
_change: function (value) {
|
|
var that = this, oldValue = that.element.val(), dateChanged;
|
|
value = that._update(value);
|
|
dateChanged = +that._old != +value;
|
|
var valueUpdated = dateChanged && !that._typing;
|
|
var textFormatted = oldValue !== that.element.val();
|
|
if (valueUpdated || textFormatted) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
if (dateChanged) {
|
|
that._old = value;
|
|
that._oldText = that.element.val();
|
|
that.trigger(CHANGE);
|
|
}
|
|
that._typing = false;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, dateView = that.dateView, value = that.element.val(), handled = false;
|
|
if (!dateView.popup.visible() && e.keyCode == keys.ENTER && value !== that._oldText) {
|
|
that._change(value);
|
|
} else {
|
|
handled = dateView.move(e);
|
|
that._updateARIA(dateView._current);
|
|
if (!handled) {
|
|
that._typing = true;
|
|
}
|
|
}
|
|
},
|
|
_icon: function () {
|
|
var that = this, element = that.element, icon;
|
|
icon = element.next('span.k-select');
|
|
if (!icon[0]) {
|
|
icon = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-calendar">select</span></span>').insertAfter(element);
|
|
}
|
|
that._dateIcon = icon.attr({
|
|
'role': 'button',
|
|
'aria-controls': that.dateView._dateViewID
|
|
});
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this, options = that.options;
|
|
if (value === undefined) {
|
|
return options[option];
|
|
}
|
|
value = parse(value, options.parseFormats, options.culture);
|
|
if (!value) {
|
|
return;
|
|
}
|
|
options[option] = new DATE(+value);
|
|
that.dateView[option](value);
|
|
},
|
|
_update: function (value) {
|
|
var that = this, options = that.options, min = options.min, max = options.max, current = that._value, date = parse(value, options.parseFormats, options.culture), isSameType = date === null && current === null || date instanceof Date && current instanceof Date, formattedValue;
|
|
if (options.disableDates(date)) {
|
|
date = null;
|
|
if (!that._old) {
|
|
value = null;
|
|
}
|
|
}
|
|
if (+date === +current && isSameType) {
|
|
formattedValue = kendo.toString(date, options.format, options.culture);
|
|
if (formattedValue !== value) {
|
|
that.element.val(date === null ? value : formattedValue);
|
|
}
|
|
return date;
|
|
}
|
|
if (date !== null && isEqualDatePart(date, min)) {
|
|
date = restrictValue(date, min, max);
|
|
} else if (!isInRange(date, min, max)) {
|
|
date = null;
|
|
}
|
|
that._value = date;
|
|
that.dateView.value(date);
|
|
that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
|
|
that._updateARIA(date);
|
|
return date;
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper;
|
|
wrapper = element.parents('.k-datepicker');
|
|
if (!wrapper[0]) {
|
|
wrapper = element.wrap(SPAN).parent().addClass('k-picker-wrap k-state-default');
|
|
wrapper = wrapper.wrap(SPAN).parent();
|
|
}
|
|
wrapper[0].style.cssText = element[0].style.cssText;
|
|
element.css({
|
|
width: '100%',
|
|
height: element[0].style.height
|
|
});
|
|
that.wrapper = wrapper.addClass('k-widget k-datepicker k-header').addClass(element[0].className);
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
that.value(element[0].defaultValue);
|
|
that.max(that._initialOptions.max);
|
|
that.min(that._initialOptions.min);
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
},
|
|
_template: function () {
|
|
this._ariaTemplate = template(this.options.ARIATemplate);
|
|
},
|
|
_updateARIA: function (date) {
|
|
var cell;
|
|
var that = this;
|
|
var calendar = that.dateView.calendar;
|
|
that.element.removeAttr('aria-activedescendant');
|
|
if (calendar) {
|
|
cell = calendar._cell;
|
|
cell.attr('aria-label', that._ariaTemplate({ current: date || calendar.current() }));
|
|
that.element.attr('aria-activedescendant', cell.attr('id'));
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(DatePicker);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.autocomplete', [
|
|
'kendo.list',
|
|
'kendo.mobile.scroller'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'autocomplete',
|
|
name: 'AutoComplete',
|
|
category: 'web',
|
|
description: 'The AutoComplete widget provides suggestions depending on the typed text.It also allows multiple value entries.',
|
|
depends: ['list'],
|
|
features: [
|
|
{
|
|
id: 'mobile-scroller',
|
|
name: 'Mobile scroller',
|
|
description: 'Support for kinetic scrolling in mobile device',
|
|
depends: ['mobile.scroller']
|
|
},
|
|
{
|
|
id: 'virtualization',
|
|
name: 'VirtualList',
|
|
description: 'Support for virtualization',
|
|
depends: ['virtuallist']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, support = kendo.support, caret = kendo.caret, activeElement = kendo._activeElement, placeholderSupported = support.placeholder, ui = kendo.ui, List = ui.List, keys = kendo.keys, DataSource = kendo.data.DataSource, ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', CHANGE = 'change', DEFAULT = 'k-state-default', DISABLED = 'disabled', READONLY = 'readonly', FOCUSED = 'k-state-focused', SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', HOVER = 'k-state-hover', ns = '.kendoAutoComplete', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, proxy = $.proxy;
|
|
function indexOfWordAtCaret(caretIdx, text, separator) {
|
|
return separator ? text.substring(0, caretIdx).split(separator).length - 1 : 0;
|
|
}
|
|
function wordAtCaret(caretIdx, text, separator) {
|
|
return text.split(separator)[indexOfWordAtCaret(caretIdx, text, separator)];
|
|
}
|
|
function replaceWordAtCaret(caretIdx, text, word, separator) {
|
|
var words = text.split(separator);
|
|
words.splice(indexOfWordAtCaret(caretIdx, text, separator), 1, word);
|
|
if (separator && words[words.length - 1] !== '') {
|
|
words.push('');
|
|
}
|
|
return words.join(separator);
|
|
}
|
|
var AutoComplete = List.extend({
|
|
init: function (element, options) {
|
|
var that = this, wrapper, disabled;
|
|
that.ns = ns;
|
|
options = $.isArray(options) ? { dataSource: options } : options;
|
|
List.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
options.placeholder = options.placeholder || element.attr('placeholder');
|
|
if (placeholderSupported) {
|
|
element.attr('placeholder', options.placeholder);
|
|
}
|
|
that._wrapper();
|
|
that._loader();
|
|
that._dataSource();
|
|
that._ignoreCase();
|
|
element[0].type = 'text';
|
|
wrapper = that.wrapper;
|
|
that._popup();
|
|
element.addClass('k-input').on('keydown' + ns, proxy(that._keydown, that)).on('keypress' + ns, proxy(that._keypress, that)).on('paste' + ns, proxy(that._search, that)).on('focus' + ns, function () {
|
|
that._prev = that._accessor();
|
|
that._oldText = that._prev;
|
|
that._placeholder(false);
|
|
wrapper.addClass(FOCUSED);
|
|
}).on('focusout' + ns, function () {
|
|
that._change();
|
|
that._placeholder();
|
|
wrapper.removeClass(FOCUSED);
|
|
}).attr({
|
|
autocomplete: 'off',
|
|
role: 'textbox',
|
|
'aria-haspopup': true
|
|
});
|
|
that._enable();
|
|
that._old = that._accessor();
|
|
if (element[0].id) {
|
|
element.attr('aria-owns', that.ul[0].id);
|
|
}
|
|
that._aria();
|
|
that._placeholder();
|
|
that._initList();
|
|
disabled = $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
}
|
|
that.listView.bind('click', function (e) {
|
|
e.preventDefault();
|
|
});
|
|
that._resetFocusItemHandler = $.proxy(that._resetFocusItem, that);
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'AutoComplete',
|
|
enabled: true,
|
|
suggest: false,
|
|
template: '',
|
|
groupTemplate: '#:data#',
|
|
fixedGroupTemplate: '#:data#',
|
|
dataTextField: '',
|
|
minLength: 1,
|
|
delay: 200,
|
|
height: 200,
|
|
filter: 'startswith',
|
|
ignoreCase: true,
|
|
highlightFirst: false,
|
|
separator: null,
|
|
placeholder: '',
|
|
animation: {},
|
|
virtual: false,
|
|
value: null
|
|
},
|
|
_dataSource: function () {
|
|
var that = this;
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that._unbindDataSource();
|
|
} else {
|
|
that._progressHandler = proxy(that._showBusy, that);
|
|
that._errorHandler = proxy(that._hideBusy, that);
|
|
}
|
|
that.dataSource = DataSource.create(that.options.dataSource).bind('progress', that._progressHandler).bind('error', that._errorHandler);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
this.listView.setDataSource(this.dataSource);
|
|
},
|
|
events: [
|
|
'open',
|
|
'close',
|
|
CHANGE,
|
|
'select',
|
|
'filtering',
|
|
'dataBinding',
|
|
'dataBound'
|
|
],
|
|
setOptions: function (options) {
|
|
var listOptions = this._listOptions(options);
|
|
List.fn.setOptions.call(this, options);
|
|
this.listView.setOptions(listOptions);
|
|
this._accessors();
|
|
this._aria();
|
|
},
|
|
_listOptions: function (options) {
|
|
var listOptions = List.fn._listOptions.call(this, $.extend(options, { skipUpdateOnBind: true }));
|
|
listOptions.dataValueField = listOptions.dataTextField;
|
|
listOptions.selectedItemChange = null;
|
|
return listOptions;
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, element = that.element, wrapper = that.wrapper.off(ns), readonly = options.readonly, disable = options.disable;
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
element.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
close: function () {
|
|
var that = this;
|
|
var current = that.listView.focus();
|
|
if (current) {
|
|
current.removeClass(SELECTED);
|
|
}
|
|
that.popup.close();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.element.off(ns);
|
|
that.wrapper.off(ns);
|
|
List.fn.destroy.call(that);
|
|
},
|
|
refresh: function () {
|
|
this.listView.refresh();
|
|
},
|
|
select: function (li) {
|
|
this._select(li);
|
|
},
|
|
search: function (word) {
|
|
var that = this, options = that.options, ignoreCase = options.ignoreCase, separator = options.separator, length;
|
|
word = word || that._accessor();
|
|
clearTimeout(that._typingTimeout);
|
|
if (separator) {
|
|
word = wordAtCaret(caret(that.element)[0], word, separator);
|
|
}
|
|
length = word.length;
|
|
if (!length || length >= options.minLength) {
|
|
that._open = true;
|
|
that._mute(function () {
|
|
this.listView.value([]);
|
|
});
|
|
that._filterSource({
|
|
value: ignoreCase ? word.toLowerCase() : word,
|
|
operator: options.filter,
|
|
field: options.dataTextField,
|
|
ignoreCase: ignoreCase
|
|
});
|
|
}
|
|
},
|
|
suggest: function (word) {
|
|
var that = this, key = that._last, value = that._accessor(), element = that.element[0], caretIdx = caret(element)[0], separator = that.options.separator, words = value.split(separator), wordIndex = indexOfWordAtCaret(caretIdx, value, separator), selectionEnd = caretIdx, idx;
|
|
if (key == keys.BACKSPACE || key == keys.DELETE) {
|
|
that._last = undefined;
|
|
return;
|
|
}
|
|
word = word || '';
|
|
if (typeof word !== 'string') {
|
|
if (word[0]) {
|
|
word = that.dataSource.view()[List.inArray(word[0], that.ul[0])];
|
|
}
|
|
word = word ? that._text(word) : '';
|
|
}
|
|
if (caretIdx <= 0) {
|
|
caretIdx = value.toLowerCase().indexOf(word.toLowerCase()) + 1;
|
|
}
|
|
idx = value.substring(0, caretIdx).lastIndexOf(separator);
|
|
idx = idx > -1 ? caretIdx - (idx + separator.length) : caretIdx;
|
|
value = words[wordIndex].substring(0, idx);
|
|
if (word) {
|
|
word = word.toString();
|
|
idx = word.toLowerCase().indexOf(value.toLowerCase());
|
|
if (idx > -1) {
|
|
word = word.substring(idx + value.length);
|
|
selectionEnd = caretIdx + word.length;
|
|
value += word;
|
|
}
|
|
if (separator && words[words.length - 1] !== '') {
|
|
words.push('');
|
|
}
|
|
}
|
|
words[wordIndex] = value;
|
|
that._accessor(words.join(separator || ''));
|
|
if (element === activeElement()) {
|
|
caret(element, caretIdx, selectionEnd);
|
|
}
|
|
},
|
|
value: function (value) {
|
|
if (value !== undefined) {
|
|
this.listView.value(value);
|
|
this._accessor(value);
|
|
this._old = this._accessor();
|
|
this._oldText = this._accessor();
|
|
} else {
|
|
return this._accessor();
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var item = e.item;
|
|
var element = this.element;
|
|
e.preventDefault();
|
|
this._active = true;
|
|
if (this.trigger('select', { item: item })) {
|
|
this.close();
|
|
return;
|
|
}
|
|
this._oldText = element.val();
|
|
this._select(item);
|
|
this._blur();
|
|
caret(element, element.val().length);
|
|
},
|
|
_resetFocusItem: function () {
|
|
var index = this.options.highlightFirst ? 0 : -1;
|
|
if (this.options.virtual) {
|
|
this.listView.scrollTo(0);
|
|
}
|
|
this.listView.focus(index);
|
|
},
|
|
_listBound: function () {
|
|
var that = this;
|
|
var popup = that.popup;
|
|
var options = that.options;
|
|
var data = that.dataSource.flatView();
|
|
var length = data.length;
|
|
var isActive = that.element[0] === activeElement();
|
|
var action;
|
|
that._angularItems('compile');
|
|
that._resizePopup();
|
|
popup.position();
|
|
if (length) {
|
|
if (options.suggest && isActive) {
|
|
that.suggest(data[0]);
|
|
}
|
|
}
|
|
if (that._open) {
|
|
that._open = false;
|
|
action = length ? 'open' : 'close';
|
|
if (that._typingTimeout && !isActive) {
|
|
action = 'close';
|
|
}
|
|
if (length) {
|
|
that._resetFocusItem();
|
|
if (options.virtual) {
|
|
that.popup.unbind('activate', that._resetFocusItemHandler).one('activate', that._resetFocusItemHandler);
|
|
}
|
|
}
|
|
popup[action]();
|
|
that._typingTimeout = undefined;
|
|
}
|
|
if (that._touchScroller) {
|
|
that._touchScroller.reset();
|
|
}
|
|
that._hideBusy();
|
|
that._makeUnselectable();
|
|
that.trigger('dataBound');
|
|
},
|
|
_mute: function (callback) {
|
|
this._muted = true;
|
|
callback.call(this);
|
|
this._muted = false;
|
|
},
|
|
_listChange: function () {
|
|
var isActive = this._active || this.element[0] === activeElement();
|
|
if (isActive && !this._muted) {
|
|
this._selectValue(this.listView.selectedDataItems()[0]);
|
|
}
|
|
},
|
|
_selectValue: function (dataItem) {
|
|
var separator = this.options.separator;
|
|
var text = '';
|
|
if (dataItem) {
|
|
text = this._text(dataItem);
|
|
}
|
|
if (text === null) {
|
|
text = '';
|
|
}
|
|
if (separator) {
|
|
text = replaceWordAtCaret(caret(this.element)[0], this._accessor(), text, separator);
|
|
}
|
|
this._prev = text;
|
|
this._accessor(text);
|
|
this._placeholder();
|
|
},
|
|
_change: function () {
|
|
var that = this;
|
|
var value = that.value();
|
|
var trigger = value !== List.unifyType(that._old, typeof value);
|
|
var valueUpdated = trigger && !that._typing;
|
|
var itemSelected = that._oldText !== value;
|
|
if (valueUpdated || itemSelected) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
if (trigger) {
|
|
that._old = value;
|
|
that.trigger(CHANGE);
|
|
}
|
|
that.typing = false;
|
|
},
|
|
_accessor: function (value) {
|
|
var that = this, element = that.element[0];
|
|
if (value !== undefined) {
|
|
element.value = value === null ? '' : value;
|
|
that._placeholder();
|
|
} else {
|
|
value = element.value;
|
|
if (element.className.indexOf('k-readonly') > -1) {
|
|
if (value === that.options.placeholder) {
|
|
return '';
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
var key = e.keyCode;
|
|
var visible = that.popup.visible();
|
|
var current = this.listView.focus();
|
|
that._last = key;
|
|
if (key === keys.DOWN) {
|
|
if (visible) {
|
|
this._move(current ? 'focusNext' : 'focusFirst');
|
|
}
|
|
e.preventDefault();
|
|
} else if (key === keys.UP) {
|
|
if (visible) {
|
|
this._move(current ? 'focusPrev' : 'focusLast');
|
|
}
|
|
e.preventDefault();
|
|
} else if (key === keys.ENTER || key === keys.TAB) {
|
|
if (key === keys.ENTER && visible) {
|
|
e.preventDefault();
|
|
}
|
|
if (visible && current) {
|
|
if (that.trigger('select', { item: current })) {
|
|
return;
|
|
}
|
|
this._select(current);
|
|
}
|
|
this._blur();
|
|
} else if (key === keys.ESC) {
|
|
if (visible) {
|
|
e.preventDefault();
|
|
}
|
|
that.close();
|
|
} else {
|
|
that._search();
|
|
}
|
|
},
|
|
_keypress: function () {
|
|
this._oldText = this.element.val();
|
|
this._typing = true;
|
|
},
|
|
_move: function (action) {
|
|
this.listView[action]();
|
|
if (this.options.suggest) {
|
|
this.suggest(this.listView.focus());
|
|
}
|
|
},
|
|
_hideBusy: function () {
|
|
var that = this;
|
|
clearTimeout(that._busy);
|
|
that._loading.hide();
|
|
that.element.attr('aria-busy', false);
|
|
that._busy = null;
|
|
},
|
|
_showBusy: function () {
|
|
var that = this;
|
|
if (that._busy) {
|
|
return;
|
|
}
|
|
that._busy = setTimeout(function () {
|
|
that.element.attr('aria-busy', true);
|
|
that._loading.show();
|
|
}, 100);
|
|
},
|
|
_placeholder: function (show) {
|
|
if (placeholderSupported) {
|
|
return;
|
|
}
|
|
var that = this, element = that.element, placeholder = that.options.placeholder, value;
|
|
if (placeholder) {
|
|
value = element.val();
|
|
if (show === undefined) {
|
|
show = !value;
|
|
}
|
|
if (!show) {
|
|
if (value !== placeholder) {
|
|
placeholder = value;
|
|
} else {
|
|
placeholder = '';
|
|
}
|
|
}
|
|
if (value === that._old && !show) {
|
|
return;
|
|
}
|
|
element.toggleClass('k-readonly', show).val(placeholder);
|
|
if (!placeholder && element[0] === document.activeElement) {
|
|
caret(element[0], 0, 0);
|
|
}
|
|
}
|
|
},
|
|
_search: function () {
|
|
var that = this;
|
|
clearTimeout(that._typingTimeout);
|
|
that._typingTimeout = setTimeout(function () {
|
|
if (that._prev !== that._accessor()) {
|
|
that._prev = that._accessor();
|
|
that.search();
|
|
}
|
|
}, that.options.delay);
|
|
},
|
|
_select: function (candidate) {
|
|
this._active = true;
|
|
this.listView.select(candidate);
|
|
this._active = false;
|
|
},
|
|
_loader: function () {
|
|
this._loading = $('<span class="k-icon k-loading" style="display:none"></span>').insertAfter(this.element);
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, DOMelement = element[0], wrapper;
|
|
wrapper = element.parent();
|
|
if (!wrapper.is('span.k-widget')) {
|
|
wrapper = element.wrap('<span />').parent();
|
|
}
|
|
wrapper.attr('tabindex', -1);
|
|
wrapper.attr('role', 'presentation');
|
|
wrapper[0].style.cssText = DOMelement.style.cssText;
|
|
element.css({
|
|
width: '100%',
|
|
height: DOMelement.style.height
|
|
});
|
|
that._focused = that.element;
|
|
that.wrapper = wrapper.addClass('k-widget k-autocomplete k-header').addClass(DOMelement.className);
|
|
}
|
|
});
|
|
ui.plugin(AutoComplete);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dropdownlist', [
|
|
'kendo.list',
|
|
'kendo.mobile.scroller'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dropdownlist',
|
|
name: 'DropDownList',
|
|
category: 'web',
|
|
description: 'The DropDownList widget displays a list of values and allows the selection of a single value from the list.',
|
|
depends: ['list'],
|
|
features: [
|
|
{
|
|
id: 'mobile-scroller',
|
|
name: 'Mobile scroller',
|
|
description: 'Support for kinetic scrolling in mobile device',
|
|
depends: ['mobile.scroller']
|
|
},
|
|
{
|
|
id: 'virtualization',
|
|
name: 'VirtualList',
|
|
description: 'Support for virtualization',
|
|
depends: ['virtuallist']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, List = ui.List, Select = ui.Select, support = kendo.support, activeElement = kendo._activeElement, ObservableObject = kendo.data.ObservableObject, keys = kendo.keys, ns = '.kendoDropDownList', DISABLED = 'disabled', READONLY = 'readonly', CHANGE = 'change', FOCUSED = 'k-state-focused', DEFAULT = 'k-state-default', STATEDISABLED = 'k-state-disabled', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, TABINDEX = 'tabindex', STATE_FILTER = 'filter', STATE_ACCEPT = 'accept', MSG_INVALID_OPTION_LABEL = 'The `optionLabel` option is not valid due to missing fields. Define a custom optionLabel as shown here http://docs.telerik.com/kendo-ui/api/javascript/ui/dropdownlist#configuration-optionLabel', proxy = $.proxy;
|
|
var DropDownList = Select.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
var index = options && options.index;
|
|
var optionLabel, text, disabled;
|
|
that.ns = ns;
|
|
options = $.isArray(options) ? { dataSource: options } : options;
|
|
Select.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element.on('focus' + ns, proxy(that._focusHandler, that));
|
|
that._focusInputHandler = $.proxy(that._focusInput, that);
|
|
that.optionLabel = $();
|
|
that._optionLabel();
|
|
that._inputTemplate();
|
|
that._reset();
|
|
that._prev = '';
|
|
that._word = '';
|
|
that._wrapper();
|
|
that._tabindex();
|
|
that.wrapper.data(TABINDEX, that.wrapper.attr(TABINDEX));
|
|
that._span();
|
|
that._popup();
|
|
that._mobile();
|
|
that._dataSource();
|
|
that._ignoreCase();
|
|
that._filterHeader();
|
|
that._aria();
|
|
that._enable();
|
|
that._oldIndex = that.selectedIndex = -1;
|
|
if (index !== undefined) {
|
|
options.index = index;
|
|
}
|
|
that._initialIndex = options.index;
|
|
that._initList();
|
|
that._cascade();
|
|
if (options.autoBind) {
|
|
that.dataSource.fetch();
|
|
} else if (that.selectedIndex === -1) {
|
|
text = options.text || '';
|
|
if (!text) {
|
|
optionLabel = options.optionLabel;
|
|
if (optionLabel && options.index === 0) {
|
|
text = optionLabel;
|
|
} else if (that._isSelect) {
|
|
text = element.children(':selected').text();
|
|
}
|
|
}
|
|
that._textAccessor(text);
|
|
}
|
|
disabled = $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
}
|
|
that.listView.bind('click', function (e) {
|
|
e.preventDefault();
|
|
});
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'DropDownList',
|
|
enabled: true,
|
|
autoBind: true,
|
|
index: 0,
|
|
text: null,
|
|
value: null,
|
|
delay: 500,
|
|
height: 200,
|
|
dataTextField: '',
|
|
dataValueField: '',
|
|
optionLabel: '',
|
|
cascadeFrom: '',
|
|
cascadeFromField: '',
|
|
ignoreCase: true,
|
|
animation: {},
|
|
filter: 'none',
|
|
minLength: 1,
|
|
virtual: false,
|
|
template: null,
|
|
valueTemplate: null,
|
|
optionLabelTemplate: null,
|
|
groupTemplate: '#:data#',
|
|
fixedGroupTemplate: '#:data#'
|
|
},
|
|
events: [
|
|
'open',
|
|
'close',
|
|
CHANGE,
|
|
'select',
|
|
'filtering',
|
|
'dataBinding',
|
|
'dataBound',
|
|
'cascade',
|
|
'set'
|
|
],
|
|
setOptions: function (options) {
|
|
Select.fn.setOptions.call(this, options);
|
|
this.listView.setOptions(this._listOptions(options));
|
|
this._optionLabel();
|
|
this._inputTemplate();
|
|
this._accessors();
|
|
this._filterHeader();
|
|
this._enable();
|
|
this._aria();
|
|
if (!this.value() && this.hasOptionLabel()) {
|
|
this.select(0);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Select.fn.destroy.call(that);
|
|
that.wrapper.off(ns);
|
|
that.element.off(ns);
|
|
that._inputWrapper.off(ns);
|
|
that._arrow.off();
|
|
that._arrow = null;
|
|
that.optionLabel.off();
|
|
},
|
|
open: function () {
|
|
var that = this;
|
|
if (that.popup.visible()) {
|
|
return;
|
|
}
|
|
if (!that.listView.bound() || that._state === STATE_ACCEPT) {
|
|
that._open = true;
|
|
that._state = 'rebind';
|
|
if (that.filterInput) {
|
|
that.filterInput.val('');
|
|
that._prev = '';
|
|
}
|
|
that._filterSource();
|
|
} else if (that._allowOpening()) {
|
|
that.popup.one('activate', that._focusInputHandler);
|
|
that.popup.open();
|
|
that._focusItem();
|
|
}
|
|
},
|
|
_focusInput: function () {
|
|
this._focusElement(this.filterInput);
|
|
},
|
|
_allowOpening: function () {
|
|
return this.hasOptionLabel() || this.filterInput || this.dataSource.view().length;
|
|
},
|
|
toggle: function (toggle) {
|
|
this._toggle(toggle, true);
|
|
},
|
|
current: function (candidate) {
|
|
var current;
|
|
if (candidate === undefined) {
|
|
current = this.listView.focus();
|
|
if (!current && this.selectedIndex === 0 && this.hasOptionLabel()) {
|
|
return this.optionLabel;
|
|
}
|
|
return current;
|
|
}
|
|
this._focus(candidate);
|
|
},
|
|
dataItem: function (index) {
|
|
var that = this;
|
|
var dataItem = null;
|
|
if (index === null) {
|
|
return index;
|
|
}
|
|
if (index === undefined) {
|
|
dataItem = that.listView.selectedDataItems()[0];
|
|
} else {
|
|
if (typeof index !== 'number') {
|
|
if (that.options.virtual) {
|
|
return that.dataSource.getByUid($(index).data('uid'));
|
|
}
|
|
if (index.hasClass('k-list-optionlabel')) {
|
|
index = -1;
|
|
} else {
|
|
index = $(that.items()).index(index);
|
|
}
|
|
} else if (that.hasOptionLabel()) {
|
|
index -= 1;
|
|
}
|
|
dataItem = that.dataSource.flatView()[index];
|
|
}
|
|
if (!dataItem) {
|
|
dataItem = that._optionLabelDataItem();
|
|
}
|
|
return dataItem;
|
|
},
|
|
refresh: function () {
|
|
this.listView.refresh();
|
|
},
|
|
text: function (text) {
|
|
var that = this;
|
|
var dataItem, loweredText;
|
|
var ignoreCase = that.options.ignoreCase;
|
|
text = text === null ? '' : text;
|
|
if (text !== undefined) {
|
|
if (typeof text === 'string') {
|
|
loweredText = ignoreCase ? text.toLowerCase() : text;
|
|
that._select(function (data) {
|
|
data = that._text(data);
|
|
if (ignoreCase) {
|
|
data = (data + '').toLowerCase();
|
|
}
|
|
return data === loweredText;
|
|
});
|
|
dataItem = that.dataItem();
|
|
if (dataItem) {
|
|
text = dataItem;
|
|
}
|
|
}
|
|
that._textAccessor(text);
|
|
} else {
|
|
return that._textAccessor();
|
|
}
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var listView = that.listView;
|
|
var dataSource = that.dataSource;
|
|
if (value === undefined) {
|
|
value = that._accessor() || that.listView.value()[0];
|
|
return value === undefined || value === null ? '' : value;
|
|
}
|
|
if (value || !that.hasOptionLabel()) {
|
|
that._initialIndex = null;
|
|
}
|
|
this.trigger('set', { value: value });
|
|
if (that._request && that.options.cascadeFrom && that.listView.bound()) {
|
|
if (that._valueSetter) {
|
|
dataSource.unbind(CHANGE, that._valueSetter);
|
|
}
|
|
that._valueSetter = proxy(function () {
|
|
that.value(value);
|
|
}, that);
|
|
dataSource.one(CHANGE, that._valueSetter);
|
|
return;
|
|
}
|
|
if (that._isFilterEnabled() && listView.bound() && listView.isFiltered()) {
|
|
that._clearFilter();
|
|
} else {
|
|
that._fetchData();
|
|
}
|
|
listView.value(value).done(function () {
|
|
if (that.selectedIndex === -1 && that.text()) {
|
|
that.text('');
|
|
that._accessor('', -1);
|
|
}
|
|
that._old = that._accessor();
|
|
that._oldIndex = that.selectedIndex;
|
|
});
|
|
},
|
|
hasOptionLabel: function () {
|
|
return this.optionLabel && !!this.optionLabel[0];
|
|
},
|
|
_optionLabel: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var optionLabel = options.optionLabel;
|
|
var template = options.optionLabelTemplate;
|
|
if (!optionLabel) {
|
|
that.optionLabel.off().remove();
|
|
that.optionLabel = $();
|
|
return;
|
|
}
|
|
if (!template) {
|
|
template = '#:';
|
|
if (typeof optionLabel === 'string') {
|
|
template += 'data';
|
|
} else {
|
|
template += kendo.expr(options.dataTextField, 'data');
|
|
}
|
|
template += '#';
|
|
}
|
|
if (typeof template !== 'function') {
|
|
template = kendo.template(template);
|
|
}
|
|
that.optionLabelTemplate = template;
|
|
if (!that.hasOptionLabel()) {
|
|
that.optionLabel = $('<div class="k-list-optionlabel"></div>').prependTo(that.list);
|
|
}
|
|
that.optionLabel.html(template(optionLabel)).off().click(proxy(that._click, that)).on(HOVEREVENTS, that._toggleHover);
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: that.optionLabel,
|
|
data: [{ dataItem: that._optionLabelDataItem() }]
|
|
};
|
|
});
|
|
},
|
|
_optionLabelText: function () {
|
|
var optionLabel = this.options.optionLabel;
|
|
return typeof optionLabel === 'string' ? optionLabel : this._text(optionLabel);
|
|
},
|
|
_optionLabelDataItem: function () {
|
|
var that = this;
|
|
var optionLabel = that.options.optionLabel;
|
|
if (that.hasOptionLabel()) {
|
|
return $.isPlainObject(optionLabel) ? new ObservableObject(optionLabel) : that._assignInstance(that._optionLabelText(), '');
|
|
}
|
|
return null;
|
|
},
|
|
_buildOptions: function (data) {
|
|
var that = this;
|
|
if (!that._isSelect) {
|
|
return;
|
|
}
|
|
var value = that.listView.value()[0];
|
|
var optionLabel = that._optionLabelDataItem();
|
|
if (value === undefined || value === null) {
|
|
value = '';
|
|
}
|
|
if (optionLabel) {
|
|
optionLabel = '<option value="' + that._value(optionLabel) + '">' + that._text(optionLabel) + '</option>';
|
|
}
|
|
that._options(data, optionLabel, value);
|
|
if (value !== List.unifyType(that._accessor(), typeof value)) {
|
|
that._customOption = null;
|
|
that._custom(value);
|
|
}
|
|
},
|
|
_listBound: function () {
|
|
var that = this;
|
|
var initialIndex = that._initialIndex;
|
|
var filtered = that._state === STATE_FILTER;
|
|
var data = that.dataSource.flatView();
|
|
var dataItem;
|
|
that._angularItems('compile');
|
|
that._presetValue = false;
|
|
that._resizePopup(true);
|
|
that.popup.position();
|
|
that._buildOptions(data);
|
|
that._makeUnselectable();
|
|
if (!filtered) {
|
|
if (that._open) {
|
|
that.toggle(that._allowOpening());
|
|
}
|
|
that._open = false;
|
|
if (!that._fetch) {
|
|
if (data.length) {
|
|
if (!that.listView.value().length && initialIndex > -1 && initialIndex !== null) {
|
|
that.select(initialIndex);
|
|
}
|
|
that._initialIndex = null;
|
|
dataItem = that.listView.selectedDataItems()[0];
|
|
if (dataItem && that.text() !== that._text(dataItem)) {
|
|
that._selectValue(dataItem);
|
|
}
|
|
} else if (that._textAccessor() !== that._optionLabelText()) {
|
|
that.listView.value('');
|
|
that._selectValue(null);
|
|
that._oldIndex = that.selectedIndex;
|
|
}
|
|
}
|
|
}
|
|
that._hideBusy();
|
|
that.trigger('dataBound');
|
|
},
|
|
_listChange: function () {
|
|
this._selectValue(this.listView.selectedDataItems()[0]);
|
|
if (this._presetValue || this._old && this._oldIndex === -1) {
|
|
this._oldIndex = this.selectedIndex;
|
|
}
|
|
},
|
|
_focusHandler: function () {
|
|
this.wrapper.focus();
|
|
},
|
|
_focusinHandler: function () {
|
|
this._inputWrapper.addClass(FOCUSED);
|
|
this._prevent = false;
|
|
},
|
|
_focusoutHandler: function () {
|
|
var that = this;
|
|
var filtered = that._state === STATE_FILTER;
|
|
var isIFrame = window.self !== window.top;
|
|
var focusedItem = that._focus();
|
|
if (!that._prevent) {
|
|
clearTimeout(that._typingTimeout);
|
|
if (filtered && focusedItem && !that.trigger('select', { item: focusedItem })) {
|
|
that._select(focusedItem, !that.dataSource.view().length);
|
|
}
|
|
if (support.mobileOS.ios && isIFrame) {
|
|
that._change();
|
|
} else {
|
|
that._blur();
|
|
}
|
|
that._inputWrapper.removeClass(FOCUSED);
|
|
that._prevent = true;
|
|
that._open = false;
|
|
that.element.blur();
|
|
}
|
|
},
|
|
_wrapperMousedown: function () {
|
|
this._prevent = !!this.filterInput;
|
|
},
|
|
_wrapperClick: function (e) {
|
|
e.preventDefault();
|
|
this.popup.unbind('activate', this._focusInputHandler);
|
|
this._focused = this.wrapper;
|
|
this._toggle();
|
|
},
|
|
_editable: function (options) {
|
|
var that = this;
|
|
var element = that.element;
|
|
var disable = options.disable;
|
|
var readonly = options.readonly;
|
|
var wrapper = that.wrapper.add(that.filterInput).off(ns);
|
|
var dropDownWrapper = that._inputWrapper.off(HOVEREVENTS);
|
|
if (!readonly && !disable) {
|
|
element.removeAttr(DISABLED).removeAttr(READONLY);
|
|
dropDownWrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
wrapper.attr(TABINDEX, wrapper.data(TABINDEX)).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, proxy(that._keydown, that)).on('focusin' + ns, proxy(that._focusinHandler, that)).on('focusout' + ns, proxy(that._focusoutHandler, that)).on('mousedown' + ns, proxy(that._wrapperMousedown, that));
|
|
that.wrapper.on('click' + ns, proxy(that._wrapperClick, that));
|
|
if (!that.filterInput) {
|
|
wrapper.on('keypress' + ns, proxy(that._keypress, that));
|
|
}
|
|
} else if (disable) {
|
|
wrapper.removeAttr(TABINDEX);
|
|
dropDownWrapper.addClass(STATEDISABLED).removeClass(DEFAULT);
|
|
} else {
|
|
dropDownWrapper.addClass(DEFAULT).removeClass(STATEDISABLED);
|
|
wrapper.on('focusin' + ns, proxy(that._focusinHandler, that)).on('focusout' + ns, proxy(that._focusoutHandler, that));
|
|
}
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly);
|
|
wrapper.attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
var key = e.keyCode;
|
|
var altKey = e.altKey;
|
|
var isInputActive;
|
|
var handled;
|
|
var isPopupVisible = that.popup.visible();
|
|
if (that.filterInput) {
|
|
isInputActive = that.filterInput[0] === activeElement();
|
|
}
|
|
if (key === keys.LEFT) {
|
|
key = keys.UP;
|
|
handled = true;
|
|
} else if (key === keys.RIGHT) {
|
|
key = keys.DOWN;
|
|
handled = true;
|
|
}
|
|
if (handled && isInputActive) {
|
|
return;
|
|
}
|
|
e.keyCode = key;
|
|
if (altKey && key === keys.UP || key === keys.ESC) {
|
|
that._focusElement(that.wrapper);
|
|
}
|
|
if (key === keys.ENTER && that._typingTimeout && that.filterInput && isPopupVisible) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
handled = that._move(e);
|
|
if (handled) {
|
|
return;
|
|
}
|
|
if (!isPopupVisible || !that.filterInput) {
|
|
if (key === keys.HOME) {
|
|
handled = true;
|
|
that._firstItem();
|
|
} else if (key === keys.END) {
|
|
handled = true;
|
|
that._lastItem();
|
|
}
|
|
if (handled) {
|
|
that._select(that._focus());
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
if (!altKey && !handled && that.filterInput) {
|
|
that._search();
|
|
}
|
|
},
|
|
_matchText: function (text, word) {
|
|
var ignoreCase = this.options.ignoreCase;
|
|
if (text === undefined || text === null) {
|
|
return false;
|
|
}
|
|
text = text + '';
|
|
if (ignoreCase) {
|
|
text = text.toLowerCase();
|
|
}
|
|
return text.indexOf(word) === 0;
|
|
},
|
|
_shuffleData: function (data, splitIndex) {
|
|
var optionDataItem = this._optionLabelDataItem();
|
|
if (optionDataItem) {
|
|
data = [optionDataItem].concat(data);
|
|
}
|
|
return data.slice(splitIndex).concat(data.slice(0, splitIndex));
|
|
},
|
|
_selectNext: function () {
|
|
var that = this;
|
|
var data = that.dataSource.flatView();
|
|
var dataLength = data.length + (that.hasOptionLabel() ? 1 : 0);
|
|
var isInLoop = sameCharsOnly(that._word, that._last);
|
|
var startIndex = that.selectedIndex;
|
|
var oldFocusedItem;
|
|
var text;
|
|
if (startIndex === -1) {
|
|
startIndex = 0;
|
|
} else {
|
|
startIndex += isInLoop ? 1 : 0;
|
|
startIndex = normalizeIndex(startIndex, dataLength);
|
|
}
|
|
data = data.toJSON ? data.toJSON() : data.slice();
|
|
data = that._shuffleData(data, startIndex);
|
|
for (var idx = 0; idx < dataLength; idx++) {
|
|
text = that._text(data[idx]);
|
|
if (isInLoop && that._matchText(text, that._last)) {
|
|
break;
|
|
} else if (that._matchText(text, that._word)) {
|
|
break;
|
|
}
|
|
}
|
|
if (idx !== dataLength) {
|
|
oldFocusedItem = that._focus();
|
|
that._select(normalizeIndex(startIndex + idx, dataLength));
|
|
if (that.trigger('select', { item: that._focus() })) {
|
|
that._select(oldFocusedItem);
|
|
}
|
|
if (!that.popup.visible()) {
|
|
that._change();
|
|
}
|
|
}
|
|
},
|
|
_keypress: function (e) {
|
|
var that = this;
|
|
if (e.which === 0 || e.keyCode === kendo.keys.ENTER) {
|
|
return;
|
|
}
|
|
var character = String.fromCharCode(e.charCode || e.keyCode);
|
|
if (that.options.ignoreCase) {
|
|
character = character.toLowerCase();
|
|
}
|
|
if (character === ' ') {
|
|
e.preventDefault();
|
|
}
|
|
that._word += character;
|
|
that._last = character;
|
|
that._search();
|
|
},
|
|
_popupOpen: function () {
|
|
var popup = this.popup;
|
|
popup.wrapper = kendo.wrap(popup.element);
|
|
if (popup.element.closest('.km-root')[0]) {
|
|
popup.wrapper.addClass('km-popup km-widget');
|
|
this.wrapper.addClass('km-widget');
|
|
}
|
|
},
|
|
_popup: function () {
|
|
Select.fn._popup.call(this);
|
|
this.popup.one('open', proxy(this._popupOpen, this));
|
|
},
|
|
_click: function (e) {
|
|
var item = e.item || $(e.currentTarget);
|
|
e.preventDefault();
|
|
if (this.trigger('select', { item: item })) {
|
|
this.close();
|
|
return;
|
|
}
|
|
this._userTriggered = true;
|
|
this._select(item);
|
|
this._focusElement(this.wrapper);
|
|
this._blur();
|
|
},
|
|
_focusElement: function (element) {
|
|
var active = activeElement();
|
|
var wrapper = this.wrapper;
|
|
var filterInput = this.filterInput;
|
|
var compareElement = element === filterInput ? wrapper : filterInput;
|
|
var touchEnabled = support.mobileOS && (support.touch || support.MSPointers || support.pointers);
|
|
if (filterInput && filterInput[0] === element[0] && touchEnabled) {
|
|
return;
|
|
}
|
|
if (filterInput && compareElement[0] === active) {
|
|
this._prevent = true;
|
|
this._focused = element.focus();
|
|
}
|
|
},
|
|
_filter: function (word) {
|
|
if (word) {
|
|
var that = this;
|
|
var ignoreCase = that.options.ignoreCase;
|
|
if (ignoreCase) {
|
|
word = word.toLowerCase();
|
|
}
|
|
that._select(function (dataItem) {
|
|
return that._matchText(that._text(dataItem), word);
|
|
});
|
|
}
|
|
},
|
|
_search: function () {
|
|
var that = this;
|
|
var dataSource = that.dataSource;
|
|
clearTimeout(that._typingTimeout);
|
|
if (that._isFilterEnabled()) {
|
|
that._typingTimeout = setTimeout(function () {
|
|
var value = that.filterInput.val();
|
|
if (that._prev !== value) {
|
|
that._prev = value;
|
|
that.search(value);
|
|
}
|
|
that._typingTimeout = null;
|
|
}, that.options.delay);
|
|
} else {
|
|
that._typingTimeout = setTimeout(function () {
|
|
that._word = '';
|
|
}, that.options.delay);
|
|
if (!that.listView.bound()) {
|
|
dataSource.fetch().done(function () {
|
|
that._selectNext();
|
|
});
|
|
return;
|
|
}
|
|
that._selectNext();
|
|
}
|
|
},
|
|
_get: function (candidate) {
|
|
var data, found, idx;
|
|
var isFunction = typeof candidate === 'function';
|
|
var jQueryCandidate = !isFunction ? $(candidate) : $();
|
|
if (this.hasOptionLabel()) {
|
|
if (typeof candidate === 'number') {
|
|
if (candidate > -1) {
|
|
candidate -= 1;
|
|
}
|
|
} else if (jQueryCandidate.hasClass('k-list-optionlabel')) {
|
|
candidate = -1;
|
|
}
|
|
}
|
|
if (isFunction) {
|
|
data = this.dataSource.flatView();
|
|
for (idx = 0; idx < data.length; idx++) {
|
|
if (candidate(data[idx])) {
|
|
candidate = idx;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
candidate = -1;
|
|
}
|
|
}
|
|
return candidate;
|
|
},
|
|
_firstItem: function () {
|
|
if (this.hasOptionLabel()) {
|
|
this._focus(this.optionLabel);
|
|
} else {
|
|
this.listView.focusFirst();
|
|
}
|
|
},
|
|
_lastItem: function () {
|
|
this._resetOptionLabel();
|
|
this.listView.focusLast();
|
|
},
|
|
_nextItem: function () {
|
|
if (this.optionLabel.hasClass('k-state-focused')) {
|
|
this._resetOptionLabel();
|
|
this.listView.focusFirst();
|
|
} else {
|
|
this.listView.focusNext();
|
|
}
|
|
},
|
|
_prevItem: function () {
|
|
if (this.optionLabel.hasClass('k-state-focused')) {
|
|
return;
|
|
}
|
|
this.listView.focusPrev();
|
|
if (!this.listView.focus()) {
|
|
this._focus(this.optionLabel);
|
|
}
|
|
},
|
|
_focusItem: function () {
|
|
var listView = this.listView;
|
|
var focusedItem = listView.focus();
|
|
var index = listView.select();
|
|
index = index[index.length - 1];
|
|
if (index === undefined && this.options.highlightFirst && !focusedItem) {
|
|
index = 0;
|
|
}
|
|
if (index !== undefined) {
|
|
listView.focus(index);
|
|
} else {
|
|
if (this.options.optionLabel) {
|
|
this._focus(this.optionLabel);
|
|
this._select(this.optionLabel);
|
|
} else {
|
|
listView.scrollToIndex(0);
|
|
}
|
|
}
|
|
},
|
|
_resetOptionLabel: function (additionalClass) {
|
|
this.optionLabel.removeClass('k-state-focused' + (additionalClass || '')).removeAttr('id');
|
|
},
|
|
_focus: function (candidate) {
|
|
var listView = this.listView;
|
|
var optionLabel = this.optionLabel;
|
|
if (candidate === undefined) {
|
|
candidate = listView.focus();
|
|
if (!candidate && optionLabel.hasClass('k-state-focused')) {
|
|
candidate = optionLabel;
|
|
}
|
|
return candidate;
|
|
}
|
|
this._resetOptionLabel();
|
|
candidate = this._get(candidate);
|
|
listView.focus(candidate);
|
|
if (candidate === -1) {
|
|
optionLabel.addClass('k-state-focused').attr('id', listView._optionID);
|
|
this._focused.add(this.filterInput).removeAttr('aria-activedescendant').attr('aria-activedescendant', listView._optionID);
|
|
}
|
|
},
|
|
_select: function (candidate, keepState) {
|
|
var that = this;
|
|
candidate = that._get(candidate);
|
|
that.listView.select(candidate);
|
|
if (!keepState && that._state === STATE_FILTER) {
|
|
that._state = STATE_ACCEPT;
|
|
}
|
|
if (candidate === -1) {
|
|
that._selectValue(null);
|
|
}
|
|
},
|
|
_selectValue: function (dataItem) {
|
|
var that = this;
|
|
var optionLabel = that.options.optionLabel;
|
|
var idx = that.listView.select();
|
|
var value = '';
|
|
var text = '';
|
|
idx = idx[idx.length - 1];
|
|
if (idx === undefined) {
|
|
idx = -1;
|
|
}
|
|
this._resetOptionLabel(' k-state-selected');
|
|
if (dataItem) {
|
|
text = dataItem;
|
|
value = that._dataValue(dataItem);
|
|
if (optionLabel) {
|
|
idx += 1;
|
|
}
|
|
} else if (optionLabel) {
|
|
that._focus(that.optionLabel.addClass('k-state-selected'));
|
|
text = that._optionLabelText();
|
|
if (typeof optionLabel === 'string') {
|
|
value = '';
|
|
} else {
|
|
value = that._value(optionLabel);
|
|
}
|
|
idx = 0;
|
|
}
|
|
that.selectedIndex = idx;
|
|
if (value === null) {
|
|
value = '';
|
|
}
|
|
that._textAccessor(text);
|
|
that._accessor(value, idx);
|
|
that._triggerCascade();
|
|
},
|
|
_mobile: function () {
|
|
var that = this, popup = that.popup, mobileOS = support.mobileOS, root = popup.element.parents('.km-root').eq(0);
|
|
if (root.length && mobileOS) {
|
|
popup.options.animation.open.effects = mobileOS.android || mobileOS.meego ? 'fadeIn' : mobileOS.ios || mobileOS.wp ? 'slideIn:up' : popup.options.animation.open.effects;
|
|
}
|
|
},
|
|
_filterHeader: function () {
|
|
var icon;
|
|
if (this.filterInput) {
|
|
this.filterInput.off(ns).parent().remove();
|
|
this.filterInput = null;
|
|
}
|
|
if (this._isFilterEnabled()) {
|
|
icon = '<span unselectable="on" class="k-icon k-i-search">select</span>';
|
|
this.filterInput = $('<input class="k-textbox"/>').attr({
|
|
placeholder: this.element.attr('placeholder'),
|
|
role: 'listbox',
|
|
'aria-haspopup': true,
|
|
'aria-expanded': false
|
|
});
|
|
this.list.prepend($('<span class="k-list-filter" />').append(this.filterInput.add(icon)));
|
|
}
|
|
},
|
|
_span: function () {
|
|
var that = this, wrapper = that.wrapper, SELECTOR = 'span.k-input', span;
|
|
span = wrapper.find(SELECTOR);
|
|
if (!span[0]) {
|
|
wrapper.append('<span unselectable="on" class="k-dropdown-wrap k-state-default"><span unselectable="on" class="k-input"> </span><span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-arrow-s">select</span></span></span>').append(that.element);
|
|
span = wrapper.find(SELECTOR);
|
|
}
|
|
that.span = span;
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
that._arrow = wrapper.find('.k-icon');
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, DOMelement = element[0], wrapper;
|
|
wrapper = element.parent();
|
|
if (!wrapper.is('span.k-widget')) {
|
|
wrapper = element.wrap('<span />').parent();
|
|
wrapper[0].style.cssText = DOMelement.style.cssText;
|
|
wrapper[0].title = DOMelement.title;
|
|
}
|
|
element.hide();
|
|
that._focused = that.wrapper = wrapper.addClass('k-widget k-dropdown k-header').addClass(DOMelement.className).css('display', '').attr({
|
|
accesskey: element.attr('accesskey'),
|
|
unselectable: 'on',
|
|
role: 'listbox',
|
|
'aria-haspopup': true,
|
|
'aria-expanded': false
|
|
});
|
|
},
|
|
_clearSelection: function (parent) {
|
|
this.select(parent.value() ? 0 : -1);
|
|
},
|
|
_inputTemplate: function () {
|
|
var that = this, template = that.options.valueTemplate;
|
|
if (!template) {
|
|
template = $.proxy(kendo.template('#:this._text(data)#', { useWithBlock: false }), that);
|
|
} else {
|
|
template = kendo.template(template);
|
|
}
|
|
that.valueTemplate = template;
|
|
if (that.hasOptionLabel() && !that.options.optionLabelTemplate) {
|
|
try {
|
|
that.valueTemplate(that._optionLabelDataItem());
|
|
} catch (e) {
|
|
throw new Error(MSG_INVALID_OPTION_LABEL);
|
|
}
|
|
}
|
|
},
|
|
_textAccessor: function (text) {
|
|
var dataItem = null;
|
|
var template = this.valueTemplate;
|
|
var options = this.options;
|
|
var optionLabel = options.optionLabel;
|
|
var span = this.span;
|
|
if (text !== undefined) {
|
|
if ($.isPlainObject(text) || text instanceof ObservableObject) {
|
|
dataItem = text;
|
|
} else if (optionLabel && this._optionLabelText() === text) {
|
|
dataItem = optionLabel;
|
|
template = this.optionLabelTemplate;
|
|
}
|
|
if (!dataItem) {
|
|
dataItem = this._assignInstance(text, this._accessor());
|
|
}
|
|
var getElements = function () {
|
|
return {
|
|
elements: span.get(),
|
|
data: [{ dataItem: dataItem }]
|
|
};
|
|
};
|
|
this.angular('cleanup', getElements);
|
|
try {
|
|
span.html(template(dataItem));
|
|
} catch (e) {
|
|
span.html('');
|
|
}
|
|
this.angular('compile', getElements);
|
|
} else {
|
|
return span.text();
|
|
}
|
|
},
|
|
_preselect: function (value, text) {
|
|
if (!value && !text) {
|
|
text = this._optionLabelText();
|
|
}
|
|
this._accessor(value);
|
|
this._textAccessor(text);
|
|
this._old = this._accessor();
|
|
this._oldIndex = this.selectedIndex;
|
|
this.listView.setValue(value);
|
|
this._initialIndex = null;
|
|
this._presetValue = true;
|
|
},
|
|
_assignInstance: function (text, value) {
|
|
var dataTextField = this.options.dataTextField;
|
|
var dataItem = {};
|
|
if (dataTextField) {
|
|
assign(dataItem, dataTextField.split('.'), text);
|
|
assign(dataItem, this.options.dataValueField.split('.'), value);
|
|
dataItem = new ObservableObject(dataItem);
|
|
} else {
|
|
dataItem = text;
|
|
}
|
|
return dataItem;
|
|
}
|
|
});
|
|
function assign(instance, fields, value) {
|
|
var idx = 0, lastIndex = fields.length - 1, field;
|
|
for (; idx < lastIndex; ++idx) {
|
|
field = fields[idx];
|
|
if (!(field in instance)) {
|
|
instance[field] = {};
|
|
}
|
|
instance = instance[field];
|
|
}
|
|
instance[fields[lastIndex]] = value;
|
|
}
|
|
function normalizeIndex(index, length) {
|
|
if (index >= length) {
|
|
index -= length;
|
|
}
|
|
return index;
|
|
}
|
|
function sameCharsOnly(word, character) {
|
|
for (var idx = 0; idx < word.length; idx++) {
|
|
if (word.charAt(idx) !== character) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
ui.plugin(DropDownList);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.combobox', [
|
|
'kendo.list',
|
|
'kendo.mobile.scroller'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'combobox',
|
|
name: 'ComboBox',
|
|
category: 'web',
|
|
description: 'The ComboBox widget allows the selection from pre-defined values or entering a new value.',
|
|
depends: ['list'],
|
|
features: [
|
|
{
|
|
id: 'mobile-scroller',
|
|
name: 'Mobile scroller',
|
|
description: 'Support for kinetic scrolling in mobile device',
|
|
depends: ['mobile.scroller']
|
|
},
|
|
{
|
|
id: 'virtualization',
|
|
name: 'VirtualList',
|
|
description: 'Support for virtualization',
|
|
depends: ['virtuallist']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, List = ui.List, Select = ui.Select, caret = kendo.caret, support = kendo.support, placeholderSupported = support.placeholder, activeElement = kendo._activeElement, keys = kendo.keys, ns = '.kendoComboBox', CLICK = 'click' + ns, MOUSEDOWN = 'mousedown' + ns, DISABLED = 'disabled', READONLY = 'readonly', CHANGE = 'change', DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', STATEDISABLED = 'k-state-disabled', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', STATE_FILTER = 'filter', STATE_ACCEPT = 'accept', STATE_REBIND = 'rebind', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, proxy = $.proxy;
|
|
var ComboBox = Select.extend({
|
|
init: function (element, options) {
|
|
var that = this, text, disabled;
|
|
that.ns = ns;
|
|
options = $.isArray(options) ? { dataSource: options } : options;
|
|
Select.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element.on('focus' + ns, proxy(that._focusHandler, that));
|
|
options.placeholder = options.placeholder || element.attr('placeholder');
|
|
that._reset();
|
|
that._wrapper();
|
|
that._input();
|
|
that._tabindex(that.input);
|
|
that._popup();
|
|
that._dataSource();
|
|
that._ignoreCase();
|
|
that._enable();
|
|
that._oldIndex = that.selectedIndex = -1;
|
|
that._aria();
|
|
that._initialIndex = options.index;
|
|
that._initList();
|
|
that._cascade();
|
|
if (options.autoBind) {
|
|
that._filterSource();
|
|
} else {
|
|
text = options.text;
|
|
if (!text && that._isSelect) {
|
|
text = element.children(':selected').text();
|
|
}
|
|
if (text) {
|
|
that._setText(text);
|
|
}
|
|
}
|
|
if (!text) {
|
|
that._placeholder();
|
|
}
|
|
disabled = $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'ComboBox',
|
|
enabled: true,
|
|
index: -1,
|
|
text: null,
|
|
value: null,
|
|
autoBind: true,
|
|
delay: 200,
|
|
dataTextField: '',
|
|
dataValueField: '',
|
|
minLength: 0,
|
|
height: 200,
|
|
highlightFirst: true,
|
|
filter: 'none',
|
|
placeholder: '',
|
|
suggest: false,
|
|
cascadeFrom: '',
|
|
cascadeFromField: '',
|
|
ignoreCase: true,
|
|
animation: {},
|
|
virtual: false,
|
|
template: null,
|
|
groupTemplate: '#:data#',
|
|
fixedGroupTemplate: '#:data#'
|
|
},
|
|
events: [
|
|
'open',
|
|
'close',
|
|
CHANGE,
|
|
'select',
|
|
'filtering',
|
|
'dataBinding',
|
|
'dataBound',
|
|
'cascade',
|
|
'set'
|
|
],
|
|
setOptions: function (options) {
|
|
Select.fn.setOptions.call(this, options);
|
|
this.listView.setOptions(options);
|
|
this._accessors();
|
|
this._aria();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.input.off(ns);
|
|
that.element.off(ns);
|
|
that._inputWrapper.off(ns);
|
|
that._arrow.parent().off(CLICK + ' ' + MOUSEDOWN);
|
|
Select.fn.destroy.call(that);
|
|
},
|
|
_focusHandler: function () {
|
|
this.input.focus();
|
|
},
|
|
_arrowClick: function () {
|
|
this._toggle();
|
|
},
|
|
_inputFocus: function () {
|
|
this._inputWrapper.addClass(FOCUSED);
|
|
this._placeholder(false);
|
|
},
|
|
_inputFocusout: function () {
|
|
var that = this;
|
|
var value = that.value();
|
|
that._inputWrapper.removeClass(FOCUSED);
|
|
clearTimeout(that._typingTimeout);
|
|
that._typingTimeout = null;
|
|
that.text(that.text());
|
|
if (value !== that.value() && that.trigger('select', { item: that._focus() })) {
|
|
that.value(value);
|
|
return;
|
|
}
|
|
that._placeholder();
|
|
that._blur();
|
|
that.element.blur();
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, disable = options.disable, readonly = options.readonly, wrapper = that._inputWrapper.off(ns), input = that.element.add(that.input.off(ns)), arrow = that._arrow.parent().off(CLICK + ' ' + MOUSEDOWN);
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
input.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
|
|
arrow.on(CLICK, proxy(that._arrowClick, that)).on(MOUSEDOWN, function (e) {
|
|
e.preventDefault();
|
|
});
|
|
that.input.on('keydown' + ns, proxy(that._keydown, that)).on('focus' + ns, proxy(that._inputFocus, that)).on('focusout' + ns, proxy(that._inputFocusout, that));
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
input.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
open: function () {
|
|
var that = this;
|
|
var state = that._state;
|
|
if (that.popup.visible()) {
|
|
return;
|
|
}
|
|
if (!that.listView.bound() && state !== STATE_FILTER || state === STATE_ACCEPT) {
|
|
that._open = true;
|
|
that._state = STATE_REBIND;
|
|
that._filterSource();
|
|
} else {
|
|
that.popup.open();
|
|
that._focusItem();
|
|
}
|
|
},
|
|
_updateSelectionState: function () {
|
|
var that = this;
|
|
var text = that.options.text;
|
|
var value = that.options.value;
|
|
if (that.listView.isFiltered()) {
|
|
return;
|
|
}
|
|
if (that.selectedIndex === -1) {
|
|
if (text === undefined || text === null) {
|
|
text = value;
|
|
}
|
|
that._accessor(value);
|
|
that.input.val(text || that.input.val());
|
|
that._placeholder();
|
|
} else if (that._oldIndex === -1) {
|
|
that._oldIndex = that.selectedIndex;
|
|
}
|
|
},
|
|
_buildOptions: function (data) {
|
|
var that = this;
|
|
if (!that._isSelect) {
|
|
return;
|
|
}
|
|
var custom = that._customOption;
|
|
if (that._state === STATE_REBIND) {
|
|
that._state = '';
|
|
}
|
|
that._customOption = undefined;
|
|
that._options(data, '', that.value());
|
|
if (custom && custom[0].selected) {
|
|
that._custom(custom.val());
|
|
}
|
|
},
|
|
_updateSelection: function () {
|
|
var that = this;
|
|
var listView = that.listView;
|
|
var initialIndex = that._initialIndex;
|
|
var hasInitialIndex = initialIndex !== null && initialIndex > -1;
|
|
var filtered = that._state === STATE_FILTER;
|
|
if (filtered) {
|
|
$(listView.focus()).removeClass('k-state-selected');
|
|
return;
|
|
}
|
|
if (that._fetch) {
|
|
return;
|
|
}
|
|
if (!listView.value().length) {
|
|
if (hasInitialIndex) {
|
|
that.select(initialIndex);
|
|
} else if (that._accessor()) {
|
|
listView.value(that._accessor());
|
|
}
|
|
}
|
|
that._initialIndex = null;
|
|
var dataItem = listView.selectedDataItems()[0];
|
|
if (!dataItem) {
|
|
return;
|
|
}
|
|
if (that._value(dataItem) !== that.value()) {
|
|
that._custom(that._value(dataItem));
|
|
}
|
|
if (that.text() && that.text() !== that._text(dataItem)) {
|
|
that._selectValue(dataItem);
|
|
}
|
|
},
|
|
_updateItemFocus: function () {
|
|
var listView = this.listView;
|
|
if (!this.options.highlightFirst) {
|
|
listView.focus(-1);
|
|
} else if (!listView.focus() && !listView.focusIndex()) {
|
|
listView.focus(0);
|
|
}
|
|
},
|
|
_listBound: function () {
|
|
var that = this;
|
|
var isActive = that.input[0] === activeElement();
|
|
var data = that.dataSource.flatView();
|
|
var skip = that.listView.skip();
|
|
var isFirstPage = skip === undefined || skip === 0;
|
|
that._angularItems('compile');
|
|
that._presetValue = false;
|
|
that._resizePopup();
|
|
that.popup.position();
|
|
that._buildOptions(data);
|
|
that._makeUnselectable();
|
|
that._updateSelection();
|
|
if (data.length && isFirstPage) {
|
|
that._updateItemFocus();
|
|
if (that.options.suggest && isActive && that.input.val()) {
|
|
that.suggest(data[0]);
|
|
}
|
|
}
|
|
if (that._open) {
|
|
that._open = false;
|
|
if (that._typingTimeout && !isActive) {
|
|
that.popup.close();
|
|
} else {
|
|
that.toggle(!!data.length);
|
|
}
|
|
that._typingTimeout = null;
|
|
}
|
|
that._hideBusy();
|
|
that.trigger('dataBound');
|
|
},
|
|
_listChange: function () {
|
|
this._selectValue(this.listView.selectedDataItems()[0]);
|
|
if (this._presetValue) {
|
|
this._oldIndex = this.selectedIndex;
|
|
}
|
|
},
|
|
_get: function (candidate) {
|
|
var data, found, idx;
|
|
if (typeof candidate === 'function') {
|
|
data = this.dataSource.flatView();
|
|
for (idx = 0; idx < data.length; idx++) {
|
|
if (candidate(data[idx])) {
|
|
candidate = idx;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
candidate = -1;
|
|
}
|
|
}
|
|
return candidate;
|
|
},
|
|
_select: function (candidate, keepState) {
|
|
candidate = this._get(candidate);
|
|
if (candidate === -1) {
|
|
this.input[0].value = '';
|
|
this._accessor('');
|
|
}
|
|
this.listView.select(candidate);
|
|
if (!keepState && this._state === STATE_FILTER) {
|
|
this._state = STATE_ACCEPT;
|
|
}
|
|
},
|
|
_selectValue: function (dataItem) {
|
|
var idx = this.listView.select();
|
|
var value = '';
|
|
var text = '';
|
|
idx = idx[idx.length - 1];
|
|
if (idx === undefined) {
|
|
idx = -1;
|
|
}
|
|
this.selectedIndex = idx;
|
|
if (idx === -1) {
|
|
value = text = this.input[0].value;
|
|
this.listView.focus(-1);
|
|
} else {
|
|
if (dataItem) {
|
|
value = this._dataValue(dataItem);
|
|
text = this._text(dataItem);
|
|
}
|
|
if (value === null) {
|
|
value = '';
|
|
}
|
|
}
|
|
this._prev = this.input[0].value = text;
|
|
this._accessor(value !== undefined ? value : text, idx);
|
|
this._placeholder();
|
|
this._triggerCascade();
|
|
},
|
|
refresh: function () {
|
|
this.listView.refresh();
|
|
},
|
|
suggest: function (word) {
|
|
var that = this;
|
|
var element = that.input[0];
|
|
var value = that.text();
|
|
var caretIdx = caret(element)[0];
|
|
var key = that._last;
|
|
var idx;
|
|
if (key == keys.BACKSPACE || key == keys.DELETE) {
|
|
that._last = undefined;
|
|
return;
|
|
}
|
|
word = word || '';
|
|
if (typeof word !== 'string') {
|
|
if (word[0]) {
|
|
word = that.dataSource.view()[List.inArray(word[0], that.ul[0])];
|
|
}
|
|
word = word ? that._text(word) : '';
|
|
}
|
|
if (caretIdx <= 0) {
|
|
caretIdx = value.toLowerCase().indexOf(word.toLowerCase()) + 1;
|
|
}
|
|
if (word) {
|
|
word = word.toString();
|
|
idx = word.toLowerCase().indexOf(value.toLowerCase());
|
|
if (idx > -1) {
|
|
value += word.substring(idx + value.length);
|
|
}
|
|
} else {
|
|
value = value.substring(0, caretIdx);
|
|
}
|
|
if (value.length !== caretIdx || !word) {
|
|
element.value = value;
|
|
if (element === activeElement()) {
|
|
caret(element, caretIdx, value.length);
|
|
}
|
|
}
|
|
},
|
|
text: function (text) {
|
|
text = text === null ? '' : text;
|
|
var that = this;
|
|
var input = that.input[0];
|
|
var ignoreCase = that.options.ignoreCase;
|
|
var loweredText = text;
|
|
var dataItem;
|
|
var value;
|
|
if (text === undefined) {
|
|
return input.value;
|
|
}
|
|
if (that.options.autoBind === false && !that.listView.bound()) {
|
|
that._setText(text);
|
|
return;
|
|
}
|
|
dataItem = that.dataItem();
|
|
if (dataItem && that._text(dataItem) === text) {
|
|
value = that._value(dataItem);
|
|
if (value === List.unifyType(that._old, typeof value)) {
|
|
that._triggerCascade();
|
|
return;
|
|
}
|
|
}
|
|
if (ignoreCase) {
|
|
loweredText = loweredText.toLowerCase();
|
|
}
|
|
that._select(function (data) {
|
|
data = that._text(data);
|
|
if (ignoreCase) {
|
|
data = (data + '').toLowerCase();
|
|
}
|
|
return data === loweredText;
|
|
});
|
|
if (that.selectedIndex < 0) {
|
|
that._accessor(text);
|
|
input.value = text;
|
|
that._triggerCascade();
|
|
}
|
|
that._prev = input.value;
|
|
},
|
|
toggle: function (toggle) {
|
|
this._toggle(toggle, true);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var listView = that.listView;
|
|
if (value === undefined) {
|
|
value = that._accessor() || that.listView.value()[0];
|
|
return value === undefined || value === null ? '' : value;
|
|
}
|
|
that.trigger('set', { value: value });
|
|
if (value === options.value && that.input.val() === options.text) {
|
|
return;
|
|
}
|
|
that._accessor(value);
|
|
if (that._isFilterEnabled() && listView.bound() && listView.isFiltered()) {
|
|
that._clearFilter();
|
|
} else {
|
|
that._fetchData();
|
|
}
|
|
listView.value(value).done(function () {
|
|
if (that.selectedIndex === -1) {
|
|
that._accessor(value);
|
|
that.input.val(value);
|
|
that._placeholder(true);
|
|
}
|
|
that._old = that._accessor();
|
|
that._oldIndex = that.selectedIndex;
|
|
that._prev = that.input.val();
|
|
if (that._state === STATE_FILTER) {
|
|
that._state = STATE_ACCEPT;
|
|
}
|
|
});
|
|
},
|
|
_click: function (e) {
|
|
var item = e.item;
|
|
e.preventDefault();
|
|
if (this.trigger('select', { item: item })) {
|
|
this.close();
|
|
return;
|
|
}
|
|
this._userTriggered = true;
|
|
this._select(item);
|
|
this._blur();
|
|
},
|
|
_filter: function (word) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var dataSource = that.dataSource;
|
|
var ignoreCase = options.ignoreCase;
|
|
var predicate = function (dataItem) {
|
|
var text = that._text(dataItem);
|
|
if (text !== undefined) {
|
|
text = text + '';
|
|
if (text !== '' && word === '') {
|
|
return false;
|
|
}
|
|
if (ignoreCase) {
|
|
text = text.toLowerCase();
|
|
}
|
|
return text.indexOf(word) === 0;
|
|
}
|
|
};
|
|
if (ignoreCase) {
|
|
word = word.toLowerCase();
|
|
}
|
|
if (!that.ul[0].firstChild) {
|
|
dataSource.one(CHANGE, function () {
|
|
if (dataSource.view()[0]) {
|
|
that.search(word);
|
|
}
|
|
}).fetch();
|
|
return;
|
|
}
|
|
this.listView.focus(this._get(predicate));
|
|
var current = this.listView.focus();
|
|
if (current) {
|
|
if (options.suggest) {
|
|
that.suggest(current);
|
|
}
|
|
this.open();
|
|
}
|
|
if (this.options.highlightFirst && !word) {
|
|
this.listView.focusFirst();
|
|
}
|
|
},
|
|
_input: function () {
|
|
var that = this, element = that.element.removeClass('k-input')[0], accessKey = element.accessKey, wrapper = that.wrapper, SELECTOR = 'input.k-input', name = element.name || '', input;
|
|
if (name) {
|
|
name = 'name="' + name + '_input" ';
|
|
}
|
|
input = wrapper.find(SELECTOR);
|
|
if (!input[0]) {
|
|
wrapper.append('<span tabindex="-1" unselectable="on" class="k-dropdown-wrap k-state-default"><input ' + name + 'class="k-input" type="text" autocomplete="off"/><span tabindex="-1" unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-arrow-s">select</span></span></span>').append(that.element);
|
|
input = wrapper.find(SELECTOR);
|
|
}
|
|
input[0].style.cssText = element.style.cssText;
|
|
input[0].title = element.title;
|
|
if (element.maxLength > -1) {
|
|
input[0].maxLength = element.maxLength;
|
|
}
|
|
input.addClass(element.className).val(this.options.text || element.value).css({
|
|
width: '100%',
|
|
height: element.style.height
|
|
}).attr({
|
|
'role': 'combobox',
|
|
'aria-expanded': false
|
|
}).show();
|
|
if (placeholderSupported) {
|
|
input.attr('placeholder', that.options.placeholder);
|
|
}
|
|
if (accessKey) {
|
|
element.accessKey = '';
|
|
input[0].accessKey = accessKey;
|
|
}
|
|
that._focused = that.input = input;
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
that._arrow = wrapper.find('.k-icon').attr({
|
|
'role': 'button',
|
|
'tabIndex': -1
|
|
});
|
|
if (element.id) {
|
|
that._arrow.attr('aria-controls', that.ul[0].id);
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode;
|
|
that._last = key;
|
|
clearTimeout(that._typingTimeout);
|
|
that._typingTimeout = null;
|
|
if (key != keys.TAB && !that._move(e)) {
|
|
that._search();
|
|
}
|
|
},
|
|
_placeholder: function (show) {
|
|
if (placeholderSupported) {
|
|
return;
|
|
}
|
|
var that = this, input = that.input, placeholder = that.options.placeholder, value;
|
|
if (placeholder) {
|
|
value = that.value();
|
|
if (show === undefined) {
|
|
show = !value;
|
|
}
|
|
input.toggleClass('k-readonly', show);
|
|
if (!show) {
|
|
if (!value) {
|
|
placeholder = '';
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
input.val(placeholder);
|
|
if (!placeholder && input[0] === activeElement()) {
|
|
caret(input[0], 0, 0);
|
|
}
|
|
}
|
|
},
|
|
_search: function () {
|
|
var that = this;
|
|
that._typingTimeout = setTimeout(function () {
|
|
var value = that.text();
|
|
if (that._prev !== value) {
|
|
that._prev = value;
|
|
if (that.options.filter === 'none') {
|
|
that.listView.select(-1);
|
|
}
|
|
that.search(value);
|
|
}
|
|
that._typingTimeout = null;
|
|
}, that.options.delay);
|
|
},
|
|
_setText: function (text) {
|
|
this.input.val(text);
|
|
this._prev = text;
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper = element.parent();
|
|
if (!wrapper.is('span.k-widget')) {
|
|
wrapper = element.hide().wrap('<span />').parent();
|
|
wrapper[0].style.cssText = element[0].style.cssText;
|
|
}
|
|
that.wrapper = wrapper.addClass('k-widget k-combobox k-header').addClass(element[0].className).css('display', '');
|
|
},
|
|
_clearSelection: function (parent, isFiltered) {
|
|
var that = this;
|
|
var hasValue = parent.value();
|
|
var custom = hasValue && parent.selectedIndex === -1;
|
|
if (this.selectedIndex == -1 && this.value()) {
|
|
return;
|
|
}
|
|
if (isFiltered || !hasValue || custom) {
|
|
that.options.value = '';
|
|
that.value('');
|
|
}
|
|
},
|
|
_preselect: function (value, text) {
|
|
this.input.val(text);
|
|
this._accessor(value);
|
|
this._old = this._accessor();
|
|
this._oldIndex = this.selectedIndex;
|
|
this.listView.setValue(value);
|
|
this._placeholder();
|
|
this._initialIndex = null;
|
|
this._presetValue = true;
|
|
}
|
|
});
|
|
ui.plugin(ComboBox);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.multiselect', [
|
|
'kendo.list',
|
|
'kendo.mobile.scroller'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'multiselect',
|
|
name: 'MultiSelect',
|
|
category: 'web',
|
|
description: 'The MultiSelect widget allows the selection from pre-defined values.',
|
|
depends: ['list'],
|
|
features: [
|
|
{
|
|
id: 'mobile-scroller',
|
|
name: 'Mobile scroller',
|
|
description: 'Support for kinetic scrolling in mobile device',
|
|
depends: ['mobile.scroller']
|
|
},
|
|
{
|
|
id: 'virtualization',
|
|
name: 'VirtualList',
|
|
description: 'Support for virtualization',
|
|
depends: ['virtuallist']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, List = ui.List, keys = kendo.keys, activeElement = kendo._activeElement, ObservableArray = kendo.data.ObservableArray, proxy = $.proxy, ID = 'id', LI = 'li', ACCEPT = 'accept', FILTER = 'filter', REBIND = 'rebind', OPEN = 'open', CLOSE = 'close', CHANGE = 'change', PROGRESS = 'progress', SELECT = 'select', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', FOCUSEDCLASS = 'k-state-focused', HIDDENCLASS = 'k-loading-hidden', HOVERCLASS = 'k-state-hover', STATEDISABLED = 'k-state-disabled', DISABLED = 'disabled', READONLY = 'readonly', ns = '.kendoMultiSelect', CLICK = 'click' + ns, KEYDOWN = 'keydown' + ns, MOUSEENTER = 'mouseenter' + ns, MOUSELEAVE = 'mouseleave' + ns, HOVEREVENTS = MOUSEENTER + ' ' + MOUSELEAVE, quotRegExp = /"/g, isArray = $.isArray, styles = [
|
|
'font-family',
|
|
'font-size',
|
|
'font-stretch',
|
|
'font-style',
|
|
'font-weight',
|
|
'letter-spacing',
|
|
'text-transform',
|
|
'line-height'
|
|
];
|
|
var MultiSelect = List.extend({
|
|
init: function (element, options) {
|
|
var that = this, id, disabled;
|
|
that.ns = ns;
|
|
List.fn.init.call(that, element, options);
|
|
that._optionsMap = {};
|
|
that._customOptions = {};
|
|
that._wrapper();
|
|
that._tagList();
|
|
that._input();
|
|
that._textContainer();
|
|
that._loader();
|
|
that._tabindex(that.input);
|
|
element = that.element.attr('multiple', 'multiple').hide();
|
|
options = that.options;
|
|
if (!options.placeholder) {
|
|
options.placeholder = element.data('placeholder');
|
|
}
|
|
id = element.attr(ID);
|
|
if (id) {
|
|
that._tagID = id + '_tag_active';
|
|
id = id + '_taglist';
|
|
that.tagList.attr(ID, id);
|
|
}
|
|
that._aria(id);
|
|
that._dataSource();
|
|
that._ignoreCase();
|
|
that._popup();
|
|
that._tagTemplate();
|
|
that._initList();
|
|
that._reset();
|
|
that._enable();
|
|
that._placeholder();
|
|
if (options.autoBind) {
|
|
that.dataSource.fetch();
|
|
} else if (options.value) {
|
|
that._preselect(options.value);
|
|
}
|
|
disabled = $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'MultiSelect',
|
|
tagMode: 'multiple',
|
|
enabled: true,
|
|
autoBind: true,
|
|
autoClose: true,
|
|
highlightFirst: true,
|
|
dataTextField: '',
|
|
dataValueField: '',
|
|
filter: 'startswith',
|
|
ignoreCase: true,
|
|
minLength: 0,
|
|
delay: 100,
|
|
value: null,
|
|
maxSelectedItems: null,
|
|
placeholder: '',
|
|
height: 200,
|
|
animation: {},
|
|
virtual: false,
|
|
itemTemplate: '',
|
|
tagTemplate: '',
|
|
groupTemplate: '#:data#',
|
|
fixedGroupTemplate: '#:data#'
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
CHANGE,
|
|
SELECT,
|
|
'filtering',
|
|
'dataBinding',
|
|
'dataBound'
|
|
],
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._state = '';
|
|
this._dataSource();
|
|
this.listView.setDataSource(this.dataSource);
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var listOptions = this._listOptions(options);
|
|
List.fn.setOptions.call(this, options);
|
|
this.listView.setOptions(listOptions);
|
|
this._accessors();
|
|
this._aria(this.tagList.attr(ID));
|
|
this._tagTemplate();
|
|
},
|
|
currentTag: function (candidate) {
|
|
var that = this;
|
|
if (candidate !== undefined) {
|
|
if (that._currentTag) {
|
|
that._currentTag.removeClass(FOCUSEDCLASS).removeAttr(ID);
|
|
that.input.removeAttr('aria-activedescendant');
|
|
}
|
|
if (candidate) {
|
|
candidate.addClass(FOCUSEDCLASS).attr(ID, that._tagID);
|
|
that.input.attr('aria-activedescendant', that._tagID);
|
|
}
|
|
that._currentTag = candidate;
|
|
} else {
|
|
return that._currentTag;
|
|
}
|
|
},
|
|
dataItems: function () {
|
|
return this.listView.selectedDataItems();
|
|
},
|
|
destroy: function () {
|
|
var that = this, ns = that.ns;
|
|
clearTimeout(that._busy);
|
|
clearTimeout(that._typingTimeout);
|
|
that.wrapper.off(ns);
|
|
that.tagList.off(ns);
|
|
that.input.off(ns);
|
|
List.fn.destroy.call(that);
|
|
},
|
|
_activateItem: function () {
|
|
List.fn._activateItem.call(this);
|
|
this.currentTag(null);
|
|
},
|
|
_listOptions: function (options) {
|
|
var that = this;
|
|
var listOptions = List.fn._listOptions.call(that, $.extend(options, {
|
|
selectedItemChange: proxy(that._selectedItemChange, that),
|
|
selectable: 'multiple'
|
|
}));
|
|
var itemTemplate = this.options.itemTemplate || this.options.template;
|
|
var template = listOptions.itemTemplate || itemTemplate || listOptions.template;
|
|
if (!template) {
|
|
template = '#:' + kendo.expr(listOptions.dataTextField, 'data') + '#';
|
|
}
|
|
listOptions.template = template;
|
|
return listOptions;
|
|
},
|
|
_setListValue: function () {
|
|
List.fn._setListValue.call(this, this._initialValues.slice(0));
|
|
},
|
|
_listChange: function (e) {
|
|
if (this._state === REBIND) {
|
|
this._state = '';
|
|
e.added = [];
|
|
}
|
|
this._selectValue(e.added, e.removed);
|
|
},
|
|
_selectedItemChange: function (e) {
|
|
var items = e.items;
|
|
var context;
|
|
var idx;
|
|
for (idx = 0; idx < items.length; idx++) {
|
|
context = items[idx];
|
|
this.tagList.children().eq(context.index).children('span:first').html(this.tagTextTemplate(context.item));
|
|
}
|
|
},
|
|
_wrapperMousedown: function (e) {
|
|
var that = this;
|
|
var notInput = e.target.nodeName.toLowerCase() !== 'input';
|
|
var target = $(e.target);
|
|
var closeButton = target.hasClass('k-select') || target.hasClass('k-icon');
|
|
if (closeButton) {
|
|
closeButton = !target.closest('.k-select').children('.k-i-arrow-s').length;
|
|
}
|
|
if (notInput && !(closeButton && kendo.support.mobileOS)) {
|
|
e.preventDefault();
|
|
}
|
|
if (!closeButton) {
|
|
if (that.input[0] !== activeElement() && notInput) {
|
|
that.input.focus();
|
|
}
|
|
if (that.options.minLength === 0) {
|
|
that.open();
|
|
}
|
|
}
|
|
},
|
|
_inputFocus: function () {
|
|
this._placeholder(false);
|
|
this.wrapper.addClass(FOCUSEDCLASS);
|
|
},
|
|
_inputFocusout: function () {
|
|
var that = this;
|
|
clearTimeout(that._typingTimeout);
|
|
that.wrapper.removeClass(FOCUSEDCLASS);
|
|
that._placeholder(!that.listView.selectedDataItems()[0], true);
|
|
that.close();
|
|
if (that._state === FILTER) {
|
|
that._state = ACCEPT;
|
|
that.listView.skipUpdate(true);
|
|
}
|
|
that.element.blur();
|
|
},
|
|
_removeTag: function (tag) {
|
|
var that = this;
|
|
var state = that._state;
|
|
var position = tag.index();
|
|
var listView = that.listView;
|
|
var value = listView.value()[position];
|
|
var customIndex = that._customOptions[value];
|
|
var option;
|
|
if (customIndex === undefined && (state === ACCEPT || state === FILTER)) {
|
|
customIndex = that._optionsMap[value];
|
|
}
|
|
if (customIndex !== undefined) {
|
|
option = that.element[0].children[customIndex];
|
|
option.removeAttribute('selected');
|
|
option.selected = false;
|
|
listView.removeAt(position);
|
|
tag.remove();
|
|
} else {
|
|
listView.select(listView.select()[position]);
|
|
}
|
|
that.currentTag(null);
|
|
that._change();
|
|
that._close();
|
|
},
|
|
_tagListClick: function (e) {
|
|
var target = $(e.currentTarget);
|
|
if (!target.children('.k-i-arrow-s').length) {
|
|
this._removeTag(target.closest(LI));
|
|
}
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, disable = options.disable, readonly = options.readonly, wrapper = that.wrapper.off(ns), tagList = that.tagList.off(ns), input = that.element.add(that.input.off(ns));
|
|
if (!readonly && !disable) {
|
|
wrapper.removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover).on('mousedown' + ns + ' touchend' + ns, proxy(that._wrapperMousedown, that));
|
|
that.input.on(KEYDOWN, proxy(that._keydown, that)).on('paste' + ns, proxy(that._search, that)).on('focus' + ns, proxy(that._inputFocus, that)).on('focusout' + ns, proxy(that._inputFocusout, that));
|
|
input.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
|
|
tagList.on(MOUSEENTER, LI, function () {
|
|
$(this).addClass(HOVERCLASS);
|
|
}).on(MOUSELEAVE, LI, function () {
|
|
$(this).removeClass(HOVERCLASS);
|
|
}).on(CLICK, 'li.k-button .k-select', proxy(that._tagListClick, that));
|
|
} else {
|
|
if (disable) {
|
|
wrapper.addClass(STATEDISABLED);
|
|
} else {
|
|
wrapper.removeClass(STATEDISABLED);
|
|
}
|
|
input.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
_close: function () {
|
|
var that = this;
|
|
if (that.options.autoClose) {
|
|
that.close();
|
|
} else {
|
|
that.popup.position();
|
|
}
|
|
},
|
|
_filterSource: function (filter, force) {
|
|
if (!force) {
|
|
force = this._retrieveData;
|
|
}
|
|
this._retrieveData = false;
|
|
List.fn._filterSource.call(this, filter, force);
|
|
},
|
|
close: function () {
|
|
this.popup.close();
|
|
},
|
|
open: function () {
|
|
var that = this;
|
|
if (that._request) {
|
|
that._retrieveData = false;
|
|
}
|
|
if (that._retrieveData || !that.listView.bound() || that._state === ACCEPT) {
|
|
that._open = true;
|
|
that._state = REBIND;
|
|
that.listView.skipUpdate(true);
|
|
that._filterSource();
|
|
} else if (that._allowSelection()) {
|
|
that.popup.open();
|
|
that._focusItem();
|
|
}
|
|
},
|
|
toggle: function (toggle) {
|
|
toggle = toggle !== undefined ? toggle : !this.popup.visible();
|
|
this[toggle ? OPEN : CLOSE]();
|
|
},
|
|
refresh: function () {
|
|
this.listView.refresh();
|
|
},
|
|
_angularItems: function (cmd) {
|
|
var that = this;
|
|
that.angular(cmd, function () {
|
|
return {
|
|
elements: that.items(),
|
|
data: $.map(that.dataSource.flatView(), function (dataItem) {
|
|
return { dataItem: dataItem };
|
|
})
|
|
};
|
|
});
|
|
},
|
|
_listBound: function () {
|
|
var that = this;
|
|
var data = that.dataSource.flatView();
|
|
var skip = that.listView.skip();
|
|
var length = data.length;
|
|
that._angularItems('compile');
|
|
that._render(data);
|
|
that._resizePopup();
|
|
if (that._open) {
|
|
that._open = false;
|
|
that.toggle(length);
|
|
}
|
|
that.popup.position();
|
|
if (that.options.highlightFirst && (skip === undefined || skip === 0)) {
|
|
that.listView.focusFirst();
|
|
}
|
|
if (that._touchScroller) {
|
|
that._touchScroller.reset();
|
|
}
|
|
that._hideBusy();
|
|
that._makeUnselectable();
|
|
that.trigger('dataBound');
|
|
},
|
|
search: function (word) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var ignoreCase = options.ignoreCase;
|
|
var field = options.dataTextField;
|
|
var inputValue = that.input.val();
|
|
var expression;
|
|
var length;
|
|
if (options.placeholder === inputValue) {
|
|
inputValue = '';
|
|
}
|
|
clearTimeout(that._typingTimeout);
|
|
word = typeof word === 'string' ? word : inputValue;
|
|
length = word.length;
|
|
if (!length || length >= options.minLength) {
|
|
that._state = FILTER;
|
|
that._open = true;
|
|
expression = {
|
|
value: ignoreCase ? word.toLowerCase() : word,
|
|
field: field,
|
|
operator: options.filter,
|
|
ignoreCase: ignoreCase
|
|
};
|
|
that._filterSource(expression);
|
|
}
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var listView = that.listView;
|
|
var oldValue = listView.value().slice();
|
|
var maxSelectedItems = that.options.maxSelectedItems;
|
|
var clearFilters = listView.bound() && listView.isFiltered();
|
|
if (value === undefined) {
|
|
return oldValue;
|
|
}
|
|
value = that._normalizeValues(value);
|
|
if (maxSelectedItems !== null && value.length > maxSelectedItems) {
|
|
value = value.slice(0, maxSelectedItems);
|
|
}
|
|
if (clearFilters) {
|
|
that._clearFilter();
|
|
}
|
|
listView.value(value);
|
|
that._old = value;
|
|
if (!clearFilters) {
|
|
that._fetchData();
|
|
}
|
|
},
|
|
_preselect: function (data, value) {
|
|
var that = this;
|
|
if (!isArray(data) && !(data instanceof kendo.data.ObservableArray)) {
|
|
data = [data];
|
|
}
|
|
if ($.isPlainObject(data[0]) || data[0] instanceof kendo.data.ObservableObject || !that.options.dataValueField) {
|
|
that.dataSource.data(data);
|
|
that.value(value || that._initialValues);
|
|
that._retrieveData = true;
|
|
}
|
|
},
|
|
_setOption: function (value, selected) {
|
|
var option = this.element[0].children[this._optionsMap[value]];
|
|
if (option) {
|
|
if (selected) {
|
|
option.setAttribute('selected', 'selected');
|
|
} else {
|
|
option.removeAttribute('selected');
|
|
}
|
|
option.selected = selected;
|
|
}
|
|
},
|
|
_fetchData: function () {
|
|
var that = this;
|
|
var hasItems = !!that.dataSource.view().length;
|
|
var isEmptyArray = that.listView.value().length === 0;
|
|
if (isEmptyArray || that._request) {
|
|
return;
|
|
}
|
|
if (that._retrieveData || !that._fetch && !hasItems) {
|
|
that._fetch = true;
|
|
that._retrieveData = false;
|
|
that.dataSource.read().done(function () {
|
|
that._fetch = false;
|
|
});
|
|
}
|
|
},
|
|
_isBound: function () {
|
|
return this.listView.bound() && !this._retrieveData;
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, element = that.element, options = that.options, dataSource = options.dataSource || {};
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
dataSource.select = element;
|
|
dataSource.fields = [
|
|
{ field: options.dataTextField },
|
|
{ field: options.dataValueField }
|
|
];
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that._unbindDataSource();
|
|
} else {
|
|
that._progressHandler = proxy(that._showBusy, that);
|
|
that._errorHandler = proxy(that._hideBusy, that);
|
|
}
|
|
that.dataSource = kendo.data.DataSource.create(dataSource).bind(PROGRESS, that._progressHandler).bind('error', that._errorHandler);
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
setTimeout(function () {
|
|
that.value(that._initialValues);
|
|
that._placeholder();
|
|
});
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
},
|
|
_initValue: function () {
|
|
var value = this.options.value || this.element.val();
|
|
this._old = this._initialValues = this._normalizeValues(value);
|
|
},
|
|
_normalizeValues: function (value) {
|
|
var that = this;
|
|
if (value === null) {
|
|
value = [];
|
|
} else if (value && $.isPlainObject(value)) {
|
|
value = [that._value(value)];
|
|
} else if (value && $.isPlainObject(value[0])) {
|
|
value = $.map(value, function (dataItem) {
|
|
return that._value(dataItem);
|
|
});
|
|
} else if (!isArray(value) && !(value instanceof ObservableArray)) {
|
|
value = [value];
|
|
}
|
|
return value;
|
|
},
|
|
_change: function () {
|
|
var that = this, value = that.value();
|
|
if (!compare(value, that._old)) {
|
|
that._old = value.slice();
|
|
that.trigger(CHANGE);
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var item = e.item;
|
|
e.preventDefault();
|
|
if (this.trigger(SELECT, { item: item })) {
|
|
this._close();
|
|
return;
|
|
}
|
|
this._select(item);
|
|
this._change();
|
|
this._close();
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
var key = e.keyCode;
|
|
var tag = that._currentTag;
|
|
var current = that.listView.focus();
|
|
var hasValue = that.input.val();
|
|
var isRtl = kendo.support.isRtl(that.wrapper);
|
|
var visible = that.popup.visible();
|
|
if (key === keys.DOWN) {
|
|
e.preventDefault();
|
|
if (!visible) {
|
|
that.open();
|
|
if (!current) {
|
|
this.listView.focusFirst();
|
|
}
|
|
return;
|
|
}
|
|
if (current) {
|
|
this.listView.focusNext();
|
|
if (!this.listView.focus()) {
|
|
this.listView.focusLast();
|
|
}
|
|
} else {
|
|
this.listView.focusFirst();
|
|
}
|
|
} else if (key === keys.UP) {
|
|
if (visible) {
|
|
if (current) {
|
|
this.listView.focusPrev();
|
|
}
|
|
if (!this.listView.focus()) {
|
|
that.close();
|
|
}
|
|
}
|
|
e.preventDefault();
|
|
} else if (key === keys.LEFT && !isRtl || key === keys.RIGHT && isRtl) {
|
|
if (!hasValue) {
|
|
tag = tag ? tag.prev() : $(that.tagList[0].lastChild);
|
|
if (tag[0]) {
|
|
that.currentTag(tag);
|
|
}
|
|
}
|
|
} else if (key === keys.RIGHT && !isRtl || key === keys.LEFT && isRtl) {
|
|
if (!hasValue && tag) {
|
|
tag = tag.next();
|
|
that.currentTag(tag[0] ? tag : null);
|
|
}
|
|
} else if (key === keys.ENTER && visible) {
|
|
if (current) {
|
|
if (that.trigger(SELECT, { item: current })) {
|
|
that._close();
|
|
return;
|
|
}
|
|
that._select(current);
|
|
}
|
|
that._change();
|
|
that._close();
|
|
e.preventDefault();
|
|
} else if (key === keys.ESC) {
|
|
if (visible) {
|
|
e.preventDefault();
|
|
} else {
|
|
that.currentTag(null);
|
|
}
|
|
that.close();
|
|
} else if (key === keys.HOME) {
|
|
if (visible) {
|
|
this.listView.focusFirst();
|
|
} else if (!hasValue) {
|
|
tag = that.tagList[0].firstChild;
|
|
if (tag) {
|
|
that.currentTag($(tag));
|
|
}
|
|
}
|
|
} else if (key === keys.END) {
|
|
if (visible) {
|
|
this.listView.focusLast();
|
|
} else if (!hasValue) {
|
|
tag = that.tagList[0].lastChild;
|
|
if (tag) {
|
|
that.currentTag($(tag));
|
|
}
|
|
}
|
|
} else if ((key === keys.DELETE || key === keys.BACKSPACE) && !hasValue) {
|
|
if (key === keys.BACKSPACE && !tag) {
|
|
tag = $(that.tagList[0].lastChild);
|
|
}
|
|
if (tag && tag[0]) {
|
|
that._removeTag(tag);
|
|
}
|
|
} else {
|
|
clearTimeout(that._typingTimeout);
|
|
setTimeout(function () {
|
|
that._scale();
|
|
});
|
|
that._search();
|
|
}
|
|
},
|
|
_hideBusy: function () {
|
|
var that = this;
|
|
clearTimeout(that._busy);
|
|
that.input.attr('aria-busy', false);
|
|
that._loading.addClass(HIDDENCLASS);
|
|
that._request = false;
|
|
that._busy = null;
|
|
},
|
|
_showBusyHandler: function () {
|
|
this.input.attr('aria-busy', true);
|
|
this._loading.removeClass(HIDDENCLASS);
|
|
},
|
|
_showBusy: function () {
|
|
var that = this;
|
|
that._request = true;
|
|
if (that._busy) {
|
|
return;
|
|
}
|
|
that._busy = setTimeout(proxy(that._showBusyHandler, that), 100);
|
|
},
|
|
_placeholder: function (show, skipCaret) {
|
|
var that = this, input = that.input, active = activeElement();
|
|
if (show === undefined) {
|
|
show = false;
|
|
if (input[0] !== active) {
|
|
show = !that.listView.selectedDataItems()[0];
|
|
}
|
|
}
|
|
that._prev = '';
|
|
input.toggleClass('k-readonly', show).val(show ? that.options.placeholder : '');
|
|
if (input[0] === active && !skipCaret) {
|
|
kendo.caret(input[0], 0, 0);
|
|
}
|
|
that._scale();
|
|
},
|
|
_scale: function () {
|
|
var that = this, wrapper = that.wrapper, wrapperWidth = wrapper.width(), span = that._span.text(that.input.val()), textWidth;
|
|
if (!wrapper.is(':visible')) {
|
|
span.appendTo(document.documentElement);
|
|
wrapperWidth = textWidth = span.width() + 25;
|
|
span.appendTo(wrapper);
|
|
} else {
|
|
textWidth = span.width() + 25;
|
|
}
|
|
that.input.width(textWidth > wrapperWidth ? wrapperWidth : textWidth);
|
|
},
|
|
_option: function (dataValue, dataText, selected) {
|
|
var option = '<option';
|
|
if (dataValue !== undefined) {
|
|
dataValue += '';
|
|
if (dataValue.indexOf('"') !== -1) {
|
|
dataValue = dataValue.replace(quotRegExp, '"');
|
|
}
|
|
option += ' value="' + dataValue + '"';
|
|
}
|
|
if (selected) {
|
|
option += ' selected';
|
|
}
|
|
option += '>';
|
|
if (dataText !== undefined) {
|
|
option += kendo.htmlEncode(dataText);
|
|
}
|
|
return option += '</option>';
|
|
},
|
|
_render: function (data) {
|
|
var selectedItems = this.listView.selectedDataItems();
|
|
var values = this.listView.value();
|
|
var length = data.length;
|
|
var selectedIndex;
|
|
var options = '';
|
|
var dataItem;
|
|
var value;
|
|
var idx;
|
|
if (values.length !== selectedItems.length) {
|
|
selectedItems = this._buildSelectedItems(values);
|
|
}
|
|
var custom = {};
|
|
var optionsMap = {};
|
|
for (idx = 0; idx < length; idx++) {
|
|
dataItem = data[idx];
|
|
value = this._value(dataItem);
|
|
selectedIndex = this._selectedItemIndex(value, selectedItems);
|
|
if (selectedIndex !== -1) {
|
|
selectedItems.splice(selectedIndex, 1);
|
|
}
|
|
optionsMap[value] = idx;
|
|
options += this._option(value, this._text(dataItem), selectedIndex !== -1);
|
|
}
|
|
if (selectedItems.length) {
|
|
for (idx = 0; idx < selectedItems.length; idx++) {
|
|
dataItem = selectedItems[idx];
|
|
value = this._value(dataItem);
|
|
custom[value] = length;
|
|
optionsMap[value] = length;
|
|
length += 1;
|
|
options += this._option(value, this._text(dataItem), true);
|
|
}
|
|
}
|
|
this._customOptions = custom;
|
|
this._optionsMap = optionsMap;
|
|
this.element.html(options);
|
|
},
|
|
_buildSelectedItems: function (values) {
|
|
var valueField = this.options.dataValueField;
|
|
var textField = this.options.dataTextField;
|
|
var result = [];
|
|
var item;
|
|
for (var idx = 0; idx < values.length; idx++) {
|
|
item = {};
|
|
item[valueField] = values[idx];
|
|
item[textField] = values[idx];
|
|
result.push(item);
|
|
}
|
|
return result;
|
|
},
|
|
_selectedItemIndex: function (value, selectedItems) {
|
|
var valueGetter = this._value;
|
|
var idx = 0;
|
|
for (; idx < selectedItems.length; idx++) {
|
|
if (value === valueGetter(selectedItems[idx])) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
_search: function () {
|
|
var that = this;
|
|
that._typingTimeout = setTimeout(function () {
|
|
var value = that.input.val();
|
|
if (that._prev !== value) {
|
|
that._prev = value;
|
|
that.search(value);
|
|
}
|
|
}, that.options.delay);
|
|
},
|
|
_allowSelection: function () {
|
|
var max = this.options.maxSelectedItems;
|
|
return max === null || max > this.listView.value().length;
|
|
},
|
|
_angularTagItems: function (cmd) {
|
|
var that = this;
|
|
that.angular(cmd, function () {
|
|
return {
|
|
elements: that.tagList[0].children,
|
|
data: $.map(that.dataItems(), function (dataItem) {
|
|
return { dataItem: dataItem };
|
|
})
|
|
};
|
|
});
|
|
},
|
|
_selectValue: function (added, removed) {
|
|
var that = this;
|
|
var values = that.value();
|
|
var total = that.dataSource.total();
|
|
var tagList = that.tagList;
|
|
var getter = that._value;
|
|
var removedItem;
|
|
var addedItem;
|
|
var idx;
|
|
that._angularTagItems('cleanup');
|
|
if (that.options.tagMode === 'multiple') {
|
|
for (idx = removed.length - 1; idx > -1; idx--) {
|
|
removedItem = removed[idx];
|
|
tagList[0].removeChild(tagList[0].children[removedItem.position]);
|
|
that._setOption(getter(removedItem.dataItem), false);
|
|
}
|
|
for (idx = 0; idx < added.length; idx++) {
|
|
addedItem = added[idx];
|
|
tagList.append(that.tagTemplate(addedItem.dataItem));
|
|
that._setOption(getter(addedItem.dataItem), true);
|
|
}
|
|
} else {
|
|
if (!that._maxTotal || that._maxTotal < total) {
|
|
that._maxTotal = total;
|
|
}
|
|
tagList.html('');
|
|
if (values.length) {
|
|
tagList.append(that.tagTemplate({
|
|
values: values,
|
|
dataItems: that.dataItems(),
|
|
maxTotal: that._maxTotal,
|
|
currentTotal: total
|
|
}));
|
|
}
|
|
for (idx = removed.length - 1; idx > -1; idx--) {
|
|
that._setOption(getter(removed[idx].dataItem), false);
|
|
}
|
|
for (idx = 0; idx < added.length; idx++) {
|
|
that._setOption(getter(added[idx].dataItem), true);
|
|
}
|
|
}
|
|
that._angularTagItems('compile');
|
|
that._placeholder();
|
|
},
|
|
_select: function (candidate) {
|
|
var that = this;
|
|
if (that._state === REBIND) {
|
|
that._state = '';
|
|
}
|
|
if (!that._allowSelection()) {
|
|
return;
|
|
}
|
|
this.listView.select(candidate);
|
|
that._placeholder();
|
|
if (that._state === FILTER) {
|
|
that._state = ACCEPT;
|
|
that.listView.skipUpdate(true);
|
|
}
|
|
},
|
|
_input: function () {
|
|
var that = this, accessKey = that.element[0].accessKey, input = that._innerWrapper.children('input.k-input');
|
|
if (!input[0]) {
|
|
input = $('<input class="k-input" style="width: 25px" />').appendTo(that._innerWrapper);
|
|
}
|
|
that.element.removeAttr('accesskey');
|
|
that._focused = that.input = input.attr({
|
|
'accesskey': accessKey,
|
|
'autocomplete': 'off',
|
|
'role': 'listbox',
|
|
'aria-expanded': false
|
|
});
|
|
},
|
|
_tagList: function () {
|
|
var that = this, tagList = that._innerWrapper.children('ul');
|
|
if (!tagList[0]) {
|
|
tagList = $('<ul role="listbox" unselectable="on" class="k-reset"/>').appendTo(that._innerWrapper);
|
|
}
|
|
that.tagList = tagList;
|
|
},
|
|
_tagTemplate: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var tagTemplate = options.tagTemplate;
|
|
var hasDataSource = options.dataSource;
|
|
var isMultiple = options.tagMode === 'multiple';
|
|
var defaultTemplate;
|
|
if (that.element[0].length && !hasDataSource) {
|
|
options.dataTextField = options.dataTextField || 'text';
|
|
options.dataValueField = options.dataValueField || 'value';
|
|
}
|
|
defaultTemplate = isMultiple ? kendo.template('#:' + kendo.expr(options.dataTextField, 'data') + '#', { useWithBlock: false }) : kendo.template('#:values.length# item(s) selected');
|
|
that.tagTextTemplate = tagTemplate = tagTemplate ? kendo.template(tagTemplate) : defaultTemplate;
|
|
that.tagTemplate = function (data) {
|
|
return '<li class="k-button" unselectable="on"><span unselectable="on">' + tagTemplate(data) + '</span><span unselectable="on" class="k-select"><span unselectable="on" class="k-icon ' + (isMultiple ? 'k-i-close' : 'k-i-arrow-s') + '">' + (isMultiple ? 'delete' : 'open') + '</span></span></li>';
|
|
};
|
|
},
|
|
_loader: function () {
|
|
this._loading = $('<span class="k-icon k-loading ' + HIDDENCLASS + '"></span>').insertAfter(this.input);
|
|
},
|
|
_textContainer: function () {
|
|
var computedStyles = kendo.getComputedStyles(this.input[0], styles);
|
|
computedStyles.position = 'absolute';
|
|
computedStyles.visibility = 'hidden';
|
|
computedStyles.top = -3333;
|
|
computedStyles.left = -3333;
|
|
this._span = $('<span/>').css(computedStyles).appendTo(this.wrapper);
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper = element.parent('span.k-multiselect');
|
|
if (!wrapper[0]) {
|
|
wrapper = element.wrap('<div class="k-widget k-multiselect k-header" unselectable="on" />').parent();
|
|
wrapper[0].style.cssText = element[0].style.cssText;
|
|
wrapper[0].title = element[0].title;
|
|
$('<div class="k-multiselect-wrap k-floatwrap" unselectable="on" />').insertBefore(element);
|
|
}
|
|
that.wrapper = wrapper.addClass(element[0].className).css('display', '');
|
|
that._innerWrapper = $(wrapper[0].firstChild);
|
|
}
|
|
});
|
|
function compare(a, b) {
|
|
var length;
|
|
if (a === null && b !== null || a !== null && b === null) {
|
|
return false;
|
|
}
|
|
length = a.length;
|
|
if (length !== b.length) {
|
|
return false;
|
|
}
|
|
while (length--) {
|
|
if (a[length] !== b[length]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
ui.plugin(MultiSelect);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.slider', ['kendo.draganddrop'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'slider',
|
|
name: 'Slider',
|
|
category: 'web',
|
|
description: 'The Slider widget provides a rich input for selecting values or ranges of values.',
|
|
depends: ['draganddrop']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, Draggable = kendo.ui.Draggable, extend = $.extend, format = kendo.format, parse = kendo.parseFloat, proxy = $.proxy, isArray = $.isArray, math = Math, support = kendo.support, pointers = support.pointers, msPointers = support.msPointers, CHANGE = 'change', SLIDE = 'slide', NS = '.slider', MOUSE_DOWN = 'touchstart' + NS + ' mousedown' + NS, TRACK_MOUSE_DOWN = pointers ? 'pointerdown' + NS : msPointers ? 'MSPointerDown' + NS : MOUSE_DOWN, MOUSE_UP = 'touchend' + NS + ' mouseup' + NS, TRACK_MOUSE_UP = pointers ? 'pointerup' : msPointers ? 'MSPointerUp' + NS : MOUSE_UP, MOVE_SELECTION = 'moveSelection', KEY_DOWN = 'keydown' + NS, CLICK = 'click' + NS, MOUSE_OVER = 'mouseover' + NS, FOCUS = 'focus' + NS, BLUR = 'blur' + NS, DRAG_HANDLE = '.k-draghandle', TRACK_SELECTOR = '.k-slider-track', TICK_SELECTOR = '.k-tick', STATE_SELECTED = 'k-state-selected', STATE_FOCUSED = 'k-state-focused', STATE_DEFAULT = 'k-state-default', STATE_DISABLED = 'k-state-disabled', DISABLED = 'disabled', UNDEFINED = 'undefined', TABINDEX = 'tabindex', getTouches = kendo.getTouches;
|
|
var SliderBase = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
that._distance = round(options.max - options.min);
|
|
that._isHorizontal = options.orientation == 'horizontal';
|
|
that._isRtl = that._isHorizontal && kendo.support.isRtl(element);
|
|
that._position = that._isHorizontal ? 'left' : 'bottom';
|
|
that._sizeFn = that._isHorizontal ? 'width' : 'height';
|
|
that._outerSize = that._isHorizontal ? 'outerWidth' : 'outerHeight';
|
|
options.tooltip.format = options.tooltip.enabled ? options.tooltip.format || '{0}' : '{0}';
|
|
if (options.smallStep <= 0) {
|
|
throw new Error('Kendo UI Slider smallStep must be a positive number.');
|
|
}
|
|
that._createHtml();
|
|
that.wrapper = that.element.closest('.k-slider');
|
|
that._trackDiv = that.wrapper.find(TRACK_SELECTOR);
|
|
that._setTrackDivWidth();
|
|
that._maxSelection = that._trackDiv[that._sizeFn]();
|
|
that._sliderItemsInit();
|
|
that._reset();
|
|
that._tabindex(that.wrapper.find(DRAG_HANDLE));
|
|
that[options.enabled ? 'enable' : 'disable']();
|
|
var rtlDirectionSign = kendo.support.isRtl(that.wrapper) ? -1 : 1;
|
|
that._keyMap = {
|
|
37: step(-1 * rtlDirectionSign * options.smallStep),
|
|
40: step(-options.smallStep),
|
|
39: step(+1 * rtlDirectionSign * options.smallStep),
|
|
38: step(+options.smallStep),
|
|
35: setValue(options.max),
|
|
36: setValue(options.min),
|
|
33: step(+options.largeStep),
|
|
34: step(-options.largeStep)
|
|
};
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
SLIDE
|
|
],
|
|
options: {
|
|
enabled: true,
|
|
min: 0,
|
|
max: 10,
|
|
smallStep: 1,
|
|
largeStep: 5,
|
|
orientation: 'horizontal',
|
|
tickPlacement: 'both',
|
|
tooltip: {
|
|
enabled: true,
|
|
format: '{0}'
|
|
}
|
|
},
|
|
_resize: function () {
|
|
this._setTrackDivWidth();
|
|
this.wrapper.find('.k-slider-items').remove();
|
|
this._maxSelection = this._trackDiv[this._sizeFn]();
|
|
this._sliderItemsInit();
|
|
this._refresh();
|
|
if (this.options.enabled) {
|
|
this.enable(true);
|
|
}
|
|
},
|
|
_sliderItemsInit: function () {
|
|
var that = this, options = that.options;
|
|
var sizeBetweenTicks = that._maxSelection / ((options.max - options.min) / options.smallStep);
|
|
var pixelWidths = that._calculateItemsWidth(math.floor(that._distance / options.smallStep));
|
|
if (options.tickPlacement != 'none' && sizeBetweenTicks >= 2) {
|
|
that._trackDiv.before(createSliderItems(options, that._distance));
|
|
that._setItemsWidth(pixelWidths);
|
|
that._setItemsTitle();
|
|
}
|
|
that._calculateSteps(pixelWidths);
|
|
if (options.tickPlacement != 'none' && sizeBetweenTicks >= 2 && options.largeStep >= options.smallStep) {
|
|
that._setItemsLargeTick();
|
|
}
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.wrapper);
|
|
},
|
|
_setTrackDivWidth: function () {
|
|
var that = this, trackDivPosition = parseFloat(that._trackDiv.css(that._isRtl ? 'right' : that._position), 10) * 2;
|
|
that._trackDiv[that._sizeFn](that.wrapper[that._sizeFn]() - 2 - trackDivPosition);
|
|
},
|
|
_setItemsWidth: function (pixelWidths) {
|
|
var that = this, options = that.options, first = 0, last = pixelWidths.length - 1, items = that.wrapper.find(TICK_SELECTOR), i, paddingTop = 0, bordersWidth = 2, count = items.length, selection = 0;
|
|
for (i = 0; i < count - 2; i++) {
|
|
$(items[i + 1])[that._sizeFn](pixelWidths[i]);
|
|
}
|
|
if (that._isHorizontal) {
|
|
$(items[first]).addClass('k-first')[that._sizeFn](pixelWidths[last - 1]);
|
|
$(items[last]).addClass('k-last')[that._sizeFn](pixelWidths[last]);
|
|
} else {
|
|
$(items[last]).addClass('k-first')[that._sizeFn](pixelWidths[last]);
|
|
$(items[first]).addClass('k-last')[that._sizeFn](pixelWidths[last - 1]);
|
|
}
|
|
if (that._distance % options.smallStep !== 0 && !that._isHorizontal) {
|
|
for (i = 0; i < pixelWidths.length; i++) {
|
|
selection += pixelWidths[i];
|
|
}
|
|
paddingTop = that._maxSelection - selection;
|
|
paddingTop += parseFloat(that._trackDiv.css(that._position), 10) + bordersWidth;
|
|
that.wrapper.find('.k-slider-items').css('padding-top', paddingTop);
|
|
}
|
|
},
|
|
_setItemsTitle: function () {
|
|
var that = this, options = that.options, items = that.wrapper.find(TICK_SELECTOR), titleNumber = options.min, count = items.length, i = that._isHorizontal && !that._isRtl ? 0 : count - 1, limit = that._isHorizontal && !that._isRtl ? count : -1, increment = that._isHorizontal && !that._isRtl ? 1 : -1;
|
|
for (; i - limit !== 0; i += increment) {
|
|
$(items[i]).attr('title', format(options.tooltip.format, round(titleNumber)));
|
|
titleNumber += options.smallStep;
|
|
}
|
|
},
|
|
_setItemsLargeTick: function () {
|
|
var that = this, options = that.options, items = that.wrapper.find(TICK_SELECTOR), i = 0, item, value;
|
|
if (removeFraction(options.largeStep) % removeFraction(options.smallStep) === 0 || that._distance / options.largeStep >= 3) {
|
|
if (!that._isHorizontal && !that._isRtl) {
|
|
items = $.makeArray(items).reverse();
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
item = $(items[i]);
|
|
value = that._values[i];
|
|
var valueWithoutFraction = round(removeFraction(value - this.options.min));
|
|
if (valueWithoutFraction % removeFraction(options.smallStep) === 0 && valueWithoutFraction % removeFraction(options.largeStep) === 0) {
|
|
item.addClass('k-tick-large').html('<span class=\'k-label\'>' + item.attr('title') + '</span>');
|
|
if (i !== 0 && i !== items.length - 1) {
|
|
item.css('line-height', item[that._sizeFn]() + 'px');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_calculateItemsWidth: function (itemsCount) {
|
|
var that = this, options = that.options, trackDivSize = parseFloat(that._trackDiv.css(that._sizeFn)) + 1, pixelStep = trackDivSize / that._distance, itemWidth, pixelWidths, i;
|
|
if (that._distance / options.smallStep - math.floor(that._distance / options.smallStep) > 0) {
|
|
trackDivSize -= that._distance % options.smallStep * pixelStep;
|
|
}
|
|
itemWidth = trackDivSize / itemsCount;
|
|
pixelWidths = [];
|
|
for (i = 0; i < itemsCount - 1; i++) {
|
|
pixelWidths[i] = itemWidth;
|
|
}
|
|
pixelWidths[itemsCount - 1] = pixelWidths[itemsCount] = itemWidth / 2;
|
|
return that._roundWidths(pixelWidths);
|
|
},
|
|
_roundWidths: function (pixelWidthsArray) {
|
|
var balance = 0, count = pixelWidthsArray.length, i;
|
|
for (i = 0; i < count; i++) {
|
|
balance += pixelWidthsArray[i] - math.floor(pixelWidthsArray[i]);
|
|
pixelWidthsArray[i] = math.floor(pixelWidthsArray[i]);
|
|
}
|
|
balance = math.round(balance);
|
|
return this._addAdditionalSize(balance, pixelWidthsArray);
|
|
},
|
|
_addAdditionalSize: function (additionalSize, pixelWidthsArray) {
|
|
if (additionalSize === 0) {
|
|
return pixelWidthsArray;
|
|
}
|
|
var step = parseFloat(pixelWidthsArray.length - 1) / parseFloat(additionalSize == 1 ? additionalSize : additionalSize - 1), i;
|
|
for (i = 0; i < additionalSize; i++) {
|
|
pixelWidthsArray[parseInt(math.round(step * i), 10)] += 1;
|
|
}
|
|
return pixelWidthsArray;
|
|
},
|
|
_calculateSteps: function (pixelWidths) {
|
|
var that = this, options = that.options, val = options.min, selection = 0, itemsCount = math.ceil(that._distance / options.smallStep), i = 1, lastItem;
|
|
itemsCount += that._distance / options.smallStep % 1 === 0 ? 1 : 0;
|
|
pixelWidths.splice(0, 0, pixelWidths[itemsCount - 2] * 2);
|
|
pixelWidths.splice(itemsCount - 1, 1, pixelWidths.pop() * 2);
|
|
that._pixelSteps = [selection];
|
|
that._values = [val];
|
|
if (itemsCount === 0) {
|
|
return;
|
|
}
|
|
while (i < itemsCount) {
|
|
selection += (pixelWidths[i - 1] + pixelWidths[i]) / 2;
|
|
that._pixelSteps[i] = selection;
|
|
val += options.smallStep;
|
|
that._values[i] = round(val);
|
|
i++;
|
|
}
|
|
lastItem = that._distance % options.smallStep === 0 ? itemsCount - 1 : itemsCount;
|
|
that._pixelSteps[lastItem] = that._maxSelection;
|
|
that._values[lastItem] = options.max;
|
|
if (that._isRtl) {
|
|
that._pixelSteps.reverse();
|
|
that._values.reverse();
|
|
}
|
|
},
|
|
_getValueFromPosition: function (mousePosition, dragableArea) {
|
|
var that = this, options = that.options, step = math.max(options.smallStep * (that._maxSelection / that._distance), 0), position = 0, halfStep = step / 2, i;
|
|
if (that._isHorizontal) {
|
|
position = mousePosition - dragableArea.startPoint;
|
|
if (that._isRtl) {
|
|
position = that._maxSelection - position;
|
|
}
|
|
} else {
|
|
position = dragableArea.startPoint - mousePosition;
|
|
}
|
|
if (that._maxSelection - (parseInt(that._maxSelection % step, 10) - 3) / 2 < position) {
|
|
return options.max;
|
|
}
|
|
for (i = 0; i < that._pixelSteps.length; i++) {
|
|
if (math.abs(that._pixelSteps[i] - position) - 1 <= halfStep) {
|
|
return round(that._values[i]);
|
|
}
|
|
}
|
|
},
|
|
_getFormattedValue: function (val, drag) {
|
|
var that = this, html = '', tooltip = that.options.tooltip, tooltipTemplate, selectionStart, selectionEnd;
|
|
if (isArray(val)) {
|
|
selectionStart = val[0];
|
|
selectionEnd = val[1];
|
|
} else if (drag && drag.type) {
|
|
selectionStart = drag.selectionStart;
|
|
selectionEnd = drag.selectionEnd;
|
|
}
|
|
if (drag) {
|
|
tooltipTemplate = drag.tooltipTemplate;
|
|
}
|
|
if (!tooltipTemplate && tooltip.template) {
|
|
tooltipTemplate = kendo.template(tooltip.template);
|
|
}
|
|
if (isArray(val) || drag && drag.type) {
|
|
if (tooltipTemplate) {
|
|
html = tooltipTemplate({
|
|
selectionStart: selectionStart,
|
|
selectionEnd: selectionEnd
|
|
});
|
|
} else {
|
|
selectionStart = format(tooltip.format, selectionStart);
|
|
selectionEnd = format(tooltip.format, selectionEnd);
|
|
html = selectionStart + ' - ' + selectionEnd;
|
|
}
|
|
} else {
|
|
if (drag) {
|
|
drag.val = val;
|
|
}
|
|
if (tooltipTemplate) {
|
|
html = tooltipTemplate({ value: val });
|
|
} else {
|
|
html = format(tooltip.format, val);
|
|
}
|
|
}
|
|
return html;
|
|
},
|
|
_getDraggableArea: function () {
|
|
var that = this, offset = kendo.getOffset(that._trackDiv);
|
|
return {
|
|
startPoint: that._isHorizontal ? offset.left : offset.top + that._maxSelection,
|
|
endPoint: that._isHorizontal ? offset.left + that._maxSelection : offset.top
|
|
};
|
|
},
|
|
_createHtml: function () {
|
|
var that = this, element = that.element, options = that.options, inputs = element.find('input');
|
|
if (inputs.length == 2) {
|
|
inputs.eq(0).prop('value', formatValue(options.selectionStart));
|
|
inputs.eq(1).prop('value', formatValue(options.selectionEnd));
|
|
} else {
|
|
element.prop('value', formatValue(options.value));
|
|
}
|
|
element.wrap(createWrapper(options, element, that._isHorizontal)).hide();
|
|
if (options.showButtons) {
|
|
element.before(createButton(options, 'increase', that._isHorizontal)).before(createButton(options, 'decrease', that._isHorizontal));
|
|
}
|
|
element.before(createTrack(options, element));
|
|
},
|
|
_focus: function (e) {
|
|
var that = this, target = e.target, val = that.value(), drag = that._drag;
|
|
if (!drag) {
|
|
if (target == that.wrapper.find(DRAG_HANDLE).eq(0)[0]) {
|
|
drag = that._firstHandleDrag;
|
|
that._activeHandle = 0;
|
|
} else {
|
|
drag = that._lastHandleDrag;
|
|
that._activeHandle = 1;
|
|
}
|
|
val = val[that._activeHandle];
|
|
}
|
|
$(target).addClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
if (drag) {
|
|
that._activeHandleDrag = drag;
|
|
drag.selectionStart = that.options.selectionStart;
|
|
drag.selectionEnd = that.options.selectionEnd;
|
|
drag._updateTooltip(val);
|
|
}
|
|
},
|
|
_focusWithMouse: function (target) {
|
|
target = $(target);
|
|
var that = this, idx = target.is(DRAG_HANDLE) ? target.index() : 0;
|
|
window.setTimeout(function () {
|
|
that.wrapper.find(DRAG_HANDLE)[idx == 2 ? 1 : 0].focus();
|
|
}, 1);
|
|
that._setTooltipTimeout();
|
|
},
|
|
_blur: function (e) {
|
|
var that = this, drag = that._activeHandleDrag;
|
|
$(e.target).removeClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
if (drag) {
|
|
drag._removeTooltip();
|
|
delete that._activeHandleDrag;
|
|
delete that._activeHandle;
|
|
}
|
|
},
|
|
_setTooltipTimeout: function () {
|
|
var that = this;
|
|
that._tooltipTimeout = window.setTimeout(function () {
|
|
var drag = that._drag || that._activeHandleDrag;
|
|
if (drag) {
|
|
drag._removeTooltip();
|
|
}
|
|
}, 300);
|
|
},
|
|
_clearTooltipTimeout: function () {
|
|
var that = this;
|
|
window.clearTimeout(this._tooltipTimeout);
|
|
var drag = that._drag || that._activeHandleDrag;
|
|
if (drag && drag.tooltipDiv) {
|
|
drag.tooltipDiv.stop(true, false).css('opacity', 1);
|
|
}
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._form = form.on('reset', proxy(that._formResetHandler, that));
|
|
}
|
|
},
|
|
destroy: function () {
|
|
if (this._form) {
|
|
this._form.off('reset', this._formResetHandler);
|
|
}
|
|
Widget.fn.destroy.call(this);
|
|
}
|
|
});
|
|
function createWrapper(options, element, isHorizontal) {
|
|
var orientationCssClass = isHorizontal ? ' k-slider-horizontal' : ' k-slider-vertical', style = options.style ? options.style : element.attr('style'), cssClasses = element.attr('class') ? ' ' + element.attr('class') : '', tickPlacementCssClass = '';
|
|
if (options.tickPlacement == 'bottomRight') {
|
|
tickPlacementCssClass = ' k-slider-bottomright';
|
|
} else if (options.tickPlacement == 'topLeft') {
|
|
tickPlacementCssClass = ' k-slider-topleft';
|
|
}
|
|
style = style ? ' style=\'' + style + '\'' : '';
|
|
return '<div class=\'k-widget k-slider' + orientationCssClass + cssClasses + '\'' + style + '>' + '<div class=\'k-slider-wrap' + (options.showButtons ? ' k-slider-buttons' : '') + tickPlacementCssClass + '\'></div></div>';
|
|
}
|
|
function createButton(options, type, isHorizontal) {
|
|
var buttonCssClass = '';
|
|
if (type == 'increase') {
|
|
buttonCssClass = isHorizontal ? 'k-i-arrow-e' : 'k-i-arrow-n';
|
|
} else {
|
|
buttonCssClass = isHorizontal ? 'k-i-arrow-w' : 'k-i-arrow-s';
|
|
}
|
|
return '<a class=\'k-button k-button-' + type + '\'><span class=\'k-icon ' + buttonCssClass + '\' title=\'' + options[type + 'ButtonTitle'] + '\'>' + options[type + 'ButtonTitle'] + '</span></a>';
|
|
}
|
|
function createSliderItems(options, distance) {
|
|
var result = '<ul class=\'k-reset k-slider-items\'>', count = math.floor(round(distance / options.smallStep)) + 1, i;
|
|
for (i = 0; i < count; i++) {
|
|
result += '<li class=\'k-tick\' role=\'presentation\'> </li>';
|
|
}
|
|
result += '</ul>';
|
|
return result;
|
|
}
|
|
function createTrack(options, element) {
|
|
var dragHandleCount = element.is('input') ? 1 : 2, firstDragHandleTitle = dragHandleCount == 2 ? options.leftDragHandleTitle : options.dragHandleTitle;
|
|
return '<div class=\'k-slider-track\'><div class=\'k-slider-selection\'><!-- --></div>' + '<a href=\'#\' class=\'k-draghandle\' title=\'' + firstDragHandleTitle + '\' role=\'slider\' aria-valuemin=\'' + options.min + '\' aria-valuemax=\'' + options.max + '\' aria-valuenow=\'' + (dragHandleCount > 1 ? options.selectionStart || options.min : options.value || options.min) + '\'>Drag</a>' + (dragHandleCount > 1 ? '<a href=\'#\' class=\'k-draghandle\' title=\'' + options.rightDragHandleTitle + '\'role=\'slider\' aria-valuemin=\'' + options.min + '\' aria-valuemax=\'' + options.max + '\' aria-valuenow=\'' + (options.selectionEnd || options.max) + '\'>Drag</a>' : '') + '</div>';
|
|
}
|
|
function step(stepValue) {
|
|
return function (value) {
|
|
return value + stepValue;
|
|
};
|
|
}
|
|
function setValue(value) {
|
|
return function () {
|
|
return value;
|
|
};
|
|
}
|
|
function formatValue(value) {
|
|
return (value + '').replace('.', kendo.cultures.current.numberFormat['.']);
|
|
}
|
|
function calculatePrecision(value) {
|
|
var number = value.toString();
|
|
var precision = 0;
|
|
number = number.split('.');
|
|
if (number[1]) {
|
|
precision = number[1].length;
|
|
}
|
|
precision = precision > 10 ? 10 : precision;
|
|
return precision;
|
|
}
|
|
function round(value) {
|
|
var precision, power;
|
|
value = parseFloat(value, 10);
|
|
precision = calculatePrecision(value);
|
|
power = math.pow(10, precision || 0);
|
|
return math.round(value * power) / power;
|
|
}
|
|
function parseAttr(element, name) {
|
|
var value = parse(element.getAttribute(name));
|
|
if (value === null) {
|
|
value = undefined;
|
|
}
|
|
return value;
|
|
}
|
|
function defined(value) {
|
|
return typeof value !== UNDEFINED;
|
|
}
|
|
function removeFraction(value) {
|
|
return value * 10000;
|
|
}
|
|
var Slider = SliderBase.extend({
|
|
init: function (element, options) {
|
|
var that = this, dragHandle;
|
|
element.type = 'text';
|
|
options = extend({}, {
|
|
value: parseAttr(element, 'value'),
|
|
min: parseAttr(element, 'min'),
|
|
max: parseAttr(element, 'max'),
|
|
smallStep: parseAttr(element, 'step')
|
|
}, options);
|
|
element = $(element);
|
|
if (options && options.enabled === undefined) {
|
|
options.enabled = !element.is('[disabled]');
|
|
}
|
|
SliderBase.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
if (!defined(options.value) || options.value === null) {
|
|
options.value = options.min;
|
|
element.prop('value', formatValue(options.min));
|
|
}
|
|
options.value = math.max(math.min(options.value, options.max), options.min);
|
|
dragHandle = that.wrapper.find(DRAG_HANDLE);
|
|
this._selection = new Slider.Selection(dragHandle, that, options);
|
|
that._drag = new Slider.Drag(dragHandle, '', that, options);
|
|
},
|
|
options: {
|
|
name: 'Slider',
|
|
showButtons: true,
|
|
increaseButtonTitle: 'Increase',
|
|
decreaseButtonTitle: 'Decrease',
|
|
dragHandleTitle: 'drag',
|
|
tooltip: { format: '{0:#,#.##}' },
|
|
value: null
|
|
},
|
|
enable: function (enable) {
|
|
var that = this, options = that.options, clickHandler, move;
|
|
that.disable();
|
|
if (enable === false) {
|
|
return;
|
|
}
|
|
that.wrapper.removeClass(STATE_DISABLED).addClass(STATE_DEFAULT);
|
|
that.wrapper.find('input').removeAttr(DISABLED);
|
|
clickHandler = function (e) {
|
|
var touch = getTouches(e)[0];
|
|
if (!touch) {
|
|
return;
|
|
}
|
|
var mousePosition = that._isHorizontal ? touch.location.pageX : touch.location.pageY, dragableArea = that._getDraggableArea(), target = $(e.target);
|
|
if (target.hasClass('k-draghandle')) {
|
|
target.addClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
return;
|
|
}
|
|
that._update(that._getValueFromPosition(mousePosition, dragableArea));
|
|
that._focusWithMouse(e.target);
|
|
that._drag.dragstart(e);
|
|
e.preventDefault();
|
|
};
|
|
that.wrapper.find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).on(TRACK_MOUSE_DOWN, clickHandler).end().on(TRACK_MOUSE_DOWN, function () {
|
|
$(document.documentElement).one('selectstart', kendo.preventDefault);
|
|
}).on(TRACK_MOUSE_UP, function () {
|
|
that._drag._end();
|
|
});
|
|
that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, 0).on(MOUSE_UP, function () {
|
|
that._setTooltipTimeout();
|
|
}).on(CLICK, function (e) {
|
|
that._focusWithMouse(e.target);
|
|
e.preventDefault();
|
|
}).on(FOCUS, proxy(that._focus, that)).on(BLUR, proxy(that._blur, that));
|
|
move = proxy(function (sign) {
|
|
var newVal = that._nextValueByIndex(that._valueIndex + sign * 1);
|
|
that._setValueInRange(newVal);
|
|
that._drag._updateTooltip(newVal);
|
|
}, that);
|
|
if (options.showButtons) {
|
|
var mouseDownHandler = proxy(function (e, sign) {
|
|
this._clearTooltipTimeout();
|
|
if (e.which === 1 || support.touch && e.which === 0) {
|
|
move(sign);
|
|
this.timeout = setTimeout(proxy(function () {
|
|
this.timer = setInterval(function () {
|
|
move(sign);
|
|
}, 60);
|
|
}, this), 200);
|
|
}
|
|
}, that);
|
|
that.wrapper.find('.k-button').on(MOUSE_UP, proxy(function (e) {
|
|
this._clearTimer();
|
|
that._focusWithMouse(e.target);
|
|
}, that)).on(MOUSE_OVER, function (e) {
|
|
$(e.currentTarget).addClass('k-state-hover');
|
|
}).on('mouseout' + NS, proxy(function (e) {
|
|
$(e.currentTarget).removeClass('k-state-hover');
|
|
this._clearTimer();
|
|
}, that)).eq(0).on(MOUSE_DOWN, proxy(function (e) {
|
|
mouseDownHandler(e, 1);
|
|
}, that)).click(false).end().eq(1).on(MOUSE_DOWN, proxy(function (e) {
|
|
mouseDownHandler(e, -1);
|
|
}, that)).click(kendo.preventDefault);
|
|
}
|
|
that.wrapper.find(DRAG_HANDLE).off(KEY_DOWN, false).on(KEY_DOWN, proxy(this._keydown, that));
|
|
options.enabled = true;
|
|
},
|
|
disable: function () {
|
|
var that = this;
|
|
that.wrapper.removeClass(STATE_DEFAULT).addClass(STATE_DISABLED);
|
|
$(that.element).prop(DISABLED, DISABLED);
|
|
that.wrapper.find('.k-button').off(MOUSE_DOWN).on(MOUSE_DOWN, kendo.preventDefault).off(MOUSE_UP).on(MOUSE_UP, kendo.preventDefault).off('mouseleave' + NS).on('mouseleave' + NS, kendo.preventDefault).off(MOUSE_OVER).on(MOUSE_OVER, kendo.preventDefault);
|
|
that.wrapper.find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).off(TRACK_MOUSE_DOWN).off(TRACK_MOUSE_UP);
|
|
that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, -1).off(MOUSE_UP).off(KEY_DOWN).off(CLICK).off(FOCUS).off(BLUR);
|
|
that.options.enabled = false;
|
|
},
|
|
_update: function (val) {
|
|
var that = this, change = that.value() != val;
|
|
that.value(val);
|
|
if (change) {
|
|
that.trigger(CHANGE, { value: that.options.value });
|
|
}
|
|
},
|
|
value: function (value) {
|
|
var that = this, options = that.options;
|
|
value = round(value);
|
|
if (isNaN(value)) {
|
|
return options.value;
|
|
}
|
|
if (value >= options.min && value <= options.max) {
|
|
if (options.value != value) {
|
|
that.element.prop('value', formatValue(value));
|
|
options.value = value;
|
|
that._refreshAriaAttr(value);
|
|
that._refresh();
|
|
}
|
|
}
|
|
},
|
|
_refresh: function () {
|
|
this.trigger(MOVE_SELECTION, { value: this.options.value });
|
|
},
|
|
_refreshAriaAttr: function (value) {
|
|
var that = this, drag = that._drag, formattedValue;
|
|
if (drag && drag._tooltipDiv) {
|
|
formattedValue = drag._tooltipDiv.text();
|
|
} else {
|
|
formattedValue = that._getFormattedValue(value, null);
|
|
}
|
|
this.wrapper.find(DRAG_HANDLE).attr('aria-valuenow', value).attr('aria-valuetext', formattedValue);
|
|
},
|
|
_clearTimer: function () {
|
|
clearTimeout(this.timeout);
|
|
clearInterval(this.timer);
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
if (e.keyCode in that._keyMap) {
|
|
that._clearTooltipTimeout();
|
|
that._setValueInRange(that._keyMap[e.keyCode](that.options.value));
|
|
that._drag._updateTooltip(that.value());
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_setValueInRange: function (val) {
|
|
var that = this, options = that.options;
|
|
val = round(val);
|
|
if (isNaN(val)) {
|
|
that._update(options.min);
|
|
return;
|
|
}
|
|
val = math.max(math.min(val, options.max), options.min);
|
|
that._update(val);
|
|
},
|
|
_nextValueByIndex: function (index) {
|
|
var count = this._values.length;
|
|
if (this._isRtl) {
|
|
index = count - 1 - index;
|
|
}
|
|
return this._values[math.max(0, math.min(index, count - 1))];
|
|
},
|
|
_formResetHandler: function () {
|
|
var that = this, min = that.options.min;
|
|
setTimeout(function () {
|
|
var value = that.element[0].value;
|
|
that.value(value === '' || isNaN(value) ? min : value);
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
SliderBase.fn.destroy.call(that);
|
|
that.wrapper.off(NS).find('.k-button').off(NS).end().find(DRAG_HANDLE).off(NS).end().find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).off(NS).end();
|
|
that._drag.draggable.destroy();
|
|
that._drag._removeTooltip(true);
|
|
}
|
|
});
|
|
Slider.Selection = function (dragHandle, that, options) {
|
|
function moveSelection(val) {
|
|
var selectionValue = val - options.min, index = that._valueIndex = math.ceil(round(selectionValue / options.smallStep)), selection = parseInt(that._pixelSteps[index], 10), selectionDiv = that._trackDiv.find('.k-slider-selection'), halfDragHanndle = parseInt(dragHandle[that._outerSize]() / 2, 10), rtlCorrection = that._isRtl ? 2 : 0;
|
|
selectionDiv[that._sizeFn](that._isRtl ? that._maxSelection - selection : selection);
|
|
dragHandle.css(that._position, selection - halfDragHanndle - rtlCorrection);
|
|
}
|
|
moveSelection(options.value);
|
|
that.bind([
|
|
CHANGE,
|
|
SLIDE,
|
|
MOVE_SELECTION
|
|
], function (e) {
|
|
moveSelection(parseFloat(e.value, 10));
|
|
});
|
|
};
|
|
Slider.Drag = function (element, type, owner, options) {
|
|
var that = this;
|
|
that.owner = owner;
|
|
that.options = options;
|
|
that.element = element;
|
|
that.type = type;
|
|
that.draggable = new Draggable(element, {
|
|
distance: 0,
|
|
dragstart: proxy(that._dragstart, that),
|
|
drag: proxy(that.drag, that),
|
|
dragend: proxy(that.dragend, that),
|
|
dragcancel: proxy(that.dragcancel, that)
|
|
});
|
|
element.click(false);
|
|
};
|
|
Slider.Drag.prototype = {
|
|
dragstart: function (e) {
|
|
this.owner._activeDragHandle = this;
|
|
this.draggable.userEvents.cancel();
|
|
this._dragstart(e);
|
|
this.dragend();
|
|
},
|
|
_dragstart: function (e) {
|
|
var that = this, owner = that.owner, options = that.options;
|
|
if (!options.enabled) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
this.owner._activeDragHandle = this;
|
|
owner.element.off(MOUSE_OVER);
|
|
owner.wrapper.find('.' + STATE_FOCUSED).removeClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
that.element.addClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
$(document.documentElement).css('cursor', 'pointer');
|
|
that.dragableArea = owner._getDraggableArea();
|
|
that.step = math.max(options.smallStep * (owner._maxSelection / owner._distance), 0);
|
|
if (that.type) {
|
|
that.selectionStart = options.selectionStart;
|
|
that.selectionEnd = options.selectionEnd;
|
|
owner._setZIndex(that.type);
|
|
} else {
|
|
that.oldVal = that.val = options.value;
|
|
}
|
|
that._removeTooltip(true);
|
|
that._createTooltip();
|
|
},
|
|
_createTooltip: function () {
|
|
var that = this, owner = that.owner, tooltip = that.options.tooltip, html = '', wnd = $(window), tooltipTemplate, colloutCssClass;
|
|
if (!tooltip.enabled) {
|
|
return;
|
|
}
|
|
if (tooltip.template) {
|
|
tooltipTemplate = that.tooltipTemplate = kendo.template(tooltip.template);
|
|
}
|
|
$('.k-slider-tooltip').remove();
|
|
that.tooltipDiv = $('<div class=\'k-widget k-tooltip k-slider-tooltip\'><!-- --></div>').appendTo(document.body);
|
|
html = owner._getFormattedValue(that.val || owner.value(), that);
|
|
if (!that.type) {
|
|
colloutCssClass = 'k-callout-' + (owner._isHorizontal ? 's' : 'e');
|
|
that.tooltipInnerDiv = '<div class=\'k-callout ' + colloutCssClass + '\'><!-- --></div>';
|
|
html += that.tooltipInnerDiv;
|
|
}
|
|
that.tooltipDiv.html(html);
|
|
that._scrollOffset = {
|
|
top: wnd.scrollTop(),
|
|
left: wnd.scrollLeft()
|
|
};
|
|
that.moveTooltip();
|
|
},
|
|
drag: function (e) {
|
|
var that = this, owner = that.owner, x = e.x.location, y = e.y.location, startPoint = that.dragableArea.startPoint, endPoint = that.dragableArea.endPoint, slideParams;
|
|
e.preventDefault();
|
|
if (owner._isHorizontal) {
|
|
if (owner._isRtl) {
|
|
that.val = that.constrainValue(x, startPoint, endPoint, x < endPoint);
|
|
} else {
|
|
that.val = that.constrainValue(x, startPoint, endPoint, x >= endPoint);
|
|
}
|
|
} else {
|
|
that.val = that.constrainValue(y, endPoint, startPoint, y <= endPoint);
|
|
}
|
|
if (that.oldVal != that.val) {
|
|
that.oldVal = that.val;
|
|
if (that.type) {
|
|
if (that.type == 'firstHandle') {
|
|
if (that.val < that.selectionEnd) {
|
|
that.selectionStart = that.val;
|
|
} else {
|
|
that.selectionStart = that.selectionEnd = that.val;
|
|
}
|
|
} else {
|
|
if (that.val > that.selectionStart) {
|
|
that.selectionEnd = that.val;
|
|
} else {
|
|
that.selectionStart = that.selectionEnd = that.val;
|
|
}
|
|
}
|
|
slideParams = {
|
|
values: [
|
|
that.selectionStart,
|
|
that.selectionEnd
|
|
],
|
|
value: [
|
|
that.selectionStart,
|
|
that.selectionEnd
|
|
]
|
|
};
|
|
} else {
|
|
slideParams = { value: that.val };
|
|
}
|
|
owner.trigger(SLIDE, slideParams);
|
|
}
|
|
that._updateTooltip(that.val);
|
|
},
|
|
_updateTooltip: function (val) {
|
|
var that = this, options = that.options, tooltip = options.tooltip, html = '';
|
|
if (!tooltip.enabled) {
|
|
return;
|
|
}
|
|
if (!that.tooltipDiv) {
|
|
that._createTooltip();
|
|
}
|
|
html = that.owner._getFormattedValue(round(val), that);
|
|
if (!that.type) {
|
|
html += that.tooltipInnerDiv;
|
|
}
|
|
that.tooltipDiv.html(html);
|
|
that.moveTooltip();
|
|
},
|
|
dragcancel: function () {
|
|
this.owner._refresh();
|
|
$(document.documentElement).css('cursor', '');
|
|
return this._end();
|
|
},
|
|
dragend: function () {
|
|
var that = this, owner = that.owner;
|
|
$(document.documentElement).css('cursor', '');
|
|
if (that.type) {
|
|
owner._update(that.selectionStart, that.selectionEnd);
|
|
} else {
|
|
owner._update(that.val);
|
|
that.draggable.userEvents._disposeAll();
|
|
}
|
|
that.draggable.userEvents.cancel();
|
|
return that._end();
|
|
},
|
|
_end: function () {
|
|
var that = this, owner = that.owner;
|
|
owner._focusWithMouse(that.element);
|
|
owner.element.on(MOUSE_OVER);
|
|
return false;
|
|
},
|
|
_removeTooltip: function (noAnimation) {
|
|
var that = this, owner = that.owner;
|
|
if (that.tooltipDiv && owner.options.tooltip.enabled && owner.options.enabled) {
|
|
if (noAnimation) {
|
|
that.tooltipDiv.remove();
|
|
that.tooltipDiv = null;
|
|
} else {
|
|
that.tooltipDiv.fadeOut('slow', function () {
|
|
$(this).remove();
|
|
that.tooltipDiv = null;
|
|
});
|
|
}
|
|
}
|
|
},
|
|
moveTooltip: function () {
|
|
var that = this, owner = that.owner, top = 0, left = 0, element = that.element, offset = kendo.getOffset(element), margin = 8, viewport = $(window), callout = that.tooltipDiv.find('.k-callout'), width = that.tooltipDiv.outerWidth(), height = that.tooltipDiv.outerHeight(), dragHandles, sdhOffset, diff, anchorSize;
|
|
if (that.type) {
|
|
dragHandles = owner.wrapper.find(DRAG_HANDLE);
|
|
offset = kendo.getOffset(dragHandles.eq(0));
|
|
sdhOffset = kendo.getOffset(dragHandles.eq(1));
|
|
if (owner._isHorizontal) {
|
|
top = sdhOffset.top;
|
|
left = offset.left + (sdhOffset.left - offset.left) / 2;
|
|
} else {
|
|
top = offset.top + (sdhOffset.top - offset.top) / 2;
|
|
left = sdhOffset.left;
|
|
}
|
|
anchorSize = dragHandles.eq(0).outerWidth() + 2 * margin;
|
|
} else {
|
|
top = offset.top;
|
|
left = offset.left;
|
|
anchorSize = element.outerWidth() + 2 * margin;
|
|
}
|
|
if (owner._isHorizontal) {
|
|
left -= parseInt((width - element[owner._outerSize]()) / 2, 10);
|
|
top -= height + callout.height() + margin;
|
|
} else {
|
|
top -= parseInt((height - element[owner._outerSize]()) / 2, 10);
|
|
left -= width + callout.width() + margin;
|
|
}
|
|
if (owner._isHorizontal) {
|
|
diff = that._flip(top, height, anchorSize, viewport.outerHeight() + that._scrollOffset.top);
|
|
top += diff;
|
|
left += that._fit(left, width, viewport.outerWidth() + that._scrollOffset.left);
|
|
} else {
|
|
diff = that._flip(left, width, anchorSize, viewport.outerWidth() + that._scrollOffset.left);
|
|
top += that._fit(top, height, viewport.outerHeight() + that._scrollOffset.top);
|
|
left += diff;
|
|
}
|
|
if (diff > 0 && callout) {
|
|
callout.removeClass();
|
|
callout.addClass('k-callout k-callout-' + (owner._isHorizontal ? 'n' : 'w'));
|
|
}
|
|
that.tooltipDiv.css({
|
|
top: top,
|
|
left: left
|
|
});
|
|
},
|
|
_fit: function (position, size, viewPortEnd) {
|
|
var output = 0;
|
|
if (position + size > viewPortEnd) {
|
|
output = viewPortEnd - (position + size);
|
|
}
|
|
if (position < 0) {
|
|
output = -position;
|
|
}
|
|
return output;
|
|
},
|
|
_flip: function (offset, size, anchorSize, viewPortEnd) {
|
|
var output = 0;
|
|
if (offset + size > viewPortEnd) {
|
|
output += -(anchorSize + size);
|
|
}
|
|
if (offset + output < 0) {
|
|
output += anchorSize + size;
|
|
}
|
|
return output;
|
|
},
|
|
constrainValue: function (position, min, max, maxOverflow) {
|
|
var that = this, val = 0;
|
|
if (min < position && position < max) {
|
|
val = that.owner._getValueFromPosition(position, that.dragableArea);
|
|
} else {
|
|
if (maxOverflow) {
|
|
val = that.options.max;
|
|
} else {
|
|
val = that.options.min;
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
};
|
|
kendo.ui.plugin(Slider);
|
|
var RangeSlider = SliderBase.extend({
|
|
init: function (element, options) {
|
|
var that = this, inputs = $(element).find('input'), firstInput = inputs.eq(0)[0], secondInput = inputs.eq(1)[0];
|
|
firstInput.type = 'text';
|
|
secondInput.type = 'text';
|
|
if (options && options.showButtons) {
|
|
if (window.console) {
|
|
window.console.warn('showbuttons option is not supported for the range slider, ignoring');
|
|
}
|
|
options.showButtons = false;
|
|
}
|
|
options = extend({}, {
|
|
selectionStart: parseAttr(firstInput, 'value'),
|
|
min: parseAttr(firstInput, 'min'),
|
|
max: parseAttr(firstInput, 'max'),
|
|
smallStep: parseAttr(firstInput, 'step')
|
|
}, {
|
|
selectionEnd: parseAttr(secondInput, 'value'),
|
|
min: parseAttr(secondInput, 'min'),
|
|
max: parseAttr(secondInput, 'max'),
|
|
smallStep: parseAttr(secondInput, 'step')
|
|
}, options);
|
|
if (options && options.enabled === undefined) {
|
|
options.enabled = !inputs.is('[disabled]');
|
|
}
|
|
SliderBase.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
if (!defined(options.selectionStart) || options.selectionStart === null) {
|
|
options.selectionStart = options.min;
|
|
inputs.eq(0).prop('value', formatValue(options.min));
|
|
}
|
|
if (!defined(options.selectionEnd) || options.selectionEnd === null) {
|
|
options.selectionEnd = options.max;
|
|
inputs.eq(1).prop('value', formatValue(options.max));
|
|
}
|
|
var dragHandles = that.wrapper.find(DRAG_HANDLE);
|
|
this._selection = new RangeSlider.Selection(dragHandles, that, options);
|
|
that._firstHandleDrag = new Slider.Drag(dragHandles.eq(0), 'firstHandle', that, options);
|
|
that._lastHandleDrag = new Slider.Drag(dragHandles.eq(1), 'lastHandle', that, options);
|
|
},
|
|
options: {
|
|
name: 'RangeSlider',
|
|
leftDragHandleTitle: 'drag',
|
|
rightDragHandleTitle: 'drag',
|
|
tooltip: { format: '{0:#,#.##}' },
|
|
selectionStart: null,
|
|
selectionEnd: null
|
|
},
|
|
enable: function (enable) {
|
|
var that = this, options = that.options, clickHandler;
|
|
that.disable();
|
|
if (enable === false) {
|
|
return;
|
|
}
|
|
that.wrapper.removeClass(STATE_DISABLED).addClass(STATE_DEFAULT);
|
|
that.wrapper.find('input').removeAttr(DISABLED);
|
|
clickHandler = function (e) {
|
|
var touch = getTouches(e)[0];
|
|
if (!touch) {
|
|
return;
|
|
}
|
|
var mousePosition = that._isHorizontal ? touch.location.pageX : touch.location.pageY, dragableArea = that._getDraggableArea(), val = that._getValueFromPosition(mousePosition, dragableArea), target = $(e.target), from, to, drag;
|
|
if (target.hasClass('k-draghandle')) {
|
|
that.wrapper.find('.' + STATE_FOCUSED).removeClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
target.addClass(STATE_FOCUSED + ' ' + STATE_SELECTED);
|
|
return;
|
|
}
|
|
if (val < options.selectionStart) {
|
|
from = val;
|
|
to = options.selectionEnd;
|
|
drag = that._firstHandleDrag;
|
|
} else if (val > that.selectionEnd) {
|
|
from = options.selectionStart;
|
|
to = val;
|
|
drag = that._lastHandleDrag;
|
|
} else {
|
|
if (val - options.selectionStart <= options.selectionEnd - val) {
|
|
from = val;
|
|
to = options.selectionEnd;
|
|
drag = that._firstHandleDrag;
|
|
} else {
|
|
from = options.selectionStart;
|
|
to = val;
|
|
drag = that._lastHandleDrag;
|
|
}
|
|
}
|
|
drag.dragstart(e);
|
|
that._setValueInRange(from, to);
|
|
that._focusWithMouse(drag.element);
|
|
};
|
|
that.wrapper.find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).on(TRACK_MOUSE_DOWN, clickHandler).end().on(TRACK_MOUSE_DOWN, function () {
|
|
$(document.documentElement).one('selectstart', kendo.preventDefault);
|
|
}).on(TRACK_MOUSE_UP, function () {
|
|
if (that._activeDragHandle) {
|
|
that._activeDragHandle._end();
|
|
}
|
|
});
|
|
that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, 0).on(MOUSE_UP, function () {
|
|
that._setTooltipTimeout();
|
|
}).on(CLICK, function (e) {
|
|
that._focusWithMouse(e.target);
|
|
e.preventDefault();
|
|
}).on(FOCUS, proxy(that._focus, that)).on(BLUR, proxy(that._blur, that));
|
|
that.wrapper.find(DRAG_HANDLE).off(KEY_DOWN, kendo.preventDefault).eq(0).on(KEY_DOWN, proxy(function (e) {
|
|
this._keydown(e, 'firstHandle');
|
|
}, that)).end().eq(1).on(KEY_DOWN, proxy(function (e) {
|
|
this._keydown(e, 'lastHandle');
|
|
}, that));
|
|
that.options.enabled = true;
|
|
},
|
|
disable: function () {
|
|
var that = this;
|
|
that.wrapper.removeClass(STATE_DEFAULT).addClass(STATE_DISABLED);
|
|
that.wrapper.find('input').prop(DISABLED, DISABLED);
|
|
that.wrapper.find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).off(TRACK_MOUSE_DOWN).off(TRACK_MOUSE_UP);
|
|
that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, -1).off(MOUSE_UP).off(KEY_DOWN).off(CLICK).off(FOCUS).off(BLUR);
|
|
that.options.enabled = false;
|
|
},
|
|
_keydown: function (e, handle) {
|
|
var that = this, selectionStartValue = that.options.selectionStart, selectionEndValue = that.options.selectionEnd, dragSelectionStart, dragSelectionEnd, activeHandleDrag;
|
|
if (e.keyCode in that._keyMap) {
|
|
that._clearTooltipTimeout();
|
|
if (handle == 'firstHandle') {
|
|
activeHandleDrag = that._activeHandleDrag = that._firstHandleDrag;
|
|
selectionStartValue = that._keyMap[e.keyCode](selectionStartValue);
|
|
if (selectionStartValue > selectionEndValue) {
|
|
selectionEndValue = selectionStartValue;
|
|
}
|
|
} else {
|
|
activeHandleDrag = that._activeHandleDrag = that._lastHandleDrag;
|
|
selectionEndValue = that._keyMap[e.keyCode](selectionEndValue);
|
|
if (selectionStartValue > selectionEndValue) {
|
|
selectionStartValue = selectionEndValue;
|
|
}
|
|
}
|
|
that._setValueInRange(round(selectionStartValue), round(selectionEndValue));
|
|
dragSelectionStart = Math.max(selectionStartValue, that.options.selectionStart);
|
|
dragSelectionEnd = Math.min(selectionEndValue, that.options.selectionEnd);
|
|
activeHandleDrag.selectionEnd = Math.max(dragSelectionEnd, that.options.selectionStart);
|
|
activeHandleDrag.selectionStart = Math.min(dragSelectionStart, that.options.selectionEnd);
|
|
activeHandleDrag._updateTooltip(that.value()[that._activeHandle]);
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_update: function (selectionStart, selectionEnd) {
|
|
var that = this, values = that.value();
|
|
var change = values[0] != selectionStart || values[1] != selectionEnd;
|
|
that.value([
|
|
selectionStart,
|
|
selectionEnd
|
|
]);
|
|
if (change) {
|
|
that.trigger(CHANGE, {
|
|
values: [
|
|
selectionStart,
|
|
selectionEnd
|
|
],
|
|
value: [
|
|
selectionStart,
|
|
selectionEnd
|
|
]
|
|
});
|
|
}
|
|
},
|
|
value: function (value) {
|
|
if (value && value.length) {
|
|
return this._value(value[0], value[1]);
|
|
} else {
|
|
return this._value();
|
|
}
|
|
},
|
|
_value: function (start, end) {
|
|
var that = this, options = that.options, selectionStart = options.selectionStart, selectionEnd = options.selectionEnd;
|
|
if (isNaN(start) && isNaN(end)) {
|
|
return [
|
|
selectionStart,
|
|
selectionEnd
|
|
];
|
|
} else {
|
|
start = round(start);
|
|
end = round(end);
|
|
}
|
|
if (start >= options.min && start <= options.max && end >= options.min && end <= options.max && start <= end) {
|
|
if (selectionStart != start || selectionEnd != end) {
|
|
that.element.find('input').eq(0).prop('value', formatValue(start)).end().eq(1).prop('value', formatValue(end));
|
|
options.selectionStart = start;
|
|
options.selectionEnd = end;
|
|
that._refresh();
|
|
that._refreshAriaAttr(start, end);
|
|
}
|
|
}
|
|
},
|
|
values: function (start, end) {
|
|
if (isArray(start)) {
|
|
return this._value(start[0], start[1]);
|
|
} else {
|
|
return this._value(start, end);
|
|
}
|
|
},
|
|
_refresh: function () {
|
|
var that = this, options = that.options;
|
|
that.trigger(MOVE_SELECTION, {
|
|
values: [
|
|
options.selectionStart,
|
|
options.selectionEnd
|
|
],
|
|
value: [
|
|
options.selectionStart,
|
|
options.selectionEnd
|
|
]
|
|
});
|
|
if (options.selectionStart == options.max && options.selectionEnd == options.max) {
|
|
that._setZIndex('firstHandle');
|
|
}
|
|
},
|
|
_refreshAriaAttr: function (start, end) {
|
|
var that = this, dragHandles = that.wrapper.find(DRAG_HANDLE), drag = that._activeHandleDrag, formattedValue;
|
|
formattedValue = that._getFormattedValue([
|
|
start,
|
|
end
|
|
], drag);
|
|
dragHandles.eq(0).attr('aria-valuenow', start);
|
|
dragHandles.eq(1).attr('aria-valuenow', end);
|
|
dragHandles.attr('aria-valuetext', formattedValue);
|
|
},
|
|
_setValueInRange: function (selectionStart, selectionEnd) {
|
|
var options = this.options;
|
|
selectionStart = math.max(math.min(selectionStart, options.max), options.min);
|
|
selectionEnd = math.max(math.min(selectionEnd, options.max), options.min);
|
|
if (selectionStart == options.max && selectionEnd == options.max) {
|
|
this._setZIndex('firstHandle');
|
|
}
|
|
this._update(math.min(selectionStart, selectionEnd), math.max(selectionStart, selectionEnd));
|
|
},
|
|
_setZIndex: function (type) {
|
|
this.wrapper.find(DRAG_HANDLE).each(function (index) {
|
|
$(this).css('z-index', type == 'firstHandle' ? 1 - index : index);
|
|
});
|
|
},
|
|
_formResetHandler: function () {
|
|
var that = this, options = that.options;
|
|
setTimeout(function () {
|
|
var inputs = that.element.find('input');
|
|
var start = inputs[0].value;
|
|
var end = inputs[1].value;
|
|
that.values(start === '' || isNaN(start) ? options.min : start, end === '' || isNaN(end) ? options.max : end);
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
SliderBase.fn.destroy.call(that);
|
|
that.wrapper.off(NS).find(TICK_SELECTOR + ', ' + TRACK_SELECTOR).off(NS).end().find(DRAG_HANDLE).off(NS);
|
|
that._firstHandleDrag.draggable.destroy();
|
|
that._lastHandleDrag.draggable.destroy();
|
|
}
|
|
});
|
|
RangeSlider.Selection = function (dragHandles, that, options) {
|
|
function moveSelection(value) {
|
|
value = value || [];
|
|
var selectionStartValue = value[0] - options.min, selectionEndValue = value[1] - options.min, selectionStartIndex = math.ceil(round(selectionStartValue / options.smallStep)), selectionEndIndex = math.ceil(round(selectionEndValue / options.smallStep)), selectionStart = that._pixelSteps[selectionStartIndex], selectionEnd = that._pixelSteps[selectionEndIndex], halfHandle = parseInt(dragHandles.eq(0)[that._outerSize]() / 2, 10), rtlCorrection = that._isRtl ? 2 : 0;
|
|
dragHandles.eq(0).css(that._position, selectionStart - halfHandle - rtlCorrection).end().eq(1).css(that._position, selectionEnd - halfHandle - rtlCorrection);
|
|
makeSelection(selectionStart, selectionEnd);
|
|
}
|
|
function makeSelection(selectionStart, selectionEnd) {
|
|
var selection, selectionPosition, selectionDiv = that._trackDiv.find('.k-slider-selection');
|
|
selection = math.abs(selectionStart - selectionEnd);
|
|
selectionDiv[that._sizeFn](selection);
|
|
if (that._isRtl) {
|
|
selectionPosition = math.max(selectionStart, selectionEnd);
|
|
selectionDiv.css('right', that._maxSelection - selectionPosition - 1);
|
|
} else {
|
|
selectionPosition = math.min(selectionStart, selectionEnd);
|
|
selectionDiv.css(that._position, selectionPosition - 1);
|
|
}
|
|
}
|
|
moveSelection(that.value());
|
|
that.bind([
|
|
CHANGE,
|
|
SLIDE,
|
|
MOVE_SELECTION
|
|
], function (e) {
|
|
moveSelection(e.values);
|
|
});
|
|
};
|
|
kendo.ui.plugin(RangeSlider);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.colorpicker', [
|
|
'kendo.core',
|
|
'kendo.color',
|
|
'kendo.popup',
|
|
'kendo.slider',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'colorpicker',
|
|
name: 'Color tools',
|
|
category: 'web',
|
|
description: 'Color selection widgets',
|
|
depends: [
|
|
'core',
|
|
'color',
|
|
'popup',
|
|
'slider',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, parseInt, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, parseColor = kendo.parseColor, Color = kendo.Color, KEYS = kendo.keys, BACKGROUNDCOLOR = 'background-color', ITEMSELECTEDCLASS = 'k-state-selected', SIMPLEPALETTE = '000000,7f7f7f,880015,ed1c24,ff7f27,fff200,22b14c,00a2e8,3f48cc,a349a4,ffffff,c3c3c3,b97a57,ffaec9,ffc90e,efe4b0,b5e61d,99d9ea,7092be,c8bfe7', WEBPALETTE = 'FFFFFF,FFCCFF,FF99FF,FF66FF,FF33FF,FF00FF,CCFFFF,CCCCFF,CC99FF,CC66FF,CC33FF,CC00FF,99FFFF,99CCFF,9999FF,9966FF,9933FF,9900FF,FFFFCC,FFCCCC,FF99CC,FF66CC,FF33CC,FF00CC,CCFFCC,CCCCCC,CC99CC,CC66CC,CC33CC,CC00CC,99FFCC,99CCCC,9999CC,9966CC,9933CC,9900CC,FFFF99,FFCC99,FF9999,FF6699,FF3399,FF0099,CCFF99,CCCC99,CC9999,CC6699,CC3399,CC0099,99FF99,99CC99,999999,996699,993399,990099,FFFF66,FFCC66,FF9966,FF6666,FF3366,FF0066,CCFF66,CCCC66,CC9966,CC6666,CC3366,CC0066,99FF66,99CC66,999966,996666,993366,990066,FFFF33,FFCC33,FF9933,FF6633,FF3333,FF0033,CCFF33,CCCC33,CC9933,CC6633,CC3333,CC0033,99FF33,99CC33,999933,996633,993333,990033,FFFF00,FFCC00,FF9900,FF6600,FF3300,FF0000,CCFF00,CCCC00,CC9900,CC6600,CC3300,CC0000,99FF00,99CC00,999900,996600,993300,990000,66FFFF,66CCFF,6699FF,6666FF,6633FF,6600FF,33FFFF,33CCFF,3399FF,3366FF,3333FF,3300FF,00FFFF,00CCFF,0099FF,0066FF,0033FF,0000FF,66FFCC,66CCCC,6699CC,6666CC,6633CC,6600CC,33FFCC,33CCCC,3399CC,3366CC,3333CC,3300CC,00FFCC,00CCCC,0099CC,0066CC,0033CC,0000CC,66FF99,66CC99,669999,666699,663399,660099,33FF99,33CC99,339999,336699,333399,330099,00FF99,00CC99,009999,006699,003399,000099,66FF66,66CC66,669966,666666,663366,660066,33FF66,33CC66,339966,336666,333366,330066,00FF66,00CC66,009966,006666,003366,000066,66FF33,66CC33,669933,666633,663333,660033,33FF33,33CC33,339933,336633,333333,330033,00FF33,00CC33,009933,006633,003333,000033,66FF00,66CC00,669900,666600,663300,660000,33FF00,33CC00,339900,336600,333300,330000,00FF00,00CC00,009900,006600,003300,000000', APPLY_CANCEL = {
|
|
apply: 'Apply',
|
|
cancel: 'Cancel'
|
|
}, NS = '.kendoColorTools', CLICK_NS = 'click' + NS, KEYDOWN_NS = 'keydown' + NS, browser = kendo.support.browser, isIE8 = browser.msie && browser.version < 9;
|
|
var ColorSelector = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, ariaId;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
that._value = options.value = parseColor(options.value);
|
|
that._tabIndex = element.attr('tabIndex') || 0;
|
|
ariaId = that._ariaId = options.ariaId;
|
|
if (ariaId) {
|
|
element.attr('aria-labelledby', ariaId);
|
|
}
|
|
if (options._standalone) {
|
|
that._triggerSelect = that._triggerChange;
|
|
}
|
|
},
|
|
options: {
|
|
name: 'ColorSelector',
|
|
value: null,
|
|
_standalone: true
|
|
},
|
|
events: [
|
|
'change',
|
|
'select',
|
|
'cancel'
|
|
],
|
|
color: function (value) {
|
|
if (value !== undefined) {
|
|
this._value = parseColor(value);
|
|
this._updateUI(this._value);
|
|
}
|
|
return this._value;
|
|
},
|
|
value: function (color) {
|
|
color = this.color(color);
|
|
if (color) {
|
|
if (this.options.opacity) {
|
|
color = color.toCssRgba();
|
|
} else {
|
|
color = color.toCss();
|
|
}
|
|
}
|
|
return color || null;
|
|
},
|
|
enable: function (enable) {
|
|
if (arguments.length === 0) {
|
|
enable = true;
|
|
}
|
|
$('.k-disabled-overlay', this.wrapper).remove();
|
|
if (!enable) {
|
|
this.wrapper.append('<div class=\'k-disabled-overlay\'></div>');
|
|
}
|
|
this._onEnable(enable);
|
|
},
|
|
_select: function (color, nohooks) {
|
|
var prev = this._value;
|
|
color = this.color(color);
|
|
if (!nohooks) {
|
|
this.element.trigger('change');
|
|
if (!color.equals(prev)) {
|
|
this.trigger('change', { value: this.value() });
|
|
} else if (!this._standalone) {
|
|
this.trigger('cancel');
|
|
}
|
|
}
|
|
},
|
|
_triggerSelect: function (color) {
|
|
triggerEvent(this, 'select', color);
|
|
},
|
|
_triggerChange: function (color) {
|
|
triggerEvent(this, 'change', color);
|
|
},
|
|
destroy: function () {
|
|
if (this.element) {
|
|
this.element.off(NS);
|
|
}
|
|
if (this.wrapper) {
|
|
this.wrapper.off(NS).find('*').off(NS);
|
|
}
|
|
this.wrapper = null;
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
_updateUI: $.noop,
|
|
_selectOnHide: function () {
|
|
return null;
|
|
},
|
|
_cancel: function () {
|
|
this.trigger('cancel');
|
|
}
|
|
});
|
|
function triggerEvent(self, type, color) {
|
|
color = parseColor(color);
|
|
if (color && !color.equals(self.color())) {
|
|
if (type == 'change') {
|
|
self._value = color;
|
|
}
|
|
if (color.a != 1) {
|
|
color = color.toCssRgba();
|
|
} else {
|
|
color = color.toCss();
|
|
}
|
|
self.trigger(type, { value: color });
|
|
}
|
|
}
|
|
var ColorPalette = ColorSelector.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
ColorSelector.fn.init.call(that, element, options);
|
|
element = that.wrapper = that.element;
|
|
options = that.options;
|
|
var colors = options.palette;
|
|
if (colors == 'websafe') {
|
|
colors = WEBPALETTE;
|
|
options.columns = 18;
|
|
} else if (colors == 'basic') {
|
|
colors = SIMPLEPALETTE;
|
|
}
|
|
if (typeof colors == 'string') {
|
|
colors = colors.split(',');
|
|
}
|
|
if ($.isArray(colors)) {
|
|
colors = $.map(colors, function (x) {
|
|
return parseColor(x);
|
|
});
|
|
}
|
|
that._selectedID = (options.ariaId || kendo.guid()) + '_selected';
|
|
element.addClass('k-widget k-colorpalette').attr('role', 'grid').attr('aria-readonly', 'true').append($(that._template({
|
|
colors: colors,
|
|
columns: options.columns,
|
|
tileSize: options.tileSize,
|
|
value: that._value,
|
|
id: options.ariaId
|
|
}))).on(CLICK_NS, '.k-item', function (ev) {
|
|
that._select($(ev.currentTarget).css(BACKGROUNDCOLOR));
|
|
}).attr('tabIndex', that._tabIndex).on(KEYDOWN_NS, bind(that._keydown, that));
|
|
var tileSize = options.tileSize, width, height;
|
|
if (tileSize) {
|
|
if (/number|string/.test(typeof tileSize)) {
|
|
width = height = parseFloat(tileSize);
|
|
} else if (typeof tileSize == 'object') {
|
|
width = parseFloat(tileSize.width);
|
|
height = parseFloat(tileSize.height);
|
|
} else {
|
|
throw new Error('Unsupported value for the \'tileSize\' argument');
|
|
}
|
|
element.find('.k-item').css({
|
|
width: width,
|
|
height: height
|
|
});
|
|
}
|
|
},
|
|
focus: function () {
|
|
this.wrapper.focus();
|
|
},
|
|
options: {
|
|
name: 'ColorPalette',
|
|
columns: 10,
|
|
tileSize: null,
|
|
palette: 'basic'
|
|
},
|
|
_onEnable: function (enable) {
|
|
if (enable) {
|
|
this.wrapper.attr('tabIndex', this._tabIndex);
|
|
} else {
|
|
this.wrapper.removeAttr('tabIndex');
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var selected, wrapper = this.wrapper, items = wrapper.find('.k-item'), current = items.filter('.' + ITEMSELECTEDCLASS).get(0), keyCode = e.keyCode;
|
|
if (keyCode == KEYS.LEFT) {
|
|
selected = relative(items, current, -1);
|
|
} else if (keyCode == KEYS.RIGHT) {
|
|
selected = relative(items, current, 1);
|
|
} else if (keyCode == KEYS.DOWN) {
|
|
selected = relative(items, current, this.options.columns);
|
|
} else if (keyCode == KEYS.UP) {
|
|
selected = relative(items, current, -this.options.columns);
|
|
} else if (keyCode == KEYS.ENTER) {
|
|
preventDefault(e);
|
|
if (current) {
|
|
this._select($(current).css(BACKGROUNDCOLOR));
|
|
}
|
|
} else if (keyCode == KEYS.ESC) {
|
|
this._cancel();
|
|
}
|
|
if (selected) {
|
|
preventDefault(e);
|
|
this._current(selected);
|
|
try {
|
|
var color = parseColor(selected.css(BACKGROUNDCOLOR));
|
|
this._triggerSelect(color);
|
|
} catch (ex) {
|
|
}
|
|
}
|
|
},
|
|
_current: function (item) {
|
|
this.wrapper.find('.' + ITEMSELECTEDCLASS).removeClass(ITEMSELECTEDCLASS).attr('aria-selected', false).removeAttr('id');
|
|
$(item).addClass(ITEMSELECTEDCLASS).attr('aria-selected', true).attr('id', this._selectedID);
|
|
this.element.removeAttr('aria-activedescendant').attr('aria-activedescendant', this._selectedID);
|
|
},
|
|
_updateUI: function (color) {
|
|
var item = null;
|
|
this.wrapper.find('.k-item').each(function () {
|
|
var c = parseColor($(this).css(BACKGROUNDCOLOR));
|
|
if (c && c.equals(color)) {
|
|
item = this;
|
|
return false;
|
|
}
|
|
});
|
|
this._current(item);
|
|
},
|
|
_template: kendo.template('<table class="k-palette k-reset" role="presentation"><tr role="row">' + '# for (var i = 0; i < colors.length; ++i) { #' + '# var selected = colors[i].equals(value); #' + '# if (i && i % columns == 0) { # </tr><tr role="row"> # } #' + '<td role="gridcell" unselectable="on" style="background-color:#= colors[i].toCss() #"' + '#= selected ? " aria-selected=true" : "" # ' + '#=(id && i === 0) ? "id=\\""+id+"\\" " : "" # ' + 'class="k-item#= selected ? " ' + ITEMSELECTEDCLASS + '" : "" #" ' + 'aria-label="#= colors[i].toCss() #"></td>' + '# } #' + '</tr></table>')
|
|
});
|
|
var FlatColorPicker = ColorSelector.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
ColorSelector.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element;
|
|
that.wrapper = element.addClass('k-widget k-flatcolorpicker').append(that._template(options));
|
|
that._hueElements = $('.k-hsv-rectangle, .k-transparency-slider .k-slider-track', element);
|
|
that._selectedColor = $('.k-selected-color-display', element);
|
|
that._colorAsText = $('input.k-color-value', element);
|
|
that._sliders();
|
|
that._hsvArea();
|
|
that._updateUI(that._value || parseColor('#f00'));
|
|
element.find('input.k-color-value').on(KEYDOWN_NS, function (ev) {
|
|
var input = this;
|
|
if (ev.keyCode == KEYS.ENTER) {
|
|
try {
|
|
var color = parseColor(input.value);
|
|
var val = that.color();
|
|
that._select(color, color.equals(val));
|
|
} catch (ex) {
|
|
$(input).addClass('k-state-error');
|
|
}
|
|
} else if (that.options.autoupdate) {
|
|
setTimeout(function () {
|
|
var color = parseColor(input.value, true);
|
|
if (color) {
|
|
that._updateUI(color, true);
|
|
}
|
|
}, 10);
|
|
}
|
|
}).end().on(CLICK_NS, '.k-controls button.apply', function () {
|
|
that._select(that._getHSV());
|
|
}).on(CLICK_NS, '.k-controls button.cancel', function () {
|
|
that._updateUI(that.color());
|
|
that._cancel();
|
|
});
|
|
if (isIE8) {
|
|
that._applyIEFilter();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this._hueSlider.destroy();
|
|
if (this._opacitySlider) {
|
|
this._opacitySlider.destroy();
|
|
}
|
|
this._hueSlider = this._opacitySlider = this._hsvRect = this._hsvHandle = this._hueElements = this._selectedColor = this._colorAsText = null;
|
|
ColorSelector.fn.destroy.call(this);
|
|
},
|
|
options: {
|
|
name: 'FlatColorPicker',
|
|
opacity: false,
|
|
buttons: false,
|
|
input: true,
|
|
preview: true,
|
|
autoupdate: true,
|
|
messages: APPLY_CANCEL
|
|
},
|
|
_applyIEFilter: function () {
|
|
var track = this.element.find('.k-hue-slider .k-slider-track')[0], url = track.currentStyle.backgroundImage;
|
|
url = url.replace(/^url\([\'\"]?|[\'\"]?\)$/g, '');
|
|
track.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + url + '\', sizingMethod=\'scale\')';
|
|
},
|
|
_sliders: function () {
|
|
var that = this, element = that.element;
|
|
function hueChange(e) {
|
|
that._updateUI(that._getHSV(e.value, null, null, null));
|
|
}
|
|
that._hueSlider = element.find('.k-hue-slider').kendoSlider({
|
|
min: 0,
|
|
max: 359,
|
|
tickPlacement: 'none',
|
|
showButtons: false,
|
|
slide: hueChange,
|
|
change: hueChange
|
|
}).data('kendoSlider');
|
|
function opacityChange(e) {
|
|
that._updateUI(that._getHSV(null, null, null, e.value / 100));
|
|
}
|
|
that._opacitySlider = element.find('.k-transparency-slider').kendoSlider({
|
|
min: 0,
|
|
max: 100,
|
|
tickPlacement: 'none',
|
|
showButtons: false,
|
|
slide: opacityChange,
|
|
change: opacityChange
|
|
}).data('kendoSlider');
|
|
},
|
|
_hsvArea: function () {
|
|
var that = this, element = that.element, hsvRect = element.find('.k-hsv-rectangle'), hsvHandle = hsvRect.find('.k-draghandle').attr('tabIndex', 0).on(KEYDOWN_NS, bind(that._keydown, that));
|
|
function update(x, y) {
|
|
var offset = this.offset, dx = x - offset.left, dy = y - offset.top, rw = this.width, rh = this.height;
|
|
dx = dx < 0 ? 0 : dx > rw ? rw : dx;
|
|
dy = dy < 0 ? 0 : dy > rh ? rh : dy;
|
|
that._svChange(dx / rw, 1 - dy / rh);
|
|
}
|
|
that._hsvEvents = new kendo.UserEvents(hsvRect, {
|
|
global: true,
|
|
press: function (e) {
|
|
this.offset = kendo.getOffset(hsvRect);
|
|
this.width = hsvRect.width();
|
|
this.height = hsvRect.height();
|
|
hsvHandle.focus();
|
|
update.call(this, e.x.location, e.y.location);
|
|
},
|
|
start: function () {
|
|
hsvRect.addClass('k-dragging');
|
|
hsvHandle.focus();
|
|
},
|
|
move: function (e) {
|
|
e.preventDefault();
|
|
update.call(this, e.x.location, e.y.location);
|
|
},
|
|
end: function () {
|
|
hsvRect.removeClass('k-dragging');
|
|
}
|
|
});
|
|
that._hsvRect = hsvRect;
|
|
that._hsvHandle = hsvHandle;
|
|
},
|
|
_onEnable: function (enable) {
|
|
this._hueSlider.enable(enable);
|
|
if (this._opacitySlider) {
|
|
this._opacitySlider.enable(enable);
|
|
}
|
|
this.wrapper.find('input').attr('disabled', !enable);
|
|
var handle = this._hsvRect.find('.k-draghandle');
|
|
if (enable) {
|
|
handle.attr('tabIndex', this._tabIndex);
|
|
} else {
|
|
handle.removeAttr('tabIndex');
|
|
}
|
|
},
|
|
_keydown: function (ev) {
|
|
var that = this;
|
|
function move(prop, d) {
|
|
var c = that._getHSV();
|
|
c[prop] += d * (ev.shiftKey ? 0.01 : 0.05);
|
|
if (c[prop] < 0) {
|
|
c[prop] = 0;
|
|
}
|
|
if (c[prop] > 1) {
|
|
c[prop] = 1;
|
|
}
|
|
that._updateUI(c);
|
|
preventDefault(ev);
|
|
}
|
|
function hue(d) {
|
|
var c = that._getHSV();
|
|
c.h += d * (ev.shiftKey ? 1 : 5);
|
|
if (c.h < 0) {
|
|
c.h = 0;
|
|
}
|
|
if (c.h > 359) {
|
|
c.h = 359;
|
|
}
|
|
that._updateUI(c);
|
|
preventDefault(ev);
|
|
}
|
|
switch (ev.keyCode) {
|
|
case KEYS.LEFT:
|
|
if (ev.ctrlKey) {
|
|
hue(-1);
|
|
} else {
|
|
move('s', -1);
|
|
}
|
|
break;
|
|
case KEYS.RIGHT:
|
|
if (ev.ctrlKey) {
|
|
hue(1);
|
|
} else {
|
|
move('s', 1);
|
|
}
|
|
break;
|
|
case KEYS.UP:
|
|
move(ev.ctrlKey && that._opacitySlider ? 'a' : 'v', 1);
|
|
break;
|
|
case KEYS.DOWN:
|
|
move(ev.ctrlKey && that._opacitySlider ? 'a' : 'v', -1);
|
|
break;
|
|
case KEYS.ENTER:
|
|
that._select(that._getHSV());
|
|
break;
|
|
case KEYS.F2:
|
|
that.wrapper.find('input.k-color-value').focus().select();
|
|
break;
|
|
case KEYS.ESC:
|
|
that._cancel();
|
|
break;
|
|
}
|
|
},
|
|
focus: function () {
|
|
this._hsvHandle.focus();
|
|
},
|
|
_getHSV: function (h, s, v, a) {
|
|
var rect = this._hsvRect, width = rect.width(), height = rect.height(), handlePosition = this._hsvHandle.position();
|
|
if (h == null) {
|
|
h = this._hueSlider.value();
|
|
}
|
|
if (s == null) {
|
|
s = handlePosition.left / width;
|
|
}
|
|
if (v == null) {
|
|
v = 1 - handlePosition.top / height;
|
|
}
|
|
if (a == null) {
|
|
a = this._opacitySlider ? this._opacitySlider.value() / 100 : 1;
|
|
}
|
|
return Color.fromHSV(h, s, v, a);
|
|
},
|
|
_svChange: function (s, v) {
|
|
var color = this._getHSV(null, s, v, null);
|
|
this._updateUI(color);
|
|
},
|
|
_updateUI: function (color, dontChangeInput) {
|
|
var that = this, rect = that._hsvRect;
|
|
if (!color) {
|
|
return;
|
|
}
|
|
this._colorAsText.removeClass('k-state-error');
|
|
that._selectedColor.css(BACKGROUNDCOLOR, color.toDisplay());
|
|
if (!dontChangeInput) {
|
|
that._colorAsText.val(that._opacitySlider ? color.toCssRgba() : color.toCss());
|
|
}
|
|
that._triggerSelect(color);
|
|
color = color.toHSV();
|
|
that._hsvHandle.css({
|
|
left: color.s * rect.width() + 'px',
|
|
top: (1 - color.v) * rect.height() + 'px'
|
|
});
|
|
that._hueElements.css(BACKGROUNDCOLOR, Color.fromHSV(color.h, 1, 1, 1).toCss());
|
|
that._hueSlider.value(color.h);
|
|
if (that._opacitySlider) {
|
|
that._opacitySlider.value(100 * color.a);
|
|
}
|
|
},
|
|
_selectOnHide: function () {
|
|
return this.options.buttons ? null : this._getHSV();
|
|
},
|
|
_template: kendo.template('# if (preview) { #' + '<div class="k-selected-color"><div class="k-selected-color-display"><input class="k-color-value" #= !data.input ? \'style="visibility: hidden;"\' : "" #></div></div>' + '# } #' + '<div class="k-hsv-rectangle"><div class="k-hsv-gradient"></div><div class="k-draghandle"></div></div>' + '<input class="k-hue-slider" />' + '# if (opacity) { #' + '<input class="k-transparency-slider" />' + '# } #' + '# if (buttons) { #' + '<div unselectable="on" class="k-controls"><button class="k-button k-primary apply">#: messages.apply #</button> <button class="k-button cancel">#: messages.cancel #</button></div>' + '# } #')
|
|
});
|
|
function relative(array, element, delta) {
|
|
array = Array.prototype.slice.call(array);
|
|
var n = array.length;
|
|
var pos = array.indexOf(element);
|
|
if (pos < 0) {
|
|
return delta < 0 ? array[n - 1] : array[0];
|
|
}
|
|
pos += delta;
|
|
if (pos < 0) {
|
|
pos += n;
|
|
} else {
|
|
pos %= n;
|
|
}
|
|
return array[pos];
|
|
}
|
|
var ColorPicker = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element;
|
|
var value = element.attr('value') || element.val();
|
|
if (value) {
|
|
value = parseColor(value, true);
|
|
} else {
|
|
value = parseColor(options.value, true);
|
|
}
|
|
that._value = options.value = value;
|
|
var content = that.wrapper = $(that._template(options));
|
|
element.hide().after(content);
|
|
if (element.is('input')) {
|
|
element.appendTo(content);
|
|
var label = element.closest('label');
|
|
var id = element.attr('id');
|
|
if (id) {
|
|
label = label.add('label[for="' + id + '"]');
|
|
}
|
|
label.click(function (ev) {
|
|
that.open();
|
|
ev.preventDefault();
|
|
});
|
|
}
|
|
that._tabIndex = element.attr('tabIndex') || 0;
|
|
that.enable(!element.attr('disabled'));
|
|
var accesskey = element.attr('accesskey');
|
|
if (accesskey) {
|
|
element.attr('accesskey', null);
|
|
content.attr('accesskey', accesskey);
|
|
}
|
|
that.bind('activate', function (ev) {
|
|
if (!ev.isDefaultPrevented()) {
|
|
that.toggle();
|
|
}
|
|
});
|
|
that._updateUI(value);
|
|
},
|
|
destroy: function () {
|
|
this.wrapper.off(NS).find('*').off(NS);
|
|
if (this._popup) {
|
|
this._selector.destroy();
|
|
this._popup.destroy();
|
|
}
|
|
this._selector = this._popup = this.wrapper = null;
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
enable: function (enable) {
|
|
var that = this, wrapper = that.wrapper, innerWrapper = wrapper.children('.k-picker-wrap'), icon = innerWrapper.find('.k-select');
|
|
if (arguments.length === 0) {
|
|
enable = true;
|
|
}
|
|
that.element.attr('disabled', !enable);
|
|
wrapper.attr('aria-disabled', !enable);
|
|
icon.off(NS).on('mousedown' + NS, preventDefault);
|
|
wrapper.addClass('k-state-disabled').removeAttr('tabIndex').add('*', wrapper).off(NS);
|
|
if (enable) {
|
|
wrapper.removeClass('k-state-disabled').attr('tabIndex', that._tabIndex).on('mouseenter' + NS, function () {
|
|
innerWrapper.addClass('k-state-hover');
|
|
}).on('mouseleave' + NS, function () {
|
|
innerWrapper.removeClass('k-state-hover');
|
|
}).on('focus' + NS, function () {
|
|
innerWrapper.addClass('k-state-focused');
|
|
}).on('blur' + NS, function () {
|
|
innerWrapper.removeClass('k-state-focused');
|
|
}).on(KEYDOWN_NS, bind(that._keydown, that)).on(CLICK_NS, '.k-select', bind(that.toggle, that)).on(CLICK_NS, that.options.toolIcon ? '.k-tool-icon' : '.k-selected-color', function () {
|
|
that.trigger('activate');
|
|
});
|
|
}
|
|
},
|
|
_template: kendo.template('<span role="textbox" aria-haspopup="true" class="k-widget k-colorpicker k-header">' + '<span class="k-picker-wrap k-state-default">' + '# if (toolIcon) { #' + '<span class="k-tool-icon #= toolIcon #">' + '<span class="k-selected-color"></span>' + '</span>' + '# } else { #' + '<span class="k-selected-color"></span>' + '# } #' + '<span class="k-select" unselectable="on">' + '<span class="k-icon k-i-arrow-s" unselectable="on"></span>' + '</span>' + '</span>' + '</span>'),
|
|
options: {
|
|
name: 'ColorPicker',
|
|
palette: null,
|
|
columns: 10,
|
|
toolIcon: null,
|
|
value: null,
|
|
messages: APPLY_CANCEL,
|
|
opacity: false,
|
|
buttons: true,
|
|
preview: true,
|
|
ARIATemplate: 'Current selected color is #=data || ""#'
|
|
},
|
|
events: [
|
|
'activate',
|
|
'change',
|
|
'select',
|
|
'open',
|
|
'close'
|
|
],
|
|
open: function () {
|
|
this._getPopup().open();
|
|
},
|
|
close: function () {
|
|
this._getPopup().close();
|
|
},
|
|
toggle: function () {
|
|
this._getPopup().toggle();
|
|
},
|
|
color: ColorSelector.fn.color,
|
|
value: ColorSelector.fn.value,
|
|
_select: ColorSelector.fn._select,
|
|
_triggerSelect: ColorSelector.fn._triggerSelect,
|
|
_isInputTypeColor: function () {
|
|
var el = this.element[0];
|
|
return /^input$/i.test(el.tagName) && /^color$/i.test(el.type);
|
|
},
|
|
_updateUI: function (value) {
|
|
var formattedValue = '';
|
|
if (value) {
|
|
if (this._isInputTypeColor() || value.a == 1) {
|
|
formattedValue = value.toCss();
|
|
} else {
|
|
formattedValue = value.toCssRgba();
|
|
}
|
|
this.element.val(formattedValue);
|
|
}
|
|
if (!this._ariaTemplate) {
|
|
this._ariaTemplate = kendo.template(this.options.ARIATemplate);
|
|
}
|
|
this.wrapper.attr('aria-label', this._ariaTemplate(formattedValue));
|
|
this._triggerSelect(value);
|
|
this.wrapper.find('.k-selected-color').css(BACKGROUNDCOLOR, value ? value.toDisplay() : 'transparent');
|
|
},
|
|
_keydown: function (ev) {
|
|
var key = ev.keyCode;
|
|
if (this._getPopup().visible()) {
|
|
if (key == KEYS.ESC) {
|
|
this._selector._cancel();
|
|
} else {
|
|
this._selector._keydown(ev);
|
|
}
|
|
preventDefault(ev);
|
|
} else if (key == KEYS.ENTER || key == KEYS.DOWN) {
|
|
this.open();
|
|
preventDefault(ev);
|
|
}
|
|
},
|
|
_getPopup: function () {
|
|
var that = this, popup = that._popup;
|
|
if (!popup) {
|
|
var options = that.options;
|
|
var selectorType;
|
|
if (options.palette) {
|
|
selectorType = ColorPalette;
|
|
} else {
|
|
selectorType = FlatColorPicker;
|
|
}
|
|
options._standalone = false;
|
|
delete options.select;
|
|
delete options.change;
|
|
delete options.cancel;
|
|
var id = kendo.guid();
|
|
var selector = that._selector = new selectorType($('<div id="' + id + '"/>').appendTo(document.body), options);
|
|
that.wrapper.attr('aria-owns', id);
|
|
that._popup = popup = selector.wrapper.kendoPopup({
|
|
anchor: that.wrapper,
|
|
adjustSize: {
|
|
width: 5,
|
|
height: 0
|
|
}
|
|
}).data('kendoPopup');
|
|
selector.bind({
|
|
select: function (ev) {
|
|
that._updateUI(parseColor(ev.value));
|
|
},
|
|
change: function () {
|
|
that._select(selector.color());
|
|
that.close();
|
|
},
|
|
cancel: function () {
|
|
that.close();
|
|
}
|
|
});
|
|
popup.bind({
|
|
close: function (ev) {
|
|
if (that.trigger('close')) {
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
that.wrapper.children('.k-picker-wrap').removeClass('k-state-focused');
|
|
var color = selector._selectOnHide();
|
|
if (!color) {
|
|
that.wrapper.focus();
|
|
that._updateUI(that.color());
|
|
} else {
|
|
that._select(color);
|
|
}
|
|
},
|
|
open: function (ev) {
|
|
if (that.trigger('open')) {
|
|
ev.preventDefault();
|
|
} else {
|
|
that.wrapper.children('.k-picker-wrap').addClass('k-state-focused');
|
|
}
|
|
},
|
|
activate: function () {
|
|
selector._select(that.color(), true);
|
|
selector.focus();
|
|
that.wrapper.children('.k-picker-wrap').addClass('k-state-focused');
|
|
}
|
|
});
|
|
}
|
|
return popup;
|
|
}
|
|
});
|
|
function preventDefault(ev) {
|
|
ev.preventDefault();
|
|
}
|
|
function bind(callback, obj) {
|
|
return function () {
|
|
return callback.apply(obj, arguments);
|
|
};
|
|
}
|
|
ui.plugin(ColorPalette);
|
|
ui.plugin(FlatColorPicker);
|
|
ui.plugin(ColorPicker);
|
|
}(jQuery, parseInt));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.numerictextbox', [
|
|
'kendo.core',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'numerictextbox',
|
|
name: 'NumericTextBox',
|
|
category: 'web',
|
|
description: 'The NumericTextBox widget can format and display numeric, percentage or currency textbox.',
|
|
depends: [
|
|
'core',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, caret = kendo.caret, keys = kendo.keys, ui = kendo.ui, Widget = ui.Widget, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, parse = kendo.parseFloat, placeholderSupported = kendo.support.placeholder, getCulture = kendo.getCulture, round = kendo._round, CHANGE = 'change', DISABLED = 'disabled', READONLY = 'readonly', INPUT = 'k-input', SPIN = 'spin', ns = '.kendoNumericTextBox', TOUCHEND = 'touchend', MOUSELEAVE = 'mouseleave' + ns, HOVEREVENTS = 'mouseenter' + ns + ' ' + MOUSELEAVE, DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', FOCUS = 'focus', POINT = '.', SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', INTEGER_REGEXP = /^(-)?(\d*)$/, NULL = null, proxy = $.proxy, extend = $.extend;
|
|
var NumericTextBox = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, isStep = options && options.step !== undefined, min, max, step, value, disabled;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element.on('focusout' + ns, proxy(that._focusout, that)).attr('role', 'spinbutton');
|
|
options.placeholder = options.placeholder || element.attr('placeholder');
|
|
that._initialOptions = extend({}, options);
|
|
that._reset();
|
|
that._wrapper();
|
|
that._arrows();
|
|
that._input();
|
|
if (!kendo.support.mobileOS) {
|
|
that._text.on(FOCUS + ns, proxy(that._click, that));
|
|
} else {
|
|
that._text.on(TOUCHEND + ns + ' ' + FOCUS + ns, function () {
|
|
that._toggleText(false);
|
|
element.focus();
|
|
});
|
|
}
|
|
min = that.min(element.attr('min'));
|
|
max = that.max(element.attr('max'));
|
|
step = that._parse(element.attr('step'));
|
|
if (options.min === NULL && min !== NULL) {
|
|
options.min = min;
|
|
}
|
|
if (options.max === NULL && max !== NULL) {
|
|
options.max = max;
|
|
}
|
|
if (!isStep && step !== NULL) {
|
|
options.step = step;
|
|
}
|
|
element.attr('aria-valuemin', options.min).attr('aria-valuemax', options.max);
|
|
options.format = extractFormat(options.format);
|
|
value = options.value;
|
|
that.value(value !== NULL ? value : element.val());
|
|
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(element.is('[readonly]'));
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'NumericTextBox',
|
|
decimals: NULL,
|
|
min: NULL,
|
|
max: NULL,
|
|
value: NULL,
|
|
step: 1,
|
|
culture: '',
|
|
format: 'n',
|
|
spinners: true,
|
|
placeholder: '',
|
|
upArrowText: 'Increase value',
|
|
downArrowText: 'Decrease value'
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
SPIN
|
|
],
|
|
_editable: function (options) {
|
|
var that = this, element = that.element, disable = options.disable, readonly = options.readonly, text = that._text.add(element), wrapper = that._inputWrapper.off(HOVEREVENTS);
|
|
that._toggleText(true);
|
|
that._upArrowEventHandler.unbind('press');
|
|
that._downArrowEventHandler.unbind('press');
|
|
element.off('keydown' + ns).off('keypress' + ns).off('paste' + ns);
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
text.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
|
|
that._upArrowEventHandler.bind('press', function (e) {
|
|
e.preventDefault();
|
|
that._spin(1);
|
|
that._upArrow.addClass(SELECTED);
|
|
});
|
|
that._downArrowEventHandler.bind('press', function (e) {
|
|
e.preventDefault();
|
|
that._spin(-1);
|
|
that._downArrow.addClass(SELECTED);
|
|
});
|
|
that.element.on('keydown' + ns, proxy(that._keydown, that)).on('keypress' + ns, proxy(that._keypress, that)).on('paste' + ns, proxy(that._paste, that));
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
text.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.element.add(that._text).add(that._upArrow).add(that._downArrow).add(that._inputWrapper).off(ns);
|
|
that._upArrowEventHandler.destroy();
|
|
that._downArrowEventHandler.destroy();
|
|
if (that._form) {
|
|
that._form.off('reset', that._resetHandler);
|
|
}
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
min: function (value) {
|
|
return this._option('min', value);
|
|
},
|
|
max: function (value) {
|
|
return this._option('max', value);
|
|
},
|
|
step: function (value) {
|
|
return this._option('step', value);
|
|
},
|
|
value: function (value) {
|
|
var that = this, adjusted;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
value = that._parse(value);
|
|
adjusted = that._adjust(value);
|
|
if (value !== adjusted) {
|
|
return;
|
|
}
|
|
that._update(value);
|
|
that._old = that._value;
|
|
},
|
|
focus: function () {
|
|
this._focusin();
|
|
},
|
|
_adjust: function (value) {
|
|
var that = this, options = that.options, min = options.min, max = options.max;
|
|
if (value === NULL) {
|
|
return value;
|
|
}
|
|
if (min !== NULL && value < min) {
|
|
value = min;
|
|
} else if (max !== NULL && value > max) {
|
|
value = max;
|
|
}
|
|
return value;
|
|
},
|
|
_arrows: function () {
|
|
var that = this, arrows, _release = function () {
|
|
clearTimeout(that._spinning);
|
|
arrows.removeClass(SELECTED);
|
|
}, options = that.options, spinners = options.spinners, element = that.element;
|
|
arrows = element.siblings('.k-icon');
|
|
if (!arrows[0]) {
|
|
arrows = $(buttonHtml('n', options.upArrowText) + buttonHtml('s', options.downArrowText)).insertAfter(element);
|
|
arrows.wrapAll('<span class="k-select"/>');
|
|
}
|
|
if (!spinners) {
|
|
arrows.parent().toggle(spinners);
|
|
that._inputWrapper.addClass('k-expand-padding');
|
|
}
|
|
that._upArrow = arrows.eq(0);
|
|
that._upArrowEventHandler = new kendo.UserEvents(that._upArrow, { release: _release });
|
|
that._downArrow = arrows.eq(1);
|
|
that._downArrowEventHandler = new kendo.UserEvents(that._downArrow, { release: _release });
|
|
},
|
|
_blur: function () {
|
|
var that = this;
|
|
that._toggleText(true);
|
|
that._change(that.element.val());
|
|
},
|
|
_click: function (e) {
|
|
var that = this;
|
|
clearTimeout(that._focusing);
|
|
that._focusing = setTimeout(function () {
|
|
var input = e.target, idx = caret(input)[0], value = input.value.substring(0, idx), format = that._format(that.options.format), group = format[','], result, groupRegExp, extractRegExp, caretPosition = 0;
|
|
if (group) {
|
|
groupRegExp = new RegExp('\\' + group, 'g');
|
|
extractRegExp = new RegExp('([\\d\\' + group + ']+)(\\' + format[POINT] + ')?(\\d+)?');
|
|
}
|
|
if (extractRegExp) {
|
|
result = extractRegExp.exec(value);
|
|
}
|
|
if (result) {
|
|
caretPosition = result[0].replace(groupRegExp, '').length;
|
|
if (value.indexOf('(') != -1 && that._value < 0) {
|
|
caretPosition++;
|
|
}
|
|
}
|
|
that._focusin();
|
|
caret(that.element[0], caretPosition);
|
|
});
|
|
},
|
|
_change: function (value) {
|
|
var that = this;
|
|
that._update(value);
|
|
value = that._value;
|
|
if (that._old != value) {
|
|
that._old = value;
|
|
if (!that._typing) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
that.trigger(CHANGE);
|
|
}
|
|
that._typing = false;
|
|
},
|
|
_culture: function (culture) {
|
|
return culture || getCulture(this.options.culture);
|
|
},
|
|
_focusin: function () {
|
|
var that = this;
|
|
that._inputWrapper.addClass(FOCUSED);
|
|
that._toggleText(false);
|
|
that.element[0].focus();
|
|
},
|
|
_focusout: function () {
|
|
var that = this;
|
|
clearTimeout(that._focusing);
|
|
that._inputWrapper.removeClass(FOCUSED).removeClass(HOVER);
|
|
that._blur();
|
|
},
|
|
_format: function (format, culture) {
|
|
var numberFormat = this._culture(culture).numberFormat;
|
|
format = format.toLowerCase();
|
|
if (format.indexOf('c') > -1) {
|
|
numberFormat = numberFormat.currency;
|
|
} else if (format.indexOf('p') > -1) {
|
|
numberFormat = numberFormat.percent;
|
|
}
|
|
return numberFormat;
|
|
},
|
|
_input: function () {
|
|
var that = this, CLASSNAME = 'k-formatted-value', element = that.element.addClass(INPUT).show()[0], accessKey = element.accessKey, wrapper = that.wrapper, text;
|
|
text = wrapper.find(POINT + CLASSNAME);
|
|
if (!text[0]) {
|
|
text = $('<input type="text"/>').insertBefore(element).addClass(CLASSNAME);
|
|
}
|
|
try {
|
|
element.setAttribute('type', 'text');
|
|
} catch (e) {
|
|
element.type = 'text';
|
|
}
|
|
text[0].tabIndex = element.tabIndex;
|
|
text[0].style.cssText = element.style.cssText;
|
|
text[0].title = element.title;
|
|
text.prop('placeholder', that.options.placeholder);
|
|
if (accessKey) {
|
|
text.attr('accesskey', accessKey);
|
|
element.accessKey = '';
|
|
}
|
|
that._text = text.addClass(element.className);
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode;
|
|
that._key = key;
|
|
if (key == keys.DOWN) {
|
|
that._step(-1);
|
|
} else if (key == keys.UP) {
|
|
that._step(1);
|
|
} else if (key == keys.ENTER) {
|
|
that._change(that.element.val());
|
|
} else {
|
|
that._typing = true;
|
|
}
|
|
},
|
|
_keypress: function (e) {
|
|
if (e.which === 0 || e.metaKey || e.ctrlKey || e.keyCode === keys.BACKSPACE || e.keyCode === keys.ENTER) {
|
|
return;
|
|
}
|
|
var that = this;
|
|
var min = that.options.min;
|
|
var element = that.element;
|
|
var selection = caret(element);
|
|
var selectionStart = selection[0];
|
|
var selectionEnd = selection[1];
|
|
var character = String.fromCharCode(e.which);
|
|
var numberFormat = that._format(that.options.format);
|
|
var isNumPadDecimal = that._key === keys.NUMPAD_DOT;
|
|
var value = element.val();
|
|
var isValid;
|
|
if (isNumPadDecimal) {
|
|
character = numberFormat[POINT];
|
|
}
|
|
value = value.substring(0, selectionStart) + character + value.substring(selectionEnd);
|
|
isValid = that._numericRegex(numberFormat).test(value);
|
|
if (isValid && isNumPadDecimal) {
|
|
element.val(value);
|
|
caret(element, selectionStart + character.length);
|
|
e.preventDefault();
|
|
} else if (min !== null && min >= 0 && value.charAt(0) === '-' || !isValid) {
|
|
e.preventDefault();
|
|
}
|
|
that._key = 0;
|
|
},
|
|
_numericRegex: function (numberFormat) {
|
|
var that = this;
|
|
var separator = numberFormat[POINT];
|
|
var precision = that.options.decimals;
|
|
if (separator === POINT) {
|
|
separator = '\\' + separator;
|
|
}
|
|
if (precision === NULL) {
|
|
precision = numberFormat.decimals;
|
|
}
|
|
if (precision === 0) {
|
|
return INTEGER_REGEXP;
|
|
}
|
|
if (that._separator !== separator) {
|
|
that._separator = separator;
|
|
that._floatRegExp = new RegExp('^(-)?(((\\d+(' + separator + '\\d*)?)|(' + separator + '\\d*)))?$');
|
|
}
|
|
return that._floatRegExp;
|
|
},
|
|
_paste: function (e) {
|
|
var that = this, element = e.target, value = element.value;
|
|
setTimeout(function () {
|
|
if (that._parse(element.value) === NULL) {
|
|
that._update(value);
|
|
}
|
|
});
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this, options = that.options;
|
|
if (value === undefined) {
|
|
return options[option];
|
|
}
|
|
value = that._parse(value);
|
|
if (!value && option === 'step') {
|
|
return;
|
|
}
|
|
options[option] = value;
|
|
that.element.attr('aria-value' + option, value).attr(option, value);
|
|
},
|
|
_spin: function (step, timeout) {
|
|
var that = this;
|
|
timeout = timeout || 500;
|
|
clearTimeout(that._spinning);
|
|
that._spinning = setTimeout(function () {
|
|
that._spin(step, 50);
|
|
}, timeout);
|
|
that._step(step);
|
|
},
|
|
_step: function (step) {
|
|
var that = this, element = that.element, value = that._parse(element.val()) || 0;
|
|
if (activeElement() != element[0]) {
|
|
that._focusin();
|
|
}
|
|
value += that.options.step * step;
|
|
that._update(that._adjust(value));
|
|
that._typing = false;
|
|
that.trigger(SPIN);
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_toggleText: function (toggle) {
|
|
var that = this;
|
|
that._text.toggle(toggle);
|
|
that.element.toggle(!toggle);
|
|
},
|
|
_parse: function (value, culture) {
|
|
return parse(value, this._culture(culture), this.options.format);
|
|
},
|
|
_update: function (value) {
|
|
var that = this, options = that.options, format = options.format, decimals = options.decimals, culture = that._culture(), numberFormat = that._format(format, culture), isNotNull;
|
|
if (decimals === NULL) {
|
|
decimals = numberFormat.decimals;
|
|
}
|
|
value = that._parse(value, culture);
|
|
isNotNull = value !== NULL;
|
|
if (isNotNull) {
|
|
value = parseFloat(round(value, decimals));
|
|
}
|
|
that._value = value = that._adjust(value);
|
|
that._placeholder(kendo.toString(value, format, culture));
|
|
if (isNotNull) {
|
|
value = value.toString();
|
|
if (value.indexOf('e') !== -1) {
|
|
value = round(+value, decimals);
|
|
}
|
|
value = value.replace(POINT, numberFormat[POINT]);
|
|
} else {
|
|
value = '';
|
|
}
|
|
that.element.val(value).attr('aria-valuenow', value);
|
|
},
|
|
_placeholder: function (value) {
|
|
this._text.val(value);
|
|
if (!placeholderSupported && !value) {
|
|
this._text.val(this.options.placeholder);
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, DOMElement = element[0], wrapper;
|
|
wrapper = element.parents('.k-numerictextbox');
|
|
if (!wrapper.is('span.k-numerictextbox')) {
|
|
wrapper = element.hide().wrap('<span class="k-numeric-wrap k-state-default" />').parent();
|
|
wrapper = wrapper.wrap('<span/>').parent();
|
|
}
|
|
wrapper[0].style.cssText = DOMElement.style.cssText;
|
|
DOMElement.style.width = '';
|
|
that.wrapper = wrapper.addClass('k-widget k-numerictextbox').addClass(DOMElement.className).css('display', '');
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
setTimeout(function () {
|
|
that.value(element[0].value);
|
|
that.max(that._initialOptions.max);
|
|
that.min(that._initialOptions.min);
|
|
});
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
}
|
|
});
|
|
function buttonHtml(className, text) {
|
|
return '<span unselectable="on" class="k-link"><span unselectable="on" class="k-icon k-i-arrow-' + className + '" title="' + text + '">' + text + '</span></span>';
|
|
}
|
|
ui.plugin(NumericTextBox);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.filtermenu', [
|
|
'kendo.datepicker',
|
|
'kendo.numerictextbox',
|
|
'kendo.dropdownlist',
|
|
'kendo.binder'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'filtermenu',
|
|
name: 'Filtering Menu',
|
|
category: 'framework',
|
|
depends: [
|
|
'datepicker',
|
|
'numerictextbox',
|
|
'dropdownlist',
|
|
'binder'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, proxy = $.proxy, POPUP = 'kendoPopup', INIT = 'init', REFRESH = 'refresh', CHANGE = 'change', NS = '.kendoFilterMenu', EQ = 'Is equal to', NEQ = 'Is not equal to', roles = {
|
|
'number': 'numerictextbox',
|
|
'date': 'datepicker'
|
|
}, mobileRoles = {
|
|
'string': 'text',
|
|
'number': 'number',
|
|
'date': 'date'
|
|
}, isFunction = kendo.isFunction, Widget = ui.Widget;
|
|
var booleanTemplate = '<div>' + '<div class="k-filter-help-text">#=messages.info#</div>' + '<label>' + '<input type="radio" data-#=ns#bind="checked: filters[0].value" value="true" name="filters[0].value"/>' + '#=messages.isTrue#' + '</label>' + '<label>' + '<input type="radio" data-#=ns#bind="checked: filters[0].value" value="false" name="filters[0].value"/>' + '#=messages.isFalse#' + '</label>' + '<div>' + '<button type="submit" class="k-button k-primary">#=messages.filter#</button>' + '<button type="reset" class="k-button">#=messages.clear#</button>' + '</div>' + '</div>';
|
|
var defaultTemplate = '<div>' + '<div class="k-filter-help-text">#=messages.info#</div>' + '<select data-#=ns#bind="value: filters[0].operator" data-#=ns#role="dropdownlist">' + '#for(var op in operators){#' + '<option value="#=op#">#=operators[op]#</option>' + '#}#' + '</select>' + '#if(values){#' + '<select data-#=ns#bind="value:filters[0].value" data-#=ns#text-field="text" data-#=ns#value-field="value" data-#=ns#source=\'#=kendo.stringify(values).replace(/\'/g,"&\\#39;")#\' data-#=ns#role="dropdownlist" data-#=ns#option-label="#=messages.selectValue#" data-#=ns#value-primitive="true">' + '</select>' + '#}else{#' + '<input data-#=ns#bind="value:filters[0].value" class="k-textbox" type="text" #=role ? "data-" + ns + "role=\'" + role + "\'" : ""# />' + '#}#' + '#if(extra){#' + '<select class="k-filter-and" data-#=ns#bind="value: logic" data-#=ns#role="dropdownlist">' + '<option value="and">#=messages.and#</option>' + '<option value="or">#=messages.or#</option>' + '</select>' + '<select data-#=ns#bind="value: filters[1].operator" data-#=ns#role="dropdownlist">' + '#for(var op in operators){#' + '<option value="#=op#">#=operators[op]#</option>' + '#}#' + '</select>' + '#if(values){#' + '<select data-#=ns#bind="value:filters[1].value" data-#=ns#text-field="text" data-#=ns#value-field="value" data-#=ns#source=\'#=kendo.stringify(values).replace(/\'/g,"&\\#39;")#\' data-#=ns#role="dropdownlist" data-#=ns#option-label="#=messages.selectValue#" data-#=ns#value-primitive="true">' + '</select>' + '#}else{#' + '<input data-#=ns#bind="value: filters[1].value" class="k-textbox" type="text" #=role ? "data-" + ns + "role=\'" + role + "\'" : ""#/>' + '#}#' + '#}#' + '<div>' + '<button type="submit" class="k-button k-primary">#=messages.filter#</button>' + '<button type="reset" class="k-button">#=messages.clear#</button>' + '</div>' + '</div>';
|
|
var defaultMobileTemplate = '<div data-#=ns#role="view" data-#=ns#init-widgets="false" data-#=ns#use-native-scrolling="true" class="k-grid-filter-menu">' + '<div data-#=ns#role="header" class="k-header">' + '<button class="k-button k-cancel">#=messages.cancel#</button>' + '#=title#' + '<button type="submit" class="k-button k-submit">#=messages.filter#</button>' + '</div>' + '<form class="k-filter-menu k-mobile-list">' + '<ul class="k-filter-help-text"><li><span class="k-link">#=messages.info#</span>' + '<ul>' + '<li class="k-item"><label class="k-label">#=messages.operator#' + '<select data-#=ns#bind="value: filters[0].operator">' + '#for(var op in operators){#' + '<option value="#=op#">#=operators[op]#</option>' + '#}#' + '</select>' + '</label></li>' + '<li class="k-item"><label class="k-label">#=messages.value#' + '#if(values){#' + '<select data-#=ns#bind="value:filters[0].value">' + '<option value="">#=messages.selectValue#</option>' + '#for(var val in values){#' + '<option value="#=values[val].value#">#=values[val].text#</option>' + '#}#' + '</select>' + '#}else{#' + '<input data-#=ns#bind="value:filters[0].value" class="k-textbox" type="#=inputType#" ' + '#=useRole ? "data-" + ns + "role=\'" + role + "\'" : ""# />' + '#}#' + '</label></li>' + '#if(extra){#' + '</ul>' + '<ul class="k-filter-help-text"><li><span class="k-link"></span>' + '<li class="k-item"><label class="k-label"><input type="radio" name="logic" class="k-check" data-#=ns#bind="checked: logic" value="and" />#=messages.and#</label></li>' + '<li class="k-item"><label class="k-label"><input type="radio" name="logic" class="k-check" data-#=ns#bind="checked: logic" value="or" />#=messages.or#</label></li>' + '</ul>' + '<ul class="k-filter-help-text"><li><span class="k-link"></span>' + '<li class="k-item"><label class="k-label">#=messages.operator#' + '<select data-#=ns#bind="value: filters[1].operator">' + '#for(var op in operators){#' + '<option value="#=op#">#=operators[op]#</option>' + '#}#' + '</select>' + '</label></li>' + '<li class="k-item"><label class="k-label">#=messages.value#' + '#if(values){#' + '<select data-#=ns#bind="value:filters[1].value">' + '<option value="">#=messages.selectValue#</option>' + '#for(var val in values){#' + '<option value="#=values[val].value#">#=values[val].text#</option>' + '#}#' + '</select>' + '#}else{#' + '<input data-#=ns#bind="value:filters[1].value" class="k-textbox" type="#=inputType#" ' + '#=useRole ? "data-" + ns + "role=\'" + role + "\'" : ""# />' + '#}#' + '</label></li>' + '#}#' + '</ul>' + '</li><li class="k-button-container">' + '<button type="reset" class="k-button">#=messages.clear#</button>' + '</li></ul>' + '</div>' + '</form>' + '</div>';
|
|
var booleanMobileTemplate = '<div data-#=ns#role="view" data-#=ns#init-widgets="false" data-#=ns#use-native-scrolling="true" class="k-grid-filter-menu">' + '<div data-#=ns#role="header" class="k-header">' + '<button class="k-button k-cancel">#=messages.cancel#</button>' + '#=title#' + '<button type="submit" class="k-button k-submit">#=messages.filter#</button>' + '</div>' + '<form class="k-filter-menu k-mobile-list">' + '<ul class="k-filter-help-text"><li><span class="k-link">#=messages.info#</span>' + '<ul>' + '<li class="k-item"><label class="k-label">' + '<input class="k-check" type="radio" data-#=ns#bind="checked: filters[0].value" value="true" name="filters[0].value"/>' + '#=messages.isTrue#' + '</label></li>' + '<li class="k-item"><label class="k-label">' + '<input class="k-check" type="radio" data-#=ns#bind="checked: filters[0].value" value="false" name="filters[0].value"/>' + '#=messages.isFalse#' + '</label></li>' + '</ul>' + '</li><li class="k-button-container">' + '<button type="reset" class="k-button">#=messages.clear#</button>' + '</li></ul>' + '</form>' + '</div>';
|
|
function removeFiltersForField(expression, field) {
|
|
if (expression.filters) {
|
|
expression.filters = $.grep(expression.filters, function (filter) {
|
|
removeFiltersForField(filter, field);
|
|
if (filter.filters) {
|
|
return filter.filters.length;
|
|
} else {
|
|
return filter.field != field;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function convertItems(items) {
|
|
var idx, length, item, value, text, result;
|
|
if (items && items.length) {
|
|
result = [];
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
item = items[idx];
|
|
text = item.text !== '' ? item.text || item.value || item : item.text;
|
|
value = item.value == null ? item.text || item : item.value;
|
|
result[idx] = {
|
|
text: text,
|
|
value: value
|
|
};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function clearFilter(filters, field) {
|
|
return $.grep(filters, function (expr) {
|
|
if (expr.filters) {
|
|
expr.filters = $.grep(expr.filters, function (nested) {
|
|
return nested.field != field;
|
|
});
|
|
return expr.filters.length;
|
|
}
|
|
return expr.field != field;
|
|
});
|
|
}
|
|
var FilterMenu = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, type = 'string', operators, initial, link, field;
|
|
Widget.fn.init.call(that, element, options);
|
|
operators = that.operators = options.operators || {};
|
|
element = that.element;
|
|
options = that.options;
|
|
if (!options.appendToElement) {
|
|
link = element.addClass('k-with-icon k-filterable').find('.k-grid-filter');
|
|
if (!link[0]) {
|
|
link = element.prepend('<a class="k-grid-filter" href="#"><span class="k-icon k-filter">' + options.messages.filter + '</span></a>').find('.k-grid-filter');
|
|
}
|
|
link.attr('tabindex', -1).on('click' + NS, proxy(that._click, that));
|
|
}
|
|
that.link = link || $();
|
|
that.dataSource = DataSource.create(options.dataSource);
|
|
that.field = options.field || element.attr(kendo.attr('field'));
|
|
that.model = that.dataSource.reader.model;
|
|
that._parse = function (value) {
|
|
return value != null ? value + '' : value;
|
|
};
|
|
if (that.model && that.model.fields) {
|
|
field = that.model.fields[that.field];
|
|
if (field) {
|
|
type = field.type || 'string';
|
|
if (field.parse) {
|
|
that._parse = proxy(field.parse, field);
|
|
}
|
|
}
|
|
}
|
|
if (options.values) {
|
|
type = 'enums';
|
|
}
|
|
that.type = type;
|
|
operators = operators[type] || options.operators[type];
|
|
for (initial in operators) {
|
|
break;
|
|
}
|
|
that._defaultFilter = function () {
|
|
return {
|
|
field: that.field,
|
|
operator: initial || 'eq',
|
|
value: ''
|
|
};
|
|
};
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
if (options.appendToElement) {
|
|
that._init();
|
|
} else {
|
|
that.refresh();
|
|
}
|
|
},
|
|
_init: function () {
|
|
var that = this, ui = that.options.ui, setUI = isFunction(ui), role;
|
|
that.pane = that.options.pane;
|
|
if (that.pane) {
|
|
that._isMobile = true;
|
|
}
|
|
if (!setUI) {
|
|
role = ui || roles[that.type];
|
|
}
|
|
if (that._isMobile) {
|
|
that._createMobileForm(role);
|
|
} else {
|
|
that._createForm(role);
|
|
}
|
|
that.form.on('submit' + NS, proxy(that._submit, that)).on('reset' + NS, proxy(that._reset, that));
|
|
if (setUI) {
|
|
that.form.find('.k-textbox').removeClass('k-textbox').each(function () {
|
|
ui($(this));
|
|
});
|
|
}
|
|
that.form.find('[' + kendo.attr('role') + '=numerictextbox]').removeClass('k-textbox').end().find('[' + kendo.attr('role') + '=datetimepicker]').removeClass('k-textbox').end().find('[' + kendo.attr('role') + '=timepicker]').removeClass('k-textbox').end().find('[' + kendo.attr('role') + '=datepicker]').removeClass('k-textbox');
|
|
that.refresh();
|
|
that.trigger(INIT, {
|
|
field: that.field,
|
|
container: that.form
|
|
});
|
|
kendo.cycleForm(that.form);
|
|
},
|
|
_createForm: function (role) {
|
|
var that = this, options = that.options, operators = that.operators || {}, type = that.type;
|
|
operators = operators[type] || options.operators[type];
|
|
that.form = $('<form class="k-filter-menu"/>').html(kendo.template(type === 'boolean' ? booleanTemplate : defaultTemplate)({
|
|
field: that.field,
|
|
format: options.format,
|
|
ns: kendo.ns,
|
|
messages: options.messages,
|
|
extra: options.extra,
|
|
operators: operators,
|
|
type: type,
|
|
role: role,
|
|
values: convertItems(options.values)
|
|
}));
|
|
if (!options.appendToElement) {
|
|
that.popup = that.form[POPUP]({
|
|
anchor: that.link,
|
|
open: proxy(that._open, that),
|
|
activate: proxy(that._activate, that),
|
|
close: function () {
|
|
if (that.options.closeCallback) {
|
|
that.options.closeCallback(that.element);
|
|
}
|
|
}
|
|
}).data(POPUP);
|
|
} else {
|
|
that.element.append(that.form);
|
|
that.popup = that.element.closest('.k-popup').data(POPUP);
|
|
}
|
|
that.form.on('keydown' + NS, proxy(that._keydown, that));
|
|
},
|
|
_createMobileForm: function (role) {
|
|
var that = this, options = that.options, operators = that.operators || {}, type = that.type;
|
|
operators = operators[type] || options.operators[type];
|
|
that.form = $('<div />').html(kendo.template(type === 'boolean' ? booleanMobileTemplate : defaultMobileTemplate)({
|
|
field: that.field,
|
|
title: options.title || that.field,
|
|
format: options.format,
|
|
ns: kendo.ns,
|
|
messages: options.messages,
|
|
extra: options.extra,
|
|
operators: operators,
|
|
type: type,
|
|
role: role,
|
|
useRole: !kendo.support.input.date && type === 'date' || type === 'number',
|
|
inputType: mobileRoles[type],
|
|
values: convertItems(options.values)
|
|
}));
|
|
that.view = that.pane.append(that.form.html());
|
|
that.form = that.view.element.find('form');
|
|
that.view.element.on('click', '.k-submit', function (e) {
|
|
that.form.submit();
|
|
e.preventDefault();
|
|
}).on('click', '.k-cancel', function (e) {
|
|
that._closeForm();
|
|
e.preventDefault();
|
|
});
|
|
},
|
|
refresh: function () {
|
|
var that = this, expression = that.dataSource.filter() || {
|
|
filters: [],
|
|
logic: 'and'
|
|
};
|
|
that.filterModel = kendo.observable({
|
|
logic: 'and',
|
|
filters: [
|
|
that._defaultFilter(),
|
|
that._defaultFilter()
|
|
]
|
|
});
|
|
if (that.form) {
|
|
kendo.bind(that.form.children().first(), that.filterModel);
|
|
}
|
|
if (that._bind(expression)) {
|
|
that.link.addClass('k-state-active');
|
|
} else {
|
|
that.link.removeClass('k-state-active');
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
if (that.form) {
|
|
kendo.unbind(that.form);
|
|
kendo.destroy(that.form);
|
|
that.form.unbind(NS);
|
|
if (that.popup) {
|
|
that.popup.destroy();
|
|
that.popup = null;
|
|
}
|
|
that.form = null;
|
|
}
|
|
if (that.view) {
|
|
that.view.purge();
|
|
that.view = null;
|
|
}
|
|
that.link.unbind(NS);
|
|
if (that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that.dataSource = null;
|
|
}
|
|
that.element = that.link = that._refreshHandler = that.filterModel = null;
|
|
},
|
|
_bind: function (expression) {
|
|
var that = this, filters = expression.filters, idx, length, found = false, current = 0, filterModel = that.filterModel, currentFilter, filter;
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
if (filter.field == that.field) {
|
|
filterModel.set('logic', expression.logic);
|
|
currentFilter = filterModel.filters[current];
|
|
if (!currentFilter) {
|
|
filterModel.filters.push({ field: that.field });
|
|
currentFilter = filterModel.filters[current];
|
|
}
|
|
currentFilter.set('value', that._parse(filter.value));
|
|
currentFilter.set('operator', filter.operator);
|
|
current++;
|
|
found = true;
|
|
} else if (filter.filters) {
|
|
found = found || that._bind(filter);
|
|
}
|
|
}
|
|
return found;
|
|
},
|
|
_stripFilters: function (filters) {
|
|
return $.grep(filters, function (filter) {
|
|
return filter.value !== '' && filter.value != null || (filter.operator === 'isnull' || filter.operator === 'isnotnull' || filter.operator === 'isempty' || filter.operator === 'isnotempty');
|
|
});
|
|
},
|
|
_merge: function (expression) {
|
|
var that = this, logic = expression.logic || 'and', filters = this._stripFilters(expression.filters), filter, result = that.dataSource.filter() || {
|
|
filters: [],
|
|
logic: 'and'
|
|
}, idx, length;
|
|
removeFiltersForField(result, that.field);
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
filter.value = that._parse(filter.value);
|
|
}
|
|
if (filters.length) {
|
|
if (result.filters.length) {
|
|
expression.filters = filters;
|
|
if (result.logic !== 'and') {
|
|
result.filters = [{
|
|
logic: result.logic,
|
|
filters: result.filters
|
|
}];
|
|
result.logic = 'and';
|
|
}
|
|
if (filters.length > 1) {
|
|
result.filters.push(expression);
|
|
} else {
|
|
result.filters.push(filters[0]);
|
|
}
|
|
} else {
|
|
result.filters = filters;
|
|
result.logic = logic;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
filter: function (expression) {
|
|
expression = this._merge(expression);
|
|
if (expression.filters.length) {
|
|
this.dataSource.filter(expression);
|
|
}
|
|
},
|
|
clear: function () {
|
|
var that = this, expression = that.dataSource.filter() || { filters: [] };
|
|
expression.filters = $.grep(expression.filters, function (filter) {
|
|
if (filter.filters) {
|
|
filter.filters = clearFilter(filter.filters, that.field);
|
|
return filter.filters.length;
|
|
}
|
|
return filter.field != that.field;
|
|
});
|
|
if (!expression.filters.length) {
|
|
expression = null;
|
|
}
|
|
that.dataSource.filter(expression);
|
|
},
|
|
_submit: function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.filter(this.filterModel.toJSON());
|
|
this._closeForm();
|
|
},
|
|
_reset: function () {
|
|
this.clear();
|
|
this._closeForm();
|
|
},
|
|
_closeForm: function () {
|
|
if (this._isMobile) {
|
|
this.pane.navigate('', this.options.animations.right);
|
|
} else {
|
|
this.popup.close();
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (!this.popup && !this.pane) {
|
|
this._init();
|
|
}
|
|
if (this._isMobile) {
|
|
this.pane.navigate(this.view, this.options.animations.left);
|
|
} else {
|
|
this.popup.toggle();
|
|
}
|
|
},
|
|
_open: function () {
|
|
var popup;
|
|
$('.k-filter-menu').not(this.form).each(function () {
|
|
popup = $(this).data(POPUP);
|
|
if (popup) {
|
|
popup.close();
|
|
}
|
|
});
|
|
},
|
|
_activate: function () {
|
|
this.form.find(':kendoFocusable:first').focus();
|
|
},
|
|
_keydown: function (e) {
|
|
if (e.keyCode == kendo.keys.ESC) {
|
|
this.popup.close();
|
|
}
|
|
},
|
|
events: [INIT],
|
|
options: {
|
|
name: 'FilterMenu',
|
|
extra: true,
|
|
appendToElement: false,
|
|
type: 'string',
|
|
operators: {
|
|
string: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
startswith: 'Starts with',
|
|
contains: 'Contains',
|
|
doesnotcontain: 'Does not contain',
|
|
endswith: 'Ends with',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null',
|
|
isempty: 'Is empty',
|
|
isnotempty: 'Is not empty'
|
|
},
|
|
number: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
gte: 'Is greater than or equal to',
|
|
gt: 'Is greater than',
|
|
lte: 'Is less than or equal to',
|
|
lt: 'Is less than',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
},
|
|
date: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
gte: 'Is after or equal to',
|
|
gt: 'Is after',
|
|
lte: 'Is before or equal to',
|
|
lt: 'Is before',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
},
|
|
enums: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
}
|
|
},
|
|
messages: {
|
|
info: 'Show items with value that:',
|
|
isTrue: 'is true',
|
|
isFalse: 'is false',
|
|
filter: 'Filter',
|
|
clear: 'Clear',
|
|
and: 'And',
|
|
or: 'Or',
|
|
selectValue: '-Select value-',
|
|
operator: 'Operator',
|
|
value: 'Value',
|
|
cancel: 'Cancel'
|
|
},
|
|
animations: {
|
|
left: 'slide',
|
|
right: 'slide:right'
|
|
}
|
|
}
|
|
});
|
|
var multiCheckNS = '.kendoFilterMultiCheck';
|
|
function filterValuesForField(expression, field) {
|
|
if (expression.filters) {
|
|
expression.filters = $.grep(expression.filters, function (filter) {
|
|
filterValuesForField(filter, field);
|
|
if (filter.filters) {
|
|
return filter.filters.length;
|
|
} else {
|
|
return filter.field == field && filter.operator == 'eq';
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function flatFilterValues(expression) {
|
|
if (expression.logic == 'and' && expression.filters.length > 1) {
|
|
return [];
|
|
}
|
|
if (expression.filters) {
|
|
return $.map(expression.filters, function (filter) {
|
|
return flatFilterValues(filter);
|
|
});
|
|
} else if (expression.value !== null && expression.value !== undefined) {
|
|
return [expression.value];
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
function distinct(items, field) {
|
|
var getter = kendo.getter(field, true), result = [], index = 0, seen = {};
|
|
while (index < items.length) {
|
|
var item = items[index++], text = getter(item);
|
|
if (text !== undefined && text !== null && !seen.hasOwnProperty(text)) {
|
|
result.push(item);
|
|
seen[text] = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function removeDuplicates(dataSelector, dataTextField) {
|
|
return function (e) {
|
|
var items = dataSelector(e);
|
|
return distinct(items, dataTextField);
|
|
};
|
|
}
|
|
var DataSource = kendo.data.DataSource;
|
|
var FilterMultiCheck = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
options = this.options;
|
|
this.element = $(element);
|
|
var field = this.field = this.options.field || this.element.attr(kendo.attr('field'));
|
|
var checkSource = options.checkSource;
|
|
if (this._foreignKeyValues()) {
|
|
this.checkSource = DataSource.create(options.values);
|
|
this.checkSource.fetch();
|
|
} else if (options.forceUnique) {
|
|
checkSource = options.dataSource.options;
|
|
delete checkSource.pageSize;
|
|
this.checkSource = DataSource.create(checkSource);
|
|
this.checkSource.reader.data = removeDuplicates(this.checkSource.reader.data, this.field);
|
|
} else {
|
|
this.checkSource = DataSource.create(checkSource);
|
|
}
|
|
this.dataSource = options.dataSource;
|
|
this.model = this.dataSource.reader.model;
|
|
this._parse = function (value) {
|
|
return value + '';
|
|
};
|
|
if (this.model && this.model.fields) {
|
|
field = this.model.fields[this.field];
|
|
if (field) {
|
|
if (field.type == 'number') {
|
|
this._parse = parseFloat;
|
|
} else if (field.parse) {
|
|
this._parse = proxy(field.parse, field);
|
|
}
|
|
this.type = field.type || 'string';
|
|
}
|
|
}
|
|
if (!options.appendToElement) {
|
|
this._createLink();
|
|
} else {
|
|
this._init();
|
|
}
|
|
this._refreshHandler = proxy(this.refresh, this);
|
|
this.dataSource.bind(CHANGE, this._refreshHandler);
|
|
},
|
|
_createLink: function () {
|
|
var element = this.element;
|
|
var link = element.addClass('k-with-icon k-filterable').find('.k-grid-filter');
|
|
if (!link[0]) {
|
|
link = element.prepend('<a class="k-grid-filter" href="#"><span class="k-icon k-filter"/></a>').find('.k-grid-filter');
|
|
}
|
|
this._link = link.attr('tabindex', -1).on('click' + NS, proxy(this._click, this));
|
|
},
|
|
_init: function () {
|
|
var that = this;
|
|
var forceUnique = this.options.forceUnique;
|
|
var options = this.options;
|
|
this.pane = options.pane;
|
|
if (this.pane) {
|
|
this._isMobile = true;
|
|
}
|
|
this._createForm();
|
|
if (this._foreignKeyValues()) {
|
|
this.refresh();
|
|
} else if (forceUnique && !this.checkSource.options.serverPaging && this.dataSource.data().length) {
|
|
this.checkSource.data(distinct(this.dataSource.data(), this.field));
|
|
this.refresh();
|
|
} else {
|
|
this._attachProgress();
|
|
this.checkSource.fetch(function () {
|
|
that.refresh.call(that);
|
|
});
|
|
}
|
|
if (!this.options.forceUnique) {
|
|
this.checkChangeHandler = function () {
|
|
that.container.empty();
|
|
that.refresh();
|
|
};
|
|
this.checkSource.bind(CHANGE, this.checkChangeHandler);
|
|
}
|
|
this.form.on('keydown' + multiCheckNS, proxy(this._keydown, this)).on('submit' + multiCheckNS, proxy(this._filter, this)).on('reset' + multiCheckNS, proxy(this._reset, this));
|
|
this.trigger(INIT, {
|
|
field: this.field,
|
|
container: this.form
|
|
});
|
|
},
|
|
_attachProgress: function () {
|
|
var that = this;
|
|
this._progressHandler = function () {
|
|
ui.progress(that.container, true);
|
|
};
|
|
this._progressHideHandler = function () {
|
|
ui.progress(that.container, false);
|
|
};
|
|
this.checkSource.bind('progress', this._progressHandler).bind('change', this._progressHideHandler);
|
|
},
|
|
_input: function () {
|
|
var that = this;
|
|
that._clearTypingTimeout();
|
|
that._typingTimeout = setTimeout(function () {
|
|
that.search();
|
|
}, 100);
|
|
},
|
|
_clearTypingTimeout: function () {
|
|
if (this._typingTimeout) {
|
|
clearTimeout(this._typingTimeout);
|
|
this._typingTimeout = null;
|
|
}
|
|
},
|
|
search: function () {
|
|
var ignoreCase = this.options.ignoreCase;
|
|
var searchString = this.searchTextBox[0].value;
|
|
var labels = this.container.find('label');
|
|
if (ignoreCase) {
|
|
searchString = searchString.toLowerCase();
|
|
}
|
|
for (var i = this.options.checkAll ? 1 : 0; i < labels.length; i++) {
|
|
var label = labels[i];
|
|
var labelText = label.textContent || label.innerText;
|
|
if (ignoreCase) {
|
|
labelText = labelText.toLowerCase();
|
|
}
|
|
label.style.display = labelText.indexOf(searchString) >= 0 ? '' : 'none';
|
|
}
|
|
},
|
|
_createForm: function () {
|
|
var options = this.options;
|
|
var html = '';
|
|
if (!this._isMobile && options.search) {
|
|
html += '<div class=\'k-textbox k-space-right\'>' + '<input placeholder=\'' + options.messages.search + '\'/>' + '<span class=\'k-icon k-font-icon k-i-search\' />' + '</div>';
|
|
}
|
|
html += '<ul class=\'k-reset k-multicheck-wrap\'></ul><button type=\'submit\' class=\'k-button k-primary\'>' + options.messages.filter + '</button>';
|
|
html += '<button type=\'reset\' class=\'k-button\'>' + options.messages.clear + '</button>';
|
|
this.form = $('<form class="k-filter-menu"/>').html(html);
|
|
this.container = this.form.find('.k-multicheck-wrap');
|
|
if (options.search) {
|
|
this.searchTextBox = this.form.find('.k-textbox > input');
|
|
this.searchTextBox.on('input', proxy(this._input, this));
|
|
}
|
|
if (this._isMobile) {
|
|
this.view = this.pane.append(this.form.addClass('k-mobile-list').wrap('<div/>').parent().html());
|
|
var element = this.view.element;
|
|
this.form = element.find('form');
|
|
this.container = element.find('.k-multicheck-wrap');
|
|
var that = this;
|
|
element.on('click', '.k-primary', function (e) {
|
|
that.form.submit();
|
|
e.preventDefault();
|
|
}).on('click', '[type=reset]', function (e) {
|
|
that._reset();
|
|
e.preventDefault();
|
|
});
|
|
} else {
|
|
if (!options.appendToElement) {
|
|
this.popup = this.form.kendoPopup({ anchor: this._link }).data(POPUP);
|
|
} else {
|
|
this.popup = this.element.closest('.k-popup').data(POPUP);
|
|
this.element.append(this.form);
|
|
}
|
|
}
|
|
},
|
|
createCheckAllItem: function () {
|
|
var options = this.options;
|
|
var template = kendo.template(options.itemTemplate({
|
|
field: 'all',
|
|
mobile: this._isMobile
|
|
}));
|
|
var checkAllContainer = $(template({ all: options.messages.checkAll }));
|
|
this.container.prepend(checkAllContainer);
|
|
this.checkBoxAll = checkAllContainer.find(':checkbox').eq(0).addClass('k-check-all');
|
|
this.checkAllHandler = proxy(this.checkAll, this);
|
|
this.checkBoxAll.on(CHANGE + multiCheckNS, this.checkAllHandler);
|
|
},
|
|
updateCheckAllState: function () {
|
|
if (this.checkBoxAll) {
|
|
var state = this.container.find(':checkbox:not(.k-check-all)').length == this.container.find(':checked:not(.k-check-all)').length;
|
|
this.checkBoxAll.prop('checked', state);
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var forceUnique = this.options.forceUnique;
|
|
var dataSource = this.dataSource;
|
|
var filters = this.getFilterArray();
|
|
if (this._link) {
|
|
this._link.toggleClass('k-state-active', filters.length !== 0);
|
|
}
|
|
if (this.form) {
|
|
if (e && forceUnique && e.sender === dataSource && !dataSource.options.serverPaging && (e.action == 'itemchange' || e.action == 'add' || e.action == 'remove' || dataSource.options.autoSync && e.action === 'sync') && !this._foreignKeyValues()) {
|
|
this.checkSource.data(distinct(this.dataSource.data(), this.field));
|
|
this.container.empty();
|
|
}
|
|
if (this.container.is(':empty')) {
|
|
this.createCheckBoxes();
|
|
}
|
|
this.checkValues(filters);
|
|
this.trigger(REFRESH);
|
|
}
|
|
},
|
|
getFilterArray: function () {
|
|
var expression = $.extend(true, {}, {
|
|
filters: [],
|
|
logic: 'and'
|
|
}, this.dataSource.filter());
|
|
filterValuesForField(expression, this.field);
|
|
var flatValues = flatFilterValues(expression);
|
|
return flatValues;
|
|
},
|
|
createCheckBoxes: function () {
|
|
var options = this.options;
|
|
var data;
|
|
var templateOptions = {
|
|
field: this.field,
|
|
format: options.format,
|
|
mobile: this._isMobile,
|
|
type: this.type
|
|
};
|
|
if (!this.options.forceUnique) {
|
|
data = this.checkSource.view();
|
|
} else if (this._foreignKeyValues()) {
|
|
data = this.checkSource.data();
|
|
templateOptions.valueField = 'value';
|
|
templateOptions.field = 'text';
|
|
} else {
|
|
data = this.checkSource.data();
|
|
}
|
|
var template = kendo.template(options.itemTemplate(templateOptions));
|
|
var itemsHtml = kendo.render(template, data);
|
|
if (options.checkAll) {
|
|
this.createCheckAllItem();
|
|
this.container.on(CHANGE + multiCheckNS, ':checkbox', proxy(this.updateCheckAllState, this));
|
|
}
|
|
this.container.append(itemsHtml);
|
|
},
|
|
checkAll: function () {
|
|
var state = this.checkBoxAll.is(':checked');
|
|
this.container.find(':checkbox').prop('checked', state);
|
|
},
|
|
checkValues: function (values) {
|
|
var that = this;
|
|
$($.grep(this.container.find(':checkbox').prop('checked', false), function (ele) {
|
|
var found = false;
|
|
if ($(ele).is('.k-check-all')) {
|
|
return;
|
|
}
|
|
var checkBoxVal = that._parse($(ele).val());
|
|
for (var i = 0; i < values.length; i++) {
|
|
if (that.type == 'date') {
|
|
found = values[i].getTime() == checkBoxVal.getTime();
|
|
} else {
|
|
found = values[i] == checkBoxVal;
|
|
}
|
|
if (found) {
|
|
return found;
|
|
}
|
|
}
|
|
})).prop('checked', true);
|
|
this.updateCheckAllState();
|
|
},
|
|
_filter: function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var expression = { logic: 'or' };
|
|
var that = this;
|
|
expression.filters = $.map(this.form.find(':checkbox:checked:not(.k-check-all)'), function (item) {
|
|
return {
|
|
value: $(item).val(),
|
|
operator: 'eq',
|
|
field: that.field
|
|
};
|
|
});
|
|
expression = this._merge(expression);
|
|
if (expression.filters.length) {
|
|
this.dataSource.filter(expression);
|
|
}
|
|
this._closeForm();
|
|
},
|
|
_stripFilters: function (filters) {
|
|
return $.grep(filters, function (filter) {
|
|
return filter.value != null;
|
|
});
|
|
},
|
|
_foreignKeyValues: function () {
|
|
var options = this.options;
|
|
return options.values && !options.checkSource;
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
if (that.form) {
|
|
kendo.unbind(that.form);
|
|
kendo.destroy(that.form);
|
|
that.form.unbind(multiCheckNS);
|
|
if (that.popup) {
|
|
that.popup.destroy();
|
|
that.popup = null;
|
|
}
|
|
that.form = null;
|
|
if (that.container) {
|
|
that.container.unbind(multiCheckNS);
|
|
that.container = null;
|
|
}
|
|
if (that.checkBoxAll) {
|
|
that.checkBoxAll.unbind(multiCheckNS);
|
|
}
|
|
}
|
|
if (that.view) {
|
|
that.view.purge();
|
|
that.view = null;
|
|
}
|
|
if (that._link) {
|
|
that._link.unbind(NS);
|
|
}
|
|
if (that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that.dataSource = null;
|
|
}
|
|
if (that.checkChangeHandler) {
|
|
that.checkSource.unbind(CHANGE, that.checkChangeHandler);
|
|
}
|
|
if (that._progressHandler) {
|
|
that.checkSource.unbind('progress', that._progressHandler);
|
|
}
|
|
if (that._progressHideHandler) {
|
|
that.checkSource.unbind('change', that._progressHideHandler);
|
|
}
|
|
this._clearTypingTimeout();
|
|
this.searchTextBox = null;
|
|
that.element = that.checkSource = that.container = that.checkBoxAll = that._link = that._refreshHandler = that.checkAllHandler = null;
|
|
},
|
|
options: {
|
|
name: 'FilterMultiCheck',
|
|
itemTemplate: function (options) {
|
|
var field = options.field;
|
|
var format = options.format;
|
|
var valueField = options.valueField;
|
|
var mobile = options.mobile;
|
|
var valueFormat = '';
|
|
if (valueField === undefined) {
|
|
valueField = field;
|
|
}
|
|
if (options.type == 'date') {
|
|
valueFormat = ':yyyy-MM-ddTHH:mm:sszzz';
|
|
}
|
|
return '<li class=\'k-item\'>' + '<label class=\'k-label\'>' + '<input type=\'checkbox\' class=\'' + (mobile ? 'k-check' : '') + '\' value=\'#:kendo.format(\'{0' + valueFormat + '}\',' + valueField + ')#\'/>' + '#:kendo.format(\'' + (format ? format : '{0}') + '\', ' + field + ')#' + '</label>' + '</li>';
|
|
},
|
|
checkAll: true,
|
|
search: false,
|
|
ignoreCase: true,
|
|
appendToElement: false,
|
|
messages: {
|
|
checkAll: 'Select All',
|
|
clear: 'Clear',
|
|
filter: 'Filter',
|
|
search: 'Search'
|
|
},
|
|
forceUnique: true,
|
|
animations: {
|
|
left: 'slide',
|
|
right: 'slide:right'
|
|
}
|
|
},
|
|
events: [
|
|
INIT,
|
|
REFRESH
|
|
]
|
|
});
|
|
$.extend(FilterMultiCheck.fn, {
|
|
_click: FilterMenu.fn._click,
|
|
_keydown: FilterMenu.fn._keydown,
|
|
_reset: FilterMenu.fn._reset,
|
|
_closeForm: FilterMenu.fn._closeForm,
|
|
clear: FilterMenu.fn.clear,
|
|
_merge: FilterMenu.fn._merge
|
|
});
|
|
ui.plugin(FilterMenu);
|
|
ui.plugin(FilterMultiCheck);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.menu', ['kendo.popup'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'menu',
|
|
name: 'Menu',
|
|
category: 'web',
|
|
description: 'The Menu widget displays hierarchical data as a multi-level menu.',
|
|
depends: ['popup']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, activeElement = kendo._activeElement, touch = kendo.support.touch && kendo.support.mobileOS, MOUSEDOWN = 'mousedown', CLICK = 'click', extend = $.extend, proxy = $.proxy, each = $.each, template = kendo.template, keys = kendo.keys, Widget = ui.Widget, excludedNodesRegExp = /^(ul|a|div)$/i, NS = '.kendoMenu', IMG = 'img', OPEN = 'open', MENU = 'k-menu', LINK = 'k-link', LAST = 'k-last', CLOSE = 'close', TIMER = 'timer', FIRST = 'k-first', IMAGE = 'k-image', SELECT = 'select', ZINDEX = 'zIndex', ACTIVATE = 'activate', DEACTIVATE = 'deactivate', POINTERDOWN = 'touchstart' + NS + ' MSPointerDown' + NS + ' pointerdown' + NS, pointers = kendo.support.pointers, msPointers = kendo.support.msPointers, allPointers = msPointers || pointers, MOUSEENTER = pointers ? 'pointerover' : msPointers ? 'MSPointerOver' : 'mouseenter', MOUSELEAVE = pointers ? 'pointerout' : msPointers ? 'MSPointerOut' : 'mouseleave', mobile = touch || allPointers, DOCUMENT_ELEMENT = $(document.documentElement), KENDOPOPUP = 'kendoPopup', DEFAULTSTATE = 'k-state-default', HOVERSTATE = 'k-state-hover', FOCUSEDSTATE = 'k-state-focused', DISABLEDSTATE = 'k-state-disabled', menuSelector = '.k-menu', groupSelector = '.k-menu-group', popupSelector = groupSelector + ',.k-animation-container', allItemsSelector = ':not(.k-list) > .k-item', disabledSelector = '.k-item.k-state-disabled', itemSelector = '.k-item:not(.k-state-disabled)', linkSelector = '.k-item:not(.k-state-disabled) > .k-link', exclusionSelector = ':not(.k-item.k-separator)', nextSelector = exclusionSelector + ':eq(0)', lastSelector = exclusionSelector + ':last', templateSelector = '> div:not(.k-animation-container,.k-list-container)', touchPointerTypes = {
|
|
'2': 1,
|
|
'touch': 1
|
|
}, templates = {
|
|
content: template('<div class=\'k-content #= groupCssClass() #\' tabindex=\'-1\'>#= content(item) #</div>'),
|
|
group: template('<ul class=\'#= groupCssClass(group) #\'#= groupAttributes(group) # role=\'menu\' aria-hidden=\'true\'>' + '#= renderItems(data) #' + '</ul>'),
|
|
itemWrapper: template('<#= tag(item) # class=\'#= textClass(item) #\'#= textAttributes(item) #>' + '#= image(item) ##= sprite(item) ##= text(item) #' + '#= arrow(data) #' + '</#= tag(item) #>'),
|
|
item: template('<li class=\'#= wrapperCssClass(group, item) #\' role=\'menuitem\' #=item.items ? "aria-haspopup=\'true\'": ""#' + '#=item.enabled === false ? "aria-disabled=\'true\'" : \'\'#>' + '#= itemWrapper(data) #' + '# if (item.items) { #' + '#= subGroup({ items: item.items, menu: menu, group: { expanded: item.expanded } }) #' + '# } else if (item.content || item.contentUrl) { #' + '#= renderContent(data) #' + '# } #' + '</li>'),
|
|
image: template('<img class=\'k-image\' alt=\'\' src=\'#= imageUrl #\' />'),
|
|
arrow: template('<span class=\'#= arrowClass(item, group) #\'></span>'),
|
|
sprite: template('<span class=\'k-sprite #= spriteCssClass #\'></span>'),
|
|
empty: template('')
|
|
}, rendering = {
|
|
wrapperCssClass: function (group, item) {
|
|
var result = 'k-item', index = item.index;
|
|
if (item.enabled === false) {
|
|
result += ' k-state-disabled';
|
|
} else {
|
|
result += ' k-state-default';
|
|
}
|
|
if (group.firstLevel && index === 0) {
|
|
result += ' k-first';
|
|
}
|
|
if (index == group.length - 1) {
|
|
result += ' k-last';
|
|
}
|
|
if (item.cssClass) {
|
|
result += ' ' + item.cssClass;
|
|
}
|
|
return result;
|
|
},
|
|
textClass: function () {
|
|
return LINK;
|
|
},
|
|
textAttributes: function (item) {
|
|
return item.url ? ' href=\'' + item.url + '\'' : '';
|
|
},
|
|
arrowClass: function (item, group) {
|
|
var result = 'k-icon';
|
|
if (group.horizontal) {
|
|
result += ' k-i-arrow-s';
|
|
} else {
|
|
result += ' k-i-arrow-e';
|
|
}
|
|
return result;
|
|
},
|
|
text: function (item) {
|
|
return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
|
|
},
|
|
tag: function (item) {
|
|
return item.url ? 'a' : 'span';
|
|
},
|
|
groupAttributes: function (group) {
|
|
return group.expanded !== true ? ' style=\'display:none\'' : '';
|
|
},
|
|
groupCssClass: function () {
|
|
return 'k-group k-menu-group';
|
|
},
|
|
content: function (item) {
|
|
return item.content ? item.content : ' ';
|
|
}
|
|
};
|
|
function getEffectDirection(direction, root) {
|
|
direction = direction.split(' ')[!root + 0] || direction;
|
|
return direction.replace('top', 'up').replace('bottom', 'down');
|
|
}
|
|
function parseDirection(direction, root, isRtl) {
|
|
direction = direction.split(' ')[!root + 0] || direction;
|
|
var output = {
|
|
origin: [
|
|
'bottom',
|
|
isRtl ? 'right' : 'left'
|
|
],
|
|
position: [
|
|
'top',
|
|
isRtl ? 'right' : 'left'
|
|
]
|
|
}, horizontal = /left|right/.test(direction);
|
|
if (horizontal) {
|
|
output.origin = [
|
|
'top',
|
|
direction
|
|
];
|
|
output.position[1] = kendo.directions[direction].reverse;
|
|
} else {
|
|
output.origin[0] = direction;
|
|
output.position[0] = kendo.directions[direction].reverse;
|
|
}
|
|
output.origin = output.origin.join(' ');
|
|
output.position = output.position.join(' ');
|
|
return output;
|
|
}
|
|
function contains(parent, child) {
|
|
try {
|
|
return $.contains(parent, child);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
function updateItemClasses(item) {
|
|
item = $(item);
|
|
item.addClass('k-item').children(IMG).addClass(IMAGE);
|
|
item.children('a').addClass(LINK).children(IMG).addClass(IMAGE);
|
|
item.filter(':not([disabled])').addClass(DEFAULTSTATE);
|
|
item.filter('.k-separator:empty').append(' ');
|
|
item.filter('li[disabled]').addClass(DISABLEDSTATE).removeAttr('disabled').attr('aria-disabled', true);
|
|
if (!item.filter('[role]').length) {
|
|
item.attr('role', 'menuitem');
|
|
}
|
|
if (!item.children('.' + LINK).length) {
|
|
item.contents().filter(function () {
|
|
return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue));
|
|
}).wrapAll('<span class=\'' + LINK + '\'/>');
|
|
}
|
|
updateArrow(item);
|
|
updateFirstLast(item);
|
|
}
|
|
function updateArrow(item) {
|
|
item = $(item);
|
|
item.find('> .k-link > [class*=k-i-arrow]:not(.k-sprite)').remove();
|
|
item.filter(':has(.k-menu-group)').children('.k-link:not(:has([class*=k-i-arrow]:not(.k-sprite)))').each(function () {
|
|
var item = $(this), parent = item.parent().parent();
|
|
item.append('<span class=\'k-icon ' + (parent.hasClass(MENU + '-horizontal') ? 'k-i-arrow-s' : 'k-i-arrow-e') + '\'/>');
|
|
});
|
|
}
|
|
function updateFirstLast(item) {
|
|
item = $(item);
|
|
item.filter('.k-first:not(:first-child)').removeClass(FIRST);
|
|
item.filter('.k-last:not(:last-child)').removeClass(LAST);
|
|
item.filter(':first-child').addClass(FIRST);
|
|
item.filter(':last-child').addClass(LAST);
|
|
}
|
|
var Menu = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.wrapper = that.element;
|
|
options = that.options;
|
|
that._initData(options);
|
|
that._updateClasses();
|
|
that._animations(options);
|
|
that.nextItemZIndex = 100;
|
|
that._tabindex();
|
|
that._focusProxy = proxy(that._focusHandler, that);
|
|
element.on(POINTERDOWN, itemSelector, that._focusProxy).on(CLICK + NS, disabledSelector, false).on(CLICK + NS, itemSelector, proxy(that._click, that)).on('keydown' + NS, proxy(that._keydown, that)).on('focus' + NS, proxy(that._focus, that)).on('focus' + NS, '.k-content', proxy(that._focus, that)).on(POINTERDOWN + ' ' + MOUSEDOWN + NS, '.k-content', proxy(that._preventClose, that)).on('blur' + NS, proxy(that._removeHoverItem, that)).on('blur' + NS, '[tabindex]', proxy(that._checkActiveElement, that)).on(MOUSEENTER + NS, itemSelector, proxy(that._mouseenter, that)).on(MOUSELEAVE + NS, itemSelector, proxy(that._mouseleave, that)).on(MOUSEENTER + NS + ' ' + MOUSELEAVE + NS + ' ' + MOUSEDOWN + NS + ' ' + CLICK + NS, linkSelector, proxy(that._toggleHover, that));
|
|
if (options.openOnClick) {
|
|
that.clicked = false;
|
|
that._documentClickHandler = proxy(that._documentClick, that);
|
|
$(document).click(that._documentClickHandler);
|
|
}
|
|
element.attr('role', 'menubar');
|
|
if (element[0].id) {
|
|
that._ariaId = kendo.format('{0}_mn_active', element[0].id);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
ACTIVATE,
|
|
DEACTIVATE,
|
|
SELECT
|
|
],
|
|
options: {
|
|
name: 'Menu',
|
|
animation: {
|
|
open: { duration: 200 },
|
|
close: { duration: 100 }
|
|
},
|
|
orientation: 'horizontal',
|
|
direction: 'default',
|
|
openOnClick: false,
|
|
closeOnClick: true,
|
|
hoverDelay: 100,
|
|
popupCollision: undefined
|
|
},
|
|
_initData: function (options) {
|
|
var that = this;
|
|
if (options.dataSource) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.element.children() };
|
|
});
|
|
that.element.empty();
|
|
that.append(options.dataSource, that.element);
|
|
that.angular('compile', function () {
|
|
return { elements: that.element.children() };
|
|
});
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var animation = this.options.animation;
|
|
this._animations(options);
|
|
options.animation = extend(true, animation, options.animation);
|
|
if ('dataSource' in options) {
|
|
this._initData(options);
|
|
}
|
|
this._updateClasses();
|
|
Widget.fn.setOptions.call(this, options);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.element.off(NS);
|
|
if (that._documentClickHandler) {
|
|
$(document).unbind('click', that._documentClickHandler);
|
|
}
|
|
kendo.destroy(that.element);
|
|
},
|
|
enable: function (element, enable) {
|
|
this._toggleDisabled(element, enable !== false);
|
|
return this;
|
|
},
|
|
disable: function (element) {
|
|
this._toggleDisabled(element, false);
|
|
return this;
|
|
},
|
|
append: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find('> .k-menu-group, > .k-animation-container > .k-menu-group') : null);
|
|
each(inserted.items, function () {
|
|
inserted.group.append(this);
|
|
updateArrow(this);
|
|
});
|
|
updateArrow(referenceItem);
|
|
updateFirstLast(inserted.group.find('.k-first, .k-last').add(inserted.items));
|
|
return this;
|
|
},
|
|
insertBefore: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.parent());
|
|
each(inserted.items, function () {
|
|
referenceItem.before(this);
|
|
updateArrow(this);
|
|
updateFirstLast(this);
|
|
});
|
|
updateFirstLast(referenceItem);
|
|
return this;
|
|
},
|
|
insertAfter: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.parent());
|
|
each(inserted.items, function () {
|
|
referenceItem.after(this);
|
|
updateArrow(this);
|
|
updateFirstLast(this);
|
|
});
|
|
updateFirstLast(referenceItem);
|
|
return this;
|
|
},
|
|
_insert: function (item, referenceItem, parent) {
|
|
var that = this, items, groups;
|
|
if (!referenceItem || !referenceItem.length) {
|
|
parent = that.element;
|
|
}
|
|
var plain = $.isPlainObject(item), groupData = {
|
|
firstLevel: parent.hasClass(MENU),
|
|
horizontal: parent.hasClass(MENU + '-horizontal'),
|
|
expanded: true,
|
|
length: parent.children().length
|
|
};
|
|
if (referenceItem && !parent.length) {
|
|
parent = $(Menu.renderGroup({ group: groupData })).appendTo(referenceItem);
|
|
}
|
|
if (plain || $.isArray(item)) {
|
|
items = $($.map(plain ? [item] : item, function (value, idx) {
|
|
if (typeof value === 'string') {
|
|
return $(value).get();
|
|
} else {
|
|
return $(Menu.renderItem({
|
|
group: groupData,
|
|
item: extend(value, { index: idx })
|
|
})).get();
|
|
}
|
|
}));
|
|
} else {
|
|
if (typeof item == 'string' && item.charAt(0) != '<') {
|
|
items = that.element.find(item);
|
|
} else {
|
|
items = $(item);
|
|
}
|
|
groups = items.find('> ul').addClass('k-menu-group').attr('role', 'menu');
|
|
items = items.filter('li');
|
|
items.add(groups.find('> li')).each(function () {
|
|
updateItemClasses(this);
|
|
});
|
|
}
|
|
return {
|
|
items: items,
|
|
group: parent
|
|
};
|
|
},
|
|
remove: function (element) {
|
|
element = this.element.find(element);
|
|
var that = this, parent = element.parentsUntil(that.element, allItemsSelector), group = element.parent('ul:not(.k-menu)');
|
|
element.remove();
|
|
if (group && !group.children(allItemsSelector).length) {
|
|
var container = group.parent('.k-animation-container');
|
|
if (container.length) {
|
|
container.remove();
|
|
} else {
|
|
group.remove();
|
|
}
|
|
}
|
|
if (parent.length) {
|
|
parent = parent.eq(0);
|
|
updateArrow(parent);
|
|
updateFirstLast(parent);
|
|
}
|
|
return that;
|
|
},
|
|
open: function (element) {
|
|
var that = this, options = that.options, horizontal = options.orientation == 'horizontal', direction = options.direction, isRtl = kendo.support.isRtl(that.wrapper);
|
|
element = that.element.find(element);
|
|
if (/^(top|bottom|default)$/.test(direction)) {
|
|
if (isRtl) {
|
|
direction = horizontal ? (direction + ' left').replace('default', 'bottom') : 'left';
|
|
} else {
|
|
direction = horizontal ? (direction + ' right').replace('default', 'bottom') : 'right';
|
|
}
|
|
}
|
|
element.siblings().find('>.k-popup:visible,>.k-animation-container>.k-popup:visible').each(function () {
|
|
var popup = $(this).data('kendoPopup');
|
|
if (popup) {
|
|
popup.close();
|
|
}
|
|
});
|
|
element.each(function () {
|
|
var li = $(this);
|
|
clearTimeout(li.data(TIMER));
|
|
li.data(TIMER, setTimeout(function () {
|
|
var ul = li.find('.k-menu-group:first:hidden'), popup;
|
|
if (ul[0] && that._triggerEvent({
|
|
item: li[0],
|
|
type: OPEN
|
|
}) === false) {
|
|
if (!ul.find('.k-menu-group')[0] && ul.children('.k-item').length > 1) {
|
|
var windowHeight = $(window).height(), setScrolling = function () {
|
|
ul.css({
|
|
maxHeight: windowHeight - (ul.outerHeight() - ul.height()) - kendo.getShadows(ul).bottom,
|
|
overflow: 'auto'
|
|
});
|
|
};
|
|
if (kendo.support.browser.msie && kendo.support.browser.version <= 7) {
|
|
setTimeout(setScrolling, 0);
|
|
} else {
|
|
setScrolling();
|
|
}
|
|
} else {
|
|
ul.css({
|
|
maxHeight: '',
|
|
overflow: ''
|
|
});
|
|
}
|
|
li.data(ZINDEX, li.css(ZINDEX));
|
|
li.css(ZINDEX, that.nextItemZIndex++);
|
|
popup = ul.data(KENDOPOPUP);
|
|
var root = li.parent().hasClass(MENU), parentHorizontal = root && horizontal, directions = parseDirection(direction, root, isRtl), effects = options.animation.open.effects, openEffects = effects !== undefined ? effects : 'slideIn:' + getEffectDirection(direction, root);
|
|
if (!popup) {
|
|
popup = ul.kendoPopup({
|
|
activate: function () {
|
|
that._triggerEvent({
|
|
item: this.wrapper.parent(),
|
|
type: ACTIVATE
|
|
});
|
|
},
|
|
deactivate: function (e) {
|
|
e.sender.element.removeData('targetTransform').css({ opacity: '' });
|
|
that._triggerEvent({
|
|
item: this.wrapper.parent(),
|
|
type: DEACTIVATE
|
|
});
|
|
},
|
|
origin: directions.origin,
|
|
position: directions.position,
|
|
collision: options.popupCollision !== undefined ? options.popupCollision : parentHorizontal ? 'fit' : 'fit flip',
|
|
anchor: li,
|
|
appendTo: li,
|
|
animation: {
|
|
open: extend(true, { effects: openEffects }, options.animation.open),
|
|
close: options.animation.close
|
|
},
|
|
close: function (e) {
|
|
var li = e.sender.wrapper.parent();
|
|
if (!that._triggerEvent({
|
|
item: li[0],
|
|
type: CLOSE
|
|
})) {
|
|
li.css(ZINDEX, li.data(ZINDEX));
|
|
li.removeData(ZINDEX);
|
|
if (touch) {
|
|
li.removeClass(HOVERSTATE);
|
|
that._removeHoverItem();
|
|
}
|
|
} else {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}).data(KENDOPOPUP);
|
|
} else {
|
|
popup = ul.data(KENDOPOPUP);
|
|
popup.options.origin = directions.origin;
|
|
popup.options.position = directions.position;
|
|
popup.options.animation.open.effects = openEffects;
|
|
}
|
|
ul.removeAttr('aria-hidden');
|
|
popup.open();
|
|
}
|
|
}, that.options.hoverDelay));
|
|
});
|
|
return that;
|
|
},
|
|
close: function (items, dontClearClose) {
|
|
var that = this, element = that.element;
|
|
items = element.find(items);
|
|
if (!items.length) {
|
|
items = element.find('>.k-item');
|
|
}
|
|
items.each(function () {
|
|
var li = $(this);
|
|
if (!dontClearClose && that._isRootItem(li)) {
|
|
that.clicked = false;
|
|
}
|
|
clearTimeout(li.data(TIMER));
|
|
li.data(TIMER, setTimeout(function () {
|
|
var popup = li.find('.k-menu-group:not(.k-list-container):not(.k-calendar-container):first:visible').data(KENDOPOPUP);
|
|
if (popup) {
|
|
popup.close();
|
|
popup.element.attr('aria-hidden', true);
|
|
}
|
|
}, that.options.hoverDelay));
|
|
});
|
|
return that;
|
|
},
|
|
_toggleDisabled: function (items, enable) {
|
|
this.element.find(items).each(function () {
|
|
$(this).toggleClass(DEFAULTSTATE, enable).toggleClass(DISABLEDSTATE, !enable).attr('aria-disabled', !enable);
|
|
});
|
|
},
|
|
_toggleHover: function (e) {
|
|
var target = $(kendo.eventTarget(e) || e.target).closest(allItemsSelector), isEnter = e.type == MOUSEENTER || MOUSEDOWN.indexOf(e.type) !== -1;
|
|
if (!target.parents('li.' + DISABLEDSTATE).length) {
|
|
target.toggleClass(HOVERSTATE, isEnter || e.type == 'mousedown' || e.type == 'click');
|
|
}
|
|
this._removeHoverItem();
|
|
},
|
|
_preventClose: function () {
|
|
if (!this.options.closeOnClick) {
|
|
this._closurePrevented = true;
|
|
}
|
|
},
|
|
_checkActiveElement: function (e) {
|
|
var that = this, hoverItem = $(e ? e.currentTarget : this._hoverItem()), target = that._findRootParent(hoverItem)[0];
|
|
if (!this._closurePrevented) {
|
|
setTimeout(function () {
|
|
if (!document.hasFocus() || !contains(target, kendo._activeElement()) && e && !contains(target, e.currentTarget)) {
|
|
that.close(target);
|
|
}
|
|
}, 0);
|
|
}
|
|
this._closurePrevented = false;
|
|
},
|
|
_removeHoverItem: function () {
|
|
var oldHoverItem = this._hoverItem();
|
|
if (oldHoverItem && oldHoverItem.hasClass(FOCUSEDSTATE)) {
|
|
oldHoverItem.removeClass(FOCUSEDSTATE);
|
|
this._oldHoverItem = null;
|
|
}
|
|
},
|
|
_updateClasses: function () {
|
|
var element = this.element, nonContentGroupsSelector = '.k-menu-init div ul', items;
|
|
element.removeClass('k-menu-horizontal k-menu-vertical');
|
|
element.addClass('k-widget k-reset k-header k-menu-init ' + MENU).addClass(MENU + '-' + this.options.orientation);
|
|
element.find('li > ul').filter(function () {
|
|
return !kendo.support.matchesSelector.call(this, nonContentGroupsSelector);
|
|
}).addClass('k-group k-menu-group').attr('role', 'menu').attr('aria-hidden', element.is(':visible')).end().find('li > div').addClass('k-content').attr('tabindex', '-1');
|
|
items = element.find('> li,.k-menu-group > li');
|
|
element.removeClass('k-menu-init');
|
|
items.each(function () {
|
|
updateItemClasses(this);
|
|
});
|
|
},
|
|
_mouseenter: function (e) {
|
|
var that = this, element = $(e.currentTarget), hasChildren = element.children('.k-animation-container').length || element.children(groupSelector).length;
|
|
if (e.delegateTarget != element.parents(menuSelector)[0]) {
|
|
return;
|
|
}
|
|
if ((!that.options.openOnClick || that.clicked) && !touch && !((pointers || msPointers) && e.originalEvent.pointerType in touchPointerTypes && that._isRootItem(element.closest(allItemsSelector)))) {
|
|
if (!contains(e.currentTarget, e.relatedTarget) && hasChildren) {
|
|
that.open(element);
|
|
}
|
|
}
|
|
if (that.options.openOnClick && that.clicked || mobile) {
|
|
element.siblings().each(proxy(function (_, sibling) {
|
|
that.close(sibling, true);
|
|
}, that));
|
|
}
|
|
},
|
|
_mouseleave: function (e) {
|
|
var that = this, element = $(e.currentTarget), hasChildren = element.children('.k-animation-container').length || element.children(groupSelector).length;
|
|
if (element.parentsUntil('.k-animation-container', '.k-list-container,.k-calendar-container')[0]) {
|
|
e.stopImmediatePropagation();
|
|
return;
|
|
}
|
|
if (!that.options.openOnClick && !touch && !((pointers || msPointers) && e.originalEvent.pointerType in touchPointerTypes) && !contains(e.currentTarget, e.relatedTarget || e.target) && hasChildren && !contains(e.currentTarget, kendo._activeElement())) {
|
|
that.close(element);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var that = this, openHandle, options = that.options, target = $(kendo.eventTarget(e)), nodeName = target[0] ? target[0].nodeName.toUpperCase() : '', formNode = nodeName == 'INPUT' || nodeName == 'SELECT' || nodeName == 'BUTTON' || nodeName == 'LABEL', link = target.closest('.' + LINK), element = target.closest(allItemsSelector), href = link.attr('href'), childGroup, childGroupVisible, targetHref = target.attr('href'), sampleHref = $('<a href=\'#\' />').attr('href'), isLink = !!href && href !== sampleHref, isLocalLink = isLink && !!href.match(/^#/), isTargetLink = !!targetHref && targetHref !== sampleHref, shouldCloseTheRootItem = options.openOnClick && childGroupVisible && that._isRootItem(element);
|
|
if (target.closest(templateSelector, element[0]).length) {
|
|
return;
|
|
}
|
|
if (element.hasClass(DISABLEDSTATE)) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (!e.handled && that._triggerEvent({
|
|
item: element[0],
|
|
type: SELECT
|
|
}) && !formNode) {
|
|
e.preventDefault();
|
|
}
|
|
e.handled = true;
|
|
childGroup = element.children(popupSelector);
|
|
childGroupVisible = childGroup.is(':visible');
|
|
if (options.closeOnClick && (!isLink || isLocalLink) && (!childGroup.length || shouldCloseTheRootItem)) {
|
|
element.removeClass(HOVERSTATE).css('height');
|
|
that._oldHoverItem = that._findRootParent(element);
|
|
that.close(link.parentsUntil(that.element, allItemsSelector));
|
|
that.clicked = false;
|
|
if ('MSPointerUp'.indexOf(e.type) != -1) {
|
|
e.preventDefault();
|
|
}
|
|
return;
|
|
}
|
|
if (isLink && e.enterKey) {
|
|
link[0].click();
|
|
}
|
|
if ((!that._isRootItem(element) || !options.openOnClick) && !kendo.support.touch && !((pointers || msPointers) && that._isRootItem(element.closest(allItemsSelector)))) {
|
|
return;
|
|
}
|
|
if (!isLink && !formNode && !isTargetLink) {
|
|
e.preventDefault();
|
|
}
|
|
that.clicked = true;
|
|
openHandle = childGroup.is(':visible') ? CLOSE : OPEN;
|
|
if (!options.closeOnClick && openHandle == CLOSE) {
|
|
return;
|
|
}
|
|
that[openHandle](element);
|
|
},
|
|
_documentClick: function (e) {
|
|
if (contains(this.element[0], e.target)) {
|
|
return;
|
|
}
|
|
this.clicked = false;
|
|
},
|
|
_focus: function (e) {
|
|
var that = this, target = e.target, hoverItem = that._hoverItem(), active = activeElement();
|
|
if (target != that.wrapper[0] && !$(target).is(':kendoFocusable')) {
|
|
e.stopPropagation();
|
|
$(target).closest('.k-content').closest('.k-menu-group').closest('.k-item').addClass(FOCUSEDSTATE);
|
|
that.wrapper.focus();
|
|
return;
|
|
}
|
|
if (active === e.currentTarget) {
|
|
if (hoverItem.length) {
|
|
that._moveHover([], hoverItem);
|
|
} else if (!that._oldHoverItem) {
|
|
that._moveHover([], that.wrapper.children().first());
|
|
}
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, hoverItem = that._oldHoverItem, target, belongsToVertical, hasChildren, isRtl = kendo.support.isRtl(that.wrapper);
|
|
if (e.target != e.currentTarget && key != keys.ESC) {
|
|
return;
|
|
}
|
|
if (!hoverItem) {
|
|
hoverItem = that._oldHoverItem = that._hoverItem();
|
|
}
|
|
belongsToVertical = that._itemBelongsToVertival(hoverItem);
|
|
hasChildren = that._itemHasChildren(hoverItem);
|
|
if (key == keys.RIGHT) {
|
|
target = that[isRtl ? '_itemLeft' : '_itemRight'](hoverItem, belongsToVertical, hasChildren);
|
|
} else if (key == keys.LEFT) {
|
|
target = that[isRtl ? '_itemRight' : '_itemLeft'](hoverItem, belongsToVertical, hasChildren);
|
|
} else if (key == keys.DOWN) {
|
|
target = that._itemDown(hoverItem, belongsToVertical, hasChildren);
|
|
} else if (key == keys.UP) {
|
|
target = that._itemUp(hoverItem, belongsToVertical, hasChildren);
|
|
} else if (key == keys.ESC) {
|
|
target = that._itemEsc(hoverItem, belongsToVertical);
|
|
} else if (key == keys.ENTER || key == keys.SPACEBAR) {
|
|
target = hoverItem.children('.k-link');
|
|
if (target.length > 0) {
|
|
that._click({
|
|
target: target[0],
|
|
preventDefault: function () {
|
|
},
|
|
enterKey: true
|
|
});
|
|
that._moveHover(hoverItem, that._findRootParent(hoverItem));
|
|
}
|
|
} else if (key == keys.TAB) {
|
|
target = that._findRootParent(hoverItem);
|
|
that._moveHover(hoverItem, target);
|
|
that._checkActiveElement();
|
|
return;
|
|
}
|
|
if (target && target[0]) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
_hoverItem: function () {
|
|
return this.wrapper.find('.k-item.k-state-hover,.k-item.k-state-focused').filter(':visible');
|
|
},
|
|
_itemBelongsToVertival: function (item) {
|
|
var menuIsVertical = this.wrapper.hasClass('k-menu-vertical');
|
|
if (!item.length) {
|
|
return menuIsVertical;
|
|
}
|
|
return item.parent().hasClass('k-menu-group') || menuIsVertical;
|
|
},
|
|
_itemHasChildren: function (item) {
|
|
if (!item.length) {
|
|
return false;
|
|
}
|
|
return item.children('ul.k-menu-group, div.k-animation-container').length > 0;
|
|
},
|
|
_moveHover: function (item, nextItem) {
|
|
var that = this, id = that._ariaId;
|
|
if (item.length && nextItem.length) {
|
|
item.removeClass(FOCUSEDSTATE);
|
|
}
|
|
if (nextItem.length) {
|
|
if (nextItem[0].id) {
|
|
id = nextItem[0].id;
|
|
}
|
|
nextItem.addClass(FOCUSEDSTATE);
|
|
that._oldHoverItem = nextItem;
|
|
if (id) {
|
|
that.element.removeAttr('aria-activedescendant');
|
|
$('#' + id).removeAttr('id');
|
|
nextItem.attr('id', id);
|
|
that.element.attr('aria-activedescendant', id);
|
|
}
|
|
}
|
|
},
|
|
_findRootParent: function (item) {
|
|
if (this._isRootItem(item)) {
|
|
return item;
|
|
} else {
|
|
return item.parentsUntil(menuSelector, 'li.k-item').last();
|
|
}
|
|
},
|
|
_isRootItem: function (item) {
|
|
return item.parent().hasClass(MENU);
|
|
},
|
|
_itemRight: function (item, belongsToVertical, hasChildren) {
|
|
var that = this, nextItem, parentItem;
|
|
if (item.hasClass(DISABLEDSTATE)) {
|
|
return;
|
|
}
|
|
if (!belongsToVertical) {
|
|
nextItem = item.nextAll(nextSelector);
|
|
if (!nextItem.length) {
|
|
nextItem = item.prevAll(lastSelector);
|
|
}
|
|
} else if (hasChildren) {
|
|
that.open(item);
|
|
nextItem = item.find('.k-menu-group').children().first();
|
|
} else if (that.options.orientation == 'horizontal') {
|
|
parentItem = that._findRootParent(item);
|
|
that.close(parentItem);
|
|
nextItem = parentItem.nextAll(nextSelector);
|
|
}
|
|
if (nextItem && !nextItem.length) {
|
|
nextItem = that.wrapper.children('.k-item').first();
|
|
} else if (!nextItem) {
|
|
nextItem = [];
|
|
}
|
|
that._moveHover(item, nextItem);
|
|
return nextItem;
|
|
},
|
|
_itemLeft: function (item, belongsToVertical) {
|
|
var that = this, nextItem;
|
|
if (!belongsToVertical) {
|
|
nextItem = item.prevAll(nextSelector);
|
|
if (!nextItem.length) {
|
|
nextItem = item.nextAll(lastSelector);
|
|
}
|
|
} else {
|
|
nextItem = item.parent().closest('.k-item');
|
|
that.close(nextItem);
|
|
if (that._isRootItem(nextItem) && that.options.orientation == 'horizontal') {
|
|
nextItem = nextItem.prevAll(nextSelector);
|
|
}
|
|
}
|
|
if (!nextItem.length) {
|
|
nextItem = that.wrapper.children('.k-item').last();
|
|
}
|
|
that._moveHover(item, nextItem);
|
|
return nextItem;
|
|
},
|
|
_itemDown: function (item, belongsToVertical, hasChildren) {
|
|
var that = this, nextItem;
|
|
if (!belongsToVertical) {
|
|
if (!hasChildren || item.hasClass(DISABLEDSTATE)) {
|
|
return;
|
|
} else {
|
|
that.open(item);
|
|
nextItem = item.find('.k-menu-group').children().first();
|
|
}
|
|
} else {
|
|
nextItem = item.nextAll(nextSelector);
|
|
}
|
|
if (!nextItem.length && item.length) {
|
|
nextItem = item.parent().children().first();
|
|
} else if (!item.length) {
|
|
nextItem = that.wrapper.children('.k-item').first();
|
|
}
|
|
that._moveHover(item, nextItem);
|
|
return nextItem;
|
|
},
|
|
_itemUp: function (item, belongsToVertical) {
|
|
var that = this, nextItem;
|
|
if (!belongsToVertical) {
|
|
return;
|
|
} else {
|
|
nextItem = item.prevAll(nextSelector);
|
|
}
|
|
if (!nextItem.length && item.length) {
|
|
nextItem = item.parent().children().last();
|
|
} else if (!item.length) {
|
|
nextItem = that.wrapper.children('.k-item').last();
|
|
}
|
|
that._moveHover(item, nextItem);
|
|
return nextItem;
|
|
},
|
|
_itemEsc: function (item, belongsToVertical) {
|
|
var that = this, nextItem;
|
|
if (!belongsToVertical) {
|
|
return item;
|
|
} else {
|
|
nextItem = item.parent().closest('.k-item');
|
|
that.close(nextItem);
|
|
that._moveHover(item, nextItem);
|
|
}
|
|
return nextItem;
|
|
},
|
|
_triggerEvent: function (e) {
|
|
var that = this;
|
|
return that.trigger(e.type, {
|
|
type: e.type,
|
|
item: e.item
|
|
});
|
|
},
|
|
_focusHandler: function (e) {
|
|
var that = this, item = $(kendo.eventTarget(e)).closest(allItemsSelector);
|
|
setTimeout(function () {
|
|
that._moveHover([], item);
|
|
if (item.children('.k-content')[0]) {
|
|
item.parent().closest('.k-item').removeClass(FOCUSEDSTATE);
|
|
}
|
|
}, 200);
|
|
},
|
|
_animations: function (options) {
|
|
if (options && 'animation' in options && !options.animation) {
|
|
options.animation = {
|
|
open: { effects: {} },
|
|
close: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
});
|
|
extend(Menu, {
|
|
renderItem: function (options) {
|
|
options = extend({
|
|
menu: {},
|
|
group: {}
|
|
}, options);
|
|
var empty = templates.empty, item = options.item;
|
|
return templates.item(extend(options, {
|
|
image: item.imageUrl ? templates.image : empty,
|
|
sprite: item.spriteCssClass ? templates.sprite : empty,
|
|
itemWrapper: templates.itemWrapper,
|
|
renderContent: Menu.renderContent,
|
|
arrow: item.items || item.content ? templates.arrow : empty,
|
|
subGroup: Menu.renderGroup
|
|
}, rendering));
|
|
},
|
|
renderGroup: function (options) {
|
|
return templates.group(extend({
|
|
renderItems: function (options) {
|
|
var html = '', i = 0, items = options.items, len = items ? items.length : 0, group = extend({ length: len }, options.group);
|
|
for (; i < len; i++) {
|
|
html += Menu.renderItem(extend(options, {
|
|
group: group,
|
|
item: extend({ index: i }, items[i])
|
|
}));
|
|
}
|
|
return html;
|
|
}
|
|
}, options, rendering));
|
|
},
|
|
renderContent: function (options) {
|
|
return templates.content(extend(options, rendering));
|
|
}
|
|
});
|
|
var ContextMenu = Menu.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Menu.fn.init.call(that, element, options);
|
|
that.target = $(that.options.target);
|
|
that._popup();
|
|
that._wire();
|
|
},
|
|
options: {
|
|
name: 'ContextMenu',
|
|
filter: null,
|
|
showOn: 'contextmenu',
|
|
orientation: 'vertical',
|
|
alignToAnchor: false,
|
|
target: 'body'
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
ACTIVATE,
|
|
DEACTIVATE,
|
|
SELECT
|
|
],
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
Menu.fn.setOptions.call(that, options);
|
|
that.target.off(that.showOn + NS, that._showProxy);
|
|
if (that.userEvents) {
|
|
that.userEvents.destroy();
|
|
}
|
|
that.target = $(that.options.target);
|
|
if (options.orientation && that.popup.wrapper[0]) {
|
|
that.popup.element.unwrap();
|
|
}
|
|
that._wire();
|
|
Menu.fn.setOptions.call(this, options);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.target.off(that.options.showOn + NS);
|
|
DOCUMENT_ELEMENT.off(kendo.support.mousedown + NS, that._closeProxy);
|
|
if (that.userEvents) {
|
|
that.userEvents.destroy();
|
|
}
|
|
Menu.fn.destroy.call(that);
|
|
},
|
|
open: function (x, y) {
|
|
var that = this;
|
|
x = $(x)[0];
|
|
if (contains(that.element[0], $(x)[0])) {
|
|
Menu.fn.open.call(that, x);
|
|
} else {
|
|
if (that._triggerEvent({
|
|
item: that.element,
|
|
type: OPEN
|
|
}) === false) {
|
|
if (that.popup.visible() && that.options.filter) {
|
|
that.popup.close(true);
|
|
that.popup.element.kendoStop(true);
|
|
}
|
|
if (y !== undefined) {
|
|
that.popup.wrapper.hide();
|
|
that.popup.open(x, y);
|
|
} else {
|
|
that.popup.options.anchor = (x ? x : that.popup.anchor) || that.target;
|
|
that.popup.element.kendoStop(true);
|
|
that.popup.open();
|
|
}
|
|
DOCUMENT_ELEMENT.off(that.popup.downEvent, that.popup._mousedownProxy);
|
|
DOCUMENT_ELEMENT.on(kendo.support.mousedown + NS, that._closeProxy);
|
|
}
|
|
}
|
|
return that;
|
|
},
|
|
close: function () {
|
|
var that = this;
|
|
if (contains(that.element[0], $(arguments[0])[0])) {
|
|
Menu.fn.close.call(that, arguments[0]);
|
|
} else {
|
|
if (that.popup.visible()) {
|
|
if (that._triggerEvent({
|
|
item: that.element,
|
|
type: CLOSE
|
|
}) === false) {
|
|
that.popup.close();
|
|
DOCUMENT_ELEMENT.off(kendo.support.mousedown + NS, that._closeProxy);
|
|
that.unbind(SELECT, that._closeTimeoutProxy);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_showHandler: function (e) {
|
|
var ev = e, offset, that = this, options = that.options;
|
|
if (e.event) {
|
|
ev = e.event;
|
|
ev.pageX = e.x.location;
|
|
ev.pageY = e.y.location;
|
|
}
|
|
if (contains(that.element[0], e.relatedTarget || e.target)) {
|
|
return;
|
|
}
|
|
that._eventOrigin = ev;
|
|
ev.preventDefault();
|
|
ev.stopImmediatePropagation();
|
|
that.element.find('.' + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
|
|
if (options.filter && kendo.support.matchesSelector.call(ev.currentTarget, options.filter) || !options.filter) {
|
|
if (options.alignToAnchor) {
|
|
that.popup.options.anchor = ev.currentTarget;
|
|
that.open(ev.currentTarget);
|
|
} else {
|
|
that.popup.options.anchor = ev.currentTarget;
|
|
if (that._targetChild) {
|
|
offset = that.target.offset();
|
|
that.open(ev.pageX - offset.left, ev.pageY - offset.top);
|
|
} else {
|
|
that.open(ev.pageX, ev.pageY);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_closeHandler: function (e) {
|
|
var that = this, target = $(e.relatedTarget || e.target), sameTarget = target.closest(that.target.selector)[0] == that.target[0], children = target.closest(itemSelector).children(popupSelector), containment = contains(that.element[0], target[0]);
|
|
that._eventOrigin = e;
|
|
var normalClick = e.which !== 3;
|
|
if (that.popup.visible() && (normalClick && sameTarget || !sameTarget) && (that.options.closeOnClick && !children[0] && containment || !containment)) {
|
|
if (containment) {
|
|
this.unbind(SELECT, this._closeTimeoutProxy);
|
|
that.bind(SELECT, that._closeTimeoutProxy);
|
|
} else {
|
|
that.close();
|
|
}
|
|
}
|
|
},
|
|
_wire: function () {
|
|
var that = this, options = that.options, target = that.target;
|
|
that._showProxy = proxy(that._showHandler, that);
|
|
that._closeProxy = proxy(that._closeHandler, that);
|
|
that._closeTimeoutProxy = proxy(that.close, that);
|
|
if (target[0]) {
|
|
if (kendo.support.mobileOS && options.showOn == 'contextmenu') {
|
|
that.userEvents = new kendo.UserEvents(target, {
|
|
filter: options.filter,
|
|
allowSelection: false
|
|
});
|
|
target.on(options.showOn + NS, false);
|
|
that.userEvents.bind('hold', that._showProxy);
|
|
} else {
|
|
if (options.filter) {
|
|
target.on(options.showOn + NS, options.filter, that._showProxy);
|
|
} else {
|
|
target.on(options.showOn + NS, that._showProxy);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_triggerEvent: function (e) {
|
|
var that = this, anchor = $(that.popup.options.anchor)[0], origin = that._eventOrigin;
|
|
that._eventOrigin = undefined;
|
|
return that.trigger(e.type, extend({
|
|
type: e.type,
|
|
item: e.item || this.element[0],
|
|
target: anchor
|
|
}, origin ? { event: origin } : {}));
|
|
},
|
|
_popup: function () {
|
|
var that = this;
|
|
that._triggerProxy = proxy(that._triggerEvent, that);
|
|
that.popup = that.element.addClass('k-context-menu').kendoPopup({
|
|
anchor: that.target || 'body',
|
|
copyAnchorStyles: that.options.copyAnchorStyles,
|
|
collision: that.options.popupCollision || 'fit',
|
|
animation: that.options.animation,
|
|
activate: that._triggerProxy,
|
|
deactivate: that._triggerProxy
|
|
}).data('kendoPopup');
|
|
that._targetChild = contains(that.target[0], that.popup.element[0]);
|
|
}
|
|
});
|
|
ui.plugin(Menu);
|
|
ui.plugin(ContextMenu);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.columnmenu', [
|
|
'kendo.popup',
|
|
'kendo.filtermenu',
|
|
'kendo.menu'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'columnmenu',
|
|
name: 'Column Menu',
|
|
category: 'framework',
|
|
depends: [
|
|
'popup',
|
|
'filtermenu',
|
|
'menu'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, proxy = $.proxy, extend = $.extend, grep = $.grep, map = $.map, inArray = $.inArray, ACTIVE = 'k-state-selected', ASC = 'asc', DESC = 'desc', CHANGE = 'change', INIT = 'init', SELECT = 'select', POPUP = 'kendoPopup', FILTERMENU = 'kendoFilterMenu', MENU = 'kendoMenu', NS = '.kendoColumnMenu', Widget = ui.Widget;
|
|
function trim(text) {
|
|
return $.trim(text).replace(/ /gi, '');
|
|
}
|
|
function toHash(arr, key) {
|
|
var result = {};
|
|
var idx, len, current;
|
|
for (idx = 0, len = arr.length; idx < len; idx++) {
|
|
current = arr[idx];
|
|
result[current[key]] = current;
|
|
}
|
|
return result;
|
|
}
|
|
function leafColumns(columns) {
|
|
var result = [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (!columns[idx].columns) {
|
|
result.push(columns[idx]);
|
|
continue;
|
|
}
|
|
result = result.concat(leafColumns(columns[idx].columns));
|
|
}
|
|
return result;
|
|
}
|
|
var ColumnMenu = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, link;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
that.owner = options.owner;
|
|
that.dataSource = options.dataSource;
|
|
that.field = element.attr(kendo.attr('field'));
|
|
that.title = element.attr(kendo.attr('title'));
|
|
link = element.find('.k-header-column-menu');
|
|
if (!link[0]) {
|
|
link = element.addClass('k-with-icon').prepend('<a class="k-header-column-menu" href="#"><span class="k-icon k-i-arrowhead-s">' + options.messages.settings + '</span></a>').find('.k-header-column-menu');
|
|
}
|
|
that.link = link.attr('tabindex', -1).on('click' + NS, proxy(that._click, that));
|
|
that.wrapper = $('<div class="k-column-menu"/>');
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
},
|
|
_init: function () {
|
|
var that = this;
|
|
that.pane = that.options.pane;
|
|
if (that.pane) {
|
|
that._isMobile = true;
|
|
}
|
|
if (that._isMobile) {
|
|
that._createMobileMenu();
|
|
} else {
|
|
that._createMenu();
|
|
}
|
|
that.owner._muteAngularRebind(function () {
|
|
that._angularItems('compile');
|
|
});
|
|
that._sort();
|
|
that._columns();
|
|
that._filter();
|
|
that._lockColumns();
|
|
that.trigger(INIT, {
|
|
field: that.field,
|
|
container: that.wrapper
|
|
});
|
|
},
|
|
events: [INIT],
|
|
options: {
|
|
name: 'ColumnMenu',
|
|
messages: {
|
|
sortAscending: 'Sort Ascending',
|
|
sortDescending: 'Sort Descending',
|
|
filter: 'Filter',
|
|
columns: 'Columns',
|
|
done: 'Done',
|
|
settings: 'Column Settings',
|
|
lock: 'Lock',
|
|
unlock: 'Unlock'
|
|
},
|
|
filter: '',
|
|
columns: true,
|
|
sortable: true,
|
|
filterable: true,
|
|
animations: { left: 'slide' }
|
|
},
|
|
_createMenu: function () {
|
|
var that = this, options = that.options;
|
|
that.wrapper.html(kendo.template(template)({
|
|
ns: kendo.ns,
|
|
messages: options.messages,
|
|
sortable: options.sortable,
|
|
filterable: options.filterable,
|
|
columns: that._ownerColumns(),
|
|
showColumns: options.columns,
|
|
lockedColumns: options.lockedColumns
|
|
}));
|
|
that.popup = that.wrapper[POPUP]({
|
|
anchor: that.link,
|
|
open: proxy(that._open, that),
|
|
activate: proxy(that._activate, that),
|
|
close: function () {
|
|
if (that.options.closeCallback) {
|
|
that.options.closeCallback(that.element);
|
|
}
|
|
}
|
|
}).data(POPUP);
|
|
that.menu = that.wrapper.children()[MENU]({
|
|
orientation: 'vertical',
|
|
closeOnClick: false
|
|
}).data(MENU);
|
|
},
|
|
_createMobileMenu: function () {
|
|
var that = this, options = that.options;
|
|
var html = kendo.template(mobileTemplate)({
|
|
ns: kendo.ns,
|
|
field: that.field,
|
|
title: that.title || that.field,
|
|
messages: options.messages,
|
|
sortable: options.sortable,
|
|
filterable: options.filterable,
|
|
columns: that._ownerColumns(),
|
|
showColumns: options.columns,
|
|
lockedColumns: options.lockedColumns
|
|
});
|
|
that.view = that.pane.append(html);
|
|
that.wrapper = that.view.element.find('.k-column-menu');
|
|
that.menu = new MobileMenu(that.wrapper.children(), { pane: that.pane });
|
|
that.view.element.on('click', '.k-done', function (e) {
|
|
that.close();
|
|
e.preventDefault();
|
|
});
|
|
if (that.options.lockedColumns) {
|
|
that.view.bind('show', function () {
|
|
that._updateLockedColumns();
|
|
});
|
|
}
|
|
},
|
|
_angularItems: function (action) {
|
|
var that = this;
|
|
that.angular(action, function () {
|
|
var items = that.wrapper.find('.k-columns-item input[' + kendo.attr('field') + ']').map(function () {
|
|
return $(this).closest('li');
|
|
});
|
|
var data = map(that._ownerColumns(), function (col) {
|
|
return { column: col._originalObject };
|
|
});
|
|
return {
|
|
elements: items,
|
|
data: data
|
|
};
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that._angularItems('cleanup');
|
|
Widget.fn.destroy.call(that);
|
|
if (that.filterMenu) {
|
|
that.filterMenu.destroy();
|
|
}
|
|
if (that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
}
|
|
if (that.options.columns && that.owner) {
|
|
if (that._updateColumnsMenuHandler) {
|
|
that.owner.unbind('columnShow', that._updateColumnsMenuHandler);
|
|
that.owner.unbind('columnHide', that._updateColumnsMenuHandler);
|
|
}
|
|
if (that._updateColumnsLockedStateHandler) {
|
|
that.owner.unbind('columnLock', that._updateColumnsLockedStateHandler);
|
|
that.owner.unbind('columnUnlock', that._updateColumnsLockedStateHandler);
|
|
}
|
|
}
|
|
if (that.menu) {
|
|
that.menu.element.off(NS);
|
|
that.menu.destroy();
|
|
}
|
|
that.wrapper.off(NS);
|
|
if (that.popup) {
|
|
that.popup.destroy();
|
|
}
|
|
if (that.view) {
|
|
that.view.purge();
|
|
}
|
|
that.link.off(NS);
|
|
that.owner = null;
|
|
that.wrapper = null;
|
|
that.element = null;
|
|
},
|
|
close: function () {
|
|
this.menu.close();
|
|
if (this.popup) {
|
|
this.popup.close();
|
|
this.popup.element.off('keydown' + NS);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var options = this.options;
|
|
if (options.filter && this.element.is(!options.filter)) {
|
|
return;
|
|
}
|
|
if (!this.popup && !this.pane) {
|
|
this._init();
|
|
}
|
|
if (this._isMobile) {
|
|
this.pane.navigate(this.view, this.options.animations.left);
|
|
} else {
|
|
this.popup.toggle();
|
|
}
|
|
},
|
|
_open: function () {
|
|
var that = this;
|
|
$('.k-column-menu').not(that.wrapper).each(function () {
|
|
$(this).data(POPUP).close();
|
|
});
|
|
that.popup.element.on('keydown' + NS, function (e) {
|
|
if (e.keyCode == kendo.keys.ESC) {
|
|
that.close();
|
|
}
|
|
});
|
|
if (that.options.lockedColumns) {
|
|
that._updateLockedColumns();
|
|
}
|
|
},
|
|
_activate: function () {
|
|
this.menu.element.focus();
|
|
},
|
|
_ownerColumns: function () {
|
|
var columns = leafColumns(this.owner.columns), menuColumns = grep(columns, function (col) {
|
|
var result = true, title = trim(col.title || '');
|
|
if (col.menu === false || !col.field && !title.length) {
|
|
result = false;
|
|
}
|
|
return result;
|
|
});
|
|
return map(menuColumns, function (col) {
|
|
return {
|
|
originalField: col.field,
|
|
field: col.field || col.title,
|
|
title: col.title || col.field,
|
|
hidden: col.hidden,
|
|
index: inArray(col, columns),
|
|
locked: !!col.locked,
|
|
_originalObject: col
|
|
};
|
|
});
|
|
},
|
|
_sort: function () {
|
|
var that = this;
|
|
if (that.options.sortable) {
|
|
that.refresh();
|
|
that.menu.bind(SELECT, function (e) {
|
|
var item = $(e.item), dir;
|
|
if (item.hasClass('k-sort-asc')) {
|
|
dir = ASC;
|
|
} else if (item.hasClass('k-sort-desc')) {
|
|
dir = DESC;
|
|
}
|
|
if (!dir) {
|
|
return;
|
|
}
|
|
item.parent().find('.k-sort-' + (dir == ASC ? DESC : ASC)).removeClass(ACTIVE);
|
|
that._sortDataSource(item, dir);
|
|
that.close();
|
|
});
|
|
}
|
|
},
|
|
_sortDataSource: function (item, dir) {
|
|
var that = this, sortable = that.options.sortable, compare = sortable.compare === null ? undefined : sortable.compare, dataSource = that.dataSource, idx, length, sort = dataSource.sort() || [];
|
|
if (item.hasClass(ACTIVE) && sortable && sortable.allowUnsort !== false) {
|
|
item.removeClass(ACTIVE);
|
|
dir = undefined;
|
|
} else {
|
|
item.addClass(ACTIVE);
|
|
}
|
|
if (sortable.mode === 'multiple') {
|
|
for (idx = 0, length = sort.length; idx < length; idx++) {
|
|
if (sort[idx].field === that.field) {
|
|
sort.splice(idx, 1);
|
|
break;
|
|
}
|
|
}
|
|
sort.push({
|
|
field: that.field,
|
|
dir: dir,
|
|
compare: compare
|
|
});
|
|
} else {
|
|
sort = [{
|
|
field: that.field,
|
|
dir: dir,
|
|
compare: compare
|
|
}];
|
|
}
|
|
dataSource.sort(sort);
|
|
},
|
|
_columns: function () {
|
|
var that = this;
|
|
if (that.options.columns) {
|
|
that._updateColumnsMenu();
|
|
that._updateColumnsMenuHandler = proxy(that._updateColumnsMenu, that);
|
|
that.owner.bind([
|
|
'columnHide',
|
|
'columnShow'
|
|
], that._updateColumnsMenuHandler);
|
|
that._updateColumnsLockedStateHandler = proxy(that._updateColumnsLockedState, that);
|
|
that.owner.bind([
|
|
'columnUnlock',
|
|
'columnLock'
|
|
], that._updateColumnsLockedStateHandler);
|
|
that.menu.bind(SELECT, function (e) {
|
|
var item = $(e.item), input, column, columns = leafColumns(that.owner.columns), field;
|
|
if (that._isMobile) {
|
|
e.preventDefault();
|
|
}
|
|
if (!item.parent().closest('li.k-columns-item')[0]) {
|
|
return;
|
|
}
|
|
input = item.find(':checkbox');
|
|
if (input.attr('disabled')) {
|
|
return;
|
|
}
|
|
field = input.attr(kendo.attr('field'));
|
|
column = grep(columns, function (column) {
|
|
return column.field == field || column.title == field;
|
|
})[0];
|
|
if (column.hidden === true) {
|
|
that.owner.showColumn(column);
|
|
} else {
|
|
that.owner.hideColumn(column);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_updateColumnsMenu: function () {
|
|
var idx, length, current, checked, locked;
|
|
var fieldAttr = kendo.attr('field'), lockedAttr = kendo.attr('locked'), visible = grep(this._ownerColumns(), function (field) {
|
|
return !field.hidden;
|
|
}), visibleDataFields = grep(visible, function (field) {
|
|
return field.originalField;
|
|
}), lockedCount = grep(visibleDataFields, function (col) {
|
|
return col.locked === true;
|
|
}).length, nonLockedCount = grep(visibleDataFields, function (col) {
|
|
return col.locked !== true;
|
|
}).length;
|
|
visible = map(visible, function (col) {
|
|
return col.field;
|
|
});
|
|
var checkboxes = this.wrapper.find('.k-columns-item input[' + fieldAttr + ']').prop('disabled', false).prop('checked', false);
|
|
for (idx = 0, length = checkboxes.length; idx < length; idx++) {
|
|
current = checkboxes.eq(idx);
|
|
locked = current.attr(lockedAttr) === 'true';
|
|
checked = false;
|
|
if (inArray(current.attr(fieldAttr), visible) > -1) {
|
|
checked = true;
|
|
current.prop('checked', checked);
|
|
}
|
|
if (checked) {
|
|
if (lockedCount == 1 && locked) {
|
|
current.prop('disabled', true);
|
|
}
|
|
if (nonLockedCount == 1 && !locked) {
|
|
current.prop('disabled', true);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_updateColumnsLockedState: function () {
|
|
var idx, length, current, column;
|
|
var fieldAttr = kendo.attr('field');
|
|
var lockedAttr = kendo.attr('locked');
|
|
var columns = toHash(this._ownerColumns(), 'field');
|
|
var checkboxes = this.wrapper.find('.k-columns-item input[type=checkbox]');
|
|
for (idx = 0, length = checkboxes.length; idx < length; idx++) {
|
|
current = checkboxes.eq(idx);
|
|
column = columns[current.attr(fieldAttr)];
|
|
if (column) {
|
|
current.attr(lockedAttr, column.locked);
|
|
}
|
|
}
|
|
this._updateColumnsMenu();
|
|
},
|
|
_filter: function () {
|
|
var that = this, widget = FILTERMENU, options = that.options;
|
|
if (options.filterable !== false) {
|
|
if (options.filterable.multi) {
|
|
widget = 'kendoFilterMultiCheck';
|
|
if (options.filterable.dataSource) {
|
|
options.filterable.checkSource = options.filterable.dataSource;
|
|
delete options.filterable.dataSource;
|
|
}
|
|
}
|
|
that.filterMenu = that.wrapper.find('.k-filterable')[widget](extend(true, {}, {
|
|
appendToElement: true,
|
|
dataSource: options.dataSource,
|
|
values: options.values,
|
|
field: that.field,
|
|
title: that.title
|
|
}, options.filterable)).data(widget);
|
|
if (that._isMobile) {
|
|
that.menu.bind(SELECT, function (e) {
|
|
var item = $(e.item);
|
|
if (item.hasClass('k-filter-item')) {
|
|
that.pane.navigate(that.filterMenu.view, that.options.animations.left);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_lockColumns: function () {
|
|
var that = this;
|
|
that.menu.bind(SELECT, function (e) {
|
|
var item = $(e.item);
|
|
if (item.hasClass('k-lock')) {
|
|
that.owner.lockColumn(that.field);
|
|
that.close();
|
|
} else if (item.hasClass('k-unlock')) {
|
|
that.owner.unlockColumn(that.field);
|
|
that.close();
|
|
}
|
|
});
|
|
},
|
|
_updateLockedColumns: function () {
|
|
var field = this.field;
|
|
var columns = this.owner.columns;
|
|
var column = grep(columns, function (column) {
|
|
return column.field == field || column.title == field;
|
|
})[0];
|
|
if (!column) {
|
|
return;
|
|
}
|
|
var locked = column.locked === true;
|
|
var length = grep(columns, function (column) {
|
|
return !column.hidden && (column.locked && locked || !column.locked && !locked);
|
|
}).length;
|
|
var lockItem = this.wrapper.find('.k-lock').removeClass('k-state-disabled');
|
|
var unlockItem = this.wrapper.find('.k-unlock').removeClass('k-state-disabled');
|
|
if (locked || length == 1) {
|
|
lockItem.addClass('k-state-disabled');
|
|
}
|
|
if (!locked || length == 1) {
|
|
unlockItem.addClass('k-state-disabled');
|
|
}
|
|
this._updateColumnsLockedState();
|
|
},
|
|
refresh: function () {
|
|
var that = this, sort = that.options.dataSource.sort() || [], descriptor, field = that.field, idx, length;
|
|
that.wrapper.find('.k-sort-asc, .k-sort-desc').removeClass(ACTIVE);
|
|
for (idx = 0, length = sort.length; idx < length; idx++) {
|
|
descriptor = sort[idx];
|
|
if (field == descriptor.field) {
|
|
that.wrapper.find('.k-sort-' + descriptor.dir).addClass(ACTIVE);
|
|
}
|
|
}
|
|
that.link[that._filterExist(that.dataSource.filter()) ? 'addClass' : 'removeClass']('k-state-active');
|
|
},
|
|
_filterExist: function (filters) {
|
|
var found = false;
|
|
var filter;
|
|
if (!filters) {
|
|
return;
|
|
}
|
|
filters = filters.filters;
|
|
for (var idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
if (filter.field == this.field) {
|
|
found = true;
|
|
} else if (filter.filters) {
|
|
found = found || this._filterExist(filter);
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
});
|
|
var template = '<ul>' + '#if(sortable){#' + '<li class="k-item k-sort-asc"><span class="k-link"><span class="k-sprite k-i-sort-asc"></span>${messages.sortAscending}</span></li>' + '<li class="k-item k-sort-desc"><span class="k-link"><span class="k-sprite k-i-sort-desc"></span>${messages.sortDescending}</span></li>' + '#if(showColumns || filterable){#' + '<li class="k-separator"></li>' + '#}#' + '#}#' + '#if(showColumns){#' + '<li class="k-item k-columns-item"><span class="k-link"><span class="k-sprite k-i-columns"></span>${messages.columns}</span><ul>' + '#for (var idx = 0; idx < columns.length; idx++) {#' + '<li><input type="checkbox" data-#=ns#field="#=columns[idx].field.replace(/"/g,"&\\#34;")#" data-#=ns#index="#=columns[idx].index#" data-#=ns#locked="#=columns[idx].locked#"/>#=columns[idx].title#</li>' + '#}#' + '</ul></li>' + '#if(filterable || lockedColumns){#' + '<li class="k-separator"></li>' + '#}#' + '#}#' + '#if(filterable){#' + '<li class="k-item k-filter-item"><span class="k-link"><span class="k-sprite k-filter"></span>${messages.filter}</span><ul>' + '<li><div class="k-filterable"></div></li>' + '</ul></li>' + '#if(lockedColumns){#' + '<li class="k-separator"></li>' + '#}#' + '#}#' + '#if(lockedColumns){#' + '<li class="k-item k-lock"><span class="k-link"><span class="k-sprite k-i-lock"></span>${messages.lock}</span></li>' + '<li class="k-item k-unlock"><span class="k-link"><span class="k-sprite k-i-unlock"></span>${messages.unlock}</span></li>' + '#}#' + '</ul>';
|
|
var mobileTemplate = '<div data-#=ns#role="view" data-#=ns#init-widgets="false" data-#=ns#use-native-scrolling="true" class="k-grid-column-menu">' + '<div data-#=ns#role="header" class="k-header">' + '${messages.settings}' + '<button class="k-button k-done">#=messages.done#</button>' + '</div>' + '<div class="k-column-menu k-mobile-list"><ul><li>' + '<span class="k-link">${title}</span><ul>' + '#if(sortable){#' + '<li class="k-item k-sort-asc"><span class="k-link"><span class="k-sprite k-i-sort-asc"></span>${messages.sortAscending}</span></li>' + '<li class="k-item k-sort-desc"><span class="k-link"><span class="k-sprite k-i-sort-desc"></span>${messages.sortDescending}</span></li>' + '#}#' + '#if(lockedColumns){#' + '<li class="k-item k-lock"><span class="k-link"><span class="k-sprite k-i-lock"></span>${messages.lock}</span></li>' + '<li class="k-item k-unlock"><span class="k-link"><span class="k-sprite k-i-unlock"></span>${messages.unlock}</span></li>' + '#}#' + '#if(filterable){#' + '<li class="k-item k-filter-item">' + '<span class="k-link k-filterable">' + '<span class="k-sprite k-filter"></span>' + '${messages.filter}</span>' + '</li>' + '#}#' + '</ul></li>' + '#if(showColumns){#' + '<li class="k-columns-item"><span class="k-link">${messages.columns}</span><ul>' + '#for (var idx = 0; idx < columns.length; idx++) {#' + '<li class="k-item"><label class="k-label"><input type="checkbox" class="k-check" data-#=ns#field="#=columns[idx].field.replace(/"/g,"&\\#34;")#" data-#=ns#index="#=columns[idx].index#" data-#=ns#locked="#=columns[idx].locked#"/>#=columns[idx].title#</label></li>' + '#}#' + '</ul></li>' + '#}#' + '</ul></div>' + '</div>';
|
|
var MobileMenu = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.on('click' + NS, 'li.k-item:not(.k-separator):not(.k-state-disabled)', '_click');
|
|
},
|
|
events: [SELECT],
|
|
_click: function (e) {
|
|
if (!$(e.target).is('[type=checkbox]')) {
|
|
e.preventDefault();
|
|
}
|
|
this.trigger(SELECT, { item: e.currentTarget });
|
|
},
|
|
close: function () {
|
|
this.options.pane.navigate('');
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.element.off(NS);
|
|
}
|
|
});
|
|
ui.plugin(ColumnMenu);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.columnsorter', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'columnsorter',
|
|
name: 'Column Sorter',
|
|
category: 'framework',
|
|
depends: ['core'],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var ui = kendo.ui;
|
|
var Widget = ui.Widget;
|
|
var DIR = 'dir';
|
|
var ASC = 'asc';
|
|
var SINGLE = 'single';
|
|
var FIELD = 'field';
|
|
var DESC = 'desc';
|
|
var sorterNS = '.kendoColumnSorter';
|
|
var TLINK = '.k-link';
|
|
var ARIASORT = 'aria-sort';
|
|
var proxy = $.proxy;
|
|
var ColumnSorter = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, link;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that.dataSource = that.options.dataSource.bind('change', that._refreshHandler);
|
|
link = that.element.find(TLINK);
|
|
if (!link[0]) {
|
|
link = that.element.wrapInner('<a class="k-link" href="#"/>').find(TLINK);
|
|
}
|
|
that.link = link;
|
|
that.element.on('click' + sorterNS, proxy(that._click, that));
|
|
},
|
|
options: {
|
|
name: 'ColumnSorter',
|
|
mode: SINGLE,
|
|
allowUnsort: true,
|
|
compare: null,
|
|
filter: ''
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.element.off(sorterNS);
|
|
that.dataSource.unbind('change', that._refreshHandler);
|
|
that._refreshHandler = that.element = that.link = that.dataSource = null;
|
|
},
|
|
refresh: function () {
|
|
var that = this, sort = that.dataSource.sort() || [], idx, length, descriptor, dir, element = that.element, field = element.attr(kendo.attr(FIELD));
|
|
element.removeAttr(kendo.attr(DIR));
|
|
element.removeAttr(ARIASORT);
|
|
for (idx = 0, length = sort.length; idx < length; idx++) {
|
|
descriptor = sort[idx];
|
|
if (field == descriptor.field) {
|
|
element.attr(kendo.attr(DIR), descriptor.dir);
|
|
}
|
|
}
|
|
dir = element.attr(kendo.attr(DIR));
|
|
element.find('.k-i-arrow-n,.k-i-arrow-s').remove();
|
|
if (dir === ASC) {
|
|
$('<span class="k-icon k-i-arrow-n" />').appendTo(that.link);
|
|
element.attr(ARIASORT, 'ascending');
|
|
} else if (dir === DESC) {
|
|
$('<span class="k-icon k-i-arrow-s" />').appendTo(that.link);
|
|
element.attr(ARIASORT, 'descending');
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var that = this, element = that.element, field = element.attr(kendo.attr(FIELD)), dir = element.attr(kendo.attr(DIR)), options = that.options, compare = that.options.compare === null ? undefined : that.options.compare, sort = that.dataSource.sort() || [], idx, length;
|
|
e.preventDefault();
|
|
if (options.filter && !element.is(options.filter)) {
|
|
return;
|
|
}
|
|
if (dir === ASC) {
|
|
dir = DESC;
|
|
} else if (dir === DESC && options.allowUnsort) {
|
|
dir = undefined;
|
|
} else {
|
|
dir = ASC;
|
|
}
|
|
if (options.mode === SINGLE) {
|
|
sort = [{
|
|
field: field,
|
|
dir: dir,
|
|
compare: compare
|
|
}];
|
|
} else if (options.mode === 'multiple') {
|
|
for (idx = 0, length = sort.length; idx < length; idx++) {
|
|
if (sort[idx].field === field) {
|
|
sort.splice(idx, 1);
|
|
break;
|
|
}
|
|
}
|
|
sort.push({
|
|
field: field,
|
|
dir: dir,
|
|
compare: compare
|
|
});
|
|
}
|
|
this.dataSource.sort(sort);
|
|
}
|
|
});
|
|
ui.plugin(ColumnSorter);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.editable', [
|
|
'kendo.datepicker',
|
|
'kendo.numerictextbox',
|
|
'kendo.validator',
|
|
'kendo.binder'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'editable',
|
|
name: 'Editable',
|
|
category: 'framework',
|
|
depends: [
|
|
'datepicker',
|
|
'numerictextbox',
|
|
'validator',
|
|
'binder'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, extend = $.extend, oldIE = kendo.support.browser.msie && kendo.support.browser.version < 9, isFunction = kendo.isFunction, isPlainObject = $.isPlainObject, inArray = $.inArray, nameSpecialCharRegExp = /("|\%|'|\[|\]|\$|\.|\,|\:|\;|\+|\*|\&|\!|\#|\(|\)|<|>|\=|\?|\@|\^|\{|\}|\~|\/|\||`)/g, ERRORTEMPLATE = '<div class="k-widget k-tooltip k-tooltip-validation" style="margin:0.5em"><span class="k-icon k-warning"> </span>' + '#=message#<div class="k-callout k-callout-n"></div></div>', CHANGE = 'change';
|
|
var specialRules = [
|
|
'url',
|
|
'email',
|
|
'number',
|
|
'date',
|
|
'boolean'
|
|
];
|
|
function fieldType(field) {
|
|
field = field != null ? field : '';
|
|
return field.type || $.type(field) || 'string';
|
|
}
|
|
function convertToValueBinding(container) {
|
|
container.find(':input:not(:button, [' + kendo.attr('role') + '=upload], [' + kendo.attr('skip') + '], [type=file]), select').each(function () {
|
|
var bindAttr = kendo.attr('bind'), binding = this.getAttribute(bindAttr) || '', bindingName = this.type === 'checkbox' || this.type === 'radio' ? 'checked:' : 'value:', fieldName = this.name;
|
|
if (binding.indexOf(bindingName) === -1 && fieldName) {
|
|
binding += (binding.length ? ',' : '') + bindingName + fieldName;
|
|
$(this).attr(bindAttr, binding);
|
|
}
|
|
});
|
|
}
|
|
function createAttributes(options) {
|
|
var field = (options.model.fields || options.model)[options.field], type = fieldType(field), validation = field ? field.validation : {}, ruleName, DATATYPE = kendo.attr('type'), BINDING = kendo.attr('bind'), rule, attr = { name: options.field };
|
|
for (ruleName in validation) {
|
|
rule = validation[ruleName];
|
|
if (inArray(ruleName, specialRules) >= 0) {
|
|
attr[DATATYPE] = ruleName;
|
|
} else if (!isFunction(rule)) {
|
|
attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule;
|
|
}
|
|
attr[kendo.attr(ruleName + '-msg')] = rule.message;
|
|
}
|
|
if (inArray(type, specialRules) >= 0) {
|
|
attr[DATATYPE] = type;
|
|
}
|
|
attr[BINDING] = (type === 'boolean' ? 'checked:' : 'value:') + options.field;
|
|
return attr;
|
|
}
|
|
function convertItems(items) {
|
|
var idx, length, item, value, text, result;
|
|
if (items && items.length) {
|
|
result = [];
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
item = items[idx];
|
|
text = item.text || item.value || item;
|
|
value = item.value == null ? item.text || item : item.value;
|
|
result[idx] = {
|
|
text: text,
|
|
value: value
|
|
};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
var editors = {
|
|
'number': function (container, options) {
|
|
var attr = createAttributes(options);
|
|
$('<input type="text"/>').attr(attr).appendTo(container).kendoNumericTextBox({ format: options.format });
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
},
|
|
'date': function (container, options) {
|
|
var attr = createAttributes(options), format = options.format;
|
|
if (format) {
|
|
format = kendo._extractFormat(format);
|
|
}
|
|
attr[kendo.attr('format')] = format;
|
|
$('<input type="text"/>').attr(attr).appendTo(container).kendoDatePicker({ format: options.format });
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
},
|
|
'string': function (container, options) {
|
|
var attr = createAttributes(options);
|
|
$('<input type="text" class="k-input k-textbox"/>').attr(attr).appendTo(container);
|
|
},
|
|
'boolean': function (container, options) {
|
|
var attr = createAttributes(options);
|
|
$('<input type="checkbox" />').attr(attr).appendTo(container);
|
|
},
|
|
'values': function (container, options) {
|
|
var attr = createAttributes(options);
|
|
var items = kendo.stringify(convertItems(options.values));
|
|
$('<select ' + kendo.attr('text-field') + '="text"' + kendo.attr('value-field') + '="value"' + kendo.attr('source') + '=\'' + (items ? items.replace(/\'/g, ''') : items) + '\'' + kendo.attr('role') + '="dropdownlist"/>').attr(attr).appendTo(container);
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
}
|
|
};
|
|
function addValidationRules(modelField, rules) {
|
|
var validation = modelField ? modelField.validation || {} : {}, rule, descriptor;
|
|
for (rule in validation) {
|
|
descriptor = validation[rule];
|
|
if (isPlainObject(descriptor) && descriptor.value) {
|
|
descriptor = descriptor.value;
|
|
}
|
|
if (isFunction(descriptor)) {
|
|
rules[rule] = descriptor;
|
|
}
|
|
}
|
|
}
|
|
var Editable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
if (options.target) {
|
|
options.$angular = options.target.options.$angular;
|
|
}
|
|
Widget.fn.init.call(that, element, options);
|
|
that._validateProxy = $.proxy(that._validate, that);
|
|
that.refresh();
|
|
},
|
|
events: [CHANGE],
|
|
options: {
|
|
name: 'Editable',
|
|
editors: editors,
|
|
clearContainer: true,
|
|
errorTemplate: ERRORTEMPLATE
|
|
},
|
|
editor: function (field, modelField) {
|
|
var that = this, editors = that.options.editors, isObject = isPlainObject(field), fieldName = isObject ? field.field : field, model = that.options.model || {}, isValuesEditor = isObject && field.values, type = isValuesEditor ? 'values' : fieldType(modelField), isCustomEditor = isObject && field.editor, editor = isCustomEditor ? field.editor : editors[type], container = that.element.find('[' + kendo.attr('container-for') + '=' + fieldName.replace(nameSpecialCharRegExp, '\\$1') + ']');
|
|
editor = editor ? editor : editors.string;
|
|
if (isCustomEditor && typeof field.editor === 'string') {
|
|
editor = function (container) {
|
|
container.append(field.editor);
|
|
};
|
|
}
|
|
container = container.length ? container : that.element;
|
|
editor(container, extend(true, {}, isObject ? field : { field: fieldName }, { model: model }));
|
|
},
|
|
_validate: function (e) {
|
|
var that = this, input, value = e.value, preventChangeTrigger = that._validationEventInProgress, values = {}, bindAttribute = kendo.attr('bind'), fieldName = e.field.replace(nameSpecialCharRegExp, '\\$1'), bindingRegex = new RegExp('(value|checked)\\s*:\\s*' + fieldName + '\\s*(,|$)');
|
|
values[e.field] = e.value;
|
|
input = $(':input[' + bindAttribute + '*="' + fieldName + '"]', that.element).filter('[' + kendo.attr('validate') + '!=\'false\']').filter(function () {
|
|
return bindingRegex.test($(this).attr(bindAttribute));
|
|
});
|
|
if (input.length > 1) {
|
|
input = input.filter(function () {
|
|
var element = $(this);
|
|
return !element.is(':radio') || element.val() == value;
|
|
});
|
|
}
|
|
try {
|
|
that._validationEventInProgress = true;
|
|
if (!that.validatable.validateInput(input) || !preventChangeTrigger && that.trigger(CHANGE, { values: values })) {
|
|
e.preventDefault();
|
|
}
|
|
} finally {
|
|
that._validationEventInProgress = false;
|
|
}
|
|
},
|
|
end: function () {
|
|
return this.validatable.validate();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.element };
|
|
});
|
|
Widget.fn.destroy.call(that);
|
|
that.options.model.unbind('set', that._validateProxy);
|
|
kendo.unbind(that.element);
|
|
if (that.validatable) {
|
|
that.validatable.destroy();
|
|
}
|
|
kendo.destroy(that.element);
|
|
that.element.removeData('kendoValidator');
|
|
if (that.element.is('[' + kendo.attr('role') + '=editable]')) {
|
|
that.element.removeAttr(kendo.attr('role'));
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var that = this, idx, length, fields = that.options.fields || [], container = that.options.clearContainer ? that.element.empty() : that.element, model = that.options.model || {}, rules = {}, field, isObject, fieldName, modelField, modelFields;
|
|
if (!$.isArray(fields)) {
|
|
fields = [fields];
|
|
}
|
|
for (idx = 0, length = fields.length; idx < length; idx++) {
|
|
field = fields[idx];
|
|
isObject = isPlainObject(field);
|
|
fieldName = isObject ? field.field : field;
|
|
modelField = (model.fields || model)[fieldName];
|
|
addValidationRules(modelField, rules);
|
|
that.editor(field, modelField);
|
|
}
|
|
if (that.options.target) {
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: container,
|
|
data: container.map(function () {
|
|
return { dataItem: model };
|
|
})
|
|
};
|
|
});
|
|
}
|
|
if (!length) {
|
|
modelFields = model.fields || model;
|
|
for (fieldName in modelFields) {
|
|
addValidationRules(modelFields[fieldName], rules);
|
|
}
|
|
}
|
|
convertToValueBinding(container);
|
|
if (that.validatable) {
|
|
that.validatable.destroy();
|
|
}
|
|
kendo.bind(container, that.options.model);
|
|
that.options.model.unbind('set', that._validateProxy);
|
|
that.options.model.bind('set', that._validateProxy);
|
|
that.validatable = new kendo.ui.Validator(container, {
|
|
validateOnBlur: false,
|
|
errorTemplate: that.options.errorTemplate || undefined,
|
|
rules: rules
|
|
});
|
|
var focusable = container.find(':kendoFocusable').eq(0).focus();
|
|
if (oldIE) {
|
|
focusable.focus();
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Editable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.window', ['kendo.draganddrop'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'window',
|
|
name: 'Window',
|
|
category: 'web',
|
|
description: 'The Window widget displays content in a modal or non-modal HTML window.',
|
|
depends: ['draganddrop']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, Draggable = kendo.ui.Draggable, isPlainObject = $.isPlainObject, activeElement = kendo._activeElement, proxy = $.proxy, extend = $.extend, each = $.each, template = kendo.template, BODY = 'body', templates, NS = '.kendoWindow', KWINDOW = '.k-window', KWINDOWTITLE = '.k-window-title', KWINDOWTITLEBAR = KWINDOWTITLE + 'bar', KWINDOWCONTENT = '.k-window-content', KWINDOWRESIZEHANDLES = '.k-resize-handle', KOVERLAY = '.k-overlay', KCONTENTFRAME = 'k-content-frame', LOADING = 'k-loading', KHOVERSTATE = 'k-state-hover', KFOCUSEDSTATE = 'k-state-focused', MAXIMIZEDSTATE = 'k-window-maximized', VISIBLE = ':visible', HIDDEN = 'hidden', CURSOR = 'cursor', OPEN = 'open', ACTIVATE = 'activate', DEACTIVATE = 'deactivate', CLOSE = 'close', REFRESH = 'refresh', MINIMIZE = 'minimize', MAXIMIZE = 'maximize', RESIZE = 'resize', RESIZEEND = 'resizeEnd', DRAGSTART = 'dragstart', DRAGEND = 'dragend', ERROR = 'error', OVERFLOW = 'overflow', ZINDEX = 'zIndex', MINIMIZE_MAXIMIZE = '.k-window-actions .k-i-minimize,.k-window-actions .k-i-maximize', KPIN = '.k-i-pin', KUNPIN = '.k-i-unpin', PIN_UNPIN = KPIN + ',' + KUNPIN, TITLEBAR_BUTTONS = '.k-window-titlebar .k-window-action', REFRESHICON = '.k-window-titlebar .k-i-refresh', isLocalUrl = kendo.isLocalUrl;
|
|
function defined(x) {
|
|
return typeof x != 'undefined';
|
|
}
|
|
function constrain(value, low, high) {
|
|
return Math.max(Math.min(parseInt(value, 10), high === Infinity ? high : parseInt(high, 10)), parseInt(low, 10));
|
|
}
|
|
function executableScript() {
|
|
return !this.type || this.type.toLowerCase().indexOf('script') >= 0;
|
|
}
|
|
var Window = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, wrapper, offset = {}, visibility, display, position, isVisible = false, content, windowContent, suppressActions = options && options.actions && !options.actions.length, id;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
position = options.position;
|
|
element = that.element;
|
|
content = options.content;
|
|
if (suppressActions) {
|
|
options.actions = [];
|
|
}
|
|
that.appendTo = $(options.appendTo);
|
|
if (content && !isPlainObject(content)) {
|
|
content = options.content = { url: content };
|
|
}
|
|
element.find('script').filter(executableScript).remove();
|
|
if (!element.parent().is(that.appendTo) && (position.top === undefined || position.left === undefined)) {
|
|
if (element.is(VISIBLE)) {
|
|
offset = element.offset();
|
|
isVisible = true;
|
|
} else {
|
|
visibility = element.css('visibility');
|
|
display = element.css('display');
|
|
element.css({
|
|
visibility: HIDDEN,
|
|
display: ''
|
|
});
|
|
offset = element.offset();
|
|
element.css({
|
|
visibility: visibility,
|
|
display: display
|
|
});
|
|
}
|
|
if (position.top === undefined) {
|
|
position.top = offset.top;
|
|
}
|
|
if (position.left === undefined) {
|
|
position.left = offset.left;
|
|
}
|
|
}
|
|
if (!defined(options.visible) || options.visible === null) {
|
|
options.visible = element.is(VISIBLE);
|
|
}
|
|
wrapper = that.wrapper = element.closest(KWINDOW);
|
|
if (!element.is('.k-content') || !wrapper[0]) {
|
|
element.addClass('k-window-content k-content');
|
|
that._createWindow(element, options);
|
|
wrapper = that.wrapper = element.closest(KWINDOW);
|
|
that._dimensions();
|
|
}
|
|
that._position();
|
|
if (options.pinned) {
|
|
that.pin(true);
|
|
}
|
|
if (content) {
|
|
that.refresh(content);
|
|
}
|
|
if (options.visible) {
|
|
that.toFront();
|
|
}
|
|
windowContent = wrapper.children(KWINDOWCONTENT);
|
|
that._tabindex(windowContent);
|
|
if (options.visible && options.modal) {
|
|
that._overlay(wrapper.is(VISIBLE)).css({ opacity: 0.5 });
|
|
}
|
|
wrapper.on('mouseenter' + NS, TITLEBAR_BUTTONS, proxy(that._buttonEnter, that)).on('mouseleave' + NS, TITLEBAR_BUTTONS, proxy(that._buttonLeave, that)).on('click' + NS, '> ' + TITLEBAR_BUTTONS, proxy(that._windowActionHandler, that));
|
|
windowContent.on('keydown' + NS, proxy(that._keydown, that)).on('focus' + NS, proxy(that._focus, that)).on('blur' + NS, proxy(that._blur, that));
|
|
this._resizable();
|
|
this._draggable();
|
|
id = element.attr('id');
|
|
if (id) {
|
|
id = id + '_wnd_title';
|
|
wrapper.children(KWINDOWTITLEBAR).children(KWINDOWTITLE).attr('id', id);
|
|
windowContent.attr({
|
|
'role': 'dialog',
|
|
'aria-labelledby': id
|
|
});
|
|
}
|
|
wrapper.add(wrapper.children('.k-resize-handle,' + KWINDOWTITLEBAR)).on('mousedown' + NS, proxy(that.toFront, that));
|
|
that.touchScroller = kendo.touchScroller(element);
|
|
that._resizeHandler = proxy(that._onDocumentResize, that);
|
|
that._marker = kendo.guid().substring(0, 8);
|
|
$(window).on('resize' + NS + that._marker, that._resizeHandler);
|
|
if (options.visible) {
|
|
that.trigger(OPEN);
|
|
that.trigger(ACTIVATE);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
_buttonEnter: function (e) {
|
|
$(e.currentTarget).addClass(KHOVERSTATE);
|
|
},
|
|
_buttonLeave: function (e) {
|
|
$(e.currentTarget).removeClass(KHOVERSTATE);
|
|
},
|
|
_focus: function () {
|
|
this.wrapper.addClass(KFOCUSEDSTATE);
|
|
},
|
|
_blur: function () {
|
|
this.wrapper.removeClass(KFOCUSEDSTATE);
|
|
},
|
|
_dimensions: function () {
|
|
var wrapper = this.wrapper;
|
|
var options = this.options;
|
|
var width = options.width;
|
|
var height = options.height;
|
|
var maxHeight = options.maxHeight;
|
|
var dimensions = [
|
|
'minWidth',
|
|
'minHeight',
|
|
'maxWidth',
|
|
'maxHeight'
|
|
];
|
|
this.title(options.title);
|
|
for (var i = 0; i < dimensions.length; i++) {
|
|
var value = options[dimensions[i]];
|
|
if (value && value != Infinity) {
|
|
wrapper.css(dimensions[i], value);
|
|
}
|
|
}
|
|
if (maxHeight && maxHeight != Infinity) {
|
|
this.element.css('maxHeight', maxHeight);
|
|
}
|
|
if (width) {
|
|
if (width.toString().indexOf('%') > 0) {
|
|
wrapper.width(width);
|
|
} else {
|
|
wrapper.width(constrain(width, options.minWidth, options.maxWidth));
|
|
}
|
|
}
|
|
if (height) {
|
|
if (height.toString().indexOf('%') > 0) {
|
|
wrapper.height(height);
|
|
} else {
|
|
wrapper.height(constrain(height, options.minHeight, options.maxHeight));
|
|
}
|
|
}
|
|
if (!options.visible) {
|
|
wrapper.hide();
|
|
}
|
|
},
|
|
_position: function () {
|
|
var wrapper = this.wrapper, position = this.options.position;
|
|
if (position.top === 0) {
|
|
position.top = position.top.toString();
|
|
}
|
|
if (position.left === 0) {
|
|
position.left = position.left.toString();
|
|
}
|
|
wrapper.css({
|
|
top: position.top || '',
|
|
left: position.left || ''
|
|
});
|
|
},
|
|
_animationOptions: function (id) {
|
|
var animation = this.options.animation;
|
|
var basicAnimation = {
|
|
open: { effects: {} },
|
|
close: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
return animation && animation[id] || basicAnimation[id];
|
|
},
|
|
_resize: function () {
|
|
kendo.resize(this.element.children());
|
|
},
|
|
_resizable: function () {
|
|
var resizable = this.options.resizable;
|
|
var wrapper = this.wrapper;
|
|
if (this.resizing) {
|
|
wrapper.off('dblclick' + NS).children(KWINDOWRESIZEHANDLES).remove();
|
|
this.resizing.destroy();
|
|
this.resizing = null;
|
|
}
|
|
if (resizable) {
|
|
wrapper.on('dblclick' + NS, KWINDOWTITLEBAR, proxy(function (e) {
|
|
if (!$(e.target).closest('.k-window-action').length) {
|
|
this.toggleMaximization();
|
|
}
|
|
}, this));
|
|
each('n e s w se sw ne nw'.split(' '), function (index, handler) {
|
|
wrapper.append(templates.resizeHandle(handler));
|
|
});
|
|
this.resizing = new WindowResizing(this);
|
|
}
|
|
wrapper = null;
|
|
},
|
|
_draggable: function () {
|
|
var draggable = this.options.draggable;
|
|
if (this.dragging) {
|
|
this.dragging.destroy();
|
|
this.dragging = null;
|
|
}
|
|
if (draggable) {
|
|
this.dragging = new WindowDragging(this, draggable.dragHandle || KWINDOWTITLEBAR);
|
|
}
|
|
},
|
|
_actions: function () {
|
|
var actions = this.options.actions;
|
|
var titlebar = this.wrapper.children(KWINDOWTITLEBAR);
|
|
var container = titlebar.find('.k-window-actions');
|
|
actions = $.map(actions, function (action) {
|
|
return { name: action };
|
|
});
|
|
container.html(kendo.render(templates.action, actions));
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
var scrollable = this.options.scrollable !== false;
|
|
this.restore();
|
|
this._dimensions();
|
|
this._position();
|
|
this._resizable();
|
|
this._draggable();
|
|
this._actions();
|
|
if (typeof options.modal !== 'undefined') {
|
|
var visible = this.options.visible !== false;
|
|
this._overlay(options.modal && visible);
|
|
}
|
|
this.element.css(OVERFLOW, scrollable ? '' : 'hidden');
|
|
},
|
|
events: [
|
|
OPEN,
|
|
ACTIVATE,
|
|
DEACTIVATE,
|
|
CLOSE,
|
|
MINIMIZE,
|
|
MAXIMIZE,
|
|
REFRESH,
|
|
RESIZE,
|
|
RESIZEEND,
|
|
DRAGSTART,
|
|
DRAGEND,
|
|
ERROR
|
|
],
|
|
options: {
|
|
name: 'Window',
|
|
animation: {
|
|
open: {
|
|
effects: {
|
|
zoom: { direction: 'in' },
|
|
fade: { direction: 'in' }
|
|
},
|
|
duration: 350
|
|
},
|
|
close: {
|
|
effects: {
|
|
zoom: {
|
|
direction: 'out',
|
|
properties: { scale: 0.7 }
|
|
},
|
|
fade: { direction: 'out' }
|
|
},
|
|
duration: 350,
|
|
hide: true
|
|
}
|
|
},
|
|
title: '',
|
|
actions: ['Close'],
|
|
autoFocus: true,
|
|
modal: false,
|
|
resizable: true,
|
|
draggable: true,
|
|
minWidth: 90,
|
|
minHeight: 50,
|
|
maxWidth: Infinity,
|
|
maxHeight: Infinity,
|
|
pinned: false,
|
|
scrollable: true,
|
|
position: {},
|
|
content: null,
|
|
visible: null,
|
|
height: null,
|
|
width: null,
|
|
appendTo: 'body'
|
|
},
|
|
_closable: function () {
|
|
return $.inArray('close', $.map(this.options.actions, function (x) {
|
|
return x.toLowerCase();
|
|
})) > -1;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, options = that.options, keys = kendo.keys, keyCode = e.keyCode, wrapper = that.wrapper, offset, handled, distance = 10, isMaximized = that.options.isMaximized, newWidth, newHeight, w, h;
|
|
if (e.target != e.currentTarget || that._closing) {
|
|
return;
|
|
}
|
|
if (keyCode == keys.ESC && that._closable()) {
|
|
that._close(false);
|
|
}
|
|
if (options.draggable && !e.ctrlKey && !isMaximized) {
|
|
offset = kendo.getOffset(wrapper);
|
|
if (keyCode == keys.UP) {
|
|
handled = wrapper.css('top', offset.top - distance);
|
|
} else if (keyCode == keys.DOWN) {
|
|
handled = wrapper.css('top', offset.top + distance);
|
|
} else if (keyCode == keys.LEFT) {
|
|
handled = wrapper.css('left', offset.left - distance);
|
|
} else if (keyCode == keys.RIGHT) {
|
|
handled = wrapper.css('left', offset.left + distance);
|
|
}
|
|
}
|
|
if (options.resizable && e.ctrlKey && !isMaximized) {
|
|
if (keyCode == keys.UP) {
|
|
handled = true;
|
|
newHeight = wrapper.height() - distance;
|
|
} else if (keyCode == keys.DOWN) {
|
|
handled = true;
|
|
newHeight = wrapper.height() + distance;
|
|
}
|
|
if (keyCode == keys.LEFT) {
|
|
handled = true;
|
|
newWidth = wrapper.width() - distance;
|
|
} else if (keyCode == keys.RIGHT) {
|
|
handled = true;
|
|
newWidth = wrapper.width() + distance;
|
|
}
|
|
if (handled) {
|
|
w = constrain(newWidth, options.minWidth, options.maxWidth);
|
|
h = constrain(newHeight, options.minHeight, options.maxHeight);
|
|
if (!isNaN(w)) {
|
|
wrapper.width(w);
|
|
that.options.width = w + 'px';
|
|
}
|
|
if (!isNaN(h)) {
|
|
wrapper.height(h);
|
|
that.options.height = h + 'px';
|
|
}
|
|
that.resize();
|
|
}
|
|
}
|
|
if (handled) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_overlay: function (visible) {
|
|
var overlay = this.appendTo.children(KOVERLAY), wrapper = this.wrapper;
|
|
if (!overlay.length) {
|
|
overlay = $('<div class=\'k-overlay\' />');
|
|
}
|
|
overlay.insertBefore(wrapper[0]).toggle(visible).css(ZINDEX, parseInt(wrapper.css(ZINDEX), 10) - 1);
|
|
return overlay;
|
|
},
|
|
_actionForIcon: function (icon) {
|
|
var iconClass = /\bk-i-\w+\b/.exec(icon[0].className)[0];
|
|
return {
|
|
'k-i-close': '_close',
|
|
'k-i-maximize': 'maximize',
|
|
'k-i-minimize': 'minimize',
|
|
'k-i-restore': 'restore',
|
|
'k-i-refresh': 'refresh',
|
|
'k-i-pin': 'pin',
|
|
'k-i-unpin': 'unpin'
|
|
}[iconClass];
|
|
},
|
|
_windowActionHandler: function (e) {
|
|
if (this._closing) {
|
|
return;
|
|
}
|
|
var icon = $(e.target).closest('.k-window-action').find('.k-icon');
|
|
var action = this._actionForIcon(icon);
|
|
if (action) {
|
|
e.preventDefault();
|
|
this[action]();
|
|
return false;
|
|
}
|
|
},
|
|
_modals: function () {
|
|
var that = this;
|
|
var zStack = $(KWINDOW).filter(function () {
|
|
var dom = $(this);
|
|
var object = that._object(dom);
|
|
var options = object && object.options;
|
|
return options && options.modal && options.visible && options.appendTo === that.options.appendTo && dom.is(VISIBLE);
|
|
}).sort(function (a, b) {
|
|
return +$(a).css('zIndex') - +$(b).css('zIndex');
|
|
});
|
|
that = null;
|
|
return zStack;
|
|
},
|
|
_object: function (element) {
|
|
var content = element.children(KWINDOWCONTENT);
|
|
var widget = kendo.widgetInstance(content);
|
|
if (widget instanceof Window) {
|
|
return widget;
|
|
}
|
|
return undefined;
|
|
},
|
|
center: function () {
|
|
var that = this, position = that.options.position, wrapper = that.wrapper, documentWindow = $(window), scrollTop = 0, scrollLeft = 0, newTop, newLeft;
|
|
if (that.options.isMaximized) {
|
|
return that;
|
|
}
|
|
if (!that.options.pinned) {
|
|
scrollTop = documentWindow.scrollTop();
|
|
scrollLeft = documentWindow.scrollLeft();
|
|
}
|
|
newLeft = scrollLeft + Math.max(0, (documentWindow.width() - wrapper.width()) / 2);
|
|
newTop = scrollTop + Math.max(0, (documentWindow.height() - wrapper.height() - parseInt(wrapper.css('paddingTop'), 10)) / 2);
|
|
wrapper.css({
|
|
left: newLeft,
|
|
top: newTop
|
|
});
|
|
position.top = newTop;
|
|
position.left = newLeft;
|
|
return that;
|
|
},
|
|
title: function (text) {
|
|
var that = this, wrapper = that.wrapper, options = that.options, titleBar = wrapper.children(KWINDOWTITLEBAR), title = titleBar.children(KWINDOWTITLE), titleBarHeight;
|
|
if (!arguments.length) {
|
|
return title.html();
|
|
}
|
|
if (text === false) {
|
|
wrapper.addClass('k-window-titleless');
|
|
titleBar.remove();
|
|
} else {
|
|
if (!titleBar.length) {
|
|
wrapper.prepend(templates.titlebar(options));
|
|
that._actions();
|
|
titleBar = wrapper.children(KWINDOWTITLEBAR);
|
|
} else {
|
|
title.html(text);
|
|
}
|
|
titleBarHeight = titleBar.outerHeight();
|
|
wrapper.css('padding-top', titleBarHeight);
|
|
titleBar.css('margin-top', -titleBarHeight);
|
|
}
|
|
that.options.title = text;
|
|
return that;
|
|
},
|
|
content: function (html, data) {
|
|
var content = this.wrapper.children(KWINDOWCONTENT), scrollContainer = content.children('.km-scroll-container');
|
|
content = scrollContainer[0] ? scrollContainer : content;
|
|
if (!defined(html)) {
|
|
return content.html();
|
|
}
|
|
this.angular('cleanup', function () {
|
|
return { elements: content.children() };
|
|
});
|
|
kendo.destroy(this.element.children());
|
|
content.empty().html(html);
|
|
this.angular('compile', function () {
|
|
var a = [];
|
|
for (var i = content.length; --i >= 0;) {
|
|
a.push({ dataItem: data });
|
|
}
|
|
return {
|
|
elements: content.children(),
|
|
data: a
|
|
};
|
|
});
|
|
return this;
|
|
},
|
|
open: function () {
|
|
var that = this, wrapper = that.wrapper, options = that.options, showOptions = this._animationOptions('open'), contentElement = wrapper.children(KWINDOWCONTENT), overlay, doc = $(document);
|
|
if (!that.trigger(OPEN)) {
|
|
if (that._closing) {
|
|
wrapper.kendoStop(true, true);
|
|
}
|
|
that._closing = false;
|
|
that.toFront();
|
|
if (options.autoFocus) {
|
|
that.element.focus();
|
|
}
|
|
options.visible = true;
|
|
if (options.modal) {
|
|
overlay = that._overlay(false);
|
|
overlay.kendoStop(true, true);
|
|
if (showOptions.duration && kendo.effects.Fade) {
|
|
var overlayFx = kendo.fx(overlay).fadeIn();
|
|
overlayFx.duration(showOptions.duration || 0);
|
|
overlayFx.endValue(0.5);
|
|
overlayFx.play();
|
|
} else {
|
|
overlay.css('opacity', 0.5);
|
|
}
|
|
overlay.show();
|
|
}
|
|
if (!wrapper.is(VISIBLE)) {
|
|
contentElement.css(OVERFLOW, HIDDEN);
|
|
wrapper.show().kendoStop().kendoAnimate({
|
|
effects: showOptions.effects,
|
|
duration: showOptions.duration,
|
|
complete: proxy(this._activate, this)
|
|
});
|
|
}
|
|
}
|
|
if (options.isMaximized) {
|
|
that._documentScrollTop = doc.scrollTop();
|
|
that._documentScrollLeft = doc.scrollLeft();
|
|
$('html, body').css(OVERFLOW, HIDDEN);
|
|
}
|
|
return that;
|
|
},
|
|
_activate: function () {
|
|
var scrollable = this.options.scrollable !== false;
|
|
if (this.options.autoFocus) {
|
|
this.element.focus();
|
|
}
|
|
this.element.css(OVERFLOW, scrollable ? '' : 'hidden');
|
|
this.trigger(ACTIVATE);
|
|
},
|
|
_removeOverlay: function (suppressAnimation) {
|
|
var modals = this._modals();
|
|
var options = this.options;
|
|
var hideOverlay = options.modal && !modals.length;
|
|
var overlay = options.modal ? this._overlay(true) : $(undefined);
|
|
var hideOptions = this._animationOptions('close');
|
|
if (hideOverlay) {
|
|
if (!suppressAnimation && hideOptions.duration && kendo.effects.Fade) {
|
|
var overlayFx = kendo.fx(overlay).fadeOut();
|
|
overlayFx.duration(hideOptions.duration || 0);
|
|
overlayFx.startValue(0.5);
|
|
overlayFx.play();
|
|
} else {
|
|
this._overlay(false).remove();
|
|
}
|
|
} else if (modals.length) {
|
|
this._object(modals.last())._overlay(true);
|
|
}
|
|
},
|
|
_close: function (systemTriggered) {
|
|
var that = this, wrapper = that.wrapper, options = that.options, showOptions = this._animationOptions('open'), hideOptions = this._animationOptions('close'), doc = $(document);
|
|
if (wrapper.is(VISIBLE) && !that.trigger(CLOSE, { userTriggered: !systemTriggered })) {
|
|
if (that._closing) {
|
|
return;
|
|
}
|
|
that._closing = true;
|
|
options.visible = false;
|
|
$(KWINDOW).each(function (i, element) {
|
|
var contentElement = $(element).children(KWINDOWCONTENT);
|
|
if (element != wrapper && contentElement.find('> .' + KCONTENTFRAME).length > 0) {
|
|
contentElement.children(KOVERLAY).remove();
|
|
}
|
|
});
|
|
this._removeOverlay();
|
|
wrapper.kendoStop().kendoAnimate({
|
|
effects: hideOptions.effects || showOptions.effects,
|
|
reverse: hideOptions.reverse === true,
|
|
duration: hideOptions.duration,
|
|
complete: proxy(this._deactivate, this)
|
|
});
|
|
}
|
|
if (that.options.isMaximized) {
|
|
$('html, body').css(OVERFLOW, '');
|
|
if (that._documentScrollTop && that._documentScrollTop > 0) {
|
|
doc.scrollTop(that._documentScrollTop);
|
|
}
|
|
if (that._documentScrollLeft && that._documentScrollLeft > 0) {
|
|
doc.scrollLeft(that._documentScrollLeft);
|
|
}
|
|
}
|
|
},
|
|
_deactivate: function () {
|
|
var that = this;
|
|
that.wrapper.hide().css('opacity', '');
|
|
that.trigger(DEACTIVATE);
|
|
if (that.options.modal) {
|
|
var lastModal = that._object(that._modals().last());
|
|
if (lastModal) {
|
|
lastModal.toFront();
|
|
}
|
|
}
|
|
},
|
|
close: function () {
|
|
this._close(true);
|
|
return this;
|
|
},
|
|
_actionable: function (element) {
|
|
return $(element).is(TITLEBAR_BUTTONS + ',' + TITLEBAR_BUTTONS + ' .k-icon,:input,a');
|
|
},
|
|
_shouldFocus: function (target) {
|
|
var active = activeElement(), element = this.element;
|
|
return this.options.autoFocus && !$(active).is(element) && !this._actionable(target) && (!element.find(active).length || !element.find(target).length);
|
|
},
|
|
toFront: function (e) {
|
|
var that = this, wrapper = that.wrapper, currentWindow = wrapper[0], zIndex = +wrapper.css(ZINDEX), originalZIndex = zIndex, target = e && e.target || null;
|
|
$(KWINDOW).each(function (i, element) {
|
|
var windowObject = $(element), zIndexNew = windowObject.css(ZINDEX), contentElement = windowObject.children(KWINDOWCONTENT);
|
|
if (!isNaN(zIndexNew)) {
|
|
zIndex = Math.max(+zIndexNew, zIndex);
|
|
}
|
|
if (element != currentWindow && contentElement.find('> .' + KCONTENTFRAME).length > 0) {
|
|
contentElement.append(templates.overlay);
|
|
}
|
|
});
|
|
if (!wrapper[0].style.zIndex || originalZIndex < zIndex) {
|
|
wrapper.css(ZINDEX, zIndex + 2);
|
|
}
|
|
that.element.find('> .k-overlay').remove();
|
|
if (that._shouldFocus(target)) {
|
|
that.element.focus();
|
|
var scrollTop = $(window).scrollTop(), windowTop = parseInt(wrapper.position().top, 10);
|
|
if (windowTop > 0 && windowTop < scrollTop) {
|
|
if (scrollTop > 0) {
|
|
$(window).scrollTop(windowTop);
|
|
} else {
|
|
wrapper.css('top', scrollTop);
|
|
}
|
|
}
|
|
}
|
|
wrapper = null;
|
|
return that;
|
|
},
|
|
toggleMaximization: function () {
|
|
if (this._closing) {
|
|
return this;
|
|
}
|
|
return this[this.options.isMaximized ? 'restore' : 'maximize']();
|
|
},
|
|
restore: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var minHeight = options.minHeight;
|
|
var restoreOptions = that.restoreOptions;
|
|
var doc = $(document);
|
|
if (!options.isMaximized && !options.isMinimized) {
|
|
return that;
|
|
}
|
|
if (minHeight && minHeight != Infinity) {
|
|
that.wrapper.css('min-height', minHeight);
|
|
}
|
|
that.wrapper.css({
|
|
position: options.pinned ? 'fixed' : 'absolute',
|
|
left: restoreOptions.left,
|
|
top: restoreOptions.top,
|
|
width: restoreOptions.width,
|
|
height: restoreOptions.height
|
|
}).removeClass(MAXIMIZEDSTATE).find('.k-window-content,.k-resize-handle').show().end().find('.k-window-titlebar .k-i-restore').parent().remove().end().end().find(MINIMIZE_MAXIMIZE).parent().show().end().end().find(PIN_UNPIN).parent().show();
|
|
that.options.width = restoreOptions.width;
|
|
that.options.height = restoreOptions.height;
|
|
$('html, body').css(OVERFLOW, '');
|
|
if (this._documentScrollTop && this._documentScrollTop > 0) {
|
|
doc.scrollTop(this._documentScrollTop);
|
|
}
|
|
if (this._documentScrollLeft && this._documentScrollLeft > 0) {
|
|
doc.scrollLeft(this._documentScrollLeft);
|
|
}
|
|
options.isMaximized = options.isMinimized = false;
|
|
that.resize();
|
|
return that;
|
|
},
|
|
_sizingAction: function (actionId, callback) {
|
|
var that = this, wrapper = that.wrapper, style = wrapper[0].style, options = that.options;
|
|
if (options.isMaximized || options.isMinimized) {
|
|
return that;
|
|
}
|
|
that.restoreOptions = {
|
|
width: style.width,
|
|
height: style.height
|
|
};
|
|
wrapper.children(KWINDOWRESIZEHANDLES).hide().end().children(KWINDOWTITLEBAR).find(MINIMIZE_MAXIMIZE).parent().hide().eq(0).before(templates.action({ name: 'Restore' }));
|
|
callback.call(that);
|
|
that.wrapper.children(KWINDOWTITLEBAR).find(PIN_UNPIN).parent().toggle(actionId !== 'maximize');
|
|
that.trigger(actionId);
|
|
return that;
|
|
},
|
|
maximize: function () {
|
|
this._sizingAction('maximize', function () {
|
|
var that = this, wrapper = that.wrapper, position = wrapper.position(), doc = $(document);
|
|
extend(that.restoreOptions, {
|
|
left: position.left,
|
|
top: position.top
|
|
});
|
|
wrapper.css({
|
|
left: 0,
|
|
top: 0,
|
|
position: 'fixed'
|
|
}).addClass(MAXIMIZEDSTATE);
|
|
this._documentScrollTop = doc.scrollTop();
|
|
this._documentScrollLeft = doc.scrollLeft();
|
|
$('html, body').css(OVERFLOW, HIDDEN);
|
|
that.options.isMaximized = true;
|
|
that._onDocumentResize();
|
|
});
|
|
},
|
|
minimize: function () {
|
|
this._sizingAction('minimize', function () {
|
|
var that = this;
|
|
that.wrapper.css({
|
|
height: '',
|
|
minHeight: ''
|
|
});
|
|
that.element.hide();
|
|
that.options.isMinimized = true;
|
|
});
|
|
},
|
|
pin: function (force) {
|
|
var that = this, win = $(window), wrapper = that.wrapper, top = parseInt(wrapper.css('top'), 10), left = parseInt(wrapper.css('left'), 10);
|
|
if (force || !that.options.pinned && !that.options.isMaximized) {
|
|
wrapper.css({
|
|
position: 'fixed',
|
|
top: top - win.scrollTop(),
|
|
left: left - win.scrollLeft()
|
|
});
|
|
wrapper.children(KWINDOWTITLEBAR).find(KPIN).addClass('k-i-unpin').removeClass('k-i-pin');
|
|
that.options.pinned = true;
|
|
}
|
|
},
|
|
unpin: function () {
|
|
var that = this, win = $(window), wrapper = that.wrapper, top = parseInt(wrapper.css('top'), 10), left = parseInt(wrapper.css('left'), 10);
|
|
if (that.options.pinned && !that.options.isMaximized) {
|
|
wrapper.css({
|
|
position: '',
|
|
top: top + win.scrollTop(),
|
|
left: left + win.scrollLeft()
|
|
});
|
|
wrapper.children(KWINDOWTITLEBAR).find(KUNPIN).addClass('k-i-pin').removeClass('k-i-unpin');
|
|
that.options.pinned = false;
|
|
}
|
|
},
|
|
_onDocumentResize: function () {
|
|
var that = this, wrapper = that.wrapper, wnd = $(window), zoomLevel = kendo.support.zoomLevel(), w, h;
|
|
if (!that.options.isMaximized) {
|
|
return;
|
|
}
|
|
w = wnd.width() / zoomLevel;
|
|
h = wnd.height() / zoomLevel - parseInt(wrapper.css('padding-top'), 10);
|
|
wrapper.css({
|
|
width: w,
|
|
height: h
|
|
});
|
|
that.options.width = w;
|
|
that.options.height = h;
|
|
that.resize();
|
|
},
|
|
refresh: function (options) {
|
|
var that = this, initOptions = that.options, element = $(that.element), iframe, showIframe, url;
|
|
if (!isPlainObject(options)) {
|
|
options = { url: options };
|
|
}
|
|
options = extend({}, initOptions.content, options);
|
|
showIframe = defined(initOptions.iframe) ? initOptions.iframe : options.iframe;
|
|
url = options.url;
|
|
if (url) {
|
|
if (!defined(showIframe)) {
|
|
showIframe = !isLocalUrl(url);
|
|
}
|
|
if (!showIframe) {
|
|
that._ajaxRequest(options);
|
|
} else {
|
|
iframe = element.find('.' + KCONTENTFRAME)[0];
|
|
if (iframe) {
|
|
iframe.src = url || iframe.src;
|
|
} else {
|
|
element.html(templates.contentFrame(extend({}, initOptions, { content: options })));
|
|
}
|
|
element.find('.' + KCONTENTFRAME).unbind('load' + NS).on('load' + NS, proxy(this._triggerRefresh, this));
|
|
}
|
|
} else {
|
|
if (options.template) {
|
|
that.content(template(options.template)({}));
|
|
}
|
|
that.trigger(REFRESH);
|
|
}
|
|
element.toggleClass('k-window-iframecontent', !!showIframe);
|
|
return that;
|
|
},
|
|
_triggerRefresh: function () {
|
|
this.trigger(REFRESH);
|
|
},
|
|
_ajaxComplete: function () {
|
|
clearTimeout(this._loadingIconTimeout);
|
|
this.wrapper.find(REFRESHICON).removeClass(LOADING);
|
|
},
|
|
_ajaxError: function (xhr, status) {
|
|
this.trigger(ERROR, {
|
|
status: status,
|
|
xhr: xhr
|
|
});
|
|
},
|
|
_ajaxSuccess: function (contentTemplate) {
|
|
return function (data) {
|
|
var html = data;
|
|
if (contentTemplate) {
|
|
html = template(contentTemplate)(data || {});
|
|
}
|
|
this.content(html, data);
|
|
this.element.prop('scrollTop', 0);
|
|
this.trigger(REFRESH);
|
|
};
|
|
},
|
|
_showLoading: function () {
|
|
this.wrapper.find(REFRESHICON).addClass(LOADING);
|
|
},
|
|
_ajaxRequest: function (options) {
|
|
this._loadingIconTimeout = setTimeout(proxy(this._showLoading, this), 100);
|
|
$.ajax(extend({
|
|
type: 'GET',
|
|
dataType: 'html',
|
|
cache: false,
|
|
error: proxy(this._ajaxError, this),
|
|
complete: proxy(this._ajaxComplete, this),
|
|
success: proxy(this._ajaxSuccess(options.template), this)
|
|
}, options));
|
|
},
|
|
_destroy: function () {
|
|
if (this.resizing) {
|
|
this.resizing.destroy();
|
|
}
|
|
if (this.dragging) {
|
|
this.dragging.destroy();
|
|
}
|
|
this.wrapper.off(NS).children(KWINDOWCONTENT).off(NS).end().find('.k-resize-handle,.k-window-titlebar').off(NS);
|
|
$(window).off('resize' + NS + this._marker);
|
|
clearTimeout(this._loadingIconTimeout);
|
|
Widget.fn.destroy.call(this);
|
|
this.unbind(undefined);
|
|
kendo.destroy(this.wrapper);
|
|
this._removeOverlay(true);
|
|
},
|
|
destroy: function () {
|
|
this._destroy();
|
|
this.wrapper.empty().remove();
|
|
this.wrapper = this.appendTo = this.element = $();
|
|
},
|
|
_createWindow: function () {
|
|
var contentHtml = this.element, options = this.options, iframeSrcAttributes, wrapper, isRtl = kendo.support.isRtl(contentHtml);
|
|
if (options.scrollable === false) {
|
|
contentHtml.attr('style', 'overflow:hidden;');
|
|
}
|
|
wrapper = $(templates.wrapper(options));
|
|
iframeSrcAttributes = contentHtml.find('iframe:not(.k-content)').map(function () {
|
|
var src = this.getAttribute('src');
|
|
this.src = '';
|
|
return src;
|
|
});
|
|
wrapper.toggleClass('k-rtl', isRtl).appendTo(this.appendTo).append(contentHtml).find('iframe:not(.k-content)').each(function (index) {
|
|
this.src = iframeSrcAttributes[index];
|
|
});
|
|
wrapper.find('.k-window-title').css(isRtl ? 'left' : 'right', wrapper.find('.k-window-actions').outerWidth() + 10);
|
|
contentHtml.css('visibility', '').show();
|
|
contentHtml.find('[data-role=editor]').each(function () {
|
|
var editor = $(this).data('kendoEditor');
|
|
if (editor) {
|
|
editor.refresh();
|
|
}
|
|
});
|
|
wrapper = contentHtml = null;
|
|
}
|
|
});
|
|
templates = {
|
|
wrapper: template('<div class=\'k-widget k-window\' />'),
|
|
action: template('<a role=\'button\' href=\'\\#\' class=\'k-window-action k-link\'>' + '<span role=\'presentation\' class=\'k-icon k-i-#= name.toLowerCase() #\'>#= name #</span>' + '</a>'),
|
|
titlebar: template('<div class=\'k-window-titlebar k-header\'> ' + '<span class=\'k-window-title\'>#= title #</span>' + '<div class=\'k-window-actions\' />' + '</div>'),
|
|
overlay: '<div class=\'k-overlay\' />',
|
|
contentFrame: template('<iframe frameborder=\'0\' title=\'#= title #\' class=\'' + KCONTENTFRAME + '\' ' + 'src=\'#= content.url #\'>' + 'This page requires frames in order to show content' + '</iframe>'),
|
|
resizeHandle: template('<div class=\'k-resize-handle k-resize-#= data #\'></div>')
|
|
};
|
|
function WindowResizing(wnd) {
|
|
var that = this;
|
|
that.owner = wnd;
|
|
that._draggable = new Draggable(wnd.wrapper, {
|
|
filter: '>' + KWINDOWRESIZEHANDLES,
|
|
group: wnd.wrapper.id + '-resizing',
|
|
dragstart: proxy(that.dragstart, that),
|
|
drag: proxy(that.drag, that),
|
|
dragend: proxy(that.dragend, that)
|
|
});
|
|
that._draggable.userEvents.bind('press', proxy(that.addOverlay, that));
|
|
that._draggable.userEvents.bind('release', proxy(that.removeOverlay, that));
|
|
}
|
|
WindowResizing.prototype = {
|
|
addOverlay: function () {
|
|
this.owner.wrapper.append(templates.overlay);
|
|
},
|
|
removeOverlay: function () {
|
|
this.owner.wrapper.find(KOVERLAY).remove();
|
|
},
|
|
dragstart: function (e) {
|
|
var that = this;
|
|
var wnd = that.owner;
|
|
var wrapper = wnd.wrapper;
|
|
that.elementPadding = parseInt(wrapper.css('padding-top'), 10);
|
|
that.initialPosition = kendo.getOffset(wrapper, 'position');
|
|
that.resizeDirection = e.currentTarget.prop('className').replace('k-resize-handle k-resize-', '');
|
|
that.initialSize = {
|
|
width: wrapper.width(),
|
|
height: wrapper.height()
|
|
};
|
|
that.containerOffset = kendo.getOffset(wnd.appendTo, 'position');
|
|
wrapper.children(KWINDOWRESIZEHANDLES).not(e.currentTarget).hide();
|
|
$(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
|
|
},
|
|
drag: function (e) {
|
|
var that = this, wnd = that.owner, wrapper = wnd.wrapper, options = wnd.options, direction = that.resizeDirection, containerOffset = that.containerOffset, initialPosition = that.initialPosition, initialSize = that.initialSize, newWidth, newHeight, windowBottom, windowRight, x = Math.max(e.x.location, containerOffset.left), y = Math.max(e.y.location, containerOffset.top);
|
|
if (direction.indexOf('e') >= 0) {
|
|
newWidth = x - initialPosition.left;
|
|
wrapper.width(constrain(newWidth, options.minWidth, options.maxWidth));
|
|
} else if (direction.indexOf('w') >= 0) {
|
|
windowRight = initialPosition.left + initialSize.width;
|
|
newWidth = constrain(windowRight - x, options.minWidth, options.maxWidth);
|
|
wrapper.css({
|
|
left: windowRight - newWidth - containerOffset.left,
|
|
width: newWidth
|
|
});
|
|
}
|
|
if (direction.indexOf('s') >= 0) {
|
|
newHeight = y - initialPosition.top - that.elementPadding;
|
|
wrapper.height(constrain(newHeight, options.minHeight, options.maxHeight));
|
|
} else if (direction.indexOf('n') >= 0) {
|
|
windowBottom = initialPosition.top + initialSize.height;
|
|
newHeight = constrain(windowBottom - y, options.minHeight, options.maxHeight);
|
|
wrapper.css({
|
|
top: windowBottom - newHeight - containerOffset.top,
|
|
height: newHeight
|
|
});
|
|
}
|
|
if (newWidth) {
|
|
wnd.options.width = newWidth + 'px';
|
|
}
|
|
if (newHeight) {
|
|
wnd.options.height = newHeight + 'px';
|
|
}
|
|
wnd.resize();
|
|
},
|
|
dragend: function (e) {
|
|
var that = this, wnd = that.owner, wrapper = wnd.wrapper;
|
|
wrapper.children(KWINDOWRESIZEHANDLES).not(e.currentTarget).show();
|
|
$(BODY).css(CURSOR, '');
|
|
if (wnd.touchScroller) {
|
|
wnd.touchScroller.reset();
|
|
}
|
|
if (e.keyCode == 27) {
|
|
wrapper.css(that.initialPosition).css(that.initialSize);
|
|
}
|
|
wnd.trigger(RESIZEEND);
|
|
return false;
|
|
},
|
|
destroy: function () {
|
|
if (this._draggable) {
|
|
this._draggable.destroy();
|
|
}
|
|
this._draggable = this.owner = null;
|
|
}
|
|
};
|
|
function WindowDragging(wnd, dragHandle) {
|
|
var that = this;
|
|
that.owner = wnd;
|
|
that._draggable = new Draggable(wnd.wrapper, {
|
|
filter: dragHandle,
|
|
group: wnd.wrapper.id + '-moving',
|
|
dragstart: proxy(that.dragstart, that),
|
|
drag: proxy(that.drag, that),
|
|
dragend: proxy(that.dragend, that),
|
|
dragcancel: proxy(that.dragcancel, that)
|
|
});
|
|
that._draggable.userEvents.stopPropagation = false;
|
|
}
|
|
WindowDragging.prototype = {
|
|
dragstart: function (e) {
|
|
var wnd = this.owner, element = wnd.element, actions = element.find('.k-window-actions'), containerOffset = kendo.getOffset(wnd.appendTo);
|
|
wnd.trigger(DRAGSTART);
|
|
wnd.initialWindowPosition = kendo.getOffset(wnd.wrapper, 'position');
|
|
wnd.startPosition = {
|
|
left: e.x.client - wnd.initialWindowPosition.left,
|
|
top: e.y.client - wnd.initialWindowPosition.top
|
|
};
|
|
if (actions.length > 0) {
|
|
wnd.minLeftPosition = actions.outerWidth() + parseInt(actions.css('right'), 10) - element.outerWidth();
|
|
} else {
|
|
wnd.minLeftPosition = 20 - element.outerWidth();
|
|
}
|
|
wnd.minLeftPosition -= containerOffset.left;
|
|
wnd.minTopPosition = -containerOffset.top;
|
|
wnd.wrapper.append(templates.overlay).children(KWINDOWRESIZEHANDLES).hide();
|
|
$(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
|
|
},
|
|
drag: function (e) {
|
|
var wnd = this.owner, position = wnd.options.position, newTop = Math.max(e.y.client - wnd.startPosition.top, wnd.minTopPosition), newLeft = Math.max(e.x.client - wnd.startPosition.left, wnd.minLeftPosition), coordinates = {
|
|
left: newLeft,
|
|
top: newTop
|
|
};
|
|
$(wnd.wrapper).css(coordinates);
|
|
position.top = newTop;
|
|
position.left = newLeft;
|
|
},
|
|
_finishDrag: function () {
|
|
var wnd = this.owner;
|
|
wnd.wrapper.children(KWINDOWRESIZEHANDLES).toggle(!wnd.options.isMinimized).end().find(KOVERLAY).remove();
|
|
$(BODY).css(CURSOR, '');
|
|
},
|
|
dragcancel: function (e) {
|
|
this._finishDrag();
|
|
e.currentTarget.closest(KWINDOW).css(this.owner.initialWindowPosition);
|
|
},
|
|
dragend: function () {
|
|
this._finishDrag();
|
|
this.owner.trigger(DRAGEND);
|
|
return false;
|
|
},
|
|
destroy: function () {
|
|
if (this._draggable) {
|
|
this._draggable.destroy();
|
|
}
|
|
this._draggable = this.owner = null;
|
|
}
|
|
};
|
|
kendo.ui.plugin(Window);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.view', [
|
|
'kendo.core',
|
|
'kendo.fx',
|
|
'kendo.mobile.scroller',
|
|
'kendo.view'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.view',
|
|
name: 'View',
|
|
category: 'mobile',
|
|
description: 'Mobile View',
|
|
depends: [
|
|
'core',
|
|
'fx',
|
|
'mobile.scroller',
|
|
'view'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, attr = kendo.attr, Widget = ui.Widget, ViewClone = kendo.ViewClone, INIT = 'init', UI_OVERLAY = '<div style="height: 100%; width: 100%; position: absolute; top: 0; left: 0; z-index: 20000; display: none" />', BEFORE_SHOW = 'beforeShow', SHOW = 'show', AFTER_SHOW = 'afterShow', BEFORE_HIDE = 'beforeHide', TRANSITION_END = 'transitionEnd', TRANSITION_START = 'transitionStart', HIDE = 'hide', DESTROY = 'destroy', attrValue = kendo.attrValue, roleSelector = kendo.roleSelector, directiveSelector = kendo.directiveSelector, compileMobileDirective = kendo.compileMobileDirective;
|
|
function initPopOvers(element) {
|
|
var popovers = element.find(roleSelector('popover')), idx, length, roles = ui.roles;
|
|
for (idx = 0, length = popovers.length; idx < length; idx++) {
|
|
kendo.initWidget(popovers[idx], {}, roles);
|
|
}
|
|
}
|
|
function preventScrollIfNotInput(e) {
|
|
if (!kendo.triggeredByInput(e)) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
var View = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.params = {};
|
|
$.extend(this, options);
|
|
this.transition = this.transition || this.defaultTransition;
|
|
this._id();
|
|
if (!this.options.$angular) {
|
|
this._layout();
|
|
this._overlay();
|
|
this._scroller();
|
|
this._model();
|
|
} else {
|
|
this._overlay();
|
|
}
|
|
},
|
|
events: [
|
|
INIT,
|
|
BEFORE_SHOW,
|
|
SHOW,
|
|
AFTER_SHOW,
|
|
BEFORE_HIDE,
|
|
HIDE,
|
|
DESTROY,
|
|
TRANSITION_START,
|
|
TRANSITION_END
|
|
],
|
|
options: {
|
|
name: 'View',
|
|
title: '',
|
|
layout: null,
|
|
getLayout: $.noop,
|
|
reload: false,
|
|
transition: '',
|
|
defaultTransition: '',
|
|
useNativeScrolling: false,
|
|
stretch: false,
|
|
zoom: false,
|
|
model: null,
|
|
modelScope: window,
|
|
scroller: {},
|
|
initWidgets: true
|
|
},
|
|
enable: function (enable) {
|
|
if (typeof enable == 'undefined') {
|
|
enable = true;
|
|
}
|
|
if (enable) {
|
|
this.overlay.hide();
|
|
} else {
|
|
this.overlay.show();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
if (this.layout) {
|
|
this.layout.detach(this);
|
|
}
|
|
this.trigger(DESTROY);
|
|
Widget.fn.destroy.call(this);
|
|
if (this.scroller) {
|
|
this.scroller.destroy();
|
|
}
|
|
if (this.options.$angular) {
|
|
this.element.scope().$destroy();
|
|
}
|
|
kendo.destroy(this.element);
|
|
},
|
|
purge: function () {
|
|
this.destroy();
|
|
this.element.remove();
|
|
},
|
|
triggerBeforeShow: function () {
|
|
if (this.trigger(BEFORE_SHOW, { view: this })) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
triggerBeforeHide: function () {
|
|
if (this.trigger(BEFORE_HIDE, { view: this })) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
showStart: function () {
|
|
var element = this.element;
|
|
element.css('display', '');
|
|
if (!this.inited) {
|
|
this.inited = true;
|
|
this.trigger(INIT, { view: this });
|
|
} else {
|
|
this._invokeNgController();
|
|
}
|
|
if (this.layout) {
|
|
this.layout.attach(this);
|
|
}
|
|
this._padIfNativeScrolling();
|
|
this.trigger(SHOW, { view: this });
|
|
kendo.resize(element);
|
|
},
|
|
showEnd: function () {
|
|
this.trigger(AFTER_SHOW, { view: this });
|
|
this._padIfNativeScrolling();
|
|
},
|
|
hideEnd: function () {
|
|
var that = this;
|
|
that.element.hide();
|
|
that.trigger(HIDE, { view: that });
|
|
if (that.layout) {
|
|
that.layout.trigger(HIDE, {
|
|
view: that,
|
|
layout: that.layout
|
|
});
|
|
}
|
|
},
|
|
beforeTransition: function (type) {
|
|
this.trigger(TRANSITION_START, { type: type });
|
|
},
|
|
afterTransition: function (type) {
|
|
this.trigger(TRANSITION_END, { type: type });
|
|
},
|
|
_padIfNativeScrolling: function () {
|
|
if (mobile.appLevelNativeScrolling()) {
|
|
var isAndroid = kendo.support.mobileOS && kendo.support.mobileOS.android, skin = mobile.application.skin() || '', isAndroidForced = mobile.application.os.android || skin.indexOf('android') > -1, hasPlatformIndependentSkin = skin === 'flat' || skin.indexOf('material') > -1, topContainer = (isAndroid || isAndroidForced) && !hasPlatformIndependentSkin ? 'footer' : 'header', bottomContainer = (isAndroid || isAndroidForced) && !hasPlatformIndependentSkin ? 'header' : 'footer';
|
|
this.content.css({
|
|
paddingTop: this[topContainer].height(),
|
|
paddingBottom: this[bottomContainer].height()
|
|
});
|
|
}
|
|
},
|
|
contentElement: function () {
|
|
var that = this;
|
|
return that.options.stretch ? that.content : that.scrollerContent;
|
|
},
|
|
clone: function () {
|
|
return new ViewClone(this);
|
|
},
|
|
_scroller: function () {
|
|
var that = this;
|
|
if (mobile.appLevelNativeScrolling()) {
|
|
return;
|
|
}
|
|
if (that.options.stretch) {
|
|
that.content.addClass('km-stretched-view');
|
|
} else {
|
|
that.content.kendoMobileScroller($.extend(that.options.scroller, {
|
|
zoom: that.options.zoom,
|
|
useNative: that.options.useNativeScrolling
|
|
}));
|
|
that.scroller = that.content.data('kendoMobileScroller');
|
|
that.scrollerContent = that.scroller.scrollElement;
|
|
}
|
|
if (kendo.support.kineticScrollNeeded) {
|
|
$(that.element).on('touchmove', '.km-header', preventScrollIfNotInput);
|
|
if (!that.options.useNativeScrolling && !that.options.stretch) {
|
|
$(that.element).on('touchmove', '.km-content', preventScrollIfNotInput);
|
|
}
|
|
}
|
|
},
|
|
_model: function () {
|
|
var that = this, element = that.element, model = that.options.model;
|
|
if (typeof model === 'string') {
|
|
model = kendo.getter(model)(that.options.modelScope);
|
|
}
|
|
that.model = model;
|
|
initPopOvers(element);
|
|
that.element.css('display', '');
|
|
if (that.options.initWidgets) {
|
|
if (model) {
|
|
kendo.bind(element, model, ui, kendo.ui, kendo.dataviz.ui);
|
|
} else {
|
|
mobile.init(element.children());
|
|
}
|
|
}
|
|
that.element.css('display', 'none');
|
|
},
|
|
_id: function () {
|
|
var element = this.element, idAttrValue = element.attr('id') || '';
|
|
this.id = attrValue(element, 'url') || '#' + idAttrValue;
|
|
if (this.id == '#') {
|
|
this.id = kendo.guid();
|
|
element.attr('id', this.id);
|
|
}
|
|
},
|
|
_layout: function () {
|
|
var contentSelector = roleSelector('content'), element = this.element;
|
|
element.addClass('km-view');
|
|
this.header = element.children(roleSelector('header')).addClass('km-header');
|
|
this.footer = element.children(roleSelector('footer')).addClass('km-footer');
|
|
if (!element.children(contentSelector)[0]) {
|
|
element.wrapInner('<div ' + attr('role') + '="content"></div>');
|
|
}
|
|
this.content = element.children(roleSelector('content')).addClass('km-content');
|
|
this.element.prepend(this.header).append(this.footer);
|
|
this.layout = this.options.getLayout(this.layout);
|
|
if (this.layout) {
|
|
this.layout.setup(this);
|
|
}
|
|
},
|
|
_overlay: function () {
|
|
this.overlay = $(UI_OVERLAY).appendTo(this.element);
|
|
},
|
|
_invokeNgController: function () {
|
|
var controller, scope;
|
|
if (this.options.$angular) {
|
|
controller = this.element.controller();
|
|
scope = this.options.$angular[0];
|
|
if (controller) {
|
|
var callback = $.proxy(this, '_callController', controller, scope);
|
|
if (/^\$(digest|apply)$/.test(scope.$$phase)) {
|
|
callback();
|
|
} else {
|
|
scope.$apply(callback);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_callController: function (controller, scope) {
|
|
this.element.injector().invoke(controller.constructor, controller, { $scope: scope });
|
|
}
|
|
});
|
|
function initWidgets(collection) {
|
|
collection.each(function () {
|
|
kendo.initWidget($(this), {}, ui.roles);
|
|
});
|
|
}
|
|
var Layout = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
element = this.element;
|
|
this.header = element.children(this._locate('header')).addClass('km-header');
|
|
this.footer = element.children(this._locate('footer')).addClass('km-footer');
|
|
this.elements = this.header.add(this.footer);
|
|
initPopOvers(element);
|
|
if (!this.options.$angular) {
|
|
kendo.mobile.init(this.element.children());
|
|
}
|
|
this.element.detach();
|
|
this.trigger(INIT, { layout: this });
|
|
},
|
|
_locate: function (selectors) {
|
|
return this.options.$angular ? directiveSelector(selectors) : roleSelector(selectors);
|
|
},
|
|
options: {
|
|
name: 'Layout',
|
|
id: null,
|
|
platform: null
|
|
},
|
|
events: [
|
|
INIT,
|
|
SHOW,
|
|
HIDE
|
|
],
|
|
setup: function (view) {
|
|
if (!view.header[0]) {
|
|
view.header = this.header;
|
|
}
|
|
if (!view.footer[0]) {
|
|
view.footer = this.footer;
|
|
}
|
|
},
|
|
detach: function (view) {
|
|
var that = this;
|
|
if (view.header === that.header && that.header[0]) {
|
|
view.element.prepend(that.header.detach()[0].cloneNode(true));
|
|
}
|
|
if (view.footer === that.footer && that.footer.length) {
|
|
view.element.append(that.footer.detach()[0].cloneNode(true));
|
|
}
|
|
},
|
|
attach: function (view) {
|
|
var that = this, previousView = that.currentView;
|
|
if (previousView) {
|
|
that.detach(previousView);
|
|
}
|
|
if (view.header === that.header) {
|
|
that.header.detach();
|
|
view.element.children(roleSelector('header')).remove();
|
|
view.element.prepend(that.header);
|
|
}
|
|
if (view.footer === that.footer) {
|
|
that.footer.detach();
|
|
view.element.children(roleSelector('footer')).remove();
|
|
view.element.append(that.footer);
|
|
}
|
|
that.trigger(SHOW, {
|
|
layout: that,
|
|
view: view
|
|
});
|
|
that.currentView = view;
|
|
}
|
|
});
|
|
var Observable = kendo.Observable, bodyRegExp = /<body[^>]*>(([\u000a\u000d\u2028\u2029]|.)*)<\/body>/i, LOAD_START = 'loadStart', LOAD_COMPLETE = 'loadComplete', SHOW_START = 'showStart', SAME_VIEW_REQUESTED = 'sameViewRequested', VIEW_SHOW = 'viewShow', VIEW_TYPE_DETERMINED = 'viewTypeDetermined', AFTER = 'after';
|
|
var ViewEngine = Observable.extend({
|
|
init: function (options) {
|
|
var that = this, views, errorMessage, container, collection;
|
|
Observable.fn.init.call(that);
|
|
$.extend(that, options);
|
|
that.sandbox = $('<div />');
|
|
container = that.container;
|
|
views = that._hideViews(container);
|
|
that.rootView = views.first();
|
|
if (!that.rootView[0] && options.rootNeeded) {
|
|
if (container[0] == kendo.mobile.application.element[0]) {
|
|
errorMessage = 'Your kendo mobile application element does not contain any direct child elements with data-role="view" attribute set. Make sure that you instantiate the mobile application using the correct container.';
|
|
} else {
|
|
errorMessage = 'Your pane element does not contain any direct child elements with data-role="view" attribute set.';
|
|
}
|
|
throw new Error(errorMessage);
|
|
}
|
|
that.layouts = {};
|
|
that.viewContainer = new kendo.ViewContainer(that.container);
|
|
that.viewContainer.bind('accepted', function (e) {
|
|
e.view.params = that.params;
|
|
});
|
|
that.viewContainer.bind('complete', function (e) {
|
|
that.trigger(VIEW_SHOW, { view: e.view });
|
|
});
|
|
that.viewContainer.bind(AFTER, function () {
|
|
that.trigger(AFTER);
|
|
});
|
|
this.getLayoutProxy = $.proxy(this, '_getLayout');
|
|
that._setupLayouts(container);
|
|
collection = container.children(that._locate('modalview drawer'));
|
|
if (that.$angular) {
|
|
that.$angular[0].viewOptions = {
|
|
defaultTransition: that.transition,
|
|
loader: that.loader,
|
|
container: that.container,
|
|
getLayout: that.getLayoutProxy
|
|
};
|
|
collection.each(function (idx, element) {
|
|
compileMobileDirective($(element), options.$angular[0]);
|
|
});
|
|
} else {
|
|
initWidgets(collection);
|
|
}
|
|
this.bind(this.events, options);
|
|
},
|
|
events: [
|
|
SHOW_START,
|
|
AFTER,
|
|
VIEW_SHOW,
|
|
LOAD_START,
|
|
LOAD_COMPLETE,
|
|
SAME_VIEW_REQUESTED,
|
|
VIEW_TYPE_DETERMINED
|
|
],
|
|
destroy: function () {
|
|
kendo.destroy(this.container);
|
|
for (var id in this.layouts) {
|
|
this.layouts[id].destroy();
|
|
}
|
|
},
|
|
view: function () {
|
|
return this.viewContainer.view;
|
|
},
|
|
showView: function (url, transition, params) {
|
|
url = url.replace(new RegExp('^' + this.remoteViewURLPrefix), '');
|
|
if (url === '' && this.remoteViewURLPrefix) {
|
|
url = '/';
|
|
}
|
|
if (url.replace(/^#/, '') === this.url) {
|
|
this.trigger(SAME_VIEW_REQUESTED);
|
|
return false;
|
|
}
|
|
this.trigger(SHOW_START);
|
|
var that = this, showClosure = function (view) {
|
|
return that.viewContainer.show(view, transition, url);
|
|
}, element = that._findViewElement(url), view = kendo.widgetInstance(element);
|
|
that.url = url.replace(/^#/, '');
|
|
that.params = params;
|
|
if (view && view.reload) {
|
|
view.purge();
|
|
element = [];
|
|
}
|
|
this.trigger(VIEW_TYPE_DETERMINED, {
|
|
remote: element.length === 0,
|
|
url: url
|
|
});
|
|
if (element[0]) {
|
|
if (!view) {
|
|
view = that._createView(element);
|
|
}
|
|
return showClosure(view);
|
|
} else {
|
|
if (this.serverNavigation) {
|
|
location.href = url;
|
|
} else {
|
|
that._loadView(url, showClosure);
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
append: function (html, url) {
|
|
var sandbox = this.sandbox, urlPath = (url || '').split('?')[0], container = this.container, views, modalViews, view;
|
|
if (bodyRegExp.test(html)) {
|
|
html = RegExp.$1;
|
|
}
|
|
sandbox[0].innerHTML = html;
|
|
container.append(sandbox.children('script, style'));
|
|
views = this._hideViews(sandbox);
|
|
view = views.first();
|
|
if (!view.length) {
|
|
views = view = sandbox.wrapInner('<div data-role=view />').children();
|
|
}
|
|
if (urlPath) {
|
|
view.hide().attr(attr('url'), urlPath);
|
|
}
|
|
this._setupLayouts(sandbox);
|
|
modalViews = sandbox.children(this._locate('modalview drawer'));
|
|
container.append(sandbox.children(this._locate('layout modalview drawer')).add(views));
|
|
initWidgets(modalViews);
|
|
return this._createView(view);
|
|
},
|
|
_locate: function (selectors) {
|
|
return this.$angular ? directiveSelector(selectors) : roleSelector(selectors);
|
|
},
|
|
_findViewElement: function (url) {
|
|
var element, urlPath = url.split('?')[0];
|
|
if (!urlPath) {
|
|
return this.rootView;
|
|
}
|
|
element = this.container.children('[' + attr('url') + '=\'' + urlPath + '\']');
|
|
if (!element[0] && urlPath.indexOf('/') === -1) {
|
|
element = this.container.children(urlPath.charAt(0) === '#' ? urlPath : '#' + urlPath);
|
|
}
|
|
return element;
|
|
},
|
|
_createView: function (element) {
|
|
if (this.$angular) {
|
|
return compileMobileDirective(element, this.$angular[0]);
|
|
} else {
|
|
return kendo.initWidget(element, {
|
|
defaultTransition: this.transition,
|
|
loader: this.loader,
|
|
container: this.container,
|
|
getLayout: this.getLayoutProxy,
|
|
modelScope: this.modelScope,
|
|
reload: attrValue(element, 'reload')
|
|
}, ui.roles);
|
|
}
|
|
},
|
|
_getLayout: function (name) {
|
|
if (name === '') {
|
|
return null;
|
|
}
|
|
return name ? this.layouts[name] : this.layouts[this.layout];
|
|
},
|
|
_loadView: function (url, callback) {
|
|
if (this._xhr) {
|
|
this._xhr.abort();
|
|
}
|
|
this.trigger(LOAD_START);
|
|
this._xhr = $.get(kendo.absoluteURL(url, this.remoteViewURLPrefix), 'html').always($.proxy(this, '_xhrComplete', callback, url));
|
|
},
|
|
_xhrComplete: function (callback, url, response) {
|
|
var success = true;
|
|
if (typeof response === 'object') {
|
|
if (response.status === 0) {
|
|
if (response.responseText && response.responseText.length > 0) {
|
|
success = true;
|
|
response = response.responseText;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
this.trigger(LOAD_COMPLETE);
|
|
if (success) {
|
|
callback(this.append(response, url));
|
|
}
|
|
},
|
|
_hideViews: function (container) {
|
|
return container.children(this._locate('view splitview')).hide();
|
|
},
|
|
_setupLayouts: function (element) {
|
|
var that = this, layout;
|
|
element.children(that._locate('layout')).each(function () {
|
|
if (that.$angular) {
|
|
layout = compileMobileDirective($(this), that.$angular[0]);
|
|
} else {
|
|
layout = kendo.initWidget($(this), {}, ui.roles);
|
|
}
|
|
var platform = layout.options.platform;
|
|
if (!platform || platform === mobile.application.os.name) {
|
|
that.layouts[layout.options.id] = layout;
|
|
} else {
|
|
layout.destroy();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.mobile.ViewEngine = ViewEngine;
|
|
ui.plugin(View);
|
|
ui.plugin(Layout);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.loader', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.loader',
|
|
name: 'Loader',
|
|
category: 'mobile',
|
|
description: 'Mobile Loader',
|
|
depends: ['core'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, CAPTURE_EVENTS = $.map(kendo.eventMap, function (value) {
|
|
return value;
|
|
}).join(' ').split(' ');
|
|
var Loader = Widget.extend({
|
|
init: function (container, options) {
|
|
var that = this, element = $('<div class="km-loader"><span class="km-loading km-spin"></span><span class="km-loading-left"></span><span class="km-loading-right"></span></div>');
|
|
Widget.fn.init.call(that, element, options);
|
|
that.container = container;
|
|
that.captureEvents = false;
|
|
that._attachCapture();
|
|
element.append(that.options.loading).hide().appendTo(container);
|
|
},
|
|
options: {
|
|
name: 'Loader',
|
|
loading: '<h1>Loading...</h1>',
|
|
timeout: 100
|
|
},
|
|
show: function () {
|
|
var that = this;
|
|
clearTimeout(that._loading);
|
|
if (that.options.loading === false) {
|
|
return;
|
|
}
|
|
that.captureEvents = true;
|
|
that._loading = setTimeout(function () {
|
|
that.element.show();
|
|
}, that.options.timeout);
|
|
},
|
|
hide: function () {
|
|
this.captureEvents = false;
|
|
clearTimeout(this._loading);
|
|
this.element.hide();
|
|
},
|
|
changeMessage: function (message) {
|
|
this.options.loading = message;
|
|
this.element.find('>h1').html(message);
|
|
},
|
|
transition: function () {
|
|
this.captureEvents = true;
|
|
this.container.css('pointer-events', 'none');
|
|
},
|
|
transitionDone: function () {
|
|
this.captureEvents = false;
|
|
this.container.css('pointer-events', '');
|
|
},
|
|
_attachCapture: function () {
|
|
var that = this;
|
|
that.captureEvents = false;
|
|
function capture(e) {
|
|
if (that.captureEvents) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
for (var i = 0; i < CAPTURE_EVENTS.length; i++) {
|
|
that.container[0].addEventListener(CAPTURE_EVENTS[i], capture, true);
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Loader);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.pane', [
|
|
'kendo.mobile.view',
|
|
'kendo.mobile.loader'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.pane',
|
|
name: 'Pane',
|
|
category: 'mobile',
|
|
description: 'Mobile Pane',
|
|
depends: [
|
|
'mobile.view',
|
|
'mobile.loader'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, roleSelector = kendo.roleSelector, ui = mobile.ui, Widget = ui.Widget, ViewEngine = mobile.ViewEngine, View = ui.View, Loader = mobile.ui.Loader, EXTERNAL = 'external', HREF = 'href', DUMMY_HREF = '#!', NAVIGATE = 'navigate', VIEW_SHOW = 'viewShow', SAME_VIEW_REQUESTED = 'sameViewRequested', OS = kendo.support.mobileOS, SKIP_TRANSITION_ON_BACK_BUTTON = OS.ios && !OS.appMode && OS.flatVersion >= 700, WIDGET_RELS = /popover|actionsheet|modalview|drawer/, BACK = '#:back', attrValue = kendo.attrValue;
|
|
var Pane = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element;
|
|
element.addClass('km-pane');
|
|
if (that.options.collapsible) {
|
|
element.addClass('km-collapsible-pane');
|
|
}
|
|
this.history = [];
|
|
this.historyCallback = function (url, params, backButtonPressed) {
|
|
var transition = that.transition;
|
|
that.transition = null;
|
|
if (SKIP_TRANSITION_ON_BACK_BUTTON && backButtonPressed) {
|
|
transition = 'none';
|
|
}
|
|
return that.viewEngine.showView(url, transition, params);
|
|
};
|
|
this._historyNavigate = function (url) {
|
|
if (url === BACK) {
|
|
if (that.history.length === 1) {
|
|
return;
|
|
}
|
|
that.history.pop();
|
|
url = that.history[that.history.length - 1];
|
|
} else {
|
|
that.history.push(url);
|
|
}
|
|
that.historyCallback(url, kendo.parseQueryStringParams(url));
|
|
};
|
|
this._historyReplace = function (url) {
|
|
var params = kendo.parseQueryStringParams(url);
|
|
that.history[that.history.length - 1] = url;
|
|
that.historyCallback(url, params);
|
|
};
|
|
that.loader = new Loader(element, { loading: that.options.loading });
|
|
that.viewEngine = new ViewEngine({
|
|
container: element,
|
|
transition: options.transition,
|
|
modelScope: options.modelScope,
|
|
rootNeeded: !options.initial,
|
|
serverNavigation: options.serverNavigation,
|
|
remoteViewURLPrefix: options.root || '',
|
|
layout: options.layout,
|
|
$angular: options.$angular,
|
|
loader: that.loader,
|
|
showStart: function () {
|
|
that.loader.transition();
|
|
that.closeActiveDialogs();
|
|
},
|
|
after: function () {
|
|
that.loader.transitionDone();
|
|
},
|
|
viewShow: function (e) {
|
|
that.trigger(VIEW_SHOW, e);
|
|
},
|
|
loadStart: function () {
|
|
that.loader.show();
|
|
},
|
|
loadComplete: function () {
|
|
that.loader.hide();
|
|
},
|
|
sameViewRequested: function () {
|
|
that.trigger(SAME_VIEW_REQUESTED);
|
|
},
|
|
viewTypeDetermined: function (e) {
|
|
if (!e.remote || !that.options.serverNavigation) {
|
|
that.trigger(NAVIGATE, { url: e.url });
|
|
}
|
|
}
|
|
});
|
|
this._setPortraitWidth();
|
|
kendo.onResize(function () {
|
|
that._setPortraitWidth();
|
|
});
|
|
that._setupAppLinks();
|
|
},
|
|
closeActiveDialogs: function () {
|
|
var dialogs = this.element.find(roleSelector('actionsheet popover modalview')).filter(':visible');
|
|
dialogs.each(function () {
|
|
kendo.widgetInstance($(this), ui).close();
|
|
});
|
|
},
|
|
navigateToInitial: function () {
|
|
var initial = this.options.initial;
|
|
if (initial) {
|
|
this.navigate(initial);
|
|
}
|
|
return initial;
|
|
},
|
|
options: {
|
|
name: 'Pane',
|
|
portraitWidth: '',
|
|
transition: '',
|
|
layout: '',
|
|
collapsible: false,
|
|
initial: null,
|
|
modelScope: window,
|
|
loading: '<h1>Loading...</h1>'
|
|
},
|
|
events: [
|
|
NAVIGATE,
|
|
VIEW_SHOW,
|
|
SAME_VIEW_REQUESTED
|
|
],
|
|
append: function (html) {
|
|
return this.viewEngine.append(html);
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.viewEngine.destroy();
|
|
this.userEvents.destroy();
|
|
},
|
|
navigate: function (url, transition) {
|
|
if (url instanceof View) {
|
|
url = url.id;
|
|
}
|
|
this.transition = transition;
|
|
this._historyNavigate(url);
|
|
},
|
|
replace: function (url, transition) {
|
|
if (url instanceof View) {
|
|
url = url.id;
|
|
}
|
|
this.transition = transition;
|
|
this._historyReplace(url);
|
|
},
|
|
bindToRouter: function (router) {
|
|
var that = this, history = this.history, viewEngine = this.viewEngine;
|
|
router.bind('init', function (e) {
|
|
var url = e.url, attrUrl = router.pushState ? url : '/';
|
|
viewEngine.rootView.attr(kendo.attr('url'), attrUrl);
|
|
var length = history.length;
|
|
if (url === '/' && length) {
|
|
router.navigate(history[length - 1], true);
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
router.bind('routeMissing', function (e) {
|
|
if (!that.historyCallback(e.url, e.params, e.backButtonPressed)) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
router.bind('same', function () {
|
|
that.trigger(SAME_VIEW_REQUESTED);
|
|
});
|
|
that._historyNavigate = function (url) {
|
|
router.navigate(url);
|
|
};
|
|
that._historyReplace = function (url) {
|
|
router.replace(url);
|
|
};
|
|
},
|
|
hideLoading: function () {
|
|
this.loader.hide();
|
|
},
|
|
showLoading: function () {
|
|
this.loader.show();
|
|
},
|
|
changeLoadingMessage: function (message) {
|
|
this.loader.changeMessage(message);
|
|
},
|
|
view: function () {
|
|
return this.viewEngine.view();
|
|
},
|
|
_setPortraitWidth: function () {
|
|
var width, portraitWidth = this.options.portraitWidth;
|
|
if (portraitWidth) {
|
|
width = kendo.mobile.application.element.is('.km-vertical') ? portraitWidth : 'auto';
|
|
this.element.css('width', width);
|
|
}
|
|
},
|
|
_setupAppLinks: function () {
|
|
var that = this, linkRoles = 'tab', pressedButtonSelector = '[data-' + kendo.ns + 'navigate-on-press]', buttonSelectors = $.map([
|
|
'button',
|
|
'backbutton',
|
|
'detailbutton',
|
|
'listview-link'
|
|
], function (role) {
|
|
return roleSelector(role) + ':not(' + pressedButtonSelector + ')';
|
|
}).join(',');
|
|
this.element.handler(this).on('down', roleSelector(linkRoles) + ',' + pressedButtonSelector, '_mouseup').on('click', roleSelector(linkRoles) + ',' + buttonSelectors + ',' + pressedButtonSelector, '_appLinkClick');
|
|
this.userEvents = new kendo.UserEvents(this.element, {
|
|
fastTap: true,
|
|
filter: buttonSelectors,
|
|
tap: function (e) {
|
|
e.event.currentTarget = e.touch.currentTarget;
|
|
that._mouseup(e.event);
|
|
}
|
|
});
|
|
this.element.css('-ms-touch-action', '');
|
|
},
|
|
_appLinkClick: function (e) {
|
|
var href = $(e.currentTarget).attr('href');
|
|
var remote = href && href[0] !== '#' && this.options.serverNavigation;
|
|
if (!remote && attrValue($(e.currentTarget), 'rel') != EXTERNAL) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_mouseup: function (e) {
|
|
if (e.which > 1 || e.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
var pane = this, link = $(e.currentTarget), transition = attrValue(link, 'transition'), rel = attrValue(link, 'rel') || '', target = attrValue(link, 'target'), href = link.attr(HREF), delayedTouchEnd = SKIP_TRANSITION_ON_BACK_BUTTON && link[0].offsetHeight === 0, remote = href && href[0] !== '#' && this.options.serverNavigation;
|
|
if (delayedTouchEnd || remote || rel === EXTERNAL || typeof href === 'undefined' || href === DUMMY_HREF) {
|
|
return;
|
|
}
|
|
link.attr(HREF, DUMMY_HREF);
|
|
setTimeout(function () {
|
|
link.attr(HREF, href);
|
|
});
|
|
if (rel.match(WIDGET_RELS)) {
|
|
kendo.widgetInstance($(href), ui).openFor(link);
|
|
if (rel === 'actionsheet' || rel === 'drawer') {
|
|
e.stopPropagation();
|
|
}
|
|
} else {
|
|
if (target === '_top') {
|
|
pane = mobile.application.pane;
|
|
} else if (target) {
|
|
pane = $('#' + target).data('kendoMobilePane');
|
|
}
|
|
pane.navigate(href, transition);
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
Pane.wrap = function (element) {
|
|
if (!element.is(roleSelector('view'))) {
|
|
element = element.wrap('<div data-' + kendo.ns + 'role="view" data-stretch="true"></div>').parent();
|
|
}
|
|
var paneContainer = element.wrap('<div class="km-pane-wrapper"><div></div></div>').parent(), pane = new Pane(paneContainer);
|
|
pane.navigate('');
|
|
return pane;
|
|
};
|
|
ui.plugin(Pane);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.popover', [
|
|
'kendo.popup',
|
|
'kendo.mobile.pane'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.popover',
|
|
name: 'PopOver',
|
|
category: 'mobile',
|
|
description: 'The mobile PopOver widget represents a transient view which is displayed when the user taps on a navigational widget or area on the screen. ',
|
|
depends: [
|
|
'popup',
|
|
'mobile.pane'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, HIDE = 'hide', OPEN = 'open', CLOSE = 'close', WRAPPER = '<div class="km-popup-wrapper" />', ARROW = '<div class="km-popup-arrow" />', OVERLAY = '<div class="km-popup-overlay" />', DIRECTION_CLASSES = 'km-up km-down km-left km-right', Widget = ui.Widget, DIRECTIONS = {
|
|
'down': {
|
|
origin: 'bottom center',
|
|
position: 'top center'
|
|
},
|
|
'up': {
|
|
origin: 'top center',
|
|
position: 'bottom center'
|
|
},
|
|
'left': {
|
|
origin: 'center left',
|
|
position: 'center right',
|
|
collision: 'fit flip'
|
|
},
|
|
'right': {
|
|
origin: 'center right',
|
|
position: 'center left',
|
|
collision: 'fit flip'
|
|
}
|
|
}, ANIMATION = {
|
|
animation: {
|
|
open: {
|
|
effects: 'fade:in',
|
|
duration: 0
|
|
},
|
|
close: {
|
|
effects: 'fade:out',
|
|
duration: 400
|
|
}
|
|
}
|
|
}, DIMENSIONS = {
|
|
'horizontal': {
|
|
offset: 'top',
|
|
size: 'height'
|
|
},
|
|
'vertical': {
|
|
offset: 'left',
|
|
size: 'width'
|
|
}
|
|
}, REVERSE = {
|
|
'up': 'down',
|
|
'down': 'up',
|
|
'left': 'right',
|
|
'right': 'left'
|
|
};
|
|
var Popup = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, containerPopup = element.closest('.km-modalview-wrapper'), viewport = element.closest('.km-root').children('.km-pane').first(), container = containerPopup[0] ? containerPopup : viewport, popupOptions, axis;
|
|
if (options.viewport) {
|
|
viewport = options.viewport;
|
|
} else if (!viewport[0]) {
|
|
viewport = window;
|
|
}
|
|
if (options.container) {
|
|
container = options.container;
|
|
} else if (!container[0]) {
|
|
container = document.body;
|
|
}
|
|
popupOptions = {
|
|
viewport: viewport,
|
|
copyAnchorStyles: false,
|
|
autosize: true,
|
|
open: function () {
|
|
that.overlay.show();
|
|
},
|
|
activate: $.proxy(that._activate, that),
|
|
deactivate: function () {
|
|
that.overlay.hide();
|
|
if (!that._apiCall) {
|
|
that.trigger(HIDE);
|
|
}
|
|
that._apiCall = false;
|
|
}
|
|
};
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
element.wrap(WRAPPER).addClass('km-popup').show();
|
|
axis = that.options.direction.match(/left|right/) ? 'horizontal' : 'vertical';
|
|
that.dimensions = DIMENSIONS[axis];
|
|
that.wrapper = element.parent().css({
|
|
width: options.width,
|
|
height: options.height
|
|
}).addClass('km-popup-wrapper km-' + options.direction).hide();
|
|
that.arrow = $(ARROW).prependTo(that.wrapper).hide();
|
|
that.overlay = $(OVERLAY).appendTo(container).hide();
|
|
popupOptions.appendTo = that.overlay;
|
|
if (options.className) {
|
|
that.overlay.addClass(options.className);
|
|
}
|
|
that.popup = new kendo.ui.Popup(that.wrapper, $.extend(true, popupOptions, ANIMATION, DIRECTIONS[options.direction]));
|
|
},
|
|
options: {
|
|
name: 'Popup',
|
|
width: 240,
|
|
height: '',
|
|
direction: 'down',
|
|
container: null,
|
|
viewport: null
|
|
},
|
|
events: [HIDE],
|
|
show: function (target) {
|
|
this.popup.options.anchor = $(target);
|
|
this.popup.open();
|
|
},
|
|
hide: function () {
|
|
this._apiCall = true;
|
|
this.popup.close();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.popup.destroy();
|
|
this.overlay.remove();
|
|
},
|
|
target: function () {
|
|
return this.popup.options.anchor;
|
|
},
|
|
_activate: function () {
|
|
var that = this, direction = that.options.direction, dimensions = that.dimensions, offset = dimensions.offset, popup = that.popup, anchor = popup.options.anchor, anchorOffset = $(anchor).offset(), elementOffset = $(popup.element).offset(), cssClass = popup.flipped ? REVERSE[direction] : direction, min = that.arrow[dimensions.size]() * 2, max = that.element[dimensions.size]() - that.arrow[dimensions.size](), size = $(anchor)[dimensions.size](), offsetAmount = anchorOffset[offset] - elementOffset[offset] + size / 2;
|
|
if (offsetAmount < min) {
|
|
offsetAmount = min;
|
|
}
|
|
if (offsetAmount > max) {
|
|
offsetAmount = max;
|
|
}
|
|
that.wrapper.removeClass(DIRECTION_CLASSES).addClass('km-' + cssClass);
|
|
that.arrow.css(offset, offsetAmount).show();
|
|
}
|
|
});
|
|
var PopOver = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, popupOptions;
|
|
that.initialOpen = false;
|
|
Widget.fn.init.call(that, element, options);
|
|
popupOptions = $.extend({
|
|
className: 'km-popover-root',
|
|
hide: function () {
|
|
that.trigger(CLOSE);
|
|
}
|
|
}, this.options.popup);
|
|
that.popup = new Popup(that.element, popupOptions);
|
|
that.popup.overlay.on('move', function (e) {
|
|
if (e.target == that.popup.overlay[0]) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
that.pane = new ui.Pane(that.element, $.extend(this.options.pane, { $angular: this.options.$angular }));
|
|
kendo.notify(that, ui);
|
|
},
|
|
options: {
|
|
name: 'PopOver',
|
|
popup: {},
|
|
pane: {}
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE
|
|
],
|
|
open: function (target) {
|
|
this.popup.show(target);
|
|
if (!this.initialOpen) {
|
|
if (!this.pane.navigateToInitial()) {
|
|
this.pane.navigate('');
|
|
}
|
|
this.popup.popup._position();
|
|
this.initialOpen = true;
|
|
} else {
|
|
this.pane.view()._invokeNgController();
|
|
}
|
|
},
|
|
openFor: function (target) {
|
|
this.open(target);
|
|
this.trigger(OPEN, { target: this.popup.target() });
|
|
},
|
|
close: function () {
|
|
this.popup.hide();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.pane.destroy();
|
|
this.popup.destroy();
|
|
kendo.destroy(this.element);
|
|
}
|
|
});
|
|
ui.plugin(Popup);
|
|
ui.plugin(PopOver);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.shim', ['kendo.popup'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.shim',
|
|
name: 'Shim',
|
|
category: 'mobile',
|
|
description: 'Mobile Shim',
|
|
depends: ['popup'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Popup = kendo.ui.Popup, SHIM = '<div class="km-shim"/>', HIDE = 'hide', Widget = ui.Widget;
|
|
var Shim = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, app = kendo.mobile.application, os = kendo.support.mobileOS, osname = app ? app.os.name : os ? os.name : 'ios', ioswp = osname === 'ios' || osname === 'wp' || (app ? app.os.skin : false), bb = osname === 'blackberry', align = options.align || (ioswp ? 'bottom center' : bb ? 'center right' : 'center center'), position = options.position || (ioswp ? 'bottom center' : bb ? 'center right' : 'center center'), effect = options.effect || (ioswp ? 'slideIn:up' : bb ? 'slideIn:left' : 'fade:in'), shim = $(SHIM).handler(that).hide();
|
|
Widget.fn.init.call(that, element, options);
|
|
that.shim = shim;
|
|
element = that.element;
|
|
options = that.options;
|
|
if (options.className) {
|
|
that.shim.addClass(options.className);
|
|
}
|
|
if (!options.modal) {
|
|
that.shim.on('down', '_hide');
|
|
}
|
|
(app ? app.element : $(document.body)).append(shim);
|
|
that.popup = new Popup(that.element, {
|
|
anchor: shim,
|
|
modal: true,
|
|
appendTo: shim,
|
|
origin: align,
|
|
position: position,
|
|
animation: {
|
|
open: {
|
|
effects: effect,
|
|
duration: options.duration
|
|
},
|
|
close: { duration: options.duration }
|
|
},
|
|
close: function (e) {
|
|
var prevented = false;
|
|
if (!that._apiCall) {
|
|
prevented = that.trigger(HIDE);
|
|
}
|
|
if (prevented) {
|
|
e.preventDefault();
|
|
}
|
|
that._apiCall = false;
|
|
},
|
|
deactivate: function () {
|
|
shim.hide();
|
|
},
|
|
open: function () {
|
|
shim.show();
|
|
}
|
|
});
|
|
kendo.notify(that);
|
|
},
|
|
events: [HIDE],
|
|
options: {
|
|
name: 'Shim',
|
|
modal: false,
|
|
align: undefined,
|
|
position: undefined,
|
|
effect: undefined,
|
|
duration: 200
|
|
},
|
|
show: function () {
|
|
this.popup.open();
|
|
},
|
|
hide: function () {
|
|
this._apiCall = true;
|
|
this.popup.close();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.shim.kendoDestroy();
|
|
this.popup.destroy();
|
|
this.shim.remove();
|
|
},
|
|
_hide: function (e) {
|
|
if (!e || !$.contains(this.shim.children().children('.k-popup')[0], e.target)) {
|
|
this.popup.close();
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Shim);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.actionsheet', [
|
|
'kendo.mobile.popover',
|
|
'kendo.mobile.shim'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.actionsheet',
|
|
name: 'ActionSheet',
|
|
category: 'mobile',
|
|
description: 'The mobile ActionSheet widget displays a set of choices related to a task the user initiates.',
|
|
depends: [
|
|
'mobile.popover',
|
|
'mobile.shim'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, support = kendo.support, ui = kendo.mobile.ui, Shim = ui.Shim, Popup = ui.Popup, Widget = ui.Widget, OPEN = 'open', CLOSE = 'close', COMMAND = 'command', BUTTONS = 'li>a', CONTEXT_DATA = 'actionsheetContext', WRAP = '<div class="km-actionsheet-wrapper" />', cancelTemplate = kendo.template('<li class="km-actionsheet-cancel"><a href="\\#">#:cancel#</a></li>');
|
|
var ActionSheet = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, ShimClass, tablet, type, os = support.mobileOS;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
type = options.type;
|
|
element = that.element;
|
|
if (type === 'auto') {
|
|
tablet = os && os.tablet;
|
|
} else {
|
|
tablet = type === 'tablet';
|
|
}
|
|
ShimClass = tablet ? Popup : Shim;
|
|
if (options.cancelTemplate) {
|
|
cancelTemplate = kendo.template(options.cancelTemplate);
|
|
}
|
|
element.addClass('km-actionsheet').append(cancelTemplate({ cancel: that.options.cancel })).wrap(WRAP).on('up', BUTTONS, '_click').on('click', BUTTONS, kendo.preventDefault);
|
|
that.view().bind('destroy', function () {
|
|
that.destroy();
|
|
});
|
|
that.wrapper = element.parent().addClass(type ? ' km-actionsheet-' + type : '');
|
|
that.shim = new ShimClass(that.wrapper, $.extend({
|
|
modal: os.ios && os.majorVersion < 7,
|
|
className: 'km-actionsheet-root'
|
|
}, that.options.popup));
|
|
that._closeProxy = $.proxy(that, '_close');
|
|
that._shimHideProxy = $.proxy(that, '_shimHide');
|
|
that.shim.bind('hide', that._shimHideProxy);
|
|
if (tablet) {
|
|
kendo.onResize(that._closeProxy);
|
|
}
|
|
kendo.notify(that, ui);
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
COMMAND
|
|
],
|
|
options: {
|
|
name: 'ActionSheet',
|
|
cancel: 'Cancel',
|
|
type: 'auto',
|
|
popup: { height: 'auto' }
|
|
},
|
|
open: function (target, context) {
|
|
var that = this;
|
|
that.target = $(target);
|
|
that.context = context;
|
|
that.shim.show(target);
|
|
},
|
|
close: function () {
|
|
this.context = this.target = null;
|
|
this.shim.hide();
|
|
},
|
|
openFor: function (target) {
|
|
var that = this, context = target.data(CONTEXT_DATA);
|
|
that.open(target, context);
|
|
that.trigger(OPEN, {
|
|
target: target,
|
|
context: context
|
|
});
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.unbindResize(this._closeProxy);
|
|
this.shim.destroy();
|
|
},
|
|
_click: function (e) {
|
|
if (e.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
var currentTarget = $(e.currentTarget);
|
|
var action = currentTarget.data('action');
|
|
if (action) {
|
|
var actionData = {
|
|
target: this.target,
|
|
context: this.context
|
|
}, $angular = this.options.$angular;
|
|
if ($angular) {
|
|
this.element.injector().get('$parse')(action)($angular[0])(actionData);
|
|
} else {
|
|
kendo.getter(action)(window)(actionData);
|
|
}
|
|
}
|
|
this.trigger(COMMAND, {
|
|
target: this.target,
|
|
context: this.context,
|
|
currentTarget: currentTarget
|
|
});
|
|
e.preventDefault();
|
|
this._close();
|
|
},
|
|
_shimHide: function (e) {
|
|
if (!this.trigger(CLOSE)) {
|
|
this.context = this.target = null;
|
|
} else {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_close: function (e) {
|
|
if (!this.trigger(CLOSE)) {
|
|
this.close();
|
|
} else {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(ActionSheet);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.progressbar', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'progressbar',
|
|
name: 'ProgressBar',
|
|
category: 'web',
|
|
description: 'The ProgressBar offers rich functionality for displaying and tracking progress',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, HORIZONTAL = 'horizontal', VERTICAL = 'vertical', DEFAULTMIN = 0, DEFAULTMAX = 100, DEFAULTVALUE = 0, DEFAULTCHUNKCOUNT = 5, KPROGRESSBAR = 'k-progressbar', KPROGRESSBARREVERSE = 'k-progressbar-reverse', KPROGRESSBARINDETERMINATE = 'k-progressbar-indeterminate', KPROGRESSBARCOMPLETE = 'k-complete', KPROGRESSWRAPPER = 'k-state-selected', KPROGRESSSTATUS = 'k-progress-status', KCOMPLETEDCHUNK = 'k-state-selected', KUPCOMINGCHUNK = 'k-state-default', KSTATEDISABLED = 'k-state-disabled', PROGRESSTYPE = {
|
|
VALUE: 'value',
|
|
PERCENT: 'percent',
|
|
CHUNK: 'chunk'
|
|
}, CHANGE = 'change', COMPLETE = 'complete', BOOLEAN = 'boolean', math = Math, extend = $.extend, proxy = $.proxy, HUNDREDPERCENT = 100, DEFAULTANIMATIONDURATION = 400, PRECISION = 3, templates = { progressStatus: '<span class=\'k-progress-status-wrap\'><span class=\'k-progress-status\'></span></span>' };
|
|
var ProgressBar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(this, element, options);
|
|
options = that.options;
|
|
that._progressProperty = options.orientation === HORIZONTAL ? 'width' : 'height';
|
|
that._fields();
|
|
options.value = that._validateValue(options.value);
|
|
that._validateType(options.type);
|
|
that._wrapper();
|
|
that._progressAnimation();
|
|
if (options.value !== options.min && options.value !== false) {
|
|
that._updateProgress();
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
Widget.fn.setOptions.call(that, options);
|
|
if (options.hasOwnProperty('reverse')) {
|
|
that.wrapper.toggleClass('k-progressbar-reverse', options.reverse);
|
|
}
|
|
if (options.hasOwnProperty('enable')) {
|
|
that.enable(options.enable);
|
|
}
|
|
that._progressAnimation();
|
|
that._validateValue();
|
|
that._updateProgress();
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
COMPLETE
|
|
],
|
|
options: {
|
|
name: 'ProgressBar',
|
|
orientation: HORIZONTAL,
|
|
reverse: false,
|
|
min: DEFAULTMIN,
|
|
max: DEFAULTMAX,
|
|
value: DEFAULTVALUE,
|
|
enable: true,
|
|
type: PROGRESSTYPE.VALUE,
|
|
chunkCount: DEFAULTCHUNKCOUNT,
|
|
showStatus: true,
|
|
animation: {}
|
|
},
|
|
_fields: function () {
|
|
var that = this;
|
|
that._isStarted = false;
|
|
that.progressWrapper = that.progressStatus = $();
|
|
},
|
|
_validateType: function (currentType) {
|
|
var isValid = false;
|
|
$.each(PROGRESSTYPE, function (k, type) {
|
|
if (type === currentType) {
|
|
isValid = true;
|
|
return false;
|
|
}
|
|
});
|
|
if (!isValid) {
|
|
throw new Error(kendo.format('Invalid ProgressBar type \'{0}\'', currentType));
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var that = this;
|
|
var container = that.wrapper = that.element;
|
|
var options = that.options;
|
|
var orientation = options.orientation;
|
|
var initialStatusValue;
|
|
container.addClass('k-widget ' + KPROGRESSBAR);
|
|
container.addClass(KPROGRESSBAR + '-' + (orientation === HORIZONTAL ? HORIZONTAL : VERTICAL));
|
|
if (options.enable === false) {
|
|
container.addClass(KSTATEDISABLED);
|
|
}
|
|
if (options.reverse) {
|
|
container.addClass(KPROGRESSBARREVERSE);
|
|
}
|
|
if (options.value === false) {
|
|
container.addClass(KPROGRESSBARINDETERMINATE);
|
|
}
|
|
if (options.type === PROGRESSTYPE.CHUNK) {
|
|
that._addChunkProgressWrapper();
|
|
} else {
|
|
if (options.showStatus) {
|
|
that.progressStatus = that.wrapper.prepend(templates.progressStatus).find('.' + KPROGRESSSTATUS);
|
|
initialStatusValue = options.value !== false ? options.value : options.min;
|
|
if (options.type === PROGRESSTYPE.VALUE) {
|
|
that.progressStatus.text(initialStatusValue);
|
|
} else {
|
|
that.progressStatus.text(that._calculatePercentage(initialStatusValue).toFixed() + '%');
|
|
}
|
|
}
|
|
}
|
|
},
|
|
value: function (value) {
|
|
return this._value(value);
|
|
},
|
|
_value: function (value) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var validated;
|
|
if (value === undefined) {
|
|
return options.value;
|
|
} else {
|
|
if (typeof value !== BOOLEAN) {
|
|
value = that._roundValue(value);
|
|
if (!isNaN(value)) {
|
|
validated = that._validateValue(value);
|
|
if (validated !== options.value) {
|
|
that.wrapper.removeClass(KPROGRESSBARINDETERMINATE);
|
|
options.value = validated;
|
|
that._isStarted = true;
|
|
that._updateProgress();
|
|
}
|
|
}
|
|
} else if (!value) {
|
|
that.wrapper.addClass(KPROGRESSBARINDETERMINATE);
|
|
options.value = false;
|
|
}
|
|
}
|
|
},
|
|
_roundValue: function (value) {
|
|
value = parseFloat(value);
|
|
var power = math.pow(10, PRECISION);
|
|
return math.floor(value * power) / power;
|
|
},
|
|
_validateValue: function (value) {
|
|
var that = this;
|
|
var options = that.options;
|
|
if (value !== false) {
|
|
if (value <= options.min || value === true) {
|
|
return options.min;
|
|
} else if (value >= options.max) {
|
|
return options.max;
|
|
}
|
|
} else if (value === false) {
|
|
return false;
|
|
}
|
|
if (isNaN(that._roundValue(value))) {
|
|
return options.min;
|
|
}
|
|
return value;
|
|
},
|
|
_updateProgress: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var percentage = that._calculatePercentage();
|
|
if (options.type === PROGRESSTYPE.CHUNK) {
|
|
that._updateChunks(percentage);
|
|
that._onProgressUpdateAlways(options.value);
|
|
} else {
|
|
that._updateProgressWrapper(percentage);
|
|
}
|
|
},
|
|
_updateChunks: function (percentage) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var chunkCount = options.chunkCount;
|
|
var percentagesPerChunk = parseInt(HUNDREDPERCENT / chunkCount * 100, 10) / 100;
|
|
var percentageParsed = parseInt(percentage * 100, 10) / 100;
|
|
var completedChunksCount = math.floor(percentageParsed / percentagesPerChunk);
|
|
var completedChunks;
|
|
if (options.orientation === HORIZONTAL && !options.reverse || options.orientation === VERTICAL && options.reverse) {
|
|
completedChunks = that.wrapper.find('li.k-item:lt(' + completedChunksCount + ')');
|
|
} else {
|
|
completedChunks = that.wrapper.find('li.k-item:gt(-' + (completedChunksCount + 1) + ')');
|
|
}
|
|
that.wrapper.find('.' + KCOMPLETEDCHUNK).removeClass(KCOMPLETEDCHUNK).addClass(KUPCOMINGCHUNK);
|
|
completedChunks.removeClass(KUPCOMINGCHUNK).addClass(KCOMPLETEDCHUNK);
|
|
},
|
|
_updateProgressWrapper: function (percentage) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var progressWrapper = that.wrapper.find('.' + KPROGRESSWRAPPER);
|
|
var animationDuration = that._isStarted ? that._animation.duration : 0;
|
|
var animationCssOptions = {};
|
|
if (progressWrapper.length === 0) {
|
|
that._addRegularProgressWrapper();
|
|
}
|
|
animationCssOptions[that._progressProperty] = percentage + '%';
|
|
that.progressWrapper.animate(animationCssOptions, {
|
|
duration: animationDuration,
|
|
start: proxy(that._onProgressAnimateStart, that),
|
|
progress: proxy(that._onProgressAnimate, that),
|
|
complete: proxy(that._onProgressAnimateComplete, that, options.value),
|
|
always: proxy(that._onProgressUpdateAlways, that, options.value)
|
|
});
|
|
},
|
|
_onProgressAnimateStart: function () {
|
|
this.progressWrapper.show();
|
|
},
|
|
_onProgressAnimate: function (e) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var progressInPercent = parseFloat(e.elem.style[that._progressProperty], 10);
|
|
var progressStatusWrapSize;
|
|
if (options.showStatus) {
|
|
progressStatusWrapSize = 10000 / parseFloat(that.progressWrapper[0].style[that._progressProperty]);
|
|
that.progressWrapper.find('.k-progress-status-wrap').css(that._progressProperty, progressStatusWrapSize + '%');
|
|
}
|
|
if (options.type !== PROGRESSTYPE.CHUNK && progressInPercent <= 98) {
|
|
that.progressWrapper.removeClass(KPROGRESSBARCOMPLETE);
|
|
}
|
|
},
|
|
_onProgressAnimateComplete: function (currentValue) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var progressWrapperSize = parseFloat(that.progressWrapper[0].style[that._progressProperty]);
|
|
var progressValue;
|
|
if (options.type !== PROGRESSTYPE.CHUNK && progressWrapperSize > 98) {
|
|
that.progressWrapper.addClass(KPROGRESSBARCOMPLETE);
|
|
}
|
|
if (options.showStatus) {
|
|
if (options.type === PROGRESSTYPE.VALUE) {
|
|
progressValue = currentValue;
|
|
} else if (options.type == PROGRESSTYPE.PERCENT) {
|
|
progressValue = that._calculatePercentage(currentValue).toFixed() + '%';
|
|
} else {
|
|
progressValue = math.floor(that._calculatePercentage(currentValue)) + '%';
|
|
}
|
|
that.progressStatus.text(progressValue);
|
|
}
|
|
if (currentValue === options.min) {
|
|
that.progressWrapper.hide();
|
|
}
|
|
},
|
|
_onProgressUpdateAlways: function (currentValue) {
|
|
var that = this;
|
|
var options = that.options;
|
|
if (that._isStarted) {
|
|
that.trigger(CHANGE, { value: currentValue });
|
|
}
|
|
if (currentValue === options.max && that._isStarted) {
|
|
that.trigger(COMPLETE, { value: options.max });
|
|
}
|
|
},
|
|
enable: function (enable) {
|
|
var that = this;
|
|
var options = that.options;
|
|
options.enable = typeof enable === 'undefined' ? true : enable;
|
|
that.wrapper.toggleClass(KSTATEDISABLED, !options.enable);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
_addChunkProgressWrapper: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var container = that.wrapper;
|
|
var chunkSize = HUNDREDPERCENT / options.chunkCount;
|
|
var html = '';
|
|
if (options.chunkCount <= 1) {
|
|
options.chunkCount = 1;
|
|
}
|
|
html += '<ul class=\'k-reset\'>';
|
|
for (var i = options.chunkCount - 1; i >= 0; i--) {
|
|
html += '<li class=\'k-item k-state-default\'></li>';
|
|
}
|
|
html += '</ul>';
|
|
container.append(html).find('.k-item').css(that._progressProperty, chunkSize + '%').first().addClass('k-first').end().last().addClass('k-last');
|
|
that._normalizeChunkSize();
|
|
},
|
|
_normalizeChunkSize: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var lastChunk = that.wrapper.find('.k-item:last');
|
|
var currentSize = parseFloat(lastChunk[0].style[that._progressProperty]);
|
|
var difference = HUNDREDPERCENT - options.chunkCount * currentSize;
|
|
if (difference > 0) {
|
|
lastChunk.css(that._progressProperty, currentSize + difference + '%');
|
|
}
|
|
},
|
|
_addRegularProgressWrapper: function () {
|
|
var that = this;
|
|
that.progressWrapper = $('<div class=\'' + KPROGRESSWRAPPER + '\'></div>').appendTo(that.wrapper);
|
|
if (that.options.showStatus) {
|
|
that.progressWrapper.append(templates.progressStatus);
|
|
that.progressStatus = that.wrapper.find('.' + KPROGRESSSTATUS);
|
|
}
|
|
},
|
|
_calculateChunkSize: function () {
|
|
var that = this;
|
|
var chunkCount = that.options.chunkCount;
|
|
var chunkContainer = that.wrapper.find('ul.k-reset');
|
|
return (parseInt(chunkContainer.css(that._progressProperty), 10) - (chunkCount - 1)) / chunkCount;
|
|
},
|
|
_calculatePercentage: function (currentValue) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var value = currentValue !== undefined ? currentValue : options.value;
|
|
var min = options.min;
|
|
var max = options.max;
|
|
that._onePercent = math.abs((max - min) / 100);
|
|
return math.abs((value - min) / that._onePercent);
|
|
},
|
|
_progressAnimation: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var animation = options.animation;
|
|
if (animation === false) {
|
|
that._animation = { duration: 0 };
|
|
} else {
|
|
that._animation = extend({ duration: DEFAULTANIMATIONDURATION }, options.animation);
|
|
}
|
|
}
|
|
});
|
|
kendo.ui.plugin(ProgressBar);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('pdf/core', [
|
|
'kendo.core',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
(function (window, parseFloat, undefined) {
|
|
'use strict';
|
|
var kendo = window.kendo;
|
|
var HAS_TYPED_ARRAYS = !!window.Uint8Array;
|
|
var NL = '\n';
|
|
var RESOURCE_COUNTER = 0;
|
|
var BASE64 = function () {
|
|
var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
return {
|
|
decode: function (str) {
|
|
var input = str.replace(/[^A-Za-z0-9\+\/\=]/g, ''), i = 0, n = input.length, output = [];
|
|
while (i < n) {
|
|
var enc1 = keyStr.indexOf(input.charAt(i++));
|
|
var enc2 = keyStr.indexOf(input.charAt(i++));
|
|
var enc3 = keyStr.indexOf(input.charAt(i++));
|
|
var enc4 = keyStr.indexOf(input.charAt(i++));
|
|
var chr1 = enc1 << 2 | enc2 >>> 4;
|
|
var chr2 = (enc2 & 15) << 4 | enc3 >>> 2;
|
|
var chr3 = (enc3 & 3) << 6 | enc4;
|
|
output.push(chr1);
|
|
if (enc3 != 64) {
|
|
output.push(chr2);
|
|
}
|
|
if (enc4 != 64) {
|
|
output.push(chr3);
|
|
}
|
|
}
|
|
return output;
|
|
},
|
|
encode: function (bytes) {
|
|
var i = 0, n = bytes.length;
|
|
var output = '';
|
|
while (i < n) {
|
|
var chr1 = bytes[i++];
|
|
var chr2 = bytes[i++];
|
|
var chr3 = bytes[i++];
|
|
var enc1 = chr1 >>> 2;
|
|
var enc2 = (chr1 & 3) << 4 | chr2 >>> 4;
|
|
var enc3 = (chr2 & 15) << 2 | chr3 >>> 6;
|
|
var enc4 = chr3 & 63;
|
|
if (i - n == 2) {
|
|
enc3 = enc4 = 64;
|
|
} else if (i - n == 1) {
|
|
enc4 = 64;
|
|
}
|
|
output += keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
|
|
}
|
|
return output;
|
|
}
|
|
};
|
|
}();
|
|
var PAPER_SIZE = {
|
|
a0: [
|
|
2383.94,
|
|
3370.39
|
|
],
|
|
a1: [
|
|
1683.78,
|
|
2383.94
|
|
],
|
|
a2: [
|
|
1190.55,
|
|
1683.78
|
|
],
|
|
a3: [
|
|
841.89,
|
|
1190.55
|
|
],
|
|
a4: [
|
|
595.28,
|
|
841.89
|
|
],
|
|
a5: [
|
|
419.53,
|
|
595.28
|
|
],
|
|
a6: [
|
|
297.64,
|
|
419.53
|
|
],
|
|
a7: [
|
|
209.76,
|
|
297.64
|
|
],
|
|
a8: [
|
|
147.4,
|
|
209.76
|
|
],
|
|
a9: [
|
|
104.88,
|
|
147.4
|
|
],
|
|
a10: [
|
|
73.7,
|
|
104.88
|
|
],
|
|
b0: [
|
|
2834.65,
|
|
4008.19
|
|
],
|
|
b1: [
|
|
2004.09,
|
|
2834.65
|
|
],
|
|
b2: [
|
|
1417.32,
|
|
2004.09
|
|
],
|
|
b3: [
|
|
1000.63,
|
|
1417.32
|
|
],
|
|
b4: [
|
|
708.66,
|
|
1000.63
|
|
],
|
|
b5: [
|
|
498.9,
|
|
708.66
|
|
],
|
|
b6: [
|
|
354.33,
|
|
498.9
|
|
],
|
|
b7: [
|
|
249.45,
|
|
354.33
|
|
],
|
|
b8: [
|
|
175.75,
|
|
249.45
|
|
],
|
|
b9: [
|
|
124.72,
|
|
175.75
|
|
],
|
|
b10: [
|
|
87.87,
|
|
124.72
|
|
],
|
|
c0: [
|
|
2599.37,
|
|
3676.54
|
|
],
|
|
c1: [
|
|
1836.85,
|
|
2599.37
|
|
],
|
|
c2: [
|
|
1298.27,
|
|
1836.85
|
|
],
|
|
c3: [
|
|
918.43,
|
|
1298.27
|
|
],
|
|
c4: [
|
|
649.13,
|
|
918.43
|
|
],
|
|
c5: [
|
|
459.21,
|
|
649.13
|
|
],
|
|
c6: [
|
|
323.15,
|
|
459.21
|
|
],
|
|
c7: [
|
|
229.61,
|
|
323.15
|
|
],
|
|
c8: [
|
|
161.57,
|
|
229.61
|
|
],
|
|
c9: [
|
|
113.39,
|
|
161.57
|
|
],
|
|
c10: [
|
|
79.37,
|
|
113.39
|
|
],
|
|
executive: [
|
|
521.86,
|
|
756
|
|
],
|
|
folio: [
|
|
612,
|
|
936
|
|
],
|
|
legal: [
|
|
612,
|
|
1008
|
|
],
|
|
letter: [
|
|
612,
|
|
792
|
|
],
|
|
tabloid: [
|
|
792,
|
|
1224
|
|
]
|
|
};
|
|
function makeOutput() {
|
|
var indentLevel = 0, output = BinaryStream();
|
|
function out() {
|
|
for (var i = 0; i < arguments.length; ++i) {
|
|
var x = arguments[i];
|
|
if (x === undefined) {
|
|
throw new Error('Cannot output undefined to PDF');
|
|
} else if (x instanceof PDFValue) {
|
|
x.beforeRender(out);
|
|
x.render(out);
|
|
} else if (isArray(x)) {
|
|
renderArray(x, out);
|
|
} else if (isDate(x)) {
|
|
renderDate(x, out);
|
|
} else if (typeof x == 'number') {
|
|
if (isNaN(x)) {
|
|
throw new Error('Cannot output NaN to PDF');
|
|
}
|
|
var num = x.toFixed(7);
|
|
if (num.indexOf('.') >= 0) {
|
|
num = num.replace(/\.?0+$/, '');
|
|
}
|
|
if (num == '-0') {
|
|
num = '0';
|
|
}
|
|
output.writeString(num);
|
|
} else if (/string|boolean/.test(typeof x)) {
|
|
output.writeString(x + '');
|
|
} else if (typeof x.get == 'function') {
|
|
output.write(x.get());
|
|
} else if (typeof x == 'object') {
|
|
if (!x) {
|
|
output.writeString('null');
|
|
} else {
|
|
out(new PDFDictionary(x));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
out.writeData = function (data) {
|
|
output.write(data);
|
|
};
|
|
out.withIndent = function (f) {
|
|
++indentLevel;
|
|
f(out);
|
|
--indentLevel;
|
|
};
|
|
out.indent = function () {
|
|
out(NL, pad('', indentLevel * 2, ' '));
|
|
out.apply(null, arguments);
|
|
};
|
|
out.offset = function () {
|
|
return output.offset();
|
|
};
|
|
out.toString = function () {
|
|
throw new Error('FIX CALLER');
|
|
};
|
|
out.get = function () {
|
|
return output.get();
|
|
};
|
|
out.stream = function () {
|
|
return output;
|
|
};
|
|
return out;
|
|
}
|
|
function wrapObject(value, id) {
|
|
var beforeRender = value.beforeRender;
|
|
var renderValue = value.render;
|
|
value.beforeRender = function () {
|
|
};
|
|
value.render = function (out) {
|
|
out(id, ' 0 R');
|
|
};
|
|
value.renderFull = function (out) {
|
|
value._offset = out.offset();
|
|
out(id, ' 0 obj ');
|
|
beforeRender.call(value, out);
|
|
renderValue.call(value, out);
|
|
out(' endobj');
|
|
};
|
|
}
|
|
function getPaperOptions(getOption) {
|
|
if (typeof getOption != 'function') {
|
|
var options = getOption;
|
|
getOption = function (key, def) {
|
|
return key in options ? options[key] : def;
|
|
};
|
|
}
|
|
var paperSize = getOption('paperSize', PAPER_SIZE.a4);
|
|
if (!paperSize) {
|
|
return {};
|
|
}
|
|
if (typeof paperSize == 'string') {
|
|
paperSize = PAPER_SIZE[paperSize.toLowerCase()];
|
|
if (paperSize == null) {
|
|
throw new Error('Unknown paper size');
|
|
}
|
|
}
|
|
paperSize[0] = unitsToPoints(paperSize[0]);
|
|
paperSize[1] = unitsToPoints(paperSize[1]);
|
|
if (getOption('landscape', false)) {
|
|
paperSize = [
|
|
Math.max(paperSize[0], paperSize[1]),
|
|
Math.min(paperSize[0], paperSize[1])
|
|
];
|
|
}
|
|
var margin = getOption('margin');
|
|
if (margin) {
|
|
if (typeof margin == 'string' || typeof margin == 'number') {
|
|
margin = unitsToPoints(margin, 0);
|
|
margin = {
|
|
left: margin,
|
|
top: margin,
|
|
right: margin,
|
|
bottom: margin
|
|
};
|
|
} else {
|
|
margin = {
|
|
left: unitsToPoints(margin.left, 0),
|
|
top: unitsToPoints(margin.top, 0),
|
|
right: unitsToPoints(margin.right, 0),
|
|
bottom: unitsToPoints(margin.bottom, 0)
|
|
};
|
|
}
|
|
if (getOption('addMargin')) {
|
|
paperSize[0] += margin.left + margin.right;
|
|
paperSize[1] += margin.top + margin.bottom;
|
|
}
|
|
}
|
|
return {
|
|
paperSize: paperSize,
|
|
margin: margin
|
|
};
|
|
}
|
|
function PDFDocument(options) {
|
|
var self = this;
|
|
var out = makeOutput();
|
|
var objcount = 0;
|
|
var objects = [];
|
|
function getOption(name, defval) {
|
|
return options && options[name] != null ? options[name] : defval;
|
|
}
|
|
self.getOption = getOption;
|
|
self.attach = function (value) {
|
|
if (objects.indexOf(value) < 0) {
|
|
wrapObject(value, ++objcount);
|
|
objects.push(value);
|
|
}
|
|
return value;
|
|
};
|
|
self.pages = [];
|
|
self.FONTS = {};
|
|
self.IMAGES = {};
|
|
self.GRAD_COL_FUNCTIONS = {};
|
|
self.GRAD_OPC_FUNCTIONS = {};
|
|
self.GRAD_COL = {};
|
|
self.GRAD_OPC = {};
|
|
var catalog = self.attach(new PDFCatalog());
|
|
var pageTree = self.attach(new PDFPageTree());
|
|
catalog.setPages(pageTree);
|
|
self.addPage = function (options) {
|
|
var paperOptions = getPaperOptions(function (name, defval) {
|
|
return options && options[name] != null ? options[name] : defval;
|
|
});
|
|
var paperSize = paperOptions.paperSize;
|
|
var margin = paperOptions.margin;
|
|
var contentWidth = paperSize[0];
|
|
var contentHeight = paperSize[1];
|
|
if (margin) {
|
|
contentWidth -= margin.left + margin.right;
|
|
contentHeight -= margin.top + margin.bottom;
|
|
}
|
|
var content = new PDFStream(makeOutput(), null, true);
|
|
var props = {
|
|
Contents: self.attach(content),
|
|
Parent: pageTree,
|
|
MediaBox: [
|
|
0,
|
|
0,
|
|
paperSize[0],
|
|
paperSize[1]
|
|
]
|
|
};
|
|
var page = new PDFPage(self, props);
|
|
page._content = content;
|
|
pageTree.addPage(self.attach(page));
|
|
page.transform(1, 0, 0, -1, 0, paperSize[1]);
|
|
if (margin) {
|
|
page.translate(margin.left, margin.top);
|
|
page.rect(0, 0, contentWidth, contentHeight);
|
|
page.clip();
|
|
}
|
|
self.pages.push(page);
|
|
return page;
|
|
};
|
|
self.render = function () {
|
|
var i;
|
|
out('%PDF-1.4', NL, '%ÂÁÚÏÎ', NL, NL);
|
|
for (i = 0; i < objects.length; ++i) {
|
|
objects[i].renderFull(out);
|
|
out(NL, NL);
|
|
}
|
|
var xrefOffset = out.offset();
|
|
out('xref', NL, 0, ' ', objects.length + 1, NL);
|
|
out('0000000000 65535 f ', NL);
|
|
for (i = 0; i < objects.length; ++i) {
|
|
out(zeropad(objects[i]._offset, 10), ' 00000 n ', NL);
|
|
}
|
|
out(NL);
|
|
out('trailer', NL);
|
|
out(new PDFDictionary({
|
|
Size: objects.length + 1,
|
|
Root: catalog,
|
|
Info: new PDFDictionary({
|
|
Producer: new PDFString(getOption('producer', 'Kendo UI PDF Generator v.' + kendo.version)),
|
|
Title: new PDFString(getOption('title', '')),
|
|
Author: new PDFString(getOption('author', '')),
|
|
Subject: new PDFString(getOption('subject', '')),
|
|
Keywords: new PDFString(getOption('keywords', '')),
|
|
Creator: new PDFString(getOption('creator', 'Kendo UI PDF Generator v.' + kendo.version)),
|
|
CreationDate: getOption('date', new Date())
|
|
})
|
|
}), NL, NL);
|
|
out('startxref', NL, xrefOffset, NL);
|
|
out('%%EOF', NL);
|
|
return out.stream().offset(0);
|
|
};
|
|
}
|
|
var FONT_CACHE = {
|
|
'Times-Roman': true,
|
|
'Times-Bold': true,
|
|
'Times-Italic': true,
|
|
'Times-BoldItalic': true,
|
|
'Helvetica': true,
|
|
'Helvetica-Bold': true,
|
|
'Helvetica-Oblique': true,
|
|
'Helvetica-BoldOblique': true,
|
|
'Courier': true,
|
|
'Courier-Bold': true,
|
|
'Courier-Oblique': true,
|
|
'Courier-BoldOblique': true,
|
|
'Symbol': true,
|
|
'ZapfDingbats': true
|
|
};
|
|
function loadBinary(url, cont) {
|
|
function error() {
|
|
if (window.console) {
|
|
if (window.console.error) {
|
|
window.console.error('Cannot load URL: %s', url);
|
|
} else {
|
|
window.console.log('Cannot load URL: %s', url);
|
|
}
|
|
}
|
|
cont(null);
|
|
}
|
|
var req = new XMLHttpRequest();
|
|
req.open('GET', url, true);
|
|
if (HAS_TYPED_ARRAYS) {
|
|
req.responseType = 'arraybuffer';
|
|
}
|
|
req.onload = function () {
|
|
if (req.status == 200 || req.status == 304) {
|
|
if (HAS_TYPED_ARRAYS) {
|
|
cont(new Uint8Array(req.response));
|
|
} else {
|
|
cont(new VBArray(req.responseBody).toArray());
|
|
}
|
|
} else {
|
|
error();
|
|
}
|
|
};
|
|
req.onerror = error;
|
|
req.send(null);
|
|
}
|
|
function loadFont(url, cont) {
|
|
var font = FONT_CACHE[url];
|
|
if (font) {
|
|
cont(font);
|
|
} else {
|
|
loadBinary(url, function (data) {
|
|
if (data == null) {
|
|
throw new Error('Cannot load font from ' + url);
|
|
} else {
|
|
var font = new kendo.pdf.TTFFont(data);
|
|
FONT_CACHE[url] = font;
|
|
cont(font);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
var IMAGE_CACHE = {};
|
|
function loadImage(url, cont) {
|
|
var img = IMAGE_CACHE[url], bloburl, blob;
|
|
if (img) {
|
|
cont(img);
|
|
} else {
|
|
img = new Image();
|
|
if (!/^data:/i.test(url)) {
|
|
img.crossOrigin = 'Anonymous';
|
|
}
|
|
if (HAS_TYPED_ARRAYS && !/^data:/i.test(url)) {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onload = function () {
|
|
blob = xhr.response;
|
|
bloburl = URL.createObjectURL(blob);
|
|
_load(bloburl);
|
|
};
|
|
xhr.onerror = _onerror;
|
|
xhr.open('GET', url, true);
|
|
xhr.responseType = 'blob';
|
|
xhr.send();
|
|
} else {
|
|
_load(url);
|
|
}
|
|
}
|
|
function _load(url) {
|
|
img.src = url;
|
|
if (img.complete && !kendo.support.browser.msie) {
|
|
_onload();
|
|
} else {
|
|
img.onload = _onload;
|
|
img.onerror = _onerror;
|
|
}
|
|
}
|
|
function _onerror() {
|
|
cont(IMAGE_CACHE[url] = 'TAINTED');
|
|
}
|
|
function _onload() {
|
|
if (blob && /^image\/jpe?g$/i.test(blob.type)) {
|
|
var reader = new FileReader();
|
|
reader.onload = function () {
|
|
img = new PDFJpegImage(img.width, img.height, BinaryStream(new Uint8Array(this.result)));
|
|
URL.revokeObjectURL(bloburl);
|
|
cont(IMAGE_CACHE[url] = img);
|
|
};
|
|
reader.readAsArrayBuffer(blob);
|
|
return;
|
|
}
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = img.width;
|
|
canvas.height = img.height;
|
|
var ctx = canvas.getContext('2d');
|
|
ctx.drawImage(img, 0, 0);
|
|
var imgdata;
|
|
try {
|
|
imgdata = ctx.getImageData(0, 0, img.width, img.height);
|
|
} catch (ex) {
|
|
return _onerror();
|
|
} finally {
|
|
if (bloburl) {
|
|
URL.revokeObjectURL(bloburl);
|
|
}
|
|
}
|
|
var hasAlpha = false, rgb = BinaryStream(), alpha = BinaryStream();
|
|
var rawbytes = imgdata.data;
|
|
var i = 0;
|
|
while (i < rawbytes.length) {
|
|
rgb.writeByte(rawbytes[i++]);
|
|
rgb.writeByte(rawbytes[i++]);
|
|
rgb.writeByte(rawbytes[i++]);
|
|
var a = rawbytes[i++];
|
|
if (a < 255) {
|
|
hasAlpha = true;
|
|
}
|
|
alpha.writeByte(a);
|
|
}
|
|
if (hasAlpha) {
|
|
img = new PDFRawImage(img.width, img.height, rgb, alpha);
|
|
} else {
|
|
var data = canvas.toDataURL('image/jpeg');
|
|
data = data.substr(data.indexOf(';base64,') + 8);
|
|
var stream = BinaryStream();
|
|
stream.writeBase64(data);
|
|
stream.offset(0);
|
|
img = new PDFJpegImage(img.width, img.height, stream);
|
|
}
|
|
cont(IMAGE_CACHE[url] = img);
|
|
}
|
|
}
|
|
function manyLoader(loadOne) {
|
|
return function (urls, callback) {
|
|
var n = urls.length, i = n;
|
|
if (n === 0) {
|
|
return callback();
|
|
}
|
|
while (i-- > 0) {
|
|
loadOne(urls[i], function () {
|
|
if (--n === 0) {
|
|
callback();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}
|
|
var loadFonts = manyLoader(loadFont);
|
|
var loadImages = manyLoader(loadImage);
|
|
PDFDocument.prototype = {
|
|
loadFonts: loadFonts,
|
|
loadImages: loadImages,
|
|
getFont: function (url) {
|
|
var font = this.FONTS[url];
|
|
if (!font) {
|
|
font = FONT_CACHE[url];
|
|
if (!font) {
|
|
throw new Error('Font ' + url + ' has not been loaded');
|
|
}
|
|
if (font === true) {
|
|
font = this.attach(new PDFStandardFont(url));
|
|
} else {
|
|
font = this.attach(new PDFFont(this, font));
|
|
}
|
|
this.FONTS[url] = font;
|
|
}
|
|
return font;
|
|
},
|
|
getImage: function (url) {
|
|
var img = this.IMAGES[url];
|
|
if (!img) {
|
|
img = IMAGE_CACHE[url];
|
|
if (!img) {
|
|
throw new Error('Image ' + url + ' has not been loaded');
|
|
}
|
|
if (img === 'TAINTED') {
|
|
return null;
|
|
}
|
|
img = this.IMAGES[url] = this.attach(img.asStream(this));
|
|
}
|
|
return img;
|
|
},
|
|
getOpacityGS: function (opacity, forStroke) {
|
|
var id = parseFloat(opacity).toFixed(3);
|
|
opacity = parseFloat(id);
|
|
id += forStroke ? 'S' : 'F';
|
|
var cache = this._opacityGSCache || (this._opacityGSCache = {});
|
|
var gs = cache[id];
|
|
if (!gs) {
|
|
var props = { Type: _('ExtGState') };
|
|
if (forStroke) {
|
|
props.CA = opacity;
|
|
} else {
|
|
props.ca = opacity;
|
|
}
|
|
gs = this.attach(new PDFDictionary(props));
|
|
gs._resourceName = _('GS' + ++RESOURCE_COUNTER);
|
|
cache[id] = gs;
|
|
}
|
|
return gs;
|
|
},
|
|
dict: function (props) {
|
|
return new PDFDictionary(props);
|
|
},
|
|
name: function (str) {
|
|
return _(str);
|
|
},
|
|
stream: function (props, content) {
|
|
return new PDFStream(content, props);
|
|
}
|
|
};
|
|
function pad(str, len, ch) {
|
|
while (str.length < len) {
|
|
str = ch + str;
|
|
}
|
|
return str;
|
|
}
|
|
function zeropad(n, len) {
|
|
return pad(n + '', len, '0');
|
|
}
|
|
function hasOwnProperty(obj, key) {
|
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
}
|
|
var isArray = Array.isArray || function (obj) {
|
|
return obj instanceof Array;
|
|
};
|
|
function isDate(obj) {
|
|
return obj instanceof Date;
|
|
}
|
|
function renderArray(a, out) {
|
|
out('[');
|
|
if (a.length > 0) {
|
|
out.withIndent(function () {
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (i > 0 && i % 8 === 0) {
|
|
out.indent(a[i]);
|
|
} else {
|
|
out(' ', a[i]);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
out(' ]');
|
|
}
|
|
function renderDate(date, out) {
|
|
out('(D:', zeropad(date.getUTCFullYear(), 4), zeropad(date.getUTCMonth() + 1, 2), zeropad(date.getUTCDate(), 2), zeropad(date.getUTCHours(), 2), zeropad(date.getUTCMinutes(), 2), zeropad(date.getUTCSeconds(), 2), 'Z)');
|
|
}
|
|
function mm2pt(mm) {
|
|
return mm * (72 / 25.4);
|
|
}
|
|
function cm2pt(cm) {
|
|
return mm2pt(cm * 10);
|
|
}
|
|
function in2pt(inch) {
|
|
return inch * 72;
|
|
}
|
|
function unitsToPoints(x, def) {
|
|
if (typeof x == 'number') {
|
|
return x;
|
|
}
|
|
if (typeof x == 'string') {
|
|
var m;
|
|
m = /^\s*([0-9.]+)\s*(mm|cm|in|pt)\s*$/.exec(x);
|
|
if (m) {
|
|
var num = parseFloat(m[1]);
|
|
if (!isNaN(num)) {
|
|
if (m[2] == 'pt') {
|
|
return num;
|
|
}
|
|
return {
|
|
'mm': mm2pt,
|
|
'cm': cm2pt,
|
|
'in': in2pt
|
|
}[m[2]](num);
|
|
}
|
|
}
|
|
}
|
|
if (def != null) {
|
|
return def;
|
|
}
|
|
throw new Error('Can\'t parse unit: ' + x);
|
|
}
|
|
function PDFValue() {
|
|
}
|
|
PDFValue.prototype.beforeRender = function () {
|
|
};
|
|
function defclass(Ctor, proto, Base) {
|
|
if (!Base) {
|
|
Base = PDFValue;
|
|
}
|
|
Ctor.prototype = new Base();
|
|
for (var i in proto) {
|
|
if (hasOwnProperty(proto, i)) {
|
|
Ctor.prototype[i] = proto[i];
|
|
}
|
|
}
|
|
return Ctor;
|
|
}
|
|
var PDFString = defclass(function PDFString(value) {
|
|
this.value = value;
|
|
}, {
|
|
render: function (out) {
|
|
var txt = '', esc = this.escape();
|
|
for (var i = 0; i < esc.length; ++i) {
|
|
txt += String.fromCharCode(esc.charCodeAt(i) & 255);
|
|
}
|
|
out('(', txt, ')');
|
|
},
|
|
escape: function () {
|
|
return this.value.replace(/([\(\)\\])/g, '\\$1');
|
|
},
|
|
toString: function () {
|
|
return this.value;
|
|
}
|
|
});
|
|
var PDFHexString = defclass(function PDFHexString(value) {
|
|
this.value = value;
|
|
}, {
|
|
render: function (out) {
|
|
out('<');
|
|
for (var i = 0; i < this.value.length; ++i) {
|
|
out(zeropad(this.value.charCodeAt(i).toString(16), 4));
|
|
}
|
|
out('>');
|
|
}
|
|
}, PDFString);
|
|
var PDFName = defclass(function PDFName(name) {
|
|
this.name = name;
|
|
}, {
|
|
render: function (out) {
|
|
out('/' + this.escape());
|
|
},
|
|
escape: function () {
|
|
return this.name.replace(/[^\x21-\x7E]/g, function (c) {
|
|
return '#' + zeropad(c.charCodeAt(0).toString(16), 2);
|
|
});
|
|
},
|
|
toString: function () {
|
|
return this.name;
|
|
}
|
|
});
|
|
var PDFName_cache = {};
|
|
PDFName.get = _;
|
|
function _(name) {
|
|
if (hasOwnProperty(PDFName_cache, name)) {
|
|
return PDFName_cache[name];
|
|
}
|
|
return PDFName_cache[name] = new PDFName(name);
|
|
}
|
|
var PDFDictionary = defclass(function PDFDictionary(props) {
|
|
this.props = props;
|
|
}, {
|
|
render: function (out) {
|
|
var props = this.props, empty = true;
|
|
out('<<');
|
|
out.withIndent(function () {
|
|
for (var i in props) {
|
|
if (hasOwnProperty(props, i) && !/^_/.test(i)) {
|
|
empty = false;
|
|
out.indent(_(i), ' ', props[i]);
|
|
}
|
|
}
|
|
});
|
|
if (!empty) {
|
|
out.indent();
|
|
}
|
|
out('>>');
|
|
}
|
|
});
|
|
var PDFStream = defclass(function PDFStream(data, props, compress) {
|
|
if (typeof data == 'string') {
|
|
var tmp = BinaryStream();
|
|
tmp.write(data);
|
|
data = tmp;
|
|
}
|
|
this.data = data;
|
|
this.props = props || {};
|
|
this.compress = compress;
|
|
}, {
|
|
render: function (out) {
|
|
var data = this.data.get(), props = this.props;
|
|
if (this.compress && window.pako && typeof window.pako.deflate == 'function') {
|
|
if (!props.Filter) {
|
|
props.Filter = [];
|
|
} else if (!(props.Filter instanceof Array)) {
|
|
props.Filter = [props.Filter];
|
|
}
|
|
props.Filter.unshift(_('FlateDecode'));
|
|
data = window.pako.deflate(data);
|
|
}
|
|
props.Length = data.length;
|
|
out(new PDFDictionary(props), ' stream', NL);
|
|
out.writeData(data);
|
|
out(NL, 'endstream');
|
|
}
|
|
});
|
|
var PDFCatalog = defclass(function PDFCatalog(props) {
|
|
props = this.props = props || {};
|
|
props.Type = _('Catalog');
|
|
}, {
|
|
setPages: function (pagesObj) {
|
|
this.props.Pages = pagesObj;
|
|
}
|
|
}, PDFDictionary);
|
|
var PDFPageTree = defclass(function PDFPageTree() {
|
|
this.props = {
|
|
Type: _('Pages'),
|
|
Kids: [],
|
|
Count: 0
|
|
};
|
|
}, {
|
|
addPage: function (pageObj) {
|
|
this.props.Kids.push(pageObj);
|
|
this.props.Count++;
|
|
}
|
|
}, PDFDictionary);
|
|
function PDFJpegImage(width, height, data) {
|
|
this.asStream = function () {
|
|
var stream = new PDFStream(data, {
|
|
Type: _('XObject'),
|
|
Subtype: _('Image'),
|
|
Width: width,
|
|
Height: height,
|
|
BitsPerComponent: 8,
|
|
ColorSpace: _('DeviceRGB'),
|
|
Filter: _('DCTDecode')
|
|
});
|
|
stream._resourceName = _('I' + ++RESOURCE_COUNTER);
|
|
return stream;
|
|
};
|
|
}
|
|
function PDFRawImage(width, height, rgb, alpha) {
|
|
this.asStream = function (pdf) {
|
|
var mask = new PDFStream(alpha, {
|
|
Type: _('XObject'),
|
|
Subtype: _('Image'),
|
|
Width: width,
|
|
Height: height,
|
|
BitsPerComponent: 8,
|
|
ColorSpace: _('DeviceGray')
|
|
}, true);
|
|
var stream = new PDFStream(rgb, {
|
|
Type: _('XObject'),
|
|
Subtype: _('Image'),
|
|
Width: width,
|
|
Height: height,
|
|
BitsPerComponent: 8,
|
|
ColorSpace: _('DeviceRGB'),
|
|
SMask: pdf.attach(mask)
|
|
}, true);
|
|
stream._resourceName = _('I' + ++RESOURCE_COUNTER);
|
|
return stream;
|
|
};
|
|
}
|
|
var PDFStandardFont = defclass(function PDFStandardFont(name) {
|
|
this.props = {
|
|
Type: _('Font'),
|
|
Subtype: _('Type1'),
|
|
BaseFont: _(name)
|
|
};
|
|
this._resourceName = _('F' + ++RESOURCE_COUNTER);
|
|
}, {
|
|
encodeText: function (str) {
|
|
return new PDFString(str + '');
|
|
}
|
|
}, PDFDictionary);
|
|
var PDFFont = defclass(function PDFFont(pdf, font, props) {
|
|
props = this.props = props || {};
|
|
props.Type = _('Font');
|
|
props.Subtype = _('Type0');
|
|
props.Encoding = _('Identity-H');
|
|
this._pdf = pdf;
|
|
this._font = font;
|
|
this._sub = font.makeSubset();
|
|
this._resourceName = _('F' + ++RESOURCE_COUNTER);
|
|
var head = font.head;
|
|
this.name = font.psName;
|
|
var scale = this.scale = font.scale;
|
|
this.bbox = [
|
|
head.xMin * scale,
|
|
head.yMin * scale,
|
|
head.xMax * scale,
|
|
head.yMax * scale
|
|
];
|
|
this.italicAngle = font.post.italicAngle;
|
|
this.ascent = font.ascent * scale;
|
|
this.descent = font.descent * scale;
|
|
this.lineGap = font.lineGap * scale;
|
|
this.capHeight = font.os2.capHeight || this.ascent;
|
|
this.xHeight = font.os2.xHeight || 0;
|
|
this.stemV = 0;
|
|
this.familyClass = (font.os2.familyClass || 0) >> 8;
|
|
this.isSerif = this.familyClass >= 1 && this.familyClass <= 7;
|
|
this.isScript = this.familyClass == 10;
|
|
this.flags = (font.post.isFixedPitch ? 1 : 0) | (this.isSerif ? 1 << 1 : 0) | (this.isScript ? 1 << 3 : 0) | (this.italicAngle !== 0 ? 1 << 6 : 0) | 1 << 5;
|
|
}, {
|
|
encodeText: function (text) {
|
|
return new PDFHexString(this._sub.encodeText(text + ''));
|
|
},
|
|
getTextWidth: function (fontSize, text) {
|
|
var width = 0, codeMap = this._font.cmap.codeMap;
|
|
for (var i = 0; i < text.length; ++i) {
|
|
var glyphId = codeMap[text.charCodeAt(i)];
|
|
width += this._font.widthOfGlyph(glyphId || 0);
|
|
}
|
|
return width * fontSize / 1000;
|
|
},
|
|
beforeRender: function () {
|
|
var self = this;
|
|
var sub = self._sub;
|
|
var data = sub.render();
|
|
var fontStream = new PDFStream(BinaryStream(data), { Length1: data.length }, true);
|
|
var descriptor = self._pdf.attach(new PDFDictionary({
|
|
Type: _('FontDescriptor'),
|
|
FontName: _(self._sub.psName),
|
|
FontBBox: self.bbox,
|
|
Flags: self.flags,
|
|
StemV: self.stemV,
|
|
ItalicAngle: self.italicAngle,
|
|
Ascent: self.ascent,
|
|
Descent: self.descent,
|
|
CapHeight: self.capHeight,
|
|
XHeight: self.xHeight,
|
|
FontFile2: self._pdf.attach(fontStream)
|
|
}));
|
|
var cmap = sub.ncid2ogid;
|
|
var firstChar = sub.firstChar;
|
|
var lastChar = sub.lastChar;
|
|
var charWidths = [];
|
|
(function loop(i, chunk) {
|
|
if (i <= lastChar) {
|
|
var gid = cmap[i];
|
|
if (gid == null) {
|
|
loop(i + 1);
|
|
} else {
|
|
if (!chunk) {
|
|
charWidths.push(i, chunk = []);
|
|
}
|
|
chunk.push(self._font.widthOfGlyph(gid));
|
|
loop(i + 1, chunk);
|
|
}
|
|
}
|
|
}(firstChar));
|
|
var descendant = new PDFDictionary({
|
|
Type: _('Font'),
|
|
Subtype: _('CIDFontType2'),
|
|
BaseFont: _(self._sub.psName),
|
|
CIDSystemInfo: new PDFDictionary({
|
|
Registry: new PDFString('Adobe'),
|
|
Ordering: new PDFString('Identity'),
|
|
Supplement: 0
|
|
}),
|
|
FontDescriptor: descriptor,
|
|
FirstChar: firstChar,
|
|
LastChar: lastChar,
|
|
DW: Math.round(self._font.widthOfGlyph(0)),
|
|
W: charWidths,
|
|
CIDToGIDMap: self._pdf.attach(self._makeCidToGidMap())
|
|
});
|
|
var dict = self.props;
|
|
dict.BaseFont = _(self._sub.psName);
|
|
dict.DescendantFonts = [self._pdf.attach(descendant)];
|
|
var unimap = new PDFToUnicodeCmap(firstChar, lastChar, sub.subset);
|
|
var unimapStream = new PDFStream(makeOutput(), null, true);
|
|
unimapStream.data(unimap);
|
|
dict.ToUnicode = self._pdf.attach(unimapStream);
|
|
},
|
|
_makeCidToGidMap: function () {
|
|
return new PDFStream(BinaryStream(this._sub.cidToGidMap()), null, true);
|
|
}
|
|
}, PDFDictionary);
|
|
var PDFToUnicodeCmap = defclass(function PDFUnicodeCMap(firstChar, lastChar, map) {
|
|
this.firstChar = firstChar;
|
|
this.lastChar = lastChar;
|
|
this.map = map;
|
|
}, {
|
|
render: function (out) {
|
|
out.indent('/CIDInit /ProcSet findresource begin');
|
|
out.indent('12 dict begin');
|
|
out.indent('begincmap');
|
|
out.indent('/CIDSystemInfo <<');
|
|
out.indent(' /Registry (Adobe)');
|
|
out.indent(' /Ordering (UCS)');
|
|
out.indent(' /Supplement 0');
|
|
out.indent('>> def');
|
|
out.indent('/CMapName /Adobe-Identity-UCS def');
|
|
out.indent('/CMapType 2 def');
|
|
out.indent('1 begincodespacerange');
|
|
out.indent(' <0000><ffff>');
|
|
out.indent('endcodespacerange');
|
|
var self = this;
|
|
out.indent(self.lastChar - self.firstChar + 1, ' beginbfchar');
|
|
out.withIndent(function () {
|
|
for (var code = self.firstChar; code <= self.lastChar; ++code) {
|
|
var unicode = self.map[code];
|
|
var str = kendo.util.ucs2encode([unicode]);
|
|
out.indent('<', zeropad(code.toString(16), 4), '>', '<');
|
|
for (var i = 0; i < str.length; ++i) {
|
|
out(zeropad(str.charCodeAt(i).toString(16), 4));
|
|
}
|
|
out('>');
|
|
}
|
|
});
|
|
out.indent('endbfchar');
|
|
out.indent('endcmap');
|
|
out.indent('CMapName currentdict /CMap defineresource pop');
|
|
out.indent('end');
|
|
out.indent('end');
|
|
}
|
|
});
|
|
function makeHash(a) {
|
|
return a.map(function (x) {
|
|
return isArray(x) ? makeHash(x) : typeof x == 'number' ? (Math.round(x * 1000) / 1000).toFixed(3) : x;
|
|
}).join(' ');
|
|
}
|
|
function cacheColorGradientFunction(pdf, r1, g1, b1, r2, g2, b2) {
|
|
var hash = makeHash([
|
|
r1,
|
|
g1,
|
|
b1,
|
|
r2,
|
|
g2,
|
|
b2
|
|
]);
|
|
var func = pdf.GRAD_COL_FUNCTIONS[hash];
|
|
if (!func) {
|
|
func = pdf.GRAD_COL_FUNCTIONS[hash] = pdf.attach(new PDFDictionary({
|
|
FunctionType: 2,
|
|
Domain: [
|
|
0,
|
|
1
|
|
],
|
|
Range: [
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
0,
|
|
1
|
|
],
|
|
N: 1,
|
|
C0: [
|
|
r1,
|
|
g1,
|
|
b1
|
|
],
|
|
C1: [
|
|
r2,
|
|
g2,
|
|
b2
|
|
]
|
|
}));
|
|
}
|
|
return func;
|
|
}
|
|
function cacheOpacityGradientFunction(pdf, a1, a2) {
|
|
var hash = makeHash([
|
|
a1,
|
|
a2
|
|
]);
|
|
var func = pdf.GRAD_OPC_FUNCTIONS[hash];
|
|
if (!func) {
|
|
func = pdf.GRAD_OPC_FUNCTIONS[hash] = pdf.attach(new PDFDictionary({
|
|
FunctionType: 2,
|
|
Domain: [
|
|
0,
|
|
1
|
|
],
|
|
Range: [
|
|
0,
|
|
1
|
|
],
|
|
N: 1,
|
|
C0: [a1],
|
|
C1: [a2]
|
|
}));
|
|
}
|
|
return func;
|
|
}
|
|
function makeGradientFunctions(pdf, stops) {
|
|
var hasAlpha = false;
|
|
var opacities = [];
|
|
var colors = [];
|
|
var offsets = [];
|
|
var encode = [];
|
|
var i, prev, cur, prevColor, curColor;
|
|
for (i = 1; i < stops.length; ++i) {
|
|
prev = stops[i - 1];
|
|
cur = stops[i];
|
|
prevColor = prev.color;
|
|
curColor = cur.color;
|
|
colors.push(cacheColorGradientFunction(pdf, prevColor.r, prevColor.g, prevColor.b, curColor.r, curColor.g, curColor.b));
|
|
if (prevColor.a < 1 || curColor.a < 1) {
|
|
hasAlpha = true;
|
|
}
|
|
offsets.push(cur.offset);
|
|
encode.push(0, 1);
|
|
}
|
|
if (hasAlpha) {
|
|
for (i = 1; i < stops.length; ++i) {
|
|
prev = stops[i - 1];
|
|
cur = stops[i];
|
|
prevColor = prev.color;
|
|
curColor = cur.color;
|
|
opacities.push(cacheOpacityGradientFunction(pdf, prevColor.a, curColor.a));
|
|
}
|
|
}
|
|
offsets.pop();
|
|
return {
|
|
hasAlpha: hasAlpha,
|
|
colors: assemble(colors),
|
|
opacities: hasAlpha ? assemble(opacities) : null
|
|
};
|
|
function assemble(funcs) {
|
|
if (funcs.length == 1) {
|
|
return funcs[0];
|
|
}
|
|
return {
|
|
FunctionType: 3,
|
|
Functions: funcs,
|
|
Domain: [
|
|
0,
|
|
1
|
|
],
|
|
Bounds: offsets,
|
|
Encode: encode
|
|
};
|
|
}
|
|
}
|
|
function cacheColorGradient(pdf, isRadial, stops, coords, funcs, box) {
|
|
var shading, hash;
|
|
if (!box) {
|
|
var a = [isRadial].concat(coords);
|
|
stops.forEach(function (x) {
|
|
a.push(x.offset, x.color.r, x.color.g, x.color.b);
|
|
});
|
|
hash = makeHash(a);
|
|
shading = pdf.GRAD_COL[hash];
|
|
}
|
|
if (!shading) {
|
|
shading = new PDFDictionary({
|
|
Type: _('Shading'),
|
|
ShadingType: isRadial ? 3 : 2,
|
|
ColorSpace: _('DeviceRGB'),
|
|
Coords: coords,
|
|
Domain: [
|
|
0,
|
|
1
|
|
],
|
|
Function: funcs,
|
|
Extend: [
|
|
true,
|
|
true
|
|
]
|
|
});
|
|
pdf.attach(shading);
|
|
shading._resourceName = 'S' + ++RESOURCE_COUNTER;
|
|
if (hash) {
|
|
pdf.GRAD_COL[hash] = shading;
|
|
}
|
|
}
|
|
return shading;
|
|
}
|
|
function cacheOpacityGradient(pdf, isRadial, stops, coords, funcs, box) {
|
|
var opacity, hash;
|
|
if (!box) {
|
|
var a = [isRadial].concat(coords);
|
|
stops.forEach(function (x) {
|
|
a.push(x.offset, x.color.a);
|
|
});
|
|
hash = makeHash(a);
|
|
opacity = pdf.GRAD_OPC[hash];
|
|
}
|
|
if (!opacity) {
|
|
opacity = new PDFDictionary({
|
|
Type: _('ExtGState'),
|
|
AIS: false,
|
|
CA: 1,
|
|
ca: 1,
|
|
SMask: {
|
|
Type: _('Mask'),
|
|
S: _('Luminosity'),
|
|
G: pdf.attach(new PDFStream('/a0 gs /s0 sh', {
|
|
Type: _('XObject'),
|
|
Subtype: _('Form'),
|
|
FormType: 1,
|
|
BBox: box ? [
|
|
box.left,
|
|
box.top + box.height,
|
|
box.left + box.width,
|
|
box.top
|
|
] : [
|
|
0,
|
|
1,
|
|
1,
|
|
0
|
|
],
|
|
Group: {
|
|
Type: _('Group'),
|
|
S: _('Transparency'),
|
|
CS: _('DeviceGray'),
|
|
I: true
|
|
},
|
|
Resources: {
|
|
ExtGState: {
|
|
a0: {
|
|
CA: 1,
|
|
ca: 1
|
|
}
|
|
},
|
|
Shading: {
|
|
s0: {
|
|
ColorSpace: _('DeviceGray'),
|
|
Coords: coords,
|
|
Domain: [
|
|
0,
|
|
1
|
|
],
|
|
ShadingType: isRadial ? 3 : 2,
|
|
Function: funcs,
|
|
Extend: [
|
|
true,
|
|
true
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
}
|
|
});
|
|
pdf.attach(opacity);
|
|
opacity._resourceName = 'O' + ++RESOURCE_COUNTER;
|
|
if (hash) {
|
|
pdf.GRAD_OPC[hash] = opacity;
|
|
}
|
|
}
|
|
return opacity;
|
|
}
|
|
function cacheGradient(pdf, gradient, box) {
|
|
var isRadial = gradient.type == 'radial';
|
|
var funcs = makeGradientFunctions(pdf, gradient.stops);
|
|
var coords = isRadial ? [
|
|
gradient.start.x,
|
|
gradient.start.y,
|
|
gradient.start.r,
|
|
gradient.end.x,
|
|
gradient.end.y,
|
|
gradient.end.r
|
|
] : [
|
|
gradient.start.x,
|
|
gradient.start.y,
|
|
gradient.end.x,
|
|
gradient.end.y
|
|
];
|
|
var shading = cacheColorGradient(pdf, isRadial, gradient.stops, coords, funcs.colors, gradient.userSpace && box);
|
|
var opacity = funcs.hasAlpha ? cacheOpacityGradient(pdf, isRadial, gradient.stops, coords, funcs.opacities, gradient.userSpace && box) : null;
|
|
return {
|
|
hasAlpha: funcs.hasAlpha,
|
|
shading: shading,
|
|
opacity: opacity
|
|
};
|
|
}
|
|
var PDFPage = defclass(function PDFPage(pdf, props) {
|
|
this._pdf = pdf;
|
|
this._rcount = 0;
|
|
this._textMode = false;
|
|
this._fontResources = {};
|
|
this._gsResources = {};
|
|
this._xResources = {};
|
|
this._patResources = {};
|
|
this._shResources = {};
|
|
this._opacity = 1;
|
|
this._matrix = [
|
|
1,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
0
|
|
];
|
|
this._annotations = [];
|
|
this._font = null;
|
|
this._fontSize = null;
|
|
this._contextStack = [];
|
|
props = this.props = props || {};
|
|
props.Type = _('Page');
|
|
props.ProcSet = [
|
|
_('PDF'),
|
|
_('Text'),
|
|
_('ImageB'),
|
|
_('ImageC'),
|
|
_('ImageI')
|
|
];
|
|
props.Resources = new PDFDictionary({
|
|
Font: new PDFDictionary(this._fontResources),
|
|
ExtGState: new PDFDictionary(this._gsResources),
|
|
XObject: new PDFDictionary(this._xResources),
|
|
Pattern: new PDFDictionary(this._patResources),
|
|
Shading: new PDFDictionary(this._shResources)
|
|
});
|
|
props.Annots = this._annotations;
|
|
}, {
|
|
_out: function () {
|
|
this._content.data.apply(null, arguments);
|
|
},
|
|
transform: function (a, b, c, d, e, f) {
|
|
if (!isIdentityMatrix(arguments)) {
|
|
this._matrix = mmul(arguments, this._matrix);
|
|
this._out(a, ' ', b, ' ', c, ' ', d, ' ', e, ' ', f, ' cm');
|
|
this._out(NL);
|
|
}
|
|
},
|
|
translate: function (dx, dy) {
|
|
this.transform(1, 0, 0, 1, dx, dy);
|
|
},
|
|
scale: function (sx, sy) {
|
|
this.transform(sx, 0, 0, sy, 0, 0);
|
|
},
|
|
rotate: function (angle) {
|
|
var cos = Math.cos(angle), sin = Math.sin(angle);
|
|
this.transform(cos, sin, -sin, cos, 0, 0);
|
|
},
|
|
beginText: function () {
|
|
this._textMode = true;
|
|
this._out('BT', NL);
|
|
},
|
|
endText: function () {
|
|
this._textMode = false;
|
|
this._out('ET', NL);
|
|
},
|
|
_requireTextMode: function () {
|
|
if (!this._textMode) {
|
|
throw new Error('Text mode required; call page.beginText() first');
|
|
}
|
|
},
|
|
_requireFont: function () {
|
|
if (!this._font) {
|
|
throw new Error('No font selected; call page.setFont() first');
|
|
}
|
|
},
|
|
setFont: function (font, size) {
|
|
this._requireTextMode();
|
|
if (font == null) {
|
|
font = this._font;
|
|
} else if (!(font instanceof PDFFont)) {
|
|
font = this._pdf.getFont(font);
|
|
}
|
|
if (size == null) {
|
|
size = this._fontSize;
|
|
}
|
|
this._fontResources[font._resourceName] = font;
|
|
this._font = font;
|
|
this._fontSize = size;
|
|
this._out(font._resourceName, ' ', size, ' Tf', NL);
|
|
},
|
|
setTextLeading: function (size) {
|
|
this._requireTextMode();
|
|
this._out(size, ' TL', NL);
|
|
},
|
|
setTextRenderingMode: function (mode) {
|
|
this._requireTextMode();
|
|
this._out(mode, ' Tr', NL);
|
|
},
|
|
showText: function (text, requestedWidth) {
|
|
this._requireFont();
|
|
if (text.length > 1 && requestedWidth && this._font instanceof PDFFont) {
|
|
var outputWidth = this._font.getTextWidth(this._fontSize, text);
|
|
var scale = requestedWidth / outputWidth * 100;
|
|
this._out(scale, ' Tz ');
|
|
}
|
|
this._out(this._font.encodeText(text), ' Tj', NL);
|
|
},
|
|
showTextNL: function (text) {
|
|
this._requireFont();
|
|
this._out(this._font.encodeText(text), ' \'', NL);
|
|
},
|
|
addLink: function (uri, box) {
|
|
var ll = this._toPage({
|
|
x: box.left,
|
|
y: box.bottom
|
|
});
|
|
var ur = this._toPage({
|
|
x: box.right,
|
|
y: box.top
|
|
});
|
|
this._annotations.push(new PDFDictionary({
|
|
Type: _('Annot'),
|
|
Subtype: _('Link'),
|
|
Rect: [
|
|
ll.x,
|
|
ll.y,
|
|
ur.x,
|
|
ur.y
|
|
],
|
|
Border: [
|
|
0,
|
|
0,
|
|
0
|
|
],
|
|
A: new PDFDictionary({
|
|
Type: _('Action'),
|
|
S: _('URI'),
|
|
URI: new PDFString(uri)
|
|
})
|
|
}));
|
|
},
|
|
setStrokeColor: function (r, g, b) {
|
|
this._out(r, ' ', g, ' ', b, ' RG', NL);
|
|
},
|
|
setOpacity: function (opacity) {
|
|
this.setFillOpacity(opacity);
|
|
this.setStrokeOpacity(opacity);
|
|
this._opacity *= opacity;
|
|
},
|
|
setStrokeOpacity: function (opacity) {
|
|
if (opacity < 1) {
|
|
var gs = this._pdf.getOpacityGS(this._opacity * opacity, true);
|
|
this._gsResources[gs._resourceName] = gs;
|
|
this._out(gs._resourceName, ' gs', NL);
|
|
}
|
|
},
|
|
setFillColor: function (r, g, b) {
|
|
this._out(r, ' ', g, ' ', b, ' rg', NL);
|
|
},
|
|
setFillOpacity: function (opacity) {
|
|
if (opacity < 1) {
|
|
var gs = this._pdf.getOpacityGS(this._opacity * opacity, false);
|
|
this._gsResources[gs._resourceName] = gs;
|
|
this._out(gs._resourceName, ' gs', NL);
|
|
}
|
|
},
|
|
gradient: function (gradient, box) {
|
|
this.save();
|
|
this.rect(box.left, box.top, box.width, box.height);
|
|
this.clip();
|
|
if (!gradient.userSpace) {
|
|
this.transform(box.width, 0, 0, box.height, box.left, box.top);
|
|
}
|
|
var g = cacheGradient(this._pdf, gradient, box);
|
|
var sname = g.shading._resourceName, oname;
|
|
this._shResources[sname] = g.shading;
|
|
if (g.hasAlpha) {
|
|
oname = g.opacity._resourceName;
|
|
this._gsResources[oname] = g.opacity;
|
|
this._out('/' + oname + ' gs ');
|
|
}
|
|
this._out('/' + sname + ' sh', NL);
|
|
this.restore();
|
|
},
|
|
setDashPattern: function (dashArray, dashPhase) {
|
|
this._out(dashArray, ' ', dashPhase, ' d', NL);
|
|
},
|
|
setLineWidth: function (width) {
|
|
this._out(width, ' w', NL);
|
|
},
|
|
setLineCap: function (lineCap) {
|
|
this._out(lineCap, ' J', NL);
|
|
},
|
|
setLineJoin: function (lineJoin) {
|
|
this._out(lineJoin, ' j', NL);
|
|
},
|
|
setMitterLimit: function (mitterLimit) {
|
|
this._out(mitterLimit, ' M', NL);
|
|
},
|
|
save: function () {
|
|
this._contextStack.push(this._context());
|
|
this._out('q', NL);
|
|
},
|
|
restore: function () {
|
|
this._out('Q', NL);
|
|
this._context(this._contextStack.pop());
|
|
},
|
|
moveTo: function (x, y) {
|
|
this._out(x, ' ', y, ' m', NL);
|
|
},
|
|
lineTo: function (x, y) {
|
|
this._out(x, ' ', y, ' l', NL);
|
|
},
|
|
bezier: function (x1, y1, x2, y2, x3, y3) {
|
|
this._out(x1, ' ', y1, ' ', x2, ' ', y2, ' ', x3, ' ', y3, ' c', NL);
|
|
},
|
|
bezier1: function (x1, y1, x3, y3) {
|
|
this._out(x1, ' ', y1, ' ', x3, ' ', y3, ' y', NL);
|
|
},
|
|
bezier2: function (x2, y2, x3, y3) {
|
|
this._out(x2, ' ', y2, ' ', x3, ' ', y3, ' v', NL);
|
|
},
|
|
close: function () {
|
|
this._out('h', NL);
|
|
},
|
|
rect: function (x, y, w, h) {
|
|
this._out(x, ' ', y, ' ', w, ' ', h, ' re', NL);
|
|
},
|
|
ellipse: function (x, y, rx, ry) {
|
|
function _X(v) {
|
|
return x + v;
|
|
}
|
|
function _Y(v) {
|
|
return y + v;
|
|
}
|
|
var k = 0.5522847498307936;
|
|
this.moveTo(_X(0), _Y(ry));
|
|
this.bezier(_X(rx * k), _Y(ry), _X(rx), _Y(ry * k), _X(rx), _Y(0));
|
|
this.bezier(_X(rx), _Y(-ry * k), _X(rx * k), _Y(-ry), _X(0), _Y(-ry));
|
|
this.bezier(_X(-rx * k), _Y(-ry), _X(-rx), _Y(-ry * k), _X(-rx), _Y(0));
|
|
this.bezier(_X(-rx), _Y(ry * k), _X(-rx * k), _Y(ry), _X(0), _Y(ry));
|
|
},
|
|
circle: function (x, y, r) {
|
|
this.ellipse(x, y, r, r);
|
|
},
|
|
stroke: function () {
|
|
this._out('S', NL);
|
|
},
|
|
nop: function () {
|
|
this._out('n', NL);
|
|
},
|
|
clip: function () {
|
|
this._out('W n', NL);
|
|
},
|
|
clipStroke: function () {
|
|
this._out('W S', NL);
|
|
},
|
|
closeStroke: function () {
|
|
this._out('s', NL);
|
|
},
|
|
fill: function () {
|
|
this._out('f', NL);
|
|
},
|
|
fillStroke: function () {
|
|
this._out('B', NL);
|
|
},
|
|
drawImage: function (url) {
|
|
var img = this._pdf.getImage(url);
|
|
if (img) {
|
|
this._xResources[img._resourceName] = img;
|
|
this._out(img._resourceName, ' Do', NL);
|
|
}
|
|
},
|
|
comment: function (txt) {
|
|
var self = this;
|
|
txt.split(/\r?\n/g).forEach(function (line) {
|
|
self._out('% ', line, NL);
|
|
});
|
|
},
|
|
_context: function (val) {
|
|
if (val != null) {
|
|
this._opacity = val.opacity;
|
|
this._matrix = val.matrix;
|
|
} else {
|
|
return {
|
|
opacity: this._opacity,
|
|
matrix: this._matrix
|
|
};
|
|
}
|
|
},
|
|
_toPage: function (p) {
|
|
var m = this._matrix;
|
|
var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
|
return {
|
|
x: a * p.x + c * p.y + e,
|
|
y: b * p.x + d * p.y + f
|
|
};
|
|
}
|
|
}, PDFDictionary);
|
|
function BinaryStream(data) {
|
|
var offset = 0, length = 0;
|
|
if (data == null) {
|
|
data = HAS_TYPED_ARRAYS ? new Uint8Array(256) : [];
|
|
} else {
|
|
length = data.length;
|
|
}
|
|
var ensure = HAS_TYPED_ARRAYS ? function (len) {
|
|
if (len >= data.length) {
|
|
var tmp = new Uint8Array(Math.max(len + 256, data.length * 2));
|
|
tmp.set(data, 0);
|
|
data = tmp;
|
|
}
|
|
} : function () {
|
|
};
|
|
var get = HAS_TYPED_ARRAYS ? function () {
|
|
return new Uint8Array(data.buffer, 0, length);
|
|
} : function () {
|
|
return data;
|
|
};
|
|
var write = HAS_TYPED_ARRAYS ? function (bytes) {
|
|
if (typeof bytes == 'string') {
|
|
return writeString(bytes);
|
|
}
|
|
var len = bytes.length;
|
|
ensure(offset + len);
|
|
data.set(bytes, offset);
|
|
offset += len;
|
|
if (offset > length) {
|
|
length = offset;
|
|
}
|
|
} : function (bytes) {
|
|
if (typeof bytes == 'string') {
|
|
return writeString(bytes);
|
|
}
|
|
for (var i = 0; i < bytes.length; ++i) {
|
|
writeByte(bytes[i]);
|
|
}
|
|
};
|
|
var slice = HAS_TYPED_ARRAYS ? function (start, length) {
|
|
if (data.buffer.slice) {
|
|
return new Uint8Array(data.buffer.slice(start, start + length));
|
|
} else {
|
|
var x = new Uint8Array(length);
|
|
x.set(new Uint8Array(data.buffer, start, length));
|
|
return x;
|
|
}
|
|
} : function (start, length) {
|
|
return data.slice(start, start + length);
|
|
};
|
|
function eof() {
|
|
return offset >= length;
|
|
}
|
|
function readByte() {
|
|
return offset < length ? data[offset++] : 0;
|
|
}
|
|
function writeByte(b) {
|
|
ensure(offset);
|
|
data[offset++] = b & 255;
|
|
if (offset > length) {
|
|
length = offset;
|
|
}
|
|
}
|
|
function readShort() {
|
|
return readByte() << 8 | readByte();
|
|
}
|
|
function writeShort(w) {
|
|
writeByte(w >> 8);
|
|
writeByte(w);
|
|
}
|
|
function readShort_() {
|
|
var w = readShort();
|
|
return w >= 32768 ? w - 65536 : w;
|
|
}
|
|
function writeShort_(w) {
|
|
writeShort(w < 0 ? w + 65536 : w);
|
|
}
|
|
function readLong() {
|
|
return readShort() * 65536 + readShort();
|
|
}
|
|
function writeLong(w) {
|
|
writeShort(w >>> 16 & 65535);
|
|
writeShort(w & 65535);
|
|
}
|
|
function readLong_() {
|
|
var w = readLong();
|
|
return w >= 2147483648 ? w - 4294967296 : w;
|
|
}
|
|
function writeLong_(w) {
|
|
writeLong(w < 0 ? w + 4294967296 : w);
|
|
}
|
|
function readFixed() {
|
|
return readLong() / 65536;
|
|
}
|
|
function writeFixed(f) {
|
|
writeLong(Math.round(f * 65536));
|
|
}
|
|
function readFixed_() {
|
|
return readLong_() / 65536;
|
|
}
|
|
function writeFixed_(f) {
|
|
writeLong_(Math.round(f * 65536));
|
|
}
|
|
function read(len) {
|
|
return times(len, readByte);
|
|
}
|
|
function readString(len) {
|
|
return String.fromCharCode.apply(String, read(len));
|
|
}
|
|
function writeString(str) {
|
|
for (var i = 0; i < str.length; ++i) {
|
|
writeByte(str.charCodeAt(i));
|
|
}
|
|
}
|
|
function times(n, reader) {
|
|
for (var ret = new Array(n), i = 0; i < n; ++i) {
|
|
ret[i] = reader();
|
|
}
|
|
return ret;
|
|
}
|
|
var stream = {
|
|
eof: eof,
|
|
readByte: readByte,
|
|
writeByte: writeByte,
|
|
readShort: readShort,
|
|
writeShort: writeShort,
|
|
readLong: readLong,
|
|
writeLong: writeLong,
|
|
readFixed: readFixed,
|
|
writeFixed: writeFixed,
|
|
readShort_: readShort_,
|
|
writeShort_: writeShort_,
|
|
readLong_: readLong_,
|
|
writeLong_: writeLong_,
|
|
readFixed_: readFixed_,
|
|
writeFixed_: writeFixed_,
|
|
read: read,
|
|
write: write,
|
|
readString: readString,
|
|
writeString: writeString,
|
|
times: times,
|
|
get: get,
|
|
slice: slice,
|
|
offset: function (pos) {
|
|
if (pos != null) {
|
|
offset = pos;
|
|
return stream;
|
|
}
|
|
return offset;
|
|
},
|
|
skip: function (nbytes) {
|
|
offset += nbytes;
|
|
},
|
|
toString: function () {
|
|
throw new Error('FIX CALLER. BinaryStream is no longer convertible to string!');
|
|
},
|
|
length: function () {
|
|
return length;
|
|
},
|
|
saveExcursion: function (f) {
|
|
var pos = offset;
|
|
try {
|
|
return f();
|
|
} finally {
|
|
offset = pos;
|
|
}
|
|
},
|
|
writeBase64: function (base64) {
|
|
if (window.atob) {
|
|
writeString(window.atob(base64));
|
|
} else {
|
|
write(BASE64.decode(base64));
|
|
}
|
|
},
|
|
base64: function () {
|
|
return BASE64.encode(get());
|
|
}
|
|
};
|
|
return stream;
|
|
}
|
|
function unquote(str) {
|
|
return str.replace(/^\s*(['"])(.*)\1\s*$/, '$2');
|
|
}
|
|
function parseFontDef(fontdef) {
|
|
var rx = /^\s*((normal|italic)\s+)?((normal|small-caps)\s+)?((normal|bold|\d+)\s+)?(([0-9.]+)(px|pt))(\/(([0-9.]+)(px|pt)|normal))?\s+(.*?)\s*$/i;
|
|
var m = rx.exec(fontdef);
|
|
if (!m) {
|
|
return {
|
|
fontSize: 12,
|
|
fontFamily: 'sans-serif'
|
|
};
|
|
}
|
|
var fontSize = m[8] ? parseInt(m[8], 10) : 12;
|
|
return {
|
|
italic: m[2] && m[2].toLowerCase() == 'italic',
|
|
variant: m[4],
|
|
bold: m[6] && /bold|700/i.test(m[6]),
|
|
fontSize: fontSize,
|
|
lineHeight: m[12] ? m[12] == 'normal' ? fontSize : parseInt(m[12], 10) : null,
|
|
fontFamily: m[14].split(/\s*,\s*/g).map(unquote)
|
|
};
|
|
}
|
|
function getFontURL(style) {
|
|
function mkFamily(name) {
|
|
if (style.bold) {
|
|
name += '|bold';
|
|
}
|
|
if (style.italic) {
|
|
name += '|italic';
|
|
}
|
|
return name.toLowerCase();
|
|
}
|
|
var fontFamily = style.fontFamily;
|
|
var name, url;
|
|
if (fontFamily instanceof Array) {
|
|
for (var i = 0; i < fontFamily.length; ++i) {
|
|
name = mkFamily(fontFamily[i]);
|
|
url = FONT_MAPPINGS[name];
|
|
if (url) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
url = FONT_MAPPINGS[fontFamily.toLowerCase()];
|
|
}
|
|
while (typeof url == 'function') {
|
|
url = url();
|
|
}
|
|
if (!url) {
|
|
url = 'Times-Roman';
|
|
}
|
|
return url;
|
|
}
|
|
var FONT_MAPPINGS = {
|
|
'serif': 'Times-Roman',
|
|
'serif|bold': 'Times-Bold',
|
|
'serif|italic': 'Times-Italic',
|
|
'serif|bold|italic': 'Times-BoldItalic',
|
|
'sans-serif': 'Helvetica',
|
|
'sans-serif|bold': 'Helvetica-Bold',
|
|
'sans-serif|italic': 'Helvetica-Oblique',
|
|
'sans-serif|bold|italic': 'Helvetica-BoldOblique',
|
|
'monospace': 'Courier',
|
|
'monospace|bold': 'Courier-Bold',
|
|
'monospace|italic': 'Courier-Oblique',
|
|
'monospace|bold|italic': 'Courier-BoldOblique',
|
|
'zapfdingbats': 'ZapfDingbats',
|
|
'zapfdingbats|bold': 'ZapfDingbats',
|
|
'zapfdingbats|italic': 'ZapfDingbats',
|
|
'zapfdingbats|bold|italic': 'ZapfDingbats'
|
|
};
|
|
function fontAlias(alias, name) {
|
|
alias = alias.toLowerCase();
|
|
FONT_MAPPINGS[alias] = function () {
|
|
return FONT_MAPPINGS[name];
|
|
};
|
|
FONT_MAPPINGS[alias + '|bold'] = function () {
|
|
return FONT_MAPPINGS[name + '|bold'];
|
|
};
|
|
FONT_MAPPINGS[alias + '|italic'] = function () {
|
|
return FONT_MAPPINGS[name + '|italic'];
|
|
};
|
|
FONT_MAPPINGS[alias + '|bold|italic'] = function () {
|
|
return FONT_MAPPINGS[name + '|bold|italic'];
|
|
};
|
|
}
|
|
fontAlias('Times New Roman', 'serif');
|
|
fontAlias('Courier New', 'monospace');
|
|
fontAlias('Arial', 'sans-serif');
|
|
fontAlias('Helvetica', 'sans-serif');
|
|
fontAlias('Verdana', 'sans-serif');
|
|
fontAlias('Tahoma', 'sans-serif');
|
|
fontAlias('Georgia', 'sans-serif');
|
|
fontAlias('Monaco', 'monospace');
|
|
fontAlias('Andale Mono', 'monospace');
|
|
function defineFont(name, url) {
|
|
if (arguments.length == 1) {
|
|
for (var i in name) {
|
|
if (hasOwnProperty(name, i)) {
|
|
defineFont(i, name[i]);
|
|
}
|
|
}
|
|
} else {
|
|
name = name.toLowerCase();
|
|
FONT_MAPPINGS[name] = url;
|
|
switch (name) {
|
|
case 'dejavu sans':
|
|
FONT_MAPPINGS['sans-serif'] = url;
|
|
break;
|
|
case 'dejavu sans|bold':
|
|
FONT_MAPPINGS['sans-serif|bold'] = url;
|
|
break;
|
|
case 'dejavu sans|italic':
|
|
FONT_MAPPINGS['sans-serif|italic'] = url;
|
|
break;
|
|
case 'dejavu sans|bold|italic':
|
|
FONT_MAPPINGS['sans-serif|bold|italic'] = url;
|
|
break;
|
|
case 'dejavu serif':
|
|
FONT_MAPPINGS['serif'] = url;
|
|
break;
|
|
case 'dejavu serif|bold':
|
|
FONT_MAPPINGS['serif|bold'] = url;
|
|
break;
|
|
case 'dejavu serif|italic':
|
|
FONT_MAPPINGS['serif|italic'] = url;
|
|
break;
|
|
case 'dejavu serif|bold|italic':
|
|
FONT_MAPPINGS['serif|bold|italic'] = url;
|
|
break;
|
|
case 'dejavu mono':
|
|
FONT_MAPPINGS['monospace'] = url;
|
|
break;
|
|
case 'dejavu mono|bold':
|
|
FONT_MAPPINGS['monospace|bold'] = url;
|
|
break;
|
|
case 'dejavu mono|italic':
|
|
FONT_MAPPINGS['monospace|italic'] = url;
|
|
break;
|
|
case 'dejavu mono|bold|italic':
|
|
FONT_MAPPINGS['monospace|bold|italic'] = url;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
kendo.pdf = {
|
|
Document: PDFDocument,
|
|
BinaryStream: BinaryStream,
|
|
defineFont: defineFont,
|
|
parseFontDef: parseFontDef,
|
|
getFontURL: getFontURL,
|
|
loadFonts: loadFonts,
|
|
loadImages: loadImages,
|
|
getPaperOptions: getPaperOptions,
|
|
TEXT_RENDERING_MODE: {
|
|
fill: 0,
|
|
stroke: 1,
|
|
fillAndStroke: 2,
|
|
invisible: 3,
|
|
fillAndClip: 4,
|
|
strokeAndClip: 5,
|
|
fillStrokeClip: 6,
|
|
clip: 7
|
|
}
|
|
};
|
|
function mmul(a, b) {
|
|
var a1 = a[0], b1 = a[1], c1 = a[2], d1 = a[3], e1 = a[4], f1 = a[5];
|
|
var a2 = b[0], b2 = b[1], c2 = b[2], d2 = b[3], e2 = b[4], f2 = b[5];
|
|
return [
|
|
a1 * a2 + b1 * c2,
|
|
a1 * b2 + b1 * d2,
|
|
c1 * a2 + d1 * c2,
|
|
c1 * b2 + d1 * d2,
|
|
e1 * a2 + f1 * c2 + e2,
|
|
e1 * b2 + f1 * d2 + f2
|
|
];
|
|
}
|
|
function isIdentityMatrix(m) {
|
|
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
|
|
}
|
|
}(window, parseFloat));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('pdf/ttf', [
|
|
'pdf/core',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
(function (window) {
|
|
'use strict';
|
|
function hasOwnProperty(obj, key) {
|
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
}
|
|
function sortedKeys(obj) {
|
|
return Object.keys(obj).sort(function (a, b) {
|
|
return a - b;
|
|
}).map(parseFloat);
|
|
}
|
|
var PDF = window.kendo.pdf;
|
|
var BinaryStream = PDF.BinaryStream;
|
|
function Directory(data) {
|
|
this.raw = data;
|
|
this.scalerType = data.readLong();
|
|
this.tableCount = data.readShort();
|
|
this.searchRange = data.readShort();
|
|
this.entrySelector = data.readShort();
|
|
this.rangeShift = data.readShort();
|
|
var tables = this.tables = {};
|
|
for (var i = 0; i < this.tableCount; ++i) {
|
|
var entry = {
|
|
tag: data.readString(4),
|
|
checksum: data.readLong(),
|
|
offset: data.readLong(),
|
|
length: data.readLong()
|
|
};
|
|
tables[entry.tag] = entry;
|
|
}
|
|
}
|
|
Directory.prototype = {
|
|
readTable: function (name, Ctor) {
|
|
var def = this.tables[name];
|
|
if (!def) {
|
|
throw new Error('Table ' + name + ' not found in directory');
|
|
}
|
|
return this[name] = def.table = new Ctor(this, def);
|
|
},
|
|
render: function (tables) {
|
|
var tableCount = Object.keys(tables).length;
|
|
var maxpow2 = Math.pow(2, Math.floor(Math.log(tableCount) / Math.LN2));
|
|
var searchRange = maxpow2 * 16;
|
|
var entrySelector = Math.floor(Math.log(maxpow2) / Math.LN2);
|
|
var rangeShift = tableCount * 16 - searchRange;
|
|
var out = BinaryStream();
|
|
out.writeLong(this.scalerType);
|
|
out.writeShort(tableCount);
|
|
out.writeShort(searchRange);
|
|
out.writeShort(entrySelector);
|
|
out.writeShort(rangeShift);
|
|
var directoryLength = tableCount * 16;
|
|
var offset = out.offset() + directoryLength;
|
|
var headOffset = null;
|
|
var tableData = BinaryStream();
|
|
for (var tag in tables) {
|
|
if (hasOwnProperty(tables, tag)) {
|
|
var table = tables[tag];
|
|
out.writeString(tag);
|
|
out.writeLong(this.checksum(table));
|
|
out.writeLong(offset);
|
|
out.writeLong(table.length);
|
|
tableData.write(table);
|
|
if (tag == 'head') {
|
|
headOffset = offset;
|
|
}
|
|
offset += table.length;
|
|
while (offset % 4) {
|
|
tableData.writeByte(0);
|
|
offset++;
|
|
}
|
|
}
|
|
}
|
|
out.write(tableData.get());
|
|
var sum = this.checksum(out.get());
|
|
var adjustment = 2981146554 - sum;
|
|
out.offset(headOffset + 8);
|
|
out.writeLong(adjustment);
|
|
return out.get();
|
|
},
|
|
checksum: function (data) {
|
|
data = BinaryStream(data);
|
|
var sum = 0;
|
|
while (!data.eof()) {
|
|
sum += data.readLong();
|
|
}
|
|
return sum & 4294967295;
|
|
}
|
|
};
|
|
function deftable(methods) {
|
|
function Ctor(file, def) {
|
|
this.definition = def;
|
|
this.length = def.length;
|
|
this.offset = def.offset;
|
|
this.file = file;
|
|
this.rawData = file.raw;
|
|
this.parse(file.raw);
|
|
}
|
|
Ctor.prototype.raw = function () {
|
|
return this.rawData.slice(this.offset, this.length);
|
|
};
|
|
for (var i in methods) {
|
|
if (hasOwnProperty(methods, i)) {
|
|
Ctor[i] = Ctor.prototype[i] = methods[i];
|
|
}
|
|
}
|
|
return Ctor;
|
|
}
|
|
var HeadTable = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
this.version = data.readLong();
|
|
this.revision = data.readLong();
|
|
this.checkSumAdjustment = data.readLong();
|
|
this.magicNumber = data.readLong();
|
|
this.flags = data.readShort();
|
|
this.unitsPerEm = data.readShort();
|
|
this.created = data.read(8);
|
|
this.modified = data.read(8);
|
|
this.xMin = data.readShort_();
|
|
this.yMin = data.readShort_();
|
|
this.xMax = data.readShort_();
|
|
this.yMax = data.readShort_();
|
|
this.macStyle = data.readShort();
|
|
this.lowestRecPPEM = data.readShort();
|
|
this.fontDirectionHint = data.readShort_();
|
|
this.indexToLocFormat = data.readShort_();
|
|
this.glyphDataFormat = data.readShort_();
|
|
},
|
|
render: function (indexToLocFormat) {
|
|
var out = BinaryStream();
|
|
out.writeLong(this.version);
|
|
out.writeLong(this.revision);
|
|
out.writeLong(0);
|
|
out.writeLong(this.magicNumber);
|
|
out.writeShort(this.flags);
|
|
out.writeShort(this.unitsPerEm);
|
|
out.write(this.created);
|
|
out.write(this.modified);
|
|
out.writeShort_(this.xMin);
|
|
out.writeShort_(this.yMin);
|
|
out.writeShort_(this.xMax);
|
|
out.writeShort_(this.yMax);
|
|
out.writeShort(this.macStyle);
|
|
out.writeShort(this.lowestRecPPEM);
|
|
out.writeShort_(this.fontDirectionHint);
|
|
out.writeShort_(indexToLocFormat);
|
|
out.writeShort_(this.glyphDataFormat);
|
|
return out.get();
|
|
}
|
|
});
|
|
var LocaTable = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
var format = this.file.head.indexToLocFormat;
|
|
if (format === 0) {
|
|
this.offsets = data.times(this.length / 2, function () {
|
|
return 2 * data.readShort();
|
|
});
|
|
} else {
|
|
this.offsets = data.times(this.length / 4, data.readLong);
|
|
}
|
|
},
|
|
offsetOf: function (id) {
|
|
return this.offsets[id];
|
|
},
|
|
lengthOf: function (id) {
|
|
return this.offsets[id + 1] - this.offsets[id];
|
|
},
|
|
render: function (offsets) {
|
|
var out = BinaryStream();
|
|
var needsLongFormat = offsets[offsets.length - 1] > 65535;
|
|
for (var i = 0; i < offsets.length; ++i) {
|
|
if (needsLongFormat) {
|
|
out.writeLong(offsets[i]);
|
|
} else {
|
|
out.writeShort(offsets[i] / 2);
|
|
}
|
|
}
|
|
return {
|
|
format: needsLongFormat ? 1 : 0,
|
|
table: out.get()
|
|
};
|
|
}
|
|
});
|
|
var HheaTable = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
this.version = data.readLong();
|
|
this.ascent = data.readShort_();
|
|
this.descent = data.readShort_();
|
|
this.lineGap = data.readShort_();
|
|
this.advanceWidthMax = data.readShort();
|
|
this.minLeftSideBearing = data.readShort_();
|
|
this.minRightSideBearing = data.readShort_();
|
|
this.xMaxExtent = data.readShort_();
|
|
this.caretSlopeRise = data.readShort_();
|
|
this.caretSlopeRun = data.readShort_();
|
|
this.caretOffset = data.readShort_();
|
|
data.skip(4 * 2);
|
|
this.metricDataFormat = data.readShort_();
|
|
this.numOfLongHorMetrics = data.readShort();
|
|
},
|
|
render: function (ids) {
|
|
var out = BinaryStream();
|
|
out.writeLong(this.version);
|
|
out.writeShort_(this.ascent);
|
|
out.writeShort_(this.descent);
|
|
out.writeShort_(this.lineGap);
|
|
out.writeShort(this.advanceWidthMax);
|
|
out.writeShort_(this.minLeftSideBearing);
|
|
out.writeShort_(this.minRightSideBearing);
|
|
out.writeShort_(this.xMaxExtent);
|
|
out.writeShort_(this.caretSlopeRise);
|
|
out.writeShort_(this.caretSlopeRun);
|
|
out.writeShort_(this.caretOffset);
|
|
out.write([
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
]);
|
|
out.writeShort_(this.metricDataFormat);
|
|
out.writeShort(ids.length);
|
|
return out.get();
|
|
}
|
|
});
|
|
var MaxpTable = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
this.version = data.readLong();
|
|
this.numGlyphs = data.readShort();
|
|
this.maxPoints = data.readShort();
|
|
this.maxContours = data.readShort();
|
|
this.maxComponentPoints = data.readShort();
|
|
this.maxComponentContours = data.readShort();
|
|
this.maxZones = data.readShort();
|
|
this.maxTwilightPoints = data.readShort();
|
|
this.maxStorage = data.readShort();
|
|
this.maxFunctionDefs = data.readShort();
|
|
this.maxInstructionDefs = data.readShort();
|
|
this.maxStackElements = data.readShort();
|
|
this.maxSizeOfInstructions = data.readShort();
|
|
this.maxComponentElements = data.readShort();
|
|
this.maxComponentDepth = data.readShort();
|
|
},
|
|
render: function (glyphIds) {
|
|
var out = BinaryStream();
|
|
out.writeLong(this.version);
|
|
out.writeShort(glyphIds.length);
|
|
out.writeShort(this.maxPoints);
|
|
out.writeShort(this.maxContours);
|
|
out.writeShort(this.maxComponentPoints);
|
|
out.writeShort(this.maxComponentContours);
|
|
out.writeShort(this.maxZones);
|
|
out.writeShort(this.maxTwilightPoints);
|
|
out.writeShort(this.maxStorage);
|
|
out.writeShort(this.maxFunctionDefs);
|
|
out.writeShort(this.maxInstructionDefs);
|
|
out.writeShort(this.maxStackElements);
|
|
out.writeShort(this.maxSizeOfInstructions);
|
|
out.writeShort(this.maxComponentElements);
|
|
out.writeShort(this.maxComponentDepth);
|
|
return out.get();
|
|
}
|
|
});
|
|
var HmtxTable = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
var dir = this.file, hhea = dir.hhea;
|
|
this.metrics = data.times(hhea.numOfLongHorMetrics, function () {
|
|
return {
|
|
advance: data.readShort(),
|
|
lsb: data.readShort_()
|
|
};
|
|
});
|
|
var lsbCount = dir.maxp.numGlyphs - dir.hhea.numOfLongHorMetrics;
|
|
this.leftSideBearings = data.times(lsbCount, data.readShort_);
|
|
},
|
|
forGlyph: function (id) {
|
|
var metrics = this.metrics;
|
|
var n = metrics.length;
|
|
if (id < n) {
|
|
return metrics[id];
|
|
}
|
|
return {
|
|
advance: metrics[n - 1].advance,
|
|
lsb: this.leftSideBearings[id - n]
|
|
};
|
|
},
|
|
render: function (glyphIds) {
|
|
var out = BinaryStream();
|
|
for (var i = 0; i < glyphIds.length; ++i) {
|
|
var m = this.forGlyph(glyphIds[i]);
|
|
out.writeShort(m.advance);
|
|
out.writeShort_(m.lsb);
|
|
}
|
|
return out.get();
|
|
}
|
|
});
|
|
var GlyfTable = function () {
|
|
function SimpleGlyph(raw) {
|
|
this.raw = raw;
|
|
}
|
|
SimpleGlyph.prototype = {
|
|
compound: false,
|
|
render: function () {
|
|
return this.raw.get();
|
|
}
|
|
};
|
|
var ARG_1_AND_2_ARE_WORDS = 1;
|
|
var WE_HAVE_A_SCALE = 8;
|
|
var MORE_COMPONENTS = 32;
|
|
var WE_HAVE_AN_X_AND_Y_SCALE = 64;
|
|
var WE_HAVE_A_TWO_BY_TWO = 128;
|
|
function CompoundGlyph(data) {
|
|
this.raw = data;
|
|
var ids = this.glyphIds = [];
|
|
var offsets = this.idOffsets = [];
|
|
while (true) {
|
|
var flags = data.readShort();
|
|
offsets.push(data.offset());
|
|
ids.push(data.readShort());
|
|
if (!(flags & MORE_COMPONENTS)) {
|
|
break;
|
|
}
|
|
data.skip(flags & ARG_1_AND_2_ARE_WORDS ? 4 : 2);
|
|
if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|
data.skip(8);
|
|
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
data.skip(4);
|
|
} else if (flags & WE_HAVE_A_SCALE) {
|
|
data.skip(2);
|
|
}
|
|
}
|
|
}
|
|
CompoundGlyph.prototype = {
|
|
compound: true,
|
|
render: function (old2new) {
|
|
var out = BinaryStream(this.raw.get());
|
|
for (var i = 0; i < this.glyphIds.length; ++i) {
|
|
var id = this.glyphIds[i];
|
|
out.offset(this.idOffsets[i]);
|
|
out.writeShort(old2new[id]);
|
|
}
|
|
return out.get();
|
|
}
|
|
};
|
|
return deftable({
|
|
parse: function () {
|
|
this.cache = {};
|
|
},
|
|
glyphFor: function (id) {
|
|
var cache = this.cache;
|
|
if (hasOwnProperty(cache, id)) {
|
|
return cache[id];
|
|
}
|
|
var loca = this.file.loca;
|
|
var length = loca.lengthOf(id);
|
|
if (length === 0) {
|
|
return cache[id] = null;
|
|
}
|
|
var data = this.rawData;
|
|
var offset = this.offset + loca.offsetOf(id);
|
|
var raw = BinaryStream(data.slice(offset, length));
|
|
var numberOfContours = raw.readShort_();
|
|
var xMin = raw.readShort_();
|
|
var yMin = raw.readShort_();
|
|
var xMax = raw.readShort_();
|
|
var yMax = raw.readShort_();
|
|
var glyph = cache[id] = numberOfContours == -1 ? new CompoundGlyph(raw) : new SimpleGlyph(raw);
|
|
glyph.numberOfContours = numberOfContours;
|
|
glyph.xMin = xMin;
|
|
glyph.yMin = yMin;
|
|
glyph.xMax = xMax;
|
|
glyph.yMax = yMax;
|
|
return glyph;
|
|
},
|
|
render: function (glyphs, oldIds, old2new) {
|
|
var out = BinaryStream(), offsets = [];
|
|
for (var i = 0; i < oldIds.length; ++i) {
|
|
var id = oldIds[i];
|
|
var glyph = glyphs[id];
|
|
offsets.push(out.offset());
|
|
if (glyph) {
|
|
out.write(glyph.render(old2new));
|
|
}
|
|
}
|
|
offsets.push(out.offset());
|
|
return {
|
|
table: out.get(),
|
|
offsets: offsets
|
|
};
|
|
}
|
|
});
|
|
}();
|
|
var NameTable = function () {
|
|
function NameEntry(text, entry) {
|
|
this.text = text;
|
|
this.length = text.length;
|
|
this.platformID = entry.platformID;
|
|
this.platformSpecificID = entry.platformSpecificID;
|
|
this.languageID = entry.languageID;
|
|
this.nameID = entry.nameID;
|
|
}
|
|
return deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
data.readShort();
|
|
var count = data.readShort();
|
|
var stringOffset = this.offset + data.readShort();
|
|
var nameRecords = data.times(count, function () {
|
|
return {
|
|
platformID: data.readShort(),
|
|
platformSpecificID: data.readShort(),
|
|
languageID: data.readShort(),
|
|
nameID: data.readShort(),
|
|
length: data.readShort(),
|
|
offset: data.readShort() + stringOffset
|
|
};
|
|
});
|
|
var strings = this.strings = {};
|
|
for (var i = 0; i < nameRecords.length; ++i) {
|
|
var rec = nameRecords[i];
|
|
data.offset(rec.offset);
|
|
var text = data.readString(rec.length);
|
|
if (!strings[rec.nameID]) {
|
|
strings[rec.nameID] = [];
|
|
}
|
|
strings[rec.nameID].push(new NameEntry(text, rec));
|
|
}
|
|
this.postscriptEntry = strings[6][0];
|
|
this.postscriptName = this.postscriptEntry.text.replace(/[^\x20-\x7F]/g, '');
|
|
},
|
|
render: function (psName) {
|
|
var strings = this.strings;
|
|
var strCount = 0;
|
|
for (var i in strings) {
|
|
if (hasOwnProperty(strings, i)) {
|
|
strCount += strings[i].length;
|
|
}
|
|
}
|
|
var out = BinaryStream();
|
|
var strTable = BinaryStream();
|
|
out.writeShort(0);
|
|
out.writeShort(strCount);
|
|
out.writeShort(6 + 12 * strCount);
|
|
for (i in strings) {
|
|
if (hasOwnProperty(strings, i)) {
|
|
var list = i == 6 ? [new NameEntry(psName, this.postscriptEntry)] : strings[i];
|
|
for (var j = 0; j < list.length; ++j) {
|
|
var str = list[j];
|
|
out.writeShort(str.platformID);
|
|
out.writeShort(str.platformSpecificID);
|
|
out.writeShort(str.languageID);
|
|
out.writeShort(str.nameID);
|
|
out.writeShort(str.length);
|
|
out.writeShort(strTable.offset());
|
|
strTable.writeString(str.text);
|
|
}
|
|
}
|
|
}
|
|
out.write(strTable.get());
|
|
return out.get();
|
|
}
|
|
});
|
|
}();
|
|
var PostTable = function () {
|
|
var POSTSCRIPT_GLYPHS = '.notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar percent ampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash zero one two three four five six seven eight nine colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde Adieresis Aring Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis dagger degree cent sterling section bullet paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash infinity plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft guillemotright ellipsis nonbreakingspace Agrave Atilde Otilde OE oe endash emdash quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright fi fl daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex apple Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla hungarumlaut ogonek caron Lslash lslash Scaron scaron Zcaron zcaron brokenbar Eth eth Yacute yacute Thorn thorn minus multiply onesuperior twosuperior threesuperior onehalf onequarter threequarters franc Gbreve gbreve Idotaccent Scedilla scedilla Cacute cacute Ccaron ccaron dcroat'.split(/\s+/g);
|
|
return deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
this.format = data.readLong();
|
|
this.italicAngle = data.readFixed_();
|
|
this.underlinePosition = data.readShort_();
|
|
this.underlineThickness = data.readShort_();
|
|
this.isFixedPitch = data.readLong();
|
|
this.minMemType42 = data.readLong();
|
|
this.maxMemType42 = data.readLong();
|
|
this.minMemType1 = data.readLong();
|
|
this.maxMemType1 = data.readLong();
|
|
var numberOfGlyphs;
|
|
switch (this.format) {
|
|
case 65536:
|
|
case 196608:
|
|
break;
|
|
case 131072:
|
|
numberOfGlyphs = data.readShort();
|
|
this.glyphNameIndex = data.times(numberOfGlyphs, data.readShort);
|
|
this.names = [];
|
|
var limit = this.offset + this.length;
|
|
while (data.offset() < limit) {
|
|
this.names.push(data.readString(data.readByte()));
|
|
}
|
|
break;
|
|
case 151552:
|
|
numberOfGlyphs = data.readShort();
|
|
this.offsets = data.read(numberOfGlyphs);
|
|
break;
|
|
case 262144:
|
|
this.map = data.times(this.file.maxp.numGlyphs, data.readShort);
|
|
break;
|
|
}
|
|
},
|
|
glyphFor: function (code) {
|
|
switch (this.format) {
|
|
case 65536:
|
|
return POSTSCRIPT_GLYPHS[code] || '.notdef';
|
|
case 131072:
|
|
var index = this.glyphNameIndex[code];
|
|
if (index < POSTSCRIPT_GLYPHS.length) {
|
|
return POSTSCRIPT_GLYPHS[index];
|
|
}
|
|
return this.names[index - POSTSCRIPT_GLYPHS.length] || '.notdef';
|
|
case 151552:
|
|
case 196608:
|
|
return '.notdef';
|
|
case 262144:
|
|
return this.map[code] || 65535;
|
|
}
|
|
},
|
|
render: function (mapping) {
|
|
if (this.format == 196608) {
|
|
return this.raw();
|
|
}
|
|
var out = BinaryStream(this.rawData.slice(this.offset, 32));
|
|
out.writeLong(131072);
|
|
out.offset(32);
|
|
var indexes = [];
|
|
var strings = [];
|
|
for (var i = 0; i < mapping.length; ++i) {
|
|
var id = mapping[i];
|
|
var post = this.glyphFor(id);
|
|
var index = POSTSCRIPT_GLYPHS.indexOf(post);
|
|
if (index >= 0) {
|
|
indexes.push(index);
|
|
} else {
|
|
indexes.push(POSTSCRIPT_GLYPHS.length + strings.length);
|
|
strings.push(post);
|
|
}
|
|
}
|
|
out.writeShort(mapping.length);
|
|
for (i = 0; i < indexes.length; ++i) {
|
|
out.writeShort(indexes[i]);
|
|
}
|
|
for (i = 0; i < strings.length; ++i) {
|
|
out.writeByte(strings[i].length);
|
|
out.writeString(strings[i]);
|
|
}
|
|
return out.get();
|
|
}
|
|
});
|
|
}();
|
|
var CmapTable = function () {
|
|
function CmapEntry(data, offset, codeMap) {
|
|
var self = this;
|
|
self.platformID = data.readShort();
|
|
self.platformSpecificID = data.readShort();
|
|
self.offset = offset + data.readLong();
|
|
data.saveExcursion(function () {
|
|
var code;
|
|
data.offset(self.offset);
|
|
self.format = data.readShort();
|
|
switch (self.format) {
|
|
case 0:
|
|
self.length = data.readShort();
|
|
self.language = data.readShort();
|
|
for (var i = 0; i < 256; ++i) {
|
|
codeMap[i] = data.readByte();
|
|
}
|
|
break;
|
|
case 4:
|
|
self.length = data.readShort();
|
|
self.language = data.readShort();
|
|
var segCount = data.readShort() / 2;
|
|
data.skip(6);
|
|
var endCode = data.times(segCount, data.readShort);
|
|
data.skip(2);
|
|
var startCode = data.times(segCount, data.readShort);
|
|
var idDelta = data.times(segCount, data.readShort_);
|
|
var idRangeOffset = data.times(segCount, data.readShort);
|
|
var count = (self.length + self.offset - data.offset()) / 2;
|
|
var glyphIds = data.times(count, data.readShort);
|
|
for (i = 0; i < segCount; ++i) {
|
|
var start = startCode[i], end = endCode[i];
|
|
for (code = start; code <= end; ++code) {
|
|
var glyphId;
|
|
if (idRangeOffset[i] === 0) {
|
|
glyphId = code + idDelta[i];
|
|
} else {
|
|
var index = idRangeOffset[i] / 2 - (segCount - i) + (code - start);
|
|
glyphId = glyphIds[index] || 0;
|
|
if (glyphId !== 0) {
|
|
glyphId += idDelta[i];
|
|
}
|
|
}
|
|
codeMap[code] = glyphId & 65535;
|
|
}
|
|
}
|
|
break;
|
|
case 6:
|
|
self.length = data.readShort();
|
|
self.language = data.readShort();
|
|
code = data.readShort();
|
|
var length = data.readShort();
|
|
while (length-- > 0) {
|
|
codeMap[code++] = data.readShort();
|
|
}
|
|
break;
|
|
case 12:
|
|
data.readShort();
|
|
self.length = data.readLong();
|
|
self.language = data.readLong();
|
|
var ngroups = data.readLong();
|
|
while (ngroups-- > 0) {
|
|
code = data.readLong();
|
|
var endCharCode = data.readLong();
|
|
var glyphCode = data.readLong();
|
|
while (code <= endCharCode) {
|
|
codeMap[code++] = glyphCode++;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (window.console) {
|
|
window.console.error('Unhandled CMAP format: ' + self.format);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function renderCharmap(ncid2ogid, ogid2ngid) {
|
|
var codes = sortedKeys(ncid2ogid);
|
|
var startCodes = [];
|
|
var endCodes = [];
|
|
var last = null;
|
|
var diff = null;
|
|
function new_gid(charcode) {
|
|
return ogid2ngid[ncid2ogid[charcode]];
|
|
}
|
|
for (var i = 0; i < codes.length; ++i) {
|
|
var code = codes[i];
|
|
var gid = new_gid(code);
|
|
var delta = gid - code;
|
|
if (last == null || delta !== diff) {
|
|
if (last) {
|
|
endCodes.push(last);
|
|
}
|
|
startCodes.push(code);
|
|
diff = delta;
|
|
}
|
|
last = code;
|
|
}
|
|
if (last) {
|
|
endCodes.push(last);
|
|
}
|
|
endCodes.push(65535);
|
|
startCodes.push(65535);
|
|
var segCount = startCodes.length;
|
|
var segCountX2 = segCount * 2;
|
|
var searchRange = 2 * Math.pow(2, Math.floor(Math.log(segCount) / Math.LN2));
|
|
var entrySelector = Math.log(searchRange / 2) / Math.LN2;
|
|
var rangeShift = segCountX2 - searchRange;
|
|
var deltas = [];
|
|
var rangeOffsets = [];
|
|
var glyphIds = [];
|
|
for (i = 0; i < segCount; ++i) {
|
|
var startCode = startCodes[i];
|
|
var endCode = endCodes[i];
|
|
if (startCode == 65535) {
|
|
deltas.push(0);
|
|
rangeOffsets.push(0);
|
|
break;
|
|
}
|
|
var startGlyph = new_gid(startCode);
|
|
if (startCode - startGlyph >= 32768) {
|
|
deltas.push(0);
|
|
rangeOffsets.push(2 * (glyphIds.length + segCount - i));
|
|
for (var j = startCode; j <= endCode; ++j) {
|
|
glyphIds.push(new_gid(j));
|
|
}
|
|
} else {
|
|
deltas.push(startGlyph - startCode);
|
|
rangeOffsets.push(0);
|
|
}
|
|
}
|
|
var out = BinaryStream();
|
|
out.writeShort(3);
|
|
out.writeShort(1);
|
|
out.writeLong(12);
|
|
out.writeShort(4);
|
|
out.writeShort(16 + segCount * 8 + glyphIds.length * 2);
|
|
out.writeShort(0);
|
|
out.writeShort(segCountX2);
|
|
out.writeShort(searchRange);
|
|
out.writeShort(entrySelector);
|
|
out.writeShort(rangeShift);
|
|
endCodes.forEach(out.writeShort);
|
|
out.writeShort(0);
|
|
startCodes.forEach(out.writeShort);
|
|
deltas.forEach(out.writeShort_);
|
|
rangeOffsets.forEach(out.writeShort);
|
|
glyphIds.forEach(out.writeShort);
|
|
return out.get();
|
|
}
|
|
return deftable({
|
|
parse: function (data) {
|
|
var self = this;
|
|
var offset = self.offset;
|
|
data.offset(offset);
|
|
self.codeMap = {};
|
|
self.version = data.readShort();
|
|
var tableCount = data.readShort();
|
|
self.tables = data.times(tableCount, function () {
|
|
return new CmapEntry(data, offset, self.codeMap);
|
|
});
|
|
},
|
|
render: function (ncid2ogid, ogid2ngid) {
|
|
var out = BinaryStream();
|
|
out.writeShort(0);
|
|
out.writeShort(1);
|
|
out.write(renderCharmap(ncid2ogid, ogid2ngid));
|
|
return out.get();
|
|
}
|
|
});
|
|
}();
|
|
var OS2Table = deftable({
|
|
parse: function (data) {
|
|
data.offset(this.offset);
|
|
this.version = data.readShort();
|
|
this.averageCharWidth = data.readShort_();
|
|
this.weightClass = data.readShort();
|
|
this.widthClass = data.readShort();
|
|
this.type = data.readShort();
|
|
this.ySubscriptXSize = data.readShort_();
|
|
this.ySubscriptYSize = data.readShort_();
|
|
this.ySubscriptXOffset = data.readShort_();
|
|
this.ySubscriptYOffset = data.readShort_();
|
|
this.ySuperscriptXSize = data.readShort_();
|
|
this.ySuperscriptYSize = data.readShort_();
|
|
this.ySuperscriptXOffset = data.readShort_();
|
|
this.ySuperscriptYOffset = data.readShort_();
|
|
this.yStrikeoutSize = data.readShort_();
|
|
this.yStrikeoutPosition = data.readShort_();
|
|
this.familyClass = data.readShort_();
|
|
this.panose = data.times(10, data.readByte);
|
|
this.charRange = data.times(4, data.readLong);
|
|
this.vendorID = data.readString(4);
|
|
this.selection = data.readShort();
|
|
this.firstCharIndex = data.readShort();
|
|
this.lastCharIndex = data.readShort();
|
|
if (this.version > 0) {
|
|
this.ascent = data.readShort_();
|
|
this.descent = data.readShort_();
|
|
this.lineGap = data.readShort_();
|
|
this.winAscent = data.readShort();
|
|
this.winDescent = data.readShort();
|
|
this.codePageRange = data.times(2, data.readLong);
|
|
if (this.version > 1) {
|
|
this.xHeight = data.readShort();
|
|
this.capHeight = data.readShort();
|
|
this.defaultChar = data.readShort();
|
|
this.breakChar = data.readShort();
|
|
this.maxContext = data.readShort();
|
|
}
|
|
}
|
|
},
|
|
render: function () {
|
|
return this.raw();
|
|
}
|
|
});
|
|
var subsetTag = 100000;
|
|
function nextSubsetTag() {
|
|
var ret = '', n = subsetTag + '';
|
|
for (var i = 0; i < n.length; ++i) {
|
|
ret += String.fromCharCode(n.charCodeAt(i) - 48 + 65);
|
|
}
|
|
++subsetTag;
|
|
return ret;
|
|
}
|
|
function Subfont(font) {
|
|
this.font = font;
|
|
this.subset = {};
|
|
this.unicodes = {};
|
|
this.ogid2ngid = { 0: 0 };
|
|
this.ngid2ogid = { 0: 0 };
|
|
this.ncid2ogid = {};
|
|
this.next = this.firstChar = 1;
|
|
this.nextGid = 1;
|
|
this.psName = nextSubsetTag() + '+' + this.font.psName;
|
|
}
|
|
Subfont.prototype = {
|
|
use: function (ch) {
|
|
var self = this;
|
|
if (typeof ch == 'string') {
|
|
return kendo.util.ucs2decode(ch).reduce(function (ret, code) {
|
|
return ret + String.fromCharCode(self.use(code));
|
|
}, '');
|
|
}
|
|
var code = self.unicodes[ch];
|
|
if (!code) {
|
|
code = self.next++;
|
|
self.subset[code] = ch;
|
|
self.unicodes[ch] = code;
|
|
var old_gid = self.font.cmap.codeMap[ch];
|
|
if (old_gid) {
|
|
self.ncid2ogid[code] = old_gid;
|
|
if (self.ogid2ngid[old_gid] == null) {
|
|
var new_gid = self.nextGid++;
|
|
self.ogid2ngid[old_gid] = new_gid;
|
|
self.ngid2ogid[new_gid] = old_gid;
|
|
}
|
|
}
|
|
}
|
|
return code;
|
|
},
|
|
encodeText: function (text) {
|
|
return this.use(text);
|
|
},
|
|
glyphIds: function () {
|
|
return sortedKeys(this.ogid2ngid);
|
|
},
|
|
glyphsFor: function (glyphIds, result) {
|
|
if (!result) {
|
|
result = {};
|
|
}
|
|
for (var i = 0; i < glyphIds.length; ++i) {
|
|
var id = glyphIds[i];
|
|
if (!result[id]) {
|
|
var glyph = result[id] = this.font.glyf.glyphFor(id);
|
|
if (glyph && glyph.compound) {
|
|
this.glyphsFor(glyph.glyphIds, result);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
render: function () {
|
|
var glyphs = this.glyphsFor(this.glyphIds());
|
|
for (var old_gid in glyphs) {
|
|
if (hasOwnProperty(glyphs, old_gid)) {
|
|
old_gid = parseInt(old_gid, 10);
|
|
if (this.ogid2ngid[old_gid] == null) {
|
|
var new_gid = this.nextGid++;
|
|
this.ogid2ngid[old_gid] = new_gid;
|
|
this.ngid2ogid[new_gid] = old_gid;
|
|
}
|
|
}
|
|
}
|
|
var new_gid_ids = sortedKeys(this.ngid2ogid);
|
|
var old_gid_ids = new_gid_ids.map(function (id) {
|
|
return this.ngid2ogid[id];
|
|
}, this);
|
|
var font = this.font;
|
|
var glyf = font.glyf.render(glyphs, old_gid_ids, this.ogid2ngid);
|
|
var loca = font.loca.render(glyf.offsets);
|
|
this.lastChar = this.next - 1;
|
|
var tables = {
|
|
'cmap': CmapTable.render(this.ncid2ogid, this.ogid2ngid),
|
|
'glyf': glyf.table,
|
|
'loca': loca.table,
|
|
'hmtx': font.hmtx.render(old_gid_ids),
|
|
'hhea': font.hhea.render(old_gid_ids),
|
|
'maxp': font.maxp.render(old_gid_ids),
|
|
'post': font.post.render(old_gid_ids),
|
|
'name': font.name.render(this.psName),
|
|
'head': font.head.render(loca.format),
|
|
'OS/2': font.os2.render()
|
|
};
|
|
return this.font.directory.render(tables);
|
|
},
|
|
cidToGidMap: function () {
|
|
var out = BinaryStream(), len = 0;
|
|
for (var cid = this.firstChar; cid < this.next; ++cid) {
|
|
while (len < cid) {
|
|
out.writeShort(0);
|
|
len++;
|
|
}
|
|
var old_gid = this.ncid2ogid[cid];
|
|
if (old_gid) {
|
|
var new_gid = this.ogid2ngid[old_gid];
|
|
out.writeShort(new_gid);
|
|
} else {
|
|
out.writeShort(0);
|
|
}
|
|
len++;
|
|
}
|
|
return out.get();
|
|
}
|
|
};
|
|
function TTFFont(rawData, name) {
|
|
var self = this;
|
|
var data = self.contents = BinaryStream(rawData);
|
|
if (data.readString(4) == 'ttcf') {
|
|
if (!name) {
|
|
throw new Error('Must specify a name for TTC files');
|
|
}
|
|
data.readLong();
|
|
var numFonts = data.readLong();
|
|
for (var i = 0; i < numFonts; ++i) {
|
|
var offset = data.readLong();
|
|
data.saveExcursion(function () {
|
|
data.offset(offset);
|
|
self.parse();
|
|
});
|
|
if (self.psName == name) {
|
|
return;
|
|
}
|
|
}
|
|
throw new Error('Font ' + name + ' not found in collection');
|
|
} else {
|
|
data.offset(0);
|
|
self.parse();
|
|
}
|
|
}
|
|
TTFFont.prototype = {
|
|
parse: function () {
|
|
var dir = this.directory = new Directory(this.contents);
|
|
this.head = dir.readTable('head', HeadTable);
|
|
this.loca = dir.readTable('loca', LocaTable);
|
|
this.hhea = dir.readTable('hhea', HheaTable);
|
|
this.maxp = dir.readTable('maxp', MaxpTable);
|
|
this.hmtx = dir.readTable('hmtx', HmtxTable);
|
|
this.glyf = dir.readTable('glyf', GlyfTable);
|
|
this.name = dir.readTable('name', NameTable);
|
|
this.post = dir.readTable('post', PostTable);
|
|
this.cmap = dir.readTable('cmap', CmapTable);
|
|
this.os2 = dir.readTable('OS/2', OS2Table);
|
|
this.psName = this.name.postscriptName;
|
|
this.ascent = this.os2.ascent || this.hhea.ascent;
|
|
this.descent = this.os2.descent || this.hhea.descent;
|
|
this.lineGap = this.os2.lineGap || this.hhea.lineGap;
|
|
this.scale = 1000 / this.head.unitsPerEm;
|
|
},
|
|
widthOfGlyph: function (glyph) {
|
|
return this.hmtx.forGlyph(glyph).advance * this.scale;
|
|
},
|
|
makeSubset: function () {
|
|
return new Subfont(this);
|
|
}
|
|
};
|
|
PDF.TTFFont = TTFFont;
|
|
}(window));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('pdf/drawing', [
|
|
'kendo.core',
|
|
'kendo.color',
|
|
'kendo.drawing',
|
|
'pdf/core'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo, $) {
|
|
'use strict';
|
|
var drawing = kendo.drawing;
|
|
var geo = kendo.geometry;
|
|
var TEXT_RENDERING_MODE = kendo.pdf.TEXT_RENDERING_MODE;
|
|
var DASH_PATTERNS = {
|
|
dash: [4],
|
|
dashDot: [
|
|
4,
|
|
2,
|
|
1,
|
|
2
|
|
],
|
|
dot: [
|
|
1,
|
|
2
|
|
],
|
|
longDash: [
|
|
8,
|
|
2
|
|
],
|
|
longDashDot: [
|
|
8,
|
|
2,
|
|
1,
|
|
2
|
|
],
|
|
longDashDotDot: [
|
|
8,
|
|
2,
|
|
1,
|
|
2,
|
|
1,
|
|
2
|
|
],
|
|
solid: []
|
|
};
|
|
var LINE_CAP = {
|
|
butt: 0,
|
|
round: 1,
|
|
square: 2
|
|
};
|
|
var LINE_JOIN = {
|
|
miter: 0,
|
|
round: 1,
|
|
bevel: 2
|
|
};
|
|
function render(group, callback) {
|
|
var fonts = [], images = [], options = group.options;
|
|
function getOption(name, defval, hash) {
|
|
if (!hash) {
|
|
hash = options;
|
|
}
|
|
if (hash.pdf && hash.pdf[name] != null) {
|
|
return hash.pdf[name];
|
|
}
|
|
return defval;
|
|
}
|
|
var multiPage = getOption('multiPage');
|
|
group.traverse(function (element) {
|
|
dispatch({
|
|
Image: function (element) {
|
|
if (images.indexOf(element.src()) < 0) {
|
|
images.push(element.src());
|
|
}
|
|
},
|
|
Text: function (element) {
|
|
var style = kendo.pdf.parseFontDef(element.options.font);
|
|
var url = kendo.pdf.getFontURL(style);
|
|
if (fonts.indexOf(url) < 0) {
|
|
fonts.push(url);
|
|
}
|
|
}
|
|
}, element);
|
|
});
|
|
function doIt() {
|
|
if (--count > 0) {
|
|
return;
|
|
}
|
|
var pdf = new kendo.pdf.Document({
|
|
producer: getOption('producer'),
|
|
title: getOption('title'),
|
|
author: getOption('author'),
|
|
subject: getOption('subject'),
|
|
keywords: getOption('keywords'),
|
|
creator: getOption('creator'),
|
|
date: getOption('date')
|
|
});
|
|
function drawPage(group) {
|
|
var options = group.options;
|
|
var tmp = optimize(group);
|
|
var bbox = tmp.bbox;
|
|
group = tmp.root;
|
|
var paperSize = getOption('paperSize', getOption('paperSize', 'auto'), options), addMargin = false;
|
|
if (paperSize == 'auto') {
|
|
if (bbox) {
|
|
var size = bbox.getSize();
|
|
paperSize = [
|
|
size.width,
|
|
size.height
|
|
];
|
|
addMargin = true;
|
|
var origin = bbox.getOrigin();
|
|
tmp = new drawing.Group();
|
|
tmp.transform(new geo.Matrix(1, 0, 0, 1, -origin.x, -origin.y));
|
|
tmp.append(group);
|
|
group = tmp;
|
|
} else {
|
|
paperSize = 'A4';
|
|
}
|
|
}
|
|
var page;
|
|
page = pdf.addPage({
|
|
paperSize: paperSize,
|
|
margin: getOption('margin', getOption('margin'), options),
|
|
addMargin: addMargin,
|
|
landscape: getOption('landscape', getOption('landscape', false), options)
|
|
});
|
|
drawElement(group, page, pdf);
|
|
}
|
|
if (multiPage) {
|
|
group.children.forEach(drawPage);
|
|
} else {
|
|
drawPage(group);
|
|
}
|
|
callback(pdf.render(), pdf);
|
|
}
|
|
var count = 2;
|
|
kendo.pdf.loadFonts(fonts, doIt);
|
|
kendo.pdf.loadImages(images, doIt);
|
|
}
|
|
function toDataURL(group, callback) {
|
|
render(group, function (data) {
|
|
callback('data:application/pdf;base64,' + data.base64());
|
|
});
|
|
}
|
|
function toBlob(group, callback) {
|
|
render(group, function (data) {
|
|
callback(new Blob([data.get()], { type: 'application/pdf' }));
|
|
});
|
|
}
|
|
function saveAs(group, filename, proxy, callback) {
|
|
if (window.Blob && !kendo.support.browser.safari) {
|
|
toBlob(group, function (blob) {
|
|
kendo.saveAs({
|
|
dataURI: blob,
|
|
fileName: filename
|
|
});
|
|
if (callback) {
|
|
callback(blob);
|
|
}
|
|
});
|
|
} else {
|
|
toDataURL(group, function (dataURL) {
|
|
kendo.saveAs({
|
|
dataURI: dataURL,
|
|
fileName: filename,
|
|
proxyURL: proxy
|
|
});
|
|
if (callback) {
|
|
callback(dataURL);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function dispatch(handlers, element) {
|
|
var handler = handlers[element.nodeType];
|
|
if (handler) {
|
|
return handler.call.apply(handler, arguments);
|
|
}
|
|
return element;
|
|
}
|
|
function drawElement(element, page, pdf) {
|
|
if (element.options._pdfDebug) {
|
|
page.comment('BEGIN: ' + element.options._pdfDebug);
|
|
}
|
|
var transform = element.transform();
|
|
var opacity = element.opacity();
|
|
page.save();
|
|
if (opacity != null && opacity < 1) {
|
|
page.setOpacity(opacity);
|
|
}
|
|
setStrokeOptions(element, page, pdf);
|
|
setFillOptions(element, page, pdf);
|
|
if (transform) {
|
|
var m = transform.matrix();
|
|
page.transform(m.a, m.b, m.c, m.d, m.e, m.f);
|
|
}
|
|
setClipping(element, page, pdf);
|
|
dispatch({
|
|
Path: drawPath,
|
|
MultiPath: drawMultiPath,
|
|
Circle: drawCircle,
|
|
Arc: drawArc,
|
|
Text: drawText,
|
|
Image: drawImage,
|
|
Group: drawGroup,
|
|
Rect: drawRect
|
|
}, element, page, pdf);
|
|
page.restore();
|
|
if (element.options._pdfDebug) {
|
|
page.comment('END: ' + element.options._pdfDebug);
|
|
}
|
|
}
|
|
function setStrokeOptions(element, page) {
|
|
var stroke = element.stroke && element.stroke();
|
|
if (!stroke) {
|
|
return;
|
|
}
|
|
var color = stroke.color;
|
|
if (color) {
|
|
color = parseColor(color);
|
|
if (color == null) {
|
|
return;
|
|
}
|
|
page.setStrokeColor(color.r, color.g, color.b);
|
|
if (color.a != 1) {
|
|
page.setStrokeOpacity(color.a);
|
|
}
|
|
}
|
|
var width = stroke.width;
|
|
if (width != null) {
|
|
if (width === 0) {
|
|
return;
|
|
}
|
|
page.setLineWidth(width);
|
|
}
|
|
var dashType = stroke.dashType;
|
|
if (dashType) {
|
|
page.setDashPattern(DASH_PATTERNS[dashType], 0);
|
|
}
|
|
var lineCap = stroke.lineCap;
|
|
if (lineCap) {
|
|
page.setLineCap(LINE_CAP[lineCap]);
|
|
}
|
|
var lineJoin = stroke.lineJoin;
|
|
if (lineJoin) {
|
|
page.setLineJoin(LINE_JOIN[lineJoin]);
|
|
}
|
|
var opacity = stroke.opacity;
|
|
if (opacity != null) {
|
|
page.setStrokeOpacity(opacity);
|
|
}
|
|
}
|
|
function setFillOptions(element, page) {
|
|
var fill = element.fill && element.fill();
|
|
if (!fill) {
|
|
return;
|
|
}
|
|
if (fill instanceof drawing.Gradient) {
|
|
return;
|
|
}
|
|
var color = fill.color;
|
|
if (color) {
|
|
color = parseColor(color);
|
|
if (color == null) {
|
|
return;
|
|
}
|
|
page.setFillColor(color.r, color.g, color.b);
|
|
if (color.a != 1) {
|
|
page.setFillOpacity(color.a);
|
|
}
|
|
}
|
|
var opacity = fill.opacity;
|
|
if (opacity != null) {
|
|
page.setFillOpacity(opacity);
|
|
}
|
|
}
|
|
function setClipping(element, page, pdf) {
|
|
var clip = element.clip();
|
|
if (clip) {
|
|
_drawPath(clip, page, pdf);
|
|
page.clip();
|
|
}
|
|
}
|
|
function shouldDraw(thing) {
|
|
return thing && (thing instanceof drawing.Gradient || thing.color && !/^(none|transparent)$/i.test(thing.color) && (thing.width == null || thing.width > 0) && (thing.opacity == null || thing.opacity > 0));
|
|
}
|
|
function maybeGradient(element, page, pdf, stroke) {
|
|
var fill = element.fill();
|
|
if (fill instanceof drawing.Gradient) {
|
|
if (stroke) {
|
|
page.clipStroke();
|
|
} else {
|
|
page.clip();
|
|
}
|
|
var isRadial = fill instanceof drawing.RadialGradient;
|
|
var start, end;
|
|
if (isRadial) {
|
|
start = {
|
|
x: fill.center().x,
|
|
y: fill.center().y,
|
|
r: 0
|
|
};
|
|
end = {
|
|
x: fill.center().x,
|
|
y: fill.center().y,
|
|
r: fill.radius()
|
|
};
|
|
} else {
|
|
start = {
|
|
x: fill.start().x,
|
|
y: fill.start().y
|
|
};
|
|
end = {
|
|
x: fill.end().x,
|
|
y: fill.end().y
|
|
};
|
|
}
|
|
var gradient = {
|
|
type: isRadial ? 'radial' : 'linear',
|
|
start: start,
|
|
end: end,
|
|
userSpace: fill.userSpace(),
|
|
stops: fill.stops.elements().map(function (stop) {
|
|
var offset = stop.offset();
|
|
if (/%$/.test(offset)) {
|
|
offset = parseFloat(offset) / 100;
|
|
} else {
|
|
offset = parseFloat(offset);
|
|
}
|
|
var color = parseColor(stop.color());
|
|
color.a *= stop.opacity();
|
|
return {
|
|
offset: offset,
|
|
color: color
|
|
};
|
|
})
|
|
};
|
|
var box = element.rawBBox();
|
|
var tl = box.topLeft(), size = box.getSize();
|
|
box = {
|
|
left: tl.x,
|
|
top: tl.y,
|
|
width: size.width,
|
|
height: size.height
|
|
};
|
|
page.gradient(gradient, box);
|
|
return true;
|
|
}
|
|
}
|
|
function maybeFillStroke(element, page, pdf) {
|
|
if (shouldDraw(element.fill()) && shouldDraw(element.stroke())) {
|
|
if (!maybeGradient(element, page, pdf, true)) {
|
|
page.fillStroke();
|
|
}
|
|
} else if (shouldDraw(element.fill())) {
|
|
if (!maybeGradient(element, page, pdf, false)) {
|
|
page.fill();
|
|
}
|
|
} else if (shouldDraw(element.stroke())) {
|
|
page.stroke();
|
|
} else {
|
|
page.nop();
|
|
}
|
|
}
|
|
function maybeDrawRect(path, page) {
|
|
var segments = path.segments;
|
|
if (segments.length == 4 && path.options.closed) {
|
|
var a = [];
|
|
for (var i = 0; i < segments.length; ++i) {
|
|
if (segments[i].controlIn()) {
|
|
return false;
|
|
}
|
|
a[i] = segments[i].anchor();
|
|
}
|
|
var isRect = a[0].y == a[1].y && a[1].x == a[2].x && a[2].y == a[3].y && a[3].x == a[0].x || a[0].x == a[1].x && a[1].y == a[2].y && a[2].x == a[3].x && a[3].y == a[0].y;
|
|
if (isRect) {
|
|
page.rect(a[0].x, a[0].y, a[2].x - a[0].x, a[2].y - a[0].y);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
function _drawPath(element, page, pdf) {
|
|
var segments = element.segments;
|
|
if (segments.length === 0) {
|
|
return;
|
|
}
|
|
if (!maybeDrawRect(element, page, pdf)) {
|
|
for (var prev, i = 0; i < segments.length; ++i) {
|
|
var seg = segments[i];
|
|
var anchor = seg.anchor();
|
|
if (!prev) {
|
|
page.moveTo(anchor.x, anchor.y);
|
|
} else {
|
|
var prevOut = prev.controlOut();
|
|
var controlIn = seg.controlIn();
|
|
if (prevOut && controlIn) {
|
|
page.bezier(prevOut.x, prevOut.y, controlIn.x, controlIn.y, anchor.x, anchor.y);
|
|
} else {
|
|
page.lineTo(anchor.x, anchor.y);
|
|
}
|
|
}
|
|
prev = seg;
|
|
}
|
|
if (element.options.closed) {
|
|
page.close();
|
|
}
|
|
}
|
|
}
|
|
function drawPath(element, page, pdf) {
|
|
_drawPath(element, page, pdf);
|
|
maybeFillStroke(element, page, pdf);
|
|
}
|
|
function drawMultiPath(element, page, pdf) {
|
|
var paths = element.paths;
|
|
for (var i = 0; i < paths.length; ++i) {
|
|
_drawPath(paths[i], page, pdf);
|
|
}
|
|
maybeFillStroke(element, page, pdf);
|
|
}
|
|
function drawCircle(element, page, pdf) {
|
|
var g = element.geometry();
|
|
page.circle(g.center.x, g.center.y, g.radius);
|
|
maybeFillStroke(element, page, pdf);
|
|
}
|
|
function drawArc(element, page, pdf) {
|
|
var points = element.geometry().curvePoints();
|
|
page.moveTo(points[0].x, points[0].y);
|
|
for (var i = 1; i < points.length;) {
|
|
page.bezier(points[i].x, points[i++].y, points[i].x, points[i++].y, points[i].x, points[i++].y);
|
|
}
|
|
maybeFillStroke(element, page, pdf);
|
|
}
|
|
function drawText(element, page) {
|
|
var style = kendo.pdf.parseFontDef(element.options.font);
|
|
var pos = element._position;
|
|
var mode;
|
|
if (element.fill() && element.stroke()) {
|
|
mode = TEXT_RENDERING_MODE.fillAndStroke;
|
|
} else if (element.fill()) {
|
|
mode = TEXT_RENDERING_MODE.fill;
|
|
} else if (element.stroke()) {
|
|
mode = TEXT_RENDERING_MODE.stroke;
|
|
}
|
|
page.transform(1, 0, 0, -1, pos.x, pos.y + style.fontSize);
|
|
page.beginText();
|
|
page.setFont(kendo.pdf.getFontURL(style), style.fontSize);
|
|
page.setTextRenderingMode(mode);
|
|
page.showText(element.content(), element._pdfRect ? element._pdfRect.width() : null);
|
|
page.endText();
|
|
}
|
|
function drawGroup(element, page, pdf) {
|
|
if (element._pdfLink) {
|
|
page.addLink(element._pdfLink.url, element._pdfLink);
|
|
}
|
|
var children = element.children;
|
|
for (var i = 0; i < children.length; ++i) {
|
|
drawElement(children[i], page, pdf);
|
|
}
|
|
}
|
|
function drawImage(element, page) {
|
|
var url = element.src();
|
|
if (!url) {
|
|
return;
|
|
}
|
|
var rect = element.rect();
|
|
var tl = rect.getOrigin();
|
|
var sz = rect.getSize();
|
|
page.transform(sz.width, 0, 0, -sz.height, tl.x, tl.y + sz.height);
|
|
page.drawImage(url);
|
|
}
|
|
function drawRect(element, page, pdf) {
|
|
var geometry = element.geometry();
|
|
page.rect(geometry.origin.x, geometry.origin.y, geometry.size.width, geometry.size.height);
|
|
maybeFillStroke(element, page, pdf);
|
|
}
|
|
function exportPDF(group, options) {
|
|
var defer = $.Deferred();
|
|
for (var i in options) {
|
|
group.options.set('pdf.' + i, options[i]);
|
|
}
|
|
drawing.pdf.toDataURL(group, defer.resolve);
|
|
return defer.promise();
|
|
}
|
|
function parseColor(x) {
|
|
var color = kendo.parseColor(x, true);
|
|
return color ? color.toRGB() : null;
|
|
}
|
|
function optimize(root) {
|
|
var clipbox = false;
|
|
var matrix = geo.Matrix.unit();
|
|
var currentBox = null;
|
|
var changed;
|
|
do {
|
|
changed = false;
|
|
root = opt(root);
|
|
} while (root && changed);
|
|
return {
|
|
root: root,
|
|
bbox: currentBox
|
|
};
|
|
function change(newShape) {
|
|
changed = true;
|
|
return newShape;
|
|
}
|
|
function visible(shape) {
|
|
return shape.visible() && shape.opacity() > 0 && (shouldDraw(shape.fill()) || shouldDraw(shape.stroke()));
|
|
}
|
|
function optArray(a) {
|
|
var b = [];
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var el = opt(a[i]);
|
|
if (el != null) {
|
|
b.push(el);
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
function withClipping(shape, f) {
|
|
var saveclipbox = clipbox;
|
|
var savematrix = matrix;
|
|
if (shape.transform()) {
|
|
matrix = matrix.multiplyCopy(shape.transform().matrix());
|
|
}
|
|
var clip = shape.clip();
|
|
if (clip) {
|
|
clip = clip.bbox();
|
|
if (clip) {
|
|
clip = clip.bbox(matrix);
|
|
clipbox = clipbox ? geo.Rect.intersect(clipbox, clip) : clip;
|
|
}
|
|
}
|
|
try {
|
|
return f();
|
|
} finally {
|
|
clipbox = saveclipbox;
|
|
matrix = savematrix;
|
|
}
|
|
}
|
|
function inClipbox(shape) {
|
|
if (clipbox == null) {
|
|
return false;
|
|
}
|
|
var box = shape.rawBBox().bbox(matrix);
|
|
if (clipbox && box) {
|
|
box = geo.Rect.intersect(box, clipbox);
|
|
}
|
|
return box;
|
|
}
|
|
function opt(shape) {
|
|
return withClipping(shape, function () {
|
|
if (!(shape instanceof drawing.Group || shape instanceof drawing.MultiPath)) {
|
|
var box = inClipbox(shape);
|
|
if (!box) {
|
|
return change(null);
|
|
}
|
|
currentBox = currentBox ? geo.Rect.union(currentBox, box) : box;
|
|
}
|
|
return dispatch({
|
|
Path: function (shape) {
|
|
if (shape.segments.length === 0 || !visible(shape)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
},
|
|
MultiPath: function (shape) {
|
|
if (!visible(shape)) {
|
|
return change(null);
|
|
}
|
|
var el = new drawing.MultiPath(shape.options);
|
|
el.paths = optArray(shape.paths);
|
|
if (el.paths.length === 0) {
|
|
return change(null);
|
|
}
|
|
return el;
|
|
},
|
|
Circle: function (shape) {
|
|
if (!visible(shape)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
},
|
|
Arc: function (shape) {
|
|
if (!visible(shape)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
},
|
|
Text: function (shape) {
|
|
if (!/\S/.test(shape.content()) || !visible(shape)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
},
|
|
Image: function (shape) {
|
|
if (!(shape.visible() && shape.opacity() > 0)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
},
|
|
Group: function (shape) {
|
|
var el = new drawing.Group(shape.options);
|
|
el.children = optArray(shape.children);
|
|
el._pdfLink = shape._pdfLink;
|
|
if (shape !== root && el.children.length === 0 && !shape._pdfLink) {
|
|
return change(null);
|
|
}
|
|
return el;
|
|
},
|
|
Rect: function (shape) {
|
|
if (!visible(shape)) {
|
|
return change(null);
|
|
}
|
|
return shape;
|
|
}
|
|
}, shape);
|
|
});
|
|
}
|
|
}
|
|
kendo.deepExtend(drawing, {
|
|
exportPDF: exportPDF,
|
|
pdf: {
|
|
toDataURL: toDataURL,
|
|
toBlob: toBlob,
|
|
saveAs: saveAs,
|
|
toStream: render
|
|
}
|
|
});
|
|
}(window.kendo, window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.pdf', [
|
|
'kendo.core',
|
|
'pdf/core',
|
|
'pdf/ttf',
|
|
'pdf/drawing',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'pdf',
|
|
name: 'PDF export',
|
|
description: 'PDF Generation framework',
|
|
mixin: true,
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'drawing'
|
|
]
|
|
};
|
|
(function (kendo, $) {
|
|
kendo.PDFMixin = {
|
|
extend: function (proto) {
|
|
proto.events.push('pdfExport');
|
|
proto.options.pdf = this.options;
|
|
proto.saveAsPDF = this.saveAsPDF;
|
|
proto._drawPDF = this._drawPDF;
|
|
proto._drawPDFShadow = this._drawPDFShadow;
|
|
},
|
|
options: {
|
|
fileName: 'Export.pdf',
|
|
proxyURL: '',
|
|
paperSize: 'auto',
|
|
allPages: false,
|
|
landscape: false,
|
|
margin: null,
|
|
title: null,
|
|
author: null,
|
|
subject: null,
|
|
keywords: null,
|
|
creator: 'Kendo UI PDF Generator v.' + kendo.version,
|
|
date: null
|
|
},
|
|
saveAsPDF: function () {
|
|
var progress = new $.Deferred();
|
|
var promise = progress.promise();
|
|
var args = { promise: promise };
|
|
if (this.trigger('pdfExport', args)) {
|
|
return;
|
|
}
|
|
var options = this.options.pdf;
|
|
options.multiPage = options.allPages;
|
|
this._drawPDF(progress).then(function (root) {
|
|
return kendo.drawing.exportPDF(root, options);
|
|
}).done(function (dataURI) {
|
|
kendo.saveAs({
|
|
dataURI: dataURI,
|
|
fileName: options.fileName,
|
|
proxyURL: options.proxyURL,
|
|
forceProxy: options.forceProxy,
|
|
proxyTarget: options.proxyTarget
|
|
});
|
|
progress.resolve();
|
|
}).fail(function (err) {
|
|
progress.reject(err);
|
|
});
|
|
return promise;
|
|
},
|
|
_drawPDF: function (progress) {
|
|
var promise = new $.Deferred();
|
|
kendo.drawing.drawDOM(this.wrapper).done(function (group) {
|
|
var args = {
|
|
page: group,
|
|
pageNumber: 1,
|
|
progress: 1,
|
|
totalPages: 1
|
|
};
|
|
progress.notify(args);
|
|
promise.resolve(args.page);
|
|
}).fail(function (err) {
|
|
promise.reject(err);
|
|
});
|
|
return promise;
|
|
},
|
|
_drawPDFShadow: function (settings, drawOptions) {
|
|
settings = settings || {};
|
|
var wrapper = this.wrapper;
|
|
var shadow = $('<div class=\'k-pdf-export-shadow\'>');
|
|
if (settings.width) {
|
|
shadow.css({
|
|
width: settings.width,
|
|
overflow: 'visible'
|
|
});
|
|
}
|
|
wrapper.before(shadow);
|
|
shadow.append(settings.content || wrapper.clone(true, true));
|
|
var defer = $.Deferred();
|
|
setTimeout(function () {
|
|
var promise = kendo.drawing.drawDOM(shadow, drawOptions);
|
|
promise.always(function () {
|
|
shadow.remove();
|
|
}).then(function () {
|
|
defer.resolve.apply(defer, arguments);
|
|
}).fail(function () {
|
|
defer.reject.apply(defer, arguments);
|
|
}).progress(function () {
|
|
defer.progress.apply(defer, arguments);
|
|
});
|
|
}, 15);
|
|
return defer.promise();
|
|
}
|
|
};
|
|
}(kendo, window.kendo.jQuery));
|
|
return kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.grid', [
|
|
'kendo.data',
|
|
'kendo.columnsorter',
|
|
'kendo.editable',
|
|
'kendo.window',
|
|
'kendo.filtermenu',
|
|
'kendo.columnmenu',
|
|
'kendo.groupable',
|
|
'kendo.pager',
|
|
'kendo.selectable',
|
|
'kendo.sortable',
|
|
'kendo.reorderable',
|
|
'kendo.resizable',
|
|
'kendo.mobile.actionsheet',
|
|
'kendo.mobile.pane',
|
|
'kendo.ooxml',
|
|
'kendo.excel',
|
|
'kendo.progressbar',
|
|
'kendo.pdf'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'grid',
|
|
name: 'Grid',
|
|
category: 'web',
|
|
description: 'The Grid widget displays tabular data and offers rich support for interacting with data,including paging, sorting, grouping, and selection.',
|
|
depends: [
|
|
'data',
|
|
'columnsorter'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'grid-editing',
|
|
name: 'Editing',
|
|
description: 'Support for record editing',
|
|
depends: [
|
|
'editable',
|
|
'window'
|
|
]
|
|
},
|
|
{
|
|
id: 'grid-filtering',
|
|
name: 'Filtering',
|
|
description: 'Support for record filtering',
|
|
depends: ['filtermenu']
|
|
},
|
|
{
|
|
id: 'grid-columnmenu',
|
|
name: 'Column menu',
|
|
description: 'Support for header column menu',
|
|
depends: ['columnmenu']
|
|
},
|
|
{
|
|
id: 'grid-grouping',
|
|
name: 'Grouping',
|
|
description: 'Support for grid grouping',
|
|
depends: ['groupable']
|
|
},
|
|
{
|
|
id: 'grid-filtercell',
|
|
name: 'Row filter',
|
|
description: 'Support for grid header filtering',
|
|
depends: ['filtercell']
|
|
},
|
|
{
|
|
id: 'grid-paging',
|
|
name: 'Paging',
|
|
description: 'Support for grid paging',
|
|
depends: ['pager']
|
|
},
|
|
{
|
|
id: 'grid-selection',
|
|
name: 'Selection',
|
|
description: 'Support for row selection',
|
|
depends: ['selectable']
|
|
},
|
|
{
|
|
id: 'grid-column-reorder',
|
|
name: 'Column reordering',
|
|
description: 'Support for column reordering',
|
|
depends: ['reorderable']
|
|
},
|
|
{
|
|
id: 'grid-column-resize',
|
|
name: 'Column resizing',
|
|
description: 'Support for column resizing',
|
|
depends: ['resizable']
|
|
},
|
|
{
|
|
id: 'grid-mobile',
|
|
name: 'Grid adaptive rendering',
|
|
description: 'Support for adaptive rendering',
|
|
depends: [
|
|
'mobile.actionsheet',
|
|
'mobile.pane'
|
|
]
|
|
},
|
|
{
|
|
id: 'grid-excel-export',
|
|
name: 'Excel export',
|
|
description: 'Export grid data as Excel spreadsheet',
|
|
depends: ['excel']
|
|
},
|
|
{
|
|
id: 'grid-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export grid data as PDF',
|
|
depends: [
|
|
'pdf',
|
|
'drawing',
|
|
'progressbar'
|
|
]
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, DataSource = kendo.data.DataSource, tbodySupportsInnerHtml = kendo.support.tbodyInnerHtml, activeElement = kendo._activeElement, Widget = ui.Widget, keys = kendo.keys, isPlainObject = $.isPlainObject, extend = $.extend, map = $.map, grep = $.grep, isArray = $.isArray, inArray = $.inArray, push = Array.prototype.push, proxy = $.proxy, isFunction = kendo.isFunction, isEmptyObject = $.isEmptyObject, math = Math, PROGRESS = 'progress', ERROR = 'error', DATA_CELL = ':not(.k-group-cell):not(.k-hierarchy-cell):visible', SELECTION_CELL_SELECTOR = 'tbody>tr:not(.k-grouping-row):not(.k-detail-row):not(.k-group-footer) > td:not(.k-group-cell):not(.k-hierarchy-cell)', NAVROW = 'tr:not(.k-footer-template):visible', NAVCELL = ':not(.k-group-cell):not(.k-hierarchy-cell):visible', FIRSTNAVITEM = NAVROW + ':first>' + NAVCELL + ':first', HEADERCELLS = 'th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)', NS = '.kendoGrid', EDIT = 'edit', SAVE = 'save', REMOVE = 'remove', DETAILINIT = 'detailInit', FILTERMENUINIT = 'filterMenuInit', COLUMNMENUINIT = 'columnMenuInit', CHANGE = 'change', COLUMNHIDE = 'columnHide', COLUMNSHOW = 'columnShow', SAVECHANGES = 'saveChanges', DATABOUND = 'dataBound', DETAILEXPAND = 'detailExpand', DETAILCOLLAPSE = 'detailCollapse', FOCUSED = 'k-state-focused', SELECTED = 'k-state-selected', NORECORDSCLASS = 'k-grid-norecords', COLUMNRESIZE = 'columnResize', COLUMNREORDER = 'columnReorder', COLUMNLOCK = 'columnLock', COLUMNUNLOCK = 'columnUnlock', NAVIGATE = 'navigate', CLICK = 'click', HEIGHT = 'height', TABINDEX = 'tabIndex', FUNCTION = 'function', STRING = 'string', DELETECONFIRM = 'Are you sure you want to delete this record?', NORECORDS = 'No records available.', CONFIRMDELETE = 'Delete', CANCELDELETE = 'Cancel', formatRegExp = /(\}|\#)/gi, templateHashRegExp = /#/gi, whitespaceRegExp = '[\\x20\\t\\r\\n\\f]', nonDataCellsRegExp = new RegExp('(^|' + whitespaceRegExp + ')' + '(k-group-cell|k-hierarchy-cell)' + '(' + whitespaceRegExp + '|$)'), filterRowRegExp = new RegExp('(^|' + whitespaceRegExp + ')' + '(k-filter-row)' + '(' + whitespaceRegExp + '|$)'), COMMANDBUTTONTMPL = '<a class="k-button k-button-icontext #=className#" #=attr# href="\\#"><span class="#=iconClass# #=imageClass#"></span>#=text#</a>', isRtl = false, browser = kendo.support.browser, isIE7 = browser.msie && browser.version == 7, isIE8 = browser.msie && browser.version == 8;
|
|
var VirtualScrollable = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that.setDataSource(options.dataSource);
|
|
that.wrap();
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
if (that.dataSource) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
}
|
|
that.dataSource = dataSource;
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
},
|
|
options: {
|
|
name: 'VirtualScrollable',
|
|
itemHeight: $.noop,
|
|
prefetch: true
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that.wrapper.add(that.verticalScrollbar).off(NS);
|
|
if (that.drag) {
|
|
that.drag.destroy();
|
|
that.drag = null;
|
|
}
|
|
that.wrapper = that.element = that.verticalScrollbar = null;
|
|
that._refreshHandler = null;
|
|
},
|
|
wrap: function () {
|
|
var that = this, scrollbar = kendo.support.scrollbar() + 1, element = that.element, wrapper;
|
|
element.css({
|
|
width: 'auto',
|
|
overflow: 'hidden'
|
|
}).css(isRtl ? 'padding-left' : 'padding-right', scrollbar);
|
|
that.content = element.children().first();
|
|
wrapper = that.wrapper = that.content.wrap('<div class="k-virtual-scrollable-wrap"/>').parent().bind('DOMMouseScroll' + NS + ' mousewheel' + NS, proxy(that._wheelScroll, that));
|
|
if (kendo.support.kineticScrollNeeded) {
|
|
that.drag = new kendo.UserEvents(that.wrapper, {
|
|
global: true,
|
|
start: function (e) {
|
|
e.sender.capture();
|
|
},
|
|
move: function (e) {
|
|
that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta);
|
|
wrapper.scrollLeft(wrapper.scrollLeft() - e.x.delta);
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
that.verticalScrollbar = $('<div class="k-scrollbar k-scrollbar-vertical" />').css({ width: scrollbar }).appendTo(element).bind('scroll' + NS, proxy(that._scroll, that));
|
|
},
|
|
_wheelScroll: function (e) {
|
|
if (e.ctrlKey) {
|
|
return;
|
|
}
|
|
var scrollbar = this.verticalScrollbar, scrollTop = scrollbar.scrollTop(), delta = kendo.wheelDeltaY(e);
|
|
if (delta && !(delta > 0 && scrollTop === 0) && !(delta < 0 && scrollTop + scrollbar[0].clientHeight == scrollbar[0].scrollHeight)) {
|
|
e.preventDefault();
|
|
$(e.currentTarget).one('wheel' + NS, false);
|
|
this.verticalScrollbar.scrollTop(scrollTop + -delta);
|
|
}
|
|
},
|
|
_scroll: function (e) {
|
|
var that = this, delayLoading = !that.options.prefetch, scrollTop = e.currentTarget.scrollTop, dataSource = that.dataSource, rowHeight = that.itemHeight, skip = dataSource.skip() || 0, start = that._rangeStart || skip, height = that.element.innerHeight(), isScrollingUp = !!(that._scrollbarTop && that._scrollbarTop > scrollTop), firstItemIndex = math.max(math.floor(scrollTop / rowHeight), 0), lastItemIndex = math.max(firstItemIndex + math.floor(height / rowHeight), 0);
|
|
that._scrollTop = scrollTop - start * rowHeight;
|
|
that._scrollbarTop = scrollTop;
|
|
that._scrolling = delayLoading;
|
|
if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) {
|
|
that.wrapper[0].scrollTop = that._scrollTop;
|
|
}
|
|
if (delayLoading) {
|
|
if (that._scrollingTimeout) {
|
|
clearTimeout(that._scrollingTimeout);
|
|
}
|
|
that._scrollingTimeout = setTimeout(function () {
|
|
that._scrolling = false;
|
|
that._page(that._rangeStart, that.dataSource.take());
|
|
}, 100);
|
|
}
|
|
},
|
|
itemIndex: function (rowIndex) {
|
|
var rangeStart = this._rangeStart || this.dataSource.skip() || 0;
|
|
return rangeStart + rowIndex;
|
|
},
|
|
position: function (index) {
|
|
var rangeStart = this._rangeStart || this.dataSource.skip() || 0;
|
|
var pageSize = this.dataSource.pageSize();
|
|
var result;
|
|
if (index > rangeStart) {
|
|
result = index - rangeStart + 1;
|
|
} else {
|
|
result = rangeStart - index - 1;
|
|
}
|
|
return result > pageSize ? pageSize : result;
|
|
},
|
|
scrollIntoView: function (row) {
|
|
var container = this.wrapper[0];
|
|
var containerHeight = container.clientHeight;
|
|
var containerScroll = this._scrollTop || container.scrollTop;
|
|
var elementOffset = row[0].offsetTop;
|
|
var elementHeight = row[0].offsetHeight;
|
|
if (containerScroll > elementOffset) {
|
|
this.verticalScrollbar[0].scrollTop -= containerHeight / 2;
|
|
} else if (elementOffset + elementHeight >= containerScroll + containerHeight) {
|
|
this.verticalScrollbar[0].scrollTop += containerHeight / 2;
|
|
}
|
|
},
|
|
_fetch: function (firstItemIndex, lastItemIndex, scrollingUp) {
|
|
var that = this, dataSource = that.dataSource, itemHeight = that.itemHeight, take = dataSource.take(), rangeStart = that._rangeStart || dataSource.skip() || 0, currentSkip = math.floor(firstItemIndex / take) * take, fetching = false, prefetchAt = 0.33;
|
|
if (firstItemIndex < rangeStart) {
|
|
fetching = true;
|
|
rangeStart = math.max(0, lastItemIndex - take);
|
|
that._scrollTop = (firstItemIndex - rangeStart) * itemHeight;
|
|
that._page(rangeStart, take);
|
|
} else if (lastItemIndex >= rangeStart + take && !scrollingUp) {
|
|
fetching = true;
|
|
rangeStart = firstItemIndex;
|
|
that._scrollTop = itemHeight;
|
|
that._page(rangeStart, take);
|
|
} else if (!that._fetching && that.options.prefetch) {
|
|
if (firstItemIndex < currentSkip + take - take * prefetchAt && firstItemIndex > take) {
|
|
dataSource.prefetch(currentSkip - take, take, $.noop);
|
|
}
|
|
if (lastItemIndex > currentSkip + take * prefetchAt) {
|
|
dataSource.prefetch(currentSkip + take, take, $.noop);
|
|
}
|
|
}
|
|
return fetching;
|
|
},
|
|
fetching: function () {
|
|
return this._fetching;
|
|
},
|
|
_page: function (skip, take) {
|
|
var that = this, delayLoading = !that.options.prefetch, dataSource = that.dataSource;
|
|
clearTimeout(that._timeout);
|
|
that._fetching = true;
|
|
that._rangeStart = skip;
|
|
if (dataSource.inRange(skip, take)) {
|
|
dataSource.range(skip, take);
|
|
} else {
|
|
if (!delayLoading) {
|
|
kendo.ui.progress(that.wrapper.parent(), true);
|
|
}
|
|
that._timeout = setTimeout(function () {
|
|
if (!that._scrolling) {
|
|
if (delayLoading) {
|
|
kendo.ui.progress(that.wrapper.parent(), true);
|
|
}
|
|
dataSource.range(skip, take);
|
|
}
|
|
}, 100);
|
|
}
|
|
},
|
|
repaintScrollbar: function () {
|
|
var that = this, html = '', maxHeight = 250000, dataSource = that.dataSource, scrollbar = !kendo.support.kineticScrollNeeded ? kendo.support.scrollbar() : 0, wrapperElement = that.wrapper[0], totalHeight, idx, itemHeight;
|
|
itemHeight = that.itemHeight = that.options.itemHeight() || 0;
|
|
var addScrollBarHeight = wrapperElement.scrollWidth > wrapperElement.offsetWidth ? scrollbar : 0;
|
|
totalHeight = dataSource.total() * itemHeight + addScrollBarHeight;
|
|
for (idx = 0; idx < math.floor(totalHeight / maxHeight); idx++) {
|
|
html += '<div style="width:1px;height:' + maxHeight + 'px"></div>';
|
|
}
|
|
if (totalHeight % maxHeight) {
|
|
html += '<div style="width:1px;height:' + totalHeight % maxHeight + 'px"></div>';
|
|
}
|
|
that.verticalScrollbar.html(html);
|
|
wrapperElement.scrollTop = that._scrollTop;
|
|
},
|
|
refresh: function () {
|
|
var that = this, dataSource = that.dataSource, rangeStart = that._rangeStart;
|
|
kendo.ui.progress(that.wrapper.parent(), false);
|
|
clearTimeout(that._timeout);
|
|
that.repaintScrollbar();
|
|
if (that.drag) {
|
|
that.drag.cancel();
|
|
}
|
|
if (rangeStart && !that._fetching) {
|
|
that._rangeStart = dataSource.skip();
|
|
if (dataSource.page() === 1) {
|
|
that.verticalScrollbar[0].scrollTop = 0;
|
|
}
|
|
}
|
|
that._fetching = false;
|
|
}
|
|
});
|
|
function groupCells(count) {
|
|
return new Array(count + 1).join('<td class="k-group-cell"> </td>');
|
|
}
|
|
function stringifyAttributes(attributes) {
|
|
var attr, result = ' ';
|
|
if (attributes) {
|
|
if (typeof attributes === STRING) {
|
|
return attributes;
|
|
}
|
|
for (attr in attributes) {
|
|
result += attr + '="' + attributes[attr] + '"';
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
var defaultCommands = {
|
|
create: {
|
|
text: 'Add new record',
|
|
imageClass: 'k-add',
|
|
className: 'k-grid-add',
|
|
iconClass: 'k-icon'
|
|
},
|
|
cancel: {
|
|
text: 'Cancel changes',
|
|
imageClass: 'k-cancel',
|
|
className: 'k-grid-cancel-changes',
|
|
iconClass: 'k-icon'
|
|
},
|
|
save: {
|
|
text: 'Save changes',
|
|
imageClass: 'k-update',
|
|
className: 'k-grid-save-changes',
|
|
iconClass: 'k-icon'
|
|
},
|
|
destroy: {
|
|
text: 'Delete',
|
|
imageClass: 'k-delete',
|
|
className: 'k-grid-delete',
|
|
iconClass: 'k-icon'
|
|
},
|
|
edit: {
|
|
text: 'Edit',
|
|
imageClass: 'k-edit',
|
|
className: 'k-grid-edit',
|
|
iconClass: 'k-icon'
|
|
},
|
|
update: {
|
|
text: 'Update',
|
|
imageClass: 'k-update',
|
|
className: 'k-primary k-grid-update',
|
|
iconClass: 'k-icon'
|
|
},
|
|
canceledit: {
|
|
text: 'Cancel',
|
|
imageClass: 'k-cancel',
|
|
className: 'k-grid-cancel',
|
|
iconClass: 'k-icon'
|
|
},
|
|
excel: {
|
|
text: 'Export to Excel',
|
|
imageClass: 'k-i-excel',
|
|
className: 'k-grid-excel',
|
|
iconClass: 'k-icon'
|
|
},
|
|
pdf: {
|
|
text: 'Export to PDF',
|
|
imageClass: 'k-i-pdf',
|
|
className: 'k-grid-pdf',
|
|
iconClass: 'k-icon'
|
|
}
|
|
};
|
|
function cursor(context, value) {
|
|
$('th, th .k-grid-filter, th .k-link', context).add(document.body).css('cursor', value);
|
|
}
|
|
function reorder(selector, source, dest, before, count) {
|
|
var sourceIndex = source;
|
|
source = $();
|
|
count = count || 1;
|
|
for (var idx = 0; idx < count; idx++) {
|
|
source = source.add(selector.eq(sourceIndex + idx));
|
|
}
|
|
if (typeof dest == 'number') {
|
|
source[before ? 'insertBefore' : 'insertAfter'](selector.eq(dest));
|
|
} else {
|
|
source.appendTo(dest);
|
|
}
|
|
}
|
|
function elements(lockedContent, content, filter) {
|
|
return $(lockedContent).add(content).find(filter);
|
|
}
|
|
function attachCustomCommandEvent(context, container, commands) {
|
|
var idx, length, command, commandName;
|
|
commands = !isArray(commands) ? [commands] : commands;
|
|
for (idx = 0, length = commands.length; idx < length; idx++) {
|
|
command = commands[idx];
|
|
if (isPlainObject(command) && command.click) {
|
|
commandName = command.name || command.text;
|
|
container.on(CLICK + NS, 'a.k-grid-' + (commandName || '').replace(/\s/g, ''), { commandName: commandName }, proxy(command.click, context));
|
|
}
|
|
}
|
|
}
|
|
function normalizeColumns(columns, encoded, hide) {
|
|
return map(columns, function (column) {
|
|
column = typeof column === STRING ? { field: column } : column;
|
|
var hidden;
|
|
if (!isVisible(column) || hide) {
|
|
column.attributes = addHiddenStyle(column.attributes);
|
|
column.footerAttributes = addHiddenStyle(column.footerAttributes);
|
|
column.headerAttributes = addHiddenStyle(column.headerAttributes);
|
|
hidden = true;
|
|
}
|
|
if (column.columns) {
|
|
column.columns = normalizeColumns(column.columns, encoded, hidden);
|
|
}
|
|
var uid = kendo.guid();
|
|
column.headerAttributes = extend({ id: uid }, column.headerAttributes);
|
|
return extend({
|
|
encoded: encoded,
|
|
hidden: hidden
|
|
}, column);
|
|
});
|
|
}
|
|
function columnParent(column, columns) {
|
|
var parents = [];
|
|
columnParents(column, columns, parents);
|
|
return parents[parents.length - 1];
|
|
}
|
|
function columnParents(column, columns, parents) {
|
|
parents = parents || [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (column === columns[idx]) {
|
|
return true;
|
|
} else if (columns[idx].columns) {
|
|
var inserted = parents.length;
|
|
parents.push(columns[idx]);
|
|
if (!columnParents(column, columns[idx].columns, parents)) {
|
|
parents.splice(inserted, parents.length - inserted);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function setColumnVisibility(column, visible) {
|
|
var method = visible ? removeHiddenStyle : addHiddenStyle;
|
|
column.hidden = !visible;
|
|
column.attributes = method(column.attributes);
|
|
column.footerAttributes = method(column.footerAttributes);
|
|
column.headerAttributes = method(column.headerAttributes);
|
|
}
|
|
function isCellVisible() {
|
|
return this.style.display !== 'none';
|
|
}
|
|
function isVisible(column) {
|
|
return visibleColumns([column]).length > 0;
|
|
}
|
|
function visibleColumns(columns) {
|
|
return grep(columns, function (column) {
|
|
var result = !column.hidden;
|
|
if (result && column.columns) {
|
|
result = visibleColumns(column.columns).length > 0;
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
function toJQuery(elements) {
|
|
return $(elements).map(function () {
|
|
return this.toArray();
|
|
});
|
|
}
|
|
function updateCellRowSpan(cell, columns, sourceLockedColumnsCount) {
|
|
var lockedColumnDepth = depth(lockedColumns(columns));
|
|
var nonLockedColumnDepth = depth(nonLockedColumns(columns));
|
|
var rowSpan = cell.rowSpan;
|
|
if (sourceLockedColumnsCount) {
|
|
if (lockedColumnDepth > nonLockedColumnDepth) {
|
|
cell.rowSpan = rowSpan - (lockedColumnDepth - nonLockedColumnDepth) || 1;
|
|
} else {
|
|
cell.rowSpan = rowSpan + (nonLockedColumnDepth - lockedColumnDepth);
|
|
}
|
|
} else {
|
|
if (lockedColumnDepth > nonLockedColumnDepth) {
|
|
cell.rowSpan = rowSpan + (lockedColumnDepth - nonLockedColumnDepth);
|
|
} else {
|
|
cell.rowSpan = rowSpan - (nonLockedColumnDepth - lockedColumnDepth) || 1;
|
|
}
|
|
}
|
|
}
|
|
function moveCellsBetweenContainers(sources, target, leafs, columns, container, destination, groups) {
|
|
var sourcesDepth = depth(sources);
|
|
var targetDepth = depth([target]);
|
|
if (sourcesDepth > targetDepth) {
|
|
var groupCells = new Array(groups + 1).join('<th class="k-group-cell k-header" scope="col"> </th>');
|
|
var rows = destination.children(':not(.k-filter-row)');
|
|
$(new Array(sourcesDepth - targetDepth + 1).join('<tr>' + groupCells + '</tr>')).insertAfter(rows.last());
|
|
}
|
|
addRowSpanValue(destination, sourcesDepth - targetDepth);
|
|
moveCells(leafs, columns, container, destination);
|
|
}
|
|
function updateCellIndex(thead, columns, offset) {
|
|
offset = offset || 0;
|
|
var position;
|
|
var cell;
|
|
var allColumns = columns;
|
|
columns = leafColumns(columns);
|
|
var cells = {};
|
|
var rows = thead.find('>tr:not(.k-filter-row)');
|
|
var filter = function () {
|
|
var el = $(this);
|
|
return !el.hasClass('k-group-cell') && !el.hasClass('k-hierarchy-cell');
|
|
};
|
|
for (var idx = 0, length = columns.length; idx < length; idx++) {
|
|
position = columnPosition(columns[idx], allColumns);
|
|
if (!cells[position.row]) {
|
|
cells[position.row] = rows.eq(position.row).find('.k-header').filter(filter);
|
|
}
|
|
cell = cells[position.row].eq(position.cell);
|
|
cell.attr(kendo.attr('index'), offset + idx);
|
|
}
|
|
return columns.length;
|
|
}
|
|
function depth(columns) {
|
|
var result = 1;
|
|
var max = 0;
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (columns[idx].columns) {
|
|
var temp = depth(columns[idx].columns);
|
|
if (temp > max) {
|
|
max = temp;
|
|
}
|
|
}
|
|
}
|
|
return result + max;
|
|
}
|
|
function moveCells(leafs, columns, container, destination) {
|
|
var sourcePosition = columnVisiblePosition(leafs[0], columns);
|
|
var ths = container.find('>tr:not(.k-filter-row):eq(' + sourcePosition.row + ')>th.k-header');
|
|
var t = $();
|
|
var sourceIndex = sourcePosition.cell;
|
|
var idx;
|
|
for (idx = 0; idx < leafs.length; idx++) {
|
|
t = t.add(ths.eq(sourceIndex + idx));
|
|
}
|
|
destination.find('>tr:not(.k-filter-row)').eq(sourcePosition.row).append(t);
|
|
var children = [];
|
|
for (idx = 0; idx < leafs.length; idx++) {
|
|
if (leafs[idx].columns) {
|
|
children = children.concat(leafs[idx].columns);
|
|
}
|
|
}
|
|
if (children.length) {
|
|
moveCells(children, columns, container, destination);
|
|
}
|
|
}
|
|
function columnPosition(column, columns, row, cellCounts) {
|
|
var result;
|
|
var idx;
|
|
row = row || 0;
|
|
cellCounts = cellCounts || {};
|
|
cellCounts[row] = cellCounts[row] || 0;
|
|
for (idx = 0; idx < columns.length; idx++) {
|
|
if (columns[idx] == column) {
|
|
result = {
|
|
cell: cellCounts[row],
|
|
row: row
|
|
};
|
|
break;
|
|
} else if (columns[idx].columns) {
|
|
result = columnPosition(column, columns[idx].columns, row + 1, cellCounts);
|
|
if (result) {
|
|
break;
|
|
}
|
|
}
|
|
cellCounts[row]++;
|
|
}
|
|
return result;
|
|
}
|
|
function findParentColumnWithChildren(columns, index, source, rtl) {
|
|
var target;
|
|
var locked = source.locked;
|
|
do {
|
|
target = columns[index];
|
|
index += rtl ? 1 : -1;
|
|
} while (target && index > -1 && index < columns.length && target != source && !target.columns && target.locked == locked);
|
|
return target;
|
|
}
|
|
function findReorderTarget(columns, target, source, before) {
|
|
if (target.columns) {
|
|
target = target.columns;
|
|
return target[before ? 0 : target.length - 1];
|
|
} else {
|
|
var parent = columnParent(target, columns);
|
|
var parentColumns;
|
|
if (parent) {
|
|
parentColumns = parent.columns;
|
|
} else {
|
|
parentColumns = columns;
|
|
}
|
|
var index = inArray(target, parentColumns);
|
|
if (index === 0 && before) {
|
|
index++;
|
|
} else if (index == parentColumns.length - 1 && !before) {
|
|
index--;
|
|
} else if (index > 0 || index === 0 && !before) {
|
|
index += before ? -1 : 1;
|
|
}
|
|
var sourceIndex = inArray(source, parentColumns);
|
|
target = findParentColumnWithChildren(parentColumns, index, source, sourceIndex > index);
|
|
if (target && target != source && target.columns) {
|
|
return findReorderTarget(columns, target, source, before);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function columnVisiblePosition(column, columns, row, cellCounts) {
|
|
var result;
|
|
var idx;
|
|
row = row || 0;
|
|
cellCounts = cellCounts || {};
|
|
cellCounts[row] = cellCounts[row] || 0;
|
|
for (idx = 0; idx < columns.length; idx++) {
|
|
if (columns[idx] == column) {
|
|
result = {
|
|
cell: cellCounts[row],
|
|
row: row
|
|
};
|
|
break;
|
|
} else if (columns[idx].columns) {
|
|
result = columnVisiblePosition(column, columns[idx].columns, row + 1, cellCounts);
|
|
if (result) {
|
|
break;
|
|
}
|
|
}
|
|
if (!columns[idx].hidden) {
|
|
cellCounts[row]++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function flatColumnsInDomOrder(columns) {
|
|
var result = flatColumns(lockedColumns(columns));
|
|
return result.concat(flatColumns(nonLockedColumns(columns)));
|
|
}
|
|
function flatColumns(columns) {
|
|
var result = [];
|
|
var children = [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
result.push(columns[idx]);
|
|
if (columns[idx].columns) {
|
|
children = children.concat(columns[idx].columns);
|
|
}
|
|
}
|
|
if (children.length) {
|
|
result = result.concat(flatColumns(children));
|
|
}
|
|
return result;
|
|
}
|
|
function hiddenLeafColumnsCount(columns) {
|
|
var counter = 0;
|
|
var column;
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
column = columns[idx];
|
|
if (column.columns) {
|
|
counter += hiddenLeafColumnsCount(column.columns);
|
|
} else if (column.hidden) {
|
|
counter++;
|
|
}
|
|
}
|
|
return counter;
|
|
}
|
|
function columnsWidth(cols) {
|
|
var colWidth, width = 0;
|
|
for (var idx = 0, length = cols.length; idx < length; idx++) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
width += parseInt(colWidth, 10);
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
function removeRowSpanValue(container, count) {
|
|
var cells = container.find('tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)');
|
|
var rowSpan;
|
|
for (var idx = 0; idx < cells.length; idx++) {
|
|
rowSpan = cells[idx].rowSpan;
|
|
if (rowSpan > 1) {
|
|
cells[idx].rowSpan = rowSpan - count || 1;
|
|
}
|
|
}
|
|
}
|
|
function addRowSpanValue(container, count) {
|
|
var cells = container.find('tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)');
|
|
for (var idx = 0; idx < cells.length; idx++) {
|
|
cells[idx].rowSpan += count;
|
|
}
|
|
}
|
|
function removeEmptyRows(container) {
|
|
var rows = container.find('tr:not(.k-filter-row)');
|
|
var emptyRowsCount = rows.filter(function () {
|
|
return !$(this).children().length;
|
|
}).remove().length;
|
|
var cells = rows.find('th:not(.k-group-cell,.k-hierarchy-cell)');
|
|
for (var idx = 0; idx < cells.length; idx++) {
|
|
if (cells[idx].rowSpan > 1) {
|
|
cells[idx].rowSpan -= emptyRowsCount;
|
|
}
|
|
}
|
|
return rows.length - emptyRowsCount;
|
|
}
|
|
function mapColumnToCellRows(columns, cells, rows, rowIndex, offset) {
|
|
var idx, row, length, children = [];
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
row = rows[rowIndex] || [];
|
|
row.push(cells.eq(offset + idx));
|
|
rows[rowIndex] = row;
|
|
if (columns[idx].columns) {
|
|
children = children.concat(columns[idx].columns);
|
|
}
|
|
}
|
|
if (children.length) {
|
|
mapColumnToCellRows(children, cells, rows, rowIndex + 1, offset + columns.length);
|
|
}
|
|
}
|
|
function lockedColumns(columns) {
|
|
return grep(columns, function (column) {
|
|
return column.locked;
|
|
});
|
|
}
|
|
function nonLockedColumns(columns) {
|
|
return grep(columns, function (column) {
|
|
return !column.locked;
|
|
});
|
|
}
|
|
function visibleNonLockedColumns(columns) {
|
|
return grep(columns, function (column) {
|
|
return !column.locked && isVisible(column);
|
|
});
|
|
}
|
|
function visibleLockedColumns(columns) {
|
|
return grep(columns, function (column) {
|
|
return column.locked && isVisible(column);
|
|
});
|
|
}
|
|
function visibleLeafColumns(columns) {
|
|
var result = [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (columns[idx].hidden) {
|
|
continue;
|
|
}
|
|
if (columns[idx].columns) {
|
|
result = result.concat(visibleLeafColumns(columns[idx].columns));
|
|
} else {
|
|
result.push(columns[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function leafColumns(columns) {
|
|
var result = [];
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
if (!columns[idx].columns) {
|
|
result.push(columns[idx]);
|
|
continue;
|
|
}
|
|
result = result.concat(leafColumns(columns[idx].columns));
|
|
}
|
|
return result;
|
|
}
|
|
function leafDataCells(container) {
|
|
var rows = container.find('>tr:not(.k-filter-row)');
|
|
var filter = function () {
|
|
var el = $(this);
|
|
return !el.hasClass('k-group-cell') && !el.hasClass('k-hierarchy-cell');
|
|
};
|
|
var cells = $();
|
|
if (rows.length > 1) {
|
|
cells = rows.find('th').filter(filter).filter(function () {
|
|
return this.rowSpan > 1;
|
|
});
|
|
}
|
|
cells = cells.add(rows.last().find('th').filter(filter));
|
|
var indexAttr = kendo.attr('index');
|
|
cells.sort(function (a, b) {
|
|
a = $(a);
|
|
b = $(b);
|
|
var indexA = a.attr(indexAttr);
|
|
var indexB = b.attr(indexAttr);
|
|
if (indexA === undefined) {
|
|
indexA = $(a).index();
|
|
}
|
|
if (indexB === undefined) {
|
|
indexB = $(b).index();
|
|
}
|
|
indexA = parseInt(indexA, 10);
|
|
indexB = parseInt(indexB, 10);
|
|
return indexA > indexB ? 1 : indexA < indexB ? -1 : 0;
|
|
});
|
|
return cells;
|
|
}
|
|
function parentColumnsCells(cell) {
|
|
var container = cell.closest('table');
|
|
var result = $().add(cell);
|
|
var row = cell.closest('tr');
|
|
var headerRows = container.find('tr:not(.k-filter-row)');
|
|
var level = headerRows.index(row);
|
|
if (level > 0) {
|
|
var parent = headerRows.eq(level - 1);
|
|
var parentCellsWithChildren = parent.find('th:not(.k-group-cell,.k-hierarchy-cell)').filter(function () {
|
|
return !$(this).attr('rowspan');
|
|
});
|
|
var offset = 0;
|
|
var index = row.find('th:not(.k-group-cell,.k-hierarchy-cell)').index(cell);
|
|
var prevCells = cell.prevAll(':not(.k-group-cell,.k-hierarchy-cell)').filter(function () {
|
|
return this.colSpan > 1;
|
|
});
|
|
for (var idx = 0; idx < prevCells.length; idx++) {
|
|
offset += prevCells[idx].colSpan || 1;
|
|
}
|
|
index += Math.max(offset - 1, 0);
|
|
offset = 0;
|
|
for (idx = 0; idx < parentCellsWithChildren.length; idx++) {
|
|
var parentCell = parentCellsWithChildren.eq(idx);
|
|
if (parentCell.attr('colSpan')) {
|
|
offset += parentCell[0].colSpan;
|
|
} else {
|
|
offset += 1;
|
|
}
|
|
if (index >= idx && index < offset) {
|
|
result = parentColumnsCells(parentCell).add(result);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function childColumnsCells(cell) {
|
|
var container = cell.closest('thead');
|
|
var result = $().add(cell);
|
|
var row = cell.closest('tr');
|
|
var headerRows = container.find('tr:not(.k-filter-row)');
|
|
var level = headerRows.index(row) + cell[0].rowSpan;
|
|
var colSpanAttr = kendo.attr('colspan');
|
|
if (level <= headerRows.length - 1) {
|
|
var child = row.next();
|
|
var prevCells = cell.prevAll(':not(.k-group-cell,.k-hierarchy-cell)');
|
|
var idx;
|
|
prevCells = prevCells.filter(function () {
|
|
return !this.rowSpan || this.rowSpan === 1;
|
|
});
|
|
var offset = 0;
|
|
for (idx = 0; idx < prevCells.length; idx++) {
|
|
offset += parseInt(prevCells.eq(idx).attr(colSpanAttr), 10) || 1;
|
|
}
|
|
var cells = child.find('th:not(.k-group-cell,.k-hierarchy-cell)');
|
|
var colSpan = parseInt(cell.attr(colSpanAttr), 10) || 1;
|
|
idx = 0;
|
|
while (idx < colSpan) {
|
|
child = cells.eq(idx + offset);
|
|
result = result.add(childColumnsCells(child));
|
|
var value = parseInt(child.attr(colSpanAttr), 10);
|
|
if (value > 1) {
|
|
colSpan -= value - 1;
|
|
}
|
|
idx++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function appendContent(tbody, table, html, empty) {
|
|
var placeholder, tmp = tbody;
|
|
if (empty) {
|
|
tbody.empty();
|
|
}
|
|
if (tbodySupportsInnerHtml) {
|
|
tbody[0].innerHTML = html;
|
|
} else {
|
|
placeholder = document.createElement('div');
|
|
placeholder.innerHTML = '<table><tbody>' + html + '</tbody></table>';
|
|
tbody = placeholder.firstChild.firstChild;
|
|
table[0].replaceChild(tbody, tmp[0]);
|
|
tbody = $(tbody);
|
|
}
|
|
return tbody;
|
|
}
|
|
function addHiddenStyle(attr) {
|
|
attr = attr || {};
|
|
var style = attr.style;
|
|
if (!style) {
|
|
style = 'display:none';
|
|
} else {
|
|
style = style.replace(/((.*)?display)(.*)?:([^;]*)/i, '$1:none');
|
|
if (style === attr.style) {
|
|
style = style.replace(/(.*)?/i, 'display:none;$1');
|
|
}
|
|
}
|
|
return extend({}, attr, { style: style });
|
|
}
|
|
function removeHiddenStyle(attr) {
|
|
attr = attr || {};
|
|
var style = attr.style;
|
|
if (style) {
|
|
attr.style = style.replace(/(display\s*:\s*none\s*;?)*/gi, '');
|
|
}
|
|
return attr;
|
|
}
|
|
function normalizeCols(table, visibleColumns, hasDetails, groups) {
|
|
var colgroup = table.find('>colgroup'), width, cols = map(visibleColumns, function (column) {
|
|
width = column.width;
|
|
if (width && parseInt(width, 10) !== 0) {
|
|
return kendo.format('<col style="width:{0}"/>', typeof width === STRING ? width : width + 'px');
|
|
}
|
|
return '<col />';
|
|
});
|
|
if (hasDetails || colgroup.find('.k-hierarchy-col').length) {
|
|
cols.splice(0, 0, '<col class="k-hierarchy-col" />');
|
|
}
|
|
if (colgroup.length) {
|
|
colgroup.remove();
|
|
}
|
|
colgroup = $(new Array(groups + 1).join('<col class="k-group-col">') + cols.join(''));
|
|
if (!colgroup.is('colgroup')) {
|
|
colgroup = $('<colgroup/>').append(colgroup);
|
|
}
|
|
table.prepend(colgroup);
|
|
if (browser.msie && browser.version == 8) {
|
|
table.css('display', 'inline-table');
|
|
window.setTimeout(function () {
|
|
table.css('display', '');
|
|
}, 1);
|
|
}
|
|
}
|
|
function normalizeHeaderCells(container, columns) {
|
|
var lastIndex = 0;
|
|
var idx, len;
|
|
var th = container.find('th:not(.k-group-cell)');
|
|
for (idx = 0, len = columns.length; idx < len; idx++) {
|
|
if (columns[idx].locked) {
|
|
th.eq(idx).insertBefore(th.eq(lastIndex));
|
|
th = container.find('th:not(.k-group-cell)');
|
|
lastIndex++;
|
|
}
|
|
}
|
|
}
|
|
function convertToObject(array) {
|
|
var result = {}, item, idx, length;
|
|
for (idx = 0, length = array.length; idx < length; idx++) {
|
|
item = array[idx];
|
|
result[item.value] = item.text;
|
|
}
|
|
return result;
|
|
}
|
|
function formatGroupValue(value, format, columnValues, encoded) {
|
|
var isForeignKey = columnValues && columnValues.length && isPlainObject(columnValues[0]) && 'value' in columnValues[0], groupValue = isForeignKey ? convertToObject(columnValues)[value] : value;
|
|
groupValue = groupValue != null ? groupValue : '';
|
|
return format ? kendo.format(format, groupValue) : encoded === false ? groupValue : kendo.htmlEncode(groupValue);
|
|
}
|
|
function setCellVisibility(cells, index, visible) {
|
|
var pad = 0, state, cell = cells[pad];
|
|
while (cell) {
|
|
state = visible ? true : cell.style.display !== 'none';
|
|
if (state && !nonDataCellsRegExp.test(cell.className) && --index < 0) {
|
|
cell.style.display = visible ? '' : 'none';
|
|
break;
|
|
}
|
|
cell = cells[++pad];
|
|
}
|
|
}
|
|
function hideColumnCells(rows, columnIndex) {
|
|
var idx = 0, length = rows.length, cell, row;
|
|
for (; idx < length; idx += 1) {
|
|
row = rows.eq(idx);
|
|
if (row.is('.k-grouping-row,.k-detail-row')) {
|
|
cell = row.children(':not(.k-group-cell):first,.k-detail-cell').last();
|
|
cell.attr('colspan', parseInt(cell.attr('colspan'), 10) - 1);
|
|
} else {
|
|
if (row.hasClass('k-grid-edit-row') && (cell = row.children('.k-edit-container')[0])) {
|
|
cell = $(cell);
|
|
cell.attr('colspan', parseInt(cell.attr('colspan'), 10) - 1);
|
|
cell.find('col').eq(columnIndex).remove();
|
|
row = cell.find('tr:first');
|
|
}
|
|
setCellVisibility(row[0].cells, columnIndex, false);
|
|
}
|
|
}
|
|
}
|
|
function groupRows(data) {
|
|
var result = [];
|
|
var item;
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
item = data[idx];
|
|
if (!('field' in item && 'value' in item && 'items' in item)) {
|
|
break;
|
|
}
|
|
result.push(item);
|
|
if (item.hasSubgroups) {
|
|
result = result.concat(groupRows(item.items));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function groupFooters(data) {
|
|
var result = [];
|
|
var item;
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
item = data[idx];
|
|
if (!('field' in item && 'value' in item && 'items' in item)) {
|
|
break;
|
|
}
|
|
if (item.hasSubgroups) {
|
|
result = result.concat(groupFooters(item.items));
|
|
}
|
|
result.push(item.aggregates);
|
|
}
|
|
return result;
|
|
}
|
|
function showColumnCells(rows, columnIndex) {
|
|
var idx = 0, length = rows.length, cell, row, columns;
|
|
for (; idx < length; idx += 1) {
|
|
row = rows.eq(idx);
|
|
if (row.is('.k-grouping-row,.k-detail-row')) {
|
|
cell = row.children(':not(.k-group-cell):first,.k-detail-cell').last();
|
|
cell.attr('colspan', parseInt(cell.attr('colspan'), 10) + 1);
|
|
} else {
|
|
if (row.hasClass('k-grid-edit-row') && (cell = row.children('.k-edit-container')[0])) {
|
|
cell = $(cell);
|
|
cell.attr('colspan', parseInt(cell.attr('colspan'), 10) + 1);
|
|
normalizeCols(cell.find('>form>table'), visibleColumns(columns), false, 0);
|
|
row = cell.find('tr:first');
|
|
}
|
|
setCellVisibility(row[0].cells, columnIndex, true);
|
|
}
|
|
}
|
|
}
|
|
function updateColspan(toAdd, toRemove, num) {
|
|
num = num || 1;
|
|
var item, idx, length;
|
|
for (idx = 0, length = toAdd.length; idx < length; idx++) {
|
|
item = toAdd.eq(idx).children().last();
|
|
item.attr('colspan', parseInt(item.attr('colspan'), 10) + num);
|
|
item = toRemove.eq(idx).children().last();
|
|
item.attr('colspan', parseInt(item.attr('colspan'), 10) - num);
|
|
}
|
|
}
|
|
function tableWidth(table) {
|
|
var idx, length, width = 0;
|
|
var cols = table.find('>colgroup>col');
|
|
for (idx = 0, length = cols.length; idx < length; idx += 1) {
|
|
width += parseInt(cols[idx].style.width, 10);
|
|
}
|
|
return width;
|
|
}
|
|
var Grid = kendo.ui.DataBoundWidget.extend({
|
|
init: function (element, options, events) {
|
|
var that = this;
|
|
options = isArray(options) ? { dataSource: options } : options;
|
|
Widget.fn.init.call(that, element, options);
|
|
if (events) {
|
|
that._events = events;
|
|
}
|
|
isRtl = kendo.support.isRtl(element);
|
|
that._element();
|
|
that._aria();
|
|
that._columns(that.options.columns);
|
|
that._dataSource();
|
|
that._tbody();
|
|
that._pageable();
|
|
that._thead();
|
|
that._groupable();
|
|
that._toolbar();
|
|
that._setContentHeight();
|
|
that._templates();
|
|
that._navigatable();
|
|
that._selectable();
|
|
that._clipboard();
|
|
that._details();
|
|
that._editable();
|
|
that._attachCustomCommandsEvent();
|
|
that._minScreenSupport();
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
} else {
|
|
that._group = that._groups() > 0;
|
|
that._footer();
|
|
}
|
|
if (that.lockedContent) {
|
|
that.wrapper.addClass('k-grid-lockedcolumns');
|
|
that._resizeHandler = function () {
|
|
that.resize();
|
|
};
|
|
$(window).on('resize' + NS, that._resizeHandler);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
'dataBinding',
|
|
'cancel',
|
|
DATABOUND,
|
|
DETAILEXPAND,
|
|
DETAILCOLLAPSE,
|
|
DETAILINIT,
|
|
FILTERMENUINIT,
|
|
COLUMNMENUINIT,
|
|
EDIT,
|
|
SAVE,
|
|
REMOVE,
|
|
SAVECHANGES,
|
|
COLUMNRESIZE,
|
|
COLUMNREORDER,
|
|
COLUMNSHOW,
|
|
COLUMNHIDE,
|
|
COLUMNLOCK,
|
|
COLUMNUNLOCK,
|
|
NAVIGATE
|
|
],
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
var scrollable = that.options.scrollable;
|
|
that.options.dataSource = dataSource;
|
|
that._dataSource();
|
|
that._pageable();
|
|
that._thead();
|
|
if (scrollable) {
|
|
if (scrollable.virtual) {
|
|
that.content.find('>.k-virtual-scrollable-wrap').scrollLeft(0);
|
|
} else {
|
|
that.content.scrollLeft(0);
|
|
}
|
|
}
|
|
if (that.options.groupable) {
|
|
that._groupable();
|
|
}
|
|
if (that.virtualScrollable) {
|
|
that.virtualScrollable.setDataSource(that.options.dataSource);
|
|
}
|
|
if (that.options.navigatable) {
|
|
that._navigatable();
|
|
}
|
|
if (that.options.selectable) {
|
|
that._selectable();
|
|
}
|
|
if (that.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
options: {
|
|
name: 'Grid',
|
|
columns: [],
|
|
toolbar: null,
|
|
autoBind: true,
|
|
filterable: false,
|
|
scrollable: true,
|
|
sortable: false,
|
|
selectable: false,
|
|
allowCopy: false,
|
|
navigatable: false,
|
|
pageable: false,
|
|
editable: false,
|
|
groupable: false,
|
|
rowTemplate: '',
|
|
altRowTemplate: '',
|
|
noRecords: false,
|
|
dataSource: {},
|
|
height: null,
|
|
resizable: false,
|
|
reorderable: false,
|
|
columnMenu: false,
|
|
detailTemplate: null,
|
|
columnResizeHandleWidth: 3,
|
|
mobile: '',
|
|
messages: {
|
|
editable: {
|
|
cancelDelete: CANCELDELETE,
|
|
confirmation: DELETECONFIRM,
|
|
confirmDelete: CONFIRMDELETE
|
|
},
|
|
commands: {
|
|
create: defaultCommands.create.text,
|
|
cancel: defaultCommands.cancel.text,
|
|
save: defaultCommands.save.text,
|
|
destroy: defaultCommands.destroy.text,
|
|
edit: defaultCommands.edit.text,
|
|
update: defaultCommands.update.text,
|
|
canceledit: defaultCommands.canceledit.text,
|
|
excel: defaultCommands.excel.text,
|
|
pdf: defaultCommands.pdf.text
|
|
},
|
|
noRecords: NORECORDS
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var that = this, element;
|
|
that._angularItems('cleanup');
|
|
that._destroyColumnAttachments();
|
|
Widget.fn.destroy.call(that);
|
|
this._navigatableTables = null;
|
|
if (that._resizeHandler) {
|
|
$(window).off('resize' + NS, that._resizeHandler);
|
|
}
|
|
if (that.pager && that.pager.element) {
|
|
that.pager.destroy();
|
|
}
|
|
that.pager = null;
|
|
if (that.groupable && that.groupable.element) {
|
|
that.groupable.element.kendoGroupable('destroy');
|
|
}
|
|
that.groupable = null;
|
|
if (that.options.reorderable) {
|
|
that.wrapper.data('kendoReorderable').destroy();
|
|
}
|
|
if (that.selectable && that.selectable.element) {
|
|
that.selectable.destroy();
|
|
that.clearArea();
|
|
if (that.copyHandler) {
|
|
that.wrapper.off('keydown', that.copyHandler);
|
|
that.unbind(that.copyHandler);
|
|
}
|
|
if (that.updateClipBoardState) {
|
|
that.unbind(that.updateClipBoardState);
|
|
that.updateClipBoardState = null;
|
|
}
|
|
if (that.clearAreaHandler) {
|
|
that.wrapper.off('keyup', that.clearAreaHandler);
|
|
}
|
|
}
|
|
that.selectable = null;
|
|
if (that.resizable) {
|
|
that.resizable.destroy();
|
|
if (that._resizeUserEvents) {
|
|
if (that._resizeHandleDocumentClickHandler) {
|
|
$(document).off('click', that._resizeHandleDocumentClickHandler);
|
|
}
|
|
that._resizeUserEvents.destroy();
|
|
that._resizeUserEvents = null;
|
|
}
|
|
that.resizable = null;
|
|
}
|
|
if (that.virtualScrollable && that.virtualScrollable.element) {
|
|
that.virtualScrollable.destroy();
|
|
}
|
|
that.virtualScrollable = null;
|
|
that._destroyEditable();
|
|
if (that.dataSource) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
|
|
that._refreshHandler = that._progressHandler = that._errorHandler = null;
|
|
}
|
|
element = that.element.add(that.wrapper).add(that.table).add(that.thead).add(that.wrapper.find('>.k-grid-toolbar'));
|
|
if (that.content) {
|
|
element = element.add(that.content).add(that.content.find('>.k-virtual-scrollable-wrap'));
|
|
}
|
|
if (that.lockedHeader) {
|
|
that._removeLockedContainers();
|
|
}
|
|
if (that.pane) {
|
|
that.pane.destroy();
|
|
}
|
|
if (that.minScreenResizeHandler) {
|
|
$(window).off('resize', that.minScreenResizeHandler);
|
|
}
|
|
if (that._draggableInstance && that._draggableInstance.element) {
|
|
that._draggableInstance.destroy();
|
|
}
|
|
that._draggableInstance = null;
|
|
element.off(NS);
|
|
kendo.destroy(that.wrapper);
|
|
that.rowTemplate = that.altRowTemplate = that.lockedRowTemplate = that.lockedAltRowTemplate = that.detailTemplate = that.footerTemplate = that.groupFooterTemplate = that.lockedGroupFooterTemplate = that.noRecordsTemplate = null;
|
|
that.scrollables = that.thead = that.tbody = that.element = that.table = that.content = that.footer = that.wrapper = that.lockedTable = that.lockedContent = that.lockedHeader = that.lockedFooter = that._groupableClickHandler = that._setContentWidthHandler = null;
|
|
},
|
|
getOptions: function () {
|
|
var options = this.options;
|
|
options.dataSource = null;
|
|
var result = extend(true, {}, this.options);
|
|
result.columns = kendo.deepExtend([], this.columns);
|
|
var dataSource = this.dataSource;
|
|
var initialData = dataSource.options.data && dataSource._data;
|
|
dataSource.options.data = null;
|
|
result.dataSource = $.extend(true, {}, dataSource.options);
|
|
dataSource.options.data = initialData;
|
|
result.dataSource.data = initialData;
|
|
result.dataSource.page = dataSource.page();
|
|
result.dataSource.filter = dataSource.filter();
|
|
result.dataSource.pageSize = dataSource.pageSize();
|
|
result.dataSource.sort = dataSource.sort();
|
|
result.dataSource.group = dataSource.group();
|
|
result.dataSource.aggregate = dataSource.aggregate();
|
|
if (result.dataSource.transport) {
|
|
result.dataSource.transport.dataSource = null;
|
|
}
|
|
if (result.pageable && result.pageable.pageSize) {
|
|
result.pageable.pageSize = dataSource.pageSize();
|
|
}
|
|
result.$angular = undefined;
|
|
return result;
|
|
},
|
|
setOptions: function (options) {
|
|
var currentOptions = this.getOptions();
|
|
kendo.deepExtend(currentOptions, options);
|
|
if (!options.dataSource) {
|
|
currentOptions.dataSource = this.dataSource;
|
|
}
|
|
var wrapper = this.wrapper;
|
|
var events = this._events;
|
|
var element = this.element;
|
|
this.destroy();
|
|
this.options = null;
|
|
if (this._isMobile) {
|
|
var mobileWrapper = wrapper.closest(kendo.roleSelector('pane')).parent();
|
|
mobileWrapper.after(wrapper);
|
|
mobileWrapper.remove();
|
|
wrapper.removeClass('k-grid-mobile');
|
|
}
|
|
if (wrapper[0] !== element[0]) {
|
|
wrapper.before(element);
|
|
wrapper.remove();
|
|
}
|
|
element.empty();
|
|
this.init(element, currentOptions, events);
|
|
this._setEvents(currentOptions);
|
|
},
|
|
items: function () {
|
|
if (this.lockedContent) {
|
|
return this._items(this.tbody).add(this._items(this.lockedTable.children('tbody')));
|
|
} else {
|
|
return this._items(this.tbody);
|
|
}
|
|
},
|
|
_items: function (container) {
|
|
return container.children().filter(function () {
|
|
var tr = $(this);
|
|
return !tr.hasClass('k-grouping-row') && !tr.hasClass('k-detail-row') && !tr.hasClass('k-group-footer');
|
|
});
|
|
},
|
|
dataItems: function () {
|
|
var dataItems = kendo.ui.DataBoundWidget.fn.dataItems.call(this);
|
|
if (this.lockedContent) {
|
|
var n = dataItems.length, tmp = new Array(2 * n);
|
|
for (var i = n; --i >= 0;) {
|
|
tmp[i] = tmp[i + n] = dataItems[i];
|
|
}
|
|
dataItems = tmp;
|
|
}
|
|
return dataItems;
|
|
},
|
|
_destroyColumnAttachments: function () {
|
|
var that = this;
|
|
that.resizeHandle = null;
|
|
if (!that.thead) {
|
|
return;
|
|
}
|
|
this.angular('cleanup', function () {
|
|
return { elements: that.thead.get() };
|
|
});
|
|
that.thead.add(that.lockedHeader).find('th').each(function () {
|
|
var th = $(this), filterMenu = th.data('kendoFilterMenu'), sortable = th.data('kendoColumnSorter'), columnMenu = th.data('kendoColumnMenu');
|
|
if (filterMenu) {
|
|
filterMenu.destroy();
|
|
}
|
|
if (sortable) {
|
|
sortable.destroy();
|
|
}
|
|
if (columnMenu) {
|
|
columnMenu.destroy();
|
|
}
|
|
});
|
|
},
|
|
_attachCustomCommandsEvent: function () {
|
|
var that = this, columns = leafColumns(that.columns || []), command, idx, length;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
command = columns[idx].command;
|
|
if (command) {
|
|
attachCustomCommandEvent(that, that.wrapper, command);
|
|
}
|
|
}
|
|
},
|
|
_aria: function () {
|
|
var id = this.element.attr('id') || 'aria';
|
|
if (id) {
|
|
this._cellId = id + '_active_cell';
|
|
}
|
|
},
|
|
_element: function () {
|
|
var that = this, table = that.element;
|
|
if (!table.is('table')) {
|
|
if (that.options.scrollable) {
|
|
table = that.element.find('> .k-grid-content > table');
|
|
} else {
|
|
table = that.element.children('table');
|
|
}
|
|
if (!table.length) {
|
|
table = $('<table />').appendTo(that.element);
|
|
}
|
|
}
|
|
if (isIE7) {
|
|
table.attr('cellspacing', 0);
|
|
}
|
|
that.table = table.attr('role', that._hasDetails() ? 'treegrid' : 'grid');
|
|
that._wrapper();
|
|
},
|
|
_createResizeHandle: function (container, th) {
|
|
var that = this;
|
|
var indicatorWidth = that.options.columnResizeHandleWidth;
|
|
var scrollable = that.options.scrollable;
|
|
var resizeHandle = that.resizeHandle;
|
|
var groups = this._groups();
|
|
var left;
|
|
if (resizeHandle && that.lockedContent && resizeHandle.data('th')[0] !== th[0]) {
|
|
resizeHandle.off(NS).remove();
|
|
resizeHandle = null;
|
|
}
|
|
if (!resizeHandle) {
|
|
resizeHandle = that.resizeHandle = $('<div class="k-resize-handle"><div class="k-resize-handle-inner"></div></div>');
|
|
container.append(resizeHandle);
|
|
}
|
|
if (!isRtl) {
|
|
left = th[0].offsetWidth;
|
|
var cells = leafDataCells(th.closest('thead')).filter(':visible');
|
|
for (var idx = 0; idx < cells.length; idx++) {
|
|
if (cells[idx] == th[0]) {
|
|
break;
|
|
}
|
|
left += cells[idx].offsetWidth;
|
|
}
|
|
if (groups > 0) {
|
|
left += container.find('.k-group-cell:first').outerWidth() * groups;
|
|
}
|
|
if (that._hasDetails()) {
|
|
left += container.find('.k-hierarchy-cell:first').outerWidth();
|
|
}
|
|
} else {
|
|
left = th.position().left;
|
|
if (scrollable) {
|
|
var headerWrap = th.closest('.k-grid-header-wrap, .k-grid-header-locked'), ieCorrection = browser.msie ? headerWrap.scrollLeft() : 0, webkitCorrection = browser.webkit ? headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - headerWrap.scrollLeft() : 0, firefoxCorrection = browser.mozilla ? headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - (headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - headerWrap.scrollLeft()) : 0;
|
|
left -= webkitCorrection - firefoxCorrection + ieCorrection;
|
|
}
|
|
}
|
|
resizeHandle.css({
|
|
top: th.position().top,
|
|
left: left - indicatorWidth,
|
|
height: th.outerHeight(),
|
|
width: indicatorWidth * 3
|
|
}).data('th', th).show();
|
|
resizeHandle.off('dblclick' + NS).on('dblclick' + NS, function () {
|
|
that._autoFitLeafColumn(th.data('index'));
|
|
});
|
|
},
|
|
_positionColumnResizeHandle: function () {
|
|
var that = this, indicatorWidth = that.options.columnResizeHandleWidth, lockedHead = that.lockedHeader ? that.lockedHeader.find('thead:first') : $();
|
|
that.thead.add(lockedHead).on('mousemove' + NS, 'th', function (e) {
|
|
var th = $(this);
|
|
if (th.hasClass('k-group-cell') || th.hasClass('k-hierarchy-cell')) {
|
|
return;
|
|
}
|
|
var clientX = e.clientX / parseFloat(document.documentElement.style.zoom || document.body.style.zoom || 1), winScrollLeft = $(window).scrollLeft(), position = th.offset().left + (!isRtl ? this.offsetWidth : 0);
|
|
if (clientX + winScrollLeft > position - indicatorWidth && clientX + winScrollLeft < position + indicatorWidth) {
|
|
that._createResizeHandle(th.closest('div'), th);
|
|
} else if (that.resizeHandle) {
|
|
that.resizeHandle.hide();
|
|
} else {
|
|
cursor(that.wrapper, '');
|
|
}
|
|
});
|
|
},
|
|
_resizeHandleDocumentClick: function (e) {
|
|
if ($(e.target).closest('.k-column-active').length) {
|
|
return;
|
|
}
|
|
$(document).off(e);
|
|
this._hideResizeHandle();
|
|
},
|
|
_hideResizeHandle: function () {
|
|
if (this.resizeHandle) {
|
|
this.resizeHandle.data('th').removeClass('k-column-active');
|
|
if (this.lockedContent && !this._isMobile) {
|
|
this.resizeHandle.off(NS).remove();
|
|
this.resizeHandle = null;
|
|
} else {
|
|
this.resizeHandle.hide();
|
|
}
|
|
}
|
|
},
|
|
_positionColumnResizeHandleTouch: function () {
|
|
var that = this, lockedHead = that.lockedHeader ? that.lockedHeader.find('thead:first') : $();
|
|
that._resizeUserEvents = new kendo.UserEvents(lockedHead.add(that.thead), {
|
|
filter: 'th:not(.k-group-cell):not(.k-hierarchy-cell)',
|
|
threshold: 10,
|
|
hold: function (e) {
|
|
var th = $(e.target);
|
|
e.preventDefault();
|
|
th.addClass('k-column-active');
|
|
that._createResizeHandle(th.closest('div'), th);
|
|
if (!that._resizeHandleDocumentClickHandler) {
|
|
that._resizeHandleDocumentClickHandler = proxy(that._resizeHandleDocumentClick, that);
|
|
}
|
|
$(document).on('click', that._resizeHandleDocumentClickHandler);
|
|
}
|
|
});
|
|
},
|
|
_resizable: function () {
|
|
var that = this, options = that.options, container, columnStart, columnWidth, gridWidth, isMobile = this._isMobile, scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0, isLocked, col, th;
|
|
if (options.resizable) {
|
|
container = options.scrollable ? that.wrapper.find('.k-grid-header-wrap:first') : that.wrapper;
|
|
if (isMobile) {
|
|
that._positionColumnResizeHandleTouch(container);
|
|
} else {
|
|
that._positionColumnResizeHandle(container);
|
|
}
|
|
if (that.resizable) {
|
|
that.resizable.destroy();
|
|
}
|
|
that.resizable = new ui.Resizable(container.add(that.lockedHeader), {
|
|
handle: (!!options.scrollable ? '' : '>') + '.k-resize-handle',
|
|
hint: function (handle) {
|
|
return $('<div class="k-grid-resize-indicator" />').css({ height: handle.data('th').outerHeight() + that.tbody.attr('clientHeight') });
|
|
},
|
|
start: function (e) {
|
|
th = $(e.currentTarget).data('th');
|
|
if (isMobile) {
|
|
that._hideResizeHandle();
|
|
}
|
|
var header = th.closest('table'), index = $.inArray(th[0], leafDataCells(th.closest('thead')).filter(':visible'));
|
|
isLocked = header.parent().hasClass('k-grid-header-locked');
|
|
var contentTable = isLocked ? that.lockedTable : that.table, footer = that.footer || $();
|
|
if (that.footer && that.lockedContent) {
|
|
footer = isLocked ? that.footer.children('.k-grid-footer-locked') : that.footer.children('.k-grid-footer-wrap');
|
|
}
|
|
cursor(that.wrapper, 'col-resize');
|
|
if (options.scrollable) {
|
|
col = header.find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')').add(contentTable.children('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')')).add(footer.find('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')'));
|
|
} else {
|
|
col = contentTable.children('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')');
|
|
}
|
|
columnStart = e.x.location;
|
|
columnWidth = th.outerWidth();
|
|
gridWidth = isLocked ? contentTable.children('tbody').outerWidth() : that.tbody.outerWidth();
|
|
if (browser.webkit) {
|
|
that.wrapper.addClass('k-grid-column-resizing');
|
|
}
|
|
},
|
|
resize: function (e) {
|
|
var rtlMultiplier = isRtl ? -1 : 1, currentWidth = columnWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier;
|
|
if (options.scrollable) {
|
|
var footer;
|
|
if (isLocked && that.lockedFooter) {
|
|
footer = that.lockedFooter.children('table');
|
|
} else if (that.footer) {
|
|
footer = that.footer.find('>.k-grid-footer-wrap>table');
|
|
}
|
|
if (!footer || !footer[0]) {
|
|
footer = $();
|
|
}
|
|
var header = th.closest('table');
|
|
var contentTable = isLocked ? that.lockedTable : that.table;
|
|
var constrain = false;
|
|
var totalWidth = that.wrapper.width() - scrollbar;
|
|
var width = currentWidth;
|
|
if (isLocked && gridWidth - columnWidth + width > totalWidth) {
|
|
width = columnWidth + (totalWidth - gridWidth - scrollbar * 2);
|
|
if (width < 0) {
|
|
width = currentWidth;
|
|
}
|
|
constrain = true;
|
|
}
|
|
if (width > 10) {
|
|
col.css('width', width);
|
|
if (gridWidth) {
|
|
if (constrain) {
|
|
width = totalWidth - scrollbar * 2;
|
|
} else {
|
|
width = gridWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier;
|
|
}
|
|
contentTable.add(header).add(footer).css('width', width);
|
|
if (!isLocked) {
|
|
that._footerWidth = width;
|
|
}
|
|
}
|
|
}
|
|
} else if (currentWidth > 10) {
|
|
col.css('width', currentWidth);
|
|
}
|
|
},
|
|
resizeend: function () {
|
|
var newWidth = th.outerWidth(), column, header;
|
|
cursor(that.wrapper, '');
|
|
if (browser.webkit) {
|
|
that.wrapper.removeClass('k-grid-column-resizing');
|
|
}
|
|
if (columnWidth != newWidth) {
|
|
header = that.lockedHeader ? that.lockedHeader.find('thead:first tr:first').add(that.thead.find('tr:first')) : th.parent();
|
|
var index = th.attr(kendo.attr('index'));
|
|
if (!index) {
|
|
index = header.find('th:not(.k-group-cell):not(.k-hierarchy-cell)').index(th);
|
|
}
|
|
column = leafColumns(that.columns)[index];
|
|
column.width = newWidth;
|
|
that.trigger(COLUMNRESIZE, {
|
|
column: column,
|
|
oldWidth: columnWidth,
|
|
newWidth: newWidth
|
|
});
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
}
|
|
that._hideResizeHandle();
|
|
th = null;
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_draggable: function () {
|
|
var that = this;
|
|
if (that.options.reorderable) {
|
|
if (that._draggableInstance) {
|
|
that._draggableInstance.destroy();
|
|
}
|
|
that._draggableInstance = that.wrapper.kendoDraggable({
|
|
group: kendo.guid(),
|
|
autoScroll: true,
|
|
filter: that.content ? '.k-grid-header:first ' + HEADERCELLS : 'table:first>.k-grid-header ' + HEADERCELLS,
|
|
drag: function () {
|
|
that._hideResizeHandle();
|
|
},
|
|
hint: function (target) {
|
|
var title = target.attr(kendo.attr('title'));
|
|
if (title) {
|
|
title = kendo.htmlEncode(title);
|
|
}
|
|
return $('<div class="k-header k-drag-clue" />').css({
|
|
width: target.width(),
|
|
paddingLeft: target.css('paddingLeft'),
|
|
paddingRight: target.css('paddingRight'),
|
|
lineHeight: target.height() + 'px',
|
|
paddingTop: target.css('paddingTop'),
|
|
paddingBottom: target.css('paddingBottom')
|
|
}).html(title || target.attr(kendo.attr('field')) || target.text()).prepend('<span class="k-icon k-drag-status k-denied" />');
|
|
}
|
|
}).data('kendoDraggable');
|
|
}
|
|
},
|
|
_reorderable: function () {
|
|
var that = this;
|
|
if (that.options.reorderable) {
|
|
if (that.wrapper.data('kendoReorderable')) {
|
|
that.wrapper.data('kendoReorderable').destroy();
|
|
}
|
|
var targetParentContainerIndex = function (columns, sourceIndex, targetIndex) {
|
|
var column = columns[sourceIndex];
|
|
var target = columns[targetIndex];
|
|
var parent = columnParent(column, that.columns);
|
|
columns = parent ? parent.columns : that.columns;
|
|
return inArray(target, columns);
|
|
};
|
|
that.wrapper.kendoReorderable({
|
|
draggable: that._draggableInstance,
|
|
dragOverContainers: function (sourceIndex, targetIndex) {
|
|
var columns = flatColumnsInDomOrder(that.columns);
|
|
return columns[sourceIndex].lockable !== false && targetParentContainerIndex(columns, sourceIndex, targetIndex) > -1;
|
|
},
|
|
inSameContainer: function (e) {
|
|
return $(e.source).parent()[0] === $(e.target).parent()[0] && targetParentContainerIndex(flatColumnsInDomOrder(that.columns), e.sourceIndex, e.targetIndex) > -1;
|
|
},
|
|
change: function (e) {
|
|
var columns = flatColumnsInDomOrder(that.columns);
|
|
var column = columns[e.oldIndex];
|
|
var newIndex = targetParentContainerIndex(columns, e.oldIndex, e.newIndex);
|
|
that.trigger(COLUMNREORDER, {
|
|
newIndex: newIndex,
|
|
oldIndex: inArray(column, columns),
|
|
column: column
|
|
});
|
|
that.reorderColumn(newIndex, column, e.position === 'before');
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_reorderHeader: function (sources, target, before) {
|
|
var that = this;
|
|
var sourcePosition = columnPosition(sources[0], that.columns);
|
|
var destPosition = columnPosition(target, that.columns);
|
|
var leafs = [];
|
|
for (var idx = 0; idx < sources.length; idx++) {
|
|
if (sources[idx].columns) {
|
|
leafs = leafs.concat(sources[idx].columns);
|
|
}
|
|
}
|
|
var ths = elements(that.lockedHeader, that.thead, 'tr:eq(' + sourcePosition.row + ')>th.k-header:not(.k-group-cell,.k-hierarchy-cell)');
|
|
var sourceLockedColumns = lockedColumns(sources).length;
|
|
var targetLockedColumns = lockedColumns([target]).length;
|
|
if (leafs.length) {
|
|
if (sourceLockedColumns > 0 && targetLockedColumns === 0) {
|
|
moveCellsBetweenContainers(sources, target, leafs, that.columns, that.lockedHeader.find('thead'), that.thead, this._groups());
|
|
} else if (sourceLockedColumns === 0 && targetLockedColumns > 0) {
|
|
moveCellsBetweenContainers(sources, target, leafs, that.columns, that.thead, that.lockedHeader.find('thead'), this._groups());
|
|
}
|
|
if (target.columns || sourcePosition.cell - destPosition.cell > 1 || destPosition.cell - sourcePosition.cell > 1) {
|
|
target = findReorderTarget(that.columns, target, sources[0], before);
|
|
if (target) {
|
|
that._reorderHeader(leafs, target, before);
|
|
}
|
|
}
|
|
} else if (sourceLockedColumns !== targetLockedColumns) {
|
|
updateCellRowSpan(ths[sourcePosition.cell], that.columns, sourceLockedColumns);
|
|
}
|
|
reorder(ths, sourcePosition.cell, destPosition.cell, before, sources.length);
|
|
},
|
|
_reorderContent: function (sources, destination, before) {
|
|
var that = this;
|
|
var lockedRows = $();
|
|
var source = sources[0];
|
|
var visibleSources = visibleColumns(sources);
|
|
var sourceIndex = inArray(source, leafColumns(that.columns));
|
|
var destIndex = inArray(destination, leafColumns(that.columns));
|
|
var colSourceIndex = inArray(source, visibleLeafColumns(that.columns));
|
|
var colDest = inArray(destination, visibleLeafColumns(that.columns));
|
|
var lockedCount = lockedColumns(that.columns).length;
|
|
var isLocked = !!destination.locked;
|
|
var footer = that.footer || that.wrapper.find('.k-grid-footer');
|
|
var headerCol, footerCol;
|
|
headerCol = footerCol = colDest;
|
|
if (destination.hidden) {
|
|
if (isLocked) {
|
|
colDest = that.lockedTable.find('colgroup');
|
|
headerCol = that.lockedHeader.find('colgroup');
|
|
footerCol = $(that.lockedFooter).find('>table>colgroup');
|
|
} else {
|
|
colDest = that.tbody.prev();
|
|
headerCol = that.thead.prev();
|
|
footerCol = footer.find('.k-grid-footer-wrap').find('>table>colgroup');
|
|
}
|
|
}
|
|
if (that._hasFilterRow()) {
|
|
reorder(that.wrapper.find('.k-filter-row th:not(.k-group-cell,.k-hierarchy-cell)'), sourceIndex, destIndex, before, sources.length);
|
|
}
|
|
reorder(elements(that.lockedHeader, that.thead.prev(), 'col:not(.k-group-col,.k-hierarchy-col)'), colSourceIndex, headerCol, before, visibleSources.length);
|
|
if (that.options.scrollable) {
|
|
reorder(elements(that.lockedTable, that.tbody.prev(), 'col:not(.k-group-col,.k-hierarchy-col)'), colSourceIndex, colDest, before, visibleSources.length);
|
|
}
|
|
if (footer && footer.length) {
|
|
reorder(elements(that.lockedFooter, footer.find('.k-grid-footer-wrap'), '>table>colgroup>col:not(.k-group-col,.k-hierarchy-col)'), colSourceIndex, footerCol, before, visibleSources.length);
|
|
reorder(footer.find('.k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)'), sourceIndex, destIndex, before, sources.length);
|
|
}
|
|
var rows = that.tbody.children(':not(.k-grouping-row,.k-detail-row)');
|
|
if (that.lockedTable) {
|
|
if (lockedCount > destIndex) {
|
|
if (lockedCount <= sourceIndex) {
|
|
updateColspan(that.lockedTable.find('>tbody>tr.k-grouping-row'), that.table.find('>tbody>tr.k-grouping-row'), sources.length);
|
|
}
|
|
} else if (lockedCount > sourceIndex) {
|
|
updateColspan(that.table.find('>tbody>tr.k-grouping-row'), that.lockedTable.find('>tbody>tr.k-grouping-row'), sources.length);
|
|
}
|
|
lockedRows = that.lockedTable.find('>tbody>tr:not(.k-grouping-row,.k-detail-row)');
|
|
}
|
|
for (var idx = 0, length = rows.length; idx < length; idx += 1) {
|
|
reorder(elements(lockedRows[idx], rows[idx], '>td:not(.k-group-cell,.k-hierarchy-cell)'), sourceIndex, destIndex, before, sources.length);
|
|
}
|
|
},
|
|
_autoFitLeafColumn: function (leafIndex) {
|
|
this.autoFitColumn(leafColumns(this.columns)[leafIndex]);
|
|
},
|
|
autoFitColumn: function (column) {
|
|
var that = this, options = that.options, columns = that.columns, index, th, headerTable, isLocked, visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find('>table>thead')).filter(isCellVisible).length : 0, col, notGroupOrHierarchyCol = 'col:not(.k-group-col):not(.k-hierarchy-col)', notGroupOrHierarchyVisibleCell = 'td:visible:not(.k-group-cell):not(.k-hierarchy-cell)';
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else if (isPlainObject(column)) {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item === column;
|
|
})[0];
|
|
} else {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || !isVisible(column)) {
|
|
return;
|
|
}
|
|
index = inArray(column, leafColumns(columns));
|
|
isLocked = column.locked;
|
|
if (isLocked) {
|
|
headerTable = that.lockedHeader.children('table');
|
|
} else {
|
|
headerTable = that.thead.parent();
|
|
}
|
|
th = headerTable.find('[data-index=\'' + index + '\']');
|
|
var contentTable = isLocked ? that.lockedTable : that.table, footer = that.footer || $();
|
|
if (that.footer && that.lockedContent) {
|
|
footer = isLocked ? that.footer.children('.k-grid-footer-locked') : that.footer.children('.k-grid-footer-wrap');
|
|
}
|
|
var footerTable = footer.find('table').first();
|
|
if (that.lockedHeader && !isLocked) {
|
|
index -= visibleLocked;
|
|
}
|
|
for (var j = 0; j < columns.length; j++) {
|
|
if (columns[j] === column) {
|
|
break;
|
|
} else {
|
|
if (columns[j].hidden) {
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
if (options.scrollable) {
|
|
col = headerTable.find(notGroupOrHierarchyCol).eq(index).add(contentTable.children('colgroup').find(notGroupOrHierarchyCol).eq(index)).add(footerTable.find('colgroup').find(notGroupOrHierarchyCol).eq(index));
|
|
} else {
|
|
col = contentTable.children('colgroup').find(notGroupOrHierarchyCol).eq(index);
|
|
}
|
|
var tables = headerTable.add(contentTable).add(footerTable);
|
|
var oldColumnWidth = th.outerWidth();
|
|
col.width('');
|
|
tables.css('table-layout', 'fixed');
|
|
col.width('auto');
|
|
tables.addClass('k-autofitting');
|
|
tables.css('table-layout', '');
|
|
var newColumnWidth = Math.ceil(Math.max(th.outerWidth(), contentTable.find('tr:not(.k-grouping-row)').eq(0).children(notGroupOrHierarchyVisibleCell).eq(index).outerWidth(), footerTable.find('tr').eq(0).children(notGroupOrHierarchyVisibleCell).eq(index).outerWidth())) + 1;
|
|
col.width(newColumnWidth);
|
|
column.width = newColumnWidth;
|
|
if (options.scrollable) {
|
|
var cols = headerTable.find('col'), colWidth, totalWidth = 0;
|
|
for (var idx = 0, length = cols.length; idx < length; idx += 1) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
totalWidth += parseInt(colWidth, 10);
|
|
} else {
|
|
totalWidth = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (totalWidth) {
|
|
tables.each(function () {
|
|
this.style.width = totalWidth + 'px';
|
|
});
|
|
}
|
|
}
|
|
if (browser.msie && browser.version == 8) {
|
|
tables.css('display', 'inline-table');
|
|
setTimeout(function () {
|
|
tables.css('display', 'table');
|
|
}, 1);
|
|
}
|
|
tables.removeClass('k-autofitting');
|
|
that.trigger(COLUMNRESIZE, {
|
|
column: column,
|
|
oldWidth: oldColumnWidth,
|
|
newWidth: newColumnWidth
|
|
});
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
},
|
|
reorderColumn: function (destIndex, column, before) {
|
|
var that = this, parent = columnParent(column, that.columns), columns = parent ? parent.columns : that.columns, sourceIndex = inArray(column, columns), destColumn = columns[destIndex], lockChanged, isLocked = !!destColumn.locked, lockedCount = lockedColumns(that.columns).length;
|
|
if (sourceIndex === destIndex) {
|
|
return;
|
|
}
|
|
if (!column.locked && isLocked && nonLockedColumns(that.columns).length == 1) {
|
|
return;
|
|
}
|
|
if (column.locked && !isLocked && lockedCount == 1) {
|
|
return;
|
|
}
|
|
that._hideResizeHandle();
|
|
if (before === undefined) {
|
|
before = destIndex < sourceIndex;
|
|
}
|
|
var sourceColumns = [column];
|
|
that._reorderHeader(sourceColumns, destColumn, before);
|
|
if (that.lockedHeader) {
|
|
removeEmptyRows(that.thead);
|
|
removeEmptyRows(that.lockedHeader);
|
|
}
|
|
if (destColumn.columns) {
|
|
destColumn = leafColumns(destColumn.columns);
|
|
destColumn = destColumn[before ? 0 : destColumn.length - 1];
|
|
}
|
|
if (column.columns) {
|
|
sourceColumns = leafColumns(column.columns);
|
|
}
|
|
that._reorderContent(sourceColumns, destColumn, before);
|
|
lockChanged = !!column.locked;
|
|
lockChanged = lockChanged != isLocked;
|
|
column.locked = isLocked;
|
|
columns.splice(before ? destIndex : destIndex + 1, 0, column);
|
|
columns.splice(sourceIndex < destIndex ? sourceIndex : sourceIndex + 1, 1);
|
|
that._templates();
|
|
that._updateColumnCellIndex();
|
|
that._updateTablesWidth();
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedHeaderHeight();
|
|
that._syncLockedContentHeight();
|
|
that._updateFirstColumnClass();
|
|
if (!lockChanged) {
|
|
return;
|
|
}
|
|
if (isLocked) {
|
|
that.trigger(COLUMNLOCK, { column: column });
|
|
} else {
|
|
that.trigger(COLUMNUNLOCK, { column: column });
|
|
}
|
|
},
|
|
_updateColumnCellIndex: function () {
|
|
var header;
|
|
var offset = 0;
|
|
if (this.lockedHeader) {
|
|
header = this.lockedHeader.find('thead');
|
|
offset = updateCellIndex(header, lockedColumns(this.columns));
|
|
}
|
|
updateCellIndex(this.thead, nonLockedColumns(this.columns), offset);
|
|
},
|
|
lockColumn: function (column) {
|
|
var columns = this.columns;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else {
|
|
column = grep(columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || column.locked || column.hidden) {
|
|
return;
|
|
}
|
|
var index = lockedColumns(columns).length - 1;
|
|
this.reorderColumn(index, column, false);
|
|
},
|
|
unlockColumn: function (column) {
|
|
var columns = this.columns;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else {
|
|
column = grep(columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || !column.locked || column.hidden) {
|
|
return;
|
|
}
|
|
var index = lockedColumns(columns).length;
|
|
this.reorderColumn(index, column, true);
|
|
},
|
|
cellIndex: function (td) {
|
|
var lockedColumnOffset = 0;
|
|
if (this.lockedTable && !$.contains(this.lockedTable[0], td[0])) {
|
|
lockedColumnOffset = leafColumns(lockedColumns(this.columns)).length;
|
|
}
|
|
return $(td).parent().children('td:not(.k-group-cell,.k-hierarchy-cell)').index(td) + lockedColumnOffset;
|
|
},
|
|
_modelForContainer: function (container) {
|
|
container = $(container);
|
|
if (!container.is('tr') && this._editMode() !== 'popup') {
|
|
container = container.closest('tr');
|
|
}
|
|
var id = container.attr(kendo.attr('uid'));
|
|
return this.dataSource.getByUid(id);
|
|
},
|
|
_editable: function () {
|
|
var that = this, selectable = that.selectable && that.selectable.options.multiple, editable = that.options.editable, handler = function () {
|
|
var target = activeElement(), cell = that._editContainer;
|
|
if (cell && !$.contains(cell[0], target) && cell[0] !== target && !$(target).closest('.k-animation-container').length) {
|
|
if (that.editable.end()) {
|
|
that.closeCell();
|
|
}
|
|
}
|
|
};
|
|
if (editable) {
|
|
this.wrapper.addClass('k-editable');
|
|
var mode = that._editMode();
|
|
if (mode === 'incell') {
|
|
if (editable.update !== false) {
|
|
that.wrapper.on(CLICK + NS, 'tr:not(.k-grouping-row) > td', function (e) {
|
|
var td = $(this), isLockedCell = that.lockedTable && td.closest('table')[0] === that.lockedTable[0];
|
|
if (td.hasClass('k-hierarchy-cell') || td.hasClass('k-detail-cell') || td.hasClass('k-group-cell') || td.hasClass('k-edit-cell') || td.has('a.k-grid-delete').length || td.has('button.k-grid-delete').length || td.closest('tbody')[0] !== that.tbody[0] && !isLockedCell || $(e.target).is(':input')) {
|
|
return;
|
|
}
|
|
if (that.editable) {
|
|
if (that.editable.end()) {
|
|
if (selectable) {
|
|
$(activeElement()).blur();
|
|
}
|
|
that.closeCell();
|
|
that.editCell(td);
|
|
}
|
|
} else {
|
|
that.editCell(td);
|
|
}
|
|
}).on('focusin' + NS, function () {
|
|
if (!$.contains(this, activeElement())) {
|
|
clearTimeout(that.timer);
|
|
that.timer = null;
|
|
}
|
|
}).on('focusout' + NS, function () {
|
|
that.timer = setTimeout(handler, 1);
|
|
});
|
|
}
|
|
} else {
|
|
if (editable.update !== false) {
|
|
that.wrapper.on(CLICK + NS, 'tbody>tr:not(.k-detail-row,.k-grouping-row):visible a.k-grid-edit', function (e) {
|
|
e.preventDefault();
|
|
that.editRow($(this).closest('tr'));
|
|
});
|
|
}
|
|
}
|
|
if (editable.destroy !== false) {
|
|
that.wrapper.on(CLICK + NS, 'tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-delete', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.removeRow($(this).closest('tr'));
|
|
});
|
|
} else {
|
|
that.wrapper.on(CLICK + NS, 'tbody>tr:not(.k-detail-row,.k-grouping-row):visible button.k-grid-delete', function (e) {
|
|
e.stopPropagation();
|
|
if (!that._confirmation()) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
editCell: function (cell) {
|
|
cell = $(cell);
|
|
var that = this, column = leafColumns(that.columns)[that.cellIndex(cell)], model = that._modelForContainer(cell);
|
|
that.closeCell();
|
|
if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field) {
|
|
that._attachModelChange(model);
|
|
that._editContainer = cell;
|
|
that.editable = cell.addClass('k-edit-cell').kendoEditable({
|
|
fields: {
|
|
field: column.field,
|
|
format: column.format,
|
|
editor: column.editor,
|
|
values: column.values
|
|
},
|
|
model: model,
|
|
target: that,
|
|
change: function (e) {
|
|
if (that.trigger(SAVE, {
|
|
values: e.values,
|
|
container: cell,
|
|
model: model
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}).data('kendoEditable');
|
|
var tr = cell.parent().addClass('k-grid-edit-row');
|
|
if (that.lockedContent) {
|
|
adjustRowHeight(tr[0], that._relatedRow(tr).addClass('k-grid-edit-row')[0]);
|
|
}
|
|
that.trigger(EDIT, {
|
|
container: cell,
|
|
model: model
|
|
});
|
|
}
|
|
},
|
|
_adjustLockedHorizontalScrollBar: function () {
|
|
var table = this.table, content = table.parent();
|
|
var scrollbar = table[0].offsetWidth > content[0].clientWidth ? kendo.support.scrollbar() : 0;
|
|
this.lockedContent.height(content.height() - scrollbar);
|
|
},
|
|
_syncLockedContentHeight: function () {
|
|
if (this.lockedTable) {
|
|
if (!this.touchScroller) {
|
|
this._adjustLockedHorizontalScrollBar();
|
|
}
|
|
this._adjustRowsHeight(this.table, this.lockedTable);
|
|
}
|
|
},
|
|
_syncLockedHeaderHeight: function () {
|
|
if (this.lockedHeader) {
|
|
var lockedTable = this.lockedHeader.children('table');
|
|
var table = this.thead.parent();
|
|
this._adjustRowsHeight(lockedTable, table);
|
|
syncTableHeight(lockedTable, table);
|
|
}
|
|
},
|
|
_syncLockedFooterHeight: function () {
|
|
if (this.lockedFooter && this.footer && this.footer.length) {
|
|
this._adjustRowsHeight(this.lockedFooter.children('table'), this.footer.find('.k-grid-footer-wrap > table'));
|
|
}
|
|
},
|
|
_destroyEditable: function () {
|
|
var that = this;
|
|
var destroy = function () {
|
|
if (that.editable) {
|
|
var container = that.editView ? that.editView.element : that._editContainer;
|
|
if (container) {
|
|
container.off(CLICK + NS, 'a.k-grid-cancel', that._editCancelClickHandler);
|
|
container.off(CLICK + NS, 'a.k-grid-update', that._editUpdateClickHandler);
|
|
}
|
|
that._detachModelChange();
|
|
that.editable.destroy();
|
|
that.editable = null;
|
|
that._editContainer = null;
|
|
that._destroyEditView();
|
|
}
|
|
};
|
|
if (that.editable) {
|
|
if (that._editMode() === 'popup' && !that._isMobile) {
|
|
that._editContainer.data('kendoWindow').bind('deactivate', destroy).close();
|
|
} else {
|
|
destroy();
|
|
}
|
|
}
|
|
if (that._actionSheet) {
|
|
that._actionSheet.destroy();
|
|
that._actionSheet = null;
|
|
}
|
|
},
|
|
_destroyEditView: function () {
|
|
if (this.editView) {
|
|
this.editView.purge();
|
|
this.editView = null;
|
|
this.pane.navigate('');
|
|
}
|
|
},
|
|
_attachModelChange: function (model) {
|
|
var that = this;
|
|
that._modelChangeHandler = function (e) {
|
|
that._modelChange({
|
|
field: e.field,
|
|
model: this
|
|
});
|
|
};
|
|
model.bind('change', that._modelChangeHandler);
|
|
},
|
|
_detachModelChange: function () {
|
|
var that = this, container = that._editContainer, model = that._modelForContainer(container);
|
|
if (model) {
|
|
model.unbind(CHANGE, that._modelChangeHandler);
|
|
}
|
|
},
|
|
closeCell: function (isCancel) {
|
|
var that = this, cell = that._editContainer, id, column, tr, model;
|
|
if (!cell) {
|
|
return;
|
|
}
|
|
id = cell.closest('tr').attr(kendo.attr('uid'));
|
|
model = that.dataSource.getByUid(id);
|
|
if (isCancel && that.trigger('cancel', {
|
|
container: cell,
|
|
model: model
|
|
})) {
|
|
return;
|
|
}
|
|
cell.removeClass('k-edit-cell');
|
|
column = leafColumns(that.columns)[that.cellIndex(cell)];
|
|
tr = cell.parent().removeClass('k-grid-edit-row');
|
|
that._destroyEditable();
|
|
that._displayCell(cell, column, model);
|
|
if (cell.hasClass('k-dirty-cell')) {
|
|
$('<span class="k-dirty"/>').prependTo(cell);
|
|
}
|
|
that.trigger('itemChange', {
|
|
item: tr,
|
|
data: model,
|
|
ns: ui
|
|
});
|
|
if (that.lockedContent) {
|
|
adjustRowHeight(tr.css('height', '')[0], that._relatedRow(tr).css('height', '')[0]);
|
|
}
|
|
},
|
|
_displayCell: function (cell, column, dataItem) {
|
|
var that = this, state = {
|
|
storage: {},
|
|
count: 0
|
|
}, settings = extend({}, kendo.Template, that.options.templateSettings), tmpl = kendo.template(that._cellTmpl(column, state), settings);
|
|
if (state.count > 0) {
|
|
tmpl = proxy(tmpl, state.storage);
|
|
}
|
|
cell.empty().html(tmpl(dataItem));
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: cell,
|
|
data: [{ dataItem: dataItem }]
|
|
};
|
|
});
|
|
},
|
|
removeRow: function (row) {
|
|
if (!this._confirmation(row)) {
|
|
return;
|
|
}
|
|
this._removeRow(row);
|
|
},
|
|
_removeRow: function (row) {
|
|
var that = this, model, mode = that._editMode();
|
|
if (mode !== 'incell') {
|
|
that.cancelRow();
|
|
}
|
|
row = $(row);
|
|
if (that.lockedContent) {
|
|
row = row.add(that._relatedRow(row));
|
|
}
|
|
row = row.hide();
|
|
model = that._modelForContainer(row);
|
|
if (model && !that.trigger(REMOVE, {
|
|
row: row,
|
|
model: model
|
|
})) {
|
|
that.dataSource.remove(model);
|
|
if (mode === 'inline' || mode === 'popup') {
|
|
that.dataSource.sync();
|
|
}
|
|
} else if (mode === 'incell') {
|
|
that._destroyEditable();
|
|
}
|
|
},
|
|
_editMode: function () {
|
|
var mode = 'incell', editable = this.options.editable;
|
|
if (editable !== true) {
|
|
if (typeof editable == 'string') {
|
|
mode = editable;
|
|
} else {
|
|
mode = editable.mode || mode;
|
|
}
|
|
}
|
|
return mode;
|
|
},
|
|
editRow: function (row) {
|
|
var model;
|
|
var that = this;
|
|
if (row instanceof kendo.data.ObservableObject) {
|
|
model = row;
|
|
} else {
|
|
row = $(row);
|
|
model = that._modelForContainer(row);
|
|
}
|
|
var mode = that._editMode();
|
|
var container;
|
|
that.cancelRow();
|
|
if (model) {
|
|
row = that.tbody.children('[' + kendo.attr('uid') + '=' + model.uid + ']');
|
|
that._attachModelChange(model);
|
|
if (mode === 'popup') {
|
|
that._createPopupEditor(model);
|
|
} else if (mode === 'inline') {
|
|
that._createInlineEditor(row, model);
|
|
} else if (mode === 'incell') {
|
|
$(row).children(DATA_CELL).each(function () {
|
|
var cell = $(this);
|
|
var column = leafColumns(that.columns)[that.cellIndex(cell)];
|
|
model = that._modelForContainer(cell);
|
|
if (model && (!model.editable || model.editable(column.field)) && column.field) {
|
|
that.editCell(cell);
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
container = that.editView ? that.editView.element : that._editContainer;
|
|
if (container) {
|
|
if (!this._editCancelClickHandler) {
|
|
this._editCancelClickHandler = proxy(this._editCancelClick, this);
|
|
}
|
|
container.on(CLICK + NS, 'a.k-grid-cancel', this._editCancelClickHandler);
|
|
if (!this._editUpdateClickHandler) {
|
|
this._editUpdateClickHandler = proxy(this._editUpdateClick, this);
|
|
}
|
|
container.on(CLICK + NS, 'a.k-grid-update', this._editUpdateClickHandler);
|
|
}
|
|
}
|
|
},
|
|
_editUpdateClick: function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.saveRow();
|
|
},
|
|
_editCancelClick: function (e) {
|
|
var that = this;
|
|
var navigatable = that.options.navigatable;
|
|
var model = that.editable.options.model;
|
|
var container = that.editView ? that.editView.element : that._editContainer;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (that.trigger('cancel', {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
return;
|
|
}
|
|
var currentIndex = that.items().index($(that.current()).parent());
|
|
that.cancelRow();
|
|
if (navigatable) {
|
|
that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
|
|
focusTable(that.table, true);
|
|
}
|
|
},
|
|
_createPopupEditor: function (model) {
|
|
var that = this, html = '<div ' + kendo.attr('uid') + '="' + model.uid + '" class="k-popup-edit-form' + (that._isMobile ? ' k-mobile-list' : '') + '"><div class="k-edit-form-container">', column, command, fields = [], idx, length, tmpl, updateText, cancelText, tempCommand, columns = leafColumns(that.columns), attr, editable = that.options.editable, template = editable.template, options = isPlainObject(editable) ? editable.window : {}, settings = extend({}, kendo.Template, that.options.templateSettings);
|
|
options = options || {};
|
|
if (template) {
|
|
if (typeof template === STRING) {
|
|
template = window.unescape(template);
|
|
}
|
|
html += kendo.template(template, settings)(model);
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.command) {
|
|
tempCommand = getCommand(column.command, 'edit');
|
|
if (tempCommand) {
|
|
command = tempCommand;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (!column.command) {
|
|
html += '<div class="k-edit-label"><label for="' + column.field + '">' + (column.title || column.field || '') + '</label></div>';
|
|
if ((!model.editable || model.editable(column.field)) && column.field) {
|
|
fields.push({
|
|
field: column.field,
|
|
format: column.format,
|
|
editor: column.editor,
|
|
values: column.values
|
|
});
|
|
html += '<div ' + kendo.attr('container-for') + '="' + column.field + '" class="k-edit-field"></div>';
|
|
} else {
|
|
var state = {
|
|
storage: {},
|
|
count: 0
|
|
};
|
|
tmpl = kendo.template(that._cellTmpl(column, state), settings);
|
|
if (state.count > 0) {
|
|
tmpl = proxy(tmpl, state.storage);
|
|
}
|
|
html += '<div class="k-edit-field">' + tmpl(model) + '</div>';
|
|
}
|
|
} else if (column.command) {
|
|
tempCommand = getCommand(column.command, 'edit');
|
|
if (tempCommand) {
|
|
command = tempCommand;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (command) {
|
|
if (isPlainObject(command)) {
|
|
if (command.text && isPlainObject(command.text)) {
|
|
updateText = command.text.update;
|
|
cancelText = command.text.cancel;
|
|
}
|
|
if (command.attr) {
|
|
attr = command.attr;
|
|
}
|
|
}
|
|
}
|
|
var container;
|
|
if (!that._isMobile) {
|
|
html += '<div class="k-edit-buttons k-state-default">';
|
|
html += that._createButton({
|
|
name: 'update',
|
|
text: updateText,
|
|
attr: attr
|
|
}) + that._createButton({
|
|
name: 'canceledit',
|
|
text: cancelText,
|
|
attr: attr
|
|
});
|
|
html += '</div></div></div>';
|
|
container = that._editContainer = $(html).appendTo(that.wrapper).eq(0).kendoWindow(extend({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: 'Edit',
|
|
visible: false,
|
|
close: function (e) {
|
|
if (e.userTriggered) {
|
|
e.sender.element.focus();
|
|
if (that.trigger('cancel', {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
var currentIndex = that.items().index($(that.current()).parent());
|
|
that.cancelRow();
|
|
if (that.options.navigatable) {
|
|
that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
|
|
focusTable(that.table, true);
|
|
}
|
|
}
|
|
}
|
|
}, options));
|
|
} else {
|
|
html += '</div></div>';
|
|
that.editView = that.pane.append('<div data-' + kendo.ns + 'role="view" data-' + kendo.ns + 'use-native-scrolling="true" data-' + kendo.ns + 'init-widgets="false" class="k-grid-edit-form">' + '<div data-' + kendo.ns + 'role="header" class="k-header">' + that._createButton({
|
|
name: 'update',
|
|
text: updateText,
|
|
attr: attr
|
|
}) + (options.title || 'Edit') + that._createButton({
|
|
name: 'canceledit',
|
|
text: cancelText,
|
|
attr: attr
|
|
}) + '</div>' + html + '</div>');
|
|
container = that._editContainer = that.editView.element.find('.k-popup-edit-form');
|
|
}
|
|
that.editable = that._editContainer.kendoEditable({
|
|
fields: fields,
|
|
model: model,
|
|
clearContainer: false,
|
|
target: that
|
|
}).data('kendoEditable');
|
|
if (that._isMobile) {
|
|
container.find('input[type=checkbox],input[type=radio]').parent('.k-edit-field').addClass('k-check').prev('.k-edit-label').addClass('k-check').click(function () {
|
|
$(this).next().children('input').click();
|
|
});
|
|
}
|
|
that._openPopUpEditor();
|
|
that.trigger(EDIT, {
|
|
container: container,
|
|
model: model
|
|
});
|
|
},
|
|
_openPopUpEditor: function () {
|
|
if (!this._isMobile) {
|
|
this._editContainer.data('kendoWindow').center().open();
|
|
} else {
|
|
this.pane.navigate(this.editView, this._editAnimation);
|
|
}
|
|
},
|
|
_createInlineEditor: function (row, model) {
|
|
var that = this, column, cell, command, fields = [];
|
|
if (that.lockedContent) {
|
|
row = row.add(that._relatedRow(row));
|
|
}
|
|
row.children(':not(.k-group-cell,.k-hierarchy-cell)').each(function () {
|
|
cell = $(this);
|
|
column = leafColumns(that.columns)[that.cellIndex(cell)];
|
|
if (!column.command && column.field && (!model.editable || model.editable(column.field))) {
|
|
fields.push({
|
|
field: column.field,
|
|
format: column.format,
|
|
editor: column.editor,
|
|
values: column.values
|
|
});
|
|
cell.attr(kendo.attr('container-for'), column.field);
|
|
cell.empty();
|
|
} else if (column.command) {
|
|
command = getCommand(column.command, 'edit');
|
|
if (command) {
|
|
cell.empty();
|
|
var updateText, cancelText, attr;
|
|
if (isPlainObject(command)) {
|
|
if (command.text && isPlainObject(command.text)) {
|
|
updateText = command.text.update;
|
|
cancelText = command.text.cancel;
|
|
}
|
|
if (command.attr) {
|
|
attr = command.attr;
|
|
}
|
|
}
|
|
$(that._createButton({
|
|
name: 'update',
|
|
text: updateText,
|
|
attr: attr
|
|
}) + that._createButton({
|
|
name: 'canceledit',
|
|
text: cancelText,
|
|
attr: attr
|
|
})).appendTo(cell);
|
|
}
|
|
}
|
|
});
|
|
that._editContainer = row;
|
|
that.editable = new kendo.ui.Editable(row.addClass('k-grid-edit-row'), {
|
|
target: that,
|
|
fields: fields,
|
|
model: model,
|
|
clearContainer: false
|
|
});
|
|
if (row.length > 1) {
|
|
adjustRowHeight(row[0], row[1]);
|
|
that._applyLockedContainersWidth();
|
|
}
|
|
that.trigger(EDIT, {
|
|
container: row,
|
|
model: model
|
|
});
|
|
},
|
|
cancelRow: function (notify) {
|
|
var that = this, container = that._editContainer, model;
|
|
if (container) {
|
|
model = that._modelForContainer(container);
|
|
if (notify && that.trigger('cancel', {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
return;
|
|
}
|
|
that._destroyEditable();
|
|
that.dataSource.cancelChanges(model);
|
|
if (that._editMode() !== 'popup') {
|
|
that._displayRow(container);
|
|
} else {
|
|
that._displayRow(that.tbody.find('[' + kendo.attr('uid') + '=' + model.uid + ']'));
|
|
}
|
|
}
|
|
},
|
|
saveRow: function () {
|
|
var that = this, container = that._editContainer, model = that._modelForContainer(container), editable = that.editable;
|
|
if (container && editable && editable.end() && !that.trigger(SAVE, {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
that.dataSource.sync();
|
|
}
|
|
},
|
|
_displayRow: function (row) {
|
|
var that = this, model = that._modelForContainer(row), related, newRow, nextRow, isSelected = row.hasClass('k-state-selected'), isAlt = row.hasClass('k-alt');
|
|
if (model) {
|
|
if (that.lockedContent) {
|
|
related = $((isAlt ? that.lockedAltRowTemplate : that.lockedRowTemplate)(model));
|
|
that._relatedRow(row.last()).replaceWith(related);
|
|
}
|
|
that.angular('cleanup', function () {
|
|
return { elements: row.get() };
|
|
});
|
|
newRow = $((isAlt ? that.altRowTemplate : that.rowTemplate)(model));
|
|
row.replaceWith(newRow);
|
|
that.trigger('itemChange', {
|
|
item: newRow,
|
|
data: model,
|
|
ns: ui
|
|
});
|
|
if (related && related.length) {
|
|
that.trigger('itemChange', {
|
|
item: related,
|
|
data: model,
|
|
ns: ui
|
|
});
|
|
}
|
|
var angularElements = newRow;
|
|
var angularData = [{ dataItem: model }];
|
|
if (related && related.length) {
|
|
angularElements = newRow.add(related);
|
|
angularData.push({ dataItem: model });
|
|
}
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: angularElements.get(),
|
|
data: angularData
|
|
};
|
|
});
|
|
if (isSelected && that.options.selectable) {
|
|
that.select(newRow.add(related));
|
|
}
|
|
if (related) {
|
|
adjustRowHeight(newRow[0], related[0]);
|
|
}
|
|
nextRow = newRow.next();
|
|
if (nextRow.hasClass('k-detail-row') && nextRow.is(':visible')) {
|
|
newRow.find('.k-hierarchy-cell .k-icon').removeClass('k-plus').addClass('k-minus');
|
|
}
|
|
}
|
|
},
|
|
_showMessage: function (messages, row) {
|
|
var that = this;
|
|
if (!that._isMobile) {
|
|
return window.confirm(messages.title);
|
|
}
|
|
var template = kendo.template('<ul>' + '<li class="km-actionsheet-title">#:title#</li>' + '<li><a href="\\#" class="k-button k-grid-delete">#:confirmDelete#</a></li>' + '</ul>');
|
|
var html = $(template(messages)).appendTo(that.view.element);
|
|
var actionSheet = that._actionSheet = new kendo.mobile.ui.ActionSheet(html, {
|
|
cancel: messages.cancelDelete,
|
|
cancelTemplate: '<li class="km-actionsheet-cancel"><a class="k-button" href="\\#">#:cancel#</a></li>',
|
|
close: function () {
|
|
this.destroy();
|
|
},
|
|
command: function (e) {
|
|
var item = $(e.currentTarget).parent();
|
|
if (!item.hasClass('km-actionsheet-cancel')) {
|
|
that._removeRow(row);
|
|
}
|
|
},
|
|
popup: that._actionSheetPopupOptions
|
|
});
|
|
actionSheet.open(row);
|
|
return false;
|
|
},
|
|
_confirmation: function (row) {
|
|
var that = this, editable = that.options.editable, confirmation = editable === true || typeof editable === STRING ? that.options.messages.editable.confirmation : editable.confirmation;
|
|
if (confirmation !== false && confirmation != null) {
|
|
if (typeof confirmation === FUNCTION) {
|
|
confirmation = confirmation(that._modelForContainer(row));
|
|
}
|
|
return that._showMessage({
|
|
confirmDelete: editable.confirmDelete || that.options.messages.editable.confirmDelete,
|
|
cancelDelete: editable.cancelDelete || that.options.messages.editable.cancelDelete,
|
|
title: confirmation === true ? that.options.messages.editable.confirmation : confirmation
|
|
}, row);
|
|
}
|
|
return true;
|
|
},
|
|
cancelChanges: function () {
|
|
this.dataSource.cancelChanges();
|
|
},
|
|
saveChanges: function () {
|
|
var that = this;
|
|
if ((that.editable && that.editable.end() || !that.editable) && !that.trigger(SAVECHANGES)) {
|
|
that.dataSource.sync();
|
|
}
|
|
},
|
|
addRow: function () {
|
|
var that = this, index, dataSource = that.dataSource, mode = that._editMode(), createAt = that.options.editable.createAt || '', pageSize = dataSource.pageSize(), view = dataSource.view() || [];
|
|
if (that.editable && that.editable.end() || !that.editable) {
|
|
if (mode != 'incell') {
|
|
that.cancelRow();
|
|
}
|
|
index = dataSource.indexOf(view[0]);
|
|
if (createAt.toLowerCase() == 'bottom') {
|
|
index += view.length;
|
|
if (pageSize && !dataSource.options.serverPaging && pageSize <= view.length) {
|
|
index -= 1;
|
|
}
|
|
}
|
|
if (index < 0) {
|
|
if (dataSource.page() > dataSource.totalPages()) {
|
|
index = (dataSource.page() - 1) * pageSize;
|
|
} else {
|
|
index = 0;
|
|
}
|
|
}
|
|
var model = dataSource.insert(index, {}), id = model.uid, table = that.lockedContent ? that.lockedTable : that.table, row = table.find('tr[' + kendo.attr('uid') + '=' + id + ']'), cell = row.children('td:not(.k-group-cell,.k-hierarchy-cell)').eq(that._firstEditableColumnIndex(row));
|
|
if (mode === 'inline' && row.length) {
|
|
that.editRow(row);
|
|
} else if (mode === 'popup') {
|
|
that.editRow(model);
|
|
} else if (cell.length) {
|
|
that.editCell(cell);
|
|
}
|
|
if (createAt.toLowerCase() == 'bottom' && that.lockedContent) {
|
|
that.lockedContent[0].scrollTop = that.content[0].scrollTop = that.table[0].offsetHeight;
|
|
}
|
|
}
|
|
},
|
|
_firstEditableColumnIndex: function (container) {
|
|
var that = this, column, columns = leafColumns(that.columns), idx, length, model = that._modelForContainer(container);
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field && column.hidden !== true) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
_toolbar: function () {
|
|
var that = this, wrapper = that.wrapper, toolbar = that.options.toolbar, editable = that.options.editable, container;
|
|
if (toolbar) {
|
|
container = that.wrapper.find('.k-grid-toolbar');
|
|
if (!container.length) {
|
|
if (!isFunction(toolbar)) {
|
|
toolbar = typeof toolbar === STRING ? toolbar : that._toolbarTmpl(toolbar).replace(templateHashRegExp, '\\#');
|
|
toolbar = proxy(kendo.template(toolbar), that);
|
|
}
|
|
container = $('<div class="k-header k-grid-toolbar" />').html(toolbar({})).prependTo(wrapper);
|
|
that.angular('compile', function () {
|
|
return { elements: container.get() };
|
|
});
|
|
}
|
|
if (editable && editable.create !== false) {
|
|
container.on(CLICK + NS, '.k-grid-add', function (e) {
|
|
e.preventDefault();
|
|
that.addRow();
|
|
}).on(CLICK + NS, '.k-grid-cancel-changes', function (e) {
|
|
e.preventDefault();
|
|
that.cancelChanges();
|
|
}).on(CLICK + NS, '.k-grid-save-changes', function (e) {
|
|
e.preventDefault();
|
|
that.saveChanges();
|
|
});
|
|
}
|
|
container.on(CLICK + NS, '.k-grid-excel', function (e) {
|
|
e.preventDefault();
|
|
that.saveAsExcel();
|
|
});
|
|
container.on(CLICK + NS, '.k-grid-pdf', function (e) {
|
|
e.preventDefault();
|
|
that.saveAsPDF();
|
|
});
|
|
}
|
|
},
|
|
_toolbarTmpl: function (commands) {
|
|
var that = this, idx, length, html = '';
|
|
if (isArray(commands)) {
|
|
for (idx = 0, length = commands.length; idx < length; idx++) {
|
|
html += that._createButton(commands[idx]);
|
|
}
|
|
}
|
|
return html;
|
|
},
|
|
_createButton: function (command) {
|
|
var template = command.template || COMMANDBUTTONTMPL, commandName = typeof command === STRING ? command : command.name || command.text, className = defaultCommands[commandName] ? defaultCommands[commandName].className : 'k-grid-' + (commandName || '').replace(/\s/g, ''), options = {
|
|
className: className,
|
|
text: commandName,
|
|
imageClass: '',
|
|
attr: '',
|
|
iconClass: ''
|
|
}, messages = this.options.messages.commands, attributeClassMatch;
|
|
if (!commandName && !(isPlainObject(command) && command.template)) {
|
|
throw new Error('Custom commands should have name specified');
|
|
}
|
|
if (isPlainObject(command)) {
|
|
command = extend(true, {}, command);
|
|
if (command.className && inArray(options.className, command.className.split(' ')) < 0) {
|
|
command.className += ' ' + options.className;
|
|
} else if (command.className === undefined) {
|
|
command.className = options.className;
|
|
}
|
|
if (commandName === 'edit' && isPlainObject(command.text)) {
|
|
command = extend(true, {}, command);
|
|
command.text = command.text.edit;
|
|
}
|
|
if (command.attr) {
|
|
if (isPlainObject(command.attr)) {
|
|
command.attr = stringifyAttributes(command.attr);
|
|
}
|
|
if (typeof command.attr === STRING) {
|
|
attributeClassMatch = command.attr.match(/class="(.+?)"/);
|
|
if (attributeClassMatch && inArray(attributeClassMatch[1], command.className.split(' ')) < 0) {
|
|
command.className += ' ' + attributeClassMatch[1];
|
|
}
|
|
}
|
|
}
|
|
options = extend(true, options, defaultCommands[commandName], { text: messages[commandName] }, command);
|
|
} else {
|
|
options = extend(true, options, defaultCommands[commandName], { text: messages[commandName] });
|
|
}
|
|
return kendo.template(template)(options);
|
|
},
|
|
_hasFooters: function () {
|
|
return !!this.footerTemplate || !!this.groupFooterTemplate || this.footer && this.footer.length > 0 || this.wrapper.find('.k-grid-footer').length > 0;
|
|
},
|
|
_groupable: function () {
|
|
var that = this;
|
|
if (that._groupableClickHandler) {
|
|
that.table.add(that.lockedTable).off(CLICK + NS, that._groupableClickHandler);
|
|
} else {
|
|
that._groupableClickHandler = function (e) {
|
|
var element = $(this), group = element.closest('tr');
|
|
if (element.hasClass('k-i-collapse')) {
|
|
that.collapseGroup(group);
|
|
} else {
|
|
that.expandGroup(group);
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
};
|
|
}
|
|
if (that._isLocked()) {
|
|
that.lockedTable.on(CLICK + NS, '.k-grouping-row .k-i-collapse, .k-grouping-row .k-i-expand', that._groupableClickHandler);
|
|
} else {
|
|
that.table.on(CLICK + NS, '.k-grouping-row .k-i-collapse, .k-grouping-row .k-i-expand', that._groupableClickHandler);
|
|
}
|
|
that._attachGroupable();
|
|
},
|
|
_attachGroupable: function () {
|
|
var that = this, wrapper = that.wrapper, groupable = that.options.groupable, draggables = HEADERCELLS + '[' + kendo.attr('field') + ']', filter = that.content ? '.k-grid-header:first ' + draggables : 'table:first>.k-grid-header ' + draggables;
|
|
if (groupable && groupable.enabled !== false) {
|
|
if (!wrapper.has('div.k-grouping-header')[0]) {
|
|
$('<div> </div>').addClass('k-grouping-header').prependTo(wrapper);
|
|
}
|
|
if (that.groupable) {
|
|
that.groupable.destroy();
|
|
}
|
|
that.groupable = new ui.Groupable(wrapper, extend({}, groupable, {
|
|
draggable: that._draggableInstance,
|
|
groupContainer: '>div.k-grouping-header',
|
|
dataSource: that.dataSource,
|
|
draggableElements: filter,
|
|
filter: filter,
|
|
allowDrag: that.options.reorderable
|
|
}));
|
|
}
|
|
},
|
|
_continuousItems: function (filter, cell) {
|
|
if (!this.lockedContent) {
|
|
return;
|
|
}
|
|
var that = this;
|
|
var elements = that.table.add(that.lockedTable);
|
|
var lockedItems = $(filter, elements[0]);
|
|
var nonLockedItems = $(filter, elements[1]);
|
|
var columns = cell ? lockedColumns(that.columns).length : 1;
|
|
var nonLockedColumns = cell ? that.columns.length - columns : 1;
|
|
var result = [];
|
|
for (var idx = 0; idx < lockedItems.length; idx += columns) {
|
|
push.apply(result, lockedItems.slice(idx, idx + columns));
|
|
push.apply(result, nonLockedItems.splice(0, nonLockedColumns));
|
|
}
|
|
return result;
|
|
},
|
|
_selectable: function () {
|
|
var that = this, multi, cell, notString = [], isLocked = that._isLocked(), selectable = that.options.selectable;
|
|
if (selectable) {
|
|
if (that.selectable) {
|
|
that.selectable.destroy();
|
|
}
|
|
selectable = kendo.ui.Selectable.parseOptions(selectable);
|
|
multi = selectable.multiple;
|
|
cell = selectable.cell;
|
|
if (that._hasDetails()) {
|
|
notString[notString.length] = '.k-detail-row';
|
|
}
|
|
if (that.options.groupable || that._hasFooters()) {
|
|
notString[notString.length] = '.k-grouping-row,.k-group-footer';
|
|
}
|
|
notString = notString.join(',');
|
|
if (notString !== '') {
|
|
notString = ':not(' + notString + ')';
|
|
}
|
|
var elements = that.table;
|
|
if (isLocked) {
|
|
elements = elements.add(that.lockedTable);
|
|
}
|
|
var filter = '>' + (cell ? SELECTION_CELL_SELECTOR : 'tbody>tr' + notString);
|
|
that.selectable = new kendo.ui.Selectable(elements, {
|
|
filter: filter,
|
|
aria: true,
|
|
multiple: multi,
|
|
change: function () {
|
|
that.trigger(CHANGE);
|
|
},
|
|
useAllItems: isLocked && multi && cell,
|
|
relatedTarget: function (items) {
|
|
if (cell || !isLocked) {
|
|
return;
|
|
}
|
|
var related;
|
|
var result = $();
|
|
for (var idx = 0, length = items.length; idx < length; idx++) {
|
|
related = that._relatedRow(items[idx]);
|
|
if (inArray(related[0], items) < 0) {
|
|
result = result.add(related);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
continuousItems: function () {
|
|
return that._continuousItems(filter, cell);
|
|
}
|
|
});
|
|
if (that.options.navigatable) {
|
|
elements.on('keydown' + NS, function (e) {
|
|
var current = that.current();
|
|
var target = e.target;
|
|
if (e.keyCode === keys.SPACEBAR && $.inArray(target, elements) > -1 && !current.is('.k-edit-cell,.k-header') && current.parent().is(':not(.k-grouping-row,.k-detail-row,.k-group-footer)')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
current = cell ? current : current.parent();
|
|
if (isLocked && !cell) {
|
|
current = current.add(that._relatedRow(current));
|
|
}
|
|
if (multi) {
|
|
if (!e.ctrlKey) {
|
|
that.selectable.clear();
|
|
} else {
|
|
if (current.hasClass(SELECTED)) {
|
|
current.removeClass(SELECTED);
|
|
that.trigger(CHANGE);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
that.selectable.clear();
|
|
}
|
|
that.selectable.value(current);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_clipboard: function () {
|
|
var options = this.options;
|
|
var selectable = options.selectable;
|
|
if (selectable && options.allowCopy) {
|
|
var grid = this;
|
|
if (!options.navigatable) {
|
|
grid.table.add(grid.lockedTable).attr('tabindex', 0).on('mousedown' + NS + ' keydown' + NS, '.k-detail-cell', function (e) {
|
|
if (e.target !== e.currentTarget) {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
}).on('mousedown' + NS, NAVROW + '>' + NAVCELL, proxy(tableClick, grid));
|
|
}
|
|
grid.copyHandler = proxy(grid.copySelection, grid);
|
|
grid.updateClipBoardState = function () {
|
|
if (grid.areaClipBoard) {
|
|
grid.areaClipBoard.val(grid.getTSV()).focus().select();
|
|
}
|
|
};
|
|
grid.bind('change', grid.updateClipBoardState);
|
|
grid.wrapper.on('keydown', grid.copyHandler);
|
|
grid.clearAreaHandler = proxy(grid.clearArea, grid);
|
|
grid.wrapper.on('keyup', grid.clearAreaHandler);
|
|
}
|
|
},
|
|
copySelection: function (e) {
|
|
if (e instanceof jQuery.Event && !(e.ctrlKey || e.metaKey) || $(e.target).is('input:visible,textarea:visible') || window.getSelection && window.getSelection().toString() || document.selection && document.selection.createRange().text) {
|
|
return;
|
|
}
|
|
if (!this.areaClipBoard) {
|
|
this.areaClipBoard = $('<textarea />').css({
|
|
position: 'fixed',
|
|
top: '50%',
|
|
left: '50%',
|
|
opacity: 0,
|
|
width: 0,
|
|
height: 0
|
|
}).appendTo(this.wrapper);
|
|
}
|
|
this.areaClipBoard.val(this.getTSV()).focus().select();
|
|
},
|
|
getTSV: function () {
|
|
var grid = this;
|
|
var selected = grid.select();
|
|
var delimeter = '\t';
|
|
var allowCopy = grid.options.allowCopy;
|
|
var onlyVisible = true;
|
|
if ($.isPlainObject(allowCopy) && allowCopy.delimeter) {
|
|
delimeter = allowCopy.delimeter;
|
|
}
|
|
var text = '';
|
|
if (selected.length) {
|
|
if (selected.eq(0).is('tr')) {
|
|
selected = selected.find('td:not(.k-group-cell)');
|
|
}
|
|
if (onlyVisible) {
|
|
selected.filter(':visible');
|
|
}
|
|
var result = [];
|
|
var cellsOffset = this.columns.length;
|
|
var lockedCols = grid._isLocked() && lockedColumns(grid.columns).length;
|
|
var inLockedArea = true;
|
|
$.each(selected, function (idx, cell) {
|
|
cell = $(cell);
|
|
var tr = cell.closest('tr');
|
|
var rowIndex = tr.index();
|
|
var cellIndex = cell.index();
|
|
if (onlyVisible) {
|
|
cellIndex -= cell.prevAll(':hidden').length;
|
|
}
|
|
if (lockedCols && inLockedArea) {
|
|
inLockedArea = $.contains(grid.lockedTable[0], cell[0]);
|
|
}
|
|
if (grid._groups() && inLockedArea) {
|
|
cellIndex -= grid._groups();
|
|
}
|
|
cellIndex = inLockedArea ? cellIndex : cellIndex + lockedCols;
|
|
if (cellsOffset > cellIndex) {
|
|
cellsOffset = cellIndex;
|
|
}
|
|
var cellText = cell.text();
|
|
if (!result[rowIndex]) {
|
|
result[rowIndex] = [];
|
|
}
|
|
result[rowIndex][cellIndex] = cellText;
|
|
});
|
|
var rowsOffset = result.length;
|
|
result = $.each(result, function (idx, val) {
|
|
if (val) {
|
|
result[idx] = val.slice(cellsOffset);
|
|
if (rowsOffset > idx) {
|
|
rowsOffset = idx;
|
|
}
|
|
}
|
|
});
|
|
$.each(result.slice(rowsOffset), function (idx, val) {
|
|
if (val) {
|
|
text += val.join(delimeter) + '\r\n';
|
|
} else {
|
|
text += '\r\n';
|
|
}
|
|
});
|
|
}
|
|
return text;
|
|
},
|
|
clearArea: function (e) {
|
|
var table;
|
|
if (this.areaClipBoard && e && e.target === this.areaClipBoard[0]) {
|
|
if (this.options.navigatable) {
|
|
table = $(this.current()).closest('table');
|
|
} else {
|
|
table = this.table;
|
|
}
|
|
focusTable(table, true);
|
|
}
|
|
if (this.areaClipBoard) {
|
|
this.areaClipBoard.remove();
|
|
this.areaClipBoard = null;
|
|
}
|
|
},
|
|
_minScreenSupport: function () {
|
|
var any = this.hideMinScreenCols();
|
|
if (any) {
|
|
this.minScreenResizeHandler = proxy(this.hideMinScreenCols, this);
|
|
$(window).on('resize', this.minScreenResizeHandler);
|
|
}
|
|
},
|
|
hideMinScreenCols: function () {
|
|
var cols = this.columns, screenWidth = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
|
return this._iterateMinScreenCols(cols, screenWidth);
|
|
},
|
|
_iterateMinScreenCols: function (cols, screenWidth) {
|
|
var any = false;
|
|
for (var i = 0; i < cols.length; i++) {
|
|
var col = cols[i];
|
|
var minWidth = col.minScreenWidth;
|
|
if (minWidth !== undefined && minWidth !== null) {
|
|
any = true;
|
|
if (minWidth > screenWidth) {
|
|
this.hideColumn(col);
|
|
} else {
|
|
this.showColumn(col);
|
|
}
|
|
}
|
|
if (!col.hidden && col.columns) {
|
|
any = this._iterateMinScreenCols(col.columns, screenWidth) || any;
|
|
}
|
|
}
|
|
return any;
|
|
},
|
|
_relatedRow: function (row) {
|
|
var lockedTable = this.lockedTable;
|
|
row = $(row);
|
|
if (!lockedTable) {
|
|
return row;
|
|
}
|
|
var table = row.closest(this.table.add(this.lockedTable));
|
|
var index = table.find('>tbody>tr').index(row);
|
|
table = table[0] === this.table[0] ? lockedTable : this.table;
|
|
return table.find('>tbody>tr').eq(index);
|
|
},
|
|
clearSelection: function () {
|
|
var that = this;
|
|
that.selectable.clear();
|
|
that.trigger(CHANGE);
|
|
},
|
|
select: function (items) {
|
|
var that = this, selectable = that.selectable;
|
|
items = that.table.add(that.lockedTable).find(items);
|
|
if (items.length) {
|
|
if (!selectable.options.multiple) {
|
|
selectable.clear();
|
|
items = items.first();
|
|
}
|
|
if (that._isLocked()) {
|
|
items = items.add(items.map(function () {
|
|
return that._relatedRow(this);
|
|
}));
|
|
}
|
|
selectable.value(items);
|
|
return;
|
|
}
|
|
return selectable.value();
|
|
},
|
|
_updateCurrentAttr: function (current, next) {
|
|
var headerId = $(current).data('headerId');
|
|
$(current).removeClass(FOCUSED).removeAttr('aria-describedby').closest('table').removeAttr('aria-activedescendant');
|
|
if (headerId) {
|
|
headerId = headerId.replace(this._cellId, '');
|
|
$(current).attr('id', headerId);
|
|
} else {
|
|
$(current).removeAttr('id');
|
|
}
|
|
next.data('headerId', next.attr('id')).attr('id', this._cellId).addClass(FOCUSED).closest('table').attr('aria-activedescendant', this._cellId);
|
|
if (!next.closest('tr').hasClass('k-grouping-row') && !next.hasClass('k-header')) {
|
|
var column = this.columns[this.cellIndex(next)];
|
|
if (column) {
|
|
headerId = column.headerAttributes.id;
|
|
}
|
|
next.attr('aria-describedby', headerId + ' ' + this._cellId);
|
|
} else {
|
|
next.attr('aria-describedby', this._cellId);
|
|
}
|
|
this._current = next;
|
|
},
|
|
_scrollCurrent: function () {
|
|
var current = this._current;
|
|
var scrollable = this.options.scrollable;
|
|
if (!current || !scrollable) {
|
|
return;
|
|
}
|
|
var row = current.parent();
|
|
var tableContainer = row.closest('table').parent();
|
|
var isInLockedContainer = tableContainer.is('.k-grid-content-locked,.k-grid-header-locked');
|
|
var isInContent = tableContainer.is('.k-grid-content-locked,.k-grid-content,.k-virtual-scrollable-wrap');
|
|
var scrollableContainer = $(this.content).find('>.k-virtual-scrollable-wrap').andSelf().last()[0];
|
|
if (isInContent) {
|
|
if (scrollable.virtual) {
|
|
var rowIndex = Math.max(inArray(row[0], this._items(row.parent())), 0);
|
|
this._rowVirtualIndex = this.virtualScrollable.itemIndex(rowIndex);
|
|
this.virtualScrollable.scrollIntoView(row);
|
|
} else {
|
|
this._scrollTo(this._relatedRow(row)[0], scrollableContainer);
|
|
}
|
|
}
|
|
if (this.lockedContent) {
|
|
this.lockedContent[0].scrollTop = scrollableContainer.scrollTop;
|
|
}
|
|
if (!isInLockedContainer) {
|
|
this._scrollTo(current[0], scrollableContainer);
|
|
}
|
|
},
|
|
current: function (next) {
|
|
return this._setCurrent(next, true);
|
|
},
|
|
_setCurrent: function (next, preventTrigger) {
|
|
var current = this._current;
|
|
next = $(next);
|
|
if (next.length) {
|
|
if (!current || current[0] !== next[0]) {
|
|
this._updateCurrentAttr(current, next);
|
|
this._scrollCurrent();
|
|
if (!preventTrigger) {
|
|
this.trigger(NAVIGATE, { element: next });
|
|
}
|
|
}
|
|
}
|
|
return this._current;
|
|
},
|
|
_removeCurrent: function () {
|
|
if (this._current) {
|
|
this._current.removeClass(FOCUSED);
|
|
this._current = null;
|
|
}
|
|
},
|
|
_scrollTo: function (element, container) {
|
|
var elementToLowercase = element.tagName.toLowerCase();
|
|
var isHorizontal = elementToLowercase === 'td' || elementToLowercase === 'th';
|
|
var elementOffset = element[isHorizontal ? 'offsetLeft' : 'offsetTop'];
|
|
var elementOffsetDir = element[isHorizontal ? 'offsetWidth' : 'offsetHeight'];
|
|
var containerScroll = container[isHorizontal ? 'scrollLeft' : 'scrollTop'];
|
|
var containerOffsetDir = container[isHorizontal ? 'clientWidth' : 'clientHeight'];
|
|
var bottomDistance = elementOffset + elementOffsetDir;
|
|
var result = 0;
|
|
var ieCorrection = 0;
|
|
var firefoxCorrection = 0;
|
|
if (isRtl && isHorizontal) {
|
|
var table = $(element).closest('table')[0];
|
|
if (browser.msie) {
|
|
ieCorrection = table.offsetLeft;
|
|
} else if (browser.mozilla) {
|
|
firefoxCorrection = table.offsetLeft - kendo.support.scrollbar();
|
|
}
|
|
}
|
|
containerScroll = Math.abs(containerScroll + ieCorrection - firefoxCorrection);
|
|
if (containerScroll > elementOffset) {
|
|
result = elementOffset;
|
|
} else if (bottomDistance > containerScroll + containerOffsetDir) {
|
|
if (elementOffsetDir <= containerOffsetDir) {
|
|
result = bottomDistance - containerOffsetDir;
|
|
} else {
|
|
result = elementOffset;
|
|
}
|
|
} else {
|
|
result = containerScroll;
|
|
}
|
|
result = Math.abs(result + ieCorrection) + firefoxCorrection;
|
|
container[isHorizontal ? 'scrollLeft' : 'scrollTop'] = result;
|
|
},
|
|
_navigatable: function () {
|
|
var that = this;
|
|
if (!that.options.navigatable) {
|
|
return;
|
|
}
|
|
var dataTables = that.table.add(that.lockedTable);
|
|
var headerTables = that.thead.parent().add($('>table', that.lockedHeader));
|
|
var tables = dataTables;
|
|
if (that.options.scrollable) {
|
|
tables = tables.add(headerTables);
|
|
headerTables.attr(TABINDEX, -1);
|
|
}
|
|
this._navigatableTables = tables;
|
|
tables.off('mousedown' + NS + ' focus' + NS + ' focusout' + NS + ' keydown' + NS);
|
|
headerTables.on('keydown' + NS, proxy(that._openHeaderMenu, that)).find('a.k-link').attr('tabIndex', -1);
|
|
dataTables.attr(TABINDEX, math.max(dataTables.attr(TABINDEX) || 0, 0)).on('mousedown' + NS + ' keydown' + NS, '.k-detail-cell', function (e) {
|
|
if (e.target !== e.currentTarget) {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
});
|
|
tables.on(kendo.support.touch ? 'touchstart' + NS : 'mousedown' + NS, NAVROW + '>' + NAVCELL, proxy(tableClick, that)).on('focus' + NS, proxy(that._tableFocus, that)).on('focusout' + NS, proxy(that._tableBlur, that)).on('keydown' + NS, proxy(that._tableKeyDown, that));
|
|
},
|
|
_openHeaderMenu: function (e) {
|
|
if (e.altKey && e.keyCode == keys.DOWN) {
|
|
this.current().find('.k-grid-filter, .k-header-column-menu').click();
|
|
e.stopImmediatePropagation();
|
|
}
|
|
},
|
|
_setTabIndex: function (table) {
|
|
this._navigatableTables.attr(TABINDEX, -1);
|
|
table.attr(TABINDEX, 0);
|
|
},
|
|
_tableFocus: function (e) {
|
|
if (kendo.support.touch) {
|
|
return;
|
|
}
|
|
var current = this.current();
|
|
var table = $(e.currentTarget);
|
|
if (current && current.is(':visible')) {
|
|
current.addClass(FOCUSED);
|
|
} else {
|
|
this._setCurrent(table.find(FIRSTNAVITEM));
|
|
}
|
|
this._setTabIndex(table);
|
|
},
|
|
_tableBlur: function () {
|
|
var current = this.current();
|
|
if (current) {
|
|
current.removeClass(FOCUSED);
|
|
}
|
|
},
|
|
_tableKeyDown: function (e) {
|
|
var current = this.current();
|
|
var requestInProgress = this.virtualScrollable && this.virtualScrollable.fetching();
|
|
var target = $(e.target);
|
|
var canHandle = !e.isDefaultPrevented() && !target.is(':button,a,:input,a>.k-icon');
|
|
if (requestInProgress) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
current = current ? current : $(this.lockedTable).add(this.table).find(FIRSTNAVITEM);
|
|
if (!current.length) {
|
|
return;
|
|
}
|
|
var handled = false;
|
|
if (canHandle && e.keyCode == keys.UP) {
|
|
handled = this._moveUp(current);
|
|
}
|
|
if (canHandle && e.keyCode == keys.DOWN) {
|
|
handled = this._moveDown(current);
|
|
}
|
|
if (canHandle && e.keyCode == (isRtl ? keys.LEFT : keys.RIGHT)) {
|
|
handled = this._moveRight(current, e.altKey);
|
|
}
|
|
if (canHandle && e.keyCode == (isRtl ? keys.RIGHT : keys.LEFT)) {
|
|
handled = this._moveLeft(current, e.altKey);
|
|
}
|
|
if (canHandle && e.keyCode == keys.PAGEDOWN) {
|
|
handled = this._handlePageDown();
|
|
}
|
|
if (canHandle && e.keyCode == keys.PAGEUP) {
|
|
handled = this._handlePageUp();
|
|
}
|
|
if (e.keyCode == keys.ENTER || e.keyCode == keys.F2) {
|
|
handled = this._handleEnterKey(current, e.currentTarget, target);
|
|
}
|
|
if (e.keyCode == keys.ESC) {
|
|
handled = this._handleEscKey(current, e.currentTarget);
|
|
}
|
|
if (e.keyCode == keys.TAB) {
|
|
handled = this._handleTabKey(current, e.currentTarget, e.shiftKey);
|
|
}
|
|
if (handled) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
_moveLeft: function (current, altKey) {
|
|
var next, index;
|
|
var row = current.parent();
|
|
var container = row.parent();
|
|
if (altKey) {
|
|
this.collapseRow(row);
|
|
} else {
|
|
index = container.find(NAVROW).index(row);
|
|
next = this._prevHorizontalCell(container, current, index);
|
|
if (!next[0]) {
|
|
container = this._horizontalContainer(container);
|
|
next = this._prevHorizontalCell(container, current, index);
|
|
if (next[0] !== current[0]) {
|
|
focusTable(container.parent(), true);
|
|
}
|
|
}
|
|
this._setCurrent(next);
|
|
}
|
|
return true;
|
|
},
|
|
_moveRight: function (current, altKey) {
|
|
var next, index;
|
|
var row = current.parent();
|
|
var container = row.parent();
|
|
if (altKey) {
|
|
this.expandRow(row);
|
|
} else {
|
|
index = container.find(NAVROW).index(row);
|
|
next = this._nextHorizontalCell(container, current, index);
|
|
if (!next[0]) {
|
|
container = this._horizontalContainer(container, true);
|
|
next = this._nextHorizontalCell(container, current, index);
|
|
if (next[0] !== current[0]) {
|
|
focusTable(container.parent(), true);
|
|
}
|
|
}
|
|
this._setCurrent(next);
|
|
}
|
|
return true;
|
|
},
|
|
_moveUp: function (current) {
|
|
var container = current.parent().parent();
|
|
var next = this._prevVerticalCell(container, current);
|
|
if (!next[0]) {
|
|
container = this._verticalContainer(container, true);
|
|
next = this._prevVerticalCell(container, current);
|
|
if (next[0]) {
|
|
focusTable(container.parent(), true);
|
|
}
|
|
}
|
|
this._setCurrent(next);
|
|
return true;
|
|
},
|
|
_moveDown: function (current) {
|
|
var container = current.parent().parent();
|
|
var next = this._nextVerticalCell(container, current);
|
|
if (!next[0]) {
|
|
container = this._verticalContainer(container);
|
|
next = this._nextVerticalCell(container, current);
|
|
if (next[0]) {
|
|
focusTable(container.parent(), true);
|
|
}
|
|
}
|
|
this._setCurrent(next);
|
|
return true;
|
|
},
|
|
_handlePageDown: function () {
|
|
if (!this.options.pageable) {
|
|
return false;
|
|
}
|
|
this.dataSource.page(this.dataSource.page() + 1);
|
|
return true;
|
|
},
|
|
_handlePageUp: function () {
|
|
if (!this.options.pageable) {
|
|
return false;
|
|
}
|
|
this.dataSource.page(this.dataSource.page() - 1);
|
|
return true;
|
|
},
|
|
_handleTabKey: function (current, currentTable, shiftKey) {
|
|
var isInCell = this.options.editable && this._editMode() == 'incell';
|
|
var cell;
|
|
if (!isInCell || current.is('th')) {
|
|
return false;
|
|
}
|
|
cell = $(activeElement()).closest('.k-edit-cell');
|
|
if (cell[0] && cell[0] !== current[0]) {
|
|
current = cell;
|
|
}
|
|
cell = this._tabNext(current, currentTable, shiftKey);
|
|
if (cell.length) {
|
|
this._handleEditing(current, cell, cell.closest('table'));
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_handleEscKey: function (current, currentTable) {
|
|
var active = activeElement();
|
|
var isInCell = this._editMode() == 'incell';
|
|
if (!isInEdit(current)) {
|
|
if (current.has(active).length) {
|
|
focusTable(currentTable, true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (isInCell) {
|
|
this.closeCell(true);
|
|
} else {
|
|
var currentIndex = $(current).parent().index();
|
|
if (active) {
|
|
active.blur();
|
|
}
|
|
this.cancelRow(true);
|
|
if (currentIndex >= 0) {
|
|
this._setCurrent(this.items().eq(currentIndex).children(NAVCELL).first());
|
|
}
|
|
}
|
|
if (browser.msie && browser.version < 9) {
|
|
document.body.focus();
|
|
}
|
|
focusTable(currentTable, true);
|
|
return true;
|
|
},
|
|
_toggleCurrent: function (current, editable) {
|
|
var row = current.parent();
|
|
if (row.is('.k-grouping-row')) {
|
|
row.find('.k-icon:first').click();
|
|
return true;
|
|
}
|
|
if (!editable && row.is('.k-master-row')) {
|
|
row.find('.k-icon:first').click();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_handleEnterKey: function (current, currentTable, target) {
|
|
var editable = this.options.editable;
|
|
var container = target.closest('[role=gridcell]');
|
|
if (!target.is('table') && !$.contains(current[0], target[0])) {
|
|
current = container;
|
|
}
|
|
if (current.is('th')) {
|
|
current.find('.k-link').click();
|
|
return true;
|
|
}
|
|
if (this._toggleCurrent(current, editable)) {
|
|
return true;
|
|
}
|
|
var focusable = current.find(':kendoFocusable:first');
|
|
if (focusable[0] && !current.hasClass('k-edit-cell') && current.hasClass('k-state-focused')) {
|
|
focusable.focus();
|
|
return true;
|
|
}
|
|
if (editable && !target.is(':button,.k-button,textarea')) {
|
|
if (!container[0]) {
|
|
container = current;
|
|
}
|
|
this._handleEditing(container, false, currentTable);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_nextHorizontalCell: function (table, current, originalIndex) {
|
|
var cells = current.nextAll(DATA_CELL);
|
|
if (!cells.length) {
|
|
var rows = table.find(NAVROW);
|
|
var rowIndex = rows.index(current.parent());
|
|
if (rowIndex == -1) {
|
|
if (current.hasClass('k-header')) {
|
|
var headerRows = [];
|
|
mapColumnToCellRows([lockedColumns(this.columns)[0]], childColumnsCells(rows.eq(0).children().first()), headerRows, 0, 0);
|
|
if (headerRows[originalIndex]) {
|
|
return headerRows[originalIndex][0];
|
|
}
|
|
return current;
|
|
}
|
|
if (current.parent().hasClass('k-filter-row')) {
|
|
return rows.last().children(DATA_CELL).first();
|
|
}
|
|
return rows.eq(originalIndex).children(DATA_CELL).first();
|
|
}
|
|
}
|
|
return cells.first();
|
|
},
|
|
_prevHorizontalCell: function (table, current, originalIndex) {
|
|
var cells = current.prevAll(DATA_CELL);
|
|
if (!cells.length) {
|
|
var rows = table.find(NAVROW);
|
|
var rowIndex = rows.index(current.parent());
|
|
if (rowIndex == -1) {
|
|
if (current.hasClass('k-header')) {
|
|
var headerRows = [];
|
|
var columns = lockedColumns(this.columns);
|
|
mapColumnToCellRows([columns[columns.length - 1]], childColumnsCells(rows.eq(0).children().last()), headerRows, 0, 0);
|
|
if (headerRows[originalIndex]) {
|
|
return headerRows[originalIndex][0];
|
|
}
|
|
return current;
|
|
}
|
|
if (current.parent().hasClass('k-filter-row')) {
|
|
return rows.last().children(DATA_CELL).last();
|
|
}
|
|
return rows.eq(originalIndex).children(DATA_CELL).last();
|
|
}
|
|
}
|
|
return cells.first();
|
|
},
|
|
_currentDataIndex: function (table, current) {
|
|
var index = current.attr('data-index');
|
|
if (!index) {
|
|
return undefined;
|
|
}
|
|
var lockedColumnsCount = lockedColumns(this.columns).length;
|
|
if (lockedColumnsCount && !table.closest('div').hasClass('k-grid-content-locked')[0]) {
|
|
return index - lockedColumnsCount;
|
|
}
|
|
return index;
|
|
},
|
|
_prevVerticalCell: function (container, current) {
|
|
var cells;
|
|
var row = current.parent();
|
|
var rows = container.children(NAVROW);
|
|
var rowIndex = rows.index(row);
|
|
var index = this._currentDataIndex(container, current);
|
|
if (index || current.hasClass('k-header')) {
|
|
cells = parentColumnsCells(current);
|
|
return cells.eq(cells.length - 2);
|
|
}
|
|
index = row.children(DATA_CELL).index(current);
|
|
if (row.hasClass('k-filter-row')) {
|
|
return leafDataCells(container).eq(index);
|
|
}
|
|
if (rowIndex == -1) {
|
|
row = container.find('.k-filter-row');
|
|
if (!row[0]) {
|
|
return leafDataCells(container).eq(index);
|
|
}
|
|
} else {
|
|
row = rowIndex === 0 ? $() : rows.eq(rowIndex - 1);
|
|
}
|
|
cells = row.children(DATA_CELL);
|
|
if (cells.length > index) {
|
|
return cells.eq(index);
|
|
}
|
|
return cells.eq(0);
|
|
},
|
|
_nextVerticalCell: function (container, current) {
|
|
var cells;
|
|
var row = current.parent();
|
|
var rows = container.children(NAVROW);
|
|
var rowIndex = rows.index(row);
|
|
var index = this._currentDataIndex(container, current);
|
|
if (rowIndex != -1 && index === undefined && current.hasClass('k-header')) {
|
|
return childColumnsCells(current).eq(1);
|
|
}
|
|
index = index ? parseInt(index, 10) : row.children(DATA_CELL).index(current);
|
|
if (rowIndex == -1) {
|
|
row = rows.eq(0);
|
|
} else {
|
|
row = rows.eq(rowIndex + current[0].rowSpan);
|
|
}
|
|
cells = row.children(DATA_CELL);
|
|
if (cells.length > index) {
|
|
return cells.eq(index);
|
|
}
|
|
return cells.eq(0);
|
|
},
|
|
_verticalContainer: function (container, up) {
|
|
var table = container.parent();
|
|
var length = this._navigatableTables.length;
|
|
var step = Math.floor(length / 2);
|
|
var index = inArray(table[0], this._navigatableTables);
|
|
if (up) {
|
|
step *= -1;
|
|
}
|
|
index += step;
|
|
if (index >= 0 || index < length) {
|
|
table = this._navigatableTables.eq(index);
|
|
}
|
|
return table.find(up ? 'thead' : 'tbody');
|
|
},
|
|
_horizontalContainer: function (container, right) {
|
|
var length = this._navigatableTables.length;
|
|
if (length <= 2) {
|
|
return container;
|
|
}
|
|
var table = container.parent();
|
|
var index = inArray(table[0], this._navigatableTables);
|
|
index += right ? 1 : -1;
|
|
if (right && (index == 2 || index == length)) {
|
|
return container;
|
|
}
|
|
if (!right && (index == 1 || index < 0)) {
|
|
return container;
|
|
}
|
|
return this._navigatableTables.eq(index).find('thead, tbody');
|
|
},
|
|
_tabNext: function (current, currentTable, back) {
|
|
var switchRow = true;
|
|
var next = back ? current.prevAll(DATA_CELL + ':first') : current.nextAll(':visible:first');
|
|
if (!next.length) {
|
|
next = current.parent();
|
|
if (this.lockedTable) {
|
|
switchRow = back && currentTable == this.lockedTable[0] || !back && currentTable == this.table[0];
|
|
next = this._relatedRow(next);
|
|
}
|
|
if (switchRow) {
|
|
next = next[back ? 'prevAll' : 'nextAll']('tr:not(.k-grouping-row):not(.k-detail-row):visible:first');
|
|
}
|
|
next = next.children(DATA_CELL + (back ? ':last' : ':first'));
|
|
}
|
|
return next;
|
|
},
|
|
_handleEditing: function (current, next, table) {
|
|
var that = this, active = $(activeElement()), mode = that._editMode(), isIE = browser.msie, oldIE = isIE && browser.version < 9, editContainer = that._editContainer, focusable, isEdited;
|
|
table = $(table);
|
|
if (mode == 'incell') {
|
|
isEdited = current.hasClass('k-edit-cell');
|
|
} else {
|
|
isEdited = current.parent().hasClass('k-grid-edit-row');
|
|
}
|
|
if (that.editable) {
|
|
if ($.contains(editContainer[0], active[0])) {
|
|
if (browser.opera || oldIE) {
|
|
active.blur().change().triggerHandler('blur');
|
|
} else {
|
|
active.blur();
|
|
if (isIE) {
|
|
active.blur();
|
|
}
|
|
}
|
|
}
|
|
if (!that.editable) {
|
|
focusTable(table);
|
|
return;
|
|
}
|
|
if (that.editable.end()) {
|
|
if (mode == 'incell') {
|
|
that.closeCell();
|
|
} else {
|
|
that.saveRow();
|
|
isEdited = true;
|
|
}
|
|
} else {
|
|
if (mode == 'incell') {
|
|
that._setCurrent(editContainer);
|
|
} else {
|
|
that._setCurrent(editContainer.children().filter(DATA_CELL).first());
|
|
}
|
|
focusable = editContainer.find(':kendoFocusable:first')[0];
|
|
if (focusable) {
|
|
focusable.focus();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (next) {
|
|
that._setCurrent(next);
|
|
}
|
|
if (oldIE) {
|
|
document.body.focus();
|
|
}
|
|
focusTable(table, true);
|
|
if (!isEdited && !next || next) {
|
|
if (mode == 'incell') {
|
|
that.editCell(that.current());
|
|
} else {
|
|
that.editRow(that.current().parent());
|
|
}
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, table = that.table, height = that.options.height, wrapper = that.element;
|
|
if (!wrapper.is('div')) {
|
|
wrapper = wrapper.wrap('<div/>').parent();
|
|
}
|
|
that.wrapper = wrapper.addClass('k-grid k-widget');
|
|
if (height) {
|
|
that.wrapper.css(HEIGHT, height);
|
|
table.css(HEIGHT, 'auto');
|
|
}
|
|
that._initMobile();
|
|
},
|
|
_initMobile: function () {
|
|
var options = this.options;
|
|
var that = this;
|
|
this._isMobile = options.mobile === true && kendo.support.mobileOS || options.mobile === 'phone' || options.mobile === 'tablet';
|
|
if (this._isMobile) {
|
|
var html = this.wrapper.addClass('k-grid-mobile').wrap('<div data-' + kendo.ns + 'stretch="true" data-' + kendo.ns + 'role="view" ' + 'data-' + kendo.ns + 'init-widgets="false"></div>').parent();
|
|
this.pane = kendo.mobile.ui.Pane.wrap(html);
|
|
this.view = this.pane.view();
|
|
this._actionSheetPopupOptions = $(document.documentElement).hasClass('km-root') ? { modal: false } : {
|
|
align: 'bottom center',
|
|
position: 'bottom center',
|
|
effect: 'slideIn:up'
|
|
};
|
|
if (options.height) {
|
|
this.pane.element.parent().css(HEIGHT, options.height);
|
|
}
|
|
this._editAnimation = 'slide';
|
|
this.view.bind('show', function () {
|
|
if (that._isLocked()) {
|
|
that._updateTablesWidth();
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
that._syncLockedFooterHeight();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_tbody: function () {
|
|
var that = this, table = that.table, tbody;
|
|
tbody = table.find('>tbody');
|
|
if (!tbody.length) {
|
|
tbody = $('<tbody/>').appendTo(table);
|
|
}
|
|
that.tbody = tbody.attr('role', 'rowgroup');
|
|
},
|
|
_scrollable: function () {
|
|
var that = this, header, table, options = that.options, scrollable = options.scrollable, hasVirtualScroll = scrollable !== true && scrollable.virtual && !that.virtualScrollable, scrollbar = !kendo.support.kineticScrollNeeded || hasVirtualScroll ? kendo.support.scrollbar() : 0;
|
|
if (scrollable) {
|
|
header = that.wrapper.children('.k-grid-header');
|
|
if (!header[0]) {
|
|
header = $('<div class="k-grid-header" />').insertBefore(that.table);
|
|
}
|
|
header.css(isRtl ? 'padding-left' : 'padding-right', scrollable.virtual ? scrollbar + 1 : scrollbar);
|
|
table = $('<table role="grid" />');
|
|
if (isIE7) {
|
|
table.attr('cellspacing', 0);
|
|
}
|
|
table.width(that.table[0].style.width);
|
|
table.append(that.thead);
|
|
header.empty().append($('<div class="k-grid-header-wrap k-auto-scrollable" />').append(table));
|
|
that.content = that.table.parent();
|
|
if (that.content.is('.k-virtual-scrollable-wrap, .km-scroll-container')) {
|
|
that.content = that.content.parent();
|
|
}
|
|
if (!that.content.is('.k-grid-content, .k-virtual-scrollable-wrap')) {
|
|
that.content = that.table.wrap('<div class="k-grid-content k-auto-scrollable" />').parent();
|
|
}
|
|
if (hasVirtualScroll) {
|
|
that.virtualScrollable = new VirtualScrollable(that.content, {
|
|
dataSource: that.dataSource,
|
|
itemHeight: function () {
|
|
return that._averageRowHeight();
|
|
}
|
|
});
|
|
}
|
|
that.scrollables = header.children('.k-grid-header-wrap').add(that.content);
|
|
var footer = that.wrapper.find('.k-grid-footer');
|
|
if (footer.length) {
|
|
that.scrollables = that.scrollables.add(footer.children('.k-grid-footer-wrap'));
|
|
}
|
|
if (scrollable.virtual) {
|
|
that.content.find('>.k-virtual-scrollable-wrap').unbind('scroll' + NS).bind('scroll' + NS, function () {
|
|
that.scrollables.scrollLeft(this.scrollLeft);
|
|
if (that.lockedContent) {
|
|
that.lockedContent[0].scrollTop = this.scrollTop;
|
|
}
|
|
});
|
|
} else {
|
|
that.scrollables.unbind('scroll' + NS).bind('scroll' + NS, function (e) {
|
|
that.scrollables.not(e.currentTarget).scrollLeft(this.scrollLeft);
|
|
if (that.lockedContent && e.currentTarget == that.content[0]) {
|
|
that.lockedContent[0].scrollTop = this.scrollTop;
|
|
}
|
|
});
|
|
var touchScroller = that.content.data('kendoTouchScroller');
|
|
if (touchScroller) {
|
|
touchScroller.destroy();
|
|
}
|
|
touchScroller = kendo.touchScroller(that.content);
|
|
if (touchScroller && touchScroller.movable) {
|
|
that.touchScroller = touchScroller;
|
|
touchScroller.movable.bind('change', function (e) {
|
|
that.scrollables.scrollLeft(-e.sender.x);
|
|
if (that.lockedContent) {
|
|
that.lockedContent.scrollTop(-e.sender.y);
|
|
}
|
|
});
|
|
that.one(DATABOUND, function (e) {
|
|
e.sender.wrapper.addClass('k-grid-backface');
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_renderNoRecordsContent: function () {
|
|
var that = this;
|
|
if (that.options.noRecords) {
|
|
var noRecordsElement = that.table.parent().children('.' + NORECORDSCLASS);
|
|
if (noRecordsElement.length) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: noRecordsElement.get() };
|
|
});
|
|
noRecordsElement.remove();
|
|
}
|
|
if (!that.dataSource || !that.dataSource.view().length) {
|
|
noRecordsElement = $(that.noRecordsTemplate({})).insertAfter(that.table);
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: noRecordsElement.get(),
|
|
data: [{}]
|
|
};
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_setContentWidth: function (scrollLeft) {
|
|
var that = this, hiddenDivClass = 'k-grid-content-expander', hiddenDiv = '<div class="' + hiddenDivClass + '"></div>', resizable = that.resizable, expander;
|
|
if (that.options.scrollable && that.wrapper.is(':visible')) {
|
|
expander = that.table.parent().children('.' + hiddenDivClass);
|
|
that._setContentWidthHandler = proxy(that._setContentWidth, that);
|
|
if (!that.dataSource || !that.dataSource.view().length) {
|
|
if (!expander[0]) {
|
|
expander = $(hiddenDiv).appendTo(that.table.parent());
|
|
if (resizable) {
|
|
resizable.bind('resize', that._setContentWidthHandler);
|
|
}
|
|
}
|
|
if (that.thead) {
|
|
expander.width(that.thead.width());
|
|
if (scrollLeft) {
|
|
that.content.scrollLeft(scrollLeft);
|
|
}
|
|
}
|
|
} else if (expander[0]) {
|
|
expander.remove();
|
|
if (resizable) {
|
|
resizable.unbind('resize', that._setContentWidthHandler);
|
|
}
|
|
}
|
|
that._applyLockedContainersWidth();
|
|
if (that.lockedHeader && that.table[0].clientWidth === 0) {
|
|
that.table[0].style.width = '1px';
|
|
}
|
|
}
|
|
},
|
|
_applyLockedContainersWidth: function () {
|
|
if (this.options.scrollable && this.lockedHeader) {
|
|
var headerTable = this.thead.parent(), headerWrap = headerTable.parent(), contentWidth = this.wrapper[0].clientWidth, groups = this._groups(), scrollbar = kendo.support.scrollbar(), cols = this.lockedHeader.find('>table>colgroup>col:not(.k-group-col, .k-hierarchy-col)'), nonLockedCols = headerTable.find('>colgroup>col:not(.k-group-col, .k-hierarchy-col)'), width = columnsWidth(cols), nonLockedColsWidth = columnsWidth(nonLockedCols), footerWrap;
|
|
if (groups > 0) {
|
|
width += this.lockedHeader.find('.k-group-cell:first').outerWidth() * groups;
|
|
}
|
|
if (width >= contentWidth) {
|
|
width = contentWidth - 3 * scrollbar;
|
|
}
|
|
this.lockedHeader.add(this.lockedContent).width(width);
|
|
headerWrap[0].style.width = headerWrap.parent().width() - width - 2 + 'px';
|
|
headerTable.add(this.table).width(nonLockedColsWidth);
|
|
if (this.virtualScrollable) {
|
|
contentWidth -= scrollbar;
|
|
}
|
|
this.content[0].style.width = contentWidth - width - 2 + 'px';
|
|
if (this.lockedFooter && this.lockedFooter.length) {
|
|
this.lockedFooter.width(width);
|
|
footerWrap = this.footer.find('.k-grid-footer-wrap');
|
|
footerWrap[0].style.width = headerWrap[0].clientWidth + 'px';
|
|
footerWrap.children().first().width(nonLockedColsWidth);
|
|
}
|
|
}
|
|
},
|
|
_setContentHeight: function () {
|
|
var that = this, options = that.options, height = that.wrapper.innerHeight(), header = that.wrapper.children('.k-grid-header'), scrollbar = kendo.support.scrollbar();
|
|
if (options.scrollable && that.wrapper.is(':visible')) {
|
|
height -= header.outerHeight();
|
|
if (that.pager) {
|
|
height -= that.pager.element.outerHeight();
|
|
}
|
|
if (options.groupable) {
|
|
height -= that.wrapper.children('.k-grouping-header').outerHeight();
|
|
}
|
|
if (options.toolbar) {
|
|
height -= that.wrapper.children('.k-grid-toolbar').outerHeight();
|
|
}
|
|
if (that.footerTemplate) {
|
|
height -= that.wrapper.children('.k-grid-footer').outerHeight();
|
|
}
|
|
var isGridHeightSet = function (el) {
|
|
var initialHeight, newHeight;
|
|
if (el[0].style.height) {
|
|
return true;
|
|
} else {
|
|
initialHeight = el.height();
|
|
}
|
|
el.height('auto');
|
|
newHeight = el.height();
|
|
if (initialHeight != newHeight) {
|
|
el.height('');
|
|
return true;
|
|
}
|
|
el.height('');
|
|
return false;
|
|
};
|
|
if (isGridHeightSet(that.wrapper)) {
|
|
if (height > scrollbar * 2) {
|
|
if (that.lockedContent) {
|
|
scrollbar = that.table[0].offsetWidth > that.table.parent()[0].clientWidth ? scrollbar : 0;
|
|
that.lockedContent.height(height - scrollbar);
|
|
}
|
|
that.content.height(height);
|
|
} else {
|
|
that.content.height(scrollbar * 2 + 1);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_averageRowHeight: function () {
|
|
var that = this, itemsCount = that._items(that.tbody).length, rowHeight = that._rowHeight;
|
|
if (itemsCount === 0) {
|
|
return rowHeight;
|
|
}
|
|
if (!that._rowHeight) {
|
|
that._rowHeight = rowHeight = that.table.outerHeight() / itemsCount;
|
|
that._sum = rowHeight;
|
|
that._measures = 1;
|
|
}
|
|
var currentRowHeight = that.table.outerHeight() / itemsCount;
|
|
if (rowHeight !== currentRowHeight) {
|
|
that._measures++;
|
|
that._sum += currentRowHeight;
|
|
that._rowHeight = that._sum / that._measures;
|
|
}
|
|
return rowHeight;
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, options = that.options, pageable, dataSource = options.dataSource;
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (isPlainObject(dataSource)) {
|
|
extend(dataSource, {
|
|
table: that.table,
|
|
fields: that.columns
|
|
});
|
|
pageable = options.pageable;
|
|
if (isPlainObject(pageable) && pageable.pageSize !== undefined) {
|
|
dataSource.pageSize = pageable.pageSize;
|
|
}
|
|
}
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
|
|
} else {
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that._progressHandler = proxy(that._requestStart, that);
|
|
that._errorHandler = proxy(that._error, that);
|
|
}
|
|
that.dataSource = DataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(ERROR, that._errorHandler);
|
|
},
|
|
_error: function () {
|
|
this._progress(false);
|
|
},
|
|
_requestStart: function () {
|
|
this._progress(true);
|
|
},
|
|
_modelChange: function (e) {
|
|
var that = this, tbody = that.tbody, model = e.model, row = that.tbody.find('tr[' + kendo.attr('uid') + '=' + model.uid + ']'), relatedRow, cell, column, isAlt = row.hasClass('k-alt'), tmp, idx = that._items(tbody).index(row), isLocked = that.lockedContent, selectable, selectableRow, childCells, originalCells, length;
|
|
if (isLocked) {
|
|
relatedRow = that._relatedRow(row);
|
|
}
|
|
if (row.add(relatedRow).children('.k-edit-cell').length && !that.options.rowTemplate) {
|
|
row.add(relatedRow).children(':not(.k-group-cell,.k-hierarchy-cell)').each(function () {
|
|
cell = $(this);
|
|
column = leafColumns(that.columns)[that.cellIndex(cell)];
|
|
if (column.field === e.field) {
|
|
if (!cell.hasClass('k-edit-cell')) {
|
|
that._displayCell(cell, column, model);
|
|
$('<span class="k-dirty"/>').prependTo(cell);
|
|
} else {
|
|
cell.addClass('k-dirty-cell');
|
|
}
|
|
}
|
|
});
|
|
} else if (!row.hasClass('k-grid-edit-row')) {
|
|
selectableRow = $().add(row);
|
|
if (isLocked) {
|
|
tmp = (isAlt ? that.lockedAltRowTemplate : that.lockedRowTemplate)(model);
|
|
selectableRow = selectableRow.add(relatedRow);
|
|
relatedRow.replaceWith(tmp);
|
|
}
|
|
that.angular('cleanup', function () {
|
|
return { elements: selectableRow.get() };
|
|
});
|
|
tmp = (isAlt ? that.altRowTemplate : that.rowTemplate)(model);
|
|
row.replaceWith(tmp);
|
|
tmp = that._items(tbody).eq(idx);
|
|
var angularData = [{ dataItem: model }];
|
|
if (isLocked) {
|
|
row = row.add(relatedRow);
|
|
relatedRow = that._relatedRow(tmp)[0];
|
|
adjustRowHeight(tmp[0], relatedRow);
|
|
tmp = tmp.add(relatedRow);
|
|
angularData.push({ dataItem: model });
|
|
}
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: tmp.get(),
|
|
data: angularData
|
|
};
|
|
});
|
|
selectable = that.options.selectable;
|
|
if (selectable && row.hasClass('k-state-selected')) {
|
|
that.select(tmp);
|
|
}
|
|
originalCells = selectableRow.children(':not(.k-group-cell,.k-hierarchy-cell)');
|
|
childCells = tmp.children(':not(.k-group-cell,.k-hierarchy-cell)');
|
|
for (idx = 0, length = that.columns.length; idx < length; idx++) {
|
|
column = that.columns[idx];
|
|
cell = childCells.eq(idx);
|
|
if (selectable && originalCells.eq(idx).hasClass('k-state-selected')) {
|
|
cell.addClass('k-state-selected');
|
|
}
|
|
if (column.field === e.field) {
|
|
$('<span class="k-dirty"/>').prependTo(cell);
|
|
}
|
|
}
|
|
that.trigger('itemChange', {
|
|
item: tmp,
|
|
data: model,
|
|
ns: ui
|
|
});
|
|
}
|
|
},
|
|
_pageable: function () {
|
|
var that = this, wrapper, pageable = that.options.pageable;
|
|
if (pageable) {
|
|
wrapper = that.wrapper.children('div.k-grid-pager');
|
|
if (!wrapper.length) {
|
|
wrapper = $('<div class="k-pager-wrap k-grid-pager"/>').appendTo(that.wrapper);
|
|
}
|
|
if (that.pager) {
|
|
that.pager.destroy();
|
|
}
|
|
if (typeof pageable === 'object' && pageable instanceof kendo.ui.Pager) {
|
|
that.pager = pageable;
|
|
} else {
|
|
that.pager = new kendo.ui.Pager(wrapper, extend({}, pageable, { dataSource: that.dataSource }));
|
|
}
|
|
}
|
|
},
|
|
_footer: function () {
|
|
var that = this, aggregates = that.dataSource.aggregates(), html = '', footerTemplate = that.footerTemplate, options = that.options, footerWrap, footer = that.footer || that.wrapper.find('.k-grid-footer');
|
|
if (footerTemplate) {
|
|
html = $(that._wrapFooter(footerTemplate(aggregates)));
|
|
if (footer.length) {
|
|
var tmp = html;
|
|
that.angular('cleanup', function () {
|
|
return { elements: footer.get() };
|
|
});
|
|
footer.replaceWith(tmp);
|
|
footer = that.footer = tmp;
|
|
} else {
|
|
if (options.scrollable) {
|
|
footer = that.footer = options.pageable ? html.insertBefore(that.wrapper.children('div.k-grid-pager')) : html.appendTo(that.wrapper);
|
|
} else {
|
|
footer = that.footer = html.insertBefore(that.tbody);
|
|
}
|
|
}
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: footer.find('td:not(.k-group-cell, .k-hierarchy-cell)').get(),
|
|
data: map(that.columns, function (col) {
|
|
return {
|
|
column: col,
|
|
aggregate: aggregates[col.field]
|
|
};
|
|
})
|
|
};
|
|
});
|
|
} else if (footer && !that.footer) {
|
|
that.footer = footer;
|
|
}
|
|
if (footer.length) {
|
|
if (options.scrollable) {
|
|
footerWrap = footer.attr('tabindex', -1).children('.k-grid-footer-wrap');
|
|
that.scrollables = that.scrollables.filter(function () {
|
|
return !$(this).is('.k-grid-footer-wrap');
|
|
}).add(footerWrap);
|
|
}
|
|
if (that._footerWidth) {
|
|
footer.find('table').css('width', that._footerWidth);
|
|
}
|
|
if (footerWrap) {
|
|
var offset = that.content.scrollLeft();
|
|
if (options.scrollable !== true && options.scrollable.virtual) {
|
|
offset = that.wrapper.find('.k-virtual-scrollable-wrap').scrollLeft();
|
|
}
|
|
footerWrap.scrollLeft(offset);
|
|
}
|
|
}
|
|
if (that.lockedContent) {
|
|
that._appendLockedColumnFooter();
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedFooterHeight();
|
|
}
|
|
},
|
|
_wrapFooter: function (footerRow) {
|
|
var that = this, html = '', scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;
|
|
if (that.options.scrollable) {
|
|
html = $('<div class="k-grid-footer"><div class="k-grid-footer-wrap"><table' + (isIE7 ? ' cellspacing="0"' : '') + '><tbody>' + footerRow + '</tbody></table></div></div>');
|
|
that._appendCols(html.find('table'));
|
|
html.css(isRtl ? 'padding-left' : 'padding-right', scrollbar);
|
|
return html;
|
|
}
|
|
return '<tfoot class="k-grid-footer">' + footerRow + '</tfoot>';
|
|
},
|
|
_columnMenu: function () {
|
|
var that = this, menu, columns = leafColumns(that.columns), column, options = that.options, columnMenu = options.columnMenu, menuOptions, sortable, filterable, cells, hasMultiColumnHeaders = grep(that.columns, function (item) {
|
|
return item.columns !== undefined;
|
|
}).length > 0, isMobile = this._isMobile, initCallback = function (e) {
|
|
that.trigger(COLUMNMENUINIT, {
|
|
field: e.field,
|
|
container: e.container
|
|
});
|
|
}, closeCallback = function (element) {
|
|
focusTable(element.closest('table'), true);
|
|
}, $angular = options.$angular;
|
|
if (columnMenu) {
|
|
if (typeof columnMenu == 'boolean') {
|
|
columnMenu = {};
|
|
}
|
|
cells = leafDataCells(that.thead);
|
|
for (var idx = 0, length = cells.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
var cell = cells.eq(idx);
|
|
if (!column.command && (column.field || cell.attr('data-' + kendo.ns + 'field'))) {
|
|
menu = cell.data('kendoColumnMenu');
|
|
if (menu) {
|
|
menu.destroy();
|
|
}
|
|
sortable = column.sortable !== false && columnMenu.sortable !== false && options.sortable !== false ? extend({}, options.sortable, { compare: (column.sortable || {}).compare }) : false;
|
|
filterable = options.filterable && column.filterable !== false && columnMenu.filterable !== false ? extend({ pane: that.pane }, options.filterable, column.filterable) : false;
|
|
if (column.filterable && column.filterable.dataSource) {
|
|
filterable.forceUnique = false;
|
|
filterable.checkSource = column.filterable.dataSource;
|
|
}
|
|
if (filterable) {
|
|
filterable.format = column.format;
|
|
}
|
|
menuOptions = {
|
|
dataSource: that.dataSource,
|
|
values: column.values,
|
|
columns: columnMenu.columns,
|
|
sortable: sortable,
|
|
filterable: filterable,
|
|
messages: columnMenu.messages,
|
|
owner: that,
|
|
closeCallback: closeCallback,
|
|
init: initCallback,
|
|
pane: that.pane,
|
|
filter: isMobile ? ':not(.k-column-active)' : '',
|
|
lockedColumns: !hasMultiColumnHeaders && column.lockable !== false && lockedColumns(columns).length > 0
|
|
};
|
|
if ($angular) {
|
|
menuOptions.$angular = $angular;
|
|
}
|
|
cell.kendoColumnMenu(menuOptions);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_headerCells: function () {
|
|
return this.thead.find('th').filter(function () {
|
|
var th = $(this);
|
|
return !th.hasClass('k-group-cell') && !th.hasClass('k-hierarchy-cell');
|
|
});
|
|
},
|
|
_filterable: function () {
|
|
var that = this, columns = leafColumns(that.columns), filterMenu, cells, cell, filterInit = function (e) {
|
|
that.trigger(FILTERMENUINIT, {
|
|
field: e.field,
|
|
container: e.container
|
|
});
|
|
}, closeCallback = function (element) {
|
|
focusTable(element.closest('table'), true);
|
|
}, filterable = that.options.filterable;
|
|
if (filterable && typeof filterable.mode == STRING && filterable.mode.indexOf('menu') == -1) {
|
|
filterable = false;
|
|
}
|
|
if (filterable && !that.options.columnMenu) {
|
|
cells = leafDataCells(that.thead);
|
|
for (var idx = 0, length = cells.length; idx < length; idx++) {
|
|
cell = cells.eq(idx);
|
|
if (columns[idx].filterable !== false && !columns[idx].command && (columns[idx].field || cell.attr('data-' + kendo.ns + 'field'))) {
|
|
filterMenu = cell.data('kendoFilterMenu');
|
|
if (filterMenu) {
|
|
filterMenu.destroy();
|
|
}
|
|
filterMenu = cell.data('kendoFilterMultiCheck');
|
|
if (filterMenu) {
|
|
filterMenu.destroy();
|
|
}
|
|
var columnFilterable = columns[idx].filterable;
|
|
var options = extend({}, filterable, columnFilterable, {
|
|
dataSource: that.dataSource,
|
|
values: columns[idx].values,
|
|
format: columns[idx].format,
|
|
closeCallback: closeCallback,
|
|
title: columns[idx].title || columns[idx].field,
|
|
init: filterInit,
|
|
pane: that.pane
|
|
});
|
|
if (columnFilterable && columnFilterable.messages) {
|
|
options.messages = extend(true, {}, filterable.messages, columnFilterable.messages);
|
|
}
|
|
if (columnFilterable && columnFilterable.dataSource) {
|
|
options.forceUnique = false;
|
|
options.checkSource = columnFilterable.dataSource;
|
|
}
|
|
if (columnFilterable && columnFilterable.multi) {
|
|
cell.kendoFilterMultiCheck(options);
|
|
} else {
|
|
cell.kendoFilterMenu(options);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_filterRow: function () {
|
|
var that = this;
|
|
if (!that._hasFilterRow()) {
|
|
return;
|
|
}
|
|
var settings;
|
|
var $angular = that.options.$angular;
|
|
var columns = leafColumns(that.columns), filterable = that.options.filterable, rowheader = that.thead.find('.k-filter-row');
|
|
this._updateHeader(this.dataSource.group().length);
|
|
for (var i = 0; i < columns.length; i++) {
|
|
var suggestDataSource, col = columns[i], operators = that.options.filterable.operators, customDataSource = false, th = $('<th/>'), field = col.field;
|
|
if (col.hidden) {
|
|
th.hide();
|
|
}
|
|
rowheader.append(th);
|
|
if (field && col.filterable !== false) {
|
|
var cellOptions = col.filterable && col.filterable.cell || {};
|
|
suggestDataSource = that.options.dataSource;
|
|
if (suggestDataSource instanceof DataSource) {
|
|
suggestDataSource = that.options.dataSource.options;
|
|
}
|
|
var messages = extend(true, {}, filterable.messages);
|
|
if (col.filterable) {
|
|
extend(true, messages, col.filterable.messages);
|
|
}
|
|
if (cellOptions.enabled === false) {
|
|
th.html(' ');
|
|
continue;
|
|
}
|
|
if (cellOptions.dataSource) {
|
|
suggestDataSource = cellOptions.dataSource;
|
|
customDataSource = true;
|
|
}
|
|
if (col.filterable && col.filterable.operators) {
|
|
operators = col.filterable.operators;
|
|
}
|
|
settings = {
|
|
column: col,
|
|
dataSource: that.dataSource,
|
|
suggestDataSource: suggestDataSource,
|
|
customDataSource: customDataSource,
|
|
field: field,
|
|
messages: messages,
|
|
values: col.values,
|
|
template: cellOptions.template,
|
|
delay: cellOptions.delay,
|
|
inputWidth: cellOptions.inputWidth,
|
|
suggestionOperator: cellOptions.suggestionOperator,
|
|
minLength: cellOptions.minLength,
|
|
dataTextField: cellOptions.dataTextField,
|
|
operator: cellOptions.operator,
|
|
operators: operators,
|
|
showOperators: cellOptions.showOperators
|
|
};
|
|
if ($angular) {
|
|
settings.$angular = $angular;
|
|
}
|
|
$('<span/>').attr(kendo.attr('field'), field).appendTo(th).kendoFilterCell(settings);
|
|
} else {
|
|
th.html(' ');
|
|
}
|
|
}
|
|
},
|
|
_sortable: function () {
|
|
var that = this, columns = leafColumns(that.columns), column, sorterInstance, cell, sortable = that.options.sortable;
|
|
if (sortable) {
|
|
var cells = leafDataCells(that.thead);
|
|
for (var idx = 0, length = cells.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.sortable !== false && !column.command && column.field) {
|
|
cell = cells.eq(idx);
|
|
sorterInstance = cell.data('kendoColumnSorter');
|
|
if (sorterInstance) {
|
|
sorterInstance.destroy();
|
|
}
|
|
cell.attr('data-' + kendo.ns + 'field', column.field).kendoColumnSorter(extend({}, sortable, column.sortable, {
|
|
dataSource: that.dataSource,
|
|
aria: true,
|
|
filter: ':not(.k-column-active)'
|
|
}));
|
|
}
|
|
}
|
|
cells = null;
|
|
}
|
|
},
|
|
_columns: function (columns) {
|
|
var that = this, table = that.table, encoded, cols = table.find('col'), lockedCols, dataSource = that.options.dataSource;
|
|
columns = columns.length ? columns : map(table.find('th'), function (th, idx) {
|
|
th = $(th);
|
|
var sortable = th.attr(kendo.attr('sortable')), filterable = th.attr(kendo.attr('filterable')), type = th.attr(kendo.attr('type')), groupable = th.attr(kendo.attr('groupable')), field = th.attr(kendo.attr('field')), title = th.attr(kendo.attr('title')), menu = th.attr(kendo.attr('menu'));
|
|
if (!field) {
|
|
field = th.text().replace(/\s|[^A-z0-9]/g, '');
|
|
}
|
|
return {
|
|
field: field,
|
|
type: type,
|
|
title: title,
|
|
sortable: sortable !== 'false',
|
|
filterable: filterable !== 'false',
|
|
groupable: groupable !== 'false',
|
|
menu: menu,
|
|
template: th.attr(kendo.attr('template')),
|
|
width: cols.eq(idx).css('width')
|
|
};
|
|
});
|
|
encoded = !(that.table.find('tbody tr').length > 0 && (!dataSource || !dataSource.transport));
|
|
if (that.options.scrollable) {
|
|
var initialColumns = columns;
|
|
lockedCols = lockedColumns(columns);
|
|
columns = nonLockedColumns(columns);
|
|
if (lockedCols.length > 0 && columns.length === 0) {
|
|
throw new Error('There should be at least one non locked column');
|
|
}
|
|
normalizeHeaderCells(that.element.find('tr:has(th):first'), initialColumns);
|
|
columns = lockedCols.concat(columns);
|
|
}
|
|
that.columns = normalizeColumns(columns, encoded);
|
|
},
|
|
_groups: function () {
|
|
var group = this.dataSource.group();
|
|
return group ? group.length : 0;
|
|
},
|
|
_tmpl: function (rowTemplate, columns, alt, skipGroupCells) {
|
|
var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), idx, length = columns.length, template, state = {
|
|
storage: {},
|
|
count: 0
|
|
}, column, type, hasDetails = that._hasDetails(), className = [], groups = that._groups();
|
|
if (!rowTemplate) {
|
|
rowTemplate = '<tr';
|
|
if (alt) {
|
|
className.push('k-alt');
|
|
}
|
|
if (hasDetails) {
|
|
className.push('k-master-row');
|
|
}
|
|
if (className.length) {
|
|
rowTemplate += ' class="' + className.join(' ') + '"';
|
|
}
|
|
if (length) {
|
|
rowTemplate += ' ' + kendo.attr('uid') + '="#=' + kendo.expr('uid', settings.paramName) + '#"';
|
|
}
|
|
rowTemplate += ' role=\'row\'>';
|
|
if (groups > 0 && !skipGroupCells) {
|
|
rowTemplate += groupCells(groups);
|
|
}
|
|
if (hasDetails) {
|
|
rowTemplate += '<td class="k-hierarchy-cell"><a class="k-icon k-plus" href="\\#" tabindex="-1"></a></td>';
|
|
}
|
|
for (idx = 0; idx < length; idx++) {
|
|
column = columns[idx];
|
|
template = column.template;
|
|
type = typeof template;
|
|
rowTemplate += '<td' + stringifyAttributes(column.attributes) + ' role=\'gridcell\'>';
|
|
rowTemplate += that._cellTmpl(column, state);
|
|
rowTemplate += '</td>';
|
|
}
|
|
rowTemplate += '</tr>';
|
|
}
|
|
rowTemplate = kendo.template(rowTemplate, settings);
|
|
if (state.count > 0) {
|
|
return proxy(rowTemplate, state.storage);
|
|
}
|
|
return rowTemplate;
|
|
},
|
|
_headerCellText: function (column) {
|
|
var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.headerTemplate, type = typeof template, text = column.title || column.field || '';
|
|
if (type === FUNCTION) {
|
|
text = kendo.template(template, settings)({});
|
|
} else if (type === STRING) {
|
|
text = template;
|
|
}
|
|
return text;
|
|
},
|
|
_cellTmpl: function (column, state) {
|
|
var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.template, paramName = settings.paramName, field = column.field, html = '', idx, length, format = column.format, type = typeof template, columnValues = column.values;
|
|
if (column.command) {
|
|
if (isArray(column.command)) {
|
|
for (idx = 0, length = column.command.length; idx < length; idx++) {
|
|
html += that._createButton(column.command[idx]);
|
|
}
|
|
return html.replace(templateHashRegExp, '\\#');
|
|
}
|
|
return that._createButton(column.command).replace(templateHashRegExp, '\\#');
|
|
}
|
|
if (type === FUNCTION) {
|
|
state.storage['tmpl' + state.count] = template;
|
|
html += '#=this.tmpl' + state.count + '(' + paramName + ')#';
|
|
state.count++;
|
|
} else if (type === STRING) {
|
|
html += template;
|
|
} else if (columnValues && columnValues.length && isPlainObject(columnValues[0]) && 'value' in columnValues[0] && field) {
|
|
html += '#var v =' + kendo.stringify(convertToObject(columnValues)).replace(templateHashRegExp, '\\#') + '#';
|
|
html += '#var f = v[';
|
|
if (!settings.useWithBlock) {
|
|
html += paramName + '.';
|
|
}
|
|
html += field + ']#';
|
|
html += '${f != null ? f : \'\'}';
|
|
} else {
|
|
html += column.encoded ? '#:' : '#=';
|
|
if (format) {
|
|
html += 'kendo.format("' + format.replace(formatRegExp, '\\$1') + '",';
|
|
}
|
|
if (field) {
|
|
field = kendo.expr(field, paramName);
|
|
html += field + '==null?\'\':' + field;
|
|
} else {
|
|
html += '\'\'';
|
|
}
|
|
if (format) {
|
|
html += ')';
|
|
}
|
|
html += '#';
|
|
}
|
|
return html;
|
|
},
|
|
_templates: function () {
|
|
var that = this, options = that.options, dataSource = that.dataSource, groups = dataSource.group(), footer = that.footer || that.wrapper.find('.k-grid-footer'), aggregates = dataSource.aggregate(), columnLeafs = leafColumns(that.columns), columnsLocked = leafColumns(lockedColumns(that.columns)), columns = options.scrollable ? leafColumns(nonLockedColumns(that.columns)) : columnLeafs;
|
|
if (options.scrollable && columnsLocked.length) {
|
|
if (options.rowTemplate || options.altRowTemplate) {
|
|
throw new Error('Having both row template and locked columns is not supported');
|
|
}
|
|
that.rowTemplate = that._tmpl(options.rowTemplate, columns, false, true);
|
|
that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, columns, true, true);
|
|
that.lockedRowTemplate = that._tmpl(options.rowTemplate, columnsLocked);
|
|
that.lockedAltRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, columnsLocked, true);
|
|
} else {
|
|
that.rowTemplate = that._tmpl(options.rowTemplate, columns);
|
|
that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, columns, true);
|
|
}
|
|
if (that._hasDetails()) {
|
|
that.detailTemplate = that._detailTmpl(options.detailTemplate || '');
|
|
}
|
|
if (that._group && !isEmptyObject(aggregates) || !isEmptyObject(aggregates) && !footer.length || grep(columnLeafs, function (column) {
|
|
return column.footerTemplate;
|
|
}).length) {
|
|
that.footerTemplate = that._footerTmpl(columnLeafs, aggregates, 'footerTemplate', 'k-footer-template');
|
|
}
|
|
if (groups && grep(columnLeafs, function (column) {
|
|
return column.groupFooterTemplate;
|
|
}).length) {
|
|
aggregates = $.map(groups, function (g) {
|
|
return g.aggregates;
|
|
});
|
|
that.groupFooterTemplate = that._footerTmpl(columns, aggregates, 'groupFooterTemplate', 'k-group-footer', columnsLocked.length);
|
|
if (options.scrollable && columnsLocked.length) {
|
|
that.lockedGroupFooterTemplate = that._footerTmpl(columnsLocked, aggregates, 'groupFooterTemplate', 'k-group-footer');
|
|
}
|
|
}
|
|
if (that.options.noRecords) {
|
|
that.noRecordsTemplate = that._noRecordsTmpl();
|
|
}
|
|
},
|
|
_noRecordsTmpl: function () {
|
|
var wrapper = '<div class="{0}">{1}</div>';
|
|
var defaultTemplate = '<div class="k-grid-norecords-template"{1}>{0}</div>';
|
|
var scrollableNoGridHeightStyles = this.options.scrollable && !this.wrapper[0].style.height ? ' style="margin:0 auto;position:static;"' : '';
|
|
var state = {
|
|
storage: {},
|
|
count: 0
|
|
};
|
|
var settings = $.extend({}, kendo.Template, this.options.templateSettings);
|
|
var paramName = settings.paramName;
|
|
var template;
|
|
var html = '';
|
|
var type;
|
|
var tmpl;
|
|
if (this.options.noRecords.template) {
|
|
template = this.options.noRecords.template;
|
|
} else {
|
|
template = kendo.format(defaultTemplate, this.options.messages.noRecords, scrollableNoGridHeightStyles);
|
|
}
|
|
type = typeof template;
|
|
if (type === 'function') {
|
|
state.storage['tmpl' + state.count] = template;
|
|
html += '#=this.tmpl' + state.count + '(' + paramName + ')#';
|
|
state.count++;
|
|
} else if (type === 'string') {
|
|
html += template;
|
|
}
|
|
tmpl = kendo.template(kendo.format(wrapper, NORECORDSCLASS, html), settings);
|
|
if (state.count > 0) {
|
|
tmpl = $.proxy(tmpl, state.storage);
|
|
}
|
|
return tmpl;
|
|
},
|
|
_footerTmpl: function (columns, aggregates, templateName, rowClass, skipGroupCells) {
|
|
var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, html = '', idx, length, template, type, storage = {}, count = 0, scope = {}, groups = that._groups(), fieldsMap = that.dataSource._emptyAggregates(aggregates), column;
|
|
html += '<tr class="' + rowClass + '">';
|
|
if (groups > 0 && !skipGroupCells) {
|
|
html += groupCells(groups);
|
|
}
|
|
if (that._hasDetails()) {
|
|
html += '<td class="k-hierarchy-cell"> </td>';
|
|
}
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
template = column[templateName];
|
|
type = typeof template;
|
|
html += '<td' + stringifyAttributes(column.footerAttributes) + '>';
|
|
if (template) {
|
|
if (type !== FUNCTION) {
|
|
scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + '[\'' + column.field + '\']' }) : {};
|
|
template = kendo.template(template, scope);
|
|
}
|
|
storage['tmpl' + count] = template;
|
|
html += '#=this.tmpl' + count + '(' + paramName + ')#';
|
|
count++;
|
|
} else {
|
|
html += ' ';
|
|
}
|
|
html += '</td>';
|
|
}
|
|
html += '</tr>';
|
|
html = kendo.template(html, settings);
|
|
if (count > 0) {
|
|
return proxy(html, storage);
|
|
}
|
|
return html;
|
|
},
|
|
_detailTmpl: function (template) {
|
|
var that = this, html = '', settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, templateFunctionStorage = {}, templateFunctionCount = 0, groups = that._groups(), colspan = visibleColumns(leafColumns(that.columns)).length, type = typeof template;
|
|
html += '<tr class="k-detail-row">';
|
|
if (groups > 0) {
|
|
html += groupCells(groups);
|
|
}
|
|
html += '<td class="k-hierarchy-cell"></td><td class="k-detail-cell"' + (colspan ? ' colspan="' + colspan + '"' : '') + '>';
|
|
if (type === FUNCTION) {
|
|
templateFunctionStorage['tmpl' + templateFunctionCount] = template;
|
|
html += '#=this.tmpl' + templateFunctionCount + '(' + paramName + ')#';
|
|
templateFunctionCount++;
|
|
} else {
|
|
html += template;
|
|
}
|
|
html += '</td></tr>';
|
|
html = kendo.template(html, settings);
|
|
if (templateFunctionCount > 0) {
|
|
return proxy(html, templateFunctionStorage);
|
|
}
|
|
return html;
|
|
},
|
|
_hasDetails: function () {
|
|
var that = this;
|
|
return that.options.detailTemplate !== null || (that._events[DETAILINIT] || []).length;
|
|
},
|
|
_hasFilterRow: function () {
|
|
var filterable = this.options.filterable;
|
|
var hasFiltering = filterable && typeof filterable.mode == STRING && filterable.mode.indexOf('row') != -1;
|
|
var columns = this.columns;
|
|
var columnsWithoutFiltering = $.grep(columns, function (col) {
|
|
return col.filterable === false;
|
|
});
|
|
if (columns.length && columnsWithoutFiltering.length == columns.length) {
|
|
hasFiltering = false;
|
|
}
|
|
return hasFiltering;
|
|
},
|
|
_details: function () {
|
|
var that = this;
|
|
if (that.options.scrollable && that._hasDetails() && lockedColumns(that.columns).length) {
|
|
throw new Error('Having both detail template and locked columns is not supported');
|
|
}
|
|
that.table.on(CLICK + NS, '.k-hierarchy-cell .k-plus, .k-hierarchy-cell .k-minus', function (e) {
|
|
var button = $(this), expanding = button.hasClass('k-plus'), masterRow = button.closest('tr.k-master-row'), detailRow, detailTemplate = that.detailTemplate, data, hasDetails = that._hasDetails();
|
|
button.toggleClass('k-plus', !expanding).toggleClass('k-minus', expanding);
|
|
detailRow = masterRow.next();
|
|
if (hasDetails && !detailRow.hasClass('k-detail-row')) {
|
|
data = that.dataItem(masterRow);
|
|
detailRow = $(detailTemplate(data)).addClass(masterRow.hasClass('k-alt') ? 'k-alt' : '').insertAfter(masterRow);
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: detailRow.get(),
|
|
data: [{ dataItem: data }]
|
|
};
|
|
});
|
|
that.trigger(DETAILINIT, {
|
|
masterRow: masterRow,
|
|
detailRow: detailRow,
|
|
data: data,
|
|
detailCell: detailRow.find('.k-detail-cell')
|
|
});
|
|
}
|
|
that.trigger(expanding ? DETAILEXPAND : DETAILCOLLAPSE, {
|
|
masterRow: masterRow,
|
|
detailRow: detailRow
|
|
});
|
|
detailRow.toggle(expanding);
|
|
if (that._current) {
|
|
that._current.attr('aria-expanded', expanding);
|
|
}
|
|
e.preventDefault();
|
|
return false;
|
|
});
|
|
},
|
|
dataItem: function (tr) {
|
|
tr = $(tr)[0];
|
|
if (!tr) {
|
|
return null;
|
|
}
|
|
var rows = this.tbody.children(), classesRegEx = /k-grouping-row|k-detail-row|k-group-footer/, idx = tr.sectionRowIndex, j, correctIdx;
|
|
correctIdx = idx;
|
|
for (j = 0; j < idx; j++) {
|
|
if (classesRegEx.test(rows[j].className)) {
|
|
correctIdx--;
|
|
}
|
|
}
|
|
return this._data[correctIdx];
|
|
},
|
|
expandRow: function (tr) {
|
|
$(tr).find('> td .k-plus, > td .k-i-expand').click();
|
|
},
|
|
collapseRow: function (tr) {
|
|
$(tr).find('> td .k-minus, > td .k-i-collapse').click();
|
|
},
|
|
_createHeaderCells: function (columns, rowSpan) {
|
|
var that = this, idx, th, text, html = '', length, leafs = leafColumns(that.columns), field;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
th = columns[idx].column || columns[idx];
|
|
text = that._headerCellText(th);
|
|
field = '';
|
|
var index = inArray(th, leafs);
|
|
if (!th.command) {
|
|
if (th.field) {
|
|
field = kendo.attr('field') + '=\'' + th.field + '\' ';
|
|
}
|
|
html += '<th scope=\'col\' role=\'columnheader\' ' + field;
|
|
if (rowSpan && !columns[idx].colSpan) {
|
|
html += ' rowspan=\'' + rowSpan + '\'';
|
|
}
|
|
if (columns[idx].colSpan > 1) {
|
|
html += 'colspan="' + (columns[idx].colSpan - hiddenLeafColumnsCount(th.columns)) + '" ';
|
|
html += kendo.attr('colspan') + '=\'' + columns[idx].colSpan + '\'';
|
|
}
|
|
if (th.title) {
|
|
html += kendo.attr('title') + '="' + th.title.replace('"', '"').replace(/'/g, '\'') + '" ';
|
|
}
|
|
if (th.groupable !== undefined) {
|
|
html += kendo.attr('groupable') + '=\'' + th.groupable + '\' ';
|
|
}
|
|
if (th.aggregates && th.aggregates.length) {
|
|
html += kendo.attr('aggregates') + '=\'' + th.aggregates + '\'';
|
|
}
|
|
if (index > -1) {
|
|
html += kendo.attr('index') + '=\'' + index + '\'';
|
|
}
|
|
html += stringifyAttributes(th.headerAttributes);
|
|
html += '>' + text + '</th>';
|
|
} else {
|
|
html += '<th scope=\'col\'' + stringifyAttributes(th.headerAttributes);
|
|
if (rowSpan && !columns[idx].colSpan) {
|
|
html += ' rowspan=\'' + rowSpan + '\'';
|
|
}
|
|
if (index > -1) {
|
|
html += kendo.attr('index') + '=\'' + index + '\'';
|
|
}
|
|
html += '>' + text + '</th>';
|
|
}
|
|
}
|
|
return html;
|
|
},
|
|
_appendLockedColumnContent: function () {
|
|
var columns = this.columns, idx, colgroup = this.table.find('colgroup'), cols = colgroup.find('col:not(.k-group-col,.k-hierarchy-col)'), length, lockedCols = $(), skipHiddenCount = 0, container, colSpan, spanIdx, colOffset = 0;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
if (columns[idx].locked) {
|
|
if (isVisible(columns[idx])) {
|
|
colSpan = 1;
|
|
if (columns[idx].columns) {
|
|
colSpan = leafColumns(columns[idx].columns).length - hiddenLeafColumnsCount(columns[idx].columns);
|
|
}
|
|
colSpan = colSpan || 1;
|
|
for (spanIdx = 0; spanIdx < colSpan; spanIdx++) {
|
|
lockedCols = lockedCols.add(cols.eq(idx + colOffset + spanIdx - skipHiddenCount));
|
|
}
|
|
colOffset += colSpan - 1;
|
|
} else {
|
|
skipHiddenCount++;
|
|
}
|
|
}
|
|
}
|
|
container = $('<div class="k-grid-content-locked"><table' + (isIE7 ? ' cellspacing="0"' : '') + '><colgroup/><tbody></tbody></table></div>');
|
|
colgroup.detach();
|
|
container.find('colgroup').append(lockedCols);
|
|
colgroup.insertBefore(this.table.find('tbody'));
|
|
this.lockedContent = container.insertBefore(this.content);
|
|
this.lockedTable = container.children('table');
|
|
},
|
|
_appendLockedColumnFooter: function () {
|
|
var that = this;
|
|
var footer = that.footer;
|
|
var cells = footer.find('.k-footer-template>td');
|
|
var cols = footer.find('.k-grid-footer-wrap>table>colgroup>col');
|
|
var html = $('<div class="k-grid-footer-locked"><table><colgroup /><tbody><tr class="k-footer-template"></tr></tbody></table></div>');
|
|
var idx, length;
|
|
var groups = that._groups();
|
|
var lockedCells = $(), lockedCols = $();
|
|
lockedCells = lockedCells.add(cells.filter('.k-group-cell'));
|
|
for (idx = 0, length = leafColumns(lockedColumns(that.columns)).length; idx < length; idx++) {
|
|
lockedCells = lockedCells.add(cells.eq(idx + groups));
|
|
}
|
|
lockedCols = lockedCols.add(cols.filter('.k-group-col'));
|
|
for (idx = 0, length = visibleColumns(leafColumns(visibleLockedColumns(that.columns))).length; idx < length; idx++) {
|
|
lockedCols = lockedCols.add(cols.eq(idx + groups));
|
|
}
|
|
lockedCells.appendTo(html.find('tr'));
|
|
lockedCols.appendTo(html.find('colgroup'));
|
|
that.lockedFooter = html.prependTo(footer);
|
|
},
|
|
_appendLockedColumnHeader: function (container) {
|
|
var that = this, columns = this.columns, idx, html, length, colgroup, tr, trFilter, table, header, filtercellCells, rows = [], skipHiddenCount = 0, cols = $(), hasFilterRow = that._hasFilterRow(), filterCellOffset = 0, filterCells = $(), cell, leafColumnsCount = 0, cells = $();
|
|
colgroup = that.thead.prev().find('col:not(.k-group-col,.k-hierarchy-col)');
|
|
header = that.thead.find('tr:first .k-header:not(.k-group-cell,.k-hierarchy-cell)');
|
|
filtercellCells = that.thead.find('.k-filter-row').find('th:not(.k-group-cell,.k-hierarchy-cell)');
|
|
var colOffset = 0;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
if (columns[idx].locked) {
|
|
cell = header.eq(idx);
|
|
leafColumnsCount = leafColumns(columns[idx].columns || []).length;
|
|
if (isVisible(columns[idx])) {
|
|
var colSpan = null;
|
|
if (columns[idx].columns) {
|
|
colSpan = leafColumnsCount - hiddenLeafColumnsCount(columns[idx].columns);
|
|
}
|
|
colSpan = colSpan || 1;
|
|
for (var spanIdx = 0; spanIdx < colSpan; spanIdx++) {
|
|
cols = cols.add(colgroup.eq(idx + colOffset + spanIdx - skipHiddenCount));
|
|
}
|
|
colOffset += colSpan - 1;
|
|
}
|
|
mapColumnToCellRows([columns[idx]], childColumnsCells(cell), rows, 0, 0);
|
|
leafColumnsCount = leafColumnsCount || 1;
|
|
for (var j = 0; j < leafColumnsCount; j++) {
|
|
filterCells = filterCells.add(filtercellCells.eq(filterCellOffset + j));
|
|
}
|
|
filterCellOffset += leafColumnsCount;
|
|
}
|
|
if (columns[idx].columns) {
|
|
skipHiddenCount += hiddenLeafColumnsCount(columns[idx].columns);
|
|
}
|
|
if (!isVisible(columns[idx])) {
|
|
skipHiddenCount++;
|
|
}
|
|
}
|
|
if (rows.length) {
|
|
html = '<div class="k-grid-header-locked" style="width:1px"><table' + (isIE7 ? ' cellspacing="0"' : '') + '><colgroup/><thead>';
|
|
html += new Array(rows.length + 1).join('<tr></tr>');
|
|
html += (hasFilterRow ? '<tr class="k-filter-row" />' : '') + '</thead></table></div>';
|
|
table = $(html);
|
|
colgroup = table.find('colgroup');
|
|
colgroup.append(that.thead.prev().find('col.k-group-col').add(cols));
|
|
tr = table.find('thead tr:not(.k-filter-row)');
|
|
for (idx = 0, length = rows.length; idx < length; idx++) {
|
|
cells = toJQuery(rows[idx]);
|
|
tr.eq(idx).append(that.thead.find('tr:eq(' + idx + ') .k-group-cell').add(cells));
|
|
}
|
|
var count = removeEmptyRows(this.thead);
|
|
if (rows.length < count) {
|
|
removeRowSpanValue(table, count - rows.length);
|
|
}
|
|
trFilter = table.find('.k-filter-row');
|
|
trFilter.append(that.thead.find('.k-filter-row .k-group-cell').add(filterCells));
|
|
this.lockedHeader = table.prependTo(container);
|
|
this.thead.find('.k-group-cell').remove();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_removeLockedContainers: function () {
|
|
var elements = this.lockedHeader.add(this.lockedContent).add(this.lockedFooter);
|
|
kendo.destroy(elements);
|
|
elements.off(NS).remove();
|
|
this.lockedHeader = this.lockedContent = this.lockedFooter = null;
|
|
this.selectable = null;
|
|
},
|
|
_thead: function () {
|
|
var that = this, columns = that.columns, hasDetails = that._hasDetails() && columns.length, hasFilterRow = that._hasFilterRow(), idx, html = '', thead = that.table.find('>thead'), hasTHead = that.element.find('thead:first').length > 0, tr;
|
|
if (!thead.length) {
|
|
thead = $('<thead/>').insertBefore(that.tbody);
|
|
}
|
|
if (that.lockedHeader && that.thead) {
|
|
tr = that.thead.find('tr:has(th):not(.k-filter-row)').html('');
|
|
tr.remove();
|
|
tr = $();
|
|
that._removeLockedContainers();
|
|
} else if (hasTHead) {
|
|
tr = that.element.find('thead:first tr:has(th):not(.k-filter-row)');
|
|
} else {
|
|
tr = that.element.find('tr:has(th):first');
|
|
}
|
|
if (!tr.length) {
|
|
tr = thead.children().first();
|
|
if (!tr.length) {
|
|
var rows = [{
|
|
rowSpan: 1,
|
|
cells: [],
|
|
index: 0
|
|
}];
|
|
that._prepareColumns(rows, columns);
|
|
for (idx = 0; idx < rows.length; idx++) {
|
|
html += '<tr>';
|
|
if (hasDetails) {
|
|
html += '<th class="k-hierarchy-cell" scope="col"> </th>';
|
|
}
|
|
html += that._createHeaderCells(rows[idx].cells, rows[idx].rowSpan);
|
|
html += '</tr>';
|
|
}
|
|
tr = $(html);
|
|
}
|
|
}
|
|
if (hasFilterRow) {
|
|
var filterRow = $('<tr/>');
|
|
filterRow.addClass('k-filter-row');
|
|
if (hasDetails || tr.find('.k-hierarchy-cell').length) {
|
|
filterRow.prepend('<th class="k-hierarchy-cell" scope="col"> </th>');
|
|
}
|
|
var existingFilterRow = (that.thead || thead).find('.k-filter-row');
|
|
if (existingFilterRow.length) {
|
|
kendo.destroy(existingFilterRow);
|
|
existingFilterRow.remove();
|
|
}
|
|
thead.append(filterRow);
|
|
}
|
|
if (!tr.children().length) {
|
|
html = '';
|
|
if (hasDetails) {
|
|
html += '<th class="k-hierarchy-cell" scope="col"> </th>';
|
|
}
|
|
html += that._createHeaderCells(columns);
|
|
tr.html(html);
|
|
} else if (hasDetails && !tr.find('.k-hierarchy-cell')[0]) {
|
|
tr.prepend('<th class="k-hierarchy-cell" scope="col"> </th>');
|
|
}
|
|
tr.attr('role', 'row').find('th').addClass('k-header');
|
|
if (!that.options.scrollable) {
|
|
thead.addClass('k-grid-header');
|
|
}
|
|
tr.find('script').remove().end().prependTo(thead);
|
|
if (that.thead) {
|
|
that._destroyColumnAttachments();
|
|
}
|
|
this.angular('cleanup', function () {
|
|
return { elements: thead.find('th').get() };
|
|
});
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: thead.find('th').get(),
|
|
data: map(columns, function (col) {
|
|
return { column: col };
|
|
})
|
|
};
|
|
});
|
|
that.thead = thead.attr('role', 'rowgroup');
|
|
that._sortable();
|
|
that._filterable();
|
|
that._filterRow();
|
|
that._scrollable();
|
|
that._updateCols();
|
|
that._columnMenu();
|
|
var syncHeight;
|
|
var hasLockedColumns = this.options.scrollable && lockedColumns(this.columns).length;
|
|
if (hasLockedColumns) {
|
|
syncHeight = that._appendLockedColumnHeader(that.thead.closest('.k-grid-header'));
|
|
that._appendLockedColumnContent();
|
|
that.lockedContent.bind('DOMMouseScroll' + NS + ' mousewheel' + NS, proxy(that._wheelScroll, that));
|
|
}
|
|
that._updateColumnCellIndex();
|
|
that._updateFirstColumnClass();
|
|
that._resizable();
|
|
that._draggable();
|
|
that._reorderable();
|
|
that._updateHeader(that._groups());
|
|
if (hasLockedColumns) {
|
|
if (syncHeight) {
|
|
that._syncLockedHeaderHeight();
|
|
}
|
|
that._applyLockedContainersWidth();
|
|
}
|
|
if (that.groupable) {
|
|
that._attachGroupable();
|
|
}
|
|
},
|
|
_retrieveFirstColumn: function (columns, rows) {
|
|
var result = $();
|
|
if (rows.length && columns[0]) {
|
|
var column = columns[0];
|
|
while (column.columns && column.columns.length) {
|
|
column = column.columns[0];
|
|
rows = rows.filter(':not(:first())');
|
|
}
|
|
result = result.add(rows);
|
|
}
|
|
return result;
|
|
},
|
|
_updateFirstColumnClass: function () {
|
|
var that = this, columns = that.columns || [], hasDetails = that._hasDetails() && columns.length;
|
|
if (!hasDetails && !that._groups()) {
|
|
var tr = that.thead.find('>tr:not(.k-filter-row):not(:first)');
|
|
columns = nonLockedColumns(columns);
|
|
var rows = that._retrieveFirstColumn(columns, tr);
|
|
if (that._isLocked()) {
|
|
tr = that.lockedHeader.find('thead>tr:not(.k-filter-row):not(:first)');
|
|
columns = lockedColumns(that.columns);
|
|
rows = rows.add(that._retrieveFirstColumn(columns, tr));
|
|
}
|
|
rows.each(function () {
|
|
var ths = $(this).find('th');
|
|
ths.removeClass('k-first');
|
|
ths.eq(0).addClass('k-first');
|
|
});
|
|
}
|
|
},
|
|
_prepareColumns: function (rows, columns, parentCell, parentRow) {
|
|
var row = parentRow || rows[rows.length - 1];
|
|
var childRow = rows[row.index + 1];
|
|
var totalColSpan = 0;
|
|
for (var idx = 0; idx < columns.length; idx++) {
|
|
var cell = {
|
|
column: columns[idx],
|
|
colSpan: 0
|
|
};
|
|
row.cells.push(cell);
|
|
if (columns[idx].columns && columns[idx].columns.length) {
|
|
if (!childRow) {
|
|
childRow = {
|
|
rowSpan: 0,
|
|
cells: [],
|
|
index: rows.length
|
|
};
|
|
rows.push(childRow);
|
|
}
|
|
cell.colSpan = columns[idx].columns.length;
|
|
this._prepareColumns(rows, columns[idx].columns, cell, childRow);
|
|
totalColSpan += cell.colSpan - 1;
|
|
row.rowSpan = rows.length - row.index;
|
|
}
|
|
}
|
|
if (parentCell) {
|
|
parentCell.colSpan += totalColSpan;
|
|
}
|
|
},
|
|
_wheelScroll: function (e) {
|
|
if (e.ctrlKey) {
|
|
return;
|
|
}
|
|
var content = this.content;
|
|
if (this.options.scrollable.virtual) {
|
|
content = this.virtualScrollable.verticalScrollbar;
|
|
}
|
|
var scrollTop = content.scrollTop(), delta = kendo.wheelDeltaY(e);
|
|
if (delta) {
|
|
e.preventDefault();
|
|
$(e.currentTarget).one('wheel' + NS, false);
|
|
content.scrollTop(scrollTop + -delta);
|
|
}
|
|
},
|
|
_isLocked: function () {
|
|
return this.lockedHeader != null;
|
|
},
|
|
_updateHeaderCols: function () {
|
|
var table = this.thead.parent().add(this.table);
|
|
if (this._isLocked()) {
|
|
normalizeCols(table, visibleLeafColumns(visibleNonLockedColumns(this.columns)), this._hasDetails(), 0);
|
|
} else {
|
|
normalizeCols(table, visibleLeafColumns(visibleColumns(this.columns)), this._hasDetails(), 0);
|
|
}
|
|
},
|
|
_updateCols: function (table) {
|
|
table = table || this.thead.parent().add(this.table);
|
|
this._appendCols(table, this._isLocked());
|
|
},
|
|
_updateLockedCols: function (table) {
|
|
if (this._isLocked()) {
|
|
table = table || this.lockedHeader.find('table').add(this.lockedTable);
|
|
normalizeCols(table, visibleLeafColumns(visibleLockedColumns(this.columns)), this._hasDetails(), this._groups());
|
|
}
|
|
},
|
|
_appendCols: function (table, locked) {
|
|
if (locked) {
|
|
normalizeCols(table, visibleLeafColumns(visibleNonLockedColumns(this.columns)), this._hasDetails(), 0);
|
|
} else {
|
|
normalizeCols(table, visibleLeafColumns(visibleColumns(this.columns)), this._hasDetails(), this._groups());
|
|
}
|
|
},
|
|
_autoColumns: function (schema) {
|
|
if (schema && schema.toJSON) {
|
|
var that = this, field, encoded;
|
|
schema = schema.toJSON();
|
|
encoded = !(that.table.find('tbody tr').length > 0 && (!that.dataSource || !that.dataSource.transport));
|
|
for (field in schema) {
|
|
that.columns.push({
|
|
field: field,
|
|
encoded: encoded,
|
|
headerAttributes: { id: kendo.guid() }
|
|
});
|
|
}
|
|
that._thead();
|
|
that._templates();
|
|
}
|
|
},
|
|
_rowsHtml: function (data, templates) {
|
|
var that = this, html = '', idx, rowTemplate = templates.rowTemplate, altRowTemplate = templates.altRowTemplate, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (idx % 2) {
|
|
html += altRowTemplate(data[idx]);
|
|
} else {
|
|
html += rowTemplate(data[idx]);
|
|
}
|
|
that._data.push(data[idx]);
|
|
}
|
|
return html;
|
|
},
|
|
_groupRowHtml: function (group, colspan, level, groupHeaderBuilder, templates, skipColspan) {
|
|
var that = this, html = '', idx, length, field = group.field, column = grep(leafColumns(that.columns), function (column) {
|
|
return column.field == field;
|
|
})[0] || {}, template = column.groupHeaderTemplate, text = (column.title || field) + ': ' + formatGroupValue(group.value, column.format, column.values, column.encoded), footerDefaults = that._groupAggregatesDefaultObject || {}, aggregates = extend({}, footerDefaults, group.aggregates), data = extend({}, {
|
|
field: group.field,
|
|
value: group.value,
|
|
aggregates: aggregates
|
|
}, group.aggregates[group.field]), groupFooterTemplate = templates.groupFooterTemplate, groupItems = group.items;
|
|
if (template) {
|
|
text = typeof template === FUNCTION ? template(data) : kendo.template(template)(data);
|
|
}
|
|
html += groupHeaderBuilder(colspan, level, text);
|
|
if (group.hasSubgroups) {
|
|
for (idx = 0, length = groupItems.length; idx < length; idx++) {
|
|
html += that._groupRowHtml(groupItems[idx], skipColspan ? colspan : colspan - 1, level + 1, groupHeaderBuilder, templates, skipColspan);
|
|
}
|
|
} else {
|
|
html += that._rowsHtml(groupItems, templates);
|
|
}
|
|
if (groupFooterTemplate) {
|
|
html += groupFooterTemplate(aggregates);
|
|
}
|
|
return html;
|
|
},
|
|
collapseGroup: function (group) {
|
|
group = $(group);
|
|
var level, groupable = this.options.groupable, showFooter = groupable.showFooter, footerCount = showFooter ? 0 : 1, offset, relatedGroup = $(), idx, length, tr;
|
|
if (this._isLocked()) {
|
|
if (!group.closest('div').hasClass('k-grid-content-locked')) {
|
|
relatedGroup = group.nextAll('tr');
|
|
group = this.lockedTable.find('>tbody>tr:eq(' + group.index() + ')');
|
|
} else {
|
|
relatedGroup = this.tbody.children('tr:eq(' + group.index() + ')').nextAll('tr');
|
|
}
|
|
}
|
|
level = group.find('.k-group-cell').length;
|
|
group.find('.k-i-collapse').addClass('k-i-expand').removeClass('k-i-collapse');
|
|
group.find('td[aria-expanded=\'true\']:first').attr('aria-expanded', false);
|
|
group = group.nextAll('tr');
|
|
var toHide = [];
|
|
for (idx = 0, length = group.length; idx < length; idx++) {
|
|
tr = group.eq(idx);
|
|
offset = tr.find('.k-group-cell').length;
|
|
if (tr.hasClass('k-grouping-row')) {
|
|
footerCount++;
|
|
} else if (tr.hasClass('k-group-footer')) {
|
|
footerCount--;
|
|
}
|
|
if (offset <= level || tr.hasClass('k-group-footer') && footerCount < 0) {
|
|
break;
|
|
}
|
|
if (relatedGroup.length) {
|
|
toHide.push(relatedGroup[idx]);
|
|
}
|
|
toHide.push(tr[0]);
|
|
}
|
|
$(toHide).hide();
|
|
},
|
|
expandGroup: function (group) {
|
|
group = $(group);
|
|
var that = this, showFooter = that.options.groupable.showFooter, level, tr, offset, relatedGroup = $(), idx, length, footersVisibility = [], groupsCount = 1;
|
|
if (this._isLocked()) {
|
|
if (!group.closest('div').hasClass('k-grid-content-locked')) {
|
|
relatedGroup = group.nextAll('tr');
|
|
group = this.lockedTable.find('>tbody>tr:eq(' + group.index() + ')');
|
|
} else {
|
|
relatedGroup = this.tbody.children('tr:eq(' + group.index() + ')').nextAll('tr');
|
|
}
|
|
}
|
|
level = group.find('.k-group-cell').length;
|
|
group.find('.k-i-expand').addClass('k-i-collapse').removeClass('k-i-expand');
|
|
group.find('td[aria-expanded=\'false\']:first').attr('aria-expanded', true);
|
|
group = group.nextAll('tr');
|
|
for (idx = 0, length = group.length; idx < length; idx++) {
|
|
tr = group.eq(idx);
|
|
offset = tr.find('.k-group-cell').length;
|
|
if (offset <= level) {
|
|
break;
|
|
}
|
|
if (offset == level + 1 && !tr.hasClass('k-detail-row')) {
|
|
tr.show();
|
|
relatedGroup.eq(idx).show();
|
|
if (tr.hasClass('k-grouping-row') && tr.find('.k-icon').hasClass('k-i-collapse')) {
|
|
that.expandGroup(tr);
|
|
}
|
|
if (tr.hasClass('k-master-row') && tr.find('.k-icon').hasClass('k-minus')) {
|
|
tr.next().show();
|
|
relatedGroup.eq(idx + 1).show();
|
|
}
|
|
}
|
|
if (tr.hasClass('k-grouping-row')) {
|
|
if (showFooter) {
|
|
footersVisibility.push(tr.is(':visible'));
|
|
}
|
|
groupsCount++;
|
|
}
|
|
if (tr.hasClass('k-group-footer')) {
|
|
if (showFooter) {
|
|
tr.toggle(footersVisibility.pop());
|
|
}
|
|
if (groupsCount == 1) {
|
|
tr.show();
|
|
relatedGroup.eq(idx).show();
|
|
} else {
|
|
groupsCount--;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_updateHeader: function (groups) {
|
|
var that = this, container = that._isLocked() ? that.lockedHeader.find('thead') : that.thead, filterCells = container.find('tr.k-filter-row').find('th.k-group-cell').length, length = container.find('tr:first').find('th.k-group-cell').length, rows = container.children('tr:not(:first)').filter(function () {
|
|
return !$(this).children(':visible').length;
|
|
});
|
|
if (groups > length) {
|
|
$(new Array(groups - length + 1).join('<th class="k-group-cell k-header" scope="col"> </th>')).prependTo(container.children('tr:not(.k-filter-row)'));
|
|
if (that.element.is(':visible')) {
|
|
rows.find('th.k-group-cell').hide();
|
|
}
|
|
} else if (groups < length) {
|
|
container.find('tr').each(function () {
|
|
$(this).find('th.k-group-cell').filter(':eq(' + groups + '),' + ':gt(' + groups + ')').remove();
|
|
});
|
|
}
|
|
if (groups > filterCells) {
|
|
$(new Array(groups - filterCells + 1).join('<th class="k-group-cell k-header" scope="col"> </th>')).prependTo(container.find('.k-filter-row'));
|
|
}
|
|
},
|
|
_firstDataItem: function (data, grouped) {
|
|
if (data && grouped) {
|
|
if (data.hasSubgroups) {
|
|
data = this._firstDataItem(data.items[0], grouped);
|
|
} else {
|
|
data = data.items[0];
|
|
}
|
|
}
|
|
return data;
|
|
},
|
|
_updateTablesWidth: function () {
|
|
var that = this, tables;
|
|
if (!that._isLocked()) {
|
|
return;
|
|
}
|
|
tables = $('>.k-grid-footer>.k-grid-footer-wrap>table', that.wrapper).add(that.thead.parent()).add(that.table);
|
|
that._footerWidth = tableWidth(tables.eq(0));
|
|
tables.width(that._footerWidth);
|
|
tables = $('>.k-grid-footer>.k-grid-footer-locked>table', that.wrapper).add(that.lockedHeader.find('>table')).add(that.lockedTable);
|
|
tables.width(tableWidth(tables.eq(0)));
|
|
},
|
|
hideColumn: function (column) {
|
|
var that = this, cell, tables, idx, cols, colWidth, position, width = 0, headerCellIndex, length, footer = that.footer || that.wrapper.find('.k-grid-footer'), columns = that.columns, visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find('>table>thead')).filter(isCellVisible).length : 0, columnIndex;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else if (isPlainObject(column)) {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item === column;
|
|
})[0];
|
|
} else {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || !isVisible(column)) {
|
|
return;
|
|
}
|
|
if (column.columns && column.columns.length) {
|
|
position = columnVisiblePosition(column, columns);
|
|
setColumnVisibility(column, false);
|
|
setCellVisibility(elements($('>table>thead', that.lockedHeader), that.thead, '>tr:eq(' + position.row + ')>th'), position.cell, false);
|
|
for (idx = 0; idx < column.columns.length; idx++) {
|
|
this.hideColumn(column.columns[idx]);
|
|
}
|
|
that.trigger(COLUMNHIDE, { column: column });
|
|
return;
|
|
}
|
|
columnIndex = inArray(column, visibleColumns(leafColumns(columns)));
|
|
setColumnVisibility(column, false);
|
|
that._setParentsVisibility(column, false);
|
|
that._templates();
|
|
that._updateCols();
|
|
that._updateLockedCols();
|
|
var container = that.thead;
|
|
headerCellIndex = columnIndex;
|
|
if (that.lockedHeader && visibleLocked > columnIndex) {
|
|
container = that.lockedHeader.find('>table>thead');
|
|
} else {
|
|
headerCellIndex -= visibleLocked;
|
|
}
|
|
cell = leafDataCells(container).filter(isCellVisible).eq(headerCellIndex);
|
|
cell[0].style.display = 'none';
|
|
setCellVisibility(elements($('>table>thead', that.lockedHeader), that.thead, '>tr.k-filter-row>th'), columnIndex, false);
|
|
if (footer[0]) {
|
|
that._updateCols(footer.find('>.k-grid-footer-wrap>table'));
|
|
that._updateLockedCols(footer.find('>.k-grid-footer-locked>table'));
|
|
setCellVisibility(footer.find('.k-footer-template>td'), columnIndex, false);
|
|
}
|
|
if (that.lockedTable && visibleLocked > columnIndex) {
|
|
hideColumnCells(that.lockedTable.find('>tbody>tr'), columnIndex);
|
|
} else {
|
|
hideColumnCells(that.tbody.children(), columnIndex - visibleLocked);
|
|
}
|
|
if (that.lockedTable) {
|
|
that._updateTablesWidth();
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
that._syncLockedFooterHeight();
|
|
} else {
|
|
cols = that.thead.prev().find('col');
|
|
for (idx = 0, length = cols.length; idx < length; idx += 1) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
width += parseInt(colWidth, 10);
|
|
} else {
|
|
width = 0;
|
|
break;
|
|
}
|
|
}
|
|
tables = $('>.k-grid-header table:first,>.k-grid-footer table:first', that.wrapper).add(that.table);
|
|
that._footerWidth = null;
|
|
if (width) {
|
|
tables.each(function () {
|
|
this.style.width = width + 'px';
|
|
});
|
|
that._footerWidth = width;
|
|
}
|
|
if (browser.msie && browser.version == 8) {
|
|
tables.css('display', 'inline-table');
|
|
setTimeout(function () {
|
|
tables.css('display', 'table');
|
|
}, 1);
|
|
}
|
|
}
|
|
that._updateFirstColumnClass();
|
|
that.trigger(COLUMNHIDE, { column: column });
|
|
},
|
|
_setParentsVisibility: function (column, visible) {
|
|
var columns = this.columns;
|
|
var idx;
|
|
var parents = [];
|
|
var parent;
|
|
var position;
|
|
var cell;
|
|
var colSpan;
|
|
var predicate = visible ? function (p) {
|
|
return visibleColumns(p.columns).length && p.hidden;
|
|
} : function (p) {
|
|
return !visibleColumns(p.columns).length && !p.hidden;
|
|
};
|
|
if (columnParents(column, columns, parents) && parents.length) {
|
|
for (idx = parents.length - 1; idx >= 0; idx--) {
|
|
parent = parents[idx];
|
|
position = columnPosition(parent, columns);
|
|
cell = elements($('>table>thead', this.lockedHeader), this.thead, '>tr:eq(' + position.row + ')>th:not(.k-group-cell):not(.k-hierarchy-cell)').eq(position.cell);
|
|
if (predicate(parent)) {
|
|
setColumnVisibility(parent, visible);
|
|
cell[0].style.display = visible ? '' : 'none';
|
|
}
|
|
if (cell.filter('[' + kendo.attr('colspan') + ']').length) {
|
|
colSpan = parseInt(cell.attr(kendo.attr('colspan')), 10);
|
|
cell[0].colSpan = colSpan - hiddenLeafColumnsCount(parent.columns) || 1;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
showColumn: function (column) {
|
|
var that = this, idx, length, cell, tables, width, headerCellIndex, position, colWidth, cols, columns = that.columns, footer = that.footer || that.wrapper.find('.k-grid-footer'), lockedColumnsCount = that.lockedHeader ? leafDataCells(that.lockedHeader.find('>table>thead')).length : 0, columnIndex;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else if (isPlainObject(column)) {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item === column;
|
|
})[0];
|
|
} else {
|
|
column = grep(flatColumns(columns), function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || isVisible(column)) {
|
|
return;
|
|
}
|
|
if (column.columns && column.columns.length) {
|
|
position = columnPosition(column, columns);
|
|
setColumnVisibility(column, true);
|
|
setCellVisibility(elements($('>table>thead', that.lockedHeader), that.thead, '>tr:eq(' + position.row + ')>th'), position.cell, true);
|
|
for (idx = 0; idx < column.columns.length; idx++) {
|
|
this.showColumn(column.columns[idx]);
|
|
}
|
|
that.trigger(COLUMNSHOW, { column: column });
|
|
return;
|
|
}
|
|
columnIndex = inArray(column, leafColumns(columns));
|
|
setColumnVisibility(column, true);
|
|
that._setParentsVisibility(column, true);
|
|
that._templates();
|
|
that._updateCols();
|
|
that._updateLockedCols();
|
|
var container = that.thead;
|
|
headerCellIndex = columnIndex;
|
|
if (that.lockedHeader && lockedColumnsCount > columnIndex) {
|
|
container = that.lockedHeader.find('>table>thead');
|
|
} else {
|
|
headerCellIndex -= lockedColumnsCount;
|
|
}
|
|
cell = leafDataCells(container).eq(headerCellIndex);
|
|
cell[0].style.display = '';
|
|
setCellVisibility(elements($('>table>thead', that.lockedHeader), that.thead, '>tr.k-filter-row>th'), columnIndex, true);
|
|
if (footer[0]) {
|
|
that._updateCols(footer.find('>.k-grid-footer-wrap>table'));
|
|
that._updateLockedCols(footer.find('>.k-grid-footer-locked>table'));
|
|
setCellVisibility(footer.find('.k-footer-template>td'), columnIndex, true);
|
|
}
|
|
if (that.lockedTable && lockedColumnsCount > columnIndex) {
|
|
showColumnCells(that.lockedTable.find('>tbody>tr'), columnIndex);
|
|
} else {
|
|
showColumnCells(that.tbody.children(), columnIndex - lockedColumnsCount);
|
|
}
|
|
if (that.lockedTable) {
|
|
that._updateTablesWidth();
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
} else {
|
|
tables = $('>.k-grid-header table:first,>.k-grid-footer table:first', that.wrapper).add(that.table);
|
|
if (!column.width) {
|
|
tables.width('');
|
|
} else {
|
|
width = 0;
|
|
cols = that.thead.prev().find('col');
|
|
for (idx = 0, length = cols.length; idx < length; idx += 1) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth.indexOf('%') > -1) {
|
|
width = 0;
|
|
break;
|
|
}
|
|
width += parseInt(colWidth, 10);
|
|
}
|
|
that._footerWidth = null;
|
|
if (width) {
|
|
tables.each(function () {
|
|
this.style.width = width + 'px';
|
|
});
|
|
that._footerWidth = width;
|
|
}
|
|
}
|
|
}
|
|
that._updateFirstColumnClass();
|
|
that.trigger(COLUMNSHOW, { column: column });
|
|
},
|
|
_progress: function (toggle) {
|
|
var element = this.element;
|
|
if (this.lockedContent) {
|
|
element = this.wrapper;
|
|
} else if (this.element.is('table')) {
|
|
element = this.element.parent();
|
|
} else if (this.content && this.content.length) {
|
|
element = this.content;
|
|
}
|
|
kendo.ui.progress(element, toggle);
|
|
},
|
|
_resize: function (size, force) {
|
|
this._syncLockedHeaderHeight();
|
|
if (this.content) {
|
|
this._setContentWidth();
|
|
this._setContentHeight();
|
|
}
|
|
if (this.virtualScrollable && (force || this._rowHeight)) {
|
|
if (force) {
|
|
this._rowHeight = null;
|
|
}
|
|
this.virtualScrollable.repaintScrollbar();
|
|
}
|
|
},
|
|
_isActiveInTable: function () {
|
|
var active = activeElement();
|
|
if (!active) {
|
|
return false;
|
|
}
|
|
return this.table[0] === active || $.contains(this.table[0], active) || this._isLocked() && (this.lockedTable[0] === active || $.contains(this.lockedTable[0], active));
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, data = that.dataSource.view(), navigatable = that.options.navigatable, currentIndex, current = $(that.current()), isCurrentInHeader = false, groups = (that.dataSource.group() || []).length, offsetLeft = that.content && that.content.scrollLeft(), colspan = groups + visibleLeafColumns(visibleColumns(that.columns)).length;
|
|
if (e && e.action === 'itemchange' && that.editable) {
|
|
return;
|
|
}
|
|
e = e || {};
|
|
if (that.trigger('dataBinding', {
|
|
action: e.action || 'rebind',
|
|
index: e.index,
|
|
items: e.items
|
|
})) {
|
|
return;
|
|
}
|
|
that._angularItems('cleanup');
|
|
if (navigatable && (that._isActiveInTable() || that._editContainer && that._editContainer.data('kendoWindow'))) {
|
|
isCurrentInHeader = current.is('th');
|
|
currentIndex = Math.max(that.cellIndex(current), 0);
|
|
}
|
|
that._destroyEditable();
|
|
that._progress(false);
|
|
that._hideResizeHandle();
|
|
that._data = [];
|
|
if (!that.columns.length) {
|
|
that._autoColumns(that._firstDataItem(data[0], groups));
|
|
colspan = groups + that.columns.length;
|
|
}
|
|
that._group = groups > 0 || that._group;
|
|
if (that._group) {
|
|
that._templates();
|
|
that._updateCols();
|
|
that._updateLockedCols();
|
|
that._updateHeader(groups);
|
|
that._group = groups > 0;
|
|
}
|
|
that._renderContent(data, colspan, groups);
|
|
that._renderLockedContent(data, colspan, groups);
|
|
that._footer();
|
|
that._renderNoRecordsContent();
|
|
that._setContentHeight();
|
|
that._setContentWidth(offsetLeft);
|
|
if (that.lockedTable) {
|
|
if (that.options.scrollable.virtual) {
|
|
that.content.find('>.k-virtual-scrollable-wrap').trigger('scroll');
|
|
} else if (that.touchScroller) {
|
|
that.touchScroller.movable.trigger('change');
|
|
} else {
|
|
that.wrapper.one('scroll', function (e) {
|
|
e.stopPropagation();
|
|
});
|
|
that.content.trigger('scroll');
|
|
}
|
|
}
|
|
that._restoreCurrent(currentIndex, isCurrentInHeader);
|
|
if (that.touchScroller) {
|
|
that.touchScroller.contentResized();
|
|
}
|
|
if (that.selectable) {
|
|
that.selectable.resetTouchEvents();
|
|
}
|
|
that._muteAngularRebind(function () {
|
|
that._angularItems('compile');
|
|
});
|
|
that.trigger(DATABOUND);
|
|
},
|
|
_restoreCurrent: function (currentIndex, isCurrentInHeader) {
|
|
if (currentIndex === undefined || currentIndex < 0) {
|
|
return;
|
|
}
|
|
this._removeCurrent();
|
|
if (isCurrentInHeader) {
|
|
this._setCurrent(this.thead.find('th:not(.k-group-cell)').eq(currentIndex));
|
|
} else {
|
|
var rowIndex = 0;
|
|
if (this._rowVirtualIndex) {
|
|
rowIndex = this.virtualScrollable.position(this._rowVirtualIndex);
|
|
} else {
|
|
currentIndex = 0;
|
|
}
|
|
var row = $();
|
|
if (this.lockedTable) {
|
|
row = this.lockedTable.find('>tbody>tr').eq(rowIndex);
|
|
}
|
|
row = row.add(this.tbody.children().eq(rowIndex));
|
|
var td = row.find('>td:not(.k-group-cell):not(.k-hierarchy-cell)').eq(currentIndex);
|
|
this._setCurrent(td);
|
|
}
|
|
if (this._current) {
|
|
focusTable(this._current.closest('table')[0], true);
|
|
}
|
|
},
|
|
_angularItems: function (cmd) {
|
|
kendo.ui.DataBoundWidget.fn._angularItems.call(this, cmd);
|
|
if (cmd === 'cleanup') {
|
|
this._cleanupDetailItems();
|
|
}
|
|
this._angularGroupItems(cmd);
|
|
this._angularGroupFooterItems(cmd);
|
|
},
|
|
_cleanupDetailItems: function () {
|
|
var that = this;
|
|
if (that._hasDetails()) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.tbody.children('.k-detail-row') };
|
|
});
|
|
that.tbody.find('.k-detail-cell').empty();
|
|
}
|
|
},
|
|
_angularGroupItems: function (cmd) {
|
|
var that = this, container = that.tbody;
|
|
if (that.lockedContent) {
|
|
container = that.lockedTable.find('tbody');
|
|
}
|
|
if (that._group) {
|
|
that.angular(cmd, function () {
|
|
return {
|
|
elements: container.children('.k-grouping-row'),
|
|
data: $.map(groupRows(that.dataSource.view()), function (dataItem) {
|
|
return { dataItem: dataItem };
|
|
})
|
|
};
|
|
});
|
|
}
|
|
},
|
|
_angularGroupFooterItems: function (cmd) {
|
|
var that = this, container = that.tbody;
|
|
if (that.lockedContent) {
|
|
container = that.element;
|
|
}
|
|
if (that._group && that.groupFooterTemplate) {
|
|
that.angular(cmd, function () {
|
|
return {
|
|
elements: container.find('.k-group-footer'),
|
|
data: $.map(groupFooters(that.dataSource.view()), function (dataItem) {
|
|
return { dataItem: dataItem };
|
|
})
|
|
};
|
|
});
|
|
}
|
|
},
|
|
_renderContent: function (data, colspan, groups) {
|
|
var that = this, idx, length, html = '', isLocked = that.lockedContent != null, templates = {
|
|
rowTemplate: that.rowTemplate,
|
|
altRowTemplate: that.altRowTemplate,
|
|
groupFooterTemplate: that.groupFooterTemplate
|
|
};
|
|
colspan = isLocked ? colspan - visibleLeafColumns(visibleLockedColumns(that.columns)).length : colspan;
|
|
if (groups > 0) {
|
|
colspan = isLocked ? colspan - groups : colspan;
|
|
if (that.detailTemplate) {
|
|
colspan++;
|
|
}
|
|
if (that.groupFooterTemplate) {
|
|
that._groupAggregatesDefaultObject = that.dataSource.aggregates();
|
|
}
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
html += that._groupRowHtml(data[idx], colspan, 0, isLocked ? groupRowLockedContentBuilder : groupRowBuilder, templates, isLocked);
|
|
}
|
|
} else {
|
|
html += that._rowsHtml(data, templates);
|
|
}
|
|
that.tbody = appendContent(that.tbody, that.table, html, this.options.$angular);
|
|
},
|
|
_renderLockedContent: function (data, colspan, groups) {
|
|
var html = '', idx, length, templates = {
|
|
rowTemplate: this.lockedRowTemplate,
|
|
altRowTemplate: this.lockedAltRowTemplate,
|
|
groupFooterTemplate: this.lockedGroupFooterTemplate
|
|
};
|
|
if (this.lockedContent) {
|
|
var table = this.lockedTable;
|
|
if (groups > 0) {
|
|
colspan = colspan - visibleColumns(leafColumns(nonLockedColumns(this.columns))).length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
html += this._groupRowHtml(data[idx], colspan, 0, groupRowBuilder, templates);
|
|
}
|
|
} else {
|
|
html = this._rowsHtml(data, templates);
|
|
}
|
|
appendContent(table.children('tbody'), table, html, this.options.$angular);
|
|
this._syncLockedContentHeight();
|
|
}
|
|
},
|
|
_adjustRowsHeight: function (table1, table2) {
|
|
var rows = table1[0].rows, length = rows.length, idx, rows2 = table2[0].rows, containers = table1.add(table2), containersLength = containers.length, heights = [];
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (!rows2[idx]) {
|
|
break;
|
|
}
|
|
if (rows[idx].style.height) {
|
|
rows[idx].style.height = rows2[idx].style.height = '';
|
|
}
|
|
var offsetHeight1 = rows[idx].offsetHeight;
|
|
var offsetHeight2 = rows2[idx].offsetHeight;
|
|
var height = 0;
|
|
if (offsetHeight1 > offsetHeight2) {
|
|
height = offsetHeight1;
|
|
} else if (offsetHeight1 < offsetHeight2) {
|
|
height = offsetHeight2;
|
|
}
|
|
heights.push(height);
|
|
}
|
|
for (idx = 0; idx < containersLength; idx++) {
|
|
containers[idx].style.display = 'none';
|
|
}
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (heights[idx]) {
|
|
rows[idx].style.height = rows2[idx].style.height = heights[idx] + 1 + 'px';
|
|
}
|
|
}
|
|
for (idx = 0; idx < containersLength; idx++) {
|
|
containers[idx].style.display = '';
|
|
}
|
|
}
|
|
});
|
|
if (kendo.ExcelMixin) {
|
|
kendo.ExcelMixin.extend(Grid.prototype);
|
|
}
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Grid.prototype);
|
|
Grid.prototype._drawPDF = function (progress) {
|
|
var result = new $.Deferred();
|
|
var grid = this;
|
|
var dataSource = grid.dataSource;
|
|
var allPages = grid.options.pdf.allPages;
|
|
this._initPDFProgress(progress);
|
|
var doc = new kendo.drawing.Group();
|
|
var startingPage = dataSource.page();
|
|
function resolve() {
|
|
if (allPages && startingPage !== undefined) {
|
|
dataSource.unbind('change', exportPage);
|
|
dataSource.one('change', function () {
|
|
result.resolve(doc);
|
|
});
|
|
dataSource.page(startingPage);
|
|
} else {
|
|
result.resolve(doc);
|
|
}
|
|
}
|
|
function exportPage() {
|
|
grid._drawPDFShadow({ width: grid.wrapper.width() }, { avoidLinks: grid.options.pdf.avoidLinks }).done(function (group) {
|
|
var pageNum = dataSource.page();
|
|
var totalPages = allPages ? dataSource.totalPages() : 1;
|
|
var args = {
|
|
page: group,
|
|
pageNumber: pageNum,
|
|
progress: pageNum / totalPages,
|
|
totalPages: totalPages
|
|
};
|
|
progress.notify(args);
|
|
doc.append(args.page);
|
|
if (pageNum < totalPages) {
|
|
dataSource.page(pageNum + 1);
|
|
} else {
|
|
resolve();
|
|
}
|
|
}).fail(function (err) {
|
|
result.reject(err);
|
|
});
|
|
}
|
|
if (allPages) {
|
|
dataSource.bind('change', exportPage);
|
|
dataSource.page(1);
|
|
} else {
|
|
exportPage();
|
|
}
|
|
return result.promise();
|
|
};
|
|
Grid.prototype._initPDFProgress = function (deferred) {
|
|
var loading = $('<div class=\'k-loading-pdf-mask\'><div class=\'k-loading-color\'/></div>');
|
|
loading.prepend(this.wrapper.clone().css({
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0
|
|
}));
|
|
this.wrapper.append(loading);
|
|
var pb = $('<div class=\'k-loading-pdf-progress\'>').appendTo(loading).kendoProgressBar({
|
|
type: 'chunk',
|
|
chunkCount: 10,
|
|
min: 0,
|
|
max: 1,
|
|
value: 0
|
|
}).data('kendoProgressBar');
|
|
deferred.progress(function (e) {
|
|
pb.value(e.progress);
|
|
}).always(function () {
|
|
kendo.destroy(loading);
|
|
loading.remove();
|
|
});
|
|
};
|
|
}
|
|
function syncTableHeight(table1, table2) {
|
|
table1 = table1[0];
|
|
table2 = table2[0];
|
|
if (table1.rows.length !== table2.rows.length) {
|
|
var lockedHeigth = table1.offsetHeight;
|
|
var tableHeigth = table2.offsetHeight;
|
|
var row;
|
|
var diff;
|
|
if (lockedHeigth > tableHeigth) {
|
|
row = table2.rows[table2.rows.length - 1];
|
|
if (filterRowRegExp.test(row.className)) {
|
|
row = table2.rows[table2.rows.length - 2];
|
|
}
|
|
diff = lockedHeigth - tableHeigth;
|
|
} else {
|
|
row = table1.rows[table1.rows.length - 1];
|
|
if (filterRowRegExp.test(row.className)) {
|
|
row = table1.rows[table1.rows.length - 2];
|
|
}
|
|
diff = tableHeigth - lockedHeigth;
|
|
}
|
|
row.style.height = row.offsetHeight + diff + 'px';
|
|
}
|
|
}
|
|
function adjustRowHeight(row1, row2) {
|
|
var height;
|
|
var offsetHeight1 = row1.offsetHeight;
|
|
var offsetHeight2 = row2.offsetHeight;
|
|
if (offsetHeight1 > offsetHeight2) {
|
|
height = offsetHeight1 + 'px';
|
|
} else if (offsetHeight1 < offsetHeight2) {
|
|
height = offsetHeight2 + 'px';
|
|
}
|
|
if (height) {
|
|
row1.style.height = row2.style.height = height;
|
|
}
|
|
}
|
|
function getCommand(commands, name) {
|
|
var idx, length, command;
|
|
if (typeof commands === STRING && commands === name) {
|
|
return commands;
|
|
}
|
|
if (isPlainObject(commands) && commands.name === name) {
|
|
return commands;
|
|
}
|
|
if (isArray(commands)) {
|
|
for (idx = 0, length = commands.length; idx < length; idx++) {
|
|
command = commands[idx];
|
|
if (typeof command === STRING && command === name || command.name === name) {
|
|
return command;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function focusTable(table, direct) {
|
|
var msie = browser.msie || browser.edge;
|
|
if (direct === true) {
|
|
table = $(table);
|
|
var scrollTop, scrollLeft;
|
|
scrollTop = table.parent().scrollTop();
|
|
scrollLeft = table.parent().scrollLeft();
|
|
if (msie) {
|
|
try {
|
|
table[0].setActive();
|
|
} catch (e) {
|
|
table[0].focus();
|
|
}
|
|
} else {
|
|
table[0].focus();
|
|
}
|
|
table.parent().scrollTop(scrollTop).scrollLeft(scrollLeft);
|
|
} else {
|
|
$(table).one('focusin', function (e) {
|
|
e.preventDefault();
|
|
}).focus();
|
|
}
|
|
}
|
|
function isInputElement(element) {
|
|
return $(element).is(':button,a,:input,a>.k-icon,textarea,span.k-select,span.k-icon,span.k-link,.k-input,.k-multiselect-wrap,.k-tool-icon');
|
|
}
|
|
function tableClick(e) {
|
|
var currentTarget = $(e.currentTarget), isHeader = currentTarget.is('th'), table = this.table.add(this.lockedTable), headerTable = this.thead.parent().add($('>table', this.lockedHeader)), isInput = isInputElement(e.target), currentTable = currentTarget.closest('table')[0];
|
|
if (kendo.support.touch) {
|
|
return;
|
|
}
|
|
if (isInput && currentTarget.find(kendo.roleSelector('filtercell')).length) {
|
|
this._setCurrent(currentTarget);
|
|
return;
|
|
}
|
|
if (currentTable !== table[0] && currentTable !== table[1] && currentTable !== headerTable[0] && currentTable !== headerTable[1]) {
|
|
return;
|
|
}
|
|
if ($(e.target).is('a.k-i-collapse, a.k-i-expand')) {
|
|
return;
|
|
}
|
|
if (this.options.navigatable) {
|
|
this._setCurrent(currentTarget);
|
|
}
|
|
if (isHeader || !isInput) {
|
|
setTimeout(function () {
|
|
if (!(isIE8 && $(kendo._activeElement()).hasClass('k-widget'))) {
|
|
if (!isInputElement(kendo._activeElement())) {
|
|
focusTable(currentTable, true);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (isHeader) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
function isInEdit(cell) {
|
|
return cell && (cell.hasClass('k-edit-cell') || cell.parent().hasClass('k-grid-edit-row'));
|
|
}
|
|
function groupRowBuilder(colspan, level, text) {
|
|
return '<tr role="row" class="k-grouping-row">' + groupCells(level) + '<td colspan="' + colspan + '" aria-expanded="true">' + '<p class="k-reset">' + '<a class="k-icon k-i-collapse" href="#" tabindex="-1"></a>' + text + '</p></td></tr>';
|
|
}
|
|
function groupRowLockedContentBuilder(colspan) {
|
|
return '<tr role="row" class="k-grouping-row">' + '<td colspan="' + colspan + '" aria-expanded="true">' + '<p class="k-reset"> </p></td></tr>';
|
|
}
|
|
ui.plugin(Grid);
|
|
ui.plugin(VirtualScrollable);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.listview', [
|
|
'kendo.data',
|
|
'kendo.editable',
|
|
'kendo.selectable'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'listview',
|
|
name: 'ListView',
|
|
category: 'web',
|
|
description: 'The ListView widget offers rich support for interacting with data.',
|
|
depends: ['data'],
|
|
features: [
|
|
{
|
|
id: 'listview-editing',
|
|
name: 'Editing',
|
|
description: 'Support for record editing',
|
|
depends: ['editable']
|
|
},
|
|
{
|
|
id: 'listview-selection',
|
|
name: 'Selection',
|
|
description: 'Support for selection',
|
|
depends: ['selectable']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, CHANGE = 'change', CANCEL = 'cancel', DATABOUND = 'dataBound', DATABINDING = 'dataBinding', Widget = kendo.ui.Widget, keys = kendo.keys, FOCUSSELECTOR = '>*:not(.k-loading-mask)', PROGRESS = 'progress', ERROR = 'error', FOCUSED = 'k-state-focused', SELECTED = 'k-state-selected', KEDITITEM = 'k-edit-item', EDIT = 'edit', REMOVE = 'remove', SAVE = 'save', CLICK = 'click', NS = '.kendoListView', proxy = $.proxy, activeElement = kendo._activeElement, progress = kendo.ui.progress, DataSource = kendo.data.DataSource;
|
|
var ListView = kendo.ui.DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = $.isArray(options) ? { dataSource: options } : options;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
that.wrapper = element = that.element;
|
|
if (element[0].id) {
|
|
that._itemId = element[0].id + '_lv_active';
|
|
}
|
|
that._element();
|
|
that._dataSource();
|
|
that._templates();
|
|
that._navigatable();
|
|
that._selectable();
|
|
that._pageable();
|
|
that._crudHandlers();
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
CANCEL,
|
|
DATABINDING,
|
|
DATABOUND,
|
|
EDIT,
|
|
REMOVE,
|
|
SAVE
|
|
],
|
|
options: {
|
|
name: 'ListView',
|
|
autoBind: true,
|
|
selectable: false,
|
|
navigatable: false,
|
|
template: '',
|
|
altTemplate: '',
|
|
editTemplate: ''
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
this._templates();
|
|
if (this.selectable) {
|
|
this.selectable.destroy();
|
|
this.selectable = null;
|
|
}
|
|
this._selectable();
|
|
},
|
|
_templates: function () {
|
|
var options = this.options;
|
|
this.template = kendo.template(options.template || '');
|
|
this.altTemplate = kendo.template(options.altTemplate || options.template);
|
|
this.editTemplate = kendo.template(options.editTemplate || '');
|
|
},
|
|
_item: function (action) {
|
|
return this.element.children()[action]();
|
|
},
|
|
items: function () {
|
|
return this.element.children();
|
|
},
|
|
dataItem: function (element) {
|
|
var attr = kendo.attr('uid');
|
|
var uid = $(element).closest('[' + attr + ']').attr(attr);
|
|
return this.dataSource.getByUid(uid);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (this.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
_unbindDataSource: function () {
|
|
var that = this;
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
|
|
},
|
|
_dataSource: function () {
|
|
var that = this;
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that._unbindDataSource();
|
|
} else {
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that._progressHandler = proxy(that._progress, that);
|
|
that._errorHandler = proxy(that._error, that);
|
|
}
|
|
that.dataSource = DataSource.create(that.options.dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(ERROR, that._errorHandler);
|
|
},
|
|
_progress: function () {
|
|
progress(this.element, true);
|
|
},
|
|
_error: function () {
|
|
progress(this.element, false);
|
|
},
|
|
_element: function () {
|
|
this.element.addClass('k-widget k-listview').attr('role', 'listbox');
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, view = that.dataSource.view(), data, items, item, html = '', idx, length, template = that.template, altTemplate = that.altTemplate, active = activeElement();
|
|
e = e || {};
|
|
if (e.action === 'itemchange') {
|
|
if (!that._hasBindingTarget() && !that.editable) {
|
|
data = e.items[0];
|
|
item = that.items().filter('[' + kendo.attr('uid') + '=' + data.uid + ']');
|
|
if (item.length > 0) {
|
|
idx = item.index();
|
|
that.angular('cleanup', function () {
|
|
return { elements: [item] };
|
|
});
|
|
item.replaceWith(template(data));
|
|
item = that.items().eq(idx);
|
|
item.attr(kendo.attr('uid'), data.uid);
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: [item],
|
|
data: [{ dataItem: data }]
|
|
};
|
|
});
|
|
that.trigger('itemChange', {
|
|
item: item,
|
|
data: data
|
|
});
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (that.trigger(DATABINDING, {
|
|
action: e.action || 'rebind',
|
|
items: e.items,
|
|
index: e.index
|
|
})) {
|
|
return;
|
|
}
|
|
that._angularItems('cleanup');
|
|
that._destroyEditable();
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
if (idx % 2) {
|
|
html += altTemplate(view[idx]);
|
|
} else {
|
|
html += template(view[idx]);
|
|
}
|
|
}
|
|
that.element.html(html);
|
|
items = that.items();
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
items.eq(idx).attr(kendo.attr('uid'), view[idx].uid).attr('role', 'option').attr('aria-selected', 'false');
|
|
}
|
|
if (that.element[0] === active && that.options.navigatable) {
|
|
that.current(items.eq(0));
|
|
}
|
|
that._angularItems('compile');
|
|
that.trigger(DATABOUND);
|
|
},
|
|
_pageable: function () {
|
|
var that = this, pageable = that.options.pageable, settings, pagerId;
|
|
if ($.isPlainObject(pageable)) {
|
|
pagerId = pageable.pagerId;
|
|
settings = $.extend({}, pageable, {
|
|
dataSource: that.dataSource,
|
|
pagerId: null
|
|
});
|
|
that.pager = new kendo.ui.Pager($('#' + pagerId), settings);
|
|
}
|
|
},
|
|
_selectable: function () {
|
|
var that = this, multi, current, selectable = that.options.selectable, navigatable = that.options.navigatable;
|
|
if (selectable) {
|
|
multi = kendo.ui.Selectable.parseOptions(selectable).multiple;
|
|
that.selectable = new kendo.ui.Selectable(that.element, {
|
|
aria: true,
|
|
multiple: multi,
|
|
filter: FOCUSSELECTOR,
|
|
change: function () {
|
|
that.trigger(CHANGE);
|
|
}
|
|
});
|
|
if (navigatable) {
|
|
that.element.on('keydown' + NS, function (e) {
|
|
if (e.keyCode === keys.SPACEBAR) {
|
|
current = that.current();
|
|
if (e.target == e.currentTarget) {
|
|
e.preventDefault();
|
|
}
|
|
if (multi) {
|
|
if (!e.ctrlKey) {
|
|
that.selectable.clear();
|
|
} else {
|
|
if (current && current.hasClass(SELECTED)) {
|
|
current.removeClass(SELECTED);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
that.selectable.clear();
|
|
}
|
|
that.selectable.value(current);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
current: function (candidate) {
|
|
var that = this, element = that.element, current = that._current, id = that._itemId;
|
|
if (candidate === undefined) {
|
|
return current;
|
|
}
|
|
if (current && current[0]) {
|
|
if (current[0].id === id) {
|
|
current.removeAttr('id');
|
|
}
|
|
current.removeClass(FOCUSED);
|
|
element.removeAttr('aria-activedescendant');
|
|
}
|
|
if (candidate && candidate[0]) {
|
|
id = candidate[0].id || id;
|
|
that._scrollTo(candidate[0]);
|
|
element.attr('aria-activedescendant', id);
|
|
candidate.addClass(FOCUSED).attr('id', id);
|
|
}
|
|
that._current = candidate;
|
|
},
|
|
_scrollTo: function (element) {
|
|
var that = this, container, UseJQueryoffset = false, SCROLL = 'scroll';
|
|
if (that.wrapper.css('overflow') == 'auto' || that.wrapper.css('overflow') == SCROLL) {
|
|
container = that.wrapper[0];
|
|
} else {
|
|
container = window;
|
|
UseJQueryoffset = true;
|
|
}
|
|
var scrollDirectionFunc = function (direction, dimension) {
|
|
var elementOffset = UseJQueryoffset ? $(element).offset()[direction.toLowerCase()] : element['offset' + direction], elementDimension = element['client' + dimension], containerScrollAmount = $(container)[SCROLL + direction](), containerDimension = $(container)[dimension.toLowerCase()]();
|
|
if (elementOffset + elementDimension > containerScrollAmount + containerDimension) {
|
|
$(container)[SCROLL + direction](elementOffset + elementDimension - containerDimension);
|
|
} else if (elementOffset < containerScrollAmount) {
|
|
$(container)[SCROLL + direction](elementOffset);
|
|
}
|
|
};
|
|
scrollDirectionFunc('Top', 'Height');
|
|
scrollDirectionFunc('Left', 'Width');
|
|
},
|
|
_navigatable: function () {
|
|
var that = this, navigatable = that.options.navigatable, element = that.element, clickCallback = function (e) {
|
|
that.current($(e.currentTarget));
|
|
if (!$(e.target).is(':button,a,:input,a>.k-icon,textarea')) {
|
|
element.focus();
|
|
}
|
|
};
|
|
if (navigatable) {
|
|
that._tabindex();
|
|
element.on('focus' + NS, function () {
|
|
var current = that._current;
|
|
if (!current || !current.is(':visible')) {
|
|
current = that._item('first');
|
|
}
|
|
that.current(current);
|
|
}).on('focusout' + NS, function () {
|
|
if (that._current) {
|
|
that._current.removeClass(FOCUSED);
|
|
}
|
|
}).on('keydown' + NS, function (e) {
|
|
var key = e.keyCode, current = that.current(), target = $(e.target), canHandle = !target.is(':button,textarea,a,a>.t-icon,input'), isTextBox = target.is(':text,:password'), preventDefault = kendo.preventDefault, editItem = element.find('.' + KEDITITEM), active = activeElement(), idx;
|
|
if (!canHandle && !isTextBox && keys.ESC != key || isTextBox && keys.ESC != key && keys.ENTER != key) {
|
|
return;
|
|
}
|
|
if (keys.UP === key || keys.LEFT === key) {
|
|
if (current) {
|
|
current = current.prev();
|
|
}
|
|
that.current(!current || !current[0] ? that._item('last') : current);
|
|
preventDefault(e);
|
|
} else if (keys.DOWN === key || keys.RIGHT === key) {
|
|
if (current) {
|
|
current = current.next();
|
|
}
|
|
that.current(!current || !current[0] ? that._item('first') : current);
|
|
preventDefault(e);
|
|
} else if (keys.PAGEUP === key) {
|
|
that.current(null);
|
|
that.dataSource.page(that.dataSource.page() - 1);
|
|
preventDefault(e);
|
|
} else if (keys.PAGEDOWN === key) {
|
|
that.current(null);
|
|
that.dataSource.page(that.dataSource.page() + 1);
|
|
preventDefault(e);
|
|
} else if (keys.HOME === key) {
|
|
that.current(that._item('first'));
|
|
preventDefault(e);
|
|
} else if (keys.END === key) {
|
|
that.current(that._item('last'));
|
|
preventDefault(e);
|
|
} else if (keys.ENTER === key) {
|
|
if (editItem.length !== 0 && (canHandle || isTextBox)) {
|
|
idx = that.items().index(editItem);
|
|
if (active) {
|
|
active.blur();
|
|
}
|
|
that.save();
|
|
var focusAgain = function () {
|
|
that.element.trigger('focus');
|
|
that.current(that.items().eq(idx));
|
|
};
|
|
that.one('dataBound', focusAgain);
|
|
} else if (that.options.editTemplate !== '') {
|
|
that.edit(current);
|
|
}
|
|
} else if (keys.ESC === key) {
|
|
editItem = element.find('.' + KEDITITEM);
|
|
if (editItem.length === 0) {
|
|
return;
|
|
}
|
|
idx = that.items().index(editItem);
|
|
that.cancel();
|
|
that.element.trigger('focus');
|
|
that.current(that.items().eq(idx));
|
|
}
|
|
});
|
|
element.on('mousedown' + NS + ' touchstart' + NS, FOCUSSELECTOR, proxy(clickCallback, that));
|
|
}
|
|
},
|
|
clearSelection: function () {
|
|
var that = this;
|
|
that.selectable.clear();
|
|
that.trigger(CHANGE);
|
|
},
|
|
select: function (items) {
|
|
var that = this, selectable = that.selectable;
|
|
items = $(items);
|
|
if (items.length) {
|
|
if (!selectable.options.multiple) {
|
|
selectable.clear();
|
|
items = items.first();
|
|
}
|
|
selectable.value(items);
|
|
return;
|
|
}
|
|
return selectable.value();
|
|
},
|
|
_destroyEditable: function () {
|
|
var that = this;
|
|
if (that.editable) {
|
|
that.editable.destroy();
|
|
delete that.editable;
|
|
}
|
|
},
|
|
_modelFromElement: function (element) {
|
|
var uid = element.attr(kendo.attr('uid'));
|
|
return this.dataSource.getByUid(uid);
|
|
},
|
|
_closeEditable: function () {
|
|
var that = this, editable = that.editable, data, item, index, template = that.template;
|
|
if (editable) {
|
|
if (editable.element.index() % 2) {
|
|
template = that.altTemplate;
|
|
}
|
|
that.angular('cleanup', function () {
|
|
return { elements: [editable.element] };
|
|
});
|
|
data = that._modelFromElement(editable.element);
|
|
that._destroyEditable();
|
|
index = editable.element.index();
|
|
editable.element.replaceWith(template(data));
|
|
item = that.items().eq(index);
|
|
item.attr(kendo.attr('uid'), data.uid);
|
|
if (that._hasBindingTarget()) {
|
|
kendo.bind(item, data);
|
|
}
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: [item],
|
|
data: [{ dataItem: data }]
|
|
};
|
|
});
|
|
}
|
|
return true;
|
|
},
|
|
edit: function (item) {
|
|
var that = this, data = that._modelFromElement(item), container, uid = data.uid, index;
|
|
that.cancel();
|
|
item = that.items().filter('[' + kendo.attr('uid') + '=' + uid + ']');
|
|
index = item.index();
|
|
item.replaceWith(that.editTemplate(data));
|
|
container = that.items().eq(index).addClass(KEDITITEM).attr(kendo.attr('uid'), data.uid);
|
|
that.editable = container.kendoEditable({
|
|
model: data,
|
|
clearContainer: false,
|
|
errorTemplate: false,
|
|
target: that
|
|
}).data('kendoEditable');
|
|
that.trigger(EDIT, {
|
|
model: data,
|
|
item: container
|
|
});
|
|
},
|
|
save: function () {
|
|
var that = this, editable = that.editable, model;
|
|
if (!editable) {
|
|
return;
|
|
}
|
|
var container = editable.element;
|
|
model = that._modelFromElement(container);
|
|
if (editable.end() && !that.trigger(SAVE, {
|
|
model: model,
|
|
item: container
|
|
})) {
|
|
that._closeEditable();
|
|
that.dataSource.sync();
|
|
}
|
|
},
|
|
remove: function (item) {
|
|
var that = this, dataSource = that.dataSource, data = that._modelFromElement(item);
|
|
if (that.editable) {
|
|
dataSource.cancelChanges(that._modelFromElement(that.editable.element));
|
|
that._closeEditable();
|
|
}
|
|
if (!that.trigger(REMOVE, {
|
|
model: data,
|
|
item: item
|
|
})) {
|
|
item.hide();
|
|
dataSource.remove(data);
|
|
dataSource.sync();
|
|
}
|
|
},
|
|
add: function () {
|
|
var that = this, dataItem, dataSource = that.dataSource, index = dataSource.indexOf((dataSource.view() || [])[0]);
|
|
if (index < 0) {
|
|
index = 0;
|
|
}
|
|
that.cancel();
|
|
dataItem = dataSource.insert(index, {});
|
|
that.edit(that.element.find('[data-uid=\'' + dataItem.uid + '\']'));
|
|
},
|
|
cancel: function () {
|
|
var that = this, dataSource = that.dataSource;
|
|
if (that.editable) {
|
|
var container = that.editable.element;
|
|
var model = that._modelFromElement(container);
|
|
if (!that.trigger(CANCEL, {
|
|
model: model,
|
|
container: container
|
|
})) {
|
|
dataSource.cancelChanges(model);
|
|
that._closeEditable();
|
|
}
|
|
}
|
|
},
|
|
_crudHandlers: function () {
|
|
var that = this, clickNS = CLICK + NS;
|
|
that.element.on(clickNS, '.k-edit-button', function (e) {
|
|
var item = $(this).closest('[' + kendo.attr('uid') + ']');
|
|
that.edit(item);
|
|
e.preventDefault();
|
|
});
|
|
that.element.on(clickNS, '.k-delete-button', function (e) {
|
|
var item = $(this).closest('[' + kendo.attr('uid') + ']');
|
|
that.remove(item);
|
|
e.preventDefault();
|
|
});
|
|
that.element.on(clickNS, '.k-update-button', function (e) {
|
|
that.save();
|
|
e.preventDefault();
|
|
});
|
|
that.element.on(clickNS, '.k-cancel-button', function (e) {
|
|
that.cancel();
|
|
e.preventDefault();
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that._unbindDataSource();
|
|
that._destroyEditable();
|
|
that.element.off(NS);
|
|
if (that.pager) {
|
|
that.pager.destroy();
|
|
}
|
|
kendo.destroy(that.element);
|
|
}
|
|
});
|
|
kendo.ui.plugin(ListView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.upload', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'upload',
|
|
name: 'Upload',
|
|
category: 'web',
|
|
description: 'The Upload widget uses progressive enhancement to deliver the best possible uploading experience to users.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, antiForgeryTokens = kendo.antiForgeryTokens, logToConsole = kendo.logToConsole, rFileExtension = /\.([^\.]+)$/, NS = '.kendoUpload', SELECT = 'select', UPLOAD = 'upload', SUCCESS = 'success', ERROR = 'error', COMPLETE = 'complete', CANCEL = 'cancel', PROGRESS = 'progress', REMOVE = 'remove';
|
|
var Upload = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.name = element.name;
|
|
that.multiple = that.options.multiple;
|
|
that.localization = that.options.localization;
|
|
var activeInput = that.element;
|
|
that.wrapper = activeInput.closest('.k-upload');
|
|
if (that.wrapper.length === 0) {
|
|
that.wrapper = that._wrapInput(activeInput);
|
|
}
|
|
that._activeInput(activeInput);
|
|
that.toggle(that.options.enabled);
|
|
var ns = that._ns = NS + '-' + kendo.guid();
|
|
activeInput.closest('form').on('submit' + ns, $.proxy(that._onParentFormSubmit, that)).on('reset' + ns, $.proxy(that._onParentFormReset, that));
|
|
if (that.options.async.saveUrl) {
|
|
that._module = that._supportsFormData() ? new formDataUploadModule(that) : new iframeUploadModule(that);
|
|
that._async = true;
|
|
var initialFiles = that.options.files;
|
|
if (initialFiles.length > 0) {
|
|
that._renderInitialFiles(initialFiles);
|
|
}
|
|
} else {
|
|
that._module = new syncUploadModule(that);
|
|
}
|
|
if (that._supportsDrop()) {
|
|
that._setupDropZone();
|
|
}
|
|
that.wrapper.on('click', '.k-upload-action', $.proxy(that._onFileAction, that)).on('click', '.k-upload-selected', $.proxy(that._onUploadSelected, that));
|
|
if (that.element.val()) {
|
|
that._onInputChange({ target: that.element });
|
|
}
|
|
},
|
|
events: [
|
|
SELECT,
|
|
UPLOAD,
|
|
SUCCESS,
|
|
ERROR,
|
|
COMPLETE,
|
|
CANCEL,
|
|
PROGRESS,
|
|
REMOVE
|
|
],
|
|
options: {
|
|
name: 'Upload',
|
|
enabled: true,
|
|
multiple: true,
|
|
showFileList: true,
|
|
template: '',
|
|
files: [],
|
|
async: {
|
|
removeVerb: 'POST',
|
|
autoUpload: true,
|
|
withCredentials: true
|
|
},
|
|
localization: {
|
|
'select': 'Select files...',
|
|
'cancel': 'Cancel',
|
|
'retry': 'Retry',
|
|
'remove': 'Remove',
|
|
'uploadSelectedFiles': 'Upload files',
|
|
'dropFilesHere': 'drop files here to upload',
|
|
'statusUploading': 'uploading',
|
|
'statusUploaded': 'uploaded',
|
|
'statusWarning': 'warning',
|
|
'statusFailed': 'failed',
|
|
'headerStatusUploading': 'Uploading...',
|
|
'headerStatusUploaded': 'Done'
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this, activeInput = that.element;
|
|
Widget.fn.setOptions.call(that, options);
|
|
that.multiple = that.options.multiple;
|
|
activeInput.attr('multiple', that._supportsMultiple() ? that.multiple : false);
|
|
that.toggle(that.options.enabled);
|
|
},
|
|
enable: function (enable) {
|
|
enable = typeof enable === 'undefined' ? true : enable;
|
|
this.toggle(enable);
|
|
},
|
|
disable: function () {
|
|
this.toggle(false);
|
|
},
|
|
toggle: function (enable) {
|
|
enable = typeof enable === 'undefined' ? enable : !enable;
|
|
this.wrapper.toggleClass('k-state-disabled', enable);
|
|
this.element.prop('disabled', enable);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
$(document).add($('.k-dropzone', that.wrapper)).add(that.wrapper.closest('form')).off(that._ns);
|
|
$(that.element).off(NS);
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
_addInput: function (sourceInput) {
|
|
if (!sourceInput[0].nodeType) {
|
|
return;
|
|
}
|
|
var that = this, input = sourceInput.clone().val('');
|
|
input.insertAfter(that.element).data('kendoUpload', that);
|
|
$(that.element).hide().attr('tabindex', '-1').removeAttr('id').off(NS);
|
|
that._activeInput(input);
|
|
that.element.focus();
|
|
},
|
|
_activeInput: function (input) {
|
|
var that = this, wrapper = that.wrapper;
|
|
that.element = input;
|
|
input.attr('multiple', that._supportsMultiple() ? that.multiple : false).attr('autocomplete', 'off').on('click' + NS, function (e) {
|
|
if (wrapper.hasClass('k-state-disabled')) {
|
|
e.preventDefault();
|
|
}
|
|
}).on('focus' + NS, function () {
|
|
$(this).parent().addClass('k-state-focused');
|
|
}).on('blur' + NS, function () {
|
|
$(this).parent().removeClass('k-state-focused');
|
|
}).on('change' + NS, $.proxy(that._onInputChange, that)).on('keydown' + NS, $.proxy(that._onInputKeyDown, that));
|
|
},
|
|
_onInputKeyDown: function (e) {
|
|
var that = this;
|
|
var firstButton = that.wrapper.find('.k-upload-action:first');
|
|
if (e.keyCode === kendo.keys.TAB && firstButton.length > 0) {
|
|
e.preventDefault();
|
|
firstButton.focus();
|
|
}
|
|
},
|
|
_onInputChange: function (e) {
|
|
var that = this;
|
|
var input = $(e.target);
|
|
var files = assignGuidToFiles(that._inputFiles(input), that._isAsyncNonBatch());
|
|
var prevented = that.trigger(SELECT, { files: files });
|
|
if (prevented) {
|
|
that._addInput(input);
|
|
input.remove();
|
|
} else {
|
|
that._module.onSelect({ target: input }, files);
|
|
}
|
|
},
|
|
_onDrop: function (e) {
|
|
var dt = e.originalEvent.dataTransfer;
|
|
var that = this;
|
|
var droppedFiles = dt.files;
|
|
var files = assignGuidToFiles(getAllFileInfo(droppedFiles), that._isAsyncNonBatch());
|
|
stopEvent(e);
|
|
if (droppedFiles.length > 0 && !that.wrapper.hasClass('k-state-disabled')) {
|
|
if (!that.multiple && files.length > 1) {
|
|
files.splice(1, files.length - 1);
|
|
}
|
|
var prevented = that.trigger(SELECT, { files: files });
|
|
if (!prevented) {
|
|
that._module.onSelect({ target: $('.k-dropzone', that.wrapper) }, files);
|
|
}
|
|
}
|
|
},
|
|
_isAsyncNonBatch: function () {
|
|
return this._async && !this.options.async.batch || false;
|
|
},
|
|
_renderInitialFiles: function (files) {
|
|
var that = this;
|
|
var idx = 0;
|
|
files = assignGuidToFiles(files, true);
|
|
for (idx = 0; idx < files.length; idx++) {
|
|
var currentFile = files[idx];
|
|
var fileEntry = that._enqueueFile(currentFile.name, { fileNames: [currentFile] });
|
|
fileEntry.addClass('k-file-success').data('files', [files[idx]]);
|
|
$('.k-progress', fileEntry).width('100%');
|
|
if (!that.options.template) {
|
|
$('.k-upload-status', fileEntry).prepend('<span class=\'k-upload-pct\'>100%</span>');
|
|
}
|
|
if (that._supportsRemove()) {
|
|
that._fileAction(fileEntry, REMOVE);
|
|
}
|
|
}
|
|
},
|
|
_prepareTemplateData: function (name, data) {
|
|
var filesData = data.fileNames, templateData = {}, totalSize = 0, idx = 0;
|
|
for (idx = 0; idx < filesData.length; idx++) {
|
|
totalSize += filesData[idx].size;
|
|
}
|
|
templateData.name = name;
|
|
templateData.size = totalSize;
|
|
templateData.files = data.fileNames;
|
|
return templateData;
|
|
},
|
|
_prepareDefaultFileEntryTemplate: function (name, data) {
|
|
var extension = '';
|
|
var defaultTemplate = $('<li class=\'k-file\'>' + '<span class=\'k-progress\'></span>' + '<span class=\'k-icon\'></span>' + '<span class=\'k-filename\' title=\'' + name + '\'>' + name + '</span>' + '<strong class=\'k-upload-status\'></strong>' + '</li>');
|
|
if (data.fileNames.length == 1 && !!data.fileNames[0].extension) {
|
|
extension = data.fileNames[0].extension.substring(1);
|
|
$('.k-icon', defaultTemplate).addClass('k-i-' + extension);
|
|
}
|
|
return defaultTemplate;
|
|
},
|
|
_enqueueFile: function (name, data) {
|
|
var that = this;
|
|
var existingFileEntries;
|
|
var fileEntry;
|
|
var fileUid = data.fileNames[0].uid;
|
|
var fileList = $('.k-upload-files', that.wrapper);
|
|
var options = that.options;
|
|
var template = options.template;
|
|
var templateData;
|
|
var removeEventArgs;
|
|
if (fileList.length === 0) {
|
|
fileList = $('<ul class=\'k-upload-files k-reset\'></ul>').appendTo(that.wrapper);
|
|
if (!that.options.showFileList) {
|
|
fileList.hide();
|
|
}
|
|
that.wrapper.removeClass('k-upload-empty');
|
|
}
|
|
existingFileEntries = $('.k-file', fileList);
|
|
if (!template) {
|
|
fileEntry = that._prepareDefaultFileEntryTemplate(name, data);
|
|
} else {
|
|
templateData = that._prepareTemplateData(name, data);
|
|
template = kendo.template(template);
|
|
fileEntry = $('<li class=\'k-file\'>' + template(templateData) + '</li>');
|
|
fileEntry.find('.k-upload-action').addClass('k-button k-button-bare');
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: fileEntry,
|
|
data: [templateData]
|
|
};
|
|
});
|
|
}
|
|
fileEntry.attr(kendo.attr('uid'), fileUid).appendTo(fileList).data(data);
|
|
if (!that._async) {
|
|
$('.k-progress', fileEntry).width('100%');
|
|
}
|
|
if (!that.multiple && existingFileEntries.length > 0) {
|
|
removeEventArgs = { files: existingFileEntries.data('fileNames') };
|
|
if (!that.trigger(REMOVE, removeEventArgs)) {
|
|
that._module.onRemove({ target: $(existingFileEntries, that.wrapper) }, removeEventArgs.data);
|
|
}
|
|
}
|
|
return fileEntry;
|
|
},
|
|
_removeFileEntry: function (fileEntry) {
|
|
var that = this;
|
|
var fileList = fileEntry.closest('.k-upload-files');
|
|
var allFiles;
|
|
var allCompletedFiles;
|
|
fileEntry.remove();
|
|
allFiles = $('.k-file', fileList);
|
|
allCompletedFiles = $('.k-file-success, .k-file-error', fileList);
|
|
if (allCompletedFiles.length === allFiles.length) {
|
|
this._hideUploadButton();
|
|
}
|
|
if (allFiles.length === 0) {
|
|
fileList.remove();
|
|
that.wrapper.addClass('k-upload-empty');
|
|
that._hideHeaderUploadstatus();
|
|
}
|
|
},
|
|
_fileAction: function (fileElement, actionKey) {
|
|
var classDictionary = {
|
|
remove: 'k-delete',
|
|
cancel: 'k-cancel',
|
|
retry: 'k-retry'
|
|
};
|
|
var iconsClassDictionary = {
|
|
remove: 'k-i-close',
|
|
cancel: 'k-i-close',
|
|
retry: 'k-i-refresh'
|
|
};
|
|
if (!classDictionary.hasOwnProperty(actionKey)) {
|
|
return;
|
|
}
|
|
this._clearFileAction(fileElement);
|
|
if (!this.options.template) {
|
|
fileElement.find('.k-upload-status .k-upload-action').remove();
|
|
fileElement.find('.k-upload-status').append(this._renderAction(classDictionary[actionKey], this.localization[actionKey], iconsClassDictionary[actionKey]));
|
|
} else {
|
|
fileElement.find('.k-upload-action').addClass('k-button k-button-bare').append('<span class=\'k-icon ' + iconsClassDictionary[actionKey] + ' ' + classDictionary[actionKey] + '\' title=\'' + this.localization[actionKey] + '\'></span>').show();
|
|
}
|
|
},
|
|
_fileState: function (fileEntry, stateKey) {
|
|
var localization = this.localization, states = {
|
|
uploading: { text: localization.statusUploading },
|
|
uploaded: { text: localization.statusUploaded },
|
|
failed: { text: localization.statusFailed }
|
|
}, currentState = states[stateKey];
|
|
if (currentState) {
|
|
$('.k-icon:not(.k-delete, .k-cancel, .k-retry)', fileEntry).text(currentState.text);
|
|
}
|
|
},
|
|
_renderAction: function (actionClass, actionText, iconClass) {
|
|
if (actionClass !== '') {
|
|
return $('<button type=\'button\' class=\'k-button k-button-bare k-upload-action\'>' + '<span class=\'k-icon ' + iconClass + ' ' + actionClass + '\' title=\'' + actionText + '\'></span>' + '</button>');
|
|
} else {
|
|
return $('<button type=\'button\' class=\'k-button\'>' + actionText + '</button>');
|
|
}
|
|
},
|
|
_clearFileAction: function (fileElement) {
|
|
$('.k-upload-action', fileElement).empty().hide();
|
|
},
|
|
_onFileAction: function (e) {
|
|
var that = this;
|
|
if (!that.wrapper.hasClass('k-state-disabled')) {
|
|
var button = $(e.target).closest('.k-upload-action'), icon = button.find('.k-icon'), fileEntry = button.closest('.k-file'), eventArgs = { files: fileEntry.data('fileNames') };
|
|
if (icon.hasClass('k-delete')) {
|
|
if (!that.trigger(REMOVE, eventArgs)) {
|
|
that._module.onRemove({ target: $(fileEntry, that.wrapper) }, eventArgs.data);
|
|
}
|
|
} else if (icon.hasClass('k-cancel')) {
|
|
that.trigger(CANCEL, eventArgs);
|
|
that._module.onCancel({ target: $(fileEntry, that.wrapper) });
|
|
this._checkAllComplete();
|
|
that._updateHeaderUploadStatus();
|
|
} else if (icon.hasClass('k-retry')) {
|
|
$('.k-warning', fileEntry).remove();
|
|
that._module.onRetry({ target: $(fileEntry, that.wrapper) });
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_onUploadSelected: function () {
|
|
var that = this;
|
|
var wrapper = that.wrapper;
|
|
if (!wrapper.hasClass('k-state-disabled')) {
|
|
this._module.onSaveSelected();
|
|
}
|
|
return false;
|
|
},
|
|
_onFileProgress: function (e, percentComplete) {
|
|
var progressPct;
|
|
if (percentComplete > 100) {
|
|
percentComplete = 100;
|
|
}
|
|
if (!this.options.template) {
|
|
progressPct = $('.k-upload-pct', e.target);
|
|
if (progressPct.length === 0) {
|
|
$('.k-upload-status', e.target).prepend('<span class=\'k-upload-pct\'></span>');
|
|
}
|
|
$('.k-upload-pct', e.target).text(percentComplete + '%');
|
|
$('.k-progress', e.target).width(percentComplete + '%');
|
|
} else {
|
|
$('.k-progress', e.target).width(percentComplete + '%');
|
|
}
|
|
this.trigger(PROGRESS, {
|
|
files: getFileEntry(e).data('fileNames'),
|
|
percentComplete: percentComplete
|
|
});
|
|
},
|
|
_onUploadSuccess: function (e, response, xhr) {
|
|
var fileEntry = getFileEntry(e);
|
|
this._fileState(fileEntry, 'uploaded');
|
|
fileEntry.removeClass('k-file-progress').addClass('k-file-success');
|
|
this._updateHeaderUploadStatus();
|
|
this.trigger(SUCCESS, {
|
|
files: fileEntry.data('fileNames'),
|
|
response: response,
|
|
operation: 'upload',
|
|
XMLHttpRequest: xhr
|
|
});
|
|
if (this._supportsRemove()) {
|
|
this._fileAction(fileEntry, REMOVE);
|
|
} else {
|
|
this._clearFileAction(fileEntry);
|
|
}
|
|
this._checkAllComplete();
|
|
},
|
|
_onUploadError: function (e, xhr) {
|
|
var fileEntry = getFileEntry(e);
|
|
var uploadPercentage = $('.k-upload-pct', fileEntry);
|
|
this._fileState(fileEntry, 'failed');
|
|
fileEntry.removeClass('k-file-progress').addClass('k-file-error');
|
|
$('.k-progress', fileEntry).width('100%');
|
|
if (uploadPercentage.length > 0) {
|
|
uploadPercentage.empty().removeClass('k-upload-pct').addClass('k-icon k-warning');
|
|
} else {
|
|
$('.k-upload-status', fileEntry).prepend('<span class=\'k-icon k-warning\'></span>');
|
|
}
|
|
this._updateHeaderUploadStatus();
|
|
this._fileAction(fileEntry, 'retry');
|
|
this.trigger(ERROR, {
|
|
operation: 'upload',
|
|
files: fileEntry.data('fileNames'),
|
|
XMLHttpRequest: xhr
|
|
});
|
|
logToConsole('Server response: ' + xhr.responseText);
|
|
this._checkAllComplete();
|
|
},
|
|
_showUploadButton: function () {
|
|
var uploadButton = $('.k-upload-selected', this.wrapper);
|
|
if (uploadButton.length === 0) {
|
|
uploadButton = this._renderAction('', this.localization.uploadSelectedFiles).addClass('k-upload-selected');
|
|
}
|
|
this.wrapper.append(uploadButton);
|
|
},
|
|
_hideUploadButton: function () {
|
|
$('.k-upload-selected', this.wrapper).remove();
|
|
},
|
|
_showHeaderUploadStatus: function () {
|
|
var localization = this.localization;
|
|
var dropZone = $('.k-dropzone', this.wrapper);
|
|
var headerUploadStatus = $('.k-upload-status-total', this.wrapper);
|
|
if (headerUploadStatus.length !== 0) {
|
|
headerUploadStatus.remove();
|
|
}
|
|
headerUploadStatus = '<strong class="k-upload-status k-upload-status-total">' + localization.headerStatusUploading + '<span class="k-icon k-loading">' + localization.statusUploading + '</span>' + '</strong>';
|
|
if (dropZone.length > 0) {
|
|
dropZone.append(headerUploadStatus);
|
|
} else {
|
|
$('.k-upload-button', this.wrapper).after(headerUploadStatus);
|
|
}
|
|
},
|
|
_updateHeaderUploadStatus: function () {
|
|
var that = this;
|
|
var localization = that.localization;
|
|
var currentlyUploading = $('.k-file', that.wrapper).not('.k-file-success, .k-file-error');
|
|
var failedUploads;
|
|
var headerUploadStatus;
|
|
var headerUploadStatusIcon;
|
|
if (currentlyUploading.length === 0) {
|
|
failedUploads = $('.k-file.k-file-error', that.wrapper);
|
|
headerUploadStatus = $('.k-upload-status-total', that.wrapper);
|
|
headerUploadStatusIcon = $('.k-icon', headerUploadStatus).removeClass('k-loading').addClass(failedUploads.length !== 0 ? 'k-warning' : 'k-i-tick').text(failedUploads.length !== 0 ? localization.statusWarning : localization.statusUploaded);
|
|
headerUploadStatus.text(that.localization.headerStatusUploaded).append(headerUploadStatusIcon);
|
|
}
|
|
},
|
|
_hideHeaderUploadstatus: function () {
|
|
$('.k-upload-status-total', this.wrapper).remove();
|
|
},
|
|
_onParentFormSubmit: function () {
|
|
var upload = this, element = upload.element;
|
|
if (typeof this._module.onAbort !== 'undefined') {
|
|
this._module.onAbort();
|
|
}
|
|
if (!element.value) {
|
|
var input = $(element);
|
|
input.attr('disabled', 'disabled');
|
|
window.setTimeout(function () {
|
|
input.removeAttr('disabled');
|
|
}, 0);
|
|
}
|
|
},
|
|
_onParentFormReset: function () {
|
|
$('.k-upload-files', this.wrapper).remove();
|
|
},
|
|
_supportsFormData: function () {
|
|
return typeof FormData != 'undefined';
|
|
},
|
|
_supportsMultiple: function () {
|
|
var windows = this._userAgent().indexOf('Windows') > -1;
|
|
return !kendo.support.browser.opera && !(kendo.support.browser.safari && windows);
|
|
},
|
|
_supportsDrop: function () {
|
|
var userAgent = this._userAgent().toLowerCase(), isChrome = /chrome/.test(userAgent), isSafari = !isChrome && /safari/.test(userAgent), isWindowsSafari = isSafari && /windows/.test(userAgent);
|
|
return !isWindowsSafari && this._supportsFormData() && this.options.async.saveUrl;
|
|
},
|
|
_userAgent: function () {
|
|
return navigator.userAgent;
|
|
},
|
|
_setupDropZone: function () {
|
|
var that = this;
|
|
$('.k-upload-button', this.wrapper).wrap('<div class=\'k-dropzone\'></div>');
|
|
var ns = that._ns;
|
|
var dropZone = $('.k-dropzone', that.wrapper).append($('<em>' + that.localization.dropFilesHere + '</em>')).on('dragenter' + ns, stopEvent).on('dragover' + ns, function (e) {
|
|
e.preventDefault();
|
|
}).on('drop' + ns, $.proxy(this._onDrop, this));
|
|
bindDragEventWrappers(dropZone, ns, function () {
|
|
if (!dropZone.closest('.k-upload').hasClass('k-state-disabled')) {
|
|
dropZone.addClass('k-dropzone-hovered');
|
|
}
|
|
}, function () {
|
|
dropZone.removeClass('k-dropzone-hovered');
|
|
});
|
|
bindDragEventWrappers($(document), ns, function () {
|
|
if (!dropZone.closest('.k-upload').hasClass('k-state-disabled')) {
|
|
dropZone.addClass('k-dropzone-active');
|
|
dropZone.closest('.k-upload').removeClass('k-upload-empty');
|
|
}
|
|
}, function () {
|
|
dropZone.removeClass('k-dropzone-active');
|
|
if ($('li.k-file', dropZone.closest('.k-upload')).length === 0) {
|
|
dropZone.closest('.k-upload').addClass('k-upload-empty');
|
|
}
|
|
});
|
|
},
|
|
_supportsRemove: function () {
|
|
return !!this.options.async.removeUrl;
|
|
},
|
|
_submitRemove: function (fileNames, data, onSuccess, onError) {
|
|
var upload = this, removeField = upload.options.async.removeField || 'fileNames', params = $.extend(data, antiForgeryTokens());
|
|
params[removeField] = fileNames;
|
|
jQuery.ajax({
|
|
type: this.options.async.removeVerb,
|
|
dataType: 'json',
|
|
dataFilter: normalizeJSON,
|
|
url: this.options.async.removeUrl,
|
|
traditional: true,
|
|
data: params,
|
|
success: onSuccess,
|
|
error: onError,
|
|
xhrFields: { withCredentials: this.options.async.withCredentials }
|
|
});
|
|
},
|
|
_wrapInput: function (input) {
|
|
var that = this;
|
|
var options = that.options;
|
|
input.wrap('<div class=\'k-widget k-upload k-header\'><div class=\'k-button k-upload-button\'></div></div>');
|
|
if (!options.async.saveUrl) {
|
|
input.closest('.k-upload').addClass('k-upload-sync');
|
|
}
|
|
input.closest('.k-upload').addClass('k-upload-empty');
|
|
input.closest('.k-button').append('<span>' + this.localization.select + '</span>');
|
|
return input.closest('.k-upload');
|
|
},
|
|
_checkAllComplete: function () {
|
|
if ($('.k-file.k-file-progress', this.wrapper).length === 0) {
|
|
this.trigger(COMPLETE);
|
|
}
|
|
},
|
|
_inputFiles: function (sourceInput) {
|
|
return inputFiles(sourceInput);
|
|
}
|
|
});
|
|
var syncUploadModule = function (upload) {
|
|
this.name = 'syncUploadModule';
|
|
this.element = upload.wrapper;
|
|
this.upload = upload;
|
|
this.element.closest('form').attr('enctype', 'multipart/form-data').attr('encoding', 'multipart/form-data');
|
|
};
|
|
syncUploadModule.prototype = {
|
|
onSelect: function (e, files) {
|
|
var upload = this.upload;
|
|
var sourceInput = $(e.target);
|
|
upload._addInput(sourceInput);
|
|
var file = upload._enqueueFile(getFileName(sourceInput), {
|
|
'relatedInput': sourceInput,
|
|
'fileNames': files
|
|
});
|
|
upload._fileAction(file, REMOVE);
|
|
},
|
|
onRemove: function (e) {
|
|
var fileEntry = getFileEntry(e);
|
|
fileEntry.data('relatedInput').remove();
|
|
this.upload._removeFileEntry(fileEntry);
|
|
}
|
|
};
|
|
var iframeUploadModule = function (upload) {
|
|
this.name = 'iframeUploadModule';
|
|
this.element = upload.wrapper;
|
|
this.upload = upload;
|
|
this.iframes = [];
|
|
};
|
|
Upload._frameId = 0;
|
|
iframeUploadModule.prototype = {
|
|
onSelect: function (e, files) {
|
|
var upload = this.upload, sourceInput = $(e.target);
|
|
var fileEntry = this.prepareUpload(sourceInput, files);
|
|
if (upload.options.async.autoUpload) {
|
|
this.performUpload(fileEntry);
|
|
} else {
|
|
if (upload._supportsRemove()) {
|
|
this.upload._fileAction(fileEntry, REMOVE);
|
|
}
|
|
upload._showUploadButton();
|
|
}
|
|
},
|
|
prepareUpload: function (sourceInput, files) {
|
|
var upload = this.upload;
|
|
var activeInput = $(upload.element);
|
|
var name = upload.options.async.saveField || sourceInput.attr('name');
|
|
upload._addInput(sourceInput);
|
|
sourceInput.attr('name', name);
|
|
var iframe = this.createFrame(upload.name + '_' + Upload._frameId++);
|
|
this.registerFrame(iframe);
|
|
var form = this.createForm(upload.options.async.saveUrl, iframe.attr('name')).append(activeInput);
|
|
var fileEntry = upload._enqueueFile(getFileName(sourceInput), {
|
|
'frame': iframe,
|
|
'relatedInput': activeInput,
|
|
'fileNames': files
|
|
});
|
|
iframe.data({
|
|
'form': form,
|
|
'file': fileEntry
|
|
});
|
|
return fileEntry;
|
|
},
|
|
performUpload: function (fileEntry) {
|
|
var e = { files: fileEntry.data('fileNames') }, iframe = fileEntry.data('frame'), upload = this.upload;
|
|
if (!upload.trigger(UPLOAD, e)) {
|
|
upload._hideUploadButton();
|
|
upload._showHeaderUploadStatus();
|
|
iframe.appendTo(document.body);
|
|
var form = iframe.data('form').attr('action', upload.options.async.saveUrl).appendTo(document.body);
|
|
e.data = $.extend({}, e.data, antiForgeryTokens());
|
|
for (var key in e.data) {
|
|
var dataInput = form.find('input[name=\'' + key + '\']');
|
|
if (dataInput.length === 0) {
|
|
dataInput = $('<input>', {
|
|
type: 'hidden',
|
|
name: key
|
|
}).prependTo(form);
|
|
}
|
|
dataInput.val(e.data[key]);
|
|
}
|
|
upload._fileAction(fileEntry, CANCEL);
|
|
upload._fileState(fileEntry, 'uploading');
|
|
$(fileEntry).removeClass('k-file-error').addClass('k-file-progress');
|
|
iframe.one('load', $.proxy(this.onIframeLoad, this));
|
|
form[0].submit();
|
|
} else {
|
|
upload._removeFileEntry(iframe.data('file'));
|
|
this.cleanupFrame(iframe);
|
|
this.unregisterFrame(iframe);
|
|
}
|
|
},
|
|
onSaveSelected: function () {
|
|
var module = this;
|
|
$('.k-file', this.element).each(function () {
|
|
var fileEntry = $(this), started = isFileUploadStarted(fileEntry);
|
|
if (!started) {
|
|
module.performUpload(fileEntry);
|
|
}
|
|
});
|
|
},
|
|
onIframeLoad: function (e) {
|
|
var iframe = $(e.target), responseText;
|
|
try {
|
|
responseText = iframe.contents().text();
|
|
} catch (ex) {
|
|
responseText = 'Error trying to get server response: ' + ex;
|
|
}
|
|
this.processResponse(iframe, responseText);
|
|
},
|
|
processResponse: function (iframe, responseText) {
|
|
var fileEntry = iframe.data('file'), module = this, fakeXHR = { responseText: responseText };
|
|
tryParseJSON(responseText, function (jsonResult) {
|
|
$.extend(fakeXHR, {
|
|
statusText: 'OK',
|
|
status: '200'
|
|
});
|
|
module.upload._onFileProgress({ target: $(fileEntry, module.upload.wrapper) }, 100);
|
|
module.upload._onUploadSuccess({ target: $(fileEntry, module.upload.wrapper) }, jsonResult, fakeXHR);
|
|
module.cleanupFrame(iframe);
|
|
module.unregisterFrame(iframe);
|
|
}, function () {
|
|
$.extend(fakeXHR, {
|
|
statusText: 'error',
|
|
status: '500'
|
|
});
|
|
module.upload._onUploadError({ target: $(fileEntry, module.upload.wrapper) }, fakeXHR);
|
|
});
|
|
},
|
|
onCancel: function (e) {
|
|
var iframe = $(e.target).data('frame');
|
|
this.stopFrameSubmit(iframe);
|
|
this.cleanupFrame(iframe);
|
|
this.unregisterFrame(iframe);
|
|
this.upload._removeFileEntry(iframe.data('file'));
|
|
},
|
|
onRetry: function (e) {
|
|
var fileEntry = getFileEntry(e);
|
|
this.performUpload(fileEntry);
|
|
},
|
|
onRemove: function (e, data) {
|
|
var fileEntry = getFileEntry(e);
|
|
var iframe = fileEntry.data('frame');
|
|
if (iframe) {
|
|
this.unregisterFrame(iframe);
|
|
this.upload._removeFileEntry(fileEntry);
|
|
this.cleanupFrame(iframe);
|
|
} else {
|
|
removeUploadedFile(fileEntry, this.upload, data);
|
|
}
|
|
},
|
|
onAbort: function () {
|
|
var element = this.element, module = this;
|
|
$.each(this.iframes, function () {
|
|
$('input', this.data('form')).appendTo(element);
|
|
module.stopFrameSubmit(this[0]);
|
|
this.data('form').remove();
|
|
this.remove();
|
|
});
|
|
this.iframes = [];
|
|
},
|
|
createFrame: function (id) {
|
|
return $('<iframe' + ' name=\'' + id + '\'' + ' id=\'' + id + '\'' + ' style=\'display:none;\' />');
|
|
},
|
|
createForm: function (action, target) {
|
|
return $('<form enctype=\'multipart/form-data\' method=\'POST\'' + ' action=\'' + action + '\'' + ' target=\'' + target + '\'' + '/>');
|
|
},
|
|
stopFrameSubmit: function (frame) {
|
|
if (typeof frame.stop != 'undefined') {
|
|
frame.stop();
|
|
} else if (frame.document) {
|
|
frame.document.execCommand('Stop');
|
|
}
|
|
},
|
|
registerFrame: function (frame) {
|
|
this.iframes.push(frame);
|
|
},
|
|
unregisterFrame: function (frame) {
|
|
this.iframes = $.grep(this.iframes, function (value) {
|
|
return value.attr('name') != frame.attr('name');
|
|
});
|
|
},
|
|
cleanupFrame: function (frame) {
|
|
var form = frame.data('form');
|
|
frame.data('file').data('frame', null);
|
|
setTimeout(function () {
|
|
form.remove();
|
|
frame.remove();
|
|
}, 1);
|
|
}
|
|
};
|
|
var formDataUploadModule = function (upload) {
|
|
this.name = 'formDataUploadModule';
|
|
this.element = upload.wrapper;
|
|
this.upload = upload;
|
|
};
|
|
formDataUploadModule.prototype = {
|
|
onSelect: function (e, files) {
|
|
var upload = this.upload, module = this, sourceElement = $(e.target), fileEntries = this.prepareUpload(sourceElement, files);
|
|
$.each(fileEntries, function () {
|
|
if (upload.options.async.autoUpload) {
|
|
module.performUpload(this);
|
|
} else {
|
|
if (upload._supportsRemove()) {
|
|
upload._fileAction(this, REMOVE);
|
|
}
|
|
upload._showUploadButton();
|
|
}
|
|
});
|
|
},
|
|
prepareUpload: function (sourceElement, files) {
|
|
var fileEntries = this.enqueueFiles(files);
|
|
if (sourceElement.is('input')) {
|
|
$.each(fileEntries, function () {
|
|
$(this).data('relatedInput', sourceElement);
|
|
});
|
|
sourceElement.data('relatedFileEntries', fileEntries);
|
|
this.upload._addInput(sourceElement);
|
|
}
|
|
return fileEntries;
|
|
},
|
|
enqueueFiles: function (files) {
|
|
var upload = this.upload, name, i, filesLength = files.length, currentFile, fileEntry, fileEntries = [];
|
|
if (upload.options.async.batch === true) {
|
|
name = $.map(files, function (file) {
|
|
return file.name;
|
|
}).join(', ');
|
|
fileEntry = upload._enqueueFile(name, { fileNames: files });
|
|
fileEntry.data('files', files);
|
|
fileEntries.push(fileEntry);
|
|
} else {
|
|
for (i = 0; i < filesLength; i++) {
|
|
currentFile = files[i];
|
|
name = currentFile.name;
|
|
fileEntry = upload._enqueueFile(name, { fileNames: [currentFile] });
|
|
fileEntry.data('files', [currentFile]);
|
|
fileEntries.push(fileEntry);
|
|
}
|
|
}
|
|
return fileEntries;
|
|
},
|
|
performUpload: function (fileEntry) {
|
|
var upload = this.upload, formData = this.createFormData(), xhr = this.createXHR(), e = {
|
|
files: fileEntry.data('fileNames'),
|
|
XMLHttpRequest: xhr
|
|
};
|
|
if (!upload.trigger(UPLOAD, e)) {
|
|
upload._fileAction(fileEntry, CANCEL);
|
|
upload._hideUploadButton();
|
|
upload._showHeaderUploadStatus();
|
|
if (e.formData) {
|
|
formData = e.formData;
|
|
} else {
|
|
e.data = $.extend({}, e.data, antiForgeryTokens());
|
|
for (var key in e.data) {
|
|
formData.append(key, e.data[key]);
|
|
}
|
|
this.populateFormData(formData, fileEntry.data('files'));
|
|
}
|
|
upload._fileState(fileEntry, 'uploading');
|
|
$(fileEntry).removeClass('k-file-error').addClass('k-file-progress');
|
|
this.postFormData(upload.options.async.saveUrl, formData, fileEntry, xhr);
|
|
} else {
|
|
this.removeFileEntry(fileEntry);
|
|
}
|
|
},
|
|
onSaveSelected: function () {
|
|
var module = this;
|
|
$('.k-file', this.element).each(function () {
|
|
var fileEntry = $(this), started = isFileUploadStarted(fileEntry);
|
|
if (!started) {
|
|
module.performUpload(fileEntry);
|
|
}
|
|
});
|
|
},
|
|
onCancel: function (e) {
|
|
var fileEntry = getFileEntry(e);
|
|
this.stopUploadRequest(fileEntry);
|
|
this.removeFileEntry(fileEntry);
|
|
},
|
|
onRetry: function (e) {
|
|
var fileEntry = getFileEntry(e);
|
|
this.performUpload(fileEntry);
|
|
},
|
|
onRemove: function (e, data) {
|
|
var fileEntry = getFileEntry(e);
|
|
if (fileEntry.hasClass('k-file-success')) {
|
|
removeUploadedFile(fileEntry, this.upload, data);
|
|
} else {
|
|
this.removeFileEntry(fileEntry);
|
|
}
|
|
},
|
|
createXHR: function () {
|
|
return new XMLHttpRequest();
|
|
},
|
|
postFormData: function (url, data, fileEntry, xhr) {
|
|
var module = this;
|
|
fileEntry.data('request', xhr);
|
|
xhr.addEventListener('load', function (e) {
|
|
module.onRequestSuccess.call(module, e, fileEntry);
|
|
}, false);
|
|
xhr.addEventListener(ERROR, function (e) {
|
|
module.onRequestError.call(module, e, fileEntry);
|
|
}, false);
|
|
xhr.upload.addEventListener('progress', function (e) {
|
|
module.onRequestProgress.call(module, e, fileEntry);
|
|
}, false);
|
|
xhr.open('POST', url, true);
|
|
xhr.withCredentials = this.upload.options.async.withCredentials;
|
|
xhr.send(data);
|
|
},
|
|
createFormData: function () {
|
|
return new FormData();
|
|
},
|
|
populateFormData: function (data, files) {
|
|
var upload = this.upload, i, length = files.length;
|
|
for (i = 0; i < length; i++) {
|
|
data.append(upload.options.async.saveField || upload.name, files[i].rawFile);
|
|
}
|
|
return data;
|
|
},
|
|
onRequestSuccess: function (e, fileEntry) {
|
|
var xhr = e.target, module = this;
|
|
function raiseError() {
|
|
module.upload._onUploadError({ target: $(fileEntry, module.upload.wrapper) }, xhr);
|
|
}
|
|
if (xhr.status >= 200 && xhr.status <= 299) {
|
|
tryParseJSON(xhr.responseText, function (jsonResult) {
|
|
module.upload._onFileProgress({ target: $(fileEntry, module.upload.wrapper) }, 100);
|
|
module.upload._onUploadSuccess({ target: $(fileEntry, module.upload.wrapper) }, jsonResult, xhr);
|
|
module.cleanupFileEntry(fileEntry);
|
|
}, raiseError);
|
|
} else {
|
|
raiseError();
|
|
}
|
|
},
|
|
onRequestError: function (e, fileEntry) {
|
|
var xhr = e.target;
|
|
this.upload._onUploadError({ target: $(fileEntry, this.upload.wrapper) }, xhr);
|
|
},
|
|
cleanupFileEntry: function (fileEntry) {
|
|
var relatedInput = fileEntry.data('relatedInput'), uploadComplete = true;
|
|
if (relatedInput) {
|
|
$.each(relatedInput.data('relatedFileEntries') || [], function () {
|
|
if (this.parent().length > 0 && this[0] != fileEntry[0]) {
|
|
uploadComplete = uploadComplete && this.hasClass('k-file-success');
|
|
}
|
|
});
|
|
if (uploadComplete) {
|
|
relatedInput.remove();
|
|
}
|
|
}
|
|
},
|
|
removeFileEntry: function (fileEntry) {
|
|
this.cleanupFileEntry(fileEntry);
|
|
this.upload._removeFileEntry(fileEntry);
|
|
},
|
|
onRequestProgress: function (e, fileEntry) {
|
|
var percentComplete = Math.round(e.loaded * 100 / e.total);
|
|
this.upload._onFileProgress({ target: $(fileEntry, this.upload.wrapper) }, percentComplete);
|
|
},
|
|
stopUploadRequest: function (fileEntry) {
|
|
fileEntry.data('request').abort();
|
|
}
|
|
};
|
|
function getFileName(input) {
|
|
return $.map(inputFiles(input), function (file) {
|
|
return file.name;
|
|
}).join(', ');
|
|
}
|
|
function inputFiles($input) {
|
|
var input = $input[0];
|
|
if (input.files) {
|
|
return getAllFileInfo(input.files);
|
|
} else {
|
|
return [{
|
|
name: stripPath(input.value),
|
|
extension: getFileExtension(input.value),
|
|
size: null
|
|
}];
|
|
}
|
|
}
|
|
function getAllFileInfo(rawFiles) {
|
|
return $.map(rawFiles, function (file) {
|
|
return getFileInfo(file);
|
|
});
|
|
}
|
|
function getFileInfo(rawFile) {
|
|
var fileName = rawFile.name || rawFile.fileName;
|
|
return {
|
|
name: kendo.htmlEncode(fileName),
|
|
extension: getFileExtension(fileName),
|
|
size: rawFile.size || rawFile.fileSize,
|
|
rawFile: rawFile
|
|
};
|
|
}
|
|
function getFileExtension(fileName) {
|
|
var matches = fileName.match(rFileExtension);
|
|
return matches ? matches[0] : '';
|
|
}
|
|
function stripPath(name) {
|
|
var slashIndex = name.lastIndexOf('\\');
|
|
return slashIndex != -1 ? name.substr(slashIndex + 1) : name;
|
|
}
|
|
function assignGuidToFiles(files, unique) {
|
|
var uid = kendo.guid();
|
|
return $.map(files, function (file) {
|
|
file.uid = unique ? kendo.guid() : uid;
|
|
return file;
|
|
});
|
|
}
|
|
function shouldRemoveFileEntry(upload) {
|
|
return !upload.multiple && $('.k-file', upload.wrapper).length > 1;
|
|
}
|
|
function removeUploadedFile(fileEntry, upload, data) {
|
|
if (!upload._supportsRemove()) {
|
|
if (shouldRemoveFileEntry(upload)) {
|
|
upload._removeFileEntry(fileEntry);
|
|
}
|
|
return;
|
|
}
|
|
var files = fileEntry.data('fileNames');
|
|
var fileNames = $.map(files, function (file) {
|
|
return file.name;
|
|
});
|
|
upload._submitRemove(fileNames, data, function onSuccess(data, textStatus, xhr) {
|
|
upload._removeFileEntry(fileEntry);
|
|
upload.trigger(SUCCESS, {
|
|
operation: 'remove',
|
|
files: files,
|
|
response: data,
|
|
XMLHttpRequest: xhr
|
|
});
|
|
}, function onError(xhr) {
|
|
if (shouldRemoveFileEntry(upload)) {
|
|
upload._removeFileEntry(fileEntry);
|
|
}
|
|
upload.trigger(ERROR, {
|
|
operation: 'remove',
|
|
files: files,
|
|
XMLHttpRequest: xhr
|
|
});
|
|
logToConsole('Server response: ' + xhr.responseText);
|
|
});
|
|
}
|
|
function tryParseJSON(input, onSuccess, onError) {
|
|
var success = false, json = '';
|
|
try {
|
|
json = $.parseJSON(normalizeJSON(input));
|
|
success = true;
|
|
} catch (e) {
|
|
onError();
|
|
}
|
|
if (success) {
|
|
onSuccess(json);
|
|
}
|
|
}
|
|
function normalizeJSON(input) {
|
|
if (typeof input === 'undefined' || input === '') {
|
|
input = '{}';
|
|
}
|
|
return input;
|
|
}
|
|
function stopEvent(e) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
function bindDragEventWrappers(element, namespace, onDragEnter, onDragLeave) {
|
|
var hideInterval, lastDrag;
|
|
element.on('dragenter' + namespace, function () {
|
|
onDragEnter();
|
|
lastDrag = new Date();
|
|
if (!hideInterval) {
|
|
hideInterval = setInterval(function () {
|
|
var sinceLastDrag = new Date() - lastDrag;
|
|
if (sinceLastDrag > 100) {
|
|
onDragLeave();
|
|
clearInterval(hideInterval);
|
|
hideInterval = null;
|
|
}
|
|
}, 100);
|
|
}
|
|
}).on('dragover' + namespace, function () {
|
|
lastDrag = new Date();
|
|
});
|
|
}
|
|
function isFileUploadStarted(fileEntry) {
|
|
return fileEntry.is('.k-file-progress, .k-file-success, .k-file-error');
|
|
}
|
|
function getFileEntry(e) {
|
|
return $(e.target).closest('.k-file');
|
|
}
|
|
kendo.ui.plugin(Upload);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.filebrowser', [
|
|
'kendo.listview',
|
|
'kendo.dropdownlist',
|
|
'kendo.upload'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'filebrowser',
|
|
name: 'FileBrowser',
|
|
category: 'web',
|
|
description: '',
|
|
hidden: true,
|
|
depends: [
|
|
'selectable',
|
|
'listview',
|
|
'dropdownlist',
|
|
'upload'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, isPlainObject = $.isPlainObject, proxy = $.proxy, extend = $.extend, placeholderSupported = kendo.support.placeholder, browser = kendo.support.browser, isFunction = kendo.isFunction, trimSlashesRegExp = /(^\/|\/$)/g, CHANGE = 'change', APPLY = 'apply', ERROR = 'error', CLICK = 'click', NS = '.kendoFileBrowser', BREADCRUBMSNS = '.kendoBreadcrumbs', SEARCHBOXNS = '.kendoSearchBox', NAMEFIELD = 'name', SIZEFIELD = 'size', TYPEFIELD = 'type', DEFAULTSORTORDER = {
|
|
field: TYPEFIELD,
|
|
dir: 'asc'
|
|
}, EMPTYTILE = kendo.template('<li class="k-tile-empty"><strong>${text}</strong></li>'), TOOLBARTMPL = '<div class="k-widget k-filebrowser-toolbar k-header k-floatwrap">' + '<div class="k-toolbar-wrap">' + '# if (showUpload) { # ' + '<div class="k-widget k-upload"><div class="k-button k-button-icontext k-upload-button">' + '<span class="k-icon k-add"></span>#=messages.uploadFile#<input type="file" name="file" /></div></div>' + '# } #' + '# if (showCreate) { #' + '<button type="button" class="k-button k-button-icon"><span class="k-icon k-addfolder" /></button>' + '# } #' + '# if (showDelete) { #' + '<button type="button" class="k-button k-button-icon k-state-disabled"><span class="k-icon k-delete" /></button> ' + '# } #' + '</div>' + '<div class="k-tiles-arrange">' + '<label>#=messages.orderBy#: <select /></label>' + '</div>' + '</div>';
|
|
extend(true, kendo.data, {
|
|
schemas: {
|
|
'filebrowser': {
|
|
data: function (data) {
|
|
return data.items || data || [];
|
|
},
|
|
model: {
|
|
id: 'name',
|
|
fields: {
|
|
name: 'name',
|
|
size: 'size',
|
|
type: 'type'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
extend(true, kendo.data, {
|
|
transports: {
|
|
'filebrowser': kendo.data.RemoteTransport.extend({
|
|
init: function (options) {
|
|
kendo.data.RemoteTransport.fn.init.call(this, $.extend(true, {}, this.options, options));
|
|
},
|
|
_call: function (type, options) {
|
|
options.data = $.extend({}, options.data, { path: this.options.path() });
|
|
if (isFunction(this.options[type])) {
|
|
this.options[type].call(this, options);
|
|
} else {
|
|
kendo.data.RemoteTransport.fn[type].call(this, options);
|
|
}
|
|
},
|
|
read: function (options) {
|
|
this._call('read', options);
|
|
},
|
|
create: function (options) {
|
|
this._call('create', options);
|
|
},
|
|
destroy: function (options) {
|
|
this._call('destroy', options);
|
|
},
|
|
update: function () {
|
|
},
|
|
options: {
|
|
read: { type: 'POST' },
|
|
update: { type: 'POST' },
|
|
create: { type: 'POST' },
|
|
destroy: { type: 'POST' }
|
|
}
|
|
})
|
|
}
|
|
});
|
|
function bindDragEventWrappers(element, onDragEnter, onDragLeave) {
|
|
var hideInterval, lastDrag;
|
|
element.on('dragenter' + NS, function () {
|
|
onDragEnter();
|
|
lastDrag = new Date();
|
|
if (!hideInterval) {
|
|
hideInterval = setInterval(function () {
|
|
var sinceLastDrag = new Date() - lastDrag;
|
|
if (sinceLastDrag > 100) {
|
|
onDragLeave();
|
|
clearInterval(hideInterval);
|
|
hideInterval = null;
|
|
}
|
|
}, 100);
|
|
}
|
|
}).on('dragover' + NS, function () {
|
|
lastDrag = new Date();
|
|
});
|
|
}
|
|
var offsetTop;
|
|
if (browser.msie && browser.version < 8) {
|
|
offsetTop = function (element) {
|
|
return element.offsetTop;
|
|
};
|
|
} else {
|
|
offsetTop = function (element) {
|
|
return element.offsetTop - $(element).height();
|
|
};
|
|
}
|
|
function concatPaths(path, name) {
|
|
if (path === undefined || !path.match(/\/$/)) {
|
|
path = (path || '') + '/';
|
|
}
|
|
return path + name;
|
|
}
|
|
function sizeFormatter(value) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
var suffix = ' bytes';
|
|
if (value >= 1073741824) {
|
|
suffix = ' GB';
|
|
value /= 1073741824;
|
|
} else if (value >= 1048576) {
|
|
suffix = ' MB';
|
|
value /= 1048576;
|
|
} else if (value >= 1024) {
|
|
suffix = ' KB';
|
|
value /= 1024;
|
|
}
|
|
return Math.round(value * 100) / 100 + suffix;
|
|
}
|
|
function fieldName(fields, name) {
|
|
var descriptor = fields[name];
|
|
if (isPlainObject(descriptor)) {
|
|
return descriptor.from || descriptor.field || name;
|
|
}
|
|
return descriptor;
|
|
}
|
|
var FileBrowser = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = options || {};
|
|
Widget.fn.init.call(that, element, options);
|
|
that.element.addClass('k-filebrowser');
|
|
that.element.on(CLICK + NS, '.k-filebrowser-toolbar button:not(.k-state-disabled):has(.k-delete)', proxy(that._deleteClick, that)).on(CLICK + NS, '.k-filebrowser-toolbar button:not(.k-state-disabled):has(.k-addfolder)', proxy(that._addClick, that)).on('keydown' + NS, 'li.k-state-selected input', proxy(that._directoryKeyDown, that)).on('blur' + NS, 'li.k-state-selected input', proxy(that._directoryBlur, that));
|
|
that._dataSource();
|
|
that.refresh();
|
|
that.path(that.options.path);
|
|
},
|
|
options: {
|
|
name: 'FileBrowser',
|
|
messages: {
|
|
uploadFile: 'Upload',
|
|
orderBy: 'Arrange by',
|
|
orderByName: 'Name',
|
|
orderBySize: 'Size',
|
|
directoryNotFound: 'A directory with this name was not found.',
|
|
emptyFolder: 'Empty Folder',
|
|
deleteFile: 'Are you sure you want to delete "{0}"?',
|
|
invalidFileType: 'The selected file "{0}" is not valid. Supported file types are {1}.',
|
|
overwriteFile: 'A file with name "{0}" already exists in the current directory. Do you want to overwrite it?',
|
|
dropFilesHere: 'drop file here to upload',
|
|
search: 'Search'
|
|
},
|
|
transport: {},
|
|
path: '/',
|
|
fileTypes: '*.*'
|
|
},
|
|
events: [
|
|
ERROR,
|
|
CHANGE,
|
|
APPLY
|
|
],
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.dataSource.unbind(ERROR, that._errorHandler);
|
|
that.element.add(that.list).add(that.toolbar).off(NS);
|
|
kendo.destroy(that.element);
|
|
},
|
|
value: function () {
|
|
var that = this, selected = that._selectedItem(), path, fileUrl = that.options.transport.fileUrl;
|
|
if (selected && selected.get(TYPEFIELD) === 'f') {
|
|
path = concatPaths(that.path(), selected.get(NAMEFIELD)).replace(trimSlashesRegExp, '');
|
|
if (fileUrl) {
|
|
path = isFunction(fileUrl) ? fileUrl(path) : kendo.format(fileUrl, encodeURIComponent(path));
|
|
}
|
|
return path;
|
|
}
|
|
},
|
|
_selectedItem: function () {
|
|
var listView = this.listView, selected = listView.select();
|
|
if (selected.length) {
|
|
return this.dataSource.getByUid(selected.attr(kendo.attr('uid')));
|
|
}
|
|
},
|
|
_toolbar: function () {
|
|
var that = this, template = kendo.template(TOOLBARTMPL), messages = that.options.messages, arrangeBy = [
|
|
{
|
|
text: messages.orderByName,
|
|
value: 'name'
|
|
},
|
|
{
|
|
text: messages.orderBySize,
|
|
value: 'size'
|
|
}
|
|
];
|
|
that.toolbar = $(template({
|
|
messages: messages,
|
|
showUpload: that.options.transport.uploadUrl,
|
|
showCreate: that.options.transport.create,
|
|
showDelete: that.options.transport.destroy
|
|
})).appendTo(that.element).find('.k-upload input').kendoUpload({
|
|
multiple: false,
|
|
localization: { dropFilesHere: messages.dropFilesHere },
|
|
async: {
|
|
saveUrl: that.options.transport.uploadUrl,
|
|
autoUpload: true
|
|
},
|
|
upload: proxy(that._fileUpload, that),
|
|
error: function (e) {
|
|
that._error({
|
|
xhr: e.XMLHttpRequest,
|
|
status: 'error'
|
|
});
|
|
}
|
|
}).end();
|
|
that.upload = that.toolbar.find('.k-upload input').data('kendoUpload');
|
|
that.arrangeBy = that.toolbar.find('.k-tiles-arrange select').kendoDropDownList({
|
|
dataSource: arrangeBy,
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
change: function () {
|
|
that.orderBy(this.value());
|
|
}
|
|
}).data('kendoDropDownList');
|
|
that._attachDropzoneEvents();
|
|
},
|
|
_attachDropzoneEvents: function () {
|
|
var that = this;
|
|
if (that.options.transport.uploadUrl) {
|
|
bindDragEventWrappers($(document.documentElement), $.proxy(that._dropEnter, that), $.proxy(that._dropLeave, that));
|
|
that._scrollHandler = proxy(that._positionDropzone, that);
|
|
}
|
|
},
|
|
_dropEnter: function () {
|
|
this._positionDropzone();
|
|
$(document).on('scroll' + NS, this._scrollHandler);
|
|
},
|
|
_dropLeave: function () {
|
|
this._removeDropzone();
|
|
$(document).off('scroll' + NS, this._scrollHandler);
|
|
},
|
|
_positionDropzone: function () {
|
|
var that = this, element = that.element, offset = element.offset();
|
|
that.toolbar.find('.k-dropzone').addClass('k-filebrowser-dropzone').offset(offset).css({
|
|
width: element[0].clientWidth,
|
|
height: element[0].clientHeight,
|
|
lineHeight: element[0].clientHeight + 'px'
|
|
});
|
|
},
|
|
_removeDropzone: function () {
|
|
this.toolbar.find('.k-dropzone').removeClass('k-filebrowser-dropzone').css({
|
|
width: '',
|
|
height: '',
|
|
lineHeight: '',
|
|
top: '',
|
|
left: ''
|
|
});
|
|
},
|
|
_deleteClick: function () {
|
|
var that = this, item = that.listView.select(), message = kendo.format(that.options.messages.deleteFile, item.find('strong').text());
|
|
if (item.length && that._showMessage(message, 'confirm')) {
|
|
that.listView.remove(item);
|
|
}
|
|
},
|
|
_addClick: function () {
|
|
this.createDirectory();
|
|
},
|
|
_getFieldName: function (name) {
|
|
return fieldName(this.dataSource.reader.model.fields, name);
|
|
},
|
|
_fileUpload: function (e) {
|
|
var that = this, options = that.options, fileTypes = options.fileTypes, filterRegExp = new RegExp(('(' + fileTypes.split(',').join(')|(') + ')').replace(/\*\./g, '.*.'), 'i'), fileName = e.files[0].name, fileNameField = NAMEFIELD, sizeField = SIZEFIELD, model;
|
|
if (filterRegExp.test(fileName)) {
|
|
e.data = { path: that.path() };
|
|
model = that._createFile(fileName);
|
|
if (!model) {
|
|
e.preventDefault();
|
|
} else {
|
|
that.upload.one('success', function (e) {
|
|
model.set(fileNameField, e.response[that._getFieldName(fileNameField)]);
|
|
model.set(sizeField, e.response[that._getFieldName(sizeField)]);
|
|
that._tiles = that.listView.items().filter('[' + kendo.attr('type') + '=f]');
|
|
});
|
|
}
|
|
} else {
|
|
e.preventDefault();
|
|
that._showMessage(kendo.format(options.messages.invalidFileType, fileName, fileTypes));
|
|
}
|
|
},
|
|
_findFile: function (name) {
|
|
var data = this.dataSource.data(), idx, result, typeField = TYPEFIELD, nameField = NAMEFIELD, length;
|
|
name = name.toLowerCase();
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].get(typeField) === 'f' && data[idx].get(nameField).toLowerCase() === name) {
|
|
result = data[idx];
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_createFile: function (fileName) {
|
|
var that = this, idx, length, index = 0, model = {}, typeField = TYPEFIELD, view = that.dataSource.view(), file = that._findFile(fileName);
|
|
if (file) {
|
|
if (!that._showMessage(kendo.format(that.options.messages.overwriteFile, fileName), 'confirm')) {
|
|
return null;
|
|
} else {
|
|
file._forceReload = true;
|
|
return file;
|
|
}
|
|
}
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
if (view[idx].get(typeField) === 'f') {
|
|
index = idx;
|
|
break;
|
|
}
|
|
}
|
|
model[typeField] = 'f';
|
|
model[NAMEFIELD] = fileName;
|
|
model[SIZEFIELD] = 0;
|
|
return that.dataSource.insert(++index, model);
|
|
},
|
|
createDirectory: function () {
|
|
var that = this, idx, length, lastDirectoryIdx = 0, typeField = TYPEFIELD, nameField = NAMEFIELD, view = that.dataSource.data(), name = that._nameDirectory(), model = new that.dataSource.reader.model();
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
if (view[idx].get(typeField) === 'd') {
|
|
lastDirectoryIdx = idx;
|
|
}
|
|
}
|
|
model.set(typeField, 'd');
|
|
model.set(nameField, name);
|
|
that.listView.one('dataBound', function () {
|
|
var selected = that.listView.items().filter('[' + kendo.attr('uid') + '=' + model.uid + ']'), input = selected.find('input');
|
|
if (selected.length) {
|
|
this.edit(selected);
|
|
}
|
|
this.element.scrollTop(selected.attr('offsetTop') - this.element[0].offsetHeight);
|
|
setTimeout(function () {
|
|
input.select();
|
|
});
|
|
}).one('save', function (e) {
|
|
var value = e.model.get(nameField);
|
|
if (!value) {
|
|
e.model.set(nameField, name);
|
|
} else {
|
|
e.model.set(nameField, that._nameExists(value, model.uid) ? that._nameDirectory() : value);
|
|
}
|
|
});
|
|
that.dataSource.insert(++lastDirectoryIdx, model);
|
|
},
|
|
_directoryKeyDown: function (e) {
|
|
if (e.keyCode == 13) {
|
|
e.currentTarget.blur();
|
|
}
|
|
},
|
|
_directoryBlur: function () {
|
|
this.listView.save();
|
|
},
|
|
_nameExists: function (name, uid) {
|
|
var data = this.dataSource.data(), typeField = TYPEFIELD, nameField = NAMEFIELD, idx, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].get(typeField) === 'd' && data[idx].get(nameField).toLowerCase() === name.toLowerCase() && data[idx].uid !== uid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_nameDirectory: function () {
|
|
var name = 'New folder', data = this.dataSource.data(), directoryNames = [], typeField = TYPEFIELD, nameField = NAMEFIELD, candidate, idx, length;
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
if (data[idx].get(typeField) === 'd' && data[idx].get(nameField).toLowerCase().indexOf(name.toLowerCase()) > -1) {
|
|
directoryNames.push(data[idx].get(nameField));
|
|
}
|
|
}
|
|
if ($.inArray(name, directoryNames) > -1) {
|
|
idx = 2;
|
|
do {
|
|
candidate = name + ' (' + idx + ')';
|
|
idx++;
|
|
} while ($.inArray(candidate, directoryNames) > -1);
|
|
name = candidate;
|
|
}
|
|
return name;
|
|
},
|
|
orderBy: function (field) {
|
|
this.dataSource.sort([
|
|
{
|
|
field: TYPEFIELD,
|
|
dir: 'asc'
|
|
},
|
|
{
|
|
field: field,
|
|
dir: 'asc'
|
|
}
|
|
]);
|
|
},
|
|
search: function (name) {
|
|
this.dataSource.filter({
|
|
field: NAMEFIELD,
|
|
operator: 'contains',
|
|
value: name
|
|
});
|
|
},
|
|
_content: function () {
|
|
var that = this;
|
|
that.list = $('<ul class="k-reset k-floats k-tiles" />').appendTo(that.element).on('dblclick' + NS, 'li', proxy(that._dblClick, that));
|
|
that.listView = new kendo.ui.ListView(that.list, {
|
|
dataSource: that.dataSource,
|
|
template: that._itemTmpl(),
|
|
editTemplate: that._editTmpl(),
|
|
selectable: true,
|
|
autoBind: false,
|
|
dataBinding: function (e) {
|
|
that.toolbar.find('.k-delete').parent().addClass('k-state-disabled');
|
|
if (e.action === 'remove' || e.action === 'sync') {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
dataBound: function () {
|
|
if (that.dataSource.view().length) {
|
|
that._tiles = this.items().filter('[' + kendo.attr('type') + '=f]');
|
|
} else {
|
|
this.wrapper.append(EMPTYTILE({ text: that.options.messages.emptyFolder }));
|
|
}
|
|
},
|
|
change: proxy(that._listViewChange, that)
|
|
});
|
|
},
|
|
_dblClick: function (e) {
|
|
var that = this, li = $(e.currentTarget);
|
|
if (li.hasClass('k-edit-item')) {
|
|
that._directoryBlur();
|
|
}
|
|
if (li.filter('[' + kendo.attr('type') + '=d]').length) {
|
|
var folder = that.dataSource.getByUid(li.attr(kendo.attr('uid')));
|
|
if (folder) {
|
|
that.path(concatPaths(that.path(), folder.get(NAMEFIELD)));
|
|
that.breadcrumbs.value(that.path());
|
|
}
|
|
} else if (li.filter('[' + kendo.attr('type') + '=f]').length) {
|
|
that.trigger(APPLY);
|
|
}
|
|
},
|
|
_listViewChange: function () {
|
|
var selected = this._selectedItem();
|
|
if (selected) {
|
|
this.toolbar.find('.k-delete').parent().removeClass('k-state-disabled');
|
|
if (selected.get(TYPEFIELD) === 'f') {
|
|
this.trigger(CHANGE);
|
|
}
|
|
}
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, options = that.options, transport = options.transport, typeSortOrder = extend({}, DEFAULTSORTORDER), nameSortOrder = {
|
|
field: NAMEFIELD,
|
|
dir: 'asc'
|
|
}, schema, dataSource = {
|
|
type: transport.type || 'filebrowser',
|
|
sort: [
|
|
typeSortOrder,
|
|
nameSortOrder
|
|
]
|
|
};
|
|
if (isPlainObject(transport)) {
|
|
transport.path = proxy(that.path, that);
|
|
dataSource.transport = transport;
|
|
}
|
|
if (isPlainObject(options.schema)) {
|
|
dataSource.schema = options.schema;
|
|
} else if (transport.type && isPlainObject(kendo.data.schemas[transport.type])) {
|
|
schema = kendo.data.schemas[transport.type];
|
|
}
|
|
if (that.dataSource && that._errorHandler) {
|
|
that.dataSource.unbind(ERROR, that._errorHandler);
|
|
} else {
|
|
that._errorHandler = proxy(that._error, that);
|
|
}
|
|
that.dataSource = kendo.data.DataSource.create(dataSource).bind(ERROR, that._errorHandler);
|
|
},
|
|
_navigation: function () {
|
|
var that = this, navigation = $('<div class="k-floatwrap"><input/><input/></div>').appendTo(this.element);
|
|
that.breadcrumbs = navigation.find('input:first').kendoBreadcrumbs({
|
|
value: that.options.path,
|
|
change: function () {
|
|
that.path(this.value());
|
|
}
|
|
}).data('kendoBreadcrumbs');
|
|
that.searchBox = navigation.parent().find('input:last').kendoSearchBox({
|
|
label: that.options.messages.search,
|
|
change: function () {
|
|
that.search(this.value());
|
|
}
|
|
}).data('kendoSearchBox');
|
|
},
|
|
_error: function (e) {
|
|
var that = this, status;
|
|
if (!that.trigger(ERROR, e)) {
|
|
status = e.xhr.status;
|
|
if (e.status == 'error') {
|
|
if (status == '404') {
|
|
that._showMessage(that.options.messages.directoryNotFound);
|
|
} else if (status != '0') {
|
|
that._showMessage('Error! The requested URL returned ' + status + ' - ' + e.xhr.statusText);
|
|
}
|
|
} else if (status == 'timeout') {
|
|
that._showMessage('Error! Server timeout.');
|
|
}
|
|
}
|
|
},
|
|
_showMessage: function (message, type) {
|
|
return window[type || 'alert'](message);
|
|
},
|
|
refresh: function () {
|
|
var that = this;
|
|
that._navigation();
|
|
that._toolbar();
|
|
that._content();
|
|
},
|
|
_editTmpl: function () {
|
|
var html = '<li class="k-tile k-state-selected" ' + kendo.attr('uid') + '="#=uid#" ';
|
|
html += kendo.attr('type') + '="${' + TYPEFIELD + '}">';
|
|
html += '#if(' + TYPEFIELD + ' == "d") { #';
|
|
html += '<div class="k-thumb"><span class="k-icon k-folder"></span></div>';
|
|
html += '#}else{#';
|
|
html += '<div class="k-thumb"><span class="k-icon k-loading"></span></div>';
|
|
html += '#}#';
|
|
html += '#if(' + TYPEFIELD + ' == "d") { #';
|
|
html += '<input class="k-input" ' + kendo.attr('bind') + '="value:' + NAMEFIELD + '"/>';
|
|
html += '#}#';
|
|
html += '</li>';
|
|
return proxy(kendo.template(html), { sizeFormatter: sizeFormatter });
|
|
},
|
|
_itemTmpl: function () {
|
|
var html = '<li class="k-tile" ' + kendo.attr('uid') + '="#=uid#" ';
|
|
html += kendo.attr('type') + '="${' + TYPEFIELD + '}">';
|
|
html += '#if(' + TYPEFIELD + ' == "d") { #';
|
|
html += '<div class="k-thumb"><span class="k-icon k-folder"></span></div>';
|
|
html += '#}else{#';
|
|
html += '<div class="k-thumb"><span class="k-icon k-file"></span></div>';
|
|
html += '#}#';
|
|
html += '<strong>${' + NAMEFIELD + '}</strong>';
|
|
html += '#if(' + TYPEFIELD + ' == "f") { # <span class="k-filesize">${this.sizeFormatter(' + SIZEFIELD + ')}</span> #}#';
|
|
html += '</li>';
|
|
return proxy(kendo.template(html), { sizeFormatter: sizeFormatter });
|
|
},
|
|
path: function (value) {
|
|
var that = this, path = that._path || '';
|
|
if (value !== undefined) {
|
|
that._path = value.replace(trimSlashesRegExp, '') + '/';
|
|
that.dataSource.read({ path: that._path });
|
|
return;
|
|
}
|
|
if (path) {
|
|
path = path.replace(trimSlashesRegExp, '');
|
|
}
|
|
return path === '/' || path === '' ? '' : path + '/';
|
|
}
|
|
});
|
|
var SearchBox = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = options || {};
|
|
Widget.fn.init.call(that, element, options);
|
|
if (placeholderSupported) {
|
|
that.element.attr('placeholder', that.options.label);
|
|
}
|
|
that._wrapper();
|
|
that.element.on('keydown' + SEARCHBOXNS, proxy(that._keydown, that)).on('change' + SEARCHBOXNS, proxy(that._updateValue, that));
|
|
that.wrapper.on(CLICK + SEARCHBOXNS, 'a', proxy(that._click, that));
|
|
if (!placeholderSupported) {
|
|
that.element.on('focus' + SEARCHBOXNS, proxy(that._focus, that)).on('blur' + SEARCHBOXNS, proxy(that._blur, that));
|
|
}
|
|
},
|
|
options: {
|
|
name: 'SearchBox',
|
|
label: 'Search',
|
|
value: ''
|
|
},
|
|
events: [CHANGE],
|
|
destroy: function () {
|
|
var that = this;
|
|
that.wrapper.add(that.element).add(that.label).off(SEARCHBOXNS);
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
_keydown: function (e) {
|
|
if (e.keyCode === 13) {
|
|
this._updateValue();
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
e.preventDefault();
|
|
this._updateValue();
|
|
},
|
|
_updateValue: function () {
|
|
var that = this, value = that.element.val();
|
|
if (value !== that.value()) {
|
|
that.value(value);
|
|
that.trigger(CHANGE);
|
|
}
|
|
},
|
|
_blur: function () {
|
|
this._updateValue();
|
|
this._toggleLabel();
|
|
},
|
|
_toggleLabel: function () {
|
|
if (!placeholderSupported) {
|
|
this.label.toggle(!this.element.val());
|
|
}
|
|
},
|
|
_focus: function () {
|
|
this.label.hide();
|
|
},
|
|
_wrapper: function () {
|
|
var element = this.element, wrapper = element.parents('.k-search-wrap');
|
|
element[0].style.width = '';
|
|
element.addClass('k-input');
|
|
if (!wrapper.length) {
|
|
wrapper = element.wrap($('<div class="k-widget k-search-wrap k-textbox"/>')).parent();
|
|
if (!placeholderSupported) {
|
|
$('<label style="display:block">' + this.options.label + '</label>').insertBefore(element);
|
|
}
|
|
$('<a href="#" class="k-icon k-i-search k-search"/>').appendTo(wrapper);
|
|
}
|
|
this.wrapper = wrapper;
|
|
this.label = wrapper.find('>label');
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value !== undefined) {
|
|
that.options.value = value;
|
|
that.element.val(value);
|
|
that._toggleLabel();
|
|
return;
|
|
}
|
|
return that.options.value;
|
|
}
|
|
});
|
|
var Breadcrumbs = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = options || {};
|
|
Widget.fn.init.call(that, element, options);
|
|
that._wrapper();
|
|
that.wrapper.on('focus' + BREADCRUBMSNS, 'input', proxy(that._focus, that)).on('blur' + BREADCRUBMSNS, 'input', proxy(that._blur, that)).on('keydown' + BREADCRUBMSNS, 'input', proxy(that._keydown, that)).on(CLICK + BREADCRUBMSNS, 'a.k-i-arrow-n:first', proxy(that._rootClick, that)).on(CLICK + BREADCRUBMSNS, 'a:not(.k-i-arrow-n)', proxy(that._click, that));
|
|
that.value(that.options.value);
|
|
},
|
|
options: {
|
|
name: 'Breadcrumbs',
|
|
gap: 50
|
|
},
|
|
events: [CHANGE],
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.wrapper.add(that.wrapper.find('input')).add(that.wrapper.find('a')).off(BREADCRUBMSNS);
|
|
},
|
|
_update: function (val) {
|
|
val = (val || '').charAt(0) === '/' ? val : '/' + (val || '');
|
|
if (val !== this.value()) {
|
|
this.value(val);
|
|
this.trigger(CHANGE);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
e.preventDefault();
|
|
this._update(this._path($(e.target).prevAll('a:not(.k-i-arrow-n)').addBack()));
|
|
},
|
|
_rootClick: function (e) {
|
|
e.preventDefault();
|
|
this._update('');
|
|
},
|
|
_focus: function () {
|
|
var that = this, element = that.element;
|
|
that.overlay.hide();
|
|
that.element.val(that.value());
|
|
setTimeout(function () {
|
|
element.select();
|
|
});
|
|
},
|
|
_blur: function () {
|
|
if (this.overlay.is(':visible')) {
|
|
return;
|
|
}
|
|
var that = this, element = that.element, val = element.val().replace(/\/{2,}/g, '/');
|
|
that.overlay.show();
|
|
element.val('');
|
|
that._update(val);
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this;
|
|
if (e.keyCode === 13) {
|
|
that._blur();
|
|
setTimeout(function () {
|
|
that.overlay.find('a:first').focus();
|
|
});
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var element = this.element, wrapper = element.parents('.k-breadcrumbs'), overlay;
|
|
element[0].style.width = '';
|
|
element.addClass('k-input');
|
|
if (!wrapper.length) {
|
|
wrapper = element.wrap($('<div class="k-widget k-breadcrumbs k-textbox"/>')).parent();
|
|
}
|
|
overlay = wrapper.find('.k-breadcrumbs-wrap');
|
|
if (!overlay.length) {
|
|
overlay = $('<div class="k-breadcrumbs-wrap"/>').appendTo(wrapper);
|
|
}
|
|
this.wrapper = wrapper;
|
|
this.overlay = overlay;
|
|
},
|
|
refresh: function () {
|
|
var html = '', value = this.value(), segments, segment, idx, length;
|
|
if (value === undefined || !value.match(/^\//)) {
|
|
value = '/' + (value || '');
|
|
}
|
|
segments = value.split('/');
|
|
for (idx = 0, length = segments.length; idx < length; idx++) {
|
|
segment = segments[idx];
|
|
if (segment) {
|
|
if (!html) {
|
|
html += '<a href="#" class="k-icon k-i-arrow-n">root</a>';
|
|
}
|
|
html += '<a class="k-link" href="#">' + segments[idx] + '</a>';
|
|
html += '<span class="k-icon k-i-arrow-e">></span>';
|
|
}
|
|
}
|
|
this.overlay.empty().append($(html));
|
|
this._adjustSectionWidth();
|
|
},
|
|
_adjustSectionWidth: function () {
|
|
var that = this, wrapper = that.wrapper, width = wrapper.width() - that.options.gap, links = that.overlay.find('a'), a;
|
|
links.each(function (index) {
|
|
a = $(this);
|
|
if (a.parent().width() > width) {
|
|
if (index == links.length - 1) {
|
|
a.width(width);
|
|
} else {
|
|
a.prev().addBack().hide();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
value: function (val) {
|
|
if (val !== undefined) {
|
|
this._value = val.replace(/\/{2,}/g, '/');
|
|
this.refresh();
|
|
return;
|
|
}
|
|
return this._value;
|
|
},
|
|
_path: function (trail) {
|
|
return '/' + $.map(trail, function (b) {
|
|
return $(b).text();
|
|
}).join('/');
|
|
}
|
|
});
|
|
kendo.ui.plugin(FileBrowser);
|
|
kendo.ui.plugin(Breadcrumbs);
|
|
kendo.ui.plugin(SearchBox);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.imagebrowser', ['kendo.filebrowser'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'imagebrowser',
|
|
name: 'ImageBrowser',
|
|
category: 'web',
|
|
description: '',
|
|
hidden: true,
|
|
depends: ['filebrowser']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, FileBrowser = kendo.ui.FileBrowser, isPlainObject = $.isPlainObject, proxy = $.proxy, extend = $.extend, browser = kendo.support.browser, isFunction = kendo.isFunction, trimSlashesRegExp = /(^\/|\/$)/g, ERROR = 'error', NS = '.kendoImageBrowser', NAMEFIELD = 'name', SIZEFIELD = 'size', TYPEFIELD = 'type', DEFAULTSORTORDER = {
|
|
field: TYPEFIELD,
|
|
dir: 'asc'
|
|
}, EMPTYTILE = kendo.template('<li class="k-tile-empty"><strong>${text}</strong></li>');
|
|
extend(true, kendo.data, {
|
|
schemas: {
|
|
'imagebrowser': {
|
|
data: function (data) {
|
|
return data.items || data || [];
|
|
},
|
|
model: {
|
|
id: 'name',
|
|
fields: {
|
|
name: 'name',
|
|
size: 'size',
|
|
type: 'type'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
extend(true, kendo.data, {
|
|
transports: {
|
|
'imagebrowser': kendo.data.RemoteTransport.extend({
|
|
init: function (options) {
|
|
kendo.data.RemoteTransport.fn.init.call(this, $.extend(true, {}, this.options, options));
|
|
},
|
|
_call: function (type, options) {
|
|
options.data = $.extend({}, options.data, { path: this.options.path() });
|
|
if (isFunction(this.options[type])) {
|
|
this.options[type].call(this, options);
|
|
} else {
|
|
kendo.data.RemoteTransport.fn[type].call(this, options);
|
|
}
|
|
},
|
|
read: function (options) {
|
|
this._call('read', options);
|
|
},
|
|
create: function (options) {
|
|
this._call('create', options);
|
|
},
|
|
destroy: function (options) {
|
|
this._call('destroy', options);
|
|
},
|
|
update: function () {
|
|
},
|
|
options: {
|
|
read: { type: 'POST' },
|
|
update: { type: 'POST' },
|
|
create: { type: 'POST' },
|
|
destroy: { type: 'POST' }
|
|
}
|
|
})
|
|
}
|
|
});
|
|
var offsetTop;
|
|
if (browser.msie && browser.version < 8) {
|
|
offsetTop = function (element) {
|
|
return element.offsetTop;
|
|
};
|
|
} else {
|
|
offsetTop = function (element) {
|
|
return element.offsetTop - $(element).height();
|
|
};
|
|
}
|
|
function concatPaths(path, name) {
|
|
if (path === undefined || !path.match(/\/$/)) {
|
|
path = (path || '') + '/';
|
|
}
|
|
return path + name;
|
|
}
|
|
function sizeFormatter(value) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
var suffix = ' bytes';
|
|
if (value >= 1073741824) {
|
|
suffix = ' GB';
|
|
value /= 1073741824;
|
|
} else if (value >= 1048576) {
|
|
suffix = ' MB';
|
|
value /= 1048576;
|
|
} else if (value >= 1024) {
|
|
suffix = ' KB';
|
|
value /= 1024;
|
|
}
|
|
return Math.round(value * 100) / 100 + suffix;
|
|
}
|
|
var ImageBrowser = FileBrowser.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = options || {};
|
|
FileBrowser.fn.init.call(that, element, options);
|
|
that.element.addClass('k-imagebrowser');
|
|
},
|
|
options: {
|
|
name: 'ImageBrowser',
|
|
fileTypes: '*.png,*.gif,*.jpg,*.jpeg'
|
|
},
|
|
value: function () {
|
|
var that = this, selected = that._selectedItem(), path, imageUrl = that.options.transport.imageUrl;
|
|
if (selected && selected.get(TYPEFIELD) === 'f') {
|
|
path = concatPaths(that.path(), selected.get(NAMEFIELD)).replace(trimSlashesRegExp, '');
|
|
if (imageUrl) {
|
|
path = isFunction(imageUrl) ? imageUrl(path) : kendo.format(imageUrl, encodeURIComponent(path));
|
|
}
|
|
return path;
|
|
}
|
|
},
|
|
_fileUpload: function (e) {
|
|
var that = this, options = that.options, fileTypes = options.fileTypes, filterRegExp = new RegExp(('(' + fileTypes.split(',').join(')|(') + ')').replace(/\*\./g, '.*.'), 'i'), fileName = e.files[0].name, fileNameField = NAMEFIELD, sizeField = SIZEFIELD, model;
|
|
if (filterRegExp.test(fileName)) {
|
|
e.data = { path: that.path() };
|
|
model = that._createFile(fileName);
|
|
if (!model) {
|
|
e.preventDefault();
|
|
} else {
|
|
model._uploading = true;
|
|
that.upload.one('success', function (e) {
|
|
delete model._uploading;
|
|
model.set(fileNameField, e.response[that._getFieldName(fileNameField)]);
|
|
model.set(sizeField, e.response[that._getFieldName(sizeField)]);
|
|
that._tiles = that.listView.items().filter('[' + kendo.attr('type') + '=f]');
|
|
that._scroll();
|
|
});
|
|
}
|
|
} else {
|
|
e.preventDefault();
|
|
that._showMessage(kendo.format(options.messages.invalidFileType, fileName, fileTypes));
|
|
}
|
|
},
|
|
_content: function () {
|
|
var that = this;
|
|
that.list = $('<ul class="k-reset k-floats k-tiles" />').appendTo(that.element).on('scroll' + NS, proxy(that._scroll, that)).on('dblclick' + NS, 'li', proxy(that._dblClick, that));
|
|
that.listView = new kendo.ui.ListView(that.list, {
|
|
dataSource: that.dataSource,
|
|
template: that._itemTmpl(),
|
|
editTemplate: that._editTmpl(),
|
|
selectable: true,
|
|
autoBind: false,
|
|
dataBinding: function (e) {
|
|
that.toolbar.find('.k-delete').parent().addClass('k-state-disabled');
|
|
if (e.action === 'remove' || e.action === 'sync') {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
dataBound: function () {
|
|
if (that.dataSource.view().length) {
|
|
that._tiles = this.items().filter('[' + kendo.attr('type') + '=f]');
|
|
that._scroll();
|
|
} else {
|
|
this.wrapper.append(EMPTYTILE({ text: that.options.messages.emptyFolder }));
|
|
}
|
|
},
|
|
change: proxy(that._listViewChange, that)
|
|
});
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, options = that.options, transport = options.transport, typeSortOrder = extend({}, DEFAULTSORTORDER), nameSortOrder = {
|
|
field: NAMEFIELD,
|
|
dir: 'asc'
|
|
}, schema, dataSource = {
|
|
type: transport.type || 'imagebrowser',
|
|
sort: [
|
|
typeSortOrder,
|
|
nameSortOrder
|
|
]
|
|
};
|
|
if (isPlainObject(transport)) {
|
|
transport.path = proxy(that.path, that);
|
|
dataSource.transport = transport;
|
|
}
|
|
if (isPlainObject(options.schema)) {
|
|
dataSource.schema = options.schema;
|
|
} else if (transport.type && isPlainObject(kendo.data.schemas[transport.type])) {
|
|
schema = kendo.data.schemas[transport.type];
|
|
}
|
|
if (that.dataSource && that._errorHandler) {
|
|
that.dataSource.unbind(ERROR, that._errorHandler);
|
|
} else {
|
|
that._errorHandler = proxy(that._error, that);
|
|
}
|
|
that.dataSource = kendo.data.DataSource.create(dataSource).bind(ERROR, that._errorHandler);
|
|
},
|
|
_loadImage: function (li) {
|
|
var that = this, element = $(li), dataItem = that.dataSource.getByUid(element.attr(kendo.attr('uid'))), name = dataItem.get(NAMEFIELD), thumbnailUrl = that.options.transport.thumbnailUrl, img = $('<img />', { alt: name }), urlJoin = '?';
|
|
if (dataItem._uploading) {
|
|
return;
|
|
}
|
|
img.hide().on('load' + NS, function () {
|
|
$(this).prev().remove().end().addClass('k-image').fadeIn();
|
|
});
|
|
element.find('.k-loading').after(img);
|
|
if (isFunction(thumbnailUrl)) {
|
|
thumbnailUrl = thumbnailUrl(that.path(), encodeURIComponent(name));
|
|
} else {
|
|
if (thumbnailUrl.indexOf('?') >= 0) {
|
|
urlJoin = '&';
|
|
}
|
|
thumbnailUrl = thumbnailUrl + urlJoin + 'path=' + encodeURIComponent(that.path() + name);
|
|
if (dataItem._forceReload) {
|
|
thumbnailUrl += '&_=' + new Date().getTime();
|
|
delete dataItem._forceReload;
|
|
}
|
|
}
|
|
img.attr('src', thumbnailUrl);
|
|
li.loaded = true;
|
|
},
|
|
_scroll: function () {
|
|
var that = this;
|
|
if (that.options.transport && that.options.transport.thumbnailUrl) {
|
|
clearTimeout(that._timeout);
|
|
that._timeout = setTimeout(function () {
|
|
var height = that.list.outerHeight(), viewTop = that.list.scrollTop(), viewBottom = viewTop + height;
|
|
that._tiles.each(function () {
|
|
var top = offsetTop(this), bottom = top + this.offsetHeight;
|
|
if (top >= viewTop && top < viewBottom || bottom >= viewTop && bottom < viewBottom) {
|
|
that._loadImage(this);
|
|
}
|
|
if (top > viewBottom) {
|
|
return false;
|
|
}
|
|
});
|
|
that._tiles = that._tiles.filter(function () {
|
|
return !this.loaded;
|
|
});
|
|
}, 250);
|
|
}
|
|
},
|
|
_itemTmpl: function () {
|
|
var that = this, html = '<li class="k-tile" ' + kendo.attr('uid') + '="#=uid#" ';
|
|
html += kendo.attr('type') + '="${' + TYPEFIELD + '}">';
|
|
html += '#if(' + TYPEFIELD + ' == "d") { #';
|
|
html += '<div class="k-thumb"><span class="k-icon k-folder"></span></div>';
|
|
html += '#}else{#';
|
|
if (that.options.transport && that.options.transport.thumbnailUrl) {
|
|
html += '<div class="k-thumb"><span class="k-icon k-loading"></span></div>';
|
|
} else {
|
|
html += '<div class="k-thumb"><span class="k-icon k-file"></span></div>';
|
|
}
|
|
html += '#}#';
|
|
html += '<strong>${' + NAMEFIELD + '}</strong>';
|
|
html += '#if(' + TYPEFIELD + ' == "f") { # <span class="k-filesize">${this.sizeFormatter(' + SIZEFIELD + ')}</span> #}#';
|
|
html += '</li>';
|
|
return proxy(kendo.template(html), { sizeFormatter: sizeFormatter });
|
|
}
|
|
});
|
|
kendo.ui.plugin(ImageBrowser);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/undoredostack', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
var UndoRedoStack = kendo.Observable.extend({
|
|
init: function (options) {
|
|
kendo.Observable.fn.init.call(this, options);
|
|
this.clear();
|
|
},
|
|
events: [
|
|
'undo',
|
|
'redo'
|
|
],
|
|
push: function (command) {
|
|
this.stack = this.stack.slice(0, this.currentCommandIndex + 1);
|
|
this.currentCommandIndex = this.stack.push(command) - 1;
|
|
},
|
|
undo: function () {
|
|
if (this.canUndo()) {
|
|
var command = this.stack[this.currentCommandIndex--];
|
|
command.undo();
|
|
this.trigger('undo', { command: command });
|
|
}
|
|
},
|
|
redo: function () {
|
|
if (this.canRedo()) {
|
|
var command = this.stack[++this.currentCommandIndex];
|
|
command.redo();
|
|
this.trigger('redo', { command: command });
|
|
}
|
|
},
|
|
clear: function () {
|
|
this.stack = [];
|
|
this.currentCommandIndex = -1;
|
|
},
|
|
canUndo: function () {
|
|
return this.currentCommandIndex >= 0;
|
|
},
|
|
canRedo: function () {
|
|
return this.currentCommandIndex != this.stack.length - 1;
|
|
}
|
|
});
|
|
kendo.deepExtend(kendo, { util: { UndoRedoStack: UndoRedoStack } });
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/main', [
|
|
'util/undoredostack',
|
|
'kendo.combobox',
|
|
'kendo.dropdownlist',
|
|
'kendo.window',
|
|
'kendo.colorpicker'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, os = kendo.support.mobileOS, browser = kendo.support.browser, extend = $.extend, proxy = $.proxy, deepExtend = kendo.deepExtend, keys = kendo.keys;
|
|
var ToolTemplate = Class.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
},
|
|
getHtml: function () {
|
|
var options = this.options;
|
|
return kendo.template(options.template, { useWithBlock: false })(options);
|
|
}
|
|
});
|
|
var EditorUtils = {
|
|
editorWrapperTemplate: '<table cellspacing="4" cellpadding="0" class="k-widget k-editor k-header" role="presentation"><tbody>' + '<tr role="presentation"><td class="k-editor-toolbar-wrap" role="presentation"><ul class="k-editor-toolbar" role="toolbar" /></td></tr>' + '<tr><td class="k-editable-area" /></tr>' + '</tbody></table>',
|
|
buttonTemplate: '<a href="" role="button" class="k-tool"' + '#= data.popup ? " data-popup" : "" #' + ' unselectable="on" title="#= data.title #"><span unselectable="on" class="k-tool-icon #= data.cssClass #"></span><span class="k-tool-text">#= data.title #</span></a>',
|
|
colorPickerTemplate: '<div class="k-colorpicker #= data.cssClass #" />',
|
|
comboBoxTemplate: '<select title="#= data.title #" class="#= data.cssClass #" />',
|
|
dropDownListTemplate: '<span class="k-editor-dropdown"><select title="#= data.title #" class="#= data.cssClass #" /></span>',
|
|
separatorTemplate: '<span class="k-separator" />',
|
|
overflowAnchorTemplate: '<a href="" role="button" class="k-tool k-overflow-anchor" data-popup' + ' unselectable="on"><span unselectable="on" class="k-icon k-i-more"></span></a>',
|
|
formatByName: function (name, format) {
|
|
for (var i = 0; i < format.length; i++) {
|
|
if ($.inArray(name, format[i].tags) >= 0) {
|
|
return format[i];
|
|
}
|
|
}
|
|
},
|
|
registerTool: function (toolName, tool) {
|
|
var toolOptions = tool.options;
|
|
if (toolOptions && toolOptions.template) {
|
|
toolOptions.template.options.cssClass = 'k-' + toolName;
|
|
}
|
|
if (!tool.name) {
|
|
tool.options.name = toolName;
|
|
tool.name = toolName.toLowerCase();
|
|
}
|
|
Editor.defaultTools[toolName] = tool;
|
|
},
|
|
registerFormat: function (formatName, format) {
|
|
Editor.fn.options.formats[formatName] = format;
|
|
}
|
|
};
|
|
var messages = {
|
|
bold: 'Bold',
|
|
italic: 'Italic',
|
|
underline: 'Underline',
|
|
strikethrough: 'Strikethrough',
|
|
superscript: 'Superscript',
|
|
subscript: 'Subscript',
|
|
justifyCenter: 'Center text',
|
|
justifyLeft: 'Align text left',
|
|
justifyRight: 'Align text right',
|
|
justifyFull: 'Justify',
|
|
insertUnorderedList: 'Insert unordered list',
|
|
insertOrderedList: 'Insert ordered list',
|
|
indent: 'Indent',
|
|
outdent: 'Outdent',
|
|
createLink: 'Insert hyperlink',
|
|
unlink: 'Remove hyperlink',
|
|
insertImage: 'Insert image',
|
|
insertFile: 'Insert file',
|
|
insertHtml: 'Insert HTML',
|
|
viewHtml: 'View HTML',
|
|
fontName: 'Select font family',
|
|
fontNameInherit: '(inherited font)',
|
|
fontSize: 'Select font size',
|
|
fontSizeInherit: '(inherited size)',
|
|
formatBlock: 'Format',
|
|
formatting: 'Format',
|
|
foreColor: 'Color',
|
|
backColor: 'Background color',
|
|
style: 'Styles',
|
|
emptyFolder: 'Empty Folder',
|
|
editAreaTitle: 'Editable area. Press F10 for toolbar.',
|
|
uploadFile: 'Upload',
|
|
orderBy: 'Arrange by:',
|
|
orderBySize: 'Size',
|
|
orderByName: 'Name',
|
|
invalidFileType: 'The selected file "{0}" is not valid. Supported file types are {1}.',
|
|
deleteFile: 'Are you sure you want to delete "{0}"?',
|
|
overwriteFile: 'A file with name "{0}" already exists in the current directory. Do you want to overwrite it?',
|
|
directoryNotFound: 'A directory with this name was not found.',
|
|
imageWebAddress: 'Web address',
|
|
imageAltText: 'Alternate text',
|
|
imageWidth: 'Width (px)',
|
|
imageHeight: 'Height (px)',
|
|
fileWebAddress: 'Web address',
|
|
fileTitle: 'Title',
|
|
linkWebAddress: 'Web address',
|
|
linkText: 'Text',
|
|
linkToolTip: 'ToolTip',
|
|
linkOpenInNewWindow: 'Open link in new window',
|
|
dialogUpdate: 'Update',
|
|
dialogInsert: 'Insert',
|
|
dialogCancel: 'Cancel',
|
|
createTable: 'Create table',
|
|
createTableHint: 'Create a {0} x {1} table',
|
|
addColumnLeft: 'Add column on the left',
|
|
addColumnRight: 'Add column on the right',
|
|
addRowAbove: 'Add row above',
|
|
addRowBelow: 'Add row below',
|
|
deleteRow: 'Delete row',
|
|
deleteColumn: 'Delete column'
|
|
};
|
|
var supportedBrowser = !os || os.ios && os.flatVersion >= 500 || !os.ios && typeof document.documentElement.contentEditable != 'undefined';
|
|
var toolGroups = {
|
|
basic: [
|
|
'bold',
|
|
'italic',
|
|
'underline'
|
|
],
|
|
alignment: [
|
|
'justifyLeft',
|
|
'justifyCenter',
|
|
'justifyRight'
|
|
],
|
|
lists: [
|
|
'insertUnorderedList',
|
|
'insertOrderedList'
|
|
],
|
|
indenting: [
|
|
'indent',
|
|
'outdent'
|
|
],
|
|
links: [
|
|
'createLink',
|
|
'unlink'
|
|
],
|
|
tables: [
|
|
'createTable',
|
|
'addColumnLeft',
|
|
'addColumnRight',
|
|
'addRowAbove',
|
|
'addRowBelow',
|
|
'deleteRow',
|
|
'deleteColumn'
|
|
]
|
|
};
|
|
var Editor = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, value, editorNS = kendo.ui.editor, toolbarContainer, toolbarOptions, type;
|
|
var domElement;
|
|
var dom = editorNS.Dom;
|
|
if (!supportedBrowser) {
|
|
return;
|
|
}
|
|
Widget.fn.init.call(that, element, options);
|
|
that.options = deepExtend({}, that.options, options);
|
|
that.options.tools = that.options.tools.slice();
|
|
element = that.element;
|
|
domElement = element[0];
|
|
type = dom.name(domElement);
|
|
this._registerHandler(element.closest('form'), 'submit', proxy(that.update, that, undefined));
|
|
toolbarOptions = extend({}, that.options);
|
|
toolbarOptions.editor = that;
|
|
if (type == 'textarea') {
|
|
that._wrapTextarea();
|
|
toolbarContainer = that.wrapper.find('.k-editor-toolbar');
|
|
if (domElement.id) {
|
|
toolbarContainer.attr('aria-controls', domElement.id);
|
|
}
|
|
} else {
|
|
that.element.attr('contenteditable', true).addClass('k-widget k-editor k-editor-inline');
|
|
toolbarOptions.popup = true;
|
|
toolbarContainer = $('<ul class="k-editor-toolbar" role="toolbar" />').insertBefore(element);
|
|
}
|
|
that.toolbar = new editorNS.Toolbar(toolbarContainer[0], toolbarOptions);
|
|
that.toolbar.bindTo(that);
|
|
if (type == 'textarea') {
|
|
setTimeout(function () {
|
|
var heightStyle = that.wrapper[0].style.height;
|
|
var expectedHeight = parseInt(heightStyle, 10);
|
|
var actualHeight = that.wrapper.height();
|
|
if (heightStyle.indexOf('px') > 0 && !isNaN(expectedHeight) && actualHeight > expectedHeight) {
|
|
that.wrapper.height(expectedHeight - (actualHeight - expectedHeight));
|
|
}
|
|
});
|
|
}
|
|
that._resizable();
|
|
that._initializeContentElement(that);
|
|
that.keyboard = new editorNS.Keyboard([
|
|
new editorNS.BackspaceHandler(that),
|
|
new editorNS.TypingHandler(that),
|
|
new editorNS.SystemHandler(that)
|
|
]);
|
|
that.clipboard = new editorNS.Clipboard(this);
|
|
that.undoRedoStack = new kendo.util.UndoRedoStack();
|
|
if (options && options.value) {
|
|
value = options.value;
|
|
} else if (that.textarea) {
|
|
value = domElement.value;
|
|
if (that.options.encoded && $.trim(domElement.defaultValue).length) {
|
|
value = domElement.defaultValue;
|
|
}
|
|
value = value.replace(/[\r\n\v\f\t ]+/gi, ' ');
|
|
} else {
|
|
value = domElement.innerHTML;
|
|
}
|
|
that.value(value || kendo.ui.editor.emptyElementContent);
|
|
this._registerHandler(document, {
|
|
'mousedown': function () {
|
|
that._endTyping();
|
|
},
|
|
'mouseup': function () {
|
|
that._mouseup();
|
|
}
|
|
});
|
|
that.toolbar.resize();
|
|
kendo.notify(that);
|
|
},
|
|
setOptions: function (options) {
|
|
var editor = this;
|
|
Widget.fn.setOptions.call(editor, options);
|
|
if (options.tools) {
|
|
editor.toolbar.bindTo(editor);
|
|
}
|
|
},
|
|
_endTyping: function () {
|
|
var keyboard = this.keyboard;
|
|
try {
|
|
if (keyboard.isTypingInProgress()) {
|
|
keyboard.endTyping(true);
|
|
this.saveSelection();
|
|
}
|
|
} catch (e) {
|
|
}
|
|
},
|
|
_selectionChange: function () {
|
|
if (!browser.msie) {
|
|
kendo.ui.editor.Dom.ensureTrailingBreaks(this.body);
|
|
}
|
|
this._selectionStarted = false;
|
|
this.saveSelection();
|
|
this.trigger('select', {});
|
|
},
|
|
_resizable: function () {
|
|
var resizable = this.options.resizable;
|
|
var isResizable = $.isPlainObject(resizable) ? resizable.content === undefined || resizable.content === true : resizable;
|
|
if (isResizable && this.textarea) {
|
|
$('<div class=\'k-resize-handle\'><span class=\'k-icon k-resize-se\' /></div>').insertAfter(this.textarea);
|
|
this.wrapper.kendoResizable(extend({}, this.options.resizable, {
|
|
start: function (e) {
|
|
var editor = this.editor = $(e.currentTarget).closest('.k-editor');
|
|
this.initialSize = editor.height();
|
|
editor.find('td:last').append('<div class=\'k-overlay\' />');
|
|
},
|
|
resize: function (e) {
|
|
var delta = e.y.initialDelta;
|
|
var newSize = this.initialSize + delta;
|
|
var min = this.options.min || 0;
|
|
var max = this.options.max || Infinity;
|
|
newSize = Math.min(max, Math.max(min, newSize));
|
|
this.editor.height(newSize);
|
|
},
|
|
resizeend: function () {
|
|
this.editor.find('.k-overlay').remove();
|
|
this.editor = null;
|
|
}
|
|
}));
|
|
}
|
|
},
|
|
_wrapTextarea: function () {
|
|
var that = this, textarea = that.element, w = textarea[0].style.width, h = textarea[0].style.height, template = EditorUtils.editorWrapperTemplate, editorWrap = $(template).insertBefore(textarea).width(w).height(h), editArea = editorWrap.find('.k-editable-area');
|
|
textarea.attr('autocomplete', 'off').appendTo(editArea).addClass('k-content k-raw-content').css('display', 'none');
|
|
that.textarea = textarea;
|
|
that.wrapper = editorWrap;
|
|
},
|
|
_createContentElement: function (stylesheets) {
|
|
var editor = this;
|
|
var iframe, wnd, doc;
|
|
var textarea = editor.textarea;
|
|
var specifiedDomain = editor.options.domain;
|
|
var domain = specifiedDomain || document.domain;
|
|
var domainScript = '';
|
|
var src = 'javascript:""';
|
|
if (specifiedDomain || domain != location.hostname) {
|
|
domainScript = '<script>document.domain="' + domain + '"</script>';
|
|
src = 'javascript:document.write(\'' + domainScript + '\')';
|
|
}
|
|
textarea.hide();
|
|
iframe = $('<iframe />', {
|
|
title: editor.options.messages.editAreaTitle,
|
|
frameBorder: '0'
|
|
})[0];
|
|
$(iframe).css('display', '').addClass('k-content').attr('tabindex', textarea[0].tabIndex).insertBefore(textarea);
|
|
iframe.src = src;
|
|
wnd = iframe.contentWindow || iframe;
|
|
doc = wnd.document || iframe.contentDocument;
|
|
$(iframe).one('load', function () {
|
|
editor.toolbar.decorateFrom(doc.body);
|
|
});
|
|
doc.open();
|
|
doc.write('<!DOCTYPE html><html><head>' + '<meta charset=\'utf-8\' />' + '<style>' + 'html,body{padding:0;margin:0;height:100%;min-height:100%;}' + 'body{font-size:12px;font-family:Verdana,Geneva,sans-serif;margin-top:-1px;padding:1px .2em 0;' + 'word-wrap: break-word;-webkit-nbsp-mode: space;-webkit-line-break: after-white-space;' + (kendo.support.isRtl(textarea) ? 'direction:rtl;' : '') + '}' + 'h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em}h3{font-size:1.16em}h4{font-size:1em}h5{font-size:.83em}h6{font-size:.7em}' + 'p{margin:0 0 1em;}.k-marker{display:none;}.k-paste-container,.Apple-style-span{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden}' + 'ul,ol{padding-left:2.5em}' + 'span{-ms-high-contrast-adjust:none;}' + 'a{color:#00a}' + 'code{font-size:1.23em}' + 'telerik\\3Ascript{display: none;}' + '.k-table{table-layout:fixed;width:100%;border-spacing:0;margin: 0 0 1em;}' + '.k-table td{min-width:1px;padding:.2em .3em;}' + '.k-table,.k-table td{outline:0;border: 1px dotted #ccc;}' + '.k-table p{margin:0;padding:0;}' + 'k\\:script{display:none;}' + '</style>' + domainScript + '<script>(function(d,c){d[c](\'header\'),d[c](\'article\'),d[c](\'nav\'),d[c](\'section\'),d[c](\'footer\');})(document, \'createElement\');</script>' + $.map(stylesheets, function (href) {
|
|
return '<link rel=\'stylesheet\' href=\'' + href + '\'>';
|
|
}).join('') + '</head><body autocorrect=\'off\' contenteditable=\'true\'></body></html>');
|
|
doc.close();
|
|
return wnd;
|
|
},
|
|
_blur: function () {
|
|
var textarea = this.textarea;
|
|
var old = textarea ? textarea.val() : this._oldValue;
|
|
var value = this.options.encoded ? this.encodedValue() : this.value();
|
|
this.update();
|
|
if (textarea) {
|
|
textarea.trigger('blur');
|
|
}
|
|
if (value != old) {
|
|
this.trigger('change');
|
|
}
|
|
},
|
|
_spellCorrect: function (editor) {
|
|
var beforeCorrection;
|
|
var falseTrigger = false;
|
|
this._registerHandler(editor.body, {
|
|
'contextmenu': function () {
|
|
editor.one('select', function () {
|
|
beforeCorrection = null;
|
|
});
|
|
editor._spellCorrectTimeout = setTimeout(function () {
|
|
beforeCorrection = new kendo.ui.editor.RestorePoint(editor.getRange());
|
|
falseTrigger = false;
|
|
}, 10);
|
|
},
|
|
'input': function () {
|
|
if (!beforeCorrection) {
|
|
return;
|
|
}
|
|
if (kendo.support.browser.mozilla && !falseTrigger) {
|
|
falseTrigger = true;
|
|
return;
|
|
}
|
|
kendo.ui.editor._finishUpdate(editor, beforeCorrection);
|
|
}
|
|
});
|
|
},
|
|
_registerHandler: function (element, type, handler) {
|
|
var NS = '.kendoEditor';
|
|
element = $(element);
|
|
if (!this._handlers) {
|
|
this._handlers = [];
|
|
}
|
|
if (element.length) {
|
|
if ($.isPlainObject(type)) {
|
|
for (var t in type) {
|
|
if (type.hasOwnProperty(t)) {
|
|
this._registerHandler(element, t, type[t]);
|
|
}
|
|
}
|
|
} else {
|
|
type = type.split(' ').join(NS + ' ') + NS;
|
|
this._handlers.push({
|
|
element: element,
|
|
type: type,
|
|
handler: handler
|
|
});
|
|
element.on(type, handler);
|
|
}
|
|
}
|
|
},
|
|
_deregisterHandlers: function () {
|
|
var handlers = this._handlers;
|
|
for (var i = 0; i < handlers.length; i++) {
|
|
var h = handlers[i];
|
|
h.element.off(h.type, h.handler);
|
|
}
|
|
this._handlers = [];
|
|
},
|
|
_initializeContentElement: function () {
|
|
var editor = this;
|
|
var doc;
|
|
var blurTrigger;
|
|
if (editor.textarea) {
|
|
editor.window = editor._createContentElement(editor.options.stylesheets);
|
|
doc = editor.document = editor.window.contentDocument || editor.window.document;
|
|
editor.body = doc.body;
|
|
blurTrigger = editor.window;
|
|
this._registerHandler(doc, 'mouseup', proxy(this._mouseup, this));
|
|
} else {
|
|
editor.window = window;
|
|
doc = editor.document = document;
|
|
editor.body = editor.element[0];
|
|
blurTrigger = editor.body;
|
|
editor.toolbar.decorateFrom(editor.body);
|
|
}
|
|
this._registerHandler(blurTrigger, 'blur', proxy(this._blur, this));
|
|
try {
|
|
doc.execCommand('enableInlineTableEditing', null, false);
|
|
} catch (e) {
|
|
}
|
|
if (kendo.support.touch) {
|
|
this._registerHandler(doc, {
|
|
'selectionchange': proxy(this._selectionChange, this),
|
|
'keydown': function () {
|
|
if (kendo._activeElement() != doc.body) {
|
|
editor.window.focus();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
this._spellCorrect(editor);
|
|
this._registerHandler(editor.body, {
|
|
'dragstart': function (e) {
|
|
e.preventDefault();
|
|
},
|
|
'keydown': function (e) {
|
|
var range;
|
|
if ((e.keyCode === keys.BACKSPACE || e.keyCode === keys.DELETE) && editor.body.getAttribute('contenteditable') !== 'true') {
|
|
return false;
|
|
}
|
|
if (e.keyCode === keys.F10) {
|
|
setTimeout(proxy(editor.toolbar.focus, editor.toolbar), 100);
|
|
e.preventDefault();
|
|
return;
|
|
} else if (e.keyCode == keys.LEFT || e.keyCode == keys.RIGHT) {
|
|
range = editor.getRange();
|
|
var left = e.keyCode == keys.LEFT;
|
|
var container = range[left ? 'startContainer' : 'endContainer'];
|
|
var offset = range[left ? 'startOffset' : 'endOffset'];
|
|
var direction = left ? -1 : 1;
|
|
if (left) {
|
|
offset -= 1;
|
|
}
|
|
if (offset + direction > 0 && container.nodeType == 3 && container.nodeValue[offset] == '\uFEFF') {
|
|
range.setStart(container, offset + direction);
|
|
range.collapse(true);
|
|
editor.selectRange(range);
|
|
}
|
|
}
|
|
var toolName = editor.keyboard.toolFromShortcut(editor.toolbar.tools, e);
|
|
if (toolName) {
|
|
e.preventDefault();
|
|
if (!/^(undo|redo)$/.test(toolName)) {
|
|
editor.keyboard.endTyping(true);
|
|
}
|
|
editor.trigger('keydown', e);
|
|
editor.exec(toolName);
|
|
return false;
|
|
}
|
|
editor.keyboard.clearTimeout();
|
|
editor.keyboard.keydown(e);
|
|
},
|
|
'keyup': function (e) {
|
|
var selectionCodes = [
|
|
8,
|
|
9,
|
|
33,
|
|
34,
|
|
35,
|
|
36,
|
|
37,
|
|
38,
|
|
39,
|
|
40,
|
|
40,
|
|
45,
|
|
46
|
|
];
|
|
if ($.inArray(e.keyCode, selectionCodes) > -1 || e.keyCode == 65 && e.ctrlKey && !e.altKey && !e.shiftKey) {
|
|
editor._selectionChange();
|
|
}
|
|
editor.keyboard.keyup(e);
|
|
},
|
|
'mousedown': function (e) {
|
|
editor._selectionStarted = true;
|
|
if (browser.gecko) {
|
|
return;
|
|
}
|
|
var target = $(e.target);
|
|
if ((e.which == 2 || e.which == 1 && e.ctrlKey) && target.is('a[href]')) {
|
|
window.open(target.attr('href'), '_new');
|
|
}
|
|
},
|
|
'click': function (e) {
|
|
var dom = kendo.ui.editor.Dom, range;
|
|
if (dom.name(e.target) === 'img') {
|
|
range = editor.createRange();
|
|
range.selectNode(e.target);
|
|
editor.selectRange(range);
|
|
}
|
|
},
|
|
'cut copy paste': function (e) {
|
|
editor.clipboard['on' + e.type](e);
|
|
},
|
|
'focusin': function () {
|
|
if (editor.body.hasAttribute('contenteditable')) {
|
|
$(this).addClass('k-state-active');
|
|
editor.toolbar.show();
|
|
}
|
|
},
|
|
'focusout': function () {
|
|
setTimeout(function () {
|
|
var active = kendo._activeElement();
|
|
var body = editor.body;
|
|
var toolbar = editor.toolbar;
|
|
if (active != body && !$.contains(body, active) && !$(active).is('.k-editortoolbar-dragHandle') && !toolbar.focused()) {
|
|
$(body).removeClass('k-state-active');
|
|
toolbar.hide();
|
|
}
|
|
}, 10);
|
|
}
|
|
});
|
|
},
|
|
_mouseup: function () {
|
|
var that = this;
|
|
if (that._selectionStarted) {
|
|
setTimeout(function () {
|
|
that._selectionChange();
|
|
}, 1);
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var that = this;
|
|
if (that.textarea) {
|
|
that.textarea.val(that.value());
|
|
that.wrapper.find('iframe').remove();
|
|
that._initializeContentElement(that);
|
|
that.value(that.textarea.val());
|
|
}
|
|
},
|
|
events: [
|
|
'select',
|
|
'change',
|
|
'execute',
|
|
'error',
|
|
'paste',
|
|
'keydown',
|
|
'keyup'
|
|
],
|
|
options: {
|
|
name: 'Editor',
|
|
messages: messages,
|
|
formats: {},
|
|
encoded: true,
|
|
domain: null,
|
|
resizable: false,
|
|
serialization: {
|
|
entities: true,
|
|
semantic: true,
|
|
scripts: false
|
|
},
|
|
stylesheets: [],
|
|
dialogOptions: {
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
animation: false
|
|
},
|
|
imageBrowser: null,
|
|
fileBrowser: null,
|
|
fontName: [
|
|
{
|
|
text: 'Arial',
|
|
value: 'Arial,Helvetica,sans-serif'
|
|
},
|
|
{
|
|
text: 'Courier New',
|
|
value: '\'Courier New\',Courier,monospace'
|
|
},
|
|
{
|
|
text: 'Georgia',
|
|
value: 'Georgia,serif'
|
|
},
|
|
{
|
|
text: 'Impact',
|
|
value: 'Impact,Charcoal,sans-serif'
|
|
},
|
|
{
|
|
text: 'Lucida Console',
|
|
value: '\'Lucida Console\',Monaco,monospace'
|
|
},
|
|
{
|
|
text: 'Tahoma',
|
|
value: 'Tahoma,Geneva,sans-serif'
|
|
},
|
|
{
|
|
text: 'Times New Roman',
|
|
value: '\'Times New Roman\',Times,serif'
|
|
},
|
|
{
|
|
text: 'Trebuchet MS',
|
|
value: '\'Trebuchet MS\',Helvetica,sans-serif'
|
|
},
|
|
{
|
|
text: 'Verdana',
|
|
value: 'Verdana,Geneva,sans-serif'
|
|
}
|
|
],
|
|
fontSize: [
|
|
{
|
|
text: '1 (8pt)',
|
|
value: 'xx-small'
|
|
},
|
|
{
|
|
text: '2 (10pt)',
|
|
value: 'x-small'
|
|
},
|
|
{
|
|
text: '3 (12pt)',
|
|
value: 'small'
|
|
},
|
|
{
|
|
text: '4 (14pt)',
|
|
value: 'medium'
|
|
},
|
|
{
|
|
text: '5 (18pt)',
|
|
value: 'large'
|
|
},
|
|
{
|
|
text: '6 (24pt)',
|
|
value: 'x-large'
|
|
},
|
|
{
|
|
text: '7 (36pt)',
|
|
value: 'xx-large'
|
|
}
|
|
],
|
|
formatBlock: [
|
|
{
|
|
text: 'Paragraph',
|
|
value: 'p'
|
|
},
|
|
{
|
|
text: 'Quotation',
|
|
value: 'blockquote'
|
|
},
|
|
{
|
|
text: 'Heading 1',
|
|
value: 'h1'
|
|
},
|
|
{
|
|
text: 'Heading 2',
|
|
value: 'h2'
|
|
},
|
|
{
|
|
text: 'Heading 3',
|
|
value: 'h3'
|
|
},
|
|
{
|
|
text: 'Heading 4',
|
|
value: 'h4'
|
|
},
|
|
{
|
|
text: 'Heading 5',
|
|
value: 'h5'
|
|
},
|
|
{
|
|
text: 'Heading 6',
|
|
value: 'h6'
|
|
}
|
|
],
|
|
tools: [].concat.call(['formatting'], toolGroups.basic, toolGroups.alignment, toolGroups.lists, toolGroups.indenting, toolGroups.links, ['insertImage'], toolGroups.tables)
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this._deregisterHandlers();
|
|
clearTimeout(this._spellCorrectTimeout);
|
|
this._focusOutside();
|
|
this.toolbar.destroy();
|
|
kendo.destroy(this.wrapper);
|
|
},
|
|
_focusOutside: function () {
|
|
if (kendo.support.browser.msie && this.textarea) {
|
|
var tempInput = $('<input style=\'position:fixed;left:1px;top:1px;width:1px;height:1px;font-size:0;border:0;opacity:0\' />').appendTo(document.body).focus();
|
|
tempInput.blur().remove();
|
|
}
|
|
},
|
|
state: function (toolName) {
|
|
var tool = Editor.defaultTools[toolName];
|
|
var finder = tool && (tool.options.finder || tool.finder);
|
|
var RangeUtils = kendo.ui.editor.RangeUtils;
|
|
var range, textNodes;
|
|
if (finder) {
|
|
range = this.getRange();
|
|
textNodes = RangeUtils.textNodes(range);
|
|
if (!textNodes.length && range.collapsed) {
|
|
textNodes = [range.startContainer];
|
|
}
|
|
return finder.getFormat ? finder.getFormat(textNodes) : finder.isFormatted(textNodes);
|
|
}
|
|
return false;
|
|
},
|
|
value: function (html) {
|
|
var body = this.body, editorNS = kendo.ui.editor, currentHtml = editorNS.Serializer.domToXhtml(body, this.options.serialization);
|
|
if (html === undefined) {
|
|
return currentHtml;
|
|
}
|
|
if (html == currentHtml) {
|
|
return;
|
|
}
|
|
editorNS.Serializer.htmlToDom(html, body);
|
|
if (!browser.msie) {
|
|
kendo.ui.editor.Dom.ensureTrailingBreaks(this.body);
|
|
}
|
|
this.selectionRestorePoint = null;
|
|
this.update();
|
|
this.toolbar.refreshTools();
|
|
},
|
|
saveSelection: function (range) {
|
|
range = range || this.getRange();
|
|
var container = range.commonAncestorContainer, body = this.body;
|
|
if (container == body || $.contains(body, container)) {
|
|
this.selectionRestorePoint = new kendo.ui.editor.RestorePoint(range);
|
|
}
|
|
},
|
|
_focusBody: function () {
|
|
var body = this.body;
|
|
var iframe = this.wrapper && this.wrapper.find('iframe')[0];
|
|
var documentElement = this.document.documentElement;
|
|
var activeElement = kendo._activeElement();
|
|
if (activeElement != body && activeElement != iframe) {
|
|
var scrollTop = documentElement.scrollTop;
|
|
body.focus();
|
|
documentElement.scrollTop = scrollTop;
|
|
}
|
|
},
|
|
restoreSelection: function () {
|
|
this._focusBody();
|
|
if (this.selectionRestorePoint) {
|
|
this.selectRange(this.selectionRestorePoint.toRange());
|
|
}
|
|
},
|
|
focus: function () {
|
|
this.restoreSelection();
|
|
},
|
|
update: function (value) {
|
|
value = value || this.options.encoded ? this.encodedValue() : this.value();
|
|
if (this.textarea) {
|
|
this.textarea.val(value);
|
|
} else {
|
|
this._oldValue = value;
|
|
}
|
|
},
|
|
encodedValue: function () {
|
|
return kendo.ui.editor.Dom.encode(this.value());
|
|
},
|
|
createRange: function (document) {
|
|
return kendo.ui.editor.RangeUtils.createRange(document || this.document);
|
|
},
|
|
getSelection: function () {
|
|
return kendo.ui.editor.SelectionUtils.selectionFromDocument(this.document);
|
|
},
|
|
selectRange: function (range) {
|
|
this._focusBody();
|
|
var selection = this.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
this.saveSelection(range);
|
|
},
|
|
getRange: function () {
|
|
var selection = this.getSelection(), range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : this.createRange(), doc = this.document;
|
|
if (range.startContainer == doc && range.endContainer == doc && !range.startOffset && !range.endOffset) {
|
|
range.setStart(this.body, 0);
|
|
range.collapse(true);
|
|
}
|
|
return range;
|
|
},
|
|
selectedHtml: function () {
|
|
return kendo.ui.editor.Serializer.domToXhtml(this.getRange().cloneContents());
|
|
},
|
|
paste: function (html, options) {
|
|
this.focus();
|
|
var command = new kendo.ui.editor.InsertHtmlCommand($.extend({
|
|
range: this.getRange(),
|
|
html: html
|
|
}, options));
|
|
command.editor = this;
|
|
command.exec();
|
|
},
|
|
exec: function (name, params) {
|
|
var that = this;
|
|
var command = null;
|
|
var range, tool, prevented;
|
|
if (!name) {
|
|
throw new Error('kendoEditor.exec(): `name` parameter cannot be empty');
|
|
}
|
|
if (that.body.getAttribute('contenteditable') !== 'true' && name !== 'print') {
|
|
return false;
|
|
}
|
|
name = name.toLowerCase();
|
|
if (!that.keyboard.isTypingInProgress()) {
|
|
that.restoreSelection();
|
|
}
|
|
tool = that.toolbar.toolById(name);
|
|
if (!tool) {
|
|
for (var id in Editor.defaultTools) {
|
|
if (id.toLowerCase() == name) {
|
|
tool = Editor.defaultTools[id];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (tool) {
|
|
range = that.getRange();
|
|
if (tool.command) {
|
|
command = tool.command(extend({ range: range }, params));
|
|
}
|
|
prevented = that.trigger('execute', {
|
|
name: name,
|
|
command: command
|
|
});
|
|
if (prevented) {
|
|
return;
|
|
}
|
|
if (/^(undo|redo)$/i.test(name)) {
|
|
that.undoRedoStack[name]();
|
|
} else if (command) {
|
|
if (!command.managesUndoRedo) {
|
|
that.undoRedoStack.push(command);
|
|
}
|
|
command.editor = that;
|
|
command.exec();
|
|
if (command.async) {
|
|
command.change = proxy(that._selectionChange, that);
|
|
return;
|
|
}
|
|
}
|
|
that._selectionChange();
|
|
}
|
|
}
|
|
});
|
|
Editor.defaultTools = {
|
|
undo: {
|
|
options: {
|
|
key: 'Z',
|
|
ctrl: true
|
|
}
|
|
},
|
|
redo: {
|
|
options: {
|
|
key: 'Y',
|
|
ctrl: true
|
|
}
|
|
}
|
|
};
|
|
kendo.ui.plugin(Editor);
|
|
var Tool = Class.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
},
|
|
initialize: function (ui, options) {
|
|
ui.attr({
|
|
unselectable: 'on',
|
|
title: options.title
|
|
});
|
|
ui.children('.k-tool-text').html(options.title);
|
|
},
|
|
command: function (commandArguments) {
|
|
return new this.options.command(commandArguments);
|
|
},
|
|
update: $.noop
|
|
});
|
|
Tool.exec = function (editor, name, value) {
|
|
editor.exec(name, { value: value });
|
|
};
|
|
var FormatTool = Tool.extend({
|
|
init: function (options) {
|
|
Tool.fn.init.call(this, options);
|
|
},
|
|
command: function (commandArguments) {
|
|
var that = this;
|
|
return new kendo.ui.editor.FormatCommand(extend(commandArguments, { formatter: that.options.formatter }));
|
|
},
|
|
update: function (ui, nodes) {
|
|
var isFormatted = this.options.finder.isFormatted(nodes);
|
|
ui.toggleClass('k-state-selected', isFormatted);
|
|
ui.attr('aria-pressed', isFormatted);
|
|
}
|
|
});
|
|
EditorUtils.registerTool('separator', new Tool({ template: new ToolTemplate({ template: EditorUtils.separatorTemplate }) }));
|
|
var bomFill = browser.msie && browser.version < 9 ? '\uFEFF' : '';
|
|
var emptyElementContent = '<br class="k-br" />';
|
|
if (browser.msie) {
|
|
if (browser.version < 10) {
|
|
emptyElementContent = '\uFEFF';
|
|
} else if (browser.version < 11) {
|
|
emptyElementContent = ' ';
|
|
}
|
|
}
|
|
extend(kendo.ui, {
|
|
editor: {
|
|
ToolTemplate: ToolTemplate,
|
|
EditorUtils: EditorUtils,
|
|
Tool: Tool,
|
|
FormatTool: FormatTool,
|
|
_bomFill: bomFill,
|
|
emptyElementContent: emptyElementContent
|
|
}
|
|
});
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Editor.prototype);
|
|
Editor.prototype._drawPDF = function () {
|
|
return kendo.drawing.drawDOM(this.body, this.options.pdf);
|
|
};
|
|
Editor.prototype.saveAsPDF = function () {
|
|
var progress = new $.Deferred();
|
|
var promise = progress.promise();
|
|
var args = { promise: promise };
|
|
if (this.trigger('pdfExport', args)) {
|
|
return;
|
|
}
|
|
var options = this.options.pdf;
|
|
var paperSize = options.paperSize;
|
|
this._drawPDF(progress).then(function (root) {
|
|
options.paperSize = 'auto';
|
|
return kendo.drawing.exportPDF(root, options);
|
|
}).done(function (dataURI) {
|
|
kendo.saveAs({
|
|
dataURI: dataURI,
|
|
fileName: options.fileName,
|
|
proxyURL: options.proxyURL,
|
|
forceProxy: options.forceProxy
|
|
});
|
|
options.paperSize = paperSize;
|
|
progress.resolve();
|
|
}).fail(function (err) {
|
|
progress.reject(err);
|
|
});
|
|
return promise;
|
|
};
|
|
}
|
|
}(window.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/dom', ['editor/main'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, map = $.map, extend = $.extend, browser = kendo.support.browser, STYLE = 'style', FLOAT = 'float', CSSFLOAT = 'cssFloat', STYLEFLOAT = 'styleFloat', CLASS = 'class', KMARKER = 'k-marker';
|
|
function makeMap(items) {
|
|
var obj = {}, i, len;
|
|
for (i = 0, len = items.length; i < len; i++) {
|
|
obj[items[i]] = true;
|
|
}
|
|
return obj;
|
|
}
|
|
var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed'.split(',')), nonListBlockElements = 'div,p,h1,h2,h3,h4,h5,h6,address,applet,blockquote,button,center,dd,dir,dl,dt,fieldset,form,frameset,hr,iframe,isindex,map,menu,noframes,noscript,object,pre,script,table,tbody,td,tfoot,th,thead,tr,header,article,nav,footer,section,aside,main,figure,figcaption'.split(','), blockElements = nonListBlockElements.concat([
|
|
'ul',
|
|
'ol',
|
|
'li'
|
|
]), block = makeMap(blockElements), inlineElements = 'span,em,a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,strike,strong,sub,sup,textarea,tt,u,var,data,time,mark,ruby'.split(','), inline = makeMap(inlineElements), fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'.split(','));
|
|
var normalize = function (node) {
|
|
if (node.nodeType == 1) {
|
|
node.normalize();
|
|
}
|
|
};
|
|
if (browser.msie && browser.version >= 8) {
|
|
normalize = function (parent) {
|
|
if (parent.nodeType == 1 && parent.firstChild) {
|
|
var prev = parent.firstChild, node = prev;
|
|
while (true) {
|
|
node = node.nextSibling;
|
|
if (!node) {
|
|
break;
|
|
}
|
|
if (node.nodeType == 3 && prev.nodeType == 3) {
|
|
node.nodeValue = prev.nodeValue + node.nodeValue;
|
|
Dom.remove(prev);
|
|
}
|
|
prev = node;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
var whitespace = /^\s+$/, emptyspace = /^[\n\r\t]+$/, rgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i, bom = /\ufeff/g, whitespaceOrBom = /^(\s+|\ufeff)$/, persistedScrollTop, cssAttributes = ('color,padding-left,padding-right,padding-top,padding-bottom,' + 'background-color,background-attachment,background-image,background-position,background-repeat,' + 'border-top-style,border-top-width,border-top-color,' + 'border-bottom-style,border-bottom-width,border-bottom-color,' + 'border-left-style,border-left-width,border-left-color,' + 'border-right-style,border-right-width,border-right-color,' + 'font-family,font-size,font-style,font-variant,font-weight,line-height').split(','), htmlRe = /[<>\&]/g, entityRe = /[\u00A0-\u2666<>\&]/g, entityTable = {
|
|
34: 'quot',
|
|
38: 'amp',
|
|
39: 'apos',
|
|
60: 'lt',
|
|
62: 'gt',
|
|
160: 'nbsp',
|
|
161: 'iexcl',
|
|
162: 'cent',
|
|
163: 'pound',
|
|
164: 'curren',
|
|
165: 'yen',
|
|
166: 'brvbar',
|
|
167: 'sect',
|
|
168: 'uml',
|
|
169: 'copy',
|
|
170: 'ordf',
|
|
171: 'laquo',
|
|
172: 'not',
|
|
173: 'shy',
|
|
174: 'reg',
|
|
175: 'macr',
|
|
176: 'deg',
|
|
177: 'plusmn',
|
|
178: 'sup2',
|
|
179: 'sup3',
|
|
180: 'acute',
|
|
181: 'micro',
|
|
182: 'para',
|
|
183: 'middot',
|
|
184: 'cedil',
|
|
185: 'sup1',
|
|
186: 'ordm',
|
|
187: 'raquo',
|
|
188: 'frac14',
|
|
189: 'frac12',
|
|
190: 'frac34',
|
|
191: 'iquest',
|
|
192: 'Agrave',
|
|
193: 'Aacute',
|
|
194: 'Acirc',
|
|
195: 'Atilde',
|
|
196: 'Auml',
|
|
197: 'Aring',
|
|
198: 'AElig',
|
|
199: 'Ccedil',
|
|
200: 'Egrave',
|
|
201: 'Eacute',
|
|
202: 'Ecirc',
|
|
203: 'Euml',
|
|
204: 'Igrave',
|
|
205: 'Iacute',
|
|
206: 'Icirc',
|
|
207: 'Iuml',
|
|
208: 'ETH',
|
|
209: 'Ntilde',
|
|
210: 'Ograve',
|
|
211: 'Oacute',
|
|
212: 'Ocirc',
|
|
213: 'Otilde',
|
|
214: 'Ouml',
|
|
215: 'times',
|
|
216: 'Oslash',
|
|
217: 'Ugrave',
|
|
218: 'Uacute',
|
|
219: 'Ucirc',
|
|
220: 'Uuml',
|
|
221: 'Yacute',
|
|
222: 'THORN',
|
|
223: 'szlig',
|
|
224: 'agrave',
|
|
225: 'aacute',
|
|
226: 'acirc',
|
|
227: 'atilde',
|
|
228: 'auml',
|
|
229: 'aring',
|
|
230: 'aelig',
|
|
231: 'ccedil',
|
|
232: 'egrave',
|
|
233: 'eacute',
|
|
234: 'ecirc',
|
|
235: 'euml',
|
|
236: 'igrave',
|
|
237: 'iacute',
|
|
238: 'icirc',
|
|
239: 'iuml',
|
|
240: 'eth',
|
|
241: 'ntilde',
|
|
242: 'ograve',
|
|
243: 'oacute',
|
|
244: 'ocirc',
|
|
245: 'otilde',
|
|
246: 'ouml',
|
|
247: 'divide',
|
|
248: 'oslash',
|
|
249: 'ugrave',
|
|
250: 'uacute',
|
|
251: 'ucirc',
|
|
252: 'uuml',
|
|
253: 'yacute',
|
|
254: 'thorn',
|
|
255: 'yuml',
|
|
402: 'fnof',
|
|
913: 'Alpha',
|
|
914: 'Beta',
|
|
915: 'Gamma',
|
|
916: 'Delta',
|
|
917: 'Epsilon',
|
|
918: 'Zeta',
|
|
919: 'Eta',
|
|
920: 'Theta',
|
|
921: 'Iota',
|
|
922: 'Kappa',
|
|
923: 'Lambda',
|
|
924: 'Mu',
|
|
925: 'Nu',
|
|
926: 'Xi',
|
|
927: 'Omicron',
|
|
928: 'Pi',
|
|
929: 'Rho',
|
|
931: 'Sigma',
|
|
932: 'Tau',
|
|
933: 'Upsilon',
|
|
934: 'Phi',
|
|
935: 'Chi',
|
|
936: 'Psi',
|
|
937: 'Omega',
|
|
945: 'alpha',
|
|
946: 'beta',
|
|
947: 'gamma',
|
|
948: 'delta',
|
|
949: 'epsilon',
|
|
950: 'zeta',
|
|
951: 'eta',
|
|
952: 'theta',
|
|
953: 'iota',
|
|
954: 'kappa',
|
|
955: 'lambda',
|
|
956: 'mu',
|
|
957: 'nu',
|
|
958: 'xi',
|
|
959: 'omicron',
|
|
960: 'pi',
|
|
961: 'rho',
|
|
962: 'sigmaf',
|
|
963: 'sigma',
|
|
964: 'tau',
|
|
965: 'upsilon',
|
|
966: 'phi',
|
|
967: 'chi',
|
|
968: 'psi',
|
|
969: 'omega',
|
|
977: 'thetasym',
|
|
978: 'upsih',
|
|
982: 'piv',
|
|
8226: 'bull',
|
|
8230: 'hellip',
|
|
8242: 'prime',
|
|
8243: 'Prime',
|
|
8254: 'oline',
|
|
8260: 'frasl',
|
|
8472: 'weierp',
|
|
8465: 'image',
|
|
8476: 'real',
|
|
8482: 'trade',
|
|
8501: 'alefsym',
|
|
8592: 'larr',
|
|
8593: 'uarr',
|
|
8594: 'rarr',
|
|
8595: 'darr',
|
|
8596: 'harr',
|
|
8629: 'crarr',
|
|
8656: 'lArr',
|
|
8657: 'uArr',
|
|
8658: 'rArr',
|
|
8659: 'dArr',
|
|
8660: 'hArr',
|
|
8704: 'forall',
|
|
8706: 'part',
|
|
8707: 'exist',
|
|
8709: 'empty',
|
|
8711: 'nabla',
|
|
8712: 'isin',
|
|
8713: 'notin',
|
|
8715: 'ni',
|
|
8719: 'prod',
|
|
8721: 'sum',
|
|
8722: 'minus',
|
|
8727: 'lowast',
|
|
8730: 'radic',
|
|
8733: 'prop',
|
|
8734: 'infin',
|
|
8736: 'ang',
|
|
8743: 'and',
|
|
8744: 'or',
|
|
8745: 'cap',
|
|
8746: 'cup',
|
|
8747: 'int',
|
|
8756: 'there4',
|
|
8764: 'sim',
|
|
8773: 'cong',
|
|
8776: 'asymp',
|
|
8800: 'ne',
|
|
8801: 'equiv',
|
|
8804: 'le',
|
|
8805: 'ge',
|
|
8834: 'sub',
|
|
8835: 'sup',
|
|
8836: 'nsub',
|
|
8838: 'sube',
|
|
8839: 'supe',
|
|
8853: 'oplus',
|
|
8855: 'otimes',
|
|
8869: 'perp',
|
|
8901: 'sdot',
|
|
8968: 'lceil',
|
|
8969: 'rceil',
|
|
8970: 'lfloor',
|
|
8971: 'rfloor',
|
|
9001: 'lang',
|
|
9002: 'rang',
|
|
9674: 'loz',
|
|
9824: 'spades',
|
|
9827: 'clubs',
|
|
9829: 'hearts',
|
|
9830: 'diams',
|
|
338: 'OElig',
|
|
339: 'oelig',
|
|
352: 'Scaron',
|
|
353: 'scaron',
|
|
376: 'Yuml',
|
|
710: 'circ',
|
|
732: 'tilde',
|
|
8194: 'ensp',
|
|
8195: 'emsp',
|
|
8201: 'thinsp',
|
|
8204: 'zwnj',
|
|
8205: 'zwj',
|
|
8206: 'lrm',
|
|
8207: 'rlm',
|
|
8211: 'ndash',
|
|
8212: 'mdash',
|
|
8216: 'lsquo',
|
|
8217: 'rsquo',
|
|
8218: 'sbquo',
|
|
8220: 'ldquo',
|
|
8221: 'rdquo',
|
|
8222: 'bdquo',
|
|
8224: 'dagger',
|
|
8225: 'Dagger',
|
|
8240: 'permil',
|
|
8249: 'lsaquo',
|
|
8250: 'rsaquo',
|
|
8364: 'euro'
|
|
};
|
|
var Dom = {
|
|
block: block,
|
|
inline: inline,
|
|
findNodeIndex: function (node, skipText) {
|
|
var i = 0;
|
|
if (!node) {
|
|
return -1;
|
|
}
|
|
while (true) {
|
|
node = node.previousSibling;
|
|
if (!node) {
|
|
break;
|
|
}
|
|
if (!(skipText && node.nodeType == 3)) {
|
|
i++;
|
|
}
|
|
}
|
|
return i;
|
|
},
|
|
isDataNode: function (node) {
|
|
return node && node.nodeValue !== null && node.data !== null;
|
|
},
|
|
isAncestorOf: function (parent, node) {
|
|
try {
|
|
return !Dom.isDataNode(parent) && ($.contains(parent, Dom.isDataNode(node) ? node.parentNode : node) || node.parentNode == parent);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
},
|
|
isAncestorOrSelf: function (root, node) {
|
|
return Dom.isAncestorOf(root, node) || root == node;
|
|
},
|
|
findClosestAncestor: function (root, node) {
|
|
if (Dom.isAncestorOf(root, node)) {
|
|
while (node && node.parentNode != root) {
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
return node;
|
|
},
|
|
getNodeLength: function (node) {
|
|
return Dom.isDataNode(node) ? node.length : node.childNodes.length;
|
|
},
|
|
splitDataNode: function (node, offset) {
|
|
var newNode = node.cloneNode(false);
|
|
var denormalizedText = '';
|
|
var iterator = node.nextSibling;
|
|
var temp;
|
|
while (iterator && iterator.nodeType == 3 && iterator.nodeValue) {
|
|
denormalizedText += iterator.nodeValue;
|
|
temp = iterator;
|
|
iterator = iterator.nextSibling;
|
|
Dom.remove(temp);
|
|
}
|
|
node.deleteData(offset, node.length);
|
|
newNode.deleteData(0, offset);
|
|
newNode.nodeValue += denormalizedText;
|
|
Dom.insertAfter(newNode, node);
|
|
},
|
|
attrEquals: function (node, attributes) {
|
|
for (var key in attributes) {
|
|
var value = node[key];
|
|
if (key == FLOAT) {
|
|
value = node[kendo.support.cssFloat ? CSSFLOAT : STYLEFLOAT];
|
|
}
|
|
if (typeof value == 'object') {
|
|
if (!Dom.attrEquals(value, attributes[key])) {
|
|
return false;
|
|
}
|
|
} else if (value != attributes[key]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
blockParentOrBody: function (node) {
|
|
return Dom.parentOfType(node, blockElements) || node.ownerDocument.body;
|
|
},
|
|
blockParents: function (nodes) {
|
|
var blocks = [], i, len;
|
|
for (i = 0, len = nodes.length; i < len; i++) {
|
|
var block = Dom.parentOfType(nodes[i], Dom.blockElements);
|
|
if (block && $.inArray(block, blocks) < 0) {
|
|
blocks.push(block);
|
|
}
|
|
}
|
|
return blocks;
|
|
},
|
|
windowFromDocument: function (document) {
|
|
return document.defaultView || document.parentWindow;
|
|
},
|
|
normalize: normalize,
|
|
blockElements: blockElements,
|
|
nonListBlockElements: nonListBlockElements,
|
|
inlineElements: inlineElements,
|
|
empty: empty,
|
|
fillAttrs: fillAttrs,
|
|
toHex: function (color) {
|
|
var matches = rgb.exec(color);
|
|
if (!matches) {
|
|
return color;
|
|
}
|
|
return '#' + map(matches.slice(1), function (x) {
|
|
x = parseInt(x, 10).toString(16);
|
|
return x.length > 1 ? x : '0' + x;
|
|
}).join('');
|
|
},
|
|
encode: function (value, options) {
|
|
var encodableChars = !options || options.entities ? entityRe : htmlRe;
|
|
return value.replace(encodableChars, function (c) {
|
|
var charCode = c.charCodeAt(0);
|
|
var entity = entityTable[charCode];
|
|
return entity ? '&' + entity + ';' : c;
|
|
});
|
|
},
|
|
stripBom: function (text) {
|
|
return (text || '').replace(bom, '');
|
|
},
|
|
insignificant: function (node) {
|
|
var attr = node.attributes;
|
|
return node.className == 'k-marker' || Dom.is(node, 'br') && (node.className == 'k-br' || attr._moz_dirty || attr._moz_editor_bogus_node);
|
|
},
|
|
significantNodes: function (nodes) {
|
|
return $.grep(nodes, function (child) {
|
|
var name = Dom.name(child);
|
|
if (name == 'br') {
|
|
return false;
|
|
} else if (Dom.insignificant(child)) {
|
|
return false;
|
|
} else if (child.nodeType == 3 && whitespaceOrBom.test(child.nodeValue)) {
|
|
return false;
|
|
} else if (child.nodeType == 1 && !empty[name] && Dom.emptyNode(child)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
},
|
|
emptyNode: function (node) {
|
|
return node.nodeType == 1 && !Dom.significantNodes(node.childNodes).length;
|
|
},
|
|
name: function (node) {
|
|
return node.nodeName.toLowerCase();
|
|
},
|
|
significantChildNodes: function (node) {
|
|
return $.grep(node.childNodes, function (child) {
|
|
return child.nodeType != 3 || !Dom.isWhitespace(child);
|
|
});
|
|
},
|
|
lastTextNode: function (node) {
|
|
var result = null;
|
|
if (node.nodeType == 3) {
|
|
return node;
|
|
}
|
|
for (var child = node.lastChild; child; child = child.previousSibling) {
|
|
result = Dom.lastTextNode(child);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
is: function (node, nodeName) {
|
|
return Dom.name(node) == nodeName;
|
|
},
|
|
isMarker: function (node) {
|
|
return node.className == KMARKER;
|
|
},
|
|
isWhitespace: function (node) {
|
|
return whitespace.test(node.nodeValue);
|
|
},
|
|
isEmptyspace: function (node) {
|
|
return emptyspace.test(node.nodeValue);
|
|
},
|
|
isBlock: function (node) {
|
|
return block[Dom.name(node)];
|
|
},
|
|
isEmpty: function (node) {
|
|
return empty[Dom.name(node)];
|
|
},
|
|
isInline: function (node) {
|
|
return inline[Dom.name(node)];
|
|
},
|
|
scrollContainer: function (doc) {
|
|
var wnd = Dom.windowFromDocument(doc), scrollContainer = (wnd.contentWindow || wnd).document || wnd.ownerDocument || wnd;
|
|
if (kendo.support.browser.webkit || scrollContainer.compatMode == 'BackCompat') {
|
|
scrollContainer = scrollContainer.body;
|
|
} else {
|
|
scrollContainer = scrollContainer.documentElement;
|
|
}
|
|
return scrollContainer;
|
|
},
|
|
scrollTo: function (node) {
|
|
var element = $(Dom.isDataNode(node) ? node.parentNode : node), wnd = Dom.windowFromDocument(node.ownerDocument), windowHeight = wnd.innerHeight, elementTop, elementHeight, scrollContainer = Dom.scrollContainer(node.ownerDocument);
|
|
elementTop = element.offset().top;
|
|
elementHeight = element[0].offsetHeight;
|
|
if (!elementHeight) {
|
|
elementHeight = parseInt(element.css('line-height'), 10) || Math.ceil(1.2 * parseInt(element.css('font-size'), 10)) || 15;
|
|
}
|
|
if (elementHeight + elementTop > scrollContainer.scrollTop + windowHeight) {
|
|
scrollContainer.scrollTop = elementHeight + elementTop - windowHeight;
|
|
}
|
|
},
|
|
persistScrollTop: function (doc) {
|
|
persistedScrollTop = Dom.scrollContainer(doc).scrollTop;
|
|
},
|
|
restoreScrollTop: function (doc) {
|
|
Dom.scrollContainer(doc).scrollTop = persistedScrollTop;
|
|
},
|
|
insertAt: function (parent, newElement, position) {
|
|
parent.insertBefore(newElement, parent.childNodes[position] || null);
|
|
},
|
|
insertBefore: function (newElement, referenceElement) {
|
|
if (referenceElement.parentNode) {
|
|
return referenceElement.parentNode.insertBefore(newElement, referenceElement);
|
|
} else {
|
|
return referenceElement;
|
|
}
|
|
},
|
|
insertAfter: function (newElement, referenceElement) {
|
|
return referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
|
|
},
|
|
remove: function (node) {
|
|
node.parentNode.removeChild(node);
|
|
},
|
|
removeTextSiblings: function (node) {
|
|
var parentNode = node.parentNode;
|
|
while (node.nextSibling && node.nextSibling.nodeType == 3) {
|
|
parentNode.removeChild(node.nextSibling);
|
|
}
|
|
while (node.previousSibling && node.previousSibling.nodeType == 3) {
|
|
parentNode.removeChild(node.previousSibling);
|
|
}
|
|
},
|
|
trim: function (parent) {
|
|
for (var i = parent.childNodes.length - 1; i >= 0; i--) {
|
|
var node = parent.childNodes[i];
|
|
if (Dom.isDataNode(node)) {
|
|
if (!Dom.stripBom(node.nodeValue).length) {
|
|
Dom.remove(node);
|
|
}
|
|
if (Dom.isWhitespace(node)) {
|
|
Dom.insertBefore(node, parent);
|
|
}
|
|
} else if (node.className != KMARKER) {
|
|
Dom.trim(node);
|
|
if (!node.childNodes.length && !Dom.isEmpty(node)) {
|
|
Dom.remove(node);
|
|
}
|
|
}
|
|
}
|
|
return parent;
|
|
},
|
|
closest: function (node, tag) {
|
|
while (node && Dom.name(node) != tag) {
|
|
node = node.parentNode;
|
|
}
|
|
return node;
|
|
},
|
|
sibling: function (node, direction) {
|
|
do {
|
|
node = node[direction];
|
|
} while (node && node.nodeType != 1);
|
|
return node;
|
|
},
|
|
next: function (node) {
|
|
return Dom.sibling(node, 'nextSibling');
|
|
},
|
|
prev: function (node) {
|
|
return Dom.sibling(node, 'previousSibling');
|
|
},
|
|
parentOfType: function (node, tags) {
|
|
do {
|
|
node = node.parentNode;
|
|
} while (node && !Dom.ofType(node, tags));
|
|
return node;
|
|
},
|
|
ofType: function (node, tags) {
|
|
return $.inArray(Dom.name(node), tags) >= 0;
|
|
},
|
|
changeTag: function (referenceElement, tagName, skipAttributes) {
|
|
var newElement = Dom.create(referenceElement.ownerDocument, tagName), attributes = referenceElement.attributes, i, len, name, value, attribute;
|
|
if (!skipAttributes) {
|
|
for (i = 0, len = attributes.length; i < len; i++) {
|
|
attribute = attributes[i];
|
|
if (attribute.specified) {
|
|
name = attribute.nodeName;
|
|
value = attribute.nodeValue;
|
|
if (name == CLASS) {
|
|
newElement.className = value;
|
|
} else if (name == STYLE) {
|
|
newElement.style.cssText = referenceElement.style.cssText;
|
|
} else {
|
|
newElement.setAttribute(name, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (referenceElement.firstChild) {
|
|
newElement.appendChild(referenceElement.firstChild);
|
|
}
|
|
Dom.insertBefore(newElement, referenceElement);
|
|
Dom.remove(referenceElement);
|
|
return newElement;
|
|
},
|
|
editableParent: function (node) {
|
|
while (node && (node.nodeType == 3 || node.contentEditable !== 'true')) {
|
|
node = node.parentNode;
|
|
}
|
|
return node;
|
|
},
|
|
wrap: function (node, wrapper) {
|
|
Dom.insertBefore(wrapper, node);
|
|
wrapper.appendChild(node);
|
|
return wrapper;
|
|
},
|
|
unwrap: function (node) {
|
|
var parent = node.parentNode;
|
|
while (node.firstChild) {
|
|
parent.insertBefore(node.firstChild, node);
|
|
}
|
|
parent.removeChild(node);
|
|
},
|
|
create: function (document, tagName, attributes) {
|
|
return Dom.attr(document.createElement(tagName), attributes);
|
|
},
|
|
attr: function (element, attributes) {
|
|
attributes = extend({}, attributes);
|
|
if (attributes && STYLE in attributes) {
|
|
Dom.style(element, attributes.style);
|
|
delete attributes.style;
|
|
}
|
|
for (var attr in attributes) {
|
|
if (attributes[attr] === null) {
|
|
element.removeAttribute(attr);
|
|
delete attributes[attr];
|
|
} else if (attr == 'className') {
|
|
element[attr] = attributes[attr];
|
|
}
|
|
}
|
|
return extend(element, attributes);
|
|
},
|
|
style: function (node, value) {
|
|
$(node).css(value || {});
|
|
},
|
|
unstyle: function (node, value) {
|
|
for (var key in value) {
|
|
if (key == FLOAT) {
|
|
key = kendo.support.cssFloat ? CSSFLOAT : STYLEFLOAT;
|
|
}
|
|
node.style[key] = '';
|
|
}
|
|
if (node.style.cssText === '') {
|
|
node.removeAttribute(STYLE);
|
|
}
|
|
},
|
|
inlineStyle: function (body, name, attributes) {
|
|
var span = $(Dom.create(body.ownerDocument, name, attributes)), style;
|
|
body.appendChild(span[0]);
|
|
style = map(cssAttributes, function (value) {
|
|
if (browser.msie && value == 'line-height' && span.css(value) == '1px') {
|
|
return 'line-height:1.5';
|
|
} else {
|
|
return value + ':' + span.css(value);
|
|
}
|
|
}).join(';');
|
|
span.remove();
|
|
return style;
|
|
},
|
|
getEffectiveBackground: function (element) {
|
|
var backgroundStyle = element.css('background-color');
|
|
if (backgroundStyle.indexOf('rgba(0, 0, 0, 0') < 0 && backgroundStyle !== 'transparent') {
|
|
return backgroundStyle;
|
|
} else if (element[0].tagName.toLowerCase() === 'html') {
|
|
return 'Window';
|
|
} else {
|
|
return Dom.getEffectiveBackground(element.parent());
|
|
}
|
|
},
|
|
removeClass: function (node, classNames) {
|
|
var className = ' ' + node.className + ' ', classes = classNames.split(' '), i, len;
|
|
for (i = 0, len = classes.length; i < len; i++) {
|
|
className = className.replace(' ' + classes[i] + ' ', ' ');
|
|
}
|
|
className = $.trim(className);
|
|
if (className.length) {
|
|
node.className = className;
|
|
} else {
|
|
node.removeAttribute(CLASS);
|
|
}
|
|
},
|
|
commonAncestor: function () {
|
|
var count = arguments.length, paths = [], minPathLength = Infinity, output = null, i, ancestors, node, first, j;
|
|
if (!count) {
|
|
return null;
|
|
}
|
|
if (count == 1) {
|
|
return arguments[0];
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
ancestors = [];
|
|
node = arguments[i];
|
|
while (node) {
|
|
ancestors.push(node);
|
|
node = node.parentNode;
|
|
}
|
|
paths.push(ancestors.reverse());
|
|
minPathLength = Math.min(minPathLength, ancestors.length);
|
|
}
|
|
if (count == 1) {
|
|
return paths[0][0];
|
|
}
|
|
for (i = 0; i < minPathLength; i++) {
|
|
first = paths[0][i];
|
|
for (j = 1; j < count; j++) {
|
|
if (first != paths[j][i]) {
|
|
return output;
|
|
}
|
|
}
|
|
output = first;
|
|
}
|
|
return output;
|
|
},
|
|
closestSplittableParent: function (nodes) {
|
|
var result;
|
|
if (nodes.length == 1) {
|
|
result = Dom.parentOfType(nodes[0], [
|
|
'ul',
|
|
'ol'
|
|
]);
|
|
} else {
|
|
result = Dom.commonAncestor.apply(null, nodes);
|
|
}
|
|
if (!result) {
|
|
result = Dom.parentOfType(nodes[0], [
|
|
'p',
|
|
'td'
|
|
]) || nodes[0].ownerDocument.body;
|
|
}
|
|
if (Dom.isInline(result)) {
|
|
result = Dom.blockParentOrBody(result);
|
|
}
|
|
var editableParents = map(nodes, Dom.editableParent);
|
|
var editableAncestor = Dom.commonAncestor(editableParents)[0];
|
|
if ($.contains(result, editableAncestor)) {
|
|
result = editableAncestor;
|
|
}
|
|
return result;
|
|
},
|
|
closestEditable: function (node, types) {
|
|
var closest;
|
|
var editable = Dom.editableParent(node);
|
|
if (Dom.ofType(node, types)) {
|
|
closest = node;
|
|
} else {
|
|
closest = Dom.parentOfType(node, types);
|
|
}
|
|
if (closest && editable && $.contains(closest, editable)) {
|
|
closest = editable;
|
|
} else if (!closest && editable) {
|
|
closest = editable;
|
|
}
|
|
return closest;
|
|
},
|
|
closestEditableOfType: function (node, types) {
|
|
var editable = Dom.closestEditable(node, types);
|
|
if (editable && Dom.ofType(editable, types)) {
|
|
return editable;
|
|
}
|
|
},
|
|
filter: function (tagName, nodes, invert) {
|
|
var i = 0;
|
|
var len = nodes.length;
|
|
var result = [];
|
|
var name;
|
|
for (; i < len; i++) {
|
|
name = Dom.name(nodes[i]);
|
|
if (!invert && name == tagName || invert && name != tagName) {
|
|
result.push(nodes[i]);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
ensureTrailingBreaks: function (node) {
|
|
var elements = $(node).find('p,td,th');
|
|
var length = elements.length;
|
|
var i = 0;
|
|
if (length) {
|
|
for (; i < length; i++) {
|
|
Dom.ensureTrailingBreak(elements[i]);
|
|
}
|
|
} else {
|
|
Dom.ensureTrailingBreak(node);
|
|
}
|
|
},
|
|
removeTrailingBreak: function (node) {
|
|
$(node).find('br[type=_moz],.k-br').remove();
|
|
},
|
|
ensureTrailingBreak: function (node) {
|
|
Dom.removeTrailingBreak(node);
|
|
var lastChild = node.lastChild;
|
|
var name = lastChild && Dom.name(lastChild);
|
|
var br;
|
|
if (!name || name != 'br' && name != 'img' || name == 'br' && lastChild.className != 'k-br') {
|
|
br = node.ownerDocument.createElement('br');
|
|
br.className = 'k-br';
|
|
node.appendChild(br);
|
|
}
|
|
}
|
|
};
|
|
kendo.ui.editor.Dom = Dom;
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/serializer', ['editor/dom'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var Editor = kendo.ui.editor;
|
|
var dom = Editor.Dom;
|
|
var extend = $.extend;
|
|
var fontSizeMappings = 'xx-small,x-small,small,medium,large,x-large,xx-large'.split(',');
|
|
var quoteRe = /"/g;
|
|
var brRe = /<br[^>]*>/i;
|
|
var pixelRe = /^\d+(\.\d*)?(px)?$/i;
|
|
var emptyPRe = /<p><\/p>/i;
|
|
var cssDeclaration = /(\*?[-#\/\*\\\w]+(?:\[[0-9a-z_-]+\])?)\s*:\s*((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/g;
|
|
var sizzleAttr = /^sizzle-\d+/i;
|
|
var scriptAttr = /^k-script-/i;
|
|
var onerrorRe = /\s*onerror\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/i;
|
|
var div = document.createElement('div');
|
|
div.innerHTML = ' <hr>';
|
|
var supportsLeadingWhitespace = div.firstChild.nodeType === 3;
|
|
div = null;
|
|
var Serializer = {
|
|
toEditableHtml: function (html) {
|
|
var br = '<br class="k-br">';
|
|
html = html || '';
|
|
return html.replace(/<!\[CDATA\[(.*)?\]\]>/g, '<!--[CDATA[$1]]-->').replace(/<(\/?)script([^>]*)>/gi, '<$1k:script$2>').replace(/<img([^>]*)>/gi, function (match) {
|
|
return match.replace(onerrorRe, '');
|
|
}).replace(/(<\/?img[^>]*>)[\r\n\v\f\t ]+/gi, '$1').replace(/^<(table|blockquote)/i, br + '<$1').replace(/^[\s]*( |\u00a0)/i, '$1').replace(/<\/(table|blockquote)>$/i, '</$1>' + br);
|
|
},
|
|
_fillEmptyElements: function (body) {
|
|
$(body).find('p').each(function () {
|
|
var p = $(this);
|
|
if (/^\s*$/g.test(p.text()) && !p.find('img,input').length) {
|
|
var node = this;
|
|
while (node.firstChild && node.firstChild.nodeType != 3) {
|
|
node = node.firstChild;
|
|
}
|
|
if (node.nodeType == 1 && !dom.empty[dom.name(node)]) {
|
|
node.innerHTML = kendo.ui.editor.emptyElementContent;
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_removeSystemElements: function (body) {
|
|
$('.k-paste-container', body).remove();
|
|
},
|
|
_resetOrderedLists: function (root) {
|
|
var ols = root.getElementsByTagName('ol'), i, ol, originalStart;
|
|
for (i = 0; i < ols.length; i++) {
|
|
ol = ols[i];
|
|
originalStart = ol.getAttribute('start');
|
|
ol.setAttribute('start', 1);
|
|
if (originalStart) {
|
|
ol.setAttribute('start', originalStart);
|
|
} else {
|
|
ol.removeAttribute(originalStart);
|
|
}
|
|
}
|
|
},
|
|
_preventScriptExecution: function (root) {
|
|
$(root).find('*').each(function () {
|
|
var attributes = this.attributes;
|
|
var attribute, i, l, name;
|
|
for (i = 0, l = attributes.length; i < l; i++) {
|
|
attribute = attributes[i];
|
|
name = attribute.nodeName;
|
|
if (attribute.specified && /^on/i.test(name)) {
|
|
this.setAttribute('k-script-' + name, attribute.value);
|
|
this.removeAttribute(name);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
htmlToDom: function (html, root) {
|
|
var browser = kendo.support.browser;
|
|
var msie = browser.msie;
|
|
var legacyIE = msie && browser.version < 9;
|
|
var originalSrc = 'originalsrc';
|
|
var originalHref = 'originalhref';
|
|
html = Serializer.toEditableHtml(html);
|
|
if (legacyIE) {
|
|
html = '<br/>' + html;
|
|
html = html.replace(/href\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalHref + '="$1"');
|
|
html = html.replace(/src\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalSrc + '="$1"');
|
|
}
|
|
root.innerHTML = html;
|
|
if (legacyIE) {
|
|
dom.remove(root.firstChild);
|
|
$(root).find('k\\:script,script,link,img,a').each(function () {
|
|
var node = this;
|
|
if (node[originalHref]) {
|
|
node.setAttribute('href', node[originalHref]);
|
|
node.removeAttribute(originalHref);
|
|
}
|
|
if (node[originalSrc]) {
|
|
node.setAttribute('src', node[originalSrc]);
|
|
node.removeAttribute(originalSrc);
|
|
}
|
|
});
|
|
} else if (msie) {
|
|
dom.normalize(root);
|
|
Serializer._resetOrderedLists(root);
|
|
}
|
|
Serializer._preventScriptExecution(root);
|
|
Serializer._fillEmptyElements(root);
|
|
Serializer._removeSystemElements(root);
|
|
$('table', root).addClass('k-table');
|
|
return root;
|
|
},
|
|
domToXhtml: function (root, options) {
|
|
var result = [];
|
|
function semanticFilter(attributes) {
|
|
return $.grep(attributes, function (attr) {
|
|
return attr.name != 'style';
|
|
});
|
|
}
|
|
var tagMap = {
|
|
iframe: {
|
|
start: function (node) {
|
|
result.push('<iframe');
|
|
attr(node);
|
|
result.push('>');
|
|
},
|
|
end: function () {
|
|
result.push('</iframe>');
|
|
}
|
|
},
|
|
'k:script': {
|
|
start: function (node) {
|
|
result.push('<script');
|
|
attr(node);
|
|
result.push('>');
|
|
},
|
|
end: function () {
|
|
result.push('</script>');
|
|
},
|
|
skipEncoding: true
|
|
},
|
|
span: {
|
|
semantic: true,
|
|
start: function (node) {
|
|
var style = node.style;
|
|
var attributes = specifiedAttributes(node);
|
|
var semanticAttributes = semanticFilter(attributes);
|
|
if (semanticAttributes.length) {
|
|
result.push('<span');
|
|
attr(node, semanticAttributes);
|
|
result.push('>');
|
|
}
|
|
if (style.textDecoration == 'underline') {
|
|
result.push('<u>');
|
|
}
|
|
var font = [];
|
|
if (style.color) {
|
|
font.push('color="' + dom.toHex(style.color) + '"');
|
|
}
|
|
if (style.fontFamily) {
|
|
font.push('face="' + style.fontFamily + '"');
|
|
}
|
|
if (style.fontSize) {
|
|
var size = $.inArray(style.fontSize, fontSizeMappings);
|
|
font.push('size="' + size + '"');
|
|
}
|
|
if (font.length) {
|
|
result.push('<font ' + font.join(' ') + '>');
|
|
}
|
|
},
|
|
end: function (node) {
|
|
var style = node.style;
|
|
if (style.color || style.fontFamily || style.fontSize) {
|
|
result.push('</font>');
|
|
}
|
|
if (style.textDecoration == 'underline') {
|
|
result.push('</u>');
|
|
}
|
|
if (semanticFilter(specifiedAttributes(node)).length) {
|
|
result.push('</span>');
|
|
}
|
|
}
|
|
},
|
|
strong: {
|
|
semantic: true,
|
|
start: function () {
|
|
result.push('<b>');
|
|
},
|
|
end: function () {
|
|
result.push('</b>');
|
|
}
|
|
},
|
|
em: {
|
|
semantic: true,
|
|
start: function () {
|
|
result.push('<i>');
|
|
},
|
|
end: function () {
|
|
result.push('</i>');
|
|
}
|
|
},
|
|
b: {
|
|
semantic: false,
|
|
start: function () {
|
|
result.push('<strong>');
|
|
},
|
|
end: function () {
|
|
result.push('</strong>');
|
|
}
|
|
},
|
|
i: {
|
|
semantic: false,
|
|
start: function () {
|
|
result.push('<em>');
|
|
},
|
|
end: function () {
|
|
result.push('</em>');
|
|
}
|
|
},
|
|
u: {
|
|
semantic: false,
|
|
start: function () {
|
|
result.push('<span style="text-decoration:underline;">');
|
|
},
|
|
end: function () {
|
|
result.push('</span>');
|
|
}
|
|
},
|
|
font: {
|
|
semantic: false,
|
|
start: function (node) {
|
|
result.push('<span style="');
|
|
var color = node.getAttribute('color');
|
|
var size = fontSizeMappings[node.getAttribute('size')];
|
|
var face = node.getAttribute('face');
|
|
if (color) {
|
|
result.push('color:');
|
|
result.push(dom.toHex(color));
|
|
result.push(';');
|
|
}
|
|
if (face) {
|
|
result.push('font-family:');
|
|
result.push(face);
|
|
result.push(';');
|
|
}
|
|
if (size) {
|
|
result.push('font-size:');
|
|
result.push(size);
|
|
result.push(';');
|
|
}
|
|
result.push('">');
|
|
},
|
|
end: function () {
|
|
result.push('</span>');
|
|
}
|
|
}
|
|
};
|
|
tagMap.script = tagMap['k:script'];
|
|
options = options || {};
|
|
if (typeof options.semantic == 'undefined') {
|
|
options.semantic = true;
|
|
}
|
|
function cssProperties(cssText) {
|
|
var trim = $.trim;
|
|
var css = trim(cssText);
|
|
var match;
|
|
var property, value;
|
|
var properties = [];
|
|
cssDeclaration.lastIndex = 0;
|
|
while (true) {
|
|
match = cssDeclaration.exec(css);
|
|
if (!match) {
|
|
break;
|
|
}
|
|
property = trim(match[1].toLowerCase());
|
|
value = trim(match[2]);
|
|
if (property == 'font-size-adjust' || property == 'font-stretch') {
|
|
continue;
|
|
}
|
|
if (property.indexOf('color') >= 0) {
|
|
value = dom.toHex(value);
|
|
} else if (property.indexOf('font') >= 0) {
|
|
value = value.replace(quoteRe, '\'');
|
|
} else if (/\burl\(/g.test(value)) {
|
|
value = value.replace(quoteRe, '');
|
|
}
|
|
properties.push({
|
|
property: property,
|
|
value: value
|
|
});
|
|
}
|
|
return properties;
|
|
}
|
|
function styleAttr(cssText) {
|
|
var properties = cssProperties(cssText);
|
|
var i;
|
|
for (i = 0; i < properties.length; i++) {
|
|
result.push(properties[i].property);
|
|
result.push(':');
|
|
result.push(properties[i].value);
|
|
result.push(';');
|
|
}
|
|
}
|
|
function specifiedAttributes(node) {
|
|
var result = [];
|
|
var attributes = node.attributes;
|
|
var attribute, i, l;
|
|
var name, value, specified;
|
|
for (i = 0, l = attributes.length; i < l; i++) {
|
|
attribute = attributes[i];
|
|
name = attribute.nodeName;
|
|
value = attribute.value;
|
|
specified = attribute.specified;
|
|
if (name == 'value' && 'value' in node && node.value) {
|
|
specified = true;
|
|
} else if (name == 'type' && value == 'text') {
|
|
specified = true;
|
|
} else if (name == 'class' && !value) {
|
|
specified = false;
|
|
} else if (sizzleAttr.test(name)) {
|
|
specified = false;
|
|
} else if (name == 'complete') {
|
|
specified = false;
|
|
} else if (name == 'altHtml') {
|
|
specified = false;
|
|
} else if (name == 'start' && dom.is(node, 'ul')) {
|
|
specified = false;
|
|
} else if (name == 'start' && dom.is(node, 'ol') && value == '1') {
|
|
specified = false;
|
|
} else if (name.indexOf('_moz') >= 0) {
|
|
specified = false;
|
|
} else if (scriptAttr.test(name)) {
|
|
specified = !!options.scripts;
|
|
}
|
|
if (specified) {
|
|
result.push(attribute);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function attr(node, attributes) {
|
|
var i, l, attribute, name, value;
|
|
attributes = attributes || specifiedAttributes(node);
|
|
if (dom.is(node, 'img')) {
|
|
var width = node.style.width, height = node.style.height, $node = $(node);
|
|
if (width && pixelRe.test(width)) {
|
|
$node.attr('width', parseInt(width, 10));
|
|
dom.unstyle(node, { width: undefined });
|
|
}
|
|
if (height && pixelRe.test(height)) {
|
|
$node.attr('height', parseInt(height, 10));
|
|
dom.unstyle(node, { height: undefined });
|
|
}
|
|
}
|
|
if (!attributes.length) {
|
|
return;
|
|
}
|
|
attributes.sort(function (a, b) {
|
|
return a.nodeName > b.nodeName ? 1 : a.nodeName < b.nodeName ? -1 : 0;
|
|
});
|
|
for (i = 0, l = attributes.length; i < l; i++) {
|
|
attribute = attributes[i];
|
|
name = attribute.nodeName;
|
|
value = attribute.value;
|
|
if (name == 'class' && value == 'k-table') {
|
|
continue;
|
|
}
|
|
name = name.replace(scriptAttr, '');
|
|
result.push(' ');
|
|
result.push(name);
|
|
result.push('="');
|
|
if (name == 'style') {
|
|
styleAttr(value || node.style.cssText);
|
|
} else if (name == 'src' || name == 'href') {
|
|
result.push(kendo.htmlEncode(node.getAttribute(name, 2)));
|
|
} else {
|
|
result.push(dom.fillAttrs[name] ? name : value);
|
|
}
|
|
result.push('"');
|
|
}
|
|
}
|
|
function children(node, skip, skipEncoding) {
|
|
for (var childNode = node.firstChild; childNode; childNode = childNode.nextSibling) {
|
|
child(childNode, skip, skipEncoding);
|
|
}
|
|
}
|
|
function text(node) {
|
|
return node.nodeValue.replace(/\ufeff/g, '');
|
|
}
|
|
function child(node, skip, skipEncoding) {
|
|
var nodeType = node.nodeType, tagName, mapper, parent, value, previous;
|
|
if (nodeType == 1) {
|
|
tagName = dom.name(node);
|
|
if (!tagName || dom.insignificant(node)) {
|
|
return;
|
|
}
|
|
if (dom.isInline(node) && node.childNodes.length == 1 && node.firstChild.nodeType == 3 && !text(node.firstChild)) {
|
|
return;
|
|
}
|
|
if (!options.scripts && (tagName == 'script' || tagName == 'k:script')) {
|
|
return;
|
|
}
|
|
mapper = tagMap[tagName];
|
|
if (mapper) {
|
|
if (typeof mapper.semantic == 'undefined' || options.semantic ^ mapper.semantic) {
|
|
mapper.start(node);
|
|
children(node, false, mapper.skipEncoding);
|
|
mapper.end(node);
|
|
return;
|
|
}
|
|
}
|
|
result.push('<');
|
|
result.push(tagName);
|
|
attr(node);
|
|
if (dom.empty[tagName]) {
|
|
result.push(' />');
|
|
} else {
|
|
result.push('>');
|
|
children(node, skip || dom.is(node, 'pre'));
|
|
result.push('</');
|
|
result.push(tagName);
|
|
result.push('>');
|
|
}
|
|
} else if (nodeType == 3) {
|
|
value = text(node);
|
|
if (!skip && supportsLeadingWhitespace) {
|
|
parent = node.parentNode;
|
|
previous = node.previousSibling;
|
|
if (!previous) {
|
|
previous = (dom.isInline(parent) ? parent : node).previousSibling;
|
|
}
|
|
if (!previous || previous.innerHTML === '' || dom.isBlock(previous)) {
|
|
value = value.replace(/^[\r\n\v\f\t ]+/, '');
|
|
}
|
|
value = value.replace(/ +/, ' ');
|
|
}
|
|
result.push(skipEncoding ? value : dom.encode(value, options));
|
|
} else if (nodeType == 4) {
|
|
result.push('<![CDATA[');
|
|
result.push(node.data);
|
|
result.push(']]>');
|
|
} else if (nodeType == 8) {
|
|
if (node.data.indexOf('[CDATA[') < 0) {
|
|
result.push('<!--');
|
|
result.push(node.data);
|
|
result.push('-->');
|
|
} else {
|
|
result.push('<!');
|
|
result.push(node.data);
|
|
result.push('>');
|
|
}
|
|
}
|
|
}
|
|
function textOnly(root) {
|
|
var childrenCount = root.childNodes.length;
|
|
var textChild = childrenCount && root.firstChild.nodeType == 3;
|
|
return textChild && (childrenCount == 1 || childrenCount == 2 && dom.insignificant(root.lastChild));
|
|
}
|
|
if (textOnly(root)) {
|
|
return dom.encode(text(root.firstChild).replace(/[\r\n\v\f\t ]+/, ' '), options);
|
|
}
|
|
children(root);
|
|
result = result.join('');
|
|
if (result.replace(brRe, '').replace(emptyPRe, '') === '') {
|
|
return '';
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
extend(Editor, { Serializer: Serializer });
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/range', ['editor/serializer'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, browser = kendo.support.browser, dom = Editor.Dom, findNodeIndex = dom.findNodeIndex, isDataNode = dom.isDataNode, findClosestAncestor = dom.findClosestAncestor, getNodeLength = dom.getNodeLength, normalize = dom.normalize;
|
|
var SelectionUtils = {
|
|
selectionFromWindow: function (window) {
|
|
if (!('getSelection' in window)) {
|
|
return new W3CSelection(window.document);
|
|
}
|
|
return window.getSelection();
|
|
},
|
|
selectionFromRange: function (range) {
|
|
var rangeDocument = RangeUtils.documentFromRange(range);
|
|
return SelectionUtils.selectionFromDocument(rangeDocument);
|
|
},
|
|
selectionFromDocument: function (document) {
|
|
return SelectionUtils.selectionFromWindow(dom.windowFromDocument(document));
|
|
}
|
|
};
|
|
var W3CRange = Class.extend({
|
|
init: function (doc) {
|
|
$.extend(this, {
|
|
ownerDocument: doc,
|
|
startContainer: doc,
|
|
endContainer: doc,
|
|
commonAncestorContainer: doc,
|
|
startOffset: 0,
|
|
endOffset: 0,
|
|
collapsed: true
|
|
});
|
|
},
|
|
setStart: function (node, offset) {
|
|
this.startContainer = node;
|
|
this.startOffset = offset;
|
|
updateRangeProperties(this);
|
|
fixIvalidRange(this, true);
|
|
},
|
|
setEnd: function (node, offset) {
|
|
this.endContainer = node;
|
|
this.endOffset = offset;
|
|
updateRangeProperties(this);
|
|
fixIvalidRange(this, false);
|
|
},
|
|
setStartBefore: function (node) {
|
|
this.setStart(node.parentNode, findNodeIndex(node));
|
|
},
|
|
setStartAfter: function (node) {
|
|
this.setStart(node.parentNode, findNodeIndex(node) + 1);
|
|
},
|
|
setEndBefore: function (node) {
|
|
this.setEnd(node.parentNode, findNodeIndex(node));
|
|
},
|
|
setEndAfter: function (node) {
|
|
this.setEnd(node.parentNode, findNodeIndex(node) + 1);
|
|
},
|
|
selectNode: function (node) {
|
|
this.setStartBefore(node);
|
|
this.setEndAfter(node);
|
|
},
|
|
selectNodeContents: function (node) {
|
|
this.setStart(node, 0);
|
|
this.setEnd(node, node[node.nodeType === 1 ? 'childNodes' : 'nodeValue'].length);
|
|
},
|
|
collapse: function (toStart) {
|
|
var that = this;
|
|
if (toStart) {
|
|
that.setEnd(that.startContainer, that.startOffset);
|
|
} else {
|
|
that.setStart(that.endContainer, that.endOffset);
|
|
}
|
|
},
|
|
deleteContents: function () {
|
|
var that = this, range = that.cloneRange();
|
|
if (that.startContainer != that.commonAncestorContainer) {
|
|
that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer));
|
|
}
|
|
that.collapse(true);
|
|
(function deleteSubtree(iterator) {
|
|
while (iterator.next()) {
|
|
if (iterator.hasPartialSubtree()) {
|
|
deleteSubtree(iterator.getSubtreeIterator());
|
|
} else {
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}(new RangeIterator(range)));
|
|
},
|
|
cloneContents: function () {
|
|
var document = RangeUtils.documentFromRange(this);
|
|
return function cloneSubtree(iterator) {
|
|
var node, frag = document.createDocumentFragment();
|
|
while (node = iterator.next()) {
|
|
node = node.cloneNode(!iterator.hasPartialSubtree());
|
|
if (iterator.hasPartialSubtree()) {
|
|
node.appendChild(cloneSubtree(iterator.getSubtreeIterator()));
|
|
}
|
|
frag.appendChild(node);
|
|
}
|
|
return frag;
|
|
}(new RangeIterator(this));
|
|
},
|
|
extractContents: function () {
|
|
var that = this, range = that.cloneRange();
|
|
if (that.startContainer != that.commonAncestorContainer) {
|
|
that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer));
|
|
}
|
|
that.collapse(true);
|
|
var document = RangeUtils.documentFromRange(that);
|
|
return function extractSubtree(iterator) {
|
|
var node, frag = document.createDocumentFragment();
|
|
while (node = iterator.next()) {
|
|
if (iterator.hasPartialSubtree()) {
|
|
node = node.cloneNode(false);
|
|
node.appendChild(extractSubtree(iterator.getSubtreeIterator()));
|
|
} else {
|
|
iterator.remove(that.originalRange);
|
|
}
|
|
frag.appendChild(node);
|
|
}
|
|
return frag;
|
|
}(new RangeIterator(range));
|
|
},
|
|
insertNode: function (node) {
|
|
var that = this;
|
|
if (isDataNode(that.startContainer)) {
|
|
if (that.startOffset != that.startContainer.nodeValue.length) {
|
|
dom.splitDataNode(that.startContainer, that.startOffset);
|
|
}
|
|
dom.insertAfter(node, that.startContainer);
|
|
} else {
|
|
dom.insertAt(that.startContainer, node, that.startOffset);
|
|
}
|
|
that.setStart(that.startContainer, that.startOffset);
|
|
},
|
|
cloneRange: function () {
|
|
return $.extend(new W3CRange(this.ownerDocument), {
|
|
startContainer: this.startContainer,
|
|
endContainer: this.endContainer,
|
|
commonAncestorContainer: this.commonAncestorContainer,
|
|
startOffset: this.startOffset,
|
|
endOffset: this.endOffset,
|
|
collapsed: this.collapsed,
|
|
originalRange: this
|
|
});
|
|
},
|
|
toString: function () {
|
|
var startNodeName = this.startContainer.nodeName, endNodeName = this.endContainer.nodeName;
|
|
return [
|
|
startNodeName == '#text' ? this.startContainer.nodeValue : startNodeName,
|
|
'(',
|
|
this.startOffset,
|
|
') : ',
|
|
endNodeName == '#text' ? this.endContainer.nodeValue : endNodeName,
|
|
'(',
|
|
this.endOffset,
|
|
')'
|
|
].join('');
|
|
}
|
|
});
|
|
W3CRange.fromNode = function (node) {
|
|
return new W3CRange(node.ownerDocument);
|
|
};
|
|
function compareBoundaries(start, end, startOffset, endOffset) {
|
|
if (start == end) {
|
|
return endOffset - startOffset;
|
|
}
|
|
var container = end;
|
|
while (container && container.parentNode != start) {
|
|
container = container.parentNode;
|
|
}
|
|
if (container) {
|
|
return findNodeIndex(container) - startOffset;
|
|
}
|
|
container = start;
|
|
while (container && container.parentNode != end) {
|
|
container = container.parentNode;
|
|
}
|
|
if (container) {
|
|
return endOffset - findNodeIndex(container) - 1;
|
|
}
|
|
var root = dom.commonAncestor(start, end);
|
|
var startAncestor = start;
|
|
while (startAncestor && startAncestor.parentNode != root) {
|
|
startAncestor = startAncestor.parentNode;
|
|
}
|
|
if (!startAncestor) {
|
|
startAncestor = root;
|
|
}
|
|
var endAncestor = end;
|
|
while (endAncestor && endAncestor.parentNode != root) {
|
|
endAncestor = endAncestor.parentNode;
|
|
}
|
|
if (!endAncestor) {
|
|
endAncestor = root;
|
|
}
|
|
if (startAncestor == endAncestor) {
|
|
return 0;
|
|
}
|
|
return findNodeIndex(endAncestor) - findNodeIndex(startAncestor);
|
|
}
|
|
function fixIvalidRange(range, toStart) {
|
|
function isInvalidRange(range) {
|
|
try {
|
|
return compareBoundaries(range.startContainer, range.endContainer, range.startOffset, range.endOffset) < 0;
|
|
} catch (ex) {
|
|
return true;
|
|
}
|
|
}
|
|
if (isInvalidRange(range)) {
|
|
if (toStart) {
|
|
range.commonAncestorContainer = range.endContainer = range.startContainer;
|
|
range.endOffset = range.startOffset;
|
|
} else {
|
|
range.commonAncestorContainer = range.startContainer = range.endContainer;
|
|
range.startOffset = range.endOffset;
|
|
}
|
|
range.collapsed = true;
|
|
}
|
|
}
|
|
function updateRangeProperties(range) {
|
|
range.collapsed = range.startContainer == range.endContainer && range.startOffset == range.endOffset;
|
|
var node = range.startContainer;
|
|
while (node && node != range.endContainer && !dom.isAncestorOf(node, range.endContainer)) {
|
|
node = node.parentNode;
|
|
}
|
|
range.commonAncestorContainer = node;
|
|
}
|
|
var RangeIterator = Class.extend({
|
|
init: function (range) {
|
|
$.extend(this, {
|
|
range: range,
|
|
_current: null,
|
|
_next: null,
|
|
_end: null
|
|
});
|
|
if (range.collapsed) {
|
|
return;
|
|
}
|
|
var root = range.commonAncestorContainer;
|
|
this._next = range.startContainer == root && !isDataNode(range.startContainer) ? range.startContainer.childNodes[range.startOffset] : findClosestAncestor(root, range.startContainer);
|
|
this._end = range.endContainer == root && !isDataNode(range.endContainer) ? range.endContainer.childNodes[range.endOffset] : findClosestAncestor(root, range.endContainer).nextSibling;
|
|
},
|
|
hasNext: function () {
|
|
return !!this._next;
|
|
},
|
|
next: function () {
|
|
var that = this, current = that._current = that._next;
|
|
that._next = that._current && that._current.nextSibling != that._end ? that._current.nextSibling : null;
|
|
if (isDataNode(that._current)) {
|
|
if (that.range.endContainer == that._current) {
|
|
current = current.cloneNode(true);
|
|
current.deleteData(that.range.endOffset, current.length - that.range.endOffset);
|
|
}
|
|
if (that.range.startContainer == that._current) {
|
|
current = current.cloneNode(true);
|
|
current.deleteData(0, that.range.startOffset);
|
|
}
|
|
}
|
|
return current;
|
|
},
|
|
traverse: function (callback) {
|
|
var that = this, current;
|
|
function next() {
|
|
that._current = that._next;
|
|
that._next = that._current && that._current.nextSibling != that._end ? that._current.nextSibling : null;
|
|
return that._current;
|
|
}
|
|
while (current = next()) {
|
|
if (that.hasPartialSubtree()) {
|
|
that.getSubtreeIterator().traverse(callback);
|
|
} else {
|
|
callback(current);
|
|
}
|
|
}
|
|
return current;
|
|
},
|
|
remove: function (originalRange) {
|
|
var that = this, inStartContainer = that.range.startContainer == that._current, inEndContainer = that.range.endContainer == that._current, start, end, delta;
|
|
if (isDataNode(that._current) && (inStartContainer || inEndContainer)) {
|
|
start = inStartContainer ? that.range.startOffset : 0;
|
|
end = inEndContainer ? that.range.endOffset : that._current.length;
|
|
delta = end - start;
|
|
if (originalRange && (inStartContainer || inEndContainer)) {
|
|
if (that._current == originalRange.startContainer && start <= originalRange.startOffset) {
|
|
originalRange.startOffset -= delta;
|
|
}
|
|
if (that._current == originalRange.endContainer && end <= originalRange.endOffset) {
|
|
originalRange.endOffset -= delta;
|
|
}
|
|
}
|
|
that._current.deleteData(start, delta);
|
|
} else {
|
|
var parent = that._current.parentNode;
|
|
if (originalRange && (that.range.startContainer == parent || that.range.endContainer == parent)) {
|
|
var nodeIndex = findNodeIndex(that._current);
|
|
if (parent == originalRange.startContainer && nodeIndex <= originalRange.startOffset) {
|
|
originalRange.startOffset -= 1;
|
|
}
|
|
if (parent == originalRange.endContainer && nodeIndex < originalRange.endOffset) {
|
|
originalRange.endOffset -= 1;
|
|
}
|
|
}
|
|
dom.remove(that._current);
|
|
}
|
|
},
|
|
hasPartialSubtree: function () {
|
|
return !isDataNode(this._current) && (dom.isAncestorOrSelf(this._current, this.range.startContainer) || dom.isAncestorOrSelf(this._current, this.range.endContainer));
|
|
},
|
|
getSubtreeIterator: function () {
|
|
var that = this, subRange = that.range.cloneRange();
|
|
subRange.selectNodeContents(that._current);
|
|
if (dom.isAncestorOrSelf(that._current, that.range.startContainer)) {
|
|
subRange.setStart(that.range.startContainer, that.range.startOffset);
|
|
}
|
|
if (dom.isAncestorOrSelf(that._current, that.range.endContainer)) {
|
|
subRange.setEnd(that.range.endContainer, that.range.endOffset);
|
|
}
|
|
return new RangeIterator(subRange);
|
|
}
|
|
});
|
|
var W3CSelection = Class.extend({
|
|
init: function (doc) {
|
|
this.ownerDocument = doc;
|
|
this.rangeCount = 1;
|
|
},
|
|
addRange: function (range) {
|
|
var textRange = this.ownerDocument.body.createTextRange();
|
|
adoptContainer(textRange, range, false);
|
|
adoptContainer(textRange, range, true);
|
|
textRange.select();
|
|
},
|
|
removeAllRanges: function () {
|
|
var selection = this.ownerDocument.selection;
|
|
if (selection.type != 'None') {
|
|
selection.empty();
|
|
}
|
|
},
|
|
getRangeAt: function () {
|
|
var textRange, range = new W3CRange(this.ownerDocument), selection = this.ownerDocument.selection, element, commonAncestor;
|
|
try {
|
|
textRange = selection.createRange();
|
|
element = textRange.item ? textRange.item(0) : textRange.parentElement();
|
|
if (element.ownerDocument != this.ownerDocument) {
|
|
return range;
|
|
}
|
|
} catch (ex) {
|
|
return range;
|
|
}
|
|
if (selection.type == 'Control') {
|
|
range.selectNode(textRange.item(0));
|
|
} else {
|
|
commonAncestor = textRangeContainer(textRange);
|
|
adoptEndPoint(textRange, range, commonAncestor, true);
|
|
adoptEndPoint(textRange, range, commonAncestor, false);
|
|
if (range.startContainer.nodeType == 9) {
|
|
range.setStart(range.endContainer, range.startOffset);
|
|
}
|
|
if (range.endContainer.nodeType == 9) {
|
|
range.setEnd(range.startContainer, range.endOffset);
|
|
}
|
|
if (textRange.compareEndPoints('StartToEnd', textRange) === 0) {
|
|
range.collapse(false);
|
|
}
|
|
var startContainer = range.startContainer, endContainer = range.endContainer, body = this.ownerDocument.body;
|
|
if (!range.collapsed && range.startOffset === 0 && range.endOffset == getNodeLength(range.endContainer) && !(startContainer == endContainer && isDataNode(startContainer) && startContainer.parentNode == body)) {
|
|
var movedStart = false, movedEnd = false;
|
|
while (findNodeIndex(startContainer) === 0 && startContainer == startContainer.parentNode.firstChild && startContainer != body) {
|
|
startContainer = startContainer.parentNode;
|
|
movedStart = true;
|
|
}
|
|
while (findNodeIndex(endContainer) == getNodeLength(endContainer.parentNode) - 1 && endContainer == endContainer.parentNode.lastChild && endContainer != body) {
|
|
endContainer = endContainer.parentNode;
|
|
movedEnd = true;
|
|
}
|
|
if (startContainer == body && endContainer == body && movedStart && movedEnd) {
|
|
range.setStart(startContainer, 0);
|
|
range.setEnd(endContainer, getNodeLength(body));
|
|
}
|
|
}
|
|
}
|
|
return range;
|
|
}
|
|
});
|
|
function textRangeContainer(textRange) {
|
|
var left = textRange.duplicate(), right = textRange.duplicate();
|
|
left.collapse(true);
|
|
right.collapse(false);
|
|
return dom.commonAncestor(textRange.parentElement(), left.parentElement(), right.parentElement());
|
|
}
|
|
function adoptContainer(textRange, range, start) {
|
|
var container = range[start ? 'startContainer' : 'endContainer'], offset = range[start ? 'startOffset' : 'endOffset'], textOffset = 0, isData = isDataNode(container), anchorNode = isData ? container : container.childNodes[offset] || null, anchorParent = isData ? container.parentNode : container, doc = range.ownerDocument, cursor = doc.body.createTextRange(), cursorNode;
|
|
if (container.nodeType == 3 || container.nodeType == 4) {
|
|
textOffset = offset;
|
|
}
|
|
if (!anchorParent) {
|
|
anchorParent = doc.body;
|
|
}
|
|
if (anchorParent.nodeName.toLowerCase() == 'img') {
|
|
cursor.moveToElementText(anchorParent);
|
|
cursor.collapse(false);
|
|
textRange.setEndPoint(start ? 'StartToStart' : 'EndToStart', cursor);
|
|
} else {
|
|
cursorNode = anchorParent.insertBefore(dom.create(doc, 'a'), anchorNode);
|
|
cursor.moveToElementText(cursorNode);
|
|
dom.remove(cursorNode);
|
|
cursor[start ? 'moveStart' : 'moveEnd']('character', textOffset);
|
|
cursor.collapse(false);
|
|
textRange.setEndPoint(start ? 'StartToStart' : 'EndToStart', cursor);
|
|
}
|
|
}
|
|
function adoptEndPoint(textRange, range, commonAncestor, start) {
|
|
var cursorNode = dom.create(range.ownerDocument, 'a'), cursor = textRange.duplicate(), comparison = start ? 'StartToStart' : 'StartToEnd', result, parent, target, previous, next, args, index, appended = false;
|
|
cursorNode.innerHTML = '\uFEFF';
|
|
cursor.collapse(start);
|
|
parent = cursor.parentElement();
|
|
if (!dom.isAncestorOrSelf(commonAncestor, parent)) {
|
|
parent = commonAncestor;
|
|
}
|
|
do {
|
|
if (appended) {
|
|
parent.insertBefore(cursorNode, cursorNode.previousSibling);
|
|
} else {
|
|
parent.appendChild(cursorNode);
|
|
appended = true;
|
|
}
|
|
cursor.moveToElementText(cursorNode);
|
|
} while ((result = cursor.compareEndPoints(comparison, textRange)) > 0 && cursorNode.previousSibling);
|
|
target = cursorNode.nextSibling;
|
|
if (result == -1 && isDataNode(target)) {
|
|
cursor.setEndPoint(start ? 'EndToStart' : 'EndToEnd', textRange);
|
|
dom.remove(cursorNode);
|
|
args = [
|
|
target,
|
|
cursor.text.length
|
|
];
|
|
} else {
|
|
previous = !start && cursorNode.previousSibling;
|
|
next = start && cursorNode.nextSibling;
|
|
if (isDataNode(next)) {
|
|
args = [
|
|
next,
|
|
0
|
|
];
|
|
} else if (isDataNode(previous)) {
|
|
args = [
|
|
previous,
|
|
previous.length
|
|
];
|
|
} else {
|
|
index = findNodeIndex(cursorNode);
|
|
if (parent.nextSibling && index == parent.childNodes.length - 1) {
|
|
args = [
|
|
parent.nextSibling,
|
|
0
|
|
];
|
|
} else {
|
|
args = [
|
|
parent,
|
|
index
|
|
];
|
|
}
|
|
}
|
|
dom.remove(cursorNode);
|
|
}
|
|
range[start ? 'setStart' : 'setEnd'].apply(range, args);
|
|
}
|
|
var RangeEnumerator = Class.extend({
|
|
init: function (range) {
|
|
this.enumerate = function () {
|
|
var nodes = [];
|
|
function visit(node) {
|
|
if (dom.is(node, 'img') || node.nodeType == 3 && (!dom.isEmptyspace(node) || node.nodeValue == '\uFEFF')) {
|
|
nodes.push(node);
|
|
} else {
|
|
node = node.firstChild;
|
|
while (node) {
|
|
visit(node);
|
|
node = node.nextSibling;
|
|
}
|
|
}
|
|
}
|
|
new RangeIterator(range).traverse(visit);
|
|
return nodes;
|
|
};
|
|
}
|
|
});
|
|
var RestorePoint = Class.extend({
|
|
init: function (range, body) {
|
|
var that = this;
|
|
that.range = range;
|
|
that.rootNode = RangeUtils.documentFromRange(range);
|
|
that.body = body || that.getEditable(range);
|
|
if (dom.name(that.body) != 'body') {
|
|
that.rootNode = that.body;
|
|
}
|
|
that.html = that.body.innerHTML;
|
|
that.startContainer = that.nodeToPath(range.startContainer);
|
|
that.endContainer = that.nodeToPath(range.endContainer);
|
|
that.startOffset = that.offset(range.startContainer, range.startOffset);
|
|
that.endOffset = that.offset(range.endContainer, range.endOffset);
|
|
},
|
|
index: function (node) {
|
|
var result = 0, lastType = node.nodeType;
|
|
while (node = node.previousSibling) {
|
|
var nodeType = node.nodeType;
|
|
if (nodeType != 3 || lastType != nodeType) {
|
|
result++;
|
|
}
|
|
lastType = nodeType;
|
|
}
|
|
return result;
|
|
},
|
|
getEditable: function (range) {
|
|
var root = range.commonAncestorContainer;
|
|
while (root && (root.nodeType == 3 || root.attributes && !root.attributes.contentEditable)) {
|
|
root = root.parentNode;
|
|
}
|
|
return root;
|
|
},
|
|
restoreHtml: function () {
|
|
this.body.innerHTML = this.html;
|
|
},
|
|
offset: function (node, value) {
|
|
if (node.nodeType == 3) {
|
|
while ((node = node.previousSibling) && node.nodeType == 3) {
|
|
value += node.nodeValue.length;
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
nodeToPath: function (node) {
|
|
var path = [];
|
|
while (node != this.rootNode) {
|
|
path.push(this.index(node));
|
|
node = node.parentNode;
|
|
}
|
|
return path;
|
|
},
|
|
toRangePoint: function (range, start, path, denormalizedOffset) {
|
|
var node = this.rootNode, length = path.length, offset = denormalizedOffset;
|
|
while (length--) {
|
|
node = node.childNodes[path[length]];
|
|
}
|
|
while (node && node.nodeType == 3 && node.nodeValue.length < offset) {
|
|
offset -= node.nodeValue.length;
|
|
node = node.nextSibling;
|
|
}
|
|
if (node && offset >= 0) {
|
|
range[start ? 'setStart' : 'setEnd'](node, offset);
|
|
}
|
|
},
|
|
toRange: function () {
|
|
var that = this, result = that.range.cloneRange();
|
|
that.toRangePoint(result, true, that.startContainer, that.startOffset);
|
|
that.toRangePoint(result, false, that.endContainer, that.endOffset);
|
|
return result;
|
|
}
|
|
});
|
|
var Marker = Class.extend({
|
|
init: function () {
|
|
this.caret = null;
|
|
},
|
|
addCaret: function (range) {
|
|
var that = this;
|
|
that.caret = dom.create(RangeUtils.documentFromRange(range), 'span', { className: 'k-marker' });
|
|
range.insertNode(that.caret);
|
|
range.selectNode(that.caret);
|
|
return that.caret;
|
|
},
|
|
removeCaret: function (range) {
|
|
var that = this, previous = that.caret.previousSibling, startOffset = 0;
|
|
if (previous) {
|
|
startOffset = isDataNode(previous) ? previous.nodeValue.length : findNodeIndex(previous);
|
|
}
|
|
var container = that.caret.parentNode;
|
|
var containerIndex = previous ? findNodeIndex(previous) : 0;
|
|
dom.remove(that.caret);
|
|
normalize(container);
|
|
var node = container.childNodes[containerIndex];
|
|
if (isDataNode(node)) {
|
|
range.setStart(node, startOffset);
|
|
} else if (node) {
|
|
var textNode = dom.lastTextNode(node);
|
|
if (textNode) {
|
|
range.setStart(textNode, textNode.nodeValue.length);
|
|
} else {
|
|
range[previous ? 'setStartAfter' : 'setStartBefore'](node);
|
|
}
|
|
} else {
|
|
if (!browser.msie && !container.innerHTML) {
|
|
container.innerHTML = '<br _moz_dirty="" />';
|
|
}
|
|
range.selectNodeContents(container);
|
|
}
|
|
range.collapse(true);
|
|
},
|
|
add: function (range, expand) {
|
|
var that = this;
|
|
var collapsed = range.collapsed && !RangeUtils.isExpandable(range);
|
|
var doc = RangeUtils.documentFromRange(range);
|
|
if (expand && range.collapsed) {
|
|
that.addCaret(range);
|
|
range = RangeUtils.expand(range);
|
|
}
|
|
var rangeBoundary = range.cloneRange();
|
|
rangeBoundary.collapse(false);
|
|
that.end = dom.create(doc, 'span', { className: 'k-marker' });
|
|
rangeBoundary.insertNode(that.end);
|
|
rangeBoundary = range.cloneRange();
|
|
rangeBoundary.collapse(true);
|
|
that.start = that.end.cloneNode(true);
|
|
rangeBoundary.insertNode(that.start);
|
|
that._removeDeadMarkers(that.start, that.end);
|
|
if (collapsed) {
|
|
var bom = doc.createTextNode('\uFEFF');
|
|
dom.insertAfter(bom.cloneNode(), that.start);
|
|
dom.insertBefore(bom, that.end);
|
|
}
|
|
normalize(range.commonAncestorContainer);
|
|
range.setStartBefore(that.start);
|
|
range.setEndAfter(that.end);
|
|
return range;
|
|
},
|
|
_removeDeadMarkers: function (start, end) {
|
|
if (start.previousSibling && start.previousSibling.nodeValue == '\uFEFF') {
|
|
dom.remove(start.previousSibling);
|
|
}
|
|
if (end.nextSibling && end.nextSibling.nodeValue == '\uFEFF') {
|
|
dom.remove(end.nextSibling);
|
|
}
|
|
},
|
|
_normalizedIndex: function (node) {
|
|
var index = findNodeIndex(node);
|
|
var pointer = node;
|
|
while (pointer.previousSibling) {
|
|
if (pointer.nodeType == 3 && pointer.previousSibling.nodeType == 3) {
|
|
index--;
|
|
}
|
|
pointer = pointer.previousSibling;
|
|
}
|
|
return index;
|
|
},
|
|
remove: function (range) {
|
|
var that = this, start = that.start, end = that.end, shouldNormalizeStart, shouldNormalizeEnd, shouldNormalize;
|
|
normalize(range.commonAncestorContainer);
|
|
while (!start.nextSibling && start.parentNode) {
|
|
start = start.parentNode;
|
|
}
|
|
while (!end.previousSibling && end.parentNode) {
|
|
end = end.parentNode;
|
|
}
|
|
shouldNormalizeStart = start.previousSibling && start.previousSibling.nodeType == 3 && (start.nextSibling && start.nextSibling.nodeType == 3);
|
|
shouldNormalizeEnd = end.previousSibling && end.previousSibling.nodeType == 3 && (end.nextSibling && end.nextSibling.nodeType == 3);
|
|
shouldNormalize = shouldNormalizeStart && shouldNormalizeEnd;
|
|
start = start.nextSibling;
|
|
end = end.previousSibling;
|
|
var collapsed = false;
|
|
var collapsedToStart = false;
|
|
if (start == that.end) {
|
|
collapsedToStart = !!that.start.previousSibling;
|
|
start = end = that.start.previousSibling || that.end.nextSibling;
|
|
collapsed = true;
|
|
}
|
|
dom.remove(that.start);
|
|
dom.remove(that.end);
|
|
if (!start || !end) {
|
|
range.selectNodeContents(range.commonAncestorContainer);
|
|
range.collapse(true);
|
|
return;
|
|
}
|
|
var startOffset = collapsed ? isDataNode(start) ? start.nodeValue.length : start.childNodes.length : 0;
|
|
var endOffset = isDataNode(end) ? end.nodeValue.length : end.childNodes.length;
|
|
if (start.nodeType == 3) {
|
|
while (start.previousSibling && start.previousSibling.nodeType == 3) {
|
|
start = start.previousSibling;
|
|
startOffset += start.nodeValue.length;
|
|
}
|
|
}
|
|
if (end.nodeType == 3) {
|
|
while (end.previousSibling && end.previousSibling.nodeType == 3) {
|
|
end = end.previousSibling;
|
|
endOffset += end.nodeValue.length;
|
|
}
|
|
}
|
|
var startParent = start.parentNode;
|
|
var endParent = end.parentNode;
|
|
var startIndex = this._normalizedIndex(start);
|
|
var endIndex = this._normalizedIndex(end);
|
|
normalize(startParent);
|
|
if (start.nodeType == 3) {
|
|
start = startParent.childNodes[startIndex];
|
|
}
|
|
normalize(endParent);
|
|
if (end.nodeType == 3) {
|
|
end = endParent.childNodes[endIndex];
|
|
}
|
|
if (collapsed) {
|
|
if (start.nodeType == 3) {
|
|
range.setStart(start, startOffset);
|
|
} else {
|
|
range[collapsedToStart ? 'setStartAfter' : 'setStartBefore'](start);
|
|
}
|
|
range.collapse(true);
|
|
} else {
|
|
if (start.nodeType == 3) {
|
|
range.setStart(start, startOffset);
|
|
} else {
|
|
range.setStartBefore(start);
|
|
}
|
|
if (end.nodeType == 3) {
|
|
range.setEnd(end, endOffset);
|
|
} else {
|
|
range.setEndAfter(end);
|
|
}
|
|
}
|
|
if (that.caret) {
|
|
that.removeCaret(range);
|
|
}
|
|
}
|
|
});
|
|
var boundary = /[\u0009-\u000d]|\u0020|\u00a0|\ufeff|\.|,|;|:|!|\(|\)|\?/;
|
|
var RangeUtils = {
|
|
nodes: function (range) {
|
|
var nodes = RangeUtils.textNodes(range);
|
|
if (!nodes.length) {
|
|
range.selectNodeContents(range.commonAncestorContainer);
|
|
nodes = RangeUtils.textNodes(range);
|
|
if (!nodes.length) {
|
|
nodes = dom.significantChildNodes(range.commonAncestorContainer);
|
|
}
|
|
}
|
|
return nodes;
|
|
},
|
|
textNodes: function (range) {
|
|
return new RangeEnumerator(range).enumerate();
|
|
},
|
|
documentFromRange: function (range) {
|
|
var startContainer = range.startContainer;
|
|
return startContainer.nodeType == 9 ? startContainer : startContainer.ownerDocument;
|
|
},
|
|
createRange: function (document) {
|
|
if (browser.msie && browser.version < 9) {
|
|
return new W3CRange(document);
|
|
}
|
|
return document.createRange();
|
|
},
|
|
selectRange: function (range) {
|
|
var image = RangeUtils.image(range);
|
|
if (image) {
|
|
range.setStartAfter(image);
|
|
range.setEndAfter(image);
|
|
}
|
|
var selection = SelectionUtils.selectionFromRange(range);
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
},
|
|
stringify: function (range) {
|
|
return kendo.format('{0}:{1} - {2}:{3}', dom.name(range.startContainer), range.startOffset, dom.name(range.endContainer), range.endOffset);
|
|
},
|
|
split: function (range, node, trim) {
|
|
function partition(start) {
|
|
var partitionRange = range.cloneRange();
|
|
partitionRange.collapse(start);
|
|
partitionRange[start ? 'setStartBefore' : 'setEndAfter'](node);
|
|
var contents = partitionRange.extractContents();
|
|
if (trim) {
|
|
contents = dom.trim(contents);
|
|
}
|
|
dom[start ? 'insertBefore' : 'insertAfter'](contents, node);
|
|
}
|
|
partition(true);
|
|
partition(false);
|
|
},
|
|
mapAll: function (range, map) {
|
|
var nodes = [];
|
|
new RangeIterator(range).traverse(function (node) {
|
|
var mapped = map(node);
|
|
if (mapped && $.inArray(mapped, nodes) < 0) {
|
|
nodes.push(mapped);
|
|
}
|
|
});
|
|
return nodes;
|
|
},
|
|
getAll: function (range, predicate) {
|
|
var selector = predicate;
|
|
if (typeof predicate == 'string') {
|
|
predicate = function (node) {
|
|
return dom.is(node, selector);
|
|
};
|
|
}
|
|
return RangeUtils.mapAll(range, function (node) {
|
|
if (predicate(node)) {
|
|
return node;
|
|
}
|
|
});
|
|
},
|
|
getMarkers: function (range) {
|
|
return RangeUtils.getAll(range, function (node) {
|
|
return node.className == 'k-marker';
|
|
});
|
|
},
|
|
image: function (range) {
|
|
var nodes = RangeUtils.getAll(range, 'img');
|
|
if (nodes.length == 1) {
|
|
return nodes[0];
|
|
}
|
|
},
|
|
isStartOf: function (originalRange, node) {
|
|
if (originalRange.startOffset !== 0) {
|
|
return false;
|
|
}
|
|
var range = originalRange.cloneRange();
|
|
while (range.startOffset === 0 && range.startContainer != node) {
|
|
var index = dom.findNodeIndex(range.startContainer);
|
|
var parent = range.startContainer.parentNode;
|
|
while (index > 0 && parent[index - 1] && dom.insignificant(parent[index - 1])) {
|
|
index--;
|
|
}
|
|
range.setStart(parent, index);
|
|
}
|
|
return range.startOffset === 0 && range.startContainer == node;
|
|
},
|
|
isEndOf: function (originalRange, node) {
|
|
var range = originalRange.cloneRange();
|
|
range.collapse(false);
|
|
var start = range.startContainer;
|
|
if (dom.isDataNode(start) && range.startOffset == dom.getNodeLength(start)) {
|
|
range.setStart(start.parentNode, dom.findNodeIndex(start) + 1);
|
|
range.collapse(true);
|
|
}
|
|
range.setEnd(node, dom.getNodeLength(node));
|
|
var nodes = [];
|
|
function visit(node) {
|
|
if (!dom.insignificant(node)) {
|
|
nodes.push(node);
|
|
}
|
|
}
|
|
new RangeIterator(range).traverse(visit);
|
|
return !nodes.length;
|
|
},
|
|
wrapSelectedElements: function (range) {
|
|
var startEditable = dom.editableParent(range.startContainer);
|
|
var endEditable = dom.editableParent(range.endContainer);
|
|
while (range.startOffset === 0 && range.startContainer != startEditable) {
|
|
range.setStart(range.startContainer.parentNode, dom.findNodeIndex(range.startContainer));
|
|
}
|
|
function isEnd(offset, container) {
|
|
var length = dom.getNodeLength(container);
|
|
if (offset == length) {
|
|
return true;
|
|
}
|
|
for (var i = offset; i < length; i++) {
|
|
if (!dom.insignificant(container.childNodes[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
while (isEnd(range.endOffset, range.endContainer) && range.endContainer != endEditable) {
|
|
range.setEnd(range.endContainer.parentNode, dom.findNodeIndex(range.endContainer) + 1);
|
|
}
|
|
return range;
|
|
},
|
|
expand: function (range) {
|
|
var result = range.cloneRange();
|
|
var startContainer = result.startContainer.childNodes[result.startOffset === 0 ? 0 : result.startOffset - 1];
|
|
var endContainer = result.endContainer.childNodes[result.endOffset];
|
|
if (!isDataNode(startContainer) || !isDataNode(endContainer)) {
|
|
return result;
|
|
}
|
|
var beforeCaret = startContainer.nodeValue;
|
|
var afterCaret = endContainer.nodeValue;
|
|
if (!beforeCaret || !afterCaret) {
|
|
return result;
|
|
}
|
|
var startOffset = beforeCaret.split('').reverse().join('').search(boundary);
|
|
var endOffset = afterCaret.search(boundary);
|
|
if (!startOffset || !endOffset) {
|
|
return result;
|
|
}
|
|
endOffset = endOffset == -1 ? afterCaret.length : endOffset;
|
|
startOffset = startOffset == -1 ? 0 : beforeCaret.length - startOffset;
|
|
result.setStart(startContainer, startOffset);
|
|
result.setEnd(endContainer, endOffset);
|
|
return result;
|
|
},
|
|
isExpandable: function (range) {
|
|
var node = range.startContainer;
|
|
var rangeDocument = RangeUtils.documentFromRange(range);
|
|
if (node == rangeDocument || node == rangeDocument.body) {
|
|
return false;
|
|
}
|
|
var result = range.cloneRange();
|
|
var value = node.nodeValue;
|
|
if (!value) {
|
|
return false;
|
|
}
|
|
var beforeCaret = value.substring(0, result.startOffset);
|
|
var afterCaret = value.substring(result.startOffset);
|
|
var startOffset = 0, endOffset = 0;
|
|
if (beforeCaret) {
|
|
startOffset = beforeCaret.split('').reverse().join('').search(boundary);
|
|
}
|
|
if (afterCaret) {
|
|
endOffset = afterCaret.search(boundary);
|
|
}
|
|
return startOffset && endOffset;
|
|
}
|
|
};
|
|
extend(Editor, {
|
|
SelectionUtils: SelectionUtils,
|
|
W3CRange: W3CRange,
|
|
RangeIterator: RangeIterator,
|
|
W3CSelection: W3CSelection,
|
|
RangeEnumerator: RangeEnumerator,
|
|
RestorePoint: RestorePoint,
|
|
Marker: Marker,
|
|
RangeUtils: RangeUtils
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/system', ['editor/range'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, editorNS = kendo.ui.editor, EditorUtils = editorNS.EditorUtils, registerTool = EditorUtils.registerTool, dom = editorNS.Dom, Tool = editorNS.Tool, ToolTemplate = editorNS.ToolTemplate, RestorePoint = editorNS.RestorePoint, Marker = editorNS.Marker, extend = $.extend;
|
|
function finishUpdate(editor, startRestorePoint) {
|
|
var endRestorePoint = editor.selectionRestorePoint = new RestorePoint(editor.getRange());
|
|
var command = new GenericCommand(startRestorePoint, endRestorePoint);
|
|
command.editor = editor;
|
|
editor.undoRedoStack.push(command);
|
|
return endRestorePoint;
|
|
}
|
|
var Command = Class.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
this.restorePoint = new RestorePoint(options.range);
|
|
this.marker = new Marker();
|
|
this.formatter = options.formatter;
|
|
},
|
|
getRange: function () {
|
|
return this.restorePoint.toRange();
|
|
},
|
|
lockRange: function (expand) {
|
|
return this.marker.add(this.getRange(), expand);
|
|
},
|
|
releaseRange: function (range) {
|
|
this.marker.remove(range);
|
|
this.editor.selectRange(range);
|
|
},
|
|
undo: function () {
|
|
var point = this.restorePoint;
|
|
point.restoreHtml();
|
|
this.editor.selectRange(point.toRange());
|
|
},
|
|
redo: function () {
|
|
this.exec();
|
|
},
|
|
createDialog: function (content, options) {
|
|
var editor = this.editor;
|
|
return $(content).appendTo(document.body).kendoWindow(extend({}, editor.options.dialogOptions, options)).closest('.k-window').toggleClass('k-rtl', kendo.support.isRtl(editor.wrapper)).end();
|
|
},
|
|
exec: function () {
|
|
var range = this.lockRange(true);
|
|
this.formatter.editor = this.editor;
|
|
this.formatter.toggle(range);
|
|
this.releaseRange(range);
|
|
}
|
|
});
|
|
var GenericCommand = Class.extend({
|
|
init: function (startRestorePoint, endRestorePoint) {
|
|
this.body = startRestorePoint.body;
|
|
this.startRestorePoint = startRestorePoint;
|
|
this.endRestorePoint = endRestorePoint;
|
|
},
|
|
redo: function () {
|
|
this.body.innerHTML = this.endRestorePoint.html;
|
|
this.editor.selectRange(this.endRestorePoint.toRange());
|
|
},
|
|
undo: function () {
|
|
this.body.innerHTML = this.startRestorePoint.html;
|
|
this.editor.selectRange(this.startRestorePoint.toRange());
|
|
}
|
|
});
|
|
var InsertHtmlCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this.managesUndoRedo = true;
|
|
},
|
|
exec: function () {
|
|
var editor = this.editor;
|
|
var options = this.options;
|
|
var range = options.range;
|
|
var body = editor.body;
|
|
var startRestorePoint = new RestorePoint(range, body);
|
|
var html = options.html || options.value || '';
|
|
editor.selectRange(range);
|
|
editor.clipboard.paste(html, options);
|
|
if (options.postProcess) {
|
|
options.postProcess(editor, editor.getRange());
|
|
}
|
|
var genericCommand = new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange(), body));
|
|
genericCommand.editor = editor;
|
|
editor.undoRedoStack.push(genericCommand);
|
|
editor.focus();
|
|
}
|
|
});
|
|
var InsertHtmlTool = Tool.extend({
|
|
initialize: function (ui, initOptions) {
|
|
var editor = initOptions.editor, options = this.options, dataSource = options.items ? options.items : editor.options.insertHtml;
|
|
this._selectBox = new editorNS.SelectBox(ui, {
|
|
dataSource: dataSource,
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
change: function () {
|
|
Tool.exec(editor, 'insertHtml', this.value());
|
|
},
|
|
title: editor.options.messages.insertHtml,
|
|
highlightFirst: false
|
|
});
|
|
},
|
|
command: function (commandArguments) {
|
|
return new InsertHtmlCommand(commandArguments);
|
|
},
|
|
update: function (ui) {
|
|
var selectbox = ui.data('kendoSelectBox') || ui.find('select').data('kendoSelectBox');
|
|
selectbox.close();
|
|
selectbox.value(selectbox.options.title);
|
|
}
|
|
});
|
|
var TypingHandler = Class.extend({
|
|
init: function (editor) {
|
|
this.editor = editor;
|
|
},
|
|
keydown: function (e) {
|
|
var that = this, editor = that.editor, keyboard = editor.keyboard, isTypingKey = keyboard.isTypingKey(e), evt = extend($.Event(), e);
|
|
that.editor.trigger('keydown', evt);
|
|
if (evt.isDefaultPrevented()) {
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
if (!evt.isDefaultPrevented() && isTypingKey && !keyboard.isTypingInProgress()) {
|
|
var range = editor.getRange();
|
|
that.startRestorePoint = new RestorePoint(range);
|
|
keyboard.startTyping(function () {
|
|
that.endRestorePoint = finishUpdate(editor, that.startRestorePoint);
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
keyup: function (e) {
|
|
var keyboard = this.editor.keyboard;
|
|
this.editor.trigger('keyup', e);
|
|
if (keyboard.isTypingInProgress()) {
|
|
keyboard.endTyping();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var BackspaceHandler = Class.extend({
|
|
init: function (editor) {
|
|
this.editor = editor;
|
|
},
|
|
_addCaret: function (container) {
|
|
var caret = dom.create(this.editor.document, 'a');
|
|
dom.insertAt(container, caret, 0);
|
|
return caret;
|
|
},
|
|
_restoreCaret: function (caret) {
|
|
var range = this.editor.createRange();
|
|
range.setStartAfter(caret);
|
|
range.collapse(true);
|
|
this.editor.selectRange(range);
|
|
dom.remove(caret);
|
|
},
|
|
_handleDelete: function (range) {
|
|
var node = range.endContainer;
|
|
var block = dom.closestEditableOfType(node, dom.blockElements);
|
|
if (block && editorNS.RangeUtils.isEndOf(range, block)) {
|
|
var next = dom.next(block);
|
|
if (!next || dom.name(next) != 'p') {
|
|
return false;
|
|
}
|
|
var caret = this._addCaret(next);
|
|
this._merge(block, next);
|
|
this._restoreCaret(caret);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_cleanBomBefore: function (range) {
|
|
var offset = range.startOffset;
|
|
var node = range.startContainer;
|
|
var text = node.nodeValue;
|
|
var count = 0;
|
|
while (offset - count >= 0 && text[offset - count - 1] == '\uFEFF') {
|
|
count++;
|
|
}
|
|
if (count > 0) {
|
|
node.deleteData(offset - count, count);
|
|
range.setStart(node, Math.max(0, offset - count));
|
|
range.collapse(true);
|
|
this.editor.selectRange(range);
|
|
}
|
|
},
|
|
_handleBackspace: function (range) {
|
|
var node = range.startContainer;
|
|
var li = dom.closestEditableOfType(node, ['li']);
|
|
var block = dom.closestEditableOfType(node, 'p,h1,h2,h3,h4,h5,h6'.split(','));
|
|
if (dom.isDataNode(node)) {
|
|
this._cleanBomBefore(range);
|
|
}
|
|
if (block && block.previousSibling && editorNS.RangeUtils.isStartOf(range, block)) {
|
|
var prev = block.previousSibling;
|
|
var caret = this._addCaret(block);
|
|
this._merge(prev, block);
|
|
this._restoreCaret(caret);
|
|
return true;
|
|
}
|
|
if (li && editorNS.RangeUtils.isStartOf(range, li)) {
|
|
var child = li.firstChild;
|
|
if (!child) {
|
|
li.innerHTML = editorNS.emptyElementContent;
|
|
child = li.firstChild;
|
|
}
|
|
var formatter = new editorNS.ListFormatter(dom.name(li.parentNode), 'p');
|
|
range.selectNodeContents(li);
|
|
formatter.toggle(range);
|
|
if (dom.insignificant(child)) {
|
|
range.setStartBefore(child);
|
|
} else {
|
|
range.setStart(child, 0);
|
|
}
|
|
this.editor.selectRange(range);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_handleSelection: function (range) {
|
|
var ancestor = range.commonAncestorContainer;
|
|
var table = dom.closest(ancestor, 'table');
|
|
var emptyParagraphContent = editorNS.emptyElementContent;
|
|
if (/t(able|body)/i.test(dom.name(ancestor))) {
|
|
range.selectNode(table);
|
|
}
|
|
var marker = new Marker();
|
|
marker.add(range, false);
|
|
range.setStartAfter(marker.start);
|
|
range.setEndBefore(marker.end);
|
|
var start = range.startContainer;
|
|
var end = range.endContainer;
|
|
range.deleteContents();
|
|
if (table && $(table).text() === '') {
|
|
range.selectNode(table);
|
|
range.deleteContents();
|
|
}
|
|
ancestor = range.commonAncestorContainer;
|
|
if (dom.name(ancestor) === 'p' && ancestor.innerHTML === '') {
|
|
ancestor.innerHTML = emptyParagraphContent;
|
|
range.setStart(ancestor, 0);
|
|
}
|
|
this._join(start, end);
|
|
dom.insertAfter(this.editor.document.createTextNode('\uFEFF'), marker.start);
|
|
marker.remove(range);
|
|
start = range.startContainer;
|
|
if (dom.name(start) == 'tr') {
|
|
start = start.childNodes[Math.max(0, range.startOffset - 1)];
|
|
range.setStart(start, dom.getNodeLength(start));
|
|
}
|
|
range.collapse(true);
|
|
this.editor.selectRange(range);
|
|
return true;
|
|
},
|
|
_root: function (node) {
|
|
while (node && node.parentNode && dom.name(node.parentNode) != 'body') {
|
|
node = node.parentNode;
|
|
}
|
|
return node;
|
|
},
|
|
_join: function (start, end) {
|
|
start = this._root(start);
|
|
end = this._root(end);
|
|
if (start != end && dom.is(end, 'p')) {
|
|
this._merge(start, end);
|
|
}
|
|
},
|
|
_merge: function (dest, src) {
|
|
dom.removeTrailingBreak(dest);
|
|
while (src.firstChild) {
|
|
if (dest.nodeType == 1) {
|
|
dest.appendChild(src.firstChild);
|
|
} else {
|
|
dest.parentNode.appendChild(src.firstChild);
|
|
}
|
|
}
|
|
dom.remove(src);
|
|
},
|
|
keydown: function (e) {
|
|
var method, startRestorePoint;
|
|
var range = this.editor.getRange();
|
|
var keyCode = e.keyCode;
|
|
var keys = kendo.keys;
|
|
var backspace = keyCode === keys.BACKSPACE;
|
|
var del = keyCode == keys.DELETE;
|
|
if ((backspace || del) && !range.collapsed) {
|
|
method = '_handleSelection';
|
|
} else if (backspace) {
|
|
method = '_handleBackspace';
|
|
} else if (del) {
|
|
method = '_handleDelete';
|
|
}
|
|
if (!method) {
|
|
return;
|
|
}
|
|
startRestorePoint = new RestorePoint(range);
|
|
if (this[method](range)) {
|
|
e.preventDefault();
|
|
finishUpdate(this.editor, startRestorePoint);
|
|
}
|
|
},
|
|
keyup: $.noop
|
|
});
|
|
var SystemHandler = Class.extend({
|
|
init: function (editor) {
|
|
this.editor = editor;
|
|
this.systemCommandIsInProgress = false;
|
|
},
|
|
createUndoCommand: function () {
|
|
this.startRestorePoint = this.endRestorePoint = finishUpdate(this.editor, this.startRestorePoint);
|
|
},
|
|
changed: function () {
|
|
if (this.startRestorePoint) {
|
|
return this.startRestorePoint.html != this.editor.body.innerHTML;
|
|
}
|
|
return false;
|
|
},
|
|
keydown: function (e) {
|
|
var that = this, editor = that.editor, keyboard = editor.keyboard;
|
|
if (keyboard.isModifierKey(e)) {
|
|
if (keyboard.isTypingInProgress()) {
|
|
keyboard.endTyping(true);
|
|
}
|
|
that.startRestorePoint = new RestorePoint(editor.getRange());
|
|
return true;
|
|
}
|
|
if (keyboard.isSystem(e)) {
|
|
that.systemCommandIsInProgress = true;
|
|
if (that.changed()) {
|
|
that.systemCommandIsInProgress = false;
|
|
that.createUndoCommand();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
keyup: function () {
|
|
var that = this;
|
|
if (that.systemCommandIsInProgress && that.changed()) {
|
|
that.systemCommandIsInProgress = false;
|
|
that.createUndoCommand();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var Keyboard = Class.extend({
|
|
init: function (handlers) {
|
|
this.handlers = handlers;
|
|
this.typingInProgress = false;
|
|
},
|
|
isCharacter: function (keyCode) {
|
|
return keyCode >= 48 && keyCode <= 90 || keyCode >= 96 && keyCode <= 111 || keyCode >= 186 && keyCode <= 192 || keyCode >= 219 && keyCode <= 222 || keyCode == 229;
|
|
},
|
|
toolFromShortcut: function (tools, e) {
|
|
var key = String.fromCharCode(e.keyCode), toolName, toolOptions;
|
|
for (toolName in tools) {
|
|
toolOptions = $.extend({
|
|
ctrl: false,
|
|
alt: false,
|
|
shift: false
|
|
}, tools[toolName].options);
|
|
if ((toolOptions.key == key || toolOptions.key == e.keyCode) && toolOptions.ctrl == e.ctrlKey && toolOptions.alt == e.altKey && toolOptions.shift == e.shiftKey) {
|
|
return toolName;
|
|
}
|
|
}
|
|
},
|
|
isTypingKey: function (e) {
|
|
var keyCode = e.keyCode;
|
|
return this.isCharacter(keyCode) && !e.ctrlKey && !e.altKey || keyCode == 32 || keyCode == 13 || keyCode == 8 || keyCode == 46 && !e.shiftKey && !e.ctrlKey && !e.altKey;
|
|
},
|
|
isModifierKey: function (e) {
|
|
var keyCode = e.keyCode;
|
|
return keyCode == 17 && !e.shiftKey && !e.altKey || keyCode == 16 && !e.ctrlKey && !e.altKey || keyCode == 18 && !e.ctrlKey && !e.shiftKey;
|
|
},
|
|
isSystem: function (e) {
|
|
return e.keyCode == 46 && e.ctrlKey && !e.altKey && !e.shiftKey;
|
|
},
|
|
startTyping: function (callback) {
|
|
this.onEndTyping = callback;
|
|
this.typingInProgress = true;
|
|
},
|
|
stopTyping: function () {
|
|
if (this.typingInProgress && this.onEndTyping) {
|
|
this.onEndTyping();
|
|
}
|
|
this.typingInProgress = false;
|
|
},
|
|
endTyping: function (force) {
|
|
var that = this;
|
|
that.clearTimeout();
|
|
if (force) {
|
|
that.stopTyping();
|
|
} else {
|
|
that.timeout = window.setTimeout($.proxy(that.stopTyping, that), 1000);
|
|
}
|
|
},
|
|
isTypingInProgress: function () {
|
|
return this.typingInProgress;
|
|
},
|
|
clearTimeout: function () {
|
|
window.clearTimeout(this.timeout);
|
|
},
|
|
notify: function (e, what) {
|
|
var i, handlers = this.handlers;
|
|
for (i = 0; i < handlers.length; i++) {
|
|
if (handlers[i][what](e)) {
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
keydown: function (e) {
|
|
this.notify(e, 'keydown');
|
|
},
|
|
keyup: function (e) {
|
|
this.notify(e, 'keyup');
|
|
}
|
|
});
|
|
var Clipboard = Class.extend({
|
|
init: function (editor) {
|
|
this.editor = editor;
|
|
this.cleaners = [
|
|
new ScriptCleaner(),
|
|
new TabCleaner(),
|
|
new MSWordFormatCleaner(),
|
|
new WebkitFormatCleaner()
|
|
];
|
|
},
|
|
htmlToFragment: function (html) {
|
|
var editor = this.editor, doc = editor.document, container = dom.create(doc, 'div'), fragment = doc.createDocumentFragment();
|
|
container.innerHTML = html;
|
|
while (container.firstChild) {
|
|
fragment.appendChild(container.firstChild);
|
|
}
|
|
return fragment;
|
|
},
|
|
isBlock: function (html) {
|
|
return /<(div|p|ul|ol|table|h[1-6])/i.test(html);
|
|
},
|
|
_startModification: function () {
|
|
var range;
|
|
var restorePoint;
|
|
var editor = this.editor;
|
|
if (this._inProgress) {
|
|
return;
|
|
}
|
|
this._inProgress = true;
|
|
range = editor.getRange();
|
|
restorePoint = new RestorePoint(range);
|
|
dom.persistScrollTop(editor.document);
|
|
return {
|
|
range: range,
|
|
restorePoint: restorePoint
|
|
};
|
|
},
|
|
_endModification: function (modificationInfo) {
|
|
finishUpdate(this.editor, modificationInfo.restorePoint);
|
|
this.editor._selectionChange();
|
|
this._inProgress = false;
|
|
},
|
|
_contentModification: function (before, after) {
|
|
var that = this;
|
|
var editor = that.editor;
|
|
var modificationInfo = that._startModification();
|
|
if (!modificationInfo) {
|
|
return;
|
|
}
|
|
before.call(that, editor, modificationInfo.range);
|
|
setTimeout(function () {
|
|
after.call(that, editor, modificationInfo.range);
|
|
that._endModification(modificationInfo);
|
|
});
|
|
},
|
|
_removeBomNodes: function (range) {
|
|
var nodes = editorNS.RangeUtils.textNodes(range);
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
nodes[i].nodeValue = dom.stripBom(nodes[i].nodeValue);
|
|
}
|
|
},
|
|
_onBeforeCopy: function (range) {
|
|
var marker = new Marker();
|
|
marker.add(range);
|
|
this._removeBomNodes(range);
|
|
marker.remove(range);
|
|
this.editor.selectRange(range);
|
|
},
|
|
oncopy: function () {
|
|
this._onBeforeCopy(this.editor.getRange());
|
|
},
|
|
oncut: function () {
|
|
this._onBeforeCopy(this.editor.getRange());
|
|
this._contentModification($.noop, $.noop);
|
|
},
|
|
_fileToDataURL: function (blob) {
|
|
var deferred = $.Deferred();
|
|
var reader = new FileReader();
|
|
if (!(blob instanceof window.File) && blob.getAsFile) {
|
|
blob = blob.getAsFile();
|
|
}
|
|
reader.onload = $.proxy(deferred.resolve, deferred);
|
|
reader.readAsDataURL(blob);
|
|
return deferred.promise();
|
|
},
|
|
_triggerPaste: function (html, options) {
|
|
var args = { html: html || '' };
|
|
args.html = args.html.replace(/\ufeff/g, '');
|
|
this.editor.trigger('paste', args);
|
|
this.paste(args.html, options || {});
|
|
},
|
|
_handleImagePaste: function (e) {
|
|
if (!('FileReader' in window)) {
|
|
return;
|
|
}
|
|
var clipboardData = e.clipboardData || e.originalEvent.clipboardData || window.clipboardData || {};
|
|
var items = clipboardData.items || clipboardData.files;
|
|
if (!items) {
|
|
return;
|
|
}
|
|
var images = $.grep(items, function (item) {
|
|
return /^image\//i.test(item.type);
|
|
});
|
|
var html = $.grep(items, function (item) {
|
|
return /^text\/html/i.test(item.type);
|
|
});
|
|
if (html.length || !images.length) {
|
|
return;
|
|
}
|
|
var modificationInfo = this._startModification();
|
|
if (!modificationInfo) {
|
|
return;
|
|
}
|
|
$.when.apply($, $.map(images, this._fileToDataURL)).done($.proxy(function () {
|
|
var results = Array.prototype.slice.call(arguments);
|
|
var html = $.map(results, function (e) {
|
|
return '<img src="' + e.target.result + '" />';
|
|
}).join('');
|
|
this._triggerPaste(html);
|
|
this._endModification(modificationInfo);
|
|
}, this));
|
|
return true;
|
|
},
|
|
onpaste: function (e) {
|
|
if (this._handleImagePaste(e)) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
this._contentModification(function beforePaste(editor, range) {
|
|
var clipboardNode = dom.create(editor.document, 'div', {
|
|
className: 'k-paste-container',
|
|
innerHTML: '\uFEFF'
|
|
});
|
|
var browser = kendo.support.browser;
|
|
editor.body.appendChild(clipboardNode);
|
|
if (browser.msie && browser.version < 11) {
|
|
e.preventDefault();
|
|
var r = editor.createRange();
|
|
r.selectNodeContents(clipboardNode);
|
|
editor.selectRange(r);
|
|
var textRange = editor.document.body.createTextRange();
|
|
textRange.moveToElementText(clipboardNode);
|
|
$(editor.body).unbind('paste');
|
|
textRange.execCommand('Paste');
|
|
$(editor.body).bind('paste', $.proxy(this.onpaste, this));
|
|
} else {
|
|
var clipboardRange = editor.createRange();
|
|
clipboardRange.selectNodeContents(clipboardNode);
|
|
editor.selectRange(clipboardRange);
|
|
}
|
|
range.deleteContents();
|
|
}, function afterPaste(editor, range) {
|
|
var html = '', containers;
|
|
editor.selectRange(range);
|
|
containers = $(editor.body).children('.k-paste-container');
|
|
containers.each(function () {
|
|
var lastChild = this.lastChild;
|
|
if (lastChild && dom.is(lastChild, 'br')) {
|
|
dom.remove(lastChild);
|
|
}
|
|
html += this.innerHTML;
|
|
});
|
|
containers.remove();
|
|
this._triggerPaste(html, { clean: true });
|
|
});
|
|
},
|
|
splittableParent: function (block, node) {
|
|
var parentNode, body;
|
|
if (block) {
|
|
return dom.closestEditableOfType(node, [
|
|
'p',
|
|
'ul',
|
|
'ol'
|
|
]) || node.parentNode;
|
|
}
|
|
parentNode = node.parentNode;
|
|
body = node.ownerDocument.body;
|
|
if (dom.isInline(parentNode)) {
|
|
while (parentNode.parentNode != body && !dom.isBlock(parentNode.parentNode)) {
|
|
parentNode = parentNode.parentNode;
|
|
}
|
|
}
|
|
return parentNode;
|
|
},
|
|
paste: function (html, options) {
|
|
var editor = this.editor, i, l;
|
|
options = extend({
|
|
clean: false,
|
|
split: true
|
|
}, options);
|
|
for (i = 0, l = this.cleaners.length; i < l; i++) {
|
|
if (this.cleaners[i].applicable(html)) {
|
|
html = this.cleaners[i].clean(html);
|
|
}
|
|
}
|
|
if (options.clean) {
|
|
html = html.replace(/(<br>(\s| )*)+(<\/?(div|p|li|col|t))/gi, '$3');
|
|
html = html.replace(/<(a|span)[^>]*><\/\1>/gi, '');
|
|
}
|
|
html = html.replace(/^<li/i, '<ul><li').replace(/li>$/g, 'li></ul>');
|
|
var block = this.isBlock(html);
|
|
editor.focus();
|
|
var range = editor.getRange();
|
|
range.deleteContents();
|
|
if (range.startContainer == editor.document) {
|
|
range.selectNodeContents(editor.body);
|
|
}
|
|
var marker = new Marker();
|
|
var caret = marker.addCaret(range);
|
|
var parent = this.splittableParent(block, caret);
|
|
var unwrap = false;
|
|
var splittable = parent != editor.body && !dom.is(parent, 'td');
|
|
if (options.split && splittable && (block || dom.isInline(parent))) {
|
|
range.selectNode(caret);
|
|
editorNS.RangeUtils.split(range, parent, true);
|
|
unwrap = true;
|
|
}
|
|
var fragment = this.htmlToFragment(html);
|
|
if (fragment.firstChild && fragment.firstChild.className === 'k-paste-container') {
|
|
var fragmentsHtml = [];
|
|
for (i = 0, l = fragment.childNodes.length; i < l; i++) {
|
|
fragmentsHtml.push(fragment.childNodes[i].innerHTML);
|
|
}
|
|
fragment = this.htmlToFragment(fragmentsHtml.join('<br />'));
|
|
}
|
|
$(fragment.childNodes).filter('table').addClass('k-table').end().find('table').addClass('k-table');
|
|
range.insertNode(fragment);
|
|
parent = this.splittableParent(block, caret);
|
|
if (unwrap) {
|
|
while (caret.parentNode != parent) {
|
|
dom.unwrap(caret.parentNode);
|
|
}
|
|
dom.unwrap(caret.parentNode);
|
|
}
|
|
dom.normalize(range.commonAncestorContainer);
|
|
caret.style.display = 'inline';
|
|
dom.restoreScrollTop(editor.document);
|
|
dom.scrollTo(caret);
|
|
marker.removeCaret(range);
|
|
var rangeEnd = range.commonAncestorContainer.parentNode;
|
|
if (range.collapsed && dom.name(rangeEnd) == 'tbody') {
|
|
range.setStartAfter($(rangeEnd).closest('table')[0]);
|
|
range.collapse(true);
|
|
}
|
|
editor.selectRange(range);
|
|
}
|
|
});
|
|
var Cleaner = Class.extend({
|
|
clean: function (html) {
|
|
var that = this, replacements = that.replacements, i, l;
|
|
for (i = 0, l = replacements.length; i < l; i += 2) {
|
|
html = html.replace(replacements[i], replacements[i + 1]);
|
|
}
|
|
return html;
|
|
}
|
|
});
|
|
var ScriptCleaner = Cleaner.extend({
|
|
init: function () {
|
|
this.replacements = [
|
|
/<(\/?)script([^>]*)>/i,
|
|
'<$1telerik:script$2>'
|
|
];
|
|
},
|
|
applicable: function (html) {
|
|
return /<script[^>]*>/i.test(html);
|
|
}
|
|
});
|
|
var TabCleaner = Cleaner.extend({
|
|
init: function () {
|
|
var replacement = ' ';
|
|
this.replacements = [
|
|
/<span\s+class="Apple-tab-span"[^>]*>\s*<\/span>/gi,
|
|
replacement,
|
|
/\t/gi,
|
|
replacement,
|
|
/ /gi,
|
|
replacement
|
|
];
|
|
},
|
|
applicable: function (html) {
|
|
return / |class="?Apple-tab-span/i.test(html);
|
|
}
|
|
});
|
|
var MSWordFormatCleaner = Cleaner.extend({
|
|
init: function () {
|
|
this.replacements = [
|
|
/<\?xml[^>]*>/gi,
|
|
'',
|
|
/<!--(.|\n)*?-->/g,
|
|
'',
|
|
/"/g,
|
|
'\'',
|
|
/(?:<br> [\s\r\n]+|<br>)*(<\/?(h[1-6]|hr|p|div|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|address|pre|form|blockquote|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br> [\s\r\n]+|<br>)*/g,
|
|
'$1',
|
|
/<br><br>/g,
|
|
'<BR><BR>',
|
|
/<br>(?!\n)/g,
|
|
' ',
|
|
/<table([^>]*)>(\s| )+<t/gi,
|
|
'<table$1><t',
|
|
/<tr[^>]*>(\s| )*<\/tr>/gi,
|
|
'',
|
|
/<tbody[^>]*>(\s| )*<\/tbody>/gi,
|
|
'',
|
|
/<table[^>]*>(\s| )*<\/table>/gi,
|
|
'',
|
|
/<BR><BR>/g,
|
|
'<br>',
|
|
/^\s*( )+/gi,
|
|
'',
|
|
/( |<br[^>]*>)+\s*$/gi,
|
|
'',
|
|
/mso-[^;"]*;?/gi,
|
|
'',
|
|
/<(\/?)b(\s[^>]*)?>/gi,
|
|
'<$1strong$2>',
|
|
/<(\/?)font(\s[^>]*)?>/gi,
|
|
this.convertFontMatch,
|
|
/<(\/?)i(\s[^>]*)?>/gi,
|
|
'<$1em$2>',
|
|
/<o:p> <\/o:p>/gi,
|
|
' ',
|
|
/<\/?(meta|link|style|o:|v:|x:)[^>]*>((?:.|\n)*?<\/(meta|link|style|o:|v:|x:)[^>]*>)?/gi,
|
|
'',
|
|
/<\/o>/g,
|
|
'',
|
|
/style=(["|'])\s*\1/g,
|
|
'',
|
|
/(<br[^>]*>)?\n/g,
|
|
function ($0, $1) {
|
|
return $1 ? $0 : ' ';
|
|
}
|
|
];
|
|
},
|
|
convertFontMatch: function (match, closing, args) {
|
|
var faceRe = /face=['"]([^'"]+)['"]/i;
|
|
var face = faceRe.exec(args);
|
|
var family = args && face && face[1];
|
|
if (closing) {
|
|
return '</span>';
|
|
} else if (family) {
|
|
return '<span style="font-family:' + family + '">';
|
|
} else {
|
|
return '<span>';
|
|
}
|
|
},
|
|
applicable: function (html) {
|
|
return /class="?Mso/i.test(html) || /style="[^"]*mso-/i.test(html) || /urn:schemas-microsoft-com:office/.test(html);
|
|
},
|
|
stripEmptyAnchors: function (html) {
|
|
return html.replace(/<a([^>]*)>\s*<\/a>/gi, function (a, attributes) {
|
|
if (!attributes || attributes.indexOf('href') < 0) {
|
|
return '';
|
|
}
|
|
return a;
|
|
});
|
|
},
|
|
listType: function (html) {
|
|
var startingSymbol;
|
|
if (/^(<span [^>]*texhtml[^>]*>)?<span [^>]*(Symbol|Wingdings)[^>]*>/i.test(html)) {
|
|
startingSymbol = true;
|
|
}
|
|
html = html.replace(/<\/?\w+[^>]*>/g, '').replace(/ /g, '\xA0');
|
|
if (!startingSymbol && /^[\u2022\u00b7\u00a7\u00d8o]\u00a0+/.test(html) || startingSymbol && /^.\u00a0+/.test(html)) {
|
|
return 'ul';
|
|
}
|
|
if (/^\s*\w+[\.\)]\u00a0{2,}/.test(html)) {
|
|
return 'ol';
|
|
}
|
|
},
|
|
_convertToLi: function (p) {
|
|
var content;
|
|
if (p.childNodes.length == 1) {
|
|
content = p.firstChild.innerHTML.replace(/^\w+[\.\)]( )+ /, '');
|
|
} else {
|
|
dom.remove(p.firstChild);
|
|
if (p.firstChild.nodeType == 3) {
|
|
if (/^[ivx]+\.$/i.test(p.firstChild.nodeValue)) {
|
|
dom.remove(p.firstChild);
|
|
}
|
|
}
|
|
if (/^( |\s)+$/i.test(p.firstChild.innerHTML)) {
|
|
dom.remove(p.firstChild);
|
|
}
|
|
content = p.innerHTML;
|
|
}
|
|
dom.remove(p);
|
|
return dom.create(document, 'li', { innerHTML: content });
|
|
},
|
|
lists: function (placeholder) {
|
|
var blockChildren = $(dom.blockElements.join(','), placeholder), lastMargin = -1, lastType, name, levels = {
|
|
'ul': {},
|
|
'ol': {}
|
|
}, li = placeholder, i, p, type, margin, list, key, child;
|
|
for (i = 0; i < blockChildren.length; i++) {
|
|
p = blockChildren[i];
|
|
type = this.listType(p.innerHTML);
|
|
name = dom.name(p);
|
|
if (name == 'td') {
|
|
continue;
|
|
}
|
|
if (!type || name != 'p') {
|
|
if (!p.innerHTML) {
|
|
dom.remove(p);
|
|
} else {
|
|
levels = {
|
|
'ul': {},
|
|
'ol': {}
|
|
};
|
|
li = placeholder;
|
|
lastMargin = -1;
|
|
}
|
|
continue;
|
|
}
|
|
margin = parseFloat(p.style.marginLeft || 0);
|
|
list = levels[type][margin];
|
|
if (margin > lastMargin || !list) {
|
|
list = dom.create(document, type);
|
|
if (li == placeholder) {
|
|
dom.insertBefore(list, p);
|
|
} else {
|
|
li.appendChild(list);
|
|
}
|
|
levels[type][margin] = list;
|
|
}
|
|
if (lastType != type) {
|
|
for (key in levels) {
|
|
for (child in levels[key]) {
|
|
if ($.contains(list, levels[key][child])) {
|
|
delete levels[key][child];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
li = this._convertToLi(p);
|
|
list.appendChild(li);
|
|
lastMargin = margin;
|
|
lastType = type;
|
|
}
|
|
},
|
|
removeAttributes: function (element) {
|
|
var attributes = element.attributes, i = attributes.length;
|
|
while (i--) {
|
|
if (dom.name(attributes[i]) != 'colspan') {
|
|
element.removeAttributeNode(attributes[i]);
|
|
}
|
|
}
|
|
},
|
|
createColGroup: function (row) {
|
|
var cells = row.cells;
|
|
var table = $(row).closest('table');
|
|
var colgroup = table.children('colgroup');
|
|
if (cells.length < 2) {
|
|
return;
|
|
} else if (colgroup.length) {
|
|
cells = colgroup.children();
|
|
colgroup[0].parentNode.removeChild(colgroup[0]);
|
|
}
|
|
colgroup = $($.map(cells, function (cell) {
|
|
var width = cell.width;
|
|
if (width && parseInt(width, 10) !== 0) {
|
|
return kendo.format('<col style="width:{0}px;"/>', width);
|
|
}
|
|
return '<col />';
|
|
}).join(''));
|
|
if (!colgroup.is('colgroup')) {
|
|
colgroup = $('<colgroup/>').append(colgroup);
|
|
}
|
|
colgroup.prependTo(table);
|
|
},
|
|
convertHeaders: function (row) {
|
|
var cells = row.cells, i, boldedCells = $.map(cells, function (cell) {
|
|
var child = $(cell).children('p').children('strong')[0];
|
|
if (child && dom.name(child) == 'strong') {
|
|
return child;
|
|
}
|
|
});
|
|
if (boldedCells.length == cells.length) {
|
|
for (i = 0; i < boldedCells.length; i++) {
|
|
dom.unwrap(boldedCells[i]);
|
|
}
|
|
$(row).closest('table').find('colgroup').after('<thead></thead>').end().find('thead').append(row);
|
|
for (i = 0; i < cells.length; i++) {
|
|
dom.changeTag(cells[i], 'th');
|
|
}
|
|
}
|
|
},
|
|
removeParagraphs: function (cells) {
|
|
var i, j, len, cell, paragraphs;
|
|
for (i = 0; i < cells.length; i++) {
|
|
this.removeAttributes(cells[i]);
|
|
cell = $(cells[i]);
|
|
paragraphs = cell.children('p');
|
|
for (j = 0, len = paragraphs.length; j < len; j++) {
|
|
if (j < len - 1) {
|
|
dom.insertAfter(dom.create(document, 'br'), paragraphs[j]);
|
|
}
|
|
dom.unwrap(paragraphs[j]);
|
|
}
|
|
}
|
|
},
|
|
removeDefaultColors: function (spans) {
|
|
for (var i = 0; i < spans.length; i++) {
|
|
if (/^\s*color:\s*[^;]*;?$/i.test(spans[i].style.cssText)) {
|
|
dom.unwrap(spans[i]);
|
|
}
|
|
}
|
|
},
|
|
tables: function (placeholder) {
|
|
var tables = $(placeholder).find('table'), that = this, rows, firstRow, longestRow, i, j;
|
|
for (i = 0; i < tables.length; i++) {
|
|
rows = tables[i].rows;
|
|
longestRow = firstRow = rows[0];
|
|
for (j = 1; j < rows.length; j++) {
|
|
if (rows[j].cells.length > longestRow.cells.length) {
|
|
longestRow = rows[j];
|
|
}
|
|
}
|
|
that.createColGroup(longestRow);
|
|
that.convertHeaders(firstRow);
|
|
that.removeAttributes(tables[i]);
|
|
that.removeParagraphs(tables.eq(i).find('td,th'));
|
|
that.removeDefaultColors(tables.eq(i).find('span'));
|
|
}
|
|
},
|
|
headers: function (placeholder) {
|
|
var titles = $(placeholder).find('p.MsoTitle');
|
|
for (var i = 0; i < titles.length; i++) {
|
|
dom.changeTag(titles[i], 'h1');
|
|
}
|
|
},
|
|
clean: function (html) {
|
|
var that = this, placeholder;
|
|
html = Cleaner.fn.clean.call(that, html);
|
|
html = that.stripEmptyAnchors(html);
|
|
placeholder = dom.create(document, 'div', { innerHTML: html });
|
|
that.headers(placeholder);
|
|
that.lists(placeholder);
|
|
that.tables(placeholder);
|
|
html = placeholder.innerHTML.replace(/(<[^>]*)\s+class="?[^"\s>]*"?/gi, '$1');
|
|
return html;
|
|
}
|
|
});
|
|
var WebkitFormatCleaner = Cleaner.extend({
|
|
init: function () {
|
|
this.replacements = [
|
|
/\s+class="Apple-style-span[^"]*"/gi,
|
|
'',
|
|
/<(div|p|h[1-6])\s+style="[^"]*"/gi,
|
|
'<$1',
|
|
/^<div>(.*)<\/div>$/,
|
|
'$1'
|
|
];
|
|
},
|
|
applicable: function (html) {
|
|
return /class="?Apple-style-span|style="[^"]*-webkit-nbsp-mode/i.test(html);
|
|
}
|
|
});
|
|
var PrintCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this.managesUndoRedo = true;
|
|
},
|
|
exec: function () {
|
|
var editor = this.editor;
|
|
if (kendo.support.browser.msie) {
|
|
editor.document.execCommand('print', false, null);
|
|
} else if (editor.window.print) {
|
|
editor.window.print();
|
|
}
|
|
}
|
|
});
|
|
var ExportPdfCommand = Command.extend({
|
|
init: function (options) {
|
|
this.async = true;
|
|
Command.fn.init.call(this, options);
|
|
},
|
|
exec: function () {
|
|
var that = this;
|
|
var range = this.lockRange(true);
|
|
this.editor.saveAsPDF().then(function () {
|
|
that.releaseRange(range);
|
|
});
|
|
}
|
|
});
|
|
extend(editorNS, {
|
|
_finishUpdate: finishUpdate,
|
|
Command: Command,
|
|
GenericCommand: GenericCommand,
|
|
InsertHtmlCommand: InsertHtmlCommand,
|
|
InsertHtmlTool: InsertHtmlTool,
|
|
TypingHandler: TypingHandler,
|
|
SystemHandler: SystemHandler,
|
|
BackspaceHandler: BackspaceHandler,
|
|
Keyboard: Keyboard,
|
|
Clipboard: Clipboard,
|
|
Cleaner: Cleaner,
|
|
TabCleaner: TabCleaner,
|
|
MSWordFormatCleaner: MSWordFormatCleaner,
|
|
WebkitFormatCleaner: WebkitFormatCleaner,
|
|
PrintCommand: PrintCommand,
|
|
ExportPdfCommand: ExportPdfCommand
|
|
});
|
|
registerTool('insertHtml', new InsertHtmlTool({
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.dropDownListTemplate,
|
|
title: 'Insert HTML',
|
|
initialValue: 'Insert HTML'
|
|
})
|
|
}));
|
|
registerTool('print', new Tool({
|
|
command: PrintCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Print'
|
|
})
|
|
}));
|
|
registerTool('pdf', new Tool({
|
|
command: ExportPdfCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Export PDF'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/inlineformat', ['editor/system'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, Editor = kendo.ui.editor, formats = kendo.ui.Editor.fn.options.formats, EditorUtils = Editor.EditorUtils, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, extend = $.extend, registerTool = Editor.EditorUtils.registerTool, registerFormat = Editor.EditorUtils.registerFormat, KMARKER = 'k-marker';
|
|
var InlineFormatFinder = Class.extend({
|
|
init: function (format) {
|
|
this.format = format;
|
|
},
|
|
numberOfSiblings: function (referenceNode) {
|
|
var textNodesCount = 0, elementNodesCount = 0, markerCount = 0, parentNode = referenceNode.parentNode, node;
|
|
for (node = parentNode.firstChild; node; node = node.nextSibling) {
|
|
if (node != referenceNode) {
|
|
if (node.className == KMARKER) {
|
|
markerCount++;
|
|
} else if (node.nodeType == 3) {
|
|
textNodesCount++;
|
|
} else {
|
|
elementNodesCount++;
|
|
}
|
|
}
|
|
}
|
|
if (markerCount > 1 && parentNode.firstChild.className == KMARKER && parentNode.lastChild.className == KMARKER) {
|
|
return 0;
|
|
} else {
|
|
return elementNodesCount + textNodesCount;
|
|
}
|
|
},
|
|
findSuitable: function (sourceNode, skip) {
|
|
if (!skip && this.numberOfSiblings(sourceNode) > 0) {
|
|
return null;
|
|
}
|
|
var node = sourceNode.parentNode;
|
|
var tags = this.format[0].tags;
|
|
while (!dom.ofType(node, tags)) {
|
|
if (this.numberOfSiblings(node) > 0) {
|
|
return null;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
return node;
|
|
},
|
|
findFormat: function (sourceNode) {
|
|
var format = this.format, attrEquals = dom.attrEquals, i, len, node, tags, attributes;
|
|
for (i = 0, len = format.length; i < len; i++) {
|
|
node = sourceNode;
|
|
tags = format[i].tags;
|
|
attributes = format[i].attr;
|
|
if (node && dom.ofType(node, tags) && attrEquals(node, attributes)) {
|
|
return node;
|
|
}
|
|
while (node) {
|
|
node = dom.parentOfType(node, tags);
|
|
if (node && attrEquals(node, attributes)) {
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
isFormatted: function (nodes) {
|
|
var i, len;
|
|
for (i = 0, len = nodes.length; i < len; i++) {
|
|
if (this.findFormat(nodes[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var InlineFormatter = Class.extend({
|
|
init: function (format, values) {
|
|
this.finder = new InlineFormatFinder(format);
|
|
this.attributes = extend({}, format[0].attr, values);
|
|
this.tag = format[0].tags[0];
|
|
},
|
|
wrap: function (node) {
|
|
return dom.wrap(node, dom.create(node.ownerDocument, this.tag, this.attributes));
|
|
},
|
|
activate: function (range, nodes) {
|
|
if (this.finder.isFormatted(nodes)) {
|
|
this.split(range);
|
|
this.remove(nodes);
|
|
} else {
|
|
this.apply(nodes);
|
|
}
|
|
},
|
|
toggle: function (range) {
|
|
var nodes = RangeUtils.textNodes(range);
|
|
if (nodes.length > 0) {
|
|
this.activate(range, nodes);
|
|
}
|
|
},
|
|
apply: function (nodes) {
|
|
var formatNodes = [];
|
|
var i, l, node, formatNode;
|
|
for (i = 0, l = nodes.length; i < l; i++) {
|
|
node = nodes[i];
|
|
formatNode = this.finder.findSuitable(node);
|
|
if (formatNode) {
|
|
dom.attr(formatNode, this.attributes);
|
|
} else {
|
|
while (!dom.isBlock(node.parentNode) && node.parentNode.childNodes.length == 1) {
|
|
node = node.parentNode;
|
|
}
|
|
formatNode = this.wrap(node);
|
|
}
|
|
formatNodes.push(formatNode);
|
|
}
|
|
this.consolidate(formatNodes);
|
|
},
|
|
remove: function (nodes) {
|
|
var i, l, formatNode;
|
|
for (i = 0, l = nodes.length; i < l; i++) {
|
|
formatNode = this.finder.findFormat(nodes[i]);
|
|
if (formatNode) {
|
|
if (this.attributes && this.attributes.style) {
|
|
dom.unstyle(formatNode, this.attributes.style);
|
|
if (!formatNode.style.cssText && !formatNode.attributes['class']) {
|
|
dom.unwrap(formatNode);
|
|
}
|
|
} else {
|
|
dom.unwrap(formatNode);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
split: function (range) {
|
|
var nodes = RangeUtils.textNodes(range);
|
|
var l = nodes.length;
|
|
var i, formatNode;
|
|
if (l > 0) {
|
|
for (i = 0; i < l; i++) {
|
|
formatNode = this.finder.findFormat(nodes[i]);
|
|
if (formatNode) {
|
|
RangeUtils.split(range, formatNode, true);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
consolidate: function (nodes) {
|
|
var node, last;
|
|
while (nodes.length > 1) {
|
|
node = nodes.pop();
|
|
last = nodes[nodes.length - 1];
|
|
if (node.previousSibling && node.previousSibling.className == KMARKER) {
|
|
last.appendChild(node.previousSibling);
|
|
}
|
|
if (node.tagName == last.tagName && node.previousSibling == last && node.style.cssText == last.style.cssText) {
|
|
while (node.firstChild) {
|
|
last.appendChild(node.firstChild);
|
|
}
|
|
dom.remove(node);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var GreedyInlineFormatFinder = InlineFormatFinder.extend({
|
|
init: function (format, greedyProperty) {
|
|
this.format = format;
|
|
this.greedyProperty = greedyProperty;
|
|
InlineFormatFinder.fn.init.call(this, format);
|
|
},
|
|
getInlineCssValue: function (node) {
|
|
var attributes = node.attributes;
|
|
var trim = $.trim;
|
|
var i, l, attribute, name, attributeValue, css, pair, cssIndex, len;
|
|
var propertyAndValue, property, value;
|
|
if (!attributes) {
|
|
return;
|
|
}
|
|
for (i = 0, l = attributes.length; i < l; i++) {
|
|
attribute = attributes[i];
|
|
name = attribute.nodeName;
|
|
attributeValue = attribute.nodeValue;
|
|
if (attribute.specified && name == 'style') {
|
|
css = trim(attributeValue || node.style.cssText).split(';');
|
|
for (cssIndex = 0, len = css.length; cssIndex < len; cssIndex++) {
|
|
pair = css[cssIndex];
|
|
if (pair.length) {
|
|
propertyAndValue = pair.split(':');
|
|
property = trim(propertyAndValue[0].toLowerCase());
|
|
value = trim(propertyAndValue[1]);
|
|
if (property != this.greedyProperty) {
|
|
continue;
|
|
}
|
|
return property.indexOf('color') >= 0 ? dom.toHex(value) : value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getFormatInner: function (node) {
|
|
var $node = $(dom.isDataNode(node) ? node.parentNode : node);
|
|
var parents = $node.parentsUntil('[contentEditable]').addBack().toArray().reverse();
|
|
var i, len, value;
|
|
for (i = 0, len = parents.length; i < len; i++) {
|
|
value = this.greedyProperty == 'className' ? parents[i].className : this.getInlineCssValue(parents[i]);
|
|
if (value) {
|
|
return value;
|
|
}
|
|
}
|
|
return 'inherit';
|
|
},
|
|
getFormat: function (nodes) {
|
|
var result = this.getFormatInner(nodes[0]), i, len;
|
|
for (i = 1, len = nodes.length; i < len; i++) {
|
|
if (result != this.getFormatInner(nodes[i])) {
|
|
return '';
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
isFormatted: function (nodes) {
|
|
return this.getFormat(nodes) !== '';
|
|
}
|
|
});
|
|
var GreedyInlineFormatter = InlineFormatter.extend({
|
|
init: function (format, values, greedyProperty) {
|
|
InlineFormatter.fn.init.call(this, format, values);
|
|
this.values = values;
|
|
this.finder = new GreedyInlineFormatFinder(format, greedyProperty);
|
|
if (greedyProperty) {
|
|
this.greedyProperty = kendo.toCamelCase(greedyProperty);
|
|
}
|
|
},
|
|
activate: function (range, nodes) {
|
|
var greedyProperty = this.greedyProperty;
|
|
var action = 'apply';
|
|
this.split(range);
|
|
if (greedyProperty && this.values.style[greedyProperty] == 'inherit') {
|
|
action = 'remove';
|
|
}
|
|
this[action](nodes);
|
|
}
|
|
});
|
|
var InlineFormatTool = FormatTool.extend({
|
|
init: function (options) {
|
|
FormatTool.fn.init.call(this, extend(options, {
|
|
finder: new InlineFormatFinder(options.format),
|
|
formatter: function () {
|
|
return new InlineFormatter(options.format);
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
var DelayedExecutionTool = Tool.extend({
|
|
update: function (ui, nodes) {
|
|
var list = ui.data(this.type);
|
|
list.close();
|
|
list.value(this.finder.getFormat(nodes));
|
|
}
|
|
});
|
|
var FontTool = DelayedExecutionTool.extend({
|
|
init: function (options) {
|
|
Tool.fn.init.call(this, options);
|
|
this.type = kendo.support.browser.msie || kendo.support.touch ? 'kendoDropDownList' : 'kendoComboBox';
|
|
this.format = [{ tags: ['span'] }];
|
|
this.finder = new GreedyInlineFormatFinder(this.format, options.cssAttr);
|
|
},
|
|
command: function (commandArguments) {
|
|
var options = this.options, format = this.format, style = {};
|
|
return new Editor.FormatCommand(extend(commandArguments, {
|
|
formatter: function () {
|
|
style[options.domAttr] = commandArguments.value;
|
|
return new GreedyInlineFormatter(format, { style: style }, options.cssAttr);
|
|
}
|
|
}));
|
|
},
|
|
initialize: function (ui, initOptions) {
|
|
var editor = initOptions.editor, options = this.options, toolName = options.name, dataSource, defaultValue = [];
|
|
if (options.defaultValue) {
|
|
defaultValue = [{
|
|
text: editor.options.messages[options.defaultValue[0].text],
|
|
value: options.defaultValue[0].value
|
|
}];
|
|
}
|
|
dataSource = defaultValue.concat(options.items ? options.items : editor.options[toolName] || []);
|
|
ui.attr({ title: initOptions.title });
|
|
ui[this.type]({
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: dataSource,
|
|
change: function () {
|
|
Tool.exec(editor, toolName, this.value());
|
|
},
|
|
highlightFirst: false
|
|
});
|
|
ui.closest('.k-widget').removeClass('k-' + toolName).find('*').addBack().attr('unselectable', 'on');
|
|
ui.data(this.type).value('inherit');
|
|
}
|
|
});
|
|
var ColorTool = Tool.extend({
|
|
init: function (options) {
|
|
Tool.fn.init.call(this, options);
|
|
this.format = [{ tags: ['span'] }];
|
|
this.finder = new GreedyInlineFormatFinder(this.format, options.cssAttr);
|
|
},
|
|
options: { palette: 'websafe' },
|
|
update: function () {
|
|
this._widget.close();
|
|
},
|
|
command: function (commandArguments) {
|
|
var options = this.options, format = this.format, style = {};
|
|
return new Editor.FormatCommand(extend(commandArguments, {
|
|
formatter: function () {
|
|
style[options.domAttr] = commandArguments.value;
|
|
return new GreedyInlineFormatter(format, { style: style }, options.cssAttr);
|
|
}
|
|
}));
|
|
},
|
|
initialize: function (ui, initOptions) {
|
|
var editor = initOptions.editor, toolName = this.name, options = extend({}, ColorTool.fn.options, this.options), palette = options.palette;
|
|
ui = this._widget = new kendo.ui.ColorPicker(ui, {
|
|
toolIcon: 'k-' + options.name,
|
|
palette: palette,
|
|
change: function () {
|
|
var color = ui.value();
|
|
if (color) {
|
|
Tool.exec(editor, toolName, color);
|
|
}
|
|
editor.focus();
|
|
},
|
|
activate: function (e) {
|
|
e.preventDefault();
|
|
ui.trigger('change');
|
|
}
|
|
});
|
|
ui.wrapper.attr({
|
|
title: initOptions.title,
|
|
unselectable: 'on'
|
|
}).find('*').attr('unselectable', 'on');
|
|
}
|
|
});
|
|
extend(Editor, {
|
|
InlineFormatFinder: InlineFormatFinder,
|
|
InlineFormatter: InlineFormatter,
|
|
DelayedExecutionTool: DelayedExecutionTool,
|
|
GreedyInlineFormatFinder: GreedyInlineFormatFinder,
|
|
GreedyInlineFormatter: GreedyInlineFormatter,
|
|
InlineFormatTool: InlineFormatTool,
|
|
FontTool: FontTool,
|
|
ColorTool: ColorTool
|
|
});
|
|
registerFormat('bold', [
|
|
{
|
|
tags: [
|
|
'strong',
|
|
'b'
|
|
]
|
|
},
|
|
{
|
|
tags: ['span'],
|
|
attr: { style: { fontWeight: 'bold' } }
|
|
}
|
|
]);
|
|
registerTool('bold', new InlineFormatTool({
|
|
key: 'B',
|
|
ctrl: true,
|
|
format: formats.bold,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Bold'
|
|
})
|
|
}));
|
|
registerFormat('italic', [
|
|
{
|
|
tags: [
|
|
'em',
|
|
'i'
|
|
]
|
|
},
|
|
{
|
|
tags: ['span'],
|
|
attr: { style: { fontStyle: 'italic' } }
|
|
}
|
|
]);
|
|
registerTool('italic', new InlineFormatTool({
|
|
key: 'I',
|
|
ctrl: true,
|
|
format: formats.italic,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Italic'
|
|
})
|
|
}));
|
|
registerFormat('underline', [
|
|
{
|
|
tags: ['span'],
|
|
attr: { style: { textDecoration: 'underline' } }
|
|
},
|
|
{ tags: ['u'] }
|
|
]);
|
|
registerTool('underline', new InlineFormatTool({
|
|
key: 'U',
|
|
ctrl: true,
|
|
format: formats.underline,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Underline'
|
|
})
|
|
}));
|
|
registerFormat('strikethrough', [
|
|
{
|
|
tags: [
|
|
'del',
|
|
'strike'
|
|
]
|
|
},
|
|
{
|
|
tags: ['span'],
|
|
attr: { style: { textDecoration: 'line-through' } }
|
|
}
|
|
]);
|
|
registerTool('strikethrough', new InlineFormatTool({
|
|
format: formats.strikethrough,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Strikethrough'
|
|
})
|
|
}));
|
|
registerFormat('superscript', [{ tags: ['sup'] }]);
|
|
registerTool('superscript', new InlineFormatTool({
|
|
format: formats.superscript,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Superscript'
|
|
})
|
|
}));
|
|
registerFormat('subscript', [{ tags: ['sub'] }]);
|
|
registerTool('subscript', new InlineFormatTool({
|
|
format: formats.subscript,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Subscript'
|
|
})
|
|
}));
|
|
registerTool('foreColor', new ColorTool({
|
|
cssAttr: 'color',
|
|
domAttr: 'color',
|
|
name: 'foreColor',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.colorPickerTemplate,
|
|
title: 'Color'
|
|
})
|
|
}));
|
|
registerTool('backColor', new ColorTool({
|
|
cssAttr: 'background-color',
|
|
domAttr: 'backgroundColor',
|
|
name: 'backColor',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.colorPickerTemplate,
|
|
title: 'Background Color'
|
|
})
|
|
}));
|
|
registerTool('fontName', new FontTool({
|
|
cssAttr: 'font-family',
|
|
domAttr: 'fontFamily',
|
|
name: 'fontName',
|
|
defaultValue: [{
|
|
text: 'fontNameInherit',
|
|
value: 'inherit'
|
|
}],
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.comboBoxTemplate,
|
|
title: 'Font Name'
|
|
})
|
|
}));
|
|
registerTool('fontSize', new FontTool({
|
|
cssAttr: 'font-size',
|
|
domAttr: 'fontSize',
|
|
name: 'fontSize',
|
|
defaultValue: [{
|
|
text: 'fontSizeInherit',
|
|
value: 'inherit'
|
|
}],
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.comboBoxTemplate,
|
|
title: 'Font Size'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/formatblock', ['editor/inlineformat'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, formats = kendo.ui.Editor.fn.options.formats, dom = Editor.Dom, Command = Editor.Command, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, EditorUtils = Editor.EditorUtils, registerTool = EditorUtils.registerTool, registerFormat = EditorUtils.registerFormat, RangeUtils = Editor.RangeUtils;
|
|
var BlockFormatFinder = Class.extend({
|
|
init: function (format) {
|
|
this.format = format;
|
|
},
|
|
contains: function (node, children) {
|
|
var i, len, child;
|
|
for (i = 0, len = children.length; i < len; i++) {
|
|
child = children[i];
|
|
if (!child || !dom.isAncestorOrSelf(node, child)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
findSuitable: function (nodes) {
|
|
var format = this.format, suitable = [], i, len, candidate;
|
|
for (i = 0, len = nodes.length; i < len; i++) {
|
|
for (var f = format.length - 1; f >= 0; f--) {
|
|
candidate = dom.ofType(nodes[i], format[f].tags) ? nodes[i] : dom.closestEditableOfType(nodes[i], format[f].tags);
|
|
if (candidate) {
|
|
break;
|
|
}
|
|
}
|
|
if (!candidate || candidate.contentEditable === 'true') {
|
|
return [];
|
|
}
|
|
if ($.inArray(candidate, suitable) < 0) {
|
|
suitable.push(candidate);
|
|
}
|
|
}
|
|
for (i = 0, len = suitable.length; i < len; i++) {
|
|
if (this.contains(suitable[i], suitable)) {
|
|
return [suitable[i]];
|
|
}
|
|
}
|
|
return suitable;
|
|
},
|
|
findFormat: function (sourceNode) {
|
|
var format = this.format, i, len, node, tags, attributes;
|
|
var editableParent = dom.editableParent(sourceNode);
|
|
for (i = 0, len = format.length; i < len; i++) {
|
|
node = sourceNode;
|
|
tags = format[i].tags;
|
|
attributes = format[i].attr;
|
|
while (node && dom.isAncestorOf(editableParent, node)) {
|
|
if (dom.ofType(node, tags) && dom.attrEquals(node, attributes)) {
|
|
return node;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
getFormat: function (nodes) {
|
|
var that = this, findFormat = function (node) {
|
|
return that.findFormat(dom.isDataNode(node) ? node.parentNode : node);
|
|
}, result = findFormat(nodes[0]), i, len;
|
|
if (!result) {
|
|
return '';
|
|
}
|
|
for (i = 1, len = nodes.length; i < len; i++) {
|
|
if (result != findFormat(nodes[i])) {
|
|
return '';
|
|
}
|
|
}
|
|
return result.nodeName.toLowerCase();
|
|
},
|
|
isFormatted: function (nodes) {
|
|
for (var i = 0, len = nodes.length; i < len; i++) {
|
|
if (!this.findFormat(nodes[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
var BlockFormatter = Class.extend({
|
|
init: function (format, values) {
|
|
this.format = format;
|
|
this.values = values;
|
|
this.finder = new BlockFormatFinder(format);
|
|
},
|
|
wrap: function (tag, attributes, nodes) {
|
|
var commonAncestor = nodes.length == 1 ? dom.blockParentOrBody(nodes[0]) : dom.commonAncestor.apply(null, nodes);
|
|
if (dom.isInline(commonAncestor)) {
|
|
commonAncestor = dom.blockParentOrBody(commonAncestor);
|
|
}
|
|
var ancestors = dom.significantChildNodes(commonAncestor), position = dom.findNodeIndex(ancestors[0]), wrapper = dom.create(commonAncestor.ownerDocument, tag, attributes), i, ancestor;
|
|
for (i = 0; i < ancestors.length; i++) {
|
|
ancestor = ancestors[i];
|
|
if (dom.isBlock(ancestor)) {
|
|
dom.attr(ancestor, attributes);
|
|
if (wrapper.childNodes.length) {
|
|
dom.insertBefore(wrapper, ancestor);
|
|
wrapper = wrapper.cloneNode(false);
|
|
}
|
|
position = dom.findNodeIndex(ancestor) + 1;
|
|
continue;
|
|
}
|
|
wrapper.appendChild(ancestor);
|
|
}
|
|
if (wrapper.firstChild) {
|
|
dom.insertAt(commonAncestor, wrapper, position);
|
|
}
|
|
},
|
|
apply: function (nodes) {
|
|
var format, values = this.values;
|
|
function attributes(format) {
|
|
return extend({}, format && format.attr, values);
|
|
}
|
|
var images = dom.filter('img', nodes);
|
|
var imageFormat = EditorUtils.formatByName('img', this.format);
|
|
var imageAttributes = attributes(imageFormat);
|
|
$.each(images, function () {
|
|
dom.attr(this, imageAttributes);
|
|
});
|
|
if (images.length == nodes.length) {
|
|
return;
|
|
}
|
|
var nonImages = dom.filter('img', nodes, true);
|
|
var formatNodes = this.finder.findSuitable(nonImages);
|
|
if (formatNodes.length) {
|
|
for (var i = 0, len = formatNodes.length; i < len; i++) {
|
|
format = EditorUtils.formatByName(dom.name(formatNodes[i]), this.format);
|
|
dom.attr(formatNodes[i], attributes(format));
|
|
}
|
|
} else {
|
|
format = this.format[0];
|
|
this.wrap(format.tags[0], attributes(format), nonImages);
|
|
}
|
|
},
|
|
remove: function (nodes) {
|
|
var i, l, formatNode, namedFormat, name;
|
|
for (i = 0, l = nodes.length; i < l; i++) {
|
|
formatNode = this.finder.findFormat(nodes[i]);
|
|
if (formatNode) {
|
|
name = dom.name(formatNode);
|
|
if (name == 'div' && !formatNode.getAttribute('class')) {
|
|
dom.unwrap(formatNode);
|
|
} else {
|
|
namedFormat = EditorUtils.formatByName(name, this.format);
|
|
if (namedFormat.attr.style) {
|
|
dom.unstyle(formatNode, namedFormat.attr.style);
|
|
}
|
|
if (namedFormat.attr.className) {
|
|
dom.removeClass(formatNode, namedFormat.attr.className);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toggle: function (range) {
|
|
var that = this, nodes = RangeUtils.nodes(range);
|
|
if (that.finder.isFormatted(nodes)) {
|
|
that.remove(nodes);
|
|
} else {
|
|
that.apply(nodes);
|
|
}
|
|
}
|
|
});
|
|
var GreedyBlockFormatter = Class.extend({
|
|
init: function (format, values) {
|
|
var that = this;
|
|
that.format = format;
|
|
that.values = values;
|
|
that.finder = new BlockFormatFinder(format);
|
|
},
|
|
apply: function (nodes) {
|
|
var format = this.format;
|
|
var blocks = dom.blockParents(nodes);
|
|
var formatTag = format[0].tags[0];
|
|
var i, len, list, formatter, range;
|
|
var element;
|
|
var tagName;
|
|
if (blocks.length) {
|
|
for (i = 0, len = blocks.length; i < len; i++) {
|
|
tagName = dom.name(blocks[i]);
|
|
if (tagName == 'li') {
|
|
list = blocks[i].parentNode;
|
|
formatter = new Editor.ListFormatter(list.nodeName.toLowerCase(), formatTag);
|
|
range = this.editor.createRange();
|
|
range.selectNode(blocks[i]);
|
|
formatter.toggle(range);
|
|
} else if (formatTag && (tagName == 'td' || blocks[i].attributes.contentEditable)) {
|
|
new BlockFormatter(format, this.values).apply(blocks[i].childNodes);
|
|
} else {
|
|
element = dom.changeTag(blocks[i], formatTag);
|
|
dom.attr(element, format[0].attr);
|
|
}
|
|
}
|
|
} else {
|
|
new BlockFormatter(format, this.values).apply(nodes);
|
|
}
|
|
},
|
|
toggle: function (range) {
|
|
var nodes = RangeUtils.textNodes(range);
|
|
if (!nodes.length) {
|
|
range.selectNodeContents(range.commonAncestorContainer);
|
|
nodes = RangeUtils.textNodes(range);
|
|
if (!nodes.length) {
|
|
nodes = dom.significantChildNodes(range.commonAncestorContainer);
|
|
}
|
|
}
|
|
this.apply(nodes);
|
|
}
|
|
});
|
|
var FormatCommand = Command.extend({
|
|
init: function (options) {
|
|
options.formatter = options.formatter();
|
|
Command.fn.init.call(this, options);
|
|
}
|
|
});
|
|
var BlockFormatTool = FormatTool.extend({
|
|
init: function (options) {
|
|
FormatTool.fn.init.call(this, extend(options, {
|
|
finder: new BlockFormatFinder(options.format),
|
|
formatter: function () {
|
|
return new BlockFormatter(options.format);
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
extend(Editor, {
|
|
BlockFormatFinder: BlockFormatFinder,
|
|
BlockFormatter: BlockFormatter,
|
|
GreedyBlockFormatter: GreedyBlockFormatter,
|
|
FormatCommand: FormatCommand,
|
|
BlockFormatTool: BlockFormatTool
|
|
});
|
|
var listElements = [
|
|
'ul',
|
|
'ol',
|
|
'li'
|
|
];
|
|
registerFormat('justifyLeft', [
|
|
{
|
|
tags: dom.nonListBlockElements,
|
|
attr: { style: { textAlign: 'left' } }
|
|
},
|
|
{
|
|
tags: ['img'],
|
|
attr: {
|
|
style: {
|
|
'float': 'left',
|
|
display: '',
|
|
marginLeft: '',
|
|
marginRight: ''
|
|
}
|
|
}
|
|
},
|
|
{
|
|
tags: listElements,
|
|
attr: {
|
|
style: {
|
|
textAlign: 'left',
|
|
listStylePosition: ''
|
|
}
|
|
}
|
|
}
|
|
]);
|
|
registerTool('justifyLeft', new BlockFormatTool({
|
|
format: formats.justifyLeft,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Justify Left'
|
|
})
|
|
}));
|
|
registerFormat('justifyCenter', [
|
|
{
|
|
tags: dom.nonListBlockElements,
|
|
attr: { style: { textAlign: 'center' } }
|
|
},
|
|
{
|
|
tags: ['img'],
|
|
attr: {
|
|
style: {
|
|
display: 'block',
|
|
marginLeft: 'auto',
|
|
marginRight: 'auto',
|
|
'float': ''
|
|
}
|
|
}
|
|
},
|
|
{
|
|
tags: listElements,
|
|
attr: {
|
|
style: {
|
|
textAlign: 'center',
|
|
listStylePosition: 'inside'
|
|
}
|
|
}
|
|
}
|
|
]);
|
|
registerTool('justifyCenter', new BlockFormatTool({
|
|
format: formats.justifyCenter,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Justify Center'
|
|
})
|
|
}));
|
|
registerFormat('justifyRight', [
|
|
{
|
|
tags: dom.nonListBlockElements,
|
|
attr: { style: { textAlign: 'right' } }
|
|
},
|
|
{
|
|
tags: ['img'],
|
|
attr: {
|
|
style: {
|
|
'float': 'right',
|
|
display: '',
|
|
marginLeft: '',
|
|
marginRight: ''
|
|
}
|
|
}
|
|
},
|
|
{
|
|
tags: listElements,
|
|
attr: {
|
|
style: {
|
|
textAlign: 'right',
|
|
listStylePosition: 'inside'
|
|
}
|
|
}
|
|
}
|
|
]);
|
|
registerTool('justifyRight', new BlockFormatTool({
|
|
format: formats.justifyRight,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Justify Right'
|
|
})
|
|
}));
|
|
registerFormat('justifyFull', [
|
|
{
|
|
tags: dom.nonListBlockElements,
|
|
attr: { style: { textAlign: 'justify' } }
|
|
},
|
|
{
|
|
tags: ['img'],
|
|
attr: {
|
|
style: {
|
|
display: 'block',
|
|
marginLeft: 'auto',
|
|
marginRight: 'auto',
|
|
'float': ''
|
|
}
|
|
}
|
|
},
|
|
{
|
|
tags: listElements,
|
|
attr: {
|
|
style: {
|
|
textAlign: 'justify',
|
|
listStylePosition: ''
|
|
}
|
|
}
|
|
}
|
|
]);
|
|
registerTool('justifyFull', new BlockFormatTool({
|
|
format: formats.justifyFull,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Justify Full'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/linebreak', ['editor/formatblock'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, extend = $.extend, editorNS = kendo.ui.editor, dom = editorNS.Dom, Command = editorNS.Command, Tool = editorNS.Tool, BlockFormatter = editorNS.BlockFormatter, normalize = dom.normalize, RangeUtils = editorNS.RangeUtils, registerTool = editorNS.EditorUtils.registerTool;
|
|
var ParagraphCommand = Command.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
Command.fn.init.call(this, options);
|
|
},
|
|
_insertMarker: function (doc, range) {
|
|
var marker = dom.create(doc, 'a'), container;
|
|
marker.className = 'k-marker';
|
|
range.insertNode(marker);
|
|
if (!marker.parentNode) {
|
|
container = range.commonAncestorContainer;
|
|
container.innerHTML = '';
|
|
container.appendChild(marker);
|
|
}
|
|
normalize(marker.parentNode);
|
|
return marker;
|
|
},
|
|
_moveFocus: function (range, candidate) {
|
|
if (dom.isEmpty(candidate)) {
|
|
range.setStartBefore(candidate);
|
|
} else {
|
|
range.selectNodeContents(candidate);
|
|
var focusNode = RangeUtils.textNodes(range)[0];
|
|
if (!focusNode) {
|
|
while (candidate.childNodes.length && !dom.is(candidate.firstChild, 'br')) {
|
|
candidate = candidate.firstChild;
|
|
}
|
|
focusNode = candidate;
|
|
}
|
|
if (dom.isEmpty(focusNode)) {
|
|
range.setStartBefore(focusNode);
|
|
} else {
|
|
if (dom.emptyNode(focusNode)) {
|
|
focusNode.innerHTML = '\uFEFF';
|
|
}
|
|
range.setStartBefore(focusNode.firstChild || focusNode);
|
|
}
|
|
}
|
|
},
|
|
shouldTrim: function (range) {
|
|
var blocks = 'p,h1,h2,h3,h4,h5,h6'.split(','), startInBlock = dom.parentOfType(range.startContainer, blocks), endInBlock = dom.parentOfType(range.endContainer, blocks);
|
|
return startInBlock && !endInBlock || !startInBlock && endInBlock;
|
|
},
|
|
_blankAfter: function (node) {
|
|
while (node && (dom.isMarker(node) || dom.stripBom(node.nodeValue) === '')) {
|
|
node = node.nextSibling;
|
|
}
|
|
return !node;
|
|
},
|
|
exec: function () {
|
|
var range = this.getRange(), doc = RangeUtils.documentFromRange(range), parent, previous, next, emptyParagraphContent = editorNS.emptyElementContent, paragraph, marker, li, heading, rng, shouldTrim = this.shouldTrim(range);
|
|
range.deleteContents();
|
|
marker = this._insertMarker(doc, range);
|
|
li = dom.closestEditableOfType(marker, ['li']);
|
|
heading = dom.closestEditableOfType(marker, 'h1,h2,h3,h4,h5,h6'.split(','));
|
|
if (li) {
|
|
if (dom.emptyNode(li)) {
|
|
paragraph = dom.create(doc, 'p');
|
|
if (li.nextSibling) {
|
|
rng = range.cloneRange();
|
|
rng.selectNode(li);
|
|
RangeUtils.split(rng, li.parentNode);
|
|
}
|
|
dom.insertAfter(paragraph, li.parentNode);
|
|
dom.remove(li.parentNode.childNodes.length == 1 ? li.parentNode : li);
|
|
paragraph.innerHTML = emptyParagraphContent;
|
|
next = paragraph;
|
|
}
|
|
} else if (heading && this._blankAfter(marker)) {
|
|
paragraph = dom.create(doc, 'p');
|
|
dom.insertAfter(paragraph, heading);
|
|
paragraph.innerHTML = emptyParagraphContent;
|
|
dom.remove(marker);
|
|
next = paragraph;
|
|
}
|
|
if (!next) {
|
|
if (!(li || heading)) {
|
|
new BlockFormatter([{ tags: ['p'] }]).apply([marker]);
|
|
}
|
|
range.selectNode(marker);
|
|
parent = dom.parentOfType(marker, [li ? 'li' : heading ? dom.name(heading) : 'p']);
|
|
RangeUtils.split(range, parent, shouldTrim);
|
|
previous = parent.previousSibling;
|
|
if (dom.is(previous, 'li') && previous.firstChild && !dom.is(previous.firstChild, 'br')) {
|
|
previous = previous.firstChild;
|
|
}
|
|
next = parent.nextSibling;
|
|
this.clean(previous);
|
|
this.clean(next, { links: true });
|
|
if (dom.is(next, 'li') && next.firstChild && !dom.is(next.firstChild, 'br')) {
|
|
next = next.firstChild;
|
|
}
|
|
dom.remove(parent);
|
|
normalize(previous);
|
|
}
|
|
normalize(next);
|
|
this._moveFocus(range, next);
|
|
range.collapse(true);
|
|
dom.scrollTo(next);
|
|
RangeUtils.selectRange(range);
|
|
},
|
|
clean: function (node, options) {
|
|
var root = node;
|
|
if (node.firstChild && dom.is(node.firstChild, 'br')) {
|
|
dom.remove(node.firstChild);
|
|
}
|
|
if (dom.isDataNode(node) && !node.nodeValue) {
|
|
node = node.parentNode;
|
|
}
|
|
if (node) {
|
|
var siblings = false;
|
|
while (node.firstChild && node.firstChild.nodeType == 1) {
|
|
siblings = siblings || dom.significantNodes(node.childNodes).length > 1;
|
|
node = node.firstChild;
|
|
}
|
|
if (!dom.isEmpty(node) && /^\s*$/.test(node.innerHTML) && !siblings) {
|
|
$(root).find('.k-br').remove();
|
|
node.innerHTML = editorNS.emptyElementContent;
|
|
}
|
|
if (options && options.links) {
|
|
while (node != root) {
|
|
if (dom.is(node, 'a') && dom.emptyNode(node)) {
|
|
dom.unwrap(node);
|
|
break;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var NewLineCommand = Command.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
Command.fn.init.call(this, options);
|
|
},
|
|
exec: function () {
|
|
var range = this.getRange();
|
|
var br = dom.create(RangeUtils.documentFromRange(range), 'br');
|
|
var filler;
|
|
var browser = kendo.support.browser;
|
|
var oldIE = browser.msie && browser.version < 11;
|
|
range.deleteContents();
|
|
range.insertNode(br);
|
|
normalize(br.parentNode);
|
|
if (!oldIE && (!br.nextSibling || dom.isWhitespace(br.nextSibling))) {
|
|
filler = br.cloneNode(true);
|
|
filler.className = 'k-br';
|
|
dom.insertAfter(filler, br);
|
|
}
|
|
range.setStartAfter(br);
|
|
range.collapse(true);
|
|
dom.scrollTo(br.nextSibling || br);
|
|
RangeUtils.selectRange(range);
|
|
}
|
|
});
|
|
extend(editorNS, {
|
|
ParagraphCommand: ParagraphCommand,
|
|
NewLineCommand: NewLineCommand
|
|
});
|
|
registerTool('insertLineBreak', new Tool({
|
|
key: 13,
|
|
shift: true,
|
|
command: NewLineCommand
|
|
}));
|
|
registerTool('insertParagraph', new Tool({
|
|
key: 13,
|
|
command: ParagraphCommand
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/lists', ['editor/linebreak'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, EditorUtils = Editor.EditorUtils, Command = Editor.Command, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, BlockFormatFinder = Editor.BlockFormatFinder, textNodes = RangeUtils.textNodes, registerTool = Editor.EditorUtils.registerTool;
|
|
var ListFormatFinder = BlockFormatFinder.extend({
|
|
init: function (tag) {
|
|
this.tag = tag;
|
|
var tags = this.tags = [
|
|
tag == 'ul' ? 'ol' : 'ul',
|
|
tag
|
|
];
|
|
BlockFormatFinder.fn.init.call(this, [{ tags: tags }]);
|
|
},
|
|
isFormatted: function (nodes) {
|
|
var formatNodes = [];
|
|
var formatNode, i;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
formatNode = this.findFormat(nodes[i]);
|
|
if (formatNode && dom.name(formatNode) == this.tag) {
|
|
formatNodes.push(formatNode);
|
|
}
|
|
}
|
|
if (formatNodes.length < 1) {
|
|
return false;
|
|
}
|
|
if (formatNodes.length != nodes.length) {
|
|
return false;
|
|
}
|
|
for (i = 0; i < formatNodes.length; i++) {
|
|
if (formatNodes[i].parentNode != formatNode.parentNode) {
|
|
break;
|
|
}
|
|
if (formatNodes[i] != formatNode) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
findSuitable: function (nodes) {
|
|
var candidate = this.findFormat(nodes[0]);
|
|
if (candidate && dom.name(candidate) == this.tag) {
|
|
return candidate;
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
var ListFormatter = Class.extend({
|
|
init: function (tag, unwrapTag) {
|
|
var that = this;
|
|
that.finder = new ListFormatFinder(tag);
|
|
that.tag = tag;
|
|
that.unwrapTag = unwrapTag;
|
|
},
|
|
isList: function (node) {
|
|
var name = dom.name(node);
|
|
return name == 'ul' || name == 'ol' || name == 'dl';
|
|
},
|
|
wrap: function (list, nodes) {
|
|
var li = dom.create(list.ownerDocument, 'li'), i, node;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
node = nodes[i];
|
|
if (dom.is(node, 'li')) {
|
|
list.appendChild(node);
|
|
continue;
|
|
}
|
|
if (this.isList(node)) {
|
|
while (node.firstChild) {
|
|
list.appendChild(node.firstChild);
|
|
}
|
|
continue;
|
|
}
|
|
if (dom.is(node, 'td')) {
|
|
while (node.firstChild) {
|
|
li.appendChild(node.firstChild);
|
|
}
|
|
list.appendChild(li);
|
|
node.appendChild(list);
|
|
list = list.cloneNode(false);
|
|
li = li.cloneNode(false);
|
|
continue;
|
|
}
|
|
li.appendChild(node);
|
|
if (dom.isBlock(node)) {
|
|
list.appendChild(li);
|
|
dom.unwrap(node);
|
|
li = li.cloneNode(false);
|
|
}
|
|
}
|
|
if (li.firstChild) {
|
|
list.appendChild(li);
|
|
}
|
|
},
|
|
containsAny: function (parent, nodes) {
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
if (dom.isAncestorOrSelf(parent, nodes[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
suitable: function (candidate, nodes) {
|
|
if (candidate.className == 'k-marker') {
|
|
var sibling = candidate.nextSibling;
|
|
if (sibling && dom.isBlock(sibling)) {
|
|
return false;
|
|
}
|
|
sibling = candidate.previousSibling;
|
|
if (sibling && dom.isBlock(sibling)) {
|
|
return false;
|
|
}
|
|
}
|
|
return this.containsAny(candidate, nodes) || dom.isInline(candidate) || candidate.nodeType == 3;
|
|
},
|
|
_parentLists: function (node) {
|
|
var editable = dom.closestEditable(node);
|
|
return $(node).parentsUntil(editable, 'ul,ol');
|
|
},
|
|
split: function (range) {
|
|
var nodes = textNodes(range);
|
|
var start, end, parents;
|
|
if (nodes.length) {
|
|
start = dom.parentOfType(nodes[0], ['li']);
|
|
end = dom.parentOfType(nodes[nodes.length - 1], ['li']);
|
|
range.setStartBefore(start);
|
|
range.setEndAfter(end);
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
var formatNode = this.finder.findFormat(nodes[i]);
|
|
if (formatNode) {
|
|
parents = this._parentLists(formatNode);
|
|
if (parents.length) {
|
|
RangeUtils.split(range, parents.last()[0], true);
|
|
} else {
|
|
RangeUtils.split(range, formatNode, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
merge: function (tag, formatNode) {
|
|
var prev = formatNode.previousSibling, next;
|
|
while (prev && (prev.className == 'k-marker' || prev.nodeType == 3 && dom.isWhitespace(prev))) {
|
|
prev = prev.previousSibling;
|
|
}
|
|
if (prev && dom.name(prev) == tag) {
|
|
while (formatNode.firstChild) {
|
|
prev.appendChild(formatNode.firstChild);
|
|
}
|
|
dom.remove(formatNode);
|
|
formatNode = prev;
|
|
}
|
|
next = formatNode.nextSibling;
|
|
while (next && (next.className == 'k-marker' || next.nodeType == 3 && dom.isWhitespace(next))) {
|
|
next = next.nextSibling;
|
|
}
|
|
if (next && dom.name(next) == tag) {
|
|
while (formatNode.lastChild) {
|
|
next.insertBefore(formatNode.lastChild, next.firstChild);
|
|
}
|
|
dom.remove(formatNode);
|
|
}
|
|
},
|
|
breakable: function (node) {
|
|
return node != node.ownerDocument.body && !/table|tbody|tr|td/.test(dom.name(node)) && !node.attributes.contentEditable;
|
|
},
|
|
applyOnSection: function (section, nodes) {
|
|
var tag = this.tag;
|
|
var commonAncestor = dom.closestSplittableParent(nodes);
|
|
var ancestors = [];
|
|
var formatNode = this.finder.findSuitable(nodes);
|
|
if (!formatNode) {
|
|
formatNode = new ListFormatFinder(tag == 'ul' ? 'ol' : 'ul').findSuitable(nodes);
|
|
}
|
|
var childNodes;
|
|
if (/table|tbody/.test(dom.name(commonAncestor))) {
|
|
childNodes = $.map(nodes, function (node) {
|
|
return dom.parentOfType(node, ['td']);
|
|
});
|
|
} else {
|
|
childNodes = dom.significantChildNodes(commonAncestor);
|
|
if ($.grep(childNodes, dom.isBlock).length) {
|
|
childNodes = $.grep(childNodes, $.proxy(function (node) {
|
|
return this.containsAny(node, nodes);
|
|
}, this));
|
|
}
|
|
if (!childNodes.length) {
|
|
childNodes = nodes;
|
|
}
|
|
}
|
|
function pushAncestor() {
|
|
ancestors.push(this);
|
|
}
|
|
for (var i = 0; i < childNodes.length; i++) {
|
|
var child = childNodes[i];
|
|
var suitable = (!formatNode || !dom.isAncestorOrSelf(formatNode, child)) && this.suitable(child, nodes);
|
|
if (!suitable) {
|
|
continue;
|
|
}
|
|
if (formatNode && this.isList(child)) {
|
|
$.each(child.childNodes, pushAncestor);
|
|
dom.remove(child);
|
|
} else {
|
|
ancestors.push(child);
|
|
}
|
|
}
|
|
if (ancestors.length == childNodes.length && this.breakable(commonAncestor)) {
|
|
ancestors = [commonAncestor];
|
|
}
|
|
if (!formatNode) {
|
|
formatNode = dom.create(commonAncestor.ownerDocument, tag);
|
|
dom.insertBefore(formatNode, ancestors[0]);
|
|
}
|
|
this.wrap(formatNode, ancestors);
|
|
if (!dom.is(formatNode, tag)) {
|
|
dom.changeTag(formatNode, tag);
|
|
}
|
|
this.merge(tag, formatNode);
|
|
},
|
|
apply: function (nodes) {
|
|
var i = 0, sections = [], lastSection, lastNodes, section;
|
|
do {
|
|
section = dom.closestEditable(nodes[i], [
|
|
'td',
|
|
'body'
|
|
]);
|
|
if (!lastSection || section != lastSection) {
|
|
if (lastSection) {
|
|
sections.push({
|
|
section: lastSection,
|
|
nodes: lastNodes
|
|
});
|
|
}
|
|
lastNodes = [nodes[i]];
|
|
lastSection = section;
|
|
} else {
|
|
lastNodes.push(nodes[i]);
|
|
}
|
|
i++;
|
|
} while (i < nodes.length);
|
|
sections.push({
|
|
section: lastSection,
|
|
nodes: lastNodes
|
|
});
|
|
for (i = 0; i < sections.length; i++) {
|
|
this.applyOnSection(sections[i].section, sections[i].nodes);
|
|
}
|
|
},
|
|
unwrap: function (ul) {
|
|
var fragment = ul.ownerDocument.createDocumentFragment(), unwrapTag = this.unwrapTag, parents, li, p, child;
|
|
for (li = ul.firstChild; li; li = li.nextSibling) {
|
|
p = dom.create(ul.ownerDocument, unwrapTag || 'p');
|
|
while (li.firstChild) {
|
|
child = li.firstChild;
|
|
if (dom.isBlock(child)) {
|
|
if (p.firstChild) {
|
|
fragment.appendChild(p);
|
|
p = dom.create(ul.ownerDocument, unwrapTag || 'p');
|
|
}
|
|
fragment.appendChild(child);
|
|
} else {
|
|
p.appendChild(child);
|
|
}
|
|
}
|
|
if (p.firstChild) {
|
|
fragment.appendChild(p);
|
|
}
|
|
}
|
|
parents = this._parentLists(ul);
|
|
if (parents[0]) {
|
|
dom.insertAfter(fragment, parents.last()[0]);
|
|
parents.last().remove();
|
|
} else {
|
|
dom.insertAfter(fragment, ul);
|
|
}
|
|
dom.remove(ul);
|
|
},
|
|
remove: function (nodes) {
|
|
var formatNode;
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
formatNode = this.finder.findFormat(nodes[i]);
|
|
if (formatNode) {
|
|
this.unwrap(formatNode);
|
|
}
|
|
}
|
|
},
|
|
toggle: function (range) {
|
|
var that = this, nodes = textNodes(range), ancestor = range.commonAncestorContainer;
|
|
if (!nodes.length) {
|
|
range.selectNodeContents(ancestor);
|
|
nodes = textNodes(range);
|
|
if (!nodes.length) {
|
|
var text = ancestor.ownerDocument.createTextNode('');
|
|
range.startContainer.appendChild(text);
|
|
nodes = [text];
|
|
range.selectNode(text.parentNode);
|
|
}
|
|
}
|
|
if (that.finder.isFormatted(nodes)) {
|
|
that.split(range);
|
|
that.remove(nodes);
|
|
} else {
|
|
that.apply(nodes);
|
|
}
|
|
}
|
|
});
|
|
var ListCommand = Command.extend({
|
|
init: function (options) {
|
|
options.formatter = new ListFormatter(options.tag);
|
|
Command.fn.init.call(this, options);
|
|
}
|
|
});
|
|
var ListTool = FormatTool.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
FormatTool.fn.init.call(this, extend(options, { finder: new ListFormatFinder(options.tag) }));
|
|
},
|
|
command: function (commandArguments) {
|
|
return new ListCommand(extend(commandArguments, { tag: this.options.tag }));
|
|
}
|
|
});
|
|
extend(Editor, {
|
|
ListFormatFinder: ListFormatFinder,
|
|
ListFormatter: ListFormatter,
|
|
ListCommand: ListCommand,
|
|
ListTool: ListTool
|
|
});
|
|
registerTool('insertUnorderedList', new ListTool({
|
|
tag: 'ul',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Insert unordered list'
|
|
})
|
|
}));
|
|
registerTool('insertOrderedList', new ListTool({
|
|
tag: 'ol',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Insert ordered list'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/link', ['editor/lists'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Class = kendo.Class, extend = $.extend, proxy = $.proxy, Editor = kendo.ui.editor, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, EditorUtils = Editor.EditorUtils, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, InlineFormatter = Editor.InlineFormatter, InlineFormatFinder = Editor.InlineFormatFinder, textNodes = RangeUtils.textNodes, registerTool = Editor.EditorUtils.registerTool;
|
|
var LinkFormatFinder = Class.extend({
|
|
findSuitable: function (sourceNode) {
|
|
return dom.parentOfType(sourceNode, ['a']);
|
|
}
|
|
});
|
|
var LinkFormatter = Class.extend({
|
|
init: function () {
|
|
this.finder = new LinkFormatFinder();
|
|
},
|
|
apply: function (range, attributes) {
|
|
var nodes = textNodes(range);
|
|
var markers, doc, formatter, a, parent;
|
|
if (attributes.innerHTML) {
|
|
doc = RangeUtils.documentFromRange(range);
|
|
markers = RangeUtils.getMarkers(range);
|
|
range.deleteContents();
|
|
a = dom.create(doc, 'a', attributes);
|
|
range.insertNode(a);
|
|
parent = a.parentNode;
|
|
if (dom.name(parent) == 'a') {
|
|
dom.insertAfter(a, parent);
|
|
}
|
|
if (dom.emptyNode(parent)) {
|
|
dom.remove(parent);
|
|
}
|
|
var ref = a;
|
|
for (var i = 0; i < markers.length; i++) {
|
|
dom.insertAfter(markers[i], ref);
|
|
ref = markers[i];
|
|
}
|
|
if (markers.length) {
|
|
dom.insertBefore(doc.createTextNode('\uFEFF'), markers[1]);
|
|
dom.insertAfter(doc.createTextNode('\uFEFF'), markers[1]);
|
|
range.setStartBefore(markers[0]);
|
|
range.setEndAfter(markers[markers.length - 1]);
|
|
}
|
|
} else {
|
|
formatter = new InlineFormatter([{ tags: ['a'] }], attributes);
|
|
formatter.finder = this.finder;
|
|
formatter.apply(nodes);
|
|
}
|
|
}
|
|
});
|
|
var UnlinkCommand = Command.extend({
|
|
init: function (options) {
|
|
options.formatter = {
|
|
toggle: function (range) {
|
|
new InlineFormatter([{ tags: ['a'] }]).remove(textNodes(range));
|
|
}
|
|
};
|
|
this.options = options;
|
|
Command.fn.init.call(this, options);
|
|
}
|
|
});
|
|
var LinkCommand = Command.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
Command.fn.init.call(this, options);
|
|
this.formatter = new LinkFormatter();
|
|
if (!options.url) {
|
|
this.attributes = null;
|
|
this.async = true;
|
|
} else {
|
|
this.exec = function () {
|
|
this.formatter.apply(options.range, {
|
|
href: options.url,
|
|
innerHTML: options.text || options.url,
|
|
target: options.target
|
|
});
|
|
};
|
|
}
|
|
},
|
|
_dialogTemplate: function () {
|
|
return kendo.template('<div class="k-editor-dialog k-popup-edit-form k-edit-form-container">' + '<div class=\'k-edit-label\'>' + '<label for=\'k-editor-link-url\'>#: messages.linkWebAddress #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type=\'text\' class=\'k-input k-textbox\' id=\'k-editor-link-url\'>' + '</div>' + '<div class=\'k-edit-label k-editor-link-text-row\'>' + '<label for=\'k-editor-link-text\'>#: messages.linkText #</label>' + '</div>' + '<div class=\'k-edit-field k-editor-link-text-row\'>' + '<input type=\'text\' class=\'k-input k-textbox\' id=\'k-editor-link-text\'>' + '</div>' + '<div class=\'k-edit-label\'>' + '<label for=\'k-editor-link-title\'>#: messages.linkToolTip #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type=\'text\' class=\'k-input k-textbox\' id=\'k-editor-link-title\'>' + '</div>' + '<div class=\'k-edit-label\'></div>' + '<div class=\'k-edit-field\'>' + '<input type=\'checkbox\' class=\'k-checkbox\' id=\'k-editor-link-target\'>' + '<label for=\'k-editor-link-target\' class=\'k-checkbox-label\'>#: messages.linkOpenInNewWindow #</label>' + '</div>' + '<div class=\'k-edit-buttons k-state-default\'>' + '<button class="k-dialog-insert k-button k-primary">#: messages.dialogInsert #</button>' + '<button class="k-dialog-close k-button">#: messages.dialogCancel #</button>' + '</div>' + '</div>')({ messages: this.editor.options.messages });
|
|
},
|
|
exec: function () {
|
|
var messages = this.editor.options.messages;
|
|
this._initialText = '';
|
|
this._range = this.lockRange(true);
|
|
var nodes = textNodes(this._range);
|
|
var a = nodes.length ? this.formatter.finder.findSuitable(nodes[0]) : null;
|
|
var img = nodes.length && dom.name(nodes[0]) == 'img';
|
|
var dialog = this.createDialog(this._dialogTemplate(), {
|
|
title: messages.createLink,
|
|
close: proxy(this._close, this),
|
|
visible: false
|
|
});
|
|
if (a) {
|
|
this._range.selectNodeContents(a);
|
|
nodes = textNodes(this._range);
|
|
}
|
|
this._initialText = this.linkText(nodes);
|
|
dialog.find('.k-dialog-insert').click(proxy(this._apply, this)).end().find('.k-dialog-close').click(proxy(this._close, this)).end().find('.k-edit-field input').keydown(proxy(this._keydown, this)).end().find('#k-editor-link-url').val(this.linkUrl(a)).end().find('#k-editor-link-text').val(this._initialText).end().find('#k-editor-link-title').val(a ? a.title : '').end().find('#k-editor-link-target').attr('checked', a ? a.target == '_blank' : false).end().find('.k-editor-link-text-row').toggle(!img);
|
|
this._dialog = dialog.data('kendoWindow').center().open();
|
|
$('#k-editor-link-url', dialog).focus().select();
|
|
},
|
|
_keydown: function (e) {
|
|
var keys = kendo.keys;
|
|
if (e.keyCode == keys.ENTER) {
|
|
this._apply(e);
|
|
} else if (e.keyCode == keys.ESC) {
|
|
this._close(e);
|
|
}
|
|
},
|
|
_apply: function (e) {
|
|
var element = this._dialog.element;
|
|
var href = $('#k-editor-link-url', element).val();
|
|
var title, text, target;
|
|
var textInput = $('#k-editor-link-text', element);
|
|
if (href && href != 'http://') {
|
|
if (href.indexOf('@') > 0 && !/^(\w+:)|(\/\/)/i.test(href)) {
|
|
href = 'mailto:' + href;
|
|
}
|
|
this.attributes = { href: href };
|
|
title = $('#k-editor-link-title', element).val();
|
|
if (title) {
|
|
this.attributes.title = title;
|
|
}
|
|
if (textInput.is(':visible')) {
|
|
text = textInput.val();
|
|
if (!text && !this._initialText) {
|
|
this.attributes.innerHTML = href;
|
|
} else if (text && text !== this._initialText) {
|
|
this.attributes.innerHTML = dom.stripBom(text);
|
|
}
|
|
}
|
|
target = $('#k-editor-link-target', element).is(':checked');
|
|
this.attributes.target = target ? '_blank' : null;
|
|
this.formatter.apply(this._range, this.attributes);
|
|
}
|
|
this._close(e);
|
|
if (this.change) {
|
|
this.change();
|
|
}
|
|
},
|
|
_close: function (e) {
|
|
e.preventDefault();
|
|
this._dialog.destroy();
|
|
dom.windowFromDocument(RangeUtils.documentFromRange(this._range)).focus();
|
|
this.releaseRange(this._range);
|
|
},
|
|
linkUrl: function (anchor) {
|
|
if (anchor) {
|
|
return anchor.getAttribute('href', 2);
|
|
}
|
|
return 'http://';
|
|
},
|
|
linkText: function (nodes) {
|
|
var text = '';
|
|
var i;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
text += nodes[i].nodeValue;
|
|
}
|
|
return dom.stripBom(text || '');
|
|
},
|
|
redo: function () {
|
|
var range = this.lockRange(true);
|
|
this.formatter.apply(range, this.attributes);
|
|
this.releaseRange(range);
|
|
}
|
|
});
|
|
var UnlinkTool = Tool.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
this.finder = new InlineFormatFinder([{ tags: ['a'] }]);
|
|
Tool.fn.init.call(this, $.extend(options, { command: UnlinkCommand }));
|
|
},
|
|
initialize: function (ui, options) {
|
|
Tool.fn.initialize.call(this, ui, options);
|
|
ui.addClass('k-state-disabled');
|
|
},
|
|
update: function (ui, nodes) {
|
|
ui.toggleClass('k-state-disabled', !this.finder.isFormatted(nodes)).removeClass('k-state-hover');
|
|
}
|
|
});
|
|
extend(kendo.ui.editor, {
|
|
LinkFormatFinder: LinkFormatFinder,
|
|
LinkFormatter: LinkFormatter,
|
|
UnlinkCommand: UnlinkCommand,
|
|
LinkCommand: LinkCommand,
|
|
UnlinkTool: UnlinkTool
|
|
});
|
|
registerTool('createLink', new Tool({
|
|
key: 'K',
|
|
ctrl: true,
|
|
command: LinkCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Create Link'
|
|
})
|
|
}));
|
|
registerTool('unlink', new UnlinkTool({
|
|
key: 'K',
|
|
ctrl: true,
|
|
shift: true,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Remove Link'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/file', [
|
|
'kendo.filebrowser',
|
|
'editor/link'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, EditorUtils = Editor.EditorUtils, dom = Editor.Dom, registerTool = EditorUtils.registerTool, ToolTemplate = Editor.ToolTemplate, RangeUtils = Editor.RangeUtils, Command = Editor.Command, LinkFormatter = Editor.LinkFormatter, textNodes = RangeUtils.textNodes, keys = kendo.keys, KEDITORFILEURL = '#k-editor-file-url', KEDITORFILETITLE = '#k-editor-file-title';
|
|
var FileCommand = Command.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Command.fn.init.call(that, options);
|
|
that.formatter = new LinkFormatter();
|
|
that.async = true;
|
|
that.attributes = {};
|
|
},
|
|
insertFile: function (file, range) {
|
|
var attributes = this.attributes;
|
|
var doc = RangeUtils.documentFromRange(range);
|
|
if (attributes.href && attributes.href != 'http://') {
|
|
if (!file) {
|
|
file = dom.create(doc, 'a', { href: attributes.href });
|
|
file.innerHTML = attributes.innerHTML;
|
|
range.deleteContents();
|
|
range.insertNode(file);
|
|
if (!file.nextSibling) {
|
|
dom.insertAfter(doc.createTextNode('\uFEFF'), file);
|
|
}
|
|
range.setStartAfter(file);
|
|
range.setEndAfter(file);
|
|
RangeUtils.selectRange(range);
|
|
return true;
|
|
} else {
|
|
dom.attr(file, attributes);
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_dialogTemplate: function (showBrowser) {
|
|
return kendo.template('<div class="k-editor-dialog k-popup-edit-form k-edit-form-container">' + '# if (showBrowser) { #' + '<div class="k-filebrowser"></div>' + '# } #' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-file-url">#: messages.fileWebAddress #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-file-url">' + '</div>' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-file-title">#: messages.fileTitle #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-file-title">' + '</div>' + '<div class="k-edit-buttons k-state-default">' + '<button class="k-dialog-insert k-button k-primary">#: messages.dialogInsert #</button>' + '<button class="k-dialog-close k-button">#: messages.dialogCancel #</button>' + '</div>' + '</div>')({
|
|
messages: this.editor.options.messages,
|
|
showBrowser: showBrowser
|
|
});
|
|
},
|
|
redo: function () {
|
|
var that = this, range = that.lockRange();
|
|
this.formatter.apply(range, this.attributes);
|
|
that.releaseRange(range);
|
|
},
|
|
exec: function () {
|
|
var that = this, range = that.lockRange(), nodes = textNodes(range), applied = false, file = nodes.length ? this.formatter.finder.findSuitable(nodes[0]) : null, dialog, options = that.editor.options, messages = options.messages, fileBrowser = options.fileBrowser, showBrowser = !!(kendo.ui.FileBrowser && fileBrowser && fileBrowser.transport && fileBrowser.transport.read !== undefined), dialogOptions = {
|
|
title: messages.insertFile,
|
|
visible: false,
|
|
resizable: showBrowser
|
|
};
|
|
function apply(e) {
|
|
var element = dialog.element, href = element.find(KEDITORFILEURL).val().replace(/ /g, '%20'), innerHTML = element.find(KEDITORFILETITLE).val();
|
|
that.attributes = {
|
|
href: href,
|
|
innerHTML: innerHTML !== '' ? innerHTML : href
|
|
};
|
|
applied = that.insertFile(file, range);
|
|
close(e);
|
|
if (that.change) {
|
|
that.change();
|
|
}
|
|
}
|
|
function close(e) {
|
|
e.preventDefault();
|
|
dialog.destroy();
|
|
dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
|
|
if (!applied) {
|
|
that.releaseRange(range);
|
|
}
|
|
}
|
|
function keyDown(e) {
|
|
if (e.keyCode == keys.ENTER) {
|
|
apply(e);
|
|
} else if (e.keyCode == keys.ESC) {
|
|
close(e);
|
|
}
|
|
}
|
|
dialogOptions.close = close;
|
|
if (showBrowser) {
|
|
dialogOptions.width = 750;
|
|
}
|
|
dialog = this.createDialog(that._dialogTemplate(showBrowser), dialogOptions).toggleClass('k-filebrowser-dialog', showBrowser).find('.k-dialog-insert').click(apply).end().find('.k-dialog-close').click(close).end().find('.k-edit-field input').keydown(keyDown).end().find(KEDITORFILEURL).val(file ? file.getAttribute('href', 2) : 'http://').end().find(KEDITORFILETITLE).val(file ? file.title : '').end().data('kendoWindow');
|
|
if (showBrowser) {
|
|
that._fileBrowser = new kendo.ui.FileBrowser(dialog.element.find('.k-filebrowser'), extend({}, fileBrowser, {
|
|
change: function () {
|
|
dialog.element.find(KEDITORFILEURL).val(this.value());
|
|
},
|
|
apply: apply
|
|
}));
|
|
}
|
|
dialog.center().open();
|
|
dialog.element.find(KEDITORFILEURL).focus().select();
|
|
}
|
|
});
|
|
kendo.ui.editor.FileCommand = FileCommand;
|
|
registerTool('insertFile', new Editor.Tool({
|
|
command: FileCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Insert File'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/image', [
|
|
'kendo.imagebrowser',
|
|
'editor/link'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, EditorUtils = Editor.EditorUtils, dom = Editor.Dom, registerTool = EditorUtils.registerTool, ToolTemplate = Editor.ToolTemplate, RangeUtils = Editor.RangeUtils, Command = Editor.Command, keys = kendo.keys, KEDITORIMAGEURL = '#k-editor-image-url', KEDITORIMAGETITLE = '#k-editor-image-title', KEDITORIMAGEWIDTH = '#k-editor-image-width', KEDITORIMAGEHEIGHT = '#k-editor-image-height';
|
|
var ImageCommand = Command.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Command.fn.init.call(that, options);
|
|
that.async = true;
|
|
that.attributes = {};
|
|
},
|
|
insertImage: function (img, range) {
|
|
var attributes = this.attributes;
|
|
var doc = RangeUtils.documentFromRange(range);
|
|
if (attributes.src && attributes.src != 'http://') {
|
|
var removeIEAttributes = function () {
|
|
setTimeout(function () {
|
|
if (!attributes.width) {
|
|
img.removeAttribute('width');
|
|
}
|
|
if (!attributes.height) {
|
|
img.removeAttribute('height');
|
|
}
|
|
img.removeAttribute('complete');
|
|
});
|
|
};
|
|
if (!img) {
|
|
img = dom.create(doc, 'img', attributes);
|
|
img.onload = img.onerror = removeIEAttributes;
|
|
range.deleteContents();
|
|
range.insertNode(img);
|
|
if (!img.nextSibling) {
|
|
dom.insertAfter(doc.createTextNode('\uFEFF'), img);
|
|
}
|
|
removeIEAttributes();
|
|
range.setStartAfter(img);
|
|
range.setEndAfter(img);
|
|
RangeUtils.selectRange(range);
|
|
return true;
|
|
} else {
|
|
img.onload = img.onerror = removeIEAttributes;
|
|
dom.attr(img, attributes);
|
|
removeIEAttributes();
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_dialogTemplate: function (showBrowser) {
|
|
return kendo.template('<div class="k-editor-dialog k-popup-edit-form k-edit-form-container">' + '# if (showBrowser) { #' + '<div class="k-filebrowser k-imagebrowser"></div>' + '# } #' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-image-url">#: messages.imageWebAddress #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-image-url">' + '</div>' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-image-title">#: messages.imageAltText #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-image-title">' + '</div>' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-image-width">#: messages.imageWidth #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-image-width">' + '</div>' + '<div class=\'k-edit-label\'>' + '<label for="k-editor-image-height">#: messages.imageHeight #</label>' + '</div>' + '<div class=\'k-edit-field\'>' + '<input type="text" class="k-input k-textbox" id="k-editor-image-height">' + '</div>' + '<div class="k-edit-buttons k-state-default">' + '<button class="k-dialog-insert k-button k-primary">#: messages.dialogInsert #</button>' + '<button class="k-dialog-close k-button">#: messages.dialogCancel #</button>' + '</div>' + '</div>')({
|
|
messages: this.editor.options.messages,
|
|
showBrowser: showBrowser
|
|
});
|
|
},
|
|
redo: function () {
|
|
var that = this, range = that.lockRange();
|
|
if (!that.insertImage(RangeUtils.image(range), range)) {
|
|
that.releaseRange(range);
|
|
}
|
|
},
|
|
exec: function () {
|
|
var that = this, range = that.lockRange(), applied = false, img = RangeUtils.image(range), imageWidth = img && img.getAttribute('width') || '', imageHeight = img && img.getAttribute('height') || '', dialog, options = that.editor.options, messages = options.messages, imageBrowser = options.imageBrowser, showBrowser = !!(kendo.ui.ImageBrowser && imageBrowser && imageBrowser.transport && imageBrowser.transport.read !== undefined), dialogOptions = {
|
|
title: messages.insertImage,
|
|
visible: false,
|
|
resizable: showBrowser
|
|
};
|
|
function apply(e) {
|
|
var element = dialog.element, w = parseInt(element.find(KEDITORIMAGEWIDTH).val(), 10), h = parseInt(element.find(KEDITORIMAGEHEIGHT).val(), 10);
|
|
that.attributes = {
|
|
src: element.find(KEDITORIMAGEURL).val().replace(/ /g, '%20'),
|
|
alt: element.find(KEDITORIMAGETITLE).val()
|
|
};
|
|
that.attributes.width = null;
|
|
that.attributes.height = null;
|
|
if (!isNaN(w) && w > 0) {
|
|
that.attributes.width = w;
|
|
}
|
|
if (!isNaN(h) && h > 0) {
|
|
that.attributes.height = h;
|
|
}
|
|
applied = that.insertImage(img, range);
|
|
close(e);
|
|
if (that.change) {
|
|
that.change();
|
|
}
|
|
}
|
|
function close(e) {
|
|
e.preventDefault();
|
|
dialog.destroy();
|
|
dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
|
|
if (!applied) {
|
|
that.releaseRange(range);
|
|
}
|
|
}
|
|
function keyDown(e) {
|
|
if (e.keyCode == keys.ENTER) {
|
|
apply(e);
|
|
} else if (e.keyCode == keys.ESC) {
|
|
close(e);
|
|
}
|
|
}
|
|
dialogOptions.close = close;
|
|
if (showBrowser) {
|
|
dialogOptions.width = 750;
|
|
}
|
|
dialog = this.createDialog(that._dialogTemplate(showBrowser), dialogOptions).toggleClass('k-filebrowser-dialog', showBrowser).find('.k-dialog-insert').click(apply).end().find('.k-dialog-close').click(close).end().find('.k-edit-field input').keydown(keyDown).end().find(KEDITORIMAGEURL).val(img ? img.getAttribute('src', 2) : 'http://').end().find(KEDITORIMAGETITLE).val(img ? img.alt : '').end().find(KEDITORIMAGEWIDTH).val(imageWidth).end().find(KEDITORIMAGEHEIGHT).val(imageHeight).end().data('kendoWindow');
|
|
if (showBrowser) {
|
|
this._imageBrowser = new kendo.ui.ImageBrowser(dialog.element.find('.k-imagebrowser'), extend({}, imageBrowser, {
|
|
change: function () {
|
|
dialog.element.find(KEDITORIMAGEURL).val(this.value());
|
|
},
|
|
apply: apply
|
|
}));
|
|
}
|
|
dialog.center().open();
|
|
dialog.element.find(KEDITORIMAGEURL).focus().select();
|
|
}
|
|
});
|
|
kendo.ui.editor.ImageCommand = ImageCommand;
|
|
registerTool('insertImage', new Editor.Tool({
|
|
command: ImageCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Insert Image'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/components', ['editor/image'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, DropDownList = kendo.ui.DropDownList, dom = kendo.ui.editor.Dom;
|
|
var SelectBox = DropDownList.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
DropDownList.fn.init.call(that, element, options);
|
|
if (kendo.support.mobileOS.ios) {
|
|
this._initSelectOverlay();
|
|
this.bind('dataBound', $.proxy(this._initSelectOverlay, this));
|
|
}
|
|
that.text(that.options.title);
|
|
that.bind('open', function () {
|
|
if (that.options.autoSize) {
|
|
var list = that.list, listWidth;
|
|
list.css({
|
|
whiteSpace: 'nowrap',
|
|
width: 'auto'
|
|
});
|
|
listWidth = list.width();
|
|
if (listWidth) {
|
|
listWidth += 20;
|
|
} else {
|
|
listWidth = that._listWidth;
|
|
}
|
|
list.css('width', listWidth + kendo.support.scrollbar());
|
|
that._listWidth = listWidth;
|
|
}
|
|
});
|
|
},
|
|
options: {
|
|
name: 'SelectBox',
|
|
index: -1
|
|
},
|
|
_initSelectOverlay: function () {
|
|
var selectBox = this;
|
|
var value = selectBox.value();
|
|
var view = this.dataSource.view();
|
|
var item;
|
|
var html = '';
|
|
var encode = kendo.htmlEncode;
|
|
for (var i = 0; i < view.length; i++) {
|
|
item = view[i];
|
|
html += '<option value=\'' + encode(item.value) + '\'';
|
|
if (item.value == value) {
|
|
html += ' selected';
|
|
}
|
|
html += '>' + encode(item.text) + '</option>';
|
|
}
|
|
var select = $('<select class=\'k-select-overlay\'>' + html + '</select>');
|
|
var wrapper = $(this.element).closest('.k-widget');
|
|
wrapper.next('.k-select-overlay').remove();
|
|
select.insertAfter(wrapper);
|
|
select.on('change', function () {
|
|
selectBox.value(this.value);
|
|
selectBox.trigger('change');
|
|
});
|
|
},
|
|
value: function (value) {
|
|
var that = this, result = DropDownList.fn.value.call(that, value);
|
|
if (value === undefined) {
|
|
return result;
|
|
}
|
|
if (!DropDownList.fn.value.call(that)) {
|
|
that.text(that.options.title);
|
|
}
|
|
},
|
|
decorate: function (body) {
|
|
var that = this, dataSource = that.dataSource, items = dataSource.data(), i, tag, className, style;
|
|
if (body) {
|
|
that.list.css('background-color', dom.getEffectiveBackground($(body)));
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
tag = items[i].tag || 'span';
|
|
className = items[i].className;
|
|
style = dom.inlineStyle(body, tag, { className: className });
|
|
style = style.replace(/"/g, '\'');
|
|
items[i].style = style + ';display:inline-block';
|
|
}
|
|
dataSource.trigger('change');
|
|
}
|
|
});
|
|
kendo.ui.plugin(SelectBox);
|
|
kendo.ui.editor.SelectBox = SelectBox;
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/indent', ['editor/components'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, EditorUtils = Editor.EditorUtils, registerTool = EditorUtils.registerTool, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, RangeUtils = Editor.RangeUtils, blockElements = dom.blockElements, BlockFormatFinder = Editor.BlockFormatFinder, BlockFormatter = Editor.BlockFormatter;
|
|
function indent(node, value) {
|
|
var isRtl = $(node).css('direction') == 'rtl', indentDirection = isRtl ? 'Right' : 'Left', property = dom.name(node) != 'td' ? 'margin' + indentDirection : 'padding' + indentDirection;
|
|
if (value === undefined) {
|
|
return node.style[property] || 0;
|
|
} else {
|
|
if (value > 0) {
|
|
node.style[property] = value + 'px';
|
|
} else {
|
|
node.style[property] = '';
|
|
if (!node.style.cssText) {
|
|
node.removeAttribute('style');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var IndentFormatter = Class.extend({
|
|
init: function () {
|
|
this.finder = new BlockFormatFinder([{ tags: dom.blockElements }]);
|
|
},
|
|
apply: function (nodes) {
|
|
var formatNodes = this.finder.findSuitable(nodes), targets = [], i, len, formatNode, parentList, sibling;
|
|
if (formatNodes.length) {
|
|
for (i = 0, len = formatNodes.length; i < len; i++) {
|
|
if (dom.is(formatNodes[i], 'li')) {
|
|
if (!$(formatNodes[i]).index()) {
|
|
targets.push(formatNodes[i].parentNode);
|
|
} else if ($.inArray(formatNodes[i].parentNode, targets) < 0) {
|
|
targets.push(formatNodes[i]);
|
|
}
|
|
} else {
|
|
targets.push(formatNodes[i]);
|
|
}
|
|
}
|
|
while (targets.length) {
|
|
formatNode = targets.shift();
|
|
if (dom.is(formatNode, 'li')) {
|
|
parentList = formatNode.parentNode;
|
|
sibling = $(formatNode).prev('li');
|
|
var siblingList = sibling.find('ul,ol').last();
|
|
var nestedList = $(formatNode).children('ul,ol')[0];
|
|
if (nestedList && sibling[0]) {
|
|
if (siblingList[0]) {
|
|
siblingList.append(formatNode);
|
|
siblingList.append($(nestedList).children());
|
|
dom.remove(nestedList);
|
|
} else {
|
|
sibling.append(nestedList);
|
|
nestedList.insertBefore(formatNode, nestedList.firstChild);
|
|
}
|
|
} else {
|
|
nestedList = sibling.children('ul,ol')[0];
|
|
if (!nestedList) {
|
|
nestedList = dom.create(formatNode.ownerDocument, dom.name(parentList));
|
|
sibling.append(nestedList);
|
|
}
|
|
while (formatNode && formatNode.parentNode == parentList) {
|
|
nestedList.appendChild(formatNode);
|
|
formatNode = targets.shift();
|
|
}
|
|
}
|
|
} else {
|
|
var marginLeft = parseInt(indent(formatNode), 10) + 30;
|
|
indent(formatNode, marginLeft);
|
|
for (var targetIndex = 0; targetIndex < targets.length; targetIndex++) {
|
|
if ($.contains(formatNode, targets[targetIndex])) {
|
|
targets.splice(targetIndex, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var formatter = new BlockFormatter([{ tags: ['p'] }], { style: { marginLeft: 30 } });
|
|
formatter.apply(nodes);
|
|
}
|
|
},
|
|
remove: function (nodes) {
|
|
var formatNodes = this.finder.findSuitable(nodes), targetNode, i, len, list, listParent, siblings, formatNode, marginLeft;
|
|
for (i = 0, len = formatNodes.length; i < len; i++) {
|
|
formatNode = $(formatNodes[i]);
|
|
if (formatNode.is('li')) {
|
|
list = formatNode.parent();
|
|
listParent = list.parent();
|
|
if (listParent.is('li,ul,ol') && !indent(list[0])) {
|
|
if (targetNode && $.contains(targetNode, listParent[0])) {
|
|
continue;
|
|
}
|
|
siblings = formatNode.nextAll('li');
|
|
if (siblings.length) {
|
|
$(list[0].cloneNode(false)).appendTo(formatNode).append(siblings);
|
|
}
|
|
if (listParent.is('li')) {
|
|
formatNode.insertAfter(listParent);
|
|
} else {
|
|
formatNode.appendTo(listParent);
|
|
}
|
|
if (!list.children('li').length) {
|
|
list.remove();
|
|
}
|
|
continue;
|
|
} else {
|
|
if (targetNode == list[0]) {
|
|
continue;
|
|
}
|
|
targetNode = list[0];
|
|
}
|
|
} else {
|
|
targetNode = formatNodes[i];
|
|
}
|
|
marginLeft = parseInt(indent(targetNode), 10) - 30;
|
|
indent(targetNode, marginLeft);
|
|
}
|
|
}
|
|
});
|
|
var IndentCommand = Command.extend({
|
|
init: function (options) {
|
|
options.formatter = {
|
|
toggle: function (range) {
|
|
new IndentFormatter().apply(RangeUtils.nodes(range));
|
|
}
|
|
};
|
|
Command.fn.init.call(this, options);
|
|
}
|
|
});
|
|
var OutdentCommand = Command.extend({
|
|
init: function (options) {
|
|
options.formatter = {
|
|
toggle: function (range) {
|
|
new IndentFormatter().remove(RangeUtils.nodes(range));
|
|
}
|
|
};
|
|
Command.fn.init.call(this, options);
|
|
}
|
|
});
|
|
var OutdentTool = Tool.extend({
|
|
init: function (options) {
|
|
Tool.fn.init.call(this, options);
|
|
this.finder = new BlockFormatFinder([{ tags: blockElements }]);
|
|
},
|
|
initialize: function (ui, options) {
|
|
Tool.fn.initialize.call(this, ui, options);
|
|
ui.addClass('k-state-disabled');
|
|
},
|
|
update: function (ui, nodes) {
|
|
var suitable = this.finder.findSuitable(nodes), isOutdentable, listParentsCount, i, len;
|
|
for (i = 0, len = suitable.length; i < len; i++) {
|
|
isOutdentable = indent(suitable[i]);
|
|
if (!isOutdentable) {
|
|
listParentsCount = $(suitable[i]).parents('ul,ol').length;
|
|
isOutdentable = dom.is(suitable[i], 'li') && (listParentsCount > 1 || indent(suitable[i].parentNode)) || dom.ofType(suitable[i], [
|
|
'ul',
|
|
'ol'
|
|
]) && listParentsCount > 0;
|
|
}
|
|
if (isOutdentable) {
|
|
ui.removeClass('k-state-disabled');
|
|
return;
|
|
}
|
|
}
|
|
ui.addClass('k-state-disabled').removeClass('k-state-hover');
|
|
}
|
|
});
|
|
extend(Editor, {
|
|
IndentFormatter: IndentFormatter,
|
|
IndentCommand: IndentCommand,
|
|
OutdentCommand: OutdentCommand,
|
|
OutdentTool: OutdentTool
|
|
});
|
|
registerTool('indent', new Tool({
|
|
command: IndentCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Indent'
|
|
})
|
|
}));
|
|
registerTool('outdent', new OutdentTool({
|
|
command: OutdentCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Outdent'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/viewhtml', ['editor/indent'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, EditorUtils = Editor.EditorUtils, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate;
|
|
var ViewHtmlCommand = Command.extend({
|
|
init: function (options) {
|
|
var cmd = this;
|
|
cmd.options = options;
|
|
Command.fn.init.call(cmd, options);
|
|
cmd.attributes = null;
|
|
cmd.async = true;
|
|
},
|
|
exec: function () {
|
|
var that = this, editor = that.editor, messages = editor.options.messages, dialog = $(kendo.template(ViewHtmlCommand.template)(messages)).appendTo(document.body), content = ViewHtmlCommand.indent(editor.value()), textarea = '.k-editor-textarea';
|
|
function apply(e) {
|
|
editor.value(dialog.find(textarea).val());
|
|
close(e);
|
|
if (that.change) {
|
|
that.change();
|
|
}
|
|
editor.trigger('change');
|
|
}
|
|
function close(e) {
|
|
e.preventDefault();
|
|
dialog.data('kendoWindow').destroy();
|
|
editor.focus();
|
|
}
|
|
this.createDialog(dialog, {
|
|
title: messages.viewHtml,
|
|
close: close,
|
|
visible: false
|
|
}).find(textarea).val(content).end().find('.k-dialog-update').click(apply).end().find('.k-dialog-close').click(close).end().data('kendoWindow').center().open();
|
|
dialog.find(textarea).focus();
|
|
}
|
|
});
|
|
extend(ViewHtmlCommand, {
|
|
template: '<div class=\'k-editor-dialog k-popup-edit-form k-edit-form-container k-viewhtml-dialog\'>' + '<textarea class=\'k-editor-textarea k-input\'></textarea>' + '<div class=\'k-edit-buttons k-state-default\'>' + '<button class=\'k-dialog-update k-button k-primary\'>#: dialogUpdate #</button>' + '<button class=\'k-dialog-close k-button\'>#: dialogCancel #</button>' + '</div>' + '</div>',
|
|
indent: function (content) {
|
|
return content.replace(/<\/(p|li|ul|ol|h[1-6]|table|tr|td|th)>/gi, '</$1>\n').replace(/<(ul|ol)([^>]*)><li/gi, '<$1$2>\n<li').replace(/<br \/>/gi, '<br />\n').replace(/\n$/, '');
|
|
}
|
|
});
|
|
kendo.ui.editor.ViewHtmlCommand = ViewHtmlCommand;
|
|
Editor.EditorUtils.registerTool('viewHtml', new Tool({
|
|
command: ViewHtmlCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'View HTML'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/formatting', ['editor/viewhtml'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo, Editor = kendo.ui.editor, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, DelayedExecutionTool = Editor.DelayedExecutionTool, Command = Editor.Command, dom = Editor.Dom, EditorUtils = Editor.EditorUtils, RangeUtils = Editor.RangeUtils, registerTool = EditorUtils.registerTool;
|
|
var FormattingTool = DelayedExecutionTool.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
Tool.fn.init.call(that, kendo.deepExtend({}, that.options, options));
|
|
that.type = 'kendoSelectBox';
|
|
that.finder = {
|
|
getFormat: function () {
|
|
return '';
|
|
}
|
|
};
|
|
},
|
|
options: {
|
|
items: [
|
|
{
|
|
text: 'Paragraph',
|
|
value: 'p'
|
|
},
|
|
{
|
|
text: 'Quotation',
|
|
value: 'blockquote'
|
|
},
|
|
{
|
|
text: 'Heading 1',
|
|
value: 'h1'
|
|
},
|
|
{
|
|
text: 'Heading 2',
|
|
value: 'h2'
|
|
},
|
|
{
|
|
text: 'Heading 3',
|
|
value: 'h3'
|
|
},
|
|
{
|
|
text: 'Heading 4',
|
|
value: 'h4'
|
|
},
|
|
{
|
|
text: 'Heading 5',
|
|
value: 'h5'
|
|
},
|
|
{
|
|
text: 'Heading 6',
|
|
value: 'h6'
|
|
}
|
|
],
|
|
width: 110
|
|
},
|
|
toFormattingItem: function (item) {
|
|
var value = item.value;
|
|
if (!value) {
|
|
return item;
|
|
}
|
|
if (item.tag || item.className) {
|
|
return item;
|
|
}
|
|
var dot = value.indexOf('.');
|
|
if (dot === 0) {
|
|
item.className = value.substring(1);
|
|
} else if (dot == -1) {
|
|
item.tag = value;
|
|
} else {
|
|
item.tag = value.substring(0, dot);
|
|
item.className = value.substring(dot + 1);
|
|
}
|
|
return item;
|
|
},
|
|
command: function (args) {
|
|
var item = args.value;
|
|
item = this.toFormattingItem(item);
|
|
return new Editor.FormatCommand({
|
|
range: args.range,
|
|
formatter: function () {
|
|
var formatter, tags = (item.tag || item.context || 'span').split(','), format = [{
|
|
tags: tags,
|
|
attr: { className: item.className || '' }
|
|
}];
|
|
if ($.inArray(tags[0], dom.inlineElements) >= 0) {
|
|
formatter = new Editor.GreedyInlineFormatter(format);
|
|
} else {
|
|
formatter = new Editor.GreedyBlockFormatter(format);
|
|
}
|
|
return formatter;
|
|
}
|
|
});
|
|
},
|
|
initialize: function (ui, initOptions) {
|
|
var editor = initOptions.editor;
|
|
var options = this.options;
|
|
var toolName = options.name;
|
|
var that = this;
|
|
ui.width(options.width);
|
|
ui.kendoSelectBox({
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: options.items || editor.options[toolName],
|
|
title: editor.options.messages[toolName],
|
|
autoSize: true,
|
|
change: function () {
|
|
var dataItem = this.dataItem();
|
|
if (dataItem) {
|
|
Tool.exec(editor, toolName, dataItem.toJSON());
|
|
}
|
|
},
|
|
dataBound: function () {
|
|
var i, items = this.dataSource.data();
|
|
for (i = 0; i < items.length; i++) {
|
|
items[i] = that.toFormattingItem(items[i]);
|
|
}
|
|
},
|
|
highlightFirst: false,
|
|
template: kendo.template('<span unselectable="on" style="display:block;#=(data.style||"")#">#:data.text#</span>')
|
|
});
|
|
ui.addClass('k-decorated').closest('.k-widget').removeClass('k-' + toolName).find('*').addBack().attr('unselectable', 'on');
|
|
},
|
|
getFormattingValue: function (items, nodes) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
var item = items[i];
|
|
var tag = item.tag || item.context || '';
|
|
var className = item.className ? '.' + item.className : '';
|
|
var selector = tag + className;
|
|
var element = $(nodes[0]).closest(selector)[0];
|
|
if (!element) {
|
|
continue;
|
|
}
|
|
if (nodes.length == 1) {
|
|
return item.value;
|
|
}
|
|
for (var n = 1; n < nodes.length; n++) {
|
|
if (!$(nodes[n]).closest(selector)[0]) {
|
|
break;
|
|
} else if (n == nodes.length - 1) {
|
|
return item.value;
|
|
}
|
|
}
|
|
}
|
|
return '';
|
|
},
|
|
update: function (ui, nodes) {
|
|
var selectBox = $(ui).data(this.type);
|
|
if (!selectBox) {
|
|
return;
|
|
}
|
|
var dataSource = selectBox.dataSource, items = dataSource.data(), i, context, ancestor = dom.commonAncestor.apply(null, nodes);
|
|
if (ancestor != dom.closestEditable(ancestor) && this._ancestor == ancestor) {
|
|
return;
|
|
} else {
|
|
this._ancestor = ancestor;
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
context = items[i].context;
|
|
items[i].visible = !context || !!$(ancestor).closest(context).length;
|
|
}
|
|
dataSource.filter([{
|
|
field: 'visible',
|
|
operator: 'eq',
|
|
value: true
|
|
}]);
|
|
DelayedExecutionTool.fn.update.call(this, ui, nodes);
|
|
selectBox.value(this.getFormattingValue(dataSource.view(), nodes));
|
|
selectBox.wrapper.toggleClass('k-state-disabled', !dataSource.view().length);
|
|
},
|
|
destroy: function () {
|
|
this._ancestor = null;
|
|
}
|
|
});
|
|
var CleanFormatCommand = Command.extend({
|
|
exec: function () {
|
|
var range = this.lockRange(true);
|
|
this.tagsToClean = this.options.remove || 'strong,em,span,sup,sub,del,b,i,u,font'.split(',');
|
|
RangeUtils.wrapSelectedElements(range);
|
|
var nodes = RangeUtils.mapAll(range, function (node) {
|
|
return node;
|
|
});
|
|
for (var c = nodes.length - 1; c >= 0; c--) {
|
|
this.clean(nodes[c]);
|
|
}
|
|
this.releaseRange(range);
|
|
},
|
|
clean: function (node) {
|
|
if (!node || dom.isMarker(node)) {
|
|
return;
|
|
}
|
|
var name = dom.name(node);
|
|
if (name == 'ul' || name == 'ol') {
|
|
var listFormatter = new Editor.ListFormatter(name);
|
|
var prev = node.previousSibling;
|
|
var next = node.nextSibling;
|
|
listFormatter.unwrap(node);
|
|
for (; prev && prev != next; prev = prev.nextSibling) {
|
|
this.clean(prev);
|
|
}
|
|
} else if (name == 'blockquote') {
|
|
dom.changeTag(node, 'p');
|
|
} else if (node.nodeType == 1 && !dom.insignificant(node)) {
|
|
for (var i = node.childNodes.length - 1; i >= 0; i--) {
|
|
this.clean(node.childNodes[i]);
|
|
}
|
|
node.removeAttribute('style');
|
|
node.removeAttribute('class');
|
|
} else {
|
|
unwrapListItem(node);
|
|
}
|
|
if ($.inArray(name, this.tagsToClean) > -1) {
|
|
dom.unwrap(node);
|
|
}
|
|
}
|
|
});
|
|
function unwrapListItem(node) {
|
|
var li = dom.closestEditableOfType(node, ['li']);
|
|
if (li) {
|
|
var listFormatter = new Editor.ListFormatter(dom.name(li.parentNode));
|
|
var range = kendo.ui.editor.W3CRange.fromNode(node);
|
|
range.selectNode(li);
|
|
listFormatter.toggle(range);
|
|
}
|
|
}
|
|
$.extend(Editor, {
|
|
FormattingTool: FormattingTool,
|
|
CleanFormatCommand: CleanFormatCommand
|
|
});
|
|
registerTool('formatting', new FormattingTool({
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.dropDownListTemplate,
|
|
title: 'Format'
|
|
})
|
|
}));
|
|
registerTool('cleanFormatting', new Tool({
|
|
command: CleanFormatCommand,
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Clean formatting'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/toolbar', ['editor/formatting'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var ui = kendo.ui;
|
|
var editorNS = ui.editor;
|
|
var Widget = ui.Widget;
|
|
var extend = $.extend;
|
|
var proxy = $.proxy;
|
|
var keys = kendo.keys;
|
|
var NS = '.kendoEditor';
|
|
var EditorUtils = kendo.ui.editor.EditorUtils;
|
|
var ToolTemplate = kendo.ui.editor.ToolTemplate;
|
|
var Tool = kendo.ui.editor.Tool;
|
|
var OVERFLOWANCHOR = 'overflowAnchor';
|
|
var focusable = '.k-tool-group:visible a.k-tool:not(.k-state-disabled),' + '.k-tool.k-overflow-anchor,' + '.k-tool-group:visible .k-widget.k-colorpicker,' + '.k-tool-group:visible .k-selectbox,' + '.k-tool-group:visible .k-dropdown,' + '.k-tool-group:visible .k-combobox .k-input';
|
|
var OverflowAnchorTool = Tool.extend({
|
|
initialize: function (ui, options) {
|
|
ui.attr({ unselectable: 'on' });
|
|
var toolbar = options.editor.toolbar;
|
|
ui.on('click', $.proxy(function () {
|
|
this.overflowPopup.toggle();
|
|
}, toolbar));
|
|
},
|
|
options: { name: OVERFLOWANCHOR },
|
|
command: $.noop,
|
|
update: $.noop,
|
|
destroy: $.noop
|
|
});
|
|
EditorUtils.registerTool(OVERFLOWANCHOR, new OverflowAnchorTool({
|
|
key: '',
|
|
ctrl: true,
|
|
template: new ToolTemplate({ template: EditorUtils.overflowAnchorTemplate })
|
|
}));
|
|
var Toolbar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
options = extend({}, options, { name: 'EditorToolbar' });
|
|
Widget.fn.init.call(that, element, options);
|
|
if (options.popup) {
|
|
that._initPopup();
|
|
}
|
|
if (options.resizable && options.resizable.toolbar) {
|
|
that._resizeHandler = kendo.onResize(function () {
|
|
that.resize();
|
|
});
|
|
that.element.addClass('k-toolbar-resizable');
|
|
}
|
|
},
|
|
events: ['execute'],
|
|
groups: {
|
|
basic: [
|
|
'bold',
|
|
'italic',
|
|
'underline',
|
|
'strikethrough'
|
|
],
|
|
scripts: [
|
|
'subscript',
|
|
'superscript'
|
|
],
|
|
alignment: [
|
|
'justifyLeft',
|
|
'justifyCenter',
|
|
'justifyRight',
|
|
'justifyFull'
|
|
],
|
|
links: [
|
|
'insertImage',
|
|
'insertFile',
|
|
'createLink',
|
|
'unlink'
|
|
],
|
|
lists: [
|
|
'insertUnorderedList',
|
|
'insertOrderedList',
|
|
'indent',
|
|
'outdent'
|
|
],
|
|
tables: [
|
|
'createTable',
|
|
'addColumnLeft',
|
|
'addColumnRight',
|
|
'addRowAbove',
|
|
'addRowBelow',
|
|
'deleteRow',
|
|
'deleteColumn'
|
|
],
|
|
advanced: [
|
|
'viewHtml',
|
|
'cleanFormatting',
|
|
'print',
|
|
'pdf'
|
|
],
|
|
fonts: [
|
|
'fontName',
|
|
'fontSize'
|
|
],
|
|
colors: [
|
|
'foreColor',
|
|
'backColor'
|
|
]
|
|
},
|
|
overflowFlaseTools: [
|
|
'formatting',
|
|
'fontName',
|
|
'fontSize',
|
|
'foreColor',
|
|
'backColor',
|
|
'insertHtml'
|
|
],
|
|
_initPopup: function () {
|
|
this.window = $(this.element).wrap('<div class=\'editorToolbarWindow k-header\' />').parent().prepend('<button class=\'k-button k-button-bare k-editortoolbar-dragHandle\'><span class=\'k-icon k-i-move\' /></button>').kendoWindow({
|
|
title: false,
|
|
resizable: false,
|
|
draggable: { dragHandle: '.k-editortoolbar-dragHandle' },
|
|
animation: {
|
|
open: { effects: 'fade:in' },
|
|
close: { effects: 'fade:out' }
|
|
},
|
|
minHeight: 42,
|
|
visible: false,
|
|
autoFocus: false,
|
|
actions: [],
|
|
dragend: function () {
|
|
this._moved = true;
|
|
}
|
|
}).on('mousedown', function (e) {
|
|
if (!$(e.target).is('.k-icon')) {
|
|
e.preventDefault();
|
|
}
|
|
}).data('kendoWindow');
|
|
},
|
|
_toggleOverflowStyles: function (element, show) {
|
|
element.find('li').toggleClass('k-item k-state-default', show).find('.k-tool:not(.k-state-disabled),.k-overflow-button').toggleClass('k-overflow-button k-button', show);
|
|
},
|
|
_initOverflowPopup: function (ui) {
|
|
var that = this;
|
|
var popupTemplate = '<ul class=\'k-editor-overflow-popup k-overflow-container k-list-container\'></ul>';
|
|
that.overflowPopup = $(popupTemplate).appendTo('body').kendoPopup({
|
|
anchor: ui,
|
|
origin: 'bottom right',
|
|
position: 'top right',
|
|
copyAnchorStyles: false,
|
|
open: function (e) {
|
|
if (this.element.is(':empty')) {
|
|
e.preventDefault();
|
|
}
|
|
that._toggleOverflowStyles(this.element, true);
|
|
},
|
|
activate: proxy(that.focusOverflowPopup, that)
|
|
}).data('kendoPopup');
|
|
},
|
|
items: function () {
|
|
var isResizable = this.options.resizable && this.options.resizable.toolbar, popup, result;
|
|
result = this.element.children().find('> *, select');
|
|
if (isResizable) {
|
|
popup = this.overflowPopup;
|
|
result = result.add(popup.element.children().find('> *'));
|
|
}
|
|
return result;
|
|
},
|
|
focused: function () {
|
|
return this.element.find('.k-state-focused').length > 0;
|
|
},
|
|
toolById: function (name) {
|
|
var id, tools = this.tools;
|
|
for (id in tools) {
|
|
if (id.toLowerCase() == name) {
|
|
return tools[id];
|
|
}
|
|
}
|
|
},
|
|
toolGroupFor: function (toolName) {
|
|
var i, groups = this.groups;
|
|
if (this.isCustomTool(toolName)) {
|
|
return 'custom';
|
|
}
|
|
for (i in groups) {
|
|
if ($.inArray(toolName, groups[i]) >= 0) {
|
|
return i;
|
|
}
|
|
}
|
|
},
|
|
bindTo: function (editor) {
|
|
var that = this, window = that.window;
|
|
if (that._editor) {
|
|
that._editor.unbind('select', proxy(that.resize, that));
|
|
}
|
|
that._editor = editor;
|
|
if (that.options.resizable && that.options.resizable.toolbar) {
|
|
editor.options.tools.push(OVERFLOWANCHOR);
|
|
}
|
|
that.tools = that.expandTools(editor.options.tools);
|
|
that.render();
|
|
that.element.find('.k-combobox .k-input').keydown(function (e) {
|
|
var combobox = $(this).closest('.k-combobox').data('kendoComboBox'), key = e.keyCode;
|
|
if (key == keys.RIGHT || key == keys.LEFT) {
|
|
combobox.close();
|
|
} else if (key == keys.DOWN) {
|
|
if (!combobox.dropDown.isOpened()) {
|
|
e.stopImmediatePropagation();
|
|
combobox.open();
|
|
}
|
|
}
|
|
});
|
|
that._attachEvents();
|
|
that.items().each(function initializeTool() {
|
|
var toolName = that._toolName(this), tool = toolName !== 'more' ? that.tools[toolName] : that.tools.overflowAnchor, options = tool && tool.options, messages = editor.options.messages, description = options && options.tooltip || messages[toolName], ui = $(this);
|
|
if (!tool || !tool.initialize) {
|
|
return;
|
|
}
|
|
if (toolName == 'fontSize' || toolName == 'fontName') {
|
|
var inheritText = messages[toolName + 'Inherit'];
|
|
ui.find('input').val(inheritText).end().find('span.k-input').text(inheritText).end();
|
|
}
|
|
tool.initialize(ui, {
|
|
title: that._appendShortcutSequence(description, tool),
|
|
editor: that._editor
|
|
});
|
|
ui.closest('.k-widget', that.element).addClass('k-editor-widget');
|
|
ui.closest('.k-colorpicker', that.element).next('.k-colorpicker').addClass('k-editor-widget');
|
|
});
|
|
editor.bind('select', proxy(that.resize, that));
|
|
that.update();
|
|
if (window) {
|
|
window.wrapper.css({
|
|
top: '',
|
|
left: '',
|
|
width: ''
|
|
});
|
|
}
|
|
},
|
|
show: function () {
|
|
var that = this, window = that.window, editorOptions = that.options.editor, wrapper, editorElement, editorOffset;
|
|
if (window) {
|
|
wrapper = window.wrapper;
|
|
editorElement = editorOptions.element;
|
|
if (!wrapper.is(':visible') || !that.window.options.visible) {
|
|
if (!wrapper[0].style.width) {
|
|
wrapper.width(editorElement.outerWidth() - parseInt(wrapper.css('border-left-width'), 10) - parseInt(wrapper.css('border-right-width'), 10));
|
|
}
|
|
if (!window._moved) {
|
|
editorOffset = editorElement.offset();
|
|
wrapper.css({
|
|
top: Math.max(0, parseInt(editorOffset.top, 10) - wrapper.outerHeight() - parseInt(that.window.element.css('padding-bottom'), 10)),
|
|
left: Math.max(0, parseInt(editorOffset.left, 10))
|
|
});
|
|
}
|
|
window.open();
|
|
}
|
|
}
|
|
},
|
|
hide: function () {
|
|
if (this.window) {
|
|
this.window.close();
|
|
}
|
|
},
|
|
focus: function () {
|
|
var TABINDEX = 'tabIndex';
|
|
var element = this.element;
|
|
var tabIndex = this._editor.element.attr(TABINDEX);
|
|
element.attr(TABINDEX, tabIndex || 0).focus().find(focusable).first().focus();
|
|
if (!tabIndex && tabIndex !== 0) {
|
|
element.removeAttr(TABINDEX);
|
|
}
|
|
},
|
|
focusOverflowPopup: function () {
|
|
var TABINDEX = 'tabIndex';
|
|
var element = this.overflowPopup.element;
|
|
var tabIndex = this._editor.element.attr(TABINDEX);
|
|
element.closest('.k-animation-container').addClass('k-overflow-wrapper');
|
|
element.attr(TABINDEX, tabIndex || 0).find(focusable).first().focus();
|
|
if (!tabIndex && tabIndex !== 0) {
|
|
element.removeAttr(TABINDEX);
|
|
}
|
|
},
|
|
_appendShortcutSequence: function (localizedText, tool) {
|
|
if (!tool.key) {
|
|
return localizedText;
|
|
}
|
|
var res = localizedText + ' (';
|
|
if (tool.ctrl) {
|
|
res += 'Ctrl + ';
|
|
}
|
|
if (tool.shift) {
|
|
res += 'Shift + ';
|
|
}
|
|
if (tool.alt) {
|
|
res += 'Alt + ';
|
|
}
|
|
res += tool.key + ')';
|
|
return res;
|
|
},
|
|
_nativeTools: [
|
|
'insertLineBreak',
|
|
'insertParagraph',
|
|
'redo',
|
|
'undo'
|
|
],
|
|
tools: {},
|
|
isCustomTool: function (toolName) {
|
|
return !(toolName in kendo.ui.Editor.defaultTools);
|
|
},
|
|
expandTools: function (tools) {
|
|
var currentTool, i, nativeTools = this._nativeTools, options, defaultTools = kendo.deepExtend({}, kendo.ui.Editor.defaultTools), result = {}, name;
|
|
for (i = 0; i < tools.length; i++) {
|
|
currentTool = tools[i];
|
|
name = currentTool.name;
|
|
if ($.isPlainObject(currentTool)) {
|
|
if (name && defaultTools[name]) {
|
|
result[name] = extend({}, defaultTools[name]);
|
|
extend(result[name].options, currentTool);
|
|
} else {
|
|
options = extend({
|
|
cssClass: 'k-i-custom',
|
|
type: 'button',
|
|
title: ''
|
|
}, currentTool);
|
|
if (!options.name) {
|
|
options.name = 'custom';
|
|
}
|
|
options.cssClass = 'k-' + (options.name == 'custom' ? 'i-custom' : options.name);
|
|
if (!options.template && options.type == 'button') {
|
|
options.template = editorNS.EditorUtils.buttonTemplate;
|
|
options.title = options.title || options.tooltip;
|
|
}
|
|
result[name] = { options: options };
|
|
}
|
|
} else if (defaultTools[currentTool]) {
|
|
result[currentTool] = defaultTools[currentTool];
|
|
}
|
|
}
|
|
for (i = 0; i < nativeTools.length; i++) {
|
|
if (!result[nativeTools[i]]) {
|
|
result[nativeTools[i]] = defaultTools[nativeTools[i]];
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
render: function () {
|
|
var that = this, tools = that.tools, options, template, toolElement, toolName, editorElement = that._editor.element, element = that.element.empty(), groupName, newGroupName, toolConfig = that._editor.options.tools, browser = kendo.support.browser, group, i, groupPosition = 0, resizable = that.options.resizable && that.options.resizable.toolbar, overflowFlaseTools = this.overflowFlaseTools;
|
|
function stringify(template) {
|
|
var result;
|
|
if (template.getHtml) {
|
|
result = template.getHtml();
|
|
} else {
|
|
if (!$.isFunction(template)) {
|
|
template = kendo.template(template);
|
|
}
|
|
result = template(options);
|
|
}
|
|
return $.trim(result);
|
|
}
|
|
function endGroup() {
|
|
if (group.children().length) {
|
|
if (resizable) {
|
|
group.data('position', groupPosition);
|
|
groupPosition++;
|
|
}
|
|
group.appendTo(element);
|
|
}
|
|
}
|
|
function startGroup(toolName) {
|
|
if (toolName !== OVERFLOWANCHOR) {
|
|
group = $('<li class=\'k-tool-group\' role=\'presentation\' />');
|
|
group.data('overflow', $.inArray(toolName, overflowFlaseTools) === -1 ? true : false);
|
|
} else {
|
|
group = $('<li class=\'k-overflow-tools\' />');
|
|
}
|
|
}
|
|
element.empty();
|
|
if (toolConfig.length) {
|
|
toolName = toolConfig[0].name || toolConfig[0];
|
|
}
|
|
startGroup(toolName, overflowFlaseTools);
|
|
for (i = 0; i < toolConfig.length; i++) {
|
|
toolName = toolConfig[i].name || toolConfig[i];
|
|
options = tools[toolName] && tools[toolName].options;
|
|
if (!options && $.isPlainObject(toolName)) {
|
|
options = toolName;
|
|
}
|
|
template = options && options.template;
|
|
if (toolName == 'break') {
|
|
endGroup();
|
|
$('<li class=\'k-row-break\' />').appendTo(that.element);
|
|
startGroup(toolName, overflowFlaseTools);
|
|
}
|
|
if (!template) {
|
|
continue;
|
|
}
|
|
newGroupName = that.toolGroupFor(toolName);
|
|
if (groupName != newGroupName || toolName == OVERFLOWANCHOR) {
|
|
endGroup();
|
|
startGroup(toolName, overflowFlaseTools);
|
|
groupName = newGroupName;
|
|
}
|
|
template = stringify(template);
|
|
toolElement = $(template).appendTo(group);
|
|
if (newGroupName == 'custom') {
|
|
endGroup();
|
|
startGroup(toolName, overflowFlaseTools);
|
|
}
|
|
if (options.exec && toolElement.hasClass('k-tool')) {
|
|
toolElement.click(proxy(options.exec, editorElement[0]));
|
|
}
|
|
}
|
|
endGroup();
|
|
$(that.element).children(':has(> .k-tool)').addClass('k-button-group');
|
|
if (that.options.popup && browser.msie && browser.version < 9) {
|
|
that.window.wrapper.find('*').attr('unselectable', 'on');
|
|
}
|
|
that.updateGroups();
|
|
if (resizable) {
|
|
that._initOverflowPopup(that.element.find('.k-overflow-anchor'));
|
|
}
|
|
that.angular('compile', function () {
|
|
return { elements: that.element };
|
|
});
|
|
},
|
|
updateGroups: function () {
|
|
$(this.element).children().each(function () {
|
|
$(this).children().filter(function () {
|
|
return !$(this).hasClass('k-state-disabled');
|
|
}).removeClass('k-group-end').first().addClass('k-group-start').end().last().addClass('k-group-end').end();
|
|
});
|
|
},
|
|
decorateFrom: function (body) {
|
|
this.items().filter('.k-decorated').each(function () {
|
|
var selectBox = $(this).data('kendoSelectBox');
|
|
if (selectBox) {
|
|
selectBox.decorate(body);
|
|
}
|
|
});
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
var id, tools = this.tools;
|
|
for (id in tools) {
|
|
if (tools[id].destroy) {
|
|
tools[id].destroy();
|
|
}
|
|
}
|
|
if (this.window) {
|
|
this.window.destroy();
|
|
}
|
|
if (this._resizeHandler) {
|
|
kendo.unbindResize(this._resizeHandler);
|
|
}
|
|
if (this.overflowPopup) {
|
|
this.overflowPopup.destroy();
|
|
}
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this, buttons = '[role=button].k-tool', enabledButtons = buttons + ':not(.k-state-disabled)', disabledButtons = buttons + '.k-state-disabled', popupElement = that.overflowPopup ? that.overflowPopup.element : $([]);
|
|
that.element.add(popupElement).off(NS).on('mouseenter' + NS, enabledButtons, function () {
|
|
$(this).addClass('k-state-hover');
|
|
}).on('mouseleave' + NS, enabledButtons, function () {
|
|
$(this).removeClass('k-state-hover');
|
|
}).on('mousedown' + NS, buttons, function (e) {
|
|
e.preventDefault();
|
|
}).on('keydown' + NS, focusable, function (e) {
|
|
var current = this;
|
|
var resizable = that.options.resizable && that.options.resizable.toolbar;
|
|
var focusElement, currentContainer, keyCode = e.keyCode;
|
|
function move(direction, container, constrain) {
|
|
var tools = container.find(focusable);
|
|
var index = tools.index(current) + direction;
|
|
if (constrain) {
|
|
index = Math.max(0, Math.min(tools.length - 1, index));
|
|
}
|
|
return tools[index];
|
|
}
|
|
if (keyCode == keys.RIGHT || keyCode == keys.LEFT) {
|
|
if (!$(current).hasClass('.k-dropdown')) {
|
|
focusElement = move(keyCode == keys.RIGHT ? 1 : -1, that.element, true);
|
|
}
|
|
} else if (resizable && (keyCode == keys.UP || keyCode == keys.DOWN)) {
|
|
focusElement = move(keyCode == keys.DOWN ? 1 : -1, that.overflowPopup.element, true);
|
|
} else if (keyCode == keys.ESC) {
|
|
if (that.overflowPopup.visible()) {
|
|
that.overflowPopup.close();
|
|
}
|
|
focusElement = that._editor;
|
|
} else if (keyCode == keys.TAB && !(e.ctrlKey || e.altKey)) {
|
|
if (resizable) {
|
|
currentContainer = $(current.parentElement).hasClass('k-overflow-tool-group') ? that.overflowPopup.element : that.element;
|
|
} else {
|
|
currentContainer = that.element;
|
|
}
|
|
if (e.shiftKey) {
|
|
focusElement = move(-1, currentContainer);
|
|
} else {
|
|
focusElement = move(1, currentContainer);
|
|
if (!focusElement) {
|
|
focusElement = that._editor;
|
|
}
|
|
}
|
|
}
|
|
if (focusElement) {
|
|
e.preventDefault();
|
|
focusElement.focus();
|
|
}
|
|
}).on('click' + NS, enabledButtons, function (e) {
|
|
var button = $(this);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
button.removeClass('k-state-hover');
|
|
if (!button.is('[data-popup]')) {
|
|
that._editor.exec(that._toolName(this));
|
|
}
|
|
}).on('click' + NS, disabledButtons, function (e) {
|
|
e.preventDefault();
|
|
});
|
|
},
|
|
_toolName: function (element) {
|
|
if (!element) {
|
|
return;
|
|
}
|
|
var className = element.className;
|
|
if (/k-tool\b/i.test(className)) {
|
|
className = element.firstChild.className;
|
|
}
|
|
var tool = $.grep(className.split(' '), function (x) {
|
|
return !/^k-(widget|tool|tool-icon|icon|state-hover|header|combobox|dropdown|selectbox|colorpicker)$/i.test(x);
|
|
});
|
|
return tool[0] ? tool[0].substring(tool[0].lastIndexOf('-') + 1) : 'custom';
|
|
},
|
|
refreshTools: function () {
|
|
var that = this, editor = that._editor, range = editor.getRange(), nodes = kendo.ui.editor.RangeUtils.textNodes(range);
|
|
if (!nodes.length) {
|
|
nodes = [range.startContainer];
|
|
}
|
|
that.items().each(function () {
|
|
var tool = that.tools[that._toolName(this)];
|
|
if (tool && tool.update) {
|
|
tool.update($(this), nodes);
|
|
}
|
|
});
|
|
this.update();
|
|
},
|
|
update: function () {
|
|
this.updateGroups();
|
|
},
|
|
_resize: function (e) {
|
|
var containerWidth = e.width;
|
|
var resizable = this.options.resizable && this.options.resizable.toolbar;
|
|
var popup = this.overflowPopup;
|
|
this.refreshTools();
|
|
if (!resizable) {
|
|
return;
|
|
}
|
|
if (popup.visible()) {
|
|
popup.close(true);
|
|
}
|
|
this._refreshWidths();
|
|
this._shrink(containerWidth);
|
|
this._stretch(containerWidth);
|
|
this._toggleOverflowStyles(this.element, false);
|
|
this._toggleOverflowStyles(this.overflowPopup.element, true);
|
|
this.element.children('li.k-overflow-tools').css('visibility', popup.element.is(':empty') ? 'hidden' : 'visible');
|
|
},
|
|
_refreshWidths: function () {
|
|
this.element.children('li').each(function (idx, element) {
|
|
var group = $(element);
|
|
group.data('outerWidth', group.outerWidth(true));
|
|
});
|
|
},
|
|
_shrink: function (width) {
|
|
var group, visibleGroups;
|
|
if (width < this._groupsWidth()) {
|
|
visibleGroups = this._visibleGroups().filter(':not(.k-overflow-tools)');
|
|
for (var i = visibleGroups.length - 1; i >= 0; i--) {
|
|
group = visibleGroups.eq(i);
|
|
if (width > this._groupsWidth()) {
|
|
break;
|
|
} else {
|
|
this._hideGroup(group);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_stretch: function (width) {
|
|
var group, hiddenGroups;
|
|
if (width > this._groupsWidth()) {
|
|
hiddenGroups = this._hiddenGroups();
|
|
for (var i = 0; i < hiddenGroups.length; i++) {
|
|
group = hiddenGroups.eq(i);
|
|
if (width < this._groupsWidth() || !this._showGroup(group, width)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_hiddenGroups: function () {
|
|
var popup = this.overflowPopup;
|
|
var hiddenGroups = this.element.children('li.k-tool-group').filter(':hidden');
|
|
hiddenGroups = hiddenGroups.add(popup.element.children('li'));
|
|
hiddenGroups.sort(function (a, b) {
|
|
return $(a).data('position') > $(b).data('position') ? 1 : -1;
|
|
});
|
|
return hiddenGroups;
|
|
},
|
|
_visibleGroups: function () {
|
|
return this.element.children('li.k-tool-group, li.k-overflow-tools').filter(':visible');
|
|
},
|
|
_groupsWidth: function () {
|
|
var width = 0;
|
|
this._visibleGroups().each(function () {
|
|
width += $(this).data('outerWidth');
|
|
});
|
|
return Math.ceil(width);
|
|
},
|
|
_hideGroup: function (group) {
|
|
if (group.data('overflow')) {
|
|
var popup = this.overflowPopup;
|
|
group.detach().prependTo(popup.element).addClass('k-overflow-tool-group');
|
|
} else {
|
|
group.hide();
|
|
}
|
|
},
|
|
_showGroup: function (group, width) {
|
|
var position, previous;
|
|
if (group.length && width > this._groupsWidth() + group.data('outerWidth')) {
|
|
if (group.hasClass('k-overflow-tool-group')) {
|
|
position = group.data('position');
|
|
if (position === 0) {
|
|
group.detach().prependTo(this.element);
|
|
} else {
|
|
previous = this.element.children().filter(function (idx, element) {
|
|
return $(element).data('position') === position - 1;
|
|
});
|
|
group.detach().insertAfter(previous);
|
|
}
|
|
group.removeClass('k-overflow-tool-group');
|
|
} else {
|
|
group.show();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
$.extend(editorNS, { Toolbar: Toolbar });
|
|
}(window.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('editor/tables', ['editor/toolbar'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, proxy = $.proxy, Editor = kendo.ui.editor, dom = Editor.Dom, EditorUtils = Editor.EditorUtils, RangeUtils = Editor.RangeUtils, Command = Editor.Command, NS = '.kendoEditor', ACTIVESTATE = 'k-state-active', SELECTEDSTATE = 'k-state-selected', Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, InsertHtmlCommand = Editor.InsertHtmlCommand, BlockFormatFinder = Editor.BlockFormatFinder, registerTool = Editor.EditorUtils.registerTool;
|
|
var editableCell = '<td>' + Editor.emptyElementContent + '</td>';
|
|
var tableFormatFinder = new BlockFormatFinder([{ tags: ['table'] }]);
|
|
var TableCommand = InsertHtmlCommand.extend({
|
|
_tableHtml: function (rows, columns) {
|
|
rows = rows || 1;
|
|
columns = columns || 1;
|
|
return '<table class=\'k-table\' data-last>' + new Array(rows + 1).join('<tr>' + new Array(columns + 1).join(editableCell) + '</tr>') + '</table>';
|
|
},
|
|
postProcess: function (editor, range) {
|
|
var insertedTable = $('table[data-last]', editor.document).removeAttr('data-last');
|
|
range.setStart(insertedTable.find('td')[0], 0);
|
|
range.collapse(true);
|
|
editor.selectRange(range);
|
|
},
|
|
exec: function () {
|
|
var options = this.options;
|
|
options.html = this._tableHtml(options.rows, options.columns);
|
|
options.postProcess = this.postProcess;
|
|
InsertHtmlCommand.fn.exec.call(this);
|
|
}
|
|
});
|
|
var PopupTool = Tool.extend({
|
|
initialize: function (ui, options) {
|
|
Tool.fn.initialize.call(this, ui, options);
|
|
var popup = $(this.options.popupTemplate).appendTo('body').kendoPopup({
|
|
anchor: ui,
|
|
copyAnchorStyles: false,
|
|
open: proxy(this._open, this),
|
|
activate: proxy(this._activate, this),
|
|
close: proxy(this._close, this)
|
|
}).data('kendoPopup');
|
|
ui.click(proxy(this._toggle, this)).keydown(proxy(this._keydown, this));
|
|
this._editor = options.editor;
|
|
this._popup = popup;
|
|
},
|
|
popup: function () {
|
|
return this._popup;
|
|
},
|
|
_activate: $.noop,
|
|
_open: function () {
|
|
this._popup.options.anchor.addClass(ACTIVESTATE);
|
|
},
|
|
_close: function () {
|
|
this._popup.options.anchor.removeClass(ACTIVESTATE);
|
|
},
|
|
_keydown: function (e) {
|
|
var keys = kendo.keys;
|
|
var key = e.keyCode;
|
|
if (key == keys.DOWN && e.altKey) {
|
|
this._popup.open();
|
|
} else if (key == keys.ESC) {
|
|
this._popup.close();
|
|
}
|
|
},
|
|
_toggle: function (e) {
|
|
var button = $(e.target).closest('.k-tool');
|
|
if (!button.hasClass('k-state-disabled')) {
|
|
this.popup().toggle();
|
|
}
|
|
},
|
|
update: function (ui) {
|
|
var popup = this.popup();
|
|
if (popup.wrapper && popup.wrapper.css('display') == 'block') {
|
|
popup.close();
|
|
}
|
|
ui.removeClass('k-state-hover');
|
|
},
|
|
destroy: function () {
|
|
this._popup.destroy();
|
|
}
|
|
});
|
|
var InsertTableTool = PopupTool.extend({
|
|
init: function (options) {
|
|
this.cols = 8;
|
|
this.rows = 6;
|
|
PopupTool.fn.init.call(this, $.extend(options, {
|
|
command: TableCommand,
|
|
popupTemplate: '<div class=\'k-ct-popup\'>' + new Array(this.cols * this.rows + 1).join('<span class=\'k-ct-cell k-state-disabled\' />') + '<div class=\'k-status\'></div>' + '</div>'
|
|
}));
|
|
},
|
|
_activate: function () {
|
|
var that = this, element = that._popup.element, cells = element.find('.k-ct-cell'), firstCell = cells.eq(0), lastCell = cells.eq(cells.length - 1), start = kendo.getOffset(firstCell), end = kendo.getOffset(lastCell), cols = that.cols, rows = that.rows, cellWidth, cellHeight;
|
|
element.find('*').addBack().attr('unselectable', 'on');
|
|
end.left += lastCell[0].offsetWidth;
|
|
end.top += lastCell[0].offsetHeight;
|
|
cellWidth = (end.left - start.left) / cols;
|
|
cellHeight = (end.top - start.top) / rows;
|
|
function tableFromLocation(e) {
|
|
var w = $(window);
|
|
return {
|
|
row: Math.floor((e.clientY + w.scrollTop() - start.top) / cellHeight) + 1,
|
|
col: Math.floor((e.clientX + w.scrollLeft() - start.left) / cellWidth) + 1
|
|
};
|
|
}
|
|
element.on('mousemove' + NS, function (e) {
|
|
that._setTableSize(tableFromLocation(e));
|
|
}).on('mouseleave' + NS, function () {
|
|
that._setTableSize();
|
|
}).on('mouseup' + NS, function (e) {
|
|
that._exec(tableFromLocation(e));
|
|
});
|
|
},
|
|
_valid: function (size) {
|
|
return size && size.row > 0 && size.col > 0 && size.row <= this.rows && size.col <= this.cols;
|
|
},
|
|
_exec: function (size) {
|
|
if (this._valid(size)) {
|
|
this._editor.exec('createTable', {
|
|
rows: size.row,
|
|
columns: size.col
|
|
});
|
|
this._popup.close();
|
|
}
|
|
},
|
|
_setTableSize: function (size) {
|
|
var element = this._popup.element;
|
|
var status = element.find('.k-status');
|
|
var cells = element.find('.k-ct-cell');
|
|
var cols = this.cols;
|
|
var messages = this._editor.options.messages;
|
|
if (this._valid(size)) {
|
|
status.text(kendo.format(messages.createTableHint, size.row, size.col));
|
|
cells.each(function (i) {
|
|
$(this).toggleClass(SELECTEDSTATE, i % cols < size.col && i / cols < size.row);
|
|
});
|
|
} else {
|
|
status.text(messages.dialogCancel);
|
|
cells.removeClass(SELECTEDSTATE);
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
PopupTool.fn._keydown.call(this, e);
|
|
if (!this._popup.visible()) {
|
|
return;
|
|
}
|
|
var keys = kendo.keys;
|
|
var key = e.keyCode;
|
|
var cells = this._popup.element.find('.k-ct-cell');
|
|
var focus = Math.max(cells.filter('.k-state-selected').last().index(), 0);
|
|
var selectedRows = Math.floor(focus / this.cols);
|
|
var selectedColumns = focus % this.cols;
|
|
var changed = false;
|
|
if (key == keys.DOWN && !e.altKey) {
|
|
changed = true;
|
|
selectedRows++;
|
|
} else if (key == keys.UP) {
|
|
changed = true;
|
|
selectedRows--;
|
|
} else if (key == keys.RIGHT) {
|
|
changed = true;
|
|
selectedColumns++;
|
|
} else if (key == keys.LEFT) {
|
|
changed = true;
|
|
selectedColumns--;
|
|
}
|
|
var tableSize = {
|
|
row: Math.max(1, Math.min(this.rows, selectedRows + 1)),
|
|
col: Math.max(1, Math.min(this.cols, selectedColumns + 1))
|
|
};
|
|
if (key == keys.ENTER) {
|
|
this._exec(tableSize);
|
|
} else {
|
|
this._setTableSize(tableSize);
|
|
}
|
|
if (changed) {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
}
|
|
},
|
|
_open: function () {
|
|
var messages = this._editor.options.messages;
|
|
PopupTool.fn._open.call(this);
|
|
this.popup().element.find('.k-status').text(messages.dialogCancel).end().find('.k-ct-cell').removeClass(SELECTEDSTATE);
|
|
},
|
|
_close: function () {
|
|
PopupTool.fn._close.call(this);
|
|
this.popup().element.off(NS);
|
|
},
|
|
update: function (ui, nodes) {
|
|
var isFormatted;
|
|
PopupTool.fn.update.call(this, ui);
|
|
isFormatted = tableFormatFinder.isFormatted(nodes);
|
|
ui.toggleClass('k-state-disabled', isFormatted);
|
|
}
|
|
});
|
|
var InsertRowCommand = Command.extend({
|
|
exec: function () {
|
|
var range = this.lockRange(true), td = range.endContainer, cellCount, row, newRow;
|
|
while (dom.name(td) != 'td') {
|
|
td = td.parentNode;
|
|
}
|
|
row = td.parentNode;
|
|
cellCount = row.children.length;
|
|
newRow = row.cloneNode(true);
|
|
for (var i = 0; i < row.cells.length; i++) {
|
|
newRow.cells[i].innerHTML = Editor.emptyElementContent;
|
|
}
|
|
if (this.options.position == 'before') {
|
|
dom.insertBefore(newRow, row);
|
|
} else {
|
|
dom.insertAfter(newRow, row);
|
|
}
|
|
this.releaseRange(range);
|
|
}
|
|
});
|
|
var InsertColumnCommand = Command.extend({
|
|
exec: function () {
|
|
var range = this.lockRange(true), td = dom.closest(range.endContainer, 'td'), table = dom.closest(td, 'table'), columnIndex, i, rows = table.rows, cell, newCell, position = this.options.position;
|
|
columnIndex = dom.findNodeIndex(td, true);
|
|
for (i = 0; i < rows.length; i++) {
|
|
cell = rows[i].cells[columnIndex];
|
|
newCell = cell.cloneNode();
|
|
newCell.innerHTML = Editor.emptyElementContent;
|
|
if (position == 'before') {
|
|
dom.insertBefore(newCell, cell);
|
|
} else {
|
|
dom.insertAfter(newCell, cell);
|
|
}
|
|
}
|
|
this.releaseRange(range);
|
|
}
|
|
});
|
|
var DeleteRowCommand = Command.extend({
|
|
exec: function () {
|
|
var range = this.lockRange();
|
|
var rows = RangeUtils.mapAll(range, function (node) {
|
|
return $(node).closest('tr')[0];
|
|
});
|
|
var table = dom.closest(rows[0], 'table');
|
|
var focusElement;
|
|
if (table.rows.length <= rows.length) {
|
|
focusElement = dom.next(table);
|
|
if (!focusElement || dom.insignificant(focusElement)) {
|
|
focusElement = dom.prev(table);
|
|
}
|
|
dom.remove(table);
|
|
} else {
|
|
for (var i = 0; i < rows.length; i++) {
|
|
var row = rows[i];
|
|
dom.removeTextSiblings(row);
|
|
focusElement = dom.next(row) || dom.prev(row);
|
|
focusElement = focusElement.cells[0];
|
|
dom.remove(row);
|
|
}
|
|
}
|
|
if (focusElement) {
|
|
range.setStart(focusElement, 0);
|
|
range.collapse(true);
|
|
this.editor.selectRange(range);
|
|
}
|
|
}
|
|
});
|
|
var DeleteColumnCommand = Command.extend({
|
|
exec: function () {
|
|
var range = this.lockRange(), td = dom.closest(range.endContainer, 'td'), table = dom.closest(td, 'table'), rows = table.rows, columnIndex = dom.findNodeIndex(td, true), columnCount = rows[0].cells.length, focusElement, i;
|
|
if (columnCount == 1) {
|
|
focusElement = dom.next(table);
|
|
if (!focusElement || dom.insignificant(focusElement)) {
|
|
focusElement = dom.prev(table);
|
|
}
|
|
dom.remove(table);
|
|
} else {
|
|
dom.removeTextSiblings(td);
|
|
focusElement = dom.next(td) || dom.prev(td);
|
|
for (i = 0; i < rows.length; i++) {
|
|
dom.remove(rows[i].cells[columnIndex]);
|
|
}
|
|
}
|
|
if (focusElement) {
|
|
range.setStart(focusElement, 0);
|
|
range.collapse(true);
|
|
this.editor.selectRange(range);
|
|
}
|
|
}
|
|
});
|
|
var TableModificationTool = Tool.extend({
|
|
command: function (options) {
|
|
options = extend(options, this.options);
|
|
if (options.action == 'delete') {
|
|
if (options.type == 'row') {
|
|
return new DeleteRowCommand(options);
|
|
} else {
|
|
return new DeleteColumnCommand(options);
|
|
}
|
|
} else {
|
|
if (options.type == 'row') {
|
|
return new InsertRowCommand(options);
|
|
} else {
|
|
return new InsertColumnCommand(options);
|
|
}
|
|
}
|
|
},
|
|
initialize: function (ui, options) {
|
|
Tool.fn.initialize.call(this, ui, options);
|
|
ui.addClass('k-state-disabled');
|
|
},
|
|
update: function (ui, nodes) {
|
|
var isFormatted = !tableFormatFinder.isFormatted(nodes);
|
|
ui.toggleClass('k-state-disabled', isFormatted);
|
|
}
|
|
});
|
|
extend(kendo.ui.editor, {
|
|
PopupTool: PopupTool,
|
|
TableCommand: TableCommand,
|
|
InsertTableTool: InsertTableTool,
|
|
TableModificationTool: TableModificationTool,
|
|
InsertRowCommand: InsertRowCommand,
|
|
InsertColumnCommand: InsertColumnCommand,
|
|
DeleteRowCommand: DeleteRowCommand,
|
|
DeleteColumnCommand: DeleteColumnCommand
|
|
});
|
|
registerTool('createTable', new InsertTableTool({
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
popup: true,
|
|
title: 'Create table'
|
|
})
|
|
}));
|
|
registerTool('addColumnLeft', new TableModificationTool({
|
|
type: 'column',
|
|
position: 'before',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Add column on the left'
|
|
})
|
|
}));
|
|
registerTool('addColumnRight', new TableModificationTool({
|
|
type: 'column',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Add column on the right'
|
|
})
|
|
}));
|
|
registerTool('addRowAbove', new TableModificationTool({
|
|
type: 'row',
|
|
position: 'before',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Add row above'
|
|
})
|
|
}));
|
|
registerTool('addRowBelow', new TableModificationTool({
|
|
type: 'row',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Add row below'
|
|
})
|
|
}));
|
|
registerTool('deleteRow', new TableModificationTool({
|
|
type: 'row',
|
|
action: 'delete',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Delete row'
|
|
})
|
|
}));
|
|
registerTool('deleteColumn', new TableModificationTool({
|
|
type: 'column',
|
|
action: 'delete',
|
|
template: new ToolTemplate({
|
|
template: EditorUtils.buttonTemplate,
|
|
title: 'Delete column'
|
|
})
|
|
}));
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.editor', [
|
|
'kendo.combobox',
|
|
'kendo.dropdownlist',
|
|
'kendo.resizable',
|
|
'kendo.window',
|
|
'kendo.colorpicker',
|
|
'kendo.imagebrowser',
|
|
'util/undoredostack',
|
|
'editor/main',
|
|
'editor/dom',
|
|
'editor/serializer',
|
|
'editor/range',
|
|
'editor/system',
|
|
'editor/inlineformat',
|
|
'editor/formatblock',
|
|
'editor/linebreak',
|
|
'editor/lists',
|
|
'editor/link',
|
|
'editor/file',
|
|
'editor/image',
|
|
'editor/components',
|
|
'editor/indent',
|
|
'editor/viewhtml',
|
|
'editor/formatting',
|
|
'editor/toolbar',
|
|
'editor/tables'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'editor',
|
|
name: 'Editor',
|
|
category: 'web',
|
|
description: 'Rich text editor component',
|
|
depends: [
|
|
'combobox',
|
|
'dropdownlist',
|
|
'window',
|
|
'colorpicker'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'editor-imagebrowser',
|
|
name: 'Image Browser',
|
|
description: 'Support for uploading and inserting images',
|
|
depends: ['imagebrowser']
|
|
},
|
|
{
|
|
id: 'editor-resizable',
|
|
name: 'Resize handle',
|
|
description: 'Support for resizing the content area via a resize handle',
|
|
depends: ['resizable']
|
|
},
|
|
{
|
|
id: 'editor-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export Editor content as PDF',
|
|
depends: [
|
|
'pdf',
|
|
'drawing'
|
|
]
|
|
}
|
|
]
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.maskedtextbox', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'maskedtextbox',
|
|
name: 'MaskedTextBox',
|
|
category: 'web',
|
|
description: 'The MaskedTextBox widget allows to specify a mask type on an input field.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var caret = kendo.caret;
|
|
var keys = kendo.keys;
|
|
var ui = kendo.ui;
|
|
var Widget = ui.Widget;
|
|
var ns = '.kendoMaskedTextBox';
|
|
var proxy = $.proxy;
|
|
var INPUT_EVENT_NAME = (kendo.support.propertyChangeEvent ? 'propertychange' : 'input') + ns;
|
|
var STATEDISABLED = 'k-state-disabled';
|
|
var DISABLED = 'disabled';
|
|
var READONLY = 'readonly';
|
|
var CHANGE = 'change';
|
|
var MaskedTextBox = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
var DOMElement;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._rules = $.extend({}, that.rules, that.options.rules);
|
|
element = that.element;
|
|
DOMElement = element[0];
|
|
that.wrapper = element;
|
|
that._tokenize();
|
|
that._form();
|
|
that.element.addClass('k-textbox').attr('autocomplete', 'off').on('focus' + ns, function () {
|
|
var value = DOMElement.value;
|
|
if (!value) {
|
|
DOMElement.value = that._old = that._emptyMask;
|
|
} else {
|
|
that._togglePrompt(true);
|
|
}
|
|
that._oldValue = value;
|
|
that._timeoutId = setTimeout(function () {
|
|
caret(element, 0, value ? that._maskLength : 0);
|
|
});
|
|
}).on('focusout' + ns, function () {
|
|
var value = element.val();
|
|
clearTimeout(that._timeoutId);
|
|
DOMElement.value = that._old = '';
|
|
if (value !== that._emptyMask) {
|
|
DOMElement.value = that._old = value;
|
|
}
|
|
that._change();
|
|
that._togglePrompt();
|
|
});
|
|
var disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(element.is('[readonly]'));
|
|
}
|
|
that.value(that.options.value || element.val());
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'MaskedTextBox',
|
|
clearPromptChar: false,
|
|
unmaskOnPost: false,
|
|
promptChar: '_',
|
|
culture: '',
|
|
rules: {},
|
|
value: '',
|
|
mask: ''
|
|
},
|
|
events: [CHANGE],
|
|
rules: {
|
|
'0': /\d/,
|
|
'9': /\d|\s/,
|
|
'#': /\d|\s|\+|\-/,
|
|
'L': /[a-zA-Z]/,
|
|
'?': /[a-zA-Z]|\s/,
|
|
'&': /\S/,
|
|
'C': /./,
|
|
'A': /[a-zA-Z0-9]/,
|
|
'a': /[a-zA-Z0-9]|\s/
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
Widget.fn.setOptions.call(that, options);
|
|
that._rules = $.extend({}, that.rules, that.options.rules);
|
|
that._tokenize();
|
|
this._unbindInput();
|
|
this._bindInput();
|
|
that.value(that.element.val());
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.element.off(ns);
|
|
if (that._formElement) {
|
|
that._formElement.off('reset', that._resetHandler);
|
|
that._formElement.off('submit', that._submitHandler);
|
|
}
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
raw: function () {
|
|
var unmasked = this._unmask(this.element.val(), 0);
|
|
return unmasked.replace(new RegExp(this.options.promptChar, 'g'), '');
|
|
},
|
|
value: function (value) {
|
|
var element = this.element;
|
|
var emptyMask = this._emptyMask;
|
|
if (value === undefined) {
|
|
return this.element.val();
|
|
}
|
|
if (value === null) {
|
|
value = '';
|
|
}
|
|
if (!emptyMask) {
|
|
element.val(value);
|
|
return;
|
|
}
|
|
value = this._unmask(value + '');
|
|
element.val(value ? emptyMask : '');
|
|
this._mask(0, this._maskLength, value);
|
|
value = element.val();
|
|
this._oldValue = value;
|
|
if (kendo._activeElement() !== element) {
|
|
if (value === emptyMask) {
|
|
element.val('');
|
|
} else {
|
|
this._togglePrompt();
|
|
}
|
|
}
|
|
},
|
|
_togglePrompt: function (show) {
|
|
var DOMElement = this.element[0];
|
|
var value = DOMElement.value;
|
|
if (this.options.clearPromptChar) {
|
|
if (!show) {
|
|
value = value.replace(new RegExp(this.options.promptChar, 'g'), ' ');
|
|
} else {
|
|
value = this._oldValue;
|
|
}
|
|
DOMElement.value = this._old = value;
|
|
}
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
_bindInput: function () {
|
|
var that = this;
|
|
if (that._maskLength) {
|
|
that.element.on('keydown' + ns, proxy(that._keydown, that)).on('keypress' + ns, proxy(that._keypress, that)).on('paste' + ns, proxy(that._paste, that)).on(INPUT_EVENT_NAME, proxy(that._propertyChange, that));
|
|
}
|
|
},
|
|
_unbindInput: function () {
|
|
this.element.off('keydown' + ns).off('keypress' + ns).off('paste' + ns).off(INPUT_EVENT_NAME);
|
|
},
|
|
_editable: function (options) {
|
|
var that = this;
|
|
var element = that.element;
|
|
var disable = options.disable;
|
|
var readonly = options.readonly;
|
|
that._unbindInput();
|
|
if (!readonly && !disable) {
|
|
element.removeAttr(DISABLED).removeAttr(READONLY).removeClass(STATEDISABLED);
|
|
that._bindInput();
|
|
} else {
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly).toggleClass(STATEDISABLED, disable);
|
|
}
|
|
},
|
|
_change: function () {
|
|
var that = this;
|
|
var value = that.value();
|
|
if (value !== that._oldValue) {
|
|
that._oldValue = value;
|
|
that.trigger(CHANGE);
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
},
|
|
_propertyChange: function () {
|
|
var that = this;
|
|
var element = that.element[0];
|
|
var value = element.value;
|
|
var unmasked;
|
|
var start;
|
|
if (kendo._activeElement() !== element) {
|
|
return;
|
|
}
|
|
if (value !== that._old && !that._pasting) {
|
|
start = caret(element)[0];
|
|
unmasked = that._unmask(value.substring(start), start);
|
|
element.value = that._old = value.substring(0, start) + that._emptyMask.substring(start);
|
|
that._mask(start, start, unmasked);
|
|
caret(element, start);
|
|
}
|
|
},
|
|
_paste: function (e) {
|
|
var that = this;
|
|
var element = e.target;
|
|
var position = caret(element);
|
|
var start = position[0];
|
|
var end = position[1];
|
|
var unmasked = that._unmask(element.value.substring(end), end);
|
|
that._pasting = true;
|
|
setTimeout(function () {
|
|
var value = element.value;
|
|
var pasted = value.substring(start, caret(element)[0]);
|
|
element.value = that._old = value.substring(0, start) + that._emptyMask.substring(start);
|
|
that._mask(start, start, pasted);
|
|
start = caret(element)[0];
|
|
that._mask(start, start, unmasked);
|
|
caret(element, start);
|
|
that._pasting = false;
|
|
});
|
|
},
|
|
_form: function () {
|
|
var that = this;
|
|
var element = that.element;
|
|
var formId = element.attr('form');
|
|
var form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
setTimeout(function () {
|
|
that.value(element[0].value);
|
|
});
|
|
};
|
|
that._submitHandler = function () {
|
|
that.element[0].value = that._old = that.raw();
|
|
};
|
|
if (that.options.unmaskOnPost) {
|
|
form.on('submit', that._submitHandler);
|
|
}
|
|
that._formElement = form.on('reset', that._resetHandler);
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var key = e.keyCode;
|
|
var element = this.element[0];
|
|
var selection = caret(element);
|
|
var start = selection[0];
|
|
var end = selection[1];
|
|
var placeholder;
|
|
var backward = key === keys.BACKSPACE;
|
|
if (backward || key === keys.DELETE) {
|
|
if (start === end) {
|
|
if (backward) {
|
|
start -= 1;
|
|
} else {
|
|
end += 1;
|
|
}
|
|
placeholder = this._find(start, backward);
|
|
}
|
|
if (placeholder !== undefined && placeholder !== start) {
|
|
if (backward) {
|
|
placeholder += 1;
|
|
}
|
|
caret(element, placeholder);
|
|
} else if (start > -1) {
|
|
this._mask(start, end, '', backward);
|
|
}
|
|
e.preventDefault();
|
|
} else if (key === keys.ENTER) {
|
|
this._change();
|
|
}
|
|
},
|
|
_keypress: function (e) {
|
|
if (e.which === 0 || e.metaKey || e.ctrlKey || e.keyCode === keys.ENTER) {
|
|
return;
|
|
}
|
|
var character = String.fromCharCode(e.which);
|
|
var selection = caret(this.element);
|
|
this._mask(selection[0], selection[1], character);
|
|
if (e.keyCode === keys.BACKSPACE || character) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_find: function (idx, backward) {
|
|
var value = this.element.val() || this._emptyMask;
|
|
var step = 1;
|
|
if (backward === true) {
|
|
step = -1;
|
|
}
|
|
while (idx > -1 || idx <= this._maskLength) {
|
|
if (value.charAt(idx) !== this.tokens[idx]) {
|
|
return idx;
|
|
}
|
|
idx += step;
|
|
}
|
|
return -1;
|
|
},
|
|
_mask: function (start, end, value, backward) {
|
|
var element = this.element[0];
|
|
var current = element.value || this._emptyMask;
|
|
var empty = this.options.promptChar;
|
|
var valueLength;
|
|
var chrIdx = 0;
|
|
var unmasked;
|
|
var chr;
|
|
var idx;
|
|
start = this._find(start, backward);
|
|
if (start > end) {
|
|
end = start;
|
|
}
|
|
unmasked = this._unmask(current.substring(end), end);
|
|
value = this._unmask(value, start);
|
|
valueLength = value.length;
|
|
if (value) {
|
|
unmasked = unmasked.replace(new RegExp('^_{0,' + valueLength + '}'), '');
|
|
}
|
|
value += unmasked;
|
|
current = current.split('');
|
|
chr = value.charAt(chrIdx);
|
|
while (start < this._maskLength) {
|
|
current[start] = chr || empty;
|
|
chr = value.charAt(++chrIdx);
|
|
if (idx === undefined && chrIdx > valueLength) {
|
|
idx = start;
|
|
}
|
|
start = this._find(start + 1);
|
|
}
|
|
element.value = this._old = current.join('');
|
|
if (kendo._activeElement() === element) {
|
|
if (idx === undefined) {
|
|
idx = this._maskLength;
|
|
}
|
|
caret(element, idx);
|
|
}
|
|
},
|
|
_unmask: function (value, idx) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
value = (value + '').split('');
|
|
var chr;
|
|
var token;
|
|
var chrIdx = 0;
|
|
var tokenIdx = idx || 0;
|
|
var empty = this.options.promptChar;
|
|
var valueLength = value.length;
|
|
var tokensLength = this.tokens.length;
|
|
var result = '';
|
|
while (tokenIdx < tokensLength) {
|
|
chr = value[chrIdx];
|
|
token = this.tokens[tokenIdx];
|
|
if (chr === token || chr === empty) {
|
|
result += chr === empty ? empty : '';
|
|
chrIdx += 1;
|
|
tokenIdx += 1;
|
|
} else if (typeof token !== 'string') {
|
|
if (token.test && token.test(chr) || $.isFunction(token) && token(chr)) {
|
|
result += chr;
|
|
tokenIdx += 1;
|
|
}
|
|
chrIdx += 1;
|
|
} else {
|
|
tokenIdx += 1;
|
|
}
|
|
if (chrIdx >= valueLength) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_tokenize: function () {
|
|
var tokens = [];
|
|
var tokenIdx = 0;
|
|
var mask = this.options.mask || '';
|
|
var maskChars = mask.split('');
|
|
var length = maskChars.length;
|
|
var idx = 0;
|
|
var chr;
|
|
var rule;
|
|
var emptyMask = '';
|
|
var promptChar = this.options.promptChar;
|
|
var numberFormat = kendo.getCulture(this.options.culture).numberFormat;
|
|
var rules = this._rules;
|
|
for (; idx < length; idx++) {
|
|
chr = maskChars[idx];
|
|
rule = rules[chr];
|
|
if (rule) {
|
|
tokens[tokenIdx] = rule;
|
|
emptyMask += promptChar;
|
|
tokenIdx += 1;
|
|
} else {
|
|
if (chr === '.' || chr === ',') {
|
|
chr = numberFormat[chr];
|
|
} else if (chr === '$') {
|
|
chr = numberFormat.currency.symbol;
|
|
} else if (chr === '\\') {
|
|
idx += 1;
|
|
chr = maskChars[idx];
|
|
}
|
|
chr = chr.split('');
|
|
for (var i = 0, l = chr.length; i < l; i++) {
|
|
tokens[tokenIdx] = chr[i];
|
|
emptyMask += chr[i];
|
|
tokenIdx += 1;
|
|
}
|
|
}
|
|
}
|
|
this.tokens = tokens;
|
|
this._emptyMask = emptyMask;
|
|
this._maskLength = emptyMask.length;
|
|
}
|
|
});
|
|
ui.plugin(MaskedTextBox);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.pivotgrid', [
|
|
'kendo.dom',
|
|
'kendo.data'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'pivotgrid',
|
|
name: 'PivotGrid',
|
|
category: 'web',
|
|
description: 'The PivotGrid widget is a data summarization tool.',
|
|
depends: [
|
|
'dom',
|
|
'data',
|
|
'data.xml',
|
|
'sortable'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'pivotgrid-configurator',
|
|
name: 'Configurator',
|
|
description: 'The PivotConfigurator widget allows the user to select data slices displayed in PivotGrid',
|
|
depends: ['pivot.configurator']
|
|
},
|
|
{
|
|
id: 'pivotgrid-filtering',
|
|
name: 'Filtering',
|
|
description: 'Support for filtering',
|
|
depends: ['pivot.fieldmenu']
|
|
},
|
|
{
|
|
id: 'pivotgrid-excel-export',
|
|
name: 'Excel export',
|
|
description: 'Export pivot grid data as Excel spreadsheet',
|
|
depends: ['ooxml']
|
|
},
|
|
{
|
|
id: 'pivotgrid-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export pivot grid data as PDF',
|
|
depends: [
|
|
'pdf',
|
|
'drawing'
|
|
]
|
|
},
|
|
{
|
|
id: 'mobile-scroller',
|
|
name: 'Mobile scroller',
|
|
description: 'Support for kinetic scrolling in mobile device',
|
|
depends: ['mobile.scroller']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Class = kendo.Class, Widget = ui.Widget, DataSource = kendo.data.DataSource, toString = {}.toString, identity = function (o) {
|
|
return o;
|
|
}, map = $.map, extend = $.extend, isFunction = kendo.isFunction, CHANGE = 'change', ERROR = 'error', MEASURES = 'Measures', PROGRESS = 'progress', STATERESET = 'stateReset', AUTO = 'auto', DIV = '<div/>', NS = '.kendoPivotGrid', ROW_TOTAL_KEY = '__row_total__', DATABINDING = 'dataBinding', DATABOUND = 'dataBound', EXPANDMEMBER = 'expandMember', COLLAPSEMEMBER = 'collapseMember', STATE_EXPANDED = 'k-i-arrow-s', STATE_COLLAPSED = 'k-i-arrow-e', HEADER_TEMPLATE = '<span>#: data.member.caption || data.member.name #</span>', KPISTATUS_TEMPLATE = '<span class="k-icon k-i-kpi-#=data.dataItem.value > 0 ? "open" : data.dataItem.value < 0 ? "denied" : "hold"#">#:data.dataItem.value#</span>', KPITREND_TEMPLATE = '<span class="k-icon k-i-kpi-#=data.dataItem.value > 0 ? "increase" : data.dataItem.value < 0 ? "decrease" : "equal"#">#:data.dataItem.value#</span>', DATACELL_TEMPLATE = '#= data.dataItem ? kendo.htmlEncode(data.dataItem.fmtValue || data.dataItem.value) || " " : " " #', LAYOUT_TABLE = '<table class="k-pivot-layout">' + '<tr>' + '<td>' + '<div class="k-pivot-rowheaders"></div>' + '</td>' + '<td>' + '<div class="k-pivot-table k-state-default"></div>' + '</td>' + '</tr>' + '</table>';
|
|
function normalizeMeasures(measure) {
|
|
var descriptor = typeof measure === 'string' ? [{ name: measure }] : measure;
|
|
var descriptors = toString.call(descriptor) === '[object Array]' ? descriptor : descriptor !== undefined ? [descriptor] : [];
|
|
return map(descriptors, function (d) {
|
|
if (typeof d === 'string') {
|
|
return { name: d };
|
|
}
|
|
return {
|
|
name: d.name,
|
|
type: d.type
|
|
};
|
|
});
|
|
}
|
|
function normalizeMembers(member) {
|
|
var descriptor = typeof member === 'string' ? [{
|
|
name: [member],
|
|
expand: false
|
|
}] : member;
|
|
var descriptors = toString.call(descriptor) === '[object Array]' ? descriptor : descriptor !== undefined ? [descriptor] : [];
|
|
return map(descriptors, function (d) {
|
|
if (typeof d === 'string') {
|
|
return {
|
|
name: [d],
|
|
expand: false
|
|
};
|
|
}
|
|
return {
|
|
name: toString.call(d.name) === '[object Array]' ? d.name.slice() : [d.name],
|
|
expand: d.expand
|
|
};
|
|
});
|
|
}
|
|
function normalizeName(name) {
|
|
if (name.indexOf(' ') !== -1) {
|
|
name = '["' + name + '"]';
|
|
}
|
|
return name;
|
|
}
|
|
function accumulateMembers(accumulator, rootTuple, tuple, level) {
|
|
var idx, length;
|
|
var children;
|
|
var member;
|
|
if (!tuple) {
|
|
tuple = rootTuple;
|
|
}
|
|
if (!level) {
|
|
level = 0;
|
|
}
|
|
member = tuple.members[level];
|
|
if (!member || member.measure) {
|
|
return;
|
|
}
|
|
children = member.children;
|
|
length = children.length;
|
|
if (tuple === rootTuple) {
|
|
accumulator[kendo.stringify([member.name])] = !!length;
|
|
} else if (length) {
|
|
accumulator[kendo.stringify(buildPath(tuple, level))] = true;
|
|
}
|
|
if (length) {
|
|
for (idx = 0; idx < length; idx++) {
|
|
accumulateMembers(accumulator, rootTuple, children[idx], level);
|
|
}
|
|
}
|
|
accumulateMembers(accumulator, rootTuple, tuple, level + 1);
|
|
}
|
|
function descriptorsForAxes(tuples) {
|
|
var result = {};
|
|
if (tuples.length) {
|
|
accumulateMembers(result, tuples[0]);
|
|
}
|
|
var descriptors = [];
|
|
for (var k in result) {
|
|
descriptors.push({
|
|
name: $.parseJSON(k),
|
|
expand: result[k]
|
|
});
|
|
}
|
|
return descriptors;
|
|
}
|
|
function addMissingPathMembers(members, axis) {
|
|
var tuples = axis.tuples || [];
|
|
var firstTuple = tuples[0];
|
|
if (firstTuple && members.length < firstTuple.members.length) {
|
|
var tupleMembers = firstTuple.members;
|
|
for (var idx = 0; idx < tupleMembers.length; idx++) {
|
|
if (tupleMembers[idx].measure) {
|
|
continue;
|
|
}
|
|
var found = false;
|
|
for (var j = 0; j < members.length; j++) {
|
|
if (getName(members[j]).indexOf(tupleMembers[idx].hierarchy) === 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
members.push({
|
|
name: [tupleMembers[idx].name],
|
|
expand: false
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function tupleToDescriptors(tuple) {
|
|
var result = [];
|
|
var members = tuple.members;
|
|
for (var idx = 0; idx < members.length; idx++) {
|
|
if (members[idx].measure) {
|
|
continue;
|
|
}
|
|
result.push({
|
|
name: [members[idx].name],
|
|
expand: members[idx].children.length > 0
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
function descriptorsForMembers(axis, members, measures) {
|
|
axis = axis || {};
|
|
addMissingPathMembers(members, axis);
|
|
if (measures.length > 1) {
|
|
members.push({
|
|
name: MEASURES,
|
|
measure: true,
|
|
children: normalizeMembers(measures)
|
|
});
|
|
}
|
|
var tupletoSearch = { members: members };
|
|
if (axis.tuples) {
|
|
var result = findExistingTuple(axis.tuples, tupletoSearch);
|
|
if (result.tuple) {
|
|
members = tupleToDescriptors(result.tuple);
|
|
}
|
|
}
|
|
return members;
|
|
}
|
|
function createAggregateGetter(m) {
|
|
var measureGetter = kendo.getter(m.field, true);
|
|
return function (aggregatorContext, state) {
|
|
return m.aggregate(measureGetter(aggregatorContext.dataItem), state, aggregatorContext);
|
|
};
|
|
}
|
|
function isNumber(val) {
|
|
return typeof val === 'number' && !isNaN(val);
|
|
}
|
|
function isDate(val) {
|
|
return val && val.getTime;
|
|
}
|
|
var functions = {
|
|
sum: function (value, state) {
|
|
var accumulator = state.accumulator;
|
|
if (!isNumber(accumulator)) {
|
|
accumulator = value;
|
|
} else if (isNumber(value)) {
|
|
accumulator += value;
|
|
}
|
|
return accumulator;
|
|
},
|
|
count: function (value, state) {
|
|
return (state.accumulator || 0) + 1;
|
|
},
|
|
average: {
|
|
aggregate: function (value, state) {
|
|
var accumulator = state.accumulator;
|
|
if (state.count === undefined) {
|
|
state.count = 0;
|
|
}
|
|
if (!isNumber(accumulator)) {
|
|
accumulator = value;
|
|
} else if (isNumber(value)) {
|
|
accumulator += value;
|
|
}
|
|
if (isNumber(value)) {
|
|
state.count++;
|
|
}
|
|
return accumulator;
|
|
},
|
|
result: function (state) {
|
|
var accumulator = state.accumulator;
|
|
if (isNumber(accumulator)) {
|
|
accumulator = accumulator / state.count;
|
|
}
|
|
return accumulator;
|
|
}
|
|
},
|
|
max: function (value, state) {
|
|
var accumulator = state.accumulator;
|
|
if (!isNumber(accumulator) && !isDate(accumulator)) {
|
|
accumulator = value;
|
|
}
|
|
if (accumulator < value && (isNumber(value) || isDate(value))) {
|
|
accumulator = value;
|
|
}
|
|
return accumulator;
|
|
},
|
|
min: function (value, state) {
|
|
var accumulator = state.accumulator;
|
|
if (!isNumber(accumulator) && !isDate(accumulator)) {
|
|
accumulator = value;
|
|
}
|
|
if (accumulator > value && (isNumber(value) || isDate(value))) {
|
|
accumulator = value;
|
|
}
|
|
return accumulator;
|
|
}
|
|
};
|
|
var PivotCubeBuilder = Class.extend({
|
|
init: function (options) {
|
|
this.options = extend({}, this.options, options);
|
|
this.dimensions = this._normalizeDescriptors('field', this.options.dimensions);
|
|
this.measures = this._normalizeDescriptors('name', this.options.measures);
|
|
},
|
|
_normalizeDescriptors: function (keyField, descriptors) {
|
|
descriptors = descriptors || {};
|
|
var fields = {};
|
|
var field;
|
|
if (toString.call(descriptors) === '[object Array]') {
|
|
for (var idx = 0, length = descriptors.length; idx < length; idx++) {
|
|
field = descriptors[idx];
|
|
if (typeof field === 'string') {
|
|
fields[field] = {};
|
|
} else if (field[keyField]) {
|
|
fields[field[keyField]] = field;
|
|
}
|
|
}
|
|
descriptors = fields;
|
|
}
|
|
return descriptors;
|
|
},
|
|
_rootTuples: function (rootNames, measureAggregators) {
|
|
var aggregatorsLength = measureAggregators.length || 1;
|
|
var dimensionsSchema = this.dimensions || [];
|
|
var root, name, parts;
|
|
var measureIdx = 0;
|
|
var idx;
|
|
var rootNamesLength = rootNames.length;
|
|
var result = [];
|
|
var keys = [];
|
|
if (rootNamesLength || measureAggregators.length) {
|
|
for (measureIdx = 0; measureIdx < aggregatorsLength; measureIdx++) {
|
|
root = { members: [] };
|
|
for (idx = 0; idx < rootNamesLength; idx++) {
|
|
name = rootNames[idx];
|
|
parts = name.split('&');
|
|
root.members[root.members.length] = {
|
|
children: [],
|
|
caption: (dimensionsSchema[name] || {}).caption || 'All',
|
|
name: name,
|
|
levelName: name,
|
|
levelNum: '0',
|
|
hasChildren: true,
|
|
parentName: parts.length > 1 ? parts[0] : undefined,
|
|
hierarchy: name
|
|
};
|
|
}
|
|
if (aggregatorsLength > 1) {
|
|
root.members[root.members.length] = {
|
|
children: [],
|
|
caption: measureAggregators[measureIdx].caption,
|
|
name: measureAggregators[measureIdx].descriptor.name,
|
|
levelName: 'MEASURES',
|
|
levelNum: '0',
|
|
hasChildren: false,
|
|
parentName: undefined,
|
|
hierarchy: 'MEASURES'
|
|
};
|
|
}
|
|
result[result.length] = root;
|
|
}
|
|
keys.push(ROW_TOTAL_KEY);
|
|
}
|
|
return {
|
|
keys: keys,
|
|
tuples: result
|
|
};
|
|
},
|
|
_expandedTuples: function (map, expanded, measureAggregators) {
|
|
var aggregatorsLength = measureAggregators.length || 1;
|
|
var dimensionsSchema = this.dimensions || [];
|
|
var measureIdx;
|
|
var tuple;
|
|
var key;
|
|
var mapItem;
|
|
var current;
|
|
var currentKeys;
|
|
var accumulator = [];
|
|
var accumulatorKeys = [];
|
|
var memberInfo;
|
|
var expandedNames;
|
|
var parts;
|
|
var name;
|
|
var idx;
|
|
for (key in map) {
|
|
mapItem = map[key];
|
|
memberInfo = this._findExpandedMember(expanded, mapItem.uniquePath);
|
|
current = accumulator[memberInfo.index] || [];
|
|
currentKeys = accumulatorKeys[memberInfo.index] || [];
|
|
expandedNames = memberInfo.member.names;
|
|
for (measureIdx = 0; measureIdx < aggregatorsLength; measureIdx++) {
|
|
tuple = { members: [] };
|
|
for (idx = 0; idx < expandedNames.length; idx++) {
|
|
if (idx === memberInfo.member.expandedIdx) {
|
|
tuple.members[tuple.members.length] = {
|
|
children: [],
|
|
caption: mapItem.value,
|
|
name: mapItem.name,
|
|
hasChildren: false,
|
|
levelNum: 1,
|
|
levelName: mapItem.parentName + mapItem.name,
|
|
parentName: mapItem.parentName,
|
|
hierarchy: mapItem.parentName + mapItem.name
|
|
};
|
|
if (measureIdx === 0) {
|
|
currentKeys.push(buildPath(tuple, idx).join(''));
|
|
}
|
|
} else {
|
|
name = expandedNames[idx];
|
|
parts = name.split('&');
|
|
tuple.members[tuple.members.length] = {
|
|
children: [],
|
|
caption: (dimensionsSchema[name] || {}).caption || 'All',
|
|
name: name,
|
|
levelName: name,
|
|
levelNum: '0',
|
|
hasChildren: true,
|
|
parentName: parts.length > 1 ? parts[0] : undefined,
|
|
hierarchy: name
|
|
};
|
|
}
|
|
}
|
|
if (aggregatorsLength > 1) {
|
|
tuple.members[tuple.members.length] = {
|
|
children: [],
|
|
caption: measureAggregators[measureIdx].caption,
|
|
name: measureAggregators[measureIdx].descriptor.name,
|
|
levelName: 'MEASURES',
|
|
levelNum: '0',
|
|
hasChildren: true,
|
|
parentName: undefined,
|
|
hierarchy: 'MEASURES'
|
|
};
|
|
}
|
|
current[current.length] = tuple;
|
|
}
|
|
accumulator[memberInfo.index] = current;
|
|
accumulatorKeys[memberInfo.index] = currentKeys;
|
|
}
|
|
return {
|
|
keys: accumulatorKeys,
|
|
tuples: accumulator
|
|
};
|
|
},
|
|
_findExpandedMember: function (members, parentName) {
|
|
for (var idx = 0; idx < members.length; idx++) {
|
|
if (members[idx].uniquePath === parentName) {
|
|
return {
|
|
member: members[idx],
|
|
index: idx
|
|
};
|
|
}
|
|
}
|
|
},
|
|
_asTuples: function (map, descriptor, measureAggregators) {
|
|
measureAggregators = measureAggregators || [];
|
|
var rootInfo = this._rootTuples(descriptor.root, measureAggregators);
|
|
var expandedInfo = this._expandedTuples(map, descriptor.expanded, measureAggregators);
|
|
return {
|
|
keys: [].concat.apply(rootInfo.keys, expandedInfo.keys),
|
|
tuples: [].concat.apply(rootInfo.tuples, expandedInfo.tuples)
|
|
};
|
|
},
|
|
_measuresInfo: function (measures, rowAxis) {
|
|
var idx = 0;
|
|
var length = measures && measures.length;
|
|
var aggregateNames = [];
|
|
var resultFuncs = {};
|
|
var formats = {};
|
|
var descriptors = this.measures || {};
|
|
var measure;
|
|
var name;
|
|
for (; idx < length; idx++) {
|
|
name = measures[idx].descriptor.name;
|
|
measure = descriptors[name] || {};
|
|
aggregateNames.push(name);
|
|
if (measure.result) {
|
|
resultFuncs[name] = measure.result;
|
|
}
|
|
if (measure.format) {
|
|
formats[name] = measure.format;
|
|
}
|
|
}
|
|
return {
|
|
names: aggregateNames,
|
|
formats: formats,
|
|
resultFuncs: resultFuncs,
|
|
rowAxis: rowAxis
|
|
};
|
|
},
|
|
_toDataArray: function (map, measuresInfo, rowKeys, columnKeys) {
|
|
var result = [];
|
|
var aggregates;
|
|
var name, i, j, k, n;
|
|
var row, column, columnKey;
|
|
var rowMeasureNamesLength = 1;
|
|
var rowMeasureNames = [];
|
|
var columnMeasureNames;
|
|
var rowLength = rowKeys.length || 1;
|
|
var columnLength = columnKeys.length || 1;
|
|
if (measuresInfo.rowAxis) {
|
|
rowMeasureNames = measuresInfo.names;
|
|
rowMeasureNamesLength = rowMeasureNames.length;
|
|
} else {
|
|
columnMeasureNames = measuresInfo.names;
|
|
}
|
|
for (i = 0; i < rowLength; i++) {
|
|
row = map[rowKeys[i] || ROW_TOTAL_KEY];
|
|
for (n = 0; n < rowMeasureNamesLength; n++) {
|
|
if (measuresInfo.rowAxis) {
|
|
columnMeasureNames = [rowMeasureNames[n]];
|
|
}
|
|
for (j = 0; j < columnLength; j++) {
|
|
columnKey = columnKeys[j] || ROW_TOTAL_KEY;
|
|
column = row.items[columnKey];
|
|
if (columnKey === ROW_TOTAL_KEY) {
|
|
aggregates = row.aggregates;
|
|
} else {
|
|
aggregates = column ? column.aggregates : {};
|
|
}
|
|
for (k = 0; k < columnMeasureNames.length; k++) {
|
|
name = columnMeasureNames[k];
|
|
this._addData(result, aggregates[name], measuresInfo.formats[name], measuresInfo.resultFuncs[name]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_addData: function (result, value, format, resultFunc) {
|
|
var fmtValue = '';
|
|
var ordinal;
|
|
if (value) {
|
|
value = resultFunc ? resultFunc(value) : value.accumulator;
|
|
fmtValue = format ? kendo.format(format, value) : value;
|
|
}
|
|
ordinal = result.length;
|
|
result[ordinal] = {
|
|
ordinal: ordinal,
|
|
value: value || '',
|
|
fmtValue: fmtValue
|
|
};
|
|
},
|
|
_matchDescriptors: function (dataItem, descriptor, getters) {
|
|
var parts;
|
|
var parentField;
|
|
var expectedValue;
|
|
var names = descriptor.names;
|
|
var idx = descriptor.expandedIdx;
|
|
var value;
|
|
while (idx > 0) {
|
|
parts = names[--idx].split('&');
|
|
if (parts.length > 1) {
|
|
parentField = parts[0];
|
|
expectedValue = parts[1];
|
|
value = getters[parentField](dataItem);
|
|
value = value !== undefined && value !== null ? value.toString() : value;
|
|
if (value != expectedValue) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
_calculateAggregate: function (measureAggregators, aggregatorContext, totalItem) {
|
|
var result = {};
|
|
var state;
|
|
var name;
|
|
for (var measureIdx = 0; measureIdx < measureAggregators.length; measureIdx++) {
|
|
name = measureAggregators[measureIdx].descriptor.name;
|
|
state = totalItem.aggregates[name] || {};
|
|
state.accumulator = measureAggregators[measureIdx].aggregator(aggregatorContext, state);
|
|
result[name] = state;
|
|
}
|
|
return result;
|
|
},
|
|
_processColumns: function (measureAggregators, descriptors, getters, columns, aggregatorContext, rowTotal, state, updateColumn) {
|
|
var value;
|
|
var descriptor;
|
|
var column;
|
|
var totalItem;
|
|
var key, name, parentName, path;
|
|
var dataItem = aggregatorContext.dataItem;
|
|
var idx = 0;
|
|
for (; idx < descriptors.length; idx++) {
|
|
descriptor = descriptors[idx];
|
|
if (!this._matchDescriptors(dataItem, descriptor, getters)) {
|
|
continue;
|
|
}
|
|
path = descriptor.names.slice(0, descriptor.expandedIdx).join('');
|
|
name = descriptor.names[descriptor.expandedIdx];
|
|
value = getters[name](dataItem);
|
|
value = value !== undefined && value !== null ? value.toString() : value;
|
|
parentName = name;
|
|
name = name + '&' + value;
|
|
key = path + name;
|
|
column = columns[key] || {
|
|
index: state.columnIndex,
|
|
parentName: parentName,
|
|
name: name,
|
|
uniquePath: path + parentName,
|
|
value: value
|
|
};
|
|
totalItem = rowTotal.items[key] || { aggregates: {} };
|
|
rowTotal.items[key] = {
|
|
index: column.index,
|
|
aggregates: this._calculateAggregate(measureAggregators, aggregatorContext, totalItem)
|
|
};
|
|
if (updateColumn) {
|
|
if (!columns[key]) {
|
|
state.columnIndex++;
|
|
}
|
|
columns[key] = column;
|
|
}
|
|
}
|
|
},
|
|
_measureAggregators: function (options) {
|
|
var measureDescriptors = options.measures || [];
|
|
var measures = this.measures || {};
|
|
var aggregators = [];
|
|
var descriptor, measure, idx, length;
|
|
var defaultAggregate, aggregate;
|
|
if (measureDescriptors.length) {
|
|
for (idx = 0, length = measureDescriptors.length; idx < length; idx++) {
|
|
descriptor = measureDescriptors[idx];
|
|
measure = measures[descriptor.name];
|
|
defaultAggregate = null;
|
|
if (measure) {
|
|
aggregate = measure.aggregate;
|
|
if (typeof aggregate === 'string') {
|
|
defaultAggregate = functions[aggregate.toLowerCase()];
|
|
if (!defaultAggregate) {
|
|
throw new Error('There is no such aggregate function');
|
|
}
|
|
measure.aggregate = defaultAggregate.aggregate || defaultAggregate;
|
|
measure.result = defaultAggregate.result;
|
|
}
|
|
aggregators.push({
|
|
descriptor: descriptor,
|
|
caption: measure.caption,
|
|
result: measure.result,
|
|
aggregator: createAggregateGetter(measure)
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
aggregators.push({
|
|
descriptor: { name: 'default' },
|
|
caption: 'default',
|
|
aggregator: function () {
|
|
return 1;
|
|
}
|
|
});
|
|
}
|
|
return aggregators;
|
|
},
|
|
_buildGetters: function (names) {
|
|
var result = {};
|
|
var parts;
|
|
var name;
|
|
for (var idx = 0; idx < names.length; idx++) {
|
|
name = names[idx];
|
|
parts = name.split('&');
|
|
if (parts.length > 1) {
|
|
result[parts[0]] = kendo.getter(parts[0], true);
|
|
} else {
|
|
result[name] = kendo.getter(normalizeName(name), true);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_parseDescriptors: function (descriptors) {
|
|
var parsedDescriptors = parseDescriptors(descriptors);
|
|
var rootNames = getRootNames(parsedDescriptors.root);
|
|
var expanded = parsedDescriptors.expanded;
|
|
var result = [];
|
|
for (var idx = 0; idx < expanded.length; idx++) {
|
|
result.push(mapNames(expanded[idx].name, rootNames));
|
|
}
|
|
return {
|
|
root: rootNames,
|
|
expanded: result
|
|
};
|
|
},
|
|
_filter: function (data, filter) {
|
|
if (!filter) {
|
|
return data;
|
|
}
|
|
var expr;
|
|
var idx = 0;
|
|
var filters = filter.filters;
|
|
for (; idx < filters.length; idx++) {
|
|
expr = filters[idx];
|
|
if (expr.operator === 'in') {
|
|
filters[idx] = this._normalizeFilter(expr);
|
|
}
|
|
}
|
|
return new kendo.data.Query(data).filter(filter).data;
|
|
},
|
|
_normalizeFilter: function (filter) {
|
|
var value = filter.value.split(',');
|
|
var result = [];
|
|
if (!value.length) {
|
|
return value;
|
|
}
|
|
for (var idx = 0; idx < value.length; idx++) {
|
|
result.push({
|
|
field: filter.field,
|
|
operator: 'eq',
|
|
value: value[idx]
|
|
});
|
|
}
|
|
return {
|
|
logic: 'or',
|
|
filters: result
|
|
};
|
|
},
|
|
process: function (data, options) {
|
|
data = data || [];
|
|
options = options || {};
|
|
data = this._filter(data, options.filter);
|
|
var measures = options.measures || [];
|
|
var measuresRowAxis = options.measuresAxis === 'rows';
|
|
var columnDescriptors = options.columns || [];
|
|
var rowDescriptors = options.rows || [];
|
|
if (!columnDescriptors.length && rowDescriptors.length && (!measures.length || measures.length && measuresRowAxis)) {
|
|
columnDescriptors = rowDescriptors;
|
|
rowDescriptors = [];
|
|
measuresRowAxis = false;
|
|
}
|
|
if (!columnDescriptors.length && !rowDescriptors.length) {
|
|
measuresRowAxis = false;
|
|
}
|
|
if (!columnDescriptors.length && measures.length) {
|
|
columnDescriptors = normalizeMembers(options.measures);
|
|
}
|
|
columnDescriptors = this._parseDescriptors(columnDescriptors);
|
|
rowDescriptors = this._parseDescriptors(rowDescriptors);
|
|
var aggregatedData = {};
|
|
var columns = {};
|
|
var rows = {};
|
|
var rowValue;
|
|
var state = { columnIndex: 0 };
|
|
var measureAggregators = this._measureAggregators(options);
|
|
var columnGetters = this._buildGetters(columnDescriptors.root);
|
|
var rowGetters = this._buildGetters(rowDescriptors.root);
|
|
var processed = false;
|
|
var expandedColumns = columnDescriptors.expanded;
|
|
var expandedRows = rowDescriptors.expanded;
|
|
var dataItem;
|
|
var aggregatorContext;
|
|
var hasExpandedRows = expandedRows.length !== 0;
|
|
var rowIdx, rowDescriptor, rowName, rowTotal;
|
|
var key, path, parentName, value;
|
|
var columnsInfo, rowsInfo;
|
|
var length = data.length;
|
|
var idx = 0;
|
|
if (columnDescriptors.root.length || rowDescriptors.root.length) {
|
|
processed = true;
|
|
for (idx = 0; idx < length; idx++) {
|
|
dataItem = data[idx];
|
|
aggregatorContext = {
|
|
dataItem: dataItem,
|
|
index: idx
|
|
};
|
|
rowTotal = aggregatedData[ROW_TOTAL_KEY] || {
|
|
items: {},
|
|
aggregates: {}
|
|
};
|
|
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, rowTotal, state, !hasExpandedRows);
|
|
rowTotal.aggregates = this._calculateAggregate(measureAggregators, aggregatorContext, rowTotal);
|
|
aggregatedData[ROW_TOTAL_KEY] = rowTotal;
|
|
for (rowIdx = 0; rowIdx < expandedRows.length; rowIdx++) {
|
|
rowDescriptor = expandedRows[rowIdx];
|
|
if (!this._matchDescriptors(dataItem, rowDescriptor, rowGetters)) {
|
|
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, {
|
|
items: {},
|
|
aggregates: {}
|
|
}, state, true);
|
|
continue;
|
|
}
|
|
path = rowDescriptor.names.slice(0, rowDescriptor.expandedIdx).join('');
|
|
rowName = rowDescriptor.names[rowDescriptor.expandedIdx];
|
|
parentName = rowName;
|
|
rowValue = rowGetters[rowName](dataItem);
|
|
rowValue = rowValue !== undefined ? rowValue.toString() : rowValue;
|
|
rowName = rowName + '&' + rowValue;
|
|
key = path + rowName;
|
|
rows[key] = {
|
|
uniquePath: path + parentName,
|
|
parentName: parentName,
|
|
name: rowName,
|
|
value: rowValue
|
|
};
|
|
value = aggregatedData[key] || {
|
|
items: {},
|
|
aggregates: {}
|
|
};
|
|
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, value, state, true);
|
|
value.aggregates = this._calculateAggregate(measureAggregators, aggregatorContext, value);
|
|
aggregatedData[key] = value;
|
|
}
|
|
}
|
|
}
|
|
if (processed && length) {
|
|
if (measureAggregators.length > 1 && (!options.columns || !options.columns.length)) {
|
|
columnDescriptors = {
|
|
root: [],
|
|
expanded: []
|
|
};
|
|
}
|
|
columnsInfo = this._asTuples(columns, columnDescriptors, measuresRowAxis ? [] : measureAggregators);
|
|
rowsInfo = this._asTuples(rows, rowDescriptors, measuresRowAxis ? measureAggregators : []);
|
|
columns = columnsInfo.tuples;
|
|
rows = rowsInfo.tuples;
|
|
aggregatedData = this._toDataArray(aggregatedData, this._measuresInfo(measureAggregators, measuresRowAxis), rowsInfo.keys, columnsInfo.keys);
|
|
} else {
|
|
aggregatedData = columns = rows = [];
|
|
}
|
|
return {
|
|
axes: {
|
|
columns: { tuples: columns },
|
|
rows: { tuples: rows }
|
|
},
|
|
data: aggregatedData
|
|
};
|
|
}
|
|
});
|
|
var PivotTransport = Class.extend({
|
|
init: function (options, transport) {
|
|
this.transport = transport;
|
|
this.options = transport.options || {};
|
|
if (!this.transport.discover) {
|
|
if (isFunction(options.discover)) {
|
|
this.discover = options.discover;
|
|
}
|
|
}
|
|
},
|
|
read: function (options) {
|
|
return this.transport.read(options);
|
|
},
|
|
update: function (options) {
|
|
return this.transport.update(options);
|
|
},
|
|
create: function (options) {
|
|
return this.transport.create(options);
|
|
},
|
|
destroy: function (options) {
|
|
return this.transport.destroy(options);
|
|
},
|
|
discover: function (options) {
|
|
if (this.transport.discover) {
|
|
return this.transport.discover(options);
|
|
}
|
|
options.success({});
|
|
},
|
|
catalog: function (val) {
|
|
var options = this.options || {};
|
|
if (val === undefined) {
|
|
return (options.connection || {}).catalog;
|
|
}
|
|
var connection = options.connection || {};
|
|
connection.catalog = val;
|
|
this.options.connection = connection;
|
|
$.extend(this.transport.options, { connection: connection });
|
|
},
|
|
cube: function (val) {
|
|
var options = this.options || {};
|
|
if (val === undefined) {
|
|
return (options.connection || {}).cube;
|
|
}
|
|
var connection = options.connection || {};
|
|
connection.cube = val;
|
|
this.options.connection = connection;
|
|
extend(true, this.transport.options, { connection: connection });
|
|
}
|
|
});
|
|
var PivotDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
var cube = ((options || {}).schema || {}).cube;
|
|
var measuresAxis = 'columns';
|
|
var measures;
|
|
var schema = {
|
|
axes: identity,
|
|
cubes: identity,
|
|
catalogs: identity,
|
|
measures: identity,
|
|
dimensions: identity,
|
|
hierarchies: identity,
|
|
levels: identity,
|
|
members: identity
|
|
};
|
|
if (cube) {
|
|
schema = $.extend(schema, this._cubeSchema(cube));
|
|
this.cubeBuilder = new PivotCubeBuilder(cube);
|
|
}
|
|
DataSource.fn.init.call(this, extend(true, {}, { schema: schema }, options));
|
|
this.transport = new PivotTransport(this.options.transport || {}, this.transport);
|
|
this._columns = normalizeMembers(this.options.columns);
|
|
this._rows = normalizeMembers(this.options.rows);
|
|
measures = this.options.measures || [];
|
|
if (toString.call(measures) === '[object Object]') {
|
|
measuresAxis = measures.axis || 'columns';
|
|
measures = measures.values || [];
|
|
}
|
|
this._measures = normalizeMeasures(measures);
|
|
this._measuresAxis = measuresAxis;
|
|
this._skipNormalize = 0;
|
|
this._axes = {};
|
|
},
|
|
_cubeSchema: function (cube) {
|
|
return {
|
|
dimensions: function () {
|
|
var result = [];
|
|
var dimensions = cube.dimensions;
|
|
for (var key in dimensions) {
|
|
result.push({
|
|
name: key,
|
|
caption: dimensions[key].caption || key,
|
|
uniqueName: key,
|
|
defaultHierarchy: key,
|
|
type: 1
|
|
});
|
|
}
|
|
if (cube.measures) {
|
|
result.push({
|
|
name: MEASURES,
|
|
caption: MEASURES,
|
|
uniqueName: MEASURES,
|
|
type: 2
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
hierarchies: function () {
|
|
return [];
|
|
},
|
|
measures: function () {
|
|
var result = [];
|
|
var measures = cube.measures;
|
|
for (var key in measures) {
|
|
result.push({
|
|
name: key,
|
|
caption: key,
|
|
uniqueName: key,
|
|
aggregator: key
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
members: $.proxy(function (response, restrictions) {
|
|
var name = restrictions.levelUniqueName || restrictions.memberUniqueName;
|
|
var dataGetter = kendo.getter(this.options.schema.data, true);
|
|
var data = dataGetter(this.options.data) || this._rawData || [];
|
|
var result = [];
|
|
var getter;
|
|
var value;
|
|
var idx = 0;
|
|
var distinct = {};
|
|
if (name) {
|
|
name = name.split('.')[0];
|
|
}
|
|
if (!restrictions.treeOp) {
|
|
result.push({
|
|
caption: cube.dimensions[name].caption || name,
|
|
childrenCardinality: '1',
|
|
dimensionUniqueName: name,
|
|
hierarchyUniqueName: name,
|
|
levelUniqueName: name,
|
|
name: name,
|
|
uniqueName: name
|
|
});
|
|
return result;
|
|
}
|
|
getter = kendo.getter(normalizeName(name), true);
|
|
for (; idx < data.length; idx++) {
|
|
value = getter(data[idx]);
|
|
if ((value || value === 0) && !distinct[value]) {
|
|
distinct[value] = true;
|
|
result.push({
|
|
caption: value,
|
|
childrenCardinality: '0',
|
|
dimensionUniqueName: name,
|
|
hierarchyUniqueName: name,
|
|
levelUniqueName: name,
|
|
name: value,
|
|
uniqueName: value
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
}, this)
|
|
};
|
|
},
|
|
options: {
|
|
serverSorting: true,
|
|
serverPaging: true,
|
|
serverFiltering: true,
|
|
serverGrouping: true,
|
|
serverAggregates: true
|
|
},
|
|
catalog: function (val) {
|
|
if (val === undefined) {
|
|
return this.transport.catalog();
|
|
}
|
|
this.transport.catalog(val);
|
|
this._mergeState({});
|
|
this._axes = {};
|
|
this.data([]);
|
|
},
|
|
cube: function (val) {
|
|
if (val === undefined) {
|
|
return this.transport.cube();
|
|
}
|
|
this.transport.cube(val);
|
|
this._axes = {};
|
|
this._mergeState({});
|
|
this.data([]);
|
|
},
|
|
axes: function () {
|
|
return this._axes;
|
|
},
|
|
columns: function (val) {
|
|
if (val === undefined) {
|
|
return this._columns;
|
|
}
|
|
this._skipNormalize += 1;
|
|
this._clearAxesData = true;
|
|
this._columns = normalizeMembers(val);
|
|
this.query({
|
|
columns: val,
|
|
rows: this.rowsAxisDescriptors(),
|
|
measures: this.measures()
|
|
});
|
|
},
|
|
rows: function (val) {
|
|
if (val === undefined) {
|
|
return this._rows;
|
|
}
|
|
this._skipNormalize += 1;
|
|
this._clearAxesData = true;
|
|
this._rows = normalizeMembers(val);
|
|
this.query({
|
|
columns: this.columnsAxisDescriptors(),
|
|
rows: val,
|
|
measures: this.measures()
|
|
});
|
|
},
|
|
measures: function (val) {
|
|
if (val === undefined) {
|
|
return this._measures;
|
|
}
|
|
this._skipNormalize += 1;
|
|
this._clearAxesData = true;
|
|
this.query({
|
|
columns: this.columnsAxisDescriptors(),
|
|
rows: this.rowsAxisDescriptors(),
|
|
measures: normalizeMeasures(val)
|
|
});
|
|
},
|
|
measuresAxis: function () {
|
|
return this._measuresAxis || 'columns';
|
|
},
|
|
_expandPath: function (path, axis) {
|
|
var origin = axis === 'columns' ? 'columns' : 'rows';
|
|
var other = axis === 'columns' ? 'rows' : 'columns';
|
|
var members = normalizeMembers(path);
|
|
var memberToExpand = getName(members[members.length - 1]);
|
|
this._lastExpanded = origin;
|
|
members = descriptorsForMembers(this.axes()[origin], members, this.measures());
|
|
for (var idx = 0; idx < members.length; idx++) {
|
|
var memberName = getName(members[idx]);
|
|
if (memberName === memberToExpand) {
|
|
if (members[idx].expand) {
|
|
return;
|
|
}
|
|
members[idx].expand = true;
|
|
} else {
|
|
members[idx].expand = false;
|
|
}
|
|
}
|
|
var descriptors = {};
|
|
descriptors[origin] = members;
|
|
descriptors[other] = this._descriptorsForAxis(other);
|
|
this._query(descriptors);
|
|
},
|
|
_descriptorsForAxis: function (axis) {
|
|
var axes = this.axes();
|
|
var descriptors = this[axis]() || [];
|
|
if (axes && axes[axis] && axes[axis].tuples && axes[axis].tuples[0]) {
|
|
descriptors = descriptorsForAxes(axes[axis].tuples || []);
|
|
}
|
|
return descriptors;
|
|
},
|
|
columnsAxisDescriptors: function () {
|
|
return this._descriptorsForAxis('columns');
|
|
},
|
|
rowsAxisDescriptors: function () {
|
|
return this._descriptorsForAxis('rows');
|
|
},
|
|
_process: function (data, e) {
|
|
this._view = data;
|
|
e = e || {};
|
|
e.items = e.items || this._view;
|
|
this.trigger(CHANGE, e);
|
|
},
|
|
_query: function (options) {
|
|
var that = this;
|
|
if (!options) {
|
|
this._skipNormalize += 1;
|
|
this._clearAxesData = true;
|
|
}
|
|
return that.query(extend({}, {
|
|
page: that.page(),
|
|
pageSize: that.pageSize(),
|
|
sort: that.sort(),
|
|
filter: that.filter(),
|
|
group: that.group(),
|
|
aggregate: that.aggregate(),
|
|
columns: this.columnsAxisDescriptors(),
|
|
rows: this.rowsAxisDescriptors(),
|
|
measures: this.measures()
|
|
}, options));
|
|
},
|
|
query: function (options) {
|
|
var state = this._mergeState(options);
|
|
if (this._data.length && this.cubeBuilder) {
|
|
this._params(state);
|
|
this._updateLocalData(this._pristineData);
|
|
return $.Deferred().resolve().promise();
|
|
}
|
|
return this.read(state);
|
|
},
|
|
_mergeState: function (options) {
|
|
options = DataSource.fn._mergeState.call(this, options);
|
|
if (options !== undefined) {
|
|
this._measures = normalizeMeasures(options.measures);
|
|
if (options.columns) {
|
|
options.columns = normalizeMembers(options.columns);
|
|
} else if (!options.columns) {
|
|
this._columns = [];
|
|
}
|
|
if (options.rows) {
|
|
options.rows = normalizeMembers(options.rows);
|
|
} else if (!options.rows) {
|
|
this._rows = [];
|
|
}
|
|
}
|
|
return options;
|
|
},
|
|
filter: function (val) {
|
|
if (val === undefined) {
|
|
return this._filter;
|
|
}
|
|
this._skipNormalize += 1;
|
|
this._clearAxesData = true;
|
|
this._query({
|
|
filter: val,
|
|
page: 1
|
|
});
|
|
},
|
|
expandColumn: function (path) {
|
|
this._expandPath(path, 'columns');
|
|
},
|
|
expandRow: function (path) {
|
|
this._expandPath(path, 'rows');
|
|
},
|
|
success: function (data) {
|
|
var originalData;
|
|
if (this.cubeBuilder) {
|
|
originalData = (this.reader.data(data) || []).slice(0);
|
|
}
|
|
DataSource.fn.success.call(this, data);
|
|
if (originalData) {
|
|
this._pristineData = originalData;
|
|
}
|
|
},
|
|
_processResult: function (data, axes) {
|
|
if (this.cubeBuilder) {
|
|
var processedData = this.cubeBuilder.process(data, this._requestData);
|
|
data = processedData.data;
|
|
axes = processedData.axes;
|
|
}
|
|
var columnIndexes, rowIndexes;
|
|
var tuples, resultAxis, measures, axisToSkip;
|
|
var columnDescriptors = this.columns();
|
|
var rowDescriptors = this.rows();
|
|
var hasColumnTuples = axes.columns && axes.columns.tuples;
|
|
if (!columnDescriptors.length && rowDescriptors.length && hasColumnTuples && (this._rowMeasures().length || !this.measures().length)) {
|
|
axes = {
|
|
columns: {},
|
|
rows: axes.columns
|
|
};
|
|
}
|
|
if (!columnDescriptors.length && !rowDescriptors.length && this.measuresAxis() === 'rows' && hasColumnTuples) {
|
|
axes = {
|
|
columns: {},
|
|
rows: axes.columns
|
|
};
|
|
}
|
|
this._axes = {
|
|
columns: normalizeAxis(this._axes.columns),
|
|
rows: normalizeAxis(this._axes.rows)
|
|
};
|
|
axes = {
|
|
columns: normalizeAxis(axes.columns),
|
|
rows: normalizeAxis(axes.rows)
|
|
};
|
|
columnIndexes = this._normalizeTuples(axes.columns.tuples, this._axes.columns.tuples, columnDescriptors, this._columnMeasures());
|
|
rowIndexes = this._normalizeTuples(axes.rows.tuples, this._axes.rows.tuples, rowDescriptors, this._rowMeasures());
|
|
this._skipNormalize -= 1;
|
|
if (!this.cubeBuilder) {
|
|
data = this._normalizeData({
|
|
columnsLength: axes.columns.tuples.length,
|
|
rowsLength: axes.rows.tuples.length,
|
|
columnIndexes: columnIndexes,
|
|
rowIndexes: rowIndexes,
|
|
data: data
|
|
});
|
|
}
|
|
if (this._lastExpanded == 'rows') {
|
|
tuples = axes.columns.tuples;
|
|
measures = this._columnMeasures();
|
|
resultAxis = validateAxis(axes.columns, this._axes.columns, measures);
|
|
if (resultAxis) {
|
|
axisToSkip = 'columns';
|
|
axes.columns = resultAxis;
|
|
adjustDataByColumn(tuples, resultAxis.tuples, axes.rows.tuples.length, measures, data);
|
|
if (!this.cubeBuilder) {
|
|
data = this._normalizeData({
|
|
columnsLength: membersCount(axes.columns.tuples, measures),
|
|
rowsLength: axes.rows.tuples.length,
|
|
data: data
|
|
});
|
|
}
|
|
}
|
|
} else if (this._lastExpanded == 'columns') {
|
|
tuples = axes.rows.tuples;
|
|
measures = this._rowMeasures();
|
|
resultAxis = validateAxis(axes.rows, this._axes.rows, measures);
|
|
if (resultAxis) {
|
|
axisToSkip = 'rows';
|
|
axes.rows = resultAxis;
|
|
adjustDataByRow(tuples, resultAxis.tuples, axes.columns.tuples.length, measures, data);
|
|
if (!this.cubeBuilder) {
|
|
data = this._normalizeData({
|
|
columnsLength: membersCount(axes.rows.tuples, measures),
|
|
rowsLength: axes.columns.tuples.length,
|
|
data: data
|
|
});
|
|
}
|
|
}
|
|
}
|
|
this._lastExpanded = null;
|
|
var result = this._mergeAxes(axes, data, axisToSkip);
|
|
this._axes = result.axes;
|
|
return result.data;
|
|
},
|
|
_readData: function (data) {
|
|
var axes = this.reader.axes(data);
|
|
var newData = this.reader.data(data);
|
|
if (this.cubeBuilder) {
|
|
this._rawData = newData;
|
|
}
|
|
return this._processResult(newData, axes);
|
|
},
|
|
_createTuple: function (tuple, measure, buildRoot) {
|
|
var members = tuple.members;
|
|
var length = members.length;
|
|
var root = { members: [] };
|
|
var levelName, levelNum;
|
|
var name, parentName;
|
|
var hasChildren;
|
|
var hierarchy;
|
|
var caption;
|
|
var member;
|
|
var idx = 0;
|
|
if (measure) {
|
|
length -= 1;
|
|
}
|
|
for (; idx < length; idx++) {
|
|
member = members[idx];
|
|
levelNum = Number(member.levelNum);
|
|
name = member.name;
|
|
parentName = member.parentName;
|
|
caption = member.caption || name;
|
|
hasChildren = member.hasChildren;
|
|
hierarchy = member.hierarchy;
|
|
levelName = member.levelName;
|
|
if (buildRoot) {
|
|
caption = 'All';
|
|
if (levelNum === 0) {
|
|
parentName = member.name;
|
|
} else {
|
|
levelNum -= 1;
|
|
}
|
|
hasChildren = true;
|
|
name = hierarchy = levelName = parentName;
|
|
}
|
|
root.members.push({
|
|
name: name,
|
|
children: [],
|
|
caption: caption,
|
|
levelName: levelName,
|
|
levelNum: levelNum.toString(),
|
|
hasChildren: hasChildren,
|
|
hierarchy: hierarchy,
|
|
parentName: !buildRoot ? parentName : ''
|
|
});
|
|
}
|
|
if (measure) {
|
|
root.members.push({
|
|
name: measure.name,
|
|
children: []
|
|
});
|
|
}
|
|
return root;
|
|
},
|
|
_hasRoot: function (target, source, descriptors) {
|
|
if (source.length) {
|
|
return findExistingTuple(source, target).tuple;
|
|
}
|
|
var members = target.members;
|
|
var member;
|
|
var descriptor;
|
|
var isRoot = true;
|
|
var levelNum;
|
|
for (var idx = 0, length = members.length; idx < length; idx++) {
|
|
member = members[idx];
|
|
levelNum = Number(member.levelNum) || 0;
|
|
descriptor = descriptors[idx];
|
|
if (!(levelNum === 0 || descriptor && member.name === getName(descriptor))) {
|
|
isRoot = false;
|
|
break;
|
|
}
|
|
}
|
|
return isRoot;
|
|
},
|
|
_mergeAxes: function (sourceAxes, data, axisToSkip) {
|
|
var columnMeasures = this._columnMeasures();
|
|
var rowMeasures = this._rowMeasures();
|
|
var axes = this.axes();
|
|
var startIndex, tuples;
|
|
var oldRowsLength = membersCount(axes.rows.tuples, rowMeasures);
|
|
var newRowsLength = sourceAxes.rows.tuples.length;
|
|
var oldColumnsLength = membersCount(axes.columns.tuples, columnMeasures);
|
|
var newColumnsLength = sourceAxes.columns.tuples.length;
|
|
if (axisToSkip == 'columns') {
|
|
newColumnsLength = oldColumnsLength;
|
|
tuples = sourceAxes.columns.tuples;
|
|
} else {
|
|
tuples = parseSource(sourceAxes.columns.tuples, columnMeasures);
|
|
data = prepareDataOnColumns(tuples, data);
|
|
}
|
|
var mergedColumns = mergeTuples(axes.columns.tuples, tuples, columnMeasures);
|
|
if (axisToSkip == 'rows') {
|
|
newRowsLength = membersCount(sourceAxes.rows.tuples, rowMeasures);
|
|
tuples = sourceAxes.rows.tuples;
|
|
} else {
|
|
tuples = parseSource(sourceAxes.rows.tuples, rowMeasures);
|
|
data = prepareDataOnRows(tuples, data);
|
|
}
|
|
var mergedRows = mergeTuples(axes.rows.tuples, tuples, rowMeasures);
|
|
axes.columns.tuples = mergedColumns.tuples;
|
|
axes.rows.tuples = mergedRows.tuples;
|
|
if (oldColumnsLength !== membersCount(axes.columns.tuples, columnMeasures)) {
|
|
startIndex = mergedColumns.index + findDataIndex(mergedColumns.parsedRoot, mergedColumns.memberIndex, columnMeasures);
|
|
var offset = oldColumnsLength + newColumnsLength;
|
|
data = this._mergeColumnData(data, startIndex, newRowsLength, newColumnsLength, offset);
|
|
} else if (oldRowsLength !== membersCount(axes.rows.tuples, rowMeasures)) {
|
|
startIndex = mergedRows.index + findDataIndex(mergedRows.parsedRoot, mergedRows.memberIndex, rowMeasures);
|
|
data = this._mergeRowData(data, startIndex, newRowsLength, newColumnsLength);
|
|
}
|
|
if (axes.columns.tuples.length === 0 && axes.rows.tuples.length === 0) {
|
|
data = [];
|
|
}
|
|
return {
|
|
axes: axes,
|
|
data: data
|
|
};
|
|
},
|
|
_mergeColumnData: function (newData, columnIndex, rowsLength, columnsLength, offset) {
|
|
var data = this.data().toJSON();
|
|
var rowIndex, index, drop = 0, toAdd;
|
|
var columnMeasures = Math.max(this._columnMeasures().length, 1);
|
|
rowsLength = Math.max(rowsLength, 1);
|
|
if (data.length > 0) {
|
|
drop = columnMeasures;
|
|
offset -= columnMeasures;
|
|
}
|
|
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
|
|
index = columnIndex + rowIndex * offset;
|
|
toAdd = newData.splice(0, columnsLength);
|
|
toAdd.splice(0, drop);
|
|
[].splice.apply(data, [
|
|
index,
|
|
0
|
|
].concat(toAdd));
|
|
}
|
|
return data;
|
|
},
|
|
_mergeRowData: function (newData, rowIndex, rowsLength, columnsLength) {
|
|
var data = this.data().toJSON();
|
|
var idx, dataIndex, toAdd;
|
|
var rowMeasures = Math.max(this._rowMeasures().length, 1);
|
|
columnsLength = Math.max(columnsLength, 1);
|
|
if (data.length > 0) {
|
|
rowsLength -= rowMeasures;
|
|
newData.splice(0, columnsLength * rowMeasures);
|
|
}
|
|
for (idx = 0; idx < rowsLength; idx++) {
|
|
toAdd = newData.splice(0, columnsLength);
|
|
dataIndex = rowIndex * columnsLength + idx * columnsLength;
|
|
[].splice.apply(data, [
|
|
dataIndex,
|
|
0
|
|
].concat(toAdd));
|
|
}
|
|
return data;
|
|
},
|
|
_columnMeasures: function () {
|
|
var measures = this.measures();
|
|
var columnMeasures = [];
|
|
if (this.measuresAxis() === 'columns') {
|
|
if (this.columns().length === 0) {
|
|
columnMeasures = measures;
|
|
} else if (measures.length > 1) {
|
|
columnMeasures = measures;
|
|
}
|
|
}
|
|
return columnMeasures;
|
|
},
|
|
_rowMeasures: function () {
|
|
var measures = this.measures();
|
|
var rowMeasures = [];
|
|
if (this.measuresAxis() === 'rows') {
|
|
if (this.rows().length === 0) {
|
|
rowMeasures = measures;
|
|
} else if (measures.length > 1) {
|
|
rowMeasures = measures;
|
|
}
|
|
}
|
|
return rowMeasures;
|
|
},
|
|
_updateLocalData: function (data, state) {
|
|
if (this.cubeBuilder) {
|
|
if (state) {
|
|
this._requestData = state;
|
|
}
|
|
data = this._processResult(data);
|
|
}
|
|
this._data = this._observe(data);
|
|
this._ranges = [];
|
|
this._addRange(this._data);
|
|
this._total = this._data.length;
|
|
this._pristineTotal = this._total;
|
|
this._process(this._data);
|
|
},
|
|
data: function (value) {
|
|
var that = this;
|
|
if (value !== undefined) {
|
|
this._pristineData = value.slice(0);
|
|
this._updateLocalData(value, {
|
|
columns: this.columns(),
|
|
rows: this.rows(),
|
|
measures: this.measures()
|
|
});
|
|
} else {
|
|
return that._data;
|
|
}
|
|
},
|
|
_normalizeTuples: function (tuples, source, descriptors, measures) {
|
|
var length = measures.length || 1;
|
|
var idx = 0;
|
|
var roots = [];
|
|
var indexes = {};
|
|
var measureIdx = 0;
|
|
var tuple, memberIdx, last;
|
|
if (!tuples.length) {
|
|
return;
|
|
}
|
|
if (this._skipNormalize <= 0 && !this._hasRoot(tuples[0], source, descriptors)) {
|
|
this._skipNormalize = 0;
|
|
for (; idx < length; idx++) {
|
|
roots.push(this._createTuple(tuples[0], measures[idx], true));
|
|
indexes[idx] = idx;
|
|
}
|
|
tuples.splice.apply(tuples, [
|
|
0,
|
|
tuples.length
|
|
].concat(roots).concat(tuples));
|
|
idx = length;
|
|
}
|
|
if (measures.length) {
|
|
last = tuple = tuples[idx];
|
|
memberIdx = tuple.members.length - 1;
|
|
while (tuple) {
|
|
if (measureIdx >= length) {
|
|
measureIdx = 0;
|
|
}
|
|
if (tuple.members[memberIdx].name !== measures[measureIdx].name) {
|
|
tuples.splice(idx, 0, this._createTuple(tuple, measures[measureIdx]));
|
|
indexes[idx] = idx;
|
|
}
|
|
idx += 1;
|
|
measureIdx += 1;
|
|
tuple = tuples[idx];
|
|
if (length > measureIdx && (!tuple || tupleName(last, memberIdx - 1) !== tupleName(tuple, memberIdx - 1))) {
|
|
for (; measureIdx < length; measureIdx++) {
|
|
tuples.splice(idx, 0, this._createTuple(last, measures[measureIdx]));
|
|
indexes[idx] = idx;
|
|
idx += 1;
|
|
}
|
|
tuple = tuples[idx];
|
|
}
|
|
last = tuple;
|
|
}
|
|
}
|
|
return indexes;
|
|
},
|
|
_addMissingDataItems: function (result, metadata) {
|
|
while (metadata.rowIndexes[parseInt(result.length / metadata.columnsLength, 10)] !== undefined) {
|
|
for (var idx = 0; idx < metadata.columnsLength; idx++) {
|
|
result = addEmptyDataItem(result);
|
|
}
|
|
}
|
|
while (metadata.columnIndexes[result.length % metadata.columnsLength] !== undefined) {
|
|
result = addEmptyDataItem(result);
|
|
}
|
|
return result;
|
|
},
|
|
_normalizeOrdinals: function (result, dataItem, metadata) {
|
|
var lastOrdinal = metadata.lastOrdinal;
|
|
if (!dataItem) {
|
|
return addEmptyDataItem(result);
|
|
}
|
|
if (dataItem.ordinal - lastOrdinal > 1) {
|
|
lastOrdinal += 1;
|
|
while (lastOrdinal < dataItem.ordinal && result.length < metadata.length) {
|
|
result = this._addMissingDataItems(addEmptyDataItem(result), metadata);
|
|
lastOrdinal += 1;
|
|
}
|
|
}
|
|
dataItem.ordinal = result.length;
|
|
result[result.length] = dataItem;
|
|
return result;
|
|
},
|
|
_normalizeData: function (options) {
|
|
var data = options.data;
|
|
var dataIdx = 0;
|
|
var dataItem;
|
|
var result = [];
|
|
var lastOrdinal;
|
|
var length;
|
|
options.lastOrdinal = 0;
|
|
options.columnIndexes = options.columnIndexes || {};
|
|
options.rowIndexes = options.rowIndexes || {};
|
|
options.columnsLength = options.columnsLength || 1;
|
|
options.rowsLength = options.rowsLength || 1;
|
|
options.length = options.columnsLength * options.rowsLength;
|
|
length = options.length;
|
|
if (data.length === length) {
|
|
return data;
|
|
}
|
|
while (result.length < length) {
|
|
dataItem = data[dataIdx++];
|
|
if (dataItem) {
|
|
lastOrdinal = dataItem.ordinal;
|
|
}
|
|
result = this._normalizeOrdinals(this._addMissingDataItems(result, options), dataItem, options);
|
|
options.lastOrdinal = lastOrdinal;
|
|
}
|
|
return result;
|
|
},
|
|
discover: function (options, converter) {
|
|
var that = this, transport = that.transport;
|
|
return $.Deferred(function (deferred) {
|
|
transport.discover(extend({
|
|
success: function (response) {
|
|
response = that.reader.parse(response);
|
|
if (that._handleCustomErrors(response)) {
|
|
return;
|
|
}
|
|
if (converter) {
|
|
response = converter(response);
|
|
}
|
|
deferred.resolve(response);
|
|
},
|
|
error: function (response, status, error) {
|
|
deferred.reject(response);
|
|
that.error(response, status, error);
|
|
}
|
|
}, options));
|
|
}).promise().done(function () {
|
|
that.trigger('schemaChange');
|
|
});
|
|
},
|
|
schemaMeasures: function () {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaMeasures',
|
|
restrictions: {
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube()
|
|
}
|
|
}
|
|
}, function (response) {
|
|
return that.reader.measures(response);
|
|
});
|
|
},
|
|
schemaKPIs: function () {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaKPIs',
|
|
restrictions: {
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube()
|
|
}
|
|
}
|
|
}, function (response) {
|
|
return that.reader.kpis(response);
|
|
});
|
|
},
|
|
schemaDimensions: function () {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaDimensions',
|
|
restrictions: {
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube()
|
|
}
|
|
}
|
|
}, function (response) {
|
|
return that.reader.dimensions(response);
|
|
});
|
|
},
|
|
schemaHierarchies: function (dimensionName) {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaHierarchies',
|
|
restrictions: {
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube(),
|
|
dimensionUniqueName: dimensionName
|
|
}
|
|
}
|
|
}, function (response) {
|
|
return that.reader.hierarchies(response);
|
|
});
|
|
},
|
|
schemaLevels: function (hierarchyName) {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaLevels',
|
|
restrictions: {
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube(),
|
|
hierarchyUniqueName: hierarchyName
|
|
}
|
|
}
|
|
}, function (response) {
|
|
return that.reader.levels(response);
|
|
});
|
|
},
|
|
schemaCubes: function () {
|
|
var that = this;
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaCubes',
|
|
restrictions: { catalogName: that.transport.catalog() }
|
|
}
|
|
}, function (response) {
|
|
return that.reader.cubes(response);
|
|
});
|
|
},
|
|
schemaCatalogs: function () {
|
|
var that = this;
|
|
return that.discover({ data: { command: 'schemaCatalogs' } }, function (response) {
|
|
return that.reader.catalogs(response);
|
|
});
|
|
},
|
|
schemaMembers: function (restrictions) {
|
|
var that = this;
|
|
var success = function (restrictions) {
|
|
return function (response) {
|
|
return that.reader.members(response, restrictions);
|
|
};
|
|
}(restrictions);
|
|
return that.discover({
|
|
data: {
|
|
command: 'schemaMembers',
|
|
restrictions: extend({
|
|
catalogName: that.transport.catalog(),
|
|
cubeName: that.transport.cube()
|
|
}, restrictions)
|
|
}
|
|
}, success);
|
|
},
|
|
_params: function (data) {
|
|
if (this._clearAxesData) {
|
|
this._axes = {};
|
|
this._data = this._observe([]);
|
|
this._clearAxesData = false;
|
|
this.trigger(STATERESET);
|
|
}
|
|
var options = DataSource.fn._params.call(this, data);
|
|
options = extend({
|
|
measures: this.measures(),
|
|
measuresAxis: this.measuresAxis(),
|
|
columns: this.columns(),
|
|
rows: this.rows()
|
|
}, options);
|
|
if (this.cubeBuilder) {
|
|
this._requestData = options;
|
|
}
|
|
return options;
|
|
}
|
|
});
|
|
function addEmptyDataItem(result) {
|
|
result[result.length] = {
|
|
value: '',
|
|
fmtValue: '',
|
|
ordinal: result.length
|
|
};
|
|
return result;
|
|
}
|
|
function validateAxis(newAxis, axis, measures) {
|
|
if (newAxis.tuples.length < membersCount(axis.tuples, measures)) {
|
|
return axis;
|
|
}
|
|
return;
|
|
}
|
|
function adjustDataByColumn(sourceTuples, targetTuples, rowsLength, measures, data) {
|
|
var columnIdx, rowIdx, dataIdx;
|
|
var columnsLength = sourceTuples.length;
|
|
var targetColumnsLength = membersCount(targetTuples, measures);
|
|
var measuresLength = measures.length || 1;
|
|
for (rowIdx = 0; rowIdx < rowsLength; rowIdx++) {
|
|
for (columnIdx = 0; columnIdx < columnsLength; columnIdx++) {
|
|
dataIdx = tupleIndex(sourceTuples[columnIdx], targetTuples) * measuresLength;
|
|
dataIdx += columnIdx % measuresLength;
|
|
data[rowIdx * columnsLength + columnIdx].ordinal = rowIdx * targetColumnsLength + dataIdx;
|
|
}
|
|
}
|
|
}
|
|
function adjustDataByRow(sourceTuples, targetTuples, columnsLength, measures, data) {
|
|
var columnIdx, rowIdx, dataIdx;
|
|
var rowsLength = sourceTuples.length;
|
|
var measuresLength = measures.length || 1;
|
|
for (rowIdx = 0; rowIdx < rowsLength; rowIdx++) {
|
|
dataIdx = tupleIndex(sourceTuples[rowIdx], targetTuples);
|
|
dataIdx *= measuresLength;
|
|
dataIdx += rowIdx % measuresLength;
|
|
for (columnIdx = 0; columnIdx < columnsLength; columnIdx++) {
|
|
data[rowIdx * columnsLength + columnIdx].ordinal = dataIdx * columnsLength + columnIdx;
|
|
}
|
|
}
|
|
}
|
|
function tupleIndex(tuple, collection) {
|
|
return findExistingTuple(collection, tuple).index;
|
|
}
|
|
function membersCount(tuples, measures) {
|
|
if (!tuples.length) {
|
|
return 0;
|
|
}
|
|
var queue = tuples.slice();
|
|
var current = queue.shift();
|
|
var result = 1;
|
|
while (current) {
|
|
if (current.members) {
|
|
[].push.apply(queue, current.members);
|
|
} else if (current.children) {
|
|
if (!current.measure) {
|
|
result += current.children.length;
|
|
}
|
|
[].push.apply(queue, current.children);
|
|
}
|
|
current = queue.shift();
|
|
}
|
|
if (measures.length) {
|
|
result = result * measures.length;
|
|
}
|
|
return result;
|
|
}
|
|
function normalizeAxis(axis) {
|
|
if (!axis) {
|
|
axis = { tuples: [] };
|
|
}
|
|
if (!axis.tuples) {
|
|
axis.tuples = [];
|
|
}
|
|
return axis;
|
|
}
|
|
function findDataIndex(tuple, memberIndex, measures) {
|
|
if (!tuple) {
|
|
return 0;
|
|
}
|
|
var measuresLength = Math.max(measures.length, 1);
|
|
var tuples = tuple.members.slice(0, memberIndex);
|
|
var counter = measuresLength;
|
|
var current = tuples.shift();
|
|
if (measuresLength > 1) {
|
|
measuresLength += 1;
|
|
}
|
|
while (current) {
|
|
if (current.name === MEASURES) {
|
|
counter += measuresLength;
|
|
} else if (current.children) {
|
|
[].push.apply(tuples, current.children);
|
|
} else {
|
|
counter++;
|
|
[].push.apply(tuples, current.members);
|
|
}
|
|
current = tuples.shift();
|
|
}
|
|
return counter;
|
|
}
|
|
function mergeTuples(target, source, measures) {
|
|
if (!source[0]) {
|
|
return {
|
|
parsedRoot: null,
|
|
tuples: target,
|
|
memberIndex: 0,
|
|
index: 0
|
|
};
|
|
}
|
|
var result = findExistingTuple(target, source[0]);
|
|
if (!result.tuple) {
|
|
return {
|
|
parsedRoot: null,
|
|
tuples: source,
|
|
memberIndex: 0,
|
|
index: 0
|
|
};
|
|
}
|
|
var targetMembers = result.tuple.members;
|
|
var sourceMembers = source[0].members;
|
|
var memberIndex = -1;
|
|
if (targetMembers.length !== sourceMembers.length) {
|
|
return {
|
|
parsedRoot: null,
|
|
tuples: source,
|
|
memberIndex: 0,
|
|
index: 0
|
|
};
|
|
}
|
|
for (var idx = 0, length = targetMembers.length; idx < length; idx++) {
|
|
if (!targetMembers[idx].measure && sourceMembers[idx].children[0]) {
|
|
if (memberIndex == -1 && sourceMembers[idx].children.length) {
|
|
memberIndex = idx;
|
|
}
|
|
targetMembers[idx].children = sourceMembers[idx].children;
|
|
}
|
|
}
|
|
measures = Math.max(measures.length, 1);
|
|
return {
|
|
parsedRoot: result.tuple,
|
|
index: result.index * measures,
|
|
memberIndex: memberIndex,
|
|
tuples: target
|
|
};
|
|
}
|
|
function equalTuples(first, second) {
|
|
var equal = true;
|
|
var idx, length;
|
|
first = first.members;
|
|
second = second.members;
|
|
for (idx = 0, length = first.length; idx < length; idx++) {
|
|
if (first[idx].measure || second[idx].measure) {
|
|
continue;
|
|
}
|
|
equal = equal && getName(first[idx]) === getName(second[idx]);
|
|
}
|
|
return equal;
|
|
}
|
|
function findExistingTuple(tuples, toFind) {
|
|
var idx, length, tuple, found, counter = 0;
|
|
var memberIndex, membersLength, member;
|
|
for (idx = 0, length = tuples.length; idx < length; idx++) {
|
|
tuple = tuples[idx];
|
|
if (equalTuples(tuple, toFind)) {
|
|
return {
|
|
tuple: tuple,
|
|
index: counter
|
|
};
|
|
}
|
|
counter++;
|
|
for (memberIndex = 0, membersLength = tuple.members.length; memberIndex < membersLength; memberIndex++) {
|
|
member = tuple.members[memberIndex];
|
|
if (member.measure) {
|
|
continue;
|
|
}
|
|
found = findExistingTuple(member.children, toFind);
|
|
counter += found.index;
|
|
if (found.tuple) {
|
|
return {
|
|
tuple: found.tuple,
|
|
index: counter
|
|
};
|
|
}
|
|
}
|
|
}
|
|
return { index: counter };
|
|
}
|
|
function addMembers(members, map) {
|
|
var member, i, len, path = '';
|
|
for (i = 0, len = members.length; i < len; i++) {
|
|
member = members[i];
|
|
path += member.name;
|
|
if (!map[path]) {
|
|
map[path] = member;
|
|
}
|
|
}
|
|
}
|
|
function findParentMember(tuple, map) {
|
|
var members = tuple.members;
|
|
var i, len, member, path = '';
|
|
var parentPath = '';
|
|
var parentMember;
|
|
for (i = 0, len = members.length; i < len; i++) {
|
|
member = members[i];
|
|
if (parentMember) {
|
|
if (map[path + member.name]) {
|
|
path += member.name;
|
|
parentMember = map[path];
|
|
continue;
|
|
} else if (map[path + member.parentName]) {
|
|
return map[path + member.parentName];
|
|
} else if (map[parentPath + member.parentName]) {
|
|
return map[parentPath + member.parentName];
|
|
} else {
|
|
return map[parentPath];
|
|
}
|
|
}
|
|
path += member.name;
|
|
parentMember = map[member.parentName];
|
|
if (!parentMember) {
|
|
parentMember = map[path];
|
|
if (!parentMember) {
|
|
return null;
|
|
}
|
|
}
|
|
if (parentMember) {
|
|
parentPath += parentMember.name;
|
|
}
|
|
}
|
|
return parentMember;
|
|
}
|
|
function measurePosition(tuple, measures) {
|
|
if (measures.length === 0) {
|
|
return -1;
|
|
}
|
|
var measure = measures[0];
|
|
var members = tuple.members;
|
|
for (var idx = 0, len = members.length; idx < len; idx++) {
|
|
if (members[idx].name == measure.name) {
|
|
return idx;
|
|
}
|
|
}
|
|
}
|
|
function normalizeTupleMeasures(tuple, index) {
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
var member = {
|
|
name: MEASURES,
|
|
measure: true,
|
|
children: [$.extend({
|
|
members: [],
|
|
dataIndex: tuple.dataIndex
|
|
}, tuple.members[index])]
|
|
};
|
|
tuple.members.splice(index, 1, member);
|
|
tuple.dataIndex = undefined;
|
|
}
|
|
function parseSource(tuples, measures) {
|
|
if (tuples.length < 1) {
|
|
return [];
|
|
}
|
|
var result = [];
|
|
var map = {};
|
|
var measureIndex = measurePosition(tuples[0], measures);
|
|
for (var i = 0; i < tuples.length; i++) {
|
|
var tuple = tuples[i];
|
|
tuple.dataIndex = i;
|
|
normalizeTupleMeasures(tuple, measureIndex);
|
|
var parentMember = findParentMember(tuple, map);
|
|
if (parentMember) {
|
|
if (measureIndex < 0 || !parentMember.measure) {
|
|
parentMember.children.push(tuple);
|
|
} else {
|
|
parentMember.children.push(tuple.members[measureIndex].children[0]);
|
|
}
|
|
} else {
|
|
result.push(tuple);
|
|
}
|
|
addMembers(tuple.members, map);
|
|
}
|
|
return result;
|
|
}
|
|
function prepareDataOnRows(tuples, data) {
|
|
if (!tuples || !tuples.length) {
|
|
return data;
|
|
}
|
|
var result = [];
|
|
var indices = buildDataIndices(tuples);
|
|
var rowsLength = indices.length;
|
|
var columnsLength = Math.max(data.length / rowsLength, 1);
|
|
var rowIndex, columnIndex, targetIndex, sourceIndex;
|
|
var calcIndex;
|
|
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
|
|
targetIndex = columnsLength * rowIndex;
|
|
sourceIndex = columnsLength * indices[rowIndex];
|
|
for (columnIndex = 0; columnIndex < columnsLength; columnIndex++) {
|
|
calcIndex = parseInt(sourceIndex + columnIndex, 10);
|
|
result[parseInt(targetIndex + columnIndex, 10)] = data[calcIndex] || {
|
|
value: '',
|
|
fmtValue: '',
|
|
ordinal: calcIndex
|
|
};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function prepareDataOnColumns(tuples, data) {
|
|
if (!tuples || !tuples.length) {
|
|
return data;
|
|
}
|
|
var result = [];
|
|
var indices = buildDataIndices(tuples);
|
|
var columnsLength = indices.length;
|
|
var rowsLength = Math.max(data.length / columnsLength, 1);
|
|
var columnIndex, rowIndex, dataIndex, calcIndex;
|
|
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
|
|
dataIndex = columnsLength * rowIndex;
|
|
for (columnIndex = 0; columnIndex < columnsLength; columnIndex++) {
|
|
calcIndex = indices[columnIndex] + dataIndex;
|
|
result[dataIndex + columnIndex] = data[calcIndex] || {
|
|
value: '',
|
|
fmtValue: '',
|
|
ordinal: calcIndex
|
|
};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function buildDataIndices(tuples) {
|
|
tuples = tuples.slice();
|
|
var result = [];
|
|
var tuple = tuples.shift();
|
|
var idx, length, spliceIndex, children, member;
|
|
while (tuple) {
|
|
if (tuple.dataIndex !== undefined) {
|
|
result.push(tuple.dataIndex);
|
|
}
|
|
spliceIndex = 0;
|
|
for (idx = 0, length = tuple.members.length; idx < length; idx++) {
|
|
member = tuple.members[idx];
|
|
children = member.children;
|
|
if (member.measure) {
|
|
[].splice.apply(tuples, [
|
|
0,
|
|
0
|
|
].concat(children));
|
|
} else {
|
|
[].splice.apply(tuples, [
|
|
spliceIndex,
|
|
0
|
|
].concat(children));
|
|
}
|
|
spliceIndex += children.length;
|
|
}
|
|
tuple = tuples.shift();
|
|
}
|
|
return result;
|
|
}
|
|
PivotDataSource.create = function (options) {
|
|
options = options && options.push ? { data: options } : options;
|
|
var dataSource = options || {}, data = dataSource.data;
|
|
dataSource.data = data;
|
|
if (!(dataSource instanceof PivotDataSource) && dataSource instanceof kendo.data.DataSource) {
|
|
throw new Error('Incorrect DataSource type. Only PivotDataSource instances are supported');
|
|
}
|
|
return dataSource instanceof PivotDataSource ? dataSource : new PivotDataSource(dataSource);
|
|
};
|
|
function baseHierarchyPath(memberName) {
|
|
var parts = memberName.split('.');
|
|
if (parts.length > 2) {
|
|
return parts[0] + '.' + parts[1];
|
|
}
|
|
return memberName;
|
|
}
|
|
function expandMemberDescriptor(names, sort) {
|
|
var idx = names.length - 1;
|
|
var name = names[idx];
|
|
var sortDescriptor;
|
|
sortDescriptor = sortDescriptorForMember(sort, name);
|
|
if (sortDescriptor && sortDescriptor.dir) {
|
|
name = 'ORDER(' + name + '.Children,' + sortDescriptor.field + '.CurrentMember.MEMBER_CAPTION,' + sortDescriptor.dir + ')';
|
|
} else {
|
|
name += '.Children';
|
|
}
|
|
names[idx] = name;
|
|
return names;
|
|
}
|
|
function sortDescriptorForMember(sort, member) {
|
|
for (var idx = 0, length = sort.length; idx < length; idx++) {
|
|
if (member.indexOf(sort[idx].field) === 0) {
|
|
return sort[idx];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function crossJoin(names) {
|
|
var result = 'CROSSJOIN({';
|
|
var r;
|
|
if (names.length > 2) {
|
|
r = names.pop();
|
|
result += crossJoin(names);
|
|
} else {
|
|
result += names.shift();
|
|
r = names.pop();
|
|
}
|
|
result += '},{';
|
|
result += r;
|
|
result += '})';
|
|
return result;
|
|
}
|
|
function crossJoinCommand(members, measures) {
|
|
var tmp = members.slice(0);
|
|
if (measures.length > 1) {
|
|
tmp.push('{' + measureNames(measures).join(',') + '}');
|
|
}
|
|
return crossJoin(tmp);
|
|
}
|
|
function measureNames(measures) {
|
|
var idx = 0;
|
|
var length = measures.length;
|
|
var result = [];
|
|
var measure;
|
|
for (; idx < length; idx++) {
|
|
measure = measures[idx];
|
|
result.push(measure.name !== undefined ? measure.name : measure);
|
|
}
|
|
return result;
|
|
}
|
|
function getName(name) {
|
|
name = name.name || name;
|
|
if (toString.call(name) === '[object Array]') {
|
|
name = name[name.length - 1];
|
|
}
|
|
return name;
|
|
}
|
|
function getRootNames(members) {
|
|
var length = members.length;
|
|
var names = [];
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
names.push(members[idx].name[0]);
|
|
}
|
|
return names;
|
|
}
|
|
function mapNames(names, rootNames) {
|
|
var name;
|
|
var rootName;
|
|
var j;
|
|
var idx = 0;
|
|
var length = names.length;
|
|
var rootLength = rootNames.length;
|
|
rootNames = rootNames.slice(0);
|
|
for (; idx < length; idx++) {
|
|
name = names[idx];
|
|
for (j = 0; j < rootLength; j++) {
|
|
rootName = baseHierarchyPath(rootNames[j]);
|
|
if (name.indexOf(rootName) !== -1) {
|
|
rootNames[j] = name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
names: rootNames,
|
|
expandedIdx: j,
|
|
uniquePath: rootNames.slice(0, j + 1).join('')
|
|
};
|
|
}
|
|
function parseDescriptors(members) {
|
|
var expanded = [];
|
|
var child = [];
|
|
var root = [];
|
|
var member;
|
|
var j, l;
|
|
var idx = 0;
|
|
var length = members.length;
|
|
var name;
|
|
var hierarchyName;
|
|
var found;
|
|
for (; idx < length; idx++) {
|
|
member = members[idx];
|
|
name = member.name;
|
|
found = false;
|
|
if (toString.call(name) !== '[object Array]') {
|
|
member.name = name = [name];
|
|
}
|
|
if (name.length > 1) {
|
|
child.push(member);
|
|
} else {
|
|
hierarchyName = baseHierarchyPath(name[0]);
|
|
for (j = 0, l = root.length; j < l; j++) {
|
|
if (root[j].name[0].indexOf(hierarchyName) === 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
root.push(member);
|
|
}
|
|
if (member.expand) {
|
|
expanded.push(member);
|
|
}
|
|
}
|
|
}
|
|
expanded = expanded.concat(child);
|
|
return {
|
|
root: root,
|
|
expanded: expanded
|
|
};
|
|
}
|
|
function serializeMembers(members, measures, sort) {
|
|
var command = '';
|
|
members = members || [];
|
|
var expanded = parseDescriptors(members);
|
|
var root = expanded.root;
|
|
var rootNames = getRootNames(root);
|
|
var crossJoinCommands = [];
|
|
expanded = expanded.expanded;
|
|
var length = expanded.length;
|
|
var idx = 0;
|
|
var memberName;
|
|
var names = [];
|
|
if (rootNames.length > 1 || measures.length > 1) {
|
|
crossJoinCommands.push(crossJoinCommand(rootNames, measures));
|
|
for (; idx < length; idx++) {
|
|
memberName = expandMemberDescriptor(expanded[idx].name, sort);
|
|
names = mapNames(memberName, rootNames).names;
|
|
crossJoinCommands.push(crossJoinCommand(names, measures));
|
|
}
|
|
command += crossJoinCommands.join(',');
|
|
} else {
|
|
for (; idx < length; idx++) {
|
|
memberName = expandMemberDescriptor(expanded[idx].name, sort);
|
|
names.push(memberName[0]);
|
|
}
|
|
command += rootNames.concat(names).join(',');
|
|
}
|
|
return command;
|
|
}
|
|
var filterFunctionFormats = {
|
|
contains: ', InStr({0}.CurrentMember.MEMBER_CAPTION,"{1}") > 0',
|
|
doesnotcontain: ', InStr({0}.CurrentMember.MEMBER_CAPTION,"{1}") = 0',
|
|
startswith: ', Left({0}.CurrentMember.MEMBER_CAPTION,Len("{1}"))="{1}"',
|
|
endswith: ', Right({0}.CurrentMember.MEMBER_CAPTION,Len("{1}"))="{1}"',
|
|
eq: ', {0}.CurrentMember.MEMBER_CAPTION = "{1}"',
|
|
neq: ', NOT {0}.CurrentMember.MEMBER_CAPTION = "{1}"'
|
|
};
|
|
function serializeExpression(expression) {
|
|
var command = '';
|
|
var value = expression.value;
|
|
var field = expression.field;
|
|
var operator = expression.operator;
|
|
if (operator == 'in') {
|
|
command += '{';
|
|
command += value;
|
|
command += '}';
|
|
} else {
|
|
command += 'Filter(';
|
|
command += field + '.MEMBERS';
|
|
command += kendo.format(filterFunctionFormats[operator], field, value);
|
|
command += ')';
|
|
}
|
|
return command;
|
|
}
|
|
function serializeFilters(filter, cube) {
|
|
var command = '', current;
|
|
var filters = filter.filters;
|
|
var length = filters.length;
|
|
var idx;
|
|
for (idx = length - 1; idx >= 0; idx--) {
|
|
current = 'SELECT (';
|
|
current += serializeExpression(filters[idx]);
|
|
current += ') ON 0';
|
|
if (idx == length - 1) {
|
|
current += ' FROM [' + cube + ']';
|
|
command = current;
|
|
} else {
|
|
command = current + ' FROM ( ' + command + ' )';
|
|
}
|
|
}
|
|
return command;
|
|
}
|
|
function serializeOptions(parentTagName, options, capitalize) {
|
|
var result = '';
|
|
if (options) {
|
|
result += '<' + parentTagName + '>';
|
|
var value;
|
|
for (var key in options) {
|
|
value = options[key];
|
|
if (capitalize) {
|
|
key = key.replace(/([A-Z]+(?=$|[A-Z][a-z])|[A-Z]?[a-z]+)/g, '$1_').toUpperCase().replace(/_$/, '');
|
|
}
|
|
result += '<' + key + '>' + value + '</' + key + '>';
|
|
}
|
|
result += '</' + parentTagName + '>';
|
|
} else {
|
|
result += '<' + parentTagName + '/>';
|
|
}
|
|
return result;
|
|
}
|
|
var xmlaDiscoverCommands = {
|
|
schemaCubes: 'MDSCHEMA_CUBES',
|
|
schemaCatalogs: 'DBSCHEMA_CATALOGS',
|
|
schemaMeasures: 'MDSCHEMA_MEASURES',
|
|
schemaDimensions: 'MDSCHEMA_DIMENSIONS',
|
|
schemaHierarchies: 'MDSCHEMA_HIERARCHIES',
|
|
schemaLevels: 'MDSCHEMA_LEVELS',
|
|
schemaMembers: 'MDSCHEMA_MEMBERS',
|
|
schemaKPIs: 'MDSCHEMA_KPIS'
|
|
};
|
|
var convertersMap = {
|
|
read: function (options) {
|
|
var command = '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Header/><Body><Execute xmlns="urn:schemas-microsoft-com:xml-analysis"><Command><Statement>';
|
|
command += 'SELECT NON EMPTY {';
|
|
var columns = options.columns || [];
|
|
var rows = options.rows || [];
|
|
var measures = options.measures || [];
|
|
var measuresRowAxis = options.measuresAxis === 'rows';
|
|
var sort = options.sort || [];
|
|
if (!columns.length && rows.length && (!measures.length || measures.length && measuresRowAxis)) {
|
|
columns = rows;
|
|
rows = [];
|
|
measuresRowAxis = false;
|
|
}
|
|
if (!columns.length && !rows.length) {
|
|
measuresRowAxis = false;
|
|
}
|
|
if (columns.length) {
|
|
command += serializeMembers(columns, !measuresRowAxis ? measures : [], sort);
|
|
} else if (measures.length && !measuresRowAxis) {
|
|
command += measureNames(measures).join(',');
|
|
}
|
|
command += '} DIMENSION PROPERTIES CHILDREN_CARDINALITY, PARENT_UNIQUE_NAME ON COLUMNS';
|
|
if (rows.length || measuresRowAxis && measures.length > 1) {
|
|
command += ', NON EMPTY {';
|
|
if (rows.length) {
|
|
command += serializeMembers(rows, measuresRowAxis ? measures : [], sort);
|
|
} else {
|
|
command += measureNames(measures).join(',');
|
|
}
|
|
command += '} DIMENSION PROPERTIES CHILDREN_CARDINALITY, PARENT_UNIQUE_NAME ON ROWS';
|
|
}
|
|
if (options.filter) {
|
|
command += ' FROM ';
|
|
command += '(';
|
|
command += serializeFilters(options.filter, options.connection.cube);
|
|
command += ')';
|
|
} else {
|
|
command += ' FROM [' + options.connection.cube + ']';
|
|
}
|
|
if (measures.length == 1 && columns.length) {
|
|
command += ' WHERE (' + measureNames(measures).join(',') + ')';
|
|
}
|
|
command += '</Statement></Command><Properties><PropertyList><Catalog>' + options.connection.catalog + '</Catalog><Format>Multidimensional</Format></PropertyList></Properties></Execute></Body></Envelope>';
|
|
return command.replace(/\&/g, '&');
|
|
},
|
|
discover: function (options) {
|
|
options = options || {};
|
|
var command = '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Header/><Body><Discover xmlns="urn:schemas-microsoft-com:xml-analysis">';
|
|
command += '<RequestType>' + (xmlaDiscoverCommands[options.command] || options.command) + '</RequestType>';
|
|
command += '<Restrictions>' + serializeOptions('RestrictionList', options.restrictions, true) + '</Restrictions>';
|
|
if (options.connection && options.connection.catalog) {
|
|
options.properties = $.extend({}, { Catalog: options.connection.catalog }, options.properties);
|
|
}
|
|
command += '<Properties>' + serializeOptions('PropertyList', options.properties) + '</Properties>';
|
|
command += '</Discover></Body></Envelope>';
|
|
return command;
|
|
}
|
|
};
|
|
var XmlaTransport = kendo.data.RemoteTransport.extend({
|
|
init: function (options) {
|
|
var originalOptions = options;
|
|
options = this.options = extend(true, {}, this.options, options);
|
|
kendo.data.RemoteTransport.call(this, options);
|
|
if (isFunction(originalOptions.discover)) {
|
|
this.discover = originalOptions.discover;
|
|
} else if (typeof originalOptions.discover === 'string') {
|
|
this.options.discover = { url: originalOptions.discover };
|
|
} else if (!originalOptions.discover) {
|
|
this.options.discover = this.options.read;
|
|
}
|
|
},
|
|
setup: function (options, type) {
|
|
options.data = options.data || {};
|
|
$.extend(true, options.data, { connection: this.options.connection });
|
|
return kendo.data.RemoteTransport.fn.setup.call(this, options, type);
|
|
},
|
|
options: {
|
|
read: {
|
|
dataType: 'text',
|
|
contentType: 'text/xml',
|
|
type: 'POST'
|
|
},
|
|
discover: {
|
|
dataType: 'text',
|
|
contentType: 'text/xml',
|
|
type: 'POST'
|
|
},
|
|
parameterMap: function (options, type) {
|
|
return convertersMap[type](options, type);
|
|
}
|
|
},
|
|
discover: function (options) {
|
|
return $.ajax(this.setup(options, 'discover'));
|
|
}
|
|
});
|
|
function asArray(object) {
|
|
if (object == null) {
|
|
return [];
|
|
}
|
|
var type = toString.call(object);
|
|
if (type !== '[object Array]') {
|
|
return [object];
|
|
}
|
|
return object;
|
|
}
|
|
function translateAxis(axis) {
|
|
var result = { tuples: [] };
|
|
var tuples = asArray(kendo.getter('Tuples.Tuple', true)(axis));
|
|
var captionGetter = kendo.getter('Caption[\'#text\']');
|
|
var unameGetter = kendo.getter('UName[\'#text\']');
|
|
var levelNameGetter = kendo.getter('LName[\'#text\']');
|
|
var levelNumGetter = kendo.getter('LNum[\'#text\']');
|
|
var childrenGetter = kendo.getter('CHILDREN_CARDINALITY[\'#text\']', true);
|
|
var hierarchyGetter = kendo.getter('[\'@Hierarchy\']');
|
|
var parentNameGetter = kendo.getter('PARENT_UNIQUE_NAME[\'#text\']', true);
|
|
for (var idx = 0; idx < tuples.length; idx++) {
|
|
var members = [];
|
|
var member = asArray(tuples[idx].Member);
|
|
for (var memberIdx = 0; memberIdx < member.length; memberIdx++) {
|
|
members.push({
|
|
children: [],
|
|
caption: captionGetter(member[memberIdx]),
|
|
name: unameGetter(member[memberIdx]),
|
|
levelName: levelNameGetter(member[memberIdx]),
|
|
levelNum: levelNumGetter(member[memberIdx]),
|
|
hasChildren: parseInt(childrenGetter(member[memberIdx]), 10) > 0,
|
|
parentName: parentNameGetter(member[memberIdx]),
|
|
hierarchy: hierarchyGetter(member[memberIdx])
|
|
});
|
|
}
|
|
result.tuples.push({ members: members });
|
|
}
|
|
return result;
|
|
}
|
|
var schemaDataReaderMap = {
|
|
cubes: {
|
|
name: kendo.getter('CUBE_NAME[\'#text\']', true),
|
|
caption: kendo.getter('CUBE_CAPTION[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true),
|
|
type: kendo.getter('CUBE_TYPE[\'#text\']', true)
|
|
},
|
|
catalogs: {
|
|
name: kendo.getter('CATALOG_NAME[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true)
|
|
},
|
|
measures: {
|
|
name: kendo.getter('MEASURE_NAME[\'#text\']', true),
|
|
caption: kendo.getter('MEASURE_CAPTION[\'#text\']', true),
|
|
uniqueName: kendo.getter('MEASURE_UNIQUE_NAME[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true),
|
|
aggregator: kendo.getter('MEASURE_AGGREGATOR[\'#text\']', true),
|
|
groupName: kendo.getter('MEASUREGROUP_NAME[\'#text\']', true),
|
|
displayFolder: kendo.getter('MEASURE_DISPLAY_FOLDER[\'#text\']', true),
|
|
defaultFormat: kendo.getter('DEFAULT_FORMAT_STRING[\'#text\']', true)
|
|
},
|
|
kpis: {
|
|
name: kendo.getter('KPI_NAME[\'#text\']', true),
|
|
caption: kendo.getter('KPI_CAPTION[\'#text\']', true),
|
|
value: kendo.getter('KPI_VALUE[\'#text\']', true),
|
|
goal: kendo.getter('KPI_GOAL[\'#text\']', true),
|
|
status: kendo.getter('KPI_STATUS[\'#text\']', true),
|
|
trend: kendo.getter('KPI_TREND[\'#text\']', true),
|
|
statusGraphic: kendo.getter('KPI_STATUS_GRAPHIC[\'#text\']', true),
|
|
trendGraphic: kendo.getter('KPI_TREND_GRAPHIC[\'#text\']', true),
|
|
description: kendo.getter('KPI_DESCRIPTION[\'#text\']', true),
|
|
groupName: kendo.getter('MEASUREGROUP_NAME[\'#text\']', true)
|
|
},
|
|
dimensions: {
|
|
name: kendo.getter('DIMENSION_NAME[\'#text\']', true),
|
|
caption: kendo.getter('DIMENSION_CAPTION[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true),
|
|
uniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
|
|
defaultHierarchy: kendo.getter('DEFAULT_HIERARCHY[\'#text\']', true),
|
|
type: kendo.getter('DIMENSION_TYPE[\'#text\']', true)
|
|
},
|
|
hierarchies: {
|
|
name: kendo.getter('HIERARCHY_NAME[\'#text\']', true),
|
|
caption: kendo.getter('HIERARCHY_CAPTION[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true),
|
|
uniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true),
|
|
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
|
|
displayFolder: kendo.getter('HIERARCHY_DISPLAY_FOLDER[\'#text\']', true),
|
|
origin: kendo.getter('HIERARCHY_ORIGIN[\'#text\']', true),
|
|
defaultMember: kendo.getter('DEFAULT_MEMBER[\'#text\']', true)
|
|
},
|
|
levels: {
|
|
name: kendo.getter('LEVEL_NAME[\'#text\']', true),
|
|
caption: kendo.getter('LEVEL_CAPTION[\'#text\']', true),
|
|
description: kendo.getter('DESCRIPTION[\'#text\']', true),
|
|
uniqueName: kendo.getter('LEVEL_UNIQUE_NAME[\'#text\']', true),
|
|
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
|
|
displayFolder: kendo.getter('LEVEL_DISPLAY_FOLDER[\'#text\']', true),
|
|
orderingProperty: kendo.getter('LEVEL_ORDERING_PROPERTY[\'#text\']', true),
|
|
origin: kendo.getter('LEVEL_ORIGIN[\'#text\']', true),
|
|
hierarchyUniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true)
|
|
},
|
|
members: {
|
|
name: kendo.getter('MEMBER_NAME[\'#text\']', true),
|
|
caption: kendo.getter('MEMBER_CAPTION[\'#text\']', true),
|
|
uniqueName: kendo.getter('MEMBER_UNIQUE_NAME[\'#text\']', true),
|
|
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
|
|
hierarchyUniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true),
|
|
levelUniqueName: kendo.getter('LEVEL_UNIQUE_NAME[\'#text\']', true),
|
|
childrenCardinality: kendo.getter('CHILDREN_CARDINALITY[\'#text\']', true)
|
|
}
|
|
};
|
|
var xmlaReaderMethods = [
|
|
'axes',
|
|
'catalogs',
|
|
'cubes',
|
|
'dimensions',
|
|
'hierarchies',
|
|
'levels',
|
|
'measures'
|
|
];
|
|
var XmlaDataReader = kendo.data.XmlDataReader.extend({
|
|
init: function (options) {
|
|
kendo.data.XmlDataReader.call(this, options);
|
|
this._extend(options);
|
|
},
|
|
_extend: function (options) {
|
|
var idx = 0;
|
|
var length = xmlaReaderMethods.length;
|
|
var methodName;
|
|
var option;
|
|
for (; idx < length; idx++) {
|
|
methodName = xmlaReaderMethods[idx];
|
|
option = options[methodName];
|
|
if (option && option !== identity) {
|
|
this[methodName] = option;
|
|
}
|
|
}
|
|
},
|
|
parse: function (xml) {
|
|
var result = kendo.data.XmlDataReader.fn.parse(xml.replace(/<(\/?)(\w|-)+:/g, '<$1'));
|
|
return kendo.getter('[\'Envelope\'][\'Body\']', true)(result);
|
|
},
|
|
errors: function (root) {
|
|
var fault = kendo.getter('[\'Fault\']', true)(root);
|
|
if (fault) {
|
|
return [{
|
|
faultstring: kendo.getter('faultstring[\'#text\']', true)(fault),
|
|
faultcode: kendo.getter('faultcode[\'#text\']', true)(fault)
|
|
}];
|
|
}
|
|
return null;
|
|
},
|
|
axes: function (root) {
|
|
root = kendo.getter('ExecuteResponse["return"].root', true)(root);
|
|
var axes = asArray(kendo.getter('Axes.Axis', true)(root));
|
|
var axis;
|
|
var result = {
|
|
columns: {},
|
|
rows: {}
|
|
};
|
|
for (var idx = 0; idx < axes.length; idx++) {
|
|
axis = axes[idx];
|
|
if (axis['@name'].toLowerCase() !== 'sliceraxis') {
|
|
if (!result.columns.tuples) {
|
|
result.columns = translateAxis(axis);
|
|
} else {
|
|
result.rows = translateAxis(axis);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
data: function (root) {
|
|
root = kendo.getter('ExecuteResponse["return"].root', true)(root);
|
|
var cells = asArray(kendo.getter('CellData.Cell', true)(root));
|
|
var result = [];
|
|
var ordinalGetter = kendo.getter('[\'@CellOrdinal\']');
|
|
var valueGetter = kendo.getter('Value[\'#text\']');
|
|
var fmtValueGetter = kendo.getter('FmtValue[\'#text\']');
|
|
for (var idx = 0; idx < cells.length; idx++) {
|
|
result.push({
|
|
value: valueGetter(cells[idx]),
|
|
fmtValue: fmtValueGetter(cells[idx]),
|
|
ordinal: parseInt(ordinalGetter(cells[idx]), 10)
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
_mapSchema: function (root, getters) {
|
|
root = kendo.getter('DiscoverResponse["return"].root', true)(root);
|
|
var rows = asArray(kendo.getter('row', true)(root));
|
|
var result = [];
|
|
for (var idx = 0; idx < rows.length; idx++) {
|
|
var obj = {};
|
|
for (var key in getters) {
|
|
obj[key] = getters[key](rows[idx]);
|
|
}
|
|
result.push(obj);
|
|
}
|
|
return result;
|
|
},
|
|
measures: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.measures);
|
|
},
|
|
kpis: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.kpis);
|
|
},
|
|
hierarchies: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.hierarchies);
|
|
},
|
|
levels: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.levels);
|
|
},
|
|
dimensions: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.dimensions);
|
|
},
|
|
cubes: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.cubes);
|
|
},
|
|
catalogs: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.catalogs);
|
|
},
|
|
members: function (root) {
|
|
return this._mapSchema(root, schemaDataReaderMap.members);
|
|
}
|
|
});
|
|
extend(true, kendo.data, {
|
|
PivotDataSource: PivotDataSource,
|
|
XmlaTransport: XmlaTransport,
|
|
XmlaDataReader: XmlaDataReader,
|
|
PivotCubeBuilder: PivotCubeBuilder,
|
|
transports: { xmla: XmlaTransport },
|
|
readers: { xmla: XmlaDataReader }
|
|
});
|
|
var sortExpr = function (expressions, name) {
|
|
if (!expressions) {
|
|
return null;
|
|
}
|
|
for (var idx = 0, length = expressions.length; idx < length; idx++) {
|
|
if (expressions[idx].field === name) {
|
|
return expressions[idx];
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
var removeExpr = function (expressions, name) {
|
|
var result = [];
|
|
for (var idx = 0, length = expressions.length; idx < length; idx++) {
|
|
if (expressions[idx].field !== name) {
|
|
result.push(expressions[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
kendo.ui.PivotSettingTarget = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.element.addClass('k-pivot-setting');
|
|
that.dataSource = kendo.data.PivotDataSource.create(options.dataSource);
|
|
that._refreshHandler = $.proxy(that.refresh, that);
|
|
that.dataSource.first(CHANGE, that._refreshHandler);
|
|
if (!options.template) {
|
|
that.options.template = '<div data-' + kendo.ns + 'name="${data.name || data}">${data.name || data}' + (that.options.enabled ? '<a class="k-button k-button-icon k-button-bare"><span class="k-icon k-setting-delete"></span></a>' : '') + '</div>';
|
|
}
|
|
that.template = kendo.template(that.options.template);
|
|
that.emptyTemplate = kendo.template(that.options.emptyTemplate);
|
|
that._sortable();
|
|
that.element.on('click' + NS, '.k-button,.k-item', function (e) {
|
|
var target = $(e.target);
|
|
var name = target.closest('[' + kendo.attr('name') + ']').attr(kendo.attr('name'));
|
|
if (!name) {
|
|
return;
|
|
}
|
|
if (target.hasClass('k-setting-delete')) {
|
|
that.remove(name);
|
|
} else if (that.options.sortable && target[0] === e.currentTarget) {
|
|
that.sort({
|
|
field: name,
|
|
dir: target.find('.k-i-sort-asc')[0] ? 'desc' : 'asc'
|
|
});
|
|
}
|
|
});
|
|
if (options.filterable || options.sortable) {
|
|
that.fieldMenu = new ui.PivotFieldMenu(that.element, {
|
|
messages: that.options.messages.fieldMenu,
|
|
filter: '.k-setting-fieldmenu',
|
|
filterable: options.filterable,
|
|
sortable: options.sortable,
|
|
dataSource: that.dataSource
|
|
});
|
|
}
|
|
that.refresh();
|
|
},
|
|
options: {
|
|
name: 'PivotSettingTarget',
|
|
template: null,
|
|
filterable: false,
|
|
sortable: false,
|
|
emptyTemplate: '<div class=\'k-empty\'>${data}</div>',
|
|
setting: 'columns',
|
|
enabled: true,
|
|
messages: { empty: 'Drop Fields Here' }
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.dataSource.unbind(CHANGE, this._refreshHandler);
|
|
this.dataSource = this.options.dataSource = dataSource;
|
|
if (this.fieldMenu) {
|
|
this.fieldMenu.setDataSource(dataSource);
|
|
}
|
|
dataSource.first(CHANGE, this._refreshHandler);
|
|
this.refresh();
|
|
},
|
|
_sortable: function () {
|
|
var that = this;
|
|
if (that.options.enabled) {
|
|
this.sortable = this.element.kendoSortable({
|
|
connectWith: this.options.connectWith,
|
|
filter: '>:not(.k-empty)',
|
|
hint: that.options.hint,
|
|
cursor: 'move',
|
|
start: function (e) {
|
|
e.item.focus().blur();
|
|
},
|
|
change: function (e) {
|
|
var name = e.item.attr(kendo.attr('name'));
|
|
if (e.action == 'receive') {
|
|
that.add(name);
|
|
} else if (e.action == 'remove') {
|
|
that.remove(name);
|
|
} else if (e.action == 'sort') {
|
|
that.move(name, e.newIndex);
|
|
}
|
|
}
|
|
}).data('kendoSortable');
|
|
}
|
|
},
|
|
_indexOf: function (name, items) {
|
|
var idx, length, index = -1;
|
|
for (idx = 0, length = items.length; idx < length; idx++) {
|
|
if (getName(items[idx]) === name) {
|
|
index = idx;
|
|
break;
|
|
}
|
|
}
|
|
return index;
|
|
},
|
|
_isKPI: function (data) {
|
|
return data.type === 'kpi' || data.measure;
|
|
},
|
|
validate: function (data) {
|
|
var isMeasure = data.type == 2 || 'aggregator' in data || this._isKPI(data);
|
|
if (isMeasure) {
|
|
return this.options.setting === 'measures';
|
|
}
|
|
if (this.options.setting === 'measures') {
|
|
return isMeasure;
|
|
}
|
|
var items = this.dataSource[this.options.setting]();
|
|
var name = data.defaultHierarchy || data.uniqueName;
|
|
if (this._indexOf(name, items) > -1) {
|
|
return false;
|
|
}
|
|
items = this.dataSource[this.options.setting === 'columns' ? 'rows' : 'columns']();
|
|
if (this._indexOf(name, items) > -1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
add: function (name) {
|
|
var items = this.dataSource[this.options.setting]();
|
|
var i, l;
|
|
name = $.isArray(name) ? name.slice(0) : [name];
|
|
for (i = 0, l = name.length; i < l; i++) {
|
|
if (this._indexOf(name[i], items) !== -1) {
|
|
name.splice(i, 1);
|
|
i -= 1;
|
|
l -= 1;
|
|
}
|
|
}
|
|
if (name.length) {
|
|
items = items.concat(name);
|
|
this.dataSource[this.options.setting](items);
|
|
}
|
|
},
|
|
move: function (name, index) {
|
|
var items = this.dataSource[this.options.setting]();
|
|
var idx = this._indexOf(name, items);
|
|
if (idx > -1) {
|
|
name = items.splice(idx, 1)[0];
|
|
items.splice(index, 0, name);
|
|
this.dataSource[this.options.setting](items);
|
|
}
|
|
},
|
|
remove: function (name) {
|
|
var items = this.dataSource[this.options.setting]();
|
|
var idx = this._indexOf(name, items);
|
|
if (idx > -1) {
|
|
items.splice(idx, 1);
|
|
this.dataSource[this.options.setting](items);
|
|
}
|
|
},
|
|
sort: function (expr) {
|
|
var sortable = this.options.sortable;
|
|
var allowUnsort = sortable === true || sortable.allowUnsort;
|
|
var skipExpr = allowUnsort && expr.dir === 'asc';
|
|
var expressions = this.dataSource.sort() || [];
|
|
var result = removeExpr(expressions, expr.field);
|
|
if (skipExpr && expressions.length !== result.length) {
|
|
expr = null;
|
|
}
|
|
if (expr) {
|
|
result.push(expr);
|
|
}
|
|
this.dataSource.sort(result);
|
|
},
|
|
refresh: function () {
|
|
var html = '';
|
|
var items = this.dataSource[this.options.setting]();
|
|
var length = items.length;
|
|
var idx = 0;
|
|
var item;
|
|
if (length) {
|
|
for (; idx < length; idx++) {
|
|
item = items[idx];
|
|
item = item.name === undefined ? { name: item } : item;
|
|
html += this.template(extend({ sortIcon: this._sortIcon(item.name) }, item));
|
|
}
|
|
} else {
|
|
html = this.emptyTemplate(this.options.messages.empty);
|
|
}
|
|
this.element.html(html);
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.dataSource.unbind(CHANGE, this._refreshHandler);
|
|
this.element.off(NS);
|
|
if (this.sortable) {
|
|
this.sortable.destroy();
|
|
}
|
|
if (this.fieldMenu) {
|
|
this.fieldMenu.destroy();
|
|
}
|
|
this.element = null;
|
|
this._refreshHandler = null;
|
|
},
|
|
_sortIcon: function (name) {
|
|
var expressions = this.dataSource.sort();
|
|
var expr = sortExpr(expressions, getName(name));
|
|
var icon = '';
|
|
if (expr) {
|
|
icon = 'k-i-sort-' + expr.dir;
|
|
}
|
|
return icon;
|
|
}
|
|
});
|
|
var PivotGrid = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
var columnBuilder;
|
|
var rowBuilder;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._dataSource();
|
|
that._bindConfigurator();
|
|
that._wrapper();
|
|
that._createLayout();
|
|
that._columnBuilder = columnBuilder = new ColumnBuilder();
|
|
that._rowBuilder = rowBuilder = new RowBuilder();
|
|
that._contentBuilder = new ContentBuilder();
|
|
that._templates();
|
|
that.columnsHeader.add(that.rowsHeader).on('click', 'span.k-icon', function () {
|
|
var button = $(this);
|
|
var builder = columnBuilder;
|
|
var action = 'expandColumn';
|
|
var eventName;
|
|
var path = button.attr(kendo.attr('path'));
|
|
var eventArgs = {
|
|
axis: 'columns',
|
|
path: $.parseJSON(path)
|
|
};
|
|
if (button.parent().is('td')) {
|
|
builder = rowBuilder;
|
|
action = 'expandRow';
|
|
eventArgs.axis = 'rows';
|
|
}
|
|
var expanded = button.hasClass(STATE_EXPANDED);
|
|
var metadata = builder.metadata[path];
|
|
var request = metadata.expanded === undefined;
|
|
eventName = expanded ? COLLAPSEMEMBER : EXPANDMEMBER;
|
|
eventArgs.childrenLoaded = metadata.maxChildren > metadata.children;
|
|
if (that.trigger(eventName, eventArgs)) {
|
|
return;
|
|
}
|
|
builder.metadata[path].expanded = !expanded;
|
|
button.toggleClass(STATE_EXPANDED, !expanded).toggleClass(STATE_COLLAPSED, expanded);
|
|
if (!expanded && request) {
|
|
that.dataSource[action](eventArgs.path);
|
|
} else {
|
|
that.refresh();
|
|
}
|
|
});
|
|
that._scrollable();
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
DATABINDING,
|
|
DATABOUND,
|
|
EXPANDMEMBER,
|
|
COLLAPSEMEMBER
|
|
],
|
|
options: {
|
|
name: 'PivotGrid',
|
|
autoBind: true,
|
|
reorderable: true,
|
|
filterable: false,
|
|
sortable: false,
|
|
height: null,
|
|
columnWidth: 100,
|
|
configurator: '',
|
|
columnHeaderTemplate: null,
|
|
rowHeaderTemplate: null,
|
|
dataCellTemplate: null,
|
|
kpiStatusTemplate: null,
|
|
kpiTrendTemplate: null,
|
|
messages: {
|
|
measureFields: 'Drop Data Fields Here',
|
|
columnFields: 'Drop Column Fields Here',
|
|
rowFields: 'Drop Rows Fields Here'
|
|
}
|
|
},
|
|
_templates: function () {
|
|
var columnTemplate = this.options.columnHeaderTemplate;
|
|
var rowTemplate = this.options.rowHeaderTemplate;
|
|
var dataTemplate = this.options.dataCellTemplate;
|
|
var kpiStatusTemplate = this.options.kpiStatusTemplate;
|
|
var kpiTrendTemplate = this.options.kpiTrendTemplate;
|
|
this._columnBuilder.template = kendo.template(columnTemplate || HEADER_TEMPLATE, { useWithBlock: !!columnTemplate });
|
|
this._contentBuilder.dataTemplate = kendo.template(dataTemplate || DATACELL_TEMPLATE, { useWithBlock: !!dataTemplate });
|
|
this._contentBuilder.kpiStatusTemplate = kendo.template(kpiStatusTemplate || KPISTATUS_TEMPLATE, { useWithBlock: !!kpiStatusTemplate });
|
|
this._contentBuilder.kpiTrendTemplate = kendo.template(kpiTrendTemplate || KPITREND_TEMPLATE, { useWithBlock: !!kpiTrendTemplate });
|
|
this._rowBuilder.template = kendo.template(rowTemplate || HEADER_TEMPLATE, { useWithBlock: !!rowTemplate });
|
|
},
|
|
_bindConfigurator: function () {
|
|
var configurator = this.options.configurator;
|
|
if (configurator) {
|
|
$(configurator).kendoPivotConfigurator('setDataSource', this.dataSource);
|
|
}
|
|
},
|
|
cellInfoByElement: function (element) {
|
|
element = $(element);
|
|
return this.cellInfo(element.index(), element.parent('tr').index());
|
|
},
|
|
cellInfo: function (columnIndex, rowIndex) {
|
|
var contentBuilder = this._contentBuilder;
|
|
var columnInfo = contentBuilder.columnIndexes[columnIndex || 0];
|
|
var rowInfo = contentBuilder.rowIndexes[rowIndex || 0];
|
|
var dataIndex;
|
|
if (!columnInfo || !rowInfo) {
|
|
return null;
|
|
}
|
|
dataIndex = rowInfo.index * contentBuilder.rowLength + columnInfo.index;
|
|
return {
|
|
columnTuple: columnInfo.tuple,
|
|
rowTuple: rowInfo.tuple,
|
|
measure: columnInfo.measure || rowInfo.measure,
|
|
dataItem: this.dataSource.view()[dataIndex]
|
|
};
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (this.measuresTarget) {
|
|
this.measuresTarget.setDataSource(dataSource);
|
|
}
|
|
if (this.rowsTarget) {
|
|
this.rowsTarget.setDataSource(dataSource);
|
|
}
|
|
if (this.columnsTarget) {
|
|
this.columnsTarget.setDataSource(dataSource);
|
|
}
|
|
this._bindConfigurator();
|
|
if (this.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
this._templates();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
clearTimeout(this._headerReflowTimeout);
|
|
},
|
|
_dataSource: function () {
|
|
var that = this;
|
|
var dataSource = that.options.dataSource;
|
|
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (that.dataSource && this._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(STATERESET, that._stateResetHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
|
|
} else {
|
|
that._refreshHandler = $.proxy(that.refresh, that);
|
|
that._progressHandler = $.proxy(that._requestStart, that);
|
|
that._stateResetHandler = $.proxy(that._stateReset, that);
|
|
that._errorHandler = $.proxy(that._error, that);
|
|
}
|
|
that.dataSource = kendo.data.PivotDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(STATERESET, that._stateResetHandler).bind(ERROR, that._errorHandler);
|
|
},
|
|
_error: function () {
|
|
this._progress(false);
|
|
},
|
|
_requestStart: function () {
|
|
this._progress(true);
|
|
},
|
|
_stateReset: function () {
|
|
this._columnBuilder.reset();
|
|
this._rowBuilder.reset();
|
|
},
|
|
_wrapper: function () {
|
|
var height = this.options.height;
|
|
this.wrapper = this.element.addClass('k-widget k-pivot');
|
|
if (height) {
|
|
this.wrapper.css('height', height);
|
|
}
|
|
},
|
|
_measureFields: function () {
|
|
this.measureFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-measures');
|
|
this.measuresTarget = this._createSettingTarget(this.measureFields, {
|
|
setting: 'measures',
|
|
messages: { empty: this.options.messages.measureFields }
|
|
});
|
|
},
|
|
_createSettingTarget: function (element, options) {
|
|
var template = '<span tabindex="0" class="k-button" data-' + kendo.ns + 'name="${data.name}">${data.name}';
|
|
var sortable = options.sortable;
|
|
var icons = '';
|
|
if (sortable) {
|
|
icons += '#if (data.sortIcon) {#';
|
|
icons += '<span class="k-icon ${data.sortIcon} k-setting-sort"></span>';
|
|
icons += '#}#';
|
|
}
|
|
if (options.filterable || sortable) {
|
|
icons += '<span class="k-icon k-i-arrowhead-s k-setting-fieldmenu"></span>';
|
|
}
|
|
if (this.options.reorderable) {
|
|
icons += '<span class="k-icon k-si-close k-setting-delete"></span>';
|
|
}
|
|
if (icons) {
|
|
template += '<span class="k-field-actions">' + icons + '</span>';
|
|
}
|
|
template += '</span>';
|
|
return new kendo.ui.PivotSettingTarget(element, $.extend({
|
|
template: template,
|
|
emptyTemplate: '<span class="k-empty">${data}</span>',
|
|
enabled: this.options.reorderable,
|
|
dataSource: this.dataSource
|
|
}, options));
|
|
},
|
|
_initSettingTargets: function () {
|
|
this.columnsTarget = this._createSettingTarget(this.columnFields, {
|
|
connectWith: this.rowFields,
|
|
setting: 'columns',
|
|
filterable: this.options.filterable,
|
|
sortable: this.options.sortable,
|
|
messages: {
|
|
empty: this.options.messages.columnFields,
|
|
fieldMenu: this.options.messages.fieldMenu
|
|
}
|
|
});
|
|
this.rowsTarget = this._createSettingTarget(this.rowFields, {
|
|
connectWith: this.columnFields,
|
|
setting: 'rows',
|
|
filterable: this.options.filterable,
|
|
sortable: this.options.sortable,
|
|
messages: {
|
|
empty: this.options.messages.rowFields,
|
|
fieldMenu: this.options.messages.fieldMenu
|
|
}
|
|
});
|
|
},
|
|
_createLayout: function () {
|
|
var that = this;
|
|
var layoutTable = $(LAYOUT_TABLE);
|
|
var leftContainer = layoutTable.find('.k-pivot-rowheaders');
|
|
var rightContainer = layoutTable.find('.k-pivot-table');
|
|
var gridWrapper = $(DIV).addClass('k-grid k-widget');
|
|
that._measureFields();
|
|
that.columnFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-columns');
|
|
that.rowFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-rows');
|
|
that.columnsHeader = $('<div class="k-grid-header-wrap" />').wrap('<div class="k-grid-header" />');
|
|
that.columnsHeader.parent().css('padding-right', kendo.support.scrollbar());
|
|
that.rowsHeader = $('<div class="k-grid k-widget k-alt"/>');
|
|
that.content = $('<div class="k-grid-content" />');
|
|
leftContainer.append(that.measureFields);
|
|
leftContainer.append(that.rowFields);
|
|
leftContainer.append(that.rowsHeader);
|
|
gridWrapper.append(that.columnsHeader.parent());
|
|
gridWrapper.append(that.content);
|
|
rightContainer.append(that.columnFields);
|
|
rightContainer.append(gridWrapper);
|
|
that.wrapper.append(layoutTable);
|
|
that.columnsHeaderTree = new kendo.dom.Tree(that.columnsHeader[0]);
|
|
that.rowsHeaderTree = new kendo.dom.Tree(that.rowsHeader[0]);
|
|
that.contentTree = new kendo.dom.Tree(that.content[0]);
|
|
that._initSettingTargets();
|
|
},
|
|
_progress: function (toggle) {
|
|
kendo.ui.progress(this.wrapper, toggle);
|
|
},
|
|
_resize: function () {
|
|
if (this.content[0].firstChild) {
|
|
this._setSectionsWidth();
|
|
this._setSectionsHeight();
|
|
this._setContentWidth();
|
|
this._setContentHeight();
|
|
this._columnHeaderReflow();
|
|
}
|
|
},
|
|
_columnHeaderReflow: function () {
|
|
var columnTable = this.columnsHeader.children('table');
|
|
if (!kendo.support.browser.mozilla) {
|
|
return;
|
|
}
|
|
clearTimeout(this._headerReflowTimeout);
|
|
columnTable.css('table-layout', 'auto');
|
|
this._headerReflowTimeout = setTimeout(function () {
|
|
columnTable.css('table-layout', '');
|
|
});
|
|
},
|
|
_setSectionsWidth: function () {
|
|
var rowsHeader = this.rowsHeader;
|
|
var leftColumn = rowsHeader.parent('.k-pivot-rowheaders').width(AUTO);
|
|
var width;
|
|
width = Math.max(this.measureFields.outerWidth(), this.rowFields.outerWidth());
|
|
width = Math.max(rowsHeader.children('table').width(), width);
|
|
leftColumn.width(width);
|
|
},
|
|
_setSectionsHeight: function () {
|
|
var measureFieldsHeight = this.measureFields.height(AUTO).height();
|
|
var columnFieldsHeight = this.columnFields.height(AUTO).height();
|
|
var rowFieldsHeight = this.rowFields.height(AUTO).innerHeight();
|
|
var columnsHeight = this.columnsHeader.height(AUTO).innerHeight();
|
|
var padding = rowFieldsHeight - this.rowFields.height();
|
|
var firstRowHeight = columnFieldsHeight > measureFieldsHeight ? columnFieldsHeight : measureFieldsHeight;
|
|
var secondRowHeight = columnsHeight > rowFieldsHeight ? columnsHeight : rowFieldsHeight;
|
|
this.measureFields.height(firstRowHeight);
|
|
this.columnFields.height(firstRowHeight);
|
|
this.rowFields.height(secondRowHeight - padding);
|
|
this.columnsHeader.height(secondRowHeight);
|
|
},
|
|
_setContentWidth: function () {
|
|
var contentTable = this.content.find('table');
|
|
var columnTable = this.columnsHeader.children('table');
|
|
var rowLength = contentTable.children('colgroup').children().length;
|
|
var calculatedWidth = rowLength * this.options.columnWidth;
|
|
var minWidth = Math.ceil(calculatedWidth / this.content.width() * 100);
|
|
if (minWidth < 100) {
|
|
minWidth = 100;
|
|
}
|
|
contentTable.add(columnTable).css('width', minWidth + '%');
|
|
this._resetColspan(columnTable);
|
|
},
|
|
_setContentHeight: function () {
|
|
var that = this;
|
|
var content = that.content;
|
|
var rowsHeader = that.rowsHeader;
|
|
var innerHeight = that.wrapper.innerHeight();
|
|
var scrollbar = kendo.support.scrollbar();
|
|
var skipScrollbar = content[0].offsetHeight === content[0].clientHeight;
|
|
var height = that.options.height;
|
|
if (that.wrapper.is(':visible')) {
|
|
if (!innerHeight || !height) {
|
|
if (skipScrollbar) {
|
|
scrollbar = 0;
|
|
}
|
|
content.height('auto');
|
|
rowsHeader.height(content.height() - scrollbar);
|
|
return;
|
|
}
|
|
innerHeight -= that.columnFields.outerHeight();
|
|
innerHeight -= that.columnsHeader.outerHeight();
|
|
if (innerHeight <= scrollbar * 2) {
|
|
innerHeight = scrollbar * 2 + 1;
|
|
if (!skipScrollbar) {
|
|
innerHeight += scrollbar;
|
|
}
|
|
}
|
|
content.height(innerHeight);
|
|
if (skipScrollbar) {
|
|
scrollbar = 0;
|
|
}
|
|
rowsHeader.height(innerHeight - scrollbar);
|
|
}
|
|
},
|
|
_resetColspan: function (columnTable) {
|
|
var that = this;
|
|
var cell = columnTable.children('tbody').children(':first').children(':first');
|
|
if (that._colspan === undefined) {
|
|
that._colspan = cell.attr('colspan');
|
|
}
|
|
cell.attr('colspan', 1);
|
|
clearTimeout(that._layoutTimeout);
|
|
that._layoutTimeout = setTimeout(function () {
|
|
cell.attr('colspan', that._colspan);
|
|
that._colspan = undefined;
|
|
});
|
|
},
|
|
_axisMeasures: function (axis) {
|
|
var result = [];
|
|
var dataSource = this.dataSource;
|
|
var measures = dataSource.measures();
|
|
var hasMeasure = measures.length > 1 || measures[0] && measures[0].type;
|
|
if (dataSource.measuresAxis() === axis) {
|
|
if (dataSource[axis]().length === 0 || hasMeasure) {
|
|
result = measures;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
items: function () {
|
|
return [];
|
|
},
|
|
refresh: function () {
|
|
var that = this;
|
|
var dataSource = that.dataSource;
|
|
var axes = dataSource.axes();
|
|
var columns = (axes.columns || {}).tuples || [];
|
|
var rows = (axes.rows || {}).tuples || [];
|
|
var columnBuilder = that._columnBuilder;
|
|
var rowBuilder = that._rowBuilder;
|
|
var columnAxis = {};
|
|
var rowAxis = {};
|
|
if (that.trigger(DATABINDING, { action: 'rebind' })) {
|
|
return;
|
|
}
|
|
columnBuilder.measures = that._axisMeasures('columns');
|
|
that.columnsHeaderTree.render(columnBuilder.build(columns));
|
|
that.rowsHeaderTree.render(rowBuilder.build(rows));
|
|
columnAxis = {
|
|
indexes: columnBuilder._indexes,
|
|
measures: columnBuilder.measures,
|
|
metadata: columnBuilder.metadata
|
|
};
|
|
rowAxis = {
|
|
indexes: rowBuilder._indexes,
|
|
measures: this._axisMeasures('rows'),
|
|
metadata: rowBuilder.metadata
|
|
};
|
|
that.contentTree.render(that._contentBuilder.build(dataSource.view(), columnAxis, rowAxis));
|
|
that._resize();
|
|
if (that.touchScroller) {
|
|
that.touchScroller.contentResized();
|
|
} else {
|
|
var touchScroller = kendo.touchScroller(that.content);
|
|
if (touchScroller && touchScroller.movable) {
|
|
that.touchScroller = touchScroller;
|
|
touchScroller.movable.bind('change', function (e) {
|
|
that.columnsHeader.scrollLeft(-e.sender.x);
|
|
that.rowsHeader.scrollTop(-e.sender.y);
|
|
});
|
|
}
|
|
}
|
|
that._progress(false);
|
|
that.trigger(DATABOUND);
|
|
},
|
|
_scrollable: function () {
|
|
var that = this;
|
|
var columnsHeader = that.columnsHeader;
|
|
var rowsHeader = that.rowsHeader;
|
|
that.content.scroll(function () {
|
|
columnsHeader.scrollLeft(this.scrollLeft);
|
|
rowsHeader.scrollTop(this.scrollTop);
|
|
});
|
|
rowsHeader.bind('DOMMouseScroll' + NS + ' mousewheel' + NS, $.proxy(that._wheelScroll, that));
|
|
},
|
|
_wheelScroll: function (e) {
|
|
if (e.ctrlKey) {
|
|
return;
|
|
}
|
|
var delta = kendo.wheelDeltaY(e);
|
|
var scrollTop = this.content.scrollTop();
|
|
if (delta) {
|
|
e.preventDefault();
|
|
$(e.currentTarget).one('wheel' + NS, false);
|
|
this.rowsHeader.scrollTop(scrollTop + -delta);
|
|
this.content.scrollTop(scrollTop + -delta);
|
|
}
|
|
}
|
|
});
|
|
var element = kendo.dom.element;
|
|
var htmlNode = kendo.dom.html;
|
|
var createMetadata = function (levelNum, memberIdx) {
|
|
return {
|
|
maxChildren: 0,
|
|
children: 0,
|
|
maxMembers: 0,
|
|
members: 0,
|
|
measures: 1,
|
|
levelNum: levelNum,
|
|
parentMember: memberIdx !== 0
|
|
};
|
|
};
|
|
var buildPath = function (tuple, index) {
|
|
var path = [];
|
|
var idx = 0;
|
|
for (; idx <= index; idx++) {
|
|
path.push(tuple.members[idx].name);
|
|
}
|
|
return path;
|
|
};
|
|
var tupleName = function (tuple, index) {
|
|
var name = '';
|
|
var idx = 0;
|
|
for (; idx <= index; idx++) {
|
|
name += tuple.members[idx].name;
|
|
}
|
|
return name;
|
|
};
|
|
var ColumnBuilder = Class.extend({
|
|
init: function () {
|
|
this.measures = 1;
|
|
this.metadata = {};
|
|
},
|
|
build: function (tuples) {
|
|
var tbody = this._tbody(tuples);
|
|
var colgroup = this._colGroup();
|
|
return [element('table', null, [
|
|
colgroup,
|
|
tbody
|
|
])];
|
|
},
|
|
reset: function () {
|
|
this.metadata = {};
|
|
},
|
|
_colGroup: function () {
|
|
var length = this._rowLength();
|
|
var children = [];
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
children.push(element('col', null));
|
|
}
|
|
return element('colgroup', null, children);
|
|
},
|
|
_tbody: function (tuples) {
|
|
var root = tuples[0];
|
|
this.map = {};
|
|
this.rows = [];
|
|
this.rootTuple = root;
|
|
this._indexes = [];
|
|
if (root) {
|
|
this._buildRows(root, 0);
|
|
this._normalize();
|
|
} else {
|
|
this.rows.push(element('tr', null, [element('th', null, [htmlNode(' ')])]));
|
|
}
|
|
return element('tbody', null, this.rows);
|
|
},
|
|
_normalize: function () {
|
|
var rows = this.rows;
|
|
var rowsLength = rows.length;
|
|
var rowIdx = 0;
|
|
var row;
|
|
var cellsLength;
|
|
var cellIdx;
|
|
var cells;
|
|
var cell;
|
|
for (; rowIdx < rowsLength; rowIdx++) {
|
|
row = rows[rowIdx];
|
|
if (row.rowSpan === 1) {
|
|
continue;
|
|
}
|
|
cells = row.children;
|
|
cellIdx = 0;
|
|
cellsLength = cells.length;
|
|
for (; cellIdx < cellsLength; cellIdx++) {
|
|
cell = cells[cellIdx];
|
|
if (cell.tupleAll) {
|
|
cell.attr.rowSpan = row.rowSpan;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_rowIndex: function (row) {
|
|
var rows = this.rows;
|
|
var length = rows.length;
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
if (rows[idx] === row) {
|
|
break;
|
|
}
|
|
}
|
|
return idx;
|
|
},
|
|
_rowLength: function () {
|
|
var cells = this.rows[0] ? this.rows[0].children : [];
|
|
var length = cells.length;
|
|
var rowLength = 0;
|
|
var idx = 0;
|
|
if (length) {
|
|
for (; idx < length; idx++) {
|
|
rowLength += cells[idx].attr.colSpan || 1;
|
|
}
|
|
}
|
|
if (!rowLength) {
|
|
rowLength = this.measures;
|
|
}
|
|
return rowLength;
|
|
},
|
|
_row: function (tuple, memberIdx, parentMember) {
|
|
var rootName = this.rootTuple.members[memberIdx].name;
|
|
var levelNum = tuple.members[memberIdx].levelNum;
|
|
var rowKey = rootName + levelNum;
|
|
var map = this.map;
|
|
var parentRow;
|
|
var children;
|
|
var row = map[rowKey];
|
|
if (!row) {
|
|
row = element('tr', null, []);
|
|
row.parentMember = parentMember;
|
|
row.collapsed = 0;
|
|
row.colSpan = 0;
|
|
row.rowSpan = 1;
|
|
map[rowKey] = row;
|
|
parentRow = map[rootName + (Number(levelNum) - 1)];
|
|
if (parentRow) {
|
|
children = parentRow.children;
|
|
if (children[1] && children[1].attr.className.indexOf('k-alt') === -1) {
|
|
row.notFirst = true;
|
|
} else {
|
|
row.notFirst = parentRow.notFirst;
|
|
}
|
|
}
|
|
this.rows.splice(this._rowIndex(parentRow) + 1, 0, row);
|
|
} else {
|
|
row.notFirst = false;
|
|
if (!row.parentMember || row.parentMember !== parentMember) {
|
|
row.parentMember = parentMember;
|
|
row.collapsed = 0;
|
|
row.colSpan = 0;
|
|
}
|
|
}
|
|
return row;
|
|
},
|
|
_measures: function (measures, tuple, className) {
|
|
var map = this.map;
|
|
var row = map.measureRow;
|
|
var measure;
|
|
if (!row) {
|
|
row = element('tr', null, []);
|
|
map.measureRow = row;
|
|
this.rows.push(row);
|
|
}
|
|
for (var idx = 0, length = measures.length; idx < length; idx++) {
|
|
measure = measures[idx];
|
|
row.children.push(this._cell(className || '', [this._content(measure, tuple)], measure));
|
|
}
|
|
return length;
|
|
},
|
|
_content: function (member, tuple) {
|
|
return htmlNode(this.template({
|
|
member: member,
|
|
tuple: tuple
|
|
}));
|
|
},
|
|
_cell: function (className, children, member) {
|
|
var cell = element('th', { className: 'k-header' + className }, children);
|
|
cell.value = member.caption || member.name;
|
|
return cell;
|
|
},
|
|
_buildRows: function (tuple, memberIdx, parentMember) {
|
|
var members = tuple.members;
|
|
var member = members[memberIdx];
|
|
var nextMember = members[memberIdx + 1];
|
|
var row, childRow, children, childrenLength;
|
|
var cell, allCell, cellAttr;
|
|
var cellChildren = [];
|
|
var path;
|
|
var idx = 0;
|
|
var metadata;
|
|
var colSpan;
|
|
var collapsed = 0;
|
|
var memberCollapsed = 0;
|
|
if (member.measure) {
|
|
this._measures(member.children, tuple);
|
|
return;
|
|
}
|
|
path = kendo.stringify(buildPath(tuple, memberIdx));
|
|
row = this._row(tuple, memberIdx, parentMember);
|
|
children = member.children;
|
|
childrenLength = children.length;
|
|
metadata = this.metadata[path];
|
|
if (!metadata) {
|
|
this.metadata[path] = metadata = createMetadata(Number(member.levelNum), memberIdx);
|
|
metadata.rootLevelNum = Number(this.rootTuple.members[memberIdx].levelNum);
|
|
}
|
|
this._indexes.push({
|
|
path: path,
|
|
tuple: tuple
|
|
});
|
|
if (member.hasChildren) {
|
|
if (metadata.expanded === false) {
|
|
collapsed = metadata.maxChildren;
|
|
row.collapsed += collapsed;
|
|
metadata.children = 0;
|
|
childrenLength = 0;
|
|
}
|
|
cellAttr = { className: 'k-icon ' + (childrenLength ? STATE_EXPANDED : STATE_COLLAPSED) };
|
|
cellAttr[kendo.attr('path')] = path;
|
|
cellChildren.push(element('span', cellAttr));
|
|
}
|
|
cellChildren.push(this._content(member, tuple));
|
|
cell = this._cell(row.notFirst ? ' k-first' : '', cellChildren, member);
|
|
row.children.push(cell);
|
|
row.colSpan += 1;
|
|
if (childrenLength) {
|
|
allCell = this._cell(' k-alt', [this._content(member, tuple)], member);
|
|
row.children.push(allCell);
|
|
for (; idx < childrenLength; idx++) {
|
|
childRow = this._buildRows(children[idx], memberIdx, member);
|
|
}
|
|
colSpan = childRow.colSpan;
|
|
collapsed = childRow.collapsed;
|
|
cell.attr.colSpan = colSpan;
|
|
metadata.children = colSpan;
|
|
metadata.members = 1;
|
|
row.colSpan += colSpan;
|
|
row.collapsed += collapsed;
|
|
row.rowSpan = childRow.rowSpan + 1;
|
|
if (nextMember) {
|
|
if (nextMember.measure) {
|
|
colSpan = this._measures(nextMember.children, tuple, ' k-alt');
|
|
} else {
|
|
childRow = this._buildRows(tuple, memberIdx + 1);
|
|
colSpan = childRow.colSpan;
|
|
row.collapsed += childRow.collapsed;
|
|
memberCollapsed = childRow.collapsed;
|
|
}
|
|
allCell.attr.colSpan = colSpan;
|
|
colSpan -= 1;
|
|
metadata.members += colSpan;
|
|
row.colSpan += colSpan;
|
|
}
|
|
} else if (nextMember) {
|
|
if (nextMember.measure) {
|
|
colSpan = this._measures(nextMember.children, tuple);
|
|
} else {
|
|
childRow = this._buildRows(tuple, memberIdx + 1);
|
|
colSpan = childRow.colSpan;
|
|
row.collapsed += childRow.collapsed;
|
|
memberCollapsed = childRow.collapsed;
|
|
}
|
|
metadata.members = colSpan;
|
|
if (colSpan > 1) {
|
|
cell.attr.colSpan = colSpan;
|
|
row.colSpan += colSpan - 1;
|
|
}
|
|
}
|
|
if (metadata.maxMembers < metadata.members + memberCollapsed) {
|
|
metadata.maxMembers = metadata.members + memberCollapsed;
|
|
}
|
|
children = metadata.children + collapsed;
|
|
if (metadata.maxChildren < children) {
|
|
metadata.maxChildren = children;
|
|
}
|
|
(allCell || cell).tupleAll = true;
|
|
return row;
|
|
}
|
|
});
|
|
var RowBuilder = Class.extend({
|
|
init: function () {
|
|
this.metadata = {};
|
|
},
|
|
build: function (tuples) {
|
|
var tbody = this._tbody(tuples);
|
|
var colgroup = this._colGroup();
|
|
return [element('table', null, [
|
|
colgroup,
|
|
tbody
|
|
])];
|
|
},
|
|
reset: function () {
|
|
this.metadata = {};
|
|
},
|
|
_rowLength: function () {
|
|
var children = this.rows[0].children;
|
|
var length = 0;
|
|
var idx = 0;
|
|
var cell = children[idx];
|
|
while (cell) {
|
|
length += cell.attr.colSpan || 1;
|
|
cell = children[++idx];
|
|
}
|
|
return length;
|
|
},
|
|
_colGroup: function () {
|
|
var length = this._rowLength();
|
|
var children = [];
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
children.push(element('col', null));
|
|
}
|
|
return element('colgroup', null, children);
|
|
},
|
|
_tbody: function (tuples) {
|
|
var root = tuples[0];
|
|
this.rootTuple = root;
|
|
this.rows = [];
|
|
this.map = {};
|
|
this._indexes = [];
|
|
if (root) {
|
|
this._buildRows(root, 0);
|
|
this._normalize();
|
|
} else {
|
|
this.rows.push(element('tr', null, [element('td', null, [htmlNode(' ')])]));
|
|
}
|
|
return element('tbody', null, this.rows);
|
|
},
|
|
_normalize: function () {
|
|
var rows = this.rows;
|
|
var rowsLength = rows.length;
|
|
var rowIdx = 0;
|
|
var members = this.rootTuple.members;
|
|
var firstMemberName = members[0].name;
|
|
var membersLength = members.length;
|
|
var memberIdx = 0;
|
|
var row;
|
|
var cell;
|
|
var maxcolSpan;
|
|
var map = this.map;
|
|
var allRow;
|
|
for (; rowIdx < rowsLength; rowIdx++) {
|
|
row = rows[rowIdx];
|
|
for (memberIdx = 0; memberIdx < membersLength; memberIdx++) {
|
|
maxcolSpan = this[members[memberIdx].name];
|
|
cell = row.colSpan['dim' + memberIdx];
|
|
if (cell && cell.colSpan < maxcolSpan) {
|
|
cell.attr.colSpan = maxcolSpan - cell.colSpan + 1;
|
|
}
|
|
}
|
|
}
|
|
row = map[firstMemberName];
|
|
allRow = map[firstMemberName + 'all'];
|
|
if (row) {
|
|
row.children[0].attr.className = 'k-first';
|
|
}
|
|
if (allRow) {
|
|
allRow.children[0].attr.className += ' k-first';
|
|
}
|
|
},
|
|
_row: function (children) {
|
|
var row = element('tr', null, children);
|
|
row.rowSpan = 1;
|
|
row.colSpan = {};
|
|
this.rows.push(row);
|
|
return row;
|
|
},
|
|
_content: function (member, tuple) {
|
|
return htmlNode(this.template({
|
|
member: member,
|
|
tuple: tuple
|
|
}));
|
|
},
|
|
_cell: function (className, children, member) {
|
|
var cell = element('td', { className: className }, children);
|
|
cell.value = member.caption || member.name;
|
|
return cell;
|
|
},
|
|
_buildRows: function (tuple, memberIdx) {
|
|
var map = this.map;
|
|
var path;
|
|
var members = tuple.members;
|
|
var member = members[memberIdx];
|
|
var nextMember = members[memberIdx + 1];
|
|
var children = member.children;
|
|
var childrenLength = children.length;
|
|
var levelNum = Number(member.levelNum);
|
|
var rootName = this.rootTuple.members[memberIdx].name;
|
|
var tuplePath = buildPath(tuple, memberIdx - 1).join('');
|
|
var rootLevelNum = Number(this.rootTuple.members[memberIdx].levelNum);
|
|
var parentName = tuplePath + (rootLevelNum === levelNum ? '' : member.parentName || '');
|
|
var row = map[parentName + 'all'] || map[parentName];
|
|
var colSpan = levelNum + 1;
|
|
var cell, allCell;
|
|
var childRow, allRow;
|
|
var metadata;
|
|
var className;
|
|
var cellChildren = [];
|
|
var expandIconAttr;
|
|
var idx;
|
|
if (!row || row.hasChild) {
|
|
row = this._row();
|
|
} else {
|
|
row.hasChild = true;
|
|
}
|
|
if (member.measure) {
|
|
className = row.allCell ? 'k-grid-footer' : '';
|
|
row.children.push(this._cell(className, [this._content(children[0], tuple)], children[0]));
|
|
row.rowSpan = childrenLength;
|
|
for (idx = 1; idx < childrenLength; idx++) {
|
|
this._row([this._cell(className, [this._content(children[idx], tuple)], children[idx])]);
|
|
}
|
|
return row;
|
|
}
|
|
map[tuplePath + member.name] = row;
|
|
path = kendo.stringify(buildPath(tuple, memberIdx));
|
|
metadata = this.metadata[path];
|
|
if (!metadata) {
|
|
this.metadata[path] = metadata = createMetadata(levelNum, memberIdx);
|
|
metadata.rootLevelNum = rootLevelNum;
|
|
}
|
|
this._indexes.push({
|
|
path: path,
|
|
tuple: tuple
|
|
});
|
|
if (member.hasChildren) {
|
|
if (metadata.expanded === false) {
|
|
childrenLength = 0;
|
|
metadata.children = 0;
|
|
}
|
|
expandIconAttr = { className: 'k-icon ' + (childrenLength ? STATE_EXPANDED : STATE_COLLAPSED) };
|
|
expandIconAttr[kendo.attr('path')] = path;
|
|
cellChildren.push(element('span', expandIconAttr));
|
|
}
|
|
cellChildren.push(this._content(member, tuple));
|
|
className = row.allCell && !childrenLength ? 'k-grid-footer' : '';
|
|
cell = this._cell(className, cellChildren, member);
|
|
cell.colSpan = colSpan;
|
|
row.children.push(cell);
|
|
row.colSpan['dim' + memberIdx] = cell;
|
|
if (!this[rootName] || this[rootName] < colSpan) {
|
|
this[rootName] = colSpan;
|
|
}
|
|
if (childrenLength) {
|
|
row.allCell = false;
|
|
row.hasChild = false;
|
|
for (idx = 0; idx < childrenLength; idx++) {
|
|
childRow = this._buildRows(children[idx], memberIdx);
|
|
if (row !== childRow) {
|
|
row.rowSpan += childRow.rowSpan;
|
|
}
|
|
}
|
|
if (row.rowSpan > 1) {
|
|
cell.attr.rowSpan = row.rowSpan;
|
|
}
|
|
metadata.children = row.rowSpan;
|
|
allCell = this._cell('k-grid-footer', [this._content(member, tuple)], member);
|
|
allCell.colSpan = colSpan;
|
|
allRow = this._row([allCell]);
|
|
allRow.colSpan['dim' + memberIdx] = allCell;
|
|
allRow.allCell = true;
|
|
map[tuplePath + member.name + 'all'] = allRow;
|
|
if (nextMember) {
|
|
childRow = this._buildRows(tuple, memberIdx + 1);
|
|
allCell.attr.rowSpan = childRow.rowSpan;
|
|
}
|
|
row.rowSpan += allRow.rowSpan;
|
|
metadata.members = allRow.rowSpan;
|
|
} else if (nextMember) {
|
|
row.hasChild = false;
|
|
this._buildRows(tuple, memberIdx + 1);
|
|
(allCell || cell).attr.rowSpan = row.rowSpan;
|
|
metadata.members = row.rowSpan;
|
|
}
|
|
if (metadata.maxChildren < metadata.children) {
|
|
metadata.maxChildren = metadata.children;
|
|
}
|
|
if (metadata.maxMembers < metadata.members) {
|
|
metadata.maxMembers = metadata.members;
|
|
}
|
|
return row;
|
|
}
|
|
});
|
|
var ContentBuilder = Class.extend({
|
|
init: function () {
|
|
this.columnAxis = {};
|
|
this.rowAxis = {};
|
|
},
|
|
build: function (data, columnAxis, rowAxis) {
|
|
var index = columnAxis.indexes[0];
|
|
var metadata = columnAxis.metadata[index ? index.path : undefined];
|
|
this.columnAxis = columnAxis;
|
|
this.rowAxis = rowAxis;
|
|
this.data = data;
|
|
this.rowLength = metadata ? metadata.maxChildren + metadata.maxMembers : columnAxis.measures.length || 1;
|
|
if (!this.rowLength) {
|
|
this.rowLength = 1;
|
|
}
|
|
var tbody = this._tbody();
|
|
var colgroup = this._colGroup();
|
|
return [element('table', null, [
|
|
colgroup,
|
|
tbody
|
|
])];
|
|
},
|
|
_colGroup: function () {
|
|
var length = this.columnAxis.measures.length || 1;
|
|
var children = [];
|
|
var idx = 0;
|
|
if (this.rows[0]) {
|
|
length = this.rows[0].children.length;
|
|
}
|
|
for (; idx < length; idx++) {
|
|
children.push(element('col', null));
|
|
}
|
|
return element('colgroup', null, children);
|
|
},
|
|
_tbody: function () {
|
|
this.rows = [];
|
|
if (this.data[0]) {
|
|
this.columnIndexes = this._indexes(this.columnAxis, this.rowLength);
|
|
this.rowIndexes = this._indexes(this.rowAxis, Math.ceil(this.data.length / this.rowLength));
|
|
this._buildRows();
|
|
} else {
|
|
this.rows.push(element('tr', null, [element('td', null, [htmlNode(' ')])]));
|
|
}
|
|
return element('tbody', null, this.rows);
|
|
},
|
|
_indexes: function (axisInfo, total) {
|
|
var result = [];
|
|
var axisInfoMember;
|
|
var indexes = axisInfo.indexes;
|
|
var metadata = axisInfo.metadata;
|
|
var measures = axisInfo.measures;
|
|
var measuresLength = measures.length || 1;
|
|
var current;
|
|
var dataIdx = 0;
|
|
var firstEmpty = 0;
|
|
var idx = 0;
|
|
var length = indexes.length;
|
|
var measureIdx;
|
|
var index;
|
|
var children;
|
|
var skipChildren;
|
|
if (!length) {
|
|
for (measureIdx = 0; measureIdx < measuresLength; measureIdx++) {
|
|
result[measureIdx] = {
|
|
index: measureIdx,
|
|
measure: measures[measureIdx],
|
|
tuple: null
|
|
};
|
|
}
|
|
return result;
|
|
}
|
|
for (; idx < length; idx++) {
|
|
axisInfoMember = indexes[idx];
|
|
current = metadata[axisInfoMember.path];
|
|
children = current.children + current.members;
|
|
skipChildren = 0;
|
|
if (children) {
|
|
children -= measuresLength;
|
|
}
|
|
if (current.expanded === false && current.children !== current.maxChildren) {
|
|
skipChildren = current.maxChildren;
|
|
}
|
|
if (current.parentMember && current.levelNum === current.rootLevelNum) {
|
|
children = -1;
|
|
}
|
|
if (children > -1) {
|
|
for (measureIdx = 0; measureIdx < measuresLength; measureIdx++) {
|
|
index = children + measureIdx;
|
|
if (!current.children) {
|
|
index += firstEmpty;
|
|
}
|
|
result[children + firstEmpty + measureIdx] = {
|
|
children: children,
|
|
index: dataIdx,
|
|
measure: measures[measureIdx],
|
|
tuple: axisInfoMember.tuple
|
|
};
|
|
dataIdx += 1;
|
|
}
|
|
while (result[firstEmpty] !== undefined) {
|
|
firstEmpty += 1;
|
|
}
|
|
}
|
|
if (firstEmpty === total) {
|
|
break;
|
|
}
|
|
dataIdx += skipChildren;
|
|
}
|
|
return result;
|
|
},
|
|
_buildRows: function () {
|
|
var rowIndexes = this.rowIndexes;
|
|
var length = rowIndexes.length;
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
this.rows.push(this._buildRow(rowIndexes[idx]));
|
|
}
|
|
},
|
|
_buildRow: function (rowInfo) {
|
|
var startIdx = rowInfo.index * this.rowLength;
|
|
var columnIndexes = this.columnIndexes;
|
|
var length = columnIndexes.length;
|
|
var columnInfo;
|
|
var cells = [];
|
|
var idx = 0;
|
|
var templateInfo;
|
|
var cell, cellContent;
|
|
var attr, dataItem, measure;
|
|
for (; idx < length; idx++) {
|
|
columnInfo = columnIndexes[idx];
|
|
attr = {};
|
|
if (columnInfo.children) {
|
|
attr.className = 'k-alt';
|
|
}
|
|
cellContent = '';
|
|
dataItem = this.data[startIdx + columnInfo.index];
|
|
measure = columnInfo.measure || rowInfo.measure;
|
|
templateInfo = {
|
|
columnTuple: columnInfo.tuple,
|
|
rowTuple: rowInfo.tuple,
|
|
measure: measure,
|
|
dataItem: dataItem
|
|
};
|
|
if (dataItem.value !== '' && measure && measure.type) {
|
|
if (measure.type === 'status') {
|
|
cellContent = this.kpiStatusTemplate(templateInfo);
|
|
} else if (measure.type === 'trend') {
|
|
cellContent = this.kpiTrendTemplate(templateInfo);
|
|
}
|
|
}
|
|
if (!cellContent) {
|
|
cellContent = this.dataTemplate(templateInfo);
|
|
}
|
|
cell = element('td', attr, [htmlNode(cellContent)]);
|
|
cell.value = dataItem.value;
|
|
cells.push(cell);
|
|
}
|
|
attr = {};
|
|
if (rowInfo.children) {
|
|
attr.className = 'k-grid-footer';
|
|
}
|
|
return element('tr', attr, cells);
|
|
}
|
|
});
|
|
ui.plugin(PivotGrid);
|
|
kendo.PivotExcelExporter = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
this.widget = options.widget;
|
|
this.dataSource = this.widget.dataSource;
|
|
},
|
|
_columns: function () {
|
|
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
|
|
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
|
|
var columnHeaderLength = columnHeaderTable.children[0].children.length;
|
|
var rowHeaderLength = rowHeaderTable.children[0].children.length;
|
|
var width = this.widget.options.columnWidth;
|
|
var result = [];
|
|
var idx;
|
|
if (rowHeaderLength && this.dataSource.data()[0]) {
|
|
for (idx = 0; idx < rowHeaderLength; idx++) {
|
|
result.push({ autoWidth: true });
|
|
}
|
|
}
|
|
for (idx = 0; idx < columnHeaderLength; idx++) {
|
|
result.push({
|
|
autoWidth: false,
|
|
width: width
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
_cells: function (rows, type, callback) {
|
|
var result = [];
|
|
var i = 0;
|
|
var length = rows.length;
|
|
var cellsLength;
|
|
var row, cells;
|
|
var j, cell;
|
|
for (; i < length; i++) {
|
|
row = [];
|
|
cells = rows[i].children;
|
|
cellsLength = cells.length;
|
|
for (j = 0; j < cellsLength; j++) {
|
|
cell = cells[j];
|
|
row.push({
|
|
background: '#7a7a7a',
|
|
color: '#fff',
|
|
value: cell.value,
|
|
colSpan: cell.attr.colSpan || 1,
|
|
rowSpan: cell.attr.rowSpan || 1
|
|
});
|
|
}
|
|
if (callback) {
|
|
callback(row, i);
|
|
}
|
|
result.push({
|
|
cells: row,
|
|
type: type
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
_rows: function () {
|
|
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
|
|
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
|
|
var columnHeaderLength = columnHeaderTable.children[0].children.length;
|
|
var rowHeaderLength = rowHeaderTable.children[0].children.length;
|
|
var columnHeaderRows = columnHeaderTable.children[1].children;
|
|
var rowHeaderRows = rowHeaderTable.children[1].children;
|
|
var contentRows = this.widget.contentTree.children[0].children[1].children;
|
|
var columnRows = this._cells(columnHeaderRows, 'header');
|
|
if (rowHeaderLength) {
|
|
columnRows[0].cells.splice(0, 0, {
|
|
background: '#7a7a7a',
|
|
color: '#fff',
|
|
value: '',
|
|
colSpan: rowHeaderLength,
|
|
rowSpan: columnHeaderRows.length
|
|
});
|
|
}
|
|
var dataCallback = function (row, index) {
|
|
var j = 0;
|
|
var cell, value;
|
|
var cells = contentRows[index].children;
|
|
for (; j < columnHeaderLength; j++) {
|
|
cell = cells[j];
|
|
value = Number(cell.value);
|
|
if (isNaN(value)) {
|
|
value = cell.value;
|
|
}
|
|
row.push({
|
|
background: '#dfdfdf',
|
|
color: '#333',
|
|
value: value,
|
|
colSpan: 1,
|
|
rowSpan: 1
|
|
});
|
|
}
|
|
};
|
|
var rowRows = this._cells(rowHeaderRows, 'data', dataCallback);
|
|
return columnRows.concat(rowRows);
|
|
},
|
|
_freezePane: function () {
|
|
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
|
|
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
|
|
var rowHeaderLength = rowHeaderTable.children[0].children.length;
|
|
var columnHeaderRows = columnHeaderTable.children[1].children;
|
|
return {
|
|
colSplit: rowHeaderLength,
|
|
rowSplit: columnHeaderRows.length
|
|
};
|
|
},
|
|
workbook: function () {
|
|
var promise;
|
|
if (this.dataSource.view()[0]) {
|
|
promise = $.Deferred();
|
|
promise.resolve();
|
|
} else {
|
|
promise = this.dataSource.fetch();
|
|
}
|
|
return promise.then($.proxy(function () {
|
|
return {
|
|
sheets: [{
|
|
columns: this._columns(),
|
|
rows: this._rows(),
|
|
freezePane: this._freezePane(),
|
|
filter: null
|
|
}]
|
|
};
|
|
}, this));
|
|
}
|
|
});
|
|
var PivotExcelMixin = {
|
|
extend: function (proto) {
|
|
proto.events.push('excelExport');
|
|
proto.options.excel = $.extend(proto.options.excel, this.options);
|
|
proto.saveAsExcel = this.saveAsExcel;
|
|
},
|
|
options: {
|
|
proxyURL: '',
|
|
filterable: false,
|
|
fileName: 'Export.xlsx'
|
|
},
|
|
saveAsExcel: function () {
|
|
var excel = this.options.excel || {};
|
|
var exporter = new kendo.PivotExcelExporter({ widget: this });
|
|
exporter.workbook().then($.proxy(function (book) {
|
|
if (!this.trigger('excelExport', { workbook: book })) {
|
|
var workbook = new kendo.ooxml.Workbook(book);
|
|
kendo.saveAs({
|
|
dataURI: workbook.toDataURL(),
|
|
fileName: book.fileName || excel.fileName,
|
|
proxyURL: excel.proxyURL,
|
|
forceProxy: excel.forceProxy
|
|
});
|
|
}
|
|
}, this));
|
|
}
|
|
};
|
|
kendo.PivotExcelMixin = PivotExcelMixin;
|
|
if (kendo.ooxml && kendo.ooxml.Workbook) {
|
|
PivotExcelMixin.extend(PivotGrid.prototype);
|
|
}
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(PivotGrid.prototype);
|
|
PivotGrid.fn._drawPDF = function () {
|
|
return this._drawPDFShadow({ width: this.wrapper.width() }, { avoidLinks: this.options.pdf.avoidLinks });
|
|
};
|
|
}
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.treeview.draganddrop', [
|
|
'kendo.data',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'treeview.draganddrop',
|
|
name: 'Hierarchical Drag & Drop',
|
|
category: 'framework',
|
|
depends: [
|
|
'core',
|
|
'draganddrop'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var ui = kendo.ui;
|
|
var proxy = $.proxy;
|
|
var extend = $.extend;
|
|
var VISIBILITY = 'visibility';
|
|
var KSTATEHOVER = 'k-state-hover';
|
|
var INPUTSELECTOR = 'input,a,textarea,.k-multiselect-wrap,select,button,a.k-button>.k-icon,button.k-button>.k-icon,span.k-icon.k-i-expand,span.k-icon.k-i-collapse';
|
|
ui.HierarchicalDragAndDrop = kendo.Class.extend({
|
|
init: function (element, options) {
|
|
this.element = element;
|
|
this.hovered = element;
|
|
this.options = extend({
|
|
dragstart: $.noop,
|
|
drag: $.noop,
|
|
drop: $.noop,
|
|
dragend: $.noop
|
|
}, options);
|
|
this._draggable = new ui.Draggable(element, {
|
|
ignore: INPUTSELECTOR,
|
|
filter: options.filter,
|
|
autoScroll: options.autoScroll,
|
|
cursorOffset: {
|
|
left: 10,
|
|
top: kendo.support.mobileOS ? -40 / kendo.support.zoomLevel() : 10
|
|
},
|
|
hint: proxy(this._hint, this),
|
|
dragstart: proxy(this.dragstart, this),
|
|
dragcancel: proxy(this.dragcancel, this),
|
|
drag: proxy(this.drag, this),
|
|
dragend: proxy(this.dragend, this),
|
|
$angular: options.$angular
|
|
});
|
|
},
|
|
_hint: function (element) {
|
|
return '<div class=\'k-header k-drag-clue\'>' + '<span class=\'k-icon k-drag-status\' />' + this.options.hintText(element) + '</div>';
|
|
},
|
|
_removeTouchHover: function () {
|
|
if (kendo.support.touch && this.hovered) {
|
|
this.hovered.find('.' + KSTATEHOVER).removeClass(KSTATEHOVER);
|
|
this.hovered = false;
|
|
}
|
|
},
|
|
_hintStatus: function (newStatus) {
|
|
var statusElement = this._draggable.hint.find('.k-drag-status')[0];
|
|
if (newStatus) {
|
|
statusElement.className = 'k-icon k-drag-status ' + newStatus;
|
|
} else {
|
|
return $.trim(statusElement.className.replace(/k-(icon|drag-status)/g, ''));
|
|
}
|
|
},
|
|
dragstart: function (e) {
|
|
this.source = e.currentTarget.closest(this.options.itemSelector);
|
|
if (this.options.dragstart(this.source)) {
|
|
e.preventDefault();
|
|
}
|
|
if (this.options.reorderable) {
|
|
this.dropHint = $('<div class=\'k-drop-hint\' />').css(VISIBILITY, 'hidden').appendTo(this.element);
|
|
} else {
|
|
this.dropHint = $();
|
|
}
|
|
},
|
|
drag: function (e) {
|
|
var options = this.options;
|
|
var source = this.source;
|
|
var target = this.dropTarget = $(kendo.eventTarget(e));
|
|
var container = target.closest(options.allowedContainers);
|
|
var hoveredItem, itemHeight, itemTop, itemContent, delta;
|
|
var insertOnTop, insertOnBottom, addChild;
|
|
var itemData, position, status;
|
|
if (!container.length) {
|
|
status = 'k-denied';
|
|
this._removeTouchHover();
|
|
} else if (source[0] == target[0] || options.contains(source[0], target[0])) {
|
|
status = 'k-denied';
|
|
} else {
|
|
status = 'k-insert-middle';
|
|
itemData = options.itemFromTarget(target);
|
|
hoveredItem = itemData.item;
|
|
if (hoveredItem.length) {
|
|
this._removeTouchHover();
|
|
itemHeight = hoveredItem.outerHeight();
|
|
itemContent = itemData.content;
|
|
if (options.reorderable) {
|
|
delta = itemHeight / (itemContent.length > 0 ? 4 : 2);
|
|
itemTop = kendo.getOffset(hoveredItem).top;
|
|
insertOnTop = e.y.location < itemTop + delta;
|
|
insertOnBottom = itemTop + itemHeight - delta < e.y.location;
|
|
addChild = itemContent.length && !insertOnTop && !insertOnBottom;
|
|
} else {
|
|
addChild = true;
|
|
insertOnTop = false;
|
|
insertOnBottom = false;
|
|
}
|
|
this.hovered = addChild ? container : false;
|
|
this.dropHint.css(VISIBILITY, addChild ? 'hidden' : 'visible');
|
|
if (this._lastHover && this._lastHover[0] != itemContent[0]) {
|
|
this._lastHover.removeClass(KSTATEHOVER);
|
|
}
|
|
this._lastHover = itemContent.toggleClass(KSTATEHOVER, addChild);
|
|
if (addChild) {
|
|
status = 'k-add';
|
|
} else {
|
|
position = hoveredItem.position();
|
|
position.top += insertOnTop ? 0 : itemHeight;
|
|
this.dropHint.css(position)[insertOnTop ? 'prependTo' : 'appendTo'](options.dropHintContainer(hoveredItem));
|
|
if (insertOnTop && itemData.first) {
|
|
status = 'k-insert-top';
|
|
}
|
|
if (insertOnBottom && itemData.last) {
|
|
status = 'k-insert-bottom';
|
|
}
|
|
}
|
|
} else if (target[0] != this.dropHint[0]) {
|
|
if (this._lastHover) {
|
|
this._lastHover.removeClass(KSTATEHOVER);
|
|
}
|
|
if (!$.contains(this.element[0], container[0])) {
|
|
status = 'k-add';
|
|
} else {
|
|
status = 'k-denied';
|
|
}
|
|
}
|
|
}
|
|
this.options.drag({
|
|
originalEvent: e.originalEvent,
|
|
source: source,
|
|
target: target,
|
|
pageY: e.y.location,
|
|
pageX: e.x.location,
|
|
status: status.substring(2),
|
|
setStatus: function (value) {
|
|
status = value;
|
|
}
|
|
});
|
|
if (status.indexOf('k-insert') !== 0) {
|
|
this.dropHint.css(VISIBILITY, 'hidden');
|
|
}
|
|
this._hintStatus(status);
|
|
},
|
|
dragcancel: function () {
|
|
this.dropHint.remove();
|
|
},
|
|
dragend: function (e) {
|
|
var position = 'over', source = this.source, destination, dropHint = this.dropHint, dropTarget = this.dropTarget, eventArgs, dropPrevented;
|
|
if (dropHint.css(VISIBILITY) == 'visible') {
|
|
position = this.options.dropPositionFrom(dropHint);
|
|
destination = dropHint.closest(this.options.itemSelector);
|
|
} else if (dropTarget) {
|
|
destination = dropTarget.closest(this.options.itemSelector);
|
|
if (!destination.length) {
|
|
destination = dropTarget.closest(this.options.allowedContainers);
|
|
}
|
|
}
|
|
eventArgs = {
|
|
originalEvent: e.originalEvent,
|
|
source: source[0],
|
|
destination: destination[0],
|
|
valid: this._hintStatus() != 'k-denied',
|
|
setValid: function (newValid) {
|
|
this.valid = newValid;
|
|
},
|
|
dropTarget: dropTarget[0],
|
|
position: position
|
|
};
|
|
dropPrevented = this.options.drop(eventArgs);
|
|
dropHint.remove();
|
|
this._removeTouchHover();
|
|
if (this._lastHover) {
|
|
this._lastHover.removeClass(KSTATEHOVER);
|
|
}
|
|
if (!eventArgs.valid || dropPrevented) {
|
|
this._draggable.dropped = eventArgs.valid;
|
|
return;
|
|
}
|
|
this._draggable.dropped = true;
|
|
this.options.dragend({
|
|
originalEvent: e.originalEvent,
|
|
source: source,
|
|
destination: destination,
|
|
position: position
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this._lastHover = this.hovered = null;
|
|
this._draggable.destroy();
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.treeview', [
|
|
'kendo.data',
|
|
'kendo.treeview.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'treeview',
|
|
name: 'TreeView',
|
|
category: 'web',
|
|
description: 'The TreeView widget displays hierarchical data in a traditional tree structure,with support for interactive drag-and-drop operations.',
|
|
depends: ['data'],
|
|
features: [{
|
|
id: 'treeview-dragging',
|
|
name: 'Drag & Drop',
|
|
description: 'Support for drag & drop',
|
|
depends: ['treeview.draganddrop']
|
|
}]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, data = kendo.data, extend = $.extend, template = kendo.template, isArray = $.isArray, Widget = ui.Widget, HierarchicalDataSource = data.HierarchicalDataSource, proxy = $.proxy, keys = kendo.keys, NS = '.kendoTreeView', SELECT = 'select', CHECK = 'check', NAVIGATE = 'navigate', EXPAND = 'expand', CHANGE = 'change', ERROR = 'error', CHECKED = 'checked', INDETERMINATE = 'indeterminate', COLLAPSE = 'collapse', DRAGSTART = 'dragstart', DRAG = 'drag', DROP = 'drop', DRAGEND = 'dragend', DATABOUND = 'dataBound', CLICK = 'click', UNDEFINED = 'undefined', KSTATEHOVER = 'k-state-hover', KTREEVIEW = 'k-treeview', VISIBLE = ':visible', NODE = '.k-item', STRING = 'string', ARIASELECTED = 'aria-selected', ARIADISABLED = 'aria-disabled', TreeView, subGroup, nodeContents, nodeIcon, spriteRe, bindings = {
|
|
text: 'dataTextField',
|
|
url: 'dataUrlField',
|
|
spriteCssClass: 'dataSpriteCssClassField',
|
|
imageUrl: 'dataImageUrlField'
|
|
}, isDomElement = function (o) {
|
|
return typeof HTMLElement === 'object' ? o instanceof HTMLElement : o && typeof o === 'object' && o.nodeType === 1 && typeof o.nodeName === STRING;
|
|
};
|
|
function contentChild(filter) {
|
|
return function (node) {
|
|
var result = node.children('.k-animation-container');
|
|
if (!result.length) {
|
|
result = node;
|
|
}
|
|
return result.children(filter);
|
|
};
|
|
}
|
|
function templateNoWith(code) {
|
|
return kendo.template(code, { useWithBlock: false });
|
|
}
|
|
subGroup = contentChild('.k-group');
|
|
nodeContents = contentChild('.k-group,.k-content');
|
|
nodeIcon = function (node) {
|
|
return node.children('div').children('.k-icon');
|
|
};
|
|
function checkboxes(node) {
|
|
return node.find('> div .k-checkbox-wrapper [type=checkbox]');
|
|
}
|
|
function insertAction(indexOffset) {
|
|
return function (nodeData, referenceNode) {
|
|
referenceNode = referenceNode.closest(NODE);
|
|
var group = referenceNode.parent(), parentNode;
|
|
if (group.parent().is('li')) {
|
|
parentNode = group.parent();
|
|
}
|
|
return this._dataSourceMove(nodeData, group, parentNode, function (dataSource, model) {
|
|
return this._insert(dataSource.data(), model, referenceNode.index() + indexOffset);
|
|
});
|
|
};
|
|
}
|
|
spriteRe = /k-sprite/;
|
|
function moveContents(node, container) {
|
|
var tmp;
|
|
while (node && node.nodeName.toLowerCase() != 'ul') {
|
|
tmp = node;
|
|
node = node.nextSibling;
|
|
if (tmp.nodeType == 3) {
|
|
tmp.nodeValue = $.trim(tmp.nodeValue);
|
|
}
|
|
if (spriteRe.test(tmp.className)) {
|
|
container.insertBefore(tmp, container.firstChild);
|
|
} else {
|
|
container.appendChild(tmp);
|
|
}
|
|
}
|
|
}
|
|
function updateNodeHtml(node) {
|
|
var wrapper = node.children('div'), group = node.children('ul'), toggleButton = wrapper.children('.k-icon'), checkbox = node.children(':checkbox'), innerWrapper = wrapper.children('.k-in');
|
|
if (node.hasClass('k-treeview')) {
|
|
return;
|
|
}
|
|
if (!wrapper.length) {
|
|
wrapper = $('<div />').prependTo(node);
|
|
}
|
|
if (!toggleButton.length && group.length) {
|
|
toggleButton = $('<span class=\'k-icon\' />').prependTo(wrapper);
|
|
} else if (!group.length || !group.children().length) {
|
|
toggleButton.remove();
|
|
group.remove();
|
|
}
|
|
if (checkbox.length) {
|
|
$('<span class=\'k-checkbox-wrapper\' />').appendTo(wrapper).append(checkbox);
|
|
}
|
|
if (!innerWrapper.length) {
|
|
innerWrapper = node.children('a').eq(0).addClass('k-in k-link');
|
|
if (!innerWrapper.length) {
|
|
innerWrapper = $('<span class=\'k-in\' />');
|
|
}
|
|
innerWrapper.appendTo(wrapper);
|
|
if (wrapper.length) {
|
|
moveContents(wrapper[0].nextSibling, innerWrapper[0]);
|
|
}
|
|
}
|
|
}
|
|
TreeView = kendo.ui.DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
var that = this, inferred = false, hasDataSource = options && !!options.dataSource, list;
|
|
if (isArray(options)) {
|
|
options = { dataSource: options };
|
|
}
|
|
if (options && typeof options.loadOnDemand == UNDEFINED && isArray(options.dataSource)) {
|
|
options.loadOnDemand = false;
|
|
}
|
|
Widget.prototype.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
list = element.is('ul') && element || element.hasClass(KTREEVIEW) && element.children('ul');
|
|
inferred = !hasDataSource && list.length;
|
|
if (inferred) {
|
|
options.dataSource.list = list;
|
|
}
|
|
that._animation();
|
|
that._accessors();
|
|
that._templates();
|
|
if (!element.hasClass(KTREEVIEW)) {
|
|
that._wrapper();
|
|
if (list) {
|
|
that.root = element;
|
|
that._group(that.wrapper);
|
|
}
|
|
} else {
|
|
that.wrapper = element;
|
|
that.root = element.children('ul').eq(0);
|
|
}
|
|
that._tabindex();
|
|
that.root.attr('role', 'tree');
|
|
that._dataSource(inferred);
|
|
that._attachEvents();
|
|
that._dragging();
|
|
if (!inferred) {
|
|
if (options.autoBind) {
|
|
that._progress(true);
|
|
that.dataSource.fetch();
|
|
}
|
|
} else {
|
|
that._syncHtmlAndDataSource();
|
|
}
|
|
if (options.checkboxes && options.checkboxes.checkChildren) {
|
|
that.updateIndeterminate();
|
|
}
|
|
if (that.element[0].id) {
|
|
that._ariaId = kendo.format('{0}_tv_active', that.element[0].id);
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this, clickableItems = '.k-in:not(.k-state-selected,.k-state-disabled)', MOUSEENTER = 'mouseenter';
|
|
that.wrapper.on(MOUSEENTER + NS, '.k-in.k-state-selected', function (e) {
|
|
e.preventDefault();
|
|
}).on(MOUSEENTER + NS, clickableItems, function () {
|
|
$(this).addClass(KSTATEHOVER);
|
|
}).on('mouseleave' + NS, clickableItems, function () {
|
|
$(this).removeClass(KSTATEHOVER);
|
|
}).on(CLICK + NS, clickableItems, proxy(that._click, that)).on('dblclick' + NS, '.k-in:not(.k-state-disabled)', proxy(that._toggleButtonClick, that)).on(CLICK + NS, '.k-plus,.k-minus', proxy(that._toggleButtonClick, that)).on('keydown' + NS, proxy(that._keydown, that)).on('focus' + NS, proxy(that._focus, that)).on('blur' + NS, proxy(that._blur, that)).on('mousedown' + NS, '.k-in,.k-checkbox-wrapper :checkbox,.k-plus,.k-minus', proxy(that._mousedown, that)).on('change' + NS, '.k-checkbox-wrapper :checkbox', proxy(that._checkboxChange, that)).on('click' + NS, '.k-checkbox-wrapper :checkbox', proxy(that._checkboxClick, that)).on('click' + NS, '.k-request-retry', proxy(that._retryRequest, that)).on('click' + NS, function (e) {
|
|
if (!$(e.target).is(':kendoFocusable')) {
|
|
that.focus();
|
|
}
|
|
});
|
|
},
|
|
_checkboxClick: function (e) {
|
|
var checkbox = $(e.target);
|
|
if (checkbox.data(INDETERMINATE)) {
|
|
checkbox.data(INDETERMINATE, false).prop(INDETERMINATE, false).prop(CHECKED, true);
|
|
this._checkboxChange(e);
|
|
}
|
|
},
|
|
_syncHtmlAndDataSource: function (root, dataSource) {
|
|
root = root || this.root;
|
|
dataSource = dataSource || this.dataSource;
|
|
var data = dataSource.view(), uidAttr = kendo.attr('uid'), expandedAttr = kendo.attr('expanded'), checkboxesEnabled = this.options.checkboxes, items = root.children('li'), i, item, dataItem, uid, itemCheckbox;
|
|
for (i = 0; i < items.length; i++) {
|
|
dataItem = data[i];
|
|
uid = dataItem.uid;
|
|
item = items.eq(i);
|
|
item.attr('role', 'treeitem').attr(uidAttr, uid);
|
|
dataItem.expanded = item.attr(expandedAttr) === 'true';
|
|
if (checkboxesEnabled) {
|
|
itemCheckbox = checkboxes(item);
|
|
dataItem.checked = itemCheckbox.prop(CHECKED);
|
|
itemCheckbox.attr('id', '_' + uid);
|
|
itemCheckbox.next('.k-checkbox-label').attr('for', '_' + uid);
|
|
}
|
|
this._syncHtmlAndDataSource(item.children('ul'), dataItem.children);
|
|
}
|
|
},
|
|
_animation: function () {
|
|
var options = this.options, animationOptions = options.animation;
|
|
if (animationOptions === false) {
|
|
animationOptions = {
|
|
expand: { effects: {} },
|
|
collapse: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
} else if (!animationOptions.collapse || !('effects' in animationOptions.collapse)) {
|
|
animationOptions.collapse = extend({ reverse: true }, animationOptions.expand);
|
|
}
|
|
extend(animationOptions.collapse, { hide: true });
|
|
options.animation = animationOptions;
|
|
},
|
|
_dragging: function () {
|
|
var enabled = this.options.dragAndDrop;
|
|
var dragging = this.dragging;
|
|
if (enabled && !dragging) {
|
|
var widget = this;
|
|
this.dragging = new ui.HierarchicalDragAndDrop(this.element, {
|
|
reorderable: true,
|
|
$angular: this.options.$angular,
|
|
autoScroll: this.options.autoScroll,
|
|
filter: 'div:not(.k-state-disabled) .k-in',
|
|
allowedContainers: '.k-treeview',
|
|
itemSelector: '.k-treeview .k-item',
|
|
hintText: proxy(this._hintText, this),
|
|
contains: function (source, destination) {
|
|
return $.contains(source, destination);
|
|
},
|
|
dropHintContainer: function (item) {
|
|
return item;
|
|
},
|
|
itemFromTarget: function (target) {
|
|
var item = target.closest('.k-top,.k-mid,.k-bot');
|
|
return {
|
|
item: item,
|
|
content: target.closest('.k-in'),
|
|
first: item.hasClass('k-top'),
|
|
last: item.hasClass('k-bot')
|
|
};
|
|
},
|
|
dropPositionFrom: function (dropHint) {
|
|
return dropHint.prevAll('.k-in').length > 0 ? 'after' : 'before';
|
|
},
|
|
dragstart: function (source) {
|
|
return widget.trigger(DRAGSTART, { sourceNode: source[0] });
|
|
},
|
|
drag: function (options) {
|
|
widget.trigger(DRAG, {
|
|
originalEvent: options.originalEvent,
|
|
sourceNode: options.source[0],
|
|
dropTarget: options.target[0],
|
|
pageY: options.pageY,
|
|
pageX: options.pageX,
|
|
statusClass: options.status,
|
|
setStatusClass: options.setStatus
|
|
});
|
|
},
|
|
drop: function (options) {
|
|
return widget.trigger(DROP, {
|
|
originalEvent: options.originalEvent,
|
|
sourceNode: options.source,
|
|
destinationNode: options.destination,
|
|
valid: options.valid,
|
|
setValid: function (state) {
|
|
this.valid = state;
|
|
options.setValid(state);
|
|
},
|
|
dropTarget: options.dropTarget,
|
|
dropPosition: options.position
|
|
});
|
|
},
|
|
dragend: function (options) {
|
|
var source = options.source;
|
|
var destination = options.destination;
|
|
var position = options.position;
|
|
function triggerDragEnd(source) {
|
|
widget.updateIndeterminate();
|
|
widget.trigger(DRAGEND, {
|
|
originalEvent: options.originalEvent,
|
|
sourceNode: source && source[0],
|
|
destinationNode: destination[0],
|
|
dropPosition: position
|
|
});
|
|
}
|
|
if (position == 'over') {
|
|
widget.append(source, destination, triggerDragEnd);
|
|
} else {
|
|
if (position == 'before') {
|
|
source = widget.insertBefore(source, destination);
|
|
} else if (position == 'after') {
|
|
source = widget.insertAfter(source, destination);
|
|
}
|
|
triggerDragEnd(source);
|
|
}
|
|
}
|
|
});
|
|
} else if (!enabled && dragging) {
|
|
dragging.destroy();
|
|
this.dragging = null;
|
|
}
|
|
},
|
|
_hintText: function (node) {
|
|
return this.templates.dragClue({
|
|
item: this.dataItem(node),
|
|
treeview: this.options
|
|
});
|
|
},
|
|
_templates: function () {
|
|
var that = this, options = that.options, fieldAccessor = proxy(that._fieldAccessor, that);
|
|
if (options.template && typeof options.template == STRING) {
|
|
options.template = template(options.template);
|
|
} else if (!options.template) {
|
|
options.template = templateNoWith('# var text = ' + fieldAccessor('text') + '(data.item); #' + '# if (typeof data.item.encoded != \'undefined\' && data.item.encoded === false) {#' + '#= text #' + '# } else { #' + '#: text #' + '# } #');
|
|
}
|
|
that._checkboxes();
|
|
that.templates = {
|
|
wrapperCssClass: function (group, item) {
|
|
var result = 'k-item', index = item.index;
|
|
if (group.firstLevel && index === 0) {
|
|
result += ' k-first';
|
|
}
|
|
if (index == group.length - 1) {
|
|
result += ' k-last';
|
|
}
|
|
return result;
|
|
},
|
|
cssClass: function (group, item) {
|
|
var result = '', index = item.index, groupLength = group.length - 1;
|
|
if (group.firstLevel && index === 0) {
|
|
result += 'k-top ';
|
|
}
|
|
if (index === 0 && index != groupLength) {
|
|
result += 'k-top';
|
|
} else if (index == groupLength) {
|
|
result += 'k-bot';
|
|
} else {
|
|
result += 'k-mid';
|
|
}
|
|
return result;
|
|
},
|
|
textClass: function (item, isLink) {
|
|
var result = 'k-in';
|
|
if (isLink) {
|
|
result += ' k-link';
|
|
}
|
|
if (item.enabled === false) {
|
|
result += ' k-state-disabled';
|
|
}
|
|
if (item.selected === true) {
|
|
result += ' k-state-selected';
|
|
}
|
|
return result;
|
|
},
|
|
toggleButtonClass: function (item) {
|
|
var result = 'k-icon';
|
|
if (item.expanded !== true) {
|
|
result += ' k-plus';
|
|
} else {
|
|
result += ' k-minus';
|
|
}
|
|
if (item.enabled === false) {
|
|
result += '-disabled';
|
|
}
|
|
return result;
|
|
},
|
|
groupAttributes: function (group) {
|
|
var attributes = '';
|
|
if (!group.firstLevel) {
|
|
attributes = 'role=\'group\'';
|
|
}
|
|
return attributes + (group.expanded !== true ? ' style=\'display:none\'' : '');
|
|
},
|
|
groupCssClass: function (group) {
|
|
var cssClass = 'k-group';
|
|
if (group.firstLevel) {
|
|
cssClass += ' k-treeview-lines';
|
|
}
|
|
return cssClass;
|
|
},
|
|
dragClue: templateNoWith('#= data.treeview.template(data) #'),
|
|
group: templateNoWith('<ul class=\'#= data.r.groupCssClass(data.group) #\'#= data.r.groupAttributes(data.group) #>' + '#= data.renderItems(data) #' + '</ul>'),
|
|
itemContent: templateNoWith('# var imageUrl = ' + fieldAccessor('imageUrl') + '(data.item); #' + '# var spriteCssClass = ' + fieldAccessor('spriteCssClass') + '(data.item); #' + '# if (imageUrl) { #' + '<img class=\'k-image\' alt=\'\' src=\'#= imageUrl #\'>' + '# } #' + '# if (spriteCssClass) { #' + '<span class=\'k-sprite #= spriteCssClass #\' />' + '# } #' + '#= data.treeview.template(data) #'),
|
|
itemElement: templateNoWith('# var item = data.item, r = data.r; #' + '# var url = ' + fieldAccessor('url') + '(item); #' + '<div class=\'#= r.cssClass(data.group, item) #\'>' + '# if (item.hasChildren) { #' + '<span class=\'#= r.toggleButtonClass(item) #\' role=\'presentation\' />' + '# } #' + '# if (data.treeview.checkboxes) { #' + '<span class=\'k-checkbox-wrapper\' role=\'presentation\'>' + '#= data.treeview.checkboxes.template(data) #' + '</span>' + '# } #' + '# var tag = url ? \'a\' : \'span\'; #' + '# var textAttr = url ? \' href=\\\'\' + url + \'\\\'\' : \'\'; #' + '<#=tag# class=\'#= r.textClass(item, !!url) #\'#= textAttr #>' + '#= r.itemContent(data) #' + '</#=tag#>' + '</div>'),
|
|
item: templateNoWith('# var item = data.item, r = data.r; #' + '<li role=\'treeitem\' class=\'#= r.wrapperCssClass(data.group, item) #\' ' + kendo.attr('uid') + '=\'#= item.uid #\' ' + 'aria-selected=\'#= item.selected ? "true" : "false " #\' ' + '#=item.enabled === false ? "aria-disabled=\'true\'" : \'\'#' + '# if (item.expanded) { #' + 'data-expanded=\'true\' aria-expanded=\'true\'' + '# } #' + '>' + '#= r.itemElement(data) #' + '</li>'),
|
|
loading: templateNoWith('<div class=\'k-icon k-loading\' /> #: data.messages.loading #'),
|
|
retry: templateNoWith('#: data.messages.requestFailed # ' + '<button class=\'k-button k-request-retry\'>#: data.messages.retry #</button>')
|
|
};
|
|
},
|
|
items: function () {
|
|
return this.element.find('.k-item > div:first-child');
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var options = this.options;
|
|
options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (options.checkboxes && options.checkboxes.checkChildren) {
|
|
this.dataSource.one('change', $.proxy(this.updateIndeterminate, this, null));
|
|
}
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
_bindDataSource: function () {
|
|
this._refreshHandler = proxy(this.refresh, this);
|
|
this._errorHandler = proxy(this._error, this);
|
|
this.dataSource.bind(CHANGE, this._refreshHandler);
|
|
this.dataSource.bind(ERROR, this._errorHandler);
|
|
},
|
|
_unbindDataSource: function () {
|
|
var dataSource = this.dataSource;
|
|
if (dataSource) {
|
|
dataSource.unbind(CHANGE, this._refreshHandler);
|
|
dataSource.unbind(ERROR, this._errorHandler);
|
|
}
|
|
},
|
|
_dataSource: function (silentRead) {
|
|
var that = this, options = that.options, dataSource = options.dataSource;
|
|
function recursiveRead(data) {
|
|
for (var i = 0; i < data.length; i++) {
|
|
data[i]._initChildren();
|
|
data[i].children.fetch();
|
|
recursiveRead(data[i].children.view());
|
|
}
|
|
}
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
that._unbindDataSource();
|
|
if (!dataSource.fields) {
|
|
dataSource.fields = [
|
|
{ field: 'text' },
|
|
{ field: 'url' },
|
|
{ field: 'spriteCssClass' },
|
|
{ field: 'imageUrl' }
|
|
];
|
|
}
|
|
that.dataSource = dataSource = HierarchicalDataSource.create(dataSource);
|
|
if (silentRead) {
|
|
dataSource.fetch();
|
|
recursiveRead(dataSource.view());
|
|
}
|
|
that._bindDataSource();
|
|
},
|
|
events: [
|
|
DRAGSTART,
|
|
DRAG,
|
|
DROP,
|
|
DRAGEND,
|
|
DATABOUND,
|
|
EXPAND,
|
|
COLLAPSE,
|
|
SELECT,
|
|
CHANGE,
|
|
NAVIGATE,
|
|
CHECK
|
|
],
|
|
options: {
|
|
name: 'TreeView',
|
|
dataSource: {},
|
|
animation: {
|
|
expand: {
|
|
effects: 'expand:vertical',
|
|
duration: 200
|
|
},
|
|
collapse: { duration: 100 }
|
|
},
|
|
messages: {
|
|
loading: 'Loading...',
|
|
requestFailed: 'Request failed.',
|
|
retry: 'Retry'
|
|
},
|
|
dragAndDrop: false,
|
|
checkboxes: false,
|
|
autoBind: true,
|
|
autoScroll: false,
|
|
loadOnDemand: true,
|
|
template: '',
|
|
dataTextField: null
|
|
},
|
|
_accessors: function () {
|
|
var that = this, options = that.options, i, field, textField, element = that.element;
|
|
for (i in bindings) {
|
|
field = options[bindings[i]];
|
|
textField = element.attr(kendo.attr(i + '-field'));
|
|
if (!field && textField) {
|
|
field = textField;
|
|
}
|
|
if (!field) {
|
|
field = i;
|
|
}
|
|
if (!isArray(field)) {
|
|
field = [field];
|
|
}
|
|
options[bindings[i]] = field;
|
|
}
|
|
},
|
|
_fieldAccessor: function (fieldName) {
|
|
var fieldBindings = this.options[bindings[fieldName]], count = fieldBindings.length, result = '(function(item) {';
|
|
if (count === 0) {
|
|
result += 'return item[\'' + fieldName + '\'];';
|
|
} else {
|
|
result += 'var levels = [' + $.map(fieldBindings, function (x) {
|
|
return 'function(d){ return ' + kendo.expr(x) + '}';
|
|
}).join(',') + '];';
|
|
result += 'return levels[Math.min(item.level(), ' + count + '-1)](item)';
|
|
}
|
|
result += '})';
|
|
return result;
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
this._animation();
|
|
this._dragging();
|
|
this._templates();
|
|
},
|
|
_trigger: function (eventName, node) {
|
|
return this.trigger(eventName, { node: node.closest(NODE)[0] });
|
|
},
|
|
_setChecked: function (datasource, value) {
|
|
if (!datasource || !$.isFunction(datasource.view)) {
|
|
return;
|
|
}
|
|
for (var i = 0, nodes = datasource.view(); i < nodes.length; i++) {
|
|
nodes[i][CHECKED] = value;
|
|
if (nodes[i].children) {
|
|
this._setChecked(nodes[i].children, value);
|
|
}
|
|
}
|
|
},
|
|
_setIndeterminate: function (node) {
|
|
var group = subGroup(node), siblings, length, all = true, i;
|
|
if (!group.length) {
|
|
return;
|
|
}
|
|
siblings = checkboxes(group.children());
|
|
length = siblings.length;
|
|
if (!length) {
|
|
return;
|
|
} else if (length > 1) {
|
|
for (i = 1; i < length; i++) {
|
|
if (siblings[i].checked != siblings[i - 1].checked || siblings[i].indeterminate || siblings[i - 1].indeterminate) {
|
|
all = false;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
all = !siblings[0].indeterminate;
|
|
}
|
|
return checkboxes(node).data(INDETERMINATE, !all).prop(INDETERMINATE, !all).prop(CHECKED, all && siblings[0].checked);
|
|
},
|
|
updateIndeterminate: function (node) {
|
|
node = node || this.wrapper;
|
|
var subnodes = subGroup(node).children();
|
|
var i;
|
|
var checkbox;
|
|
if (subnodes.length) {
|
|
for (i = 0; i < subnodes.length; i++) {
|
|
this.updateIndeterminate(subnodes.eq(i));
|
|
}
|
|
checkbox = this._setIndeterminate(node);
|
|
if (checkbox && checkbox.prop(CHECKED)) {
|
|
this.dataItem(node).checked = true;
|
|
}
|
|
}
|
|
},
|
|
_bubbleIndeterminate: function (node) {
|
|
if (!node.length) {
|
|
return;
|
|
}
|
|
var parentNode = this.parent(node), checkbox;
|
|
if (parentNode.length) {
|
|
this._setIndeterminate(parentNode);
|
|
checkbox = parentNode.children('div').find('.k-checkbox-wrapper :checkbox');
|
|
if (checkbox.prop(INDETERMINATE) === false) {
|
|
this.dataItem(parentNode).set(CHECKED, checkbox.prop(CHECKED));
|
|
} else {
|
|
delete this.dataItem(parentNode).checked;
|
|
}
|
|
this._bubbleIndeterminate(parentNode);
|
|
}
|
|
},
|
|
_checkboxChange: function (e) {
|
|
var checkbox = $(e.target);
|
|
var isChecked = checkbox.prop(CHECKED);
|
|
var node = checkbox.closest(NODE);
|
|
var dataItem = this.dataItem(node);
|
|
if (dataItem.checked != isChecked) {
|
|
dataItem.set(CHECKED, isChecked);
|
|
this._trigger(CHECK, node);
|
|
}
|
|
},
|
|
_toggleButtonClick: function (e) {
|
|
this.toggle($(e.target).closest(NODE));
|
|
},
|
|
_mousedown: function (e) {
|
|
var node = $(e.currentTarget).closest(NODE);
|
|
this._clickTarget = node;
|
|
this.current(node);
|
|
},
|
|
_focusable: function (node) {
|
|
return node && node.length && node.is(':visible') && !node.find('.k-in:first').hasClass('k-state-disabled');
|
|
},
|
|
_focus: function () {
|
|
var current = this.select(), clickTarget = this._clickTarget;
|
|
if (kendo.support.touch) {
|
|
return;
|
|
}
|
|
if (clickTarget && clickTarget.length) {
|
|
current = clickTarget;
|
|
}
|
|
if (!this._focusable(current)) {
|
|
current = this.current();
|
|
}
|
|
if (!this._focusable(current)) {
|
|
current = this._nextVisible($());
|
|
}
|
|
this.current(current);
|
|
},
|
|
focus: function () {
|
|
var wrapper = this.wrapper, scrollContainer = wrapper[0], containers = [], offsets = [], documentElement = document.documentElement, i;
|
|
do {
|
|
scrollContainer = scrollContainer.parentNode;
|
|
if (scrollContainer.scrollHeight > scrollContainer.clientHeight) {
|
|
containers.push(scrollContainer);
|
|
offsets.push(scrollContainer.scrollTop);
|
|
}
|
|
} while (scrollContainer != documentElement);
|
|
wrapper.focus();
|
|
for (i = 0; i < containers.length; i++) {
|
|
containers[i].scrollTop = offsets[i];
|
|
}
|
|
},
|
|
_blur: function () {
|
|
this.current().find('.k-in:first').removeClass('k-state-focused');
|
|
},
|
|
_enabled: function (node) {
|
|
return !node.children('div').children('.k-in').hasClass('k-state-disabled');
|
|
},
|
|
parent: function (node) {
|
|
var wrapperRe = /\bk-treeview\b/, itemRe = /\bk-item\b/, result, skipSelf;
|
|
if (typeof node == STRING) {
|
|
node = this.element.find(node);
|
|
}
|
|
if (!isDomElement(node)) {
|
|
node = node[0];
|
|
}
|
|
skipSelf = itemRe.test(node.className);
|
|
do {
|
|
node = node.parentNode;
|
|
if (itemRe.test(node.className)) {
|
|
if (skipSelf) {
|
|
result = node;
|
|
} else {
|
|
skipSelf = true;
|
|
}
|
|
}
|
|
} while (!wrapperRe.test(node.className) && !result);
|
|
return $(result);
|
|
},
|
|
_nextVisible: function (node) {
|
|
var that = this, expanded = that._expanded(node), result;
|
|
function nextParent(node) {
|
|
while (node.length && !node.next().length) {
|
|
node = that.parent(node);
|
|
}
|
|
if (node.next().length) {
|
|
return node.next();
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
if (!node.length || !node.is(':visible')) {
|
|
result = that.root.children().eq(0);
|
|
} else if (expanded) {
|
|
result = subGroup(node).children().first();
|
|
if (!result.length) {
|
|
result = nextParent(node);
|
|
}
|
|
} else {
|
|
result = nextParent(node);
|
|
}
|
|
if (!that._enabled(result)) {
|
|
result = that._nextVisible(result);
|
|
}
|
|
return result;
|
|
},
|
|
_previousVisible: function (node) {
|
|
var that = this, lastChild, result;
|
|
if (!node.length || node.prev().length) {
|
|
if (node.length) {
|
|
result = node.prev();
|
|
} else {
|
|
result = that.root.children().last();
|
|
}
|
|
while (that._expanded(result)) {
|
|
lastChild = subGroup(result).children().last();
|
|
if (!lastChild.length) {
|
|
break;
|
|
}
|
|
result = lastChild;
|
|
}
|
|
} else {
|
|
result = that.parent(node) || node;
|
|
}
|
|
if (!that._enabled(result)) {
|
|
result = that._previousVisible(result);
|
|
}
|
|
return result;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, target, focused = that.current(), expanded = that._expanded(focused), checkbox = focused.find('.k-checkbox-wrapper:first :checkbox'), rtl = kendo.support.isRtl(that.element);
|
|
if (e.target != e.currentTarget) {
|
|
return;
|
|
}
|
|
if (!rtl && key == keys.RIGHT || rtl && key == keys.LEFT) {
|
|
if (expanded) {
|
|
target = that._nextVisible(focused);
|
|
} else {
|
|
that.expand(focused);
|
|
}
|
|
} else if (!rtl && key == keys.LEFT || rtl && key == keys.RIGHT) {
|
|
if (expanded) {
|
|
that.collapse(focused);
|
|
} else {
|
|
target = that.parent(focused);
|
|
if (!that._enabled(target)) {
|
|
target = undefined;
|
|
}
|
|
}
|
|
} else if (key == keys.DOWN) {
|
|
target = that._nextVisible(focused);
|
|
} else if (key == keys.UP) {
|
|
target = that._previousVisible(focused);
|
|
} else if (key == keys.HOME) {
|
|
target = that._nextVisible($());
|
|
} else if (key == keys.END) {
|
|
target = that._previousVisible($());
|
|
} else if (key == keys.ENTER) {
|
|
if (!focused.find('.k-in:first').hasClass('k-state-selected')) {
|
|
if (!that._trigger(SELECT, focused)) {
|
|
that.select(focused);
|
|
}
|
|
}
|
|
} else if (key == keys.SPACEBAR && checkbox.length) {
|
|
checkbox.prop(CHECKED, !checkbox.prop(CHECKED)).data(INDETERMINATE, false).prop(INDETERMINATE, false);
|
|
that._checkboxChange({ target: checkbox });
|
|
target = focused;
|
|
}
|
|
if (target) {
|
|
e.preventDefault();
|
|
if (focused[0] != target[0]) {
|
|
that._trigger(NAVIGATE, target);
|
|
that.current(target);
|
|
}
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var that = this, node = $(e.currentTarget), contents = nodeContents(node.closest(NODE)), href = node.attr('href'), shouldNavigate;
|
|
if (href) {
|
|
shouldNavigate = href == '#' || href.indexOf('#' + this.element.id + '-') >= 0;
|
|
} else {
|
|
shouldNavigate = contents.length && !contents.children().length;
|
|
}
|
|
if (shouldNavigate) {
|
|
e.preventDefault();
|
|
}
|
|
if (!node.hasClass('.k-state-selected') && !that._trigger(SELECT, node)) {
|
|
that.select(node);
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper, root, wrapperClasses = 'k-widget k-treeview';
|
|
if (element.is('ul')) {
|
|
wrapper = element.wrap('<div />').parent();
|
|
root = element;
|
|
} else {
|
|
wrapper = element;
|
|
root = wrapper.children('ul').eq(0);
|
|
}
|
|
that.wrapper = wrapper.addClass(wrapperClasses);
|
|
that.root = root;
|
|
},
|
|
_group: function (item) {
|
|
var that = this, firstLevel = item.hasClass(KTREEVIEW), group = {
|
|
firstLevel: firstLevel,
|
|
expanded: firstLevel || that._expanded(item)
|
|
}, groupElement = item.children('ul');
|
|
groupElement.addClass(that.templates.groupCssClass(group)).css('display', group.expanded ? '' : 'none');
|
|
that._nodes(groupElement, group);
|
|
},
|
|
_nodes: function (groupElement, groupData) {
|
|
var that = this, nodes = groupElement.children('li'), nodeData;
|
|
groupData = extend({ length: nodes.length }, groupData);
|
|
nodes.each(function (i, node) {
|
|
node = $(node);
|
|
nodeData = {
|
|
index: i,
|
|
expanded: that._expanded(node)
|
|
};
|
|
updateNodeHtml(node);
|
|
that._updateNodeClasses(node, groupData, nodeData);
|
|
that._group(node);
|
|
});
|
|
},
|
|
_checkboxes: function () {
|
|
var options = this.options;
|
|
var checkboxes = options.checkboxes;
|
|
var defaultTemplate;
|
|
if (checkboxes) {
|
|
defaultTemplate = '<input type=\'checkbox\' tabindex=\'-1\' #= (item.enabled === false) ? \'disabled\' : \'\' # #= item.checked ? \'checked\' : \'\' #';
|
|
if (checkboxes.name) {
|
|
defaultTemplate += ' name=\'' + checkboxes.name + '\'';
|
|
}
|
|
defaultTemplate += ' id=\'_#= item.uid #\' class=\'k-checkbox\' /><label for=\'_#= item.uid #\' class=\'k-checkbox-label\'></label>';
|
|
checkboxes = extend({ template: defaultTemplate }, options.checkboxes);
|
|
if (typeof checkboxes.template == STRING) {
|
|
checkboxes.template = template(checkboxes.template);
|
|
}
|
|
options.checkboxes = checkboxes;
|
|
}
|
|
},
|
|
_updateNodeClasses: function (node, groupData, nodeData) {
|
|
var wrapper = node.children('div'), group = node.children('ul'), templates = this.templates;
|
|
if (node.hasClass('k-treeview')) {
|
|
return;
|
|
}
|
|
nodeData = nodeData || {};
|
|
nodeData.expanded = typeof nodeData.expanded != UNDEFINED ? nodeData.expanded : this._expanded(node);
|
|
nodeData.index = typeof nodeData.index != UNDEFINED ? nodeData.index : node.index();
|
|
nodeData.enabled = typeof nodeData.enabled != UNDEFINED ? nodeData.enabled : !wrapper.children('.k-in').hasClass('k-state-disabled');
|
|
groupData = groupData || {};
|
|
groupData.firstLevel = typeof groupData.firstLevel != UNDEFINED ? groupData.firstLevel : node.parent().parent().hasClass(KTREEVIEW);
|
|
groupData.length = typeof groupData.length != UNDEFINED ? groupData.length : node.parent().children().length;
|
|
node.removeClass('k-first k-last').addClass(templates.wrapperCssClass(groupData, nodeData));
|
|
wrapper.removeClass('k-top k-mid k-bot').addClass(templates.cssClass(groupData, nodeData));
|
|
var textWrap = wrapper.children('.k-in');
|
|
var isLink = textWrap[0] && textWrap[0].nodeName.toLowerCase() == 'a';
|
|
textWrap.removeClass('k-in k-link k-state-default k-state-disabled').addClass(templates.textClass(nodeData, isLink));
|
|
if (group.length || node.attr('data-hasChildren') == 'true') {
|
|
wrapper.children('.k-icon').removeClass('k-plus k-minus k-plus-disabled k-minus-disabled').addClass(templates.toggleButtonClass(nodeData));
|
|
group.addClass('k-group');
|
|
}
|
|
},
|
|
_processNodes: function (nodes, callback) {
|
|
var that = this;
|
|
that.element.find(nodes).each(function (index, item) {
|
|
callback.call(that, index, $(item).closest(NODE));
|
|
});
|
|
},
|
|
dataItem: function (node) {
|
|
var uid = $(node).closest(NODE).attr(kendo.attr('uid')), dataSource = this.dataSource;
|
|
return dataSource && dataSource.getByUid(uid);
|
|
},
|
|
_insertNode: function (nodeData, index, parentNode, insertCallback, collapsed) {
|
|
var that = this, group = subGroup(parentNode), updatedGroupLength = group.children().length + 1, childrenData, groupData = {
|
|
firstLevel: parentNode.hasClass(KTREEVIEW),
|
|
expanded: !collapsed,
|
|
length: updatedGroupLength
|
|
}, node, i, item, nodeHtml = '', append = function (item, group) {
|
|
item.appendTo(group);
|
|
};
|
|
for (i = 0; i < nodeData.length; i++) {
|
|
item = nodeData[i];
|
|
item.index = index + i;
|
|
nodeHtml += that._renderItem({
|
|
group: groupData,
|
|
item: item
|
|
});
|
|
}
|
|
node = $(nodeHtml);
|
|
if (!node.length) {
|
|
return;
|
|
}
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: node.get(),
|
|
data: nodeData.map(function (item) {
|
|
return { dataItem: item };
|
|
})
|
|
};
|
|
});
|
|
if (!group.length) {
|
|
group = $(that._renderGroup({ group: groupData })).appendTo(parentNode);
|
|
}
|
|
insertCallback(node, group);
|
|
if (parentNode.hasClass('k-item')) {
|
|
updateNodeHtml(parentNode);
|
|
that._updateNodeClasses(parentNode);
|
|
}
|
|
that._updateNodeClasses(node.prev().first());
|
|
that._updateNodeClasses(node.next().last());
|
|
for (i = 0; i < nodeData.length; i++) {
|
|
item = nodeData[i];
|
|
if (item.hasChildren) {
|
|
childrenData = item.children.data();
|
|
if (childrenData.length) {
|
|
that._insertNode(childrenData, item.index, node.eq(i), append, !that._expanded(node.eq(i)));
|
|
}
|
|
}
|
|
}
|
|
return node;
|
|
},
|
|
_updateNodes: function (items, field) {
|
|
var that = this;
|
|
var i, node, nodeWrapper, item, isChecked, isCollapsed;
|
|
var context = {
|
|
treeview: that.options,
|
|
item: item
|
|
};
|
|
var render = field != 'expanded' && field != 'checked';
|
|
function setCheckedState(root, state) {
|
|
root.find('.k-checkbox-wrapper :checkbox').prop(CHECKED, state).data(INDETERMINATE, false).prop(INDETERMINATE, false);
|
|
}
|
|
if (field == 'selected') {
|
|
item = items[0];
|
|
node = that.findByUid(item.uid).find('.k-in:first').removeClass('k-state-hover').toggleClass('k-state-selected', item[field]).end();
|
|
if (item[field]) {
|
|
that.current(node);
|
|
}
|
|
node.attr(ARIASELECTED, !!item[field]);
|
|
} else {
|
|
var elements = $.map(items, function (item) {
|
|
return that.findByUid(item.uid).children('div');
|
|
});
|
|
if (render) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: elements };
|
|
});
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
context.item = item = items[i];
|
|
nodeWrapper = elements[i];
|
|
node = nodeWrapper.parent();
|
|
if (render) {
|
|
nodeWrapper.children('.k-in').html(that.templates.itemContent(context));
|
|
}
|
|
if (field == CHECKED) {
|
|
isChecked = item[field];
|
|
setCheckedState(nodeWrapper, isChecked);
|
|
if (that.options.checkboxes.checkChildren) {
|
|
setCheckedState(node.children('.k-group'), isChecked);
|
|
that._setChecked(item.children, isChecked);
|
|
that._bubbleIndeterminate(node);
|
|
}
|
|
} else if (field == 'expanded') {
|
|
that._toggle(node, item, item[field]);
|
|
} else if (field == 'enabled') {
|
|
node.find('.k-checkbox-wrapper :checkbox').prop('disabled', !item[field]);
|
|
isCollapsed = !nodeContents(node).is(VISIBLE);
|
|
node.removeAttr(ARIADISABLED);
|
|
if (!item[field]) {
|
|
if (item.selected) {
|
|
item.set('selected', false);
|
|
}
|
|
if (item.expanded) {
|
|
item.set('expanded', false);
|
|
}
|
|
isCollapsed = true;
|
|
node.attr(ARIASELECTED, false).attr(ARIADISABLED, true);
|
|
}
|
|
that._updateNodeClasses(node, {}, {
|
|
enabled: item[field],
|
|
expanded: !isCollapsed
|
|
});
|
|
}
|
|
if (nodeWrapper.length) {
|
|
this.trigger('itemChange', {
|
|
item: nodeWrapper,
|
|
data: item,
|
|
ns: ui
|
|
});
|
|
}
|
|
}
|
|
if (render) {
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: elements,
|
|
data: $.map(items, function (item) {
|
|
return [{ dataItem: item }];
|
|
})
|
|
};
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_appendItems: function (index, items, parentNode) {
|
|
var group = subGroup(parentNode);
|
|
var children = group.children();
|
|
var collapsed = !this._expanded(parentNode);
|
|
if (typeof index == UNDEFINED) {
|
|
index = children.length;
|
|
}
|
|
this._insertNode(items, index, parentNode, function (item, group) {
|
|
if (index >= children.length) {
|
|
item.appendTo(group);
|
|
} else {
|
|
item.insertBefore(children.eq(index));
|
|
}
|
|
}, collapsed);
|
|
if (this._expanded(parentNode)) {
|
|
this._updateNodeClasses(parentNode);
|
|
subGroup(parentNode).css('display', 'block');
|
|
}
|
|
},
|
|
_refreshChildren: function (parentNode, items, index) {
|
|
var i, children, child;
|
|
var options = this.options;
|
|
var loadOnDemand = options.loadOnDemand;
|
|
var checkChildren = options.checkboxes && options.checkboxes.checkChildren;
|
|
subGroup(parentNode).empty();
|
|
if (!items.length) {
|
|
updateNodeHtml(parentNode);
|
|
} else {
|
|
this._appendItems(index, items, parentNode);
|
|
children = subGroup(parentNode).children();
|
|
if (loadOnDemand && checkChildren) {
|
|
this._bubbleIndeterminate(children.last());
|
|
}
|
|
for (i = 0; i < children.length; i++) {
|
|
child = children.eq(i);
|
|
this.trigger('itemChange', {
|
|
item: child.children('div'),
|
|
data: this.dataItem(child),
|
|
ns: ui
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_refreshRoot: function (items) {
|
|
var groupHtml = this._renderGroup({
|
|
items: items,
|
|
group: {
|
|
firstLevel: true,
|
|
expanded: true
|
|
}
|
|
});
|
|
if (this.root.length) {
|
|
this._angularItems('cleanup');
|
|
var group = $(groupHtml);
|
|
this.root.attr('class', group.attr('class')).html(group.html());
|
|
} else {
|
|
this.root = this.wrapper.html(groupHtml).children('ul');
|
|
}
|
|
this.root.attr('role', 'tree');
|
|
var elements = this.root.children('.k-item');
|
|
for (var i = 0; i < items.length; i++) {
|
|
this.trigger('itemChange', {
|
|
item: elements.eq(i),
|
|
data: items[i],
|
|
ns: ui
|
|
});
|
|
}
|
|
this._angularItems('compile');
|
|
},
|
|
refresh: function (e) {
|
|
var node = e.node;
|
|
var action = e.action;
|
|
var items = e.items;
|
|
var parentNode = this.wrapper;
|
|
var options = this.options;
|
|
var loadOnDemand = options.loadOnDemand;
|
|
var checkChildren = options.checkboxes && options.checkboxes.checkChildren;
|
|
var i;
|
|
if (e.field) {
|
|
if (!items[0] || !items[0].level) {
|
|
return;
|
|
}
|
|
return this._updateNodes(items, e.field);
|
|
}
|
|
if (node) {
|
|
parentNode = this.findByUid(node.uid);
|
|
this._progress(parentNode, false);
|
|
}
|
|
if (checkChildren && action != 'remove') {
|
|
var bubble = false;
|
|
for (i = 0; i < items.length; i++) {
|
|
if ('checked' in items[i]) {
|
|
bubble = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bubble && node && node.checked) {
|
|
for (i = 0; i < items.length; i++) {
|
|
items[i].checked = true;
|
|
}
|
|
}
|
|
}
|
|
if (action == 'add') {
|
|
this._appendItems(e.index, items, parentNode);
|
|
} else if (action == 'remove') {
|
|
this._remove(this.findByUid(items[0].uid), false);
|
|
} else if (action == 'itemchange') {
|
|
this._updateNodes(items);
|
|
} else if (action == 'itemloaded') {
|
|
this._refreshChildren(parentNode, items, e.index);
|
|
} else {
|
|
this._refreshRoot(items);
|
|
}
|
|
if (action != 'remove') {
|
|
for (i = 0; i < items.length; i++) {
|
|
if (!loadOnDemand || items[i].expanded) {
|
|
items[i].load();
|
|
}
|
|
}
|
|
}
|
|
this.trigger(DATABOUND, { node: node ? parentNode : undefined });
|
|
},
|
|
_error: function (e) {
|
|
var node = e.node && this.findByUid(e.node.uid);
|
|
var retryHtml = this.templates.retry({ messages: this.options.messages });
|
|
if (node) {
|
|
this._progress(node, false);
|
|
this._expanded(node, false);
|
|
nodeIcon(node).addClass('k-i-refresh');
|
|
e.node.loaded(false);
|
|
} else {
|
|
this._progress(false);
|
|
this.element.html(retryHtml);
|
|
}
|
|
},
|
|
_retryRequest: function (e) {
|
|
e.preventDefault();
|
|
this.dataSource.fetch();
|
|
},
|
|
expand: function (nodes) {
|
|
this._processNodes(nodes, function (index, item) {
|
|
this.toggle(item, true);
|
|
});
|
|
},
|
|
collapse: function (nodes) {
|
|
this._processNodes(nodes, function (index, item) {
|
|
this.toggle(item, false);
|
|
});
|
|
},
|
|
enable: function (nodes, enable) {
|
|
enable = arguments.length == 2 ? !!enable : true;
|
|
this._processNodes(nodes, function (index, item) {
|
|
this.dataItem(item).set('enabled', enable);
|
|
});
|
|
},
|
|
current: function (node) {
|
|
var that = this, current = that._current, element = that.element, id = that._ariaId;
|
|
if (arguments.length > 0 && node && node.length) {
|
|
if (current) {
|
|
if (current[0].id === id) {
|
|
current.removeAttr('id');
|
|
}
|
|
current.find('.k-in:first').removeClass('k-state-focused');
|
|
}
|
|
current = that._current = $(node, element).closest(NODE);
|
|
current.find('.k-in:first').addClass('k-state-focused');
|
|
id = current[0].id || id;
|
|
if (id) {
|
|
that.wrapper.removeAttr('aria-activedescendant');
|
|
current.attr('id', id);
|
|
that.wrapper.attr('aria-activedescendant', id);
|
|
}
|
|
return;
|
|
}
|
|
if (!current) {
|
|
current = that._nextVisible($());
|
|
}
|
|
return current;
|
|
},
|
|
select: function (node) {
|
|
var that = this, element = that.element;
|
|
if (!arguments.length) {
|
|
return element.find('.k-state-selected').closest(NODE);
|
|
}
|
|
node = $(node, element).closest(NODE);
|
|
element.find('.k-state-selected').each(function () {
|
|
var dataItem = that.dataItem(this);
|
|
if (dataItem) {
|
|
dataItem.set('selected', false);
|
|
delete dataItem.selected;
|
|
} else {
|
|
$(this).removeClass('k-state-selected');
|
|
}
|
|
});
|
|
if (node.length) {
|
|
that.dataItem(node).set('selected', true);
|
|
that._clickTarget = node;
|
|
}
|
|
that.trigger(CHANGE);
|
|
},
|
|
_toggle: function (node, dataItem, expand) {
|
|
var options = this.options;
|
|
var contents = nodeContents(node);
|
|
var direction = expand ? 'expand' : 'collapse';
|
|
var loaded;
|
|
if (contents.data('animating')) {
|
|
return;
|
|
}
|
|
if (!this._trigger(direction, node)) {
|
|
this._expanded(node, expand);
|
|
loaded = dataItem && dataItem.loaded();
|
|
if (expand && !loaded) {
|
|
if (options.loadOnDemand) {
|
|
this._progress(node, true);
|
|
}
|
|
contents.remove();
|
|
dataItem.load();
|
|
} else {
|
|
this._updateNodeClasses(node, {}, { expanded: expand });
|
|
if (!expand) {
|
|
contents.css('height', contents.height()).css('height');
|
|
}
|
|
contents.kendoStop(true, true).kendoAnimate(extend({ reset: true }, options.animation[direction], {
|
|
complete: function () {
|
|
if (expand) {
|
|
contents.css('height', '');
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
},
|
|
toggle: function (node, expand) {
|
|
node = $(node);
|
|
if (!nodeIcon(node).is('.k-minus,.k-plus,.k-minus-disabled,.k-plus-disabled')) {
|
|
return;
|
|
}
|
|
if (arguments.length == 1) {
|
|
expand = !this._expanded(node);
|
|
}
|
|
this._expanded(node, expand);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.wrapper.off(NS);
|
|
that._unbindDataSource();
|
|
if (that.dragging) {
|
|
that.dragging.destroy();
|
|
}
|
|
kendo.destroy(that.element);
|
|
that.root = that.wrapper = that.element = null;
|
|
},
|
|
_expanded: function (node, value) {
|
|
var expandedAttr = kendo.attr('expanded');
|
|
var dataItem = this.dataItem(node);
|
|
var expanded = value;
|
|
if (arguments.length == 1) {
|
|
return node.attr(expandedAttr) === 'true' || dataItem && dataItem.expanded;
|
|
}
|
|
if (nodeContents(node).data('animating')) {
|
|
return;
|
|
}
|
|
if (dataItem) {
|
|
dataItem.set('expanded', expanded);
|
|
expanded = dataItem.expanded;
|
|
}
|
|
if (expanded) {
|
|
node.attr(expandedAttr, 'true');
|
|
node.attr('aria-expanded', 'true');
|
|
} else {
|
|
node.removeAttr(expandedAttr);
|
|
node.attr('aria-expanded', 'false');
|
|
}
|
|
},
|
|
_progress: function (node, showProgress) {
|
|
var element = this.element;
|
|
var loadingText = this.templates.loading({ messages: this.options.messages });
|
|
if (arguments.length == 1) {
|
|
showProgress = node;
|
|
if (showProgress) {
|
|
element.html(loadingText);
|
|
} else {
|
|
element.empty();
|
|
}
|
|
} else {
|
|
nodeIcon(node).toggleClass('k-loading', showProgress).removeClass('k-i-refresh');
|
|
}
|
|
},
|
|
text: function (node, text) {
|
|
var dataItem = this.dataItem(node), fieldBindings = this.options[bindings.text], level = dataItem.level(), length = fieldBindings.length, field = fieldBindings[Math.min(level, length - 1)];
|
|
if (text) {
|
|
dataItem.set(field, text);
|
|
} else {
|
|
return dataItem[field];
|
|
}
|
|
},
|
|
_objectOrSelf: function (node) {
|
|
return $(node).closest('[data-role=treeview]').data('kendoTreeView') || this;
|
|
},
|
|
_dataSourceMove: function (nodeData, group, parentNode, callback) {
|
|
var referenceDataItem, destTreeview = this._objectOrSelf(parentNode || group), destDataSource = destTreeview.dataSource;
|
|
var loadPromise = $.Deferred().resolve().promise();
|
|
if (parentNode && parentNode[0] != destTreeview.element[0]) {
|
|
referenceDataItem = destTreeview.dataItem(parentNode);
|
|
if (!referenceDataItem.loaded()) {
|
|
destTreeview._progress(parentNode, true);
|
|
loadPromise = referenceDataItem.load();
|
|
}
|
|
if (parentNode != this.root) {
|
|
destDataSource = referenceDataItem.children;
|
|
if (!destDataSource || !(destDataSource instanceof HierarchicalDataSource)) {
|
|
referenceDataItem._initChildren();
|
|
referenceDataItem.loaded(true);
|
|
destDataSource = referenceDataItem.children;
|
|
}
|
|
}
|
|
}
|
|
nodeData = this._toObservableData(nodeData);
|
|
return callback.call(destTreeview, destDataSource, nodeData, loadPromise);
|
|
},
|
|
_toObservableData: function (node) {
|
|
var dataItem = node, dataSource, uid;
|
|
if (node instanceof window.jQuery || isDomElement(node)) {
|
|
dataSource = this._objectOrSelf(node).dataSource;
|
|
uid = $(node).attr(kendo.attr('uid'));
|
|
dataItem = dataSource.getByUid(uid);
|
|
if (dataItem) {
|
|
dataItem = dataSource.remove(dataItem);
|
|
}
|
|
}
|
|
return dataItem;
|
|
},
|
|
_insert: function (data, model, index) {
|
|
if (!(model instanceof kendo.data.ObservableArray)) {
|
|
if (!isArray(model)) {
|
|
model = [model];
|
|
}
|
|
} else {
|
|
model = model.toJSON();
|
|
}
|
|
var parentNode = data.parent();
|
|
if (parentNode && parentNode._initChildren) {
|
|
parentNode.hasChildren = true;
|
|
parentNode._initChildren();
|
|
}
|
|
data.splice.apply(data, [
|
|
index,
|
|
0
|
|
].concat(model));
|
|
return this.findByUid(data[index].uid);
|
|
},
|
|
insertAfter: insertAction(1),
|
|
insertBefore: insertAction(0),
|
|
append: function (nodeData, parentNode, success) {
|
|
var group = this.root;
|
|
if (parentNode) {
|
|
group = subGroup(parentNode);
|
|
}
|
|
return this._dataSourceMove(nodeData, group, parentNode, function (dataSource, model, loadModel) {
|
|
var inserted;
|
|
var that = this;
|
|
function add() {
|
|
if (parentNode) {
|
|
that._expanded(parentNode, true);
|
|
}
|
|
var data = dataSource.data(), index = Math.max(data.length, 0);
|
|
return that._insert(data, model, index);
|
|
}
|
|
loadModel.then(function () {
|
|
inserted = add();
|
|
success = success || $.noop;
|
|
success(inserted);
|
|
});
|
|
return inserted || null;
|
|
});
|
|
},
|
|
_remove: function (node, keepData) {
|
|
var that = this, parentNode, prevSibling, nextSibling;
|
|
node = $(node, that.element);
|
|
this.angular('cleanup', function () {
|
|
return { elements: node.get() };
|
|
});
|
|
parentNode = node.parent().parent();
|
|
prevSibling = node.prev();
|
|
nextSibling = node.next();
|
|
node[keepData ? 'detach' : 'remove']();
|
|
if (parentNode.hasClass('k-item')) {
|
|
updateNodeHtml(parentNode);
|
|
that._updateNodeClasses(parentNode);
|
|
}
|
|
that._updateNodeClasses(prevSibling);
|
|
that._updateNodeClasses(nextSibling);
|
|
return node;
|
|
},
|
|
remove: function (node) {
|
|
var dataItem = this.dataItem(node);
|
|
if (dataItem) {
|
|
this.dataSource.remove(dataItem);
|
|
}
|
|
},
|
|
detach: function (node) {
|
|
return this._remove(node, true);
|
|
},
|
|
findByText: function (text) {
|
|
return $(this.element).find('.k-in').filter(function (i, element) {
|
|
return $(element).text() == text;
|
|
}).closest(NODE);
|
|
},
|
|
findByUid: function (uid) {
|
|
var items = this.element.find('.k-item');
|
|
var uidAttr = kendo.attr('uid');
|
|
var result;
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (items[i].getAttribute(uidAttr) == uid) {
|
|
result = items[i];
|
|
break;
|
|
}
|
|
}
|
|
return $(result);
|
|
},
|
|
expandPath: function (path, complete) {
|
|
var treeview = this;
|
|
var nodeIds = path.slice(0);
|
|
var callback = complete || $.noop;
|
|
function proceed() {
|
|
nodeIds.shift();
|
|
if (nodeIds.length) {
|
|
expand(nodeIds[0]).then(proceed);
|
|
} else {
|
|
callback.call(treeview);
|
|
}
|
|
}
|
|
function expand(id) {
|
|
var result = $.Deferred();
|
|
var node = treeview.dataSource.get(id);
|
|
if (node) {
|
|
if (node.loaded()) {
|
|
node.set('expanded', true);
|
|
result.resolve();
|
|
} else {
|
|
treeview._progress(treeview.findByUid(node.uid), true);
|
|
node.load().then(function () {
|
|
node.set('expanded', true);
|
|
result.resolve();
|
|
});
|
|
}
|
|
} else {
|
|
result.resolve();
|
|
}
|
|
return result.promise();
|
|
}
|
|
expand(nodeIds[0]).then(proceed);
|
|
},
|
|
_parentIds: function (node) {
|
|
var parent = node && node.parentNode();
|
|
var parents = [];
|
|
while (parent && parent.parentNode) {
|
|
parents.unshift(parent.id);
|
|
parent = parent.parentNode();
|
|
}
|
|
return parents;
|
|
},
|
|
expandTo: function (node) {
|
|
if (!(node instanceof kendo.data.Node)) {
|
|
node = this.dataSource.get(node);
|
|
}
|
|
var parents = this._parentIds(node);
|
|
this.expandPath(parents);
|
|
},
|
|
_renderItem: function (options) {
|
|
if (!options.group) {
|
|
options.group = {};
|
|
}
|
|
options.treeview = this.options;
|
|
options.r = this.templates;
|
|
return this.templates.item(options);
|
|
},
|
|
_renderGroup: function (options) {
|
|
var that = this;
|
|
options.renderItems = function (options) {
|
|
var html = '', i = 0, items = options.items, len = items ? items.length : 0, group = options.group;
|
|
group.length = len;
|
|
for (; i < len; i++) {
|
|
options.group = group;
|
|
options.item = items[i];
|
|
options.item.index = i;
|
|
html += that._renderItem(options);
|
|
}
|
|
return html;
|
|
};
|
|
options.r = that.templates;
|
|
return that.templates.group(options);
|
|
}
|
|
});
|
|
ui.plugin(TreeView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.pivot.fieldmenu', [
|
|
'kendo.pivotgrid',
|
|
'kendo.menu',
|
|
'kendo.window',
|
|
'kendo.treeview',
|
|
'kendo.dropdownlist'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'pivot.fieldmenu',
|
|
name: 'PivotFieldMenu',
|
|
category: 'web',
|
|
description: 'The PivotFieldMenu widget allows the user to filter on fields displayed in PivotGrid',
|
|
depends: [
|
|
'menu',
|
|
'window',
|
|
'treeview',
|
|
'dropdownlist'
|
|
],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, MENU = 'kendoContextMenu', proxy = $.proxy, NS = '.kendoPivotFieldMenu', Widget = ui.Widget;
|
|
var PivotFieldMenu = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._dataSource();
|
|
this._layout();
|
|
kendo.notify(this);
|
|
},
|
|
events: [],
|
|
options: {
|
|
name: 'PivotFieldMenu',
|
|
filter: null,
|
|
filterable: true,
|
|
sortable: true,
|
|
messages: {
|
|
info: 'Show items with value that:',
|
|
sortAscending: 'Sort Ascending',
|
|
sortDescending: 'Sort Descending',
|
|
filterFields: 'Fields Filter',
|
|
filter: 'Filter',
|
|
include: 'Include Fields...',
|
|
title: 'Fields to include',
|
|
clear: 'Clear',
|
|
ok: 'OK',
|
|
cancel: 'Cancel',
|
|
operators: {
|
|
contains: 'Contains',
|
|
doesnotcontain: 'Does not contain',
|
|
startswith: 'Starts with',
|
|
endswith: 'Ends with',
|
|
eq: 'Is equal to',
|
|
neq: 'Is not equal to'
|
|
}
|
|
}
|
|
},
|
|
_layout: function () {
|
|
var options = this.options;
|
|
this.wrapper = $(kendo.template(MENUTEMPLATE)({
|
|
ns: kendo.ns,
|
|
filterable: options.filterable,
|
|
sortable: options.sortable,
|
|
messages: options.messages
|
|
}));
|
|
this.menu = this.wrapper[MENU]({
|
|
filter: options.filter,
|
|
target: this.element,
|
|
orientation: 'vertical',
|
|
showOn: 'click',
|
|
closeOnClick: false,
|
|
open: proxy(this._menuOpen, this),
|
|
select: proxy(this._select, this),
|
|
copyAnchorStyles: false
|
|
}).data(MENU);
|
|
this._createWindow();
|
|
if (options.filterable) {
|
|
this._initFilterForm();
|
|
}
|
|
},
|
|
_initFilterForm: function () {
|
|
var filterForm = this.menu.element.find('.k-filter-item');
|
|
var filterProxy = proxy(this._filter, this);
|
|
this._filterOperator = new kendo.ui.DropDownList(filterForm.find('select'));
|
|
this._filterValue = filterForm.find('.k-textbox');
|
|
filterForm.on('submit' + NS, filterProxy).on('click' + NS, '.k-button-filter', filterProxy).on('click' + NS, '.k-button-clear', proxy(this._reset, this));
|
|
},
|
|
_setFilterForm: function (expression) {
|
|
var filterOperator = this._filterOperator;
|
|
var operator = '';
|
|
var value = '';
|
|
if (expression) {
|
|
operator = expression.operator;
|
|
value = expression.value;
|
|
}
|
|
filterOperator.value(operator);
|
|
if (!filterOperator.value()) {
|
|
filterOperator.select(0);
|
|
}
|
|
this._filterValue.val(value);
|
|
},
|
|
_clearFilters: function (member) {
|
|
var filter = this.dataSource.filter() || {};
|
|
var expressions;
|
|
var idx = 0;
|
|
var length;
|
|
filter.filters = filter.filters || [];
|
|
expressions = findFilters(filter, member);
|
|
for (length = expressions.length; idx < length; idx++) {
|
|
filter.filters.splice(filter.filters.indexOf(expressions[idx]), 1);
|
|
}
|
|
return filter;
|
|
},
|
|
_convert: function (value) {
|
|
var schema = this.dataSource.options.schema;
|
|
var field = ((schema.model || {}).fields || {})[this.currentMember];
|
|
if (field) {
|
|
if (field.type === 'number') {
|
|
value = parseFloat(value);
|
|
} else if (field.type === 'boolean') {
|
|
value = Boolean($.parseJSON(value));
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
_filter: function (e) {
|
|
var that = this;
|
|
var value = that._convert(that._filterValue.val());
|
|
e.preventDefault();
|
|
if (value === '') {
|
|
that.menu.close();
|
|
return;
|
|
}
|
|
var expression = {
|
|
field: that.currentMember,
|
|
operator: that._filterOperator.value(),
|
|
value: value
|
|
};
|
|
var filter = that._clearFilters(that.currentMember);
|
|
filter.filters.push(expression);
|
|
that.dataSource.filter(filter);
|
|
that.menu.close();
|
|
},
|
|
_reset: function (e) {
|
|
var that = this;
|
|
var filter = that._clearFilters(that.currentMember);
|
|
e.preventDefault();
|
|
if (!filter.filters[0]) {
|
|
filter = {};
|
|
}
|
|
that.dataSource.filter(filter);
|
|
that._setFilterForm(null);
|
|
that.menu.close();
|
|
},
|
|
_sort: function (dir) {
|
|
var field = this.currentMember;
|
|
var expressions = this.dataSource.sort() || [];
|
|
expressions = removeExpr(expressions, field);
|
|
expressions.push({
|
|
field: field,
|
|
dir: dir
|
|
});
|
|
this.dataSource.sort(expressions);
|
|
this.menu.close();
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
},
|
|
_dataSource: function () {
|
|
this.dataSource = kendo.data.PivotDataSource.create(this.options.dataSource);
|
|
},
|
|
_createWindow: function () {
|
|
var messages = this.options.messages;
|
|
this.includeWindow = $(kendo.template(WINDOWTEMPLATE)({ messages: messages })).on('click' + NS, '.k-button-ok', proxy(this._applyIncludes, this)).on('click' + NS, '.k-button-cancel', proxy(this._closeWindow, this));
|
|
this.includeWindow = new ui.Window(this.includeWindow, {
|
|
title: messages.title,
|
|
visible: false,
|
|
resizable: false,
|
|
open: proxy(this._windowOpen, this)
|
|
});
|
|
},
|
|
_applyIncludes: function (e) {
|
|
var checkedNodes = [];
|
|
var resultExpression;
|
|
var view = this.treeView.dataSource.view();
|
|
var rootChecked = view[0].checked;
|
|
var filter = this.dataSource.filter();
|
|
var existingExpression = findFilters(filter, this.currentMember, 'in')[0];
|
|
checkedNodeIds(view, checkedNodes);
|
|
if (existingExpression) {
|
|
if (rootChecked) {
|
|
filter.filters.splice(filter.filters.indexOf(existingExpression), 1);
|
|
if (!filter.filters.length) {
|
|
filter = {};
|
|
}
|
|
} else {
|
|
existingExpression.value = checkedNodes.join(',');
|
|
}
|
|
resultExpression = filter;
|
|
}
|
|
if (checkedNodes.length) {
|
|
if (!resultExpression && !rootChecked) {
|
|
resultExpression = {
|
|
field: this.currentMember,
|
|
operator: 'in',
|
|
value: checkedNodes.join(',')
|
|
};
|
|
if (filter) {
|
|
filter.filters.push(resultExpression);
|
|
resultExpression = filter;
|
|
}
|
|
}
|
|
}
|
|
if (resultExpression) {
|
|
this.dataSource.filter(resultExpression);
|
|
}
|
|
this._closeWindow(e);
|
|
},
|
|
_closeWindow: function (e) {
|
|
e.preventDefault();
|
|
this.includeWindow.close();
|
|
},
|
|
_treeViewDataSource: function () {
|
|
var that = this;
|
|
return kendo.data.HierarchicalDataSource.create({
|
|
schema: {
|
|
model: {
|
|
id: 'uniqueName',
|
|
hasChildren: function (item) {
|
|
return parseInt(item.childrenCardinality, 10) > 0;
|
|
}
|
|
}
|
|
},
|
|
transport: {
|
|
read: function (options) {
|
|
var restrictions = {};
|
|
var node = that.treeView.dataSource.get(options.data.uniqueName);
|
|
var name = options.data.uniqueName;
|
|
if (!name) {
|
|
restrictions.levelUniqueName = that.currentMember + '.[(ALL)]';
|
|
} else {
|
|
restrictions.memberUniqueName = node.uniqueName.replace(/\&/g, '&');
|
|
restrictions.treeOp = 1;
|
|
}
|
|
that.dataSource.schemaMembers(restrictions).done(function (data) {
|
|
checkNodes(that.dataSource.filter(), that.currentMember, data);
|
|
options.success(data);
|
|
}).fail(options.error);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_createTreeView: function (element) {
|
|
var that = this;
|
|
that.treeView = new ui.TreeView(element, {
|
|
autoBind: false,
|
|
dataSource: that._treeViewDataSource(),
|
|
dataTextField: 'caption',
|
|
template: '#: data.item.caption || data.item.name #',
|
|
checkboxes: { checkChildren: true },
|
|
dataBound: function () {
|
|
ui.progress(that.includeWindow.element, false);
|
|
}
|
|
});
|
|
},
|
|
_menuOpen: function (e) {
|
|
if (!e.event) {
|
|
return;
|
|
}
|
|
var attr = kendo.attr('name');
|
|
this.currentMember = $(e.event.target).closest('[' + attr + ']').attr(attr);
|
|
if (this.options.filterable) {
|
|
this._setFilterForm(findFilters(this.dataSource.filter(), this.currentMember)[0]);
|
|
}
|
|
},
|
|
_select: function (e) {
|
|
var item = $(e.item);
|
|
$('.k-pivot-filter-window').not(this.includeWindow.element).kendoWindow('close');
|
|
if (item.hasClass('k-include-item')) {
|
|
this.includeWindow.center().open();
|
|
} else if (item.hasClass('k-sort-asc')) {
|
|
this._sort('asc');
|
|
} else if (item.hasClass('k-sort-desc')) {
|
|
this._sort('desc');
|
|
}
|
|
},
|
|
_windowOpen: function () {
|
|
if (!this.treeView) {
|
|
this._createTreeView(this.includeWindow.element.find('.k-treeview'));
|
|
}
|
|
ui.progress(this.includeWindow.element, true);
|
|
this.treeView.dataSource.read();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
if (this.menu) {
|
|
this.menu.destroy();
|
|
this.menu = null;
|
|
}
|
|
if (this.treeView) {
|
|
this.treeView.destroy();
|
|
this.treeView = null;
|
|
}
|
|
if (this.includeWindow) {
|
|
this.includeWindow.destroy();
|
|
this.includeWindow = null;
|
|
}
|
|
this.wrapper = null;
|
|
this.element = null;
|
|
}
|
|
});
|
|
function removeExpr(expressions, name) {
|
|
var result = [];
|
|
for (var idx = 0, length = expressions.length; idx < length; idx++) {
|
|
if (expressions[idx].field !== name) {
|
|
result.push(expressions[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function findFilters(filter, member, operator) {
|
|
if (!filter) {
|
|
return [];
|
|
}
|
|
filter = filter.filters;
|
|
var idx = 0;
|
|
var result = [];
|
|
var length = filter.length;
|
|
var filterOperator;
|
|
for (; idx < length; idx++) {
|
|
filterOperator = filter[idx].operator;
|
|
if ((!operator && filterOperator !== 'in' || filterOperator === operator) && filter[idx].field === member) {
|
|
result.push(filter[idx]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function checkNodes(filter, member, nodes) {
|
|
var values, idx = 0, length = nodes.length;
|
|
filter = findFilters(filter, member, 'in')[0];
|
|
if (!filter) {
|
|
for (; idx < length; idx++) {
|
|
nodes[idx].checked = true;
|
|
}
|
|
} else {
|
|
values = filter.value.split(',');
|
|
for (; idx < length; idx++) {
|
|
nodes[idx].checked = $.inArray(nodes[idx].uniqueName, values) >= 0;
|
|
}
|
|
}
|
|
}
|
|
function checkedNodeIds(nodes, checkedNodes) {
|
|
var idx, length = nodes.length;
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (nodes[idx].checked && nodes[idx].level() !== 0) {
|
|
checkedNodes.push(nodes[idx].uniqueName);
|
|
}
|
|
if (nodes[idx].hasChildren) {
|
|
checkedNodeIds(nodes[idx].children.view(), checkedNodes);
|
|
}
|
|
}
|
|
}
|
|
var LABELMENUTEMPLATE = '<div class="k-filterable k-content" tabindex="-1" data-role="fieldmenu">' + '<form class="k-filter-menu">' + '<div>' + '<div class="k-filter-help-text">#=messages.info#</div>' + '<select>' + '#for(var op in messages.operators){#' + '<option value="#=op#">#=messages.operators[op]#</option>' + '#}#' + '</select>' + '<input class="k-textbox" type="text" />' + '<div>' + '<a class="k-button k-primary k-button-filter" href="\\#">#=messages.filter#</a>' + '<a class="k-button k-button-clear" href="\\#">#=messages.clear#</a>' + '</div>' + '</div>' + '</form>' + '</div>';
|
|
var MENUTEMPLATE = '<ul class="k-pivot-fieldmenu">' + '# if (sortable) {#' + '<li class="k-item k-sort-asc">' + '<span class="k-link">' + '<span class="k-icon k-i-sort-asc"></span>' + '${messages.sortAscending}' + '</span>' + '</li>' + '<li class="k-item k-sort-desc">' + '<span class="k-link">' + '<span class="k-icon k-i-sort-desc"></span>' + '${messages.sortDescending}' + '</span>' + '</li>' + '# if (filterable) {#' + '<li class="k-separator"></li>' + '# } #' + '# } #' + '# if (filterable) {#' + '<li class="k-item k-include-item">' + '<span class="k-link">' + '<span class="k-icon k-filter"></span>' + '${messages.include}' + '</span>' + '</li>' + '<li class="k-separator"></li>' + '<li class="k-item k-filter-item">' + '<span class="k-link">' + '<span class="k-icon k-filter"></span>' + '${messages.filterFields}' + '</span>' + '<ul>' + '<li>' + LABELMENUTEMPLATE + '</li>' + '</ul>' + '</li>' + '# } #' + '</ul>';
|
|
var WINDOWTEMPLATE = '<div class="k-popup-edit-form k-pivot-filter-window"><div class="k-edit-form-container">' + '<div class="k-treeview"></div>' + '<div class="k-edit-buttons k-state-default">' + '<a class="k-button k-primary k-button-ok" href="\\#">' + '${messages.ok}' + '</a>' + '<a class="k-button k-button-cancel" href="\\#">' + '${messages.cancel}' + '</a>' + '</div></div>';
|
|
ui.plugin(PivotFieldMenu);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.filtercell', [
|
|
'kendo.autocomplete',
|
|
'kendo.datepicker',
|
|
'kendo.numerictextbox',
|
|
'kendo.combobox',
|
|
'kendo.dropdownlist'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'filtercell',
|
|
name: 'Row filter',
|
|
category: 'framework',
|
|
depends: ['autocomplete'],
|
|
advanced: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, DataSource = kendo.data.DataSource, Widget = ui.Widget, CHANGE = 'change', BOOL = 'boolean', ENUM = 'enums', STRING = 'string', EQ = 'Is equal to', NEQ = 'Is not equal to', proxy = $.proxy, nonValueOperators = [
|
|
'isnull',
|
|
'isnotnull',
|
|
'isempty',
|
|
'isnotempty'
|
|
];
|
|
function isNonValueFilter(filter) {
|
|
var operator = typeof filter === 'string' ? filter : filter.operator;
|
|
return $.inArray(operator, nonValueOperators) > -1;
|
|
}
|
|
function findFilterForField(filter, field) {
|
|
var filters = [];
|
|
if ($.isPlainObject(filter)) {
|
|
if (filter.hasOwnProperty('filters')) {
|
|
filters = filter.filters;
|
|
} else if (filter.field == field) {
|
|
return filter;
|
|
}
|
|
}
|
|
if ($.isArray(filter)) {
|
|
filters = filter;
|
|
}
|
|
for (var i = 0; i < filters.length; i++) {
|
|
var result = findFilterForField(filters[i], field);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
function removeFiltersForField(expression, field) {
|
|
if (expression.filters) {
|
|
expression.filters = $.grep(expression.filters, function (filter) {
|
|
removeFiltersForField(filter, field);
|
|
if (filter.filters) {
|
|
return filter.filters.length;
|
|
} else {
|
|
return filter.field != field;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function removeDuplicates(dataSelector, dataTextField) {
|
|
var getter = kendo.getter(dataTextField, true);
|
|
return function (e) {
|
|
var items = dataSelector(e), result = [], index = 0, seen = {};
|
|
while (index < items.length) {
|
|
var item = items[index++], text = getter(item);
|
|
if (!seen.hasOwnProperty(text)) {
|
|
result.push(item);
|
|
seen[text] = true;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
var FilterCell = Widget.extend({
|
|
init: function (element, options) {
|
|
element = $(element).addClass('k-filtercell');
|
|
var wrapper = this.wrapper = $('<span/>').appendTo(element);
|
|
var that = this, dataSource, viewModel, passedOptions = options, first, type, operators = that.operators = options.operators || {}, input = that.input = $('<input/>').attr(kendo.attr('bind'), 'value: value').appendTo(wrapper);
|
|
Widget.fn.init.call(that, element[0], options);
|
|
options = that.options;
|
|
dataSource = that.dataSource = options.dataSource;
|
|
that.model = dataSource.reader.model;
|
|
type = options.type = STRING;
|
|
var fields = kendo.getter('reader.model.fields', true)(dataSource) || {};
|
|
var target = fields[options.field];
|
|
if (target && target.type) {
|
|
type = options.type = target.type;
|
|
}
|
|
if (options.values) {
|
|
options.type = type = ENUM;
|
|
}
|
|
operators = operators[type] || options.operators[type];
|
|
if (!passedOptions.operator) {
|
|
for (first in operators) {
|
|
options.operator = first;
|
|
break;
|
|
}
|
|
}
|
|
that._parse = function (value) {
|
|
return value != null ? value + '' : value;
|
|
};
|
|
if (that.model && that.model.fields) {
|
|
var field = that.model.fields[options.field];
|
|
if (field) {
|
|
if (field.parse) {
|
|
that._parse = proxy(field.parse, field);
|
|
}
|
|
}
|
|
}
|
|
that.defaultOperator = options.operator;
|
|
that.viewModel = viewModel = kendo.observable({
|
|
operator: options.operator,
|
|
value: null,
|
|
operatorVisible: function () {
|
|
var val = this.get('value');
|
|
return val !== null && val !== undefined && val != 'undefined' || isNonValueFilter(this.get('operator')) && !that._clearInProgress;
|
|
}
|
|
});
|
|
viewModel.bind(CHANGE, proxy(that.updateDsFilter, that));
|
|
if (type == STRING) {
|
|
that.initSuggestDataSource(options);
|
|
}
|
|
if (options.inputWidth !== null) {
|
|
input.width(options.inputWidth);
|
|
}
|
|
that._setInputType(options, type);
|
|
if (type != BOOL && options.showOperators !== false) {
|
|
that._createOperatorDropDown(operators);
|
|
} else {
|
|
wrapper.addClass('k-operator-hidden');
|
|
}
|
|
that._createClearIcon();
|
|
kendo.bind(this.wrapper, viewModel);
|
|
if (type == STRING) {
|
|
if (!options.template) {
|
|
that.setAutoCompleteSource();
|
|
}
|
|
}
|
|
if (type == ENUM) {
|
|
that.setComboBoxSource(that.options.values);
|
|
}
|
|
that._refreshUI();
|
|
that._refreshHandler = proxy(that._refreshUI, that);
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
},
|
|
_setInputType: function (options, type) {
|
|
var that = this, input = that.input;
|
|
if (typeof options.template == 'function') {
|
|
options.template.call(that.viewModel, {
|
|
element: that.input,
|
|
dataSource: that.suggestDataSource
|
|
});
|
|
that._angularItems('compile');
|
|
} else if (type == STRING) {
|
|
input.attr(kendo.attr('role'), 'autocomplete').attr(kendo.attr('text-field'), options.dataTextField || options.field).attr(kendo.attr('filter'), options.suggestionOperator).attr(kendo.attr('delay'), options.delay).attr(kendo.attr('min-length'), options.minLength).attr(kendo.attr('value-primitive'), true);
|
|
} else if (type == 'date') {
|
|
input.attr(kendo.attr('role'), 'datepicker');
|
|
} else if (type == BOOL) {
|
|
input.remove();
|
|
var radioInput = $('<input type=\'radio\'/>');
|
|
var wrapper = that.wrapper;
|
|
var inputName = kendo.guid();
|
|
var labelTrue = $('<label/>').text(options.messages.isTrue).append(radioInput);
|
|
radioInput.attr(kendo.attr('bind'), 'checked:value').attr('name', inputName).val('true');
|
|
var labelFalse = labelTrue.clone().text(options.messages.isFalse);
|
|
radioInput.clone().val('false').appendTo(labelFalse);
|
|
wrapper.append([
|
|
labelTrue,
|
|
labelFalse
|
|
]);
|
|
} else if (type == 'number') {
|
|
input.attr(kendo.attr('role'), 'numerictextbox');
|
|
} else if (type == ENUM) {
|
|
input.attr(kendo.attr('role'), 'combobox').attr(kendo.attr('text-field'), 'text').attr(kendo.attr('suggest'), true).attr(kendo.attr('filter'), 'contains').attr(kendo.attr('value-field'), 'value').attr(kendo.attr('value-primitive'), true);
|
|
}
|
|
},
|
|
_createOperatorDropDown: function (operators) {
|
|
var items = [];
|
|
for (var prop in operators) {
|
|
items.push({
|
|
text: operators[prop],
|
|
value: prop
|
|
});
|
|
}
|
|
var dropdown = $('<input class="k-dropdown-operator" ' + kendo.attr('bind') + '="value: operator"/>').appendTo(this.wrapper);
|
|
this.operatorDropDown = dropdown.kendoDropDownList({
|
|
dataSource: items,
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
open: function () {
|
|
this.popup.element.width(150);
|
|
},
|
|
valuePrimitive: true
|
|
}).data('kendoDropDownList');
|
|
this.operatorDropDown.wrapper.find('.k-i-arrow-s').removeClass('k-i-arrow-s').addClass('k-filter');
|
|
},
|
|
initSuggestDataSource: function (options) {
|
|
var suggestDataSource = options.suggestDataSource;
|
|
if (!(suggestDataSource instanceof DataSource)) {
|
|
if (!options.customDataSource && suggestDataSource) {
|
|
suggestDataSource.group = undefined;
|
|
}
|
|
suggestDataSource = this.suggestDataSource = DataSource.create(suggestDataSource);
|
|
}
|
|
if (!options.customDataSource) {
|
|
suggestDataSource._pageSize = undefined;
|
|
suggestDataSource.reader.data = removeDuplicates(suggestDataSource.reader.data, this.options.field);
|
|
}
|
|
this.suggestDataSource = suggestDataSource;
|
|
},
|
|
setAutoCompleteSource: function () {
|
|
var autoComplete = this.input.data('kendoAutoComplete');
|
|
if (autoComplete) {
|
|
autoComplete.setDataSource(this.suggestDataSource);
|
|
}
|
|
},
|
|
setComboBoxSource: function (values) {
|
|
var dataSource = DataSource.create({ data: values });
|
|
var comboBox = this.input.data('kendoComboBox');
|
|
if (comboBox) {
|
|
comboBox.setDataSource(dataSource);
|
|
}
|
|
},
|
|
_refreshUI: function () {
|
|
var that = this, filter = findFilterForField(that.dataSource.filter(), this.options.field) || {}, viewModel = that.viewModel;
|
|
that.manuallyUpdatingVM = true;
|
|
filter = $.extend(true, {}, filter);
|
|
if (that.options.type == BOOL) {
|
|
if (viewModel.value !== filter.value) {
|
|
that.wrapper.find(':radio').prop('checked', false);
|
|
}
|
|
}
|
|
if (filter.operator) {
|
|
viewModel.set('operator', filter.operator);
|
|
}
|
|
viewModel.set('value', filter.value);
|
|
that.manuallyUpdatingVM = false;
|
|
},
|
|
updateDsFilter: function (e) {
|
|
var that = this, model = that.viewModel;
|
|
if (that.manuallyUpdatingVM || e.field == 'operator' && model.value === undefined && !isNonValueFilter(model)) {
|
|
return;
|
|
}
|
|
var currentFilter = $.extend({}, that.viewModel.toJSON(), { field: that.options.field });
|
|
var expression = {
|
|
logic: 'and',
|
|
filters: []
|
|
};
|
|
if (currentFilter.value !== undefined && currentFilter.value !== null || isNonValueFilter(currentFilter) && !this._clearInProgress) {
|
|
expression.filters.push(currentFilter);
|
|
}
|
|
var mergeResult = that._merge(expression);
|
|
if (mergeResult.filters.length) {
|
|
that.dataSource.filter(mergeResult);
|
|
} else {
|
|
that.dataSource.filter({});
|
|
}
|
|
},
|
|
_merge: function (expression) {
|
|
var that = this, logic = expression.logic || 'and', filters = expression.filters, filter, result = that.dataSource.filter() || {
|
|
filters: [],
|
|
logic: 'and'
|
|
}, idx, length;
|
|
removeFiltersForField(result, that.options.field);
|
|
for (idx = 0, length = filters.length; idx < length; idx++) {
|
|
filter = filters[idx];
|
|
filter.value = that._parse(filter.value);
|
|
}
|
|
filters = $.grep(filters, function (filter) {
|
|
return filter.value !== '' && filter.value !== null || isNonValueFilter(filter);
|
|
});
|
|
if (filters.length) {
|
|
if (result.filters.length) {
|
|
expression.filters = filters;
|
|
if (result.logic !== 'and') {
|
|
result.filters = [{
|
|
logic: result.logic,
|
|
filters: result.filters
|
|
}];
|
|
result.logic = 'and';
|
|
}
|
|
if (filters.length > 1) {
|
|
result.filters.push(expression);
|
|
} else {
|
|
result.filters.push(filters[0]);
|
|
}
|
|
} else {
|
|
result.filters = filters;
|
|
result.logic = logic;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_createClearIcon: function () {
|
|
var that = this;
|
|
$('<button type=\'button\' class=\'k-button k-button-icon\' title = ' + that.options.messages.clear + '/>').attr(kendo.attr('bind'), 'visible:operatorVisible').html('<span class=\'k-icon k-i-close\'/>').click(proxy(that.clearFilter, that)).appendTo(that.wrapper);
|
|
},
|
|
clearFilter: function () {
|
|
this._clearInProgress = true;
|
|
if (isNonValueFilter(this.viewModel.operator)) {
|
|
this.viewModel.set('operator', this.defaultOperator);
|
|
}
|
|
this.viewModel.set('value', null);
|
|
this._clearInProgress = false;
|
|
},
|
|
_angularItems: function (action) {
|
|
var elements = this.wrapper.closest('th').get();
|
|
var column = this.options.column;
|
|
this.angular(action, function () {
|
|
return {
|
|
elements: elements,
|
|
data: [{ column: column }]
|
|
};
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.filterModel = null;
|
|
that.operatorDropDown = null;
|
|
that._angularItems('cleanup');
|
|
if (that._refreshHandler) {
|
|
that.dataSource.bind(CHANGE, that._refreshHandler);
|
|
that._refreshHandler = null;
|
|
}
|
|
kendo.unbind(that.element);
|
|
Widget.fn.destroy.call(that);
|
|
kendo.destroy(that.element);
|
|
},
|
|
events: [CHANGE],
|
|
options: {
|
|
name: 'FilterCell',
|
|
delay: 200,
|
|
minLength: 1,
|
|
inputWidth: null,
|
|
values: undefined,
|
|
customDataSource: false,
|
|
field: '',
|
|
dataTextField: '',
|
|
type: 'string',
|
|
suggestDataSource: null,
|
|
suggestionOperator: 'startswith',
|
|
operator: 'eq',
|
|
showOperators: true,
|
|
template: null,
|
|
messages: {
|
|
isTrue: 'is true',
|
|
isFalse: 'is false',
|
|
filter: 'Filter',
|
|
clear: 'Clear',
|
|
operator: 'Operator'
|
|
},
|
|
operators: {
|
|
string: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
startswith: 'Starts with',
|
|
contains: 'Contains',
|
|
doesnotcontain: 'Does not contain',
|
|
endswith: 'Ends with',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null',
|
|
isempty: 'Is empty',
|
|
isnotempty: 'Is not empty'
|
|
},
|
|
number: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
gte: 'Is greater than or equal to',
|
|
gt: 'Is greater than',
|
|
lte: 'Is less than or equal to',
|
|
lt: 'Is less than',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
},
|
|
date: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
gte: 'Is after or equal to',
|
|
gt: 'Is after',
|
|
lte: 'Is before or equal to',
|
|
lt: 'Is before',
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
},
|
|
enums: {
|
|
eq: EQ,
|
|
neq: NEQ,
|
|
isnull: 'Is null',
|
|
isnotnull: 'Is not null'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(FilterCell);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.panelbar', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'panelbar',
|
|
name: 'PanelBar',
|
|
category: 'web',
|
|
description: 'The PanelBar widget displays hierarchical data as a multi-level expandable panel bar.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, extend = $.extend, each = $.each, template = kendo.template, Widget = ui.Widget, excludedNodesRegExp = /^(ul|a|div)$/i, NS = '.kendoPanelBar', IMG = 'img', HREF = 'href', LAST = 'k-last', LINK = 'k-link', LINKSELECTOR = '.' + LINK, ERROR = 'error', ITEM = '.k-item', GROUP = '.k-group', VISIBLEGROUP = GROUP + ':visible', IMAGE = 'k-image', FIRST = 'k-first', EXPAND = 'expand', SELECT = 'select', CONTENT = 'k-content', ACTIVATE = 'activate', COLLAPSE = 'collapse', MOUSEENTER = 'mouseenter', MOUSELEAVE = 'mouseleave', CONTENTLOAD = 'contentLoad', ACTIVECLASS = 'k-state-active', GROUPS = '> .k-panel', CONTENTS = '> .k-content', FOCUSEDCLASS = 'k-state-focused', DISABLEDCLASS = 'k-state-disabled', SELECTEDCLASS = 'k-state-selected', SELECTEDSELECTOR = '.' + SELECTEDCLASS, HIGHLIGHTCLASS = 'k-state-highlight', ACTIVEITEMSELECTOR = ITEM + ':not(.k-state-disabled)', clickableItems = '> ' + ACTIVEITEMSELECTOR + ' > ' + LINKSELECTOR + ', .k-panel > ' + ACTIVEITEMSELECTOR + ' > ' + LINKSELECTOR, disabledItems = ITEM + '.k-state-disabled > .k-link', selectableItems = '> li > ' + SELECTEDSELECTOR + ', .k-panel > li > ' + SELECTEDSELECTOR, defaultState = 'k-state-default', ARIA_DISABLED = 'aria-disabled', ARIA_EXPANDED = 'aria-expanded', ARIA_HIDDEN = 'aria-hidden', ARIA_SELECTED = 'aria-selected', VISIBLE = ':visible', EMPTY = ':empty', SINGLE = 'single', templates = {
|
|
content: template('<div role=\'region\' class=\'k-content\'#= contentAttributes(data) #>#= content(item) #</div>'),
|
|
group: template('<ul role=\'group\' aria-hidden=\'true\' class=\'#= groupCssClass(group) #\'#= groupAttributes(group) #>' + '#= renderItems(data) #' + '</ul>'),
|
|
itemWrapper: template('<#= tag(item) # class=\'#= textClass(item, group) #\' #= contentUrl(item) ##= textAttributes(item) #>' + '#= image(item) ##= sprite(item) ##= text(item) #' + '#= arrow(data) #' + '</#= tag(item) #>'),
|
|
item: template('<li role=\'menuitem\' #=aria(item)#class=\'#= wrapperCssClass(group, item) #\'>' + '#= itemWrapper(data) #' + '# if (item.items) { #' + '#= subGroup({ items: item.items, panelBar: panelBar, group: { expanded: item.expanded } }) #' + '# } else if (item.content || item.contentUrl) { #' + '#= renderContent(data) #' + '# } #' + '</li>'),
|
|
image: template('<img class=\'k-image\' alt=\'\' src=\'#= imageUrl #\' />'),
|
|
arrow: template('<span class=\'#= arrowClass(item) #\'></span>'),
|
|
sprite: template('<span class=\'k-sprite #= spriteCssClass #\'></span>'),
|
|
empty: template('')
|
|
}, rendering = {
|
|
aria: function (item) {
|
|
var attr = '';
|
|
if (item.items || item.content || item.contentUrl) {
|
|
attr += ARIA_EXPANDED + '=\'' + (item.expanded ? 'true' : 'false') + '\' ';
|
|
}
|
|
if (item.enabled === false) {
|
|
attr += ARIA_DISABLED + '=\'true\'';
|
|
}
|
|
return attr;
|
|
},
|
|
wrapperCssClass: function (group, item) {
|
|
var result = 'k-item', index = item.index;
|
|
if (item.enabled === false) {
|
|
result += ' ' + DISABLEDCLASS;
|
|
} else if (item.expanded === true) {
|
|
result += ' ' + ACTIVECLASS;
|
|
} else {
|
|
result += ' k-state-default';
|
|
}
|
|
if (index === 0) {
|
|
result += ' k-first';
|
|
}
|
|
if (index == group.length - 1) {
|
|
result += ' k-last';
|
|
}
|
|
if (item.cssClass) {
|
|
result += ' ' + item.cssClass;
|
|
}
|
|
return result;
|
|
},
|
|
textClass: function (item, group) {
|
|
var result = LINK;
|
|
if (group.firstLevel) {
|
|
result += ' k-header';
|
|
}
|
|
return result;
|
|
},
|
|
textAttributes: function (item) {
|
|
return item.url ? ' href=\'' + item.url + '\'' : '';
|
|
},
|
|
arrowClass: function (item) {
|
|
var result = 'k-icon';
|
|
result += item.expanded ? ' k-i-arrow-n k-panelbar-collapse' : ' k-i-arrow-s k-panelbar-expand';
|
|
return result;
|
|
},
|
|
text: function (item) {
|
|
return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
|
|
},
|
|
tag: function (item) {
|
|
return item.url || item.contentUrl ? 'a' : 'span';
|
|
},
|
|
groupAttributes: function (group) {
|
|
return group.expanded !== true ? ' style=\'display:none\'' : '';
|
|
},
|
|
groupCssClass: function () {
|
|
return 'k-group k-panel';
|
|
},
|
|
contentAttributes: function (content) {
|
|
return content.item.expanded !== true ? ' style=\'display:none\'' : '';
|
|
},
|
|
content: function (item) {
|
|
return item.content ? item.content : item.contentUrl ? '' : ' ';
|
|
},
|
|
contentUrl: function (item) {
|
|
return item.contentUrl ? 'href="' + item.contentUrl + '"' : '';
|
|
}
|
|
};
|
|
function updateArrow(items) {
|
|
items = $(items);
|
|
items.children(LINKSELECTOR).children('.k-icon').remove();
|
|
items.filter(':has(.k-panel),:has(.k-content)').children('.k-link:not(:has([class*=k-i-arrow]))').each(function () {
|
|
var item = $(this), parent = item.parent();
|
|
item.append('<span class=\'k-icon ' + (parent.hasClass(ACTIVECLASS) ? 'k-i-arrow-n k-panelbar-collapse' : 'k-i-arrow-s k-panelbar-expand') + '\'/>');
|
|
});
|
|
}
|
|
function updateFirstLast(items) {
|
|
items = $(items);
|
|
items.filter('.k-first:not(:first-child)').removeClass(FIRST);
|
|
items.filter('.k-last:not(:last-child)').removeClass(LAST);
|
|
items.filter(':first-child').addClass(FIRST);
|
|
items.filter(':last-child').addClass(LAST);
|
|
}
|
|
var PanelBar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, content;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.wrapper = that.element.addClass('k-widget k-reset k-header k-panelbar');
|
|
options = that.options;
|
|
if (element[0].id) {
|
|
that._itemId = element[0].id + '_pb_active';
|
|
}
|
|
that._tabindex();
|
|
that._initData(options);
|
|
that._updateClasses();
|
|
that._animations(options);
|
|
element.on('click' + NS, clickableItems, function (e) {
|
|
if (that._click($(e.currentTarget))) {
|
|
e.preventDefault();
|
|
}
|
|
}).on(MOUSEENTER + NS + ' ' + MOUSELEAVE + NS, clickableItems, that._toggleHover).on('click' + NS, disabledItems, false).on('keydown' + NS, $.proxy(that._keydown, that)).on('focus' + NS, function () {
|
|
var item = that.select();
|
|
that._current(item[0] ? item : that._first());
|
|
}).on('blur' + NS, function () {
|
|
that._current(null);
|
|
}).attr('role', 'menu');
|
|
content = element.find('li.' + ACTIVECLASS + ' > .' + CONTENT);
|
|
if (content[0]) {
|
|
that.expand(content.parent(), false);
|
|
}
|
|
if (options.dataSource) {
|
|
that._angularCompile();
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
EXPAND,
|
|
COLLAPSE,
|
|
SELECT,
|
|
ACTIVATE,
|
|
ERROR,
|
|
CONTENTLOAD
|
|
],
|
|
options: {
|
|
name: 'PanelBar',
|
|
animation: {
|
|
expand: {
|
|
effects: 'expand:vertical',
|
|
duration: 200
|
|
},
|
|
collapse: { duration: 200 }
|
|
},
|
|
expandMode: 'multiple'
|
|
},
|
|
_angularCompile: function () {
|
|
var that = this;
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: that.element.children('li'),
|
|
data: [{ dataItem: that.options.$angular }]
|
|
};
|
|
});
|
|
},
|
|
_angularCleanup: function () {
|
|
var that = this;
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.element.children('li') };
|
|
});
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.element.off(NS);
|
|
this._angularCleanup();
|
|
kendo.destroy(this.element);
|
|
},
|
|
_initData: function (options) {
|
|
var that = this;
|
|
if (options.dataSource) {
|
|
that.element.empty();
|
|
that.append(options.dataSource, that.element);
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var animation = this.options.animation;
|
|
this._animations(options);
|
|
options.animation = extend(true, animation, options.animation);
|
|
if ('dataSource' in options) {
|
|
this._initData(options);
|
|
}
|
|
Widget.fn.setOptions.call(this, options);
|
|
},
|
|
expand: function (element, useAnimation) {
|
|
var that = this, animBackup = {};
|
|
element = this.element.find(element);
|
|
if (that._animating && element.find('ul').is(':visible')) {
|
|
that.one('complete', function () {
|
|
setTimeout(function () {
|
|
that.expand(element);
|
|
});
|
|
});
|
|
return;
|
|
}
|
|
that._animating = true;
|
|
useAnimation = useAnimation !== false;
|
|
element.each(function (index, item) {
|
|
item = $(item);
|
|
var groups = item.find(GROUPS).add(item.find(CONTENTS));
|
|
if (!item.hasClass(DISABLEDCLASS) && groups.length > 0) {
|
|
if (that.options.expandMode == SINGLE && that._collapseAllExpanded(item)) {
|
|
return that;
|
|
}
|
|
element.find('.' + HIGHLIGHTCLASS).removeClass(HIGHLIGHTCLASS);
|
|
item.addClass(HIGHLIGHTCLASS);
|
|
if (!useAnimation) {
|
|
animBackup = that.options.animation;
|
|
that.options.animation = {
|
|
expand: { effects: {} },
|
|
collapse: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
}
|
|
if (!that._triggerEvent(EXPAND, item)) {
|
|
that._toggleItem(item, false);
|
|
}
|
|
if (!useAnimation) {
|
|
that.options.animation = animBackup;
|
|
}
|
|
}
|
|
});
|
|
return that;
|
|
},
|
|
collapse: function (element, useAnimation) {
|
|
var that = this, animBackup = {};
|
|
that._animating = true;
|
|
useAnimation = useAnimation !== false;
|
|
element = that.element.find(element);
|
|
element.each(function (index, item) {
|
|
item = $(item);
|
|
var groups = item.find(GROUPS).add(item.find(CONTENTS));
|
|
if (!item.hasClass(DISABLEDCLASS) && groups.is(VISIBLE)) {
|
|
item.removeClass(HIGHLIGHTCLASS);
|
|
if (!useAnimation) {
|
|
animBackup = that.options.animation;
|
|
that.options.animation = {
|
|
expand: { effects: {} },
|
|
collapse: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
}
|
|
if (!that._triggerEvent(COLLAPSE, item)) {
|
|
that._toggleItem(item, true);
|
|
}
|
|
if (!useAnimation) {
|
|
that.options.animation = animBackup;
|
|
}
|
|
}
|
|
});
|
|
return that;
|
|
},
|
|
_toggleDisabled: function (element, enable) {
|
|
element = this.element.find(element);
|
|
element.toggleClass(defaultState, enable).toggleClass(DISABLEDCLASS, !enable).attr(ARIA_DISABLED, !enable);
|
|
},
|
|
select: function (element) {
|
|
var that = this;
|
|
if (element === undefined) {
|
|
return that.element.find(selectableItems).parent();
|
|
}
|
|
element = that.element.find(element);
|
|
if (!element.length) {
|
|
this._updateSelected(element);
|
|
} else {
|
|
element.each(function () {
|
|
var item = $(this), link = item.children(LINKSELECTOR);
|
|
if (item.hasClass(DISABLEDCLASS)) {
|
|
return that;
|
|
}
|
|
if (!that._triggerEvent(SELECT, item)) {
|
|
that._updateSelected(link);
|
|
}
|
|
});
|
|
}
|
|
return that;
|
|
},
|
|
clearSelection: function () {
|
|
this.select($());
|
|
},
|
|
enable: function (element, state) {
|
|
this._toggleDisabled(element, state !== false);
|
|
return this;
|
|
},
|
|
disable: function (element) {
|
|
this._toggleDisabled(element, false);
|
|
return this;
|
|
},
|
|
append: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find(GROUPS) : null);
|
|
each(inserted.items, function () {
|
|
inserted.group.append(this);
|
|
updateFirstLast(this);
|
|
});
|
|
updateArrow(referenceItem);
|
|
updateFirstLast(inserted.group.find('.k-first, .k-last'));
|
|
inserted.group.height('auto');
|
|
return this;
|
|
},
|
|
insertBefore: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.parent());
|
|
each(inserted.items, function () {
|
|
referenceItem.before(this);
|
|
updateFirstLast(this);
|
|
});
|
|
updateFirstLast(referenceItem);
|
|
inserted.group.height('auto');
|
|
return this;
|
|
},
|
|
insertAfter: function (item, referenceItem) {
|
|
referenceItem = this.element.find(referenceItem);
|
|
var inserted = this._insert(item, referenceItem, referenceItem.parent());
|
|
each(inserted.items, function () {
|
|
referenceItem.after(this);
|
|
updateFirstLast(this);
|
|
});
|
|
updateFirstLast(referenceItem);
|
|
inserted.group.height('auto');
|
|
return this;
|
|
},
|
|
remove: function (element) {
|
|
element = this.element.find(element);
|
|
var that = this, parent = element.parentsUntil(that.element, ITEM), group = element.parent('ul');
|
|
element.remove();
|
|
if (group && !group.hasClass('k-panelbar') && !group.children(ITEM).length) {
|
|
group.remove();
|
|
}
|
|
if (parent.length) {
|
|
parent = parent.eq(0);
|
|
updateArrow(parent);
|
|
updateFirstLast(parent);
|
|
}
|
|
return that;
|
|
},
|
|
reload: function (element) {
|
|
var that = this;
|
|
element = that.element.find(element);
|
|
element.each(function () {
|
|
var item = $(this);
|
|
that._ajaxRequest(item, item.children('.' + CONTENT), !item.is(VISIBLE));
|
|
});
|
|
},
|
|
_first: function () {
|
|
return this.element.children(ACTIVEITEMSELECTOR).first();
|
|
},
|
|
_last: function () {
|
|
var item = this.element.children(ACTIVEITEMSELECTOR).last(), group = item.children(VISIBLEGROUP);
|
|
if (group[0]) {
|
|
return group.children(ACTIVEITEMSELECTOR).last();
|
|
}
|
|
return item;
|
|
},
|
|
_current: function (candidate) {
|
|
var that = this, focused = that._focused, id = that._itemId;
|
|
if (candidate === undefined) {
|
|
return focused;
|
|
}
|
|
that.element.removeAttr('aria-activedescendant');
|
|
if (focused && focused.length) {
|
|
if (focused[0].id === id) {
|
|
focused.removeAttr('id');
|
|
}
|
|
focused.children(LINKSELECTOR).removeClass(FOCUSEDCLASS);
|
|
}
|
|
if ($(candidate).length) {
|
|
id = candidate[0].id || id;
|
|
candidate.attr('id', id).children(LINKSELECTOR).addClass(FOCUSEDCLASS);
|
|
that.element.attr('aria-activedescendant', id);
|
|
}
|
|
that._focused = candidate;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, current = that._current();
|
|
if (e.target != e.currentTarget) {
|
|
return;
|
|
}
|
|
if (key == keys.DOWN || key == keys.RIGHT) {
|
|
that._current(that._nextItem(current));
|
|
e.preventDefault();
|
|
} else if (key == keys.UP || key == keys.LEFT) {
|
|
that._current(that._prevItem(current));
|
|
e.preventDefault();
|
|
} else if (key == keys.ENTER || key == keys.SPACEBAR) {
|
|
that._click(current.children(LINKSELECTOR));
|
|
e.preventDefault();
|
|
} else if (key == keys.HOME) {
|
|
that._current(that._first());
|
|
e.preventDefault();
|
|
} else if (key == keys.END) {
|
|
that._current(that._last());
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_nextItem: function (item) {
|
|
if (!item) {
|
|
return this._first();
|
|
}
|
|
var group = item.children(VISIBLEGROUP), next = item.nextAll(':visible').first();
|
|
if (group[0]) {
|
|
next = group.children('.' + FIRST);
|
|
}
|
|
if (!next[0]) {
|
|
next = item.parent(VISIBLEGROUP).parent(ITEM).next();
|
|
}
|
|
if (!next[0]) {
|
|
next = this._first();
|
|
}
|
|
if (next.hasClass(DISABLEDCLASS)) {
|
|
next = this._nextItem(next);
|
|
}
|
|
return next;
|
|
},
|
|
_prevItem: function (item) {
|
|
if (!item) {
|
|
return this._last();
|
|
}
|
|
var prev = item.prevAll(':visible').first(), result;
|
|
if (!prev[0]) {
|
|
prev = item.parent(VISIBLEGROUP).parent(ITEM);
|
|
if (!prev[0]) {
|
|
prev = this._last();
|
|
}
|
|
} else {
|
|
result = prev;
|
|
while (result[0]) {
|
|
result = result.children(VISIBLEGROUP).children('.' + LAST);
|
|
if (result[0]) {
|
|
prev = result;
|
|
}
|
|
}
|
|
}
|
|
if (prev.hasClass(DISABLEDCLASS)) {
|
|
prev = this._prevItem(prev);
|
|
}
|
|
return prev;
|
|
},
|
|
_insert: function (item, referenceItem, parent) {
|
|
var that = this, items, plain = $.isPlainObject(item), isReferenceItem = referenceItem && referenceItem[0], groupData;
|
|
if (!isReferenceItem) {
|
|
parent = that.element;
|
|
}
|
|
groupData = {
|
|
firstLevel: parent.hasClass('k-panelbar'),
|
|
expanded: parent.parent().hasClass(ACTIVECLASS),
|
|
length: parent.children().length
|
|
};
|
|
if (isReferenceItem && !parent.length) {
|
|
parent = $(PanelBar.renderGroup({ group: groupData })).appendTo(referenceItem);
|
|
}
|
|
if (item instanceof kendo.Observable) {
|
|
item = item.toJSON();
|
|
}
|
|
if (plain || $.isArray(item)) {
|
|
items = $.map(plain ? [item] : item, function (value, idx) {
|
|
if (typeof value === 'string') {
|
|
return $(value);
|
|
} else {
|
|
return $(PanelBar.renderItem({
|
|
group: groupData,
|
|
item: extend(value, { index: idx })
|
|
}));
|
|
}
|
|
});
|
|
if (isReferenceItem) {
|
|
referenceItem.attr(ARIA_EXPANDED, false);
|
|
}
|
|
} else {
|
|
if (typeof item == 'string' && item.charAt(0) != '<') {
|
|
items = that.element.find(item);
|
|
} else {
|
|
items = $(item);
|
|
}
|
|
that._updateItemsClasses(items);
|
|
}
|
|
return {
|
|
items: items,
|
|
group: parent
|
|
};
|
|
},
|
|
_toggleHover: function (e) {
|
|
var target = $(e.currentTarget);
|
|
if (!target.parents('li.' + DISABLEDCLASS).length) {
|
|
target.toggleClass('k-state-hover', e.type == MOUSEENTER);
|
|
}
|
|
},
|
|
_updateClasses: function () {
|
|
var that = this, panels, items;
|
|
panels = that.element.find('li > ul').not(function () {
|
|
return $(this).parentsUntil('.k-panelbar', 'div').length;
|
|
}).addClass('k-group k-panel').attr('role', 'group');
|
|
panels.parent().attr(ARIA_EXPANDED, false).not('.' + ACTIVECLASS).children('ul').attr(ARIA_HIDDEN, true).hide();
|
|
items = that.element.add(panels).children();
|
|
that._updateItemsClasses(items);
|
|
updateArrow(items);
|
|
updateFirstLast(items);
|
|
},
|
|
_updateItemsClasses: function (items) {
|
|
var length = items.length, idx = 0;
|
|
for (; idx < length; idx++) {
|
|
this._updateItemClasses(items[idx], idx);
|
|
}
|
|
},
|
|
_updateItemClasses: function (item, index) {
|
|
var selected = this._selected, contentUrls = this.options.contentUrls, url = contentUrls && contentUrls[index], root = this.element[0], wrapElement, link;
|
|
item = $(item).addClass('k-item').attr('role', 'menuitem');
|
|
if (kendo.support.browser.msie) {
|
|
item.css('list-style-position', 'inside').css('list-style-position', '');
|
|
}
|
|
item.children(IMG).addClass(IMAGE);
|
|
link = item.children('a').addClass(LINK);
|
|
if (link[0]) {
|
|
link.attr('href', url);
|
|
link.children(IMG).addClass(IMAGE);
|
|
}
|
|
item.filter(':not([disabled]):not([class*=k-state])').addClass('k-state-default');
|
|
item.filter('li[disabled]').addClass('k-state-disabled').attr(ARIA_DISABLED, true).removeAttr('disabled');
|
|
item.children('div').addClass(CONTENT).attr('role', 'region').attr(ARIA_HIDDEN, true).hide().parent().attr(ARIA_EXPANDED, false);
|
|
link = item.children(SELECTEDSELECTOR);
|
|
if (link[0]) {
|
|
if (selected) {
|
|
selected.removeAttr(ARIA_SELECTED).children(SELECTEDSELECTOR).removeClass(SELECTEDCLASS);
|
|
}
|
|
link.addClass(SELECTEDCLASS);
|
|
this._selected = item.attr(ARIA_SELECTED, true);
|
|
}
|
|
if (!item.children(LINKSELECTOR)[0]) {
|
|
wrapElement = '<span class=\'' + LINK + '\'/>';
|
|
if (contentUrls && contentUrls[index] && item[0].parentNode == root) {
|
|
wrapElement = '<a class="k-link k-header" href="' + contentUrls[index] + '"/>';
|
|
}
|
|
item.contents().filter(function () {
|
|
return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue));
|
|
}).wrapAll(wrapElement);
|
|
}
|
|
if (item.parent('.k-panelbar')[0]) {
|
|
item.children(LINKSELECTOR).addClass('k-header');
|
|
}
|
|
},
|
|
_click: function (target) {
|
|
var that = this, element = that.element, prevent, contents, href, isAnchor;
|
|
if (target.parents('li.' + DISABLEDCLASS).length) {
|
|
return;
|
|
}
|
|
if (target.closest('.k-widget')[0] != element[0]) {
|
|
return;
|
|
}
|
|
var link = target.closest(LINKSELECTOR), item = link.closest(ITEM);
|
|
that._updateSelected(link);
|
|
contents = item.find(GROUPS).add(item.find(CONTENTS));
|
|
href = link.attr(HREF);
|
|
isAnchor = href && (href.charAt(href.length - 1) == '#' || href.indexOf('#' + that.element[0].id + '-') != -1);
|
|
prevent = !!(isAnchor || contents.length);
|
|
if (contents.data('animating')) {
|
|
return prevent;
|
|
}
|
|
if (that._triggerEvent(SELECT, item)) {
|
|
prevent = true;
|
|
}
|
|
if (prevent === false) {
|
|
return;
|
|
}
|
|
if (that.options.expandMode == SINGLE) {
|
|
if (that._collapseAllExpanded(item)) {
|
|
return prevent;
|
|
}
|
|
}
|
|
if (contents.length) {
|
|
var visibility = contents.is(VISIBLE);
|
|
if (!that._triggerEvent(!visibility ? EXPAND : COLLAPSE, item)) {
|
|
prevent = that._toggleItem(item, visibility);
|
|
}
|
|
}
|
|
return prevent;
|
|
},
|
|
_toggleItem: function (element, isVisible) {
|
|
var that = this, childGroup = element.find(GROUPS), link = element.find(LINKSELECTOR), url = link.attr(HREF), prevent, content;
|
|
if (childGroup.length) {
|
|
this._toggleGroup(childGroup, isVisible);
|
|
prevent = true;
|
|
} else {
|
|
content = element.children('.' + CONTENT);
|
|
if (content.length) {
|
|
prevent = true;
|
|
if (!content.is(EMPTY) || url === undefined) {
|
|
that._toggleGroup(content, isVisible);
|
|
} else {
|
|
that._ajaxRequest(element, content, isVisible);
|
|
}
|
|
}
|
|
}
|
|
return prevent;
|
|
},
|
|
_toggleGroup: function (element, visibility) {
|
|
var that = this, animationSettings = that.options.animation, animation = animationSettings.expand, collapse = extend({}, animationSettings.collapse), hasCollapseAnimation = collapse && 'effects' in collapse;
|
|
if (element.is(VISIBLE) != visibility) {
|
|
that._animating = false;
|
|
return;
|
|
}
|
|
element.parent().attr(ARIA_EXPANDED, !visibility).attr(ARIA_HIDDEN, visibility).toggleClass(ACTIVECLASS, !visibility).find('> .k-link > .k-icon').toggleClass('k-i-arrow-n', !visibility).toggleClass('k-panelbar-collapse', !visibility).toggleClass('k-i-arrow-s', visibility).toggleClass('k-panelbar-expand', visibility);
|
|
if (visibility) {
|
|
animation = extend(hasCollapseAnimation ? collapse : extend({ reverse: true }, animation), { hide: true });
|
|
animation.complete = function () {
|
|
that._animationCallback();
|
|
};
|
|
} else {
|
|
animation = extend({
|
|
complete: function (element) {
|
|
that._triggerEvent(ACTIVATE, element.closest(ITEM));
|
|
that._animationCallback();
|
|
}
|
|
}, animation);
|
|
}
|
|
element.kendoStop(true, true).kendoAnimate(animation);
|
|
},
|
|
_animationCallback: function () {
|
|
var that = this;
|
|
that.trigger('complete');
|
|
that._animating = false;
|
|
},
|
|
_collapseAllExpanded: function (item) {
|
|
var that = this, children, stopExpand = false;
|
|
var groups = item.find(GROUPS).add(item.find(CONTENTS));
|
|
if (groups.is(VISIBLE)) {
|
|
stopExpand = true;
|
|
}
|
|
if (!(groups.is(VISIBLE) || groups.length === 0)) {
|
|
children = item.siblings();
|
|
children.find(GROUPS).add(children.find(CONTENTS)).filter(function () {
|
|
return $(this).is(VISIBLE);
|
|
}).each(function (index, content) {
|
|
content = $(content);
|
|
stopExpand = that._triggerEvent(COLLAPSE, content.closest(ITEM));
|
|
if (!stopExpand) {
|
|
that._toggleGroup(content, true);
|
|
}
|
|
});
|
|
}
|
|
return stopExpand;
|
|
},
|
|
_ajaxRequest: function (element, contentElement, isVisible) {
|
|
var that = this, statusIcon = element.find('.k-panelbar-collapse, .k-panelbar-expand'), link = element.find(LINKSELECTOR), loadingIconTimeout = setTimeout(function () {
|
|
statusIcon.addClass('k-loading');
|
|
}, 100), data = {}, url = link.attr(HREF);
|
|
$.ajax({
|
|
type: 'GET',
|
|
cache: false,
|
|
url: url,
|
|
dataType: 'html',
|
|
data: data,
|
|
error: function (xhr, status) {
|
|
statusIcon.removeClass('k-loading');
|
|
if (that.trigger(ERROR, {
|
|
xhr: xhr,
|
|
status: status
|
|
})) {
|
|
this.complete();
|
|
}
|
|
},
|
|
complete: function () {
|
|
clearTimeout(loadingIconTimeout);
|
|
statusIcon.removeClass('k-loading');
|
|
},
|
|
success: function (data) {
|
|
function getElements() {
|
|
return { elements: contentElement.get() };
|
|
}
|
|
try {
|
|
that.angular('cleanup', getElements);
|
|
contentElement.html(data);
|
|
that.angular('compile', getElements);
|
|
} catch (e) {
|
|
var console = window.console;
|
|
if (console && console.error) {
|
|
console.error(e.name + ': ' + e.message + ' in ' + url);
|
|
}
|
|
this.error(this.xhr, 'error');
|
|
}
|
|
that._toggleGroup(contentElement, isVisible);
|
|
that.trigger(CONTENTLOAD, {
|
|
item: element[0],
|
|
contentElement: contentElement[0]
|
|
});
|
|
}
|
|
});
|
|
},
|
|
_triggerEvent: function (eventName, element) {
|
|
var that = this;
|
|
return that.trigger(eventName, { item: element[0] });
|
|
},
|
|
_updateSelected: function (link) {
|
|
var that = this, element = that.element, item = link.parent(ITEM), selected = that._selected;
|
|
if (selected) {
|
|
selected.removeAttr(ARIA_SELECTED);
|
|
}
|
|
that._selected = item.attr(ARIA_SELECTED, true);
|
|
element.find(selectableItems).removeClass(SELECTEDCLASS);
|
|
element.find('> .' + HIGHLIGHTCLASS + ', .k-panel > .' + HIGHLIGHTCLASS).removeClass(HIGHLIGHTCLASS);
|
|
link.addClass(SELECTEDCLASS);
|
|
link.parentsUntil(element, ITEM).filter(':has(.k-header)').addClass(HIGHLIGHTCLASS);
|
|
that._current(item[0] ? item : null);
|
|
},
|
|
_animations: function (options) {
|
|
if (options && 'animation' in options && !options.animation) {
|
|
options.animation = {
|
|
expand: { effects: {} },
|
|
collapse: {
|
|
hide: true,
|
|
effects: {}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
});
|
|
extend(PanelBar, {
|
|
renderItem: function (options) {
|
|
options = extend({
|
|
panelBar: {},
|
|
group: {}
|
|
}, options);
|
|
var empty = templates.empty, item = options.item;
|
|
return templates.item(extend(options, {
|
|
image: item.imageUrl ? templates.image : empty,
|
|
sprite: item.spriteCssClass ? templates.sprite : empty,
|
|
itemWrapper: templates.itemWrapper,
|
|
renderContent: PanelBar.renderContent,
|
|
arrow: item.items || item.content || item.contentUrl ? templates.arrow : empty,
|
|
subGroup: PanelBar.renderGroup
|
|
}, rendering));
|
|
},
|
|
renderGroup: function (options) {
|
|
return templates.group(extend({
|
|
renderItems: function (options) {
|
|
var html = '', i = 0, items = options.items, len = items ? items.length : 0, group = extend({ length: len }, options.group);
|
|
for (; i < len; i++) {
|
|
html += PanelBar.renderItem(extend(options, {
|
|
group: group,
|
|
item: extend({ index: i }, items[i])
|
|
}));
|
|
}
|
|
return html;
|
|
}
|
|
}, options, rendering));
|
|
},
|
|
renderContent: function (options) {
|
|
return templates.content(extend(options, rendering));
|
|
}
|
|
});
|
|
kendo.ui.plugin(PanelBar);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.responsivepanel', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'responsive-panel',
|
|
name: 'Responsive Panel',
|
|
category: 'web',
|
|
description: 'The Responsive Panel widget allows a panel of content to be hidden on mobile devices, available through a toggle button.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var proxy = $.proxy;
|
|
var NS = '.kendoResponsivePanel';
|
|
var OPEN = 'open';
|
|
var CLOSE = 'close';
|
|
var ACTIVATE_EVENTS = 'click' + NS + ' touchstart' + NS;
|
|
var Widget = kendo.ui.Widget;
|
|
var ResponsivePanel = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._guid = '_' + kendo.guid();
|
|
this._toggleHandler = proxy(this._toggleButtonClick, this);
|
|
this._closeHandler = proxy(this._close, this);
|
|
$(document.documentElement).on(ACTIVATE_EVENTS, this.options.toggleButton, this._toggleHandler);
|
|
this._registerBreakpoint();
|
|
this.element.addClass('k-rpanel k-rpanel-' + this.options.orientation + ' ' + this._guid);
|
|
this._resizeHandler = proxy(this.resize, this, true);
|
|
$(window).on('resize' + NS, this._resizeHandler);
|
|
},
|
|
_mediaQuery: '@media (max-width: #= breakpoint-1 #px) {' + '.#= guid #.k-rpanel-animate.k-rpanel-left,' + '.#= guid #.k-rpanel-animate.k-rpanel-right {' + '-webkit-transition: -webkit-transform .2s ease-out;' + '-ms-transition: -ms-transform .2s ease-out;' + 'transition: transform .2s ease-out;' + '} ' + '.#= guid #.k-rpanel-top {' + 'overflow: hidden;' + '}' + '.#= guid #.k-rpanel-animate.k-rpanel-top {' + '-webkit-transition: max-height .2s linear;' + '-ms-transition: max-height .2s linear;' + 'transition: max-height .2s linear;' + '}' + '} ' + '@media (min-width: #= breakpoint #px) {' + '#= toggleButton # { display: none; } ' + '.#= guid #.k-rpanel-left { float: left; } ' + '.#= guid #.k-rpanel-right { float: right; } ' + '.#= guid #.k-rpanel-left, .#= guid #.k-rpanel-right {' + 'position: relative;' + '-webkit-transform: translateX(0);' + '-ms-transform: translateX(0);' + 'transform: translateX(0);' + '-webkit-transform: translateX(0) translateZ(0);' + '-ms-transform: translateX(0) translateZ(0);' + 'transform: translateX(0) translateZ(0);' + '} ' + '.#= guid #.k-rpanel-top { max-height: none; }' + '}',
|
|
_registerBreakpoint: function () {
|
|
var options = this.options;
|
|
this._registerStyle(kendo.template(this._mediaQuery)({
|
|
breakpoint: options.breakpoint,
|
|
toggleButton: options.toggleButton,
|
|
guid: this._guid
|
|
}));
|
|
},
|
|
_registerStyle: function (cssText) {
|
|
var head = $('head,body')[0];
|
|
var style = document.createElement('style');
|
|
head.appendChild(style);
|
|
if (style.styleSheet) {
|
|
style.styleSheet.cssText = cssText;
|
|
} else {
|
|
style.appendChild(document.createTextNode(cssText));
|
|
}
|
|
},
|
|
options: {
|
|
name: 'ResponsivePanel',
|
|
orientation: 'left',
|
|
toggleButton: '.k-rpanel-toggle',
|
|
breakpoint: 640,
|
|
autoClose: true
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE
|
|
],
|
|
_resize: function () {
|
|
this.element.removeClass('k-rpanel-animate k-rpanel-expanded');
|
|
$(document.documentElement).off(ACTIVATE_EVENTS, this._closeHandler);
|
|
},
|
|
_toggleButtonClick: function (e) {
|
|
e.preventDefault();
|
|
if (this.element.hasClass('k-rpanel-expanded')) {
|
|
this.close();
|
|
} else {
|
|
this.open();
|
|
}
|
|
},
|
|
open: function () {
|
|
if (!this.trigger(OPEN)) {
|
|
this.element.addClass('k-rpanel-animate k-rpanel-expanded');
|
|
if (this.options.autoClose) {
|
|
$(document.documentElement).on(ACTIVATE_EVENTS, this._closeHandler);
|
|
}
|
|
}
|
|
},
|
|
close: function () {
|
|
if (!this.trigger(CLOSE)) {
|
|
this.element.addClass('k-rpanel-animate').removeClass('k-rpanel-expanded');
|
|
$(document.documentElement).off(ACTIVATE_EVENTS, this._closeHandler);
|
|
}
|
|
},
|
|
_close: function (e) {
|
|
var prevented = e.isDefaultPrevented();
|
|
var container = $(e.target).closest(this.options.toggleButton + ',.k-rpanel');
|
|
if (!container.length && !prevented) {
|
|
this.close();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
$(window).off('resize' + NS, this._resizeHandler);
|
|
$(document.documentElement).off(ACTIVATE_EVENTS, this._closeHandler);
|
|
}
|
|
});
|
|
kendo.ui.plugin(ResponsivePanel);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.tabstrip', ['kendo.data'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'tabstrip',
|
|
name: 'TabStrip',
|
|
category: 'web',
|
|
description: 'The TabStrip widget displays a collection of tabs with associated tab content.',
|
|
depends: ['data']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, map = $.map, each = $.each, trim = $.trim, extend = $.extend, template = kendo.template, Widget = ui.Widget, excludedNodesRegExp = /^(a|div)$/i, NS = '.kendoTabStrip', IMG = 'img', HREF = 'href', PREV = 'prev', SHOW = 'show', LINK = 'k-link', LAST = 'k-last', CLICK = 'click', ERROR = 'error', EMPTY = ':empty', IMAGE = 'k-image', FIRST = 'k-first', SELECT = 'select', ACTIVATE = 'activate', CONTENT = 'k-content', CONTENTURL = 'contentUrl', MOUSEENTER = 'mouseenter', MOUSELEAVE = 'mouseleave', CONTENTLOAD = 'contentLoad', DISABLEDSTATE = 'k-state-disabled', DEFAULTSTATE = 'k-state-default', ACTIVESTATE = 'k-state-active', FOCUSEDSTATE = 'k-state-focused', HOVERSTATE = 'k-state-hover', TABONTOP = 'k-tab-on-top', NAVIGATABLEITEMS = '.k-item:not(.' + DISABLEDSTATE + ')', HOVERABLEITEMS = '.k-tabstrip-items > ' + NAVIGATABLEITEMS + ':not(.' + ACTIVESTATE + ')', templates = {
|
|
content: template('<div class=\'k-content\'#= contentAttributes(data) # role=\'tabpanel\'>#= content(item) #</div>'),
|
|
itemWrapper: template('<#= tag(item) # class=\'k-link\'#= contentUrl(item) ##= textAttributes(item) #>' + '#= image(item) ##= sprite(item) ##= text(item) #' + '</#= tag(item) #>'),
|
|
item: template('<li class=\'#= wrapperCssClass(group, item) #\' role=\'tab\' #=item.active ? "aria-selected=\'true\'" : \'\'#>' + '#= itemWrapper(data) #' + '</li>'),
|
|
image: template('<img class=\'k-image\' alt=\'\' src=\'#= imageUrl #\' />'),
|
|
sprite: template('<span class=\'k-sprite #= spriteCssClass #\'></span>'),
|
|
empty: template('')
|
|
}, rendering = {
|
|
wrapperCssClass: function (group, item) {
|
|
var result = 'k-item', index = item.index;
|
|
if (item.enabled === false) {
|
|
result += ' k-state-disabled';
|
|
} else {
|
|
result += ' k-state-default';
|
|
}
|
|
if (index === 0) {
|
|
result += ' k-first';
|
|
}
|
|
if (index == group.length - 1) {
|
|
result += ' k-last';
|
|
}
|
|
return result;
|
|
},
|
|
textAttributes: function (item) {
|
|
return item.url ? ' href=\'' + item.url + '\'' : '';
|
|
},
|
|
text: function (item) {
|
|
return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
|
|
},
|
|
tag: function (item) {
|
|
return item.url ? 'a' : 'span';
|
|
},
|
|
contentAttributes: function (content) {
|
|
return content.active !== true ? ' style=\'display:none\' aria-hidden=\'true\' aria-expanded=\'false\'' : '';
|
|
},
|
|
content: function (item) {
|
|
return item.content ? item.content : item.contentUrl ? '' : ' ';
|
|
},
|
|
contentUrl: function (item) {
|
|
return item.contentUrl ? kendo.attr('content-url') + '="' + item.contentUrl + '"' : '';
|
|
}
|
|
};
|
|
function updateTabClasses(tabs) {
|
|
tabs.children(IMG).addClass(IMAGE);
|
|
tabs.children('a').addClass(LINK).children(IMG).addClass(IMAGE);
|
|
tabs.filter(':not([disabled]):not([class*=k-state-disabled])').addClass(DEFAULTSTATE);
|
|
tabs.filter('li[disabled]').addClass(DISABLEDSTATE).removeAttr('disabled');
|
|
tabs.filter(':not([class*=k-state])').children('a').filter(':focus').parent().addClass(ACTIVESTATE + ' ' + TABONTOP);
|
|
tabs.attr('role', 'tab');
|
|
tabs.filter('.' + ACTIVESTATE).attr('aria-selected', true);
|
|
tabs.each(function () {
|
|
var item = $(this);
|
|
if (!item.children('.' + LINK).length) {
|
|
item.contents().filter(function () {
|
|
return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !trim(this.nodeValue));
|
|
}).wrapAll('<span class=\'' + LINK + '\'/>');
|
|
}
|
|
});
|
|
}
|
|
function updateFirstLast(tabGroup) {
|
|
var tabs = tabGroup.children('.k-item');
|
|
tabs.filter('.k-first:not(:first-child)').removeClass(FIRST);
|
|
tabs.filter('.k-last:not(:last-child)').removeClass(LAST);
|
|
tabs.filter(':first-child').addClass(FIRST);
|
|
tabs.filter(':last-child').addClass(LAST);
|
|
}
|
|
function scrollButtonHtml(buttonClass, iconClass) {
|
|
return '<span class=\'k-button k-button-icon k-button-bare k-tabstrip-' + buttonClass + '\' unselectable=\'on\'><span class=\'k-icon ' + iconClass + '\'></span></span>';
|
|
}
|
|
var TabStrip = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, value;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._animations(that.options);
|
|
options = that.options;
|
|
that._wrapper();
|
|
that._isRtl = kendo.support.isRtl(that.wrapper);
|
|
that._tabindex();
|
|
that._updateClasses();
|
|
that._dataSource();
|
|
if (options.dataSource) {
|
|
that.dataSource.fetch();
|
|
}
|
|
that._tabPosition();
|
|
that._scrollable();
|
|
if (that.options.contentUrls) {
|
|
that.wrapper.find('.k-tabstrip-items > .k-item').each(function (index, item) {
|
|
$(item).find('>.' + LINK).data(CONTENTURL, that.options.contentUrls[index]);
|
|
});
|
|
}
|
|
that.wrapper.on(MOUSEENTER + NS + ' ' + MOUSELEAVE + NS, HOVERABLEITEMS, that._toggleHover).on('focus' + NS, $.proxy(that._active, that)).on('blur' + NS, function () {
|
|
that._current(null);
|
|
});
|
|
that._keyDownProxy = $.proxy(that._keydown, that);
|
|
if (options.navigatable) {
|
|
that.wrapper.on('keydown' + NS, that._keyDownProxy);
|
|
}
|
|
if (that.options.value) {
|
|
value = that.options.value;
|
|
}
|
|
that.wrapper.children('.k-tabstrip-items').on(CLICK + NS, '.k-state-disabled .k-link', false).on(CLICK + NS, ' > ' + NAVIGATABLEITEMS, function (e) {
|
|
var wr = that.wrapper[0];
|
|
if (wr !== document.activeElement) {
|
|
var msie = kendo.support.browser.msie;
|
|
if (msie) {
|
|
try {
|
|
wr.setActive();
|
|
} catch (j) {
|
|
wr.focus();
|
|
}
|
|
} else {
|
|
wr.focus();
|
|
}
|
|
}
|
|
if (that._click($(e.currentTarget))) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
var selectedItems = that.tabGroup.children('li.' + ACTIVESTATE), content = that.contentHolder(selectedItems.index());
|
|
if (selectedItems[0] && content.length > 0 && content[0].childNodes.length === 0) {
|
|
that.activateTab(selectedItems.eq(0));
|
|
}
|
|
that.element.attr('role', 'tablist');
|
|
if (that.element[0].id) {
|
|
that._ariaId = that.element[0].id + '_ts_active';
|
|
}
|
|
that.value(value);
|
|
kendo.notify(that);
|
|
},
|
|
_active: function () {
|
|
var item = this.tabGroup.children().filter('.' + ACTIVESTATE);
|
|
item = item[0] ? item : this._endItem('first');
|
|
if (item[0]) {
|
|
this._current(item);
|
|
}
|
|
},
|
|
_endItem: function (action) {
|
|
return this.tabGroup.children(NAVIGATABLEITEMS)[action]();
|
|
},
|
|
_item: function (item, action) {
|
|
var endItem;
|
|
if (action === PREV) {
|
|
endItem = 'last';
|
|
} else {
|
|
endItem = 'first';
|
|
}
|
|
if (!item) {
|
|
return this._endItem(endItem);
|
|
}
|
|
item = item[action]();
|
|
if (!item[0]) {
|
|
item = this._endItem(endItem);
|
|
}
|
|
if (item.hasClass(DISABLEDSTATE)) {
|
|
item = this._item(item, action);
|
|
}
|
|
return item;
|
|
},
|
|
_current: function (candidate) {
|
|
var that = this, focused = that._focused, id = that._ariaId;
|
|
if (candidate === undefined) {
|
|
return focused;
|
|
}
|
|
if (focused) {
|
|
if (focused[0].id === id) {
|
|
focused.removeAttr('id');
|
|
}
|
|
focused.removeClass(FOCUSEDSTATE);
|
|
}
|
|
if (candidate) {
|
|
if (!candidate.hasClass(ACTIVESTATE)) {
|
|
candidate.addClass(FOCUSEDSTATE);
|
|
}
|
|
that.element.removeAttr('aria-activedescendant');
|
|
id = candidate[0].id || id;
|
|
if (id) {
|
|
candidate.attr('id', id);
|
|
that.element.attr('aria-activedescendant', id);
|
|
}
|
|
}
|
|
that._focused = candidate;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, current = that._current(), rtl = that._isRtl, action;
|
|
if (e.target != e.currentTarget) {
|
|
return;
|
|
}
|
|
if (key == keys.DOWN || key == keys.RIGHT) {
|
|
action = rtl ? PREV : 'next';
|
|
} else if (key == keys.UP || key == keys.LEFT) {
|
|
action = rtl ? 'next' : PREV;
|
|
} else if (key == keys.ENTER || key == keys.SPACEBAR) {
|
|
that._click(current);
|
|
e.preventDefault();
|
|
} else if (key == keys.HOME) {
|
|
that._click(that._endItem('first'));
|
|
e.preventDefault();
|
|
return;
|
|
} else if (key == keys.END) {
|
|
that._click(that._endItem('last'));
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (action) {
|
|
that._click(that._item(current, action));
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_dataSource: function () {
|
|
var that = this;
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind('change', that._refreshHandler);
|
|
} else {
|
|
that._refreshHandler = $.proxy(that.refresh, that);
|
|
}
|
|
that.dataSource = kendo.data.DataSource.create(that.options.dataSource).bind('change', that._refreshHandler);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
that.options.dataSource = dataSource;
|
|
that._dataSource();
|
|
that.dataSource.fetch();
|
|
},
|
|
_animations: function (options) {
|
|
if (options && 'animation' in options && !options.animation) {
|
|
options.animation = {
|
|
open: { effects: {} },
|
|
close: { effects: {} }
|
|
};
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var that = this, options = that.options, text = kendo.getter(options.dataTextField), content = kendo.getter(options.dataContentField), contentUrl = kendo.getter(options.dataContentUrlField), image = kendo.getter(options.dataImageUrlField), url = kendo.getter(options.dataUrlField), sprite = kendo.getter(options.dataSpriteCssClass), idx, tabs = [], tab, action, view = that.dataSource.view(), length;
|
|
e = e || {};
|
|
action = e.action;
|
|
if (action) {
|
|
view = e.items;
|
|
}
|
|
for (idx = 0, length = view.length; idx < length; idx++) {
|
|
tab = { text: text(view[idx]) };
|
|
if (options.dataContentField) {
|
|
tab.content = content(view[idx]);
|
|
}
|
|
if (options.dataContentUrlField) {
|
|
tab.contentUrl = contentUrl(view[idx]);
|
|
}
|
|
if (options.dataUrlField) {
|
|
tab.url = url(view[idx]);
|
|
}
|
|
if (options.dataImageUrlField) {
|
|
tab.imageUrl = image(view[idx]);
|
|
}
|
|
if (options.dataSpriteCssClass) {
|
|
tab.spriteCssClass = sprite(view[idx]);
|
|
}
|
|
tabs[idx] = tab;
|
|
}
|
|
if (e.action == 'add') {
|
|
if (e.index < that.tabGroup.children().length) {
|
|
that.insertBefore(tabs, that.tabGroup.children().eq(e.index));
|
|
} else {
|
|
that.append(tabs);
|
|
}
|
|
} else if (e.action == 'remove') {
|
|
for (idx = 0; idx < view.length; idx++) {
|
|
that.remove(e.index);
|
|
}
|
|
} else if (e.action == 'itemchange') {
|
|
idx = that.dataSource.view().indexOf(view[0]);
|
|
if (e.field === options.dataTextField) {
|
|
that.tabGroup.children().eq(idx).find('.k-link').text(view[0].get(e.field));
|
|
}
|
|
} else {
|
|
that.trigger('dataBinding');
|
|
that.remove('li');
|
|
that.append(tabs);
|
|
that.trigger('dataBound');
|
|
}
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value !== undefined) {
|
|
if (value != that.value()) {
|
|
that.tabGroup.children().each(function () {
|
|
if ($.trim($(this).text()) == value) {
|
|
that.select(this);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
return that.select().text();
|
|
}
|
|
},
|
|
items: function () {
|
|
return this.tabGroup[0].children;
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this, animation = that.options.animation;
|
|
that._animations(options);
|
|
options.animation = extend(true, animation, options.animation);
|
|
if (options.navigatable) {
|
|
that.wrapper.on('keydown' + NS, that._keyDownProxy);
|
|
} else {
|
|
that.wrapper.off('keydown' + NS, that._keyDownProxy);
|
|
}
|
|
Widget.fn.setOptions.call(that, options);
|
|
},
|
|
events: [
|
|
SELECT,
|
|
ACTIVATE,
|
|
SHOW,
|
|
ERROR,
|
|
CONTENTLOAD,
|
|
'change',
|
|
'dataBinding',
|
|
'dataBound'
|
|
],
|
|
options: {
|
|
name: 'TabStrip',
|
|
dataTextField: '',
|
|
dataContentField: '',
|
|
dataImageUrlField: '',
|
|
dataUrlField: '',
|
|
dataSpriteCssClass: '',
|
|
dataContentUrlField: '',
|
|
tabPosition: 'top',
|
|
animation: {
|
|
open: {
|
|
effects: 'expand:vertical fadeIn',
|
|
duration: 200
|
|
},
|
|
close: { duration: 200 }
|
|
},
|
|
collapsible: false,
|
|
navigatable: true,
|
|
contentUrls: false,
|
|
scrollable: { distance: 200 }
|
|
},
|
|
destroy: function () {
|
|
var that = this, scrollWrap = that.scrollWrap;
|
|
Widget.fn.destroy.call(that);
|
|
if (that._refreshHandler) {
|
|
that.dataSource.unbind('change', that._refreshHandler);
|
|
}
|
|
that.wrapper.off(NS);
|
|
that.wrapper.children('.k-tabstrip-items').off(NS);
|
|
if (that._scrollableModeActive) {
|
|
that._scrollPrevButton.off().remove();
|
|
that._scrollNextButton.off().remove();
|
|
}
|
|
kendo.destroy(that.wrapper);
|
|
scrollWrap.children('.k-tabstrip').unwrap();
|
|
},
|
|
select: function (element) {
|
|
var that = this;
|
|
if (arguments.length === 0) {
|
|
return that.tabGroup.children('li.' + ACTIVESTATE);
|
|
}
|
|
if (!isNaN(element)) {
|
|
element = that.tabGroup.children().get(element);
|
|
}
|
|
element = that.tabGroup.find(element);
|
|
$(element).each(function (index, item) {
|
|
item = $(item);
|
|
if (!item.hasClass(ACTIVESTATE) && !that.trigger(SELECT, {
|
|
item: item[0],
|
|
contentElement: that.contentHolder(item.index())[0]
|
|
})) {
|
|
that.activateTab(item);
|
|
}
|
|
});
|
|
return that;
|
|
},
|
|
enable: function (element, state) {
|
|
this._toggleDisabled(element, state !== false);
|
|
return this;
|
|
},
|
|
disable: function (element) {
|
|
this._toggleDisabled(element, false);
|
|
return this;
|
|
},
|
|
reload: function (element) {
|
|
element = this.tabGroup.find(element);
|
|
var that = this;
|
|
element.each(function () {
|
|
var item = $(this), contentUrl = item.find('.' + LINK).data(CONTENTURL), content = that.contentHolder(item.index());
|
|
if (contentUrl) {
|
|
that.ajaxRequest(item, content, null, contentUrl);
|
|
}
|
|
});
|
|
return that;
|
|
},
|
|
append: function (tab) {
|
|
var that = this, inserted = that._create(tab);
|
|
each(inserted.tabs, function (idx) {
|
|
var contents = inserted.contents[idx];
|
|
that.tabGroup.append(this);
|
|
if (that.options.tabPosition == 'bottom') {
|
|
that.tabGroup.before(contents);
|
|
} else if (that._scrollableModeActive) {
|
|
that._scrollPrevButton.before(contents);
|
|
} else {
|
|
that.wrapper.append(contents);
|
|
}
|
|
that.angular('compile', function () {
|
|
return { elements: [contents] };
|
|
});
|
|
});
|
|
updateFirstLast(that.tabGroup);
|
|
that._updateContentElements();
|
|
that.resize(true);
|
|
return that;
|
|
},
|
|
insertBefore: function (tab, referenceTab) {
|
|
referenceTab = this.tabGroup.find(referenceTab);
|
|
var that = this, inserted = that._create(tab), referenceContent = $(that.contentElement(referenceTab.index()));
|
|
each(inserted.tabs, function (idx) {
|
|
var contents = inserted.contents[idx];
|
|
referenceTab.before(this);
|
|
referenceContent.before(contents);
|
|
that.angular('compile', function () {
|
|
return { elements: [contents] };
|
|
});
|
|
});
|
|
updateFirstLast(that.tabGroup);
|
|
that._updateContentElements();
|
|
that.resize(true);
|
|
return that;
|
|
},
|
|
insertAfter: function (tab, referenceTab) {
|
|
referenceTab = this.tabGroup.find(referenceTab);
|
|
var that = this, inserted = that._create(tab), referenceContent = $(that.contentElement(referenceTab.index()));
|
|
each(inserted.tabs, function (idx) {
|
|
var contents = inserted.contents[idx];
|
|
referenceTab.after(this);
|
|
referenceContent.after(contents);
|
|
that.angular('compile', function () {
|
|
return { elements: [contents] };
|
|
});
|
|
});
|
|
updateFirstLast(that.tabGroup);
|
|
that._updateContentElements();
|
|
that.resize(true);
|
|
return that;
|
|
},
|
|
remove: function (elements) {
|
|
var that = this;
|
|
var type = typeof elements;
|
|
var contents;
|
|
if (type === 'string') {
|
|
elements = that.tabGroup.find(elements);
|
|
} else if (type === 'number') {
|
|
elements = that.tabGroup.children().eq(elements);
|
|
}
|
|
contents = elements.map(function () {
|
|
var content = that.contentElement($(this).index());
|
|
kendo.destroy(content);
|
|
return content;
|
|
});
|
|
elements.remove();
|
|
contents.remove();
|
|
that._updateContentElements();
|
|
that.resize(true);
|
|
return that;
|
|
},
|
|
_create: function (tab) {
|
|
var plain = $.isPlainObject(tab), that = this, tabs, contents, content;
|
|
if (plain || $.isArray(tab)) {
|
|
tab = $.isArray(tab) ? tab : [tab];
|
|
tabs = map(tab, function (value, idx) {
|
|
return $(TabStrip.renderItem({
|
|
group: that.tabGroup,
|
|
item: extend(value, { index: idx })
|
|
}));
|
|
});
|
|
contents = map(tab, function (value, idx) {
|
|
if (typeof value.content == 'string' || value.contentUrl) {
|
|
return $(TabStrip.renderContent({ item: extend(value, { index: idx }) }));
|
|
}
|
|
});
|
|
} else {
|
|
if (typeof tab == 'string' && tab[0] != '<') {
|
|
tabs = that.element.find(tab);
|
|
} else {
|
|
tabs = $(tab);
|
|
}
|
|
contents = $();
|
|
tabs.each(function () {
|
|
content = $('<div class=\'' + CONTENT + '\'/>');
|
|
if (/k-tabstrip-items/.test(this.parentNode.className)) {
|
|
var index = parseInt(this.getAttribute('aria-controls').replace(/^.*-/, ''), 10) - 1;
|
|
content = $(that.contentElement(index));
|
|
}
|
|
contents = contents.add(content);
|
|
});
|
|
updateTabClasses(tabs);
|
|
}
|
|
return {
|
|
tabs: tabs,
|
|
contents: contents
|
|
};
|
|
},
|
|
_toggleDisabled: function (element, enable) {
|
|
element = this.tabGroup.find(element);
|
|
element.each(function () {
|
|
$(this).toggleClass(DEFAULTSTATE, enable).toggleClass(DISABLEDSTATE, !enable);
|
|
});
|
|
},
|
|
_updateClasses: function () {
|
|
var that = this, tabs, activeItem, activeTab;
|
|
that.wrapper.addClass('k-widget k-header k-tabstrip');
|
|
that.tabGroup = that.wrapper.children('ul').addClass('k-tabstrip-items k-reset');
|
|
if (!that.tabGroup[0]) {
|
|
that.tabGroup = $('<ul class=\'k-tabstrip-items k-reset\'/>').appendTo(that.wrapper);
|
|
}
|
|
tabs = that.tabGroup.find('li').addClass('k-item');
|
|
if (tabs.length) {
|
|
activeItem = tabs.filter('.' + ACTIVESTATE).index();
|
|
activeTab = activeItem >= 0 ? activeItem : undefined;
|
|
that.tabGroup.contents().filter(function () {
|
|
return this.nodeType == 3 && !trim(this.nodeValue);
|
|
}).remove();
|
|
}
|
|
if (activeItem >= 0) {
|
|
tabs.eq(activeItem).addClass(TABONTOP);
|
|
}
|
|
that.contentElements = that.wrapper.children('div');
|
|
that.contentElements.addClass(CONTENT).eq(activeTab).addClass(ACTIVESTATE).css({ display: 'block' });
|
|
if (tabs.length) {
|
|
updateTabClasses(tabs);
|
|
updateFirstLast(that.tabGroup);
|
|
that._updateContentElements();
|
|
}
|
|
},
|
|
_updateContentElements: function () {
|
|
var that = this, contentUrls = that.options.contentUrls || [], items = that.tabGroup.find('.k-item'), tabStripID = (that.element.attr('id') || kendo.guid()) + '-', contentElements = that.wrapper.children('div');
|
|
if (contentElements.length && items.length > contentElements.length) {
|
|
contentElements.each(function (idx) {
|
|
var currentIndex = parseInt(this.id.replace(tabStripID, ''), 10), item = items.filter('[aria-controls=' + tabStripID + currentIndex + ']'), id = tabStripID + (idx + 1);
|
|
item.data('aria', id);
|
|
this.setAttribute('id', id);
|
|
});
|
|
items.each(function () {
|
|
var item = $(this);
|
|
this.setAttribute('aria-controls', item.data('aria'));
|
|
item.removeData('aria');
|
|
});
|
|
} else {
|
|
items.each(function (idx) {
|
|
var currentContent = contentElements.eq(idx), id = tabStripID + (idx + 1);
|
|
this.setAttribute('aria-controls', id);
|
|
if (!currentContent.length && contentUrls[idx]) {
|
|
$('<div class=\'' + CONTENT + '\'/>').appendTo(that.wrapper).attr('id', id);
|
|
} else {
|
|
currentContent.attr('id', id);
|
|
if (!$(this).children('.k-loading')[0] && !contentUrls[idx]) {
|
|
$('<span class=\'k-loading k-complete\'/>').prependTo(this);
|
|
}
|
|
}
|
|
currentContent.attr('role', 'tabpanel');
|
|
currentContent.filter(':not(.' + ACTIVESTATE + ')').attr('aria-hidden', true).attr('aria-expanded', false);
|
|
currentContent.filter('.' + ACTIVESTATE).attr('aria-expanded', true);
|
|
});
|
|
}
|
|
that.contentElements = that.contentAnimators = that.wrapper.children('div');
|
|
that.tabsHeight = that.tabGroup.outerHeight() + parseInt(that.wrapper.css('border-top-width'), 10) + parseInt(that.wrapper.css('border-bottom-width'), 10);
|
|
if (kendo.kineticScrollNeeded && kendo.mobile.ui.Scroller) {
|
|
kendo.touchScroller(that.contentElements);
|
|
that.contentElements = that.contentElements.children('.km-scroll-container');
|
|
}
|
|
},
|
|
_wrapper: function () {
|
|
var that = this;
|
|
if (that.element.is('ul')) {
|
|
that.wrapper = that.element.wrapAll('<div />').parent();
|
|
} else {
|
|
that.wrapper = that.element;
|
|
}
|
|
that.scrollWrap = that.wrapper.parent('.k-tabstrip-wrapper');
|
|
if (!that.scrollWrap[0]) {
|
|
that.scrollWrap = that.wrapper.wrapAll('<div class=\'k-tabstrip-wrapper\' />').parent();
|
|
}
|
|
},
|
|
_tabPosition: function () {
|
|
var that = this, tabPosition = that.options.tabPosition;
|
|
that.wrapper.addClass('k-floatwrap k-tabstrip-' + tabPosition);
|
|
if (tabPosition == 'bottom') {
|
|
that.tabGroup.appendTo(that.wrapper);
|
|
}
|
|
that.resize(true);
|
|
},
|
|
_setContentElementsDimensions: function () {
|
|
var that = this, tabPosition = that.options.tabPosition;
|
|
if (tabPosition == 'left' || tabPosition == 'right') {
|
|
var contentDivs = that.wrapper.children('.k-content'), activeDiv = contentDivs.filter(':visible'), marginStyleProperty = 'margin-' + tabPosition, tabGroup = that.tabGroup, margin = tabGroup.outerWidth();
|
|
var minHeight = Math.ceil(tabGroup.height()) - parseInt(activeDiv.css('padding-top'), 10) - parseInt(activeDiv.css('padding-bottom'), 10) - parseInt(activeDiv.css('border-top-width'), 10) - parseInt(activeDiv.css('border-bottom-width'), 10);
|
|
setTimeout(function () {
|
|
contentDivs.css(marginStyleProperty, margin).css('min-height', minHeight);
|
|
});
|
|
}
|
|
},
|
|
_resize: function () {
|
|
this._setContentElementsDimensions();
|
|
this._scrollable();
|
|
},
|
|
_sizeScrollWrap: function (element) {
|
|
if (element.is(':visible')) {
|
|
var tabPosition = this.options.tabPosition;
|
|
var h = Math.floor(element.outerHeight(true)) + (tabPosition === 'left' || tabPosition === 'right' ? 2 : this.tabsHeight);
|
|
this.scrollWrap.css('height', h).css('height');
|
|
}
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVERSTATE, e.type == MOUSEENTER);
|
|
},
|
|
_click: function (item) {
|
|
var that = this, link = item.find('.' + LINK), href = link.attr(HREF), collapse = that.options.collapsible, contentHolder = that.contentHolder(item.index()), prevent, isAnchor;
|
|
if (item.closest('.k-widget')[0] != that.wrapper[0]) {
|
|
return;
|
|
}
|
|
if (item.is('.' + DISABLEDSTATE + (!collapse ? ',.' + ACTIVESTATE : ''))) {
|
|
return true;
|
|
}
|
|
isAnchor = link.data(CONTENTURL) || href && (href.charAt(href.length - 1) == '#' || href.indexOf('#' + that.element[0].id + '-') != -1);
|
|
prevent = !href || isAnchor;
|
|
if (that.tabGroup.children('[data-animating]').length) {
|
|
return prevent;
|
|
}
|
|
if (that.trigger(SELECT, {
|
|
item: item[0],
|
|
contentElement: contentHolder[0]
|
|
})) {
|
|
return true;
|
|
}
|
|
if (prevent === false) {
|
|
return;
|
|
}
|
|
if (collapse && item.is('.' + ACTIVESTATE)) {
|
|
that.deactivateTab(item);
|
|
return true;
|
|
}
|
|
if (that.activateTab(item)) {
|
|
prevent = true;
|
|
}
|
|
return prevent;
|
|
},
|
|
_scrollable: function () {
|
|
var that = this, options = that.options, wrapperOffsetWidth, tabGroupScrollWidth, scrollPrevButton, scrollNextButton;
|
|
if (that._scrollableAllowed()) {
|
|
that.wrapper.addClass('k-tabstrip-scrollable');
|
|
wrapperOffsetWidth = that.wrapper[0].offsetWidth;
|
|
tabGroupScrollWidth = that.tabGroup[0].scrollWidth;
|
|
if (tabGroupScrollWidth > wrapperOffsetWidth && !that._scrollableModeActive) {
|
|
that._nowScrollingTabs = false;
|
|
that._isRtl = kendo.support.isRtl(that.element);
|
|
that.wrapper.append(scrollButtonHtml('prev', 'k-i-arrow-w') + scrollButtonHtml('next', 'k-i-arrow-e'));
|
|
scrollPrevButton = that._scrollPrevButton = that.wrapper.children('.k-tabstrip-prev');
|
|
scrollNextButton = that._scrollNextButton = that.wrapper.children('.k-tabstrip-next');
|
|
that.tabGroup.css({
|
|
marginLeft: scrollPrevButton.outerWidth() + 9,
|
|
marginRight: scrollNextButton.outerWidth() + 12
|
|
});
|
|
scrollPrevButton.on('mousedown' + NS, function () {
|
|
that._nowScrollingTabs = true;
|
|
that._scrollTabsByDelta(options.scrollable.distance * (that._isRtl ? 1 : -1));
|
|
});
|
|
scrollNextButton.on('mousedown' + NS, function () {
|
|
that._nowScrollingTabs = true;
|
|
that._scrollTabsByDelta(options.scrollable.distance * (that._isRtl ? -1 : 1));
|
|
});
|
|
scrollPrevButton.add(scrollNextButton).on('mouseup' + NS, function () {
|
|
that._nowScrollingTabs = false;
|
|
});
|
|
that._scrollableModeActive = true;
|
|
that._toggleScrollButtons();
|
|
} else if (that._scrollableModeActive && tabGroupScrollWidth <= wrapperOffsetWidth) {
|
|
that._scrollableModeActive = false;
|
|
that.wrapper.removeClass('k-tabstrip-scrollable');
|
|
that._scrollPrevButton.off().remove();
|
|
that._scrollNextButton.off().remove();
|
|
that.tabGroup.css({
|
|
marginLeft: '',
|
|
marginRight: ''
|
|
});
|
|
} else if (!that._scrollableModeActive) {
|
|
that.wrapper.removeClass('k-tabstrip-scrollable');
|
|
} else {
|
|
that._toggleScrollButtons();
|
|
}
|
|
}
|
|
},
|
|
_scrollableAllowed: function () {
|
|
var options = this.options;
|
|
return options.scrollable && !isNaN(options.scrollable.distance) && (options.tabPosition == 'top' || options.tabPosition == 'bottom');
|
|
},
|
|
_scrollTabsToItem: function (item) {
|
|
var that = this, tabGroup = that.tabGroup, currentScrollOffset = tabGroup.scrollLeft(), itemWidth = item.outerWidth(), itemOffset = that._isRtl ? item.position().left : item.position().left - tabGroup.children().first().position().left, tabGroupWidth = tabGroup[0].offsetWidth, tabGroupPadding = Math.ceil(parseFloat(tabGroup.css('padding-left'))), itemPosition;
|
|
if (that._isRtl) {
|
|
if (itemOffset < 0) {
|
|
itemPosition = currentScrollOffset + itemOffset - (tabGroupWidth - currentScrollOffset) - tabGroupPadding;
|
|
} else if (itemOffset + itemWidth > tabGroupWidth) {
|
|
itemPosition = currentScrollOffset + itemOffset - itemWidth + tabGroupPadding * 2;
|
|
}
|
|
} else {
|
|
if (currentScrollOffset + tabGroupWidth < itemOffset + itemWidth) {
|
|
itemPosition = itemOffset + itemWidth - tabGroupWidth + tabGroupPadding * 2;
|
|
} else if (currentScrollOffset > itemOffset) {
|
|
itemPosition = itemOffset - tabGroupPadding;
|
|
}
|
|
}
|
|
tabGroup.finish().animate({ 'scrollLeft': itemPosition }, 'fast', 'linear', function () {
|
|
that._toggleScrollButtons();
|
|
});
|
|
},
|
|
_scrollTabsByDelta: function (delta) {
|
|
var that = this;
|
|
var tabGroup = that.tabGroup;
|
|
var scrLeft = tabGroup.scrollLeft();
|
|
tabGroup.finish().animate({ 'scrollLeft': scrLeft + delta }, 'fast', 'linear', function () {
|
|
if (that._nowScrollingTabs) {
|
|
that._scrollTabsByDelta(delta);
|
|
} else {
|
|
that._toggleScrollButtons();
|
|
}
|
|
});
|
|
},
|
|
_toggleScrollButtons: function () {
|
|
var that = this, ul = that.tabGroup, scrollLeft = ul.scrollLeft();
|
|
that._scrollPrevButton.toggle(that._isRtl ? scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1 : scrollLeft !== 0);
|
|
that._scrollNextButton.toggle(that._isRtl ? scrollLeft !== 0 : scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1);
|
|
},
|
|
deactivateTab: function (item) {
|
|
var that = this, animationSettings = that.options.animation, animation = animationSettings.open, close = extend({}, animationSettings.close), hasCloseAnimation = close && 'effects' in close;
|
|
item = that.tabGroup.find(item);
|
|
close = extend(hasCloseAnimation ? close : extend({ reverse: true }, animation), { hide: true });
|
|
if (kendo.size(animation.effects)) {
|
|
item.kendoAddClass(DEFAULTSTATE, { duration: animation.duration });
|
|
item.kendoRemoveClass(ACTIVESTATE, { duration: animation.duration });
|
|
} else {
|
|
item.addClass(DEFAULTSTATE);
|
|
item.removeClass(ACTIVESTATE);
|
|
}
|
|
item.removeAttr('aria-selected');
|
|
that.contentAnimators.filter('.' + ACTIVESTATE).kendoStop(true, true).kendoAnimate(close).removeClass(ACTIVESTATE).attr('aria-hidden', true);
|
|
},
|
|
activateTab: function (item) {
|
|
if (this.tabGroup.children('[data-animating]').length) {
|
|
return;
|
|
}
|
|
item = this.tabGroup.find(item);
|
|
var that = this, animationSettings = that.options.animation, animation = animationSettings.open, close = extend({}, animationSettings.close), hasCloseAnimation = close && 'effects' in close, neighbours = item.parent().children(), oldTab = neighbours.filter('.' + ACTIVESTATE), itemIndex = neighbours.index(item);
|
|
close = extend(hasCloseAnimation ? close : extend({ reverse: true }, animation), { hide: true });
|
|
if (kendo.size(animation.effects)) {
|
|
oldTab.kendoRemoveClass(ACTIVESTATE, { duration: close.duration });
|
|
item.kendoRemoveClass(HOVERSTATE, { duration: close.duration });
|
|
} else {
|
|
oldTab.removeClass(ACTIVESTATE);
|
|
item.removeClass(HOVERSTATE);
|
|
}
|
|
var contentAnimators = that.contentAnimators;
|
|
if (that.inRequest) {
|
|
that.xhr.abort();
|
|
that.inRequest = false;
|
|
}
|
|
if (contentAnimators.length === 0) {
|
|
that.tabGroup.find('.' + TABONTOP).removeClass(TABONTOP);
|
|
item.addClass(TABONTOP).css('z-index');
|
|
item.addClass(ACTIVESTATE);
|
|
that._current(item);
|
|
that.trigger('change');
|
|
if (that._scrollableModeActive) {
|
|
that._scrollTabsToItem(item);
|
|
}
|
|
return false;
|
|
}
|
|
var visibleContents = contentAnimators.filter('.' + ACTIVESTATE), contentHolder = that.contentHolder(itemIndex), contentElement = contentHolder.closest('.k-content');
|
|
that.tabsHeight = that.tabGroup.outerHeight() + parseInt(that.wrapper.css('border-top-width'), 10) + parseInt(that.wrapper.css('border-bottom-width'), 10);
|
|
that._sizeScrollWrap(visibleContents);
|
|
if (contentHolder.length === 0) {
|
|
visibleContents.removeClass(ACTIVESTATE).attr('aria-hidden', true).kendoStop(true, true).kendoAnimate(close);
|
|
return false;
|
|
}
|
|
item.attr('data-animating', true);
|
|
var isAjaxContent = (item.children('.' + LINK).data(CONTENTURL) || false) && contentHolder.is(EMPTY), showContentElement = function () {
|
|
that.tabGroup.find('.' + TABONTOP).removeClass(TABONTOP);
|
|
item.addClass(TABONTOP).css('z-index');
|
|
if (kendo.size(animation.effects)) {
|
|
oldTab.kendoAddClass(DEFAULTSTATE, { duration: animation.duration });
|
|
item.kendoAddClass(ACTIVESTATE, { duration: animation.duration });
|
|
} else {
|
|
oldTab.addClass(DEFAULTSTATE);
|
|
item.addClass(ACTIVESTATE);
|
|
}
|
|
oldTab.removeAttr('aria-selected');
|
|
item.attr('aria-selected', true);
|
|
that._current(item);
|
|
that._sizeScrollWrap(contentElement);
|
|
contentElement.addClass(ACTIVESTATE).removeAttr('aria-hidden').kendoStop(true, true).attr('aria-expanded', true).kendoAnimate(extend({
|
|
init: function () {
|
|
that.trigger(SHOW, {
|
|
item: item[0],
|
|
contentElement: contentHolder[0]
|
|
});
|
|
kendo.resize(contentHolder);
|
|
}
|
|
}, animation, {
|
|
complete: function () {
|
|
item.removeAttr('data-animating');
|
|
that.trigger(ACTIVATE, {
|
|
item: item[0],
|
|
contentElement: contentHolder[0]
|
|
});
|
|
kendo.resize(contentHolder);
|
|
that.scrollWrap.css('height', '').css('height');
|
|
}
|
|
}));
|
|
}, showContent = function () {
|
|
if (!isAjaxContent) {
|
|
showContentElement();
|
|
that.trigger('change');
|
|
} else {
|
|
item.removeAttr('data-animating');
|
|
that.ajaxRequest(item, contentHolder, function () {
|
|
item.attr('data-animating', true);
|
|
showContentElement();
|
|
that.trigger('change');
|
|
});
|
|
}
|
|
if (that._scrollableModeActive) {
|
|
that._scrollTabsToItem(item);
|
|
}
|
|
};
|
|
visibleContents.removeClass(ACTIVESTATE);
|
|
visibleContents.attr('aria-hidden', true);
|
|
visibleContents.attr('aria-expanded', false);
|
|
if (visibleContents.length) {
|
|
visibleContents.kendoStop(true, true).kendoAnimate(extend({ complete: showContent }, close));
|
|
} else {
|
|
showContent();
|
|
}
|
|
return true;
|
|
},
|
|
contentElement: function (itemIndex) {
|
|
if (isNaN(itemIndex - 0)) {
|
|
return undefined;
|
|
}
|
|
var contentElements = this.contentElements && this.contentElements[0] && !kendo.kineticScrollNeeded ? this.contentElements : this.contentAnimators;
|
|
itemIndex = contentElements && itemIndex < 0 ? contentElements.length + itemIndex : itemIndex;
|
|
var idTest = new RegExp('-' + (itemIndex + 1) + '$');
|
|
if (contentElements) {
|
|
for (var i = 0, len = contentElements.length; i < len; i++) {
|
|
if (idTest.test(contentElements.eq(i).closest('.k-content')[0].id)) {
|
|
return contentElements[i];
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
},
|
|
contentHolder: function (itemIndex) {
|
|
var contentElement = $(this.contentElement(itemIndex)), scrollContainer = contentElement.children('.km-scroll-container');
|
|
return kendo.support.touch && scrollContainer[0] ? scrollContainer : contentElement;
|
|
},
|
|
ajaxRequest: function (element, content, complete, url) {
|
|
element = this.tabGroup.find(element);
|
|
var that = this, xhr = $.ajaxSettings.xhr, link = element.find('.' + LINK), data = {}, halfWidth = element.width() / 2, fakeProgress = false, statusIcon = element.find('.k-loading').removeClass('k-complete');
|
|
if (!statusIcon[0]) {
|
|
statusIcon = $('<span class=\'k-loading\'/>').prependTo(element);
|
|
}
|
|
var endState = halfWidth * 2 - statusIcon.width();
|
|
var oldProgressAnimation = function () {
|
|
statusIcon.animate({ marginLeft: (parseInt(statusIcon.css('marginLeft'), 10) || 0) < halfWidth ? endState : 0 }, 500, oldProgressAnimation);
|
|
};
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 10) {
|
|
setTimeout(oldProgressAnimation, 40);
|
|
}
|
|
url = url || link.data(CONTENTURL) || link.attr(HREF);
|
|
that.inRequest = true;
|
|
that.xhr = $.ajax({
|
|
type: 'GET',
|
|
cache: false,
|
|
url: url,
|
|
dataType: 'html',
|
|
data: data,
|
|
xhr: function () {
|
|
var current = this, request = xhr(), event = current.progressUpload ? 'progressUpload' : current.progress ? 'progress' : false;
|
|
if (request) {
|
|
$.each([
|
|
request,
|
|
request.upload
|
|
], function () {
|
|
if (this.addEventListener) {
|
|
this.addEventListener('progress', function (evt) {
|
|
if (event) {
|
|
current[event](evt);
|
|
}
|
|
}, false);
|
|
}
|
|
});
|
|
}
|
|
current.noProgress = !(window.XMLHttpRequest && 'upload' in new XMLHttpRequest());
|
|
return request;
|
|
},
|
|
progress: function (evt) {
|
|
if (evt.lengthComputable) {
|
|
var percent = parseInt(evt.loaded / evt.total * 100, 10) + '%';
|
|
statusIcon.stop(true).addClass('k-progress').css({
|
|
'width': percent,
|
|
'marginLeft': 0
|
|
});
|
|
}
|
|
},
|
|
error: function (xhr, status) {
|
|
if (that.trigger('error', {
|
|
xhr: xhr,
|
|
status: status
|
|
})) {
|
|
this.complete();
|
|
}
|
|
},
|
|
stopProgress: function () {
|
|
clearInterval(fakeProgress);
|
|
statusIcon.stop(true).addClass('k-progress')[0].style.cssText = '';
|
|
},
|
|
complete: function (xhr) {
|
|
that.inRequest = false;
|
|
if (this.noProgress) {
|
|
setTimeout(this.stopProgress, 500);
|
|
} else {
|
|
this.stopProgress();
|
|
}
|
|
if (xhr.statusText == 'abort') {
|
|
statusIcon.remove();
|
|
}
|
|
},
|
|
success: function (data) {
|
|
statusIcon.addClass('k-complete');
|
|
try {
|
|
var current = this, loaded = 10;
|
|
if (current.noProgress) {
|
|
statusIcon.width(loaded + '%');
|
|
fakeProgress = setInterval(function () {
|
|
current.progress({
|
|
lengthComputable: true,
|
|
loaded: Math.min(loaded, 100),
|
|
total: 100
|
|
});
|
|
loaded += 10;
|
|
}, 40);
|
|
}
|
|
that.angular('cleanup', function () {
|
|
return { elements: content.get() };
|
|
});
|
|
kendo.destroy(content);
|
|
content.html(data);
|
|
} catch (e) {
|
|
var console = window.console;
|
|
if (console && console.error) {
|
|
console.error(e.name + ': ' + e.message + ' in ' + url);
|
|
}
|
|
this.error(this.xhr, 'error');
|
|
}
|
|
if (complete) {
|
|
complete.call(that, content);
|
|
}
|
|
that.angular('compile', function () {
|
|
return { elements: content.get() };
|
|
});
|
|
that.trigger(CONTENTLOAD, {
|
|
item: element[0],
|
|
contentElement: content[0]
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
extend(TabStrip, {
|
|
renderItem: function (options) {
|
|
options = extend({
|
|
tabStrip: {},
|
|
group: {}
|
|
}, options);
|
|
var empty = templates.empty, item = options.item;
|
|
return templates.item(extend(options, {
|
|
image: item.imageUrl ? templates.image : empty,
|
|
sprite: item.spriteCssClass ? templates.sprite : empty,
|
|
itemWrapper: templates.itemWrapper
|
|
}, rendering));
|
|
},
|
|
renderContent: function (options) {
|
|
return templates.content(extend(options, rendering));
|
|
}
|
|
});
|
|
kendo.ui.plugin(TabStrip);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.timepicker', ['kendo.popup'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'timepicker',
|
|
name: 'TimePicker',
|
|
category: 'web',
|
|
description: 'The TimePicker widget allows the end user to select a value from a list of predefined values or to type a new value.',
|
|
depends: ['popup']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, keys = kendo.keys, parse = kendo.parseDate, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, support = kendo.support, browser = support.browser, ui = kendo.ui, Widget = ui.Widget, OPEN = 'open', CLOSE = 'close', CHANGE = 'change', ns = '.kendoTimePicker', CLICK = 'click' + ns, DEFAULT = 'k-state-default', DISABLED = 'disabled', READONLY = 'readonly', LI = 'li', SPAN = '<span/>', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, MOUSEDOWN = 'mousedown' + ns, MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000, SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', ARIA_SELECTED = 'aria-selected', ARIA_EXPANDED = 'aria-expanded', ARIA_HIDDEN = 'aria-hidden', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', ARIA_ACTIVEDESCENDANT = 'aria-activedescendant', ID = 'id', isArray = $.isArray, extend = $.extend, proxy = $.proxy, DATE = Date, TODAY = new DATE();
|
|
TODAY = new DATE(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDate(), 0, 0, 0);
|
|
var TimeView = function (options) {
|
|
var that = this, id = options.id;
|
|
that.options = options;
|
|
that._dates = [];
|
|
that.ul = $('<ul tabindex="-1" role="listbox" aria-hidden="true" unselectable="on" class="k-list k-reset"/>').css({ overflow: support.kineticScrollNeeded ? '' : 'auto' }).on(CLICK, LI, proxy(that._click, that)).on('mouseenter' + ns, LI, function () {
|
|
$(this).addClass(HOVER);
|
|
}).on('mouseleave' + ns, LI, function () {
|
|
$(this).removeClass(HOVER);
|
|
});
|
|
that.list = $('<div class=\'k-list-container k-list-scroller\' unselectable=\'on\'/>').append(that.ul).on(MOUSEDOWN, preventDefault);
|
|
if (id) {
|
|
that._timeViewID = id + '_timeview';
|
|
that._optionID = id + '_option_selected';
|
|
that.ul.attr(ID, that._timeViewID);
|
|
}
|
|
that._popup();
|
|
that._heightHandler = proxy(that._height, that);
|
|
that.template = kendo.template('<li tabindex="-1" role="option" class="k-item" unselectable="on">#=data#</li>', { useWithBlock: false });
|
|
};
|
|
TimeView.prototype = {
|
|
current: function (candidate) {
|
|
var that = this, active = that.options.active;
|
|
if (candidate !== undefined) {
|
|
if (that._current) {
|
|
that._current.removeClass(SELECTED).removeAttr(ARIA_SELECTED).removeAttr(ID);
|
|
}
|
|
if (candidate) {
|
|
candidate = $(candidate).addClass(SELECTED).attr(ID, that._optionID).attr(ARIA_SELECTED, true);
|
|
that.scroll(candidate[0]);
|
|
}
|
|
that._current = candidate;
|
|
if (active) {
|
|
active(candidate);
|
|
}
|
|
} else {
|
|
return that._current;
|
|
}
|
|
},
|
|
close: function () {
|
|
this.popup.close();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.ul.off(ns);
|
|
that.list.off(ns);
|
|
that.popup.destroy();
|
|
},
|
|
open: function () {
|
|
var that = this;
|
|
if (!that.ul[0].firstChild) {
|
|
that.bind();
|
|
}
|
|
that.popup.open();
|
|
if (that._current) {
|
|
that.scroll(that._current[0]);
|
|
}
|
|
},
|
|
dataBind: function (dates) {
|
|
var that = this, options = that.options, format = options.format, toString = kendo.toString, template = that.template, length = dates.length, idx = 0, date, html = '';
|
|
for (; idx < length; idx++) {
|
|
date = dates[idx];
|
|
if (isInRange(date, options.min, options.max)) {
|
|
html += template(toString(date, format, options.culture));
|
|
}
|
|
}
|
|
that._html(html);
|
|
},
|
|
refresh: function () {
|
|
var that = this, options = that.options, format = options.format, offset = dst(), ignoreDST = offset < 0, min = options.min, max = options.max, msMin = getMilliseconds(min), msMax = getMilliseconds(max), msInterval = options.interval * MS_PER_MINUTE, toString = kendo.toString, template = that.template, start = new DATE(+min), startDay = start.getDate(), msStart, lastIdx, idx = 0, length, html = '';
|
|
if (ignoreDST) {
|
|
length = (MS_PER_DAY + offset * MS_PER_MINUTE) / msInterval;
|
|
} else {
|
|
length = MS_PER_DAY / msInterval;
|
|
}
|
|
if (msMin != msMax) {
|
|
if (msMin > msMax) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
length = (msMax - msMin) / msInterval + 1;
|
|
}
|
|
lastIdx = parseInt(length, 10);
|
|
for (; idx < length; idx++) {
|
|
if (idx) {
|
|
setTime(start, msInterval, ignoreDST);
|
|
}
|
|
if (msMax && lastIdx == idx) {
|
|
msStart = getMilliseconds(start);
|
|
if (startDay < start.getDate()) {
|
|
msStart += MS_PER_DAY;
|
|
}
|
|
if (msStart > msMax) {
|
|
start = new DATE(+max);
|
|
}
|
|
}
|
|
that._dates.push(getMilliseconds(start));
|
|
html += template(toString(start, format, options.culture));
|
|
}
|
|
that._html(html);
|
|
},
|
|
bind: function () {
|
|
var that = this, dates = that.options.dates;
|
|
if (dates && dates[0]) {
|
|
that.dataBind(dates);
|
|
} else {
|
|
that.refresh();
|
|
}
|
|
},
|
|
_html: function (html) {
|
|
var that = this;
|
|
that.ul[0].innerHTML = html;
|
|
that.popup.unbind(OPEN, that._heightHandler);
|
|
that.popup.one(OPEN, that._heightHandler);
|
|
that.current(null);
|
|
that.select(that._value);
|
|
},
|
|
scroll: function (item) {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
var content = this.list[0], itemOffsetTop = item.offsetTop, itemOffsetHeight = item.offsetHeight, contentScrollTop = content.scrollTop, contentOffsetHeight = content.clientHeight, bottomDistance = itemOffsetTop + itemOffsetHeight;
|
|
if (contentScrollTop > itemOffsetTop) {
|
|
contentScrollTop = itemOffsetTop;
|
|
} else if (bottomDistance > contentScrollTop + contentOffsetHeight) {
|
|
contentScrollTop = bottomDistance - contentOffsetHeight;
|
|
}
|
|
content.scrollTop = contentScrollTop;
|
|
},
|
|
select: function (li) {
|
|
var that = this, options = that.options, current = that._current, selection;
|
|
if (li instanceof Date) {
|
|
li = kendo.toString(li, options.format, options.culture);
|
|
}
|
|
if (typeof li === 'string') {
|
|
if (!current || current.text() !== li) {
|
|
li = $.grep(that.ul[0].childNodes, function (node) {
|
|
return (node.textContent || node.innerText) == li;
|
|
});
|
|
li = li[0] ? li : null;
|
|
} else {
|
|
li = current;
|
|
}
|
|
}
|
|
selection = that._distinctSelection(li);
|
|
that.current(selection);
|
|
},
|
|
_distinctSelection: function (selection) {
|
|
var that = this, currentValue, selectionIndex;
|
|
if (selection && selection.length > 1) {
|
|
currentValue = getMilliseconds(that._value);
|
|
selectionIndex = $.inArray(currentValue, that._dates);
|
|
selection = that.ul.children()[selectionIndex];
|
|
}
|
|
return selection;
|
|
},
|
|
setOptions: function (options) {
|
|
var old = this.options;
|
|
options.min = parse(options.min);
|
|
options.max = parse(options.max);
|
|
this.options = extend(old, options, {
|
|
active: old.active,
|
|
change: old.change,
|
|
close: old.close,
|
|
open: old.open
|
|
});
|
|
this.bind();
|
|
},
|
|
toggle: function () {
|
|
var that = this;
|
|
if (that.popup.visible()) {
|
|
that.close();
|
|
} else {
|
|
that.open();
|
|
}
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
that._value = value;
|
|
if (that.ul[0].firstChild) {
|
|
that.select(value);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var that = this, li = $(e.currentTarget), date = li.text(), dates = that.options.dates;
|
|
if (dates && dates.length > 0) {
|
|
date = dates[li.index()];
|
|
}
|
|
if (!e.isDefaultPrevented()) {
|
|
that.select(li);
|
|
that.options.change(date, true);
|
|
that.close();
|
|
}
|
|
},
|
|
_height: function () {
|
|
var that = this;
|
|
var list = that.list;
|
|
var parent = list.parent('.k-animation-container');
|
|
var height = that.options.height;
|
|
if (that.ul[0].children.length) {
|
|
list.add(parent).show().height(that.ul[0].scrollHeight > height ? height : 'auto').hide();
|
|
}
|
|
},
|
|
_parse: function (value) {
|
|
var that = this, options = that.options, current = that._value || TODAY;
|
|
if (value instanceof DATE) {
|
|
return value;
|
|
}
|
|
value = parse(value, options.parseFormats, options.culture);
|
|
if (value) {
|
|
value = new DATE(current.getFullYear(), current.getMonth(), current.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds());
|
|
}
|
|
return value;
|
|
},
|
|
_adjustListWidth: function () {
|
|
var list = this.list, width = list[0].style.width, wrapper = this.options.anchor, computedStyle, computedWidth;
|
|
if (!list.data('width') && width) {
|
|
return;
|
|
}
|
|
computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0;
|
|
computedWidth = computedStyle ? parseFloat(computedStyle.width) : wrapper.outerWidth();
|
|
if (computedStyle && (browser.mozilla || browser.msie)) {
|
|
computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth);
|
|
}
|
|
width = computedWidth - (list.outerWidth() - list.width());
|
|
list.css({
|
|
fontFamily: wrapper.css('font-family'),
|
|
width: width
|
|
}).data('width', width);
|
|
},
|
|
_popup: function () {
|
|
var that = this, list = that.list, options = that.options, anchor = options.anchor;
|
|
that.popup = new ui.Popup(list, extend(options.popup, {
|
|
anchor: anchor,
|
|
open: options.open,
|
|
close: options.close,
|
|
animation: options.animation,
|
|
isRtl: support.isRtl(options.anchor)
|
|
}));
|
|
},
|
|
move: function (e) {
|
|
var that = this, key = e.keyCode, ul = that.ul[0], current = that._current, down = key === keys.DOWN;
|
|
if (key === keys.UP || down) {
|
|
if (e.altKey) {
|
|
that.toggle(down);
|
|
return;
|
|
} else if (down) {
|
|
current = current ? current[0].nextSibling : ul.firstChild;
|
|
} else {
|
|
current = current ? current[0].previousSibling : ul.lastChild;
|
|
}
|
|
if (current) {
|
|
that.select(current);
|
|
}
|
|
that.options.change(that._current.text());
|
|
e.preventDefault();
|
|
} else if (key === keys.ENTER || key === keys.TAB || key === keys.ESC) {
|
|
e.preventDefault();
|
|
if (current) {
|
|
that.options.change(current.text(), true);
|
|
}
|
|
that.close();
|
|
}
|
|
}
|
|
};
|
|
function setTime(date, time, ignoreDST) {
|
|
var offset = date.getTimezoneOffset(), offsetDiff;
|
|
date.setTime(date.getTime() + time);
|
|
if (!ignoreDST) {
|
|
offsetDiff = date.getTimezoneOffset() - offset;
|
|
date.setTime(date.getTime() + offsetDiff * MS_PER_MINUTE);
|
|
}
|
|
}
|
|
function dst() {
|
|
var today = new DATE(), midnight = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0), noon = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 12, 0, 0);
|
|
return -1 * (midnight.getTimezoneOffset() - noon.getTimezoneOffset());
|
|
}
|
|
function getMilliseconds(date) {
|
|
return date.getHours() * 60 * MS_PER_MINUTE + date.getMinutes() * MS_PER_MINUTE + date.getSeconds() * 1000 + date.getMilliseconds();
|
|
}
|
|
function isInRange(value, min, max) {
|
|
var msMin = getMilliseconds(min), msMax = getMilliseconds(max), msValue;
|
|
if (!value || msMin == msMax) {
|
|
return true;
|
|
}
|
|
msValue = getMilliseconds(value);
|
|
if (msMin > msValue) {
|
|
msValue += MS_PER_DAY;
|
|
}
|
|
if (msMax < msMin) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
return msValue >= msMin && msValue <= msMax;
|
|
}
|
|
TimeView.getMilliseconds = getMilliseconds;
|
|
kendo.TimeView = TimeView;
|
|
var TimePicker = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, ul, timeView, disabled;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
options.min = parse(element.attr('min')) || parse(options.min);
|
|
options.max = parse(element.attr('max')) || parse(options.max);
|
|
normalize(options);
|
|
that._initialOptions = extend({}, options);
|
|
that._wrapper();
|
|
that.timeView = timeView = new TimeView(extend({}, options, {
|
|
id: element.attr(ID),
|
|
anchor: that.wrapper,
|
|
format: options.format,
|
|
change: function (value, trigger) {
|
|
if (trigger) {
|
|
that._change(value);
|
|
} else {
|
|
element.val(value);
|
|
}
|
|
},
|
|
open: function (e) {
|
|
that.timeView._adjustListWidth();
|
|
if (that.trigger(OPEN)) {
|
|
e.preventDefault();
|
|
} else {
|
|
element.attr(ARIA_EXPANDED, true);
|
|
ul.attr(ARIA_HIDDEN, false);
|
|
}
|
|
},
|
|
close: function (e) {
|
|
if (that.trigger(CLOSE)) {
|
|
e.preventDefault();
|
|
} else {
|
|
element.attr(ARIA_EXPANDED, false);
|
|
ul.attr(ARIA_HIDDEN, true);
|
|
}
|
|
},
|
|
active: function (current) {
|
|
element.removeAttr(ARIA_ACTIVEDESCENDANT);
|
|
if (current) {
|
|
element.attr(ARIA_ACTIVEDESCENDANT, timeView._optionID);
|
|
}
|
|
}
|
|
}));
|
|
ul = timeView.ul;
|
|
that._icon();
|
|
that._reset();
|
|
try {
|
|
element[0].setAttribute('type', 'text');
|
|
} catch (e) {
|
|
element[0].type = 'text';
|
|
}
|
|
element.addClass('k-input').attr({
|
|
'role': 'combobox',
|
|
'aria-expanded': false,
|
|
'aria-owns': timeView._timeViewID
|
|
});
|
|
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(element.is('[readonly]'));
|
|
}
|
|
that._old = that._update(options.value || that.element.val());
|
|
that._oldText = element.val();
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'TimePicker',
|
|
min: TODAY,
|
|
max: TODAY,
|
|
format: '',
|
|
dates: [],
|
|
parseFormats: [],
|
|
value: null,
|
|
interval: 30,
|
|
height: 200,
|
|
animation: {}
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
CHANGE
|
|
],
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
var value = that._value;
|
|
Widget.fn.setOptions.call(that, options);
|
|
options = that.options;
|
|
normalize(options);
|
|
that.timeView.setOptions(options);
|
|
if (value) {
|
|
that.element.val(kendo.toString(value, options.format, options.culture));
|
|
}
|
|
},
|
|
dataBind: function (dates) {
|
|
if (isArray(dates)) {
|
|
this.timeView.dataBind(dates);
|
|
}
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, disable = options.disable, readonly = options.readonly, arrow = that._arrow.off(ns), element = that.element.off(ns), wrapper = that._inputWrapper.off(ns);
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
element.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, proxy(that._keydown, that)).on('focusout' + ns, proxy(that._blur, that)).on('focus' + ns, function () {
|
|
that._inputWrapper.addClass(FOCUSED);
|
|
});
|
|
arrow.on(CLICK, proxy(that._click, that)).on(MOUSEDOWN, preventDefault);
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.timeView.destroy();
|
|
that.element.off(ns);
|
|
that._arrow.off(ns);
|
|
that._inputWrapper.off(ns);
|
|
if (that._form) {
|
|
that._form.off('reset', that._resetHandler);
|
|
}
|
|
},
|
|
close: function () {
|
|
this.timeView.close();
|
|
},
|
|
open: function () {
|
|
this.timeView.open();
|
|
},
|
|
min: function (value) {
|
|
return this._option('min', value);
|
|
},
|
|
max: function (value) {
|
|
return this._option('max', value);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
that._old = that._update(value);
|
|
if (that._old === null) {
|
|
that.element.val('');
|
|
}
|
|
that._oldText = that.element.val();
|
|
},
|
|
_blur: function () {
|
|
var that = this, value = that.element.val();
|
|
that.close();
|
|
if (value !== that._oldText) {
|
|
that._change(value);
|
|
}
|
|
that._inputWrapper.removeClass(FOCUSED);
|
|
},
|
|
_click: function () {
|
|
var that = this, element = that.element;
|
|
that.timeView.toggle();
|
|
if (!support.touch && element[0] !== activeElement()) {
|
|
element.focus();
|
|
}
|
|
},
|
|
_change: function (value) {
|
|
var that = this;
|
|
value = that._update(value);
|
|
if (+that._old != +value) {
|
|
that._old = value;
|
|
that._oldText = that.element.val();
|
|
if (!that._typing) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
that.trigger(CHANGE);
|
|
}
|
|
that._typing = false;
|
|
},
|
|
_icon: function () {
|
|
var that = this, element = that.element, arrow;
|
|
arrow = element.next('span.k-select');
|
|
if (!arrow[0]) {
|
|
arrow = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-clock">select</span></span>').insertAfter(element);
|
|
}
|
|
that._arrow = arrow.attr({
|
|
'role': 'button',
|
|
'aria-controls': that.timeView._timeViewID
|
|
});
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, timeView = that.timeView, value = that.element.val();
|
|
if (timeView.popup.visible() || e.altKey) {
|
|
timeView.move(e);
|
|
} else if (key === keys.ENTER && value !== that._oldText) {
|
|
that._change(value);
|
|
} else {
|
|
that._typing = true;
|
|
}
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this, options = that.options;
|
|
if (value === undefined) {
|
|
return options[option];
|
|
}
|
|
value = that.timeView._parse(value);
|
|
if (!value) {
|
|
return;
|
|
}
|
|
value = new DATE(+value);
|
|
options[option] = value;
|
|
that.timeView.options[option] = value;
|
|
that.timeView.bind();
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_update: function (value) {
|
|
var that = this, options = that.options, timeView = that.timeView, date = timeView._parse(value);
|
|
if (!isInRange(date, options.min, options.max)) {
|
|
date = null;
|
|
}
|
|
that._value = date;
|
|
that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
|
|
timeView.value(date);
|
|
return date;
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper;
|
|
wrapper = element.parents('.k-timepicker');
|
|
if (!wrapper[0]) {
|
|
wrapper = element.wrap(SPAN).parent().addClass('k-picker-wrap k-state-default');
|
|
wrapper = wrapper.wrap(SPAN).parent();
|
|
}
|
|
wrapper[0].style.cssText = element[0].style.cssText;
|
|
that.wrapper = wrapper.addClass('k-widget k-timepicker k-header').addClass(element[0].className);
|
|
element.css({
|
|
width: '100%',
|
|
height: element[0].style.height
|
|
});
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
that.value(element[0].defaultValue);
|
|
that.max(that._initialOptions.max);
|
|
that.min(that._initialOptions.min);
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
}
|
|
});
|
|
function normalize(options) {
|
|
var parseFormats = options.parseFormats;
|
|
options.format = extractFormat(options.format || kendo.getCulture(options.culture).calendars.standard.patterns.t);
|
|
parseFormats = isArray(parseFormats) ? parseFormats : [parseFormats];
|
|
parseFormats.splice(0, 0, options.format);
|
|
options.parseFormats = parseFormats;
|
|
}
|
|
function preventDefault(e) {
|
|
e.preventDefault();
|
|
}
|
|
ui.plugin(TimePicker);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.toolbar', [
|
|
'kendo.core',
|
|
'kendo.userevents',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'toolbar',
|
|
name: 'ToolBar',
|
|
category: 'web',
|
|
description: 'The ToolBar widget displays one or more command buttons divided into groups.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, proxy = $.proxy, isFunction = kendo.isFunction, keys = kendo.keys, TOOLBAR = 'k-toolbar', BUTTON = 'k-button', OVERFLOW_BUTTON = 'k-overflow-button', TOGGLE_BUTTON = 'k-toggle-button', BUTTON_GROUP = 'k-button-group', SPLIT_BUTTON = 'k-split-button', SEPARATOR = 'k-separator', POPUP = 'k-popup', RESIZABLE_TOOLBAR = 'k-toolbar-resizable', STATE_ACTIVE = 'k-state-active', STATE_DISABLED = 'k-state-disabled', STATE_HIDDEN = 'k-state-hidden', GROUP_START = 'k-group-start', GROUP_END = 'k-group-end', PRIMARY = 'k-primary', ICON = 'k-icon', ICON_PREFIX = 'k-i-', BUTTON_ICON = 'k-button-icon', BUTTON_ICON_TEXT = 'k-button-icontext', LIST_CONTAINER = 'k-list-container k-split-container', SPLIT_BUTTON_ARROW = 'k-split-button-arrow', OVERFLOW_ANCHOR = 'k-overflow-anchor', OVERFLOW_CONTAINER = 'k-overflow-container', FIRST_TOOLBAR_VISIBLE = 'k-toolbar-first-visible', LAST_TOOLBAR_VISIBLE = 'k-toolbar-last-visible', CLICK = 'click', TOGGLE = 'toggle', OPEN = 'open', CLOSE = 'close', OVERFLOW_OPEN = 'overflowOpen', OVERFLOW_CLOSE = 'overflowClose', OVERFLOW_NEVER = 'never', OVERFLOW_AUTO = 'auto', OVERFLOW_ALWAYS = 'always', OVERFLOW_HIDDEN = 'k-overflow-hidden', KENDO_UID_ATTR = kendo.attr('uid');
|
|
kendo.toolbar = {};
|
|
var components = {
|
|
overflowAnchor: '<div tabindex="0" class="k-overflow-anchor"></div>',
|
|
overflowContainer: '<ul class="k-overflow-container k-list-container"></ul>'
|
|
};
|
|
kendo.toolbar.registerComponent = function (name, toolbar, overflow) {
|
|
components[name] = {
|
|
toolbar: toolbar,
|
|
overflow: overflow
|
|
};
|
|
};
|
|
var Item = kendo.Class.extend({
|
|
addOverflowAttr: function () {
|
|
this.element.attr(kendo.attr('overflow'), this.options.overflow || OVERFLOW_AUTO);
|
|
},
|
|
addUidAttr: function () {
|
|
this.element.attr(KENDO_UID_ATTR, this.options.uid);
|
|
},
|
|
addIdAttr: function () {
|
|
if (this.options.id) {
|
|
this.element.attr('id', this.options.id);
|
|
}
|
|
},
|
|
addOverflowIdAttr: function () {
|
|
if (this.options.id) {
|
|
this.element.attr('id', this.options.id + '_overflow');
|
|
}
|
|
},
|
|
attributes: function () {
|
|
if (this.options.attributes) {
|
|
this.element.attr(this.options.attributes);
|
|
}
|
|
},
|
|
show: function () {
|
|
this.element.removeClass(STATE_HIDDEN).show();
|
|
this.options.hidden = false;
|
|
},
|
|
hide: function () {
|
|
this.element.addClass(STATE_HIDDEN).hide();
|
|
this.options.hidden = true;
|
|
},
|
|
remove: function () {
|
|
this.element.remove();
|
|
},
|
|
enable: function (isEnabled) {
|
|
if (isEnabled === undefined) {
|
|
isEnabled = true;
|
|
}
|
|
this.element.toggleClass(STATE_DISABLED, !isEnabled);
|
|
this.options.enable = isEnabled;
|
|
},
|
|
twin: function () {
|
|
var uid = this.element.attr(KENDO_UID_ATTR);
|
|
if (this.overflow) {
|
|
return this.toolbar.element.find('[' + KENDO_UID_ATTR + '=\'' + uid + '\']').data(this.options.type);
|
|
} else if (this.toolbar.options.resizable) {
|
|
return this.toolbar.popup.element.find('[' + KENDO_UID_ATTR + '=\'' + uid + '\']').data(this.options.type);
|
|
}
|
|
}
|
|
});
|
|
kendo.toolbar.Item = Item;
|
|
var Button = Item.extend({
|
|
init: function (options, toolbar) {
|
|
var element = options.useButtonTag ? $('<button tabindex="0"></button>') : $('<a href tabindex="0"></a>');
|
|
this.element = element;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
if (options.primary) {
|
|
element.addClass(PRIMARY);
|
|
}
|
|
if (options.togglable) {
|
|
element.addClass(TOGGLE_BUTTON);
|
|
this.toggle(options.selected);
|
|
}
|
|
if (options.url !== undefined && !options.useButtonTag) {
|
|
element.attr('href', options.url);
|
|
if (options.mobile) {
|
|
element.attr(kendo.attr('role'), 'button');
|
|
}
|
|
}
|
|
if (options.group) {
|
|
element.attr(kendo.attr('group'), options.group);
|
|
this.group = this.toolbar.addToGroup(this, options.group);
|
|
}
|
|
if (!options.togglable && options.click && isFunction(options.click)) {
|
|
this.clickHandler = options.click;
|
|
}
|
|
if (options.togglable && options.toggle && isFunction(options.toggle)) {
|
|
this.toggleHandler = options.toggle;
|
|
}
|
|
},
|
|
toggle: function (state, propagate) {
|
|
state = !!state;
|
|
if (this.group && state) {
|
|
this.group.select(this);
|
|
} else if (!this.group) {
|
|
this.select(state);
|
|
}
|
|
if (propagate && this.twin()) {
|
|
this.twin().toggle(state);
|
|
}
|
|
},
|
|
getParentGroup: function () {
|
|
if (this.options.isChild) {
|
|
return this.element.closest('.' + BUTTON_GROUP).data('buttonGroup');
|
|
}
|
|
},
|
|
_addGraphics: function () {
|
|
var element = this.element, icon = this.options.icon, spriteCssClass = this.options.spriteCssClass, imageUrl = this.options.imageUrl, isEmpty, span, img;
|
|
if (spriteCssClass || imageUrl || icon) {
|
|
isEmpty = true;
|
|
element.contents().not('span.k-sprite,span.' + ICON + ',img.k-image').each(function (idx, el) {
|
|
if (el.nodeType == 1 || el.nodeType == 3 && $.trim(el.nodeValue).length > 0) {
|
|
isEmpty = false;
|
|
}
|
|
});
|
|
if (isEmpty) {
|
|
element.addClass(BUTTON_ICON);
|
|
} else {
|
|
element.addClass(BUTTON_ICON_TEXT);
|
|
}
|
|
}
|
|
if (icon) {
|
|
span = element.children('span.' + ICON).first();
|
|
if (!span[0]) {
|
|
span = $('<span class="' + ICON + '"></span>').prependTo(element);
|
|
}
|
|
span.addClass(ICON_PREFIX + icon);
|
|
} else if (spriteCssClass) {
|
|
span = element.children('span.k-sprite').first();
|
|
if (!span[0]) {
|
|
span = $('<span class="k-sprite"></span>').prependTo(element);
|
|
}
|
|
span.addClass(spriteCssClass);
|
|
} else if (imageUrl) {
|
|
img = element.children('img.k-image').first();
|
|
if (!img[0]) {
|
|
img = $('<img alt="icon" class="k-image" />').prependTo(element);
|
|
}
|
|
img.attr('src', imageUrl);
|
|
}
|
|
}
|
|
});
|
|
kendo.toolbar.Button = Button;
|
|
var ToolBarButton = Button.extend({
|
|
init: function (options, toolbar) {
|
|
Button.fn.init.call(this, options, toolbar);
|
|
var element = this.element;
|
|
element.addClass(BUTTON);
|
|
this.addIdAttr();
|
|
if (options.align) {
|
|
element.addClass('k-align-' + options.align);
|
|
}
|
|
if (options.showText != 'overflow' && options.text) {
|
|
if (options.mobile) {
|
|
element.html('<span class="km-text">' + options.text + '</span>');
|
|
} else {
|
|
element.html(options.text);
|
|
}
|
|
}
|
|
options.hasIcon = options.showIcon != 'overflow' && (options.icon || options.spriteCssClass || options.imageUrl);
|
|
if (options.hasIcon) {
|
|
this._addGraphics();
|
|
}
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.enable(options.enable);
|
|
if (options.hidden) {
|
|
this.hide();
|
|
}
|
|
this.element.data({
|
|
type: 'button',
|
|
button: this
|
|
});
|
|
},
|
|
select: function (selected) {
|
|
if (selected === undefined) {
|
|
selected = false;
|
|
}
|
|
this.element.toggleClass(STATE_ACTIVE, selected);
|
|
this.options.selected = selected;
|
|
}
|
|
});
|
|
kendo.toolbar.ToolBarButton = ToolBarButton;
|
|
var OverflowButton = Button.extend({
|
|
init: function (options, toolbar) {
|
|
this.overflow = true;
|
|
Button.fn.init.call(this, options, toolbar);
|
|
var element = this.element;
|
|
if (options.showText != 'toolbar' && options.text) {
|
|
if (options.mobile) {
|
|
element.html('<span class="km-text">' + options.text + '</span>');
|
|
} else {
|
|
element.html('<span class="k-text">' + options.text + '</span>');
|
|
}
|
|
}
|
|
options.hasIcon = options.showIcon != 'toolbar' && (options.icon || options.spriteCssClass || options.imageUrl);
|
|
if (options.hasIcon) {
|
|
this._addGraphics();
|
|
}
|
|
if (!options.isChild) {
|
|
this._wrap();
|
|
}
|
|
this.addOverflowIdAttr();
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.enable(options.enable);
|
|
element.addClass(OVERFLOW_BUTTON + ' ' + BUTTON);
|
|
if (options.hidden) {
|
|
this.hide();
|
|
}
|
|
this.element.data({
|
|
type: 'button',
|
|
button: this
|
|
});
|
|
},
|
|
_wrap: function () {
|
|
this.element = this.element.wrap('<li></li>').parent();
|
|
},
|
|
overflowHidden: function () {
|
|
this.element.addClass(OVERFLOW_HIDDEN);
|
|
},
|
|
select: function (selected) {
|
|
if (selected === undefined) {
|
|
selected = false;
|
|
}
|
|
if (this.options.isChild) {
|
|
this.element.toggleClass(STATE_ACTIVE, selected);
|
|
} else {
|
|
this.element.find('.k-button').toggleClass(STATE_ACTIVE, selected);
|
|
}
|
|
this.options.selected = selected;
|
|
}
|
|
});
|
|
kendo.toolbar.OverflowButton = OverflowButton;
|
|
kendo.toolbar.registerComponent('button', ToolBarButton, OverflowButton);
|
|
var ButtonGroup = Item.extend({
|
|
createButtons: function (buttonConstructor) {
|
|
var options = this.options;
|
|
var items = options.buttons || [];
|
|
var item;
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (!items[i].uid) {
|
|
items[i].uid = kendo.guid();
|
|
}
|
|
item = new buttonConstructor($.extend({
|
|
mobile: options.mobile,
|
|
isChild: true,
|
|
type: 'button'
|
|
}, items[i]), this.toolbar);
|
|
item.element.appendTo(this.element);
|
|
}
|
|
},
|
|
refresh: function () {
|
|
this.element.children().filter(':not(\'.' + STATE_HIDDEN + '\'):first').addClass(GROUP_START);
|
|
this.element.children().filter(':not(\'.' + STATE_HIDDEN + '\'):last').addClass(GROUP_END);
|
|
}
|
|
});
|
|
kendo.toolbar.ButtonGroup = ButtonGroup;
|
|
var ToolBarButtonGroup = ButtonGroup.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<div></div>');
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.addIdAttr();
|
|
if (options.align) {
|
|
element.addClass('k-align-' + options.align);
|
|
}
|
|
this.createButtons(ToolBarButton);
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.refresh();
|
|
element.addClass(BUTTON_GROUP);
|
|
this.element.data({
|
|
type: 'buttonGroup',
|
|
buttonGroup: this
|
|
});
|
|
}
|
|
});
|
|
kendo.toolbar.ToolBarButtonGroup = ToolBarButtonGroup;
|
|
var OverflowButtonGroup = ButtonGroup.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<li></li>');
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.overflow = true;
|
|
this.addOverflowIdAttr();
|
|
this.createButtons(OverflowButton);
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.refresh();
|
|
element.addClass((options.mobile ? '' : BUTTON_GROUP) + ' k-overflow-group');
|
|
this.element.data({
|
|
type: 'buttonGroup',
|
|
buttonGroup: this
|
|
});
|
|
},
|
|
overflowHidden: function () {
|
|
this.element.addClass(OVERFLOW_HIDDEN);
|
|
}
|
|
});
|
|
kendo.toolbar.OverflowButtonGroup = OverflowButtonGroup;
|
|
kendo.toolbar.registerComponent('buttonGroup', ToolBarButtonGroup, OverflowButtonGroup);
|
|
var ToolBarSplitButton = Item.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<div class="' + SPLIT_BUTTON + '" tabindex="0"></div>');
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.mainButton = new ToolBarButton(options, toolbar);
|
|
this.arrowButton = $('<a class="' + BUTTON + ' ' + SPLIT_BUTTON_ARROW + '"><span class="' + (options.mobile ? 'km-icon km-arrowdown' : 'k-icon k-i-arrow-s') + '"></span></a>');
|
|
this.popupElement = $('<ul class="' + LIST_CONTAINER + '"></ul>');
|
|
this.mainButton.element.removeAttr('href tabindex').appendTo(element);
|
|
this.arrowButton.appendTo(element);
|
|
this.popupElement.appendTo(element);
|
|
if (options.align) {
|
|
element.addClass('k-align-' + options.align);
|
|
}
|
|
if (!options.id) {
|
|
options.id = options.uid;
|
|
}
|
|
element.attr('id', options.id + '_wrapper');
|
|
this.addOverflowAttr();
|
|
this.addUidAttr();
|
|
this.createMenuButtons();
|
|
this.createPopup();
|
|
this._navigatable();
|
|
this.mainButton.main = true;
|
|
element.data({
|
|
type: 'splitButton',
|
|
splitButton: this,
|
|
kendoPopup: this.popup
|
|
});
|
|
},
|
|
_navigatable: function () {
|
|
var that = this;
|
|
that.popupElement.on('keydown', '.' + BUTTON, function (e) {
|
|
var li = $(e.target).parent();
|
|
e.preventDefault();
|
|
if (e.keyCode === keys.ESC || e.keyCode === keys.TAB || e.altKey && e.keyCode === keys.UP) {
|
|
that.toggle();
|
|
that.focus();
|
|
} else if (e.keyCode === keys.DOWN) {
|
|
findFocusableSibling(li, 'next').focus();
|
|
} else if (e.keyCode === keys.UP) {
|
|
findFocusableSibling(li, 'prev').focus();
|
|
} else if (e.keyCode === keys.SPACEBAR || e.keyCode === keys.ENTER) {
|
|
that.toolbar.userEvents.trigger('tap', { target: $(e.target) });
|
|
}
|
|
});
|
|
},
|
|
createMenuButtons: function () {
|
|
var options = this.options;
|
|
var items = options.menuButtons;
|
|
var item;
|
|
for (var i = 0; i < items.length; i++) {
|
|
item = new ToolBarButton($.extend({
|
|
mobile: options.mobile,
|
|
type: 'button',
|
|
click: options.click
|
|
}, items[i]), this.toolbar);
|
|
item.element.wrap('<li></li>').parent().appendTo(this.popupElement);
|
|
}
|
|
},
|
|
createPopup: function () {
|
|
var options = this.options;
|
|
var element = this.element;
|
|
this.popupElement.attr('id', options.id + '_optionlist').attr(KENDO_UID_ATTR, options.rootUid);
|
|
if (options.mobile) {
|
|
this.popupElement = actionSheetWrap(this.popupElement);
|
|
}
|
|
this.popup = this.popupElement.kendoPopup({
|
|
appendTo: options.mobile ? $(options.mobile).children('.km-pane') : null,
|
|
anchor: element,
|
|
isRtl: this.toolbar._isRtl,
|
|
copyAnchorStyles: false,
|
|
animation: options.animation,
|
|
open: adjustPopupWidth,
|
|
activate: function () {
|
|
this.element.find(':kendoFocusable').first().focus();
|
|
},
|
|
close: function () {
|
|
element.focus();
|
|
}
|
|
}).data('kendoPopup');
|
|
this.popup.element.on(CLICK, 'a.k-button', preventClick);
|
|
},
|
|
remove: function () {
|
|
this.popup.element.off(CLICK, 'a.k-button');
|
|
this.popup.destroy();
|
|
this.element.remove();
|
|
},
|
|
toggle: function () {
|
|
this.popup.toggle();
|
|
},
|
|
enable: function (isEnabled) {
|
|
if (isEnabled === undefined) {
|
|
isEnabled = true;
|
|
}
|
|
this.mainButton.enable(isEnabled);
|
|
this.options.enable = isEnabled;
|
|
},
|
|
focus: function () {
|
|
this.element.focus();
|
|
}
|
|
});
|
|
kendo.toolbar.ToolBarSplitButton = ToolBarSplitButton;
|
|
var OverflowSplitButton = Item.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<li class="' + SPLIT_BUTTON + '"></li>'), items = options.menuButtons, item;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.overflow = true;
|
|
this.mainButton = new OverflowButton($.extend({ isChild: true }, options));
|
|
this.mainButton.element.appendTo(element);
|
|
for (var i = 0; i < items.length; i++) {
|
|
item = new OverflowButton($.extend({
|
|
mobile: options.mobile,
|
|
isChild: true
|
|
}, items[i]), this.toolbar);
|
|
item.element.appendTo(element);
|
|
}
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.mainButton.main = true;
|
|
element.data({
|
|
type: 'splitButton',
|
|
splitButton: this
|
|
});
|
|
},
|
|
overflowHidden: function () {
|
|
this.element.addClass(OVERFLOW_HIDDEN);
|
|
}
|
|
});
|
|
kendo.toolbar.OverflowSplitButton = OverflowSplitButton;
|
|
kendo.toolbar.registerComponent('splitButton', ToolBarSplitButton, OverflowSplitButton);
|
|
var ToolBarSeparator = Item.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<div> </div>');
|
|
this.element = element;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
this.addIdAttr();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
element.addClass(SEPARATOR);
|
|
element.data({
|
|
type: 'separator',
|
|
separator: this
|
|
});
|
|
}
|
|
});
|
|
var OverflowSeparator = Item.extend({
|
|
init: function (options, toolbar) {
|
|
var element = this.element = $('<li> </li>');
|
|
this.element = element;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.overflow = true;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowIdAttr();
|
|
element.addClass(SEPARATOR);
|
|
element.data({
|
|
type: 'separator',
|
|
separator: this
|
|
});
|
|
},
|
|
overflowHidden: function () {
|
|
this.element.addClass(OVERFLOW_HIDDEN);
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('separator', ToolBarSeparator, OverflowSeparator);
|
|
var TemplateItem = Item.extend({
|
|
init: function (template, options, toolbar) {
|
|
var element = isFunction(template) ? template(options) : template;
|
|
if (!(element instanceof jQuery)) {
|
|
element = $('<div></div>').html(element);
|
|
} else {
|
|
element = element.wrap('<div></div>').parent();
|
|
}
|
|
this.element = element;
|
|
this.options = options;
|
|
this.options.type = 'template';
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addIdAttr();
|
|
this.addOverflowAttr();
|
|
element.data({
|
|
type: 'template',
|
|
template: this
|
|
});
|
|
}
|
|
});
|
|
kendo.toolbar.TemplateItem = TemplateItem;
|
|
var OverflowTemplateItem = Item.extend({
|
|
init: function (template, options, toolbar) {
|
|
var element = isFunction(template) ? $(template(options)) : $(template);
|
|
if (!(element instanceof jQuery)) {
|
|
element = $('<li></li>').html(element);
|
|
} else {
|
|
element = element.wrap('<li></li>').parent();
|
|
}
|
|
this.element = element;
|
|
this.options = options;
|
|
this.options.type = 'template';
|
|
this.toolbar = toolbar;
|
|
this.overflow = true;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowIdAttr();
|
|
this.addOverflowAttr();
|
|
element.data({
|
|
type: 'template',
|
|
template: this
|
|
});
|
|
},
|
|
overflowHidden: function () {
|
|
this.element.addClass(OVERFLOW_HIDDEN);
|
|
}
|
|
});
|
|
kendo.toolbar.OverflowTemplateItem = OverflowTemplateItem;
|
|
function adjustPopupWidth() {
|
|
var anchor = this.options.anchor, computedWidth = anchor.outerWidth(), width;
|
|
kendo.wrap(this.element).addClass('k-split-wrapper');
|
|
if (this.element.css('box-sizing') !== 'border-box') {
|
|
width = computedWidth - (this.element.outerWidth() - this.element.width());
|
|
} else {
|
|
width = computedWidth;
|
|
}
|
|
this.element.css({
|
|
fontFamily: anchor.css('font-family'),
|
|
'min-width': width
|
|
});
|
|
}
|
|
function toggleActive(e) {
|
|
if (!e.target.is('.k-toggle-button')) {
|
|
e.target.toggleClass(STATE_ACTIVE, e.type == 'press');
|
|
}
|
|
}
|
|
function actionSheetWrap(element) {
|
|
element = $(element);
|
|
return element.hasClass('km-actionsheet') ? element.closest('.km-popup-wrapper') : element.addClass('km-widget km-actionsheet').wrap('<div class="km-actionsheet-wrapper km-actionsheet-tablet km-widget km-popup"></div>').parent().wrap('<div class="km-popup-wrapper k-popup"></div>').parent();
|
|
}
|
|
function preventClick(e) {
|
|
e.preventDefault();
|
|
}
|
|
function findFocusableSibling(element, dir) {
|
|
var getSibling = dir === 'next' ? $.fn.next : $.fn.prev;
|
|
var getter = dir === 'next' ? $.fn.first : $.fn.last;
|
|
var candidate = getSibling.call(element);
|
|
if (candidate.is(':kendoFocusable') || !candidate.length) {
|
|
return candidate;
|
|
}
|
|
if (candidate.find(':kendoFocusable').length) {
|
|
return getter.call(candidate.find(':kendoFocusable'));
|
|
}
|
|
return findFocusableSibling(candidate, dir);
|
|
}
|
|
var Group = Class.extend({
|
|
init: function (name) {
|
|
this.name = name;
|
|
this.buttons = [];
|
|
},
|
|
add: function (button) {
|
|
this.buttons[this.buttons.length] = button;
|
|
},
|
|
remove: function (button) {
|
|
var index = $.inArray(button, this.buttons);
|
|
this.buttons.splice(index, 1);
|
|
},
|
|
select: function (button) {
|
|
var tmp;
|
|
for (var i = 0; i < this.buttons.length; i++) {
|
|
tmp = this.buttons[i];
|
|
tmp.select(false);
|
|
}
|
|
button.select(true);
|
|
if (button.twin()) {
|
|
button.twin().select(true);
|
|
}
|
|
}
|
|
});
|
|
var ToolBar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.wrapper = that.element;
|
|
element.addClass(TOOLBAR + ' k-widget');
|
|
this.uid = kendo.guid();
|
|
this._isRtl = kendo.support.isRtl(element);
|
|
this._groups = {};
|
|
element.attr(KENDO_UID_ATTR, this.uid);
|
|
that.isMobile = typeof options.mobile === 'boolean' ? options.mobile : that.element.closest('.km-root')[0];
|
|
that.animation = that.isMobile ? { open: { effects: 'fade' } } : {};
|
|
if (that.isMobile) {
|
|
element.addClass('km-widget');
|
|
ICON = 'km-icon';
|
|
ICON_PREFIX = 'km-';
|
|
BUTTON = 'km-button';
|
|
BUTTON_GROUP = 'km-buttongroup km-widget';
|
|
STATE_ACTIVE = 'km-state-active';
|
|
STATE_DISABLED = 'km-state-disabled';
|
|
}
|
|
if (options.resizable) {
|
|
that._renderOverflow();
|
|
element.addClass(RESIZABLE_TOOLBAR);
|
|
that.overflowUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: 5,
|
|
allowSelection: true,
|
|
filter: '.' + OVERFLOW_ANCHOR,
|
|
tap: proxy(that._toggleOverflow, that)
|
|
});
|
|
that._resizeHandler = kendo.onResize(function () {
|
|
that.resize();
|
|
});
|
|
} else {
|
|
that.popup = { element: $([]) };
|
|
}
|
|
if (options.items && options.items.length) {
|
|
for (var i = 0; i < options.items.length; i++) {
|
|
that.add(options.items[i]);
|
|
}
|
|
}
|
|
that.userEvents = new kendo.UserEvents(document, {
|
|
threshold: 5,
|
|
allowSelection: true,
|
|
filter: '[' + KENDO_UID_ATTR + '=' + this.uid + '] a.' + BUTTON + ', ' + '[' + KENDO_UID_ATTR + '=' + this.uid + '] .' + OVERFLOW_BUTTON,
|
|
tap: proxy(that._buttonClick, that),
|
|
press: toggleActive,
|
|
release: toggleActive
|
|
});
|
|
that.element.on(CLICK, 'a.k-button', preventClick);
|
|
that._navigatable();
|
|
if (options.resizable) {
|
|
that.popup.element.on(CLICK, +'a.k-button', preventClick);
|
|
}
|
|
if (options.resizable) {
|
|
this._toggleOverflowAnchor();
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
CLICK,
|
|
TOGGLE,
|
|
OPEN,
|
|
CLOSE,
|
|
OVERFLOW_OPEN,
|
|
OVERFLOW_CLOSE
|
|
],
|
|
options: {
|
|
name: 'ToolBar',
|
|
items: [],
|
|
resizable: true,
|
|
mobile: null
|
|
},
|
|
addToGroup: function (button, groupName) {
|
|
var group;
|
|
if (!this._groups[groupName]) {
|
|
group = this._groups[groupName] = new Group();
|
|
} else {
|
|
group = this._groups[groupName];
|
|
}
|
|
group.add(button);
|
|
return group;
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.element.find('.' + SPLIT_BUTTON).each(function (idx, element) {
|
|
$(element).data('kendoPopup').destroy();
|
|
});
|
|
that.element.off(CLICK, 'a.k-button');
|
|
that.userEvents.destroy();
|
|
if (that.options.resizable) {
|
|
kendo.unbindResize(that._resizeHandler);
|
|
that.overflowUserEvents.destroy();
|
|
that.popup.element.off(CLICK, 'a.k-button');
|
|
that.popup.destroy();
|
|
}
|
|
Widget.fn.destroy.call(that);
|
|
},
|
|
add: function (options) {
|
|
var component = components[options.type], template = options.template, tool, that = this, itemClasses = that.isMobile ? '' : 'k-item k-state-default', overflowTemplate = options.overflowTemplate, overflowTool;
|
|
$.extend(options, {
|
|
uid: kendo.guid(),
|
|
animation: that.animation,
|
|
mobile: that.isMobile,
|
|
rootUid: that.uid
|
|
});
|
|
if (options.menuButtons) {
|
|
for (var i = 0; i < options.menuButtons.length; i++) {
|
|
$.extend(options.menuButtons[i], { uid: kendo.guid() });
|
|
}
|
|
}
|
|
if (template && !overflowTemplate) {
|
|
options.overflow = OVERFLOW_NEVER;
|
|
} else if (!options.overflow) {
|
|
options.overflow = OVERFLOW_AUTO;
|
|
}
|
|
if (options.overflow !== OVERFLOW_NEVER && that.options.resizable) {
|
|
if (overflowTemplate) {
|
|
overflowTool = new OverflowTemplateItem(overflowTemplate, options, that);
|
|
} else if (component) {
|
|
overflowTool = new component.overflow(options, that);
|
|
overflowTool.element.addClass(itemClasses);
|
|
}
|
|
if (overflowTool) {
|
|
if (options.overflow === OVERFLOW_AUTO) {
|
|
overflowTool.overflowHidden();
|
|
}
|
|
overflowTool.element.appendTo(that.popup.container);
|
|
that.angular('compile', function () {
|
|
return { elements: overflowTool.element.get() };
|
|
});
|
|
}
|
|
}
|
|
if (options.overflow !== OVERFLOW_ALWAYS) {
|
|
if (template) {
|
|
tool = new TemplateItem(template, options, that);
|
|
} else if (component) {
|
|
tool = new component.toolbar(options, that);
|
|
}
|
|
if (tool) {
|
|
if (that.options.resizable) {
|
|
tool.element.appendTo(that.element).css('visibility', 'hidden');
|
|
that._shrink(that.element.innerWidth());
|
|
tool.element.css('visibility', 'visible');
|
|
} else {
|
|
tool.element.appendTo(that.element);
|
|
}
|
|
that.angular('compile', function () {
|
|
return { elements: tool.element.get() };
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_getItem: function (candidate) {
|
|
var element, toolbarItem, overflowItem, isResizable = this.options.resizable, type;
|
|
element = this.element.find(candidate);
|
|
if (!element.length) {
|
|
element = $('.k-split-container[data-uid=' + this.uid + ']').find(candidate);
|
|
}
|
|
type = element.length ? element.data('type') : '';
|
|
toolbarItem = element.data(type);
|
|
if (toolbarItem) {
|
|
if (toolbarItem.main) {
|
|
element = element.parent('.' + SPLIT_BUTTON);
|
|
type = 'splitButton';
|
|
toolbarItem = element.data(type);
|
|
}
|
|
if (isResizable) {
|
|
overflowItem = toolbarItem.twin();
|
|
}
|
|
} else if (isResizable) {
|
|
element = this.popup.element.find(candidate);
|
|
type = element.length ? element.data('type') : '';
|
|
overflowItem = element.data(type);
|
|
if (overflowItem && overflowItem.main) {
|
|
element = element.parent('.' + SPLIT_BUTTON);
|
|
type = 'splitButton';
|
|
overflowItem = element.data(type);
|
|
}
|
|
}
|
|
return {
|
|
type: type,
|
|
toolbar: toolbarItem,
|
|
overflow: overflowItem
|
|
};
|
|
},
|
|
remove: function (candidate) {
|
|
var item = this._getItem(candidate);
|
|
if (item.toolbar) {
|
|
item.toolbar.remove();
|
|
}
|
|
if (item.overflow) {
|
|
item.overflow.remove();
|
|
}
|
|
this.resize(true);
|
|
},
|
|
hide: function (candidate) {
|
|
var item = this._getItem(candidate);
|
|
if (item.toolbar) {
|
|
if (item.toolbar.options.type === 'button' && item.toolbar.options.isChild) {
|
|
item.toolbar.hide();
|
|
item.toolbar.getParentGroup().refresh();
|
|
} else if (!item.toolbar.options.hidden) {
|
|
item.toolbar.hide();
|
|
}
|
|
}
|
|
if (item.overflow) {
|
|
if (item.overflow.options.type === 'button' && item.overflow.options.isChild) {
|
|
item.overflow.hide();
|
|
item.overflow.getParentGroup().refresh();
|
|
} else if (!item.toolbar.options.hidden) {
|
|
item.overflow.hide();
|
|
}
|
|
}
|
|
this.resize(true);
|
|
},
|
|
show: function (candidate) {
|
|
var item = this._getItem(candidate);
|
|
if (item.toolbar) {
|
|
if (item.toolbar.options.type === 'button' && item.toolbar.options.isChild) {
|
|
item.toolbar.show();
|
|
item.toolbar.getParentGroup().refresh();
|
|
} else if (item.toolbar.options.hidden) {
|
|
item.toolbar.show();
|
|
}
|
|
}
|
|
if (item.overflow) {
|
|
if (item.overflow.options.type === 'button' && item.overflow.options.isChild) {
|
|
item.toolbar.show();
|
|
item.overflow.getParentGroup().refresh();
|
|
} else if (item.overflow.options.hidden) {
|
|
item.overflow.show();
|
|
}
|
|
}
|
|
this.resize(true);
|
|
},
|
|
enable: function (element, enable) {
|
|
var item = this._getItem(element);
|
|
if (typeof enable == 'undefined') {
|
|
enable = true;
|
|
}
|
|
if (item.toolbar) {
|
|
item.toolbar.enable(enable);
|
|
}
|
|
if (item.overflow) {
|
|
item.overflow.enable(enable);
|
|
}
|
|
},
|
|
getSelectedFromGroup: function (groupName) {
|
|
return this.element.find('.' + TOGGLE_BUTTON + '[data-group=\'' + groupName + '\']').filter('.' + STATE_ACTIVE);
|
|
},
|
|
toggle: function (button, checked) {
|
|
var element = $(button), item = element.data('button');
|
|
if (item.options.togglable) {
|
|
if (checked === undefined) {
|
|
checked = true;
|
|
}
|
|
item.toggle(checked, true);
|
|
}
|
|
},
|
|
_renderOverflow: function () {
|
|
var that = this, overflowContainer = components.overflowContainer, isRtl = that._isRtl, horizontalDirection = isRtl ? 'left' : 'right';
|
|
that.overflowAnchor = $(components.overflowAnchor).addClass(BUTTON);
|
|
that.element.append(that.overflowAnchor);
|
|
if (that.isMobile) {
|
|
that.overflowAnchor.append('<span class="km-icon km-more"></span>');
|
|
overflowContainer = actionSheetWrap(overflowContainer);
|
|
} else {
|
|
that.overflowAnchor.append('<span class="k-icon k-i-arrow-s"></span>');
|
|
}
|
|
that.popup = new kendo.ui.Popup(overflowContainer, {
|
|
origin: 'bottom ' + horizontalDirection,
|
|
position: 'top ' + horizontalDirection,
|
|
anchor: that.overflowAnchor,
|
|
isRtl: isRtl,
|
|
animation: that.animation,
|
|
appendTo: that.isMobile ? $(that.isMobile).children('.km-pane') : null,
|
|
copyAnchorStyles: false,
|
|
open: function (e) {
|
|
var wrapper = kendo.wrap(that.popup.element).addClass('k-overflow-wrapper');
|
|
if (!that.isMobile) {
|
|
wrapper.css('margin-left', (isRtl ? -1 : 1) * ((wrapper.outerWidth() - wrapper.width()) / 2 + 1));
|
|
} else {
|
|
that.popup.container.css('max-height', parseFloat($('.km-content:visible').innerHeight()) - 15 + 'px');
|
|
}
|
|
if (that.trigger(OVERFLOW_OPEN)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
activate: function () {
|
|
this.element.find(':kendoFocusable').first().focus();
|
|
},
|
|
close: function (e) {
|
|
if (that.trigger(OVERFLOW_CLOSE)) {
|
|
e.preventDefault();
|
|
}
|
|
this.element.focus();
|
|
}
|
|
});
|
|
that.popup.element.on('keydown', '.' + BUTTON, function (e) {
|
|
var target = $(e.target), li = target.parent(), isComplexTool = li.is('.' + BUTTON_GROUP) || li.is('.' + SPLIT_BUTTON), element;
|
|
e.preventDefault();
|
|
if (e.keyCode === keys.ESC || e.keyCode === keys.TAB || e.altKey && e.keyCode === keys.UP) {
|
|
that._toggleOverflow();
|
|
that.overflowAnchor.focus();
|
|
} else if (e.keyCode === keys.DOWN) {
|
|
element = !isComplexTool || isComplexTool && target.is(':last-child') ? li : target;
|
|
findFocusableSibling(element, 'next').focus();
|
|
} else if (e.keyCode === keys.UP) {
|
|
element = !isComplexTool || isComplexTool && target.is(':first-child') ? li : target;
|
|
findFocusableSibling(element, 'prev').focus();
|
|
} else if (e.keyCode === keys.SPACEBAR || e.keyCode === keys.ENTER) {
|
|
that.userEvents.trigger('tap', { target: $(e.target) });
|
|
}
|
|
});
|
|
if (that.isMobile) {
|
|
that.popup.container = that.popup.element.find('.' + OVERFLOW_CONTAINER);
|
|
} else {
|
|
that.popup.container = that.popup.element;
|
|
}
|
|
that.popup.container.attr(KENDO_UID_ATTR, this.uid);
|
|
},
|
|
_toggleOverflowAnchor: function () {
|
|
var hasVisibleChildren = false;
|
|
if (this.options.mobile) {
|
|
hasVisibleChildren = this.popup.element.find('.' + OVERFLOW_CONTAINER).children(':not(.' + OVERFLOW_HIDDEN + ', .' + POPUP + ')').length > 0;
|
|
} else {
|
|
hasVisibleChildren = this.popup.element.children(':not(.' + OVERFLOW_HIDDEN + ', .' + POPUP + ')').length > 0;
|
|
}
|
|
if (hasVisibleChildren) {
|
|
this.overflowAnchor.css({
|
|
visibility: 'visible',
|
|
width: ''
|
|
});
|
|
} else {
|
|
this.overflowAnchor.css({
|
|
visibility: 'hidden',
|
|
width: '1px'
|
|
});
|
|
}
|
|
},
|
|
_buttonClick: function (e) {
|
|
var that = this, popup, target, item, splitContainer, isSplitButtonArrow = e.target.closest('.' + SPLIT_BUTTON_ARROW).length, handler, eventData, urlTarget;
|
|
e.preventDefault();
|
|
if (isSplitButtonArrow) {
|
|
that._toggle(e);
|
|
return;
|
|
}
|
|
target = $(e.target).closest('.' + BUTTON, that.element);
|
|
if (target.hasClass(OVERFLOW_ANCHOR)) {
|
|
return;
|
|
}
|
|
item = target.data('button');
|
|
if (!item && that.popup) {
|
|
target = $(e.target).closest('.' + OVERFLOW_BUTTON, that.popup.container);
|
|
item = target.parent('li').data('button');
|
|
}
|
|
if (!item || !item.options.enable) {
|
|
return;
|
|
}
|
|
if (item.options.togglable) {
|
|
handler = isFunction(item.toggleHandler) ? item.toggleHandler : null;
|
|
item.toggle(!item.options.selected, true);
|
|
eventData = {
|
|
target: target,
|
|
group: item.options.group,
|
|
checked: item.options.selected,
|
|
id: item.options.id
|
|
};
|
|
if (handler) {
|
|
handler.call(that, eventData);
|
|
}
|
|
that.trigger(TOGGLE, eventData);
|
|
} else {
|
|
handler = isFunction(item.clickHandler) ? item.clickHandler : null;
|
|
eventData = {
|
|
sender: that,
|
|
target: target,
|
|
id: item.options.id
|
|
};
|
|
if (handler) {
|
|
handler.call(that, eventData);
|
|
}
|
|
that.trigger(CLICK, eventData);
|
|
}
|
|
if (item.options.url) {
|
|
if (item.options.attributes && item.options.attributes.target) {
|
|
urlTarget = item.options.attributes.target;
|
|
}
|
|
window.open(item.options.url, urlTarget || '_self');
|
|
}
|
|
if (target.hasClass(OVERFLOW_BUTTON)) {
|
|
that.popup.close();
|
|
}
|
|
splitContainer = target.closest('.k-split-container');
|
|
if (splitContainer[0]) {
|
|
popup = splitContainer.data('kendoPopup');
|
|
(popup ? popup : splitContainer.parents('.km-popup-wrapper').data('kendoPopup')).close();
|
|
}
|
|
},
|
|
_navigatable: function () {
|
|
var that = this;
|
|
that.element.attr('tabindex', 0).focus(function () {
|
|
var element = $(this).find(':kendoFocusable:first');
|
|
if (element.is('.' + OVERFLOW_ANCHOR)) {
|
|
element = findFocusableSibling(element, 'next');
|
|
}
|
|
element[0].focus();
|
|
}).on('keydown', proxy(that._keydown, that));
|
|
},
|
|
_keydown: function (e) {
|
|
var target = $(e.target), keyCode = e.keyCode, items = this.element.children(':not(.k-separator):visible');
|
|
if (keyCode === keys.TAB) {
|
|
var element = target.parentsUntil(this.element).last(), lastHasFocus = false, firstHasFocus = false;
|
|
if (!element.length) {
|
|
element = target;
|
|
}
|
|
if (element.is('.' + OVERFLOW_ANCHOR)) {
|
|
if (e.shiftKey) {
|
|
e.preventDefault();
|
|
}
|
|
if (items.last().is(':kendoFocusable')) {
|
|
items.last().focus();
|
|
} else {
|
|
items.last().find(':kendoFocusable').last().focus();
|
|
}
|
|
}
|
|
if (!e.shiftKey && items.index(element) === items.length - 1) {
|
|
if (element.is('.' + BUTTON_GROUP)) {
|
|
lastHasFocus = target.is(':last-child');
|
|
} else {
|
|
lastHasFocus = true;
|
|
}
|
|
}
|
|
if (e.shiftKey && items.index(element) === 1) {
|
|
if (element.is('.' + BUTTON_GROUP)) {
|
|
firstHasFocus = target.is(':first-child');
|
|
} else {
|
|
firstHasFocus = true;
|
|
}
|
|
}
|
|
if (lastHasFocus && this.overflowAnchor && this.overflowAnchor.css('visibility') !== 'hidden') {
|
|
e.preventDefault();
|
|
this.overflowAnchor.focus();
|
|
}
|
|
if (firstHasFocus) {
|
|
e.preventDefault();
|
|
this.wrapper.prev(':kendoFocusable').focus();
|
|
}
|
|
}
|
|
if (e.altKey && keyCode === keys.DOWN) {
|
|
var splitButton = $(document.activeElement).data('splitButton');
|
|
var isOverflowAnchor = $(document.activeElement).is('.' + OVERFLOW_ANCHOR);
|
|
if (splitButton) {
|
|
splitButton.toggle();
|
|
} else if (isOverflowAnchor) {
|
|
this._toggleOverflow();
|
|
}
|
|
return;
|
|
}
|
|
if ((keyCode === keys.SPACEBAR || keyCode === keys.ENTER) && !target.is('input, checkbox')) {
|
|
e.preventDefault();
|
|
if (target.is('.' + SPLIT_BUTTON)) {
|
|
target = target.children().first();
|
|
}
|
|
this.userEvents.trigger('tap', { target: target });
|
|
return;
|
|
}
|
|
},
|
|
_toggle: function (e) {
|
|
var splitButton = $(e.target).closest('.' + SPLIT_BUTTON).data('splitButton'), isDefaultPrevented;
|
|
e.preventDefault();
|
|
if (!splitButton.options.enable) {
|
|
return;
|
|
}
|
|
if (splitButton.popup.element.is(':visible')) {
|
|
isDefaultPrevented = this.trigger(CLOSE, { target: splitButton.element });
|
|
} else {
|
|
isDefaultPrevented = this.trigger(OPEN, { target: splitButton.element });
|
|
}
|
|
if (!isDefaultPrevented) {
|
|
splitButton.toggle();
|
|
}
|
|
},
|
|
_toggleOverflow: function () {
|
|
this.popup.toggle();
|
|
},
|
|
_resize: function (e) {
|
|
var containerWidth = e.width;
|
|
if (!this.options.resizable) {
|
|
return;
|
|
}
|
|
this.popup.close();
|
|
this._shrink(containerWidth);
|
|
this._stretch(containerWidth);
|
|
this._markVisibles();
|
|
this._toggleOverflowAnchor();
|
|
},
|
|
_childrenWidth: function () {
|
|
var childrenWidth = 0;
|
|
this.element.children(':visible:not(\'.' + STATE_HIDDEN + '\')').each(function () {
|
|
childrenWidth += $(this).outerWidth(true);
|
|
});
|
|
return Math.ceil(childrenWidth);
|
|
},
|
|
_shrink: function (containerWidth) {
|
|
var commandElement, visibleCommands;
|
|
if (containerWidth < this._childrenWidth()) {
|
|
visibleCommands = this.element.children(':visible:not([data-overflow=\'never\'], .' + OVERFLOW_ANCHOR + ')');
|
|
for (var i = visibleCommands.length - 1; i >= 0; i--) {
|
|
commandElement = visibleCommands.eq(i);
|
|
if (containerWidth > this._childrenWidth()) {
|
|
break;
|
|
} else {
|
|
this._hideItem(commandElement);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_stretch: function (containerWidth) {
|
|
var commandElement, hiddenCommands;
|
|
if (containerWidth > this._childrenWidth()) {
|
|
hiddenCommands = this.element.children(':hidden:not(\'.' + STATE_HIDDEN + '\')');
|
|
for (var i = 0; i < hiddenCommands.length; i++) {
|
|
commandElement = hiddenCommands.eq(i);
|
|
if (containerWidth < this._childrenWidth() || !this._showItem(commandElement, containerWidth)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_hideItem: function (item) {
|
|
item.hide();
|
|
if (this.popup) {
|
|
this.popup.container.find('>li[data-uid=\'' + item.data('uid') + '\']').removeClass(OVERFLOW_HIDDEN);
|
|
}
|
|
},
|
|
_showItem: function (item, containerWidth) {
|
|
if (item.length && containerWidth > this._childrenWidth() + item.outerWidth(true)) {
|
|
item.show();
|
|
if (this.popup) {
|
|
this.popup.container.find('>li[data-uid=\'' + item.data('uid') + '\']').addClass(OVERFLOW_HIDDEN);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_markVisibles: function () {
|
|
var overflowItems = this.popup.container.children(), toolbarItems = this.element.children(':not(.k-overflow-anchor)'), visibleOverflowItems = overflowItems.filter(':not(.k-overflow-hidden)'), visibleToolbarItems = toolbarItems.filter(':visible');
|
|
overflowItems.add(toolbarItems).removeClass(FIRST_TOOLBAR_VISIBLE + ' ' + LAST_TOOLBAR_VISIBLE);
|
|
visibleOverflowItems.first().add(visibleToolbarItems.first()).addClass(FIRST_TOOLBAR_VISIBLE);
|
|
visibleOverflowItems.last().add(visibleToolbarItems.last()).addClass(LAST_TOOLBAR_VISIBLE);
|
|
}
|
|
});
|
|
kendo.ui.plugin(ToolBar);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.datetimepicker', [
|
|
'kendo.datepicker',
|
|
'kendo.timepicker'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'datetimepicker',
|
|
name: 'DateTimePicker',
|
|
category: 'web',
|
|
description: 'The DateTimePicker allows the end user to select a value from a calendar or a time drop-down list.',
|
|
depends: [
|
|
'datepicker',
|
|
'timepicker'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, TimeView = kendo.TimeView, parse = kendo.parseDate, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, calendar = kendo.calendar, isInRange = calendar.isInRange, restrictValue = calendar.restrictValue, isEqualDatePart = calendar.isEqualDatePart, getMilliseconds = TimeView.getMilliseconds, ui = kendo.ui, Widget = ui.Widget, OPEN = 'open', CLOSE = 'close', CHANGE = 'change', ns = '.kendoDateTimePicker', CLICK = 'click' + ns, DISABLED = 'disabled', READONLY = 'readonly', DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', STATEDISABLED = 'k-state-disabled', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, MOUSEDOWN = 'mousedown' + ns, MONTH = 'month', SPAN = '<span/>', ARIA_ACTIVEDESCENDANT = 'aria-activedescendant', ARIA_EXPANDED = 'aria-expanded', ARIA_HIDDEN = 'aria-hidden', ARIA_OWNS = 'aria-owns', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', DATE = Date, MIN = new DATE(1800, 0, 1), MAX = new DATE(2099, 11, 31), dateViewParams = { view: 'date' }, timeViewParams = { view: 'time' }, extend = $.extend;
|
|
var DateTimePicker = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, disabled;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
options = that.options;
|
|
options.disableDates = kendo.calendar.disabled(options.disableDates);
|
|
options.min = parse(element.attr('min')) || parse(options.min);
|
|
options.max = parse(element.attr('max')) || parse(options.max);
|
|
normalize(options);
|
|
that._initialOptions = extend({}, options);
|
|
that._wrapper();
|
|
that._views();
|
|
that._icons();
|
|
that._reset();
|
|
that._template();
|
|
try {
|
|
element[0].setAttribute('type', 'text');
|
|
} catch (e) {
|
|
element[0].type = 'text';
|
|
}
|
|
element.addClass('k-input').attr({
|
|
'role': 'combobox',
|
|
'aria-expanded': false
|
|
});
|
|
that._midnight = that._calculateMidnight(options.min, options.max);
|
|
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
|
|
if (disabled) {
|
|
that.enable(false);
|
|
} else {
|
|
that.readonly(element.is('[readonly]'));
|
|
}
|
|
that._old = that._update(options.value || that.element.val());
|
|
that._oldText = element.val();
|
|
kendo.notify(that);
|
|
},
|
|
options: {
|
|
name: 'DateTimePicker',
|
|
value: null,
|
|
format: '',
|
|
timeFormat: '',
|
|
culture: '',
|
|
parseFormats: [],
|
|
dates: [],
|
|
min: new DATE(MIN),
|
|
max: new DATE(MAX),
|
|
interval: 30,
|
|
height: 200,
|
|
footer: '',
|
|
start: MONTH,
|
|
depth: MONTH,
|
|
animation: {},
|
|
month: {},
|
|
ARIATemplate: 'Current focused date is #=kendo.toString(data.current, "d")#'
|
|
},
|
|
events: [
|
|
OPEN,
|
|
CLOSE,
|
|
CHANGE
|
|
],
|
|
setOptions: function (options) {
|
|
var that = this, value = that._value, min, max, currentValue;
|
|
Widget.fn.setOptions.call(that, options);
|
|
options = that.options;
|
|
options.min = min = parse(options.min);
|
|
options.max = max = parse(options.max);
|
|
normalize(options);
|
|
that._midnight = that._calculateMidnight(options.min, options.max);
|
|
currentValue = options.value || that._value || that.dateView._current;
|
|
if (min && !isEqualDatePart(min, currentValue)) {
|
|
min = new DATE(MIN);
|
|
}
|
|
if (max && !isEqualDatePart(max, currentValue)) {
|
|
max = new DATE(MAX);
|
|
}
|
|
that.dateView.setOptions(options);
|
|
that.timeView.setOptions(extend({}, options, {
|
|
format: options.timeFormat,
|
|
min: min,
|
|
max: max
|
|
}));
|
|
if (value) {
|
|
that.element.val(kendo.toString(value, options.format, options.culture));
|
|
that._updateARIA(value);
|
|
}
|
|
},
|
|
_editable: function (options) {
|
|
var that = this, element = that.element.off(ns), dateIcon = that._dateIcon.off(ns), timeIcon = that._timeIcon.off(ns), wrapper = that._inputWrapper.off(ns), readonly = options.readonly, disable = options.disable;
|
|
if (!readonly && !disable) {
|
|
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
|
|
element.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, $.proxy(that._keydown, that)).on('focus' + ns, function () {
|
|
that._inputWrapper.addClass(FOCUSED);
|
|
}).on('focusout' + ns, function () {
|
|
that._inputWrapper.removeClass(FOCUSED);
|
|
if (element.val() !== that._oldText) {
|
|
that._change(element.val());
|
|
}
|
|
that.close('date');
|
|
that.close('time');
|
|
});
|
|
dateIcon.on(MOUSEDOWN, preventDefault).on(CLICK, function () {
|
|
that.toggle('date');
|
|
if (!kendo.support.touch && element[0] !== activeElement()) {
|
|
element.focus();
|
|
}
|
|
});
|
|
timeIcon.on(MOUSEDOWN, preventDefault).on(CLICK, function () {
|
|
that.toggle('time');
|
|
if (!kendo.support.touch && element[0] !== activeElement()) {
|
|
element.focus();
|
|
}
|
|
});
|
|
} else {
|
|
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
|
|
element.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
|
|
}
|
|
},
|
|
readonly: function (readonly) {
|
|
this._editable({
|
|
readonly: readonly === undefined ? true : readonly,
|
|
disable: false
|
|
});
|
|
},
|
|
enable: function (enable) {
|
|
this._editable({
|
|
readonly: false,
|
|
disable: !(enable = enable === undefined ? true : enable)
|
|
});
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
that.dateView.destroy();
|
|
that.timeView.destroy();
|
|
that.element.off(ns);
|
|
that._dateIcon.off(ns);
|
|
that._timeIcon.off(ns);
|
|
that._inputWrapper.off(ns);
|
|
if (that._form) {
|
|
that._form.off('reset', that._resetHandler);
|
|
}
|
|
},
|
|
close: function (view) {
|
|
if (view !== 'time') {
|
|
view = 'date';
|
|
}
|
|
this[view + 'View'].close();
|
|
},
|
|
open: function (view) {
|
|
if (view !== 'time') {
|
|
view = 'date';
|
|
}
|
|
this[view + 'View'].open();
|
|
},
|
|
min: function (value) {
|
|
return this._option('min', value);
|
|
},
|
|
max: function (value) {
|
|
return this._option('max', value);
|
|
},
|
|
toggle: function (view) {
|
|
var secondView = 'timeView';
|
|
if (view !== 'time') {
|
|
view = 'date';
|
|
} else {
|
|
secondView = 'dateView';
|
|
}
|
|
this[view + 'View'].toggle();
|
|
this[secondView].close();
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
that._old = that._update(value);
|
|
if (that._old === null) {
|
|
that.element.val('');
|
|
}
|
|
that._oldText = that.element.val();
|
|
},
|
|
_change: function (value) {
|
|
var that = this, oldValue = that.element.val(), dateChanged;
|
|
value = that._update(value);
|
|
dateChanged = +that._old != +value;
|
|
var valueUpdated = dateChanged && !that._typing;
|
|
var textFormatted = oldValue !== that.element.val();
|
|
if (valueUpdated || textFormatted) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
if (dateChanged) {
|
|
that._old = value;
|
|
that._oldText = that.element.val();
|
|
that.trigger(CHANGE);
|
|
}
|
|
that._typing = false;
|
|
},
|
|
_option: function (option, value) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var timeView = that.timeView;
|
|
var timeViewOptions = timeView.options;
|
|
var current = that._value || that._old;
|
|
var minDateEqual;
|
|
var maxDateEqual;
|
|
if (value === undefined) {
|
|
return options[option];
|
|
}
|
|
value = parse(value, options.parseFormats, options.culture);
|
|
if (!value) {
|
|
return;
|
|
}
|
|
if (options.min.getTime() === options.max.getTime()) {
|
|
timeViewOptions.dates = [];
|
|
}
|
|
options[option] = new DATE(value.getTime());
|
|
that.dateView[option](value);
|
|
that._midnight = that._calculateMidnight(options.min, options.max);
|
|
if (current) {
|
|
minDateEqual = isEqualDatePart(options.min, current);
|
|
maxDateEqual = isEqualDatePart(options.max, current);
|
|
}
|
|
if (minDateEqual || maxDateEqual) {
|
|
timeViewOptions[option] = value;
|
|
if (minDateEqual && !maxDateEqual) {
|
|
timeViewOptions.max = lastTimeOption(options.interval);
|
|
}
|
|
if (maxDateEqual) {
|
|
if (that._midnight) {
|
|
timeView.dataBind([MAX]);
|
|
return;
|
|
} else if (!minDateEqual) {
|
|
timeViewOptions.min = MIN;
|
|
}
|
|
}
|
|
} else {
|
|
timeViewOptions.max = MAX;
|
|
timeViewOptions.min = MIN;
|
|
}
|
|
timeView.bind();
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
|
|
},
|
|
_update: function (value) {
|
|
var that = this, options = that.options, min = options.min, max = options.max, dates = options.dates, timeView = that.timeView, current = that._value, date = parse(value, options.parseFormats, options.culture), isSameType = date === null && current === null || date instanceof Date && current instanceof Date, rebind, timeViewOptions, old, skip, formattedValue;
|
|
if (options.disableDates && options.disableDates(date)) {
|
|
date = null;
|
|
if (!that._old) {
|
|
value = null;
|
|
}
|
|
}
|
|
if (+date === +current && isSameType) {
|
|
formattedValue = kendo.toString(date, options.format, options.culture);
|
|
if (formattedValue !== value) {
|
|
that.element.val(date === null ? value : formattedValue);
|
|
if (value instanceof String) {
|
|
that.element.trigger(CHANGE);
|
|
}
|
|
}
|
|
return date;
|
|
}
|
|
if (date !== null && isEqualDatePart(date, min)) {
|
|
date = restrictValue(date, min, max);
|
|
} else if (!isInRange(date, min, max)) {
|
|
date = null;
|
|
}
|
|
that._value = date;
|
|
timeView.value(date);
|
|
that.dateView.value(date);
|
|
if (date) {
|
|
old = that._old;
|
|
timeViewOptions = timeView.options;
|
|
if (dates[0]) {
|
|
dates = $.grep(dates, function (d) {
|
|
return isEqualDatePart(date, d);
|
|
});
|
|
if (dates[0]) {
|
|
timeView.dataBind(dates);
|
|
skip = true;
|
|
}
|
|
}
|
|
if (!skip) {
|
|
if (isEqualDatePart(date, min)) {
|
|
timeViewOptions.min = min;
|
|
timeViewOptions.max = lastTimeOption(options.interval);
|
|
rebind = true;
|
|
}
|
|
if (isEqualDatePart(date, max)) {
|
|
if (that._midnight) {
|
|
timeView.dataBind([MAX]);
|
|
skip = true;
|
|
} else {
|
|
timeViewOptions.max = max;
|
|
if (!rebind) {
|
|
timeViewOptions.min = MIN;
|
|
}
|
|
rebind = true;
|
|
}
|
|
}
|
|
}
|
|
if (!skip && (!old && rebind || old && !isEqualDatePart(old, date))) {
|
|
if (!rebind) {
|
|
timeViewOptions.max = MAX;
|
|
timeViewOptions.min = MIN;
|
|
}
|
|
timeView.bind();
|
|
}
|
|
}
|
|
that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
|
|
that._updateARIA(date);
|
|
return date;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, dateView = that.dateView, timeView = that.timeView, value = that.element.val(), isDateViewVisible = dateView.popup.visible();
|
|
if (e.altKey && e.keyCode === kendo.keys.DOWN) {
|
|
that.toggle(isDateViewVisible ? 'time' : 'date');
|
|
} else if (isDateViewVisible) {
|
|
dateView.move(e);
|
|
that._updateARIA(dateView._current);
|
|
} else if (timeView.popup.visible()) {
|
|
timeView.move(e);
|
|
} else if (e.keyCode === kendo.keys.ENTER && value !== that._oldText) {
|
|
that._change(value);
|
|
} else {
|
|
that._typing = true;
|
|
}
|
|
},
|
|
_views: function () {
|
|
var that = this, element = that.element, options = that.options, id = element.attr('id'), dateView, timeView, div, ul, msMin, date;
|
|
that.dateView = dateView = new kendo.DateView(extend({}, options, {
|
|
id: id,
|
|
anchor: that.wrapper,
|
|
change: function () {
|
|
var value = dateView.calendar.value(), msValue = +value, msMin = +options.min, msMax = +options.max, current, adjustedDate;
|
|
if (msValue === msMin || msValue === msMax) {
|
|
current = msValue === msMin ? msMin : msMax;
|
|
current = new DATE(that._value || current);
|
|
current.setFullYear(value.getFullYear(), value.getMonth(), value.getDate());
|
|
if (isInRange(current, msMin, msMax)) {
|
|
value = current;
|
|
}
|
|
}
|
|
if (that._value) {
|
|
adjustedDate = kendo.date.setHours(new Date(value), that._value);
|
|
if (isInRange(adjustedDate, msMin, msMax)) {
|
|
value = adjustedDate;
|
|
}
|
|
}
|
|
that._change(value);
|
|
that.close('date');
|
|
},
|
|
close: function (e) {
|
|
if (that.trigger(CLOSE, dateViewParams)) {
|
|
e.preventDefault();
|
|
} else {
|
|
element.attr(ARIA_EXPANDED, false);
|
|
div.attr(ARIA_HIDDEN, true);
|
|
if (!timeView.popup.visible()) {
|
|
element.removeAttr(ARIA_OWNS);
|
|
}
|
|
}
|
|
},
|
|
open: function (e) {
|
|
if (that.trigger(OPEN, dateViewParams)) {
|
|
e.preventDefault();
|
|
} else {
|
|
if (element.val() !== that._oldText) {
|
|
date = parse(element.val(), options.parseFormats, options.culture);
|
|
that.dateView[date ? 'current' : 'value'](date);
|
|
}
|
|
div.attr(ARIA_HIDDEN, false);
|
|
element.attr(ARIA_EXPANDED, true).attr(ARIA_OWNS, dateView._dateViewID);
|
|
that._updateARIA(date);
|
|
}
|
|
}
|
|
}));
|
|
div = dateView.div;
|
|
msMin = options.min.getTime();
|
|
that.timeView = timeView = new TimeView({
|
|
id: id,
|
|
value: options.value,
|
|
anchor: that.wrapper,
|
|
animation: options.animation,
|
|
format: options.timeFormat,
|
|
culture: options.culture,
|
|
height: options.height,
|
|
interval: options.interval,
|
|
min: new DATE(MIN),
|
|
max: new DATE(MAX),
|
|
dates: msMin === options.max.getTime() ? [new Date(msMin)] : [],
|
|
parseFormats: options.parseFormats,
|
|
change: function (value, trigger) {
|
|
value = timeView._parse(value);
|
|
if (value < options.min) {
|
|
value = new DATE(+options.min);
|
|
timeView.options.min = value;
|
|
} else if (value > options.max) {
|
|
value = new DATE(+options.max);
|
|
timeView.options.max = value;
|
|
}
|
|
if (trigger) {
|
|
that._timeSelected = true;
|
|
that._change(value);
|
|
} else {
|
|
element.val(kendo.toString(value, options.format, options.culture));
|
|
dateView.value(value);
|
|
that._updateARIA(value);
|
|
}
|
|
},
|
|
close: function (e) {
|
|
if (that.trigger(CLOSE, timeViewParams)) {
|
|
e.preventDefault();
|
|
} else {
|
|
ul.attr(ARIA_HIDDEN, true);
|
|
element.attr(ARIA_EXPANDED, false);
|
|
if (!dateView.popup.visible()) {
|
|
element.removeAttr(ARIA_OWNS);
|
|
}
|
|
}
|
|
},
|
|
open: function (e) {
|
|
timeView._adjustListWidth();
|
|
if (that.trigger(OPEN, timeViewParams)) {
|
|
e.preventDefault();
|
|
} else {
|
|
if (element.val() !== that._oldText) {
|
|
date = parse(element.val(), options.parseFormats, options.culture);
|
|
that.timeView.value(date);
|
|
}
|
|
ul.attr(ARIA_HIDDEN, false);
|
|
element.attr(ARIA_EXPANDED, true).attr(ARIA_OWNS, timeView._timeViewID);
|
|
timeView.options.active(timeView.current());
|
|
}
|
|
},
|
|
active: function (current) {
|
|
element.removeAttr(ARIA_ACTIVEDESCENDANT);
|
|
if (current) {
|
|
element.attr(ARIA_ACTIVEDESCENDANT, timeView._optionID);
|
|
}
|
|
}
|
|
});
|
|
ul = timeView.ul;
|
|
},
|
|
_icons: function () {
|
|
var that = this, element = that.element, icons;
|
|
icons = element.next('span.k-select');
|
|
if (!icons[0]) {
|
|
icons = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-calendar">select</span><span unselectable="on" class="k-icon k-i-clock">select</span></span>').insertAfter(element);
|
|
}
|
|
icons = icons.children();
|
|
that._dateIcon = icons.eq(0).attr({
|
|
'role': 'button',
|
|
'aria-controls': that.dateView._dateViewID
|
|
});
|
|
that._timeIcon = icons.eq(1).attr({
|
|
'role': 'button',
|
|
'aria-controls': that.timeView._timeViewID
|
|
});
|
|
},
|
|
_wrapper: function () {
|
|
var that = this, element = that.element, wrapper;
|
|
wrapper = element.parents('.k-datetimepicker');
|
|
if (!wrapper[0]) {
|
|
wrapper = element.wrap(SPAN).parent().addClass('k-picker-wrap k-state-default');
|
|
wrapper = wrapper.wrap(SPAN).parent();
|
|
}
|
|
wrapper[0].style.cssText = element[0].style.cssText;
|
|
element.css({
|
|
width: '100%',
|
|
height: element[0].style.height
|
|
});
|
|
that.wrapper = wrapper.addClass('k-widget k-datetimepicker k-header').addClass(element[0].className);
|
|
that._inputWrapper = $(wrapper[0].firstChild);
|
|
},
|
|
_reset: function () {
|
|
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
|
|
if (form[0]) {
|
|
that._resetHandler = function () {
|
|
that.value(element[0].defaultValue);
|
|
that.max(that._initialOptions.max);
|
|
that.min(that._initialOptions.min);
|
|
};
|
|
that._form = form.on('reset', that._resetHandler);
|
|
}
|
|
},
|
|
_template: function () {
|
|
this._ariaTemplate = kendo.template(this.options.ARIATemplate);
|
|
},
|
|
_calculateMidnight: function (min, max) {
|
|
return getMilliseconds(min) + getMilliseconds(max) === 0;
|
|
},
|
|
_updateARIA: function (date) {
|
|
var cell;
|
|
var that = this;
|
|
var calendar = that.dateView.calendar;
|
|
that.element.removeAttr(ARIA_ACTIVEDESCENDANT);
|
|
if (calendar) {
|
|
cell = calendar._cell;
|
|
cell.attr('aria-label', that._ariaTemplate({ current: date || calendar.current() }));
|
|
that.element.attr(ARIA_ACTIVEDESCENDANT, cell.attr('id'));
|
|
}
|
|
}
|
|
});
|
|
function lastTimeOption(interval) {
|
|
var date = new Date(2100, 0, 1);
|
|
date.setMinutes(-interval);
|
|
return date;
|
|
}
|
|
function preventDefault(e) {
|
|
e.preventDefault();
|
|
}
|
|
function normalize(options) {
|
|
var patterns = kendo.getCulture(options.culture).calendars.standard.patterns, parseFormats = !options.parseFormats.length, timeFormat;
|
|
options.format = extractFormat(options.format || patterns.g);
|
|
options.timeFormat = timeFormat = extractFormat(options.timeFormat || patterns.t);
|
|
kendo.DateView.normalize(options);
|
|
if (parseFormats) {
|
|
options.parseFormats.unshift('yyyy-MM-ddTHH:mm:ss');
|
|
}
|
|
if ($.inArray(timeFormat, options.parseFormats) === -1) {
|
|
options.parseFormats.splice(1, 0, timeFormat);
|
|
}
|
|
}
|
|
ui.plugin(DateTimePicker);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.splitter', ['kendo.resizable'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'splitter',
|
|
name: 'Splitter',
|
|
category: 'web',
|
|
description: 'The Splitter widget provides an easy way to create a dynamic layout of resizable and collapsible panes.',
|
|
depends: ['resizable']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, extend = $.extend, proxy = $.proxy, Widget = ui.Widget, pxUnitsRegex = /^\d+(\.\d+)?px$/i, percentageUnitsRegex = /^\d+(\.\d+)?%$/i, NS = '.kendoSplitter', EXPAND = 'expand', COLLAPSE = 'collapse', CONTENTLOAD = 'contentLoad', ERROR = 'error', RESIZE = 'resize', LAYOUTCHANGE = 'layoutChange', HORIZONTAL = 'horizontal', VERTICAL = 'vertical', MOUSEENTER = 'mouseenter', CLICK = 'click', PANE = 'pane', MOUSELEAVE = 'mouseleave', FOCUSED = 'k-state-focused', KPANE = 'k-' + PANE, PANECLASS = '.' + KPANE;
|
|
function isPercentageSize(size) {
|
|
return percentageUnitsRegex.test(size);
|
|
}
|
|
function isPixelSize(size) {
|
|
return pxUnitsRegex.test(size) || /^\d+$/.test(size);
|
|
}
|
|
function isFluid(size) {
|
|
return !isPercentageSize(size) && !isPixelSize(size);
|
|
}
|
|
function calculateSize(size, total) {
|
|
var output = parseInt(size, 10);
|
|
if (isPercentageSize(size)) {
|
|
output = Math.floor(output * total / 100);
|
|
}
|
|
return output;
|
|
}
|
|
function panePropertyAccessor(propertyName, triggersResize) {
|
|
return function (pane, value) {
|
|
var paneConfig = this.element.find(pane).data(PANE);
|
|
if (arguments.length == 1) {
|
|
return paneConfig[propertyName];
|
|
}
|
|
paneConfig[propertyName] = value;
|
|
if (triggersResize) {
|
|
var splitter = this.element.data('kendo' + this.options.name);
|
|
splitter.resize(true);
|
|
}
|
|
};
|
|
}
|
|
var Splitter = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, isHorizontal;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.wrapper = that.element;
|
|
isHorizontal = that.options.orientation.toLowerCase() != VERTICAL;
|
|
that.orientation = isHorizontal ? HORIZONTAL : VERTICAL;
|
|
that._dimension = isHorizontal ? 'width' : 'height';
|
|
that._keys = {
|
|
decrease: isHorizontal ? keys.LEFT : keys.UP,
|
|
increase: isHorizontal ? keys.RIGHT : keys.DOWN
|
|
};
|
|
that._resizeStep = 10;
|
|
that._marker = kendo.guid().substring(0, 8);
|
|
that._initPanes();
|
|
that.resizing = new PaneResizing(that);
|
|
that.element.triggerHandler('init' + NS);
|
|
},
|
|
events: [
|
|
EXPAND,
|
|
COLLAPSE,
|
|
CONTENTLOAD,
|
|
ERROR,
|
|
RESIZE,
|
|
LAYOUTCHANGE
|
|
],
|
|
_addOverlays: function () {
|
|
this._panes().append('<div class=\'k-splitter-overlay k-overlay\' />');
|
|
},
|
|
_removeOverlays: function () {
|
|
this._panes().children('.k-splitter-overlay').remove();
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this, orientation = that.options.orientation;
|
|
that.element.children('.k-splitbar-draggable-' + orientation).on('keydown' + NS, proxy(that._keydown, that)).on('mousedown' + NS, function (e) {
|
|
e.currentTarget.focus();
|
|
}).on('focus' + NS, function (e) {
|
|
$(e.currentTarget).addClass(FOCUSED);
|
|
}).on('blur' + NS, function (e) {
|
|
$(e.currentTarget).removeClass(FOCUSED);
|
|
if (that.resizing) {
|
|
that.resizing.end();
|
|
}
|
|
}).on(MOUSEENTER + NS, function () {
|
|
$(this).addClass('k-splitbar-' + that.orientation + '-hover');
|
|
}).on(MOUSELEAVE + NS, function () {
|
|
$(this).removeClass('k-splitbar-' + that.orientation + '-hover');
|
|
}).on('mousedown' + NS, proxy(that._addOverlays, that)).end().children('.k-splitbar').on('dblclick' + NS, proxy(that._togglePane, that)).children('.k-collapse-next, .k-collapse-prev').on(CLICK + NS, that._arrowClick(COLLAPSE)).end().children('.k-expand-next, .k-expand-prev').on(CLICK + NS, that._arrowClick(EXPAND)).end().end();
|
|
$(window).on('resize' + NS + that._marker, proxy(that.resize, that, false));
|
|
$(document).on('mouseup' + NS + that._marker, proxy(that._removeOverlays, that));
|
|
},
|
|
_detachEvents: function () {
|
|
var that = this;
|
|
that.element.children('.k-splitbar-draggable-' + that.orientation).off(NS).end().children('.k-splitbar').off('dblclick' + NS).children('.k-collapse-next, .k-collapse-prev, .k-expand-next, .k-expand-prev').off(NS);
|
|
$(window).off(NS + that._marker);
|
|
$(document).off(NS + that._marker);
|
|
},
|
|
options: {
|
|
name: 'Splitter',
|
|
orientation: HORIZONTAL,
|
|
panes: []
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this._detachEvents();
|
|
if (this.resizing) {
|
|
this.resizing.destroy();
|
|
}
|
|
kendo.destroy(this.element);
|
|
this.wrapper = this.element = null;
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, resizing = that.resizing, target = $(e.currentTarget), navigationKeys = that._keys, increase = key === navigationKeys.increase, decrease = key === navigationKeys.decrease, pane;
|
|
if (increase || decrease) {
|
|
if (e.ctrlKey) {
|
|
pane = target[decrease ? 'next' : 'prev']();
|
|
if (resizing && resizing.isResizing()) {
|
|
resizing.end();
|
|
}
|
|
if (!pane[that._dimension]()) {
|
|
that._triggerAction(EXPAND, pane);
|
|
} else {
|
|
that._triggerAction(COLLAPSE, target[decrease ? 'prev' : 'next']());
|
|
}
|
|
} else if (resizing) {
|
|
resizing.move((decrease ? -1 : 1) * that._resizeStep, target);
|
|
}
|
|
e.preventDefault();
|
|
} else if (key === keys.ENTER && resizing) {
|
|
resizing.end();
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_initPanes: function () {
|
|
var panesConfig = this.options.panes || [];
|
|
var that = this;
|
|
this.element.addClass('k-widget').addClass('k-splitter').children().each(function (i, pane) {
|
|
if (pane.nodeName.toLowerCase() != 'script') {
|
|
that._initPane(pane, panesConfig[i]);
|
|
}
|
|
});
|
|
this.resize();
|
|
},
|
|
_initPane: function (pane, config) {
|
|
pane = $(pane).attr('role', 'group').addClass(KPANE);
|
|
pane.data(PANE, config ? config : {}).toggleClass('k-scrollable', config ? config.scrollable !== false : true);
|
|
this.ajaxRequest(pane);
|
|
},
|
|
ajaxRequest: function (pane, url, data) {
|
|
var that = this, paneConfig;
|
|
pane = that.element.find(pane);
|
|
paneConfig = pane.data(PANE);
|
|
url = url || paneConfig.contentUrl;
|
|
if (url) {
|
|
pane.append('<span class=\'k-icon k-loading k-pane-loading\' />');
|
|
if (kendo.isLocalUrl(url)) {
|
|
jQuery.ajax({
|
|
url: url,
|
|
data: data || {},
|
|
type: 'GET',
|
|
dataType: 'html',
|
|
success: function (data) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: pane.get() };
|
|
});
|
|
pane.html(data);
|
|
that.angular('compile', function () {
|
|
return { elements: pane.get() };
|
|
});
|
|
that.trigger(CONTENTLOAD, { pane: pane[0] });
|
|
},
|
|
error: function (xhr, status) {
|
|
that.trigger(ERROR, {
|
|
pane: pane[0],
|
|
status: status,
|
|
xhr: xhr
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
pane.removeClass('k-scrollable').html('<iframe src=\'' + url + '\' frameborder=\'0\' class=\'k-content-frame\'>' + 'This page requires frames in order to show content' + '</iframe>');
|
|
}
|
|
}
|
|
},
|
|
_triggerAction: function (type, pane) {
|
|
if (!this.trigger(type, { pane: pane[0] })) {
|
|
this[type](pane[0]);
|
|
}
|
|
},
|
|
_togglePane: function (e) {
|
|
var that = this, target = $(e.target), arrow;
|
|
if (target.closest('.k-splitter')[0] != that.element[0]) {
|
|
return;
|
|
}
|
|
arrow = target.children('.k-icon:not(.k-resize-handle)');
|
|
if (arrow.length !== 1) {
|
|
return;
|
|
}
|
|
if (arrow.is('.k-collapse-prev')) {
|
|
that._triggerAction(COLLAPSE, target.prev());
|
|
} else if (arrow.is('.k-collapse-next')) {
|
|
that._triggerAction(COLLAPSE, target.next());
|
|
} else if (arrow.is('.k-expand-prev')) {
|
|
that._triggerAction(EXPAND, target.prev());
|
|
} else if (arrow.is('.k-expand-next')) {
|
|
that._triggerAction(EXPAND, target.next());
|
|
}
|
|
},
|
|
_arrowClick: function (arrowType) {
|
|
var that = this;
|
|
return function (e) {
|
|
var target = $(e.target), pane;
|
|
if (target.closest('.k-splitter')[0] != that.element[0]) {
|
|
return;
|
|
}
|
|
if (target.is('.k-' + arrowType + '-prev')) {
|
|
pane = target.parent().prev();
|
|
} else {
|
|
pane = target.parent().next();
|
|
}
|
|
that._triggerAction(arrowType, pane);
|
|
};
|
|
},
|
|
_updateSplitBar: function (splitbar, previousPane, nextPane) {
|
|
var catIconIf = function (iconType, condition) {
|
|
return condition ? '<div class=\'k-icon ' + iconType + '\' />' : '';
|
|
}, orientation = this.orientation, draggable = previousPane.resizable !== false && nextPane.resizable !== false, prevCollapsible = previousPane.collapsible, prevCollapsed = previousPane.collapsed, nextCollapsible = nextPane.collapsible, nextCollapsed = nextPane.collapsed;
|
|
splitbar.addClass('k-splitbar k-state-default k-splitbar-' + orientation).attr('role', 'separator').attr('aria-expanded', !(prevCollapsed || nextCollapsed)).removeClass('k-splitbar-' + orientation + '-hover').toggleClass('k-splitbar-draggable-' + orientation, draggable && !prevCollapsed && !nextCollapsed).toggleClass('k-splitbar-static-' + orientation, !draggable && !prevCollapsible && !nextCollapsible).html(catIconIf('k-collapse-prev', prevCollapsible && !prevCollapsed && !nextCollapsed) + catIconIf('k-expand-prev', prevCollapsible && prevCollapsed && !nextCollapsed) + catIconIf('k-resize-handle', draggable) + catIconIf('k-collapse-next', nextCollapsible && !nextCollapsed && !prevCollapsed) + catIconIf('k-expand-next', nextCollapsible && nextCollapsed && !prevCollapsed));
|
|
if (!draggable && !prevCollapsible && !nextCollapsible) {
|
|
splitbar.removeAttr('tabindex');
|
|
}
|
|
},
|
|
_updateSplitBars: function () {
|
|
var that = this;
|
|
this.element.children('.k-splitbar').each(function () {
|
|
var splitbar = $(this), previousPane = splitbar.prevAll(PANECLASS).first().data(PANE), nextPane = splitbar.nextAll(PANECLASS).first().data(PANE);
|
|
if (!nextPane) {
|
|
return;
|
|
}
|
|
that._updateSplitBar(splitbar, previousPane, nextPane);
|
|
});
|
|
},
|
|
_removeSplitBars: function () {
|
|
this.element.children('.k-splitbar').remove();
|
|
},
|
|
_panes: function () {
|
|
if (!this.element) {
|
|
return $();
|
|
}
|
|
return this.element.children(PANECLASS);
|
|
},
|
|
_resize: function () {
|
|
var that = this, element = that.element, panes = element.children(PANECLASS), isHorizontal = that.orientation == HORIZONTAL, splitBars = element.children('.k-splitbar'), splitBarsCount = splitBars.length, sizingProperty = isHorizontal ? 'width' : 'height', totalSize = element[sizingProperty]();
|
|
that.wrapper.addClass('k-splitter-resizing');
|
|
if (splitBarsCount === 0) {
|
|
splitBarsCount = panes.length - 1;
|
|
panes.slice(0, splitBarsCount).after('<div tabindex=\'0\' class=\'k-splitbar\' data-marker=\'' + that._marker + '\' />');
|
|
that._updateSplitBars();
|
|
splitBars = element.children('.k-splitbar');
|
|
} else {
|
|
that._updateSplitBars();
|
|
}
|
|
splitBars.each(function () {
|
|
totalSize -= this[isHorizontal ? 'offsetWidth' : 'offsetHeight'];
|
|
});
|
|
var sizedPanesWidth = 0, sizedPanesCount = 0, freeSizedPanes = $();
|
|
panes.css({
|
|
position: 'absolute',
|
|
top: 0
|
|
})[sizingProperty](function () {
|
|
var element = $(this), config = element.data(PANE) || {}, size;
|
|
element.removeClass('k-state-collapsed');
|
|
if (config.collapsed) {
|
|
size = config.collapsedSize ? calculateSize(config.collapsedSize, totalSize) : 0;
|
|
element.css('overflow', 'hidden').addClass('k-state-collapsed');
|
|
} else if (isFluid(config.size)) {
|
|
freeSizedPanes = freeSizedPanes.add(this);
|
|
return;
|
|
} else {
|
|
size = calculateSize(config.size, totalSize);
|
|
}
|
|
sizedPanesCount++;
|
|
sizedPanesWidth += size;
|
|
return size;
|
|
});
|
|
totalSize -= sizedPanesWidth;
|
|
var freeSizePanesCount = freeSizedPanes.length, freeSizePaneWidth = Math.floor(totalSize / freeSizePanesCount);
|
|
freeSizedPanes.slice(0, freeSizePanesCount - 1).css(sizingProperty, freeSizePaneWidth).end().eq(freeSizePanesCount - 1).css(sizingProperty, totalSize - (freeSizePanesCount - 1) * freeSizePaneWidth);
|
|
var sum = 0, alternateSizingProperty = isHorizontal ? 'height' : 'width', positioningProperty = isHorizontal ? 'left' : 'top', sizingDomProperty = isHorizontal ? 'offsetWidth' : 'offsetHeight';
|
|
if (freeSizePanesCount === 0) {
|
|
var lastNonCollapsedPane = panes.filter(function () {
|
|
return !($(this).data(PANE) || {}).collapsed;
|
|
}).last();
|
|
lastNonCollapsedPane[sizingProperty](totalSize + lastNonCollapsedPane[0][sizingDomProperty]);
|
|
}
|
|
element.children().css(alternateSizingProperty, element[alternateSizingProperty]()).each(function (i, child) {
|
|
if (child.tagName.toLowerCase() != 'script') {
|
|
child.style[positioningProperty] = Math.floor(sum) + 'px';
|
|
sum += child[sizingDomProperty];
|
|
}
|
|
});
|
|
that._detachEvents();
|
|
that._attachEvents();
|
|
that.wrapper.removeClass('k-splitter-resizing');
|
|
kendo.resize(panes);
|
|
that.trigger(LAYOUTCHANGE);
|
|
},
|
|
toggle: function (pane, expand) {
|
|
var that = this, paneConfig;
|
|
pane = that.element.find(pane);
|
|
paneConfig = pane.data(PANE);
|
|
if (!expand && !paneConfig.collapsible) {
|
|
return;
|
|
}
|
|
if (arguments.length == 1) {
|
|
expand = paneConfig.collapsed === undefined ? false : paneConfig.collapsed;
|
|
}
|
|
paneConfig.collapsed = !expand;
|
|
if (paneConfig.collapsed) {
|
|
pane.css('overflow', 'hidden');
|
|
} else {
|
|
pane.css('overflow', '');
|
|
}
|
|
that.resize(true);
|
|
},
|
|
collapse: function (pane) {
|
|
this.toggle(pane, false);
|
|
},
|
|
expand: function (pane) {
|
|
this.toggle(pane, true);
|
|
},
|
|
_addPane: function (config, idx, paneElement) {
|
|
var that = this;
|
|
if (paneElement.length) {
|
|
that.options.panes.splice(idx, 0, config);
|
|
that._initPane(paneElement, config);
|
|
that._removeSplitBars();
|
|
that.resize(true);
|
|
}
|
|
return paneElement;
|
|
},
|
|
append: function (config) {
|
|
config = config || {};
|
|
var that = this, paneElement = $('<div />').appendTo(that.element);
|
|
return that._addPane(config, that.options.panes.length, paneElement);
|
|
},
|
|
insertBefore: function (config, referencePane) {
|
|
referencePane = $(referencePane);
|
|
config = config || {};
|
|
var that = this, idx = that.wrapper.children('.k-pane').index(referencePane), paneElement = $('<div />').insertBefore($(referencePane));
|
|
return that._addPane(config, idx, paneElement);
|
|
},
|
|
insertAfter: function (config, referencePane) {
|
|
referencePane = $(referencePane);
|
|
config = config || {};
|
|
var that = this, idx = that.wrapper.children('.k-pane').index(referencePane), paneElement = $('<div />').insertAfter($(referencePane));
|
|
return that._addPane(config, idx + 1, paneElement);
|
|
},
|
|
remove: function (pane) {
|
|
pane = $(pane);
|
|
var that = this;
|
|
if (pane.length) {
|
|
kendo.destroy(pane);
|
|
pane.each(function (idx, element) {
|
|
that.options.panes.splice(that.wrapper.children('.k-pane').index(element), 1);
|
|
$(element).remove();
|
|
});
|
|
that._removeSplitBars();
|
|
if (that.options.panes.length) {
|
|
that.resize(true);
|
|
}
|
|
}
|
|
return that;
|
|
},
|
|
size: panePropertyAccessor('size', true),
|
|
min: panePropertyAccessor('min'),
|
|
max: panePropertyAccessor('max')
|
|
});
|
|
ui.plugin(Splitter);
|
|
var verticalDefaults = {
|
|
sizingProperty: 'height',
|
|
sizingDomProperty: 'offsetHeight',
|
|
alternateSizingProperty: 'width',
|
|
positioningProperty: 'top',
|
|
mousePositioningProperty: 'pageY'
|
|
};
|
|
var horizontalDefaults = {
|
|
sizingProperty: 'width',
|
|
sizingDomProperty: 'offsetWidth',
|
|
alternateSizingProperty: 'height',
|
|
positioningProperty: 'left',
|
|
mousePositioningProperty: 'pageX'
|
|
};
|
|
function PaneResizing(splitter) {
|
|
var that = this, orientation = splitter.orientation;
|
|
that.owner = splitter;
|
|
that._element = splitter.element;
|
|
that.orientation = orientation;
|
|
extend(that, orientation === HORIZONTAL ? horizontalDefaults : verticalDefaults);
|
|
that._resizable = new kendo.ui.Resizable(splitter.element, {
|
|
orientation: orientation,
|
|
handle: '.k-splitbar-draggable-' + orientation + '[data-marker=' + splitter._marker + ']',
|
|
hint: proxy(that._createHint, that),
|
|
start: proxy(that._start, that),
|
|
max: proxy(that._max, that),
|
|
min: proxy(that._min, that),
|
|
invalidClass: 'k-restricted-size-' + orientation,
|
|
resizeend: proxy(that._stop, that)
|
|
});
|
|
}
|
|
PaneResizing.prototype = {
|
|
press: function (target) {
|
|
this._resizable.press(target);
|
|
},
|
|
move: function (delta, target) {
|
|
if (!this.pressed) {
|
|
this.press(target);
|
|
this.pressed = true;
|
|
}
|
|
if (!this._resizable.target) {
|
|
this._resizable.press(target);
|
|
}
|
|
this._resizable.move(delta);
|
|
},
|
|
end: function () {
|
|
this._resizable.end();
|
|
this.pressed = false;
|
|
},
|
|
destroy: function () {
|
|
this._resizable.destroy();
|
|
this._resizable = this._element = this.owner = null;
|
|
},
|
|
isResizing: function () {
|
|
return this._resizable.resizing;
|
|
},
|
|
_createHint: function (handle) {
|
|
var that = this;
|
|
return $('<div class=\'k-ghost-splitbar k-ghost-splitbar-' + that.orientation + ' k-state-default\' />').css(that.alternateSizingProperty, handle[that.alternateSizingProperty]());
|
|
},
|
|
_start: function (e) {
|
|
var that = this, splitbar = $(e.currentTarget), previousPane = splitbar.prev(), nextPane = splitbar.next(), previousPaneConfig = previousPane.data(PANE), nextPaneConfig = nextPane.data(PANE), prevBoundary = parseInt(previousPane[0].style[that.positioningProperty], 10), nextBoundary = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - splitbar[0][that.sizingDomProperty], totalSize = parseInt(that._element.css(that.sizingProperty), 10), toPx = function (value) {
|
|
var val = parseInt(value, 10);
|
|
return (isPixelSize(value) ? val : totalSize * val / 100) || 0;
|
|
}, prevMinSize = toPx(previousPaneConfig.min), prevMaxSize = toPx(previousPaneConfig.max) || nextBoundary - prevBoundary, nextMinSize = toPx(nextPaneConfig.min), nextMaxSize = toPx(nextPaneConfig.max) || nextBoundary - prevBoundary;
|
|
that.previousPane = previousPane;
|
|
that.nextPane = nextPane;
|
|
that._maxPosition = Math.min(nextBoundary - nextMinSize, prevBoundary + prevMaxSize);
|
|
that._minPosition = Math.max(prevBoundary + prevMinSize, nextBoundary - nextMaxSize);
|
|
},
|
|
_max: function () {
|
|
return this._maxPosition;
|
|
},
|
|
_min: function () {
|
|
return this._minPosition;
|
|
},
|
|
_stop: function (e) {
|
|
var that = this, splitbar = $(e.currentTarget), owner = that.owner;
|
|
owner._panes().children('.k-splitter-overlay').remove();
|
|
if (e.keyCode !== kendo.keys.ESC) {
|
|
var ghostPosition = e.position, previousPane = splitbar.prev(), nextPane = splitbar.next(), previousPaneConfig = previousPane.data(PANE), nextPaneConfig = nextPane.data(PANE), previousPaneNewSize = ghostPosition - parseInt(previousPane[0].style[that.positioningProperty], 10), nextPaneNewSize = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - ghostPosition - splitbar[0][that.sizingDomProperty], fluidPanesCount = that._element.children(PANECLASS).filter(function () {
|
|
return isFluid($(this).data(PANE).size);
|
|
}).length;
|
|
if (!isFluid(previousPaneConfig.size) || fluidPanesCount > 1) {
|
|
if (isFluid(previousPaneConfig.size)) {
|
|
fluidPanesCount--;
|
|
}
|
|
previousPaneConfig.size = previousPaneNewSize + 'px';
|
|
}
|
|
if (!isFluid(nextPaneConfig.size) || fluidPanesCount > 1) {
|
|
nextPaneConfig.size = nextPaneNewSize + 'px';
|
|
}
|
|
owner.resize(true);
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.virtuallist', ['kendo.data'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'virtuallist',
|
|
name: 'VirtualList',
|
|
category: 'framework',
|
|
depends: ['data'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, DataBoundWidget = ui.DataBoundWidget, proxy = $.proxy, WRAPPER = 'k-virtual-wrap', VIRTUALLIST = 'k-virtual-list', CONTENT = 'k-virtual-content', LIST = 'k-list', HEADER = 'k-group-header', VIRTUALITEM = 'k-virtual-item', ITEM = 'k-item', HEIGHTCONTAINER = 'k-height-container', GROUPITEM = 'k-group', SELECTED = 'k-state-selected', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', CHANGE = 'change', CLICK = 'click', LISTBOUND = 'listBound', ITEMCHANGE = 'itemChange', ACTIVATE = 'activate', DEACTIVATE = 'deactivate', VIRTUAL_LIST_NS = '.VirtualList';
|
|
function lastFrom(array) {
|
|
return array[array.length - 1];
|
|
}
|
|
function toArray(value) {
|
|
return value instanceof Array ? value : [value];
|
|
}
|
|
function isPrimitive(dataItem) {
|
|
return typeof dataItem === 'string' || typeof dataItem === 'number' || typeof dataItem === 'boolean';
|
|
}
|
|
function getItemCount(screenHeight, listScreens, itemHeight) {
|
|
return Math.ceil(screenHeight * listScreens / itemHeight);
|
|
}
|
|
function appendChild(parent, className, tagName) {
|
|
var element = document.createElement(tagName || 'div');
|
|
if (className) {
|
|
element.className = className;
|
|
}
|
|
parent.appendChild(element);
|
|
return element;
|
|
}
|
|
function getDefaultItemHeight() {
|
|
var mockList = $('<div class="k-popup"><ul class="k-list"><li class="k-item"><li></ul></div>'), lineHeight;
|
|
mockList.css({
|
|
position: 'absolute',
|
|
left: '-200000px',
|
|
visibility: 'hidden'
|
|
});
|
|
mockList.appendTo(document.body);
|
|
lineHeight = parseFloat(kendo.getComputedStyles(mockList.find('.k-item')[0], ['line-height'])['line-height']);
|
|
mockList.remove();
|
|
return lineHeight;
|
|
}
|
|
function bufferSizes(screenHeight, listScreens, opposite) {
|
|
return {
|
|
down: screenHeight * opposite,
|
|
up: screenHeight * (listScreens - 1 - opposite)
|
|
};
|
|
}
|
|
function listValidator(options, screenHeight) {
|
|
var downThreshold = (options.listScreens - 1 - options.threshold) * screenHeight;
|
|
var upThreshold = options.threshold * screenHeight;
|
|
return function (list, scrollTop, lastScrollTop) {
|
|
if (scrollTop > lastScrollTop) {
|
|
return scrollTop - list.top < downThreshold;
|
|
} else {
|
|
return list.top === 0 || scrollTop - list.top > upThreshold;
|
|
}
|
|
};
|
|
}
|
|
function scrollCallback(element, callback) {
|
|
return function (force) {
|
|
return callback(element.scrollTop, force);
|
|
};
|
|
}
|
|
function syncList(reorder) {
|
|
return function (list, force) {
|
|
reorder(list.items, list.index, force);
|
|
return list;
|
|
};
|
|
}
|
|
function position(element, y) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 10) {
|
|
element.style.top = y + 'px';
|
|
} else {
|
|
element.style.webkitTransform = 'translateY(' + y + 'px)';
|
|
element.style.transform = 'translateY(' + y + 'px)';
|
|
}
|
|
}
|
|
function map2(callback, templates) {
|
|
return function (arr1, arr2) {
|
|
for (var i = 0, len = arr1.length; i < len; i++) {
|
|
callback(arr1[i], arr2[i], templates);
|
|
if (arr2[i].item) {
|
|
this.trigger(ITEMCHANGE, {
|
|
item: $(arr1[i]),
|
|
data: arr2[i].item,
|
|
ns: kendo.ui
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
function reshift(items, diff) {
|
|
var range;
|
|
if (diff > 0) {
|
|
range = items.splice(0, diff);
|
|
items.push.apply(items, range);
|
|
} else {
|
|
range = items.splice(diff, -diff);
|
|
items.unshift.apply(items, range);
|
|
}
|
|
return range;
|
|
}
|
|
function render(element, data, templates) {
|
|
var itemTemplate = templates.template;
|
|
element = $(element);
|
|
if (!data.item) {
|
|
itemTemplate = templates.placeholderTemplate;
|
|
}
|
|
this.angular('cleanup', function () {
|
|
return { elements: [element] };
|
|
});
|
|
element.attr('data-uid', data.item ? data.item.uid : '').attr('data-offset-index', data.index).html(itemTemplate(data.item || {}));
|
|
element.toggleClass(FOCUSED, data.current);
|
|
element.toggleClass(SELECTED, data.selected);
|
|
element.toggleClass('k-first', data.newGroup);
|
|
element.toggleClass('k-loading-item', !data.item);
|
|
if (data.index !== 0 && data.newGroup) {
|
|
$('<div class=' + GROUPITEM + '></div>').appendTo(element).html(templates.groupTemplate(data.group));
|
|
}
|
|
if (data.top !== undefined) {
|
|
position(element[0], data.top);
|
|
}
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: [element],
|
|
data: [{
|
|
dataItem: data.item,
|
|
group: data.group,
|
|
newGroup: data.newGroup
|
|
}]
|
|
};
|
|
});
|
|
}
|
|
function mapChangedItems(selected, itemsToMatch) {
|
|
var itemsLength = itemsToMatch.length;
|
|
var selectedLength = selected.length;
|
|
var dataItem;
|
|
var found;
|
|
var i, j;
|
|
var changed = [];
|
|
var unchanged = [];
|
|
if (selectedLength) {
|
|
for (i = 0; i < selectedLength; i++) {
|
|
dataItem = selected[i];
|
|
found = false;
|
|
for (j = 0; j < itemsLength; j++) {
|
|
if (dataItem === itemsToMatch[j]) {
|
|
found = true;
|
|
changed.push({
|
|
index: i,
|
|
item: dataItem
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
unchanged.push(dataItem);
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
changed: changed,
|
|
unchanged: unchanged
|
|
};
|
|
}
|
|
var VirtualList = DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
that.bound(false);
|
|
that._fetching = false;
|
|
Widget.fn.init.call(that, element, options);
|
|
if (!that.options.itemHeight) {
|
|
that.options.itemHeight = getDefaultItemHeight();
|
|
}
|
|
options = that.options;
|
|
that.element.addClass(LIST + ' ' + VIRTUALLIST).attr('role', 'listbox');
|
|
that.content = that.element.wrap('<div unselectable=\'on\' class=\'' + CONTENT + '\'></div>').parent();
|
|
that.wrapper = that.content.wrap('<div class=\'' + WRAPPER + '\'></div>').parent();
|
|
that.header = that.content.before('<div class=\'' + HEADER + '\'></div>').prev();
|
|
that.element.on('mouseenter' + VIRTUAL_LIST_NS, 'li:not(.k-loading-item)', function () {
|
|
$(this).addClass(HOVER);
|
|
}).on('mouseleave' + VIRTUAL_LIST_NS, 'li', function () {
|
|
$(this).removeClass(HOVER);
|
|
});
|
|
that._values = toArray(that.options.value);
|
|
that._selectedDataItems = [];
|
|
that._selectedIndexes = [];
|
|
that._rangesList = {};
|
|
that._activeDeferred = null;
|
|
that._promisesList = [];
|
|
that._optionID = kendo.guid();
|
|
that.setDataSource(options.dataSource);
|
|
that.content.on('scroll' + VIRTUAL_LIST_NS, kendo.throttle(function () {
|
|
that._renderItems();
|
|
that._triggerListBound();
|
|
}, options.delay));
|
|
that._selectable();
|
|
},
|
|
options: {
|
|
name: 'VirtualList',
|
|
autoBind: true,
|
|
delay: 100,
|
|
height: null,
|
|
listScreens: 4,
|
|
threshold: 0.5,
|
|
itemHeight: null,
|
|
oppositeBuffer: 1,
|
|
type: 'flat',
|
|
selectable: false,
|
|
value: [],
|
|
dataValueField: null,
|
|
template: '#:data#',
|
|
placeholderTemplate: 'loading...',
|
|
groupTemplate: '#:data#',
|
|
fixedGroupTemplate: 'fixed header template',
|
|
valueMapper: null
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
CLICK,
|
|
LISTBOUND,
|
|
ITEMCHANGE,
|
|
ACTIVATE,
|
|
DEACTIVATE
|
|
],
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
if (this._selectProxy && this.options.selectable === false) {
|
|
this.element.off(CLICK, '.' + VIRTUALITEM, this._selectProxy);
|
|
} else if (!this._selectProxy && this.options.selectable) {
|
|
this._selectable();
|
|
}
|
|
this.refresh();
|
|
},
|
|
items: function () {
|
|
return $(this._items);
|
|
},
|
|
destroy: function () {
|
|
this.wrapper.off(VIRTUAL_LIST_NS);
|
|
this.dataSource.unbind(CHANGE, this._refreshHandler);
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
setDataSource: function (source) {
|
|
var that = this;
|
|
var dataSource = source || {};
|
|
var value;
|
|
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
dataSource = kendo.data.DataSource.create(dataSource);
|
|
if (that.dataSource) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that._clean();
|
|
that.bound(false);
|
|
that._deferValueSet = true;
|
|
value = that.value();
|
|
that.value([]);
|
|
that.mute(function () {
|
|
that.value(value);
|
|
});
|
|
} else {
|
|
that._refreshHandler = $.proxy(that.refresh, that);
|
|
}
|
|
that.dataSource = dataSource.bind(CHANGE, that._refreshHandler);
|
|
that.setDSFilter(dataSource.filter());
|
|
if (dataSource.view().length !== 0) {
|
|
that.refresh();
|
|
} else if (that.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
skip: function () {
|
|
return this.dataSource.currentRangeStart();
|
|
},
|
|
_triggerListBound: function () {
|
|
var that = this;
|
|
var skip = that.skip();
|
|
if (that.bound() && !that._selectingValue && that._skip !== skip) {
|
|
that._skip = skip;
|
|
that.trigger(LISTBOUND);
|
|
}
|
|
},
|
|
_getValues: function (dataItems) {
|
|
var getter = this._valueGetter;
|
|
return $.map(dataItems, function (dataItem) {
|
|
return getter(dataItem);
|
|
});
|
|
},
|
|
refresh: function (e) {
|
|
var that = this;
|
|
var action = e && e.action;
|
|
var isItemChange = action === 'itemchange';
|
|
var filtered = this.isFiltered();
|
|
var result;
|
|
if (that._mute) {
|
|
return;
|
|
}
|
|
that._deferValueSet = false;
|
|
if (!that._fetching) {
|
|
if (filtered) {
|
|
that.focus(0);
|
|
}
|
|
that._createList();
|
|
if (!action && that._values.length && !filtered && !that.options.skipUpdateOnBind) {
|
|
that._selectingValue = true;
|
|
that.value(that._values, true).done(function () {
|
|
that.bound(true);
|
|
that._selectingValue = false;
|
|
that._triggerListBound();
|
|
});
|
|
} else {
|
|
that.bound(true);
|
|
that._triggerListBound();
|
|
}
|
|
} else {
|
|
if (that._renderItems) {
|
|
that._renderItems(true);
|
|
}
|
|
that._triggerListBound();
|
|
}
|
|
if (isItemChange || action === 'remove') {
|
|
result = mapChangedItems(that._selectedDataItems, e.items);
|
|
if (result.changed.length) {
|
|
if (isItemChange) {
|
|
that.trigger('selectedItemChange', { items: result.changed });
|
|
} else {
|
|
that.value(that._getValues(result.unchanged));
|
|
}
|
|
}
|
|
}
|
|
that._fetching = false;
|
|
},
|
|
removeAt: function (position) {
|
|
this._selectedIndexes.splice(position, 1);
|
|
this._values.splice(position, 1);
|
|
return {
|
|
position: position,
|
|
dataItem: this._selectedDataItems.splice(position, 1)[0]
|
|
};
|
|
},
|
|
setValue: function (value) {
|
|
this._values = toArray(value);
|
|
},
|
|
value: function (value, _forcePrefetch) {
|
|
var that = this;
|
|
if (value === undefined) {
|
|
return that._values.slice();
|
|
}
|
|
if (value === null) {
|
|
value = [];
|
|
}
|
|
value = toArray(value);
|
|
if (that.options.selectable === 'multiple' && that.select().length && value.length) {
|
|
that.select(-1);
|
|
}
|
|
if (!that._valueDeferred || that._valueDeferred.state() === 'resolved') {
|
|
that._valueDeferred = $.Deferred();
|
|
}
|
|
if (!value.length) {
|
|
that.select(-1);
|
|
}
|
|
that._values = value;
|
|
if (that.bound() && !that._mute && !that._deferValueSet || _forcePrefetch) {
|
|
that._prefetchByValue(value);
|
|
}
|
|
return that._valueDeferred;
|
|
},
|
|
_prefetchByValue: function (value) {
|
|
var that = this, dataView = that._dataView, valueGetter = that._valueGetter, item, match = false, forSelection = [];
|
|
for (var i = 0; i < value.length; i++) {
|
|
for (var idx = 0; idx < dataView.length; idx++) {
|
|
item = dataView[idx].item;
|
|
if (item) {
|
|
match = isPrimitive(item) ? value[i] === item : value[i] === valueGetter(item);
|
|
if (match) {
|
|
forSelection.push(dataView[idx].index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (forSelection.length === value.length) {
|
|
that._values = [];
|
|
that.select(forSelection);
|
|
return;
|
|
}
|
|
if (typeof that.options.valueMapper === 'function') {
|
|
that.options.valueMapper({
|
|
value: this.options.selectable === 'multiple' ? value : value[0],
|
|
success: function (indexes) {
|
|
that._values = [];
|
|
that._selectedIndexes = [];
|
|
that._selectedDataItems = [];
|
|
indexes = toArray(indexes);
|
|
if (!indexes.length) {
|
|
indexes = [-1];
|
|
}
|
|
that.select(indexes);
|
|
}
|
|
});
|
|
} else {
|
|
throw new Error('valueMapper is not provided');
|
|
}
|
|
},
|
|
deferredRange: function (index) {
|
|
var dataSource = this.dataSource;
|
|
var take = this.itemCount;
|
|
var ranges = this._rangesList;
|
|
var result = $.Deferred();
|
|
var defs = [];
|
|
var low = Math.floor(index / take) * take;
|
|
var high = Math.ceil(index / take) * take;
|
|
var pages = high === low ? [high] : [
|
|
low,
|
|
high
|
|
];
|
|
$.each(pages, function (_, skip) {
|
|
var end = skip + take;
|
|
var existingRange = ranges[skip];
|
|
var deferred;
|
|
if (!existingRange || existingRange.end !== end) {
|
|
deferred = $.Deferred();
|
|
ranges[skip] = {
|
|
end: end,
|
|
deferred: deferred
|
|
};
|
|
dataSource._multiplePrefetch(skip, take, function () {
|
|
deferred.resolve();
|
|
});
|
|
} else {
|
|
deferred = existingRange.deferred;
|
|
}
|
|
defs.push(deferred);
|
|
});
|
|
$.when.apply($, defs).then(function () {
|
|
result.resolve();
|
|
});
|
|
return result;
|
|
},
|
|
prefetch: function (indexes) {
|
|
var that = this, take = this.itemCount, isEmptyList = !that._promisesList.length;
|
|
if (!that._activeDeferred) {
|
|
that._activeDeferred = $.Deferred();
|
|
that._promisesList = [];
|
|
}
|
|
$.each(indexes, function (_, index) {
|
|
var rangeStart = Math.floor(index / take) * take;
|
|
that._promisesList.push(that.deferredRange(rangeStart));
|
|
});
|
|
if (isEmptyList) {
|
|
$.when.apply($, that._promisesList).done(function () {
|
|
that._activeDeferred.resolve();
|
|
that._activeDeferred = null;
|
|
that._promisesList = [];
|
|
});
|
|
}
|
|
return that._activeDeferred;
|
|
},
|
|
_findDataItem: function (index) {
|
|
var view = this.dataSource.view(), group;
|
|
if (this.options.type === 'group') {
|
|
for (var i = 0; i < view.length; i++) {
|
|
group = view[i].items;
|
|
if (group.length <= index) {
|
|
index = index - group.length;
|
|
} else {
|
|
return group[index];
|
|
}
|
|
}
|
|
}
|
|
return view[index];
|
|
},
|
|
selectedDataItems: function () {
|
|
return this._selectedDataItems.slice();
|
|
},
|
|
scrollTo: function (y) {
|
|
this.content.scrollTop(y);
|
|
},
|
|
scrollToIndex: function (index) {
|
|
this.scrollTo(index * this.options.itemHeight);
|
|
},
|
|
focus: function (candidate) {
|
|
var element, index, data, current, itemHeight = this.options.itemHeight, id = this._optionID, triggerEvent = true;
|
|
if (candidate === undefined) {
|
|
current = this.element.find('.' + FOCUSED);
|
|
return current.length ? current : null;
|
|
}
|
|
if (typeof candidate === 'function') {
|
|
data = this.dataSource.flatView();
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
if (candidate(data[idx])) {
|
|
candidate = idx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (candidate instanceof Array) {
|
|
candidate = lastFrom(candidate);
|
|
}
|
|
if (isNaN(candidate)) {
|
|
element = $(candidate);
|
|
index = parseInt($(element).attr('data-offset-index'), 10);
|
|
} else {
|
|
index = candidate;
|
|
element = this._getElementByIndex(index);
|
|
}
|
|
if (index === -1) {
|
|
this.element.find('.' + FOCUSED).removeClass(FOCUSED);
|
|
this._focusedIndex = undefined;
|
|
return;
|
|
}
|
|
if (element.length) {
|
|
if (element.hasClass(FOCUSED)) {
|
|
triggerEvent = false;
|
|
}
|
|
if (this._focusedIndex !== undefined) {
|
|
current = this._getElementByIndex(this._focusedIndex);
|
|
current.removeClass(FOCUSED).removeAttr('id');
|
|
if (triggerEvent) {
|
|
this.trigger(DEACTIVATE);
|
|
}
|
|
}
|
|
this._focusedIndex = index;
|
|
element.addClass(FOCUSED).attr('id', id);
|
|
var position = this._getElementLocation(index);
|
|
if (position === 'top') {
|
|
this.scrollTo(index * itemHeight);
|
|
} else if (position === 'bottom') {
|
|
this.scrollTo(index * itemHeight + itemHeight - this.screenHeight);
|
|
} else if (position === 'outScreen') {
|
|
this.scrollTo(index * itemHeight);
|
|
}
|
|
if (triggerEvent) {
|
|
this.trigger(ACTIVATE);
|
|
}
|
|
} else {
|
|
this._focusedIndex = index;
|
|
this.items().removeClass(FOCUSED);
|
|
this.scrollToIndex(index);
|
|
}
|
|
},
|
|
focusIndex: function () {
|
|
return this._focusedIndex;
|
|
},
|
|
focusFirst: function () {
|
|
this.scrollTo(0);
|
|
this.focus(0);
|
|
},
|
|
focusLast: function () {
|
|
var lastIndex = this.dataSource.total();
|
|
this.scrollTo(this.heightContainer.offsetHeight);
|
|
this.focus(lastIndex);
|
|
},
|
|
focusPrev: function () {
|
|
var index = this._focusedIndex;
|
|
var current;
|
|
if (!isNaN(index) && index > 0) {
|
|
index -= 1;
|
|
this.focus(index);
|
|
current = this.focus();
|
|
if (current && current.hasClass('k-loading-item')) {
|
|
index += 1;
|
|
this.focus(index);
|
|
}
|
|
return index;
|
|
} else {
|
|
index = this.dataSource.total() - 1;
|
|
this.focus(index);
|
|
return index;
|
|
}
|
|
},
|
|
focusNext: function () {
|
|
var index = this._focusedIndex;
|
|
var lastIndex = this.dataSource.total() - 1;
|
|
var current;
|
|
if (!isNaN(index) && index < lastIndex) {
|
|
index += 1;
|
|
this.focus(index);
|
|
current = this.focus();
|
|
if (current && current.hasClass('k-loading-item')) {
|
|
index -= 1;
|
|
this.focus(index);
|
|
}
|
|
return index;
|
|
} else {
|
|
index = 0;
|
|
this.focus(index);
|
|
return index;
|
|
}
|
|
},
|
|
_triggerChange: function (removed, added) {
|
|
removed = removed || [];
|
|
added = added || [];
|
|
if (removed.length || added.length) {
|
|
this.trigger(CHANGE, {
|
|
removed: removed,
|
|
added: added
|
|
});
|
|
}
|
|
},
|
|
select: function (candidate) {
|
|
var that = this, indices, singleSelection = that.options.selectable !== 'multiple', prefetchStarted = !!that._activeDeferred, filtered = this.isFiltered(), isAlreadySelected, deferred, result, removed = [];
|
|
if (candidate === undefined) {
|
|
return that._selectedIndexes.slice();
|
|
}
|
|
indices = that._getIndecies(candidate);
|
|
isAlreadySelected = singleSelection && !filtered && lastFrom(indices) === lastFrom(this._selectedIndexes);
|
|
removed = that._deselectCurrentValues(indices);
|
|
if (removed.length || !indices.length || isAlreadySelected) {
|
|
that._triggerChange(removed);
|
|
if (that._valueDeferred) {
|
|
that._valueDeferred.resolve();
|
|
}
|
|
return;
|
|
}
|
|
if (indices.length === 1 && indices[0] === -1) {
|
|
indices = [];
|
|
}
|
|
result = that._deselect(indices);
|
|
removed = result.removed;
|
|
indices = result.indices;
|
|
if (singleSelection) {
|
|
that._activeDeferred = null;
|
|
prefetchStarted = false;
|
|
if (indices.length) {
|
|
indices = [lastFrom(indices)];
|
|
}
|
|
}
|
|
var done = function () {
|
|
var added = that._select(indices);
|
|
that.focus(indices);
|
|
that._triggerChange(removed, added);
|
|
if (that._valueDeferred) {
|
|
that._valueDeferred.resolve();
|
|
}
|
|
};
|
|
deferred = that.prefetch(indices);
|
|
if (!prefetchStarted) {
|
|
if (deferred) {
|
|
deferred.done(done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
},
|
|
bound: function (bound) {
|
|
if (bound === undefined) {
|
|
return this._listCreated;
|
|
}
|
|
this._listCreated = bound;
|
|
},
|
|
mute: function (callback) {
|
|
this._mute = true;
|
|
proxy(callback(), this);
|
|
this._mute = false;
|
|
},
|
|
setDSFilter: function (filter) {
|
|
this._lastDSFilter = $.extend({}, filter);
|
|
},
|
|
isFiltered: function () {
|
|
if (!this._lastDSFilter) {
|
|
this.setDSFilter(this.dataSource.filter());
|
|
}
|
|
return !kendo.data.Query.compareFilters(this.dataSource.filter(), this._lastDSFilter);
|
|
},
|
|
skipUpdate: $.noop,
|
|
_getElementByIndex: function (index) {
|
|
return this.items().filter(function (idx, element) {
|
|
return index === parseInt($(element).attr('data-offset-index'), 10);
|
|
});
|
|
},
|
|
_clean: function () {
|
|
this.result = undefined;
|
|
this._lastScrollTop = undefined;
|
|
this._skip = undefined;
|
|
$(this.heightContainer).remove();
|
|
this.heightContainer = undefined;
|
|
this.element.empty();
|
|
},
|
|
_height: function () {
|
|
var hasData = !!this.dataSource.view().length, height = this.options.height, itemHeight = this.options.itemHeight, total = this.dataSource.total();
|
|
if (!hasData) {
|
|
height = 0;
|
|
} else if (height / itemHeight > total) {
|
|
height = total * itemHeight;
|
|
}
|
|
return height;
|
|
},
|
|
_screenHeight: function () {
|
|
var height = this._height(), content = this.content;
|
|
content.height(height);
|
|
this.screenHeight = height;
|
|
},
|
|
_getElementLocation: function (index) {
|
|
var scrollTop = this.content.scrollTop(), screenHeight = this.screenHeight, itemHeight = this.options.itemHeight, yPosition = index * itemHeight, yDownPostion = yPosition + itemHeight, screenEnd = scrollTop + screenHeight, position;
|
|
if (yPosition === scrollTop - itemHeight || yDownPostion > scrollTop && yPosition < scrollTop) {
|
|
position = 'top';
|
|
} else if (yPosition === screenEnd || yPosition < screenEnd && screenEnd < yDownPostion) {
|
|
position = 'bottom';
|
|
} else if (yPosition >= scrollTop && yPosition <= scrollTop + (screenHeight - itemHeight)) {
|
|
position = 'inScreen';
|
|
} else {
|
|
position = 'outScreen';
|
|
}
|
|
return position;
|
|
},
|
|
_templates: function () {
|
|
var templates = {
|
|
template: this.options.template,
|
|
placeholderTemplate: this.options.placeholderTemplate,
|
|
groupTemplate: this.options.groupTemplate,
|
|
fixedGroupTemplate: this.options.fixedGroupTemplate
|
|
};
|
|
for (var key in templates) {
|
|
if (typeof templates[key] !== 'function') {
|
|
templates[key] = kendo.template(templates[key]);
|
|
}
|
|
}
|
|
this.templates = templates;
|
|
},
|
|
_generateItems: function (element, count) {
|
|
var items = [], item, itemHeight = this.options.itemHeight + 'px';
|
|
while (count-- > 0) {
|
|
item = document.createElement('li');
|
|
item.tabIndex = -1;
|
|
item.className = VIRTUALITEM + ' ' + ITEM;
|
|
item.setAttribute('role', 'option');
|
|
item.style.height = itemHeight;
|
|
item.style.minHeight = itemHeight;
|
|
element.appendChild(item);
|
|
items.push(item);
|
|
}
|
|
return items;
|
|
},
|
|
_saveInitialRanges: function () {
|
|
var ranges = this.dataSource._ranges;
|
|
var deferred = $.Deferred();
|
|
deferred.resolve();
|
|
this._rangesList = {};
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
this._rangesList[ranges[i].start] = {
|
|
end: ranges[i].end,
|
|
deferred: deferred
|
|
};
|
|
}
|
|
},
|
|
_createList: function () {
|
|
var that = this, content = that.content.get(0), options = that.options, dataSource = that.dataSource;
|
|
if (that.bound()) {
|
|
that._clean();
|
|
}
|
|
that._saveInitialRanges();
|
|
that._screenHeight();
|
|
that._buildValueGetter();
|
|
that.itemCount = getItemCount(that.screenHeight, options.listScreens, options.itemHeight);
|
|
if (that.itemCount > dataSource.total()) {
|
|
that.itemCount = dataSource.total();
|
|
}
|
|
that._templates();
|
|
that._items = that._generateItems(that.element[0], that.itemCount);
|
|
that._setHeight(options.itemHeight * dataSource.total());
|
|
that.options.type = (dataSource.group() || []).length ? 'group' : 'flat';
|
|
if (that.options.type === 'flat') {
|
|
that.header.hide();
|
|
} else {
|
|
that.header.show();
|
|
}
|
|
that.getter = that._getter(function () {
|
|
that._renderItems(true);
|
|
});
|
|
that._onScroll = function (scrollTop, force) {
|
|
var getList = that._listItems(that.getter);
|
|
return that._fixedHeader(scrollTop, getList(scrollTop, force));
|
|
};
|
|
that._renderItems = that._whenChanged(scrollCallback(content, that._onScroll), syncList(that._reorderList(that._items, $.proxy(render, that))));
|
|
that._renderItems();
|
|
that._calculateGroupPadding(that.screenHeight);
|
|
},
|
|
_setHeight: function (height) {
|
|
var currentHeight, heightContainer = this.heightContainer;
|
|
if (!heightContainer) {
|
|
heightContainer = this.heightContainer = appendChild(this.content[0], HEIGHTCONTAINER);
|
|
} else {
|
|
currentHeight = heightContainer.offsetHeight;
|
|
}
|
|
if (height !== currentHeight) {
|
|
heightContainer.innerHTML = '';
|
|
while (height > 0) {
|
|
var padHeight = Math.min(height, 250000);
|
|
appendChild(heightContainer).style.height = padHeight + 'px';
|
|
height -= padHeight;
|
|
}
|
|
}
|
|
},
|
|
_getter: function () {
|
|
var lastRequestedRange = null, dataSource = this.dataSource, lastRangeStart = dataSource.skip(), type = this.options.type, pageSize = this.itemCount, flatGroups = {};
|
|
if (dataSource.pageSize() < pageSize) {
|
|
this.mute(function () {
|
|
dataSource.pageSize(pageSize);
|
|
});
|
|
}
|
|
return function (index, rangeStart) {
|
|
var that = this;
|
|
if (!dataSource.inRange(rangeStart, pageSize)) {
|
|
if (lastRequestedRange !== rangeStart) {
|
|
lastRequestedRange = rangeStart;
|
|
lastRangeStart = rangeStart;
|
|
if (that._getterDeferred) {
|
|
that._getterDeferred.reject();
|
|
}
|
|
that._getterDeferred = that.deferredRange(rangeStart);
|
|
that._getterDeferred.then(function () {
|
|
var firstItemIndex = that._indexConstraint(that.content[0].scrollTop);
|
|
that._getterDeferred = null;
|
|
if (rangeStart <= firstItemIndex && firstItemIndex <= rangeStart + pageSize) {
|
|
that._fetching = true;
|
|
dataSource.range(rangeStart, pageSize);
|
|
}
|
|
});
|
|
}
|
|
return null;
|
|
} else {
|
|
if (lastRangeStart !== rangeStart) {
|
|
this.mute(function () {
|
|
dataSource.range(rangeStart, pageSize);
|
|
lastRangeStart = rangeStart;
|
|
});
|
|
}
|
|
var result;
|
|
if (type === 'group') {
|
|
if (!flatGroups[rangeStart]) {
|
|
var flatGroup = flatGroups[rangeStart] = [];
|
|
var groups = dataSource.view();
|
|
for (var i = 0, len = groups.length; i < len; i++) {
|
|
var group = groups[i];
|
|
for (var j = 0, groupLength = group.items.length; j < groupLength; j++) {
|
|
flatGroup.push({
|
|
item: group.items[j],
|
|
group: group.value
|
|
});
|
|
}
|
|
}
|
|
}
|
|
result = flatGroups[rangeStart][index - rangeStart];
|
|
} else {
|
|
result = dataSource.view()[index - rangeStart];
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
},
|
|
_fixedHeader: function (scrollTop, list) {
|
|
var group = this.currentVisibleGroup, itemHeight = this.options.itemHeight, firstVisibleDataItemIndex = Math.floor((scrollTop - list.top) / itemHeight), firstVisibleDataItem = list.items[firstVisibleDataItemIndex];
|
|
if (firstVisibleDataItem && firstVisibleDataItem.item) {
|
|
var firstVisibleGroup = firstVisibleDataItem.group;
|
|
if (firstVisibleGroup !== group) {
|
|
this.header[0].innerHTML = firstVisibleGroup || '';
|
|
this.currentVisibleGroup = firstVisibleGroup;
|
|
}
|
|
}
|
|
return list;
|
|
},
|
|
_itemMapper: function (item, index, value) {
|
|
var listType = this.options.type, itemHeight = this.options.itemHeight, currentIndex = this._focusedIndex, selected = false, current = false, newGroup = false, group = null, match = false, valueGetter = this._valueGetter;
|
|
if (listType === 'group') {
|
|
if (item) {
|
|
newGroup = index === 0 || this._currentGroup && this._currentGroup !== item.group;
|
|
this._currentGroup = item.group;
|
|
}
|
|
group = item ? item.group : null;
|
|
item = item ? item.item : null;
|
|
}
|
|
if (!this.isFiltered() && value.length && item) {
|
|
for (var i = 0; i < value.length; i++) {
|
|
match = isPrimitive(item) ? value[i] === item : value[i] === valueGetter(item);
|
|
if (match) {
|
|
value.splice(i, 1);
|
|
selected = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (currentIndex === index) {
|
|
current = true;
|
|
}
|
|
return {
|
|
item: item ? item : null,
|
|
group: group,
|
|
newGroup: newGroup,
|
|
selected: selected,
|
|
current: current,
|
|
index: index,
|
|
top: index * itemHeight
|
|
};
|
|
},
|
|
_range: function (index) {
|
|
var itemCount = this.itemCount, value = this._values.slice(), items = [], item;
|
|
this._view = {};
|
|
this._currentGroup = null;
|
|
for (var i = index, length = index + itemCount; i < length; i++) {
|
|
item = this._itemMapper(this.getter(i, index), i, value);
|
|
items.push(item);
|
|
this._view[item.index] = item;
|
|
}
|
|
this._dataView = items;
|
|
return items;
|
|
},
|
|
_getDataItemsCollection: function (scrollTop, lastScrollTop) {
|
|
var items = this._range(this._listIndex(scrollTop, lastScrollTop));
|
|
return {
|
|
index: items.length ? items[0].index : 0,
|
|
top: items.length ? items[0].top : 0,
|
|
items: items
|
|
};
|
|
},
|
|
_listItems: function () {
|
|
var screenHeight = this.screenHeight, options = this.options;
|
|
var theValidator = listValidator(options, screenHeight);
|
|
return $.proxy(function (value, force) {
|
|
var result = this.result, lastScrollTop = this._lastScrollTop;
|
|
if (force || !result || !theValidator(result, value, lastScrollTop)) {
|
|
result = this._getDataItemsCollection(value, lastScrollTop);
|
|
}
|
|
this._lastScrollTop = value;
|
|
this.result = result;
|
|
return result;
|
|
}, this);
|
|
},
|
|
_whenChanged: function (getter, callback) {
|
|
var current;
|
|
return function (force) {
|
|
var theNew = getter(force);
|
|
if (theNew !== current) {
|
|
current = theNew;
|
|
callback(theNew, force);
|
|
}
|
|
};
|
|
},
|
|
_reorderList: function (list, reorder) {
|
|
var that = this;
|
|
var length = list.length;
|
|
var currentOffset = -Infinity;
|
|
reorder = $.proxy(map2(reorder, this.templates), this);
|
|
return function (list2, offset, force) {
|
|
var diff = offset - currentOffset;
|
|
var range, range2;
|
|
if (force || Math.abs(diff) >= length) {
|
|
range = list;
|
|
range2 = list2;
|
|
} else {
|
|
range = reshift(list, diff);
|
|
range2 = diff > 0 ? list2.slice(-diff) : list2.slice(0, -diff);
|
|
}
|
|
reorder(range, range2, that.bound());
|
|
currentOffset = offset;
|
|
};
|
|
},
|
|
_bufferSizes: function () {
|
|
var options = this.options;
|
|
return bufferSizes(this.screenHeight, options.listScreens, options.oppositeBuffer);
|
|
},
|
|
_indexConstraint: function (position) {
|
|
var itemCount = this.itemCount, itemHeight = this.options.itemHeight, total = this.dataSource.total();
|
|
return Math.min(Math.max(total - itemCount, 0), Math.max(0, Math.floor(position / itemHeight)));
|
|
},
|
|
_listIndex: function (scrollTop, lastScrollTop) {
|
|
var buffers = this._bufferSizes(), position;
|
|
position = scrollTop - (scrollTop > lastScrollTop ? buffers.down : buffers.up);
|
|
return this._indexConstraint(position);
|
|
},
|
|
_selectable: function () {
|
|
if (this.options.selectable) {
|
|
this._selectProxy = $.proxy(this, '_clickHandler');
|
|
this.element.on(CLICK + VIRTUAL_LIST_NS, '.' + VIRTUALITEM, this._selectProxy);
|
|
}
|
|
},
|
|
_getIndecies: function (candidate) {
|
|
var result = [], data;
|
|
if (typeof candidate === 'function') {
|
|
data = this.dataSource.flatView();
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
if (candidate(data[idx])) {
|
|
result.push(idx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (typeof candidate === 'number') {
|
|
result.push(candidate);
|
|
}
|
|
if (candidate instanceof jQuery) {
|
|
candidate = parseInt(candidate.attr('data-offset-index'), 10);
|
|
if (!isNaN(candidate)) {
|
|
result.push(candidate);
|
|
}
|
|
}
|
|
if (candidate instanceof Array) {
|
|
result = candidate;
|
|
}
|
|
return result;
|
|
},
|
|
_deselect: function (indices) {
|
|
var removed = [], selectedIndex, dataItem, selectedIndexes = this._selectedIndexes, position = 0, selectable = this.options.selectable, removedindexesCounter = 0, item;
|
|
indices = indices.slice();
|
|
if (selectable === true || !indices.length) {
|
|
for (var idx = 0; idx < selectedIndexes.length; idx++) {
|
|
if (selectedIndexes[idx] !== undefined) {
|
|
this._getElementByIndex(selectedIndexes[idx]).removeClass(SELECTED);
|
|
removed.push({
|
|
index: selectedIndexes[idx],
|
|
position: idx,
|
|
dataItem: this._selectedDataItems[idx]
|
|
});
|
|
}
|
|
}
|
|
this._values = [];
|
|
this._selectedDataItems = [];
|
|
this._selectedIndexes = [];
|
|
} else if (selectable === 'multiple') {
|
|
for (var i = 0; i < indices.length; i++) {
|
|
position = $.inArray(indices[i], selectedIndexes);
|
|
selectedIndex = selectedIndexes[position];
|
|
if (selectedIndex !== undefined) {
|
|
item = this._getElementByIndex(selectedIndex);
|
|
if (!item.hasClass('k-state-selected')) {
|
|
continue;
|
|
}
|
|
item.removeClass(SELECTED);
|
|
this._values.splice(position, 1);
|
|
this._selectedIndexes.splice(position, 1);
|
|
dataItem = this._selectedDataItems.splice(position, 1)[0];
|
|
indices.splice(i, 1);
|
|
removed.push({
|
|
index: selectedIndex,
|
|
position: position + removedindexesCounter,
|
|
dataItem: dataItem
|
|
});
|
|
removedindexesCounter++;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
indices: indices,
|
|
removed: removed
|
|
};
|
|
},
|
|
_deselectCurrentValues: function (indices) {
|
|
var children = this.element[0].children;
|
|
var value, index, position;
|
|
var values = this._values;
|
|
var removed = [];
|
|
var idx = 0;
|
|
var j;
|
|
if (this.options.selectable !== 'multiple' || !this.isFiltered()) {
|
|
return [];
|
|
}
|
|
for (; idx < indices.length; idx++) {
|
|
position = -1;
|
|
index = indices[idx];
|
|
value = this._valueGetter(this._view[index].item);
|
|
for (j = 0; j < values.length; j++) {
|
|
if (value == values[j]) {
|
|
position = j;
|
|
break;
|
|
}
|
|
}
|
|
if (position > -1) {
|
|
removed.push(this.removeAt(position));
|
|
$(children[index]).removeClass('k-state-selected');
|
|
}
|
|
}
|
|
return removed;
|
|
},
|
|
_select: function (indexes) {
|
|
var that = this, singleSelection = this.options.selectable !== 'multiple', dataSource = this.dataSource, dataItem, oldSkip, take = this.itemCount, valueGetter = this._valueGetter, added = [];
|
|
if (singleSelection) {
|
|
that._selectedIndexes = [];
|
|
that._selectedDataItems = [];
|
|
that._values = [];
|
|
}
|
|
oldSkip = dataSource.skip();
|
|
$.each(indexes, function (_, index) {
|
|
var page = index < take ? 1 : Math.floor(index / take) + 1;
|
|
var skip = (page - 1) * take;
|
|
that.mute(function () {
|
|
dataSource.range(skip, take);
|
|
dataItem = that._findDataItem([index - skip]);
|
|
that._selectedIndexes.push(index);
|
|
that._selectedDataItems.push(dataItem);
|
|
that._values.push(isPrimitive(dataItem) ? dataItem : valueGetter(dataItem));
|
|
added.push({
|
|
index: index,
|
|
dataItem: dataItem
|
|
});
|
|
that._getElementByIndex(index).addClass(SELECTED);
|
|
dataSource.range(oldSkip, take);
|
|
});
|
|
});
|
|
return added;
|
|
},
|
|
_clickHandler: function (e) {
|
|
var item = $(e.currentTarget);
|
|
if (!e.isDefaultPrevented() && item.attr('data-uid')) {
|
|
this.trigger(CLICK, { item: item });
|
|
}
|
|
},
|
|
_buildValueGetter: function () {
|
|
this._valueGetter = kendo.getter(this.options.dataValueField);
|
|
},
|
|
_calculateGroupPadding: function (height) {
|
|
var firstItem = this.items().first(), groupHeader = this.header, padding = 0;
|
|
if (groupHeader[0] && groupHeader[0].style.display !== 'none') {
|
|
if (height !== 'auto') {
|
|
padding = kendo.support.scrollbar();
|
|
}
|
|
padding += parseFloat(firstItem.css('border-right-width'), 10) + parseFloat(firstItem.children('.k-group').css('right'), 10);
|
|
groupHeader.css('padding-right', padding);
|
|
}
|
|
}
|
|
});
|
|
kendo.ui.VirtualList = VirtualList;
|
|
kendo.ui.plugin(VirtualList);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.view', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.view',
|
|
name: 'Scheduler View',
|
|
category: 'web',
|
|
description: 'The Scheduler Common View',
|
|
depends: ['core'],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, NS = '.kendoSchedulerView', math = Math;
|
|
function levels(values, key) {
|
|
var result = [];
|
|
function collect(depth, values) {
|
|
values = values[key];
|
|
if (values) {
|
|
var level = result[depth] = result[depth] || [];
|
|
for (var idx = 0; idx < values.length; idx++) {
|
|
level.push(values[idx]);
|
|
collect(depth + 1, values[idx]);
|
|
}
|
|
}
|
|
}
|
|
collect(0, values);
|
|
return result;
|
|
}
|
|
function cellspacing() {
|
|
if (kendo.support.cssBorderSpacing) {
|
|
return '';
|
|
}
|
|
return 'cellspacing="0"';
|
|
}
|
|
function table(tableRows, className) {
|
|
if (!tableRows.length) {
|
|
return '';
|
|
}
|
|
return '<table ' + cellspacing() + ' class="' + $.trim('k-scheduler-table ' + (className || '')) + '">' + '<tr>' + tableRows.join('</tr><tr>') + '</tr>' + '</table>';
|
|
}
|
|
function allDayTable(tableRows, className) {
|
|
if (!tableRows.length) {
|
|
return '';
|
|
}
|
|
return '<div style=\'position:relative\'>' + table(tableRows, className) + '</div>';
|
|
}
|
|
function timesHeader(columnLevelCount, allDaySlot, rowCount) {
|
|
var tableRows = [];
|
|
if (rowCount > 0) {
|
|
for (var idx = 0; idx < columnLevelCount; idx++) {
|
|
tableRows.push('<th> </th>');
|
|
}
|
|
}
|
|
if (allDaySlot) {
|
|
tableRows.push('<th class="k-scheduler-times-all-day">' + allDaySlot.text + '</th>');
|
|
}
|
|
if (rowCount < 1) {
|
|
return $();
|
|
}
|
|
return $('<div class="k-scheduler-times">' + table(tableRows) + '</div>');
|
|
}
|
|
function datesHeader(columnLevels, columnCount, allDaySlot) {
|
|
var dateTableRows = [];
|
|
var columnIndex;
|
|
for (var columnLevelIndex = 0; columnLevelIndex < columnLevels.length; columnLevelIndex++) {
|
|
var level = columnLevels[columnLevelIndex];
|
|
var th = [];
|
|
var colspan = columnCount / level.length;
|
|
for (columnIndex = 0; columnIndex < level.length; columnIndex++) {
|
|
var column = level[columnIndex];
|
|
th.push('<th colspan="' + (column.colspan || colspan) + '" class="' + (column.className || '') + '">' + column.text + '</th>');
|
|
}
|
|
dateTableRows.push(th.join(''));
|
|
}
|
|
var allDayTableRows = [];
|
|
if (allDaySlot) {
|
|
var lastLevel = columnLevels[columnLevels.length - 1];
|
|
var td = [];
|
|
var cellContent = allDaySlot.cellContent;
|
|
for (columnIndex = 0; columnIndex < lastLevel.length; columnIndex++) {
|
|
td.push('<td class="' + (lastLevel[columnIndex].className || '') + '">' + (cellContent ? cellContent(columnIndex) : ' ') + '</th>');
|
|
}
|
|
allDayTableRows.push(td.join(''));
|
|
}
|
|
return $('<div class="k-scheduler-header k-state-default">' + '<div class="k-scheduler-header-wrap">' + table(dateTableRows) + allDayTable(allDayTableRows, 'k-scheduler-header-all-day') + '</div>' + '</div>');
|
|
}
|
|
function times(rowLevels, rowCount) {
|
|
var rows = new Array(rowCount).join().split(',');
|
|
var rowHeaderRows = [];
|
|
var rowIndex;
|
|
for (var rowLevelIndex = 0; rowLevelIndex < rowLevels.length; rowLevelIndex++) {
|
|
var level = rowLevels[rowLevelIndex];
|
|
var rowspan = rowCount / level.length;
|
|
var className;
|
|
for (rowIndex = 0; rowIndex < level.length; rowIndex++) {
|
|
className = level[rowIndex].className || '';
|
|
if (level[rowIndex].allDay) {
|
|
className = 'k-scheduler-times-all-day';
|
|
}
|
|
rows[rowspan * rowIndex] += '<th class="' + className + '" rowspan="' + rowspan + '">' + level[rowIndex].text + '</th>';
|
|
}
|
|
}
|
|
for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
rowHeaderRows.push(rows[rowIndex]);
|
|
}
|
|
if (rowCount < 1) {
|
|
return $();
|
|
}
|
|
return $('<div class="k-scheduler-times">' + table(rowHeaderRows) + '</div>');
|
|
}
|
|
function content() {
|
|
return $('<div class="k-scheduler-content">' + '<table ' + cellspacing() + ' class="k-scheduler-table"/>' + '</div>');
|
|
}
|
|
var HINT = '<div class="k-marquee k-scheduler-marquee">' + '<div class="k-marquee-color"></div>' + '<div class="k-marquee-text">' + '<div class="k-label-top"></div>' + '<div class="k-label-bottom"></div>' + '</div>' + '</div>';
|
|
kendo.ui.scheduler = {};
|
|
var ResourceView = kendo.Class.extend({
|
|
init: function (index, isRtl) {
|
|
this._index = index;
|
|
this._timeSlotCollections = [];
|
|
this._daySlotCollections = [];
|
|
this._isRtl = isRtl;
|
|
},
|
|
addTimeSlotCollection: function (startDate, endDate) {
|
|
return this._addCollection(startDate, endDate, this._timeSlotCollections);
|
|
},
|
|
addDaySlotCollection: function (startDate, endDate) {
|
|
return this._addCollection(startDate, endDate, this._daySlotCollections);
|
|
},
|
|
_addCollection: function (startDate, endDate, collections) {
|
|
var collection = new SlotCollection(startDate, endDate, this._index, collections.length);
|
|
collections.push(collection);
|
|
return collection;
|
|
},
|
|
timeSlotCollectionCount: function () {
|
|
return this._timeSlotCollections.length;
|
|
},
|
|
daySlotCollectionCount: function () {
|
|
return this._daySlotCollections.length;
|
|
},
|
|
daySlotByPosition: function (x, y) {
|
|
return this._slotByPosition(x, y, this._daySlotCollections);
|
|
},
|
|
timeSlotByPosition: function (x, y) {
|
|
return this._slotByPosition(x, y, this._timeSlotCollections);
|
|
},
|
|
_slotByPosition: function (x, y, collections) {
|
|
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
|
|
var collection = collections[collectionIndex];
|
|
for (var slotIndex = 0; slotIndex < collection.count(); slotIndex++) {
|
|
var slot = collection.at(slotIndex);
|
|
var width = slot.offsetWidth;
|
|
var height = slot.offsetHeight;
|
|
var horizontalEnd = slot.offsetLeft + width;
|
|
var verticalEnd = slot.offsetTop + height;
|
|
var nextSlot = collection.at(slotIndex + 1);
|
|
if (nextSlot) {
|
|
if (nextSlot.offsetLeft != slot.offsetLeft) {
|
|
if (this._isRtl) {
|
|
horizontalEnd = slot.offsetLeft + (slot.offsetLeft - nextSlot.offsetLeft);
|
|
} else {
|
|
horizontalEnd = nextSlot.offsetLeft;
|
|
}
|
|
} else {
|
|
verticalEnd = nextSlot.offsetTop;
|
|
}
|
|
}
|
|
if (x >= slot.offsetLeft && x < horizontalEnd && y >= slot.offsetTop && y < verticalEnd) {
|
|
return slot;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var collectionIndex;
|
|
for (collectionIndex = 0; collectionIndex < this._daySlotCollections.length; collectionIndex++) {
|
|
this._daySlotCollections[collectionIndex].refresh();
|
|
}
|
|
for (collectionIndex = 0; collectionIndex < this._timeSlotCollections.length; collectionIndex++) {
|
|
this._timeSlotCollections[collectionIndex].refresh();
|
|
}
|
|
},
|
|
timeSlotRanges: function (startTime, endTime) {
|
|
var collections = this._timeSlotCollections;
|
|
var start = this._startSlot(startTime, collections);
|
|
if (!start.inRange && startTime >= start.slot.end) {
|
|
start = null;
|
|
}
|
|
var end = start;
|
|
if (startTime < endTime) {
|
|
end = this._endSlot(endTime, collections);
|
|
}
|
|
if (end && !end.inRange && endTime <= end.slot.start) {
|
|
end = null;
|
|
}
|
|
if (start === null && end === null) {
|
|
return [];
|
|
}
|
|
if (start === null) {
|
|
start = {
|
|
inRange: true,
|
|
slot: collections[end.slot.collectionIndex].first()
|
|
};
|
|
}
|
|
if (end === null) {
|
|
end = {
|
|
inRange: true,
|
|
slot: collections[start.slot.collectionIndex].last()
|
|
};
|
|
}
|
|
return this._continuousRange(TimeSlotRange, collections, start, end);
|
|
},
|
|
daySlotRanges: function (startTime, endTime, isAllDay) {
|
|
var collections = this._daySlotCollections;
|
|
var start = this._startSlot(startTime, collections, isAllDay);
|
|
if (!start.inRange && startTime >= start.slot.end) {
|
|
start = null;
|
|
}
|
|
var end = start;
|
|
if (startTime < endTime) {
|
|
end = this._endSlot(endTime, collections, isAllDay);
|
|
}
|
|
if (end && !end.inRange && endTime <= end.slot.start) {
|
|
end = null;
|
|
}
|
|
if (start === null && end === null) {
|
|
return [];
|
|
}
|
|
if (start === null) {
|
|
do {
|
|
startTime += kendo.date.MS_PER_DAY;
|
|
start = this._startSlot(startTime, collections, isAllDay);
|
|
} while (!start.inRange && startTime >= start.slot.end);
|
|
}
|
|
if (end === null) {
|
|
do {
|
|
endTime -= kendo.date.MS_PER_DAY;
|
|
end = this._endSlot(endTime, collections, isAllDay);
|
|
} while (!end.inRange && endTime <= end.slot.start);
|
|
}
|
|
return this._continuousRange(DaySlotRange, collections, start, end);
|
|
},
|
|
_continuousRange: function (range, collections, start, end) {
|
|
var startSlot = start.slot;
|
|
var endSlot = end.slot;
|
|
var startIndex = startSlot.collectionIndex;
|
|
var endIndex = endSlot.collectionIndex;
|
|
var ranges = [];
|
|
for (var collectionIndex = startIndex; collectionIndex <= endIndex; collectionIndex++) {
|
|
var collection = collections[collectionIndex];
|
|
var first = collection.first();
|
|
var last = collection.last();
|
|
var head = false;
|
|
var tail = false;
|
|
if (collectionIndex == startIndex) {
|
|
tail = !start.inRange;
|
|
}
|
|
if (collectionIndex == endIndex) {
|
|
head = !end.inRange;
|
|
}
|
|
if (first.start < startSlot.start) {
|
|
first = startSlot;
|
|
}
|
|
if (last.start > endSlot.start) {
|
|
last = endSlot;
|
|
}
|
|
if (startIndex < endIndex) {
|
|
if (collectionIndex == startIndex) {
|
|
head = true;
|
|
} else if (collectionIndex == endIndex) {
|
|
tail = true;
|
|
} else {
|
|
head = tail = true;
|
|
}
|
|
}
|
|
ranges.push(new range({
|
|
start: first,
|
|
end: last,
|
|
collection: collection,
|
|
head: head,
|
|
tail: tail
|
|
}));
|
|
}
|
|
return ranges;
|
|
},
|
|
slotRanges: function (event, isDay) {
|
|
var startTime = event._startTime || kendo.date.toUtcTime(event.start);
|
|
var endTime = event._endTime || kendo.date.toUtcTime(event.end);
|
|
if (isDay === undefined) {
|
|
isDay = event.isMultiDay();
|
|
}
|
|
if (isDay) {
|
|
return this.daySlotRanges(startTime, endTime, event.isAllDay);
|
|
}
|
|
return this.timeSlotRanges(startTime, endTime);
|
|
},
|
|
ranges: function (startTime, endTime, isDay, isAllDay) {
|
|
if (typeof startTime != 'number') {
|
|
startTime = kendo.date.toUtcTime(startTime);
|
|
}
|
|
if (typeof endTime != 'number') {
|
|
endTime = kendo.date.toUtcTime(endTime);
|
|
}
|
|
if (isDay) {
|
|
return this.daySlotRanges(startTime, endTime, isAllDay);
|
|
}
|
|
return this.timeSlotRanges(startTime, endTime);
|
|
},
|
|
_startCollection: function (date, collections) {
|
|
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
|
|
var collection = collections[collectionIndex];
|
|
if (collection.startInRange(date)) {
|
|
return collection;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
_endCollection: function (date, collections, isAllDay) {
|
|
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
|
|
var collection = collections[collectionIndex];
|
|
if (collection.endInRange(date, isAllDay)) {
|
|
return collection;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
_getCollections: function (isDay) {
|
|
return isDay ? this._daySlotCollections : this._timeSlotCollections;
|
|
},
|
|
continuousSlot: function (slot, reverse) {
|
|
var pad = reverse ? -1 : 1;
|
|
var collections = this._getCollections(slot.isDaySlot);
|
|
var collection = collections[slot.collectionIndex + pad];
|
|
return collection ? collection[reverse ? 'last' : 'first']() : undefined;
|
|
},
|
|
firstSlot: function () {
|
|
var collections = this._getCollections(this.daySlotCollectionCount());
|
|
return collections[0].first();
|
|
},
|
|
lastSlot: function () {
|
|
var collections = this._getCollections(this.daySlotCollectionCount());
|
|
return collections[collections.length - 1].last();
|
|
},
|
|
upSlot: function (slot, keepCollection) {
|
|
var that = this;
|
|
var moveToDaySlot = function (isDaySlot, collectionIndex, index) {
|
|
var isFirstCell = index === 0;
|
|
if (!keepCollection && !isDaySlot && isFirstCell && that.daySlotCollectionCount()) {
|
|
return that._daySlotCollections[0].at(collectionIndex);
|
|
}
|
|
};
|
|
if (!this.timeSlotCollectionCount()) {
|
|
keepCollection = true;
|
|
}
|
|
return this._verticalSlot(slot, -1, moveToDaySlot);
|
|
},
|
|
downSlot: function (slot, keepCollection) {
|
|
var that = this;
|
|
var moveToTimeSlot = function (isDaySlot, collectionIndex, index) {
|
|
if (!keepCollection && isDaySlot && that.timeSlotCollectionCount()) {
|
|
return that._timeSlotCollections[index].at(0);
|
|
}
|
|
};
|
|
if (!this.timeSlotCollectionCount()) {
|
|
keepCollection = true;
|
|
}
|
|
return this._verticalSlot(slot, 1, moveToTimeSlot);
|
|
},
|
|
leftSlot: function (slot) {
|
|
return this._horizontalSlot(slot, -1);
|
|
},
|
|
rightSlot: function (slot) {
|
|
return this._horizontalSlot(slot, 1);
|
|
},
|
|
_horizontalSlot: function (slot, step) {
|
|
var index = slot.index;
|
|
var isDaySlot = slot.isDaySlot;
|
|
var collectionIndex = slot.collectionIndex;
|
|
var collections = this._getCollections(isDaySlot);
|
|
if (isDaySlot) {
|
|
index += step;
|
|
} else {
|
|
collectionIndex += step;
|
|
}
|
|
var collection = collections[collectionIndex];
|
|
return collection ? collection.at(index) : undefined;
|
|
},
|
|
_verticalSlot: function (slot, step, swapCollection) {
|
|
var index = slot.index;
|
|
var isDaySlot = slot.isDaySlot;
|
|
var collectionIndex = slot.collectionIndex;
|
|
var collections = this._getCollections(isDaySlot);
|
|
slot = swapCollection(isDaySlot, collectionIndex, index);
|
|
if (slot) {
|
|
return slot;
|
|
}
|
|
if (isDaySlot) {
|
|
collectionIndex += step;
|
|
} else {
|
|
index += step;
|
|
}
|
|
var collection = collections[collectionIndex];
|
|
return collection ? collection.at(index) : undefined;
|
|
},
|
|
_collection: function (index, multiday) {
|
|
var collections = multiday ? this._daySlotCollections : this._timeSlotCollections;
|
|
return collections[index];
|
|
},
|
|
_startSlot: function (time, collections, isAllDay) {
|
|
var collection = this._startCollection(time, collections);
|
|
var inRange = true;
|
|
if (!collection) {
|
|
collection = collections[0];
|
|
inRange = false;
|
|
}
|
|
var slot = collection.slotByStartDate(time, isAllDay);
|
|
if (!slot) {
|
|
slot = collection.first();
|
|
inRange = false;
|
|
}
|
|
return {
|
|
slot: slot,
|
|
inRange: inRange
|
|
};
|
|
},
|
|
_endSlot: function (time, collections, isAllDay) {
|
|
var collection = this._endCollection(time, collections, isAllDay);
|
|
var inRange = true;
|
|
if (!collection) {
|
|
collection = collections[collections.length - 1];
|
|
inRange = false;
|
|
}
|
|
var slot = collection.slotByEndDate(time, isAllDay);
|
|
if (!slot) {
|
|
slot = collection.last();
|
|
inRange = false;
|
|
}
|
|
return {
|
|
slot: slot,
|
|
inRange: inRange
|
|
};
|
|
},
|
|
getSlotCollection: function (index, isDay) {
|
|
return this[isDay ? 'getDaySlotCollection' : 'getTimeSlotCollection'](index);
|
|
},
|
|
getTimeSlotCollection: function (index) {
|
|
return this._timeSlotCollections[index];
|
|
},
|
|
getDaySlotCollection: function (index) {
|
|
return this._daySlotCollections[index];
|
|
}
|
|
});
|
|
var SlotRange = kendo.Class.extend({
|
|
init: function (options) {
|
|
$.extend(this, options);
|
|
},
|
|
innerHeight: function () {
|
|
var collection = this.collection;
|
|
var startIndex = this.start.index;
|
|
var endIndex = this.end.index;
|
|
var result = 0;
|
|
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
|
|
result += collection.at(slotIndex).offsetHeight;
|
|
}
|
|
return result;
|
|
},
|
|
events: function () {
|
|
return this.collection.events();
|
|
},
|
|
addEvent: function (event) {
|
|
this.events().push(event);
|
|
},
|
|
startSlot: function () {
|
|
if (this.start.offsetLeft > this.end.offsetLeft) {
|
|
return this.end;
|
|
}
|
|
return this.start;
|
|
},
|
|
endSlot: function () {
|
|
if (this.start.offsetLeft > this.end.offsetLeft) {
|
|
return this.start;
|
|
}
|
|
return this.end;
|
|
}
|
|
});
|
|
var TimeSlotRange = SlotRange.extend({
|
|
innerHeight: function () {
|
|
var collection = this.collection;
|
|
var startIndex = this.start.index;
|
|
var endIndex = this.end.index;
|
|
var result = 0;
|
|
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
|
|
result += collection.at(slotIndex).offsetHeight;
|
|
}
|
|
return result;
|
|
},
|
|
outerRect: function (start, end, snap) {
|
|
return this._rect('offset', start, end, snap);
|
|
},
|
|
_rect: function (property, start, end, snap) {
|
|
var top;
|
|
var bottom;
|
|
var left;
|
|
var right;
|
|
var startSlot = this.start;
|
|
var endSlot = this.end;
|
|
var isRtl = kendo.support.isRtl(startSlot.element);
|
|
if (typeof start != 'number') {
|
|
start = kendo.date.toUtcTime(start);
|
|
}
|
|
if (typeof end != 'number') {
|
|
end = kendo.date.toUtcTime(end);
|
|
}
|
|
if (snap) {
|
|
top = startSlot.offsetTop;
|
|
bottom = endSlot.offsetTop + endSlot[property + 'Height'];
|
|
if (isRtl) {
|
|
left = endSlot.offsetLeft;
|
|
right = startSlot.offsetLeft + startSlot[property + 'Width'];
|
|
} else {
|
|
left = startSlot.offsetLeft;
|
|
right = endSlot.offsetLeft + endSlot[property + 'Width'];
|
|
}
|
|
} else {
|
|
var startOffset = start - startSlot.start;
|
|
if (startOffset < 0) {
|
|
startOffset = 0;
|
|
}
|
|
var startSlotDuration = startSlot.end - startSlot.start;
|
|
top = startSlot.offsetTop + startSlot[property + 'Height'] * startOffset / startSlotDuration;
|
|
var endOffset = endSlot.end - end;
|
|
if (endOffset < 0) {
|
|
endOffset = 0;
|
|
}
|
|
var endSlotDuration = endSlot.end - endSlot.start;
|
|
bottom = endSlot.offsetTop + endSlot[property + 'Height'] - endSlot[property + 'Height'] * endOffset / endSlotDuration;
|
|
if (isRtl) {
|
|
left = Math.round(endSlot.offsetLeft + endSlot[property + 'Width'] * endOffset / endSlotDuration);
|
|
right = Math.round(startSlot.offsetLeft + startSlot[property + 'Width'] - startSlot[property + 'Width'] * startOffset / startSlotDuration);
|
|
} else {
|
|
left = Math.round(startSlot.offsetLeft + startSlot[property + 'Width'] * startOffset / startSlotDuration);
|
|
right = Math.round(endSlot.offsetLeft + endSlot[property + 'Width'] - endSlot[property + 'Width'] * endOffset / endSlotDuration);
|
|
}
|
|
}
|
|
return {
|
|
top: top,
|
|
bottom: bottom,
|
|
left: left === 0 ? left : left + 1,
|
|
right: right
|
|
};
|
|
},
|
|
innerRect: function (start, end, snap) {
|
|
return this._rect('client', start, end, snap);
|
|
}
|
|
});
|
|
var DaySlotRange = SlotRange.extend({
|
|
innerWidth: function () {
|
|
var collection = this.collection;
|
|
var startIndex = this.start.index;
|
|
var endIndex = this.end.index;
|
|
var result = 0;
|
|
var width = startIndex !== endIndex ? 'offsetWidth' : 'clientWidth';
|
|
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
|
|
result += collection.at(slotIndex)[width];
|
|
}
|
|
return result;
|
|
}
|
|
});
|
|
var SlotCollection = kendo.Class.extend({
|
|
init: function (startDate, endDate, groupIndex, collectionIndex) {
|
|
this._slots = [];
|
|
this._events = [];
|
|
this._start = kendo.date.toUtcTime(startDate);
|
|
this._end = kendo.date.toUtcTime(endDate);
|
|
this._groupIndex = groupIndex;
|
|
this._collectionIndex = collectionIndex;
|
|
},
|
|
refresh: function () {
|
|
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
|
|
this._slots[slotIndex].refresh();
|
|
}
|
|
},
|
|
startInRange: function (date) {
|
|
return this._start <= date && date < this._end;
|
|
},
|
|
endInRange: function (date, isAllDay) {
|
|
var end = isAllDay ? date < this._end : date <= this._end;
|
|
return this._start <= date && end;
|
|
},
|
|
slotByStartDate: function (date) {
|
|
var time = date;
|
|
if (typeof time != 'number') {
|
|
time = kendo.date.toUtcTime(date);
|
|
}
|
|
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
|
|
var slot = this._slots[slotIndex];
|
|
if (slot.startInRange(time)) {
|
|
return slot;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
slotByEndDate: function (date, allday) {
|
|
var time = date;
|
|
if (typeof time != 'number') {
|
|
time = kendo.date.toUtcTime(date);
|
|
}
|
|
if (allday) {
|
|
return this.slotByStartDate(date, false);
|
|
}
|
|
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
|
|
var slot = this._slots[slotIndex];
|
|
if (slot.endInRange(time)) {
|
|
return slot;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
count: function () {
|
|
return this._slots.length;
|
|
},
|
|
events: function () {
|
|
return this._events;
|
|
},
|
|
addTimeSlot: function (element, start, end, isHorizontal) {
|
|
var slot = new TimeSlot(element, start, end, this._groupIndex, this._collectionIndex, this._slots.length, isHorizontal);
|
|
this._slots.push(slot);
|
|
},
|
|
addDaySlot: function (element, start, end, eventCount) {
|
|
var slot = new DaySlot(element, start, end, this._groupIndex, this._collectionIndex, this._slots.length, eventCount);
|
|
this._slots.push(slot);
|
|
},
|
|
first: function () {
|
|
return this._slots[0];
|
|
},
|
|
last: function () {
|
|
return this._slots[this._slots.length - 1];
|
|
},
|
|
at: function (index) {
|
|
return this._slots[index];
|
|
}
|
|
});
|
|
var Slot = kendo.Class.extend({
|
|
init: function (element, start, end, groupIndex, collectionIndex, index) {
|
|
this.element = element;
|
|
this.clientWidth = element.clientWidth;
|
|
this.clientHeight = element.clientHeight;
|
|
this.offsetWidth = element.offsetWidth;
|
|
this.offsetHeight = element.offsetHeight;
|
|
this.offsetTop = element.offsetTop;
|
|
this.offsetLeft = element.offsetLeft;
|
|
this.start = start;
|
|
this.end = end;
|
|
this.element = element;
|
|
this.groupIndex = groupIndex;
|
|
this.collectionIndex = collectionIndex;
|
|
this.index = index;
|
|
this.isDaySlot = false;
|
|
},
|
|
startDate: function () {
|
|
return kendo.timezone.toLocalDate(this.start);
|
|
},
|
|
endDate: function () {
|
|
return kendo.timezone.toLocalDate(this.end);
|
|
},
|
|
startInRange: function (date) {
|
|
return this.start <= date && date < this.end;
|
|
},
|
|
endInRange: function (date) {
|
|
return this.start < date && date <= this.end;
|
|
},
|
|
startOffset: function () {
|
|
return this.start;
|
|
},
|
|
endOffset: function () {
|
|
return this.end;
|
|
}
|
|
});
|
|
var TimeSlot = Slot.extend({
|
|
init: function (element, start, end, groupIndex, collectionIndex, index, isHorizontal) {
|
|
Slot.fn.init.apply(this, arguments);
|
|
this.isHorizontal = isHorizontal ? true : false;
|
|
},
|
|
refresh: function () {
|
|
var element = this.element;
|
|
this.clientWidth = element.clientWidth;
|
|
this.clientHeight = element.clientHeight;
|
|
this.offsetWidth = element.offsetWidth;
|
|
this.offsetHeight = element.offsetHeight;
|
|
this.offsetTop = element.offsetTop;
|
|
this.offsetLeft = element.offsetLeft;
|
|
},
|
|
offsetX: function (rtl, offset) {
|
|
if (rtl) {
|
|
return this.offsetLeft + offset;
|
|
} else {
|
|
return this.offsetLeft + offset;
|
|
}
|
|
},
|
|
startInRange: function (date) {
|
|
return this.start <= date && date < this.end;
|
|
},
|
|
endInRange: function (date) {
|
|
return this.start < date && date <= this.end;
|
|
},
|
|
startOffset: function (x, y, snap) {
|
|
if (snap) {
|
|
return this.start;
|
|
}
|
|
var offset = $(this.element).offset();
|
|
var duration = this.end - this.start;
|
|
var difference;
|
|
var time;
|
|
if (this.isHorizontal) {
|
|
var isRtl = kendo.support.isRtl(this.element);
|
|
difference = x - offset.left;
|
|
time = Math.floor(duration * (difference / this.offsetWidth));
|
|
if (isRtl) {
|
|
return this.start + duration - time;
|
|
}
|
|
} else {
|
|
difference = y - offset.top;
|
|
time = Math.floor(duration * (difference / this.offsetHeight));
|
|
}
|
|
return this.start + time;
|
|
},
|
|
endOffset: function (x, y, snap) {
|
|
if (snap) {
|
|
return this.end;
|
|
}
|
|
var offset = $(this.element).offset();
|
|
var duration = this.end - this.start;
|
|
var difference;
|
|
var time;
|
|
if (this.isHorizontal) {
|
|
var isRtl = kendo.support.isRtl(this.element);
|
|
difference = x - offset.left;
|
|
time = Math.floor(duration * (difference / this.offsetWidth));
|
|
if (isRtl) {
|
|
return this.start + duration - time;
|
|
}
|
|
} else {
|
|
difference = y - offset.top;
|
|
time = Math.floor(duration * (difference / this.offsetHeight));
|
|
}
|
|
return this.start + time;
|
|
}
|
|
});
|
|
var DaySlot = Slot.extend({
|
|
init: function (element, start, end, groupIndex, collectionIndex, index, eventCount) {
|
|
Slot.fn.init.apply(this, arguments);
|
|
this.eventCount = eventCount;
|
|
this.isDaySlot = true;
|
|
if (this.element.children.length) {
|
|
this.firstChildHeight = this.element.children[0].offsetHeight + 3;
|
|
this.firstChildTop = this.element.children[0].offsetTop;
|
|
} else {
|
|
this.firstChildHeight = 3;
|
|
this.firstChildTop = 0;
|
|
}
|
|
},
|
|
refresh: function () {
|
|
this.clientHeight = this.element.clientHeight;
|
|
this.offsetTop = this.element.offsetTop;
|
|
},
|
|
startDate: function () {
|
|
var date = new Date(this.start);
|
|
return kendo.timezone.apply(date, 'Etc/UTC');
|
|
},
|
|
endDate: function () {
|
|
var date = new Date(this.end);
|
|
return kendo.timezone.apply(date, 'Etc/UTC');
|
|
},
|
|
startInRange: function (date) {
|
|
return this.start <= date && date < this.end;
|
|
},
|
|
endInRange: function (date) {
|
|
return this.start < date && date <= this.end;
|
|
}
|
|
});
|
|
var scrollbarWidth;
|
|
function scrollbar() {
|
|
scrollbarWidth = scrollbarWidth ? scrollbarWidth : kendo.support.scrollbar();
|
|
return scrollbarWidth;
|
|
}
|
|
kendo.ui.SchedulerView = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._normalizeOptions();
|
|
this._scrollbar = scrollbar();
|
|
this._isRtl = kendo.support.isRtl(element);
|
|
this._resizeHint = $();
|
|
this._moveHint = $();
|
|
this._cellId = kendo.guid();
|
|
this._resourcesForGroups();
|
|
this._selectedSlots = [];
|
|
},
|
|
_normalizeOptions: function () {
|
|
var options = this.options;
|
|
if (options.startTime) {
|
|
options.startTime.setMilliseconds(0);
|
|
}
|
|
if (options.endTime) {
|
|
options.endTime.setMilliseconds(0);
|
|
}
|
|
if (options.workDayStart) {
|
|
options.workDayStart.setMilliseconds(0);
|
|
}
|
|
if (options.workDayEnd) {
|
|
options.workDayEnd.setMilliseconds(0);
|
|
}
|
|
},
|
|
_isMobile: function () {
|
|
var options = this.options;
|
|
return options.mobile === true && kendo.support.mobileOS || options.mobile === 'phone' || options.mobile === 'tablet';
|
|
},
|
|
_isMobilePhoneView: function () {
|
|
var options = this.options;
|
|
return options.mobile === true && kendo.support.mobileOS && !kendo.support.mobileOS.tablet || options.mobile === 'phone';
|
|
},
|
|
_addResourceView: function () {
|
|
var resourceView = new ResourceView(this.groups.length, this._isRtl);
|
|
this.groups.push(resourceView);
|
|
return resourceView;
|
|
},
|
|
dateForTitle: function () {
|
|
return kendo.format(this.options.selectedDateFormat, this.startDate(), this.endDate());
|
|
},
|
|
shortDateForTitle: function () {
|
|
return kendo.format(this.options.selectedShortDateFormat, this.startDate(), this.endDate());
|
|
},
|
|
_changeGroup: function (selection, previous) {
|
|
var method = previous ? 'prevGroupSlot' : 'nextGroupSlot';
|
|
var slot = this[method](selection.start, selection.groupIndex, selection.isAllDay);
|
|
if (slot) {
|
|
selection.groupIndex += previous ? -1 : 1;
|
|
}
|
|
return slot;
|
|
},
|
|
_changeGroupContinuously: function () {
|
|
return null;
|
|
},
|
|
_changeViewPeriod: function () {
|
|
return false;
|
|
},
|
|
_horizontalSlots: function (selection, ranges, multiple, reverse) {
|
|
var method = reverse ? 'leftSlot' : 'rightSlot';
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
var group = this.groups[selection.groupIndex];
|
|
if (!multiple) {
|
|
var slot = this._normalizeHorizontalSelection(selection, ranges, reverse);
|
|
if (slot) {
|
|
startSlot = endSlot = slot;
|
|
}
|
|
}
|
|
startSlot = group[method](startSlot);
|
|
endSlot = group[method](endSlot);
|
|
if (!multiple && !this._isVerticallyGrouped() && (!startSlot || !endSlot)) {
|
|
startSlot = endSlot = this._changeGroup(selection, reverse);
|
|
}
|
|
var continuousSlot;
|
|
if (!startSlot || !endSlot) {
|
|
continuousSlot = this._continuousSlot(selection, ranges, reverse);
|
|
continuousSlot = this._changeGroupContinuously(selection, continuousSlot, multiple, reverse);
|
|
if (continuousSlot) {
|
|
startSlot = endSlot = continuousSlot;
|
|
}
|
|
}
|
|
return {
|
|
startSlot: startSlot,
|
|
endSlot: endSlot
|
|
};
|
|
},
|
|
_verticalSlots: function (selection, ranges, multiple, reverse) {
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
var group = this.groups[selection.groupIndex];
|
|
if (!multiple) {
|
|
var slot = this._normalizeVerticalSelection(selection, ranges, reverse);
|
|
if (slot) {
|
|
startSlot = endSlot = slot;
|
|
}
|
|
}
|
|
var method = reverse ? 'upSlot' : 'downSlot';
|
|
startSlot = group[method](startSlot, multiple);
|
|
endSlot = group[method](endSlot, multiple);
|
|
if (!multiple && this._isVerticallyGrouped() && (!startSlot || !endSlot)) {
|
|
startSlot = endSlot = this._changeGroup(selection, reverse);
|
|
}
|
|
return {
|
|
startSlot: startSlot,
|
|
endSlot: endSlot
|
|
};
|
|
},
|
|
_normalizeHorizontalSelection: function () {
|
|
return null;
|
|
},
|
|
_normalizeVerticalSelection: function (selection, ranges, reverse) {
|
|
var slot;
|
|
if (reverse) {
|
|
slot = ranges[0].start;
|
|
} else {
|
|
slot = ranges[ranges.length - 1].end;
|
|
}
|
|
return slot;
|
|
},
|
|
_continuousSlot: function () {
|
|
return null;
|
|
},
|
|
constrainSelection: function (selection) {
|
|
var group = this.groups[0];
|
|
var slot;
|
|
if (!this.inRange(selection)) {
|
|
slot = group.firstSlot();
|
|
selection.isAllDay = slot.isDaySlot;
|
|
selection.start = slot.startDate();
|
|
selection.end = slot.endDate();
|
|
} else {
|
|
if (!group.daySlotCollectionCount()) {
|
|
selection.isAllDay = false;
|
|
}
|
|
}
|
|
if (!this.groups[selection.groupIndex]) {
|
|
selection.groupIndex = 0;
|
|
}
|
|
},
|
|
move: function (selection, key, shift) {
|
|
var handled = false;
|
|
var group = this.groups[selection.groupIndex];
|
|
if (!group.timeSlotCollectionCount()) {
|
|
selection.isAllDay = true;
|
|
}
|
|
var ranges = group.ranges(selection.start, selection.end, selection.isAllDay, false);
|
|
var startSlot, endSlot, reverse, slots;
|
|
if (key === keys.DOWN || key === keys.UP) {
|
|
handled = true;
|
|
reverse = key === keys.UP;
|
|
this._updateDirection(selection, ranges, shift, reverse, true);
|
|
slots = this._verticalSlots(selection, ranges, shift, reverse);
|
|
if (!slots.startSlot && !shift && this._changeViewPeriod(selection, reverse, true)) {
|
|
return handled;
|
|
}
|
|
} else if (key === keys.LEFT || key === keys.RIGHT) {
|
|
handled = true;
|
|
reverse = key === keys.LEFT;
|
|
this._updateDirection(selection, ranges, shift, reverse, false);
|
|
slots = this._horizontalSlots(selection, ranges, shift, reverse);
|
|
if (!slots.startSlot && !shift && this._changeViewPeriod(selection, reverse, false)) {
|
|
return handled;
|
|
}
|
|
}
|
|
if (handled) {
|
|
startSlot = slots.startSlot;
|
|
endSlot = slots.endSlot;
|
|
if (shift) {
|
|
var backward = selection.backward;
|
|
if (backward && startSlot) {
|
|
selection.start = startSlot.startDate();
|
|
} else if (!backward && endSlot) {
|
|
selection.end = endSlot.endDate();
|
|
}
|
|
} else if (startSlot && endSlot) {
|
|
selection.isAllDay = startSlot.isDaySlot;
|
|
selection.start = startSlot.startDate();
|
|
selection.end = endSlot.endDate();
|
|
}
|
|
selection.events = [];
|
|
}
|
|
return handled;
|
|
},
|
|
moveToEventInGroup: function (group, slot, selectedEvents, prev) {
|
|
var events = group._continuousEvents || [];
|
|
var found, event;
|
|
var pad = prev ? -1 : 1;
|
|
var length = events.length;
|
|
var idx = prev ? length - 1 : 0;
|
|
while (idx < length && idx > -1) {
|
|
event = events[idx];
|
|
if (!prev && event.start.startDate() >= slot.startDate() || prev && event.start.startDate() <= slot.startDate()) {
|
|
if (selectedEvents.length) {
|
|
event = events[idx + pad];
|
|
}
|
|
if (event && $.inArray(event.uid, selectedEvents) === -1) {
|
|
found = !!event;
|
|
break;
|
|
}
|
|
}
|
|
idx += pad;
|
|
}
|
|
return event;
|
|
},
|
|
moveToEvent: function (selection, prev) {
|
|
var groupIndex = selection.groupIndex;
|
|
var group = this.groups[groupIndex];
|
|
var slot = group.ranges(selection.start, selection.end, selection.isAllDay, false)[0].start;
|
|
var length = this.groups.length;
|
|
var pad = prev ? -1 : 1;
|
|
var events = selection.events;
|
|
var event;
|
|
while (groupIndex < length && groupIndex > -1) {
|
|
event = this.moveToEventInGroup(group, slot, events, prev);
|
|
groupIndex += pad;
|
|
group = this.groups[groupIndex];
|
|
if (!group || event) {
|
|
break;
|
|
}
|
|
events = [];
|
|
if (prev) {
|
|
slot = group.lastSlot();
|
|
} else {
|
|
slot = group.firstSlot(true);
|
|
}
|
|
}
|
|
if (event) {
|
|
selection.events = [event.uid];
|
|
selection.start = event.start.startDate();
|
|
selection.end = event.end.endDate();
|
|
selection.isAllDay = event.start.isDaySlot;
|
|
selection.groupIndex = event.start.groupIndex;
|
|
}
|
|
return !!event;
|
|
},
|
|
current: function (candidate) {
|
|
if (candidate !== undefined) {
|
|
this._current = candidate;
|
|
if (this.content.has(candidate)) {
|
|
this._scrollTo(candidate, this.content[0]);
|
|
}
|
|
} else {
|
|
return this._current;
|
|
}
|
|
},
|
|
select: function (selection) {
|
|
this.clearSelection();
|
|
if (!this._selectEvents(selection)) {
|
|
this._selectSlots(selection);
|
|
}
|
|
},
|
|
_selectSlots: function (selection) {
|
|
var isAllDay = selection.isAllDay;
|
|
var group = this.groups[selection.groupIndex];
|
|
if (!group.timeSlotCollectionCount()) {
|
|
isAllDay = true;
|
|
}
|
|
this._selectedSlots = [];
|
|
var ranges = group.ranges(selection.start, selection.end, isAllDay, false);
|
|
var element;
|
|
var slot;
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var collection = range.collection;
|
|
for (var slotIndex = range.start.index; slotIndex <= range.end.index; slotIndex++) {
|
|
slot = collection.at(slotIndex);
|
|
element = slot.element;
|
|
element.setAttribute('aria-selected', true);
|
|
addSelectedState(element);
|
|
this._selectedSlots.push({
|
|
start: slot.startDate(),
|
|
end: slot.endDate(),
|
|
element: element
|
|
});
|
|
}
|
|
}
|
|
if (selection.backward) {
|
|
element = ranges[0].start.element;
|
|
}
|
|
this.current(element);
|
|
},
|
|
_selectEvents: function (selection) {
|
|
var found = false;
|
|
var events = selection.events;
|
|
var groupEvents = this.groups[selection.groupIndex]._continuousEvents || [];
|
|
var idx, length = groupEvents.length;
|
|
if (!events[0] || !groupEvents[0]) {
|
|
return found;
|
|
}
|
|
var result = $();
|
|
selection.events = [];
|
|
for (idx = 0; idx < length; idx++) {
|
|
if ($.inArray(groupEvents[idx].uid, events) > -1) {
|
|
result = result.add(groupEvents[idx].element);
|
|
selection.events.push(groupEvents[idx].uid);
|
|
}
|
|
}
|
|
if (result[0]) {
|
|
result.addClass('k-state-selected').attr('aria-selected', true);
|
|
this.current(result.last()[0]);
|
|
this._selectedSlots = [];
|
|
found = true;
|
|
}
|
|
return found;
|
|
},
|
|
inRange: function (options) {
|
|
var startDate = this.startDate();
|
|
var endDate = kendo.date.addDays(this.endDate(), 1);
|
|
var start = options.start;
|
|
var end = options.end;
|
|
return startDate <= start && start < endDate && startDate < end && end <= endDate;
|
|
},
|
|
_resourceValue: function (resource, item) {
|
|
if (resource.valuePrimitive) {
|
|
item = kendo.getter(resource.dataValueField)(item);
|
|
}
|
|
return item;
|
|
},
|
|
_resourceBySlot: function (slot) {
|
|
var resources = this.groupedResources;
|
|
var result = {};
|
|
if (resources.length) {
|
|
var resourceIndex = slot.groupIndex;
|
|
for (var idx = resources.length - 1; idx >= 0; idx--) {
|
|
var resource = resources[idx];
|
|
var value = this._resourceValue(resource, resource.dataSource.view()[resourceIndex % resource.dataSource.total()]);
|
|
if (resource.multiple) {
|
|
value = [value];
|
|
}
|
|
var setter = kendo.setter(resource.field);
|
|
setter(result, value);
|
|
resourceIndex = Math.floor(resourceIndex / resource.dataSource.total());
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_createResizeHint: function (left, top, width, height) {
|
|
return $(HINT).css({
|
|
left: left,
|
|
top: top,
|
|
width: width,
|
|
height: height
|
|
});
|
|
},
|
|
_removeResizeHint: function () {
|
|
this._resizeHint.remove();
|
|
this._resizeHint = $();
|
|
},
|
|
_removeMoveHint: function () {
|
|
this._moveHint.remove();
|
|
this._moveHint = $();
|
|
},
|
|
_scrollTo: function (element, container) {
|
|
var elementOffset = element.offsetTop, elementOffsetDir = element.offsetHeight, containerScroll = container.scrollTop, containerOffsetDir = container.clientHeight, bottomDistance = elementOffset + elementOffsetDir, result = 0;
|
|
if (containerScroll > elementOffset) {
|
|
result = elementOffset;
|
|
} else if (bottomDistance > containerScroll + containerOffsetDir) {
|
|
if (elementOffsetDir <= containerOffsetDir) {
|
|
result = bottomDistance - containerOffsetDir;
|
|
} else {
|
|
result = elementOffset;
|
|
}
|
|
} else {
|
|
result = containerScroll;
|
|
}
|
|
container.scrollTop = result;
|
|
},
|
|
_shouldInverseResourceColor: function (resource) {
|
|
var resourceColorIsDark = new Color(resource.color).isDark();
|
|
var currentColor = this.element.css('color');
|
|
var currentColorIsDark = new Color(currentColor).isDark();
|
|
return resourceColorIsDark == currentColorIsDark;
|
|
},
|
|
_eventTmpl: function (template, wrapper) {
|
|
var options = this.options, settings = $.extend({}, kendo.Template, options.templateSettings), paramName = settings.paramName, html = '', type = typeof template, state = {
|
|
storage: {},
|
|
count: 0
|
|
};
|
|
if (type === 'function') {
|
|
state.storage['tmpl' + state.count] = template;
|
|
html += '#=this.tmpl' + state.count + '(' + paramName + ')#';
|
|
state.count++;
|
|
} else if (type === 'string') {
|
|
html += template;
|
|
}
|
|
var tmpl = kendo.template(kendo.format(wrapper, html), settings);
|
|
if (state.count > 0) {
|
|
tmpl = $.proxy(tmpl, state.storage);
|
|
}
|
|
return tmpl;
|
|
},
|
|
eventResources: function (event) {
|
|
var resources = [], options = this.options;
|
|
if (!options.resources) {
|
|
return resources;
|
|
}
|
|
for (var idx = 0; idx < options.resources.length; idx++) {
|
|
var resource = options.resources[idx];
|
|
var field = resource.field;
|
|
var eventResources = kendo.getter(field)(event);
|
|
if (eventResources == null) {
|
|
continue;
|
|
}
|
|
if (!resource.multiple) {
|
|
eventResources = [eventResources];
|
|
}
|
|
var data = resource.dataSource.view();
|
|
for (var resourceIndex = 0; resourceIndex < eventResources.length; resourceIndex++) {
|
|
var eventResource = null;
|
|
var value = eventResources[resourceIndex];
|
|
if (!resource.valuePrimitive) {
|
|
value = kendo.getter(resource.dataValueField)(value);
|
|
}
|
|
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
|
|
if (data[dataIndex].get(resource.dataValueField) == value) {
|
|
eventResource = data[dataIndex];
|
|
break;
|
|
}
|
|
}
|
|
if (eventResource !== null) {
|
|
var resourceColor = kendo.getter(resource.dataColorField)(eventResource);
|
|
resources.push({
|
|
field: resource.field,
|
|
title: resource.title,
|
|
name: resource.name,
|
|
text: kendo.getter(resource.dataTextField)(eventResource),
|
|
value: value,
|
|
color: resourceColor
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return resources;
|
|
},
|
|
createLayout: function (layout) {
|
|
var allDayIndex = -1;
|
|
if (!layout.rows) {
|
|
layout.rows = [];
|
|
}
|
|
for (var idx = 0; idx < layout.rows.length; idx++) {
|
|
if (layout.rows[idx].allDay) {
|
|
allDayIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
var allDaySlot = layout.rows[allDayIndex];
|
|
if (allDayIndex >= 0) {
|
|
layout.rows.splice(allDayIndex, 1);
|
|
}
|
|
var columnLevels = this.columnLevels = levels(layout, 'columns');
|
|
var rowLevels = this.rowLevels = levels(layout, 'rows');
|
|
this.table = $('<table ' + cellspacing() + ' class="k-scheduler-layout k-scheduler-' + this.name + 'view"/>');
|
|
var rowCount = rowLevels[rowLevels.length - 1].length;
|
|
this.table.append(this._topSection(columnLevels, allDaySlot, rowCount));
|
|
this.table.append(this._bottomSection(columnLevels, rowLevels, rowCount));
|
|
this.element.append(this.table);
|
|
this._scroller();
|
|
},
|
|
refreshLayout: function () {
|
|
var that = this, toolbar = that.element.find('>.k-scheduler-toolbar'), height = that.element.innerHeight(), scrollbar = this._scrollbar, headerHeight = 0, paddingDirection = this._isRtl ? 'left' : 'right';
|
|
for (var idx = 0; idx < toolbar.length; idx++) {
|
|
height -= toolbar.eq(idx).outerHeight();
|
|
}
|
|
if (that.datesHeader) {
|
|
headerHeight = that.datesHeader.outerHeight();
|
|
}
|
|
if (that.timesHeader && that.timesHeader.outerHeight() > headerHeight) {
|
|
headerHeight = that.timesHeader.outerHeight();
|
|
}
|
|
if (that.datesHeader && that.timesHeader) {
|
|
var datesHeaderRows = that.datesHeader.find('table:first tr');
|
|
that.timesHeader.find('tr').height(function (index) {
|
|
$(this).height(datesHeaderRows.eq(index).height());
|
|
});
|
|
}
|
|
if (headerHeight) {
|
|
height -= headerHeight;
|
|
}
|
|
if (that.footer) {
|
|
height -= that.footer.outerHeight();
|
|
}
|
|
var isSchedulerHeightSet = function (el) {
|
|
var initialHeight, newHeight;
|
|
if (el[0].style.height) {
|
|
return true;
|
|
} else {
|
|
initialHeight = el.height();
|
|
}
|
|
el.height('auto');
|
|
newHeight = el.height();
|
|
if (initialHeight != newHeight) {
|
|
el.height('');
|
|
return true;
|
|
}
|
|
el.height('');
|
|
return false;
|
|
};
|
|
var contentDiv = that.content[0], scrollbarWidth = !kendo.support.kineticScrollNeeded ? scrollbar : 0;
|
|
if (isSchedulerHeightSet(that.element)) {
|
|
if (height > scrollbar * 2) {
|
|
that.content.height(height);
|
|
} else {
|
|
that.content.height(scrollbar * 2 + 1);
|
|
}
|
|
that.times.height(contentDiv.clientHeight);
|
|
var timesTable = that.times.find('table');
|
|
if (timesTable.length) {
|
|
timesTable.height(that.content.find('table')[0].clientHeight);
|
|
}
|
|
}
|
|
if (contentDiv.offsetWidth - contentDiv.clientWidth > 0) {
|
|
that.table.addClass('k-scrollbar-v');
|
|
that.datesHeader.css('padding-' + paddingDirection, scrollbarWidth - parseInt(that.datesHeader.children().css('border-' + paddingDirection + '-width'), 10));
|
|
} else {
|
|
that.datesHeader.css('padding-' + paddingDirection, '');
|
|
}
|
|
if (contentDiv.offsetHeight - contentDiv.clientHeight > 0 || contentDiv.clientHeight > that.content.children('.k-scheduler-table').height()) {
|
|
that.table.addClass('k-scrollbar-h');
|
|
} else {
|
|
that.table.removeClass('k-scrollbar-h');
|
|
}
|
|
},
|
|
_topSection: function (columnLevels, allDaySlot, rowCount) {
|
|
this.timesHeader = timesHeader(columnLevels.length, allDaySlot, rowCount);
|
|
var columnCount = columnLevels[columnLevels.length - 1].length;
|
|
this.datesHeader = datesHeader(columnLevels, columnCount, allDaySlot);
|
|
return $('<tr>').append(this.timesHeader.add(this.datesHeader).wrap('<td>').parent());
|
|
},
|
|
_bottomSection: function (columnLevels, rowLevels, rowCount) {
|
|
this.times = times(rowLevels, rowCount);
|
|
this.content = content(columnLevels[columnLevels.length - 1], rowLevels[rowLevels.length - 1]);
|
|
return $('<tr>').append(this.times.add(this.content).wrap('<td>').parent());
|
|
},
|
|
_scroller: function () {
|
|
var that = this;
|
|
this.content.bind('scroll' + NS, function () {
|
|
that.datesHeader.find('>.k-scheduler-header-wrap').scrollLeft(this.scrollLeft);
|
|
that.times.scrollTop(this.scrollTop);
|
|
});
|
|
var touchScroller = kendo.touchScroller(this.content, {
|
|
avoidScrolling: function (e) {
|
|
return $(e.event.target).closest('.k-event.k-event-active').length > 0;
|
|
}
|
|
});
|
|
if (touchScroller && touchScroller.movable) {
|
|
this._touchScroller = touchScroller;
|
|
this.content = touchScroller.scrollElement;
|
|
touchScroller.movable.bind('change', function (e) {
|
|
that.datesHeader.find('>.k-scheduler-header-wrap').scrollLeft(-e.sender.x);
|
|
that.times.scrollTop(-e.sender.y);
|
|
});
|
|
}
|
|
},
|
|
_resourcesForGroups: function () {
|
|
var result = [];
|
|
var groups = this.options.group;
|
|
var resources = this.options.resources;
|
|
groups = groups && groups.resources ? groups.resources : [];
|
|
if (resources && groups.length) {
|
|
for (var idx = 0, length = resources.length; idx < length; idx++) {
|
|
for (var groupIdx = 0, groupLength = groups.length; groupIdx < groupLength; groupIdx++) {
|
|
if (resources[idx].name === groups[groupIdx]) {
|
|
result.push(resources[idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.groupedResources = result;
|
|
},
|
|
_createColumnsLayout: function (resources, inner, template) {
|
|
return createLayoutConfiguration('columns', resources, inner, template);
|
|
},
|
|
_groupOrientation: function () {
|
|
var groups = this.options.group;
|
|
return groups && groups.resources ? groups.orientation : 'horizontal';
|
|
},
|
|
_isVerticallyGrouped: function () {
|
|
return this.groupedResources.length && this._groupOrientation() === 'vertical';
|
|
},
|
|
_createRowsLayout: function (resources, inner, template) {
|
|
return createLayoutConfiguration('rows', resources, inner, template);
|
|
},
|
|
selectionByElement: function () {
|
|
return null;
|
|
},
|
|
clearSelection: function () {
|
|
this.content.find('.k-state-selected').removeAttr('id').attr('aria-selected', false).removeClass('k-state-selected');
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(this);
|
|
if (that.table) {
|
|
kendo.destroy(that.table);
|
|
that.table.remove();
|
|
}
|
|
that.groups = null;
|
|
that.table = null;
|
|
that.content = null;
|
|
that.times = null;
|
|
that.datesHeader = null;
|
|
that.timesHeader = null;
|
|
that.footer = null;
|
|
that._resizeHint = null;
|
|
that._moveHint = null;
|
|
},
|
|
calendarInfo: function () {
|
|
return kendo.getCulture().calendars.standard;
|
|
},
|
|
prevGroupSlot: function (date, groupIndex, isDay) {
|
|
var collection;
|
|
var group = this.groups[groupIndex];
|
|
var slot = group.ranges(date, date, isDay, false)[0].start;
|
|
if (groupIndex <= 0) {
|
|
return;
|
|
}
|
|
if (this._isVerticallyGrouped()) {
|
|
if (!group.timeSlotCollectionCount()) {
|
|
collection = group._collection(group.daySlotCollectionCount() - 1, true);
|
|
return collection.at(slot.index);
|
|
} else {
|
|
collection = group._collection(isDay ? slot.index : slot.collectionIndex, false);
|
|
return collection.last();
|
|
}
|
|
} else {
|
|
if (!group.timeSlotCollectionCount()) {
|
|
collection = group._collection(slot.collectionIndex, true);
|
|
return collection.last();
|
|
} else {
|
|
collection = group._collection(isDay ? 0 : group.timeSlotCollectionCount() - 1, isDay);
|
|
return isDay ? collection.last() : collection.at(slot.index);
|
|
}
|
|
}
|
|
},
|
|
nextGroupSlot: function (date, groupIndex, isDay) {
|
|
var collection;
|
|
var group = this.groups[groupIndex];
|
|
var slot = group.ranges(date, date, isDay, false)[0].start;
|
|
var daySlotCollectionCount;
|
|
if (groupIndex >= this.groups.length - 1) {
|
|
return;
|
|
}
|
|
if (this._isVerticallyGrouped()) {
|
|
if (!group.timeSlotCollectionCount()) {
|
|
collection = group._collection(0, true);
|
|
return collection.at(slot.index);
|
|
} else {
|
|
daySlotCollectionCount = group.daySlotCollectionCount();
|
|
collection = group._collection(daySlotCollectionCount ? 0 : slot.collectionIndex, daySlotCollectionCount);
|
|
return isDay ? collection.first() : collection.at(slot.collectionIndex);
|
|
}
|
|
} else {
|
|
if (!group.timeSlotCollectionCount()) {
|
|
collection = group._collection(slot.collectionIndex, true);
|
|
return collection.first();
|
|
} else {
|
|
collection = group._collection(0, isDay);
|
|
return isDay ? collection.first() : collection.at(slot.index);
|
|
}
|
|
}
|
|
},
|
|
_eventOptionsForMove: function () {
|
|
return {};
|
|
},
|
|
_updateEventForResize: function () {
|
|
return;
|
|
},
|
|
_updateEventForSelection: function (event) {
|
|
return event;
|
|
}
|
|
});
|
|
function collidingEvents(elements, start, end) {
|
|
var idx, index, startIndex, overlaps, endIndex;
|
|
for (idx = elements.length - 1; idx >= 0; idx--) {
|
|
index = rangeIndex(elements[idx]);
|
|
startIndex = index.start;
|
|
endIndex = index.end;
|
|
overlaps = startIndex <= start && endIndex >= start;
|
|
if (overlaps || startIndex >= start && endIndex <= end || start <= startIndex && end >= startIndex) {
|
|
if (startIndex < start) {
|
|
start = startIndex;
|
|
}
|
|
if (endIndex > end) {
|
|
end = endIndex;
|
|
}
|
|
}
|
|
}
|
|
return eventsForSlot(elements, start, end);
|
|
}
|
|
function rangeIndex(eventElement) {
|
|
return {
|
|
start: eventElement.start,
|
|
end: eventElement.end
|
|
};
|
|
}
|
|
function eventsForSlot(elements, slotStart, slotEnd) {
|
|
var events = [];
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
var event = rangeIndex(elements[idx]);
|
|
if (event.start < slotStart && event.end > slotStart || event.start >= slotStart && event.end <= slotEnd) {
|
|
events.push(elements[idx]);
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
function createColumns(eventElements) {
|
|
return _createColumns(eventElements);
|
|
}
|
|
function createRows(eventElements) {
|
|
return _createColumns(eventElements);
|
|
}
|
|
var Color = function (value) {
|
|
var color = this, formats = Color.formats, re, processor, parts, i, channels;
|
|
if (arguments.length === 1) {
|
|
value = color.resolveColor(value);
|
|
for (i = 0; i < formats.length; i++) {
|
|
re = formats[i].re;
|
|
processor = formats[i].process;
|
|
parts = re.exec(value);
|
|
if (parts) {
|
|
channels = processor(parts);
|
|
color.r = channels[0];
|
|
color.g = channels[1];
|
|
color.b = channels[2];
|
|
}
|
|
}
|
|
} else {
|
|
color.r = arguments[0];
|
|
color.g = arguments[1];
|
|
color.b = arguments[2];
|
|
}
|
|
color.r = color.normalizeByte(color.r);
|
|
color.g = color.normalizeByte(color.g);
|
|
color.b = color.normalizeByte(color.b);
|
|
};
|
|
Color.prototype = {
|
|
resolveColor: function (value) {
|
|
value = value || '#000';
|
|
if (value.charAt(0) == '#') {
|
|
value = value.substr(1, 6);
|
|
}
|
|
value = value.replace(/ /g, '');
|
|
value = value.toLowerCase();
|
|
value = Color.namedColors[value] || value;
|
|
return value;
|
|
},
|
|
normalizeByte: function (value) {
|
|
return value < 0 || isNaN(value) ? 0 : value > 255 ? 255 : value;
|
|
},
|
|
percBrightness: function () {
|
|
var color = this;
|
|
return math.sqrt(0.241 * color.r * color.r + 0.691 * color.g * color.g + 0.068 * color.b * color.b);
|
|
},
|
|
isDark: function () {
|
|
var color = this;
|
|
var brightnessValue = color.percBrightness();
|
|
return brightnessValue < 180;
|
|
}
|
|
};
|
|
Color.formats = [
|
|
{
|
|
re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1], 10),
|
|
parseInt(parts[2], 10),
|
|
parseInt(parts[3], 10)
|
|
];
|
|
}
|
|
},
|
|
{
|
|
re: /^(\w{2})(\w{2})(\w{2})$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1], 16),
|
|
parseInt(parts[2], 16),
|
|
parseInt(parts[3], 16)
|
|
];
|
|
}
|
|
},
|
|
{
|
|
re: /^(\w{1})(\w{1})(\w{1})$/,
|
|
process: function (parts) {
|
|
return [
|
|
parseInt(parts[1] + parts[1], 16),
|
|
parseInt(parts[2] + parts[2], 16),
|
|
parseInt(parts[3] + parts[3], 16)
|
|
];
|
|
}
|
|
}
|
|
];
|
|
Color.namedColors = {
|
|
aqua: '00ffff',
|
|
azure: 'f0ffff',
|
|
beige: 'f5f5dc',
|
|
black: '000000',
|
|
blue: '0000ff',
|
|
brown: 'a52a2a',
|
|
coral: 'ff7f50',
|
|
cyan: '00ffff',
|
|
darkblue: '00008b',
|
|
darkcyan: '008b8b',
|
|
darkgray: 'a9a9a9',
|
|
darkgreen: '006400',
|
|
darkorange: 'ff8c00',
|
|
darkred: '8b0000',
|
|
dimgray: '696969',
|
|
fuchsia: 'ff00ff',
|
|
gold: 'ffd700',
|
|
goldenrod: 'daa520',
|
|
gray: '808080',
|
|
green: '008000',
|
|
greenyellow: 'adff2f',
|
|
indigo: '4b0082',
|
|
ivory: 'fffff0',
|
|
khaki: 'f0e68c',
|
|
lightblue: 'add8e6',
|
|
lightgrey: 'd3d3d3',
|
|
lightgreen: '90ee90',
|
|
lightpink: 'ffb6c1',
|
|
lightyellow: 'ffffe0',
|
|
lime: '00ff00',
|
|
limegreen: '32cd32',
|
|
linen: 'faf0e6',
|
|
magenta: 'ff00ff',
|
|
maroon: '800000',
|
|
mediumblue: '0000cd',
|
|
navy: '000080',
|
|
olive: '808000',
|
|
orange: 'ffa500',
|
|
orangered: 'ff4500',
|
|
orchid: 'da70d6',
|
|
pink: 'ffc0cb',
|
|
plum: 'dda0dd',
|
|
purple: '800080',
|
|
red: 'ff0000',
|
|
royalblue: '4169e1',
|
|
salmon: 'fa8072',
|
|
silver: 'c0c0c0',
|
|
skyblue: '87ceeb',
|
|
slateblue: '6a5acd',
|
|
slategray: '708090',
|
|
snow: 'fffafa',
|
|
steelblue: '4682b4',
|
|
tan: 'd2b48c',
|
|
teal: '008080',
|
|
tomato: 'ff6347',
|
|
turquoise: '40e0d0',
|
|
violet: 'ee82ee',
|
|
wheat: 'f5deb3',
|
|
white: 'ffffff',
|
|
whitesmoke: 'f5f5f5',
|
|
yellow: 'ffff00',
|
|
yellowgreen: '9acd32'
|
|
};
|
|
function _createColumns(eventElements) {
|
|
var columns = [];
|
|
for (var idx = 0; idx < eventElements.length; idx++) {
|
|
var event = eventElements[idx];
|
|
var eventRange = rangeIndex(event);
|
|
var column = null;
|
|
for (var j = 0, columnLength = columns.length; j < columnLength; j++) {
|
|
var endOverlaps = eventRange.start > columns[j].end;
|
|
if (eventRange.start < columns[j].start || endOverlaps) {
|
|
column = columns[j];
|
|
if (column.end < eventRange.end) {
|
|
column.end = eventRange.end;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!column) {
|
|
column = {
|
|
start: eventRange.start,
|
|
end: eventRange.end,
|
|
events: []
|
|
};
|
|
columns.push(column);
|
|
}
|
|
column.events.push(event);
|
|
}
|
|
return columns;
|
|
}
|
|
function createLayoutConfiguration(name, resources, inner, template) {
|
|
var resource = resources[0];
|
|
if (resource) {
|
|
var configuration = [];
|
|
var data = resource.dataSource.view();
|
|
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
|
|
var obj = {
|
|
text: template({
|
|
text: kendo.htmlEncode(kendo.getter(resource.dataTextField)(data[dataIndex])),
|
|
color: kendo.getter(resource.dataColorField)(data[dataIndex]),
|
|
field: resource.field,
|
|
title: resource.title,
|
|
name: resource.name,
|
|
value: kendo.getter(resource.dataValueField)(data[dataIndex])
|
|
}),
|
|
className: 'k-slot-cell'
|
|
};
|
|
obj[name] = createLayoutConfiguration(name, resources.slice(1), inner, template);
|
|
configuration.push(obj);
|
|
}
|
|
return configuration;
|
|
}
|
|
return inner;
|
|
}
|
|
function groupEqFilter(value) {
|
|
return function (item) {
|
|
if ($.isArray(item) || item instanceof kendo.data.ObservableArray) {
|
|
for (var idx = 0; idx < item.length; idx++) {
|
|
if (item[idx] == value) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
return item == value;
|
|
};
|
|
}
|
|
var selectedStateRegExp = /\s*k-state-selected/;
|
|
function addSelectedState(cell) {
|
|
cell.className = cell.className.replace(selectedStateRegExp, '') + ' k-state-selected';
|
|
}
|
|
$.extend(ui.SchedulerView, {
|
|
createColumns: createColumns,
|
|
createRows: createRows,
|
|
rangeIndex: rangeIndex,
|
|
collidingEvents: collidingEvents,
|
|
groupEqFilter: groupEqFilter
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.dayview', ['kendo.scheduler.view'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.dayview',
|
|
name: 'Scheduler Day View',
|
|
category: 'web',
|
|
description: 'The Scheduler Day View',
|
|
depends: ['scheduler.view'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, setTime = kendo.date.setTime, SchedulerView = ui.SchedulerView, extend = $.extend, proxy = $.proxy, getDate = kendo.date.getDate, MS_PER_MINUTE = kendo.date.MS_PER_MINUTE, MS_PER_DAY = kendo.date.MS_PER_DAY, getMilliseconds = kendo.date.getMilliseconds, NS = '.kendoMultiDayView';
|
|
var DAY_VIEW_EVENT_TEMPLATE = kendo.template('<div title="(#=kendo.format("{0:t} - {1:t}", start, end)#): #=title.replace(/"/g,"&\\#34;")#">' + '<div class="k-event-template k-event-time">#:kendo.format("{0:t} - {1:t}", start, end)#</div>' + '<div class="k-event-template">${title}</div>' + '</div>'), DAY_VIEW_ALL_DAY_EVENT_TEMPLATE = kendo.template('<div title="(#=kendo.format("{0:t}", start)#): #=title.replace(/"/g,"&\\#34;")#">' + '<div class="k-event-template">${title}</div>' + '</div>'), DATA_HEADER_TEMPLATE = kendo.template('<span class=\'k-link k-nav-day\'>#=kendo.toString(date, \'ddd M/dd\')#</span>'), ALLDAY_EVENT_WRAPPER_STRING = '<div role="gridcell" aria-selected="false" ' + 'data-#=ns#uid="#=uid#"' + '#if (resources[0]) { #' + 'style="background-color:#=resources[0].color#; border-color: #=resources[0].color#"' + 'class="k-event#=inverseColor ? " k-event-inverse" : ""#" ' + '#} else {#' + 'class="k-event"' + '#}#' + '>' + '<span class="k-event-actions">' + '# if(data.tail || data.middle) {#' + '<span class="k-icon k-i-arrow-w"></span>' + '#}#' + '# if(data.isException()) {#' + '<span class="k-icon k-i-exception"></span>' + '# } else if(data.isRecurring()) {#' + '<span class="k-icon k-i-refresh"></span>' + '# } #' + '</span>' + '{0}' + '<span class="k-event-actions">' + '#if (showDelete) {#' + '<a href="\\#" class="k-link k-event-delete"><span class="k-icon k-si-close"></span></a>' + '#}#' + '# if(data.head || data.middle) {#' + '<span class="k-icon k-i-arrow-e"></span>' + '#}#' + '</span>' + '#if(resizable && !singleDay && !data.tail && !data.middle){#' + '<span class="k-resize-handle k-resize-w"></span>' + '#}#' + '#if(resizable && !singleDay && !data.head && !data.middle){#' + '<span class="k-resize-handle k-resize-e"></span>' + '#}#' + '</div>', EVENT_WRAPPER_STRING = '<div role="gridcell" aria-selected="false" ' + 'data-#=ns#uid="#=uid#" ' + '#if (resources[0]) { #' + 'style="background-color:#=resources[0].color #; border-color: #=resources[0].color#"' + 'class="k-event#=inverseColor ? " k-event-inverse" : ""#"' + '#} else {#' + 'class="k-event"' + '#}#' + '>' + '<span class="k-event-actions">' + '# if(data.isException()) {#' + '<span class="k-icon k-i-exception"></span>' + '# } else if(data.isRecurring()) {#' + '<span class="k-icon k-i-refresh"></span>' + '# } #' + '</span>' + '{0}' + '<span class="k-event-actions">' + '#if (showDelete) {#' + '<a href="\\#" class="k-link k-event-delete"><span class="k-icon k-si-close"></span></a>' + '#}#' + '</span>' + '<span class="k-event-top-actions">' + '# if(data.tail || data.middle) {#' + '<span class="k-icon k-i-arrow-n"></span>' + '# } #' + '</span>' + '<span class="k-event-bottom-actions">' + '# if(data.head || data.middle) {#' + '<span class="k-icon k-i-arrow-s"></span>' + '# } #' + '</span>' + '# if(resizable && !data.tail && !data.middle) {#' + '<span class="k-resize-handle k-resize-n"></span>' + '# } #' + '# if(resizable && !data.head && !data.middle) {#' + '<span class="k-resize-handle k-resize-s"></span>' + '# } #' + '</div>';
|
|
function toInvariantTime(date) {
|
|
var staticDate = new Date(1980, 1, 1, 0, 0, 0);
|
|
setTime(staticDate, getMilliseconds(date));
|
|
return staticDate;
|
|
}
|
|
function isInDateRange(value, min, max) {
|
|
return value >= min && value <= max;
|
|
}
|
|
function isInTimeRange(value, min, max, overlaps) {
|
|
overlaps = overlaps ? value <= max : value < max;
|
|
return value > min && overlaps;
|
|
}
|
|
function addContinuousEvent(group, range, element, isAllDay) {
|
|
var events = group._continuousEvents;
|
|
var lastEvent = events[events.length - 1];
|
|
var startDate = getDate(range.start.startDate()).getTime();
|
|
if (isAllDay && lastEvent && getDate(lastEvent.start.startDate()).getTime() == startDate) {
|
|
var idx = events.length - 1;
|
|
for (; idx > -1; idx--) {
|
|
if (events[idx].isAllDay || getDate(events[idx].start.startDate()).getTime() < startDate) {
|
|
break;
|
|
}
|
|
}
|
|
events.splice(idx + 1, 0, {
|
|
element: element,
|
|
isAllDay: true,
|
|
uid: element.attr(kendo.attr('uid')),
|
|
start: range.start,
|
|
end: range.end
|
|
});
|
|
} else {
|
|
events.push({
|
|
element: element,
|
|
isAllDay: isAllDay,
|
|
uid: element.attr(kendo.attr('uid')),
|
|
start: range.start,
|
|
end: range.end
|
|
});
|
|
}
|
|
}
|
|
function getWorkDays(options) {
|
|
var workDays = [];
|
|
var dayIndex = options.workWeekStart;
|
|
workDays.push(dayIndex);
|
|
while (options.workWeekEnd != dayIndex) {
|
|
if (dayIndex > 6) {
|
|
dayIndex -= 7;
|
|
} else {
|
|
dayIndex++;
|
|
}
|
|
workDays.push(dayIndex);
|
|
}
|
|
return workDays;
|
|
}
|
|
var MultiDayView = SchedulerView.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
SchedulerView.fn.init.call(that, element, options);
|
|
that.title = that.options.title || that.options.name;
|
|
that._workDays = getWorkDays(that.options);
|
|
that._templates();
|
|
that._editable();
|
|
that.calculateDateRange();
|
|
that._groups();
|
|
that._currentTime();
|
|
},
|
|
_currentTimeMarkerUpdater: function () {
|
|
var currentTime = new Date();
|
|
var options = this.options;
|
|
if (options.currentTimeMarker.useLocalTimezone === false) {
|
|
var timezone = options.dataSource.options.schema.timezone;
|
|
if (options.dataSource && timezone) {
|
|
var timezoneOffset = kendo.timezone.offset(currentTime, timezone);
|
|
currentTime = kendo.timezone.convert(currentTime, currentTime.getTimezoneOffset(), timezoneOffset);
|
|
}
|
|
}
|
|
this.times.find('.k-current-time').remove();
|
|
var groupsCount = !options.group || options.group.orientation == 'horizontal' ? 1 : this.groups.length;
|
|
for (var groupIndex = 0; groupIndex < groupsCount; groupIndex++) {
|
|
var currentGroup = this.groups[groupIndex];
|
|
var utcCurrentTime = kendo.date.toUtcTime(currentTime);
|
|
var ranges = currentGroup.timeSlotRanges(utcCurrentTime, utcCurrentTime + 1);
|
|
if (ranges.length === 0) {
|
|
return;
|
|
}
|
|
var collection = ranges[0].collection;
|
|
var slotElement = collection.slotByStartDate(currentTime);
|
|
if (slotElement) {
|
|
var element = $('<div class=\'k-current-time\'></div>');
|
|
element.appendTo(this.times).css({
|
|
top: Math.round(ranges[0].innerRect(currentTime, new Date(currentTime.getTime() + 1), false).top),
|
|
height: '1px',
|
|
right: '1px',
|
|
left: 0
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_currentTime: function () {
|
|
var that = this;
|
|
var markerOptions = that.options.currentTimeMarker;
|
|
if (markerOptions !== false && markerOptions.updateInterval !== undefined) {
|
|
var updateInterval = markerOptions.updateInterval;
|
|
that._currentTimeMarkerUpdater();
|
|
that._currentTimeUpdateTimer = setInterval(proxy(this._currentTimeMarkerUpdater, that), updateInterval);
|
|
}
|
|
},
|
|
_updateResizeHint: function (event, groupIndex, startTime, endTime) {
|
|
var multiday = event.isMultiDay();
|
|
var group = this.groups[groupIndex];
|
|
var ranges = group.ranges(startTime, endTime, multiday, event.isAllDay);
|
|
this._removeResizeHint();
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var start = range.startSlot();
|
|
var width = start.offsetWidth;
|
|
var height = start.clientHeight;
|
|
var top = start.offsetTop;
|
|
if (multiday) {
|
|
width = range.innerWidth();
|
|
} else {
|
|
var rect = range.outerRect(startTime, endTime, this.options.snap);
|
|
top = rect.top;
|
|
height = rect.bottom - rect.top;
|
|
}
|
|
var hint = SchedulerView.fn._createResizeHint.call(this, start.offsetLeft, top, width, height);
|
|
this._resizeHint = this._resizeHint.add(hint);
|
|
}
|
|
var format = 't';
|
|
var container = this.content;
|
|
if (multiday) {
|
|
format = 'M/dd';
|
|
container = this.element.find('.k-scheduler-header-wrap:has(.k-scheduler-header-all-day) > div');
|
|
if (!container.length) {
|
|
container = this.content;
|
|
}
|
|
}
|
|
this._resizeHint.appendTo(container);
|
|
this._resizeHint.find('.k-label-top,.k-label-bottom').text('');
|
|
this._resizeHint.first().addClass('k-first').find('.k-label-top').text(kendo.toString(kendo.timezone.toLocalDate(startTime), format));
|
|
this._resizeHint.last().addClass('k-last').find('.k-label-bottom').text(kendo.toString(kendo.timezone.toLocalDate(endTime), format));
|
|
},
|
|
_updateMoveHint: function (event, groupIndex, distance) {
|
|
var multiday = event.isMultiDay();
|
|
var group = this.groups[groupIndex];
|
|
var start = kendo.date.toUtcTime(event.start) + distance;
|
|
var end = start + event.duration();
|
|
var ranges = group.ranges(start, end, multiday, event.isAllDay);
|
|
start = kendo.timezone.toLocalDate(start);
|
|
end = kendo.timezone.toLocalDate(end);
|
|
this._removeMoveHint();
|
|
if (!multiday && (getMilliseconds(end) === 0 || getMilliseconds(end) < getMilliseconds(this.startTime()))) {
|
|
if (ranges.length > 1) {
|
|
ranges.pop();
|
|
}
|
|
}
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var startSlot = range.start;
|
|
var hint = this._createEventElement(event.clone({
|
|
start: start,
|
|
end: end
|
|
}), !multiday);
|
|
hint.addClass('k-event-drag-hint');
|
|
var css = {
|
|
left: startSlot.offsetLeft + 2,
|
|
top: startSlot.offsetTop
|
|
};
|
|
if (this._isRtl) {
|
|
css.left = startSlot.clientWidth * 0.1 + startSlot.offsetLeft + 2;
|
|
}
|
|
if (multiday) {
|
|
css.width = range.innerWidth() - 4;
|
|
} else {
|
|
var rect = range.outerRect(start, end, this.options.snap);
|
|
css.top = rect.top;
|
|
css.height = rect.bottom - rect.top;
|
|
css.width = startSlot.clientWidth * 0.9 - 4;
|
|
}
|
|
hint.css(css);
|
|
this._moveHint = this._moveHint.add(hint);
|
|
}
|
|
var content = this.content;
|
|
if (multiday) {
|
|
content = this.element.find('.k-scheduler-header-wrap:has(.k-scheduler-header-all-day) > div');
|
|
if (!content.length) {
|
|
content = this.content;
|
|
}
|
|
}
|
|
this._moveHint.appendTo(content);
|
|
},
|
|
_slotByPosition: function (x, y) {
|
|
var slot;
|
|
var offset;
|
|
if (this._isVerticallyGrouped()) {
|
|
offset = this.content.offset();
|
|
y += this.content[0].scrollTop;
|
|
x += this.content[0].scrollLeft;
|
|
} else {
|
|
offset = this.element.find('.k-scheduler-header-wrap:has(.k-scheduler-header-all-day)').find('>div').offset();
|
|
}
|
|
if (offset) {
|
|
x -= offset.left;
|
|
y -= offset.top;
|
|
}
|
|
x = Math.ceil(x);
|
|
y = Math.ceil(y);
|
|
var group;
|
|
var groupIndex;
|
|
for (groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
group = this.groups[groupIndex];
|
|
slot = group.daySlotByPosition(x, y);
|
|
if (slot) {
|
|
return slot;
|
|
}
|
|
}
|
|
if (offset) {
|
|
x += offset.left;
|
|
y += offset.top;
|
|
}
|
|
offset = this.content.offset();
|
|
x -= offset.left;
|
|
y -= offset.top;
|
|
if (!this._isVerticallyGrouped()) {
|
|
y += this.content[0].scrollTop;
|
|
x += this.content[0].scrollLeft;
|
|
}
|
|
x = Math.ceil(x);
|
|
y = Math.ceil(y);
|
|
for (groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
group = this.groups[groupIndex];
|
|
slot = group.timeSlotByPosition(x, y);
|
|
if (slot) {
|
|
return slot;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
_groupCount: function () {
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (this._groupOrientation() === 'vertical') {
|
|
return this._rowCountForLevel(resources.length - 1);
|
|
} else {
|
|
return this._columnCountForLevel(resources.length) / this._columnOffsetForResource(resources.length);
|
|
}
|
|
}
|
|
return 1;
|
|
},
|
|
_columnCountInResourceView: function () {
|
|
var resources = this.groupedResources;
|
|
if (!resources.length || this._isVerticallyGrouped()) {
|
|
return this._columnCountForLevel(0);
|
|
}
|
|
return this._columnOffsetForResource(resources.length);
|
|
},
|
|
_timeSlotGroups: function (groupCount, columnCount) {
|
|
var interval = this._timeSlotInterval();
|
|
var tableRows = this.content.find('tr:not(.k-scheduler-header-all-day)');
|
|
tableRows.attr('role', 'row');
|
|
var rowCount = tableRows.length;
|
|
if (this._isVerticallyGrouped()) {
|
|
rowCount = Math.floor(rowCount / groupCount);
|
|
}
|
|
for (var groupIndex = 0; groupIndex < groupCount; groupIndex++) {
|
|
var rowMultiplier = 0;
|
|
if (this._isVerticallyGrouped()) {
|
|
rowMultiplier = groupIndex;
|
|
}
|
|
var rowIndex = rowMultiplier * rowCount;
|
|
var time;
|
|
var cellMultiplier = 0;
|
|
if (!this._isVerticallyGrouped()) {
|
|
cellMultiplier = groupIndex;
|
|
}
|
|
while (rowIndex < (rowMultiplier + 1) * rowCount) {
|
|
var cells = tableRows[rowIndex].children;
|
|
var group = this.groups[groupIndex];
|
|
if (rowIndex % rowCount === 0) {
|
|
time = getMilliseconds(new Date(+this.startTime()));
|
|
}
|
|
for (var cellIndex = cellMultiplier * columnCount; cellIndex < (cellMultiplier + 1) * columnCount; cellIndex++) {
|
|
var cell = cells[cellIndex];
|
|
var collectionIndex = cellIndex % columnCount;
|
|
var collection = group.getTimeSlotCollection(collectionIndex);
|
|
var currentDate = this._dates[collectionIndex];
|
|
var currentTime = Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
|
|
var start = currentTime + time;
|
|
var end = start + interval;
|
|
cell.setAttribute('role', 'gridcell');
|
|
cell.setAttribute('aria-selected', false);
|
|
collection.addTimeSlot(cell, start, end);
|
|
}
|
|
time += interval;
|
|
rowIndex++;
|
|
}
|
|
}
|
|
},
|
|
_daySlotGroups: function (groupCount, columnCount) {
|
|
var tableRows;
|
|
if (this._isVerticallyGrouped()) {
|
|
tableRows = this.element.find('.k-scheduler-header-all-day');
|
|
} else {
|
|
tableRows = this.element.find('.k-scheduler-header-all-day tr');
|
|
}
|
|
tableRows.attr('role', 'row');
|
|
for (var groupIndex = 0; groupIndex < groupCount; groupIndex++) {
|
|
var rowMultiplier = 0;
|
|
if (this._isVerticallyGrouped()) {
|
|
rowMultiplier = groupIndex;
|
|
}
|
|
var group = this.groups[groupIndex];
|
|
var collection = group.getDaySlotCollection(0);
|
|
var cells = tableRows[rowMultiplier].children;
|
|
var cellMultiplier = 0;
|
|
if (!this._isVerticallyGrouped()) {
|
|
cellMultiplier = groupIndex;
|
|
}
|
|
var cellCount = 0;
|
|
for (var cellIndex = cellMultiplier * columnCount; cellIndex < (cellMultiplier + 1) * columnCount; cellIndex++) {
|
|
var cell = cells[cellIndex];
|
|
if (cellIndex % columnCount === 0) {
|
|
cellCount = 0;
|
|
}
|
|
var start = this._dates[cellCount];
|
|
var currentTime = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
|
|
cellCount++;
|
|
cell.setAttribute('role', 'gridcell');
|
|
cell.setAttribute('aria-selected', false);
|
|
collection.addDaySlot(cell, currentTime, currentTime + kendo.date.MS_PER_DAY);
|
|
}
|
|
}
|
|
},
|
|
_groups: function () {
|
|
var groupCount = this._groupCount();
|
|
var columnCount = this._columnCountInResourceView();
|
|
this.groups = [];
|
|
for (var idx = 0; idx < groupCount; idx++) {
|
|
var view = this._addResourceView(idx);
|
|
for (var columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
|
view.addTimeSlotCollection(this._dates[columnIndex], kendo.date.addDays(this._dates[columnIndex], 1));
|
|
}
|
|
if (this.options.allDaySlot) {
|
|
view.addDaySlotCollection(this._dates[0], kendo.date.addDays(this._dates[this._dates.length - 1], 1));
|
|
}
|
|
}
|
|
this._timeSlotGroups(groupCount, columnCount);
|
|
if (this.options.allDaySlot) {
|
|
this._daySlotGroups(groupCount, columnCount);
|
|
}
|
|
},
|
|
options: {
|
|
name: 'MultiDayView',
|
|
selectedDateFormat: '{0:D}',
|
|
selectedShortDateFormat: '{0:d}',
|
|
allDaySlot: true,
|
|
showWorkHours: false,
|
|
title: '',
|
|
startTime: kendo.date.today(),
|
|
endTime: kendo.date.today(),
|
|
minorTickCount: 2,
|
|
majorTick: 60,
|
|
majorTimeHeaderTemplate: '#=kendo.toString(date, \'t\')#',
|
|
minorTimeHeaderTemplate: ' ',
|
|
groupHeaderTemplate: '#=text#',
|
|
slotTemplate: ' ',
|
|
allDaySlotTemplate: ' ',
|
|
eventTemplate: DAY_VIEW_EVENT_TEMPLATE,
|
|
allDayEventTemplate: DAY_VIEW_ALL_DAY_EVENT_TEMPLATE,
|
|
dateHeaderTemplate: DATA_HEADER_TEMPLATE,
|
|
editable: true,
|
|
workDayStart: new Date(1980, 1, 1, 8, 0, 0),
|
|
workDayEnd: new Date(1980, 1, 1, 17, 0, 0),
|
|
workWeekStart: 1,
|
|
workWeekEnd: 5,
|
|
footer: { command: 'workDay' },
|
|
messages: {
|
|
allDay: 'all day',
|
|
showFullDay: 'Show full day',
|
|
showWorkDay: 'Show business hours'
|
|
},
|
|
currentTimeMarker: {
|
|
updateInterval: 10000,
|
|
useLocalTimezone: true
|
|
}
|
|
},
|
|
events: [
|
|
'remove',
|
|
'add',
|
|
'edit'
|
|
],
|
|
_templates: function () {
|
|
var options = this.options, settings = extend({}, kendo.Template, options.templateSettings);
|
|
this.eventTemplate = this._eventTmpl(options.eventTemplate, EVENT_WRAPPER_STRING);
|
|
this.allDayEventTemplate = this._eventTmpl(options.allDayEventTemplate, ALLDAY_EVENT_WRAPPER_STRING);
|
|
this.majorTimeHeaderTemplate = kendo.template(options.majorTimeHeaderTemplate, settings);
|
|
this.minorTimeHeaderTemplate = kendo.template(options.minorTimeHeaderTemplate, settings);
|
|
this.dateHeaderTemplate = kendo.template(options.dateHeaderTemplate, settings);
|
|
this.slotTemplate = kendo.template(options.slotTemplate, settings);
|
|
this.allDaySlotTemplate = kendo.template(options.allDaySlotTemplate, settings);
|
|
this.groupHeaderTemplate = kendo.template(options.groupHeaderTemplate, settings);
|
|
},
|
|
_editable: function () {
|
|
if (this.options.editable) {
|
|
if (this._isMobile()) {
|
|
this._touchEditable();
|
|
} else {
|
|
this._mouseEditable();
|
|
}
|
|
}
|
|
},
|
|
_mouseEditable: function () {
|
|
var that = this;
|
|
that.element.on('click' + NS, '.k-event a:has(.k-si-close)', function (e) {
|
|
that.trigger('remove', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
if (that.options.editable.create !== false) {
|
|
that.element.on('dblclick' + NS, '.k-scheduler-content td', function (e) {
|
|
if (!$(this).parent().hasClass('k-scheduler-header-all-day')) {
|
|
var slot = that._slotByPosition(e.pageX, e.pageY);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
}).on('dblclick' + NS, '.k-scheduler-header-all-day td', function (e) {
|
|
var slot = that._slotByPosition(e.pageX, e.pageY);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({}, {
|
|
isAllDay: true,
|
|
start: kendo.date.getDate(slot.startDate()),
|
|
end: kendo.date.getDate(slot.startDate())
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that.element.on('dblclick' + NS, '.k-event', function (e) {
|
|
that.trigger('edit', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
},
|
|
_touchEditable: function () {
|
|
var that = this;
|
|
var threshold = 0;
|
|
if (kendo.support.mobileOS.android) {
|
|
threshold = 5;
|
|
}
|
|
if (that.options.editable.create !== false) {
|
|
that._addUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-scheduler-content td',
|
|
tap: function (e) {
|
|
if (!$(e.target).parent().hasClass('k-scheduler-header-all-day')) {
|
|
var x = e.x.location !== undefined ? e.x.location : e.x;
|
|
var y = e.y.location !== undefined ? e.y.location : e.y;
|
|
var slot = that._slotByPosition(x, y);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
that._allDayUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-scheduler-header-all-day td',
|
|
tap: function (e) {
|
|
var x = e.x.location !== undefined ? e.x.location : e.x;
|
|
var y = e.y.location !== undefined ? e.y.location : e.y;
|
|
var slot = that._slotByPosition(x, y);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({}, {
|
|
isAllDay: true,
|
|
start: kendo.date.getDate(slot.startDate()),
|
|
end: kendo.date.getDate(slot.startDate())
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that._editUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-event',
|
|
tap: function (e) {
|
|
var eventElement = $(e.target).closest('.k-event');
|
|
if (!eventElement.hasClass('k-event-active')) {
|
|
that.trigger('edit', { uid: eventElement.attr(kendo.attr('uid')) });
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_layout: function (dates) {
|
|
var columns = [];
|
|
var rows = [];
|
|
var options = this.options;
|
|
var that = this;
|
|
for (var idx = 0; idx < dates.length; idx++) {
|
|
var column = {};
|
|
column.text = that.dateHeaderTemplate({ date: dates[idx] });
|
|
if (kendo.date.isToday(dates[idx])) {
|
|
column.className = 'k-today';
|
|
}
|
|
columns.push(column);
|
|
}
|
|
var resources = this.groupedResources;
|
|
if (options.allDaySlot) {
|
|
rows.push({
|
|
text: options.messages.allDay,
|
|
allDay: true,
|
|
cellContent: function (idx) {
|
|
var groupIndex = idx;
|
|
idx = resources.length && that._groupOrientation() !== 'vertical' ? idx % dates.length : idx;
|
|
return that.allDaySlotTemplate({
|
|
date: dates[idx],
|
|
resources: function () {
|
|
return that._resourceBySlot({ groupIndex: groupIndex });
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
this._forTimeRange(this.startTime(), this.endTime(), function (date, majorTick, middleRow, lastSlotRow) {
|
|
var template = majorTick ? that.majorTimeHeaderTemplate : that.minorTimeHeaderTemplate;
|
|
var row = {
|
|
text: template({ date: date }),
|
|
className: lastSlotRow ? 'k-slot-cell' : ''
|
|
};
|
|
rows.push(row);
|
|
});
|
|
if (resources.length) {
|
|
if (this._groupOrientation() === 'vertical') {
|
|
rows = this._createRowsLayout(resources, rows, this.groupHeaderTemplate);
|
|
} else {
|
|
columns = this._createColumnsLayout(resources, columns, this.groupHeaderTemplate);
|
|
}
|
|
}
|
|
return {
|
|
columns: columns,
|
|
rows: rows
|
|
};
|
|
},
|
|
_footer: function () {
|
|
var options = this.options;
|
|
if (options.footer !== false) {
|
|
var html = '<div class="k-header k-scheduler-footer">';
|
|
var command = options.footer.command;
|
|
if (command && command === 'workDay') {
|
|
html += '<ul class="k-reset k-header">';
|
|
html += '<li class="k-state-default k-scheduler-fullday"><a href="#" class="k-link"><span class="k-icon k-i-clock"></span>';
|
|
html += (options.showWorkHours ? options.messages.showFullDay : options.messages.showWorkDay) + '</a></li>';
|
|
html += '</ul>';
|
|
} else {
|
|
html += ' ';
|
|
}
|
|
html += '</div>';
|
|
this.footer = $(html).appendTo(this.element);
|
|
var that = this;
|
|
this.footer.on('click' + NS, '.k-scheduler-fullday', function (e) {
|
|
e.preventDefault();
|
|
that.trigger('navigate', {
|
|
view: that.name || options.name,
|
|
date: that.startDate(),
|
|
isWorkDay: !options.showWorkHours
|
|
});
|
|
});
|
|
}
|
|
},
|
|
_forTimeRange: function (min, max, action, after) {
|
|
min = toInvariantTime(min);
|
|
max = toInvariantTime(max);
|
|
var that = this, msMin = getMilliseconds(min), msMax = getMilliseconds(max), minorTickCount = that.options.minorTickCount, msMajorInterval = that.options.majorTick * MS_PER_MINUTE, msInterval = msMajorInterval / minorTickCount || 1, start = new Date(+min), startDay = start.getDate(), msStart, idx = 0, length, html = '';
|
|
length = MS_PER_DAY / msInterval;
|
|
if (msMin != msMax) {
|
|
if (msMin > msMax) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
length = (msMax - msMin) / msInterval;
|
|
}
|
|
length = Math.round(length);
|
|
for (; idx < length; idx++) {
|
|
var majorTickDivider = idx % (msMajorInterval / msInterval), isMajorTickRow = majorTickDivider === 0, isMiddleRow = majorTickDivider < minorTickCount - 1, isLastSlotRow = majorTickDivider === minorTickCount - 1;
|
|
html += action(start, isMajorTickRow, isMiddleRow, isLastSlotRow);
|
|
setTime(start, msInterval, false);
|
|
}
|
|
if (msMax) {
|
|
msStart = getMilliseconds(start);
|
|
if (startDay < start.getDate()) {
|
|
msStart += MS_PER_DAY;
|
|
}
|
|
if (msStart > msMax) {
|
|
start = new Date(+max);
|
|
}
|
|
}
|
|
if (after) {
|
|
html += after(start);
|
|
}
|
|
return html;
|
|
},
|
|
_content: function (dates) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var start = that.startTime();
|
|
var end = this.endTime();
|
|
var groupsCount = 1;
|
|
var rowCount = 1;
|
|
var columnCount = dates.length;
|
|
var html = '';
|
|
var resources = this.groupedResources;
|
|
var slotTemplate = this.slotTemplate;
|
|
var allDaySlotTemplate = this.allDaySlotTemplate;
|
|
var isVerticalGroupped = false;
|
|
var allDayVerticalGroupRow;
|
|
if (resources.length) {
|
|
isVerticalGroupped = that._groupOrientation() === 'vertical';
|
|
if (isVerticalGroupped) {
|
|
rowCount = this._rowCountForLevel(this.rowLevels.length - 2);
|
|
if (options.allDaySlot) {
|
|
allDayVerticalGroupRow = function (groupIndex) {
|
|
var result = '<tr class="k-scheduler-header-all-day">';
|
|
var resources = function () {
|
|
return that._resourceBySlot({ groupIndex: groupIndex });
|
|
};
|
|
for (var idx = 0, length = dates.length; idx < length; idx++) {
|
|
result += '<td>' + allDaySlotTemplate({
|
|
date: dates[idx],
|
|
resources: resources
|
|
}) + '</td>';
|
|
}
|
|
return result + '</tr>';
|
|
};
|
|
}
|
|
} else {
|
|
groupsCount = this._columnCountForLevel(this.columnLevels.length - 2);
|
|
}
|
|
}
|
|
html += '<tbody>';
|
|
var appendRow = function (date, majorTick) {
|
|
var content = '';
|
|
var idx;
|
|
var length;
|
|
var classes = '';
|
|
var tmplDate;
|
|
var groupIdx = 0;
|
|
content = '<tr' + (majorTick ? ' class="k-middle-row"' : '') + '>';
|
|
var resources = function (groupIndex) {
|
|
return function () {
|
|
return that._resourceBySlot({ groupIndex: groupIndex });
|
|
};
|
|
};
|
|
for (; groupIdx < groupsCount; groupIdx++) {
|
|
for (idx = 0, length = columnCount; idx < length; idx++) {
|
|
classes = '';
|
|
if (kendo.date.isToday(dates[idx])) {
|
|
classes += 'k-today';
|
|
}
|
|
if (kendo.date.getMilliseconds(date) < kendo.date.getMilliseconds(that.options.workDayStart) || kendo.date.getMilliseconds(date) >= kendo.date.getMilliseconds(that.options.workDayEnd) || !that._isWorkDay(dates[idx])) {
|
|
classes += ' k-nonwork-hour';
|
|
}
|
|
content += '<td' + (classes !== '' ? ' class="' + classes + '"' : '') + '>';
|
|
tmplDate = kendo.date.getDate(dates[idx]);
|
|
kendo.date.setTime(tmplDate, kendo.date.getMilliseconds(date));
|
|
content += slotTemplate({
|
|
date: tmplDate,
|
|
resources: resources(isVerticalGroupped ? rowIdx : groupIdx)
|
|
});
|
|
content += '</td>';
|
|
}
|
|
}
|
|
content += '</tr>';
|
|
return content;
|
|
};
|
|
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
|
|
html += allDayVerticalGroupRow ? allDayVerticalGroupRow(rowIdx) : '';
|
|
html += this._forTimeRange(start, end, appendRow);
|
|
}
|
|
html += '</tbody>';
|
|
this.content.find('table').append(html);
|
|
},
|
|
_isWorkDay: function (date) {
|
|
var day = date.getDay();
|
|
var workDays = this._workDays;
|
|
for (var i = 0; i < workDays.length; i++) {
|
|
if (workDays[i] === day) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_render: function (dates) {
|
|
var that = this;
|
|
dates = dates || [];
|
|
this._dates = dates;
|
|
this._startDate = dates[0];
|
|
this._endDate = dates[dates.length - 1 || 0];
|
|
this.createLayout(this._layout(dates));
|
|
this._content(dates);
|
|
this._footer();
|
|
this.refreshLayout();
|
|
var allDayHeader = this.element.find('.k-scheduler-header-all-day td');
|
|
if (allDayHeader.length) {
|
|
this._allDayHeaderHeight = allDayHeader.first()[0].clientHeight;
|
|
}
|
|
that.datesHeader.on('click' + NS, '.k-nav-day', function (e) {
|
|
var th = $(e.currentTarget).closest('th');
|
|
var offset = th.offset();
|
|
var slot = that._slotByPosition(offset.left, offset.top + th.outerHeight());
|
|
that.trigger('navigate', {
|
|
view: 'day',
|
|
date: slot.startDate()
|
|
});
|
|
});
|
|
},
|
|
startTime: function () {
|
|
var options = this.options;
|
|
return options.showWorkHours ? options.workDayStart : options.startTime;
|
|
},
|
|
endTime: function () {
|
|
var options = this.options;
|
|
return options.showWorkHours ? options.workDayEnd : options.endTime;
|
|
},
|
|
startDate: function () {
|
|
return this._startDate;
|
|
},
|
|
endDate: function () {
|
|
return this._endDate;
|
|
},
|
|
_end: function (isAllDay) {
|
|
var time = getMilliseconds(this.endTime()) || MS_PER_DAY;
|
|
if (isAllDay) {
|
|
time = 0;
|
|
}
|
|
return new Date(this._endDate.getTime() + time);
|
|
},
|
|
nextDate: function () {
|
|
return kendo.date.nextDay(this.endDate());
|
|
},
|
|
previousDate: function () {
|
|
return kendo.date.previousDay(this.startDate());
|
|
},
|
|
calculateDateRange: function () {
|
|
this._render([this.options.date]);
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
if (that._currentTimeUpdateTimer) {
|
|
clearInterval(that._currentTimeUpdateTimer);
|
|
}
|
|
if (that.datesHeader) {
|
|
that.datesHeader.off(NS);
|
|
}
|
|
if (that.element) {
|
|
that.element.off(NS);
|
|
}
|
|
if (that.footer) {
|
|
that.footer.remove();
|
|
}
|
|
SchedulerView.fn.destroy.call(this);
|
|
if (this._isMobile() && that.options.editable) {
|
|
if (that.options.editable.create !== false) {
|
|
that._addUserEvents.destroy();
|
|
that._allDayUserEvents.destroy();
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that._editUserEvents.destroy();
|
|
}
|
|
}
|
|
},
|
|
inRange: function (options) {
|
|
var inRange = SchedulerView.fn.inRange.call(this, options);
|
|
var startTime = getMilliseconds(this.startTime());
|
|
var endTime = getMilliseconds(this.endTime()) || kendo.date.MS_PER_DAY;
|
|
var start = getMilliseconds(options.start);
|
|
var end = getMilliseconds(options.end) || kendo.date.MS_PER_DAY;
|
|
return inRange && startTime <= start && end <= endTime;
|
|
},
|
|
selectionByElement: function (cell) {
|
|
var offset = cell.offset();
|
|
return this._slotByPosition(offset.left, offset.top);
|
|
},
|
|
_timeSlotInterval: function () {
|
|
var options = this.options;
|
|
return options.majorTick / options.minorTickCount * MS_PER_MINUTE;
|
|
},
|
|
_timeSlotIndex: function (date) {
|
|
var options = this.options;
|
|
var eventStartTime = getMilliseconds(date);
|
|
var startTime = getMilliseconds(this.startTime());
|
|
var timeSlotInterval = options.majorTick / options.minorTickCount * MS_PER_MINUTE;
|
|
return (eventStartTime - startTime) / timeSlotInterval;
|
|
},
|
|
_slotIndex: function (date, multiday) {
|
|
if (multiday) {
|
|
return this._dateSlotIndex(date);
|
|
}
|
|
return this._timeSlotIndex(date);
|
|
},
|
|
_dateSlotIndex: function (date, overlaps) {
|
|
var idx;
|
|
var length;
|
|
var slots = this._dates || [];
|
|
var slotStart;
|
|
var slotEnd;
|
|
var offset = 1;
|
|
for (idx = 0, length = slots.length; idx < length; idx++) {
|
|
slotStart = kendo.date.getDate(slots[idx]);
|
|
slotEnd = new Date(kendo.date.getDate(slots[idx]).getTime() + MS_PER_DAY - (overlaps ? 0 : 1));
|
|
if (isInDateRange(date, slotStart, slotEnd)) {
|
|
return idx * offset;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
_positionAllDayEvent: function (element, slotRange) {
|
|
var slotWidth = slotRange.innerWidth();
|
|
var startIndex = slotRange.start.index;
|
|
var endIndex = slotRange.end.index;
|
|
var allDayEvents = SchedulerView.collidingEvents(slotRange.events(), startIndex, endIndex);
|
|
var currentColumnCount = this._headerColumnCount || 0;
|
|
var leftOffset = 2;
|
|
var rightOffset = startIndex !== endIndex ? 5 : 4;
|
|
var eventHeight = this._allDayHeaderHeight;
|
|
var start = slotRange.startSlot();
|
|
element.css({
|
|
left: start.offsetLeft + leftOffset,
|
|
width: slotWidth - rightOffset
|
|
});
|
|
slotRange.addEvent({
|
|
slotIndex: startIndex,
|
|
start: startIndex,
|
|
end: endIndex,
|
|
element: element
|
|
});
|
|
allDayEvents.push({
|
|
slotIndex: startIndex,
|
|
start: startIndex,
|
|
end: endIndex,
|
|
element: element
|
|
});
|
|
var rows = SchedulerView.createRows(allDayEvents);
|
|
if (rows.length && rows.length > currentColumnCount) {
|
|
this._headerColumnCount = rows.length;
|
|
}
|
|
var top = slotRange.start.offsetTop;
|
|
for (var idx = 0, length = rows.length; idx < length; idx++) {
|
|
var rowEvents = rows[idx].events;
|
|
for (var j = 0, eventLength = rowEvents.length; j < eventLength; j++) {
|
|
$(rowEvents[j].element).css({ top: top + idx * eventHeight });
|
|
}
|
|
}
|
|
},
|
|
_arrangeColumns: function (element, top, height, slotRange) {
|
|
var startSlot = slotRange.start;
|
|
element = {
|
|
element: element,
|
|
slotIndex: startSlot.index,
|
|
start: top,
|
|
end: top + height
|
|
};
|
|
var columns, slotWidth = startSlot.clientWidth, eventRightOffset = slotWidth * 0.1, columnEvents, eventElements = slotRange.events(), slotEvents = SchedulerView.collidingEvents(eventElements, element.start, element.end);
|
|
slotRange.addEvent(element);
|
|
slotEvents.push(element);
|
|
columns = SchedulerView.createColumns(slotEvents);
|
|
var columnWidth = (slotWidth - eventRightOffset) / columns.length;
|
|
for (var idx = 0, length = columns.length; idx < length; idx++) {
|
|
columnEvents = columns[idx].events;
|
|
for (var j = 0, eventLength = columnEvents.length; j < eventLength; j++) {
|
|
columnEvents[j].element[0].style.width = columnWidth - 4 + 'px';
|
|
columnEvents[j].element[0].style.left = (this._isRtl ? eventRightOffset : 0) + startSlot.offsetLeft + idx * columnWidth + 2 + 'px';
|
|
}
|
|
}
|
|
},
|
|
_positionEvent: function (event, element, slotRange) {
|
|
var start = event._startTime || event.start;
|
|
var end = event._endTime || event.end;
|
|
var rect = slotRange.innerRect(start, end, false);
|
|
var height = rect.bottom - rect.top - 2;
|
|
if (height < 0) {
|
|
height = 0;
|
|
}
|
|
element.css({
|
|
top: rect.top,
|
|
height: height
|
|
});
|
|
this._arrangeColumns(element, rect.top, element[0].clientHeight, slotRange);
|
|
},
|
|
_createEventElement: function (event, isOneDayEvent, head, tail) {
|
|
var template = isOneDayEvent ? this.eventTemplate : this.allDayEventTemplate;
|
|
var options = this.options;
|
|
var editable = options.editable;
|
|
var isMobile = this._isMobile();
|
|
var showDelete = editable && editable.destroy !== false && !isMobile;
|
|
var resizable = editable && editable.resize !== false;
|
|
var startDate = getDate(this.startDate());
|
|
var endDate = getDate(this.endDate());
|
|
var startTime = getMilliseconds(this.startTime());
|
|
var endTime = getMilliseconds(this.endTime());
|
|
var eventStartTime = event._time('start');
|
|
var eventEndTime = event._time('end');
|
|
var middle;
|
|
if (startTime >= endTime) {
|
|
endTime = getMilliseconds(new Date(this.endTime().getTime() + MS_PER_DAY - 1));
|
|
}
|
|
if (!isOneDayEvent && !event.isAllDay) {
|
|
endDate = new Date(endDate.getTime() + MS_PER_DAY);
|
|
}
|
|
var eventStartDate = event.start;
|
|
var eventEndDate = event.end;
|
|
if (event.isAllDay) {
|
|
eventEndDate = getDate(event.end);
|
|
}
|
|
if (!isInDateRange(getDate(eventStartDate), startDate, endDate) && !isInDateRange(eventEndDate, startDate, endDate) || isOneDayEvent && eventStartTime < startTime && eventEndTime > endTime) {
|
|
middle = true;
|
|
} else if (getDate(eventStartDate) < startDate || isOneDayEvent && eventStartTime < startTime) {
|
|
tail = true;
|
|
} else if (eventEndDate > endDate && !isOneDayEvent || isOneDayEvent && eventEndTime > endTime) {
|
|
head = true;
|
|
}
|
|
var resources = this.eventResources(event);
|
|
if (event._startTime && eventStartTime !== kendo.date.getMilliseconds(event.start)) {
|
|
eventStartDate = new Date(eventStartTime);
|
|
eventStartDate = kendo.timezone.apply(eventStartDate, 'Etc/UTC');
|
|
}
|
|
if (event._endTime && eventEndTime !== kendo.date.getMilliseconds(event.end)) {
|
|
eventEndDate = new Date(eventEndTime);
|
|
eventEndDate = kendo.timezone.apply(eventEndDate, 'Etc/UTC');
|
|
}
|
|
var data = extend({}, {
|
|
ns: kendo.ns,
|
|
resizable: resizable,
|
|
showDelete: showDelete,
|
|
middle: middle,
|
|
head: head,
|
|
tail: tail,
|
|
singleDay: this._dates.length == 1,
|
|
resources: resources,
|
|
inverseColor: resources && resources[0] ? this._shouldInverseResourceColor(resources[0]) : false
|
|
}, event, {
|
|
start: eventStartDate,
|
|
end: eventEndDate
|
|
});
|
|
var element = $(template(data));
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: element,
|
|
data: [{ dataItem: data }]
|
|
};
|
|
});
|
|
return element;
|
|
},
|
|
_isInTimeSlot: function (event) {
|
|
var slotStartTime = this.startTime(), slotEndTime = this.endTime(), startTime = event._startTime || event.start, endTime = event._endTime || event.end;
|
|
if (getMilliseconds(slotEndTime) === getMilliseconds(kendo.date.getDate(slotEndTime))) {
|
|
slotEndTime = kendo.date.getDate(slotEndTime);
|
|
setTime(slotEndTime, MS_PER_DAY - 1);
|
|
}
|
|
if (event._date('end') > event._date('start')) {
|
|
endTime = +event._date('end') + (MS_PER_DAY - 1);
|
|
}
|
|
endTime = endTime - event._date('end');
|
|
startTime = startTime - event._date('start');
|
|
slotEndTime = getMilliseconds(slotEndTime);
|
|
slotStartTime = getMilliseconds(slotStartTime);
|
|
if (slotStartTime === startTime && startTime === endTime) {
|
|
return true;
|
|
}
|
|
var overlaps = startTime !== slotEndTime;
|
|
return isInTimeRange(startTime, slotStartTime, slotEndTime, overlaps) || isInTimeRange(endTime, slotStartTime, slotEndTime, overlaps) || isInTimeRange(slotStartTime, startTime, endTime) || isInTimeRange(slotEndTime, startTime, endTime);
|
|
},
|
|
_isInDateSlot: function (event) {
|
|
var groups = this.groups[0];
|
|
var slotStart = groups.firstSlot().start;
|
|
var slotEnd = groups.lastSlot().end - 1;
|
|
var startTime = kendo.date.toUtcTime(event.start);
|
|
var endTime = kendo.date.toUtcTime(event.end);
|
|
return (isInDateRange(startTime, slotStart, slotEnd) || isInDateRange(endTime, slotStart, slotEnd) || isInDateRange(slotStart, startTime, endTime) || isInDateRange(slotEnd, startTime, endTime)) && (!isInDateRange(endTime, slotStart, slotStart) || isInDateRange(endTime, startTime, startTime) || event.isAllDay);
|
|
},
|
|
_updateAllDayHeaderHeight: function (height) {
|
|
if (this._height !== height) {
|
|
this._height = height;
|
|
var allDaySlots = this.element.find('.k-scheduler-header-all-day td');
|
|
if (allDaySlots.length) {
|
|
allDaySlots.parent().add(this.element.find('.k-scheduler-times-all-day').parent()).height(height);
|
|
for (var groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
this.groups[groupIndex].refresh();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_renderEvents: function (events, groupIndex) {
|
|
var allDayEventContainer = this.datesHeader.find('.k-scheduler-header-wrap > div');
|
|
var event;
|
|
var idx;
|
|
var length;
|
|
for (idx = 0, length = events.length; idx < length; idx++) {
|
|
event = events[idx];
|
|
if (this._isInDateSlot(event)) {
|
|
var isMultiDayEvent = event.isAllDay || event.end.getTime() - event.start.getTime() >= MS_PER_DAY;
|
|
var container = isMultiDayEvent && !this._isVerticallyGrouped() ? allDayEventContainer : this.content;
|
|
var element;
|
|
var ranges;
|
|
var group;
|
|
if (!isMultiDayEvent) {
|
|
if (this._isInTimeSlot(event)) {
|
|
group = this.groups[groupIndex];
|
|
if (!group._continuousEvents) {
|
|
group._continuousEvents = [];
|
|
}
|
|
ranges = group.slotRanges(event);
|
|
var rangeCount = ranges.length;
|
|
for (var rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var start = event.start;
|
|
var end = event.end;
|
|
if (rangeCount > 1) {
|
|
if (rangeIndex === 0) {
|
|
end = range.end.endDate();
|
|
} else if (rangeIndex == rangeCount - 1) {
|
|
start = range.start.startDate();
|
|
} else {
|
|
start = range.start.startDate();
|
|
end = range.end.endDate();
|
|
}
|
|
}
|
|
var occurrence = event.clone({
|
|
start: start,
|
|
end: end,
|
|
_startTime: event._startTime,
|
|
_endTime: event.endTime
|
|
});
|
|
if (this._isInTimeSlot(occurrence)) {
|
|
var head = range.head;
|
|
element = this._createEventElement(event, !isMultiDayEvent, head, range.tail);
|
|
element.appendTo(container);
|
|
this._positionEvent(occurrence, element, range);
|
|
addContinuousEvent(group, range, element, false);
|
|
}
|
|
}
|
|
}
|
|
} else if (this.options.allDaySlot) {
|
|
group = this.groups[groupIndex];
|
|
if (!group._continuousEvents) {
|
|
group._continuousEvents = [];
|
|
}
|
|
ranges = group.slotRanges(event);
|
|
if (ranges.length) {
|
|
element = this._createEventElement(event, !isMultiDayEvent);
|
|
this._positionAllDayEvent(element, ranges[0]);
|
|
addContinuousEvent(group, ranges[0], element, true);
|
|
element.appendTo(container);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
render: function (events) {
|
|
this._headerColumnCount = 0;
|
|
this._groups();
|
|
this.element.find('.k-event').remove();
|
|
events = new kendo.data.Query(events).sort([
|
|
{
|
|
field: 'start',
|
|
dir: 'asc'
|
|
},
|
|
{
|
|
field: 'end',
|
|
dir: 'desc'
|
|
}
|
|
]).toArray();
|
|
var eventsByResource = [];
|
|
this._eventsByResource(events, this.groupedResources, eventsByResource);
|
|
var eventsPerDate = $.map(this._dates, function (date) {
|
|
return Math.max.apply(null, $.map(eventsByResource, function (events) {
|
|
return $.grep(events, function (event) {
|
|
return event.isMultiDay() && isInDateRange(date, getDate(event.start), getDate(event.end));
|
|
}).length;
|
|
}));
|
|
});
|
|
var height = Math.max.apply(null, eventsPerDate);
|
|
this._updateAllDayHeaderHeight((height + 1) * this._allDayHeaderHeight);
|
|
for (var groupIndex = 0; groupIndex < eventsByResource.length; groupIndex++) {
|
|
this._renderEvents(eventsByResource[groupIndex], groupIndex);
|
|
}
|
|
this.refreshLayout();
|
|
this.trigger('activate');
|
|
},
|
|
_eventsByResource: function (events, resources, result) {
|
|
var resource = resources[0];
|
|
if (resource) {
|
|
var view = resource.dataSource.view();
|
|
for (var itemIdx = 0; itemIdx < view.length; itemIdx++) {
|
|
var value = this._resourceValue(resource, view[itemIdx]);
|
|
var eventsFilteredByResource = new kendo.data.Query(events).filter({
|
|
field: resource.field,
|
|
operator: SchedulerView.groupEqFilter(value)
|
|
}).toArray();
|
|
if (resources.length > 1) {
|
|
this._eventsByResource(eventsFilteredByResource, resources.slice(1), result);
|
|
} else {
|
|
result.push(eventsFilteredByResource);
|
|
}
|
|
}
|
|
} else {
|
|
result.push(events);
|
|
}
|
|
},
|
|
_columnOffsetForResource: function (index) {
|
|
return this._columnCountForLevel(index) / this._columnCountForLevel(index - 1);
|
|
},
|
|
_columnCountForLevel: function (level) {
|
|
var columnLevel = this.columnLevels[level];
|
|
return columnLevel ? columnLevel.length : 0;
|
|
},
|
|
_rowCountForLevel: function (level) {
|
|
var rowLevel = this.rowLevels[level];
|
|
return rowLevel ? rowLevel.length : 0;
|
|
},
|
|
clearSelection: function () {
|
|
this.content.add(this.datesHeader).find('.k-state-selected').removeAttr('id').attr('aria-selected', false).removeClass('k-state-selected');
|
|
},
|
|
_updateDirection: function (selection, ranges, multiple, reverse, vertical) {
|
|
var isDaySlot = selection.isAllDay;
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
if (multiple) {
|
|
if (vertical) {
|
|
if (!isDaySlot && startSlot.index === endSlot.index && startSlot.collectionIndex === endSlot.collectionIndex) {
|
|
selection.backward = reverse;
|
|
}
|
|
} else {
|
|
if (isDaySlot && startSlot.index === endSlot.index || !isDaySlot && startSlot.collectionIndex === endSlot.collectionIndex) {
|
|
selection.backward = reverse;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_changeViewPeriod: function (selection, reverse, vertical) {
|
|
if (!vertical) {
|
|
var date = reverse ? this.previousDate() : this.nextDate();
|
|
var start = selection.start;
|
|
var end = selection.end;
|
|
selection.start = new Date(date);
|
|
selection.end = new Date(date);
|
|
var endMilliseconds = selection.isAllDay ? MS_PER_DAY : getMilliseconds(end);
|
|
setTime(selection.start, getMilliseconds(start));
|
|
setTime(selection.end, endMilliseconds);
|
|
if (!this._isVerticallyGrouped()) {
|
|
selection.groupIndex = reverse ? this.groups.length - 1 : 0;
|
|
}
|
|
selection.events = [];
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
extend(true, ui, {
|
|
MultiDayView: MultiDayView,
|
|
DayView: MultiDayView.extend({
|
|
options: {
|
|
name: 'DayView',
|
|
title: 'Day'
|
|
},
|
|
name: 'day'
|
|
}),
|
|
WeekView: MultiDayView.extend({
|
|
options: {
|
|
name: 'WeekView',
|
|
title: 'Week',
|
|
selectedDateFormat: '{0:D} - {1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}'
|
|
},
|
|
name: 'week',
|
|
calculateDateRange: function () {
|
|
var selectedDate = this.options.date, start = kendo.date.dayOfWeek(selectedDate, this.calendarInfo().firstDay, -1), idx, length, dates = [];
|
|
for (idx = 0, length = 7; idx < length; idx++) {
|
|
dates.push(start);
|
|
start = kendo.date.nextDay(start);
|
|
}
|
|
this._render(dates);
|
|
}
|
|
}),
|
|
WorkWeekView: MultiDayView.extend({
|
|
options: {
|
|
name: 'WorkWeekView',
|
|
title: 'Work Week',
|
|
selectedDateFormat: '{0:D} - {1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}'
|
|
},
|
|
name: 'workWeek',
|
|
nextDate: function () {
|
|
return kendo.date.dayOfWeek(kendo.date.nextDay(this.startDate()), this.calendarInfo().firstDay, 1);
|
|
},
|
|
previousDate: function () {
|
|
var weekStart = kendo.date.dayOfWeek(this.startDate(), this.calendarInfo().firstDay, -1);
|
|
return kendo.date.previousDay(weekStart);
|
|
},
|
|
calculateDateRange: function () {
|
|
var selectedDate = this.options.date, dayOfWeek = kendo.date.dayOfWeek, weekStart = dayOfWeek(selectedDate, this.calendarInfo().firstDay, -1), start = dayOfWeek(weekStart, this.options.workWeekStart, 1), end = dayOfWeek(start, this.options.workWeekEnd, 1), dates = [];
|
|
while (start <= end) {
|
|
dates.push(start);
|
|
start = kendo.date.nextDay(start);
|
|
}
|
|
this._render(dates);
|
|
}
|
|
})
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.agendaview', ['kendo.scheduler.view'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.agendaview',
|
|
name: 'Scheduler Agenda View',
|
|
category: 'web',
|
|
description: 'The Scheduler Agenda View',
|
|
depends: ['scheduler.view'],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var kendo = window.kendo, ui = kendo.ui, NS = '.kendoAgendaView';
|
|
var EVENT_WRAPPER_FORMAT = '<div class="k-task" title="#:title.replace(/"/g,"\'")#" data-#=kendo.ns#uid="#=uid#">' + '# if (resources[0]) {#' + '<span class="k-scheduler-mark" style="background-color:#=resources[0].color#"></span>' + '# } #' + '# if (data.isException()) { #' + '<span class="k-icon k-i-exception"></span>' + '# } else if (data.isRecurring()) {#' + '<span class="k-icon k-i-refresh"></span>' + '# } #' + '{0}' + '#if (showDelete) {#' + '<a href="\\#" class="k-link k-event-delete"><span class="k-icon k-si-close"></span></a>' + '#}#' + '</div>';
|
|
ui.AgendaView = ui.SchedulerView.extend({
|
|
init: function (element, options) {
|
|
ui.SchedulerView.fn.init.call(this, element, options);
|
|
options = this.options;
|
|
if (options.editable) {
|
|
options.editable = $.extend({ 'delete': true }, options.editable, {
|
|
create: false,
|
|
update: false
|
|
});
|
|
}
|
|
this.title = options.title;
|
|
this._eventTemplate = this._eventTmpl(options.eventTemplate, EVENT_WRAPPER_FORMAT);
|
|
this._dateTemplate = kendo.template(options.eventDateTemplate);
|
|
this._groupTemplate = kendo.template(options.eventGroupTemplate);
|
|
this._timeTemplate = kendo.template(options.eventTimeTemplate);
|
|
this.element.on('mouseenter' + NS, '.k-scheduler-agenda .k-scheduler-content tr', '_mouseenter').on('mouseleave' + NS, '.k-scheduler-agenda .k-scheduler-content tr', '_mouseleave').on('click' + NS, '.k-scheduler-agenda .k-scheduler-content .k-link:has(.k-si-close)', '_remove');
|
|
this._renderLayout(options.date);
|
|
},
|
|
name: 'agenda',
|
|
_mouseenter: function (e) {
|
|
$(e.currentTarget).addClass('k-state-hover');
|
|
},
|
|
_mouseleave: function (e) {
|
|
$(e.currentTarget).removeClass('k-state-hover');
|
|
},
|
|
_remove: function (e) {
|
|
e.preventDefault();
|
|
this.trigger('remove', { uid: $(e.currentTarget).closest('.k-task').attr(kendo.attr('uid')) });
|
|
},
|
|
nextDate: function () {
|
|
return kendo.date.nextDay(this.startDate());
|
|
},
|
|
startDate: function () {
|
|
return this._startDate;
|
|
},
|
|
endDate: function () {
|
|
return this._endDate;
|
|
},
|
|
previousDate: function () {
|
|
return kendo.date.previousDay(this.startDate());
|
|
},
|
|
_renderLayout: function (date) {
|
|
this._startDate = date;
|
|
this._endDate = kendo.date.addDays(date, 7);
|
|
this.createLayout(this._layout());
|
|
this.table.addClass('k-scheduler-agenda');
|
|
},
|
|
_layout: function () {
|
|
var columns = [
|
|
{
|
|
text: this.options.messages.time,
|
|
className: 'k-scheduler-timecolumn'
|
|
},
|
|
{ text: this.options.messages.event }
|
|
];
|
|
if (!this._isMobilePhoneView()) {
|
|
columns.splice(0, 0, {
|
|
text: this.options.messages.date,
|
|
className: 'k-scheduler-datecolumn'
|
|
});
|
|
}
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
var groupHeaders = [];
|
|
for (var idx = 0; idx < resources.length; idx++) {
|
|
groupHeaders.push({
|
|
text: '',
|
|
className: 'k-scheduler-groupcolumn'
|
|
});
|
|
}
|
|
columns = groupHeaders.concat(columns);
|
|
}
|
|
return { columns: columns };
|
|
},
|
|
_tasks: function (events) {
|
|
var tasks = [];
|
|
for (var idx = 0; idx < events.length; idx++) {
|
|
var event = events[idx];
|
|
var start = event.start;
|
|
var end = event.end;
|
|
var eventDurationInDays = Math.ceil((end - kendo.date.getDate(start)) / kendo.date.MS_PER_DAY);
|
|
var task = event.clone();
|
|
task.startDate = kendo.date.getDate(start);
|
|
if (task.startDate >= this.startDate()) {
|
|
tasks.push(task);
|
|
}
|
|
if (eventDurationInDays > 1) {
|
|
task.end = kendo.date.nextDay(start);
|
|
task.head = true;
|
|
for (var day = 1; day < eventDurationInDays; day++) {
|
|
start = task.end;
|
|
task = event.clone();
|
|
task.start = start;
|
|
task.startDate = kendo.date.getDate(start);
|
|
task.end = kendo.date.nextDay(start);
|
|
if (day == eventDurationInDays - 1) {
|
|
task.end = new Date(task.start.getFullYear(), task.start.getMonth(), task.start.getDate(), end.getHours(), end.getMinutes(), end.getSeconds(), end.getMilliseconds());
|
|
task.tail = true;
|
|
} else {
|
|
task.isAllDay = true;
|
|
task.middle = true;
|
|
}
|
|
if (kendo.date.getDate(task.end) <= this.endDate() && task.start >= this.startDate() || kendo.date.getDate(task.start).getTime() == this.endDate().getTime()) {
|
|
tasks.push(task);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return new kendo.data.Query(tasks).sort([
|
|
{
|
|
field: 'start',
|
|
dir: 'asc'
|
|
},
|
|
{
|
|
field: 'end',
|
|
dir: 'asc'
|
|
}
|
|
]).groupBy({ field: 'startDate' }).toArray();
|
|
},
|
|
_renderTaskGroups: function (tasksGroups, groups) {
|
|
var tableRows = [];
|
|
var editable = this.options.editable;
|
|
var showDelete = editable && editable.destroy !== false && !this._isMobile();
|
|
var isPhoneView = this._isMobilePhoneView();
|
|
for (var taskGroupIndex = 0; taskGroupIndex < tasksGroups.length; taskGroupIndex++) {
|
|
var date = tasksGroups[taskGroupIndex].value;
|
|
var tasks = tasksGroups[taskGroupIndex].items;
|
|
var today = kendo.date.isToday(date);
|
|
for (var taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
|
|
var task = tasks[taskIndex];
|
|
var tableRow = [];
|
|
var headerCells = !isPhoneView ? tableRow : [];
|
|
if (taskGroupIndex === 0 && taskIndex === 0 && groups.length) {
|
|
for (var idx = 0; idx < groups.length; idx++) {
|
|
headerCells.push(kendo.format('<td class="k-scheduler-groupcolumn{2}" rowspan="{0}">{1}</td>', groups[idx].rowSpan, this._groupTemplate({ value: groups[idx].text }), groups[idx].className));
|
|
}
|
|
}
|
|
if (taskIndex === 0) {
|
|
if (isPhoneView) {
|
|
headerCells.push(kendo.format('<td class="k-scheduler-datecolumn" colspan="2">{0}</td>', this._dateTemplate({ date: date })));
|
|
tableRows.push('<tr role="row" aria-selected="false"' + (today ? ' class="k-today">' : '>') + headerCells.join('') + '</tr>');
|
|
} else {
|
|
tableRow.push(kendo.format('<td class="k-scheduler-datecolumn{3}{2}" rowspan="{0}">{1}</td>', tasks.length, this._dateTemplate({ date: date }), taskGroupIndex == tasksGroups.length - 1 && !groups.length ? ' k-last' : '', !groups.length ? ' k-first' : ''));
|
|
}
|
|
}
|
|
if (task.head) {
|
|
task.format = '{0:t}';
|
|
} else if (task.tail) {
|
|
task.format = '{1:t}';
|
|
} else {
|
|
task.format = '{0:t}-{1:t}';
|
|
}
|
|
task.resources = this.eventResources(task);
|
|
tableRow.push(kendo.format('<td class="k-scheduler-timecolumn"><div>{0}{1}{2}</div></td><td>{3}</td>', task.tail || task.middle ? '<span class="k-icon k-i-arrow-w"></span>' : '', this._timeTemplate(task.clone({
|
|
start: task._startTime || task.start,
|
|
end: task.endTime || task.end
|
|
})), task.head || task.middle ? '<span class="k-icon k-i-arrow-e"></span>' : '', this._eventTemplate(task.clone({ showDelete: showDelete }))));
|
|
tableRows.push('<tr role="row" aria-selected="false"' + (today ? ' class="k-today">' : '>') + tableRow.join('') + '</tr>');
|
|
}
|
|
}
|
|
return tableRows.join('');
|
|
},
|
|
render: function (events) {
|
|
var table = this.content.find('table').empty();
|
|
var groups = [];
|
|
if (events.length > 0) {
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
groups = this._createGroupConfiguration(events, resources, null);
|
|
this._renderGroups(groups, table, []);
|
|
} else {
|
|
groups = this._tasks(events);
|
|
table.append(this._renderTaskGroups(groups, []));
|
|
}
|
|
}
|
|
var items = this._eventsList = flattenTaskGroups(groups);
|
|
this._angularItems(table, items);
|
|
this.refreshLayout();
|
|
this.trigger('activate');
|
|
},
|
|
_angularItems: function (table, items) {
|
|
this.angular('compile', function () {
|
|
var data = [], elements = items.map(function (item) {
|
|
data.push({ dataItem: item });
|
|
return table.find('.k-task[' + kendo.attr('uid') + '=' + item.uid + ']');
|
|
});
|
|
return {
|
|
elements: elements,
|
|
data: data
|
|
};
|
|
});
|
|
},
|
|
_renderGroups: function (groups, table, parentGroups) {
|
|
for (var idx = 0, length = groups.length; idx < length; idx++) {
|
|
var parents = parentGroups.splice(0);
|
|
parents.push(groups[idx]);
|
|
if (groups[idx].groups) {
|
|
this._renderGroups(groups[idx].groups, table, parents);
|
|
} else {
|
|
table.append(this._renderTaskGroups(groups[idx].items, parents));
|
|
}
|
|
}
|
|
},
|
|
_createGroupConfiguration: function (events, resources, parent) {
|
|
var resource = resources[0];
|
|
var configuration = [];
|
|
var data = resource.dataSource.view();
|
|
var isPhoneView = this._isMobilePhoneView();
|
|
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
|
|
var value = resourceValue(resource, data[dataIndex]);
|
|
var tmp = new kendo.data.Query(events).filter({
|
|
field: resource.field,
|
|
operator: ui.SchedulerView.groupEqFilter(value)
|
|
}).toArray();
|
|
if (tmp.length) {
|
|
var tasks = this._tasks(tmp);
|
|
var className = parent ? '' : ' k-first';
|
|
if (dataIndex === data.length - 1 && (!parent || parent.className.indexOf('k-last') > -1)) {
|
|
className += ' k-last';
|
|
}
|
|
var obj = {
|
|
text: kendo.getter(resource.dataTextField)(data[dataIndex]),
|
|
value: value,
|
|
rowSpan: 0,
|
|
className: className
|
|
};
|
|
if (resources.length > 1) {
|
|
obj.groups = this._createGroupConfiguration(tmp, resources.slice(1), obj);
|
|
if (parent) {
|
|
parent.rowSpan += obj.rowSpan;
|
|
}
|
|
} else {
|
|
obj.items = tasks;
|
|
var span = rowSpan(obj.items);
|
|
if (isPhoneView) {
|
|
span += obj.items.length;
|
|
}
|
|
obj.rowSpan = span;
|
|
if (parent) {
|
|
parent.rowSpan += span;
|
|
}
|
|
}
|
|
configuration.push(obj);
|
|
}
|
|
}
|
|
return configuration;
|
|
},
|
|
selectionByElement: function (cell) {
|
|
var index, event;
|
|
cell = $(cell);
|
|
if (cell.hasClass('k-scheduler-datecolumn') || !this._eventsList.length) {
|
|
return;
|
|
}
|
|
if (cell.is('.k-task')) {
|
|
cell = cell.closest('td');
|
|
}
|
|
if (this._isMobile()) {
|
|
var parent = cell.parent();
|
|
index = parent.parent().children().filter(function () {
|
|
return $(this).children(':not(.k-scheduler-datecolumn)').length;
|
|
}).index(parent);
|
|
} else {
|
|
index = cell.parent().index();
|
|
}
|
|
event = this._eventsList[index];
|
|
return {
|
|
index: index,
|
|
start: event.start,
|
|
end: event.end,
|
|
isAllDay: event.isAllDay,
|
|
uid: event.uid
|
|
};
|
|
},
|
|
select: function (selection) {
|
|
this.clearSelection();
|
|
var row = this.table.find('.k-task').eq(selection.index).closest('tr').addClass('k-state-selected').attr('aria-selected', true)[0];
|
|
this.current(row);
|
|
},
|
|
move: function (selection, key) {
|
|
var handled = false;
|
|
var index = selection.index;
|
|
if (key == kendo.keys.UP) {
|
|
index--;
|
|
handled = true;
|
|
} else if (key == kendo.keys.DOWN) {
|
|
index++;
|
|
handled = true;
|
|
}
|
|
if (handled) {
|
|
var event = this._eventsList[index];
|
|
if (event) {
|
|
selection.start = event.start;
|
|
selection.end = event.end;
|
|
selection.isAllDay = event.isAllDay;
|
|
selection.events = [event.uid];
|
|
selection.index = index;
|
|
}
|
|
}
|
|
return handled;
|
|
},
|
|
moveToEvent: function () {
|
|
return false;
|
|
},
|
|
constrainSelection: function (selection) {
|
|
var event = this._eventsList[0];
|
|
if (event) {
|
|
selection.start = event.start;
|
|
selection.end = event.end;
|
|
selection.isAllDay = event.isAllDay;
|
|
selection.events = [event.uid];
|
|
selection.index = 0;
|
|
}
|
|
},
|
|
isInRange: function () {
|
|
return true;
|
|
},
|
|
destroy: function () {
|
|
if (this.element) {
|
|
this.element.off(NS);
|
|
}
|
|
ui.SchedulerView.fn.destroy.call(this);
|
|
},
|
|
options: {
|
|
title: 'Agenda',
|
|
name: 'agenda',
|
|
editable: true,
|
|
selectedDateFormat: '{0:D}-{1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}',
|
|
eventTemplate: '#:title#',
|
|
eventTimeTemplate: '#if(data.isAllDay) {#' + '#=this.options.messages.allDay#' + '#} else { #' + '#=kendo.format(format, start, end)#' + '# } #',
|
|
eventDateTemplate: '<strong class="k-scheduler-agendaday">' + '#=kendo.toString(date, "dd")#' + '</strong>' + '<em class="k-scheduler-agendaweek">' + '#=kendo.toString(date,"dddd")#' + '</em>' + '<span class="k-scheduler-agendadate">' + '#=kendo.toString(date, "y")#' + '</span>',
|
|
eventGroupTemplate: '<strong class="k-scheduler-adgendagroup">' + '#=value#' + '</strong>',
|
|
messages: {
|
|
event: 'Event',
|
|
date: 'Date',
|
|
time: 'Time',
|
|
allDay: 'all day'
|
|
}
|
|
}
|
|
});
|
|
function rowSpan(tasks) {
|
|
var result = 0;
|
|
for (var idx = 0, length = tasks.length; idx < length; idx++) {
|
|
result += tasks[idx].items.length;
|
|
}
|
|
return result;
|
|
}
|
|
function resourceValue(resource, item) {
|
|
if (resource.valuePrimitive) {
|
|
item = kendo.getter(resource.dataValueField)(item);
|
|
}
|
|
return item;
|
|
}
|
|
function flattenTaskGroups(groups) {
|
|
var idx = 0, length = groups.length, item, result = [];
|
|
for (; idx < length; idx++) {
|
|
item = groups[idx];
|
|
if (item.groups) {
|
|
item = flattenGroup(item.groups);
|
|
result = result.concat(item);
|
|
} else {
|
|
result = result.concat(flattenGroup(item.items));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function flattenGroup(groups) {
|
|
var items = [].concat(groups), item = items.shift(), result = [], push = [].push;
|
|
while (item) {
|
|
if (item.groups) {
|
|
push.apply(items, item.groups);
|
|
} else if (item.items) {
|
|
push.apply(items, item.items);
|
|
} else {
|
|
push.call(result, item);
|
|
}
|
|
item = items.shift();
|
|
}
|
|
return result;
|
|
}
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.monthview', ['kendo.scheduler.view'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.monthview',
|
|
name: 'Scheduler Month View',
|
|
category: 'web',
|
|
description: 'The Scheduler Month View',
|
|
depends: ['scheduler.view'],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var kendo = window.kendo, ui = kendo.ui, SchedulerView = ui.SchedulerView, NS = '.kendoMonthView', extend = $.extend, getDate = kendo.date.getDate, MS_PER_DAY = kendo.date.MS_PER_DAY, NUMBER_OF_ROWS = 6, NUMBER_OF_COLUMNS = 7, DAY_TEMPLATE = kendo.template('<span class="k-link k-nav-day">#:kendo.toString(date, "dd")#</span>'), EVENT_WRAPPER_STRING = '<div role="gridcell" aria-selected="false" data-#=ns#uid="#=uid#"' + '#if (resources[0]) { #' + 'style="background-color:#=resources[0].color #; border-color: #=resources[0].color#"' + 'class="k-event#=inverseColor ? " k-event-inverse" : ""#"' + '#} else {#' + 'class="k-event"' + '#}#' + '>' + '<span class="k-event-actions">' + '# if(data.tail || data.middle) {#' + '<span class="k-icon k-i-arrow-w"></span>' + '#}#' + '# if(data.isException()) {#' + '<span class="k-icon k-i-exception"></span>' + '# } else if(data.isRecurring()) {#' + '<span class="k-icon k-i-refresh"></span>' + '#}#' + '</span>' + '{0}' + '<span class="k-event-actions">' + '#if (showDelete) {#' + '<a href="\\#" class="k-link k-event-delete"><span class="k-icon k-si-close"></span></a>' + '#}#' + '# if(data.head || data.middle) {#' + '<span class="k-icon k-i-arrow-e"></span>' + '#}#' + '</span>' + '# if(resizable && !data.tail && !data.middle) {#' + '<span class="k-resize-handle k-resize-w"></span>' + '#}#' + '# if(resizable && !data.head && !data.middle) {#' + '<span class="k-resize-handle k-resize-e"></span>' + '#}#' + '</div>', EVENT_TEMPLATE = kendo.template('<div title="#=title.replace(/"/g,"&\\#34;")#">' + '<div class="k-event-template">#:title#</div>' + '</div>');
|
|
var MORE_BUTTON_TEMPLATE = kendo.template('<div style="width:#=width#px;left:#=left#px;top:#=top#px" class="k-more-events k-button"><span>...</span></div>');
|
|
ui.MonthView = SchedulerView.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
SchedulerView.fn.init.call(that, element, options);
|
|
that.title = that.options.title;
|
|
that._templates();
|
|
that._editable();
|
|
that._renderLayout(that.options.date);
|
|
that._groups();
|
|
},
|
|
name: 'month',
|
|
_updateDirection: function (selection, ranges, multiple, reverse, vertical) {
|
|
if (multiple) {
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
var isSameSlot = startSlot.index === endSlot.index;
|
|
var isSameCollection = startSlot.collectionIndex === endSlot.collectionIndex;
|
|
var updateDirection;
|
|
if (vertical) {
|
|
updateDirection = isSameSlot && isSameCollection || isSameCollection;
|
|
} else {
|
|
updateDirection = isSameSlot && isSameCollection;
|
|
}
|
|
if (updateDirection) {
|
|
selection.backward = reverse;
|
|
}
|
|
}
|
|
},
|
|
_changeViewPeriod: function (selection, reverse, vertical) {
|
|
var pad = vertical ? 7 : 1;
|
|
if (reverse) {
|
|
pad *= -1;
|
|
}
|
|
selection.start = kendo.date.addDays(selection.start, pad);
|
|
selection.end = kendo.date.addDays(selection.end, pad);
|
|
if (!vertical || vertical && this._isVerticallyGrouped()) {
|
|
selection.groupIndex = reverse ? this.groups.length - 1 : 0;
|
|
}
|
|
selection.events = [];
|
|
return true;
|
|
},
|
|
_continuousSlot: function (selection, ranges, reverse) {
|
|
var index = selection.backward ? 0 : ranges.length - 1;
|
|
var group = this.groups[selection.groupIndex];
|
|
return group.continuousSlot(ranges[index].start, reverse);
|
|
},
|
|
_changeGroupContinuously: function (selection, continuousSlot, multiple, reverse) {
|
|
if (!multiple) {
|
|
var groupIndex = selection.groupIndex;
|
|
var lastGroupIndex = this.groups.length - 1;
|
|
var vertical = this._isVerticallyGrouped();
|
|
var group = this.groups[groupIndex];
|
|
if (!continuousSlot && vertical) {
|
|
continuousSlot = group[reverse ? 'lastSlot' : 'firstSlot']();
|
|
groupIndex += reverse ? -1 : 1;
|
|
} else if (continuousSlot && !vertical) {
|
|
groupIndex = reverse ? lastGroupIndex : 0;
|
|
}
|
|
if (groupIndex < 0 || groupIndex > lastGroupIndex) {
|
|
groupIndex = reverse ? lastGroupIndex : 0;
|
|
continuousSlot = null;
|
|
}
|
|
selection.groupIndex = groupIndex;
|
|
}
|
|
return continuousSlot;
|
|
},
|
|
_normalizeHorizontalSelection: function (selection, ranges, reverse) {
|
|
var slot;
|
|
if (reverse) {
|
|
slot = ranges[0].start;
|
|
} else {
|
|
slot = ranges[ranges.length - 1].end;
|
|
}
|
|
return slot;
|
|
},
|
|
_normalizeVerticalSelection: function (selection, ranges) {
|
|
var slot;
|
|
if (selection.backward) {
|
|
slot = ranges[0].start;
|
|
} else {
|
|
slot = ranges[ranges.length - 1].end;
|
|
}
|
|
return slot;
|
|
},
|
|
_templates: function () {
|
|
var options = this.options, settings = extend({}, kendo.Template, options.templateSettings);
|
|
this.eventTemplate = this._eventTmpl(options.eventTemplate, EVENT_WRAPPER_STRING);
|
|
this.dayTemplate = kendo.template(options.dayTemplate, settings);
|
|
this.groupHeaderTemplate = kendo.template(options.groupHeaderTemplate, settings);
|
|
},
|
|
dateForTitle: function () {
|
|
return kendo.format(this.options.selectedDateFormat, this._firstDayOfMonth, this._lastDayOfMonth);
|
|
},
|
|
shortDateForTitle: function () {
|
|
return kendo.format(this.options.selectedShortDateFormat, this._firstDayOfMonth, this._lastDayOfMonth);
|
|
},
|
|
nextDate: function () {
|
|
return kendo.date.nextDay(this._lastDayOfMonth);
|
|
},
|
|
previousDate: function () {
|
|
return kendo.date.previousDay(this._firstDayOfMonth);
|
|
},
|
|
startDate: function () {
|
|
return this._startDate;
|
|
},
|
|
endDate: function () {
|
|
return this._endDate;
|
|
},
|
|
_renderLayout: function (date) {
|
|
var that = this;
|
|
this._firstDayOfMonth = kendo.date.firstDayOfMonth(date);
|
|
this._lastDayOfMonth = kendo.date.lastDayOfMonth(date);
|
|
this._startDate = firstVisibleMonthDay(date, this.calendarInfo());
|
|
this.createLayout(this._layout());
|
|
this._content();
|
|
this.refreshLayout();
|
|
this.content.on('click' + NS, '.k-nav-day,.k-more-events', function (e) {
|
|
var offset = $(e.currentTarget).offset();
|
|
var slot = that._slotByPosition(offset.left, offset.top);
|
|
e.preventDefault();
|
|
that.trigger('navigate', {
|
|
view: 'day',
|
|
date: slot.startDate()
|
|
});
|
|
});
|
|
},
|
|
_editable: function () {
|
|
if (this.options.editable && !this._isMobilePhoneView()) {
|
|
if (this._isMobile()) {
|
|
this._touchEditable();
|
|
} else {
|
|
this._mouseEditable();
|
|
}
|
|
}
|
|
},
|
|
_mouseEditable: function () {
|
|
var that = this;
|
|
that.element.on('click' + NS, '.k-scheduler-monthview .k-event a:has(.k-si-close)', function (e) {
|
|
that.trigger('remove', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
if (that.options.editable.create !== false) {
|
|
that.element.on('dblclick' + NS, '.k-scheduler-monthview .k-scheduler-content td', function (e) {
|
|
var offset = $(e.currentTarget).offset();
|
|
var slot = that._slotByPosition(offset.left, offset.top);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
isAllDay: true,
|
|
start: slot.startDate(),
|
|
end: slot.startDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that.element.on('dblclick' + NS, '.k-scheduler-monthview .k-event', function (e) {
|
|
that.trigger('edit', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
},
|
|
_touchEditable: function () {
|
|
var that = this;
|
|
var threshold = 0;
|
|
if (kendo.support.mobileOS.android) {
|
|
threshold = 5;
|
|
}
|
|
if (that.options.editable.create !== false) {
|
|
that._addUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-scheduler-monthview .k-scheduler-content td',
|
|
tap: function (e) {
|
|
var offset = $(e.target).offset();
|
|
var slot = that._slotByPosition(offset.left, offset.top);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
isAllDay: true,
|
|
start: slot.startDate(),
|
|
end: slot.startDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that._editUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-scheduler-monthview .k-event',
|
|
tap: function (e) {
|
|
if ($(e.event.target).closest('a:has(.k-si-close)').length === 0) {
|
|
that.trigger('edit', { uid: $(e.target).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
selectionByElement: function (cell) {
|
|
var offset = $(cell).offset();
|
|
return this._slotByPosition(offset.left, offset.top);
|
|
},
|
|
_columnCountForLevel: function (level) {
|
|
var columnLevel = this.columnLevels[level];
|
|
return columnLevel ? columnLevel.length : 0;
|
|
},
|
|
_rowCountForLevel: function (level) {
|
|
var rowLevel = this.rowLevels[level];
|
|
return rowLevel ? rowLevel.length : 0;
|
|
},
|
|
_content: function () {
|
|
var html = '<tbody>';
|
|
var verticalGroupCount = 1;
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (this._isVerticallyGrouped()) {
|
|
verticalGroupCount = this._rowCountForLevel(resources.length - 1);
|
|
}
|
|
}
|
|
for (var verticalGroupIdx = 0; verticalGroupIdx < verticalGroupCount; verticalGroupIdx++) {
|
|
html += this._createCalendar(verticalGroupIdx);
|
|
}
|
|
html += '</tbody>';
|
|
this.content.find('table').html(html);
|
|
},
|
|
_createCalendar: function (verticalGroupIndex) {
|
|
var start = this.startDate();
|
|
var cellCount = NUMBER_OF_COLUMNS * NUMBER_OF_ROWS;
|
|
var cellsPerRow = NUMBER_OF_COLUMNS;
|
|
var weekStartDates = [start];
|
|
var html = '';
|
|
var horizontalGroupCount = 1;
|
|
var isVerticallyGrouped = this._isVerticallyGrouped();
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (!isVerticallyGrouped) {
|
|
horizontalGroupCount = this._columnCountForLevel(resources.length - 1);
|
|
}
|
|
}
|
|
this._slotIndices = {};
|
|
for (var rowIdx = 0, length = cellCount / cellsPerRow; rowIdx < length; rowIdx++) {
|
|
html += '<tr>';
|
|
weekStartDates.push(start);
|
|
var startIdx = rowIdx * cellsPerRow;
|
|
for (var groupIdx = 0; groupIdx < horizontalGroupCount; groupIdx++) {
|
|
html += this._createRow(start, startIdx, cellsPerRow, isVerticallyGrouped ? verticalGroupIndex : groupIdx);
|
|
}
|
|
start = kendo.date.addDays(start, cellsPerRow);
|
|
html += '</tr>';
|
|
}
|
|
this._weekStartDates = weekStartDates;
|
|
this._endDate = kendo.date.previousDay(start);
|
|
return html;
|
|
},
|
|
_createRow: function (startDate, startIdx, cellsPerRow, groupIndex) {
|
|
var that = this;
|
|
var min = that._firstDayOfMonth;
|
|
var max = that._lastDayOfMonth;
|
|
var content = that.dayTemplate;
|
|
var classes = '';
|
|
var html = '';
|
|
var resources = function () {
|
|
return that._resourceBySlot({ groupIndex: groupIndex });
|
|
};
|
|
for (var cellIdx = 0; cellIdx < cellsPerRow; cellIdx++) {
|
|
classes = '';
|
|
if (kendo.date.isToday(startDate)) {
|
|
classes += 'k-today';
|
|
}
|
|
if (!kendo.date.isInDateRange(startDate, min, max)) {
|
|
classes += ' k-other-month';
|
|
}
|
|
html += '<td ';
|
|
if (classes !== '') {
|
|
html += 'class="' + classes + '"';
|
|
}
|
|
html += '>';
|
|
html += content({
|
|
date: startDate,
|
|
resources: resources
|
|
});
|
|
html += '</td>';
|
|
that._slotIndices[getDate(startDate).getTime()] = startIdx + cellIdx;
|
|
startDate = kendo.date.nextDay(startDate);
|
|
}
|
|
return html;
|
|
},
|
|
_layout: function () {
|
|
var calendarInfo = this.calendarInfo();
|
|
var weekDayNames = this._isMobile() ? calendarInfo.days.namesShort : calendarInfo.days.names;
|
|
var names = shiftArray(weekDayNames, calendarInfo.firstDay);
|
|
var columns = $.map(names, function (value) {
|
|
return { text: value };
|
|
});
|
|
var resources = this.groupedResources;
|
|
var rows;
|
|
if (resources.length) {
|
|
if (this._isVerticallyGrouped()) {
|
|
var inner = [];
|
|
for (var idx = 0; idx < 6; idx++) {
|
|
inner.push({
|
|
text: '<div> </div>',
|
|
className: 'k-hidden k-slot-cell'
|
|
});
|
|
}
|
|
rows = this._createRowsLayout(resources, inner, this.groupHeaderTemplate);
|
|
} else {
|
|
columns = this._createColumnsLayout(resources, columns, this.groupHeaderTemplate);
|
|
}
|
|
}
|
|
return {
|
|
columns: columns,
|
|
rows: rows
|
|
};
|
|
},
|
|
_createEventElement: function (event) {
|
|
var options = this.options;
|
|
var editable = options.editable;
|
|
var isMobile = this._isMobile();
|
|
event.showDelete = editable && editable.destroy !== false && !isMobile;
|
|
event.resizable = editable && editable.resize !== false && !isMobile;
|
|
event.ns = kendo.ns;
|
|
event.resources = this.eventResources(event);
|
|
event.inverseColor = event.resources && event.resources[0] ? this._shouldInverseResourceColor(event.resources[0]) : false;
|
|
var element = $(this.eventTemplate(event));
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: element,
|
|
data: [{ dataItem: event }]
|
|
};
|
|
});
|
|
return element;
|
|
},
|
|
_isInDateSlot: function (event) {
|
|
var groups = this.groups[0];
|
|
var slotStart = groups.firstSlot().start;
|
|
var slotEnd = groups.lastSlot().end - 1;
|
|
var startTime = kendo.date.toUtcTime(event.start);
|
|
var endTime = kendo.date.toUtcTime(event.end);
|
|
return (isInDateRange(startTime, slotStart, slotEnd) || isInDateRange(endTime, slotStart, slotEnd) || isInDateRange(slotStart, startTime, endTime) || isInDateRange(slotEnd, startTime, endTime)) && (!isInDateRange(endTime, slotStart, slotStart) || isInDateRange(endTime, startTime, startTime) || event.isAllDay);
|
|
},
|
|
_slotIndex: function (date) {
|
|
return this._slotIndices[getDate(date).getTime()];
|
|
},
|
|
_positionMobileEvent: function (slotRange, element, group) {
|
|
var startSlot = slotRange.start;
|
|
if (slotRange.start.offsetLeft > slotRange.end.offsetLeft) {
|
|
startSlot = slotRange.end;
|
|
}
|
|
var startIndex = slotRange.start.index;
|
|
var endIndex = startIndex;
|
|
var eventCount = 3;
|
|
var events = SchedulerView.collidingEvents(slotRange.events(), startIndex, endIndex);
|
|
events.push({
|
|
element: element,
|
|
start: startIndex,
|
|
end: endIndex
|
|
});
|
|
var rows = SchedulerView.createRows(events);
|
|
var slot = slotRange.collection.at(startIndex);
|
|
var container = slot.container;
|
|
if (!container) {
|
|
container = $(kendo.format('<div class="k-events-container" style="top:{0};left:{1};width:{2}"/>', startSlot.offsetTop + startSlot.firstChildTop + startSlot.firstChildHeight - 3 + 'px', startSlot.offsetLeft + 'px', startSlot.offsetWidth + 'px'));
|
|
slot.container = container;
|
|
this.content[0].appendChild(container[0]);
|
|
}
|
|
if (rows.length <= eventCount) {
|
|
slotRange.addEvent({
|
|
element: element,
|
|
start: startIndex,
|
|
end: endIndex,
|
|
groupIndex: startSlot.groupIndex
|
|
});
|
|
group._continuousEvents.push({
|
|
element: element,
|
|
uid: element.attr(kendo.attr('uid')),
|
|
start: slotRange.start,
|
|
end: slotRange.end
|
|
});
|
|
container[0].appendChild(element[0]);
|
|
}
|
|
},
|
|
_positionEvent: function (slotRange, element, group) {
|
|
var eventHeight = this.options.eventHeight;
|
|
var startSlot = slotRange.start;
|
|
if (slotRange.start.offsetLeft > slotRange.end.offsetLeft) {
|
|
startSlot = slotRange.end;
|
|
}
|
|
var startIndex = slotRange.start.index;
|
|
var endIndex = slotRange.end.index;
|
|
var eventCount = startSlot.eventCount;
|
|
var events = SchedulerView.collidingEvents(slotRange.events(), startIndex, endIndex);
|
|
var rightOffset = startIndex !== endIndex ? 5 : 4;
|
|
events.push({
|
|
element: element,
|
|
start: startIndex,
|
|
end: endIndex
|
|
});
|
|
var rows = SchedulerView.createRows(events);
|
|
for (var idx = 0, length = Math.min(rows.length, eventCount); idx < length; idx++) {
|
|
var rowEvents = rows[idx].events;
|
|
var eventTop = startSlot.offsetTop + startSlot.firstChildHeight + idx * eventHeight + 3 * idx + 'px';
|
|
for (var j = 0, eventLength = rowEvents.length; j < eventLength; j++) {
|
|
rowEvents[j].element[0].style.top = eventTop;
|
|
}
|
|
}
|
|
if (rows.length > eventCount) {
|
|
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
|
|
var collection = slotRange.collection;
|
|
var slot = collection.at(slotIndex);
|
|
if (slot.more) {
|
|
return;
|
|
}
|
|
slot.more = $(MORE_BUTTON_TEMPLATE({
|
|
ns: kendo.ns,
|
|
start: slotIndex,
|
|
end: slotIndex,
|
|
width: slot.clientWidth - 2,
|
|
left: slot.offsetLeft + 2,
|
|
top: slot.offsetTop + slot.firstChildHeight + eventCount * eventHeight + 3 * eventCount
|
|
}));
|
|
this.content[0].appendChild(slot.more[0]);
|
|
}
|
|
} else {
|
|
slotRange.addEvent({
|
|
element: element,
|
|
start: startIndex,
|
|
end: endIndex,
|
|
groupIndex: startSlot.groupIndex
|
|
});
|
|
element[0].style.width = slotRange.innerWidth() - rightOffset + 'px';
|
|
element[0].style.left = startSlot.offsetLeft + 2 + 'px';
|
|
element[0].style.height = eventHeight + 'px';
|
|
group._continuousEvents.push({
|
|
element: element,
|
|
uid: element.attr(kendo.attr('uid')),
|
|
start: slotRange.start,
|
|
end: slotRange.end
|
|
});
|
|
element.appendTo(this.content);
|
|
}
|
|
},
|
|
_slotByPosition: function (x, y) {
|
|
var offset = this.content.offset();
|
|
x -= offset.left;
|
|
y -= offset.top;
|
|
y += this.content[0].scrollTop;
|
|
x += this.content[0].scrollLeft;
|
|
x = Math.ceil(x);
|
|
y = Math.ceil(y);
|
|
for (var groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
var slot = this.groups[groupIndex].daySlotByPosition(x, y);
|
|
if (slot) {
|
|
return slot;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
_createResizeHint: function (range) {
|
|
var left = range.startSlot().offsetLeft;
|
|
var top = range.start.offsetTop;
|
|
var width = range.innerWidth();
|
|
var height = range.start.clientHeight - 2;
|
|
var hint = SchedulerView.fn._createResizeHint.call(this, left, top, width, height);
|
|
hint.appendTo(this.content);
|
|
this._resizeHint = this._resizeHint.add(hint);
|
|
},
|
|
_updateResizeHint: function (event, groupIndex, startTime, endTime) {
|
|
this._removeResizeHint();
|
|
var group = this.groups[groupIndex];
|
|
var ranges = group.ranges(startTime, endTime, true, event.isAllDay);
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
this._createResizeHint(ranges[rangeIndex]);
|
|
}
|
|
this._resizeHint.find('.k-label-top,.k-label-bottom').text('');
|
|
this._resizeHint.first().addClass('k-first').find('.k-label-top').text(kendo.toString(kendo.timezone.toLocalDate(startTime), 'M/dd'));
|
|
this._resizeHint.last().addClass('k-last').find('.k-label-bottom').text(kendo.toString(kendo.timezone.toLocalDate(endTime), 'M/dd'));
|
|
},
|
|
_updateMoveHint: function (event, groupIndex, distance) {
|
|
var start = kendo.date.toUtcTime(event.start) + distance;
|
|
var end = start + event.duration();
|
|
var group = this.groups[groupIndex];
|
|
var ranges = group.ranges(start, end, true, event.isAllDay);
|
|
this._removeMoveHint();
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var startSlot = range.startSlot();
|
|
var endSlot = range.endSlot();
|
|
var hint = this._createEventElement(event.clone({
|
|
head: range.head,
|
|
tail: range.tail
|
|
}));
|
|
hint.css({
|
|
left: startSlot.offsetLeft + 2,
|
|
top: startSlot.offsetTop + startSlot.firstChildHeight,
|
|
height: this.options.eventHeight,
|
|
width: range.innerWidth() - (startSlot.index !== endSlot.index ? 5 : 4)
|
|
});
|
|
hint.addClass('k-event-drag-hint');
|
|
hint.appendTo(this.content);
|
|
this._moveHint = this._moveHint.add(hint);
|
|
}
|
|
},
|
|
_groups: function () {
|
|
var groupCount = this._groupCount();
|
|
var columnCount = NUMBER_OF_COLUMNS;
|
|
var rowCount = NUMBER_OF_ROWS;
|
|
this.groups = [];
|
|
for (var idx = 0; idx < groupCount; idx++) {
|
|
this._addResourceView(idx);
|
|
}
|
|
var tableRows = this.content[0].getElementsByTagName('tr');
|
|
var startDate = this.startDate();
|
|
for (var groupIndex = 0; groupIndex < groupCount; groupIndex++) {
|
|
var cellCount = 0;
|
|
var rowMultiplier = 0;
|
|
if (this._isVerticallyGrouped()) {
|
|
rowMultiplier = groupIndex;
|
|
}
|
|
for (var rowIndex = rowMultiplier * rowCount; rowIndex < (rowMultiplier + 1) * rowCount; rowIndex++) {
|
|
var group = this.groups[groupIndex];
|
|
var collection = group.addDaySlotCollection(kendo.date.addDays(startDate, cellCount), kendo.date.addDays(this.startDate(), cellCount + columnCount));
|
|
var tableRow = tableRows[rowIndex];
|
|
var cells = tableRow.children;
|
|
var cellMultiplier = 0;
|
|
tableRow.setAttribute('role', 'row');
|
|
if (!this._isVerticallyGrouped()) {
|
|
cellMultiplier = groupIndex;
|
|
}
|
|
for (var cellIndex = cellMultiplier * columnCount; cellIndex < (cellMultiplier + 1) * columnCount; cellIndex++) {
|
|
var cell = cells[cellIndex];
|
|
var clientHeight = cell.clientHeight;
|
|
var firstChildHeight = cell.children.length ? cell.children[0].offsetHeight + 3 : 0;
|
|
var start = kendo.date.addDays(startDate, cellCount);
|
|
var end = kendo.date.MS_PER_DAY;
|
|
if (startDate.getHours() !== start.getHours()) {
|
|
end += (startDate.getHours() - start.getHours()) * kendo.date.MS_PER_HOUR;
|
|
}
|
|
start = kendo.date.toUtcTime(start);
|
|
end += start;
|
|
cellCount++;
|
|
var eventCount = Math.floor((clientHeight - firstChildHeight - this.options.moreButtonHeight) / (this.options.eventHeight + 3));
|
|
cell.setAttribute('role', 'gridcell');
|
|
cell.setAttribute('aria-selected', false);
|
|
collection.addDaySlot(cell, start, end, eventCount);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
render: function (events) {
|
|
this.content.children('.k-event,.k-more-events,.k-events-container').remove();
|
|
this._groups();
|
|
events = new kendo.data.Query(events).sort([
|
|
{
|
|
field: 'start',
|
|
dir: 'asc'
|
|
},
|
|
{
|
|
field: 'end',
|
|
dir: 'desc'
|
|
}
|
|
]).toArray();
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
this._renderGroups(events, resources, 0, 1);
|
|
} else {
|
|
this._renderEvents(events, 0);
|
|
}
|
|
this.refreshLayout();
|
|
this.trigger('activate');
|
|
},
|
|
_renderEvents: function (events, groupIndex) {
|
|
var event;
|
|
var idx;
|
|
var length;
|
|
var isMobilePhoneView = this._isMobilePhoneView();
|
|
for (idx = 0, length = events.length; idx < length; idx++) {
|
|
event = events[idx];
|
|
if (this._isInDateSlot(event)) {
|
|
var group = this.groups[groupIndex];
|
|
if (!group._continuousEvents) {
|
|
group._continuousEvents = [];
|
|
}
|
|
var ranges = group.slotRanges(event, true);
|
|
var rangeCount = ranges.length;
|
|
for (var rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var start = event.start;
|
|
var end = event.end;
|
|
if (rangeCount > 1) {
|
|
if (rangeIndex === 0) {
|
|
end = range.end.endDate();
|
|
} else if (rangeIndex == rangeCount - 1) {
|
|
start = range.start.startDate();
|
|
} else {
|
|
start = range.start.startDate();
|
|
end = range.end.endDate();
|
|
}
|
|
}
|
|
var occurrence = event.clone({
|
|
start: start,
|
|
end: end,
|
|
head: range.head,
|
|
tail: range.tail
|
|
});
|
|
if (isMobilePhoneView) {
|
|
this._positionMobileEvent(range, this._createEventElement(occurrence), group);
|
|
} else {
|
|
this._positionEvent(range, this._createEventElement(occurrence), group);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_renderGroups: function (events, resources, offset, columnLevel) {
|
|
var resource = resources[0];
|
|
if (resource) {
|
|
var view = resource.dataSource.view();
|
|
for (var itemIdx = 0; itemIdx < view.length; itemIdx++) {
|
|
var value = this._resourceValue(resource, view[itemIdx]);
|
|
var tmp = new kendo.data.Query(events).filter({
|
|
field: resource.field,
|
|
operator: SchedulerView.groupEqFilter(value)
|
|
}).toArray();
|
|
if (resources.length > 1) {
|
|
offset = this._renderGroups(tmp, resources.slice(1), offset++, columnLevel + 1);
|
|
} else {
|
|
this._renderEvents(tmp, offset++);
|
|
}
|
|
}
|
|
}
|
|
return offset;
|
|
},
|
|
_groupCount: function () {
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (this._isVerticallyGrouped()) {
|
|
return this._rowCountForLevel(resources.length - 1);
|
|
} else {
|
|
return this._columnCountForLevel(resources.length) / this._columnOffsetForResource(resources.length);
|
|
}
|
|
}
|
|
return 1;
|
|
},
|
|
_columnOffsetForResource: function (index) {
|
|
return this._columnCountForLevel(index) / this._columnCountForLevel(index - 1);
|
|
},
|
|
destroy: function () {
|
|
if (this.table) {
|
|
this.table.removeClass('k-scheduler-monthview');
|
|
}
|
|
if (this.content) {
|
|
this.content.off(NS);
|
|
}
|
|
if (this.element) {
|
|
this.element.off(NS);
|
|
}
|
|
SchedulerView.fn.destroy.call(this);
|
|
if (this._isMobile() && !this._isMobilePhoneView() && this.options.editable) {
|
|
if (this.options.editable.create !== false) {
|
|
this._addUserEvents.destroy();
|
|
}
|
|
if (this.options.editable.update !== false) {
|
|
this._editUserEvents.destroy();
|
|
}
|
|
}
|
|
},
|
|
events: [
|
|
'remove',
|
|
'add',
|
|
'edit',
|
|
'navigate'
|
|
],
|
|
options: {
|
|
title: 'Month',
|
|
name: 'month',
|
|
eventHeight: 25,
|
|
moreButtonHeight: 13,
|
|
editable: true,
|
|
selectedDateFormat: '{0:y}',
|
|
selectedShortDateFormat: '{0:y}',
|
|
groupHeaderTemplate: '#=text#',
|
|
dayTemplate: DAY_TEMPLATE,
|
|
eventTemplate: EVENT_TEMPLATE
|
|
}
|
|
});
|
|
function shiftArray(array, idx) {
|
|
return array.slice(idx).concat(array.slice(0, idx));
|
|
}
|
|
function firstVisibleMonthDay(date, calendarInfo) {
|
|
var firstDay = calendarInfo.firstDay, firstVisibleDay = new Date(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
while (firstVisibleDay.getDay() != firstDay) {
|
|
kendo.date.setTime(firstVisibleDay, -1 * MS_PER_DAY);
|
|
}
|
|
return firstVisibleDay;
|
|
}
|
|
function isInDateRange(value, min, max) {
|
|
var msMin = min, msMax = max, msValue;
|
|
msValue = value;
|
|
return msValue >= msMin && msValue <= msMax;
|
|
}
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.recurrence', [
|
|
'kendo.dropdownlist',
|
|
'kendo.datepicker',
|
|
'kendo.numerictextbox'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.recurrence',
|
|
name: 'Recurrence',
|
|
category: 'web',
|
|
depends: [
|
|
'dropdownlist',
|
|
'datepicker',
|
|
'numerictextbox'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, timezone = kendo.timezone, Class = kendo.Class, ui = kendo.ui, Widget = ui.Widget, DropDownList = ui.DropDownList, kendoDate = kendo.date, setTime = kendoDate.setTime, setDayOfWeek = kendoDate.setDayOfWeek, adjustDST = kendoDate.adjustDST, firstDayOfMonth = kendoDate.firstDayOfMonth, getMilliseconds = kendoDate.getMilliseconds, DAYS_IN_LEAPYEAR = [
|
|
0,
|
|
31,
|
|
60,
|
|
91,
|
|
121,
|
|
152,
|
|
182,
|
|
213,
|
|
244,
|
|
274,
|
|
305,
|
|
335,
|
|
366
|
|
], DAYS_IN_YEAR = [
|
|
0,
|
|
31,
|
|
59,
|
|
90,
|
|
120,
|
|
151,
|
|
181,
|
|
212,
|
|
243,
|
|
273,
|
|
304,
|
|
334,
|
|
365
|
|
], MONTHS = [
|
|
31,
|
|
28,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31
|
|
], WEEK_DAYS = {
|
|
0: 'SU',
|
|
1: 'MO',
|
|
2: 'TU',
|
|
3: 'WE',
|
|
4: 'TH',
|
|
5: 'FR',
|
|
6: 'SA'
|
|
}, WEEK_DAYS_IDX = {
|
|
'SU': 0,
|
|
'MO': 1,
|
|
'TU': 2,
|
|
'WE': 3,
|
|
'TH': 4,
|
|
'FR': 5,
|
|
'SA': 6
|
|
}, DATE_FORMATS = [
|
|
'yyyy-MM-ddTHH:mm:ss.fffzzz',
|
|
'yyyy-MM-ddTHH:mm:sszzz',
|
|
'yyyy-MM-ddTHH:mm:ss',
|
|
'yyyy-MM-ddTHH:mm',
|
|
'yyyy-MM-ddTHH',
|
|
'yyyy-MM-dd',
|
|
'yyyyMMddTHHmmssfffzzz',
|
|
'yyyyMMddTHHmmsszzz',
|
|
'yyyyMMddTHHmmss',
|
|
'yyyyMMddTHHmm',
|
|
'yyyyMMddTHH',
|
|
'yyyyMMdd'
|
|
], RULE_NAMES = [
|
|
'months',
|
|
'weeks',
|
|
'yearDays',
|
|
'monthDays',
|
|
'weekDays',
|
|
'hours',
|
|
'minutes',
|
|
'seconds'
|
|
], RULE_NAMES_LENGTH = RULE_NAMES.length, RECURRENCE_DATE_FORMAT = 'yyyyMMddTHHmmssZ', limitation = {
|
|
months: function (date, end, rule) {
|
|
var monthRules = rule.months, months = ruleValues(monthRules, date.getMonth() + 1), changed = false;
|
|
if (months !== null) {
|
|
if (months.length) {
|
|
date.setMonth(months[0] - 1, 1);
|
|
} else {
|
|
date.setFullYear(date.getFullYear() + 1, monthRules[0] - 1, 1);
|
|
}
|
|
changed = true;
|
|
}
|
|
return changed;
|
|
},
|
|
monthDays: function (date, end, rule) {
|
|
var monthLength, month, days, changed = false, hours = date.getHours(), normalize = function (monthDay) {
|
|
if (monthDay < 0) {
|
|
monthDay = monthLength + monthDay;
|
|
}
|
|
return monthDay;
|
|
};
|
|
while (date <= end) {
|
|
month = date.getMonth();
|
|
monthLength = getMonthLength(date);
|
|
days = ruleValues(rule.monthDays, date.getDate(), normalize);
|
|
if (days === null) {
|
|
return changed;
|
|
}
|
|
changed = true;
|
|
if (days.length) {
|
|
date.setMonth(month, days.sort(numberSortPredicate)[0]);
|
|
adjustDST(date, hours);
|
|
if (month === date.getMonth()) {
|
|
break;
|
|
}
|
|
} else {
|
|
date.setMonth(month + 1, 1);
|
|
}
|
|
}
|
|
return changed;
|
|
},
|
|
yearDays: function (date, end, rule) {
|
|
var year, yearDays, changed = false, hours = date.getHours(), normalize = function (yearDay) {
|
|
if (yearDay < 0) {
|
|
yearDay = year + yearDay;
|
|
}
|
|
return yearDay;
|
|
};
|
|
while (date < end) {
|
|
year = leapYear(date) ? 366 : 365;
|
|
yearDays = ruleValues(rule.yearDays, dayInYear(date), normalize);
|
|
if (yearDays === null) {
|
|
return changed;
|
|
}
|
|
changed = true;
|
|
year = date.getFullYear();
|
|
if (yearDays.length) {
|
|
date.setFullYear(year, 0, yearDays.sort(numberSortPredicate)[0]);
|
|
adjustDST(date, hours);
|
|
break;
|
|
} else {
|
|
date.setFullYear(year + 1, 0, 1);
|
|
}
|
|
}
|
|
return changed;
|
|
},
|
|
weeks: function (date, end, rule) {
|
|
var weekStart = rule.weekStart, year, weeks, day, changed = false, hours = date.getHours(), normalize = function (week) {
|
|
if (week < 0) {
|
|
week = 53 + week;
|
|
}
|
|
return week;
|
|
};
|
|
while (date < end) {
|
|
weeks = ruleValues(rule.weeks, weekInYear(date, weekStart), normalize);
|
|
if (weeks === null) {
|
|
return changed;
|
|
}
|
|
changed = true;
|
|
year = date.getFullYear();
|
|
if (weeks.length) {
|
|
day = weeks.sort(numberSortPredicate)[0] * 7 - 1;
|
|
date.setFullYear(year, 0, day);
|
|
setDayOfWeek(date, weekStart, -1);
|
|
adjustDST(date, hours);
|
|
break;
|
|
} else {
|
|
date.setFullYear(year + 1, 0, 1);
|
|
}
|
|
}
|
|
return changed;
|
|
},
|
|
weekDays: function (date, end, rule) {
|
|
var weekDays = rule.weekDays;
|
|
var weekStart = rule.weekStart;
|
|
var weekDayRules = ruleWeekValues(weekDays, date, weekStart);
|
|
var hours = date.getHours();
|
|
var weekDayRule, day;
|
|
if (weekDayRules === null) {
|
|
return false;
|
|
}
|
|
weekDayRule = weekDayRules[0];
|
|
if (!weekDayRule) {
|
|
weekDayRule = weekDays[0];
|
|
setDayOfWeek(date, weekStart);
|
|
}
|
|
day = weekDayRule.day;
|
|
if (weekDayRule.offset) {
|
|
while (date <= end && !isInWeek(date, weekDayRule, weekStart)) {
|
|
if (weekInMonth(date, weekStart) === numberOfWeeks(date, weekStart)) {
|
|
date.setMonth(date.getMonth() + 1, 1);
|
|
adjustDST(date, hours);
|
|
} else {
|
|
date.setDate(date.getDate() + 7);
|
|
adjustDST(date, hours);
|
|
setDayOfWeek(date, weekStart, -1);
|
|
}
|
|
}
|
|
}
|
|
if (date.getDay() !== day) {
|
|
setDayOfWeek(date, day);
|
|
}
|
|
return true;
|
|
},
|
|
hours: function (date, end, rule) {
|
|
var hourRules = rule.hours, startTime = rule._startTime, startHours = startTime.getHours(), hours = ruleValues(hourRules, startHours), changed = false;
|
|
if (hours !== null) {
|
|
changed = true;
|
|
date.setHours(startHours);
|
|
adjustDST(date, startHours);
|
|
if (hours.length) {
|
|
hours = hours[0];
|
|
date.setHours(hours);
|
|
} else {
|
|
hours = date.getHours();
|
|
date.setDate(date.getDate() + 1);
|
|
adjustDST(date, hours);
|
|
hours = hourRules[0];
|
|
date.setHours(hours);
|
|
adjustDST(date, hours);
|
|
}
|
|
if (rule.minutes) {
|
|
date.setMinutes(0);
|
|
}
|
|
startTime.setHours(hours, date.getMinutes());
|
|
}
|
|
return changed;
|
|
},
|
|
minutes: function (date, end, rule) {
|
|
var minuteRules = rule.minutes, currentMinutes = date.getMinutes(), minutes = ruleValues(minuteRules, currentMinutes), hours = rule._startTime.getHours(), changed = false;
|
|
if (minutes !== null) {
|
|
changed = true;
|
|
if (minutes.length) {
|
|
minutes = minutes[0];
|
|
} else {
|
|
hours += 1;
|
|
minutes = minuteRules[0];
|
|
}
|
|
if (rule.seconds) {
|
|
date.setSeconds(0);
|
|
}
|
|
date.setHours(hours, minutes);
|
|
hours = hours % 24;
|
|
adjustDST(date, hours);
|
|
rule._startTime.setHours(hours, minutes, date.getSeconds());
|
|
}
|
|
return changed;
|
|
},
|
|
seconds: function (date, end, rule) {
|
|
var secondRules = rule.seconds, hours = rule._startTime.getHours(), seconds = ruleValues(secondRules, date.getSeconds()), minutes = date.getMinutes(), changed = false;
|
|
if (seconds !== null) {
|
|
changed = true;
|
|
if (seconds.length) {
|
|
date.setSeconds(seconds[0]);
|
|
} else {
|
|
minutes += 1;
|
|
date.setMinutes(minutes, secondRules[0]);
|
|
if (minutes > 59) {
|
|
minutes = minutes % 60;
|
|
hours = (hours + 1) % 24;
|
|
}
|
|
}
|
|
rule._startTime.setHours(hours, minutes, date.getSeconds());
|
|
}
|
|
return changed;
|
|
}
|
|
}, BaseFrequency = Class.extend({
|
|
next: function (date, rule) {
|
|
var startTime = rule._startTime, day = startTime.getDate(), minutes, seconds;
|
|
if (rule.seconds) {
|
|
seconds = date.getSeconds() + 1;
|
|
date.setSeconds(seconds);
|
|
startTime.setSeconds(seconds);
|
|
startTime.setDate(day);
|
|
} else if (rule.minutes) {
|
|
minutes = date.getMinutes() + 1;
|
|
date.setMinutes(minutes);
|
|
startTime.setMinutes(minutes);
|
|
startTime.setDate(day);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
normalize: function (options) {
|
|
var rule = options.rule;
|
|
if (options.idx === 4 && rule.hours) {
|
|
rule._startTime.setHours(0);
|
|
this._hour(options.date, rule);
|
|
}
|
|
},
|
|
limit: function (date, end, rule) {
|
|
var interval = rule.interval, ruleName, firstRule, modified, idx, day;
|
|
while (date <= end) {
|
|
modified = firstRule = undefined;
|
|
day = date.getDate();
|
|
for (idx = 0; idx < RULE_NAMES_LENGTH; idx++) {
|
|
ruleName = RULE_NAMES[idx];
|
|
if (rule[ruleName]) {
|
|
modified = limitation[ruleName](date, end, rule);
|
|
if (firstRule !== undefined && modified) {
|
|
break;
|
|
} else {
|
|
firstRule = modified;
|
|
}
|
|
}
|
|
if (modified) {
|
|
this.normalize({
|
|
date: date,
|
|
rule: rule,
|
|
day: day,
|
|
idx: idx
|
|
});
|
|
}
|
|
}
|
|
if ((interval === 1 || !this.interval(rule, date)) && idx === RULE_NAMES_LENGTH) {
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
interval: function (rule, current) {
|
|
var start = new Date(rule._startPeriod);
|
|
var date = new Date(current);
|
|
var hours = current.getHours();
|
|
var weekStart = rule.weekStart;
|
|
var interval = rule.interval;
|
|
var frequency = rule.freq;
|
|
var modified = false;
|
|
var excess = 0;
|
|
var month = 0;
|
|
var day = 1;
|
|
var diff;
|
|
var startTimeHours;
|
|
if (frequency === 'hourly') {
|
|
diff = date.getTimezoneOffset() - start.getTimezoneOffset();
|
|
startTimeHours = rule._startTime.getHours();
|
|
date = date.getTime();
|
|
if (hours !== startTimeHours) {
|
|
date += (startTimeHours - hours) * kendoDate.MS_PER_HOUR;
|
|
}
|
|
date -= start;
|
|
if (diff) {
|
|
date -= diff * kendoDate.MS_PER_MINUTE;
|
|
}
|
|
diff = Math.floor(date / kendoDate.MS_PER_HOUR);
|
|
excess = intervalExcess(diff, interval);
|
|
if (excess !== 0) {
|
|
this._hour(current, rule, excess);
|
|
modified = true;
|
|
}
|
|
} else if (frequency === 'daily') {
|
|
kendoDate.setTime(date, -start, true);
|
|
diff = Math.ceil(date / kendoDate.MS_PER_DAY);
|
|
excess = intervalExcess(diff, interval);
|
|
if (excess !== 0) {
|
|
this._date(current, rule, excess);
|
|
modified = true;
|
|
}
|
|
} else if (frequency === 'weekly') {
|
|
diff = (current.getFullYear() - start.getFullYear()) * 52;
|
|
excess = weekInYear(current, weekStart) - weekInYear(start, weekStart) + diff;
|
|
excess = intervalExcess(excess, interval);
|
|
if (excess !== 0) {
|
|
kendoDate.setDayOfWeek(current, rule.weekStart, -1);
|
|
current.setDate(current.getDate() + excess * 7);
|
|
adjustDST(current, hours);
|
|
modified = true;
|
|
}
|
|
} else if (frequency === 'monthly') {
|
|
diff = current.getFullYear() - start.getFullYear();
|
|
diff = current.getMonth() - start.getMonth() + diff * 12;
|
|
excess = intervalExcess(diff, interval);
|
|
if (excess !== 0) {
|
|
day = rule._hasRuleValue ? 1 : current.getDate();
|
|
current.setFullYear(current.getFullYear(), current.getMonth() + excess, day);
|
|
adjustDST(current, hours);
|
|
modified = true;
|
|
}
|
|
} else if (frequency === 'yearly') {
|
|
diff = current.getFullYear() - start.getFullYear();
|
|
excess = intervalExcess(diff, interval);
|
|
if (!rule.months) {
|
|
month = current.getMonth();
|
|
}
|
|
if (!rule.yearDays && !rule.monthDays && !rule.weekDays) {
|
|
day = current.getDate();
|
|
}
|
|
if (excess !== 0) {
|
|
current.setFullYear(current.getFullYear() + excess, month, day);
|
|
adjustDST(current, hours);
|
|
modified = true;
|
|
}
|
|
}
|
|
return modified;
|
|
},
|
|
_hour: function (date, rule, interval) {
|
|
var startTime = rule._startTime, hours = startTime.getHours();
|
|
if (interval) {
|
|
hours += interval;
|
|
}
|
|
date.setHours(hours);
|
|
hours = hours % 24;
|
|
startTime.setHours(hours);
|
|
adjustDST(date, hours);
|
|
},
|
|
_date: function (date, rule, interval) {
|
|
var hours = date.getHours();
|
|
date.setDate(date.getDate() + interval);
|
|
if (!adjustDST(date, hours)) {
|
|
this._hour(date, rule);
|
|
}
|
|
}
|
|
}), HourlyFrequency = BaseFrequency.extend({
|
|
next: function (date, rule) {
|
|
if (!BaseFrequency.fn.next(date, rule)) {
|
|
this._hour(date, rule, 1);
|
|
}
|
|
},
|
|
normalize: function (options) {
|
|
var rule = options.rule;
|
|
if (options.idx === 4) {
|
|
rule._startTime.setHours(0);
|
|
this._hour(options.date, rule);
|
|
}
|
|
}
|
|
}), DailyFrequency = BaseFrequency.extend({
|
|
next: function (date, rule) {
|
|
if (!BaseFrequency.fn.next(date, rule)) {
|
|
this[rule.hours ? '_hour' : '_date'](date, rule, 1);
|
|
}
|
|
}
|
|
}), WeeklyFrequency = DailyFrequency.extend({
|
|
setup: function (rule, eventStartDate) {
|
|
if (!rule.weekDays) {
|
|
rule.weekDays = [{
|
|
day: eventStartDate.getDay(),
|
|
offset: 0
|
|
}];
|
|
}
|
|
}
|
|
}), MonthlyFrequency = BaseFrequency.extend({
|
|
next: function (date, rule) {
|
|
var day, hours;
|
|
if (!BaseFrequency.fn.next(date, rule)) {
|
|
if (rule.hours) {
|
|
this._hour(date, rule, 1);
|
|
} else if (rule.monthDays || rule.weekDays || rule.yearDays || rule.weeks) {
|
|
this._date(date, rule, 1);
|
|
} else {
|
|
day = date.getDate();
|
|
hours = date.getHours();
|
|
date.setMonth(date.getMonth() + 1);
|
|
adjustDST(date, hours);
|
|
while (date.getDate() !== day) {
|
|
date.setDate(day);
|
|
adjustDST(date, hours);
|
|
}
|
|
this._hour(date, rule);
|
|
}
|
|
}
|
|
},
|
|
normalize: function (options) {
|
|
var rule = options.rule, date = options.date, hours = date.getHours();
|
|
if (options.idx === 0 && !rule.monthDays && !rule.weekDays) {
|
|
date.setDate(options.day);
|
|
adjustDST(date, hours);
|
|
} else {
|
|
BaseFrequency.fn.normalize(options);
|
|
}
|
|
},
|
|
setup: function (rule, eventStartDate, date) {
|
|
if (!rule.monthDays && !rule.weekDays) {
|
|
date.setDate(eventStartDate.getDate());
|
|
}
|
|
}
|
|
}), YearlyFrequency = MonthlyFrequency.extend({
|
|
next: function (date, rule) {
|
|
var day, hours = date.getHours();
|
|
if (!BaseFrequency.fn.next(date, rule)) {
|
|
if (rule.hours) {
|
|
this._hour(date, rule, 1);
|
|
} else if (rule.monthDays || rule.weekDays || rule.yearDays || rule.weeks) {
|
|
this._date(date, rule, 1);
|
|
} else if (rule.months) {
|
|
day = date.getDate();
|
|
date.setMonth(date.getMonth() + 1);
|
|
adjustDST(date, hours);
|
|
while (date.getDate() !== day) {
|
|
date.setDate(day);
|
|
adjustDST(date, hours);
|
|
}
|
|
this._hour(date, rule);
|
|
} else {
|
|
date.setFullYear(date.getFullYear() + 1);
|
|
adjustDST(date, hours);
|
|
this._hour(date, rule);
|
|
}
|
|
}
|
|
},
|
|
setup: function () {
|
|
}
|
|
}), frequencies = {
|
|
'hourly': new HourlyFrequency(),
|
|
'daily': new DailyFrequency(),
|
|
'weekly': new WeeklyFrequency(),
|
|
'monthly': new MonthlyFrequency(),
|
|
'yearly': new YearlyFrequency()
|
|
}, CLICK = 'click';
|
|
function intervalExcess(diff, interval) {
|
|
var excess;
|
|
if (diff !== 0 && diff < interval) {
|
|
excess = interval - diff;
|
|
} else {
|
|
excess = diff % interval;
|
|
if (excess) {
|
|
excess = interval - excess;
|
|
}
|
|
}
|
|
return excess;
|
|
}
|
|
function dayInYear(date) {
|
|
var month = date.getMonth();
|
|
var days = leapYear(date) ? DAYS_IN_LEAPYEAR[month] : DAYS_IN_YEAR[month];
|
|
return days + date.getDate();
|
|
}
|
|
function weekInYear(date, weekStart) {
|
|
var year, days;
|
|
date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
adjustDST(date, 0);
|
|
year = date.getFullYear();
|
|
if (weekStart !== undefined) {
|
|
setDayOfWeek(date, weekStart, -1);
|
|
date.setDate(date.getDate() + 4);
|
|
} else {
|
|
date.setDate(date.getDate() + (4 - (date.getDay() || 7)));
|
|
}
|
|
adjustDST(date, 0);
|
|
days = Math.floor((date.getTime() - new Date(year, 0, 1, -6)) / 86400000);
|
|
return 1 + Math.floor(days / 7);
|
|
}
|
|
function weekInMonth(date, weekStart) {
|
|
var firstWeekDay = firstDayOfMonth(date).getDay();
|
|
var firstWeekLength = 7 - (firstWeekDay + 7 - (weekStart || 7)) || 7;
|
|
if (firstWeekLength < 0) {
|
|
firstWeekLength += 7;
|
|
}
|
|
return Math.ceil((date.getDate() - firstWeekLength) / 7) + 1;
|
|
}
|
|
function normalizeDayIndex(weekDay, weekStart) {
|
|
return weekDay + (weekDay < weekStart ? 7 : 0);
|
|
}
|
|
function normalizeOffset(date, rule, weekStart) {
|
|
var offset = rule.offset;
|
|
if (!offset) {
|
|
return weekInMonth(date, weekStart);
|
|
}
|
|
var lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
var weeksInMonth = weekInMonth(lastDate, weekStart);
|
|
var day = normalizeDayIndex(rule.day, weekStart);
|
|
var skipFirst = day < normalizeDayIndex(new Date(date.getFullYear(), date.getMonth(), 1).getDay(), weekStart);
|
|
var skipLast = day > normalizeDayIndex(lastDate.getDay(), weekStart);
|
|
if (offset < 0) {
|
|
offset = weeksInMonth + (offset + 1 - (skipLast ? 1 : 0));
|
|
} else if (skipFirst) {
|
|
offset += 1;
|
|
}
|
|
weeksInMonth -= skipLast ? 1 : 0;
|
|
if (offset < (skipFirst ? 1 : 0) || offset > weeksInMonth) {
|
|
return null;
|
|
}
|
|
return offset;
|
|
}
|
|
function numberOfWeeks(date, weekStart) {
|
|
return weekInMonth(new Date(date.getFullYear(), date.getMonth() + 1, 0), weekStart);
|
|
}
|
|
function isInWeek(date, rule, weekStart) {
|
|
return weekInMonth(date, weekStart) === normalizeOffset(date, rule, weekStart);
|
|
}
|
|
function ruleWeekValues(weekDays, date, weekStart) {
|
|
var currentDay = normalizeDayIndex(date.getDay(), weekStart);
|
|
var length = weekDays.length;
|
|
var ruleWeekOffset;
|
|
var weekDay, day;
|
|
var weekNumber;
|
|
var result = [];
|
|
var idx = 0;
|
|
for (; idx < length; idx++) {
|
|
weekDay = weekDays[idx];
|
|
weekNumber = weekInMonth(date, weekStart);
|
|
ruleWeekOffset = normalizeOffset(date, weekDay, weekStart);
|
|
if (ruleWeekOffset === null) {
|
|
continue;
|
|
}
|
|
if (weekNumber < ruleWeekOffset) {
|
|
result.push(weekDay);
|
|
} else if (weekNumber === ruleWeekOffset) {
|
|
day = normalizeDayIndex(weekDay.day, weekStart);
|
|
if (currentDay < day) {
|
|
result.push(weekDay);
|
|
} else if (currentDay === day) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function ruleValues(rules, value, normalize) {
|
|
var idx = 0, length = rules.length, availableRules = [], ruleValue;
|
|
for (; idx < length; idx++) {
|
|
ruleValue = rules[idx];
|
|
if (normalize) {
|
|
ruleValue = normalize(ruleValue);
|
|
}
|
|
if (value === ruleValue) {
|
|
return null;
|
|
} else if (value < ruleValue) {
|
|
availableRules.push(ruleValue);
|
|
}
|
|
}
|
|
return availableRules;
|
|
}
|
|
function parseArray(list, range) {
|
|
var idx = 0, length = list.length, value;
|
|
for (; idx < length; idx++) {
|
|
value = parseInt(list[idx], 10);
|
|
if (isNaN(value) || value < range.start || value > range.end || value === 0 && range.start < 0) {
|
|
return null;
|
|
}
|
|
list[idx] = value;
|
|
}
|
|
return list.sort(numberSortPredicate);
|
|
}
|
|
function parseWeekDayList(list) {
|
|
var idx = 0, length = list.length, value, valueLength, day;
|
|
for (; idx < length; idx++) {
|
|
value = list[idx];
|
|
valueLength = value.length;
|
|
day = value.substring(valueLength - 2).toUpperCase();
|
|
day = WEEK_DAYS_IDX[day];
|
|
if (day === undefined) {
|
|
return null;
|
|
}
|
|
list[idx] = {
|
|
offset: parseInt(value.substring(0, valueLength - 2), 10) || 0,
|
|
day: day
|
|
};
|
|
}
|
|
return list;
|
|
}
|
|
function serializeWeekDayList(list) {
|
|
var idx = 0, length = list.length, value, valueString, result = [];
|
|
for (; idx < length; idx++) {
|
|
value = list[idx];
|
|
if (typeof value === 'string') {
|
|
valueString = value;
|
|
} else {
|
|
valueString = '' + WEEK_DAYS[value.day];
|
|
if (value.offset) {
|
|
valueString = value.offset + valueString;
|
|
}
|
|
}
|
|
result.push(valueString);
|
|
}
|
|
return result.toString();
|
|
}
|
|
function getMonthLength(date) {
|
|
var month = date.getMonth();
|
|
if (month === 1) {
|
|
if (new Date(date.getFullYear(), 1, 29).getMonth() === 1) {
|
|
return 29;
|
|
}
|
|
return 28;
|
|
}
|
|
return MONTHS[month];
|
|
}
|
|
function leapYear(year) {
|
|
year = year.getFullYear();
|
|
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
}
|
|
function numberSortPredicate(a, b) {
|
|
return a - b;
|
|
}
|
|
function parseExceptions(exceptions, zone) {
|
|
var idx = 0, length, date, dates = [];
|
|
if (exceptions) {
|
|
exceptions = exceptions.split(';');
|
|
length = exceptions.length;
|
|
for (; idx < length; idx++) {
|
|
date = parseUTCDate(exceptions[idx], zone);
|
|
if (date) {
|
|
dates.push(date);
|
|
}
|
|
}
|
|
}
|
|
return dates;
|
|
}
|
|
function isException(exceptions, date, zone) {
|
|
var dates = $.isArray(exceptions) ? exceptions : parseExceptions(exceptions, zone), dateTime = date.getTime() - date.getMilliseconds(), idx = 0, length = dates.length;
|
|
for (; idx < length; idx++) {
|
|
if (dates[idx].getTime() === dateTime) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function toExceptionString(dates, zone) {
|
|
var idx = 0;
|
|
var length;
|
|
var date;
|
|
var result = [].concat(dates);
|
|
for (length = result.length; idx < length; idx++) {
|
|
date = result[idx];
|
|
date = kendo.timezone.convert(date, zone || date.getTimezoneOffset(), 'Etc/UTC');
|
|
result[idx] = kendo.toString(date, RECURRENCE_DATE_FORMAT);
|
|
}
|
|
return result.join(';') + ';';
|
|
}
|
|
function startPeriodByFreq(start, rule) {
|
|
var date = new Date(start);
|
|
switch (rule.freq) {
|
|
case 'yearly':
|
|
date.setFullYear(date.getFullYear(), 0, 1);
|
|
break;
|
|
case 'monthly':
|
|
date.setFullYear(date.getFullYear(), date.getMonth(), 1);
|
|
break;
|
|
case 'weekly':
|
|
setDayOfWeek(date, rule.weekStart, -1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (rule.hours) {
|
|
date.setHours(0);
|
|
}
|
|
if (rule.minutes) {
|
|
date.setMinutes(0);
|
|
}
|
|
if (rule.seconds) {
|
|
date.setSeconds(0);
|
|
}
|
|
return date;
|
|
}
|
|
function endPeriodByFreq(start, rule) {
|
|
var date = new Date(start);
|
|
switch (rule.freq) {
|
|
case 'yearly':
|
|
date.setFullYear(date.getFullYear(), 11, 31);
|
|
break;
|
|
case 'monthly':
|
|
date.setFullYear(date.getFullYear(), date.getMonth() + 1, 0);
|
|
break;
|
|
case 'weekly':
|
|
setDayOfWeek(date, rule.weekStart, -1);
|
|
date.setDate(date.getDate() + 6);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (rule.hours) {
|
|
date.setHours(23);
|
|
}
|
|
if (rule.minutes) {
|
|
date.setMinutes(59);
|
|
}
|
|
if (rule.seconds) {
|
|
date.setSeconds(59);
|
|
}
|
|
return date;
|
|
}
|
|
function eventsByPosition(periodEvents, start, positions) {
|
|
var periodEventsLength = periodEvents.length;
|
|
var events = [];
|
|
var position;
|
|
var event;
|
|
for (var idx = 0, length = positions.length; idx < length; idx++) {
|
|
position = positions[idx];
|
|
if (position < 0) {
|
|
position = periodEventsLength + position;
|
|
} else {
|
|
position -= 1;
|
|
}
|
|
event = periodEvents[position];
|
|
if (event && event.start >= start) {
|
|
events.push(event);
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
function removeExceptionDates(periodEvents, exceptionDates, zone) {
|
|
var events = [];
|
|
var event;
|
|
for (var idx = 0; idx < periodEvents.length; idx++) {
|
|
event = periodEvents[idx];
|
|
if (!isException(exceptionDates, event.start, zone)) {
|
|
events.push(event);
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
function expand(event, start, end, zone) {
|
|
var rule = parseRule(event.recurrenceRule, zone), startTime, endTime, endDate, hours, minutes, seconds, durationMS, startPeriod, inPeriod, ruleStart, ruleEnd, useEventStart, freqName, exceptionDates, eventStartTime, eventStartMS, eventStart, count, freq, positions, currentIdx, periodEvents, events = [];
|
|
if (!rule) {
|
|
return [event];
|
|
}
|
|
positions = rule.positions;
|
|
currentIdx = positions ? 0 : 1;
|
|
ruleStart = rule.start;
|
|
ruleEnd = rule.end;
|
|
if (ruleStart || ruleEnd) {
|
|
event = event.clone({
|
|
start: ruleStart ? new Date(ruleStart.value[0]) : undefined,
|
|
end: ruleEnd ? new Date(ruleEnd.value[0]) : undefined
|
|
});
|
|
}
|
|
eventStart = event.start;
|
|
eventStartMS = eventStart.getTime();
|
|
eventStartTime = getMilliseconds(eventStart);
|
|
exceptionDates = parseExceptions(event.recurrenceException, zone);
|
|
if (!exceptionDates[0] && rule.exdates) {
|
|
exceptionDates = rule.exdates.value;
|
|
event.set('recurrenceException', toExceptionString(exceptionDates, zone));
|
|
}
|
|
startPeriod = start = new Date(start);
|
|
end = new Date(end);
|
|
freqName = rule.freq;
|
|
freq = frequencies[freqName];
|
|
count = rule.count;
|
|
if (rule.until && rule.until < end) {
|
|
end = new Date(rule.until);
|
|
}
|
|
useEventStart = freqName === 'yearly' || freqName === 'monthly' || freqName === 'weekly';
|
|
if (start < eventStartMS || count || rule.interval > 1 || useEventStart) {
|
|
start = new Date(eventStartMS);
|
|
} else {
|
|
hours = start.getHours();
|
|
minutes = start.getMinutes();
|
|
seconds = start.getSeconds();
|
|
if (!rule.hours) {
|
|
hours = eventStart.getHours();
|
|
}
|
|
if (!rule.minutes) {
|
|
minutes = eventStart.getMinutes();
|
|
}
|
|
if (!rule.seconds) {
|
|
seconds = eventStart.getSeconds();
|
|
}
|
|
start.setHours(hours, minutes, seconds, eventStart.getMilliseconds());
|
|
}
|
|
rule._startPeriod = new Date(start);
|
|
if (positions) {
|
|
start = startPeriodByFreq(start, rule);
|
|
end = endPeriodByFreq(end, rule);
|
|
var diff = getMilliseconds(end) - getMilliseconds(start);
|
|
if (diff < 0) {
|
|
hours = start.getHours();
|
|
end.setHours(hours, start.getMinutes(), start.getSeconds(), start.getMilliseconds());
|
|
kendoDate.adjustDST(end, hours);
|
|
}
|
|
rule._startPeriod = new Date(start);
|
|
rule._endPeriod = endPeriodByFreq(start, rule);
|
|
}
|
|
durationMS = event.duration();
|
|
rule._startTime = startTime = kendoDate.toInvariantTime(start);
|
|
if (freq.setup) {
|
|
freq.setup(rule, eventStart, start);
|
|
}
|
|
freq.limit(start, end, rule);
|
|
while (start <= end) {
|
|
endDate = new Date(start);
|
|
setTime(endDate, durationMS);
|
|
inPeriod = start >= startPeriod || endDate > startPeriod;
|
|
if (inPeriod && !isException(exceptionDates, start, zone) || positions) {
|
|
startTime = kendoDate.toUtcTime(kendoDate.getDate(start)) + getMilliseconds(rule._startTime);
|
|
endTime = startTime + durationMS;
|
|
if (eventStartMS !== start.getTime() || eventStartTime !== getMilliseconds(rule._startTime)) {
|
|
events.push(event.toOccurrence({
|
|
start: new Date(start),
|
|
end: endDate,
|
|
_startTime: startTime,
|
|
_endTime: endTime
|
|
}));
|
|
} else {
|
|
event._startTime = startTime;
|
|
event._endTime = endTime;
|
|
events.push(event);
|
|
}
|
|
}
|
|
if (positions) {
|
|
freq.next(start, rule);
|
|
freq.limit(start, end, rule);
|
|
if (start > rule._endPeriod) {
|
|
periodEvents = eventsByPosition(events.slice(currentIdx), eventStart, positions);
|
|
periodEvents = removeExceptionDates(periodEvents, exceptionDates, zone);
|
|
events = events.slice(0, currentIdx).concat(periodEvents);
|
|
rule._endPeriod = endPeriodByFreq(start, rule);
|
|
currentIdx = events.length;
|
|
}
|
|
if (count && count === currentIdx) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (count && count === currentIdx) {
|
|
break;
|
|
}
|
|
currentIdx += 1;
|
|
freq.next(start, rule);
|
|
freq.limit(start, end, rule);
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
function parseUTCDate(value, zone) {
|
|
value = kendo.parseDate(value, DATE_FORMATS);
|
|
if (value && zone) {
|
|
value = timezone.convert(value, value.getTimezoneOffset(), zone);
|
|
}
|
|
return value;
|
|
}
|
|
function parseDateRule(dateRule, zone) {
|
|
var pairs = dateRule.split(';');
|
|
var pair;
|
|
var property;
|
|
var value;
|
|
var tzid;
|
|
var valueIdx, valueLength;
|
|
for (var idx = 0, length = pairs.length; idx < length; idx++) {
|
|
pair = pairs[idx].split(':');
|
|
property = pair[0];
|
|
value = pair[1];
|
|
if (property.indexOf('TZID') !== -1) {
|
|
tzid = property.substring(property.indexOf('TZID')).split('=')[1];
|
|
}
|
|
if (value) {
|
|
value = value.split(',');
|
|
for (valueIdx = 0, valueLength = value.length; valueIdx < valueLength; valueIdx++) {
|
|
value[valueIdx] = parseUTCDate(value[valueIdx], tzid || zone);
|
|
}
|
|
}
|
|
}
|
|
if (value) {
|
|
return {
|
|
value: value,
|
|
tzid: tzid
|
|
};
|
|
}
|
|
}
|
|
function parseRule(recur, zone) {
|
|
var instance = {};
|
|
var splits, value;
|
|
var idx = 0, length;
|
|
var ruleValue = false;
|
|
var rule, part, parts;
|
|
var property, weekStart, weekDays;
|
|
var predicate = function (a, b) {
|
|
var day1 = a.day, day2 = b.day;
|
|
if (day1 < weekStart) {
|
|
day1 += 7;
|
|
}
|
|
if (day2 < weekStart) {
|
|
day2 += 7;
|
|
}
|
|
return day1 - day2;
|
|
};
|
|
if (!recur) {
|
|
return null;
|
|
}
|
|
parts = recur.split('\n');
|
|
if (!parts[1] && (recur.indexOf('DTSTART') !== -1 || recur.indexOf('DTEND') !== -1 || recur.indexOf('EXDATE') !== -1)) {
|
|
parts = recur.split(' ');
|
|
}
|
|
for (idx = 0, length = parts.length; idx < length; idx++) {
|
|
part = $.trim(parts[idx]);
|
|
if (part.indexOf('DTSTART') !== -1) {
|
|
instance.start = parseDateRule(part, zone);
|
|
} else if (part.indexOf('DTEND') !== -1) {
|
|
instance.end = parseDateRule(part, zone);
|
|
} else if (part.indexOf('EXDATE') !== -1) {
|
|
instance.exdates = parseDateRule(part, zone);
|
|
} else if (part.indexOf('RRULE') !== -1) {
|
|
rule = part.substring(6);
|
|
} else if ($.trim(part)) {
|
|
rule = part;
|
|
}
|
|
}
|
|
rule = rule.split(';');
|
|
for (idx = 0, length = rule.length; idx < length; idx++) {
|
|
property = rule[idx];
|
|
splits = property.split('=');
|
|
value = $.trim(splits[1]).split(',');
|
|
switch ($.trim(splits[0]).toUpperCase()) {
|
|
case 'FREQ':
|
|
instance.freq = value[0].toLowerCase();
|
|
break;
|
|
case 'UNTIL':
|
|
instance.until = parseUTCDate(value[0], zone);
|
|
break;
|
|
case 'COUNT':
|
|
instance.count = parseInt(value[0], 10);
|
|
break;
|
|
case 'INTERVAL':
|
|
instance.interval = parseInt(value[0], 10);
|
|
break;
|
|
case 'BYSECOND':
|
|
instance.seconds = parseArray(value, {
|
|
start: 0,
|
|
end: 60
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYMINUTE':
|
|
instance.minutes = parseArray(value, {
|
|
start: 0,
|
|
end: 59
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYHOUR':
|
|
instance.hours = parseArray(value, {
|
|
start: 0,
|
|
end: 23
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYMONTHDAY':
|
|
instance.monthDays = parseArray(value, {
|
|
start: -31,
|
|
end: 31
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYYEARDAY':
|
|
instance.yearDays = parseArray(value, {
|
|
start: -366,
|
|
end: 366
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYMONTH':
|
|
instance.months = parseArray(value, {
|
|
start: 1,
|
|
end: 12
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYDAY':
|
|
instance.weekDays = weekDays = parseWeekDayList(value);
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYWEEKNO':
|
|
instance.weeks = parseArray(value, {
|
|
start: -53,
|
|
end: 53
|
|
});
|
|
ruleValue = true;
|
|
break;
|
|
case 'BYSETPOS':
|
|
instance.positions = parseArray(value, {
|
|
start: -366,
|
|
end: 366
|
|
});
|
|
break;
|
|
case 'WKST':
|
|
instance.weekStart = weekStart = WEEK_DAYS_IDX[value[0]];
|
|
break;
|
|
}
|
|
}
|
|
if (instance.freq === undefined || instance.count !== undefined && instance.until) {
|
|
return null;
|
|
}
|
|
if (!instance.interval) {
|
|
instance.interval = 1;
|
|
}
|
|
if (weekStart === undefined) {
|
|
instance.weekStart = weekStart = kendo.culture().calendar.firstDay;
|
|
}
|
|
if (weekDays) {
|
|
instance.weekDays = weekDays.sort(predicate);
|
|
}
|
|
if (instance.positions && !ruleValue) {
|
|
instance.positions = null;
|
|
}
|
|
instance._hasRuleValue = ruleValue;
|
|
return instance;
|
|
}
|
|
function serializeDateRule(dateRule, zone) {
|
|
var value = dateRule.value;
|
|
var tzid = dateRule.tzid || '';
|
|
var length = value.length;
|
|
var idx = 0;
|
|
var val;
|
|
for (; idx < length; idx++) {
|
|
val = value[idx];
|
|
val = timezone.convert(val, tzid || zone || val.getTimezoneOffset(), 'Etc/UTC');
|
|
value[idx] = kendo.toString(val, 'yyyyMMddTHHmmssZ');
|
|
}
|
|
if (tzid) {
|
|
tzid = ';TZID=' + tzid;
|
|
}
|
|
return tzid + ':' + value.join(',') + ' ';
|
|
}
|
|
function serialize(rule, zone) {
|
|
var weekStart = rule.weekStart;
|
|
var ruleString = 'FREQ=' + rule.freq.toUpperCase();
|
|
var exdates = rule.exdates || '';
|
|
var start = rule.start || '';
|
|
var end = rule.end || '';
|
|
var until = rule.until;
|
|
if (rule.interval > 1) {
|
|
ruleString += ';INTERVAL=' + rule.interval;
|
|
}
|
|
if (rule.count) {
|
|
ruleString += ';COUNT=' + rule.count;
|
|
}
|
|
if (until) {
|
|
until = timezone.convert(until, zone || until.getTimezoneOffset(), 'Etc/UTC');
|
|
ruleString += ';UNTIL=' + kendo.toString(until, 'yyyyMMddTHHmmssZ');
|
|
}
|
|
if (rule.months) {
|
|
ruleString += ';BYMONTH=' + rule.months;
|
|
}
|
|
if (rule.weeks) {
|
|
ruleString += ';BYWEEKNO=' + rule.weeks;
|
|
}
|
|
if (rule.yearDays) {
|
|
ruleString += ';BYYEARDAY=' + rule.yearDays;
|
|
}
|
|
if (rule.monthDays) {
|
|
ruleString += ';BYMONTHDAY=' + rule.monthDays;
|
|
}
|
|
if (rule.weekDays) {
|
|
ruleString += ';BYDAY=' + serializeWeekDayList(rule.weekDays);
|
|
}
|
|
if (rule.hours) {
|
|
ruleString += ';BYHOUR=' + rule.hours;
|
|
}
|
|
if (rule.minutes) {
|
|
ruleString += ';BYMINUTE=' + rule.minutes;
|
|
}
|
|
if (rule.seconds) {
|
|
ruleString += ';BYSECOND=' + rule.seconds;
|
|
}
|
|
if (rule.positions) {
|
|
ruleString += ';BYSETPOS=' + rule.positions;
|
|
}
|
|
if (weekStart !== undefined) {
|
|
ruleString += ';WKST=' + WEEK_DAYS[weekStart];
|
|
}
|
|
if (start) {
|
|
start = 'DTSTART' + serializeDateRule(start, zone);
|
|
}
|
|
if (end) {
|
|
end = 'DTEND' + serializeDateRule(end, zone);
|
|
}
|
|
if (exdates) {
|
|
exdates = 'EXDATE' + serializeDateRule(exdates, zone);
|
|
}
|
|
if (start || end || exdates) {
|
|
ruleString = start + end + exdates + 'RRULE:' + ruleString;
|
|
}
|
|
return ruleString;
|
|
}
|
|
kendo.recurrence = {
|
|
rule: {
|
|
parse: parseRule,
|
|
serialize: serialize
|
|
},
|
|
expand: expand,
|
|
dayInYear: dayInYear,
|
|
weekInYear: weekInYear,
|
|
weekInMonth: weekInMonth,
|
|
numberOfWeeks: numberOfWeeks,
|
|
isException: isException,
|
|
toExceptionString: toExceptionString
|
|
};
|
|
var weekDayCheckBoxes = function (firstDay) {
|
|
var shortNames = kendo.culture().calendar.days.namesShort, length = shortNames.length, result = '', idx = 0, values = [];
|
|
for (; idx < length; idx++) {
|
|
values.push(idx);
|
|
}
|
|
shortNames = shortNames.slice(firstDay).concat(shortNames.slice(0, firstDay));
|
|
values = values.slice(firstDay).concat(values.slice(0, firstDay));
|
|
for (idx = 0; idx < length; idx++) {
|
|
result += '<label class="k-check"><input class="k-recur-weekday-checkbox" type="checkbox" value="' + values[idx] + '" /> ' + shortNames[idx] + '</label>';
|
|
}
|
|
return result;
|
|
};
|
|
var RECURRENCE_VIEW_TEMPLATE = kendo.template('# if (frequency !== "never") { #' + '<div class="k-edit-label"><label>#:messages.repeatEvery#</label></div>' + '<div class="k-edit-field"><input class="k-recur-interval"/>#:messages.interval#</div>' + '# } #' + '# if (frequency === "weekly") { #' + '<div class="k-edit-label"><label>#:messages.repeatOn#</label></div>' + '<div class="k-edit-field">#=weekDayCheckBoxes(firstWeekDay)#</div>' + '# } else if (frequency === "monthly") { #' + '<div class="k-edit-label"><label>#:messages.repeatOn#</label></div>' + '<div class="k-edit-field">' + '<ul class="k-reset">' + '<li>' + '<label><input class="k-recur-month-radio" type="radio" name="month" value="monthday" />#:messages.day#</label>' + '<input class="k-recur-monthday" />' + '</li>' + '<li>' + '<input class="k-recur-month-radio" type="radio" name="month" value="weekday" />' + '<input class="k-recur-weekday-offset" /><input class="k-recur-weekday" />' + '</li>' + '</ul>' + '</div>' + '# } else if (frequency === "yearly") { #' + '<div class="k-edit-label"><label>#:messages.repeatOn#</label></div>' + '<div class="k-edit-field">' + '<ul class="k-reset">' + '<li>' + '<input class="k-recur-year-radio" type="radio" name="year" value="monthday" />' + '<input class="k-recur-month" /><input class="k-recur-monthday" />' + '</li>' + '<li>' + '<input class="k-recur-year-radio" type="radio" name="year" value="weekday" />' + '<input class="k-recur-weekday-offset" /><input class="k-recur-weekday" />#:messages.of#<input class="k-recur-month" />' + '</li>' + '</ul>' + '</div>' + '# } #' + '# if (frequency !== "never") { #' + '<div class="k-edit-label"><label>#:end.label#</label></div>' + '<div class="k-edit-field">' + '<ul class="k-reset">' + '<li>' + '<label><input class="k-recur-end-never" type="radio" name="end" value="never" />#:end.never#</label>' + '</li>' + '<li>' + '<label><input class="k-recur-end-count" type="radio" name="end" value="count" />#:end.after#</label>' + '<input class="k-recur-count" />#:end.occurrence#' + '</li>' + '<li>' + '<label><input class="k-recur-end-until" type="radio" name="end" value="until" />#:end.on#</label>' + '<input class="k-recur-until" />' + '</li>' + '</ul>' + '</div>' + '# } #');
|
|
var DAY_RULE = [
|
|
{
|
|
day: 0,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 1,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 2,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 3,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 4,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 5,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 6,
|
|
offset: 0
|
|
}
|
|
];
|
|
var WEEKDAY_RULE = [
|
|
{
|
|
day: 1,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 2,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 3,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 4,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 5,
|
|
offset: 0
|
|
}
|
|
];
|
|
var WEEKEND_RULE = [
|
|
{
|
|
day: 0,
|
|
offset: 0
|
|
},
|
|
{
|
|
day: 6,
|
|
offset: 0
|
|
}
|
|
];
|
|
var BaseRecurrenceEditor = Widget.extend({
|
|
init: function (element, options) {
|
|
var start;
|
|
var that = this;
|
|
var frequencies = options && options.frequencies;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.wrapper = that.element;
|
|
options = that.options;
|
|
options.start = start = options.start || kendoDate.today();
|
|
if (frequencies) {
|
|
options.frequencies = frequencies;
|
|
}
|
|
if (typeof start === 'string') {
|
|
options.start = kendo.parseDate(start, 'yyyyMMddTHHmmss');
|
|
}
|
|
if (options.firstWeekDay === null) {
|
|
options.firstWeekDay = kendo.culture().calendar.firstDay;
|
|
}
|
|
that._namespace = '.' + options.name;
|
|
},
|
|
options: {
|
|
value: '',
|
|
start: '',
|
|
timezone: '',
|
|
spinners: true,
|
|
firstWeekDay: null,
|
|
frequencies: [
|
|
'never',
|
|
'daily',
|
|
'weekly',
|
|
'monthly',
|
|
'yearly'
|
|
],
|
|
mobile: false,
|
|
messages: {
|
|
frequencies: {
|
|
never: 'Never',
|
|
hourly: 'Hourly',
|
|
daily: 'Daily',
|
|
weekly: 'Weekly',
|
|
monthly: 'Monthly',
|
|
yearly: 'Yearly'
|
|
},
|
|
hourly: {
|
|
repeatEvery: 'Repeat every: ',
|
|
interval: ' hour(s)'
|
|
},
|
|
daily: {
|
|
repeatEvery: 'Repeat every: ',
|
|
interval: ' day(s)'
|
|
},
|
|
weekly: {
|
|
interval: ' week(s)',
|
|
repeatEvery: 'Repeat every: ',
|
|
repeatOn: 'Repeat on: '
|
|
},
|
|
monthly: {
|
|
repeatEvery: 'Repeat every: ',
|
|
repeatOn: 'Repeat on: ',
|
|
interval: ' month(s)',
|
|
day: 'Day '
|
|
},
|
|
yearly: {
|
|
repeatEvery: 'Repeat every: ',
|
|
repeatOn: 'Repeat on: ',
|
|
interval: ' year(s)',
|
|
of: ' of '
|
|
},
|
|
end: {
|
|
label: 'End:',
|
|
mobileLabel: 'Ends',
|
|
never: 'Never',
|
|
after: 'After ',
|
|
occurrence: ' occurrence(s)',
|
|
on: 'On '
|
|
},
|
|
offsetPositions: {
|
|
first: 'first',
|
|
second: 'second',
|
|
third: 'third',
|
|
fourth: 'fourth',
|
|
last: 'last'
|
|
},
|
|
weekdays: {
|
|
day: 'day',
|
|
weekday: 'weekday',
|
|
weekend: 'weekend day'
|
|
}
|
|
}
|
|
},
|
|
events: ['change'],
|
|
_initInterval: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
that._container.find('.k-recur-interval').kendoNumericTextBox({
|
|
spinners: that.options.spinners,
|
|
value: rule.interval || 1,
|
|
decimals: 0,
|
|
format: '#',
|
|
min: 1,
|
|
change: function () {
|
|
rule.interval = this.value();
|
|
that._trigger();
|
|
}
|
|
});
|
|
},
|
|
_weekDayRule: function (clear) {
|
|
var that = this;
|
|
var weekday = (that._weekDay.element || that._weekDay).val();
|
|
var offset = Number((that._weekDayOffset.element || that._weekDayOffset).val());
|
|
var weekDays = null;
|
|
var positions = null;
|
|
if (!clear) {
|
|
if (weekday === 'day') {
|
|
weekDays = DAY_RULE;
|
|
positions = offset;
|
|
} else if (weekday === 'weekday') {
|
|
weekDays = WEEKDAY_RULE;
|
|
positions = offset;
|
|
} else if (weekday === 'weekend') {
|
|
weekDays = WEEKEND_RULE;
|
|
positions = offset;
|
|
} else {
|
|
weekDays = [{
|
|
offset: offset,
|
|
day: Number(weekday)
|
|
}];
|
|
}
|
|
}
|
|
that._value.weekDays = weekDays;
|
|
that._value.positions = positions;
|
|
},
|
|
_weekDayView: function () {
|
|
var that = this;
|
|
var weekDays = that._value.weekDays;
|
|
var positions = that._value.positions;
|
|
var weekDayOffsetWidget = that._weekDayOffset;
|
|
var weekDayOffset;
|
|
var weekDayValue;
|
|
var length;
|
|
var method;
|
|
if (weekDays) {
|
|
length = weekDays.length;
|
|
if (positions) {
|
|
if (length === 7) {
|
|
weekDayValue = 'day';
|
|
weekDayOffset = positions;
|
|
} else if (length === 5) {
|
|
weekDayValue = 'weekday';
|
|
weekDayOffset = positions;
|
|
} else if (length === 2) {
|
|
weekDayValue = 'weekend';
|
|
weekDayOffset = positions;
|
|
}
|
|
}
|
|
if (!weekDayValue) {
|
|
weekDays = weekDays[0];
|
|
weekDayValue = weekDays.day;
|
|
weekDayOffset = weekDays.offset || '';
|
|
}
|
|
method = weekDayOffsetWidget.value ? 'value' : 'val';
|
|
weekDayOffsetWidget[method](weekDayOffset);
|
|
that._weekDay[method](weekDayValue);
|
|
}
|
|
},
|
|
_initWeekDay: function () {
|
|
var that = this, data;
|
|
var weekdayMessage = that.options.messages.weekdays;
|
|
var offsetMessage = that.options.messages.offsetPositions;
|
|
var weekDayInput = that._container.find('.k-recur-weekday');
|
|
var change = function () {
|
|
that._weekDayRule();
|
|
that._trigger();
|
|
};
|
|
if (weekDayInput[0]) {
|
|
that._weekDayOffset = new DropDownList(that._container.find('.k-recur-weekday-offset'), {
|
|
change: change,
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: [
|
|
{
|
|
text: offsetMessage.first,
|
|
value: '1'
|
|
},
|
|
{
|
|
text: offsetMessage.second,
|
|
value: '2'
|
|
},
|
|
{
|
|
text: offsetMessage.third,
|
|
value: '3'
|
|
},
|
|
{
|
|
text: offsetMessage.fourth,
|
|
value: '4'
|
|
},
|
|
{
|
|
text: offsetMessage.last,
|
|
value: '-1'
|
|
}
|
|
]
|
|
});
|
|
data = [
|
|
{
|
|
text: weekdayMessage.day,
|
|
value: 'day'
|
|
},
|
|
{
|
|
text: weekdayMessage.weekday,
|
|
value: 'weekday'
|
|
},
|
|
{
|
|
text: weekdayMessage.weekend,
|
|
value: 'weekend'
|
|
}
|
|
];
|
|
that._weekDay = new DropDownList(weekDayInput, {
|
|
value: that.options.start.getDay(),
|
|
change: change,
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: data.concat($.map(kendo.culture().calendar.days.names, function (dayName, idx) {
|
|
return {
|
|
text: dayName,
|
|
value: idx
|
|
};
|
|
}))
|
|
});
|
|
that._weekDayView();
|
|
}
|
|
},
|
|
_initWeekDays: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var weekDays = that._container.find('.k-recur-weekday-checkbox');
|
|
if (weekDays[0]) {
|
|
weekDays.on(CLICK + that._namespace, function () {
|
|
rule.weekDays = $.map(weekDays.filter(':checked'), function (checkbox) {
|
|
return {
|
|
day: Number(checkbox.value),
|
|
offset: 0
|
|
};
|
|
});
|
|
if (!that.options.mobile) {
|
|
that._trigger();
|
|
}
|
|
});
|
|
if (rule.weekDays) {
|
|
var idx, weekDay;
|
|
var i = 0, l = weekDays.length;
|
|
var length = rule.weekDays.length;
|
|
for (; i < l; i++) {
|
|
weekDay = weekDays[i];
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (weekDay.value == rule.weekDays[idx].day) {
|
|
weekDay.checked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_initMonthDay: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var monthDayInput = that._container.find('.k-recur-monthday');
|
|
if (monthDayInput[0]) {
|
|
that._monthDay = new kendo.ui.NumericTextBox(monthDayInput, {
|
|
spinners: that.options.spinners,
|
|
min: 1,
|
|
max: 31,
|
|
decimals: 0,
|
|
format: '#',
|
|
value: rule.monthDays ? rule.monthDays[0] : that.options.start.getDate(),
|
|
change: function () {
|
|
var value = this.value();
|
|
rule.monthDays = value ? [value] : value;
|
|
that._trigger();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_initCount: function () {
|
|
var that = this, input = that._container.find('.k-recur-count'), rule = that._value;
|
|
that._count = input.kendoNumericTextBox({
|
|
spinners: that.options.spinners,
|
|
value: rule.count || 1,
|
|
decimals: 0,
|
|
format: '#',
|
|
min: 1,
|
|
change: function () {
|
|
rule.count = this.value();
|
|
that._trigger();
|
|
}
|
|
}).data('kendoNumericTextBox');
|
|
},
|
|
_initUntil: function () {
|
|
var that = this, input = that._container.find('.k-recur-until'), start = that.options.start, rule = that._value, until = rule.until;
|
|
that._until = input.kendoDatePicker({
|
|
min: until && until < start ? until : start,
|
|
value: until || new Date(start.getFullYear(), start.getMonth(), start.getDate(), 23, 59, 59),
|
|
change: function () {
|
|
var date = this.value();
|
|
rule.until = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59);
|
|
that._trigger();
|
|
}
|
|
}).data('kendoDatePicker');
|
|
},
|
|
_trigger: function () {
|
|
if (!this.options.mobile) {
|
|
this.trigger('change');
|
|
}
|
|
}
|
|
});
|
|
var RecurrenceEditor = BaseRecurrenceEditor.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
BaseRecurrenceEditor.fn.init.call(that, element, options);
|
|
that._initFrequency();
|
|
that._initContainer();
|
|
that.value(that.options.value);
|
|
},
|
|
options: { name: 'RecurrenceEditor' },
|
|
events: ['change'],
|
|
destroy: function () {
|
|
var that = this;
|
|
that._frequency.destroy();
|
|
that._container.find('input[type=radio],input[type=checkbox]').off(CLICK + that._namespace);
|
|
kendo.destroy(that._container);
|
|
BaseRecurrenceEditor.fn.destroy.call(that);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var timezone = that.options.timezone;
|
|
var freq;
|
|
if (value === undefined) {
|
|
if (!that._value.freq) {
|
|
return '';
|
|
}
|
|
return serialize(that._value, timezone);
|
|
}
|
|
that._value = parseRule(value, timezone) || {};
|
|
freq = that._value.freq;
|
|
if (freq) {
|
|
that._frequency.value(freq);
|
|
} else {
|
|
that._frequency.select(0);
|
|
}
|
|
that._initView(that._frequency.value());
|
|
},
|
|
_initContainer: function () {
|
|
var element = this.element, container = $('<div class="k-recur-view" />'), editContainer = element.parent('.k-edit-field');
|
|
if (editContainer[0]) {
|
|
container.insertAfter(editContainer);
|
|
} else {
|
|
element.append(container);
|
|
}
|
|
this._container = container;
|
|
},
|
|
_initFrequency: function () {
|
|
var that = this, options = that.options, frequencies = options.frequencies, messages = options.messages.frequencies, ddl = $('<input />'), frequency;
|
|
frequencies = $.map(frequencies, function (frequency) {
|
|
return {
|
|
text: messages[frequency],
|
|
value: frequency
|
|
};
|
|
});
|
|
frequency = frequencies[0];
|
|
if (frequency && frequency.value === 'never') {
|
|
frequency.value = '';
|
|
}
|
|
that.element.append(ddl);
|
|
that._frequency = new DropDownList(ddl, {
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: frequencies,
|
|
change: function () {
|
|
that._value = {};
|
|
that._initView(that._frequency.value());
|
|
that.trigger('change');
|
|
}
|
|
});
|
|
},
|
|
_initView: function (frequency) {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var options = that.options;
|
|
var data = {
|
|
frequency: frequency || 'never',
|
|
weekDayCheckBoxes: weekDayCheckBoxes,
|
|
firstWeekDay: options.firstWeekDay,
|
|
messages: options.messages[frequency],
|
|
end: options.messages.end
|
|
};
|
|
kendo.destroy(that._container);
|
|
that._container.html(RECURRENCE_VIEW_TEMPLATE(data));
|
|
if (!frequency) {
|
|
that._value = {};
|
|
return;
|
|
}
|
|
rule.freq = frequency;
|
|
if (frequency === 'weekly' && !rule.weekDays) {
|
|
rule.weekDays = [{
|
|
day: options.start.getDay(),
|
|
offset: 0
|
|
}];
|
|
}
|
|
that._initInterval();
|
|
that._initWeekDays();
|
|
that._initMonthDay();
|
|
that._initWeekDay();
|
|
that._initMonth();
|
|
that._initCount();
|
|
that._initUntil();
|
|
that._period();
|
|
that._end();
|
|
},
|
|
_initMonth: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var month = rule.months || [that.options.start.getMonth() + 1];
|
|
var monthInputs = that._container.find('.k-recur-month');
|
|
var options;
|
|
if (monthInputs[0]) {
|
|
options = {
|
|
change: function () {
|
|
rule.months = [Number(this.value())];
|
|
that.trigger('change');
|
|
},
|
|
dataTextField: 'text',
|
|
dataValueField: 'value',
|
|
dataSource: $.map(kendo.culture().calendar.months.names, function (monthName, idx) {
|
|
return {
|
|
text: monthName,
|
|
value: idx + 1
|
|
};
|
|
})
|
|
};
|
|
that._month1 = new DropDownList(monthInputs[0], options);
|
|
that._month2 = new DropDownList(monthInputs[1], options);
|
|
if (month) {
|
|
month = month[0];
|
|
that._month1.value(month);
|
|
that._month2.value(month);
|
|
}
|
|
}
|
|
},
|
|
_end: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var container = that._container;
|
|
var namespace = that._namespace;
|
|
var click = function (e) {
|
|
that._toggleEnd(e.currentTarget.value);
|
|
that.trigger('change');
|
|
};
|
|
var endRule;
|
|
that._buttonNever = container.find('.k-recur-end-never').on(CLICK + namespace, click);
|
|
that._buttonCount = container.find('.k-recur-end-count').on(CLICK + namespace, click);
|
|
that._buttonUntil = container.find('.k-recur-end-until').on(CLICK + namespace, click);
|
|
if (rule.count) {
|
|
endRule = 'count';
|
|
} else if (rule.until) {
|
|
endRule = 'until';
|
|
}
|
|
that._toggleEnd(endRule);
|
|
},
|
|
_period: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var monthly = rule.freq === 'monthly';
|
|
var toggleRule = monthly ? that._toggleMonthDay : that._toggleYear;
|
|
var selector = '.k-recur-' + (monthly ? 'month' : 'year') + '-radio';
|
|
var radioButtons = that._container.find(selector);
|
|
if (!monthly && rule.freq !== 'yearly') {
|
|
return;
|
|
}
|
|
radioButtons.on(CLICK + that._namespace, function (e) {
|
|
toggleRule.call(that, e.currentTarget.value);
|
|
that.trigger('change');
|
|
});
|
|
that._buttonMonthDay = radioButtons.eq(0);
|
|
that._buttonWeekDay = radioButtons.eq(1);
|
|
toggleRule.call(that, rule.weekDays ? 'weekday' : 'monthday');
|
|
},
|
|
_toggleEnd: function (endRule) {
|
|
var that = this;
|
|
var count, until;
|
|
var enableCount, enableUntil;
|
|
if (endRule === 'count') {
|
|
that._buttonCount.prop('checked', true);
|
|
enableCount = true;
|
|
enableUntil = false;
|
|
count = that._count.value();
|
|
until = null;
|
|
} else if (endRule === 'until') {
|
|
that._buttonUntil.prop('checked', true);
|
|
enableCount = false;
|
|
enableUntil = true;
|
|
count = null;
|
|
until = that._until.value();
|
|
} else {
|
|
that._buttonNever.prop('checked', true);
|
|
enableCount = enableUntil = false;
|
|
count = until = null;
|
|
}
|
|
that._count.enable(enableCount);
|
|
that._until.enable(enableUntil);
|
|
that._value.count = count;
|
|
that._value.until = until;
|
|
},
|
|
_toggleMonthDay: function (monthRule) {
|
|
var that = this;
|
|
var enableMonthDay = false;
|
|
var enableWeekDay = true;
|
|
var clear = false;
|
|
var monthDays;
|
|
if (monthRule === 'monthday') {
|
|
that._buttonMonthDay.prop('checked', true);
|
|
monthDays = [that._monthDay.value()];
|
|
enableMonthDay = true;
|
|
enableWeekDay = false;
|
|
clear = true;
|
|
} else {
|
|
that._buttonWeekDay.prop('checked', true);
|
|
monthDays = null;
|
|
}
|
|
that._weekDay.enable(enableWeekDay);
|
|
that._weekDayOffset.enable(enableWeekDay);
|
|
that._monthDay.enable(enableMonthDay);
|
|
that._value.monthDays = monthDays;
|
|
that._weekDayRule(clear);
|
|
},
|
|
_toggleYear: function (yearRule) {
|
|
var that = this;
|
|
var enableMonth1 = false;
|
|
var enableMonth2 = true;
|
|
var month;
|
|
if (yearRule === 'monthday') {
|
|
enableMonth1 = true;
|
|
enableMonth2 = false;
|
|
month = that._month1.value();
|
|
} else {
|
|
month = that._month2.value();
|
|
}
|
|
that._month1.enable(enableMonth1);
|
|
that._month2.enable(enableMonth2);
|
|
that._value.months = [month];
|
|
that._toggleMonthDay(yearRule);
|
|
}
|
|
});
|
|
ui.plugin(RecurrenceEditor);
|
|
var RECURRENCE_HEADER_TEMPLATE = kendo.template('<div class="k-edit-label"><label>#:headerTitle#</label></div>' + '<div class="k-edit-field k-recur-pattern k-scheduler-toolbar"></div>' + '<div class="k-recur-view"></div>');
|
|
var RECURRENCE_REPEAT_PATTERN_TEMPLATE = kendo.template('# if (frequency !== "never") { #' + '<div class="k-edit-label"><label>#:messages.repeatEvery#</label></div>' + '<div class="k-edit-field"><input class="k-recur-interval" pattern="\\\\d*"/>#:messages.interval#</div>' + '# } #' + '# if (frequency === "weekly") { #' + '<div class="k-edit-label"><label>#:messages.repeatOn#</label></div>' + '<div class="k-edit-field">#=weekDayCheckBoxes(firstWeekDay)#</div>' + '# } else if (frequency === "monthly") { #' + '<div class="k-edit-label"><label>#:messages.repeatBy#</label></div>' + '<div class="k-edit-field k-scheduler-toolbar k-repeat-rule"></div>' + '<div class="k-monthday-view" style="display:none">' + '<div class="k-edit-label"><label>#:messages.day#</label></div>' + '<div class="k-edit-field"><input class="k-recur-monthday" pattern="\\\\d*"/></div>' + '</div>' + '<div class="k-weekday-view" style="display:none">' + '<div class="k-edit-label"><label>#:messages.every#</label></div>' + '<div class="k-edit-field"><select class="k-recur-weekday-offset"></select></div>' + '<div class="k-edit-label"><label>#:messages.day#</label></div>' + '<div class="k-edit-field"><select class="k-recur-weekday"></select></div>' + '</div>' + '# } else if (frequency === "yearly") { #' + '<div class="k-edit-label"><label>#:messages.repeatBy#</label></div>' + '<div class="k-edit-field k-scheduler-toolbar k-repeat-rule"></div>' + '<div class="k-monthday-view" style="display:none">' + '<div class="k-edit-label"><label>#:messages.day#</label></div>' + '<div class="k-edit-field"><input class="k-recur-monthday" pattern="\\\\d*"/></div>' + '</div>' + '<div class="k-weekday-view" style="display:none">' + '<div class="k-edit-label"><label>#:messages.every#</label></div>' + '<div class="k-edit-field"><select class="k-recur-weekday-offset"></select></div>' + '<div class="k-edit-label"><label>#:messages.day#</label></div>' + '<div class="k-edit-field"><select class="k-recur-weekday"></select></div>' + '</div>' + '<div class="k-edit-label"><label>#:messages.month#</label></div>' + '<div class="k-edit-field"><select class="k-recur-month"></select></div>' + '# } #');
|
|
var RECURRENCE_END_PATTERN_TEMPLATE = kendo.template('# if (endPattern === "count") { #' + '<div class="k-edit-label"><label>#:messages.after#</label></div>' + '<div class="k-edit-field"><input class="k-recur-count" pattern="\\\\d*" /></div>' + '# } else if (endPattern === "until") { #' + '<div class="k-edit-label"><label>#:messages.on#</label></div>' + '<div class="k-edit-field"><input type="date" class="k-recur-until" /></div>' + '# } #');
|
|
var RECURRENCE_GROUP_BUTTON_TEMPLATE = kendo.template('<ul class="k-reset k-header k-scheduler-navigation">' + '#for (var i = 0, length = dataSource.length; i < length; i++) {#' + '<li class="k-state-default #= value === dataSource[i].value ? "k-state-selected" : "" #">' + '<a role="button" href="\\#" class="k-link" data-#=ns#value="#=dataSource[i].value#">#:dataSource[i].text#</a>' + '</li>' + '#}#' + '</ul>');
|
|
var MobileRecurrenceEditor = BaseRecurrenceEditor.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
BaseRecurrenceEditor.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
that._optionTemplate = kendo.template('<option value="#:value#">#:text#</option>');
|
|
that.value(options.value);
|
|
that._pane = options.pane;
|
|
that._initRepeatButton();
|
|
that._initRepeatEnd();
|
|
that._defaultValue = that._value;
|
|
},
|
|
options: {
|
|
name: 'MobileRecurrenceEditor',
|
|
animations: {
|
|
left: 'slide',
|
|
right: 'slide:right'
|
|
},
|
|
mobile: true,
|
|
messages: {
|
|
cancel: 'Cancel',
|
|
update: 'Save',
|
|
endTitle: 'Repeat ends',
|
|
repeatTitle: 'Repeat pattern',
|
|
headerTitle: 'Repeat event',
|
|
end: {
|
|
patterns: {
|
|
never: 'Never',
|
|
after: 'After...',
|
|
on: 'On...'
|
|
},
|
|
never: 'Never',
|
|
after: 'End repeat after',
|
|
on: 'End repeat on'
|
|
},
|
|
daily: { interval: '' },
|
|
hourly: { interval: '' },
|
|
weekly: { interval: '' },
|
|
monthly: {
|
|
interval: '',
|
|
repeatBy: 'Repeat by: ',
|
|
dayOfMonth: 'Day of the month',
|
|
dayOfWeek: 'Day of the week',
|
|
repeatEvery: 'Repeat every',
|
|
every: 'Every',
|
|
day: 'Day '
|
|
},
|
|
yearly: {
|
|
interval: '',
|
|
repeatBy: 'Repeat by: ',
|
|
dayOfMonth: 'Day of the month',
|
|
dayOfWeek: 'Day of the week',
|
|
repeatEvery: 'Repeat every: ',
|
|
every: 'Every',
|
|
month: 'Month',
|
|
day: 'Day'
|
|
}
|
|
}
|
|
},
|
|
events: ['change'],
|
|
value: function (value) {
|
|
var that = this;
|
|
var timezone = that.options.timezone;
|
|
if (value === undefined) {
|
|
if (!that._value.freq) {
|
|
return '';
|
|
}
|
|
return serialize(that._value, timezone);
|
|
}
|
|
that._value = parseRule(value, timezone) || {};
|
|
},
|
|
destroy: function () {
|
|
this._destroyView();
|
|
kendo.destroy(this._endFields);
|
|
this._repeatButton.off(CLICK + this._namespace);
|
|
BaseRecurrenceEditor.fn.destroy.call(this);
|
|
},
|
|
_initRepeatButton: function () {
|
|
var that = this;
|
|
var freq = that.options.messages.frequencies[this._value.freq || 'never'];
|
|
that._repeatButton = $('<a href="#" class="k-button k-scheduler-recur">' + freq + '</a>').on(CLICK + that._namespace, function (e) {
|
|
e.preventDefault();
|
|
that._createView('repeat');
|
|
that._pane.navigate('recurrence', that.options.animations.left);
|
|
});
|
|
that.element.append(that._repeatButton);
|
|
},
|
|
_initRepeatEnd: function () {
|
|
var that = this;
|
|
var endLabelField = $('<div class="k-edit-label"><label>' + that.options.messages.end.mobileLabel + '</label></div>').insertAfter(that.element.parent('.k-edit-field'));
|
|
var endEditField = $('<div class="k-edit-field"><a href="#" class="k-button k-scheduler-recur-end"></a></div>').on(CLICK + that._namespace, function (e) {
|
|
e.preventDefault();
|
|
if (!that._value.freq) {
|
|
return;
|
|
}
|
|
that._createView('end');
|
|
that._pane.navigate('recurrence', that.options.animations.left);
|
|
}).insertAfter(endLabelField);
|
|
that._endFields = endLabelField.add(endEditField).toggleClass('k-state-disabled', !that._value.freq);
|
|
that._endButton = endEditField.find('.k-scheduler-recur-end').text(that._endText());
|
|
},
|
|
_endText: function () {
|
|
var rule = this._value;
|
|
var messages = this.options.messages.end;
|
|
var text = messages.never;
|
|
if (rule.count) {
|
|
text = kendo.format('{0} {1}', messages.after, rule.count);
|
|
} else if (rule.until) {
|
|
text = kendo.format('{0} {1:d}', messages.on, rule.until);
|
|
}
|
|
return text;
|
|
},
|
|
_initFrequency: function () {
|
|
var that = this;
|
|
var frequencyMessages = that.options.messages.frequencies;
|
|
var html = RECURRENCE_GROUP_BUTTON_TEMPLATE({
|
|
dataSource: $.map(this.options.frequencies, function (frequency) {
|
|
return {
|
|
text: frequencyMessages[frequency],
|
|
value: frequency !== 'never' ? frequency : ''
|
|
};
|
|
}),
|
|
value: that._value.freq || '',
|
|
ns: kendo.ns
|
|
});
|
|
that._view.element.find('.k-recur-pattern').append(html).on(CLICK + that._namespace, '.k-scheduler-navigation li', function (e) {
|
|
var li = $(this);
|
|
e.preventDefault();
|
|
li.addClass('k-state-selected').siblings().removeClass('k-state-selected');
|
|
that._value = { freq: li.children('a').attr(kendo.attr('value')) };
|
|
that._initRepeatView();
|
|
});
|
|
},
|
|
_initEndNavigation: function () {
|
|
var that = this;
|
|
var endMessages = that.options.messages.end.patterns;
|
|
var rule = that._value;
|
|
var value = '';
|
|
if (rule.count) {
|
|
value = 'count';
|
|
} else if (rule.until) {
|
|
value = 'until';
|
|
}
|
|
var html = RECURRENCE_GROUP_BUTTON_TEMPLATE({
|
|
dataSource: [
|
|
{
|
|
text: endMessages.never,
|
|
value: ''
|
|
},
|
|
{
|
|
text: endMessages.after,
|
|
value: 'count'
|
|
},
|
|
{
|
|
text: endMessages.on,
|
|
value: 'until'
|
|
}
|
|
],
|
|
value: value,
|
|
ns: kendo.ns
|
|
});
|
|
that._view.element.find('.k-recur-pattern').append(html).on(CLICK + that._namespace, '.k-scheduler-navigation li', function (e) {
|
|
var li = $(this);
|
|
var count = null;
|
|
var until = null;
|
|
e.preventDefault();
|
|
li.addClass('k-state-selected').siblings().removeClass('k-state-selected');
|
|
that._initEndView(li.children('a').attr(kendo.attr('value')));
|
|
if (that._count) {
|
|
count = that._count.value();
|
|
until = null;
|
|
} else if (that._until) {
|
|
count = null;
|
|
until = that._until.val ? kendo.parseDate(that._until.val(), 'yyyy-MM-dd') : that._until.value();
|
|
}
|
|
rule.count = count;
|
|
rule.until = until;
|
|
});
|
|
},
|
|
_createView: function (viewType) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var messages = options.messages;
|
|
var headerTitle = messages[viewType === 'repeat' ? 'repeatTitle' : 'endTitle'];
|
|
var html = '<div data-role="view" class="k-popup-edit-form k-scheduler-edit-form k-mobile-list" id="recurrence">' + '<div data-role="header" class="k-header">' + '<a href="#" class="k-button k-scheduler-cancel">' + messages.cancel + '</a>' + messages.headerTitle + '<a href="#" class="k-button k-scheduler-update">' + messages.update + '</a>' + '</div>';
|
|
var returnViewId = that._pane.view().id;
|
|
that._view = that._pane.append(html + RECURRENCE_HEADER_TEMPLATE({ headerTitle: headerTitle }));
|
|
that._view.element.on(CLICK + that._namespace, 'a.k-scheduler-cancel, a.k-scheduler-update', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if ($(this).hasClass('k-scheduler-update')) {
|
|
that.trigger('change');
|
|
that._defaultValue = $.extend({}, that._value);
|
|
} else {
|
|
that._value = that._defaultValue;
|
|
}
|
|
var frequency = that._value.freq;
|
|
that._endButton.text(that._endText());
|
|
that._endFields.toggleClass('k-state-disabled', !frequency);
|
|
that._repeatButton.text(messages.frequencies[frequency || 'never']);
|
|
that._pane.one('viewShow', function () {
|
|
that._destroyView();
|
|
});
|
|
that._pane.navigate(returnViewId, that.options.animations.right);
|
|
});
|
|
that._container = that._view.element.find('.k-recur-view');
|
|
if (viewType === 'repeat') {
|
|
that._initFrequency();
|
|
that._initRepeatView();
|
|
} else {
|
|
that._initEndNavigation();
|
|
that._initEndView();
|
|
}
|
|
},
|
|
_destroyView: function () {
|
|
if (this._view) {
|
|
this._view.destroy();
|
|
this._view.element.remove();
|
|
}
|
|
this._view = null;
|
|
},
|
|
_initRepeatView: function () {
|
|
var that = this;
|
|
var frequency = that._value.freq || 'never';
|
|
var data = {
|
|
frequency: frequency,
|
|
weekDayCheckBoxes: weekDayCheckBoxes,
|
|
firstWeekDay: that.options.firstWeekDay,
|
|
messages: that.options.messages[frequency]
|
|
};
|
|
var html = RECURRENCE_REPEAT_PATTERN_TEMPLATE(data);
|
|
var container = that._container;
|
|
var rule = that._value;
|
|
kendo.destroy(container);
|
|
container.html(html);
|
|
if (!html) {
|
|
that._value = {};
|
|
return;
|
|
}
|
|
if (frequency === 'weekly' && !rule.weekDays) {
|
|
rule.weekDays = [{
|
|
day: that.options.start.getDay(),
|
|
offset: 0
|
|
}];
|
|
}
|
|
that._initInterval();
|
|
that._initMonthDay();
|
|
that._initWeekDays();
|
|
that._initWeekDay();
|
|
that._initMonth();
|
|
that._period();
|
|
},
|
|
_initEndView: function (endPattern) {
|
|
var that = this;
|
|
var rule = that._value;
|
|
if (endPattern === undefined) {
|
|
if (rule.count) {
|
|
endPattern = 'count';
|
|
} else if (rule.until) {
|
|
endPattern = 'until';
|
|
}
|
|
}
|
|
var data = {
|
|
endPattern: endPattern,
|
|
messages: that.options.messages.end
|
|
};
|
|
kendo.destroy(that._container);
|
|
that._container.html(RECURRENCE_END_PATTERN_TEMPLATE(data));
|
|
that._initCount();
|
|
that._initUntil();
|
|
},
|
|
_initWeekDay: function () {
|
|
var that = this, data;
|
|
var weekdayMessage = that.options.messages.weekdays;
|
|
var offsetMessage = that.options.messages.offsetPositions;
|
|
var weekDaySelect = that._container.find('.k-recur-weekday');
|
|
var change = function () {
|
|
that._weekDayRule();
|
|
that.trigger('change');
|
|
};
|
|
if (weekDaySelect[0]) {
|
|
that._weekDayOffset = that._container.find('.k-recur-weekday-offset').html(that._options([
|
|
{
|
|
text: offsetMessage.first,
|
|
value: '1'
|
|
},
|
|
{
|
|
text: offsetMessage.second,
|
|
value: '2'
|
|
},
|
|
{
|
|
text: offsetMessage.third,
|
|
value: '3'
|
|
},
|
|
{
|
|
text: offsetMessage.fourth,
|
|
value: '4'
|
|
},
|
|
{
|
|
text: offsetMessage.last,
|
|
value: '-1'
|
|
}
|
|
])).change(change);
|
|
data = [
|
|
{
|
|
text: weekdayMessage.day,
|
|
value: 'day'
|
|
},
|
|
{
|
|
text: weekdayMessage.weekday,
|
|
value: 'weekday'
|
|
},
|
|
{
|
|
text: weekdayMessage.weekend,
|
|
value: 'weekend'
|
|
}
|
|
];
|
|
data = data.concat($.map(kendo.culture().calendar.days.names, function (dayName, idx) {
|
|
return {
|
|
text: dayName,
|
|
value: idx
|
|
};
|
|
}));
|
|
that._weekDay = weekDaySelect.html(that._options(data)).change(change).val(that.options.start.getDay());
|
|
that._weekDayView();
|
|
}
|
|
},
|
|
_initMonth: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var start = that.options.start;
|
|
var month = rule.months || [start.getMonth() + 1];
|
|
var monthSelect = that._container.find('.k-recur-month');
|
|
var monthNames = kendo.culture().calendar.months.names;
|
|
if (monthSelect[0]) {
|
|
var data = $.map(monthNames, function (monthName, idx) {
|
|
return {
|
|
text: monthName,
|
|
value: idx + 1
|
|
};
|
|
});
|
|
monthSelect.html(that._options(data)).change(function () {
|
|
rule.months = [Number(this.value)];
|
|
});
|
|
that._monthSelect = monthSelect;
|
|
if (month) {
|
|
monthSelect.val(month[0]);
|
|
}
|
|
}
|
|
},
|
|
_period: function () {
|
|
var that = this;
|
|
var rule = that._value;
|
|
var container = that._container;
|
|
var messages = that.options.messages[rule.freq];
|
|
var repeatRuleGroupButton = container.find('.k-repeat-rule');
|
|
var weekDayView = container.find('.k-weekday-view');
|
|
var monthDayView = container.find('.k-monthday-view');
|
|
if (repeatRuleGroupButton[0]) {
|
|
var currentValue = rule.weekDays ? 'weekday' : 'monthday';
|
|
var html = RECURRENCE_GROUP_BUTTON_TEMPLATE({
|
|
value: currentValue,
|
|
dataSource: [
|
|
{
|
|
text: messages.dayOfMonth,
|
|
value: 'monthday'
|
|
},
|
|
{
|
|
text: messages.dayOfWeek,
|
|
value: 'weekday'
|
|
}
|
|
],
|
|
ns: kendo.ns
|
|
});
|
|
var init = function (val) {
|
|
var weekDayName = that._weekDay.val();
|
|
var weekDayOffset = that._weekDayOffset.val();
|
|
var monthDay = that._monthDay.value();
|
|
var month = that._monthSelect ? that._monthSelect.val() : null;
|
|
if (val === 'monthday') {
|
|
rule.weekDays = null;
|
|
rule.monthDays = monthDay ? [monthDay] : monthDay;
|
|
rule.months = month ? [Number(month)] : month;
|
|
weekDayView.hide();
|
|
monthDayView.show();
|
|
} else {
|
|
rule.monthDays = null;
|
|
rule.months = month ? [Number(month)] : month;
|
|
rule.weekDays = [{
|
|
offset: Number(weekDayOffset),
|
|
day: Number(weekDayName)
|
|
}];
|
|
weekDayView.show();
|
|
monthDayView.hide();
|
|
}
|
|
};
|
|
repeatRuleGroupButton.append(html).on(CLICK + that._namespace, '.k-scheduler-navigation li', function (e) {
|
|
var li = $(this).addClass('k-state-selected');
|
|
e.preventDefault();
|
|
li.siblings().removeClass('k-state-selected');
|
|
var value = li.children('a').attr(kendo.attr('value'));
|
|
init(value);
|
|
});
|
|
init(currentValue);
|
|
}
|
|
},
|
|
_initUntil: function () {
|
|
var that = this;
|
|
var input = that._container.find('.k-recur-until');
|
|
var start = that.options.start;
|
|
var rule = that._value;
|
|
var until = rule.until;
|
|
var min = until && until < start ? until : start;
|
|
if (kendo.support.input.date) {
|
|
that._until = input.attr('min', kendo.toString(min, 'yyyy-MM-dd')).val(kendo.toString(until || start, 'yyyy-MM-dd')).on('change', function () {
|
|
rule.until = kendo.parseDate(this.value, 'yyyy-MM-dd');
|
|
});
|
|
} else {
|
|
that._until = input.kendoDatePicker({
|
|
min: min,
|
|
value: until || start,
|
|
change: function () {
|
|
rule.until = this.value();
|
|
}
|
|
}).data('kendoDatePicker');
|
|
}
|
|
},
|
|
_options: function (data, optionLabel) {
|
|
var idx = 0;
|
|
var html = '';
|
|
var length = data.length;
|
|
var template = this._optionTemplate;
|
|
if (optionLabel) {
|
|
html += template({
|
|
value: '',
|
|
text: optionLabel
|
|
});
|
|
}
|
|
for (; idx < length; idx++) {
|
|
html += template(data[idx]);
|
|
}
|
|
return html;
|
|
}
|
|
});
|
|
ui.plugin(MobileRecurrenceEditor);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler.timelineview', ['kendo.scheduler.view'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler.timelineview',
|
|
name: 'Scheduler Timeline View',
|
|
category: 'web',
|
|
description: 'The Scheduler Timeline View',
|
|
depends: ['scheduler.view'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, setTime = kendo.date.setTime, SchedulerView = ui.SchedulerView, extend = $.extend, proxy = $.proxy, getDate = kendo.date.getDate, getMilliseconds = kendo.date.getMilliseconds, MS_PER_DAY = kendo.date.MS_PER_DAY, MS_PER_MINUTE = kendo.date.MS_PER_MINUTE, NS = '.kendoTimelineView';
|
|
var EVENT_TEMPLATE = kendo.template('<div>' + '<div class="k-event-template k-event-time">#:kendo.format("{0:t} - {1:t}", start, end)#</div>' + '<div class="k-event-template">${title}</div></div>'), DATA_HEADER_TEMPLATE = kendo.template('<span class=\'k-link k-nav-day\'>#=kendo.format(\'{0:m}\', date)#</span>'), EVENT_WRAPPER_STRING = '<div role="gridcell" aria-selected="false" ' + 'data-#=ns#uid="#=uid#"' + '#if (resources[0]) { #' + 'style="background-color:#=resources[0].color#; border-color: #=resources[0].color#"' + 'class="k-event#=inverseColor ? " k-event-inverse" : ""#" ' + '#} else {#' + 'class="k-event"' + '#}#' + '>' + '<span class="k-event-actions">' + '# if(data.tail) {#' + '<span class="k-icon k-i-arrow-w"></span>' + '#}#' + '# if(data.isException()) {#' + '<span class="k-icon k-i-exception"></span>' + '# } else if(data.isRecurring()) {#' + '<span class="k-icon k-i-refresh"></span>' + '# } #' + '</span>' + '{0}' + '<span class="k-event-actions">' + '#if (showDelete) {#' + '<a href="\\#" class="k-link k-event-delete"><span class="k-icon k-si-close"></span></a>' + '#}#' + '# if(data.head) {#' + '<span class="k-icon k-i-arrow-e"></span>' + '#}#' + '</span>' + '#if(resizable && !data.tail){#' + '<span class="k-resize-handle k-resize-w"></span>' + '#}#' + '#if(resizable && !data.head){#' + '<span class="k-resize-handle k-resize-e"></span>' + '#}#' + '</div>';
|
|
function toInvariantTime(date) {
|
|
var staticDate = new Date(1980, 1, 1, 0, 0, 0);
|
|
setTime(staticDate, getMilliseconds(date));
|
|
return staticDate;
|
|
}
|
|
function getWorkDays(options) {
|
|
var workDays = [];
|
|
var dayIndex = options.workWeekStart;
|
|
workDays.push(dayIndex);
|
|
while (options.workWeekEnd != dayIndex) {
|
|
if (dayIndex > 6) {
|
|
dayIndex -= 7;
|
|
} else {
|
|
dayIndex++;
|
|
}
|
|
workDays.push(dayIndex);
|
|
}
|
|
return workDays;
|
|
}
|
|
function setColspan(columnLevel) {
|
|
var count = 0;
|
|
if (columnLevel.columns) {
|
|
for (var i = 0; i < columnLevel.columns.length; i++) {
|
|
count += setColspan(columnLevel.columns[i]);
|
|
}
|
|
columnLevel.colspan = count;
|
|
return count;
|
|
} else {
|
|
columnLevel.colspan = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
function collidingEvents(elements, left, right) {
|
|
var idx, startPosition, overlaps, endPosition;
|
|
for (idx = elements.length - 1; idx >= 0; idx--) {
|
|
startPosition = elements[idx].rectLeft;
|
|
endPosition = elements[idx].rectRight;
|
|
overlaps = startPosition <= left && endPosition >= left;
|
|
if (overlaps || startPosition >= left && endPosition <= right || left <= startPosition && right >= startPosition) {
|
|
if (startPosition < left) {
|
|
left = startPosition;
|
|
}
|
|
if (endPosition > right) {
|
|
right = endPosition;
|
|
}
|
|
}
|
|
}
|
|
return eventsForSlot(elements, left, right);
|
|
}
|
|
function eventsForSlot(elements, left, right) {
|
|
var events = [];
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
var event = {
|
|
rectLeft: elements[idx].rectLeft,
|
|
rectRight: elements[idx].rectRight
|
|
};
|
|
if (event.rectLeft < left && event.rectRight > left || event.rectLeft >= left && event.rectRight <= right) {
|
|
events.push(elements[idx]);
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
var TimelineView = SchedulerView.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
SchedulerView.fn.init.call(that, element, options);
|
|
that.title = that.options.title || that.options.name;
|
|
that._workDays = getWorkDays(that.options);
|
|
that._templates();
|
|
that._editable();
|
|
that.calculateDateRange();
|
|
that._groups();
|
|
that._currentTime();
|
|
},
|
|
name: 'timeline',
|
|
_currentTimeMarkerUpdater: function () {
|
|
var currentTime = new Date();
|
|
var options = this.options;
|
|
this.datesHeader.find('.k-current-time').remove();
|
|
if (!this._isInDateSlot({
|
|
start: currentTime,
|
|
end: currentTime
|
|
})) {
|
|
return;
|
|
}
|
|
if (options.currentTimeMarker.useLocalTimezone === false) {
|
|
var timezone = options.dataSource.options.schema.timezone;
|
|
if (options.dataSource && timezone) {
|
|
var timezoneOffset = kendo.timezone.offset(currentTime, timezone);
|
|
currentTime = kendo.timezone.convert(currentTime, currentTime.getTimezoneOffset(), timezoneOffset);
|
|
}
|
|
}
|
|
var groupsCount = !options.group || options.group.orientation == 'vertical' ? 1 : this.groups.length;
|
|
for (var groupIndex = 0; groupIndex < groupsCount; groupIndex++) {
|
|
var currentGroup = this.groups[groupIndex];
|
|
if (!currentGroup) {
|
|
return;
|
|
}
|
|
var utcCurrentTime = kendo.date.toUtcTime(currentTime);
|
|
var ranges = currentGroup.timeSlotRanges(utcCurrentTime, utcCurrentTime + 1);
|
|
if (ranges.length === 0) {
|
|
return;
|
|
}
|
|
var collection = ranges[0].collection;
|
|
var slotElement = collection.slotByStartDate(currentTime);
|
|
if (slotElement) {
|
|
var element = $('<div class=\'k-current-time\'></div>');
|
|
var datesHeader = this.datesHeader;
|
|
var left = Math.round(ranges[0].innerRect(currentTime, new Date(currentTime.getTime() + 1), false).left);
|
|
element.appendTo(datesHeader.find('.k-scheduler-header-wrap')).css({
|
|
left: this._adjustLeftPosition(left),
|
|
width: '1px',
|
|
bottom: '1px',
|
|
top: 0
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_adjustLeftPosition: function (left) {
|
|
if (this._isRtl) {
|
|
left -= this.content[0].scrollWidth - this.content[0].offsetWidth;
|
|
}
|
|
return left;
|
|
},
|
|
_currentTime: function () {
|
|
var that = this;
|
|
var markerOptions = that.options.currentTimeMarker;
|
|
if (markerOptions !== false && markerOptions.updateInterval !== undefined) {
|
|
var updateInterval = markerOptions.updateInterval;
|
|
that._currentTimeMarkerUpdater();
|
|
that._currentTimeUpdateTimer = setInterval(proxy(this._currentTimeMarkerUpdater, that), updateInterval);
|
|
}
|
|
},
|
|
_editable: function () {
|
|
if (this.options.editable) {
|
|
if (this._isMobile()) {
|
|
this._touchEditable();
|
|
} else {
|
|
this._mouseEditable();
|
|
}
|
|
}
|
|
},
|
|
_mouseEditable: function () {
|
|
var that = this;
|
|
that.element.on('click' + NS, '.k-event a:has(.k-si-close)', function (e) {
|
|
that.trigger('remove', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
if (that.options.editable.create !== false) {
|
|
that.element.on('dblclick' + NS, '.k-scheduler-content td', function (e) {
|
|
var slot = that._slotByPosition(e.pageX, e.pageY);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that.element.on('dblclick' + NS, '.k-event', function (e) {
|
|
that.trigger('edit', { uid: $(this).closest('.k-event').attr(kendo.attr('uid')) });
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
},
|
|
_touchEditable: function () {
|
|
var that = this;
|
|
var threshold = 0;
|
|
if (kendo.support.mobileOS.android) {
|
|
threshold = 5;
|
|
}
|
|
if (that.options.editable.create !== false) {
|
|
that._addUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-scheduler-content td',
|
|
tap: function (e) {
|
|
var x = e.x.location !== undefined ? e.x.location : e.x;
|
|
var y = e.y.location !== undefined ? e.y.location : e.y;
|
|
var slot = that._slotByPosition(x, y);
|
|
if (slot) {
|
|
var resourceInfo = that._resourceBySlot(slot);
|
|
that.trigger('add', {
|
|
eventInfo: extend({
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
}, resourceInfo)
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that._editUserEvents = new kendo.UserEvents(that.element, {
|
|
threshold: threshold,
|
|
filter: '.k-event',
|
|
tap: function (e) {
|
|
var eventElement = $(e.target).closest('.k-event');
|
|
if (!eventElement.hasClass('k-event-active')) {
|
|
that.trigger('edit', { uid: eventElement.attr(kendo.attr('uid')) });
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_slotByPosition: function (x, y) {
|
|
var slot;
|
|
var content = this.content;
|
|
var offset = content.offset();
|
|
var group;
|
|
var groupIndex;
|
|
x -= offset.left;
|
|
y -= offset.top;
|
|
if (this._isRtl) {
|
|
var browser = kendo.support.browser;
|
|
if (browser.mozilla) {
|
|
x += content[0].scrollWidth - content[0].offsetWidth;
|
|
x += content[0].scrollLeft;
|
|
} else if (browser.msie) {
|
|
x -= content.scrollLeft();
|
|
x += content[0].scrollWidth - content[0].offsetWidth;
|
|
} else if (browser.webkit) {
|
|
x += content[0].scrollLeft;
|
|
}
|
|
} else {
|
|
x += content[0].scrollLeft;
|
|
}
|
|
y += content[0].scrollTop;
|
|
x = Math.ceil(x);
|
|
y = Math.ceil(y);
|
|
for (groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
group = this.groups[groupIndex];
|
|
slot = group.timeSlotByPosition(x, y);
|
|
if (slot) {
|
|
return slot;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
options: {
|
|
name: 'TimelineView',
|
|
title: 'Timeline',
|
|
selectedDateFormat: '{0:D}',
|
|
selectedShortDateFormat: '{0:d}',
|
|
date: kendo.date.today(),
|
|
startTime: kendo.date.today(),
|
|
endTime: kendo.date.today(),
|
|
showWorkHours: false,
|
|
minorTickCount: 2,
|
|
editable: true,
|
|
workDayStart: new Date(1980, 1, 1, 8, 0, 0),
|
|
workDayEnd: new Date(1980, 1, 1, 17, 0, 0),
|
|
workWeekStart: 1,
|
|
workWeekEnd: 5,
|
|
majorTick: 60,
|
|
eventHeight: 25,
|
|
eventMinWidth: 0,
|
|
columnWidth: 100,
|
|
groupHeaderTemplate: '#=text#',
|
|
majorTimeHeaderTemplate: '#=kendo.toString(date, \'t\')#',
|
|
slotTemplate: ' ',
|
|
eventTemplate: EVENT_TEMPLATE,
|
|
dateHeaderTemplate: DATA_HEADER_TEMPLATE,
|
|
footer: { command: 'workDay' },
|
|
currentTimeMarker: {
|
|
updateInterval: 10000,
|
|
useLocalTimezone: true
|
|
},
|
|
messages: {
|
|
defaultRowText: 'All events',
|
|
showFullDay: 'Show full day',
|
|
showWorkDay: 'Show business hours'
|
|
}
|
|
},
|
|
events: [
|
|
'remove',
|
|
'add',
|
|
'edit'
|
|
],
|
|
_templates: function () {
|
|
var options = this.options, settings = extend({}, kendo.Template, options.templateSettings);
|
|
this.eventTemplate = this._eventTmpl(options.eventTemplate, EVENT_WRAPPER_STRING);
|
|
this.majorTimeHeaderTemplate = kendo.template(options.majorTimeHeaderTemplate, settings);
|
|
this.dateHeaderTemplate = kendo.template(options.dateHeaderTemplate, settings);
|
|
this.slotTemplate = kendo.template(options.slotTemplate, settings);
|
|
this.groupHeaderTemplate = kendo.template(options.groupHeaderTemplate, settings);
|
|
},
|
|
_render: function (dates) {
|
|
var that = this;
|
|
dates = dates || [];
|
|
that._dates = dates;
|
|
that._startDate = dates[0];
|
|
that._endDate = dates[dates.length - 1 || 0];
|
|
that._calculateSlotRanges();
|
|
that.createLayout(that._layout(dates));
|
|
that._content(dates);
|
|
that._footer();
|
|
that._setContentWidth();
|
|
that.refreshLayout();
|
|
that.datesHeader.on('click' + NS, '.k-nav-day', function (e) {
|
|
var th = $(e.currentTarget).closest('th');
|
|
var slot = that._slotByPosition(th.offset().left, that.content.offset().top);
|
|
that.trigger('navigate', {
|
|
view: 'timeline',
|
|
date: slot.startDate()
|
|
});
|
|
});
|
|
that.timesHeader.find('table tr:last').hide();
|
|
that.datesHeader.find('table tr:last').hide();
|
|
},
|
|
_setContentWidth: function () {
|
|
var content = this.content;
|
|
var contentWidth = content.width();
|
|
var contentTable = this.content.find('table');
|
|
var columnCount = contentTable.find('tr:first').children().length;
|
|
var minWidth = 100;
|
|
var calculatedWidth = columnCount * this.options.columnWidth;
|
|
if (contentWidth < calculatedWidth) {
|
|
minWidth = Math.ceil(calculatedWidth / contentWidth * 100);
|
|
}
|
|
contentTable.add(this.datesHeader.find('table')).css('width', minWidth + '%');
|
|
},
|
|
_calculateSlotRanges: function () {
|
|
var dates = this._dates;
|
|
var slotStartTime = this.startTime();
|
|
var slotEndTime = this.endTime();
|
|
if (getMilliseconds(slotEndTime) === getMilliseconds(kendo.date.getDate(slotEndTime))) {
|
|
slotEndTime = kendo.date.getDate(slotEndTime);
|
|
setTime(slotEndTime, MS_PER_DAY - 1);
|
|
}
|
|
slotEndTime = getMilliseconds(slotEndTime);
|
|
slotStartTime = getMilliseconds(slotStartTime);
|
|
var slotRanges = [];
|
|
for (var i = 0; i < dates.length; i++) {
|
|
var rangeStart = getDate(dates[i]);
|
|
setTime(rangeStart, slotStartTime);
|
|
var rangeEnd = getDate(dates[i]);
|
|
setTime(rangeEnd, slotEndTime);
|
|
slotRanges.push({
|
|
start: kendo.date.toUtcTime(rangeStart),
|
|
end: kendo.date.toUtcTime(rangeEnd)
|
|
});
|
|
}
|
|
this._slotRanges = slotRanges;
|
|
},
|
|
_forTimeRange: function (min, max, action, after) {
|
|
min = toInvariantTime(min);
|
|
max = toInvariantTime(max);
|
|
var that = this, msMin = getMilliseconds(min), msMax = getMilliseconds(max), minorTickCount = that.options.minorTickCount, msMajorInterval = that.options.majorTick * MS_PER_MINUTE, msInterval = msMajorInterval / minorTickCount || 1, start = new Date(+min), startDay = start.getDate(), msStart, idx = 0, length, html = '';
|
|
length = MS_PER_DAY / msInterval;
|
|
if (msMin != msMax) {
|
|
if (msMin > msMax) {
|
|
msMax += MS_PER_DAY;
|
|
}
|
|
length = (msMax - msMin) / msInterval;
|
|
}
|
|
length = Math.round(length);
|
|
for (; idx < length; idx++) {
|
|
var majorTickDivider = idx % (msMajorInterval / msInterval);
|
|
var isMajorTickColumn = majorTickDivider === 0;
|
|
var isMiddleColumn = majorTickDivider < minorTickCount - 1;
|
|
var isLastSlotColumn = majorTickDivider === minorTickCount - 1;
|
|
var minorTickColumns = minorTickCount;
|
|
if (length % minorTickCount !== 0) {
|
|
var isLastMajorSlot = length - (idx + 1) < minorTickCount;
|
|
if (isMajorTickColumn && isLastMajorSlot) {
|
|
minorTickColumns = length % minorTickCount;
|
|
}
|
|
}
|
|
html += action(start, isMajorTickColumn, isMiddleColumn, isLastSlotColumn, minorTickColumns);
|
|
setTime(start, msInterval, false);
|
|
}
|
|
if (msMax) {
|
|
msStart = getMilliseconds(start);
|
|
if (startDay < start.getDate()) {
|
|
msStart += MS_PER_DAY;
|
|
}
|
|
if (msStart > msMax) {
|
|
start = new Date(+max);
|
|
}
|
|
}
|
|
if (after) {
|
|
html += after(start);
|
|
}
|
|
return html;
|
|
},
|
|
_layout: function (dates) {
|
|
var timeColumns = [];
|
|
var columns = [];
|
|
var that = this;
|
|
var rows = [{ text: that.options.messages.defaultRowText }];
|
|
var minorTickSlots = [];
|
|
for (var minorTickIndex = 0; minorTickIndex < that.options.minorTickCount; minorTickIndex++) {
|
|
minorTickSlots.push({
|
|
text: '',
|
|
className: ''
|
|
});
|
|
}
|
|
this._forTimeRange(that.startTime(), that.endTime(), function (date, majorTick, middleColumn, lastSlotColumn, minorSlotsCount) {
|
|
var template = that.majorTimeHeaderTemplate;
|
|
if (majorTick) {
|
|
var timeColumn = {
|
|
text: template({ date: date }),
|
|
className: lastSlotColumn ? 'k-slot-cell' : '',
|
|
columns: minorTickSlots.slice(0, minorSlotsCount)
|
|
};
|
|
setColspan(timeColumn);
|
|
timeColumns.push(timeColumn);
|
|
}
|
|
});
|
|
for (var idx = 0; idx < dates.length; idx++) {
|
|
columns.push({
|
|
text: that.dateHeaderTemplate({ date: dates[idx] }),
|
|
className: 'k-slot-cell',
|
|
columns: timeColumns.slice(0)
|
|
});
|
|
}
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (this._groupOrientation() === 'vertical') {
|
|
rows = that._createRowsLayout(resources, null, this.groupHeaderTemplate);
|
|
} else {
|
|
columns = that._createColumnsLayout(resources, columns, this.groupHeaderTemplate);
|
|
}
|
|
}
|
|
return {
|
|
columns: columns,
|
|
rows: rows
|
|
};
|
|
},
|
|
_footer: function () {
|
|
var options = this.options;
|
|
if (options.footer !== false) {
|
|
var html = '<div class="k-header k-scheduler-footer">';
|
|
var command = options.footer.command;
|
|
if (command && command === 'workDay') {
|
|
html += '<ul class="k-reset k-header">';
|
|
html += '<li class="k-state-default k-scheduler-fullday"><a href="#" class="k-link"><span class="k-icon k-i-clock"></span>';
|
|
html += (options.showWorkHours ? options.messages.showFullDay : options.messages.showWorkDay) + '</a></li>';
|
|
html += '</ul>';
|
|
} else {
|
|
html += ' ';
|
|
}
|
|
html += '</div>';
|
|
this.footer = $(html).appendTo(this.element);
|
|
var that = this;
|
|
this.footer.on('click' + NS, '.k-scheduler-fullday', function (e) {
|
|
e.preventDefault();
|
|
that.trigger('navigate', {
|
|
view: that.name || options.name,
|
|
date: that.startDate(),
|
|
isWorkDay: !options.showWorkHours
|
|
});
|
|
});
|
|
}
|
|
},
|
|
_columnCountForLevel: function (level) {
|
|
var columnLevel = this.columnLevels[level];
|
|
return columnLevel ? columnLevel.length : 0;
|
|
},
|
|
_rowCountForLevel: function (level) {
|
|
var rowLevel = this.rowLevels[level];
|
|
return rowLevel ? rowLevel.length : 0;
|
|
},
|
|
_isWorkDay: function (date) {
|
|
var day = date.getDay();
|
|
var workDays = this._workDays;
|
|
for (var i = 0; i < workDays.length; i++) {
|
|
if (workDays[i] === day) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_content: function (dates) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var start = that.startTime();
|
|
var end = this.endTime();
|
|
var groupsCount = 1;
|
|
var rowCount = 1;
|
|
var columnCount = dates.length;
|
|
var html = '';
|
|
var resources = this.groupedResources;
|
|
var slotTemplate = this.slotTemplate;
|
|
var isVerticalGrouped = false;
|
|
if (resources.length) {
|
|
isVerticalGrouped = that._groupOrientation() === 'vertical';
|
|
if (isVerticalGrouped) {
|
|
rowCount = that._groupCount();
|
|
} else {
|
|
groupsCount = that._groupCount();
|
|
}
|
|
}
|
|
html += '<tbody>';
|
|
var appendRow = function (date) {
|
|
var content = '';
|
|
var classes = '';
|
|
var tmplDate;
|
|
var resources = function (groupIndex) {
|
|
return function () {
|
|
return that._resourceBySlot({ groupIndex: groupIndex });
|
|
};
|
|
};
|
|
if (kendo.date.isToday(dates[idx])) {
|
|
classes += 'k-today';
|
|
}
|
|
if (kendo.date.getMilliseconds(date) < kendo.date.getMilliseconds(options.workDayStart) || kendo.date.getMilliseconds(date) >= kendo.date.getMilliseconds(options.workDayEnd) || !that._isWorkDay(dates[idx])) {
|
|
classes += ' k-nonwork-hour';
|
|
}
|
|
content += '<td' + (classes !== '' ? ' class="' + classes + '"' : '') + '>';
|
|
tmplDate = kendo.date.getDate(dates[idx]);
|
|
kendo.date.setTime(tmplDate, kendo.date.getMilliseconds(date));
|
|
content += slotTemplate({
|
|
date: tmplDate,
|
|
resources: resources(isVerticalGrouped ? rowIdx : groupIdx)
|
|
});
|
|
content += '</td>';
|
|
return content;
|
|
};
|
|
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
|
|
html += '<tr>';
|
|
for (var groupIdx = 0; groupIdx < groupsCount; groupIdx++) {
|
|
for (var idx = 0, length = columnCount; idx < length; idx++) {
|
|
html += this._forTimeRange(start, end, appendRow);
|
|
}
|
|
}
|
|
html += '</tr>';
|
|
}
|
|
html += '</tbody>';
|
|
this.content.find('table').append(html);
|
|
},
|
|
_groups: function () {
|
|
var groupCount = this._groupCount();
|
|
var dates = this._dates;
|
|
var columnCount = dates.length;
|
|
this.groups = [];
|
|
for (var idx = 0; idx < groupCount; idx++) {
|
|
var view = this._addResourceView(idx);
|
|
var start = dates[0];
|
|
var end = dates[dates.length - 1 || 0];
|
|
view.addTimeSlotCollection(start, kendo.date.addDays(end, 1));
|
|
}
|
|
this._timeSlotGroups(groupCount, columnCount);
|
|
},
|
|
_isVerticallyGrouped: function () {
|
|
return this.groupedResources.length && this._groupOrientation() === 'vertical';
|
|
},
|
|
_isHorizontallyGrouped: function () {
|
|
return this.groupedResources.length && this._groupOrientation() === 'horizontal';
|
|
},
|
|
_timeSlotGroups: function (groupCount, datesCount) {
|
|
var interval = this._timeSlotInterval();
|
|
var isVerticallyGrouped = this._isVerticallyGrouped();
|
|
var tableRows = this.content.find('tr');
|
|
var rowCount = tableRows.length;
|
|
tableRows.attr('role', 'row');
|
|
if (isVerticallyGrouped) {
|
|
rowCount = Math.floor(rowCount / groupCount);
|
|
}
|
|
for (var groupIndex = 0; groupIndex < groupCount; groupIndex++) {
|
|
var rowMultiplier = 0;
|
|
var group = this.groups[groupIndex];
|
|
var time;
|
|
if (isVerticallyGrouped) {
|
|
rowMultiplier = groupIndex;
|
|
}
|
|
var rowIndex = rowMultiplier * rowCount;
|
|
var cellMultiplier = 0;
|
|
if (!isVerticallyGrouped) {
|
|
cellMultiplier = groupIndex;
|
|
}
|
|
var cells = tableRows[rowIndex].children;
|
|
var cellsPerGroup = cells.length / (!isVerticallyGrouped ? groupCount : 1);
|
|
var cellsPerDay = cellsPerGroup / datesCount;
|
|
for (var dateIndex = 0; dateIndex < datesCount; dateIndex++) {
|
|
var cellOffset = dateIndex * cellsPerDay + cellsPerGroup * cellMultiplier;
|
|
time = getMilliseconds(new Date(+this.startTime()));
|
|
for (var cellIndex = 0; cellIndex < cellsPerDay; cellIndex++) {
|
|
var cell = cells[cellIndex + cellOffset];
|
|
var collection = group.getTimeSlotCollection(0);
|
|
var currentDate = this._dates[dateIndex];
|
|
var currentTime = Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
|
|
var start = currentTime + time;
|
|
var end = start + interval;
|
|
cell.setAttribute('role', 'gridcell');
|
|
cell.setAttribute('aria-selected', false);
|
|
collection.addTimeSlot(cell, start, end, true);
|
|
time += interval;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
startDate: function () {
|
|
return this._startDate;
|
|
},
|
|
endDate: function () {
|
|
return this._endDate;
|
|
},
|
|
startTime: function () {
|
|
var options = this.options;
|
|
return options.showWorkHours ? options.workDayStart : options.startTime;
|
|
},
|
|
endTime: function () {
|
|
var options = this.options;
|
|
return options.showWorkHours ? options.workDayEnd : options.endTime;
|
|
},
|
|
_timeSlotInterval: function () {
|
|
var options = this.options;
|
|
return options.majorTick / options.minorTickCount * MS_PER_MINUTE;
|
|
},
|
|
nextDate: function () {
|
|
return kendo.date.nextDay(this.endDate());
|
|
},
|
|
previousDate: function () {
|
|
return kendo.date.previousDay(this.startDate());
|
|
},
|
|
calculateDateRange: function () {
|
|
this._render([this.options.date]);
|
|
},
|
|
render: function (events) {
|
|
this._headerColumnCount = 0;
|
|
this._groups();
|
|
this.element.find('.k-event').remove();
|
|
events = new kendo.data.Query(events).sort([
|
|
{
|
|
field: 'start',
|
|
dir: 'asc'
|
|
},
|
|
{
|
|
field: 'end',
|
|
dir: 'desc'
|
|
}
|
|
]).toArray();
|
|
var eventsByResource = [];
|
|
this._eventsByResource(events, this.groupedResources, eventsByResource);
|
|
var eventGroups = [];
|
|
var maxRowCount = 0;
|
|
for (var groupIndex = 0; groupIndex < eventsByResource.length; groupIndex++) {
|
|
var eventGroup = {
|
|
groupIndex: groupIndex,
|
|
maxRowCount: 0,
|
|
events: {}
|
|
};
|
|
eventGroups.push(eventGroup);
|
|
this._renderEvents(eventsByResource[groupIndex], groupIndex, eventGroup);
|
|
if (maxRowCount < eventGroup.maxRowCount) {
|
|
maxRowCount = eventGroup.maxRowCount;
|
|
}
|
|
}
|
|
this._setRowsHeight(eventGroups, eventsByResource.length, maxRowCount);
|
|
this._positionEvents(eventGroups, eventsByResource.length);
|
|
this.trigger('activate');
|
|
},
|
|
_positionEvents: function (eventGroups, groupsCount) {
|
|
for (var groupIndex = 0; groupIndex < groupsCount; groupIndex++) {
|
|
var eventsForGroup = eventGroups[groupIndex].events;
|
|
for (var eventUid in eventsForGroup) {
|
|
var eventObject = eventsForGroup[eventUid];
|
|
this._positionEvent(eventObject);
|
|
}
|
|
}
|
|
},
|
|
_setRowsHeight: function (eventGroups, groupsCount, maxRowCount) {
|
|
var eventHeight = this.options.eventHeight + 2;
|
|
var eventBottomOffset = this._getBottomRowOffset();
|
|
groupsCount = this._isVerticallyGrouped() ? groupsCount : 1;
|
|
for (var groupIndex = 0; groupIndex < groupsCount; groupIndex++) {
|
|
var rowsCount = this._isVerticallyGrouped() ? eventGroups[groupIndex].maxRowCount : maxRowCount;
|
|
rowsCount = rowsCount ? rowsCount : 1;
|
|
var rowHeight = (eventHeight + 2) * rowsCount + eventBottomOffset;
|
|
var timesRow = $(this.times.find('tr')[groupIndex]);
|
|
var row = $(this.content.find('tr')[groupIndex]);
|
|
timesRow.height(rowHeight);
|
|
row.height(rowHeight);
|
|
}
|
|
this._setContentWidth();
|
|
this.refreshLayout();
|
|
this._refreshSlots();
|
|
},
|
|
_getBottomRowOffset: function () {
|
|
var eventBottomOffset = this.options.eventHeight * 0.5;
|
|
var isMobile = this._isMobile();
|
|
var minOffset;
|
|
var maxOffset;
|
|
if (isMobile) {
|
|
minOffset = 30;
|
|
maxOffset = 60;
|
|
} else {
|
|
minOffset = 15;
|
|
maxOffset = 30;
|
|
}
|
|
if (eventBottomOffset > maxOffset) {
|
|
eventBottomOffset = maxOffset;
|
|
} else if (eventBottomOffset < minOffset) {
|
|
eventBottomOffset = minOffset;
|
|
}
|
|
return eventBottomOffset;
|
|
},
|
|
_positionEvent: function (eventObject) {
|
|
var eventHeight = this.options.eventHeight + 2;
|
|
var rect = eventObject.slotRange.innerRect(eventObject.start, eventObject.end, false);
|
|
var left = this._adjustLeftPosition(rect.left);
|
|
var width = rect.right - rect.left - 2;
|
|
if (width < 0) {
|
|
width = 0;
|
|
}
|
|
if (width < this.options.eventMinWidth) {
|
|
var slotsCollection = eventObject.slotRange.collection;
|
|
var lastSlot = slotsCollection._slots[slotsCollection._slots.length - 1];
|
|
var offsetRight = lastSlot.offsetLeft + lastSlot.offsetWidth;
|
|
width = this.options.eventMinWidth;
|
|
if (offsetRight < left + width) {
|
|
width = offsetRight - rect.left - 2;
|
|
}
|
|
}
|
|
eventObject.element.css({
|
|
top: eventObject.slotRange.start.offsetTop + eventObject.rowIndex * (eventHeight + 2) + 'px',
|
|
left: left,
|
|
width: width
|
|
});
|
|
},
|
|
_refreshSlots: function () {
|
|
for (var groupIndex = 0; groupIndex < this.groups.length; groupIndex++) {
|
|
this.groups[groupIndex].refresh();
|
|
}
|
|
},
|
|
_eventsByResource: function (events, resources, result) {
|
|
var resource = resources[0];
|
|
if (resource) {
|
|
var view = resource.dataSource.view();
|
|
for (var itemIdx = 0; itemIdx < view.length; itemIdx++) {
|
|
var value = this._resourceValue(resource, view[itemIdx]);
|
|
var eventsFilteredByResource = new kendo.data.Query(events).filter({
|
|
field: resource.field,
|
|
operator: SchedulerView.groupEqFilter(value)
|
|
}).toArray();
|
|
if (resources.length > 1) {
|
|
this._eventsByResource(eventsFilteredByResource, resources.slice(1), result);
|
|
} else {
|
|
result.push(eventsFilteredByResource);
|
|
}
|
|
}
|
|
} else {
|
|
result.push(events);
|
|
}
|
|
},
|
|
_isInDateSlot: function (event) {
|
|
var startTime = event.start;
|
|
var endTime = event.end;
|
|
var rangeStart = getDate(this._startDate);
|
|
var rangeEnd = kendo.date.addDays(getDate(this._endDate), 1);
|
|
if (startTime < rangeEnd && rangeStart <= endTime) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_isInTimeSlot: function (event) {
|
|
var startTime = event._startTime || kendo.date.toUtcTime(event.start);
|
|
var endTime = event._endTime || kendo.date.toUtcTime(event.end);
|
|
var slotRanges = this._slotRanges;
|
|
if (startTime === endTime) {
|
|
endTime = endTime + 1;
|
|
}
|
|
for (var slotIndex = 0; slotIndex < slotRanges.length; slotIndex++) {
|
|
if (startTime < slotRanges[slotIndex].end && slotRanges[slotIndex].start < endTime) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_adjustEvent: function (event) {
|
|
var start = event.start;
|
|
var end = event.end;
|
|
var eventStartTime = event._time('start');
|
|
var eventEndTime = event._time('end');
|
|
var startTime = getMilliseconds(this.startTime());
|
|
var endTime = getMilliseconds(this.endTime());
|
|
var adjustedStartDate = null;
|
|
var adjustedEndDate = null;
|
|
var occurrence;
|
|
var head = false;
|
|
var tail = false;
|
|
if (event.isAllDay) {
|
|
adjustedStartDate = getDate(start);
|
|
if (startTime > eventStartTime) {
|
|
setTime(adjustedStartDate, startTime);
|
|
tail = true;
|
|
}
|
|
adjustedEndDate = getDate(end);
|
|
if (endTime === getMilliseconds(getDate(this.endTime()))) {
|
|
adjustedEndDate = kendo.date.addDays(adjustedEndDate, 1);
|
|
} else {
|
|
setTime(adjustedEndDate, endTime);
|
|
head = true;
|
|
}
|
|
} else {
|
|
endTime = endTime === 0 ? MS_PER_DAY : endTime;
|
|
if (startTime > eventStartTime) {
|
|
adjustedStartDate = getDate(start);
|
|
setTime(adjustedStartDate, startTime);
|
|
tail = true;
|
|
} else if (endTime < eventStartTime) {
|
|
adjustedStartDate = getDate(start);
|
|
adjustedStartDate = kendo.date.addDays(adjustedStartDate, 1);
|
|
setTime(adjustedStartDate, startTime);
|
|
tail = true;
|
|
}
|
|
if (endTime < eventEndTime) {
|
|
adjustedEndDate = getDate(end);
|
|
setTime(adjustedEndDate, endTime);
|
|
head = true;
|
|
} else if (startTime > eventEndTime) {
|
|
adjustedEndDate = getDate(end);
|
|
adjustedEndDate = kendo.date.addDays(adjustedEndDate, -1);
|
|
setTime(adjustedEndDate, endTime);
|
|
head = true;
|
|
}
|
|
}
|
|
occurrence = event.clone({
|
|
start: adjustedStartDate ? adjustedStartDate : start,
|
|
end: adjustedEndDate ? adjustedEndDate : end,
|
|
_startTime: adjustedStartDate ? kendo.date.toUtcTime(adjustedStartDate) : event._startTime,
|
|
_endTime: adjustedEndDate ? kendo.date.toUtcTime(adjustedEndDate) : event._endTime,
|
|
isAllDay: false
|
|
});
|
|
return {
|
|
occurrence: occurrence,
|
|
head: head,
|
|
tail: tail
|
|
};
|
|
},
|
|
_renderEvents: function (events, groupIndex, eventGroup) {
|
|
var event;
|
|
var idx;
|
|
var length;
|
|
for (idx = 0, length = events.length; idx < length; idx++) {
|
|
event = events[idx];
|
|
if (this._isInDateSlot(event)) {
|
|
var isMultiDayEvent = event.isAllDay || event.end.getTime() - event.start.getTime() >= MS_PER_DAY;
|
|
var container = this.content;
|
|
if (isMultiDayEvent || this._isInTimeSlot(event)) {
|
|
var adjustedEvent = this._adjustEvent(event);
|
|
var group = this.groups[groupIndex];
|
|
if (!group._continuousEvents) {
|
|
group._continuousEvents = [];
|
|
}
|
|
var ranges = group.slotRanges(adjustedEvent.occurrence, false);
|
|
var range = ranges[0];
|
|
var element;
|
|
if (this._isInTimeSlot(adjustedEvent.occurrence)) {
|
|
element = this._createEventElement(adjustedEvent.occurrence, event, range.head || adjustedEvent.head, range.tail || adjustedEvent.tail);
|
|
element.appendTo(container).css({
|
|
top: 0,
|
|
height: this.options.eventHeight
|
|
});
|
|
var eventObject = {
|
|
start: adjustedEvent.occurrence._startTime || adjustedEvent.occurrence.start,
|
|
end: adjustedEvent.occurrence._endTime || adjustedEvent.occurrence.end,
|
|
element: element,
|
|
uid: event.uid,
|
|
slotRange: range,
|
|
rowIndex: 0,
|
|
offsetTop: 0
|
|
};
|
|
eventGroup.events[event.uid] = eventObject;
|
|
this.addContinuousEvent(group, range, element, event.isAllDay);
|
|
this._arrangeRows(eventObject, range, eventGroup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
addContinuousEvent: function (group, range, element, isAllDay) {
|
|
var events = group._continuousEvents;
|
|
events.push({
|
|
element: element,
|
|
isAllDay: isAllDay,
|
|
uid: element.attr(kendo.attr('uid')),
|
|
start: range.start,
|
|
end: range.end
|
|
});
|
|
},
|
|
_createEventElement: function (occurrence, event, head, tail) {
|
|
var template = this.eventTemplate;
|
|
var editable = this.options.editable;
|
|
var isMobile = this._isMobile();
|
|
var showDelete = editable && editable.destroy !== false && !isMobile;
|
|
var resizable = editable && editable.resize !== false;
|
|
var eventStartTime = event._time('start');
|
|
var eventEndTime = event._time('end');
|
|
var eventStartDate = event.start;
|
|
var eventEndDate = event.end;
|
|
var resources = this.eventResources(event);
|
|
if (event._startTime && eventStartTime !== kendo.date.getMilliseconds(event.start)) {
|
|
eventStartDate = new Date(eventStartTime);
|
|
eventStartDate = kendo.timezone.apply(eventStartDate, 'Etc/UTC');
|
|
}
|
|
if (event._endTime && eventEndTime !== kendo.date.getMilliseconds(event.end)) {
|
|
eventEndDate = new Date(eventEndTime);
|
|
eventEndDate = kendo.timezone.apply(eventEndDate, 'Etc/UTC');
|
|
}
|
|
var data = extend({}, {
|
|
ns: kendo.ns,
|
|
resizable: resizable,
|
|
showDelete: showDelete,
|
|
head: head,
|
|
tail: tail,
|
|
singleDay: this._dates.length == 1,
|
|
resources: resources,
|
|
inverseColor: resources && resources[0] ? this._shouldInverseResourceColor(resources[0]) : false
|
|
}, event, {
|
|
start: eventStartDate,
|
|
end: eventEndDate
|
|
});
|
|
var element = $(template(data));
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: element,
|
|
data: [{ dataItem: data }]
|
|
};
|
|
});
|
|
return element;
|
|
},
|
|
_arrangeRows: function (eventObject, slotRange, eventGroup) {
|
|
var startIndex = slotRange.start.index;
|
|
var endIndex = slotRange.end.index;
|
|
var rect = eventObject.slotRange.innerRect(eventObject.start, eventObject.end, false);
|
|
var rectRight = rect.right + this.options.eventMinWidth;
|
|
var events = collidingEvents(slotRange.events(), rect.left, rectRight);
|
|
slotRange.addEvent({
|
|
slotIndex: startIndex,
|
|
start: startIndex,
|
|
end: endIndex,
|
|
rectLeft: rect.left,
|
|
rectRight: rectRight,
|
|
element: eventObject.element,
|
|
uid: eventObject.uid
|
|
});
|
|
events.push({
|
|
start: startIndex,
|
|
end: endIndex,
|
|
uid: eventObject.uid
|
|
});
|
|
var rows = SchedulerView.createRows(events);
|
|
if (eventGroup.maxRowCount < rows.length) {
|
|
eventGroup.maxRowCount = rows.length;
|
|
}
|
|
for (var idx = 0, length = rows.length; idx < length; idx++) {
|
|
var rowEvents = rows[idx].events;
|
|
for (var j = 0, eventLength = rowEvents.length; j < eventLength; j++) {
|
|
eventGroup.events[rowEvents[j].uid].rowIndex = idx;
|
|
}
|
|
}
|
|
},
|
|
_groupCount: function () {
|
|
var resources = this.groupedResources;
|
|
if (resources.length) {
|
|
if (this._groupOrientation() === 'vertical') {
|
|
return this._rowCountForLevel(resources.length - 1);
|
|
} else {
|
|
return this._columnCountForLevel(resources.length - 1);
|
|
}
|
|
}
|
|
return 1;
|
|
},
|
|
_updateEventForSelection: function (event) {
|
|
var adjustedEvent = this._adjustEvent(event.clone());
|
|
return adjustedEvent.occurrence;
|
|
},
|
|
_eventOptionsForMove: function (event) {
|
|
if (event.isAllDay) {
|
|
return { isAllDay: false };
|
|
}
|
|
return {};
|
|
},
|
|
_updateEventForResize: function (event) {
|
|
if (event.isAllDay) {
|
|
event.set('isAllDay', false);
|
|
}
|
|
},
|
|
_updateMoveHint: function (event, groupIndex, distance) {
|
|
var group = this.groups[groupIndex];
|
|
var clonedEvent = event.clone({
|
|
start: event.start,
|
|
end: event.end
|
|
});
|
|
var eventDuraton = clonedEvent.duration();
|
|
clonedEvent.start = new Date(clonedEvent.start.getTime() + distance);
|
|
clonedEvent.end = new Date(+clonedEvent.start + eventDuraton);
|
|
var adjustedEvent = this._adjustEvent(clonedEvent);
|
|
var ranges = group.slotRanges(adjustedEvent.occurrence, false);
|
|
this._removeMoveHint();
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var startSlot = range.start;
|
|
var hint = this._createEventElement(adjustedEvent.occurrence, adjustedEvent.occurrence, false, false);
|
|
hint.addClass('k-event-drag-hint');
|
|
var rect = range.innerRect(adjustedEvent.occurrence.start, adjustedEvent.occurrence.end, this.options.snap);
|
|
var width = rect.right - rect.left - 2;
|
|
if (width < 0) {
|
|
width = 0;
|
|
}
|
|
var left = this._adjustLeftPosition(rect.left);
|
|
var css = {
|
|
left: left,
|
|
top: startSlot.offsetTop,
|
|
height: startSlot.offsetHeight - 2,
|
|
width: width
|
|
};
|
|
hint.css(css);
|
|
this._moveHint = this._moveHint.add(hint);
|
|
}
|
|
var content = this.content;
|
|
this._moveHint.appendTo(content);
|
|
},
|
|
_updateResizeHint: function (event, groupIndex, startTime, endTime) {
|
|
var group = this.groups[groupIndex];
|
|
var ranges = group.ranges(startTime, endTime, false, false);
|
|
this._removeResizeHint();
|
|
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
|
|
var range = ranges[rangeIndex];
|
|
var start = range.startSlot();
|
|
var startRect = range.innerRect(startTime, endTime, false);
|
|
startRect.top = start.offsetTop;
|
|
var width = startRect.right - startRect.left;
|
|
var height = start.offsetHeight;
|
|
var left = this._adjustLeftPosition(startRect.left);
|
|
var hint = SchedulerView.fn._createResizeHint.call(this, left, startRect.top, width, height);
|
|
this._resizeHint = this._resizeHint.add(hint);
|
|
}
|
|
var format = 't';
|
|
var container = this.content;
|
|
this._resizeHint.appendTo(container);
|
|
this._resizeHint.find('.k-label-top,.k-label-bottom').text('');
|
|
this._resizeHint.first().addClass('k-first').find('.k-label-top').text(kendo.toString(kendo.timezone.toLocalDate(startTime), format));
|
|
this._resizeHint.last().addClass('k-last').find('.k-label-bottom').text(kendo.toString(kendo.timezone.toLocalDate(endTime), format));
|
|
},
|
|
selectionByElement: function (cell) {
|
|
var offset = cell.offset();
|
|
return this._slotByPosition(offset.left, offset.top);
|
|
},
|
|
_updateDirection: function (selection, ranges, multiple, reverse, vertical) {
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
if (multiple && !vertical) {
|
|
if (startSlot.index === endSlot.index && startSlot.collectionIndex === endSlot.collectionIndex) {
|
|
selection.backward = reverse;
|
|
}
|
|
}
|
|
},
|
|
_changeGroup: function (selection, previous) {
|
|
var method = previous ? 'prevGroupSlot' : 'nextGroupSlot';
|
|
var slot = this[method](selection.start, selection.groupIndex, false);
|
|
if (slot) {
|
|
selection.groupIndex += previous ? -1 : 1;
|
|
}
|
|
return slot;
|
|
},
|
|
prevGroupSlot: function (date, groupIndex, isDay) {
|
|
var group = this.groups[groupIndex];
|
|
var slot = group.ranges(date, date, isDay, false)[0].start;
|
|
if (groupIndex <= 0) {
|
|
return;
|
|
}
|
|
if (this._isVerticallyGrouped()) {
|
|
return slot;
|
|
} else {
|
|
var collection = group._collection(0, isDay);
|
|
return collection.last();
|
|
}
|
|
},
|
|
nextGroupSlot: function (date, groupIndex, isDay) {
|
|
var group = this.groups[groupIndex];
|
|
var slot = group.ranges(date, date, isDay, false)[0].start;
|
|
if (groupIndex >= this.groups.length - 1) {
|
|
return;
|
|
}
|
|
if (this._isVerticallyGrouped()) {
|
|
return slot;
|
|
} else {
|
|
var collection = group._collection(0, isDay);
|
|
return collection.first();
|
|
}
|
|
},
|
|
_verticalSlots: function (selection, ranges, multiple, reverse) {
|
|
var method = reverse ? 'leftSlot' : 'rightSlot';
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
var group = this.groups[selection.groupIndex];
|
|
startSlot = group[method](startSlot);
|
|
endSlot = group[method](endSlot);
|
|
if (!multiple && this._isVerticallyGrouped() && (!startSlot || !endSlot)) {
|
|
startSlot = endSlot = this._changeGroup(selection, reverse);
|
|
}
|
|
return {
|
|
startSlot: startSlot,
|
|
endSlot: endSlot
|
|
};
|
|
},
|
|
_horizontalSlots: function (selection, ranges, multiple, reverse) {
|
|
var method = reverse ? 'upSlot' : 'downSlot';
|
|
var startSlot = ranges[0].start;
|
|
var endSlot = ranges[ranges.length - 1].end;
|
|
var group = this.groups[selection.groupIndex];
|
|
startSlot = group[method](startSlot);
|
|
endSlot = group[method](endSlot);
|
|
if (!multiple && this._isHorizontallyGrouped() && (!startSlot || !endSlot)) {
|
|
startSlot = endSlot = this._changeGroup(selection, reverse);
|
|
}
|
|
return {
|
|
startSlot: startSlot,
|
|
endSlot: endSlot
|
|
};
|
|
},
|
|
_changeViewPeriod: function (selection, reverse) {
|
|
var date = reverse ? this.previousDate() : this.nextDate();
|
|
var start = selection.start;
|
|
var end = selection.end;
|
|
selection.start = new Date(date);
|
|
selection.end = new Date(date);
|
|
if (this._isHorizontallyGrouped()) {
|
|
selection.groupIndex = reverse ? this.groups.length - 1 : 0;
|
|
}
|
|
var duration = end - start;
|
|
if (reverse) {
|
|
end = getMilliseconds(this.endTime());
|
|
end = end === 0 ? MS_PER_DAY : end;
|
|
setTime(selection.start, end - duration);
|
|
setTime(selection.end, end);
|
|
} else {
|
|
start = getMilliseconds(this.startTime());
|
|
setTime(selection.start, start);
|
|
setTime(selection.end, start + duration);
|
|
}
|
|
selection.events = [];
|
|
return true;
|
|
},
|
|
move: function (selection, key, shift) {
|
|
var handled = false;
|
|
var group = this.groups[selection.groupIndex];
|
|
var keys = kendo.keys;
|
|
var ranges = group.ranges(selection.start, selection.end, false, false);
|
|
var startSlot, endSlot, reverse, slots;
|
|
if (key === keys.DOWN || key === keys.UP) {
|
|
handled = true;
|
|
reverse = key === keys.UP;
|
|
this._updateDirection(selection, ranges, shift, reverse, true);
|
|
slots = this._verticalSlots(selection, ranges, shift, reverse);
|
|
} else if (key === keys.LEFT || key === keys.RIGHT) {
|
|
handled = true;
|
|
reverse = key === keys.LEFT;
|
|
this._updateDirection(selection, ranges, shift, reverse, false);
|
|
slots = this._horizontalSlots(selection, ranges, shift, reverse);
|
|
if ((!slots.startSlot || !slots.endSlot) && !shift && this._changeViewPeriod(selection, reverse, false)) {
|
|
return handled;
|
|
}
|
|
}
|
|
if (handled) {
|
|
startSlot = slots.startSlot;
|
|
endSlot = slots.endSlot;
|
|
if (shift) {
|
|
var backward = selection.backward;
|
|
if (backward && startSlot) {
|
|
selection.start = startSlot.startDate();
|
|
} else if (!backward && endSlot) {
|
|
selection.end = endSlot.endDate();
|
|
}
|
|
} else if (startSlot && endSlot) {
|
|
selection.start = startSlot.startDate();
|
|
selection.end = endSlot.endDate();
|
|
}
|
|
selection.events = [];
|
|
}
|
|
return handled;
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
if (that.element) {
|
|
that.element.off(NS);
|
|
}
|
|
if (that.footer) {
|
|
that.footer.remove();
|
|
}
|
|
if (that._currentTimeUpdateTimer) {
|
|
clearInterval(that._currentTimeUpdateTimer);
|
|
}
|
|
SchedulerView.fn.destroy.call(this);
|
|
if (this._isMobile() && that.options.editable) {
|
|
if (that.options.editable.create !== false) {
|
|
that._addUserEvents.destroy();
|
|
}
|
|
if (that.options.editable.update !== false) {
|
|
that._editUserEvents.destroy();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
extend(true, ui, {
|
|
TimelineView: TimelineView,
|
|
TimelineWeekView: TimelineView.extend({
|
|
options: {
|
|
name: 'TimelineWeekView',
|
|
title: 'Timeline Week',
|
|
selectedDateFormat: '{0:D} - {1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}',
|
|
majorTick: 120
|
|
},
|
|
name: 'timelineWeek',
|
|
calculateDateRange: function () {
|
|
var selectedDate = this.options.date, start = kendo.date.dayOfWeek(selectedDate, this.calendarInfo().firstDay, -1), idx, length, dates = [];
|
|
for (idx = 0, length = 7; idx < length; idx++) {
|
|
dates.push(start);
|
|
start = kendo.date.nextDay(start);
|
|
}
|
|
this._render(dates);
|
|
}
|
|
}),
|
|
TimelineWorkWeekView: TimelineView.extend({
|
|
options: {
|
|
name: 'TimelineWorkWeekView',
|
|
title: 'Timeline Work Week',
|
|
selectedDateFormat: '{0:D} - {1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}',
|
|
majorTick: 120
|
|
},
|
|
name: 'timelineWorkWeek',
|
|
nextDate: function () {
|
|
return kendo.date.dayOfWeek(kendo.date.nextDay(this.endDate()), this.options.workWeekStart, 1);
|
|
},
|
|
previousDate: function () {
|
|
return kendo.date.previousDay(this.startDate());
|
|
},
|
|
calculateDateRange: function () {
|
|
var selectedDate = this.options.date, start = kendo.date.dayOfWeek(selectedDate, this.options.workWeekStart, -1), end = kendo.date.dayOfWeek(start, this.options.workWeekEnd, 1), dates = [];
|
|
while (start <= end) {
|
|
dates.push(start);
|
|
start = kendo.date.nextDay(start);
|
|
}
|
|
this._render(dates);
|
|
}
|
|
}),
|
|
TimelineMonthView: TimelineView.extend({
|
|
options: {
|
|
name: 'TimelineMonthView',
|
|
title: 'Timeline Month',
|
|
selectedDateFormat: '{0:D} - {1:D}',
|
|
selectedShortDateFormat: '{0:d} - {1:d}',
|
|
workDayStart: new Date(1980, 1, 1, 0, 0, 0),
|
|
workDayEnd: new Date(1980, 1, 1, 23, 59, 59),
|
|
footer: false,
|
|
majorTick: 1440,
|
|
minorTickCount: 1
|
|
},
|
|
name: 'timelineMonth',
|
|
calculateDateRange: function () {
|
|
var selectedDate = this.options.date, start = kendo.date.firstDayOfMonth(selectedDate), end = kendo.date.lastDayOfMonth(selectedDate), idx, length, dates = [];
|
|
for (idx = 0, length = end.getDate(); idx < length; idx++) {
|
|
dates.push(start);
|
|
start = kendo.date.nextDay(start);
|
|
}
|
|
this._render(dates);
|
|
}
|
|
})
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.scheduler', [
|
|
'kendo.dropdownlist',
|
|
'kendo.editable',
|
|
'kendo.multiselect',
|
|
'kendo.window',
|
|
'kendo.datetimepicker',
|
|
'kendo.scheduler.recurrence',
|
|
'kendo.scheduler.view',
|
|
'kendo.scheduler.dayview',
|
|
'kendo.scheduler.agendaview',
|
|
'kendo.scheduler.monthview',
|
|
'kendo.scheduler.timelineview',
|
|
'kendo.mobile.actionsheet',
|
|
'kendo.mobile.pane',
|
|
'kendo.pdf'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'scheduler',
|
|
name: 'Scheduler',
|
|
category: 'web',
|
|
description: 'The Scheduler is an event calendar.',
|
|
depends: [
|
|
'dropdownlist',
|
|
'editable',
|
|
'multiselect',
|
|
'window',
|
|
'datepicker',
|
|
'datetimepicker',
|
|
'scheduler.recurrence',
|
|
'scheduler.view'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'scheduler-dayview',
|
|
name: 'Scheduler Day View',
|
|
description: 'Scheduler Day View',
|
|
depends: ['scheduler.dayview']
|
|
},
|
|
{
|
|
id: 'scheduler-agendaview',
|
|
name: 'Scheduler Agenda View',
|
|
description: 'Scheduler Agenda View',
|
|
depends: ['scheduler.agendaview']
|
|
},
|
|
{
|
|
id: 'scheduler-monthview',
|
|
name: 'Scheduler Month View',
|
|
description: 'Scheduler Month View',
|
|
depends: ['scheduler.monthview']
|
|
},
|
|
{
|
|
id: 'scheduler-timelineview',
|
|
name: 'Scheduler Timeline View',
|
|
description: 'Scheduler Timeline View',
|
|
depends: ['scheduler.timelineview']
|
|
},
|
|
{
|
|
id: 'scheduler-mobile',
|
|
name: 'Scheduler adaptive rendering',
|
|
description: 'Support for adaptive rendering',
|
|
depends: [
|
|
'mobile.actionsheet',
|
|
'mobile.pane'
|
|
]
|
|
},
|
|
{
|
|
id: 'scheduler-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export the scheduler events as PDF',
|
|
depends: [
|
|
'pdf',
|
|
'drawing'
|
|
]
|
|
},
|
|
{
|
|
id: 'scheduler-timezones',
|
|
name: 'Timezones',
|
|
description: 'Allow selecting timezones different than Etc/UTC',
|
|
depends: ['timezones']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, date = kendo.date, input_support = kendo.support.input, MS_PER_DAY = date.MS_PER_DAY, getDate = date.getDate, getMilliseconds = kendo.date.getMilliseconds, recurrence = kendo.recurrence, keys = kendo.keys, ui = kendo.ui, Widget = ui.Widget, DataBoundWidget = ui.DataBoundWidget, STRING = 'string', Popup = ui.Popup, Calendar = ui.Calendar, DataSource = kendo.data.DataSource, isPlainObject = $.isPlainObject, extend = $.extend, proxy = $.proxy, toString = Object.prototype.toString, isArray = $.isArray, NS = '.kendoScheduler', CLICK = 'click', CHANGE = 'change', CANCEL = 'cancel', REMOVE = 'remove', SAVE = 'save', ADD = 'add', EDIT = 'edit', valueStartEndBoundRegex = /(?:value:start|value:end)(?:,|$)/, TODAY = getDate(new Date()), RECURRENCE_EXCEPTION = 'recurrenceException', DELETECONFIRM = 'Are you sure you want to delete this event?', DELETERECURRING = 'Do you want to delete only this event occurrence or the whole series?', EDITRECURRING = 'Do you want to edit only this event occurrence or the whole series?', DELETERECURRINGCONFIRM = 'Are you sure you want to delete this event occurrence?', DELETESERIESCONFIRM = 'Are you sure you want to delete the whole series?', COMMANDBUTTONTMPL = '<a class="k-button #=className#" #=attr# href="\\#">#=text#</a>', VIEWBUTTONTEMPLATE = kendo.template('<li class="k-current-view" data-#=ns#name="#=view#"><a role="button" href="\\#" class="k-link">${views[view].title}</a></li>'), TOOLBARTEMPLATE = kendo.template('<div class="k-floatwrap k-header k-scheduler-toolbar">' + '# if (pdf) { #' + '<ul class="k-reset k-scheduler-tools">' + '<li><a role="button" href="\\#" class="k-button k-pdf"><span class="k-icon k-i-pdf"></span>${messages.pdf}</a></li>' + '</ul>' + '# } #' + '<ul class="k-reset k-scheduler-navigation">' + '<li class="k-state-default k-header k-nav-today"><a role="button" href="\\#" class="k-link">${messages.today}</a></li>' + '<li class="k-state-default k-header k-nav-prev"><a role="button" href="\\#" class="k-link"><span class="k-icon k-i-arrow-w"></span></a></li>' + '<li class="k-state-default k-header k-nav-next"><a role="button" href="\\#" class="k-link"><span class="k-icon k-i-arrow-e"></span></a></li>' + '<li class="k-state-default k-nav-current">' + '<a role="button" href="\\#" class="k-link">' + '<span class="k-icon k-i-calendar"></span>' + '<span class="k-sm-date-format" data-#=ns#bind="text: formattedShortDate"></span>' + '<span class="k-lg-date-format" data-#=ns#bind="text: formattedDate"></span>' + '</a>' + '</li>' + '</ul>' + '#if(viewsCount === 1){#' + '<a role="button" data-#=ns#name="#=view#" href="\\#" class="k-link k-scheduler-refresh">' + '<span class="k-icon k-i-refresh"></span>' + '</a>' + '#}else{#' + '<ul class="k-reset k-header k-scheduler-views">' + '#for(var view in views){#' + '<li class="k-state-default k-view-#= view.toLowerCase() #" data-#=ns#name="#=view#"><a role="button" href="\\#" class="k-link">${views[view].title}</a></li>' + '#}#' + '</ul>' + '#}#' + '</div>'), MOBILETOOLBARTEMPLATE = kendo.template('<div class="k-floatwrap k-header k-scheduler-toolbar">' + '<ul class="k-reset k-header k-scheduler-navigation">' + '<li class="k-state-default k-nav-today"><a role="button" href="\\#" class="k-link">${messages.today}</a></li>' + '</ul>' + '#if(viewsCount === 1){#' + '<a role="button" data-#=ns#name="#=view#" href="\\#" class="k-link k-scheduler-refresh">' + '<span class="k-icon k-i-refresh"></span>' + '</a>' + '#}else{#' + '<ul class="k-reset k-header k-scheduler-views">' + '#for(var view in views){#' + '<li class="k-state-default k-view-#= view.toLowerCase() #" data-#=ns#name="#=view#"><a role="button" href="\\#" class="k-link">${views[view].title}</a></li>' + '#}#' + '</ul>' + '#}#' + '</div>' + '<div class="k-floatwrap k-header k-scheduler-toolbar">' + '<ul class="k-reset k-header k-scheduler-navigation">' + '<li class="k-state-default k-nav-prev"><a role="button" href="\\#" class="k-link"><span class="k-icon k-i-arrow-w"></span></a></li>' + '<li class="k-state-default k-nav-current">' + '<span class="k-sm-date-format" data-#=ns#bind="text: formattedShortDate"></span>' + '<span class="k-lg-date-format" data-#=ns#bind="text: formattedDate"></span>' + '</li>' + '<li class="k-state-default k-nav-next"><a role="button" href="\\#" class="k-link"><span class="k-icon k-i-arrow-e"></span></a></li>' + '</ul>' + '</div>'), MOBILEDATERANGEEDITOR = function (container, options) {
|
|
var attr = { name: options.field };
|
|
var datepicker_role = !input_support.date ? kendo.attr('role') + '="datepicker" ' : '';
|
|
var datetimepicker_role = kendo.attr('role') + '="datetimepicker" ';
|
|
var isAllDay = options.model.isAllDay;
|
|
var dateTimeValidate = kendo.attr('validate') + '=\'' + !isAllDay + '\'';
|
|
var dateValidate = kendo.attr('validate') + '=\'' + isAllDay + '\'';
|
|
appendTimezoneAttr(attr, options);
|
|
appendDateCompareValidator(attr, options);
|
|
$('<input type="datetime-local" required ' + kendo.attr('type') + '="date" ' + datetimepicker_role + kendo.attr('bind') + '="value:' + options.field + ',invisible:isAllDay" ' + dateTimeValidate + '/>').attr(attr).appendTo(container);
|
|
$('<input type="date" required ' + kendo.attr('type') + '="date" ' + datepicker_role + kendo.attr('bind') + '="value:' + options.field + ',visible:isAllDay" ' + dateValidate + '/>').attr(attr).appendTo(container);
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
}, DATERANGEEDITOR = function (container, options) {
|
|
var attr = { name: options.field }, isAllDay = options.model.isAllDay, dateTimeValidate = kendo.attr('validate') + '=\'' + !isAllDay + '\' ', dateValidate = kendo.attr('validate') + '=\'' + isAllDay + '\' ';
|
|
appendTimezoneAttr(attr, options);
|
|
appendDateCompareValidator(attr, options);
|
|
$('<input type="text" required ' + kendo.attr('type') + '="date"' + ' ' + kendo.attr('role') + '="datetimepicker" ' + kendo.attr('bind') + '="value:' + options.field + ',invisible:isAllDay" ' + dateTimeValidate + '/>').attr(attr).appendTo(container);
|
|
$('<input type="text" required ' + kendo.attr('type') + '="date"' + ' ' + kendo.attr('role') + '="datepicker" ' + kendo.attr('bind') + '="value:' + options.field + ',visible:isAllDay" ' + dateValidate + '/>').attr(attr).appendTo(container);
|
|
$('<span ' + kendo.attr('bind') + '="text: ' + options.field + 'Timezone"></span>').appendTo(container);
|
|
if (options.field === 'end') {
|
|
$('<span ' + kendo.attr('bind') + '="text: startTimezone, invisible: endTimezone"></span>').appendTo(container);
|
|
}
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
}, RECURRENCEEDITOR = function (container, options) {
|
|
$('<div ' + kendo.attr('bind') + '="value:' + options.field + '" />').attr({ name: options.field }).appendTo(container).kendoRecurrenceEditor({
|
|
start: options.model.start,
|
|
timezone: options.timezone,
|
|
messages: options.messages
|
|
});
|
|
}, MOBILERECURRENCEEDITOR = function (container, options) {
|
|
$('<div ' + kendo.attr('bind') + '="value:' + options.field + '" />').attr({ name: options.field }).appendTo(container).kendoMobileRecurrenceEditor({
|
|
start: options.model.start,
|
|
timezone: options.timezone,
|
|
messages: options.messages,
|
|
pane: options.pane,
|
|
value: options.model[options.field]
|
|
});
|
|
}, MOBILETIMEZONEPOPUP = function (container, options) {
|
|
var text = timezoneButtonText(options.model, options.messages.noTimezone);
|
|
$('<a href="#" class="k-button k-timezone-button" data-bind="invisible:isAllDay">' + text + '</a>').click(options.click).appendTo(container);
|
|
}, TIMEZONEPOPUP = function (container, options) {
|
|
$('<a href="#" class="k-button" data-bind="invisible:isAllDay">' + options.messages.timezoneEditorButton + '</a>').click(options.click).appendTo(container);
|
|
}, MOBILETIMEZONEEDITOR = function (container, options) {
|
|
$('<div ' + kendo.attr('bind') + '="value:' + options.field + '" />').attr({ name: options.field }).toggle(options.visible).appendTo(container).kendoMobileTimezoneEditor({ optionLabel: options.noTimezone });
|
|
}, TIMEZONEEDITOR = function (container, options) {
|
|
$('<div ' + kendo.attr('bind') + '="value:' + options.field + '" />').attr({ name: options.field }).toggle(options.visible).appendTo(container).kendoTimezoneEditor({ optionLabel: options.noTimezone });
|
|
};
|
|
function timezoneButtonText(model, message) {
|
|
message = message || '';
|
|
if (model.startTimezone) {
|
|
message = model.startTimezone;
|
|
if (model.endTimezone) {
|
|
message += ' | ' + model.endTimezone;
|
|
}
|
|
}
|
|
return message;
|
|
}
|
|
function appendTimezoneAttr(attrs, options) {
|
|
var timezone = options.timezone;
|
|
if (timezone) {
|
|
attrs[kendo.attr('timezone')] = timezone;
|
|
}
|
|
}
|
|
function appendDateCompareValidator(attrs, options) {
|
|
var validationRules = options.model.fields[options.field].validation;
|
|
if (validationRules) {
|
|
var dateCompareRule = validationRules.dateCompare;
|
|
if (dateCompareRule && isPlainObject(dateCompareRule) && dateCompareRule.message) {
|
|
attrs[kendo.attr('dateCompare-msg')] = dateCompareRule.message;
|
|
}
|
|
}
|
|
}
|
|
function wrapDataAccess(originalFunction, timezone) {
|
|
return function (data) {
|
|
data = originalFunction(data);
|
|
convertData(data, 'apply', timezone);
|
|
return data || [];
|
|
};
|
|
}
|
|
function wrapDataSerialization(originalFunction, timezone) {
|
|
return function (data) {
|
|
if (data) {
|
|
if (toString.call(data) !== '[object Array]' && !(data instanceof kendo.data.ObservableArray)) {
|
|
data = [data];
|
|
}
|
|
}
|
|
convertData(data, 'remove', timezone, true);
|
|
data = originalFunction(data);
|
|
return data || [];
|
|
};
|
|
}
|
|
function convertData(data, method, timezone, removeUid) {
|
|
var event, idx, length;
|
|
data = data || [];
|
|
for (idx = 0, length = data.length; idx < length; idx++) {
|
|
event = data[idx];
|
|
if (removeUid) {
|
|
if (event.startTimezone || event.endTimezone) {
|
|
if (timezone) {
|
|
event.start = kendo.timezone.convert(event.start, event.startTimezone || event.endTimezone, timezone);
|
|
event.end = kendo.timezone.convert(event.end, event.endTimezone || event.startTimezone, timezone);
|
|
event.start = kendo.timezone[method](event.start, timezone);
|
|
event.end = kendo.timezone[method](event.end, timezone);
|
|
} else {
|
|
event.start = kendo.timezone[method](event.start, event.startTimezone || event.endTimezone);
|
|
event.end = kendo.timezone[method](event.end, event.endTimezone || event.startTimezone);
|
|
}
|
|
} else if (timezone) {
|
|
event.start = kendo.timezone[method](event.start, timezone);
|
|
event.end = kendo.timezone[method](event.end, timezone);
|
|
}
|
|
} else {
|
|
if (event.startTimezone || event.endTimezone) {
|
|
event.start = kendo.timezone[method](event.start, event.startTimezone || event.endTimezone);
|
|
event.end = kendo.timezone[method](event.end, event.endTimezone || event.startTimezone);
|
|
if (timezone) {
|
|
event.start = kendo.timezone.convert(event.start, event.startTimezone || event.endTimezone, timezone);
|
|
event.end = kendo.timezone.convert(event.end, event.endTimezone || event.startTimezone, timezone);
|
|
}
|
|
} else if (timezone) {
|
|
event.start = kendo.timezone[method](event.start, timezone);
|
|
event.end = kendo.timezone[method](event.end, timezone);
|
|
}
|
|
}
|
|
if (removeUid) {
|
|
delete event.uid;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
function getOccurrenceByUid(data, uid) {
|
|
var length = data.length, idx = 0, event;
|
|
for (; idx < length; idx++) {
|
|
event = data[idx];
|
|
if (event.uid === uid) {
|
|
return event;
|
|
}
|
|
}
|
|
}
|
|
var SchedulerDataReader = kendo.Class.extend({
|
|
init: function (schema, reader) {
|
|
var timezone = schema.timezone;
|
|
this.reader = reader;
|
|
if (reader.model) {
|
|
this.model = reader.model;
|
|
}
|
|
this.timezone = timezone;
|
|
this.data = wrapDataAccess($.proxy(this.data, this), timezone);
|
|
this.serialize = wrapDataSerialization($.proxy(this.serialize, this), timezone);
|
|
},
|
|
errors: function (data) {
|
|
return this.reader.errors(data);
|
|
},
|
|
parse: function (data) {
|
|
return this.reader.parse(data);
|
|
},
|
|
data: function (data) {
|
|
return this.reader.data(data);
|
|
},
|
|
total: function (data) {
|
|
return this.reader.total(data);
|
|
},
|
|
groups: function (data) {
|
|
return this.reader.groups(data);
|
|
},
|
|
aggregates: function (data) {
|
|
return this.reader.aggregates(data);
|
|
},
|
|
serialize: function (data) {
|
|
return this.reader.serialize(data);
|
|
}
|
|
});
|
|
function applyZone(date, fromZone, toZone) {
|
|
if (toZone) {
|
|
date = kendo.timezone.convert(date, fromZone, toZone);
|
|
} else {
|
|
date = kendo.timezone.remove(date, fromZone);
|
|
}
|
|
return date;
|
|
}
|
|
function dateCompareValidator(input) {
|
|
if (input.filter('[name=end]').length) {
|
|
var container = input.closest('.k-scheduler-edit-form');
|
|
var startInput = container.find('[name=start]:visible');
|
|
var endInput = container.find('[name=end]:visible');
|
|
if (endInput[0] && startInput[0]) {
|
|
var start, end;
|
|
var startPicker = kendo.widgetInstance(startInput, kendo.ui);
|
|
var endPicker = kendo.widgetInstance(endInput, kendo.ui);
|
|
var editable = container.data('kendoEditable');
|
|
var model = editable ? editable.options.model : null;
|
|
if (startPicker && endPicker) {
|
|
start = startPicker.value();
|
|
end = endPicker.value();
|
|
} else {
|
|
start = kendo.parseDate(startInput.val());
|
|
end = kendo.parseDate(endInput.val());
|
|
}
|
|
if (start && end) {
|
|
if (model) {
|
|
var timezone = startInput.attr(kendo.attr('timezone'));
|
|
var startTimezone = model.startTimezone;
|
|
var endTimezone = model.endTimezone;
|
|
startTimezone = startTimezone || endTimezone;
|
|
endTimezone = endTimezone || startTimezone;
|
|
if (startTimezone) {
|
|
start = applyZone(start, startTimezone, timezone);
|
|
end = applyZone(end, endTimezone, timezone);
|
|
}
|
|
}
|
|
return start <= end;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
var SchedulerEvent = kendo.data.Model.define({
|
|
init: function (value) {
|
|
var that = this;
|
|
kendo.data.Model.fn.init.call(that, value);
|
|
that._defaultId = that.defaults[that.idField];
|
|
},
|
|
_time: function (field) {
|
|
var date = this[field];
|
|
var fieldTime = '_' + field + 'Time';
|
|
if (this[fieldTime]) {
|
|
return this[fieldTime] - kendo.date.toUtcTime(kendo.date.getDate(date));
|
|
}
|
|
return getMilliseconds(date);
|
|
},
|
|
_date: function (field) {
|
|
var fieldTime = '_' + field + 'Time';
|
|
if (this[fieldTime]) {
|
|
return this[fieldTime] - this._time(field);
|
|
}
|
|
return kendo.date.getDate(this[field]);
|
|
},
|
|
clone: function (options, updateUid) {
|
|
var uid = this.uid, event = new this.constructor($.extend({}, this.toJSON(), options));
|
|
if (!updateUid) {
|
|
event.uid = uid;
|
|
}
|
|
return event;
|
|
},
|
|
duration: function () {
|
|
var end = this.end;
|
|
var start = this.start;
|
|
var offset = (end.getTimezoneOffset() - start.getTimezoneOffset()) * kendo.date.MS_PER_MINUTE;
|
|
return end - start - offset;
|
|
},
|
|
expand: function (start, end, zone) {
|
|
return recurrence ? recurrence.expand(this, start, end, zone) : [this];
|
|
},
|
|
update: function (eventInfo) {
|
|
for (var field in eventInfo) {
|
|
this.set(field, eventInfo[field]);
|
|
}
|
|
if (this._startTime) {
|
|
this.set('_startTime', kendo.date.toUtcTime(this.start));
|
|
}
|
|
if (this._endTime) {
|
|
this.set('_endTime', kendo.date.toUtcTime(this.end));
|
|
}
|
|
},
|
|
isMultiDay: function () {
|
|
return this.isAllDay || this.duration() >= kendo.date.MS_PER_DAY;
|
|
},
|
|
isException: function () {
|
|
return !this.isNew() && this.recurrenceId;
|
|
},
|
|
isOccurrence: function () {
|
|
return this.isNew() && this.recurrenceId;
|
|
},
|
|
isRecurring: function () {
|
|
return !!(this.recurrenceRule || this.recurrenceId);
|
|
},
|
|
isRecurrenceHead: function () {
|
|
return !!(this.id && this.recurrenceRule);
|
|
},
|
|
toOccurrence: function (options) {
|
|
options = $.extend(options, {
|
|
recurrenceException: null,
|
|
recurrenceRule: null,
|
|
recurrenceId: this.id || this.recurrenceId
|
|
});
|
|
options[this.idField] = this.defaults[this.idField];
|
|
return this.clone(options, true);
|
|
},
|
|
toJSON: function () {
|
|
var obj = kendo.data.Model.fn.toJSON.call(this);
|
|
obj.uid = this.uid;
|
|
delete obj._startTime;
|
|
delete obj._endTime;
|
|
return obj;
|
|
},
|
|
shouldSerialize: function (field) {
|
|
return kendo.data.Model.fn.shouldSerialize.call(this, field) && field !== '_defaultId';
|
|
},
|
|
set: function (key, value) {
|
|
var isAllDay = this.isAllDay || false;
|
|
kendo.data.Model.fn.set.call(this, key, value);
|
|
if (key == 'isAllDay' && value != isAllDay) {
|
|
var start = kendo.date.getDate(this.start);
|
|
var end = new Date(this.end);
|
|
var milliseconds = kendo.date.getMilliseconds(end);
|
|
if (milliseconds === 0 && value) {
|
|
milliseconds = MS_PER_DAY;
|
|
}
|
|
this.set('start', start);
|
|
if (value === true) {
|
|
kendo.date.setTime(end, -milliseconds);
|
|
if (end < start) {
|
|
end = start;
|
|
}
|
|
} else {
|
|
kendo.date.setTime(end, MS_PER_DAY - milliseconds);
|
|
}
|
|
this.set('end', end);
|
|
}
|
|
},
|
|
id: 'id',
|
|
fields: {
|
|
id: { type: 'number' },
|
|
title: {
|
|
defaultValue: '',
|
|
type: 'string'
|
|
},
|
|
start: {
|
|
type: 'date',
|
|
validation: { required: true }
|
|
},
|
|
startTimezone: { type: 'string' },
|
|
end: {
|
|
type: 'date',
|
|
validation: {
|
|
required: true,
|
|
dateCompare: { value: dateCompareValidator }
|
|
}
|
|
},
|
|
endTimezone: { type: 'string' },
|
|
recurrenceRule: {
|
|
defaultValue: '',
|
|
type: 'string'
|
|
},
|
|
recurrenceException: {
|
|
defaultValue: '',
|
|
type: 'string'
|
|
},
|
|
isAllDay: {
|
|
type: 'boolean',
|
|
defaultValue: false
|
|
},
|
|
description: { type: 'string' }
|
|
}
|
|
});
|
|
var SchedulerDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
DataSource.fn.init.call(this, extend(true, {}, {
|
|
schema: {
|
|
modelBase: SchedulerEvent,
|
|
model: SchedulerEvent
|
|
}
|
|
}, options));
|
|
this.reader = new SchedulerDataReader(this.options.schema, this.reader);
|
|
},
|
|
expand: function (start, end) {
|
|
var data = this.view(), filter = {};
|
|
if (start && end) {
|
|
end = new Date(end.getTime() + MS_PER_DAY - 1);
|
|
filter = {
|
|
logic: 'or',
|
|
filters: [
|
|
{
|
|
logic: 'and',
|
|
filters: [
|
|
{
|
|
field: 'start',
|
|
operator: 'gte',
|
|
value: start
|
|
},
|
|
{
|
|
field: 'end',
|
|
operator: 'gte',
|
|
value: start
|
|
},
|
|
{
|
|
field: 'start',
|
|
operator: 'lte',
|
|
value: end
|
|
}
|
|
]
|
|
},
|
|
{
|
|
logic: 'and',
|
|
filters: [
|
|
{
|
|
field: 'start',
|
|
operator: 'lte',
|
|
value: new Date(start.getTime() + MS_PER_DAY - 1)
|
|
},
|
|
{
|
|
field: 'end',
|
|
operator: 'gte',
|
|
value: start
|
|
}
|
|
]
|
|
}
|
|
]
|
|
};
|
|
data = new kendo.data.Query(expandAll(data, start, end, this.reader.timezone)).filter(filter).toArray();
|
|
}
|
|
return data;
|
|
},
|
|
cancelChanges: function (model) {
|
|
if (model && model.isOccurrence()) {
|
|
this._removeExceptionDate(model);
|
|
}
|
|
DataSource.fn.cancelChanges.call(this, model);
|
|
},
|
|
insert: function (index, model) {
|
|
if (!model) {
|
|
return;
|
|
}
|
|
if (!(model instanceof SchedulerEvent)) {
|
|
var eventInfo = model;
|
|
model = this._createNewModel();
|
|
model.accept(eventInfo);
|
|
}
|
|
if (!this._pushCreated && model.isRecurrenceHead() || model.recurrenceId) {
|
|
model = model.recurrenceId ? model : model.toOccurrence();
|
|
this._addExceptionDate(model);
|
|
}
|
|
return DataSource.fn.insert.call(this, index, model);
|
|
},
|
|
pushCreate: function (items) {
|
|
this._pushCreated = true;
|
|
DataSource.fn.pushCreate.call(this, items);
|
|
this._pushCreated = false;
|
|
},
|
|
remove: function (model) {
|
|
if (model.isRecurrenceHead()) {
|
|
this._removeExceptions(model);
|
|
} else if (model.isRecurring()) {
|
|
this._addExceptionDate(model);
|
|
}
|
|
return DataSource.fn.remove.call(this, model);
|
|
},
|
|
_removeExceptions: function (model) {
|
|
var data = this.data().slice(0), item = data.shift(), id = model.id;
|
|
while (item) {
|
|
if (item.recurrenceId === id) {
|
|
DataSource.fn.remove.call(this, item);
|
|
}
|
|
item = data.shift();
|
|
}
|
|
model.set(RECURRENCE_EXCEPTION, '');
|
|
},
|
|
_removeExceptionDate: function (model) {
|
|
if (model.recurrenceId) {
|
|
var head = this.get(model.recurrenceId);
|
|
if (head) {
|
|
var start = model.start;
|
|
head.set(RECURRENCE_EXCEPTION, head.recurrenceException.replace(recurrence.toExceptionString(start, this.reader.timezone), ''));
|
|
}
|
|
}
|
|
},
|
|
_addExceptionDate: function (model) {
|
|
var start = model.start;
|
|
var zone = this.reader.timezone;
|
|
var head = this.get(model.recurrenceId);
|
|
var recurrenceException = head.recurrenceException || '';
|
|
if (!recurrence.isException(recurrenceException, start, zone)) {
|
|
head.set(RECURRENCE_EXCEPTION, recurrenceException + recurrence.toExceptionString(start, zone));
|
|
}
|
|
}
|
|
});
|
|
function expandAll(events, start, end, zone) {
|
|
var length = events.length, data = [], idx = 0;
|
|
for (; idx < length; idx++) {
|
|
data = data.concat(events[idx].expand(start, end, zone));
|
|
}
|
|
return data;
|
|
}
|
|
SchedulerDataSource.create = function (options) {
|
|
if (isArray(options) || options instanceof kendo.data.ObservableArray) {
|
|
options = { data: options };
|
|
}
|
|
var dataSource = options || {}, data = dataSource.data;
|
|
dataSource.data = data;
|
|
if (!(dataSource instanceof SchedulerDataSource) && dataSource instanceof kendo.data.DataSource) {
|
|
throw new Error('Incorrect DataSource type. Only SchedulerDataSource instances are supported');
|
|
}
|
|
return dataSource instanceof SchedulerDataSource ? dataSource : new SchedulerDataSource(dataSource);
|
|
};
|
|
extend(true, kendo.data, {
|
|
SchedulerDataSource: SchedulerDataSource,
|
|
SchedulerDataReader: SchedulerDataReader,
|
|
SchedulerEvent: SchedulerEvent
|
|
});
|
|
var defaultCommands = {
|
|
update: {
|
|
text: 'Save',
|
|
className: 'k-primary k-scheduler-update'
|
|
},
|
|
canceledit: {
|
|
text: 'Cancel',
|
|
className: 'k-scheduler-cancel'
|
|
},
|
|
destroy: {
|
|
text: 'Delete',
|
|
imageClass: 'k-delete',
|
|
className: 'k-primary k-scheduler-delete',
|
|
iconClass: 'k-icon'
|
|
}
|
|
};
|
|
function trimOptions(options) {
|
|
delete options.name;
|
|
delete options.prefix;
|
|
delete options.remove;
|
|
delete options.edit;
|
|
delete options.add;
|
|
delete options.navigate;
|
|
return options;
|
|
}
|
|
function createValidationAttributes(model, field) {
|
|
var modelField = (model.fields || model)[field];
|
|
var specialRules = [
|
|
'url',
|
|
'email',
|
|
'number',
|
|
'date',
|
|
'boolean'
|
|
];
|
|
var validation = modelField ? modelField.validation : {};
|
|
var datatype = kendo.attr('type');
|
|
var inArray = $.inArray;
|
|
var ruleName;
|
|
var rule;
|
|
var attr = {};
|
|
for (ruleName in validation) {
|
|
rule = validation[ruleName];
|
|
if (inArray(ruleName, specialRules) >= 0) {
|
|
attr[datatype] = ruleName;
|
|
} else if (!kendo.isFunction(rule)) {
|
|
attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule;
|
|
}
|
|
attr[kendo.attr(ruleName + '-msg')] = rule.message;
|
|
}
|
|
return attr;
|
|
}
|
|
function dropDownResourceEditor(resource, model) {
|
|
var attr = createValidationAttributes(model, resource.field);
|
|
return function (container) {
|
|
$(kendo.format('<select data-{0}bind="value:{1}">', kendo.ns, resource.field)).appendTo(container).attr(attr).kendoDropDownList({
|
|
dataTextField: resource.dataTextField,
|
|
dataValueField: resource.dataValueField,
|
|
dataSource: resource.dataSource,
|
|
valuePrimitive: resource.valuePrimitive,
|
|
optionLabel: 'None',
|
|
template: kendo.format('<span class="k-scheduler-mark" style="background-color:#= data.{0}?{0}:"none" #"></span>#={1}#', resource.dataColorField, resource.dataTextField)
|
|
});
|
|
};
|
|
}
|
|
function descriptionEditor(options) {
|
|
var attr = createValidationAttributes(options.model, options.field);
|
|
return function (container) {
|
|
$('<textarea name="description" class="k-textbox"/>').attr(attr).appendTo(container);
|
|
};
|
|
}
|
|
function multiSelectResourceEditor(resource, model) {
|
|
var attr = createValidationAttributes(model, resource.field);
|
|
return function (container) {
|
|
$(kendo.format('<select data-{0}bind="value:{1}">', kendo.ns, resource.field)).appendTo(container).attr(attr).kendoMultiSelect({
|
|
dataTextField: resource.dataTextField,
|
|
dataValueField: resource.dataValueField,
|
|
dataSource: resource.dataSource,
|
|
valuePrimitive: resource.valuePrimitive,
|
|
itemTemplate: kendo.format('<span class="k-scheduler-mark" style="background-color:#= data.{0}?{0}:"none" #"></span>#={1}#', resource.dataColorField, resource.dataTextField),
|
|
tagTemplate: kendo.format('<span class="k-scheduler-mark" style="background-color:#= data.{0}?{0}:"none" #"></span>#={1}#', resource.dataColorField, resource.dataTextField)
|
|
});
|
|
};
|
|
}
|
|
function multiSelectResourceEditorMobile(resource, model) {
|
|
var attr = createValidationAttributes(model, resource.field);
|
|
return function (container) {
|
|
var options = '';
|
|
var view = resource.dataSource.view();
|
|
for (var idx = 0, length = view.length; idx < length; idx++) {
|
|
options += kendo.format('<option value="{0}">{1}</option>', kendo.getter(resource.dataValueField)(view[idx]), kendo.getter(resource.dataTextField)(view[idx]));
|
|
}
|
|
$(kendo.format('<select data-{0}bind="value:{1}" multiple="multiple" data-{0}value-primitive="{3}">{2}</select>', kendo.ns, resource.field, options, resource.valuePrimitive)).appendTo(container).attr(attr);
|
|
};
|
|
}
|
|
function moveEventRange(event, distance) {
|
|
var duration = event.end.getTime() - event.start.getTime();
|
|
var start = new Date(event.start.getTime());
|
|
kendo.date.setTime(start, distance);
|
|
var end = new Date(start.getTime());
|
|
kendo.date.setTime(end, duration, true);
|
|
return {
|
|
start: start,
|
|
end: end
|
|
};
|
|
}
|
|
var editors = {
|
|
mobile: {
|
|
dateRange: MOBILEDATERANGEEDITOR,
|
|
timezonePopUp: MOBILETIMEZONEPOPUP,
|
|
timezone: MOBILETIMEZONEEDITOR,
|
|
recurrence: MOBILERECURRENCEEDITOR,
|
|
description: descriptionEditor,
|
|
multipleResources: multiSelectResourceEditorMobile,
|
|
resources: dropDownResourceEditor
|
|
},
|
|
desktop: {
|
|
dateRange: DATERANGEEDITOR,
|
|
timezonePopUp: TIMEZONEPOPUP,
|
|
timezone: TIMEZONEEDITOR,
|
|
recurrence: RECURRENCEEDITOR,
|
|
description: descriptionEditor,
|
|
multipleResources: multiSelectResourceEditor,
|
|
resources: dropDownResourceEditor
|
|
}
|
|
};
|
|
var Editor = kendo.Observable.extend({
|
|
init: function (element, options) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.element = element;
|
|
this.options = extend(true, {}, this.options, options);
|
|
this.createButton = this.options.createButton;
|
|
this.toggleDateValidationHandler = proxy(this._toggleDateValidation, this);
|
|
},
|
|
_toggleDateValidation: function (e) {
|
|
if (e.field == 'isAllDay') {
|
|
var container = this.container, isAllDay = this.editable.options.model.isAllDay, bindAttribute = kendo.attr('bind'), element, isDateTimeInput, shouldValidate;
|
|
container.find('[' + bindAttribute + '*=end],[' + bindAttribute + '*=start]').each(function () {
|
|
element = $(this);
|
|
if (valueStartEndBoundRegex.test(element.attr(bindAttribute))) {
|
|
isDateTimeInput = element.is('[' + kendo.attr('role') + '=datetimepicker],[type*=datetime]');
|
|
shouldValidate = isAllDay !== isDateTimeInput;
|
|
element.attr(kendo.attr('validate'), shouldValidate);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
fields: function (editors, model) {
|
|
var that = this;
|
|
var messages = that.options.messages;
|
|
var timezone = that.options.timezone;
|
|
var click = function (e) {
|
|
e.preventDefault();
|
|
that._initTimezoneEditor(model, this);
|
|
};
|
|
var fields = [
|
|
{
|
|
field: 'title',
|
|
title: messages.editor.title
|
|
},
|
|
{
|
|
field: 'start',
|
|
title: messages.editor.start,
|
|
editor: editors.dateRange,
|
|
timezone: timezone
|
|
},
|
|
{
|
|
field: 'end',
|
|
title: messages.editor.end,
|
|
editor: editors.dateRange,
|
|
timezone: timezone
|
|
},
|
|
{
|
|
field: 'isAllDay',
|
|
title: messages.editor.allDayEvent
|
|
}
|
|
];
|
|
if (kendo.timezone.windows_zones) {
|
|
fields.push({
|
|
field: 'timezone',
|
|
title: messages.editor.timezone,
|
|
editor: editors.timezonePopUp,
|
|
click: click,
|
|
messages: messages.editor,
|
|
model: model
|
|
});
|
|
fields.push({
|
|
field: 'startTimezone',
|
|
title: messages.editor.startTimezone,
|
|
editor: editors.timezone,
|
|
noTimezone: messages.editor.noTimezone
|
|
});
|
|
fields.push({
|
|
field: 'endTimezone',
|
|
title: messages.editor.endTimezone,
|
|
editor: editors.timezone,
|
|
noTimezone: messages.editor.noTimezone
|
|
});
|
|
}
|
|
if (!model.recurrenceId) {
|
|
fields.push({
|
|
field: 'recurrenceRule',
|
|
title: messages.editor.repeat,
|
|
editor: editors.recurrence,
|
|
timezone: timezone,
|
|
messages: messages.recurrenceEditor,
|
|
pane: this.pane
|
|
});
|
|
}
|
|
if ('description' in model) {
|
|
fields.push({
|
|
field: 'description',
|
|
title: messages.editor.description,
|
|
editor: editors.description({
|
|
model: model,
|
|
field: 'description'
|
|
})
|
|
});
|
|
}
|
|
for (var resourceIndex = 0; resourceIndex < this.options.resources.length; resourceIndex++) {
|
|
var resource = this.options.resources[resourceIndex];
|
|
fields.push({
|
|
field: resource.field,
|
|
title: resource.title,
|
|
editor: resource.multiple ? editors.multipleResources(resource, model) : editors.resources(resource, model)
|
|
});
|
|
}
|
|
return fields;
|
|
},
|
|
end: function () {
|
|
return this.editable.end();
|
|
},
|
|
_buildEditTemplate: function (model, fields, editableFields) {
|
|
var messages = this.options.messages;
|
|
var settings = extend({}, kendo.Template, this.options.templateSettings);
|
|
var paramName = settings.paramName;
|
|
var template = this.options.editable.template;
|
|
var html = '';
|
|
if (template) {
|
|
if (typeof template === STRING) {
|
|
template = window.unescape(template);
|
|
}
|
|
html += kendo.template(template, settings)(model);
|
|
} else {
|
|
for (var idx = 0, length = fields.length; idx < length; idx++) {
|
|
var field = fields[idx];
|
|
if (field.field === 'startTimezone') {
|
|
html += '<div class="k-popup-edit-form k-scheduler-edit-form k-scheduler-timezones" style="display:none">';
|
|
html += '<div class="k-edit-form-container">';
|
|
html += '<div class="k-edit-label"></div>';
|
|
html += '<div class="k-edit-field"><label class="k-check"><input class="k-timezone-toggle" type="checkbox" />' + messages.editor.separateTimezones + '</label></div>';
|
|
}
|
|
html += '<div class="k-edit-label"><label for="' + field.field + '">' + (field.title || field.field || '') + '</label></div>';
|
|
if (!model.editable || model.editable(field.field)) {
|
|
editableFields.push(field);
|
|
html += '<div ' + kendo.attr('container-for') + '="' + field.field + '" class="k-edit-field"></div>';
|
|
} else {
|
|
var tmpl = '#:';
|
|
if (field.field) {
|
|
field = kendo.expr(field.field, paramName);
|
|
tmpl += field + '==null?\'\':' + field;
|
|
} else {
|
|
tmpl += '\'\'';
|
|
}
|
|
tmpl += '#';
|
|
tmpl = kendo.template(tmpl, settings);
|
|
html += '<div class="k-edit-field">' + tmpl(model) + '</div>';
|
|
}
|
|
if (field.field === 'endTimezone') {
|
|
html += this._createEndTimezoneButton();
|
|
}
|
|
}
|
|
}
|
|
return html;
|
|
},
|
|
_createEndTimezoneButton: function () {
|
|
return '</div></div>';
|
|
},
|
|
_revertTimezones: function (model) {
|
|
model.set('startTimezone', this._startTimezone);
|
|
model.set('endTimezone', this._endTimezone);
|
|
delete this._startTimezone;
|
|
delete this._endTimezone;
|
|
}
|
|
});
|
|
var MobileEditor = Editor.extend({
|
|
init: function () {
|
|
Editor.fn.init.apply(this, arguments);
|
|
this.pane = kendo.mobile.ui.Pane.wrap(this.element);
|
|
this.pane.element.parent().css('height', this.options.height);
|
|
this.view = this.pane.view();
|
|
this._actionSheetButtonTemplate = kendo.template('<li><a #=attr# class="k-button #=className#" href="\\#">#:text#</a></li>');
|
|
this._actionSheetPopupOptions = $(document.documentElement).hasClass('km-root') ? { modal: false } : {
|
|
align: 'bottom center',
|
|
position: 'bottom center',
|
|
effect: 'slideIn:up'
|
|
};
|
|
},
|
|
options: {
|
|
animations: {
|
|
left: 'slide',
|
|
right: 'slide:right'
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this.close();
|
|
this.unbind();
|
|
this.pane.destroy();
|
|
},
|
|
_initTimezoneEditor: function (model) {
|
|
var that = this;
|
|
var pane = that.pane;
|
|
var messages = that.options.messages;
|
|
var timezoneView = that.timezoneView;
|
|
var container = that.container.find('.k-scheduler-timezones');
|
|
var checkbox = container.find('.k-timezone-toggle');
|
|
var endTimezoneRow = container.find('.k-edit-label:last').add(container.find('.k-edit-field:last'));
|
|
var startTimezoneChange = function (e) {
|
|
if (e.field === 'startTimezone') {
|
|
var value = model.startTimezone;
|
|
checkbox.prop('disabled', !value);
|
|
if (!value) {
|
|
endTimezoneRow.hide();
|
|
model.set('endTimezone', '');
|
|
checkbox.prop('checked', false);
|
|
}
|
|
}
|
|
};
|
|
that._startTimezone = model.startTimezone || '';
|
|
that._endTimezone = model.endTimezone || '';
|
|
if (!timezoneView) {
|
|
var html = '<div data-role="view" class="k-popup-edit-form k-scheduler-edit-form k-mobile-list">' + '<div data-role="header" class="k-header"><a href="#" class="k-button k-scheduler-cancel">' + messages.cancel + '</a>' + messages.editor.timezoneTitle + '<a href="#" class="k-button k-scheduler-update">' + messages.save + '</a></div></div>';
|
|
this.timezoneView = timezoneView = pane.append(html);
|
|
timezoneView.contentElement().append(container.show());
|
|
timezoneView.element.on(CLICK + NS, '.k-scheduler-cancel, .k-scheduler-update', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if ($(this).hasClass('k-scheduler-cancel')) {
|
|
that._revertTimezones(model);
|
|
}
|
|
model.unbind('change', startTimezoneChange);
|
|
var editView = pane.element.find('#edit').data('kendoMobileView');
|
|
var text = timezoneButtonText(model, messages.editor.noTimezone);
|
|
editView.contentElement().find('.k-timezone-button').text(text);
|
|
pane.navigate(editView, that.options.animations.right);
|
|
});
|
|
checkbox.click(function () {
|
|
endTimezoneRow.toggle(checkbox.prop('checked'));
|
|
model.set('endTimezone', '');
|
|
});
|
|
model.bind('change', startTimezoneChange);
|
|
}
|
|
checkbox.prop('checked', model.endTimezone).prop('disabled', !model.startTimezone);
|
|
if (model.endTimezone) {
|
|
endTimezoneRow.show();
|
|
} else {
|
|
endTimezoneRow.hide();
|
|
}
|
|
pane.navigate(timezoneView, that.options.animations.left);
|
|
},
|
|
_createActionSheetButton: function (options) {
|
|
options.template = this._actionSheetButtonTemplate;
|
|
return this.createButton(options);
|
|
},
|
|
showDialog: function (options) {
|
|
var type = '';
|
|
var html = '<ul><li class="km-actionsheet-title">' + options.title + '</li>';
|
|
var target = this.element.find('.k-event[' + kendo.attr('uid') + '=\'' + options.model.uid + '\']');
|
|
if (this.container) {
|
|
target = this.container.find('.k-scheduler-delete');
|
|
if (target[0]) {
|
|
type = 'phone';
|
|
}
|
|
}
|
|
for (var buttonIndex = 0; buttonIndex < options.buttons.length; buttonIndex++) {
|
|
html += this._createActionSheetButton(options.buttons[buttonIndex]);
|
|
}
|
|
html += '</ul>';
|
|
var actionSheet = $(html).appendTo(this.pane.view().element).kendoMobileActionSheet({
|
|
type: type,
|
|
cancel: this.options.messages.cancel,
|
|
cancelTemplate: '<li class="km-actionsheet-cancel"><a class="k-button" href="\\#">#:cancel#</a></li>',
|
|
close: function () {
|
|
this.destroy();
|
|
},
|
|
command: function (e) {
|
|
var buttonIndex = actionSheet.element.find('li:not(.km-actionsheet-cancel) > .k-button').index($(e.currentTarget));
|
|
if (buttonIndex > -1) {
|
|
actionSheet.close();
|
|
options.buttons[buttonIndex].click();
|
|
}
|
|
},
|
|
popup: this._actionSheetPopupOptions
|
|
}).data('kendoMobileActionSheet');
|
|
actionSheet.open(target);
|
|
},
|
|
editEvent: function (model) {
|
|
var pane = this.pane;
|
|
var html = '';
|
|
var messages = this.options.messages;
|
|
var updateText = messages.save;
|
|
var removeText = messages.destroy;
|
|
var cancelText = messages.cancel;
|
|
var titleText = messages.editor.editorTitle;
|
|
html += '<div data-role="view" class="k-popup-edit-form k-scheduler-edit-form k-mobile-list" id="edit" ' + kendo.attr('uid') + '="' + model.uid + '">' + '<div data-role="header" class="k-header"><a href="#" class="k-button k-scheduler-cancel">' + cancelText + '</a>' + titleText + '<a href="#" class="k-button k-scheduler-update">' + updateText + '</a></div>';
|
|
var fields = this.fields(editors.mobile, model);
|
|
var that = this;
|
|
var editableFields = [];
|
|
html += this._buildEditTemplate(model, fields, editableFields);
|
|
if (!model.isNew() && this.options.editable && this.options.editable.destroy !== false) {
|
|
html += '<div class="k-edit-buttons"><a href="#" class="k-scheduler-delete k-button">' + removeText + '</a></div>';
|
|
}
|
|
html += '</div>';
|
|
var view = pane.append(html);
|
|
var container = this.container = view.element;
|
|
this.editable = container.kendoEditable({
|
|
fields: editableFields,
|
|
model: model,
|
|
clearContainer: false,
|
|
target: that.options.target,
|
|
validateOnBlur: true
|
|
}).data('kendoEditable');
|
|
container.find('input[type=checkbox],input[type=radio]').parent('.k-edit-field').addClass('k-check').prev('.k-edit-label').addClass('k-check').click(function () {
|
|
$(this).next().children('input').click();
|
|
});
|
|
if (!this.trigger('edit', {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
container.on(CLICK + NS, 'a.k-scheduler-edit, a.k-scheduler-cancel, a.k-scheduler-update, a.k-scheduler-delete', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var button = $(this);
|
|
if (!button.hasClass('k-scheduler-edit')) {
|
|
var name = 'cancel';
|
|
if (button.hasClass('k-scheduler-update')) {
|
|
name = 'save';
|
|
} else if (button.hasClass('k-scheduler-delete')) {
|
|
name = 'remove';
|
|
}
|
|
that.trigger(name, {
|
|
container: container,
|
|
model: model
|
|
});
|
|
} else {
|
|
pane.navigate('#edit', that.options.animations.right);
|
|
}
|
|
});
|
|
pane.navigate(view, that.options.animations.left);
|
|
model.bind('change', that.toggleDateValidationHandler);
|
|
} else {
|
|
this.trigger('cancel', {
|
|
container: container,
|
|
model: model
|
|
});
|
|
}
|
|
return this.editable;
|
|
},
|
|
_views: function () {
|
|
return this.pane.element.find(kendo.roleSelector('view')).not(this.view.element);
|
|
},
|
|
close: function () {
|
|
if (this.container) {
|
|
this.pane.navigate('', this.options.animations.right);
|
|
var views = this._views();
|
|
var view;
|
|
for (var idx = 0, length = views.length; idx < length; idx++) {
|
|
view = views.eq(idx).data('kendoMobileView');
|
|
if (view) {
|
|
view.purge();
|
|
}
|
|
}
|
|
views.remove();
|
|
this.container = null;
|
|
if (this.editable) {
|
|
this.editable.options.model.unbind('change', this.toggleDateValidationHandler);
|
|
this.editable.destroy();
|
|
this.editable = null;
|
|
}
|
|
this.timezoneView = null;
|
|
}
|
|
}
|
|
});
|
|
var PopupEditor = Editor.extend({
|
|
destroy: function () {
|
|
this.close();
|
|
this.unbind();
|
|
},
|
|
editEvent: function (model) {
|
|
var that = this;
|
|
var editable = that.options.editable;
|
|
var html = '<div ' + kendo.attr('uid') + '="' + model.uid + '" class="k-popup-edit-form k-scheduler-edit-form"><div class="k-edit-form-container">';
|
|
var messages = that.options.messages;
|
|
var updateText = messages.save;
|
|
var cancelText = messages.cancel;
|
|
var deleteText = messages.destroy;
|
|
var fields = this.fields(editors.desktop, model);
|
|
var editableFields = [];
|
|
html += this._buildEditTemplate(model, fields, editableFields);
|
|
var attr;
|
|
var options = isPlainObject(editable) ? editable.window : {};
|
|
html += '<div class="k-edit-buttons k-state-default">';
|
|
html += this.createButton({
|
|
name: 'update',
|
|
text: updateText,
|
|
attr: attr
|
|
}) + this.createButton({
|
|
name: 'canceledit',
|
|
text: cancelText,
|
|
attr: attr
|
|
});
|
|
if (!model.isNew() && editable.destroy !== false) {
|
|
html += this.createButton({
|
|
name: 'delete',
|
|
text: deleteText,
|
|
attr: attr
|
|
});
|
|
}
|
|
html += '</div></div></div>';
|
|
var container = this.container = $(html).appendTo(that.element).eq(0).kendoWindow(extend({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: messages.editor.editorTitle,
|
|
visible: false,
|
|
close: function (e) {
|
|
if (e.userTriggered) {
|
|
if (that.trigger(CANCEL, {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
}, options));
|
|
that.editable = container.kendoEditable({
|
|
fields: editableFields,
|
|
model: model,
|
|
clearContainer: false,
|
|
validateOnBlur: true,
|
|
target: that.options.target
|
|
}).data('kendoEditable');
|
|
if (!that.trigger(EDIT, {
|
|
container: container,
|
|
model: model
|
|
})) {
|
|
container.data('kendoWindow').center().open();
|
|
container.on(CLICK + NS, 'a.k-scheduler-cancel', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.trigger(CANCEL, {
|
|
container: container,
|
|
model: model
|
|
});
|
|
});
|
|
container.on(CLICK + NS, 'a.k-scheduler-update', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.trigger('save', {
|
|
container: container,
|
|
model: model
|
|
});
|
|
});
|
|
container.on(CLICK + NS, 'a.k-scheduler-delete', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.trigger(REMOVE, {
|
|
container: container,
|
|
model: model
|
|
});
|
|
});
|
|
kendo.cycleForm(container);
|
|
model.bind('change', that.toggleDateValidationHandler);
|
|
} else {
|
|
that.trigger(CANCEL, {
|
|
container: container,
|
|
model: model
|
|
});
|
|
}
|
|
return that.editable;
|
|
},
|
|
close: function () {
|
|
var that = this;
|
|
var destroy = function () {
|
|
if (that.editable) {
|
|
that.editable.options.model.unbind('change', that.toggleDateValidationHandler);
|
|
that.editable.destroy();
|
|
that.editable = null;
|
|
that.container = null;
|
|
}
|
|
if (that.popup) {
|
|
that.popup.destroy();
|
|
that.popup = null;
|
|
}
|
|
};
|
|
if (that.editable) {
|
|
if (that._timezonePopup && that._timezonePopup.data('kendoWindow')) {
|
|
that._timezonePopup.data('kendoWindow').destroy();
|
|
that._timezonePopup = null;
|
|
}
|
|
if (that.container.is(':visible')) {
|
|
that.container.data('kendoWindow').bind('deactivate', destroy).close();
|
|
} else {
|
|
destroy();
|
|
}
|
|
} else {
|
|
destroy();
|
|
}
|
|
},
|
|
_createEndTimezoneButton: function () {
|
|
var messages = this.options.messages;
|
|
var html = '';
|
|
html += '<div class="k-edit-buttons k-state-default">';
|
|
html += this.createButton({
|
|
name: 'savetimezone',
|
|
text: messages.save
|
|
}) + this.createButton({
|
|
name: 'canceltimezone',
|
|
text: messages.cancel
|
|
});
|
|
html += '</div></div></div>';
|
|
return html;
|
|
},
|
|
showDialog: function (options) {
|
|
var html = kendo.format('<div class=\'k-popup-edit-form\'><div class=\'k-edit-form-container\'><p class=\'k-popup-message\'>{0}</p>', options.text);
|
|
html += '<div class="k-edit-buttons k-state-default">';
|
|
for (var buttonIndex = 0; buttonIndex < options.buttons.length; buttonIndex++) {
|
|
html += this.createButton(options.buttons[buttonIndex]);
|
|
}
|
|
html += '</div></div></div>';
|
|
var wrapper = this.element;
|
|
if (this.popup) {
|
|
this.popup.destroy();
|
|
}
|
|
var popup = this.popup = $(html).appendTo(wrapper).eq(0).on('click', '.k-button', function (e) {
|
|
e.preventDefault();
|
|
popup.close();
|
|
var buttonIndex = $(e.currentTarget).index();
|
|
options.buttons[buttonIndex].click();
|
|
}).kendoWindow({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: false,
|
|
title: options.title,
|
|
visible: false,
|
|
close: function () {
|
|
this.destroy();
|
|
wrapper.focus();
|
|
}
|
|
}).getKendoWindow();
|
|
popup.center().open();
|
|
},
|
|
_initTimezoneEditor: function (model, activator) {
|
|
var that = this;
|
|
var container = that.container.find('.k-scheduler-timezones');
|
|
var checkbox = container.find('.k-timezone-toggle');
|
|
var endTimezoneRow = container.find('.k-edit-label:last').add(container.find('.k-edit-field:last'));
|
|
var saveButton = container.find('.k-scheduler-savetimezone');
|
|
var cancelButton = container.find('.k-scheduler-canceltimezone');
|
|
var timezonePopup = that._timezonePopup;
|
|
var startTimezoneChange = function (e) {
|
|
if (e.field === 'startTimezone') {
|
|
var value = model.startTimezone;
|
|
checkbox.prop('disabled', !value);
|
|
if (!value) {
|
|
endTimezoneRow.hide();
|
|
model.set('endTimezone', '');
|
|
checkbox.prop('checked', false);
|
|
}
|
|
}
|
|
};
|
|
var wnd;
|
|
that._startTimezone = model.startTimezone;
|
|
that._endTimezone = model.endTimezone;
|
|
if (!timezonePopup) {
|
|
that._timezonePopup = timezonePopup = container.kendoWindow({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: that.options.messages.editor.timezoneEditorTitle,
|
|
visible: false,
|
|
close: function (e) {
|
|
model.unbind('change', startTimezoneChange);
|
|
if (e.userTriggered) {
|
|
that._revertTimezones(model);
|
|
}
|
|
if (activator) {
|
|
activator.focus();
|
|
}
|
|
}
|
|
});
|
|
checkbox.click(function () {
|
|
endTimezoneRow.toggle(checkbox.prop('checked'));
|
|
model.set('endTimezone', '');
|
|
});
|
|
saveButton.click(function (e) {
|
|
e.preventDefault();
|
|
wnd.close();
|
|
});
|
|
cancelButton.click(function (e) {
|
|
e.preventDefault();
|
|
that._revertTimezones(model);
|
|
wnd.close();
|
|
});
|
|
model.bind('change', startTimezoneChange);
|
|
}
|
|
checkbox.prop('checked', model.endTimezone).prop('disabled', !model.startTimezone);
|
|
if (model.endTimezone) {
|
|
endTimezoneRow.show();
|
|
} else {
|
|
endTimezoneRow.hide();
|
|
}
|
|
wnd = timezonePopup.data('kendoWindow');
|
|
wnd.center().open();
|
|
}
|
|
});
|
|
var Scheduler = DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
if (!that.options.views || !that.options.views.length) {
|
|
that.options.views = [
|
|
'day',
|
|
'week'
|
|
];
|
|
}
|
|
that.resources = [];
|
|
that._initModel();
|
|
that._wrapper();
|
|
that._views();
|
|
that._toolbar();
|
|
that._dataSource();
|
|
that._resources();
|
|
that._resizeHandler = function () {
|
|
that.resize();
|
|
};
|
|
that.wrapper.on('mousedown' + NS + ' selectstart' + NS, function (e) {
|
|
if (!$(e.target).is(':kendoFocusable')) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
if (that.options.editable && that.options.editable.resize !== false) {
|
|
that._resizable();
|
|
}
|
|
that._movable();
|
|
that._bindResize();
|
|
if (that.options.messages && that.options.messages.recurrence) {
|
|
recurrence.options = that.options.messages.recurrence;
|
|
}
|
|
that._selectable();
|
|
that._ariaId = kendo.guid();
|
|
that._createEditor();
|
|
},
|
|
_bindResize: function () {
|
|
$(window).on('resize' + NS, this._resizeHandler);
|
|
},
|
|
_unbindResize: function () {
|
|
$(window).off('resize' + NS, this._resizeHandler);
|
|
},
|
|
dataItems: function () {
|
|
var that = this;
|
|
var items = that.items();
|
|
var events = that._data;
|
|
var eventsUids = $.map(items, function (item) {
|
|
return $(item).attr('data-uid');
|
|
});
|
|
var i;
|
|
var key;
|
|
var dict = {};
|
|
var eventsUidsLength = eventsUids.length;
|
|
for (i = 0; i < eventsUidsLength; i++) {
|
|
dict[eventsUids[i]] = null;
|
|
}
|
|
var eventsCount = events.length;
|
|
for (i = 0; i < eventsCount; i++) {
|
|
var event = events[i];
|
|
if (dict[event.uid] !== undefined) {
|
|
dict[event.uid] = event;
|
|
}
|
|
}
|
|
var sortedData = [];
|
|
for (key in dict) {
|
|
sortedData.push(dict[key]);
|
|
}
|
|
return sortedData;
|
|
},
|
|
_isMobile: function () {
|
|
var options = this.options;
|
|
return options.mobile === true && kendo.support.mobileOS || options.mobile === 'phone' || options.mobile === 'tablet';
|
|
},
|
|
_isMobilePhoneView: function () {
|
|
var options = this.options;
|
|
return options.mobile === true && kendo.support.mobileOS && !kendo.support.mobileOS.tablet || options.mobile === 'phone';
|
|
},
|
|
_groupsByResource: function (resources, groupIndex, groupsArray, parentFieldValue, parentField) {
|
|
if (!groupsArray) {
|
|
groupsArray = [];
|
|
}
|
|
var resource = resources[0];
|
|
if (resource) {
|
|
var group;
|
|
var data = resource.dataSource.view();
|
|
var prevIndex = 0;
|
|
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
|
|
var fieldValue = kendo.getter(resource.dataValueField)(data[dataIndex]);
|
|
var currentGroupIndex = groupIndex + prevIndex + dataIndex;
|
|
group = this._groupsByResource(resources.slice(1), currentGroupIndex, groupsArray, fieldValue, resource.field);
|
|
group[resource.field] = fieldValue;
|
|
prevIndex = group.groupIndex;
|
|
if (parentField && parentFieldValue) {
|
|
group[parentField] = parentFieldValue;
|
|
}
|
|
if (resources.length === 1) {
|
|
group.groupIndex = groupIndex + dataIndex;
|
|
groupsArray.push(group);
|
|
}
|
|
}
|
|
return group;
|
|
} else {
|
|
return {};
|
|
}
|
|
},
|
|
data: function () {
|
|
return this._data;
|
|
},
|
|
select: function (options) {
|
|
var that = this;
|
|
var view = that.view();
|
|
var selection = that._selection;
|
|
var groups = view.groups;
|
|
var selectedGroups;
|
|
if (options === undefined) {
|
|
var selectedEvents;
|
|
var slots = view._selectedSlots;
|
|
if (!selection) {
|
|
return [];
|
|
}
|
|
if (selection && selection.events) {
|
|
selectedEvents = that._selectedEvents();
|
|
}
|
|
return {
|
|
start: selection.start,
|
|
end: selection.end,
|
|
events: selectedEvents,
|
|
slots: slots,
|
|
resources: view._resourceBySlot(selection)
|
|
};
|
|
}
|
|
if (!options) {
|
|
that._selection = null;
|
|
that._old = null;
|
|
view.clearSelection();
|
|
return;
|
|
}
|
|
if ($.isArray(options)) {
|
|
options = { events: options.splice(0) };
|
|
}
|
|
if (options.resources) {
|
|
var fieldName;
|
|
var filters = [];
|
|
var groupsByResource = [];
|
|
if (view.groupedResources) {
|
|
that._groupsByResource(view.groupedResources, 0, groupsByResource);
|
|
}
|
|
for (fieldName in options.resources) {
|
|
filters.push({
|
|
field: fieldName,
|
|
operator: 'eq',
|
|
value: options.resources[fieldName]
|
|
});
|
|
}
|
|
selectedGroups = new kendo.data.Query(groupsByResource).filter(filters).toArray();
|
|
}
|
|
if (options.events && options.events.length) {
|
|
that._selectEvents(options.events, selectedGroups);
|
|
that._select();
|
|
return;
|
|
}
|
|
if (groups && (options.start && options.end)) {
|
|
var rangeStart = getDate(view._startDate);
|
|
var rangeEnd = kendo.date.addDays(getDate(view._endDate), 1);
|
|
var group;
|
|
var ranges;
|
|
if (options.start < rangeEnd && rangeStart <= options.end) {
|
|
if (selectedGroups && selectedGroups.length) {
|
|
group = groups[selectedGroups[0].groupIndex];
|
|
} else {
|
|
group = groups[0];
|
|
}
|
|
ranges = group.ranges(options.start, options.end, options.isAllDay, false);
|
|
if (ranges.length) {
|
|
that._selection = {
|
|
start: kendo.timezone.toLocalDate(ranges[0].start.start),
|
|
end: kendo.timezone.toLocalDate(ranges[ranges.length - 1].end.end),
|
|
groupIndex: ranges[0].start.groupIndex,
|
|
index: ranges[0].start.index,
|
|
isAllDay: ranges[0].start.isDaySlot,
|
|
events: []
|
|
};
|
|
that._select();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_selectEvents: function (eventsUids, selectedGroups) {
|
|
var that = this;
|
|
var idx;
|
|
var view = that.view();
|
|
var groups = view.groups;
|
|
var eventsLength = eventsUids.length;
|
|
var isGrouped = selectedGroups && selectedGroups.length;
|
|
for (idx = 0; idx < eventsLength; idx++) {
|
|
if (groups && isGrouped) {
|
|
var currentGroup = groups[selectedGroups[0].groupIndex];
|
|
var events = [];
|
|
var timeSlotCollectionCount = currentGroup.timeSlotCollectionCount();
|
|
var daySlotCollectionCount = currentGroup.daySlotCollectionCount();
|
|
for (var collIdx = 0; collIdx < timeSlotCollectionCount; collIdx++) {
|
|
events = events.concat(currentGroup.getTimeSlotCollection(collIdx).events());
|
|
}
|
|
for (var dayCollIdx = 0; dayCollIdx < daySlotCollectionCount; dayCollIdx++) {
|
|
events = events.concat(currentGroup.getDaySlotCollection(dayCollIdx).events());
|
|
}
|
|
events = new kendo.data.Query(events).filter({
|
|
field: 'element[0].getAttribute(\'data-uid\')',
|
|
operator: 'eq',
|
|
value: eventsUids[idx]
|
|
}).toArray();
|
|
if (events[0]) {
|
|
that._createSelection(events[0].element);
|
|
}
|
|
} else {
|
|
var element = view.element.find(kendo.format('.k-event[data-uid={0}], .k-task[data-uid={0}]', eventsUids[idx]));
|
|
if (element.length) {
|
|
that._createSelection(element[0]);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_selectable: function () {
|
|
var that = this, wrapper = that.wrapper, selectEvent = kendo.support.mobileOS ? 'touchend' : 'mousedown';
|
|
if (!that.options.selectable) {
|
|
return;
|
|
}
|
|
that._tabindex();
|
|
wrapper.on(selectEvent + NS, '.k-scheduler-header-all-day td, .k-scheduler-content td, .k-event', function (e) {
|
|
var which = e.which;
|
|
var button = e.button;
|
|
var browser = kendo.support.browser;
|
|
var isRight = which && which === 3 || button && button == 2;
|
|
if (kendo.support.mobileOS && e.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
if (!isRight) {
|
|
that._createSelection(e.currentTarget);
|
|
}
|
|
wrapper.focus();
|
|
if (browser.msie && browser.version < 9) {
|
|
setTimeout(function () {
|
|
wrapper.focus();
|
|
});
|
|
}
|
|
});
|
|
var mouseMoveHandler = $.proxy(that._mouseMove, that);
|
|
wrapper.on('mousedown' + NS, '.k-scheduler-header-all-day td, .k-scheduler-content td', function (e) {
|
|
var which = e.which;
|
|
var button = e.button;
|
|
var isRight = which && which === 3 || button && button == 2;
|
|
if (!isRight) {
|
|
wrapper.on('mousemove' + NS, '.k-scheduler-header-all-day td, .k-scheduler-content td', mouseMoveHandler);
|
|
}
|
|
});
|
|
wrapper.on('mouseup' + NS + ' mouseleave' + NS, function () {
|
|
wrapper.off('mousemove' + NS, '.k-scheduler-header-all-day td, .k-scheduler-content td', mouseMoveHandler);
|
|
});
|
|
wrapper.on('focus' + NS, function () {
|
|
if (!that._selection) {
|
|
that._createSelection(that.wrapper.find('.k-scheduler-content').find('td:first'));
|
|
}
|
|
that._select();
|
|
});
|
|
wrapper.on('focusout' + NS, function () {
|
|
that.view().clearSelection();
|
|
that._ctrlKey = that._shiftKey = false;
|
|
});
|
|
wrapper.on('keydown' + NS, proxy(that._keydown, that));
|
|
wrapper.on('keyup' + NS, function (e) {
|
|
that._ctrlKey = e.ctrlKey;
|
|
that._shiftKey = e.shiftKey;
|
|
});
|
|
},
|
|
_select: function () {
|
|
var that = this;
|
|
var view = that.view();
|
|
var wrapper = that.wrapper;
|
|
var current = view.current();
|
|
var selection = that._selection;
|
|
if (current) {
|
|
current.removeAttribute('id');
|
|
current.removeAttribute('aria-label');
|
|
wrapper.removeAttr('aria-activedescendant');
|
|
}
|
|
view.select(selection);
|
|
current = view.current();
|
|
if (current && that._old !== current) {
|
|
var currentUid = $(current).data('uid');
|
|
if (that._old && currentUid && currentUid === $(that._old).data('uid')) {
|
|
return;
|
|
}
|
|
var labelFormat;
|
|
var data = selection;
|
|
var events = that._selectedEvents();
|
|
var slots = view._selectedSlots;
|
|
if (events[0]) {
|
|
data = events[0] || selection;
|
|
labelFormat = kendo.format(that.options.messages.ariaEventLabel, data.title, data.start, data.start);
|
|
} else {
|
|
labelFormat = kendo.format(that.options.messages.ariaSlotLabel, data.start, data.end);
|
|
}
|
|
current.setAttribute('id', that._ariaId);
|
|
current.setAttribute('aria-label', labelFormat);
|
|
wrapper.attr('aria-activedescendant', that._ariaId);
|
|
that._old = current;
|
|
that.trigger('change', {
|
|
start: selection.start,
|
|
end: selection.end,
|
|
events: events,
|
|
slots: slots,
|
|
resources: view._resourceBySlot(selection)
|
|
});
|
|
}
|
|
},
|
|
_selectedEvents: function () {
|
|
var uids = this._selection.events;
|
|
var length = uids.length;
|
|
var idx = 0;
|
|
var event;
|
|
var events = [];
|
|
for (; idx < length; idx++) {
|
|
event = this.occurrenceByUid(uids[idx]);
|
|
if (event) {
|
|
events.push(event);
|
|
}
|
|
}
|
|
return events;
|
|
},
|
|
_mouseMove: function (e) {
|
|
var that = this;
|
|
clearTimeout(that._moveTimer);
|
|
that._moveTimer = setTimeout(function () {
|
|
var view = that.view();
|
|
var selection = that._selection;
|
|
if (selection) {
|
|
var slot = view.selectionByElement($(e.currentTarget));
|
|
if (slot && selection.groupIndex === slot.groupIndex) {
|
|
var startDate = slot.startDate();
|
|
var endDate = slot.endDate();
|
|
if (startDate >= selection.end) {
|
|
selection.backward = false;
|
|
} else if (endDate <= selection.start) {
|
|
selection.backward = true;
|
|
}
|
|
if (selection.backward) {
|
|
selection.start = startDate;
|
|
} else {
|
|
selection.end = endDate;
|
|
}
|
|
that._select();
|
|
}
|
|
}
|
|
}, 5);
|
|
},
|
|
_viewByIndex: function (index) {
|
|
var view, views = this.views;
|
|
for (view in views) {
|
|
if (!index) {
|
|
return view;
|
|
}
|
|
index--;
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
var that = this, key = e.keyCode, view = that.view(), editable = view.options.editable, selection = that._selection, shiftKey = e.shiftKey;
|
|
that._ctrlKey = e.ctrlKey;
|
|
that._shiftKey = e.shiftKey;
|
|
if (key === keys.TAB) {
|
|
if (view.moveToEvent(selection, shiftKey)) {
|
|
that._select();
|
|
e.preventDefault();
|
|
}
|
|
} else if (editable && key === keys.ENTER) {
|
|
if (selection.events.length) {
|
|
if (editable.update !== false) {
|
|
that.editEvent(selection.events[0]);
|
|
}
|
|
} else if (editable.create !== false) {
|
|
if (selection.isAllDay) {
|
|
selection = $.extend({}, selection, { end: kendo.date.addDays(selection.end, -1) });
|
|
}
|
|
that.addEvent(extend({}, selection, view._resourceBySlot(selection)));
|
|
}
|
|
} else if (key === keys.DELETE && editable !== false && editable.destroy !== false) {
|
|
that.removeEvent(selection.events[0]);
|
|
} else if (key >= 49 && key <= 57) {
|
|
that.view(that._viewByIndex(key - 49));
|
|
} else if (view.move(selection, key, shiftKey)) {
|
|
if (view.inRange(selection)) {
|
|
that._select();
|
|
} else {
|
|
that.date(selection.start);
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
that._adjustSelectedDate();
|
|
},
|
|
_createSelection: function (item) {
|
|
var uid, slot, selection;
|
|
if (!this._selection || !this._ctrlKey && !this._shiftKey) {
|
|
this._selection = {
|
|
events: [],
|
|
groupIndex: 0
|
|
};
|
|
}
|
|
item = $(item);
|
|
selection = this._selection;
|
|
if (item.is('.k-event')) {
|
|
uid = item.attr(kendo.attr('uid'));
|
|
}
|
|
slot = this.view().selectionByElement(item);
|
|
if (slot) {
|
|
selection.groupIndex = slot.groupIndex || 0;
|
|
}
|
|
if (uid) {
|
|
slot = getOccurrenceByUid(this._data, uid);
|
|
}
|
|
if (slot && slot.uid) {
|
|
uid = [slot.uid];
|
|
}
|
|
this._updateSelection(slot, uid);
|
|
this._adjustSelectedDate();
|
|
},
|
|
_updateSelection: function (dataItem, events) {
|
|
var selection = this._selection;
|
|
if (dataItem && selection) {
|
|
var view = this.view();
|
|
if (dataItem.uid) {
|
|
dataItem = view._updateEventForSelection(dataItem);
|
|
}
|
|
if (this._shiftKey && selection.start && selection.end) {
|
|
var backward = dataItem.end < selection.end;
|
|
selection.end = dataItem.endDate ? dataItem.endDate() : dataItem.end;
|
|
if (backward && view._timeSlotInterval) {
|
|
kendo.date.setTime(selection.end, -view._timeSlotInterval());
|
|
}
|
|
} else {
|
|
selection.start = dataItem.startDate ? dataItem.startDate() : dataItem.start;
|
|
selection.end = dataItem.endDate ? dataItem.endDate() : dataItem.end;
|
|
}
|
|
if ('isDaySlot' in dataItem) {
|
|
selection.isAllDay = dataItem.isDaySlot;
|
|
} else {
|
|
selection.isAllDay = dataItem.isAllDay;
|
|
}
|
|
selection.index = dataItem.index;
|
|
if (this._ctrlKey) {
|
|
selection.events = selection.events.concat(events || []);
|
|
} else {
|
|
selection.events = events || [];
|
|
}
|
|
}
|
|
},
|
|
options: {
|
|
name: 'Scheduler',
|
|
date: TODAY,
|
|
editable: true,
|
|
autoBind: true,
|
|
snap: true,
|
|
mobile: false,
|
|
timezone: '',
|
|
allDaySlot: true,
|
|
min: new Date(1900, 0, 1),
|
|
max: new Date(2099, 11, 31),
|
|
toolbar: null,
|
|
messages: {
|
|
today: 'Today',
|
|
pdf: 'Export to PDF',
|
|
save: 'Save',
|
|
cancel: 'Cancel',
|
|
destroy: 'Delete',
|
|
deleteWindowTitle: 'Delete event',
|
|
ariaSlotLabel: 'Selected from {0:t} to {1:t}',
|
|
ariaEventLabel: '{0} on {1:D} at {2:t}',
|
|
views: {
|
|
day: 'Day',
|
|
week: 'Week',
|
|
workWeek: 'Work Week',
|
|
agenda: 'Agenda',
|
|
month: 'Month',
|
|
timeline: 'Timeline',
|
|
timelineWeek: 'Timeline Week',
|
|
timelineWorkWeek: 'Timeline Work Week',
|
|
timelineMonth: 'Timeline Month'
|
|
},
|
|
recurrenceMessages: {
|
|
deleteWindowTitle: 'Delete Recurring Item',
|
|
deleteWindowOccurrence: 'Delete current occurrence',
|
|
deleteWindowSeries: 'Delete the series',
|
|
editWindowTitle: 'Edit Recurring Item',
|
|
editWindowOccurrence: 'Edit current occurrence',
|
|
editWindowSeries: 'Edit the series'
|
|
},
|
|
editable: { confirmation: DELETECONFIRM },
|
|
editor: {
|
|
title: 'Title',
|
|
start: 'Start',
|
|
end: 'End',
|
|
allDayEvent: 'All day event',
|
|
description: 'Description',
|
|
repeat: 'Repeat',
|
|
timezone: ' ',
|
|
startTimezone: 'Start timezone',
|
|
endTimezone: 'End timezone',
|
|
separateTimezones: 'Use separate start and end time zones',
|
|
timezoneEditorTitle: 'Timezones',
|
|
timezoneEditorButton: 'Time zone',
|
|
timezoneTitle: 'Time zones',
|
|
noTimezone: 'No timezone',
|
|
editorTitle: 'Event'
|
|
}
|
|
},
|
|
height: null,
|
|
width: null,
|
|
resources: [],
|
|
group: {
|
|
resources: [],
|
|
direction: 'horizontal'
|
|
},
|
|
views: [],
|
|
selectable: false
|
|
},
|
|
events: [
|
|
REMOVE,
|
|
EDIT,
|
|
CANCEL,
|
|
SAVE,
|
|
'add',
|
|
'dataBinding',
|
|
'dataBound',
|
|
'moveStart',
|
|
'move',
|
|
'moveEnd',
|
|
'resizeStart',
|
|
'resize',
|
|
'resizeEnd',
|
|
'navigate',
|
|
'change'
|
|
],
|
|
destroy: function () {
|
|
var that = this, element;
|
|
Widget.fn.destroy.call(that);
|
|
if (that.dataSource) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler);
|
|
that.dataSource.unbind('progress', that._progressHandler);
|
|
that.dataSource.unbind('error', that._errorHandler);
|
|
}
|
|
if (that.calendar) {
|
|
that.calendar.destroy();
|
|
that.popup.destroy();
|
|
}
|
|
if (that.view()) {
|
|
that.view().destroy();
|
|
}
|
|
if (that._editor) {
|
|
that._editor.destroy();
|
|
}
|
|
if (this._moveDraggable) {
|
|
this._moveDraggable.destroy();
|
|
}
|
|
if (this._resizeDraggable) {
|
|
this._resizeDraggable.destroy();
|
|
}
|
|
element = that.element.add(that.wrapper).add(that.toolbar).add(that.popup);
|
|
element.off(NS);
|
|
clearTimeout(that._moveTimer);
|
|
that._model = null;
|
|
that.toolbar = null;
|
|
that.element = null;
|
|
$(window).off('resize' + NS, that._resizeHandler);
|
|
kendo.destroy(that.wrapper);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (this.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
items: function () {
|
|
return this.wrapper.find('.k-scheduler-content').children('.k-event, .k-task');
|
|
},
|
|
_movable: function () {
|
|
var startSlot;
|
|
var endSlot;
|
|
var startTime;
|
|
var endTime;
|
|
var event;
|
|
var clonedEvent;
|
|
var that = this;
|
|
var originSlot;
|
|
var distance = 0;
|
|
var isMobile = that._isMobile();
|
|
var movable = that.options.editable && that.options.editable.move !== false;
|
|
var resizable = that.options.editable && that.options.editable.resize !== false;
|
|
if (movable || resizable && isMobile) {
|
|
if (isMobile && kendo.support.mobileOS.android) {
|
|
distance = 5;
|
|
}
|
|
that._moveDraggable = new kendo.ui.Draggable(that.element, {
|
|
distance: distance,
|
|
filter: '.k-event',
|
|
ignore: '.k-resize-handle',
|
|
holdToDrag: isMobile
|
|
});
|
|
if (movable) {
|
|
that._moveDraggable.bind('dragstart', function (e) {
|
|
var view = that.view();
|
|
var eventElement = e.currentTarget;
|
|
if (!view.options.editable || view.options.editable.move === false) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (isMobile && !eventElement.hasClass('k-event-active')) {
|
|
that.element.find('.k-event-active').removeClass('k-event-active');
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
event = that.occurrenceByUid(eventElement.attr(kendo.attr('uid')));
|
|
clonedEvent = event.clone();
|
|
clonedEvent.update(view._eventOptionsForMove(clonedEvent));
|
|
startSlot = view._slotByPosition(e.x.startLocation, e.y.startLocation);
|
|
startTime = startSlot.startOffset(e.x.startLocation, e.y.startLocation, that.options.snap);
|
|
endSlot = startSlot;
|
|
originSlot = startSlot;
|
|
if (!startSlot || that.trigger('moveStart', { event: event })) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('drag', function (e) {
|
|
var view = that.view();
|
|
var slot = view._slotByPosition(e.x.location, e.y.location);
|
|
var distance;
|
|
var range;
|
|
if (!slot) {
|
|
return;
|
|
}
|
|
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
if (slot.isDaySlot !== startSlot.isDaySlot) {
|
|
startSlot = view._slotByPosition(e.x.location, e.y.location);
|
|
startTime = startSlot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
distance = endTime - startTime;
|
|
clonedEvent.isAllDay = slot.isDaySlot;
|
|
clonedEvent.start = kendo.timezone.toLocalDate(startTime);
|
|
clonedEvent.end = kendo.timezone.toLocalDate(endTime);
|
|
view._updateMoveHint(clonedEvent, slot.groupIndex, distance);
|
|
range = {
|
|
start: clonedEvent.start,
|
|
end: clonedEvent.end
|
|
};
|
|
} else {
|
|
distance = endTime - startTime;
|
|
view._updateMoveHint(clonedEvent, slot.groupIndex, distance);
|
|
range = moveEventRange(clonedEvent, distance);
|
|
}
|
|
if (!that.trigger('move', {
|
|
event: event,
|
|
slot: {
|
|
element: slot.element,
|
|
start: slot.startDate(),
|
|
end: slot.endDate(),
|
|
isDaySlot: slot.isDaySlot
|
|
},
|
|
resources: view._resourceBySlot(slot),
|
|
start: range.start,
|
|
end: range.end
|
|
})) {
|
|
endSlot = slot;
|
|
} else {
|
|
view._updateMoveHint(clonedEvent, slot.groupIndex, distance);
|
|
}
|
|
}).bind('dragend', function (e) {
|
|
that.view()._removeMoveHint();
|
|
var distance = endTime - startTime;
|
|
var range = moveEventRange(clonedEvent, distance);
|
|
var start = range.start;
|
|
var end = range.end;
|
|
var endResources = that.view()._resourceBySlot(endSlot);
|
|
var startResources = that.view()._resourceBySlot(startSlot);
|
|
var prevented = that.trigger('moveEnd', {
|
|
event: event,
|
|
slot: {
|
|
element: endSlot.element,
|
|
start: endSlot.startDate(),
|
|
end: endSlot.endDate()
|
|
},
|
|
start: start,
|
|
end: end,
|
|
resources: endResources
|
|
});
|
|
if (!prevented && (event.start.getTime() !== start.getTime() || event.end.getTime() !== end.getTime() || originSlot.isDaySlot !== endSlot.isDaySlot || kendo.stringify(endResources) !== kendo.stringify(startResources))) {
|
|
var updatedEventOptions = that.view()._eventOptionsForMove(event);
|
|
var eventOptions;
|
|
if (originSlot.isDaySlot !== endSlot.isDaySlot) {
|
|
if (endSlot.isDaySlot) {
|
|
eventOptions = $.extend({
|
|
start: endSlot.startDate(),
|
|
end: endSlot.startDate(),
|
|
isAllDay: endSlot.isDaySlot
|
|
}, updatedEventOptions, endResources);
|
|
} else {
|
|
eventOptions = $.extend({
|
|
isAllDay: endSlot.isDaySlot,
|
|
start: start,
|
|
end: end
|
|
}, updatedEventOptions, endResources);
|
|
}
|
|
} else {
|
|
eventOptions = $.extend({
|
|
isAllDay: event.isAllDay,
|
|
start: start,
|
|
end: end
|
|
}, updatedEventOptions, endResources);
|
|
}
|
|
that._updateEvent(null, event, eventOptions);
|
|
}
|
|
e.currentTarget.removeClass('k-event-active');
|
|
this.cancelHold();
|
|
}).bind('dragcancel', function () {
|
|
that.view()._removeMoveHint();
|
|
this.cancelHold();
|
|
});
|
|
}
|
|
if (isMobile) {
|
|
that._moveDraggable.bind('hold', function (e) {
|
|
if (that.element.find('.k-scheduler-monthview').length) {
|
|
e.preventDefault();
|
|
}
|
|
that.element.find('.k-event-active').removeClass('k-event-active');
|
|
e.currentTarget.addClass('k-event-active');
|
|
});
|
|
if (!kendo.support.mobileOS.android) {
|
|
that._moveDraggable.userEvents.bind('press', function (e) {
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_resizable: function () {
|
|
var startTime;
|
|
var endTime;
|
|
var event;
|
|
var clonedEvent;
|
|
var slot;
|
|
var that = this;
|
|
var distance = 0;
|
|
function direction(handle) {
|
|
var directions = {
|
|
'k-resize-e': 'east',
|
|
'k-resize-w': 'west',
|
|
'k-resize-n': 'north',
|
|
'k-resize-s': 'south'
|
|
};
|
|
for (var key in directions) {
|
|
if (handle.hasClass(key)) {
|
|
return directions[key];
|
|
}
|
|
}
|
|
}
|
|
if (that._isMobile() && kendo.support.mobileOS.android) {
|
|
distance = 5;
|
|
}
|
|
that._resizeDraggable = new kendo.ui.Draggable(that.element, {
|
|
distance: distance,
|
|
filter: '.k-resize-handle',
|
|
dragstart: function (e) {
|
|
var dragHandle = $(e.currentTarget);
|
|
var eventElement = dragHandle.closest('.k-event');
|
|
var uid = eventElement.attr(kendo.attr('uid'));
|
|
var view = that.view();
|
|
event = that.occurrenceByUid(uid);
|
|
clonedEvent = event.clone();
|
|
view._updateEventForResize(clonedEvent);
|
|
slot = view._slotByPosition(e.x.startLocation, e.y.startLocation);
|
|
if (that.trigger('resizeStart', { event: event })) {
|
|
e.preventDefault();
|
|
}
|
|
startTime = kendo.date.toUtcTime(clonedEvent.start);
|
|
endTime = kendo.date.toUtcTime(clonedEvent.end);
|
|
},
|
|
drag: function (e) {
|
|
if (!slot) {
|
|
return;
|
|
}
|
|
var dragHandle = $(e.currentTarget);
|
|
var dir = direction(dragHandle);
|
|
var view = that.view();
|
|
var currentSlot = view._slotByPosition(e.x.location, e.y.location);
|
|
if (!currentSlot || slot.groupIndex != currentSlot.groupIndex) {
|
|
return;
|
|
}
|
|
slot = currentSlot;
|
|
var originalStart = startTime;
|
|
var originalEnd = endTime;
|
|
if (dir == 'south') {
|
|
if (!slot.isDaySlot && slot.end - kendo.date.toUtcTime(clonedEvent.start) >= view._timeSlotInterval()) {
|
|
if (clonedEvent.isAllDay) {
|
|
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
} else {
|
|
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
|
|
}
|
|
}
|
|
} else if (dir == 'north') {
|
|
if (!slot.isDaySlot && kendo.date.toUtcTime(clonedEvent.end) - slot.start >= view._timeSlotInterval()) {
|
|
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
}
|
|
} else if (dir == 'east') {
|
|
if (slot.isDaySlot && kendo.date.toUtcTime(kendo.date.getDate(slot.endDate())) >= kendo.date.toUtcTime(kendo.date.getDate(clonedEvent.start))) {
|
|
if (clonedEvent.isAllDay) {
|
|
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
} else {
|
|
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
|
|
}
|
|
} else if (!slot.isDaySlot && slot.end - kendo.date.toUtcTime(clonedEvent.start) >= view._timeSlotInterval()) {
|
|
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
|
|
}
|
|
} else if (dir == 'west') {
|
|
if (slot.isDaySlot && kendo.date.toUtcTime(kendo.date.getDate(clonedEvent.end)) >= kendo.date.toUtcTime(kendo.date.getDate(slot.startDate()))) {
|
|
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
} else if (!slot.isDaySlot && kendo.date.toUtcTime(clonedEvent.end) - slot.start >= view._timeSlotInterval()) {
|
|
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
|
|
}
|
|
}
|
|
if (!that.trigger('resize', {
|
|
event: event,
|
|
slot: {
|
|
element: slot.element,
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
},
|
|
start: kendo.timezone.toLocalDate(startTime),
|
|
end: kendo.timezone.toLocalDate(endTime),
|
|
resources: view._resourceBySlot(slot)
|
|
})) {
|
|
view._updateResizeHint(clonedEvent, slot.groupIndex, startTime, endTime);
|
|
} else {
|
|
startTime = originalStart;
|
|
endTime = originalEnd;
|
|
}
|
|
},
|
|
dragend: function (e) {
|
|
var dragHandle = $(e.currentTarget);
|
|
var start = new Date(clonedEvent.start.getTime());
|
|
var end = new Date(clonedEvent.end.getTime());
|
|
var dir = direction(dragHandle);
|
|
that.view()._removeResizeHint();
|
|
if (dir == 'south') {
|
|
end = kendo.timezone.toLocalDate(endTime);
|
|
} else if (dir == 'north') {
|
|
start = kendo.timezone.toLocalDate(startTime);
|
|
} else if (dir == 'east') {
|
|
if (slot.isDaySlot) {
|
|
end = kendo.date.getDate(kendo.timezone.toLocalDate(endTime));
|
|
} else {
|
|
end = kendo.timezone.toLocalDate(endTime);
|
|
}
|
|
} else if (dir == 'west') {
|
|
if (slot.isDaySlot) {
|
|
start = new Date(kendo.timezone.toLocalDate(startTime));
|
|
start.setHours(0);
|
|
start.setMinutes(0);
|
|
} else {
|
|
start = kendo.timezone.toLocalDate(startTime);
|
|
}
|
|
}
|
|
var prevented = that.trigger('resizeEnd', {
|
|
event: event,
|
|
slot: {
|
|
element: slot.element,
|
|
start: slot.startDate(),
|
|
end: slot.endDate()
|
|
},
|
|
start: start,
|
|
end: end,
|
|
resources: that.view()._resourceBySlot(slot)
|
|
});
|
|
if (!prevented && end.getTime() >= start.getTime()) {
|
|
if (clonedEvent.start.getTime() != start.getTime() || clonedEvent.end.getTime() != end.getTime()) {
|
|
that.view()._updateEventForResize(event);
|
|
that._updateEvent(dir, event, {
|
|
start: start,
|
|
end: end
|
|
});
|
|
}
|
|
}
|
|
slot = null;
|
|
event = null;
|
|
},
|
|
dragcancel: function () {
|
|
that.view()._removeResizeHint();
|
|
slot = null;
|
|
event = null;
|
|
}
|
|
});
|
|
},
|
|
_updateEvent: function (dir, event, eventInfo) {
|
|
var that = this;
|
|
var updateEvent = function (event, callback) {
|
|
try {
|
|
that._preventRefresh = true;
|
|
event.update(eventInfo);
|
|
that._convertDates(event);
|
|
} finally {
|
|
that._preventRefresh = false;
|
|
}
|
|
if (!that.trigger(SAVE, { event: event })) {
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
that._updateSelection(event);
|
|
that.dataSource.sync();
|
|
}
|
|
};
|
|
var recurrenceHead = function (event) {
|
|
if (event.recurrenceRule) {
|
|
return that.dataSource.getByUid(event.uid);
|
|
} else {
|
|
return that.dataSource.get(event.recurrenceId);
|
|
}
|
|
};
|
|
var updateSeries = function () {
|
|
var head = recurrenceHead(event);
|
|
if (dir == 'south' || dir == 'north') {
|
|
if (eventInfo.start) {
|
|
var start = kendo.date.getDate(head.start);
|
|
kendo.date.setTime(start, getMilliseconds(eventInfo.start));
|
|
eventInfo.start = start;
|
|
}
|
|
if (eventInfo.end) {
|
|
var end = kendo.date.getDate(head.end);
|
|
kendo.date.setTime(end, getMilliseconds(eventInfo.end));
|
|
eventInfo.end = end;
|
|
}
|
|
}
|
|
that.dataSource._removeExceptions(head);
|
|
updateEvent(head);
|
|
};
|
|
var updateOccurrence = function () {
|
|
var head = recurrenceHead(event);
|
|
var callback = function () {
|
|
that._convertDates(head);
|
|
};
|
|
var exception = head.toOccurrence({
|
|
start: event.start,
|
|
end: event.end
|
|
});
|
|
updateEvent(that.dataSource.add(exception), callback);
|
|
};
|
|
if (event.recurrenceRule || event.isOccurrence()) {
|
|
var recurrenceMessages = that.options.messages.recurrenceMessages;
|
|
that._showRecurringDialog(event, updateOccurrence, updateSeries, {
|
|
title: recurrenceMessages.editWindowTitle,
|
|
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
|
|
occurrenceText: recurrenceMessages.editWindowOccurrence,
|
|
seriesText: recurrenceMessages.editWindowSeries
|
|
});
|
|
} else {
|
|
updateEvent(that.dataSource.getByUid(event.uid));
|
|
}
|
|
},
|
|
_modelForContainer: function (container) {
|
|
container = $(container).closest('[' + kendo.attr('uid') + ']');
|
|
return this.dataSource.getByUid(container.attr(kendo.attr('uid')));
|
|
},
|
|
showDialog: function (options) {
|
|
this._editor.showDialog(options);
|
|
},
|
|
focus: function () {
|
|
this.wrapper.focus();
|
|
},
|
|
_confirmation: function (callback, model) {
|
|
var editable = this.options.editable;
|
|
if (editable === true || editable.confirmation !== false) {
|
|
var messages = this.options.messages;
|
|
var title = messages.deleteWindowTitle;
|
|
var text = typeof editable.confirmation === STRING ? editable.confirmation : messages.editable.confirmation;
|
|
if (this._isEditorOpened() && model.isRecurring()) {
|
|
var recurrenceMessages = this.options.messages.recurrenceMessages;
|
|
title = recurrenceMessages.deleteWindowTitle;
|
|
if (model.isException()) {
|
|
text = recurrenceMessages.deleteRecurringConfirmation ? recurrenceMessages.deleteRecurringConfirmation : DELETERECURRINGCONFIRM;
|
|
} else {
|
|
text = recurrenceMessages.deleteSeriesConfirmation ? recurrenceMessages.deleteSeriesConfirmation : DELETESERIESCONFIRM;
|
|
}
|
|
}
|
|
var buttons = [{
|
|
name: 'destroy',
|
|
text: messages.destroy,
|
|
click: function () {
|
|
callback();
|
|
}
|
|
}];
|
|
if (!(this._isMobile() && kendo.mobile.ui.Pane)) {
|
|
buttons.push({
|
|
name: 'canceledit',
|
|
text: messages.cancel,
|
|
click: function () {
|
|
callback(true);
|
|
}
|
|
});
|
|
}
|
|
this._unbindResize();
|
|
this.showDialog({
|
|
model: model,
|
|
text: text,
|
|
title: title,
|
|
buttons: buttons
|
|
});
|
|
this._bindResize();
|
|
} else {
|
|
callback();
|
|
}
|
|
},
|
|
addEvent: function (eventInfo) {
|
|
var editable = this._editor.editable;
|
|
var dataSource = this.dataSource;
|
|
var event;
|
|
eventInfo = eventInfo || {};
|
|
var prevented = this.trigger('add', { event: eventInfo });
|
|
if (!prevented && (editable && editable.end() || !editable)) {
|
|
this.cancelEvent();
|
|
if (eventInfo && eventInfo.toJSON) {
|
|
eventInfo = eventInfo.toJSON();
|
|
}
|
|
event = dataSource.add(eventInfo);
|
|
if (event) {
|
|
this.cancelEvent();
|
|
this._editEvent(event);
|
|
}
|
|
}
|
|
},
|
|
saveEvent: function () {
|
|
var editor = this._editor;
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
var editable = editor.editable;
|
|
var container = editor.container;
|
|
var model = this._modelForContainer(container);
|
|
if (container && editable && editable.end() && !this.trigger(SAVE, {
|
|
container: container,
|
|
event: model
|
|
})) {
|
|
if (model.isRecurrenceHead()) {
|
|
this.dataSource._removeExceptions(model);
|
|
}
|
|
if (!model.dirty && !model.isOccurrence()) {
|
|
this._convertDates(model, 'remove');
|
|
}
|
|
this.dataSource.sync();
|
|
}
|
|
},
|
|
cancelEvent: function () {
|
|
var editor = this._editor;
|
|
var container = editor.container;
|
|
var model;
|
|
if (container) {
|
|
model = this._modelForContainer(container);
|
|
if (model && model.isOccurrence()) {
|
|
this._convertDates(model, 'remove');
|
|
this._convertDates(this.dataSource.get(model.recurrenceId), 'remove');
|
|
}
|
|
this.dataSource.cancelChanges(model);
|
|
editor.close();
|
|
}
|
|
},
|
|
editEvent: function (uid) {
|
|
var model = typeof uid == 'string' ? this.occurrenceByUid(uid) : uid;
|
|
if (!model) {
|
|
return;
|
|
}
|
|
this.cancelEvent();
|
|
if (model.isRecurring()) {
|
|
this._editRecurringDialog(model);
|
|
} else {
|
|
this._editEvent(model);
|
|
}
|
|
},
|
|
_editEvent: function (model) {
|
|
this._unbindResize();
|
|
this._createPopupEditor(model);
|
|
this._bindResize();
|
|
},
|
|
_editRecurringDialog: function (model) {
|
|
var that = this;
|
|
var editOccurrence = function () {
|
|
if (model.isException()) {
|
|
that._editEvent(model);
|
|
} else {
|
|
that.addEvent(model);
|
|
}
|
|
};
|
|
var editSeries = function () {
|
|
if (model.recurrenceId) {
|
|
model = that.dataSource.get(model.recurrenceId);
|
|
}
|
|
that._editEvent(model);
|
|
};
|
|
var recurrenceMessages = that.options.messages.recurrenceMessages;
|
|
that._showRecurringDialog(model, editOccurrence, editSeries, {
|
|
title: recurrenceMessages.editWindowTitle,
|
|
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
|
|
occurrenceText: recurrenceMessages.editWindowOccurrence,
|
|
seriesText: recurrenceMessages.editWindowSeries
|
|
});
|
|
},
|
|
_showRecurringDialog: function (model, editOccurrence, editSeries, messages) {
|
|
var that = this;
|
|
var editable = that.options.editable;
|
|
var editRecurringMode = isPlainObject(editable) ? editable.editRecurringMode : 'dialog';
|
|
if (editRecurringMode === 'series') {
|
|
editSeries();
|
|
} else if (editRecurringMode === 'occurrence') {
|
|
editOccurrence();
|
|
} else {
|
|
this._unbindResize();
|
|
that.showDialog({
|
|
model: model,
|
|
title: messages.title,
|
|
text: messages.text,
|
|
buttons: [
|
|
{
|
|
text: messages.occurrenceText,
|
|
click: editOccurrence
|
|
},
|
|
{
|
|
text: messages.seriesText,
|
|
click: editSeries
|
|
}
|
|
]
|
|
});
|
|
this._bindResize();
|
|
}
|
|
},
|
|
_createButton: function (command) {
|
|
var template = command.template || COMMANDBUTTONTMPL, commandName = typeof command === STRING ? command : command.name || command.text, options = {
|
|
className: 'k-scheduler-' + (commandName || '').replace(/\s/g, ''),
|
|
text: commandName,
|
|
attr: ''
|
|
};
|
|
if (!commandName && !(isPlainObject(command) && command.template)) {
|
|
throw new Error('Custom commands should have name specified');
|
|
}
|
|
if (isPlainObject(command)) {
|
|
if (command.className) {
|
|
command.className += ' ' + options.className;
|
|
}
|
|
if (commandName === 'edit' && isPlainObject(command.text)) {
|
|
command = extend(true, {}, command);
|
|
command.text = command.text.edit;
|
|
}
|
|
options = extend(true, options, defaultCommands[commandName], command);
|
|
} else {
|
|
options = extend(true, options, defaultCommands[commandName]);
|
|
}
|
|
return kendo.template(template)(options);
|
|
},
|
|
_convertDates: function (model, method) {
|
|
var timezone = this.dataSource.reader.timezone;
|
|
var startTimezone = model.startTimezone;
|
|
var endTimezone = model.endTimezone;
|
|
var start = model.start;
|
|
var end = model.start;
|
|
method = method || 'apply';
|
|
startTimezone = startTimezone || endTimezone;
|
|
endTimezone = endTimezone || startTimezone;
|
|
if (startTimezone) {
|
|
if (timezone) {
|
|
if (method === 'apply') {
|
|
start = kendo.timezone.convert(model.start, timezone, startTimezone);
|
|
end = kendo.timezone.convert(model.end, timezone, endTimezone);
|
|
} else {
|
|
start = kendo.timezone.convert(model.start, startTimezone, timezone);
|
|
end = kendo.timezone.convert(model.end, endTimezone, timezone);
|
|
}
|
|
} else {
|
|
start = kendo.timezone[method](model.start, startTimezone);
|
|
end = kendo.timezone[method](model.end, endTimezone);
|
|
}
|
|
model._set('start', start);
|
|
model._set('end', end);
|
|
}
|
|
},
|
|
_createEditor: function () {
|
|
var that = this;
|
|
var editor;
|
|
if (this._isMobile() && kendo.mobile.ui.Pane) {
|
|
editor = that._editor = new MobileEditor(this.wrapper, extend({}, this.options, {
|
|
target: this,
|
|
timezone: that.dataSource.reader.timezone,
|
|
resources: that.resources,
|
|
createButton: proxy(this._createButton, this)
|
|
}));
|
|
} else {
|
|
editor = that._editor = new PopupEditor(this.wrapper, extend({}, this.options, {
|
|
target: this,
|
|
createButton: proxy(this._createButton, this),
|
|
timezone: that.dataSource.reader.timezone,
|
|
resources: that.resources
|
|
}));
|
|
}
|
|
editor.bind('cancel', function (e) {
|
|
if (that.trigger('cancel', {
|
|
container: e.container,
|
|
event: e.model
|
|
})) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
that.cancelEvent();
|
|
that.focus();
|
|
});
|
|
editor.bind('edit', function (e) {
|
|
if (that.trigger(EDIT, {
|
|
container: e.container,
|
|
event: e.model
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
editor.bind('save', function () {
|
|
that.saveEvent();
|
|
});
|
|
editor.bind('remove', function (e) {
|
|
that.removeEvent(e.model);
|
|
});
|
|
},
|
|
_createPopupEditor: function (model) {
|
|
var editor = this._editor;
|
|
if (!model.isNew() || model.isOccurrence()) {
|
|
if (model.isOccurrence()) {
|
|
this._convertDates(model.recurrenceId ? this.dataSource.get(model.recurrenceId) : model);
|
|
}
|
|
this._convertDates(model);
|
|
}
|
|
this.editable = editor.editEvent(model);
|
|
},
|
|
removeEvent: function (uid) {
|
|
var that = this, model = typeof uid == 'string' ? that.occurrenceByUid(uid) : uid;
|
|
if (!model) {
|
|
return;
|
|
}
|
|
if (model.isRecurring()) {
|
|
that._deleteRecurringDialog(model);
|
|
} else {
|
|
that._confirmation(function (cancel) {
|
|
if (!cancel) {
|
|
that._removeEvent(model);
|
|
}
|
|
}, model);
|
|
}
|
|
},
|
|
occurrenceByUid: function (uid) {
|
|
var occurrence = this.dataSource.getByUid(uid);
|
|
if (!occurrence) {
|
|
occurrence = getOccurrenceByUid(this._data, uid);
|
|
}
|
|
return occurrence;
|
|
},
|
|
occurrencesInRange: function (start, end) {
|
|
return new kendo.data.Query(this._data).filter({
|
|
logic: 'or',
|
|
filters: [
|
|
{
|
|
logic: 'and',
|
|
filters: [
|
|
{
|
|
field: 'start',
|
|
operator: 'gte',
|
|
value: start
|
|
},
|
|
{
|
|
field: 'end',
|
|
operator: 'gte',
|
|
value: start
|
|
},
|
|
{
|
|
field: 'start',
|
|
operator: 'lt',
|
|
value: end
|
|
}
|
|
]
|
|
},
|
|
{
|
|
logic: 'and',
|
|
filters: [
|
|
{
|
|
field: 'start',
|
|
operator: 'lte',
|
|
value: start
|
|
},
|
|
{
|
|
field: 'end',
|
|
operator: 'gt',
|
|
value: start
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}).toArray();
|
|
},
|
|
_removeEvent: function (model) {
|
|
if (!this.trigger(REMOVE, { event: model })) {
|
|
if (this.dataSource.remove(model)) {
|
|
this.dataSource.sync();
|
|
}
|
|
}
|
|
},
|
|
_deleteRecurringDialog: function (model) {
|
|
var that = this;
|
|
var currentModel = model;
|
|
var editable = that.options.editable;
|
|
var deleteOccurrence;
|
|
var deleteSeries;
|
|
var deleteOccurrenceConfirmation;
|
|
var deleteSeriesConfirmation;
|
|
var editRecurringMode = isPlainObject(editable) ? editable.editRecurringMode : 'dialog';
|
|
deleteOccurrence = function () {
|
|
var occurrence = currentModel.recurrenceId ? currentModel : currentModel.toOccurrence();
|
|
var head = that.dataSource.get(occurrence.recurrenceId);
|
|
that._convertDates(head);
|
|
that._removeEvent(occurrence);
|
|
};
|
|
deleteSeries = function () {
|
|
if (currentModel.recurrenceId) {
|
|
currentModel = that.dataSource.get(currentModel.recurrenceId);
|
|
}
|
|
that._removeEvent(currentModel);
|
|
};
|
|
if (editRecurringMode != 'dialog' || that._isEditorOpened()) {
|
|
deleteOccurrenceConfirmation = function () {
|
|
that._confirmation(function (cancel) {
|
|
if (!cancel) {
|
|
deleteOccurrence();
|
|
}
|
|
}, currentModel);
|
|
};
|
|
deleteSeriesConfirmation = function () {
|
|
that._confirmation(function (cancel) {
|
|
if (!cancel) {
|
|
deleteSeries();
|
|
}
|
|
}, currentModel);
|
|
};
|
|
}
|
|
var seriesCallback = deleteSeriesConfirmation || deleteSeries;
|
|
var occurrenceCallback = deleteOccurrenceConfirmation || deleteOccurrence;
|
|
if (that._isEditorOpened()) {
|
|
if (model.isException()) {
|
|
occurrenceCallback();
|
|
} else {
|
|
seriesCallback();
|
|
}
|
|
} else {
|
|
var recurrenceMessages = that.options.messages.recurrenceMessages;
|
|
that._showRecurringDialog(model, occurrenceCallback, seriesCallback, {
|
|
title: recurrenceMessages.deleteWindowTitle,
|
|
text: recurrenceMessages.deleteRecurring ? recurrenceMessages.deleteRecurring : DELETERECURRING,
|
|
occurrenceText: recurrenceMessages.deleteWindowOccurrence,
|
|
seriesText: recurrenceMessages.deleteWindowSeries
|
|
});
|
|
}
|
|
},
|
|
_isEditorOpened: function () {
|
|
return !!this._editor.container;
|
|
},
|
|
_unbindView: function (view) {
|
|
var that = this;
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.items() };
|
|
});
|
|
view.destroy();
|
|
},
|
|
_bindView: function (view) {
|
|
var that = this;
|
|
if (that.options.editable) {
|
|
if (that._viewRemoveHandler) {
|
|
view.unbind(REMOVE, that._viewRemoveHandler);
|
|
}
|
|
that._viewRemoveHandler = function (e) {
|
|
that.removeEvent(e.uid);
|
|
};
|
|
view.bind(REMOVE, that._viewRemoveHandler);
|
|
if (that._viewAddHandler) {
|
|
view.unbind(ADD, that._viewAddHandler);
|
|
}
|
|
that._viewAddHandler = function (e) {
|
|
that.addEvent(e.eventInfo);
|
|
};
|
|
view.bind(ADD, this._viewAddHandler);
|
|
if (that._viewEditHandler) {
|
|
view.unbind(EDIT, that._viewEditHandler);
|
|
}
|
|
that._viewEditHandler = function (e) {
|
|
that.editEvent(e.uid);
|
|
};
|
|
view.bind(EDIT, this._viewEditHandler);
|
|
}
|
|
if (that._viewNavigateHandler) {
|
|
view.unbind('navigate', that._viewNavigateHandler);
|
|
}
|
|
that._viewNavigateHandler = function (e) {
|
|
if (e.view) {
|
|
var switchWorkDay = 'isWorkDay' in e;
|
|
var action = switchWorkDay ? 'changeWorkDay' : 'changeView';
|
|
if (!that.trigger('navigate', {
|
|
view: e.view,
|
|
isWorkDay: e.isWorkDay,
|
|
action: action,
|
|
date: e.date
|
|
})) {
|
|
if (switchWorkDay) {
|
|
that._workDayMode = e.isWorkDay;
|
|
}
|
|
that._selectView(e.view);
|
|
that.date(e.date);
|
|
}
|
|
}
|
|
};
|
|
view.bind('navigate', that._viewNavigateHandler);
|
|
if (that._viewActivateHandler) {
|
|
view.unbind('activate', that._viewActivateHandler);
|
|
}
|
|
that._viewActivateHandler = function () {
|
|
var view = this;
|
|
if (that._selection) {
|
|
view.constrainSelection(that._selection);
|
|
that._select();
|
|
that._adjustSelectedDate();
|
|
}
|
|
};
|
|
view.bind('activate', that._viewActivateHandler);
|
|
},
|
|
_selectView: function (name) {
|
|
var that = this;
|
|
if (name && that.views[name]) {
|
|
if (that._selectedView) {
|
|
that._unbindView(that._selectedView);
|
|
}
|
|
that._selectedView = that._renderView(name);
|
|
that._selectedViewName = name;
|
|
if (that._viewsCount > 1) {
|
|
var viewButton = VIEWBUTTONTEMPLATE({
|
|
views: that.views,
|
|
view: name,
|
|
ns: kendo.ns
|
|
});
|
|
var firstButton = that.toolbar.find('.k-scheduler-views li:first-child');
|
|
if (firstButton.is('.k-current-view')) {
|
|
firstButton.replaceWith(viewButton);
|
|
} else {
|
|
that.toolbar.find('.k-scheduler-views').prepend(viewButton);
|
|
}
|
|
var viewButtons = that.toolbar.find('.k-scheduler-views li').removeClass('k-state-selected');
|
|
viewButtons.end().find('.k-view-' + name.replace(/\./g, '\\.').toLowerCase()).addClass('k-state-selected');
|
|
}
|
|
}
|
|
},
|
|
view: function (name) {
|
|
var that = this;
|
|
if (name) {
|
|
that._selectView(name);
|
|
that.rebind();
|
|
return;
|
|
}
|
|
return that._selectedView;
|
|
},
|
|
viewName: function () {
|
|
return this.view().name;
|
|
},
|
|
_renderView: function (name) {
|
|
var view = this._initializeView(name);
|
|
this._bindView(view);
|
|
this._model.set('formattedDate', view.dateForTitle());
|
|
this._model.set('formattedShortDate', view.shortDateForTitle());
|
|
return view;
|
|
},
|
|
resize: function (force) {
|
|
var size = this.getSize();
|
|
var currentSize = this._size;
|
|
var view = this.view();
|
|
if (!view || !view.groups) {
|
|
return;
|
|
}
|
|
if (force || !currentSize || size.width !== currentSize.width || size.height !== currentSize.height) {
|
|
this.refresh({ action: 'resize' });
|
|
this._size = size;
|
|
}
|
|
},
|
|
_adjustSelectedDate: function () {
|
|
var date = this._model.selectedDate, selection = this._selection, start = selection.start;
|
|
if (start && !kendo.date.isInDateRange(date, getDate(start), getDate(selection.end))) {
|
|
date.setFullYear(start.getFullYear(), start.getMonth(), start.getDate());
|
|
}
|
|
},
|
|
_initializeView: function (name) {
|
|
var view = this.views[name];
|
|
if (view) {
|
|
var isSettings = isPlainObject(view), type = view.type;
|
|
if (typeof type === STRING) {
|
|
type = kendo.getter(view.type)(window);
|
|
}
|
|
if (type) {
|
|
view = new type(this.wrapper, trimOptions(extend(true, {}, this.options, isSettings ? view : {}, {
|
|
resources: this.resources,
|
|
date: this.date(),
|
|
showWorkHours: this._workDayMode
|
|
})));
|
|
} else {
|
|
throw new Error('There is no such view');
|
|
}
|
|
}
|
|
return view;
|
|
},
|
|
_views: function () {
|
|
var views = this.options.views;
|
|
var view;
|
|
var defaultView;
|
|
var selected;
|
|
var isSettings;
|
|
var name;
|
|
var type;
|
|
var idx;
|
|
var length;
|
|
this.views = {};
|
|
this._viewsCount = 0;
|
|
for (idx = 0, length = views.length; idx < length; idx++) {
|
|
var hasType = false;
|
|
view = views[idx];
|
|
isSettings = isPlainObject(view);
|
|
if (isSettings) {
|
|
type = name = view.type ? view.type : view;
|
|
if (typeof type !== STRING) {
|
|
name = view.name || view.title;
|
|
hasType = true;
|
|
}
|
|
} else {
|
|
type = name = view;
|
|
}
|
|
defaultView = defaultViews[name];
|
|
if (defaultView && !hasType) {
|
|
view.type = defaultView.type;
|
|
defaultView.title = this.options.messages.views[name];
|
|
if (defaultView.type === 'day') {
|
|
defaultView.messages = { allDay: this.options.messages.allDay };
|
|
} else if (defaultView.type === 'agenda') {
|
|
defaultView.messages = {
|
|
event: this.options.messages.event,
|
|
date: this.options.messages.date,
|
|
time: this.options.messages.time
|
|
};
|
|
}
|
|
}
|
|
view = extend({ title: name }, defaultView, isSettings ? view : {});
|
|
if (name) {
|
|
this.views[name] = view;
|
|
this._viewsCount++;
|
|
if (!selected || view.selected) {
|
|
selected = name;
|
|
}
|
|
}
|
|
}
|
|
if (selected) {
|
|
this._selectedViewName = selected;
|
|
}
|
|
},
|
|
rebind: function () {
|
|
this.dataSource.fetch();
|
|
},
|
|
_dataSource: function () {
|
|
var that = this, options = that.options, dataSource = options.dataSource;
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (options.timezone && !(dataSource instanceof SchedulerDataSource)) {
|
|
dataSource = extend(true, dataSource, { schema: { timezone: options.timezone } });
|
|
} else if (dataSource instanceof SchedulerDataSource) {
|
|
options.timezone = dataSource.options.schema ? dataSource.options.schema.timezone : '';
|
|
}
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind('progress', that._progressHandler).unbind('error', that._errorHandler);
|
|
} else {
|
|
that._refreshHandler = proxy(that.refresh, that);
|
|
that._progressHandler = proxy(that._requestStart, that);
|
|
that._errorHandler = proxy(that._error, that);
|
|
}
|
|
that.dataSource = kendo.data.SchedulerDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind('progress', that._progressHandler).bind('error', that._errorHandler);
|
|
that.options.dataSource = that.dataSource;
|
|
},
|
|
_error: function () {
|
|
this._progress(false);
|
|
},
|
|
_requestStart: function () {
|
|
this._progress(true);
|
|
},
|
|
_progress: function (toggle) {
|
|
var element = this.element.find('.k-scheduler-content');
|
|
kendo.ui.progress(element, toggle);
|
|
},
|
|
_resources: function () {
|
|
var that = this;
|
|
var resources = that.options.resources;
|
|
for (var idx = 0; idx < resources.length; idx++) {
|
|
var resource = resources[idx];
|
|
var field = resource.field;
|
|
var dataSource = resource.dataSource;
|
|
if (!field || !dataSource) {
|
|
throw new Error('The "field" and "dataSource" options of the scheduler resource are mandatory.');
|
|
}
|
|
that.resources.push({
|
|
field: field,
|
|
name: resource.name || field,
|
|
title: resource.title || field,
|
|
dataTextField: resource.dataTextField || 'text',
|
|
dataValueField: resource.dataValueField || 'value',
|
|
dataColorField: resource.dataColorField || 'color',
|
|
valuePrimitive: resource.valuePrimitive != null ? resource.valuePrimitive : true,
|
|
multiple: resource.multiple || false,
|
|
dataSource: kendo.data.DataSource.create(dataSource)
|
|
});
|
|
}
|
|
var promises = $.map(that.resources, function (resource) {
|
|
return resource.dataSource.fetch();
|
|
});
|
|
$.when.apply(null, promises).then(function () {
|
|
if (that.options.autoBind) {
|
|
that.view(that._selectedViewName);
|
|
} else {
|
|
that._selectView(that._selectedViewName);
|
|
}
|
|
});
|
|
},
|
|
_initModel: function () {
|
|
var that = this;
|
|
that._model = kendo.observable({
|
|
selectedDate: new Date(this.options.date),
|
|
formattedDate: '',
|
|
formattedShortDate: ''
|
|
});
|
|
that._model.bind('change', function (e) {
|
|
if (e.field === 'selectedDate') {
|
|
that.view(that._selectedViewName);
|
|
}
|
|
});
|
|
},
|
|
_wrapper: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var height = options.height;
|
|
var width = options.width;
|
|
that.wrapper = that.element.addClass('k-widget k-scheduler k-floatwrap').attr('role', 'grid').attr('aria-multiselectable', true);
|
|
if (that._isMobile()) {
|
|
that.wrapper.addClass('k-scheduler-mobile');
|
|
}
|
|
if (that._isMobilePhoneView()) {
|
|
that.wrapper.addClass('k-scheduler-phone');
|
|
}
|
|
if (height) {
|
|
that.wrapper.height(height);
|
|
}
|
|
if (width) {
|
|
that.wrapper.width(width);
|
|
}
|
|
},
|
|
date: function (value) {
|
|
if (value != null && getDate(value) >= getDate(this.options.min) && getDate(value) <= getDate(this.options.max)) {
|
|
this._model.set('selectedDate', value);
|
|
}
|
|
return getDate(this._model.get('selectedDate'));
|
|
},
|
|
_toolbar: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var commands = [];
|
|
if (options.toolbar) {
|
|
commands = $.isArray(options.toolbar) ? options.toolbar : [options.toolbar];
|
|
}
|
|
var template = this._isMobilePhoneView() ? MOBILETOOLBARTEMPLATE : TOOLBARTEMPLATE;
|
|
var toolbar = $(template({
|
|
messages: options.messages,
|
|
pdf: $.grep(commands, function (item) {
|
|
return item == 'pdf' || item.name == 'pdf';
|
|
}).length > 0,
|
|
ns: kendo.ns,
|
|
views: that.views,
|
|
viewsCount: that._viewsCount
|
|
}));
|
|
that.wrapper.append(toolbar);
|
|
that.toolbar = toolbar;
|
|
kendo.bind(that.toolbar, that._model);
|
|
toolbar.on(CLICK + NS, '.k-pdf', function (e) {
|
|
e.preventDefault();
|
|
that.saveAsPDF();
|
|
});
|
|
toolbar.on(CLICK + NS, '.k-scheduler-navigation li', function (e) {
|
|
var li = $(this);
|
|
var date = new Date(that.date());
|
|
var action = '';
|
|
e.preventDefault();
|
|
if (li.hasClass('k-nav-today')) {
|
|
action = 'today';
|
|
date = new Date();
|
|
} else if (li.hasClass('k-nav-next')) {
|
|
action = 'next';
|
|
date = that.view().nextDate();
|
|
} else if (li.hasClass('k-nav-prev')) {
|
|
action = 'previous';
|
|
date = that.view().previousDate();
|
|
} else if (li.hasClass('k-nav-current') && !that._isMobilePhoneView()) {
|
|
that._showCalendar();
|
|
return;
|
|
}
|
|
if (!that.trigger('navigate', {
|
|
view: that._selectedViewName,
|
|
action: action,
|
|
date: date
|
|
})) {
|
|
that.date(date);
|
|
}
|
|
});
|
|
toolbar.on(CLICK + NS, '.k-scheduler-views li, .k-scheduler-refresh', function (e) {
|
|
e.preventDefault();
|
|
var name = $(this).attr(kendo.attr('name'));
|
|
if (!that.trigger('navigate', {
|
|
view: name,
|
|
action: 'changeView',
|
|
date: that.date()
|
|
})) {
|
|
that.view(name);
|
|
that.element.find('.k-state-expanded').removeClass('k-state-expanded');
|
|
}
|
|
});
|
|
toolbar.on(CLICK + NS, '.k-scheduler-views li.k-current-view', function () {
|
|
that.element.find('.k-scheduler-views').toggleClass('k-state-expanded');
|
|
});
|
|
toolbar.find('li').hover(function () {
|
|
$(this).addClass('k-state-hover');
|
|
}, function () {
|
|
$(this).removeClass('k-state-hover');
|
|
});
|
|
},
|
|
_showCalendar: function () {
|
|
var that = this, target = that.toolbar.find('.k-nav-current'), html = $('<div class="k-calendar-container"><div class="k-scheduler-calendar"/></div>');
|
|
if (!that.popup) {
|
|
that.popup = new Popup(html, {
|
|
anchor: target,
|
|
activate: function () {
|
|
if (!that.calendar) {
|
|
that.calendar = new Calendar(this.element.find('.k-scheduler-calendar'), {
|
|
change: function () {
|
|
var date = this.value();
|
|
if (!that.trigger('navigate', {
|
|
view: that._selectedViewName,
|
|
action: 'changeDate',
|
|
date: date
|
|
})) {
|
|
that.date(date);
|
|
that.popup.close();
|
|
}
|
|
},
|
|
min: that.options.min,
|
|
max: that.options.max
|
|
});
|
|
}
|
|
that.calendar.value(that.date());
|
|
},
|
|
copyAnchorStyles: false
|
|
});
|
|
}
|
|
that.popup.open();
|
|
},
|
|
refresh: function (e) {
|
|
var that = this;
|
|
var view = this.view();
|
|
this._progress(false);
|
|
this.angular('cleanup', function () {
|
|
return { elements: that.items() };
|
|
});
|
|
e = e || {};
|
|
if (!view) {
|
|
return;
|
|
}
|
|
if (e && e.action === 'itemchange' && (this._editor.editable || this._preventRefresh)) {
|
|
return;
|
|
}
|
|
if (this.trigger('dataBinding', {
|
|
action: e.action || 'rebind',
|
|
index: e.index,
|
|
items: e.items
|
|
})) {
|
|
return;
|
|
}
|
|
if (!(e && e.action === 'resize') && this._editor) {
|
|
this._editor.close();
|
|
}
|
|
this._data = this.dataSource.expand(view.startDate(), view.endDate());
|
|
view.render(this._data);
|
|
this.trigger('dataBound');
|
|
},
|
|
slotByPosition: function (x, y) {
|
|
var view = this.view();
|
|
if (!view._slotByPosition) {
|
|
return null;
|
|
}
|
|
var slot = view._slotByPosition(x, y);
|
|
if (!slot) {
|
|
return null;
|
|
}
|
|
return {
|
|
startDate: slot.startDate(),
|
|
endDate: slot.endDate(),
|
|
groupIndex: slot.groupIndex,
|
|
element: slot.element,
|
|
isDaySlot: slot.isDaySlot
|
|
};
|
|
},
|
|
slotByElement: function (element) {
|
|
var offset = $(element).offset();
|
|
return this.slotByPosition(offset.left, offset.top);
|
|
},
|
|
resourcesBySlot: function (slot) {
|
|
return this.view()._resourceBySlot(slot);
|
|
}
|
|
});
|
|
var defaultViews = {
|
|
day: { type: 'kendo.ui.DayView' },
|
|
week: { type: 'kendo.ui.WeekView' },
|
|
workWeek: { type: 'kendo.ui.WorkWeekView' },
|
|
agenda: { type: 'kendo.ui.AgendaView' },
|
|
month: { type: 'kendo.ui.MonthView' },
|
|
timeline: { type: 'kendo.ui.TimelineView' },
|
|
timelineWeek: { type: 'kendo.ui.TimelineWeekView' },
|
|
timelineWorkWeek: { type: 'kendo.ui.TimelineWorkWeekView' },
|
|
timelineMonth: { type: 'kendo.ui.TimelineMonthView' }
|
|
};
|
|
ui.plugin(Scheduler);
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Scheduler.prototype);
|
|
var SCHEDULER_EXPORT = 'k-scheduler-pdf-export';
|
|
Scheduler.fn._drawPDF = function (progress) {
|
|
var wrapper = this.wrapper;
|
|
var styles = wrapper[0].style.cssText;
|
|
wrapper.css({
|
|
width: wrapper.width(),
|
|
height: wrapper.height()
|
|
});
|
|
wrapper.addClass(SCHEDULER_EXPORT);
|
|
var scheduler = this;
|
|
var promise = new $.Deferred();
|
|
var table = wrapper.find('.k-scheduler-content').find('table').css('table-layout', 'auto');
|
|
setTimeout(function () {
|
|
table.css('table-layout', 'fixed');
|
|
scheduler.resize(true);
|
|
scheduler._drawPDFShadow({}, { avoidLinks: scheduler.options.pdf.avoidLinks }).done(function (group) {
|
|
var args = {
|
|
page: group,
|
|
pageNumber: 1,
|
|
progress: 1,
|
|
totalPages: 1
|
|
};
|
|
progress.notify(args);
|
|
promise.resolve(args.page);
|
|
}).fail(function (err) {
|
|
promise.reject(err);
|
|
}).always(function () {
|
|
wrapper[0].style.cssText = styles;
|
|
wrapper.removeClass(SCHEDULER_EXPORT);
|
|
scheduler.resize(true);
|
|
scheduler.resize(true);
|
|
});
|
|
});
|
|
return promise;
|
|
};
|
|
}
|
|
var TimezoneEditor = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, zones = kendo.timezone.windows_zones;
|
|
if (!zones || !kendo.timezone.zones_titles) {
|
|
throw new Error('kendo.timezones.min.js is not included.');
|
|
}
|
|
Widget.fn.init.call(that, element, options);
|
|
that.wrapper = that.element;
|
|
that._zonesQuery = new kendo.data.Query(zones);
|
|
that._zoneTitleId = kendo.guid();
|
|
that._zoneTitlePicker();
|
|
that._zonePicker();
|
|
that._zoneTitle.bind('cascade', function () {
|
|
if (!this.value()) {
|
|
that._zone.wrapper.hide();
|
|
}
|
|
});
|
|
that._zone.bind('cascade', function () {
|
|
that._value = this.value();
|
|
that.trigger('change');
|
|
});
|
|
that.value(that.options.value);
|
|
},
|
|
options: {
|
|
name: 'TimezoneEditor',
|
|
value: '',
|
|
optionLabel: 'No timezone'
|
|
},
|
|
events: ['change'],
|
|
_zoneTitlePicker: function () {
|
|
var that = this, zoneTitle = $('<input id="' + that._zoneTitleId + '"/>').appendTo(that.wrapper);
|
|
that._zoneTitle = new kendo.ui.DropDownList(zoneTitle, {
|
|
dataSource: kendo.timezone.zones_titles,
|
|
dataValueField: 'other_zone',
|
|
dataTextField: 'name',
|
|
optionLabel: that.options.optionLabel
|
|
});
|
|
},
|
|
_zonePicker: function () {
|
|
var that = this, zone = $('<input />').appendTo(this.wrapper);
|
|
that._zone = new kendo.ui.DropDownList(zone, {
|
|
dataValueField: 'zone',
|
|
dataTextField: 'territory',
|
|
dataSource: that._zonesQuery.data,
|
|
cascadeFrom: that._zoneTitleId,
|
|
dataBound: function () {
|
|
that._value = this.value();
|
|
this.wrapper.toggle(this.dataSource.view().length > 1);
|
|
}
|
|
});
|
|
that._zone.wrapper.hide();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.wrapper);
|
|
},
|
|
value: function (value) {
|
|
var that = this, zone;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
zone = that._zonesQuery.filter({
|
|
field: 'zone',
|
|
operator: 'eq',
|
|
value: value
|
|
}).data[0];
|
|
if (zone) {
|
|
that._zoneTitle.value(zone.other_zone);
|
|
that._zone.value(zone.zone);
|
|
} else {
|
|
that._zoneTitle.select(0);
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(TimezoneEditor);
|
|
var ZONETITLEOPTIONTEMPLATE = kendo.template('<option value="#=other_zone#">#=name#</option>');
|
|
var ZONEOPTIONTEMPLATE = kendo.template('<option value="#=zone#">#=territory#</option>');
|
|
var MobileTimezoneEditor = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, zones = kendo.timezone.windows_zones;
|
|
if (!zones || !kendo.timezone.zones_titles) {
|
|
throw new Error('kendo.timezones.min.js is not included.');
|
|
}
|
|
Widget.fn.init.call(that, element, options);
|
|
that.wrapper = that.element;
|
|
that._zonesQuery = new kendo.data.Query(zones);
|
|
that._zoneTitlePicker();
|
|
that._zonePicker();
|
|
that.value(that.options.value);
|
|
},
|
|
options: {
|
|
name: 'MobileTimezoneEditor',
|
|
optionLabel: 'No timezone',
|
|
value: ''
|
|
},
|
|
events: ['change'],
|
|
_bindZones: function (value) {
|
|
var data = value ? this._filter(value) : [];
|
|
this._zone.html(this._options(data, ZONEOPTIONTEMPLATE));
|
|
},
|
|
_filter: function (value) {
|
|
return this._zonesQuery.filter({
|
|
field: 'other_zone',
|
|
operator: 'eq',
|
|
value: value
|
|
}).data;
|
|
},
|
|
_options: function (data, template, optionLabel) {
|
|
var idx = 0;
|
|
var html = '';
|
|
var length = data.length;
|
|
if (optionLabel) {
|
|
html += template({
|
|
other_zone: '',
|
|
name: optionLabel
|
|
});
|
|
}
|
|
for (; idx < length; idx++) {
|
|
html += template(data[idx]);
|
|
}
|
|
return html;
|
|
},
|
|
_zoneTitlePicker: function () {
|
|
var that = this;
|
|
var options = that._options(kendo.timezone.zones_titles, ZONETITLEOPTIONTEMPLATE, that.options.optionLabel);
|
|
that._zoneTitle = $('<select>' + options + '</select>').appendTo(that.wrapper).change(function () {
|
|
var value = this.value;
|
|
var zone = that._zone;
|
|
that._bindZones(value);
|
|
if (value && zone[0].children.length > 1) {
|
|
zone.show();
|
|
} else {
|
|
zone.hide();
|
|
}
|
|
that._value = zone[0].value;
|
|
that.trigger('change');
|
|
});
|
|
},
|
|
_zonePicker: function () {
|
|
var that = this;
|
|
that._zone = $('<select style="display:none"></select>').appendTo(this.wrapper).change(function () {
|
|
that._value = this.value;
|
|
that.trigger('change');
|
|
});
|
|
that._bindZones(that._zoneTitle.val());
|
|
that._value = that._zone[0].value;
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.wrapper);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var zonePicker = that._zone;
|
|
var other_zone = '';
|
|
var zone_value = '';
|
|
var zone;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
zone = that._zonesQuery.filter({
|
|
field: 'zone',
|
|
operator: 'eq',
|
|
value: value
|
|
}).data[0];
|
|
if (zone) {
|
|
zone_value = zone.zone;
|
|
other_zone = zone.other_zone;
|
|
}
|
|
that._zoneTitle.val(other_zone);
|
|
that._bindZones(other_zone);
|
|
zonePicker.val(zone_value);
|
|
zone_value = zonePicker[0].value;
|
|
if (zone_value && zonePicker[0].children.length > 1) {
|
|
zonePicker.show();
|
|
} else {
|
|
zonePicker.hide();
|
|
}
|
|
that._value = zone_value;
|
|
}
|
|
});
|
|
ui.plugin(MobileTimezoneEditor);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.touch', [
|
|
'kendo.core',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'touch',
|
|
name: 'Touch',
|
|
category: 'mobile',
|
|
description: 'The kendo Touch widget provides a cross-platform compatible API for handling user-initiated touch events, multi-touch gestures and event sequences (drag, swipe, etc.). ',
|
|
depends: [
|
|
'core',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, abs = Math.abs, MAX_DOUBLE_TAP_DISTANCE = 20;
|
|
var Touch = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element;
|
|
that.wrapper = element;
|
|
function eventProxy(name) {
|
|
return function (e) {
|
|
that._triggerTouch(name, e);
|
|
};
|
|
}
|
|
function gestureEventProxy(name) {
|
|
return function (e) {
|
|
that.trigger(name, {
|
|
touches: e.touches,
|
|
distance: e.distance,
|
|
center: e.center,
|
|
event: e.event
|
|
});
|
|
};
|
|
}
|
|
that.events = new kendo.UserEvents(element, {
|
|
filter: options.filter,
|
|
surface: options.surface,
|
|
minHold: options.minHold,
|
|
multiTouch: options.multiTouch,
|
|
allowSelection: true,
|
|
fastTap: options.fastTap,
|
|
press: eventProxy('touchstart'),
|
|
hold: eventProxy('hold'),
|
|
tap: proxy(that, '_tap'),
|
|
gesturestart: gestureEventProxy('gesturestart'),
|
|
gesturechange: gestureEventProxy('gesturechange'),
|
|
gestureend: gestureEventProxy('gestureend')
|
|
});
|
|
if (options.enableSwipe) {
|
|
that.events.bind('start', proxy(that, '_swipestart'));
|
|
that.events.bind('move', proxy(that, '_swipemove'));
|
|
} else {
|
|
that.events.bind('start', proxy(that, '_dragstart'));
|
|
that.events.bind('move', eventProxy('drag'));
|
|
that.events.bind('end', eventProxy('dragend'));
|
|
}
|
|
kendo.notify(that);
|
|
},
|
|
events: [
|
|
'touchstart',
|
|
'dragstart',
|
|
'drag',
|
|
'dragend',
|
|
'tap',
|
|
'doubletap',
|
|
'hold',
|
|
'swipe',
|
|
'gesturestart',
|
|
'gesturechange',
|
|
'gestureend'
|
|
],
|
|
options: {
|
|
name: 'Touch',
|
|
surface: null,
|
|
global: false,
|
|
fastTap: false,
|
|
filter: null,
|
|
multiTouch: false,
|
|
enableSwipe: false,
|
|
minXDelta: 30,
|
|
maxYDelta: 20,
|
|
maxDuration: 1000,
|
|
minHold: 800,
|
|
doubleTapTimeout: 800
|
|
},
|
|
cancel: function () {
|
|
this.events.cancel();
|
|
},
|
|
_triggerTouch: function (type, e) {
|
|
if (this.trigger(type, {
|
|
touch: e.touch,
|
|
event: e.event
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_tap: function (e) {
|
|
var that = this, lastTap = that.lastTap, touch = e.touch;
|
|
if (lastTap && touch.endTime - lastTap.endTime < that.options.doubleTapTimeout && kendo.touchDelta(touch, lastTap).distance < MAX_DOUBLE_TAP_DISTANCE) {
|
|
that._triggerTouch('doubletap', e);
|
|
that.lastTap = null;
|
|
} else {
|
|
that._triggerTouch('tap', e);
|
|
that.lastTap = touch;
|
|
}
|
|
},
|
|
_dragstart: function (e) {
|
|
this._triggerTouch('dragstart', e);
|
|
},
|
|
_swipestart: function (e) {
|
|
if (abs(e.x.velocity) * 2 >= abs(e.y.velocity)) {
|
|
e.sender.capture();
|
|
}
|
|
},
|
|
_swipemove: function (e) {
|
|
var that = this, options = that.options, touch = e.touch, duration = e.event.timeStamp - touch.startTime, direction = touch.x.initialDelta > 0 ? 'right' : 'left';
|
|
if (abs(touch.x.initialDelta) >= options.minXDelta && abs(touch.y.initialDelta) < options.maxYDelta && duration < options.maxDuration) {
|
|
that.trigger('swipe', {
|
|
direction: direction,
|
|
touch: e.touch
|
|
});
|
|
touch.cancel();
|
|
}
|
|
}
|
|
});
|
|
kendo.ui.plugin(Touch);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.gantt.list', [
|
|
'kendo.dom',
|
|
'kendo.touch',
|
|
'kendo.draganddrop',
|
|
'kendo.columnsorter',
|
|
'kendo.datetimepicker',
|
|
'kendo.editable'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'gantt.list',
|
|
name: 'Gantt List',
|
|
category: 'web',
|
|
description: 'The Gantt List',
|
|
depends: [
|
|
'dom',
|
|
'touch',
|
|
'draganddrop',
|
|
'columnsorter',
|
|
'datetimepicker',
|
|
'editable'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var kendo = window.kendo;
|
|
var kendoDom = kendo.dom;
|
|
var kendoDomElement = kendoDom.element;
|
|
var kendoTextElement = kendoDom.text;
|
|
var browser = kendo.support.browser;
|
|
var mobileOS = kendo.support.mobileOS;
|
|
var ui = kendo.ui;
|
|
var Widget = ui.Widget;
|
|
var extend = $.extend;
|
|
var map = $.map;
|
|
var isFunction = $.isFunction;
|
|
var oldIE = browser.msie && browser.version < 9;
|
|
var keys = kendo.keys;
|
|
var titleFromField = {
|
|
'title': 'Title',
|
|
'start': 'Start Time',
|
|
'end': 'End Time',
|
|
'percentComplete': '% Done',
|
|
'parentId': 'Predecessor ID',
|
|
'id': 'ID',
|
|
'orderId': 'Order ID'
|
|
};
|
|
var STRING = 'string';
|
|
var NS = '.kendoGanttList';
|
|
var CLICK = 'click';
|
|
var DOT = '.';
|
|
var SIZE_CALCULATION_TEMPLATE = '<table style=\'visibility: hidden;\'>' + '<tbody>' + '<tr style=\'height:{0}\'>' + '<td> </td>' + '</tr>' + '</tbody>' + '</table>';
|
|
var listStyles = {
|
|
wrapper: 'k-treelist k-grid k-widget',
|
|
header: 'k-header',
|
|
alt: 'k-alt',
|
|
rtl: 'k-rtl',
|
|
editCell: 'k-edit-cell',
|
|
group: 'k-treelist-group',
|
|
gridHeader: 'k-grid-header',
|
|
gridHeaderWrap: 'k-grid-header-wrap',
|
|
gridContent: 'k-grid-content',
|
|
gridContentWrap: 'k-grid-content',
|
|
selected: 'k-state-selected',
|
|
icon: 'k-icon',
|
|
iconCollapse: 'k-i-collapse',
|
|
iconExpand: 'k-i-expand',
|
|
iconHidden: 'k-i-none',
|
|
iconPlaceHolder: 'k-icon k-i-none',
|
|
input: 'k-input',
|
|
link: 'k-link',
|
|
resizeHandle: 'k-resize-handle',
|
|
resizeHandleInner: 'k-resize-handle-inner',
|
|
dropPositions: 'k-insert-top k-insert-bottom k-add k-insert-middle',
|
|
dropTop: 'k-insert-top',
|
|
dropBottom: 'k-insert-bottom',
|
|
dropAdd: 'k-add',
|
|
dropMiddle: 'k-insert-middle',
|
|
dropDenied: 'k-denied',
|
|
dragStatus: 'k-drag-status',
|
|
dragClue: 'k-drag-clue',
|
|
dragClueText: 'k-clue-text'
|
|
};
|
|
function createPlaceholders(options) {
|
|
var spans = [];
|
|
var className = options.className;
|
|
for (var i = 0, level = options.level; i < level; i++) {
|
|
spans.push(kendoDomElement('span', { className: className }));
|
|
}
|
|
return spans;
|
|
}
|
|
function blurActiveElement() {
|
|
var activeElement = kendo._activeElement();
|
|
if (activeElement.nodeName.toLowerCase() !== 'body') {
|
|
$(activeElement).blur();
|
|
}
|
|
}
|
|
var GanttList = ui.GanttList = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
if (this.options.columns.length === 0) {
|
|
this.options.columns.push('title');
|
|
}
|
|
this.dataSource = this.options.dataSource;
|
|
this._columns();
|
|
this._layout();
|
|
this._domTrees();
|
|
this._header();
|
|
this._sortable();
|
|
this._editable();
|
|
this._selectable();
|
|
this._draggable();
|
|
this._resizable();
|
|
this._attachEvents();
|
|
this._adjustHeight();
|
|
this.bind('render', function () {
|
|
var headerCols;
|
|
var tableCols;
|
|
if (this.options.resizable) {
|
|
headerCols = this.header.find('col');
|
|
tableCols = this.content.find('col');
|
|
this.header.find('th').not(':last').each(function (index) {
|
|
var width = $(this).outerWidth();
|
|
headerCols.eq(index).width(width);
|
|
tableCols.eq(index).width(width);
|
|
});
|
|
headerCols.last().css('width', 'auto');
|
|
tableCols.last().css('width', 'auto');
|
|
}
|
|
}, true);
|
|
},
|
|
_adjustHeight: function () {
|
|
this.content.height(this.element.height() - this.header.parent().outerHeight());
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
if (this._reorderDraggable) {
|
|
this._reorderDraggable.destroy();
|
|
}
|
|
if (this._tableDropArea) {
|
|
this._tableDropArea.destroy();
|
|
}
|
|
if (this._contentDropArea) {
|
|
this._contentDropArea.destroy();
|
|
}
|
|
if (this._columnResizable) {
|
|
this._columnResizable.destroy();
|
|
}
|
|
if (this.touch) {
|
|
this.touch.destroy();
|
|
}
|
|
if (this.timer) {
|
|
clearTimeout(this.timer);
|
|
}
|
|
this.content.off(NS);
|
|
this.header.find('thead').off(NS);
|
|
this.header.find(DOT + GanttList.link).off(NS);
|
|
this.header = null;
|
|
this.content = null;
|
|
this.levels = null;
|
|
kendo.destroy(this.element);
|
|
},
|
|
options: {
|
|
name: 'GanttList',
|
|
selectable: true,
|
|
editable: true,
|
|
resizable: false
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this;
|
|
var listStyles = GanttList.styles;
|
|
that.content.on(CLICK + NS, 'td > span.' + listStyles.icon + ':not(.' + listStyles.iconHidden + ')', function (e) {
|
|
var element = $(this);
|
|
var model = that._modelFromElement(element);
|
|
model.set('expanded', !model.get('expanded'));
|
|
e.stopPropagation();
|
|
});
|
|
},
|
|
_domTrees: function () {
|
|
this.headerTree = new kendoDom.Tree(this.header[0]);
|
|
this.contentTree = new kendoDom.Tree(this.content[0]);
|
|
},
|
|
_columns: function () {
|
|
var columns = this.options.columns;
|
|
var model = function () {
|
|
this.field = '';
|
|
this.title = '';
|
|
this.editable = false;
|
|
this.sortable = false;
|
|
};
|
|
this.columns = map(columns, function (column) {
|
|
column = typeof column === STRING ? {
|
|
field: column,
|
|
title: titleFromField[column]
|
|
} : column;
|
|
return extend(new model(), column);
|
|
});
|
|
},
|
|
_layout: function () {
|
|
var that = this;
|
|
var options = this.options;
|
|
var element = this.element;
|
|
var listStyles = GanttList.styles;
|
|
var calculateRowHeight = function () {
|
|
var rowHeight = typeof options.rowHeight === STRING ? options.rowHeight : options.rowHeight + 'px';
|
|
var table = $(kendo.format(SIZE_CALCULATION_TEMPLATE, rowHeight));
|
|
var height;
|
|
that.content.append(table);
|
|
height = table.find('tr').outerHeight();
|
|
table.remove();
|
|
return height;
|
|
};
|
|
element.addClass(listStyles.wrapper).append('<div class=\'' + listStyles.gridHeader + '\'><div class=\'' + listStyles.gridHeaderWrap + '\'></div></div>').append('<div class=\'' + listStyles.gridContentWrap + '\'></div>');
|
|
this.header = element.find(DOT + listStyles.gridHeaderWrap);
|
|
this.content = element.find(DOT + listStyles.gridContent);
|
|
if (options.rowHeight) {
|
|
this._rowHeight = calculateRowHeight();
|
|
}
|
|
},
|
|
_header: function () {
|
|
var domTree = this.headerTree;
|
|
var colgroup;
|
|
var thead;
|
|
var table;
|
|
colgroup = kendoDomElement('colgroup', null, this._cols());
|
|
thead = kendoDomElement('thead', { 'role': 'rowgroup' }, [kendoDomElement('tr', { 'role': 'row' }, this._ths())]);
|
|
table = kendoDomElement('table', {
|
|
'style': { 'minWidth': this.options.listWidth + 'px' },
|
|
'role': 'grid'
|
|
}, [
|
|
colgroup,
|
|
thead
|
|
]);
|
|
domTree.render([table]);
|
|
},
|
|
_render: function (tasks) {
|
|
var colgroup;
|
|
var tbody;
|
|
var table;
|
|
var tableAttr = {
|
|
'style': { 'minWidth': this.options.listWidth + 'px' },
|
|
'tabIndex': 0,
|
|
'role': 'treegrid'
|
|
};
|
|
if (this._rowHeight) {
|
|
tableAttr.style.height = tasks.length * this._rowHeight + 'px';
|
|
}
|
|
this.levels = [{
|
|
field: null,
|
|
value: 0
|
|
}];
|
|
colgroup = kendoDomElement('colgroup', null, this._cols());
|
|
tbody = kendoDomElement('tbody', { 'role': 'rowgroup' }, this._trs(tasks));
|
|
table = kendoDomElement('table', tableAttr, [
|
|
colgroup,
|
|
tbody
|
|
]);
|
|
this.contentTree.render([table]);
|
|
this.trigger('render');
|
|
},
|
|
_ths: function () {
|
|
var columns = this.columns;
|
|
var column;
|
|
var attr;
|
|
var ths = [];
|
|
for (var i = 0, length = columns.length; i < length; i++) {
|
|
column = columns[i];
|
|
attr = {
|
|
'data-field': column.field,
|
|
'data-title': column.title,
|
|
className: GanttList.styles.header,
|
|
'role': 'columnheader'
|
|
};
|
|
ths.push(kendoDomElement('th', attr, [kendoTextElement(column.title)]));
|
|
}
|
|
if (this.options.resizable) {
|
|
ths.push(kendoDomElement('th', {
|
|
className: GanttList.styles.header,
|
|
'role': 'columnheader'
|
|
}));
|
|
}
|
|
return ths;
|
|
},
|
|
_cols: function () {
|
|
var columns = this.columns;
|
|
var column;
|
|
var style;
|
|
var width;
|
|
var cols = [];
|
|
for (var i = 0, length = columns.length; i < length; i++) {
|
|
column = columns[i];
|
|
width = column.width;
|
|
if (width && parseInt(width, 10) !== 0) {
|
|
style = { style: { width: typeof width === STRING ? width : width + 'px' } };
|
|
} else {
|
|
style = null;
|
|
}
|
|
cols.push(kendoDomElement('col', style, []));
|
|
}
|
|
if (this.options.resizable) {
|
|
cols.push(kendoDomElement('col', { style: { width: '1px' } }));
|
|
}
|
|
return cols;
|
|
},
|
|
_trs: function (tasks) {
|
|
var task;
|
|
var rows = [];
|
|
var attr;
|
|
var className = [];
|
|
var level;
|
|
var listStyles = GanttList.styles;
|
|
for (var i = 0, length = tasks.length; i < length; i++) {
|
|
task = tasks[i];
|
|
level = this._levels({
|
|
idx: task.parentId,
|
|
id: task.id,
|
|
summary: task.summary
|
|
});
|
|
attr = {
|
|
'data-uid': task.uid,
|
|
'data-level': level,
|
|
'role': 'row'
|
|
};
|
|
if (task.summary) {
|
|
attr['aria-expanded'] = task.expanded;
|
|
}
|
|
if (i % 2 !== 0) {
|
|
className.push(listStyles.alt);
|
|
}
|
|
if (task.summary) {
|
|
className.push(listStyles.group);
|
|
}
|
|
if (className.length) {
|
|
attr.className = className.join(' ');
|
|
}
|
|
rows.push(this._tds({
|
|
task: task,
|
|
attr: attr,
|
|
level: level
|
|
}));
|
|
className = [];
|
|
}
|
|
return rows;
|
|
},
|
|
_tds: function (options) {
|
|
var children = [];
|
|
var columns = this.columns;
|
|
var column;
|
|
for (var i = 0, l = columns.length; i < l; i++) {
|
|
column = columns[i];
|
|
children.push(this._td({
|
|
task: options.task,
|
|
column: column,
|
|
level: options.level
|
|
}));
|
|
}
|
|
if (this.options.resizable) {
|
|
children.push(kendoDomElement('td', { 'role': 'gridcell' }));
|
|
}
|
|
return kendoDomElement('tr', options.attr, children);
|
|
},
|
|
_td: function (options) {
|
|
var children = [];
|
|
var resourcesField = this.options.resourcesField;
|
|
var listStyles = GanttList.styles;
|
|
var task = options.task;
|
|
var column = options.column;
|
|
var value = task.get(column.field);
|
|
var formatedValue;
|
|
var label;
|
|
if (column.field == resourcesField) {
|
|
value = value || [];
|
|
formatedValue = [];
|
|
for (var i = 0; i < value.length; i++) {
|
|
formatedValue.push(kendo.format('{0} [{1}]', value[i].get('name'), value[i].get('formatedValue')));
|
|
}
|
|
formatedValue = formatedValue.join(', ');
|
|
} else {
|
|
formatedValue = column.format ? kendo.format(column.format, value) : value;
|
|
}
|
|
if (column.field === 'title') {
|
|
children = createPlaceholders({
|
|
level: options.level,
|
|
className: listStyles.iconPlaceHolder
|
|
});
|
|
children.push(kendoDomElement('span', { className: listStyles.icon + ' ' + (task.summary ? task.expanded ? listStyles.iconCollapse : listStyles.iconExpand : listStyles.iconHidden) }));
|
|
label = kendo.format('{0}, {1:P0}', formatedValue, task.percentComplete);
|
|
}
|
|
children.push(kendoDomElement('span', { 'aria-label': label }, [kendoTextElement(formatedValue)]));
|
|
return kendoDomElement('td', { 'role': 'gridcell' }, children);
|
|
},
|
|
_levels: function (options) {
|
|
var levels = this.levels;
|
|
var level;
|
|
var summary = options.summary;
|
|
var idx = options.idx;
|
|
var id = options.id;
|
|
for (var i = 0, length = levels.length; i < length; i++) {
|
|
level = levels[i];
|
|
if (level.field == idx) {
|
|
if (summary) {
|
|
levels.push({
|
|
field: id,
|
|
value: level.value + 1
|
|
});
|
|
}
|
|
return level.value;
|
|
}
|
|
}
|
|
},
|
|
_sortable: function () {
|
|
var that = this;
|
|
var resourcesField = this.options.resourcesField;
|
|
var columns = this.columns;
|
|
var column;
|
|
var sortableInstance;
|
|
var cells = this.header.find('th[' + kendo.attr('field') + ']');
|
|
var handler = function (e) {
|
|
if (that.dataSource.total() === 0 || that.editable && that.editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
}
|
|
};
|
|
var cell;
|
|
for (var idx = 0, length = cells.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.sortable && column.field !== resourcesField) {
|
|
cell = cells.eq(idx);
|
|
sortableInstance = cell.data('kendoColumnSorter');
|
|
if (sortableInstance) {
|
|
sortableInstance.destroy();
|
|
}
|
|
cell.attr('data-' + kendo.ns + 'field', column.field).kendoColumnSorter({ dataSource: this.dataSource }).find(DOT + GanttList.styles.link).on('click' + NS, handler);
|
|
}
|
|
}
|
|
cells = null;
|
|
},
|
|
_selectable: function () {
|
|
var that = this;
|
|
var selectable = this.options.selectable;
|
|
if (selectable) {
|
|
this.content.on(CLICK + NS, 'tr', function (e) {
|
|
var element = $(this);
|
|
if (that.editable) {
|
|
that.editable.trigger('validate');
|
|
}
|
|
if (!e.ctrlKey) {
|
|
that.select(element);
|
|
} else {
|
|
that.clearSelection();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
select: function (value) {
|
|
var element = this.content.find(value);
|
|
var selectedClassName = GanttList.styles.selected;
|
|
if (element.length) {
|
|
element.siblings(DOT + selectedClassName).removeClass(selectedClassName).attr('aria-selected', false).end().addClass(selectedClassName).attr('aria-selected', true);
|
|
this.trigger('change');
|
|
return;
|
|
}
|
|
return this.content.find(DOT + selectedClassName);
|
|
},
|
|
clearSelection: function () {
|
|
var selected = this.select();
|
|
if (selected.length) {
|
|
selected.removeClass(GanttList.styles.selected);
|
|
this.trigger('change');
|
|
}
|
|
},
|
|
_setDataSource: function (dataSource) {
|
|
this.dataSource = dataSource;
|
|
},
|
|
_editable: function () {
|
|
var that = this;
|
|
var listStyles = GanttList.styles;
|
|
var iconSelector = 'span.' + listStyles.icon + ':not(' + listStyles.iconHidden + ')';
|
|
var finishEdit = function () {
|
|
var editable = that.editable;
|
|
if (editable) {
|
|
if (editable.end()) {
|
|
that._closeCell();
|
|
} else {
|
|
editable.trigger('validate');
|
|
}
|
|
}
|
|
};
|
|
var mousedown = function (e) {
|
|
var currentTarget = $(e.currentTarget);
|
|
if (!currentTarget.hasClass(listStyles.editCell)) {
|
|
blurActiveElement();
|
|
}
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this._startEditHandler = function (e) {
|
|
var td = e.currentTarget ? $(e.currentTarget) : e;
|
|
var column = that._columnFromElement(td);
|
|
if (that.editable) {
|
|
return;
|
|
}
|
|
if (column && column.editable) {
|
|
that._editCell({
|
|
cell: td,
|
|
column: column
|
|
});
|
|
}
|
|
};
|
|
that.content.on('focusin' + NS, function () {
|
|
clearTimeout(that.timer);
|
|
that.timer = null;
|
|
}).on('focusout' + NS, function () {
|
|
that.timer = setTimeout(finishEdit, 1);
|
|
}).on('keydown' + NS, function (e) {
|
|
if (e.keyCode === keys.ENTER) {
|
|
e.preventDefault();
|
|
}
|
|
}).on('keyup' + NS, function (e) {
|
|
var key = e.keyCode;
|
|
var cell;
|
|
var model;
|
|
switch (key) {
|
|
case keys.ENTER:
|
|
blurActiveElement();
|
|
finishEdit();
|
|
break;
|
|
case keys.ESC:
|
|
if (that.editable) {
|
|
cell = that._editableContainer;
|
|
model = that._modelFromElement(cell);
|
|
if (!that.trigger('cancel', {
|
|
model: model,
|
|
cell: cell
|
|
})) {
|
|
that._closeCell(true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
if (!mobileOS) {
|
|
that.content.on('mousedown' + NS, 'td', function (e) {
|
|
mousedown(e);
|
|
}).on('dblclick' + NS, 'td', function (e) {
|
|
if (!$(e.target).is(iconSelector)) {
|
|
that._startEditHandler(e);
|
|
}
|
|
});
|
|
} else {
|
|
that.touch = that.content.kendoTouch({
|
|
filter: 'td',
|
|
touchstart: function (e) {
|
|
mousedown(e.touch);
|
|
},
|
|
doubletap: function (e) {
|
|
if (!$(e.touch.initialTouch).is(iconSelector)) {
|
|
that._startEditHandler(e.touch);
|
|
}
|
|
}
|
|
}).data('kendoTouch');
|
|
}
|
|
},
|
|
_editCell: function (options) {
|
|
var resourcesField = this.options.resourcesField;
|
|
var listStyles = GanttList.styles;
|
|
var cell = options.cell;
|
|
var column = options.column;
|
|
var model = this._modelFromElement(cell);
|
|
var modelCopy = this.dataSource._createNewModel(model.toJSON());
|
|
var field = modelCopy.fields[column.field] || modelCopy[column.field];
|
|
var validation = field.validation;
|
|
var DATATYPE = kendo.attr('type');
|
|
var BINDING = kendo.attr('bind');
|
|
var FORMAT = kendo.attr('format');
|
|
var attr = {
|
|
'name': column.field,
|
|
'required': field.validation ? field.validation.required === true : false
|
|
};
|
|
var editor;
|
|
if (column.field === resourcesField) {
|
|
column.editor(cell, modelCopy);
|
|
return;
|
|
}
|
|
this._editableContent = cell.children().detach();
|
|
this._editableContainer = cell;
|
|
cell.data('modelCopy', modelCopy);
|
|
if ((field.type === 'date' || $.type(field) === 'date') && (!column.format || /H|m|s|F|g|u/.test(column.format))) {
|
|
attr[BINDING] = 'value:' + column.field;
|
|
attr[DATATYPE] = 'date';
|
|
if (column.format) {
|
|
attr[FORMAT] = kendo._extractFormat(column.format);
|
|
}
|
|
editor = function (container, options) {
|
|
$('<input type="text"/>').attr(attr).appendTo(container).kendoDateTimePicker({ format: options.format });
|
|
};
|
|
}
|
|
this.editable = cell.addClass(listStyles.editCell).kendoEditable({
|
|
fields: {
|
|
field: column.field,
|
|
format: column.format,
|
|
editor: column.editor || editor
|
|
},
|
|
model: modelCopy,
|
|
clearContainer: false
|
|
}).data('kendoEditable');
|
|
if (validation && validation.dateCompare && isFunction(validation.dateCompare) && validation.message) {
|
|
$('<span ' + kendo.attr('for') + '="' + column.field + '" class="k-invalid-msg"/>').hide().appendTo(cell);
|
|
cell.find('[name=' + column.field + ']').attr(kendo.attr('dateCompare-msg'), validation.message);
|
|
}
|
|
this.editable.bind('validate', function (e) {
|
|
var focusable = this.element.find(':kendoFocusable:first').focus();
|
|
if (oldIE) {
|
|
focusable.focus();
|
|
}
|
|
e.preventDefault();
|
|
});
|
|
if (this.trigger('edit', {
|
|
model: model,
|
|
cell: cell
|
|
})) {
|
|
this._closeCell(true);
|
|
}
|
|
},
|
|
_closeCell: function (cancelUpdate) {
|
|
var listStyles = GanttList.styles;
|
|
var cell = this._editableContainer;
|
|
var model = this._modelFromElement(cell);
|
|
var column = this._columnFromElement(cell);
|
|
var field = column.field;
|
|
var copy = cell.data('modelCopy');
|
|
var taskInfo = {};
|
|
taskInfo[field] = copy.get(field);
|
|
cell.empty().removeData('modelCopy').removeClass(listStyles.editCell).append(this._editableContent);
|
|
this.editable.unbind();
|
|
this.editable.destroy();
|
|
this.editable = null;
|
|
this._editableContainer = null;
|
|
this._editableContent = null;
|
|
if (!cancelUpdate) {
|
|
if (field === 'start') {
|
|
taskInfo.end = new Date(taskInfo.start.getTime() + model.duration());
|
|
}
|
|
this.trigger('update', {
|
|
task: model,
|
|
updateInfo: taskInfo
|
|
});
|
|
}
|
|
},
|
|
_draggable: function () {
|
|
var that = this;
|
|
var draggedTask = null;
|
|
var dropAllowed = true;
|
|
var dropTarget;
|
|
var listStyles = GanttList.styles;
|
|
var isRtl = kendo.support.isRtl(this.element);
|
|
var selector = 'tr[' + kendo.attr('level') + ' = 0]:last';
|
|
var action = {};
|
|
var clear = function () {
|
|
draggedTask = null;
|
|
dropTarget = null;
|
|
dropAllowed = true;
|
|
action = {};
|
|
};
|
|
var allowDrop = function (task) {
|
|
var parent = task;
|
|
while (parent) {
|
|
if (draggedTask.get('id') === parent.get('id')) {
|
|
dropAllowed = false;
|
|
break;
|
|
}
|
|
parent = that.dataSource.taskParent(parent);
|
|
}
|
|
};
|
|
var defineLimits = function () {
|
|
var height = $(dropTarget).height();
|
|
var offsetTop = kendo.getOffset(dropTarget).top;
|
|
extend(dropTarget, {
|
|
beforeLimit: offsetTop + height * 0.25,
|
|
afterLimit: offsetTop + height * 0.75
|
|
});
|
|
};
|
|
var defineAction = function (coordinate) {
|
|
if (!dropTarget) {
|
|
return;
|
|
}
|
|
var location = coordinate.location;
|
|
var className = listStyles.dropAdd;
|
|
var command = 'add';
|
|
var level = parseInt(dropTarget.attr(kendo.attr('level')), 10);
|
|
var sibling;
|
|
if (location <= dropTarget.beforeLimit) {
|
|
sibling = dropTarget.prev();
|
|
className = listStyles.dropTop;
|
|
command = 'insert-before';
|
|
} else if (location >= dropTarget.afterLimit) {
|
|
sibling = dropTarget.next();
|
|
className = listStyles.dropBottom;
|
|
command = 'insert-after';
|
|
}
|
|
if (sibling && parseInt(sibling.attr(kendo.attr('level')), 10) === level) {
|
|
className = listStyles.dropMiddle;
|
|
}
|
|
action.className = className;
|
|
action.command = command;
|
|
};
|
|
var status = function () {
|
|
return that._reorderDraggable.hint.children(DOT + listStyles.dragStatus).removeClass(listStyles.dropPositions);
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this._reorderDraggable = this.content.kendoDraggable({
|
|
distance: 10,
|
|
holdToDrag: mobileOS,
|
|
group: 'listGroup',
|
|
filter: 'tr[data-uid]',
|
|
ignore: DOT + listStyles.input,
|
|
hint: function (target) {
|
|
return $('<div class="' + listStyles.header + ' ' + listStyles.dragClue + '"/>').css({
|
|
width: 300,
|
|
paddingLeft: target.css('paddingLeft'),
|
|
paddingRight: target.css('paddingRight'),
|
|
lineHeight: target.height() + 'px',
|
|
paddingTop: target.css('paddingTop'),
|
|
paddingBottom: target.css('paddingBottom')
|
|
}).append('<span class="' + listStyles.icon + ' ' + listStyles.dragStatus + '" /><span class="' + listStyles.dragClueText + '"/>');
|
|
},
|
|
cursorOffset: {
|
|
top: -20,
|
|
left: 0
|
|
},
|
|
container: this.content,
|
|
'dragstart': function (e) {
|
|
if (that.editable && that.editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
draggedTask = that._modelFromElement(e.currentTarget);
|
|
this.hint.children(DOT + listStyles.dragClueText).text(draggedTask.get('title'));
|
|
if (isRtl) {
|
|
this.hint.addClass(listStyles.rtl);
|
|
}
|
|
},
|
|
'drag': function (e) {
|
|
if (dropAllowed) {
|
|
defineAction(e.y);
|
|
status().addClass(action.className);
|
|
}
|
|
},
|
|
'dragend': function () {
|
|
clear();
|
|
},
|
|
'dragcancel': function () {
|
|
clear();
|
|
}
|
|
}).data('kendoDraggable');
|
|
this._tableDropArea = this.content.kendoDropTargetArea({
|
|
distance: 0,
|
|
group: 'listGroup',
|
|
filter: 'tr[data-uid]',
|
|
'dragenter': function (e) {
|
|
dropTarget = e.dropTarget;
|
|
allowDrop(that._modelFromElement(dropTarget));
|
|
defineLimits();
|
|
status().toggleClass(listStyles.dropDenied, !dropAllowed);
|
|
},
|
|
'dragleave': function () {
|
|
dropAllowed = true;
|
|
status();
|
|
},
|
|
'drop': function () {
|
|
var target = that._modelFromElement(dropTarget);
|
|
var orderId = target.orderId;
|
|
var taskInfo = { parentId: target.parentId };
|
|
if (dropAllowed) {
|
|
switch (action.command) {
|
|
case 'add':
|
|
taskInfo.parentId = target.id;
|
|
break;
|
|
case 'insert-before':
|
|
if (target.parentId === draggedTask.parentId && target.orderId > draggedTask.orderId) {
|
|
taskInfo.orderId = orderId - 1;
|
|
} else {
|
|
taskInfo.orderId = orderId;
|
|
}
|
|
break;
|
|
case 'insert-after':
|
|
if (target.parentId === draggedTask.parentId && target.orderId > draggedTask.orderId) {
|
|
taskInfo.orderId = orderId;
|
|
} else {
|
|
taskInfo.orderId = orderId + 1;
|
|
}
|
|
break;
|
|
}
|
|
that.trigger('update', {
|
|
task: draggedTask,
|
|
updateInfo: taskInfo
|
|
});
|
|
}
|
|
}
|
|
}).data('kendoDropTargetArea');
|
|
this._contentDropArea = this.element.kendoDropTargetArea({
|
|
distance: 0,
|
|
group: 'listGroup',
|
|
filter: DOT + listStyles.gridContent,
|
|
'drop': function () {
|
|
var target = that._modelFromElement(that.content.find(selector));
|
|
var orderId = target.orderId;
|
|
var taskInfo = {
|
|
parentId: null,
|
|
orderId: draggedTask.parentId !== null ? orderId + 1 : orderId
|
|
};
|
|
that.trigger('update', {
|
|
task: draggedTask,
|
|
updateInfo: taskInfo
|
|
});
|
|
}
|
|
}).data('kendoDropTargetArea');
|
|
},
|
|
_resizable: function () {
|
|
var that = this;
|
|
var listStyles = GanttList.styles;
|
|
var positionResizeHandle = function (e) {
|
|
var th = $(e.currentTarget);
|
|
var resizeHandle = that.resizeHandle;
|
|
var position = th.position();
|
|
var left = position.left;
|
|
var cellWidth = th.outerWidth();
|
|
var container = th.closest('div');
|
|
var clientX = e.clientX + $(window).scrollLeft();
|
|
var indicatorWidth = that.options.columnResizeHandleWidth;
|
|
left += container.scrollLeft();
|
|
if (!resizeHandle) {
|
|
resizeHandle = that.resizeHandle = $('<div class="' + listStyles.resizeHandle + '"><div class="' + listStyles.resizeHandleInner + '" /></div>');
|
|
}
|
|
var cellOffset = th.offset().left + cellWidth;
|
|
var show = clientX > cellOffset - indicatorWidth && clientX < cellOffset + indicatorWidth;
|
|
if (!show) {
|
|
resizeHandle.hide();
|
|
return;
|
|
}
|
|
container.append(resizeHandle);
|
|
resizeHandle.show().css({
|
|
top: position.top,
|
|
left: left + cellWidth - indicatorWidth - 1,
|
|
height: th.outerHeight(),
|
|
width: indicatorWidth * 3
|
|
}).data('th', th);
|
|
};
|
|
if (!this.options.resizable) {
|
|
return;
|
|
}
|
|
if (this._columnResizable) {
|
|
this._columnResizable.destroy();
|
|
}
|
|
this.header.find('thead').on('mousemove' + NS, 'th', positionResizeHandle);
|
|
this._columnResizable = this.header.kendoResizable({
|
|
handle: DOT + listStyles.resizeHandle,
|
|
start: function (e) {
|
|
var th = $(e.currentTarget).data('th');
|
|
var colSelector = 'col:eq(' + th.index() + ')';
|
|
var header = that.header.find('table');
|
|
var contentTable = that.content.find('table');
|
|
that.element.addClass('k-grid-column-resizing');
|
|
this.col = contentTable.children('colgroup').find(colSelector).add(header.find(colSelector));
|
|
this.th = th;
|
|
this.startLocation = e.x.location;
|
|
this.columnWidth = th.outerWidth();
|
|
this.table = header.add(contentTable);
|
|
this.totalWidth = this.table.width() - header.find('th:last').outerWidth();
|
|
},
|
|
resize: function (e) {
|
|
var minColumnWidth = 11;
|
|
var delta = e.x.location - this.startLocation;
|
|
if (this.columnWidth + delta < minColumnWidth) {
|
|
delta = minColumnWidth - this.columnWidth;
|
|
}
|
|
this.table.css({ 'minWidth': this.totalWidth + delta });
|
|
this.col.width(this.columnWidth + delta);
|
|
},
|
|
resizeend: function () {
|
|
that.element.removeClass('k-grid-column-resizing');
|
|
var oldWidth = Math.floor(this.columnWidth);
|
|
var newWidth = Math.floor(this.th.outerWidth());
|
|
var column = that.columns[this.th.index()];
|
|
that.trigger('columnResize', {
|
|
column: column,
|
|
oldWidth: oldWidth,
|
|
newWidth: newWidth
|
|
});
|
|
this.table = this.col = this.th = null;
|
|
}
|
|
}).data('kendoResizable');
|
|
},
|
|
_modelFromElement: function (element) {
|
|
var row = element.closest('tr');
|
|
var model = this.dataSource.getByUid(row.attr(kendo.attr('uid')));
|
|
return model;
|
|
},
|
|
_columnFromElement: function (element) {
|
|
var td = element.closest('td');
|
|
var tr = td.parent();
|
|
var idx = tr.children().index(td);
|
|
return this.columns[idx];
|
|
}
|
|
});
|
|
extend(true, ui.GanttList, { styles: listStyles });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.gantt.timeline', [
|
|
'kendo.dom',
|
|
'kendo.touch',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'gantt.timeline',
|
|
name: 'Gantt Timeline',
|
|
category: 'web',
|
|
description: 'The Gantt Timeline',
|
|
depends: [
|
|
'dom',
|
|
'touch',
|
|
'draganddrop'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var Widget = kendo.ui.Widget;
|
|
var kendoDomElement = kendo.dom.element;
|
|
var kendoTextElement = kendo.dom.text;
|
|
var kendoHtmlElement = kendo.dom.html;
|
|
var isPlainObject = $.isPlainObject;
|
|
var extend = $.extend;
|
|
var proxy = $.proxy;
|
|
var browser = kendo.support.browser;
|
|
var isRtl = false;
|
|
var keys = kendo.keys;
|
|
var Query = kendo.data.Query;
|
|
var STRING = 'string';
|
|
var NS = '.kendoGanttTimeline';
|
|
var CLICK = 'click';
|
|
var DBLCLICK = 'dblclick';
|
|
var MOUSEMOVE = 'mousemove';
|
|
var MOUSEENTER = 'mouseenter';
|
|
var MOUSELEAVE = 'mouseleave';
|
|
var KEYDOWN = 'keydown';
|
|
var DOT = '.';
|
|
var TIME_HEADER_TEMPLATE = kendo.template('#=kendo.toString(start, \'t\')#');
|
|
var DAY_HEADER_TEMPLATE = kendo.template('#=kendo.toString(start, \'ddd M/dd\')#');
|
|
var WEEK_HEADER_TEMPLATE = kendo.template('#=kendo.toString(start, \'ddd M/dd\')# - #=kendo.toString(kendo.date.addDays(end, -1), \'ddd M/dd\')#');
|
|
var MONTH_HEADER_TEMPLATE = kendo.template('#=kendo.toString(start, \'MMM\')#');
|
|
var YEAR_HEADER_TEMPLATE = kendo.template('#=kendo.toString(start, \'yyyy\')#');
|
|
var RESIZE_HINT = kendo.template('<div class="#=styles.marquee#">' + '<div class="#=styles.marqueeColor#"></div>' + '</div>');
|
|
var RESIZE_TOOLTIP_TEMPLATE = kendo.template('<div style="z-index: 100002;" class="#=styles.tooltipWrapper#">' + '<div class="#=styles.tooltipContent#">' + '<div>#=messages.start#: #=kendo.toString(start, format)#</div>' + '<div>#=messages.end#: #=kendo.toString(end, format)#</div>' + '</div>' + '</div>');
|
|
var PERCENT_RESIZE_TOOLTIP_TEMPLATE = kendo.template('<div style="z-index: 100002;" class="#=styles.tooltipWrapper#" >' + '<div class="#=styles.tooltipContent#">#=text#%</div>' + '<div class="#=styles.tooltipCallout#" style="left:13px;"></div>' + '</div>');
|
|
var TASK_TOOLTIP_TEMPLATE = kendo.template('<div class="#=styles.taskDetails#">' + '<strong>#=task.title#</strong>' + '<div class="#=styles.taskDetailsPercent#">#=kendo.toString(task.percentComplete, "p0")#</div>' + '<ul class="#=styles.reset#">' + '<li>#=messages.start#: #=kendo.toString(task.start, "h:mm tt ddd, MMM d")#</li>' + '<li>#=messages.end#: #=kendo.toString(task.end, "h:mm tt ddd, MMM d")#</li>' + '</ul>' + '</div>');
|
|
var SIZE_CALCULATION_TEMPLATE = '<table style=\'visibility: hidden;\'>' + '<tbody>' + '<tr style=\'height:{0}\'>' + '<td> </td>' + '</tr>' + '</tbody>' + '</table>';
|
|
var defaultViews = {
|
|
day: { type: 'kendo.ui.GanttDayView' },
|
|
week: { type: 'kendo.ui.GanttWeekView' },
|
|
month: { type: 'kendo.ui.GanttMonthView' },
|
|
year: { type: 'kendo.ui.GanttYearView' }
|
|
};
|
|
function trimOptions(options) {
|
|
delete options.name;
|
|
delete options.prefix;
|
|
delete options.views;
|
|
return options;
|
|
}
|
|
function getWorkDays(options) {
|
|
var workDays = [];
|
|
var dayIndex = options.workWeekStart;
|
|
workDays.push(dayIndex);
|
|
while (options.workWeekEnd != dayIndex) {
|
|
if (dayIndex > 6) {
|
|
dayIndex -= 7;
|
|
} else {
|
|
dayIndex++;
|
|
}
|
|
workDays.push(dayIndex);
|
|
}
|
|
return workDays;
|
|
}
|
|
function blurActiveElement() {
|
|
var activeElement = kendo._activeElement();
|
|
if (activeElement && activeElement.nodeName.toLowerCase() !== 'body') {
|
|
$(activeElement).blur();
|
|
}
|
|
}
|
|
var viewStyles = {
|
|
alt: 'k-alt',
|
|
reset: 'k-reset',
|
|
nonWorking: 'k-nonwork-hour',
|
|
header: 'k-header',
|
|
gridHeader: 'k-grid-header',
|
|
gridHeaderWrap: 'k-grid-header-wrap',
|
|
gridContent: 'k-grid-content',
|
|
tasksWrapper: 'k-gantt-tables',
|
|
rowsTable: 'k-gantt-rows',
|
|
columnsTable: 'k-gantt-columns',
|
|
tasksTable: 'k-gantt-tasks',
|
|
dependenciesWrapper: 'k-gantt-dependencies',
|
|
resource: 'k-resource',
|
|
resourceAlt: 'k-resource k-alt',
|
|
task: 'k-task',
|
|
taskSingle: 'k-task-single',
|
|
taskMilestone: 'k-task-milestone',
|
|
taskSummary: 'k-task-summary',
|
|
taskWrap: 'k-task-wrap',
|
|
taskMilestoneWrap: 'k-milestone-wrap',
|
|
resourcesWrap: 'k-resources-wrap',
|
|
taskDot: 'k-task-dot',
|
|
taskDotStart: 'k-task-start',
|
|
taskDotEnd: 'k-task-end',
|
|
taskDragHandle: 'k-task-draghandle',
|
|
taskContent: 'k-task-content',
|
|
taskTemplate: 'k-task-template',
|
|
taskActions: 'k-task-actions',
|
|
taskDelete: 'k-task-delete',
|
|
taskComplete: 'k-task-complete',
|
|
taskDetails: 'k-task-details',
|
|
taskDetailsPercent: 'k-task-pct',
|
|
link: 'k-link',
|
|
icon: 'k-icon',
|
|
iconDelete: 'k-si-close',
|
|
taskResizeHandle: 'k-resize-handle',
|
|
taskResizeHandleWest: 'k-resize-w',
|
|
taskResizeHandleEast: 'k-resize-e',
|
|
taskSummaryProgress: 'k-task-summary-progress',
|
|
taskSummaryComplete: 'k-task-summary-complete',
|
|
line: 'k-line',
|
|
lineHorizontal: 'k-line-h',
|
|
lineVertical: 'k-line-v',
|
|
arrowWest: 'k-arrow-w',
|
|
arrowEast: 'k-arrow-e',
|
|
dragHint: 'k-drag-hint',
|
|
dependencyHint: 'k-dependency-hint',
|
|
tooltipWrapper: 'k-widget k-tooltip k-popup k-group k-reset',
|
|
tooltipContent: 'k-tooltip-content',
|
|
tooltipCallout: 'k-callout k-callout-s',
|
|
callout: 'k-callout',
|
|
marquee: 'k-marquee k-gantt-marquee',
|
|
marqueeColor: 'k-marquee-color'
|
|
};
|
|
var GanttView = kendo.ui.GanttView = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.title = this.options.title || this.options.name;
|
|
this.header = this.element.find(DOT + GanttView.styles.gridHeader);
|
|
this.content = this.element.find(DOT + GanttView.styles.gridContent);
|
|
this.contentWidth = this.content.width();
|
|
this._workDays = getWorkDays(this.options);
|
|
this._headerTree = options.headerTree;
|
|
this._taskTree = options.taskTree;
|
|
this._taskTemplate = options.taskTemplate ? kendo.template(options.taskTemplate, extend({}, kendo.Template, options.templateSettings)) : null;
|
|
this._dependencyTree = options.dependencyTree;
|
|
this._taskCoordinates = {};
|
|
this._currentTime();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
clearTimeout(this._tooltipTimeout);
|
|
this.headerRow = null;
|
|
this.header = null;
|
|
this.content = null;
|
|
this._dragHint = null;
|
|
this._resizeHint = null;
|
|
this._resizeTooltip = null;
|
|
this._taskTooltip = null;
|
|
this._percentCompleteResizeTooltip = null;
|
|
this._headerTree = null;
|
|
this._taskTree = null;
|
|
this._dependencyTree = null;
|
|
},
|
|
options: {
|
|
showWorkHours: false,
|
|
showWorkDays: false,
|
|
workDayStart: new Date(1980, 1, 1, 8, 0, 0),
|
|
workDayEnd: new Date(1980, 1, 1, 17, 0, 0),
|
|
workWeekStart: 1,
|
|
workWeekEnd: 5,
|
|
hourSpan: 1,
|
|
slotSize: 100,
|
|
currentTimeMarker: { updateInterval: 10000 }
|
|
},
|
|
renderLayout: function () {
|
|
this._slots = this._createSlots();
|
|
this._tableWidth = this._calculateTableWidth();
|
|
this.createLayout(this._layout());
|
|
this._slotDimensions();
|
|
this._adjustHeight();
|
|
this.content.find(DOT + GanttView.styles.dependenciesWrapper).width(this._tableWidth);
|
|
},
|
|
_adjustHeight: function () {
|
|
this.content.height(this.element.height() - this.header.outerHeight());
|
|
},
|
|
createLayout: function (rows) {
|
|
var headers = this._headers(rows);
|
|
var colgroup = this._colgroup();
|
|
var tree = this._headerTree;
|
|
var header = kendoDomElement('thead', null, headers);
|
|
var table = kendoDomElement('table', { style: { width: this._tableWidth + 'px' } }, [
|
|
colgroup,
|
|
header
|
|
]);
|
|
tree.render([table]);
|
|
this.headerRow = this.header.find('table:first tr').last();
|
|
},
|
|
_slotDimensions: function () {
|
|
var headers = this.headerRow[0].children;
|
|
var slots = this._timeSlots();
|
|
var slot;
|
|
var header;
|
|
for (var i = 0, length = headers.length; i < length; i++) {
|
|
header = headers[i];
|
|
slot = slots[i];
|
|
slot.offsetLeft = header.offsetLeft;
|
|
slot.offsetWidth = header.offsetWidth;
|
|
}
|
|
},
|
|
render: function (tasks) {
|
|
var taskCount = tasks.length;
|
|
var styles = GanttView.styles;
|
|
var contentTable;
|
|
var rowsTable = this._rowsTable(taskCount);
|
|
var columnsTable = this._columnsTable(taskCount);
|
|
var tasksTable = this._tasksTable(tasks);
|
|
var currentTimeMarker = this.options.currentTimeMarker;
|
|
var calculatedSize = this.options.calculatedSize;
|
|
var totalHeight;
|
|
this._taskTree.render([
|
|
rowsTable,
|
|
columnsTable,
|
|
tasksTable
|
|
]);
|
|
contentTable = this.content.find(DOT + styles.rowsTable);
|
|
if (calculatedSize) {
|
|
totalHeight = calculatedSize.row * tasks.length;
|
|
this.content.find(DOT + styles.tasksTable).height(totalHeight);
|
|
contentTable.height(totalHeight);
|
|
}
|
|
this._contentHeight = contentTable.height();
|
|
this._rowHeight = calculatedSize ? calculatedSize.row : this._contentHeight / contentTable.find('tr').length;
|
|
this.content.find(DOT + styles.columnsTable).height(this._contentHeight);
|
|
if (currentTimeMarker !== false && currentTimeMarker.updateInterval !== undefined) {
|
|
this._renderCurrentTime();
|
|
}
|
|
},
|
|
_rowsTable: function (rowCount) {
|
|
var rows = [];
|
|
var row;
|
|
var styles = GanttView.styles;
|
|
var attributes = [
|
|
null,
|
|
{ className: styles.alt }
|
|
];
|
|
for (var i = 0; i < rowCount; i++) {
|
|
row = kendoDomElement('tr', attributes[i % 2], [kendoDomElement('td', null, [kendoTextElement('\xA0')])]);
|
|
rows.push(row);
|
|
}
|
|
return this._createTable(1, rows, { className: styles.rowsTable });
|
|
},
|
|
_columnsTable: function () {
|
|
var cells = [];
|
|
var row;
|
|
var styles = GanttView.styles;
|
|
var slots = this._timeSlots();
|
|
var slotsCount = slots.length;
|
|
var slot;
|
|
var slotSpan;
|
|
var totalSpan = 0;
|
|
var attributes;
|
|
for (var i = 0; i < slotsCount; i++) {
|
|
slot = slots[i];
|
|
attributes = {};
|
|
slotSpan = slot.span;
|
|
totalSpan += slotSpan;
|
|
if (slotSpan !== 1) {
|
|
attributes.colspan = slotSpan;
|
|
}
|
|
if (slot.isNonWorking) {
|
|
attributes.className = styles.nonWorking;
|
|
}
|
|
cells.push(kendoDomElement('td', attributes, [kendoTextElement('\xA0')]));
|
|
}
|
|
row = kendoDomElement('tr', null, cells);
|
|
return this._createTable(totalSpan, [row], { className: styles.columnsTable });
|
|
},
|
|
_tasksTable: function (tasks) {
|
|
var rows = [];
|
|
var row;
|
|
var cell;
|
|
var position;
|
|
var task;
|
|
var styles = GanttView.styles;
|
|
var coordinates = this._taskCoordinates = {};
|
|
var size = this._calculateMilestoneWidth();
|
|
var milestoneWidth = Math.round(size.width);
|
|
var resourcesField = this.options.resourcesField;
|
|
var className = [
|
|
styles.resource,
|
|
styles.resourceAlt
|
|
];
|
|
var calculatedSize = this.options.calculatedSize;
|
|
var resourcesPosition;
|
|
var resourcesMargin = this._calculateResourcesMargin();
|
|
var taskBorderWidth = this._calculateTaskBorderWidth();
|
|
var resourceStyle;
|
|
var addCoordinates = function (rowIndex) {
|
|
var taskLeft;
|
|
var taskRight;
|
|
taskLeft = position.left;
|
|
taskRight = taskLeft + position.width;
|
|
if (task.isMilestone()) {
|
|
taskLeft -= milestoneWidth / 2;
|
|
taskRight = taskLeft + milestoneWidth;
|
|
}
|
|
coordinates[task.id] = {
|
|
start: taskLeft,
|
|
end: taskRight,
|
|
rowIndex: rowIndex
|
|
};
|
|
};
|
|
for (var i = 0, l = tasks.length; i < l; i++) {
|
|
task = tasks[i];
|
|
position = this._taskPosition(task);
|
|
position.borderWidth = taskBorderWidth;
|
|
row = kendoDomElement('tr', null);
|
|
cell = kendoDomElement('td', null, [this._renderTask(tasks[i], position)]);
|
|
if (task[resourcesField] && task[resourcesField].length) {
|
|
if (isRtl) {
|
|
resourcesPosition = this._tableWidth - position.left;
|
|
} else {
|
|
resourcesPosition = Math.max(position.width || size.clientWidth, 0) + position.left;
|
|
}
|
|
resourceStyle = { width: this._tableWidth - (resourcesPosition + resourcesMargin) + 'px' };
|
|
resourceStyle[isRtl ? 'right' : 'left'] = resourcesPosition + 'px';
|
|
if (calculatedSize) {
|
|
resourceStyle.height = calculatedSize.cell + 'px';
|
|
}
|
|
cell.children.push(kendoDomElement('div', {
|
|
className: styles.resourcesWrap,
|
|
style: resourceStyle
|
|
}, this._renderResources(task[resourcesField], className[i % 2])));
|
|
}
|
|
row.children.push(cell);
|
|
rows.push(row);
|
|
addCoordinates(i);
|
|
}
|
|
return this._createTable(1, rows, { className: GanttView.styles.tasksTable });
|
|
},
|
|
_createTable: function (colspan, rows, styles) {
|
|
var cols = [];
|
|
var colgroup;
|
|
var tbody;
|
|
for (var i = 0; i < colspan; i++) {
|
|
cols.push(kendoDomElement('col'));
|
|
}
|
|
colgroup = kendoDomElement('colgroup', null, cols);
|
|
tbody = kendoDomElement('tbody', null, rows);
|
|
if (!styles.style) {
|
|
styles.style = {};
|
|
}
|
|
styles.style.width = this._tableWidth + 'px';
|
|
return kendoDomElement('table', styles, [
|
|
colgroup,
|
|
tbody
|
|
]);
|
|
},
|
|
_calculateTableWidth: function () {
|
|
var slots = this._timeSlots();
|
|
var maxSpan = 0;
|
|
var totalSpan = 0;
|
|
var currentSpan;
|
|
var tableWidth;
|
|
for (var i = 0, length = slots.length; i < length; i++) {
|
|
currentSpan = slots[i].span;
|
|
totalSpan += currentSpan;
|
|
if (currentSpan > maxSpan) {
|
|
maxSpan = currentSpan;
|
|
}
|
|
}
|
|
tableWidth = Math.round(totalSpan * this.options.slotSize / maxSpan);
|
|
return tableWidth;
|
|
},
|
|
_calculateMilestoneWidth: function () {
|
|
var size;
|
|
var className = GanttView.styles.task + ' ' + GanttView.styles.taskMilestone;
|
|
var milestone = $('<div class=\'' + className + '\' style=\'visibility: hidden; position: absolute\'>');
|
|
var boundingClientRect;
|
|
this.content.append(milestone);
|
|
boundingClientRect = milestone[0].getBoundingClientRect();
|
|
size = {
|
|
'width': boundingClientRect.right - boundingClientRect.left,
|
|
'clientWidth': milestone[0].clientWidth
|
|
};
|
|
milestone.remove();
|
|
return size;
|
|
},
|
|
_calculateResourcesMargin: function () {
|
|
var margin;
|
|
var wrapper = $('<div class=\'' + GanttView.styles.resourcesWrap + '\' style=\'visibility: hidden; position: absolute\'>');
|
|
this.content.append(wrapper);
|
|
margin = parseInt(wrapper.css(isRtl ? 'margin-right' : 'margin-left'), 10);
|
|
wrapper.remove();
|
|
return margin;
|
|
},
|
|
_calculateTaskBorderWidth: function () {
|
|
var width;
|
|
var className = GanttView.styles.task + ' ' + GanttView.styles.taskSingle;
|
|
var task = $('<div class=\'' + className + '\' style=\'visibility: hidden; position: absolute\'>');
|
|
var computedStyle;
|
|
this.content.append(task);
|
|
computedStyle = kendo.getComputedStyles(task[0], ['border-left-width']);
|
|
width = parseFloat(computedStyle['border-left-width'], 10);
|
|
task.remove();
|
|
return width;
|
|
},
|
|
_renderTask: function (task, position) {
|
|
var taskWrapper;
|
|
var taskElement;
|
|
var editable = this.options.editable;
|
|
var progressHandleOffset;
|
|
var taskLeft = position.left;
|
|
var styles = GanttView.styles;
|
|
var wrapClassName = styles.taskWrap;
|
|
var calculatedSize = this.options.calculatedSize;
|
|
var dragHandleStyle = {};
|
|
var taskWrapAttr = {
|
|
className: wrapClassName,
|
|
style: { left: taskLeft + 'px' }
|
|
};
|
|
if (calculatedSize) {
|
|
taskWrapAttr.style.height = calculatedSize.cell + 'px';
|
|
}
|
|
if (task.summary) {
|
|
taskElement = this._renderSummary(task, position);
|
|
} else if (task.isMilestone()) {
|
|
taskElement = this._renderMilestone(task, position);
|
|
taskWrapAttr.className += ' ' + styles.taskMilestoneWrap;
|
|
} else {
|
|
taskElement = this._renderSingleTask(task, position);
|
|
}
|
|
taskWrapper = kendoDomElement('div', taskWrapAttr, [taskElement]);
|
|
if (editable) {
|
|
taskWrapper.children.push(kendoDomElement('div', { className: styles.taskDot + ' ' + styles.taskDotStart }));
|
|
taskWrapper.children.push(kendoDomElement('div', { className: styles.taskDot + ' ' + styles.taskDotEnd }));
|
|
}
|
|
if (!task.summary && !task.isMilestone() && editable && this._taskTemplate === null) {
|
|
progressHandleOffset = Math.round(position.width * task.percentComplete);
|
|
dragHandleStyle[isRtl ? 'right' : 'left'] = progressHandleOffset + 'px';
|
|
taskWrapper.children.push(kendoDomElement('div', {
|
|
className: styles.taskDragHandle,
|
|
style: dragHandleStyle
|
|
}));
|
|
}
|
|
return taskWrapper;
|
|
},
|
|
_renderSingleTask: function (task, position) {
|
|
var styles = GanttView.styles;
|
|
var progressWidth = Math.round(position.width * task.percentComplete);
|
|
var taskChildren = [];
|
|
var taskContent;
|
|
if (this._taskTemplate !== null) {
|
|
taskContent = kendoHtmlElement(this._taskTemplate(task));
|
|
} else {
|
|
taskContent = kendoTextElement(task.title);
|
|
taskChildren.push(kendoDomElement('div', {
|
|
className: styles.taskComplete,
|
|
style: { width: progressWidth + 'px' }
|
|
}));
|
|
}
|
|
var content = kendoDomElement('div', { className: styles.taskContent }, [kendoDomElement('div', { className: styles.taskTemplate }, [taskContent])]);
|
|
taskChildren.push(content);
|
|
if (this.options.editable) {
|
|
content.children.push(kendoDomElement('span', { className: styles.taskActions }, [kendoDomElement('a', {
|
|
className: styles.link + ' ' + styles.taskDelete,
|
|
href: '#'
|
|
}, [kendoDomElement('span', { className: styles.icon + ' ' + styles.iconDelete })])]));
|
|
content.children.push(kendoDomElement('span', { className: styles.taskResizeHandle + ' ' + styles.taskResizeHandleWest }));
|
|
content.children.push(kendoDomElement('span', { className: styles.taskResizeHandle + ' ' + styles.taskResizeHandleEast }));
|
|
}
|
|
var element = kendoDomElement('div', {
|
|
className: styles.task + ' ' + styles.taskSingle,
|
|
'data-uid': task.uid,
|
|
style: { width: Math.max(position.width - position.borderWidth * 2, 0) + 'px' }
|
|
}, taskChildren);
|
|
return element;
|
|
},
|
|
_renderMilestone: function (task) {
|
|
var styles = GanttView.styles;
|
|
var element = kendoDomElement('div', {
|
|
className: styles.task + ' ' + styles.taskMilestone,
|
|
'data-uid': task.uid
|
|
});
|
|
return element;
|
|
},
|
|
_renderSummary: function (task, position) {
|
|
var styles = GanttView.styles;
|
|
var progressWidth = Math.round(position.width * task.percentComplete);
|
|
var element = kendoDomElement('div', {
|
|
className: styles.task + ' ' + styles.taskSummary,
|
|
'data-uid': task.uid,
|
|
style: { width: position.width + 'px' }
|
|
}, [kendoDomElement('div', {
|
|
className: styles.taskSummaryProgress,
|
|
style: { width: progressWidth + 'px' }
|
|
}, [kendoDomElement('div', {
|
|
className: styles.taskSummaryComplete,
|
|
style: { width: position.width + 'px' }
|
|
})])]);
|
|
return element;
|
|
},
|
|
_renderResources: function (resources, className) {
|
|
var children = [];
|
|
var resource;
|
|
for (var i = 0, length = resources.length; i < length; i++) {
|
|
resource = resources[i];
|
|
children.push(kendoDomElement('span', {
|
|
className: className,
|
|
style: { 'color': resource.get('color') }
|
|
}, [kendoTextElement(resource.get('name'))]));
|
|
}
|
|
if (isRtl) {
|
|
children.reverse();
|
|
}
|
|
return children;
|
|
},
|
|
_taskPosition: function (task) {
|
|
var round = Math.round;
|
|
var startLeft = round(this._offset(isRtl ? task.end : task.start));
|
|
var endLeft = round(this._offset(isRtl ? task.start : task.end));
|
|
return {
|
|
left: startLeft,
|
|
width: endLeft - startLeft
|
|
};
|
|
},
|
|
_offset: function (date) {
|
|
var slots = this._timeSlots();
|
|
var slot;
|
|
var startOffset;
|
|
var slotDuration;
|
|
var slotOffset = 0;
|
|
var startIndex;
|
|
if (!slots.length) {
|
|
return 0;
|
|
}
|
|
startIndex = this._slotIndex('start', date);
|
|
slot = slots[startIndex];
|
|
if (slot.end < date) {
|
|
slotOffset = slot.offsetWidth;
|
|
} else if (slot.start <= date) {
|
|
startOffset = date - slot.start;
|
|
slotDuration = slot.end - slot.start;
|
|
slotOffset = startOffset / slotDuration * slot.offsetWidth;
|
|
}
|
|
if (isRtl) {
|
|
slotOffset = slot.offsetWidth + 1 - slotOffset;
|
|
}
|
|
return slot.offsetLeft + slotOffset;
|
|
},
|
|
_slotIndex: function (field, value, reverse) {
|
|
var slots = this._timeSlots();
|
|
var startIdx = 0;
|
|
var endIdx = slots.length - 1;
|
|
var middle;
|
|
if (reverse) {
|
|
slots = [].slice.call(slots).reverse();
|
|
}
|
|
do {
|
|
middle = Math.ceil((endIdx + startIdx) / 2);
|
|
if (slots[middle][field] < value) {
|
|
startIdx = middle;
|
|
} else {
|
|
if (middle === endIdx) {
|
|
middle--;
|
|
}
|
|
endIdx = middle;
|
|
}
|
|
} while (startIdx !== endIdx);
|
|
if (reverse) {
|
|
startIdx = slots.length - 1 - startIdx;
|
|
}
|
|
return startIdx;
|
|
},
|
|
_timeByPosition: function (x, snap, snapToEnd) {
|
|
var slot = this._slotByPosition(x);
|
|
if (snap) {
|
|
return snapToEnd ? slot.end : slot.start;
|
|
}
|
|
var offsetLeft = x - $(DOT + GanttView.styles.tasksTable).offset().left;
|
|
var duration = slot.end - slot.start;
|
|
var slotOffset = offsetLeft - slot.offsetLeft;
|
|
if (isRtl) {
|
|
slotOffset = slot.offsetWidth - slotOffset;
|
|
}
|
|
return new Date(slot.start.getTime() + duration * (slotOffset / slot.offsetWidth));
|
|
},
|
|
_slotByPosition: function (x) {
|
|
var offsetLeft = x - $(DOT + GanttView.styles.tasksTable).offset().left;
|
|
var slotIndex = this._slotIndex('offsetLeft', offsetLeft, isRtl);
|
|
return this._timeSlots()[slotIndex];
|
|
},
|
|
_renderDependencies: function (dependencies) {
|
|
var elements = [];
|
|
var tree = this._dependencyTree;
|
|
for (var i = 0, l = dependencies.length; i < l; i++) {
|
|
elements.push.apply(elements, this._renderDependency(dependencies[i]));
|
|
}
|
|
tree.render(elements);
|
|
},
|
|
_renderDependency: function (dependency) {
|
|
var predecessor = this._taskCoordinates[dependency.predecessorId];
|
|
var successor = this._taskCoordinates[dependency.successorId];
|
|
var elements;
|
|
var method;
|
|
if (!predecessor || !successor) {
|
|
return [];
|
|
}
|
|
method = '_render' + [
|
|
'FF',
|
|
'FS',
|
|
'SF',
|
|
'SS'
|
|
][isRtl ? 3 - dependency.type : dependency.type];
|
|
elements = this[method](predecessor, successor);
|
|
for (var i = 0, length = elements.length; i < length; i++) {
|
|
elements[i].attr['data-uid'] = dependency.uid;
|
|
}
|
|
return elements;
|
|
},
|
|
_renderFF: function (from, to) {
|
|
var lines = this._dependencyFF(from, to, false);
|
|
lines[lines.length - 1].children[0] = this._arrow(true);
|
|
return lines;
|
|
},
|
|
_renderSS: function (from, to) {
|
|
var lines = this._dependencyFF(to, from, true);
|
|
lines[0].children[0] = this._arrow(false);
|
|
return lines.reverse();
|
|
},
|
|
_renderFS: function (from, to) {
|
|
var lines = this._dependencyFS(from, to, false);
|
|
lines[lines.length - 1].children[0] = this._arrow(false);
|
|
return lines;
|
|
},
|
|
_renderSF: function (from, to) {
|
|
var lines = this._dependencyFS(to, from, true);
|
|
lines[0].children[0] = this._arrow(true);
|
|
return lines.reverse();
|
|
},
|
|
_dependencyFF: function (from, to, reverse) {
|
|
var that = this;
|
|
var lines = [];
|
|
var left = 0;
|
|
var top = 0;
|
|
var width = 0;
|
|
var height = 0;
|
|
var dir = reverse ? 'start' : 'end';
|
|
var delta;
|
|
var overlap = 2;
|
|
var arrowOverlap = 1;
|
|
var rowHeight = this._rowHeight;
|
|
var minLineWidth = 10;
|
|
var fromTop = from.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
|
|
var toTop = to.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
|
|
var styles = GanttView.styles;
|
|
var addHorizontal = function () {
|
|
lines.push(that._line(styles.line + ' ' + styles.lineHorizontal, {
|
|
left: left + 'px',
|
|
top: top + 'px',
|
|
width: width + 'px'
|
|
}));
|
|
};
|
|
var addVertical = function () {
|
|
lines.push(that._line(styles.line + ' ' + styles.lineVertical, {
|
|
left: left + 'px',
|
|
top: top + 'px',
|
|
height: height + 'px'
|
|
}));
|
|
};
|
|
left = from[dir];
|
|
top = fromTop;
|
|
width = minLineWidth;
|
|
delta = to[dir] - from[dir];
|
|
if (delta > 0 !== reverse) {
|
|
width = Math.abs(delta) + minLineWidth;
|
|
}
|
|
if (reverse) {
|
|
left -= width;
|
|
width -= arrowOverlap;
|
|
addHorizontal();
|
|
} else {
|
|
addHorizontal();
|
|
left += width - overlap;
|
|
}
|
|
if (toTop < top) {
|
|
height = top - toTop;
|
|
height += overlap;
|
|
top = toTop;
|
|
addVertical();
|
|
} else {
|
|
height = toTop - top;
|
|
height += overlap;
|
|
addVertical();
|
|
top += height - overlap;
|
|
}
|
|
width = Math.abs(left - to[dir]);
|
|
if (!reverse) {
|
|
width -= arrowOverlap;
|
|
left -= width;
|
|
}
|
|
addHorizontal();
|
|
return lines;
|
|
},
|
|
_dependencyFS: function (from, to, reverse) {
|
|
var that = this;
|
|
var lines = [];
|
|
var left = 0;
|
|
var top = 0;
|
|
var width = 0;
|
|
var height = 0;
|
|
var rowHeight = this._rowHeight;
|
|
var minLineHeight = Math.floor(rowHeight / 2);
|
|
var minLineWidth = 10;
|
|
var minDistance = 2 * minLineWidth;
|
|
var delta = to.start - from.end;
|
|
var overlap = 2;
|
|
var arrowOverlap = 1;
|
|
var fromTop = from.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
|
|
var toTop = to.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
|
|
var styles = GanttView.styles;
|
|
var addHorizontal = function () {
|
|
lines.push(that._line(styles.line + ' ' + styles.lineHorizontal, {
|
|
left: left + 'px',
|
|
top: top + 'px',
|
|
width: width + 'px'
|
|
}));
|
|
};
|
|
var addVertical = function () {
|
|
lines.push(that._line(styles.line + ' ' + styles.lineVertical, {
|
|
left: left + 'px',
|
|
top: top + 'px',
|
|
height: height + 'px'
|
|
}));
|
|
};
|
|
left = from.end;
|
|
top = fromTop;
|
|
width = minLineWidth;
|
|
if (reverse) {
|
|
left += arrowOverlap;
|
|
if (delta > minDistance) {
|
|
width = delta - (minLineWidth - overlap);
|
|
}
|
|
width -= arrowOverlap;
|
|
}
|
|
addHorizontal();
|
|
left += width - overlap;
|
|
if (delta <= minDistance) {
|
|
height = reverse ? Math.abs(toTop - fromTop) - minLineHeight : minLineHeight;
|
|
if (toTop < fromTop) {
|
|
top -= height;
|
|
height += overlap;
|
|
addVertical();
|
|
} else {
|
|
addVertical();
|
|
top += height;
|
|
}
|
|
width = from.end - to.start + minDistance;
|
|
if (width < minLineWidth) {
|
|
width = minLineWidth;
|
|
}
|
|
left -= width - overlap;
|
|
addHorizontal();
|
|
}
|
|
if (toTop < fromTop) {
|
|
height = top - toTop;
|
|
top = toTop;
|
|
height += overlap;
|
|
addVertical();
|
|
} else {
|
|
height = toTop - top;
|
|
addVertical();
|
|
top += height;
|
|
}
|
|
width = to.start - left;
|
|
if (!reverse) {
|
|
width -= arrowOverlap;
|
|
}
|
|
addHorizontal();
|
|
return lines;
|
|
},
|
|
_line: function (className, styles) {
|
|
return kendoDomElement('div', {
|
|
className: className,
|
|
style: styles
|
|
});
|
|
},
|
|
_arrow: function (direction) {
|
|
return kendoDomElement('span', { className: direction ? GanttView.styles.arrowWest : GanttView.styles.arrowEast });
|
|
},
|
|
_colgroup: function () {
|
|
var slots = this._timeSlots();
|
|
var count = slots.length;
|
|
var cols = [];
|
|
for (var i = 0; i < count; i++) {
|
|
for (var j = 0, length = slots[i].span; j < length; j++) {
|
|
cols.push(kendoDomElement('col'));
|
|
}
|
|
}
|
|
return kendoDomElement('colgroup', null, cols);
|
|
},
|
|
_createDragHint: function (element) {
|
|
this._dragHint = element.clone().addClass(GanttView.styles.dragHint).css('cursor', 'move');
|
|
element.parent().append(this._dragHint);
|
|
},
|
|
_updateDragHint: function (start) {
|
|
var left = this._offset(start);
|
|
this._dragHint.css({ 'left': left });
|
|
},
|
|
_removeDragHint: function () {
|
|
this._dragHint.remove();
|
|
this._dragHint = null;
|
|
},
|
|
_createResizeHint: function (task) {
|
|
var styles = GanttView.styles;
|
|
var taskTop = this._taskCoordinates[task.id].rowIndex * this._rowHeight;
|
|
var tooltipHeight;
|
|
var tooltipTop;
|
|
var options = this.options;
|
|
var messages = options.messages;
|
|
this._resizeHint = $(RESIZE_HINT({ styles: styles })).css({
|
|
'top': 0,
|
|
'height': this._contentHeight
|
|
});
|
|
this.content.append(this._resizeHint);
|
|
this._resizeTooltip = $(RESIZE_TOOLTIP_TEMPLATE({
|
|
styles: styles,
|
|
start: task.start,
|
|
end: task.end,
|
|
messages: messages.views,
|
|
format: options.resizeTooltipFormat
|
|
})).css({
|
|
'top': 0,
|
|
'left': 0
|
|
});
|
|
this.content.append(this._resizeTooltip);
|
|
this._resizeTooltipWidth = this._resizeTooltip.outerWidth();
|
|
tooltipHeight = this._resizeTooltip.outerHeight();
|
|
tooltipTop = taskTop - tooltipHeight;
|
|
if (tooltipTop < 0) {
|
|
tooltipTop = taskTop + this._rowHeight;
|
|
}
|
|
this._resizeTooltipTop = tooltipTop;
|
|
},
|
|
_updateResizeHint: function (start, end, resizeStart) {
|
|
var left = this._offset(isRtl ? end : start);
|
|
var right = this._offset(isRtl ? start : end);
|
|
var width = right - left;
|
|
var tooltipLeft = resizeStart !== isRtl ? left : right;
|
|
var tablesWidth = this._tableWidth - kendo.support.scrollbar();
|
|
var tooltipWidth = this._resizeTooltipWidth;
|
|
var options = this.options;
|
|
var messages = options.messages;
|
|
var tableOffset = $(DOT + GanttView.styles.tasksTable).offset().left - $(DOT + GanttView.styles.tasksWrapper).offset().left;
|
|
if (isRtl) {
|
|
left += tableOffset;
|
|
}
|
|
this._resizeHint.css({
|
|
'left': left,
|
|
'width': width
|
|
});
|
|
if (this._resizeTooltip) {
|
|
this._resizeTooltip.remove();
|
|
}
|
|
tooltipLeft -= Math.round(tooltipWidth / 2);
|
|
if (tooltipLeft < 0) {
|
|
tooltipLeft = 0;
|
|
} else if (tooltipLeft + tooltipWidth > tablesWidth) {
|
|
tooltipLeft = tablesWidth - tooltipWidth;
|
|
}
|
|
if (isRtl) {
|
|
tooltipLeft += tableOffset;
|
|
}
|
|
this._resizeTooltip = $(RESIZE_TOOLTIP_TEMPLATE({
|
|
styles: GanttView.styles,
|
|
start: start,
|
|
end: end,
|
|
messages: messages.views,
|
|
format: options.resizeTooltipFormat
|
|
})).css({
|
|
'top': this._resizeTooltipTop,
|
|
'left': tooltipLeft,
|
|
'min-width': tooltipWidth
|
|
}).appendTo(this.content);
|
|
},
|
|
_removeResizeHint: function () {
|
|
this._resizeHint.remove();
|
|
this._resizeHint = null;
|
|
this._resizeTooltip.remove();
|
|
this._resizeTooltip = null;
|
|
},
|
|
_updatePercentCompleteTooltip: function (top, left, text) {
|
|
this._removePercentCompleteTooltip();
|
|
var tooltip = this._percentCompleteResizeTooltip = $(PERCENT_RESIZE_TOOLTIP_TEMPLATE({
|
|
styles: GanttView.styles,
|
|
text: text
|
|
})).appendTo(this.element);
|
|
var tooltipMiddle = Math.round(tooltip.outerWidth() / 2);
|
|
var arrow = tooltip.find(DOT + GanttView.styles.callout);
|
|
var arrowHeight = Math.round(arrow.outerWidth() / 2);
|
|
tooltip.css({
|
|
'top': top - (tooltip.outerHeight() + arrowHeight),
|
|
'left': left - tooltipMiddle
|
|
});
|
|
arrow.css('left', tooltipMiddle - arrowHeight);
|
|
},
|
|
_removePercentCompleteTooltip: function () {
|
|
if (this._percentCompleteResizeTooltip) {
|
|
this._percentCompleteResizeTooltip.remove();
|
|
}
|
|
this._percentCompleteResizeTooltip = null;
|
|
},
|
|
_updateDependencyDragHint: function (from, to, useVML) {
|
|
this._removeDependencyDragHint();
|
|
if (useVML) {
|
|
this._creteVmlDependencyDragHint(from, to);
|
|
} else {
|
|
this._creteDependencyDragHint(from, to);
|
|
}
|
|
},
|
|
_creteDependencyDragHint: function (from, to) {
|
|
var styles = GanttView.styles;
|
|
var deltaX = to.x - from.x;
|
|
var deltaY = to.y - from.y;
|
|
var width = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
var angle = Math.atan(deltaY / deltaX);
|
|
if (deltaX < 0) {
|
|
angle += Math.PI;
|
|
}
|
|
$('<div class=\'' + styles.line + ' ' + styles.lineHorizontal + ' ' + styles.dependencyHint + '\'></div>').css({
|
|
'top': from.y,
|
|
'left': from.x,
|
|
'width': width,
|
|
'transform-origin': '0% 0',
|
|
'-ms-transform-origin': '0% 0',
|
|
'-webkit-transform-origin': '0% 0',
|
|
'transform': 'rotate(' + angle + 'rad)',
|
|
'-ms-transform': 'rotate(' + angle + 'rad)',
|
|
'-webkit-transform': 'rotate(' + angle + 'rad)'
|
|
}).appendTo(this.content);
|
|
},
|
|
_creteVmlDependencyDragHint: function (from, to) {
|
|
var hint = $('<kvml:line class=\'' + GanttView.styles.dependencyHint + '\' style=\'position:absolute; top: 0px; left: 0px;\' strokecolor=\'black\' strokeweight=\'2px\' from=\'' + from.x + 'px,' + from.y + 'px\' to=\'' + to.x + 'px,' + to.y + 'px\'' + '></kvml:line>').appendTo(this.content);
|
|
hint[0].outerHTML = hint[0].outerHTML;
|
|
},
|
|
_removeDependencyDragHint: function () {
|
|
this.content.find(DOT + GanttView.styles.dependencyHint).remove();
|
|
},
|
|
_createTaskTooltip: function (task, element, mouseLeft) {
|
|
var styles = GanttView.styles;
|
|
var options = this.options;
|
|
var content = this.content;
|
|
var contentOffset = content.offset();
|
|
var contentWidth = content.width();
|
|
var contentScrollLeft = kendo.scrollLeft(content);
|
|
var row = $(element).parents('tr').first();
|
|
var rowOffset = row.offset();
|
|
var template = options.tooltip && options.tooltip.template ? kendo.template(options.tooltip.template) : TASK_TOOLTIP_TEMPLATE;
|
|
var left = isRtl ? mouseLeft - (contentOffset.left + contentScrollLeft + kendo.support.scrollbar()) : mouseLeft - (contentOffset.left - contentScrollLeft);
|
|
var top = rowOffset.top + row.outerHeight() - contentOffset.top + content.scrollTop();
|
|
var tooltip = this._taskTooltip = $('<div style="z-index: 100002;" class="' + styles.tooltipWrapper + '" >' + '<div class="' + styles.taskContent + '"></div></div>');
|
|
var tooltipWidth;
|
|
tooltip.css({
|
|
'left': left,
|
|
'top': top
|
|
}).appendTo(content).find(DOT + styles.taskContent).append(template({
|
|
styles: styles,
|
|
task: task,
|
|
messages: options.messages.views
|
|
}));
|
|
if (tooltip.outerHeight() < rowOffset.top - contentOffset.top) {
|
|
tooltip.css('top', rowOffset.top - contentOffset.top - tooltip.outerHeight() + content.scrollTop());
|
|
}
|
|
tooltipWidth = tooltip.outerWidth();
|
|
if (tooltipWidth + left - contentScrollLeft > contentWidth) {
|
|
left -= tooltipWidth;
|
|
if (left < contentScrollLeft) {
|
|
left = contentScrollLeft + contentWidth - (tooltipWidth + 17);
|
|
}
|
|
tooltip.css('left', left);
|
|
}
|
|
},
|
|
_removeTaskTooltip: function () {
|
|
if (this._taskTooltip) {
|
|
this._taskTooltip.remove();
|
|
}
|
|
this._taskTooltip = null;
|
|
},
|
|
_scrollTo: function (element) {
|
|
var elementLeft = element.offset().left;
|
|
var elementWidth = element.width();
|
|
var elementRight = elementLeft + elementWidth;
|
|
var row = element.closest('tr');
|
|
var rowTop = row.offset().top;
|
|
var rowHeight = row.height();
|
|
var rowBottom = rowTop + rowHeight;
|
|
var content = this.content;
|
|
var contentOffset = content.offset();
|
|
var contentTop = contentOffset.top;
|
|
var contentHeight = content.height();
|
|
var contentBottom = contentTop + contentHeight;
|
|
var contentLeft = contentOffset.left;
|
|
var contentWidth = content.width();
|
|
var contentRight = contentLeft + contentWidth;
|
|
var scrollbarWidth = kendo.support.scrollbar();
|
|
if (rowTop < contentTop) {
|
|
content.scrollTop(content.scrollTop() + (rowTop - contentTop));
|
|
} else if (rowBottom > contentBottom) {
|
|
content.scrollTop(content.scrollTop() + (rowBottom + scrollbarWidth - contentBottom));
|
|
}
|
|
if (elementLeft < contentLeft && elementWidth > contentWidth && elementRight < contentRight || elementRight > contentRight && elementWidth < contentWidth) {
|
|
content.scrollLeft(content.scrollLeft() + (elementRight + scrollbarWidth - contentRight));
|
|
} else if (elementRight > contentRight && elementWidth > contentWidth && elementLeft > contentLeft || elementLeft < contentLeft && elementWidth < contentWidth) {
|
|
content.scrollLeft(content.scrollLeft() + (elementLeft - contentLeft));
|
|
}
|
|
},
|
|
_timeSlots: function () {
|
|
if (!this._slots || !this._slots.length) {
|
|
return [];
|
|
}
|
|
return this._slots[this._slots.length - 1];
|
|
},
|
|
_headers: function (columnLevels) {
|
|
var rows = [];
|
|
var level;
|
|
var headers;
|
|
var column;
|
|
var headerText;
|
|
var styles = GanttView.styles;
|
|
for (var levelIndex = 0, levelCount = columnLevels.length; levelIndex < levelCount; levelIndex++) {
|
|
level = columnLevels[levelIndex];
|
|
headers = [];
|
|
for (var columnIndex = 0, columnCount = level.length; columnIndex < columnCount; columnIndex++) {
|
|
column = level[columnIndex];
|
|
headerText = kendoTextElement(column.text);
|
|
headers.push(kendoDomElement('th', {
|
|
colspan: column.span,
|
|
className: styles.header + (column.isNonWorking ? ' ' + styles.nonWorking : '')
|
|
}, [headerText]));
|
|
}
|
|
rows.push(kendoDomElement('tr', null, headers));
|
|
}
|
|
return rows;
|
|
},
|
|
_hours: function (start, end) {
|
|
var slotEnd;
|
|
var slots = [];
|
|
var options = this.options;
|
|
var workDayStart = options.workDayStart.getHours();
|
|
var workDayEnd = options.workDayEnd.getHours();
|
|
var isWorkHour;
|
|
var hours;
|
|
var hourSpan = options.hourSpan;
|
|
start = new Date(start);
|
|
end = new Date(end);
|
|
while (start < end) {
|
|
slotEnd = new Date(start);
|
|
hours = slotEnd.getHours();
|
|
isWorkHour = hours >= workDayStart && hours < workDayEnd;
|
|
slotEnd.setHours(slotEnd.getHours() + hourSpan);
|
|
if (hours == slotEnd.getHours()) {
|
|
slotEnd.setHours(slotEnd.getHours() + 2 * hourSpan);
|
|
}
|
|
if (!options.showWorkHours || isWorkHour) {
|
|
slots.push({
|
|
start: start,
|
|
end: slotEnd,
|
|
isNonWorking: !isWorkHour,
|
|
span: 1
|
|
});
|
|
}
|
|
start = slotEnd;
|
|
}
|
|
return slots;
|
|
},
|
|
_days: function (start, end) {
|
|
var slotEnd;
|
|
var slots = [];
|
|
var isWorkDay;
|
|
start = new Date(start);
|
|
end = new Date(end);
|
|
while (start < end) {
|
|
slotEnd = kendo.date.nextDay(start);
|
|
isWorkDay = this._isWorkDay(start);
|
|
if (!this.options.showWorkDays || isWorkDay) {
|
|
slots.push({
|
|
start: start,
|
|
end: slotEnd,
|
|
isNonWorking: !isWorkDay,
|
|
span: 1
|
|
});
|
|
}
|
|
start = slotEnd;
|
|
}
|
|
return slots;
|
|
},
|
|
_weeks: function (start, end) {
|
|
var slotEnd;
|
|
var slots = [];
|
|
var firstDay = this.calendarInfo().firstDay;
|
|
var daySlots;
|
|
var span;
|
|
start = new Date(start);
|
|
end = new Date(end);
|
|
while (start < end) {
|
|
slotEnd = kendo.date.dayOfWeek(kendo.date.addDays(start, 1), firstDay, 1);
|
|
if (slotEnd > end) {
|
|
slotEnd = end;
|
|
}
|
|
daySlots = this._days(start, slotEnd);
|
|
span = daySlots.length;
|
|
if (span > 0) {
|
|
slots.push({
|
|
start: daySlots[0].start,
|
|
end: daySlots[span - 1].end,
|
|
span: span
|
|
});
|
|
}
|
|
start = slotEnd;
|
|
}
|
|
return slots;
|
|
},
|
|
_months: function (start, end) {
|
|
var slotEnd;
|
|
var slots = [];
|
|
var daySlots;
|
|
var span;
|
|
start = new Date(start);
|
|
end = new Date(end);
|
|
while (start < end) {
|
|
slotEnd = new Date(start);
|
|
slotEnd.setMonth(slotEnd.getMonth() + 1);
|
|
daySlots = this._days(start, slotEnd);
|
|
span = daySlots.length;
|
|
if (span > 0) {
|
|
slots.push({
|
|
start: daySlots[0].start,
|
|
end: daySlots[span - 1].end,
|
|
span: span
|
|
});
|
|
}
|
|
start = slotEnd;
|
|
}
|
|
return slots;
|
|
},
|
|
_years: function (start, end) {
|
|
var slotEnd;
|
|
var slots = [];
|
|
start = new Date(start);
|
|
end = new Date(end);
|
|
while (start < end) {
|
|
slotEnd = new Date(start);
|
|
slotEnd.setFullYear(slotEnd.getFullYear() + 1);
|
|
slots.push({
|
|
start: start,
|
|
end: slotEnd,
|
|
span: 12
|
|
});
|
|
start = slotEnd;
|
|
}
|
|
return slots;
|
|
},
|
|
_slotHeaders: function (slots, template) {
|
|
var columns = [];
|
|
var slot;
|
|
for (var i = 0, l = slots.length; i < l; i++) {
|
|
slot = slots[i];
|
|
columns.push({
|
|
text: template(slot),
|
|
isNonWorking: !!slot.isNonWorking,
|
|
span: slot.span
|
|
});
|
|
}
|
|
return columns;
|
|
},
|
|
_isWorkDay: function (date) {
|
|
var day = date.getDay();
|
|
var workDays = this._workDays;
|
|
for (var i = 0, l = workDays.length; i < l; i++) {
|
|
if (workDays[i] === day) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
calendarInfo: function () {
|
|
return kendo.getCulture().calendars.standard;
|
|
},
|
|
_renderCurrentTime: function () {
|
|
var currentTime = this._getCurrentTime();
|
|
var timeOffset = this._offset(currentTime);
|
|
var element = $('<div class=\'k-current-time\'></div>');
|
|
var viewStyles = GanttView.styles;
|
|
var tablesWrap = $(DOT + viewStyles.tasksWrapper);
|
|
var tasksTable = $(DOT + viewStyles.tasksTable);
|
|
var slot;
|
|
if (!this.content || !this._timeSlots().length) {
|
|
return;
|
|
}
|
|
this.content.find('.k-current-time').remove();
|
|
slot = this._timeSlots()[this._slotIndex('start', currentTime)];
|
|
if (currentTime < slot.start || currentTime > slot.end) {
|
|
return;
|
|
}
|
|
if (tablesWrap.length && tasksTable.length) {
|
|
timeOffset += tasksTable.offset().left - tablesWrap.offset().left;
|
|
}
|
|
element.css({
|
|
left: timeOffset + 'px',
|
|
top: '0px',
|
|
width: '1px',
|
|
height: this._contentHeight + 'px'
|
|
}).appendTo(this.content);
|
|
},
|
|
_getCurrentTime: function () {
|
|
return new Date();
|
|
},
|
|
_currentTime: function () {
|
|
var markerOptions = this.options.currentTimeMarker;
|
|
if (markerOptions !== false && markerOptions.updateInterval !== undefined) {
|
|
this._renderCurrentTime();
|
|
this._currentTimeUpdateTimer = setInterval(proxy(this._renderCurrentTime, this), markerOptions.updateInterval);
|
|
}
|
|
}
|
|
});
|
|
extend(true, GanttView, { styles: viewStyles });
|
|
kendo.ui.GanttDayView = GanttView.extend({
|
|
name: 'day',
|
|
options: {
|
|
timeHeaderTemplate: TIME_HEADER_TEMPLATE,
|
|
dayHeaderTemplate: DAY_HEADER_TEMPLATE,
|
|
resizeTooltipFormat: 'h:mm tt ddd, MMM d'
|
|
},
|
|
range: function (range) {
|
|
this.start = kendo.date.getDate(range.start);
|
|
this.end = kendo.date.getDate(range.end);
|
|
if (kendo.date.getMilliseconds(range.end) > 0 || this.end.getTime() === this.start.getTime()) {
|
|
this.end = kendo.date.addDays(this.end, 1);
|
|
}
|
|
},
|
|
_createSlots: function () {
|
|
var daySlots;
|
|
var daySlot;
|
|
var hourSlots;
|
|
var hours;
|
|
var slots = [];
|
|
daySlots = this._days(this.start, this.end);
|
|
hourSlots = [];
|
|
for (var i = 0, l = daySlots.length; i < l; i++) {
|
|
daySlot = daySlots[i];
|
|
hours = this._hours(daySlot.start, daySlot.end);
|
|
daySlot.span = hours.length;
|
|
hourSlots.push.apply(hourSlots, hours);
|
|
}
|
|
slots.push(daySlots);
|
|
slots.push(hourSlots);
|
|
return slots;
|
|
},
|
|
_layout: function () {
|
|
var rows = [];
|
|
var options = this.options;
|
|
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.dayHeaderTemplate)));
|
|
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.timeHeaderTemplate)));
|
|
return rows;
|
|
}
|
|
});
|
|
kendo.ui.GanttWeekView = GanttView.extend({
|
|
name: 'week',
|
|
options: {
|
|
dayHeaderTemplate: DAY_HEADER_TEMPLATE,
|
|
weekHeaderTemplate: WEEK_HEADER_TEMPLATE,
|
|
resizeTooltipFormat: 'h:mm tt ddd, MMM d'
|
|
},
|
|
range: function (range) {
|
|
var calendarInfo = this.calendarInfo();
|
|
var firstDay = calendarInfo.firstDay;
|
|
var rangeEnd = range.end;
|
|
if (firstDay === rangeEnd.getDay()) {
|
|
rangeEnd.setDate(rangeEnd.getDate() + 7);
|
|
}
|
|
this.start = kendo.date.getDate(kendo.date.dayOfWeek(range.start, firstDay, -1));
|
|
this.end = kendo.date.getDate(kendo.date.dayOfWeek(rangeEnd, firstDay, 1));
|
|
},
|
|
_createSlots: function () {
|
|
var slots = [];
|
|
slots.push(this._weeks(this.start, this.end));
|
|
slots.push(this._days(this.start, this.end));
|
|
return slots;
|
|
},
|
|
_layout: function () {
|
|
var rows = [];
|
|
var options = this.options;
|
|
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.weekHeaderTemplate)));
|
|
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.dayHeaderTemplate)));
|
|
return rows;
|
|
}
|
|
});
|
|
kendo.ui.GanttMonthView = GanttView.extend({
|
|
name: 'month',
|
|
options: {
|
|
weekHeaderTemplate: WEEK_HEADER_TEMPLATE,
|
|
monthHeaderTemplate: MONTH_HEADER_TEMPLATE,
|
|
resizeTooltipFormat: 'dddd, MMM d, yyyy'
|
|
},
|
|
range: function (range) {
|
|
this.start = kendo.date.firstDayOfMonth(range.start);
|
|
this.end = kendo.date.addDays(kendo.date.getDate(kendo.date.lastDayOfMonth(range.end)), 1);
|
|
},
|
|
_createSlots: function () {
|
|
var slots = [];
|
|
slots.push(this._months(this.start, this.end));
|
|
slots.push(this._weeks(this.start, this.end));
|
|
return slots;
|
|
},
|
|
_layout: function () {
|
|
var rows = [];
|
|
var options = this.options;
|
|
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.monthHeaderTemplate)));
|
|
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.weekHeaderTemplate)));
|
|
return rows;
|
|
}
|
|
});
|
|
kendo.ui.GanttYearView = GanttView.extend({
|
|
name: 'year',
|
|
options: {
|
|
yearHeaderTemplate: YEAR_HEADER_TEMPLATE,
|
|
monthHeaderTemplate: MONTH_HEADER_TEMPLATE,
|
|
resizeTooltipFormat: 'dddd, MMM d, yyyy'
|
|
},
|
|
range: function (range) {
|
|
this.start = kendo.date.firstDayOfMonth(new Date(range.start.setMonth(0)));
|
|
this.end = kendo.date.firstDayOfMonth(new Date(range.end.setMonth(12)));
|
|
},
|
|
_createSlots: function () {
|
|
var slots = [];
|
|
var monthSlots = this._months(this.start, this.end);
|
|
$(monthSlots).each(function (index, slot) {
|
|
slot.span = 1;
|
|
});
|
|
slots.push(this._years(this.start, this.end));
|
|
slots.push(monthSlots);
|
|
return slots;
|
|
},
|
|
_layout: function () {
|
|
var rows = [];
|
|
var options = this.options;
|
|
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.yearHeaderTemplate)));
|
|
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.monthHeaderTemplate)));
|
|
return rows;
|
|
}
|
|
});
|
|
var timelineStyles = {
|
|
wrapper: 'k-timeline k-grid k-widget',
|
|
gridHeader: 'k-grid-header',
|
|
gridHeaderWrap: 'k-grid-header-wrap',
|
|
gridContent: 'k-grid-content',
|
|
gridContentWrap: 'k-grid-content',
|
|
tasksWrapper: 'k-gantt-tables',
|
|
dependenciesWrapper: 'k-gantt-dependencies',
|
|
task: 'k-task',
|
|
line: 'k-line',
|
|
taskResizeHandle: 'k-resize-handle',
|
|
taskResizeHandleWest: 'k-resize-w',
|
|
taskDragHandle: 'k-task-draghandle',
|
|
taskComplete: 'k-task-complete',
|
|
taskDelete: 'k-task-delete',
|
|
taskWrapActive: 'k-task-wrap-active',
|
|
taskWrap: 'k-task-wrap',
|
|
taskDot: 'k-task-dot',
|
|
taskDotStart: 'k-task-start',
|
|
taskDotEnd: 'k-task-end',
|
|
hovered: 'k-state-hover',
|
|
selected: 'k-state-selected',
|
|
origin: 'k-origin'
|
|
};
|
|
var GanttTimeline = kendo.ui.GanttTimeline = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
if (!this.options.views || !this.options.views.length) {
|
|
this.options.views = [
|
|
'day',
|
|
'week',
|
|
'month'
|
|
];
|
|
}
|
|
isRtl = kendo.support.isRtl(element);
|
|
this._wrapper();
|
|
this._domTrees();
|
|
this._views();
|
|
this._selectable();
|
|
this._draggable();
|
|
this._resizable();
|
|
this._percentResizeDraggable();
|
|
this._createDependencyDraggable();
|
|
this._attachEvents();
|
|
this._tooltip();
|
|
},
|
|
options: {
|
|
name: 'GanttTimeline',
|
|
messages: {
|
|
views: {
|
|
day: 'Day',
|
|
week: 'Week',
|
|
month: 'Month',
|
|
year: 'Year',
|
|
start: 'Start',
|
|
end: 'End'
|
|
}
|
|
},
|
|
snap: true,
|
|
selectable: true,
|
|
editable: true
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
clearTimeout(this._tooltipTimeout);
|
|
if (this._currentTimeUpdateTimer) {
|
|
clearInterval(this._currentTimeUpdateTimer);
|
|
}
|
|
this._unbindView(this._selectedView);
|
|
if (this._moveDraggable) {
|
|
this._moveDraggable.destroy();
|
|
}
|
|
if (this._resizeDraggable) {
|
|
this._resizeDraggable.destroy();
|
|
}
|
|
if (this._percentDraggable) {
|
|
this._percentDraggable.destroy();
|
|
}
|
|
if (this._dependencyDraggable) {
|
|
this._dependencyDraggable.destroy();
|
|
}
|
|
if (this.touch) {
|
|
this.touch.destroy();
|
|
}
|
|
this._headerTree = null;
|
|
this._taskTree = null;
|
|
this._dependencyTree = null;
|
|
this.wrapper.off(NS);
|
|
kendo.destroy(this.wrapper);
|
|
},
|
|
_wrapper: function () {
|
|
var styles = GanttTimeline.styles;
|
|
var that = this;
|
|
var options = this.options;
|
|
var calculateSize = function () {
|
|
var rowHeight = typeof options.rowHeight === STRING ? options.rowHeight : options.rowHeight + 'px';
|
|
var table = $(kendo.format(SIZE_CALCULATION_TEMPLATE, rowHeight));
|
|
var calculatedRowHeight;
|
|
var calculatedCellHeight;
|
|
var content = that.wrapper.find(DOT + styles.tasksWrapper);
|
|
content.append(table);
|
|
calculatedRowHeight = table.find('tr').outerHeight();
|
|
calculatedCellHeight = table.find('td').height();
|
|
table.remove();
|
|
return {
|
|
'row': calculatedRowHeight,
|
|
'cell': calculatedCellHeight
|
|
};
|
|
};
|
|
this.wrapper = this.element.addClass(styles.wrapper).append('<div class=\'' + styles.gridHeader + '\'><div class=\'' + styles.gridHeaderWrap + '\'></div></div>').append('<div class=\'' + styles.gridContentWrap + '\'><div class=\'' + styles.tasksWrapper + '\'></div><div class=\'' + styles.dependenciesWrapper + '\'></div></div>');
|
|
if (options.rowHeight) {
|
|
this._calculatedSize = calculateSize();
|
|
}
|
|
},
|
|
_domTrees: function () {
|
|
var styles = GanttTimeline.styles;
|
|
var tree = kendo.dom.Tree;
|
|
var wrapper = this.wrapper;
|
|
this._headerTree = new tree(wrapper.find(DOT + styles.gridHeaderWrap)[0]);
|
|
this._taskTree = new tree(wrapper.find(DOT + styles.tasksWrapper)[0]);
|
|
this._dependencyTree = new tree(wrapper.find(DOT + styles.dependenciesWrapper)[0]);
|
|
},
|
|
_views: function () {
|
|
var views = this.options.views;
|
|
var view;
|
|
var isSettings;
|
|
var name;
|
|
var defaultView;
|
|
var selected;
|
|
this.views = {};
|
|
for (var i = 0, l = views.length; i < l; i++) {
|
|
view = views[i];
|
|
isSettings = isPlainObject(view);
|
|
if (isSettings && view.selectable === false) {
|
|
continue;
|
|
}
|
|
name = isSettings ? typeof view.type !== 'string' ? view.title : view.type : view;
|
|
defaultView = defaultViews[name];
|
|
if (defaultView) {
|
|
if (isSettings) {
|
|
view.type = defaultView.type;
|
|
}
|
|
defaultView.title = this.options.messages.views[name];
|
|
}
|
|
view = extend({ title: name }, defaultView, isSettings ? view : {});
|
|
if (name) {
|
|
this.views[name] = view;
|
|
if (!selected || view.selected) {
|
|
selected = name;
|
|
}
|
|
}
|
|
}
|
|
if (selected) {
|
|
this._selectedViewName = selected;
|
|
}
|
|
},
|
|
view: function (name) {
|
|
if (name) {
|
|
this._selectView(name);
|
|
this.trigger('navigate', {
|
|
view: name,
|
|
action: 'changeView'
|
|
});
|
|
}
|
|
return this._selectedView;
|
|
},
|
|
_selectView: function (name) {
|
|
if (name && this.views[name]) {
|
|
if (this._selectedView) {
|
|
this._unbindView(this._selectedView);
|
|
}
|
|
this._selectedView = this._initializeView(name);
|
|
this._selectedViewName = name;
|
|
}
|
|
},
|
|
_viewByIndex: function (index) {
|
|
var view;
|
|
var views = this.views;
|
|
for (view in views) {
|
|
if (!index) {
|
|
return view;
|
|
}
|
|
index--;
|
|
}
|
|
},
|
|
_initializeView: function (name) {
|
|
var view = this.views[name];
|
|
if (view) {
|
|
var type = view.type;
|
|
if (typeof type === 'string') {
|
|
type = kendo.getter(view.type)(window);
|
|
}
|
|
if (type) {
|
|
view = new type(this.wrapper, trimOptions(extend(true, {
|
|
headerTree: this._headerTree,
|
|
taskTree: this._taskTree,
|
|
dependencyTree: this._dependencyTree,
|
|
calculatedSize: this._calculatedSize
|
|
}, view, this.options)));
|
|
} else {
|
|
throw new Error('There is no such view');
|
|
}
|
|
}
|
|
return view;
|
|
},
|
|
_unbindView: function (view) {
|
|
if (view) {
|
|
view.destroy();
|
|
}
|
|
},
|
|
_range: function (tasks) {
|
|
var startOrder = {
|
|
field: 'start',
|
|
dir: 'asc'
|
|
};
|
|
var endOrder = {
|
|
field: 'end',
|
|
dir: 'desc'
|
|
};
|
|
if (!tasks || !tasks.length) {
|
|
return {
|
|
start: new Date(),
|
|
end: new Date()
|
|
};
|
|
}
|
|
var start = new Query(tasks).sort(startOrder).toArray()[0].start || new Date();
|
|
var end = new Query(tasks).sort(endOrder).toArray()[0].end || new Date();
|
|
return {
|
|
start: new Date(start),
|
|
end: new Date(end)
|
|
};
|
|
},
|
|
_render: function (tasks) {
|
|
var view = this.view();
|
|
var range = this._range(tasks);
|
|
this._tasks = tasks;
|
|
view.range(range);
|
|
view.renderLayout();
|
|
view.render(tasks);
|
|
},
|
|
_renderDependencies: function (dependencies) {
|
|
this.view()._renderDependencies(dependencies);
|
|
},
|
|
_taskByUid: function (uid) {
|
|
var tasks = this._tasks;
|
|
var length = tasks.length;
|
|
var task;
|
|
for (var i = 0; i < length; i++) {
|
|
task = tasks[i];
|
|
if (task.uid === uid) {
|
|
return task;
|
|
}
|
|
}
|
|
},
|
|
_draggable: function () {
|
|
var that = this;
|
|
var element;
|
|
var task;
|
|
var currentStart;
|
|
var startOffset;
|
|
var snap = this.options.snap;
|
|
var styles = GanttTimeline.styles;
|
|
var cleanUp = function () {
|
|
that.view()._removeDragHint();
|
|
if (element) {
|
|
element.css('opacity', 1);
|
|
}
|
|
element = null;
|
|
task = null;
|
|
that.dragInProgress = false;
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this._moveDraggable = new kendo.ui.Draggable(this.wrapper, {
|
|
distance: 0,
|
|
filter: DOT + styles.task,
|
|
holdToDrag: kendo.support.mobileOS,
|
|
ignore: DOT + styles.taskResizeHandle
|
|
});
|
|
this._moveDraggable.bind('dragstart', function (e) {
|
|
var view = that.view();
|
|
element = e.currentTarget.parent();
|
|
task = that._taskByUid(e.currentTarget.attr('data-uid'));
|
|
if (that.trigger('moveStart', { task: task })) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
currentStart = task.start;
|
|
startOffset = view._timeByPosition(e.x.location, snap) - currentStart;
|
|
view._createDragHint(element);
|
|
element.css('opacity', 0.5);
|
|
clearTimeout(that._tooltipTimeout);
|
|
that.dragInProgress = true;
|
|
}).bind('drag', kendo.throttle(function (e) {
|
|
if (!that.dragInProgress) {
|
|
return;
|
|
}
|
|
var view = that.view();
|
|
var date = new Date(view._timeByPosition(e.x.location, snap) - startOffset);
|
|
var updateHintDate = date;
|
|
if (!that.trigger('move', {
|
|
task: task,
|
|
start: date
|
|
})) {
|
|
currentStart = date;
|
|
if (isRtl) {
|
|
updateHintDate = new Date(currentStart.getTime() + task.duration());
|
|
}
|
|
view._updateDragHint(updateHintDate);
|
|
}
|
|
}, 15)).bind('dragend', function () {
|
|
that.trigger('moveEnd', {
|
|
task: task,
|
|
start: currentStart
|
|
});
|
|
cleanUp();
|
|
}).bind('dragcancel', function () {
|
|
cleanUp();
|
|
}).userEvents.bind('select', function () {
|
|
blurActiveElement();
|
|
});
|
|
},
|
|
_resizable: function () {
|
|
var that = this;
|
|
var element;
|
|
var task;
|
|
var currentStart;
|
|
var currentEnd;
|
|
var resizeStart;
|
|
var snap = this.options.snap;
|
|
var styles = GanttTimeline.styles;
|
|
var cleanUp = function () {
|
|
that.view()._removeResizeHint();
|
|
element = null;
|
|
task = null;
|
|
that.dragInProgress = false;
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this._resizeDraggable = new kendo.ui.Draggable(this.wrapper, {
|
|
distance: 0,
|
|
filter: DOT + styles.taskResizeHandle,
|
|
holdToDrag: false
|
|
});
|
|
this._resizeDraggable.bind('dragstart', function (e) {
|
|
resizeStart = e.currentTarget.hasClass(styles.taskResizeHandleWest);
|
|
if (isRtl) {
|
|
resizeStart = !resizeStart;
|
|
}
|
|
element = e.currentTarget.closest(DOT + styles.task);
|
|
task = that._taskByUid(element.attr('data-uid'));
|
|
if (that.trigger('resizeStart', { task: task })) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
currentStart = task.start;
|
|
currentEnd = task.end;
|
|
that.view()._createResizeHint(task);
|
|
clearTimeout(that._tooltipTimeout);
|
|
that.dragInProgress = true;
|
|
}).bind('drag', kendo.throttle(function (e) {
|
|
if (!that.dragInProgress) {
|
|
return;
|
|
}
|
|
var view = that.view();
|
|
var date = view._timeByPosition(e.x.location, snap, !resizeStart);
|
|
if (resizeStart) {
|
|
if (date < currentEnd) {
|
|
currentStart = date;
|
|
} else {
|
|
currentStart = currentEnd;
|
|
}
|
|
} else {
|
|
if (date > currentStart) {
|
|
currentEnd = date;
|
|
} else {
|
|
currentEnd = currentStart;
|
|
}
|
|
}
|
|
if (!that.trigger('resize', {
|
|
task: task,
|
|
start: currentStart,
|
|
end: currentEnd
|
|
})) {
|
|
view._updateResizeHint(currentStart, currentEnd, resizeStart);
|
|
}
|
|
}, 15)).bind('dragend', function () {
|
|
that.trigger('resizeEnd', {
|
|
task: task,
|
|
resizeStart: resizeStart,
|
|
start: currentStart,
|
|
end: currentEnd
|
|
});
|
|
cleanUp();
|
|
}).bind('dragcancel', function () {
|
|
cleanUp();
|
|
}).userEvents.bind('select', function () {
|
|
blurActiveElement();
|
|
});
|
|
},
|
|
_percentResizeDraggable: function () {
|
|
var that = this;
|
|
var task;
|
|
var taskElement;
|
|
var taskElementOffset;
|
|
var timelineOffset;
|
|
var originalPercentWidth;
|
|
var maxPercentWidth;
|
|
var currentPercentComplete;
|
|
var tooltipTop;
|
|
var tooltipLeft;
|
|
var styles = GanttTimeline.styles;
|
|
var delta;
|
|
var cleanUp = function () {
|
|
that.view()._removePercentCompleteTooltip();
|
|
taskElement = null;
|
|
task = null;
|
|
that.dragInProgress = false;
|
|
};
|
|
var updateElement = function (width) {
|
|
taskElement.find(DOT + styles.taskComplete).width(width).end().siblings(DOT + styles.taskDragHandle).css(isRtl ? 'right' : 'left', width);
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this._percentDraggable = new kendo.ui.Draggable(this.wrapper, {
|
|
distance: 0,
|
|
filter: DOT + styles.taskDragHandle,
|
|
holdToDrag: false
|
|
});
|
|
this._percentDraggable.bind('dragstart', function (e) {
|
|
if (that.trigger('percentResizeStart')) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
taskElement = e.currentTarget.siblings(DOT + styles.task);
|
|
task = that._taskByUid(taskElement.attr('data-uid'));
|
|
currentPercentComplete = task.percentComplete;
|
|
taskElementOffset = taskElement.offset();
|
|
timelineOffset = this.element.offset();
|
|
originalPercentWidth = taskElement.find(DOT + styles.taskComplete).width();
|
|
maxPercentWidth = taskElement.outerWidth();
|
|
clearTimeout(that._tooltipTimeout);
|
|
that.dragInProgress = true;
|
|
}).bind('drag', kendo.throttle(function (e) {
|
|
if (!that.dragInProgress) {
|
|
return;
|
|
}
|
|
delta = isRtl ? -e.x.initialDelta : e.x.initialDelta;
|
|
var currentWidth = Math.max(0, Math.min(maxPercentWidth, originalPercentWidth + delta));
|
|
currentPercentComplete = Math.round(currentWidth / maxPercentWidth * 100);
|
|
updateElement(currentWidth);
|
|
tooltipTop = taskElementOffset.top - timelineOffset.top;
|
|
tooltipLeft = taskElementOffset.left + currentWidth - timelineOffset.left;
|
|
if (isRtl) {
|
|
tooltipLeft += maxPercentWidth - 2 * currentWidth;
|
|
}
|
|
that.view()._updatePercentCompleteTooltip(tooltipTop, tooltipLeft, currentPercentComplete);
|
|
}, 15)).bind('dragend', function () {
|
|
that.trigger('percentResizeEnd', {
|
|
task: task,
|
|
percentComplete: currentPercentComplete / 100
|
|
});
|
|
cleanUp();
|
|
}).bind('dragcancel', function () {
|
|
updateElement(originalPercentWidth);
|
|
cleanUp();
|
|
}).userEvents.bind('select', function () {
|
|
blurActiveElement();
|
|
});
|
|
},
|
|
_createDependencyDraggable: function () {
|
|
var that = this;
|
|
var originalHandle;
|
|
var hoveredHandle = $();
|
|
var hoveredTask = $();
|
|
var startX;
|
|
var startY;
|
|
var useVML = browser.msie && browser.version < 9;
|
|
var styles = GanttTimeline.styles;
|
|
var cleanUp = function () {
|
|
originalHandle.css('display', '').removeClass(styles.hovered);
|
|
originalHandle.parent().removeClass(styles.origin);
|
|
originalHandle = null;
|
|
toggleHandles(false);
|
|
hoveredTask = $();
|
|
hoveredHandle = $();
|
|
that.view()._removeDependencyDragHint();
|
|
that.dragInProgress = false;
|
|
};
|
|
var toggleHandles = function (value) {
|
|
if (!hoveredTask.hasClass(styles.origin)) {
|
|
hoveredTask.find(DOT + styles.taskDot).css('display', value ? 'block' : '');
|
|
hoveredHandle.toggleClass(styles.hovered, value);
|
|
}
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
if (useVML && document.namespaces) {
|
|
document.namespaces.add('kvml', 'urn:schemas-microsoft-com:vml', '#default#VML');
|
|
}
|
|
this._dependencyDraggable = new kendo.ui.Draggable(this.wrapper, {
|
|
distance: 0,
|
|
filter: DOT + styles.taskDot,
|
|
holdToDrag: false
|
|
});
|
|
this._dependencyDraggable.bind('dragstart', function (e) {
|
|
if (that.trigger('dependencyDragStart')) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
originalHandle = e.currentTarget.css('display', 'block').addClass(styles.hovered);
|
|
originalHandle.parent().addClass(styles.origin);
|
|
var elementOffset = originalHandle.offset();
|
|
var tablesOffset = that.wrapper.find(DOT + styles.tasksWrapper).offset();
|
|
startX = Math.round(elementOffset.left - tablesOffset.left + originalHandle.outerHeight() / 2);
|
|
startY = Math.round(elementOffset.top - tablesOffset.top + originalHandle.outerWidth() / 2);
|
|
clearTimeout(that._tooltipTimeout);
|
|
that.dragInProgress = true;
|
|
}).bind('drag', kendo.throttle(function (e) {
|
|
if (!that.dragInProgress) {
|
|
return;
|
|
}
|
|
that.view()._removeDependencyDragHint();
|
|
var target = $(kendo.elementUnderCursor(e));
|
|
var tablesOffset = that.wrapper.find(DOT + styles.tasksWrapper).offset();
|
|
var currentX = e.x.location - tablesOffset.left;
|
|
var currentY = e.y.location - tablesOffset.top;
|
|
that.view()._updateDependencyDragHint({
|
|
x: startX,
|
|
y: startY
|
|
}, {
|
|
x: currentX,
|
|
y: currentY
|
|
}, useVML);
|
|
toggleHandles(false);
|
|
hoveredHandle = target.hasClass(styles.taskDot) ? target : $();
|
|
hoveredTask = target.closest(DOT + styles.taskWrap);
|
|
toggleHandles(true);
|
|
}, 15)).bind('dragend', function () {
|
|
if (hoveredHandle.length) {
|
|
var fromStart = originalHandle.hasClass(styles.taskDotStart);
|
|
var toStart = hoveredHandle.hasClass(styles.taskDotStart);
|
|
var type = fromStart ? toStart ? 3 : 2 : toStart ? 1 : 0;
|
|
var predecessor = that._taskByUid(originalHandle.siblings(DOT + styles.task).attr('data-uid'));
|
|
var successor = that._taskByUid(hoveredHandle.siblings(DOT + styles.task).attr('data-uid'));
|
|
if (predecessor !== successor) {
|
|
that.trigger('dependencyDragEnd', {
|
|
type: type,
|
|
predecessor: predecessor,
|
|
successor: successor
|
|
});
|
|
}
|
|
}
|
|
cleanUp();
|
|
}).bind('dragcancel', function () {
|
|
cleanUp();
|
|
}).userEvents.bind('select', function () {
|
|
blurActiveElement();
|
|
});
|
|
},
|
|
_selectable: function () {
|
|
var that = this;
|
|
var styles = GanttTimeline.styles;
|
|
if (this.options.selectable) {
|
|
this.wrapper.on(CLICK + NS, DOT + styles.task, function (e) {
|
|
e.stopPropagation();
|
|
if (!e.ctrlKey) {
|
|
that.trigger('select', { uid: $(this).attr('data-uid') });
|
|
} else {
|
|
that.trigger('clear');
|
|
}
|
|
}).on(CLICK + NS, DOT + styles.taskWrap, function (e) {
|
|
e.stopPropagation();
|
|
$(this).css('z-index', '0');
|
|
var target = $(document.elementFromPoint(e.clientX, e.clientY));
|
|
if (target.hasClass(styles.line)) {
|
|
target.click();
|
|
}
|
|
$(this).css('z-index', '');
|
|
}).on(CLICK + NS, DOT + styles.tasksWrapper, function () {
|
|
if (that.selectDependency().length > 0) {
|
|
that.clearSelection();
|
|
} else {
|
|
that.trigger('clear');
|
|
}
|
|
}).on(CLICK + NS, DOT + styles.line, function (e) {
|
|
e.stopPropagation();
|
|
that.selectDependency(this);
|
|
});
|
|
}
|
|
},
|
|
select: function (value) {
|
|
var element = this.wrapper.find(value);
|
|
var styles = GanttTimeline.styles;
|
|
if (element.length) {
|
|
this.clearSelection();
|
|
element.addClass(styles.selected);
|
|
if (kendo.support.mobileOS) {
|
|
element.parent().addClass(styles.taskWrapActive);
|
|
}
|
|
return;
|
|
}
|
|
return this.wrapper.find(DOT + styles.task + DOT + styles.selected);
|
|
},
|
|
selectDependency: function (value) {
|
|
var element = this.wrapper.find(value);
|
|
var uid;
|
|
var styles = GanttTimeline.styles;
|
|
if (element.length) {
|
|
this.clearSelection();
|
|
this.trigger('clear');
|
|
uid = $(element).attr('data-uid');
|
|
this.wrapper.find(DOT + styles.line + '[data-uid=\'' + uid + '\']').addClass(styles.selected);
|
|
return;
|
|
}
|
|
return this.wrapper.find(DOT + styles.line + DOT + styles.selected);
|
|
},
|
|
clearSelection: function () {
|
|
var styles = GanttTimeline.styles;
|
|
this.wrapper.find(DOT + styles.selected).removeClass(styles.selected);
|
|
if (kendo.support.mobileOS) {
|
|
this.wrapper.find(DOT + styles.taskWrapActive).removeClass(styles.taskWrapActive);
|
|
}
|
|
},
|
|
_attachEvents: function () {
|
|
var that = this;
|
|
var styles = GanttTimeline.styles;
|
|
if (this.options.editable) {
|
|
this._tabindex();
|
|
this.wrapper.on(CLICK + NS, DOT + styles.taskDelete, function (e) {
|
|
that.trigger('removeTask', { uid: $(this).closest(DOT + styles.task).attr('data-uid') });
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}).on(KEYDOWN + NS, function (e) {
|
|
var selectedDependency;
|
|
if (e.keyCode === keys.DELETE) {
|
|
selectedDependency = that.selectDependency();
|
|
if (selectedDependency.length) {
|
|
that.trigger('removeDependency', { uid: selectedDependency.attr('data-uid') });
|
|
that.clearSelection();
|
|
}
|
|
}
|
|
});
|
|
if (!kendo.support.mobileOS) {
|
|
this.wrapper.on(DBLCLICK + NS, DOT + styles.task, function (e) {
|
|
that.trigger('editTask', { uid: $(this).attr('data-uid') });
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
});
|
|
} else {
|
|
this.touch = this.wrapper.kendoTouch({
|
|
filter: DOT + styles.task,
|
|
doubletap: function (e) {
|
|
that.trigger('editTask', { uid: $(e.touch.currentTarget).attr('data-uid') });
|
|
}
|
|
}).data('kendoTouch');
|
|
}
|
|
}
|
|
},
|
|
_tooltip: function () {
|
|
var that = this;
|
|
var tooltipOptions = this.options.tooltip;
|
|
var styles = GanttTimeline.styles;
|
|
var currentMousePosition;
|
|
var mouseMoveHandler = function (e) {
|
|
currentMousePosition = e.clientX;
|
|
};
|
|
if (tooltipOptions && tooltipOptions.visible === false) {
|
|
return;
|
|
}
|
|
if (!kendo.support.mobileOS) {
|
|
this.wrapper.on(MOUSEENTER + NS, DOT + styles.task, function () {
|
|
var element = this;
|
|
var task = that._taskByUid($(this).attr('data-uid'));
|
|
if (that.dragInProgress) {
|
|
return;
|
|
}
|
|
that._tooltipTimeout = setTimeout(function () {
|
|
that.view()._createTaskTooltip(task, element, currentMousePosition);
|
|
}, 800);
|
|
$(this).on(MOUSEMOVE, mouseMoveHandler);
|
|
}).on(MOUSELEAVE + NS, DOT + styles.task, function () {
|
|
clearTimeout(that._tooltipTimeout);
|
|
that.view()._removeTaskTooltip();
|
|
$(this).off(MOUSEMOVE, mouseMoveHandler);
|
|
});
|
|
} else {
|
|
this.wrapper.on(CLICK + NS, DOT + styles.taskDelete, function (e) {
|
|
e.stopPropagation();
|
|
that.view()._removeTaskTooltip();
|
|
}).on(MOUSELEAVE + NS, DOT + styles.task, function (e) {
|
|
var parents = $(e.relatedTarget).parents(DOT + styles.taskWrap, DOT + styles.task);
|
|
if (parents.length === 0) {
|
|
that.view()._removeTaskTooltip();
|
|
}
|
|
});
|
|
this.touch.bind('tap', function (e) {
|
|
var element = e.touch.target;
|
|
var task = that._taskByUid($(element).attr('data-uid'));
|
|
var currentPosition = e.touch.x.client;
|
|
if (that.view()._taskTooltip) {
|
|
that.view()._removeTaskTooltip();
|
|
}
|
|
that.view()._createTaskTooltip(task, element, currentPosition);
|
|
}).bind('doubletap', function () {
|
|
that.view()._removeTaskTooltip();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
extend(true, GanttTimeline, { styles: timelineStyles });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.gantt', [
|
|
'kendo.data',
|
|
'kendo.popup',
|
|
'kendo.window',
|
|
'kendo.resizable',
|
|
'kendo.gantt.list',
|
|
'kendo.gantt.timeline',
|
|
'kendo.grid',
|
|
'kendo.pdf'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'gantt',
|
|
name: 'Gantt',
|
|
category: 'web',
|
|
description: 'The Gantt component.',
|
|
depends: [
|
|
'data',
|
|
'popup',
|
|
'resizable',
|
|
'window',
|
|
'gantt.list',
|
|
'gantt.timeline',
|
|
'grid'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo;
|
|
var supportsMedia = 'matchMedia' in window;
|
|
var browser = kendo.support.browser;
|
|
var mobileOS = kendo.support.mobileOS;
|
|
var Observable = kendo.Observable;
|
|
var Widget = kendo.ui.Widget;
|
|
var DataSource = kendo.data.DataSource;
|
|
var ObservableObject = kendo.data.ObservableObject;
|
|
var ObservableArray = kendo.data.ObservableArray;
|
|
var Query = kendo.data.Query;
|
|
var isArray = $.isArray;
|
|
var inArray = $.inArray;
|
|
var isFunction = kendo.isFunction;
|
|
var proxy = $.proxy;
|
|
var extend = $.extend;
|
|
var isPlainObject = $.isPlainObject;
|
|
var map = $.map;
|
|
var keys = kendo.keys;
|
|
var defaultIndicatorWidth = 3;
|
|
var NS = '.kendoGantt';
|
|
var PERCENTAGE_FORMAT = 'p0';
|
|
var TABINDEX = 'tabIndex';
|
|
var CLICK = 'click';
|
|
var WIDTH = 'width';
|
|
var STRING = 'string';
|
|
var DIRECTIONS = {
|
|
'down': {
|
|
origin: 'bottom left',
|
|
position: 'top left'
|
|
},
|
|
'up': {
|
|
origin: 'top left',
|
|
position: 'bottom left'
|
|
}
|
|
};
|
|
var ARIA_DESCENDANT = 'aria-activedescendant';
|
|
var ACTIVE_CELL = 'gantt_active_cell';
|
|
var ACTIVE_OPTION = 'action-option-focused';
|
|
var DOT = '.';
|
|
var TASK_DELETE_CONFIRM = 'Are you sure you want to delete this task?';
|
|
var DEPENDENCY_DELETE_CONFIRM = 'Are you sure you want to delete this dependency?';
|
|
var TOGGLE_BUTTON_TEMPLATE = kendo.template('<button class="#=styles.buttonToggle#"><span class="#=styles.iconToggle#">&nbps;</span></button>');
|
|
var BUTTON_TEMPLATE = '<button class="#=styles.button# #=className#" ' + '#if (action) {#' + 'data-action="#=action#"' + '#}#' + '><span class="#=iconClass#"></span><span>#=text#</span></button>';
|
|
var COMMAND_BUTTON_TEMPLATE = '<a class="#=className#" #=attr# href="\\#">#=text#</a>';
|
|
var VIEWBUTTONTEMPLATE = kendo.template('<li class="#=styles.currentView# #=styles.viewButtonDefault#"><a href="\\#" class="#=styles.link#">&nbps;</a></li>');
|
|
var HEADER_VIEWS_TEMPLATE = kendo.template('<ul class="#=styles.viewsWrapper#">' + '#for(var view in views){#' + '<li class="#=styles.viewButtonDefault# #=styles.viewButton#-#= view.toLowerCase() #" data-#=ns#name="#=view#"><a href="\\#" class="#=styles.link#">#=views[view].title#</a></li>' + '#}#' + '</ul>');
|
|
var TASK_DROPDOWN_TEMPLATE = kendo.template('<div class="#=styles.popupWrapper#">' + '<ul class="#=styles.popupList#" role="listbox">' + '#for(var i = 0, l = actions.length; i < l; i++){#' + '<li class="#=styles.item#" data-action="#=actions[i].data#" role="option">#=actions[i].text#</span>' + '#}#' + '</ul>' + '</div>');
|
|
var DATERANGEEDITOR = function (container, options) {
|
|
var attr = { name: options.field };
|
|
var validationRules = options.model.fields[options.field].validation;
|
|
if (validationRules && isPlainObject(validationRules) && validationRules.message) {
|
|
attr[kendo.attr('dateCompare-msg')] = validationRules.message;
|
|
}
|
|
$('<input type="text" required ' + kendo.attr('type') + '="date" ' + kendo.attr('role') + '="datetimepicker" ' + kendo.attr('bind') + '="value:' + options.field + '" ' + kendo.attr('validate') + '=\'true\' />').attr(attr).appendTo(container);
|
|
$('<span ' + kendo.attr('for') + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
|
|
};
|
|
var RESOURCESEDITOR = function (container, options) {
|
|
$('<a href="#" class="' + options.styles.button + '">' + options.messages.assignButton + '</a>').click(options.click).appendTo(container);
|
|
};
|
|
var ganttStyles = {
|
|
wrapper: 'k-widget k-gantt',
|
|
rowHeight: 'k-gantt-rowheight',
|
|
listWrapper: 'k-gantt-layout k-gantt-treelist',
|
|
list: 'k-gantt-treelist',
|
|
timelineWrapper: 'k-gantt-layout k-gantt-timeline',
|
|
timeline: 'k-gantt-timeline',
|
|
splitBarWrapper: 'k-splitbar k-state-default k-splitbar-horizontal k-splitbar-draggable-horizontal k-gantt-layout',
|
|
splitBar: 'k-splitbar',
|
|
splitBarHover: 'k-splitbar-horizontal-hover',
|
|
popupWrapper: 'k-list-container',
|
|
popupList: 'k-list k-reset',
|
|
resizeHandle: 'k-resize-handle',
|
|
icon: 'k-icon',
|
|
item: 'k-item',
|
|
line: 'k-line',
|
|
buttonDelete: 'k-gantt-delete',
|
|
buttonCancel: 'k-gantt-cancel',
|
|
buttonSave: 'k-gantt-update',
|
|
buttonToggle: 'k-gantt-toggle',
|
|
primary: 'k-primary',
|
|
hovered: 'k-state-hover',
|
|
selected: 'k-state-selected',
|
|
focused: 'k-state-focused',
|
|
gridHeader: 'k-grid-header',
|
|
gridHeaderWrap: 'k-grid-header-wrap',
|
|
gridContent: 'k-grid-content',
|
|
popup: {
|
|
form: 'k-popup-edit-form',
|
|
editForm: 'k-gantt-edit-form',
|
|
formContainer: 'k-edit-form-container',
|
|
resourcesFormContainer: 'k-resources-form-container',
|
|
message: 'k-popup-message',
|
|
buttonsContainer: 'k-edit-buttons k-state-default',
|
|
button: 'k-button',
|
|
editField: 'k-edit-field',
|
|
editLabel: 'k-edit-label',
|
|
resourcesField: 'k-gantt-resources'
|
|
},
|
|
toolbar: {
|
|
headerWrapper: 'k-floatwrap k-header k-gantt-toolbar',
|
|
footerWrapper: 'k-floatwrap k-header k-gantt-toolbar',
|
|
toolbar: 'k-gantt-toolbar',
|
|
expanded: 'k-state-expanded',
|
|
views: 'k-gantt-views',
|
|
viewsWrapper: 'k-reset k-header k-gantt-views',
|
|
actions: 'k-gantt-actions',
|
|
button: 'k-button k-button-icontext',
|
|
buttonToggle: 'k-button k-button-icon k-gantt-toggle',
|
|
iconPlus: 'k-icon k-i-plus',
|
|
iconPdf: 'k-icon k-i-pdf',
|
|
iconToggle: 'k-icon k-i-gantt-toggle',
|
|
viewButtonDefault: 'k-state-default',
|
|
viewButton: 'k-view',
|
|
currentView: 'k-current-view',
|
|
link: 'k-link',
|
|
pdfButton: 'k-gantt-pdf',
|
|
appendButton: 'k-gantt-create'
|
|
}
|
|
};
|
|
function selector(uid) {
|
|
return '[' + kendo.attr('uid') + (uid ? '=\'' + uid + '\']' : ']');
|
|
}
|
|
function trimOptions(options) {
|
|
delete options.name;
|
|
delete options.prefix;
|
|
delete options.remove;
|
|
delete options.edit;
|
|
delete options.add;
|
|
delete options.navigate;
|
|
return options;
|
|
}
|
|
function dateCompareValidator(input) {
|
|
if (input.filter('[name=end], [name=start]').length) {
|
|
var field = input.attr('name');
|
|
var picker = kendo.widgetInstance(input, kendo.ui);
|
|
var dates = {};
|
|
var container = input;
|
|
var editable;
|
|
var model;
|
|
while (container !== window && !editable) {
|
|
container = container.parent();
|
|
editable = container.data('kendoEditable');
|
|
}
|
|
model = editable ? editable.options.model : null;
|
|
if (!model) {
|
|
return true;
|
|
}
|
|
dates.start = model.start;
|
|
dates.end = model.end;
|
|
dates[field] = picker ? picker.value() : kendo.parseDate(input.value());
|
|
return dates.start <= dates.end;
|
|
}
|
|
return true;
|
|
}
|
|
function focusTable(table, direct) {
|
|
var wrapper = table.parents('[' + kendo.attr('role') + '="gantt"]');
|
|
var scrollPositions = [];
|
|
var parents = scrollableParents(wrapper);
|
|
table.attr(TABINDEX, 0);
|
|
if (direct) {
|
|
parents.each(function (index, parent) {
|
|
scrollPositions[index] = $(parent).scrollTop();
|
|
});
|
|
}
|
|
try {
|
|
table[0].setActive();
|
|
} catch (e) {
|
|
table[0].focus();
|
|
}
|
|
if (direct) {
|
|
parents.each(function (index, parent) {
|
|
$(parent).scrollTop(scrollPositions[index]);
|
|
});
|
|
}
|
|
}
|
|
function scrollableParents(element) {
|
|
return $(element).parentsUntil('body').filter(function (index, element) {
|
|
var computedStyle = kendo.getComputedStyles(element, ['overflow']);
|
|
return computedStyle.overflow != 'visible';
|
|
}).add(window);
|
|
}
|
|
var defaultCommands;
|
|
var TaskDropDown = Observable.extend({
|
|
init: function (element, options) {
|
|
Observable.fn.init.call(this);
|
|
this.element = element;
|
|
this.options = extend(true, {}, this.options, options);
|
|
this._popup();
|
|
},
|
|
options: {
|
|
direction: 'down',
|
|
navigatable: false
|
|
},
|
|
_current: function (method) {
|
|
var ganttStyles = Gantt.styles;
|
|
var current = this.list.find(DOT + ganttStyles.focused);
|
|
var sibling = current[method]();
|
|
if (sibling.length) {
|
|
current.removeClass(ganttStyles.focused).removeAttr('id');
|
|
sibling.addClass(ganttStyles.focused).attr('id', ACTIVE_OPTION);
|
|
this.list.find('ul').removeAttr(ARIA_DESCENDANT).attr(ARIA_DESCENDANT, ACTIVE_OPTION);
|
|
}
|
|
},
|
|
_popup: function () {
|
|
var that = this;
|
|
var ganttStyles = Gantt.styles;
|
|
var itemSelector = 'li' + DOT + ganttStyles.item;
|
|
var appendButtonSelector = DOT + ganttStyles.toolbar.appendButton;
|
|
var actions = this.options.messages.actions;
|
|
var navigatable = this.options.navigatable;
|
|
this.list = $(TASK_DROPDOWN_TEMPLATE({
|
|
styles: ganttStyles,
|
|
actions: [
|
|
{
|
|
data: 'add',
|
|
text: actions.addChild
|
|
},
|
|
{
|
|
data: 'insert-before',
|
|
text: actions.insertBefore
|
|
},
|
|
{
|
|
data: 'insert-after',
|
|
text: actions.insertAfter
|
|
}
|
|
]
|
|
}));
|
|
this.element.append(this.list);
|
|
this.popup = new kendo.ui.Popup(this.list, extend({
|
|
anchor: this.element.find(appendButtonSelector),
|
|
open: function () {
|
|
that._adjustListWidth();
|
|
},
|
|
animation: this.options.animation
|
|
}, DIRECTIONS[this.options.direction]));
|
|
this.element.on(CLICK + NS, appendButtonSelector, function (e) {
|
|
var target = $(this);
|
|
var action = target.attr(kendo.attr('action'));
|
|
e.preventDefault();
|
|
if (action) {
|
|
that.trigger('command', { type: action });
|
|
} else {
|
|
that.popup.open();
|
|
if (navigatable) {
|
|
that.list.find('li:first').addClass(ganttStyles.focused).attr('id', ACTIVE_OPTION).end().find('ul').attr({
|
|
TABINDEX: 0,
|
|
'aria-activedescendant': ACTIVE_OPTION
|
|
}).focus();
|
|
}
|
|
}
|
|
});
|
|
this.list.find(itemSelector).hover(function () {
|
|
$(this).addClass(ganttStyles.hovered);
|
|
}, function () {
|
|
$(this).removeClass(ganttStyles.hovered);
|
|
}).end().on(CLICK + NS, itemSelector, function () {
|
|
that.trigger('command', { type: $(this).attr(kendo.attr('action')) });
|
|
that.popup.close();
|
|
});
|
|
if (navigatable) {
|
|
this.popup.bind('close', function () {
|
|
that.list.find(itemSelector).removeClass(ganttStyles.focused).end().find('ul').attr(TABINDEX, 0);
|
|
that.element.parents('[' + kendo.attr('role') + '="gantt"]').find(DOT + ganttStyles.gridContent + ' > table:first').focus();
|
|
});
|
|
this.list.find('ul').on('keydown' + NS, function (e) {
|
|
var key = e.keyCode;
|
|
switch (key) {
|
|
case keys.UP:
|
|
e.preventDefault();
|
|
that._current('prev');
|
|
break;
|
|
case keys.DOWN:
|
|
e.preventDefault();
|
|
that._current('next');
|
|
break;
|
|
case keys.ENTER:
|
|
that.list.find(DOT + ganttStyles.focused).click();
|
|
break;
|
|
case keys.ESC:
|
|
e.preventDefault();
|
|
that.popup.close();
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_adjustListWidth: function () {
|
|
var list = this.list;
|
|
var ganttStyles = Gantt.styles;
|
|
var width = list[0].style.width;
|
|
var wrapper = this.element.find(DOT + ganttStyles.toolbar.appendButton);
|
|
var outerWidth = list.outerWidth();
|
|
var computedStyle;
|
|
var computedWidth;
|
|
if (!list.data(WIDTH) && width) {
|
|
return;
|
|
}
|
|
computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0;
|
|
computedWidth = computedStyle ? parseFloat(computedStyle.width) : wrapper.outerWidth();
|
|
if (computedStyle && (browser.mozilla || browser.msie)) {
|
|
computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth);
|
|
}
|
|
if (list.css('box-sizing') !== 'border-box') {
|
|
width = computedWidth - (list.outerWidth() - list.width());
|
|
} else {
|
|
width = computedWidth;
|
|
}
|
|
if (outerWidth > width) {
|
|
width = outerWidth;
|
|
}
|
|
list.css({
|
|
fontFamily: wrapper.css('font-family'),
|
|
width: width
|
|
}).data(WIDTH, width);
|
|
},
|
|
destroy: function () {
|
|
clearTimeout(this._focusTimeout);
|
|
this.popup.destroy();
|
|
this.element.off(NS);
|
|
this.list.off(NS);
|
|
this.unbind();
|
|
}
|
|
});
|
|
var createDataSource = function (type, name) {
|
|
return function (options) {
|
|
options = isArray(options) ? { data: options } : options;
|
|
var dataSource = options || {};
|
|
var data = dataSource.data;
|
|
dataSource.data = data;
|
|
if (!(dataSource instanceof type) && dataSource instanceof DataSource) {
|
|
throw new Error('Incorrect DataSource type. Only ' + name + ' instances are supported');
|
|
}
|
|
return dataSource instanceof type ? dataSource : new type(dataSource);
|
|
};
|
|
};
|
|
var GanttDependency = kendo.data.Model.define({
|
|
id: 'id',
|
|
fields: {
|
|
id: { type: 'number' },
|
|
predecessorId: { type: 'number' },
|
|
successorId: { type: 'number' },
|
|
type: { type: 'number' }
|
|
}
|
|
});
|
|
var GanttDependencyDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
DataSource.fn.init.call(this, extend(true, {}, {
|
|
schema: {
|
|
modelBase: GanttDependency,
|
|
model: GanttDependency
|
|
}
|
|
}, options));
|
|
},
|
|
successors: function (id) {
|
|
return this._dependencies('predecessorId', id);
|
|
},
|
|
predecessors: function (id) {
|
|
return this._dependencies('successorId', id);
|
|
},
|
|
dependencies: function (id) {
|
|
var predecessors = this.predecessors(id);
|
|
var successors = this.successors(id);
|
|
predecessors.push.apply(predecessors, successors);
|
|
return predecessors;
|
|
},
|
|
_dependencies: function (field, id) {
|
|
var data = this.view();
|
|
var filter = {
|
|
field: field,
|
|
operator: 'eq',
|
|
value: id
|
|
};
|
|
data = new Query(data).filter(filter).toArray();
|
|
return data;
|
|
}
|
|
});
|
|
GanttDependencyDataSource.create = createDataSource(GanttDependencyDataSource, 'GanttDependencyDataSource');
|
|
var GanttTask = kendo.data.Model.define({
|
|
duration: function () {
|
|
var end = this.end;
|
|
var start = this.start;
|
|
return end - start;
|
|
},
|
|
isMilestone: function () {
|
|
return this.duration() === 0;
|
|
},
|
|
_offset: function (value) {
|
|
var field = [
|
|
'start',
|
|
'end'
|
|
];
|
|
var newValue;
|
|
for (var i = 0; i < field.length; i++) {
|
|
newValue = new Date(this.get(field[i]).getTime() + value);
|
|
this.set(field[i], newValue);
|
|
}
|
|
},
|
|
id: 'id',
|
|
fields: {
|
|
id: { type: 'number' },
|
|
parentId: {
|
|
type: 'number',
|
|
defaultValue: null,
|
|
validation: { required: true }
|
|
},
|
|
orderId: {
|
|
type: 'number',
|
|
validation: { required: true }
|
|
},
|
|
title: {
|
|
type: 'string',
|
|
defaultValue: 'New task'
|
|
},
|
|
start: {
|
|
type: 'date',
|
|
validation: { required: true }
|
|
},
|
|
end: {
|
|
type: 'date',
|
|
validation: {
|
|
required: true,
|
|
dateCompare: dateCompareValidator,
|
|
message: 'End date should be after or equal to the start date'
|
|
}
|
|
},
|
|
percentComplete: {
|
|
type: 'number',
|
|
validation: {
|
|
required: true,
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.01
|
|
}
|
|
},
|
|
summary: { type: 'boolean' },
|
|
expanded: {
|
|
type: 'boolean',
|
|
defaultValue: true
|
|
}
|
|
}
|
|
});
|
|
var GanttDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
DataSource.fn.init.call(this, extend(true, {}, {
|
|
schema: {
|
|
modelBase: GanttTask,
|
|
model: GanttTask
|
|
}
|
|
}, options));
|
|
},
|
|
remove: function (task) {
|
|
var parentId = task.get('parentId');
|
|
var children = this.taskAllChildren(task);
|
|
this._removeItems(children);
|
|
task = DataSource.fn.remove.call(this, task);
|
|
this._childRemoved(parentId, task.get('orderId'));
|
|
return task;
|
|
},
|
|
add: function (task) {
|
|
if (!task) {
|
|
return;
|
|
}
|
|
task = this._toGanttTask(task);
|
|
return this.insert(this.taskSiblings(task).length, task);
|
|
},
|
|
insert: function (index, task) {
|
|
if (!task) {
|
|
return;
|
|
}
|
|
task = this._toGanttTask(task);
|
|
task.set('orderId', index);
|
|
task = DataSource.fn.insert.call(this, index, task);
|
|
this._reorderSiblings(task, this.taskSiblings(task).length - 1);
|
|
this._resolveSummaryFields(this.taskParent(task));
|
|
return task;
|
|
},
|
|
taskChildren: function (task) {
|
|
var data = this.view();
|
|
var filter = {
|
|
field: 'parentId',
|
|
operator: 'eq',
|
|
value: null
|
|
};
|
|
var order = this._sort && this._sort.length ? this._sort : {
|
|
field: 'orderId',
|
|
dir: 'asc'
|
|
};
|
|
var taskId;
|
|
if (!!task) {
|
|
taskId = task.get('id');
|
|
if (taskId === undefined || taskId === null || taskId === '') {
|
|
return [];
|
|
}
|
|
filter.value = taskId;
|
|
}
|
|
data = new Query(data).filter(filter).sort(order).toArray();
|
|
return data;
|
|
},
|
|
taskAllChildren: function (task) {
|
|
var data = [];
|
|
var that = this;
|
|
var callback = function (task) {
|
|
var tasks = that.taskChildren(task);
|
|
data.push.apply(data, tasks);
|
|
map(tasks, callback);
|
|
};
|
|
if (!!task) {
|
|
callback(task);
|
|
} else {
|
|
data = this.view();
|
|
}
|
|
return data;
|
|
},
|
|
taskSiblings: function (task) {
|
|
if (!task) {
|
|
return null;
|
|
}
|
|
var parent = this.taskParent(task);
|
|
return this.taskChildren(parent);
|
|
},
|
|
taskParent: function (task) {
|
|
if (!task || task.get('parentId') === null) {
|
|
return null;
|
|
}
|
|
return this.get(task.parentId);
|
|
},
|
|
taskLevel: function (task) {
|
|
var level = 0;
|
|
var parent = this.taskParent(task);
|
|
while (parent !== null) {
|
|
level += 1;
|
|
parent = this.taskParent(parent);
|
|
}
|
|
return level;
|
|
},
|
|
taskTree: function (task) {
|
|
var data = [];
|
|
var current;
|
|
var tasks = this.taskChildren(task);
|
|
for (var i = 0, l = tasks.length; i < l; i++) {
|
|
current = tasks[i];
|
|
data.push(current);
|
|
if (current.get('expanded')) {
|
|
var children = this.taskTree(current);
|
|
data.push.apply(data, children);
|
|
}
|
|
}
|
|
return data;
|
|
},
|
|
update: function (task, taskInfo) {
|
|
var that = this;
|
|
var oldValue;
|
|
var offsetChildren = function (parentTask, offset) {
|
|
var children = that.taskAllChildren(parentTask);
|
|
for (var i = 0, l = children.length; i < l; i++) {
|
|
children[i]._offset(offset);
|
|
}
|
|
};
|
|
var modelChangeHandler = function (e) {
|
|
var field = e.field;
|
|
var model = e.sender;
|
|
switch (field) {
|
|
case 'start':
|
|
that._resolveSummaryStart(that.taskParent(model));
|
|
offsetChildren(model, model.get(field).getTime() - oldValue.getTime());
|
|
break;
|
|
case 'end':
|
|
that._resolveSummaryEnd(that.taskParent(model));
|
|
break;
|
|
case 'percentComplete':
|
|
that._resolveSummaryPercentComplete(that.taskParent(model));
|
|
break;
|
|
case 'orderId':
|
|
that._reorderSiblings(model, oldValue);
|
|
break;
|
|
}
|
|
};
|
|
if (taskInfo.parentId !== undefined) {
|
|
oldValue = task.get('parentId');
|
|
if (oldValue !== taskInfo.parentId) {
|
|
task.set('parentId', taskInfo.parentId);
|
|
that._childRemoved(oldValue, task.get('orderId'));
|
|
task.set('orderId', that.taskSiblings(task).length - 1);
|
|
that._resolveSummaryFields(that.taskParent(task));
|
|
}
|
|
delete taskInfo.parentId;
|
|
}
|
|
task.bind('change', modelChangeHandler);
|
|
for (var field in taskInfo) {
|
|
oldValue = task.get(field);
|
|
task.set(field, taskInfo[field]);
|
|
}
|
|
task.unbind('change', modelChangeHandler);
|
|
},
|
|
_resolveSummaryFields: function (summary) {
|
|
if (!summary) {
|
|
return;
|
|
}
|
|
this._updateSummary(summary);
|
|
if (!this.taskChildren(summary).length) {
|
|
return;
|
|
}
|
|
this._resolveSummaryStart(summary);
|
|
this._resolveSummaryEnd(summary);
|
|
this._resolveSummaryPercentComplete(summary);
|
|
},
|
|
_resolveSummaryStart: function (summary) {
|
|
var that = this;
|
|
var getSummaryStart = function (parentTask) {
|
|
var children = that.taskChildren(parentTask);
|
|
var min = children[0].start.getTime();
|
|
var currentMin;
|
|
for (var i = 1, l = children.length; i < l; i++) {
|
|
currentMin = children[i].start.getTime();
|
|
if (currentMin < min) {
|
|
min = currentMin;
|
|
}
|
|
}
|
|
return new Date(min);
|
|
};
|
|
this._updateSummaryRecursive(summary, 'start', getSummaryStart);
|
|
},
|
|
_resolveSummaryEnd: function (summary) {
|
|
var that = this;
|
|
var getSummaryEnd = function (parentTask) {
|
|
var children = that.taskChildren(parentTask);
|
|
var max = children[0].end.getTime();
|
|
var currentMax;
|
|
for (var i = 1, l = children.length; i < l; i++) {
|
|
currentMax = children[i].end.getTime();
|
|
if (currentMax > max) {
|
|
max = currentMax;
|
|
}
|
|
}
|
|
return new Date(max);
|
|
};
|
|
this._updateSummaryRecursive(summary, 'end', getSummaryEnd);
|
|
},
|
|
_resolveSummaryPercentComplete: function (summary) {
|
|
var that = this;
|
|
var getSummaryPercentComplete = function (parentTask) {
|
|
var children = that.taskChildren(parentTask);
|
|
var percentComplete = new Query(children).aggregate([{
|
|
field: 'percentComplete',
|
|
aggregate: 'average'
|
|
}]);
|
|
return percentComplete.percentComplete.average;
|
|
};
|
|
this._updateSummaryRecursive(summary, 'percentComplete', getSummaryPercentComplete);
|
|
},
|
|
_updateSummaryRecursive: function (summary, field, callback) {
|
|
if (!summary) {
|
|
return;
|
|
}
|
|
var value = callback(summary);
|
|
summary.set(field, value);
|
|
var parent = this.taskParent(summary);
|
|
if (parent) {
|
|
this._updateSummaryRecursive(parent, field, callback);
|
|
}
|
|
},
|
|
_childRemoved: function (parentId, index) {
|
|
var parent = parentId === null ? null : this.get(parentId);
|
|
var children = this.taskChildren(parent);
|
|
for (var i = index, l = children.length; i < l; i++) {
|
|
children[i].set('orderId', i);
|
|
}
|
|
this._resolveSummaryFields(parent);
|
|
},
|
|
_reorderSiblings: function (task, oldOrderId) {
|
|
var orderId = task.get('orderId');
|
|
var direction = orderId > oldOrderId;
|
|
var startIndex = direction ? oldOrderId : orderId;
|
|
var endIndex = direction ? orderId : oldOrderId;
|
|
var newIndex = direction ? startIndex : startIndex + 1;
|
|
var siblings = this.taskSiblings(task);
|
|
endIndex = Math.min(endIndex, siblings.length - 1);
|
|
for (var i = startIndex; i <= endIndex; i++) {
|
|
if (siblings[i] === task) {
|
|
continue;
|
|
}
|
|
siblings[i].set('orderId', newIndex);
|
|
newIndex += 1;
|
|
}
|
|
},
|
|
_updateSummary: function (task) {
|
|
if (task !== null) {
|
|
var childCount = this.taskChildren(task).length;
|
|
task.set('summary', childCount > 0);
|
|
}
|
|
},
|
|
_toGanttTask: function (task) {
|
|
if (!(task instanceof GanttTask)) {
|
|
var taskInfo = task;
|
|
task = this._createNewModel();
|
|
task.accept(taskInfo);
|
|
}
|
|
return task;
|
|
}
|
|
});
|
|
GanttDataSource.create = createDataSource(GanttDataSource, 'GanttDataSource');
|
|
extend(true, kendo.data, {
|
|
GanttDataSource: GanttDataSource,
|
|
GanttTask: GanttTask,
|
|
GanttDependencyDataSource: GanttDependencyDataSource,
|
|
GanttDependency: GanttDependency
|
|
});
|
|
var editors = {
|
|
desktop: {
|
|
dateRange: DATERANGEEDITOR,
|
|
resources: RESOURCESEDITOR
|
|
}
|
|
};
|
|
var Editor = kendo.Observable.extend({
|
|
init: function (element, options) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.element = element;
|
|
this.options = extend(true, {}, this.options, options);
|
|
this.createButton = this.options.createButton;
|
|
},
|
|
fields: function (editors, model) {
|
|
var that = this;
|
|
var options = this.options;
|
|
var messages = options.messages.editor;
|
|
var resources = options.resources;
|
|
var fields;
|
|
var click = function (e) {
|
|
e.preventDefault();
|
|
resources.editor(that.container.find(DOT + Gantt.styles.popup.resourcesField), model);
|
|
};
|
|
if (options.editable.template) {
|
|
fields = $.map(model.fields, function (value, key) {
|
|
return { field: key };
|
|
});
|
|
} else {
|
|
fields = [
|
|
{
|
|
field: 'title',
|
|
title: messages.title
|
|
},
|
|
{
|
|
field: 'start',
|
|
title: messages.start,
|
|
editor: editors.dateRange
|
|
},
|
|
{
|
|
field: 'end',
|
|
title: messages.end,
|
|
editor: editors.dateRange
|
|
},
|
|
{
|
|
field: 'percentComplete',
|
|
title: messages.percentComplete,
|
|
format: PERCENTAGE_FORMAT
|
|
}
|
|
];
|
|
if (model.get(resources.field)) {
|
|
fields.push({
|
|
field: resources.field,
|
|
title: messages.resources,
|
|
messages: messages,
|
|
editor: editors.resources,
|
|
click: click,
|
|
styles: Gantt.styles.popup
|
|
});
|
|
}
|
|
}
|
|
return fields;
|
|
},
|
|
_buildEditTemplate: function (model, fields, editableFields) {
|
|
var resources = this.options.resources;
|
|
var template = this.options.editable.template;
|
|
var settings = extend({}, kendo.Template, this.options.templateSettings);
|
|
var paramName = settings.paramName;
|
|
var popupStyles = Gantt.styles.popup;
|
|
var html = '';
|
|
if (template) {
|
|
if (typeof template === STRING) {
|
|
template = window.unescape(template);
|
|
}
|
|
html += kendo.template(template, settings)(model);
|
|
} else {
|
|
for (var i = 0, length = fields.length; i < length; i++) {
|
|
var field = fields[i];
|
|
html += '<div class="' + popupStyles.editLabel + '"><label for="' + field.field + '">' + (field.title || field.field || '') + '</label></div>';
|
|
if (field.field === resources.field) {
|
|
html += '<div class="' + popupStyles.resourcesField + '" style="display:none"></div>';
|
|
}
|
|
if (!model.editable || model.editable(field.field)) {
|
|
editableFields.push(field);
|
|
html += '<div ' + kendo.attr('container-for') + '="' + field.field + '" class="' + popupStyles.editField + '"></div>';
|
|
} else {
|
|
var tmpl = '#:';
|
|
if (field.field) {
|
|
field = kendo.expr(field.field, paramName);
|
|
tmpl += field + '==null?\'\':' + field;
|
|
} else {
|
|
tmpl += '\'\'';
|
|
}
|
|
tmpl += '#';
|
|
tmpl = kendo.template(tmpl, settings);
|
|
html += '<div class="' + popupStyles.editField + '">' + tmpl(model) + '</div>';
|
|
}
|
|
}
|
|
}
|
|
return html;
|
|
}
|
|
});
|
|
var PopupEditor = Editor.extend({
|
|
destroy: function () {
|
|
this.close();
|
|
this.unbind();
|
|
},
|
|
editTask: function (task) {
|
|
this.editable = this._createPopupEditor(task);
|
|
},
|
|
close: function () {
|
|
var that = this;
|
|
var destroy = function () {
|
|
if (that.editable) {
|
|
that.editable.destroy();
|
|
that.editable = null;
|
|
that.container = null;
|
|
}
|
|
if (that.popup) {
|
|
that.popup.destroy();
|
|
that.popup = null;
|
|
}
|
|
};
|
|
if (this.editable && this.container.is(':visible')) {
|
|
this.container.data('kendoWindow').bind('deactivate', destroy).close();
|
|
} else {
|
|
destroy();
|
|
}
|
|
},
|
|
showDialog: function (options) {
|
|
var buttons = options.buttons;
|
|
var popupStyles = Gantt.styles.popup;
|
|
var html = kendo.format('<div class="{0}"><div class="{1}"><p class="{2}">{3}</p><div class="{4}">', popupStyles.form, popupStyles.formContainer, popupStyles.message, options.text, popupStyles.buttonsContainer);
|
|
for (var i = 0, length = buttons.length; i < length; i++) {
|
|
html += this.createButton(buttons[i]);
|
|
}
|
|
html += '</div></div></div>';
|
|
var wrapper = this.element;
|
|
if (this.popup) {
|
|
this.popup.destroy();
|
|
}
|
|
var popup = this.popup = $(html).appendTo(wrapper).eq(0).on('click', DOT + popupStyles.button, function (e) {
|
|
e.preventDefault();
|
|
popup.close();
|
|
var buttonIndex = $(e.currentTarget).index();
|
|
buttons[buttonIndex].click();
|
|
}).kendoWindow({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: false,
|
|
title: options.title,
|
|
visible: false,
|
|
close: function () {
|
|
this.destroy();
|
|
wrapper.focus();
|
|
}
|
|
}).getKendoWindow();
|
|
popup.center().open();
|
|
},
|
|
_createPopupEditor: function (task) {
|
|
var that = this;
|
|
var options = {};
|
|
var messages = this.options.messages;
|
|
var ganttStyles = Gantt.styles;
|
|
var popupStyles = ganttStyles.popup;
|
|
var html = kendo.format('<div {0}="{1}" class="{2} {3}"><div class="{4}">', kendo.attr('uid'), task.uid, popupStyles.form, popupStyles.editForm, popupStyles.formContainer);
|
|
var fields = this.fields(editors.desktop, task);
|
|
var editableFields = [];
|
|
html += this._buildEditTemplate(task, fields, editableFields);
|
|
html += '<div class="' + popupStyles.buttonsContainer + '">';
|
|
html += this.createButton({
|
|
name: 'update',
|
|
text: messages.save,
|
|
className: Gantt.styles.primary
|
|
});
|
|
html += this.createButton({
|
|
name: 'cancel',
|
|
text: messages.cancel
|
|
});
|
|
html += this.createButton({
|
|
name: 'delete',
|
|
text: messages.destroy
|
|
});
|
|
html += '</div></div></div>';
|
|
var container = this.container = $(html).appendTo(this.element).eq(0).kendoWindow(extend({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: messages.editor.editorTitle,
|
|
visible: false,
|
|
close: function (e) {
|
|
if (e.userTriggered) {
|
|
if (that.trigger('cancel', {
|
|
container: container,
|
|
model: task
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
}, options));
|
|
var editableWidget = container.kendoEditable({
|
|
fields: editableFields,
|
|
model: task,
|
|
clearContainer: false,
|
|
validateOnBlur: true,
|
|
target: that.options.target
|
|
}).data('kendoEditable');
|
|
kendo.cycleForm(container);
|
|
if (!this.trigger('edit', {
|
|
container: container,
|
|
model: task
|
|
})) {
|
|
container.data('kendoWindow').center().open();
|
|
container.on(CLICK + NS, DOT + ganttStyles.buttonCancel, function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.trigger('cancel', {
|
|
container: container,
|
|
model: task
|
|
});
|
|
});
|
|
container.on(CLICK + NS, DOT + ganttStyles.buttonSave, function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var fields = that.fields(editors.desktop, task);
|
|
var updateInfo = {};
|
|
var field;
|
|
for (var i = 0, length = fields.length; i < length; i++) {
|
|
field = fields[i].field;
|
|
updateInfo[field] = task.get(field);
|
|
}
|
|
that.trigger('save', {
|
|
container: container,
|
|
model: task,
|
|
updateInfo: updateInfo
|
|
});
|
|
});
|
|
container.on(CLICK + NS, DOT + ganttStyles.buttonDelete, function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
that.trigger('remove', {
|
|
container: container,
|
|
model: task
|
|
});
|
|
});
|
|
} else {
|
|
that.trigger('cancel', {
|
|
container: container,
|
|
model: task
|
|
});
|
|
}
|
|
return editableWidget;
|
|
}
|
|
});
|
|
var ResourceEditor = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.wrapper = this.element;
|
|
this.model = this.options.model;
|
|
this.resourcesField = this.options.resourcesField;
|
|
this.createButton = this.options.createButton;
|
|
this._initContainer();
|
|
this._attachHandlers();
|
|
},
|
|
events: ['save'],
|
|
open: function () {
|
|
this.window.center().open();
|
|
this.grid.resize(true);
|
|
},
|
|
close: function () {
|
|
this.window.bind('deactivate', proxy(this.destroy, this)).close();
|
|
},
|
|
destroy: function () {
|
|
this._dettachHandlers();
|
|
this.grid.destroy();
|
|
this.grid = null;
|
|
this.window.destroy();
|
|
this.window = null;
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.wrapper);
|
|
this.element = this.wrapper = null;
|
|
},
|
|
_attachHandlers: function () {
|
|
var ganttStyles = Gantt.styles;
|
|
var grid = this.grid;
|
|
var closeHandler = this._cancelProxy = proxy(this._cancel, this);
|
|
this.container.on(CLICK + NS, DOT + ganttStyles.buttonCancel, this._cancelProxy);
|
|
this._saveProxy = proxy(this._save, this);
|
|
this.container.on(CLICK + NS, DOT + ganttStyles.buttonSave, this._saveProxy);
|
|
this.window.bind('close', function (e) {
|
|
if (e.userTriggered) {
|
|
closeHandler(e);
|
|
}
|
|
});
|
|
grid.wrapper.on(CLICK + NS, 'input[type=\'checkbox\']', function () {
|
|
var element = $(this);
|
|
var row = $(element).closest('tr');
|
|
var model = grid.dataSource.getByUid(row.attr(kendo.attr('uid')));
|
|
var value = $(element).is(':checked') ? 1 : '';
|
|
model.set('value', value);
|
|
});
|
|
},
|
|
_dettachHandlers: function () {
|
|
this._cancelProxy = null;
|
|
this._saveProxy = null;
|
|
this.container.off(NS);
|
|
this.grid.wrapper.off();
|
|
},
|
|
_cancel: function (e) {
|
|
e.preventDefault();
|
|
this.close();
|
|
},
|
|
_save: function (e) {
|
|
e.preventDefault();
|
|
this._updateModel();
|
|
if (!this.wrapper.is(DOT + Gantt.styles.popup.resourcesField)) {
|
|
this.trigger('save', {
|
|
container: this.wrapper,
|
|
model: this.model
|
|
});
|
|
}
|
|
this.close();
|
|
},
|
|
_initContainer: function () {
|
|
var popupStyles = Gantt.styles.popup;
|
|
var dom = kendo.format('<div class="{0} {1}"><div class="{2} {3}"/></div>"', popupStyles.form, popupStyles.editForm, popupStyles.formContainer, popupStyles.resourcesFormContainer);
|
|
dom = $(dom);
|
|
this.container = dom.find(DOT + popupStyles.resourcesFormContainer);
|
|
this.window = dom.kendoWindow({
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
visible: false,
|
|
title: this.options.messages.resourcesEditorTitle
|
|
}).data('kendoWindow');
|
|
this._resourceGrid();
|
|
this._createButtons();
|
|
},
|
|
_resourceGrid: function () {
|
|
var that = this;
|
|
var messages = this.options.messages;
|
|
var element = $('<div id="resources-grid"/>').appendTo(this.container);
|
|
this.grid = new kendo.ui.Grid(element, {
|
|
columns: [
|
|
{
|
|
field: 'name',
|
|
title: messages.resourcesHeader,
|
|
template: '<label><input type=\'checkbox\' value=\'#=name#\'' + '# if (value > 0 && value !== null) {#' + 'checked=\'checked\'' + '# } #' + '/>#=name#</labe>'
|
|
},
|
|
{
|
|
field: 'value',
|
|
title: messages.unitsHeader,
|
|
template: function (dataItem) {
|
|
var valueFormat = dataItem.format;
|
|
var value = dataItem.value !== null ? dataItem.value : '';
|
|
return valueFormat ? kendo.toString(value, valueFormat) : value;
|
|
}
|
|
}
|
|
],
|
|
height: 280,
|
|
sortable: true,
|
|
editable: true,
|
|
filterable: true,
|
|
dataSource: {
|
|
data: that.options.data,
|
|
schema: {
|
|
model: {
|
|
id: 'id',
|
|
fields: {
|
|
id: { from: 'id' },
|
|
name: {
|
|
from: 'name',
|
|
type: 'string',
|
|
editable: false
|
|
},
|
|
value: {
|
|
from: 'value',
|
|
type: 'number',
|
|
defaultValue: ''
|
|
},
|
|
format: {
|
|
from: 'format',
|
|
type: 'string'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
save: function (e) {
|
|
var value = !!e.values.value;
|
|
e.container.parent().find('input[type=\'checkbox\']').prop('checked', value);
|
|
}
|
|
});
|
|
},
|
|
_createButtons: function () {
|
|
var buttons = this.options.buttons;
|
|
var html = '<div class="' + Gantt.styles.popup.buttonsContainer + '">';
|
|
for (var i = 0, length = buttons.length; i < length; i++) {
|
|
html += this.createButton(buttons[i]);
|
|
}
|
|
html += '</div>';
|
|
this.container.append(html);
|
|
},
|
|
_updateModel: function () {
|
|
var resources = [];
|
|
var value;
|
|
var data = this.grid.dataSource.data();
|
|
for (var i = 0, length = data.length; i < length; i++) {
|
|
value = data[i].get('value');
|
|
if (value !== null && value > 0) {
|
|
resources.push(data[i]);
|
|
}
|
|
}
|
|
this.model[this.resourcesField] = resources;
|
|
}
|
|
});
|
|
var Gantt = Widget.extend({
|
|
init: function (element, options, events) {
|
|
if (isArray(options)) {
|
|
options = { dataSource: options };
|
|
}
|
|
defaultCommands = {
|
|
append: {
|
|
text: 'Add Task',
|
|
action: 'add',
|
|
className: Gantt.styles.toolbar.appendButton,
|
|
iconClass: Gantt.styles.toolbar.iconPlus
|
|
},
|
|
pdf: {
|
|
text: 'Export to PDF',
|
|
className: Gantt.styles.toolbar.pdfButton,
|
|
iconClass: Gantt.styles.toolbar.iconPdf
|
|
}
|
|
};
|
|
Widget.fn.init.call(this, element, options);
|
|
if (events) {
|
|
this._events = events;
|
|
}
|
|
this._wrapper();
|
|
this._resources();
|
|
if (!this.options.views || !this.options.views.length) {
|
|
this.options.views = [
|
|
'day',
|
|
'week',
|
|
'month'
|
|
];
|
|
}
|
|
this._timeline();
|
|
this._toolbar();
|
|
this._footer();
|
|
this._adjustDimensions();
|
|
this._preventRefresh = true;
|
|
this.view(this.timeline._selectedViewName);
|
|
this._preventRefresh = false;
|
|
this._dataSource();
|
|
this._assignments();
|
|
this._dropDowns();
|
|
this._list();
|
|
this._dependencies();
|
|
this._resizable();
|
|
this._scrollable();
|
|
this._dataBind();
|
|
this._attachEvents();
|
|
this._createEditor();
|
|
kendo.notify(this);
|
|
},
|
|
events: [
|
|
'dataBinding',
|
|
'dataBound',
|
|
'add',
|
|
'edit',
|
|
'remove',
|
|
'cancel',
|
|
'save',
|
|
'change',
|
|
'navigate',
|
|
'moveStart',
|
|
'move',
|
|
'moveEnd',
|
|
'resizeStart',
|
|
'resize',
|
|
'resizeEnd',
|
|
'columnResize'
|
|
],
|
|
options: {
|
|
name: 'Gantt',
|
|
autoBind: true,
|
|
navigatable: false,
|
|
selectable: true,
|
|
editable: true,
|
|
resizable: false,
|
|
columnResizeHandleWidth: defaultIndicatorWidth,
|
|
columns: [],
|
|
views: [],
|
|
dataSource: {},
|
|
dependencies: {},
|
|
resources: {},
|
|
assignments: {},
|
|
taskTemplate: null,
|
|
messages: {
|
|
save: 'Save',
|
|
cancel: 'Cancel',
|
|
destroy: 'Delete',
|
|
deleteTaskConfirmation: TASK_DELETE_CONFIRM,
|
|
deleteDependencyConfirmation: DEPENDENCY_DELETE_CONFIRM,
|
|
deleteTaskWindowTitle: 'Delete task',
|
|
deleteDependencyWindowTitle: 'Delete dependency',
|
|
views: {
|
|
day: 'Day',
|
|
week: 'Week',
|
|
month: 'Month',
|
|
year: 'Year',
|
|
start: 'Start',
|
|
end: 'End'
|
|
},
|
|
actions: {
|
|
append: 'Add Task',
|
|
addChild: 'Add Child',
|
|
insertBefore: 'Add Above',
|
|
insertAfter: 'Add Below',
|
|
pdf: 'Export to PDF'
|
|
},
|
|
editor: {
|
|
editorTitle: 'Task',
|
|
resourcesEditorTitle: 'Resources',
|
|
title: 'Title',
|
|
start: 'Start',
|
|
end: 'End',
|
|
percentComplete: 'Complete',
|
|
resources: 'Resources',
|
|
assignButton: 'Assign',
|
|
resourcesHeader: 'Resources',
|
|
unitsHeader: 'Units'
|
|
}
|
|
},
|
|
showWorkHours: true,
|
|
showWorkDays: true,
|
|
toolbar: null,
|
|
workDayStart: new Date(1980, 1, 1, 8, 0, 0),
|
|
workDayEnd: new Date(1980, 1, 1, 17, 0, 0),
|
|
workWeekStart: 1,
|
|
workWeekEnd: 5,
|
|
hourSpan: 1,
|
|
snap: true,
|
|
height: 600,
|
|
listWidth: '30%',
|
|
rowHeight: null
|
|
},
|
|
select: function (value) {
|
|
var list = this.list;
|
|
if (!value) {
|
|
return list.select();
|
|
}
|
|
list.select(value);
|
|
return;
|
|
},
|
|
clearSelection: function () {
|
|
this.list.clearSelection();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
if (this.dataSource) {
|
|
this.dataSource.unbind('change', this._refreshHandler);
|
|
this.dataSource.unbind('progress', this._progressHandler);
|
|
this.dataSource.unbind('error', this._errorHandler);
|
|
}
|
|
if (this.dependencies) {
|
|
this.dependencies.unbind('change', this._dependencyRefreshHandler);
|
|
this.dependencies.unbind('error', this._dependencyErrorHandler);
|
|
}
|
|
if (this.timeline) {
|
|
this.timeline.unbind();
|
|
this.timeline.destroy();
|
|
}
|
|
if (this.list) {
|
|
this.list.unbind();
|
|
this.list.destroy();
|
|
}
|
|
if (this.footerDropDown) {
|
|
this.footerDropDown.destroy();
|
|
}
|
|
if (this.headerDropDown) {
|
|
this.headerDropDown.destroy();
|
|
}
|
|
if (this._editor) {
|
|
this._editor.destroy();
|
|
}
|
|
if (this._resizeDraggable) {
|
|
this._resizeDraggable.destroy();
|
|
}
|
|
this.toolbar.off(NS);
|
|
if (supportsMedia) {
|
|
this._mediaQuery.removeListener(this._mediaQueryHandler);
|
|
this._mediaQuery = null;
|
|
}
|
|
$(window).off('resize' + NS, this._resizeHandler);
|
|
$(this.wrapper).off(NS);
|
|
this.toolbar = null;
|
|
this.footer = null;
|
|
},
|
|
setOptions: function (options) {
|
|
var newOptions = kendo.deepExtend({}, this.options, options);
|
|
var events = this._events;
|
|
if (!options.views) {
|
|
var selectedView = this.view().name;
|
|
newOptions.views = $.map(this.options.views, function (view) {
|
|
var isSettings = isPlainObject(view);
|
|
var name = isSettings ? typeof view.type !== 'string' ? view.title : view.type : view;
|
|
if (selectedView === name) {
|
|
if (isSettings) {
|
|
view.selected = true;
|
|
} else {
|
|
view = {
|
|
type: name,
|
|
selected: true
|
|
};
|
|
}
|
|
} else if (isSettings) {
|
|
view.selected = false;
|
|
}
|
|
return view;
|
|
});
|
|
}
|
|
if (!options.dataSource) {
|
|
newOptions.dataSource = this.dataSource;
|
|
}
|
|
if (!options.dependencies) {
|
|
newOptions.dependencies = this.dependencies;
|
|
}
|
|
if (!options.resources) {
|
|
newOptions.resources = this.resources;
|
|
}
|
|
if (!options.assignments) {
|
|
newOptions.assignments = this.assignments;
|
|
}
|
|
this.destroy();
|
|
this.element.empty();
|
|
this.options = null;
|
|
this.init(this.element, newOptions, events);
|
|
Widget.fn._setEvents.call(this, newOptions);
|
|
},
|
|
_attachEvents: function () {
|
|
this._resizeHandler = proxy(this.resize, this, false);
|
|
$(window).on('resize' + NS, this._resizeHandler);
|
|
},
|
|
_wrapper: function () {
|
|
var ganttStyles = Gantt.styles;
|
|
var splitBarHandleClassName = [
|
|
ganttStyles.icon,
|
|
ganttStyles.resizeHandle
|
|
].join(' ');
|
|
var options = this.options;
|
|
var height = options.height;
|
|
var width = options.width;
|
|
this.wrapper = this.element.addClass(ganttStyles.wrapper).append('<div class=\'' + ganttStyles.listWrapper + '\'><div></div></div>').append('<div class=\'' + ganttStyles.splitBarWrapper + '\'><div class=\'' + splitBarHandleClassName + '\'></div></div>').append('<div class=\'' + ganttStyles.timelineWrapper + '\'><div></div></div>');
|
|
this.wrapper.find(DOT + ganttStyles.list).width(options.listWidth);
|
|
if (height) {
|
|
this.wrapper.height(height);
|
|
}
|
|
if (width) {
|
|
this.wrapper.width(width);
|
|
}
|
|
if (options.rowHeight) {
|
|
this.wrapper.addClass(ganttStyles.rowHeight);
|
|
}
|
|
},
|
|
_toolbar: function () {
|
|
var that = this;
|
|
var ganttStyles = Gantt.styles;
|
|
var viewsSelector = DOT + ganttStyles.toolbar.views + ' > li';
|
|
var pdfSelector = DOT + ganttStyles.toolbar.pdfButton;
|
|
var toggleSelector = DOT + ganttStyles.buttonToggle;
|
|
var contentSelector = DOT + ganttStyles.gridContent;
|
|
var treelist = $(DOT + ganttStyles.list);
|
|
var timeline = $(DOT + ganttStyles.timeline);
|
|
var hoveredClassName = ganttStyles.hovered;
|
|
var actions = this.options.toolbar;
|
|
var actionsWrap = $('<div class=\'' + ganttStyles.toolbar.actions + '\'>');
|
|
var toolbar;
|
|
var views;
|
|
var toggleButton;
|
|
var handler = function (e) {
|
|
if (e.matches) {
|
|
treelist.css({
|
|
'display': 'none',
|
|
'max-width': 0
|
|
});
|
|
} else {
|
|
treelist.css({
|
|
'display': 'inline-block',
|
|
'width': '30%',
|
|
'max-width': 'none'
|
|
});
|
|
timeline.css('display', 'inline-block');
|
|
that.refresh();
|
|
timeline.find(contentSelector).scrollTop(that.scrollTop);
|
|
}
|
|
that._resize();
|
|
};
|
|
if (!isFunction(actions)) {
|
|
actions = typeof actions === STRING ? actions : this._actions(actions);
|
|
actions = proxy(kendo.template(actions), this);
|
|
}
|
|
toggleButton = $(TOGGLE_BUTTON_TEMPLATE({ styles: ganttStyles.toolbar }));
|
|
views = $(HEADER_VIEWS_TEMPLATE({
|
|
ns: kendo.ns,
|
|
views: this.timeline.views,
|
|
styles: ganttStyles.toolbar
|
|
}));
|
|
actionsWrap.append(actions({}));
|
|
toolbar = $('<div class=\'' + ganttStyles.toolbar.headerWrapper + '\'>').append(toggleButton).append(views).append(actionsWrap);
|
|
if (views.find('li').length > 1) {
|
|
views.prepend(VIEWBUTTONTEMPLATE({ styles: ganttStyles.toolbar }));
|
|
}
|
|
this.wrapper.prepend(toolbar);
|
|
this.toolbar = toolbar;
|
|
if (supportsMedia) {
|
|
this._mediaQueryHandler = proxy(handler, this);
|
|
this._mediaQuery = window.matchMedia('(max-width: 480px)');
|
|
this._mediaQuery.addListener(this._mediaQueryHandler);
|
|
}
|
|
toolbar.on(CLICK + NS, viewsSelector, function (e) {
|
|
e.preventDefault();
|
|
var list = that.list;
|
|
var name = $(this).attr(kendo.attr('name'));
|
|
var currentView = views.find(DOT + ganttStyles.toolbar.currentView);
|
|
if (currentView.is(':visible')) {
|
|
currentView.parent().toggleClass(ganttStyles.toolbar.expanded);
|
|
}
|
|
if (list.editable && list.editable.trigger('validate')) {
|
|
return;
|
|
}
|
|
if (!that.trigger('navigate', { view: name })) {
|
|
that.view(name);
|
|
}
|
|
}).on(CLICK + NS, pdfSelector, function (e) {
|
|
e.preventDefault();
|
|
that.saveAsPDF();
|
|
}).on(CLICK + NS, toggleSelector, function (e) {
|
|
e.preventDefault();
|
|
if (treelist.is(':visible')) {
|
|
treelist.css({
|
|
'display': 'none',
|
|
'width': '0'
|
|
});
|
|
timeline.css({
|
|
'display': 'inline-block',
|
|
'width': '100%'
|
|
});
|
|
that.refresh();
|
|
timeline.find(contentSelector).scrollTop(that.scrollTop);
|
|
} else {
|
|
timeline.css({
|
|
'display': 'none',
|
|
'width': 0
|
|
});
|
|
treelist.css({
|
|
'display': 'inline-block',
|
|
'width': '100%',
|
|
'max-width': 'none'
|
|
}).find(contentSelector).scrollTop(that.scrollTop);
|
|
}
|
|
that._resize();
|
|
});
|
|
this.wrapper.find(DOT + ganttStyles.toolbar.toolbar + ' li').hover(function () {
|
|
$(this).addClass(hoveredClassName);
|
|
}, function () {
|
|
$(this).removeClass(hoveredClassName);
|
|
});
|
|
},
|
|
_actions: function () {
|
|
var options = this.options;
|
|
var actions = options.toolbar;
|
|
var html = '';
|
|
if (!isArray(actions)) {
|
|
if (options.editable) {
|
|
actions = ['append'];
|
|
} else {
|
|
return html;
|
|
}
|
|
}
|
|
for (var i = 0, length = actions.length; i < length; i++) {
|
|
html += this._createButton(actions[i]);
|
|
}
|
|
return html;
|
|
},
|
|
_footer: function () {
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
var ganttStyles = Gantt.styles.toolbar;
|
|
var messages = this.options.messages.actions;
|
|
var button = $(kendo.template(BUTTON_TEMPLATE)(extend(true, { styles: ganttStyles }, defaultCommands.append, { text: messages.append })));
|
|
var actionsWrap = $('<div class=\'' + ganttStyles.actions + '\'>').append(button);
|
|
var footer = $('<div class=\'' + ganttStyles.footerWrapper + '\'>').append(actionsWrap);
|
|
this.wrapper.append(footer);
|
|
this.footer = footer;
|
|
},
|
|
_createButton: function (command) {
|
|
var template = command.template || BUTTON_TEMPLATE;
|
|
var messages = this.options.messages.actions;
|
|
var commandName = typeof command === STRING ? command : command.name || command.text;
|
|
var className = defaultCommands[commandName] ? defaultCommands[commandName].className : 'k-gantt-' + (commandName || '').replace(/\s/g, '');
|
|
var options = {
|
|
iconClass: '',
|
|
action: '',
|
|
text: commandName,
|
|
className: className,
|
|
styles: Gantt.styles.toolbar
|
|
};
|
|
if (!commandName && !(isPlainObject(command) && command.template)) {
|
|
throw new Error('Custom commands should have name specified');
|
|
}
|
|
options = extend(true, options, defaultCommands[commandName], { text: messages[commandName] });
|
|
if (isPlainObject(command)) {
|
|
if (command.className && inArray(options.className, command.className.split(' ')) < 0) {
|
|
command.className += ' ' + options.className;
|
|
}
|
|
options = extend(true, options, command);
|
|
}
|
|
return kendo.template(template)(options);
|
|
},
|
|
_adjustDimensions: function () {
|
|
var element = this.element;
|
|
var ganttStyles = Gantt.styles;
|
|
var listSelector = DOT + ganttStyles.list;
|
|
var timelineSelector = DOT + ganttStyles.timeline;
|
|
var splitBarSelector = DOT + ganttStyles.splitBar;
|
|
var toolbarHeight = this.toolbar.outerHeight();
|
|
var footerHeight = this.footer ? this.footer.outerHeight() : 0;
|
|
var totalHeight = element.height();
|
|
var totalWidth = element.width();
|
|
var splitBarWidth = element.find(splitBarSelector).outerWidth();
|
|
var treeListWidth = element.find(listSelector).outerWidth();
|
|
element.children([
|
|
listSelector,
|
|
timelineSelector,
|
|
splitBarSelector
|
|
].join(',')).height(totalHeight - (toolbarHeight + footerHeight)).end().children(timelineSelector).width(totalWidth - (splitBarWidth + treeListWidth));
|
|
if (totalWidth < treeListWidth + splitBarWidth) {
|
|
element.find(listSelector).width(totalWidth - splitBarWidth);
|
|
}
|
|
},
|
|
_scrollTo: function (value) {
|
|
var view = this.timeline.view();
|
|
var list = this.list;
|
|
var attr = kendo.attr('uid');
|
|
var id = typeof value === 'string' ? value : value.closest('tr' + selector()).attr(attr);
|
|
var action;
|
|
var scrollTarget;
|
|
var scrollIntoView = function () {
|
|
if (scrollTarget.length !== 0) {
|
|
action();
|
|
}
|
|
};
|
|
if (view.content.is(':visible')) {
|
|
scrollTarget = view.content.find(selector(id));
|
|
action = function () {
|
|
view._scrollTo(scrollTarget);
|
|
};
|
|
} else {
|
|
scrollTarget = list.content.find(selector(id));
|
|
action = function () {
|
|
scrollTarget.get(0).scrollIntoView();
|
|
};
|
|
}
|
|
scrollIntoView();
|
|
},
|
|
_dropDowns: function () {
|
|
var that = this;
|
|
var actionsSelector = DOT + Gantt.styles.toolbar.actions;
|
|
var actionMessages = this.options.messages.actions;
|
|
var timeline = this.timeline;
|
|
var handler = function (e) {
|
|
var type = e.type;
|
|
var orderId;
|
|
var dataSource = that.dataSource;
|
|
var task = dataSource._createNewModel();
|
|
var selected = that.dataItem(that.select());
|
|
var parent = dataSource.taskParent(selected);
|
|
var firstSlot = timeline.view()._timeSlots()[0];
|
|
var target = type === 'add' ? selected : parent;
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
return;
|
|
}
|
|
task.set('title', 'New task');
|
|
if (target) {
|
|
task.set('parentId', target.get('id'));
|
|
task.set('start', target.get('start'));
|
|
task.set('end', target.get('end'));
|
|
} else {
|
|
task.set('start', firstSlot.start);
|
|
task.set('end', firstSlot.end);
|
|
}
|
|
if (type !== 'add') {
|
|
orderId = selected.get('orderId');
|
|
orderId = type === 'insert-before' ? orderId : orderId + 1;
|
|
}
|
|
that._createTask(task, orderId);
|
|
};
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
this.footerDropDown = new TaskDropDown(this.footer.children(actionsSelector).eq(0), {
|
|
messages: { actions: actionMessages },
|
|
direction: 'up',
|
|
animation: { open: { effects: 'slideIn:up' } },
|
|
navigatable: that.options.navigatable
|
|
});
|
|
this.headerDropDown = new TaskDropDown(this.toolbar.children(actionsSelector).eq(0), {
|
|
messages: { actions: actionMessages },
|
|
navigatable: that.options.navigatable
|
|
});
|
|
this.footerDropDown.bind('command', handler);
|
|
this.headerDropDown.bind('command', handler);
|
|
},
|
|
_list: function () {
|
|
var that = this;
|
|
var navigatable = that.options.navigatable;
|
|
var ganttStyles = Gantt.styles;
|
|
var listWrapper = this.wrapper.find(DOT + ganttStyles.list);
|
|
var element = listWrapper.find('> div');
|
|
var toggleButtons = this.wrapper.find(DOT + ganttStyles.toolbar.actions + ' > button');
|
|
var options = {
|
|
columns: this.options.columns || [],
|
|
dataSource: this.dataSource,
|
|
selectable: this.options.selectable,
|
|
editable: this.options.editable,
|
|
resizable: this.options.resizable,
|
|
columnResizeHandleWidth: this.options.columnResizeHandleWidth,
|
|
listWidth: listWrapper.outerWidth(),
|
|
resourcesField: this.resources.field,
|
|
rowHeight: this.options.rowHeight
|
|
};
|
|
var columns = options.columns;
|
|
var column;
|
|
var restoreFocus = function () {
|
|
if (navigatable) {
|
|
that._current(that._cachedCurrent);
|
|
focusTable(that.list.content.find('table'), true);
|
|
}
|
|
delete that._cachedCurrent;
|
|
};
|
|
for (var i = 0; i < columns.length; i++) {
|
|
column = columns[i];
|
|
if (column.field === this.resources.field && typeof column.editor !== 'function') {
|
|
column.editor = proxy(this._createResourceEditor, this);
|
|
}
|
|
}
|
|
this.list = new kendo.ui.GanttList(element, options);
|
|
this.list.bind('render', function () {
|
|
that._navigatable();
|
|
}, true).bind('edit', function (e) {
|
|
that._cachedCurrent = e.cell;
|
|
if (that.trigger('edit', {
|
|
task: e.model,
|
|
container: e.cell
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('cancel', function (e) {
|
|
if (that.trigger('cancel', {
|
|
task: e.model,
|
|
container: e.cell
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
restoreFocus();
|
|
}).bind('update', function (e) {
|
|
that._updateTask(e.task, e.updateInfo);
|
|
restoreFocus();
|
|
}).bind('change', function () {
|
|
that.trigger('change');
|
|
var selection = that.list.select();
|
|
if (selection.length) {
|
|
toggleButtons.removeAttr('data-action', 'add');
|
|
that.timeline.select('[data-uid=\'' + selection.attr('data-uid') + '\']');
|
|
} else {
|
|
toggleButtons.attr('data-action', 'add');
|
|
that.timeline.clearSelection();
|
|
}
|
|
}).bind('columnResize', function (e) {
|
|
that.trigger('columnResize', {
|
|
column: e.column,
|
|
oldWidth: e.oldWidth,
|
|
newWidth: e.newWidth
|
|
});
|
|
});
|
|
},
|
|
_timeline: function () {
|
|
var that = this;
|
|
var ganttStyles = Gantt.styles;
|
|
var options = trimOptions(extend(true, { resourcesField: this.resources.field }, this.options));
|
|
var element = this.wrapper.find(DOT + ganttStyles.timeline + ' > div');
|
|
var currentViewSelector = DOT + ganttStyles.toolbar.currentView + ' > ' + DOT + ganttStyles.toolbar.link;
|
|
this.timeline = new kendo.ui.GanttTimeline(element, options);
|
|
this.timeline.bind('navigate', function (e) {
|
|
var viewName = e.view.replace(/\./g, '\\.').toLowerCase();
|
|
var text = that.toolbar.find(DOT + ganttStyles.toolbar.views + ' > li').removeClass(ganttStyles.selected).end().find(DOT + ganttStyles.toolbar.viewButton + '-' + viewName).addClass(ganttStyles.selected).find(DOT + ganttStyles.toolbar.link).text();
|
|
that.toolbar.find(currentViewSelector).text(text);
|
|
that.refresh();
|
|
}).bind('moveStart', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (that.trigger('moveStart', { task: e.task })) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('move', function (e) {
|
|
var task = e.task;
|
|
var start = e.start;
|
|
var end = new Date(start.getTime() + task.duration());
|
|
if (that.trigger('move', {
|
|
task: task,
|
|
start: start,
|
|
end: end
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('moveEnd', function (e) {
|
|
var task = e.task;
|
|
var start = e.start;
|
|
var end = new Date(start.getTime() + task.duration());
|
|
if (!that.trigger('moveEnd', {
|
|
task: task,
|
|
start: start,
|
|
end: end
|
|
})) {
|
|
that._updateTask(that.dataSource.getByUid(task.uid), {
|
|
start: start,
|
|
end: end
|
|
});
|
|
}
|
|
}).bind('resizeStart', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (that.trigger('resizeStart', { task: e.task })) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('resize', function (e) {
|
|
if (that.trigger('resize', {
|
|
task: e.task,
|
|
start: e.start,
|
|
end: e.end
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('resizeEnd', function (e) {
|
|
var task = e.task;
|
|
var updateInfo = {};
|
|
if (e.resizeStart) {
|
|
updateInfo.start = e.start;
|
|
} else {
|
|
updateInfo.end = e.end;
|
|
}
|
|
if (!that.trigger('resizeEnd', {
|
|
task: task,
|
|
start: e.start,
|
|
end: e.end
|
|
})) {
|
|
that._updateTask(that.dataSource.getByUid(task.uid), updateInfo);
|
|
}
|
|
}).bind('percentResizeStart', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('percentResizeEnd', function (e) {
|
|
that._updateTask(that.dataSource.getByUid(e.task.uid), { percentComplete: e.percentComplete });
|
|
}).bind('dependencyDragStart', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('dependencyDragEnd', function (e) {
|
|
var dependency = that.dependencies._createNewModel({
|
|
type: e.type,
|
|
predecessorId: e.predecessor.id,
|
|
successorId: e.successor.id
|
|
});
|
|
that._createDependency(dependency);
|
|
}).bind('select', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable) {
|
|
editable.trigger('validate');
|
|
}
|
|
that.select('[data-uid=\'' + e.uid + '\']');
|
|
}).bind('editTask', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
return;
|
|
}
|
|
that.editTask(e.uid);
|
|
}).bind('clear', function () {
|
|
that.clearSelection();
|
|
}).bind('removeTask', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
return;
|
|
}
|
|
that.removeTask(that.dataSource.getByUid(e.uid));
|
|
}).bind('removeDependency', function (e) {
|
|
var editable = that.list.editable;
|
|
if (editable && editable.trigger('validate')) {
|
|
return;
|
|
}
|
|
that.removeDependency(that.dependencies.getByUid(e.uid));
|
|
});
|
|
},
|
|
_dataSource: function () {
|
|
var options = this.options;
|
|
var dataSource = options.dataSource;
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (this.dataSource && this._refreshHandler) {
|
|
this.dataSource.unbind('change', this._refreshHandler).unbind('progress', this._progressHandler).unbind('error', this._errorHandler);
|
|
} else {
|
|
this._refreshHandler = proxy(this.refresh, this);
|
|
this._progressHandler = proxy(this._requestStart, this);
|
|
this._errorHandler = proxy(this._error, this);
|
|
}
|
|
this.dataSource = kendo.data.GanttDataSource.create(dataSource).bind('change', this._refreshHandler).bind('progress', this._progressHandler).bind('error', this._errorHandler);
|
|
},
|
|
_dependencies: function () {
|
|
var dependencies = this.options.dependencies || {};
|
|
var dataSource = isArray(dependencies) ? { data: dependencies } : dependencies;
|
|
if (this.dependencies && this._dependencyRefreshHandler) {
|
|
this.dependencies.unbind('change', this._dependencyRefreshHandler).unbind('error', this._dependencyErrorHandler);
|
|
} else {
|
|
this._dependencyRefreshHandler = proxy(this.refreshDependencies, this);
|
|
this._dependencyErrorHandler = proxy(this._error, this);
|
|
}
|
|
this.dependencies = kendo.data.GanttDependencyDataSource.create(dataSource).bind('change', this._dependencyRefreshHandler).bind('error', this._dependencyErrorHandler);
|
|
},
|
|
_resources: function () {
|
|
var resources = this.options.resources;
|
|
var dataSource = resources.dataSource || {};
|
|
this.resources = {
|
|
field: 'resources',
|
|
dataTextField: 'name',
|
|
dataColorField: 'color',
|
|
dataFormatField: 'format'
|
|
};
|
|
extend(this.resources, resources);
|
|
this.resources.dataSource = kendo.data.DataSource.create(dataSource);
|
|
},
|
|
_assignments: function () {
|
|
var assignments = this.options.assignments;
|
|
var dataSource = assignments.dataSource || {};
|
|
if (this.assignments) {
|
|
this.assignments.dataSource.unbind('change', this._assignmentsRefreshHandler);
|
|
} else {
|
|
this._assignmentsRefreshHandler = proxy(this.refresh, this);
|
|
}
|
|
this.assignments = {
|
|
dataTaskIdField: 'taskId',
|
|
dataResourceIdField: 'resourceId',
|
|
dataValueField: 'value'
|
|
};
|
|
extend(this.assignments, assignments);
|
|
this.assignments.dataSource = kendo.data.DataSource.create(dataSource);
|
|
this.assignments.dataSource.bind('change', this._assignmentsRefreshHandler);
|
|
},
|
|
_createEditor: function () {
|
|
var that = this;
|
|
var editor = this._editor = new PopupEditor(this.wrapper, extend({}, this.options, {
|
|
target: this,
|
|
resources: {
|
|
field: this.resources.field,
|
|
editor: proxy(this._createResourceEditor, this)
|
|
},
|
|
createButton: proxy(this._createPopupButton, this)
|
|
}));
|
|
editor.bind('cancel', function (e) {
|
|
var task = that.dataSource.getByUid(e.model.uid);
|
|
if (that.trigger('cancel', {
|
|
container: e.container,
|
|
task: task
|
|
})) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
that.cancelTask();
|
|
}).bind('edit', function (e) {
|
|
var task = that.dataSource.getByUid(e.model.uid);
|
|
if (that.trigger('edit', {
|
|
container: e.container,
|
|
task: task
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
}).bind('save', function (e) {
|
|
var task = that.dataSource.getByUid(e.model.uid);
|
|
that.saveTask(task, e.updateInfo);
|
|
}).bind('remove', function (e) {
|
|
that.removeTask(e.model.uid);
|
|
});
|
|
},
|
|
_createResourceEditor: function (container, options) {
|
|
var that = this;
|
|
var model = options instanceof ObservableObject ? options : options.model;
|
|
var id = model.get('id');
|
|
var messages = this.options.messages;
|
|
var resourcesField = that.resources.field;
|
|
var editor = this._resourceEditor = new ResourceEditor(container, {
|
|
resourcesField: resourcesField,
|
|
data: this._wrapResourceData(id),
|
|
model: model,
|
|
messages: extend({}, messages.editor),
|
|
buttons: [
|
|
{
|
|
name: 'update',
|
|
text: messages.save,
|
|
className: Gantt.styles.primary
|
|
},
|
|
{
|
|
name: 'cancel',
|
|
text: messages.cancel
|
|
}
|
|
],
|
|
createButton: proxy(this._createPopupButton, this),
|
|
save: function (e) {
|
|
that._updateAssignments(e.model.get('id'), e.model.get(resourcesField));
|
|
}
|
|
});
|
|
editor.open();
|
|
},
|
|
_createPopupButton: function (command) {
|
|
var commandName = command.name || command.text;
|
|
var options = {
|
|
className: Gantt.styles.popup.button + ' k-gantt-' + (commandName || '').replace(/\s/g, ''),
|
|
text: commandName,
|
|
attr: ''
|
|
};
|
|
if (!commandName && !(isPlainObject(command) && command.template)) {
|
|
throw new Error('Custom commands should have name specified');
|
|
}
|
|
if (isPlainObject(command)) {
|
|
if (command.className) {
|
|
command.className += ' ' + options.className;
|
|
}
|
|
options = extend(true, options, command);
|
|
}
|
|
return kendo.template(COMMAND_BUTTON_TEMPLATE)(options);
|
|
},
|
|
view: function (type) {
|
|
return this.timeline.view(type);
|
|
},
|
|
dataItem: function (value) {
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
var list = this.list;
|
|
var element = list.content.find(value);
|
|
return list._modelFromElement(element);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
this.list._setDataSource(this.dataSource);
|
|
if (this.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
setDependenciesDataSource: function (dependencies) {
|
|
this.options.dependencies = dependencies;
|
|
this._dependencies();
|
|
if (this.options.autoBind) {
|
|
dependencies.fetch();
|
|
}
|
|
},
|
|
items: function () {
|
|
return this.wrapper.children('.k-task');
|
|
},
|
|
_updateAssignments: function (id, resources) {
|
|
var dataSource = this.assignments.dataSource;
|
|
var taskId = this.assignments.dataTaskIdField;
|
|
var resourceId = this.assignments.dataResourceIdField;
|
|
var hasMatch = false;
|
|
var assignments = new Query(dataSource.view()).filter({
|
|
field: taskId,
|
|
operator: 'eq',
|
|
value: id
|
|
}).toArray();
|
|
var assignment;
|
|
var resource;
|
|
var value;
|
|
while (assignments.length) {
|
|
assignment = assignments[0];
|
|
for (var i = 0, length = resources.length; i < length; i++) {
|
|
resource = resources[i];
|
|
if (assignment.get(resourceId) === resource.get('id')) {
|
|
value = resources[i].get('value');
|
|
this._updateAssignment(assignment, value);
|
|
resources.splice(i, 1);
|
|
hasMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasMatch) {
|
|
this._removeAssignment(assignment);
|
|
}
|
|
hasMatch = false;
|
|
assignments.shift();
|
|
}
|
|
for (var j = 0, newLength = resources.length; j < newLength; j++) {
|
|
resource = resources[j];
|
|
this._createAssignment(resource, id);
|
|
}
|
|
dataSource.sync();
|
|
},
|
|
cancelTask: function () {
|
|
var editor = this._editor;
|
|
var container = editor.container;
|
|
if (container) {
|
|
editor.close();
|
|
}
|
|
},
|
|
editTask: function (uid) {
|
|
var task = typeof uid === 'string' ? this.dataSource.getByUid(uid) : uid;
|
|
if (!task) {
|
|
return;
|
|
}
|
|
var taskCopy = this.dataSource._createNewModel(task.toJSON());
|
|
taskCopy.uid = task.uid;
|
|
this.cancelTask();
|
|
this._editTask(taskCopy);
|
|
},
|
|
_editTask: function (task) {
|
|
this._editor.editTask(task);
|
|
},
|
|
saveTask: function (task, updateInfo) {
|
|
var editor = this._editor;
|
|
var container = editor.container;
|
|
var editable = editor.editable;
|
|
if (container && editable && editable.end()) {
|
|
this._updateTask(task, updateInfo);
|
|
}
|
|
},
|
|
_updateTask: function (task, updateInfo) {
|
|
var resourcesField = this.resources.field;
|
|
if (!this.trigger('save', {
|
|
task: task,
|
|
values: updateInfo
|
|
})) {
|
|
this._preventRefresh = true;
|
|
this.dataSource.update(task, updateInfo);
|
|
if (updateInfo[resourcesField]) {
|
|
this._updateAssignments(task.get('id'), updateInfo[resourcesField]);
|
|
}
|
|
this._syncDataSource();
|
|
}
|
|
},
|
|
_updateAssignment: function (assignment, value) {
|
|
var resourceValueField = this.assignments.dataValueField;
|
|
assignment.set(resourceValueField, value);
|
|
},
|
|
removeTask: function (uid) {
|
|
var that = this;
|
|
var task = typeof uid === 'string' ? this.dataSource.getByUid(uid) : uid;
|
|
if (!task) {
|
|
return;
|
|
}
|
|
this._taskConfirm(function (cancel) {
|
|
if (!cancel) {
|
|
that._removeTask(task);
|
|
}
|
|
}, task);
|
|
},
|
|
_createTask: function (task, index) {
|
|
if (!this.trigger('add', {
|
|
task: task,
|
|
dependency: null
|
|
})) {
|
|
var dataSource = this.dataSource;
|
|
this._preventRefresh = true;
|
|
if (index === undefined) {
|
|
dataSource.add(task);
|
|
} else {
|
|
dataSource.insert(index, task);
|
|
}
|
|
this._scrollToUid = task.uid;
|
|
this._syncDataSource();
|
|
}
|
|
},
|
|
_createDependency: function (dependency) {
|
|
if (!this.trigger('add', {
|
|
task: null,
|
|
dependency: dependency
|
|
})) {
|
|
this._preventDependencyRefresh = true;
|
|
this.dependencies.add(dependency);
|
|
this._preventDependencyRefresh = false;
|
|
this.dependencies.sync();
|
|
}
|
|
},
|
|
_createAssignment: function (resource, id) {
|
|
var assignments = this.assignments;
|
|
var dataSource = assignments.dataSource;
|
|
var taskId = assignments.dataTaskIdField;
|
|
var resourceId = assignments.dataResourceIdField;
|
|
var resourceValue = assignments.dataValueField;
|
|
var assignment = dataSource._createNewModel();
|
|
assignment[taskId] = id;
|
|
assignment[resourceId] = resource.get('id');
|
|
assignment[resourceValue] = resource.get('value');
|
|
dataSource.add(assignment);
|
|
},
|
|
removeDependency: function (uid) {
|
|
var that = this;
|
|
var dependency = typeof uid === 'string' ? this.dependencies.getByUid(uid) : uid;
|
|
if (!dependency) {
|
|
return;
|
|
}
|
|
this._dependencyConfirm(function (cancel) {
|
|
if (!cancel) {
|
|
that._removeDependency(dependency);
|
|
}
|
|
}, dependency);
|
|
},
|
|
_removeTaskDependencies: function (task, dependencies) {
|
|
this._preventDependencyRefresh = true;
|
|
for (var i = 0, length = dependencies.length; i < length; i++) {
|
|
this.dependencies.remove(dependencies[i]);
|
|
}
|
|
this._preventDependencyRefresh = false;
|
|
this.dependencies.sync();
|
|
},
|
|
_removeTaskAssignments: function (task) {
|
|
var dataSource = this.assignments.dataSource;
|
|
var assignments = dataSource.view();
|
|
var filter = {
|
|
field: this.assignments.dataTaskIdField,
|
|
operator: 'eq',
|
|
value: task.get('id')
|
|
};
|
|
assignments = new Query(assignments).filter(filter).toArray();
|
|
this._preventRefresh = true;
|
|
for (var i = 0, length = assignments.length; i < length; i++) {
|
|
dataSource.remove(assignments[i]);
|
|
}
|
|
this._preventRefresh = false;
|
|
dataSource.sync();
|
|
},
|
|
_removeTask: function (task) {
|
|
var dependencies = this.dependencies.dependencies(task.id);
|
|
if (!this.trigger('remove', {
|
|
task: task,
|
|
dependencies: dependencies
|
|
})) {
|
|
this._removeTaskDependencies(task, dependencies);
|
|
this._removeTaskAssignments(task);
|
|
this._preventRefresh = true;
|
|
if (this.dataSource.remove(task)) {
|
|
this._syncDataSource();
|
|
}
|
|
this._preventRefresh = false;
|
|
}
|
|
},
|
|
_removeDependency: function (dependency) {
|
|
if (!this.trigger('remove', {
|
|
task: null,
|
|
dependencies: [dependency]
|
|
})) {
|
|
if (this.dependencies.remove(dependency)) {
|
|
this.dependencies.sync();
|
|
}
|
|
}
|
|
},
|
|
_removeAssignment: function (assignment) {
|
|
this.assignments.dataSource.remove(assignment);
|
|
},
|
|
_taskConfirm: function (callback, task) {
|
|
var messages = this.options.messages;
|
|
this._confirm(callback, {
|
|
model: task,
|
|
text: messages.deleteTaskConfirmation,
|
|
title: messages.deleteTaskWindowTitle
|
|
});
|
|
},
|
|
_dependencyConfirm: function (callback, dependency) {
|
|
var messages = this.options.messages;
|
|
this._confirm(callback, {
|
|
model: dependency,
|
|
text: messages.deleteDependencyConfirmation,
|
|
title: messages.deleteDependencyWindowTitle
|
|
});
|
|
},
|
|
_confirm: function (callback, options) {
|
|
var editable = this.options.editable;
|
|
var messages;
|
|
var buttons;
|
|
if (editable === true || editable.confirmation !== false) {
|
|
messages = this.options.messages;
|
|
buttons = [
|
|
{
|
|
name: 'delete',
|
|
text: messages.destroy,
|
|
className: Gantt.styles.primary,
|
|
click: function () {
|
|
callback();
|
|
}
|
|
},
|
|
{
|
|
name: 'cancel',
|
|
text: messages.cancel,
|
|
click: function () {
|
|
callback(true);
|
|
}
|
|
}
|
|
];
|
|
this.showDialog(extend(true, {}, options, { buttons: buttons }));
|
|
} else {
|
|
callback();
|
|
}
|
|
},
|
|
showDialog: function (options) {
|
|
this._editor.showDialog(options);
|
|
},
|
|
refresh: function () {
|
|
if (this._preventRefresh || this.list.editable) {
|
|
return;
|
|
}
|
|
this._progress(false);
|
|
var dataSource = this.dataSource;
|
|
var taskTree = dataSource.taskTree();
|
|
var scrollToUid = this._scrollToUid;
|
|
var current;
|
|
var cachedUid;
|
|
var cachedIndex = -1;
|
|
if (this.current) {
|
|
cachedUid = this.current.closest('tr').attr(kendo.attr('uid'));
|
|
cachedIndex = this.current.index();
|
|
}
|
|
if (this.trigger('dataBinding')) {
|
|
return;
|
|
}
|
|
if (this.resources.dataSource.data().length !== 0) {
|
|
this._assignResources(taskTree);
|
|
}
|
|
if (this._editor) {
|
|
this._editor.close();
|
|
}
|
|
this.clearSelection();
|
|
this.list._render(taskTree);
|
|
this.timeline._render(taskTree);
|
|
this.timeline._renderDependencies(this.dependencies.view());
|
|
if (scrollToUid) {
|
|
this._scrollTo(scrollToUid);
|
|
this.select(selector(scrollToUid));
|
|
}
|
|
if ((scrollToUid || cachedUid) && cachedIndex >= 0) {
|
|
current = this.list.content.find('tr' + selector(scrollToUid || cachedUid) + ' > td:eq(' + cachedIndex + ')');
|
|
this._current(current);
|
|
}
|
|
this._scrollToUid = null;
|
|
this.trigger('dataBound');
|
|
},
|
|
refreshDependencies: function () {
|
|
if (this._preventDependencyRefresh) {
|
|
return;
|
|
}
|
|
if (this.trigger('dataBinding')) {
|
|
return;
|
|
}
|
|
this.timeline._renderDependencies(this.dependencies.view());
|
|
this.trigger('dataBound');
|
|
},
|
|
_assignResources: function (taskTree) {
|
|
var resources = this.resources;
|
|
var assignments = this.assignments;
|
|
var groupAssigments = function () {
|
|
var data = assignments.dataSource.view();
|
|
var group = { field: assignments.dataTaskIdField };
|
|
data = new Query(data).group(group).toArray();
|
|
return data;
|
|
};
|
|
var assigments = groupAssigments();
|
|
var applyTaskResource = function (task, action) {
|
|
var taskId = task.get('id');
|
|
kendo.setter(resources.field)(task, new ObservableArray([]));
|
|
for (var i = 0, length = assigments.length; i < length; i++) {
|
|
if (assigments[i].value === taskId) {
|
|
action(task, assigments[i].items);
|
|
}
|
|
}
|
|
};
|
|
var wrapTask = function (task, items) {
|
|
for (var j = 0, length = items.length; j < length; j++) {
|
|
var item = items[j];
|
|
var resource = resources.dataSource.get(item.get(assignments.dataResourceIdField));
|
|
var resourceValue = item.get(assignments.dataValueField);
|
|
var resourcedId = item.get(assignments.dataResourceIdField);
|
|
var valueFormat = resource.get(resources.dataFormatField) || PERCENTAGE_FORMAT;
|
|
var formatedValue = kendo.toString(resourceValue, valueFormat);
|
|
task[resources.field].push(new ObservableObject({
|
|
id: resourcedId,
|
|
name: resource.get(resources.dataTextField),
|
|
color: resource.get(resources.dataColorField),
|
|
value: resourceValue,
|
|
formatedValue: formatedValue
|
|
}));
|
|
}
|
|
};
|
|
for (var i = 0, length = taskTree.length; i < length; i++) {
|
|
applyTaskResource(taskTree[i], wrapTask);
|
|
}
|
|
},
|
|
_wrapResourceData: function (id) {
|
|
var that = this;
|
|
var result = [];
|
|
var resource;
|
|
var resources = this.resources.dataSource.view();
|
|
var assignments = this.assignments.dataSource.view();
|
|
var taskAssignments = new Query(assignments).filter({
|
|
field: that.assignments.dataTaskIdField,
|
|
operator: 'eq',
|
|
value: id
|
|
}).toArray();
|
|
var valuePerResource = function (id) {
|
|
var resourceValue = null;
|
|
new Query(taskAssignments).filter({
|
|
field: that.assignments.dataResourceIdField,
|
|
operator: 'eq',
|
|
value: id
|
|
}).select(function (assignment) {
|
|
resourceValue += assignment.get(that.assignments.dataValueField);
|
|
});
|
|
return resourceValue;
|
|
};
|
|
for (var i = 0, length = resources.length; i < length; i++) {
|
|
resource = resources[i];
|
|
result.push({
|
|
id: resource.get('id'),
|
|
name: resource.get(that.resources.dataTextField),
|
|
format: resource.get(that.resources.dataFormatField) || PERCENTAGE_FORMAT,
|
|
value: valuePerResource(resource.id)
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
_syncDataSource: function () {
|
|
this._preventRefresh = false;
|
|
this._requestStart();
|
|
this.dataSource.sync();
|
|
},
|
|
_requestStart: function () {
|
|
this._progress(true);
|
|
},
|
|
_error: function () {
|
|
this._progress(false);
|
|
},
|
|
_progress: function (toggle) {
|
|
kendo.ui.progress(this.element, toggle);
|
|
},
|
|
_resizable: function () {
|
|
var that = this;
|
|
var wrapper = this.wrapper;
|
|
var ganttStyles = Gantt.styles;
|
|
var contentSelector = DOT + ganttStyles.gridContent;
|
|
var treeListWrapper = wrapper.find(DOT + ganttStyles.list);
|
|
var timelineWrapper = wrapper.find(DOT + ganttStyles.timeline);
|
|
var treeListWidth;
|
|
var timelineWidth;
|
|
var timelineScroll;
|
|
this._resizeDraggable = wrapper.find(DOT + ganttStyles.splitBar).height(treeListWrapper.height()).hover(function () {
|
|
$(this).addClass(ganttStyles.splitBarHover);
|
|
}, function () {
|
|
$(this).removeClass(ganttStyles.splitBarHover);
|
|
}).end().kendoResizable({
|
|
orientation: 'horizontal',
|
|
handle: DOT + ganttStyles.splitBar,
|
|
'start': function () {
|
|
treeListWidth = treeListWrapper.width();
|
|
timelineWidth = timelineWrapper.width();
|
|
timelineScroll = timelineWrapper.find(contentSelector).scrollLeft();
|
|
},
|
|
'resize': function (e) {
|
|
var delta = e.x.initialDelta;
|
|
if (kendo.support.isRtl(wrapper)) {
|
|
delta *= -1;
|
|
}
|
|
if (treeListWidth + delta < 0 || timelineWidth - delta < 0) {
|
|
return;
|
|
}
|
|
treeListWrapper.width(treeListWidth + delta);
|
|
timelineWrapper.width(timelineWidth - delta);
|
|
timelineWrapper.find(contentSelector).scrollLeft(timelineScroll + delta);
|
|
that.timeline.view()._renderCurrentTime();
|
|
}
|
|
}).data('kendoResizable');
|
|
},
|
|
_scrollable: function () {
|
|
var that = this;
|
|
var ganttStyles = Gantt.styles;
|
|
var contentSelector = DOT + ganttStyles.gridContent;
|
|
var headerSelector = DOT + ganttStyles.gridHeaderWrap;
|
|
var timelineHeader = this.timeline.element.find(headerSelector);
|
|
var timelineContent = this.timeline.element.find(contentSelector);
|
|
var treeListHeader = this.list.element.find(headerSelector);
|
|
var treeListContent = this.list.element.find(contentSelector);
|
|
if (mobileOS) {
|
|
treeListContent.css('overflow-y', 'auto');
|
|
}
|
|
timelineContent.on('scroll', function () {
|
|
that.scrollTop = this.scrollTop;
|
|
timelineHeader.scrollLeft(this.scrollLeft);
|
|
treeListContent.scrollTop(this.scrollTop);
|
|
});
|
|
treeListContent.on('scroll', function () {
|
|
that.scrollTop = this.scrollTop;
|
|
treeListHeader.scrollLeft(this.scrollLeft);
|
|
timelineContent.scrollTop(this.scrollTop);
|
|
}).on('DOMMouseScroll' + NS + ' mousewheel' + NS, function (e) {
|
|
var scrollTop = timelineContent.scrollTop();
|
|
var delta = kendo.wheelDeltaY(e);
|
|
if (delta) {
|
|
e.preventDefault();
|
|
$(e.currentTarget).one('wheel' + NS, false);
|
|
timelineContent.scrollTop(scrollTop + -delta);
|
|
}
|
|
});
|
|
},
|
|
_navigatable: function () {
|
|
var that = this;
|
|
var navigatable = this.options.navigatable;
|
|
var editable = this.options.editable;
|
|
var headerTable = this.list.header.find('table');
|
|
var contentTable = this.list.content.find('table');
|
|
var ganttStyles = Gantt.styles;
|
|
var isRtl = kendo.support.isRtl(this.wrapper);
|
|
var timelineContent = this.timeline.element.find(DOT + ganttStyles.gridContent);
|
|
var tables = headerTable.add(contentTable);
|
|
var attr = selector();
|
|
var cellIndex;
|
|
var expandState = {
|
|
collapse: false,
|
|
expand: true
|
|
};
|
|
var scroll = function (reverse) {
|
|
var width = that.timeline.view()._timeSlots()[0].offsetWidth;
|
|
timelineContent.scrollLeft(timelineContent.scrollLeft() + (reverse ? -width : width));
|
|
};
|
|
var moveVertical = function (method) {
|
|
var parent = that.current.parent('tr' + selector());
|
|
var index = that.current.index();
|
|
var subling = parent[method]();
|
|
if (that.select().length !== 0) {
|
|
that.clearSelection();
|
|
}
|
|
if (subling.length !== 0) {
|
|
that._current(subling.children('td:eq(' + index + ')'));
|
|
that._scrollTo(that.current);
|
|
} else {
|
|
if (that.current.is('td') && method == 'prev') {
|
|
focusTable(headerTable);
|
|
} else if (that.current.is('th') && method == 'next') {
|
|
focusTable(contentTable);
|
|
}
|
|
}
|
|
};
|
|
var moveHorizontal = function (method) {
|
|
var subling = that.current[method]();
|
|
if (subling.length !== 0) {
|
|
that._current(subling);
|
|
cellIndex = that.current.index();
|
|
}
|
|
};
|
|
var toggleExpandedState = function (value) {
|
|
var model = that.dataItem(that.current);
|
|
if (model.summary && model.expanded !== value) {
|
|
model.set('expanded', value);
|
|
}
|
|
};
|
|
var deleteAction = function () {
|
|
if (!that.options.editable || that.list.editable) {
|
|
return;
|
|
}
|
|
var selectedTask = that.select();
|
|
var uid = kendo.attr('uid');
|
|
if (selectedTask.length) {
|
|
that.removeTask(selectedTask.attr(uid));
|
|
}
|
|
};
|
|
$(this.wrapper).on('mousedown' + NS, 'tr' + attr + ', div' + attr + ':not(' + DOT + ganttStyles.line + ')', function (e) {
|
|
var currentTarget = $(e.currentTarget);
|
|
var isInput = $(e.target).is(':button,a,:input,a>.k-icon,textarea,span.k-icon,span.k-link,.k-input,.k-multiselect-wrap');
|
|
var current;
|
|
if (e.ctrlKey) {
|
|
return;
|
|
}
|
|
if (navigatable) {
|
|
if (currentTarget.is('tr')) {
|
|
current = $(e.target).closest('td');
|
|
} else {
|
|
current = that.list.content.find('tr' + selector(currentTarget.attr(kendo.attr('uid'))) + ' > td:first');
|
|
}
|
|
that._current(current);
|
|
}
|
|
if ((navigatable || editable) && !isInput) {
|
|
that._focusTimeout = setTimeout(function () {
|
|
focusTable(that.list.content.find('table'), true);
|
|
}, 2);
|
|
}
|
|
});
|
|
if (navigatable !== true) {
|
|
contentTable.on('keydown' + NS, function (e) {
|
|
if (e.keyCode == keys.DELETE) {
|
|
deleteAction();
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
tables.on('focus' + NS, function () {
|
|
var selector = this === contentTable.get(0) ? 'td' : 'th';
|
|
var selection = that.select();
|
|
var current = that.current || $(selection.length ? selection : this).find(selector + ':eq(' + (cellIndex || 0) + ')');
|
|
that._current(current);
|
|
}).on('blur' + NS, function () {
|
|
that._current();
|
|
if (this == headerTable) {
|
|
$(this).attr(TABINDEX, -1);
|
|
}
|
|
}).on('keydown' + NS, function (e) {
|
|
var key = e.keyCode;
|
|
var isCell;
|
|
if (!that.current) {
|
|
return;
|
|
}
|
|
isCell = that.current.is('td');
|
|
switch (key) {
|
|
case keys.RIGHT:
|
|
e.preventDefault();
|
|
if (e.altKey) {
|
|
scroll();
|
|
} else if (e.ctrlKey) {
|
|
toggleExpandedState(isRtl ? expandState.collapse : expandState.expand);
|
|
} else {
|
|
moveHorizontal(isRtl ? 'prev' : 'next');
|
|
}
|
|
break;
|
|
case keys.LEFT:
|
|
e.preventDefault();
|
|
if (e.altKey) {
|
|
scroll(true);
|
|
} else if (e.ctrlKey) {
|
|
toggleExpandedState(isRtl ? expandState.expand : expandState.collapse);
|
|
} else {
|
|
moveHorizontal(isRtl ? 'next' : 'prev');
|
|
}
|
|
break;
|
|
case keys.UP:
|
|
e.preventDefault();
|
|
moveVertical('prev');
|
|
break;
|
|
case keys.DOWN:
|
|
e.preventDefault();
|
|
moveVertical('next');
|
|
break;
|
|
case keys.SPACEBAR:
|
|
e.preventDefault();
|
|
if (isCell) {
|
|
that.select(that.current.closest('tr'));
|
|
}
|
|
break;
|
|
case keys.ENTER:
|
|
e.preventDefault();
|
|
if (isCell) {
|
|
if (that.options.editable) {
|
|
that._cachedCurrent = that.current;
|
|
that.list._startEditHandler(that.current);
|
|
$(this).one('keyup', function (e) {
|
|
e.stopPropagation();
|
|
});
|
|
}
|
|
} else {
|
|
that.current.children('a.k-link').click();
|
|
}
|
|
break;
|
|
case keys.ESC:
|
|
e.stopPropagation();
|
|
break;
|
|
case keys.DELETE:
|
|
if (isCell) {
|
|
deleteAction();
|
|
}
|
|
break;
|
|
default:
|
|
if (key >= 49 && key <= 57) {
|
|
that.view(that.timeline._viewByIndex(key - 49));
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
},
|
|
_current: function (element) {
|
|
var ganttStyles = Gantt.styles;
|
|
var activeElement;
|
|
if (this.current && this.current.length) {
|
|
this.current.removeClass(ganttStyles.focused).removeAttr('id');
|
|
}
|
|
if (element && element.length) {
|
|
this.current = element.addClass(ganttStyles.focused).attr('id', ACTIVE_CELL);
|
|
activeElement = $(kendo._activeElement());
|
|
if (activeElement.is('table') && this.wrapper.find(activeElement).length > 0) {
|
|
activeElement.removeAttr(ARIA_DESCENDANT).attr(ARIA_DESCENDANT, ACTIVE_CELL);
|
|
}
|
|
} else {
|
|
this.current = null;
|
|
}
|
|
},
|
|
_dataBind: function () {
|
|
var that = this;
|
|
if (that.options.autoBind) {
|
|
this._preventRefresh = true;
|
|
this._preventDependencyRefresh = true;
|
|
var promises = $.map([
|
|
this.dataSource,
|
|
this.dependencies,
|
|
this.resources.dataSource,
|
|
this.assignments.dataSource
|
|
], function (dataSource) {
|
|
return dataSource.fetch();
|
|
});
|
|
$.when.apply(null, promises).done(function () {
|
|
that._preventRefresh = false;
|
|
that._preventDependencyRefresh = false;
|
|
that.refresh();
|
|
});
|
|
}
|
|
},
|
|
_resize: function () {
|
|
this._adjustDimensions();
|
|
this.timeline.view()._adjustHeight();
|
|
this.timeline.view()._renderCurrentTime();
|
|
this.list._adjustHeight();
|
|
}
|
|
});
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Gantt.fn);
|
|
Gantt.fn._drawPDF = function () {
|
|
var ganttStyles = Gantt.styles;
|
|
var listClass = '.' + ganttStyles.list;
|
|
var listWidth = this.wrapper.find(listClass).width();
|
|
var content = this.wrapper.clone();
|
|
content.find(listClass).css('width', listWidth);
|
|
return this._drawPDFShadow({ content: content }, { avoidLinks: this.options.pdf.avoidLinks });
|
|
};
|
|
}
|
|
kendo.ui.plugin(Gantt);
|
|
extend(true, Gantt, { styles: ganttStyles });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.treelist', [
|
|
'kendo.dom',
|
|
'kendo.data',
|
|
'kendo.columnsorter',
|
|
'kendo.editable',
|
|
'kendo.window',
|
|
'kendo.filtermenu',
|
|
'kendo.selectable',
|
|
'kendo.resizable',
|
|
'kendo.treeview.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'treelist',
|
|
name: 'TreeList',
|
|
category: 'web',
|
|
description: 'The TreeList widget displays self-referencing data and offers rich support for interacting with data, sorting, filtering, and selection.',
|
|
depends: [
|
|
'dom',
|
|
'data'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'treelist-sorting',
|
|
name: 'Sorting',
|
|
description: 'Support for column sorting',
|
|
depends: ['columnsorter']
|
|
},
|
|
{
|
|
id: 'treelist-filtering',
|
|
name: 'Filtering',
|
|
description: 'Support for record filtering',
|
|
depends: ['filtermenu']
|
|
},
|
|
{
|
|
id: 'treelist-editing',
|
|
name: 'Editing',
|
|
description: 'Support for record editing',
|
|
depends: [
|
|
'editable',
|
|
'window'
|
|
]
|
|
},
|
|
{
|
|
id: 'treelist-selection',
|
|
name: 'Selection',
|
|
description: 'Support for row selection',
|
|
depends: ['selectable']
|
|
},
|
|
{
|
|
id: 'treelist-column-resize',
|
|
name: 'Column resizing',
|
|
description: 'Support for column resizing',
|
|
depends: ['resizable']
|
|
},
|
|
{
|
|
id: 'treelist-dragging',
|
|
name: 'Drag & Drop',
|
|
description: 'Support for drag & drop of rows',
|
|
depends: ['treeview.draganddrop']
|
|
},
|
|
{
|
|
id: 'treelist-excel-export',
|
|
name: 'Excel export',
|
|
description: 'Export data as Excel spreadsheet',
|
|
depends: ['excel']
|
|
},
|
|
{
|
|
id: 'treelist-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export data as PDF',
|
|
depends: [
|
|
'pdf',
|
|
'drawing'
|
|
]
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var data = kendo.data;
|
|
var extend = $.extend;
|
|
var kendoDom = kendo.dom;
|
|
var kendoDomElement = kendoDom.element;
|
|
var kendoTextElement = kendoDom.text;
|
|
var kendoHtmlElement = kendoDom.html;
|
|
var ui = kendo.ui;
|
|
var DataBoundWidget = ui.DataBoundWidget;
|
|
var DataSource = data.DataSource;
|
|
var ObservableArray = data.ObservableArray;
|
|
var Query = data.Query;
|
|
var Model = data.Model;
|
|
var proxy = $.proxy;
|
|
var map = $.map;
|
|
var grep = $.grep;
|
|
var inArray = $.inArray;
|
|
var isPlainObject = $.isPlainObject;
|
|
var push = Array.prototype.push;
|
|
var STRING = 'string';
|
|
var CHANGE = 'change';
|
|
var ERROR = 'error';
|
|
var PROGRESS = 'progress';
|
|
var DOT = '.';
|
|
var NS = '.kendoTreeList';
|
|
var CLICK = 'click';
|
|
var MOUSEDOWN = 'mousedown';
|
|
var EDIT = 'edit';
|
|
var SAVE = 'save';
|
|
var EXPAND = 'expand';
|
|
var COLLAPSE = 'collapse';
|
|
var REMOVE = 'remove';
|
|
var DATABINDING = 'dataBinding';
|
|
var DATABOUND = 'dataBound';
|
|
var CANCEL = 'cancel';
|
|
var FILTERMENUINIT = 'filterMenuInit';
|
|
var COLUMNHIDE = 'columnHide';
|
|
var COLUMNSHOW = 'columnShow';
|
|
var HEADERCELLS = 'th.k-header';
|
|
var COLUMNREORDER = 'columnReorder';
|
|
var COLUMNRESIZE = 'columnResize';
|
|
var COLUMNMENUINIT = 'columnMenuInit';
|
|
var COLUMNLOCK = 'columnLock';
|
|
var COLUMNUNLOCK = 'columnUnlock';
|
|
var PARENTIDFIELD = 'parentId';
|
|
var DRAGSTART = 'dragstart';
|
|
var DRAG = 'drag';
|
|
var DROP = 'drop';
|
|
var DRAGEND = 'dragend';
|
|
var classNames = {
|
|
wrapper: 'k-treelist k-grid k-widget',
|
|
header: 'k-header',
|
|
button: 'k-button',
|
|
alt: 'k-alt',
|
|
editCell: 'k-edit-cell',
|
|
group: 'k-treelist-group',
|
|
gridToolbar: 'k-grid-toolbar',
|
|
gridHeader: 'k-grid-header',
|
|
gridHeaderWrap: 'k-grid-header-wrap',
|
|
gridContent: 'k-grid-content',
|
|
gridContentWrap: 'k-grid-content',
|
|
gridFilter: 'k-grid-filter',
|
|
footerTemplate: 'k-footer-template',
|
|
loading: 'k-loading',
|
|
refresh: 'k-i-refresh',
|
|
retry: 'k-request-retry',
|
|
selected: 'k-state-selected',
|
|
status: 'k-status',
|
|
link: 'k-link',
|
|
withIcon: 'k-with-icon',
|
|
filterable: 'k-filterable',
|
|
icon: 'k-icon',
|
|
iconFilter: 'k-filter',
|
|
iconCollapse: 'k-i-collapse',
|
|
iconExpand: 'k-i-expand',
|
|
iconHidden: 'k-i-none',
|
|
iconPlaceHolder: 'k-icon k-i-none',
|
|
input: 'k-input',
|
|
dropPositions: 'k-insert-top k-insert-bottom k-add k-insert-middle',
|
|
dropTop: 'k-insert-top',
|
|
dropBottom: 'k-insert-bottom',
|
|
dropAdd: 'k-add',
|
|
dropMiddle: 'k-insert-middle',
|
|
dropDenied: 'k-denied',
|
|
dragStatus: 'k-drag-status',
|
|
dragClue: 'k-drag-clue',
|
|
dragClueText: 'k-clue-text'
|
|
};
|
|
var defaultCommands = {
|
|
create: {
|
|
imageClass: 'k-add',
|
|
className: 'k-grid-add',
|
|
methodName: 'addRow'
|
|
},
|
|
createchild: {
|
|
imageClass: 'k-add',
|
|
className: 'k-grid-add',
|
|
methodName: 'addRow'
|
|
},
|
|
destroy: {
|
|
imageClass: 'k-delete',
|
|
className: 'k-grid-delete',
|
|
methodName: 'removeRow'
|
|
},
|
|
edit: {
|
|
imageClass: 'k-edit',
|
|
className: 'k-grid-edit',
|
|
methodName: 'editRow'
|
|
},
|
|
update: {
|
|
imageClass: 'k-update',
|
|
className: 'k-primary k-grid-update',
|
|
methodName: 'saveRow'
|
|
},
|
|
canceledit: {
|
|
imageClass: 'k-cancel',
|
|
className: 'k-grid-cancel',
|
|
methodName: '_cancelEdit'
|
|
},
|
|
excel: {
|
|
imageClass: 'k-i-excel',
|
|
className: 'k-grid-excel',
|
|
methodName: 'saveAsExcel'
|
|
},
|
|
pdf: {
|
|
imageClass: 'k-i-pdf',
|
|
className: 'k-grid-pdf',
|
|
methodName: 'saveAsPDF'
|
|
}
|
|
};
|
|
var TreeListModel = Model.define({
|
|
id: 'id',
|
|
parentId: PARENTIDFIELD,
|
|
fields: {
|
|
id: { type: 'number' },
|
|
parentId: {
|
|
type: 'number',
|
|
nullable: true
|
|
}
|
|
},
|
|
init: function (value) {
|
|
Model.fn.init.call(this, value);
|
|
this._loaded = false;
|
|
if (!this.parentIdField) {
|
|
this.parentIdField = PARENTIDFIELD;
|
|
}
|
|
this.parentId = this.get(this.parentIdField);
|
|
},
|
|
accept: function (data) {
|
|
Model.fn.accept.call(this, data);
|
|
this.parentId = this.get(this.parentIdField);
|
|
},
|
|
set: function (field, value, initiator) {
|
|
if (field == PARENTIDFIELD && this.parentIdField != PARENTIDFIELD) {
|
|
this[this.parentIdField] = value;
|
|
}
|
|
Model.fn.set.call(this, field, value, initiator);
|
|
if (field == this.parentIdField) {
|
|
this.parentId = this.get(this.parentIdField);
|
|
}
|
|
},
|
|
loaded: function (value) {
|
|
if (value !== undefined) {
|
|
this._loaded = value;
|
|
} else {
|
|
return this._loaded;
|
|
}
|
|
},
|
|
shouldSerialize: function (field) {
|
|
return Model.fn.shouldSerialize.call(this, field) && field !== '_loaded' && field != '_error' && field != '_edit' && !(this.parentIdField !== 'parentId' && field === 'parentId');
|
|
}
|
|
});
|
|
TreeListModel.parentIdField = PARENTIDFIELD;
|
|
TreeListModel.define = function (base, options) {
|
|
if (options === undefined) {
|
|
options = base;
|
|
base = TreeListModel;
|
|
}
|
|
var parentId = options.parentId || PARENTIDFIELD;
|
|
options.parentIdField = parentId;
|
|
var model = Model.define(base, options);
|
|
if (parentId) {
|
|
model.parentIdField = parentId;
|
|
}
|
|
return model;
|
|
};
|
|
function is(field) {
|
|
return function (object) {
|
|
return object[field];
|
|
};
|
|
}
|
|
function not(func) {
|
|
return function (object) {
|
|
return !func(object);
|
|
};
|
|
}
|
|
var TreeListDataSource = DataSource.extend({
|
|
init: function (options) {
|
|
DataSource.fn.init.call(this, extend(true, {}, {
|
|
schema: {
|
|
modelBase: TreeListModel,
|
|
model: TreeListModel
|
|
}
|
|
}, options));
|
|
},
|
|
_createNewModel: function (data) {
|
|
var model = {};
|
|
var fromModel = data instanceof Model;
|
|
if (fromModel) {
|
|
model = data;
|
|
}
|
|
model = DataSource.fn._createNewModel.call(this, model);
|
|
if (!fromModel) {
|
|
if (data.parentId) {
|
|
data[model.parentIdField] = data.parentId;
|
|
}
|
|
model.accept(data);
|
|
}
|
|
return model;
|
|
},
|
|
_shouldWrap: function () {
|
|
return true;
|
|
},
|
|
_readData: function (newData) {
|
|
var data = this.data();
|
|
newData = DataSource.fn._readData.call(this, newData);
|
|
this._concat(newData, data);
|
|
if (newData instanceof ObservableArray) {
|
|
return newData;
|
|
}
|
|
return data;
|
|
},
|
|
_concat: function (source, target) {
|
|
var targetLength = target.length;
|
|
for (var i = 0; i < source.length; i++) {
|
|
target[targetLength++] = source[i];
|
|
}
|
|
target.length = targetLength;
|
|
},
|
|
_readAggregates: function (data) {
|
|
var result = extend(this._aggregateResult, this.reader.aggregates(data));
|
|
if ('' in result) {
|
|
result[this._defaultParentId()] = result[''];
|
|
delete result[''];
|
|
}
|
|
return result;
|
|
},
|
|
remove: function (root) {
|
|
var items = this._subtree(this._childrenMap(this.data()), root.id);
|
|
this._removeItems(items);
|
|
DataSource.fn.remove.call(this, root);
|
|
},
|
|
_filterCallback: function (query) {
|
|
var i, item;
|
|
var map = {};
|
|
var result = [];
|
|
var data = query.toArray();
|
|
for (i = 0; i < data.length; i++) {
|
|
item = data[i];
|
|
while (item) {
|
|
if (!map[item.id]) {
|
|
map[item.id] = true;
|
|
result.push(item);
|
|
}
|
|
if (!map[item.parentId]) {
|
|
map[item.parentId] = true;
|
|
item = this.parentNode(item);
|
|
if (item) {
|
|
result.push(item);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return new Query(result);
|
|
},
|
|
_subtree: function (map, id) {
|
|
var result = map[id] || [];
|
|
var defaultParentId = this._defaultParentId();
|
|
for (var i = 0, len = result.length; i < len; i++) {
|
|
if (result[i].id !== defaultParentId) {
|
|
result = result.concat(this._subtree(map, result[i].id));
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_childrenMap: function (data) {
|
|
var map = {};
|
|
var i, item, id, parentId;
|
|
data = this._observeView(data);
|
|
for (i = 0; i < data.length; i++) {
|
|
item = data[i];
|
|
id = item.id;
|
|
parentId = item.parentId;
|
|
map[id] = map[id] || [];
|
|
map[parentId] = map[parentId] || [];
|
|
map[parentId].push(item);
|
|
}
|
|
return map;
|
|
},
|
|
_calculateAggregates: function (data, options) {
|
|
options = options || {};
|
|
var result = {};
|
|
var item, subtree, i;
|
|
var filter = options.filter;
|
|
if (filter) {
|
|
data = Query.process(data, {
|
|
filter: filter,
|
|
filterCallback: proxy(this._filterCallback, this)
|
|
}).data;
|
|
}
|
|
var map = this._childrenMap(data);
|
|
result[this._defaultParentId()] = new Query(this._subtree(map, this._defaultParentId())).aggregate(options.aggregate);
|
|
for (i = 0; i < data.length; i++) {
|
|
item = data[i];
|
|
subtree = this._subtree(map, item.id);
|
|
result[item.id] = new Query(subtree).aggregate(options.aggregate);
|
|
}
|
|
return result;
|
|
},
|
|
_queryProcess: function (data, options) {
|
|
options = options || {};
|
|
options.filterCallback = proxy(this._filterCallback, this);
|
|
var defaultParentId = this._defaultParentId();
|
|
var result = Query.process(data, options);
|
|
var map = this._childrenMap(result.data);
|
|
var hasLoadedChildren, i, item, children;
|
|
data = map[defaultParentId] || [];
|
|
for (i = 0; i < data.length; i++) {
|
|
item = data[i];
|
|
if (item.id === defaultParentId) {
|
|
continue;
|
|
}
|
|
children = map[item.id];
|
|
hasLoadedChildren = !!(children && children.length);
|
|
if (!item.loaded()) {
|
|
item.loaded(hasLoadedChildren || !item.hasChildren);
|
|
}
|
|
if (item.loaded() || item.hasChildren !== true) {
|
|
item.hasChildren = hasLoadedChildren;
|
|
}
|
|
if (hasLoadedChildren) {
|
|
data = data.slice(0, i + 1).concat(children, data.slice(i + 1));
|
|
}
|
|
}
|
|
result.data = data;
|
|
return result;
|
|
},
|
|
_queueRequest: function (options, callback) {
|
|
callback.call(this);
|
|
},
|
|
_modelLoaded: function (id) {
|
|
var model = this.get(id);
|
|
model.loaded(true);
|
|
model.hasChildren = this.childNodes(model).length > 0;
|
|
},
|
|
_modelError: function (id, e) {
|
|
this.get(id)._error = e;
|
|
},
|
|
success: function (data, requestParams) {
|
|
if (!requestParams || typeof requestParams.id == 'undefined') {
|
|
this._data = this._observe([]);
|
|
}
|
|
return DataSource.fn.success.call(this, data, requestParams);
|
|
},
|
|
load: function (model) {
|
|
var method = '_query';
|
|
var remote = this.options.serverSorting || this.options.serverPaging || this.options.serverFiltering || this.options.serverGrouping || this.options.serverAggregates;
|
|
var defaultPromise = $.Deferred().resolve().promise();
|
|
if (model.loaded()) {
|
|
if (remote) {
|
|
return defaultPromise;
|
|
}
|
|
} else if (model.hasChildren) {
|
|
method = 'read';
|
|
}
|
|
return this[method]({ id: model.id }).then(proxy(this._modelLoaded, this, model.id), proxy(this._modelError, this, model.id));
|
|
},
|
|
contains: function (root, child) {
|
|
var rootId = root.id;
|
|
while (child) {
|
|
if (child.parentId === rootId) {
|
|
return true;
|
|
}
|
|
child = this.parentNode(child);
|
|
}
|
|
return false;
|
|
},
|
|
_byParentId: function (id, defaultId) {
|
|
var result = [];
|
|
var view = this.view();
|
|
var current;
|
|
if (id === defaultId) {
|
|
return [];
|
|
}
|
|
for (var i = 0; i < view.length; i++) {
|
|
current = view.at(i);
|
|
if (current.parentId == id) {
|
|
result.push(current);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_defaultParentId: function () {
|
|
return this.reader.model.fn.defaults[this.reader.model.parentIdField];
|
|
},
|
|
childNodes: function (model) {
|
|
return this._byParentId(model.id, this._defaultParentId());
|
|
},
|
|
rootNodes: function () {
|
|
return this._byParentId(this._defaultParentId());
|
|
},
|
|
parentNode: function (model) {
|
|
return this.get(model.parentId);
|
|
},
|
|
level: function (model) {
|
|
var result = -1;
|
|
if (!(model instanceof TreeListModel)) {
|
|
model = this.get(model);
|
|
}
|
|
do {
|
|
model = this.parentNode(model);
|
|
result++;
|
|
} while (model);
|
|
return result;
|
|
},
|
|
filter: function (value) {
|
|
var baseFilter = DataSource.fn.filter;
|
|
if (value === undefined) {
|
|
return baseFilter.call(this, value);
|
|
}
|
|
baseFilter.call(this, value);
|
|
}
|
|
});
|
|
TreeListDataSource.create = function (options) {
|
|
if ($.isArray(options)) {
|
|
options = { data: options };
|
|
} else if (options instanceof ObservableArray) {
|
|
options = { data: options.toJSON() };
|
|
}
|
|
return options instanceof TreeListDataSource ? options : new TreeListDataSource(options);
|
|
};
|
|
function isCellVisible() {
|
|
return this.style.display !== 'none';
|
|
}
|
|
function leafDataCells(container) {
|
|
var rows = container.find('>tr:not(.k-filter-row)');
|
|
var filter = function () {
|
|
var el = $(this);
|
|
return !el.hasClass('k-group-cell') && !el.hasClass('k-hierarchy-cell');
|
|
};
|
|
var cells = $();
|
|
if (rows.length > 1) {
|
|
cells = rows.find('th').filter(filter).filter(function () {
|
|
return this.rowSpan > 1;
|
|
});
|
|
}
|
|
cells = cells.add(rows.last().find('th').filter(filter));
|
|
var indexAttr = kendo.attr('index');
|
|
cells.sort(function (a, b) {
|
|
a = $(a);
|
|
b = $(b);
|
|
var indexA = a.attr(indexAttr);
|
|
var indexB = b.attr(indexAttr);
|
|
if (indexA === undefined) {
|
|
indexA = $(a).index();
|
|
}
|
|
if (indexB === undefined) {
|
|
indexB = $(b).index();
|
|
}
|
|
indexA = parseInt(indexA, 10);
|
|
indexB = parseInt(indexB, 10);
|
|
return indexA > indexB ? 1 : indexA < indexB ? -1 : 0;
|
|
});
|
|
return cells;
|
|
}
|
|
function createPlaceholders(options) {
|
|
var spans = [];
|
|
var className = options.className;
|
|
for (var i = 0, level = options.level; i < level; i++) {
|
|
spans.push(kendoDomElement('span', { className: className }));
|
|
}
|
|
return spans;
|
|
}
|
|
function columnsWidth(cols) {
|
|
var colWidth, width = 0;
|
|
for (var idx = 0, length = cols.length; idx < length; idx++) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
width += parseInt(colWidth, 10);
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
function syncTableHeight(table1, table2) {
|
|
table1 = table1[0];
|
|
table2 = table2[0];
|
|
if (table1.rows.length !== table2.rows.length) {
|
|
var lockedHeigth = table1.offsetHeight;
|
|
var tableHeigth = table2.offsetHeight;
|
|
var row;
|
|
var diff;
|
|
if (lockedHeigth > tableHeigth) {
|
|
row = table2.rows[table2.rows.length - 1];
|
|
diff = lockedHeigth - tableHeigth;
|
|
} else {
|
|
row = table1.rows[table1.rows.length - 1];
|
|
diff = tableHeigth - lockedHeigth;
|
|
}
|
|
row.style.height = row.offsetHeight + diff + 'px';
|
|
}
|
|
}
|
|
var Editor = kendo.Observable.extend({
|
|
init: function (element, options) {
|
|
kendo.Observable.fn.init.call(this);
|
|
options = this.options = extend(true, {}, this.options, options);
|
|
this.element = element;
|
|
this.bind(this.events, options);
|
|
this.model = this.options.model;
|
|
this.fields = this._fields(this.options.columns);
|
|
this._initContainer();
|
|
this.createEditable();
|
|
},
|
|
events: [],
|
|
_initContainer: function () {
|
|
this.wrapper = this.element;
|
|
},
|
|
createEditable: function () {
|
|
var options = this.options;
|
|
this.editable = new ui.Editable(this.wrapper, {
|
|
fields: this.fields,
|
|
target: options.target,
|
|
clearContainer: options.clearContainer,
|
|
model: this.model
|
|
});
|
|
},
|
|
_isEditable: function (column) {
|
|
return column.field && this.model.editable(column.field);
|
|
},
|
|
_fields: function (columns) {
|
|
var fields = [];
|
|
var idx, length, column;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (this._isEditable(column)) {
|
|
fields.push({
|
|
field: column.field,
|
|
format: column.format,
|
|
editor: column.editor
|
|
});
|
|
}
|
|
}
|
|
return fields;
|
|
},
|
|
end: function () {
|
|
return this.editable.end();
|
|
},
|
|
close: function () {
|
|
this.destroy();
|
|
},
|
|
destroy: function () {
|
|
this.editable.destroy();
|
|
this.editable.element.find('[' + kendo.attr('container-for') + ']').empty().end().removeAttr(kendo.attr('role'));
|
|
this.model = this.wrapper = this.element = this.columns = this.editable = null;
|
|
}
|
|
});
|
|
var PopupEditor = Editor.extend({
|
|
init: function (element, options) {
|
|
Editor.fn.init.call(this, element, options);
|
|
this._attachHandlers();
|
|
kendo.cycleForm(this.wrapper);
|
|
this.open();
|
|
},
|
|
events: [
|
|
CANCEL,
|
|
SAVE
|
|
],
|
|
options: {
|
|
window: {
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: 'Edit',
|
|
visible: false
|
|
}
|
|
},
|
|
_initContainer: function () {
|
|
var options = this.options;
|
|
var formContent = [];
|
|
this.wrapper = $('<div class="k-popup-edit-form"/>').attr(kendo.attr('uid'), this.model.uid).append('<div class="k-edit-form-container"/>');
|
|
if (options.template) {
|
|
this._appendTemplate(formContent);
|
|
this.fields = [];
|
|
} else {
|
|
this._appendFields(formContent);
|
|
}
|
|
this._appendButtons(formContent);
|
|
new kendoDom.Tree(this.wrapper.children()[0]).render(formContent);
|
|
this.wrapper.appendTo(options.appendTo);
|
|
this.window = new ui.Window(this.wrapper, options.window);
|
|
},
|
|
_appendTemplate: function (form) {
|
|
var template = this.options.template;
|
|
if (typeof template === STRING) {
|
|
template = window.unescape(template);
|
|
}
|
|
template = kendo.template(template)(this.model);
|
|
form.push(kendoHtmlElement(template));
|
|
},
|
|
_appendFields: function (form) {
|
|
var idx, length, column;
|
|
var columns = this.options.columns;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.command) {
|
|
continue;
|
|
}
|
|
form.push(kendoHtmlElement('<div class="k-edit-label"><label for="' + column.field + '">' + (column.title || column.field || '') + '</label></div>'));
|
|
if (this._isEditable(column)) {
|
|
form.push(kendoHtmlElement('<div ' + kendo.attr('container-for') + '="' + column.field + '" class="k-edit-field"></div>'));
|
|
} else {
|
|
form.push(kendoDomElement('div', { 'class': 'k-edit-field' }, [this.options.fieldRenderer(column, this.model)]));
|
|
}
|
|
}
|
|
},
|
|
_appendButtons: function (form) {
|
|
form.push(kendoDomElement('div', { 'class': 'k-edit-buttons k-state-default' }, this.options.commandRenderer()));
|
|
},
|
|
_attachHandlers: function () {
|
|
var closeHandler = this._cancelProxy = proxy(this._cancel, this);
|
|
this.wrapper.on(CLICK + NS, '.k-grid-cancel', this._cancelProxy);
|
|
this._saveProxy = proxy(this._save, this);
|
|
this.wrapper.on(CLICK + NS, '.k-grid-update', this._saveProxy);
|
|
this.window.bind('close', function (e) {
|
|
if (e.userTriggered) {
|
|
closeHandler(e);
|
|
}
|
|
});
|
|
},
|
|
_dettachHandlers: function () {
|
|
this._cancelProxy = null;
|
|
this._saveProxy = null;
|
|
this.wrapper.off(NS);
|
|
},
|
|
_cancel: function (e) {
|
|
this.trigger(CANCEL, e);
|
|
},
|
|
_save: function () {
|
|
this.trigger(SAVE);
|
|
},
|
|
open: function () {
|
|
this.window.center().open();
|
|
},
|
|
close: function () {
|
|
this.window.bind('deactivate', proxy(this.destroy, this)).close();
|
|
},
|
|
destroy: function () {
|
|
this.window.destroy();
|
|
this.window = null;
|
|
this._dettachHandlers();
|
|
Editor.fn.destroy.call(this);
|
|
}
|
|
});
|
|
var TreeList = DataBoundWidget.extend({
|
|
init: function (element, options) {
|
|
DataBoundWidget.fn.init.call(this, element, options);
|
|
this._dataSource(this.options.dataSource);
|
|
this._columns();
|
|
this._layout();
|
|
this._selectable();
|
|
this._sortable();
|
|
this._resizable();
|
|
this._filterable();
|
|
this._attachEvents();
|
|
this._toolbar();
|
|
this._scrollable();
|
|
this._reorderable();
|
|
this._columnMenu();
|
|
this._minScreenSupport();
|
|
this._draggable();
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
if (this._hasLockedColumns) {
|
|
var widget = this;
|
|
this.wrapper.addClass('k-grid-lockedcolumns');
|
|
this._resizeHandler = function () {
|
|
widget.resize();
|
|
};
|
|
$(window).on('resize' + NS, this._resizeHandler);
|
|
}
|
|
kendo.notify(this);
|
|
},
|
|
_draggable: function () {
|
|
var editable = this.options.editable;
|
|
if (!editable || !editable.move) {
|
|
return;
|
|
}
|
|
this._dragging = new kendo.ui.HierarchicalDragAndDrop(this.wrapper, {
|
|
$angular: this.$angular,
|
|
autoScroll: true,
|
|
filter: 'tbody>tr',
|
|
itemSelector: 'tr',
|
|
allowedContainers: this.wrapper,
|
|
hintText: function (row) {
|
|
var text = function () {
|
|
return $(this).text();
|
|
};
|
|
var separator = '<span class=\'k-header k-drag-separator\' />';
|
|
return row.children('td').map(text).toArray().join(separator);
|
|
},
|
|
contains: proxy(function (source, destination) {
|
|
var dest = this.dataItem(destination);
|
|
var src = this.dataItem(source);
|
|
return src == dest || this.dataSource.contains(src, dest);
|
|
}, this),
|
|
itemFromTarget: function (target) {
|
|
var tr = target.closest('tr');
|
|
return {
|
|
item: tr,
|
|
content: tr
|
|
};
|
|
},
|
|
dragstart: proxy(function (source) {
|
|
this.wrapper.addClass('k-treelist-dragging');
|
|
var model = this.dataItem(source);
|
|
return this.trigger(DRAGSTART, { source: model });
|
|
}, this),
|
|
drag: proxy(function (e) {
|
|
e.source = this.dataItem(e.source);
|
|
this.trigger(DRAG, e);
|
|
}, this),
|
|
drop: proxy(function (e) {
|
|
e.source = this.dataItem(e.source);
|
|
e.destination = this.dataItem(e.destination);
|
|
this.wrapper.removeClass('k-treelist-dragging');
|
|
return this.trigger(DROP, e);
|
|
}, this),
|
|
dragend: proxy(function (e) {
|
|
var dest = this.dataItem(e.destination);
|
|
var src = this.dataItem(e.source);
|
|
src.set('parentId', dest ? dest.id : null);
|
|
e.source = src;
|
|
e.destination = dest;
|
|
this.trigger(DRAGEND, e);
|
|
}, this),
|
|
reorderable: false,
|
|
dropHintContainer: function (item) {
|
|
return item.children('td:eq(1)');
|
|
},
|
|
dropPositionFrom: function (dropHint) {
|
|
return dropHint.prevAll('.k-i-none').length > 0 ? 'after' : 'before';
|
|
}
|
|
});
|
|
},
|
|
itemFor: function (model) {
|
|
if (typeof model == 'number') {
|
|
model = this.dataSource.get(model);
|
|
}
|
|
return this.tbody.find('[' + kendo.attr('uid') + '=' + model.uid + ']');
|
|
},
|
|
_scrollable: function () {
|
|
if (this.options.scrollable) {
|
|
var scrollables = this.thead.closest('.k-grid-header-wrap');
|
|
var lockedContent = $(this.lockedContent).bind('DOMMouseScroll' + NS + ' mousewheel' + NS, proxy(this._wheelScroll, this));
|
|
this.content.bind('scroll' + NS, function () {
|
|
scrollables.scrollLeft(this.scrollLeft);
|
|
lockedContent.scrollTop(this.scrollTop);
|
|
});
|
|
var touchScroller = kendo.touchScroller(this.content);
|
|
if (touchScroller && touchScroller.movable) {
|
|
this._touchScroller = touchScroller;
|
|
touchScroller.movable.bind('change', function (e) {
|
|
scrollables.scrollLeft(-e.sender.x);
|
|
if (lockedContent) {
|
|
lockedContent.scrollTop(-e.sender.y);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_wheelScroll: function (e) {
|
|
if (e.ctrlKey) {
|
|
return;
|
|
}
|
|
var delta = kendo.wheelDeltaY(e);
|
|
if (delta) {
|
|
e.preventDefault();
|
|
$(e.currentTarget).one('wheel' + NS, false);
|
|
this.content.scrollTop(this.content.scrollTop() + -delta);
|
|
}
|
|
},
|
|
_progress: function () {
|
|
var messages = this.options.messages;
|
|
if (!this.tbody.find('tr').length) {
|
|
this._showStatus(kendo.template('<span class=\'#= className #\' /> #: messages.loading #')({
|
|
className: classNames.icon + ' ' + classNames.loading,
|
|
messages: messages
|
|
}));
|
|
}
|
|
},
|
|
_error: function (e) {
|
|
if (!this.dataSource.rootNodes().length) {
|
|
this._render({ error: e });
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
e = e || {};
|
|
if (e.action == 'itemchange' && this.editor) {
|
|
return;
|
|
}
|
|
if (this.trigger(DATABINDING)) {
|
|
return;
|
|
}
|
|
this._cancelEditor();
|
|
this._render();
|
|
this._adjustHeight();
|
|
this.trigger(DATABOUND);
|
|
},
|
|
_angularFooters: function (command) {
|
|
var i, footer, aggregates;
|
|
var allAggregates = this.dataSource.aggregates();
|
|
var footerRows = this._footerItems();
|
|
for (i = 0; i < footerRows.length; i++) {
|
|
footer = footerRows.eq(i);
|
|
aggregates = allAggregates[footer.attr('data-parentId')];
|
|
this._angularFooter(command, footer.find('td').get(), aggregates);
|
|
}
|
|
},
|
|
_angularFooter: function (command, cells, aggregates) {
|
|
var columns = this.columns;
|
|
this.angular(command, function () {
|
|
return {
|
|
elements: cells,
|
|
data: map(columns, function (col) {
|
|
return {
|
|
column: col,
|
|
aggregate: aggregates && aggregates[col.field]
|
|
};
|
|
})
|
|
};
|
|
});
|
|
},
|
|
items: function () {
|
|
if (this._hasLockedColumns) {
|
|
return this._items(this.tbody).add(this._items(this.lockedTable));
|
|
} else {
|
|
return this._items(this.tbody);
|
|
}
|
|
},
|
|
_items: function (container) {
|
|
return container.find('tr').filter(function () {
|
|
return !$(this).hasClass(classNames.footerTemplate);
|
|
});
|
|
},
|
|
_footerItems: function () {
|
|
var container = this.tbody;
|
|
if (this._hasLockedColumns) {
|
|
container = container.add(this.lockedTable);
|
|
}
|
|
return container.find('tr').filter(function () {
|
|
return $(this).hasClass(classNames.footerTemplate);
|
|
});
|
|
},
|
|
dataItems: function () {
|
|
var dataItems = kendo.ui.DataBoundWidget.fn.dataItems.call(this);
|
|
if (this._hasLockedColumns) {
|
|
var n = dataItems.length, tmp = new Array(2 * n);
|
|
for (var i = n; --i >= 0;) {
|
|
tmp[i] = tmp[i + n] = dataItems[i];
|
|
}
|
|
dataItems = tmp;
|
|
}
|
|
return dataItems;
|
|
},
|
|
_showStatus: function (message) {
|
|
var status = this.element.find('.k-status');
|
|
var content = $(this.content).add(this.lockedContent);
|
|
if (!status.length) {
|
|
status = $('<div class=\'k-status\' />').appendTo(this.element);
|
|
}
|
|
this._contentTree.render([]);
|
|
if (this._hasLockedColumns) {
|
|
this._lockedContentTree.render([]);
|
|
}
|
|
content.hide();
|
|
status.html(message);
|
|
},
|
|
_hideStatus: function () {
|
|
this.element.find('.k-status').remove();
|
|
$(this.content).add(this.lockedContent).show();
|
|
},
|
|
_adjustHeight: function () {
|
|
var element = this.element;
|
|
var contentWrap = element.find(DOT + classNames.gridContentWrap);
|
|
var header = element.find(DOT + classNames.gridHeader);
|
|
var toolbar = element.find(DOT + classNames.gridToolbar);
|
|
var height;
|
|
var scrollbar = kendo.support.scrollbar();
|
|
element.height(this.options.height);
|
|
var isHeightSet = function (el) {
|
|
var initialHeight, newHeight;
|
|
if (el[0].style.height) {
|
|
return true;
|
|
} else {
|
|
initialHeight = el.height();
|
|
}
|
|
el.height('auto');
|
|
newHeight = el.height();
|
|
el.height('');
|
|
return initialHeight != newHeight;
|
|
};
|
|
if (isHeightSet(element)) {
|
|
height = element.height() - header.outerHeight() - toolbar.outerHeight();
|
|
contentWrap.height(height);
|
|
if (this._hasLockedColumns) {
|
|
scrollbar = this.table[0].offsetWidth > this.table.parent()[0].clientWidth ? scrollbar : 0;
|
|
this.lockedContent.height(height - scrollbar);
|
|
}
|
|
}
|
|
},
|
|
_resize: function () {
|
|
this._applyLockedContainersWidth();
|
|
this._adjustHeight();
|
|
},
|
|
_minScreenSupport: function () {
|
|
var any = this.hideMinScreenCols();
|
|
if (any) {
|
|
this.minScreenResizeHandler = proxy(this.hideMinScreenCols, this);
|
|
$(window).on('resize', this.minScreenResizeHandler);
|
|
}
|
|
},
|
|
hideMinScreenCols: function () {
|
|
var cols = this.columns, any = false, screenWidth = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
|
for (var i = 0; i < cols.length; i++) {
|
|
var col = cols[i];
|
|
var minWidth = col.minScreenWidth;
|
|
if (minWidth !== undefined && minWidth !== null) {
|
|
any = true;
|
|
if (minWidth > screenWidth) {
|
|
this.hideColumn(col);
|
|
} else {
|
|
this.showColumn(col);
|
|
}
|
|
}
|
|
}
|
|
return any;
|
|
},
|
|
destroy: function () {
|
|
DataBoundWidget.fn.destroy.call(this);
|
|
var dataSource = this.dataSource;
|
|
dataSource.unbind(CHANGE, this._refreshHandler);
|
|
dataSource.unbind(ERROR, this._errorHandler);
|
|
dataSource.unbind(PROGRESS, this._progressHandler);
|
|
if (this._resizeHandler) {
|
|
$(window).off('resize' + NS, this._resizeHandler);
|
|
}
|
|
if (this._dragging) {
|
|
this._dragging.destroy();
|
|
this._dragging = null;
|
|
}
|
|
if (this.resizable) {
|
|
this.resizable.destroy();
|
|
this.resizable = null;
|
|
}
|
|
if (this.reorderable) {
|
|
this.reorderable.destroy();
|
|
this.reorderable = null;
|
|
}
|
|
if (this._draggableInstance && this._draggableInstance.element) {
|
|
this._draggableInstance.destroy();
|
|
this._draggableInstance = null;
|
|
}
|
|
if (this.minScreenResizeHandler) {
|
|
$(window).off('resize', this.minScreenResizeHandler);
|
|
}
|
|
this._destroyEditor();
|
|
this.element.off(NS);
|
|
if (this._touchScroller) {
|
|
this._touchScroller.destroy();
|
|
}
|
|
this._autoExpandable = null;
|
|
this._refreshHandler = this._errorHandler = this._progressHandler = null;
|
|
this.thead = this.content = this.tbody = this.table = this.element = this.lockedHeader = this.lockedContent = null;
|
|
this._statusTree = this._headerTree = this._contentTree = this._lockedHeaderColsTree = this._lockedContentColsTree = this._lockedHeaderTree = this._lockedContentTree = null;
|
|
},
|
|
options: {
|
|
name: 'TreeList',
|
|
columns: [],
|
|
autoBind: true,
|
|
scrollable: true,
|
|
selectable: false,
|
|
sortable: false,
|
|
toolbar: null,
|
|
height: null,
|
|
columnMenu: false,
|
|
messages: {
|
|
noRows: 'No records to display',
|
|
loading: 'Loading...',
|
|
requestFailed: 'Request failed.',
|
|
retry: 'Retry',
|
|
commands: {
|
|
edit: 'Edit',
|
|
update: 'Update',
|
|
canceledit: 'Cancel',
|
|
create: 'Add new record',
|
|
createchild: 'Add child record',
|
|
destroy: 'Delete',
|
|
excel: 'Export to Excel',
|
|
pdf: 'Export to PDF'
|
|
}
|
|
},
|
|
excel: { hierarchy: true },
|
|
resizable: false,
|
|
filterable: false,
|
|
editable: false,
|
|
reorderable: false
|
|
},
|
|
events: [
|
|
CHANGE,
|
|
EDIT,
|
|
SAVE,
|
|
REMOVE,
|
|
EXPAND,
|
|
COLLAPSE,
|
|
DATABINDING,
|
|
DATABOUND,
|
|
CANCEL,
|
|
DRAGSTART,
|
|
DRAG,
|
|
DROP,
|
|
DRAGEND,
|
|
FILTERMENUINIT,
|
|
COLUMNHIDE,
|
|
COLUMNSHOW,
|
|
COLUMNREORDER,
|
|
COLUMNRESIZE,
|
|
COLUMNMENUINIT,
|
|
COLUMNLOCK,
|
|
COLUMNUNLOCK
|
|
],
|
|
_toggle: function (model, expand) {
|
|
var defaultPromise = $.Deferred().resolve().promise();
|
|
var loaded = model.loaded();
|
|
if (model._error) {
|
|
model.expanded = false;
|
|
model._error = undefined;
|
|
}
|
|
if (!loaded && model.expanded) {
|
|
return defaultPromise;
|
|
}
|
|
if (typeof expand == 'undefined') {
|
|
expand = !model.expanded;
|
|
}
|
|
model.expanded = expand;
|
|
if (!loaded) {
|
|
defaultPromise = this.dataSource.load(model).always(proxy(function () {
|
|
this._render();
|
|
this._syncLockedContentHeight();
|
|
}, this));
|
|
}
|
|
this._render();
|
|
this._syncLockedContentHeight();
|
|
return defaultPromise;
|
|
},
|
|
expand: function (row) {
|
|
return this._toggle(this.dataItem(row), true);
|
|
},
|
|
collapse: function (row) {
|
|
return this._toggle(this.dataItem(row), false);
|
|
},
|
|
_toggleChildren: function (e) {
|
|
var icon = $(e.currentTarget);
|
|
var model = this.dataItem(icon);
|
|
var event = !model.expanded ? EXPAND : COLLAPSE;
|
|
if (!this.trigger(event, { model: model })) {
|
|
this._toggle(model);
|
|
}
|
|
e.preventDefault();
|
|
},
|
|
_attachEvents: function () {
|
|
var icons = DOT + classNames.iconCollapse + ', .' + classNames.iconExpand + ', .' + classNames.refresh;
|
|
var retryButton = DOT + classNames.retry;
|
|
var dataSource = this.dataSource;
|
|
this.element.on(MOUSEDOWN + NS, icons, proxy(this._toggleChildren, this)).on(CLICK + NS, retryButton, proxy(dataSource.fetch, dataSource)).on(CLICK + NS, '.k-button[data-command]', proxy(this._commandClick, this));
|
|
},
|
|
_commandByName: function (name) {
|
|
var columns = this.columns;
|
|
var toolbar = $.isArray(this.options.toolbar) ? this.options.toolbar : [];
|
|
var i, j, commands, currentName;
|
|
name = name.toLowerCase();
|
|
if (defaultCommands[name]) {
|
|
return defaultCommands[name];
|
|
}
|
|
for (i = 0; i < columns.length; i++) {
|
|
commands = columns[i].command;
|
|
if (commands) {
|
|
for (j = 0; j < commands.length; j++) {
|
|
currentName = commands[j].name;
|
|
if (!currentName) {
|
|
continue;
|
|
}
|
|
if (currentName.toLowerCase() == name) {
|
|
return commands[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < toolbar.length; i++) {
|
|
currentName = toolbar[i].name;
|
|
if (!currentName) {
|
|
continue;
|
|
}
|
|
if (currentName.toLowerCase() == name) {
|
|
return toolbar[i];
|
|
}
|
|
}
|
|
},
|
|
_commandClick: function (e) {
|
|
var button = $(e.currentTarget);
|
|
var commandName = button.attr('data-command');
|
|
var command = this._commandByName(commandName);
|
|
var row = button.parentsUntil(this.wrapper, 'tr');
|
|
row = row.length ? row : undefined;
|
|
if (command) {
|
|
if (command.methodName) {
|
|
this[command.methodName](row);
|
|
} else if (command.click) {
|
|
command.click.call(this, e);
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_ensureExpandableColumn: function () {
|
|
if (this._autoExpandable) {
|
|
delete this._autoExpandable.expandable;
|
|
}
|
|
var visibleColumns = grep(this.columns, not(is('hidden')));
|
|
var expandableColumns = grep(visibleColumns, is('expandable'));
|
|
if (this.columns.length && !expandableColumns.length) {
|
|
this._autoExpandable = visibleColumns[0];
|
|
visibleColumns[0].expandable = true;
|
|
}
|
|
},
|
|
_columns: function () {
|
|
var columns = this.options.columns || [];
|
|
this.columns = map(columns, function (column) {
|
|
column = typeof column === 'string' ? { field: column } : column;
|
|
return extend({ encoded: true }, column);
|
|
});
|
|
var lockedColumns = this._lockedColumns();
|
|
if (lockedColumns.length > 0) {
|
|
this._hasLockedColumns = true;
|
|
this.columns = lockedColumns.concat(this._nonLockedColumns());
|
|
}
|
|
this._ensureExpandableColumn();
|
|
this._columnTemplates();
|
|
this._columnAttributes();
|
|
},
|
|
_columnTemplates: function () {
|
|
var idx, length, column;
|
|
var columns = this.columns;
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.template) {
|
|
column.template = kendo.template(column.template);
|
|
}
|
|
if (column.headerTemplate) {
|
|
column.headerTemplate = kendo.template(column.headerTemplate);
|
|
}
|
|
if (column.footerTemplate) {
|
|
column.footerTemplate = kendo.template(column.footerTemplate);
|
|
}
|
|
}
|
|
},
|
|
_columnAttributes: function () {
|
|
var idx, length;
|
|
var columns = this.columns;
|
|
function convertStyle(attr) {
|
|
var properties, i, declaration;
|
|
if (attr && attr.style) {
|
|
properties = attr.style.split(';');
|
|
attr.style = {};
|
|
for (i = 0; i < properties.length; i++) {
|
|
declaration = properties[i].split(':');
|
|
var name = $.trim(declaration[0]);
|
|
if (name) {
|
|
attr.style[$.camelCase(name)] = $.trim(declaration[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (idx = 0, length = columns.length; idx < length; idx++) {
|
|
convertStyle(columns[idx].attributes);
|
|
convertStyle(columns[idx].headerAttributes);
|
|
}
|
|
},
|
|
_layout: function () {
|
|
var columns = this.columns;
|
|
var element = this.element;
|
|
var layout = '';
|
|
this.wrapper = element.addClass(classNames.wrapper);
|
|
layout = '<div class=\'#= gridHeader #\'>';
|
|
if (this._hasLockedColumns) {
|
|
layout += '<div class=\'k-grid-header-locked\'>' + '<table role=\'grid\'>' + '<colgroup></colgroup>' + '<thead role=\'rowgroup\' />' + '</table>' + '</div>';
|
|
}
|
|
layout += '<div class=\'#= gridHeaderWrap #\'>' + '<table role=\'grid\'>' + '<colgroup></colgroup>' + '<thead role=\'rowgroup\' />' + '</table>' + '</div>' + '</div>';
|
|
if (this._hasLockedColumns) {
|
|
layout += '<div class=\'k-grid-content-locked\'>' + '<table role=\'treegrid\' tabindex=\'0\'>' + '<colgroup></colgroup>' + '<tbody />' + '</table>' + '</div>';
|
|
}
|
|
layout += '<div class=\'#= gridContentWrap # k-auto-scrollable\'>' + '<table role=\'treegrid\' tabindex=\'0\'>' + '<colgroup></colgroup>' + '<tbody />' + '</table>' + '</div>';
|
|
if (!this.options.scrollable) {
|
|
layout = '<table role=\'treegrid\' tabindex=\'0\'>' + '<colgroup></colgroup>' + '<thead class=\'#= gridHeader #\' role=\'rowgroup\' />' + '<tbody />' + '</table>';
|
|
}
|
|
if (this.options.toolbar) {
|
|
layout = '<div class=\'#= header # #= gridToolbar #\' />' + layout;
|
|
}
|
|
element.append(kendo.template(layout)(classNames) + '<div class=\'k-status\' />');
|
|
this.toolbar = element.find(DOT + classNames.gridToolbar);
|
|
var header = element.find(DOT + classNames.gridHeader).find('thead').addBack().filter('thead');
|
|
this.thead = header.last();
|
|
if (this.options.scrollable) {
|
|
var rtl = kendo.support.isRtl(element);
|
|
element.find('div.' + classNames.gridHeader).css(rtl ? 'padding-left' : 'padding-right', kendo.support.scrollbar());
|
|
}
|
|
var content = element.find(DOT + classNames.gridContentWrap);
|
|
if (!content.length) {
|
|
content = element;
|
|
} else {
|
|
this.content = content;
|
|
}
|
|
this.table = content.find('>table');
|
|
this.tbody = this.table.find('>tbody');
|
|
if (this._hasLockedColumns) {
|
|
this.lockedHeader = header.first().closest('.k-grid-header-locked');
|
|
this.lockedContent = element.find('.k-grid-content-locked');
|
|
this.lockedTable = this.lockedContent.children();
|
|
}
|
|
this._initVirtualTrees();
|
|
this._renderCols();
|
|
this._renderHeader();
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: header.find('th.k-header').get(),
|
|
data: map(columns, function (col) {
|
|
return { column: col };
|
|
})
|
|
};
|
|
});
|
|
},
|
|
_initVirtualTrees: function () {
|
|
this._headerColsTree = new kendoDom.Tree(this.thead.prev()[0]);
|
|
this._contentColsTree = new kendoDom.Tree(this.tbody.prev()[0]);
|
|
this._headerTree = new kendoDom.Tree(this.thead[0]);
|
|
this._contentTree = new kendoDom.Tree(this.tbody[0]);
|
|
this._statusTree = new kendoDom.Tree(this.element.children('.k-status')[0]);
|
|
if (this.lockedHeader) {
|
|
this._lockedHeaderColsTree = new kendoDom.Tree(this.lockedHeader.find('colgroup')[0]);
|
|
this._lockedContentColsTree = new kendoDom.Tree(this.lockedTable.find('>colgroup')[0]);
|
|
this._lockedHeaderTree = new kendoDom.Tree(this.lockedHeader.find('thead')[0]);
|
|
this._lockedContentTree = new kendoDom.Tree(this.lockedTable.find('>tbody')[0]);
|
|
}
|
|
},
|
|
_toolbar: function () {
|
|
var options = this.options.toolbar;
|
|
var toolbar = this.toolbar;
|
|
if (!options) {
|
|
return;
|
|
}
|
|
if ($.isArray(options)) {
|
|
var buttons = this._buildCommands(options);
|
|
new kendoDom.Tree(toolbar[0]).render(buttons);
|
|
} else {
|
|
toolbar.append(kendo.template(options)({}));
|
|
}
|
|
this.angular('compile', function () {
|
|
return { elements: toolbar.get() };
|
|
});
|
|
},
|
|
_lockedColumns: function () {
|
|
return grep(this.columns, is('locked'));
|
|
},
|
|
_nonLockedColumns: function () {
|
|
return grep(this.columns, not(is('locked')));
|
|
},
|
|
_templateColumns: function () {
|
|
return grep(this.columns, is('template'));
|
|
},
|
|
_flushCache: function () {
|
|
if (this.options.$angular && this._templateColumns().length) {
|
|
this._contentTree.render([]);
|
|
if (this._hasLockedColumns) {
|
|
this._lockedContentTree.render([]);
|
|
}
|
|
}
|
|
},
|
|
_render: function (options) {
|
|
options = options || {};
|
|
var messages = this.options.messages;
|
|
var data = this.dataSource.rootNodes();
|
|
var selected = this.select().removeClass('k-state-selected').map(function (_, row) {
|
|
return $(row).attr(kendo.attr('uid'));
|
|
});
|
|
this._absoluteIndex = 0;
|
|
this._angularItems('cleanup');
|
|
this._angularFooters('cleanup');
|
|
this._flushCache();
|
|
if (options.error) {
|
|
this._showStatus(kendo.template('#: messages.requestFailed # ' + '<button class=\'#= buttonClass #\'>#: messages.retry #</button>')({
|
|
buttonClass: [
|
|
classNames.button,
|
|
classNames.retry
|
|
].join(' '),
|
|
messages: messages
|
|
}));
|
|
} else if (!data.length) {
|
|
this._showStatus(kendo.htmlEncode(messages.noRows));
|
|
} else {
|
|
this._hideStatus();
|
|
this._contentTree.render(this._trs({
|
|
columns: this._nonLockedColumns(),
|
|
aggregates: options.aggregates,
|
|
selected: selected,
|
|
data: data,
|
|
visible: true,
|
|
level: 0
|
|
}));
|
|
if (this._hasLockedColumns) {
|
|
this._absoluteIndex = 0;
|
|
this._lockedContentTree.render(this._trs({
|
|
columns: this._lockedColumns(),
|
|
aggregates: options.aggregates,
|
|
selected: selected,
|
|
data: data,
|
|
visible: true,
|
|
level: 0
|
|
}));
|
|
}
|
|
}
|
|
if (this._touchScroller) {
|
|
this._touchScroller.contentResized();
|
|
}
|
|
this._muteAngularRebind(function () {
|
|
this._angularItems('compile');
|
|
this._angularFooters('compile');
|
|
});
|
|
this._adjustRowsHeight();
|
|
},
|
|
_adjustRowsHeight: function () {
|
|
if (!this._hasLockedColumns) {
|
|
return;
|
|
}
|
|
var table = this.table;
|
|
var lockedTable = this.lockedTable;
|
|
var rows = table[0].rows;
|
|
var length = rows.length;
|
|
var idx;
|
|
var lockedRows = lockedTable[0].rows;
|
|
var containers = table.add(lockedTable);
|
|
var containersLength = containers.length;
|
|
var heights = [];
|
|
var lockedHeaderRows = this.lockedHeader.find('tr');
|
|
var headerRows = this.thead.find('tr');
|
|
lockedHeaderRows.add(headerRows).height('auto').height(Math.max(lockedHeaderRows.height(), headerRows.height()));
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (!lockedRows[idx]) {
|
|
break;
|
|
}
|
|
if (rows[idx].style.height) {
|
|
rows[idx].style.height = lockedRows[idx].style.height = '';
|
|
}
|
|
var offsetHeight1 = rows[idx].offsetHeight;
|
|
var offsetHeight2 = lockedRows[idx].offsetHeight;
|
|
var height = 0;
|
|
if (offsetHeight1 > offsetHeight2) {
|
|
height = offsetHeight1;
|
|
} else if (offsetHeight1 < offsetHeight2) {
|
|
height = offsetHeight2;
|
|
}
|
|
heights.push(height);
|
|
}
|
|
for (idx = 0; idx < containersLength; idx++) {
|
|
containers[idx].style.display = 'none';
|
|
}
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (heights[idx]) {
|
|
rows[idx].style.height = lockedRows[idx].style.height = heights[idx] + 1 + 'px';
|
|
}
|
|
}
|
|
for (idx = 0; idx < containersLength; idx++) {
|
|
containers[idx].style.display = '';
|
|
}
|
|
},
|
|
_ths: function (columns) {
|
|
var ths = [];
|
|
var column, title, children, cellClasses, attr, headerContent;
|
|
for (var i = 0, length = columns.length; i < length; i++) {
|
|
column = columns[i];
|
|
children = [];
|
|
cellClasses = [classNames.header];
|
|
if (column.headerTemplate) {
|
|
title = column.headerTemplate({});
|
|
} else {
|
|
title = column.title || column.field || '';
|
|
}
|
|
if (column.headerTemplate) {
|
|
headerContent = kendoHtmlElement(title);
|
|
} else {
|
|
headerContent = kendoTextElement(title);
|
|
}
|
|
if (column.sortable) {
|
|
children.push(kendoDomElement('a', {
|
|
href: '#',
|
|
className: classNames.link
|
|
}, [headerContent]));
|
|
} else {
|
|
children.push(headerContent);
|
|
}
|
|
attr = {
|
|
'data-field': column.field,
|
|
'data-title': column.title,
|
|
'style': column.hidden === true ? { 'display': 'none' } : {},
|
|
className: cellClasses.join(' '),
|
|
'role': 'columnheader'
|
|
};
|
|
attr = extend(true, {}, attr, column.headerAttributes);
|
|
ths.push(kendoDomElement('th', attr, children));
|
|
}
|
|
return ths;
|
|
},
|
|
_cols: function (columns) {
|
|
var cols = [];
|
|
var width, attr;
|
|
for (var i = 0; i < columns.length; i++) {
|
|
if (columns[i].hidden === true) {
|
|
continue;
|
|
}
|
|
width = columns[i].width;
|
|
attr = {};
|
|
if (width && parseInt(width, 10) !== 0) {
|
|
attr.style = { width: typeof width === 'string' ? width : width + 'px' };
|
|
}
|
|
cols.push(kendoDomElement('col', attr));
|
|
}
|
|
return cols;
|
|
},
|
|
_renderCols: function () {
|
|
var columns = this._nonLockedColumns();
|
|
this._headerColsTree.render(this._cols(columns));
|
|
if (this.options.scrollable) {
|
|
this._contentColsTree.render(this._cols(columns));
|
|
}
|
|
if (this._hasLockedColumns) {
|
|
columns = this._lockedColumns();
|
|
this._lockedHeaderColsTree.render(this._cols(columns));
|
|
this._lockedContentColsTree.render(this._cols(columns));
|
|
}
|
|
},
|
|
_renderHeader: function () {
|
|
var columns = this._nonLockedColumns();
|
|
this._headerTree.render([kendoDomElement('tr', { 'role': 'row' }, this._ths(columns))]);
|
|
if (this._hasLockedColumns) {
|
|
columns = this._lockedColumns();
|
|
this._lockedHeaderTree.render([kendoDomElement('tr', { 'role': 'row' }, this._ths(columns))]);
|
|
this._applyLockedContainersWidth();
|
|
}
|
|
},
|
|
_applyLockedContainersWidth: function () {
|
|
if (!this._hasLockedColumns) {
|
|
return;
|
|
}
|
|
var lockedWidth = columnsWidth(this.lockedHeader.find('>table>colgroup>col'));
|
|
var headerTable = this.thead.parent();
|
|
var nonLockedWidth = columnsWidth(headerTable.find('>colgroup>col'));
|
|
var wrapperWidth = this.wrapper[0].clientWidth;
|
|
var scrollbar = kendo.support.scrollbar();
|
|
if (lockedWidth >= wrapperWidth) {
|
|
lockedWidth = wrapperWidth - 3 * scrollbar;
|
|
}
|
|
this.lockedHeader.add(this.lockedContent).width(lockedWidth);
|
|
headerTable.add(this.table).width(nonLockedWidth);
|
|
var width = wrapperWidth - lockedWidth - 2;
|
|
this.content.width(width);
|
|
headerTable.parent().width(width - scrollbar);
|
|
},
|
|
_trs: function (options) {
|
|
var model, attr, className, hasChildren, childNodes, i, length;
|
|
var rows = [];
|
|
var level = options.level;
|
|
var data = options.data;
|
|
var dataSource = this.dataSource;
|
|
var aggregates = dataSource.aggregates() || {};
|
|
var columns = options.columns;
|
|
for (i = 0, length = data.length; i < length; i++) {
|
|
className = [];
|
|
model = data[i];
|
|
childNodes = model.loaded() && dataSource.childNodes(model);
|
|
hasChildren = childNodes && childNodes.length;
|
|
attr = { 'role': 'row' };
|
|
attr[kendo.attr('uid')] = model.uid;
|
|
if (hasChildren) {
|
|
attr['aria-expanded'] = !!model.expanded;
|
|
}
|
|
if (options.visible) {
|
|
if (this._absoluteIndex % 2 !== 0) {
|
|
className.push(classNames.alt);
|
|
}
|
|
this._absoluteIndex++;
|
|
} else {
|
|
attr.style = { display: 'none' };
|
|
}
|
|
if ($.inArray(model.uid, options.selected) >= 0) {
|
|
className.push(classNames.selected);
|
|
}
|
|
if (hasChildren) {
|
|
className.push(classNames.group);
|
|
}
|
|
if (model._edit) {
|
|
className.push('k-grid-edit-row');
|
|
}
|
|
attr.className = className.join(' ');
|
|
rows.push(this._tds({
|
|
model: model,
|
|
attr: attr,
|
|
level: level
|
|
}, columns, proxy(this._td, this)));
|
|
if (hasChildren) {
|
|
rows = rows.concat(this._trs({
|
|
columns: columns,
|
|
aggregates: aggregates,
|
|
selected: options.selected,
|
|
visible: options.visible && !!model.expanded,
|
|
data: childNodes,
|
|
level: level + 1
|
|
}));
|
|
}
|
|
}
|
|
if (this._hasFooterTemplate()) {
|
|
attr = {
|
|
className: classNames.footerTemplate,
|
|
'data-parentId': model.parentId
|
|
};
|
|
if (!options.visible) {
|
|
attr.style = { display: 'none' };
|
|
}
|
|
rows.push(this._tds({
|
|
model: aggregates[model.parentId],
|
|
attr: attr,
|
|
level: level
|
|
}, columns, this._footerTd));
|
|
}
|
|
return rows;
|
|
},
|
|
_footerTd: function (options) {
|
|
var content = [];
|
|
var column = options.column;
|
|
var template = options.column.footerTemplate || $.noop;
|
|
var aggregates = options.model[column.field] || {};
|
|
var attr = {
|
|
'role': 'gridcell',
|
|
'style': column.hidden === true ? { 'display': 'none' } : {}
|
|
};
|
|
if (column.expandable) {
|
|
content = content.concat(createPlaceholders({
|
|
level: options.level + 1,
|
|
className: classNames.iconPlaceHolder
|
|
}));
|
|
}
|
|
if (column.attributes) {
|
|
extend(attr, column.attributes);
|
|
}
|
|
content.push(kendoHtmlElement(template(aggregates) || ''));
|
|
return kendoDomElement('td', attr, content);
|
|
},
|
|
_hasFooterTemplate: function () {
|
|
return !!grep(this.columns, function (c) {
|
|
return c.footerTemplate;
|
|
}).length;
|
|
},
|
|
_tds: function (options, columns, renderer) {
|
|
var children = [];
|
|
var column;
|
|
for (var i = 0, l = columns.length; i < l; i++) {
|
|
column = columns[i];
|
|
children.push(renderer({
|
|
model: options.model,
|
|
column: column,
|
|
level: options.level
|
|
}));
|
|
}
|
|
return kendoDomElement('tr', options.attr, children);
|
|
},
|
|
_td: function (options) {
|
|
var children = [];
|
|
var model = options.model;
|
|
var column = options.column;
|
|
var iconClass;
|
|
var attr = {
|
|
'role': 'gridcell',
|
|
'style': column.hidden === true ? { 'display': 'none' } : {}
|
|
};
|
|
if (model._edit && column.field && model.editable(column.field)) {
|
|
attr[kendo.attr('container-for')] = column.field;
|
|
} else {
|
|
if (column.expandable) {
|
|
children = createPlaceholders({
|
|
level: options.level,
|
|
className: classNames.iconPlaceHolder
|
|
});
|
|
iconClass = [classNames.icon];
|
|
if (model.hasChildren) {
|
|
iconClass.push(model.expanded ? classNames.iconCollapse : classNames.iconExpand);
|
|
} else {
|
|
iconClass.push(classNames.iconHidden);
|
|
}
|
|
if (model._error) {
|
|
iconClass.push(classNames.refresh);
|
|
} else if (!model.loaded() && model.expanded) {
|
|
iconClass.push(classNames.loading);
|
|
}
|
|
children.push(kendoDomElement('span', { className: iconClass.join(' ') }));
|
|
attr.style['white-space'] = 'nowrap';
|
|
}
|
|
if (column.attributes) {
|
|
extend(true, attr, column.attributes);
|
|
}
|
|
if (column.command) {
|
|
if (model._edit) {
|
|
children = this._buildCommands([
|
|
'update',
|
|
'canceledit'
|
|
]);
|
|
} else {
|
|
children = this._buildCommands(column.command);
|
|
}
|
|
} else {
|
|
children.push(this._cellContent(column, model));
|
|
}
|
|
}
|
|
return kendoDomElement('td', attr, children);
|
|
},
|
|
_cellContent: function (column, model) {
|
|
var value;
|
|
if (column.template) {
|
|
value = column.template(model);
|
|
} else if (column.field) {
|
|
value = model.get(column.field);
|
|
if (value !== null && column.format) {
|
|
value = kendo.format(column.format, value);
|
|
}
|
|
}
|
|
if (value === null || typeof value == 'undefined') {
|
|
value = '';
|
|
}
|
|
if (column.template || !column.encoded) {
|
|
return kendoHtmlElement(value);
|
|
} else {
|
|
return kendoTextElement(value);
|
|
}
|
|
},
|
|
_buildCommands: function (commands) {
|
|
var i, result = [];
|
|
for (i = 0; i < commands.length; i++) {
|
|
result.push(this._button(commands[i]));
|
|
}
|
|
return result;
|
|
},
|
|
_button: function (command) {
|
|
var name = (command.name || command).toLowerCase();
|
|
var text = this.options.messages.commands[name];
|
|
var icon = [];
|
|
command = extend({}, defaultCommands[name], { text: text }, command);
|
|
if (command.imageClass) {
|
|
icon.push(kendoDomElement('span', {
|
|
className: [
|
|
'k-icon',
|
|
command.imageClass
|
|
].join(' ')
|
|
}));
|
|
}
|
|
return kendoDomElement('button', {
|
|
'type': 'button',
|
|
'data-command': name,
|
|
className: [
|
|
'k-button',
|
|
'k-button-icontext',
|
|
command.className
|
|
].join(' ')
|
|
}, icon.concat([kendoTextElement(command.text || command.name)]));
|
|
},
|
|
_positionResizeHandle: function (e) {
|
|
var th = $(e.currentTarget);
|
|
var resizeHandle = this.resizeHandle;
|
|
var position = th.position();
|
|
var left = position.left;
|
|
var cellWidth = th.outerWidth();
|
|
var container = th.closest('div');
|
|
var clientX = e.clientX + $(window).scrollLeft();
|
|
var indicatorWidth = this.options.columnResizeHandleWidth || 3;
|
|
left += container.scrollLeft();
|
|
if (!resizeHandle) {
|
|
resizeHandle = this.resizeHandle = $('<div class="k-resize-handle"><div class="k-resize-handle-inner" /></div>');
|
|
}
|
|
var cellOffset = th.offset().left + cellWidth;
|
|
var show = clientX > cellOffset - indicatorWidth && clientX < cellOffset + indicatorWidth;
|
|
if (!show) {
|
|
resizeHandle.hide();
|
|
return;
|
|
}
|
|
container.append(resizeHandle);
|
|
resizeHandle.show().css({
|
|
top: position.top,
|
|
left: left + cellWidth - indicatorWidth - 1,
|
|
height: th.outerHeight(),
|
|
width: indicatorWidth * 3
|
|
}).data('th', th);
|
|
var that = this;
|
|
resizeHandle.off('dblclick' + NS).on('dblclick' + NS, function () {
|
|
var index = th.index();
|
|
if ($.contains(that.thead[0], th[0])) {
|
|
index += grep(that.columns, function (val) {
|
|
return val.locked && !val.hidden;
|
|
}).length;
|
|
}
|
|
that.autoFitColumn(index);
|
|
});
|
|
},
|
|
autoFitColumn: function (column) {
|
|
var that = this, options = that.options, columns = that.columns, index, browser = kendo.support.browser, th, headerTable, isLocked, visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find('>table>thead')).filter(isCellVisible).length : 0, col;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else if (isPlainObject(column)) {
|
|
column = grep(columns, function (item) {
|
|
return item === column;
|
|
})[0];
|
|
} else {
|
|
column = grep(columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || column.hidden) {
|
|
return;
|
|
}
|
|
index = inArray(column, columns);
|
|
isLocked = column.locked;
|
|
if (isLocked) {
|
|
headerTable = that.lockedHeader.children('table');
|
|
} else {
|
|
headerTable = that.thead.parent();
|
|
}
|
|
th = headerTable.find('[data-index=\'' + index + '\']');
|
|
var contentTable = isLocked ? that.lockedTable : that.table, footer = that.footer || $();
|
|
if (that.footer && that.lockedContent) {
|
|
footer = isLocked ? that.footer.children('.k-grid-footer-locked') : that.footer.children('.k-grid-footer-wrap');
|
|
}
|
|
var footerTable = footer.find('table').first();
|
|
if (that.lockedHeader && visibleLocked >= index && !isLocked) {
|
|
index -= visibleLocked;
|
|
}
|
|
for (var j = 0; j < columns.length; j++) {
|
|
if (columns[j] === column) {
|
|
break;
|
|
} else {
|
|
if (columns[j].hidden) {
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
if (options.scrollable) {
|
|
col = headerTable.find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')').add(contentTable.children('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')')).add(footerTable.find('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')'));
|
|
} else {
|
|
col = contentTable.children('colgroup').find('col:not(.k-group-col):not(.k-hierarchy-col):eq(' + index + ')');
|
|
}
|
|
var tables = headerTable.add(contentTable).add(footerTable);
|
|
var oldColumnWidth = th.outerWidth();
|
|
col.width('');
|
|
tables.css('table-layout', 'fixed');
|
|
col.width('auto');
|
|
tables.addClass('k-autofitting');
|
|
tables.css('table-layout', '');
|
|
var newColumnWidth = Math.ceil(Math.max(th.outerWidth(), contentTable.find('tr').eq(0).children('td:visible').eq(index).outerWidth(), footerTable.find('tr').eq(0).children('td:visible').eq(index).outerWidth()));
|
|
col.width(newColumnWidth);
|
|
column.width = newColumnWidth;
|
|
if (options.scrollable) {
|
|
var cols = headerTable.find('col'), colWidth, totalWidth = 0;
|
|
for (var idx = 0, length = cols.length; idx < length; idx += 1) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
totalWidth += parseInt(colWidth, 10);
|
|
} else {
|
|
totalWidth = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (totalWidth) {
|
|
tables.each(function () {
|
|
this.style.width = totalWidth + 'px';
|
|
});
|
|
}
|
|
}
|
|
if (browser.msie && browser.version == 8) {
|
|
tables.css('display', 'inline-table');
|
|
setTimeout(function () {
|
|
tables.css('display', 'table');
|
|
}, 1);
|
|
}
|
|
tables.removeClass('k-autofitting');
|
|
that.trigger(COLUMNRESIZE, {
|
|
column: column,
|
|
oldWidth: oldColumnWidth,
|
|
newWidth: newColumnWidth
|
|
});
|
|
that._applyLockedContainersWidth();
|
|
that._syncLockedContentHeight();
|
|
that._syncLockedHeaderHeight();
|
|
},
|
|
_adjustLockedHorizontalScrollBar: function () {
|
|
var table = this.table, content = table.parent();
|
|
var scrollbar = table[0].offsetWidth > content[0].clientWidth ? kendo.support.scrollbar() : 0;
|
|
this.lockedContent.height(content.height() - scrollbar);
|
|
},
|
|
_syncLockedContentHeight: function () {
|
|
if (this.lockedTable) {
|
|
if (!this._touchScroller) {
|
|
this._adjustLockedHorizontalScrollBar();
|
|
}
|
|
this._adjustRowsHeight(this.table, this.lockedTable);
|
|
}
|
|
},
|
|
_syncLockedHeaderHeight: function () {
|
|
if (this.lockedHeader) {
|
|
var lockedTable = this.lockedHeader.children('table');
|
|
var table = this.thead.parent();
|
|
this._adjustRowsHeight(lockedTable, table);
|
|
syncTableHeight(lockedTable, table);
|
|
}
|
|
},
|
|
_resizable: function () {
|
|
if (!this.options.resizable) {
|
|
return;
|
|
}
|
|
if (this.resizable) {
|
|
this.resizable.destroy();
|
|
}
|
|
var treelist = this;
|
|
$(this.lockedHeader).find('thead').add(this.thead).on('mousemove' + NS, 'th', $.proxy(this._positionResizeHandle, this));
|
|
this.resizable = new kendo.ui.Resizable(this.wrapper, {
|
|
handle: '.k-resize-handle',
|
|
start: function (e) {
|
|
var th = $(e.currentTarget).data('th');
|
|
var colSelector = 'col:eq(' + $.inArray(th[0], th.parent().children().filter(':visible')) + ')';
|
|
var header, contentTable;
|
|
treelist.wrapper.addClass('k-grid-column-resizing');
|
|
if (treelist.lockedHeader && $.contains(treelist.lockedHeader[0], th[0])) {
|
|
header = treelist.lockedHeader;
|
|
contentTable = treelist.lockedTable;
|
|
} else {
|
|
header = treelist.thead.parent();
|
|
contentTable = treelist.table;
|
|
}
|
|
this.col = contentTable.children('colgroup').find(colSelector).add(header.find(colSelector));
|
|
this.th = th;
|
|
this.startLocation = e.x.location;
|
|
this.columnWidth = th.outerWidth();
|
|
this.table = this.col.closest('table');
|
|
this.totalWidth = this.table.width();
|
|
},
|
|
resize: function (e) {
|
|
var minColumnWidth = 11;
|
|
var delta = e.x.location - this.startLocation;
|
|
if (this.columnWidth + delta < minColumnWidth) {
|
|
delta = minColumnWidth - this.columnWidth;
|
|
}
|
|
this.table.width(this.totalWidth + delta);
|
|
this.col.width(this.columnWidth + delta);
|
|
},
|
|
resizeend: function () {
|
|
treelist.wrapper.removeClass('k-grid-column-resizing');
|
|
var field = this.th.attr('data-field');
|
|
var column = grep(treelist.columns, function (c) {
|
|
return c.field == field;
|
|
});
|
|
var newWidth = Math.floor(this.th.outerWidth());
|
|
column[0].width = newWidth;
|
|
treelist._resize();
|
|
treelist._adjustRowsHeight();
|
|
treelist.trigger(COLUMNRESIZE, {
|
|
column: column,
|
|
oldWidth: this.columnWidth,
|
|
newWidth: newWidth
|
|
});
|
|
this.table = this.col = this.th = null;
|
|
}
|
|
});
|
|
},
|
|
_sortable: function () {
|
|
var columns = this.columns;
|
|
var column;
|
|
var sortableInstance;
|
|
var cells = $(this.lockedHeader).add(this.thead).find('th');
|
|
var cell, idx, length;
|
|
var fieldAttr = kendo.attr('field');
|
|
var sortable = this.options.sortable;
|
|
if (!sortable) {
|
|
return;
|
|
}
|
|
for (idx = 0, length = cells.length; idx < length; idx++) {
|
|
column = columns[idx];
|
|
if (column.sortable !== false && !column.command && column.field) {
|
|
cell = cells.eq(idx);
|
|
sortableInstance = cell.data('kendoColumnSorter');
|
|
if (sortableInstance) {
|
|
sortableInstance.destroy();
|
|
}
|
|
cell.attr(fieldAttr, column.field).kendoColumnSorter(extend({}, sortable, column.sortable, { dataSource: this.dataSource }));
|
|
}
|
|
}
|
|
},
|
|
_filterable: function () {
|
|
var cells = $(this.lockedHeader).add(this.thead).find('th');
|
|
var filterable = this.options.filterable;
|
|
var idx, length, column, cell, filterMenuInstance;
|
|
if (!filterable || this.options.columnMenu) {
|
|
return;
|
|
}
|
|
var filterInit = proxy(function (e) {
|
|
this.trigger(FILTERMENUINIT, {
|
|
field: e.field,
|
|
container: e.container
|
|
});
|
|
}, this);
|
|
for (idx = 0, length = cells.length; idx < length; idx++) {
|
|
column = this.columns[idx];
|
|
cell = cells.eq(idx);
|
|
filterMenuInstance = cell.data('kendoFilterMenu');
|
|
if (filterMenuInstance) {
|
|
filterMenuInstance.destroy();
|
|
}
|
|
if (column.command || column.filterable === false) {
|
|
continue;
|
|
}
|
|
cell.kendoFilterMenu(extend(true, {}, filterable, column.filterable, {
|
|
dataSource: this.dataSource,
|
|
init: filterInit
|
|
}));
|
|
}
|
|
},
|
|
_change: function () {
|
|
this.trigger(CHANGE);
|
|
},
|
|
_selectable: function () {
|
|
var selectable = this.options.selectable;
|
|
var filter;
|
|
var element = this.table;
|
|
var useAllItems;
|
|
if (selectable) {
|
|
selectable = kendo.ui.Selectable.parseOptions(selectable);
|
|
if (this._hasLockedColumns) {
|
|
element = element.add(this.lockedTable);
|
|
useAllItems = selectable.multiple && selectable.cell;
|
|
}
|
|
filter = '>tbody>tr:not(.k-footer-template)';
|
|
if (selectable.cell) {
|
|
filter = filter + '>td';
|
|
}
|
|
this.selectable = new kendo.ui.Selectable(element, {
|
|
filter: filter,
|
|
aria: true,
|
|
multiple: selectable.multiple,
|
|
change: proxy(this._change, this),
|
|
useAllItems: useAllItems,
|
|
continuousItems: proxy(this._continuousItems, this, filter, selectable.cell),
|
|
relatedTarget: !selectable.cell && this._hasLockedColumns ? proxy(this._selectableTarget, this) : undefined
|
|
});
|
|
}
|
|
},
|
|
_continuousItems: function (filter, cell) {
|
|
if (!this.lockedContent) {
|
|
return;
|
|
}
|
|
var lockedItems = $(filter, this.lockedTable);
|
|
var nonLockedItems = $(filter, this.table);
|
|
var columns = cell ? this._lockedColumns().length : 1;
|
|
var nonLockedColumns = cell ? this.columns.length - columns : 1;
|
|
var result = [];
|
|
for (var idx = 0; idx < lockedItems.length; idx += columns) {
|
|
push.apply(result, lockedItems.slice(idx, idx + columns));
|
|
push.apply(result, nonLockedItems.splice(0, nonLockedColumns));
|
|
}
|
|
return result;
|
|
},
|
|
_selectableTarget: function (items) {
|
|
var related;
|
|
var result = $();
|
|
for (var idx = 0, length = items.length; idx < length; idx++) {
|
|
related = this._relatedRow(items[idx]);
|
|
if (inArray(related[0], items) < 0) {
|
|
result = result.add(related);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_relatedRow: function (row) {
|
|
var lockedTable = this.lockedTable;
|
|
row = $(row);
|
|
if (!lockedTable) {
|
|
return row;
|
|
}
|
|
var table = row.closest(this.table.add(this.lockedTable));
|
|
var index = table.find('>tbody>tr').index(row);
|
|
table = table[0] === this.table[0] ? lockedTable : this.table;
|
|
return table.find('>tbody>tr').eq(index);
|
|
},
|
|
select: function (value) {
|
|
var selectable = this.selectable;
|
|
if (!selectable) {
|
|
return $();
|
|
}
|
|
if (typeof value !== 'undefined') {
|
|
if (!selectable.options.multiple) {
|
|
selectable.clear();
|
|
value = value.first();
|
|
}
|
|
if (this._hasLockedColumns) {
|
|
value = value.add($.map(value, proxy(this._relatedRow, this)));
|
|
}
|
|
}
|
|
return selectable.value(value);
|
|
},
|
|
clearSelection: function () {
|
|
var selected = this.select();
|
|
if (selected.length) {
|
|
this.selectable.clear();
|
|
this.trigger(CHANGE);
|
|
}
|
|
},
|
|
_dataSource: function (dataSource) {
|
|
var ds = this.dataSource;
|
|
if (ds) {
|
|
ds.unbind(CHANGE, this._refreshHandler);
|
|
ds.unbind(ERROR, this._errorHandler);
|
|
ds.unbind(PROGRESS, this._progressHandler);
|
|
}
|
|
this._refreshHandler = proxy(this.refresh, this);
|
|
this._errorHandler = proxy(this._error, this);
|
|
this._progressHandler = proxy(this._progress, this);
|
|
ds = this.dataSource = TreeListDataSource.create(dataSource);
|
|
ds.bind(CHANGE, this._refreshHandler);
|
|
ds.bind(ERROR, this._errorHandler);
|
|
ds.bind(PROGRESS, this._progressHandler);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this._dataSource(dataSource);
|
|
this._sortable();
|
|
this._filterable();
|
|
this._contentTree.render([]);
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
dataItem: function (element) {
|
|
if (element instanceof TreeListModel) {
|
|
return element;
|
|
}
|
|
var row = $(element).closest('tr');
|
|
var model = this.dataSource.getByUid(row.attr(kendo.attr('uid')));
|
|
return model;
|
|
},
|
|
editRow: function (row) {
|
|
var model;
|
|
if (typeof row === STRING) {
|
|
row = this.tbody.find(row);
|
|
}
|
|
model = this.dataItem(row);
|
|
if (!model) {
|
|
return;
|
|
}
|
|
if (this._editMode() != 'popup') {
|
|
model._edit = true;
|
|
}
|
|
this._cancelEditor();
|
|
this._render();
|
|
this._createEditor(model);
|
|
this.trigger(EDIT, {
|
|
container: this.editor.wrapper,
|
|
model: model
|
|
});
|
|
},
|
|
_cancelEdit: function (e) {
|
|
e = extend(e, {
|
|
container: this.editor.wrapper,
|
|
model: this.editor.model
|
|
});
|
|
if (this.trigger(CANCEL, e)) {
|
|
return;
|
|
}
|
|
this.cancelRow();
|
|
},
|
|
cancelRow: function () {
|
|
this._cancelEditor();
|
|
this._render();
|
|
},
|
|
saveRow: function () {
|
|
var editor = this.editor;
|
|
var args;
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
args = {
|
|
model: editor.model,
|
|
container: editor.wrapper
|
|
};
|
|
if (editor.end() && !this.trigger(SAVE, args)) {
|
|
this.dataSource.sync();
|
|
}
|
|
},
|
|
addRow: function (parent) {
|
|
var editor = this.editor;
|
|
var index = 0;
|
|
var model = {};
|
|
if (editor && !editor.end()) {
|
|
return;
|
|
}
|
|
if (parent) {
|
|
if (!(parent instanceof TreeListModel)) {
|
|
parent = this.dataItem(parent);
|
|
}
|
|
model[parent.parentIdField] = parent.id;
|
|
index = this.dataSource.indexOf(parent) + 1;
|
|
this.expand(parent).then(proxy(this._insertAt, this, model, index));
|
|
return;
|
|
}
|
|
this._insertAt(model, index);
|
|
},
|
|
_insertAt: function (model, index) {
|
|
model = this.dataSource.insert(index, model);
|
|
var row = this.itemFor(model);
|
|
this.editRow(row);
|
|
},
|
|
removeRow: function (row) {
|
|
var model = this.dataItem(row);
|
|
var args = {
|
|
model: model,
|
|
row: row
|
|
};
|
|
if (model && !this.trigger(REMOVE, args)) {
|
|
this.dataSource.remove(model);
|
|
this.dataSource.sync();
|
|
}
|
|
},
|
|
_cancelEditor: function () {
|
|
var model;
|
|
var editor = this.editor;
|
|
if (editor) {
|
|
model = editor.model;
|
|
this._destroyEditor();
|
|
this.dataSource.cancelChanges(model);
|
|
model._edit = false;
|
|
}
|
|
},
|
|
_destroyEditor: function () {
|
|
if (!this.editor) {
|
|
return;
|
|
}
|
|
this.editor.close();
|
|
this.editor = null;
|
|
},
|
|
_createEditor: function (model) {
|
|
var row = this.itemFor(model);
|
|
row = row.add(this._relatedRow(row));
|
|
var mode = this._editMode();
|
|
var options = {
|
|
columns: this.columns,
|
|
model: model,
|
|
target: this,
|
|
clearContainer: false,
|
|
template: this.options.editable.template
|
|
};
|
|
if (mode == 'inline') {
|
|
this.editor = new Editor(row, options);
|
|
} else {
|
|
extend(options, {
|
|
window: this.options.editable.window,
|
|
commandRenderer: proxy(function () {
|
|
return this._buildCommands([
|
|
'update',
|
|
'canceledit'
|
|
]);
|
|
}, this),
|
|
fieldRenderer: this._cellContent,
|
|
save: proxy(this.saveRow, this),
|
|
cancel: proxy(this._cancelEdit, this),
|
|
appendTo: this.wrapper
|
|
});
|
|
this.editor = new PopupEditor(row, options);
|
|
}
|
|
},
|
|
_editMode: function () {
|
|
var mode = 'inline', editable = this.options.editable;
|
|
if (editable !== true) {
|
|
if (typeof editable == 'string') {
|
|
mode = editable;
|
|
} else {
|
|
mode = editable.mode || mode;
|
|
}
|
|
}
|
|
return mode.toLowerCase();
|
|
},
|
|
hideColumn: function (column) {
|
|
this._toggleColumnVisibility(column, true);
|
|
},
|
|
showColumn: function (column) {
|
|
this._toggleColumnVisibility(column, false);
|
|
},
|
|
_toggleColumnVisibility: function (column, hidden) {
|
|
column = this._findColumn(column);
|
|
if (!column || column.hidden === hidden) {
|
|
return;
|
|
}
|
|
column.hidden = hidden;
|
|
this._ensureExpandableColumn();
|
|
this._renderCols();
|
|
this._renderHeader();
|
|
this._render();
|
|
this._adjustTablesWidth();
|
|
this.trigger(hidden ? COLUMNHIDE : COLUMNSHOW, { column: column });
|
|
if (!hidden && !column.width) {
|
|
this.table.add(this.thead.closest('table')).width('');
|
|
}
|
|
},
|
|
_findColumn: function (column) {
|
|
if (typeof column == 'number') {
|
|
column = this.columns[column];
|
|
} else if (isPlainObject(column)) {
|
|
column = grep(this.columns, function (item) {
|
|
return item === column;
|
|
})[0];
|
|
} else {
|
|
column = grep(this.columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
return column;
|
|
},
|
|
_adjustTablesWidth: function () {
|
|
var idx, length;
|
|
var cols = this.thead.prev().children();
|
|
var colWidth, width = 0;
|
|
for (idx = 0, length = cols.length; idx < length; idx++) {
|
|
colWidth = cols[idx].style.width;
|
|
if (colWidth && colWidth.indexOf('%') == -1) {
|
|
width += parseInt(colWidth, 10);
|
|
} else {
|
|
width = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (width) {
|
|
this.table.add(this.thead.closest('table')).width(width);
|
|
}
|
|
},
|
|
_reorderable: function () {
|
|
if (!this.options.reorderable) {
|
|
return;
|
|
}
|
|
var scrollable = this.options.scrollable === true;
|
|
var selector = (scrollable ? '.k-grid-header:first ' : 'table:first>.k-grid-header ') + HEADERCELLS;
|
|
var that = this;
|
|
this._draggableInstance = new ui.Draggable(this.wrapper, {
|
|
group: kendo.guid(),
|
|
filter: selector,
|
|
hint: function (target) {
|
|
return $('<div class="k-header k-drag-clue" />').css({
|
|
width: target.width(),
|
|
paddingLeft: target.css('paddingLeft'),
|
|
paddingRight: target.css('paddingRight'),
|
|
lineHeight: target.height() + 'px',
|
|
paddingTop: target.css('paddingTop'),
|
|
paddingBottom: target.css('paddingBottom')
|
|
}).html(target.attr(kendo.attr('title')) || target.attr(kendo.attr('field')) || target.text()).prepend('<span class="k-icon k-drag-status k-denied" />');
|
|
}
|
|
});
|
|
this.reorderable = new ui.Reorderable(this.wrapper, {
|
|
draggable: this._draggableInstance,
|
|
dragOverContainers: proxy(this._allowDragOverContainers, this),
|
|
inSameContainer: function (e) {
|
|
return $(e.source).parent()[0] === $(e.target).parent()[0];
|
|
},
|
|
change: function (e) {
|
|
var newIndex = e.newIndex;
|
|
var oldIndex = e.oldIndex;
|
|
var before = e.position === 'before';
|
|
var column = that.columns[oldIndex];
|
|
that.trigger(COLUMNREORDER, {
|
|
newIndex: newIndex,
|
|
oldIndex: oldIndex,
|
|
column: column
|
|
});
|
|
that.reorderColumn(newIndex, column, before);
|
|
}
|
|
});
|
|
},
|
|
_allowDragOverContainers: function (index) {
|
|
return this.columns[index].lockable !== false;
|
|
},
|
|
reorderColumn: function (destIndex, column, before) {
|
|
var lockChanged;
|
|
var columns = this.columns;
|
|
var sourceIndex = inArray(column, columns);
|
|
var destColumn = columns[destIndex];
|
|
var isLocked = !!destColumn.locked;
|
|
var nonLockedColumnsLength = this._nonLockedColumns().length;
|
|
if (sourceIndex === destIndex) {
|
|
return;
|
|
}
|
|
if (isLocked && !column.locked && nonLockedColumnsLength == 1) {
|
|
return;
|
|
}
|
|
if (!isLocked && column.locked && columns.length - nonLockedColumnsLength == 1) {
|
|
return;
|
|
}
|
|
if (before === undefined) {
|
|
before = destIndex < sourceIndex;
|
|
}
|
|
lockChanged = !!column.locked;
|
|
lockChanged = lockChanged != isLocked;
|
|
column.locked = isLocked;
|
|
columns.splice(before ? destIndex : destIndex + 1, 0, column);
|
|
columns.splice(sourceIndex < destIndex ? sourceIndex : sourceIndex + 1, 1);
|
|
this._renderCols();
|
|
var ths = $(this.lockedHeader).add(this.thead).find('th');
|
|
ths.eq(sourceIndex)[before ? 'insertBefore' : 'insertAfter'](ths.eq(destIndex));
|
|
var dom = this._headerTree.children[0].children;
|
|
if (this._hasLockedColumns) {
|
|
dom = this._lockedHeaderTree.children[0].children.concat(dom);
|
|
}
|
|
dom.splice(before ? destIndex : destIndex + 1, 0, dom[sourceIndex]);
|
|
dom.splice(sourceIndex < destIndex ? sourceIndex : sourceIndex + 1, 1);
|
|
if (this._hasLockedColumns) {
|
|
this._lockedHeaderTree.children[0].children = dom.splice(0, this._lockedColumns().length);
|
|
this._headerTree.children[0].children = dom;
|
|
}
|
|
this._applyLockedContainersWidth();
|
|
this.refresh();
|
|
if (!lockChanged) {
|
|
return;
|
|
}
|
|
if (isLocked) {
|
|
this.trigger(COLUMNLOCK, { column: column });
|
|
} else {
|
|
this.trigger(COLUMNUNLOCK, { column: column });
|
|
}
|
|
},
|
|
lockColumn: function (column) {
|
|
var columns = this.columns;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else {
|
|
column = grep(columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || column.hidden) {
|
|
return;
|
|
}
|
|
var index = this._lockedColumns().length - 1;
|
|
this.reorderColumn(index, column, false);
|
|
},
|
|
unlockColumn: function (column) {
|
|
var columns = this.columns;
|
|
if (typeof column == 'number') {
|
|
column = columns[column];
|
|
} else {
|
|
column = grep(columns, function (item) {
|
|
return item.field === column;
|
|
})[0];
|
|
}
|
|
if (!column || column.hidden) {
|
|
return;
|
|
}
|
|
var index = this._lockedColumns().length;
|
|
this.reorderColumn(index, column, true);
|
|
},
|
|
_columnMenu: function () {
|
|
var ths = $(this.lockedHeader).add(this.thead).find('th');
|
|
var columns = this.columns;
|
|
var options = this.options;
|
|
var columnMenu = options.columnMenu;
|
|
var column, menu, menuOptions, sortable, filterable;
|
|
var initHandler = proxy(this._columnMenuInit, this);
|
|
var lockedColumnsLength = this._lockedColumns().length;
|
|
if (!columnMenu) {
|
|
return;
|
|
}
|
|
if (typeof columnMenu == 'boolean') {
|
|
columnMenu = {};
|
|
}
|
|
for (var i = 0; i < ths.length; i++) {
|
|
column = columns[i];
|
|
if (!column.field) {
|
|
continue;
|
|
}
|
|
menu = ths.eq(i).data('kendoColumnMenu');
|
|
if (menu) {
|
|
menu.destroy();
|
|
}
|
|
sortable = false;
|
|
if (column.sortable !== false && columnMenu.sortable !== false && options.sortable !== false) {
|
|
sortable = extend({}, options.sortable, { compare: (column.sortable || {}).compare });
|
|
}
|
|
filterable = false;
|
|
if (options.filterable && column.filterable !== false && columnMenu.filterable !== false) {
|
|
filterable = extend({ pane: this.pane }, column.filterable, options.filterable);
|
|
}
|
|
menuOptions = {
|
|
dataSource: this.dataSource,
|
|
values: column.values,
|
|
columns: columnMenu.columns,
|
|
sortable: sortable,
|
|
filterable: filterable,
|
|
messages: columnMenu.messages,
|
|
owner: this,
|
|
closeCallback: $.noop,
|
|
init: initHandler,
|
|
pane: this.pane,
|
|
lockedColumns: column.lockable !== false && lockedColumnsLength > 0
|
|
};
|
|
if (options.$angular) {
|
|
menuOptions.$angular = options.$angular;
|
|
}
|
|
ths.eq(i).kendoColumnMenu(menuOptions);
|
|
}
|
|
},
|
|
_columnMenuInit: function (e) {
|
|
this.trigger(COLUMNMENUINIT, {
|
|
field: e.field,
|
|
container: e.container
|
|
});
|
|
}
|
|
});
|
|
if (kendo.ExcelMixin) {
|
|
kendo.ExcelMixin.extend(TreeList.prototype);
|
|
}
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(TreeList.prototype);
|
|
TreeList.fn._drawPDF = function (progress) {
|
|
var promise = new $.Deferred();
|
|
this._drawPDFShadow({ width: this.wrapper.width() }, { avoidLinks: this.options.pdf.avoidLinks }).done(function (group) {
|
|
var args = {
|
|
page: group,
|
|
pageNumber: 1,
|
|
progress: 1,
|
|
totalPages: 1
|
|
};
|
|
progress.notify(args);
|
|
promise.resolve(args.page);
|
|
}).fail(function (err) {
|
|
promise.reject(err);
|
|
});
|
|
return promise;
|
|
};
|
|
}
|
|
extend(true, kendo.data, {
|
|
TreeListDataSource: TreeListDataSource,
|
|
TreeListModel: TreeListModel
|
|
});
|
|
extend(true, kendo.ui, { TreeList: TreeList });
|
|
ui.plugin(TreeList);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/parse-xml', [
|
|
'kendo.core',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
var STRING = String.fromCharCode;
|
|
var ENTITIES = {
|
|
'amp': 38,
|
|
'lt': 60,
|
|
'gt': 62,
|
|
'quot': 34,
|
|
'nbsp': 160
|
|
};
|
|
function CODE(str) {
|
|
var out = [];
|
|
for (var i = 0; i < str.length; ++i) {
|
|
out.push(str.charCodeAt(i));
|
|
}
|
|
return out;
|
|
}
|
|
function UCS2(out, code) {
|
|
if (code > 65535) {
|
|
code -= 65536;
|
|
out.push(code >>> 10 & 1023 | 55296, 56320 | code & 1023);
|
|
} else {
|
|
out.push(code);
|
|
}
|
|
}
|
|
var START_CDATA = CODE('<![CDATA[');
|
|
var END_CDATA = CODE(']]>');
|
|
var END_COMMENT = CODE('-->');
|
|
var START_COMMENT = CODE('!--');
|
|
var END_SHORT_TAG = CODE('/>');
|
|
var END_TAG = CODE('</');
|
|
var END_DECLARATION = CODE('?>');
|
|
var QUESTION_MARK = CODE('?');
|
|
var LESS_THAN = CODE('<');
|
|
var GREATER_THAN = CODE('>');
|
|
var SEMICOLON = CODE(';');
|
|
var EQUAL = CODE('=');
|
|
var AMPERSAND = CODE('&');
|
|
var QUOTE = CODE('"');
|
|
var APOSTROPHE = CODE('\'');
|
|
var SHARP = CODE('#');
|
|
var LOWERCASE_X = CODE('x');
|
|
var UPPERCASE_X = CODE('X');
|
|
var EXIT = {};
|
|
function parse(data, callbacks) {
|
|
var index = 0;
|
|
var stack = [];
|
|
var object = {
|
|
is: function (selector) {
|
|
var i = stack.length, j = selector.length;
|
|
while (--i >= 0 && --j >= 0) {
|
|
if (stack[i].$tag != selector[j] && selector[j] != '*') {
|
|
return false;
|
|
}
|
|
}
|
|
return j < 0 ? stack[stack.length - 1] : null;
|
|
},
|
|
exit: function () {
|
|
throw EXIT;
|
|
},
|
|
stack: stack
|
|
};
|
|
function readChar(body) {
|
|
var code = data[index++];
|
|
if (!(code & 240 ^ 240)) {
|
|
UCS2(body, (code & 3) << 18 | (data[index++] & 63) << 12 | (data[index++] & 63) << 6 | data[index++] & 63);
|
|
} else if (!(code & 224 ^ 224)) {
|
|
UCS2(body, (code & 15) << 12 | (data[index++] & 63) << 6 | data[index++] & 63);
|
|
} else if (!(code & 192 ^ 192)) {
|
|
UCS2(body, (code & 31) << 6 | data[index++] & 63);
|
|
} else {
|
|
body.push(code);
|
|
}
|
|
}
|
|
function croak(msg) {
|
|
throw new Error(msg + ', at ' + index);
|
|
}
|
|
function readWhile(pred) {
|
|
var a = [];
|
|
while (index < data.length && pred(data[index])) {
|
|
a.push(data[index++]);
|
|
}
|
|
return a;
|
|
}
|
|
function readAsciiWhile(pred) {
|
|
return STRING.apply(0, readWhile(pred));
|
|
}
|
|
function skipWhitespace() {
|
|
readWhile(isWhitespace);
|
|
}
|
|
function eat(a) {
|
|
var save = index;
|
|
for (var i = 0; i < a.length; ++i) {
|
|
if (data[index++] != a[i]) {
|
|
index = save;
|
|
return false;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
function skip(code) {
|
|
if (!eat(code)) {
|
|
croak('Expecting ' + code.join(', '));
|
|
}
|
|
}
|
|
function isWhitespace(code) {
|
|
return code == 9 || code == 10 || code == 13 || code == 32;
|
|
}
|
|
function isDigit(code) {
|
|
return code >= 48 && code <= 57;
|
|
}
|
|
function isHexDigit(code) {
|
|
return code >= 48 && code <= 57 || (code |= 32) >= 97 && code <= 102;
|
|
}
|
|
function isNameStart(code) {
|
|
return code == 58 || code == 95 || (code |= 32) >= 97 && code <= 122;
|
|
}
|
|
function isName(code) {
|
|
return code == 45 || isDigit(code) || isNameStart(code);
|
|
}
|
|
function xmlComment() {
|
|
var body = [];
|
|
while (index < data.length) {
|
|
if (eat(END_COMMENT)) {
|
|
return call('comment', STRING.apply(0, body));
|
|
}
|
|
readChar(body);
|
|
}
|
|
}
|
|
function xmlTag() {
|
|
var name, attrs;
|
|
if (eat(QUESTION_MARK)) {
|
|
xmlDecl();
|
|
} else if (eat(START_COMMENT)) {
|
|
xmlComment();
|
|
} else {
|
|
name = xmlName();
|
|
attrs = xmlAttrs(name);
|
|
stack.push(attrs);
|
|
if (eat(END_SHORT_TAG)) {
|
|
call('enter', name, attrs, true);
|
|
} else {
|
|
skip(GREATER_THAN);
|
|
call('enter', name, attrs);
|
|
xmlContent(name);
|
|
if (name != xmlName()) {
|
|
croak('Bad closing tag');
|
|
}
|
|
call('leave', name, attrs);
|
|
skipWhitespace();
|
|
skip(GREATER_THAN);
|
|
}
|
|
stack.pop();
|
|
}
|
|
}
|
|
function xmlContent(name) {
|
|
var body = [];
|
|
while (index < data.length) {
|
|
if (eat(END_TAG)) {
|
|
return body.length && call('text', STRING.apply(0, body));
|
|
} else if (eat(START_CDATA)) {
|
|
while (index < data.length && !eat(END_CDATA)) {
|
|
readChar(body);
|
|
}
|
|
} else if (eat(LESS_THAN)) {
|
|
if (body.length) {
|
|
call('text', STRING.apply(0, body));
|
|
}
|
|
xmlTag();
|
|
body = [];
|
|
} else if (eat(AMPERSAND)) {
|
|
xmlEntity(body);
|
|
} else {
|
|
readChar(body);
|
|
}
|
|
}
|
|
croak('Unclosed tag ' + name);
|
|
}
|
|
function xmlName() {
|
|
if (!isNameStart(data[index])) {
|
|
croak('Expecting XML name');
|
|
}
|
|
return readAsciiWhile(isName);
|
|
}
|
|
function xmlString() {
|
|
var quote = eat(QUOTE) || eat(APOSTROPHE);
|
|
if (!quote) {
|
|
croak('Expecting string');
|
|
}
|
|
var body = [];
|
|
while (index < data.length) {
|
|
if (eat(quote)) {
|
|
return STRING.apply(0, body);
|
|
} else if (eat(AMPERSAND)) {
|
|
xmlEntity(body);
|
|
} else {
|
|
readChar(body);
|
|
}
|
|
}
|
|
croak('Unfinished string');
|
|
}
|
|
function xmlEntity(body) {
|
|
var code;
|
|
if (eat(SHARP)) {
|
|
if (eat(LOWERCASE_X) || eat(UPPERCASE_X)) {
|
|
code = parseInt(readAsciiWhile(isHexDigit), 16);
|
|
} else {
|
|
code = parseInt(readAsciiWhile(isDigit), 10);
|
|
}
|
|
if (isNaN(code)) {
|
|
croak('Bad numeric entity');
|
|
}
|
|
} else {
|
|
var name = xmlName();
|
|
code = ENTITIES[name];
|
|
if (code === undefined) {
|
|
croak('Unknown entity ' + name);
|
|
}
|
|
}
|
|
UCS2(body, code);
|
|
skip(SEMICOLON);
|
|
}
|
|
function xmlDecl() {
|
|
call('decl', xmlName(), xmlAttrs());
|
|
skip(END_DECLARATION);
|
|
}
|
|
function xmlAttrs(name) {
|
|
var map = { $tag: name };
|
|
while (index < data.length) {
|
|
skipWhitespace();
|
|
var code = data[index];
|
|
if (code == 63 || code == 62 || code == 47) {
|
|
break;
|
|
}
|
|
map[xmlName()] = (skip(EQUAL), xmlString());
|
|
}
|
|
return map;
|
|
}
|
|
function call(what, thing, arg1, arg2) {
|
|
var f = callbacks && callbacks[what];
|
|
if (f) {
|
|
f.call(object, thing, arg1, arg2);
|
|
}
|
|
}
|
|
var tmp = [];
|
|
readChar(tmp);
|
|
if (tmp[0] != 65279) {
|
|
index = 0;
|
|
}
|
|
while (index < data.length) {
|
|
skipWhitespace();
|
|
skip(LESS_THAN);
|
|
xmlTag();
|
|
skipWhitespace();
|
|
}
|
|
}
|
|
kendo.util.parseXML = function parseXML() {
|
|
try {
|
|
return parse.apply(this, arguments);
|
|
} catch (ex) {
|
|
if (ex !== EXIT) {
|
|
throw ex;
|
|
}
|
|
}
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/commands', [
|
|
'kendo.core',
|
|
'kendo.binder',
|
|
'kendo.window',
|
|
'kendo.list',
|
|
'kendo.tabstrip'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Command = kendo.spreadsheet.Command = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.options = options;
|
|
this._workbook = options.workbook;
|
|
this._property = options && options.property;
|
|
this._state = {};
|
|
},
|
|
range: function (range) {
|
|
if (range !== undefined) {
|
|
this._range = range;
|
|
}
|
|
return this._range;
|
|
},
|
|
redo: function () {
|
|
this.exec();
|
|
},
|
|
undo: function () {
|
|
this.range().setState(this._state);
|
|
},
|
|
getState: function () {
|
|
this._state = this.range().getState(this._property);
|
|
},
|
|
_forEachCell: function (callback) {
|
|
var range = this.range();
|
|
var ref = range._ref;
|
|
ref.forEach(function (ref) {
|
|
range.sheet().forEach(ref.toRangeRef(), callback.bind(this));
|
|
}.bind(this));
|
|
}
|
|
});
|
|
var PropertyChangeCommand = kendo.spreadsheet.PropertyChangeCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._value = options.value;
|
|
},
|
|
exec: function () {
|
|
var range = this.range();
|
|
this.getState();
|
|
range[this._property](this._value);
|
|
}
|
|
});
|
|
kendo.spreadsheet.ClearContentCommand = Command.extend({
|
|
exec: function () {
|
|
this.getState();
|
|
this.range().clearContent();
|
|
}
|
|
});
|
|
kendo.spreadsheet.EditCommand = PropertyChangeCommand.extend({
|
|
init: function (options) {
|
|
options.property = 'input';
|
|
PropertyChangeCommand.fn.init.call(this, options);
|
|
},
|
|
rejectState: function (validationState) {
|
|
this.undo();
|
|
return {
|
|
title: validationState.title,
|
|
body: validationState.message,
|
|
reason: 'error',
|
|
type: 'validationError'
|
|
};
|
|
},
|
|
exec: function () {
|
|
var range = this.range();
|
|
var value = this._value;
|
|
this.getState();
|
|
try {
|
|
range.input(value);
|
|
var validationState = range._getValidationState();
|
|
if (validationState) {
|
|
return this.rejectState(validationState);
|
|
}
|
|
} catch (ex1) {
|
|
if (ex1 instanceof kendo.spreadsheet.calc.ParseError) {
|
|
try {
|
|
range.input(value + ')');
|
|
var validationState = range._getValidationState();
|
|
if (validationState) {
|
|
return this.rejectState(validationState);
|
|
}
|
|
} catch (ex2) {
|
|
if (ex2 instanceof kendo.spreadsheet.calc.ParseError) {
|
|
range.input('\'' + value);
|
|
return {
|
|
title: 'Error in formula',
|
|
body: ex1 + '',
|
|
reason: 'error'
|
|
};
|
|
}
|
|
}
|
|
} else {
|
|
throw ex1;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.TextWrapCommand = PropertyChangeCommand.extend({
|
|
init: function (options) {
|
|
options.property = 'wrap';
|
|
PropertyChangeCommand.fn.init.call(this, options);
|
|
this._value = options.value;
|
|
},
|
|
getState: function () {
|
|
var rowHeight = {};
|
|
this.range().forEachRow(function (range) {
|
|
var index = range.topLeft().row;
|
|
rowHeight[index] = range.sheet().rowHeight(index);
|
|
});
|
|
this._state = this.range().getState(this._property);
|
|
this._rowHeight = rowHeight;
|
|
},
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
var rowHeight = this._rowHeight;
|
|
this.range().setState(this._state);
|
|
for (var row in rowHeight) {
|
|
sheet.rowHeight(row, rowHeight[row]);
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.AdjustDecimalsCommand = Command.extend({
|
|
init: function (options) {
|
|
this._decimals = options.value;
|
|
options.property = 'format';
|
|
Command.fn.init.call(this, options);
|
|
},
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
var decimals = this._decimals;
|
|
var formatting = kendo.spreadsheet.formatting;
|
|
this.getState();
|
|
sheet.batch(function () {
|
|
this.range().forEachCell(function (row, col, cell) {
|
|
var format = cell.format;
|
|
if (format || decimals > 0) {
|
|
format = formatting.adjustDecimals(format || '#', decimals);
|
|
sheet.range(row, col).format(format);
|
|
}
|
|
});
|
|
}.bind(this));
|
|
}
|
|
});
|
|
kendo.spreadsheet.BorderChangeCommand = Command.extend({
|
|
init: function (options) {
|
|
options.property = 'border';
|
|
Command.fn.init.call(this, options);
|
|
this._type = options.border;
|
|
this._style = options.style;
|
|
},
|
|
exec: function () {
|
|
this.getState();
|
|
this[this._type](this._style);
|
|
},
|
|
noBorders: function () {
|
|
var range = this.range();
|
|
range.sheet().batch(function () {
|
|
range.borderLeft(null).borderTop(null).borderRight(null).borderBottom(null);
|
|
}.bind(this), {});
|
|
},
|
|
allBorders: function (style) {
|
|
var range = this.range();
|
|
range.sheet().batch(function () {
|
|
range.borderLeft(style).borderTop(style).borderRight(style).borderBottom(style);
|
|
}.bind(this), {});
|
|
},
|
|
leftBorder: function (style) {
|
|
this.range().leftColumn().borderLeft(style);
|
|
},
|
|
rightBorder: function (style) {
|
|
this.range().rightColumn().borderRight(style);
|
|
},
|
|
topBorder: function (style) {
|
|
this.range().topRow().borderTop(style);
|
|
},
|
|
bottomBorder: function (style) {
|
|
this.range().bottomRow().borderBottom(style);
|
|
},
|
|
outsideBorders: function (style) {
|
|
var range = this.range();
|
|
range.sheet().batch(function () {
|
|
range.leftColumn().borderLeft(style);
|
|
range.topRow().borderTop(style);
|
|
range.rightColumn().borderRight(style);
|
|
range.bottomRow().borderBottom(style);
|
|
}.bind(this), {});
|
|
},
|
|
insideBorders: function (style) {
|
|
this.range().sheet().batch(function () {
|
|
this.allBorders(style);
|
|
this.outsideBorders(null);
|
|
}.bind(this), {});
|
|
},
|
|
insideHorizontalBorders: function (style) {
|
|
var range = this.range();
|
|
range.sheet().batch(function () {
|
|
range.borderBottom(style);
|
|
range.bottomRow().borderBottom(null);
|
|
}.bind(this), {});
|
|
},
|
|
insideVerticalBorders: function (style) {
|
|
var range = this.range();
|
|
range.sheet().batch(function () {
|
|
range.borderRight(style);
|
|
range.rightColumn().borderRight(null);
|
|
}.bind(this), {});
|
|
}
|
|
});
|
|
kendo.spreadsheet.MergeCellCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._type = options.value;
|
|
},
|
|
exec: function () {
|
|
this.getState();
|
|
this[this._type]();
|
|
},
|
|
activate: function (ref) {
|
|
this.range().sheet().activeCell(ref);
|
|
},
|
|
getState: function () {
|
|
this._state = this.range().getState();
|
|
},
|
|
undo: function () {
|
|
if (this._type !== 'unmerge') {
|
|
this.range().unmerge();
|
|
this.activate(this.range().topLeft());
|
|
}
|
|
this.range().setState(this._state);
|
|
},
|
|
cells: function () {
|
|
var range = this.range();
|
|
var ref = range._ref;
|
|
range.merge();
|
|
this.activate(ref);
|
|
},
|
|
horizontally: function () {
|
|
var ref = this.range().topRow()._ref;
|
|
this.range().forEachRow(function (range) {
|
|
range.merge();
|
|
});
|
|
this.activate(ref);
|
|
},
|
|
vertically: function () {
|
|
var ref = this.range().leftColumn()._ref;
|
|
this.range().forEachColumn(function (range) {
|
|
range.merge();
|
|
});
|
|
this.activate(ref);
|
|
},
|
|
unmerge: function () {
|
|
var range = this.range();
|
|
var ref = range._ref.topLeft;
|
|
range.unmerge();
|
|
this.activate(ref);
|
|
}
|
|
});
|
|
kendo.spreadsheet.FreezePanesCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._type = options.value;
|
|
},
|
|
exec: function () {
|
|
this.getState();
|
|
this._topLeft = this.range().topLeft();
|
|
this[this._type]();
|
|
},
|
|
getState: function () {
|
|
this._state = this.range().sheet().getState();
|
|
},
|
|
undo: function () {
|
|
this.range().sheet().setState(this._state);
|
|
},
|
|
panes: function () {
|
|
var topLeft = this._topLeft;
|
|
var sheet = this.range().sheet();
|
|
sheet.frozenColumns(topLeft.col).frozenRows(topLeft.row);
|
|
},
|
|
rows: function () {
|
|
var topLeft = this._topLeft;
|
|
var sheet = this.range().sheet();
|
|
sheet.frozenRows(topLeft.row);
|
|
},
|
|
columns: function () {
|
|
var topLeft = this._topLeft;
|
|
var sheet = this.range().sheet();
|
|
sheet.frozenColumns(topLeft.col);
|
|
},
|
|
unfreeze: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.frozenRows(0).frozenColumns(0);
|
|
}
|
|
});
|
|
kendo.spreadsheet.PasteCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._clipboard = this._workbook.clipboard();
|
|
},
|
|
getState: function () {
|
|
this._range = this._workbook.activeSheet().range(this._clipboard.pasteRef());
|
|
this._state = this._range.getState();
|
|
},
|
|
exec: function () {
|
|
var status = this._clipboard.canPaste();
|
|
this._clipboard.menuInvoked = true;
|
|
if (!status.canPaste) {
|
|
if (status.menuInvoked) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'useKeyboard'
|
|
};
|
|
}
|
|
if (status.pasteOnMerged) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'modifyMerged'
|
|
};
|
|
}
|
|
if (status.overflow) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'overflow'
|
|
};
|
|
}
|
|
return { reason: 'error' };
|
|
}
|
|
this.getState();
|
|
this._clipboard.paste();
|
|
var range = this._workbook.activeSheet().range(this._clipboard.pasteRef());
|
|
range._adjustRowHeight();
|
|
}
|
|
});
|
|
kendo.spreadsheet.AdjustRowHeightCommand = Command.extend({
|
|
exec: function () {
|
|
var options = this.options;
|
|
var sheet = this._workbook.activeSheet();
|
|
var range = options.range || sheet.range(options.rowIndex);
|
|
range._adjustRowHeight();
|
|
}
|
|
});
|
|
kendo.spreadsheet.ToolbarPasteCommand = Command.extend({
|
|
exec: function () {
|
|
if (kendo.support.clipboard.paste) {
|
|
this._workbook._view.clipboard.focus().select();
|
|
document.execCommand('paste');
|
|
} else {
|
|
return {
|
|
reason: 'error',
|
|
type: 'useKeyboard'
|
|
};
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.CopyCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._clipboard = options.workbook.clipboard();
|
|
},
|
|
undo: $.noop,
|
|
exec: function () {
|
|
var status = this._clipboard.canCopy();
|
|
this._clipboard.menuInvoked = true;
|
|
if (!status.canCopy) {
|
|
if (status.menuInvoked) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'useKeyboard'
|
|
};
|
|
} else if (status.multiSelection) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'unsupportedSelection'
|
|
};
|
|
}
|
|
return;
|
|
}
|
|
this._clipboard.copy();
|
|
}
|
|
});
|
|
kendo.spreadsheet.ToolbarCopyCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._clipboard = options.workbook.clipboard();
|
|
},
|
|
undo: $.noop,
|
|
exec: function () {
|
|
if (kendo.support.clipboard.copy) {
|
|
var clipboard = this._workbook._view.clipboard;
|
|
var textarea = document.createElement('textarea');
|
|
$(textarea).addClass('k-spreadsheet-clipboard').val(clipboard.html()).appendTo(document.body).focus().select();
|
|
document.execCommand('copy');
|
|
clipboard.trigger('copy');
|
|
$(textarea).remove();
|
|
} else {
|
|
return {
|
|
reason: 'error',
|
|
type: 'useKeyboard'
|
|
};
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.CutCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._clipboard = options.workbook.clipboard();
|
|
},
|
|
exec: function () {
|
|
if (this._clipboard.canCopy()) {
|
|
this.getState();
|
|
this._clipboard.cut();
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.AutoFillCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
},
|
|
origin: function (origin) {
|
|
this._origin = origin;
|
|
},
|
|
exec: function () {
|
|
this.getState();
|
|
this.range().fillFrom(this._origin);
|
|
}
|
|
});
|
|
kendo.spreadsheet.ToolbarCutCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._clipboard = options.workbook.clipboard();
|
|
},
|
|
exec: function () {
|
|
if (kendo.support.clipboard.copy) {
|
|
var clipboard = this._workbook._view.clipboard;
|
|
var textarea = document.createElement('textarea');
|
|
$(textarea).val(clipboard.html()).appendTo(document.body).focus().select();
|
|
document.execCommand('copy');
|
|
clipboard.trigger('cut');
|
|
$(textarea).remove();
|
|
} else {
|
|
return {
|
|
reason: 'error',
|
|
type: 'useKeyboard'
|
|
};
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.FilterCommand = Command.extend({
|
|
undo: function () {
|
|
this.range().filter(this._state);
|
|
},
|
|
exec: function () {
|
|
var range = this.range();
|
|
this._state = range.hasFilter();
|
|
if (range.hasFilter()) {
|
|
range.filter(false);
|
|
} else if (!range.intersectingMerged().length) {
|
|
range.filter(true);
|
|
} else {
|
|
return {
|
|
reason: 'error',
|
|
type: 'filterRangeContainingMerges'
|
|
};
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.SortCommand = Command.extend({
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.setState(this._state);
|
|
},
|
|
exec: function () {
|
|
var range = this.range();
|
|
var sheet = range.sheet();
|
|
var activeCell = sheet.activeCell();
|
|
var col = this.options.sheet ? activeCell.topLeft.col : this.options.column || 0;
|
|
var ascending = this.options.value === 'asc' ? true : false;
|
|
this._state = sheet.getState();
|
|
if (this.options.sheet) {
|
|
range = this.expandRange();
|
|
}
|
|
if (!range.intersectingMerged().length) {
|
|
range.sort({
|
|
column: col,
|
|
ascending: ascending
|
|
});
|
|
} else {
|
|
return {
|
|
reason: 'error',
|
|
type: 'sortRangeContainingMerges'
|
|
};
|
|
}
|
|
},
|
|
expandRange: function () {
|
|
var sheet = this.range().sheet();
|
|
return new kendo.spreadsheet.Range(sheet._sheetRef, sheet);
|
|
}
|
|
});
|
|
var ApplyFilterCommand = kendo.spreadsheet.ApplyFilterCommand = Command.extend({
|
|
column: function () {
|
|
return this.options.column || 0;
|
|
},
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.clearFilter(this.column());
|
|
if (this._state.length) {
|
|
this.range().filter(this._state);
|
|
}
|
|
},
|
|
getState: function () {
|
|
var sheet = this.range().sheet();
|
|
var current = sheet.filter();
|
|
if (current) {
|
|
this._state = current.columns.filter(function (c) {
|
|
return c.index == this.column();
|
|
}.bind(this));
|
|
}
|
|
},
|
|
exec: function () {
|
|
var range = this.range();
|
|
var column = this.column();
|
|
var current = range.sheet().filter();
|
|
var options;
|
|
var filterRule;
|
|
var exists = false;
|
|
if (this.options.valueFilter) {
|
|
filterRule = {
|
|
column: column,
|
|
filter: new kendo.spreadsheet.ValueFilter(this.options.valueFilter)
|
|
};
|
|
} else if (this.options.customFilter) {
|
|
filterRule = {
|
|
column: column,
|
|
filter: new kendo.spreadsheet.CustomFilter(this.options.customFilter)
|
|
};
|
|
}
|
|
this.getState();
|
|
if (current && current.ref.eq(range._ref) && current.columns.length) {
|
|
current.columns.forEach(function (element) {
|
|
if (element.index === column) {
|
|
exists = true;
|
|
}
|
|
});
|
|
options = current.columns.map(function (element) {
|
|
return element.index === column ? filterRule : {
|
|
column: element.index,
|
|
filter: element.filter
|
|
};
|
|
});
|
|
if (!exists) {
|
|
options.push(filterRule);
|
|
}
|
|
} else {
|
|
options = filterRule;
|
|
}
|
|
range.filter(options);
|
|
}
|
|
});
|
|
kendo.spreadsheet.ClearFilterCommand = ApplyFilterCommand.extend({
|
|
exec: function () {
|
|
var range = this.range();
|
|
var column = this.column();
|
|
this.getState();
|
|
range.clearFilter(column);
|
|
}
|
|
});
|
|
kendo.spreadsheet.HideLineCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this.axis = options.axis;
|
|
},
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.setAxisState(this._state);
|
|
},
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
this._state = sheet.getAxisState();
|
|
if (this.axis == 'row') {
|
|
sheet.axisManager().hideSelectedRows();
|
|
} else {
|
|
sheet.axisManager().hideSelectedColumns();
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.UnHideLineCommand = kendo.spreadsheet.HideLineCommand.extend({
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
this._state = sheet.getAxisState();
|
|
if (this.axis == 'row') {
|
|
sheet.axisManager().unhideSelectedRows();
|
|
} else {
|
|
sheet.axisManager().unhideSelectedColumns();
|
|
}
|
|
}
|
|
});
|
|
var DeleteCommand = kendo.spreadsheet.DeleteCommand = Command.extend({
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.setState(this._state);
|
|
}
|
|
});
|
|
kendo.spreadsheet.DeleteRowCommand = DeleteCommand.extend({
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
this._state = sheet.getState();
|
|
sheet.axisManager().deleteSelectedRows();
|
|
}
|
|
});
|
|
kendo.spreadsheet.DeleteColumnCommand = DeleteCommand.extend({
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
this._state = sheet.getState();
|
|
sheet.axisManager().deleteSelectedColumns();
|
|
}
|
|
});
|
|
var AddCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._value = options.value;
|
|
},
|
|
undo: function () {
|
|
var sheet = this.range().sheet();
|
|
sheet.setState(this._state);
|
|
}
|
|
});
|
|
kendo.spreadsheet.AddColumnCommand = AddCommand.extend({
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
this._state = sheet.getState();
|
|
if (this._value === 'left') {
|
|
sheet.axisManager().addColumnLeft();
|
|
} else {
|
|
sheet.axisManager().addColumnRight();
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.AddRowCommand = AddCommand.extend({
|
|
exec: function () {
|
|
var sheet = this.range().sheet();
|
|
if (!sheet.axisManager().canAddRow()) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'shiftingNonblankCells'
|
|
};
|
|
}
|
|
this._state = sheet.getState();
|
|
if (this._value === 'above') {
|
|
sheet.axisManager().addRowAbove();
|
|
} else {
|
|
sheet.axisManager().addRowBelow();
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.EditValidationCommand = Command.extend({
|
|
init: function (options) {
|
|
Command.fn.init.call(this, options);
|
|
this._value = options.value;
|
|
},
|
|
exec: function () {
|
|
this.range().validation(this._value);
|
|
}
|
|
});
|
|
kendo.spreadsheet.OpenCommand = Command.extend({
|
|
exec: function () {
|
|
var file = this.options.file;
|
|
if (file.name.match(/.xlsx$/i) === null) {
|
|
return {
|
|
reason: 'error',
|
|
type: 'openUnsupported'
|
|
};
|
|
}
|
|
this.options.workbook.fromFile(this.options.file);
|
|
}
|
|
});
|
|
kendo.spreadsheet.SaveAsCommand = Command.extend({
|
|
exec: function () {
|
|
var fileName = this.options.name + this.options.extension;
|
|
if (this.options.extension === '.xlsx') {
|
|
this.options.workbook.saveAsExcel({ fileName: fileName });
|
|
} else if (this.options.extension === '.pdf') {
|
|
this.options.workbook.saveAsPDF($.extend(this.options.pdf, {
|
|
workbook: this.options.workbook,
|
|
fileName: fileName
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/formulabar', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var classNames = { wrapper: 'k-spreadsheet-formula-bar' };
|
|
var FormulaBar = kendo.ui.Widget.extend({
|
|
init: function (element, options) {
|
|
kendo.ui.Widget.call(this, element, options);
|
|
element = this.element.addClass(FormulaBar.classNames.wrapper);
|
|
this.formulaInput = new kendo.spreadsheet.FormulaInput($('<div/>').appendTo(element));
|
|
},
|
|
destroy: function () {
|
|
if (this.formulaInput) {
|
|
this.formulaInput.destroy();
|
|
}
|
|
this.formulaInput = null;
|
|
}
|
|
});
|
|
kendo.spreadsheet.FormulaBar = FormulaBar;
|
|
$.extend(true, FormulaBar, { classNames: classNames });
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/formulainput', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo, window) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Widget = kendo.ui.Widget;
|
|
var ns = '.kendoFormulaInput';
|
|
var keys = kendo.keys;
|
|
var classNames = {
|
|
wrapper: 'k-spreadsheet-formula-input',
|
|
listWrapper: 'k-spreadsheet-formula-list'
|
|
};
|
|
var styles = [
|
|
'font-family',
|
|
'font-size',
|
|
'font-stretch',
|
|
'font-style',
|
|
'font-weight',
|
|
'letter-spacing',
|
|
'text-transform',
|
|
'line-height'
|
|
];
|
|
var KEY_NAMES = {
|
|
27: 'esc',
|
|
37: 'left',
|
|
39: 'right',
|
|
35: 'end',
|
|
36: 'home',
|
|
32: 'spacebar'
|
|
};
|
|
var PRIVATE_FORMULA_CHECK = /(^_|[^a-z0-9]$)/i;
|
|
var FormulaInput = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.call(this, element, options);
|
|
element = this.element;
|
|
element.addClass(FormulaInput.classNames.wrapper).attr('contenteditable', true).attr('spellcheck', false);
|
|
if (this.options.autoScale) {
|
|
element.on('input', this.scale.bind(this));
|
|
}
|
|
this._highlightedRefs = [];
|
|
this._staticTokens = [];
|
|
this._formulaSource();
|
|
this._formulaList();
|
|
this._popup();
|
|
this._tooltip();
|
|
element.on('keydown', this._keydown.bind(this)).on('keyup', this._keyup.bind(this)).on('blur', this._blur.bind(this)).on('input click', this._input.bind(this)).on('focus', this._focus.bind(this));
|
|
},
|
|
options: {
|
|
name: 'FormulaInput',
|
|
autoScale: false,
|
|
filterOperator: 'startswith',
|
|
scalePadding: 30,
|
|
minLength: 1
|
|
},
|
|
events: [
|
|
'keyup',
|
|
'focus'
|
|
],
|
|
enable: function (enable) {
|
|
if (enable === undefined) {
|
|
return this.element.attr('contenteditable') === 'false' ? false : true;
|
|
}
|
|
this.element.attr('contenteditable', enable).toggleClass('k-state-disabled', !enable);
|
|
},
|
|
getPos: function () {
|
|
var div = this.element[0];
|
|
var sel = window.getSelection();
|
|
var a = lookup(sel.focusNode, sel.focusOffset);
|
|
var b = lookup(sel.anchorNode, sel.anchorOffset);
|
|
if (a != null && b != null) {
|
|
if (a > b) {
|
|
var tmp = a;
|
|
a = b;
|
|
b = tmp;
|
|
}
|
|
return {
|
|
begin: a,
|
|
end: b,
|
|
collapsed: a == b
|
|
};
|
|
}
|
|
function lookup(lookupNode, pos) {
|
|
try {
|
|
(function loop(node) {
|
|
if (node === lookupNode) {
|
|
throw pos;
|
|
} else if (node.nodeType == 1) {
|
|
for (var i = node.firstChild; i; i = i.nextSibling) {
|
|
loop(i);
|
|
}
|
|
} else if (node.nodeType == 3) {
|
|
pos += node.nodeValue.length;
|
|
}
|
|
}(div));
|
|
} catch (index) {
|
|
return index;
|
|
}
|
|
}
|
|
},
|
|
setPos: function (begin, end) {
|
|
var eiv = this.element[0];
|
|
begin = lookup(eiv, begin);
|
|
if (end != null) {
|
|
end = lookup(eiv, end);
|
|
} else {
|
|
end = begin;
|
|
}
|
|
if (begin && end) {
|
|
var range = document.createRange();
|
|
range.setStart(begin.node, begin.pos);
|
|
range.setEnd(end.node, end.pos);
|
|
var sel = window.getSelection();
|
|
var currentRange = sel.getRangeAt(0);
|
|
if (differ(range, currentRange)) {
|
|
sel.removeAllRanges();
|
|
sel.addRange(range);
|
|
}
|
|
}
|
|
function differ(a, b) {
|
|
return a.startOffset != b.startOffset || a.endOffset != b.endOffset || a.startContainer != b.endContainer || a.endContainer != b.endContainer;
|
|
}
|
|
function lookup(node, pos) {
|
|
try {
|
|
(function loop(node) {
|
|
if (node.nodeType == 3) {
|
|
var len = node.nodeValue.length;
|
|
if (len >= pos) {
|
|
throw node;
|
|
}
|
|
pos -= len;
|
|
} else if (node.nodeType == 1) {
|
|
for (var i = node.firstChild; i; i = i.nextSibling) {
|
|
loop(i);
|
|
}
|
|
}
|
|
}(node));
|
|
} catch (el) {
|
|
return {
|
|
node: el,
|
|
pos: pos
|
|
};
|
|
}
|
|
}
|
|
},
|
|
end: function () {
|
|
this.setPos(this.length());
|
|
},
|
|
home: function () {
|
|
this.setPos(0);
|
|
},
|
|
length: function () {
|
|
return this.value().length;
|
|
},
|
|
_formulaSource: function () {
|
|
var result = [];
|
|
var value;
|
|
for (var key in kendo.spreadsheet.calc.runtime.FUNCS) {
|
|
if (!PRIVATE_FORMULA_CHECK.test(key)) {
|
|
value = key.toUpperCase();
|
|
result.push({
|
|
value: value,
|
|
text: value
|
|
});
|
|
}
|
|
}
|
|
this.formulaSource = new kendo.data.DataSource({ data: result });
|
|
},
|
|
_formulaList: function () {
|
|
this.list = new kendo.ui.StaticList($('<ul />').addClass(FormulaInput.classNames.listWrapper).insertAfter(this.element), {
|
|
autoBind: false,
|
|
selectable: true,
|
|
change: this._formulaListChange.bind(this),
|
|
dataSource: this.formulaSource,
|
|
dataValueField: 'value',
|
|
template: '#:data.value#'
|
|
});
|
|
this.list.element.on('mousedown', function (e) {
|
|
e.preventDefault();
|
|
});
|
|
},
|
|
_formulaListChange: function () {
|
|
var tokenCtx = this._tokenContext();
|
|
if (!tokenCtx || this._mute) {
|
|
return;
|
|
}
|
|
var activeToken = tokenCtx.token;
|
|
var completion = this.list.value()[0];
|
|
var ctx = {
|
|
replace: true,
|
|
token: activeToken,
|
|
end: activeToken.end
|
|
};
|
|
if (!tokenCtx.nextToken || tokenCtx.nextToken.value != '(') {
|
|
completion += '(';
|
|
}
|
|
this._replaceAt(ctx, completion);
|
|
this.popup.close();
|
|
},
|
|
_popup: function () {
|
|
this.popup = new kendo.ui.Popup(this.list.element, { anchor: this.element });
|
|
},
|
|
_blur: function () {
|
|
this.popup.close();
|
|
clearTimeout(this._focusId);
|
|
},
|
|
_isFormula: function () {
|
|
return /^=/.test(this.value());
|
|
},
|
|
_keydown: function (e) {
|
|
var key = e.keyCode;
|
|
if (KEY_NAMES[key]) {
|
|
this.popup.close();
|
|
this._navigated = true;
|
|
} else if (this._move(key)) {
|
|
this._navigated = true;
|
|
e.preventDefault();
|
|
}
|
|
this._keyDownTimeout = setTimeout(this._syntaxHighlight.bind(this));
|
|
},
|
|
_keyup: function () {
|
|
var popup = this.popup;
|
|
var value;
|
|
if (this._isFormula() && !this._navigated) {
|
|
value = ((this._tokenContext() || {}).token || {}).value;
|
|
this.filter(value);
|
|
if (!value || !this.formulaSource.view().length) {
|
|
popup.close();
|
|
} else {
|
|
popup[popup.visible() ? 'position' : 'open']();
|
|
this.list.focusFirst();
|
|
}
|
|
}
|
|
this._navigated = false;
|
|
this._syntaxHighlight();
|
|
this.trigger('keyup');
|
|
},
|
|
_input: function () {
|
|
this._syntaxHighlight();
|
|
},
|
|
_focus: function () {
|
|
this._focusTimeout = setTimeout(this._syntaxHighlight.bind(this));
|
|
this.trigger('focus');
|
|
},
|
|
_move: function (key) {
|
|
var list = this.list;
|
|
var pressed = false;
|
|
var popup = this.popup;
|
|
if (key === keys.DOWN) {
|
|
list.focusNext();
|
|
if (!list.focus()) {
|
|
list.focusFirst();
|
|
}
|
|
pressed = true;
|
|
} else if (key === keys.UP) {
|
|
list.focusPrev();
|
|
if (!list.focus()) {
|
|
list.focusLast();
|
|
}
|
|
pressed = true;
|
|
} else if (key === keys.ENTER) {
|
|
if (popup.visible()) {
|
|
list.select(list.focus());
|
|
}
|
|
popup.close();
|
|
pressed = true;
|
|
} else if (key === keys.TAB) {
|
|
list.select(list.focus());
|
|
popup.close();
|
|
pressed = true;
|
|
} else if (key === keys.PAGEUP) {
|
|
list.focusFirst();
|
|
pressed = true;
|
|
} else if (key === keys.PAGEDOWN) {
|
|
list.focusLast();
|
|
pressed = true;
|
|
}
|
|
return pressed;
|
|
},
|
|
_tokenContext: function () {
|
|
var point = this.getPos();
|
|
var value = this.value();
|
|
if (!value || !point || !point.collapsed) {
|
|
return null;
|
|
}
|
|
var tokens = kendo.spreadsheet.calc.tokenize(value, this.row(), this.col());
|
|
var tok;
|
|
for (var i = 0; i < tokens.length; ++i) {
|
|
tok = tokens[i];
|
|
if (touches(tok, point) && /^(?:str|sym|func)$/.test(tok.type)) {
|
|
return {
|
|
token: tok,
|
|
nextToken: tokens[i + 1]
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
_sync: function () {
|
|
if (this._editorToSync && this.isActive()) {
|
|
this._editorToSync.value(this.value());
|
|
}
|
|
},
|
|
_textContainer: function () {
|
|
var computedStyles = kendo.getComputedStyles(this.element[0], styles);
|
|
computedStyles.position = 'absolute';
|
|
computedStyles.visibility = 'hidden';
|
|
computedStyles.whiteSpace = 'pre';
|
|
computedStyles.top = -3333;
|
|
computedStyles.left = -3333;
|
|
this._span = $('<span/>').css(computedStyles).insertAfter(this.element);
|
|
},
|
|
_tooltip: function () {
|
|
this._cellTooltip = $('<div class="k-widget k-tooltip" style="position:absolute; display:none">A1</div>').insertAfter(this.element);
|
|
},
|
|
tooltip: function (value) {
|
|
this._cellTooltip.text(value);
|
|
},
|
|
toggleTooltip: function (show) {
|
|
this._cellTooltip.toggle(show);
|
|
},
|
|
isActive: function () {
|
|
return this.element[0] === kendo._activeElement();
|
|
},
|
|
filter: function (value) {
|
|
if (!value || value.length < this.options.minLength) {
|
|
return;
|
|
}
|
|
this._mute = true;
|
|
this.list.select(-1);
|
|
this._mute = false;
|
|
this.formulaSource.filter({
|
|
field: this.list.options.dataValueField,
|
|
operator: this.options.filterOperator,
|
|
value: value
|
|
});
|
|
},
|
|
hide: function () {
|
|
this.element.hide();
|
|
this._cellTooltip.hide();
|
|
},
|
|
show: function () {
|
|
this.element.show();
|
|
},
|
|
row: function () {
|
|
if (this.activeCell) {
|
|
return this.activeCell.row;
|
|
}
|
|
},
|
|
col: function () {
|
|
if (this.activeCell) {
|
|
return this.activeCell.col;
|
|
}
|
|
},
|
|
position: function (rectangle) {
|
|
if (!rectangle) {
|
|
return;
|
|
}
|
|
this.element.show().css({
|
|
'top': rectangle.top + 1 + 'px',
|
|
'left': rectangle.left + 1 + 'px'
|
|
});
|
|
this._cellTooltip.css({
|
|
'top': rectangle.top - this._cellTooltip.height() - 10 + 'px',
|
|
'left': rectangle.left
|
|
});
|
|
},
|
|
resize: function (rectangle) {
|
|
if (!rectangle) {
|
|
return;
|
|
}
|
|
this.element.css({
|
|
width: rectangle.width - 1,
|
|
height: rectangle.height - 1
|
|
});
|
|
},
|
|
canInsertRef: function (isKeyboardAction) {
|
|
var result = this._canInsertRef(isKeyboardAction);
|
|
var token = result && result.token;
|
|
var idx;
|
|
if (token) {
|
|
for (idx = 0; idx < this._staticTokens.length; idx++) {
|
|
if (isEqualToken(token, this._staticTokens[idx])) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_canInsertRef: function (isKeyboardAction) {
|
|
if (this.popup.visible()) {
|
|
return null;
|
|
}
|
|
var strictMode = isKeyboardAction;
|
|
var point = this.getPos();
|
|
var tokens, tok;
|
|
if (point && this._isFormula()) {
|
|
if (point.begin === 0) {
|
|
return null;
|
|
}
|
|
tokens = kendo.spreadsheet.calc.tokenize(this.value(), this.row(), this.col());
|
|
for (var i = 0; i < tokens.length; ++i) {
|
|
tok = tokens[i];
|
|
if (touches(tok, point)) {
|
|
return canReplace(tok);
|
|
}
|
|
if (afterPoint(tok)) {
|
|
return canInsertBetween(tokens[i - 1], tok);
|
|
}
|
|
}
|
|
return canInsertBetween(tok, null);
|
|
}
|
|
return null;
|
|
function afterPoint(tok) {
|
|
return tok.begin > point.begin;
|
|
}
|
|
function canReplace(tok) {
|
|
if (tok) {
|
|
if (/^(?:num|str|bool|sym|ref)$/.test(tok.type)) {
|
|
return {
|
|
replace: true,
|
|
token: tok,
|
|
end: tok.end
|
|
};
|
|
}
|
|
if (/^(?:op|punc|startexp)$/.test(tok.type)) {
|
|
if (tok.end == point.end) {
|
|
return canInsertBetween(tok, tokens[i + 1]);
|
|
}
|
|
return canInsertBetween(tokens[i - 1], tok);
|
|
}
|
|
}
|
|
}
|
|
function canInsertBetween(left, right) {
|
|
if (left == null) {
|
|
return null;
|
|
}
|
|
if (right == null) {
|
|
if (/^(?:op|startexp)$/.test(left.type) || isOpenParen(left.value)) {
|
|
return {
|
|
token: left,
|
|
end: point.end
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
if (strictMode) {
|
|
if (left.type == 'op' && /^(?:punc|op)$/.test(right.type)) {
|
|
return {
|
|
token: left,
|
|
end: point.end
|
|
};
|
|
}
|
|
} else {
|
|
if (left.type == 'startexp') {
|
|
return {
|
|
token: left,
|
|
end: point.end
|
|
};
|
|
}
|
|
if (/^(?:ref|op|punc)$/.test(left.type)) {
|
|
return {
|
|
token: left,
|
|
end: point.end
|
|
};
|
|
}
|
|
if (/^(?:punc|op)$/.test(left.type)) {
|
|
return /^[,;({]$/.test(left.value) ? {
|
|
token: left,
|
|
end: point.end
|
|
} : null;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
refAtPoint: function (ref) {
|
|
var x = this._canInsertRef();
|
|
if (x) {
|
|
this._replaceAt(x, ref.simplify().toString());
|
|
}
|
|
},
|
|
_replaceAt: function (ctx, newValue) {
|
|
var value = this.value();
|
|
var tok = ctx.token;
|
|
var rest = value.substr(ctx.end);
|
|
value = value.substr(0, ctx.replace ? tok.begin : ctx.end) + newValue;
|
|
var point = value.length;
|
|
value += rest;
|
|
this._value(value);
|
|
this.setPos(point);
|
|
this.scale();
|
|
this._syntaxHighlight();
|
|
this._sync();
|
|
},
|
|
syncWith: function (formulaInput) {
|
|
var self = this;
|
|
var eventName = 'input' + ns;
|
|
var handler = self._sync.bind(self), iehandler;
|
|
if (kendo.support.browser.msie) {
|
|
eventName = 'keydown' + ns;
|
|
iehandler = function () {
|
|
setTimeout(handler);
|
|
};
|
|
}
|
|
self._editorToSync = formulaInput;
|
|
self.element.off(eventName).on(eventName, iehandler || handler);
|
|
},
|
|
scale: function () {
|
|
var element = this.element;
|
|
var width;
|
|
if (!this._span) {
|
|
this._textContainer();
|
|
}
|
|
this._span.html(element.html());
|
|
width = this._span.width() + this.options.scalePadding;
|
|
if (width > element.width()) {
|
|
element.width(width);
|
|
}
|
|
},
|
|
_value: function (value) {
|
|
this.element.text(value);
|
|
},
|
|
value: function (value) {
|
|
if (value === undefined) {
|
|
return this.element.text();
|
|
}
|
|
this._value(value);
|
|
this._syntaxHighlight();
|
|
},
|
|
highlightedRefs: function () {
|
|
return this._highlightedRefs.slice();
|
|
},
|
|
_syntaxHighlight: function () {
|
|
var pos = this.getPos();
|
|
var value = this.value();
|
|
var refClasses = kendo.spreadsheet.Pane.classNames.series;
|
|
var highlightedRefs = [];
|
|
var refIndex = 0;
|
|
var parens = [];
|
|
var tokens = [];
|
|
var activeToken;
|
|
if (pos && !pos.collapsed) {
|
|
return;
|
|
}
|
|
if (!/^=/.test(value)) {
|
|
if (this.element.html() != value) {
|
|
this.element.text(value);
|
|
}
|
|
if (this.popup) {
|
|
this.popup.close();
|
|
}
|
|
} else {
|
|
tokens = kendo.spreadsheet.calc.tokenize(value, this.row(), this.col());
|
|
tokens.forEach(function (tok) {
|
|
tok.active = false;
|
|
tok.cls = ['k-syntax-' + tok.type];
|
|
if (tok.type == 'ref') {
|
|
tok.colorClass = refClasses[refIndex++ % refClasses.length];
|
|
tok.cls.push(tok.colorClass);
|
|
highlightedRefs.push(tok);
|
|
}
|
|
if (pos && tok.type == 'punc') {
|
|
if (isOpenParen(tok.value)) {
|
|
parens.unshift(tok);
|
|
} else if (isCloseParen(tok.value)) {
|
|
var open = parens.shift();
|
|
if (open) {
|
|
if (isMatchingParen(tok.value, open.value)) {
|
|
if (touches(tok, pos) || touches(open, pos)) {
|
|
tok.cls.push('k-syntax-paren-match');
|
|
open.cls.push('k-syntax-paren-match');
|
|
}
|
|
} else {
|
|
tok.cls.push('k-syntax-error');
|
|
open.cls.push('k-syntax-error');
|
|
}
|
|
} else {
|
|
tok.cls.push('k-syntax-error');
|
|
}
|
|
}
|
|
}
|
|
if (pos && touches(tok, pos)) {
|
|
tok.cls.push('k-syntax-at-point');
|
|
tok.active = true;
|
|
activeToken = tok;
|
|
}
|
|
if (tok.type == 'func' && !knownFunction(tok.value) && (!pos || !touches(tok, pos))) {
|
|
tok.cls.push('k-syntax-error');
|
|
}
|
|
});
|
|
tokens.reverse().forEach(function (tok) {
|
|
var begin = tok.begin, end = tok.end;
|
|
var text = kendo.htmlEncode(value.substring(begin, end));
|
|
value = value.substr(0, begin) + '<span class=\'' + tok.cls.join(' ') + '\'>' + text + '</span>' + value.substr(end);
|
|
});
|
|
this.element.html(value);
|
|
}
|
|
if (pos) {
|
|
this.setPos(pos.begin, pos.end);
|
|
}
|
|
if (activeToken && /^(?:startexp|op|punc)$/.test(activeToken.type)) {
|
|
this._setStaticTokens(tokens);
|
|
}
|
|
this._highlightedRefs = highlightedRefs;
|
|
},
|
|
_setStaticTokens: function (tokens) {
|
|
var idx, tok;
|
|
this._staticTokens = [];
|
|
for (idx = 0; idx < tokens.length; idx++) {
|
|
tok = tokens[idx];
|
|
if (/^(?:num|str|bool|sym|ref)$/.test(tok.type)) {
|
|
this._staticTokens.push(tok);
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this._editorToSync = null;
|
|
this.element.off(ns);
|
|
clearTimeout(this._focusTimeout);
|
|
clearTimeout(this._keyDownTimeout);
|
|
this._cellTooltip = null;
|
|
this._span = null;
|
|
this.popup.destroy();
|
|
this.popup = null;
|
|
Widget.fn.destroy.call(this);
|
|
}
|
|
});
|
|
function isOpenParen(ch) {
|
|
return ch == '(' || ch == '[' || ch == '{';
|
|
}
|
|
function isCloseParen(ch) {
|
|
return ch == ')' || ch == ']' || ch == '}';
|
|
}
|
|
function isMatchingParen(close, open) {
|
|
return open == '(' ? close == ')' : open == '[' ? close == ']' : open == '{' ? close == '}' : false;
|
|
}
|
|
function touches(pos, target) {
|
|
return pos.begin <= target.begin && pos.end >= target.end;
|
|
}
|
|
function knownFunction(name) {
|
|
return kendo.spreadsheet.calc.runtime.FUNCS[name.toLowerCase()];
|
|
}
|
|
function isEqualToken(tok1, tok2) {
|
|
if (!tok1 || !tok2) {
|
|
return false;
|
|
}
|
|
if (tok1.type == 'ref' && tok2.type == 'ref') {
|
|
return tok1.ref.eq(tok2.ref);
|
|
} else {
|
|
return tok1.value === tok2.value;
|
|
}
|
|
}
|
|
kendo.spreadsheet.FormulaInput = FormulaInput;
|
|
$.extend(true, FormulaInput, { classNames: classNames });
|
|
}(kendo, window));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/eventlistener', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var KEY_NAMES = {
|
|
8: 'backspace',
|
|
9: 'tab',
|
|
13: 'enter',
|
|
27: 'esc',
|
|
37: 'left',
|
|
38: 'up',
|
|
39: 'right',
|
|
40: 'down',
|
|
35: 'end',
|
|
36: 'home',
|
|
32: 'spacebar',
|
|
33: 'pageup',
|
|
34: 'pagedown',
|
|
46: 'delete',
|
|
113: ':edit'
|
|
};
|
|
var Mac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
|
var isAlphaNum = function (keyCode) {
|
|
if (keyCode > 47 && keyCode < 58 || keyCode > 64 && keyCode < 91 || keyCode > 95 && keyCode < 112 || keyCode > 185 && keyCode < 193 || keyCode > 218 && keyCode < 223) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var keyName = function (keyCode) {
|
|
var name = KEY_NAMES[keyCode];
|
|
if (!name && isAlphaNum(keyCode)) {
|
|
name = ':alphanum';
|
|
}
|
|
return name;
|
|
};
|
|
var EventListener = kendo.Class.extend({
|
|
init: function (target, observer, handlers) {
|
|
this._handlers = {};
|
|
this.target = target;
|
|
this._observer = observer || window;
|
|
this.keyDownProxy = this.keyDown.bind(this);
|
|
this.mouseProxy = this.mouse.bind(this);
|
|
this.threshold = 5;
|
|
this._pressLocation = null;
|
|
target.on('keydown', this.keyDownProxy);
|
|
target.on('contextmenu mousedown cut copy paste scroll wheel click dblclick focus', this.mouseProxy);
|
|
$(document.documentElement).on('mousemove mouseup', this.mouseProxy);
|
|
if (handlers) {
|
|
for (var key in handlers) {
|
|
this.on(key, handlers[key]);
|
|
}
|
|
}
|
|
},
|
|
keyDown: function (e) {
|
|
this.handleEvent(e, keyName(e.keyCode));
|
|
},
|
|
mouse: function (e) {
|
|
var rightClick;
|
|
if (e.which) {
|
|
rightClick = e.which == 3;
|
|
} else if (e.button) {
|
|
rightClick = e.button == 2;
|
|
}
|
|
var type = e.type;
|
|
if (type === 'mousedown') {
|
|
if (rightClick) {
|
|
type = 'rightmousedown';
|
|
} else {
|
|
this._pressLocation = {
|
|
x: e.pageX,
|
|
y: e.pageY
|
|
};
|
|
}
|
|
}
|
|
if (type === 'mouseup') {
|
|
if (!rightClick) {
|
|
this._pressLocation = null;
|
|
}
|
|
}
|
|
if (type === 'mousemove' && this._pressLocation) {
|
|
var dx = this._pressLocation.x - e.pageX;
|
|
var dy = this._pressLocation.y - e.pageY;
|
|
var distance = Math.sqrt(dx * dx + dy * dy);
|
|
if (distance > this.threshold) {
|
|
type = 'mousedrag';
|
|
}
|
|
}
|
|
this.handleEvent(e, type);
|
|
},
|
|
handleEvent: function (e, name) {
|
|
var eventKey = '';
|
|
e.mod = Mac ? e.metaKey : e.ctrlKey;
|
|
if (e.shiftKey) {
|
|
eventKey += 'shift+';
|
|
}
|
|
if (e.ctrlKey) {
|
|
eventKey += 'ctrl+';
|
|
}
|
|
eventKey += name;
|
|
var catchAllHandler = this._handlers['*+' + name];
|
|
if (catchAllHandler) {
|
|
catchAllHandler.call(this._observer, e, eventKey);
|
|
}
|
|
var handler = this._handlers[eventKey];
|
|
if (handler) {
|
|
handler.call(this._observer, e, eventKey);
|
|
}
|
|
},
|
|
on: function (event, callback) {
|
|
var handlers = this._handlers;
|
|
if (typeof callback === 'string') {
|
|
callback = this._observer[callback];
|
|
}
|
|
if (typeof event === 'string') {
|
|
event = event.split(',');
|
|
}
|
|
event.forEach(function (e) {
|
|
handlers[e] = callback;
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this.target.off('keydown', this.keyDownProxy);
|
|
this.target.off('keydown', this.mouseProxy);
|
|
$(document.documentElement).off('mousemove mouseup', this.mouseProxy);
|
|
}
|
|
});
|
|
kendo.spreadsheet.EventListener = EventListener;
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/rangelist', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var RangeTreeNode = kendo.Class.extend({
|
|
init: function Node(level, value, left, right) {
|
|
this.level = level;
|
|
this.value = value;
|
|
this.left = left;
|
|
this.right = right;
|
|
}
|
|
});
|
|
var NilNode = new function NIL() {
|
|
this.left = this;
|
|
this.right = this;
|
|
this.level = 0;
|
|
}();
|
|
function passThrough(value) {
|
|
return value;
|
|
}
|
|
function skew(node) {
|
|
if (node.left.level === node.level) {
|
|
var temp = node;
|
|
node = node.left;
|
|
temp.left = node.right;
|
|
node.right = temp;
|
|
}
|
|
return node;
|
|
}
|
|
function split(node) {
|
|
if (node.right.right.level === node.level) {
|
|
var temp = node;
|
|
node = node.right;
|
|
temp.right = node.left;
|
|
node.left = temp;
|
|
node.level += 1;
|
|
}
|
|
return node;
|
|
}
|
|
function insert(node, value) {
|
|
if (node === NilNode) {
|
|
return new RangeTreeNode(1, value, NilNode, NilNode);
|
|
} else if (node.value.start - value.start > 0) {
|
|
node.left = insert(node.left, value);
|
|
} else {
|
|
node.right = insert(node.right, value);
|
|
}
|
|
return split(skew(node));
|
|
}
|
|
function remove(node, value) {
|
|
if (node === NilNode) {
|
|
return node;
|
|
}
|
|
var diff = node.value.start - value.start;
|
|
if (diff === 0) {
|
|
if (node.left !== NilNode && node.right !== NilNode) {
|
|
var heir = node.left;
|
|
while (heir.right !== NilNode) {
|
|
heir = heir.right;
|
|
}
|
|
node.value = heir.value;
|
|
node.left = remove(node.left, node.value);
|
|
} else if (node.left === NilNode) {
|
|
node = node.right;
|
|
} else {
|
|
node = node.left;
|
|
}
|
|
} else if (diff > 0) {
|
|
node.left = remove(node.left, value);
|
|
} else {
|
|
node.right = remove(node.right, value);
|
|
}
|
|
if (node.left.level < node.level - 1 || node.right.level < node.level - 1) {
|
|
node.level -= 1;
|
|
if (node.right.level > node.level) {
|
|
node.right.level = node.level;
|
|
}
|
|
node = skew(node);
|
|
node.right = skew(node.right);
|
|
node.right.right = skew(node.right.right);
|
|
node = split(node);
|
|
node.right = split(node.right);
|
|
}
|
|
return node;
|
|
}
|
|
var Range = kendo.Class.extend({
|
|
init: function Value(start, end, value) {
|
|
this.start = start;
|
|
this.end = end;
|
|
this.value = value;
|
|
},
|
|
intersects: function (range) {
|
|
return range.start <= this.end && range.end >= this.start;
|
|
}
|
|
});
|
|
var RangeTree = kendo.Class.extend({
|
|
init: function () {
|
|
this.root = NilNode;
|
|
},
|
|
insert: function (value) {
|
|
this.root = insert(this.root, value);
|
|
},
|
|
remove: function (value) {
|
|
this.root = remove(this.root, value);
|
|
},
|
|
findrange: function (value) {
|
|
var node = this.root;
|
|
while (node != NilNode) {
|
|
if (value < node.value.start) {
|
|
node = node.left;
|
|
} else if (value > node.value.end) {
|
|
node = node.right;
|
|
} else {
|
|
return node.value;
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
values: function () {
|
|
var result = [];
|
|
values(this.root, result);
|
|
return result;
|
|
},
|
|
intersecting: function (start, end) {
|
|
var ranges = [];
|
|
intersecting(this.root, new Range(start, end), ranges);
|
|
return ranges;
|
|
},
|
|
map: function (callback) {
|
|
var tree = new RangeTree();
|
|
map(tree, this.root, callback);
|
|
return tree;
|
|
},
|
|
clone: function () {
|
|
return this.map(passThrough);
|
|
},
|
|
first: function () {
|
|
var first = this.root;
|
|
while (first.left != NilNode) {
|
|
first = first.left;
|
|
}
|
|
return first;
|
|
},
|
|
last: function () {
|
|
var last = this.root;
|
|
while (last.right != NilNode) {
|
|
last = last.right;
|
|
}
|
|
return last;
|
|
}
|
|
});
|
|
function values(node, result) {
|
|
if (node === NilNode) {
|
|
return;
|
|
}
|
|
values(node.left, result);
|
|
result.push(node.value);
|
|
values(node.right, result);
|
|
}
|
|
function intersecting(node, range, ranges) {
|
|
if (node === NilNode) {
|
|
return;
|
|
}
|
|
var value = node.value;
|
|
if (range.start < value.start) {
|
|
intersecting(node.left, range, ranges);
|
|
}
|
|
if (value.intersects(range)) {
|
|
ranges.push(value);
|
|
}
|
|
if (range.end > value.end) {
|
|
intersecting(node.right, range, ranges);
|
|
}
|
|
}
|
|
function map(tree, root, callback) {
|
|
if (root === NilNode) {
|
|
return;
|
|
}
|
|
map(tree, root.left, callback);
|
|
tree.insert(callback(root.value));
|
|
map(tree, root.right, callback);
|
|
}
|
|
var RangeList = kendo.Class.extend({
|
|
init: function (start, end, value) {
|
|
if (end === undefined) {
|
|
this.tree = start;
|
|
} else {
|
|
this.tree = new RangeTree();
|
|
this.tree.insert(new Range(start, end, value));
|
|
}
|
|
},
|
|
values: function () {
|
|
return this.tree.values();
|
|
},
|
|
map: function (callback) {
|
|
return new RangeList(this.tree.map(callback));
|
|
},
|
|
intersecting: function (start, end) {
|
|
return this.tree.intersecting(start, end);
|
|
},
|
|
first: function () {
|
|
return this.tree.first().value;
|
|
},
|
|
last: function () {
|
|
return this.tree.last().value;
|
|
},
|
|
insert: function (start, end, value) {
|
|
return this.tree.insert(new Range(start, end, value));
|
|
},
|
|
value: function (start, end, value) {
|
|
if (value === undefined) {
|
|
if (end === undefined) {
|
|
end = start;
|
|
}
|
|
return this.intersecting(start, end)[0].value;
|
|
}
|
|
var ranges = this.tree.intersecting(start - 1, end + 1);
|
|
if (ranges.length) {
|
|
var firstRange = ranges[0], lastRange = ranges[ranges.length - 1];
|
|
if (firstRange.end < start) {
|
|
if (firstRange.value === value) {
|
|
start = firstRange.start;
|
|
} else {
|
|
ranges.shift();
|
|
}
|
|
}
|
|
if (lastRange.start > end) {
|
|
if (lastRange.value === value) {
|
|
end = lastRange.end;
|
|
} else {
|
|
ranges.pop();
|
|
}
|
|
}
|
|
for (var i = 0, length = ranges.length; i < length; i++) {
|
|
var range = ranges[i];
|
|
var rangeValue = range.value;
|
|
var rangeStart = range.start;
|
|
var rangeEnd = range.end;
|
|
this.tree.remove(range);
|
|
if (rangeStart < start) {
|
|
if (rangeValue !== value) {
|
|
this.insert(rangeStart, start - 1, rangeValue);
|
|
} else {
|
|
start = rangeStart;
|
|
}
|
|
}
|
|
if (rangeEnd > end) {
|
|
if (rangeValue !== value) {
|
|
this.insert(end + 1, rangeEnd, rangeValue);
|
|
} else {
|
|
end = rangeEnd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.insert(start, end, value);
|
|
},
|
|
expandedValues: function (start, end) {
|
|
var ranges = this.intersecting(start, end);
|
|
var result = [];
|
|
var rangeIndex = 0;
|
|
for (var i = start; i <= end; i++) {
|
|
if (ranges[rangeIndex].end < i) {
|
|
rangeIndex++;
|
|
}
|
|
result.push({
|
|
index: i - start,
|
|
value: ranges[rangeIndex].value
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
sortedIndices: function (start, end, valueComparer, indices) {
|
|
var result = this.expandedValues(start, end);
|
|
var comparer = function (a, b) {
|
|
if (a.value === b.value) {
|
|
return a.index - b.index;
|
|
}
|
|
return valueComparer(a.value, b.value);
|
|
};
|
|
if (indices) {
|
|
comparer = function (a, b) {
|
|
var x = indices[a.index];
|
|
var y = indices[b.index];
|
|
if (x.value === y.value) {
|
|
return valueComparer(a.value, b.value);
|
|
}
|
|
return a.index - b.index;
|
|
};
|
|
}
|
|
result.sort(comparer);
|
|
return result;
|
|
},
|
|
sort: function (start, end, indices) {
|
|
if (this.intersecting(start, end).length === 1) {
|
|
return;
|
|
}
|
|
var values = this.expandedValues(start, end);
|
|
for (var i = 0, len = indices.length; i < len; i++) {
|
|
this.value(i + start, i + start, values[indices[i].index].value);
|
|
}
|
|
},
|
|
copy: function (sourceStart, sourceEnd, targetStart) {
|
|
var values = this.intersecting(sourceStart, sourceEnd);
|
|
var start = targetStart;
|
|
var end;
|
|
for (var i = 0, len = values.length; i < len; i++) {
|
|
var rangeStart = values[i].start;
|
|
if (rangeStart < sourceStart) {
|
|
rangeStart = sourceStart;
|
|
}
|
|
var rangeEnd = values[i].end;
|
|
if (rangeEnd > sourceEnd) {
|
|
rangeEnd = sourceEnd;
|
|
}
|
|
end = start + (rangeEnd - rangeStart);
|
|
this.value(start, end, values[i].value);
|
|
start = ++end;
|
|
}
|
|
},
|
|
iterator: function (start, end) {
|
|
return new Iterator(start, end, this.intersecting(start, end));
|
|
},
|
|
getState: function () {
|
|
return this.tree.clone();
|
|
},
|
|
setState: function (state) {
|
|
this.tree = state;
|
|
}
|
|
});
|
|
var Iterator = kendo.Class.extend({
|
|
init: function (start, end, ranges) {
|
|
this.start = start;
|
|
this.end = end;
|
|
this.index = 0;
|
|
this.ranges = ranges;
|
|
},
|
|
unique: function () {
|
|
return this.ranges.map(function (range) {
|
|
return range.value;
|
|
});
|
|
},
|
|
at: function (index) {
|
|
while (this.ranges[this.index].end < index) {
|
|
this.index++;
|
|
}
|
|
return this.ranges[this.index].value;
|
|
},
|
|
forEach: function (callback) {
|
|
for (var i = this.start; i <= this.end; i++) {
|
|
callback(this.at(i), i);
|
|
}
|
|
this.index = 0;
|
|
}
|
|
});
|
|
var SparseRangeList = RangeList.extend({
|
|
init: function (start, end, value) {
|
|
this.tree = new RangeTree();
|
|
this.range = new Range(start, end, value);
|
|
},
|
|
intersecting: function (start, end) {
|
|
var ranges = this.tree.intersecting(start, end);
|
|
var result = [];
|
|
var range;
|
|
if (!ranges.length) {
|
|
return [this.range];
|
|
}
|
|
for (var i = 0, len = ranges.length; i < len; i++) {
|
|
range = ranges[i];
|
|
if (range.start > start) {
|
|
result.push(new Range(start, range.start - 1, this.range.value));
|
|
}
|
|
result.push(range);
|
|
start = range.end + 1;
|
|
}
|
|
if (range.end < end) {
|
|
result.push(new Range(range.end + 1, end, this.range.value));
|
|
}
|
|
return result;
|
|
},
|
|
insert: function (start, end, value) {
|
|
if (value !== this.range.value) {
|
|
this.tree.insert(new Range(start, end, value));
|
|
}
|
|
},
|
|
lastRangeStart: function () {
|
|
var node = this.tree.root;
|
|
if (node === NilNode) {
|
|
return this.range.start;
|
|
}
|
|
while (node.right !== NilNode) {
|
|
node = node.right;
|
|
}
|
|
return node.value.end + 1;
|
|
}
|
|
});
|
|
kendo.spreadsheet.RangeTree = RangeTree;
|
|
kendo.spreadsheet.RangeList = RangeList;
|
|
kendo.spreadsheet.SparseRangeList = SparseRangeList;
|
|
kendo.spreadsheet.ValueRange = Range;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/propertybag', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Property = kendo.Class.extend({
|
|
init: function (list) {
|
|
this.list = list;
|
|
},
|
|
get: function (index) {
|
|
return this.parse(this.list.value(index, index));
|
|
},
|
|
set: function (start, end, value) {
|
|
if (value === undefined) {
|
|
value = end;
|
|
end = start;
|
|
}
|
|
this.list.value(start, end, value);
|
|
},
|
|
parse: function (value) {
|
|
return value;
|
|
},
|
|
copy: function (start, end, dst) {
|
|
this.list.copy(start, end, dst);
|
|
},
|
|
iterator: function (start, end) {
|
|
return this.list.iterator(start, end);
|
|
}
|
|
});
|
|
var JsonProperty = Property.extend({
|
|
set: function (start, end, value) {
|
|
this.list.value(start, end, JSON.stringify(value));
|
|
},
|
|
parse: function (value) {
|
|
return JSON.parse(value);
|
|
}
|
|
});
|
|
var ValueProperty = Property.extend({
|
|
init: function (values, formats, validations) {
|
|
Property.prototype.init.call(this, values);
|
|
this.validations = validations;
|
|
this.formats = formats;
|
|
},
|
|
set: function (start, end, value) {
|
|
if (value instanceof Date) {
|
|
value = kendo.spreadsheet.dateToNumber(value);
|
|
this.formats.value(start, end, toExcelFormat(kendo.culture().calendar.patterns.d));
|
|
}
|
|
this.list.value(start, end, value);
|
|
}
|
|
});
|
|
function toExcelFormat(format) {
|
|
return format.replace(/M/g, 'm').replace(/'/g, '"').replace(/tt/, 'am/pm');
|
|
}
|
|
kendo.spreadsheet.PropertyBag = kendo.Class.extend({
|
|
specs: [
|
|
{
|
|
property: ValueProperty,
|
|
name: 'value',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true,
|
|
depends: 'format'
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'format',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'formula',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'background',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: JsonProperty,
|
|
name: 'borderBottom',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: JsonProperty,
|
|
name: 'borderRight',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: JsonProperty,
|
|
name: 'borderLeft',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: JsonProperty,
|
|
name: 'borderTop',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'color',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'fontFamily',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'underline',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'fontSize',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'italic',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'bold',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'textAlign',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'verticalAlign',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'wrap',
|
|
value: null,
|
|
sortable: true,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'validation',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
},
|
|
{
|
|
property: Property,
|
|
name: 'enable',
|
|
value: null,
|
|
sortable: false,
|
|
serializable: true
|
|
}
|
|
],
|
|
init: function (cellCount) {
|
|
this.properties = {};
|
|
this.lists = {};
|
|
this.specs.forEach(function (spec) {
|
|
this.lists[spec.name] = new kendo.spreadsheet.SparseRangeList(0, cellCount, spec.value);
|
|
}, this);
|
|
this.specs.forEach(function (spec) {
|
|
this.properties[spec.name] = new spec.property(this.lists[spec.name], this.lists[spec.depends]);
|
|
}, this);
|
|
},
|
|
getState: function () {
|
|
var state = {};
|
|
this.specs.forEach(function (spec) {
|
|
state[spec.name] = this.lists[spec.name].getState();
|
|
}, this);
|
|
return state;
|
|
},
|
|
setState: function (state) {
|
|
this.specs.forEach(function (spec) {
|
|
this.lists[spec.name].setState(state[spec.name]);
|
|
}, this);
|
|
},
|
|
get: function (name, index) {
|
|
if (index === undefined) {
|
|
return this.lists[name];
|
|
}
|
|
return this.properties[name].get(index);
|
|
},
|
|
set: function (name, start, end, value) {
|
|
this.properties[name].set(start, end, value);
|
|
},
|
|
fromJSON: function (index, value) {
|
|
for (var si = 0; si < this.specs.length; si++) {
|
|
var spec = this.specs[si];
|
|
if (spec.serializable) {
|
|
if (value[spec.name] !== undefined) {
|
|
this.set(spec.name, index, index, value[spec.name], false);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
copy: function (sourceStart, sourceEnd, targetStart) {
|
|
this.specs.forEach(function (spec) {
|
|
this.properties[spec.name].copy(sourceStart, sourceEnd, targetStart);
|
|
}, this);
|
|
},
|
|
iterator: function (name, start, end) {
|
|
return this.properties[name].iterator(start, end);
|
|
},
|
|
sortable: function () {
|
|
return this.specs.filter(function (spec) {
|
|
return spec.sortable;
|
|
}).map(function (spec) {
|
|
return this.lists[spec.name];
|
|
}, this);
|
|
},
|
|
iterators: function (start, end) {
|
|
var specs = this.specs.filter(function (spec) {
|
|
return spec.serializable;
|
|
});
|
|
return specs.map(function (spec) {
|
|
var iterator = this.iterator(spec.name, start, end);
|
|
return {
|
|
name: spec.name,
|
|
value: spec.value,
|
|
at: function (index) {
|
|
return spec.property.fn.parse(iterator.at(index));
|
|
}
|
|
};
|
|
}, this);
|
|
},
|
|
forEach: function (start, end, callback) {
|
|
var iterators = this.iterators(start, end);
|
|
for (var index = start; index <= end; index++) {
|
|
var values = {};
|
|
for (var i = 0; i < iterators.length; i++) {
|
|
var iterator = iterators[i];
|
|
var value = iterator.at(index);
|
|
if (value !== iterator.value) {
|
|
values[iterator.name] = value;
|
|
}
|
|
}
|
|
callback(values);
|
|
}
|
|
},
|
|
forEachProperty: function (callback) {
|
|
for (var name in this.properties) {
|
|
callback(this.properties[name]);
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.ALL_PROPERTIES = $.map(kendo.spreadsheet.PropertyBag.prototype.specs, function (spec) {
|
|
return spec.name;
|
|
});
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/references', ['kendo.core'], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var Class = kendo.Class;
|
|
function columnName(colIndex) {
|
|
var letter = Math.floor(colIndex / 26) - 1;
|
|
return (letter >= 0 ? columnName(letter) : '') + String.fromCharCode(65 + colIndex % 26);
|
|
}
|
|
function displaySheet(sheet) {
|
|
if (/^[a-z0-9_]*$/i.test(sheet)) {
|
|
return sheet;
|
|
}
|
|
return '\'' + sheet.replace(/\x27/g, '\\\'') + '\'';
|
|
}
|
|
function displayRef(sheet, row, col, rel) {
|
|
var aa = '';
|
|
++row;
|
|
if (!isFinite(row)) {
|
|
row = '';
|
|
} else if (rel != null && !(rel & 2)) {
|
|
row = '$' + row;
|
|
}
|
|
if (!isFinite(col)) {
|
|
col = '';
|
|
} else {
|
|
aa = columnName(col);
|
|
if (rel != null && !(rel & 1)) {
|
|
aa = '$' + aa;
|
|
}
|
|
}
|
|
if (sheet) {
|
|
return displaySheet(sheet) + '!' + aa + row;
|
|
} else {
|
|
return aa + row;
|
|
}
|
|
}
|
|
var Ref = Class.extend({
|
|
type: 'ref',
|
|
hasSheet: function () {
|
|
return this._hasSheet;
|
|
},
|
|
simplify: function () {
|
|
return this;
|
|
},
|
|
setSheet: function (sheet, hasSheet) {
|
|
this.sheet = sheet;
|
|
if (hasSheet != null) {
|
|
this._hasSheet = hasSheet;
|
|
}
|
|
return this;
|
|
},
|
|
absolute: function () {
|
|
return this;
|
|
},
|
|
relative: function () {
|
|
return this;
|
|
},
|
|
adjust: function () {
|
|
return this;
|
|
},
|
|
toString: function () {
|
|
return this.relative(0, 0, 3, 3).print(0, 0);
|
|
},
|
|
forEach: function (callback, obj) {
|
|
callback.call(obj, this);
|
|
},
|
|
map: function (callback, obj) {
|
|
return callback.call(obj, this);
|
|
},
|
|
intersects: function (ref) {
|
|
return this.intersect(ref) !== NULL;
|
|
},
|
|
isCell: function () {
|
|
return false;
|
|
},
|
|
toRow: function () {
|
|
return this;
|
|
},
|
|
toColumn: function () {
|
|
return this;
|
|
},
|
|
first: function () {
|
|
return this;
|
|
},
|
|
lastRange: function () {
|
|
return this;
|
|
},
|
|
size: function () {
|
|
return 1;
|
|
},
|
|
rangeAt: function () {
|
|
return this;
|
|
},
|
|
nextRangeIndex: function () {
|
|
return 0;
|
|
},
|
|
previousRangeIndex: function () {
|
|
return 0;
|
|
},
|
|
eq: function (reference) {
|
|
var r1 = this;
|
|
var r2 = reference;
|
|
if (r1 === NULL || r2 === NULL) {
|
|
return r1 === r2;
|
|
}
|
|
if (r2 instanceof CellRef || r2 instanceof RangeRef && !(r1 instanceof CellRef)) {
|
|
r1 = reference;
|
|
r2 = this;
|
|
}
|
|
if (r1 instanceof CellRef) {
|
|
r2 = r2.simplify();
|
|
return r2 instanceof CellRef && r1.row == r2.row && r1.col == r2.col && r1.sheet == r2.sheet;
|
|
} else if (r1 instanceof RangeRef) {
|
|
if (r2 instanceof RangeRef) {
|
|
return r2.topLeft.eq(r1.topLeft) && r2.bottomRight.eq(r1.bottomRight);
|
|
}
|
|
if (r2 instanceof UnionRef) {
|
|
return r2.single() && r1.eq(r2.refs[0]);
|
|
}
|
|
} else if (r1 instanceof UnionRef && r2 instanceof UnionRef) {
|
|
var refs1 = r1.refs;
|
|
var refs2 = r2.refs;
|
|
if (refs1.length != refs2.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0, len = refs1.length; i < len; i++) {
|
|
if (!refs1[i].eq(refs2[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return r1 === r2;
|
|
},
|
|
concat: function (ref) {
|
|
return new UnionRef([
|
|
this,
|
|
ref
|
|
]);
|
|
},
|
|
replaceAt: function (index, ref) {
|
|
return ref;
|
|
},
|
|
forEachColumnIndex: function (callback) {
|
|
this.forEachAxisIndex('col', callback);
|
|
},
|
|
forEachRowIndex: function (callback) {
|
|
this.forEachAxisIndex('row', callback);
|
|
},
|
|
forEachAxisIndex: function (axis, callback) {
|
|
var sorted = [];
|
|
var method = axis === 'row' ? 'forEachRow' : 'forEachColumn';
|
|
this[method](function (ref) {
|
|
var index = ref.first()[axis];
|
|
if (sorted.indexOf(index) === -1) {
|
|
sorted.push(index);
|
|
}
|
|
});
|
|
sorted.sort(function (a, b) {
|
|
return a > b ? 1 : a < b ? -1 : 0;
|
|
}).forEach(callback);
|
|
},
|
|
valid: function () {
|
|
return false;
|
|
}
|
|
});
|
|
Ref.display = displayRef;
|
|
var NULL = new (Ref.extend({
|
|
init: function NullRef() {
|
|
},
|
|
print: function () {
|
|
return '#NULL!';
|
|
},
|
|
clone: function () {
|
|
return this;
|
|
},
|
|
eq: function (ref) {
|
|
return ref === this;
|
|
},
|
|
forEach: function () {
|
|
}
|
|
}))();
|
|
var NameRef = Ref.extend({
|
|
ref: 'name',
|
|
init: function NameRef(name) {
|
|
this.name = name;
|
|
},
|
|
print: function () {
|
|
var ret = displaySheet(this.name);
|
|
if (this.hasSheet()) {
|
|
ret = displaySheet(this.sheet) + '!' + ret;
|
|
}
|
|
return ret;
|
|
}
|
|
});
|
|
var CellRef = Ref.extend({
|
|
ref: 'cell',
|
|
init: function CellRef(row, col, rel) {
|
|
this.row = row;
|
|
this.col = col;
|
|
this.rel = rel || 0;
|
|
},
|
|
clone: function () {
|
|
return new CellRef(this.row, this.col, this.rel).setSheet(this.sheet, this.hasSheet());
|
|
},
|
|
intersect: function (ref) {
|
|
if (ref instanceof CellRef) {
|
|
if (this.eq(ref)) {
|
|
return this;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
return ref.intersect(this);
|
|
},
|
|
print: function (trow, tcol) {
|
|
var col = this.col, row = this.row, rel = this.rel, abs;
|
|
if (trow == null) {
|
|
var sheet = this.hasSheet() ? displaySheet(this.sheet) + '!' : '';
|
|
if (isFinite(col)) {
|
|
col = rel & 1 ? 'C[' + col + ']' : 'C' + (col + 1);
|
|
} else {
|
|
col = '';
|
|
}
|
|
if (isFinite(row)) {
|
|
row = rel & 2 ? 'R[' + row + ']' : 'R' + (row + 1);
|
|
} else {
|
|
row = '';
|
|
}
|
|
return sheet + row + col;
|
|
} else {
|
|
abs = this.absolute(trow, tcol);
|
|
return abs.valid() ? displayRef(this._hasSheet && this.sheet, abs.row, abs.col, rel) : '#REF!';
|
|
}
|
|
},
|
|
absolute: function (arow, acol) {
|
|
var ret = this.clone();
|
|
if (ret.rel & 3 === 0) {
|
|
return ret;
|
|
}
|
|
if (ret.rel & 1) {
|
|
ret.col += acol;
|
|
}
|
|
if (ret.rel & 2) {
|
|
ret.row += arow;
|
|
}
|
|
ret.rel = 0;
|
|
return ret;
|
|
},
|
|
toRangeRef: function () {
|
|
return new RangeRef(this, this);
|
|
},
|
|
relative: function (arow, acol, rel) {
|
|
if (rel == null) {
|
|
rel = this.rel;
|
|
}
|
|
var row = rel & 2 ? this.row - arow : this.row;
|
|
var col = rel & 1 ? this.col - acol : this.col;
|
|
return new CellRef(row, col, rel).setSheet(this.sheet, this.hasSheet());
|
|
},
|
|
height: function () {
|
|
return 1;
|
|
},
|
|
width: function () {
|
|
return 1;
|
|
},
|
|
toString: function () {
|
|
return displayRef(null, this.row, this.col, 3);
|
|
},
|
|
isCell: function () {
|
|
return true;
|
|
},
|
|
leftColumn: function () {
|
|
return this;
|
|
},
|
|
rightColumn: function () {
|
|
return this;
|
|
},
|
|
topRow: function () {
|
|
return this;
|
|
},
|
|
bottomRow: function () {
|
|
return this;
|
|
},
|
|
forEachRow: function (callback) {
|
|
callback(this.toRangeRef());
|
|
},
|
|
forEachColumn: function (callback) {
|
|
callback(this.toRangeRef());
|
|
},
|
|
adjust: function (row, col, trow, tcol, forRow, start, delta) {
|
|
var ref = this.absolute(row, col);
|
|
if (forRow) {
|
|
if (ref.row >= start) {
|
|
if (delta < 0 && ref.row < start - delta) {
|
|
return NULL;
|
|
}
|
|
ref.row += delta;
|
|
}
|
|
} else {
|
|
if (ref.col >= start) {
|
|
if (delta < 0 && ref.col < start - delta) {
|
|
return NULL;
|
|
}
|
|
ref.col += delta;
|
|
}
|
|
}
|
|
if (trow != null && tcol != null) {
|
|
ref = ref.relative(trow, tcol, this.rel);
|
|
}
|
|
return ref;
|
|
},
|
|
valid: function () {
|
|
if (this.rel) {
|
|
throw new Error('valid() called on relative reference');
|
|
}
|
|
var col = this.col, row = this.row;
|
|
return !(isFinite(col) && col < 0 || isFinite(row) && row < 0);
|
|
}
|
|
});
|
|
var RangeRef = Ref.extend({
|
|
ref: 'range',
|
|
init: function RangeRef(tl, br) {
|
|
if (tl._hasSheet && br._hasSheet && tl.sheet.toLowerCase() != br.sheet.toLowerCase()) {
|
|
this.endSheet = br.sheet;
|
|
}
|
|
this.topLeft = new CellRef(tl.row, tl.col, tl.rel);
|
|
this.bottomRight = new CellRef(br.row, br.col, br.rel);
|
|
this.normalize();
|
|
},
|
|
clone: function () {
|
|
return new RangeRef(this.topLeft.clone(), this.bottomRight.clone()).setSheet(this.sheet, this.hasSheet());
|
|
},
|
|
_containsRange: function (range) {
|
|
return this._containsCell(range.topLeft) && this._containsCell(range.bottomRight);
|
|
},
|
|
_containsCell: function (cell) {
|
|
return cell.sheet == this.sheet && cell.row >= this.topLeft.row && cell.col >= this.topLeft.col && cell.row <= this.bottomRight.row && cell.col <= this.bottomRight.col;
|
|
},
|
|
contains: function (ref) {
|
|
if (ref instanceof Array) {
|
|
var that = this;
|
|
return ref.some(function (_ref) {
|
|
return that.contains(_ref);
|
|
});
|
|
}
|
|
if (ref instanceof CellRef) {
|
|
return this._containsCell(ref);
|
|
}
|
|
if (ref instanceof RangeRef) {
|
|
return this._containsRange(ref);
|
|
}
|
|
return false;
|
|
},
|
|
_intersectRange: function (ref) {
|
|
if (this.sheet != ref.sheet) {
|
|
return NULL;
|
|
}
|
|
var a_left = this.topLeft.col;
|
|
var a_top = this.topLeft.row;
|
|
var a_right = this.bottomRight.col;
|
|
var a_bottom = this.bottomRight.row;
|
|
var b_left = ref.topLeft.col;
|
|
var b_top = ref.topLeft.row;
|
|
var b_right = ref.bottomRight.col;
|
|
var b_bottom = ref.bottomRight.row;
|
|
if (a_left <= b_right && b_left <= a_right && a_top <= b_bottom && b_top <= a_bottom) {
|
|
return new RangeRef(new CellRef(Math.max(a_top, b_top), Math.max(a_left, b_left)), new CellRef(Math.min(a_bottom, b_bottom), Math.min(a_right, b_right))).setSheet(this.sheet, this.hasSheet());
|
|
} else {
|
|
return NULL;
|
|
}
|
|
},
|
|
intersect: function (ref) {
|
|
if (ref === NULL) {
|
|
return ref;
|
|
}
|
|
if (ref instanceof CellRef) {
|
|
return this._containsCell(ref) ? ref : NULL;
|
|
}
|
|
if (ref instanceof RangeRef) {
|
|
return this._intersectRange(ref).simplify();
|
|
}
|
|
if (ref instanceof UnionRef) {
|
|
return ref.intersect(this);
|
|
}
|
|
throw new Error('Unknown reference');
|
|
},
|
|
simplify: function () {
|
|
if (this.isCell()) {
|
|
return new CellRef(this.topLeft.row, this.topLeft.col, this.topLeft.rel).setSheet(this.sheet, this.hasSheet());
|
|
}
|
|
return this;
|
|
},
|
|
normalize: function () {
|
|
var a = this.topLeft, b = this.bottomRight;
|
|
var r1 = a.row, c1 = a.col, r2 = b.row, c2 = b.col;
|
|
var rr1 = a.rel & 2, rc1 = a.rel & 1;
|
|
var rr2 = b.rel & 2, rc2 = b.rel & 1;
|
|
var tmp, changes = false;
|
|
if (r1 > r2) {
|
|
changes = true;
|
|
tmp = r1;
|
|
r1 = r2;
|
|
r2 = tmp;
|
|
tmp = rr1;
|
|
rr1 = rr2;
|
|
rr2 = tmp;
|
|
}
|
|
if (c1 > c2) {
|
|
changes = true;
|
|
tmp = c1;
|
|
c1 = c2;
|
|
c2 = tmp;
|
|
tmp = rc1;
|
|
rc1 = rc2;
|
|
rc2 = tmp;
|
|
}
|
|
if (changes) {
|
|
this.topLeft = new CellRef(r1, c1, rc1 | rr1);
|
|
this.bottomRight = new CellRef(r2, c2, rc2 | rr2);
|
|
}
|
|
return this;
|
|
},
|
|
print: function (trow, tcol) {
|
|
var abs = this.absolute(trow, tcol);
|
|
if (abs.valid()) {
|
|
var ret = this.topLeft.print(trow, tcol) + ':' + this.bottomRight.print(trow, tcol);
|
|
if (this.hasSheet()) {
|
|
ret = displaySheet(this.sheet) + (this.endSheet ? ':' + displaySheet(this.endSheet) : '') + '!' + ret;
|
|
}
|
|
return ret;
|
|
}
|
|
return '#REF!';
|
|
},
|
|
absolute: function (arow, acol) {
|
|
return new RangeRef(this.topLeft.absolute(arow, acol), this.bottomRight.absolute(arow, acol)).setSheet(this.sheet, this.hasSheet());
|
|
},
|
|
relative: function (arow, acol, relTL, relBR) {
|
|
if (relBR == null) {
|
|
relBR = relTL;
|
|
}
|
|
return new RangeRef(this.topLeft.relative(arow, acol, relTL), this.bottomRight.relative(arow, acol, relBR)).setSheet(this.sheet, this.hasSheet());
|
|
},
|
|
height: function () {
|
|
if (this.topLeft.rel != this.bottomRight.rel) {
|
|
throw new Error('Mixed relative/absolute references');
|
|
}
|
|
return this.bottomRight.row - this.topLeft.row + 1;
|
|
},
|
|
width: function () {
|
|
if (this.topLeft.rel != this.bottomRight.rel) {
|
|
throw new Error('Mixed relative/absolute references');
|
|
}
|
|
return this.bottomRight.col - this.topLeft.col + 1;
|
|
},
|
|
collapse: function () {
|
|
return this.topLeft.toRangeRef();
|
|
},
|
|
leftColumn: function () {
|
|
return new RangeRef(this.topLeft, new CellRef(this.bottomRight.row, this.topLeft.col));
|
|
},
|
|
rightColumn: function () {
|
|
return new RangeRef(new CellRef(this.topLeft.row, this.bottomRight.col), this.bottomRight);
|
|
},
|
|
topRow: function () {
|
|
return new RangeRef(this.topLeft, new CellRef(this.topLeft.row, this.bottomRight.col));
|
|
},
|
|
bottomRow: function () {
|
|
return new RangeRef(new CellRef(this.bottomRight.row, this.topLeft.col), this.bottomRight);
|
|
},
|
|
toRangeRef: function () {
|
|
return this;
|
|
},
|
|
toRow: function (row) {
|
|
return new RangeRef(new CellRef(this.topLeft.row + row, this.topLeft.col), new CellRef(this.topLeft.row + row, this.bottomRight.col));
|
|
},
|
|
toColumn: function (col) {
|
|
return new RangeRef(new CellRef(this.topLeft.row, this.topLeft.col + col), new CellRef(this.bottomRight.row, this.topLeft.col + col));
|
|
},
|
|
forEachRow: function (callback) {
|
|
var startRow = this.topLeft.row;
|
|
var endRow = this.bottomRight.row;
|
|
var startCol = this.topLeft.col;
|
|
var endCol = this.bottomRight.col;
|
|
for (var i = startRow; i <= endRow; i++) {
|
|
callback(new RangeRef(new CellRef(i, startCol), new CellRef(i, endCol)));
|
|
}
|
|
},
|
|
forEachColumn: function (callback) {
|
|
var startRow = this.topLeft.row;
|
|
var endRow = this.bottomRight.row;
|
|
var startCol = this.topLeft.col;
|
|
var endCol = this.bottomRight.col;
|
|
for (var i = startCol; i <= endCol; i++) {
|
|
callback(new RangeRef(new CellRef(startRow, i), new CellRef(endRow, i)));
|
|
}
|
|
},
|
|
intersecting: function (refs) {
|
|
return refs.filter(function (ref) {
|
|
return ref.toRangeRef().intersects(this);
|
|
}, this);
|
|
},
|
|
union: function (refs, callback) {
|
|
var intersecting = this.intersecting(refs);
|
|
var topLeftRow = this.topLeft.row;
|
|
var topLeftCol = this.topLeft.col;
|
|
var bottomRightRow = this.bottomRight.row;
|
|
var bottomRightCol = this.bottomRight.col;
|
|
var modified = false;
|
|
intersecting.forEach(function (ref) {
|
|
ref = ref.toRangeRef();
|
|
if (ref.topLeft.row < topLeftRow) {
|
|
modified = true;
|
|
topLeftRow = ref.topLeft.row;
|
|
}
|
|
if (ref.topLeft.col < topLeftCol) {
|
|
modified = true;
|
|
topLeftCol = ref.topLeft.col;
|
|
}
|
|
if (ref.bottomRight.row > bottomRightRow) {
|
|
modified = true;
|
|
bottomRightRow = ref.bottomRight.row;
|
|
}
|
|
if (ref.bottomRight.col > bottomRightCol) {
|
|
modified = true;
|
|
bottomRightCol = ref.bottomRight.col;
|
|
}
|
|
if (callback) {
|
|
callback(ref);
|
|
}
|
|
});
|
|
var result = new RangeRef(new CellRef(topLeftRow, topLeftCol), new CellRef(bottomRightRow, bottomRightCol));
|
|
if (modified) {
|
|
return result.union(refs, callback);
|
|
} else {
|
|
return result;
|
|
}
|
|
},
|
|
resize: function (options) {
|
|
var limit = Math.max.bind(Math, 0);
|
|
function num(value) {
|
|
return value || 0;
|
|
}
|
|
var top = this.topLeft.row + num(options.top);
|
|
var left = this.topLeft.col + num(options.left);
|
|
var bottom = this.bottomRight.row + num(options.bottom);
|
|
var right = this.bottomRight.col + num(options.right);
|
|
if (left < 0 && right < 0 || top < 0 && bottom < 0) {
|
|
return NULL;
|
|
} else if (top <= bottom && left <= right) {
|
|
return new RangeRef(new CellRef(limit(top), limit(left)), new CellRef(limit(bottom), limit(right)));
|
|
} else {
|
|
return NULL;
|
|
}
|
|
},
|
|
move: function (rows, cols) {
|
|
return new RangeRef(new CellRef(this.topLeft.row + rows, this.topLeft.col + cols), new CellRef(this.bottomRight.row + rows, this.bottomRight.col + cols));
|
|
},
|
|
first: function () {
|
|
return this.topLeft;
|
|
},
|
|
isCell: function () {
|
|
return !this.endSheet && this.topLeft.eq(this.bottomRight);
|
|
},
|
|
toString: function () {
|
|
return this.topLeft + ':' + this.bottomRight;
|
|
},
|
|
adjust: function (row, col, trow, tcol, forRow, start, delta) {
|
|
var tl = this.topLeft.adjust(row, col, trow, tcol, forRow, start, delta);
|
|
var tr = this.bottomRight.adjust(row, col, trow, tcol, forRow, start, delta);
|
|
if (tl === NULL && tr === NULL) {
|
|
return NULL;
|
|
}
|
|
if (tl === NULL) {
|
|
tl = this.topLeft.absolute(row, col);
|
|
if (forRow) {
|
|
tl.row = start;
|
|
} else {
|
|
tl.col = start;
|
|
}
|
|
if (trow != null && tcol != null) {
|
|
tl = tl.relative(trow, tcol, this.topLeft.rel);
|
|
}
|
|
} else if (tr === NULL) {
|
|
tr = this.bottomRight.absolute(row, col);
|
|
if (forRow) {
|
|
tr.row = start - 1;
|
|
} else {
|
|
tr.col = start - 1;
|
|
}
|
|
if (trow != null && tcol != null) {
|
|
tr = tr.relative(trow, tcol, this.bottomRight.rel);
|
|
}
|
|
}
|
|
return new RangeRef(tl, tr).setSheet(this.sheet, this.hasSheet()).simplify();
|
|
},
|
|
valid: function () {
|
|
return this.topLeft.valid() && this.bottomRight.valid();
|
|
}
|
|
});
|
|
var UnionRef = Ref.extend({
|
|
init: function UnionRef(refs) {
|
|
this.refs = refs;
|
|
this.length = this.refs.length;
|
|
},
|
|
intersect: function (ref) {
|
|
var a = [];
|
|
for (var i = 0; i < this.length; ++i) {
|
|
var x = ref.intersect(this.refs[i]);
|
|
if (x !== NULL) {
|
|
a.push(x);
|
|
}
|
|
}
|
|
if (a.length > 0) {
|
|
return new UnionRef(a).simplify();
|
|
}
|
|
return NULL;
|
|
},
|
|
simplify: function () {
|
|
var u = new UnionRef(this.refs.reduce(function (a, ref) {
|
|
ref = ref.simplify();
|
|
if (ref !== NULL) {
|
|
a.push(ref);
|
|
}
|
|
return a;
|
|
}, []));
|
|
if (u.empty()) {
|
|
return NULL;
|
|
}
|
|
if (u.single()) {
|
|
return u.refs[0];
|
|
}
|
|
return u;
|
|
},
|
|
absolute: function (arow, acol) {
|
|
return new UnionRef(this.refs.map(function (ref) {
|
|
return ref.absolute(arow, acol);
|
|
}));
|
|
},
|
|
forEach: function (callback, obj) {
|
|
this.refs.forEach(callback, obj);
|
|
},
|
|
toRangeRef: function () {
|
|
return this.refs[0].toRangeRef();
|
|
},
|
|
contains: function (theRef) {
|
|
return this.refs.some(function (ref) {
|
|
return ref.contains(theRef);
|
|
});
|
|
},
|
|
map: function (callback, obj) {
|
|
return new UnionRef(this.refs.map(callback, obj));
|
|
},
|
|
first: function () {
|
|
return this.refs[0].first();
|
|
},
|
|
lastRange: function () {
|
|
return this.refs[this.length - 1];
|
|
},
|
|
size: function () {
|
|
return this.length;
|
|
},
|
|
single: function () {
|
|
return this.length == 1;
|
|
},
|
|
empty: function () {
|
|
return this.length === 0;
|
|
},
|
|
isCell: function () {
|
|
return this.single() && this.refs[0].isCell();
|
|
},
|
|
rangeAt: function (index) {
|
|
return this.refs[index];
|
|
},
|
|
nextRangeIndex: function (index) {
|
|
if (index === this.length - 1) {
|
|
return 0;
|
|
} else {
|
|
return index + 1;
|
|
}
|
|
},
|
|
previousRangeIndex: function (index) {
|
|
if (index === 0) {
|
|
return this.length - 1;
|
|
} else {
|
|
return index - 1;
|
|
}
|
|
},
|
|
concat: function (ref) {
|
|
return new UnionRef(this.refs.concat([ref]));
|
|
},
|
|
print: function (row, col) {
|
|
return this.refs.map(function (ref) {
|
|
return ref.print(row, col);
|
|
}).join(',');
|
|
},
|
|
replaceAt: function (index, ref) {
|
|
var newRefs = this.refs.slice();
|
|
newRefs.splice(index, 1, ref);
|
|
return new UnionRef(newRefs);
|
|
},
|
|
leftColumn: function () {
|
|
return this.map(function (ref) {
|
|
return ref.leftColumn();
|
|
});
|
|
},
|
|
rightColumn: function () {
|
|
return this.map(function (ref) {
|
|
return ref.rightColumn();
|
|
});
|
|
},
|
|
topRow: function () {
|
|
return this.map(function (ref) {
|
|
return ref.topRow();
|
|
});
|
|
},
|
|
bottomRow: function () {
|
|
return this.map(function (ref) {
|
|
return ref.bottomRow();
|
|
});
|
|
},
|
|
forEachRow: function (callback) {
|
|
this.forEach(function (ref) {
|
|
ref.forEachRow(callback);
|
|
});
|
|
},
|
|
forEachColumn: function (callback) {
|
|
this.forEach(function (ref) {
|
|
ref.forEachColumn(callback);
|
|
});
|
|
},
|
|
adjust: function (row, col, trow, tcol, forRow, start, delta) {
|
|
return this.map(function (ref) {
|
|
return ref.adjust(row, col, trow, tcol, forRow, start, delta);
|
|
}).simplify();
|
|
},
|
|
toString: function () {
|
|
return this.refs.map(function (ref) {
|
|
return ref.toString();
|
|
}).join(', ');
|
|
},
|
|
valid: function () {
|
|
for (var i = this.refs.length; --i >= 0;) {
|
|
if (this.refs[i].valid()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
spreadsheet.NULLREF = NULL;
|
|
spreadsheet.SHEETREF = new RangeRef(new CellRef(0, 0), new CellRef(Infinity, Infinity));
|
|
spreadsheet.FIRSTREF = new CellRef(0, 0);
|
|
spreadsheet.Ref = Ref;
|
|
spreadsheet.NameRef = NameRef;
|
|
spreadsheet.CellRef = CellRef;
|
|
spreadsheet.RangeRef = RangeRef;
|
|
spreadsheet.UnionRef = UnionRef;
|
|
spreadsheet.SHEETREF.print = function () {
|
|
return '#SHEET';
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/autofillcalculator', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var RangeRef = kendo.spreadsheet.RangeRef;
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var AutoFillCalculator = kendo.Class.extend({
|
|
init: function (grid) {
|
|
this._grid = grid;
|
|
},
|
|
rectIsVertical: function (start, end, x, y) {
|
|
var startRect = this._grid.rectangle(start.toRangeRef());
|
|
var endRect = this._grid.rectangle(end.toRangeRef());
|
|
return Math.abs(endRect[y] - startRect[y]) > Math.abs(startRect[x] - endRect[x]);
|
|
},
|
|
autoFillDest: function (selection, cursor) {
|
|
var topLeft = selection.topLeft;
|
|
var bottomRight = selection.bottomRight;
|
|
var quadrant;
|
|
var lower = cursor.row >= topLeft.row;
|
|
var further = cursor.col >= topLeft.col;
|
|
if (lower) {
|
|
quadrant = further ? 4 : 3;
|
|
} else {
|
|
quadrant = further ? 2 : 1;
|
|
}
|
|
var pivot, opposite, cornerResult, expanding;
|
|
if (quadrant === 4) {
|
|
pivot = topLeft;
|
|
opposite = bottomRight;
|
|
expanding = cursor.row > opposite.row || cursor.col > opposite.col;
|
|
if (expanding) {
|
|
cursor = new CellRef(Math.max(cursor.row, opposite.row), Math.max(cursor.col, opposite.col));
|
|
}
|
|
if (this.rectIsVertical(opposite, cursor, 'right', 'bottom')) {
|
|
cornerResult = new CellRef(cursor.row, opposite.col);
|
|
} else {
|
|
cornerResult = new CellRef(opposite.row, cursor.col);
|
|
}
|
|
} else if (quadrant === 3) {
|
|
var bottomLeft = new CellRef(topLeft.col, bottomRight.row);
|
|
if (cursor.row > bottomRight.row && this.rectIsVertical(bottomLeft, cursor, 'left', 'bottom')) {
|
|
pivot = topLeft;
|
|
cornerResult = new CellRef(cursor.row, bottomRight.col);
|
|
} else {
|
|
pivot = bottomRight;
|
|
cornerResult = new CellRef(topLeft.row, cursor.col);
|
|
}
|
|
} else if (quadrant === 2) {
|
|
var topRight = new CellRef(topLeft.row, bottomRight.col);
|
|
if (cursor.col > bottomRight.col && !this.rectIsVertical(topRight, cursor, 'right', 'top')) {
|
|
pivot = topLeft;
|
|
cornerResult = new CellRef(bottomRight.row, cursor.col);
|
|
} else {
|
|
pivot = bottomRight;
|
|
cornerResult = new CellRef(cursor.row, topLeft.col);
|
|
}
|
|
} else {
|
|
pivot = bottomRight;
|
|
if (this.rectIsVertical(topLeft, cursor, 'left', 'top')) {
|
|
cornerResult = new CellRef(cursor.row, topLeft.col);
|
|
} else {
|
|
cornerResult = new CellRef(topLeft.row, cursor.col);
|
|
}
|
|
}
|
|
return this._grid.normalize(new RangeRef(pivot, cornerResult));
|
|
}
|
|
});
|
|
kendo.spreadsheet.AutoFillCalculator = AutoFillCalculator;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/navigator', [
|
|
'kendo.core',
|
|
'spreadsheet/autofillcalculator'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var RangeRef = kendo.spreadsheet.RangeRef;
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var EdgeNavigator = kendo.Class.extend({
|
|
init: function (field, axis, rangeGetter, union) {
|
|
this.rangeGetter = rangeGetter;
|
|
this.prevLeft = function (index) {
|
|
var current = union(this.range(index));
|
|
var range = this.range(axis.prevVisible(current.topLeft[field]));
|
|
return union(range).topLeft[field];
|
|
};
|
|
this.nextRight = function (index) {
|
|
var current = union(this.range(index));
|
|
var range = this.range(axis.nextVisible(current.bottomRight[field]));
|
|
return union(range).bottomRight[field];
|
|
};
|
|
this.nextLeft = function (index) {
|
|
var range = union(this.range(index));
|
|
return axis.nextVisible(range.bottomRight[field]);
|
|
};
|
|
this.prevRight = function (index) {
|
|
var range = union(this.range(index));
|
|
return axis.prevVisible(range.topLeft[field]);
|
|
};
|
|
},
|
|
boundary: function (top, bottom) {
|
|
this.top = top;
|
|
this.bottom = bottom;
|
|
},
|
|
range: function (index) {
|
|
return this.rangeGetter(index, this.top, this.bottom);
|
|
}
|
|
});
|
|
var SheetNavigator = kendo.Class.extend({
|
|
init: function (sheet) {
|
|
this._sheet = sheet;
|
|
this.columns = this._sheet._grid._columns;
|
|
this.autoFillCalculator = new kendo.spreadsheet.AutoFillCalculator(sheet._grid);
|
|
this.colEdge = new EdgeNavigator('col', this._sheet._grid._columns, this.columnRange.bind(this), this.union.bind(this));
|
|
this.rowEdge = new EdgeNavigator('row', this._sheet._grid._rows, this.rowRange.bind(this), this.union.bind(this));
|
|
},
|
|
height: function (height) {
|
|
this._viewPortHeight = height;
|
|
},
|
|
union: function (ref) {
|
|
return this._sheet.unionWithMerged(ref);
|
|
},
|
|
columnRange: function (col, topRow, bottomRow) {
|
|
return this._sheet._ref(topRow, col, bottomRow - topRow, 1);
|
|
},
|
|
rowRange: function (row, leftCol, rightCol) {
|
|
return this._sheet._ref(row, leftCol, 1, rightCol - leftCol);
|
|
},
|
|
selectionIncludesMergedCells: function () {
|
|
return this._sheet.select().contains(this._sheet._mergedCells);
|
|
},
|
|
setSelectionValue: function (value) {
|
|
var selection = this._sheet.selection();
|
|
setTimeout(function () {
|
|
selection.value(value());
|
|
});
|
|
},
|
|
selectAll: function () {
|
|
this._sheet.select(this._sheet._sheetRef);
|
|
},
|
|
select: function (ref, mode, addToExisting) {
|
|
ref = this.refForMode(ref, mode);
|
|
if (addToExisting) {
|
|
ref = this._sheet.select().concat(ref);
|
|
}
|
|
this._sheet.select(ref);
|
|
},
|
|
refForMode: function (ref, mode) {
|
|
var grid = this._sheet._grid;
|
|
switch (mode) {
|
|
case 'range':
|
|
ref = grid.normalize(ref);
|
|
break;
|
|
case 'row':
|
|
ref = grid.rowRef(ref.row);
|
|
break;
|
|
case 'column':
|
|
ref = grid.colRef(ref.col);
|
|
break;
|
|
case 'sheet':
|
|
ref = this._sheet._sheetRef;
|
|
break;
|
|
}
|
|
return ref;
|
|
},
|
|
startSelection: function (ref, mode, addToExisting) {
|
|
if (mode == 'autofill') {
|
|
this._sheet.startAutoFill();
|
|
} else {
|
|
this._sheet.startSelection();
|
|
this.select(ref, mode, addToExisting);
|
|
}
|
|
},
|
|
completeSelection: function () {
|
|
this._sheet.completeSelection();
|
|
},
|
|
selectForContextMenu: function (ref, mode) {
|
|
var sheet = this._sheet;
|
|
if (!sheet.select().contains(this.refForMode(ref, mode))) {
|
|
this.select(ref, mode);
|
|
}
|
|
},
|
|
modifySelection: function (action) {
|
|
var direction = this.determineDirection(action);
|
|
var sheet = this._sheet;
|
|
var viewPortHeight = this._viewPortHeight;
|
|
var rows = sheet._grid._rows;
|
|
var columns = sheet._grid._columns;
|
|
var originalSelection = sheet.currentOriginalSelectionRange();
|
|
var selection = sheet.select().toRangeRef();
|
|
var activeCell = sheet.activeCell();
|
|
var topLeft = originalSelection.topLeft.clone();
|
|
var bottomRight = originalSelection.bottomRight.clone();
|
|
var scrollInto;
|
|
this.colEdge.boundary(selection.topLeft.row, selection.bottomRight.row);
|
|
this.rowEdge.boundary(selection.topLeft.col, selection.bottomRight.col);
|
|
switch (direction) {
|
|
case 'expand-left':
|
|
topLeft.col = this.colEdge.prevLeft(topLeft.col);
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'shrink-right':
|
|
topLeft.col = this.colEdge.nextLeft(topLeft.col);
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'expand-right':
|
|
bottomRight.col = this.colEdge.nextRight(bottomRight.col);
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'shrink-left':
|
|
bottomRight.col = this.colEdge.prevRight(bottomRight.col);
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'expand-up':
|
|
topLeft.row = this.rowEdge.prevLeft(topLeft.row);
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'shrink-down':
|
|
topLeft.row = this.rowEdge.nextLeft(topLeft.row);
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'expand-down':
|
|
bottomRight.row = this.rowEdge.nextRight(bottomRight.row);
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'shrink-up':
|
|
bottomRight.row = this.rowEdge.prevRight(bottomRight.row);
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'expand-page-up':
|
|
topLeft.row = rows.prevPage(topLeft.row, viewPortHeight);
|
|
break;
|
|
case 'shrink-page-up':
|
|
bottomRight.row = rows.prevPage(bottomRight.row, viewPortHeight);
|
|
break;
|
|
case 'expand-page-down':
|
|
bottomRight.row = rows.nextPage(bottomRight.row, viewPortHeight);
|
|
break;
|
|
case 'shrink-page-down':
|
|
topLeft.row = rows.nextPage(topLeft.row, viewPortHeight);
|
|
break;
|
|
case 'first-col':
|
|
topLeft.col = columns.firstVisible();
|
|
bottomRight.col = activeCell.bottomRight.col;
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'last-col':
|
|
bottomRight.col = columns.lastVisible();
|
|
topLeft.col = activeCell.topLeft.col;
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'first-row':
|
|
topLeft.row = rows.firstVisible();
|
|
bottomRight.row = activeCell.bottomRight.row;
|
|
scrollInto = topLeft;
|
|
break;
|
|
case 'last-row':
|
|
bottomRight.row = rows.lastVisible();
|
|
topLeft.row = activeCell.topLeft.row;
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'last':
|
|
bottomRight.row = rows.lastVisible();
|
|
bottomRight.col = columns.lastVisible();
|
|
topLeft = activeCell.topLeft;
|
|
scrollInto = bottomRight;
|
|
break;
|
|
case 'first':
|
|
topLeft.row = rows.firstVisible();
|
|
topLeft.col = columns.firstVisible();
|
|
bottomRight = activeCell.bottomRight;
|
|
scrollInto = topLeft;
|
|
break;
|
|
}
|
|
var newSelection = new RangeRef(topLeft, bottomRight);
|
|
if (!this.union(newSelection).intersects(activeCell)) {
|
|
this.modifySelection(direction.replace('shrink', 'expand'));
|
|
return;
|
|
}
|
|
if (scrollInto) {
|
|
sheet.focus(scrollInto);
|
|
}
|
|
this.updateCurrentSelectionRange(newSelection);
|
|
},
|
|
moveActiveCell: function (direction) {
|
|
var sheet = this._sheet;
|
|
var activeCell = sheet.activeCell();
|
|
var topLeft = activeCell.topLeft;
|
|
var bottomRight = activeCell.bottomRight;
|
|
var cell = sheet.originalActiveCell();
|
|
var rows = sheet._grid._rows;
|
|
var columns = sheet._grid._columns;
|
|
var row = cell.row;
|
|
var column = cell.col;
|
|
switch (direction) {
|
|
case 'left':
|
|
column = columns.prevVisible(topLeft.col);
|
|
break;
|
|
case 'up':
|
|
row = rows.prevVisible(topLeft.row);
|
|
break;
|
|
case 'right':
|
|
column = columns.nextVisible(bottomRight.col);
|
|
break;
|
|
case 'down':
|
|
row = rows.nextVisible(bottomRight.row);
|
|
break;
|
|
case 'first-col':
|
|
column = columns.firstVisible();
|
|
break;
|
|
case 'last-col':
|
|
column = columns.lastVisible();
|
|
break;
|
|
case 'first-row':
|
|
row = rows.firstVisible();
|
|
break;
|
|
case 'last-row':
|
|
row = rows.lastVisible();
|
|
break;
|
|
case 'last':
|
|
row = rows.lastVisible();
|
|
column = columns.lastVisible();
|
|
break;
|
|
case 'first':
|
|
row = rows.firstVisible();
|
|
column = columns.firstVisible();
|
|
break;
|
|
case 'next-page':
|
|
row = rows.nextPage(bottomRight.row, this._viewPortHeight);
|
|
break;
|
|
case 'prev-page':
|
|
row = rows.prevPage(bottomRight.row, this._viewPortHeight);
|
|
break;
|
|
}
|
|
sheet.select(new CellRef(row, column));
|
|
},
|
|
navigateInSelection: function (direction) {
|
|
var sheet = this._sheet;
|
|
var activeCell = sheet.activeCell();
|
|
var topLeft = activeCell.topLeft;
|
|
var cell = sheet.originalActiveCell();
|
|
var rows = sheet._grid._rows;
|
|
var columns = sheet._grid._columns;
|
|
var row = cell.row;
|
|
var column = cell.col;
|
|
var selection = sheet.currentNavigationRange();
|
|
var selTopLeft = selection.topLeft;
|
|
var selBottomRight = selection.bottomRight;
|
|
var done = false;
|
|
var topLeftCol = topLeft.col;
|
|
var topLeftRow = topLeft.row;
|
|
while (!done) {
|
|
var current = new CellRef(row, column);
|
|
switch (direction) {
|
|
case 'next':
|
|
if (selBottomRight.eq(current)) {
|
|
selection = sheet.nextNavigationRange();
|
|
row = selection.topLeft.row;
|
|
column = selection.topLeft.col;
|
|
} else {
|
|
column = columns.nextVisible(topLeftCol, true);
|
|
if (column > selBottomRight.col) {
|
|
column = selTopLeft.col;
|
|
row = rows.nextVisible(row, true);
|
|
}
|
|
}
|
|
break;
|
|
case 'previous':
|
|
if (selTopLeft.eq(current)) {
|
|
selection = sheet.previousNavigationRange();
|
|
row = selection.bottomRight.row;
|
|
column = selection.bottomRight.col;
|
|
} else {
|
|
column = columns.prevVisible(topLeftCol, true);
|
|
if (column < selTopLeft.col) {
|
|
column = selBottomRight.col;
|
|
row = rows.prevVisible(row, true);
|
|
}
|
|
}
|
|
break;
|
|
case 'lower':
|
|
if (selBottomRight.eq(current)) {
|
|
selection = sheet.nextNavigationRange();
|
|
row = selection.topLeft.row;
|
|
column = selection.topLeft.col;
|
|
} else {
|
|
row = rows.nextVisible(topLeftRow, true);
|
|
if (row > selBottomRight.row) {
|
|
row = selTopLeft.row;
|
|
column = columns.nextVisible(column, true);
|
|
}
|
|
}
|
|
break;
|
|
case 'upper':
|
|
if (selTopLeft.eq(current)) {
|
|
selection = sheet.previousNavigationRange();
|
|
row = selection.bottomRight.row;
|
|
column = selection.bottomRight.col;
|
|
} else {
|
|
row = rows.prevVisible(topLeftRow, true);
|
|
if (row < selTopLeft.row) {
|
|
row = selBottomRight.row;
|
|
column = columns.prevVisible(column, true);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error('Unknown entry navigation: ' + direction);
|
|
}
|
|
done = !this.shouldSkip(row, column);
|
|
topLeftCol = column;
|
|
topLeftRow = row;
|
|
}
|
|
if (sheet.singleCellSelection()) {
|
|
sheet.select(new CellRef(row, column));
|
|
} else {
|
|
sheet.activeCell(new CellRef(row, column));
|
|
}
|
|
},
|
|
extendSelection: function (ref, mode) {
|
|
var sheet = this._sheet;
|
|
var grid = sheet._grid;
|
|
if (mode === 'autofill') {
|
|
this.resizeAutoFill(ref);
|
|
return;
|
|
}
|
|
if (mode === 'range') {
|
|
ref = grid.normalize(ref);
|
|
} else if (mode === 'row') {
|
|
ref = grid.rowRef(ref.row).bottomRight;
|
|
} else if (mode === 'column') {
|
|
ref = grid.colRef(ref.col).bottomRight;
|
|
}
|
|
var activeCell = sheet.originalActiveCell().toRangeRef();
|
|
this.updateCurrentSelectionRange(new RangeRef(activeCell.topLeft, ref));
|
|
},
|
|
shouldSkip: function (row, col) {
|
|
var ref = new CellRef(row, col);
|
|
var isMerged = false;
|
|
this._sheet.forEachMergedCell(function (merged) {
|
|
if (merged.intersects(ref) && !merged.collapse().eq(ref)) {
|
|
isMerged = true;
|
|
}
|
|
});
|
|
return isMerged;
|
|
},
|
|
resizeAutoFill: function (ref) {
|
|
var sheet = this._sheet;
|
|
var selection = sheet.select();
|
|
var origin = sheet._autoFillOrigin;
|
|
var dest = this.autoFillCalculator.autoFillDest(selection, ref);
|
|
var punch = this.punch(selection, dest);
|
|
var hint, direction, row;
|
|
if (!punch) {
|
|
var preview = sheet.range(dest)._previewFillFrom(sheet.range(origin));
|
|
if (preview) {
|
|
direction = preview.direction;
|
|
var props = preview.props;
|
|
if (direction === 0 || direction == 1) {
|
|
row = props[props.length - 1];
|
|
hint = row[row.length - 1].value;
|
|
} else if (direction === 2) {
|
|
row = props[0];
|
|
hint = row[row.length - 1].value;
|
|
} else if (direction === 3) {
|
|
row = props[props.length - 1];
|
|
hint = row[0].value;
|
|
}
|
|
}
|
|
}
|
|
sheet.updateAutoFill(dest, punch, hint, direction);
|
|
},
|
|
determineDirection: function (action) {
|
|
var selection = this._sheet.currentSelectionRange();
|
|
var activeCell = this._sheet.activeCell();
|
|
var leftMode = activeCell.topLeft.col == selection.topLeft.col;
|
|
var rightMode = activeCell.bottomRight.col == selection.bottomRight.col;
|
|
var topMode = activeCell.topLeft.row == selection.topLeft.row;
|
|
var bottomMode = activeCell.bottomRight.row == selection.bottomRight.row;
|
|
switch (action) {
|
|
case 'left':
|
|
action = rightMode ? 'expand-left' : 'shrink-left';
|
|
break;
|
|
case 'right':
|
|
action = leftMode ? 'expand-right' : 'shrink-right';
|
|
break;
|
|
case 'up':
|
|
action = bottomMode ? 'expand-up' : 'shrink-up';
|
|
break;
|
|
case 'down':
|
|
action = topMode ? 'expand-down' : 'shrink-down';
|
|
break;
|
|
case 'prev-page':
|
|
action = bottomMode ? 'expand-page-up' : 'shrink-page-up';
|
|
break;
|
|
case 'next-page':
|
|
action = topMode ? 'expand-page-down' : 'shrink-page-down';
|
|
break;
|
|
}
|
|
return action;
|
|
},
|
|
updateCurrentSelectionRange: function (ref) {
|
|
var sheet = this._sheet;
|
|
sheet.select(sheet.originalSelect().replaceAt(sheet.selectionRangeIndex(), ref), false);
|
|
},
|
|
punch: function (selection, subset) {
|
|
var punch;
|
|
if (subset.topLeft.eq(selection.topLeft)) {
|
|
if (subset.bottomRight.row < selection.bottomRight.row) {
|
|
var bottomRow = this.rowEdge.nextRight(subset.bottomRight.row);
|
|
punch = new RangeRef(new CellRef(bottomRow, selection.topLeft.col), selection.bottomRight);
|
|
} else if (subset.bottomRight.col < selection.bottomRight.col) {
|
|
var bottomCol = this.colEdge.nextRight(subset.bottomRight.col);
|
|
punch = new RangeRef(new CellRef(selection.topLeft.row, bottomCol), selection.bottomRight);
|
|
}
|
|
}
|
|
return punch;
|
|
}
|
|
});
|
|
kendo.spreadsheet.SheetNavigator = SheetNavigator;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/axismanager', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var AxisManager = kendo.Class.extend({
|
|
init: function (sheet) {
|
|
this._sheet = sheet;
|
|
},
|
|
forEachSelectedColumn: function (callback) {
|
|
var sheet = this._sheet;
|
|
sheet.batch(function () {
|
|
sheet.select().forEachColumnIndex(function (index, i) {
|
|
callback(sheet, index, i);
|
|
});
|
|
}, {
|
|
layout: true,
|
|
recalc: true
|
|
});
|
|
},
|
|
forEachSelectedRow: function (callback) {
|
|
var sheet = this._sheet;
|
|
sheet.batch(function () {
|
|
sheet.select().forEachRowIndex(function (index, i) {
|
|
callback(sheet, index, i);
|
|
});
|
|
}, {
|
|
layout: true,
|
|
recalc: true
|
|
});
|
|
},
|
|
includesHiddenColumns: function (ref) {
|
|
return this._sheet._grid._columns.includesHidden(ref.topLeft.col, ref.bottomRight.col);
|
|
},
|
|
includesHiddenRows: function (ref) {
|
|
return this._sheet._grid._rows.includesHidden(ref.topLeft.row, ref.bottomRight.row);
|
|
},
|
|
selectionIncludesHiddenColumns: function () {
|
|
return this.includesHiddenColumns(this._sheet.select());
|
|
},
|
|
selectionIncludesHiddenRows: function () {
|
|
return this.includesHiddenRows(this._sheet.select());
|
|
},
|
|
deleteSelectedColumns: function () {
|
|
this.forEachSelectedColumn(function (sheet, index, i) {
|
|
sheet.deleteColumn(index - i);
|
|
});
|
|
},
|
|
deleteSelectedRows: function () {
|
|
this.forEachSelectedRow(function (sheet, index, i) {
|
|
sheet.deleteRow(index - i);
|
|
});
|
|
},
|
|
hideSelectedColumns: function () {
|
|
this.forEachSelectedColumn(function (sheet, index) {
|
|
sheet.hideColumn(index);
|
|
});
|
|
},
|
|
hideSelectedRows: function () {
|
|
this.forEachSelectedRow(function (sheet, index) {
|
|
sheet.hideRow(index);
|
|
});
|
|
},
|
|
unhideSelectedColumns: function () {
|
|
this.forEachSelectedColumn(function (sheet, index) {
|
|
sheet.unhideColumn(index);
|
|
});
|
|
},
|
|
unhideSelectedRows: function () {
|
|
this.forEachSelectedRow(function (sheet, index) {
|
|
sheet.unhideRow(index);
|
|
});
|
|
},
|
|
addColumnLeft: function () {
|
|
this.forEachSelectedColumn(function (sheet, index, i) {
|
|
sheet.insertColumn(index - i);
|
|
});
|
|
},
|
|
addColumnRight: function () {
|
|
this.forEachSelectedColumn(function (sheet, index, i) {
|
|
sheet.insertColumn(index + (i + 1));
|
|
});
|
|
},
|
|
canAddRow: function () {
|
|
var range = this._sheet.select().toRangeRef();
|
|
var rowCount = range.height();
|
|
return this._sheet.canInsertRow(0, rowCount);
|
|
},
|
|
addRowAbove: function () {
|
|
this.forEachSelectedRow(function (sheet, index, i) {
|
|
sheet.insertRow(index - i);
|
|
});
|
|
},
|
|
addRowBelow: function () {
|
|
this.forEachSelectedRow(function (sheet, index, i) {
|
|
sheet.insertRow(index + (i + 1));
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.AxisManager = AxisManager;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/clipboard', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var RangeRef = kendo.spreadsheet.RangeRef;
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var Clipboard = kendo.Class.extend({
|
|
init: function (workbook) {
|
|
this.workbook = workbook;
|
|
this.origin = kendo.spreadsheet.NULLREF;
|
|
this.iframe = document.createElement('iframe');
|
|
this.iframe.className = 'k-spreadsheet-clipboard-paste';
|
|
this.menuInvoked = true;
|
|
this._external = {};
|
|
this._uid = kendo.guid();
|
|
document.body.appendChild(this.iframe);
|
|
},
|
|
canCopy: function () {
|
|
var status = { canCopy: true };
|
|
var selection = this.workbook.activeSheet().select();
|
|
if (selection === kendo.spreadsheet.NULLREF) {
|
|
status.canCopy = false;
|
|
}
|
|
if (selection instanceof kendo.spreadsheet.UnionRef) {
|
|
status.canCopy = false;
|
|
status.multiSelection = true;
|
|
}
|
|
if (this.menuInvoked) {
|
|
status.canCopy = false;
|
|
status.menuInvoked = true;
|
|
}
|
|
return status;
|
|
},
|
|
canPaste: function () {
|
|
var sheet = this.workbook.activeSheet();
|
|
var ref = this.pasteRef();
|
|
var status = { canPaste: true };
|
|
if (ref === kendo.spreadsheet.NULLREF) {
|
|
var external = this._external.hasOwnProperty('html') || this._external.hasOwnProperty('plain');
|
|
status.pasteOnMerged = this.intersectsMerged();
|
|
status.canPaste = status.pasteOnMerged ? false : external;
|
|
return status;
|
|
}
|
|
if (!ref.eq(sheet.unionWithMerged(ref))) {
|
|
status.canPaste = false;
|
|
status.pasteOnMerged = true;
|
|
}
|
|
if (this.menuInvoked) {
|
|
status.canPaste = false;
|
|
status.menuInvoked = true;
|
|
}
|
|
if (ref.bottomRight.row >= sheet._rows._count || ref.bottomRight.col >= sheet._columns._count) {
|
|
status.canPaste = false;
|
|
status.overflow = true;
|
|
}
|
|
return status;
|
|
},
|
|
intersectsMerged: function () {
|
|
var sheet = this.workbook.activeSheet();
|
|
var state = this.parse(this._external);
|
|
this.origin = this.stateRangeRef(state);
|
|
var ref = this.pasteRef();
|
|
return !ref.eq(sheet.unionWithMerged(ref));
|
|
},
|
|
copy: function () {
|
|
var sheet = this.workbook.activeSheet();
|
|
this.origin = sheet.select();
|
|
this.contents = sheet.selection().getState();
|
|
},
|
|
cut: function () {
|
|
var sheet = this.workbook.activeSheet();
|
|
this.copy();
|
|
sheet.range(sheet.select()).clear();
|
|
},
|
|
pasteRef: function () {
|
|
var sheet = this.workbook.activeSheet();
|
|
var destination = sheet.activeCell().first();
|
|
var originActiveCell = this.origin.first();
|
|
var rowDelta = originActiveCell.row - destination.row;
|
|
var colDelta = originActiveCell.col - destination.col;
|
|
return this.origin.relative(rowDelta, colDelta, 3);
|
|
},
|
|
stateRangeRef: function (state) {
|
|
var rows = [];
|
|
var cols = [];
|
|
for (var key in state) {
|
|
if (key === 'mergedCells' || key === 'ref') {
|
|
continue;
|
|
}
|
|
var address = key.split(',');
|
|
rows.push(address[0]);
|
|
cols.push(address[1]);
|
|
}
|
|
var topLeft = new CellRef(Math.min.apply(null, rows), Math.min.apply(null, cols));
|
|
var bottomRight = new CellRef(Math.max.apply(null, rows), Math.max.apply(null, cols));
|
|
return new RangeRef(topLeft, bottomRight);
|
|
},
|
|
destroy: function () {
|
|
document.body.removeChild(this.iframe);
|
|
},
|
|
paste: function () {
|
|
var state = {};
|
|
var sheet = this.workbook.activeSheet();
|
|
if (this._isInternal()) {
|
|
state = this.contents;
|
|
} else {
|
|
state = this.parse(this._external);
|
|
this.origin = this.stateRangeRef(state);
|
|
}
|
|
var pasteRef = this.pasteRef();
|
|
sheet.range(pasteRef).clear().setState(state);
|
|
sheet.triggerChange({
|
|
recalc: true,
|
|
ref: pasteRef
|
|
});
|
|
},
|
|
external: function (data) {
|
|
if (data.html || data.plain) {
|
|
this._external = data;
|
|
} else {
|
|
return this._external;
|
|
}
|
|
},
|
|
parse: function (data) {
|
|
var state = {
|
|
ref: new CellRef(0, 0, 0),
|
|
mergedCells: []
|
|
};
|
|
if (data.html) {
|
|
var doc = this.iframe.contentWindow.document;
|
|
doc.open();
|
|
doc.write(data.html);
|
|
doc.close();
|
|
var table = $(doc).find('table:first');
|
|
if (table.length) {
|
|
state = this._parseHTML(table.find('tbody:first'));
|
|
} else {
|
|
if (!data.plain) {
|
|
var element = $(doc.body).find(':not(style)');
|
|
state['0,0'] = this._cellState(element.text());
|
|
} else {
|
|
state = this._parseTSV(data.plain);
|
|
}
|
|
}
|
|
} else {
|
|
state = this._parseTSV(data.plain);
|
|
}
|
|
return state;
|
|
},
|
|
_parseHTML: function (tbody) {
|
|
var that = this;
|
|
var state = {
|
|
ref: new CellRef(0, 0, 0),
|
|
mergedCells: []
|
|
};
|
|
tbody.find('tr').each(function (rowIndex, tr) {
|
|
$(tr).find('td').each(function (colIndex, td) {
|
|
var rowspan = parseInt($(td).attr('rowspan'), 10) - 1 || 0;
|
|
var colspan = parseInt($(td).attr('colspan'), 10) - 1 || 0;
|
|
var blankCell = '<td/>';
|
|
var ci;
|
|
if (rowspan) {
|
|
var endRow = rowIndex + rowspan;
|
|
for (var ri = rowIndex; ri <= endRow; ri++) {
|
|
var row = tbody.find('tr').eq(ri);
|
|
if (ri > rowIndex) {
|
|
blankCell = '<td class=\'rowspan\'></td>';
|
|
if (colIndex === 0) {
|
|
row.find('td').eq(colIndex).after(blankCell);
|
|
} else {
|
|
var last = Math.min(row.find('td').length, colIndex);
|
|
row.find('td').eq(last - 1).after(blankCell);
|
|
}
|
|
}
|
|
if (colspan) {
|
|
for (ci = colIndex; ci < colspan + colIndex; ci++) {
|
|
blankCell = '<td class=\'rowspan colspan\'></td>';
|
|
row.find('td').eq(ci).after(blankCell);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (colspan) {
|
|
for (ci = colIndex; ci < colspan + colIndex; ci++) {
|
|
blankCell = '<td class=\'colspan\'></td>';
|
|
$(tr).find('td').eq(ci).after(blankCell);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
tbody.find('tr').each(function (rowIndex, tr) {
|
|
$(tr).find('td').each(function (colIndex, td) {
|
|
var key = rowIndex + ',' + colIndex;
|
|
var rowspan = parseInt($(td).attr('rowspan'), 10) - 1 || 0;
|
|
var colspan = parseInt($(td).attr('colspan'), 10) - 1 || 0;
|
|
var cellState = that._cellState($(td));
|
|
state[key] = cellState;
|
|
if (rowspan || colspan) {
|
|
var startCol = String.fromCharCode(65 + colIndex);
|
|
var endCol = String.fromCharCode(65 + colIndex + colspan);
|
|
var address = startCol + (rowIndex + 1) + ':' + endCol + (rowIndex + 1 + rowspan);
|
|
state.mergedCells.push(address);
|
|
}
|
|
});
|
|
});
|
|
return state;
|
|
},
|
|
_parseTSV: function (data) {
|
|
var state = {
|
|
ref: new CellRef(0, 0, 0),
|
|
mergedCells: []
|
|
};
|
|
if (data.indexOf('\t') === -1 && data.indexOf('\n') == -1) {
|
|
state['0,0'] = { value: data };
|
|
} else {
|
|
var rows = data.split('\n');
|
|
for (var ri = 0; ri < rows.length; ri++) {
|
|
var cols = rows[ri].split('\t');
|
|
for (var ci = 0; ci < cols.length; ci++) {
|
|
state[ri + ',' + ci] = { value: cols[ci] };
|
|
}
|
|
}
|
|
}
|
|
return state;
|
|
},
|
|
_isInternal: function () {
|
|
if (this._external.html === undefined) {
|
|
return true;
|
|
}
|
|
var internalHTML = $('<div/>').html(this._external.html).find('table.kendo-clipboard-' + this._uid).length ? true : false;
|
|
var internalPlain = $('<div/>').html(this._external.plain).find('table.kendo-clipboard-' + this._uid).length ? true : false;
|
|
if (internalHTML || internalPlain) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
_cellState: function (element) {
|
|
var styles = window.getComputedStyle(element[0]);
|
|
var text = element.text();
|
|
var borders = this._borderObject(styles);
|
|
var state = {
|
|
value: text === '' ? null : text,
|
|
borderBottom: borders.borderBottom,
|
|
borderRight: borders.borderRight,
|
|
borderLeft: borders.borderLeft,
|
|
borderTop: borders.borderTop,
|
|
fontSize: parseInt(styles['font-size'], 10)
|
|
};
|
|
if (styles['background-color'] !== 'rgb(0, 0, 0)' && styles['background-color'] !== 'rgba(0, 0, 0, 0)') {
|
|
state.background = styles['background-color'];
|
|
}
|
|
if (styles.color !== 'rgb(0, 0, 0)' && styles.color !== 'rgba(0, 0, 0, 0)') {
|
|
state.color = styles.color;
|
|
}
|
|
if (styles['text-decoration'] == 'underline') {
|
|
state.underline = true;
|
|
}
|
|
if (styles['font-style'] == 'italic') {
|
|
state.italic = true;
|
|
}
|
|
if (styles['font-weight'] == 'bold') {
|
|
state.bold = true;
|
|
}
|
|
if (this._strippedStyle(styles['text-align']) !== 'right') {
|
|
state.textAlign = this._strippedStyle(styles['text-align']);
|
|
}
|
|
if (styles['vertical-align'] !== 'middle') {
|
|
state.verticalAlign = styles['vertical-align'];
|
|
}
|
|
if (styles['word-wrap'] !== 'normal') {
|
|
state.wrap = true;
|
|
}
|
|
return state;
|
|
},
|
|
_strippedStyle: function (style) {
|
|
var prefixes = [
|
|
'-ms-',
|
|
'-moz-',
|
|
'-webkit-'
|
|
];
|
|
prefixes.forEach(function (prefix) {
|
|
style = style.replace(prefix, '');
|
|
});
|
|
return style;
|
|
},
|
|
_borderObject: function (styles) {
|
|
var borderObject = {};
|
|
var borders = [
|
|
'borderBottom',
|
|
'borderRight',
|
|
'borderLeft',
|
|
'borderTop'
|
|
];
|
|
borders.forEach(function (key) {
|
|
if (styles[key + 'Style'] == 'none') {
|
|
borderObject[key] = null;
|
|
return;
|
|
}
|
|
borderObject[key] = {
|
|
size: 1,
|
|
color: styles[key + 'Color']
|
|
};
|
|
});
|
|
return borderObject;
|
|
}
|
|
});
|
|
kendo.spreadsheet.Clipboard = Clipboard;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/range', [
|
|
'kendo.core',
|
|
'util/text-metrics'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var UnionRef = kendo.spreadsheet.UnionRef;
|
|
var styles = [
|
|
'color',
|
|
'fontFamily',
|
|
'underline',
|
|
'italic',
|
|
'bold',
|
|
'textAlign',
|
|
'verticalAlign',
|
|
'background'
|
|
];
|
|
var borders = {
|
|
borderTop: {
|
|
complement: 'borderBottom',
|
|
direction: {
|
|
top: -1,
|
|
bottom: -1
|
|
}
|
|
},
|
|
borderLeft: {
|
|
complement: 'borderRight',
|
|
direction: {
|
|
left: -1,
|
|
right: -1
|
|
}
|
|
},
|
|
borderRight: {
|
|
complement: 'borderLeft',
|
|
direction: {
|
|
left: 1,
|
|
right: 1
|
|
}
|
|
},
|
|
borderBottom: {
|
|
complement: 'borderTop',
|
|
direction: {
|
|
top: 1,
|
|
bottom: 1
|
|
}
|
|
}
|
|
};
|
|
var Range = kendo.Class.extend({
|
|
init: function (ref, sheet) {
|
|
this._sheet = sheet;
|
|
this._ref = ref;
|
|
},
|
|
_normalize: function (ref) {
|
|
return this._sheet._grid.normalize(ref);
|
|
},
|
|
_set: function (name, value, noTrigger) {
|
|
var sheet = this._sheet;
|
|
this._ref.forEach(function (ref) {
|
|
sheet._set(ref.toRangeRef(), name, value);
|
|
});
|
|
if (!noTrigger) {
|
|
sheet.triggerChange({
|
|
recalc: name == 'formula' || name == 'value' || name == 'validation',
|
|
value: value,
|
|
ref: this._ref
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
_get: function (name) {
|
|
return this._sheet._get(this._ref.toRangeRef(), name);
|
|
},
|
|
_property: function (name, value) {
|
|
if (value === undefined) {
|
|
return this._get(name);
|
|
} else {
|
|
return this._set(name, value);
|
|
}
|
|
},
|
|
value: function (value) {
|
|
if (value !== undefined) {
|
|
this._set('formula', null, true);
|
|
}
|
|
return this._property('value', value);
|
|
},
|
|
resize: function (direction) {
|
|
var ref = this._resizedRef(direction);
|
|
return new Range(ref, this._sheet);
|
|
},
|
|
_resizedRef: function (direction) {
|
|
return this._ref.map(function (ref) {
|
|
return ref.toRangeRef().resize(direction);
|
|
});
|
|
},
|
|
_border: function (property, value) {
|
|
var result;
|
|
var complement = borders[property].complement;
|
|
var direction = borders[property].direction;
|
|
var sheet = this._sheet;
|
|
sheet.batch(function () {
|
|
result = this._property(property, value);
|
|
if (value !== undefined) {
|
|
this._resizedRef(direction).forEach(function (ref) {
|
|
if (ref !== kendo.spreadsheet.NULLREF) {
|
|
new Range(ref, sheet)._property(complement, null);
|
|
}
|
|
});
|
|
}
|
|
}.bind(this), {});
|
|
return result;
|
|
},
|
|
_collapsedBorder: function (property) {
|
|
var result = this._property(property);
|
|
var complement = borders[property].complement;
|
|
var direction = borders[property].direction;
|
|
this._resizedRef(direction).forEach(function (ref) {
|
|
if (!result && ref !== kendo.spreadsheet.NULLREF) {
|
|
var range = new Range(ref, this._sheet);
|
|
result = range._property(complement);
|
|
}
|
|
}.bind(this));
|
|
return result;
|
|
},
|
|
borderTop: function (value) {
|
|
return this._border('borderTop', value);
|
|
},
|
|
borderRight: function (value) {
|
|
return this._border('borderRight', value);
|
|
},
|
|
borderBottom: function (value) {
|
|
return this._border('borderBottom', value);
|
|
},
|
|
borderLeft: function (value) {
|
|
return this._border('borderLeft', value);
|
|
},
|
|
collapsedBorderTop: function () {
|
|
return this._collapsedBorder('borderTop');
|
|
},
|
|
collapsedBorderRight: function () {
|
|
return this._collapsedBorder('borderRight');
|
|
},
|
|
collapsedBorderBottom: function () {
|
|
return this._collapsedBorder('borderBottom');
|
|
},
|
|
collapsedBorderLeft: function () {
|
|
return this._collapsedBorder('borderLeft');
|
|
},
|
|
input: function (value) {
|
|
var existingFormat = this._get('format'), x;
|
|
if (value !== undefined) {
|
|
var tl = this._ref.toRangeRef().topLeft;
|
|
x = kendo.spreadsheet.calc.parse(this._sheet.name(), tl.row, tl.col, value);
|
|
this._sheet.batch(function () {
|
|
var formula = null;
|
|
if (x.type == 'exp') {
|
|
formula = kendo.spreadsheet.calc.compile(x);
|
|
} else if (x.type == 'date') {
|
|
this.format(x.format || toExcelFormat(kendo.culture().calendar.patterns.d));
|
|
} else if (x.type == 'percent') {
|
|
this.format(x.value * 100 == (x.value * 100 | 0) ? '0%' : '0.00%');
|
|
} else if (x.format && !existingFormat) {
|
|
this.format(x.format);
|
|
}
|
|
this.formula(formula);
|
|
if (!formula) {
|
|
this.value(x.value);
|
|
}
|
|
}.bind(this), {
|
|
recalc: true,
|
|
value: value,
|
|
ref: this._ref,
|
|
editorChange: this._sheet.isInEditMode()
|
|
});
|
|
return this;
|
|
} else {
|
|
value = this._get('value');
|
|
var formula = this._get('formula');
|
|
var type = existingFormat && !formula && kendo.spreadsheet.formatting.type(value, existingFormat);
|
|
if (formula) {
|
|
value = '=' + formula;
|
|
} else
|
|
OUT: {
|
|
if (existingFormat && typeof value == 'number') {
|
|
var t1 = kendo.spreadsheet.formatting.text(value, existingFormat);
|
|
x = kendo.spreadsheet.calc.parse(null, null, null, t1);
|
|
var t2 = kendo.spreadsheet.formatting.text(x.value, existingFormat);
|
|
if (t1 == t2) {
|
|
value = t1;
|
|
break OUT;
|
|
}
|
|
}
|
|
if (type === 'date') {
|
|
value = kendo.toString(kendo.spreadsheet.numberToDate(value), kendo.culture().calendar.patterns.d);
|
|
} else if (type === 'percent') {
|
|
value = value * 100 + '%';
|
|
} else if (typeof value == 'string' && (/^[=']/.test(value) || /^(?:true|false)$/i.test(value) || looksLikeANumber(value))) {
|
|
value = '\'' + value;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
},
|
|
enable: function (value) {
|
|
if (value === undefined) {
|
|
value = true;
|
|
this._sheet.forEach(this._ref.toRangeRef(), function (_, _, data) {
|
|
if (data.enable === false) {
|
|
value = false;
|
|
}
|
|
});
|
|
return value;
|
|
}
|
|
this._property('enable', value);
|
|
},
|
|
format: function (value) {
|
|
return this._property('format', value);
|
|
},
|
|
formula: function (value) {
|
|
if (value === undefined) {
|
|
var f = this._get('formula');
|
|
return f ? '' + f : null;
|
|
}
|
|
return this._property('formula', value);
|
|
},
|
|
validation: function (value) {
|
|
if (value === undefined) {
|
|
var f = this._get('validation');
|
|
return f ? f.toJSON() : null;
|
|
}
|
|
return this._property('validation', value);
|
|
},
|
|
_getValidationState: function () {
|
|
var ref = this._ref.toRangeRef();
|
|
var topLeftRow = ref.topLeft.row;
|
|
var topLeftCol = ref.topLeft.col;
|
|
var bottomRightRow = ref.bottomRight.row;
|
|
var bottomRightCol = ref.bottomRight.col;
|
|
var ci, ri;
|
|
for (ci = topLeftCol; ci <= bottomRightCol; ci++) {
|
|
for (ri = topLeftRow; ri <= bottomRightRow; ri++) {
|
|
var validation = this._sheet._validation(ri, ci);
|
|
if (validation && validation.type === 'reject' && validation.value === false) {
|
|
return validation;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
merge: function () {
|
|
this._ref = this._sheet._merge(this._ref);
|
|
return this;
|
|
},
|
|
unmerge: function () {
|
|
var mergedCells = this._sheet._mergedCells;
|
|
this._ref.forEach(function (ref) {
|
|
ref.toRangeRef().intersecting(mergedCells).forEach(function (mergedRef) {
|
|
mergedCells.splice(mergedCells.indexOf(mergedRef), 1);
|
|
});
|
|
});
|
|
this._sheet.triggerChange({});
|
|
return this;
|
|
},
|
|
select: function () {
|
|
this._sheet.select(this._ref);
|
|
return this;
|
|
},
|
|
values: function (values) {
|
|
if (this._ref instanceof UnionRef) {
|
|
throw new Error('Unsupported for multiple ranges.');
|
|
}
|
|
if (this._ref === kendo.spreadsheet.NULLREF) {
|
|
if (values !== undefined) {
|
|
throw new Error('Unsupported for NULLREF.');
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
var ref = this._ref.toRangeRef();
|
|
var topLeftRow = ref.topLeft.row;
|
|
var topLeftCol = ref.topLeft.col;
|
|
var bottomRightRow = ref.bottomRight.row;
|
|
var bottomRightCol = ref.bottomRight.col;
|
|
var ci, ri;
|
|
if (values === undefined) {
|
|
values = new Array(ref.height());
|
|
for (var vi = 0; vi < values.length; vi++) {
|
|
values[vi] = new Array(ref.width());
|
|
}
|
|
for (ci = topLeftCol; ci <= bottomRightCol; ci++) {
|
|
for (ri = topLeftRow; ri <= bottomRightRow; ri++) {
|
|
values[ri - topLeftRow][ci - topLeftCol] = this._sheet._value(ri, ci);
|
|
}
|
|
}
|
|
return values;
|
|
} else {
|
|
for (ci = topLeftCol; ci <= bottomRightCol; ci++) {
|
|
for (ri = topLeftRow; ri <= bottomRightRow; ri++) {
|
|
var row = values[ri - topLeftRow];
|
|
if (row) {
|
|
var value = row[ci - topLeftCol];
|
|
if (value !== undefined) {
|
|
this._sheet._value(ri, ci, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._sheet.triggerChange({ recalc: true });
|
|
return this;
|
|
}
|
|
},
|
|
_properties: function (props) {
|
|
if (this._ref instanceof UnionRef) {
|
|
throw new Error('Unsupported for multiple ranges.');
|
|
}
|
|
if (this._ref === kendo.spreadsheet.NULLREF) {
|
|
if (props !== undefined) {
|
|
throw new Error('Unsupported for NULLREF.');
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
var ref = this._ref.toRangeRef();
|
|
var topLeftRow = ref.topLeft.row;
|
|
var topLeftCol = ref.topLeft.col;
|
|
var bottomRightRow = ref.bottomRight.row;
|
|
var bottomRightCol = ref.bottomRight.col;
|
|
var ci, ri;
|
|
var sheet = this._sheet;
|
|
if (props === undefined) {
|
|
props = new Array(ref.height());
|
|
sheet.forEach(ref, function (row, col, data) {
|
|
row -= topLeftRow;
|
|
col -= topLeftCol;
|
|
var line = props[row] || (props[row] = []);
|
|
line[col] = data;
|
|
});
|
|
return props;
|
|
} else {
|
|
var data;
|
|
ref = ref.clone();
|
|
var setProp = function (propName) {
|
|
var propValue = data[propName];
|
|
ref.topLeft.row = ref.bottomRight.row = ri;
|
|
ref.topLeft.col = ref.bottomRight.col = ci;
|
|
sheet._set(ref, propName, propValue);
|
|
};
|
|
for (ci = topLeftCol; ci <= bottomRightCol; ci++) {
|
|
for (ri = topLeftRow; ri <= bottomRightRow; ri++) {
|
|
var row = props[ri - topLeftRow];
|
|
if (row) {
|
|
data = row[ci - topLeftCol];
|
|
if (data) {
|
|
Object.keys(data).forEach(setProp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sheet.triggerChange({
|
|
recalc: true,
|
|
ref: this._ref
|
|
});
|
|
return this;
|
|
}
|
|
},
|
|
clear: function (options) {
|
|
var clearAll = !options || !Object.keys(options).length;
|
|
var sheet = this._sheet;
|
|
var reason = {
|
|
recalc: clearAll || options && options.contentsOnly === true,
|
|
ref: this._ref
|
|
};
|
|
sheet.batch(function () {
|
|
if (reason.recalc) {
|
|
this.formula(null);
|
|
}
|
|
if (clearAll) {
|
|
this.validation(null);
|
|
}
|
|
if (clearAll || options && options.formatOnly === true) {
|
|
styles.forEach(function (x) {
|
|
this[x](null);
|
|
}.bind(this));
|
|
this.format(null);
|
|
this.unmerge();
|
|
}
|
|
}.bind(this), reason);
|
|
return this;
|
|
},
|
|
clearContent: function () {
|
|
return this.clear({ contentsOnly: true });
|
|
},
|
|
clearFormat: function () {
|
|
return this.clear({ formatOnly: true });
|
|
},
|
|
isSortable: function () {
|
|
return !(this._ref instanceof UnionRef || this._ref === kendo.spreadsheet.NULLREF);
|
|
},
|
|
sort: function (spec) {
|
|
if (this._ref instanceof UnionRef) {
|
|
throw new Error('Unsupported for multiple ranges.');
|
|
}
|
|
if (this._ref === kendo.spreadsheet.NULLREF) {
|
|
throw new Error('Unsupported for NULLREF.');
|
|
}
|
|
if (spec === undefined) {
|
|
spec = { column: 0 };
|
|
}
|
|
spec = spec instanceof Array ? spec : [spec];
|
|
this._sheet._sortBy(this._ref.toRangeRef(), spec.map(function (spec, index) {
|
|
if (typeof spec === 'number') {
|
|
spec = { column: spec };
|
|
}
|
|
return {
|
|
index: spec.column === undefined ? index : spec.column,
|
|
ascending: spec.ascending === undefined ? true : spec.ascending
|
|
};
|
|
}));
|
|
return this;
|
|
},
|
|
isFilterable: function () {
|
|
return !(this._ref instanceof UnionRef);
|
|
},
|
|
filter: function (spec) {
|
|
if (this._ref instanceof UnionRef) {
|
|
throw new Error('Unsupported for multiple ranges.');
|
|
}
|
|
if (spec === false) {
|
|
this.clearFilters();
|
|
} else {
|
|
spec = spec === true ? [] : spec instanceof Array ? spec : [spec];
|
|
this._sheet._filterBy(this._ref.toRangeRef(), spec.map(function (spec, index) {
|
|
return {
|
|
index: spec.column === undefined ? index : spec.column,
|
|
filter: spec.filter
|
|
};
|
|
}));
|
|
}
|
|
return this;
|
|
},
|
|
clearFilter: function (spec) {
|
|
this._sheet.clearFilter(spec);
|
|
},
|
|
clearFilters: function () {
|
|
var filter = this._sheet.filter();
|
|
var spec = [];
|
|
if (filter) {
|
|
for (var i = 0; i < filter.columns.length; i++) {
|
|
spec.push(i);
|
|
}
|
|
this._sheet.batch(function () {
|
|
this.clearFilter(spec);
|
|
this._filter = null;
|
|
}, {
|
|
layout: true,
|
|
filter: true
|
|
});
|
|
}
|
|
},
|
|
hasFilter: function () {
|
|
var filter = this._sheet.filter();
|
|
return !!filter;
|
|
},
|
|
leftColumn: function () {
|
|
return new Range(this._ref.leftColumn(), this._sheet);
|
|
},
|
|
rightColumn: function () {
|
|
return new Range(this._ref.rightColumn(), this._sheet);
|
|
},
|
|
topRow: function () {
|
|
return new Range(this._ref.topRow(), this._sheet);
|
|
},
|
|
bottomRow: function () {
|
|
return new Range(this._ref.bottomRow(), this._sheet);
|
|
},
|
|
column: function (column) {
|
|
return new Range(this._ref.toColumn(column), this._sheet);
|
|
},
|
|
row: function (row) {
|
|
return new Range(this._ref.toRow(row), this._sheet);
|
|
},
|
|
forEachRow: function (callback) {
|
|
this._ref.forEachRow(function (ref) {
|
|
callback(new Range(ref, this._sheet));
|
|
}.bind(this));
|
|
},
|
|
forEachColumn: function (callback) {
|
|
this._ref.forEachColumn(function (ref) {
|
|
callback(new Range(ref, this._sheet));
|
|
}.bind(this));
|
|
},
|
|
sheet: function () {
|
|
return this._sheet;
|
|
},
|
|
topLeft: function () {
|
|
return this._ref.toRangeRef().topLeft;
|
|
},
|
|
intersectingMerged: function () {
|
|
var sheet = this._sheet;
|
|
var mergedCells = [];
|
|
sheet._mergedCells.forEach(function (ref) {
|
|
if (ref.intersects(this._ref)) {
|
|
mergedCells.push(ref.toString());
|
|
}
|
|
}.bind(this));
|
|
return mergedCells;
|
|
},
|
|
getState: function (propertyName) {
|
|
var state = { ref: this._ref.first() };
|
|
var properties;
|
|
if (!propertyName) {
|
|
properties = kendo.spreadsheet.ALL_PROPERTIES;
|
|
state.mergedCells = this.intersectingMerged();
|
|
} else if (propertyName === 'input') {
|
|
properties = [
|
|
'value',
|
|
'formula'
|
|
];
|
|
} else if (propertyName === 'border') {
|
|
properties = [
|
|
'borderLeft',
|
|
'borderTop',
|
|
'borderRight',
|
|
'borderBottom'
|
|
];
|
|
} else {
|
|
properties = [propertyName];
|
|
}
|
|
this.forEachCell(function (row, col, cell) {
|
|
var cellState = state[row + ',' + col] = {};
|
|
properties.forEach(function (property) {
|
|
cellState[property] = cell[property] || null;
|
|
});
|
|
});
|
|
return state;
|
|
},
|
|
setState: function (state) {
|
|
var sheet = this._sheet;
|
|
var origin = this._ref.first();
|
|
var rowDelta = state.ref.row - origin.row;
|
|
var colDelta = state.ref.col - origin.col;
|
|
sheet.batch(function () {
|
|
if (state.mergedCells) {
|
|
this.unmerge();
|
|
}
|
|
this.forEachCell(function (row, col) {
|
|
var cellState = state[row + rowDelta + ',' + (col + colDelta)];
|
|
var range = sheet.range(row, col);
|
|
for (var property in cellState) {
|
|
if (property != 'value') {
|
|
range._set(property, cellState[property]);
|
|
}
|
|
}
|
|
if (!cellState.formula) {
|
|
range._set('value', cellState.value);
|
|
}
|
|
});
|
|
if (state.mergedCells) {
|
|
state.mergedCells.forEach(function (merged) {
|
|
merged = sheet._ref(merged).relative(rowDelta, colDelta, 3);
|
|
sheet.range(merged).merge();
|
|
}, this);
|
|
}
|
|
}.bind(this), { recalc: true });
|
|
},
|
|
_adjustRowHeight: function () {
|
|
var sheet = this._sheet;
|
|
var state = this.getState();
|
|
var mergedCells = [];
|
|
for (var i = 0; i < state.mergedCells.length; i++) {
|
|
mergedCells.push(sheet.range(state.mergedCells[i]));
|
|
}
|
|
this.forEachRow(function (row) {
|
|
var maxHeight = row.sheet().rowHeight(row.topLeft().row);
|
|
row.forEachCell(function (rowIndex, colIndex, cell) {
|
|
var cellRange = sheet.range(rowIndex, colIndex);
|
|
var totalWidth = 0;
|
|
for (var i = 0; i < mergedCells.length; i++) {
|
|
if (cellRange._ref.intersects(mergedCells[i]._ref)) {
|
|
totalWidth += cell.width;
|
|
break;
|
|
}
|
|
}
|
|
var width = Math.max(sheet.columnWidth(colIndex), totalWidth);
|
|
maxHeight = Math.max(maxHeight, kendo.spreadsheet.util.getTextHeight(cell.value, width, cell.fontSize, cell.wrap));
|
|
});
|
|
sheet.rowHeight(row.topLeft().row, Math.max(sheet.rowHeight(row.topLeft().row), maxHeight));
|
|
});
|
|
},
|
|
forEachCell: function (callback) {
|
|
this._ref.forEach(function (ref) {
|
|
this._sheet.forEach(ref.toRangeRef(), callback.bind(this));
|
|
}.bind(this));
|
|
},
|
|
hasValue: function () {
|
|
var result = false;
|
|
this.forEachCell(function (row, col, cell) {
|
|
if (Object.keys(cell).length !== 0) {
|
|
result = true;
|
|
}
|
|
});
|
|
return result;
|
|
},
|
|
wrap: function (flag) {
|
|
if (flag === undefined) {
|
|
return !!this._property('wrap');
|
|
}
|
|
this.forEachRow(function (range) {
|
|
var maxHeight = range.sheet().rowHeight(range.topLeft().row);
|
|
range.forEachCell(function (row, col, cell) {
|
|
var width = this._sheet.columnWidth(col);
|
|
if (cell.value !== null && cell.value !== undefined) {
|
|
maxHeight = Math.max(maxHeight, kendo.spreadsheet.util.getTextHeight(cell.value, width, cell.fontSize, true));
|
|
}
|
|
});
|
|
range.sheet().rowHeight(range.topLeft().row, maxHeight);
|
|
}.bind(this));
|
|
this._property('wrap', flag);
|
|
return this;
|
|
},
|
|
fontSize: function (size) {
|
|
if (size === undefined) {
|
|
return this._property('fontSize');
|
|
}
|
|
this.forEachRow(function (range) {
|
|
var maxHeight = range.sheet().rowHeight(range.topLeft().row);
|
|
range.forEachCell(function (row, col, cell) {
|
|
var width = this._sheet.columnWidth(col);
|
|
if (cell.value !== null && cell.value !== undefined) {
|
|
maxHeight = Math.max(maxHeight, kendo.spreadsheet.util.getTextHeight(cell.value, width, size, cell.wrap));
|
|
}
|
|
});
|
|
range.sheet().rowHeight(range.topLeft().row, maxHeight);
|
|
}.bind(this));
|
|
this._property('fontSize', size);
|
|
return this;
|
|
},
|
|
draw: function (options, callback) {
|
|
this._sheet.draw(this, options, callback);
|
|
}
|
|
});
|
|
$.each(styles, function (i, property) {
|
|
Range.prototype[property] = function (value) {
|
|
return this._property(property, value);
|
|
};
|
|
});
|
|
function toExcelFormat(format) {
|
|
return format.replace(/M/g, 'm').replace(/'/g, '"').replace(/tt/, 'am/pm');
|
|
}
|
|
function looksLikeANumber(str) {
|
|
return !/^=/.test(str) && /number|percent/.test(kendo.spreadsheet.calc.parse(null, 0, 0, str).type);
|
|
}
|
|
var measureBox = $('<div style="position: absolute !important; top: -4000px !important; height: auto !important;' + 'padding: 1px !important; margin: 0 !important; border: 1px solid black !important;' + 'line-height: normal !important; visibility: hidden !important;' + 'white-space: pre-wrap !important; word-break: break-all !important;" />')[0];
|
|
function getTextHeight(text, width, fontSize, wrap) {
|
|
var styles = {
|
|
'baselineMarkerSize': 0,
|
|
'width': width + 'px',
|
|
'font-size': (fontSize || 12) + 'px',
|
|
'word-break': wrap === true ? 'break-all' : 'normal',
|
|
'white-space': wrap === true ? 'pre-wrap' : 'pre'
|
|
};
|
|
return kendo.util.measureText(text, styles, measureBox).height;
|
|
}
|
|
kendo.spreadsheet.util = { getTextHeight: getTextHeight };
|
|
kendo.spreadsheet.Range = Range;
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/runtime', ['spreadsheet/references'], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var calc = {};
|
|
var spreadsheet = kendo.spreadsheet;
|
|
spreadsheet.calc = calc;
|
|
var exports = calc.runtime = {};
|
|
var Class = kendo.Class;
|
|
var Ref = spreadsheet.Ref;
|
|
var CellRef = spreadsheet.CellRef;
|
|
var RangeRef = spreadsheet.RangeRef;
|
|
var UnionRef = spreadsheet.UnionRef;
|
|
var NULL = spreadsheet.NULLREF;
|
|
function CalcError(code) {
|
|
if (code instanceof CalcError) {
|
|
return code;
|
|
}
|
|
this.code = code;
|
|
}
|
|
CalcError.prototype.toString = function () {
|
|
return '#' + this.code + (this.code == 'NAME' ? '?' : '!');
|
|
};
|
|
var Context = Class.extend({
|
|
init: function Context(callback, formula, ss, parent) {
|
|
this.callback = callback;
|
|
this.formula = formula;
|
|
this.ss = ss;
|
|
this.parent = parent;
|
|
},
|
|
resolve: function (val) {
|
|
var self = this;
|
|
if (val instanceof Ref) {
|
|
self.resolveCells([val], function () {
|
|
val = self.getRefData(val);
|
|
if (Array.isArray(val)) {
|
|
val = val[0];
|
|
}
|
|
self._resolve(val);
|
|
});
|
|
} else {
|
|
self._resolve(val);
|
|
}
|
|
},
|
|
_resolve: function (val) {
|
|
if (val === undefined) {
|
|
val = null;
|
|
}
|
|
var f = this.formula;
|
|
f.value = val;
|
|
if (this.ss.onFormula(f) && this.callback) {
|
|
this.callback.call(f, val);
|
|
}
|
|
},
|
|
resolveCells: function (a, f) {
|
|
var context = this, formulas = [];
|
|
(function loop(a) {
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var x = a[i];
|
|
if (x instanceof Ref) {
|
|
add(context.getRefCells(x));
|
|
}
|
|
if (Array.isArray(x)) {
|
|
loop(x);
|
|
}
|
|
}
|
|
}(a));
|
|
if (!formulas.length) {
|
|
return f.call(context);
|
|
}
|
|
for (var pending = formulas.length, i = 0; i < formulas.length; ++i) {
|
|
fetch(formulas[i]);
|
|
}
|
|
function fetch(cell) {
|
|
cell.formula.exec(context.ss, function () {
|
|
if (!--pending) {
|
|
f.call(context);
|
|
}
|
|
}, context);
|
|
}
|
|
function add(a) {
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var cell = a[i];
|
|
if (cell.formula) {
|
|
formulas.push(cell);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
cellValues: function (a, f) {
|
|
var ret = [];
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var val = a[i];
|
|
if (val instanceof Ref) {
|
|
val = this.getRefData(val);
|
|
ret = ret.concat(val);
|
|
} else if (Array.isArray(val)) {
|
|
ret = ret.concat(this.cellValues(val));
|
|
} else if (val instanceof Matrix) {
|
|
ret = ret.concat(this.cellValues(val.data));
|
|
} else {
|
|
ret.push(val);
|
|
}
|
|
}
|
|
if (f) {
|
|
return f.apply(this, ret);
|
|
}
|
|
return ret;
|
|
},
|
|
force: function (val) {
|
|
if (val instanceof Ref) {
|
|
return this.getRefData(val);
|
|
}
|
|
return val;
|
|
},
|
|
func: function (fname, callback, args) {
|
|
fname = fname.toLowerCase();
|
|
var f = FUNCS[fname];
|
|
if (f) {
|
|
return f.call(this, callback, args);
|
|
}
|
|
callback(new CalcError('NAME'));
|
|
},
|
|
bool: function (val) {
|
|
if (val instanceof Ref) {
|
|
val = this.getRefData(val);
|
|
}
|
|
if (typeof val == 'string') {
|
|
return val.toLowerCase() == 'true';
|
|
}
|
|
if (typeof val == 'number') {
|
|
return val !== 0;
|
|
}
|
|
if (typeof val == 'boolean') {
|
|
return val;
|
|
}
|
|
return val != null;
|
|
},
|
|
asMatrix: function (range) {
|
|
if (range instanceof Matrix) {
|
|
return range;
|
|
}
|
|
var self = this;
|
|
if (range instanceof RangeRef) {
|
|
var tl = range.topLeft;
|
|
var top = tl.row, left = tl.col;
|
|
var cells = self.getRefCells(range);
|
|
var m = new Matrix(self);
|
|
if (isFinite(range.width())) {
|
|
m.width = range.width();
|
|
}
|
|
if (isFinite(range.height())) {
|
|
m.height = range.height();
|
|
}
|
|
if (!isFinite(top)) {
|
|
top = 0;
|
|
}
|
|
if (!isFinite(left)) {
|
|
left = 0;
|
|
}
|
|
cells.forEach(function (cell) {
|
|
m.set(cell.row - top, cell.col - left, cell.value);
|
|
});
|
|
return m;
|
|
}
|
|
if (Array.isArray(range) && range.length > 0) {
|
|
var m = new Matrix(self), row = 0;
|
|
range.forEach(function (line) {
|
|
var col = 0;
|
|
var h = 1;
|
|
line.forEach(function (el) {
|
|
var isRange = el instanceof RangeRef;
|
|
if (el instanceof Ref && !isRange) {
|
|
el = self.getRefData(el);
|
|
}
|
|
if (isRange || Array.isArray(el)) {
|
|
el = self.asMatrix(el);
|
|
}
|
|
if (el instanceof Matrix) {
|
|
el.each(function (el, r, c) {
|
|
m.set(row + r, col + c, el);
|
|
});
|
|
h = Math.max(h, el.height);
|
|
col += el.width;
|
|
} else {
|
|
m.set(row, col++, el);
|
|
}
|
|
});
|
|
row += h;
|
|
});
|
|
return m;
|
|
}
|
|
},
|
|
getRefCells: function (refs, hiddenInfo) {
|
|
return this.ss.getRefCells(refs, hiddenInfo);
|
|
},
|
|
getRefData: function (ref) {
|
|
return this.ss.getData(ref);
|
|
},
|
|
workbook: function () {
|
|
return this.ss.workbook;
|
|
}
|
|
});
|
|
var Matrix = Class.extend({
|
|
init: function Matrix(context) {
|
|
this.context = context;
|
|
this.height = 0;
|
|
this.width = 0;
|
|
this.data = [];
|
|
},
|
|
clone: function () {
|
|
var m = new Matrix(this.context);
|
|
m.height = this.height;
|
|
m.width = this.width;
|
|
m.data = this.data.map(function (row) {
|
|
return row.slice();
|
|
});
|
|
return m;
|
|
},
|
|
get: function (row, col) {
|
|
var line = this.data[row];
|
|
var val = line ? line[col] : null;
|
|
return val instanceof Ref ? this.context.getRefData(val) : val;
|
|
},
|
|
set: function (row, col, data) {
|
|
var line = this.data[row];
|
|
if (line == null) {
|
|
line = this.data[row] = [];
|
|
}
|
|
line[col] = data;
|
|
if (row >= this.height) {
|
|
this.height = row + 1;
|
|
}
|
|
if (col >= this.width) {
|
|
this.width = col + 1;
|
|
}
|
|
},
|
|
each: function (f, includeEmpty) {
|
|
for (var row = 0; row < this.height; ++row) {
|
|
for (var col = 0; col < this.width; ++col) {
|
|
var val = this.get(row, col);
|
|
if (includeEmpty || val != null) {
|
|
val = f.call(this.context, val, row, col);
|
|
if (val !== undefined) {
|
|
return val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
map: function (f, includeEmpty) {
|
|
var m = new Matrix(this.context);
|
|
this.each(function (el, row, col) {
|
|
m.set(row, col, f.call(this, el, row, col));
|
|
}, includeEmpty);
|
|
return m;
|
|
},
|
|
eachRow: function (f) {
|
|
for (var row = 0; row < this.height; ++row) {
|
|
var val = f.call(this.context, row);
|
|
if (val !== undefined) {
|
|
return val;
|
|
}
|
|
}
|
|
},
|
|
eachCol: function (f) {
|
|
for (var col = 0; col < this.width; ++col) {
|
|
var val = f.call(this.context, col);
|
|
if (val !== undefined) {
|
|
return val;
|
|
}
|
|
}
|
|
},
|
|
mapRow: function (f) {
|
|
var m = new Matrix(this.context);
|
|
this.eachRow(function (row) {
|
|
m.set(row, 0, f.call(this.context, row));
|
|
});
|
|
return m;
|
|
},
|
|
mapCol: function (f) {
|
|
var m = new Matrix(this.context);
|
|
this.eachCol(function (col) {
|
|
m.set(0, col, f.call(this.context, col));
|
|
});
|
|
return m;
|
|
},
|
|
toString: function () {
|
|
return JSON.stringify(this.data);
|
|
},
|
|
transpose: function () {
|
|
var m = new Matrix(this.context);
|
|
this.each(function (el, row, col) {
|
|
m.set(col, row, el);
|
|
});
|
|
return m;
|
|
},
|
|
unit: function (n) {
|
|
this.width = this.height = n;
|
|
var a = this.data = new Array(n);
|
|
for (var i = n; --i >= 0;) {
|
|
var row = a[i] = new Array(n);
|
|
for (var j = n; --j >= 0;) {
|
|
row[j] = i == j ? 1 : 0;
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
multiply: function (b) {
|
|
var a = this, m = new Matrix(a.context);
|
|
for (var row = 0; row < a.height; ++row) {
|
|
for (var col = 0; col < b.width; ++col) {
|
|
var s = 0;
|
|
for (var i = 0; i < a.width; ++i) {
|
|
var va = a.get(row, i);
|
|
var vb = b.get(i, col);
|
|
if (typeof va != 'number' || typeof vb != 'number') {
|
|
throw new CalcError('VALUE');
|
|
}
|
|
s += va * vb;
|
|
}
|
|
m.set(row, col, s);
|
|
}
|
|
}
|
|
return m;
|
|
},
|
|
adds: function (b, s) {
|
|
var a = this, m = new Matrix(a.context);
|
|
var sign = s ? -1 : 1;
|
|
for (var row = 0; row < a.height; ++row) {
|
|
for (var col = 0; col < a.width; ++col) {
|
|
var x = a.get(row, col), y = b.get(row, col);
|
|
m.set(row, col, x + sign * y);
|
|
}
|
|
}
|
|
return m;
|
|
},
|
|
determinant: function () {
|
|
var a = this.clone().data;
|
|
var n = a.length;
|
|
var d = 1, C, L, i, k;
|
|
for (C = 0; C < n; C++) {
|
|
for (L = C; L < n && !a[L][C]; L++) {
|
|
}
|
|
if (L == n) {
|
|
return 0;
|
|
}
|
|
if (L != C) {
|
|
d = -d;
|
|
for (k = C; k < n; k++) {
|
|
var t = a[C][k];
|
|
a[C][k] = a[L][k];
|
|
a[L][k] = t;
|
|
}
|
|
}
|
|
for (i = C + 1; i < n; i++) {
|
|
for (k = C + 1; k < n; k++) {
|
|
a[i][k] -= a[C][k] * a[i][C] / a[C][C];
|
|
}
|
|
}
|
|
d *= a[C][C];
|
|
}
|
|
return d;
|
|
},
|
|
inverse: function () {
|
|
var n = this.width;
|
|
var m = this.augment(new Matrix(this.context).unit(n));
|
|
var a = m.data;
|
|
var tmp;
|
|
for (var k = 0; k < n; ++k) {
|
|
var imax = argmax(k, n, function (i) {
|
|
return a[i][k];
|
|
});
|
|
if (!a[imax][k]) {
|
|
return null;
|
|
}
|
|
if (k != imax) {
|
|
tmp = a[k];
|
|
a[k] = a[imax];
|
|
a[imax] = tmp;
|
|
}
|
|
for (var i = k + 1; i < n; ++i) {
|
|
for (var j = k + 1; j < 2 * n; ++j) {
|
|
a[i][j] -= a[k][j] * a[i][k] / a[k][k];
|
|
}
|
|
a[i][k] = 0;
|
|
}
|
|
}
|
|
for (var i = 0; i < n; ++i) {
|
|
for (var f = a[i][i], j = 0; j < 2 * n; ++j) {
|
|
a[i][j] /= f;
|
|
}
|
|
}
|
|
for (var k = n; --k >= 0;) {
|
|
for (var i = k; --i >= 0;) {
|
|
if (a[i][k]) {
|
|
for (var j = 2 * n; --j >= n;) {
|
|
a[i][j] -= a[k][j] * a[i][k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return m.slice(0, n, n, n);
|
|
},
|
|
augment: function (m) {
|
|
var ret = this.clone(), n = ret.width;
|
|
m.each(function (val, row, col) {
|
|
ret.set(row, col + n, val);
|
|
});
|
|
return ret;
|
|
},
|
|
slice: function (row, col, height, width) {
|
|
var m = new Matrix(this.context);
|
|
for (var i = 0; i < height; ++i) {
|
|
for (var j = 0; j < width; ++j) {
|
|
m.set(i, j, this.get(row + i, col + j));
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
});
|
|
function argmax(i, end, f) {
|
|
var max = f(i), pos = i;
|
|
while (++i < end) {
|
|
var v = f(i);
|
|
if (v > max) {
|
|
max = v;
|
|
pos = i;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
var Formula = Class.extend({
|
|
init: function Formula(refs, handler, printer, sheet, row, col) {
|
|
this.refs = refs;
|
|
this.handler = handler;
|
|
this.print = printer;
|
|
this.absrefs = null;
|
|
this.sheet = sheet;
|
|
this.row = row;
|
|
this.col = col;
|
|
this.onReady = [];
|
|
this.pending = false;
|
|
},
|
|
clone: function (sheet, row, col) {
|
|
var lcsheet = sheet.toLowerCase();
|
|
var refs = this.refs;
|
|
if (lcsheet != this.sheet.toLowerCase()) {
|
|
refs = refs.map(function (ref) {
|
|
if (!ref.hasSheet() && ref.sheet.toLowerCase() != lcsheet) {
|
|
ref = ref.clone().setSheet(sheet);
|
|
}
|
|
return ref;
|
|
});
|
|
}
|
|
return new Formula(refs, this.handler, this.print, sheet, row, col);
|
|
},
|
|
resolve: function (val) {
|
|
this.pending = false;
|
|
this.onReady.forEach(function (callback) {
|
|
callback(val);
|
|
});
|
|
},
|
|
exec: function (ss, callback, parentContext) {
|
|
if ('value' in this) {
|
|
if (callback) {
|
|
callback(this.value);
|
|
}
|
|
} else {
|
|
if (callback) {
|
|
this.onReady.push(callback);
|
|
}
|
|
var ctx = new Context(this.resolve, this, ss, parentContext);
|
|
while (parentContext) {
|
|
if (parentContext.formula === this) {
|
|
this.pending = false;
|
|
ctx.resolve(new CalcError('CIRCULAR'));
|
|
return;
|
|
}
|
|
parentContext = parentContext.parent;
|
|
}
|
|
if (this.pending) {
|
|
return;
|
|
}
|
|
this.pending = true;
|
|
if (!this.absrefs) {
|
|
this.absrefs = this.refs.map(function (ref) {
|
|
return ref.absolute(this.row, this.col);
|
|
}, this);
|
|
}
|
|
this.handler.call(ctx);
|
|
}
|
|
},
|
|
reset: function () {
|
|
this.onReady = [];
|
|
this.pending = false;
|
|
delete this.value;
|
|
},
|
|
renameSheet: function (oldSheetName, newSheetName) {
|
|
oldSheetName = oldSheetName.toLowerCase();
|
|
this.absrefs = null;
|
|
if (this.sheet.toLowerCase() == oldSheetName) {
|
|
this.sheet = newSheetName;
|
|
}
|
|
this.refs.forEach(function (ref) {
|
|
if (ref.sheet.toLowerCase() == oldSheetName) {
|
|
ref.sheet = newSheetName;
|
|
}
|
|
});
|
|
},
|
|
adjust: function (affectedSheet, operation, start, delta) {
|
|
affectedSheet = affectedSheet.toLowerCase();
|
|
var formulaRow = this.row;
|
|
var formulaCol = this.col;
|
|
var formulaSheet = this.sheet.toLowerCase();
|
|
var formulaMoves = false;
|
|
if (formulaSheet == affectedSheet) {
|
|
if (operation == 'row' && formulaRow >= start) {
|
|
this.row += delta;
|
|
formulaMoves = true;
|
|
}
|
|
if (operation == 'col' && formulaCol >= start) {
|
|
this.col += delta;
|
|
formulaMoves = true;
|
|
}
|
|
}
|
|
var newFormulaRow = this.row;
|
|
var newFormulaCol = this.col;
|
|
this.absrefs = null;
|
|
this.refs = this.refs.map(function (ref) {
|
|
if (ref === NULL) {
|
|
return ref;
|
|
}
|
|
if (ref.sheet.toLowerCase() != affectedSheet) {
|
|
if (formulaMoves) {
|
|
if (operation == 'row' && formulaRow >= start) {
|
|
ref = ref.relative(delta, 0);
|
|
}
|
|
if (operation == 'col' && formulaCol >= start) {
|
|
ref = ref.relative(0, delta);
|
|
}
|
|
}
|
|
return ref;
|
|
}
|
|
return ref.adjust(formulaRow, formulaCol, newFormulaRow, newFormulaCol, operation == 'row', start, delta);
|
|
}, this);
|
|
},
|
|
toString: function () {
|
|
return this.print(this.row, this.col);
|
|
}
|
|
});
|
|
var FUNCS = Object.create(null);
|
|
FUNCS['if'] = function (callback, args) {
|
|
var self = this;
|
|
var co = args[0], th = args[1], el = args[2];
|
|
this.resolveCells([co], function () {
|
|
var comatrix = self.asMatrix(co);
|
|
if (comatrix) {
|
|
th(function (th) {
|
|
el(function (el) {
|
|
var thmatrix = self.asMatrix(th);
|
|
var elmatrix = self.asMatrix(el);
|
|
callback(comatrix.map(function (val, row, col) {
|
|
if (val instanceof CalcError) {
|
|
return val;
|
|
} else if (self.bool(val)) {
|
|
return thmatrix ? thmatrix.get(row, col) : th;
|
|
} else {
|
|
return elmatrix ? elmatrix.get(row, col) : el;
|
|
}
|
|
}));
|
|
});
|
|
});
|
|
} else {
|
|
co = this.force(co);
|
|
if (co instanceof CalcError) {
|
|
callback(co);
|
|
} else if (self.bool(co)) {
|
|
th(callback);
|
|
} else {
|
|
el(callback);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
FUNCS['φ'] = function (callback) {
|
|
callback((1 + Math.sqrt(5)) / 2);
|
|
};
|
|
function compileArgumentChecks(functionName, args) {
|
|
var arrayArgs = 'function arrayArgs(args) { var xargs = [], width = 0, height = 0, arrays = [], i = 0; ';
|
|
var resolve = 'function resolve(args, callback) { var toResolve = [], i = 0; ';
|
|
var name, forced, main = '\'use strict\'; function check(args) { var stack = [], tmp, xargs = [], i = 0, m, err = \'VALUE\'; ', haveForced = false;
|
|
var canBeArrayArg = false, hasArrayArgs = false;
|
|
main += args.map(comp).join('');
|
|
main += 'if (i < args.length) return new CalcError(\'N/A\'); ';
|
|
main += 'return xargs; } ';
|
|
arrayArgs += 'return { args: xargs, width: width, height: height, arrays: arrays }; } ';
|
|
var f;
|
|
if (haveForced) {
|
|
resolve += 'this.resolveCells(toResolve, callback); } ';
|
|
f = new Function('CalcError', main + resolve + arrayArgs + ' return { resolve: resolve, check: check, arrayArgs: arrayArgs };');
|
|
} else {
|
|
f = new Function('CalcError', main + ' return { check: check };');
|
|
}
|
|
f = f(CalcError);
|
|
if (!hasArrayArgs) {
|
|
delete f.arrayArgs;
|
|
}
|
|
return f;
|
|
function comp(x) {
|
|
name = x[0];
|
|
var code = '{ ';
|
|
if (Array.isArray(name)) {
|
|
arrayArgs += 'while (i < args.length) { ';
|
|
resolve += 'while (i < args.length) { ';
|
|
code += 'xargs.push(tmp = []); stack.push(xargs); xargs = tmp; ';
|
|
code += 'while (i < args.length) { ';
|
|
code += x.map(comp).join('');
|
|
code += '} ';
|
|
code += 'xargs = stack.pop(); ';
|
|
resolve += '} ';
|
|
arrayArgs += '} ';
|
|
} else if (name == '+') {
|
|
arrayArgs += 'while (i < args.length) { ';
|
|
resolve += 'while (i < args.length) { ';
|
|
code += 'xargs.push(tmp = []); stack.push(xargs); xargs = tmp; ';
|
|
code += 'do { ';
|
|
code += x.slice(1).map(comp).join('');
|
|
code += '} while (i < args.length); ';
|
|
code += 'xargs = stack.pop(); ';
|
|
resolve += '} ';
|
|
arrayArgs += '} ';
|
|
} else if (name == '?') {
|
|
code += 'if (!(' + cond(x[1]) + ')) return new CalcError(err); ';
|
|
} else {
|
|
var type = x[1];
|
|
if (Array.isArray(type) && /^#?collect/.test(type[0])) {
|
|
var n = type[2];
|
|
force();
|
|
code += 'try {' + 'var $' + name + ' = this.cellValues(args.slice(i';
|
|
if (n) {
|
|
code += ', i + ' + n;
|
|
}
|
|
code += ')).filter(function($' + name + '){ ';
|
|
if (type[0] == 'collect') {
|
|
code += 'if ($' + name + ' instanceof CalcError) throw $' + name + '; ';
|
|
}
|
|
code += 'return ' + cond(type[1]) + '; }, this); ';
|
|
if (n) {
|
|
code += 'i += ' + n + '; ';
|
|
} else {
|
|
code += 'i = args.length; ';
|
|
}
|
|
code += 'xargs.push($' + name + ')' + '} catch(ex) { if (ex instanceof CalcError) return ex; throw ex; } ';
|
|
resolve += 'toResolve.push(args.slice(i)); ';
|
|
} else if (type == 'rest') {
|
|
code += 'xargs.push(args.slice(i)); i = args.length; ';
|
|
} else {
|
|
if (canBeArrayArg = /^\*/.test(name)) {
|
|
hasArrayArgs = true;
|
|
name = name.substr(1);
|
|
}
|
|
code += 'var $' + name + ' = args[i++]; ';
|
|
var allowError = false;
|
|
if (/!$/.test(type)) {
|
|
type = type.substr(0, type.length - 1);
|
|
allowError = true;
|
|
} else {
|
|
code += 'if ($' + name + ' instanceof CalcError) return $' + name + '; ';
|
|
}
|
|
code += typeCheck(type, allowError) + 'xargs.push($' + name + '); ';
|
|
}
|
|
}
|
|
code += '} ';
|
|
return code;
|
|
}
|
|
function force() {
|
|
if (forced) {
|
|
return '$' + name + '';
|
|
}
|
|
haveForced = true;
|
|
forced = true;
|
|
resolve += 'toResolve.push(args[i++]); ';
|
|
return '($' + name + ' = this.force($' + name + '))';
|
|
}
|
|
function typeCheck(type, allowError) {
|
|
forced = false;
|
|
var ret = 'if (!(' + cond(type) + ')) { ';
|
|
if (forced && !allowError) {
|
|
ret += ' if ($' + name + ' instanceof CalcError) return $' + name + '; ';
|
|
}
|
|
ret += 'return new CalcError(err); } ';
|
|
if (!forced) {
|
|
resolve += 'i++; ';
|
|
}
|
|
if (canBeArrayArg) {
|
|
arrayArgs += 'var $' + name + ' = this.asMatrix(args[i]); ' + 'if ($' + name + ') { ' + 'xargs.push($' + name + '); ' + 'width = Math.max(width, $' + name + '.width); ' + 'height = Math.max(height, $' + name + '.height); ' + 'arrays.push(true) } else { ' + 'xargs.push(args[i]); ' + 'arrays.push(false); } i++; ';
|
|
} else {
|
|
arrayArgs += 'xargs.push(args[i++]); arrays.push(false); ';
|
|
}
|
|
return ret;
|
|
}
|
|
function cond(type) {
|
|
if (Array.isArray(type)) {
|
|
if (type[0] == 'or') {
|
|
return '(' + type.slice(1).map(cond).join(') || (') + ')';
|
|
}
|
|
if (type[0] == 'and') {
|
|
return '(' + type.slice(1).map(cond).join(') && (') + ')';
|
|
}
|
|
if (type[0] == 'values') {
|
|
return '(' + type.slice(1).map(function (val) {
|
|
return force() + ' === ' + val;
|
|
}).join(') || (') + ')';
|
|
}
|
|
if (type[0] == 'null') {
|
|
return '(' + cond('null') + ' ? (($' + name + ' = ' + type[1] + '), true) : false)';
|
|
}
|
|
if (type[0] == 'between' || type[0] == '[between]') {
|
|
return '(' + force() + ' >= ' + type[1] + ' && ' + '$' + name + ' <= ' + type[2] + ' ? true : ((err = \'NUM\'), false))';
|
|
}
|
|
if (type[0] == '(between)') {
|
|
return '(' + force() + ' > ' + type[1] + ' && ' + '$' + name + ' < ' + type[2] + ' ? true : ((err = \'NUM\'), false))';
|
|
}
|
|
if (type[0] == '(between]') {
|
|
return '(' + force() + ' > ' + type[1] + ' && ' + '$' + name + ' <= ' + type[2] + ' ? true : ((err = \'NUM\'), false))';
|
|
}
|
|
if (type[0] == '[between)') {
|
|
return '(' + force() + ' >= ' + type[1] + ' && ' + '$' + name + ' < ' + type[2] + ' ? true : ((err = \'NUM\'), false))';
|
|
}
|
|
if (type[0] == 'assert') {
|
|
var err = type[2] || 'N/A';
|
|
return '((' + type[1] + ') ? true : (err = ' + JSON.stringify(err) + ', false))';
|
|
}
|
|
if (type[0] == 'not') {
|
|
return '!(' + cond(type[1]) + ')';
|
|
}
|
|
throw new Error('Unknown array type condition: ' + type[0]);
|
|
}
|
|
if (type == 'number') {
|
|
return '(typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\')';
|
|
}
|
|
if (type == 'integer') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') ? ($' + name + ' |= 0, true) : false)';
|
|
}
|
|
if (type == 'date') {
|
|
return '((typeof ' + force() + ' == \'number\') ? ($' + name + ' |= 0, true) : false)';
|
|
}
|
|
if (type == 'datetime') {
|
|
return '(typeof ' + force() + ' == \'number\')';
|
|
}
|
|
if (type == 'divisor') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') && ' + '($' + name + ' == 0 ? ((err = \'DIV/0\'), false) : true))';
|
|
}
|
|
if (type == 'number+') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') && ($' + name + ' >= 0 ? true : ((err = \'NUM\'), false)))';
|
|
}
|
|
if (type == 'integer+') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') && (($' + name + ' |= 0) >= 0 ? true : ((err = \'NUM\'), false)))';
|
|
}
|
|
if (type == 'number++') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') && ($' + name + ' > 0 ? true : ((err = \'NUM\'), false)))';
|
|
}
|
|
if (type == 'integer++') {
|
|
return '((typeof ' + force() + ' == \'number\' || typeof $' + name + ' == \'boolean\') && (($' + name + ' |= 0 ) > 0) ? true : ((err = \'NUM\'), false))';
|
|
}
|
|
if (type == 'string') {
|
|
return '((typeof ' + force() + ' == \'string\' || typeof $' + name + ' == \'boolean\' || typeof $' + name + ' == \'number\') ? ($' + name + ' += \'\', true) : false)';
|
|
}
|
|
if (type == 'boolean') {
|
|
return '(typeof ' + force() + ' == \'boolean\')';
|
|
}
|
|
if (type == 'logical') {
|
|
return '(typeof ' + force() + ' == \'boolean\' || (typeof $' + name + ' == \'number\' ? ($' + name + ' = !!$' + name + ', true) : false))';
|
|
}
|
|
if (type == 'matrix') {
|
|
force();
|
|
return '((m = this.asMatrix($' + name + ')) ? ($' + name + ' = m) : false)';
|
|
}
|
|
if (type == '#matrix') {
|
|
return '((m = this.asMatrix($' + name + ')) ? ($' + name + ' = m) : false)';
|
|
}
|
|
if (type == 'ref') {
|
|
return '($' + name + ' instanceof kendo.spreadsheet.Ref)';
|
|
}
|
|
if (type == 'area') {
|
|
return '($' + name + ' instanceof kendo.spreadsheet.CellRef || $' + name + ' instanceof kendo.spreadsheet.RangeRef)';
|
|
}
|
|
if (type == 'cell') {
|
|
return '($' + name + ' instanceof kendo.spreadsheet.CellRef)';
|
|
}
|
|
if (type == 'null') {
|
|
return '(' + force() + ' == null)';
|
|
}
|
|
if (type == 'anyvalue') {
|
|
return '(' + force() + ' != null && i <= args.length)';
|
|
}
|
|
if (type == 'forced') {
|
|
return '(' + force() + ', i <= args.length)';
|
|
}
|
|
if (type == 'anything') {
|
|
return '(i <= args.length)';
|
|
}
|
|
if (type == 'blank') {
|
|
return '(' + force() + ' == null || $' + name + ' === \'\')';
|
|
}
|
|
throw new Error('Can\'t check for type: ' + type);
|
|
}
|
|
}
|
|
function withErrorHandling(obj, f, args) {
|
|
if (args instanceof CalcError) {
|
|
return args;
|
|
}
|
|
try {
|
|
return f.apply(obj, args);
|
|
} catch (ex) {
|
|
if (ex instanceof CalcError) {
|
|
return ex;
|
|
} else {
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|
|
function makeSyncFunction(handler, resolve, check, arrayArgs) {
|
|
return function (callback, args) {
|
|
function doit() {
|
|
if (arrayArgs) {
|
|
var x = arrayArgs.call(this, args);
|
|
args = x.args;
|
|
if (x.width > 0 && x.height > 0) {
|
|
var result = new Matrix(this);
|
|
for (var row = 0; row < x.height; ++row) {
|
|
for (var col = 0; col < x.width; ++col) {
|
|
var xargs = [];
|
|
for (var i = 0; i < args.length; ++i) {
|
|
if (x.arrays[i]) {
|
|
var m = args[i];
|
|
xargs[i] = m.get(row % m.height, col % m.width);
|
|
} else {
|
|
xargs[i] = args[i];
|
|
}
|
|
}
|
|
xargs = check.call(this, xargs);
|
|
result.set(row, col, withErrorHandling(this, handler, xargs));
|
|
}
|
|
}
|
|
return callback(result);
|
|
}
|
|
}
|
|
var xargs = check.call(this, args);
|
|
callback(withErrorHandling(this, handler, xargs));
|
|
}
|
|
if (resolve) {
|
|
resolve.call(this, args, doit);
|
|
} else {
|
|
doit.call(this);
|
|
}
|
|
};
|
|
}
|
|
function makeAsyncFunction(handler, resolve, check, arrayArgs) {
|
|
return function (callback, args) {
|
|
function doit() {
|
|
if (arrayArgs) {
|
|
var x = arrayArgs.call(this, args);
|
|
args = x.args;
|
|
if (x.width > 0 && x.height > 0) {
|
|
var result = new Matrix(this);
|
|
var count = x.width * x.height;
|
|
var makeCallback = function (row, col) {
|
|
return function (value) {
|
|
result.set(row, col, value);
|
|
--count;
|
|
if (count === 0) {
|
|
return callback(result);
|
|
}
|
|
};
|
|
};
|
|
for (var row = 0; row < x.height && count > 0; ++row) {
|
|
for (var col = 0; col < x.width && count > 0; ++col) {
|
|
var xargs = [];
|
|
for (var i = 0; i < args.length; ++i) {
|
|
if (x.arrays[i]) {
|
|
var m = args[i];
|
|
xargs[i] = m.get(row % m.height, col % m.width);
|
|
} else {
|
|
xargs[i] = args[i];
|
|
}
|
|
}
|
|
xargs = check.call(this, xargs);
|
|
if (xargs instanceof CalcError) {
|
|
result.set(row, col, xargs);
|
|
--count;
|
|
if (count === 0) {
|
|
return callback(result);
|
|
}
|
|
} else {
|
|
xargs.unshift(makeCallback(row, col));
|
|
handler.apply(this, xargs);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
var x = check.call(this, args);
|
|
if (x instanceof CalcError) {
|
|
callback(x);
|
|
} else {
|
|
x.unshift(callback);
|
|
handler.apply(this, x);
|
|
}
|
|
}
|
|
if (resolve) {
|
|
resolve.call(this, args, doit);
|
|
} else {
|
|
doit.call(this);
|
|
}
|
|
};
|
|
}
|
|
function defineFunction(name, func) {
|
|
name = name.toLowerCase();
|
|
FUNCS[name] = func;
|
|
return {
|
|
args: function (args, log) {
|
|
var code = compileArgumentChecks(name, args);
|
|
if (log) {
|
|
if (code.arrayArgs) {
|
|
console.log(code.arrayArgs.toString());
|
|
}
|
|
if (code.resolve) {
|
|
console.log(code.resolve.toString());
|
|
}
|
|
if (code.check) {
|
|
console.log(code.check.toString());
|
|
}
|
|
}
|
|
var f = FUNCS[name] = makeSyncFunction(func, code.resolve, code.check, code.arrayArgs);
|
|
f.kendoSpreadsheetArgs = args;
|
|
return this;
|
|
},
|
|
argsAsync: function (args, log) {
|
|
var code = compileArgumentChecks(name, args);
|
|
if (log) {
|
|
if (code.arrayArgs) {
|
|
console.log(code.arrayArgs.toString());
|
|
}
|
|
if (code.resolve) {
|
|
console.log(code.resolve.toString());
|
|
}
|
|
if (code.check) {
|
|
console.log(code.check.toString());
|
|
}
|
|
}
|
|
var f = FUNCS[name] = makeAsyncFunction(func, code.resolve, code.check, code.arrayArgs);
|
|
f.kendoSpreadsheetArgs = args;
|
|
return this;
|
|
}
|
|
};
|
|
}
|
|
function dateToJulianDays(y, m, d) {
|
|
m++;
|
|
return (1461 * (y + 4800 + ((m - 14) / 12 | 0)) / 4 | 0) + (367 * (m - 2 - 12 * ((m - 14) / 12 | 0)) / 12 | 0) - (3 * ((y + 4900 + ((m - 14) / 12 | 0)) / 100 | 0) / 4 | 0) + d - 32075;
|
|
}
|
|
function julianDaysToDate(jd) {
|
|
var l, n, j, i, m, d, y;
|
|
l = jd + 68569;
|
|
n = 4 * l / 146097 | 0;
|
|
l = l - ((146097 * n + 3) / 4 | 0);
|
|
i = 4000 * (l + 1) / 1461001 | 0;
|
|
l = l - (1461 * i / 4 | 0) + 31;
|
|
j = 80 * l / 2447 | 0;
|
|
d = l - (2447 * j / 80 | 0);
|
|
l = j / 11 | 0;
|
|
m = j + 2 - 12 * l;
|
|
y = 100 * (n - 49) + i + l;
|
|
m--;
|
|
return {
|
|
year: y,
|
|
month: m,
|
|
date: d,
|
|
day: (jd + 1) % 7,
|
|
ord: ORDINAL_ADD_DAYS[isLeapYear(y)][m] + d
|
|
};
|
|
}
|
|
var BASE_DATE = dateToJulianDays(1900, 0, -1);
|
|
var DAYS_IN_MONTH = [
|
|
31,
|
|
28,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31
|
|
];
|
|
var ORDINAL_ADD_DAYS = [
|
|
[
|
|
0,
|
|
31,
|
|
59,
|
|
90,
|
|
120,
|
|
151,
|
|
181,
|
|
212,
|
|
243,
|
|
273,
|
|
304,
|
|
334
|
|
],
|
|
[
|
|
0,
|
|
31,
|
|
60,
|
|
91,
|
|
121,
|
|
152,
|
|
182,
|
|
213,
|
|
244,
|
|
274,
|
|
305,
|
|
335
|
|
]
|
|
];
|
|
function isLeapYear(yr) {
|
|
if (yr % 4) {
|
|
return 0;
|
|
}
|
|
if (yr % 100) {
|
|
return 1;
|
|
}
|
|
if (yr % 400) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
function daysInYear(yr) {
|
|
return isLeapYear(yr) ? 366 : 365;
|
|
}
|
|
function daysInMonth(yr, mo) {
|
|
return isLeapYear(yr) && mo == 1 ? 29 : DAYS_IN_MONTH[mo];
|
|
}
|
|
function unpackDate(serial) {
|
|
return julianDaysToDate((serial | 0) + BASE_DATE);
|
|
}
|
|
function packDate(year, month, date) {
|
|
return dateToJulianDays(year, month, date) - BASE_DATE;
|
|
}
|
|
var MS_IN_MIN = 60 * 1000;
|
|
var MS_IN_HOUR = 60 * MS_IN_MIN;
|
|
var MS_IN_DAY = 24 * MS_IN_HOUR;
|
|
function unpackTime(serial) {
|
|
var frac = serial - (serial | 0);
|
|
if (frac < 0) {
|
|
frac++;
|
|
}
|
|
var ms = Math.round(MS_IN_DAY * frac);
|
|
var hours = Math.floor(ms / MS_IN_HOUR);
|
|
ms -= hours * MS_IN_HOUR;
|
|
var minutes = Math.floor(ms / MS_IN_MIN);
|
|
ms -= minutes * MS_IN_MIN;
|
|
var seconds = Math.floor(ms / 1000);
|
|
ms -= seconds * 1000;
|
|
return {
|
|
hours: hours,
|
|
minutes: minutes,
|
|
seconds: seconds,
|
|
milliseconds: ms
|
|
};
|
|
}
|
|
function serialToDate(serial) {
|
|
var d = unpackDate(serial), t = unpackTime(serial);
|
|
return new Date(d.year, d.month, d.date, t.hours, t.minutes, t.seconds, t.milliseconds);
|
|
}
|
|
function packTime(hh, mm, ss, ms) {
|
|
return (hh + (mm + (ss + ms / 1000) / 60) / 60) / 24;
|
|
}
|
|
function dateToSerial(date) {
|
|
var time = packTime(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
date = packDate(date.getFullYear(), date.getMonth(), date.getDate());
|
|
if (date < 0) {
|
|
return date - 1 + time;
|
|
} else {
|
|
return date + time;
|
|
}
|
|
}
|
|
function parseDate(str) {
|
|
return kendo.parseDate(str, [
|
|
'MM/dd/yyyy',
|
|
'MM-dd-yyyy',
|
|
'MM/dd/yy',
|
|
'MM-dd-yy',
|
|
'MMMM dd yyyy',
|
|
'MMMM dd yy',
|
|
'MMM dd yyyy',
|
|
'MMM dd yy',
|
|
'dd MMMM yyyy',
|
|
'dd MMMM yy',
|
|
'dd MMM yyyy',
|
|
'dd MMM yy',
|
|
'MMMM dd, yyyy',
|
|
'MMMM dd, yy',
|
|
'MMM dd, yyyy',
|
|
'MMM dd, yy',
|
|
'MMMM dd',
|
|
'MMM dd',
|
|
'MMMM yyyy',
|
|
'MMM yyyy',
|
|
'dd MMMM',
|
|
'dd MMM',
|
|
'MM-dd',
|
|
'MM/dd'
|
|
]) || kendo.parseDate(str);
|
|
}
|
|
exports.CalcError = CalcError;
|
|
exports.Formula = Formula;
|
|
exports.Matrix = Matrix;
|
|
exports.packDate = packDate;
|
|
exports.unpackDate = unpackDate;
|
|
exports.packTime = packTime;
|
|
exports.unpackTime = unpackTime;
|
|
exports.serialToDate = serialToDate;
|
|
exports.dateToSerial = dateToSerial;
|
|
exports.daysInMonth = daysInMonth;
|
|
exports.isLeapYear = isLeapYear;
|
|
exports.daysInYear = daysInYear;
|
|
exports.parseDate = parseDate;
|
|
spreadsheet.dateToNumber = dateToSerial;
|
|
spreadsheet.numberToDate = serialToDate;
|
|
spreadsheet.defineFunction = defineFunction;
|
|
spreadsheet.CalcError = CalcError;
|
|
exports.defineFunction = defineFunction;
|
|
exports.defineAlias = function (alias, name) {
|
|
var orig = FUNCS[name];
|
|
if (!orig) {
|
|
throw new Error('Function ' + name + ' is not yet defined');
|
|
}
|
|
if (!orig.kendoSpreadsheetAliases) {
|
|
orig.kendoSpreadsheetAliases = [name];
|
|
}
|
|
orig.kendoSpreadsheetAliases.push(alias);
|
|
FUNCS[alias] = orig;
|
|
};
|
|
exports.FUNCS = FUNCS;
|
|
var NUMBER_OR_ZERO = [
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
];
|
|
var ARGS_NUMERIC = [
|
|
[
|
|
'*a',
|
|
NUMBER_OR_ZERO
|
|
],
|
|
[
|
|
'*b',
|
|
NUMBER_OR_ZERO
|
|
]
|
|
];
|
|
var ARGS_ANYVALUE = [
|
|
[
|
|
'*a',
|
|
[
|
|
'or',
|
|
'anyvalue',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*b',
|
|
[
|
|
'or',
|
|
'anyvalue',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
];
|
|
defineFunction('binary+', function (a, b) {
|
|
return a + b;
|
|
}).args(ARGS_NUMERIC);
|
|
defineFunction('binary-', function (a, b) {
|
|
return a - b;
|
|
}).args(ARGS_NUMERIC);
|
|
defineFunction('binary*', function (a, b) {
|
|
return a * b;
|
|
}).args(ARGS_NUMERIC);
|
|
defineFunction('binary/', function (a, b) {
|
|
return a / b;
|
|
}).args([
|
|
[
|
|
'*a',
|
|
NUMBER_OR_ZERO
|
|
],
|
|
[
|
|
'*b',
|
|
'divisor'
|
|
]
|
|
]);
|
|
defineFunction('binary^', function (a, b) {
|
|
return Math.pow(a, b);
|
|
}).args(ARGS_NUMERIC);
|
|
defineFunction('binary&', function (a, b) {
|
|
if (a == null) {
|
|
a = '';
|
|
}
|
|
if (b == null) {
|
|
b = '';
|
|
}
|
|
return '' + a + b;
|
|
}).args([
|
|
[
|
|
'*a',
|
|
[
|
|
'or',
|
|
'number',
|
|
'string',
|
|
'boolean',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'*b',
|
|
[
|
|
'or',
|
|
'number',
|
|
'string',
|
|
'boolean',
|
|
'null'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('binary=', function (a, b) {
|
|
return a === b;
|
|
}).args(ARGS_ANYVALUE);
|
|
defineFunction('binary<>', function (a, b) {
|
|
return a !== b;
|
|
}).args(ARGS_ANYVALUE);
|
|
defineFunction('binary<', binaryCompare(function (a, b) {
|
|
return a < b;
|
|
})).args(ARGS_ANYVALUE);
|
|
defineFunction('binary<=', binaryCompare(function (a, b) {
|
|
return a <= b;
|
|
})).args(ARGS_ANYVALUE);
|
|
defineFunction('binary>', binaryCompare(function (a, b) {
|
|
return a > b;
|
|
})).args(ARGS_ANYVALUE);
|
|
defineFunction('binary>=', binaryCompare(function (a, b) {
|
|
return a >= b;
|
|
})).args(ARGS_ANYVALUE);
|
|
defineFunction('unary+', function (a) {
|
|
return a;
|
|
}).args([[
|
|
'*a',
|
|
NUMBER_OR_ZERO
|
|
]]);
|
|
defineFunction('unary-', function (a) {
|
|
return -a;
|
|
}).args([[
|
|
'*a',
|
|
NUMBER_OR_ZERO
|
|
]]);
|
|
defineFunction('unary%', function (a) {
|
|
return a / 100;
|
|
}).args([[
|
|
'*a',
|
|
NUMBER_OR_ZERO
|
|
]]);
|
|
defineFunction('binary:', function (a, b) {
|
|
return new RangeRef(a, b).setSheet(a.sheet || this.formula.sheet, a.hasSheet());
|
|
}).args([
|
|
[
|
|
'a',
|
|
'cell'
|
|
],
|
|
[
|
|
'b',
|
|
'cell'
|
|
]
|
|
]);
|
|
defineFunction('binary,', function (a, b) {
|
|
return new UnionRef([
|
|
a,
|
|
b
|
|
]);
|
|
}).args([
|
|
[
|
|
'a',
|
|
'ref'
|
|
],
|
|
[
|
|
'b',
|
|
'ref'
|
|
]
|
|
]);
|
|
defineFunction('binary ', function (a, b) {
|
|
return a.intersect(b);
|
|
}).args([
|
|
[
|
|
'a',
|
|
'ref'
|
|
],
|
|
[
|
|
'b',
|
|
'ref'
|
|
]
|
|
]);
|
|
defineFunction('not', function (a) {
|
|
return !this.bool(a);
|
|
}).args([[
|
|
'*a',
|
|
[
|
|
'or',
|
|
'anyvalue',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('isblank', function (val) {
|
|
if (val instanceof CellRef) {
|
|
val = this.getRefData(val);
|
|
return val == null;
|
|
}
|
|
return false;
|
|
}).args([[
|
|
'*value',
|
|
'anything!'
|
|
]]);
|
|
defineFunction('iserror', function (val) {
|
|
return val instanceof CalcError;
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('iserr', function (val) {
|
|
return val instanceof CalcError && val.code != 'N/A';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('isna', function (val) {
|
|
return val instanceof CalcError && val.code == 'N/A';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('islogical', function (val) {
|
|
return typeof val == 'boolean';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('isnontext', function (val) {
|
|
return typeof val != 'string';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('istext', function (val) {
|
|
return typeof val == 'string';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('isnumber', function (val) {
|
|
return typeof val == 'number';
|
|
}).args([[
|
|
'*value',
|
|
'forced!'
|
|
]]);
|
|
defineFunction('isref', function (val) {
|
|
return val instanceof CellRef || val instanceof RangeRef;
|
|
}).args([[
|
|
'*value',
|
|
'anything!'
|
|
]]);
|
|
function binaryCompare(func) {
|
|
return function (left, right) {
|
|
if (typeof left == 'string' && typeof right != 'string') {
|
|
right = right == null ? '' : right + '';
|
|
}
|
|
if (typeof left != 'string' && typeof right == 'string') {
|
|
left = left == null ? '' : left + '';
|
|
}
|
|
if (typeof left == 'number' && right == null) {
|
|
right = 0;
|
|
}
|
|
if (typeof right == 'number' && left == null) {
|
|
left = 0;
|
|
}
|
|
if (typeof left == 'string' && typeof right == 'string') {
|
|
left = left.toLowerCase();
|
|
right = right.toLowerCase();
|
|
}
|
|
if (typeof right == typeof left) {
|
|
return func(left, right);
|
|
} else {
|
|
return new CalcError('VALUE');
|
|
}
|
|
};
|
|
}
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/validation', ['spreadsheet/runtime'], f);
|
|
}(function () {
|
|
'use strict';
|
|
var $ = kendo.jQuery;
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var exports = {};
|
|
spreadsheet.validation = exports;
|
|
var calc = spreadsheet.calc;
|
|
var Class = kendo.Class;
|
|
var TRANSPOSE_FORMAT = '_matrix({0})';
|
|
var DATE_FORMAT = 'DATEVALUE("{0}")';
|
|
calc.runtime.defineFunction('_matrix', function (m) {
|
|
return m;
|
|
}).args([[
|
|
'm',
|
|
'matrix'
|
|
]]);
|
|
function compileValidation(sheet, row, col, validation) {
|
|
var validationHandler;
|
|
var comparer;
|
|
var parsedFromDate;
|
|
var parsedToDate;
|
|
if (typeof validation === 'string') {
|
|
validation = JSON.parse(validation);
|
|
}
|
|
if (validation.from) {
|
|
if (validation.dataType === 'list') {
|
|
validation.from = kendo.format(TRANSPOSE_FORMAT, validation.from);
|
|
}
|
|
if (validation.dataType === 'date') {
|
|
parsedFromDate = calc.runtime.parseDate(validation.from);
|
|
if (parsedFromDate) {
|
|
validation.from = kendo.format(DATE_FORMAT, validation.from);
|
|
validation.fromIsDateValue = true;
|
|
}
|
|
}
|
|
validation.from = calc.compile(calc.parseFormula(sheet, row, col, validation.from));
|
|
}
|
|
if (validation.to) {
|
|
if (validation.dataType === 'date') {
|
|
parsedToDate = calc.runtime.parseDate(validation.to);
|
|
if (parsedToDate) {
|
|
validation.to = kendo.format(DATE_FORMAT, validation.to);
|
|
validation.toIsDateValue = true;
|
|
}
|
|
}
|
|
validation.to = calc.compile(calc.parseFormula(sheet, row, col, validation.to));
|
|
}
|
|
if (validation.dataType == 'custom') {
|
|
comparer = exports.validationComparers.custom;
|
|
} else if (validation.dataType == 'list') {
|
|
comparer = exports.validationComparers.list;
|
|
} else {
|
|
comparer = exports.validationComparers[validation.comparerType];
|
|
}
|
|
if (!comparer) {
|
|
throw kendo.format('\'{0}\' comparer is not implemented.', validation.comparerType);
|
|
}
|
|
validationHandler = function (valueToCompare) {
|
|
var toValue = this.to && this.to.value ? this.to.value : undefined;
|
|
if (valueToCompare === null || valueToCompare === '') {
|
|
if (this.allowNulls) {
|
|
this.value = true;
|
|
} else {
|
|
this.value = false;
|
|
}
|
|
} else if (this.dataType == 'custom') {
|
|
this.value = comparer(valueToCompare, this.from.value, toValue);
|
|
} else if (this.dataType == 'list') {
|
|
var data = this._getListData();
|
|
this.value = comparer(valueToCompare, data, toValue);
|
|
} else {
|
|
this.value = comparer(valueToCompare, this.from.value, toValue);
|
|
}
|
|
return this.value;
|
|
};
|
|
return new kendo.spreadsheet.validation.Validation($.extend(validation, {
|
|
handler: validationHandler,
|
|
sheet: sheet,
|
|
row: row,
|
|
col: col
|
|
}));
|
|
}
|
|
var Validation = Class.extend({
|
|
init: function Validation(options) {
|
|
this.handler = options.handler;
|
|
this.from = options.from;
|
|
this.to = options.to;
|
|
this.dataType = options.dataType;
|
|
this.comparerType = options.comparerType;
|
|
this.type = options.type ? options.type : 'warning';
|
|
this.allowNulls = options.allowNulls ? true : false;
|
|
this.fromIsDateValue = options.fromIsDateValue ? true : false;
|
|
this.toIsDateValue = options.toIsDateValue ? true : false;
|
|
this.sheet = options.sheet;
|
|
this.row = options.row;
|
|
this.col = options.col;
|
|
if (options.tooltipMessageTemplate) {
|
|
this.tooltipMessageTemplate = options.tooltipMessageTemplate;
|
|
}
|
|
if (options.tooltipTitleTemplate) {
|
|
this.tooltipTitleTemplate = options.tooltipTitleTemplate;
|
|
}
|
|
if (options.messageTemplate) {
|
|
this.messageTemplate = options.messageTemplate;
|
|
}
|
|
if (options.titleTemplate) {
|
|
this.titleTemplate = options.titleTemplate;
|
|
}
|
|
},
|
|
_formatMessages: function (format) {
|
|
var from = this.from ? this.from.value : '';
|
|
var to = this.to ? this.to.value : '';
|
|
var fromFormula = this.from ? this.from.toString() : '';
|
|
var toFormula = this.to ? this.to.toString() : '';
|
|
var dataType = this.dataType;
|
|
var type = this.type;
|
|
var comparerType = this.comparerType;
|
|
return kendo.format(format, from, to, fromFormula, toFormula, dataType, type, comparerType);
|
|
},
|
|
_setMessages: function () {
|
|
this.title = '';
|
|
this.message = '';
|
|
if (this.tooltipTitleTemplate) {
|
|
this.tooltipTitle = this._formatMessages(this.tooltipTitleTemplate);
|
|
}
|
|
if (this.tooltipMessageTemplate) {
|
|
this.tooltipMessage = this._formatMessages(this.tooltipMessageTemplate);
|
|
}
|
|
if (this.titleTemplate) {
|
|
this.title = this._formatMessages(this.titleTemplate);
|
|
}
|
|
if (this.messageTemplate) {
|
|
this.message = this._formatMessages(this.messageTemplate);
|
|
}
|
|
},
|
|
_getListData: function () {
|
|
if (!this.from.value || !this.from.value.data) {
|
|
return [];
|
|
}
|
|
var cube = this.from.value.data;
|
|
var i;
|
|
var y;
|
|
var data = [];
|
|
for (i = 0; i < cube.length; i++) {
|
|
var array = cube[i];
|
|
if (array) {
|
|
for (y = 0; y < array.length; y++) {
|
|
data.push(array[y]);
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
},
|
|
clone: function (sheet, row, col) {
|
|
var options = this._getOptions();
|
|
if (options.from) {
|
|
options.from = options.from.clone(sheet, row, col);
|
|
}
|
|
if (options.to) {
|
|
options.to = options.to.clone(sheet, row, col);
|
|
}
|
|
return new Validation($.extend(options, { handler: this.handler }, {
|
|
sheet: sheet,
|
|
row: row,
|
|
col: col
|
|
}));
|
|
},
|
|
exec: function (ss, compareValue, compareFormat, callback) {
|
|
var self = this;
|
|
var calculateFromCallBack = function () {
|
|
self.value = self.handler.call(self, compareValue, compareFormat);
|
|
self._setMessages();
|
|
if (callback) {
|
|
callback(self.value);
|
|
}
|
|
};
|
|
if (self.to) {
|
|
self.to.exec(ss, function () {
|
|
self.from.exec(ss, calculateFromCallBack);
|
|
});
|
|
} else {
|
|
self.from.exec(ss, calculateFromCallBack);
|
|
}
|
|
},
|
|
reset: function () {
|
|
if (this.from) {
|
|
this.from.reset();
|
|
}
|
|
if (this.to) {
|
|
this.to.reset();
|
|
}
|
|
delete this.value;
|
|
},
|
|
adjust: function (affectedSheet, operation, start, delta) {
|
|
if (this.from) {
|
|
this.from.adjust(affectedSheet, operation, start, delta);
|
|
}
|
|
if (this.to) {
|
|
this.to.adjust(affectedSheet, operation, start, delta);
|
|
}
|
|
if (this.sheet.toLowerCase() == affectedSheet.toLowerCase()) {
|
|
var formulaRow = this.row;
|
|
var formulaCol = this.col;
|
|
switch (operation) {
|
|
case 'row':
|
|
if (formulaRow >= start) {
|
|
this.row += delta;
|
|
}
|
|
break;
|
|
case 'col':
|
|
if (formulaCol >= start) {
|
|
this.col += delta;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
var options = this._getOptions();
|
|
if (options.from) {
|
|
options.from = options.from.toString();
|
|
if (options.dataType === 'list') {
|
|
options.from = options.from.replace(/^_matrix\((.*)\)$/i, '$1');
|
|
}
|
|
if (options.dataType === 'date') {
|
|
if (this.fromIsDateValue) {
|
|
options.from = options.from.replace(/^DATEVALUE\("(.*)"\)$/i, '$1');
|
|
}
|
|
}
|
|
}
|
|
if (options.to) {
|
|
options.to = options.to.toString();
|
|
if (options.dataType === 'date') {
|
|
if (this.toIsDateValue) {
|
|
options.to = options.to.replace(/^DATEVALUE\("(.*)"\)$/i, '$1');
|
|
}
|
|
}
|
|
}
|
|
return options;
|
|
},
|
|
_getOptions: function () {
|
|
return {
|
|
from: this.from,
|
|
to: this.to,
|
|
dataType: this.dataType,
|
|
type: this.type,
|
|
comparerType: this.comparerType,
|
|
row: this.row,
|
|
col: this.col,
|
|
sheet: this.sheet,
|
|
allowNulls: this.allowNulls,
|
|
tooltipMessageTemplate: this.tooltipMessageTemplate,
|
|
tooltipTitleTemplate: this.tooltipTitleTemplate,
|
|
messageTemplate: this.messageTemplate,
|
|
titleTemplate: this.titleTemplate
|
|
};
|
|
}
|
|
});
|
|
exports.compile = compileValidation;
|
|
exports.validationComparers = {
|
|
greaterThan: function (valueToCompare, from) {
|
|
return valueToCompare > from;
|
|
},
|
|
lessThan: function (valueToCompare, from) {
|
|
return valueToCompare < from;
|
|
},
|
|
between: function (valueToCompare, from, to) {
|
|
return valueToCompare > from && valueToCompare < to;
|
|
},
|
|
equalTo: function (valueToCompare, from) {
|
|
return valueToCompare == from;
|
|
},
|
|
notEqualTo: function (valueToCompare, from) {
|
|
return valueToCompare != from;
|
|
},
|
|
greaterThanOrEqualTo: function (valueToCompare, from) {
|
|
return valueToCompare >= from;
|
|
},
|
|
lessThanOrEqualTo: function (valueToCompare, from) {
|
|
return valueToCompare <= from;
|
|
},
|
|
notBetween: function (valueToCompare, from, to) {
|
|
return valueToCompare < from || valueToCompare > to;
|
|
},
|
|
custom: function (valueToCompare, from) {
|
|
return from;
|
|
},
|
|
list: function (valueToCompare, data) {
|
|
return data.indexOf(valueToCompare) > -1;
|
|
}
|
|
};
|
|
exports.Validation = Validation;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/sheet', [
|
|
'kendo.core',
|
|
'kendo.color',
|
|
'spreadsheet/runtime',
|
|
'spreadsheet/validation',
|
|
'spreadsheet/references'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var RangeRef = kendo.spreadsheet.RangeRef;
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var Range = kendo.spreadsheet.Range;
|
|
var Selection = kendo.Class.extend({
|
|
init: function (sheet) {
|
|
this._sheet = sheet;
|
|
this.selection = kendo.spreadsheet.FIRSTREF.toRangeRef();
|
|
this.originalSelection = kendo.spreadsheet.FIRSTREF.toRangeRef();
|
|
this._activeCell = kendo.spreadsheet.FIRSTREF.toRangeRef();
|
|
this.originalActiveCell = kendo.spreadsheet.FIRSTREF;
|
|
},
|
|
currentSelectionRange: function () {
|
|
return this.selection.rangeAt(this.selectionRangeIndex).toRangeRef();
|
|
},
|
|
currentOriginalNavigationRange: function () {
|
|
return this.originalSelection.rangeAt(this.selectionRangeIndex).toRangeRef();
|
|
},
|
|
currentNavigationRange: function () {
|
|
if (this.singleCellSelection()) {
|
|
return this._sheet._sheetRef;
|
|
} else {
|
|
return this.selection.rangeAt(this.selectionRangeIndex).toRangeRef();
|
|
}
|
|
},
|
|
nextNavigationRange: function () {
|
|
if (!this.singleCellSelection()) {
|
|
this.selectionRangeIndex = this.selection.nextRangeIndex(this.selectionRangeIndex);
|
|
}
|
|
return this.currentNavigationRange();
|
|
},
|
|
previousNavigationRange: function () {
|
|
if (!this.singleCellSelection()) {
|
|
this.selectionRangeIndex = this.selection.previousRangeIndex(this.selectionRangeIndex);
|
|
}
|
|
return this.currentNavigationRange();
|
|
},
|
|
activeCell: function (ref) {
|
|
if (ref) {
|
|
this.originalActiveCell = ref.first();
|
|
this._activeCell = this._sheet.unionWithMerged(ref.toRangeRef());
|
|
this._sheet.focus(ref);
|
|
this._sheet.triggerChange({
|
|
activeCell: true,
|
|
selection: true
|
|
});
|
|
}
|
|
return this._activeCell;
|
|
},
|
|
select: function (ref, expanded, changeActiveCell) {
|
|
if (ref) {
|
|
if (ref.eq(this.originalSelection)) {
|
|
return;
|
|
}
|
|
this.originalSelection = ref;
|
|
this.selection = expanded;
|
|
if (changeActiveCell !== false) {
|
|
if (ref.isCell()) {
|
|
this.activeCell(ref);
|
|
} else {
|
|
this.activeCell(this.selection.lastRange().first());
|
|
}
|
|
this.selectionRangeIndex = this.selection.size() - 1;
|
|
} else {
|
|
this._sheet.triggerChange({ selection: true });
|
|
}
|
|
}
|
|
return this.selection;
|
|
},
|
|
singleCellSelection: function () {
|
|
return this._activeCell.eq(this.selection);
|
|
}
|
|
});
|
|
var Sheet = kendo.Observable.extend({
|
|
init: function (rowCount, columnCount, rowHeight, columnWidth, headerHeight, headerWidth) {
|
|
kendo.Observable.prototype.init.call(this);
|
|
var cellCount = rowCount * columnCount - 1;
|
|
this._rows = new kendo.spreadsheet.Axis(rowCount, rowHeight);
|
|
this._columns = new kendo.spreadsheet.Axis(columnCount, columnWidth);
|
|
this._mergedCells = [];
|
|
this._frozenRows = 0;
|
|
this._frozenColumns = 0;
|
|
this._suspendChanges = false;
|
|
this._filter = null;
|
|
this._grid = new kendo.spreadsheet.Grid(this._rows, this._columns, rowCount, columnCount, headerHeight, headerWidth);
|
|
this._sheetRef = this._grid.normalize(kendo.spreadsheet.SHEETREF);
|
|
this._properties = new kendo.spreadsheet.PropertyBag(cellCount);
|
|
this._sorter = new kendo.spreadsheet.Sorter(this._grid, this._properties.sortable());
|
|
this._viewSelection = new Selection(this);
|
|
this._editSelection = new Selection(this);
|
|
this._formulaSelections = [];
|
|
this.options = { showGridLines: true };
|
|
},
|
|
_selectionState: function () {
|
|
return this._inEdit ? this._editSelection : this._viewSelection;
|
|
},
|
|
navigator: function () {
|
|
if (!this._navigator) {
|
|
this._navigator = new kendo.spreadsheet.SheetNavigator(this);
|
|
}
|
|
return this._navigator;
|
|
},
|
|
axisManager: function () {
|
|
if (!this._axisManager) {
|
|
this._axisManager = new kendo.spreadsheet.AxisManager(this);
|
|
}
|
|
return this._axisManager;
|
|
},
|
|
_name: function (value) {
|
|
if (!value) {
|
|
return this._sheetName;
|
|
}
|
|
this._sheetName = value;
|
|
return this;
|
|
},
|
|
name: function () {
|
|
return this._name();
|
|
},
|
|
_property: function (accessor, value, reason) {
|
|
if (value === undefined) {
|
|
return accessor();
|
|
} else {
|
|
accessor(value);
|
|
return this.triggerChange(reason);
|
|
}
|
|
},
|
|
_field: function (name, value, reason) {
|
|
if (value === undefined) {
|
|
return this[name];
|
|
} else {
|
|
this[name] = value;
|
|
return this.triggerChange(reason);
|
|
}
|
|
},
|
|
suspendChanges: function (value) {
|
|
if (value === undefined) {
|
|
return this._suspendChanges;
|
|
}
|
|
this._suspendChanges = value;
|
|
return this;
|
|
},
|
|
triggerChange: function (reason) {
|
|
if (!this._suspendChanges) {
|
|
this.trigger('change', reason);
|
|
}
|
|
return this;
|
|
},
|
|
setDataSource: function (dataSource, columns) {
|
|
if (this.dataSourceBinder) {
|
|
this.dataSourceBinder.destroy();
|
|
}
|
|
this.dataSourceBinder = new kendo.spreadsheet.SheetDataSourceBinder({
|
|
dataSource: dataSource,
|
|
sheet: this,
|
|
columns: columns
|
|
});
|
|
},
|
|
hideColumn: function (columnIndex) {
|
|
return this._property(this._columns.hide.bind(this._columns), columnIndex, { layout: true });
|
|
},
|
|
unhideColumn: function (columnIndex) {
|
|
return this._property(this._columns.unhide.bind(this._columns), columnIndex, { layout: true });
|
|
},
|
|
isHiddenColumn: function (columnIndex) {
|
|
return this._grid._columns.hidden(columnIndex);
|
|
},
|
|
_copyRange: function (sourceRangeRef, targetRef) {
|
|
var grid = this._grid;
|
|
var rowCount = grid.rowCount;
|
|
var nextRefTopLeft = grid.normalize(sourceRangeRef.topLeft);
|
|
var nextRefBottomRight = grid.normalize(sourceRangeRef.bottomRight);
|
|
var nextIndex = nextRefTopLeft.col * rowCount + nextRefTopLeft.row;
|
|
var nextBottomIndex = nextRefBottomRight.col * rowCount + nextRefBottomRight.row;
|
|
var targetIndex = targetRef.col * rowCount + targetRef.row;
|
|
this._properties.copy(nextIndex, nextBottomIndex, targetIndex);
|
|
},
|
|
_adjustReferences: function (operation, start, delta, mergedCells) {
|
|
this._mergedCells = mergedCells.reduce(function (a, ref) {
|
|
ref = ref.adjust(null, null, null, null, operation == 'row', start, delta);
|
|
if (ref !== kendo.spreadsheet.NULLREF) {
|
|
a.push(ref);
|
|
}
|
|
return a;
|
|
}, []);
|
|
if (this._workbook) {
|
|
var affectedSheet = this._name();
|
|
this._workbook._sheets.forEach(function (sheet) {
|
|
sheet._forFormulas(function (formula) {
|
|
formula.adjust(affectedSheet, operation, start, delta);
|
|
});
|
|
sheet._forValidations(function (validation) {
|
|
validation.adjust(affectedSheet, operation, start, delta);
|
|
});
|
|
});
|
|
this._workbook.adjustNames(affectedSheet, operation == 'row', start, delta);
|
|
}
|
|
var selection = this.select();
|
|
selection = selection.adjust(null, null, null, null, operation == 'row', start, delta);
|
|
if (selection !== kendo.spreadsheet.NULLREF) {
|
|
this.select(selection);
|
|
}
|
|
},
|
|
_forFormulas: function (callback) {
|
|
var props = this._properties;
|
|
var formulas = props.get('formula').values();
|
|
var n = formulas.length;
|
|
formulas.forEach(function (f, i) {
|
|
callback.call(this, f.value, i, n);
|
|
}, this);
|
|
},
|
|
_forValidations: function (callback) {
|
|
var props = this._properties;
|
|
props.get('validation').values().forEach(function (v) {
|
|
callback.call(this, v.value);
|
|
}, this);
|
|
},
|
|
canInsertRow: function (rowIndex, count) {
|
|
count = count || 1;
|
|
var grid = this._grid;
|
|
var range = this.range(grid.rowCount - count, 0, count, grid.columnCount);
|
|
return !range.hasValue();
|
|
},
|
|
insertRow: function (rowIndex) {
|
|
if (!this.canInsertRow(rowIndex)) {
|
|
throw new Error('Shifting nonblank cells off the worksheet is not supported!');
|
|
}
|
|
this.batch(function () {
|
|
var grid = this._grid;
|
|
var columnCount = grid.columnCount;
|
|
var rowCount = grid.rowCount;
|
|
var frozenRows = this.frozenRows();
|
|
if (rowIndex < frozenRows) {
|
|
this.frozenRows(frozenRows + 1);
|
|
}
|
|
var mergedCells = this._mergedCells.slice();
|
|
for (var ci = 0; ci < columnCount; ci++) {
|
|
var ref = new RangeRef(new CellRef(rowIndex, ci), new CellRef(rowIndex, ci));
|
|
var topLeft = grid.normalize(ref.topLeft);
|
|
var bottomRight = grid.normalize(ref.bottomRight);
|
|
var nextRef = new RangeRef(new CellRef(topLeft.row, topLeft.col), new CellRef(rowCount - 2, bottomRight.col));
|
|
this._copyRange(nextRef, new CellRef(topLeft.row + 1, topLeft.col));
|
|
new Range(ref, this).clear();
|
|
}
|
|
this._adjustReferences('row', rowIndex, 1, mergedCells);
|
|
}, {
|
|
recalc: true,
|
|
layout: true
|
|
});
|
|
this.trigger('insertRow', { index: rowIndex });
|
|
return this;
|
|
},
|
|
isEnabledRow: function (rowIndex) {
|
|
var ref = new RangeRef(new CellRef(rowIndex, 0), new CellRef(rowIndex, this._grid.columnCount));
|
|
return new Range(ref, this).enable();
|
|
},
|
|
deleteRow: function (rowIndex) {
|
|
if (!this.isEnabledRow(rowIndex)) {
|
|
return this;
|
|
}
|
|
this.batch(function () {
|
|
var grid = this._grid;
|
|
var columnCount = grid.columnCount;
|
|
var frozenRows = this.frozenRows();
|
|
if (rowIndex < frozenRows) {
|
|
this.frozenRows(frozenRows - 1);
|
|
}
|
|
var mergedCells = this._mergedCells.slice();
|
|
for (var ci = 0; ci < columnCount; ci++) {
|
|
var ref = new RangeRef(new CellRef(rowIndex, ci), new CellRef(rowIndex, ci));
|
|
new Range(ref, this).clear();
|
|
var topLeft = grid.normalize(ref.topLeft);
|
|
var bottomRight = grid.normalize(ref.bottomRight);
|
|
var nextRef = new RangeRef(new CellRef(topLeft.row + 1, topLeft.col), new CellRef(Infinity, bottomRight.col));
|
|
this._copyRange(nextRef, topLeft);
|
|
var nextRefBottomRight = grid.normalize(nextRef.bottomRight);
|
|
new Range(new RangeRef(nextRefBottomRight, nextRefBottomRight), this).clear();
|
|
}
|
|
this._adjustReferences('row', rowIndex, -1, mergedCells);
|
|
}, {
|
|
recalc: true,
|
|
layout: true
|
|
});
|
|
this.trigger('deleteRow', { index: rowIndex });
|
|
return this;
|
|
},
|
|
insertColumn: function (columnIndex) {
|
|
this.batch(function () {
|
|
var grid = this._grid;
|
|
var columnCount = grid.columnCount;
|
|
var frozenColumns = this.frozenColumns();
|
|
if (columnIndex < frozenColumns) {
|
|
this.frozenColumns(frozenColumns + 1);
|
|
}
|
|
var mergedCells = this._mergedCells.slice();
|
|
for (var ci = columnCount; ci >= columnIndex; ci--) {
|
|
var ref = new RangeRef(new CellRef(0, ci), new CellRef(Infinity, ci));
|
|
new Range(ref, this).clear();
|
|
if (ci == columnIndex) {
|
|
break;
|
|
}
|
|
var topLeft = grid.normalize(ref.topLeft);
|
|
var bottomRight = grid.normalize(ref.bottomRight);
|
|
var nextRef = new RangeRef(new CellRef(topLeft.row, topLeft.col - 1), new CellRef(bottomRight.row, bottomRight.col - 1));
|
|
this._copyRange(nextRef, topLeft);
|
|
}
|
|
this._adjustReferences('col', columnIndex, 1, mergedCells);
|
|
}, {
|
|
recalc: true,
|
|
layout: true
|
|
});
|
|
return this;
|
|
},
|
|
isEnabledColumn: function (columnIndex) {
|
|
var ref = new RangeRef(new CellRef(0, columnIndex), new CellRef(Infinity, columnIndex));
|
|
return new Range(ref, this).enable();
|
|
},
|
|
deleteColumn: function (columnIndex) {
|
|
if (!this.isEnabledColumn(columnIndex)) {
|
|
return this;
|
|
}
|
|
this.batch(function () {
|
|
var grid = this._grid;
|
|
var columnCount = grid.columnCount;
|
|
var frozenColumns = this.frozenColumns();
|
|
if (columnIndex < frozenColumns) {
|
|
this.frozenColumns(frozenColumns - 1);
|
|
}
|
|
var mergedCells = this._mergedCells.slice();
|
|
for (var ci = columnIndex; ci < columnCount; ci++) {
|
|
var ref = new RangeRef(new CellRef(0, ci), new CellRef(Infinity, ci));
|
|
new Range(ref, this).clear();
|
|
if (ci == columnCount - 1) {
|
|
break;
|
|
}
|
|
var topLeft = grid.normalize(ref.topLeft);
|
|
var bottomRight = grid.normalize(ref.bottomRight);
|
|
var nextRef = new RangeRef(new CellRef(topLeft.row, topLeft.col + 1), new CellRef(bottomRight.row, bottomRight.col + 1));
|
|
this._copyRange(nextRef, topLeft);
|
|
}
|
|
this._adjustReferences('col', columnIndex, -1, mergedCells);
|
|
}, {
|
|
recalc: true,
|
|
layout: true
|
|
});
|
|
return this;
|
|
},
|
|
hideRow: function (rowIndex) {
|
|
return this._property(this._rows.hide.bind(this._rows), rowIndex, { layout: true });
|
|
},
|
|
unhideRow: function (rowIndex) {
|
|
return this._property(this._rows.unhide.bind(this._rows), rowIndex, { layout: true });
|
|
},
|
|
isHiddenRow: function (rowIndex) {
|
|
return this._grid._rows.hidden(rowIndex);
|
|
},
|
|
columnWidth: function (columnIndex, width) {
|
|
return this._property(this._columns.value.bind(this._columns, columnIndex, columnIndex), width, { layout: true });
|
|
},
|
|
rowHeight: function (rowIndex, height) {
|
|
return this._property(this._rows.value.bind(this._rows, rowIndex, rowIndex), height, { layout: true });
|
|
},
|
|
frozenRows: function (value) {
|
|
return this._field('_frozenRows', value, { layout: true });
|
|
},
|
|
frozenColumns: function (value) {
|
|
return this._field('_frozenColumns', value, { layout: true });
|
|
},
|
|
_ref: function (row, column, numRows, numColumns) {
|
|
var ref = null;
|
|
if (row instanceof kendo.spreadsheet.Ref) {
|
|
return row;
|
|
}
|
|
if (row instanceof kendo.spreadsheet.Range) {
|
|
return row._ref.toRangeRef();
|
|
}
|
|
if (typeof row === 'string') {
|
|
ref = kendo.spreadsheet.calc.parseReference(row);
|
|
} else {
|
|
if (!numRows) {
|
|
numRows = 1;
|
|
}
|
|
if (!numColumns) {
|
|
numColumns = 1;
|
|
}
|
|
ref = new RangeRef(new CellRef(row, column), new CellRef(row + numRows - 1, column + numColumns - 1));
|
|
}
|
|
return ref;
|
|
},
|
|
range: function (row, column, numRows, numColumns) {
|
|
return new Range(this._ref(row, column, numRows, numColumns), this);
|
|
},
|
|
forEachMergedCell: function (ref, callback) {
|
|
var selectAll = false;
|
|
if (typeof callback === 'undefined') {
|
|
callback = ref;
|
|
selectAll = true;
|
|
}
|
|
this._mergedCells.forEach(function (merged) {
|
|
if (selectAll || merged.intersects(ref)) {
|
|
callback(merged);
|
|
}
|
|
});
|
|
},
|
|
forEachFilterHeader: function (ref, callback) {
|
|
var selectAll = false;
|
|
if (typeof callback === 'undefined') {
|
|
callback = ref;
|
|
selectAll = true;
|
|
}
|
|
if (this._filter) {
|
|
var refs = [];
|
|
this._filter.ref.forEachColumn(function (columnRef) {
|
|
if (selectAll || columnRef.intersects(ref)) {
|
|
refs.push(columnRef.topLeft);
|
|
}
|
|
});
|
|
this._mergedCells.forEach(function (merged) {
|
|
refs = refs.map(function (ref) {
|
|
if (merged.intersects(ref)) {
|
|
return merged;
|
|
}
|
|
return ref;
|
|
});
|
|
});
|
|
refs.reduce(function unique(result, element) {
|
|
if (result.indexOf(element) < 0) {
|
|
result.push(element);
|
|
}
|
|
return result;
|
|
}, []).forEach(callback);
|
|
}
|
|
},
|
|
forEach: function (ref, callback) {
|
|
if (!(ref instanceof RangeRef)) {
|
|
ref = this._ref(ref);
|
|
}
|
|
var topLeft = this._grid.normalize(ref.topLeft);
|
|
var bottomRight = this._grid.normalize(ref.bottomRight);
|
|
for (var ci = topLeft.col; ci <= bottomRight.col; ci++) {
|
|
var ri = topLeft.row;
|
|
var startCellIndex = this._grid.index(ri, ci);
|
|
var endCellIndex = this._grid.index(bottomRight.row, ci);
|
|
this._properties.forEach(startCellIndex, endCellIndex, function (value) {
|
|
callback(ri++, ci, value);
|
|
});
|
|
}
|
|
},
|
|
startResizing: function (initialPosition) {
|
|
this._initialPosition = initialPosition;
|
|
this._resizeInProgress = true;
|
|
},
|
|
startAutoFill: function () {
|
|
this._autoFillInProgress = true;
|
|
var selection = this.select();
|
|
this._autoFillOrigin = selection;
|
|
this._autoFillDest = selection;
|
|
this.triggerChange({ selection: true });
|
|
},
|
|
updateAutoFill: function (dest, punch, hint, direction) {
|
|
this._autoFillDest = dest;
|
|
this._autoFillPunch = punch;
|
|
this._autoFillHint = hint;
|
|
this._autoFillDirection = direction;
|
|
this.triggerChange({ selection: true });
|
|
},
|
|
autoFillRef: function () {
|
|
return this._autoFillDest;
|
|
},
|
|
autoFillPunch: function () {
|
|
return this._autoFillPunch;
|
|
},
|
|
autoFillInProgress: function () {
|
|
return this._autoFillInProgress;
|
|
},
|
|
resizingInProgress: function () {
|
|
return this._resizeInProgress;
|
|
},
|
|
completeResizing: function () {
|
|
if (this._resizeInProgress) {
|
|
this._resizeInProgress = false;
|
|
var hintPosition = this.resizeHintPosition();
|
|
if (this._initialPosition && hintPosition) {
|
|
var handlePosition = this.resizeHandlePosition();
|
|
if (handlePosition.col !== -Infinity) {
|
|
this.columnWidth(handlePosition.col, this.columnWidth(handlePosition.col) - (this._initialPosition.x - hintPosition.x));
|
|
} else {
|
|
this.rowHeight(handlePosition.row, this.rowHeight(handlePosition.row) - (this._initialPosition.y - hintPosition.y));
|
|
}
|
|
} else {
|
|
this.trigger('change', { resize: true });
|
|
}
|
|
}
|
|
},
|
|
resizeHandlePosition: function () {
|
|
return this._resizeHandlePosition;
|
|
},
|
|
resizeHintPosition: function (location) {
|
|
if (location !== undefined) {
|
|
this._resizeHintPosition = location;
|
|
this.trigger('change', { resize: true });
|
|
}
|
|
return this._resizeHintPosition;
|
|
},
|
|
removeResizeHandle: function () {
|
|
if (this._resizeHandlePosition) {
|
|
this._resizeHintPosition = undefined;
|
|
this._resizeHandlePosition = undefined;
|
|
this._initialPosition = undefined;
|
|
this.trigger('change', { resize: true });
|
|
}
|
|
},
|
|
positionResizeHandle: function (ref) {
|
|
this._resizeHandlePosition = ref;
|
|
this.trigger('change', { resize: true });
|
|
},
|
|
startSelection: function () {
|
|
this._selectionInProgress = true;
|
|
},
|
|
completeSelection: function () {
|
|
if (this._selectionInProgress) {
|
|
this._selectionInProgress = false;
|
|
this._resizeHintPosition = undefined;
|
|
this.trigger('change', { selection: true });
|
|
}
|
|
if (this._autoFillInProgress) {
|
|
this._autoFillInProgress = false;
|
|
var dest = this._autoFillDest;
|
|
var origin = this._autoFillOrigin;
|
|
if (this._autoFillPunch) {
|
|
this._workbook.execute({
|
|
command: 'ClearContentCommand',
|
|
options: { operatingRange: this.range(this._autoFillPunch) }
|
|
});
|
|
} else {
|
|
if (!dest.eq(origin)) {
|
|
this._workbook.execute({
|
|
command: 'AutoFillCommand',
|
|
options: {
|
|
operatingRange: this.range(dest),
|
|
origin: this.range(origin)
|
|
}
|
|
});
|
|
} else {
|
|
this.triggerChange({ selection: true });
|
|
}
|
|
}
|
|
this._autoFillDest = null;
|
|
this._autoFillPunch = null;
|
|
this._autoFillOrigin = null;
|
|
this.select(dest);
|
|
}
|
|
},
|
|
selectionInProgress: function () {
|
|
return this._selectionInProgress;
|
|
},
|
|
select: function (ref, changeActiveCell) {
|
|
var selectionState = this._selectionState();
|
|
var expandedRef;
|
|
if (ref) {
|
|
ref = this._ref(ref);
|
|
expandedRef = this._grid.isAxis(ref) ? ref : this.unionWithMerged(ref);
|
|
}
|
|
return selectionState.select(ref, expandedRef, changeActiveCell);
|
|
},
|
|
originalSelect: function () {
|
|
return this._selectionState().originalSelection;
|
|
},
|
|
currentSelectionRange: function () {
|
|
return this._selectionState().currentSelectionRange();
|
|
},
|
|
currentOriginalSelectionRange: function () {
|
|
return this._selectionState().currentOriginalNavigationRange();
|
|
},
|
|
currentNavigationRange: function () {
|
|
return this._selectionState().currentNavigationRange();
|
|
},
|
|
nextNavigationRange: function () {
|
|
return this._selectionState().nextNavigationRange();
|
|
},
|
|
previousNavigationRange: function () {
|
|
return this._selectionState().previousNavigationRange();
|
|
},
|
|
selectionRangeIndex: function () {
|
|
return this._selectionState().selectionRangeIndex;
|
|
},
|
|
activeCell: function (ref) {
|
|
return this._selectionState().activeCell(ref);
|
|
},
|
|
originalActiveCell: function () {
|
|
return this._selectionState().originalActiveCell;
|
|
},
|
|
singleCellSelection: function () {
|
|
return this._selectionState().singleCellSelection();
|
|
},
|
|
unionWithMerged: function (ref) {
|
|
var mergedCells = this._mergedCells;
|
|
return ref.map(function (ref) {
|
|
return ref.toRangeRef().union(mergedCells);
|
|
});
|
|
},
|
|
trim: function (ref) {
|
|
var trims = [];
|
|
var grid = this._grid;
|
|
this._properties.forEachProperty(function (property) {
|
|
trims.push(grid.trim(ref, property.list));
|
|
});
|
|
return this.unionWithMerged(ref.topLeft.toRangeRef().union(trims));
|
|
},
|
|
focus: function (ref) {
|
|
if (ref) {
|
|
this._focus = ref.toRangeRef();
|
|
} else {
|
|
var focus = this._focus;
|
|
this._focus = null;
|
|
return focus;
|
|
}
|
|
},
|
|
activeCellSelection: function () {
|
|
return new Range(this._grid.normalize(this.activeCell()), this);
|
|
},
|
|
selection: function () {
|
|
return new Range(this._grid.normalize(this._selectionState().selection), this);
|
|
},
|
|
selectedHeaders: function () {
|
|
var selection = this.select();
|
|
var rows = {};
|
|
var cols = {};
|
|
var allCols = false;
|
|
var allRows = false;
|
|
selection.forEach(function (ref) {
|
|
var i;
|
|
var rowState = 'partial';
|
|
var colState = 'partial';
|
|
ref = ref.toRangeRef();
|
|
var bottomRight = ref.bottomRight;
|
|
var rowSelection = bottomRight.col === Infinity;
|
|
var colSelection = bottomRight.row === Infinity;
|
|
if (colSelection) {
|
|
allRows = true;
|
|
colState = 'full';
|
|
}
|
|
if (rowSelection) {
|
|
allCols = true;
|
|
rowState = 'full';
|
|
}
|
|
if (!colSelection) {
|
|
for (i = ref.topLeft.row; i <= bottomRight.row; i++) {
|
|
if (rows[i] !== 'full') {
|
|
rows[i] = rowState;
|
|
}
|
|
}
|
|
}
|
|
if (!rowSelection) {
|
|
for (i = ref.topLeft.col; i <= bottomRight.col; i++) {
|
|
if (cols[i] !== 'full') {
|
|
cols[i] = colState;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return {
|
|
rows: rows,
|
|
cols: cols,
|
|
allRows: allRows,
|
|
allCols: allCols,
|
|
all: allRows && allCols
|
|
};
|
|
},
|
|
isInEditMode: function (isInEdit) {
|
|
if (isInEdit === undefined) {
|
|
return this._inEdit;
|
|
}
|
|
this._inEdit = isInEdit;
|
|
if (isInEdit) {
|
|
this._editSelection.selection = this._viewSelection.selection.clone();
|
|
this._editSelection.originalSelection = this._viewSelection.originalSelection.clone();
|
|
this._editSelection._activeCell = this._viewSelection._activeCell.clone();
|
|
this._editSelection.originalActiveCell = this._viewSelection.originalActiveCell.clone();
|
|
}
|
|
},
|
|
_setFormulaSelections: function (selection) {
|
|
this._formulaSelections = (selection || []).slice();
|
|
this.triggerChange({ selection: true });
|
|
},
|
|
_viewActiveCell: function () {
|
|
return this._viewSelection._activeCell.toRangeRef();
|
|
},
|
|
toJSON: function () {
|
|
var positions = {};
|
|
var rows = this._rows.toJSON('height', positions);
|
|
var columns = this._columns.toJSON('width', {});
|
|
var viewSelection = this._viewSelection;
|
|
this.forEach(kendo.spreadsheet.SHEETREF, function (row, col, cell) {
|
|
if (Object.keys(cell).length === 0) {
|
|
return;
|
|
}
|
|
var position = positions[row];
|
|
if (position === undefined) {
|
|
position = rows.length;
|
|
rows.push({ index: row });
|
|
positions[row] = position;
|
|
}
|
|
row = rows[position];
|
|
cell.index = col;
|
|
if (row.cells === undefined) {
|
|
row.cells = [];
|
|
}
|
|
if (cell.formula) {
|
|
cell.formula = cell.formula.toString();
|
|
}
|
|
if (cell.validation) {
|
|
cell.validation = cell.validation.toJSON();
|
|
}
|
|
if (cell.color) {
|
|
cell.color = kendo.parseColor(cell.color).toCss();
|
|
}
|
|
if (cell.background) {
|
|
cell.background = kendo.parseColor(cell.background).toCss();
|
|
}
|
|
if (cell.borderTop && cell.borderTop.color) {
|
|
cell.borderTop.color = kendo.parseColor(cell.borderTop.color).toCss();
|
|
}
|
|
if (cell.borderBottom && cell.borderBottom.color) {
|
|
cell.borderBottom.color = kendo.parseColor(cell.borderBottom.color).toCss();
|
|
}
|
|
if (cell.borderRight && cell.borderRight.color) {
|
|
cell.borderRight.color = kendo.parseColor(cell.borderRight.color).toCss();
|
|
}
|
|
if (cell.borderLeft && cell.borderLeft.color) {
|
|
cell.borderLeft.color = kendo.parseColor(cell.borderLeft.color).toCss();
|
|
}
|
|
row.cells.push(cell);
|
|
});
|
|
var json = {
|
|
name: this._name(),
|
|
rows: rows,
|
|
columns: columns,
|
|
selection: viewSelection.selection.toString(),
|
|
activeCell: viewSelection.activeCell().toString(),
|
|
frozenRows: this.frozenRows(),
|
|
frozenColumns: this.frozenColumns(),
|
|
mergedCells: this._mergedCells.map(function (ref) {
|
|
return ref.toString();
|
|
})
|
|
};
|
|
if (this._sort) {
|
|
json.sort = {
|
|
ref: this._sort.ref.toString(),
|
|
columns: this._sort.columns.map(function (column) {
|
|
return {
|
|
index: column.index,
|
|
ascending: column.ascending
|
|
};
|
|
})
|
|
};
|
|
}
|
|
if (this._filter) {
|
|
json.filter = {
|
|
ref: this._filter.ref.toString(),
|
|
columns: this._filter.columns.map(function (column) {
|
|
var filter = column.filter.toJSON();
|
|
filter.index = column.index;
|
|
return filter;
|
|
})
|
|
};
|
|
}
|
|
return json;
|
|
},
|
|
fromJSON: function (json) {
|
|
this.batch(function () {
|
|
if (json.name !== undefined) {
|
|
this._name(json.name);
|
|
}
|
|
if (json.frozenColumns !== undefined) {
|
|
this.frozenColumns(json.frozenColumns);
|
|
}
|
|
if (json.frozenRows !== undefined) {
|
|
this.frozenRows(json.frozenRows);
|
|
}
|
|
if (json.columns !== undefined) {
|
|
this._columns.fromJSON('width', json.columns);
|
|
}
|
|
if (json.rows !== undefined) {
|
|
this._rows.fromJSON('height', json.rows);
|
|
for (var ri = 0; ri < json.rows.length; ri++) {
|
|
var row = json.rows[ri];
|
|
var rowIndex = row.index;
|
|
if (rowIndex === undefined) {
|
|
rowIndex = ri;
|
|
}
|
|
if (row.cells) {
|
|
for (var ci = 0; ci < row.cells.length; ci++) {
|
|
var cell = row.cells[ci];
|
|
var columnIndex = cell.index;
|
|
if (columnIndex === undefined) {
|
|
columnIndex = ci;
|
|
}
|
|
if (cell.formula) {
|
|
cell.formula = this._compileFormula(rowIndex, columnIndex, cell.formula);
|
|
}
|
|
if (cell.validation) {
|
|
cell.validation = this._compileValidation(rowIndex, columnIndex, cell.validation);
|
|
}
|
|
this._properties.fromJSON(this._grid.index(rowIndex, columnIndex), cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (json.selection) {
|
|
this._viewSelection.selection = this._viewSelection.originalSelection = this._ref(json.selection);
|
|
}
|
|
if (json.activeCell) {
|
|
var activeCellRef = this._ref(json.activeCell);
|
|
this._viewSelection._activeCell = activeCellRef.toRangeRef();
|
|
this._viewSelection.originalActiveCell = activeCellRef;
|
|
}
|
|
if (json.mergedCells) {
|
|
json.mergedCells.forEach(function (ref) {
|
|
this.range(ref).merge();
|
|
}, this);
|
|
}
|
|
if (json.sort) {
|
|
this._sort = {
|
|
ref: this._ref(json.sort.ref),
|
|
columns: json.sort.columns.slice(0)
|
|
};
|
|
}
|
|
if (json.filter) {
|
|
var ref = json.filter.ref;
|
|
var columns = json.filter.columns === undefined ? [] : json.filter.columns;
|
|
if (!ref) {
|
|
kendo.logToConsole('Dropping filter for sheet \'' + json.name + '\' due to missing ref');
|
|
} else {
|
|
this._filter = {
|
|
ref: this._ref(ref),
|
|
columns: columns.map(function (column) {
|
|
return {
|
|
index: column.index,
|
|
filter: kendo.spreadsheet.Filter.create(column)
|
|
};
|
|
})
|
|
};
|
|
this._refreshFilter();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
formula: function (ref) {
|
|
return this._properties.get('formula', this._grid.cellRefIndex(ref));
|
|
},
|
|
validation: function (ref) {
|
|
return this._properties.get('validation', this._grid.cellRefIndex(ref));
|
|
},
|
|
resetFormulas: function () {
|
|
this._forFormulas(function (formula) {
|
|
formula.reset();
|
|
});
|
|
},
|
|
resetValidations: function () {
|
|
this._forValidations(function (validation) {
|
|
validation.reset();
|
|
});
|
|
},
|
|
recalc: function (context, callback) {
|
|
var formulas = this._properties.get('formula').values();
|
|
var count = formulas.length, pending = 0, i = 0;
|
|
if (!count && callback) {
|
|
return callback();
|
|
}
|
|
function next() {
|
|
pending--;
|
|
if (i == count && !pending) {
|
|
callback();
|
|
}
|
|
}
|
|
while (i < count) {
|
|
pending++;
|
|
formulas[i++].value.exec(context, callback ? next : null);
|
|
}
|
|
},
|
|
revalidate: function (context) {
|
|
var self = this;
|
|
this._forValidations(function (validation) {
|
|
var cellRef = new CellRef(validation.row, validation.col);
|
|
var ref = new RangeRef(cellRef, cellRef);
|
|
validation.exec(context, self._get(ref, 'value'), self._get(ref, 'format'));
|
|
});
|
|
},
|
|
_value: function (row, col, value) {
|
|
var index = this._grid.index(row, col);
|
|
if (value !== undefined) {
|
|
this._properties.set('value', index, index, value);
|
|
} else {
|
|
return this._properties.get('value', index);
|
|
}
|
|
},
|
|
_validation: function (row, col) {
|
|
var index = this._grid.index(row, col);
|
|
return this._properties.get('validation', index);
|
|
},
|
|
_compileValidation: function (row, col, validation) {
|
|
if (validation.from !== null || validation.from !== undefined) {
|
|
validation.from = (validation.from + '').replace(/^=/, '');
|
|
}
|
|
if (validation.to !== null || validation.to !== undefined) {
|
|
validation.to = (validation.to + '').replace(/^=/, '');
|
|
}
|
|
return kendo.spreadsheet.validation.compile(this._name(), row, col, validation);
|
|
},
|
|
_compileFormula: function (row, col, f) {
|
|
f = f.replace(/^=/, '');
|
|
f = kendo.spreadsheet.calc.parseFormula(this._name(), row, col, f);
|
|
return kendo.spreadsheet.calc.compile(f);
|
|
},
|
|
_copyValuesInRange: function (topLeft, bottomRight, value, property) {
|
|
var ci, start, end;
|
|
for (ci = topLeft.col; ci <= bottomRight.col; ci++) {
|
|
start = this._grid.index(topLeft.row, ci);
|
|
end = this._grid.index(bottomRight.row, ci);
|
|
for (var index = start, row = topLeft.row; index <= end; ++index, ++row) {
|
|
value = value.clone(this._name(), row, ci);
|
|
this._properties.set(property, index, index, value);
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
_set: function (ref, name, value) {
|
|
var topLeft = this._grid.normalize(ref.topLeft);
|
|
var bottomRight = this._grid.normalize(ref.bottomRight);
|
|
var ci, start, end;
|
|
if (value && name == 'formula') {
|
|
if (typeof value == 'string') {
|
|
value = this._compileFormula(topLeft.row, topLeft.col, value);
|
|
}
|
|
value = this._copyValuesInRange(topLeft, bottomRight, value, 'formula');
|
|
} else if (value && name == 'validation') {
|
|
value = this._compileValidation(topLeft.row, topLeft.col, value);
|
|
value = this._copyValuesInRange(topLeft, bottomRight, value, 'validation');
|
|
} else {
|
|
for (ci = topLeft.col; ci <= bottomRight.col; ci++) {
|
|
start = this._grid.index(topLeft.row, ci);
|
|
end = this._grid.index(bottomRight.row, ci);
|
|
this._properties.set(name, start, end, value);
|
|
if (name == 'formula') {
|
|
this._properties.set('value', start, end, null);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_get: function (ref, name) {
|
|
var topLeft = this._grid.normalize(ref.topLeft);
|
|
var index = this._grid.index(topLeft.row, topLeft.col);
|
|
return this._properties.get(name, index);
|
|
},
|
|
batch: function (callback, reason) {
|
|
var suspended = this.suspendChanges();
|
|
this.suspendChanges(true);
|
|
callback.call(this);
|
|
return this.suspendChanges(suspended).triggerChange(reason);
|
|
},
|
|
_sortBy: function (ref, columns) {
|
|
var indices = null;
|
|
columns.forEach(function (column) {
|
|
indices = this._sorter.sortBy(ref, column.index, this._properties.get('value'), column.ascending, indices);
|
|
}, this);
|
|
this._sort = {
|
|
ref: ref,
|
|
columns: columns
|
|
};
|
|
this._refreshFilter();
|
|
this.triggerChange({ recalc: true });
|
|
},
|
|
_refreshFilter: function () {
|
|
if (this._filter) {
|
|
this._filterBy(this._filter.ref, this._filter.columns);
|
|
}
|
|
},
|
|
_filterBy: function (ref, columns) {
|
|
this.batch(function () {
|
|
for (var ri = ref.topLeft.row; ri <= ref.bottomRight.row; ri++) {
|
|
if (this._rows.hidden(ri)) {
|
|
this._rows.unhide(ri);
|
|
}
|
|
}
|
|
columns.forEach(function (column) {
|
|
var columnRef = ref.resize({ top: 1 }).toColumn(column.index);
|
|
var cells = [];
|
|
if (columnRef === kendo.spreadsheet.NULLREF) {
|
|
return;
|
|
}
|
|
this.forEach(columnRef, function (row, col, cell) {
|
|
cell.row = row;
|
|
cells.push(cell);
|
|
});
|
|
column.filter.prepare(cells);
|
|
for (var ci = 0; ci < cells.length; ci++) {
|
|
var cell = cells[ci];
|
|
var value = column.filter.value(cell);
|
|
if (column.filter.matches(value) === false) {
|
|
this.hideRow(cell.row);
|
|
}
|
|
}
|
|
}, this);
|
|
this._filter = {
|
|
ref: ref,
|
|
columns: columns
|
|
};
|
|
}, {
|
|
layout: true,
|
|
filter: true
|
|
});
|
|
},
|
|
filterColumn: function (ref) {
|
|
var filterRef = this.filter().ref;
|
|
return ref.toRangeRef().topLeft.col - filterRef.topLeft.col;
|
|
},
|
|
filter: function () {
|
|
return this._filter;
|
|
},
|
|
clearFilter: function (spec) {
|
|
this._clearFilter(spec instanceof Array ? spec : [spec]);
|
|
},
|
|
_clearFilter: function (indices) {
|
|
if (this._filter) {
|
|
this.batch(function () {
|
|
this._filter.columns = this._filter.columns.filter(function (column) {
|
|
return indices.indexOf(column.index) < 0;
|
|
});
|
|
this._refreshFilter();
|
|
}, {
|
|
layout: true,
|
|
filter: true
|
|
});
|
|
}
|
|
},
|
|
getAxisState: function () {
|
|
return {
|
|
rows: this._rows.getState(),
|
|
columns: this._columns.getState()
|
|
};
|
|
},
|
|
setAxisState: function (state) {
|
|
this._rows.setState(state.rows);
|
|
this._columns.setState(state.columns);
|
|
this.triggerChange({ layout: true });
|
|
},
|
|
getState: function () {
|
|
return {
|
|
rows: this._rows.getState(),
|
|
columns: this._columns.getState(),
|
|
mergedCells: this._mergedCells.map(function (cell) {
|
|
return cell.clone();
|
|
}),
|
|
properties: this._properties.getState()
|
|
};
|
|
},
|
|
setState: function (state) {
|
|
this._rows.setState(state.rows);
|
|
this._columns.setState(state.columns);
|
|
this._mergedCells = state.mergedCells;
|
|
this._properties.setState(state.properties);
|
|
this.triggerChange(kendo.spreadsheet.ALL_REASONS);
|
|
},
|
|
_merge: function (ref) {
|
|
var mergedCells = this._mergedCells;
|
|
var sheet = this;
|
|
var mergedRef;
|
|
this.batch(function () {
|
|
mergedRef = ref.map(function (ref) {
|
|
if (ref instanceof kendo.spreadsheet.CellRef) {
|
|
return ref;
|
|
}
|
|
var currentRef = ref.toRangeRef().union(mergedCells, function (ref) {
|
|
mergedCells.splice(mergedCells.indexOf(ref), 1);
|
|
});
|
|
var range = new Range(currentRef, sheet);
|
|
var formula = range._get('formula');
|
|
var value = range.value();
|
|
var format = range.format();
|
|
var background = range.background();
|
|
range.value(null);
|
|
range.format(null);
|
|
range.background(null);
|
|
var topLeft = new Range(currentRef.collapse(), sheet);
|
|
if (formula) {
|
|
topLeft._set('formula', formula);
|
|
} else {
|
|
topLeft.value(value);
|
|
}
|
|
topLeft.format(format);
|
|
topLeft.background(background);
|
|
mergedCells.push(currentRef);
|
|
return currentRef;
|
|
});
|
|
var viewSelection = sheet._viewSelection;
|
|
viewSelection.selection = sheet.unionWithMerged(viewSelection.originalSelection);
|
|
viewSelection._activeCell = sheet.unionWithMerged(viewSelection.originalActiveCell);
|
|
}, {
|
|
activeCell: true,
|
|
selection: true
|
|
});
|
|
return mergedRef;
|
|
}
|
|
});
|
|
kendo.spreadsheet.Sheet = Sheet;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/sheetsbar', [
|
|
'kendo.core',
|
|
'kendo.sortable'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var DOT = '.';
|
|
var EMPTYCHAR = ' ';
|
|
var sheetsBarClassNames = {
|
|
sheetsBarWrapper: 'k-widget k-header',
|
|
sheetsBarSheetsWrapper: 'k-tabstrip k-floatwrap k-tabstrip-bottom',
|
|
sheetsBarActive: 'k-spreadsheet-sheets-bar-active',
|
|
sheetsBarInactive: 'k-spreadsheet-sheets-bar-inactive',
|
|
sheetsBarAdd: 'k-spreadsheet-sheets-bar-add',
|
|
sheetsBarRemove: 'k-spreadsheet-sheets-remove',
|
|
sheetsBarItems: 'k-spreadsheet-sheets-items',
|
|
sheetsBarEditor: 'k-spreadsheet-sheets-editor',
|
|
sheetsBarScrollable: 'k-tabstrip-scrollable',
|
|
sheetsBarNext: 'k-tabstrip-next',
|
|
sheetsBarPrev: 'k-tabstrip-prev',
|
|
sheetsBarKItem: 'k-item k-state-default',
|
|
sheetsBarKActive: 'k-state-active k-state-tab-on-top',
|
|
sheetsBarKTextbox: 'k-textbox',
|
|
sheetsBarKLink: 'k-link',
|
|
sheetsBarKIcon: 'k-icon',
|
|
sheetsBarKFontIcon: 'k-font-icon',
|
|
sheetsBarKButton: 'k-button k-button-icon',
|
|
sheetsBarKButtonBare: 'k-button-bare',
|
|
sheetsBarKArrowW: 'k-i-arrow-w',
|
|
sheetsBarKArrowE: 'k-i-arrow-e',
|
|
sheetsBarKReset: 'k-reset k-tabstrip-items',
|
|
sheetsBarKIconX: 'k-i-x',
|
|
sheetsBarKSprite: 'k-sprite',
|
|
sheetsBarKIconPlus: 'k-i-plus',
|
|
sheetsBarHintWrapper: 'k-widget k-tabstrip k-tabstrip-bottom k-spreadsheet-sheets-items-hint',
|
|
sheetsBarKResetItems: 'k-reset k-tabstrip-items'
|
|
};
|
|
var SheetsBar = kendo.ui.Widget.extend({
|
|
init: function (element, options) {
|
|
var classNames = SheetsBar.classNames;
|
|
kendo.ui.Widget.call(this, element, options);
|
|
element = this.element;
|
|
element.addClass(classNames.sheetsBarWrapper);
|
|
this._tree = new kendo.dom.Tree(element[0]);
|
|
this._tree.render([
|
|
this._addButton(),
|
|
this._createSheetsWrapper([])
|
|
]);
|
|
this._toggleScrollEvents(true);
|
|
this._createSortable();
|
|
this._sortable.bind('start', this._onSheetReorderStart.bind(this));
|
|
this._sortable.bind('end', this._onSheetReorderEnd.bind(this));
|
|
element.on('click', DOT + classNames.sheetsBarRemove, this._onSheetRemove.bind(this));
|
|
element.on('click', 'li', this._onSheetSelect.bind(this));
|
|
element.on('dblclick', 'li' + DOT + classNames.sheetsBarActive, this._createEditor.bind(this));
|
|
element.on('click', DOT + classNames.sheetsBarAdd, this._onAddSelect.bind(this));
|
|
},
|
|
options: {
|
|
name: 'SheetsBar',
|
|
scrollable: { distance: 200 }
|
|
},
|
|
events: [
|
|
'select',
|
|
'reorder',
|
|
'rename'
|
|
],
|
|
_createEditor: function () {
|
|
if (this._editor) {
|
|
return;
|
|
}
|
|
this._renderSheets(this._sheets, this._selectedIndex, true);
|
|
this._editor = this.element.find(kendo.format('input{0}{1}', DOT, SheetsBar.classNames.sheetsBarEditor)).focus().on('keydown', this._onEditorKeydown.bind(this)).on('blur', this._onEditorBlur.bind(this));
|
|
},
|
|
_destroyEditor: function () {
|
|
this._editor.off();
|
|
this._editor = null;
|
|
this._renderSheets(this._sheets, this._selectedIndex, false);
|
|
},
|
|
renderSheets: function (sheets, selectedIndex) {
|
|
if (!sheets || selectedIndex < 0) {
|
|
return;
|
|
}
|
|
this._renderSheets(sheets, selectedIndex, false);
|
|
},
|
|
_renderSheets: function (sheets, selectedIndex, isInEditMode) {
|
|
var that = this;
|
|
var wrapperOffsetWidth;
|
|
var sheetsGroupScrollWidth;
|
|
var classNames = SheetsBar.classNames;
|
|
that._isRtl = kendo.support.isRtl(that.element);
|
|
that._sheets = sheets;
|
|
that._selectedIndex = selectedIndex;
|
|
that._renderHtml(isInEditMode, true);
|
|
if (!that._scrollableAllowed()) {
|
|
return;
|
|
}
|
|
var sheetsWrapper = that._sheetsWrapper();
|
|
var scrollPrevButton = sheetsWrapper.children(DOT + classNames.sheetsBarPrev);
|
|
var scrollNextButton = sheetsWrapper.children(DOT + classNames.sheetsBarNext);
|
|
var gapWidth = 2;
|
|
var addButton = that.element.find(DOT + classNames.sheetsBarAdd);
|
|
var addButtonWidth = addButton.outerWidth() + addButton.position().left + gapWidth;
|
|
var scrollPrevButtonWidth = scrollPrevButton.outerWidth() + gapWidth;
|
|
var sheetsGroup = that._sheetsGroup();
|
|
scrollPrevButton.css({ left: addButtonWidth });
|
|
sheetsWrapper.addClass(classNames.sheetsBarScrollable + EMPTYCHAR + classNames.sheetsBarSheetsWrapper);
|
|
sheetsGroup.css({ marginLeft: addButtonWidth });
|
|
wrapperOffsetWidth = sheetsWrapper[0].offsetWidth;
|
|
sheetsGroupScrollWidth = sheetsGroup[0].scrollWidth;
|
|
if (sheetsGroupScrollWidth + addButtonWidth > wrapperOffsetWidth) {
|
|
var scrollNextButtonRight = Math.ceil(kendo.parseFloat(scrollNextButton.css('right')));
|
|
if (!that._scrollableModeActive) {
|
|
that._nowScrollingSheets = false;
|
|
that._scrollableModeActive = true;
|
|
}
|
|
sheetsGroup.css({
|
|
marginLeft: scrollPrevButtonWidth + addButtonWidth,
|
|
marginRight: scrollNextButton.outerWidth() + scrollNextButtonRight + gapWidth
|
|
});
|
|
} else {
|
|
if (that._scrollableModeActive && sheetsGroupScrollWidth <= wrapperOffsetWidth) {
|
|
that._scrollableModeActive = false;
|
|
sheetsGroup.css({
|
|
marginLeft: addButtonWidth,
|
|
marginRight: ''
|
|
});
|
|
} else {
|
|
sheetsGroup.css({ marginLeft: addButtonWidth });
|
|
}
|
|
}
|
|
that._toggleScrollButtons();
|
|
},
|
|
_toggleScrollButtons: function (toggle) {
|
|
var that = this;
|
|
var ul = that._sheetsGroup();
|
|
var wrapper = that._sheetsWrapper();
|
|
var scrollLeft = ul.scrollLeft();
|
|
var prev = wrapper.find(DOT + SheetsBar.classNames.sheetsBarPrev);
|
|
var next = wrapper.find(DOT + SheetsBar.classNames.sheetsBarNext);
|
|
if (toggle === false) {
|
|
prev.toggle(false);
|
|
next.toggle(false);
|
|
} else {
|
|
prev.toggle(that._isRtl ? scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1 : scrollLeft !== 0);
|
|
next.toggle(that._isRtl ? scrollLeft !== 0 : scrollLeft < ul[0].scrollWidth - ul[0].offsetWidth - 1);
|
|
}
|
|
},
|
|
_toggleScrollEvents: function (toggle) {
|
|
var that = this;
|
|
var classNames = SheetsBar.classNames;
|
|
var options = that.options;
|
|
var scrollPrevButton;
|
|
var scrollNextButton;
|
|
var sheetsWrapper = that._sheetsWrapper();
|
|
scrollPrevButton = sheetsWrapper.children(DOT + classNames.sheetsBarPrev);
|
|
scrollNextButton = sheetsWrapper.children(DOT + classNames.sheetsBarNext);
|
|
if (toggle) {
|
|
scrollPrevButton.on('mousedown', function () {
|
|
that._nowScrollingSheets = true;
|
|
that._scrollSheetsByDelta(options.scrollable.distance * (that._isRtl ? 1 : -1));
|
|
});
|
|
scrollNextButton.on('mousedown', function () {
|
|
that._nowScrollingSheets = true;
|
|
that._scrollSheetsByDelta(options.scrollable.distance * (that._isRtl ? -1 : 1));
|
|
});
|
|
scrollPrevButton.add(scrollNextButton).on('mouseup', function () {
|
|
that._nowScrollingSheets = false;
|
|
});
|
|
} else {
|
|
scrollPrevButton.off();
|
|
scrollNextButton.off();
|
|
}
|
|
},
|
|
_renderHtml: function (isInEditMode, renderScrollButtons) {
|
|
var idx;
|
|
var sheetElements = [];
|
|
var dom = kendo.dom;
|
|
var element = dom.element;
|
|
var sheets = this._sheets;
|
|
var selectedIndex = this._selectedIndex;
|
|
var classNames = SheetsBar.classNames;
|
|
for (idx = 0; idx < sheets.length; idx++) {
|
|
var sheet = sheets[idx];
|
|
var isSelectedSheet = idx === selectedIndex;
|
|
var attr = { className: classNames.sheetsBarKItem + EMPTYCHAR };
|
|
var elementContent = [];
|
|
if (isSelectedSheet) {
|
|
attr.className += classNames.sheetsBarKActive + EMPTYCHAR + classNames.sheetsBarActive;
|
|
} else {
|
|
attr.className += classNames.sheetsBarInactive;
|
|
}
|
|
if (isSelectedSheet && isInEditMode) {
|
|
elementContent.push(element('input', {
|
|
type: 'text',
|
|
value: sheet.name(),
|
|
className: classNames.sheetsBarKTextbox + EMPTYCHAR + classNames.sheetsBarEditor,
|
|
maxlength: 50
|
|
}, []));
|
|
} else {
|
|
elementContent.push(element('span', {
|
|
className: classNames.sheetsBarKLink,
|
|
title: sheet.name()
|
|
}, [dom.text(sheet.name())]));
|
|
var deleteIcon = element('span', { className: classNames.sheetsBarKIcon + EMPTYCHAR + classNames.sheetsBarKFontIcon + EMPTYCHAR + classNames.sheetsBarKIconX }, []);
|
|
elementContent.push(element('span', { className: classNames.sheetsBarKLink + EMPTYCHAR + classNames.sheetsBarRemove }, [deleteIcon]));
|
|
}
|
|
sheetElements.push(element('li', attr, elementContent));
|
|
}
|
|
this._tree.render([
|
|
this._addButton(),
|
|
this._createSheetsWrapper(sheetElements, renderScrollButtons)
|
|
]);
|
|
},
|
|
_createSheetsWrapper: function (sheetElements, renderScrollButtons) {
|
|
var element = kendo.dom.element;
|
|
var classNames = SheetsBar.classNames;
|
|
var childrenElements = [element('ul', { className: classNames.sheetsBarKReset }, sheetElements)];
|
|
renderScrollButtons = true;
|
|
if (renderScrollButtons) {
|
|
var baseButtonClass = classNames.sheetsBarKButton + EMPTYCHAR + classNames.sheetsBarKButtonBare + EMPTYCHAR;
|
|
childrenElements.push(element('span', { className: baseButtonClass + classNames.sheetsBarPrev }, [element('span', { className: classNames.sheetsBarKIcon + EMPTYCHAR + classNames.sheetsBarKArrowW }, [])]));
|
|
childrenElements.push(element('span', { className: baseButtonClass + classNames.sheetsBarNext }, [element('span', { className: classNames.sheetsBarKIcon + EMPTYCHAR + classNames.sheetsBarKArrowE }, [])]));
|
|
}
|
|
return element('div', { className: classNames.sheetsBarItems }, childrenElements);
|
|
},
|
|
_createSortable: function () {
|
|
var classNames = SheetsBar.classNames;
|
|
this._sortable = new kendo.ui.Sortable(this.element, {
|
|
filter: kendo.format('ul li.{0},ul li.{1}', classNames.sheetsBarActive, classNames.sheetsBarInactive),
|
|
container: DOT + classNames.sheetsBarItems,
|
|
axis: 'x',
|
|
animation: false,
|
|
ignore: 'input',
|
|
end: function () {
|
|
if (this.draggable.hint) {
|
|
this.draggable.hint.remove();
|
|
}
|
|
},
|
|
hint: function (element) {
|
|
var hint = $(element).clone();
|
|
return hint.wrap('<div class=\'' + classNames.sheetsBarHintWrapper + '\'><ul class=\'' + classNames.sheetsBarKResetItems + '\'></ul></div>').closest('div');
|
|
}
|
|
});
|
|
},
|
|
_onEditorKeydown: function (e) {
|
|
if (this._editor) {
|
|
if (e.which === 13) {
|
|
this._destroyEditor();
|
|
this._onSheetRename($(e.target).val());
|
|
}
|
|
if (e.which === 27) {
|
|
this._destroyEditor();
|
|
this._onSheetRename();
|
|
}
|
|
}
|
|
},
|
|
_onEditorBlur: function (e) {
|
|
if (this._editor) {
|
|
this._destroyEditor();
|
|
this._onSheetRename($(e.target).val());
|
|
}
|
|
},
|
|
_onSheetReorderEnd: function (e) {
|
|
e.preventDefault();
|
|
this.trigger('reorder', {
|
|
oldIndex: e.oldIndex,
|
|
newIndex: e.newIndex
|
|
});
|
|
},
|
|
_onSheetReorderStart: function (e) {
|
|
if (this._editor) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_onSheetRemove: function (e) {
|
|
var removedSheetName = $(e.target).closest('li').text();
|
|
if (this._editor) {
|
|
this._destroyEditor();
|
|
this._onSheetRename(this._editor.val());
|
|
}
|
|
this.trigger('remove', { name: removedSheetName });
|
|
},
|
|
_onSheetSelect: function (e) {
|
|
var selectedSheetText = $(e.target).text();
|
|
if ($(e.target).is(DOT + SheetsBar.classNames.sheetsBarEditor) || !selectedSheetText) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (this._editor) {
|
|
var editorValue = this._editor.val();
|
|
this._destroyEditor();
|
|
this._onSheetRename(editorValue);
|
|
}
|
|
this._scrollSheetsToItem($(e.target).closest('li'));
|
|
this.trigger('select', {
|
|
name: selectedSheetText,
|
|
isAddButton: false
|
|
});
|
|
},
|
|
_onSheetRename: function (newSheetName) {
|
|
this.trigger('rename', {
|
|
name: newSheetName,
|
|
sheetIndex: this._selectedIndex
|
|
});
|
|
},
|
|
_onAddSelect: function () {
|
|
this.trigger('select', { isAddButton: true });
|
|
},
|
|
_addButton: function () {
|
|
var element = kendo.dom.element;
|
|
var classNames = SheetsBar.classNames;
|
|
return element('a', { className: classNames.sheetsBarAdd + EMPTYCHAR + classNames.sheetsBarKButton }, [element('span', { className: classNames.sheetsBarKSprite + EMPTYCHAR + classNames.sheetsBarKIcon + EMPTYCHAR + classNames.sheetsBarKFontIcon + EMPTYCHAR + classNames.sheetsBarKIconPlus }, [])]);
|
|
},
|
|
destroy: function () {
|
|
this._sortable.destroy();
|
|
},
|
|
_scrollableAllowed: function () {
|
|
var options = this.options;
|
|
return options.scrollable && !isNaN(options.scrollable.distance);
|
|
},
|
|
_scrollSheetsToItem: function (item) {
|
|
var that = this;
|
|
if (!that._scrollableModeActive) {
|
|
return;
|
|
}
|
|
var sheetsGroup = that._sheetsGroup();
|
|
var currentScrollOffset = sheetsGroup.scrollLeft();
|
|
var itemWidth = item.outerWidth();
|
|
var itemOffset = that._isRtl ? item.position().left : item.position().left - sheetsGroup.children().first().position().left;
|
|
var sheetsGroupWidth = sheetsGroup[0].offsetWidth;
|
|
var sheetsGroupPadding = Math.ceil(parseFloat(sheetsGroup.css('padding-left')));
|
|
var itemPosition;
|
|
if (that._isRtl) {
|
|
if (itemOffset < 0) {
|
|
itemPosition = currentScrollOffset + itemOffset - (sheetsGroupWidth - currentScrollOffset) - sheetsGroupPadding;
|
|
} else if (itemOffset + itemWidth > sheetsGroupWidth) {
|
|
itemPosition = currentScrollOffset + itemOffset - itemWidth + sheetsGroupPadding * 2;
|
|
}
|
|
} else {
|
|
if (currentScrollOffset + sheetsGroupWidth < itemOffset + itemWidth) {
|
|
itemPosition = itemOffset + itemWidth - sheetsGroupWidth + sheetsGroupPadding * 2;
|
|
} else if (currentScrollOffset > itemOffset) {
|
|
itemPosition = itemOffset - sheetsGroupPadding;
|
|
}
|
|
}
|
|
sheetsGroup.finish().animate({ 'scrollLeft': itemPosition }, 'fast', 'linear', function () {
|
|
that._toggleScrollButtons();
|
|
});
|
|
},
|
|
_sheetsGroup: function () {
|
|
return this._sheetsWrapper().children('ul');
|
|
},
|
|
_sheetsWrapper: function () {
|
|
return this.element.find(DOT + SheetsBar.classNames.sheetsBarItems);
|
|
},
|
|
_scrollSheetsByDelta: function (delta) {
|
|
var that = this;
|
|
var sheetsGroup = that._sheetsGroup();
|
|
var scrLeft = sheetsGroup.scrollLeft();
|
|
sheetsGroup.finish().animate({ 'scrollLeft': scrLeft + delta }, 'fast', 'linear', function () {
|
|
if (that._nowScrollingSheets) {
|
|
that._scrollSheetsByDelta(delta);
|
|
} else {
|
|
that._toggleScrollButtons();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.SheetsBar = SheetsBar;
|
|
$.extend(true, SheetsBar, { classNames: sheetsBarClassNames });
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/calc', ['spreadsheet/runtime'], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var RangeRef = spreadsheet.RangeRef;
|
|
var CellRef = spreadsheet.CellRef;
|
|
var NameRef = spreadsheet.NameRef;
|
|
var exports = spreadsheet.calc;
|
|
var runtime = exports.runtime;
|
|
var OPERATORS = Object.create(null);
|
|
var ParseError = kendo.Class.extend({
|
|
init: function ParseError(message, pos) {
|
|
this.message = message;
|
|
this.pos = pos;
|
|
},
|
|
toString: function () {
|
|
return this.message;
|
|
}
|
|
});
|
|
(function (ops) {
|
|
ops.forEach(function (cls, i) {
|
|
cls.forEach(function (op) {
|
|
OPERATORS[op] = ops.length - i;
|
|
});
|
|
});
|
|
}([
|
|
[':'],
|
|
[' '],
|
|
[','],
|
|
['%'],
|
|
['^'],
|
|
[
|
|
'*',
|
|
'/'
|
|
],
|
|
[
|
|
'+',
|
|
'-'
|
|
],
|
|
['&'],
|
|
[
|
|
'=',
|
|
'<',
|
|
'>',
|
|
'<=',
|
|
'>=',
|
|
'<>'
|
|
]
|
|
]));
|
|
var TRUE = {
|
|
type: 'bool',
|
|
value: true
|
|
};
|
|
var FALSE = {
|
|
type: 'bool',
|
|
value: false
|
|
};
|
|
function getcol(str) {
|
|
str = str.toUpperCase();
|
|
for (var col = 0, i = 0; i < str.length; ++i) {
|
|
col = col * 26 + str.charCodeAt(i) - 64;
|
|
}
|
|
return col - 1;
|
|
}
|
|
function getrow(str) {
|
|
return parseInt(str, 10) - 1;
|
|
}
|
|
function parseReference(name, noThrow) {
|
|
if (name.toLowerCase() == '#sheet') {
|
|
return spreadsheet.SHEETREF;
|
|
}
|
|
OUT: {
|
|
var m;
|
|
if (m = /^(\$)?([a-z]+)(\$)?(\d+)$/i.exec(name)) {
|
|
var row = getrow(m[4]), col = getcol(m[2]);
|
|
if (row <= 1048576 && col <= 16383) {
|
|
return new CellRef(getrow(m[4]), getcol(m[2]));
|
|
}
|
|
break OUT;
|
|
}
|
|
var stream = TokenStream(name, {});
|
|
var a = [];
|
|
while (true) {
|
|
var ref = stream.next();
|
|
if (ref instanceof CellRef) {
|
|
ref.rel = 0;
|
|
} else if (ref instanceof RangeRef) {
|
|
ref.topLeft.rel = 0;
|
|
ref.bottomRight.rel = 0;
|
|
} else {
|
|
break OUT;
|
|
}
|
|
a.push(ref);
|
|
if (stream.eof()) {
|
|
break;
|
|
}
|
|
if (!stream.is('op', ',')) {
|
|
break OUT;
|
|
}
|
|
stream.next();
|
|
}
|
|
return a.length == 1 ? a[0] : new spreadsheet.UnionRef(a);
|
|
}
|
|
if (!noThrow) {
|
|
throw new Error('Cannot parse reference: ' + name);
|
|
}
|
|
}
|
|
function parseFormula(sheet, row, col, input) {
|
|
var refs = [];
|
|
input = TokenStream(input, {
|
|
row: row,
|
|
col: col
|
|
});
|
|
var is = input.is;
|
|
return {
|
|
type: 'exp',
|
|
ast: parseExpression(true),
|
|
refs: refs,
|
|
sheet: sheet,
|
|
row: row,
|
|
col: col
|
|
};
|
|
function addReference(ref) {
|
|
ref.index = refs.length;
|
|
refs.push(ref);
|
|
return ref;
|
|
}
|
|
function skip(type, value) {
|
|
if (is(type, value)) {
|
|
return input.next();
|
|
} else {
|
|
var tok = input.peek();
|
|
if (tok) {
|
|
input.croak('Expected ' + type + ' \xAB' + value + '\xBB but found ' + tok.type + ' \xAB' + tok.value + '\xBB');
|
|
} else {
|
|
input.croak('Expected ' + type + ' \xAB' + value + '\xBB');
|
|
}
|
|
}
|
|
}
|
|
function parseExpression(commas) {
|
|
return maybeBinary(maybeIntersect(parseAtom(commas)), 0, commas);
|
|
}
|
|
function parseSymbol(tok) {
|
|
if (tok.upper == 'TRUE' || tok.upper == 'FALSE') {
|
|
return tok.upper == 'TRUE' ? TRUE : FALSE;
|
|
}
|
|
return addReference(new NameRef(tok.value));
|
|
}
|
|
function parseFuncall() {
|
|
var fname = input.next();
|
|
fname = fname.value;
|
|
skip('punc', '(');
|
|
var args = [];
|
|
if (!is('punc', ')')) {
|
|
while (1) {
|
|
if (is('op', ',')) {
|
|
args.push({ type: 'null' });
|
|
input.next();
|
|
continue;
|
|
}
|
|
args.push(parseExpression(false));
|
|
if (input.eof() || is('punc', ')')) {
|
|
break;
|
|
}
|
|
skip('op', ',');
|
|
}
|
|
}
|
|
skip('punc', ')');
|
|
return {
|
|
type: 'func',
|
|
func: fname,
|
|
args: args
|
|
};
|
|
}
|
|
function fixReference(ref) {
|
|
if (!ref.hasSheet()) {
|
|
ref.setSheet(sheet);
|
|
}
|
|
return addReference(ref);
|
|
}
|
|
function parseAtom(commas) {
|
|
var exp;
|
|
if (is('ref')) {
|
|
exp = fixReference(input.next());
|
|
} else if (is('func')) {
|
|
exp = parseFuncall();
|
|
} else if (is('punc', '(')) {
|
|
input.next();
|
|
exp = parseExpression(true);
|
|
skip('punc', ')');
|
|
} else if (is('punc', '{')) {
|
|
input.next();
|
|
exp = parseArray();
|
|
skip('punc', '}');
|
|
} else if (is('num') || is('str')) {
|
|
exp = input.next();
|
|
} else if (is('sym')) {
|
|
exp = parseSymbol(input.next());
|
|
} else if (is('op', '+') || is('op', '-')) {
|
|
exp = {
|
|
type: 'prefix',
|
|
op: input.next().value,
|
|
exp: parseExpression(commas)
|
|
};
|
|
} else if (!input.peek()) {
|
|
input.croak('Incomplete expression');
|
|
} else {
|
|
input.croak('Parse error');
|
|
}
|
|
return maybePercent(exp);
|
|
}
|
|
function parseArray() {
|
|
var row = [], value = [row], first = true;
|
|
while (!input.eof() && !is('punc', '}')) {
|
|
if (first) {
|
|
first = false;
|
|
} else if (is('punc', ';')) {
|
|
value.push(row = []);
|
|
input.next();
|
|
} else {
|
|
skip('op', ',');
|
|
}
|
|
row.push(parseExpression(false));
|
|
}
|
|
return {
|
|
type: 'matrix',
|
|
value: value
|
|
};
|
|
}
|
|
function maybeIntersect(exp) {
|
|
if (is('punc', '(') || is('ref') || is('num') || is('func')) {
|
|
return {
|
|
type: 'binary',
|
|
op: ' ',
|
|
left: exp,
|
|
right: parseExpression(false)
|
|
};
|
|
} else {
|
|
return exp;
|
|
}
|
|
}
|
|
function maybePercent(exp) {
|
|
if (is('op', '%')) {
|
|
input.next();
|
|
return maybePercent({
|
|
type: 'postfix',
|
|
op: '%',
|
|
exp: exp
|
|
});
|
|
} else {
|
|
return exp;
|
|
}
|
|
}
|
|
function maybeBinary(left, my_prec, commas) {
|
|
var tok = is('op');
|
|
if (tok && (commas || tok.value != ',')) {
|
|
var his_prec = OPERATORS[tok.value];
|
|
if (his_prec > my_prec) {
|
|
input.next();
|
|
var right = maybeBinary(parseAtom(commas), his_prec, commas);
|
|
return maybeBinary({
|
|
type: 'binary',
|
|
op: tok.value,
|
|
left: left,
|
|
right: right
|
|
}, my_prec, commas);
|
|
}
|
|
}
|
|
return left;
|
|
}
|
|
}
|
|
function makePrinter(exp) {
|
|
return makeClosure('function(row, col){return(' + print(exp.ast, exp, 0) + ')}');
|
|
function print(node, parent, prec) {
|
|
switch (node.type) {
|
|
case 'num':
|
|
case 'bool':
|
|
return JSON.stringify(node.value);
|
|
case 'str':
|
|
return JSON.stringify(JSON.stringify(node.value));
|
|
case 'ref':
|
|
return 'this.refs[' + node.index + '].print(row, col)';
|
|
case 'prefix':
|
|
return withParens(function () {
|
|
return JSON.stringify(node.op) + ' + ' + print(node.exp, node, OPERATORS[node.op]);
|
|
});
|
|
case 'postfix':
|
|
return withParens(function () {
|
|
return print(node.exp, node, OPERATORS[node.op]) + ' + ' + JSON.stringify(node.op);
|
|
});
|
|
case 'binary':
|
|
return withParens(function () {
|
|
var left = parenthesize(print(node.left, node, OPERATORS[node.op]), node.left instanceof NameRef && node.op == ':');
|
|
var right = parenthesize(print(node.right, node, OPERATORS[node.op]), node.right instanceof NameRef && node.op == ':');
|
|
return left + ' + ' + JSON.stringify(node.op) + ' + ' + right;
|
|
});
|
|
case 'func':
|
|
return JSON.stringify(node.func + '(') + ' + ' + (node.args.length > 0 ? node.args.map(function (arg) {
|
|
return print(arg, node, 0);
|
|
}).join(' + \', \' + ') : '\'\'') + ' + \')\'';
|
|
case 'matrix':
|
|
return '\'{ \' + ' + node.value.map(function (el) {
|
|
return el.map(function (el) {
|
|
return print(el, node, 0);
|
|
}).join(' + \', \' + ');
|
|
}).join(' + \'; \' + ') + '+ \' }\'';
|
|
case 'null':
|
|
return '\'\'';
|
|
}
|
|
throw new Error('Cannot make printer for node ' + node.type);
|
|
function withParens(f) {
|
|
var op = node.op;
|
|
var needParens = OPERATORS[op] < prec || !prec && op == ',' || parent.type == 'binary' && prec == OPERATORS[op] && node === parent.right;
|
|
return parenthesize(f(), needParens);
|
|
}
|
|
}
|
|
function parenthesize(code, cond) {
|
|
return cond ? '\'(\' + ' + code + ' + \')\'' : code;
|
|
}
|
|
}
|
|
function toCPS(ast, k) {
|
|
var GENSYM = 0;
|
|
return cps(ast, k);
|
|
function cps(node, k) {
|
|
switch (node.type) {
|
|
case 'ref':
|
|
case 'num':
|
|
case 'str':
|
|
case 'null':
|
|
case 'bool':
|
|
return cpsAtom(node, k);
|
|
case 'prefix':
|
|
case 'postfix':
|
|
return cpsUnary(node, k);
|
|
case 'binary':
|
|
return cpsBinary(node, k);
|
|
case 'func':
|
|
return cpsFunc(node, k);
|
|
case 'lambda':
|
|
return cpsLambda(node, k);
|
|
case 'matrix':
|
|
return cpsMatrix(node.value, k, true);
|
|
}
|
|
throw new Error('Cannot CPS ' + node.type);
|
|
}
|
|
function cpsAtom(node, k) {
|
|
return k(node);
|
|
}
|
|
function cpsUnary(node, k) {
|
|
return cps({
|
|
type: 'func',
|
|
func: 'unary' + node.op,
|
|
args: [node.exp]
|
|
}, k);
|
|
}
|
|
function cpsBinary(node, k) {
|
|
return cps({
|
|
type: 'func',
|
|
func: 'binary' + node.op,
|
|
args: [
|
|
node.left,
|
|
node.right
|
|
]
|
|
}, k);
|
|
}
|
|
function cpsIf(co, th, el, k) {
|
|
return cps(co, function (co) {
|
|
var rest = makeContinuation(k);
|
|
var thenK = gensym('T');
|
|
var elseK = gensym('E');
|
|
return {
|
|
type: 'func',
|
|
func: 'if',
|
|
args: [
|
|
rest,
|
|
co,
|
|
{
|
|
type: 'lambda',
|
|
vars: [thenK],
|
|
body: cps(th || TRUE, function (th) {
|
|
return {
|
|
type: 'call',
|
|
func: {
|
|
type: 'var',
|
|
name: thenK
|
|
},
|
|
args: [th]
|
|
};
|
|
})
|
|
},
|
|
{
|
|
type: 'lambda',
|
|
vars: [elseK],
|
|
body: cps(el || FALSE, function (el) {
|
|
return {
|
|
type: 'call',
|
|
func: {
|
|
type: 'var',
|
|
name: elseK
|
|
},
|
|
args: [el]
|
|
};
|
|
})
|
|
}
|
|
]
|
|
};
|
|
});
|
|
}
|
|
function cpsAnd(args, k) {
|
|
if (args.length === 0) {
|
|
return cpsAtom(TRUE, k);
|
|
}
|
|
return cps({
|
|
type: 'func',
|
|
func: 'IF',
|
|
args: [
|
|
args[0],
|
|
{
|
|
type: 'func',
|
|
func: 'AND',
|
|
args: args.slice(1)
|
|
},
|
|
FALSE
|
|
]
|
|
}, k);
|
|
}
|
|
function cpsOr(args, k) {
|
|
if (args.length === 0) {
|
|
return cpsAtom(FALSE, k);
|
|
}
|
|
return cps({
|
|
type: 'func',
|
|
func: 'IF',
|
|
args: [
|
|
args[0],
|
|
TRUE,
|
|
{
|
|
type: 'func',
|
|
func: 'OR',
|
|
args: args.slice(1)
|
|
}
|
|
]
|
|
}, k);
|
|
}
|
|
function cpsFunc(node, k) {
|
|
switch (node.func.toLowerCase()) {
|
|
case 'if':
|
|
return cpsIf(node.args[0], node.args[1], node.args[2], k);
|
|
case 'and':
|
|
return cpsAnd(node.args, k);
|
|
case 'or':
|
|
return cpsOr(node.args, k);
|
|
case 'true':
|
|
return k(TRUE);
|
|
case 'false':
|
|
return k(FALSE);
|
|
}
|
|
return function loop(args, i) {
|
|
if (i == node.args.length) {
|
|
return {
|
|
type: 'func',
|
|
func: node.func,
|
|
args: args
|
|
};
|
|
} else {
|
|
return cps(node.args[i], function (value) {
|
|
return loop(args.concat([value]), i + 1);
|
|
});
|
|
}
|
|
}([makeContinuation(k)], 0);
|
|
}
|
|
function cpsLambda(node, k) {
|
|
var cont = gensym('K');
|
|
var body = cps(node.body, function (body) {
|
|
return {
|
|
type: 'call',
|
|
func: {
|
|
type: 'var',
|
|
value: cont
|
|
},
|
|
args: [body]
|
|
};
|
|
});
|
|
return k({
|
|
type: 'lambda',
|
|
vars: [cont].concat(node.vars),
|
|
body: body
|
|
});
|
|
}
|
|
function cpsMatrix(elements, k, isMatrix) {
|
|
var a = [];
|
|
return function loop(i) {
|
|
if (i == elements.length) {
|
|
return k({
|
|
type: 'matrix',
|
|
value: a
|
|
});
|
|
} else {
|
|
return (isMatrix ? cpsMatrix : cps)(elements[i], function (val) {
|
|
a[i] = val;
|
|
return loop(i + 1);
|
|
});
|
|
}
|
|
}(0);
|
|
}
|
|
function makeContinuation(k) {
|
|
var cont = gensym('R');
|
|
return {
|
|
type: 'lambda',
|
|
vars: [cont],
|
|
body: k({
|
|
type: 'var',
|
|
name: cont
|
|
})
|
|
};
|
|
}
|
|
function gensym(name) {
|
|
if (!name) {
|
|
name = '';
|
|
}
|
|
name = '_' + name;
|
|
return name + ++GENSYM;
|
|
}
|
|
}
|
|
var makeClosure = function (cache) {
|
|
return function (code) {
|
|
var f = cache[code];
|
|
if (!f) {
|
|
f = cache[code] = new Function('\'use strict\';return(' + code + ')')();
|
|
}
|
|
return f;
|
|
};
|
|
}(Object.create(null));
|
|
var FORMULA_CACHE = Object.create(null);
|
|
function makeFormula(exp) {
|
|
var printer = makePrinter(exp);
|
|
var hash = printer.call(exp);
|
|
var formula = FORMULA_CACHE[hash];
|
|
if (formula) {
|
|
return formula.clone(exp.sheet, exp.row, exp.col);
|
|
}
|
|
var code = js(toCPS(exp.ast, function (ret) {
|
|
return {
|
|
type: 'return',
|
|
value: ret
|
|
};
|
|
}));
|
|
code = [
|
|
'function(){',
|
|
'var context = this, refs = context.formula.absrefs',
|
|
code,
|
|
'}'
|
|
].join(';\n');
|
|
formula = new runtime.Formula(exp.refs, makeClosure(code), printer, exp.sheet, exp.row, exp.col);
|
|
FORMULA_CACHE[hash] = formula;
|
|
return formula;
|
|
function js(node) {
|
|
var type = node.type;
|
|
if (type == 'num') {
|
|
return node.value + '';
|
|
} else if (type == 'str') {
|
|
return JSON.stringify(node.value);
|
|
} else if (type == 'return') {
|
|
return 'context.resolve(' + js(node.value) + ')';
|
|
} else if (type == 'func') {
|
|
return 'context.func(' + JSON.stringify(node.func) + ', ' + js(node.args[0]) + ', ' + jsArray(node.args.slice(1)) + ')';
|
|
} else if (type == 'call') {
|
|
return js(node.func) + '(' + node.args.map(js).join(', ') + ')';
|
|
} else if (type == 'ref') {
|
|
return 'refs[' + node.index + ']';
|
|
} else if (type == 'bool') {
|
|
return '' + node.value;
|
|
} else if (type == 'if') {
|
|
return '(context.bool(' + js(node.co) + ') ? ' + js(node.th) + ' : ' + js(node.el) + ')';
|
|
} else if (type == 'lambda') {
|
|
return '(function(' + node.vars.join(', ') + '){ return(' + js(node.body) + ') })';
|
|
} else if (type == 'var') {
|
|
return node.name;
|
|
} else if (type == 'matrix') {
|
|
return jsArray(node.value);
|
|
} else if (type == 'null') {
|
|
return 'null';
|
|
} else {
|
|
throw new Error('Cannot compile expression ' + type);
|
|
}
|
|
}
|
|
function jsArray(a) {
|
|
return '[ ' + a.map(js).join(', ') + ' ]';
|
|
}
|
|
}
|
|
function identity(x) {
|
|
return x;
|
|
}
|
|
function TokenStream(input, options) {
|
|
input = RawTokenStream(InputStream(input), options);
|
|
var ahead = input.ahead;
|
|
var skip = input.skip;
|
|
var token = null;
|
|
var fixCell = options.row != null && options.col != null ? function (cell) {
|
|
if (cell.rel & 1) {
|
|
cell.col -= options.col;
|
|
}
|
|
if (cell.rel & 2) {
|
|
cell.row -= options.row;
|
|
}
|
|
return cell;
|
|
} : identity;
|
|
var addPos = options.forEditor ? function (thing, startToken, endToken) {
|
|
thing.begin = startToken.begin;
|
|
thing.end = endToken.end;
|
|
return thing;
|
|
} : identity;
|
|
return {
|
|
peek: peek,
|
|
next: next,
|
|
croak: input.croak,
|
|
eof: input.eof,
|
|
is: is
|
|
};
|
|
function is(type, value) {
|
|
var tok = peek();
|
|
return tok != null && (type == null || tok.type === type) && (value == null || tok.value === value) ? tok : null;
|
|
}
|
|
function peek() {
|
|
if (token == null) {
|
|
token = readNext();
|
|
}
|
|
return token;
|
|
}
|
|
function next() {
|
|
if (token != null) {
|
|
var tmp = token;
|
|
token = null;
|
|
return tmp;
|
|
}
|
|
return readNext();
|
|
}
|
|
function readNext() {
|
|
var ret;
|
|
var t = input.peek();
|
|
if (t) {
|
|
if (t.type == 'sym' || t.type == 'rc' || t.type == 'num') {
|
|
ret = ahead(8, refRange3D) || ahead(6, refCell3D) || ahead(6, refSheetRange) || ahead(4, refSheetCell) || ahead(4, refRange) || ahead(2, refCell) || ahead(2, funcall);
|
|
}
|
|
if (!ret) {
|
|
ret = input.next();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
function toCell(tok, isFirst) {
|
|
if (tok.type == 'rc') {
|
|
if (tok.rel && !options.forEditor && (options.row == null || options.col == null)) {
|
|
input.croak('Cannot read relative cell in RC notation');
|
|
}
|
|
return new CellRef(tok.row, tok.col, tok.rel);
|
|
}
|
|
if (tok.type == 'num') {
|
|
if (tok.value <= 1048577) {
|
|
return fixCell(new CellRef(getrow(tok.value), isFirst ? -Infinity : +Infinity, 2));
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
var name = tok.value;
|
|
var m = /^(\$)?([a-z]+)(\$)?(\d+)$/i.exec(name);
|
|
if (m) {
|
|
var row = getrow(m[4]), col = getcol(m[2]);
|
|
if (row <= 1048576 && col <= 16383) {
|
|
return fixCell(new CellRef(getrow(m[4]), getcol(m[2]), (m[1] ? 0 : 1) | (m[3] ? 0 : 2)));
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
var abs = name.charAt(0) == '$';
|
|
if (abs) {
|
|
name = name.substr(1);
|
|
}
|
|
if (/^\d+$/.test(name)) {
|
|
var row = getrow(name);
|
|
if (row <= 1048576) {
|
|
return fixCell(new CellRef(getrow(name), isFirst ? -Infinity : +Infinity, abs ? 0 : 2));
|
|
}
|
|
} else {
|
|
var col = getcol(name);
|
|
if (col <= 16383) {
|
|
return fixCell(new CellRef(isFirst ? -Infinity : +Infinity, getcol(name), abs ? 0 : 1));
|
|
}
|
|
}
|
|
}
|
|
function refRange3D(a, b, c, d, e, f, g, h) {
|
|
if (a.type == 'sym' && b.type == 'op' && b.value == ':' && c.type == 'sym' && d.type == 'punc' && d.value == '!' && (e.type == 'sym' || e.type == 'rc' || e.type == 'num' && e.value == e.value | 0) && f.type == 'op' && f.value == ':' && (g.type == 'sym' || g.type == 'rc' || g.type == 'num' && g.value == g.value | 0) && g.type == e.type && !(h.type == 'punc' && h.value == '(' && !g.space)) {
|
|
var tl = toCell(e, true), br = toCell(g, false);
|
|
if (tl && br) {
|
|
skip(7);
|
|
return addPos(new RangeRef(tl.setSheet(a.value, true), br.setSheet(c.value, true)).setSheet(a.value, true), a, g);
|
|
}
|
|
}
|
|
}
|
|
function refCell3D(a, b, c, d, e, f) {
|
|
if (a.type == 'sym' && b.type == 'op' && b.value == ':' && c.type == 'sym' && d.type == 'punc' && d.value == '!' && (e.type == 'sym' || e.type == 'rc' || e.type == 'num' && e.value == e.value | 0) && !(f.type == 'punc' && f.value == '(' && !e.space)) {
|
|
var tl = toCell(e);
|
|
if (tl) {
|
|
skip(5);
|
|
var br = tl.clone();
|
|
return addPos(new RangeRef(tl.setSheet(a.value, true), br.setSheet(c.value, true)).setSheet(a.value, true), a, e);
|
|
}
|
|
}
|
|
}
|
|
function refSheetRange(a, b, c, d, e, f) {
|
|
if (a.type == 'sym' && b.type == 'punc' && b.value == '!' && (c.type == 'sym' || c.type == 'rc' || c.type == 'num' && c.value == c.value | 0) && d.type == 'op' && d.value == ':' && (e.type == 'sym' || e.type == 'rc' || e.type == 'num' && e.value == e.value | 0) && !(f.type == 'punc' && f.value == '(' && !e.space)) {
|
|
var tl = toCell(c, true), br = toCell(e, false);
|
|
if (tl && br) {
|
|
skip(5);
|
|
return addPos(new RangeRef(tl, br).setSheet(a.value, true), a, e);
|
|
}
|
|
}
|
|
}
|
|
function refSheetCell(a, b, c, d) {
|
|
if (a.type == 'sym' && b.type == 'punc' && b.value == '!' && (c.type == 'sym' || c.type == 'rc' || c.type == 'num' && c.value == c.value | 0) && !(d.type == 'punc' && d.value == '(' && !c.space)) {
|
|
skip(3);
|
|
var x = toCell(c);
|
|
if (!x) {
|
|
x = new NameRef(c.value);
|
|
}
|
|
return addPos(x.setSheet(a.value, true), a, c);
|
|
}
|
|
}
|
|
function refRange(a, b, c, d) {
|
|
if ((a.type == 'sym' || a.type == 'rc' || a.type == 'num' && a.value == a.value | 0) && (b.type == 'op' && b.value == ':') && (c.type == 'sym' || c.type == 'rc' || c.type == 'num' && c.value == c.value | 0) && !(d.type == 'punc' && d.value == '(' && !c.space)) {
|
|
var tl = toCell(a, true), br = toCell(c, false);
|
|
if (tl && br) {
|
|
skip(3);
|
|
return addPos(new RangeRef(tl, br), a, c);
|
|
}
|
|
}
|
|
}
|
|
function refCell(a, b) {
|
|
if ((a.type == 'sym' || a.type == 'rc') && !(b.type == 'punc' && b.value == '(' && !a.space)) {
|
|
var x = toCell(a);
|
|
if (x && isFinite(x.row) && isFinite(x.col)) {
|
|
skip(1);
|
|
return addPos(x, a, a);
|
|
}
|
|
}
|
|
}
|
|
function funcall(a, b) {
|
|
if (a.type == 'sym' && b.type == 'punc' && b.value == '(' && !a.space) {
|
|
a.type = 'func';
|
|
skip(1);
|
|
return a;
|
|
}
|
|
}
|
|
}
|
|
function RawTokenStream(input, options) {
|
|
var tokens = [], index = 0;
|
|
var readWhile = input.readWhile;
|
|
return {
|
|
next: next,
|
|
peek: peek,
|
|
eof: eof,
|
|
croak: input.croak,
|
|
ahead: ahead,
|
|
skip: skip
|
|
};
|
|
function isDigit(ch) {
|
|
return /[0-9]/i.test(ch);
|
|
}
|
|
function isIdStart(ch) {
|
|
return /[a-z$_]/i.test(ch) || ch.toLowerCase() != ch.toUpperCase();
|
|
}
|
|
function isId(ch) {
|
|
return isIdStart(ch) || isDigit(ch) || ch == '.';
|
|
}
|
|
function isOpChar(ch) {
|
|
return ch in OPERATORS;
|
|
}
|
|
function isPunc(ch) {
|
|
return '!;(){}[]'.indexOf(ch) >= 0;
|
|
}
|
|
function isWhitespace(ch) {
|
|
return ' \t\n\xA0'.indexOf(ch) >= 0;
|
|
}
|
|
function readNumber() {
|
|
var has_dot = false;
|
|
var number = readWhile(function (ch) {
|
|
if (ch == '.') {
|
|
if (has_dot) {
|
|
return false;
|
|
}
|
|
has_dot = true;
|
|
return true;
|
|
}
|
|
return isDigit(ch);
|
|
});
|
|
return {
|
|
type: 'num',
|
|
value: parseFloat(number)
|
|
};
|
|
}
|
|
function symbol(id, quote) {
|
|
return {
|
|
type: 'sym',
|
|
value: id,
|
|
upper: id.toUpperCase(),
|
|
space: isWhitespace(input.peek()),
|
|
quote: quote
|
|
};
|
|
}
|
|
function getRC(a, b, c) {
|
|
if (!a && !c || a && c) {
|
|
var negative = a && /-$/.test(a);
|
|
var num = parseInt(b, 10);
|
|
if (negative) {
|
|
num = -num;
|
|
}
|
|
if (!a) {
|
|
num--;
|
|
}
|
|
return num;
|
|
}
|
|
}
|
|
function readSymbol() {
|
|
var m = input.lookingAt(/^R(\[-?)?([0-9]+)(\])?C(\[-?)?([0-9]+)(\])?/i);
|
|
if (m) {
|
|
var row = getRC(m[1], m[2], m[3]);
|
|
var col = getRC(m[4], m[5], m[6]);
|
|
if (row != null && col != null) {
|
|
input.skip(m);
|
|
return {
|
|
type: 'rc',
|
|
row: row,
|
|
col: col,
|
|
rel: (m[4] ? 1 : 0) | (m[1] ? 2 : 0)
|
|
};
|
|
}
|
|
}
|
|
return symbol(readWhile(isId));
|
|
}
|
|
function readString() {
|
|
input.next();
|
|
return {
|
|
type: 'str',
|
|
value: input.readEscaped('"')
|
|
};
|
|
}
|
|
function readSheetName() {
|
|
input.next();
|
|
return symbol(input.readEscaped('\''), true);
|
|
}
|
|
function readOperator() {
|
|
return {
|
|
type: 'op',
|
|
value: readWhile(function (ch, op) {
|
|
return op + ch in OPERATORS;
|
|
})
|
|
};
|
|
}
|
|
function readPunc() {
|
|
return {
|
|
type: 'punc',
|
|
value: input.next()
|
|
};
|
|
}
|
|
function readNext() {
|
|
if (input.eof()) {
|
|
return null;
|
|
}
|
|
var ch = input.peek(), m;
|
|
if (ch == '"') {
|
|
return readString();
|
|
}
|
|
if (ch == '\'') {
|
|
return readSheetName();
|
|
}
|
|
if (isDigit(ch)) {
|
|
return readNumber();
|
|
}
|
|
if (isIdStart(ch)) {
|
|
return readSymbol();
|
|
}
|
|
if (isOpChar(ch)) {
|
|
return readOperator();
|
|
}
|
|
if (isPunc(ch)) {
|
|
return readPunc();
|
|
}
|
|
if (m = input.lookingAt(/^#([a-z\/]+)[?!]/i)) {
|
|
input.skip(m);
|
|
return {
|
|
type: 'error',
|
|
value: m[1]
|
|
};
|
|
}
|
|
if (!options.forEditor) {
|
|
input.croak('Can\'t handle character: ' + ch);
|
|
}
|
|
return {
|
|
type: 'error',
|
|
value: input.next()
|
|
};
|
|
}
|
|
function peek() {
|
|
while (tokens.length <= index) {
|
|
readWhile(isWhitespace);
|
|
var begin = input.pos();
|
|
var tok = readNext();
|
|
if (options.forEditor && tok) {
|
|
tok.begin = begin;
|
|
tok.end = input.pos();
|
|
}
|
|
tokens.push(tok);
|
|
}
|
|
return tokens[index];
|
|
}
|
|
function next() {
|
|
var tok = peek();
|
|
if (tok) {
|
|
index++;
|
|
}
|
|
return tok;
|
|
}
|
|
function ahead(n, f) {
|
|
var pos = index, a = [];
|
|
while (n-- > 0) {
|
|
a.push(next() || EOF);
|
|
}
|
|
index = pos;
|
|
return f.apply(a, a);
|
|
}
|
|
function skip(n) {
|
|
index += n;
|
|
}
|
|
function eof() {
|
|
return peek() == null;
|
|
}
|
|
}
|
|
var EOF = { type: 'eof' };
|
|
function InputStream(input) {
|
|
var pos = 0, line = 1, col = 0;
|
|
return {
|
|
next: next,
|
|
peek: peek,
|
|
eof: eof,
|
|
croak: croak,
|
|
readWhile: readWhile,
|
|
readEscaped: readEscaped,
|
|
lookingAt: lookingAt,
|
|
skip: skip,
|
|
forward: forward,
|
|
pos: location
|
|
};
|
|
function location() {
|
|
return pos;
|
|
}
|
|
function next() {
|
|
var ch = input.charAt(pos++);
|
|
if (ch == '\n') {
|
|
line++;
|
|
col = 0;
|
|
} else {
|
|
col++;
|
|
}
|
|
return ch;
|
|
}
|
|
function peek() {
|
|
return input.charAt(pos);
|
|
}
|
|
function eof() {
|
|
return peek() === '';
|
|
}
|
|
function croak(msg) {
|
|
throw new ParseError(msg, pos);
|
|
}
|
|
function skip(ch) {
|
|
if (typeof ch == 'string') {
|
|
if (input.substr(pos, ch.length) != ch) {
|
|
croak('Expected ' + ch);
|
|
}
|
|
forward(ch.length);
|
|
} else if (ch instanceof RegExp) {
|
|
var m = ch.exec(input.substr(pos));
|
|
if (m) {
|
|
forward(m[0].length);
|
|
return m;
|
|
}
|
|
} else {
|
|
forward(ch[0].length);
|
|
}
|
|
}
|
|
function forward(n) {
|
|
while (n-- > 0) {
|
|
next();
|
|
}
|
|
}
|
|
function readEscaped(end) {
|
|
var escaped = false, str = '';
|
|
while (!eof()) {
|
|
var ch = next();
|
|
if (escaped) {
|
|
str += ch;
|
|
escaped = false;
|
|
} else if (ch == '\\') {
|
|
escaped = true;
|
|
} else if (ch == end) {
|
|
break;
|
|
} else {
|
|
str += ch;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
function readWhile(predicate) {
|
|
var str = '';
|
|
while (!eof() && predicate(peek(), str)) {
|
|
str += next();
|
|
}
|
|
return str;
|
|
}
|
|
function lookingAt(rx) {
|
|
return rx.exec(input.substr(pos));
|
|
}
|
|
}
|
|
var FORMAT_PARSERS = [];
|
|
var registerFormatParser = exports.registerFormatParser = function (p) {
|
|
FORMAT_PARSERS.push(p);
|
|
};
|
|
exports.parse = function (sheet, row, col, input) {
|
|
if (input instanceof Date) {
|
|
return {
|
|
type: 'date',
|
|
value: runtime.dateToSerial(input)
|
|
};
|
|
}
|
|
if (typeof input == 'number') {
|
|
return {
|
|
type: 'number',
|
|
value: input
|
|
};
|
|
}
|
|
if (typeof input == 'boolean') {
|
|
return {
|
|
type: 'boolean',
|
|
value: input
|
|
};
|
|
}
|
|
input += '';
|
|
if (/^'/.test(input)) {
|
|
return {
|
|
type: 'string',
|
|
value: input.substr(1)
|
|
};
|
|
}
|
|
if (/^[0-9.]+%$/.test(input)) {
|
|
var str = input.substr(0, input.length - 1);
|
|
var num = parseFloat(str);
|
|
if (!isNaN(num) && num == str) {
|
|
return {
|
|
type: 'percent',
|
|
value: num / 100
|
|
};
|
|
}
|
|
}
|
|
if (/^=/.test(input)) {
|
|
input = input.substr(1);
|
|
if (/\S/.test(input)) {
|
|
return parseFormula(sheet, row, col, input);
|
|
} else {
|
|
return {
|
|
type: 'string',
|
|
value: '=' + input
|
|
};
|
|
}
|
|
}
|
|
for (var i = 0; i < FORMAT_PARSERS.length; ++i) {
|
|
var result = FORMAT_PARSERS[i](input);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
if (input.toLowerCase() == 'true') {
|
|
return {
|
|
type: 'boolean',
|
|
value: true
|
|
};
|
|
}
|
|
if (input.toLowerCase() == 'false') {
|
|
return {
|
|
type: 'boolean',
|
|
value: false
|
|
};
|
|
}
|
|
var date = runtime.parseDate(input);
|
|
if (date) {
|
|
return {
|
|
type: 'date',
|
|
value: runtime.dateToSerial(date)
|
|
};
|
|
}
|
|
var num = parseFloat(input);
|
|
if (!isNaN(num) && input.length > 0 && num == input) {
|
|
return {
|
|
type: 'number',
|
|
value: num
|
|
};
|
|
}
|
|
return {
|
|
type: 'string',
|
|
value: input
|
|
};
|
|
};
|
|
function tokenize(input, row, col) {
|
|
var tokens = [];
|
|
input = TokenStream(input, {
|
|
forEditor: true,
|
|
row: row,
|
|
col: col
|
|
});
|
|
while (!input.eof()) {
|
|
tokens.push(next());
|
|
}
|
|
var tok = tokens[0];
|
|
if (tok.type == 'op' && tok.value == '=') {
|
|
tok.type = 'startexp';
|
|
}
|
|
return tokens;
|
|
function next() {
|
|
var tok = input.next();
|
|
if (tok.type == 'sym') {
|
|
if (tok.upper == 'TRUE') {
|
|
tok.type = 'bool';
|
|
tok.value = true;
|
|
} else if (tok.upper == 'FALSE') {
|
|
tok.type = 'bool';
|
|
tok.value = false;
|
|
}
|
|
} else if (tok.type == 'ref') {
|
|
tok = {
|
|
type: 'ref',
|
|
ref: row != null && col != null ? tok.absolute(row, col) : tok,
|
|
begin: tok.begin,
|
|
end: tok.end
|
|
};
|
|
}
|
|
return tok;
|
|
}
|
|
}
|
|
exports.parseFormula = parseFormula;
|
|
exports.parseReference = parseReference;
|
|
exports.compile = makeFormula;
|
|
exports.InputStream = InputStream;
|
|
exports.ParseError = ParseError;
|
|
exports.tokenize = tokenize;
|
|
registerFormatParser(function (input) {
|
|
var m;
|
|
if (m = /^(\d+):(\d+)$/.exec(input)) {
|
|
var hh = parseInt(m[1], 10);
|
|
var mm = parseInt(m[2], 10);
|
|
return {
|
|
type: 'date',
|
|
format: 'hh:mm',
|
|
value: runtime.packTime(hh, mm, 0, 0)
|
|
};
|
|
}
|
|
if (m = /^(\d+):(\d+)(\.\d+)$/.exec(input)) {
|
|
var mm = parseInt(m[1], 10);
|
|
var ss = parseInt(m[2], 10);
|
|
var ms = parseFloat(m[3]) * 1000;
|
|
return {
|
|
type: 'date',
|
|
format: 'mm:ss.00',
|
|
value: runtime.packTime(0, mm, ss, ms)
|
|
};
|
|
}
|
|
if (m = /^(\d+):(\d+):(\d+)$/.exec(input)) {
|
|
var hh = parseInt(m[1], 10);
|
|
var mm = parseInt(m[2], 10);
|
|
var ss = parseInt(m[3], 10);
|
|
return {
|
|
type: 'date',
|
|
format: 'hh:mm:ss',
|
|
value: runtime.packTime(hh, mm, ss, 0)
|
|
};
|
|
}
|
|
if (m = /^(\d+):(\d+):(\d+)(\.\d+)$/.exec(input)) {
|
|
var hh = parseInt(m[1], 10);
|
|
var mm = parseInt(m[2], 10);
|
|
var ss = parseInt(m[3], 10);
|
|
var ms = parseFloat(m[4]) * 1000;
|
|
return {
|
|
type: 'date',
|
|
format: 'hh:mm:ss.00',
|
|
value: runtime.packTime(hh, mm, ss, ms)
|
|
};
|
|
}
|
|
});
|
|
registerFormatParser(function (input) {
|
|
var m;
|
|
var culture = kendo.culture();
|
|
var comma = culture.numberFormat[','];
|
|
var dot = culture.numberFormat['.'];
|
|
var currency = culture.numberFormat.currency.symbol;
|
|
var rx = new RegExp('^(\\' + currency + '\\s*)?(\\d+(\\' + comma + '\\d{3})*(\\' + dot + '\\d+)?)(\\s*\\' + currency + ')?$');
|
|
if (m = rx.exec(input)) {
|
|
var value = m[2].replace(new RegExp('\\' + comma, 'g'), '').replace(dot, '.');
|
|
var format = '#';
|
|
if (m[1] || m[3] || m[5]) {
|
|
format += ',#';
|
|
}
|
|
if (m[4]) {
|
|
format += '.' + repeat('0', m[1] || m[5] ? 2 : m[4].length - 1);
|
|
}
|
|
if (m[1]) {
|
|
format = '"' + m[1] + '"' + format;
|
|
}
|
|
if (m[5]) {
|
|
format = format + '"' + m[5] + '"';
|
|
}
|
|
return {
|
|
type: 'number',
|
|
format: format == '#' ? null : format,
|
|
value: parseFloat(value)
|
|
};
|
|
}
|
|
});
|
|
function repeat(str, len) {
|
|
var out = '';
|
|
while (len-- > 0) {
|
|
out += str;
|
|
}
|
|
return out;
|
|
}
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/excel-reader', [
|
|
'kendo.core',
|
|
'kendo.color',
|
|
'util/parse-xml',
|
|
'spreadsheet/calc'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var parseXML = kendo.util.parseXML;
|
|
var parseReference = kendo.spreadsheet.calc.parseReference;
|
|
function readExcel(file, workbook, deferred) {
|
|
var reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
var zip = new JSZip(e.target.result);
|
|
readWorkbook(zip, workbook, deferred);
|
|
};
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
var SEL_CELL = [
|
|
'sheetData',
|
|
'row',
|
|
'c'
|
|
];
|
|
var SEL_COL = [
|
|
'cols',
|
|
'col'
|
|
];
|
|
var SEL_DEFINED_NAME = [
|
|
'definedNames',
|
|
'definedName'
|
|
];
|
|
var SEL_FORMULA = [
|
|
'sheetData',
|
|
'row',
|
|
'c',
|
|
'f'
|
|
];
|
|
var SEL_MERGE = [
|
|
'mergeCells',
|
|
'mergeCell'
|
|
];
|
|
var SEL_PANE = [
|
|
'sheetViews',
|
|
'sheetView',
|
|
'pane'
|
|
];
|
|
var SEL_ROW = [
|
|
'sheetData',
|
|
'row'
|
|
];
|
|
var SEL_SELECTION = [
|
|
'sheetViews',
|
|
'sheetView',
|
|
'selection'
|
|
];
|
|
var SEL_SHEET = [
|
|
'sheets',
|
|
'sheet'
|
|
];
|
|
var SEL_STRING = [
|
|
'sheetData',
|
|
'row',
|
|
'c',
|
|
'is'
|
|
];
|
|
var SEL_TEXT = ['t'];
|
|
var SEL_SHARED_STRING = ['si'];
|
|
var SEL_VALUE = [
|
|
'sheetData',
|
|
'row',
|
|
'c',
|
|
'v'
|
|
];
|
|
var SEL_VIEW = [
|
|
'bookViews',
|
|
'workbookView'
|
|
];
|
|
var SEL_SHEET_VIEW = [
|
|
'sheetViews',
|
|
'sheetView'
|
|
];
|
|
function readWorkbook(zip, workbook, deferred) {
|
|
var strings = readStrings(zip);
|
|
var relationships = readRelationships(zip, 'workbook.xml');
|
|
var theme = readTheme(zip, relationships.byType.theme[0]);
|
|
var styles = readStyles(zip, theme);
|
|
var items = [];
|
|
var activeSheet = 0;
|
|
parse(zip, 'xl/workbook.xml', {
|
|
enter: function (tag, attrs) {
|
|
if (this.is(SEL_SHEET)) {
|
|
var relId = attrs['r:id'];
|
|
var file = relationships.byId[relId];
|
|
var name = attrs.name;
|
|
var dim = sheetDimensions(zip, file);
|
|
items.push({
|
|
workbook: workbook,
|
|
zip: zip,
|
|
strings: strings,
|
|
styles: styles,
|
|
file: file,
|
|
options: {
|
|
name: name,
|
|
rows: Math.max(workbook.options.rows || 0, dim.rows),
|
|
columns: Math.max(workbook.options.columns || 0, dim.cols),
|
|
columnWidth: dim.columnWidth,
|
|
rowHeight: dim.rowHeight
|
|
}
|
|
});
|
|
} else if (this.is(SEL_VIEW)) {
|
|
if (attrs.activeTab) {
|
|
activeSheet = integer(attrs.activeTab);
|
|
}
|
|
}
|
|
},
|
|
text: function (text) {
|
|
var attrs = this.is(SEL_DEFINED_NAME);
|
|
if (attrs && !(bool(attrs['function']) || bool(attrs.vbProcedure))) {
|
|
var ref = parseReference(text, true);
|
|
workbook.defineName(attrs.name, ref, bool(attrs.hidden));
|
|
}
|
|
}
|
|
});
|
|
loadSheets(items, workbook, deferred).then(function () {
|
|
var sheets = workbook.sheets();
|
|
recalcSheets(sheets);
|
|
workbook.activeSheet(sheets[activeSheet]);
|
|
});
|
|
}
|
|
function loadSheets(items, workbook, deferred) {
|
|
var ready = new $.Deferred().resolve();
|
|
for (var i = 0; i < items.length; i++) {
|
|
(function (entry, i) {
|
|
ready = ready.then(function () {
|
|
var sheet = workbook.insertSheet(entry.options);
|
|
sheet.suspendChanges(true);
|
|
var promise = queueSheet(sheet, entry);
|
|
if (deferred) {
|
|
var args = {
|
|
sheet: sheet,
|
|
progress: i / (items.length - 1)
|
|
};
|
|
promise.then(function () {
|
|
deferred.notify(args);
|
|
});
|
|
}
|
|
return promise;
|
|
});
|
|
}(items[i], i));
|
|
}
|
|
if (deferred) {
|
|
ready.then(function () {
|
|
deferred.resolve();
|
|
});
|
|
}
|
|
return ready;
|
|
}
|
|
function queueSheet(sheet, ctx) {
|
|
var deferred = new $.Deferred();
|
|
setTimeout(function () {
|
|
readSheet(ctx.zip, ctx.file, sheet, ctx.strings, ctx.styles);
|
|
deferred.resolve();
|
|
}, 0);
|
|
return deferred;
|
|
}
|
|
function recalcSheets(sheets) {
|
|
for (var i = 0; i < sheets.length; i++) {
|
|
sheets[i].suspendChanges(false).triggerChange({ recalc: true });
|
|
}
|
|
}
|
|
function sheetDimensions(zip, file) {
|
|
var dim = {
|
|
rows: 0,
|
|
cols: 0
|
|
};
|
|
parse(zip, 'xl/' + file, {
|
|
enter: function (tag, attrs) {
|
|
if (tag == 'dimension') {
|
|
var ref = parseReference(attrs.ref);
|
|
if (ref.bottomRight) {
|
|
dim.cols = ref.bottomRight.col + 1;
|
|
dim.rows = ref.bottomRight.row + 1;
|
|
}
|
|
} else if (tag === 'sheetFormatPr') {
|
|
if (attrs.defaultColWidth) {
|
|
dim.columnWidth = toColWidth(parseFloat(attrs.defaultColWidth));
|
|
}
|
|
if (attrs.defaultRowHeight) {
|
|
dim.rowHeight = toRowHeight(parseFloat(attrs.defaultRowHeight));
|
|
}
|
|
} else if (this.is(SEL_ROW)) {
|
|
this.exit();
|
|
}
|
|
}
|
|
});
|
|
return dim;
|
|
}
|
|
function toColWidth(size) {
|
|
var maximumDigitWidth = 7;
|
|
var fraction = (256 * size + Math.floor(128 / maximumDigitWidth)) / 256;
|
|
return Math.floor(fraction) * maximumDigitWidth;
|
|
}
|
|
function toRowHeight(pts) {
|
|
return pts * 1.5625;
|
|
}
|
|
function readSheet(zip, file, sheet, strings, styles) {
|
|
var ref, type, value, formula, formulaRange;
|
|
var nCols = sheet._columns._count;
|
|
var prevCellRef = null;
|
|
parse(zip, 'xl/' + file, {
|
|
enter: function (tag, attrs) {
|
|
if (this.is(SEL_CELL)) {
|
|
value = null;
|
|
formula = null;
|
|
formulaRange = null;
|
|
ref = attrs.r;
|
|
if (ref == null) {
|
|
ref = parseReference(prevCellRef);
|
|
ref.col++;
|
|
ref = ref.toString();
|
|
}
|
|
prevCellRef = ref;
|
|
type = attrs.t;
|
|
var styleIndex = attrs.s;
|
|
if (styleIndex != null) {
|
|
applyStyle(sheet, ref, styles, styleIndex);
|
|
}
|
|
} else if (this.is(SEL_MERGE)) {
|
|
sheet.range(attrs.ref).merge();
|
|
} else if (this.is(SEL_COL)) {
|
|
var start = integer(attrs.min) - 1;
|
|
var stop = Math.min(nCols, integer(attrs.max)) - 1;
|
|
if (attrs.width) {
|
|
var width = toColWidth(parseFloat(attrs.width));
|
|
sheet._columns.values.value(start, stop, width);
|
|
}
|
|
if (attrs.hidden === '1') {
|
|
for (var ci = start; ci <= stop; ci++) {
|
|
sheet.hideColumn(ci);
|
|
}
|
|
}
|
|
} else if (this.is(SEL_ROW)) {
|
|
var row = integer(attrs.r) - 1;
|
|
if (attrs.ht) {
|
|
var height = toRowHeight(parseFloat(attrs.ht));
|
|
sheet._rows.values.value(row, row, height);
|
|
}
|
|
if (attrs.hidden === '1') {
|
|
sheet.hideRow(row);
|
|
}
|
|
} else if (this.is(SEL_SELECTION)) {
|
|
if (attrs.activeCell) {
|
|
var acRef = parseReference(attrs.activeCell);
|
|
sheet.select(acRef, true);
|
|
}
|
|
} else if (this.is(SEL_PANE)) {
|
|
if (attrs.state == 'frozen') {
|
|
if (attrs.xSplit) {
|
|
sheet.frozenColumns(integer(attrs.xSplit));
|
|
}
|
|
if (attrs.ySplit) {
|
|
sheet.frozenRows(integer(attrs.ySplit));
|
|
}
|
|
}
|
|
} else if (this.is(SEL_SHEET_VIEW)) {
|
|
sheet.options.showGridLines = bool(attrs.showGridLines, true);
|
|
}
|
|
},
|
|
leave: function (tag) {
|
|
if (this.is(SEL_CELL)) {
|
|
if (formula != null) {
|
|
try {
|
|
sheet.range(formulaRange || ref).formula(formula);
|
|
} catch (ex) {
|
|
sheet.range(formulaRange || ref).value(formula).background('#ffaaaa');
|
|
}
|
|
} else if (value != null) {
|
|
var range = sheet.range(ref);
|
|
if (!range._get('formula')) {
|
|
if (!type || type == 'n') {
|
|
value = parseFloat(value);
|
|
} else if (type == 's') {
|
|
value = strings[integer(value)];
|
|
} else if (type == 'b') {
|
|
value = value === '1';
|
|
} else if (type == 'd') {
|
|
value = kendo.parseDate(value);
|
|
}
|
|
range.value(value);
|
|
}
|
|
}
|
|
} else if (tag == 'cols') {
|
|
sheet._columns._refresh();
|
|
} else if (tag == 'sheetData') {
|
|
sheet._rows._refresh();
|
|
}
|
|
},
|
|
text: function (text) {
|
|
var attrs;
|
|
if (this.is(SEL_VALUE) || this.is(SEL_STRING)) {
|
|
value = text;
|
|
} else if (attrs = this.is(SEL_FORMULA)) {
|
|
formula = text;
|
|
if (attrs.t == 'shared') {
|
|
formulaRange = attrs.ref;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
var BORDER_WIDTHS = {
|
|
'none': 0,
|
|
'thin': 1,
|
|
'medium': 2,
|
|
'dashed': 1,
|
|
'dotted': 1,
|
|
'thick': 3,
|
|
'double': 3,
|
|
'hair': 1,
|
|
'mediumDashed': 2,
|
|
'dashDot': 1,
|
|
'mediumDashDot': 2,
|
|
'dashDotDot': 1,
|
|
'mediumDashDotDot': 2,
|
|
'slantDashDot': 1
|
|
};
|
|
var DEFAULT_FORMATS = {
|
|
0: 'General',
|
|
1: '0',
|
|
2: '0.00',
|
|
3: '#,##0',
|
|
4: '#,##0.00',
|
|
9: '0%',
|
|
10: '0.00%',
|
|
11: '0.00E+00',
|
|
12: '# ?/?',
|
|
13: '# ??/??',
|
|
14: 'mm-dd-yy',
|
|
15: 'd-mmm-yy',
|
|
16: 'd-mmm',
|
|
17: 'mmm-yy',
|
|
18: 'h:mm AM/PM',
|
|
19: 'h:mm:ss AM/PM',
|
|
20: 'h:mm',
|
|
21: 'h:mm:ss',
|
|
22: 'm/d/yy h:mm',
|
|
37: '#,##0 ;(#,##0)',
|
|
38: '#,##0 ;[Red](#,##0)',
|
|
39: '#,##0.00;(#,##0.00)',
|
|
40: '#,##0.00;[Red](#,##0.00)',
|
|
45: 'mm:ss',
|
|
46: '[h]:mm:ss',
|
|
47: 'mmss.0',
|
|
48: '##0.0E+0',
|
|
49: '@'
|
|
};
|
|
function applyStyle(sheet, ref, styles, styleIndex) {
|
|
var range = sheet.range(ref);
|
|
var xf = styles.inlineStyles[styleIndex], base, value;
|
|
if (xf.xfId) {
|
|
base = styles.namedStyles[xf.xfId];
|
|
}
|
|
if (shouldSet('applyBorder', 'borderId')) {
|
|
setBorder(styles.borders[value]);
|
|
}
|
|
if (shouldSet('applyFont', 'fontId')) {
|
|
setFont(styles.fonts[value]);
|
|
}
|
|
if (shouldSet('applyAlignment', 'textAlign')) {
|
|
range.textAlign(value);
|
|
}
|
|
if (shouldSet('applyAlignment', 'verticalAlign')) {
|
|
range.verticalAlign(value);
|
|
}
|
|
if (shouldSet('applyAlignment', 'wrapText')) {
|
|
range._property('wrap', value);
|
|
}
|
|
if (shouldSet('applyFill', 'fillId')) {
|
|
setFill(styles.fills[value]);
|
|
}
|
|
if (shouldSet('applyNumberFormat', 'numFmtId')) {
|
|
setFormat(styles.numFmts[value] || DEFAULT_FORMATS[value]);
|
|
}
|
|
function setFormat(f) {
|
|
var format = typeof f == 'string' ? f : f.formatCode;
|
|
if (format != null && !/^general$/i.test(format)) {
|
|
format = format.replace(/^\[\$-[0-9]+\]/, '');
|
|
range.format(format);
|
|
}
|
|
}
|
|
function setFill(f) {
|
|
if (f.type == 'solid') {
|
|
range.background(f.color);
|
|
}
|
|
}
|
|
function setFont(f) {
|
|
range.fontFamily(f.name);
|
|
range._property('fontSize', f.size);
|
|
if (f.bold) {
|
|
range.bold(true);
|
|
}
|
|
if (f.italic) {
|
|
range.italic(true);
|
|
}
|
|
}
|
|
function setBorder(b) {
|
|
function set(side, prop) {
|
|
var border = b[side];
|
|
if (!border) {
|
|
return;
|
|
}
|
|
var width = BORDER_WIDTHS[border.style];
|
|
if (width === 0) {
|
|
return;
|
|
}
|
|
var color = border.color;
|
|
if (color == null) {
|
|
color = '#000';
|
|
}
|
|
range._property(prop, {
|
|
size: width,
|
|
color: color
|
|
});
|
|
}
|
|
set('left', 'borderLeft');
|
|
set('top', 'borderTop');
|
|
set('right', 'borderRight');
|
|
set('bottom', 'borderBottom');
|
|
}
|
|
function shouldSet(applyName, propName) {
|
|
var t = xf[applyName];
|
|
if (t != null && !t) {
|
|
return false;
|
|
}
|
|
value = xf[propName];
|
|
if (base && value == null) {
|
|
t = base[applyName];
|
|
if (t != null && !t) {
|
|
return false;
|
|
}
|
|
value = base[propName];
|
|
}
|
|
return value != null;
|
|
}
|
|
}
|
|
function parse(zip, file, callbacks) {
|
|
var part = zip.files[file];
|
|
if (part) {
|
|
parseXML(part.asUint8Array(), callbacks);
|
|
}
|
|
}
|
|
function readStrings(zip) {
|
|
var strings = [];
|
|
var current;
|
|
parse(zip, 'xl/sharedStrings.xml', {
|
|
enter: function () {
|
|
if (this.is(SEL_SHARED_STRING)) {
|
|
current = '';
|
|
}
|
|
},
|
|
leave: function () {
|
|
if (this.is(SEL_SHARED_STRING)) {
|
|
strings.push(current);
|
|
}
|
|
},
|
|
text: function (text) {
|
|
if (this.is(SEL_TEXT)) {
|
|
current += text;
|
|
}
|
|
}
|
|
});
|
|
return strings;
|
|
}
|
|
function readRelationships(zip, file) {
|
|
var map = {
|
|
byId: {},
|
|
byType: { theme: [] }
|
|
};
|
|
parse(zip, 'xl/_rels/' + file + '.rels', {
|
|
enter: function (tag, attrs) {
|
|
if (tag == 'Relationship') {
|
|
map.byId[attrs.Id] = attrs.Target;
|
|
var type = attrs.Type.match(/\w+$/)[0];
|
|
var entries = map.byType[type] || [];
|
|
entries.push(attrs.Target);
|
|
map.byType[type] = entries;
|
|
}
|
|
}
|
|
});
|
|
return map;
|
|
}
|
|
var SEL_BORDER = [
|
|
'borders',
|
|
'border'
|
|
];
|
|
var SEL_FILL = [
|
|
'fills',
|
|
'fill'
|
|
];
|
|
var SEL_FONT = [
|
|
'fonts',
|
|
'font'
|
|
];
|
|
var SEL_INLINE_STYLE = [
|
|
'cellXfs',
|
|
'xf'
|
|
];
|
|
var SEL_NAMED_STYLE = [
|
|
'cellStyleXfs',
|
|
'xf'
|
|
];
|
|
var SEL_NUM_FMT = [
|
|
'numFmts',
|
|
'numFmt'
|
|
];
|
|
var INDEXED_COLORS = [
|
|
toCSSColor('FF000000'),
|
|
toCSSColor('FFFFFFFF'),
|
|
toCSSColor('FFFF0000'),
|
|
toCSSColor('FF00FF00'),
|
|
toCSSColor('FF0000FF'),
|
|
toCSSColor('FFFFFF00'),
|
|
toCSSColor('FFFF00FF'),
|
|
toCSSColor('FF00FFFF'),
|
|
toCSSColor('FF000000'),
|
|
toCSSColor('FFFFFFFF'),
|
|
toCSSColor('FFFF0000'),
|
|
toCSSColor('FF00FF00'),
|
|
toCSSColor('FF0000FF'),
|
|
toCSSColor('FFFFFF00'),
|
|
toCSSColor('FFFF00FF'),
|
|
toCSSColor('FF00FFFF'),
|
|
toCSSColor('FF800000'),
|
|
toCSSColor('FF008000'),
|
|
toCSSColor('FF000080'),
|
|
toCSSColor('FF808000'),
|
|
toCSSColor('FF800080'),
|
|
toCSSColor('FF008080'),
|
|
toCSSColor('FFC0C0C0'),
|
|
toCSSColor('FF808080'),
|
|
toCSSColor('FF9999FF'),
|
|
toCSSColor('FF993366'),
|
|
toCSSColor('FFFFFFCC'),
|
|
toCSSColor('FFCCFFFF'),
|
|
toCSSColor('FF660066'),
|
|
toCSSColor('FFFF8080'),
|
|
toCSSColor('FF0066CC'),
|
|
toCSSColor('FFCCCCFF'),
|
|
toCSSColor('FF000080'),
|
|
toCSSColor('FFFF00FF'),
|
|
toCSSColor('FFFFFF00'),
|
|
toCSSColor('FF00FFFF'),
|
|
toCSSColor('FF800080'),
|
|
toCSSColor('FF800000'),
|
|
toCSSColor('FF008080'),
|
|
toCSSColor('FF0000FF'),
|
|
toCSSColor('FF00CCFF'),
|
|
toCSSColor('FFCCFFFF'),
|
|
toCSSColor('FFCCFFCC'),
|
|
toCSSColor('FFFFFF99'),
|
|
toCSSColor('FF99CCFF'),
|
|
toCSSColor('FFFF99CC'),
|
|
toCSSColor('FFCC99FF'),
|
|
toCSSColor('FFFFCC99'),
|
|
toCSSColor('FF3366FF'),
|
|
toCSSColor('FF33CCCC'),
|
|
toCSSColor('FF99CC00'),
|
|
toCSSColor('FFFFCC00'),
|
|
toCSSColor('FFFF9900'),
|
|
toCSSColor('FFFF6600'),
|
|
toCSSColor('FF666699'),
|
|
toCSSColor('FF969696'),
|
|
toCSSColor('FF003366'),
|
|
toCSSColor('FF339966'),
|
|
toCSSColor('FF003300'),
|
|
toCSSColor('FF333300'),
|
|
toCSSColor('FF993300'),
|
|
toCSSColor('FF993366'),
|
|
toCSSColor('FF333399'),
|
|
toCSSColor('FF333333'),
|
|
toCSSColor('FF000000'),
|
|
toCSSColor('FFFFFFFF')
|
|
];
|
|
function readStyles(zip, theme) {
|
|
var styles = {
|
|
fonts: [],
|
|
numFmts: {},
|
|
fills: [],
|
|
borders: [],
|
|
namedStyles: [],
|
|
inlineStyles: []
|
|
};
|
|
var font = null;
|
|
var fill = null;
|
|
var border = null;
|
|
var xf = null;
|
|
parse(zip, 'xl/styles.xml', {
|
|
enter: function (tag, attrs, closed) {
|
|
if (this.is(SEL_NUM_FMT)) {
|
|
styles.numFmts[attrs.numFmtId] = attrs;
|
|
} else if (this.is(SEL_FONT)) {
|
|
styles.fonts.push(font = {});
|
|
} else if (font) {
|
|
if (tag == 'sz') {
|
|
font.size = parseFloat(attrs.val);
|
|
} else if (tag == 'name') {
|
|
font.name = attrs.val;
|
|
} else if (tag == 'b') {
|
|
font.bold = bool(attrs.val, true);
|
|
} else if (tag == 'i') {
|
|
font.italic = bool(attrs.val, true);
|
|
}
|
|
} else if (this.is(SEL_FILL)) {
|
|
styles.fills.push(fill = {});
|
|
} else if (fill) {
|
|
if (tag == 'patternFill') {
|
|
fill.type = attrs.patternType;
|
|
} else if (tag == 'fgColor' && fill.type === 'solid') {
|
|
fill.color = getColor(attrs);
|
|
} else if (tag == 'bgColor' && fill.type !== 'solid') {
|
|
fill.color = getColor(attrs);
|
|
}
|
|
} else if (this.is(SEL_BORDER)) {
|
|
styles.borders.push(border = {});
|
|
} else if (border) {
|
|
if (/^(?:left|top|right|bottom)$/.test(tag) && attrs.style) {
|
|
border[tag] = { style: attrs.style };
|
|
}
|
|
if (tag == 'color') {
|
|
var side = this.stack[this.stack.length - 2].$tag;
|
|
border[side].color = getColor(attrs);
|
|
}
|
|
} else if (this.is(SEL_NAMED_STYLE)) {
|
|
xf = getXf(attrs);
|
|
styles.namedStyles.push(xf);
|
|
if (closed) {
|
|
xf = null;
|
|
}
|
|
} else if (this.is(SEL_INLINE_STYLE)) {
|
|
xf = getXf(attrs);
|
|
styles.inlineStyles.push(xf);
|
|
if (closed) {
|
|
xf = null;
|
|
}
|
|
} else if (xf) {
|
|
if (tag == 'alignment') {
|
|
if (/^(?:left|center|right|justify)$/.test(attrs.horizontal)) {
|
|
xf.textAlign = attrs.horizontal;
|
|
}
|
|
if (/^(?:top|center|bottom)$/.test(attrs.vertical)) {
|
|
xf.verticalAlign = attrs.vertical;
|
|
}
|
|
if (attrs.wrapText != null) {
|
|
xf.wrapText = bool(attrs.wrapText);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
leave: function (tag) {
|
|
if (this.is(SEL_FONT)) {
|
|
font = null;
|
|
} else if (this.is(SEL_FILL)) {
|
|
fill = null;
|
|
} else if (this.is(SEL_BORDER)) {
|
|
border = null;
|
|
} else if (tag == 'xf') {
|
|
xf = null;
|
|
}
|
|
}
|
|
});
|
|
function getXf(attrs) {
|
|
var xf = {
|
|
borderId: integer(attrs.borderId),
|
|
fillId: integer(attrs.fillId),
|
|
fontId: integer(attrs.fontId),
|
|
numFmtId: integer(attrs.numFmtId),
|
|
pivotButton: bool(attrs.pivotButton),
|
|
quotePrefix: bool(attrs.quotePrefix),
|
|
xfId: integer(attrs.xfId)
|
|
};
|
|
addBool('applyAlignment');
|
|
addBool('applyBorder');
|
|
addBool('applyFill');
|
|
addBool('applyFont');
|
|
addBool('applyNumberFormat');
|
|
addBool('applyProtection');
|
|
function addBool(name) {
|
|
if (attrs[name] != null) {
|
|
xf[name] = bool(attrs[name]);
|
|
}
|
|
}
|
|
return xf;
|
|
}
|
|
function getColor(attrs) {
|
|
if (attrs.rgb) {
|
|
return toCSSColor(attrs.rgb);
|
|
} else if (attrs.indexed) {
|
|
return INDEXED_COLORS[integer(attrs.indexed)];
|
|
} else if (attrs.theme) {
|
|
var themeColor = theme.colorScheme[integer(attrs.theme)];
|
|
var color = kendo.parseColor(themeColor);
|
|
if (attrs.tint) {
|
|
color = color.toHSL();
|
|
var tint = parseFloat(attrs.tint);
|
|
if (tint < 0) {
|
|
color.l = color.l * (1 + tint);
|
|
} else {
|
|
color.l = color.l * (1 - tint) + (100 - 100 * (1 - tint));
|
|
}
|
|
}
|
|
return color.toCssRgba();
|
|
}
|
|
}
|
|
return styles;
|
|
}
|
|
var SEL_SCHEME_RGBCLR = [
|
|
'a:clrScheme',
|
|
'*',
|
|
'a:srgbClr'
|
|
];
|
|
var SEL_SCHEME_SYSCLR = [
|
|
'a:clrScheme',
|
|
'*',
|
|
'a:sysClr'
|
|
];
|
|
function readTheme(zip, rel) {
|
|
var scheme = [];
|
|
var theme = { colorScheme: scheme };
|
|
var file = 'xl/' + rel;
|
|
if (zip.files[file]) {
|
|
parse(zip, file, {
|
|
enter: function (tag, attrs) {
|
|
if (this.is(SEL_SCHEME_SYSCLR)) {
|
|
scheme.push(toCSSColor(attrs.val == 'window' ? 'FFFFFFFF' : 'FF000000'));
|
|
} else if (this.is(SEL_SCHEME_RGBCLR)) {
|
|
scheme.push(toCSSColor('FF' + attrs.val));
|
|
}
|
|
}
|
|
});
|
|
if (scheme.length > 3) {
|
|
swap(scheme, 0, 1);
|
|
swap(scheme, 2, 3);
|
|
}
|
|
}
|
|
function swap(arr, a, b) {
|
|
var tmp = arr[a];
|
|
arr[a] = arr[b];
|
|
arr[b] = tmp;
|
|
}
|
|
return theme;
|
|
}
|
|
function integer(val) {
|
|
return val == null ? null : parseInt(val, 10);
|
|
}
|
|
function bool(val, def) {
|
|
if (val == null) {
|
|
return def;
|
|
}
|
|
return val == 'true' || val === true || val == 1;
|
|
}
|
|
function toCSSColor(rgb) {
|
|
var m = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(rgb);
|
|
return 'rgba(' + parseInt(m[2], 16) + ', ' + parseInt(m[3], 16) + ', ' + parseInt(m[4], 16) + ', ' + parseInt(m[1], 16) / 255 + ')';
|
|
}
|
|
kendo.spreadsheet.readExcel = readExcel;
|
|
kendo.spreadsheet._readSheet = readSheet;
|
|
kendo.spreadsheet._readStrings = readStrings;
|
|
kendo.spreadsheet._readStyles = readStyles;
|
|
kendo.spreadsheet._readTheme = readTheme;
|
|
kendo.spreadsheet._readWorkbook = readWorkbook;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/workbook', [
|
|
'kendo.core',
|
|
'spreadsheet/runtime',
|
|
'spreadsheet/references',
|
|
'spreadsheet/excel-reader'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Workbook = kendo.Observable.extend({
|
|
init: function (options, view) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.options = options;
|
|
this._view = view;
|
|
this._sheets = [];
|
|
this._sheetsSearchCache = {};
|
|
this._sheet = this.insertSheet({
|
|
rows: this.options.rows,
|
|
columns: this.options.columns,
|
|
rowHeight: this.options.rowHeight,
|
|
columnWidth: this.options.columnWidth,
|
|
headerHeight: this.options.headerHeight,
|
|
headerWidth: this.options.headerWidth,
|
|
dataSource: this.options.dataSource
|
|
});
|
|
this.undoRedoStack = new kendo.util.UndoRedoStack();
|
|
this.undoRedoStack.bind([
|
|
'undo',
|
|
'redo'
|
|
], this._onUndoRedo.bind(this));
|
|
this._context = new kendo.spreadsheet.FormulaContext(this);
|
|
this._validationContext = new kendo.spreadsheet.ValidationFormulaContext(this);
|
|
this._names = Object.create(null);
|
|
this.fromJSON(this.options);
|
|
},
|
|
clipboard: function () {
|
|
if (!this._clipboard) {
|
|
this._clipboard = new kendo.spreadsheet.Clipboard(this);
|
|
}
|
|
return this._clipboard;
|
|
},
|
|
destroy: function () {
|
|
this.unbind();
|
|
if (this._clipboard) {
|
|
this._clipboard.destroy();
|
|
}
|
|
},
|
|
events: [
|
|
'change',
|
|
'excelImport',
|
|
'excelExport'
|
|
],
|
|
_sheetChange: function (e) {
|
|
this.trigger('change', e);
|
|
},
|
|
_inputForRef: function (ref) {
|
|
return new kendo.spreadsheet.Range(ref, this._sheet).input();
|
|
},
|
|
_onUndoRedo: function (e) {
|
|
e.command.range().select();
|
|
},
|
|
execute: function (options) {
|
|
var commandOptions = $.extend({ workbook: this }, options.options);
|
|
var command = new kendo.spreadsheet[options.command](commandOptions);
|
|
var sheet = this.activeSheet();
|
|
if (commandOptions.origin) {
|
|
command.origin(commandOptions.origin);
|
|
}
|
|
if (commandOptions.operatingRange) {
|
|
command.range(commandOptions.operatingRange);
|
|
} else if (commandOptions.editActiveCell) {
|
|
command.range(sheet.activeCellSelection());
|
|
} else {
|
|
command.range(sheet.selection());
|
|
}
|
|
var result = command.exec();
|
|
if (!result || result.reason !== 'error') {
|
|
this.undoRedoStack.push(command);
|
|
}
|
|
return result;
|
|
},
|
|
resetFormulas: function () {
|
|
this._sheets.forEach(function (sheet) {
|
|
sheet.resetFormulas();
|
|
});
|
|
},
|
|
resetValidations: function () {
|
|
this._sheets.forEach(function (sheet) {
|
|
sheet.resetValidations();
|
|
});
|
|
},
|
|
refresh: function (reason) {
|
|
if (reason.recalc) {
|
|
this.resetFormulas();
|
|
this.resetValidations();
|
|
this._sheet.recalc(this._context);
|
|
this._sheet.revalidate(this._validationContext);
|
|
}
|
|
},
|
|
activeSheet: function (sheet) {
|
|
if (sheet === undefined) {
|
|
return this._sheet;
|
|
}
|
|
if (!this.sheetByName(sheet.name())) {
|
|
return;
|
|
}
|
|
this._sheet = sheet;
|
|
sheet.triggerChange(kendo.spreadsheet.ALL_REASONS);
|
|
},
|
|
moveSheetToIndex: function (sheet, toIndex) {
|
|
var fromIndex = this.sheetIndex(sheet);
|
|
var sheets = this._sheets;
|
|
if (fromIndex === -1) {
|
|
return;
|
|
}
|
|
this._sheetsSearchCache = {};
|
|
sheets.splice(toIndex, 0, sheets.splice(fromIndex, 1)[0]);
|
|
this.trigger('change', { sheetSelection: true });
|
|
},
|
|
insertSheet: function (options) {
|
|
options = options || {};
|
|
var that = this;
|
|
var insertIndex = typeof options.index === 'number' ? options.index : that._sheets.length;
|
|
var sheetName;
|
|
var sheets = that._sheets;
|
|
var getUniqueSheetName = function (sheetNameSuffix) {
|
|
sheetNameSuffix = sheetNameSuffix ? sheetNameSuffix : 1;
|
|
var name = 'Sheet' + sheetNameSuffix;
|
|
if (!that.sheetByName(name)) {
|
|
return name;
|
|
}
|
|
return getUniqueSheetName(sheetNameSuffix + 1);
|
|
};
|
|
if (options.name && that.sheetByName(options.name)) {
|
|
return;
|
|
}
|
|
this._sheetsSearchCache = {};
|
|
sheetName = options.name || getUniqueSheetName();
|
|
var sheet = new kendo.spreadsheet.Sheet(options.rows || this.options.rows, options.columns || this.options.columns, options.rowHeight || this.options.rowHeight, options.columnWidth || this.options.columnWidth, options.headerHeight || this.options.headerHeight, options.headerWidth || this.options.headerWidth);
|
|
sheet._workbook = this;
|
|
sheet._name(sheetName);
|
|
sheet.bind('change', this._sheetChange.bind(this));
|
|
sheets.splice(insertIndex, 0, sheet);
|
|
if (options.data) {
|
|
sheet.fromJSON(options.data);
|
|
}
|
|
if (options.dataSource) {
|
|
sheet.setDataSource(options.dataSource);
|
|
}
|
|
this.trigger('change', { sheetSelection: true });
|
|
return sheet;
|
|
},
|
|
sheets: function () {
|
|
return this._sheets.slice();
|
|
},
|
|
sheetByName: function (sheetName) {
|
|
return this._sheets[this.sheetIndex(sheetName)];
|
|
},
|
|
sheetByIndex: function (index) {
|
|
return this._sheets[index];
|
|
},
|
|
sheetIndex: function (sheet) {
|
|
var sheets = this._sheets;
|
|
var sheetName = (typeof sheet == 'string' ? sheet : sheet.name()).toLowerCase();
|
|
var idx = this._sheetsSearchCache[sheetName];
|
|
if (idx >= 0) {
|
|
return idx;
|
|
}
|
|
for (idx = 0; idx < sheets.length; idx++) {
|
|
var name = sheets[idx].name().toLowerCase();
|
|
this._sheetsSearchCache[name] = idx;
|
|
if (name === sheetName) {
|
|
return idx;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
renameSheet: function (sheet, newSheetName) {
|
|
var oldSheetName = sheet.name();
|
|
if (!newSheetName || oldSheetName === newSheetName) {
|
|
return;
|
|
}
|
|
sheet = this.sheetByName(oldSheetName);
|
|
if (!sheet) {
|
|
return;
|
|
}
|
|
this._sheetsSearchCache = {};
|
|
this._sheets.forEach(function (sheet) {
|
|
sheet._forFormulas(function (formula) {
|
|
formula.renameSheet(oldSheetName, newSheetName);
|
|
});
|
|
});
|
|
sheet._name(newSheetName);
|
|
this.trigger('change', { sheetSelection: true });
|
|
return sheet;
|
|
},
|
|
removeSheet: function (sheet) {
|
|
var that = this;
|
|
var sheets = that._sheets;
|
|
var name = sheet.name();
|
|
var index = that.sheetIndex(sheet);
|
|
if (sheets.length === 1) {
|
|
return;
|
|
}
|
|
this._sheetsSearchCache = {};
|
|
if (index > -1) {
|
|
sheet.unbind();
|
|
sheets.splice(index, 1);
|
|
if (that.activeSheet().name() === name) {
|
|
var newSheet = sheets[index === sheets.length ? index - 1 : index];
|
|
that.activeSheet(newSheet);
|
|
} else {
|
|
this.trigger('change', {
|
|
recalc: true,
|
|
sheetSelection: true
|
|
});
|
|
}
|
|
}
|
|
},
|
|
fromJSON: function (json) {
|
|
if (json.sheets) {
|
|
for (var idx = 0; idx < json.sheets.length; idx++) {
|
|
var sheet = this.sheetByIndex(idx);
|
|
if (!sheet) {
|
|
sheet = this.insertSheet();
|
|
}
|
|
sheet.fromJSON(json.sheets[idx]);
|
|
var dataSource = json.sheets[idx].dataSource;
|
|
if (dataSource) {
|
|
sheet.setDataSource(dataSource);
|
|
}
|
|
}
|
|
}
|
|
if (json.activeSheet) {
|
|
this.activeSheet(this.sheetByName(json.activeSheet));
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
this.resetFormulas();
|
|
this.resetValidations();
|
|
return {
|
|
activeSheet: this.activeSheet().name(),
|
|
sheets: this._sheets.map(function (sheet) {
|
|
sheet.recalc(this._context);
|
|
return sheet.toJSON();
|
|
}, this)
|
|
};
|
|
},
|
|
fromFile: function (file) {
|
|
var deferred = new $.Deferred();
|
|
var promise = deferred.promise();
|
|
var args = {
|
|
file: file,
|
|
promise: promise
|
|
};
|
|
if (file && !this.trigger('excelImport', args)) {
|
|
for (var i = 0; i < this._sheets.length; i++) {
|
|
this._sheets[i].unbind();
|
|
}
|
|
this._sheets = [];
|
|
this._sheetsSearchCache = {};
|
|
kendo.spreadsheet.readExcel(file, this, deferred);
|
|
} else {
|
|
deferred.reject();
|
|
}
|
|
return promise;
|
|
},
|
|
saveAsExcel: function (options) {
|
|
options = $.extend({}, this.options.excel, options);
|
|
var data = this.toJSON();
|
|
if (!this.trigger('excelExport', { workbook: data })) {
|
|
var workbook = new kendo.ooxml.Workbook(data);
|
|
kendo.saveAs({
|
|
dataURI: workbook.toDataURL(),
|
|
fileName: data.fileName || options.fileName,
|
|
proxyURL: options.proxyURL,
|
|
forceProxy: options.forceProxy
|
|
});
|
|
}
|
|
},
|
|
draw: function (options, callback) {
|
|
if (typeof options == 'function' && !callback) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
var parts = [], sheets = this._sheets;
|
|
(function loop(i) {
|
|
if (i < sheets.length) {
|
|
sheets[i].draw(kendo.spreadsheet.SHEETREF, options, function (group) {
|
|
parts.push(group);
|
|
loop(i + 1);
|
|
});
|
|
} else {
|
|
var group = parts[0];
|
|
for (i = 1; i < parts.length; ++i) {
|
|
group.children = group.children.concat(parts[i].children);
|
|
}
|
|
callback(group);
|
|
}
|
|
}(0));
|
|
},
|
|
defineName: function (name, value, hidden) {
|
|
this._names[name] = {
|
|
value: value,
|
|
hidden: hidden
|
|
};
|
|
},
|
|
undefineName: function (name) {
|
|
delete this._names[name];
|
|
},
|
|
nameValue: function (name) {
|
|
if (name in this._names) {
|
|
return this._names[name].value;
|
|
}
|
|
return null;
|
|
},
|
|
adjustNames: function (affectedSheet, forRow, start, delta) {
|
|
affectedSheet = affectedSheet.toLowerCase();
|
|
Object.keys(this._names).forEach(function (name) {
|
|
var ref = this.nameValue(name);
|
|
if (ref instanceof kendo.spreadsheet.Ref && ref.sheet.toLowerCase() == affectedSheet) {
|
|
ref = ref.adjust(null, null, null, null, forRow, start, delta);
|
|
this.defineName(name, ref);
|
|
}
|
|
}, this);
|
|
},
|
|
options: {}
|
|
});
|
|
kendo.spreadsheet.Workbook = Workbook;
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Workbook.prototype);
|
|
Workbook.prototype.saveAsPDF = function (options) {
|
|
var progress = new $.Deferred();
|
|
var promise = progress.promise();
|
|
var args = { promise: promise };
|
|
if (this.trigger('pdfExport', args)) {
|
|
return;
|
|
}
|
|
this._drawPDF(options, progress).then(function (root) {
|
|
return kendo.drawing.exportPDF(root);
|
|
}).done(function (dataURI) {
|
|
kendo.saveAs({
|
|
dataURI: dataURI,
|
|
fileName: options.fileName,
|
|
proxyURL: options.proxyURL,
|
|
forceProxy: options.forceProxy,
|
|
proxyTarget: options.proxyTarget
|
|
});
|
|
progress.resolve();
|
|
}).fail(function (err) {
|
|
progress.reject(err);
|
|
});
|
|
return promise;
|
|
};
|
|
Workbook.prototype._drawPDF = function (options) {
|
|
var result = new $.Deferred();
|
|
var callback = function (group) {
|
|
result.resolve(group);
|
|
};
|
|
switch (options.area) {
|
|
case 'workbook':
|
|
options.workbook.draw(options, callback);
|
|
break;
|
|
case 'sheet':
|
|
options.workbook.activeSheet().draw(options, callback);
|
|
break;
|
|
case 'selection':
|
|
options.workbook.activeSheet().selection().draw(options, callback);
|
|
break;
|
|
}
|
|
return result.promise();
|
|
};
|
|
}
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/formulacontext', ['kendo.core'], f);
|
|
}(function () {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var CellRef = spreadsheet.CellRef;
|
|
var RangeRef = spreadsheet.RangeRef;
|
|
var UnionRef = spreadsheet.UnionRef;
|
|
var NameRef = spreadsheet.NameRef;
|
|
var Ref = spreadsheet.Ref;
|
|
var FormulaContext = kendo.Class.extend({
|
|
init: function (workbook) {
|
|
this.workbook = workbook;
|
|
},
|
|
getRefCells: function (ref, hiddenInfo) {
|
|
var sheet, formula, value, i;
|
|
if (ref instanceof CellRef) {
|
|
sheet = this.workbook.sheetByName(ref.sheet);
|
|
if (!sheet || !ref.valid()) {
|
|
return [{ value: new kendo.spreadsheet.calc.runtime.CalcError('REF') }];
|
|
}
|
|
formula = sheet.formula(ref);
|
|
value = sheet.range(ref.row, ref.col).value();
|
|
if (formula != null || value != null) {
|
|
return [{
|
|
formula: formula,
|
|
value: value,
|
|
row: ref.row,
|
|
col: ref.col,
|
|
sheet: ref.sheet,
|
|
hidden: hiddenInfo ? sheet.columnWidth(ref.col) === 0 || sheet.rowHeight(ref.row) === 0 : false
|
|
}];
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
if (ref instanceof RangeRef) {
|
|
i = this.workbook.sheetIndex(ref.sheet);
|
|
var states = [], n = i;
|
|
if (ref.endSheet) {
|
|
n = this.workbook.sheetIndex(ref.endSheet);
|
|
if (i > n) {
|
|
var tmp = i;
|
|
i = n;
|
|
n = tmp;
|
|
}
|
|
}
|
|
if (i < 0 || n < 0 || !ref.valid()) {
|
|
return [{ value: new kendo.spreadsheet.calc.runtime.CalcError('REF') }];
|
|
}
|
|
while (i <= n) {
|
|
sheet = this.workbook.sheetByIndex(i++);
|
|
var tl = sheet._grid.normalize(ref.topLeft);
|
|
var br = sheet._grid.normalize(ref.bottomRight);
|
|
var startCellIndex = sheet._grid.cellRefIndex(tl);
|
|
var endCellIndex = sheet._grid.cellRefIndex(br);
|
|
var values = sheet._properties.iterator('value', startCellIndex, endCellIndex);
|
|
for (var col = tl.col; col <= br.col; ++col) {
|
|
for (var row = tl.row; row <= br.row; ++row) {
|
|
var index = sheet._grid.index(row, col);
|
|
formula = sheet._properties.get('formula', index);
|
|
value = values.at(index);
|
|
if (formula != null || value != null) {
|
|
states.push({
|
|
formula: formula,
|
|
value: value,
|
|
row: row,
|
|
col: col,
|
|
sheet: sheet.name(),
|
|
hidden: hiddenInfo ? sheet.columnWidth(col) === 0 || sheet.rowHeight(row) === 0 : false
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return states;
|
|
}
|
|
if (ref instanceof UnionRef) {
|
|
var a = [];
|
|
for (i = 0; i < ref.refs.length; ++i) {
|
|
a = a.concat(this.getRefCells(ref.refs[i], hiddenInfo));
|
|
}
|
|
return a;
|
|
}
|
|
if (ref instanceof NameRef) {
|
|
var val = this.workbook.nameValue(ref.name);
|
|
if (val instanceof Ref) {
|
|
return this.getRefCells(val, hiddenInfo);
|
|
}
|
|
return [{ value: new kendo.spreadsheet.calc.runtime.CalcError('NAME') }];
|
|
}
|
|
return [];
|
|
},
|
|
getData: function (ref) {
|
|
var single = ref instanceof CellRef;
|
|
if (ref instanceof NameRef) {
|
|
single = this.workbook.nameValue(ref) instanceof CellRef;
|
|
}
|
|
var data = this.getRefCells(ref).map(function (cell) {
|
|
return cell.value;
|
|
});
|
|
return single ? data[0] : data;
|
|
},
|
|
onFormula: function (f) {
|
|
var sheet = this.workbook.sheetByName(f.sheet);
|
|
var row = f.row, col = f.col, value = f.value;
|
|
var currentFormula = sheet.formula({
|
|
row: row,
|
|
col: col
|
|
});
|
|
if (currentFormula !== f) {
|
|
return false;
|
|
}
|
|
if (value instanceof kendo.spreadsheet.calc.runtime.Matrix) {
|
|
value.each(function (value, r, c) {
|
|
sheet._value(row + r, col + c, value);
|
|
});
|
|
} else {
|
|
sheet._value(row, col, value);
|
|
}
|
|
clearTimeout(sheet._formulaContextRefresh);
|
|
sheet._formulaContextRefresh = setTimeout(function () {
|
|
sheet.batch(function () {
|
|
}, { layout: true });
|
|
}, 50);
|
|
return true;
|
|
}
|
|
});
|
|
var ValidationFormulaContext = FormulaContext.extend({
|
|
onFormula: function () {
|
|
return true;
|
|
}
|
|
});
|
|
spreadsheet.FormulaContext = FormulaContext;
|
|
spreadsheet.ValidationFormulaContext = ValidationFormulaContext;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/controller', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var alphaNumRegExp = /:alphanum$/;
|
|
var ACTIONS = {
|
|
'up': 'up',
|
|
'down': 'down',
|
|
'left': 'left',
|
|
'right': 'right',
|
|
'home': 'first-col',
|
|
'ctrl+left': 'first-col',
|
|
'end': 'last-col',
|
|
'ctrl+right': 'last-col',
|
|
'ctrl+up': 'first-row',
|
|
'ctrl+down': 'last-row',
|
|
'ctrl+home': 'first',
|
|
'ctrl+end': 'last',
|
|
'pageup': 'prev-page',
|
|
'pagedown': 'next-page'
|
|
};
|
|
var ENTRY_ACTIONS = {
|
|
'tab': 'next',
|
|
'shift+tab': 'previous',
|
|
'enter': 'lower',
|
|
'shift+enter': 'upper',
|
|
'delete': 'clearContents',
|
|
'backspace': 'clearContents',
|
|
'shift+:alphanum': 'edit',
|
|
':alphanum': 'edit',
|
|
'ctrl+:alphanum': 'ctrl',
|
|
':edit': 'edit'
|
|
};
|
|
var CONTAINER_EVENTS = {
|
|
'wheel': 'onWheel',
|
|
'*+mousedown': 'onMouseDown',
|
|
'contextmenu': 'onContextMenu',
|
|
'*+mousedrag': 'onMouseDrag',
|
|
'*+mouseup': 'onMouseUp',
|
|
'*+dblclick': 'onDblClick',
|
|
'mousemove': 'onMouseMove'
|
|
};
|
|
var CLIPBOARD_EVENTS = {
|
|
'*+pageup': 'onPageUp',
|
|
'*+pagedown': 'onPageDown',
|
|
'mouseup': 'onMouseUp',
|
|
'*+cut': 'onCut',
|
|
'*+paste': 'onPaste',
|
|
'*+copy': 'onCopy'
|
|
};
|
|
var EDITOR_EVENTS = {
|
|
'esc': 'onEditorEsc',
|
|
'enter': 'onEditorBlur',
|
|
'shift+enter': 'onEditorBlur',
|
|
'tab': 'onEditorBlur',
|
|
'shift+tab': 'onEditorBlur'
|
|
};
|
|
var FORMULABAR_EVENTS = $.extend({ focus: 'onEditorBarFocus' }, EDITOR_EVENTS);
|
|
var FORMULAINPUT_EVENTS = $.extend({ focus: 'onEditorCellFocus' }, EDITOR_EVENTS);
|
|
var SELECTION_MODES = {
|
|
cell: 'range',
|
|
rowheader: 'row',
|
|
columnheader: 'column',
|
|
topcorner: 'sheet',
|
|
autofill: 'autofill'
|
|
};
|
|
function toActionSelector(selectors) {
|
|
return selectors.map(function (action) {
|
|
return '[data-action="' + action + '"]';
|
|
}).join(',');
|
|
}
|
|
var COMPOSITE_UNAVAILABLE_ACTION_SELECTORS = toActionSelector([
|
|
'cut',
|
|
'copy',
|
|
'paste',
|
|
'insert-left',
|
|
'insert-right',
|
|
'insert-above',
|
|
'insert-below'
|
|
]);
|
|
var UNHIDE_ACTION_SELECTORS = toActionSelector([
|
|
'unhide-row',
|
|
'unhide-column'
|
|
]);
|
|
var ACTION_KEYS = [];
|
|
var SHIFT_ACTION_KEYS = [];
|
|
var ENTRY_ACTION_KEYS = [];
|
|
for (var key in ACTIONS) {
|
|
ACTION_KEYS.push(key);
|
|
SHIFT_ACTION_KEYS.push('shift+' + key);
|
|
}
|
|
for (key in ENTRY_ACTIONS) {
|
|
ENTRY_ACTION_KEYS.push(key);
|
|
}
|
|
CLIPBOARD_EVENTS[ACTION_KEYS] = 'onAction';
|
|
CLIPBOARD_EVENTS[SHIFT_ACTION_KEYS] = 'onShiftAction';
|
|
CLIPBOARD_EVENTS[ENTRY_ACTION_KEYS] = 'onEntryAction';
|
|
FORMULAINPUT_EVENTS[ACTION_KEYS] = 'onEditorAction';
|
|
FORMULAINPUT_EVENTS[SHIFT_ACTION_KEYS] = 'onEditorShiftAction';
|
|
var Controller = kendo.Class.extend({
|
|
init: function (view, workbook) {
|
|
this.view = view;
|
|
this.workbook(workbook);
|
|
this.container = $(view.container);
|
|
this.clipboardElement = $(view.clipboard);
|
|
this.cellContextMenu = view.cellContextMenu;
|
|
this.rowHeaderContextMenu = view.rowHeaderContextMenu;
|
|
this.colHeaderContextMenu = view.colHeaderContextMenu;
|
|
this.scroller = view.scroller;
|
|
this.tabstrip = view.tabstrip;
|
|
this.sheetsbar = view.sheetsbar;
|
|
this.editor = view.editor;
|
|
this.editor.bind('change', this.onEditorChange.bind(this));
|
|
this.editor.bind('activate', this.onEditorActivate.bind(this));
|
|
this.editor.bind('deactivate', this.onEditorDeactivate.bind(this));
|
|
this.editor.bind('update', this.onEditorUpdate.bind(this));
|
|
$(view.scroller).on('scroll', this.onScroll.bind(this));
|
|
this.listener = new kendo.spreadsheet.EventListener(this.container, this, CONTAINER_EVENTS);
|
|
this.keyListener = new kendo.spreadsheet.EventListener(this.clipboardElement, this, CLIPBOARD_EVENTS);
|
|
this.barKeyListener = new kendo.spreadsheet.EventListener(this.editor.barElement(), this, FORMULABAR_EVENTS);
|
|
this.inputKeyListener = new kendo.spreadsheet.EventListener(this.editor.cellElement(), this, FORMULAINPUT_EVENTS);
|
|
if (this.sheetsbar) {
|
|
this.sheetsbar.bind('select', this.onSheetBarSelect.bind(this));
|
|
this.sheetsbar.bind('reorder', this.onSheetBarReorder.bind(this));
|
|
this.sheetsbar.bind('rename', this.onSheetBarRename.bind(this));
|
|
this.sheetsbar.bind('remove', this.onSheetBarRemove.bind(this));
|
|
}
|
|
this.cellContextMenu.bind('select', this.onContextMenuSelect.bind(this));
|
|
this.rowHeaderContextMenu.bind('select', this.onContextMenuSelect.bind(this));
|
|
this.colHeaderContextMenu.bind('select', this.onContextMenuSelect.bind(this));
|
|
this.cellContextMenu.element.add(this.rowHeaderContextMenu.element).add(this.colHeaderContextMenu.element).on('contextmenu', false);
|
|
if (this.tabstrip) {
|
|
this.tabstrip.bind('action', this.onCommandRequest.bind(this));
|
|
this.tabstrip.bind('dialog', this.onDialogRequest.bind(this));
|
|
}
|
|
},
|
|
_execute: function (options) {
|
|
var result = this._workbook.execute(options);
|
|
if (options.command === 'EditCommand' && !result) {
|
|
this._workbook.trigger('change', { editorClose: true });
|
|
}
|
|
if (result) {
|
|
if (result.reason === 'error') {
|
|
this.view.showError(result);
|
|
} else {
|
|
this.view.openDialog(result.reason);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_activeTooltip: function () {
|
|
return this._workbook.activeSheet().activeCell().simplify().toString();
|
|
},
|
|
onContextMenuSelect: function (e) {
|
|
var action = $(e.item).data('action');
|
|
var command;
|
|
switch (action) {
|
|
case 'cut':
|
|
command = {
|
|
command: 'ToolbarCutCommand',
|
|
options: { workbook: this._workbook }
|
|
};
|
|
break;
|
|
case 'copy':
|
|
command = {
|
|
command: 'ToolbarCopyCommand',
|
|
options: { workbook: this._workbook }
|
|
};
|
|
break;
|
|
case 'paste':
|
|
command = {
|
|
command: 'ToolbarPasteCommand',
|
|
options: { workbook: this._workbook }
|
|
};
|
|
break;
|
|
case 'unmerge':
|
|
command = {
|
|
command: 'MergeCellCommand',
|
|
options: { value: 'unmerge' }
|
|
};
|
|
break;
|
|
case 'merge':
|
|
this.view.openDialog('merge');
|
|
break;
|
|
case 'hide-row':
|
|
command = {
|
|
command: 'HideLineCommand',
|
|
options: { axis: 'row' }
|
|
};
|
|
break;
|
|
case 'hide-column':
|
|
command = {
|
|
command: 'HideLineCommand',
|
|
options: { axis: 'column' }
|
|
};
|
|
break;
|
|
case 'unhide-row':
|
|
command = {
|
|
command: 'UnHideLineCommand',
|
|
options: { axis: 'row' }
|
|
};
|
|
break;
|
|
case 'unhide-column':
|
|
command = {
|
|
command: 'UnHideLineCommand',
|
|
options: { axis: 'column' }
|
|
};
|
|
break;
|
|
case 'delete-row':
|
|
command = { command: 'DeleteRowCommand' };
|
|
break;
|
|
case 'delete-column':
|
|
command = { command: 'DeleteColumnCommand' };
|
|
break;
|
|
}
|
|
if (command) {
|
|
this._execute(command);
|
|
}
|
|
},
|
|
onSheetBarRemove: function (e) {
|
|
var sheet = this._workbook.sheetByName(e.name);
|
|
if (!sheet) {
|
|
return;
|
|
}
|
|
this._workbook.removeSheet(sheet);
|
|
},
|
|
destroy: function () {
|
|
this.listener.destroy();
|
|
this.keyListener.destroy();
|
|
this.inputKeyListener.destroy();
|
|
},
|
|
onSheetBarSelect: function (e) {
|
|
var sheet;
|
|
var workbook = this._workbook;
|
|
if (e.isAddButton) {
|
|
sheet = workbook.insertSheet();
|
|
} else {
|
|
sheet = workbook.sheetByName(e.name);
|
|
}
|
|
if (workbook.activeSheet().name() !== sheet.name()) {
|
|
workbook.activeSheet(sheet);
|
|
}
|
|
},
|
|
onSheetBarReorder: function (e) {
|
|
var sheet = this._workbook.sheetByIndex(e.oldIndex);
|
|
this._workbook.moveSheetToIndex(sheet, e.newIndex);
|
|
this._workbook.activeSheet(sheet);
|
|
},
|
|
onSheetBarRename: function (e) {
|
|
var sheet = this._workbook.sheetByIndex(e.sheetIndex);
|
|
this._workbook.renameSheet(sheet, e.name);
|
|
this.clipboardElement.focus();
|
|
},
|
|
sheet: function (sheet) {
|
|
this.navigator = sheet.navigator();
|
|
this.axisManager = sheet.axisManager();
|
|
},
|
|
workbook: function (workbook) {
|
|
this._workbook = workbook;
|
|
this.clipboard = workbook.clipboard();
|
|
},
|
|
refresh: function () {
|
|
var editor = this.editor;
|
|
var workbook = this._workbook;
|
|
var sheet = workbook.activeSheet();
|
|
this._viewPortHeight = this.view.scroller.clientHeight;
|
|
this.navigator.height(this._viewPortHeight);
|
|
if (!editor.isActive()) {
|
|
editor.enable(sheet.selection().enable() !== false);
|
|
editor.value(workbook._inputForRef(sheet.activeCell()));
|
|
}
|
|
},
|
|
onScroll: function () {
|
|
this.view.render();
|
|
},
|
|
onWheel: function (event) {
|
|
var deltaX = event.originalEvent.deltaX;
|
|
var deltaY = event.originalEvent.deltaY;
|
|
if (event.originalEvent.deltaMode === 1) {
|
|
deltaX *= 10;
|
|
deltaY *= 10;
|
|
}
|
|
this.scrollWith(deltaX, deltaY);
|
|
event.preventDefault();
|
|
},
|
|
onAction: function (event, action) {
|
|
this.navigator.moveActiveCell(ACTIONS[action]);
|
|
event.preventDefault();
|
|
},
|
|
onPageUp: function () {
|
|
this.scrollDown(-this._viewPortHeight);
|
|
},
|
|
onPageDown: function () {
|
|
this.scrollDown(this._viewPortHeight);
|
|
},
|
|
onEntryAction: function (event, action) {
|
|
if (event.mod) {
|
|
var shouldPrevent = true;
|
|
var key = String.fromCharCode(event.keyCode);
|
|
switch (key) {
|
|
case 'A':
|
|
this.navigator.selectAll();
|
|
break;
|
|
case 'Y':
|
|
this._workbook.undoRedoStack.redo();
|
|
break;
|
|
case 'Z':
|
|
this._workbook.undoRedoStack.undo();
|
|
break;
|
|
default:
|
|
shouldPrevent = false;
|
|
break;
|
|
}
|
|
if (shouldPrevent) {
|
|
event.preventDefault();
|
|
}
|
|
} else {
|
|
var disabled = this._workbook.activeSheet().selection().enable() === false;
|
|
if (action == 'delete' || action == 'backspace') {
|
|
if (disabled) {
|
|
return;
|
|
}
|
|
this._execute({ command: 'ClearContentCommand' });
|
|
event.preventDefault();
|
|
} else if (alphaNumRegExp.test(action) || action === ':edit') {
|
|
if (disabled) {
|
|
return;
|
|
}
|
|
if (action !== ':edit') {
|
|
this.editor.value('');
|
|
}
|
|
this.editor.activate({
|
|
range: this._workbook.activeSheet()._viewActiveCell(),
|
|
rect: this.view.activeCellRectangle(),
|
|
tooltip: this._activeTooltip()
|
|
}).focus();
|
|
} else {
|
|
this.navigator.navigateInSelection(ENTRY_ACTIONS[action]);
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
onShiftAction: function (event, action) {
|
|
this.navigator.modifySelection(ACTIONS[action.replace('shift+', '')], this.appendSelection);
|
|
event.preventDefault();
|
|
},
|
|
onMouseMove: function (event) {
|
|
var sheet = this._workbook.activeSheet();
|
|
if (sheet.resizingInProgress() || sheet.selectionInProgress()) {
|
|
return;
|
|
}
|
|
var object = this.objectAt(event);
|
|
if (object.type === 'columnresizehandle' || object.type === 'rowresizehandle') {
|
|
sheet.positionResizeHandle(object.ref);
|
|
} else {
|
|
sheet.removeResizeHandle();
|
|
}
|
|
},
|
|
onMouseDown: function (event) {
|
|
var object = this.objectAt(event);
|
|
if (object.pane) {
|
|
this.originFrame = object.pane;
|
|
}
|
|
if (this.editor.canInsertRef(false) && object.ref) {
|
|
this._workbook.activeSheet()._setFormulaSelections(this.editor.highlightedRefs());
|
|
this.navigator.startSelection(object.ref, this._selectionMode, this.appendSelection);
|
|
event.preventDefault();
|
|
return;
|
|
} else {
|
|
this.editor.deactivate();
|
|
if (this.editor.isActive()) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
var sheet = this._workbook.activeSheet();
|
|
if (object.type === 'columnresizehandle' || object.type === 'rowresizehandle') {
|
|
sheet.startResizing({
|
|
x: object.x,
|
|
y: object.y
|
|
});
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
if (object.type === 'filtericon') {
|
|
this.openFilterMenu(event);
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
this._selectionMode = SELECTION_MODES[object.type];
|
|
this.appendSelection = event.mod;
|
|
this.navigator.startSelection(object.ref, this._selectionMode, this.appendSelection);
|
|
},
|
|
onContextMenu: function (event) {
|
|
var sheet = this._workbook.activeSheet();
|
|
if (sheet.resizingInProgress()) {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
this.cellContextMenu.close();
|
|
this.colHeaderContextMenu.close();
|
|
this.rowHeaderContextMenu.close();
|
|
var menu;
|
|
var location = {
|
|
pageX: event.pageX,
|
|
pageY: event.pageY
|
|
};
|
|
var object = this.objectAt(location);
|
|
if (object.type === 'columnresizehandle' || object.type === 'rowresizehandle') {
|
|
return;
|
|
}
|
|
this.navigator.selectForContextMenu(object.ref, SELECTION_MODES[object.type]);
|
|
var isComposite = this.navigator._sheet.select() instanceof kendo.spreadsheet.UnionRef;
|
|
var showUnhide = false;
|
|
var showUnmerge = false;
|
|
if (object.type == 'columnheader') {
|
|
menu = this.colHeaderContextMenu;
|
|
showUnhide = !isComposite && this.axisManager.selectionIncludesHiddenColumns();
|
|
} else if (object.type == 'rowheader') {
|
|
menu = this.rowHeaderContextMenu;
|
|
showUnhide = !isComposite && this.axisManager.selectionIncludesHiddenRows();
|
|
} else {
|
|
menu = this.cellContextMenu;
|
|
showUnmerge = this.navigator.selectionIncludesMergedCells();
|
|
}
|
|
menu.element.find(COMPOSITE_UNAVAILABLE_ACTION_SELECTORS).toggle(!isComposite);
|
|
menu.element.find(UNHIDE_ACTION_SELECTORS).toggle(showUnhide);
|
|
menu.element.find('[data-action=unmerge]').toggle(showUnmerge);
|
|
setTimeout(function () {
|
|
menu.open(event.pageX, event.pageY);
|
|
});
|
|
},
|
|
prevent: function (event) {
|
|
event.preventDefault();
|
|
},
|
|
constrainResize: function (type, ref) {
|
|
var sheet = this._workbook.activeSheet();
|
|
var resizeHandle = sheet.resizeHandlePosition();
|
|
return !resizeHandle || type === 'outside' || type === 'topcorner' || ref.col < resizeHandle.col || ref.row < resizeHandle.row;
|
|
},
|
|
onMouseDrag: function (event) {
|
|
if (this._selectionMode === 'sheet') {
|
|
return;
|
|
}
|
|
var location = {
|
|
pageX: event.pageX,
|
|
pageY: event.pageY
|
|
};
|
|
var object = this.objectAt(location);
|
|
var sheet = this._workbook.activeSheet();
|
|
if (sheet.resizingInProgress()) {
|
|
if (!this.constrainResize(object.type, object.ref)) {
|
|
sheet.resizeHintPosition({
|
|
x: object.x,
|
|
y: object.y
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (object.type === 'outside') {
|
|
this.startAutoScroll(object);
|
|
return;
|
|
}
|
|
if (this.originFrame === object.pane) {
|
|
this.selectToLocation(location);
|
|
} else {
|
|
var frame = this.originFrame._grid;
|
|
if (object.x > frame.right) {
|
|
this.scrollLeft();
|
|
}
|
|
if (object.y > frame.bottom) {
|
|
this.scrollTop();
|
|
}
|
|
if (object.y < frame.top || object.x < frame.left) {
|
|
this.startAutoScroll(object, location);
|
|
} else {
|
|
this.selectToLocation(location);
|
|
}
|
|
}
|
|
event.preventDefault();
|
|
},
|
|
onMouseUp: function (event) {
|
|
var sheet = this._workbook.activeSheet();
|
|
sheet.completeResizing();
|
|
this.navigator.completeSelection();
|
|
this.stopAutoScroll();
|
|
var editor = this.editor.activeEditor();
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
var el = event.target;
|
|
while (el) {
|
|
if (el === editor.element[0]) {
|
|
return;
|
|
}
|
|
el = el.parentNode;
|
|
}
|
|
var object = this.objectAt(event);
|
|
if (object && object.ref && editor.canInsertRef(false)) {
|
|
editor.refAtPoint(sheet.selection()._ref);
|
|
sheet._setFormulaSelections(editor.highlightedRefs());
|
|
}
|
|
},
|
|
onDblClick: function (event) {
|
|
var object = this.objectAt(event);
|
|
var disabled = this._workbook.activeSheet().selection().enable() === false;
|
|
if (object.type !== 'cell' || disabled) {
|
|
return;
|
|
}
|
|
this.editor.activate({
|
|
range: this._workbook.activeSheet()._viewActiveCell(),
|
|
rect: this.view.activeCellRectangle(),
|
|
tooltip: this._activeTooltip()
|
|
}).focus();
|
|
this.onEditorUpdate();
|
|
},
|
|
onCut: function (e) {
|
|
if (e) {
|
|
var table = this.clipboardElement.find('table.kendo-clipboard-' + this.clipboard._uid).detach();
|
|
this.clipboardElement.append(table.clone(false));
|
|
setTimeout(function () {
|
|
this.clipboardElement.empty().append(table);
|
|
}.bind(this));
|
|
}
|
|
this._execute({
|
|
command: 'CutCommand',
|
|
options: { workbook: this.view._workbook }
|
|
});
|
|
},
|
|
clipBoardValue: function () {
|
|
return this.clipboardElement.html();
|
|
},
|
|
onPaste: function (e) {
|
|
var html = '';
|
|
var plain = '';
|
|
this.clipboard.menuInvoked = e === undefined;
|
|
if (e) {
|
|
if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
|
|
e.preventDefault();
|
|
var hasHTML = false;
|
|
var hasPlainText = false;
|
|
if (window.DOMStringList && e.originalEvent.clipboardData.types instanceof window.DOMStringList) {
|
|
hasHTML = e.originalEvent.clipboardData.types.contains('text/html');
|
|
hasPlainText = e.originalEvent.clipboardData.types.contains('text/plain');
|
|
} else {
|
|
hasHTML = /text\/html/.test(e.originalEvent.clipboardData.types);
|
|
hasPlainText = /text\/plain/.test(e.originalEvent.clipboardData.types);
|
|
}
|
|
if (hasHTML) {
|
|
html = e.originalEvent.clipboardData.getData('text/html');
|
|
}
|
|
if (hasPlainText) {
|
|
plain = e.originalEvent.clipboardData.getData('text/plain').trim();
|
|
}
|
|
} else {
|
|
var table = this.clipboardElement.find('table.kendo-clipboard-' + this.clipboard._uid).detach();
|
|
this.clipboardElement.empty();
|
|
setTimeout(function () {
|
|
var html = this.clipboardElement.html();
|
|
var plain = window.clipboardData.getData('Text').trim();
|
|
if (!html && !plain) {
|
|
return;
|
|
}
|
|
this.clipboard.external({
|
|
html: html,
|
|
plain: plain
|
|
});
|
|
this.clipboardElement.empty().append(table);
|
|
this._execute({
|
|
command: 'PasteCommand',
|
|
options: { workbook: this.view._workbook }
|
|
});
|
|
this.clipboard.menuInvoked = true;
|
|
}.bind(this));
|
|
return;
|
|
}
|
|
} else {
|
|
if (kendo.support.browser.msie) {
|
|
this.clipboardElement.focus().select();
|
|
document.execCommand('paste');
|
|
return;
|
|
} else {
|
|
this.clipboard.menuInvoked = true;
|
|
}
|
|
}
|
|
if (!html && !plain) {
|
|
return;
|
|
}
|
|
this.clipboard.external({
|
|
html: html,
|
|
plain: plain
|
|
});
|
|
this._execute({
|
|
command: 'PasteCommand',
|
|
options: { workbook: this.view._workbook }
|
|
});
|
|
},
|
|
onCopy: function (e) {
|
|
this.clipboard.menuInvoked = e === undefined;
|
|
this._execute({
|
|
command: 'CopyCommand',
|
|
options: { workbook: this.view._workbook }
|
|
});
|
|
},
|
|
scrollTop: function () {
|
|
this.scroller.scrollTop = 0;
|
|
},
|
|
scrollLeft: function () {
|
|
this.scroller.scrollLeft = 0;
|
|
},
|
|
scrollDown: function (value) {
|
|
this.scroller.scrollTop += value;
|
|
},
|
|
scrollRight: function (value) {
|
|
this.scroller.scrollLeft += value;
|
|
},
|
|
scrollWith: function (right, down) {
|
|
this.scroller.scrollTop += down;
|
|
this.scroller.scrollLeft += right;
|
|
},
|
|
objectAt: function (location) {
|
|
var offset = this.container.offset();
|
|
var coordinates = {
|
|
left: location.pageX - offset.left,
|
|
top: location.pageY - offset.top
|
|
};
|
|
return this.view.objectAt(coordinates.left, coordinates.top);
|
|
},
|
|
selectToLocation: function (cellLocation) {
|
|
var object = this.objectAt(cellLocation);
|
|
if (object.pane) {
|
|
this.extendSelection(object);
|
|
this.lastKnownCellLocation = cellLocation;
|
|
this.originFrame = object.pane;
|
|
}
|
|
this.stopAutoScroll();
|
|
},
|
|
extendSelection: function (object) {
|
|
this.navigator.extendSelection(object.ref, this._selectionMode, this.appendSelection);
|
|
},
|
|
autoScroll: function () {
|
|
var x = this._autoScrollTarget.x;
|
|
var y = this._autoScrollTarget.y;
|
|
var boundaries = this.originFrame._grid;
|
|
var scroller = this.view.scroller;
|
|
var scrollStep = 8;
|
|
var scrollLeft = scroller.scrollLeft;
|
|
var scrollTop = scroller.scrollTop;
|
|
if (x < boundaries.left) {
|
|
this.scrollRight(-scrollStep);
|
|
}
|
|
if (x > boundaries.right) {
|
|
this.scrollRight(scrollStep);
|
|
}
|
|
if (y < boundaries.top) {
|
|
this.scrollDown(-scrollStep);
|
|
}
|
|
if (y > boundaries.bottom) {
|
|
this.scrollDown(scrollStep);
|
|
}
|
|
if (scrollTop === scroller.scrollTop && scrollLeft === scroller.scrollLeft) {
|
|
this.selectToLocation(this.finalLocation);
|
|
} else {
|
|
this.extendSelection(this.objectAt(this.lastKnownCellLocation));
|
|
}
|
|
},
|
|
startAutoScroll: function (viewObject, location) {
|
|
if (!this._scrollInterval) {
|
|
this._scrollInterval = setInterval(this.autoScroll.bind(this), 50);
|
|
}
|
|
this.finalLocation = location || this.lastKnownCellLocation;
|
|
this._autoScrollTarget = viewObject;
|
|
},
|
|
stopAutoScroll: function () {
|
|
clearInterval(this._scrollInterval);
|
|
this._scrollInterval = null;
|
|
},
|
|
openFilterMenu: function (event) {
|
|
var object = this.objectAt(event);
|
|
var sheet = this._workbook.activeSheet();
|
|
var column = sheet.filterColumn(object.ref);
|
|
var filterMenu = this.view.createFilterMenu(column);
|
|
filterMenu.bind('action', this.onCommandRequest.bind(this));
|
|
filterMenu.bind('action', filterMenu.close.bind(filterMenu));
|
|
filterMenu.openFor(event.target);
|
|
},
|
|
onEditorChange: function (e) {
|
|
this._workbook.activeSheet().isInEditMode(false);
|
|
var result = this._execute({
|
|
command: 'EditCommand',
|
|
options: {
|
|
editActiveCell: true,
|
|
value: e.value
|
|
}
|
|
});
|
|
if (result && result.reason === 'error') {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
onEditorActivate: function () {
|
|
var workbook = this._workbook;
|
|
var sheet = workbook.activeSheet();
|
|
sheet._setFormulaSelections(this.editor.highlightedRefs());
|
|
sheet.isInEditMode(true);
|
|
},
|
|
onEditorDeactivate: function () {
|
|
var sheet = this._workbook.activeSheet();
|
|
sheet.isInEditMode(false);
|
|
sheet._setFormulaSelections([]);
|
|
},
|
|
onEditorUpdate: function () {
|
|
this._workbook.activeSheet()._setFormulaSelections(this.editor.highlightedRefs());
|
|
},
|
|
onEditorBarFocus: function () {
|
|
var disabled = this._workbook.activeSheet().selection().enable() === false;
|
|
if (disabled) {
|
|
return;
|
|
}
|
|
this.editor.activate({
|
|
range: this._workbook.activeSheet()._viewActiveCell(),
|
|
rect: this.view.activeCellRectangle(),
|
|
tooltip: this._activeTooltip()
|
|
});
|
|
},
|
|
onEditorCellFocus: function () {
|
|
this.editor.scale();
|
|
},
|
|
onEditorEsc: function () {
|
|
this.editor.value(this._workbook._inputForRef(this._workbook.activeSheet()._viewActiveCell()));
|
|
this.editor.deactivate();
|
|
this.clipboardElement.focus();
|
|
},
|
|
onEditorBlur: function (_, action) {
|
|
if (this.editor.isFiltered()) {
|
|
return;
|
|
}
|
|
this.editor.deactivate();
|
|
if (!this.editor.isActive()) {
|
|
this.clipboardElement.focus();
|
|
this.navigator.navigateInSelection(ENTRY_ACTIONS[action]);
|
|
}
|
|
},
|
|
onEditorAction: function (event, action) {
|
|
var editor = this.editor;
|
|
var sheet = this._workbook.activeSheet();
|
|
if (editor.canInsertRef(true)) {
|
|
this.navigator.moveActiveCell(ACTIONS[action]);
|
|
editor.activeEditor().refAtPoint(sheet.selection()._ref);
|
|
sheet._setFormulaSelections(editor.highlightedRefs());
|
|
event.preventDefault();
|
|
}
|
|
},
|
|
onEditorShiftAction: function (event, action) {
|
|
var editor = this.editor;
|
|
var sheet = this._workbook.activeSheet();
|
|
if (editor.canInsertRef(true)) {
|
|
this.navigator.modifySelection(ACTIONS[action.replace('shift+', '')], this.appendSelection);
|
|
editor.activeEditor().refAtPoint(sheet.selection()._ref);
|
|
sheet._setFormulaSelections(editor.highlightedRefs());
|
|
event.preventDefault();
|
|
}
|
|
},
|
|
onCommandRequest: function (e) {
|
|
if (e.command) {
|
|
this._execute(e);
|
|
} else {
|
|
this._workbook.undoRedoStack[e.action]();
|
|
}
|
|
},
|
|
onDialogRequest: function (e) {
|
|
var exportOptions = {
|
|
pdfExport: this._workbook.options.pdf,
|
|
excelExport: this._workbook.options.excel
|
|
};
|
|
if (e.options) {
|
|
$.extend(true, e.options, exportOptions);
|
|
} else {
|
|
e.options = exportOptions;
|
|
}
|
|
this.view.openDialog(e.name, e.options);
|
|
}
|
|
});
|
|
kendo.spreadsheet.Controller = Controller;
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/view', [
|
|
'kendo.core',
|
|
'kendo.menu',
|
|
'spreadsheet/sheetsbar'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var DOT = '.';
|
|
var RESIZE_HANDLE_WIDTH = 7;
|
|
var viewClassNames = {
|
|
view: 'k-spreadsheet-view',
|
|
fixedContainer: 'k-spreadsheet-fixed-container',
|
|
scroller: 'k-spreadsheet-scroller',
|
|
viewSize: 'k-spreadsheet-view-size',
|
|
clipboard: 'k-spreadsheet-clipboard',
|
|
cellEditor: 'k-spreadsheet-cell-editor',
|
|
barEditor: 'k-spreadsheet-editor',
|
|
topCorner: 'k-spreadsheet-top-corner',
|
|
filterHeadersWrapper: 'k-filter-wrapper',
|
|
filterRange: 'k-filter-range',
|
|
filterButton: 'k-spreadsheet-filter',
|
|
filterButtonActive: 'k-state-active',
|
|
icon: 'k-icon k-font-icon',
|
|
iconFilterDefault: 'k-i-arrow-s',
|
|
sheetsBar: 'k-spreadsheet-sheets-bar',
|
|
sheetsBarActive: 'k-spreadsheet-sheets-bar-active',
|
|
sheetsBarInactive: 'k-spreadsheet-sheets-bar-inactive',
|
|
cellContextMenu: 'k-spreadsheet-cell-context-menu',
|
|
rowHeaderContextMenu: 'k-spreadsheet-row-header-context-menu',
|
|
colHeaderContextMenu: 'k-spreadsheet-col-header-context-menu'
|
|
};
|
|
var VIEW_MESAGES = kendo.spreadsheet.messages.view = {
|
|
errors: {
|
|
openUnsupported: 'Unsupported format. Please select an .xlsx file.',
|
|
shiftingNonblankCells: 'Cannot insert cells due to data loss possibility. Select another insert location or delete the data from the end of your worksheet.',
|
|
filterRangeContainingMerges: 'Cannot create a filter within a range containing merges',
|
|
sortRangeContainingMerges: 'Cannot sort a range containing merges',
|
|
validationError: 'The value that you entered violates the validation rules set on the cell.'
|
|
},
|
|
tabs: {
|
|
home: 'Home',
|
|
insert: 'Insert',
|
|
data: 'Data'
|
|
}
|
|
};
|
|
function selectElementContents(el) {
|
|
var sel = window.getSelection();
|
|
sel.removeAllRanges();
|
|
var range = document.createRange();
|
|
range.selectNodeContents(el);
|
|
sel.addRange(range);
|
|
}
|
|
function cellBefore(table, row) {
|
|
var cells = table.trs[row].children;
|
|
return cells[cells.length - 2];
|
|
}
|
|
function cellAbove(table, row) {
|
|
var prevRow = table.trs[row - 1];
|
|
var index = table.trs[row].children.length - 1;
|
|
if (prevRow && index >= 0) {
|
|
return prevRow.children[index];
|
|
}
|
|
}
|
|
function cellBorder(value) {
|
|
return [
|
|
'solid',
|
|
(value.size || 1) + 'px',
|
|
value.color || '#000'
|
|
].join(' ');
|
|
}
|
|
function drawCell(collection, cell, cls, hBorders, vBorders, showGrid) {
|
|
if (!cls && !kendo.spreadsheet.draw.shouldDrawCell(cell)) {
|
|
return;
|
|
}
|
|
var left = cell.left;
|
|
var top = cell.top;
|
|
var width = cell.width + 1;
|
|
var height = cell.height + 1;
|
|
var style = {};
|
|
var background = cell.background;
|
|
var defaultBorder = null;
|
|
if (background) {
|
|
defaultBorder = background;
|
|
if (showGrid) {
|
|
defaultBorder = kendo.parseColor(defaultBorder).toHSV();
|
|
defaultBorder.v *= 0.9;
|
|
defaultBorder = defaultBorder.toCssRgba();
|
|
}
|
|
defaultBorder = cellBorder({ color: defaultBorder });
|
|
}
|
|
if (background) {
|
|
style.backgroundColor = background;
|
|
}
|
|
if (cell.color) {
|
|
style.color = cell.color;
|
|
}
|
|
if (cell.fontFamily) {
|
|
style.fontFamily = cell.fontFamily;
|
|
}
|
|
if (cell.underline) {
|
|
style.textDecoration = 'underline';
|
|
}
|
|
if (cell.italic) {
|
|
style.fontStyle = 'italic';
|
|
}
|
|
if (cell.textAlign) {
|
|
style.textAlign = cell.textAlign;
|
|
}
|
|
if (cell.bold) {
|
|
style.fontWeight = 'bold';
|
|
}
|
|
if (cell.fontSize) {
|
|
style.fontSize = cell.fontSize + 'px';
|
|
}
|
|
if (cell.wrap === true) {
|
|
style.whiteSpace = 'pre-wrap';
|
|
style.wordBreak = 'break-all';
|
|
}
|
|
if (cell.borderLeft) {
|
|
style.borderLeft = cellBorder(cell.borderLeft);
|
|
if (vBorders) {
|
|
vBorders[cell.left] = true;
|
|
}
|
|
} else if (defaultBorder && vBorders && !vBorders[cell.left]) {
|
|
style.borderLeft = defaultBorder;
|
|
} else {
|
|
left++;
|
|
width--;
|
|
}
|
|
if (cell.borderTop) {
|
|
style.borderTop = cellBorder(cell.borderTop);
|
|
if (hBorders) {
|
|
hBorders[cell.top] = true;
|
|
}
|
|
} else if (defaultBorder && hBorders && !hBorders[cell.top]) {
|
|
style.borderTop = defaultBorder;
|
|
} else {
|
|
top++;
|
|
height--;
|
|
}
|
|
if (cell.borderRight) {
|
|
style.borderRight = cellBorder(cell.borderRight);
|
|
if (vBorders) {
|
|
vBorders[cell.right] = true;
|
|
}
|
|
} else if (defaultBorder && vBorders && !vBorders[cell.right]) {
|
|
style.borderRight = defaultBorder;
|
|
} else {
|
|
width--;
|
|
}
|
|
if (cell.borderBottom) {
|
|
style.borderBottom = cellBorder(cell.borderBottom);
|
|
if (hBorders) {
|
|
hBorders[cell.bottom] = true;
|
|
}
|
|
} else if (defaultBorder && hBorders && !hBorders[cell.bottom]) {
|
|
style.borderBottom = defaultBorder;
|
|
} else {
|
|
height--;
|
|
}
|
|
style.left = left + 'px';
|
|
style.top = top + 'px';
|
|
style.width = width + 'px';
|
|
style.height = height + 'px';
|
|
var data = cell.value, type = typeof data;
|
|
if (cell.format && data !== null) {
|
|
data = kendo.spreadsheet.formatting.format(data, cell.format);
|
|
if (data.__dataType) {
|
|
type = data.__dataType;
|
|
}
|
|
} else if (data !== null && data !== undefined) {
|
|
data = kendo.dom.text(data);
|
|
}
|
|
if (!style.textAlign) {
|
|
switch (type) {
|
|
case 'number':
|
|
case 'date':
|
|
case 'percent':
|
|
style.textAlign = 'right';
|
|
break;
|
|
case 'boolean':
|
|
style.textAlign = 'center';
|
|
break;
|
|
}
|
|
}
|
|
var classNames = [paneClassNames.cell];
|
|
if (cls) {
|
|
classNames.push(cls);
|
|
}
|
|
if (cell.enable === false) {
|
|
classNames.push('k-state-disabled');
|
|
}
|
|
if (cell.merged) {
|
|
classNames.push('k-spreadsheet-merged-cell');
|
|
}
|
|
var verticalAlign = cell.verticalAlign || 'bottom';
|
|
if (verticalAlign && data) {
|
|
data = kendo.dom.element('div', { className: 'k-vertical-align-' + verticalAlign }, [data]);
|
|
}
|
|
var children = data ? [data] : [];
|
|
var properties = { style: style };
|
|
var validation = cell.validation;
|
|
if (validation && !validation.value) {
|
|
children.push(kendo.dom.element('span', { className: 'k-dirty' }));
|
|
classNames.push('k-dirty-cell');
|
|
properties.title = validation._getOptions().messageTemplate;
|
|
}
|
|
properties.className = classNames.join(' ');
|
|
var div = kendo.dom.element('div', properties, children);
|
|
collection.push(div);
|
|
return div;
|
|
}
|
|
function addCell(table, row, cell) {
|
|
var style = {};
|
|
if (cell.background) {
|
|
style.backgroundColor = cell.background;
|
|
}
|
|
if (cell.color) {
|
|
style.color = cell.color;
|
|
}
|
|
if (cell.fontFamily) {
|
|
style.fontFamily = cell.fontFamily;
|
|
}
|
|
if (cell.underline) {
|
|
style.textDecoration = 'underline';
|
|
}
|
|
if (cell.italic) {
|
|
style.fontStyle = 'italic';
|
|
}
|
|
if (cell.textAlign) {
|
|
style.textAlign = cell.textAlign;
|
|
}
|
|
if (cell.verticalAlign) {
|
|
style.verticalAlign = cell.verticalAlign === 'center' ? 'middle' : cell.verticalAlign;
|
|
}
|
|
if (cell.bold) {
|
|
style.fontWeight = 'bold';
|
|
}
|
|
if (cell.fontSize) {
|
|
style.fontSize = cell.fontSize + 'px';
|
|
}
|
|
if (cell.wrap === true) {
|
|
style.whiteSpace = 'pre-wrap';
|
|
style.wordBreak = 'break-all';
|
|
}
|
|
if (cell.borderRight) {
|
|
style.borderRight = cellBorder(cell.borderRight);
|
|
} else if (cell.background) {
|
|
style.borderRightColor = cell.background;
|
|
}
|
|
if (cell.borderBottom) {
|
|
style.borderBottom = cellBorder(cell.borderBottom);
|
|
} else if (cell.background) {
|
|
style.borderBottomColor = cell.background;
|
|
}
|
|
var data = cell.value, type = typeof data;
|
|
if (cell.format && data !== null) {
|
|
data = kendo.spreadsheet.formatting.format(data, cell.format);
|
|
if (data.__dataType) {
|
|
type = data.__dataType;
|
|
}
|
|
}
|
|
if (!style.textAlign) {
|
|
switch (type) {
|
|
case 'number':
|
|
case 'date':
|
|
case 'percent':
|
|
style.textAlign = 'right';
|
|
break;
|
|
case 'boolean':
|
|
style.textAlign = 'center';
|
|
break;
|
|
}
|
|
}
|
|
var className = null;
|
|
if (cell.enable === false) {
|
|
className = 'k-state-disabled';
|
|
}
|
|
var td = table.addCell(row, data, style, className, cell.validation);
|
|
var border, sibling;
|
|
if (cell.borderLeft) {
|
|
sibling = cellBefore(table, row);
|
|
border = cellBorder(cell.borderLeft);
|
|
if (sibling && border) {
|
|
sibling.attr.style.borderRight = border;
|
|
}
|
|
} else if (cell.background) {
|
|
style.borderLeftColor = cell.background;
|
|
}
|
|
if (cell.borderTop) {
|
|
sibling = cellAbove(table, row);
|
|
border = cellBorder(cell.borderTop);
|
|
if (sibling && border) {
|
|
sibling.attr.style.borderBottom = border;
|
|
}
|
|
} else if (cell.background) {
|
|
style.borderTopColor = cell.background;
|
|
}
|
|
return td;
|
|
}
|
|
var HtmlTable = kendo.Class.extend({
|
|
init: function () {
|
|
this.cols = [];
|
|
this.trs = [];
|
|
this._height = 0;
|
|
this._width = 0;
|
|
},
|
|
addColumn: function (width) {
|
|
this._width += width;
|
|
var col = kendo.dom.element('col', { style: { width: width + 'px' } });
|
|
col.visible = width > 0;
|
|
this.cols.push(col);
|
|
},
|
|
addRow: function (height) {
|
|
var attr = null;
|
|
attr = { style: { height: height + 'px' } };
|
|
this._height += height;
|
|
var tr = kendo.dom.element('tr', attr);
|
|
tr.visible = height > 0;
|
|
this.trs.push(tr);
|
|
},
|
|
addCell: function (rowIndex, text, style, className, validation) {
|
|
if (text === null || text === undefined) {
|
|
text = '';
|
|
}
|
|
if (!(text instanceof kendo.dom.Node)) {
|
|
text = kendo.dom.text(text);
|
|
}
|
|
var children = [text];
|
|
var properties = { style: style };
|
|
if (validation && !validation.value) {
|
|
children.push(kendo.dom.element('span', { className: 'k-dirty' }));
|
|
className = (className || '') + (className ? ' ' : '') + 'k-dirty-cell';
|
|
properties.title = validation._getOptions().messageTemplate;
|
|
}
|
|
if (className) {
|
|
properties.className = className;
|
|
}
|
|
var td = kendo.dom.element('td', properties, children);
|
|
this.trs[rowIndex].children.push(td);
|
|
return td;
|
|
},
|
|
toDomTree: function (x, y, className) {
|
|
this.trs = this.trs.filter(function (tr) {
|
|
return tr.visible;
|
|
});
|
|
var offset = 0;
|
|
this.cols = this.cols.filter(function (col, ci) {
|
|
if (!col.visible) {
|
|
this.trs.forEach(function (tr) {
|
|
tr.children.splice(ci - offset, 1);
|
|
});
|
|
offset++;
|
|
}
|
|
return col.visible;
|
|
}, this);
|
|
return kendo.dom.element('table', {
|
|
style: {
|
|
left: x + 'px',
|
|
top: y + 'px',
|
|
height: this._height + 'px',
|
|
width: this._width + 'px'
|
|
},
|
|
className: className
|
|
}, [
|
|
kendo.dom.element('colgroup', null, this.cols),
|
|
kendo.dom.element('tbody', null, this.trs)
|
|
]);
|
|
}
|
|
});
|
|
var CELL_CONTEXT_MENU = '<ul class="#=classNames.cellContextMenu#">' + '<li data-action=cut>Cut</li>' + '<li data-action=copy>Copy</li>' + '<li data-action=paste>Paste</li>' + '<li class="k-separator"></li>' + '<li data-action=merge>Merge</li>' + '<li data-action=unmerge>Unmerge</li>' + '</ul>';
|
|
var ROW_HEADER_CONTEXT_MENU = '<ul class="#=classNames.rowHeaderContextMenu#">' + '<li data-action=cut>Cut</li>' + '<li data-action=copy>Copy</li>' + '<li data-action=paste>Paste</li>' + '<li class="k-separator"></li>' + '<li data-action="delete-row">Delete</li>' + '<li data-action="hide-row">Hide</li>' + '<li data-action="unhide-row">Unhide</li>' + '</ul>';
|
|
var COL_HEADER_CONTEXT_MENU = '<ul class="#=classNames.colHeaderContextMenu#">' + '<li data-action=cut>Cut</li>' + '<li data-action=copy>Copy</li>' + '<li data-action=paste>Paste</li>' + '<li class="k-separator"></li>' + '<li data-action="delete-column">Delete</li>' + '<li data-action="hide-column">Hide</li>' + '<li data-action="unhide-column">Unhide</li>' + '</ul>';
|
|
kendo.spreadsheet.ContextMenu = kendo.ui.ContextMenu;
|
|
var VIEW_CONTENTS = kendo.template('<div class="#=classNames.view#"><div class="#=classNames.fixedContainer#"></div><div class="#=classNames.scroller#"><div class="#=classNames.viewSize#"></div></div>' + '<div tabindex="0" class="#=classNames.clipboard#" contenteditable=true></div><div class="#=classNames.cellEditor#"></div></div><div class="#=classNames.sheetsBar#"></div>' + CELL_CONTEXT_MENU + ROW_HEADER_CONTEXT_MENU + COL_HEADER_CONTEXT_MENU);
|
|
function within(value, min, max) {
|
|
return value >= min && value <= max;
|
|
}
|
|
var View = kendo.Class.extend({
|
|
init: function (element, options) {
|
|
var classNames = View.classNames;
|
|
this.element = element;
|
|
this.options = $.extend(true, {}, this.options, options);
|
|
this._chrome();
|
|
this._dialogs = [];
|
|
element.append(VIEW_CONTENTS({ classNames: classNames }));
|
|
this._formulaInput();
|
|
this.wrapper = element.find(DOT + classNames.view);
|
|
this.container = element.find(DOT + classNames.fixedContainer)[0];
|
|
this.scroller = element.find(DOT + classNames.scroller)[0];
|
|
this.clipboard = element.find(DOT + classNames.clipboard);
|
|
this.viewSize = $(this.scroller.firstChild);
|
|
this.tree = new kendo.dom.Tree(this.container);
|
|
this.clipboardContents = new kendo.dom.Tree(this.clipboard[0]);
|
|
this.editor = new kendo.spreadsheet.SheetEditor(this);
|
|
this._sheetsbar();
|
|
var contextMenuConfig = {
|
|
target: element,
|
|
animation: false,
|
|
showOn: 'never'
|
|
};
|
|
this.cellContextMenu = new kendo.spreadsheet.ContextMenu(element.find(DOT + classNames.cellContextMenu), contextMenuConfig);
|
|
this.colHeaderContextMenu = new kendo.spreadsheet.ContextMenu(element.find(DOT + classNames.colHeaderContextMenu), contextMenuConfig);
|
|
this.rowHeaderContextMenu = new kendo.spreadsheet.ContextMenu(element.find(DOT + classNames.rowHeaderContextMenu), contextMenuConfig);
|
|
var scrollbar = kendo.support.scrollbar();
|
|
$(this.container).css({
|
|
width: this.wrapper[0].clientWidth - scrollbar,
|
|
height: this.wrapper[0].clientHeight - scrollbar
|
|
});
|
|
},
|
|
_resize: function () {
|
|
var tabstripHeight = this.tabstrip ? this.tabstrip.element.outerHeight() : 0;
|
|
var formulaBarHeight = this.formulaBar ? this.formulaBar.element.outerHeight() : 0;
|
|
var sheetsBarHeight = this.sheetsbar ? this.sheetsbar.element.outerHeight() : 0;
|
|
this.wrapper.height(this.element.height() - (tabstripHeight + formulaBarHeight + sheetsBarHeight));
|
|
if (this.tabstrip) {
|
|
this.tabstrip.quickAccessAdjust();
|
|
}
|
|
},
|
|
_chrome: function () {
|
|
var formulaBar = $('<div />').prependTo(this.element);
|
|
this.formulaBar = new kendo.spreadsheet.FormulaBar(formulaBar);
|
|
if (this.options.toolbar) {
|
|
this._tabstrip();
|
|
}
|
|
},
|
|
_formulaInput: function () {
|
|
var editor = this.element.find(DOT + View.classNames.cellEditor);
|
|
this.formulaInput = new kendo.spreadsheet.FormulaInput(editor, { autoScale: true });
|
|
},
|
|
_sheetsbar: function () {
|
|
if (this.options.sheetsbar) {
|
|
this.sheetsbar = new kendo.spreadsheet.SheetsBar(this.element.find(DOT + View.classNames.sheetsBar), $.extend(true, {}, this.options.sheetsbar));
|
|
}
|
|
},
|
|
_tabstrip: function () {
|
|
var messages = VIEW_MESAGES.tabs;
|
|
var options = $.extend(true, {
|
|
home: true,
|
|
insert: true,
|
|
data: true
|
|
}, this.options.toolbar);
|
|
var tabs = [];
|
|
if (this.tabstrip) {
|
|
this.tabstrip.destroy();
|
|
this.element.children('.k-tabstrip').remove();
|
|
}
|
|
for (var name in options) {
|
|
if (options[name] === true || options[name] instanceof Array) {
|
|
tabs.push({
|
|
id: name,
|
|
text: messages[name],
|
|
content: ''
|
|
});
|
|
}
|
|
}
|
|
this.tabstrip = new kendo.spreadsheet.TabStrip($('<div />').prependTo(this.element), {
|
|
animation: false,
|
|
dataTextField: 'text',
|
|
dataContentField: 'content',
|
|
dataSource: tabs,
|
|
toolbarOptions: options,
|
|
view: this
|
|
});
|
|
this.tabstrip.select(0);
|
|
},
|
|
_executeCommand: function (e) {
|
|
this._workbook.execute(e);
|
|
},
|
|
workbook: function (workbook) {
|
|
this._workbook = workbook;
|
|
},
|
|
sheet: function (sheet) {
|
|
this._sheet = sheet;
|
|
},
|
|
activeCellRectangle: function () {
|
|
return this.cellRectangle(this._sheet._viewActiveCell());
|
|
},
|
|
_rectangle: function (pane, ref) {
|
|
return pane._grid.boundingRectangle(ref.toRangeRef());
|
|
},
|
|
isColumnResizer: function (x, pane, ref) {
|
|
var rectangle = this._rectangle(pane, ref);
|
|
x -= this._sheet._grid._headerWidth;
|
|
var handleWidth = RESIZE_HANDLE_WIDTH / 2;
|
|
var right = rectangle.right - this.scroller.scrollLeft;
|
|
return right - handleWidth <= x && x <= right + handleWidth;
|
|
},
|
|
isRowResizer: function (y, pane, ref) {
|
|
var rectangle = this._rectangle(pane, ref);
|
|
y -= this._sheet._grid._headerHeight;
|
|
var handleWidth = RESIZE_HANDLE_WIDTH / 2;
|
|
var bottom = rectangle.bottom - this.scroller.scrollTop;
|
|
return bottom - handleWidth <= y && y <= bottom + handleWidth;
|
|
},
|
|
isFilterIcon: function (x, y, pane, ref) {
|
|
var result = false;
|
|
x -= this._sheet._grid._headerWidth - this.scroller.scrollLeft;
|
|
y -= this._sheet._grid._headerHeight - this.scroller.scrollTop;
|
|
this._sheet.forEachFilterHeader(ref, function (ref) {
|
|
var rect = this._rectangle(pane, ref);
|
|
result = result || pane.filterIconRect(rect).intersects(x, y);
|
|
}.bind(this));
|
|
return result;
|
|
},
|
|
isAutoFill: function (x, y, pane) {
|
|
var selection = this._sheet.select();
|
|
if (selection.size > 1) {
|
|
return false;
|
|
}
|
|
x -= this._sheet._grid._headerWidth;
|
|
y -= this._sheet._grid._headerHeight;
|
|
if (!pane._grid.columns.frozen) {
|
|
x += this.scroller.scrollLeft;
|
|
}
|
|
if (!pane._grid.rows.frozen) {
|
|
y += this.scroller.scrollTop;
|
|
}
|
|
var rectangle = this._rectangle(pane, selection);
|
|
return Math.abs(rectangle.right - x) < 8 && Math.abs(rectangle.bottom - y) < 8;
|
|
},
|
|
objectAt: function (x, y) {
|
|
var grid = this._sheet._grid;
|
|
var object, pane;
|
|
if (x < 0 || y < 0 || x > this.scroller.clientWidth || y > this.scroller.clientHeight) {
|
|
object = { type: 'outside' };
|
|
} else if (x < grid._headerWidth && y < grid._headerHeight) {
|
|
object = { type: 'topcorner' };
|
|
} else {
|
|
pane = this.paneAt(x, y);
|
|
var row = pane._grid.rows.index(y, this.scroller.scrollTop);
|
|
var column = pane._grid.columns.index(x, this.scroller.scrollLeft);
|
|
var type = 'cell';
|
|
var ref = new CellRef(row, column);
|
|
if (this.isAutoFill(x, y, pane)) {
|
|
type = 'autofill';
|
|
} else if (this.isFilterIcon(x, y, pane, ref)) {
|
|
type = 'filtericon';
|
|
} else if (x < grid._headerWidth) {
|
|
ref = new CellRef(row, -Infinity);
|
|
type = this.isRowResizer(y, pane, ref) ? 'rowresizehandle' : 'rowheader';
|
|
} else if (y < grid._headerHeight) {
|
|
ref = new CellRef(-Infinity, column);
|
|
type = this.isColumnResizer(x, pane, ref) ? 'columnresizehandle' : 'columnheader';
|
|
}
|
|
object = {
|
|
type: type,
|
|
ref: ref
|
|
};
|
|
}
|
|
object.pane = pane;
|
|
object.x = x;
|
|
object.y = y;
|
|
return object;
|
|
},
|
|
paneAt: function (x, y) {
|
|
return this.panes.filter(function paneLocationWithin(pane) {
|
|
var grid = pane._grid;
|
|
return within(y, grid.top, grid.bottom) && within(x, grid.left, grid.right);
|
|
})[0];
|
|
},
|
|
containingPane: function (cell) {
|
|
return this.panes.filter(function (pane) {
|
|
if (pane._grid.contains(cell)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
})[0];
|
|
},
|
|
cellRectangle: function (cell) {
|
|
var theGrid = this.containingPane(cell)._grid;
|
|
var rectangle = this._sheet._grid.rectangle(cell);
|
|
return rectangle.offset(theGrid.headerWidth - this.scroller.scrollLeft, theGrid.headerHeight - this.scroller.scrollTop);
|
|
},
|
|
refresh: function (reason) {
|
|
var sheet = this._sheet;
|
|
if (this.tabstrip) {
|
|
this.tabstrip.refreshTools(sheet.range(sheet.activeCell()));
|
|
}
|
|
if (reason.sheetSelection && this.sheetsbar) {
|
|
this.sheetsbar.renderSheets(this._workbook.sheets(), this._workbook.sheetIndex(this._sheet));
|
|
}
|
|
this._resize();
|
|
this.viewSize[0].style.height = sheet._grid.totalHeight() + 'px';
|
|
this.viewSize[0].style.width = sheet._grid.totalWidth() + 'px';
|
|
if (reason.layout) {
|
|
var frozenColumns = sheet.frozenColumns();
|
|
var frozenRows = sheet.frozenRows();
|
|
this.panes = [this._pane(frozenRows, frozenColumns)];
|
|
if (frozenColumns > 0) {
|
|
this.panes.push(this._pane(frozenRows, 0, null, frozenColumns));
|
|
}
|
|
if (frozenRows > 0) {
|
|
this.panes.push(this._pane(0, frozenColumns, frozenRows, null));
|
|
}
|
|
if (frozenRows > 0 && frozenColumns > 0) {
|
|
this.panes.push(this._pane(0, 0, frozenRows, frozenColumns));
|
|
}
|
|
}
|
|
if (reason.filter) {
|
|
this._destroyFilterMenu();
|
|
}
|
|
if (reason.activeCell) {
|
|
this._focus = sheet.activeCell().toRangeRef();
|
|
}
|
|
},
|
|
createFilterMenu: function (column) {
|
|
if (this._filterMenu && this._filterMenu.options.column == column) {
|
|
return this._filterMenu;
|
|
}
|
|
this._destroyFilterMenu();
|
|
var sheet = this._sheet;
|
|
var ref = sheet.filter().ref;
|
|
var range = new kendo.spreadsheet.Range(ref, sheet);
|
|
var element = $('<div />').appendTo(this.element);
|
|
var options = {
|
|
column: column,
|
|
range: range
|
|
};
|
|
var filterMenu = new kendo.spreadsheet.FilterMenu(element, options);
|
|
this._filterMenu = filterMenu;
|
|
return filterMenu;
|
|
},
|
|
selectClipBoardContents: function () {
|
|
this.clipboard.focus();
|
|
selectElementContents(this.clipboard[0]);
|
|
},
|
|
scrollIntoView: function (cell) {
|
|
var willScroll = false;
|
|
var theGrid = this.containingPane(cell)._grid;
|
|
var boundaries = theGrid.scrollBoundaries(cell);
|
|
var scroller = this.scroller;
|
|
var scrollTop = theGrid.rows.frozen ? 0 : scroller.scrollTop;
|
|
var scrollLeft = theGrid.columns.frozen ? 0 : scroller.scrollLeft;
|
|
if (boundaries.top < scrollTop) {
|
|
willScroll = true;
|
|
scroller.scrollTop = boundaries.scrollTop;
|
|
}
|
|
if (boundaries.bottom > scrollTop) {
|
|
willScroll = true;
|
|
scroller.scrollTop = boundaries.scrollBottom;
|
|
}
|
|
if (boundaries.left < scrollLeft) {
|
|
willScroll = true;
|
|
scroller.scrollLeft = boundaries.scrollLeft;
|
|
}
|
|
if (boundaries.right > scrollLeft) {
|
|
willScroll = true;
|
|
scroller.scrollLeft = boundaries.scrollRight;
|
|
}
|
|
return willScroll;
|
|
},
|
|
_destroyDialog: function () {
|
|
this._dialogs.pop();
|
|
},
|
|
openDialog: function (name, options) {
|
|
var sheet = this._sheet;
|
|
var ref = sheet.activeCell();
|
|
var range = new kendo.spreadsheet.Range(ref, sheet);
|
|
var dialog = kendo.spreadsheet.dialogs.create(name, options);
|
|
if (dialog) {
|
|
dialog.bind('action', this._executeCommand.bind(this));
|
|
dialog.bind('deactivate', this._destroyDialog.bind(this));
|
|
this._dialogs.push(dialog);
|
|
dialog.open(range);
|
|
return dialog;
|
|
}
|
|
},
|
|
showError: function (options) {
|
|
var errorMessages = VIEW_MESAGES.errors;
|
|
if (kendo.spreadsheet.dialogs.registered(options.type)) {
|
|
this.openDialog(options.type);
|
|
} else {
|
|
this.openDialog('message', {
|
|
title: options.title || 'Error',
|
|
text: options.body ? options.body : errorMessages[options.type],
|
|
activate: function () {
|
|
this.dialog().element.find('.k-button').focus();
|
|
},
|
|
close: function () {
|
|
this.editor.focusLastActive();
|
|
}.bind(this)
|
|
});
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this._dialogs.forEach(function (dialog) {
|
|
dialog.destroy();
|
|
});
|
|
this.cellContextMenu.destroy();
|
|
this.rowHeaderContextMenu.destroy();
|
|
this.colHeaderContextMenu.destroy();
|
|
if (this.tabstrip) {
|
|
this.tabstrip.destroy();
|
|
}
|
|
this._destroyFilterMenu();
|
|
},
|
|
_destroyFilterMenu: function () {
|
|
if (this._filterMenu) {
|
|
this._filterMenu.destroy();
|
|
this._filterMenu = undefined;
|
|
this._filterMenuColumn = undefined;
|
|
}
|
|
},
|
|
render: function () {
|
|
if (!this.element.is(':visible')) {
|
|
return;
|
|
}
|
|
var sheet = this._sheet;
|
|
var focus = sheet.focus();
|
|
if (focus && this.scrollIntoView(focus)) {
|
|
return;
|
|
}
|
|
var grid = sheet._grid;
|
|
var scrollTop = this.scroller.scrollTop;
|
|
var scrollLeft = this.scroller.scrollLeft;
|
|
if (scrollTop < 0) {
|
|
scrollTop = 0;
|
|
}
|
|
if (scrollLeft < 0) {
|
|
scrollLeft = 0;
|
|
}
|
|
var result = this.panes.map(function (pane) {
|
|
return pane.render(scrollLeft, scrollTop);
|
|
}, this);
|
|
var merged = [];
|
|
merged = Array.prototype.concat.apply(merged, result);
|
|
var topCorner = kendo.dom.element('div', {
|
|
style: {
|
|
width: grid._headerWidth + 'px',
|
|
height: grid._headerHeight + 'px'
|
|
},
|
|
className: View.classNames.topCorner
|
|
});
|
|
merged.push(topCorner);
|
|
if (sheet.resizeHandlePosition() && sheet.resizeHintPosition()) {
|
|
merged.push(this.renderResizeHint());
|
|
}
|
|
this.tree.render(merged);
|
|
if (this.editor.isActive()) {
|
|
this.editor.toggleTooltip(this.activeCellRectangle());
|
|
} else if (!sheet.selectionInProgress() && !sheet.resizingInProgress() && !sheet.isInEditMode()) {
|
|
this.renderClipboardContents();
|
|
}
|
|
},
|
|
renderResizeHint: function () {
|
|
var sheet = this._sheet;
|
|
var ref = sheet.resizeHandlePosition();
|
|
var horizontal = ref.col !== -Infinity;
|
|
var style;
|
|
if (horizontal) {
|
|
style = {
|
|
height: this.scroller.clientHeight + 'px',
|
|
width: RESIZE_HANDLE_WIDTH + 'px',
|
|
left: sheet.resizeHintPosition().x + 'px',
|
|
top: '0px'
|
|
};
|
|
} else {
|
|
style = {
|
|
height: RESIZE_HANDLE_WIDTH + 'px',
|
|
width: this.scroller.clientWidth + 'px',
|
|
top: sheet.resizeHintPosition().y + 'px',
|
|
left: '0px'
|
|
};
|
|
}
|
|
var classNames = Pane.classNames;
|
|
return kendo.dom.element('div', {
|
|
className: classNames.resizeHint + (!horizontal ? ' ' + classNames.resizeHintVertical : ''),
|
|
style: style
|
|
}, [
|
|
kendo.dom.element('div', { className: classNames.resizeHintHandle }),
|
|
kendo.dom.element('div', { className: classNames.resizeHintMarker })
|
|
]);
|
|
},
|
|
renderClipboardContents: function () {
|
|
var sheet = this._sheet;
|
|
var grid = sheet._grid;
|
|
var selection = sheet.select().toRangeRef();
|
|
var status = this._workbook.clipboard().canCopy();
|
|
if (status.canCopy === false && status.multiSelection) {
|
|
this.clipboardContents.render([]);
|
|
this.selectClipBoardContents();
|
|
return;
|
|
}
|
|
selection = sheet.trim(selection);
|
|
var table = new HtmlTable();
|
|
var selectionView = grid.rangeDimensions(selection);
|
|
selectionView.rows.forEach(function (height) {
|
|
table.addRow(height);
|
|
});
|
|
selectionView.columns.forEach(function (width) {
|
|
table.addColumn(width);
|
|
});
|
|
var primaryMergedCells = {};
|
|
var secondaryMergedCells = {};
|
|
sheet.forEachMergedCell(selection, function (ref) {
|
|
var topLeft = ref.topLeft;
|
|
grid.forEach(ref, function (cellRef) {
|
|
if (topLeft.eq(cellRef)) {
|
|
primaryMergedCells[cellRef.print()] = ref;
|
|
} else {
|
|
secondaryMergedCells[cellRef.print()] = true;
|
|
}
|
|
});
|
|
});
|
|
sheet.forEach(selection, function (row, col, cell) {
|
|
var location = new CellRef(row, col).print();
|
|
if (!secondaryMergedCells[location]) {
|
|
var td = addCell(table, row - selection.topLeft.row, cell);
|
|
var mergedCell = primaryMergedCells[location];
|
|
if (mergedCell) {
|
|
td.attr.colspan = mergedCell.width();
|
|
td.attr.rowspan = mergedCell.height();
|
|
}
|
|
}
|
|
});
|
|
this.clipboardContents.render([table.toDomTree(0, 0, 'kendo-clipboard-' + this._workbook.clipboard()._uid)]);
|
|
this.selectClipBoardContents();
|
|
},
|
|
_pane: function (row, column, rowCount, columnCount) {
|
|
var pane = new Pane(this._sheet, this._sheet._grid.pane({
|
|
row: row,
|
|
column: column,
|
|
rowCount: rowCount,
|
|
columnCount: columnCount
|
|
}));
|
|
pane.refresh(this.scroller.clientWidth, this.scroller.clientHeight);
|
|
return pane;
|
|
}
|
|
});
|
|
var paneClassNames = {
|
|
cell: 'k-spreadsheet-cell',
|
|
vaxis: 'k-spreadsheet-vaxis',
|
|
haxis: 'k-spreadsheet-haxis',
|
|
rowHeader: 'k-spreadsheet-row-header',
|
|
columnHeader: 'k-spreadsheet-column-header',
|
|
pane: 'k-spreadsheet-pane',
|
|
data: 'k-spreadsheet-data',
|
|
mergedCell: 'k-spreadsheet-merged-cell',
|
|
mergedCellsWrapper: 'k-merged-cells-wrapper',
|
|
activeCell: 'k-spreadsheet-active-cell',
|
|
selection: 'k-spreadsheet-selection',
|
|
selectionWrapper: 'k-selection-wrapper',
|
|
autoFillWrapper: 'k-auto-fill-wrapper',
|
|
single: 'k-single',
|
|
top: 'k-top',
|
|
right: 'k-right',
|
|
bottom: 'k-bottom',
|
|
left: 'k-left',
|
|
resizeHandle: 'k-resize-handle',
|
|
resizeHint: 'k-resize-hint',
|
|
resizeHintHandle: 'k-resize-hint-handle',
|
|
resizeHintMarker: 'k-resize-hint-marker',
|
|
resizeHintVertical: 'k-resize-hint-vertical',
|
|
selectionHighlight: 'k-spreadsheet-selection-highlight',
|
|
series: [
|
|
'k-series-a',
|
|
'k-series-b',
|
|
'k-series-c',
|
|
'k-series-d',
|
|
'k-series-e',
|
|
'k-series-f'
|
|
]
|
|
};
|
|
var Pane = kendo.Class.extend({
|
|
init: function (sheet, grid) {
|
|
this._sheet = sheet;
|
|
this._grid = grid;
|
|
},
|
|
refresh: function (width, height) {
|
|
this._grid.refresh(width, height);
|
|
},
|
|
isVisible: function (scrollLeft, scrollTop, ref) {
|
|
return this._grid.view(scrollLeft, scrollTop).ref.intersects(ref);
|
|
},
|
|
render: function (scrollLeft, scrollTop) {
|
|
var classNames = Pane.classNames;
|
|
var sheet = this._sheet;
|
|
var grid = this._grid;
|
|
var view = grid.view(scrollLeft, scrollTop);
|
|
this._currentView = view;
|
|
this._currentRect = this._rectangle(view.ref);
|
|
this._selectedHeaders = sheet.selectedHeaders();
|
|
var children = [];
|
|
children.push(this.renderData());
|
|
children.push(this.renderSelection());
|
|
children.push(this.renderAutoFill());
|
|
children.push(this.renderEditorSelection());
|
|
children.push(this.renderFilterHeaders());
|
|
if (grid.hasRowHeader) {
|
|
var rowHeader = kendo.dom.element('div', {
|
|
className: classNames.rowHeader,
|
|
style: {
|
|
width: grid.headerWidth + 'px',
|
|
top: view.rowOffset + 'px'
|
|
}
|
|
});
|
|
children.push(rowHeader);
|
|
sheet.forEach(view.ref.leftColumn(), function (row) {
|
|
if (!sheet.isHiddenRow(row)) {
|
|
var text = row + 1, height = sheet.rowHeight(row);
|
|
rowHeader.children.push(kendo.dom.element('div', {
|
|
className: this.headerClassName(row, 'row'),
|
|
style: {
|
|
width: grid.headerWidth + 'px',
|
|
height: height + 'px'
|
|
}
|
|
}, [kendo.dom.element('div', { className: 'k-vertical-align-center' }, [kendo.dom.text(text + '')])]));
|
|
}
|
|
}.bind(this));
|
|
}
|
|
if (grid.hasColumnHeader) {
|
|
var columnHeader = kendo.dom.element('div', {
|
|
className: classNames.columnHeader,
|
|
style: {
|
|
top: '0px',
|
|
left: view.columnOffset + 'px',
|
|
width: this._currentRect.width + 'px',
|
|
height: grid.headerHeight + 'px'
|
|
}
|
|
});
|
|
children.push(columnHeader);
|
|
var left = 0;
|
|
sheet.forEach(view.ref.topRow(), function (row, col) {
|
|
if (!sheet.isHiddenColumn(col)) {
|
|
var text = kendo.spreadsheet.Ref.display(null, Infinity, col), width = sheet.columnWidth(col);
|
|
columnHeader.children.push(kendo.dom.element('div', {
|
|
className: this.headerClassName(col, 'col'),
|
|
style: {
|
|
position: 'absolute',
|
|
left: left + 'px',
|
|
width: width + 'px',
|
|
height: grid.headerHeight + 'px'
|
|
}
|
|
}, [kendo.dom.element('div', { className: 'k-vertical-align-center' }, [kendo.dom.text(text + '')])]));
|
|
left += width;
|
|
}
|
|
}.bind(this));
|
|
}
|
|
if (sheet.resizeHandlePosition() && (grid.hasColumnHeader || grid.hasRowHeader)) {
|
|
var ref = sheet._grid.normalize(sheet.resizeHandlePosition());
|
|
if (view.ref.intersects(ref)) {
|
|
if (!sheet.resizeHintPosition()) {
|
|
children.push(this.renderResizeHandler());
|
|
}
|
|
}
|
|
}
|
|
var paneClasses = [classNames.pane];
|
|
if (grid.hasColumnHeader) {
|
|
paneClasses.push(classNames.top);
|
|
}
|
|
if (grid.hasRowHeader) {
|
|
paneClasses.push(classNames.left);
|
|
}
|
|
return kendo.dom.element('div', {
|
|
style: grid.style,
|
|
className: paneClasses.join(' ')
|
|
}, children);
|
|
},
|
|
headerClassName: function (index, type) {
|
|
var selectedHeaders = this._selectedHeaders;
|
|
var itemSelection;
|
|
var allHeaders;
|
|
if (type === 'row') {
|
|
itemSelection = selectedHeaders.rows[index];
|
|
allHeaders = selectedHeaders.allRows;
|
|
} else {
|
|
itemSelection = selectedHeaders.cols[index];
|
|
allHeaders = selectedHeaders.allCols;
|
|
}
|
|
var className = itemSelection || (selectedHeaders.all ? 'full' : allHeaders ? 'partial' : 'none');
|
|
if (className) {
|
|
className = 'k-selection-' + className;
|
|
}
|
|
return className;
|
|
},
|
|
renderData: function () {
|
|
var view = this._currentView;
|
|
var cont = kendo.dom.element('div', {
|
|
className: Pane.classNames.data,
|
|
style: {
|
|
position: 'relative',
|
|
left: view.columnOffset + 'px',
|
|
top: view.rowOffset + 'px'
|
|
}
|
|
});
|
|
var rect = this._currentRect;
|
|
var layout = kendo.spreadsheet.draw.doLayout(this._sheet, view.ref, { forScreen: true }), prev;
|
|
var showGridLines = this._sheet.options.showGridLines;
|
|
if (showGridLines) {
|
|
prev = null;
|
|
layout.xCoords.forEach(function (x) {
|
|
if (x !== prev) {
|
|
prev = x;
|
|
cont.children.push(kendo.dom.element('div', {
|
|
className: paneClassNames.vaxis,
|
|
style: {
|
|
left: x + 'px',
|
|
height: rect.height + 'px'
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
prev = null;
|
|
layout.yCoords.forEach(function (y) {
|
|
if (y !== prev) {
|
|
prev = y;
|
|
cont.children.push(kendo.dom.element('div', {
|
|
className: paneClassNames.haxis,
|
|
style: {
|
|
top: y + 'px',
|
|
width: rect.width + 'px'
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
var vBorders = {}, hBorders = {};
|
|
layout.cells.forEach(function (cell) {
|
|
var hb = hBorders[cell.col] || (hBorders[cell.col] = {});
|
|
var vb = vBorders[cell.row] || (vBorders[cell.row] = {});
|
|
drawCell(cont.children, cell, null, hb, vb, showGridLines);
|
|
});
|
|
return cont;
|
|
},
|
|
renderResizeHandler: function () {
|
|
var sheet = this._sheet;
|
|
var ref = sheet.resizeHandlePosition();
|
|
var rectangle = this._rectangle(ref);
|
|
var style;
|
|
if (ref.col !== -Infinity) {
|
|
style = {
|
|
height: this._grid.headerHeight + 'px',
|
|
width: RESIZE_HANDLE_WIDTH + 'px',
|
|
left: rectangle.right - RESIZE_HANDLE_WIDTH / 2 + 'px',
|
|
top: '0px'
|
|
};
|
|
} else {
|
|
style = {
|
|
height: RESIZE_HANDLE_WIDTH + 'px',
|
|
width: this._grid.headerWidth + 'px',
|
|
top: rectangle.bottom - RESIZE_HANDLE_WIDTH / 2 + 'px',
|
|
left: '0px'
|
|
};
|
|
}
|
|
return kendo.dom.element('div', {
|
|
className: Pane.classNames.resizeHandle,
|
|
style: style
|
|
});
|
|
},
|
|
filterIconRect: function (rect) {
|
|
var BUTTON_SIZE = 16;
|
|
var BUTTON_OFFSET = 3;
|
|
return new kendo.spreadsheet.Rectangle(rect.right - BUTTON_SIZE - BUTTON_OFFSET, rect.top + BUTTON_OFFSET, BUTTON_SIZE, BUTTON_SIZE);
|
|
},
|
|
renderFilterHeaders: function () {
|
|
var sheet = this._sheet;
|
|
var children = [];
|
|
var classNames = View.classNames;
|
|
var filter = sheet.filter();
|
|
function icon(className) {
|
|
return kendo.dom.element('span', { className: classNames.icon + ' ' + className });
|
|
}
|
|
function filterButton(classNames, position, index) {
|
|
var style = {
|
|
left: position.left + 'px',
|
|
top: position.top + 'px'
|
|
};
|
|
var filtered = filter && filter.columns.some(function (c) {
|
|
return c.index === index;
|
|
});
|
|
var classes = classNames.filterButton;
|
|
if (filtered) {
|
|
classes += ' ' + classNames.filterButtonActive;
|
|
}
|
|
var button = kendo.dom.element('span', {
|
|
className: classes,
|
|
style: style
|
|
}, [icon(classNames.iconFilterDefault)]);
|
|
return button;
|
|
}
|
|
if (filter) {
|
|
this._addDiv(children, filter.ref, classNames.filterRange);
|
|
}
|
|
sheet.forEachFilterHeader(this._currentView.ref, function (ref) {
|
|
var rect = this._rectangle(ref);
|
|
var position = this.filterIconRect(rect);
|
|
var column = this._sheet.filterColumn(ref);
|
|
var button = filterButton(classNames, position, column);
|
|
children.push(button);
|
|
}.bind(this));
|
|
return kendo.dom.element('div', { className: classNames.filterHeadersWrapper }, children);
|
|
},
|
|
renderEditorSelection: function () {
|
|
var classNames = Pane.classNames;
|
|
var sheet = this._sheet;
|
|
var selections = [];
|
|
sheet._formulaSelections.forEach(function (range) {
|
|
var ref = range.ref;
|
|
if (ref === kendo.spreadsheet.NULLREF) {
|
|
return;
|
|
}
|
|
this._addDiv(selections, ref, classNames.selectionHighlight + ' ' + range.colorClass);
|
|
}.bind(this));
|
|
return kendo.dom.element('div', { className: classNames.selectionWrapper }, selections);
|
|
},
|
|
renderSelection: function () {
|
|
var classNames = Pane.classNames;
|
|
var selections = [];
|
|
var activeCellClasses = [classNames.activeCell];
|
|
var selectionClasses = [classNames.selection];
|
|
var sheet = this._sheet;
|
|
var activeCell = sheet.activeCell().toRangeRef();
|
|
var activeFormulaColor = this._activeFormulaColor();
|
|
var selection = sheet.select();
|
|
activeCellClasses = activeCellClasses.concat(activeFormulaColor, this._directionClasses(activeCell));
|
|
selectionClasses = selectionClasses.concat(activeFormulaColor);
|
|
if (sheet.singleCellSelection()) {
|
|
activeCellClasses.push(classNames.single);
|
|
}
|
|
if (selection.size() === 1) {
|
|
selectionClasses.push('k-single-selection');
|
|
}
|
|
if (this._sheet.autoFillPunch()) {
|
|
selectionClasses.push('k-dim-auto-fill-handle');
|
|
}
|
|
selection.forEach(function (ref) {
|
|
if (ref !== kendo.spreadsheet.NULLREF) {
|
|
this._addDiv(selections, ref, selectionClasses.join(' '));
|
|
}
|
|
}.bind(this));
|
|
this._addTable(selections, activeCell, activeCellClasses.join(' '));
|
|
return kendo.dom.element('div', { className: classNames.selectionWrapper }, selections);
|
|
},
|
|
renderAutoFill: function () {
|
|
var autoFillRectangle = [];
|
|
if (this._sheet.autoFillInProgress()) {
|
|
var autoFillRef = this._sheet.autoFillRef();
|
|
var punch = this._sheet.autoFillPunch();
|
|
var direction = this._sheet._autoFillDirection;
|
|
this._addDiv(autoFillRectangle, autoFillRef, 'k-auto-fill');
|
|
if (punch) {
|
|
this._addDiv(autoFillRectangle, punch, 'k-auto-fill-punch');
|
|
} else if (direction !== undefined) {
|
|
var ref, cssClass;
|
|
switch (direction) {
|
|
case 0:
|
|
ref = autoFillRef.bottomRight;
|
|
cssClass = 'k-auto-fill-br-hint';
|
|
break;
|
|
case 1:
|
|
ref = autoFillRef.bottomRight;
|
|
cssClass = 'k-auto-fill-br-hint';
|
|
break;
|
|
case 2:
|
|
ref = new CellRef(autoFillRef.topLeft.row, autoFillRef.bottomRight.col);
|
|
cssClass = 'k-auto-fill-tr-hint';
|
|
break;
|
|
case 3:
|
|
ref = new CellRef(autoFillRef.bottomRight.row, autoFillRef.topLeft.col);
|
|
cssClass = 'k-auto-fill-bl-hint';
|
|
break;
|
|
}
|
|
var hint = kendo.dom.element('span', { className: 'k-tooltip' }, [kendo.dom.text(this._sheet._autoFillHint)]);
|
|
var rectangle = this._addDiv(autoFillRectangle, ref, cssClass);
|
|
if (rectangle) {
|
|
rectangle.children.push(hint);
|
|
}
|
|
}
|
|
}
|
|
return kendo.dom.element('div', { className: Pane.classNames.autoFillWrapper }, autoFillRectangle);
|
|
},
|
|
_addDiv: function (collection, ref, className) {
|
|
var view = this._currentView, div;
|
|
if (view.ref.intersects(ref)) {
|
|
div = this._rectangle(ref).resize(1, 1).toDiv(className);
|
|
collection.push(div);
|
|
}
|
|
return div;
|
|
},
|
|
_addTable: function (collection, ref, className) {
|
|
var sheet = this._sheet;
|
|
var view = this._currentView;
|
|
if (view.ref.intersects(ref)) {
|
|
sheet.forEach(ref.collapse(), function (row, col, cell) {
|
|
var rectangle = this._rectangle(ref);
|
|
cell.left = rectangle.left;
|
|
cell.top = rectangle.top;
|
|
cell.width = rectangle.width;
|
|
cell.height = rectangle.height;
|
|
drawCell(collection, cell, className, null, null, true);
|
|
}.bind(this));
|
|
}
|
|
},
|
|
_activeFormulaColor: function () {
|
|
var activeFormulaSelection;
|
|
var colorClasses = [];
|
|
if (this._sheet.isInEditMode()) {
|
|
activeFormulaSelection = this._sheet._formulaSelections.filter(function (sel) {
|
|
return sel.active && sel.type == 'ref';
|
|
})[0];
|
|
if (activeFormulaSelection) {
|
|
colorClasses.push(activeFormulaSelection.colorClass);
|
|
}
|
|
}
|
|
return colorClasses;
|
|
},
|
|
_directionClasses: function (cell) {
|
|
var cellClasses = [];
|
|
var classNames = Pane.classNames;
|
|
var view = this._currentView.ref;
|
|
if (!cell.move(0, -1).intersects(view)) {
|
|
cellClasses.push(classNames.left);
|
|
}
|
|
if (!cell.move(-1, 0).intersects(view)) {
|
|
cellClasses.push(classNames.top);
|
|
}
|
|
if (!cell.move(0, 1).intersects(view)) {
|
|
cellClasses.push(classNames.right);
|
|
}
|
|
if (!cell.move(1, 0).intersects(view)) {
|
|
cellClasses.push(classNames.bottom);
|
|
}
|
|
return cellClasses;
|
|
},
|
|
_rectangle: function (ref) {
|
|
return this._grid.boundingRectangle(ref.toRangeRef()).offset(-this._currentView.mergedCellLeft, -this._currentView.mergedCellTop);
|
|
}
|
|
});
|
|
kendo.spreadsheet.View = View;
|
|
kendo.spreadsheet.Pane = Pane;
|
|
kendo.spreadsheet.drawCell = drawCell;
|
|
$.extend(true, View, { classNames: viewClassNames });
|
|
$.extend(true, Pane, { classNames: paneClassNames });
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/grid', [
|
|
'kendo.core',
|
|
'spreadsheet/references'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var CellRef = kendo.spreadsheet.CellRef;
|
|
var RangeRef = kendo.spreadsheet.RangeRef;
|
|
var UnionRef = kendo.spreadsheet.UnionRef;
|
|
var Rectangle = kendo.Class.extend({
|
|
init: function (left, top, width, height) {
|
|
this.left = left;
|
|
this.width = width;
|
|
this.right = left + width;
|
|
this.top = top;
|
|
this.height = height;
|
|
this.bottom = top + height;
|
|
},
|
|
offset: function (left, top) {
|
|
return new Rectangle(this.left + left, this.top + top, this.width, this.height);
|
|
},
|
|
resize: function (width, height) {
|
|
return new Rectangle(this.left, this.top, this.width + width, this.height + height);
|
|
},
|
|
intersects: function (x, y) {
|
|
return this.left < x && x < this.left + this.width && this.top < y && y < this.top + this.height;
|
|
},
|
|
toDiv: function (className) {
|
|
return kendo.dom.element('div', {
|
|
className: className,
|
|
style: {
|
|
width: this.width + 'px',
|
|
height: this.height + 'px',
|
|
top: this.top + 'px',
|
|
left: this.left + 'px'
|
|
}
|
|
});
|
|
}
|
|
});
|
|
var Grid = kendo.Class.extend({
|
|
init: function (rows, columns, rowCount, columnCount, headerHeight, headerWidth) {
|
|
this.rowCount = rowCount;
|
|
this.columnCount = columnCount;
|
|
this._columns = columns;
|
|
this._rows = rows;
|
|
this._headerHeight = headerHeight;
|
|
this._headerWidth = headerWidth;
|
|
},
|
|
isAxis: function (ref) {
|
|
ref = ref.toRangeRef();
|
|
var topLeft = ref.topLeft;
|
|
var bottomRight = ref.bottomRight;
|
|
return topLeft.row === 0 && bottomRight.row === this.rowCount - 1 || topLeft.col === 0 && bottomRight.col === this.columnCount - 1;
|
|
},
|
|
width: function (start, end) {
|
|
return this._columns.sum(start, end);
|
|
},
|
|
height: function (start, end) {
|
|
return this._rows.sum(start, end);
|
|
},
|
|
totalHeight: function () {
|
|
return this._rows.total + this._headerHeight;
|
|
},
|
|
totalWidth: function () {
|
|
return this._columns.total + this._headerWidth;
|
|
},
|
|
index: function (row, column) {
|
|
return column * this.rowCount + row;
|
|
},
|
|
cellRef: function (index) {
|
|
return new CellRef(index % this.rowCount, index / this.rowCount >> 0);
|
|
},
|
|
rowRef: function (row) {
|
|
return new RangeRef(new CellRef(row, 0), new CellRef(row, this.columnCount - 1));
|
|
},
|
|
colRef: function (col) {
|
|
return new RangeRef(new CellRef(0, col), new CellRef(this.rowCount - 1, col));
|
|
},
|
|
cellRefIndex: function (ref) {
|
|
return this.index(ref.row, ref.col);
|
|
},
|
|
normalize: function (ref) {
|
|
if (ref instanceof RangeRef) {
|
|
return new RangeRef(this.normalize(ref.topLeft), this.normalize(ref.bottomRight)).setSheet(ref.sheet, ref.hasSheet());
|
|
}
|
|
if (ref instanceof UnionRef) {
|
|
return ref.map(function (ref) {
|
|
return this.normalize(ref);
|
|
}, this);
|
|
}
|
|
var clone = ref.clone();
|
|
clone.col = Math.max(0, Math.min(this.columnCount - 1, ref.col));
|
|
clone.row = Math.max(0, Math.min(this.rowCount - 1, ref.row));
|
|
return clone;
|
|
},
|
|
rectangle: function (ref) {
|
|
var topLeft = this.normalize(ref.topLeft);
|
|
var bottomRight = this.normalize(ref.bottomRight);
|
|
return new Rectangle(this.width(0, topLeft.col - 1), this.height(0, topLeft.row - 1), this.width(topLeft.col, bottomRight.col), this.height(topLeft.row, bottomRight.row));
|
|
},
|
|
pane: function (options) {
|
|
return new PaneGrid(new kendo.spreadsheet.PaneAxis(this._rows, options.row, options.rowCount, this._headerHeight), new kendo.spreadsheet.PaneAxis(this._columns, options.column, options.columnCount, this._headerWidth), this);
|
|
},
|
|
rangeDimensions: function (rangeRef) {
|
|
return {
|
|
rows: this._rows.values.iterator(rangeRef.topLeft.row, rangeRef.bottomRight.row),
|
|
columns: this._columns.values.iterator(rangeRef.topLeft.col, rangeRef.bottomRight.col)
|
|
};
|
|
},
|
|
forEach: function (ref, callback) {
|
|
var topLeft = this.normalize(ref.topLeft);
|
|
var bottomRight = this.normalize(ref.bottomRight);
|
|
for (var ci = topLeft.col; ci <= bottomRight.col; ci++) {
|
|
for (var ri = topLeft.row; ri <= bottomRight.row; ri++) {
|
|
callback(new CellRef(ri, ci));
|
|
}
|
|
}
|
|
},
|
|
trim: function (ref, property) {
|
|
var topLeft = ref.topLeft;
|
|
var bottomRight = ref.bottomRight;
|
|
var bottomRightRow = topLeft.row;
|
|
var bottomRightCol = topLeft.col;
|
|
for (var ci = topLeft.col; ci <= bottomRight.col; ci++) {
|
|
var start = this.index(topLeft.row, ci);
|
|
var end = this.index(bottomRight.row, ci);
|
|
var values = property.tree.intersecting(start, end);
|
|
if (values.length) {
|
|
var cell = this.cellRef(values[values.length - 1].end);
|
|
bottomRightRow = Math.max(bottomRightRow, cell.row);
|
|
bottomRightCol = ci;
|
|
}
|
|
}
|
|
return new RangeRef(ref.topLeft, new CellRef(Math.min(bottomRightRow, ref.bottomRight.row), bottomRightCol));
|
|
}
|
|
});
|
|
var PaneGrid = kendo.Class.extend({
|
|
init: function (rows, columns, grid) {
|
|
this.rows = rows;
|
|
this.columns = columns;
|
|
this._grid = grid;
|
|
this.headerHeight = rows.headerSize;
|
|
this.headerWidth = columns.headerSize;
|
|
this.hasRowHeader = columns.hasHeader;
|
|
this.hasColumnHeader = rows.hasHeader;
|
|
},
|
|
refresh: function (width, height) {
|
|
this.columns.viewSize(width);
|
|
this.rows.viewSize(height);
|
|
var x = this.columns.paneSegment();
|
|
var y = this.rows.paneSegment();
|
|
this.left = x.offset;
|
|
this.top = y.offset;
|
|
this.right = x.offset + x.length;
|
|
this.bottom = y.offset + y.length;
|
|
this.style = {
|
|
top: y.offset + 'px',
|
|
left: x.offset + 'px',
|
|
height: y.length + 'px',
|
|
width: x.length + 'px'
|
|
};
|
|
},
|
|
view: function (left, top) {
|
|
var rows = this.rows.visible(top);
|
|
var columns = this.columns.visible(left);
|
|
return {
|
|
rows: rows,
|
|
columns: columns,
|
|
rowOffset: rows.offset,
|
|
columnOffset: columns.offset,
|
|
mergedCellLeft: columns.start,
|
|
mergedCellTop: rows.start,
|
|
ref: new RangeRef(new CellRef(rows.values.start, columns.values.start), new CellRef(rows.values.end, columns.values.end))
|
|
};
|
|
},
|
|
contains: function (ref) {
|
|
return this.rows.contains(ref.topLeft.row, ref.bottomRight.row) && this.columns.contains(ref.topLeft.col, ref.bottomRight.col);
|
|
},
|
|
index: function (row, column) {
|
|
return this._grid.index(row, column);
|
|
},
|
|
boundingRectangle: function (ref) {
|
|
return this._grid.rectangle(ref);
|
|
},
|
|
cellRefIndex: function (ref) {
|
|
return this._grid.cellRefIndex(ref);
|
|
},
|
|
scrollBoundaries: function (cell) {
|
|
var position = this.boundingRectangle(cell);
|
|
var boundaries = {
|
|
top: Math.max(0, position.top - this.top + (this.hasColumnHeader ? 0 : this.headerHeight)),
|
|
left: Math.max(0, position.left - this.left + (this.hasRowHeader ? 0 : this.headerWidth)),
|
|
right: position.right - this.columns._viewSize + this.headerWidth,
|
|
bottom: position.bottom - this.rows._viewSize + this.headerHeight
|
|
};
|
|
var widthCompensation = this.columns.defaultValue / 2;
|
|
var heightCompensation = this.rows.defaultValue / 2;
|
|
boundaries.scrollTop = boundaries.top - heightCompensation;
|
|
boundaries.scrollBottom = boundaries.bottom + heightCompensation;
|
|
boundaries.scrollLeft = boundaries.left - widthCompensation;
|
|
boundaries.scrollRight = boundaries.right + widthCompensation;
|
|
return boundaries;
|
|
}
|
|
});
|
|
kendo.spreadsheet.Grid = Grid;
|
|
kendo.spreadsheet.PaneGrid = PaneGrid;
|
|
kendo.spreadsheet.Rectangle = Rectangle;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/axis', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var Axis = kendo.Class.extend({
|
|
init: function (count, value) {
|
|
this._value = value;
|
|
this._count = count;
|
|
this.values = new kendo.spreadsheet.RangeList(0, count - 1, value);
|
|
this._hidden = new kendo.spreadsheet.RangeList(0, count - 1, 0);
|
|
this.scrollBarSize = kendo.support.scrollbar();
|
|
this._refresh();
|
|
},
|
|
toJSON: function (field, positions) {
|
|
var values = [];
|
|
var iterator = this.values.iterator(0, this._count - 1);
|
|
for (var idx = 0; idx < this._count; idx++) {
|
|
var value = iterator.at(idx);
|
|
if (value === this._value) {
|
|
continue;
|
|
}
|
|
var position = positions[idx];
|
|
if (position === undefined) {
|
|
position = values.length;
|
|
var item = { index: idx };
|
|
item[field] = value;
|
|
values.push(item);
|
|
positions[idx] = position;
|
|
}
|
|
}
|
|
return values;
|
|
},
|
|
fromJSON: function (field, values) {
|
|
for (var idx = 0; idx < values.length; idx++) {
|
|
var value = values[idx][field];
|
|
var index = values[idx].index;
|
|
if (index === undefined) {
|
|
index = idx;
|
|
}
|
|
this.value(index, index, value);
|
|
}
|
|
},
|
|
hide: function (index) {
|
|
if (!this.hidden(index)) {
|
|
var value = this.value(index, index);
|
|
this._hidden.value(index, index, value);
|
|
this.value(index, index, 0);
|
|
}
|
|
},
|
|
hidden: function (index) {
|
|
return this._hidden.value(index, index) !== 0;
|
|
},
|
|
includesHidden: function (start, end) {
|
|
return this._hidden.intersecting(start, end).length > 1;
|
|
},
|
|
nextVisible: function (index, overflow) {
|
|
var end = this._count - 1;
|
|
if (index === end) {
|
|
return overflow ? index + 1 : index;
|
|
}
|
|
index += 1;
|
|
var range = this._hidden.intersecting(index, index)[0];
|
|
if (range.value !== 0) {
|
|
if (range.end === end) {
|
|
return index - 1;
|
|
} else {
|
|
return range.end + 1;
|
|
}
|
|
} else {
|
|
return index;
|
|
}
|
|
},
|
|
nextPage: function (index, pageSize) {
|
|
return this.index(this.sum(0, index - 1) + pageSize);
|
|
},
|
|
prevPage: function (index, pageSize) {
|
|
return this.index(this.sum(0, index) - pageSize);
|
|
},
|
|
firstVisible: function () {
|
|
var firstHidden = this._hidden.first();
|
|
if (firstHidden.value === 0) {
|
|
return 0;
|
|
} else {
|
|
return firstHidden.end + 1;
|
|
}
|
|
},
|
|
lastVisible: function () {
|
|
var lastHidden = this._hidden.last();
|
|
if (lastHidden.value === 0) {
|
|
return this._count - 1;
|
|
} else {
|
|
return lastHidden.start - 1;
|
|
}
|
|
},
|
|
prevVisible: function (index, overflow) {
|
|
if (index === 0) {
|
|
return overflow ? -1 : 0;
|
|
}
|
|
index -= 1;
|
|
var range = this._hidden.intersecting(index, index)[0];
|
|
if (range.value !== 0) {
|
|
if (range.start === 0) {
|
|
return index + 1;
|
|
} else {
|
|
return range.start - 1;
|
|
}
|
|
} else {
|
|
return index;
|
|
}
|
|
},
|
|
unhide: function (index) {
|
|
if (this.hidden(index)) {
|
|
var value = this._hidden.value(index, index);
|
|
this._hidden.value(index, index, 0);
|
|
this.value(index, index, value);
|
|
}
|
|
},
|
|
value: function (start, end, value) {
|
|
if (value !== undefined) {
|
|
this.values.value(start, end, value);
|
|
this._refresh();
|
|
} else {
|
|
return this.values.iterator(start, end).at(0);
|
|
}
|
|
},
|
|
sum: function (start, end) {
|
|
var values = this.values.iterator(start, end);
|
|
var sum = 0;
|
|
for (var idx = start; idx <= end; idx++) {
|
|
sum += values.at(idx);
|
|
}
|
|
return sum;
|
|
},
|
|
visible: function (start, end) {
|
|
var startSegment = null;
|
|
var endSegment = null;
|
|
var lastPage = false;
|
|
if (end >= this.total + this.scrollBarSize) {
|
|
lastPage = true;
|
|
}
|
|
var ranges = this._pixelValues.intersecting(start, end);
|
|
startSegment = ranges[0];
|
|
endSegment = ranges[ranges.length - 1];
|
|
if (!startSegment) {
|
|
return {
|
|
values: this.values.iterator(0, 0),
|
|
offset: 0
|
|
};
|
|
}
|
|
var startOffset = start - startSegment.start;
|
|
var startIndex = (startOffset / startSegment.value.value >> 0) + startSegment.value.start;
|
|
var offset = startOffset - (startIndex - startSegment.value.start) * startSegment.value.value;
|
|
var endOffset = end - endSegment.start;
|
|
var endIndex = (endOffset / endSegment.value.value >> 0) + endSegment.value.start;
|
|
if (endIndex > endSegment.value.end) {
|
|
endIndex = endSegment.value.end;
|
|
}
|
|
if (lastPage) {
|
|
offset += endSegment.value.value - (endOffset - (endIndex - endSegment.value.start) * endSegment.value.value);
|
|
}
|
|
offset = Math.min(-offset, 0);
|
|
return {
|
|
values: this.values.iterator(startIndex, endIndex),
|
|
offset: offset
|
|
};
|
|
},
|
|
index: function (value) {
|
|
var index = 0;
|
|
var iterator = this.values.iterator(0, this._count - 1);
|
|
var current = iterator.at(0);
|
|
while (current < value && index < this._count - 1) {
|
|
current += iterator.at(++index);
|
|
}
|
|
return index;
|
|
},
|
|
_refresh: function () {
|
|
var current = 0;
|
|
this._pixelValues = this.values.map(function (range) {
|
|
var start = current;
|
|
current += (range.end - range.start + 1) * range.value;
|
|
var end = current - 1;
|
|
return new kendo.spreadsheet.ValueRange(start, end, range);
|
|
});
|
|
this.total = current;
|
|
},
|
|
getState: function () {
|
|
return {
|
|
values: this.values.getState(),
|
|
hidden: this._hidden.getState()
|
|
};
|
|
},
|
|
setState: function (state) {
|
|
this.values.setState(state.values);
|
|
this._hidden.setState(state.hidden);
|
|
this._refresh();
|
|
}
|
|
});
|
|
var PaneAxis = kendo.Class.extend({
|
|
init: function (axis, start, count, headerSize) {
|
|
this._axis = axis;
|
|
this._start = start;
|
|
this._count = count;
|
|
this.hasHeader = start === 0;
|
|
this.headerSize = headerSize;
|
|
this.defaultValue = axis._value;
|
|
this.frozen = count > 0;
|
|
},
|
|
viewSize: function (viewSize) {
|
|
this._viewSize = viewSize;
|
|
},
|
|
sum: function (start, end) {
|
|
return this._axis.sum(start, end - 1);
|
|
},
|
|
start: function () {
|
|
return this.sum(0, this._start);
|
|
},
|
|
size: function () {
|
|
return this.sum(this._start, this._start + this._count);
|
|
},
|
|
index: function (value, offset) {
|
|
return this._axis.index(value + (this.frozen ? 0 : offset) - this.headerSize);
|
|
},
|
|
paneSegment: function () {
|
|
var offset = this.start();
|
|
var length;
|
|
if (!this.hasHeader) {
|
|
offset += this.headerSize;
|
|
}
|
|
if (this.frozen) {
|
|
length = this.size();
|
|
if (this.hasHeader) {
|
|
length += this.headerSize;
|
|
} else {
|
|
length -= this.headerSize;
|
|
}
|
|
} else {
|
|
length = this._viewSize - offset;
|
|
}
|
|
return {
|
|
offset: offset,
|
|
length: length
|
|
};
|
|
},
|
|
visible: function (offset) {
|
|
var start = this.start();
|
|
var size;
|
|
if (this.frozen) {
|
|
size = this.size();
|
|
if (!this.hasHeader) {
|
|
size -= this.headerSize;
|
|
}
|
|
} else {
|
|
size = this._viewSize - start - this.headerSize;
|
|
start += offset;
|
|
}
|
|
var result = this._axis.visible(start, start + size - 1);
|
|
if (this.frozen) {
|
|
result.offset = 0;
|
|
}
|
|
result.start = start;
|
|
if (this.hasHeader) {
|
|
result.offset += this.headerSize;
|
|
result.start -= this.headerSize;
|
|
}
|
|
return result;
|
|
},
|
|
contains: function (start, end) {
|
|
if (this.frozen) {
|
|
if (start > this._start + this._count) {
|
|
return false;
|
|
}
|
|
if (end < this._start) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
return end >= this._start;
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.Axis = Axis;
|
|
kendo.spreadsheet.PaneAxis = PaneAxis;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/filter', [
|
|
'kendo.core',
|
|
'kendo.data'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var Filter = kendo.spreadsheet.Filter = kendo.Class.extend({
|
|
prepare: function () {
|
|
},
|
|
value: function (cell) {
|
|
return cell.value;
|
|
},
|
|
matches: function () {
|
|
throw new Error('The \'matches\' method is not implemented.');
|
|
},
|
|
toJSON: function () {
|
|
throw new Error('The \'toJSON\' method is not implemented.');
|
|
}
|
|
});
|
|
Filter.create = function (options) {
|
|
var filter = options.filter;
|
|
if (!filter) {
|
|
throw new Error('Filter type not specified.');
|
|
}
|
|
var constructor = kendo.spreadsheet[filter.charAt(0).toUpperCase() + filter.substring(1) + 'Filter'];
|
|
if (!constructor) {
|
|
throw new Error('Filter type not recognized.');
|
|
}
|
|
return new constructor(options);
|
|
};
|
|
kendo.spreadsheet.ValueFilter = Filter.extend({
|
|
_values: [],
|
|
_dates: [],
|
|
_blanks: false,
|
|
init: function (options) {
|
|
if (options.values !== undefined) {
|
|
this._values = options.values;
|
|
}
|
|
if (options.blanks !== undefined) {
|
|
this._blanks = options.blanks;
|
|
}
|
|
if (options.dates !== undefined) {
|
|
this._dates = options.dates;
|
|
}
|
|
},
|
|
value: function (cell) {
|
|
var value = cell.value;
|
|
if (this._dates.length > 0 && cell.format && typeof value === 'number') {
|
|
var type = kendo.spreadsheet.formatting.type(value, cell.format);
|
|
if (type === 'date') {
|
|
value = kendo.spreadsheet.numberToDate(value);
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
matches: function (value) {
|
|
if (value === null || value === undefined) {
|
|
return this._blanks;
|
|
}
|
|
if (value instanceof Date) {
|
|
return this._dates.some(function (date) {
|
|
return date.year === value.getFullYear() && (date.month === undefined || date.month === value.getMonth()) && (date.day === undefined || date.day === value.getDate()) && (date.hours === undefined || date.hours === value.getHours()) && (date.minutes === undefined || date.minutes === value.getMinutes()) && (date.seconds === undefined || date.seconds === value.getSeconds());
|
|
});
|
|
}
|
|
return this._values.indexOf(value) >= 0;
|
|
},
|
|
toJSON: function () {
|
|
return {
|
|
filter: 'value',
|
|
values: this._values.slice(0)
|
|
};
|
|
}
|
|
});
|
|
kendo.spreadsheet.CustomFilter = Filter.extend({
|
|
_logic: 'and',
|
|
init: function (options) {
|
|
if (options.logic !== undefined) {
|
|
this._logic = options.logic;
|
|
}
|
|
if (options.criteria === undefined) {
|
|
throw new Error('Must specify criteria.');
|
|
}
|
|
this._criteria = options.criteria;
|
|
var expression = kendo.data.Query.filterExpr({
|
|
logic: this._logic,
|
|
filters: this._criteria
|
|
}).expression;
|
|
this._matches = new Function('d', 'return ' + expression);
|
|
},
|
|
matches: function (value) {
|
|
if (value === null) {
|
|
return false;
|
|
}
|
|
return this._matches(value);
|
|
},
|
|
value: function (cell) {
|
|
var value = cell.value;
|
|
var criterionValue = this._criteria[0].value;
|
|
var criterionType = criterionValue instanceof Date ? 'date' : typeof criterionValue;
|
|
var valueType = typeof value;
|
|
if (cell.format) {
|
|
valueType = kendo.spreadsheet.formatting.type(value, cell.format);
|
|
}
|
|
if (valueType != criterionType) {
|
|
if (criterionType == 'string') {
|
|
if (cell.format) {
|
|
value = kendo.spreadsheet.formatting.text(value, cell.format);
|
|
}
|
|
value = value + '';
|
|
}
|
|
} else if (valueType == 'date') {
|
|
value = kendo.spreadsheet.numberToDate(value);
|
|
}
|
|
return value;
|
|
},
|
|
toJSON: function () {
|
|
return {
|
|
filter: 'custom',
|
|
logic: this._logic,
|
|
criteria: this._criteria
|
|
};
|
|
}
|
|
});
|
|
kendo.spreadsheet.TopFilter = Filter.extend({
|
|
init: function (options) {
|
|
this._type = options.type;
|
|
this._value = options.value;
|
|
this._values = [];
|
|
},
|
|
prepare: function (cells) {
|
|
var values = cells.map(this.value).sort().filter(function (value, index, array) {
|
|
return index === 0 || value !== array[index - 1];
|
|
});
|
|
if (this._type === 'topNumber' || this._type == 'topPercent') {
|
|
values.sort(function (x, y) {
|
|
return y - x;
|
|
});
|
|
} else {
|
|
values.sort(function (x, y) {
|
|
return x - y;
|
|
});
|
|
}
|
|
var count = this._value;
|
|
if (this._type === 'topPercent' || this._type === 'bottomPercent') {
|
|
count = values.length * count / 100 >> 0;
|
|
}
|
|
this._values = values.slice(0, count);
|
|
},
|
|
matches: function (value) {
|
|
return this._values.indexOf(value) >= 0;
|
|
},
|
|
toJSON: function () {
|
|
return {
|
|
filter: 'top',
|
|
type: this._type,
|
|
value: this._value
|
|
};
|
|
}
|
|
});
|
|
kendo.spreadsheet.DynamicFilter = Filter.extend({
|
|
init: function (options) {
|
|
this._type = options.type;
|
|
this._predicate = this[options.type];
|
|
if (typeof this._predicate !== 'function') {
|
|
throw new Error('DynamicFilter type \'' + options.type + '\' not recognized.');
|
|
}
|
|
},
|
|
value: function (cell) {
|
|
var value = cell.value;
|
|
if (cell.format) {
|
|
var type = kendo.spreadsheet.formatting.type(value, cell.format);
|
|
if (type === 'date') {
|
|
value = kendo.spreadsheet.numberToDate(value);
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
prepare: function (cells) {
|
|
var sum = 0;
|
|
var count = 0;
|
|
for (var ci = 0; ci < cells.length; ci++) {
|
|
var value = this.value(cells[ci]);
|
|
if (typeof value === 'number') {
|
|
sum += value;
|
|
count++;
|
|
}
|
|
}
|
|
if (count > 0) {
|
|
this._average = sum / count;
|
|
} else {
|
|
this._average = 0;
|
|
}
|
|
},
|
|
matches: function (value) {
|
|
return this._predicate(value);
|
|
},
|
|
aboveAverage: function (value) {
|
|
if (value instanceof Date) {
|
|
value = kendo.spreadsheet.dateToNumber(value);
|
|
}
|
|
if (typeof value !== 'number') {
|
|
return false;
|
|
}
|
|
return value > this._average;
|
|
},
|
|
belowAverage: function (value) {
|
|
if (value instanceof Date) {
|
|
value = kendo.spreadsheet.dateToNumber(value);
|
|
}
|
|
if (typeof value !== 'number') {
|
|
return false;
|
|
}
|
|
return value < this._average;
|
|
},
|
|
tomorrow: function (value) {
|
|
if (value instanceof Date) {
|
|
var tomorrow = kendo.date.addDays(kendo.date.today(), 1);
|
|
return kendo.date.getDate(value).getTime() === tomorrow.getTime();
|
|
}
|
|
return false;
|
|
},
|
|
today: function (value) {
|
|
if (value instanceof Date) {
|
|
return kendo.date.isToday(value);
|
|
}
|
|
return false;
|
|
},
|
|
yesterday: function (value) {
|
|
if (value instanceof Date) {
|
|
var yesterday = kendo.date.addDays(kendo.date.today(), -1);
|
|
return kendo.date.getDate(value).getTime() === yesterday.getTime();
|
|
}
|
|
return false;
|
|
},
|
|
nextWeek: function (value) {
|
|
return sameWeek(kendo.date.addDays(kendo.date.today(), 7), value);
|
|
},
|
|
thisWeek: function (value) {
|
|
return sameWeek(kendo.date.today(), value);
|
|
},
|
|
lastWeek: function (value) {
|
|
return sameWeek(kendo.date.addDays(kendo.date.today(), -7), value);
|
|
},
|
|
nextMonth: function (value) {
|
|
return sameMonth(value, 1);
|
|
},
|
|
thisMonth: function (value) {
|
|
return sameMonth(value, 0);
|
|
},
|
|
lastMonth: function (value) {
|
|
return sameMonth(value, -1);
|
|
},
|
|
nextQuarter: function (value) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.today();
|
|
var diff = quarter(value) - quarter(today);
|
|
return diff === 1 && today.getFullYear() === value.getFullYear() || diff == -3 && today.getFullYear() + 1 === value.getFullYear();
|
|
}
|
|
return false;
|
|
},
|
|
thisQuarter: function (value) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.today();
|
|
var diff = quarter(value) - quarter(today);
|
|
return diff === 0 && today.getFullYear() === value.getFullYear();
|
|
}
|
|
return false;
|
|
},
|
|
lastQuarter: function (value) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.today();
|
|
var diff = quarter(today) - quarter(value);
|
|
return diff === 1 && today.getFullYear() === value.getFullYear() || diff == -3 && today.getFullYear() - 1 === value.getFullYear();
|
|
}
|
|
return false;
|
|
},
|
|
nextYear: function (value) {
|
|
return sameYear(value, 1);
|
|
},
|
|
thisYear: function (value) {
|
|
return sameYear(value, 0);
|
|
},
|
|
lastYear: function (value) {
|
|
return sameYear(value, -1);
|
|
},
|
|
yearToDate: function (value) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.today();
|
|
return value.getFullYear() === today.getFullYear() && value <= today;
|
|
}
|
|
return false;
|
|
},
|
|
toJSON: function () {
|
|
return {
|
|
filter: 'dynamic',
|
|
type: this._type
|
|
};
|
|
}
|
|
});
|
|
[
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
].forEach(function (target) {
|
|
kendo.spreadsheet.DynamicFilter.prototype['quarter' + target] = function (value) {
|
|
if (value instanceof Date) {
|
|
return quarter(value) === target;
|
|
}
|
|
return false;
|
|
};
|
|
});
|
|
kendo.cultures['en-US'].calendar.months.names.forEach(function (month, index) {
|
|
kendo.spreadsheet.DynamicFilter.prototype[month.toLowerCase()] = function (value) {
|
|
if (value instanceof Date) {
|
|
return value.getMonth() === index;
|
|
}
|
|
return false;
|
|
};
|
|
});
|
|
function quarter(value) {
|
|
var month = value.getMonth() + 1;
|
|
if (month >= 1 && month <= 3) {
|
|
return 1;
|
|
} else if (month >= 4 && month <= 6) {
|
|
return 2;
|
|
} else if (month >= 7 && month <= 9) {
|
|
return 3;
|
|
} else {
|
|
return 4;
|
|
}
|
|
}
|
|
function sameYear(value, offset) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.today();
|
|
today.setFullYear(today.getFullYear() + offset);
|
|
return today.getFullYear() === value.getFullYear();
|
|
}
|
|
return false;
|
|
}
|
|
function sameMonth(value, offset) {
|
|
if (value instanceof Date) {
|
|
var today = kendo.date.firstDayOfMonth(kendo.date.today());
|
|
today.setMonth(today.getMonth() + offset, 1);
|
|
return today.getTime() === kendo.date.firstDayOfMonth(value).getTime();
|
|
}
|
|
return false;
|
|
}
|
|
function sameWeek(a, b) {
|
|
if (b instanceof Date) {
|
|
var firstWeek = kendo.date.dayOfWeek(kendo.date.getDate(a), 1);
|
|
var secondWeek = kendo.date.dayOfWeek(kendo.date.getDate(b), 1);
|
|
return firstWeek.getTime() === secondWeek.getTime();
|
|
}
|
|
return false;
|
|
}
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/sorter', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var Sorter = kendo.Class.extend({
|
|
init: function (grid, lists) {
|
|
this._grid = grid;
|
|
this._lists = lists;
|
|
},
|
|
indices: function (rangeRef, list, ascending, indices) {
|
|
var comparer = Sorter.ascendingComparer;
|
|
if (ascending === false) {
|
|
comparer = Sorter.descendingComparer;
|
|
}
|
|
return list.sortedIndices(this._grid.cellRefIndex(rangeRef.topLeft), this._grid.cellRefIndex(rangeRef.bottomRight), comparer, indices);
|
|
},
|
|
sortBy: function (ref, column, list, ascending, indices) {
|
|
var sortedIndices = this.indices(ref.toColumn(column), list, ascending, indices);
|
|
for (var ci = ref.topLeft.col; ci <= ref.bottomRight.col; ci++) {
|
|
var start = this._grid.index(ref.topLeft.row, ci);
|
|
var end = this._grid.index(ref.bottomRight.row, ci);
|
|
for (var li = 0; li < this._lists.length; li++) {
|
|
if (start < this._lists[li].lastRangeStart()) {
|
|
this._lists[li].sort(start, end, sortedIndices);
|
|
}
|
|
}
|
|
}
|
|
return sortedIndices;
|
|
}
|
|
});
|
|
Sorter.ascendingComparer = function (a, b) {
|
|
if (a === null && b === null) {
|
|
return 0;
|
|
}
|
|
if (a === null) {
|
|
return 1;
|
|
}
|
|
if (b === null) {
|
|
return -1;
|
|
}
|
|
var typeA = typeof a;
|
|
var typeB = typeof b;
|
|
if (typeA === 'number') {
|
|
if (typeB === 'number') {
|
|
return a - b;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
if (typeA === 'string') {
|
|
switch (typeB) {
|
|
case 'number':
|
|
return 1;
|
|
case 'string':
|
|
return a.localeCompare(b);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
if (typeA === 'boolean') {
|
|
switch (typeB) {
|
|
case 'number':
|
|
return 1;
|
|
case 'string':
|
|
return 1;
|
|
case 'boolean':
|
|
return a - b;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
if (a instanceof kendo.spreadsheet.calc.runtime.CalcError) {
|
|
if (b instanceof kendo.spreadsheet.calc.runtime.CalcError) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
throw new Error('Cannot compare ' + a + ' and ' + b);
|
|
};
|
|
Sorter.descendingComparer = function (a, b) {
|
|
if (a === null && b === null) {
|
|
return 0;
|
|
}
|
|
if (a === null) {
|
|
return 1;
|
|
}
|
|
if (b === null) {
|
|
return -1;
|
|
}
|
|
return Sorter.ascendingComparer(b, a);
|
|
};
|
|
kendo.spreadsheet.Sorter = Sorter;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/numformat', [
|
|
'spreadsheet/calc',
|
|
'kendo.dom'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var calc = kendo.spreadsheet.calc;
|
|
var dom = kendo.dom;
|
|
var RX_COLORS = /^\[(black|green|white|blue|magenta|yellow|cyan|red)\]/i;
|
|
var RX_CONDITION = /^\[(<=|>=|<>|<|>|=)(-?[0-9.]+)\]/;
|
|
function parse(input) {
|
|
input = calc.InputStream(input);
|
|
var sections = [], haveConditional = false, decimalPart;
|
|
while (!input.eof()) {
|
|
var sec = readSection();
|
|
sections.push(sec);
|
|
if (sec.cond) {
|
|
haveConditional = true;
|
|
}
|
|
}
|
|
function addPlainText() {
|
|
sections.push({
|
|
cond: 'text',
|
|
body: [{ type: 'text' }]
|
|
});
|
|
}
|
|
if (haveConditional) {
|
|
addPlainText();
|
|
} else if (sections.length == 1) {
|
|
sections[0].cond = 'num';
|
|
addPlainText();
|
|
} else if (sections.length == 2) {
|
|
sections[0].cond = {
|
|
op: '>=',
|
|
value: 0
|
|
};
|
|
sections[1].cond = {
|
|
op: '<',
|
|
value: 0
|
|
};
|
|
addPlainText();
|
|
} else if (sections.length >= 3) {
|
|
sections[0].cond = {
|
|
op: '>',
|
|
value: 0
|
|
};
|
|
sections[1].cond = {
|
|
op: '<',
|
|
value: 0
|
|
};
|
|
sections[2].cond = {
|
|
op: '=',
|
|
value: 0
|
|
};
|
|
addPlainText();
|
|
if (sections.length > 3) {
|
|
sections[3].cond = 'text';
|
|
sections = sections.slice(0, 4);
|
|
}
|
|
}
|
|
return sections;
|
|
function maybeColor() {
|
|
var m = input.skip(RX_COLORS);
|
|
if (m) {
|
|
return m[1].toLowerCase();
|
|
}
|
|
}
|
|
function maybeCondition() {
|
|
var m = input.skip(RX_CONDITION);
|
|
if (m) {
|
|
var val = parseFloat(m[2]);
|
|
if (!isNaN(val)) {
|
|
return {
|
|
op: m[1],
|
|
value: val
|
|
};
|
|
}
|
|
}
|
|
}
|
|
function readFormat() {
|
|
var format = [], tok, prev = null;
|
|
while (!input.eof() && (tok = readNext())) {
|
|
if (tok.type == 'date') {
|
|
if (prev && /^(el)?time$/.test(prev.type) && prev.part == 'h' && tok.part == 'm' && tok.format < 3) {
|
|
tok.type = 'time';
|
|
}
|
|
} else if (/^(el)?time$/.test(tok.type) && tok.part == 's') {
|
|
if (prev && prev.type == 'date' && prev.part == 'm' && prev.format < 3) {
|
|
prev.type = 'time';
|
|
}
|
|
}
|
|
if (!/^(?:str|space|fill)$/.test(tok.type)) {
|
|
prev = tok;
|
|
}
|
|
format.push(tok);
|
|
}
|
|
return format;
|
|
}
|
|
function maybeFraction(tok) {
|
|
if (tok.type != 'date' || tok.part == 'm' && tok.format < 3) {
|
|
var m = input.skip(/^\.(0+)/);
|
|
if (m) {
|
|
tok.fraction = m[1].length;
|
|
if (tok.type == 'date') {
|
|
tok.type = 'time';
|
|
}
|
|
}
|
|
}
|
|
return tok;
|
|
}
|
|
function readNext() {
|
|
var ch, m;
|
|
if (m = input.skip(/^([#0?]+),([#0?]+)/)) {
|
|
return {
|
|
type: 'digit',
|
|
sep: true,
|
|
format: m[1] + m[2],
|
|
decimal: decimalPart
|
|
};
|
|
}
|
|
if (m = input.skip(/^[#0?]+/)) {
|
|
return {
|
|
type: 'digit',
|
|
sep: false,
|
|
format: m[0],
|
|
decimal: decimalPart
|
|
};
|
|
}
|
|
if (m = input.skip(/^(e)([+-])/i)) {
|
|
return {
|
|
type: 'exp',
|
|
ch: m[1],
|
|
sign: m[2]
|
|
};
|
|
}
|
|
if (m = input.skip(/^(d{1,4}|m{1,5}|yyyy|yy)/i)) {
|
|
m = m[1].toLowerCase();
|
|
return maybeFraction({
|
|
type: 'date',
|
|
part: m.charAt(0),
|
|
format: m.length
|
|
});
|
|
}
|
|
if (m = input.skip(/^(hh?|ss?)/i)) {
|
|
m = m[1].toLowerCase();
|
|
return maybeFraction({
|
|
type: 'time',
|
|
part: m.charAt(0),
|
|
format: m.length
|
|
});
|
|
}
|
|
if (m = input.skip(/^\[(hh?|mm?|ss?)\]/i)) {
|
|
m = m[1].toLowerCase();
|
|
return maybeFraction({
|
|
type: 'eltime',
|
|
part: m.charAt(0),
|
|
format: m.length
|
|
});
|
|
}
|
|
if (m = input.skip(/^(am\/pm|a\/p)/i)) {
|
|
m = m[1].split('/');
|
|
return {
|
|
type: 'ampm',
|
|
am: m[0],
|
|
pm: m[1]
|
|
};
|
|
}
|
|
switch (ch = input.next()) {
|
|
case ';':
|
|
return null;
|
|
case '\\':
|
|
return {
|
|
type: 'str',
|
|
value: input.next()
|
|
};
|
|
case '"':
|
|
return {
|
|
type: 'str',
|
|
value: input.readEscaped(ch)
|
|
};
|
|
case '@':
|
|
return { type: 'text' };
|
|
case '_':
|
|
return {
|
|
type: 'space',
|
|
value: input.next()
|
|
};
|
|
case '*':
|
|
return {
|
|
type: 'fill',
|
|
value: input.next()
|
|
};
|
|
case '.':
|
|
if (input.lookingAt(/^\s*[#0?]/)) {
|
|
decimalPart = true;
|
|
return { type: 'dec' };
|
|
}
|
|
return {
|
|
type: 'str',
|
|
value: '.'
|
|
};
|
|
case '%':
|
|
return { type: 'percent' };
|
|
case ',':
|
|
return { type: 'comma' };
|
|
}
|
|
return {
|
|
type: 'str',
|
|
value: ch
|
|
};
|
|
}
|
|
function readSection() {
|
|
decimalPart = false;
|
|
var color = maybeColor(), cond = maybeCondition();
|
|
if (!color && cond) {
|
|
color = maybeColor();
|
|
}
|
|
return {
|
|
color: color,
|
|
cond: cond,
|
|
body: readFormat()
|
|
};
|
|
}
|
|
}
|
|
function print(sections) {
|
|
return sections.map(printSection).join(';');
|
|
function printSection(sec) {
|
|
var out = '';
|
|
if (sec.color) {
|
|
out += '[' + sec.color + ']';
|
|
}
|
|
if (sec.cond) {
|
|
if (!(sec.cond == 'text' || sec.cond == 'num')) {
|
|
out += '[' + sec.cond.op + sec.cond.value + ']';
|
|
}
|
|
}
|
|
out += sec.body.map(printToken).join('');
|
|
return out;
|
|
}
|
|
function maybeFraction(fmt, tok) {
|
|
if (tok.fraction) {
|
|
fmt += '.' + padLeft('', tok.fraction, '0');
|
|
}
|
|
return fmt;
|
|
}
|
|
function printToken(tok) {
|
|
if (tok.type == 'digit') {
|
|
if (tok.sep) {
|
|
return tok.format.charAt(0) + ',' + tok.format.substr(1);
|
|
} else {
|
|
return tok.format;
|
|
}
|
|
} else if (tok.type == 'exp') {
|
|
return tok.ch + tok.sign;
|
|
} else if (tok.type == 'date' || tok.type == 'time') {
|
|
return maybeFraction(padLeft('', tok.format, tok.part), tok);
|
|
} else if (tok.type == 'eltime') {
|
|
return maybeFraction('[' + padLeft('', tok.format, tok.part) + ']', tok);
|
|
} else if (tok.type == 'ampm') {
|
|
return tok.am + '/' + tok.pm;
|
|
} else if (tok.type == 'str') {
|
|
return JSON.stringify(tok.value);
|
|
} else if (tok.type == 'text') {
|
|
return '@';
|
|
} else if (tok.type == 'space') {
|
|
return '_' + tok.value;
|
|
} else if (tok.type == 'fill') {
|
|
return '*' + tok.value;
|
|
} else if (tok.type == 'dec') {
|
|
return '.';
|
|
} else if (tok.type == 'percent') {
|
|
return '%';
|
|
} else if (tok.type == 'comma') {
|
|
return ',';
|
|
}
|
|
}
|
|
}
|
|
function adjustDecimals(sections, x) {
|
|
sections.forEach(function (sec) {
|
|
var diff = x;
|
|
if (sec.cond == 'text') {
|
|
return;
|
|
}
|
|
var body = sec.body, adjusted = false, i = body.length;
|
|
while (diff !== 0 && --i >= 0) {
|
|
var tok = body[i];
|
|
if (tok.type == 'digit') {
|
|
if (tok.decimal) {
|
|
adjusted = true;
|
|
if (diff > 0) {
|
|
tok.format += padLeft('', diff, '0');
|
|
} else if (diff < 0) {
|
|
var tmp = tok.format.length;
|
|
tok.format = tok.format.substr(0, tmp + diff);
|
|
diff += tmp - tok.format.length;
|
|
}
|
|
if (tok.format.length === 0) {
|
|
body.splice(i, 1);
|
|
while (--i >= 0) {
|
|
tok = body[i];
|
|
if (tok.type == 'digit' && tok.decimal) {
|
|
++i;
|
|
break;
|
|
}
|
|
if (tok.type == 'dec') {
|
|
body.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (diff > 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!adjusted && diff > 0) {
|
|
body.splice(i + 1, 0, { type: 'dec' }, {
|
|
type: 'digit',
|
|
sep: false,
|
|
decimal: true,
|
|
format: padLeft('', diff, '0')
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function TokenStream(parts) {
|
|
var index = 0;
|
|
return {
|
|
next: function () {
|
|
return parts[index++];
|
|
},
|
|
eof: function () {
|
|
return index >= parts.length;
|
|
},
|
|
ahead: function (n, f) {
|
|
if (index + n <= parts.length) {
|
|
var val = f.apply(null, parts.slice(index, index + n));
|
|
if (val) {
|
|
index += n;
|
|
}
|
|
return val;
|
|
}
|
|
},
|
|
restart: function () {
|
|
index = 0;
|
|
}
|
|
};
|
|
}
|
|
function compileFormatPart(format) {
|
|
var input = TokenStream(format.body);
|
|
var hasDate = false;
|
|
var hasTime = false;
|
|
var hasAmpm = false;
|
|
var percentCount = 0;
|
|
var scaleCount = 0;
|
|
var code = '';
|
|
var separeThousands = false;
|
|
var declen = 0;
|
|
var intFormat = [], decFormat = [];
|
|
var condition = format.cond;
|
|
var preamble = '';
|
|
if (condition == 'text') {
|
|
preamble = 'if (typeof value == \'string\' || value instanceof kendo.spreadsheet.CalcError) { ';
|
|
} else if (condition == 'num') {
|
|
preamble = 'if (typeof value == \'number\') { ';
|
|
} else if (condition) {
|
|
var op = condition.op == '=' ? '==' : condition.op;
|
|
preamble = 'if (typeof value == \'number\' && value ' + op + ' ' + condition.value + ') { ';
|
|
code += 'value = Math.abs(value); ';
|
|
}
|
|
if (format.color) {
|
|
code += 'result.color = ' + JSON.stringify(format.color) + '; ';
|
|
}
|
|
function checkComma(a, b) {
|
|
if (a.type == 'digit' && b.type == 'comma' || a.type == 'comma' && a.hidden && b.type == 'comma') {
|
|
b.hidden = true;
|
|
scaleCount++;
|
|
}
|
|
}
|
|
while (!input.eof()) {
|
|
input.ahead(2, checkComma);
|
|
var tok = input.next();
|
|
if (tok.type == 'percent') {
|
|
percentCount++;
|
|
} else if (tok.type == 'digit') {
|
|
if (tok.decimal) {
|
|
declen += tok.format.length;
|
|
decFormat.push(tok.format);
|
|
} else {
|
|
intFormat.push(tok.format);
|
|
if (tok.sep) {
|
|
separeThousands = true;
|
|
}
|
|
}
|
|
} else if (tok.type == 'time') {
|
|
hasTime = true;
|
|
} else if (tok.type == 'date') {
|
|
hasDate = true;
|
|
} else if (tok.type == 'ampm') {
|
|
hasAmpm = hasTime = true;
|
|
}
|
|
}
|
|
if (percentCount > 0) {
|
|
code += 'value *= ' + Math.pow(100, percentCount) + '; ';
|
|
}
|
|
if (scaleCount > 0) {
|
|
code += 'value /= ' + Math.pow(1000, scaleCount) + '; ';
|
|
}
|
|
if (intFormat.length) {
|
|
code += 'var intPart = runtime.formatInt(culture, value, ' + JSON.stringify(intFormat) + ', ' + declen + ', ' + separeThousands + '); ';
|
|
}
|
|
if (decFormat.length) {
|
|
code += 'var decPart = runtime.formatDec(value, ' + JSON.stringify(decFormat) + ', ' + declen + '); ';
|
|
}
|
|
if (intFormat.length || decFormat.length) {
|
|
code += 'type = \'number\'; ';
|
|
}
|
|
if (hasDate) {
|
|
code += 'var date = runtime.unpackDate(value); ';
|
|
}
|
|
if (hasTime) {
|
|
code += 'var time = runtime.unpackTime(value); ';
|
|
}
|
|
if (hasDate || hasTime) {
|
|
code += 'type = \'date\'; ';
|
|
}
|
|
if (percentCount > 0 || scaleCount > 0 || intFormat.length || decFormat.length || hasDate || hasTime) {
|
|
if (!preamble) {
|
|
preamble = 'if (typeof value == \'number\') { ';
|
|
}
|
|
}
|
|
input.restart();
|
|
while (!input.eof()) {
|
|
var tok = input.next();
|
|
if (tok.type == 'dec') {
|
|
code += 'output += culture.numberFormat[\'.\']; ';
|
|
} else if (tok.type == 'comma' && !tok.hidden) {
|
|
code += 'output += \',\'; ';
|
|
} else if (tok.type == 'percent') {
|
|
code += 'type = \'percent\'; ';
|
|
code += 'output += culture.numberFormat.percent.symbol; ';
|
|
} else if (tok.type == 'str') {
|
|
code += 'output += ' + JSON.stringify(tok.value) + '; ';
|
|
} else if (tok.type == 'text') {
|
|
code += 'type = \'text\'; ';
|
|
code += 'output += value; ';
|
|
} else if (tok.type == 'space') {
|
|
code += 'if (output) result.body.push(output); ';
|
|
code += 'output = \'\'; ';
|
|
code += 'result.body.push({ type: \'space\', value: ' + JSON.stringify(tok.value) + ' }); ';
|
|
} else if (tok.type == 'fill') {
|
|
code += 'output += runtime.fill(' + JSON.stringify(tok.value) + '); ';
|
|
} else if (tok.type == 'digit') {
|
|
code += 'output += ' + (tok.decimal ? 'decPart' : 'intPart') + '.shift(); ';
|
|
} else if (tok.type == 'date') {
|
|
code += 'output += runtime.date(culture, date, ' + JSON.stringify(tok.part) + ', ' + tok.format + '); ';
|
|
} else if (tok.type == 'time') {
|
|
code += 'output += runtime.time(time, ' + JSON.stringify(tok.part) + ', ' + tok.format + ', ' + hasAmpm + ', ' + tok.fraction + '); ';
|
|
} else if (tok.type == 'eltime') {
|
|
code += 'output += runtime.eltime(value, ' + JSON.stringify(tok.part) + ', ' + tok.format + ', ' + tok.fraction + '); ';
|
|
} else if (tok.type == 'ampm') {
|
|
code += 'output += time.hours < 12 ? ' + JSON.stringify(tok.am) + ' : ' + JSON.stringify(tok.pm) + '; ';
|
|
}
|
|
}
|
|
code += 'if (output) result.body.push(output); ';
|
|
code += 'result.type = type; ';
|
|
code += 'return result; ';
|
|
if (preamble) {
|
|
code = preamble + code + '}';
|
|
}
|
|
return code;
|
|
}
|
|
var CACHE = Object.create(null);
|
|
function compile(format) {
|
|
var f = CACHE[format];
|
|
if (!f) {
|
|
var tree = parse(format);
|
|
var code = tree.map(compileFormatPart).join('\n');
|
|
code = '\'use strict\'; return function(value, culture){ ' + 'if (!culture) culture = kendo.culture(); ' + 'var output = \'\', type = null, result = { body: [] }; ' + code + '; return result; };';
|
|
f = CACHE[format] = new Function('runtime', code)(runtime);
|
|
}
|
|
return f;
|
|
}
|
|
var runtime = {
|
|
unpackDate: calc.runtime.unpackDate,
|
|
unpackTime: calc.runtime.unpackTime,
|
|
date: function (culture, d, part, length) {
|
|
switch (part) {
|
|
case 'd':
|
|
switch (length) {
|
|
case 1:
|
|
return d.date;
|
|
case 2:
|
|
return padLeft(d.date, 2, '0');
|
|
case 3:
|
|
return culture.calendars.standard.days.namesAbbr[d.day];
|
|
case 4:
|
|
return culture.calendars.standard.days.names[d.day];
|
|
}
|
|
break;
|
|
case 'm':
|
|
switch (length) {
|
|
case 1:
|
|
return d.month + 1;
|
|
case 2:
|
|
return padLeft(d.month + 1, 2, '0');
|
|
case 3:
|
|
return culture.calendars.standard.months.namesAbbr[d.month];
|
|
case 4:
|
|
return culture.calendars.standard.months.names[d.month];
|
|
case 5:
|
|
return culture.calendars.standard.months.names[d.month].charAt(0);
|
|
}
|
|
break;
|
|
case 'y':
|
|
switch (length) {
|
|
case 2:
|
|
return d.year % 100;
|
|
case 4:
|
|
return d.year;
|
|
}
|
|
break;
|
|
}
|
|
return '##';
|
|
},
|
|
time: function (t, part, length, ampm, fraclen) {
|
|
var ret, fraction;
|
|
switch (part) {
|
|
case 'h':
|
|
ret = padLeft(ampm ? t.hours % 12 || 12 : t.hours, length, '0');
|
|
if (fraclen) {
|
|
fraction = (t.minutes + (t.seconds + t.milliseconds / 1000) / 60) / 60;
|
|
}
|
|
break;
|
|
case 'm':
|
|
ret = padLeft(t.minutes, length, '0');
|
|
if (fraclen) {
|
|
fraction = (t.seconds + t.milliseconds / 1000) / 60;
|
|
}
|
|
break;
|
|
case 's':
|
|
ret = padLeft(t.seconds, length, '0');
|
|
if (fraclen) {
|
|
fraction = t.milliseconds / 1000;
|
|
}
|
|
break;
|
|
}
|
|
if (fraction) {
|
|
ret += fraction.toFixed(fraclen).replace(/^0+/, '');
|
|
}
|
|
return ret;
|
|
},
|
|
eltime: function (value, part, length, fraclen) {
|
|
var ret, fraction;
|
|
switch (part) {
|
|
case 'h':
|
|
ret = value * 24;
|
|
break;
|
|
case 'm':
|
|
ret = value * 24 * 60;
|
|
break;
|
|
case 's':
|
|
ret = value * 24 * 60 * 60;
|
|
break;
|
|
}
|
|
if (fraclen) {
|
|
fraction = ret - (ret | 0);
|
|
}
|
|
ret = padLeft(ret | 0, length, '0');
|
|
if (fraction) {
|
|
ret += fraction.toFixed(fraclen).replace(/^0+/, '');
|
|
}
|
|
return ret;
|
|
},
|
|
fill: function (ch) {
|
|
return ch;
|
|
},
|
|
formatInt: function (culture, value, parts, declen, sep) {
|
|
value = value.toFixed(declen).replace(/\..*$/, '');
|
|
if (declen > 0) {
|
|
if (value === '0') {
|
|
value = '';
|
|
} else if (value === '-0') {
|
|
value = '-';
|
|
}
|
|
}
|
|
var iv = value.length - 1;
|
|
var result = [];
|
|
var len = 0, str;
|
|
function add(ch) {
|
|
if (sep && len && len % 3 === 0 && ch != ' ') {
|
|
str = culture.numberFormat[','] + str;
|
|
}
|
|
str = ch + str;
|
|
len++;
|
|
}
|
|
for (var j = parts.length; --j >= 0;) {
|
|
var format = parts[j];
|
|
str = '';
|
|
for (var k = format.length; --k >= 0;) {
|
|
var chf = format.charAt(k);
|
|
if (iv < 0) {
|
|
if (chf == '0') {
|
|
add('0');
|
|
} else if (chf == '?') {
|
|
add(' ');
|
|
}
|
|
} else {
|
|
add(value.charAt(iv--));
|
|
}
|
|
}
|
|
if (j === 0) {
|
|
while (iv >= 0) {
|
|
add(value.charAt(iv--));
|
|
}
|
|
}
|
|
result.unshift(str);
|
|
}
|
|
return result;
|
|
},
|
|
formatDec: function (value, parts, declen) {
|
|
value = value.toFixed(declen);
|
|
var pos = value.indexOf('.');
|
|
if (pos >= 0) {
|
|
value = value.substr(pos + 1).replace(/0+$/, '');
|
|
} else {
|
|
value = '';
|
|
}
|
|
var iv = 0;
|
|
var result = [];
|
|
for (var j = 0; j < parts.length; ++j) {
|
|
var format = parts[j];
|
|
var str = '';
|
|
for (var k = 0; k < format.length; ++k) {
|
|
var chf = format.charAt(k);
|
|
if (iv < value.length) {
|
|
str += value.charAt(iv++);
|
|
} else if (chf == '0') {
|
|
str += '0';
|
|
} else if (chf == '?') {
|
|
str += ' ';
|
|
}
|
|
}
|
|
result.push(str);
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
function padLeft(val, width, ch) {
|
|
val += '';
|
|
while (val.length < width) {
|
|
val = ch + val;
|
|
}
|
|
return val;
|
|
}
|
|
function text(f) {
|
|
var a = f.body;
|
|
var text = '';
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var el = a[i];
|
|
if (typeof el == 'string') {
|
|
text += el;
|
|
} else if (el.type == 'space') {
|
|
text += ' ';
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
kendo.spreadsheet.formatting = {
|
|
compile: compile,
|
|
parse: parse,
|
|
format: function (value, format, culture) {
|
|
var f = compile(format)(value, culture);
|
|
var span = dom.element('span');
|
|
span.__dataType = f.type;
|
|
var a = f.body;
|
|
if (f.color) {
|
|
span.attr.style = { color: f.color };
|
|
}
|
|
for (var i = 0; i < a.length; ++i) {
|
|
var el = a[i];
|
|
if (typeof el == 'string') {
|
|
span.children.push(dom.text(el));
|
|
} else if (el.type == 'space') {
|
|
span.children.push(dom.element('span', { style: { visibility: 'hidden' } }, [dom.text(el.value)]));
|
|
}
|
|
}
|
|
return span;
|
|
},
|
|
text: function (value, format, culture) {
|
|
var f = compile(format)(value, culture);
|
|
return text(f);
|
|
},
|
|
textAndColor: function (value, format, culture) {
|
|
var f = compile(format)(value, culture);
|
|
return {
|
|
text: text(f),
|
|
color: f.color,
|
|
type: f.type
|
|
};
|
|
},
|
|
type: function (value, format) {
|
|
return compile(format)(value).type;
|
|
},
|
|
adjustDecimals: function (format, diff) {
|
|
var ast = parse(format);
|
|
adjustDecimals(ast, diff);
|
|
return print(ast);
|
|
}
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/runtime.functions', [
|
|
'spreadsheet/runtime',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var util = kendo.util;
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var calc = spreadsheet.calc;
|
|
var runtime = calc.runtime;
|
|
var defineFunction = runtime.defineFunction;
|
|
var defineAlias = runtime.defineAlias;
|
|
var CalcError = runtime.CalcError;
|
|
var RangeRef = spreadsheet.RangeRef;
|
|
var CellRef = spreadsheet.CellRef;
|
|
var UnionRef = spreadsheet.UnionRef;
|
|
var Matrix = runtime.Matrix;
|
|
var Ref = spreadsheet.Ref;
|
|
var daysInMonth = runtime.daysInMonth;
|
|
var packDate = runtime.packDate;
|
|
var unpackDate = runtime.unpackDate;
|
|
var daysInYear = runtime.daysInYear;
|
|
[
|
|
'abs',
|
|
'cos',
|
|
'sin',
|
|
'acos',
|
|
'asin',
|
|
'tan',
|
|
'atan',
|
|
'exp',
|
|
'sqrt'
|
|
].forEach(function (name) {
|
|
defineFunction(name, Math[name]).args([[
|
|
'*n',
|
|
'number'
|
|
]]);
|
|
});
|
|
defineFunction('ln', Math.log).args([[
|
|
'*n',
|
|
'number'
|
|
]]);
|
|
defineFunction('log', function (num, base) {
|
|
return Math.log(num) / Math.log(base);
|
|
}).args([
|
|
[
|
|
'*num',
|
|
'number++'
|
|
],
|
|
[
|
|
'*base',
|
|
[
|
|
'or',
|
|
'number++',
|
|
[
|
|
'null',
|
|
10
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$base != 1',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('log10', function (num) {
|
|
return Math.log(num) / Math.log(10);
|
|
}).args([[
|
|
'*num',
|
|
'number++'
|
|
]]);
|
|
defineFunction('pi', function () {
|
|
return Math.PI;
|
|
}).args([]);
|
|
defineFunction('sqrtpi', function (n) {
|
|
return Math.sqrt(n * Math.PI);
|
|
}).args([[
|
|
'*num',
|
|
'number+'
|
|
]]);
|
|
defineFunction('degrees', function (rad) {
|
|
return 180 * rad / Math.PI % 360;
|
|
}).args([[
|
|
'*radians',
|
|
'number'
|
|
]]);
|
|
defineFunction('radians', function (deg) {
|
|
return Math.PI * deg / 180;
|
|
}).args([[
|
|
'*degrees',
|
|
'number'
|
|
]]);
|
|
function _cosh(n) {
|
|
return (Math.exp(n) + Math.exp(-n)) / 2;
|
|
}
|
|
defineFunction('cosh', _cosh).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('acosh', function (n) {
|
|
return Math.log(n + Math.sqrt(n - 1) * Math.sqrt(n + 1));
|
|
}).args([
|
|
[
|
|
'*num',
|
|
'number'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$num >= 1'
|
|
]
|
|
]
|
|
]);
|
|
function _sinh(n) {
|
|
return (Math.exp(n) - Math.exp(-n)) / 2;
|
|
}
|
|
defineFunction('sinh', _sinh).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('asinh', function (n) {
|
|
return Math.log(n + Math.sqrt(n * n + 1));
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('sec', function (n) {
|
|
return 1 / Math.cos(n);
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('sech', function (n) {
|
|
return 1 / _cosh(n);
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('csc', function (n) {
|
|
return 1 / Math.sin(n);
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('csch', function (n) {
|
|
return 1 / _sinh(n);
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('atan2', function (x, y) {
|
|
return Math.atan(y / x);
|
|
}).args([
|
|
[
|
|
'*x',
|
|
'divisor'
|
|
],
|
|
[
|
|
'*y',
|
|
'number'
|
|
]
|
|
]);
|
|
function _tanh(n) {
|
|
return _sinh(n) / _cosh(n);
|
|
}
|
|
defineFunction('tanh', _tanh).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('atanh', function (n) {
|
|
return Math.log(Math.sqrt(1 - n * n) / (1 - n));
|
|
}).args([[
|
|
'*num',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
-1,
|
|
1
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('cot', function (n) {
|
|
return 1 / Math.tan(n);
|
|
}).args([[
|
|
'*num',
|
|
'divisor'
|
|
]]);
|
|
defineFunction('coth', function (n) {
|
|
return 1 / _tanh(n);
|
|
}).args([[
|
|
'*num',
|
|
'divisor'
|
|
]]);
|
|
defineFunction('acot', function (n) {
|
|
return Math.PI / 2 - Math.atan(n);
|
|
}).args([[
|
|
'*num',
|
|
'number'
|
|
]]);
|
|
defineFunction('acoth', function (n) {
|
|
return Math.log((n + 1) / (n - 1)) / 2;
|
|
}).args([
|
|
[
|
|
'*num',
|
|
'number'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'or',
|
|
[
|
|
'assert',
|
|
'$num < -1'
|
|
],
|
|
[
|
|
'assert',
|
|
'$num > 1'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('power', function (a, b) {
|
|
return Math.pow(a, b);
|
|
}).args([
|
|
[
|
|
'*a',
|
|
'number'
|
|
],
|
|
[
|
|
'*b',
|
|
'number'
|
|
]
|
|
]);
|
|
defineFunction('mod', function (a, b) {
|
|
return a % b;
|
|
}).args([
|
|
[
|
|
'*a',
|
|
'number'
|
|
],
|
|
[
|
|
'*b',
|
|
'divisor'
|
|
]
|
|
]);
|
|
defineFunction('quotient', function (a, b) {
|
|
return Math.floor(a / b);
|
|
}).args([
|
|
[
|
|
'*a',
|
|
'number'
|
|
],
|
|
[
|
|
'*b',
|
|
'divisor'
|
|
]
|
|
]);
|
|
defineFunction('ceiling', function (num, s) {
|
|
return s ? s * Math.ceil(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
'number'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$significance >= 0 || $number < 0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('ceiling.precise', function (num, s) {
|
|
s = Math.abs(s);
|
|
return s ? s * Math.ceil(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineAlias('iso.ceiling', 'ceiling.precise');
|
|
defineFunction('ceiling.math', function (num, s, mode) {
|
|
if (!s || !num) {
|
|
return 0;
|
|
}
|
|
if (num < 0 && (!mode && s < 0 || mode && s > 0)) {
|
|
s = -s;
|
|
}
|
|
return s ? s * Math.ceil(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
'$number < 0 ? -1 : 1'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*mode',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('floor', function (num, s) {
|
|
return s ? s * Math.floor(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
'number'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$significance >= 0 || $number < 0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('floor.precise', function (num, s) {
|
|
s = Math.abs(s);
|
|
return s ? s * Math.floor(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('floor.math', function (num, s, mode) {
|
|
if (!s || !num) {
|
|
return 0;
|
|
}
|
|
if (num < 0 && (!mode && s < 0 || mode && s > 0)) {
|
|
s = -s;
|
|
}
|
|
return s ? s * Math.floor(num / s) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*significance',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
'$number < 0 ? -1 : 1'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*mode',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('int', Math.floor).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
defineFunction('mround', function (num, mult) {
|
|
return mult ? mult * Math.round(num / mult) : 0;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*multiple',
|
|
'number'
|
|
]
|
|
]);
|
|
defineFunction('round', function (num, digits) {
|
|
var sign = num < 0 ? -1 : 1;
|
|
if (sign < 0) {
|
|
num = -num;
|
|
}
|
|
digits = Math.pow(10, digits);
|
|
num *= digits;
|
|
num = Math.round(num);
|
|
return sign * num / digits;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*digits',
|
|
'number'
|
|
]
|
|
]);
|
|
defineFunction('roundup', function (num, digits) {
|
|
digits = Math.pow(10, digits);
|
|
num *= digits;
|
|
num = num < 0 ? Math.floor(num) : Math.ceil(num);
|
|
return num / digits;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*digits',
|
|
'number'
|
|
]
|
|
]);
|
|
defineFunction('rounddown', function (num, digits) {
|
|
digits = Math.pow(10, digits);
|
|
num *= digits;
|
|
num = num < 0 ? Math.ceil(num) : Math.floor(num);
|
|
return num / digits;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*digits',
|
|
'number'
|
|
]
|
|
]);
|
|
defineFunction('even', function (num) {
|
|
var n = num < 0 ? Math.floor(num) : Math.ceil(num);
|
|
return n % 2 ? n + (n < 0 ? -1 : 1) : n;
|
|
}).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
defineFunction('odd', function (num) {
|
|
var n = num < 0 ? Math.floor(num) : Math.ceil(num);
|
|
return n % 2 ? n : n + (n < 0 ? -1 : 1);
|
|
}).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
defineFunction('sign', function (num) {
|
|
return num < 0 ? -1 : num > 0 ? 1 : 0;
|
|
}).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
function _gcd(a, b) {
|
|
while (b) {
|
|
var r = a % b;
|
|
a = b;
|
|
b = r;
|
|
}
|
|
return a;
|
|
}
|
|
function _lcm(a, b) {
|
|
return Math.abs(a * b) / _gcd(a, b);
|
|
}
|
|
defineFunction('gcd', function (args) {
|
|
var a = args[0];
|
|
for (var i = 1; i < args.length; ++i) {
|
|
a = _gcd(a, args[i]);
|
|
}
|
|
return a;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('lcm', function (args) {
|
|
var a = args[0];
|
|
for (var i = 1; i < args.length; ++i) {
|
|
a = _lcm(a, args[i]);
|
|
}
|
|
return a;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('sum', function (numbers) {
|
|
return numbers.reduce(function (sum, num) {
|
|
return sum + num;
|
|
}, 0);
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('product', function (numbers) {
|
|
return numbers.reduce(function (prod, num) {
|
|
return prod * num;
|
|
}, 1);
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('sumproduct', function (first, rest) {
|
|
var sum = 0;
|
|
first.each(function (p, row, col) {
|
|
if (typeof p == 'number') {
|
|
for (var i = 0; i < rest.length; ++i) {
|
|
var v = rest[i].get(row, col);
|
|
if (typeof v != 'number') {
|
|
return;
|
|
}
|
|
p *= v;
|
|
}
|
|
sum += p;
|
|
}
|
|
});
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'a1',
|
|
'matrix'
|
|
],
|
|
[
|
|
'+',
|
|
[
|
|
'a2',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$a2.width == $a1.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$a2.height == $a1.height'
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('sumsq', function (numbers) {
|
|
return numbers.reduce(function (sum, num) {
|
|
return sum + num * num;
|
|
}, 0);
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('sumx2my2', function (a, b) {
|
|
var sum = 0;
|
|
a.each(function (x, row, col) {
|
|
var y = b.get(row, col);
|
|
if (typeof x == 'number' && typeof y == 'number') {
|
|
sum += x * x - y * y;
|
|
}
|
|
});
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'a',
|
|
'matrix'
|
|
],
|
|
[
|
|
'b',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$b.width == $a.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$b.height == $a.height'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('sumx2py2', function (a, b) {
|
|
var sum = 0;
|
|
a.each(function (x, row, col) {
|
|
var y = b.get(row, col);
|
|
if (typeof x == 'number' && typeof y == 'number') {
|
|
sum += x * x + y * y;
|
|
}
|
|
});
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'a',
|
|
'matrix'
|
|
],
|
|
[
|
|
'b',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$b.width == $a.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$b.height == $a.height'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('sumxmy2', function (a, b) {
|
|
var sum = 0;
|
|
a.each(function (x, row, col) {
|
|
var y = b.get(row, col);
|
|
if (typeof x == 'number' && typeof y == 'number') {
|
|
sum += (x - y) * (x - y);
|
|
}
|
|
});
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'a',
|
|
'matrix'
|
|
],
|
|
[
|
|
'b',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$b.width == $a.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$b.height == $a.height'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('seriessum', function (x, n, m, a) {
|
|
var sum = 0;
|
|
a.each(function (coef) {
|
|
if (typeof coef != 'number') {
|
|
throw new CalcError('VALUE');
|
|
}
|
|
sum += coef * Math.pow(x, n);
|
|
n += m;
|
|
});
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'y',
|
|
'number'
|
|
],
|
|
[
|
|
'm',
|
|
'number'
|
|
],
|
|
[
|
|
'a',
|
|
'matrix'
|
|
]
|
|
]);
|
|
defineFunction('min', function (numbers) {
|
|
return Math.min.apply(Math, numbers);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('max', function (numbers) {
|
|
return Math.max.apply(Math, numbers);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('counta', function (values) {
|
|
return values.length;
|
|
}).args([[
|
|
'values',
|
|
[
|
|
'#collect',
|
|
'anyvalue'
|
|
]
|
|
]]);
|
|
defineFunction('count', function (numbers) {
|
|
return numbers.length;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'#collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('countunique', function (values) {
|
|
var count = 0, seen = [];
|
|
values.forEach(function (val) {
|
|
if (seen.indexOf(val) < 0) {
|
|
count++;
|
|
seen.push(val);
|
|
}
|
|
});
|
|
return count;
|
|
}).args([[
|
|
'values',
|
|
[
|
|
'#collect',
|
|
'anyvalue'
|
|
]
|
|
]]);
|
|
defineFunction('countblank', function (a) {
|
|
var count = 0;
|
|
function add(val) {
|
|
if (val == null || val === '') {
|
|
count++;
|
|
}
|
|
}
|
|
function loop(args) {
|
|
for (var i = 0; i < args.length; ++i) {
|
|
var x = args[i];
|
|
if (x instanceof Matrix) {
|
|
x.each(add, true);
|
|
} else {
|
|
add(x);
|
|
}
|
|
}
|
|
}
|
|
loop(a);
|
|
return count;
|
|
}).args([[
|
|
'+',
|
|
[
|
|
'args',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'anyvalue'
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('iseven', function (num) {
|
|
return num % 2 === 0;
|
|
}).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
defineFunction('isodd', function (num) {
|
|
return num % 2 !== 0;
|
|
}).args([[
|
|
'*number',
|
|
'number'
|
|
]]);
|
|
defineFunction('n', function (val) {
|
|
if (typeof val == 'boolean') {
|
|
return val ? 1 : 0;
|
|
}
|
|
if (typeof val == 'number') {
|
|
return val;
|
|
}
|
|
return 0;
|
|
}).args([[
|
|
'*value',
|
|
'anyvalue'
|
|
]]);
|
|
defineFunction('na', function () {
|
|
return new CalcError('N/A');
|
|
}).args([]);
|
|
function forIFS(args, f) {
|
|
var chunks = [], i = 0, matrix = args[0];
|
|
while (i < args.length) {
|
|
chunks.push({
|
|
matrix: args[i++],
|
|
pred: parseCriteria(args[i++])
|
|
});
|
|
}
|
|
ROW:
|
|
for (var row = 0; row < matrix.height; ++row) {
|
|
COL:
|
|
for (var col = 0; col < matrix.width; ++col) {
|
|
for (i = 0; i < chunks.length; ++i) {
|
|
var val = chunks[i].matrix.get(row, col);
|
|
if (!chunks[i].pred(val == null || val === '' ? 0 : val)) {
|
|
continue COL;
|
|
}
|
|
}
|
|
f(row, col);
|
|
}
|
|
}
|
|
}
|
|
var ARGS_COUNTIFS = [
|
|
[
|
|
'm1',
|
|
'matrix'
|
|
],
|
|
[
|
|
'c1',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
[
|
|
'm2',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$m1.width == $m2.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$m1.height == $m2.height'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'c2',
|
|
'anyvalue'
|
|
]
|
|
]
|
|
];
|
|
defineFunction('countifs', function (m1, c1, rest) {
|
|
var count = 0;
|
|
rest.unshift(m1, c1);
|
|
forIFS(rest, function () {
|
|
count++;
|
|
});
|
|
return count;
|
|
}).args(ARGS_COUNTIFS);
|
|
var ARGS_SUMIFS = [[
|
|
'range',
|
|
'matrix'
|
|
]].concat(ARGS_COUNTIFS);
|
|
defineFunction('sumifs', function (range, m1, c1, args) {
|
|
args.unshift(range, numericPredicate, m1, c1);
|
|
var sum = 0;
|
|
forIFS(args, function (row, col) {
|
|
var val = range.get(row, col);
|
|
if (val) {
|
|
sum += val;
|
|
}
|
|
});
|
|
return sum;
|
|
}).args(ARGS_SUMIFS);
|
|
defineFunction('averageifs', function (range, m1, c1, args) {
|
|
args.unshift(range, numericPredicate, m1, c1);
|
|
var sum = 0, count = 0;
|
|
forIFS(args, function (row, col) {
|
|
var val = range.get(row, col);
|
|
if (val == null || val === '') {
|
|
val = 0;
|
|
}
|
|
sum += val;
|
|
count++;
|
|
});
|
|
return count ? sum / count : new CalcError('DIV/0');
|
|
}).args(ARGS_SUMIFS);
|
|
defineFunction('countif', function (matrix, criteria) {
|
|
criteria = parseCriteria(criteria);
|
|
var count = 0;
|
|
matrix.each(function (val) {
|
|
if (criteria(val)) {
|
|
count++;
|
|
}
|
|
});
|
|
return count;
|
|
}).args([
|
|
[
|
|
'range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'*criteria',
|
|
'anyvalue'
|
|
]
|
|
]);
|
|
var ARGS_SUMIF = [
|
|
[
|
|
'range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'*criteria',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
'sumRange',
|
|
[
|
|
'or',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$sumRange.width == $range.width'
|
|
],
|
|
[
|
|
'assert',
|
|
'$sumRange.height == $range.height'
|
|
]
|
|
],
|
|
[
|
|
'null',
|
|
'$range'
|
|
]
|
|
]
|
|
]
|
|
];
|
|
defineFunction('sumif', function (range, criteria, sumRange) {
|
|
var sum = 0;
|
|
criteria = parseCriteria(criteria);
|
|
range.each(function (val, row, col) {
|
|
if (criteria(val)) {
|
|
var v = sumRange.get(row, col);
|
|
if (numericPredicate(v)) {
|
|
sum += v || 0;
|
|
}
|
|
}
|
|
});
|
|
return sum;
|
|
}).args(ARGS_SUMIF);
|
|
defineFunction('averageif', function (range, criteria, sumRange) {
|
|
var sum = 0, count = 0;
|
|
criteria = parseCriteria(criteria);
|
|
range.each(function (val, row, col) {
|
|
if (criteria(val)) {
|
|
var v = sumRange.get(row, col);
|
|
if (numericPredicate(v)) {
|
|
sum += v || 0;
|
|
count++;
|
|
}
|
|
}
|
|
});
|
|
return count ? sum / count : new CalcError('DIV/0');
|
|
}).args(ARGS_SUMIF);
|
|
(function (def) {
|
|
def('large', function (numbers, nth) {
|
|
return numbers.sort(descending)[nth];
|
|
});
|
|
def('small', function (numbers, nth) {
|
|
return numbers.sort(ascending)[nth];
|
|
});
|
|
}(function (name, handler) {
|
|
defineFunction(name, function (matrix, nth) {
|
|
var numbers = [];
|
|
var error = matrix.each(function (val) {
|
|
if (val instanceof CalcError) {
|
|
return val;
|
|
}
|
|
if (typeof val == 'number') {
|
|
numbers.push(val);
|
|
}
|
|
});
|
|
if (error) {
|
|
return error;
|
|
}
|
|
if (nth > numbers.length) {
|
|
return new CalcError('NUM');
|
|
}
|
|
return handler(numbers, nth - 1);
|
|
}).args([
|
|
[
|
|
'array',
|
|
'matrix'
|
|
],
|
|
[
|
|
'*nth',
|
|
'number++'
|
|
]
|
|
]);
|
|
}));
|
|
function _avg(numbers) {
|
|
return numbers.reduce(function (sum, num) {
|
|
return sum + num;
|
|
}, 0) / numbers.length;
|
|
}
|
|
function _var_sp(numbers, divisor, avg) {
|
|
if (avg == null) {
|
|
avg = _avg(numbers);
|
|
}
|
|
return numbers.reduce(function (sum, num) {
|
|
return sum + Math.pow(num - avg, 2);
|
|
}, 0) / divisor;
|
|
}
|
|
function _stdev_sp(numbers, divisor) {
|
|
return Math.sqrt(_var_sp(numbers, divisor));
|
|
}
|
|
defineFunction('stdev.s', function (numbers) {
|
|
return _stdev_sp(numbers, numbers.length - 1);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 2',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('stdev.p', function (numbers) {
|
|
return _stdev_sp(numbers, numbers.length);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 2',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('var.s', function (numbers) {
|
|
return _var_sp(numbers, numbers.length - 1);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 2',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('var.p', function (numbers) {
|
|
return _var_sp(numbers, numbers.length);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 2',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('median', function (numbers) {
|
|
var n = numbers.length;
|
|
numbers.sort(ascending);
|
|
if (n % 2) {
|
|
return numbers[n >> 1];
|
|
}
|
|
return (numbers[n >> 1] + numbers[n >> 1 + 1]) / 2;
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('mode.sngl', function (numbers) {
|
|
numbers.sort(ascending);
|
|
var prev = null, count = 0, max = 1, mode = null;
|
|
for (var i = 0; i < numbers.length; ++i) {
|
|
var n = numbers[i];
|
|
if (n != prev) {
|
|
count = 1;
|
|
prev = n;
|
|
} else {
|
|
count++;
|
|
}
|
|
if (count > max) {
|
|
max = count;
|
|
mode = n;
|
|
}
|
|
}
|
|
return mode == null ? new CalcError('N/A') : mode;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('mode.mult', function (numbers) {
|
|
var seen = Object.create(null), max = 2, res = [];
|
|
numbers.forEach(function (num) {
|
|
var s = seen[num] || 0;
|
|
seen[num] = ++s;
|
|
if (s == max) {
|
|
res.push(num);
|
|
} else if (s > max) {
|
|
max = s;
|
|
res = [num];
|
|
}
|
|
});
|
|
var m = new Matrix(this);
|
|
res.forEach(function (num, i) {
|
|
m.set(i, 0, num);
|
|
});
|
|
return m;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
defineFunction('geomean', function (numbers) {
|
|
var n = numbers.length;
|
|
var p = numbers.reduce(function (p, num) {
|
|
if (num < 0) {
|
|
throw new CalcError('NUM');
|
|
}
|
|
return p * num;
|
|
}, 1);
|
|
return Math.pow(p, 1 / n);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('harmean', function (numbers) {
|
|
var n = numbers.length;
|
|
var s = numbers.reduce(function (s, num) {
|
|
if (!num) {
|
|
throw new CalcError('DIV/0');
|
|
}
|
|
return s + 1 / num;
|
|
}, 0);
|
|
return n / s;
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('trimmean', function (numbers, p) {
|
|
var n = numbers.length;
|
|
numbers.sort(ascending);
|
|
var discard = Math.floor(n * p);
|
|
if (discard % 2) {
|
|
--discard;
|
|
}
|
|
discard /= 2;
|
|
var sum = 0;
|
|
for (var i = discard; i < n - discard; ++i) {
|
|
sum += numbers[i];
|
|
}
|
|
return sum / (n - discard * 2);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'percent',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between)',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('frequency', function (data, bins) {
|
|
data.sort(ascending);
|
|
bins.sort(ascending);
|
|
var prev = -Infinity;
|
|
var i = 0;
|
|
function count(max) {
|
|
var n = 0;
|
|
while (i < data.length && data[i] > prev && data[i] <= max) {
|
|
++n;
|
|
++i;
|
|
}
|
|
return n;
|
|
}
|
|
var m = new Matrix(this);
|
|
bins.forEach(function (val, i) {
|
|
var n = count(val);
|
|
prev = val;
|
|
m.set(i, 0, n);
|
|
});
|
|
m.set(m.height, 0, data.length - i);
|
|
return m;
|
|
}).args([
|
|
[
|
|
'data',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'bins',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('rank.eq', function (val, numbers, asc) {
|
|
numbers.sort(asc ? ascending : descending);
|
|
var pos = numbers.indexOf(val);
|
|
return pos < 0 ? new CalcError('N/A') : pos + 1;
|
|
}).args([
|
|
[
|
|
'value',
|
|
'number'
|
|
],
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'order',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineAlias('rank', 'rank.eq');
|
|
defineFunction('rank.avg', function (val, numbers, asc) {
|
|
numbers.sort(asc ? ascending : descending);
|
|
var pos = numbers.indexOf(val);
|
|
if (pos < 0) {
|
|
return new CalcError('N/A');
|
|
}
|
|
for (var i = pos; numbers[i] == val; ++i) {
|
|
}
|
|
return (pos + i + 1) / 2;
|
|
}).args([
|
|
[
|
|
'value',
|
|
'number'
|
|
],
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'order',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('kurt', function (numbers) {
|
|
var n = numbers.length;
|
|
var avg = _avg(numbers);
|
|
var variance = _var_sp(numbers, n - 1, avg);
|
|
var stddev = Math.sqrt(variance);
|
|
var sum = numbers.reduce(function (sum, num) {
|
|
return sum + Math.pow((num - avg) / stddev, 4);
|
|
}, 0);
|
|
return n * (n + 1) / ((n - 1) * (n - 2) * (n - 3)) * sum - 3 * Math.pow(n - 1, 2) / ((n - 2) * (n - 3));
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 4',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
function _percentrank(numbers, x, exc) {
|
|
var nlt = 0, ngt = 0, left = null, right = null, found = false;
|
|
numbers.forEach(function (num) {
|
|
if (num < x) {
|
|
nlt++;
|
|
left = left == null ? num : Math.max(left, num);
|
|
} else if (num > x) {
|
|
ngt++;
|
|
right = right == null ? num : Math.min(right, num);
|
|
} else {
|
|
found = true;
|
|
}
|
|
});
|
|
if (!nlt && !ngt) {
|
|
return new CalcError('N/A');
|
|
}
|
|
if (found) {
|
|
if (exc) {
|
|
return (nlt + 1) / (numbers.length + 1);
|
|
}
|
|
return nlt / (nlt + ngt);
|
|
}
|
|
return ((right - x) * _percentrank(numbers, left, exc) + (x - left) * _percentrank(numbers, right, exc)) / (right - left);
|
|
}
|
|
var ARGS_PERCENTRANK = [
|
|
[
|
|
'array',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'significance',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
3
|
|
],
|
|
'integer++'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array.length > 0',
|
|
'NUM'
|
|
]
|
|
]
|
|
];
|
|
defineFunction('percentrank.inc', function (numbers, x, significance) {
|
|
var p = _percentrank(numbers, x, 0);
|
|
p = p.toFixed(significance + 1);
|
|
return parseFloat(p.substr(0, p.length - 1));
|
|
}).args(ARGS_PERCENTRANK);
|
|
defineFunction('percentrank.exc', function (numbers, x, significance) {
|
|
var p = _percentrank(numbers, x, 1);
|
|
p = p.toFixed(significance + 1);
|
|
return parseFloat(p.substr(0, p.length - 1));
|
|
}).args(ARGS_PERCENTRANK);
|
|
defineAlias('percentrank', 'percentrank.inc');
|
|
function _covariance(x, y, divisor) {
|
|
var sum = 0;
|
|
var ax = _avg(x);
|
|
var ay = _avg(y);
|
|
var n = x.length;
|
|
for (var i = 0; i < n; ++i) {
|
|
sum += (x[i] - ax) * (y[i] - ay);
|
|
}
|
|
return sum / divisor;
|
|
}
|
|
defineFunction('covariance.p', function (x, y) {
|
|
return _covariance(x, y, x.length);
|
|
}).args([
|
|
[
|
|
'array1',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'array2',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length == $array2.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length > 0',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('covariance.s', function (x, y) {
|
|
return _covariance(x, y, x.length - 1);
|
|
}).args([
|
|
[
|
|
'array1',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'array2',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length == $array2.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length > 1',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineAlias('covar', 'covariance.p');
|
|
var _fact = util.memoize(function (n) {
|
|
for (var i = 2, fact = 1; i <= n; ++i) {
|
|
fact *= i;
|
|
}
|
|
return fact;
|
|
});
|
|
defineFunction('fact', _fact).args([[
|
|
'*n',
|
|
'integer+'
|
|
]]);
|
|
defineFunction('factdouble', function (n) {
|
|
for (var i = 2 + (n & 1), fact = 1; i <= n; i += 2) {
|
|
fact *= i;
|
|
}
|
|
return fact;
|
|
}).args([[
|
|
'*n',
|
|
'integer+'
|
|
]]);
|
|
defineFunction('multinomial', function (numbers) {
|
|
var div = 1, sum = 0;
|
|
numbers.forEach(function (n) {
|
|
if (n < 0) {
|
|
throw new CalcError('NUM');
|
|
}
|
|
sum += n;
|
|
div *= _fact(n);
|
|
});
|
|
return _fact(sum) / div;
|
|
}).args([[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
]]);
|
|
var _combinations = util.memoize(function (n, k) {
|
|
for (var f1 = k + 1, f2 = 1, p1 = 1, p2 = 1; f2 <= n - k; ++f1, ++f2) {
|
|
p1 *= f1;
|
|
p2 *= f2;
|
|
}
|
|
return p1 / p2;
|
|
});
|
|
defineFunction('combin', _combinations).args([
|
|
[
|
|
'*n',
|
|
'integer++'
|
|
],
|
|
[
|
|
'*k',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
0,
|
|
'$n'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('combina', function (n, k) {
|
|
return _combinations(n + k - 1, n - 1);
|
|
}).args([
|
|
[
|
|
'*n',
|
|
'integer++'
|
|
],
|
|
[
|
|
'*k',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
1,
|
|
'$n'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('average', function (numbers) {
|
|
var sum = numbers.reduce(function (sum, num) {
|
|
return sum + num;
|
|
}, 0);
|
|
return sum / numbers.length;
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'not',
|
|
'boolean'
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length > 0',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('averagea', function (values) {
|
|
var sum = 0, count = 0;
|
|
values.forEach(function (num) {
|
|
if (typeof num != 'string') {
|
|
sum += num;
|
|
}
|
|
++count;
|
|
});
|
|
return count ? sum / count : new CalcError('DIV/0');
|
|
}).args([[
|
|
'values',
|
|
[
|
|
'collect',
|
|
'anyvalue'
|
|
]
|
|
]]);
|
|
function _percentile(numbers, rank) {
|
|
numbers.sort(ascending);
|
|
var n = numbers.length;
|
|
var k = rank | 0, d = rank - k;
|
|
if (k === 0) {
|
|
return numbers[0];
|
|
}
|
|
if (k >= n) {
|
|
return numbers[n - 1];
|
|
}
|
|
--k;
|
|
return numbers[k] + d * (numbers[k + 1] - numbers[k]);
|
|
}
|
|
function _percentile_inc(numbers, p) {
|
|
var rank = p * (numbers.length - 1) + 1;
|
|
return _percentile(numbers, rank);
|
|
}
|
|
function _percentile_exc(numbers, p) {
|
|
var rank = p * (numbers.length + 1);
|
|
return _percentile(numbers, rank);
|
|
}
|
|
defineFunction('percentile.inc', _percentile_inc).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('percentile.exc', _percentile_exc).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('quartile.inc', function (numbers, quarter) {
|
|
return _percentile_inc(numbers, quarter / 4);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'quarter',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('quartile.exc', function (numbers, quarter) {
|
|
return _percentile_exc(numbers, quarter / 4);
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'quarter',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]);
|
|
defineAlias('quartile', 'quartile.inc');
|
|
defineAlias('percentile', 'percentile.inc');
|
|
var AGGREGATE_FUNCS = [
|
|
'AVERAGE',
|
|
'COUNT',
|
|
'COUNTA',
|
|
'MAX',
|
|
'MIN',
|
|
'PRODUCT',
|
|
'STDEV.S',
|
|
'STDEV.P',
|
|
'SUM',
|
|
'VAR.S',
|
|
'VAR.P',
|
|
'MEDIAN',
|
|
'MODE.SNGL',
|
|
'LARGE',
|
|
'SMALL',
|
|
'PERCENTILE.INC',
|
|
'QUARTILE.INC',
|
|
'PERCENTILE.EXC',
|
|
'QUARTILE.EXC'
|
|
];
|
|
function fetchValuesForAggregate(self, args, options) {
|
|
var values = [];
|
|
var opt_ignore_hidden_rows = 1;
|
|
var opt_ignore_errors = 2;
|
|
var opt_use_aggregates = 4;
|
|
(function fetchValues(args) {
|
|
if (args instanceof Ref) {
|
|
self.getRefCells(args, true).forEach(function (cell) {
|
|
var value = cell.value;
|
|
if (options & opt_ignore_hidden_rows && cell.hidden) {
|
|
return;
|
|
}
|
|
if (cell.formula) {
|
|
var str = cell.formula.print(cell.row, cell.col);
|
|
if (/^\s*(?:aggregate|subtotal)\s*\(/i.test(str)) {
|
|
if (!(options & opt_use_aggregates)) {
|
|
return;
|
|
}
|
|
}
|
|
if ('value' in cell.formula) {
|
|
value = cell.formula.value;
|
|
}
|
|
}
|
|
if (options & opt_ignore_errors && value instanceof CalcError) {
|
|
return;
|
|
}
|
|
if (typeof value == 'number' || value instanceof CalcError) {
|
|
values.push(value);
|
|
}
|
|
});
|
|
} else if (Array.isArray(args)) {
|
|
for (var i = 0; i < args.length; ++i) {
|
|
fetchValues(args[i]);
|
|
}
|
|
} else if (args instanceof Matrix) {
|
|
args.each(fetchValues);
|
|
} else if (typeof args == 'number') {
|
|
values.push(args);
|
|
} else if (args instanceof CalcError && !(options & opt_ignore_errors)) {
|
|
values.push(args);
|
|
}
|
|
}(args));
|
|
return values;
|
|
}
|
|
defineFunction('aggregate', function (callback, funcId, options, args) {
|
|
var self = this;
|
|
self.resolveCells(args, function () {
|
|
var values;
|
|
if (funcId > 12) {
|
|
values = fetchValuesForAggregate(self, args[0], options);
|
|
var k = args[1];
|
|
if (k instanceof CellRef) {
|
|
k = self.getRefData(k);
|
|
}
|
|
if (typeof k != 'number') {
|
|
return callback(new CalcError('VALUE'));
|
|
}
|
|
} else {
|
|
values = fetchValuesForAggregate(self, args, options);
|
|
}
|
|
self.func(AGGREGATE_FUNCS[funcId - 1], callback, values);
|
|
});
|
|
}).argsAsync([
|
|
[
|
|
'funcId',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
16,
|
|
17,
|
|
18,
|
|
19
|
|
]
|
|
],
|
|
[
|
|
'options',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'args',
|
|
'rest'
|
|
]
|
|
]);
|
|
defineFunction('subtotal', function (callback, funcId) {
|
|
var self = this;
|
|
var ignoreHidden = funcId > 100;
|
|
if (ignoreHidden) {
|
|
funcId -= 100;
|
|
}
|
|
var args = [];
|
|
for (var i = 2; i < arguments.length; ++i) {
|
|
args.push(arguments[i]);
|
|
}
|
|
self.resolveCells(args, function () {
|
|
var values = fetchValuesForAggregate(self, args, ignoreHidden ? 1 : 0);
|
|
self.func(AGGREGATE_FUNCS[funcId - 1], callback, values);
|
|
});
|
|
}).argsAsync([
|
|
[
|
|
'funcId',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
101,
|
|
102,
|
|
103,
|
|
104,
|
|
105,
|
|
106,
|
|
107,
|
|
108,
|
|
109,
|
|
110,
|
|
111
|
|
]
|
|
],
|
|
[
|
|
'+',
|
|
[
|
|
'ref',
|
|
[
|
|
'or',
|
|
'ref',
|
|
'#matrix'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('avedev', function (numbers) {
|
|
var avg = numbers.reduce(function (sum, num) {
|
|
return sum + num;
|
|
}, 0) / numbers.length;
|
|
return numbers.reduce(function (sum, num) {
|
|
return sum + Math.abs(num - avg);
|
|
}, 0) / numbers.length;
|
|
}).args([
|
|
[
|
|
'numbers',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$numbers.length >= 2',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
function _binom_dist(x, n, p, cumulative) {
|
|
if (!cumulative) {
|
|
return _combinations(n, x) * Math.pow(p, x) * Math.pow(1 - p, n - x);
|
|
} else {
|
|
var sum = 0;
|
|
for (var j = 0; j <= x; ++j) {
|
|
sum += _combinations(n, j) * Math.pow(p, j) * Math.pow(1 - p, n - j);
|
|
}
|
|
return sum;
|
|
}
|
|
}
|
|
defineFunction('binom.dist', _binom_dist).args([
|
|
[
|
|
'successes',
|
|
'integer+'
|
|
],
|
|
[
|
|
'trials',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'assert',
|
|
'$trials >= $successes'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'probability',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineAlias('binomdist', 'binom.dist');
|
|
defineFunction('binom.inv', function (n, p, alpha) {
|
|
for (var x = 0; x <= n; ++x) {
|
|
if (_binom_dist(x, n, p, true) >= alpha) {
|
|
return x;
|
|
}
|
|
}
|
|
return new CalcError('N/A');
|
|
}).args([
|
|
[
|
|
'trials',
|
|
'integer+'
|
|
],
|
|
[
|
|
'probability',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'alpha',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineAlias('critbinom', 'binom.inv');
|
|
defineFunction('binom.dist.range', function (n, p, s, s2) {
|
|
var sum = 0;
|
|
for (var k = s; k <= s2; ++k) {
|
|
sum += _combinations(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k);
|
|
}
|
|
return sum;
|
|
}).args([
|
|
[
|
|
'trials',
|
|
'integer+'
|
|
],
|
|
[
|
|
'probability',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'successes_min',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
0,
|
|
'$trials'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'successes_max',
|
|
[
|
|
'or',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
'$successes_min',
|
|
'$trials'
|
|
]
|
|
],
|
|
[
|
|
'null',
|
|
'$successes_min'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('negbinom.dist', function (x, k, p, cumulative) {
|
|
if (cumulative) {
|
|
var sum = 0;
|
|
while (x >= 0) {
|
|
sum += _combinations(x + k - 1, x) * Math.pow(p, k) * Math.pow(1 - p, x);
|
|
x--;
|
|
}
|
|
return sum;
|
|
}
|
|
return _combinations(x + k - 1, x) * Math.pow(p, k) * Math.pow(1 - p, x);
|
|
}).args([
|
|
[
|
|
'number_f',
|
|
'integer+'
|
|
],
|
|
[
|
|
'number_s',
|
|
'integer+'
|
|
],
|
|
[
|
|
'probability_s',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineAlias('negbinomdist', 'negbinom.dist');
|
|
defineFunction('address', function (row, col, abs, a1, sheet) {
|
|
var cell = new CellRef(row - 1, col - 1, abs - 1);
|
|
if (sheet) {
|
|
cell.setSheet(sheet, true);
|
|
}
|
|
return a1 ? cell.print(0, 0) : cell.print();
|
|
}).args([
|
|
[
|
|
'row',
|
|
'integer++'
|
|
],
|
|
[
|
|
'col',
|
|
'integer++'
|
|
],
|
|
[
|
|
'abs',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
1
|
|
],
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'a1',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
true
|
|
],
|
|
'logical'
|
|
]
|
|
],
|
|
[
|
|
'sheet',
|
|
[
|
|
'or',
|
|
'null',
|
|
'string'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('areas', function (ref) {
|
|
var count = 0;
|
|
(function loop(x) {
|
|
if (x instanceof CellRef || x instanceof RangeRef) {
|
|
count++;
|
|
} else if (x instanceof UnionRef) {
|
|
x.refs.forEach(loop);
|
|
}
|
|
}(ref));
|
|
return count;
|
|
}).args([[
|
|
'ref',
|
|
'ref'
|
|
]]);
|
|
defineFunction('choose', function (index, args) {
|
|
if (index > args.length) {
|
|
return new CalcError('N/A');
|
|
} else {
|
|
return args[index - 1];
|
|
}
|
|
}).args([
|
|
[
|
|
'*index',
|
|
'integer'
|
|
],
|
|
[
|
|
'+',
|
|
[
|
|
'value',
|
|
'anything'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('column', function (ref) {
|
|
if (!ref) {
|
|
return this.formula.col + 1;
|
|
}
|
|
if (ref instanceof CellRef) {
|
|
return ref.col + 1;
|
|
}
|
|
return this.asMatrix(ref).mapCol(function (col) {
|
|
return col + ref.topLeft.col + 1;
|
|
});
|
|
}).args([[
|
|
'ref',
|
|
[
|
|
'or',
|
|
'area',
|
|
'null'
|
|
]
|
|
]]);
|
|
defineFunction('columns', function (m) {
|
|
return m instanceof Ref ? m.width() : m.width;
|
|
}).args([[
|
|
'ref',
|
|
[
|
|
'or',
|
|
'area',
|
|
'#matrix'
|
|
]
|
|
]]);
|
|
defineFunction('formulatext', function (ref) {
|
|
var cell = this.getRefCells(ref)[0];
|
|
if (!cell.formula) {
|
|
return new CalcError('N/A');
|
|
}
|
|
return cell.formula.print(cell.row, cell.col);
|
|
}).args([[
|
|
'ref',
|
|
'ref'
|
|
]]);
|
|
defineFunction('hlookup', function (value, m, row, approx) {
|
|
var resultCol = null;
|
|
m.eachCol(function (col) {
|
|
var data = m.get(0, col);
|
|
if (approx) {
|
|
if (data > value) {
|
|
return true;
|
|
}
|
|
resultCol = col;
|
|
} else if (data === value) {
|
|
resultCol = col;
|
|
return true;
|
|
}
|
|
});
|
|
if (resultCol == null) {
|
|
return new CalcError('N/A');
|
|
}
|
|
return m.get(row - 1, resultCol);
|
|
}).args([
|
|
[
|
|
'value',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
'range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'row',
|
|
'integer++'
|
|
],
|
|
[
|
|
'approx',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('index', function (ref, row, col, areanum) {
|
|
var m = ref instanceof UnionRef ? ref.refs[areanum - 1] : ref;
|
|
if (!row && !col || !m) {
|
|
return new CalcError('N/A');
|
|
}
|
|
m = this.asMatrix(m);
|
|
if (m.width > 1 && m.height > 1) {
|
|
if (row && col) {
|
|
return m.get(row - 1, col - 1);
|
|
}
|
|
if (!row) {
|
|
return m.mapRow(function (row) {
|
|
return m.get(row, col - 1);
|
|
});
|
|
}
|
|
if (!col) {
|
|
return m.mapCol(function (col) {
|
|
return m.get(row - 1, col);
|
|
});
|
|
}
|
|
}
|
|
if (m.width == 1) {
|
|
return m.get(row - 1, 0);
|
|
}
|
|
if (m.height == 1) {
|
|
return m.get(0, col - 1);
|
|
}
|
|
return new CalcError('REF');
|
|
}).args([
|
|
[
|
|
'range',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'ref'
|
|
]
|
|
],
|
|
[
|
|
'row',
|
|
[
|
|
'or',
|
|
'integer+',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'col',
|
|
[
|
|
'or',
|
|
'integer+',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'areanum',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('indirect', function (thing) {
|
|
try {
|
|
var f = this.formula;
|
|
var exp = calc.parseFormula(f.sheet, f.row, f.col, thing);
|
|
if (!(exp.ast instanceof Ref)) {
|
|
throw 1;
|
|
}
|
|
return exp.ast.absolute(f.row, f.col);
|
|
} catch (ex) {
|
|
return new CalcError('REF');
|
|
}
|
|
}).args([[
|
|
'thing',
|
|
'string'
|
|
]]);
|
|
defineFunction('match', function (val, m, type) {
|
|
var index = 1, cmp;
|
|
if (type === 0) {
|
|
cmp = parseCriteria(val);
|
|
} else if (type === -1) {
|
|
cmp = parseCriteria('<=' + val);
|
|
} else if (type === 1) {
|
|
cmp = parseCriteria('>=' + val);
|
|
}
|
|
if (m.each(function (el) {
|
|
if (el != null && cmp(el)) {
|
|
if (type !== 0 && val != el) {
|
|
--index;
|
|
}
|
|
return true;
|
|
}
|
|
index++;
|
|
}, true) && index > 0) {
|
|
return index;
|
|
} else {
|
|
return new CalcError('N/A');
|
|
}
|
|
}).args([
|
|
[
|
|
'value',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
'range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
-1,
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('offset', function (ref, rows, cols, height, width) {
|
|
var topLeft = (ref instanceof CellRef ? ref : ref.topLeft).clone();
|
|
topLeft.row += rows;
|
|
topLeft.col += cols;
|
|
if (topLeft.row < 0 || topLeft.col < 0) {
|
|
return new CalcError('VALUE');
|
|
}
|
|
if (height > 1 || width > 1) {
|
|
return new RangeRef(topLeft, new CellRef(topLeft.row + height - 1, topLeft.col + width - 1)).setSheet(ref.sheet, ref.hasSheet());
|
|
}
|
|
return topLeft;
|
|
}).args([
|
|
[
|
|
'ref',
|
|
'area'
|
|
],
|
|
[
|
|
'*rows',
|
|
'integer'
|
|
],
|
|
[
|
|
'*cols',
|
|
'integer'
|
|
],
|
|
[
|
|
'*height',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
'$ref.height()'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*width',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
'$ref.width()'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('row', function (ref) {
|
|
if (!ref) {
|
|
return this.formula.row + 1;
|
|
}
|
|
if (ref instanceof CellRef) {
|
|
return ref.row + 1;
|
|
}
|
|
return this.asMatrix(ref).mapRow(function (row) {
|
|
return row + ref.topLeft.row + 1;
|
|
});
|
|
}).args([[
|
|
'ref',
|
|
[
|
|
'or',
|
|
'area',
|
|
'null'
|
|
]
|
|
]]);
|
|
defineFunction('rows', function (m) {
|
|
return m instanceof Ref ? m.height() : m.height;
|
|
}).args([[
|
|
'ref',
|
|
[
|
|
'or',
|
|
'area',
|
|
'#matrix'
|
|
]
|
|
]]);
|
|
defineFunction('vlookup', function (value, m, col, approx) {
|
|
var resultRow = null;
|
|
m.eachRow(function (row) {
|
|
var data = m.get(row, 0);
|
|
if (approx) {
|
|
if (data > value) {
|
|
return true;
|
|
}
|
|
resultRow = row;
|
|
} else if (data === value) {
|
|
resultRow = row;
|
|
return true;
|
|
}
|
|
});
|
|
if (resultRow == null) {
|
|
return new CalcError('N/A');
|
|
}
|
|
return m.get(resultRow, col - 1);
|
|
}).args([
|
|
[
|
|
'value',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
'range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'col',
|
|
'integer++'
|
|
],
|
|
[
|
|
'approx',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('date', function (year, month, date) {
|
|
return packDate(year, month - 1, date);
|
|
}).args([
|
|
[
|
|
'*year',
|
|
'integer'
|
|
],
|
|
[
|
|
'*month',
|
|
'integer'
|
|
],
|
|
[
|
|
'*date',
|
|
'integer'
|
|
]
|
|
]);
|
|
defineFunction('day', function (date) {
|
|
return unpackDate(date).date;
|
|
}).args([[
|
|
'*date',
|
|
'date'
|
|
]]);
|
|
defineFunction('month', function (date) {
|
|
return unpackDate(date).month + 1;
|
|
}).args([[
|
|
'*date',
|
|
'date'
|
|
]]);
|
|
defineFunction('year', function (date) {
|
|
return unpackDate(date).year;
|
|
}).args([[
|
|
'*date',
|
|
'date'
|
|
]]);
|
|
defineFunction('weekday', function (date) {
|
|
return unpackDate(date).day + 1;
|
|
}).args([[
|
|
'*date',
|
|
'date'
|
|
]]);
|
|
defineFunction('weeknum', function (date, type) {
|
|
var fw = packDate(unpackDate(date).year, 0, 1);
|
|
var sy = unpackDate(fw);
|
|
var diff;
|
|
if (type == 21) {
|
|
diff = 3 - (sy.day + 6) % 7;
|
|
if (diff < 0) {
|
|
diff += 7;
|
|
}
|
|
fw += diff;
|
|
sy.date += diff;
|
|
sy.day = 4;
|
|
type = 1;
|
|
} else {
|
|
if (type == 1) {
|
|
type = 0;
|
|
} else if (type == 2) {
|
|
type = 1;
|
|
} else {
|
|
type = (type - 10) % 7;
|
|
}
|
|
}
|
|
diff = sy.day - type;
|
|
if (diff < 0) {
|
|
diff += 7;
|
|
}
|
|
fw -= diff;
|
|
return Math.ceil((date + 1 - fw) / 7);
|
|
}).args([
|
|
[
|
|
'*date',
|
|
'date'
|
|
],
|
|
[
|
|
'*type',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
1
|
|
],
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
16,
|
|
17,
|
|
21
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
function weeksInYear(year) {
|
|
var d = unpackDate(packDate(year, 0, 1));
|
|
if (d.day == 4 || d.day == 3 && runtime.isLeapYear(year)) {
|
|
return 53;
|
|
}
|
|
return 52;
|
|
}
|
|
defineFunction('isoweeknum', function isoweeknum(date) {
|
|
var d = unpackDate(date);
|
|
var dow = d.day || 7;
|
|
var wk = Math.floor((d.ord - dow + 10) / 7);
|
|
if (wk < 1) {
|
|
return weeksInYear(d.year - 1);
|
|
} else if (wk == 53 && wk > weeksInYear(d.year)) {
|
|
return 1;
|
|
}
|
|
return wk;
|
|
}).args([[
|
|
'*date',
|
|
'date'
|
|
]]);
|
|
defineFunction('now', function () {
|
|
return runtime.dateToSerial(new Date());
|
|
}).args([]);
|
|
defineFunction('today', function () {
|
|
return runtime.dateToSerial(new Date()) | 0;
|
|
}).args([]);
|
|
defineFunction('time', function (hh, mm, ss) {
|
|
return runtime.packTime(hh, mm, ss, 0);
|
|
}).args([
|
|
[
|
|
'*hours',
|
|
'integer'
|
|
],
|
|
[
|
|
'*minutes',
|
|
'integer'
|
|
],
|
|
[
|
|
'*seconds',
|
|
'integer'
|
|
]
|
|
]);
|
|
defineFunction('hour', function (time) {
|
|
return runtime.unpackTime(time).hours;
|
|
}).args([[
|
|
'*time',
|
|
'datetime'
|
|
]]);
|
|
defineFunction('minute', function (time) {
|
|
return runtime.unpackTime(time).minutes;
|
|
}).args([[
|
|
'*time',
|
|
'datetime'
|
|
]]);
|
|
defineFunction('second', function (time) {
|
|
return runtime.unpackTime(time).seconds;
|
|
}).args([[
|
|
'*time',
|
|
'datetime'
|
|
]]);
|
|
defineFunction('edate', function (base, months) {
|
|
var d = unpackDate(base);
|
|
var m = d.month + months;
|
|
var y = d.year + Math.floor(m / 12);
|
|
m %= 12;
|
|
if (m < 0) {
|
|
m += 12;
|
|
}
|
|
d = Math.min(d.date, daysInMonth(y, m));
|
|
return packDate(y, m, d);
|
|
}).args([
|
|
[
|
|
'*start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*months',
|
|
'integer'
|
|
]
|
|
]);
|
|
defineFunction('eomonth', function (base, months) {
|
|
var d = unpackDate(base);
|
|
var m = d.month + months;
|
|
var y = d.year + Math.floor(m / 12);
|
|
m %= 12;
|
|
if (m < 0) {
|
|
m += 12;
|
|
}
|
|
d = daysInMonth(y, m);
|
|
return packDate(y, m, d);
|
|
}).args([
|
|
[
|
|
'*start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*months',
|
|
'integer'
|
|
]
|
|
]);
|
|
defineFunction('workday', function (date, n, holidays) {
|
|
var inc = n > 0 ? 1 : -1;
|
|
n = Math.abs(n);
|
|
var dow = unpackDate(date).day;
|
|
while (n > 0) {
|
|
date += inc;
|
|
dow = (dow + inc) % 7;
|
|
if (dow > 0 && dow < 6 && holidays.indexOf(date) < 0) {
|
|
--n;
|
|
}
|
|
}
|
|
return date;
|
|
}).args([
|
|
[
|
|
'start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'days',
|
|
'integer'
|
|
],
|
|
[
|
|
'holidays',
|
|
[
|
|
'collect',
|
|
'date'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('networkdays', function (date, end, holidays) {
|
|
if (date > end) {
|
|
var tmp = date;
|
|
date = end;
|
|
end = tmp;
|
|
}
|
|
var count = 0;
|
|
var dow = unpackDate(date).day;
|
|
while (date <= end) {
|
|
if (dow > 0 && dow < 6 && holidays.indexOf(date) < 0) {
|
|
count++;
|
|
}
|
|
date++;
|
|
dow = (dow + 1) % 7;
|
|
}
|
|
return count;
|
|
}).args([
|
|
[
|
|
'start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'end_date',
|
|
'date'
|
|
],
|
|
[
|
|
'holidays',
|
|
[
|
|
'collect',
|
|
'date'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('days', function (start, end) {
|
|
return end - start;
|
|
}).args([
|
|
[
|
|
'*start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*end_date',
|
|
'date'
|
|
]
|
|
]);
|
|
function _days_360(start, end, method) {
|
|
var d1 = unpackDate(start);
|
|
var d2 = unpackDate(end);
|
|
if (method) {
|
|
if (d1.date == 31) {
|
|
d1.date = 30;
|
|
}
|
|
if (d2.date == 31) {
|
|
d2.date = 30;
|
|
}
|
|
} else {
|
|
if (d1.month == 1 && d2.month == 1 && d1.date == daysInMonth(d1.year, 1) && d2.date == daysInMonth(d2.year, 1)) {
|
|
d2.date = 30;
|
|
}
|
|
if (d1.date == daysInMonth(d1.year, d1.month)) {
|
|
d1.date = 30;
|
|
if (d2.date == 31) {
|
|
d2.date = 30;
|
|
}
|
|
} else {
|
|
if (d1.date == 30 && d2.date == 31) {
|
|
d2.date = 30;
|
|
}
|
|
}
|
|
}
|
|
return 360 * (d2.year - d1.year) + 30 * (d2.month - d1.month) + (d2.date - d1.date);
|
|
}
|
|
runtime._days_360 = _days_360;
|
|
defineFunction('days360', _days_360).args([
|
|
[
|
|
'*start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*end_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*method',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
'false'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('yearfrac', function (start, end, method) {
|
|
switch (method) {
|
|
case 0:
|
|
return _days_360(start, end, false) / 360;
|
|
case 1:
|
|
return (end - start) / daysInYear(unpackDate(start).year);
|
|
case 2:
|
|
return (end - start) / 360;
|
|
case 3:
|
|
return (end - start) / 365;
|
|
case 4:
|
|
return _days_360(start, end, true) / 360;
|
|
}
|
|
}).args([
|
|
[
|
|
'*start_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*end_date',
|
|
'date'
|
|
],
|
|
[
|
|
'*method',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('datevalue', function (text) {
|
|
var date = runtime.parseDate(text);
|
|
if (date) {
|
|
return runtime.dateToSerial(date);
|
|
}
|
|
return new CalcError('VALUE');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('timevalue', function (text) {
|
|
var m = text.toLowerCase().match(/(\d+):(\d+)(:(\d+)(\.(\d+))?)?\s*(am?|pm?)?/);
|
|
if (m) {
|
|
var hh = parseFloat(m[1]);
|
|
var mm = parseFloat(m[2]);
|
|
var ss = m[3] ? parseFloat(m[4]) : 0;
|
|
var ampm = m[7];
|
|
if (ampm && (hh > 12 || hh < 1)) {
|
|
return new CalcError('VALUE');
|
|
}
|
|
if (/^p/.test(ampm)) {
|
|
hh += 12;
|
|
}
|
|
return runtime.packTime(hh, mm, ss, 0);
|
|
}
|
|
return new CalcError('VALUE');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('mdeterm', function (m) {
|
|
var error = m.each(function (val) {
|
|
if (typeof val != 'number') {
|
|
return new CalcError('VALUE');
|
|
}
|
|
}, true);
|
|
return error || m.determinant();
|
|
}).args([[
|
|
'm',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$m.width == $m.height'
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('transpose', function (m) {
|
|
return m.transpose();
|
|
}).args([[
|
|
'range',
|
|
'matrix'
|
|
]]);
|
|
defineFunction('mmult', function (a, b) {
|
|
return a.multiply(b);
|
|
}).args([
|
|
[
|
|
'a',
|
|
'matrix'
|
|
],
|
|
[
|
|
'b',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$b.height == $a.width'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('munit', function (n) {
|
|
return new Matrix(this).unit(n);
|
|
}).args([[
|
|
'n',
|
|
'integer+'
|
|
]]);
|
|
defineFunction('minverse', function (m) {
|
|
var error = m.each(function (val) {
|
|
if (typeof val != 'number') {
|
|
return new CalcError('VALUE');
|
|
}
|
|
}, true);
|
|
return error || m.inverse() || new CalcError('VALUE');
|
|
}).args([[
|
|
'm',
|
|
[
|
|
'and',
|
|
'matrix',
|
|
[
|
|
'assert',
|
|
'$m.width == $m.height'
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('rand', function () {
|
|
return Math.random();
|
|
}).args([]);
|
|
defineFunction('randbetween', function (min, max) {
|
|
return min + Math.floor((max - min + 1) * Math.random());
|
|
}).args([
|
|
[
|
|
'min',
|
|
'integer'
|
|
],
|
|
[
|
|
'max',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'assert',
|
|
'$max >= $min'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('true', function () {
|
|
return true;
|
|
}).args([]);
|
|
defineFunction('false', function () {
|
|
return true;
|
|
}).args([]);
|
|
defineFunction('roman', function (num) {
|
|
return util.arabicToRoman(num).toUpperCase();
|
|
}).args([[
|
|
'*number',
|
|
'integer'
|
|
]]);
|
|
defineFunction('arabic', function (rom) {
|
|
var num = util.romanToArabic(rom);
|
|
return num == null ? new CalcError('VALUE') : num;
|
|
}).args([[
|
|
'*roman',
|
|
'string'
|
|
]]);
|
|
defineFunction('base', function (number, radix, minLen) {
|
|
var str = number.toString(radix).toUpperCase();
|
|
while (str.length < minLen) {
|
|
str = '0' + str;
|
|
}
|
|
return str;
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'integer'
|
|
],
|
|
[
|
|
'*radix',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
2,
|
|
36
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*minLen',
|
|
[
|
|
'or',
|
|
'integer+',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('decimal', function (text, radix) {
|
|
text = text.toUpperCase();
|
|
var val = 0;
|
|
for (var i = 0; i < text.length; ++i) {
|
|
var d = text.charCodeAt(i);
|
|
if (d >= 48 && d <= 57) {
|
|
d -= 48;
|
|
} else if (d >= 65 && d < 55 + radix) {
|
|
d -= 55;
|
|
} else {
|
|
return new CalcError('VALUE');
|
|
}
|
|
val = val * radix + d;
|
|
}
|
|
return val;
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*radix',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'[between]',
|
|
2,
|
|
36
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('char', function (code) {
|
|
return String.fromCharCode(code);
|
|
}).args([[
|
|
'*code',
|
|
'integer+'
|
|
]]);
|
|
var RX_NON_PRINTABLE = /[\0-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
|
|
defineFunction('clean', function (text) {
|
|
return text.replace(RX_NON_PRINTABLE, '');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('code', function (text) {
|
|
return text.charAt(0);
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineAlias('unichar', 'char');
|
|
defineAlias('unicode', 'code');
|
|
defineFunction('concatenate', function () {
|
|
var out = '';
|
|
for (var i = 0; i < arguments.length; ++i) {
|
|
out += arguments[i];
|
|
}
|
|
return out;
|
|
}).args([[
|
|
'+',
|
|
[
|
|
'*text',
|
|
'string'
|
|
]
|
|
]]);
|
|
defineFunction('dollar', function (number, decimals) {
|
|
var format = '$#,##0.DECIMALS;($#,##0.DECIMALS)';
|
|
var dec = '';
|
|
while (decimals-- > 0) {
|
|
dec += '0';
|
|
}
|
|
format = format.replace(/DECIMALS/g, dec);
|
|
return spreadsheet.formatting.text(number, format);
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*decimals',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
2
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('exact', function (a, b) {
|
|
return a === b;
|
|
}).args([
|
|
[
|
|
'*text1',
|
|
'string'
|
|
],
|
|
[
|
|
'*text2',
|
|
'string'
|
|
]
|
|
]);
|
|
defineFunction('find', function (substring, string, start) {
|
|
var pos = string.indexOf(substring, start - 1);
|
|
return pos < 0 ? new CalcError('VALUE') : pos + 1;
|
|
}).args([
|
|
[
|
|
'*substring',
|
|
'string'
|
|
],
|
|
[
|
|
'*string',
|
|
'string'
|
|
],
|
|
[
|
|
'*start',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('fixed', function (number, decimals, noCommas) {
|
|
var format = noCommas ? '0.DECIMALS' : '#,##0.DECIMALS';
|
|
var dec = '';
|
|
while (decimals-- > 0) {
|
|
dec += '0';
|
|
}
|
|
format = format.replace(/DECIMALS/g, dec);
|
|
return spreadsheet.formatting.text(number, format);
|
|
}).args([
|
|
[
|
|
'*number',
|
|
'number'
|
|
],
|
|
[
|
|
'*decimals',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
2
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'*noCommas',
|
|
[
|
|
'or',
|
|
'boolean',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('left', function (text, length) {
|
|
return text.substr(0, length);
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*length',
|
|
[
|
|
'or',
|
|
'integer+',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('right', function (text, length) {
|
|
return text.substr(-length);
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*length',
|
|
[
|
|
'or',
|
|
'integer+',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('len', function (text) {
|
|
return text.length;
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('lower', function (text) {
|
|
return text.toLowerCase();
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('upper', function (text) {
|
|
return text.toUpperCase();
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('ltrim', function (text) {
|
|
return text.replace(/^\s+/, '');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('rtrim', function (text) {
|
|
return text.replace(/\s+$/, '');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('trim', function (text) {
|
|
return text.replace(/^\s+|\s+$/, '');
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('mid', function (text, start, length) {
|
|
return text.substr(start - 1, length);
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*start',
|
|
'integer++'
|
|
],
|
|
[
|
|
'*length',
|
|
'integer+'
|
|
]
|
|
]);
|
|
defineFunction('proper', function (text) {
|
|
return text.toLowerCase().replace(/\b./g, function (s) {
|
|
return s.toUpperCase();
|
|
});
|
|
}).args([[
|
|
'*text',
|
|
'string'
|
|
]]);
|
|
defineFunction('replace', function (text, start, length, newText) {
|
|
return text.substr(0, --start) + newText + text.substr(start + length);
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*start',
|
|
'integer++'
|
|
],
|
|
[
|
|
'*length',
|
|
'integer+'
|
|
],
|
|
[
|
|
'*newText',
|
|
'string'
|
|
]
|
|
]);
|
|
defineFunction('rept', function (text, number) {
|
|
var out = '';
|
|
while (number-- > 0) {
|
|
out += text;
|
|
}
|
|
return out;
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*number',
|
|
'integer+'
|
|
]
|
|
]);
|
|
defineFunction('search', function (substring, string, start) {
|
|
var pos = string.toLowerCase().indexOf(substring.toLowerCase(), start - 1);
|
|
return pos < 0 ? new CalcError('VALUE') : pos + 1;
|
|
}).args([
|
|
[
|
|
'*substring',
|
|
'string'
|
|
],
|
|
[
|
|
'*string',
|
|
'string'
|
|
],
|
|
[
|
|
'*start',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('substitute', function (text, oldText, newText, nth) {
|
|
if (oldText === newText) {
|
|
return text;
|
|
}
|
|
var pos = -1;
|
|
function replace() {
|
|
text = text.substring(0, pos) + newText + text.substring(pos + oldText.length);
|
|
}
|
|
while ((pos = text.indexOf(oldText, pos + 1)) >= 0) {
|
|
if (nth == null) {
|
|
replace();
|
|
} else if (--nth === 0) {
|
|
replace();
|
|
break;
|
|
}
|
|
}
|
|
return text;
|
|
}).args([
|
|
[
|
|
'*text',
|
|
'string'
|
|
],
|
|
[
|
|
'*oldText',
|
|
'string'
|
|
],
|
|
[
|
|
'*newText',
|
|
'string'
|
|
],
|
|
[
|
|
'*nth',
|
|
[
|
|
'or',
|
|
'integer++',
|
|
'null'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('t', function (value) {
|
|
return typeof value == 'string' ? value : '';
|
|
}).args([[
|
|
'*value',
|
|
'anyvalue'
|
|
]]);
|
|
defineFunction('text', function (value, format) {
|
|
return spreadsheet.formatting.text(value, format);
|
|
}).args([
|
|
[
|
|
'*value',
|
|
'anyvalue'
|
|
],
|
|
[
|
|
'*format',
|
|
'string'
|
|
]
|
|
]);
|
|
defineFunction('value', function (value) {
|
|
if (typeof value == 'number') {
|
|
return value;
|
|
}
|
|
if (typeof value == 'boolean') {
|
|
return +value;
|
|
}
|
|
value = (value + '').replace(/[$€,]/g, '');
|
|
value = parseFloat(value);
|
|
return isNaN(value) ? new CalcError('VALUE') : value;
|
|
}).args([[
|
|
'*value',
|
|
'anyvalue'
|
|
]]);
|
|
defineFunction('iferror', function (value, valueIfError) {
|
|
return value instanceof CalcError ? valueIfError : value;
|
|
}).args([
|
|
[
|
|
'*value',
|
|
'forced!'
|
|
],
|
|
[
|
|
'*value_if_error',
|
|
'anyvalue'
|
|
]
|
|
]);
|
|
var parseCriteria = function () {
|
|
var RXCACHE = Object.create(null);
|
|
function makeComparator(cmp, x) {
|
|
if (typeof x == 'string') {
|
|
var num = parseFloat(x);
|
|
if (!isNaN(num) && num == x) {
|
|
x = num;
|
|
}
|
|
}
|
|
return function (a) {
|
|
var b = x;
|
|
if (typeof a == 'string' && typeof b == 'string') {
|
|
a = a.toLowerCase();
|
|
b = b.toLowerCase();
|
|
}
|
|
return cmp(a, b);
|
|
};
|
|
}
|
|
function lc(a) {
|
|
if (typeof a == 'string') {
|
|
return a.toLowerCase();
|
|
}
|
|
return a;
|
|
}
|
|
function compLT(a, b) {
|
|
return lc(a) < lc(b);
|
|
}
|
|
function compLTE(a, b) {
|
|
return lc(a) <= lc(b);
|
|
}
|
|
function compGT(a, b) {
|
|
return lc(a) > lc(b);
|
|
}
|
|
function compGTE(a, b) {
|
|
return lc(a) >= lc(b);
|
|
}
|
|
function compNE(a, b) {
|
|
return lc(a) != lc(b);
|
|
}
|
|
function compEQ(a, b) {
|
|
if (b instanceof RegExp) {
|
|
return b.test(a);
|
|
}
|
|
return lc(a) == lc(b);
|
|
}
|
|
return function (cmp) {
|
|
if (typeof cmp == 'function') {
|
|
return cmp;
|
|
}
|
|
var m;
|
|
if (m = /^=(.*)$/.exec(cmp)) {
|
|
return makeComparator(compEQ, m[1]);
|
|
}
|
|
if (m = /^<>(.*)$/.exec(cmp)) {
|
|
return makeComparator(compNE, m[1]);
|
|
}
|
|
if (m = /^<=(.*)$/.exec(cmp)) {
|
|
return makeComparator(compLTE, m[1]);
|
|
}
|
|
if (m = /^<(.*)$/.exec(cmp)) {
|
|
return makeComparator(compLT, m[1]);
|
|
}
|
|
if (m = /^>=(.*)$/.exec(cmp)) {
|
|
return makeComparator(compGTE, m[1]);
|
|
}
|
|
if (m = /^>(.*)$/.exec(cmp)) {
|
|
return makeComparator(compGT, m[1]);
|
|
}
|
|
if (/[?*]/.exec(cmp)) {
|
|
var rx = RXCACHE[cmp];
|
|
if (!rx) {
|
|
rx = cmp.replace(/(~\?|~\*|[\]({\+\.\|\^\$\\})\[]|[?*])/g, function (s) {
|
|
switch (s) {
|
|
case '~?':
|
|
return '\\?';
|
|
case '~*':
|
|
return '\\*';
|
|
case '?':
|
|
return '.';
|
|
case '*':
|
|
return '.*';
|
|
default:
|
|
return '\\' + s;
|
|
}
|
|
});
|
|
rx = RXCACHE[cmp] = new RegExp('^' + rx + '$', 'i');
|
|
}
|
|
return makeComparator(compEQ, rx);
|
|
}
|
|
return makeComparator(compEQ, cmp);
|
|
};
|
|
}();
|
|
function numericPredicate(val) {
|
|
return typeof val == 'number' || typeof val == 'boolean' || val == null || val === '';
|
|
}
|
|
function ascending(a, b) {
|
|
return a === b ? 0 : a < b ? -1 : 1;
|
|
}
|
|
function descending(a, b) {
|
|
return a === b ? 0 : a < b ? 1 : -1;
|
|
}
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/runtime.functions.2', ['spreadsheet/runtime'], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var calc = spreadsheet.calc;
|
|
var runtime = calc.runtime;
|
|
var defineFunction = runtime.defineFunction;
|
|
var CalcError = runtime.CalcError;
|
|
var packDate = runtime.packDate;
|
|
var unpackDate = runtime.unpackDate;
|
|
var isLeapYear = runtime.isLeapYear;
|
|
var daysInMonth = runtime.daysInMonth;
|
|
var _days_360 = runtime._days_360;
|
|
defineFunction('ERF', function (ll, ul) {
|
|
if (ul == null) {
|
|
return ERF(ll);
|
|
}
|
|
return ERF(ul) - ERF(ll);
|
|
}).args([
|
|
[
|
|
'lower_limit',
|
|
'number'
|
|
],
|
|
[
|
|
'upper_limit',
|
|
[
|
|
'or',
|
|
'number',
|
|
'null'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('ERFC', ERFC).args([[
|
|
'x',
|
|
'number'
|
|
]]);
|
|
defineFunction('GAMMALN', GAMMALN).args([[
|
|
'x',
|
|
'number++'
|
|
]]);
|
|
defineFunction('GAMMA', GAMMA).args([[
|
|
'x',
|
|
'number'
|
|
]]);
|
|
defineFunction('GAMMA.DIST', GAMMA_DIST).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'alpha',
|
|
'number++'
|
|
],
|
|
[
|
|
'beta',
|
|
'number++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('GAMMA.INV', GAMMA_INV).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'alpha',
|
|
'number++'
|
|
],
|
|
[
|
|
'beta',
|
|
'number++'
|
|
]
|
|
]);
|
|
defineFunction('NORM.S.DIST', NORM_S_DIST).args([
|
|
[
|
|
'z',
|
|
'number'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('NORM.S.INV', NORM_S_INV).args([[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('NORM.DIST', NORM_DIST).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'mean',
|
|
'number'
|
|
],
|
|
[
|
|
'stddev',
|
|
'number++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('NORM.INV', NORM_INV).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'mean',
|
|
'number'
|
|
],
|
|
[
|
|
'stddev',
|
|
'number++'
|
|
]
|
|
]);
|
|
defineFunction('BETADIST', BETADIST).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'alpha',
|
|
'number++'
|
|
],
|
|
[
|
|
'beta',
|
|
'number++'
|
|
],
|
|
[
|
|
'A',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'B',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$x >= $A',
|
|
'NUM'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$x <= $B',
|
|
'NUM'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$A < $B',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('BETA.DIST', BETA_DIST).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'alpha',
|
|
'number++'
|
|
],
|
|
[
|
|
'beta',
|
|
'number++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
],
|
|
[
|
|
'A',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'B',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$x >= $A',
|
|
'NUM'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$x <= $B',
|
|
'NUM'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$A < $B',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('BETA.INV', BETA_INV).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'alpha',
|
|
'number++'
|
|
],
|
|
[
|
|
'beta',
|
|
'number++'
|
|
],
|
|
[
|
|
'A',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'B',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('CHISQ.DIST', chisq_left).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('CHISQ.DIST.RT', chisq_right).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('CHISQ.INV', chisq_left_inv).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('CHISQ.INV.RT', chisq_right_inv).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('CHISQ.TEST', function (ac, ex) {
|
|
return chisq_test(ac.data, ex.data);
|
|
}).args([
|
|
[
|
|
'actual_range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'expected_range',
|
|
'matrix'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$actual_range.width == $expected_range.width'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$actual_range.height == $expected_range.height'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('EXPON.DIST', expon).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'lambda',
|
|
'number++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('POISSON.DIST', poisson).args([
|
|
[
|
|
'x',
|
|
'integer+'
|
|
],
|
|
[
|
|
'mean',
|
|
'number+'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('F.DIST', Fdist).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'deg_freedom1',
|
|
'integer++'
|
|
],
|
|
[
|
|
'deg_freedom2',
|
|
'integer++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('F.DIST.RT', Fdist_right).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'deg_freedom1',
|
|
'integer++'
|
|
],
|
|
[
|
|
'deg_freedom2',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('F.INV', Finv).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom1',
|
|
'integer++'
|
|
],
|
|
[
|
|
'deg_freedom2',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('F.INV.RT', Finv_right).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'[between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom1',
|
|
'integer++'
|
|
],
|
|
[
|
|
'deg_freedom2',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('F.TEST', Ftest).args([
|
|
[
|
|
'array1',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'array2',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length >= 2',
|
|
'DIV/0'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array2.length >= 2',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('FISHER', fisher).args([[
|
|
'x',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
-1,
|
|
1
|
|
]
|
|
]
|
|
]]);
|
|
defineFunction('FISHERINV', fisherinv).args([[
|
|
'y',
|
|
'number'
|
|
]]);
|
|
defineFunction('T.DIST', Tdist).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('T.DIST.RT', Tdist_right).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('T.DIST.2T', Tdist_2tail).args([
|
|
[
|
|
'x',
|
|
'number+'
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('T.INV', Tdist_inv).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('T.INV.2T', Tdist_2tail_inv).args([
|
|
[
|
|
'p',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between]',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'deg_freedom',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('T.TEST', Tdist_test).args([
|
|
[
|
|
'array1',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'array2',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'tails',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
1,
|
|
2
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
3
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$type != 1 || $array1.length == $array2.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array1.length >= 2',
|
|
'DIV/0'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array2.length >= 2',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('CONFIDENCE.T', confidence_t).args([
|
|
[
|
|
'alpha',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'standard_dev',
|
|
'number++'
|
|
],
|
|
[
|
|
'size',
|
|
[
|
|
'and',
|
|
'integer++',
|
|
[
|
|
'assert',
|
|
'$size != 1',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('CONFIDENCE.NORM', confidence_norm).args([
|
|
[
|
|
'alpha',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'standard_dev',
|
|
'number++'
|
|
],
|
|
[
|
|
'size',
|
|
[
|
|
'and',
|
|
'integer++'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('GAUSS', gauss).args([[
|
|
'z',
|
|
'number'
|
|
]]);
|
|
defineFunction('PHI', phi).args([[
|
|
'x',
|
|
'number'
|
|
]]);
|
|
defineFunction('LOGNORM.DIST', lognorm_dist).args([
|
|
[
|
|
'x',
|
|
'number++'
|
|
],
|
|
[
|
|
'mean',
|
|
'number'
|
|
],
|
|
[
|
|
'standard_dev',
|
|
'number++'
|
|
],
|
|
[
|
|
'cumulative',
|
|
'logical'
|
|
]
|
|
]);
|
|
defineFunction('LOGNORM.INV', lognorm_inv).args([
|
|
[
|
|
'probability',
|
|
[
|
|
'and',
|
|
'number',
|
|
[
|
|
'(between)',
|
|
0,
|
|
1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'mean',
|
|
'number'
|
|
],
|
|
[
|
|
'standard_dev',
|
|
'number++'
|
|
]
|
|
]);
|
|
defineFunction('PROB', prob).args([
|
|
[
|
|
'x_range',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'prob_range',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'lower_limit',
|
|
'number'
|
|
],
|
|
[
|
|
'upper_limit',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
'$lower_limit'
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$prob_range.length == $x_range.length',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('SLOPE', slope).args([
|
|
[
|
|
'known_y',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length == $known_y.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length > 0 && $known_y.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('INTERCEPT', intercept).args([
|
|
[
|
|
'known_y',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length == $known_y.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length > 0 && $known_y.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PEARSON', pearson).args([
|
|
[
|
|
'array1',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'array2',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array2.length == $array1.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$array2.length > 0 && $array1.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('RSQ', rsq).args([
|
|
[
|
|
'known_y',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length == $known_y.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length > 0 && $known_y.length > 0',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length != 1 && $known_y.length != 1',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('STEYX', steyx).args([
|
|
[
|
|
'known_y',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length == $known_y.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length >= 3 && $known_y.length >= 3',
|
|
'DIV/0'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('FORECAST', forecast).args([
|
|
[
|
|
'x',
|
|
'number'
|
|
],
|
|
[
|
|
'known_y',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length == $known_y.length',
|
|
'N/A'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$known_x.length > 0 && $known_y.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('LINEST', linest).args([
|
|
[
|
|
'known_y',
|
|
'matrix'
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'const',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'stats',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('LOGEST', logest).args([
|
|
[
|
|
'known_y',
|
|
'matrix'
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'const',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'stats',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('TREND', trend).args([
|
|
[
|
|
'known_y',
|
|
'matrix'
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'new_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'const',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('GROWTH', growth).args([
|
|
[
|
|
'known_y',
|
|
'matrix'
|
|
],
|
|
[
|
|
'known_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'new_x',
|
|
[
|
|
'or',
|
|
'matrix',
|
|
'null'
|
|
]
|
|
],
|
|
[
|
|
'const',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('FV', FV).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'nper',
|
|
'number'
|
|
],
|
|
[
|
|
'pmt',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'pv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$pmt || $pv'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PV', PV).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'nper',
|
|
'number'
|
|
],
|
|
[
|
|
'pmt',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$pmt || $fv'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PMT', PMT).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'nper',
|
|
'number'
|
|
],
|
|
[
|
|
'pmt',
|
|
'number'
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('NPER', NPER).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'pmt',
|
|
'number'
|
|
],
|
|
[
|
|
'pv',
|
|
'number'
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('RATE', RATE).args([
|
|
[
|
|
'nper',
|
|
'number'
|
|
],
|
|
[
|
|
'pmt',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'pv',
|
|
'number'
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'guess',
|
|
[
|
|
'or',
|
|
'number++',
|
|
[
|
|
'null',
|
|
0.01
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$pmt || $fv'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('IPMT', IPMT).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'per',
|
|
'number++'
|
|
],
|
|
[
|
|
'nper',
|
|
'number++'
|
|
],
|
|
[
|
|
'pv',
|
|
'number'
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$per >= 1 && $per <= $nper'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PPMT', PPMT).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'per',
|
|
'number++'
|
|
],
|
|
[
|
|
'nper',
|
|
'number++'
|
|
],
|
|
[
|
|
'pv',
|
|
'number'
|
|
],
|
|
[
|
|
'fv',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$per >= 1 && $per <= $nper'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('CUMPRINC', CUMPRINC).args([
|
|
[
|
|
'rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'nper',
|
|
'number++'
|
|
],
|
|
[
|
|
'pv',
|
|
'number++'
|
|
],
|
|
[
|
|
'start_period',
|
|
'number++'
|
|
],
|
|
[
|
|
'end_period',
|
|
'number++'
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$end_period >= $start_period',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('CUMIPMT', CUMIPMT).args([
|
|
[
|
|
'rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'nper',
|
|
'number++'
|
|
],
|
|
[
|
|
'pv',
|
|
'number++'
|
|
],
|
|
[
|
|
'start_period',
|
|
'number++'
|
|
],
|
|
[
|
|
'end_period',
|
|
'number++'
|
|
],
|
|
[
|
|
'type',
|
|
[
|
|
'or',
|
|
[
|
|
'values',
|
|
0,
|
|
1
|
|
],
|
|
[
|
|
'null',
|
|
0
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$end_period >= $start_period',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('NPV', NPV).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'values',
|
|
[
|
|
'collect',
|
|
'number'
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$values.length > 0',
|
|
'N/A'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('IRR', IRR).args([
|
|
[
|
|
'values',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'guess',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0.1
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('EFFECT', EFFECT).args([
|
|
[
|
|
'nominal_rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'npery',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('NOMINAL', NOMINAL).args([
|
|
[
|
|
'effect_rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'npery',
|
|
'integer++'
|
|
]
|
|
]);
|
|
defineFunction('XNPV', XNPV).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'values',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'dates',
|
|
[
|
|
'collect',
|
|
'date',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$values.length == $dates.length',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('XIRR', XIRR).args([
|
|
[
|
|
'values',
|
|
[
|
|
'collect',
|
|
'number',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'dates',
|
|
[
|
|
'collect',
|
|
'date',
|
|
1
|
|
]
|
|
],
|
|
[
|
|
'guess',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
0.1
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$values.length == $dates.length',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('ISPMT', ISPMT).args([
|
|
[
|
|
'rate',
|
|
'number'
|
|
],
|
|
[
|
|
'per',
|
|
'number++'
|
|
],
|
|
[
|
|
'nper',
|
|
'number++'
|
|
],
|
|
[
|
|
'pv',
|
|
'number'
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$per >= 1 && $per <= $nper'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('DB', DB).args([
|
|
[
|
|
'cost',
|
|
'number'
|
|
],
|
|
[
|
|
'salvage',
|
|
'number'
|
|
],
|
|
[
|
|
'life',
|
|
'number++'
|
|
],
|
|
[
|
|
'period',
|
|
'number++'
|
|
],
|
|
[
|
|
'month',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
12
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('DDB', DDB).args([
|
|
[
|
|
'cost',
|
|
'number'
|
|
],
|
|
[
|
|
'salvage',
|
|
'number'
|
|
],
|
|
[
|
|
'life',
|
|
'number++'
|
|
],
|
|
[
|
|
'period',
|
|
'number++'
|
|
],
|
|
[
|
|
'factor',
|
|
[
|
|
'or',
|
|
'number',
|
|
[
|
|
'null',
|
|
2
|
|
]
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('SLN', SLN).args([
|
|
[
|
|
'cost',
|
|
'number'
|
|
],
|
|
[
|
|
'salvage',
|
|
'number'
|
|
],
|
|
[
|
|
'life',
|
|
'number++'
|
|
]
|
|
]);
|
|
defineFunction('SYD', SYD).args([
|
|
[
|
|
'cost',
|
|
'number'
|
|
],
|
|
[
|
|
'salvage',
|
|
'number'
|
|
],
|
|
[
|
|
'life',
|
|
'number++'
|
|
],
|
|
[
|
|
'per',
|
|
'number++'
|
|
]
|
|
]);
|
|
defineFunction('VDB', VDB).args([
|
|
[
|
|
'cost',
|
|
'number+'
|
|
],
|
|
[
|
|
'salvage',
|
|
'number+'
|
|
],
|
|
[
|
|
'life',
|
|
'number++'
|
|
],
|
|
[
|
|
'start_period',
|
|
'number+'
|
|
],
|
|
[
|
|
'end_period',
|
|
'number+'
|
|
],
|
|
[
|
|
'factor',
|
|
[
|
|
'or',
|
|
'number+',
|
|
[
|
|
'null',
|
|
2
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'no_switch',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
false
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$end_period >= $start_period',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
var COUPS_ARGS = [
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'frequency',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
4
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
];
|
|
defineFunction('COUPDAYBS', COUPDAYBS).args(COUPS_ARGS);
|
|
defineFunction('COUPDAYS', COUPDAYS).args(COUPS_ARGS);
|
|
defineFunction('COUPDAYSNC', COUPDAYSNC).args(COUPS_ARGS);
|
|
defineFunction('COUPPCD', COUPPCD).args(COUPS_ARGS);
|
|
defineFunction('COUPNCD', COUPNCD).args(COUPS_ARGS);
|
|
defineFunction('COUPNUM', COUPNUM).args(COUPS_ARGS);
|
|
defineFunction('ACCRINTM', ACCRINTM).args([
|
|
[
|
|
'issue',
|
|
'date'
|
|
],
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'par',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
1000
|
|
],
|
|
'number++'
|
|
]
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$issue < $settlement',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('ACCRINT', ACCRINT).args([
|
|
[
|
|
'issue',
|
|
'date'
|
|
],
|
|
[
|
|
'first_interest',
|
|
'date'
|
|
],
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'par',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
1000
|
|
],
|
|
'number++'
|
|
]
|
|
],
|
|
[
|
|
'frequency',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
4
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'calc_method',
|
|
[
|
|
'or',
|
|
'logical',
|
|
[
|
|
'null',
|
|
true
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$issue < $settlement',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('DISC', DISC).args([
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'pr',
|
|
'number++'
|
|
],
|
|
[
|
|
'redemption',
|
|
'number++'
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('INTRATE', INTRATE).args([
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'investment',
|
|
'number++'
|
|
],
|
|
[
|
|
'redemption',
|
|
'number++'
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('RECEIVED', RECEIVED).args([
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'investment',
|
|
'number++'
|
|
],
|
|
[
|
|
'discount',
|
|
'number++'
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PRICE', PRICE).args([
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'rate',
|
|
'number++'
|
|
],
|
|
[
|
|
'yld',
|
|
'number++'
|
|
],
|
|
[
|
|
'redemption',
|
|
'number++'
|
|
],
|
|
[
|
|
'frequency',
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
1,
|
|
2,
|
|
4
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
defineFunction('PRICEDISC', PRICEDISC).args([
|
|
[
|
|
'settlement',
|
|
'date'
|
|
],
|
|
[
|
|
'maturity',
|
|
'date'
|
|
],
|
|
[
|
|
'discount',
|
|
'number++'
|
|
],
|
|
[
|
|
'redemption',
|
|
'number++'
|
|
],
|
|
[
|
|
'basis',
|
|
[
|
|
'or',
|
|
[
|
|
'null',
|
|
0
|
|
],
|
|
[
|
|
'and',
|
|
'integer',
|
|
[
|
|
'values',
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
]
|
|
]
|
|
],
|
|
[
|
|
'?',
|
|
[
|
|
'assert',
|
|
'$settlement < $maturity',
|
|
'NUM'
|
|
]
|
|
]
|
|
]);
|
|
var MAX_IT = 300, EPS = 2.2204e-16, FP_MIN = 1e-30, f_abs = Math.abs;
|
|
function ERF(x) {
|
|
if (f_abs(x) >= 3.3) {
|
|
return 1 - ERFC(x);
|
|
}
|
|
var S = x > 0 ? 1 : -1;
|
|
if (S == -1) {
|
|
x = -x;
|
|
}
|
|
var m = 0, an = 1;
|
|
for (var n = 1; n < 100; n++) {
|
|
m += an;
|
|
an *= 2 * x * x / (2 * n + 1);
|
|
}
|
|
return S * 2 / Math.sqrt(Math.PI) * x * Math.exp(-x * x) * m;
|
|
}
|
|
function ERFC(x) {
|
|
if (f_abs(x) < 3.3) {
|
|
return 1 - ERF(x);
|
|
}
|
|
var s = 1;
|
|
if (x < 0) {
|
|
s = -1;
|
|
x = -x;
|
|
}
|
|
var frac = x;
|
|
for (var n = 8; n >= 1; n -= 0.5) {
|
|
frac = x + n / frac;
|
|
}
|
|
frac = 1 / (x + frac);
|
|
return s == 1 ? Math.exp(-x * x) / Math.sqrt(Math.PI) * frac : 2 - Math.exp(-x * x) / Math.sqrt(Math.PI) * frac;
|
|
}
|
|
function GAMMALN(x) {
|
|
var cof = [
|
|
1.000000000190015,
|
|
76.18009172947146,
|
|
-86.50532032941678,
|
|
24.01409824083091,
|
|
-1.231739572450155,
|
|
0.001208650973866179,
|
|
-0.000005395239384953
|
|
];
|
|
var y = x, tmp = x + 5.5, ser = cof[0];
|
|
tmp -= (x + 0.5) * Math.log(tmp);
|
|
for (var j = 1; j <= 6; j++) {
|
|
y += 1;
|
|
ser += cof[j] / y;
|
|
}
|
|
return -tmp + Math.log(Math.sqrt(2 * Math.PI) * ser / x);
|
|
}
|
|
function GAMMA(x) {
|
|
if (x > 0) {
|
|
return Math.exp(GAMMALN(x));
|
|
}
|
|
var pi = Math.PI, y = -x;
|
|
return -pi / (y * GAMMA(y) * Math.sin(pi * y));
|
|
}
|
|
function BETALN(a, b) {
|
|
return GAMMALN(a) + GAMMALN(b) - GAMMALN(a + b);
|
|
}
|
|
function BETA(a, b) {
|
|
return Math.exp(BETALN(a, b));
|
|
}
|
|
function gamma_inc(a, x) {
|
|
return x < a + 1 ? g_series(a, x) : 1 - g_contfrac(a, x);
|
|
}
|
|
function g_series(a, x) {
|
|
var sum = 1 / a, frac = sum, ap = a;
|
|
var gln = GAMMALN(a), n;
|
|
for (n = 1; n <= MAX_IT; n++) {
|
|
ap++;
|
|
frac *= x / ap;
|
|
sum += frac;
|
|
if (f_abs(frac) < f_abs(sum) * EPS) {
|
|
break;
|
|
}
|
|
}
|
|
return sum * Math.exp(-x + a * Math.log(x) - gln);
|
|
}
|
|
function g_contfrac(a, x) {
|
|
var f = FP_MIN, c = f, d = 0, aj = 1, bj = x + 1 - a;
|
|
var gln = GAMMALN(a);
|
|
for (var i = 1; i <= MAX_IT; i++) {
|
|
d = bj + aj * d;
|
|
if (f_abs(d) < FP_MIN) {
|
|
d = FP_MIN;
|
|
}
|
|
c = bj + aj / c;
|
|
if (f_abs(c) < FP_MIN) {
|
|
c = FP_MIN;
|
|
}
|
|
d = 1 / d;
|
|
var delta = c * d;
|
|
f *= delta;
|
|
if (f_abs(delta - 1) < EPS) {
|
|
break;
|
|
}
|
|
bj += 2;
|
|
aj = -i * (i - a);
|
|
}
|
|
return f * Math.exp(-x - gln + a * Math.log(x));
|
|
}
|
|
function GAMMA_DIST(x, a, b, cumulative) {
|
|
if (!cumulative) {
|
|
return Math.pow(x / b, a - 1) * Math.exp(-x / b) / (b * GAMMA(a));
|
|
}
|
|
return gamma_inc(a, x / b);
|
|
}
|
|
function GAMMA_INV(p, a, b) {
|
|
if (p === 0) {
|
|
return 0;
|
|
}
|
|
if (p == 1) {
|
|
return Infinity;
|
|
}
|
|
var m = 0, M = 10, x = 0, ab = a * b;
|
|
if (ab > 1) {
|
|
M *= ab;
|
|
}
|
|
for (var i = 0; i < MAX_IT; i++) {
|
|
x = 0.5 * (m + M);
|
|
var q = GAMMA_DIST(x, a, b, true);
|
|
if (f_abs(p - q) < 1e-16) {
|
|
break;
|
|
}
|
|
if (q > p) {
|
|
M = x;
|
|
} else {
|
|
m = x;
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
function NORM_S_DIST(x, cumulative) {
|
|
if (!cumulative) {
|
|
return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
|
|
}
|
|
return 0.5 + 0.5 * ERF(x / Math.sqrt(2));
|
|
}
|
|
function NORM_S_INV(p) {
|
|
var a = [
|
|
-39.69683028665376,
|
|
220.9460984245205,
|
|
-275.9285104469687,
|
|
138.357751867269,
|
|
-30.66479806614716,
|
|
2.506628277459239
|
|
], b = [
|
|
-54.47609879822406,
|
|
161.5858368580409,
|
|
-155.6989798598866,
|
|
66.80131188771972,
|
|
-13.28068155288572
|
|
], c = [
|
|
-0.007784894002430293,
|
|
-0.3223964580411365,
|
|
-2.400758277161838,
|
|
-2.549732539343734,
|
|
4.374664141464968,
|
|
2.938163982698783
|
|
], d = [
|
|
0.007784695709041462,
|
|
0.3224671290700398,
|
|
2.445134137142996,
|
|
3.754408661907416
|
|
];
|
|
var plow = 0.02425, phigh = 1 - plow;
|
|
var q, r;
|
|
if (p < plow) {
|
|
q = Math.sqrt(-2 * Math.log(p));
|
|
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
}
|
|
if (phigh < p) {
|
|
q = Math.sqrt(-2 * Math.log(1 - p));
|
|
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
}
|
|
q = p - 0.5;
|
|
r = q * q;
|
|
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
|
|
}
|
|
function NORM_DIST(x, m, s, cumulative) {
|
|
if (!cumulative) {
|
|
return Math.exp(-(x - m) * (x - m) / (2 * s * s)) / (s * Math.sqrt(2 * Math.PI));
|
|
}
|
|
return NORM_S_DIST((x - m) / s, true);
|
|
}
|
|
function NORM_INV(p, m, s) {
|
|
return m + s * NORM_S_INV(p);
|
|
}
|
|
function betastd_pdf(x, a, b) {
|
|
return Math.exp((a - 1) * Math.log(x) + (b - 1) * Math.log(1 - x) - BETALN(a, b));
|
|
}
|
|
function betastd_cdf(x, a, b) {
|
|
var k = Math.exp(a * Math.log(x) + b * Math.log(1 - x) - BETALN(a, b));
|
|
return x < (a + 1) / (a + b + 2) ? k * beta_lentz(a, b, x) / a : 1 - k * beta_lentz(b, a, 1 - x) / b;
|
|
}
|
|
function beta_lentz(a, b, x) {
|
|
var m, m2;
|
|
var aa, c, d, del, h, qab, qam, qap;
|
|
qab = a + b;
|
|
qap = a + 1;
|
|
qam = a - 1;
|
|
c = 1;
|
|
d = 1 - qab * x / qap;
|
|
if (f_abs(d) < FP_MIN) {
|
|
d = FP_MIN;
|
|
}
|
|
d = 1 / d;
|
|
h = d;
|
|
for (m = 1; m <= MAX_IT; m++) {
|
|
m2 = 2 * m;
|
|
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
|
|
d = 1 + aa * d;
|
|
if (f_abs(d) < FP_MIN) {
|
|
d = FP_MIN;
|
|
}
|
|
c = 1 + aa / c;
|
|
if (f_abs(c) < FP_MIN) {
|
|
c = FP_MIN;
|
|
}
|
|
d = 1 / d;
|
|
h *= d * c;
|
|
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
|
|
d = 1 + aa * d;
|
|
if (f_abs(d) < FP_MIN) {
|
|
d = FP_MIN;
|
|
}
|
|
c = 1 + aa / c;
|
|
if (f_abs(c) < FP_MIN) {
|
|
c = FP_MIN;
|
|
}
|
|
d = 1 / d;
|
|
del = d * c;
|
|
h *= del;
|
|
if (f_abs(del - 1) < EPS) {
|
|
break;
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
function betastd_inv(p, a, b) {
|
|
var m = 0, M = 1, x = 0;
|
|
for (var i = 0; i < MAX_IT; i++) {
|
|
x = 0.5 * (m + M);
|
|
var q = betastd_cdf(x, a, b);
|
|
if (f_abs(p - q) < EPS) {
|
|
break;
|
|
}
|
|
if (q > p) {
|
|
M = x;
|
|
} else {
|
|
m = x;
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
function BETADIST(x, a, b, m, M) {
|
|
return betastd_cdf((x - m) / (M - m), a, b);
|
|
}
|
|
function BETA_DIST(x, a, b, cdf, m, M) {
|
|
if (cdf) {
|
|
return betastd_cdf((x - m) / (M - m), a, b);
|
|
}
|
|
return betastd_pdf((x - m) / (M - m), a, b) / (M - m);
|
|
}
|
|
function BETA_INV(p, a, b, m, M) {
|
|
return m + (M - m) * betastd_inv(p, a, b);
|
|
}
|
|
function chisq_left(x, n, cds) {
|
|
return GAMMA_DIST(x, n / 2, 2, cds);
|
|
}
|
|
function chisq_right(x, n) {
|
|
return 1 - chisq_left(x, n, true);
|
|
}
|
|
function chisq_left_inv(p, n) {
|
|
return GAMMA_INV(p, n / 2, 2);
|
|
}
|
|
function chisq_right_inv(p, n) {
|
|
return chisq_left_inv(1 - p, n);
|
|
}
|
|
function chisq_test(obsv, expect) {
|
|
var rows = obsv.length, cols = obsv[0].length;
|
|
var x = 0, i, j;
|
|
for (i = 0; i < rows; i++) {
|
|
for (j = 0; j < cols; j++) {
|
|
var eij = expect[i][j];
|
|
var delta = obsv[i][j] - eij;
|
|
delta *= delta;
|
|
x += delta / eij;
|
|
}
|
|
}
|
|
var n = (rows - 1) * (cols - 1);
|
|
return chisq_right(x, n);
|
|
}
|
|
function expon(x, r, cdf) {
|
|
if (cdf) {
|
|
return 1 - Math.exp(-r * x);
|
|
}
|
|
return r * Math.exp(-r * x);
|
|
}
|
|
function poisson(k, m, cdf) {
|
|
if (cdf) {
|
|
return 1 - chisq_left(2 * m, 2 * (k + 1), true);
|
|
}
|
|
var lnf = 0;
|
|
for (var i = 2; i <= k; i++) {
|
|
lnf += Math.log(i);
|
|
}
|
|
return Math.exp(k * Math.log(m) - m - lnf);
|
|
}
|
|
function Fdist(x, n, d, cdf) {
|
|
if (cdf) {
|
|
return betastd_cdf(n * x / (d + n * x), n / 2, d / 2);
|
|
}
|
|
var u = n / d;
|
|
n /= 2;
|
|
d /= 2;
|
|
return u / BETA(n, d) * Math.pow(u * x, n - 1) / Math.pow(1 + u * x, n + d);
|
|
}
|
|
function Fdist_right(x, n, d) {
|
|
return 1 - Fdist(x, n, d, true);
|
|
}
|
|
function Finv_right(p, n, d) {
|
|
return d / n * (1 / BETA_INV(p, d / 2, n / 2, 0, 1) - 1);
|
|
}
|
|
function Finv(p, n, d) {
|
|
return d / n * (1 / BETA_INV(1 - p, d / 2, n / 2, 0, 1) - 1);
|
|
}
|
|
function _mean(arr) {
|
|
var me = 0, n = arr.length;
|
|
for (var i = 0; i < n; i++) {
|
|
me += arr[i];
|
|
}
|
|
return me / n;
|
|
}
|
|
function _var_sq(arr, m) {
|
|
var v = 0, n = arr.length;
|
|
for (var i = 0; i < n; i++) {
|
|
var delta = arr[i] - m;
|
|
v += delta * delta;
|
|
}
|
|
return v / (n - 1);
|
|
}
|
|
function Ftest(arr1, arr2) {
|
|
var n1 = arr1.length - 1, n2 = arr2.length - 1;
|
|
var va1 = _var_sq(arr1, _mean(arr1)), va2 = _var_sq(arr2, _mean(arr2));
|
|
if (!va1 || !va2) {
|
|
throw new CalcError('DIV/0');
|
|
}
|
|
return 2 * Fdist(va1 / va2, n1, n2, true);
|
|
}
|
|
function fisher(x) {
|
|
return 0.5 * Math.log((1 + x) / (1 - x));
|
|
}
|
|
function fisherinv(x) {
|
|
var e2 = Math.exp(2 * x);
|
|
return (e2 - 1) / (e2 + 1);
|
|
}
|
|
function Tdist(x, n, cdf) {
|
|
if (cdf) {
|
|
return 1 - 0.5 * betastd_cdf(n / (x * x + n), n / 2, 0.5);
|
|
}
|
|
return 1 / (Math.sqrt(n) * BETA(0.5, n / 2)) * Math.pow(1 + x * x / n, -(n + 1) / 2);
|
|
}
|
|
function Tdist_right(x, n) {
|
|
return 1 - Tdist(x, n, true);
|
|
}
|
|
function Tdist_2tail(x, n) {
|
|
if (x < 0) {
|
|
x = -x;
|
|
}
|
|
return 2 * Tdist_right(x, n);
|
|
}
|
|
function Tdist_inv(p, n) {
|
|
var x = betastd_inv(2 * Math.min(p, 1 - p), n / 2, 0.5);
|
|
x = Math.sqrt(n * (1 - x) / x);
|
|
return p > 0.5 ? x : -x;
|
|
}
|
|
function Tdist_2tail_inv(p, n) {
|
|
return Tdist_inv(1 - p / 2, n);
|
|
}
|
|
function Tdist_test(gr1, gr2, tail, type) {
|
|
var n1 = gr1.length, n2 = gr2.length;
|
|
var t_st, df;
|
|
if (type == 1) {
|
|
var d = 0, d2 = 0;
|
|
for (var i = 0; i < n1; i++) {
|
|
var delta = gr1[i] - gr2[i];
|
|
d += delta;
|
|
d2 += delta * delta;
|
|
}
|
|
var md = d / n1;
|
|
t_st = md / Math.sqrt((d2 - d * md) / (n1 * (n1 - 1)));
|
|
return tail == 1 ? Tdist_right(t_st, n1 - 1) : Tdist_2tail(t_st, n1 - 1);
|
|
}
|
|
var m1 = _mean(gr1), m2 = _mean(gr2), v1 = _var_sq(gr1, m1), v2 = _var_sq(gr2, m2);
|
|
if (type == 3) {
|
|
var u1 = v1 / n1, u2 = v2 / n2, u = u1 + u2;
|
|
var q1 = u1 / u, q2 = u2 / u;
|
|
df = 1 / (q1 * q1 / (n1 - 1) + q2 * q2 / (n2 - 1));
|
|
t_st = f_abs(m1 - m2) / Math.sqrt(u);
|
|
return tail == 1 ? Tdist_right(t_st, df) : Tdist_2tail(t_st, df);
|
|
} else {
|
|
df = n1 + n2 - 2;
|
|
t_st = f_abs(m1 - m2) * Math.sqrt(df * n1 * n2 / ((n1 + n2) * ((n1 - 1) * v1 + (n2 - 1) * v2)));
|
|
return tail == 1 ? Tdist_right(t_st, df) : Tdist_2tail(t_st, df);
|
|
}
|
|
}
|
|
function confidence_t(alpha, stddev, size) {
|
|
return -Tdist_inv(alpha / 2, size - 1) * stddev / Math.sqrt(size);
|
|
}
|
|
function confidence_norm(alpha, stddev, size) {
|
|
return -NORM_S_INV(alpha / 2) * stddev / Math.sqrt(size);
|
|
}
|
|
function gauss(z) {
|
|
return NORM_S_DIST(z, true) - 0.5;
|
|
}
|
|
function phi(x) {
|
|
return NORM_S_DIST(x);
|
|
}
|
|
function lognorm_dist(x, m, s, cumulative) {
|
|
if (cumulative) {
|
|
return 0.5 + 0.5 * ERF((Math.log(x) - m) / (s * Math.sqrt(2)));
|
|
}
|
|
var t = Math.log(x) - m;
|
|
return Math.exp(-t * t / (2 * s * s)) / (x * s * Math.sqrt(2 * Math.PI));
|
|
}
|
|
function lognorm_inv(p, m, s) {
|
|
return Math.exp(NORM_INV(p, m, s));
|
|
}
|
|
function prob(x_, p_, lw, up) {
|
|
var n = x_.length;
|
|
var s = 0, i;
|
|
for (i = 0; i < n; i++) {
|
|
if (p_[i] <= 0 || p_[i] > 1) {
|
|
throw new CalcError('NUM');
|
|
}
|
|
s += p_[i];
|
|
}
|
|
if (s != 1) {
|
|
throw new CalcError('NUM');
|
|
}
|
|
var res = 0;
|
|
for (i = 0; i < n; i++) {
|
|
var x = x_[i];
|
|
if (x >= lw && x <= up) {
|
|
res += p_[i];
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
function slope(y_, x_) {
|
|
var mx = _mean(x_), my = _mean(y_), b1 = 0, b2 = 0;
|
|
for (var i = 0, n = y_.length; i < n; i++) {
|
|
var t = x_[i] - mx;
|
|
b1 += t * (y_[i] - my);
|
|
b2 += t * t;
|
|
}
|
|
return b1 / b2;
|
|
}
|
|
function intercept(y_, x_) {
|
|
var mx = _mean(x_), my = _mean(y_);
|
|
var b1 = 0, b2 = 0;
|
|
for (var i = 0, n = y_.length; i < n; i++) {
|
|
var t = x_[i] - mx;
|
|
b1 += t * (y_[i] - my);
|
|
b2 += t * t;
|
|
}
|
|
return my - b1 * mx / b2;
|
|
}
|
|
function pearson(x_, y_) {
|
|
var mx = _mean(x_), my = _mean(y_);
|
|
var s1 = 0, s2 = 0, s3 = 0;
|
|
for (var i = 0, n = x_.length; i < n; i++) {
|
|
var t1 = x_[i] - mx, t2 = y_[i] - my;
|
|
s1 += t1 * t2;
|
|
s2 += t1 * t1;
|
|
s3 += t2 * t2;
|
|
}
|
|
return s1 / Math.sqrt(s2 * s3);
|
|
}
|
|
function rsq(x_, y_) {
|
|
var r = pearson(x_, y_);
|
|
return r * r;
|
|
}
|
|
function steyx(y_, x_) {
|
|
var n = x_.length;
|
|
var mx = _mean(x_), my = _mean(y_);
|
|
var s1 = 0, s2 = 0, s3 = 0;
|
|
for (var i = 0; i < n; i++) {
|
|
var t1 = x_[i] - mx, t2 = y_[i] - my;
|
|
s1 += t2 * t2;
|
|
s2 += t1 * t2;
|
|
s3 += t1 * t1;
|
|
}
|
|
return Math.sqrt((s1 - s2 * s2 / s3) / (n - 2));
|
|
}
|
|
function forecast(x, y_, x_) {
|
|
var mx = _mean(x_), my = _mean(y_);
|
|
var s1 = 0, s2 = 0;
|
|
for (var i = 0, n = x_.length; i < n; i++) {
|
|
var t1 = x_[i] - mx, t2 = y_[i] - my;
|
|
s1 += t1 * t2;
|
|
s2 += t1 * t1;
|
|
}
|
|
if (s2 === 0) {
|
|
throw new CalcError('N/A');
|
|
}
|
|
var b = s1 / s2, a = my - b * mx;
|
|
return a + b * x;
|
|
}
|
|
function _mat_mean(Mat) {
|
|
var n = Mat.height, sum = 0;
|
|
for (var i = 0; i < n; i++) {
|
|
sum += Mat.data[i][0];
|
|
}
|
|
return sum / n;
|
|
}
|
|
function _mat_devsq(Mat, mean) {
|
|
var n = Mat.height, sq = 0;
|
|
for (var i = 0; i < n; i++) {
|
|
var x = Mat.data[i][0] - mean;
|
|
sq += x * x;
|
|
}
|
|
return sq;
|
|
}
|
|
function linest(Y, X, konst, stats) {
|
|
var i = 0;
|
|
if (!X) {
|
|
X = Y.map(function () {
|
|
return ++i;
|
|
});
|
|
}
|
|
if (konst) {
|
|
X = X.clone();
|
|
X.eachRow(function (row) {
|
|
X.data[row].unshift(1);
|
|
});
|
|
++X.width;
|
|
}
|
|
var Xt = X.transpose();
|
|
var B = Xt.multiply(X).inverse().multiply(Xt).multiply(Y);
|
|
var line_1 = [];
|
|
for (i = B.height - 1; i >= 0; i--) {
|
|
line_1.push(B.data[i][0]);
|
|
}
|
|
if (!konst) {
|
|
line_1.push(0);
|
|
}
|
|
if (!stats) {
|
|
return this.asMatrix([line_1]);
|
|
}
|
|
var Y1 = X.multiply(B);
|
|
var y_y1 = Y.adds(Y1, true);
|
|
var mp = !konst ? 0 : _mat_mean(Y1);
|
|
var SSreg = _mat_devsq(Y1, mp);
|
|
var me = !konst ? 0 : _mat_mean(y_y1);
|
|
var SSresid = _mat_devsq(y_y1, me);
|
|
var line_5 = [];
|
|
line_5.push(SSreg, SSresid);
|
|
var R2 = SSreg / (SSreg + SSresid);
|
|
var degfre = Y.height - X.width;
|
|
var err_est = Math.sqrt(SSresid / degfre);
|
|
var line_3 = [];
|
|
line_3.push(R2, err_est);
|
|
var F_sta = !konst ? R2 / X.width / ((1 - R2) / degfre) : SSreg / (X.width - 1) / (SSresid / degfre);
|
|
var line_4 = [];
|
|
line_4.push(F_sta, degfre);
|
|
var SCP = Xt.multiply(X).inverse();
|
|
var line_2 = [];
|
|
for (i = SCP.height - 1; i >= 0; i--) {
|
|
line_2.push(Math.sqrt(SCP.data[i][i] * SSresid / degfre));
|
|
}
|
|
return this.asMatrix([
|
|
line_1,
|
|
line_2,
|
|
line_3,
|
|
line_4,
|
|
line_5
|
|
]);
|
|
}
|
|
function logest(Y, X, konst, stats) {
|
|
return linest.call(this, Y.map(Math.log), X, konst, stats).map(Math.exp);
|
|
}
|
|
function trend(Y, X, W, konst) {
|
|
var i = 0;
|
|
if (!X) {
|
|
X = Y.map(function () {
|
|
return ++i;
|
|
});
|
|
}
|
|
if (konst) {
|
|
X = X.clone();
|
|
X.eachRow(function (row) {
|
|
X.data[row].unshift(1);
|
|
});
|
|
++X.width;
|
|
}
|
|
var Xt = X.transpose();
|
|
var B = Xt.multiply(X).inverse().multiply(Xt).multiply(Y);
|
|
if (!W) {
|
|
W = X;
|
|
} else {
|
|
if (konst) {
|
|
W = W.clone();
|
|
W.eachRow(function (row) {
|
|
W.data[row].unshift(1);
|
|
});
|
|
++W.width;
|
|
}
|
|
}
|
|
return W.multiply(B);
|
|
}
|
|
function growth(Y, X, new_X, konst) {
|
|
return trend.call(this, Y.map(Math.log), X, new_X, konst).map(Math.exp);
|
|
}
|
|
function root_newton(func, guess, max_it, eps) {
|
|
var MAX_IT = max_it || 20, EPS = eps || 1e-7;
|
|
var root = guess;
|
|
for (var j = 1; j <= MAX_IT; j++) {
|
|
var f_d = func(root), f = f_d[0], df = f_d[1];
|
|
var dx = f / df;
|
|
root -= dx;
|
|
if (Math.abs(dx) < EPS) {
|
|
return root;
|
|
}
|
|
}
|
|
return new CalcError('NUM');
|
|
}
|
|
function FV(rate, nper, pmt, pv, type) {
|
|
var h1 = Math.pow(1 + rate, nper);
|
|
var h2 = rate ? (h1 - 1) / rate : nper;
|
|
return -(pv * h1 + pmt * h2 * (1 + rate * type));
|
|
}
|
|
function PV(rate, nper, pmt, fv, type) {
|
|
if (!rate) {
|
|
return -fv - pmt * nper;
|
|
}
|
|
var h1 = Math.pow(1 + rate, nper);
|
|
return -(fv + pmt * (h1 - 1) / rate * (1 + rate * type)) / h1;
|
|
}
|
|
function PMT(rate, nper, pv, fv, type) {
|
|
if (!rate) {
|
|
return -(fv + pv) / nper;
|
|
}
|
|
var h1 = Math.pow(1 + rate, nper);
|
|
return -rate * (fv + pv * h1) / ((1 + rate * type) * (h1 - 1));
|
|
}
|
|
function NPER(rate, pmt, pv, fv, type) {
|
|
if (!rate) {
|
|
return -(fv + pv) / pmt;
|
|
}
|
|
var h1 = pmt * (1 + rate * type);
|
|
return Math.log((h1 - fv * rate) / (h1 + pv * rate)) / Math.log(1 + rate);
|
|
}
|
|
function RATE(nper, pmt, pv, fv, type, guess) {
|
|
function xfd(x) {
|
|
var h2 = Math.pow(1 + x, nper - 1), h1 = h2 * (1 + x);
|
|
return [
|
|
pv * h1 + pmt * (1 / x + type) * (h1 - 1) + fv,
|
|
nper * pv * h2 + pmt * (-(h1 - 1) / (x * x) + (1 / x + type) * nper * h2)
|
|
];
|
|
}
|
|
return root_newton(xfd, guess);
|
|
}
|
|
function IPMT(rate, per, nper, pv, fv, type) {
|
|
if (type == 1 && per == 1) {
|
|
return 0;
|
|
}
|
|
var pmt = PMT(rate, nper, pv, fv, type);
|
|
var ipmt = FV(rate, per - 1, pmt, pv, type) * rate;
|
|
return type ? ipmt / (1 + rate) : ipmt;
|
|
}
|
|
function PPMT(rate, per, nper, pv, fv, type) {
|
|
var pmt = PMT(rate, nper, pv, fv, type);
|
|
return pmt - IPMT(rate, per, nper, pv, fv, type);
|
|
}
|
|
function CUMPRINC(rate, nper, pv, start, end, type) {
|
|
if (type == 1) {
|
|
start--;
|
|
end--;
|
|
}
|
|
var tn = Math.pow(1 + rate, nper), ts = Math.pow(1 + rate, start - 1), te = Math.pow(1 + rate, end);
|
|
var monthlyPayment = rate * pv * tn / (tn - 1);
|
|
var remainingBalanceAtStart = ts * pv - (ts - 1) / rate * monthlyPayment;
|
|
var remainingBalanceAtEnd = te * pv - (te - 1) / rate * monthlyPayment;
|
|
return remainingBalanceAtEnd - remainingBalanceAtStart;
|
|
}
|
|
function CUMIPMT(rate, nper, pv, start, end, type) {
|
|
var cip = 0;
|
|
for (var i = start; i <= end; i++) {
|
|
cip += IPMT(rate, i, nper, pv, 0, type);
|
|
}
|
|
return cip;
|
|
}
|
|
function NPV(rate, flows) {
|
|
var npv = 0;
|
|
for (var i = 0, n = flows.length; i < n; i++) {
|
|
npv += flows[i] * Math.pow(1 + rate, -i - 1);
|
|
}
|
|
return npv;
|
|
}
|
|
function IRR(flows, guess) {
|
|
function xfd(x) {
|
|
var npv = 0, npv1 = 0;
|
|
for (var j = 0, n = flows.length; j < n; j++) {
|
|
npv += flows[j] * Math.pow(1 + x, -j - 1);
|
|
npv1 += -j * flows[j] * Math.pow(1 + x, -j - 2);
|
|
}
|
|
return [
|
|
npv,
|
|
npv1
|
|
];
|
|
}
|
|
return root_newton(xfd, guess);
|
|
}
|
|
function EFFECT(nominal_rate, npery) {
|
|
return Math.pow(1 + nominal_rate / npery, npery) - 1;
|
|
}
|
|
function NOMINAL(effect_rate, npery) {
|
|
return npery * (Math.pow(effect_rate + 1, 1 / npery) - 1);
|
|
}
|
|
function XNPV(rate, values, dates) {
|
|
var npv = 0;
|
|
for (var i = 0, n = values.length; i < n; i++) {
|
|
npv += values[i] * Math.pow(1 + rate, (dates[0] - dates[i]) / 365);
|
|
}
|
|
return npv;
|
|
}
|
|
function XIRR(values, dates, guess) {
|
|
function xfd(x) {
|
|
var npv = values[0], npv1 = 0;
|
|
for (var j = 1, n = values.length; j < n; j++) {
|
|
var delta = (dates[0] - dates[j]) / 365;
|
|
npv += values[j] * Math.pow(1 + x, delta);
|
|
npv1 += delta * values[j] * Math.pow(1 + x, delta - 1);
|
|
}
|
|
return [
|
|
npv,
|
|
npv1
|
|
];
|
|
}
|
|
return root_newton(xfd, guess);
|
|
}
|
|
function ISPMT(rate, per, nper, pv) {
|
|
var tmp = -pv * rate;
|
|
return tmp * (1 - per / nper);
|
|
}
|
|
function DB(cost, salvage, life, period, month) {
|
|
var rate = 1 - Math.pow(salvage / cost, 1 / life);
|
|
rate = Math.floor(rate * 1000 + 0.5) / 1000;
|
|
var db = cost * rate * month / 12;
|
|
if (period == 1) {
|
|
return db;
|
|
}
|
|
for (var i = 1; i < life; i++) {
|
|
if (i == period - 1) {
|
|
return (cost - db) * rate;
|
|
}
|
|
db += (cost - db) * rate;
|
|
}
|
|
return (cost - db) * rate * (12 - month) / 12;
|
|
}
|
|
function DDB(cost, salvage, life, period, factor) {
|
|
var f = factor / life;
|
|
var prior = -cost * (Math.pow(1 - f, period - 1) - 1);
|
|
var dep = (cost - prior) * f;
|
|
dep = Math.min(dep, Math.max(0, cost - prior - salvage));
|
|
return dep;
|
|
}
|
|
function SLN(cost, salvage, life) {
|
|
return (cost - salvage) / life;
|
|
}
|
|
function SYD(cost, salvage, life, per) {
|
|
return (cost - salvage) * (life - per + 1) * 2 / (life * (life + 1));
|
|
}
|
|
function VDB(cost, salvage, life, start, end, factor, no_switch) {
|
|
var interest = factor >= life ? 1 : factor / life;
|
|
function _getGDA(value, period) {
|
|
var gda, oldValue, newValue;
|
|
if (interest == 1) {
|
|
oldValue = period == 1 ? value : 0;
|
|
} else {
|
|
oldValue = value * Math.pow(1 - interest, period - 1);
|
|
}
|
|
newValue = value * Math.pow(1 - interest, period);
|
|
gda = newValue < salvage ? oldValue - salvage : oldValue - newValue;
|
|
return gda < 0 ? 0 : gda;
|
|
}
|
|
function _interVDB(cost, life1, period) {
|
|
var remValue = cost - salvage;
|
|
var intEnd = Math.ceil(period);
|
|
var term, lia = 0, vdb = 0, nowLia = false;
|
|
for (var i = 1; i <= intEnd; i++) {
|
|
if (!nowLia) {
|
|
var gda = _getGDA(cost, i);
|
|
lia = remValue / (life1 - i + 1);
|
|
if (lia > gda) {
|
|
term = lia;
|
|
nowLia = true;
|
|
} else {
|
|
term = gda;
|
|
remValue -= gda;
|
|
}
|
|
} else {
|
|
term = lia;
|
|
}
|
|
if (i == intEnd) {
|
|
term *= period + 1 - intEnd;
|
|
}
|
|
vdb += term;
|
|
}
|
|
return vdb;
|
|
}
|
|
var intStart = Math.floor(start), intEnd = Math.ceil(end);
|
|
var vdb = 0;
|
|
if (no_switch) {
|
|
for (var i = intStart + 1; i <= intEnd; i++) {
|
|
var term = _getGDA(cost, i);
|
|
if (i == intStart + 1) {
|
|
term *= Math.min(end, intStart + 1) - start;
|
|
} else {
|
|
if (i == intEnd) {
|
|
term *= end + 1 - intEnd;
|
|
}
|
|
}
|
|
vdb += term;
|
|
}
|
|
} else {
|
|
var life1 = life;
|
|
if (start != Math.floor(start)) {
|
|
if (factor > 1) {
|
|
if (start >= life / 2) {
|
|
var part = start - life / 2;
|
|
start = life / 2;
|
|
end -= part;
|
|
life1 += 1;
|
|
}
|
|
}
|
|
}
|
|
cost -= _interVDB(cost, life1, start);
|
|
vdb = _interVDB(cost, life - start, end - start);
|
|
}
|
|
return vdb;
|
|
}
|
|
function _edate(base, months) {
|
|
var d = unpackDate(base);
|
|
var m = d.month + months;
|
|
var y = d.year + Math.floor(m / 12);
|
|
m %= 12;
|
|
if (m < 0) {
|
|
m += 12;
|
|
}
|
|
d = Math.min(d.date, daysInMonth(y, m));
|
|
return packDate(y, m, d);
|
|
}
|
|
function _daysBetween(from, to, basis) {
|
|
if (basis == 1 || basis == 2 || basis == 3) {
|
|
return to - from;
|
|
}
|
|
return _days_360(from, to, basis);
|
|
}
|
|
function _borderCoupons(settlement, maturity, freq) {
|
|
var sett = unpackDate(settlement), base = unpackDate(maturity);
|
|
var periods = base.year - sett.year;
|
|
if (periods > 0) {
|
|
periods = (periods - 1) * freq;
|
|
}
|
|
var prev, next, months = 12 / freq;
|
|
do {
|
|
periods++;
|
|
prev = _edate(maturity, -periods * months);
|
|
} while (settlement < prev);
|
|
periods--;
|
|
next = _edate(maturity, -periods * months);
|
|
return [
|
|
prev,
|
|
next
|
|
];
|
|
}
|
|
function _borderCoupons_fw(first, settlement, freq) {
|
|
var sett = unpackDate(settlement), base = unpackDate(first);
|
|
var periods = sett.year - base.year;
|
|
if (periods > 0) {
|
|
periods = (periods - 1) * freq;
|
|
}
|
|
var prev = first, next, months = 12 / freq;
|
|
while (settlement > prev) {
|
|
next = prev;
|
|
periods++;
|
|
prev = _edate(first, periods * months);
|
|
}
|
|
return [
|
|
next,
|
|
prev
|
|
];
|
|
}
|
|
function COUPDAYBS(settlement, maturity, frequency, basis) {
|
|
var prev = _borderCoupons(settlement, maturity, frequency)[0];
|
|
return _daysBetween(prev, settlement, basis);
|
|
}
|
|
function COUPDAYS(settl, matur, freq, basis) {
|
|
if (basis == 1) {
|
|
var borders = _borderCoupons(settl, matur, freq);
|
|
return _daysBetween(borders[0], borders[1], 1);
|
|
}
|
|
if (basis == 3) {
|
|
return 365 / freq;
|
|
}
|
|
return 360 / freq;
|
|
}
|
|
function COUPDAYSNC(settl, matur, freq, basis) {
|
|
var next = _borderCoupons(settl, matur, freq)[1];
|
|
return _daysBetween(settl, next, basis);
|
|
}
|
|
function COUPPCD(settl, matur, freq) {
|
|
return _borderCoupons(settl, matur, freq)[0];
|
|
}
|
|
function COUPNCD(settl, matur, freq) {
|
|
return _borderCoupons(settl, matur, freq)[1];
|
|
}
|
|
function COUPNUM(settl, matur, freq) {
|
|
var sett = unpackDate(settl), mat = unpackDate(matur);
|
|
var months = 12 * (mat.year - sett.year) + mat.month - sett.month;
|
|
return 1 + (months * freq / 12 | 0);
|
|
}
|
|
function daysInYear(yr, basis) {
|
|
if (basis == 3) {
|
|
return 365;
|
|
}
|
|
if (basis == 1) {
|
|
return isLeapYear(yr) ? 366 : 365;
|
|
}
|
|
return 360;
|
|
}
|
|
function ACCRINTM(issue, maturity, rate, par, basis) {
|
|
var year_days = daysInYear(unpackDate(maturity).year, basis);
|
|
return rate * par * _daysBetween(issue, maturity, basis) / year_days;
|
|
}
|
|
function ACCRINT(issue, first, settl, rate, par, freq, basis, calc) {
|
|
var accr = 0, cost = par * rate / freq;
|
|
var brace, prev, next, prev1, next1, nrc;
|
|
var annual = basis % 2 === 0 ? 360 : 365;
|
|
function _numCoupons(from, to) {
|
|
return (to - from) * freq / annual | 0;
|
|
}
|
|
if (settl <= first) {
|
|
brace = _borderCoupons(settl, first, freq);
|
|
prev = brace[0];
|
|
next = brace[1];
|
|
if (prev <= issue) {
|
|
return cost * _daysBetween(issue, settl, basis) / _daysBetween(prev, next, basis);
|
|
}
|
|
brace = _borderCoupons(issue, prev, freq);
|
|
prev1 = brace[0];
|
|
next1 = brace[1];
|
|
nrc = _numCoupons(next1, settl);
|
|
return cost * (nrc + _daysBetween(issue, next1, basis) / _daysBetween(prev1, next1, basis) + (settl < next ? _daysBetween(prev, settl, basis) / _daysBetween(prev, next, basis) : 0));
|
|
} else {
|
|
brace = _borderCoupons_fw(first, settl, freq);
|
|
prev = brace[0];
|
|
next = brace[1];
|
|
nrc = _numCoupons(first, settl);
|
|
if (next == settl) {
|
|
accr = cost * nrc;
|
|
} else {
|
|
accr = cost * (nrc + _daysBetween(prev, settl, basis) / _daysBetween(prev, next, basis));
|
|
}
|
|
if (!calc) {
|
|
return accr;
|
|
}
|
|
brace = _borderCoupons(issue, first, freq);
|
|
prev = brace[0];
|
|
next = brace[1];
|
|
nrc = _numCoupons(issue, first);
|
|
accr += cost * (nrc + _daysBetween(issue, next, basis) / _daysBetween(prev, next, basis));
|
|
return accr;
|
|
}
|
|
}
|
|
function DISC(settl, matur, pr, redemption, basis) {
|
|
var annual = basis % 2 === 0 ? 360 : isLeapYear(unpackDate(settl).year) ? 366 : 365;
|
|
return (redemption - pr) / redemption * annual / _daysBetween(settl, matur, basis);
|
|
}
|
|
function INTRATE(settl, matur, investment, redemption, basis) {
|
|
var annual = basis % 2 === 0 ? 360 : isLeapYear(unpackDate(settl).year) ? 366 : 365;
|
|
return (redemption - investment) / investment * annual / _daysBetween(settl, matur, basis);
|
|
}
|
|
function RECEIVED(settl, matur, investment, discount, basis) {
|
|
var annual = basis % 2 === 0 ? 360 : isLeapYear(unpackDate(settl).year) ? 366 : 365;
|
|
return investment / (1 - discount * _daysBetween(settl, matur, basis) / annual);
|
|
}
|
|
function PRICE(settl, matur, rate, yld, redemption, freq, basis) {
|
|
var N = 1 + ((matur - settl) * freq / (basis % 2 === 0 ? 360 : 365) | 0);
|
|
var brace = _borderCoupons(settl, matur, freq), prev = brace[0], next = brace[1];
|
|
var beg_settl = _daysBetween(prev, settl, basis), settl_end = _daysBetween(settl, next, basis), beg_end = _daysBetween(prev, next, basis);
|
|
var den = 100 * rate / freq, yf = yld / freq, frac = settl_end / beg_end;
|
|
if (N == 1) {
|
|
return (redemption + den) / (1 + frac * yf) - beg_settl / beg_end * den;
|
|
}
|
|
return redemption / Math.pow(1 + yf, N - 1 + frac) + den * Math.pow(1 + yf, 1 - N - frac) * (Math.pow(1 + yf, N) - 1) / yf - beg_settl / beg_end * den;
|
|
}
|
|
function PRICEDISC(settl, matur, discount, redemption, basis) {
|
|
var dsm = _daysBetween(settl, matur, basis), dy = daysInYear(unpackDate(matur).year, basis);
|
|
return redemption - discount * redemption * dsm / dy;
|
|
}
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/borderpalette', [
|
|
'kendo.core',
|
|
'kendo.colorpicker',
|
|
'kendo.popup'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var BORDER_TYPES = [
|
|
'allBorders',
|
|
'insideBorders',
|
|
'insideHorizontalBorders',
|
|
'insideVerticalBorders',
|
|
'outsideBorders',
|
|
'leftBorder',
|
|
'topBorder',
|
|
'rightBorder',
|
|
'bottomBorder',
|
|
'noBorders'
|
|
];
|
|
var BORDER_PALETTE_MESSAGES = kendo.spreadsheet.messages.borderPalette = {
|
|
allBorders: 'All borders',
|
|
insideBorders: 'Inside borders',
|
|
insideHorizontalBorders: 'Inside horizontal borders',
|
|
insideVerticalBorders: 'Inside vertical borders',
|
|
outsideBorders: 'Outside borders',
|
|
leftBorder: 'Left border',
|
|
topBorder: 'Top border',
|
|
rightBorder: 'Right border',
|
|
bottomBorder: 'Bottom border',
|
|
noBorders: 'No border',
|
|
reset: 'Reset color',
|
|
customColor: 'Custom color...',
|
|
apply: 'Apply',
|
|
cancel: 'Cancel'
|
|
};
|
|
var ColorChooser = kendo.ui.Widget.extend({
|
|
init: function (element, options) {
|
|
kendo.ui.Widget.call(this, element, options);
|
|
this.element = element;
|
|
this.color = options.color;
|
|
this._resetButton();
|
|
this._colorPalette();
|
|
this._customColorPalette();
|
|
this._customColorButton();
|
|
this.resetButton.on('click', this.resetColor.bind(this));
|
|
this.customColorButton.on('click', this.customColor.bind(this));
|
|
},
|
|
options: { name: 'ColorChooser' },
|
|
events: ['change'],
|
|
destroy: function () {
|
|
kendo.unbind(this.dialog.element.find('.k-action-buttons'));
|
|
this.dialog.destroy();
|
|
this.colorPalette.destroy();
|
|
this.resetButton.off('click');
|
|
this.customColorButton.off('click');
|
|
},
|
|
value: function (value) {
|
|
if (value !== undefined) {
|
|
this.color = value;
|
|
this.customColorButton.find('.k-icon').css('background-color', this.color);
|
|
this.colorPalette.value(null);
|
|
this.flatColorPicker.value(this.color);
|
|
} else {
|
|
return this.color;
|
|
}
|
|
},
|
|
_change: function (value) {
|
|
this.color = value;
|
|
this.trigger('change', { value: value });
|
|
},
|
|
_colorPalette: function () {
|
|
var element = $('<div />', { 'class': 'k-spreadsheet-color-palette' });
|
|
var colorPalette = this.colorPalette = $('<div />').kendoColorPalette({
|
|
palette: [
|
|
'#ffffff',
|
|
'#000000',
|
|
'#d6ecff',
|
|
'#4e5b6f',
|
|
'#7fd13b',
|
|
'#ea157a',
|
|
'#feb80a',
|
|
'#00addc',
|
|
'#738ac8',
|
|
'#1ab39f',
|
|
'#f2f2f2',
|
|
'#7f7f7f',
|
|
'#a7d6ff',
|
|
'#d9dde4',
|
|
'#e5f5d7',
|
|
'#fad0e4',
|
|
'#fef0cd',
|
|
'#c5f2ff',
|
|
'#e2e7f4',
|
|
'#c9f7f1',
|
|
'#d8d8d8',
|
|
'#595959',
|
|
'#60b5ff',
|
|
'#b3bcca',
|
|
'#cbecb0',
|
|
'#f6a1c9',
|
|
'#fee29c',
|
|
'#8be6ff',
|
|
'#c7d0e9',
|
|
'#94efe3',
|
|
'#bfbfbf',
|
|
'#3f3f3f',
|
|
'#007dea',
|
|
'#8d9baf',
|
|
'#b2e389',
|
|
'#f272af',
|
|
'#fed46b',
|
|
'#51d9ff',
|
|
'#aab8de',
|
|
'#5fe7d5',
|
|
'#a5a5a5',
|
|
'#262626',
|
|
'#003e75',
|
|
'#3a4453',
|
|
'#5ea226',
|
|
'#af0f5b',
|
|
'#c58c00',
|
|
'#0081a5',
|
|
'#425ea9',
|
|
'#138677',
|
|
'#7f7f7f',
|
|
'#0c0c0c',
|
|
'#00192e',
|
|
'#272d37',
|
|
'#3f6c19',
|
|
'#750a3d',
|
|
'#835d00',
|
|
'#00566e',
|
|
'#2c3f71',
|
|
'#0c594f'
|
|
],
|
|
value: this.color,
|
|
change: function (e) {
|
|
this.customColorButton.find('.k-icon').css('background-color', 'transparent');
|
|
this.flatColorPicker.value(null);
|
|
this._change(e.value);
|
|
}.bind(this)
|
|
}).data('kendoColorPalette');
|
|
element.append(colorPalette.wrapper).appendTo(this.element);
|
|
},
|
|
_customColorPalette: function () {
|
|
var element = $('<div />', {
|
|
'class': 'k-spreadsheet-window',
|
|
'html': '<div></div>' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: apply\'>' + BORDER_PALETTE_MESSAGES.apply + '</button>' + '<button class=\'k-button\' data-bind=\'click: close\'>' + BORDER_PALETTE_MESSAGES.cancel + '</button>' + '</div>'
|
|
});
|
|
var dialog = this.dialog = element.appendTo(document.body).kendoWindow({
|
|
animation: false,
|
|
scrollable: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
modal: true,
|
|
visible: false,
|
|
width: 268,
|
|
open: function () {
|
|
this.center();
|
|
}
|
|
}).data('kendoWindow');
|
|
dialog.one('activate', function () {
|
|
this.element.find('[data-role=flatcolorpicker]').data('kendoFlatColorPicker')._hueSlider.resize();
|
|
});
|
|
var flatColorPicker = this.flatColorPicker = dialog.element.children().first().kendoFlatColorPicker().data('kendoFlatColorPicker');
|
|
var viewModel = kendo.observable({
|
|
apply: function () {
|
|
this.customColorButton.find('.k-icon').css('background-color', flatColorPicker.value());
|
|
this.colorPalette.value(null);
|
|
this._change(flatColorPicker.value());
|
|
dialog.close();
|
|
}.bind(this),
|
|
close: function () {
|
|
flatColorPicker.value(null);
|
|
dialog.close();
|
|
}
|
|
});
|
|
kendo.bind(dialog.element.find('.k-action-buttons'), viewModel);
|
|
},
|
|
_resetButton: function () {
|
|
this.resetButton = $('<a class=\'k-button k-reset-color\' href=\'#\'>' + '<span class=\'k-icon k-font-icon k-i-reset-color\'></span>' + BORDER_PALETTE_MESSAGES.reset + '</a>').appendTo(this.element);
|
|
},
|
|
_customColorButton: function () {
|
|
this.customColorButton = $('<a class=\'k-button k-custom-color\' href=\'#\'>' + '<span class=\'k-icon\'></span>' + BORDER_PALETTE_MESSAGES.customColor + '</a>').appendTo(this.element);
|
|
},
|
|
resetColor: function () {
|
|
this.colorPalette.value(null);
|
|
this.flatColorPicker.value(null);
|
|
this._change(null);
|
|
},
|
|
customColor: function () {
|
|
this.dialog.open();
|
|
}
|
|
});
|
|
var BorderPalette = kendo.ui.Widget.extend({
|
|
init: function (element, options) {
|
|
kendo.ui.Widget.call(this, element, options);
|
|
this.element = element;
|
|
this.color = '#000';
|
|
this.element.addClass('k-spreadsheet-border-palette');
|
|
this._borderTypePalette();
|
|
this._borderColorPalette();
|
|
this.element.on('click', '.k-spreadsheet-border-type-palette .k-button', this._click.bind(this));
|
|
},
|
|
options: { name: 'BorderPalette' },
|
|
events: ['change'],
|
|
destroy: function () {
|
|
this.colorChooser.destroy();
|
|
this.element.off('click');
|
|
},
|
|
_borderTypePalette: function () {
|
|
var messages = BORDER_PALETTE_MESSAGES;
|
|
var buttons = BORDER_TYPES.map(function (type) {
|
|
return '<a title="' + messages[type] + '" href="#" data-border-type="' + type + '" class="k-button k-button-icon">' + '<span class="k-sprite k-font-icon k-icon k-i-' + kendo.toHyphens(type) + '"></span>' + '</a>';
|
|
}).join('');
|
|
var element = $('<div />', {
|
|
'class': 'k-spreadsheet-border-type-palette',
|
|
'html': buttons
|
|
});
|
|
element.appendTo(this.element);
|
|
},
|
|
_borderColorPalette: function () {
|
|
var element = $('<div />', { 'class': 'k-spreadsheet-border-color-palette' });
|
|
element.appendTo(this.element);
|
|
this.colorChooser = new ColorChooser(element, {
|
|
color: this.color,
|
|
change: this._colorChange.bind(this)
|
|
});
|
|
},
|
|
_click: function (e) {
|
|
this.type = $(e.currentTarget).data('borderType');
|
|
this.trigger('change', {
|
|
type: this.type,
|
|
color: this.color
|
|
});
|
|
},
|
|
_colorChange: function (e) {
|
|
this.color = e.value;
|
|
if (this.type) {
|
|
this.trigger('change', {
|
|
type: this.type,
|
|
color: this.color
|
|
});
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.ColorChooser = ColorChooser;
|
|
kendo.spreadsheet.BorderPalette = BorderPalette;
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/toolbar', [
|
|
'kendo.toolbar',
|
|
'kendo.colorpicker',
|
|
'kendo.combobox',
|
|
'kendo.dropdownlist',
|
|
'kendo.popup',
|
|
'spreadsheet/borderpalette'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var ToolBar = kendo.ui.ToolBar;
|
|
var MESSAGES = kendo.spreadsheet.messages.toolbar = {
|
|
addColumnLeft: 'Add column left',
|
|
addColumnRight: 'Add column right',
|
|
addRowAbove: 'Add row above',
|
|
addRowBelow: 'Add row below',
|
|
alignment: 'Alignment',
|
|
alignmentButtons: {
|
|
justtifyLeft: 'Align left',
|
|
justifyCenter: 'Center',
|
|
justifyRight: 'Align right',
|
|
justifyFull: 'Justify',
|
|
alignTop: 'Align top',
|
|
alignMiddle: 'Align middle',
|
|
alignBottom: 'Align bottom'
|
|
},
|
|
backgroundColor: 'Background',
|
|
bold: 'Bold',
|
|
borders: 'Borders',
|
|
colorPicker: {
|
|
reset: 'Reset color',
|
|
customColor: 'Custom color...'
|
|
},
|
|
copy: 'Copy',
|
|
cut: 'Cut',
|
|
deleteColumn: 'Delete column',
|
|
deleteRow: 'Delete row',
|
|
filter: 'Filter',
|
|
fontFamily: 'Font',
|
|
fontSize: 'Font size',
|
|
format: 'Custom format...',
|
|
formatTypes: {
|
|
automatic: 'Automatic',
|
|
number: 'Number',
|
|
percent: 'Percent',
|
|
financial: 'Financial',
|
|
currency: 'Currency',
|
|
date: 'Date',
|
|
time: 'Time',
|
|
dateTime: 'Date time',
|
|
duration: 'Duration',
|
|
moreFormats: 'More formats...'
|
|
},
|
|
formatDecreaseDecimal: 'Decrease decimal',
|
|
formatIncreaseDecimal: 'Increase decimal',
|
|
freeze: 'Freeze panes',
|
|
freezeButtons: {
|
|
freezePanes: 'Freeze panes',
|
|
freezeRows: 'Freeze rows',
|
|
freezeColumns: 'Freeze columns',
|
|
unfreeze: 'Unfreeze panes'
|
|
},
|
|
italic: 'Italic',
|
|
merge: 'Merge cells',
|
|
mergeButtons: {
|
|
mergeCells: 'Merge all',
|
|
mergeHorizontally: 'Merge horizontally',
|
|
mergeVertically: 'Merge vertically',
|
|
unmerge: 'Unmerge'
|
|
},
|
|
open: 'Open...',
|
|
paste: 'Paste',
|
|
quickAccess: {
|
|
redo: 'Redo',
|
|
undo: 'Undo'
|
|
},
|
|
exportAs: 'Export...',
|
|
sortAsc: 'Sort ascending',
|
|
sortDesc: 'Sort descending',
|
|
sortButtons: {
|
|
sortSheetAsc: 'Sort sheet A to Z',
|
|
sortSheetDesc: 'Sort sheet Z to A',
|
|
sortRangeAsc: 'Sort range A to Z',
|
|
sortRangeDesc: 'Sort range Z to A'
|
|
},
|
|
textColor: 'Text Color',
|
|
textWrap: 'Wrap text',
|
|
underline: 'Underline',
|
|
validation: 'Data validation...'
|
|
};
|
|
var defaultTools = {
|
|
home: [
|
|
'open',
|
|
'exportAs',
|
|
[
|
|
'cut',
|
|
'copy',
|
|
'paste'
|
|
],
|
|
[
|
|
'bold',
|
|
'italic',
|
|
'underline'
|
|
],
|
|
'backgroundColor',
|
|
'textColor',
|
|
'borders',
|
|
'fontSize',
|
|
'fontFamily',
|
|
'alignment',
|
|
'textWrap',
|
|
[
|
|
'formatDecreaseDecimal',
|
|
'formatIncreaseDecimal'
|
|
],
|
|
'format',
|
|
'merge',
|
|
'freeze',
|
|
'filter'
|
|
],
|
|
insert: [
|
|
[
|
|
'addColumnLeft',
|
|
'addColumnRight',
|
|
'addRowBelow',
|
|
'addRowAbove'
|
|
],
|
|
[
|
|
'deleteColumn',
|
|
'deleteRow'
|
|
]
|
|
],
|
|
data: [
|
|
'sort',
|
|
'filter',
|
|
'validation'
|
|
]
|
|
};
|
|
var toolDefaults = {
|
|
open: {
|
|
type: 'open',
|
|
overflow: 'never',
|
|
iconClass: 'xlsa'
|
|
},
|
|
exportAs: {
|
|
type: 'exportAsDialog',
|
|
dialogName: 'exportAs',
|
|
overflow: 'never',
|
|
text: '',
|
|
iconClass: 'xlsa'
|
|
},
|
|
bold: {
|
|
type: 'button',
|
|
command: 'PropertyChangeCommand',
|
|
property: 'bold',
|
|
value: true,
|
|
iconClass: 'bold',
|
|
togglable: true
|
|
},
|
|
italic: {
|
|
type: 'button',
|
|
command: 'PropertyChangeCommand',
|
|
property: 'italic',
|
|
value: true,
|
|
iconClass: 'italic',
|
|
togglable: true
|
|
},
|
|
underline: {
|
|
type: 'button',
|
|
command: 'PropertyChangeCommand',
|
|
property: 'underline',
|
|
value: true,
|
|
iconClass: 'underline',
|
|
togglable: true
|
|
},
|
|
formatDecreaseDecimal: {
|
|
type: 'button',
|
|
command: 'AdjustDecimalsCommand',
|
|
value: -1,
|
|
iconClass: 'decrease-decimal'
|
|
},
|
|
formatIncreaseDecimal: {
|
|
type: 'button',
|
|
command: 'AdjustDecimalsCommand',
|
|
value: +1,
|
|
iconClass: 'increase-decimal'
|
|
},
|
|
textWrap: {
|
|
type: 'button',
|
|
command: 'TextWrapCommand',
|
|
property: 'wrap',
|
|
value: true,
|
|
iconClass: 'text-wrap',
|
|
togglable: true
|
|
},
|
|
cut: {
|
|
type: 'button',
|
|
command: 'ToolbarCutCommand',
|
|
iconClass: 'cut'
|
|
},
|
|
copy: {
|
|
type: 'button',
|
|
command: 'ToolbarCopyCommand',
|
|
iconClass: 'copy'
|
|
},
|
|
paste: {
|
|
type: 'button',
|
|
command: 'ToolbarPasteCommand',
|
|
iconClass: 'paste'
|
|
},
|
|
separator: { type: 'separator' },
|
|
alignment: {
|
|
type: 'alignment',
|
|
iconClass: 'justify-left'
|
|
},
|
|
backgroundColor: {
|
|
type: 'colorPicker',
|
|
property: 'background',
|
|
iconClass: 'background'
|
|
},
|
|
textColor: {
|
|
type: 'colorPicker',
|
|
property: 'color',
|
|
iconClass: 'text'
|
|
},
|
|
fontFamily: {
|
|
type: 'fontFamily',
|
|
property: 'fontFamily',
|
|
iconClass: 'text'
|
|
},
|
|
fontSize: {
|
|
type: 'fontSize',
|
|
property: 'fontSize',
|
|
iconClass: 'font-size'
|
|
},
|
|
format: {
|
|
type: 'format',
|
|
property: 'format',
|
|
iconClass: 'format-number'
|
|
},
|
|
filter: {
|
|
type: 'filter',
|
|
property: 'hasFilter',
|
|
iconClass: 'filter'
|
|
},
|
|
merge: {
|
|
type: 'merge',
|
|
iconClass: 'merge-cells'
|
|
},
|
|
freeze: {
|
|
type: 'freeze',
|
|
iconClass: 'freeze-panes'
|
|
},
|
|
borders: {
|
|
type: 'borders',
|
|
iconClass: 'all-borders'
|
|
},
|
|
formatCells: {
|
|
type: 'dialog',
|
|
dialogName: 'formatCells',
|
|
overflow: 'never'
|
|
},
|
|
addColumnLeft: {
|
|
type: 'button',
|
|
command: 'AddColumnCommand',
|
|
value: 'left',
|
|
iconClass: 'add-column-left'
|
|
},
|
|
addColumnRight: {
|
|
type: 'button',
|
|
command: 'AddColumnCommand',
|
|
value: 'right',
|
|
iconClass: 'add-column-right'
|
|
},
|
|
addRowBelow: {
|
|
type: 'button',
|
|
command: 'AddRowCommand',
|
|
value: 'below',
|
|
iconClass: 'add-row-below'
|
|
},
|
|
addRowAbove: {
|
|
type: 'button',
|
|
command: 'AddRowCommand',
|
|
value: 'above',
|
|
iconClass: 'add-row-above'
|
|
},
|
|
deleteColumn: {
|
|
type: 'button',
|
|
command: 'DeleteColumnCommand',
|
|
iconClass: 'delete-column'
|
|
},
|
|
deleteRow: {
|
|
type: 'button',
|
|
command: 'DeleteRowCommand',
|
|
iconClass: 'delete-row'
|
|
},
|
|
sort: {
|
|
type: 'sort',
|
|
iconClass: 'sort-desc'
|
|
},
|
|
validation: {
|
|
type: 'dialog',
|
|
dialogName: 'validation',
|
|
iconClass: 'exception',
|
|
overflow: 'never'
|
|
}
|
|
};
|
|
var SpreadsheetToolBar = ToolBar.extend({
|
|
init: function (element, options) {
|
|
options.items = this._expandTools(options.tools || SpreadsheetToolBar.prototype.options.tools[options.toolbarName]);
|
|
ToolBar.fn.init.call(this, element, options);
|
|
var handleClick = this._click.bind(this);
|
|
this.element.addClass('k-spreadsheet-toolbar');
|
|
this._addSeparators(this.element);
|
|
this.bind({
|
|
click: handleClick,
|
|
toggle: handleClick
|
|
});
|
|
},
|
|
_addSeparators: function (element) {
|
|
var groups = element.children('.k-widget, a.k-button, .k-button-group');
|
|
groups.before('<span class=\'k-separator\' />');
|
|
},
|
|
_expandTools: function (tools) {
|
|
function expandTool(toolName) {
|
|
var options = $.isPlainObject(toolName) ? toolName : toolDefaults[toolName] || {};
|
|
var spriteCssClass = 'k-icon k-font-icon k-i-' + options.iconClass;
|
|
var type = options.type;
|
|
var typeDefaults = {
|
|
splitButton: { spriteCssClass: spriteCssClass },
|
|
button: { showText: 'overflow' },
|
|
colorPicker: { toolIcon: spriteCssClass }
|
|
};
|
|
var tool = $.extend({
|
|
name: options.name || toolName,
|
|
text: MESSAGES[options.name || toolName],
|
|
spriteCssClass: spriteCssClass,
|
|
attributes: { title: MESSAGES[options.name || toolName] }
|
|
}, typeDefaults[type], options);
|
|
if (type == 'splitButton') {
|
|
tool.menuButtons = tool.menuButtons.map(expandTool);
|
|
}
|
|
tool.attributes['data-tool'] = toolName;
|
|
if (options.property) {
|
|
tool.attributes['data-property'] = options.property;
|
|
}
|
|
return tool;
|
|
}
|
|
return tools.reduce(function (tools, tool) {
|
|
if ($.isArray(tool)) {
|
|
tools.push({
|
|
type: 'buttonGroup',
|
|
buttons: tool.map(expandTool)
|
|
});
|
|
} else {
|
|
tools.push(expandTool.call(this, tool));
|
|
}
|
|
return tools;
|
|
}, []);
|
|
},
|
|
_click: function (e) {
|
|
var toolName = e.target.attr('data-tool');
|
|
var tool = toolDefaults[toolName] || {};
|
|
var commandType = tool.command;
|
|
if (!commandType) {
|
|
return;
|
|
}
|
|
var args = {
|
|
command: commandType,
|
|
options: {
|
|
property: tool.property || null,
|
|
value: tool.value || null
|
|
}
|
|
};
|
|
if (typeof args.options.value === 'boolean') {
|
|
args.options.value = e.checked ? true : null;
|
|
}
|
|
this.action(args);
|
|
},
|
|
events: [
|
|
'click',
|
|
'toggle',
|
|
'open',
|
|
'close',
|
|
'overflowOpen',
|
|
'overflowClose',
|
|
'action',
|
|
'dialog'
|
|
],
|
|
options: {
|
|
name: 'SpreadsheetToolBar',
|
|
resizable: true,
|
|
tools: defaultTools
|
|
},
|
|
action: function (args) {
|
|
this.trigger('action', args);
|
|
},
|
|
dialog: function (args) {
|
|
this.trigger('dialog', args);
|
|
},
|
|
refresh: function (activeCell) {
|
|
var range = activeCell;
|
|
var tools = this._tools();
|
|
function setToggle(tool, value) {
|
|
var toolbar = tool.toolbar;
|
|
var overflow = tool.overflow;
|
|
var togglable = toolbar && toolbar.options.togglable || overflow && overflow.options.togglable;
|
|
if (!togglable) {
|
|
return;
|
|
}
|
|
var toggle = false;
|
|
if (typeof value === 'boolean') {
|
|
toggle = value;
|
|
} else if (typeof value === 'string') {
|
|
toggle = toolbar.options.value === value;
|
|
}
|
|
toolbar.toggle(toggle);
|
|
if (overflow) {
|
|
overflow.toggle(toggle);
|
|
}
|
|
}
|
|
function update(tool, value) {
|
|
var toolbar = tool.toolbar;
|
|
var overflow = tool.overflow;
|
|
if (toolbar && toolbar.update) {
|
|
toolbar.update(value);
|
|
}
|
|
if (overflow && overflow.update) {
|
|
overflow.update(value);
|
|
}
|
|
}
|
|
for (var i = 0; i < tools.length; i++) {
|
|
var property = tools[i].property;
|
|
var tool = tools[i].tool;
|
|
var value = kendo.isFunction(range[property]) ? range[property]() : range;
|
|
if (tool.type === 'button') {
|
|
setToggle(tool, value);
|
|
} else {
|
|
update(tool, value);
|
|
}
|
|
}
|
|
},
|
|
_tools: function () {
|
|
return this.element.find('[data-property]').toArray().reduce(function (tools, element) {
|
|
element = $(element);
|
|
var property = element.attr('data-property');
|
|
tools.push({
|
|
property: property,
|
|
tool: this._getItem(element)
|
|
});
|
|
return tools;
|
|
}.bind(this), []);
|
|
},
|
|
destroy: function () {
|
|
this.element.find('[data-command],.k-button').each(function () {
|
|
var element = $(this);
|
|
var instance = element.data('instance');
|
|
if (instance && instance.destroy) {
|
|
instance.destroy();
|
|
}
|
|
});
|
|
ToolBar.fn.destroy.call(this);
|
|
}
|
|
});
|
|
kendo.spreadsheet.ToolBar = SpreadsheetToolBar;
|
|
var DropDownTool = kendo.toolbar.Item.extend({
|
|
init: function (options, toolbar) {
|
|
var dropDownList = $('<select />').kendoDropDownList({ height: 'auto' }).data('kendoDropDownList');
|
|
this.dropDownList = dropDownList;
|
|
this.element = dropDownList.wrapper;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
dropDownList.bind('open', this._open.bind(this));
|
|
dropDownList.bind('change', this._change.bind(this));
|
|
this.element.width(options.width).attr({
|
|
'data-command': 'PropertyChangeCommand',
|
|
'data-property': options.property
|
|
});
|
|
},
|
|
_open: function () {
|
|
var ddl = this.dropDownList;
|
|
var list = ddl.list;
|
|
var listWidth;
|
|
list.css({
|
|
whiteSpace: 'nowrap',
|
|
width: 'auto'
|
|
});
|
|
listWidth = list.width();
|
|
if (listWidth) {
|
|
listWidth += 20;
|
|
} else {
|
|
listWidth = ddl._listWidth;
|
|
}
|
|
list.css('width', listWidth + kendo.support.scrollbar());
|
|
ddl._listWidth = listWidth;
|
|
},
|
|
_change: function (e) {
|
|
var instance = e.sender;
|
|
var value = instance.value();
|
|
var dataItem = instance.dataItem();
|
|
var popupName = dataItem ? dataItem.popup : undefined;
|
|
if (popupName) {
|
|
this.toolbar.dialog({ name: popupName });
|
|
} else {
|
|
this.toolbar.action({
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: this.options.property,
|
|
value: value == 'null' ? null : value
|
|
}
|
|
});
|
|
}
|
|
},
|
|
value: function (value) {
|
|
if (value !== undefined) {
|
|
this.dropDownList.value(value);
|
|
} else {
|
|
return this.dropDownList.value();
|
|
}
|
|
}
|
|
});
|
|
var PopupTool = kendo.toolbar.Item.extend({
|
|
init: function (options, toolbar) {
|
|
this.element = $('<a href=\'#\' class=\'k-button k-button-icon\'>' + '<span class=\'' + options.spriteCssClass + '\'>' + '</span><span class=\'k-icon k-i-arrow-s\'></span>' + '</a>');
|
|
this.element.on('click', this.open.bind(this)).attr('data-command', options.command);
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this._popup();
|
|
},
|
|
destroy: function () {
|
|
this.popup.destroy();
|
|
},
|
|
open: function () {
|
|
this.popup.toggle();
|
|
},
|
|
_popup: function () {
|
|
var element = this.element;
|
|
this.popup = $('<div class=\'k-spreadsheet-popup\' />').appendTo(element).kendoPopup({ anchor: element }).data('kendoPopup');
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('dialog', kendo.toolbar.ToolBarButton.extend({
|
|
init: function (options, toolbar) {
|
|
kendo.toolbar.ToolBarButton.fn.init.call(this, options, toolbar);
|
|
this._dialogName = options.dialogName;
|
|
this.element.bind('click', this.open.bind(this)).data('instance', this);
|
|
},
|
|
open: function () {
|
|
this.toolbar.dialog({ name: this._dialogName });
|
|
}
|
|
}));
|
|
kendo.toolbar.registerComponent('exportAsDialog', kendo.toolbar.Item.extend({
|
|
init: function (options, toolbar) {
|
|
this._dialogName = options.dialogName;
|
|
this.toolbar = toolbar;
|
|
this.element = $('<button class=\'k-button k-button-icon\' title=\'' + options.attributes.title + '\'>' + '<span class=\'k-icon k-font-icon k-i-xls\' />' + '</button>').data('instance', this);
|
|
this.element.bind('click', this.open.bind(this)).data('instance', this);
|
|
},
|
|
open: function () {
|
|
this.toolbar.dialog({ name: this._dialogName });
|
|
}
|
|
}));
|
|
var OverflowDialogButton = kendo.toolbar.OverflowButton.extend({
|
|
init: function (options, toolbar) {
|
|
kendo.toolbar.OverflowButton.fn.init.call(this, options, toolbar);
|
|
this.element.on('click', this._click.bind(this));
|
|
this.message = this.options.text;
|
|
var instance = this.element.data('button');
|
|
this.element.data(this.options.type, instance);
|
|
},
|
|
_click: $.noop
|
|
});
|
|
var ColorPicker = PopupTool.extend({
|
|
init: function (options, toolbar) {
|
|
PopupTool.fn.init.call(this, options, toolbar);
|
|
this.popup.element.addClass('k-spreadsheet-colorpicker');
|
|
this.colorChooser = new kendo.spreadsheet.ColorChooser(this.popup.element, { change: this._colorChange.bind(this) });
|
|
this.element.attr({ 'data-property': options.property });
|
|
this.element.data({
|
|
type: 'colorPicker',
|
|
colorPicker: this,
|
|
instance: this
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this.colorChooser.destroy();
|
|
PopupTool.fn.destroy.call(this);
|
|
},
|
|
update: function (value) {
|
|
this.value(value);
|
|
},
|
|
value: function (value) {
|
|
this.colorChooser.value(value);
|
|
},
|
|
_colorChange: function (e) {
|
|
this.toolbar.action({
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: this.options.property,
|
|
value: e.sender.value()
|
|
}
|
|
});
|
|
this.popup.close();
|
|
}
|
|
});
|
|
var ColorPickerButton = OverflowDialogButton.extend({
|
|
init: function (options, toolbar) {
|
|
options.iconName = 'text';
|
|
OverflowDialogButton.fn.init.call(this, options, toolbar);
|
|
},
|
|
_click: function () {
|
|
this.toolbar.dialog({
|
|
name: 'colorPicker',
|
|
options: {
|
|
title: this.options.property,
|
|
property: this.options.property
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('colorPicker', ColorPicker, ColorPickerButton);
|
|
var FONT_SIZES = [
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
16,
|
|
18,
|
|
20,
|
|
22,
|
|
24,
|
|
26,
|
|
28,
|
|
36,
|
|
48,
|
|
72
|
|
];
|
|
var DEFAULT_FONT_SIZE = 12;
|
|
var FontSize = kendo.toolbar.Item.extend({
|
|
init: function (options, toolbar) {
|
|
var comboBox = $('<input />').kendoComboBox({
|
|
change: this._valueChange.bind(this),
|
|
dataSource: options.fontSizes || FONT_SIZES,
|
|
value: DEFAULT_FONT_SIZE
|
|
}).data('kendoComboBox');
|
|
this.comboBox = comboBox;
|
|
this.element = comboBox.wrapper;
|
|
this.options = options;
|
|
this.toolbar = toolbar;
|
|
this.attributes();
|
|
this.addUidAttr();
|
|
this.addOverflowAttr();
|
|
this.element.width(options.width).attr({
|
|
'data-command': 'PropertyChangeCommand',
|
|
'data-property': options.property
|
|
});
|
|
this.element.data({
|
|
type: 'fontSize',
|
|
fontSize: this
|
|
});
|
|
},
|
|
_valueChange: function (e) {
|
|
this.toolbar.action({
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: this.options.property,
|
|
value: kendo.parseInt(e.sender.value())
|
|
}
|
|
});
|
|
},
|
|
update: function (value) {
|
|
this.value(kendo.parseInt(value) || DEFAULT_FONT_SIZE);
|
|
},
|
|
value: function (value) {
|
|
if (value !== undefined) {
|
|
this.comboBox.value(value);
|
|
} else {
|
|
return this.comboBox.value();
|
|
}
|
|
}
|
|
});
|
|
var FontSizeButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({
|
|
name: 'fontSize',
|
|
options: {
|
|
sizes: FONT_SIZES,
|
|
defaultSize: DEFAULT_FONT_SIZE
|
|
}
|
|
});
|
|
},
|
|
update: function (value) {
|
|
this._value = value || DEFAULT_FONT_SIZE;
|
|
this.element.find('.k-text').text(this.message + ' (' + this._value + ') ...');
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('fontSize', FontSize, FontSizeButton);
|
|
var FONT_FAMILIES = [
|
|
'Arial',
|
|
'Courier New',
|
|
'Georgia',
|
|
'Times New Roman',
|
|
'Trebuchet MS',
|
|
'Verdana'
|
|
];
|
|
var DEFAULT_FONT_FAMILY = 'Arial';
|
|
var FontFamily = DropDownTool.extend({
|
|
init: function (options, toolbar) {
|
|
DropDownTool.fn.init.call(this, options, toolbar);
|
|
var ddl = this.dropDownList;
|
|
ddl.setDataSource(options.fontFamilies || FONT_FAMILIES);
|
|
ddl.value(DEFAULT_FONT_FAMILY);
|
|
this.element.data({
|
|
type: 'fontFamily',
|
|
fontFamily: this
|
|
});
|
|
},
|
|
update: function (value) {
|
|
this.value(value || DEFAULT_FONT_FAMILY);
|
|
}
|
|
});
|
|
var FontFamilyButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({
|
|
name: 'fontFamily',
|
|
options: {
|
|
fonts: FONT_FAMILIES,
|
|
defaultFont: DEFAULT_FONT_FAMILY
|
|
}
|
|
});
|
|
},
|
|
update: function (value) {
|
|
this._value = value || DEFAULT_FONT_FAMILY;
|
|
this.element.find('.k-text').text(this.message + ' (' + this._value + ') ...');
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('fontFamily', FontFamily, FontFamilyButton);
|
|
var defaultFormats = kendo.spreadsheet.formats = {
|
|
automatic: null,
|
|
number: '#,0.00',
|
|
percent: '0.00%',
|
|
financial: '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
|
currency: '$#,##0.00;[Red]$#,##0.00',
|
|
date: 'm/d/yyyy',
|
|
time: 'h:mm:ss AM/PM',
|
|
dateTime: 'm/d/yyyy h:mm',
|
|
duration: '[h]:mm:ss'
|
|
};
|
|
var Format = DropDownTool.extend({
|
|
_revertTitle: function (e) {
|
|
e.sender.value('');
|
|
e.sender.wrapper.width('auto');
|
|
},
|
|
init: function (options, toolbar) {
|
|
DropDownTool.fn.init.call(this, options, toolbar);
|
|
var ddl = this.dropDownList;
|
|
var icon = '<span class=\'k-icon k-font-icon k-i-' + options.iconClass + '\' style=\'line-height: 1em; width: 1.35em;\'></span>';
|
|
ddl.bind('change', this._revertTitle.bind(this));
|
|
ddl.bind('dataBound', this._revertTitle.bind(this));
|
|
ddl.setOptions({
|
|
dataValueField: 'format',
|
|
dataTextField: 'name',
|
|
dataValuePrimitive: true,
|
|
valueTemplate: icon,
|
|
template: '# if (data.sample) { #' + '<span class=\'k-spreadsheet-sample\'>#: data.sample #</span>' + '# } #' + '#: data.name #'
|
|
});
|
|
ddl.text(icon);
|
|
ddl.setDataSource([
|
|
{
|
|
format: defaultFormats.automatic,
|
|
name: MESSAGES.formatTypes.automatic
|
|
},
|
|
{
|
|
format: defaultFormats.number,
|
|
name: MESSAGES.formatTypes.number,
|
|
sample: '1,499.99'
|
|
},
|
|
{
|
|
format: defaultFormats.percent,
|
|
name: MESSAGES.formatTypes.percent,
|
|
sample: '14.50%'
|
|
},
|
|
{
|
|
format: defaultFormats.financial,
|
|
name: MESSAGES.formatTypes.financial,
|
|
sample: '(1,000.12)'
|
|
},
|
|
{
|
|
format: defaultFormats.currency,
|
|
name: MESSAGES.formatTypes.currency,
|
|
sample: '$1,499.99'
|
|
},
|
|
{
|
|
format: defaultFormats.date,
|
|
name: MESSAGES.formatTypes.date,
|
|
sample: '4/21/2012'
|
|
},
|
|
{
|
|
format: defaultFormats.time,
|
|
name: MESSAGES.formatTypes.time,
|
|
sample: '5:49:00 PM'
|
|
},
|
|
{
|
|
format: defaultFormats.dateTime,
|
|
name: MESSAGES.formatTypes.dateTime,
|
|
sample: '4/21/2012 5:49:00'
|
|
},
|
|
{
|
|
format: defaultFormats.duration,
|
|
name: MESSAGES.formatTypes.duration,
|
|
sample: '168:05:00'
|
|
},
|
|
{
|
|
popup: 'formatCells',
|
|
name: MESSAGES.formatTypes.moreFormats
|
|
}
|
|
]);
|
|
this.element.data({
|
|
type: 'format',
|
|
format: this
|
|
});
|
|
}
|
|
});
|
|
var FormatButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'formatCells' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('format', Format, FormatButton);
|
|
var BorderChangeTool = PopupTool.extend({
|
|
init: function (options, toolbar) {
|
|
PopupTool.fn.init.call(this, options, toolbar);
|
|
this._borderPalette();
|
|
this.element.data({
|
|
type: 'borders',
|
|
instance: this
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this.borderPalette.destroy();
|
|
PopupTool.fn.destroy.call(this);
|
|
},
|
|
_borderPalette: function () {
|
|
var element = $('<div />').appendTo(this.popup.element);
|
|
this.borderPalette = new kendo.spreadsheet.BorderPalette(element, { change: this._action.bind(this) });
|
|
},
|
|
_action: function (e) {
|
|
this.toolbar.action({
|
|
command: 'BorderChangeCommand',
|
|
options: {
|
|
border: e.type,
|
|
style: {
|
|
size: 1,
|
|
color: e.color
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
var BorderChangeButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'borders' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('borders', BorderChangeTool, BorderChangeButton);
|
|
var AlignmentTool = PopupTool.extend({
|
|
init: function (options, toolbar) {
|
|
PopupTool.fn.init.call(this, options, toolbar);
|
|
this.element.attr({ 'data-property': 'alignment' });
|
|
this._commandPalette();
|
|
this.popup.element.on('click', '.k-button', function (e) {
|
|
this._action($(e.currentTarget));
|
|
}.bind(this));
|
|
this.element.data({
|
|
type: 'alignment',
|
|
alignment: this,
|
|
instance: this
|
|
});
|
|
},
|
|
buttons: [
|
|
{
|
|
property: 'textAlign',
|
|
value: 'left',
|
|
iconClass: 'justify-left',
|
|
text: MESSAGES.alignmentButtons.justtifyLeft
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'center',
|
|
iconClass: 'justify-center',
|
|
text: MESSAGES.alignmentButtons.justifyCenter
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'right',
|
|
iconClass: 'justify-right',
|
|
text: MESSAGES.alignmentButtons.justifyRight
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'justify',
|
|
iconClass: 'justify-full',
|
|
text: MESSAGES.alignmentButtons.justifyFull
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'top',
|
|
iconClass: 'align-top',
|
|
text: MESSAGES.alignmentButtons.alignTop
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'center',
|
|
iconClass: 'align-middle',
|
|
text: MESSAGES.alignmentButtons.alignMiddle
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'bottom',
|
|
iconClass: 'align-bottom',
|
|
text: MESSAGES.alignmentButtons.alignBottom
|
|
}
|
|
],
|
|
destroy: function () {
|
|
this.popup.element.off();
|
|
PopupTool.fn.destroy.call(this);
|
|
},
|
|
update: function (range) {
|
|
var textAlign = range.textAlign();
|
|
var verticalAlign = range.verticalAlign();
|
|
this.popup.element.find('.k-button').removeClass('k-state-active');
|
|
if (textAlign) {
|
|
this.popup.element.find('.k-button[data-value=' + textAlign + ']').addClass('k-state-active');
|
|
}
|
|
if (verticalAlign) {
|
|
this.popup.element.find('.k-button[data-value=' + verticalAlign + ']').addClass('k-state-active');
|
|
}
|
|
},
|
|
_commandPalette: function () {
|
|
var buttons = this.buttons;
|
|
var element = $('<div />').appendTo(this.popup.element);
|
|
buttons.forEach(function (options, index) {
|
|
var button = '<a title=\'' + options.text + '\' data-property=\'' + options.property + '\' data-value=\'' + options.value + '\' class=\'k-button k-button-icon\'>' + '<span class=\'k-icon k-font-icon k-i-' + options.iconClass + '\'></span>' + '</a>';
|
|
if (index !== 0 && buttons[index - 1].property !== options.property) {
|
|
element.append($('<span class=\'k-separator\' />'));
|
|
}
|
|
element.append(button);
|
|
});
|
|
},
|
|
_action: function (button) {
|
|
var property = button.attr('data-property');
|
|
var value = button.attr('data-value');
|
|
this.toolbar.action({
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: property,
|
|
value: value
|
|
}
|
|
});
|
|
}
|
|
});
|
|
var AlignmentButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'alignment' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('alignment', AlignmentTool, AlignmentButton);
|
|
var MergeTool = PopupTool.extend({
|
|
init: function (options, toolbar) {
|
|
PopupTool.fn.init.call(this, options, toolbar);
|
|
this._commandPalette();
|
|
this.popup.element.on('click', '.k-button', function (e) {
|
|
this._action($(e.currentTarget));
|
|
}.bind(this));
|
|
this.element.data({
|
|
type: 'merge',
|
|
merge: this,
|
|
instance: this
|
|
});
|
|
},
|
|
buttons: [
|
|
{
|
|
value: 'cells',
|
|
iconClass: 'merge-cells',
|
|
text: MESSAGES.mergeButtons.mergeCells
|
|
},
|
|
{
|
|
value: 'horizontally',
|
|
iconClass: 'merge-horizontally',
|
|
text: MESSAGES.mergeButtons.mergeHorizontally
|
|
},
|
|
{
|
|
value: 'vertically',
|
|
iconClass: 'merge-vertically',
|
|
text: MESSAGES.mergeButtons.mergeVertically
|
|
},
|
|
{
|
|
value: 'unmerge',
|
|
iconClass: 'normal-layout',
|
|
text: MESSAGES.mergeButtons.unmerge
|
|
}
|
|
],
|
|
destroy: function () {
|
|
this.popup.element.off();
|
|
PopupTool.fn.destroy.call(this);
|
|
},
|
|
_commandPalette: function () {
|
|
var element = $('<div />').appendTo(this.popup.element);
|
|
this.buttons.forEach(function (options) {
|
|
var button = '<a title=\'' + options.text + '\' data-value=\'' + options.value + '\' class=\'k-button k-button-icontext\'>' + '<span class=\'k-icon k-font-icon k-i-' + options.iconClass + '\'></span>' + options.text + '</a>';
|
|
element.append(button);
|
|
});
|
|
},
|
|
_action: function (button) {
|
|
var value = button.attr('data-value');
|
|
this.toolbar.action({
|
|
command: 'MergeCellCommand',
|
|
options: { value: value }
|
|
});
|
|
}
|
|
});
|
|
var MergeButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'merge' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('merge', MergeTool, MergeButton);
|
|
var FreezeTool = PopupTool.extend({
|
|
init: function (options, toolbar) {
|
|
PopupTool.fn.init.call(this, options, toolbar);
|
|
this._commandPalette();
|
|
this.popup.element.on('click', '.k-button', function (e) {
|
|
this._action($(e.currentTarget));
|
|
}.bind(this));
|
|
this.element.data({
|
|
type: 'freeze',
|
|
freeze: this,
|
|
instance: this
|
|
});
|
|
},
|
|
buttons: [
|
|
{
|
|
value: 'panes',
|
|
iconClass: 'freeze-panes',
|
|
text: MESSAGES.freezeButtons.freezePanes
|
|
},
|
|
{
|
|
value: 'rows',
|
|
iconClass: 'freeze-row',
|
|
text: MESSAGES.freezeButtons.freezeRows
|
|
},
|
|
{
|
|
value: 'columns',
|
|
iconClass: 'freeze-col',
|
|
text: MESSAGES.freezeButtons.freezeColumns
|
|
},
|
|
{
|
|
value: 'unfreeze',
|
|
iconClass: 'normal-layout',
|
|
text: MESSAGES.freezeButtons.unfreeze
|
|
}
|
|
],
|
|
destroy: function () {
|
|
this.popup.element.off();
|
|
PopupTool.fn.destroy.call(this);
|
|
},
|
|
_commandPalette: function () {
|
|
var element = $('<div />').appendTo(this.popup.element);
|
|
this.buttons.forEach(function (options) {
|
|
var button = '<a title=\'' + options.text + '\' data-value=\'' + options.value + '\' class=\'k-button k-button-icontext\'>' + '<span class=\'k-icon k-font-icon k-i-' + options.iconClass + '\'></span>' + options.text + '</a>';
|
|
element.append(button);
|
|
});
|
|
},
|
|
_action: function (button) {
|
|
var value = button.attr('data-value');
|
|
this.toolbar.action({
|
|
command: 'FreezePanesCommand',
|
|
options: { value: value }
|
|
});
|
|
}
|
|
});
|
|
var FreezeButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'freeze' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('freeze', FreezeTool, FreezeButton);
|
|
var Sort = DropDownTool.extend({
|
|
_revertTitle: function (e) {
|
|
e.sender.value('');
|
|
e.sender.wrapper.width('auto');
|
|
},
|
|
init: function (options, toolbar) {
|
|
DropDownTool.fn.init.call(this, options, toolbar);
|
|
var ddl = this.dropDownList;
|
|
ddl.bind('change', this._revertTitle.bind(this));
|
|
ddl.bind('dataBound', this._revertTitle.bind(this));
|
|
ddl.setOptions({
|
|
valueTemplate: '<span class=\'k-icon k-font-icon k-i-' + options.iconClass + '\' style=\'line-height: 1em; width: 1.35em;\'></span>',
|
|
template: '<span class=\'k-icon k-font-icon k-i-#= iconClass #\' style=\'line-height: 1em; width: 1.35em;\'></span>#=text#',
|
|
dataTextField: 'text',
|
|
dataValueField: 'value'
|
|
});
|
|
ddl.setDataSource([
|
|
{
|
|
value: 'asc',
|
|
sheet: false,
|
|
text: MESSAGES.sortButtons.sortRangeAsc,
|
|
iconClass: 'sort-asc'
|
|
},
|
|
{
|
|
value: 'desc',
|
|
sheet: false,
|
|
text: MESSAGES.sortButtons.sortRangeDesc,
|
|
iconClass: 'sort-desc'
|
|
}
|
|
]);
|
|
ddl.select(0);
|
|
this.element.data({
|
|
type: 'sort',
|
|
sort: this
|
|
});
|
|
},
|
|
_change: function (e) {
|
|
var instance = e.sender;
|
|
var dataItem = instance.dataItem();
|
|
if (dataItem) {
|
|
this.toolbar.action({
|
|
command: 'SortCommand',
|
|
options: {
|
|
value: dataItem.value,
|
|
sheet: dataItem.sheet
|
|
}
|
|
});
|
|
}
|
|
},
|
|
value: $.noop
|
|
});
|
|
var SortButton = OverflowDialogButton.extend({
|
|
_click: function () {
|
|
this.toolbar.dialog({ name: 'sort' });
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('sort', Sort, SortButton);
|
|
var Filter = kendo.toolbar.ToolBarButton.extend({
|
|
init: function (options, toolbar) {
|
|
options.showText = 'overflow';
|
|
kendo.toolbar.ToolBarButton.fn.init.call(this, options, toolbar);
|
|
this.element.on('click', this._click.bind(this));
|
|
this.element.data({
|
|
type: 'filter',
|
|
filter: this
|
|
});
|
|
},
|
|
_click: function () {
|
|
this.toolbar.action({ command: 'FilterCommand' });
|
|
},
|
|
update: function (value) {
|
|
this.toggle(value);
|
|
}
|
|
});
|
|
var FilterButton = OverflowDialogButton.extend({
|
|
init: function (options, toolbar) {
|
|
OverflowDialogButton.fn.init.call(this, options, toolbar);
|
|
this.element.data({
|
|
type: 'filter',
|
|
filter: this
|
|
});
|
|
},
|
|
_click: function () {
|
|
this.toolbar.action({ command: 'FilterCommand' });
|
|
},
|
|
update: function (value) {
|
|
this.toggle(value);
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('filter', Filter, FilterButton);
|
|
var Open = kendo.toolbar.Item.extend({
|
|
init: function (options, toolbar) {
|
|
this.toolbar = toolbar;
|
|
this.element = $('<div class=\'k-button k-upload-button k-button-icon\'>' + '<span class=\'k-icon k-font-icon k-i-folder-open\' />' + '</div>').data('instance', this);
|
|
this._title = options.attributes.title;
|
|
this._reset();
|
|
},
|
|
_reset: function () {
|
|
this.element.remove('input');
|
|
$('<input type=\'file\' autocomplete=\'off\' accept=\'.xlsx\'/>').attr('title', this._title).one('change', this._change.bind(this)).appendTo(this.element);
|
|
},
|
|
_change: function (e) {
|
|
this.toolbar.action({
|
|
command: 'OpenCommand',
|
|
options: { file: e.target.files[0] }
|
|
});
|
|
this._reset();
|
|
}
|
|
});
|
|
kendo.toolbar.registerComponent('open', Open);
|
|
kendo.spreadsheet.TabStrip = kendo.ui.TabStrip.extend({
|
|
init: function (element, options) {
|
|
kendo.ui.TabStrip.fn.init.call(this, element, options);
|
|
element.addClass('k-spreadsheet-tabstrip');
|
|
this._quickAccessButtons();
|
|
this.quickAccessToolBar.on('click', '.k-button', function (e) {
|
|
var action = $(e.currentTarget).attr('title').toLowerCase();
|
|
this.trigger('action', { action: action });
|
|
}.bind(this));
|
|
this.toolbars = {};
|
|
var tabs = options.dataSource;
|
|
this.contentElements.each(function (idx, element) {
|
|
this._toolbar($(element), tabs[idx].id, options.toolbarOptions[tabs[idx].id]);
|
|
}.bind(this));
|
|
this.one('activate', function () {
|
|
this.toolbars[this.options.dataSource[0].id].resize();
|
|
});
|
|
},
|
|
events: kendo.ui.TabStrip.fn.events.concat([
|
|
'action',
|
|
'dialog'
|
|
]),
|
|
destroy: function () {
|
|
this.quickAccessToolBar.off('click');
|
|
kendo.ui.TabStrip.fn.destroy.call(this);
|
|
for (var name in this.toolbars) {
|
|
this.toolbars[name].destroy();
|
|
}
|
|
},
|
|
action: function (args) {
|
|
this.trigger('action', args);
|
|
},
|
|
dialog: function (args) {
|
|
this.trigger('dialog', args);
|
|
},
|
|
refreshTools: function (range) {
|
|
var toolbars = this.toolbars;
|
|
for (var name in toolbars) {
|
|
if (toolbars.hasOwnProperty(name)) {
|
|
toolbars[name].refresh(range);
|
|
}
|
|
}
|
|
},
|
|
_quickAccessButtons: function () {
|
|
var buttons = [
|
|
{
|
|
title: MESSAGES.quickAccess.undo,
|
|
iconClass: 'undo-large',
|
|
action: 'undo'
|
|
},
|
|
{
|
|
title: MESSAGES.quickAccess.redo,
|
|
iconClass: 'redo-large',
|
|
action: 'redo'
|
|
}
|
|
];
|
|
var buttonTemplate = kendo.template('<a href=\'\\#\' title=\'#= title #\' data-action=\'#= action #\' class=\'k-button k-button-icon\'><span class=\'k-icon k-font-icon k-i-#=iconClass#\'></span></a>');
|
|
this.quickAccessToolBar = $('<div />', {
|
|
'class': 'k-spreadsheet-quick-access-toolbar',
|
|
'html': kendo.render(buttonTemplate, buttons)
|
|
}).insertBefore(this.wrapper);
|
|
this.quickAccessToolBar.on('click', '.k-button', function (e) {
|
|
e.preventDefault();
|
|
var action = $(e.currentTarget).attr('title').toLowerCase();
|
|
this.action({ action: action });
|
|
}.bind(this));
|
|
this.quickAccessAdjust();
|
|
},
|
|
quickAccessAdjust: function () {
|
|
this.tabGroup.css('padding-left', this.quickAccessToolBar.outerWidth());
|
|
},
|
|
_toolbar: function (container, name, tools) {
|
|
var element;
|
|
var options;
|
|
if (this.toolbars[name]) {
|
|
this.toolbars[name].destroy();
|
|
container.children('.k-toolbar').remove();
|
|
}
|
|
if (tools) {
|
|
element = container.html('<div />').children('div');
|
|
options = {
|
|
tools: typeof tools === 'boolean' ? undefined : tools,
|
|
toolbarName: name,
|
|
action: this.action.bind(this),
|
|
dialog: this.dialog.bind(this)
|
|
};
|
|
this.toolbars[name] = new kendo.spreadsheet.ToolBar(element, options);
|
|
}
|
|
}
|
|
});
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/dialogs', [
|
|
'kendo.core',
|
|
'kendo.binder',
|
|
'kendo.validator'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var ObservableObject = kendo.data.ObservableObject;
|
|
var MESSAGES = kendo.spreadsheet.messages.dialogs = {
|
|
apply: 'Apply',
|
|
save: 'Save',
|
|
cancel: 'Cancel',
|
|
remove: 'Remove',
|
|
okText: 'OK',
|
|
formatCellsDialog: {
|
|
title: 'Format',
|
|
categories: {
|
|
number: 'Number',
|
|
currency: 'Currency',
|
|
date: 'Date'
|
|
}
|
|
},
|
|
fontFamilyDialog: { title: 'Font' },
|
|
fontSizeDialog: { title: 'Font size' },
|
|
bordersDialog: { title: 'Borders' },
|
|
alignmentDialog: {
|
|
title: 'Alignment',
|
|
buttons: {
|
|
justtifyLeft: 'Align left',
|
|
justifyCenter: 'Center',
|
|
justifyRight: 'Align right',
|
|
justifyFull: 'Justify',
|
|
alignTop: 'Align top',
|
|
alignMiddle: 'Align middle',
|
|
alignBottom: 'Align bottom'
|
|
}
|
|
},
|
|
mergeDialog: {
|
|
title: 'Merge cells',
|
|
buttons: {
|
|
mergeCells: 'Merge all',
|
|
mergeHorizontally: 'Merge horizontally',
|
|
mergeVertically: 'Merge vertically',
|
|
unmerge: 'Unmerge'
|
|
}
|
|
},
|
|
freezeDialog: {
|
|
title: 'Freeze panes',
|
|
buttons: {
|
|
freezePanes: 'Freeze panes',
|
|
freezeRows: 'Freeze rows',
|
|
freezeColumns: 'Freeze columns',
|
|
unfreeze: 'Unfreeze panes'
|
|
}
|
|
},
|
|
validationDialog: {
|
|
title: 'Data Validation',
|
|
hintMessage: 'Please enter a valid {0} value {1}.',
|
|
hintTitle: 'Validation {0}',
|
|
criteria: {
|
|
any: 'Any value',
|
|
number: 'Number',
|
|
text: 'Text',
|
|
date: 'Date',
|
|
custom: 'Custom Formula',
|
|
list: 'List'
|
|
},
|
|
comparers: {
|
|
greaterThan: 'greater than',
|
|
lessThan: 'less than',
|
|
between: 'between',
|
|
notBetween: 'not between',
|
|
equalTo: 'equal to',
|
|
notEqualTo: 'not equal to',
|
|
greaterThanOrEqualTo: 'greater than or equal to',
|
|
lessThanOrEqualTo: 'less than or equal to'
|
|
},
|
|
comparerMessages: {
|
|
greaterThan: 'greater than {0}',
|
|
lessThan: 'less than {0}',
|
|
between: 'between {0} and {1}',
|
|
notBetween: 'not between {0} and {1}',
|
|
equalTo: 'equal to {0}',
|
|
notEqualTo: 'not equal to {0}',
|
|
greaterThanOrEqualTo: 'greater than or equal to {0}',
|
|
lessThanOrEqualTo: 'less than or equal to {0}',
|
|
custom: 'that satisfies the formula: {0}'
|
|
},
|
|
labels: {
|
|
criteria: 'Criteria',
|
|
comparer: 'Comparer',
|
|
min: 'Min',
|
|
max: 'Max',
|
|
value: 'Value',
|
|
start: 'Start',
|
|
end: 'End',
|
|
onInvalidData: 'On invalid data',
|
|
rejectInput: 'Reject input',
|
|
showWarning: 'Show warning',
|
|
showHint: 'Show hint',
|
|
hintTitle: 'Hint title',
|
|
hintMessage: 'Hint message',
|
|
ignoreBlank: 'Ignore blank'
|
|
},
|
|
placeholders: {
|
|
typeTitle: 'Type title',
|
|
typeMessage: 'Type message'
|
|
}
|
|
},
|
|
exportAsDialog: {
|
|
title: 'Export...',
|
|
labels: {
|
|
scale: 'Scale',
|
|
fit: 'Fit to page',
|
|
fileName: 'File name',
|
|
saveAsType: 'Save as type',
|
|
exportArea: 'Export',
|
|
paperSize: 'Paper size',
|
|
margins: 'Margins',
|
|
orientation: 'Orientation',
|
|
print: 'Print',
|
|
guidelines: 'Guidelines',
|
|
center: 'Center',
|
|
horizontally: 'Horizontally',
|
|
vertically: 'Vertically'
|
|
}
|
|
},
|
|
modifyMergedDialog: { errorMessage: 'Cannot change part of a merged cell.' },
|
|
overflowDialog: { errorMessage: 'Cannot paste, because the copy area and the paste area are not the same size and shape.' },
|
|
useKeyboardDialog: {
|
|
title: 'Copying and pasting',
|
|
errorMessage: 'These actions cannot be invoked through the menu. Please use the keyboard shortcuts instead:',
|
|
labels: {
|
|
forCopy: 'for copy',
|
|
forCut: 'for cut',
|
|
forPaste: 'for paste'
|
|
}
|
|
},
|
|
unsupportedSelectionDialog: { errorMessage: 'That action cannot be performed on multiple selection.' }
|
|
};
|
|
var registry = {};
|
|
kendo.spreadsheet.dialogs = {
|
|
register: function (name, dialogClass) {
|
|
registry[name] = dialogClass;
|
|
},
|
|
registered: function (name) {
|
|
return !!registry[name];
|
|
},
|
|
create: function (name, options) {
|
|
var dialogClass = registry[name];
|
|
if (dialogClass) {
|
|
return new dialogClass(options);
|
|
}
|
|
}
|
|
};
|
|
var SpreadsheetDialog = kendo.spreadsheet.SpreadsheetDialog = kendo.Observable.extend({
|
|
init: function (options) {
|
|
kendo.Observable.fn.init.call(this, options);
|
|
this.options = $.extend(true, {}, this.options, options);
|
|
this.bind(this.events, options);
|
|
},
|
|
events: [
|
|
'close',
|
|
'activate'
|
|
],
|
|
dialog: function () {
|
|
if (!this._dialog) {
|
|
this._dialog = $('<div class=\'k-spreadsheet-window k-action-window\' />').addClass(this.options.className || '').append(kendo.template(this.options.template)({ messages: kendo.spreadsheet.messages.dialogs || MESSAGES })).appendTo(document.body).kendoWindow({
|
|
scrollable: false,
|
|
resizable: false,
|
|
maximizable: false,
|
|
modal: true,
|
|
visible: false,
|
|
width: this.options.width || 320,
|
|
title: this.options.title,
|
|
open: function () {
|
|
this.center();
|
|
},
|
|
close: this._onDialogClose.bind(this),
|
|
activate: this._onDialogActivate.bind(this),
|
|
deactivate: this._onDialogDeactivate.bind(this)
|
|
}).data('kendoWindow');
|
|
}
|
|
return this._dialog;
|
|
},
|
|
_onDialogClose: function () {
|
|
this.trigger('close');
|
|
},
|
|
_onDialogActivate: function () {
|
|
this.trigger('activate');
|
|
},
|
|
_onDialogDeactivate: function () {
|
|
this.trigger('deactivate');
|
|
this.destroy();
|
|
},
|
|
destroy: function () {
|
|
if (this._dialog) {
|
|
this._dialog.destroy();
|
|
this._dialog = null;
|
|
}
|
|
},
|
|
open: function () {
|
|
this.dialog().open();
|
|
},
|
|
apply: function () {
|
|
this.close();
|
|
},
|
|
close: function () {
|
|
this.dialog().close();
|
|
}
|
|
});
|
|
function formattedValue(value, format) {
|
|
return kendo.spreadsheet.formatting.text(value, format);
|
|
}
|
|
var FormatCellsViewModel = kendo.spreadsheet.FormatCellsViewModel = ObservableObject.extend({
|
|
init: function (options) {
|
|
ObservableObject.fn.init.call(this, options);
|
|
this.useCategory(this.category);
|
|
},
|
|
useCategory: function (category) {
|
|
var type = category && category.type || 'number';
|
|
var formatCurrency = type == 'currency';
|
|
this.category = category;
|
|
this.set('showCurrencyFilter', formatCurrency && this.currencies.length > 1);
|
|
if (!formatCurrency) {
|
|
this.set('formats', this.allFormats[type + 'Formats']);
|
|
} else {
|
|
this.currency(this.currencies[0]);
|
|
}
|
|
this.useFirstFormat();
|
|
},
|
|
useFirstFormat: function () {
|
|
if (this.formats.length) {
|
|
this.set('format', this.formats[0].value);
|
|
}
|
|
},
|
|
currency: function (currency) {
|
|
if (currency !== undefined) {
|
|
this._currency = currency;
|
|
var info = currency.value;
|
|
var formats = [
|
|
{
|
|
currency: info,
|
|
decimals: true
|
|
},
|
|
{
|
|
currency: info,
|
|
decimals: true,
|
|
iso: true
|
|
},
|
|
{
|
|
currency: info,
|
|
decimals: false
|
|
}
|
|
];
|
|
formats = formats.map(function (format) {
|
|
format = FormatCellsViewModel.convert.currency(format);
|
|
return {
|
|
value: format,
|
|
name: formattedValue(1000, format)
|
|
};
|
|
});
|
|
this.set('formats', formats);
|
|
this.useFirstFormat();
|
|
}
|
|
return this._currency || this.currencies[0];
|
|
},
|
|
categoryFilter: function (category) {
|
|
if (category !== undefined) {
|
|
this.useCategory(category);
|
|
}
|
|
return this.category;
|
|
},
|
|
preview: function () {
|
|
var format = this.get('format');
|
|
var value = this.value || 0;
|
|
if (format && format.length) {
|
|
return formattedValue(value, format);
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
});
|
|
FormatCellsViewModel.convert = {
|
|
currency: function (options) {
|
|
function repeat(token, n) {
|
|
return new Array(n + 1).join(token);
|
|
}
|
|
var info = options.currency;
|
|
var format = info.pattern[1];
|
|
if (options.decimals) {
|
|
format = format.replace(/n/g, 'n' + info['.'] + repeat('0', info.decimals));
|
|
}
|
|
if (options.iso) {
|
|
format = '"' + info.abbr + '" ' + format.replace(/\s*\$\s*/g, '');
|
|
} else {
|
|
format = format.replace(/\$/g, info.symbol);
|
|
}
|
|
format = format.replace(/n/g, '?');
|
|
return format;
|
|
},
|
|
date: function (format) {
|
|
if (/T|Z/.test(format)) {
|
|
return '';
|
|
}
|
|
return format.toLowerCase().replace(/tt/g, 'AM/PM').replace(/'/g, '"');
|
|
}
|
|
};
|
|
function uniqueBy(field, array) {
|
|
var result = [];
|
|
var values = [];
|
|
for (var i = 0; i < array.length; i++) {
|
|
if ($.inArray(array[i][field], values) == -1) {
|
|
result.push(array[i]);
|
|
values.push(array[i][field]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
var FormatCellsDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.formatCellsDialog || MESSAGES;
|
|
var defaultOptions = {
|
|
title: messages.title,
|
|
categories: [
|
|
{
|
|
type: 'number',
|
|
name: messages.categories.number
|
|
},
|
|
{
|
|
type: 'currency',
|
|
name: messages.categories.currency
|
|
},
|
|
{
|
|
type: 'date',
|
|
name: messages.categories.date
|
|
}
|
|
]
|
|
};
|
|
SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
|
|
this._generateFormats();
|
|
},
|
|
options: {
|
|
className: 'k-spreadsheet-format-cells',
|
|
template: '<div class=\'k-root-tabs\' data-role=\'tabstrip\' ' + 'data-text-field=\'name\' ' + 'data-bind=\'source: categories, value: categoryFilter\' ' + 'data-animation=\'false\' />' + '<div class=\'k-spreadsheet-preview\' data-bind=\'text: preview\' />' + '<script type=\'text/x-kendo-template\' id=\'format-item-template\'>' + '\\#: data.name \\#' + '</script>' + '<select data-role=\'dropdownlist\' class=\'k-format-filter\' ' + 'data-text-field=\'description\' ' + 'data-value-field=\'value.name\' ' + 'data-bind=\'visible: showCurrencyFilter, value: currency, source: currencies\' />' + '<ul data-role=\'staticlist\' tabindex=\'0\' ' + 'class=\'k-list k-reset\' ' + 'data-template=\'format-item-template\' ' + 'data-value-primitive=\'true\' ' + 'data-value-field=\'value\' ' + 'data-bind=\'source: formats, value: format\' />' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: apply\'>#: messages.apply #</button>' + '<button class=\'k-button\' data-bind=\'click: close\'>#: messages.cancel #</button>' + '</div>'
|
|
},
|
|
_generateFormats: function () {
|
|
var options = this.options;
|
|
if (!options.currencies) {
|
|
options.currencies = FormatCellsDialog.currenciesFrom(kendo.cultures);
|
|
}
|
|
if (!options.numberFormats) {
|
|
options.numberFormats = [
|
|
{
|
|
value: '#.00%',
|
|
name: '100.00%'
|
|
},
|
|
{
|
|
value: '#%',
|
|
name: '100%'
|
|
},
|
|
{
|
|
value: '#.00',
|
|
name: '1024.00'
|
|
},
|
|
{
|
|
value: '#,###.00',
|
|
name: '1,024.00'
|
|
}
|
|
];
|
|
}
|
|
if (!options.dateFormats) {
|
|
var calendarPatterns = kendo.cultures.current.calendars.standard.patterns;
|
|
options.dateFormats = uniqueBy('value', $.map(calendarPatterns, function (format) {
|
|
format = FormatCellsViewModel.convert.date(format);
|
|
if (!format) {
|
|
return;
|
|
}
|
|
return {
|
|
value: format,
|
|
name: formattedValue(34567.7678, format)
|
|
};
|
|
}));
|
|
}
|
|
},
|
|
open: function (range) {
|
|
var options = this.options;
|
|
var value = range.value();
|
|
var categories = options.categories.slice(0);
|
|
var element;
|
|
this.viewModel = new FormatCellsViewModel({
|
|
currencies: options.currencies.slice(0),
|
|
allFormats: {
|
|
numberFormats: options.numberFormats.slice(0),
|
|
dateFormats: options.dateFormats.slice(0)
|
|
},
|
|
categories: categories,
|
|
format: range.format(),
|
|
category: value instanceof Date ? categories[2] : categories[0],
|
|
apply: this.apply.bind(this),
|
|
close: this.close.bind(this),
|
|
value: value
|
|
});
|
|
SpreadsheetDialog.fn.open.call(this);
|
|
element = this.dialog().element;
|
|
kendo.bind(element, this.viewModel);
|
|
var currencyFilter = element.find('select.k-format-filter').data('kendoDropDownList');
|
|
if (options.currencies.length > 10) {
|
|
currencyFilter.setOptions({ filter: 'contains' });
|
|
}
|
|
element.find(kendo.roleSelector('staticlist')).parent().addClass('k-list-wrapper');
|
|
},
|
|
apply: function () {
|
|
var format = this.viewModel.format;
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: 'format',
|
|
value: format
|
|
}
|
|
});
|
|
}
|
|
});
|
|
FormatCellsDialog.currenciesFrom = function (cultures) {
|
|
return uniqueBy('description', $.map(cultures, function (culture, name) {
|
|
if (!/-/.test(name)) {
|
|
return;
|
|
}
|
|
var currency = culture.numberFormat.currency;
|
|
var description = kendo.format('{0} ({1}, {2})', currency.name, currency.abbr, currency.symbol);
|
|
return {
|
|
description: description,
|
|
value: currency
|
|
};
|
|
}));
|
|
};
|
|
kendo.spreadsheet.dialogs.register('formatCells', FormatCellsDialog);
|
|
kendo.spreadsheet.dialogs.FormatCellsDialog = FormatCellsDialog;
|
|
var MessageDialog = SpreadsheetDialog.extend({
|
|
options: {
|
|
className: 'k-spreadsheet-message',
|
|
title: '',
|
|
messageId: '',
|
|
text: '',
|
|
template: '<div class=\'k-spreadsheet-message-content\' data-bind=\'text: text\' />' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: close\'>' + '#= messages.okText #' + '</button>' + '</div>'
|
|
},
|
|
open: function () {
|
|
SpreadsheetDialog.fn.open.call(this);
|
|
var options = this.options;
|
|
var text = options.text;
|
|
if (options.messageId) {
|
|
text = kendo.getter(options.messageId, true)(kendo.spreadsheet.messages.dialogs);
|
|
}
|
|
kendo.bind(this.dialog().element, {
|
|
text: text,
|
|
close: this.close.bind(this)
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('message', MessageDialog);
|
|
var FontFamilyDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.fontFamilyDialog || MESSAGES;
|
|
SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));
|
|
this._list();
|
|
},
|
|
options: { template: '<ul class=\'k-list k-reset\'></ul>' },
|
|
_list: function () {
|
|
var ul = this.dialog().element.find('ul');
|
|
var fonts = this.options.fonts;
|
|
var defaultFont = this.options.defaultFont;
|
|
this.list = new kendo.ui.StaticList(ul, {
|
|
dataSource: new kendo.data.DataSource({ data: fonts }),
|
|
template: '#: data #',
|
|
value: defaultFont,
|
|
change: this.apply.bind(this)
|
|
});
|
|
this.list.dataSource.fetch();
|
|
},
|
|
apply: function (e) {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: 'fontFamily',
|
|
value: e.sender.value()[0]
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('fontFamily', FontFamilyDialog);
|
|
var FontSizeDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.fontSizeDialog || MESSAGES;
|
|
SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));
|
|
this._list();
|
|
},
|
|
options: { template: '<ul class=\'k-list k-reset\'></ul>' },
|
|
_list: function () {
|
|
var ul = this.dialog().element.find('ul');
|
|
var sizes = this.options.sizes;
|
|
var defaultSize = this.options.defaultSize;
|
|
this.list = new kendo.ui.StaticList(ul, {
|
|
dataSource: new kendo.data.DataSource({ data: sizes }),
|
|
template: '#: data #',
|
|
value: defaultSize,
|
|
change: this.apply.bind(this)
|
|
});
|
|
this.list.dataSource.fetch();
|
|
},
|
|
apply: function (e) {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: 'fontSize',
|
|
value: kendo.parseInt(e.sender.value()[0])
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('fontSize', FontSizeDialog);
|
|
var BordersDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.bordersDialog || MESSAGES;
|
|
SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));
|
|
this.element = this.dialog().element;
|
|
this._borderPalette();
|
|
this.viewModel = kendo.observable({
|
|
apply: this.apply.bind(this),
|
|
close: this.close.bind(this)
|
|
});
|
|
kendo.bind(this.element.find('.k-action-buttons'), this.viewModel);
|
|
},
|
|
options: {
|
|
width: 177,
|
|
template: '<div></div>' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: apply\'>#: messages.apply #</button>' + '<button class=\'k-button\' data-bind=\'click: close\'>#: messages.cancel #</button>' + '</div>'
|
|
},
|
|
apply: function () {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
var state = this.value();
|
|
this.trigger('action', {
|
|
command: 'BorderChangeCommand',
|
|
options: {
|
|
border: state.type,
|
|
style: {
|
|
size: 1,
|
|
color: state.color
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_borderPalette: function () {
|
|
var element = this.dialog().element.find('div:first');
|
|
this.borderPalette = new kendo.spreadsheet.BorderPalette(element, { change: this.value.bind(this) });
|
|
},
|
|
value: function (state) {
|
|
if (state === undefined) {
|
|
return this._state;
|
|
} else {
|
|
this._state = state;
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('borders', BordersDialog);
|
|
var ColorChooser = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
SpreadsheetDialog.fn.init.call(this, options);
|
|
this.element = this.dialog().element;
|
|
this.property = options.property;
|
|
this.options.title = options.title;
|
|
this.viewModel = kendo.observable({
|
|
apply: this.apply.bind(this),
|
|
close: this.close.bind(this)
|
|
});
|
|
kendo.bind(this.element.find('.k-action-buttons'), this.viewModel);
|
|
},
|
|
options: { template: '<div></div>' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: apply\'>#: messages.apply #</button>' + '<button class=\'k-button\' data-bind=\'click: close\'>#: messages.cancel #</button>' + '</div>' },
|
|
apply: function () {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: this.property,
|
|
value: this.value()
|
|
}
|
|
});
|
|
},
|
|
value: function (e) {
|
|
if (e === undefined) {
|
|
return this._value;
|
|
} else {
|
|
this._value = e.value;
|
|
}
|
|
}
|
|
});
|
|
var ColorPickerDialog = ColorChooser.extend({
|
|
init: function (options) {
|
|
options.width = 177;
|
|
ColorChooser.fn.init.call(this, options);
|
|
this._colorPalette();
|
|
},
|
|
_colorPalette: function () {
|
|
var element = this.dialog().element.find('div:first');
|
|
this.colorPalette = element.kendoColorPalette({
|
|
palette: [
|
|
'#ffffff',
|
|
'#000000',
|
|
'#d6ecff',
|
|
'#4e5b6f',
|
|
'#7fd13b',
|
|
'#ea157a',
|
|
'#feb80a',
|
|
'#00addc',
|
|
'#738ac8',
|
|
'#1ab39f',
|
|
'#f2f2f2',
|
|
'#7f7f7f',
|
|
'#a7d6ff',
|
|
'#d9dde4',
|
|
'#e5f5d7',
|
|
'#fad0e4',
|
|
'#fef0cd',
|
|
'#c5f2ff',
|
|
'#e2e7f4',
|
|
'#c9f7f1',
|
|
'#d8d8d8',
|
|
'#595959',
|
|
'#60b5ff',
|
|
'#b3bcca',
|
|
'#cbecb0',
|
|
'#f6a1c9',
|
|
'#fee29c',
|
|
'#8be6ff',
|
|
'#c7d0e9',
|
|
'#94efe3',
|
|
'#bfbfbf',
|
|
'#3f3f3f',
|
|
'#007dea',
|
|
'#8d9baf',
|
|
'#b2e389',
|
|
'#f272af',
|
|
'#fed46b',
|
|
'#51d9ff',
|
|
'#aab8de',
|
|
'#5fe7d5',
|
|
'#a5a5a5',
|
|
'#262626',
|
|
'#003e75',
|
|
'#3a4453',
|
|
'#5ea226',
|
|
'#af0f5b',
|
|
'#c58c00',
|
|
'#0081a5',
|
|
'#425ea9',
|
|
'#138677',
|
|
'#7f7f7f',
|
|
'#0c0c0c',
|
|
'#00192e',
|
|
'#272d37',
|
|
'#3f6c19',
|
|
'#750a3d',
|
|
'#835d00',
|
|
'#00566e',
|
|
'#2c3f71',
|
|
'#0c594f'
|
|
],
|
|
change: this.value.bind(this)
|
|
}).data('kendoColorPalette');
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('colorPicker', ColorPickerDialog);
|
|
var CustomColorDialog = ColorChooser.extend({
|
|
init: function (options) {
|
|
options.width = 268;
|
|
ColorChooser.fn.init.call(this, options);
|
|
this.dialog().setOptions({ animation: false });
|
|
this.dialog().one('activate', this._colorPicker.bind(this));
|
|
},
|
|
_colorPicker: function () {
|
|
var element = this.dialog().element.find('div:first');
|
|
this.colorPicker = element.kendoFlatColorPicker({ change: this.value.bind(this) }).data('kendoFlatColorPicker');
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('customColor', CustomColorDialog);
|
|
var AlignmentDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.alignmentDialog || MESSAGES;
|
|
var defaultOptions = {
|
|
title: messages.title,
|
|
buttons: [
|
|
{
|
|
property: 'textAlign',
|
|
value: 'left',
|
|
iconClass: 'justify-left',
|
|
text: messages.buttons.justtifyLeft
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'center',
|
|
iconClass: 'justify-center',
|
|
text: messages.buttons.justifyCenter
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'right',
|
|
iconClass: 'justify-right',
|
|
text: messages.buttons.justifyRight
|
|
},
|
|
{
|
|
property: 'textAlign',
|
|
value: 'justify',
|
|
iconClass: 'justify-full',
|
|
text: messages.buttons.justifyFull
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'top',
|
|
iconClass: 'align-top',
|
|
text: messages.buttons.alignTop
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'center',
|
|
iconClass: 'align-middle',
|
|
text: messages.buttons.alignMiddle
|
|
},
|
|
{
|
|
property: 'verticalAlign',
|
|
value: 'bottom',
|
|
iconClass: 'align-bottom',
|
|
text: messages.buttons.alignBottom
|
|
}
|
|
]
|
|
};
|
|
SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
|
|
this._list();
|
|
},
|
|
options: { template: '<ul class=\'k-list k-reset\'></ul>' },
|
|
_list: function () {
|
|
var ul = this.dialog().element.find('ul');
|
|
this.list = new kendo.ui.StaticList(ul, {
|
|
dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
|
|
template: '<a title=\'#=text#\' data-property=\'#=property#\' data-value=\'#=value#\'>' + '<span class=\'k-icon k-font-icon k-i-#=iconClass#\'></span>' + '#=text#' + '</a>',
|
|
change: this.apply.bind(this)
|
|
});
|
|
this.list.dataSource.fetch();
|
|
},
|
|
apply: function (e) {
|
|
var dataItem = e.sender.value()[0];
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'PropertyChangeCommand',
|
|
options: {
|
|
property: dataItem.property,
|
|
value: dataItem.value
|
|
}
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('alignment', AlignmentDialog);
|
|
var MergeDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.mergeDialog || MESSAGES;
|
|
var defaultOptions = {
|
|
title: messages.title,
|
|
buttons: [
|
|
{
|
|
value: 'cells',
|
|
iconClass: 'merge-cells',
|
|
text: messages.buttons.mergeCells
|
|
},
|
|
{
|
|
value: 'horizontally',
|
|
iconClass: 'merge-horizontally',
|
|
text: messages.buttons.mergeHorizontally
|
|
},
|
|
{
|
|
value: 'vertically',
|
|
iconClass: 'merge-vertically',
|
|
text: messages.buttons.mergeVertically
|
|
},
|
|
{
|
|
value: 'unmerge',
|
|
iconClass: 'normal-layout',
|
|
text: messages.buttons.unmerge
|
|
}
|
|
]
|
|
};
|
|
SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
|
|
this._list();
|
|
},
|
|
options: { template: '<ul class=\'k-list k-reset\'></ul>' },
|
|
_list: function () {
|
|
var ul = this.dialog().element.find('ul');
|
|
this.list = new kendo.ui.StaticList(ul, {
|
|
dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
|
|
template: '<a title=\'#=text#\' data-value=\'#=value#\'>' + '<span class=\'k-icon k-font-icon k-i-#=iconClass#\'></span>#=text#' + '</a>',
|
|
change: this.apply.bind(this)
|
|
});
|
|
this.list.dataSource.fetch();
|
|
},
|
|
apply: function (e) {
|
|
var dataItem = e.sender.value()[0];
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'MergeCellCommand',
|
|
options: { value: dataItem.value }
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('merge', MergeDialog);
|
|
var FreezeDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.freezeDialog || MESSAGES;
|
|
var defaultOptions = {
|
|
title: messages.title,
|
|
buttons: [
|
|
{
|
|
value: 'panes',
|
|
iconClass: 'freeze-panes',
|
|
text: messages.buttons.freezePanes
|
|
},
|
|
{
|
|
value: 'rows',
|
|
iconClass: 'freeze-row',
|
|
text: messages.buttons.freezeRows
|
|
},
|
|
{
|
|
value: 'columns',
|
|
iconClass: 'freeze-col',
|
|
text: messages.buttons.freezeColumns
|
|
},
|
|
{
|
|
value: 'unfreeze',
|
|
iconClass: 'normal-layout',
|
|
text: messages.buttons.unfreeze
|
|
}
|
|
]
|
|
};
|
|
SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
|
|
this._list();
|
|
},
|
|
options: { template: '<ul class=\'k-list k-reset\'></ul>' },
|
|
_list: function () {
|
|
var ul = this.dialog().element.find('ul');
|
|
this.list = new kendo.ui.StaticList(ul, {
|
|
dataSource: new kendo.data.DataSource({ data: this.options.buttons }),
|
|
template: '<a title=\'#=text#\' data-value=\'#=value#\'>' + '<span class=\'k-icon k-font-icon k-i-#=iconClass#\'></span>#=text#' + '</a>',
|
|
change: this.apply.bind(this)
|
|
});
|
|
this.list.dataSource.fetch();
|
|
},
|
|
apply: function (e) {
|
|
var dataItem = e.sender.value()[0];
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'FreezePanesCommand',
|
|
options: { value: dataItem.value }
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('freeze', FreezeDialog);
|
|
var ValidationViewModel = kendo.spreadsheet.ValidationCellsViewModel = ObservableObject.extend({
|
|
init: function (options) {
|
|
ObservableObject.fn.init.call(this, options);
|
|
this.bind('change', function (e) {
|
|
if (e.field === 'criterion') {
|
|
this.reset();
|
|
if (this.criterion === 'custom' || this.criterion === 'list') {
|
|
this.setHintMessageTemplate();
|
|
}
|
|
}
|
|
if (e.field === 'comparer') {
|
|
this.setHintMessageTemplate();
|
|
}
|
|
if ((e.field == 'hintMessage' || e.field == 'hintTitle') && !this._mute) {
|
|
this.shouldBuild = false;
|
|
}
|
|
if ((e.field == 'from' || e.field == 'to' || e.field == 'hintMessageTemplate' || e.field == 'type') && this.shouldBuild) {
|
|
this.buildMessages();
|
|
}
|
|
}.bind(this));
|
|
this.reset();
|
|
},
|
|
buildMessages: function () {
|
|
this._mute = true;
|
|
this.set('hintTitle', this.hintTitleTemplate ? kendo.format(this.hintTitleTemplate, this.type) : '');
|
|
this.set('hintMessage', this.hintMessageTemplate ? kendo.format(this.hintMessageTemplate, this.from, this.to) : '');
|
|
this._mute = false;
|
|
},
|
|
reset: function () {
|
|
this.setComparers();
|
|
this.set('comparer', this.comparers[0].type);
|
|
this.set('from', null);
|
|
this.set('to', null);
|
|
this.set('useCustomMessages', false);
|
|
this.shouldBuild = true;
|
|
this.hintTitleTemplate = this.defaultHintTitle;
|
|
this.buildMessages();
|
|
},
|
|
setComparers: function () {
|
|
var all = this.defaultComparers;
|
|
var comparers = [];
|
|
if (this.criterion === 'text') {
|
|
var text_comparers = [
|
|
'equalTo',
|
|
'notEqualTo'
|
|
];
|
|
for (var idx = 0; idx < all.length; idx++) {
|
|
if (text_comparers[0] == all[idx].type) {
|
|
comparers.push(all[idx]);
|
|
text_comparers.shift();
|
|
}
|
|
}
|
|
} else {
|
|
comparers = all.slice();
|
|
}
|
|
this.set('comparers', comparers);
|
|
},
|
|
setHintMessageTemplate: function () {
|
|
if (this.criterion !== 'custom' && this.criterion !== 'list') {
|
|
this.set('hintMessageTemplate', kendo.format(this.defaultHintMessage, this.criterion, this.comparerMessages[this.comparer]));
|
|
} else {
|
|
this.set('hintMessageTemplate', '');
|
|
this.set('hintMessage', '');
|
|
}
|
|
},
|
|
isAny: function () {
|
|
return this.get('criterion') === 'any';
|
|
},
|
|
isNumber: function () {
|
|
return this.get('criterion') === 'number';
|
|
},
|
|
showToForNumber: function () {
|
|
return this.showTo() && this.isNumber();
|
|
},
|
|
showToForDate: function () {
|
|
return this.showTo() && this.isDate();
|
|
},
|
|
isText: function () {
|
|
return this.get('criterion') === 'text';
|
|
},
|
|
isDate: function () {
|
|
return this.get('criterion') === 'date';
|
|
},
|
|
isList: function () {
|
|
return this.get('criterion') === 'list';
|
|
},
|
|
isCustom: function () {
|
|
return this.get('criterion') === 'custom';
|
|
},
|
|
showRemove: function () {
|
|
return this.get('hasValidation');
|
|
},
|
|
showTo: function () {
|
|
return this.get('comparer') == 'between' || this.get('comparer') == 'notBetween';
|
|
},
|
|
update: function (validation) {
|
|
this.set('hasValidation', !!validation);
|
|
if (validation) {
|
|
this.fromValidationObject(validation);
|
|
}
|
|
},
|
|
fromValidationObject: function (validation) {
|
|
this.set('criterion', validation.dataType);
|
|
this.set('comparer', validation.comparerType);
|
|
this.set('from', validation.from);
|
|
this.set('to', validation.to);
|
|
this.set('type', validation.type);
|
|
this.set('ignoreBlank', validation.allowNulls);
|
|
if (validation.messageTemplate || validation.titleTemplate) {
|
|
this.hintMessageTemplate = validation.messageTemplate;
|
|
this.hintMessage = validation.messageTemplate;
|
|
this.hintTitleTemplate = validation.titleTemplate;
|
|
this.hintTitle = validation.titleTemplate;
|
|
this.useCustomMessages = true;
|
|
this.buildMessages();
|
|
} else {
|
|
this.useCustomMessages = false;
|
|
}
|
|
},
|
|
toValidationObject: function () {
|
|
if (this.criterion === 'any') {
|
|
return null;
|
|
}
|
|
var options = {
|
|
type: this.type,
|
|
dataType: this.criterion,
|
|
comparerType: this.comparer,
|
|
from: this.from,
|
|
to: this.to,
|
|
allowNulls: this.ignoreBlank
|
|
};
|
|
if (this.useCustomMessages) {
|
|
options.messageTemplate = this.shouldBuild ? this.hintMessageTemplate : this.hintMessage;
|
|
options.titleTemplate = this.hintTitle;
|
|
}
|
|
return options;
|
|
}
|
|
});
|
|
var ValidationDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.validationDialog || MESSAGES;
|
|
var defaultOptions = {
|
|
title: messages.title,
|
|
hintMessage: messages.hintMessage,
|
|
hintTitle: messages.hintTitle,
|
|
criteria: [
|
|
{
|
|
type: 'any',
|
|
name: messages.criteria.any
|
|
},
|
|
{
|
|
type: 'number',
|
|
name: messages.criteria.number
|
|
},
|
|
{
|
|
type: 'text',
|
|
name: messages.criteria.text
|
|
},
|
|
{
|
|
type: 'date',
|
|
name: messages.criteria.date
|
|
},
|
|
{
|
|
type: 'custom',
|
|
name: messages.criteria.custom
|
|
},
|
|
{
|
|
type: 'list',
|
|
name: messages.criteria.list
|
|
}
|
|
],
|
|
comparers: [
|
|
{
|
|
type: 'greaterThan',
|
|
name: messages.comparers.greaterThan
|
|
},
|
|
{
|
|
type: 'lessThan',
|
|
name: messages.comparers.lessThan
|
|
},
|
|
{
|
|
type: 'between',
|
|
name: messages.comparers.between
|
|
},
|
|
{
|
|
type: 'notBetween',
|
|
name: messages.comparers.notBetween
|
|
},
|
|
{
|
|
type: 'equalTo',
|
|
name: messages.comparers.equalTo
|
|
},
|
|
{
|
|
type: 'notEqualTo',
|
|
name: messages.comparers.notEqualTo
|
|
},
|
|
{
|
|
type: 'greaterThanOrEqualTo',
|
|
name: messages.comparers.greaterThanOrEqualTo
|
|
},
|
|
{
|
|
type: 'lessThanOrEqualTo',
|
|
name: messages.comparers.lessThanOrEqualTo
|
|
}
|
|
],
|
|
comparerMessages: messages.comparerMessages
|
|
};
|
|
SpreadsheetDialog.fn.init.call(this, $.extend(defaultOptions, options));
|
|
},
|
|
options: {
|
|
width: 420,
|
|
criterion: 'any',
|
|
type: 'reject',
|
|
ignoreBlank: true,
|
|
useCustomMessages: false,
|
|
errorTemplate: '<div class="k-widget k-tooltip k-tooltip-validation" style="margin:0.5em"><span class="k-icon k-warning"> </span>' + '#= message #<div class="k-callout k-callout-n"></div></div>',
|
|
template: '<div class="k-edit-form-container">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.criteria #:</label></div>' + '<div class="k-edit-field">' + '<select data-role="dropdownlist" ' + 'data-text-field="name" ' + 'data-value-field="type" ' + 'data-bind="value: criterion, source: criteria" />' + '</div>' + '<div data-bind="visible: isNumber">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.comparer #:</label></div>' + '<div class="k-edit-field">' + '<select data-role="dropdownlist" ' + 'data-text-field="name" ' + 'data-value-field="type" ' + 'data-bind="value: comparer, source: comparers" />' + '</div>' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.min #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.min #" placeholder="e.g. 10" class="k-textbox" data-bind="value: from, enabled: isNumber" required="required" />' + '</div>' + '<div data-bind="visible: showTo">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.max #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.max #" placeholder="e.g. 100" class="k-textbox" data-bind="value: to, enabled: showToForNumber" required="required" />' + '</div>' + '</div>' + '</div>' + '<div data-bind="visible: isText">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.comparer #:</label></div>' + '<div class="k-edit-field">' + '<select data-role="dropdownlist" ' + 'data-text-field="name" ' + 'data-value-field="type" ' + 'data-bind="value: comparer, source: comparers" />' + '</div>' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.value #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.value #" class="k-textbox" data-bind="value: from, enabled: isText" required="required" />' + '</div>' + '</div>' + '<div data-bind="visible: isDate">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.comparer #:</label></div>' + '<div class="k-edit-field">' + '<select data-role="dropdownlist" ' + 'data-text-field="name" ' + 'data-value-field="type" ' + 'data-bind="value: comparer, source: comparers" />' + '</div>' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.start #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.start #" class="k-textbox" data-bind="value: from, enabled: isDate" required="required" />' + '</div>' + '<div data-bind="visible: showTo">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.end #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.end #" class="k-textbox" data-bind="value: to, enabled: showToForDate" required="required" />' + '</div>' + '</div>' + '</div>' + '<div data-bind="visible: isCustom">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.value #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.value #" class="k-textbox" data-bind="value: from, enabled: isCustom" required="required" />' + '</div>' + '</div>' + '<div data-bind="visible: isList">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.value #:</label></div>' + '<div class="k-edit-field">' + '<input name="#: messages.validationDialog.labels.value #" class="k-textbox" data-bind="value: from, enabled: isList" required="required" />' + '</div>' + '</div>' + '<div data-bind="invisible: isAny">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.ignoreBlank #:</label></div>' + '<div class="k-edit-field">' + '<input type="checkbox" name="ignoreBlank" id="ignoreBlank" class="k-checkbox" data-bind="checked: ignoreBlank"/>' + '<label class="k-checkbox-label" for="ignoreBlank"></label>' + '</div>' + '</div>' + '<div data-bind="invisible: isAny">' + '<div class="k-action-buttons"></div>' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.onInvalidData #:</label></div>' + '<div class="k-edit-field">' + '<input type="radio" id="validationTypeReject" name="validationType" value="reject" data-bind="checked: type" class="k-radio" />' + '<label for="validationTypeReject" class="k-radio-label">' + '#: messages.validationDialog.labels.rejectInput #' + '</label> ' + '<input type="radio" id="validationTypeWarning" name="validationType" value="warning" data-bind="checked: type" class="k-radio" />' + '<label for="validationTypeWarning" class="k-radio-label">' + '#: messages.validationDialog.labels.showWarning #' + '</label>' + '</div>' + '</div>' + '<div data-bind="invisible: isAny">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.showHint #:</label></div>' + '<div class="k-edit-field">' + '<input type="checkbox" name="useCustomMessages" id="useCustomMessages" class="k-checkbox" data-bind="checked: useCustomMessages" />' + '<label class="k-checkbox-label" for="useCustomMessages"></label>' + '</div>' + '<div data-bind="visible: useCustomMessages">' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.hintTitle #:</label></div>' + '<div class="k-edit-field">' + '<input class="k-textbox" placeholder="#: messages.validationDialog.placeholders.typeTitle #" data-bind="value: hintTitle" />' + '</div>' + '<div class="k-edit-label"><label>#: messages.validationDialog.labels.hintMessage #:</label></div>' + '<div class="k-edit-field">' + '<input class="k-textbox" placeholder="#: messages.validationDialog.placeholders.typeMessage #" data-bind="value: hintMessage" />' + '</div>' + '</div>' + '</div>' + '<div class="k-action-buttons">' + '<button class="k-button" data-bind="visible: showRemove, click: remove">#: messages.remove #</button>' + '<button class="k-button k-primary" data-bind="click: apply">#: messages.apply #</button>' + '<button class="k-button" data-bind="click: close">#: messages.cancel #</button>' + '</div>' + '</div>'
|
|
},
|
|
open: function (range) {
|
|
var options = this.options;
|
|
var element;
|
|
this.viewModel = new ValidationViewModel({
|
|
type: options.type,
|
|
defaultHintMessage: options.hintMessage,
|
|
defaultHintTitle: options.hintTitle,
|
|
defaultComparers: options.comparers.slice(0),
|
|
comparerMessages: options.comparerMessages,
|
|
criteria: options.criteria.slice(0),
|
|
criterion: options.criterion,
|
|
ignoreBlank: options.ignoreBlank,
|
|
apply: this.apply.bind(this),
|
|
close: this.close.bind(this),
|
|
remove: this.remove.bind(this)
|
|
});
|
|
this.viewModel.update(range.validation());
|
|
SpreadsheetDialog.fn.open.call(this);
|
|
element = this.dialog().element;
|
|
if (this.validatable) {
|
|
this.validatable.destroy();
|
|
}
|
|
kendo.bind(element, this.viewModel);
|
|
this.validatable = new kendo.ui.Validator(element.find('.k-edit-form-container'), {
|
|
validateOnBlur: false,
|
|
errorTemplate: this.options.errorTemplate || undefined
|
|
});
|
|
},
|
|
apply: function () {
|
|
if (this.validatable.validate()) {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'EditValidationCommand',
|
|
options: { value: this.viewModel.toValidationObject() }
|
|
});
|
|
}
|
|
},
|
|
remove: function () {
|
|
this.viewModel.set('criterion', 'any');
|
|
this.apply();
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('validation', ValidationDialog);
|
|
kendo.spreadsheet.dialogs.ValidationDialog = ValidationDialog;
|
|
var ExportAsDialog = SpreadsheetDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.exportAsDialog || MESSAGES;
|
|
SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));
|
|
this.viewModel = kendo.observable({
|
|
title: this.options.title,
|
|
name: this.options.name,
|
|
extension: this.options.extension,
|
|
fileFormats: this.options.fileFormats,
|
|
excel: options.excelExport,
|
|
pdf: {
|
|
proxyURL: options.pdfExport.proxyURL,
|
|
forceProxy: options.pdfExport.forceProxy,
|
|
title: options.pdfExport.title,
|
|
author: options.pdfExport.author,
|
|
subject: options.pdfExport.subject,
|
|
keywords: options.pdfExport.keywords,
|
|
creator: options.pdfExport.creator,
|
|
date: options.pdfExport.date,
|
|
fitWidth: this.options.pdf.fitWidth,
|
|
area: this.options.pdf.area,
|
|
areas: this.options.pdf.areas,
|
|
paperSize: this.options.pdf.paperSize,
|
|
paperSizes: this.options.pdf.paperSizes,
|
|
margin: this.options.pdf.margin,
|
|
margins: this.options.pdf.margins,
|
|
landscape: this.options.pdf.landscape,
|
|
guidelines: this.options.pdf.guidelines,
|
|
hCenter: this.options.pdf.hCenter,
|
|
vCenter: this.options.pdf.vCenter
|
|
},
|
|
apply: this.apply.bind(this),
|
|
close: this.close.bind(this)
|
|
});
|
|
this.viewModel.bind('change', function (e) {
|
|
if (e.field === 'extension') {
|
|
this.set('showPdfOptions', this.extension === '.pdf' ? true : false);
|
|
}
|
|
});
|
|
kendo.bind(this.dialog().element, this.viewModel);
|
|
},
|
|
options: {
|
|
name: 'Workbook',
|
|
extension: '.xlsx',
|
|
fileFormats: [
|
|
{
|
|
description: 'Excel Workbook (.xlsx)',
|
|
extension: '.xlsx'
|
|
},
|
|
{
|
|
description: 'Portable Document Format(.pdf)',
|
|
extension: '.pdf'
|
|
}
|
|
],
|
|
pdf: {
|
|
fitWidth: true,
|
|
area: 'workbook',
|
|
areas: [
|
|
{
|
|
area: 'workbook',
|
|
text: 'Entire Workbook'
|
|
},
|
|
{
|
|
area: 'sheet',
|
|
text: 'Active Sheet'
|
|
},
|
|
{
|
|
area: 'selection',
|
|
text: 'Selection'
|
|
}
|
|
],
|
|
paperSize: 'a4',
|
|
paperSizes: [
|
|
{
|
|
value: 'a2',
|
|
text: 'A2 (420 mm \xD7 594 mm) '
|
|
},
|
|
{
|
|
value: 'a3',
|
|
text: 'A3 (297 mm x 420 mm) '
|
|
},
|
|
{
|
|
value: 'a4',
|
|
text: 'A4 (210 mm x 297 mm) '
|
|
},
|
|
{
|
|
value: 'a5',
|
|
text: 'A5 (148 mm x 210 mm) '
|
|
},
|
|
{
|
|
value: 'b3',
|
|
text: 'B3 (353 mm \xD7 500 mm) '
|
|
},
|
|
{
|
|
value: 'b4',
|
|
text: 'B4 (250 mm x 353 mm) '
|
|
},
|
|
{
|
|
value: 'b5',
|
|
text: 'B5 (176 mm x 250 mm) '
|
|
},
|
|
{
|
|
value: 'folio',
|
|
text: 'Folio (8.5" x 13") '
|
|
},
|
|
{
|
|
value: 'legal',
|
|
text: 'Legal (8.5" x 14") '
|
|
},
|
|
{
|
|
value: 'letter',
|
|
text: 'Letter (8.5" x 11") '
|
|
},
|
|
{
|
|
value: 'tabloid',
|
|
text: 'Tabloid (11" x 17") '
|
|
},
|
|
{
|
|
value: 'executive',
|
|
text: 'Executive (7.25" x 10.5")'
|
|
}
|
|
],
|
|
margin: {
|
|
bottom: '0.75in',
|
|
left: '0.7in',
|
|
right: '0.7in',
|
|
top: '0.75in'
|
|
},
|
|
margins: [
|
|
{
|
|
value: {
|
|
bottom: '0.75in',
|
|
left: '0.7in',
|
|
right: '0.7in',
|
|
top: '0.75in'
|
|
},
|
|
text: 'Normal'
|
|
},
|
|
{
|
|
value: {
|
|
bottom: '0.75in',
|
|
left: '0.25in',
|
|
right: '0.25in',
|
|
top: '0.75in'
|
|
},
|
|
text: 'Narrow'
|
|
},
|
|
{
|
|
value: {
|
|
bottom: '1in',
|
|
left: '1in',
|
|
right: '1in',
|
|
top: '1in'
|
|
},
|
|
text: 'Wide'
|
|
}
|
|
],
|
|
landscape: true,
|
|
guidelines: true,
|
|
hCenter: true,
|
|
vCenter: true
|
|
},
|
|
width: 520,
|
|
template: '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.fileName #:</label></div>' + '<div class=\'k-edit-field\'>' + '<input class=\'k-textbox\' data-bind=\'value: name\' />' + '</div>' + '<div >' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.saveAsType #:</label></div>' + '<div class=\'k-edit-field\'>' + '<select data-role=\'dropdownlist\' class=\'k-file-format\' ' + 'data-text-field=\'description\' ' + 'data-value-field=\'extension\' ' + 'data-bind=\'value: extension, source: fileFormats\' />' + '</div>' + '</div>' + '<div class=\'export-config\' data-bind=\'visible: showPdfOptions\'>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.exportArea #:</label></div>' + '<div class=\'k-edit-field\'>' + '<select data-role=\'dropdownlist\' class=\'k-file-format\' ' + 'data-text-field=\'text\' ' + 'data-value-field=\'area\' ' + 'data-bind=\'value: pdf.area, source: pdf.areas\' />' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.paperSize#:</label></div>' + '<div class=\'k-edit-field\'>' + '<select data-role=\'dropdownlist\' class=\'k-file-format\' ' + 'data-text-field=\'text\' ' + 'data-value-field=\'value\' ' + 'data-bind=\'value: pdf.paperSize, source: pdf.paperSizes\' />' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.margins #:</label></div>' + '<div class=\'k-edit-field\'>' + '<select data-role=\'dropdownlist\' class=\'k-file-format\' ' + 'data-value-primitive=\'true\'' + 'data-text-field=\'text\' ' + 'data-value-field=\'value\' ' + 'data-bind=\'value: pdf.margin, source: pdf.margins\' />' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.orientation #:</label></div>' + '<div class=\'k-edit-field\'>' + '<input type=\'radio\' id=\'k-orientation-portrait\' name=\'orientation\' data-type=\'boolean\' data-bind=\'checked: pdf.landscape\' value=\'false\' /><label class=\'k-orientation-label k-orientation-portrait-label\' for=\'k-orientation-portrait\'></label>' + '<input type=\'radio\' id=\'k-orientation-landscape\' name=\'orientation\' data-type=\'boolean\' data-bind=\'checked: pdf.landscape\' value=\'true\' /><label class=\'k-orientation-label k-orientation-landscape-label\' for=\'k-orientation-landscape\'></label>' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.print #:</label></div>' + '<div class=\'k-edit-field\'>' + '<input class=\'k-checkbox\' id=\'guidelines\' type=\'checkbox\' data-bind=\'checked: pdf.guidelines\'/><label class=\'k-checkbox-label\' for=\'guidelines\'>#: messages.exportAsDialog.labels.guidelines#</label>' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.scale #:</label></div>' + '<div class=\'k-edit-field\'>' + '<input class=\'k-checkbox\' id=\'fitWidth\' type=\'checkbox\' data-bind=\'checked: pdf.fitWidth\'/><label class=\'k-checkbox-label\' for=\'fitWidth\'>#: messages.exportAsDialog.labels.fit #</label>' + '</div>' + '<div class=\'k-edit-label\'><label>#: messages.exportAsDialog.labels.center #:</label></div>' + '<div class=\'k-edit-field\'>' + '<input class=\'k-checkbox\' id=\'hCenter\' type=\'checkbox\' data-bind=\'checked: pdf.hCenter\'/><label class=\'k-checkbox-label\' for=\'hCenter\'>#: messages.exportAsDialog.labels.horizontally #</label>' + '<input class=\'k-checkbox\' id=\'vCenter\' type=\'checkbox\' data-bind=\'checked: pdf.vCenter\'/><label class=\'k-checkbox-label\' for=\'vCenter\'>#: messages.exportAsDialog.labels.vertically #</label>' + '</div>' + '<div class=\'k-page-orientation\' data-bind=\'css: {k-page-landscape: pdf.landscape}\'>' + '<div class=\'k-margins-horizontal\'></div>' + '<div class=\'k-margins-vertical\'></div>' + '</div>' + '</div>' + '<div class=\'k-action-buttons\'>' + '<button class=\'k-button k-primary\' data-bind=\'click: apply\'>#: messages.save #</button>' + '<button class=\'k-button\' data-bind=\'click: close\'>#: messages.cancel #</button>' + '</div>'
|
|
},
|
|
apply: function () {
|
|
SpreadsheetDialog.fn.apply.call(this);
|
|
this.trigger('action', {
|
|
command: 'SaveAsCommand',
|
|
options: this.viewModel
|
|
});
|
|
}
|
|
});
|
|
kendo.spreadsheet.dialogs.register('exportAs', ExportAsDialog);
|
|
var ModifyMergedDialog = MessageDialog.extend({ options: { messageId: 'modifyMergedDialog.errorMessage' } });
|
|
kendo.spreadsheet.dialogs.register('modifyMerged', ModifyMergedDialog);
|
|
var OverflowDialog = MessageDialog.extend({ options: { messageId: 'overflowDialog.errorMessage' } });
|
|
kendo.spreadsheet.dialogs.register('overflow', OverflowDialog);
|
|
var UseKeyboardDialog = MessageDialog.extend({
|
|
init: function (options) {
|
|
var messages = kendo.spreadsheet.messages.dialogs.useKeyboardDialog || MESSAGES;
|
|
SpreadsheetDialog.fn.init.call(this, $.extend({ title: messages.title }, options));
|
|
},
|
|
options: { template: '#: messages.useKeyboardDialog.errorMessage #' + '<div>Ctrl+C #: messages.useKeyboardDialog.labels.forCopy #</div>' + '<div>Ctrl+X #: messages.useKeyboardDialog.labels.forCut #</div>' + '<div>Ctrl+V #: messages.useKeyboardDialog.labels.forPaste #</div>' + '<div class="k-action-buttons">' + '<button class=\'k-button k-primary\' data-bind=\'click: close\'>' + '#= messages.okText #' + '</button>' + '</div>' }
|
|
});
|
|
kendo.spreadsheet.dialogs.register('useKeyboard', UseKeyboardDialog);
|
|
var UnsupportedSelectionDialog = MessageDialog.extend({ options: { messageId: 'unsupportedSelectionDialog.errorMessage' } });
|
|
kendo.spreadsheet.dialogs.register('unsupportedSelection', UnsupportedSelectionDialog);
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/sheetbinder', [
|
|
'kendo.core',
|
|
'kendo.data',
|
|
'spreadsheet/sheet'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var SheetDataSourceBinder = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.options = $.extend({}, this.options, options);
|
|
this.columns = this._normalizeColumns(this.options.columns);
|
|
this._sheet();
|
|
this._dataSource();
|
|
this._header();
|
|
this._boundRowsCount = 0;
|
|
this.dataSource.fetch();
|
|
},
|
|
_sheet: function () {
|
|
this.sheet = this.options.sheet;
|
|
this._sheetChangeHandler = this._sheetChange.bind(this);
|
|
this._sheetDeleteRowHandler = this._sheetDeleteRow.bind(this);
|
|
this._sheetInsertRowHandler = this._sheetInsertRow.bind(this);
|
|
this.sheet.bind('change', this._sheetChangeHandler).bind('deleteRow', this._sheetDeleteRowHandler).bind('insertRow', this._sheetInsertRowHandler);
|
|
},
|
|
_sheetInsertRow: function (e) {
|
|
if (e.index !== undefined) {
|
|
this.dataSource.insert(Math.max(e.index - 1, 0), {});
|
|
}
|
|
},
|
|
_sheetDeleteRow: function (e) {
|
|
if (e.index !== undefined) {
|
|
var dataSource = this.dataSource;
|
|
var model = dataSource.view()[e.index - 1];
|
|
if (model) {
|
|
dataSource.remove(model);
|
|
}
|
|
}
|
|
},
|
|
_header: function () {
|
|
this.sheet.batch(function () {
|
|
this.columns.forEach(function (column, index) {
|
|
this.sheet.range(0, index).value(column.title);
|
|
}.bind(this));
|
|
}.bind(this));
|
|
},
|
|
_sheetChange: function (e) {
|
|
if (e.recalc && e.ref) {
|
|
var dataSource = this.dataSource;
|
|
var data = dataSource.view();
|
|
var columns = this.columns;
|
|
if (!columns.length && data.length) {
|
|
columns = Object.keys(data[0].toJSON());
|
|
}
|
|
this._skipRebind = true;
|
|
var values = this.sheet.range(e.ref).values();
|
|
e.ref.forEach(function (ref) {
|
|
ref = ref.toRangeRef();
|
|
var record;
|
|
var valueIndex = 0;
|
|
for (var ri = ref.topLeft.row; ri <= ref.bottomRight.row; ri++) {
|
|
record = data[ri - 1];
|
|
if (!record) {
|
|
record = dataSource.insert(ri - 1, {});
|
|
data = dataSource.view();
|
|
}
|
|
var colValueIndex = 0;
|
|
for (var ci = ref.topLeft.col; ci <= ref.bottomRight.col && ci < columns.length; ci++) {
|
|
record.set(columns[ci].field, values[valueIndex][colValueIndex++]);
|
|
}
|
|
valueIndex++;
|
|
}
|
|
});
|
|
this._boundRowsCount = dataSource.view().length;
|
|
this._skipRebind = false;
|
|
}
|
|
},
|
|
_normalizeColumns: function (columns) {
|
|
return columns.map(function (column) {
|
|
var field = column.field || column;
|
|
return {
|
|
field: field,
|
|
title: column.title || field
|
|
};
|
|
});
|
|
},
|
|
_dataSource: function () {
|
|
var options = this.options;
|
|
var dataSource = options.dataSource;
|
|
dataSource = Array.isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (this.dataSource && this._changeHandler) {
|
|
this.dataSource.unbind('change', this._changeHandler);
|
|
} else {
|
|
this._changeHandler = this._change.bind(this);
|
|
}
|
|
this.dataSource = kendo.data.DataSource.create(dataSource).bind('change', this._changeHandler);
|
|
},
|
|
_change: function () {
|
|
if (this._skipRebind) {
|
|
return;
|
|
}
|
|
var data = this.dataSource.view();
|
|
var columns = this.columns;
|
|
if (!columns.length && data.length) {
|
|
this.columns = columns = this._normalizeColumns(Object.keys(data[0].toJSON()));
|
|
this._header();
|
|
}
|
|
var getters = columns.map(function (column) {
|
|
return kendo.getter(column.field);
|
|
});
|
|
this.sheet.batch(function () {
|
|
var length = Math.max(data.length, this._boundRowsCount);
|
|
for (var idx = 0; idx < length; idx++) {
|
|
for (var getterIdx = 0; getterIdx < getters.length; getterIdx++) {
|
|
var value = data[idx] ? getters[getterIdx](data[idx]) : null;
|
|
this.sheet.range(idx + 1, getterIdx).value(value);
|
|
}
|
|
}
|
|
}.bind(this));
|
|
this._boundRowsCount = data.length;
|
|
},
|
|
destroy: function () {
|
|
this.dataSource.unbind('change', this._changeHandler);
|
|
this.sheet.unbind('change', this._sheetChangeHandler).unbind('deleteRow', this._sheetDeleteRowHandler).unbind('insertRow', this._sheetInsertRowHandler);
|
|
},
|
|
options: { columns: [] }
|
|
});
|
|
kendo.spreadsheet.SheetDataSourceBinder = SheetDataSourceBinder;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/filtermenu', [
|
|
'kendo.core',
|
|
'kendo.popup',
|
|
'kendo.treeview',
|
|
'kendo.numerictextbox',
|
|
'kendo.datepicker',
|
|
'kendo.datetimepicker'
|
|
], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Widget = kendo.ui.Widget;
|
|
var classNames = {
|
|
details: 'k-details',
|
|
button: 'k-button',
|
|
detailsSummary: 'k-details-summary',
|
|
detailsContent: 'k-details-content',
|
|
icon: 'k-icon k-font-icon',
|
|
iconCollapse: 'k-i-collapse-se',
|
|
iconExpand: 'k-i-expand-e',
|
|
iconSearch: 'k-i-search',
|
|
textbox: 'k-textbox',
|
|
wrapper: 'k-spreadsheet-filter-menu',
|
|
filterByCondition: 'k-spreadsheet-condition-filter',
|
|
filterByValue: 'k-spreadsheet-value-filter',
|
|
valuesTreeViewWrapper: 'k-spreadsheet-value-treeview-wrapper',
|
|
actionButtons: 'k-action-buttons'
|
|
};
|
|
var Details = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.addClass(FilterMenu.classNames.details);
|
|
this._summary = this.element.find('.' + FilterMenu.classNames.detailsSummary).on('click', this._toggle.bind(this));
|
|
var iconClass = options.expanded ? FilterMenu.classNames.iconCollapse : FilterMenu.classNames.iconExpand;
|
|
this._icon = $('<span />', { 'class': FilterMenu.classNames.icon + ' ' + iconClass }).prependTo(this._summary);
|
|
this._container = kendo.wrap(this._summary.next(), true);
|
|
if (!options.expanded) {
|
|
this._container.hide();
|
|
}
|
|
},
|
|
options: { name: 'Details' },
|
|
events: ['toggle'],
|
|
visible: function () {
|
|
return this.options.expanded;
|
|
},
|
|
toggle: function (show) {
|
|
var animation = kendo.fx(this._container).expand('vertical');
|
|
animation.stop()[show ? 'reverse' : 'play']();
|
|
this._icon.toggleClass(FilterMenu.classNames.iconExpand, show).toggleClass(FilterMenu.classNames.iconCollapse, !show);
|
|
this.options.expanded = !show;
|
|
},
|
|
_toggle: function () {
|
|
var show = this.visible();
|
|
this.toggle(show);
|
|
this.trigger('toggle', { show: show });
|
|
}
|
|
});
|
|
var FILTERMENU_MESSAGES = kendo.spreadsheet.messages.filterMenu = {
|
|
sortAscending: 'Sort range A to Z',
|
|
sortDescending: 'Sort range Z to A',
|
|
filterByValue: 'Filter by value',
|
|
filterByCondition: 'Filter by condition',
|
|
apply: 'Apply',
|
|
search: 'Search',
|
|
addToCurrent: 'Add to current selection',
|
|
clear: 'Clear',
|
|
blanks: '(Blanks)',
|
|
operatorNone: 'None',
|
|
and: 'AND',
|
|
or: 'OR',
|
|
operators: {
|
|
string: {
|
|
contains: 'Text contains',
|
|
doesnotcontain: 'Text does not contain',
|
|
startswith: 'Text starts with',
|
|
endswith: 'Text ends with'
|
|
},
|
|
date: {
|
|
eq: 'Date is',
|
|
neq: 'Date is not',
|
|
lt: 'Date is before',
|
|
gt: 'Date is after'
|
|
},
|
|
number: {
|
|
eq: 'Is equal to',
|
|
neq: 'Is not equal to',
|
|
gte: 'Is greater than or equal to',
|
|
gt: 'Is greater than',
|
|
lte: 'Is less than or equal to',
|
|
lt: 'Is less than'
|
|
}
|
|
}
|
|
};
|
|
kendo.data.binders.spreadsheetFilterValue = kendo.data.Binder.extend({
|
|
init: function (element, bindings, options) {
|
|
kendo.data.Binder.fn.init.call(this, element, bindings, options);
|
|
this._change = $.proxy(this.change, this);
|
|
$(this.element).on('change', this._change);
|
|
},
|
|
refresh: function () {
|
|
var that = this, value = that.bindings.spreadsheetFilterValue.get();
|
|
$(that.element).val(value instanceof Date ? '' : value);
|
|
},
|
|
change: function () {
|
|
var value = this.element.value;
|
|
this.bindings.spreadsheetFilterValue.set(value);
|
|
}
|
|
});
|
|
kendo.data.binders.widget.spreadsheetFilterValue = kendo.data.Binder.extend({
|
|
init: function (widget, bindings, options) {
|
|
kendo.data.Binder.fn.init.call(this, widget.element[0], bindings, options);
|
|
this.widget = widget;
|
|
this._change = $.proxy(this.change, this);
|
|
this.widget.first('change', this._change);
|
|
},
|
|
refresh: function () {
|
|
var binding = this.bindings.spreadsheetFilterValue, value = binding.get(), type = $(this.widget.element).data('filterType');
|
|
if (type === 'date' && value instanceof Date || type === 'number' && !isNaN(value)) {
|
|
this.widget.value(value);
|
|
} else {
|
|
this.widget.value(null);
|
|
}
|
|
},
|
|
change: function () {
|
|
var value = this.widget.value(), binding = this.bindings.spreadsheetFilterValue;
|
|
binding.set(value);
|
|
}
|
|
});
|
|
var templates = {
|
|
filterByValue: '<div class=\'' + classNames.detailsSummary + '\'>#= messages.filterByValue #</div>' + '<div class=\'' + classNames.detailsContent + '\'>' + '<div class=\'k-textbox k-space-right\'>' + '<input placeholder=\'#= messages.search #\' data-#=ns#bind=\'events: { input: filterValues }\' />' + '<span class=\'k-icon k-font-icon k-i-search\' />' + '</div>' + '<div data-#=ns#bind=\'visible: hasActiveSearch\'><input class=\'k-checkbox\' type=\'checkbox\' data-#=ns#bind=\'checked: appendToSearch\' id=\'_#=guid#\' /><label class=\'k-checkbox-label\' for=\'_#=guid#\'>#= messages.addToCurrent #</label></div>' + '<div class=\'' + classNames.valuesTreeViewWrapper + '\'>' + '<div data-#=ns#role=\'treeview\' ' + 'data-#=ns#checkboxes=\'{ checkChildren: true }\' ' + 'data-#=ns#bind=\'source: valuesDataSource, events: { check: valuesChange, select: valueSelect }\' ' + '/>' + '</div>' + '</div>',
|
|
filterByCondition: '<div class=\'' + classNames.detailsSummary + '\'>#= messages.filterByCondition #</div>' + '<div class=\'' + classNames.detailsContent + '\'>' + '<div>' + '<select ' + 'data-#=ns#role="dropdownlist"' + 'data-#=ns#bind="value: operator, source: operators, events: { change: operatorChange } "' + 'data-value-primitive="false"' + 'data-option-label="#=messages.operatorNone#"' + 'data-height="auto"' + 'data-text-field="text"' + 'data-value-field="unique">' + '</select>' + '</div>' + '<div data-#=ns#bind="visible: isString">' + '<input data-filter-type="string" data-#=ns#bind="spreadsheetFilterValue: customFilter.criteria[0].value" class="k-textbox" />' + '</div>' + '<div data-#=ns#bind="visible: isNumber">' + '<input data-filter-type="number" data-#=ns#role="numerictextbox" data-#=ns#bind="spreadsheetFilterValue: customFilter.criteria[0].value" />' + '</div>' + '<div data-#=ns#bind="visible: isDate">' + '<input data-filter-type="date" data-#=ns#role="datepicker" data-#=ns#bind="spreadsheetFilterValue: customFilter.criteria[0].value" />' + '</div>' + '</div>',
|
|
menuItem: '<li data-command=\'#=command#\' data-dir=\'#=dir#\'>' + '<span class=\'k-icon k-font-icon k-i-#=iconClass#\'></span>#=text#' + '</li>',
|
|
actionButtons: '<button data-#=ns#bind=\'click: apply\' class=\'k-button k-primary\'>#=messages.apply#</button>' + '<button data-#=ns#bind=\'click: clear\' class=\'k-button\'>#=messages.clear#</button>'
|
|
};
|
|
function distinctValues(values) {
|
|
var hash = {};
|
|
var result = [];
|
|
for (var i = 0; i < values.length; i++) {
|
|
if (!hash[values[i].value]) {
|
|
hash[values[i].value] = values[i];
|
|
result.push(values[i]);
|
|
} else if (!hash[values[i].value].checked && values[i].checked) {
|
|
hash[values[i].value].checked = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function filter(dataSource, query) {
|
|
var hasVisibleChildren = false;
|
|
var data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
|
|
for (var i = 0; i < data.length; i++) {
|
|
var item = data[i];
|
|
var text = item.text.toString().toLowerCase();
|
|
var itemVisible = query === true || query === '' || text.indexOf(query) >= 0;
|
|
var anyVisibleChildren = filter(item.children, itemVisible || query);
|
|
hasVisibleChildren = hasVisibleChildren || anyVisibleChildren || itemVisible;
|
|
item.hidden = !itemVisible && !anyVisibleChildren;
|
|
item.checked = !item.hidden;
|
|
}
|
|
if (data) {
|
|
dataSource.filter({
|
|
field: 'hidden',
|
|
operator: 'neq',
|
|
value: true
|
|
});
|
|
}
|
|
return hasVisibleChildren;
|
|
}
|
|
function uncheckAll(dataSource) {
|
|
var data = dataSource instanceof kendo.data.HierarchicalDataSource && dataSource.data();
|
|
for (var i = 0; i < data.length; i++) {
|
|
var item = data[i];
|
|
item.checked = false;
|
|
if (item.hasChildren) {
|
|
uncheckAll(item.children);
|
|
}
|
|
}
|
|
}
|
|
var FilterMenuViewModel = kendo.spreadsheet.FilterMenuViewModel = kendo.data.ObservableObject.extend({
|
|
valuesChange: function (e) {
|
|
var dataSource = e ? e.sender.dataSource : this.valuesDataSource;
|
|
var checked = function (item) {
|
|
return item.checked && item.value;
|
|
};
|
|
var value = function (item) {
|
|
return item.dataType === 'date' ? kendo.spreadsheet.dateToNumber(item.value) : item.value;
|
|
};
|
|
var unique = function (value, index, array) {
|
|
return array.lastIndexOf(value) === index;
|
|
};
|
|
var data = dataSource.data();
|
|
var values = data[0].children.data().toJSON();
|
|
var blanks = values.filter(function (item) {
|
|
return item.dataType === 'blank';
|
|
});
|
|
blanks = blanks.length ? blanks[0].checked : false;
|
|
values = values.filter(checked).map(value);
|
|
if (this.appendToSearch && this.valueFilter && this.valueFilter.values.length) {
|
|
values = values.concat(this.valueFilter.values.toJSON()).sort().filter(unique);
|
|
}
|
|
this.set('valueFilter', {
|
|
values: values,
|
|
blanks: blanks
|
|
});
|
|
},
|
|
valueSelect: function (e) {
|
|
e.preventDefault();
|
|
var node = e.sender.dataItem(e.node);
|
|
node.set('checked', !node.checked);
|
|
},
|
|
hasActiveSearch: false,
|
|
appendToSearch: false,
|
|
filterValues: function (e) {
|
|
var query = typeof e == 'string' ? e : $(e.target).val().toLowerCase();
|
|
var dataSource = this.valuesDataSource;
|
|
this.set('hasActiveSearch', !!query);
|
|
uncheckAll(dataSource);
|
|
filter(dataSource, query);
|
|
},
|
|
reset: function () {
|
|
this.set('customFilter', {
|
|
logic: 'and',
|
|
criteria: [{
|
|
operator: null,
|
|
value: null
|
|
}]
|
|
});
|
|
this.set('valueFilter', { values: [] });
|
|
},
|
|
operatorChange: function (e) {
|
|
var dataItem = e.sender.dataItem();
|
|
this.set('operatorType', dataItem.type);
|
|
this.set('customFilter.criteria[0].operator', dataItem.value);
|
|
},
|
|
isNone: function () {
|
|
return this.get('operatorType') === undefined;
|
|
},
|
|
isString: function () {
|
|
return this.get('operatorType') === 'string';
|
|
},
|
|
isNumber: function () {
|
|
return this.get('operatorType') === 'number';
|
|
},
|
|
isDate: function () {
|
|
return this.get('operatorType') === 'date';
|
|
}
|
|
});
|
|
function flattenOperators(operators) {
|
|
var messages = FILTERMENU_MESSAGES.operators;
|
|
var result = [];
|
|
for (var type in operators) {
|
|
if (!operators.hasOwnProperty(type)) {
|
|
continue;
|
|
}
|
|
for (var operator in operators[type]) {
|
|
if (!operators[type].hasOwnProperty(operator)) {
|
|
continue;
|
|
}
|
|
result.push({
|
|
text: messages[type][operator],
|
|
value: operator,
|
|
unique: type + '_' + operator,
|
|
type: type
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
var FilterMenuController = kendo.spreadsheet.FilterMenuController = {
|
|
valuesTree: function (range, column) {
|
|
return [{
|
|
text: 'All',
|
|
expanded: true,
|
|
checked: true,
|
|
items: this.values(range.resize({ top: 1 }), column)
|
|
}];
|
|
},
|
|
values: function (range, column) {
|
|
var values = [];
|
|
var messages = FILTERMENU_MESSAGES;
|
|
var columnRange = range.column(column);
|
|
var sheet = range.sheet();
|
|
columnRange.forEachCell(function (row, col, cell) {
|
|
if (cell.value === undefined) {
|
|
cell.dataType = 'blank';
|
|
} else if (cell.format) {
|
|
cell.dataType = kendo.spreadsheet.formatting.type(cell.value, cell.format);
|
|
} else {
|
|
cell.dataType = typeof cell.value;
|
|
}
|
|
if (cell.value !== null && cell.format) {
|
|
cell.text = kendo.spreadsheet.formatting.text(cell.value, cell.format);
|
|
} else {
|
|
cell.text = cell.value ? cell.value : messages.blanks;
|
|
}
|
|
if (cell.dataType === 'percent') {
|
|
cell.dataType = 'number';
|
|
}
|
|
if (cell.dataType === 'date') {
|
|
cell.value = kendo.spreadsheet.numberToDate(cell.value);
|
|
}
|
|
if (cell.hasOwnProperty('wrap')) {
|
|
delete cell.wrap;
|
|
}
|
|
cell.checked = !sheet.isHiddenRow(row);
|
|
values.push(cell);
|
|
});
|
|
values = distinctValues(values);
|
|
values.sort(function (a, b) {
|
|
if (a.dataType === b.dataType) {
|
|
return 0;
|
|
}
|
|
if (a.dataType === 'blank' || b.dataType === 'blank') {
|
|
return a.dataType === 'blank' ? -1 : 1;
|
|
}
|
|
if (a.dataType === 'number' || b.dataType === 'number') {
|
|
return a.dataType === 'number' ? -1 : 1;
|
|
}
|
|
if (a.dataType === 'date' || b.dataType === 'date') {
|
|
return a.dataType === 'date' ? -1 : 1;
|
|
}
|
|
return 0;
|
|
});
|
|
return values;
|
|
},
|
|
filterType: function (range, column) {
|
|
var sheet = range.sheet();
|
|
var filter = this.filterForColumn(column, sheet);
|
|
var type;
|
|
filter = filter && filter.filter.toJSON();
|
|
if (filter && filter.filter == 'custom') {
|
|
var value = filter.criteria[0].value;
|
|
if (value instanceof Date) {
|
|
type = 'date';
|
|
} else if (typeof value == 'string') {
|
|
type = 'string';
|
|
} else if (typeof value == 'number') {
|
|
type = 'number';
|
|
}
|
|
}
|
|
if (!type) {
|
|
var topValue = this.values(range.row(1), column)[0];
|
|
type = topValue && topValue.dataType;
|
|
if (type == 'blank') {
|
|
type = null;
|
|
}
|
|
}
|
|
return type;
|
|
},
|
|
filterForColumn: function (column, sheet) {
|
|
var allFilters = sheet.filter();
|
|
var filters;
|
|
if (allFilters) {
|
|
filters = allFilters.columns.filter(function (item) {
|
|
return item.index === column;
|
|
})[0];
|
|
}
|
|
return filters;
|
|
},
|
|
filter: function (column, sheet) {
|
|
var columnFilters = this.filterForColumn(column, sheet);
|
|
if (!columnFilters) {
|
|
return;
|
|
}
|
|
var options = columnFilters.filter.toJSON();
|
|
var type = options.filter;
|
|
delete options.filter;
|
|
var result = {
|
|
type: type,
|
|
options: options
|
|
};
|
|
var criteria = options.criteria;
|
|
if (criteria && criteria.length) {
|
|
result.operator = criteria[0].operator;
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
var FilterMenu = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.call(this, element, options);
|
|
this.element.addClass(FilterMenu.classNames.wrapper);
|
|
this.viewModel = new FilterMenuViewModel({
|
|
active: 'value',
|
|
operator: null,
|
|
operators: flattenOperators(this.options.operators),
|
|
clear: this.clear.bind(this),
|
|
apply: this.apply.bind(this)
|
|
});
|
|
this._filterInit();
|
|
this._popup();
|
|
this._sort();
|
|
this._filterByCondition();
|
|
this._filterByValue();
|
|
this._actionButtons();
|
|
},
|
|
options: {
|
|
name: 'FilterMenu',
|
|
column: 0,
|
|
range: null,
|
|
operators: {
|
|
string: {
|
|
contains: 'Text contains',
|
|
doesnotcontain: 'Text does not contain',
|
|
startswith: 'Text starts with',
|
|
endswith: 'Text ends with'
|
|
},
|
|
date: {
|
|
eq: 'Date is',
|
|
neq: 'Date is not',
|
|
lt: 'Date is before',
|
|
gt: 'Date is after'
|
|
},
|
|
number: {
|
|
eq: 'Is equal to',
|
|
neq: 'Is not equal to',
|
|
gte: 'Is greater than or equal to',
|
|
gt: 'Is greater than',
|
|
lte: 'Is less than or equal to',
|
|
lt: 'Is less than'
|
|
}
|
|
}
|
|
},
|
|
events: ['action'],
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.menu.destroy();
|
|
this.valuesTreeView.destroy();
|
|
this.popup.destroy();
|
|
},
|
|
openFor: function (anchor) {
|
|
this.popup.setOptions({ anchor: anchor });
|
|
this.popup.open();
|
|
},
|
|
close: function () {
|
|
this.popup.close();
|
|
},
|
|
clear: function () {
|
|
this.action({
|
|
command: 'ClearFilterCommand',
|
|
options: { column: this.options.column }
|
|
});
|
|
this.viewModel.reset();
|
|
this.close();
|
|
},
|
|
apply: function () {
|
|
this._active();
|
|
var options = {
|
|
operatingRange: this.options.range,
|
|
column: this.options.column
|
|
};
|
|
var valueFilter;
|
|
var customFilter;
|
|
if (this.viewModel.active === 'value') {
|
|
this.viewModel.valuesChange({ sender: this.valuesTreeView });
|
|
valueFilter = this.viewModel.valueFilter.toJSON();
|
|
if (valueFilter.values && valueFilter.values.length) {
|
|
options.valueFilter = valueFilter;
|
|
}
|
|
} else if (this.viewModel.active === 'custom') {
|
|
customFilter = this.viewModel.customFilter.toJSON();
|
|
if (customFilter.criteria.length && customFilter.criteria[0].value !== null) {
|
|
options.customFilter = customFilter;
|
|
}
|
|
}
|
|
if (options.valueFilter || options.customFilter) {
|
|
this.action({
|
|
command: 'ApplyFilterCommand',
|
|
options: options
|
|
});
|
|
}
|
|
},
|
|
action: function (options) {
|
|
this.trigger('action', $.extend({}, options));
|
|
},
|
|
_filterInit: function () {
|
|
var column = this.options.column;
|
|
var range = this.options.range;
|
|
var sheet = range.sheet();
|
|
var activeFilter = FilterMenuController.filter(column, sheet);
|
|
if (activeFilter) {
|
|
var filterType = FilterMenuController.filterType(range, column);
|
|
this.viewModel.set('active', activeFilter.type);
|
|
this.viewModel.set(activeFilter.type + 'Filter', activeFilter.options);
|
|
if (activeFilter.type == 'custom') {
|
|
this.viewModel.set('operator', filterType + '_' + activeFilter.operator);
|
|
this.viewModel.set('operatorType', filterType);
|
|
}
|
|
} else {
|
|
this.viewModel.reset();
|
|
}
|
|
},
|
|
_popup: function () {
|
|
this.popup = this.element.kendoPopup({ copyAnchorStyles: false }).data('kendoPopup');
|
|
},
|
|
_sort: function () {
|
|
var template = kendo.template(FilterMenu.templates.menuItem);
|
|
var messages = FILTERMENU_MESSAGES;
|
|
var items = [
|
|
{
|
|
command: 'sort',
|
|
dir: 'asc',
|
|
text: messages.sortAscending,
|
|
iconClass: 'sort-asc'
|
|
},
|
|
{
|
|
command: 'sort',
|
|
dir: 'desc',
|
|
text: messages.sortDescending,
|
|
iconClass: 'sort-desc'
|
|
}
|
|
];
|
|
var ul = $('<ul />', { 'html': kendo.render(template, items) }).appendTo(this.element);
|
|
this.menu = ul.kendoMenu({
|
|
orientation: 'vertical',
|
|
select: function (e) {
|
|
var dir = $(e.item).data('dir');
|
|
var range = this.options.range.resize({ top: 1 });
|
|
var options = {
|
|
value: dir,
|
|
sheet: false,
|
|
operatingRange: range,
|
|
column: this.options.column
|
|
};
|
|
if (range.isSortable()) {
|
|
this.action({
|
|
command: 'SortCommand',
|
|
options: options
|
|
});
|
|
} else {
|
|
this.close();
|
|
}
|
|
}.bind(this)
|
|
}).data('kendoMenu');
|
|
},
|
|
_appendTemplate: function (template, className, details, expanded) {
|
|
var compiledTemplate = kendo.template(template);
|
|
var wrapper = $('<div class=\'' + className + '\'/>').html(compiledTemplate({
|
|
messages: FILTERMENU_MESSAGES,
|
|
guid: kendo.guid(),
|
|
ns: kendo.ns
|
|
}));
|
|
this.element.append(wrapper);
|
|
if (details) {
|
|
details = new Details(wrapper, {
|
|
expanded: expanded,
|
|
toggle: this._detailToggle.bind(this)
|
|
});
|
|
}
|
|
kendo.bind(wrapper, this.viewModel);
|
|
return wrapper;
|
|
},
|
|
_detailToggle: function (e) {
|
|
this.element.find('[data-role=details]').not(e.sender.element).data('kendoDetails').toggle(!e.show);
|
|
},
|
|
_filterByCondition: function () {
|
|
var isExpanded = this.viewModel.active === 'custom';
|
|
this._appendTemplate(FilterMenu.templates.filterByCondition, FilterMenu.classNames.filterByCondition, true, isExpanded);
|
|
},
|
|
_filterByValue: function () {
|
|
var isExpanded = this.viewModel.active === 'value';
|
|
var wrapper = this._appendTemplate(FilterMenu.templates.filterByValue, FilterMenu.classNames.filterByValue, true, isExpanded);
|
|
this.valuesTreeView = wrapper.find('[data-role=treeview]').data('kendoTreeView');
|
|
var values = FilterMenuController.valuesTree(this.options.range, this.options.column);
|
|
this.viewModel.set('valuesDataSource', new kendo.data.HierarchicalDataSource({ data: values }));
|
|
},
|
|
_actionButtons: function () {
|
|
this._appendTemplate(FilterMenu.templates.actionButtons, FilterMenu.classNames.actionButtons, false);
|
|
},
|
|
_active: function () {
|
|
var activeContainer = this.element.find('[data-role=details]').filter(function (index, element) {
|
|
return $(element).data('kendoDetails').visible();
|
|
});
|
|
if (activeContainer.hasClass(FilterMenu.classNames.filterByValue)) {
|
|
this.viewModel.set('active', 'value');
|
|
} else if (activeContainer.hasClass(FilterMenu.classNames.filterByCondition)) {
|
|
this.viewModel.set('active', 'custom');
|
|
}
|
|
}
|
|
});
|
|
kendo.spreadsheet.FilterMenu = FilterMenu;
|
|
$.extend(true, FilterMenu, {
|
|
classNames: classNames,
|
|
templates: templates
|
|
});
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/editor', ['kendo.core'], f);
|
|
}(function () {
|
|
(function (kendo) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var SheetEditor = kendo.Observable.extend({
|
|
init: function (view) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.view = view;
|
|
this.formulaBar = view.formulaBar;
|
|
this.barInput = view.formulaBar.formulaInput;
|
|
this.cellInput = view.formulaInput;
|
|
this.barInput.syncWith(this.cellInput);
|
|
this.cellInput.syncWith(this.barInput);
|
|
this.barInput.bind('keyup', this._triggerUpdate.bind(this));
|
|
this.cellInput.bind('keyup', this._triggerUpdate.bind(this));
|
|
this.barInput.bind('focus', this._focus.bind(this));
|
|
this.cellInput.bind('focus', this._focus.bind(this));
|
|
},
|
|
events: [
|
|
'activate',
|
|
'deactivate',
|
|
'change',
|
|
'update'
|
|
],
|
|
_focus: function (e) {
|
|
this.lastActive = e.sender == this.barInput ? 'bar' : 'cell';
|
|
},
|
|
_triggerUpdate: function () {
|
|
this.trigger('update', { value: this.value() });
|
|
},
|
|
activeEditor: function () {
|
|
var editor = null;
|
|
var activeElement = kendo._activeElement();
|
|
if (this.barElement()[0] === activeElement) {
|
|
editor = this.barInput;
|
|
} else if (this.cellElement()[0] === activeElement) {
|
|
editor = this.cellInput;
|
|
}
|
|
return editor;
|
|
},
|
|
activate: function (options) {
|
|
this._active = true;
|
|
this._rect = options.rect;
|
|
this.cellInput.position(options.rect);
|
|
this.cellInput.resize(options.rect);
|
|
this.cellInput.tooltip(options.tooltip);
|
|
this.cellInput.activeCell = this.barInput.activeCell = options.range.topLeft;
|
|
this.trigger('activate');
|
|
return this;
|
|
},
|
|
deactivate: function () {
|
|
var cellInput = this.cellInput;
|
|
if (!this._active) {
|
|
return;
|
|
}
|
|
if (cellInput.value() != this._value) {
|
|
if (this.trigger('change', { value: cellInput.value() })) {
|
|
return;
|
|
}
|
|
}
|
|
this._active = false;
|
|
this._rect = null;
|
|
cellInput.hide();
|
|
this.trigger('deactivate');
|
|
},
|
|
enable: function (enable) {
|
|
this.barInput.enable(enable);
|
|
},
|
|
barElement: function () {
|
|
return this.barInput.element;
|
|
},
|
|
cellElement: function () {
|
|
return this.cellInput.element;
|
|
},
|
|
focusLastActive: function () {
|
|
this.focus(this.lastActive);
|
|
},
|
|
focus: function (inputType) {
|
|
inputType = inputType || 'cell';
|
|
if (inputType === 'cell') {
|
|
this.cellInput.element.focus();
|
|
this.cellInput.end();
|
|
} else {
|
|
this.barInput.element.focus();
|
|
}
|
|
},
|
|
isActive: function () {
|
|
return this._active;
|
|
},
|
|
isFiltered: function () {
|
|
return this.barInput.popup.visible() || this.cellInput.popup.visible();
|
|
},
|
|
canInsertRef: function (isKeyboardAction) {
|
|
var editor = this.activeEditor();
|
|
return editor && editor.canInsertRef(isKeyboardAction);
|
|
},
|
|
highlightedRefs: function () {
|
|
var editor = this.activeEditor();
|
|
var refs = [];
|
|
if (editor) {
|
|
refs = editor.highlightedRefs();
|
|
}
|
|
return refs;
|
|
},
|
|
scale: function () {
|
|
this.cellInput.scale();
|
|
},
|
|
toggleTooltip: function (rect) {
|
|
this.cellInput.toggleTooltip(notEqual(this._rect, rect));
|
|
},
|
|
value: function (value) {
|
|
if (value === undefined) {
|
|
return this.barInput.value();
|
|
}
|
|
if (value === null) {
|
|
value = '';
|
|
}
|
|
this._value = value;
|
|
this.barInput.value(value);
|
|
this.cellInput.value(value);
|
|
}
|
|
});
|
|
function notEqual(oldRect, newRect) {
|
|
return oldRect && (oldRect.top !== newRect.top || oldRect.left !== newRect.left);
|
|
}
|
|
kendo.spreadsheet.SheetEditor = SheetEditor;
|
|
}(kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/autofill', [
|
|
'spreadsheet/runtime',
|
|
'spreadsheet/range'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var Range = spreadsheet.Range;
|
|
var runtime = spreadsheet.calc.runtime;
|
|
var Formula = runtime.Formula;
|
|
var MSG_INCOMPATIBLE = 'Incompatible ranges in fillFrom';
|
|
var MSG_NO_DIRECTION = 'Cannot determine fill direction';
|
|
Range.prototype._previewFillFrom = function (srcRange, direction) {
|
|
var destRange = this, sheet = destRange._sheet;
|
|
if (typeof srcRange == 'string') {
|
|
srcRange = sheet.range(srcRange);
|
|
}
|
|
var src = srcRange._ref.toRangeRef();
|
|
var dest = destRange._ref.toRangeRef();
|
|
if (src.intersects(dest)) {
|
|
if (src.eq(dest)) {
|
|
return null;
|
|
}
|
|
dest = dest.clone();
|
|
if (src.topLeft.eq(dest.topLeft)) {
|
|
if (src.width() == dest.width()) {
|
|
dest.topLeft.row += src.height();
|
|
direction = 0;
|
|
} else if (src.height() == dest.height()) {
|
|
dest.topLeft.col += src.width();
|
|
direction = 1;
|
|
} else {
|
|
throw new Error(MSG_INCOMPATIBLE);
|
|
}
|
|
} else if (src.bottomRight.eq(dest.bottomRight)) {
|
|
if (src.width() == dest.width()) {
|
|
dest.bottomRight.row -= src.height();
|
|
direction = 2;
|
|
} else if (src.height() == dest.height()) {
|
|
dest.bottomRight.col -= src.width();
|
|
direction = 3;
|
|
} else {
|
|
throw new Error(MSG_INCOMPATIBLE);
|
|
}
|
|
} else {
|
|
throw new Error(MSG_INCOMPATIBLE);
|
|
}
|
|
return sheet.range(dest)._previewFillFrom(srcRange, direction);
|
|
}
|
|
if (direction == null) {
|
|
if (src.topLeft.col == dest.topLeft.col) {
|
|
direction = src.topLeft.row < dest.topLeft.row ? 0 : 2;
|
|
} else if (src.topLeft.row == dest.topLeft.row) {
|
|
direction = src.topLeft.col < dest.topLeft.col ? 1 : 3;
|
|
} else {
|
|
throw new Error(MSG_NO_DIRECTION);
|
|
}
|
|
}
|
|
var horizontal = direction & 1;
|
|
var descending = direction & 2;
|
|
if (horizontal && src.height() != dest.height() || !horizontal && src.width() != dest.width()) {
|
|
throw new Error(MSG_INCOMPATIBLE);
|
|
}
|
|
var data = srcRange._properties(), n;
|
|
if (!horizontal) {
|
|
data = transpose(data);
|
|
n = dest.height();
|
|
} else {
|
|
n = dest.width();
|
|
}
|
|
var fill = new Array(data.length);
|
|
for (var i = 0; i < data.length; ++i) {
|
|
var s = data[i];
|
|
var f = findSeries(s);
|
|
var a = fill[i] = new Array(n);
|
|
for (var j = 0; j < n; ++j) {
|
|
var idx = descending ? -j - 1 : s.length + j;
|
|
var srcIdx = descending ? s.length - j % s.length - 1 : j % s.length;
|
|
a[descending ? n - j - 1 : j] = f(idx, srcIdx);
|
|
}
|
|
}
|
|
if (!horizontal) {
|
|
fill = transpose(fill);
|
|
}
|
|
return {
|
|
props: fill,
|
|
direction: direction,
|
|
dest: destRange
|
|
};
|
|
};
|
|
Range.prototype.fillFrom = function (srcRange, direction) {
|
|
var x = this._previewFillFrom(srcRange, direction);
|
|
x.dest._properties(x.props);
|
|
return x.dest;
|
|
};
|
|
function linearRegression(data) {
|
|
var N = data.length;
|
|
var mx = (N + 1) / 2, my = data.reduce(function (a, b) {
|
|
return a + b;
|
|
}, 0) / N;
|
|
var s1 = 0, s2 = 0;
|
|
for (var i = 0; i < N; i++) {
|
|
var t1 = i + 1 - mx, t2 = data[i] - my;
|
|
s1 += t1 * t2;
|
|
s2 += t1 * t1;
|
|
}
|
|
if (!s2) {
|
|
return function (N) {
|
|
return data[N % data.length];
|
|
};
|
|
}
|
|
var b = s1 / s2, a = my - b * mx;
|
|
return function (N) {
|
|
return a + b * (N + 1);
|
|
};
|
|
}
|
|
function findSeries(properties) {
|
|
function findStep(a) {
|
|
var diff = a[1] - a[0];
|
|
for (var i = 2; i < a.length; ++i) {
|
|
if (a[i] - a[i - 1] != diff) {
|
|
return null;
|
|
}
|
|
}
|
|
return diff;
|
|
}
|
|
function getData(a) {
|
|
return a.map(function (v) {
|
|
return v.number;
|
|
});
|
|
}
|
|
var series = [];
|
|
var data = properties.map(function (x) {
|
|
return x.formula || x.value;
|
|
});
|
|
forEachSeries(data, function (begin, end, type, a) {
|
|
var f, values;
|
|
if (type == 'number') {
|
|
values = getData(a);
|
|
if (values.length == 1 && (begin > 0 || end < data.length || formatType(values[0], properties[begin].format) == 'date')) {
|
|
values.push(values[0] + 1);
|
|
}
|
|
f = linearRegression(values);
|
|
} else if (type == 'string' || type == 'formula' || type == 'boolean') {
|
|
f = function (N, i) {
|
|
return data[i];
|
|
};
|
|
} else if (Array.isArray(type)) {
|
|
if (a.length == 1) {
|
|
f = function (N) {
|
|
return type[(a[0].number + N) % type.length];
|
|
};
|
|
} else {
|
|
var diff = findStep(getData(a));
|
|
if (diff == null) {
|
|
f = function (N) {
|
|
return a[N % a.length].value;
|
|
};
|
|
} else {
|
|
f = function (N) {
|
|
var idx = a[0].number + diff * N;
|
|
return type[idx % type.length];
|
|
};
|
|
}
|
|
}
|
|
} else if (type != 'null') {
|
|
values = getData(a);
|
|
if (values.length == 1) {
|
|
values.push(values[0] + 1);
|
|
}
|
|
values = linearRegression(values);
|
|
f = function (N, i) {
|
|
return data[i].replace(/^(.*\D)\d+/, '$1' + values(N, i));
|
|
};
|
|
} else {
|
|
f = function () {
|
|
return null;
|
|
};
|
|
}
|
|
var s = {
|
|
f: f,
|
|
begin: begin,
|
|
end: end,
|
|
len: end - begin
|
|
};
|
|
for (var i = begin; i < end; ++i) {
|
|
series[i] = s;
|
|
}
|
|
});
|
|
return function (N, i) {
|
|
var s = series[i];
|
|
var q = N / data.length | 0;
|
|
var r = N % data.length;
|
|
var n = q * s.len + r - s.begin;
|
|
var value = s.f(n, i);
|
|
var props = clone(properties[i]);
|
|
if (value instanceof Formula) {
|
|
props.formula = value;
|
|
} else {
|
|
props.value = value;
|
|
}
|
|
return props;
|
|
};
|
|
}
|
|
function formatType(value, format) {
|
|
if (format != null) {
|
|
return spreadsheet.formatting.type(value, format);
|
|
}
|
|
}
|
|
function clone(obj) {
|
|
var copy = {};
|
|
Object.keys(obj || {}).forEach(function (key) {
|
|
copy[key] = obj[key];
|
|
});
|
|
return copy;
|
|
}
|
|
function forEachSeries(data, f) {
|
|
var prev = null, start = 0, a = [], type;
|
|
for (var i = 0; i < data.length; ++i) {
|
|
type = getType(data[i]);
|
|
a.push(type);
|
|
if (prev != null && type.type !== prev.type) {
|
|
f(start, i, prev.type, a.slice(start, i));
|
|
start = i;
|
|
}
|
|
prev = type;
|
|
}
|
|
f(start, i, prev.type, a.slice(start, i));
|
|
}
|
|
function getType(el) {
|
|
if (typeof el == 'number') {
|
|
return {
|
|
type: 'number',
|
|
number: el
|
|
};
|
|
}
|
|
if (typeof el == 'string') {
|
|
var lst = findStringList(el);
|
|
if (lst) {
|
|
return lst;
|
|
}
|
|
var m = /^(.*\D)(\d+)/.exec(el);
|
|
if (m) {
|
|
el = el.replace(/^(.*\D)\d+/, '$1-######');
|
|
return {
|
|
type: el,
|
|
match: m,
|
|
number: parseFloat(m[2])
|
|
};
|
|
}
|
|
return { type: 'string' };
|
|
}
|
|
if (typeof el == 'boolean') {
|
|
return { type: 'boolean' };
|
|
}
|
|
if (el == null) {
|
|
return { type: 'null' };
|
|
}
|
|
if (el instanceof Formula) {
|
|
return { type: 'formula' };
|
|
}
|
|
window.console.error(el);
|
|
throw new Error('Cannot fill data');
|
|
}
|
|
function stringLists() {
|
|
var culture = kendo.culture();
|
|
return [
|
|
culture.calendars.standard.days.namesAbbr,
|
|
culture.calendars.standard.days.names,
|
|
culture.calendars.standard.months.namesAbbr,
|
|
culture.calendars.standard.months.names
|
|
];
|
|
}
|
|
function findStringList(str) {
|
|
var strl = str.toLowerCase();
|
|
var lists = stringLists();
|
|
for (var i = 0; i < lists.length; ++i) {
|
|
var a = lists[i];
|
|
for (var j = a.length; --j >= 0;) {
|
|
var el = a[j].toLowerCase();
|
|
if (el == strl) {
|
|
return {
|
|
type: a,
|
|
number: j,
|
|
value: str
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function transpose(a) {
|
|
var height = a.length, width = a[0].length;
|
|
var t = [];
|
|
for (var i = 0; i < width; ++i) {
|
|
t[i] = [];
|
|
for (var j = 0; j < height; ++j) {
|
|
t[i][j] = a[j][i];
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('spreadsheet/print', [
|
|
'kendo.pdf',
|
|
'spreadsheet/sheet',
|
|
'spreadsheet/range',
|
|
'spreadsheet/references',
|
|
'spreadsheet/numformat',
|
|
'util/text-metrics'
|
|
], f);
|
|
}(function () {
|
|
'use strict';
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var spreadsheet = kendo.spreadsheet;
|
|
var CellRef = spreadsheet.CellRef;
|
|
var drawing = kendo.drawing;
|
|
var formatting = spreadsheet.formatting;
|
|
var geo = kendo.geometry;
|
|
var GUIDELINE_WIDTH = 0.8;
|
|
function distributeCoords(heights, pageHeight) {
|
|
var curr = 0;
|
|
var out = [];
|
|
var threshold = 0.2 * pageHeight;
|
|
var bottom = pageHeight;
|
|
heights.forEach(function (h) {
|
|
if (pageHeight && curr + h > bottom) {
|
|
if (bottom - curr < threshold) {
|
|
curr = pageHeight * Math.ceil(curr / pageHeight);
|
|
}
|
|
bottom += pageHeight * Math.ceil(h / pageHeight);
|
|
}
|
|
out.push(curr);
|
|
curr += h;
|
|
});
|
|
out.push(curr);
|
|
return out;
|
|
}
|
|
function getMergedCells(sheet, range) {
|
|
var grid = sheet._grid;
|
|
var primary = {};
|
|
var secondary = {};
|
|
sheet.forEachMergedCell(range, function (ref) {
|
|
var topLeft = ref.topLeft;
|
|
grid.forEach(ref, function (cellRef) {
|
|
if (topLeft.eq(cellRef)) {
|
|
primary[cellRef.print()] = ref;
|
|
} else if (range.contains(cellRef)) {
|
|
secondary[cellRef.print()] = topLeft;
|
|
}
|
|
});
|
|
});
|
|
return {
|
|
primary: primary,
|
|
secondary: secondary
|
|
};
|
|
}
|
|
function doLayout(sheet, range, options) {
|
|
var cells = [];
|
|
var rowHeights = [];
|
|
var colWidths = [];
|
|
var mergedCells = getMergedCells(sheet, range);
|
|
var maxRow = -1, maxCol = -1;
|
|
sheet.forEach(range, function (row, col, cell) {
|
|
var relrow = row - range.topLeft.row;
|
|
var relcol = col - range.topLeft.col;
|
|
if (!relcol) {
|
|
rowHeights.push(sheet.rowHeight(row));
|
|
}
|
|
if (!relrow) {
|
|
colWidths.push(sheet.columnWidth(col));
|
|
}
|
|
if (sheet.isHiddenColumn(col) || sheet.isHiddenRow(row)) {
|
|
return;
|
|
}
|
|
var nonEmpty = options.forScreen || shouldDrawCell(cell);
|
|
if (!(options.emptyCells || nonEmpty)) {
|
|
return;
|
|
}
|
|
var id = new CellRef(row, col).print();
|
|
if (mergedCells.secondary[id]) {
|
|
return;
|
|
}
|
|
if (nonEmpty) {
|
|
maxRow = Math.max(maxRow, relrow);
|
|
maxCol = Math.max(maxCol, relcol);
|
|
} else {
|
|
cell.empty = true;
|
|
}
|
|
var m = mergedCells.primary[id];
|
|
if (m) {
|
|
delete mergedCells.primary[id];
|
|
cell.merged = true;
|
|
cell.rowspan = m.height();
|
|
cell.colspan = m.width();
|
|
if (options.forScreen) {
|
|
cell.width = sheet._columns.sum(m.topLeft.col, m.bottomRight.col);
|
|
cell.height = sheet._rows.sum(m.topLeft.row, m.bottomRight.row);
|
|
}
|
|
} else {
|
|
cell.rowspan = 1;
|
|
cell.colspan = 1;
|
|
}
|
|
cell.row = relrow;
|
|
cell.col = relcol;
|
|
cells.push(cell);
|
|
});
|
|
rowHeights = rowHeights.slice(0, maxRow + 1);
|
|
colWidths = colWidths.slice(0, maxCol + 1);
|
|
var pageWidth = options.pageWidth;
|
|
var pageHeight = options.pageHeight;
|
|
var scaleFactor = 1;
|
|
if (options.fitWidth) {
|
|
var width = colWidths.reduce(sum, 0);
|
|
if (width > pageWidth) {
|
|
scaleFactor = pageWidth / width;
|
|
pageWidth /= scaleFactor;
|
|
pageHeight /= scaleFactor;
|
|
}
|
|
}
|
|
var yCoords = distributeCoords(rowHeights, pageHeight || 0);
|
|
var xCoords = distributeCoords(colWidths, pageWidth || 0);
|
|
var boxWidth = 0;
|
|
var boxHeight = 0;
|
|
cells = cells.filter(function (cell) {
|
|
if (cell.empty && (cell.row > maxRow || cell.col > maxCol)) {
|
|
return false;
|
|
}
|
|
cell.left = xCoords[cell.col];
|
|
cell.top = yCoords[cell.row];
|
|
if (cell.merged) {
|
|
if (!options.forScreen) {
|
|
cell.right = orlast(xCoords, cell.col + cell.colspan);
|
|
cell.bottom = orlast(yCoords, cell.row + cell.rowspan);
|
|
cell.width = cell.right - cell.left;
|
|
cell.height = cell.bottom - cell.top;
|
|
} else {
|
|
cell.right = cell.left + cell.width;
|
|
cell.bottom = cell.top + cell.height;
|
|
}
|
|
} else {
|
|
cell.width = colWidths[cell.col];
|
|
cell.height = rowHeights[cell.row];
|
|
cell.bottom = cell.top + cell.height;
|
|
cell.right = cell.left + cell.width;
|
|
}
|
|
boxWidth = Math.max(boxWidth, cell.right);
|
|
boxHeight = Math.max(boxHeight, cell.bottom);
|
|
return true;
|
|
});
|
|
Object.keys(mergedCells.primary).forEach(function (id) {
|
|
var ref = mergedCells.primary[id];
|
|
sheet.forEach(ref.topLeft.toRangeRef(), function (row, col, cell) {
|
|
var relrow = row - range.topLeft.row;
|
|
var relcol = col - range.topLeft.col;
|
|
cell.merged = true;
|
|
cell.colspan = ref.height();
|
|
cell.rowspan = ref.width();
|
|
if (relrow < 0) {
|
|
cell.top = -sheet._rows.sum(row, row - relrow - 1);
|
|
} else {
|
|
cell.top = yCoords[relrow];
|
|
}
|
|
if (relcol < 0) {
|
|
cell.left = -sheet._columns.sum(col, col - relcol - 1);
|
|
} else {
|
|
cell.left = xCoords[relcol];
|
|
}
|
|
cell.height = sheet._rows.sum(ref.topLeft.row, ref.bottomRight.row);
|
|
cell.width = sheet._columns.sum(ref.topLeft.col, ref.bottomRight.col);
|
|
cell.right = cell.left + cell.width;
|
|
cell.bottom = cell.top + cell.height;
|
|
cells.push(cell);
|
|
});
|
|
});
|
|
return {
|
|
width: boxWidth,
|
|
height: boxHeight,
|
|
cells: cells.sort(normalOrder),
|
|
scale: scaleFactor,
|
|
xCoords: xCoords,
|
|
yCoords: yCoords
|
|
};
|
|
}
|
|
function sum(a, b) {
|
|
return a + b;
|
|
}
|
|
function orlast(a, i) {
|
|
return i < a.length ? a[i] : a[a.length - 1];
|
|
}
|
|
function shouldDrawCell(cell) {
|
|
return cell.value != null || cell.merged || cell.background != null || cell.borderTop != null || cell.borderRight != null || cell.borderBottom != null || cell.borderLeft != null;
|
|
}
|
|
function normalOrder(a, b) {
|
|
if (a.top < b.top) {
|
|
return -1;
|
|
} else if (a.top == b.top) {
|
|
if (a.left < b.left) {
|
|
return -1;
|
|
} else if (a.left == b.left) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
function drawLayout(layout, group, options) {
|
|
var ncols = Math.ceil(layout.width / options.pageWidth);
|
|
var nrows = Math.ceil(layout.height / options.pageHeight);
|
|
var pageWidth = options.pageWidth / layout.scale;
|
|
var pageHeight = options.pageHeight / layout.scale;
|
|
for (var i = 0; i < ncols; ++i) {
|
|
for (var j = 0; j < nrows; ++j) {
|
|
addPage(j, i);
|
|
}
|
|
}
|
|
function addPage(row, col) {
|
|
var left = col * pageWidth;
|
|
var right = left + pageWidth;
|
|
var top = row * pageHeight;
|
|
var bottom = top + pageHeight;
|
|
var endbottom = 0, endright = 0;
|
|
var cells = layout.cells.filter(function (cell) {
|
|
if (cell.right <= left || cell.left >= right || cell.bottom <= top || cell.top >= bottom) {
|
|
return false;
|
|
}
|
|
endbottom = Math.max(cell.bottom, endbottom);
|
|
endright = Math.max(cell.right, endright);
|
|
return true;
|
|
});
|
|
if (cells.length > 0) {
|
|
var page = new drawing.Group();
|
|
group.append(page);
|
|
page.clip(drawing.Path.fromRect(new geo.Rect([
|
|
0,
|
|
0
|
|
], [
|
|
options.pageWidth,
|
|
options.pageHeight
|
|
])));
|
|
var content = new drawing.Group();
|
|
page.append(content);
|
|
var matrix = geo.Matrix.scale(layout.scale, layout.scale).multiplyCopy(geo.Matrix.translate(-left, -top));
|
|
if (options.hCenter || options.vCenter) {
|
|
matrix = matrix.multiplyCopy(geo.Matrix.translate(options.hCenter ? (right - endright) / 2 : 0, options.vCenter ? (bottom - endbottom) / 2 : 0));
|
|
}
|
|
content.transform(matrix);
|
|
if (options.guidelines) {
|
|
var prev = null;
|
|
layout.xCoords.forEach(function (x) {
|
|
x = Math.min(x, endright);
|
|
if (x !== prev && x >= left && x <= right) {
|
|
prev = x;
|
|
content.append(new drawing.Path().moveTo(x, top).lineTo(x, endbottom).close().stroke('#999', GUIDELINE_WIDTH));
|
|
}
|
|
});
|
|
var prev = null;
|
|
layout.yCoords.forEach(function (y) {
|
|
y = Math.min(y, endbottom);
|
|
if (y !== prev && y >= top && y <= bottom) {
|
|
prev = y;
|
|
content.append(new drawing.Path().moveTo(left, y).lineTo(endright, y).close().stroke('#999', GUIDELINE_WIDTH));
|
|
}
|
|
});
|
|
}
|
|
var borders = new drawing.Group();
|
|
cells.forEach(function (cell) {
|
|
drawCell(cell, content, borders, options);
|
|
});
|
|
content.append(borders);
|
|
}
|
|
}
|
|
}
|
|
function drawCell(cell, content, borders, options) {
|
|
var g = new drawing.Group();
|
|
content.append(g);
|
|
var rect = new geo.Rect([
|
|
cell.left,
|
|
cell.top
|
|
], [
|
|
cell.width,
|
|
cell.height
|
|
]);
|
|
if (cell.background || cell.merged) {
|
|
var r2d2 = rect;
|
|
if (options.guidelines) {
|
|
r2d2 = rect.clone();
|
|
r2d2.origin.x += GUIDELINE_WIDTH / 2;
|
|
r2d2.origin.y += GUIDELINE_WIDTH / 2;
|
|
r2d2.size.width -= GUIDELINE_WIDTH;
|
|
r2d2.size.height -= GUIDELINE_WIDTH;
|
|
}
|
|
g.append(new drawing.Rect(r2d2).fill(cell.background || '#fff').stroke(null));
|
|
}
|
|
if (cell.borderLeft) {
|
|
borders.append(new drawing.Path().moveTo(cell.left, cell.top).lineTo(cell.left, cell.bottom).close().stroke(cell.borderLeft.color, cell.borderLeft.size));
|
|
}
|
|
if (cell.borderTop) {
|
|
borders.append(new drawing.Path().moveTo(cell.left, cell.top).lineTo(cell.right, cell.top).close().stroke(cell.borderTop.color, cell.borderTop.size));
|
|
}
|
|
if (cell.borderRight) {
|
|
borders.append(new drawing.Path().moveTo(cell.right, cell.top).lineTo(cell.right, cell.bottom).close().stroke(cell.borderRight.color, cell.borderRight.size));
|
|
}
|
|
if (cell.borderBottom) {
|
|
borders.append(new drawing.Path().moveTo(cell.left, cell.bottom).lineTo(cell.right, cell.bottom).close().stroke(cell.borderBottom.color, cell.borderBottom.size));
|
|
}
|
|
var val = cell.value;
|
|
if (val != null) {
|
|
var type = typeof val == 'number' ? 'number' : null;
|
|
var clip = new drawing.Group();
|
|
clip.clip(drawing.Path.fromRect(rect));
|
|
g.append(clip);
|
|
var f;
|
|
if (cell.format) {
|
|
f = formatting.textAndColor(val, cell.format);
|
|
val = f.text;
|
|
if (f.type) {
|
|
type = f.type;
|
|
}
|
|
} else {
|
|
val += '';
|
|
}
|
|
if (!cell.textAlign) {
|
|
switch (type) {
|
|
case 'number':
|
|
case 'date':
|
|
case 'percent':
|
|
cell.textAlign = 'right';
|
|
break;
|
|
case 'boolean':
|
|
cell.textAlign = 'center';
|
|
break;
|
|
}
|
|
}
|
|
drawText(val, f && f.color || cell.color || '#000', cell, clip);
|
|
}
|
|
}
|
|
function drawText(text, color, cell, group) {
|
|
var rect_left = cell.left + 2;
|
|
var rect_top = cell.top + 2;
|
|
var rect_width = cell.width - 4;
|
|
var rect_height = cell.height - 4;
|
|
var font = makeFontDef(cell);
|
|
var style = { font: font };
|
|
var props = {
|
|
font: font,
|
|
fill: { color: color }
|
|
};
|
|
var lines = [], text_height = 0, top = rect_top;
|
|
if (cell.wrap) {
|
|
lineBreak(text, style, rect_width).forEach(function (line) {
|
|
var tmp = new drawing.Text(line.text, [
|
|
rect_left,
|
|
top
|
|
], props);
|
|
top += line.box.height;
|
|
lines.push({
|
|
el: tmp,
|
|
box: line.box
|
|
});
|
|
});
|
|
text_height = top - rect_top;
|
|
} else {
|
|
var tmp = new drawing.Text(text, [
|
|
rect_left,
|
|
top
|
|
], props);
|
|
var box = kendo.util.measureText(text, style);
|
|
lines.push({
|
|
el: tmp,
|
|
box: box
|
|
});
|
|
text_height = box.height;
|
|
}
|
|
var cont = new drawing.Group();
|
|
group.append(cont);
|
|
var vtrans = 0;
|
|
switch (cell.verticalAlign) {
|
|
case 'center':
|
|
vtrans = rect_height - text_height >> 1;
|
|
break;
|
|
case undefined:
|
|
case null:
|
|
case 'bottom':
|
|
vtrans = rect_height - text_height;
|
|
break;
|
|
}
|
|
if (vtrans < 0) {
|
|
vtrans = 0;
|
|
}
|
|
lines.forEach(function (line) {
|
|
cont.append(line.el);
|
|
var htrans = 0;
|
|
switch (cell.textAlign) {
|
|
case 'center':
|
|
htrans = rect_width - line.box.width >> 1;
|
|
break;
|
|
case 'right':
|
|
htrans = rect_width - line.box.width;
|
|
break;
|
|
}
|
|
if (htrans < 0) {
|
|
htrans = 0;
|
|
}
|
|
if (htrans || vtrans) {
|
|
line.el.transform(geo.Matrix.translate(htrans, vtrans));
|
|
}
|
|
});
|
|
}
|
|
function lineBreak(text, style, width) {
|
|
var lines = [];
|
|
var len = text.length;
|
|
var start = 0;
|
|
while (start < len) {
|
|
split(start, len, len);
|
|
}
|
|
return lines;
|
|
function split(min, eol, max) {
|
|
var sub = text.substring(start, eol).trim();
|
|
var box = kendo.util.measureText(sub, style);
|
|
if (box.width <= width) {
|
|
if (eol < max - 1) {
|
|
split(eol, eol + max >> 1, max);
|
|
} else {
|
|
lines.push({
|
|
text: sub,
|
|
box: box
|
|
});
|
|
start = eol;
|
|
}
|
|
} else if (min < eol) {
|
|
split(min, min + eol >> 1, eol);
|
|
}
|
|
}
|
|
}
|
|
function makeFontDef(cell) {
|
|
var font = [];
|
|
if (cell.italic) {
|
|
font.push('italic');
|
|
}
|
|
if (cell.bold) {
|
|
font.push('bold');
|
|
}
|
|
font.push((cell.fontSize || 12) + 'px');
|
|
font.push(cell.fontFamily || 'Arial');
|
|
return font.join(' ');
|
|
}
|
|
function draw(sheet, range, options, callback) {
|
|
if (options == null && callback == null) {
|
|
callback = range;
|
|
options = {};
|
|
range = spreadsheet.SHEETREF;
|
|
}
|
|
if (callback == null) {
|
|
callback = options;
|
|
if (range instanceof spreadsheet.Range || range instanceof spreadsheet.Ref || typeof range == 'string') {
|
|
options = {};
|
|
} else {
|
|
options = range;
|
|
range = spreadsheet.SHEETREF;
|
|
}
|
|
}
|
|
options = kendo.jQuery.extend({
|
|
paperSize: 'A4',
|
|
landscape: true,
|
|
margin: '1cm',
|
|
guidelines: true,
|
|
emptyCells: true,
|
|
fitWidth: false,
|
|
center: false
|
|
}, options);
|
|
var group = new drawing.Group();
|
|
var paper = kendo.pdf.getPaperOptions(options);
|
|
group.options.set('pdf', {
|
|
author: options.author,
|
|
creator: options.creator,
|
|
date: options.date,
|
|
keywords: options.keywords,
|
|
margin: paper.margin,
|
|
multiPage: true,
|
|
paperSize: paper.paperSize,
|
|
subject: options.subject,
|
|
title: options.title
|
|
});
|
|
var pageWidth = paper.paperSize[0];
|
|
var pageHeight = paper.paperSize[1];
|
|
if (paper.margin) {
|
|
pageWidth -= paper.margin.left + paper.margin.right;
|
|
pageHeight -= paper.margin.top + paper.margin.bottom;
|
|
}
|
|
options.pageWidth = pageWidth;
|
|
options.pageHeight = pageHeight;
|
|
var layout = doLayout(sheet, sheet._ref(range), options);
|
|
drawLayout(layout, group, options);
|
|
callback(group);
|
|
}
|
|
spreadsheet.Sheet.prototype.draw = function (range, options, callback) {
|
|
var sheet = this;
|
|
if (sheet._workbook) {
|
|
sheet.recalc(sheet._workbook._context, function () {
|
|
draw(sheet, range, options, callback);
|
|
});
|
|
} else {
|
|
draw(sheet, range, options, callback);
|
|
}
|
|
};
|
|
spreadsheet.draw = {
|
|
doLayout: doLayout,
|
|
drawLayout: drawLayout,
|
|
shouldDrawCell: shouldDrawCell
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.spreadsheet', [
|
|
'util/undoredostack',
|
|
'util/text-metrics',
|
|
'util/parse-xml',
|
|
'kendo.excel',
|
|
'kendo.progressbar',
|
|
'kendo.pdf',
|
|
'spreadsheet/commands',
|
|
'spreadsheet/formulabar',
|
|
'spreadsheet/formulainput',
|
|
'spreadsheet/eventlistener',
|
|
'spreadsheet/rangelist',
|
|
'spreadsheet/propertybag',
|
|
'spreadsheet/references',
|
|
'spreadsheet/navigator',
|
|
'spreadsheet/axismanager',
|
|
'spreadsheet/clipboard',
|
|
'spreadsheet/range',
|
|
'spreadsheet/sheet',
|
|
'spreadsheet/sheetsbar',
|
|
'spreadsheet/excel-reader',
|
|
'spreadsheet/workbook',
|
|
'spreadsheet/formulacontext',
|
|
'spreadsheet/controller',
|
|
'spreadsheet/view',
|
|
'spreadsheet/grid',
|
|
'spreadsheet/axis',
|
|
'spreadsheet/filter',
|
|
'spreadsheet/sorter',
|
|
'spreadsheet/runtime',
|
|
'spreadsheet/calc',
|
|
'spreadsheet/numformat',
|
|
'spreadsheet/runtime.functions',
|
|
'spreadsheet/runtime.functions.2',
|
|
'spreadsheet/toolbar',
|
|
'spreadsheet/dialogs',
|
|
'spreadsheet/sheetbinder',
|
|
'spreadsheet/filtermenu',
|
|
'spreadsheet/editor',
|
|
'spreadsheet/autofill',
|
|
'spreadsheet/print'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'spreadsheet',
|
|
name: 'Spreadsheet',
|
|
category: 'web',
|
|
description: 'Spreadsheet component',
|
|
depends: [
|
|
'core',
|
|
'binder',
|
|
'colorpicker',
|
|
'combobox',
|
|
'data',
|
|
'dom',
|
|
'dropdownlist',
|
|
'menu',
|
|
'ooxml',
|
|
'popup',
|
|
'sortable',
|
|
'tabstrip',
|
|
'toolbar',
|
|
'treeview',
|
|
'window',
|
|
'validator',
|
|
'excel',
|
|
'pdf',
|
|
'drawing'
|
|
]
|
|
};
|
|
(function (kendo, undefined) {
|
|
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
|
|
return;
|
|
}
|
|
var $ = kendo.jQuery;
|
|
var Widget = kendo.ui.Widget;
|
|
var Workbook = kendo.spreadsheet.Workbook;
|
|
var Controller = kendo.spreadsheet.Controller;
|
|
var View = kendo.spreadsheet.View;
|
|
var NS = '.kendoSpreadsheet';
|
|
var ALL_REASONS = {
|
|
recalc: true,
|
|
selection: true,
|
|
activeCell: true,
|
|
layout: true,
|
|
sheetSelection: true,
|
|
resize: true,
|
|
editorChange: false,
|
|
editorClose: false
|
|
};
|
|
var classNames = { wrapper: 'k-widget k-spreadsheet' };
|
|
var Spreadsheet = kendo.ui.Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.addClass(Spreadsheet.classNames.wrapper);
|
|
this._view = new View(this.element, {
|
|
toolbar: this.options.toolbar,
|
|
sheetsbar: this.options.sheetsbar
|
|
});
|
|
this._workbook = new Workbook(this.options, this._view);
|
|
this._controller = new Controller(this._view, this._workbook);
|
|
this._autoRefresh = true;
|
|
this._bindWorkbookEvents();
|
|
this._view.workbook(this._workbook);
|
|
this.refresh();
|
|
this._resizeHandler = function () {
|
|
this.resize();
|
|
}.bind(this);
|
|
$(window).on('resize' + NS, this._resizeHandler);
|
|
},
|
|
_resize: function () {
|
|
this.refresh({ layout: true });
|
|
},
|
|
_workbookChange: function (e) {
|
|
if (this._autoRefresh) {
|
|
this.refresh(e);
|
|
}
|
|
if (e.recalc && e.ref) {
|
|
var range = new kendo.spreadsheet.Range(e.ref, this.activeSheet());
|
|
this.trigger('change', { range: range });
|
|
}
|
|
},
|
|
activeSheet: function (sheet) {
|
|
return this._workbook.activeSheet(sheet);
|
|
},
|
|
moveSheetToIndex: function (sheet, index) {
|
|
return this._workbook.moveSheetToIndex(sheet, index);
|
|
},
|
|
insertSheet: function (options) {
|
|
return this._workbook.insertSheet(options);
|
|
},
|
|
sheets: function () {
|
|
return this._workbook.sheets();
|
|
},
|
|
removeSheet: function (sheet) {
|
|
return this._workbook.removeSheet(sheet);
|
|
},
|
|
sheetByName: function (sheetName) {
|
|
return this._workbook.sheetByName(sheetName);
|
|
},
|
|
sheetIndex: function (sheet) {
|
|
return this._workbook.sheetIndex(sheet);
|
|
},
|
|
sheetByIndex: function (index) {
|
|
return this._workbook.sheetByIndex(index);
|
|
},
|
|
renameSheet: function (sheet, newSheetName) {
|
|
return this._workbook.renameSheet(sheet, newSheetName);
|
|
},
|
|
refresh: function (reason) {
|
|
if (!reason) {
|
|
reason = ALL_REASONS;
|
|
}
|
|
if (!reason.editorClose) {
|
|
this._view.sheet(this._workbook.activeSheet());
|
|
this._controller.sheet(this._workbook.activeSheet());
|
|
this._workbook.refresh(reason);
|
|
}
|
|
if (!reason.editorChange) {
|
|
this._view.refresh(reason);
|
|
this._controller.refresh();
|
|
this._view.render();
|
|
this.trigger('render');
|
|
}
|
|
return this;
|
|
},
|
|
openDialog: function (name, options) {
|
|
return this._view.openDialog(name, options);
|
|
},
|
|
autoRefresh: function (value) {
|
|
if (value !== undefined) {
|
|
this._autoRefresh = value;
|
|
if (value === true) {
|
|
this.refresh();
|
|
}
|
|
return this;
|
|
}
|
|
return this._autoRefresh;
|
|
},
|
|
toJSON: function () {
|
|
return this._workbook.toJSON();
|
|
},
|
|
fromJSON: function (json) {
|
|
if (json.sheets) {
|
|
this._workbook.destroy();
|
|
this._workbook = new Workbook($.extend({}, this.options, json));
|
|
this._bindWorkbookEvents();
|
|
this._view.workbook(this._workbook);
|
|
this._controller.workbook(this._workbook);
|
|
this.activeSheet(this.activeSheet());
|
|
} else {
|
|
this.refresh();
|
|
}
|
|
},
|
|
fromFile: function (blob, name) {
|
|
return this._workbook.fromFile(blob, name);
|
|
},
|
|
saveAsPDF: function (options) {
|
|
this._workbook.saveAsPDF($.extend({}, this.options.pdf, options, { workbook: this._workbook }));
|
|
},
|
|
saveAsExcel: function (options) {
|
|
this._workbook.saveAsExcel(options);
|
|
},
|
|
draw: function (options, callback) {
|
|
this._workbook.draw(options, callback);
|
|
},
|
|
_workbookExcelExport: function (e) {
|
|
if (this.trigger('excelExport', e)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_workbookExcelImport: function (e) {
|
|
if (this.trigger('excelImport', e)) {
|
|
e.preventDefault();
|
|
} else {
|
|
this._initProgress(e.promise);
|
|
}
|
|
},
|
|
_initProgress: function (deferred) {
|
|
var loading = $('<div class=\'k-loading-mask\' ' + 'style=\'width: 100%; height: 100%; top: 0;\'>' + '<div class=\'k-loading-color\'/>' + '</div>').appendTo(this.element);
|
|
var pb = $('<div class=\'k-loading-progress\'>').appendTo(loading).kendoProgressBar({
|
|
type: 'chunk',
|
|
chunkCount: 10,
|
|
min: 0,
|
|
max: 1,
|
|
value: 0
|
|
}).data('kendoProgressBar');
|
|
deferred.progress(function (e) {
|
|
pb.value(e.progress);
|
|
}).always(function () {
|
|
kendo.destroy(loading);
|
|
loading.remove();
|
|
});
|
|
},
|
|
_workbookPdfExport: function (e) {
|
|
if (this.trigger('pdfExport', e)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_bindWorkbookEvents: function () {
|
|
this._workbook.bind('change', this._workbookChange.bind(this));
|
|
this._workbook.bind('excelExport', this._workbookExcelExport.bind(this));
|
|
this._workbook.bind('excelImport', this._workbookExcelImport.bind(this));
|
|
this._workbook.bind('pdfExport', this._workbookPdfExport.bind(this));
|
|
},
|
|
destroy: function () {
|
|
kendo.ui.Widget.fn.destroy.call(this);
|
|
this._workbook.destroy();
|
|
this._controller.destroy();
|
|
this._view.destroy();
|
|
if (this._resizeHandler) {
|
|
$(window).off('resize' + NS, this._resizeHandler);
|
|
}
|
|
},
|
|
options: {
|
|
name: 'Spreadsheet',
|
|
toolbar: true,
|
|
sheetsbar: true,
|
|
rows: 200,
|
|
columns: 50,
|
|
rowHeight: 20,
|
|
columnWidth: 64,
|
|
headerHeight: 20,
|
|
headerWidth: 32,
|
|
excel: {
|
|
proxyURL: '',
|
|
fileName: 'Workbook.xlsx'
|
|
},
|
|
pdf: {
|
|
area: 'workbook',
|
|
fileName: 'Workbook.pdf',
|
|
proxyURL: '',
|
|
paperSize: 'a4',
|
|
landscape: true,
|
|
margin: null,
|
|
title: null,
|
|
author: null,
|
|
subject: null,
|
|
keywords: null,
|
|
creator: 'Kendo UI PDF Generator v.' + kendo.version,
|
|
date: null
|
|
}
|
|
},
|
|
events: [
|
|
'pdfExport',
|
|
'excelExport',
|
|
'excelImport',
|
|
'change',
|
|
'render'
|
|
]
|
|
});
|
|
kendo.spreadsheet.ALL_REASONS = ALL_REASONS;
|
|
kendo.ui.plugin(Spreadsheet);
|
|
$.extend(true, Spreadsheet, { classNames: classNames });
|
|
}(window.kendo));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.pivot.configurator', ['kendo.dom'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'pivot.configurator',
|
|
name: 'PivotConfigurator',
|
|
category: 'web',
|
|
depends: [
|
|
'dropdownlist',
|
|
'treeview',
|
|
'pivot.fieldmenu'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, ns = '.kendoPivotConfigurator', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, SETTING_CONTAINER_TEMPLATE = kendo.template('<p class="k-reset"><span class="k-icon #=icon#"></span>${name}</p>' + '<div class="k-list-container k-reset"/>');
|
|
function addKPI(data) {
|
|
var found;
|
|
var idx = 0;
|
|
var length = data.length;
|
|
for (; idx < length; idx++) {
|
|
if (data[idx].type == 2) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
data.splice(idx + 1, 0, {
|
|
caption: 'KPIs',
|
|
defaultHierarchy: '[KPIs]',
|
|
name: 'KPIs',
|
|
uniqueName: '[KPIs]'
|
|
});
|
|
}
|
|
}
|
|
function kpiNode(node) {
|
|
return {
|
|
name: node.uniqueName,
|
|
type: node.type
|
|
};
|
|
}
|
|
function normalizeKPIs(data) {
|
|
for (var idx = 0, length = data.length; idx < length; idx++) {
|
|
data[idx].uniqueName = data[idx].name;
|
|
data[idx].type = 'kpi';
|
|
}
|
|
return data;
|
|
}
|
|
function settingTargetFromNode(node) {
|
|
var target = $(node).closest('.k-pivot-setting');
|
|
if (target.length) {
|
|
return target.data('kendoPivotSettingTarget');
|
|
}
|
|
return null;
|
|
}
|
|
var PivotConfigurator = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.addClass('k-widget k-fieldselector k-alt k-edit-form-container');
|
|
this._dataSource();
|
|
this._layout();
|
|
this.refresh();
|
|
kendo.notify(this);
|
|
},
|
|
events: [],
|
|
options: {
|
|
name: 'PivotConfigurator',
|
|
filterable: false,
|
|
sortable: false,
|
|
messages: {
|
|
measures: 'Drop Data Fields Here',
|
|
columns: 'Drop Column Fields Here',
|
|
rows: 'Drop Rows Fields Here',
|
|
measuresLabel: 'Measures',
|
|
columnsLabel: 'Columns',
|
|
rowsLabel: 'Rows',
|
|
fieldsLabel: 'Fields'
|
|
}
|
|
},
|
|
_dataSource: function () {
|
|
var that = this;
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that.dataSource.unbind('change', that._refreshHandler).unbind('error', that._errorHandler).unbind('progress', that._progressHandler);
|
|
} else {
|
|
that._errorHandler = $.proxy(that._error, that);
|
|
that._refreshHandler = $.proxy(that.refresh, that);
|
|
that._progressHandler = $.proxy(that._requestStart, that);
|
|
}
|
|
that.dataSource = kendo.data.PivotDataSource.create(that.options.dataSource);
|
|
that.dataSource.bind('change', that._refreshHandler).bind('error', that._errorHandler).bind('progress', that._progressHandler);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (this.measures) {
|
|
this.measures.setDataSource(dataSource);
|
|
}
|
|
if (this.rows) {
|
|
this.rows.setDataSource(dataSource);
|
|
}
|
|
if (this.columns) {
|
|
this.columns.setDataSource(dataSource);
|
|
}
|
|
this.refresh();
|
|
},
|
|
_treeViewDataSource: function () {
|
|
var that = this;
|
|
return kendo.data.HierarchicalDataSource.create({
|
|
schema: {
|
|
model: {
|
|
id: 'uniqueName',
|
|
hasChildren: function (item) {
|
|
return !('hierarchyUniqueName' in item) && !('aggregator' in item);
|
|
}
|
|
}
|
|
},
|
|
transport: {
|
|
read: function (options) {
|
|
var promise;
|
|
var node;
|
|
var kpi;
|
|
if ($.isEmptyObject(options.data)) {
|
|
promise = that.dataSource.schemaDimensions();
|
|
promise.done(function (data) {
|
|
if (!that.dataSource.cubeBuilder) {
|
|
addKPI(data);
|
|
}
|
|
options.success(data);
|
|
}).fail(options.error);
|
|
} else {
|
|
node = that.treeView.dataSource.get(options.data.uniqueName);
|
|
if (node.uniqueName === '[KPIs]') {
|
|
kpi = true;
|
|
promise = that.dataSource.schemaKPIs();
|
|
promise.done(function (data) {
|
|
options.success(normalizeKPIs(data));
|
|
}).fail(options.error);
|
|
} else if (node.type == 'kpi') {
|
|
kpi = true;
|
|
options.success(buildKPImeasures(node));
|
|
}
|
|
if (!kpi) {
|
|
if (node.type == 2) {
|
|
promise = that.dataSource.schemaMeasures();
|
|
} else if (node.dimensionUniqueName) {
|
|
promise = that.dataSource.schemaLevels(options.data.uniqueName);
|
|
} else {
|
|
promise = that.dataSource.schemaHierarchies(options.data.uniqueName);
|
|
}
|
|
promise.done(options.success).fail(options.error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_progress: function (toggle) {
|
|
kendo.ui.progress(this.element, toggle);
|
|
},
|
|
_error: function () {
|
|
this._progress(false);
|
|
},
|
|
_requestStart: function () {
|
|
this._progress(true);
|
|
},
|
|
_layout: function () {
|
|
this.form = $('<div class="k-columns k-state-default k-floatwrap"/>').appendTo(this.element);
|
|
this._fields();
|
|
this._targets();
|
|
},
|
|
_fields: function () {
|
|
var container = $('<div class="k-state-default"><p class="k-reset"><span class="k-icon k-i-group"></span>' + this.options.messages.fieldsLabel + '</p></div>').appendTo(this.form);
|
|
var template = '# if (item.type == 2 || item.uniqueName == "[KPIs]") { #' + '<span class="k-icon k-i-#= (item.type == 2 ? "sum" : "kpi") #"></span>' + '# } else if (item.type && item.type !== "kpi") { #' + '<span class="k-icon k-i-dimension"></span>' + '# } #' + '#: item.caption || item.name #';
|
|
this.treeView = $('<div/>').appendTo(container).kendoTreeView({
|
|
template: template,
|
|
dataTextField: 'caption',
|
|
dragAndDrop: true,
|
|
autoBind: false,
|
|
dataSource: this._treeViewDataSource(),
|
|
dragstart: function (e) {
|
|
var dataItem = this.dataItem(e.sourceNode);
|
|
if (!dataItem.hasChildren && !dataItem.aggregator && !dataItem.measure || dataItem.type == 2 || dataItem.uniqueName === '[KPIs]') {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
drag: function (e) {
|
|
var status = 'k-denied';
|
|
var setting = settingTargetFromNode(e.dropTarget);
|
|
if (setting && setting.validate(this.dataItem(e.sourceNode))) {
|
|
status = 'k-add';
|
|
}
|
|
e.setStatusClass(status);
|
|
},
|
|
drop: function (e) {
|
|
e.preventDefault();
|
|
var setting = settingTargetFromNode(e.dropTarget);
|
|
var node = this.dataItem(e.sourceNode);
|
|
var idx, length, measures;
|
|
var name;
|
|
if (setting && setting.validate(node)) {
|
|
name = node.defaultHierarchy || node.uniqueName;
|
|
if (node.type === 'kpi') {
|
|
measures = buildKPImeasures(node);
|
|
length = measures.length;
|
|
name = [];
|
|
for (idx = 0; idx < length; idx++) {
|
|
name.push(kpiNode(measures[idx]));
|
|
}
|
|
} else if (node.kpi) {
|
|
name = [kpiNode(node)];
|
|
}
|
|
setting.add(name);
|
|
}
|
|
}
|
|
}).data('kendoTreeView');
|
|
},
|
|
_createTarget: function (element, options) {
|
|
var template = '<li class="k-item k-header" data-' + kendo.ns + 'name="${data.name}">${data.name}';
|
|
var sortable = options.sortable;
|
|
var icons = '';
|
|
if (sortable) {
|
|
icons += '#if (data.sortIcon) {#';
|
|
icons += '<span class="k-icon ${data.sortIcon} k-setting-sort"></span>';
|
|
icons += '#}#';
|
|
}
|
|
if (options.filterable || sortable) {
|
|
icons += '<span class="k-icon k-i-arrowhead-s k-setting-fieldmenu"></span>';
|
|
}
|
|
icons += '<span class="k-icon k-si-close k-setting-delete"></span>';
|
|
template += '<span class="k-field-actions">' + icons + '</span></li>';
|
|
return new kendo.ui.PivotSettingTarget(element, $.extend({
|
|
dataSource: this.dataSource,
|
|
hint: function (element) {
|
|
var wrapper = $('<div class="k-fieldselector"><ul class="k-list k-reset"></ul></div>');
|
|
wrapper.find('.k-list').append(element.clone());
|
|
return wrapper;
|
|
},
|
|
template: template,
|
|
emptyTemplate: '<li class="k-item k-empty">${data}</li>'
|
|
}, options));
|
|
},
|
|
_targets: function () {
|
|
var container = $('<div class="k-state-default"/>').appendTo(this.form);
|
|
var columnsContainer = $(SETTING_CONTAINER_TEMPLATE({
|
|
name: this.options.messages.columnsLabel,
|
|
icon: 'k-i-vbars'
|
|
})).appendTo(container);
|
|
var columns = $('<ul class="k-pivot-configurator-settings k-list k-reset" />').appendTo(columnsContainer.last());
|
|
var rowsContainer = $(SETTING_CONTAINER_TEMPLATE({
|
|
name: this.options.messages.rowsLabel,
|
|
icon: 'k-i-hbars'
|
|
})).appendTo(container);
|
|
var rows = $('<ul class="k-pivot-configurator-settings k-list k-reset" />').appendTo(rowsContainer.last());
|
|
var measuresContainer = $(SETTING_CONTAINER_TEMPLATE({
|
|
name: this.options.messages.measuresLabel,
|
|
icon: 'k-i-sum'
|
|
})).appendTo(container);
|
|
var measures = $('<ul class="k-pivot-configurator-settings k-list k-reset" />').appendTo(measuresContainer.last());
|
|
var options = this.options;
|
|
this.columns = this._createTarget(columns, {
|
|
filterable: options.filterable,
|
|
sortable: options.sortable,
|
|
connectWith: rows,
|
|
messages: {
|
|
empty: options.messages.columns,
|
|
fieldMenu: options.messages.fieldMenu
|
|
}
|
|
});
|
|
this.rows = this._createTarget(rows, {
|
|
filterable: options.filterable,
|
|
setting: 'rows',
|
|
connectWith: columns,
|
|
messages: {
|
|
empty: this.options.messages.rows,
|
|
fieldMenu: this.options.messages.fieldMenu
|
|
}
|
|
});
|
|
this.measures = this._createTarget(measures, {
|
|
setting: 'measures',
|
|
messages: { empty: options.messages.measures }
|
|
});
|
|
columns.add(rows).add(measures).on(HOVEREVENTS, '.k-item:not(.k-empty)', this._toggleHover);
|
|
},
|
|
_toggleHover: function (e) {
|
|
$(e.currentTarget).toggleClass('k-state-hover', e.type === 'mouseenter');
|
|
},
|
|
_resize: function () {
|
|
var element = this.element;
|
|
var height = this.options.height;
|
|
var border, fields;
|
|
if (!height) {
|
|
return;
|
|
}
|
|
element.height(height);
|
|
if (element.is(':visible')) {
|
|
fields = element.children('.k-columns').children('div.k-state-default');
|
|
height = element.innerHeight();
|
|
border = (element.outerHeight() - height) / 2;
|
|
height = height - (fields.outerHeight(true) - fields.height()) - border;
|
|
fields.height(height);
|
|
}
|
|
},
|
|
refresh: function () {
|
|
var dataSource = this.dataSource;
|
|
if (dataSource.cubeBuilder || this._cube !== dataSource.cube() || this._catalog !== dataSource.catalog()) {
|
|
this.treeView.dataSource.fetch();
|
|
}
|
|
this._catalog = this.dataSource.catalog();
|
|
this._cube = this.dataSource.cube();
|
|
this._resize();
|
|
this._progress(false);
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.dataSource.unbind('change', this._refreshHandler);
|
|
this.form.find('.k-list').off(ns);
|
|
this.rows.destroy();
|
|
this.columns.destroy();
|
|
this.measures.destroy();
|
|
this.treeView.destroy();
|
|
this.element = null;
|
|
this._refreshHandler = null;
|
|
}
|
|
});
|
|
function kpiMeasure(name, measure, type) {
|
|
return {
|
|
hierarchyUniqueName: name,
|
|
uniqueName: measure,
|
|
caption: measure,
|
|
measure: measure,
|
|
name: measure,
|
|
type: type,
|
|
kpi: true
|
|
};
|
|
}
|
|
function buildKPImeasures(node) {
|
|
var name = node.name;
|
|
return [
|
|
kpiMeasure(name, node.value, 'value'),
|
|
kpiMeasure(name, node.goal, 'goal'),
|
|
kpiMeasure(name, node.status, 'status'),
|
|
kpiMeasure(name, node.trend, 'trend')
|
|
];
|
|
}
|
|
ui.plugin(PivotConfigurator);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.angular', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'angular',
|
|
name: 'AngularJS Directives',
|
|
category: 'framework',
|
|
description: 'Adds Kendo UI for AngularJS directives',
|
|
depends: ['core'],
|
|
defer: true
|
|
};
|
|
(function ($, angular, undefined) {
|
|
'use strict';
|
|
if (!angular || !angular.injector) {
|
|
return;
|
|
}
|
|
var module = angular.module('kendo.directives', []), $injector = angular.injector(['ng']), $parse = $injector.get('$parse'), $timeout = $injector.get('$timeout'), $defaultCompile, $log = $injector.get('$log');
|
|
function withoutTimeout(f) {
|
|
var save = $timeout;
|
|
try {
|
|
$timeout = function (f) {
|
|
return f();
|
|
};
|
|
return f();
|
|
} finally {
|
|
$timeout = save;
|
|
}
|
|
}
|
|
var OPTIONS_NOW;
|
|
var createDataSource = function () {
|
|
var types = {
|
|
TreeList: 'TreeListDataSource',
|
|
TreeView: 'HierarchicalDataSource',
|
|
Scheduler: 'SchedulerDataSource',
|
|
PanelBar: '$PLAIN',
|
|
Menu: '$PLAIN',
|
|
ContextMenu: '$PLAIN'
|
|
};
|
|
var toDataSource = function (dataSource, type) {
|
|
if (type == '$PLAIN') {
|
|
return dataSource;
|
|
}
|
|
return kendo.data[type].create(dataSource);
|
|
};
|
|
return function (scope, element, role, source) {
|
|
var type = types[role] || 'DataSource';
|
|
var current = scope.$eval(source);
|
|
var ds = toDataSource(current, type);
|
|
scope.$watch(source, function (mew) {
|
|
var widget = kendoWidgetInstance(element);
|
|
if (widget && typeof widget.setDataSource == 'function') {
|
|
if (mew !== current) {
|
|
var ds = toDataSource(mew, type);
|
|
widget.setDataSource(ds);
|
|
current = mew;
|
|
}
|
|
}
|
|
});
|
|
return ds;
|
|
};
|
|
}();
|
|
var ignoredAttributes = {
|
|
kDataSource: true,
|
|
kOptions: true,
|
|
kRebind: true,
|
|
kNgModel: true,
|
|
kNgDelay: true
|
|
};
|
|
var ignoredOwnProperties = {
|
|
name: true,
|
|
title: true,
|
|
style: true
|
|
};
|
|
function createWidget(scope, element, attrs, widget, origAttr, controllers) {
|
|
if (!(element instanceof jQuery)) {
|
|
throw new Error('The Kendo UI directives require jQuery to be available before AngularJS. Please include jquery before angular in the document.');
|
|
}
|
|
var kNgDelay = attrs.kNgDelay, delayValue = scope.$eval(kNgDelay);
|
|
controllers = controllers || [];
|
|
var ngModel = controllers[0], ngForm = controllers[1];
|
|
var ctor = $(element)[widget];
|
|
if (!ctor) {
|
|
window.console.error('Could not find: ' + widget);
|
|
return null;
|
|
}
|
|
var parsed = parseOptions(scope, element, attrs, widget, ctor);
|
|
var options = parsed.options;
|
|
if (parsed.unresolved.length) {
|
|
var promises = [];
|
|
for (var i = 0, len = parsed.unresolved.length; i < len; i++) {
|
|
var unresolved = parsed.unresolved[i];
|
|
var promise = $.Deferred(function (d) {
|
|
var unwatch = scope.$watch(unresolved.path, function (newValue) {
|
|
if (newValue !== undefined) {
|
|
unwatch();
|
|
d.resolve();
|
|
}
|
|
});
|
|
}).promise();
|
|
promises.push(promise);
|
|
}
|
|
$.when.apply(null, promises).then(createIt);
|
|
return;
|
|
}
|
|
if (kNgDelay && !delayValue) {
|
|
var root = scope.$root || scope;
|
|
var register = function () {
|
|
var unregister = scope.$watch(kNgDelay, function (newValue) {
|
|
if (newValue !== undefined) {
|
|
unregister();
|
|
element.removeAttr(attrs.$attr.kNgDelay);
|
|
kNgDelay = null;
|
|
$timeout(createIt);
|
|
}
|
|
});
|
|
};
|
|
if (/^\$(digest|apply)$/.test(root.$$phase)) {
|
|
register();
|
|
} else {
|
|
scope.$apply(register);
|
|
}
|
|
return;
|
|
} else {
|
|
return createIt();
|
|
}
|
|
function createIt() {
|
|
var originalElement;
|
|
if (attrs.kRebind) {
|
|
originalElement = $($(element)[0].cloneNode(true));
|
|
}
|
|
options = parseOptions(scope, element, attrs, widget, ctor).options;
|
|
if (element.is('select')) {
|
|
(function (options) {
|
|
if (options.length > 0) {
|
|
var first = $(options[0]);
|
|
if (!/\S/.test(first.text()) && /^\?/.test(first.val())) {
|
|
first.remove();
|
|
}
|
|
}
|
|
}(element[0].options));
|
|
}
|
|
var object = ctor.call(element, OPTIONS_NOW = options).data(widget);
|
|
exposeWidget(object, scope, attrs, widget, origAttr);
|
|
scope.$emit('kendoWidgetCreated', object);
|
|
var destroyRegister = destroyWidgetOnScopeDestroy(scope, object);
|
|
if (attrs.kRebind) {
|
|
setupRebind(object, scope, element, originalElement, attrs.kRebind, destroyRegister, attrs);
|
|
}
|
|
if (attrs.kNgDisabled) {
|
|
var kNgDisabled = attrs.kNgDisabled;
|
|
var isDisabled = scope.$eval(kNgDisabled);
|
|
if (isDisabled) {
|
|
object.enable(!isDisabled);
|
|
}
|
|
bindToKNgDisabled(object, scope, element, kNgDisabled);
|
|
}
|
|
if (attrs.kNgReadonly) {
|
|
var kNgReadonly = attrs.kNgReadonly;
|
|
var isReadonly = scope.$eval(kNgReadonly);
|
|
if (isReadonly) {
|
|
object.readonly(isReadonly);
|
|
}
|
|
bindToKNgReadonly(object, scope, element, kNgReadonly);
|
|
}
|
|
if (attrs.kNgModel) {
|
|
bindToKNgModel(object, scope, attrs.kNgModel);
|
|
}
|
|
if (ngModel) {
|
|
bindToNgModel(object, scope, element, ngModel, ngForm);
|
|
}
|
|
if (object) {
|
|
propagateClassToWidgetWrapper(object, element);
|
|
}
|
|
return object;
|
|
}
|
|
}
|
|
function parseOptions(scope, element, attrs, widget, ctor) {
|
|
var role = widget.replace(/^kendo/, '');
|
|
var unresolved = [];
|
|
var optionsPath = attrs.kOptions || attrs.options;
|
|
var optionsValue = scope.$eval(optionsPath);
|
|
if (optionsPath && optionsValue === undefined) {
|
|
unresolved.push({
|
|
option: 'options',
|
|
path: optionsPath
|
|
});
|
|
}
|
|
var options = angular.extend({}, attrs.defaultOptions, optionsValue);
|
|
function addOption(name, value) {
|
|
var scopeValue = angular.copy(scope.$eval(value));
|
|
if (scopeValue === undefined) {
|
|
unresolved.push({
|
|
option: name,
|
|
path: value
|
|
});
|
|
} else {
|
|
options[name] = scopeValue;
|
|
}
|
|
}
|
|
var widgetOptions = ctor.widget.prototype.options;
|
|
var widgetEvents = ctor.widget.prototype.events;
|
|
$.each(attrs, function (name, value) {
|
|
if (name === 'source' || name === 'kDataSource' || name === 'kScopeField' || name === 'scopeField') {
|
|
return;
|
|
}
|
|
var dataName = 'data' + name.charAt(0).toUpperCase() + name.slice(1);
|
|
if (name.indexOf('on') === 0) {
|
|
var eventKey = name.replace(/^on./, function (prefix) {
|
|
return prefix.charAt(2).toLowerCase();
|
|
});
|
|
if (widgetEvents.indexOf(eventKey) > -1) {
|
|
options[eventKey] = value;
|
|
}
|
|
}
|
|
if (widgetOptions.hasOwnProperty(dataName)) {
|
|
addOption(dataName, value);
|
|
} else if (widgetOptions.hasOwnProperty(name) && !ignoredOwnProperties[name]) {
|
|
addOption(name, value);
|
|
} else if (!ignoredAttributes[name]) {
|
|
var match = name.match(/^k(On)?([A-Z].*)/);
|
|
if (match) {
|
|
var optionName = match[2].charAt(0).toLowerCase() + match[2].slice(1);
|
|
if (match[1] && name != 'kOnLabel') {
|
|
options[optionName] = value;
|
|
} else {
|
|
if (name == 'kOnLabel') {
|
|
optionName = 'onLabel';
|
|
}
|
|
addOption(optionName, value);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var dataSource = attrs.kDataSource || attrs.source;
|
|
if (dataSource) {
|
|
options.dataSource = createDataSource(scope, element, role, dataSource);
|
|
}
|
|
options.$angular = [scope];
|
|
return {
|
|
options: options,
|
|
unresolved: unresolved
|
|
};
|
|
}
|
|
function bindToKNgDisabled(widget, scope, element, kNgDisabled) {
|
|
if (kendo.ui.PanelBar && widget instanceof kendo.ui.PanelBar || kendo.ui.Menu && widget instanceof kendo.ui.Menu) {
|
|
$log.warn('k-ng-disabled specified on a widget that does not have the enable() method: ' + widget.options.name);
|
|
return;
|
|
}
|
|
scope.$watch(kNgDisabled, function (newValue, oldValue) {
|
|
if (newValue != oldValue) {
|
|
widget.enable(!newValue);
|
|
}
|
|
});
|
|
}
|
|
function bindToKNgReadonly(widget, scope, element, kNgReadonly) {
|
|
if (typeof widget.readonly != 'function') {
|
|
$log.warn('k-ng-readonly specified on a widget that does not have the readonly() method: ' + widget.options.name);
|
|
return;
|
|
}
|
|
scope.$watch(kNgReadonly, function (newValue, oldValue) {
|
|
if (newValue != oldValue) {
|
|
widget.readonly(newValue);
|
|
}
|
|
});
|
|
}
|
|
function exposeWidget(widget, scope, attrs, kendoWidget, origAttr) {
|
|
if (attrs[origAttr]) {
|
|
var set = $parse(attrs[origAttr]).assign;
|
|
if (set) {
|
|
set(scope, widget);
|
|
} else {
|
|
throw new Error(origAttr + ' attribute used but expression in it is not assignable: ' + attrs[kendoWidget]);
|
|
}
|
|
}
|
|
}
|
|
function formValue(element) {
|
|
if (/checkbox|radio/i.test(element.attr('type'))) {
|
|
return element.prop('checked');
|
|
}
|
|
return element.val();
|
|
}
|
|
var formRegExp = /^(input|select|textarea)$/i;
|
|
function isForm(element) {
|
|
return formRegExp.test(element[0].tagName);
|
|
}
|
|
function bindToNgModel(widget, scope, element, ngModel, ngForm) {
|
|
if (!widget.value) {
|
|
return;
|
|
}
|
|
var value;
|
|
var haveChangeOnElement = false;
|
|
if (isForm(element)) {
|
|
value = function () {
|
|
return formValue(element);
|
|
};
|
|
} else {
|
|
value = function () {
|
|
return widget.value();
|
|
};
|
|
}
|
|
ngModel.$render = function () {
|
|
var val = ngModel.$viewValue;
|
|
if (val === undefined) {
|
|
val = ngModel.$modelValue;
|
|
}
|
|
if (val === undefined) {
|
|
val = null;
|
|
}
|
|
haveChangeOnElement = true;
|
|
setTimeout(function () {
|
|
haveChangeOnElement = false;
|
|
if (widget) {
|
|
var kNgModel = scope[widget.element.attr('k-ng-model')];
|
|
if (kNgModel) {
|
|
val = kNgModel;
|
|
}
|
|
if (widget.options.autoBind === false && !widget.listView.bound()) {
|
|
if (val) {
|
|
widget.value(val);
|
|
}
|
|
} else {
|
|
widget.value(val);
|
|
}
|
|
}
|
|
}, 0);
|
|
};
|
|
if (isForm(element)) {
|
|
element.on('change', function () {
|
|
haveChangeOnElement = true;
|
|
});
|
|
}
|
|
var onChange = function (pristine) {
|
|
return function () {
|
|
var formPristine;
|
|
if (haveChangeOnElement) {
|
|
return;
|
|
}
|
|
if (pristine && ngForm) {
|
|
formPristine = ngForm.$pristine;
|
|
}
|
|
ngModel.$setViewValue(value());
|
|
if (pristine) {
|
|
ngModel.$setPristine();
|
|
if (formPristine) {
|
|
ngForm.$setPristine();
|
|
}
|
|
}
|
|
digest(scope);
|
|
};
|
|
};
|
|
widget.first('change', onChange(false));
|
|
if (!(kendo.ui.AutoComplete && widget instanceof kendo.ui.AutoComplete)) {
|
|
widget.first('dataBound', onChange(true));
|
|
}
|
|
var currentVal = value();
|
|
if (!isNaN(ngModel.$viewValue) && currentVal != ngModel.$viewValue) {
|
|
if (!ngModel.$isEmpty(ngModel.$viewValue)) {
|
|
widget.value(ngModel.$viewValue);
|
|
} else if (currentVal != null && currentVal !== '' && currentVal != ngModel.$viewValue) {
|
|
ngModel.$setViewValue(currentVal);
|
|
}
|
|
}
|
|
ngModel.$setPristine();
|
|
}
|
|
function bindToKNgModel(widget, scope, kNgModel) {
|
|
if (typeof widget.value != 'function') {
|
|
$log.warn('k-ng-model specified on a widget that does not have the value() method: ' + widget.options.name);
|
|
return;
|
|
}
|
|
var form = $(widget.element).parents('form');
|
|
var ngForm = scope[form.attr('name')];
|
|
var getter = $parse(kNgModel);
|
|
var setter = getter.assign;
|
|
var updating = false;
|
|
var valueIsCollection = kendo.ui.MultiSelect && widget instanceof kendo.ui.MultiSelect;
|
|
var length = function (value) {
|
|
return valueIsCollection ? value.length : 0;
|
|
};
|
|
var currentValueLength = length(getter(scope));
|
|
widget.$angular_setLogicValue(getter(scope));
|
|
var watchHandler = function (newValue, oldValue) {
|
|
if (newValue === undefined) {
|
|
newValue = null;
|
|
}
|
|
if (updating || newValue == oldValue && length(newValue) == currentValueLength) {
|
|
return;
|
|
}
|
|
currentValueLength = length(newValue);
|
|
widget.$angular_setLogicValue(newValue);
|
|
};
|
|
if (valueIsCollection) {
|
|
scope.$watchCollection(kNgModel, watchHandler);
|
|
} else {
|
|
scope.$watch(kNgModel, watchHandler);
|
|
}
|
|
widget.first('change', function () {
|
|
updating = true;
|
|
if (ngForm && ngForm.$pristine) {
|
|
ngForm.$setDirty();
|
|
}
|
|
digest(scope, function () {
|
|
setter(scope, widget.$angular_getLogicValue());
|
|
currentValueLength = length(getter(scope));
|
|
});
|
|
updating = false;
|
|
});
|
|
}
|
|
function destroyWidgetOnScopeDestroy(scope, widget) {
|
|
var deregister = scope.$on('$destroy', function () {
|
|
deregister();
|
|
if (widget) {
|
|
kendo.destroy(widget.element);
|
|
widget = null;
|
|
}
|
|
});
|
|
return deregister;
|
|
}
|
|
function propagateClassToWidgetWrapper(widget, element) {
|
|
if (!(window.MutationObserver && widget.wrapper)) {
|
|
return;
|
|
}
|
|
var prevClassList = [].slice.call($(element)[0].classList);
|
|
var mo = new MutationObserver(function (changes) {
|
|
suspend();
|
|
if (!widget) {
|
|
return;
|
|
}
|
|
changes.forEach(function (chg) {
|
|
var w = $(widget.wrapper)[0];
|
|
switch (chg.attributeName) {
|
|
case 'class':
|
|
var currClassList = [].slice.call(chg.target.classList);
|
|
currClassList.forEach(function (cls) {
|
|
if (prevClassList.indexOf(cls) < 0) {
|
|
w.classList.add(cls);
|
|
if (kendo.ui.ComboBox && widget instanceof kendo.ui.ComboBox) {
|
|
widget.input[0].classList.add(cls);
|
|
}
|
|
}
|
|
});
|
|
prevClassList.forEach(function (cls) {
|
|
if (currClassList.indexOf(cls) < 0) {
|
|
w.classList.remove(cls);
|
|
if (kendo.ui.ComboBox && widget instanceof kendo.ui.ComboBox) {
|
|
widget.input[0].classList.remove(cls);
|
|
}
|
|
}
|
|
});
|
|
prevClassList = currClassList;
|
|
break;
|
|
case 'disabled':
|
|
if (typeof widget.enable == 'function' && !widget.element.attr('readonly')) {
|
|
widget.enable(!$(chg.target).attr('disabled'));
|
|
}
|
|
break;
|
|
case 'readonly':
|
|
if (typeof widget.readonly == 'function' && !widget.element.attr('disabled')) {
|
|
widget.readonly(!!$(chg.target).attr('readonly'));
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
resume();
|
|
});
|
|
function suspend() {
|
|
mo.disconnect();
|
|
}
|
|
function resume() {
|
|
mo.observe($(element)[0], { attributes: true });
|
|
}
|
|
resume();
|
|
widget.first('destroy', suspend);
|
|
}
|
|
function setupRebind(widget, scope, element, originalElement, rebindAttr, destroyRegister, attrs) {
|
|
var unregister = scope.$watch(rebindAttr, function (newValue, oldValue) {
|
|
if (!widget._muteRebind && newValue !== oldValue) {
|
|
unregister();
|
|
if (attrs._cleanUp) {
|
|
attrs._cleanUp();
|
|
}
|
|
var templateOptions = WIDGET_TEMPLATE_OPTIONS[widget.options.name];
|
|
if (templateOptions) {
|
|
templateOptions.forEach(function (name) {
|
|
var templateContents = scope.$eval(attrs['k' + name]);
|
|
if (templateContents) {
|
|
originalElement.append($(templateContents).attr(kendo.toHyphens('k' + name), ''));
|
|
}
|
|
});
|
|
}
|
|
var _wrapper = $(widget.wrapper)[0];
|
|
var _element = $(widget.element)[0];
|
|
var isUpload = widget.options.name === 'Upload';
|
|
if (isUpload) {
|
|
element = $(_element);
|
|
}
|
|
var compile = element.injector().get('$compile');
|
|
widget._destroy();
|
|
if (destroyRegister) {
|
|
destroyRegister();
|
|
}
|
|
widget = null;
|
|
if (_element) {
|
|
if (_wrapper) {
|
|
_wrapper.parentNode.replaceChild(_element, _wrapper);
|
|
}
|
|
$(element).replaceWith(originalElement);
|
|
}
|
|
compile(originalElement)(scope);
|
|
}
|
|
}, true);
|
|
digest(scope);
|
|
}
|
|
function bind(f, obj) {
|
|
return function (a, b) {
|
|
return f.call(obj, a, b);
|
|
};
|
|
}
|
|
function setTemplate(key, value) {
|
|
this[key] = kendo.stringify(value);
|
|
}
|
|
module.factory('directiveFactory', [
|
|
'$compile',
|
|
function (compile) {
|
|
var kendoRenderedTimeout;
|
|
var RENDERED = false;
|
|
$defaultCompile = compile;
|
|
var create = function (role, origAttr) {
|
|
return {
|
|
restrict: 'AC',
|
|
require: [
|
|
'?ngModel',
|
|
'^?form'
|
|
],
|
|
scope: false,
|
|
controller: [
|
|
'$scope',
|
|
'$attrs',
|
|
'$element',
|
|
function ($scope, $attrs) {
|
|
this.template = bind(setTemplate, $attrs);
|
|
$attrs._cleanUp = bind(function () {
|
|
this.template = null;
|
|
$attrs._cleanUp = null;
|
|
}, this);
|
|
}
|
|
],
|
|
link: function (scope, element, attrs, controllers) {
|
|
var $element = $(element);
|
|
var roleattr = role.replace(/([A-Z])/g, '-$1');
|
|
$element.attr(roleattr, $element.attr('data-' + roleattr));
|
|
$element[0].removeAttribute('data-' + roleattr);
|
|
var widget = createWidget(scope, element, attrs, role, origAttr, controllers);
|
|
if (!widget) {
|
|
return;
|
|
}
|
|
if (kendoRenderedTimeout) {
|
|
clearTimeout(kendoRenderedTimeout);
|
|
}
|
|
kendoRenderedTimeout = setTimeout(function () {
|
|
scope.$emit('kendoRendered');
|
|
if (!RENDERED) {
|
|
RENDERED = true;
|
|
$('form').each(function () {
|
|
var form = $(this).controller('form');
|
|
if (form) {
|
|
form.$setPristine();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|
|
return { create: create };
|
|
}
|
|
]);
|
|
var TAGNAMES = {
|
|
Editor: 'textarea',
|
|
NumericTextBox: 'input',
|
|
DatePicker: 'input',
|
|
DateTimePicker: 'input',
|
|
TimePicker: 'input',
|
|
AutoComplete: 'input',
|
|
ColorPicker: 'input',
|
|
MaskedTextBox: 'input',
|
|
MultiSelect: 'input',
|
|
Upload: 'input',
|
|
Validator: 'form',
|
|
Button: 'button',
|
|
MobileButton: 'a',
|
|
MobileBackButton: 'a',
|
|
MobileDetailButton: 'a',
|
|
ListView: 'ul',
|
|
MobileListView: 'ul',
|
|
TreeView: 'ul',
|
|
Menu: 'ul',
|
|
ContextMenu: 'ul',
|
|
ActionSheet: 'ul'
|
|
};
|
|
var SKIP_SHORTCUTS = [
|
|
'MobileView',
|
|
'MobileDrawer',
|
|
'MobileLayout',
|
|
'MobileSplitView',
|
|
'MobilePane',
|
|
'MobileModalView'
|
|
];
|
|
var MANUAL_DIRECTIVES = [
|
|
'MobileApplication',
|
|
'MobileView',
|
|
'MobileModalView',
|
|
'MobileLayout',
|
|
'MobileActionSheet',
|
|
'MobileDrawer',
|
|
'MobileSplitView',
|
|
'MobilePane',
|
|
'MobileScrollView',
|
|
'MobilePopOver'
|
|
];
|
|
angular.forEach([
|
|
'MobileNavBar',
|
|
'MobileButton',
|
|
'MobileBackButton',
|
|
'MobileDetailButton',
|
|
'MobileTabStrip',
|
|
'MobileScrollView',
|
|
'MobileScroller'
|
|
], function (widget) {
|
|
MANUAL_DIRECTIVES.push(widget);
|
|
widget = 'kendo' + widget;
|
|
module.directive(widget, function () {
|
|
return {
|
|
restrict: 'A',
|
|
link: function (scope, element, attrs) {
|
|
createWidget(scope, element, attrs, widget, widget);
|
|
}
|
|
};
|
|
});
|
|
});
|
|
function createDirectives(klass, isMobile) {
|
|
function make(directiveName, widgetName) {
|
|
module.directive(directiveName, [
|
|
'directiveFactory',
|
|
function (directiveFactory) {
|
|
return directiveFactory.create(widgetName, directiveName);
|
|
}
|
|
]);
|
|
}
|
|
var name = isMobile ? 'Mobile' : '';
|
|
name += klass.fn.options.name;
|
|
var className = name;
|
|
var shortcut = 'kendo' + name.charAt(0) + name.substr(1).toLowerCase();
|
|
name = 'kendo' + name;
|
|
var dashed = name.replace(/([A-Z])/g, '-$1');
|
|
if (SKIP_SHORTCUTS.indexOf(name.replace('kendo', '')) == -1) {
|
|
var names = name === shortcut ? [name] : [
|
|
name,
|
|
shortcut
|
|
];
|
|
angular.forEach(names, function (directiveName) {
|
|
module.directive(directiveName, function () {
|
|
return {
|
|
restrict: 'E',
|
|
replace: true,
|
|
template: function (element, attributes) {
|
|
var tag = TAGNAMES[className] || 'div';
|
|
var scopeField = attributes.kScopeField || attributes.scopeField;
|
|
return '<' + tag + ' ' + dashed + (scopeField ? '="' + scopeField + '"' : '') + '>' + element.html() + '</' + tag + '>';
|
|
}
|
|
};
|
|
});
|
|
});
|
|
}
|
|
if (MANUAL_DIRECTIVES.indexOf(name.replace('kendo', '')) > -1) {
|
|
return;
|
|
}
|
|
make(name, name);
|
|
if (shortcut != name) {
|
|
make(shortcut, name);
|
|
}
|
|
}
|
|
function kendoWidgetInstance(el) {
|
|
el = $(el);
|
|
return kendo.widgetInstance(el, kendo.ui) || kendo.widgetInstance(el, kendo.mobile.ui) || kendo.widgetInstance(el, kendo.dataviz.ui);
|
|
}
|
|
function digest(scope, func) {
|
|
var root = scope.$root || scope;
|
|
var isDigesting = /^\$(digest|apply)$/.test(root.$$phase);
|
|
if (func) {
|
|
if (isDigesting) {
|
|
func();
|
|
} else {
|
|
root.$apply(func);
|
|
}
|
|
} else if (!isDigesting) {
|
|
root.$digest();
|
|
}
|
|
}
|
|
function destroyScope(scope, el) {
|
|
scope.$destroy();
|
|
if (el) {
|
|
$(el).removeData('$scope').removeData('$$kendoScope').removeData('$isolateScope').removeData('$isolateScopeNoTemplate').removeClass('ng-scope');
|
|
}
|
|
}
|
|
var pendingPatches = [];
|
|
function defadvice(klass, methodName, func) {
|
|
if ($.isArray(klass)) {
|
|
return angular.forEach(klass, function (klass) {
|
|
defadvice(klass, methodName, func);
|
|
});
|
|
}
|
|
if (typeof klass == 'string') {
|
|
var a = klass.split('.');
|
|
var x = kendo;
|
|
while (x && a.length > 0) {
|
|
x = x[a.shift()];
|
|
}
|
|
if (!x) {
|
|
pendingPatches.push([
|
|
klass,
|
|
methodName,
|
|
func
|
|
]);
|
|
return false;
|
|
}
|
|
klass = x.prototype;
|
|
}
|
|
var origMethod = klass[methodName];
|
|
klass[methodName] = function () {
|
|
var self = this, args = arguments;
|
|
return func.apply({
|
|
self: self,
|
|
next: function () {
|
|
return origMethod.apply(self, arguments.length > 0 ? arguments : args);
|
|
}
|
|
}, args);
|
|
};
|
|
return true;
|
|
}
|
|
kendo.onWidgetRegistered(function (entry) {
|
|
pendingPatches = $.grep(pendingPatches, function (args) {
|
|
return !defadvice.apply(null, args);
|
|
});
|
|
createDirectives(entry.widget, entry.prefix == 'Mobile');
|
|
});
|
|
defadvice([
|
|
'ui.Widget',
|
|
'mobile.ui.Widget'
|
|
], 'angular', function (cmd, arg) {
|
|
var self = this.self;
|
|
if (cmd == 'init') {
|
|
if (!arg && OPTIONS_NOW) {
|
|
arg = OPTIONS_NOW;
|
|
}
|
|
OPTIONS_NOW = null;
|
|
if (arg && arg.$angular) {
|
|
self.$angular_scope = arg.$angular[0];
|
|
self.$angular_init(self.element, arg);
|
|
}
|
|
return;
|
|
}
|
|
var scope = self.$angular_scope;
|
|
if (scope) {
|
|
withoutTimeout(function () {
|
|
var x = arg(), elements = x.elements, data = x.data;
|
|
if (elements.length > 0) {
|
|
switch (cmd) {
|
|
case 'cleanup':
|
|
angular.forEach(elements, function (el) {
|
|
var itemScope = $(el).data('$$kendoScope');
|
|
if (itemScope && itemScope !== scope && itemScope.$$kendoScope) {
|
|
destroyScope(itemScope, el);
|
|
}
|
|
});
|
|
break;
|
|
case 'compile':
|
|
var injector = self.element.injector();
|
|
var compile = injector ? injector.get('$compile') : $defaultCompile;
|
|
angular.forEach(elements, function (el, i) {
|
|
var itemScope;
|
|
if (x.scopeFrom) {
|
|
itemScope = x.scopeFrom;
|
|
} else {
|
|
var vars = data && data[i];
|
|
if (vars !== undefined) {
|
|
itemScope = $.extend(scope.$new(), vars);
|
|
itemScope.$$kendoScope = true;
|
|
} else {
|
|
itemScope = scope;
|
|
}
|
|
}
|
|
$(el).data('$$kendoScope', itemScope);
|
|
compile(el)(itemScope);
|
|
});
|
|
digest(scope);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
defadvice('ui.Widget', '$angular_getLogicValue', function () {
|
|
return this.self.value();
|
|
});
|
|
defadvice('ui.Widget', '$angular_setLogicValue', function (val) {
|
|
this.self.value(val);
|
|
});
|
|
defadvice('ui.Select', '$angular_getLogicValue', function () {
|
|
var item = this.self.dataItem(), valueField = this.self.options.dataValueField;
|
|
if (item) {
|
|
if (this.self.options.valuePrimitive) {
|
|
if (!!valueField) {
|
|
return item[valueField];
|
|
} else {
|
|
return item;
|
|
}
|
|
} else {
|
|
return item.toJSON();
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
defadvice('ui.Select', '$angular_setLogicValue', function (val) {
|
|
var self = this.self;
|
|
var options = self.options;
|
|
var valueField = options.dataValueField;
|
|
var text = options.text || '';
|
|
if (val === undefined) {
|
|
val = '';
|
|
}
|
|
if (valueField && !options.valuePrimitive && val) {
|
|
text = val[options.dataTextField] || '';
|
|
val = val[valueField || options.dataTextField];
|
|
}
|
|
if (self.options.autoBind === false && !self.listView.bound()) {
|
|
if (!text && val && options.valuePrimitive) {
|
|
self.value(val);
|
|
} else {
|
|
self._preselect(val, text);
|
|
}
|
|
} else {
|
|
self.value(val);
|
|
}
|
|
});
|
|
defadvice('ui.MultiSelect', '$angular_getLogicValue', function () {
|
|
var value = this.self.dataItems().slice(0);
|
|
var valueField = this.self.options.dataValueField;
|
|
if (valueField && this.self.options.valuePrimitive) {
|
|
value = $.map(value, function (item) {
|
|
return item[valueField];
|
|
});
|
|
}
|
|
return value;
|
|
});
|
|
defadvice('ui.MultiSelect', '$angular_setLogicValue', function (val) {
|
|
if (val == null) {
|
|
val = [];
|
|
}
|
|
var self = this.self;
|
|
var options = self.options;
|
|
var valueField = options.dataValueField;
|
|
var data = val;
|
|
if (valueField && !options.valuePrimitive) {
|
|
val = $.map(val, function (item) {
|
|
return item[valueField];
|
|
});
|
|
}
|
|
if (options.autoBind === false && !options.valuePrimitive && !self.listView.bound()) {
|
|
self._preselect(data, val);
|
|
} else {
|
|
self.value(val);
|
|
}
|
|
});
|
|
defadvice('ui.AutoComplete', '$angular_getLogicValue', function () {
|
|
var options = this.self.options;
|
|
var values = this.self.value().split(options.separator);
|
|
var valuePrimitive = options.valuePrimitive;
|
|
var data = this.self.dataSource.data();
|
|
var dataItems = [];
|
|
for (var idx = 0, length = data.length; idx < length; idx++) {
|
|
var item = data[idx];
|
|
var dataValue = options.dataTextField ? item[options.dataTextField] : item;
|
|
for (var j = 0; j < values.length; j++) {
|
|
if (dataValue === values[j]) {
|
|
if (valuePrimitive) {
|
|
dataItems.push(dataValue);
|
|
} else {
|
|
dataItems.push(item.toJSON());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return dataItems;
|
|
});
|
|
defadvice('ui.AutoComplete', '$angular_setLogicValue', function (value) {
|
|
if (value == null) {
|
|
value = [];
|
|
}
|
|
var self = this.self, dataTextField = self.options.dataTextField;
|
|
if (dataTextField && !self.options.valuePrimitive) {
|
|
if (value.length !== undefined) {
|
|
value = $.map(value, function (item) {
|
|
return item[dataTextField];
|
|
});
|
|
} else {
|
|
value = value[dataTextField];
|
|
}
|
|
}
|
|
self.value(value);
|
|
});
|
|
defadvice('ui.Widget', '$angular_init', function (element, options) {
|
|
var self = this.self;
|
|
if (options && !$.isArray(options)) {
|
|
var scope = self.$angular_scope;
|
|
for (var i = self.events.length; --i >= 0;) {
|
|
var event = self.events[i];
|
|
var handler = options[event];
|
|
if (handler && typeof handler == 'string') {
|
|
options[event] = self.$angular_makeEventHandler(event, scope, handler);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
defadvice('ui.Widget', '$angular_makeEventHandler', function (event, scope, handler) {
|
|
handler = $parse(handler);
|
|
return function (e) {
|
|
digest(scope, function () {
|
|
handler(scope, { kendoEvent: e });
|
|
});
|
|
};
|
|
});
|
|
defadvice([
|
|
'ui.Grid',
|
|
'ui.ListView',
|
|
'ui.TreeView'
|
|
], '$angular_makeEventHandler', function (event, scope, handler) {
|
|
if (event != 'change') {
|
|
return this.next();
|
|
}
|
|
handler = $parse(handler);
|
|
return function (ev) {
|
|
var widget = ev.sender;
|
|
var options = widget.options;
|
|
var cell, multiple, locals = { kendoEvent: ev }, elems, items, columns, colIdx;
|
|
if (angular.isString(options.selectable)) {
|
|
cell = options.selectable.indexOf('cell') !== -1;
|
|
multiple = options.selectable.indexOf('multiple') !== -1;
|
|
}
|
|
elems = locals.selected = this.select();
|
|
items = locals.data = [];
|
|
columns = locals.columns = [];
|
|
for (var i = 0; i < elems.length; i++) {
|
|
var item = cell ? elems[i].parentNode : elems[i];
|
|
var dataItem = widget.dataItem(item);
|
|
if (cell) {
|
|
if (angular.element.inArray(dataItem, items) < 0) {
|
|
items.push(dataItem);
|
|
}
|
|
colIdx = angular.element(elems[i]).index();
|
|
if (angular.element.inArray(colIdx, columns) < 0) {
|
|
columns.push(colIdx);
|
|
}
|
|
} else {
|
|
items.push(dataItem);
|
|
}
|
|
}
|
|
if (!multiple) {
|
|
locals.dataItem = locals.data = items[0];
|
|
locals.angularDataItem = kendo.proxyModelSetters(locals.dataItem);
|
|
locals.selected = elems[0];
|
|
}
|
|
digest(scope, function () {
|
|
handler(scope, locals);
|
|
});
|
|
};
|
|
});
|
|
defadvice('ui.Grid', '$angular_init', function (element, options) {
|
|
this.next();
|
|
if (options.columns) {
|
|
var settings = $.extend({}, kendo.Template, options.templateSettings);
|
|
angular.forEach(options.columns, function (col) {
|
|
if (col.field && !col.template && !col.format && !col.values && (col.encoded === undefined || col.encoded)) {
|
|
col.template = '<span ng-bind=\'' + kendo.expr(col.field, 'dataItem') + '\'>#: ' + kendo.expr(col.field, settings.paramName) + '#</span>';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
{
|
|
defadvice('mobile.ui.ButtonGroup', 'value', function (mew) {
|
|
var self = this.self;
|
|
if (mew != null) {
|
|
self.select(self.element.children('li.km-button').eq(mew));
|
|
self.trigger('change');
|
|
self.trigger('select', { index: self.selectedIndex });
|
|
}
|
|
return self.selectedIndex;
|
|
});
|
|
defadvice('mobile.ui.ButtonGroup', '_select', function () {
|
|
this.next();
|
|
this.self.trigger('change');
|
|
});
|
|
}
|
|
module.directive('kendoMobileApplication', function () {
|
|
return {
|
|
terminal: true,
|
|
link: function (scope, element, attrs) {
|
|
createWidget(scope, element, attrs, 'kendoMobileApplication', 'kendoMobileApplication');
|
|
}
|
|
};
|
|
}).directive('kendoMobileView', function () {
|
|
return {
|
|
scope: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileView', 'kendoMobileView');
|
|
},
|
|
post: function (scope, element, attrs) {
|
|
attrs._instance._layout();
|
|
attrs._instance._scroller();
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobileDrawer', function () {
|
|
return {
|
|
scope: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileDrawer', 'kendoMobileDrawer');
|
|
},
|
|
post: function (scope, element, attrs) {
|
|
attrs._instance._layout();
|
|
attrs._instance._scroller();
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobileModalView', function () {
|
|
return {
|
|
scope: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileModalView', 'kendoMobileModalView');
|
|
},
|
|
post: function (scope, element, attrs) {
|
|
attrs._instance._layout();
|
|
attrs._instance._scroller();
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobileSplitView', function () {
|
|
return {
|
|
terminal: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileSplitView', 'kendoMobileSplitView');
|
|
},
|
|
post: function (scope, element, attrs) {
|
|
attrs._instance._layout();
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobilePane', function () {
|
|
return {
|
|
terminal: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
createWidget(scope, element, attrs, 'kendoMobilePane', 'kendoMobilePane');
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobileLayout', function () {
|
|
return {
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
createWidget(scope, element, attrs, 'kendoMobileLayout', 'kendoMobileLayout');
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoMobileActionSheet', function () {
|
|
return {
|
|
restrict: 'A',
|
|
link: function (scope, element, attrs) {
|
|
element.find('a[k-action]').each(function () {
|
|
$(this).attr('data-' + kendo.ns + 'action', $(this).attr('k-action'));
|
|
});
|
|
createWidget(scope, element, attrs, 'kendoMobileActionSheet', 'kendoMobileActionSheet');
|
|
}
|
|
};
|
|
}).directive('kendoMobilePopOver', function () {
|
|
return {
|
|
terminal: true,
|
|
link: {
|
|
pre: function (scope, element, attrs) {
|
|
attrs.defaultOptions = scope.viewOptions;
|
|
createWidget(scope, element, attrs, 'kendoMobilePopOver', 'kendoMobilePopOver');
|
|
}
|
|
}
|
|
};
|
|
}).directive('kendoViewTitle', function () {
|
|
return {
|
|
restrict: 'E',
|
|
replace: true,
|
|
template: function (element) {
|
|
return '<span data-' + kendo.ns + 'role=\'view-title\'>' + element.html() + '</span>';
|
|
}
|
|
};
|
|
}).directive('kendoMobileHeader', function () {
|
|
return {
|
|
restrict: 'E',
|
|
link: function (scope, element) {
|
|
element.addClass('km-header').attr('data-role', 'header');
|
|
}
|
|
};
|
|
}).directive('kendoMobileFooter', function () {
|
|
return {
|
|
restrict: 'E',
|
|
link: function (scope, element) {
|
|
element.addClass('km-footer').attr('data-role', 'footer');
|
|
}
|
|
};
|
|
}).directive('kendoMobileScrollViewPage', function () {
|
|
return {
|
|
restrict: 'E',
|
|
replace: true,
|
|
template: function (element) {
|
|
return '<div data-' + kendo.ns + 'role=\'page\'>' + element.html() + '</div>';
|
|
}
|
|
};
|
|
});
|
|
angular.forEach([
|
|
'align',
|
|
'icon',
|
|
'rel',
|
|
'transition',
|
|
'actionsheetContext'
|
|
], function (attr) {
|
|
var kAttr = 'k' + attr.slice(0, 1).toUpperCase() + attr.slice(1);
|
|
module.directive(kAttr, function () {
|
|
return {
|
|
restrict: 'A',
|
|
priority: 2,
|
|
link: function (scope, element, attrs) {
|
|
element.attr(kendo.attr(kendo.toHyphens(attr)), scope.$eval(attrs[kAttr]));
|
|
}
|
|
};
|
|
});
|
|
});
|
|
var WIDGET_TEMPLATE_OPTIONS = {
|
|
'TreeMap': ['Template'],
|
|
'MobileListView': [
|
|
'HeaderTemplate',
|
|
'Template'
|
|
],
|
|
'MobileScrollView': [
|
|
'EmptyTemplate',
|
|
'Template'
|
|
],
|
|
'Grid': [
|
|
'AltRowTemplate',
|
|
'DetailTemplate',
|
|
'RowTemplate'
|
|
],
|
|
'ListView': [
|
|
'EditTemplate',
|
|
'Template',
|
|
'AltTemplate'
|
|
],
|
|
'Pager': [
|
|
'SelectTemplate',
|
|
'LinkTemplate'
|
|
],
|
|
'PivotGrid': [
|
|
'ColumnHeaderTemplate',
|
|
'DataCellTemplate',
|
|
'RowHeaderTemplate'
|
|
],
|
|
'Scheduler': [
|
|
'AllDayEventTemplate',
|
|
'DateHeaderTemplate',
|
|
'EventTemplate',
|
|
'MajorTimeHeaderTemplate',
|
|
'MinorTimeHeaderTemplate'
|
|
],
|
|
'TreeView': ['Template'],
|
|
'Validator': ['ErrorTemplate']
|
|
};
|
|
(function () {
|
|
var templateDirectives = {};
|
|
angular.forEach(WIDGET_TEMPLATE_OPTIONS, function (templates, widget) {
|
|
angular.forEach(templates, function (template) {
|
|
if (!templateDirectives[template]) {
|
|
templateDirectives[template] = [];
|
|
}
|
|
templateDirectives[template].push('?^^kendo' + widget);
|
|
});
|
|
});
|
|
angular.forEach(templateDirectives, function (parents, directive) {
|
|
var templateName = 'k' + directive;
|
|
var attrName = kendo.toHyphens(templateName);
|
|
module.directive(templateName, function () {
|
|
return {
|
|
restrict: 'A',
|
|
require: parents,
|
|
terminal: true,
|
|
compile: function ($element, $attrs) {
|
|
if ($attrs[templateName] !== '') {
|
|
return;
|
|
}
|
|
$element.removeAttr(attrName);
|
|
var template = $element[0].outerHTML;
|
|
return function (scope, element, attrs, controllers) {
|
|
var controller;
|
|
while (!controller && controllers.length) {
|
|
controller = controllers.shift();
|
|
}
|
|
if (!controller) {
|
|
$log.warn(attrName + ' without a matching parent widget found. It can be one of the following: ' + parents.join(', '));
|
|
} else {
|
|
controller.template(templateName, template);
|
|
element.remove();
|
|
}
|
|
};
|
|
}
|
|
};
|
|
});
|
|
});
|
|
}());
|
|
}(window.kendo.jQuery, window.angular));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.webcomponents', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'webcomponents',
|
|
name: 'Web Components',
|
|
category: 'framework',
|
|
description: 'Adds Kendo UI custom elements for Web Components',
|
|
depends: ['core']
|
|
};
|
|
(function ($, angular, undefined) {
|
|
if (!kendo.support.customElements || kendo.webComponents.length) {
|
|
return;
|
|
}
|
|
if (angular && (angular.version.major == 1 || angular.injector)) {
|
|
return;
|
|
}
|
|
var TAGNAMES = {
|
|
editor: 'textarea',
|
|
numerictextbox: 'input',
|
|
datepicker: 'input',
|
|
datetimepicker: 'input',
|
|
timepicker: 'input',
|
|
autocomplete: 'input',
|
|
colorpicker: 'input',
|
|
maskedtextbox: 'input',
|
|
dropdownlist: 'select',
|
|
multiselect: 'select',
|
|
upload: 'input',
|
|
validator: 'form',
|
|
button: 'button',
|
|
mobilebutton: 'a',
|
|
mobilebackbutton: 'a',
|
|
mobiledetailbutton: 'a',
|
|
listview: 'ul',
|
|
mobilelistview: 'ul',
|
|
treeview: 'ul',
|
|
menu: 'ul',
|
|
contextmenu: 'ul',
|
|
actionsheet: 'ul'
|
|
};
|
|
var EVENT_PREFIX = 'on-';
|
|
var registered = [];
|
|
kendo.onWidgetRegistered(function (entry) {
|
|
var elementName = entry.prefix + entry.widget.prototype.options.name.toLowerCase();
|
|
if (registered.indexOf(elementName) === -1) {
|
|
registered.push(elementName);
|
|
registerElement(elementName, entry.widget);
|
|
}
|
|
});
|
|
var jsonRegExp = /^\s*(?:\{(?:.|\r\n|\n)*\}|\[(?:.|\r\n|\n)*\])\s*$/;
|
|
var jsonFormatRegExp = /^\{(\d+)(:[^\}]+)?\}|^\[[A-Za-z_]*\]$/;
|
|
var numberRegExp = /^(\+|-?)\d+(\.?)\d*$/;
|
|
function parseOption(element, option) {
|
|
var value = element.getAttribute(option);
|
|
if (value === null) {
|
|
value = undefined;
|
|
} else if (value === 'null') {
|
|
value = null;
|
|
} else if (value === 'true') {
|
|
value = true;
|
|
} else if (value === 'false') {
|
|
value = false;
|
|
} else if (numberRegExp.test(value)) {
|
|
value = parseFloat(value);
|
|
} else if (jsonRegExp.test(value) && !jsonFormatRegExp.test(value)) {
|
|
value = new Function('return (' + value + ')')();
|
|
}
|
|
return value;
|
|
}
|
|
function parseOptions(element, options) {
|
|
var result = {};
|
|
Object.keys(options).concat('dataSource').forEach(function (name) {
|
|
if (element.hasAttribute(kendo.toHyphens(name))) {
|
|
result[name] = parseOption(element, kendo.toHyphens(name));
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
function cloneEvent(e) {
|
|
var result = {};
|
|
Object.keys(e).forEach(function (key) {
|
|
if (key[0] != '_') {
|
|
result[key] = e[key];
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
function eventHandler(eventName, e) {
|
|
var event = document.createEvent('CustomEvent');
|
|
event.initCustomEvent(eventName, false, true, cloneEvent(e));
|
|
this.dispatchEvent(event);
|
|
if (event.defaultPrevented) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
function expose(component, obj) {
|
|
var props = Object.keys(obj);
|
|
for (var idx = 0; idx <= props.length; idx++) {
|
|
if (typeof obj[props[idx]] === 'function') {
|
|
if (!component[props[idx]]) {
|
|
component[props[idx]] = obj[props[idx]].bind(component.widget);
|
|
}
|
|
} else {
|
|
if (props[idx] === 'options') {
|
|
continue;
|
|
}
|
|
component[props[idx]] = component[props[idx]] || obj[props[idx]];
|
|
}
|
|
}
|
|
}
|
|
function registerElement(name, widget) {
|
|
var options = widget.prototype.options;
|
|
var prototype = Object.create(HTMLElement.prototype);
|
|
Object.defineProperty(prototype, 'options', {
|
|
get: function () {
|
|
return this.widget.options;
|
|
},
|
|
set: function (options) {
|
|
var instance = this.widget;
|
|
options = $.extend(true, {}, instance.options, options);
|
|
var _wrapper = $(instance.wrapper)[0];
|
|
var _element = $(instance.element)[0];
|
|
instance._destroy();
|
|
var newElement = document.createElement(TAGNAMES[name] || 'div');
|
|
if (_wrapper && _element) {
|
|
_wrapper.parentNode.replaceChild(_element, _wrapper);
|
|
$(_element).replaceWith(newElement);
|
|
}
|
|
if (instance.value) {
|
|
options.value = instance.value();
|
|
}
|
|
instance.init(newElement, options);
|
|
this.bindEvents();
|
|
}
|
|
});
|
|
prototype.bindEvents = function () {
|
|
widget.prototype.events.forEach(function (eventName) {
|
|
this.widget.bind(eventName, eventHandler.bind(this, eventName));
|
|
if (this.hasAttribute(EVENT_PREFIX + eventName)) {
|
|
this.bind(eventName, function (e) {
|
|
window[this.getAttribute(EVENT_PREFIX + eventName)].call(this, e);
|
|
}.bind(this));
|
|
}
|
|
}.bind(this));
|
|
};
|
|
prototype.attachedCallback = function () {
|
|
var that = this;
|
|
var element = document.createElement(TAGNAMES[name] || 'div');
|
|
$(element).append(that.childNodes);
|
|
$(element).attr('class', $(that).attr('class'));
|
|
$(element).attr('style', $(that).attr('style'));
|
|
that.appendChild(element);
|
|
that.widget = new widget(element, parseOptions(that, options));
|
|
var obj = that.widget;
|
|
do {
|
|
expose(that, obj);
|
|
} while (obj = Object.getPrototypeOf(obj));
|
|
this.bindEvents();
|
|
};
|
|
prototype.detachedCallback = function () {
|
|
kendo.destroy(this.element);
|
|
};
|
|
kendo.webComponents.push('kendo-' + name);
|
|
document.registerElement('kendo-' + name, { prototype: prototype });
|
|
}
|
|
}(window.kendo.jQuery, window.angular));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.angular2', [
|
|
'kendo.core',
|
|
'kendo.webcomponents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'angular2',
|
|
name: 'Angular 2',
|
|
category: 'framework',
|
|
description: 'Supports angular2 value accessors',
|
|
depends: ['core']
|
|
};
|
|
(function (kendo, System) {
|
|
if (!System || !System.register) {
|
|
return;
|
|
}
|
|
var __decorate = this && this.__decorate || function (decorators, target, key, desc) {
|
|
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') {
|
|
return Reflect.decorate(decorators, target, key, desc);
|
|
}
|
|
switch (arguments.length) {
|
|
case 2:
|
|
return decorators.reduceRight(function (o, d) {
|
|
return d && d(o) || o;
|
|
}, target);
|
|
case 3:
|
|
return decorators.reduceRight(function (o, d) {
|
|
return d && d(target, key), void 0;
|
|
}, void 0);
|
|
case 4:
|
|
return decorators.reduceRight(function (o, d) {
|
|
return d && d(target, key, o) || o;
|
|
}, desc);
|
|
}
|
|
};
|
|
var __metadata = this && this.__metadata || function (k, v) {
|
|
if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function') {
|
|
return Reflect.metadata(k, v);
|
|
}
|
|
};
|
|
System.register('kendo/angular2', ['angular2/angular2'], function (exports_1) {
|
|
var angular2_1;
|
|
var KendoValueAccessor;
|
|
return {
|
|
setters: [function (_angular2_1) {
|
|
angular2_1 = _angular2_1;
|
|
}],
|
|
execute: function () {
|
|
KendoValueAccessor = function () {
|
|
function KendoValueAccessor(cd, elementRef) {
|
|
var _this = this;
|
|
this.elementRef = elementRef;
|
|
this.onChange = function (_) {
|
|
};
|
|
this.onTouched = function () {
|
|
};
|
|
this.element = elementRef.nativeElement;
|
|
this.element.addEventListener('change', function () {
|
|
_this.onChange(_this.element.value());
|
|
});
|
|
this.element.addEventListener('spin', function () {
|
|
_this.onChange(_this.element.value());
|
|
});
|
|
cd.valueAccessor = this;
|
|
this.cd = cd;
|
|
cd.valueAccessor = this;
|
|
}
|
|
KendoValueAccessor.prototype.writeValue = function (value) {
|
|
this.element.value(value);
|
|
};
|
|
KendoValueAccessor.prototype.registerOnChange = function (fn) {
|
|
this.onChange = fn;
|
|
};
|
|
KendoValueAccessor.prototype.registerOnTouched = function (fn) {
|
|
this.onTouched = fn;
|
|
};
|
|
KendoValueAccessor = __decorate([
|
|
angular2_1.Directive({ selector: kendo.webComponents.join(',') }),
|
|
__metadata('design:paramtypes', [
|
|
angular2_1.NgControl,
|
|
angular2_1.ElementRef
|
|
])
|
|
], KendoValueAccessor);
|
|
return KendoValueAccessor;
|
|
}();
|
|
exports_1('KendoValueAccessor', KendoValueAccessor);
|
|
}
|
|
};
|
|
});
|
|
}(window.kendo, window.System));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.web', [
|
|
'kendo.core',
|
|
'kendo.router',
|
|
'kendo.view',
|
|
'kendo.fx',
|
|
'kendo.dom',
|
|
'kendo.data.odata',
|
|
'kendo.data.xml',
|
|
'kendo.data',
|
|
'kendo.ooxml',
|
|
'kendo.excel',
|
|
'kendo.data.signalr',
|
|
'kendo.binder',
|
|
'kendo.drawing',
|
|
'kendo.validator',
|
|
'kendo.userevents',
|
|
'kendo.draganddrop',
|
|
'kendo.mobile.scroller',
|
|
'kendo.groupable',
|
|
'kendo.reorderable',
|
|
'kendo.resizable',
|
|
'kendo.sortable',
|
|
'kendo.selectable',
|
|
'kendo.button',
|
|
'kendo.pager',
|
|
'kendo.popup',
|
|
'kendo.notification',
|
|
'kendo.tooltip',
|
|
'kendo.list',
|
|
'kendo.calendar',
|
|
'kendo.datepicker',
|
|
'kendo.autocomplete',
|
|
'kendo.dropdownlist',
|
|
'kendo.combobox',
|
|
'kendo.multiselect',
|
|
'kendo.colorpicker',
|
|
'kendo.columnmenu',
|
|
'kendo.columnsorter',
|
|
'kendo.grid',
|
|
'kendo.listview',
|
|
'kendo.filebrowser',
|
|
'kendo.imagebrowser',
|
|
'kendo.editor',
|
|
'kendo.numerictextbox',
|
|
'kendo.maskedtextbox',
|
|
'kendo.menu',
|
|
'kendo.editable',
|
|
'kendo.pivot.fieldmenu',
|
|
'kendo.filtercell',
|
|
'kendo.panelbar',
|
|
'kendo.progressbar',
|
|
'kendo.responsivepanel',
|
|
'kendo.tabstrip',
|
|
'kendo.timepicker',
|
|
'kendo.toolbar',
|
|
'kendo.datetimepicker',
|
|
'kendo.treeview.draganddrop',
|
|
'kendo.treeview',
|
|
'kendo.slider',
|
|
'kendo.splitter',
|
|
'kendo.upload',
|
|
'kendo.window',
|
|
'kendo.virtuallist',
|
|
'kendo.scheduler.view',
|
|
'kendo.scheduler.dayview',
|
|
'kendo.scheduler.agendaview',
|
|
'kendo.scheduler.monthview',
|
|
'kendo.scheduler.recurrence',
|
|
'kendo.scheduler',
|
|
'kendo.gantt.list',
|
|
'kendo.gantt.timeline',
|
|
'kendo.gantt',
|
|
'kendo.treelist',
|
|
'kendo.pivotgrid',
|
|
'kendo.spreadsheet',
|
|
'kendo.pivot.configurator',
|
|
'kendo.angular',
|
|
'kendo.webcomponents',
|
|
'kendo.angular2'
|
|
], f);
|
|
}(function () {
|
|
'bundle all';
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.core', [
|
|
'kendo.core',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.core',
|
|
name: 'Core',
|
|
description: 'The DataViz core functions',
|
|
category: 'dataviz',
|
|
depends: [
|
|
'core',
|
|
'drawing'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, util = kendo.util, append = util.append, defined = util.defined, last = util.last, valueOrDefault = util.valueOrDefault, dataviz = kendo.dataviz, geom = dataviz.geometry, draw = dataviz.drawing, measureText = draw.util.measureText, Class = kendo.Class, template = kendo.template, noop = $.noop, indexOf = $.inArray, isPlainObject = $.isPlainObject, trim = $.trim, math = Math, deepExtend = kendo.deepExtend;
|
|
var AXIS_LABEL_CLICK = 'axisLabelClick', BLACK = '#000', BOTTOM = 'bottom', CENTER = 'center', COORD_PRECISION = 3, CLIP = 'clip', CIRCLE = 'circle', CROSS = 'cross', DEFAULT_FONT = '12px sans-serif', DEFAULT_HEIGHT = 400, DEFAULT_ICON_SIZE = 7, DEFAULT_PRECISION = 10, DEFAULT_WIDTH = 600, DEG_TO_RAD = math.PI / 180, FORMAT_REGEX = /\{\d+:?/, HEIGHT = 'height', COORDINATE_LIMIT = 100000, INITIAL_ANIMATION_DURATION = 600, INSIDE = 'inside', LEFT = 'left', LINEAR = 'linear', MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, NONE = 'none', NOTE_CLICK = 'noteClick', NOTE_HOVER = 'noteHover', OUTSIDE = 'outside', RADIAL = 'radial', RIGHT = 'right', TOP = 'top', TRIANGLE = 'triangle', WIDTH = 'width', WHITE = '#fff', X = 'x', Y = 'y', ZERO_THRESHOLD = 0.2;
|
|
function getSpacing(value, defaultSpacing) {
|
|
var spacing = {
|
|
top: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
left: 0
|
|
};
|
|
defaultSpacing = defaultSpacing || 0;
|
|
if (typeof value === 'number') {
|
|
spacing[TOP] = spacing[RIGHT] = spacing[BOTTOM] = spacing[LEFT] = value;
|
|
} else {
|
|
spacing[TOP] = value[TOP] || defaultSpacing;
|
|
spacing[RIGHT] = value[RIGHT] || defaultSpacing;
|
|
spacing[BOTTOM] = value[BOTTOM] || defaultSpacing;
|
|
spacing[LEFT] = value[LEFT] || defaultSpacing;
|
|
}
|
|
return spacing;
|
|
}
|
|
var Point2D = function (x, y) {
|
|
var point = this;
|
|
if (!(point instanceof Point2D)) {
|
|
return new Point2D(x, y);
|
|
}
|
|
point.x = x || 0;
|
|
point.y = y || 0;
|
|
};
|
|
Point2D.fn = Point2D.prototype = {
|
|
clone: function () {
|
|
var point = this;
|
|
return new Point2D(point.x, point.y);
|
|
},
|
|
equals: function (point) {
|
|
return point && point.x === this.x && point.y === this.y;
|
|
},
|
|
rotate: function (center, degrees) {
|
|
var point = this, theta = degrees * DEG_TO_RAD, cosT = math.cos(theta), sinT = math.sin(theta), cx = center.x, cy = center.y, x = point.x, y = point.y;
|
|
point.x = round(cx + (x - cx) * cosT + (y - cy) * sinT, COORD_PRECISION);
|
|
point.y = round(cy + (y - cy) * cosT - (x - cx) * sinT, COORD_PRECISION);
|
|
return point;
|
|
},
|
|
multiply: function (a) {
|
|
var point = this;
|
|
point.x *= a;
|
|
point.y *= a;
|
|
return point;
|
|
},
|
|
distanceTo: function (point) {
|
|
var dx = this.x - point.x, dy = this.y - point.y;
|
|
return math.sqrt(dx * dx + dy * dy);
|
|
}
|
|
};
|
|
Point2D.onCircle = function (c, a, r) {
|
|
a *= DEG_TO_RAD;
|
|
return new Point2D(c.x - r * math.cos(a), c.y - r * math.sin(a));
|
|
};
|
|
var Box2D = function (x1, y1, x2, y2) {
|
|
var box = this;
|
|
if (!(box instanceof Box2D)) {
|
|
return new Box2D(x1, y1, x2, y2);
|
|
}
|
|
box.x1 = x1 || 0;
|
|
box.x2 = x2 || 0;
|
|
box.y1 = y1 || 0;
|
|
box.y2 = y2 || 0;
|
|
};
|
|
Box2D.fn = Box2D.prototype = {
|
|
width: function () {
|
|
return this.x2 - this.x1;
|
|
},
|
|
height: function () {
|
|
return this.y2 - this.y1;
|
|
},
|
|
translate: function (dx, dy) {
|
|
var box = this;
|
|
box.x1 += dx;
|
|
box.x2 += dx;
|
|
box.y1 += dy;
|
|
box.y2 += dy;
|
|
return box;
|
|
},
|
|
move: function (x, y) {
|
|
var box = this, height = box.height(), width = box.width();
|
|
if (defined(x)) {
|
|
box.x1 = x;
|
|
box.x2 = box.x1 + width;
|
|
}
|
|
if (defined(y)) {
|
|
box.y1 = y;
|
|
box.y2 = box.y1 + height;
|
|
}
|
|
return box;
|
|
},
|
|
wrap: function (targetBox) {
|
|
var box = this;
|
|
box.x1 = math.min(box.x1, targetBox.x1);
|
|
box.y1 = math.min(box.y1, targetBox.y1);
|
|
box.x2 = math.max(box.x2, targetBox.x2);
|
|
box.y2 = math.max(box.y2, targetBox.y2);
|
|
return box;
|
|
},
|
|
wrapPoint: function (point) {
|
|
this.wrap(new Box2D(point.x, point.y, point.x, point.y));
|
|
return this;
|
|
},
|
|
snapTo: function (targetBox, axis) {
|
|
var box = this;
|
|
if (axis == X || !axis) {
|
|
box.x1 = targetBox.x1;
|
|
box.x2 = targetBox.x2;
|
|
}
|
|
if (axis == Y || !axis) {
|
|
box.y1 = targetBox.y1;
|
|
box.y2 = targetBox.y2;
|
|
}
|
|
return box;
|
|
},
|
|
alignTo: function (targetBox, anchor) {
|
|
var box = this, height = box.height(), width = box.width(), axis = anchor == TOP || anchor == BOTTOM ? Y : X, offset = axis == Y ? height : width;
|
|
if (anchor === CENTER) {
|
|
var targetCenter = targetBox.center();
|
|
var center = box.center();
|
|
box.x1 += targetCenter.x - center.x;
|
|
box.y1 += targetCenter.y - center.y;
|
|
} else if (anchor === TOP || anchor === LEFT) {
|
|
box[axis + 1] = targetBox[axis + 1] - offset;
|
|
} else {
|
|
box[axis + 1] = targetBox[axis + 2];
|
|
}
|
|
box.x2 = box.x1 + width;
|
|
box.y2 = box.y1 + height;
|
|
return box;
|
|
},
|
|
shrink: function (dw, dh) {
|
|
var box = this;
|
|
box.x2 -= dw;
|
|
box.y2 -= dh;
|
|
return box;
|
|
},
|
|
expand: function (dw, dh) {
|
|
this.shrink(-dw, -dh);
|
|
return this;
|
|
},
|
|
pad: function (padding) {
|
|
var box = this, spacing = getSpacing(padding);
|
|
box.x1 -= spacing.left;
|
|
box.x2 += spacing.right;
|
|
box.y1 -= spacing.top;
|
|
box.y2 += spacing.bottom;
|
|
return box;
|
|
},
|
|
unpad: function (padding) {
|
|
var box = this, spacing = getSpacing(padding);
|
|
spacing.left = -spacing.left;
|
|
spacing.top = -spacing.top;
|
|
spacing.right = -spacing.right;
|
|
spacing.bottom = -spacing.bottom;
|
|
return box.pad(spacing);
|
|
},
|
|
clone: function () {
|
|
var box = this;
|
|
return new Box2D(box.x1, box.y1, box.x2, box.y2);
|
|
},
|
|
center: function () {
|
|
var box = this;
|
|
return new Point2D(box.x1 + box.width() / 2, box.y1 + box.height() / 2);
|
|
},
|
|
containsPoint: function (point) {
|
|
var box = this;
|
|
return point.x >= box.x1 && point.x <= box.x2 && point.y >= box.y1 && point.y <= box.y2;
|
|
},
|
|
points: function () {
|
|
var box = this;
|
|
return [
|
|
new Point2D(box.x1, box.y1),
|
|
new Point2D(box.x2, box.y1),
|
|
new Point2D(box.x2, box.y2),
|
|
new Point2D(box.x1, box.y2)
|
|
];
|
|
},
|
|
getHash: function () {
|
|
var box = this;
|
|
return [
|
|
box.x1,
|
|
box.y1,
|
|
box.x2,
|
|
box.y2
|
|
].join(',');
|
|
},
|
|
overlaps: function (box) {
|
|
return !(box.y2 < this.y1 || this.y2 < box.y1 || box.x2 < this.x1 || this.x2 < box.x1);
|
|
},
|
|
rotate: function (rotation) {
|
|
var box = this;
|
|
var width = box.width();
|
|
var height = box.height();
|
|
var center = box.center();
|
|
var cx = center.x;
|
|
var cy = center.y;
|
|
var r1 = rotatePoint(0, 0, cx, cy, rotation);
|
|
var r2 = rotatePoint(width, 0, cx, cy, rotation);
|
|
var r3 = rotatePoint(width, height, cx, cy, rotation);
|
|
var r4 = rotatePoint(0, height, cx, cy, rotation);
|
|
width = math.max(r1.x, r2.x, r3.x, r4.x) - math.min(r1.x, r2.x, r3.x, r4.x);
|
|
height = math.max(r1.y, r2.y, r3.y, r4.y) - math.min(r1.y, r2.y, r3.y, r4.y);
|
|
box.x2 = box.x1 + width;
|
|
box.y2 = box.y1 + height;
|
|
return box;
|
|
},
|
|
toRect: function () {
|
|
return new geom.Rect([
|
|
this.x1,
|
|
this.y1
|
|
], [
|
|
this.width(),
|
|
this.height()
|
|
]);
|
|
},
|
|
hasSize: function () {
|
|
return this.width() !== 0 && this.height() !== 0;
|
|
},
|
|
align: function (targetBox, axis, alignment) {
|
|
var box = this, c1 = axis + 1, c2 = axis + 2, sizeFunc = axis === X ? WIDTH : HEIGHT, size = box[sizeFunc]();
|
|
if (inArray(alignment, [
|
|
LEFT,
|
|
TOP
|
|
])) {
|
|
box[c1] = targetBox[c1];
|
|
box[c2] = box[c1] + size;
|
|
} else if (inArray(alignment, [
|
|
RIGHT,
|
|
BOTTOM
|
|
])) {
|
|
box[c2] = targetBox[c2];
|
|
box[c1] = box[c2] - size;
|
|
} else if (alignment == CENTER) {
|
|
box[c1] = targetBox[c1] + (targetBox[sizeFunc]() - size) / 2;
|
|
box[c2] = box[c1] + size;
|
|
}
|
|
}
|
|
};
|
|
var Ring = Class.extend({
|
|
init: function (center, innerRadius, radius, startAngle, angle) {
|
|
var ring = this;
|
|
ring.c = center;
|
|
ring.ir = innerRadius;
|
|
ring.r = radius;
|
|
ring.startAngle = startAngle;
|
|
ring.angle = angle;
|
|
},
|
|
clone: function () {
|
|
var r = this;
|
|
return new Ring(r.c, r.ir, r.r, r.startAngle, r.angle);
|
|
},
|
|
middle: function () {
|
|
return this.startAngle + this.angle / 2;
|
|
},
|
|
radius: function (newRadius, innerRadius) {
|
|
var that = this;
|
|
if (innerRadius) {
|
|
that.ir = newRadius;
|
|
} else {
|
|
that.r = newRadius;
|
|
}
|
|
return that;
|
|
},
|
|
point: function (angle, innerRadius) {
|
|
var ring = this, radianAngle = angle * DEG_TO_RAD, ax = math.cos(radianAngle), ay = math.sin(radianAngle), radius = innerRadius ? ring.ir : ring.r, x = round(ring.c.x - ax * radius, COORD_PRECISION), y = round(ring.c.y - ay * radius, COORD_PRECISION);
|
|
return new Point2D(x, y);
|
|
},
|
|
adjacentBox: function (distance, width, height) {
|
|
var sector = this.clone().expand(distance), midAndle = sector.middle(), midPoint = sector.point(midAndle), hw = width / 2, hh = height / 2, x = midPoint.x - hw, y = midPoint.y - hh, sa = math.sin(midAndle * DEG_TO_RAD), ca = math.cos(midAndle * DEG_TO_RAD);
|
|
if (math.abs(sa) < 0.9) {
|
|
x += hw * -ca / math.abs(ca);
|
|
}
|
|
if (math.abs(ca) < 0.9) {
|
|
y += hh * -sa / math.abs(sa);
|
|
}
|
|
return new Box2D(x, y, x + width, y + height);
|
|
},
|
|
containsPoint: function (p) {
|
|
var ring = this, c = ring.c, ir = ring.ir, r = ring.r, startAngle = ring.startAngle, endAngle = ring.startAngle + ring.angle, dx = p.x - c.x, dy = p.y - c.y, vector = new Point2D(dx, dy), startPoint = ring.point(startAngle), startVector = new Point2D(startPoint.x - c.x, startPoint.y - c.y), endPoint = ring.point(endAngle), endVector = new Point2D(endPoint.x - c.x, endPoint.y - c.y), dist = round(dx * dx + dy * dy, COORD_PRECISION);
|
|
return (startVector.equals(vector) || clockwise(startVector, vector)) && !clockwise(endVector, vector) && dist >= ir * ir && dist <= r * r;
|
|
},
|
|
getBBox: function () {
|
|
var ring = this, box = new Box2D(MAX_VALUE, MAX_VALUE, MIN_VALUE, MIN_VALUE), sa = round(ring.startAngle % 360), ea = round((sa + ring.angle) % 360), innerRadius = ring.ir, allAngles = [
|
|
0,
|
|
90,
|
|
180,
|
|
270,
|
|
sa,
|
|
ea
|
|
].sort(numericComparer), saIndex = indexOf(sa, allAngles), eaIndex = indexOf(ea, allAngles), angles, i, point;
|
|
if (sa == ea) {
|
|
angles = allAngles;
|
|
} else {
|
|
if (saIndex < eaIndex) {
|
|
angles = allAngles.slice(saIndex, eaIndex + 1);
|
|
} else {
|
|
angles = [].concat(allAngles.slice(0, eaIndex + 1), allAngles.slice(saIndex, allAngles.length));
|
|
}
|
|
}
|
|
for (i = 0; i < angles.length; i++) {
|
|
point = ring.point(angles[i]);
|
|
box.wrapPoint(point);
|
|
box.wrapPoint(point, innerRadius);
|
|
}
|
|
if (!innerRadius) {
|
|
box.wrapPoint(ring.c);
|
|
}
|
|
return box;
|
|
},
|
|
expand: function (value) {
|
|
this.r += value;
|
|
return this;
|
|
}
|
|
});
|
|
var Sector = Ring.extend({
|
|
init: function (center, radius, startAngle, angle) {
|
|
Ring.fn.init.call(this, center, 0, radius, startAngle, angle);
|
|
},
|
|
expand: function (value) {
|
|
return Ring.fn.expand.call(this, value);
|
|
},
|
|
clone: function () {
|
|
var sector = this;
|
|
return new Sector(sector.c, sector.r, sector.startAngle, sector.angle);
|
|
},
|
|
radius: function (newRadius) {
|
|
return Ring.fn.radius.call(this, newRadius);
|
|
},
|
|
point: function (angle) {
|
|
return Ring.fn.point.call(this, angle);
|
|
}
|
|
});
|
|
var ShapeBuilder = function () {
|
|
};
|
|
ShapeBuilder.fn = ShapeBuilder.prototype = {
|
|
createRing: function (sector, options) {
|
|
var startAngle = sector.startAngle + 180;
|
|
var endAngle = sector.angle + startAngle;
|
|
var center = new geom.Point(sector.c.x, sector.c.y);
|
|
var radius = math.max(sector.r, 0);
|
|
var innerRadius = math.max(sector.ir, 0);
|
|
var arc = new geom.Arc(center, {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
radiusX: radius,
|
|
radiusY: radius
|
|
});
|
|
var path = draw.Path.fromArc(arc, options).close();
|
|
if (innerRadius) {
|
|
arc.radiusX = arc.radiusY = innerRadius;
|
|
var innerEnd = arc.pointAt(endAngle);
|
|
path.lineTo(innerEnd.x, innerEnd.y);
|
|
path.arc(endAngle, startAngle, innerRadius, innerRadius, true);
|
|
} else {
|
|
path.lineTo(center.x, center.y);
|
|
}
|
|
return path;
|
|
}
|
|
};
|
|
ShapeBuilder.current = new ShapeBuilder();
|
|
var ChartElement = Class.extend({
|
|
init: function (options) {
|
|
var element = this;
|
|
element.children = [];
|
|
element.options = deepExtend({}, element.options, options);
|
|
},
|
|
reflow: function (targetBox) {
|
|
var element = this, children = element.children, box, i, currentChild;
|
|
for (i = 0; i < children.length; i++) {
|
|
currentChild = children[i];
|
|
currentChild.reflow(targetBox);
|
|
box = box ? box.wrap(currentChild.box) : currentChild.box.clone();
|
|
}
|
|
element.box = box || targetBox;
|
|
},
|
|
destroy: function () {
|
|
var element = this, children = element.children, i;
|
|
if (this.animation) {
|
|
this.animation.destroy();
|
|
}
|
|
for (i = 0; i < children.length; i++) {
|
|
children[i].destroy();
|
|
}
|
|
},
|
|
getRoot: function () {
|
|
var parent = this.parent;
|
|
return parent ? parent.getRoot() : null;
|
|
},
|
|
getChart: function () {
|
|
var root = this.getRoot();
|
|
if (root) {
|
|
return root.chart;
|
|
}
|
|
},
|
|
translateChildren: function (dx, dy) {
|
|
var element = this, children = element.children, childrenCount = children.length, i;
|
|
for (i = 0; i < childrenCount; i++) {
|
|
children[i].box.translate(dx, dy);
|
|
}
|
|
},
|
|
append: function () {
|
|
append(this.children, arguments);
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
arguments[i].parent = this;
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
if (this.options.visible === false) {
|
|
return;
|
|
}
|
|
this.createVisual();
|
|
this.addVisual();
|
|
this.renderChildren();
|
|
this.createAnimation();
|
|
this.renderComplete();
|
|
},
|
|
addVisual: function () {
|
|
if (this.visual) {
|
|
this.visual.chartElement = this;
|
|
if (this.parent) {
|
|
this.parent.appendVisual(this.visual);
|
|
}
|
|
}
|
|
},
|
|
renderChildren: function () {
|
|
var children = this.children;
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].renderVisual();
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
this.visual = new dataviz.drawing.Group({
|
|
zIndex: this.options.zIndex,
|
|
visible: valueOrDefault(this.options.visible, true)
|
|
});
|
|
},
|
|
createAnimation: function () {
|
|
if (this.visual) {
|
|
this.animation = draw.Animation.create(this.visual, this.options.animation);
|
|
}
|
|
},
|
|
appendVisual: function (childVisual) {
|
|
if (!childVisual.chartElement) {
|
|
childVisual.chartElement = this;
|
|
}
|
|
if (childVisual.options.noclip) {
|
|
this.clipRoot().visual.append(childVisual);
|
|
} else if (defined(childVisual.options.zIndex)) {
|
|
this.stackRoot().stackVisual(childVisual);
|
|
} else if (this.visual) {
|
|
this.visual.append(childVisual);
|
|
} else {
|
|
this.parent.appendVisual(childVisual);
|
|
}
|
|
},
|
|
clipRoot: function () {
|
|
if (this.parent) {
|
|
return this.parent.clipRoot();
|
|
}
|
|
return this;
|
|
},
|
|
stackRoot: function () {
|
|
if (this.parent) {
|
|
return this.parent.stackRoot();
|
|
}
|
|
return this;
|
|
},
|
|
stackVisual: function (childVisual) {
|
|
var zIndex = childVisual.options.zIndex || 0;
|
|
var visuals = this.visual.children;
|
|
for (var pos = 0; pos < visuals.length; pos++) {
|
|
var sibling = visuals[pos];
|
|
var here = valueOrDefault(sibling.options.zIndex, 0);
|
|
if (here > zIndex) {
|
|
break;
|
|
}
|
|
}
|
|
this.visual.insertAt(childVisual, pos);
|
|
},
|
|
traverse: function (callback) {
|
|
var children = this.children;
|
|
for (var i = 0; i < children.length; i++) {
|
|
var child = children[i];
|
|
callback(child);
|
|
if (child.traverse) {
|
|
child.traverse(callback);
|
|
}
|
|
}
|
|
},
|
|
closest: function (match) {
|
|
var element = this;
|
|
var matched = false;
|
|
while (element && !matched) {
|
|
matched = match(element);
|
|
if (!matched) {
|
|
element = element.parent;
|
|
}
|
|
}
|
|
if (matched) {
|
|
return element;
|
|
}
|
|
},
|
|
renderComplete: $.noop,
|
|
hasHighlight: function () {
|
|
var options = (this.options || {}).highlight;
|
|
return !(!this.createHighlight || options && options.visible === false);
|
|
},
|
|
toggleHighlight: function (show) {
|
|
var that = this;
|
|
var highlight = that._highlight;
|
|
var options = (that.options || {}).highlight;
|
|
var customVisual = (options || {}).visual;
|
|
if (!highlight) {
|
|
var highlightOptions = {
|
|
fill: {
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
stroke: {
|
|
color: WHITE,
|
|
width: 1,
|
|
opacity: 0.2
|
|
}
|
|
};
|
|
if (customVisual) {
|
|
highlight = that._highlight = customVisual($.extend(that.highlightVisualArgs(), {
|
|
createVisual: function () {
|
|
return that.createHighlight(highlightOptions);
|
|
},
|
|
sender: that.getChart(),
|
|
series: that.series,
|
|
dataItem: that.dataItem,
|
|
category: that.category,
|
|
value: that.value,
|
|
percentage: that.percentage,
|
|
runningTotal: that.runningTotal,
|
|
total: that.total
|
|
}));
|
|
if (!highlight) {
|
|
return;
|
|
}
|
|
} else {
|
|
highlight = that._highlight = that.createHighlight(highlightOptions);
|
|
}
|
|
highlight.options.zIndex = that.options.zIndex;
|
|
that.appendVisual(highlight);
|
|
}
|
|
highlight.visible(show);
|
|
},
|
|
createGradientOverlay: function (element, options, gradientOptions) {
|
|
var overlay = new draw.Path(deepExtend({
|
|
stroke: { color: NONE },
|
|
fill: this.createGradient(gradientOptions),
|
|
closed: element.options.closed
|
|
}, options));
|
|
overlay.segments.elements(element.segments.elements());
|
|
return overlay;
|
|
},
|
|
createGradient: function (options) {
|
|
if (this.parent) {
|
|
return this.parent.createGradient(options);
|
|
}
|
|
}
|
|
});
|
|
var RootElement = ChartElement.extend({
|
|
init: function (options) {
|
|
var root = this;
|
|
root.gradients = {};
|
|
ChartElement.fn.init.call(root, options);
|
|
},
|
|
options: {
|
|
width: DEFAULT_WIDTH,
|
|
height: DEFAULT_HEIGHT,
|
|
background: WHITE,
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
margin: getSpacing(5),
|
|
zIndex: -2
|
|
},
|
|
reflow: function () {
|
|
var root = this, options = root.options, children = root.children, currentBox = new Box2D(0, 0, options.width, options.height);
|
|
root.box = currentBox.unpad(options.margin);
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].reflow(currentBox);
|
|
currentBox = boxDiff(currentBox, children[i].box) || Box2D();
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
this.visual = new draw.Group();
|
|
this.createBackground();
|
|
},
|
|
createBackground: function () {
|
|
var options = this.options;
|
|
var border = options.border || {};
|
|
var box = this.box.clone().pad(options.margin).unpad(border.width);
|
|
var background = draw.Path.fromRect(box.toRect(), {
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType
|
|
},
|
|
fill: {
|
|
color: options.background,
|
|
opacity: options.opacity
|
|
},
|
|
zIndex: -10
|
|
});
|
|
this.visual.append(background);
|
|
},
|
|
getRoot: function () {
|
|
return this;
|
|
},
|
|
createGradient: function (options) {
|
|
var gradients = this.gradients;
|
|
var hashCode = util.objectKey(options);
|
|
var gradient = dataviz.Gradients[options.gradient];
|
|
var drawingGradient;
|
|
if (gradients[hashCode]) {
|
|
drawingGradient = gradients[hashCode];
|
|
} else {
|
|
var gradientOptions = deepExtend({}, gradient, options);
|
|
if (gradient.type == 'linear') {
|
|
drawingGradient = new draw.LinearGradient(gradientOptions);
|
|
} else {
|
|
if (options.innerRadius) {
|
|
gradientOptions.stops = innerRadialStops(gradientOptions);
|
|
}
|
|
drawingGradient = new draw.RadialGradient(gradientOptions);
|
|
drawingGradient.supportVML = gradient.supportVML !== false;
|
|
}
|
|
gradients[hashCode] = drawingGradient;
|
|
}
|
|
return drawingGradient;
|
|
}
|
|
});
|
|
var BoxElement = ChartElement.extend({
|
|
options: {
|
|
align: LEFT,
|
|
vAlign: TOP,
|
|
margin: {},
|
|
padding: {},
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
background: '',
|
|
shrinkToFit: false,
|
|
width: 0,
|
|
height: 0,
|
|
visible: true
|
|
},
|
|
reflow: function (targetBox) {
|
|
var element = this, box, contentBox, options = element.options, width = options.width, height = options.height, hasSetSize = width && height, shrinkToFit = options.shrinkToFit, margin = getSpacing(options.margin), padding = getSpacing(options.padding), borderWidth = options.border.width, children = element.children, i, item;
|
|
function reflowPaddingBox() {
|
|
element.align(targetBox, X, options.align);
|
|
element.align(targetBox, Y, options.vAlign);
|
|
element.paddingBox = box.clone().unpad(margin).unpad(borderWidth);
|
|
}
|
|
contentBox = targetBox.clone();
|
|
if (hasSetSize) {
|
|
contentBox.x2 = contentBox.x1 + width;
|
|
contentBox.y2 = contentBox.y1 + height;
|
|
}
|
|
if (shrinkToFit) {
|
|
contentBox.unpad(margin).unpad(borderWidth).unpad(padding);
|
|
}
|
|
ChartElement.fn.reflow.call(element, contentBox);
|
|
if (hasSetSize) {
|
|
box = element.box = Box2D(0, 0, width, height);
|
|
} else {
|
|
box = element.box;
|
|
}
|
|
if (shrinkToFit && hasSetSize) {
|
|
reflowPaddingBox();
|
|
contentBox = element.contentBox = element.paddingBox.clone().unpad(padding);
|
|
} else {
|
|
contentBox = element.contentBox = box.clone();
|
|
box.pad(padding).pad(borderWidth).pad(margin);
|
|
reflowPaddingBox();
|
|
}
|
|
element.translateChildren(box.x1 - contentBox.x1 + margin.left + borderWidth + padding.left, box.y1 - contentBox.y1 + margin.top + borderWidth + padding.top);
|
|
for (i = 0; i < children.length; i++) {
|
|
item = children[i];
|
|
item.reflow(item.box);
|
|
}
|
|
},
|
|
align: function (targetBox, axis, alignment) {
|
|
this.box.align(targetBox, axis, alignment);
|
|
},
|
|
hasBox: function () {
|
|
var options = this.options;
|
|
return options.border.width || options.background;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var options = this.options;
|
|
if (options.visible && this.hasBox()) {
|
|
this.visual.append(draw.Path.fromRect(this.paddingBox.toRect(), this.visualStyle()));
|
|
}
|
|
},
|
|
visualStyle: function () {
|
|
var boxElement = this, options = boxElement.options, border = options.border || {};
|
|
return {
|
|
stroke: {
|
|
width: border.width,
|
|
color: border.color,
|
|
opacity: valueOrDefault(border.opacity, options.opacity),
|
|
dashType: border.dashType
|
|
},
|
|
fill: {
|
|
color: options.background,
|
|
opacity: options.opacity
|
|
},
|
|
cursor: options.cursor
|
|
};
|
|
}
|
|
});
|
|
var Text = ChartElement.extend({
|
|
init: function (content, options) {
|
|
var text = this;
|
|
ChartElement.fn.init.call(text, options);
|
|
text.content = content;
|
|
text.reflow(Box2D());
|
|
},
|
|
options: {
|
|
font: DEFAULT_FONT,
|
|
color: BLACK,
|
|
align: LEFT,
|
|
vAlign: ''
|
|
},
|
|
reflow: function (targetBox) {
|
|
var text = this, options = text.options, size;
|
|
size = options.size = measureText(text.content, { font: options.font });
|
|
text.baseline = size.baseline;
|
|
text.box = Box2D(targetBox.x1, targetBox.y1, targetBox.x1 + size.width, targetBox.y1 + size.height);
|
|
},
|
|
createVisual: function () {
|
|
var opt = this.options;
|
|
this.visual = new draw.Text(this.content, this.box.toRect().topLeft(), {
|
|
font: opt.font,
|
|
fill: {
|
|
color: opt.color,
|
|
opacity: opt.opacity
|
|
},
|
|
cursor: opt.cursor
|
|
});
|
|
}
|
|
});
|
|
var FloatElement = ChartElement.extend({
|
|
init: function (options) {
|
|
ChartElement.fn.init.call(this, options);
|
|
this._initDirection();
|
|
},
|
|
_initDirection: function () {
|
|
var options = this.options;
|
|
if (options.vertical) {
|
|
this.groupAxis = X;
|
|
this.elementAxis = Y;
|
|
this.groupSizeField = WIDTH;
|
|
this.elementSizeField = HEIGHT;
|
|
this.groupSpacing = options.spacing;
|
|
this.elementSpacing = options.vSpacing;
|
|
} else {
|
|
this.groupAxis = Y;
|
|
this.elementAxis = X;
|
|
this.groupSizeField = HEIGHT;
|
|
this.elementSizeField = WIDTH;
|
|
this.groupSpacing = options.vSpacing;
|
|
this.elementSpacing = options.spacing;
|
|
}
|
|
},
|
|
options: {
|
|
vertical: true,
|
|
wrap: true,
|
|
vSpacing: 0,
|
|
spacing: 0
|
|
},
|
|
reflow: function (targetBox) {
|
|
this.box = targetBox.clone();
|
|
this.reflowChildren();
|
|
},
|
|
reflowChildren: function () {
|
|
var floatElement = this;
|
|
var box = floatElement.box;
|
|
var elementAxis = floatElement.elementAxis;
|
|
var groupAxis = floatElement.groupAxis;
|
|
var elementSizeField = floatElement.elementSizeField;
|
|
var groupSizeField = floatElement.groupSizeField;
|
|
var groupOptions = floatElement.groupOptions();
|
|
var groups = groupOptions.groups;
|
|
var groupsCount = groups.length;
|
|
var groupsStart = box[groupAxis + 1] + floatElement.alignStart(groupOptions.groupsSize, box[groupSizeField]());
|
|
var groupStart = groupsStart;
|
|
var elementStart;
|
|
var groupElementStart;
|
|
var group;
|
|
var groupElements;
|
|
var groupElementsCount;
|
|
var idx;
|
|
var groupIdx;
|
|
var element;
|
|
var elementBox;
|
|
var elementSize;
|
|
if (groupsCount) {
|
|
for (groupIdx = 0; groupIdx < groupsCount; groupIdx++) {
|
|
group = groups[groupIdx];
|
|
groupElements = group.groupElements;
|
|
groupElementsCount = groupElements.length;
|
|
elementStart = box[elementAxis + 1];
|
|
for (idx = 0; idx < groupElementsCount; idx++) {
|
|
element = groupElements[idx];
|
|
elementSize = floatElement.elementSize(element);
|
|
groupElementStart = groupStart + floatElement.alignStart(elementSize[groupSizeField], group.groupSize);
|
|
elementBox = Box2D();
|
|
elementBox[groupAxis + 1] = groupElementStart;
|
|
elementBox[groupAxis + 2] = groupElementStart + elementSize[groupSizeField];
|
|
elementBox[elementAxis + 1] = elementStart;
|
|
elementBox[elementAxis + 2] = elementStart + elementSize[elementSizeField];
|
|
element.reflow(elementBox);
|
|
elementStart += elementSize[elementSizeField] + floatElement.elementSpacing;
|
|
}
|
|
groupStart += group.groupSize + floatElement.groupSpacing;
|
|
}
|
|
box[groupAxis + 1] = groupsStart;
|
|
box[groupAxis + 2] = groupsStart + groupOptions.groupsSize;
|
|
box[elementAxis + 2] = box[elementAxis + 1] + groupOptions.maxGroupElementsSize;
|
|
}
|
|
},
|
|
alignStart: function (size, maxSize) {
|
|
var start = 0;
|
|
var align = this.options.align;
|
|
if (align == RIGHT || align == BOTTOM) {
|
|
start = maxSize - size;
|
|
} else if (align == CENTER) {
|
|
start = (maxSize - size) / 2;
|
|
}
|
|
return start;
|
|
},
|
|
groupOptions: function () {
|
|
var floatElement = this;
|
|
var box = floatElement.box;
|
|
var children = floatElement.children;
|
|
var childrenCount = children.length;
|
|
var elementSizeField = this.elementSizeField;
|
|
var groupSizeField = this.groupSizeField;
|
|
var elementSpacing = this.elementSpacing;
|
|
var groupSpacing = this.groupSpacing;
|
|
var maxSize = round(box[elementSizeField]());
|
|
var idx = 0;
|
|
var groupSize = 0;
|
|
var elementSize;
|
|
var element;
|
|
var groupElementsSize = 0;
|
|
var groupsSize = 0;
|
|
var groups = [];
|
|
var groupElements = [];
|
|
var maxGroupElementsSize = 0;
|
|
for (idx = 0; idx < childrenCount; idx++) {
|
|
element = children[idx];
|
|
if (!element.box) {
|
|
element.reflow(box);
|
|
}
|
|
elementSize = this.elementSize(element);
|
|
if (floatElement.options.wrap && round(groupElementsSize + elementSpacing + elementSize[elementSizeField]) > maxSize) {
|
|
groups.push({
|
|
groupElements: groupElements,
|
|
groupSize: groupSize,
|
|
groupElementsSize: groupElementsSize
|
|
});
|
|
maxGroupElementsSize = math.max(maxGroupElementsSize, groupElementsSize);
|
|
groupsSize += groupSpacing + groupSize;
|
|
groupSize = 0;
|
|
groupElementsSize = 0;
|
|
groupElements = [];
|
|
}
|
|
groupSize = math.max(groupSize, elementSize[groupSizeField]);
|
|
if (groupElementsSize > 0) {
|
|
groupElementsSize += elementSpacing;
|
|
}
|
|
groupElementsSize += elementSize[elementSizeField];
|
|
groupElements.push(element);
|
|
}
|
|
groups.push({
|
|
groupElements: groupElements,
|
|
groupSize: groupSize,
|
|
groupElementsSize: groupElementsSize
|
|
});
|
|
maxGroupElementsSize = math.max(maxGroupElementsSize, groupElementsSize);
|
|
groupsSize += groupSize;
|
|
return {
|
|
groups: groups,
|
|
groupsSize: groupsSize,
|
|
maxGroupElementsSize: maxGroupElementsSize
|
|
};
|
|
},
|
|
elementSize: function (element) {
|
|
return {
|
|
width: element.box.width(),
|
|
height: element.box.height()
|
|
};
|
|
},
|
|
createVisual: noop
|
|
});
|
|
var TextBox = BoxElement.extend({
|
|
ROWS_SPLIT_REGEX: /\n|\\n/m,
|
|
init: function (content, options) {
|
|
var textbox = this;
|
|
textbox.content = content;
|
|
BoxElement.fn.init.call(textbox, options);
|
|
textbox._initContainer();
|
|
textbox.reflow(Box2D());
|
|
},
|
|
_initContainer: function () {
|
|
var textbox = this;
|
|
var options = textbox.options;
|
|
var rows = (textbox.content + '').split(textbox.ROWS_SPLIT_REGEX);
|
|
var floatElement = new FloatElement({
|
|
vertical: true,
|
|
align: options.align,
|
|
wrap: false
|
|
});
|
|
var textOptions = deepExtend({}, options, {
|
|
opacity: 1,
|
|
animation: null
|
|
});
|
|
var text;
|
|
var rowIdx;
|
|
textbox.container = floatElement;
|
|
textbox.append(floatElement);
|
|
for (rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
text = new Text(trim(rows[rowIdx]), textOptions);
|
|
floatElement.append(text);
|
|
}
|
|
},
|
|
reflow: function (targetBox) {
|
|
var options = this.options;
|
|
var visualFn = options.visual;
|
|
this.container.options.align = options.align;
|
|
if (visualFn && !this._boxReflow) {
|
|
if (!targetBox.hasSize()) {
|
|
this._boxReflow = true;
|
|
this.reflow(targetBox);
|
|
this._boxReflow = false;
|
|
targetBox = this.box;
|
|
}
|
|
this.visual = visualFn(this.visualContext(targetBox));
|
|
var visualBox = targetBox;
|
|
if (this.visual) {
|
|
visualBox = rectToBox(this.visual.clippedBBox() || new geom.Rect());
|
|
this.visual.options.zIndex = options.zIndex;
|
|
this.visual.options.noclip = options.noclip;
|
|
}
|
|
this.box = this.contentBox = this.paddingBox = visualBox;
|
|
} else {
|
|
BoxElement.fn.reflow.call(this, targetBox);
|
|
if (options.rotation) {
|
|
var margin = getSpacing(options.margin);
|
|
var box = this.box.unpad(margin);
|
|
this.targetBox = targetBox;
|
|
this.normalBox = box.clone();
|
|
box = this.rotate();
|
|
box.translate(margin.left - margin.right, margin.top - margin.bottom);
|
|
this.rotatedBox = box.clone();
|
|
box.pad(margin);
|
|
}
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var options = this.options;
|
|
if (!options.visible) {
|
|
return;
|
|
}
|
|
this.visual = new dataviz.drawing.Group({
|
|
transform: this.rotationTransform(),
|
|
zIndex: options.zIndex,
|
|
noclip: options.noclip
|
|
});
|
|
if (this.hasBox()) {
|
|
var box = draw.Path.fromRect(this.paddingBox.toRect(), this.visualStyle());
|
|
this.visual.append(box);
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
if (this.options.visual) {
|
|
this.addVisual();
|
|
this.createAnimation();
|
|
} else {
|
|
BoxElement.fn.renderVisual.call(this);
|
|
}
|
|
},
|
|
visualOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
background: options.background,
|
|
border: options.border,
|
|
color: options.color,
|
|
font: options.font,
|
|
margin: options.margin,
|
|
padding: options.padding,
|
|
visible: options.visible
|
|
};
|
|
},
|
|
visualContext: function (targetBox) {
|
|
var textbox = this;
|
|
return {
|
|
text: textbox.content,
|
|
rect: targetBox.toRect(),
|
|
sender: this.getChart(),
|
|
options: textbox.visualOptions(),
|
|
createVisual: function () {
|
|
textbox._boxReflow = true;
|
|
textbox.reflow(targetBox);
|
|
textbox._boxReflow = false;
|
|
return textbox.getDefaultVisual();
|
|
}
|
|
};
|
|
},
|
|
getDefaultVisual: function () {
|
|
this.createVisual();
|
|
this.renderChildren();
|
|
var visual = this.visual;
|
|
delete this.visual;
|
|
return visual;
|
|
},
|
|
rotate: function () {
|
|
var options = this.options;
|
|
this.box.rotate(options.rotation);
|
|
this.align(this.targetBox, X, options.align);
|
|
this.align(this.targetBox, Y, options.vAlign);
|
|
return this.box;
|
|
},
|
|
rotationTransform: function () {
|
|
var rotation = this.options.rotation;
|
|
if (!rotation) {
|
|
return null;
|
|
}
|
|
var center = this.normalBox.center();
|
|
var cx = center.x;
|
|
var cy = center.y;
|
|
var boxCenter = this.rotatedBox.center();
|
|
return geom.transform().translate(boxCenter.x - cx, boxCenter.y - cy).rotate(rotation, [
|
|
cx,
|
|
cy
|
|
]);
|
|
}
|
|
});
|
|
var Title = ChartElement.extend({
|
|
init: function (options) {
|
|
var title = this;
|
|
ChartElement.fn.init.call(title, options);
|
|
options = title.options;
|
|
title.append(new TextBox(options.text, deepExtend({}, options, { vAlign: options.position })));
|
|
},
|
|
options: {
|
|
color: BLACK,
|
|
position: TOP,
|
|
align: CENTER,
|
|
margin: getSpacing(5),
|
|
padding: getSpacing(5)
|
|
},
|
|
reflow: function (targetBox) {
|
|
var title = this;
|
|
ChartElement.fn.reflow.call(title, targetBox);
|
|
title.box.snapTo(targetBox, X);
|
|
}
|
|
});
|
|
Title.buildTitle = function (options, parent, defaultOptions) {
|
|
var title;
|
|
if (typeof options === 'string') {
|
|
options = { text: options };
|
|
}
|
|
options = deepExtend({ visible: true }, defaultOptions, options);
|
|
if (options && options.visible && options.text) {
|
|
title = new Title(options);
|
|
parent.append(title);
|
|
}
|
|
return title;
|
|
};
|
|
var AxisLabel = TextBox.extend({
|
|
init: function (value, text, index, dataItem, options) {
|
|
var label = this;
|
|
label.text = text;
|
|
label.value = value;
|
|
label.index = index;
|
|
label.dataItem = dataItem;
|
|
TextBox.fn.init.call(label, text, options);
|
|
},
|
|
visualContext: function (targetBox) {
|
|
var context = TextBox.fn.visualContext.call(this, targetBox);
|
|
context.value = this.value;
|
|
context.dataItem = this.dataItem;
|
|
context.format = this.options.format;
|
|
context.culture = this.options.culture;
|
|
return context;
|
|
},
|
|
click: function (widget, e) {
|
|
var label = this;
|
|
widget.trigger(AXIS_LABEL_CLICK, {
|
|
element: $(e.target),
|
|
value: label.value,
|
|
text: label.text,
|
|
index: label.index,
|
|
dataItem: label.dataItem,
|
|
axis: label.parent.options
|
|
});
|
|
},
|
|
rotate: function () {
|
|
if (this.options.alignRotation != CENTER) {
|
|
var box = this.normalBox.toRect();
|
|
var transform = this.rotationTransform();
|
|
this.box = rectToBox(box.bbox(transform.matrix()));
|
|
} else {
|
|
TextBox.fn.rotate.call(this);
|
|
}
|
|
return this.box;
|
|
},
|
|
rotationTransform: function () {
|
|
var options = this.options;
|
|
var rotation = options.rotation;
|
|
if (!rotation) {
|
|
return null;
|
|
}
|
|
if (options.alignRotation == CENTER) {
|
|
return TextBox.fn.rotationTransform.call(this);
|
|
}
|
|
var rotationMatrix = geom.transform().rotate(rotation).matrix();
|
|
var box = this.normalBox.toRect();
|
|
var rect = this.targetBox.toRect();
|
|
var rotationOrigin = options.rotationOrigin || TOP;
|
|
var alignAxis = rotationOrigin == TOP || rotationOrigin == BOTTOM ? X : Y;
|
|
var distanceAxis = rotationOrigin == TOP || rotationOrigin == BOTTOM ? Y : X;
|
|
var axisAnchor = rotationOrigin == TOP || rotationOrigin == LEFT ? rect.origin : rect.bottomRight();
|
|
var topLeft = box.topLeft().transformCopy(rotationMatrix);
|
|
var topRight = box.topRight().transformCopy(rotationMatrix);
|
|
var bottomRight = box.bottomRight().transformCopy(rotationMatrix);
|
|
var bottomLeft = box.bottomLeft().transformCopy(rotationMatrix);
|
|
var rotatedBox = geom.Rect.fromPoints(topLeft, topRight, bottomRight, bottomLeft);
|
|
var translate = {};
|
|
translate[distanceAxis] = rect.origin[distanceAxis] - rotatedBox.origin[distanceAxis];
|
|
var distanceLeft = math.abs(topLeft[distanceAxis] + translate[distanceAxis] - axisAnchor[distanceAxis]);
|
|
var distanceRight = math.abs(topRight[distanceAxis] + translate[distanceAxis] - axisAnchor[distanceAxis]);
|
|
var alignStart;
|
|
var alignEnd;
|
|
if (round(distanceLeft, DEFAULT_PRECISION) === round(distanceRight, DEFAULT_PRECISION)) {
|
|
alignStart = topLeft;
|
|
alignEnd = topRight;
|
|
} else if (distanceRight < distanceLeft) {
|
|
alignStart = topRight;
|
|
alignEnd = bottomRight;
|
|
} else {
|
|
alignStart = topLeft;
|
|
alignEnd = bottomLeft;
|
|
}
|
|
var alignCenter = alignStart[alignAxis] + (alignEnd[alignAxis] - alignStart[alignAxis]) / 2;
|
|
translate[alignAxis] = rect.center()[alignAxis] - alignCenter;
|
|
return geom.transform().translate(translate.x, translate.y).rotate(rotation);
|
|
}
|
|
});
|
|
function createAxisTick(options, tickOptions) {
|
|
var tickX = options.tickX, tickY = options.tickY, position = options.position;
|
|
var tick = new draw.Path({
|
|
stroke: {
|
|
width: tickOptions.width,
|
|
color: tickOptions.color
|
|
}
|
|
});
|
|
if (options.vertical) {
|
|
tick.moveTo(tickX, position).lineTo(tickX + tickOptions.size, position);
|
|
} else {
|
|
tick.moveTo(position, tickY).lineTo(position, tickY + tickOptions.size);
|
|
}
|
|
alignPathToPixel(tick);
|
|
return tick;
|
|
}
|
|
function createAxisGridLine(options, gridLine) {
|
|
var lineStart = options.lineStart, lineEnd = options.lineEnd, position = options.position;
|
|
var line = new draw.Path({
|
|
stroke: {
|
|
width: gridLine.width,
|
|
color: gridLine.color,
|
|
dashType: gridLine.dashType
|
|
}
|
|
});
|
|
if (options.vertical) {
|
|
line.moveTo(lineStart, position).lineTo(lineEnd, position);
|
|
} else {
|
|
line.moveTo(position, lineStart).lineTo(position, lineEnd);
|
|
}
|
|
alignPathToPixel(line);
|
|
return line;
|
|
}
|
|
var Axis = ChartElement.extend({
|
|
init: function (options) {
|
|
var axis = this;
|
|
ChartElement.fn.init.call(axis, options);
|
|
if (!axis.options.visible) {
|
|
axis.options = deepExtend({}, axis.options, {
|
|
labels: { visible: false },
|
|
line: { visible: false },
|
|
margin: 0,
|
|
majorTickSize: 0,
|
|
minorTickSize: 0
|
|
});
|
|
}
|
|
axis.options.minorTicks = deepExtend({}, {
|
|
color: axis.options.line.color,
|
|
width: axis.options.line.width,
|
|
visible: axis.options.minorTickType != NONE
|
|
}, axis.options.minorTicks, {
|
|
size: axis.options.minorTickSize,
|
|
align: axis.options.minorTickType
|
|
});
|
|
axis.options.majorTicks = deepExtend({}, {
|
|
color: axis.options.line.color,
|
|
width: axis.options.line.width,
|
|
visible: axis.options.majorTickType != NONE
|
|
}, axis.options.majorTicks, {
|
|
size: axis.options.majorTickSize,
|
|
align: axis.options.majorTickType
|
|
});
|
|
if (!this.options._deferLabels) {
|
|
axis.createLabels();
|
|
}
|
|
axis.createTitle();
|
|
axis.createNotes();
|
|
},
|
|
options: {
|
|
labels: {
|
|
visible: true,
|
|
rotation: 0,
|
|
mirror: false,
|
|
step: 1,
|
|
skip: 0
|
|
},
|
|
line: {
|
|
width: 1,
|
|
color: BLACK,
|
|
visible: true
|
|
},
|
|
title: {
|
|
visible: true,
|
|
position: CENTER
|
|
},
|
|
majorTicks: {
|
|
align: OUTSIDE,
|
|
size: 4,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
minorTicks: {
|
|
align: OUTSIDE,
|
|
size: 3,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
axisCrossingValue: 0,
|
|
majorTickType: OUTSIDE,
|
|
minorTickType: NONE,
|
|
majorGridLines: {
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
minorGridLines: {
|
|
visible: false,
|
|
width: 1,
|
|
color: BLACK,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
margin: 5,
|
|
visible: true,
|
|
reverse: false,
|
|
justified: true,
|
|
notes: { label: { text: '' } },
|
|
_alignLines: true,
|
|
_deferLabels: false
|
|
},
|
|
labelsRange: function () {
|
|
return {
|
|
min: this.options.labels.skip,
|
|
max: this.labelsCount()
|
|
};
|
|
},
|
|
createLabels: function () {
|
|
var axis = this, options = axis.options, align = options.vertical ? RIGHT : CENTER, labelOptions = deepExtend({}, options.labels, {
|
|
align: align,
|
|
zIndex: options.zIndex
|
|
}), step = math.max(1, labelOptions.step);
|
|
axis.children = $.grep(axis.children, function (child) {
|
|
return !(child instanceof AxisLabel);
|
|
});
|
|
axis.labels = [];
|
|
if (labelOptions.visible) {
|
|
var range = axis.labelsRange(), rotation = labelOptions.rotation, label, i;
|
|
if (isPlainObject(rotation)) {
|
|
labelOptions.alignRotation = rotation.align;
|
|
labelOptions.rotation = rotation.angle;
|
|
}
|
|
if (labelOptions.rotation == 'auto') {
|
|
labelOptions.rotation = 0;
|
|
options.autoRotateLabels = true;
|
|
}
|
|
for (i = range.min; i < range.max; i += step) {
|
|
label = axis.createAxisLabel(i, labelOptions);
|
|
if (label) {
|
|
axis.append(label);
|
|
axis.labels.push(label);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
lineBox: function () {
|
|
var axis = this, options = axis.options, box = axis.box, vertical = options.vertical, mirror = options.labels.mirror, axisX = mirror ? box.x1 : box.x2, axisY = mirror ? box.y2 : box.y1, lineWidth = options.line.width || 0;
|
|
return vertical ? Box2D(axisX, box.y1, axisX, box.y2 - lineWidth) : Box2D(box.x1, axisY, box.x2 - lineWidth, axisY);
|
|
},
|
|
createTitle: function () {
|
|
var axis = this, options = axis.options, titleOptions = deepExtend({
|
|
rotation: options.vertical ? -90 : 0,
|
|
text: '',
|
|
zIndex: 1,
|
|
visualSize: true
|
|
}, options.title), title;
|
|
if (titleOptions.visible && titleOptions.text) {
|
|
title = new TextBox(titleOptions.text, titleOptions);
|
|
axis.append(title);
|
|
axis.title = title;
|
|
}
|
|
},
|
|
createNotes: function () {
|
|
var axis = this, options = axis.options, notes = options.notes, items = notes.data || [], i, item, note;
|
|
axis.notes = [];
|
|
for (i = 0; i < items.length; i++) {
|
|
item = deepExtend({}, notes, items[i]);
|
|
item.value = axis.parseNoteValue(item.value);
|
|
note = new Note(item.value, item.label.text, null, null, null, item);
|
|
if (note.options.visible) {
|
|
if (defined(note.options.position)) {
|
|
if (options.vertical && !inArray(note.options.position, [
|
|
LEFT,
|
|
RIGHT
|
|
])) {
|
|
note.options.position = options.reverse ? LEFT : RIGHT;
|
|
} else if (!options.vertical && !inArray(note.options.position, [
|
|
TOP,
|
|
BOTTOM
|
|
])) {
|
|
note.options.position = options.reverse ? BOTTOM : TOP;
|
|
}
|
|
} else {
|
|
if (options.vertical) {
|
|
note.options.position = options.reverse ? LEFT : RIGHT;
|
|
} else {
|
|
note.options.position = options.reverse ? BOTTOM : TOP;
|
|
}
|
|
}
|
|
axis.append(note);
|
|
axis.notes.push(note);
|
|
}
|
|
}
|
|
},
|
|
parseNoteValue: function (value) {
|
|
return value;
|
|
},
|
|
renderVisual: function () {
|
|
ChartElement.fn.renderVisual.call(this);
|
|
this.createPlotBands();
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
this.createBackground();
|
|
this.createLine();
|
|
},
|
|
gridLinesVisual: function () {
|
|
var gridLines = this._gridLines;
|
|
if (!gridLines) {
|
|
gridLines = this._gridLines = new draw.Group({ zIndex: -2 });
|
|
this.appendVisual(this._gridLines);
|
|
}
|
|
return gridLines;
|
|
},
|
|
createTicks: function (lineGroup) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), mirror = options.labels.mirror, majorUnit = options.majorTicks.visible ? options.majorUnit : 0, tickLineOptions = { vertical: options.vertical };
|
|
function render(tickPositions, tickOptions, skipUnit) {
|
|
var i, count = tickPositions.length;
|
|
if (tickOptions.visible) {
|
|
for (i = tickOptions.skip; i < count; i += tickOptions.step) {
|
|
if (defined(skipUnit) && i % skipUnit === 0) {
|
|
continue;
|
|
}
|
|
tickLineOptions.tickX = mirror ? lineBox.x2 : lineBox.x2 - tickOptions.size;
|
|
tickLineOptions.tickY = mirror ? lineBox.y1 - tickOptions.size : lineBox.y1;
|
|
tickLineOptions.position = tickPositions[i];
|
|
lineGroup.append(createAxisTick(tickLineOptions, tickOptions));
|
|
}
|
|
}
|
|
}
|
|
render(axis.getMajorTickPositions(), options.majorTicks);
|
|
render(axis.getMinorTickPositions(), options.minorTicks, majorUnit / options.minorUnit);
|
|
},
|
|
createLine: function () {
|
|
var axis = this, options = axis.options, line = options.line, lineBox = axis.lineBox();
|
|
if (line.width > 0 && line.visible) {
|
|
var path = new draw.Path({
|
|
stroke: {
|
|
width: line.width,
|
|
color: line.color,
|
|
dashType: line.dashType
|
|
}
|
|
});
|
|
path.moveTo(lineBox.x1, lineBox.y1).lineTo(lineBox.x2, lineBox.y2);
|
|
if (options._alignLines) {
|
|
alignPathToPixel(path);
|
|
}
|
|
var group = this._lineGroup = new draw.Group();
|
|
group.append(path);
|
|
this.visual.append(group);
|
|
this.createTicks(group);
|
|
}
|
|
},
|
|
getActualTickSize: function () {
|
|
var axis = this, options = axis.options, tickSize = 0;
|
|
if (options.majorTicks.visible && options.minorTicks.visible) {
|
|
tickSize = math.max(options.majorTicks.size, options.minorTicks.size);
|
|
} else if (options.majorTicks.visible) {
|
|
tickSize = options.majorTicks.size;
|
|
} else if (options.minorTicks.visible) {
|
|
tickSize = options.minorTicks.size;
|
|
}
|
|
return tickSize;
|
|
},
|
|
createBackground: function () {
|
|
var axis = this, options = axis.options, background = options.background, box = axis.box;
|
|
if (background) {
|
|
axis._backgroundPath = draw.Path.fromRect(box.toRect(), {
|
|
fill: { color: background },
|
|
stroke: null
|
|
});
|
|
this.visual.append(axis._backgroundPath);
|
|
}
|
|
},
|
|
createPlotBands: function () {
|
|
var axis = this, options = axis.options, plotBands = options.plotBands || [], vertical = options.vertical, plotArea = axis.plotArea, slotX, slotY, from, to;
|
|
if (plotBands.length === 0) {
|
|
return;
|
|
}
|
|
var group = this._plotbandGroup = new draw.Group({ zIndex: -1 });
|
|
var altAxis = $.grep(axis.pane.axes, function (a) {
|
|
return a.options.vertical !== axis.options.vertical;
|
|
})[0];
|
|
$.each(plotBands, function (i, item) {
|
|
from = valueOrDefault(item.from, MIN_VALUE);
|
|
to = valueOrDefault(item.to, MAX_VALUE);
|
|
if (vertical) {
|
|
slotX = (altAxis || plotArea.axisX).lineBox();
|
|
slotY = axis.getSlot(item.from, item.to, true);
|
|
} else {
|
|
slotX = axis.getSlot(item.from, item.to, true);
|
|
slotY = (altAxis || plotArea.axisY).lineBox();
|
|
}
|
|
if (slotX.width() !== 0 && slotY.height() !== 0) {
|
|
var bandRect = new geom.Rect([
|
|
slotX.x1,
|
|
slotY.y1
|
|
], [
|
|
slotX.width(),
|
|
slotY.height()
|
|
]);
|
|
var path = draw.Path.fromRect(bandRect, {
|
|
fill: {
|
|
color: item.color,
|
|
opacity: item.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
group.append(path);
|
|
}
|
|
});
|
|
axis.appendVisual(group);
|
|
},
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, axisLineVisible = altAxis.options.line.visible, majorGridLines = options.majorGridLines, majorUnit = majorGridLines.visible ? options.majorUnit : 0, vertical = options.vertical, lineBox = altAxis.lineBox(), linePos = lineBox[vertical ? 'y1' : 'x1'], lineOptions = {
|
|
lineStart: lineBox[vertical ? 'x1' : 'y1'],
|
|
lineEnd: lineBox[vertical ? 'x2' : 'y2'],
|
|
vertical: vertical
|
|
}, pos, majorTicks = [];
|
|
var container = this.gridLinesVisual();
|
|
function render(tickPositions, gridLine, skipUnit) {
|
|
var count = tickPositions.length, i;
|
|
if (gridLine.visible) {
|
|
for (i = gridLine.skip; i < count; i += gridLine.step) {
|
|
pos = round(tickPositions[i]);
|
|
if (!inArray(pos, majorTicks)) {
|
|
if (i % skipUnit !== 0 && (!axisLineVisible || linePos !== pos)) {
|
|
lineOptions.position = pos;
|
|
container.append(createAxisGridLine(lineOptions, gridLine));
|
|
majorTicks.push(pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
render(axis.getMajorTickPositions(), options.majorGridLines);
|
|
render(axis.getMinorTickPositions(), options.minorGridLines, majorUnit / options.minorUnit);
|
|
return container.children;
|
|
},
|
|
reflow: function (box) {
|
|
var axis = this, options = axis.options, vertical = options.vertical, labels = axis.labels, count = labels.length, title = axis.title, sizeFn = vertical ? WIDTH : HEIGHT, titleSize = title ? title.box[sizeFn]() : 0, space = axis.getActualTickSize() + options.margin + titleSize, maxLabelSize = 0, rootBox = (this.getRoot() || {}).box || box, boxSize = rootBox[sizeFn](), labelSize, i;
|
|
for (i = 0; i < count; i++) {
|
|
labelSize = labels[i].box[sizeFn]();
|
|
if (labelSize + space <= boxSize) {
|
|
maxLabelSize = math.max(maxLabelSize, labelSize);
|
|
}
|
|
}
|
|
if (vertical) {
|
|
axis.box = Box2D(box.x1, box.y1, box.x1 + maxLabelSize + space, box.y2);
|
|
} else {
|
|
axis.box = Box2D(box.x1, box.y1, box.x2, box.y1 + maxLabelSize + space);
|
|
}
|
|
axis.arrangeTitle();
|
|
axis.arrangeLabels();
|
|
axis.arrangeNotes();
|
|
},
|
|
getLabelsTickPositions: function () {
|
|
return this.getMajorTickPositions();
|
|
},
|
|
labelTickIndex: function (label) {
|
|
return label.index;
|
|
},
|
|
arrangeLabels: function () {
|
|
var axis = this, options = axis.options, labels = axis.labels, labelsBetweenTicks = !options.justified, vertical = options.vertical, lineBox = axis.lineBox(), mirror = options.labels.mirror, tickPositions = axis.getLabelsTickPositions(), labelOffset = axis.getActualTickSize() + options.margin, labelBox, labelY, i;
|
|
for (i = 0; i < labels.length; i++) {
|
|
var label = labels[i], tickIx = axis.labelTickIndex(label), labelSize = vertical ? label.box.height() : label.box.width(), labelPos = tickPositions[tickIx] - labelSize / 2, firstTickPosition, nextTickPosition, middle, labelX;
|
|
if (vertical) {
|
|
if (labelsBetweenTicks) {
|
|
firstTickPosition = tickPositions[tickIx];
|
|
nextTickPosition = tickPositions[tickIx + 1];
|
|
middle = firstTickPosition + (nextTickPosition - firstTickPosition) / 2;
|
|
labelPos = middle - labelSize / 2;
|
|
}
|
|
labelX = lineBox.x2;
|
|
if (mirror) {
|
|
labelX += labelOffset;
|
|
label.options.rotationOrigin = LEFT;
|
|
} else {
|
|
labelX -= labelOffset + label.box.width();
|
|
label.options.rotationOrigin = RIGHT;
|
|
}
|
|
labelBox = label.box.move(labelX, labelPos);
|
|
} else {
|
|
if (labelsBetweenTicks) {
|
|
firstTickPosition = tickPositions[tickIx];
|
|
nextTickPosition = tickPositions[tickIx + 1];
|
|
} else {
|
|
firstTickPosition = labelPos;
|
|
nextTickPosition = labelPos + labelSize;
|
|
}
|
|
labelY = lineBox.y1;
|
|
if (mirror) {
|
|
labelY -= labelOffset + label.box.height();
|
|
label.options.rotationOrigin = BOTTOM;
|
|
} else {
|
|
labelY += labelOffset;
|
|
label.options.rotationOrigin = TOP;
|
|
}
|
|
labelBox = Box2D(firstTickPosition, labelY, nextTickPosition, labelY + label.box.height());
|
|
}
|
|
label.reflow(labelBox);
|
|
}
|
|
},
|
|
autoRotateLabels: function () {
|
|
if (this.options.autoRotateLabels && !this.options.vertical) {
|
|
var tickPositions = this.getMajorTickPositions();
|
|
var labels = this.labels;
|
|
var labelBox, angle, width, idx;
|
|
for (idx = 0; idx < labels.length; idx++) {
|
|
width = tickPositions[idx + 1] - tickPositions[idx];
|
|
labelBox = labels[idx].box;
|
|
if (labelBox.width() > width) {
|
|
if (labelBox.height() > width) {
|
|
angle = -90;
|
|
break;
|
|
}
|
|
angle = -45;
|
|
}
|
|
}
|
|
if (angle) {
|
|
for (idx = 0; idx < labels.length; idx++) {
|
|
labels[idx].options.rotation = angle;
|
|
labels[idx].reflow(Box2D());
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
arrangeTitle: function () {
|
|
var axis = this, options = axis.options, mirror = options.labels.mirror, vertical = options.vertical, title = axis.title;
|
|
if (title) {
|
|
if (vertical) {
|
|
title.options.align = mirror ? RIGHT : LEFT;
|
|
title.options.vAlign = title.options.position;
|
|
} else {
|
|
title.options.align = title.options.position;
|
|
title.options.vAlign = mirror ? TOP : BOTTOM;
|
|
}
|
|
title.reflow(axis.box);
|
|
}
|
|
},
|
|
arrangeNotes: function () {
|
|
var axis = this, i, item, slot, value;
|
|
for (i = 0; i < axis.notes.length; i++) {
|
|
item = axis.notes[i];
|
|
value = item.options.value;
|
|
if (defined(value)) {
|
|
if (axis.shouldRenderNote(value)) {
|
|
item.show();
|
|
} else {
|
|
item.hide();
|
|
}
|
|
slot = axis.getSlot(value);
|
|
} else {
|
|
item.hide();
|
|
}
|
|
item.reflow(slot || axis.lineBox());
|
|
}
|
|
},
|
|
alignTo: function (secondAxis) {
|
|
var axis = this, lineBox = secondAxis.lineBox(), vertical = axis.options.vertical, pos = vertical ? Y : X;
|
|
axis.box.snapTo(lineBox, pos);
|
|
if (vertical) {
|
|
axis.box.shrink(0, axis.lineBox().height() - lineBox.height());
|
|
} else {
|
|
axis.box.shrink(axis.lineBox().width() - lineBox.width(), 0);
|
|
}
|
|
axis.box[pos + 1] -= axis.lineBox()[pos + 1] - lineBox[pos + 1];
|
|
axis.box[pos + 2] -= axis.lineBox()[pos + 2] - lineBox[pos + 2];
|
|
},
|
|
axisLabelText: function (value, dataItem, options) {
|
|
var text = value;
|
|
if (options.template) {
|
|
var tmpl = template(options.template);
|
|
text = tmpl({
|
|
value: value,
|
|
dataItem: dataItem,
|
|
format: options.format,
|
|
culture: options.culture
|
|
});
|
|
} else if (options.format) {
|
|
if (options.format.match(FORMAT_REGEX)) {
|
|
text = kendo.format(options.format, value);
|
|
} else {
|
|
text = kendo.toString(value, options.format, options.culture);
|
|
}
|
|
}
|
|
return text;
|
|
},
|
|
slot: function (from, to, limit) {
|
|
var slot = this.getSlot(from, to, limit);
|
|
if (slot) {
|
|
return slot.toRect();
|
|
}
|
|
},
|
|
contentBox: function () {
|
|
var box = this.box.clone();
|
|
var labels = this.labels;
|
|
if (labels.length) {
|
|
if (labels[0].options.visible) {
|
|
box.wrap(labels[0].box);
|
|
}
|
|
if (last(labels).options.visible) {
|
|
box.wrap(last(labels).box);
|
|
}
|
|
}
|
|
return box;
|
|
},
|
|
limitRange: function (from, to, min, max, offset) {
|
|
var options = this.options;
|
|
if (from < min && offset < 0 && (!defined(options.min) || options.min <= min) || max < to && offset > 0 && (!defined(options.max) || max <= options.max)) {
|
|
return;
|
|
}
|
|
if (to < min && offset > 0 || max < from && offset < 0) {
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
}
|
|
var rangeSize = to - from;
|
|
if (from < min) {
|
|
from = util.limitValue(from, min, max);
|
|
to = util.limitValue(from + rangeSize, min + rangeSize, max);
|
|
} else if (to > max) {
|
|
to = util.limitValue(to, min, max);
|
|
from = util.limitValue(to - rangeSize, min, max - rangeSize);
|
|
}
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
}
|
|
});
|
|
var Note = BoxElement.extend({
|
|
init: function (value, text, dataItem, category, series, options) {
|
|
var note = this;
|
|
BoxElement.fn.init.call(note, options);
|
|
note.value = value;
|
|
note.text = text;
|
|
note.dataItem = dataItem;
|
|
note.category = category;
|
|
note.series = series;
|
|
note.render();
|
|
},
|
|
options: {
|
|
icon: {
|
|
visible: true,
|
|
type: CIRCLE
|
|
},
|
|
label: {
|
|
position: INSIDE,
|
|
visible: true,
|
|
align: CENTER,
|
|
vAlign: CENTER
|
|
},
|
|
line: { visible: true },
|
|
visible: true,
|
|
position: TOP,
|
|
zIndex: 2
|
|
},
|
|
hide: function () {
|
|
this.options.visible = false;
|
|
},
|
|
show: function () {
|
|
this.options.visible = true;
|
|
},
|
|
render: function () {
|
|
var note = this, options = note.options, label = options.label, text = note.text, icon = options.icon, size = icon.size, box = Box2D(), marker, width, height, noteTemplate;
|
|
if (options.visible) {
|
|
if (defined(label) && label.visible) {
|
|
if (label.template) {
|
|
noteTemplate = template(label.template);
|
|
text = noteTemplate({
|
|
dataItem: note.dataItem,
|
|
category: note.category,
|
|
value: note.value,
|
|
text: text,
|
|
series: note.series
|
|
});
|
|
} else if (label.format) {
|
|
text = autoFormat(label.format, text);
|
|
}
|
|
note.label = new TextBox(text, deepExtend({}, label));
|
|
if (label.position === INSIDE && !defined(size)) {
|
|
if (icon.type === CIRCLE) {
|
|
size = math.max(note.label.box.width(), note.label.box.height());
|
|
} else {
|
|
width = note.label.box.width();
|
|
height = note.label.box.height();
|
|
}
|
|
box.wrap(note.label.box);
|
|
}
|
|
}
|
|
icon.width = width || size || DEFAULT_ICON_SIZE;
|
|
icon.height = height || size || DEFAULT_ICON_SIZE;
|
|
marker = new ShapeElement(deepExtend({}, icon));
|
|
note.marker = marker;
|
|
note.append(marker);
|
|
if (note.label) {
|
|
note.append(note.label);
|
|
}
|
|
marker.reflow(Box2D());
|
|
note.wrapperBox = box.wrap(marker.box);
|
|
}
|
|
},
|
|
reflow: function (targetBox) {
|
|
var note = this, options = note.options, center = targetBox.center(), wrapperBox = note.wrapperBox, length = options.line.length, position = options.position, label = note.label, marker = note.marker, lineStart, box, contentBox;
|
|
if (options.visible) {
|
|
if (inArray(position, [
|
|
LEFT,
|
|
RIGHT
|
|
])) {
|
|
if (position === LEFT) {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(-length, targetBox.center().y - wrapperBox.center().y);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
targetBox.x1,
|
|
center.y
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
contentBox.x2,
|
|
center.y
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
} else {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(length, targetBox.center().y - wrapperBox.center().y);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
targetBox.x2,
|
|
center.y
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
contentBox.x1,
|
|
center.y
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
}
|
|
} else {
|
|
if (position === BOTTOM) {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(targetBox.center().x - wrapperBox.center().x, length);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
center.x,
|
|
targetBox.y2
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
center.x,
|
|
contentBox.y1
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
} else {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(targetBox.center().x - wrapperBox.center().x, -length);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
center.x,
|
|
targetBox.y1
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
center.x,
|
|
contentBox.y2
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
}
|
|
}
|
|
if (marker) {
|
|
marker.reflow(contentBox);
|
|
}
|
|
if (label) {
|
|
label.reflow(contentBox);
|
|
if (marker) {
|
|
if (options.label.position === OUTSIDE) {
|
|
label.box.alignTo(marker.box, position);
|
|
}
|
|
label.reflow(label.box);
|
|
}
|
|
}
|
|
note.contentBox = contentBox;
|
|
note.targetBox = targetBox;
|
|
note.box = box || contentBox;
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
BoxElement.fn.createVisual.call(this);
|
|
if (this.options.visible) {
|
|
this.createLine();
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var customVisual = options.visual;
|
|
if (options.visible && customVisual) {
|
|
that.visual = customVisual({
|
|
dataItem: that.dataItem,
|
|
category: that.category,
|
|
value: that.value,
|
|
text: that.text,
|
|
sender: that.getChart(),
|
|
series: that.series,
|
|
rect: that.targetBox.toRect(),
|
|
options: {
|
|
background: options.background,
|
|
border: options.background,
|
|
icon: options.icon,
|
|
label: options.label,
|
|
line: options.line,
|
|
position: options.position,
|
|
visible: options.visible
|
|
},
|
|
createVisual: function () {
|
|
that.createVisual();
|
|
that.renderChildren();
|
|
var defaultVisual = that.visual;
|
|
delete that.visual;
|
|
return defaultVisual;
|
|
}
|
|
});
|
|
that.addVisual();
|
|
} else {
|
|
BoxElement.fn.renderVisual.call(that);
|
|
}
|
|
},
|
|
createLine: function () {
|
|
var options = this.options.line;
|
|
if (this.linePoints) {
|
|
var path = draw.Path.fromPoints(this.linePoints, {
|
|
stroke: {
|
|
color: options.color,
|
|
width: options.width,
|
|
dashType: options.dashType
|
|
}
|
|
});
|
|
alignPathToPixel(path);
|
|
this.visual.append(path);
|
|
}
|
|
},
|
|
click: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(NOTE_CLICK, args)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
hover: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(NOTE_HOVER, args)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
leave: function (widget) {
|
|
widget._unsetActivePoint();
|
|
},
|
|
eventArgs: function (e) {
|
|
var note = this, options = note.options;
|
|
return {
|
|
element: $(e.target),
|
|
text: defined(options.label) ? options.label.text : '',
|
|
dataItem: note.dataItem,
|
|
series: note.series,
|
|
value: note.value,
|
|
category: note.category,
|
|
visual: note.visual
|
|
};
|
|
}
|
|
});
|
|
var ShapeElement = BoxElement.extend({
|
|
init: function (options, pointData) {
|
|
this.pointData = pointData;
|
|
BoxElement.fn.init.call(this, options);
|
|
},
|
|
options: {
|
|
type: CIRCLE,
|
|
align: CENTER,
|
|
vAlign: CENTER
|
|
},
|
|
getElement: function () {
|
|
var marker = this, options = marker.options, type = options.type, rotation = options.rotation, box = marker.paddingBox, element, center = box.center(), halfWidth = box.width() / 2;
|
|
if (!options.visible || !marker.hasBox()) {
|
|
return;
|
|
}
|
|
var style = marker.visualStyle();
|
|
if (type === CIRCLE) {
|
|
element = new draw.Circle(new geom.Circle([
|
|
round(box.x1 + halfWidth, COORD_PRECISION),
|
|
round(box.y1 + box.height() / 2, COORD_PRECISION)
|
|
], halfWidth), style);
|
|
} else if (type === TRIANGLE) {
|
|
element = draw.Path.fromPoints([
|
|
[
|
|
box.x1 + halfWidth,
|
|
box.y1
|
|
],
|
|
[
|
|
box.x1,
|
|
box.y2
|
|
],
|
|
[
|
|
box.x2,
|
|
box.y2
|
|
]
|
|
], style).close();
|
|
} else if (type === CROSS) {
|
|
element = new draw.MultiPath(style);
|
|
element.moveTo(box.x1, box.y1).lineTo(box.x2, box.y2);
|
|
element.moveTo(box.x1, box.y2).lineTo(box.x2, box.y1);
|
|
} else {
|
|
element = draw.Path.fromRect(box.toRect(), style);
|
|
}
|
|
if (rotation) {
|
|
element.transform(geom.transform().rotate(-rotation, [
|
|
center.x,
|
|
center.y
|
|
]));
|
|
}
|
|
element.options.zIndex = this.options.zIndex;
|
|
return element;
|
|
},
|
|
createElement: function () {
|
|
var that = this;
|
|
var customVisual = that.options.visual;
|
|
var pointData = that.pointData || {};
|
|
var visual;
|
|
if (customVisual) {
|
|
visual = customVisual({
|
|
value: pointData.value,
|
|
dataItem: pointData.dataItem,
|
|
sender: that.getChart(),
|
|
series: pointData.series,
|
|
category: pointData.category,
|
|
rect: that.paddingBox.toRect(),
|
|
options: that.visualOptions(),
|
|
createVisual: function () {
|
|
return that.getElement();
|
|
}
|
|
});
|
|
} else {
|
|
visual = that.getElement();
|
|
}
|
|
return visual;
|
|
},
|
|
visualOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
background: options.background,
|
|
border: options.border,
|
|
margin: options.margin,
|
|
padding: options.padding,
|
|
type: options.type,
|
|
size: options.width,
|
|
visible: options.visible
|
|
};
|
|
},
|
|
createVisual: function () {
|
|
this.visual = this.createElement();
|
|
}
|
|
});
|
|
var NumericAxis = Axis.extend({
|
|
init: function (seriesMin, seriesMax, options) {
|
|
var axis = this, defaultOptions = axis.initDefaults(seriesMin, seriesMax, options);
|
|
Axis.fn.init.call(axis, defaultOptions);
|
|
},
|
|
startValue: function () {
|
|
return 0;
|
|
},
|
|
options: {
|
|
type: 'numeric',
|
|
min: 0,
|
|
max: 1,
|
|
vertical: true,
|
|
majorGridLines: {
|
|
visible: true,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
labels: { format: '#.####################' },
|
|
zIndex: 1
|
|
},
|
|
initDefaults: function (seriesMin, seriesMax, options) {
|
|
var axis = this, narrowRange = options.narrowRange, autoMin = axis.autoAxisMin(seriesMin, seriesMax, narrowRange), autoMax = axis.autoAxisMax(seriesMin, seriesMax, narrowRange), majorUnit = autoMajorUnit(autoMin, autoMax), autoOptions = { majorUnit: majorUnit }, userSetLimits;
|
|
if (options.roundToMajorUnit !== false) {
|
|
if (autoMin < 0 && remainderClose(autoMin, majorUnit, 1 / 3)) {
|
|
autoMin -= majorUnit;
|
|
}
|
|
if (autoMax > 0 && remainderClose(autoMax, majorUnit, 1 / 3)) {
|
|
autoMax += majorUnit;
|
|
}
|
|
}
|
|
autoOptions.min = floor(autoMin, majorUnit);
|
|
autoOptions.max = ceil(autoMax, majorUnit);
|
|
this.totalMin = defined(options.min) ? math.min(autoOptions.min, options.min) : autoOptions.min;
|
|
this.totalMax = defined(options.max) ? math.max(autoOptions.max, options.max) : autoOptions.max;
|
|
this.totalMajorUnit = majorUnit;
|
|
if (options) {
|
|
userSetLimits = defined(options.min) || defined(options.max);
|
|
if (userSetLimits) {
|
|
if (options.min === options.max) {
|
|
if (options.min > 0) {
|
|
options.min = 0;
|
|
} else {
|
|
options.max = 1;
|
|
}
|
|
}
|
|
}
|
|
if (options.majorUnit) {
|
|
autoOptions.min = floor(autoOptions.min, options.majorUnit);
|
|
autoOptions.max = ceil(autoOptions.max, options.majorUnit);
|
|
} else if (userSetLimits) {
|
|
options = deepExtend(autoOptions, options);
|
|
autoOptions.majorUnit = autoMajorUnit(options.min, options.max);
|
|
}
|
|
}
|
|
autoOptions.minorUnit = (options.majorUnit || autoOptions.majorUnit) / 5;
|
|
return deepExtend(autoOptions, options);
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: options.min,
|
|
max: options.max
|
|
};
|
|
},
|
|
autoAxisMax: function (min, max, narrow) {
|
|
var axisMax, diff;
|
|
if (!min && !max) {
|
|
return 1;
|
|
}
|
|
if (min <= 0 && max <= 0) {
|
|
max = min == max ? 0 : max;
|
|
diff = math.abs((max - min) / max);
|
|
if (narrow === false || !narrow && diff > ZERO_THRESHOLD) {
|
|
return 0;
|
|
}
|
|
axisMax = math.min(0, max - (min - max) / 2);
|
|
} else {
|
|
min = min == max ? 0 : min;
|
|
axisMax = max;
|
|
}
|
|
return axisMax;
|
|
},
|
|
autoAxisMin: function (min, max, narrow) {
|
|
var axisMin, diff;
|
|
if (!min && !max) {
|
|
return 0;
|
|
}
|
|
if (min >= 0 && max >= 0) {
|
|
min = min == max ? 0 : min;
|
|
diff = (max - min) / max;
|
|
if (narrow === false || !narrow && diff > ZERO_THRESHOLD) {
|
|
return 0;
|
|
}
|
|
axisMin = math.max(0, min - (max - min) / 2);
|
|
} else {
|
|
max = min == max ? 0 : max;
|
|
axisMin = min;
|
|
}
|
|
return axisMin;
|
|
},
|
|
getDivisions: function (stepValue) {
|
|
if (stepValue === 0) {
|
|
return 1;
|
|
}
|
|
var options = this.options, range = options.max - options.min;
|
|
return math.floor(round(range / stepValue, COORD_PRECISION)) + 1;
|
|
},
|
|
getTickPositions: function (unit, skipUnit) {
|
|
var axis = this, options = axis.options, vertical = options.vertical, reverse = options.reverse, lineBox = axis.lineBox(), lineSize = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = lineSize / range, step = unit * scale, skipStep = 0, divisions = axis.getDivisions(unit), dir = (vertical ? -1 : 1) * (reverse ? -1 : 1), startEdge = dir === 1 ? 1 : 2, pos = lineBox[(vertical ? Y : X) + startEdge], positions = [], i;
|
|
if (skipUnit) {
|
|
skipStep = skipUnit / unit;
|
|
}
|
|
for (i = 0; i < divisions; i++) {
|
|
if (i % skipStep !== 0) {
|
|
positions.push(round(pos, COORD_PRECISION));
|
|
}
|
|
pos = pos + step * dir;
|
|
}
|
|
return positions;
|
|
},
|
|
getMajorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.majorUnit);
|
|
},
|
|
getMinorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.minorUnit);
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, step = dir * (lineSize / (options.max - options.min)), p1, p2, slotBox = new Box2D(lineBox.x1, lineBox.y1, lineBox.x1, lineBox.y1);
|
|
if (!defined(a)) {
|
|
a = b || 0;
|
|
}
|
|
if (!defined(b)) {
|
|
b = a || 0;
|
|
}
|
|
if (limit) {
|
|
a = math.max(math.min(a, options.max), options.min);
|
|
b = math.max(math.min(b, options.max), options.min);
|
|
}
|
|
if (vertical) {
|
|
p1 = options.max - math.max(a, b);
|
|
p2 = options.max - math.min(a, b);
|
|
} else {
|
|
p1 = math.min(a, b) - options.min;
|
|
p2 = math.max(a, b) - options.min;
|
|
}
|
|
slotBox[valueAxis + 1] = math.max(math.min(lineStart + step * (reverse ? p2 : p1), COORDINATE_LIMIT), -COORDINATE_LIMIT);
|
|
slotBox[valueAxis + 2] = math.max(math.min(lineStart + step * (reverse ? p1 : p2), COORDINATE_LIMIT), -COORDINATE_LIMIT);
|
|
return slotBox;
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, max = options.max * 1, min = options.min * 1, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, offset = dir * (point[valueAxis] - lineStart), step = (max - min) / lineSize, valueOffset = offset * step, value;
|
|
if (offset < 0 || offset > lineSize) {
|
|
return null;
|
|
}
|
|
value = vertical ? max - valueOffset : min + valueOffset;
|
|
return round(value, DEFAULT_PRECISION);
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), vertical = options.vertical, reverse = options.reverse, size = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = size / range, offset = round(delta / scale, DEFAULT_PRECISION);
|
|
if ((vertical || reverse) && !(vertical && reverse)) {
|
|
offset = -offset;
|
|
}
|
|
return {
|
|
min: options.min + offset,
|
|
max: options.max + offset
|
|
};
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, options = axis.options, offset = -delta * options.majorUnit;
|
|
return {
|
|
min: options.min - offset,
|
|
max: options.max + offset
|
|
};
|
|
},
|
|
labelsCount: function () {
|
|
return this.getDivisions(this.options.majorUnit);
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var axis = this, options = axis.options, value = round(options.min + index * options.majorUnit, DEFAULT_PRECISION), text = axis.axisLabelText(value, null, labelOptions);
|
|
return new AxisLabel(value, text, index, null, labelOptions);
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var range = this.range();
|
|
return range.min <= value && value <= range.max;
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.translateRange(delta);
|
|
return this.limitRange(range.min, range.max, this.totalMin, this.totalMax);
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var startValue = this.getValue(start);
|
|
var endValue = this.getValue(end);
|
|
var min = math.min(startValue, endValue);
|
|
var max = math.max(startValue, endValue);
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var newRange = this.scaleRange(delta);
|
|
var totalMax = this.totalMax;
|
|
var totalMin = this.totalMin;
|
|
var min = util.limitValue(newRange.min, totalMin, totalMax);
|
|
var max = util.limitValue(newRange.max, totalMin, totalMax);
|
|
var optionsRange = this.options.max - this.options.min;
|
|
if (optionsRange < this.totalMajorUnit || max - min >= this.totalMajorUnit) {
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
}
|
|
});
|
|
var LogarithmicAxis = Axis.extend({
|
|
init: function (seriesMin, seriesMax, options) {
|
|
this.options = this._initOptions(seriesMin, seriesMax, options);
|
|
Axis.fn.init.call(this, options);
|
|
},
|
|
startValue: function () {
|
|
return this.options.min;
|
|
},
|
|
options: {
|
|
type: 'log',
|
|
majorUnit: 10,
|
|
minorUnit: 1,
|
|
axisCrossingValue: 1,
|
|
vertical: true,
|
|
majorGridLines: {
|
|
visible: true,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
zIndex: 1
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, step = dir * (lineSize / (logMax - logMin)), p1, p2, slotBox = new Box2D(lineBox.x1, lineBox.y1, lineBox.x1, lineBox.y1);
|
|
if (!defined(a)) {
|
|
a = b || 1;
|
|
}
|
|
if (!defined(b)) {
|
|
b = a || 1;
|
|
}
|
|
if (a <= 0 || b <= 0) {
|
|
return;
|
|
}
|
|
if (limit) {
|
|
a = math.max(math.min(a, options.max), options.min);
|
|
b = math.max(math.min(b, options.max), options.min);
|
|
}
|
|
a = log(a, base);
|
|
b = log(b, base);
|
|
if (vertical) {
|
|
p1 = logMax - math.max(a, b);
|
|
p2 = logMax - math.min(a, b);
|
|
} else {
|
|
p1 = math.min(a, b) - logMin;
|
|
p2 = math.max(a, b) - logMin;
|
|
}
|
|
slotBox[valueAxis + 1] = lineStart + step * (reverse ? p2 : p1);
|
|
slotBox[valueAxis + 2] = lineStart + step * (reverse ? p1 : p2);
|
|
return slotBox;
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, lineBox = axis.lineBox(), base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, dir = vertical === reverse ? 1 : -1, startEdge = dir === 1 ? 1 : 2, lineSize = vertical ? lineBox.height() : lineBox.width(), step = (logMax - logMin) / lineSize, valueAxis = vertical ? Y : X, lineStart = lineBox[valueAxis + startEdge], offset = dir * (point[valueAxis] - lineStart), valueOffset = offset * step, value;
|
|
if (offset < 0 || offset > lineSize) {
|
|
return null;
|
|
}
|
|
value = logMin + valueOffset;
|
|
return round(math.pow(base, value), DEFAULT_PRECISION);
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: options.min,
|
|
max: options.max
|
|
};
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, options = axis.options, base = options.majorUnit, offset = -delta;
|
|
return {
|
|
min: math.pow(base, axis.logMin - offset),
|
|
max: math.pow(base, axis.logMax + offset)
|
|
};
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, base = options.majorUnit, lineBox = axis.lineBox(), vertical = options.vertical, reverse = options.reverse, size = vertical ? lineBox.height() : lineBox.width(), scale = size / (axis.logMax - axis.logMin), offset = round(delta / scale, DEFAULT_PRECISION);
|
|
if ((vertical || reverse) && !(vertical && reverse)) {
|
|
offset = -offset;
|
|
}
|
|
return {
|
|
min: math.pow(base, axis.logMin + offset),
|
|
max: math.pow(base, axis.logMax + offset)
|
|
};
|
|
},
|
|
labelsCount: function () {
|
|
var axis = this, floorMax = math.floor(axis.logMax), count = math.floor(floorMax - axis.logMin) + 1;
|
|
return count;
|
|
},
|
|
getMajorTickPositions: function () {
|
|
var axis = this, ticks = [];
|
|
axis.traverseMajorTicksPositions(function (position) {
|
|
ticks.push(position);
|
|
}, {
|
|
step: 1,
|
|
skip: 0
|
|
});
|
|
return ticks;
|
|
},
|
|
createTicks: function (lineGroup) {
|
|
var axis = this, ticks = [], options = axis.options, lineBox = axis.lineBox(), mirror = options.labels.mirror, majorTicks = options.majorTicks, minorTicks = options.minorTicks, tickLineOptions = { vertical: options.vertical };
|
|
function render(tickPosition, tickOptions) {
|
|
tickLineOptions.tickX = mirror ? lineBox.x2 : lineBox.x2 - tickOptions.size;
|
|
tickLineOptions.tickY = mirror ? lineBox.y1 - tickOptions.size : lineBox.y1;
|
|
tickLineOptions.position = tickPosition;
|
|
lineGroup.append(createAxisTick(tickLineOptions, tickOptions));
|
|
}
|
|
if (majorTicks.visible) {
|
|
axis.traverseMajorTicksPositions(render, majorTicks);
|
|
}
|
|
if (minorTicks.visible) {
|
|
axis.traverseMinorTicksPositions(render, minorTicks);
|
|
}
|
|
return ticks;
|
|
},
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, majorGridLines = options.majorGridLines, minorGridLines = options.minorGridLines, vertical = options.vertical, lineBox = altAxis.lineBox(), lineOptions = {
|
|
lineStart: lineBox[vertical ? 'x1' : 'y1'],
|
|
lineEnd: lineBox[vertical ? 'x2' : 'y2'],
|
|
vertical: vertical
|
|
}, majorTicks = [];
|
|
var container = this.gridLinesVisual();
|
|
function render(tickPosition, gridLine) {
|
|
if (!inArray(tickPosition, majorTicks)) {
|
|
lineOptions.position = tickPosition;
|
|
container.append(createAxisGridLine(lineOptions, gridLine));
|
|
majorTicks.push(tickPosition);
|
|
}
|
|
}
|
|
if (majorGridLines.visible) {
|
|
axis.traverseMajorTicksPositions(render, majorGridLines);
|
|
}
|
|
if (minorGridLines.visible) {
|
|
axis.traverseMinorTicksPositions(render, minorGridLines);
|
|
}
|
|
return container.children;
|
|
},
|
|
traverseMajorTicksPositions: function (callback, tickOptions) {
|
|
var axis = this, lineOptions = axis._lineOptions(), lineStart = lineOptions.lineStart, step = lineOptions.step, logMin = axis.logMin, logMax = axis.logMax, power, position;
|
|
for (power = math.ceil(logMin) + tickOptions.skip; power <= logMax; power += tickOptions.step) {
|
|
position = round(lineStart + step * (power - logMin), DEFAULT_PRECISION);
|
|
callback(position, tickOptions);
|
|
}
|
|
},
|
|
traverseMinorTicksPositions: function (callback, tickOptions) {
|
|
var axis = this, options = axis.options, lineOptions = axis._lineOptions(), lineStart = lineOptions.lineStart, lineStep = lineOptions.step, base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, start = math.floor(logMin), max = options.max, min = options.min, minorUnit = options.minorUnit, power, value, position, minorOptions;
|
|
for (power = start; power < logMax; power++) {
|
|
minorOptions = axis._minorIntervalOptions(power);
|
|
for (var idx = tickOptions.skip; idx < minorUnit; idx += tickOptions.step) {
|
|
value = minorOptions.value + idx * minorOptions.minorStep;
|
|
if (value > max) {
|
|
break;
|
|
}
|
|
if (value >= min) {
|
|
position = round(lineStart + lineStep * (log(value, base) - logMin), DEFAULT_PRECISION);
|
|
callback(position, tickOptions);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var axis = this, options = axis.options, power = math.ceil(axis.logMin + index), value = Math.pow(options.majorUnit, power), text = axis.axisLabelText(value, null, labelOptions);
|
|
return new AxisLabel(value, text, index, null, labelOptions);
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var range = this.range();
|
|
return range.min <= value && value <= range.max;
|
|
},
|
|
_throwNegativeValuesError: function () {
|
|
throw new Error('Non positive values cannot be used for a logarithmic axis');
|
|
},
|
|
_initOptions: function (seriesMin, seriesMax, options) {
|
|
var axis = this, axisOptions = deepExtend({}, axis.options, {
|
|
min: seriesMin,
|
|
max: seriesMax
|
|
}, options), min = axisOptions.min, max = axisOptions.max, base = axisOptions.majorUnit, autoMax = this._autoMax(seriesMax, base), autoMin = this._autoMin(seriesMin, seriesMax, axisOptions);
|
|
if (axisOptions.axisCrossingValue <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
if (!defined(options.max)) {
|
|
max = autoMax;
|
|
} else if (options.max <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
if (!defined(options.min)) {
|
|
min = autoMin;
|
|
} else if (options.min <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
this.totalMin = defined(options.min) ? math.min(autoMin, options.min) : autoMin;
|
|
this.totalMax = defined(options.max) ? math.max(autoMax, options.max) : autoMax;
|
|
axis.logMin = round(log(min, base), DEFAULT_PRECISION);
|
|
axis.logMax = round(log(max, base), DEFAULT_PRECISION);
|
|
axisOptions.max = max;
|
|
axisOptions.min = min;
|
|
axisOptions.minorUnit = options.minorUnit || round(base - 1, DEFAULT_PRECISION);
|
|
return axisOptions;
|
|
},
|
|
_autoMin: function (min, max, options) {
|
|
var autoMin = min;
|
|
var base = options.majorUnit;
|
|
if (min <= 0) {
|
|
autoMin = max <= 1 ? math.pow(base, -2) : 1;
|
|
} else if (!options.narrowRange) {
|
|
autoMin = math.pow(base, math.floor(log(min, base)));
|
|
}
|
|
return autoMin;
|
|
},
|
|
_autoMax: function (max, base) {
|
|
var logMaxRemainder = round(log(max, base), DEFAULT_PRECISION) % 1;
|
|
var autoMax;
|
|
if (max <= 0) {
|
|
autoMax = base;
|
|
} else if (logMaxRemainder !== 0 && (logMaxRemainder < 0.3 || logMaxRemainder > 0.9)) {
|
|
autoMax = math.pow(base, log(max, base) + 0.2);
|
|
} else {
|
|
autoMax = math.pow(base, math.ceil(log(max, base)));
|
|
}
|
|
return autoMax;
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.translateRange(delta);
|
|
return this.limitRange(range.min, range.max, this.totalMin, this.totalMax, -delta);
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var startValue = this.getValue(start);
|
|
var endValue = this.getValue(end);
|
|
var min = math.min(startValue, endValue);
|
|
var max = math.max(startValue, endValue);
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var options = this.options;
|
|
var newRange = this.scaleRange(delta);
|
|
var totalMax = this.totalMax;
|
|
var totalMin = this.totalMin;
|
|
var min = util.limitValue(newRange.min, totalMin, totalMax);
|
|
var max = util.limitValue(newRange.max, totalMin, totalMax);
|
|
var base = options.majorUnit;
|
|
var acceptOptionsRange = max > min && options.min && options.max && round(log(options.max, base) - log(options.min, base), DEFAULT_PRECISION) < 1;
|
|
var acceptNewRange = !(options.min === totalMin && options.max === totalMax) && round(log(max, base) - log(min, base), DEFAULT_PRECISION) >= 1;
|
|
if (acceptOptionsRange || acceptNewRange) {
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
},
|
|
_minorIntervalOptions: function (power) {
|
|
var base = this.options.majorUnit, value = math.pow(base, power), nextValue = math.pow(base, power + 1), difference = nextValue - value, minorStep = difference / this.options.minorUnit;
|
|
return {
|
|
value: value,
|
|
minorStep: minorStep
|
|
};
|
|
},
|
|
_lineOptions: function () {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), dir = vertical === reverse ? 1 : -1, startEdge = dir === 1 ? 1 : 2, lineSize = vertical ? lineBox.height() : lineBox.width(), step = dir * (lineSize / (axis.logMax - axis.logMin)), lineStart = lineBox[valueAxis + startEdge];
|
|
return {
|
|
step: step,
|
|
lineStart: lineStart,
|
|
lineBox: lineBox
|
|
};
|
|
}
|
|
});
|
|
dataviz.Gradients = {
|
|
glass: {
|
|
type: LINEAR,
|
|
rotation: 0,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.25,
|
|
color: WHITE,
|
|
opacity: 0.3
|
|
},
|
|
{
|
|
offset: 1,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
sharpBevel: {
|
|
type: RADIAL,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0.55
|
|
},
|
|
{
|
|
offset: 0.65,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.95,
|
|
color: WHITE,
|
|
opacity: 0.25
|
|
}
|
|
]
|
|
},
|
|
roundedBevel: {
|
|
type: RADIAL,
|
|
stops: [
|
|
{
|
|
offset: 0.33,
|
|
color: WHITE,
|
|
opacity: 0.06
|
|
},
|
|
{
|
|
offset: 0.83,
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
{
|
|
offset: 0.95,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
roundedGlass: {
|
|
type: RADIAL,
|
|
supportVML: false,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.5,
|
|
color: WHITE,
|
|
opacity: 0.3
|
|
},
|
|
{
|
|
offset: 0.99,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
sharpGlass: {
|
|
type: RADIAL,
|
|
supportVML: false,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
{
|
|
offset: 0.15,
|
|
color: WHITE,
|
|
opacity: 0.15
|
|
},
|
|
{
|
|
offset: 0.17,
|
|
color: WHITE,
|
|
opacity: 0.35
|
|
},
|
|
{
|
|
offset: 0.85,
|
|
color: WHITE,
|
|
opacity: 0.05
|
|
},
|
|
{
|
|
offset: 0.87,
|
|
color: WHITE,
|
|
opacity: 0.15
|
|
},
|
|
{
|
|
offset: 0.99,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
}
|
|
};
|
|
var ExportMixin = {
|
|
extend: function (proto, skipLegacy) {
|
|
if (!proto.exportVisual) {
|
|
throw new Error('Mixin target has no exportVisual method defined.');
|
|
}
|
|
proto.exportSVG = this.exportSVG;
|
|
proto.exportImage = this.exportImage;
|
|
proto.exportPDF = this.exportPDF;
|
|
if (!skipLegacy) {
|
|
proto.svg = this.svg;
|
|
proto.imageDataURL = this.imageDataURL;
|
|
}
|
|
},
|
|
exportSVG: function (options) {
|
|
return draw.exportSVG(this.exportVisual(), options);
|
|
},
|
|
exportImage: function (options) {
|
|
return draw.exportImage(this.exportVisual(options), options);
|
|
},
|
|
exportPDF: function (options) {
|
|
return draw.exportPDF(this.exportVisual(), options);
|
|
},
|
|
svg: function () {
|
|
if (draw.svg.Surface) {
|
|
return draw.svg._exportGroup(this.exportVisual());
|
|
} else {
|
|
throw new Error('SVG Export failed. Unable to export instantiate kendo.drawing.svg.Surface');
|
|
}
|
|
},
|
|
imageDataURL: function () {
|
|
if (!kendo.support.canvas) {
|
|
return null;
|
|
}
|
|
if (draw.canvas.Surface) {
|
|
var container = $('<div />').css({
|
|
display: 'none',
|
|
width: this.element.width(),
|
|
height: this.element.height()
|
|
}).appendTo(document.body);
|
|
var surface = new draw.canvas.Surface(container);
|
|
surface.draw(this.exportVisual());
|
|
var image = surface._rootElement.toDataURL();
|
|
surface.destroy();
|
|
container.remove();
|
|
return image;
|
|
} else {
|
|
throw new Error('Image Export failed. Unable to export instantiate kendo.drawing.canvas.Surface');
|
|
}
|
|
}
|
|
};
|
|
function autoMajorUnit(min, max) {
|
|
var diff = round(max - min, DEFAULT_PRECISION - 1);
|
|
if (diff === 0) {
|
|
if (max === 0) {
|
|
return 0.1;
|
|
}
|
|
diff = math.abs(max);
|
|
}
|
|
var scale = math.pow(10, math.floor(math.log(diff) / math.log(10))), relativeValue = round(diff / scale, DEFAULT_PRECISION), scaleMultiplier = 1;
|
|
if (relativeValue < 1.904762) {
|
|
scaleMultiplier = 0.2;
|
|
} else if (relativeValue < 4.761904) {
|
|
scaleMultiplier = 0.5;
|
|
} else if (relativeValue < 9.523809) {
|
|
scaleMultiplier = 1;
|
|
} else {
|
|
scaleMultiplier = 2;
|
|
}
|
|
return round(scale * scaleMultiplier, DEFAULT_PRECISION);
|
|
}
|
|
function rotatePoint(x, y, cx, cy, angle) {
|
|
var theta = angle * DEG_TO_RAD;
|
|
return new Point2D(cx + (x - cx) * math.cos(theta) + (y - cy) * math.sin(theta), cy - (x - cx) * math.sin(theta) + (y - cy) * math.cos(theta));
|
|
}
|
|
function boxDiff(r, s) {
|
|
if (r.x1 == s.x1 && r.y1 == s.y1 && r.x2 == s.x2 && r.y2 == s.y2) {
|
|
return s;
|
|
}
|
|
var a = math.min(r.x1, s.x1), b = math.max(r.x1, s.x1), c = math.min(r.x2, s.x2), d = math.max(r.x2, s.x2), e = math.min(r.y1, s.y1), f = math.max(r.y1, s.y1), g = math.min(r.y2, s.y2), h = math.max(r.y2, s.y2), result = [];
|
|
result[0] = Box2D(b, e, c, f);
|
|
result[1] = Box2D(a, f, b, g);
|
|
result[2] = Box2D(c, f, d, g);
|
|
result[3] = Box2D(b, g, c, h);
|
|
if (r.x1 == a && r.y1 == e || s.x1 == a && s.y1 == e) {
|
|
result[4] = Box2D(a, e, b, f);
|
|
result[5] = Box2D(c, g, d, h);
|
|
} else {
|
|
result[4] = Box2D(c, e, d, f);
|
|
result[5] = Box2D(a, g, b, h);
|
|
}
|
|
return $.grep(result, function (box) {
|
|
return box.height() > 0 && box.width() > 0;
|
|
})[0];
|
|
}
|
|
function inArray(value, array) {
|
|
return indexOf(value, array) != -1;
|
|
}
|
|
function ceil(value, step) {
|
|
return round(math.ceil(value / step) * step, DEFAULT_PRECISION);
|
|
}
|
|
function floor(value, step) {
|
|
return round(math.floor(value / step) * step, DEFAULT_PRECISION);
|
|
}
|
|
function round(value, precision) {
|
|
var power = math.pow(10, precision || 0);
|
|
return math.round(value * power) / power;
|
|
}
|
|
function log(y, x) {
|
|
return math.log(y) / math.log(x);
|
|
}
|
|
function remainderClose(value, divisor, ratio) {
|
|
var remainder = round(math.abs(value % divisor), DEFAULT_PRECISION), threshold = divisor * (1 - ratio);
|
|
return remainder === 0 || remainder > threshold;
|
|
}
|
|
function interpolateValue(start, end, progress) {
|
|
return round(start + (end - start) * progress, COORD_PRECISION);
|
|
}
|
|
function numericComparer(a, b) {
|
|
return a - b;
|
|
}
|
|
function autoFormat(format, value) {
|
|
if (format.match(FORMAT_REGEX)) {
|
|
return kendo.format.apply(this, arguments);
|
|
}
|
|
return kendo.toString(value, format);
|
|
}
|
|
function clockwise(v1, v2) {
|
|
return -v1.x * v2.y + v1.y * v2.x < 0;
|
|
}
|
|
function dateComparer(a, b) {
|
|
if (a && b) {
|
|
return a.getTime() - b.getTime();
|
|
}
|
|
return -1;
|
|
}
|
|
var CurveProcessor = function (closed) {
|
|
this.closed = closed;
|
|
};
|
|
CurveProcessor.prototype = CurveProcessor.fn = {
|
|
WEIGHT: 0.333,
|
|
EXTREMUM_ALLOWED_DEVIATION: 0.01,
|
|
process: function (dataPoints) {
|
|
var that = this, closed = that.closed, points = dataPoints.slice(0), length = points.length, segments = [], p0, p1, p2, controlPoints, initialControlPoint, lastControlPoint, tangent;
|
|
if (length > 2) {
|
|
that.removeDuplicates(0, points);
|
|
length = points.length;
|
|
}
|
|
if (length < 2 || length == 2 && points[0].equals(points[1])) {
|
|
return segments;
|
|
}
|
|
p0 = points[0];
|
|
p1 = points[1];
|
|
p2 = points[2];
|
|
segments.push(new draw.Segment(p0));
|
|
while (p0.equals(points[length - 1])) {
|
|
closed = true;
|
|
points.pop();
|
|
length--;
|
|
}
|
|
if (length == 2) {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
last(segments).controlOut(that.firstControlPoint(tangent, p0, p1, X, Y));
|
|
segments.push(new draw.Segment(p1, that.secondControlPoint(tangent, p0, p1, X, Y)));
|
|
return segments;
|
|
}
|
|
if (closed) {
|
|
p0 = points[length - 1];
|
|
p1 = points[0];
|
|
p2 = points[1];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
initialControlPoint = controlPoints[1];
|
|
lastControlPoint = controlPoints[0];
|
|
} else {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
initialControlPoint = that.firstControlPoint(tangent, p0, p1, X, Y);
|
|
}
|
|
var cp0 = initialControlPoint;
|
|
for (var idx = 0; idx <= length - 3; idx++) {
|
|
that.removeDuplicates(idx, points);
|
|
length = points.length;
|
|
if (idx + 3 <= length) {
|
|
p0 = points[idx];
|
|
p1 = points[idx + 1];
|
|
p2 = points[idx + 2];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
last(segments).controlOut(cp0);
|
|
cp0 = controlPoints[1];
|
|
var cp1 = controlPoints[0];
|
|
segments.push(new draw.Segment(p1, cp1));
|
|
}
|
|
}
|
|
if (closed) {
|
|
p0 = points[length - 2];
|
|
p1 = points[length - 1];
|
|
p2 = points[0];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
last(segments).controlOut(cp0);
|
|
segments.push(new draw.Segment(p1, controlPoints[0]));
|
|
last(segments).controlOut(controlPoints[1]);
|
|
segments.push(new draw.Segment(p2, lastControlPoint));
|
|
} else {
|
|
tangent = that.tangent(p1, p2, X, Y);
|
|
last(segments).controlOut(cp0);
|
|
segments.push(new draw.Segment(p2, that.secondControlPoint(tangent, p1, p2, X, Y)));
|
|
}
|
|
return segments;
|
|
},
|
|
removeDuplicates: function (idx, points) {
|
|
while (points[idx].equals(points[idx + 1]) || points[idx + 1].equals(points[idx + 2])) {
|
|
points.splice(idx + 1, 1);
|
|
}
|
|
},
|
|
invertAxis: function (p0, p1, p2) {
|
|
var that = this, fn, y2, invertAxis = false;
|
|
if (p0.x === p1.x) {
|
|
invertAxis = true;
|
|
} else if (p1.x === p2.x) {
|
|
if (p1.y < p2.y && p0.y <= p1.y || p2.y < p1.y && p1.y <= p0.y) {
|
|
invertAxis = true;
|
|
}
|
|
} else {
|
|
fn = that.lineFunction(p0, p1);
|
|
y2 = that.calculateFunction(fn, p2.x);
|
|
if (!(p0.y <= p1.y && p2.y <= y2) && !(p1.y <= p0.y && p2.y >= y2)) {
|
|
invertAxis = true;
|
|
}
|
|
}
|
|
return invertAxis;
|
|
},
|
|
isLine: function (p0, p1, p2) {
|
|
var that = this, fn = that.lineFunction(p0, p1), y2 = that.calculateFunction(fn, p2.x);
|
|
return p0.x == p1.x && p1.x == p2.x || round(y2, 1) === round(p2.y, 1);
|
|
},
|
|
lineFunction: function (p1, p2) {
|
|
var a = (p2.y - p1.y) / (p2.x - p1.x), b = p1.y - a * p1.x;
|
|
return [
|
|
b,
|
|
a
|
|
];
|
|
},
|
|
controlPoints: function (p0, p1, p2) {
|
|
var that = this, xField = X, yField = Y, restrict = false, switchOrientation = false, tangent, monotonic, firstControlPoint, secondControlPoint, allowedDeviation = that.EXTREMUM_ALLOWED_DEVIATION;
|
|
if (that.isLine(p0, p1, p2)) {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
} else {
|
|
monotonic = {
|
|
x: that.isMonotonicByField(p0, p1, p2, X),
|
|
y: that.isMonotonicByField(p0, p1, p2, Y)
|
|
};
|
|
if (monotonic.x && monotonic.y) {
|
|
tangent = that.tangent(p0, p2, X, Y);
|
|
restrict = true;
|
|
} else {
|
|
if (that.invertAxis(p0, p1, p2)) {
|
|
xField = Y;
|
|
yField = X;
|
|
}
|
|
if (monotonic[xField]) {
|
|
tangent = 0;
|
|
} else {
|
|
var sign;
|
|
if (p2[yField] < p0[yField] && p0[yField] <= p1[yField] || p0[yField] < p2[yField] && p1[yField] <= p0[yField]) {
|
|
sign = that.sign((p2[yField] - p0[yField]) * (p1[xField] - p0[xField]));
|
|
} else {
|
|
sign = -that.sign((p2[xField] - p0[xField]) * (p1[yField] - p0[yField]));
|
|
}
|
|
tangent = allowedDeviation * sign;
|
|
switchOrientation = true;
|
|
}
|
|
}
|
|
}
|
|
secondControlPoint = that.secondControlPoint(tangent, p0, p1, xField, yField);
|
|
if (switchOrientation) {
|
|
var oldXField = xField;
|
|
xField = yField;
|
|
yField = oldXField;
|
|
}
|
|
firstControlPoint = that.firstControlPoint(tangent, p1, p2, xField, yField);
|
|
if (restrict) {
|
|
that.restrictControlPoint(p0, p1, secondControlPoint, tangent);
|
|
that.restrictControlPoint(p1, p2, firstControlPoint, tangent);
|
|
}
|
|
return [
|
|
secondControlPoint,
|
|
firstControlPoint
|
|
];
|
|
},
|
|
sign: function (x) {
|
|
return x <= 0 ? -1 : 1;
|
|
},
|
|
restrictControlPoint: function (p1, p2, cp, tangent) {
|
|
if (p1.y < p2.y) {
|
|
if (p2.y < cp.y) {
|
|
cp.x = p1.x + (p2.y - p1.y) / tangent;
|
|
cp.y = p2.y;
|
|
} else if (cp.y < p1.y) {
|
|
cp.x = p2.x - (p2.y - p1.y) / tangent;
|
|
cp.y = p1.y;
|
|
}
|
|
} else {
|
|
if (cp.y < p2.y) {
|
|
cp.x = p1.x - (p1.y - p2.y) / tangent;
|
|
cp.y = p2.y;
|
|
} else if (p1.y < cp.y) {
|
|
cp.x = p2.x + (p1.y - p2.y) / tangent;
|
|
cp.y = p1.y;
|
|
}
|
|
}
|
|
},
|
|
tangent: function (p0, p1, xField, yField) {
|
|
var tangent, x = p1[xField] - p0[xField], y = p1[yField] - p0[yField];
|
|
if (x === 0) {
|
|
tangent = 0;
|
|
} else {
|
|
tangent = y / x;
|
|
}
|
|
return tangent;
|
|
},
|
|
isMonotonicByField: function (p0, p1, p2, field) {
|
|
return p2[field] > p1[field] && p1[field] > p0[field] || p2[field] < p1[field] && p1[field] < p0[field];
|
|
},
|
|
firstControlPoint: function (tangent, p0, p3, xField, yField) {
|
|
var that = this, t1 = p0[xField], t2 = p3[xField], distance = (t2 - t1) * that.WEIGHT;
|
|
return that.point(t1 + distance, p0[yField] + distance * tangent, xField, yField);
|
|
},
|
|
secondControlPoint: function (tangent, p0, p3, xField, yField) {
|
|
var that = this, t1 = p0[xField], t2 = p3[xField], distance = (t2 - t1) * that.WEIGHT;
|
|
return that.point(t2 - distance, p3[yField] - distance * tangent, xField, yField);
|
|
},
|
|
point: function (xValue, yValue, xField, yField) {
|
|
var controlPoint = new geom.Point();
|
|
controlPoint[xField] = xValue;
|
|
controlPoint[yField] = yValue;
|
|
return controlPoint;
|
|
},
|
|
calculateFunction: function (fn, x) {
|
|
var result = 0, length = fn.length;
|
|
for (var i = 0; i < length; i++) {
|
|
result += Math.pow(x, i) * fn[i];
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
function mwDelta(e) {
|
|
var origEvent = e.originalEvent, delta = 0;
|
|
if (origEvent.wheelDelta) {
|
|
delta = -origEvent.wheelDelta / 120;
|
|
delta = delta > 0 ? math.ceil(delta) : math.floor(delta);
|
|
}
|
|
if (origEvent.detail) {
|
|
delta = round(origEvent.detail / 3);
|
|
}
|
|
return delta;
|
|
}
|
|
function decodeEntities(text) {
|
|
if (!text || !text.indexOf || text.indexOf('&') < 0) {
|
|
return text;
|
|
} else {
|
|
var element = decodeEntities._element;
|
|
element.innerHTML = text;
|
|
return element.textContent || element.innerText;
|
|
}
|
|
}
|
|
function alignPathToPixel(path) {
|
|
if (!kendo.support.vml) {
|
|
var offset = 0.5;
|
|
if (path.options.stroke && defined(path.options.stroke.width)) {
|
|
if (path.options.stroke.width % 2 === 0) {
|
|
offset = 0;
|
|
}
|
|
}
|
|
for (var i = 0; i < path.segments.length; i++) {
|
|
path.segments[i].anchor().round(0).translate(offset, offset);
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
function innerRadialStops(options) {
|
|
var stops = options.stops, usedSpace = options.innerRadius / options.radius * 100, i, length = stops.length, currentStop, currentStops = [];
|
|
for (i = 0; i < length; i++) {
|
|
currentStop = deepExtend({}, stops[i]);
|
|
currentStop.offset = (currentStop.offset * (100 - usedSpace) + usedSpace) / 100;
|
|
currentStops.push(currentStop);
|
|
}
|
|
return currentStops;
|
|
}
|
|
function rectToBox(rect) {
|
|
var origin = rect.origin;
|
|
var bottomRight = rect.bottomRight();
|
|
return new Box2D(origin.x, origin.y, bottomRight.x, bottomRight.y);
|
|
}
|
|
decodeEntities._element = document.createElement('span');
|
|
deepExtend(kendo.dataviz, {
|
|
AXIS_LABEL_CLICK: AXIS_LABEL_CLICK,
|
|
COORD_PRECISION: COORD_PRECISION,
|
|
DEFAULT_PRECISION: DEFAULT_PRECISION,
|
|
DEFAULT_WIDTH: DEFAULT_WIDTH,
|
|
DEFAULT_HEIGHT: DEFAULT_HEIGHT,
|
|
DEFAULT_FONT: DEFAULT_FONT,
|
|
INITIAL_ANIMATION_DURATION: INITIAL_ANIMATION_DURATION,
|
|
NOTE_CLICK: NOTE_CLICK,
|
|
NOTE_HOVER: NOTE_HOVER,
|
|
CLIP: CLIP,
|
|
Axis: Axis,
|
|
AxisLabel: AxisLabel,
|
|
Box2D: Box2D,
|
|
BoxElement: BoxElement,
|
|
ChartElement: ChartElement,
|
|
CurveProcessor: CurveProcessor,
|
|
ExportMixin: ExportMixin,
|
|
FloatElement: FloatElement,
|
|
LogarithmicAxis: LogarithmicAxis,
|
|
Note: Note,
|
|
NumericAxis: NumericAxis,
|
|
Point2D: Point2D,
|
|
Ring: Ring,
|
|
RootElement: RootElement,
|
|
Sector: Sector,
|
|
ShapeBuilder: ShapeBuilder,
|
|
ShapeElement: ShapeElement,
|
|
Text: Text,
|
|
TextBox: TextBox,
|
|
Title: Title,
|
|
alignPathToPixel: alignPathToPixel,
|
|
autoFormat: autoFormat,
|
|
autoMajorUnit: autoMajorUnit,
|
|
boxDiff: boxDiff,
|
|
dateComparer: dateComparer,
|
|
decodeEntities: decodeEntities,
|
|
getSpacing: getSpacing,
|
|
inArray: inArray,
|
|
interpolateValue: interpolateValue,
|
|
mwDelta: mwDelta,
|
|
rectToBox: rectToBox,
|
|
rotatePoint: rotatePoint,
|
|
round: round,
|
|
ceil: ceil,
|
|
floor: floor
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.themes', ['kendo.dataviz.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.themes',
|
|
name: 'Themes',
|
|
description: 'Built-in themes for the DataViz widgets',
|
|
category: 'dataviz',
|
|
depends: ['dataviz.core'],
|
|
hidden: true
|
|
};
|
|
(function ($) {
|
|
var kendo = window.kendo, ui = kendo.dataviz.ui, deepExtend = kendo.deepExtend;
|
|
var BAR_GAP = 1.5, BAR_SPACING = 0.4, BLACK = '#000', SANS = 'Arial,Helvetica,sans-serif', SANS11 = '11px ' + SANS, SANS12 = '12px ' + SANS, SANS16 = '16px ' + SANS, WHITE = '#fff';
|
|
var chartBaseTheme = {
|
|
title: { font: SANS16 },
|
|
legend: { labels: { font: SANS12 } },
|
|
seriesDefaults: {
|
|
visible: true,
|
|
labels: { font: SANS11 },
|
|
donut: { margin: 1 },
|
|
line: { width: 2 },
|
|
vericalLine: { width: 2 },
|
|
scatterLine: { width: 1 },
|
|
area: {
|
|
opacity: 0.4,
|
|
markers: {
|
|
visible: false,
|
|
size: 6
|
|
},
|
|
highlight: {
|
|
markers: {
|
|
border: {
|
|
color: '#fff',
|
|
opacity: 1,
|
|
width: 1
|
|
}
|
|
}
|
|
},
|
|
line: {
|
|
opacity: 1,
|
|
width: 0
|
|
}
|
|
},
|
|
verticalArea: {
|
|
opacity: 0.4,
|
|
markers: {
|
|
visible: false,
|
|
size: 6
|
|
},
|
|
line: {
|
|
opacity: 1,
|
|
width: 0
|
|
}
|
|
},
|
|
radarLine: {
|
|
width: 2,
|
|
markers: { visible: false }
|
|
},
|
|
radarArea: {
|
|
opacity: 0.5,
|
|
markers: {
|
|
visible: false,
|
|
size: 6
|
|
},
|
|
line: {
|
|
opacity: 1,
|
|
width: 0
|
|
}
|
|
},
|
|
candlestick: {
|
|
line: {
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
border: {
|
|
width: 1,
|
|
_brightness: 0.8
|
|
},
|
|
gap: 1,
|
|
spacing: 0.3,
|
|
downColor: WHITE,
|
|
highlight: {
|
|
line: { width: 2 },
|
|
border: {
|
|
width: 2,
|
|
opacity: 1
|
|
}
|
|
}
|
|
},
|
|
ohlc: {
|
|
line: { width: 1 },
|
|
gap: 1,
|
|
spacing: 0.3,
|
|
highlight: {
|
|
line: {
|
|
width: 3,
|
|
opacity: 1
|
|
}
|
|
}
|
|
},
|
|
bubble: {
|
|
opacity: 0.6,
|
|
border: { width: 0 },
|
|
labels: { background: 'transparent' }
|
|
},
|
|
bar: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING
|
|
},
|
|
column: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING
|
|
},
|
|
rangeColumn: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING
|
|
},
|
|
rangeBar: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING
|
|
},
|
|
waterfall: {
|
|
gap: 0.5,
|
|
spacing: BAR_SPACING,
|
|
line: {
|
|
width: 1,
|
|
color: BLACK
|
|
}
|
|
},
|
|
horizontalWaterfall: {
|
|
gap: 0.5,
|
|
spacing: BAR_SPACING,
|
|
line: {
|
|
width: 1,
|
|
color: BLACK
|
|
}
|
|
},
|
|
bullet: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING,
|
|
target: { color: '#ff0000' }
|
|
},
|
|
verticalBullet: {
|
|
gap: BAR_GAP,
|
|
spacing: BAR_SPACING,
|
|
target: { color: '#ff0000' }
|
|
},
|
|
boxPlot: {
|
|
outliersField: '',
|
|
meanField: '',
|
|
whiskers: {
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
mean: {
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
median: {
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
border: {
|
|
width: 1,
|
|
_brightness: 0.8
|
|
},
|
|
gap: 1,
|
|
spacing: 0.3,
|
|
downColor: WHITE,
|
|
highlight: {
|
|
whiskers: { width: 2 },
|
|
border: {
|
|
width: 2,
|
|
opacity: 1
|
|
}
|
|
}
|
|
},
|
|
funnel: {
|
|
labels: {
|
|
color: '',
|
|
background: ''
|
|
}
|
|
},
|
|
notes: {
|
|
icon: { border: { width: 1 } },
|
|
label: {
|
|
padding: 3,
|
|
font: SANS12
|
|
},
|
|
line: {
|
|
length: 10,
|
|
width: 1
|
|
},
|
|
visible: true
|
|
}
|
|
},
|
|
categoryAxis: { majorGridLines: { visible: true } },
|
|
axisDefaults: {
|
|
labels: { font: SANS12 },
|
|
title: {
|
|
font: SANS16,
|
|
margin: 5
|
|
},
|
|
crosshair: { tooltip: { font: SANS12 } },
|
|
notes: {
|
|
icon: {
|
|
size: 7,
|
|
border: { width: 1 }
|
|
},
|
|
label: {
|
|
padding: 3,
|
|
font: SANS12
|
|
},
|
|
line: {
|
|
length: 10,
|
|
width: 1
|
|
},
|
|
visible: true
|
|
}
|
|
},
|
|
tooltip: { font: SANS12 },
|
|
navigator: {
|
|
pane: {
|
|
height: 90,
|
|
margin: { top: 10 }
|
|
}
|
|
}
|
|
};
|
|
var gaugeBaseTheme = { scale: { labels: { font: SANS12 } } };
|
|
var diagramBaseTheme = {
|
|
shapeDefaults: {
|
|
hover: { opacity: 0.2 },
|
|
stroke: { width: 0 }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
width: 7,
|
|
height: 7
|
|
}
|
|
}
|
|
},
|
|
selectable: {
|
|
stroke: {
|
|
width: 1,
|
|
dashType: 'dot'
|
|
}
|
|
},
|
|
connectionDefaults: {
|
|
stroke: { width: 2 },
|
|
selection: {
|
|
handles: {
|
|
width: 8,
|
|
height: 8
|
|
}
|
|
},
|
|
editable: {
|
|
tools: [
|
|
'edit',
|
|
'delete'
|
|
]
|
|
}
|
|
}
|
|
};
|
|
var themes = ui.themes, registerTheme = ui.registerTheme = function (themeName, options) {
|
|
var result = {};
|
|
result.chart = deepExtend({}, chartBaseTheme, options.chart);
|
|
result.gauge = deepExtend({}, gaugeBaseTheme, options.gauge);
|
|
result.diagram = deepExtend({}, diagramBaseTheme, options.diagram);
|
|
result.treeMap = deepExtend({}, options.treeMap);
|
|
var defaults = result.chart.seriesDefaults;
|
|
defaults.verticalLine = deepExtend({}, defaults.line);
|
|
defaults.verticalArea = deepExtend({}, defaults.area);
|
|
defaults.polarArea = deepExtend({}, defaults.radarArea);
|
|
defaults.polarLine = deepExtend({}, defaults.radarLine);
|
|
themes[themeName] = result;
|
|
};
|
|
registerTheme('black', {
|
|
chart: {
|
|
title: { color: WHITE },
|
|
legend: {
|
|
labels: { color: WHITE },
|
|
inactiveItems: {
|
|
labels: { color: '#919191' },
|
|
markers: { color: '#919191' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: WHITE },
|
|
errorBars: { color: WHITE },
|
|
notes: {
|
|
icon: {
|
|
background: '#3b3b3b',
|
|
border: { color: '#8e8e8e' }
|
|
},
|
|
label: { color: WHITE },
|
|
line: { color: '#8e8e8e' }
|
|
},
|
|
pie: { overlay: { gradient: 'sharpBevel' } },
|
|
donut: { overlay: { gradient: 'sharpGlass' } },
|
|
line: { markers: { background: '#3d3d3d' } },
|
|
scatter: { markers: { background: '#3d3d3d' } },
|
|
scatterLine: { markers: { background: '#3d3d3d' } },
|
|
waterfall: { line: { color: '#8e8e8e' } },
|
|
horizontalWaterfall: { line: { color: '#8e8e8e' } },
|
|
candlestick: {
|
|
downColor: '#555',
|
|
line: { color: WHITE },
|
|
border: {
|
|
_brightness: 1.5,
|
|
opacity: 1
|
|
},
|
|
highlight: {
|
|
border: {
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
}
|
|
}
|
|
},
|
|
ohlc: { line: { color: WHITE } }
|
|
},
|
|
chartArea: { background: '#3d3d3d' },
|
|
seriesColors: [
|
|
'#0081da',
|
|
'#3aafff',
|
|
'#99c900',
|
|
'#ffeb3d',
|
|
'#b20753',
|
|
'#ff4195'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#8e8e8e' },
|
|
labels: { color: WHITE },
|
|
majorGridLines: { color: '#545454' },
|
|
minorGridLines: { color: '#454545' },
|
|
title: { color: WHITE },
|
|
crosshair: { color: '#8e8e8e' },
|
|
notes: {
|
|
icon: {
|
|
background: '#3b3b3b',
|
|
border: { color: '#8e8e8e' }
|
|
},
|
|
label: { color: WHITE },
|
|
line: { color: '#8e8e8e' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#0070e4' },
|
|
scale: {
|
|
rangePlaceholderColor: '#1d1d1d',
|
|
labels: { color: WHITE },
|
|
minorTicks: { color: WHITE },
|
|
majorTicks: { color: WHITE },
|
|
line: { color: WHITE }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#0066cc' },
|
|
connectorDefaults: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#384049' },
|
|
hover: {
|
|
fill: { color: '#3d3d3d' },
|
|
stroke: { color: '#efefef' }
|
|
}
|
|
},
|
|
content: { color: WHITE }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: '#3d3d3d' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: WHITE },
|
|
fill: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: WHITE } },
|
|
connectionDefaults: {
|
|
stroke: { color: WHITE },
|
|
content: { color: WHITE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: '#3d3d3d' },
|
|
stroke: { color: '#efefef' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#0081da',
|
|
'#314b5c'
|
|
],
|
|
[
|
|
'#3aafff',
|
|
'#3c5464'
|
|
],
|
|
[
|
|
'#99c900',
|
|
'#4f5931'
|
|
],
|
|
[
|
|
'#ffeb3d',
|
|
'#64603d'
|
|
],
|
|
[
|
|
'#b20753',
|
|
'#543241'
|
|
],
|
|
[
|
|
'#ff4195',
|
|
'#643e4f'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('blueopal', {
|
|
chart: {
|
|
title: { color: '#293135' },
|
|
legend: {
|
|
labels: { color: '#293135' },
|
|
inactiveItems: {
|
|
labels: { color: '#27A5BA' },
|
|
markers: { color: '#27A5BA' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: {
|
|
color: BLACK,
|
|
background: WHITE,
|
|
opacity: 0.5
|
|
},
|
|
errorBars: { color: '#293135' },
|
|
candlestick: {
|
|
downColor: '#c4d0d5',
|
|
line: { color: '#9aabb2' }
|
|
},
|
|
waterfall: { line: { color: '#9aabb2' } },
|
|
horizontalWaterfall: { line: { color: '#9aabb2' } },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#9aabb2' }
|
|
},
|
|
label: { color: '#293135' },
|
|
line: { color: '#9aabb2' }
|
|
}
|
|
},
|
|
seriesColors: [
|
|
'#0069a5',
|
|
'#0098ee',
|
|
'#7bd2f6',
|
|
'#ffb800',
|
|
'#ff8517',
|
|
'#e34a00'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#9aabb2' },
|
|
labels: { color: '#293135' },
|
|
majorGridLines: { color: '#c4d0d5' },
|
|
minorGridLines: { color: '#edf1f2' },
|
|
title: { color: '#293135' },
|
|
crosshair: { color: '#9aabb2' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#9aabb2' }
|
|
},
|
|
label: { color: '#293135' },
|
|
line: { color: '#9aabb2' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#005c83' },
|
|
scale: {
|
|
rangePlaceholderColor: '#daecf4',
|
|
labels: { color: '#293135' },
|
|
minorTicks: { color: '#293135' },
|
|
majorTicks: { color: '#293135' },
|
|
line: { color: '#293135' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#7ec6e3' },
|
|
connectorDefaults: {
|
|
fill: { color: '#003f59' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#003f59' }
|
|
}
|
|
},
|
|
content: { color: '#293135' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#003f59' },
|
|
hover: {
|
|
fill: { color: '#003f59' },
|
|
stroke: { color: '#003f59' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#003f59' },
|
|
fill: { color: '#003f59' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#003f59' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#003f59' },
|
|
content: { color: '#293135' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: '#3d3d3d' },
|
|
stroke: { color: '#efefef' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#0069a5',
|
|
'#bad7e7'
|
|
],
|
|
[
|
|
'#0098ee',
|
|
'#b9e0f5'
|
|
],
|
|
[
|
|
'#7bd2f6',
|
|
'#ceeaf6'
|
|
],
|
|
[
|
|
'#ffb800',
|
|
'#e6e3c4'
|
|
],
|
|
[
|
|
'#ff8517',
|
|
'#e4d8c8'
|
|
],
|
|
[
|
|
'#e34a00',
|
|
'#ddccc2'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('highcontrast', {
|
|
chart: {
|
|
title: { color: '#ffffff' },
|
|
legend: {
|
|
labels: { color: '#ffffff' },
|
|
inactiveItems: {
|
|
labels: { color: '#66465B' },
|
|
markers: { color: '#66465B' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#ffffff' },
|
|
errorBars: { color: '#ffffff' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#ffffff' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#ffffff' }
|
|
},
|
|
pie: { overlay: { gradient: 'sharpGlass' } },
|
|
donut: { overlay: { gradient: 'sharpGlass' } },
|
|
line: { markers: { background: '#2c232b' } },
|
|
scatter: { markers: { background: '#2c232b' } },
|
|
scatterLine: { markers: { background: '#2c232b' } },
|
|
area: { opacity: 0.5 },
|
|
waterfall: { line: { color: '#ffffff' } },
|
|
horizontalWaterfall: { line: { color: '#ffffff' } },
|
|
candlestick: {
|
|
downColor: '#664e62',
|
|
line: { color: '#ffffff' },
|
|
border: {
|
|
_brightness: 1.5,
|
|
opacity: 1
|
|
},
|
|
highlight: {
|
|
border: {
|
|
color: '#ffffff',
|
|
opacity: 1
|
|
}
|
|
}
|
|
},
|
|
ohlc: { line: { color: '#ffffff' } }
|
|
},
|
|
chartArea: { background: '#2c232b' },
|
|
seriesColors: [
|
|
'#a7008f',
|
|
'#ffb800',
|
|
'#3aafff',
|
|
'#99c900',
|
|
'#b20753',
|
|
'#ff4195'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#ffffff' },
|
|
labels: { color: '#ffffff' },
|
|
majorGridLines: { color: '#664e62' },
|
|
minorGridLines: { color: '#4f394b' },
|
|
title: { color: '#ffffff' },
|
|
crosshair: { color: '#ffffff' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#ffffff' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#ffffff' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#a7008f' },
|
|
scale: {
|
|
rangePlaceholderColor: '#2c232b',
|
|
labels: { color: '#ffffff' },
|
|
minorTicks: { color: '#2c232b' },
|
|
majorTicks: { color: '#664e62' },
|
|
line: { color: '#ffffff' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#a7018f' },
|
|
connectorDefaults: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#2c232b' },
|
|
hover: {
|
|
fill: { color: '#2c232b' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
},
|
|
content: { color: WHITE }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: '#2c232b' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: WHITE },
|
|
fill: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: WHITE } },
|
|
connectionDefaults: {
|
|
stroke: { color: WHITE },
|
|
content: { color: WHITE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: '#2c232b' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#a7008f',
|
|
'#451c3f'
|
|
],
|
|
[
|
|
'#ffb800',
|
|
'#564122'
|
|
],
|
|
[
|
|
'#3aafff',
|
|
'#2f3f55'
|
|
],
|
|
[
|
|
'#99c900',
|
|
'#424422'
|
|
],
|
|
[
|
|
'#b20753',
|
|
'#471d33'
|
|
],
|
|
[
|
|
'#ff4195',
|
|
'#562940'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('default', {
|
|
chart: {
|
|
title: { color: '#8e8e8e' },
|
|
legend: {
|
|
labels: { color: '#232323' },
|
|
inactiveItems: {
|
|
labels: { color: '#919191' },
|
|
markers: { color: '#919191' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: {
|
|
color: BLACK,
|
|
background: WHITE,
|
|
opacity: 0.5
|
|
},
|
|
errorBars: { color: '#232323' },
|
|
candlestick: {
|
|
downColor: '#dedede',
|
|
line: { color: '#8d8d8d' }
|
|
},
|
|
waterfall: { line: { color: '#8e8e8e' } },
|
|
horizontalWaterfall: { line: { color: '#8e8e8e' } },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#8e8e8e' }
|
|
},
|
|
label: { color: '#232323' },
|
|
line: { color: '#8e8e8e' }
|
|
}
|
|
},
|
|
seriesColors: [
|
|
'#ff6800',
|
|
'#a0a700',
|
|
'#ff8d00',
|
|
'#678900',
|
|
'#ffb53c',
|
|
'#396000'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#8e8e8e' },
|
|
labels: { color: '#232323' },
|
|
minorGridLines: { color: '#f0f0f0' },
|
|
majorGridLines: { color: '#dfdfdf' },
|
|
title: { color: '#232323' },
|
|
crosshair: { color: '#8e8e8e' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#8e8e8e' }
|
|
},
|
|
label: { color: '#232323' },
|
|
line: { color: '#8e8e8e' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#ea7001' },
|
|
scale: {
|
|
rangePlaceholderColor: '#dedede',
|
|
labels: { color: '#2e2e2e' },
|
|
minorTicks: { color: '#2e2e2e' },
|
|
majorTicks: { color: '#2e2e2e' },
|
|
line: { color: '#2e2e2e' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#e15613' },
|
|
connectorDefaults: {
|
|
fill: { color: '#282828' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#282828' }
|
|
}
|
|
},
|
|
content: { color: '#2e2e2e' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#282828' },
|
|
hover: {
|
|
fill: { color: '#282828' },
|
|
stroke: { color: '#282828' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#282828' },
|
|
fill: { color: '#282828' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#a7018f' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#282828' },
|
|
content: { color: '#2e2e2e' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#282828' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#ff6800',
|
|
'#edcfba'
|
|
],
|
|
[
|
|
'#a0a700',
|
|
'#dadcba'
|
|
],
|
|
[
|
|
'#ff8d00',
|
|
'#edd7ba'
|
|
],
|
|
[
|
|
'#678900',
|
|
'#cfd6ba'
|
|
],
|
|
[
|
|
'#ffb53c',
|
|
'#eddfc6'
|
|
],
|
|
[
|
|
'#396000',
|
|
'#c6ceba'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('silver', {
|
|
chart: {
|
|
title: { color: '#4e5968' },
|
|
legend: {
|
|
labels: { color: '#4e5968' },
|
|
inactiveItems: {
|
|
labels: { color: '#B1BCC8' },
|
|
markers: { color: '#B1BCC8' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: {
|
|
color: '#293135',
|
|
background: '#eaeaec',
|
|
opacity: 0.5
|
|
},
|
|
errorBars: { color: '#4e5968' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#4e5968' }
|
|
},
|
|
label: { color: '#4e5968' },
|
|
line: { color: '#4e5968' }
|
|
},
|
|
line: { markers: { background: '#eaeaec' } },
|
|
scatter: { markers: { background: '#eaeaec' } },
|
|
scatterLine: { markers: { background: '#eaeaec' } },
|
|
pie: { connectors: { color: '#A6B1C0' } },
|
|
donut: { connectors: { color: '#A6B1C0' } },
|
|
waterfall: { line: { color: '#a6b1c0' } },
|
|
horizontalWaterfall: { line: { color: '#a6b1c0' } },
|
|
candlestick: { downColor: '#a6afbe' }
|
|
},
|
|
chartArea: { background: '#eaeaec' },
|
|
seriesColors: [
|
|
'#007bc3',
|
|
'#76b800',
|
|
'#ffae00',
|
|
'#ef4c00',
|
|
'#a419b7',
|
|
'#430B62'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#a6b1c0' },
|
|
labels: { color: '#4e5968' },
|
|
majorGridLines: { color: '#dcdcdf' },
|
|
minorGridLines: { color: '#eeeeef' },
|
|
title: { color: '#4e5968' },
|
|
crosshair: { color: '#a6b1c0' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#4e5968' }
|
|
},
|
|
label: { color: '#4e5968' },
|
|
line: { color: '#4e5968' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#0879c0' },
|
|
scale: {
|
|
rangePlaceholderColor: '#f3f3f4',
|
|
labels: { color: '#515967' },
|
|
minorTicks: { color: '#515967' },
|
|
majorTicks: { color: '#515967' },
|
|
line: { color: '#515967' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#1c82c2' },
|
|
connectorDefaults: {
|
|
fill: { color: '#515967' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#282828' }
|
|
}
|
|
},
|
|
content: { color: '#515967' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#515967' },
|
|
hover: {
|
|
fill: { color: '#515967' },
|
|
stroke: { color: '#515967' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#515967' },
|
|
fill: { color: '#515967' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#515967' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#515967' },
|
|
content: { color: '#515967' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#515967' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#007bc3',
|
|
'#c2dbea'
|
|
],
|
|
[
|
|
'#76b800',
|
|
'#dae7c3'
|
|
],
|
|
[
|
|
'#ffae00',
|
|
'#f5e5c3'
|
|
],
|
|
[
|
|
'#ef4c00',
|
|
'#f2d2c3'
|
|
],
|
|
[
|
|
'#a419b7',
|
|
'#e3c7e8'
|
|
],
|
|
[
|
|
'#430b62',
|
|
'#d0c5d7'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('metro', {
|
|
chart: {
|
|
title: { color: '#777777' },
|
|
legend: {
|
|
labels: { color: '#777777' },
|
|
inactiveItems: {
|
|
labels: { color: '#CBCBCB' },
|
|
markers: { color: '#CBCBCB' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: BLACK },
|
|
errorBars: { color: '#777777' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#777777' }
|
|
},
|
|
label: { color: '#777777' },
|
|
line: { color: '#777777' }
|
|
},
|
|
candlestick: {
|
|
downColor: '#c7c7c7',
|
|
line: { color: '#787878' }
|
|
},
|
|
waterfall: { line: { color: '#c7c7c7' } },
|
|
horizontalWaterfall: { line: { color: '#c7c7c7' } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: [
|
|
'#8ebc00',
|
|
'#309b46',
|
|
'#25a0da',
|
|
'#ff6900',
|
|
'#e61e26',
|
|
'#d8e404',
|
|
'#16aba9',
|
|
'#7e51a1',
|
|
'#313131',
|
|
'#ed1691'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#c7c7c7' },
|
|
labels: { color: '#777777' },
|
|
minorGridLines: { color: '#c7c7c7' },
|
|
majorGridLines: { color: '#c7c7c7' },
|
|
title: { color: '#777777' },
|
|
crosshair: { color: '#c7c7c7' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#777777' }
|
|
},
|
|
label: { color: '#777777' },
|
|
line: { color: '#777777' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#8ebc00' },
|
|
scale: {
|
|
rangePlaceholderColor: '#e6e6e6',
|
|
labels: { color: '#777' },
|
|
minorTicks: { color: '#777' },
|
|
majorTicks: { color: '#777' },
|
|
line: { color: '#777' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#8ebc00' },
|
|
connectorDefaults: {
|
|
fill: { color: BLACK },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: BLACK }
|
|
}
|
|
},
|
|
content: { color: '#777' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#787878' },
|
|
hover: {
|
|
fill: { color: '#787878' },
|
|
stroke: { color: '#787878' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#787878' },
|
|
fill: { color: '#787878' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#515967' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#787878' },
|
|
content: { color: '#777' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#787878' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#8ebc00',
|
|
'#e8f2cc'
|
|
],
|
|
[
|
|
'#309b46',
|
|
'#d6ebda'
|
|
],
|
|
[
|
|
'#25a0da',
|
|
'#d3ecf8'
|
|
],
|
|
[
|
|
'#ff6900',
|
|
'#ffe1cc'
|
|
],
|
|
[
|
|
'#e61e26',
|
|
'#fad2d4'
|
|
],
|
|
[
|
|
'#d8e404',
|
|
'#f7facd'
|
|
],
|
|
[
|
|
'#16aba9',
|
|
'#d0eeee'
|
|
],
|
|
[
|
|
'#7e51a1',
|
|
'#e5dcec'
|
|
],
|
|
[
|
|
'#313131',
|
|
'#d6d6d6'
|
|
],
|
|
[
|
|
'#ed1691',
|
|
'#fbd0e9'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('metroblack', {
|
|
chart: {
|
|
title: { color: '#ffffff' },
|
|
legend: {
|
|
labels: { color: '#ffffff' },
|
|
inactiveItems: {
|
|
labels: { color: '#797979' },
|
|
markers: { color: '#797979' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
border: { _brightness: 1 },
|
|
labels: { color: '#ffffff' },
|
|
errorBars: { color: '#ffffff' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#cecece' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#cecece' }
|
|
},
|
|
line: { markers: { background: '#0e0e0e' } },
|
|
bubble: { opacity: 0.6 },
|
|
scatter: { markers: { background: '#0e0e0e' } },
|
|
scatterLine: { markers: { background: '#0e0e0e' } },
|
|
candlestick: {
|
|
downColor: '#828282',
|
|
line: { color: '#ffffff' }
|
|
},
|
|
waterfall: { line: { color: '#cecece' } },
|
|
horizontalWaterfall: { line: { color: '#cecece' } },
|
|
overlay: { gradient: 'none' }
|
|
},
|
|
chartArea: { background: '#0e0e0e' },
|
|
seriesColors: [
|
|
'#00aba9',
|
|
'#309b46',
|
|
'#8ebc00',
|
|
'#ff6900',
|
|
'#e61e26',
|
|
'#d8e404',
|
|
'#25a0da',
|
|
'#7e51a1',
|
|
'#313131',
|
|
'#ed1691'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#cecece' },
|
|
labels: { color: '#ffffff' },
|
|
minorGridLines: { color: '#2d2d2d' },
|
|
majorGridLines: { color: '#333333' },
|
|
title: { color: '#ffffff' },
|
|
crosshair: { color: '#cecece' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#cecece' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#cecece' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#00aba9' },
|
|
scale: {
|
|
rangePlaceholderColor: '#2d2d2d',
|
|
labels: { color: '#ffffff' },
|
|
minorTicks: { color: '#333333' },
|
|
majorTicks: { color: '#cecece' },
|
|
line: { color: '#cecece' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#00aba9' },
|
|
connectorDefaults: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#0e0e0e' },
|
|
hover: {
|
|
fill: { color: '#0e0e0e' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
},
|
|
content: { color: WHITE }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: '#0e0e0e' },
|
|
stroke: { color: '#787878' },
|
|
hover: {
|
|
fill: { color: '#787878' },
|
|
stroke: { color: '#787878' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: WHITE },
|
|
fill: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#787878' } },
|
|
connectionDefaults: {
|
|
stroke: { color: WHITE },
|
|
content: { color: WHITE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: '#0e0e0e' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#00aba9',
|
|
'#0b2d2d'
|
|
],
|
|
[
|
|
'#309b46',
|
|
'#152a19'
|
|
],
|
|
[
|
|
'#8ebc00',
|
|
'#28310b'
|
|
],
|
|
[
|
|
'#ff6900',
|
|
'#3e200b'
|
|
],
|
|
[
|
|
'#e61e26',
|
|
'#391113'
|
|
],
|
|
[
|
|
'#d8e404',
|
|
'#36390c'
|
|
],
|
|
[
|
|
'#25a0da',
|
|
'#132b37'
|
|
],
|
|
[
|
|
'#7e51a1',
|
|
'#241b2b'
|
|
],
|
|
[
|
|
'#313131',
|
|
'#151515'
|
|
],
|
|
[
|
|
'#ed1691',
|
|
'#3b1028'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('moonlight', {
|
|
chart: {
|
|
title: { color: '#ffffff' },
|
|
legend: {
|
|
labels: { color: '#ffffff' },
|
|
inactiveItems: {
|
|
labels: { color: '#A1A7AB' },
|
|
markers: { color: '#A1A7AB' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#ffffff' },
|
|
errorBars: { color: '#ffffff' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#8c909e' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#8c909e' }
|
|
},
|
|
pie: { overlay: { gradient: 'sharpBevel' } },
|
|
donut: { overlay: { gradient: 'sharpGlass' } },
|
|
line: { markers: { background: '#212a33' } },
|
|
bubble: { opacity: 0.6 },
|
|
scatter: { markers: { background: '#212a33' } },
|
|
scatterLine: { markers: { background: '#212a33' } },
|
|
area: { opacity: 0.3 },
|
|
candlestick: {
|
|
downColor: '#757d87',
|
|
line: { color: '#ea9d06' },
|
|
border: {
|
|
_brightness: 1.5,
|
|
opacity: 1
|
|
},
|
|
highlight: {
|
|
border: {
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
}
|
|
}
|
|
},
|
|
waterfall: { line: { color: '#8c909e' } },
|
|
horizontalWaterfall: { line: { color: '#8c909e' } },
|
|
ohlc: { line: { color: '#ea9d06' } }
|
|
},
|
|
chartArea: { background: '#212a33' },
|
|
seriesColors: [
|
|
'#ffca08',
|
|
'#ff710f',
|
|
'#ed2e24',
|
|
'#ff9f03',
|
|
'#e13c02',
|
|
'#a00201'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#8c909e' },
|
|
minorTicks: { color: '#8c909e' },
|
|
majorTicks: { color: '#8c909e' },
|
|
labels: { color: '#ffffff' },
|
|
majorGridLines: { color: '#3e424d' },
|
|
minorGridLines: { color: '#2f3640' },
|
|
title: { color: '#ffffff' },
|
|
crosshair: { color: '#8c909e' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#8c909e' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#8c909e' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#f4af03' },
|
|
scale: {
|
|
rangePlaceholderColor: '#2f3640',
|
|
labels: { color: WHITE },
|
|
minorTicks: { color: '#8c909e' },
|
|
majorTicks: { color: '#8c909e' },
|
|
line: { color: '#8c909e' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#f3ae03' },
|
|
connectorDefaults: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#414550' },
|
|
hover: {
|
|
fill: { color: '#414550' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
},
|
|
content: { color: WHITE }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: '#414550' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: WHITE },
|
|
fill: { color: WHITE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: WHITE } },
|
|
connectionDefaults: {
|
|
stroke: { color: WHITE },
|
|
content: { color: WHITE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: '#414550' },
|
|
stroke: { color: WHITE }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#ffca08',
|
|
'#4e4b2b'
|
|
],
|
|
[
|
|
'#ff710f',
|
|
'#4e392d'
|
|
],
|
|
[
|
|
'#ed2e24',
|
|
'#4b2c31'
|
|
],
|
|
[
|
|
'#ff9f03',
|
|
'#4e422a'
|
|
],
|
|
[
|
|
'#e13c02',
|
|
'#482e2a'
|
|
],
|
|
[
|
|
'#a00201',
|
|
'#3b232a'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('uniform', {
|
|
chart: {
|
|
title: { color: '#686868' },
|
|
legend: {
|
|
labels: { color: '#686868' },
|
|
inactiveItems: {
|
|
labels: { color: '#B6B6B6' },
|
|
markers: { color: '#B6B6B6' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#686868' },
|
|
errorBars: { color: '#686868' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#9e9e9e' }
|
|
},
|
|
label: { color: '#686868' },
|
|
line: { color: '#9e9e9e' }
|
|
},
|
|
pie: { overlay: { gradient: 'sharpBevel' } },
|
|
donut: { overlay: { gradient: 'sharpGlass' } },
|
|
line: { markers: { background: '#ffffff' } },
|
|
bubble: { opacity: 0.6 },
|
|
scatter: { markers: { background: '#ffffff' } },
|
|
scatterLine: { markers: { background: '#ffffff' } },
|
|
area: { opacity: 0.3 },
|
|
candlestick: {
|
|
downColor: '#cccccc',
|
|
line: { color: '#cccccc' },
|
|
border: {
|
|
_brightness: 1.5,
|
|
opacity: 1
|
|
},
|
|
highlight: {
|
|
border: {
|
|
color: '#cccccc',
|
|
opacity: 0.2
|
|
}
|
|
}
|
|
},
|
|
waterfall: { line: { color: '#9e9e9e' } },
|
|
horizontalWaterfall: { line: { color: '#9e9e9e' } },
|
|
ohlc: { line: { color: '#cccccc' } }
|
|
},
|
|
chartArea: { background: '#ffffff' },
|
|
seriesColors: [
|
|
'#527aa3',
|
|
'#6f91b3',
|
|
'#8ca7c2',
|
|
'#a8bdd1',
|
|
'#c5d3e0',
|
|
'#e2e9f0'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#9e9e9e' },
|
|
minorTicks: { color: '#aaaaaa' },
|
|
majorTicks: { color: '#888888' },
|
|
labels: { color: '#686868' },
|
|
majorGridLines: { color: '#dadada' },
|
|
minorGridLines: { color: '#e7e7e7' },
|
|
title: { color: '#686868' },
|
|
crosshair: { color: '#9e9e9e' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#9e9e9e' }
|
|
},
|
|
label: { color: '#686868' },
|
|
line: { color: '#9e9e9e' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#527aa3' },
|
|
scale: {
|
|
rangePlaceholderColor: '#e7e7e7',
|
|
labels: { color: '#686868' },
|
|
minorTicks: { color: '#aaaaaa' },
|
|
majorTicks: { color: '#888888' },
|
|
line: { color: '#9e9e9e' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#d1d1d1' },
|
|
connectorDefaults: {
|
|
fill: { color: '#686868' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#686868' }
|
|
}
|
|
},
|
|
content: { color: '#686868' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#686868' },
|
|
hover: {
|
|
fill: { color: '#686868' },
|
|
stroke: { color: '#686868' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#686868' },
|
|
fill: { color: '#686868' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#686868' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#686868' },
|
|
content: { color: '#686868' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#686868' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#527aa3',
|
|
'#d0d8e1'
|
|
],
|
|
[
|
|
'#6f91b3',
|
|
'#d6dde4'
|
|
],
|
|
[
|
|
'#8ca7c2',
|
|
'#dce1e7'
|
|
],
|
|
[
|
|
'#a8bdd1',
|
|
'#e2e6ea'
|
|
],
|
|
[
|
|
'#c5d3e0',
|
|
'#e7eaed'
|
|
],
|
|
[
|
|
'#e2e9f0',
|
|
'#edeff0'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('bootstrap', {
|
|
chart: {
|
|
title: { color: '#333333' },
|
|
legend: {
|
|
labels: { color: '#333333' },
|
|
inactiveItems: {
|
|
labels: { color: '#999999' },
|
|
markers: { color: '#9A9A9A' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#333333' },
|
|
overlay: { gradient: 'none' },
|
|
errorBars: { color: '#343434' },
|
|
notes: {
|
|
icon: {
|
|
background: '#000000',
|
|
border: { color: '#000000' }
|
|
},
|
|
label: { color: '#333333' },
|
|
line: { color: '#000000' }
|
|
},
|
|
pie: { overlay: { gradient: 'none' } },
|
|
donut: { overlay: { gradient: 'none' } },
|
|
line: { markers: { background: '#ffffff' } },
|
|
bubble: { opacity: 0.6 },
|
|
scatter: { markers: { background: '#ffffff' } },
|
|
scatterLine: { markers: { background: '#ffffff' } },
|
|
area: { opacity: 0.8 },
|
|
candlestick: {
|
|
downColor: '#d0d0d0',
|
|
line: { color: '#333333' },
|
|
border: {
|
|
_brightness: 1.5,
|
|
opacity: 1
|
|
},
|
|
highlight: {
|
|
border: {
|
|
color: '#b8b8b8',
|
|
opacity: 0.2
|
|
}
|
|
}
|
|
},
|
|
waterfall: { line: { color: '#cccccc' } },
|
|
horizontalWaterfall: { line: { color: '#cccccc' } },
|
|
ohlc: { line: { color: '#333333' } }
|
|
},
|
|
chartArea: { background: '#ffffff' },
|
|
seriesColors: [
|
|
'#428bca',
|
|
'#5bc0de',
|
|
'#5cb85c',
|
|
'#f2b661',
|
|
'#e67d4a',
|
|
'#da3b36'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#cccccc' },
|
|
minorTicks: { color: '#ebebeb' },
|
|
majorTicks: { color: '#cccccc' },
|
|
labels: { color: '#333333' },
|
|
majorGridLines: { color: '#cccccc' },
|
|
minorGridLines: { color: '#ebebeb' },
|
|
title: { color: '#333333' },
|
|
crosshair: { color: '#000000' },
|
|
notes: {
|
|
icon: {
|
|
background: '#000000',
|
|
border: { color: '#000000' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: '#000000' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#428bca' },
|
|
scale: {
|
|
rangePlaceholderColor: '#cccccc',
|
|
labels: { color: '#333333' },
|
|
minorTicks: { color: '#ebebeb' },
|
|
majorTicks: { color: '#cccccc' },
|
|
line: { color: '#cccccc' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#428bca' },
|
|
connectorDefaults: {
|
|
fill: { color: '#333333' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#333333' }
|
|
}
|
|
},
|
|
content: { color: '#333333' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#333333' },
|
|
hover: {
|
|
fill: { color: '#333333' },
|
|
stroke: { color: '#333333' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#333333' },
|
|
fill: { color: '#333333' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#333333' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#c4c4c4' },
|
|
content: { color: '#333333' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#333333' }
|
|
},
|
|
stroke: { color: '#333333' }
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#428bca',
|
|
'#d1e0ec'
|
|
],
|
|
[
|
|
'#5bc0de',
|
|
'#d6eaf0'
|
|
],
|
|
[
|
|
'#5cb85c',
|
|
'#d6e9d6'
|
|
],
|
|
[
|
|
'#5cb85c',
|
|
'#f4e8d7'
|
|
],
|
|
[
|
|
'#e67d4a',
|
|
'#f2ddd3'
|
|
],
|
|
[
|
|
'#da3b36',
|
|
'#f0d0cf'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('flat', {
|
|
chart: {
|
|
title: { color: '#4c5356' },
|
|
legend: {
|
|
labels: { color: '#4c5356' },
|
|
inactiveItems: {
|
|
labels: { color: '#CBCBCB' },
|
|
markers: { color: '#CBCBCB' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#4c5356' },
|
|
errorBars: { color: '#4c5356' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#cdcdcd' }
|
|
},
|
|
label: { color: '#4c5356' },
|
|
line: { color: '#cdcdcd' }
|
|
},
|
|
candlestick: {
|
|
downColor: '#c7c7c7',
|
|
line: { color: '#787878' }
|
|
},
|
|
area: { opacity: 0.9 },
|
|
waterfall: { line: { color: '#cdcdcd' } },
|
|
horizontalWaterfall: { line: { color: '#cdcdcd' } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: [
|
|
'#10c4b2',
|
|
'#ff7663',
|
|
'#ffb74f',
|
|
'#a2df53',
|
|
'#1c9ec4',
|
|
'#ff63a5',
|
|
'#1cc47b'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#cdcdcd' },
|
|
labels: { color: '#4c5356' },
|
|
minorGridLines: { color: '#cdcdcd' },
|
|
majorGridLines: { color: '#cdcdcd' },
|
|
title: { color: '#4c5356' },
|
|
crosshair: { color: '#cdcdcd' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#cdcdcd' }
|
|
},
|
|
label: { color: '#4c5356' },
|
|
line: { color: '#cdcdcd' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#10c4b2' },
|
|
scale: {
|
|
rangePlaceholderColor: '#cdcdcd',
|
|
labels: { color: '#4c5356' },
|
|
minorTicks: { color: '#4c5356' },
|
|
majorTicks: { color: '#4c5356' },
|
|
line: { color: '#4c5356' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#10c4b2' },
|
|
connectorDefaults: {
|
|
fill: { color: '#363940' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#363940' }
|
|
}
|
|
},
|
|
content: { color: '#4c5356' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#363940' },
|
|
hover: {
|
|
fill: { color: '#363940' },
|
|
stroke: { color: '#363940' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#363940' },
|
|
fill: { color: '#363940' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#363940' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#cdcdcd' },
|
|
content: { color: '#4c5356' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#363940' }
|
|
},
|
|
stroke: { color: '#363940' }
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#10c4b2',
|
|
'#cff3f0'
|
|
],
|
|
[
|
|
'#ff7663',
|
|
'#ffe4e0'
|
|
],
|
|
[
|
|
'#ffb74f',
|
|
'#fff1dc'
|
|
],
|
|
[
|
|
'#a2df53',
|
|
'#ecf9dd'
|
|
],
|
|
[
|
|
'#1c9ec4',
|
|
'#d2ecf3'
|
|
],
|
|
[
|
|
'#ff63a5',
|
|
'#ffe0ed'
|
|
],
|
|
[
|
|
'#1cc47b',
|
|
'#d2f3e5'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('material', {
|
|
chart: {
|
|
title: { color: '#444444' },
|
|
legend: {
|
|
labels: { color: '#444444' },
|
|
inactiveItems: {
|
|
labels: { color: '#CBCBCB' },
|
|
markers: { color: '#CBCBCB' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#444444' },
|
|
errorBars: { color: '#444444' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#e5e5e5' }
|
|
},
|
|
label: { color: '#444444' },
|
|
line: { color: '#e5e5e5' }
|
|
},
|
|
candlestick: {
|
|
downColor: '#c7c7c7',
|
|
line: { color: '#787878' }
|
|
},
|
|
area: { opacity: 0.9 },
|
|
waterfall: { line: { color: '#e5e5e5' } },
|
|
horizontalWaterfall: { line: { color: '#e5e5e5' } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: [
|
|
'#3f51b5',
|
|
'#03a9f4',
|
|
'#4caf50',
|
|
'#f9ce1d',
|
|
'#ff9800',
|
|
'#ff5722'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#e5e5e5' },
|
|
labels: { color: '#444444' },
|
|
minorGridLines: { color: '#e5e5e5' },
|
|
majorGridLines: { color: '#e5e5e5' },
|
|
title: { color: '#444444' },
|
|
crosshair: { color: '#7f7f7f' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#e5e5e5' }
|
|
},
|
|
label: { color: '#444444' },
|
|
line: { color: '#e5e5e5' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#3f51b5' },
|
|
scale: {
|
|
rangePlaceholderColor: '#e5e5e5',
|
|
labels: { color: '#444444' },
|
|
minorTicks: { color: '#444444' },
|
|
majorTicks: { color: '#444444' },
|
|
line: { color: '#444444' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#3f51b5' },
|
|
connectorDefaults: {
|
|
fill: { color: '#7f7f7f' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#7f7f7f' }
|
|
}
|
|
},
|
|
content: { color: '#444444' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#444444' },
|
|
hover: {
|
|
fill: { color: '#444444' },
|
|
stroke: { color: '#444444' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#444444' },
|
|
fill: { color: '#444444' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#444444' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#7f7f7f' },
|
|
content: { color: '#444444' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#444444' }
|
|
},
|
|
stroke: { color: '#444444' }
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#3f51b5',
|
|
'#cff3f0'
|
|
],
|
|
[
|
|
'#03a9f4',
|
|
'#e5f6fe'
|
|
],
|
|
[
|
|
'#4caf50',
|
|
'#edf7ed'
|
|
],
|
|
[
|
|
'#f9ce1d',
|
|
'#fefae8'
|
|
],
|
|
[
|
|
'#ff9800',
|
|
'#fff4e5'
|
|
],
|
|
[
|
|
'#ff5722',
|
|
'#ffeee8'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
registerTheme('materialblack', {
|
|
chart: {
|
|
title: { color: '#fff' },
|
|
legend: {
|
|
labels: { color: '#fff' },
|
|
inactiveItems: {
|
|
labels: { color: '#CBCBCB' },
|
|
markers: { color: '#CBCBCB' }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: '#fff' },
|
|
errorBars: { color: '#fff' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#e5e5e5' }
|
|
},
|
|
label: { color: '#fff' },
|
|
line: { color: '#e5e5e5' }
|
|
},
|
|
candlestick: {
|
|
downColor: '#c7c7c7',
|
|
line: { color: '#787878' }
|
|
},
|
|
area: { opacity: 0.9 },
|
|
waterfall: { line: { color: '#4d4d4d' } },
|
|
horizontalWaterfall: { line: { color: '#4d4d4d' } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
chartArea: { background: '#1c1c1c' },
|
|
seriesColors: [
|
|
'#3f51b5',
|
|
'#03a9f4',
|
|
'#4caf50',
|
|
'#f9ce1d',
|
|
'#ff9800',
|
|
'#ff5722'
|
|
],
|
|
axisDefaults: {
|
|
line: { color: '#4d4d4d' },
|
|
labels: { color: '#fff' },
|
|
minorGridLines: { color: '#4d4d4d' },
|
|
majorGridLines: { color: '#4d4d4d' },
|
|
title: { color: '#fff' },
|
|
crosshair: { color: '#7f7f7f' },
|
|
notes: {
|
|
icon: {
|
|
background: 'transparent',
|
|
border: { color: '#4d4d4d' }
|
|
},
|
|
label: { color: '#fff' },
|
|
line: { color: '#4d4d4d' }
|
|
}
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: '#3f51b5' },
|
|
scale: {
|
|
rangePlaceholderColor: '#4d4d4d',
|
|
labels: { color: '#fff' },
|
|
minorTicks: { color: '#fff' },
|
|
majorTicks: { color: '#fff' },
|
|
line: { color: '#fff' }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: '#3f51b5' },
|
|
connectorDefaults: {
|
|
fill: { color: '#7f7f7f' },
|
|
stroke: { color: WHITE },
|
|
hover: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#7f7f7f' }
|
|
}
|
|
},
|
|
content: { color: '#fff' }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#fff' },
|
|
hover: {
|
|
fill: { color: '#fff' },
|
|
stroke: { color: '#fff' }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: '#fff' },
|
|
fill: { color: '#fff' }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: '#fff' } },
|
|
connectionDefaults: {
|
|
stroke: { color: '#7f7f7f' },
|
|
content: { color: '#fff' },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: WHITE },
|
|
stroke: { color: '#fff' }
|
|
},
|
|
stroke: { color: '#fff' }
|
|
}
|
|
}
|
|
},
|
|
treeMap: {
|
|
colors: [
|
|
[
|
|
'#3f51b5',
|
|
'#cff3f0'
|
|
],
|
|
[
|
|
'#03a9f4',
|
|
'#e5f6fe'
|
|
],
|
|
[
|
|
'#4caf50',
|
|
'#edf7ed'
|
|
],
|
|
[
|
|
'#f9ce1d',
|
|
'#fefae8'
|
|
],
|
|
[
|
|
'#ff9800',
|
|
'#fff4e5'
|
|
],
|
|
[
|
|
'#ff5722',
|
|
'#ffeee8'
|
|
]
|
|
]
|
|
}
|
|
});
|
|
(function () {
|
|
var TEXT = '#333333';
|
|
var INACTIVE = '#7f7f7f';
|
|
var INACTIVE_SHAPE = '#bdbdbd';
|
|
var AXIS = '#c8c8c8';
|
|
var AXIS_MINOR = '#dddddd';
|
|
var SERIES = [
|
|
'#008fd3',
|
|
'#99d101',
|
|
'#f39b02',
|
|
'#f05662',
|
|
'#c03c53',
|
|
'#acacac'
|
|
];
|
|
var SERIES_LIGHT = [
|
|
'#cbe8f5',
|
|
'#eaf5cb',
|
|
'#fceacc',
|
|
'#fbdcdf',
|
|
'#f2d7dc',
|
|
'#eeeeee'
|
|
];
|
|
var PRIMARY = SERIES[0];
|
|
var DIAGRAM_HOVER = WHITE;
|
|
function noteStyle() {
|
|
return {
|
|
icon: {
|
|
background: '#007cc0',
|
|
border: { color: '#007cc0' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: AXIS }
|
|
};
|
|
}
|
|
registerTheme('fiori', {
|
|
chart: {
|
|
title: { color: TEXT },
|
|
legend: {
|
|
labels: { color: TEXT },
|
|
inactiveItems: {
|
|
labels: { color: INACTIVE },
|
|
markers: { color: INACTIVE }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: TEXT },
|
|
errorBars: { color: TEXT },
|
|
notes: noteStyle(),
|
|
candlestick: {
|
|
downColor: AXIS,
|
|
line: { color: INACTIVE_SHAPE }
|
|
},
|
|
area: { opacity: 0.8 },
|
|
waterfall: { line: { color: AXIS } },
|
|
horizontalWaterfall: { line: { color: AXIS } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: SERIES,
|
|
axisDefaults: {
|
|
line: { color: AXIS },
|
|
labels: { color: TEXT },
|
|
minorGridLines: { color: AXIS_MINOR },
|
|
majorGridLines: { color: AXIS },
|
|
title: { color: TEXT },
|
|
crosshair: { color: INACTIVE },
|
|
notes: noteStyle()
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: PRIMARY },
|
|
scale: {
|
|
rangePlaceholderColor: AXIS,
|
|
labels: { color: TEXT },
|
|
minorTicks: { color: TEXT },
|
|
majorTicks: { color: TEXT },
|
|
line: { color: TEXT }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: PRIMARY },
|
|
connectorDefaults: {
|
|
fill: { color: TEXT },
|
|
stroke: { color: DIAGRAM_HOVER },
|
|
hover: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: TEXT }
|
|
}
|
|
},
|
|
content: { color: TEXT }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
hover: {
|
|
fill: { color: INACTIVE_SHAPE },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
fill: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: INACTIVE_SHAPE } },
|
|
connectionDefaults: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
content: { color: INACTIVE_SHAPE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
},
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
treeMap: { colors: fuse(SERIES, SERIES_LIGHT) }
|
|
});
|
|
}());
|
|
(function () {
|
|
var TEXT = '#4e4e4e';
|
|
var INACTIVE = '#7f7f7f';
|
|
var INACTIVE_SHAPE = '#bdbdbd';
|
|
var AXIS = '#c8c8c8';
|
|
var AXIS_MINOR = '#e5e5e5';
|
|
var SERIES = [
|
|
'#0072c6',
|
|
'#5db2ff',
|
|
'#008a17',
|
|
'#82ba00',
|
|
'#ff8f32',
|
|
'#ac193d'
|
|
];
|
|
var SERIES_LIGHT = [
|
|
'#cbe2f3',
|
|
'#deeffe',
|
|
'#cbe7d0',
|
|
'#e5f0cb',
|
|
'#fee8d5',
|
|
'#eed0d7'
|
|
];
|
|
var PRIMARY = SERIES[0];
|
|
var DIAGRAM_HOVER = WHITE;
|
|
function noteStyle() {
|
|
return {
|
|
icon: {
|
|
background: '#00b0ff',
|
|
border: { color: '#00b0ff' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: AXIS }
|
|
};
|
|
}
|
|
registerTheme('office365', {
|
|
chart: {
|
|
title: { color: TEXT },
|
|
legend: {
|
|
labels: { color: TEXT },
|
|
inactiveItems: {
|
|
labels: { color: INACTIVE },
|
|
markers: { color: INACTIVE }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: TEXT },
|
|
errorBars: { color: TEXT },
|
|
notes: noteStyle(),
|
|
candlestick: {
|
|
downColor: AXIS,
|
|
line: { color: INACTIVE_SHAPE }
|
|
},
|
|
area: { opacity: 0.8 },
|
|
waterfall: { line: { color: AXIS } },
|
|
horizontalWaterfall: { line: { color: AXIS } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: SERIES,
|
|
axisDefaults: {
|
|
line: { color: AXIS },
|
|
labels: { color: TEXT },
|
|
minorGridLines: { color: AXIS_MINOR },
|
|
majorGridLines: { color: AXIS },
|
|
title: { color: TEXT },
|
|
crosshair: { color: INACTIVE },
|
|
notes: noteStyle()
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: PRIMARY },
|
|
scale: {
|
|
rangePlaceholderColor: AXIS,
|
|
labels: { color: TEXT },
|
|
minorTicks: { color: TEXT },
|
|
majorTicks: { color: TEXT },
|
|
line: { color: TEXT }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: PRIMARY },
|
|
connectorDefaults: {
|
|
fill: { color: TEXT },
|
|
stroke: { color: DIAGRAM_HOVER },
|
|
hover: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: TEXT }
|
|
}
|
|
},
|
|
content: { color: TEXT }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
hover: {
|
|
fill: { color: INACTIVE_SHAPE },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
fill: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: INACTIVE_SHAPE } },
|
|
connectionDefaults: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
content: { color: INACTIVE_SHAPE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
},
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
treeMap: { colors: fuse(SERIES, SERIES_LIGHT) }
|
|
});
|
|
}());
|
|
(function () {
|
|
var TEXT = '#32364c';
|
|
var INACTIVE = '#7f7f7f';
|
|
var INACTIVE_SHAPE = '#bdbdbd';
|
|
var AXIS = '#dfe0e1';
|
|
var AXIS_MINOR = '#dfe0e1';
|
|
var SERIES = [
|
|
'#ff4350',
|
|
'#ff9ea5',
|
|
'#00acc1',
|
|
'#80deea',
|
|
'#ffbf46',
|
|
'#ffd78c'
|
|
];
|
|
var SERIES_LIGHT = [
|
|
'#ffd9dc',
|
|
'#ffeced',
|
|
'#cceef3',
|
|
'#e6f8fb',
|
|
'#fff2da',
|
|
'#fff7e8'
|
|
];
|
|
var PRIMARY = SERIES[0];
|
|
var DIAGRAM_HOVER = WHITE;
|
|
function noteStyle() {
|
|
return {
|
|
icon: {
|
|
background: '#007cc0',
|
|
border: { color: '#007cc0' }
|
|
},
|
|
label: { color: '#ffffff' },
|
|
line: { color: AXIS }
|
|
};
|
|
}
|
|
registerTheme('nova', {
|
|
chart: {
|
|
title: { color: TEXT },
|
|
legend: {
|
|
labels: { color: TEXT },
|
|
inactiveItems: {
|
|
labels: { color: INACTIVE },
|
|
markers: { color: INACTIVE }
|
|
}
|
|
},
|
|
seriesDefaults: {
|
|
labels: { color: TEXT },
|
|
errorBars: { color: TEXT },
|
|
notes: noteStyle(),
|
|
candlestick: {
|
|
downColor: AXIS,
|
|
line: { color: INACTIVE_SHAPE }
|
|
},
|
|
area: { opacity: 0.8 },
|
|
waterfall: { line: { color: AXIS } },
|
|
horizontalWaterfall: { line: { color: AXIS } },
|
|
overlay: { gradient: 'none' },
|
|
border: { _brightness: 1 }
|
|
},
|
|
seriesColors: SERIES,
|
|
axisDefaults: {
|
|
line: { color: AXIS },
|
|
labels: { color: TEXT },
|
|
minorGridLines: { color: AXIS_MINOR },
|
|
majorGridLines: { color: AXIS },
|
|
title: { color: TEXT },
|
|
crosshair: { color: TEXT },
|
|
notes: noteStyle()
|
|
}
|
|
},
|
|
gauge: {
|
|
pointer: { color: PRIMARY },
|
|
scale: {
|
|
rangePlaceholderColor: AXIS,
|
|
labels: { color: TEXT },
|
|
minorTicks: { color: TEXT },
|
|
majorTicks: { color: TEXT },
|
|
line: { color: TEXT }
|
|
}
|
|
},
|
|
diagram: {
|
|
shapeDefaults: {
|
|
fill: { color: PRIMARY },
|
|
connectorDefaults: {
|
|
fill: { color: TEXT },
|
|
stroke: { color: DIAGRAM_HOVER },
|
|
hover: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: TEXT }
|
|
}
|
|
},
|
|
content: { color: TEXT }
|
|
},
|
|
editable: {
|
|
resize: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
hover: {
|
|
fill: { color: INACTIVE_SHAPE },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
rotate: {
|
|
thumb: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
fill: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
selectable: { stroke: { color: INACTIVE_SHAPE } },
|
|
connectionDefaults: {
|
|
stroke: { color: INACTIVE_SHAPE },
|
|
content: { color: INACTIVE_SHAPE },
|
|
selection: {
|
|
handles: {
|
|
fill: { color: DIAGRAM_HOVER },
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
},
|
|
stroke: { color: INACTIVE_SHAPE }
|
|
}
|
|
}
|
|
},
|
|
treeMap: { colors: fuse(SERIES, SERIES_LIGHT) }
|
|
});
|
|
}());
|
|
function fuse(arr1, arr2) {
|
|
return $.map(arr1, function (item, index) {
|
|
return [[
|
|
item,
|
|
arr2[index]
|
|
]];
|
|
});
|
|
}
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.chart', [
|
|
'kendo.color',
|
|
'kendo.data',
|
|
'kendo.dataviz.core',
|
|
'kendo.dataviz.themes',
|
|
'kendo.drawing',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.chart',
|
|
name: 'Chart',
|
|
category: 'dataviz',
|
|
description: 'The Chart widget uses modern browser technologies to render high-quality data visualizations in the browser.',
|
|
depends: [
|
|
'data',
|
|
'userevents',
|
|
'drawing',
|
|
'dataviz.core',
|
|
'dataviz.themes'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'dataviz.chart-polar',
|
|
name: 'Polar & Radar',
|
|
description: 'Support for Polar and Radar charts.',
|
|
depends: ['dataviz.chart.polar'],
|
|
requireJS: false
|
|
},
|
|
{
|
|
id: 'dataviz.chart-funnel',
|
|
name: 'Funnel chart',
|
|
description: 'Support for Funnel chart.',
|
|
depends: ['dataviz.chart.funnel'],
|
|
requireJS: false
|
|
},
|
|
{
|
|
id: 'dataviz.chart-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export Chart as PDF',
|
|
depends: ['pdf']
|
|
}
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var each = $.each, isArray = $.isArray, isPlainObject = $.isPlainObject, map = $.map, math = Math, noop = $.noop, extend = $.extend, proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, Observable = kendo.Observable, DataSource = kendo.data.DataSource, Widget = kendo.ui.Widget, deepExtend = kendo.deepExtend, getter = kendo.getter, isFn = kendo.isFunction, template = kendo.template, dataviz = kendo.dataviz, Axis = dataviz.Axis, AxisLabel = dataviz.AxisLabel, Box2D = dataviz.Box2D, BoxElement = dataviz.BoxElement, ChartElement = dataviz.ChartElement, Color = kendo.drawing.Color, CurveProcessor = dataviz.CurveProcessor, FloatElement = dataviz.FloatElement, Note = dataviz.Note, LogarithmicAxis = dataviz.LogarithmicAxis, NumericAxis = dataviz.NumericAxis, Point2D = dataviz.Point2D, RootElement = dataviz.RootElement, Ring = dataviz.Ring, ShapeElement = dataviz.ShapeElement, ShapeBuilder = dataviz.ShapeBuilder, TextBox = dataviz.TextBox, Title = dataviz.Title, alignPathToPixel = dataviz.alignPathToPixel, autoFormat = dataviz.autoFormat, dateComparer = dataviz.dateComparer, getSpacing = dataviz.getSpacing, inArray = dataviz.inArray, interpolate = dataviz.interpolateValue, mwDelta = dataviz.mwDelta, round = dataviz.round, util = kendo.util, append = util.append, defined = util.defined, last = util.last, limitValue = util.limitValue, sparseArrayLimits = util.sparseArrayLimits, sparseArrayMin = util.sparseArrayMin, sparseArrayMax = util.sparseArrayMax, renderTemplate = util.renderTemplate, valueOrDefault = util.valueOrDefault, geom = dataviz.geometry, draw = dataviz.drawing;
|
|
var NS = '.kendoChart', ABOVE = 'above', AREA = 'area', AUTO = 'auto', FIT = 'fit', AXIS_LABEL_CLICK = dataviz.AXIS_LABEL_CLICK, BAR = 'bar', BAR_ALIGN_MIN_WIDTH = 6, BAR_BORDER_BRIGHTNESS = 0.8, BELOW = 'below', BLACK = '#000', BOTH = 'both', BOTTOM = 'bottom', BOX_PLOT = 'boxPlot', BUBBLE = 'bubble', BULLET = 'bullet', CANDLESTICK = 'candlestick', CATEGORY = 'category', CENTER = 'center', CHANGE = 'change', CIRCLE = 'circle', CONTEXTMENU_NS = 'contextmenu' + NS, CLIP = dataviz.CLIP, COLOR = 'color', COLUMN = 'column', COORD_PRECISION = dataviz.COORD_PRECISION, CROSS = 'cross', CSS_PREFIX = 'k-', CUSTOM = 'custom', DATABOUND = 'dataBound', DATE = 'date', DAYS = 'days', DEFAULT_FONT = dataviz.DEFAULT_FONT, DEFAULT_HEIGHT = dataviz.DEFAULT_HEIGHT, DEFAULT_PRECISION = dataviz.DEFAULT_PRECISION, DEFAULT_WIDTH = dataviz.DEFAULT_WIDTH, DEFAULT_ERROR_BAR_WIDTH = 4, DONUT = 'donut', DONUT_SECTOR_ANIM_DELAY = 50, DRAG = 'drag', DRAG_END = 'dragEnd', DRAG_START = 'dragStart', ERROR_LOW_FIELD = 'errorLow', ERROR_HIGH_FIELD = 'errorHigh', X_ERROR_LOW_FIELD = 'xErrorLow', X_ERROR_HIGH_FIELD = 'xErrorHigh', Y_ERROR_LOW_FIELD = 'yErrorLow', Y_ERROR_HIGH_FIELD = 'yErrorHigh', FADEIN = 'fadeIn', FIRST = 'first', FROM = 'from', FUNNEL = 'funnel', GLASS = 'glass', HORIZONTAL = 'horizontal', HORIZONTAL_WATERFALL = 'horizontalWaterfall', HOURS = 'hours', INITIAL_ANIMATION_DURATION = dataviz.INITIAL_ANIMATION_DURATION, INSIDE_BASE = 'insideBase', INSIDE_END = 'insideEnd', INTERPOLATE = 'interpolate', LEAVE = 'leave', LEFT = 'left', LEGEND_ITEM_CLICK = 'legendItemClick', LEGEND_ITEM_HOVER = 'legendItemHover', LINE = 'line', LINE_MARKER_SIZE = 8, LINEAR = 'linear', LOGARITHMIC = 'log', MAX = 'max', MAX_EXPAND_DEPTH = 5, MAX_VALUE = Number.MAX_VALUE, MIN = 'min', MIN_VALUE = -Number.MAX_VALUE, MINUTES = 'minutes', MONTHS = 'months', MOUSELEAVE_NS = 'mouseleave' + NS, MOUSEMOVE_TRACKING = 'mousemove.tracking', MOUSEOVER_NS = 'mouseover' + NS, MOUSEOUT_NS = 'mouseout' + NS, MOUSEMOVE_NS = 'mousemove' + NS, MOUSEMOVE_DELAY = 20, MOUSEWHEEL_DELAY = 150, MOUSEWHEEL_NS = 'DOMMouseScroll' + NS + ' mousewheel' + NS, NOTE_CLICK = dataviz.NOTE_CLICK, NOTE_HOVER = dataviz.NOTE_HOVER, NOTE_TEXT = 'noteText', OBJECT = 'object', OHLC = 'ohlc', OUTSIDE_END = 'outsideEnd', PIE = 'pie', PIE_SECTOR_ANIM_DELAY = 70, PLOT_AREA_CLICK = 'plotAreaClick', POINTER = 'pointer', RANGE_BAR = 'rangeBar', RANGE_COLUMN = 'rangeColumn', RENDER = 'render', RIGHT = 'right', ROUNDED_BEVEL = 'roundedBevel', ROUNDED_GLASS = 'roundedGlass', SCATTER = 'scatter', SCATTER_LINE = 'scatterLine', SECONDS = 'seconds', SELECT_START = 'selectStart', SELECT = 'select', SELECT_END = 'selectEnd', SERIES_CLICK = 'seriesClick', SERIES_HOVER = 'seriesHover', START_SCALE = 0.001, STEP = 'step', SMOOTH = 'smooth', STD_ERR = 'stderr', STD_DEV = 'stddev', STRING = 'string', SUMMARY_FIELD = 'summary', TIME_PER_SECOND = 1000, TIME_PER_MINUTE = 60 * TIME_PER_SECOND, TIME_PER_HOUR = 60 * TIME_PER_MINUTE, TIME_PER_DAY = 24 * TIME_PER_HOUR, TIME_PER_WEEK = 7 * TIME_PER_DAY, TIME_PER_MONTH = 31 * TIME_PER_DAY, TIME_PER_YEAR = 365 * TIME_PER_DAY, TIME_PER_UNIT = {
|
|
'years': TIME_PER_YEAR,
|
|
'months': TIME_PER_MONTH,
|
|
'weeks': TIME_PER_WEEK,
|
|
'days': TIME_PER_DAY,
|
|
'hours': TIME_PER_HOUR,
|
|
'minutes': TIME_PER_MINUTE,
|
|
'seconds': TIME_PER_SECOND
|
|
}, TO = 'to', TOP = 'top', TOOLTIP_ANIMATION_DURATION = 150, TOOLTIP_OFFSET = 5, TOOLTIP_SHOW_DELAY = 100, TOOLTIP_HIDE_DELAY = 100, TOOLTIP_INVERSE = 'chart-tooltip-inverse', VALUE = 'value', VERTICAL_AREA = 'verticalArea', VERTICAL_BULLET = 'verticalBullet', VERTICAL_LINE = 'verticalLine', WATERFALL = 'waterfall', WEEKS = 'weeks', WHITE = '#fff', X = 'x', Y = 'y', YEARS = 'years', ZERO = 'zero', ZOOM_ACCELERATION = 3, ZOOM_START = 'zoomStart', ZOOM = 'zoom', ZOOM_END = 'zoomEnd', BASE_UNITS = [
|
|
SECONDS,
|
|
MINUTES,
|
|
HOURS,
|
|
DAYS,
|
|
WEEKS,
|
|
MONTHS,
|
|
YEARS
|
|
], EQUALLY_SPACED_SERIES = [
|
|
BAR,
|
|
COLUMN,
|
|
OHLC,
|
|
CANDLESTICK,
|
|
BOX_PLOT,
|
|
BULLET,
|
|
RANGE_COLUMN,
|
|
RANGE_BAR,
|
|
WATERFALL,
|
|
HORIZONTAL_WATERFALL
|
|
];
|
|
var DateLabelFormats = {
|
|
seconds: 'HH:mm:ss',
|
|
minutes: 'HH:mm',
|
|
hours: 'HH:mm',
|
|
days: 'M/d',
|
|
weeks: 'M/d',
|
|
months: 'MMM \'yy',
|
|
years: 'yyyy'
|
|
};
|
|
var Chart = Widget.extend({
|
|
init: function (element, userOptions) {
|
|
var chart = this, options, dataSource;
|
|
kendo.destroy(element);
|
|
Widget.fn.init.call(chart, element);
|
|
chart.element.addClass(CSS_PREFIX + this.options.name.toLowerCase()).css('position', 'relative');
|
|
if (userOptions) {
|
|
dataSource = userOptions.dataSource;
|
|
userOptions.dataSource = undefined;
|
|
}
|
|
options = deepExtend({}, chart.options, userOptions);
|
|
chart._originalOptions = deepExtend({}, options);
|
|
chart._initTheme(options);
|
|
chart._initSurface();
|
|
chart.bind(chart.events, chart.options);
|
|
chart.wrapper = chart.element;
|
|
if (userOptions) {
|
|
userOptions.dataSource = dataSource;
|
|
}
|
|
chart._initDataSource(userOptions);
|
|
kendo.notify(chart, dataviz.ui);
|
|
},
|
|
_initTheme: function (options) {
|
|
var chart = this, themes = dataviz.ui.themes || {}, themeName = options.theme, theme = themes[themeName] || themes[themeName.toLowerCase()], themeOptions = themeName && theme ? theme.chart : {}, seriesCopies = [], series = options.series || [], i;
|
|
for (i = 0; i < series.length; i++) {
|
|
seriesCopies.push($.extend({}, series[i]));
|
|
}
|
|
options.series = seriesCopies;
|
|
resolveAxisAliases(options);
|
|
chart._applyDefaults(options, themeOptions);
|
|
if (options.seriesColors === null) {
|
|
options.seriesColors = undefined;
|
|
}
|
|
chart.options = deepExtend({}, themeOptions, options);
|
|
applySeriesColors(chart.options);
|
|
},
|
|
_initDataSource: function (userOptions) {
|
|
var chart = this, dataSource = (userOptions || {}).dataSource;
|
|
chart._dataChangeHandler = proxy(chart._onDataChanged, chart);
|
|
chart.dataSource = DataSource.create(dataSource).bind(CHANGE, chart._dataChangeHandler);
|
|
chart._bindCategories();
|
|
if (dataSource) {
|
|
chart._hasDataSource = true;
|
|
}
|
|
preloadFonts(userOptions, function () {
|
|
chart._redraw();
|
|
chart._attachEvents();
|
|
});
|
|
if (dataSource) {
|
|
if (chart.options.autoBind) {
|
|
chart.dataSource.fetch();
|
|
}
|
|
}
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var chart = this;
|
|
chart.dataSource.unbind(CHANGE, chart._dataChangeHandler);
|
|
chart.dataSource = dataSource = DataSource.create(dataSource);
|
|
chart._hasDataSource = true;
|
|
chart._hasData = false;
|
|
dataSource.bind(CHANGE, chart._dataChangeHandler);
|
|
if (chart.options.autoBind) {
|
|
dataSource.fetch();
|
|
}
|
|
},
|
|
events: [
|
|
DATABOUND,
|
|
SERIES_CLICK,
|
|
SERIES_HOVER,
|
|
AXIS_LABEL_CLICK,
|
|
LEGEND_ITEM_CLICK,
|
|
LEGEND_ITEM_HOVER,
|
|
PLOT_AREA_CLICK,
|
|
DRAG_START,
|
|
DRAG,
|
|
DRAG_END,
|
|
ZOOM_START,
|
|
ZOOM,
|
|
ZOOM_END,
|
|
SELECT_START,
|
|
SELECT,
|
|
SELECT_END,
|
|
NOTE_CLICK,
|
|
NOTE_HOVER,
|
|
RENDER
|
|
],
|
|
items: function () {
|
|
return $();
|
|
},
|
|
options: {
|
|
name: 'Chart',
|
|
renderAs: '',
|
|
theme: 'default',
|
|
chartArea: {},
|
|
legend: {
|
|
visible: true,
|
|
labels: {}
|
|
},
|
|
categoryAxis: {},
|
|
autoBind: true,
|
|
seriesDefaults: {
|
|
type: COLUMN,
|
|
data: [],
|
|
highlight: { visible: true },
|
|
labels: {},
|
|
negativeValues: { visible: false }
|
|
},
|
|
series: [],
|
|
seriesColors: null,
|
|
tooltip: { visible: false },
|
|
transitions: true,
|
|
valueAxis: {},
|
|
plotArea: {},
|
|
title: {},
|
|
xAxis: {},
|
|
yAxis: {},
|
|
panes: [{}],
|
|
pannable: false,
|
|
zoomable: false
|
|
},
|
|
refresh: function () {
|
|
var chart = this;
|
|
chart._applyDefaults(chart.options);
|
|
applySeriesColors(chart.options);
|
|
chart._bindSeries();
|
|
chart._bindCategories();
|
|
chart.trigger(DATABOUND);
|
|
chart._redraw();
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.element);
|
|
},
|
|
_resize: function () {
|
|
var t = this.options.transitions;
|
|
this.options.transitions = false;
|
|
this._redraw();
|
|
this.options.transitions = t;
|
|
},
|
|
redraw: function (paneName) {
|
|
var chart = this, pane, plotArea;
|
|
chart._applyDefaults(chart.options);
|
|
applySeriesColors(chart.options);
|
|
if (paneName) {
|
|
plotArea = chart._model._plotArea;
|
|
pane = plotArea.findPane(paneName);
|
|
plotArea.redraw(pane);
|
|
} else {
|
|
chart._redraw();
|
|
}
|
|
},
|
|
getAxis: function (name) {
|
|
var axes = this._plotArea.axes;
|
|
for (var idx = 0; idx < axes.length; idx++) {
|
|
if (axes[idx].options.name === name) {
|
|
return new ChartAxis(axes[idx]);
|
|
}
|
|
}
|
|
},
|
|
toggleHighlight: function (show, options) {
|
|
var plotArea = this._plotArea;
|
|
var highlight = this._highlight;
|
|
var firstSeries = (plotArea.srcSeries || plotArea.series || [])[0];
|
|
var seriesName, categoryName, points;
|
|
if (isPlainObject(options)) {
|
|
seriesName = options.series;
|
|
categoryName = options.category;
|
|
} else {
|
|
seriesName = categoryName = options;
|
|
}
|
|
if (firstSeries.type === DONUT) {
|
|
points = pointByCategoryName(plotArea.pointsBySeriesName(seriesName), categoryName);
|
|
} else if (firstSeries.type === PIE || firstSeries.type === FUNNEL) {
|
|
points = pointByCategoryName((plotArea.charts[0] || {}).points, categoryName);
|
|
} else {
|
|
points = plotArea.pointsBySeriesName(seriesName);
|
|
}
|
|
if (points) {
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
highlight.togglePointHighlight(points[idx], show);
|
|
}
|
|
}
|
|
},
|
|
_initSurface: function () {
|
|
var surface = this.surface;
|
|
var wrap = this._surfaceWrap();
|
|
var chartArea = this.options.chartArea;
|
|
if (chartArea.width) {
|
|
wrap.css('width', chartArea.width);
|
|
}
|
|
if (chartArea.height) {
|
|
wrap.css('height', chartArea.height);
|
|
}
|
|
if (!surface || surface.options.type !== this.options.renderAs) {
|
|
if (surface) {
|
|
surface.destroy();
|
|
}
|
|
this.surface = draw.Surface.create(wrap, { type: this.options.renderAs });
|
|
} else {
|
|
this.surface.clear();
|
|
this.surface.resize();
|
|
}
|
|
},
|
|
_surfaceWrap: function () {
|
|
return this.element;
|
|
},
|
|
_redraw: function () {
|
|
var chart = this, model = chart._getModel(), view;
|
|
chart._destroyView();
|
|
chart._model = model;
|
|
chart._plotArea = model._plotArea;
|
|
model.renderVisual();
|
|
if (this.options.transitions !== false) {
|
|
model.traverse(function (element) {
|
|
if (element.animation) {
|
|
element.animation.setup();
|
|
}
|
|
});
|
|
}
|
|
chart._initSurface();
|
|
chart.surface.draw(model.visual);
|
|
if (this.options.transitions !== false) {
|
|
model.traverse(function (element) {
|
|
if (element.animation) {
|
|
element.animation.play();
|
|
}
|
|
});
|
|
}
|
|
chart._tooltip = chart._createTooltip();
|
|
chart._highlight = new Highlight(view);
|
|
chart._setupSelection();
|
|
chart._createPannable();
|
|
chart._createZoomSelection();
|
|
chart._createMousewheelZoom();
|
|
if (!chart._hasDataSource || chart._hasData || !chart.options.autoBind) {
|
|
chart.trigger(RENDER);
|
|
}
|
|
},
|
|
exportVisual: function (options) {
|
|
var visual;
|
|
if (options && (options.width || options.height)) {
|
|
var chartArea = this.options.chartArea;
|
|
var originalChartArea = this._originalOptions.chartArea;
|
|
deepExtend(chartArea, options);
|
|
var model = this._getModel();
|
|
chartArea.width = originalChartArea.width;
|
|
chartArea.height = originalChartArea.height;
|
|
model.renderVisual();
|
|
visual = model.visual;
|
|
} else {
|
|
visual = this.surface.exportVisual();
|
|
}
|
|
return visual;
|
|
},
|
|
_sharedTooltip: function () {
|
|
var chart = this, options = chart.options;
|
|
return chart._plotArea instanceof CategoricalPlotArea && options.tooltip.shared;
|
|
},
|
|
_createPannable: function () {
|
|
var options = this.options;
|
|
if (options.pannable !== false) {
|
|
this._pannable = new Pannable(this._plotArea, options.pannable);
|
|
}
|
|
},
|
|
_createZoomSelection: function () {
|
|
var zoomable = this.options.zoomable;
|
|
var selection = (zoomable || {}).selection;
|
|
if (zoomable !== false && selection !== false) {
|
|
this._zoomSelection = new ZoomSelection(this, selection);
|
|
}
|
|
},
|
|
_createMousewheelZoom: function () {
|
|
var zoomable = this.options.zoomable;
|
|
var mousewheel = (zoomable || {}).mousewheel;
|
|
if (zoomable !== false && mousewheel !== false) {
|
|
this._mousewheelZoom = new MousewheelZoom(this, mousewheel);
|
|
}
|
|
},
|
|
_createTooltip: function () {
|
|
var chart = this, options = chart.options, element = chart.element, tooltip;
|
|
if (chart._sharedTooltip()) {
|
|
tooltip = new SharedTooltip(element, chart._plotArea, options.tooltip);
|
|
} else {
|
|
tooltip = new Tooltip(element, options.tooltip);
|
|
}
|
|
tooltip.bind(LEAVE, proxy(chart._tooltipleave, chart));
|
|
return tooltip;
|
|
},
|
|
_tooltipleave: function () {
|
|
var chart = this, plotArea = chart._plotArea, highlight = chart._highlight;
|
|
plotArea.hideCrosshairs();
|
|
highlight.hide();
|
|
},
|
|
_applyDefaults: function (options, themeOptions) {
|
|
applyAxisDefaults(options, themeOptions);
|
|
applySeriesDefaults(options, themeOptions);
|
|
},
|
|
_getModel: function () {
|
|
var chart = this, options = chart.options, model = new RootElement(chart._modelOptions()), plotArea;
|
|
model.chart = chart;
|
|
Title.buildTitle(options.title, model);
|
|
plotArea = model._plotArea = chart._createPlotArea();
|
|
if (options.legend.visible) {
|
|
model.append(new Legend(plotArea.options.legend));
|
|
}
|
|
model.append(plotArea);
|
|
model.reflow();
|
|
return model;
|
|
},
|
|
_modelOptions: function () {
|
|
var chart = this, options = chart.options, element = chart.element, height = math.floor(element.height()), width = math.floor(element.width());
|
|
chart._size = null;
|
|
return deepExtend({
|
|
width: width || DEFAULT_WIDTH,
|
|
height: height || DEFAULT_HEIGHT,
|
|
transitions: options.transitions
|
|
}, options.chartArea);
|
|
},
|
|
_createPlotArea: function (skipSeries) {
|
|
var chart = this, options = chart.options;
|
|
return PlotAreaFactory.current.create(skipSeries ? [] : options.series, options);
|
|
},
|
|
_setupSelection: function () {
|
|
var chart = this, plotArea = chart._plotArea, axes = plotArea.axes, selections = chart._selections = [], selection, i, axis, min, max, options;
|
|
if (!chart._selectStartHandler) {
|
|
chart._selectStartHandler = proxy(chart._selectStart, chart);
|
|
chart._selectHandler = proxy(chart._select, chart);
|
|
chart._selectEndHandler = proxy(chart._selectEnd, chart);
|
|
}
|
|
for (i = 0; i < axes.length; i++) {
|
|
axis = axes[i];
|
|
options = axis.options;
|
|
if (axis instanceof CategoryAxis && options.select && !options.vertical) {
|
|
min = 0;
|
|
max = options.categories.length - 1;
|
|
if (axis instanceof DateCategoryAxis) {
|
|
min = options.categories[min];
|
|
max = options.categories[max];
|
|
}
|
|
if (!options.justified) {
|
|
if (axis instanceof DateCategoryAxis) {
|
|
max = addDuration(max, 1, options.baseUnit, options.weekStartDay);
|
|
} else {
|
|
max++;
|
|
}
|
|
}
|
|
selection = new Selection(chart, axis, deepExtend({
|
|
min: min,
|
|
max: max
|
|
}, options.select));
|
|
selection.bind(SELECT_START, chart._selectStartHandler);
|
|
selection.bind(SELECT, chart._selectHandler);
|
|
selection.bind(SELECT_END, chart._selectEndHandler);
|
|
selections.push(selection);
|
|
}
|
|
}
|
|
},
|
|
_selectStart: function (e) {
|
|
return this.trigger(SELECT_START, e);
|
|
},
|
|
_select: function (e) {
|
|
return this.trigger(SELECT, e);
|
|
},
|
|
_selectEnd: function (e) {
|
|
return this.trigger(SELECT_END, e);
|
|
},
|
|
_attachEvents: function () {
|
|
var chart = this, element = chart.element;
|
|
element.on(CONTEXTMENU_NS, proxy(chart._click, chart));
|
|
element.on(MOUSEOVER_NS, proxy(chart._mouseover, chart));
|
|
element.on(MOUSEOUT_NS, proxy(chart._mouseout, chart));
|
|
element.on(MOUSEWHEEL_NS, proxy(chart._mousewheel, chart));
|
|
element.on(MOUSELEAVE_NS, proxy(chart._mouseleave, chart));
|
|
chart._mousemove = kendo.throttle(proxy(chart._mousemove, chart), MOUSEMOVE_DELAY);
|
|
if (chart._shouldAttachMouseMove()) {
|
|
element.on(MOUSEMOVE_NS, chart._mousemove);
|
|
}
|
|
if (kendo.UserEvents) {
|
|
chart._userEvents = new kendo.UserEvents(element, {
|
|
global: true,
|
|
filter: ':not(.k-selector)',
|
|
multiTouch: false,
|
|
fastTap: true,
|
|
tap: proxy(chart._tap, chart),
|
|
start: proxy(chart._start, chart),
|
|
move: proxy(chart._move, chart),
|
|
end: proxy(chart._end, chart)
|
|
});
|
|
}
|
|
},
|
|
_mouseout: function (e) {
|
|
var chart = this, element = chart._getChartElement(e);
|
|
if (element && element.leave) {
|
|
element.leave(chart, e);
|
|
}
|
|
},
|
|
_start: function (e) {
|
|
var chart = this, events = chart._events;
|
|
if (defined(events[DRAG_START] || events[DRAG] || events[DRAG_END])) {
|
|
chart._startNavigation(e, DRAG_START);
|
|
}
|
|
if (chart._pannable) {
|
|
chart._pannable.start(e);
|
|
}
|
|
if (chart._zoomSelection) {
|
|
if (chart._zoomSelection.start(e)) {
|
|
this.trigger(ZOOM_START, {
|
|
axisRanges: axisRanges(this._plotArea.axes),
|
|
originalEvent: e
|
|
});
|
|
}
|
|
}
|
|
},
|
|
_move: function (e) {
|
|
var chart = this, state = chart._navState, pannable = chart._pannable, axes, ranges = {}, i, currentAxis, axisName, axis, delta;
|
|
if (pannable) {
|
|
e.preventDefault();
|
|
ranges = pannable.move(e);
|
|
if (ranges && !chart.trigger(DRAG, {
|
|
axisRanges: ranges,
|
|
originalEvent: e
|
|
})) {
|
|
pannable.pan();
|
|
}
|
|
} else if (state) {
|
|
e.preventDefault();
|
|
axes = state.axes;
|
|
for (i = 0; i < axes.length; i++) {
|
|
currentAxis = axes[i];
|
|
axisName = currentAxis.options.name;
|
|
if (axisName) {
|
|
axis = currentAxis.options.vertical ? e.y : e.x;
|
|
delta = axis.startLocation - axis.location;
|
|
if (delta !== 0) {
|
|
ranges[currentAxis.options.name] = currentAxis.translateRange(delta);
|
|
}
|
|
}
|
|
}
|
|
state.axisRanges = ranges;
|
|
chart.trigger(DRAG, {
|
|
axisRanges: ranges,
|
|
originalEvent: e
|
|
});
|
|
}
|
|
if (chart._zoomSelection) {
|
|
chart._zoomSelection.move(e);
|
|
}
|
|
},
|
|
_end: function (e) {
|
|
this._endNavigation(e, DRAG_END);
|
|
if (this._zoomSelection) {
|
|
var ranges = this._zoomSelection.end(e);
|
|
if (ranges && !this.trigger(ZOOM, {
|
|
axisRanges: ranges,
|
|
originalEvent: e
|
|
})) {
|
|
this._zoomSelection.zoom();
|
|
this.trigger(ZOOM_END, {
|
|
axisRanges: ranges,
|
|
originalEvent: e
|
|
});
|
|
}
|
|
}
|
|
if (this._pannable) {
|
|
this._pannable.end(e);
|
|
}
|
|
},
|
|
_mousewheel: function (e) {
|
|
var chart = this, origEvent = e.originalEvent, prevented, delta = mwDelta(e), totalDelta, state = chart._navState, axes, i, currentAxis, axisName, ranges = {}, mousewheelZoom = chart._mousewheelZoom;
|
|
if (mousewheelZoom) {
|
|
var args = {
|
|
delta: delta,
|
|
axisRanges: axisRanges(this._plotArea.axes),
|
|
originalEvent: e
|
|
};
|
|
if (!chart.trigger(ZOOM_START, args)) {
|
|
e.preventDefault();
|
|
args.axisRanges = ranges = mousewheelZoom.updateRanges(delta);
|
|
if (ranges && !chart.trigger(ZOOM, args)) {
|
|
mousewheelZoom.zoom();
|
|
chart.trigger(ZOOM_END, args);
|
|
}
|
|
}
|
|
} else {
|
|
if (!state) {
|
|
prevented = chart._startNavigation(origEvent, ZOOM_START);
|
|
if (!prevented) {
|
|
state = chart._navState;
|
|
}
|
|
}
|
|
if (state) {
|
|
totalDelta = state.totalDelta || delta;
|
|
state.totalDelta = totalDelta + delta;
|
|
axes = chart._navState.axes;
|
|
for (i = 0; i < axes.length; i++) {
|
|
currentAxis = axes[i];
|
|
axisName = currentAxis.options.name;
|
|
if (axisName) {
|
|
ranges[axisName] = currentAxis.scaleRange(-totalDelta);
|
|
}
|
|
}
|
|
chart.trigger(ZOOM, {
|
|
delta: delta,
|
|
axisRanges: ranges,
|
|
originalEvent: e
|
|
});
|
|
if (chart._mwTimeout) {
|
|
clearTimeout(chart._mwTimeout);
|
|
}
|
|
chart._mwTimeout = setTimeout(function () {
|
|
chart._endNavigation(e, ZOOM_END);
|
|
}, MOUSEWHEEL_DELAY);
|
|
}
|
|
}
|
|
},
|
|
_startNavigation: function (e, chartEvent) {
|
|
var chart = this, coords = chart._eventCoordinates(e), plotArea = chart._model._plotArea, pane = plotArea.findPointPane(coords), axes = plotArea.axes.slice(0), i, currentAxis, inAxis = false, prevented;
|
|
if (!pane) {
|
|
return;
|
|
}
|
|
for (i = 0; i < axes.length; i++) {
|
|
currentAxis = axes[i];
|
|
if (currentAxis.box.containsPoint(coords)) {
|
|
inAxis = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!inAxis && plotArea.backgroundBox().containsPoint(coords)) {
|
|
prevented = chart.trigger(chartEvent, {
|
|
axisRanges: axisRanges(axes),
|
|
originalEvent: e
|
|
});
|
|
if (prevented) {
|
|
chart._userEvents.cancel();
|
|
} else {
|
|
chart._suppressHover = true;
|
|
chart._unsetActivePoint();
|
|
chart._navState = {
|
|
pane: pane,
|
|
axes: axes
|
|
};
|
|
}
|
|
}
|
|
},
|
|
_endNavigation: function (e, chartEvent) {
|
|
var chart = this;
|
|
if (chart._navState) {
|
|
chart.trigger(chartEvent, {
|
|
axisRanges: chart._navState.axisRanges,
|
|
originalEvent: e
|
|
});
|
|
chart._suppressHover = false;
|
|
chart._navState = null;
|
|
}
|
|
},
|
|
_getChartElement: function (e, match) {
|
|
var element = this.surface.eventTarget(e);
|
|
if (!element) {
|
|
return;
|
|
}
|
|
var chartElement;
|
|
while (element && !chartElement) {
|
|
chartElement = element.chartElement;
|
|
element = element.parent;
|
|
}
|
|
if (chartElement) {
|
|
if (chartElement.aliasFor) {
|
|
chartElement = chartElement.aliasFor(e, this._eventCoordinates(e));
|
|
}
|
|
if (match) {
|
|
chartElement = chartElement.closest(match);
|
|
}
|
|
return chartElement;
|
|
}
|
|
},
|
|
_eventCoordinates: function (e) {
|
|
var chart = this, isTouch = defined((e.x || {}).client), clientX = isTouch ? e.x.client : e.clientX, clientY = isTouch ? e.y.client : e.clientY;
|
|
return chart._toModelCoordinates(clientX, clientY);
|
|
},
|
|
_toModelCoordinates: function (clientX, clientY) {
|
|
var element = this.element, offset = element.offset(), paddingLeft = parseInt(element.css('paddingLeft'), 10), paddingTop = parseInt(element.css('paddingTop'), 10), win = $(window);
|
|
return new Point2D(clientX - offset.left - paddingLeft + win.scrollLeft(), clientY - offset.top - paddingTop + win.scrollTop());
|
|
},
|
|
_tap: function (e) {
|
|
var chart = this, element = chart._getChartElement(e);
|
|
if (chart._activePoint === element) {
|
|
chart._click(e);
|
|
} else {
|
|
if (!chart._startHover(e)) {
|
|
chart._unsetActivePoint();
|
|
}
|
|
chart._click(e);
|
|
}
|
|
},
|
|
_click: function (e) {
|
|
var chart = this, element = chart._getChartElement(e);
|
|
while (element) {
|
|
if (element.click) {
|
|
element.click(chart, e);
|
|
}
|
|
element = element.parent;
|
|
}
|
|
},
|
|
_startHover: function (e) {
|
|
var chart = this, element = chart._getChartElement(e), tooltip = chart._tooltip, highlight = chart._highlight, tooltipOptions = chart.options.tooltip, point;
|
|
if (chart._suppressHover || !highlight || highlight.isHighlighted(element) || chart._sharedTooltip()) {
|
|
return;
|
|
}
|
|
point = chart._getChartElement(e, function (element) {
|
|
return element.hover;
|
|
});
|
|
if (point && !point.hover(chart, e)) {
|
|
chart._activePoint = point;
|
|
tooltipOptions = deepExtend({}, tooltipOptions, point.options.tooltip);
|
|
if (tooltipOptions.visible) {
|
|
tooltip.show(point);
|
|
}
|
|
highlight.show(point);
|
|
return point.tooltipTracking;
|
|
}
|
|
},
|
|
_mouseover: function (e) {
|
|
var chart = this;
|
|
if (chart._startHover(e)) {
|
|
$(document).on(MOUSEMOVE_TRACKING, proxy(chart._mouseMoveTracking, chart));
|
|
}
|
|
},
|
|
_mouseMoveTracking: function (e) {
|
|
var chart = this, options = chart.options, tooltip = chart._tooltip, highlight = chart._highlight, coords = chart._eventCoordinates(e), point = chart._activePoint, tooltipOptions, seriesPoint;
|
|
if (chart._plotArea.box.containsPoint(coords)) {
|
|
if (point && point.tooltipTracking && point.series && point.parent.getNearestPoint) {
|
|
seriesPoint = point.parent.getNearestPoint(coords.x, coords.y, point.seriesIx);
|
|
if (seriesPoint && seriesPoint != point) {
|
|
seriesPoint.hover(chart, e);
|
|
chart._activePoint = seriesPoint;
|
|
tooltipOptions = deepExtend({}, options.tooltip, point.options.tooltip);
|
|
if (tooltipOptions.visible) {
|
|
tooltip.show(seriesPoint);
|
|
}
|
|
highlight.show(seriesPoint);
|
|
}
|
|
}
|
|
} else {
|
|
$(document).off(MOUSEMOVE_TRACKING);
|
|
chart._unsetActivePoint();
|
|
}
|
|
},
|
|
_mousemove: function (e) {
|
|
var coords = this._eventCoordinates(e);
|
|
this._trackCrosshairs(coords);
|
|
if (this._sharedTooltip()) {
|
|
this._trackSharedTooltip(coords, e);
|
|
}
|
|
},
|
|
_trackCrosshairs: function (coords) {
|
|
var crosshairs = this._plotArea.crosshairs, i, current;
|
|
for (i = 0; i < crosshairs.length; i++) {
|
|
current = crosshairs[i];
|
|
if (current.box.containsPoint(coords)) {
|
|
current.showAt(coords);
|
|
} else {
|
|
current.hide();
|
|
}
|
|
}
|
|
},
|
|
_trackSharedTooltip: function (coords, e) {
|
|
var chart = this, options = chart.options, plotArea = chart._plotArea, categoryAxis = plotArea.categoryAxis, tooltip = chart._tooltip, tooltipOptions = options.tooltip, highlight = chart._highlight, index, points;
|
|
if (plotArea.box.containsPoint(coords)) {
|
|
index = categoryAxis.pointCategoryIndex(coords);
|
|
if (index !== chart._tooltipCategoryIx) {
|
|
points = plotArea.pointsByCategoryIndex(index);
|
|
var pointArgs = $.map(points, function (point) {
|
|
return point.eventArgs(e);
|
|
});
|
|
var hoverArgs = pointArgs[0] || {};
|
|
hoverArgs.categoryPoints = pointArgs;
|
|
if (points.length > 0 && !this.trigger(SERIES_HOVER, hoverArgs)) {
|
|
if (tooltipOptions.visible) {
|
|
tooltip.showAt(points, coords);
|
|
}
|
|
highlight.show(points);
|
|
} else {
|
|
tooltip.hide();
|
|
}
|
|
chart._tooltipCategoryIx = index;
|
|
}
|
|
}
|
|
},
|
|
_mouseleave: function (e) {
|
|
var chart = this, plotArea = chart._plotArea, tooltip = chart._tooltip, highlight = chart._highlight, target = e.relatedTarget;
|
|
if (!(target && $(target).closest(tooltip.element).length)) {
|
|
chart._mousemove.cancel();
|
|
plotArea.hideCrosshairs();
|
|
highlight.hide();
|
|
setTimeout(proxy(tooltip.hide, tooltip), TOOLTIP_HIDE_DELAY);
|
|
chart._tooltipCategoryIx = null;
|
|
}
|
|
},
|
|
_unsetActivePoint: function () {
|
|
var chart = this, tooltip = chart._tooltip, highlight = chart._highlight;
|
|
chart._activePoint = null;
|
|
if (tooltip) {
|
|
tooltip.hide();
|
|
}
|
|
if (highlight) {
|
|
highlight.hide();
|
|
}
|
|
},
|
|
_onDataChanged: function () {
|
|
var chart = this, options = chart.options, series = chart._sourceSeries || options.series, seriesIx, seriesLength = series.length, data = chart.dataSource.view(), grouped = (chart.dataSource.group() || []).length > 0, processedSeries = [], currentSeries;
|
|
for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
if (chart._isBindable(currentSeries) && grouped) {
|
|
append(processedSeries, groupSeries(currentSeries, data));
|
|
} else {
|
|
processedSeries.push(currentSeries || []);
|
|
}
|
|
}
|
|
chart._sourceSeries = series;
|
|
options.series = processedSeries;
|
|
applySeriesColors(chart.options);
|
|
chart._bindSeries();
|
|
chart._bindCategories();
|
|
chart._hasData = true;
|
|
chart._deferRedraw();
|
|
},
|
|
_deferRedraw: function () {
|
|
var chart = this;
|
|
if (kendo.support.vml) {
|
|
chart._clearRedrawTimeout();
|
|
chart._redrawTimeout = setTimeout(function () {
|
|
if (!chart.surface) {
|
|
return;
|
|
}
|
|
chart.trigger(DATABOUND);
|
|
chart._redraw();
|
|
}, 0);
|
|
} else {
|
|
chart.trigger(DATABOUND);
|
|
chart._redraw();
|
|
}
|
|
},
|
|
_clearRedrawTimeout: function () {
|
|
if (this._redrawTimeout) {
|
|
clearInterval(this._redrawTimeout);
|
|
this._redrawTimeout = null;
|
|
}
|
|
},
|
|
_bindSeries: function () {
|
|
var chart = this, data = chart.dataSource.view(), series = chart.options.series, seriesIx, seriesLength = series.length, currentSeries, groupIx, seriesData;
|
|
for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
if (chart._isBindable(currentSeries)) {
|
|
groupIx = currentSeries._groupIx;
|
|
seriesData = defined(groupIx) ? (data[groupIx] || {}).items : data;
|
|
if (currentSeries.autoBind !== false) {
|
|
currentSeries.data = seriesData;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_bindCategories: function () {
|
|
var chart = this, data = chart.dataSource.view() || [], grouped = (chart.dataSource.group() || []).length > 0, categoriesData = data, options = chart.options, definitions = [].concat(options.categoryAxis), axisIx, axis;
|
|
if (grouped) {
|
|
if (data.length) {
|
|
categoriesData = data[0].items;
|
|
}
|
|
}
|
|
for (axisIx = 0; axisIx < definitions.length; axisIx++) {
|
|
axis = definitions[axisIx];
|
|
if (axis.autoBind !== false) {
|
|
chart._bindCategoryAxis(axis, categoriesData, axisIx);
|
|
}
|
|
}
|
|
},
|
|
_bindCategoryAxis: function (axis, data, axisIx) {
|
|
var count = (data || []).length, categoryIx, category, row;
|
|
if (axis.field) {
|
|
axis.categories = [];
|
|
for (categoryIx = 0; categoryIx < count; categoryIx++) {
|
|
row = data[categoryIx];
|
|
category = getField(axis.field, row);
|
|
if (categoryIx === 0) {
|
|
axis.categories = [category];
|
|
axis.dataItems = [row];
|
|
} else {
|
|
axis.categories.push(category);
|
|
axis.dataItems.push(row);
|
|
}
|
|
}
|
|
} else {
|
|
this._bindCategoryAxisFromSeries(axis, axisIx);
|
|
}
|
|
},
|
|
_bindCategoryAxisFromSeries: function (axis, axisIx) {
|
|
var chart = this, items = [], result, series = chart.options.series, seriesLength = series.length, seriesIx, s, onAxis, data, dataIx, dataLength, dataRow, category, uniqueCategories = {}, getFn, dateAxis;
|
|
for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
|
|
s = series[seriesIx];
|
|
onAxis = s.categoryAxis === axis.name || !s.categoryAxis && axisIx === 0;
|
|
data = s.data;
|
|
dataLength = data.length;
|
|
if (s.categoryField && onAxis && dataLength > 0) {
|
|
dateAxis = isDateAxis(axis, getField(s.categoryField, data[0]));
|
|
getFn = dateAxis ? getDateField : getField;
|
|
for (dataIx = 0; dataIx < dataLength; dataIx++) {
|
|
dataRow = data[dataIx];
|
|
category = getFn(s.categoryField, dataRow);
|
|
if (dateAxis || !uniqueCategories[category]) {
|
|
items.push([
|
|
category,
|
|
dataRow
|
|
]);
|
|
if (!dateAxis) {
|
|
uniqueCategories[category] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (items.length > 0) {
|
|
if (dateAxis) {
|
|
items = uniqueDates(items, function (a, b) {
|
|
return dateComparer(a[0], b[0]);
|
|
});
|
|
}
|
|
result = transpose(items);
|
|
axis.categories = result[0];
|
|
axis.dataItems = result[1];
|
|
}
|
|
},
|
|
_isBindable: function (series) {
|
|
var valueFields = SeriesBinder.current.valueFields(series), result = true, field, i;
|
|
for (i = 0; i < valueFields.length; i++) {
|
|
field = valueFields[i];
|
|
if (field === VALUE) {
|
|
field = 'field';
|
|
} else {
|
|
field = field + 'Field';
|
|
}
|
|
if (!defined(series[field])) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_legendItemClick: function (seriesIndex, pointIndex) {
|
|
var chart = this, plotArea = chart._plotArea, currentSeries = (plotArea.srcSeries || plotArea.series)[seriesIndex], originalSeries = (chart._sourceSeries || [])[seriesIndex] || currentSeries, transitionsState, visible, point;
|
|
if (inArray(currentSeries.type, [
|
|
PIE,
|
|
DONUT,
|
|
FUNNEL
|
|
])) {
|
|
point = originalSeries.data[pointIndex];
|
|
if (!defined(point.visible)) {
|
|
visible = false;
|
|
} else {
|
|
visible = !point.visible;
|
|
}
|
|
point.visible = visible;
|
|
} else {
|
|
visible = !originalSeries.visible;
|
|
originalSeries.visible = visible;
|
|
currentSeries.visible = visible;
|
|
}
|
|
if (chart.options.transitions) {
|
|
chart.options.transitions = false;
|
|
transitionsState = true;
|
|
}
|
|
chart.redraw();
|
|
if (transitionsState) {
|
|
chart.options.transitions = true;
|
|
}
|
|
},
|
|
_legendItemHover: function (seriesIndex, pointIndex) {
|
|
var chart = this, plotArea = chart._plotArea, highlight = chart._highlight, currentSeries = (plotArea.srcSeries || plotArea.series)[seriesIndex], index, items;
|
|
if (inArray(currentSeries.type, [
|
|
PIE,
|
|
DONUT,
|
|
FUNNEL
|
|
])) {
|
|
index = pointIndex;
|
|
} else {
|
|
index = seriesIndex;
|
|
}
|
|
items = plotArea.pointsBySeriesIndex(index);
|
|
highlight.show(items);
|
|
},
|
|
_shouldAttachMouseMove: function () {
|
|
var chart = this;
|
|
return chart._plotArea.crosshairs.length || chart._tooltip && chart._sharedTooltip();
|
|
},
|
|
setOptions: function (options) {
|
|
var chart = this, dataSource = options.dataSource;
|
|
options.dataSource = undefined;
|
|
chart._originalOptions = deepExtend(chart._originalOptions, options);
|
|
chart.options = deepExtend({}, chart._originalOptions);
|
|
chart._sourceSeries = null;
|
|
$(document).off(MOUSEMOVE_NS);
|
|
Widget.fn._setEvents.call(chart, options);
|
|
chart._initTheme(chart.options);
|
|
if (dataSource) {
|
|
chart.setDataSource(dataSource);
|
|
}
|
|
if (chart._hasDataSource) {
|
|
chart._onDataChanged();
|
|
} else {
|
|
chart._bindCategories();
|
|
chart.redraw();
|
|
}
|
|
if (chart._shouldAttachMouseMove()) {
|
|
chart.element.on(MOUSEMOVE_NS, chart._mousemove);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var chart = this, dataSource = chart.dataSource;
|
|
chart.element.off(NS);
|
|
if (dataSource) {
|
|
dataSource.unbind(CHANGE, chart._dataChangeHandler);
|
|
}
|
|
$(document).off(MOUSEMOVE_TRACKING);
|
|
if (chart._userEvents) {
|
|
chart._userEvents.destroy();
|
|
}
|
|
chart._destroyView();
|
|
chart.surface.destroy();
|
|
chart.surface = null;
|
|
chart._clearRedrawTimeout();
|
|
Widget.fn.destroy.call(chart);
|
|
},
|
|
_destroyView: function () {
|
|
var chart = this, model = chart._model, selections = chart._selections;
|
|
if (model) {
|
|
model.destroy();
|
|
chart._model = null;
|
|
}
|
|
if (selections) {
|
|
while (selections.length > 0) {
|
|
selections.shift().destroy();
|
|
}
|
|
}
|
|
chart._unsetActivePoint();
|
|
if (chart._tooltip) {
|
|
chart._tooltip.destroy();
|
|
}
|
|
if (chart._highlight) {
|
|
chart._highlight.destroy();
|
|
}
|
|
if (chart._zoomSelection) {
|
|
chart._zoomSelection.destroy();
|
|
}
|
|
}
|
|
});
|
|
dataviz.ExportMixin.extend(Chart.fn);
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Chart.fn);
|
|
}
|
|
var PlotAreaFactory = Class.extend({
|
|
init: function () {
|
|
this._registry = [];
|
|
},
|
|
register: function (type, seriesTypes) {
|
|
this._registry.push({
|
|
type: type,
|
|
seriesTypes: seriesTypes
|
|
});
|
|
},
|
|
create: function (srcSeries, options) {
|
|
var registry = this._registry, match = registry[0], i, entry, series;
|
|
for (i = 0; i < registry.length; i++) {
|
|
entry = registry[i];
|
|
series = filterSeriesByType(srcSeries, entry.seriesTypes);
|
|
if (series.length > 0) {
|
|
match = entry;
|
|
break;
|
|
}
|
|
}
|
|
return new match.type(series, options);
|
|
}
|
|
});
|
|
PlotAreaFactory.current = new PlotAreaFactory();
|
|
var SeriesBinder = Class.extend({
|
|
init: function () {
|
|
this._valueFields = {};
|
|
this._otherFields = {};
|
|
this._nullValue = {};
|
|
this._undefinedValue = {};
|
|
},
|
|
register: function (seriesTypes, valueFields, otherFields) {
|
|
var binder = this, i, type;
|
|
valueFields = valueFields || [VALUE];
|
|
for (i = 0; i < seriesTypes.length; i++) {
|
|
type = seriesTypes[i];
|
|
binder._valueFields[type] = valueFields;
|
|
binder._otherFields[type] = otherFields;
|
|
binder._nullValue[type] = binder._makeValue(valueFields, null);
|
|
binder._undefinedValue[type] = binder._makeValue(valueFields, undefined);
|
|
}
|
|
},
|
|
canonicalFields: function (series) {
|
|
return this.valueFields(series).concat(this.otherFields(series));
|
|
},
|
|
valueFields: function (series) {
|
|
return this._valueFields[series.type] || [VALUE];
|
|
},
|
|
otherFields: function (series) {
|
|
return this._otherFields[series.type] || [VALUE];
|
|
},
|
|
bindPoint: function (series, pointIx, item) {
|
|
var binder = this, data = series.data, pointData = defined(item) ? item : data[pointIx], result = { valueFields: { value: pointData } }, fields, fieldData, srcValueFields, srcPointFields, valueFields = binder.valueFields(series), otherFields = binder._otherFields[series.type], value;
|
|
if (pointData === null) {
|
|
value = binder._nullValue[series.type];
|
|
} else if (!defined(pointData)) {
|
|
value = binder._undefinedValue[series.type];
|
|
} else if (isArray(pointData)) {
|
|
fieldData = pointData.slice(valueFields.length);
|
|
value = binder._bindFromArray(pointData, valueFields);
|
|
fields = binder._bindFromArray(fieldData, otherFields);
|
|
} else if (typeof pointData === OBJECT) {
|
|
srcValueFields = binder.sourceFields(series, valueFields);
|
|
srcPointFields = binder.sourceFields(series, otherFields);
|
|
value = binder._bindFromObject(pointData, valueFields, srcValueFields);
|
|
fields = binder._bindFromObject(pointData, otherFields, srcPointFields);
|
|
}
|
|
if (defined(value)) {
|
|
if (valueFields.length === 1) {
|
|
result.valueFields.value = value[valueFields[0]];
|
|
} else {
|
|
result.valueFields = value;
|
|
}
|
|
}
|
|
result.fields = fields || {};
|
|
return result;
|
|
},
|
|
_makeValue: function (fields, initialValue) {
|
|
var value = {}, i, length = fields.length, fieldName;
|
|
for (i = 0; i < length; i++) {
|
|
fieldName = fields[i];
|
|
value[fieldName] = initialValue;
|
|
}
|
|
return value;
|
|
},
|
|
_bindFromArray: function (array, fields) {
|
|
var value = {}, i, length;
|
|
if (fields) {
|
|
length = math.min(fields.length, array.length);
|
|
for (i = 0; i < length; i++) {
|
|
value[fields[i]] = array[i];
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
_bindFromObject: function (object, fields, srcFields) {
|
|
var value = {}, i, length, fieldName, srcFieldName;
|
|
if (fields) {
|
|
length = fields.length;
|
|
srcFields = srcFields || fields;
|
|
for (i = 0; i < length; i++) {
|
|
fieldName = fields[i];
|
|
srcFieldName = srcFields[i];
|
|
value[fieldName] = getField(srcFieldName, object);
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
sourceFields: function (series, canonicalFields) {
|
|
var i, length, fieldName, sourceFields, sourceFieldName;
|
|
if (canonicalFields) {
|
|
length = canonicalFields.length;
|
|
sourceFields = [];
|
|
for (i = 0; i < length; i++) {
|
|
fieldName = canonicalFields[i];
|
|
sourceFieldName = fieldName === VALUE ? 'field' : fieldName + 'Field';
|
|
sourceFields.push(series[sourceFieldName] || fieldName);
|
|
}
|
|
}
|
|
return sourceFields;
|
|
}
|
|
});
|
|
SeriesBinder.current = new SeriesBinder();
|
|
var BarLabel = ChartElement.extend({
|
|
init: function (content, options) {
|
|
var barLabel = this;
|
|
ChartElement.fn.init.call(barLabel, options);
|
|
this.textBox = new TextBox(content, barLabel.options);
|
|
barLabel.append(this.textBox);
|
|
},
|
|
options: {
|
|
position: OUTSIDE_END,
|
|
margin: getSpacing(3),
|
|
padding: getSpacing(4),
|
|
color: BLACK,
|
|
background: '',
|
|
border: {
|
|
width: 1,
|
|
color: ''
|
|
},
|
|
aboveAxis: true,
|
|
vertical: false,
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: INITIAL_ANIMATION_DURATION
|
|
},
|
|
zIndex: 2
|
|
},
|
|
createVisual: function () {
|
|
this.textBox.options.noclip = this.options.noclip;
|
|
},
|
|
reflow: function (targetBox) {
|
|
var barLabel = this, options = barLabel.options, vertical = options.vertical, aboveAxis = options.aboveAxis, text = barLabel.children[0], box = text.box, padding = text.options.padding;
|
|
text.options.align = vertical ? CENTER : LEFT;
|
|
text.options.vAlign = vertical ? TOP : CENTER;
|
|
if (options.position == INSIDE_END) {
|
|
if (vertical) {
|
|
text.options.vAlign = TOP;
|
|
if (!aboveAxis && box.height() < targetBox.height()) {
|
|
text.options.vAlign = BOTTOM;
|
|
}
|
|
} else {
|
|
text.options.align = aboveAxis ? RIGHT : LEFT;
|
|
}
|
|
} else if (options.position == CENTER) {
|
|
text.options.vAlign = CENTER;
|
|
text.options.align = CENTER;
|
|
} else if (options.position == INSIDE_BASE) {
|
|
if (vertical) {
|
|
text.options.vAlign = aboveAxis ? BOTTOM : TOP;
|
|
} else {
|
|
text.options.align = aboveAxis ? LEFT : RIGHT;
|
|
}
|
|
} else if (options.position == OUTSIDE_END) {
|
|
if (vertical) {
|
|
if (aboveAxis) {
|
|
targetBox = new Box2D(targetBox.x1, targetBox.y1 - box.height(), targetBox.x2, targetBox.y1);
|
|
} else {
|
|
targetBox = new Box2D(targetBox.x1, targetBox.y2, targetBox.x2, targetBox.y2 + box.height());
|
|
}
|
|
} else {
|
|
text.options.align = CENTER;
|
|
if (aboveAxis) {
|
|
targetBox = new Box2D(targetBox.x2, targetBox.y1, targetBox.x2 + box.width(), targetBox.y2);
|
|
} else {
|
|
targetBox = new Box2D(targetBox.x1 - box.width(), targetBox.y1, targetBox.x1, targetBox.y2);
|
|
}
|
|
}
|
|
}
|
|
if (!options.rotation) {
|
|
if (vertical) {
|
|
padding.left = padding.right = (targetBox.width() - text.contentBox.width()) / 2;
|
|
} else {
|
|
padding.top = padding.bottom = (targetBox.height() - text.contentBox.height()) / 2;
|
|
}
|
|
}
|
|
text.reflow(targetBox);
|
|
},
|
|
alignToClipBox: function (clipBox) {
|
|
var barLabel = this, vertical = barLabel.options.vertical, field = vertical ? Y : X, start = field + '1', end = field + '2', text = barLabel.children[0], parentBox = barLabel.parent.box, targetBox;
|
|
if (parentBox[start] < clipBox[start] || clipBox[end] < parentBox[end]) {
|
|
targetBox = text.paddingBox.clone();
|
|
targetBox[start] = math.max(parentBox[start], clipBox[start]);
|
|
targetBox[end] = math.min(parentBox[end], clipBox[end]);
|
|
this.reflow(targetBox);
|
|
}
|
|
}
|
|
});
|
|
var LegendItem = BoxElement.extend({
|
|
init: function (options) {
|
|
var item = this;
|
|
BoxElement.fn.init.call(item, options);
|
|
item.createContainer();
|
|
item.createMarker();
|
|
item.createLabel();
|
|
},
|
|
createContainer: function () {
|
|
var item = this;
|
|
item.container = new FloatElement({
|
|
vertical: false,
|
|
wrap: false,
|
|
align: CENTER
|
|
});
|
|
item.append(item.container);
|
|
},
|
|
createMarker: function () {
|
|
this.container.append(new ShapeElement(this.markerOptions()));
|
|
},
|
|
markerOptions: function () {
|
|
var options = this.options;
|
|
var markerColor = options.markerColor;
|
|
return deepExtend({}, options.markers, {
|
|
background: markerColor,
|
|
border: { color: markerColor }
|
|
});
|
|
},
|
|
createLabel: function () {
|
|
var item = this, options = item.options, labelOptions = deepExtend({}, options.labels);
|
|
item.container.append(new TextBox(options.text, labelOptions));
|
|
},
|
|
renderComplete: function () {
|
|
ChartElement.fn.renderComplete.call(this);
|
|
var cursor = this.options.cursor || {};
|
|
var eventSink = this._itemOverlay = draw.Path.fromRect(this.container.box.toRect(), {
|
|
fill: {
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
stroke: null,
|
|
cursor: cursor.style || cursor
|
|
});
|
|
this.appendVisual(eventSink);
|
|
},
|
|
click: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(LEGEND_ITEM_CLICK, args)) {
|
|
e.preventDefault();
|
|
widget._legendItemClick(args.seriesIndex, args.pointIndex);
|
|
}
|
|
},
|
|
hover: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(LEGEND_ITEM_HOVER, args)) {
|
|
e.preventDefault();
|
|
widget._legendItemHover(args.seriesIndex, args.pointIndex);
|
|
}
|
|
return true;
|
|
},
|
|
leave: function (widget) {
|
|
widget._unsetActivePoint();
|
|
},
|
|
eventArgs: function (e) {
|
|
var options = this.options;
|
|
return {
|
|
element: $(e.target),
|
|
text: options.text,
|
|
series: options.series,
|
|
seriesIndex: options.series.index,
|
|
pointIndex: options.pointIndex
|
|
};
|
|
},
|
|
renderVisual: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var customVisual = options.visual;
|
|
if (customVisual) {
|
|
that.visual = customVisual({
|
|
active: options.active,
|
|
series: options.series,
|
|
pointIndex: options.pointIndex,
|
|
options: {
|
|
markers: that.markerOptions(),
|
|
labels: options.labels
|
|
},
|
|
createVisual: function () {
|
|
that.createVisual();
|
|
that.renderChildren();
|
|
that.renderComplete();
|
|
var defaultVisual = that.visual;
|
|
delete that.visual;
|
|
return defaultVisual;
|
|
}
|
|
});
|
|
this.addVisual();
|
|
} else {
|
|
ChartElement.fn.renderVisual.call(that);
|
|
}
|
|
}
|
|
});
|
|
var LegendLayout = ChartElement.extend({
|
|
render: function () {
|
|
var legendItem, items = this.children;
|
|
var options = this.options;
|
|
var vertical = options.vertical;
|
|
this.visual = new draw.Layout(null, {
|
|
spacing: vertical ? 0 : options.spacing,
|
|
lineSpacing: vertical ? options.spacing : 0,
|
|
orientation: vertical ? 'vertical' : 'horizontal'
|
|
});
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
legendItem = items[idx];
|
|
legendItem.reflow(new Box2D());
|
|
legendItem.renderVisual();
|
|
}
|
|
},
|
|
reflow: function (box) {
|
|
this.visual.rect(box.toRect());
|
|
this.visual.reflow();
|
|
var bbox = this.visual.clippedBBox();
|
|
if (bbox) {
|
|
this.box = dataviz.rectToBox(bbox);
|
|
} else {
|
|
this.box = new Box2D();
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
this.addVisual();
|
|
},
|
|
createVisual: noop
|
|
});
|
|
var Legend = ChartElement.extend({
|
|
init: function (options) {
|
|
var legend = this;
|
|
ChartElement.fn.init.call(legend, options);
|
|
if (!inArray(legend.options.position, [
|
|
TOP,
|
|
RIGHT,
|
|
BOTTOM,
|
|
LEFT,
|
|
CUSTOM
|
|
])) {
|
|
legend.options.position = RIGHT;
|
|
}
|
|
legend.createContainer();
|
|
legend.createItems();
|
|
},
|
|
options: {
|
|
position: RIGHT,
|
|
items: [],
|
|
labels: { margin: { left: 6 } },
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
margin: getSpacing(5),
|
|
padding: getSpacing(5),
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
item: { cursor: POINTER },
|
|
spacing: 6,
|
|
background: '',
|
|
zIndex: 1,
|
|
markers: {
|
|
border: { width: 1 },
|
|
width: 7,
|
|
height: 7,
|
|
type: 'rect',
|
|
align: LEFT,
|
|
vAlign: CENTER
|
|
}
|
|
},
|
|
createContainer: function () {
|
|
var legend = this, options = legend.options, userAlign = options.align, position = options.position, align = position, vAlign = CENTER;
|
|
if (position == CUSTOM) {
|
|
align = LEFT;
|
|
} else if (inArray(position, [
|
|
TOP,
|
|
BOTTOM
|
|
])) {
|
|
if (userAlign == 'start') {
|
|
align = LEFT;
|
|
} else if (userAlign == 'end') {
|
|
align = RIGHT;
|
|
} else {
|
|
align = CENTER;
|
|
}
|
|
vAlign = position;
|
|
} else if (userAlign) {
|
|
if (userAlign == 'start') {
|
|
vAlign = TOP;
|
|
} else if (userAlign == 'end') {
|
|
vAlign = BOTTOM;
|
|
}
|
|
}
|
|
legend.container = new BoxElement({
|
|
margin: options.margin,
|
|
padding: options.padding,
|
|
background: options.background,
|
|
border: options.border,
|
|
vAlign: vAlign,
|
|
align: align,
|
|
zIndex: options.zIndex,
|
|
shrinkToFit: true
|
|
});
|
|
legend.append(legend.container);
|
|
},
|
|
createItems: function () {
|
|
var legend = this, options = legend.options, items = options.items, count = items.length, vertical = legend.isVertical(), innerElement, i, item;
|
|
innerElement = new LegendLayout({
|
|
vertical: vertical,
|
|
spacing: options.spacing
|
|
});
|
|
if (options.reverse) {
|
|
items = items.slice(0).reverse();
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
item = items[i];
|
|
innerElement.append(new LegendItem(deepExtend({}, {
|
|
markers: options.markers,
|
|
labels: options.labels
|
|
}, options.item, item)));
|
|
}
|
|
innerElement.render();
|
|
legend.container.append(innerElement);
|
|
},
|
|
isVertical: function () {
|
|
var legend = this, options = legend.options, orientation = options.orientation, position = options.position, vertical = position == CUSTOM && orientation != HORIZONTAL || (defined(orientation) ? orientation != HORIZONTAL : inArray(position, [
|
|
LEFT,
|
|
RIGHT
|
|
]));
|
|
return vertical;
|
|
},
|
|
hasItems: function () {
|
|
return this.container.children[0].children.length > 0;
|
|
},
|
|
reflow: function (targetBox) {
|
|
var legend = this, options = legend.options;
|
|
targetBox = targetBox.clone();
|
|
if (!legend.hasItems()) {
|
|
legend.box = targetBox;
|
|
return;
|
|
}
|
|
if (options.position === CUSTOM) {
|
|
legend.containerCustomReflow(targetBox);
|
|
legend.box = targetBox;
|
|
} else {
|
|
legend.containerReflow(targetBox);
|
|
}
|
|
},
|
|
containerReflow: function (targetBox) {
|
|
var legend = this, options = legend.options, position = options.position, pos = position == TOP || position == BOTTOM ? X : Y, containerBox = targetBox.clone(), container = legend.container, width = options.width, height = options.height, vertical = legend.isVertical(), alignTarget = targetBox.clone(), box;
|
|
if (position == LEFT || position == RIGHT) {
|
|
containerBox.y1 = alignTarget.y1 = 0;
|
|
}
|
|
if (vertical && height) {
|
|
containerBox.y2 = containerBox.y1 + height;
|
|
containerBox.align(alignTarget, Y, container.options.vAlign);
|
|
} else if (!vertical && width) {
|
|
containerBox.x2 = containerBox.x1 + width;
|
|
containerBox.align(alignTarget, X, container.options.align);
|
|
}
|
|
container.reflow(containerBox);
|
|
containerBox = container.box;
|
|
box = containerBox.clone();
|
|
if (options.offsetX || options.offsetY) {
|
|
containerBox.translate(options.offsetX, options.offsetY);
|
|
legend.container.reflow(containerBox);
|
|
}
|
|
box[pos + 1] = targetBox[pos + 1];
|
|
box[pos + 2] = targetBox[pos + 2];
|
|
legend.box = box;
|
|
},
|
|
containerCustomReflow: function (targetBox) {
|
|
var legend = this, options = legend.options, offsetX = options.offsetX, offsetY = options.offsetY, container = legend.container, width = options.width, height = options.height, vertical = legend.isVertical(), containerBox = targetBox.clone();
|
|
if (vertical && height) {
|
|
containerBox.y2 = containerBox.y1 + height;
|
|
} else if (!vertical && width) {
|
|
containerBox.x2 = containerBox.x1 + width;
|
|
}
|
|
container.reflow(containerBox);
|
|
containerBox = container.box;
|
|
container.reflow(Box2D(offsetX, offsetY, offsetX + containerBox.width(), offsetY + containerBox.height()));
|
|
},
|
|
renderVisual: function () {
|
|
if (this.hasItems()) {
|
|
ChartElement.fn.renderVisual.call(this);
|
|
}
|
|
}
|
|
});
|
|
var CategoryAxis = Axis.extend({
|
|
init: function (options) {
|
|
var axis = this;
|
|
options = options || {};
|
|
this._initFields();
|
|
this._initCategories(options);
|
|
Axis.fn.init.call(axis, options);
|
|
},
|
|
_initFields: function () {
|
|
this._ticks = {};
|
|
this.outOfRangeMin = 0;
|
|
this.outOfRangeMax = 0;
|
|
},
|
|
_initCategories: function (options) {
|
|
var categories = (options.categories || []).slice(0);
|
|
var definedMin = defined(options.min);
|
|
var definedMax = defined(options.max);
|
|
options.categories = categories;
|
|
if ((definedMin || definedMax) && categories.length) {
|
|
options.srcCategories = options.categories;
|
|
var min = definedMin ? math.floor(options.min) : 0;
|
|
var max = definedMax ? options.justified ? math.floor(options.max) + 1 : math.ceil(options.max) : categories.length;
|
|
options.categories = options.categories.slice(min, max);
|
|
}
|
|
},
|
|
options: {
|
|
type: CATEGORY,
|
|
categories: [],
|
|
vertical: false,
|
|
majorGridLines: {
|
|
visible: false,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
labels: { zIndex: 1 },
|
|
justified: false
|
|
},
|
|
rangeIndices: function () {
|
|
var options = this.options;
|
|
var length = options.categories.length || 1;
|
|
var min = isNumber(options.min) ? options.min % 1 : 0;
|
|
var max;
|
|
if (isNumber(options.max) && options.max % 1 !== 0 && options.max < this.totalRange().max) {
|
|
max = length - (1 - options.max % 1);
|
|
} else {
|
|
max = length - (options.justified ? 1 : 0);
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
totalRangeIndices: function (limit) {
|
|
var options = this.options;
|
|
var min = isNumber(options.min) ? options.min : 0;
|
|
var max;
|
|
if (isNumber(options.max)) {
|
|
max = options.max;
|
|
} else if (isNumber(options.min)) {
|
|
max = min + options.categories.length;
|
|
} else {
|
|
max = (options.srcCategories || options.categories).length - (options.justified ? 1 : 0) || 1;
|
|
}
|
|
if (limit) {
|
|
var totalRange = this.totalRange();
|
|
min = limitValue(min, 0, totalRange.max);
|
|
max = limitValue(max, 0, totalRange.max);
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: isNumber(options.min) ? options.min : 0,
|
|
max: isNumber(options.max) ? options.max : options.categories.length
|
|
};
|
|
},
|
|
totalRange: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: 0,
|
|
max: math.max(this._seriesMax || 0, (options.srcCategories || options.categories).length) - (options.justified ? 1 : 0)
|
|
};
|
|
},
|
|
getScale: function () {
|
|
var range = this.rangeIndices();
|
|
var min = range.min;
|
|
var max = range.max;
|
|
var lineBox = this.lineBox();
|
|
var size = this.options.vertical ? lineBox.height() : lineBox.width();
|
|
var scale = size / (max - min || 1);
|
|
return scale * (this.options.reverse ? -1 : 1);
|
|
},
|
|
getTickPositions: function (stepSize) {
|
|
var axis = this, options = axis.options, vertical = options.vertical, lineBox = axis.lineBox(), reverse = options.reverse, scale = axis.getScale(), range = axis.rangeIndices(), min = range.min, max = range.max, current = min % 1 !== 0 ? math.floor(min / 1) + stepSize : min, pos = lineBox[(vertical ? Y : X) + (reverse ? 2 : 1)], positions = [];
|
|
while (current <= max) {
|
|
positions.push(pos + round(scale * (current - min), COORD_PRECISION));
|
|
current += stepSize;
|
|
}
|
|
return positions;
|
|
},
|
|
getLabelsTickPositions: function () {
|
|
var tickPositions = this.getMajorTickPositions().slice(0);
|
|
var range = this.rangeIndices();
|
|
var scale = this.getScale();
|
|
var box = this.lineBox();
|
|
var options = this.options;
|
|
var axis = options.vertical ? Y : X;
|
|
var start = options.reverse ? 2 : 1;
|
|
var end = options.reverse ? 1 : 2;
|
|
if (range.min % 1 !== 0) {
|
|
tickPositions.unshift(box[axis + start] - scale * (range.min % 1));
|
|
}
|
|
if (range.max % 1 !== 0) {
|
|
tickPositions.push(box[axis + end] + scale * (1 - range.max % 1));
|
|
}
|
|
return tickPositions;
|
|
},
|
|
labelTickIndex: function (label) {
|
|
var index = label.index;
|
|
var range = this.rangeIndices();
|
|
if (range.min > 0) {
|
|
index = index - math.floor(range.min);
|
|
}
|
|
return index;
|
|
},
|
|
arrangeLabels: function () {
|
|
Axis.fn.arrangeLabels.call(this);
|
|
this.hideOutOfRangeLabels();
|
|
},
|
|
hideOutOfRangeLabels: function () {
|
|
var box = this.box, labels = this.labels, valueAxis = this.options.vertical ? Y : X, start = box[valueAxis + 1], end = box[valueAxis + 2], firstLabel = labels[0], lastLabel = last(labels);
|
|
if (labels.length) {
|
|
if (firstLabel.box[valueAxis + 1] > end || firstLabel.box[valueAxis + 2] < start) {
|
|
firstLabel.options.visible = false;
|
|
}
|
|
if (lastLabel.box[valueAxis + 1] > end || lastLabel.box[valueAxis + 2] < start) {
|
|
lastLabel.options.visible = false;
|
|
}
|
|
}
|
|
},
|
|
getMajorTickPositions: function () {
|
|
return this.getTicks().majorTicks;
|
|
},
|
|
getMinorTickPositions: function () {
|
|
return this.getTicks().minorTicks;
|
|
},
|
|
getTicks: function () {
|
|
var axis = this, cache = axis._ticks, options = axis.options, range = axis.rangeIndices(), reverse = options.reverse, justified = options.justified, lineBox = axis.lineBox(), hash;
|
|
hash = lineBox.getHash() + range.min + ',' + range.max + reverse + justified;
|
|
if (cache._hash !== hash) {
|
|
cache._hash = hash;
|
|
cache.majorTicks = axis.getTickPositions(1);
|
|
cache.minorTicks = axis.getTickPositions(0.5);
|
|
}
|
|
return cache;
|
|
},
|
|
getSlot: function (from, to, limit) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, justified = options.justified, valueAxis = options.vertical ? Y : X, lineBox = axis.lineBox(), range = axis.rangeIndices(), min = range.min, scale = this.getScale(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], slotBox = lineBox.clone(), p1, p2;
|
|
var singleSlot = !defined(to);
|
|
from = valueOrDefault(from, 0);
|
|
to = valueOrDefault(to, from);
|
|
to = math.max(to - 1, from);
|
|
to = math.max(from, to);
|
|
p1 = lineStart + (from - min) * scale;
|
|
p2 = lineStart + (to + 1 - min) * scale;
|
|
if (singleSlot && justified) {
|
|
p2 = p1;
|
|
}
|
|
if (limit) {
|
|
p1 = limitValue(p1, lineBox[valueAxis + 1], lineBox[valueAxis + 2]);
|
|
p2 = limitValue(p2, lineBox[valueAxis + 1], lineBox[valueAxis + 2]);
|
|
}
|
|
slotBox[valueAxis + 1] = reverse ? p2 : p1;
|
|
slotBox[valueAxis + 2] = reverse ? p1 : p2;
|
|
return slotBox;
|
|
},
|
|
pointCategoryIndex: function (point) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, justified = options.justified, valueAxis = options.vertical ? Y : X, lineBox = axis.lineBox(), range = axis.rangeIndices(), startValue = reverse ? range.max : range.min, scale = this.getScale(), lineStart = lineBox[valueAxis + 1], lineEnd = lineBox[valueAxis + 2], pos = point[valueAxis];
|
|
if (pos < lineStart || pos > lineEnd) {
|
|
return null;
|
|
}
|
|
var size = pos - lineStart;
|
|
var value = size / scale;
|
|
value = startValue + value;
|
|
var diff = value % 1;
|
|
if (justified) {
|
|
value = math.round(value);
|
|
} else if (diff === 0 && value > 0) {
|
|
value--;
|
|
}
|
|
return math.floor(value);
|
|
},
|
|
getCategory: function (point) {
|
|
var index = this.pointCategoryIndex(point);
|
|
if (index === null) {
|
|
return null;
|
|
}
|
|
return this.options.categories[index];
|
|
},
|
|
categoryIndex: function (value) {
|
|
var options = this.options;
|
|
var index = indexOf(value, options.srcCategories || options.categories);
|
|
return index - math.floor(options.min || 0);
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = options.categories.length, scale = size / range, offset = round(delta / scale, DEFAULT_PRECISION);
|
|
return {
|
|
min: offset,
|
|
max: range + offset
|
|
};
|
|
},
|
|
zoomRange: function (rate) {
|
|
var rangeIndices = this.totalRangeIndices();
|
|
var totalRange = this.totalRange();
|
|
var totalMax = totalRange.max;
|
|
var totalMin = totalRange.min;
|
|
var min = limitValue(rangeIndices.min + rate, totalMin, totalMax);
|
|
var max = limitValue(rangeIndices.max - rate, totalMin, totalMax);
|
|
if (max - min > 0) {
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
},
|
|
scaleRange: function (scale) {
|
|
var axis = this, options = axis.options, range = options.categories.length, delta = scale * range;
|
|
return {
|
|
min: -delta,
|
|
max: range + delta
|
|
};
|
|
},
|
|
labelsCount: function () {
|
|
var labelsRange = this.labelsRange();
|
|
return labelsRange.max - labelsRange.min;
|
|
},
|
|
labelsRange: function () {
|
|
var options = this.options;
|
|
var labelOptions = options.labels;
|
|
var justified = options.justified;
|
|
var range = this.totalRangeIndices(true);
|
|
var min = range.min;
|
|
var max = range.max;
|
|
var start = math.floor(min);
|
|
var skip;
|
|
if (!justified) {
|
|
min = math.floor(min);
|
|
max = math.ceil(max);
|
|
} else {
|
|
min = math.ceil(min);
|
|
max = math.floor(max);
|
|
}
|
|
if (min > labelOptions.skip) {
|
|
skip = labelOptions.skip + labelOptions.step * math.ceil((min - labelOptions.skip) / labelOptions.step);
|
|
} else {
|
|
skip = labelOptions.skip;
|
|
}
|
|
return {
|
|
min: skip - start,
|
|
max: (options.categories.length ? max + (justified ? 1 : 0) : 0) - start
|
|
};
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var axis = this, options = axis.options, dataItem = options.dataItems ? options.dataItems[index] : null, category = valueOrDefault(options.categories[index], ''), text = axis.axisLabelText(category, dataItem, labelOptions);
|
|
return new AxisLabel(category, text, index, dataItem, labelOptions);
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var categories = this.options.categories;
|
|
return categories.length && (categories.length > value && value >= 0);
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.totalRangeIndices(true), scale = this.getScale(), offset = round(delta / scale, DEFAULT_PRECISION), totalRange = this.totalRange(), min = range.min + offset, max = range.max + offset;
|
|
return this.limitRange(min, max, 0, totalRange.max, offset);
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, valueAxis = options.vertical ? Y : X, lineBox = axis.lineBox(), range = axis.totalRangeIndices(true), scale = this.getScale(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)];
|
|
var diffStart = start[valueAxis] - lineStart;
|
|
var diffEnd = end[valueAxis] - lineStart;
|
|
var min = range.min + diffStart / scale;
|
|
var max = range.min + diffEnd / scale;
|
|
return {
|
|
min: math.min(min, max),
|
|
max: math.max(min, max)
|
|
};
|
|
}
|
|
});
|
|
var DateCategoryAxis = CategoryAxis.extend({
|
|
init: function (options) {
|
|
var axis = this, baseUnit, useDefault;
|
|
options = options || {};
|
|
options = deepExtend({ roundToBaseUnit: true }, options, {
|
|
categories: toDate(options.categories),
|
|
min: toDate(options.min),
|
|
max: toDate(options.max)
|
|
});
|
|
options.userSetBaseUnit = options.userSetBaseUnit || options.baseUnit;
|
|
options.userSetBaseUnitStep = options.userSetBaseUnitStep || options.baseUnitStep;
|
|
if (options.categories && options.categories.length > 0) {
|
|
baseUnit = (options.baseUnit || '').toLowerCase();
|
|
useDefault = baseUnit !== FIT && !inArray(baseUnit, BASE_UNITS);
|
|
if (useDefault) {
|
|
options.baseUnit = axis.defaultBaseUnit(options);
|
|
}
|
|
if (baseUnit === FIT || options.baseUnitStep === AUTO) {
|
|
axis.autoBaseUnit(options);
|
|
}
|
|
this._groupsStart = addDuration(options.categories[0], 0, options.baseUnit, options.weekStartDay);
|
|
axis.groupCategories(options);
|
|
} else {
|
|
options.baseUnit = options.baseUnit || DAYS;
|
|
}
|
|
this._initFields();
|
|
Axis.fn.init.call(axis, options);
|
|
},
|
|
options: {
|
|
type: DATE,
|
|
labels: { dateFormats: DateLabelFormats },
|
|
autoBaseUnitSteps: {
|
|
seconds: [
|
|
1,
|
|
2,
|
|
5,
|
|
15,
|
|
30
|
|
],
|
|
minutes: [
|
|
1,
|
|
2,
|
|
5,
|
|
15,
|
|
30
|
|
],
|
|
hours: [
|
|
1,
|
|
2,
|
|
3
|
|
],
|
|
days: [
|
|
1,
|
|
2,
|
|
3
|
|
],
|
|
weeks: [
|
|
1,
|
|
2
|
|
],
|
|
months: [
|
|
1,
|
|
2,
|
|
3,
|
|
6
|
|
],
|
|
years: [
|
|
1,
|
|
2,
|
|
3,
|
|
5,
|
|
10,
|
|
25,
|
|
50
|
|
]
|
|
},
|
|
maxDateGroups: 10
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var axis = this, range = axis.range(), categories = axis.options.categories || [];
|
|
return dateComparer(value, range.min) >= 0 && dateComparer(value, range.max) <= 0 && categories.length;
|
|
},
|
|
parseNoteValue: function (value) {
|
|
return toDate(value);
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, baseUnit = options.baseUnit, weekStartDay = options.weekStartDay, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = axis.range(), scale = size / (range.max - range.min), offset = round(delta / scale, DEFAULT_PRECISION), from, to;
|
|
if (range.min && range.max) {
|
|
from = addTicks(options.min || range.min, offset);
|
|
to = addTicks(options.max || range.max, offset);
|
|
range = {
|
|
min: addDuration(from, 0, baseUnit, weekStartDay),
|
|
max: addDuration(to, 0, baseUnit, weekStartDay)
|
|
};
|
|
}
|
|
return range;
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, rounds = math.abs(delta), range = axis.range(), from = range.min, to = range.max, step;
|
|
if (range.min && range.max) {
|
|
while (rounds--) {
|
|
range = dateDiff(from, to);
|
|
step = math.round(range * 0.1);
|
|
if (delta < 0) {
|
|
from = addTicks(from, step);
|
|
to = addTicks(to, -step);
|
|
} else {
|
|
from = addTicks(from, -step);
|
|
to = addTicks(to, step);
|
|
}
|
|
}
|
|
range = {
|
|
min: from,
|
|
max: to
|
|
};
|
|
}
|
|
return range;
|
|
},
|
|
defaultBaseUnit: function (options) {
|
|
var categories = options.categories, count = defined(categories) ? categories.length : 0, categoryIx, cat, diff, minDiff = MAX_VALUE, lastCat, unit;
|
|
for (categoryIx = 0; categoryIx < count; categoryIx++) {
|
|
cat = categories[categoryIx];
|
|
if (cat && lastCat) {
|
|
diff = absoluteDateDiff(cat, lastCat);
|
|
if (diff > 0) {
|
|
minDiff = math.min(minDiff, diff);
|
|
if (minDiff >= TIME_PER_YEAR) {
|
|
unit = YEARS;
|
|
} else if (minDiff >= TIME_PER_MONTH - TIME_PER_DAY * 3) {
|
|
unit = MONTHS;
|
|
} else if (minDiff >= TIME_PER_WEEK) {
|
|
unit = WEEKS;
|
|
} else if (minDiff >= TIME_PER_DAY) {
|
|
unit = DAYS;
|
|
} else if (minDiff >= TIME_PER_HOUR) {
|
|
unit = HOURS;
|
|
} else if (minDiff >= TIME_PER_MINUTE) {
|
|
unit = MINUTES;
|
|
} else {
|
|
unit = SECONDS;
|
|
}
|
|
}
|
|
}
|
|
lastCat = cat;
|
|
}
|
|
return unit || DAYS;
|
|
},
|
|
_categoryRange: function (categories) {
|
|
var range = categories._range;
|
|
if (!range) {
|
|
range = categories._range = sparseArrayLimits(categories);
|
|
}
|
|
return range;
|
|
},
|
|
totalRange: function () {
|
|
return {
|
|
min: 0,
|
|
max: this.options.categories.length
|
|
};
|
|
},
|
|
rangeIndices: function () {
|
|
var options = this.options;
|
|
var baseUnit = options.baseUnit;
|
|
var baseUnitStep = options.baseUnitStep || 1;
|
|
var categories = options.categories;
|
|
var categoryLimits = this.categoriesRange();
|
|
var min = toDate(options.min || categoryLimits.min);
|
|
var max = toDate(options.max || categoryLimits.max);
|
|
var minIdx = 0, maxIdx = 0;
|
|
if (categories.length) {
|
|
minIdx = dateIndex(min, categories[0], baseUnit, baseUnitStep);
|
|
maxIdx = dateIndex(max, categories[0], baseUnit, baseUnitStep);
|
|
if (options.roundToBaseUnit) {
|
|
minIdx = math.floor(minIdx);
|
|
maxIdx = options.justified ? math.floor(maxIdx) : math.ceil(maxIdx);
|
|
}
|
|
}
|
|
return {
|
|
min: minIdx,
|
|
max: maxIdx
|
|
};
|
|
},
|
|
labelsRange: function () {
|
|
var options = this.options;
|
|
var labelOptions = options.labels;
|
|
var range = this.rangeIndices();
|
|
var min = math.floor(range.min);
|
|
var max = math.ceil(range.max);
|
|
return {
|
|
min: min + labelOptions.skip,
|
|
max: options.categories.length ? max + (options.justified ? 1 : 0) : 0
|
|
};
|
|
},
|
|
categoriesRange: function () {
|
|
var options = this.options;
|
|
var range = this._categoryRange(options.srcCategories || options.categories);
|
|
var max = toDate(range.max);
|
|
if (!options.justified && dateEquals(max, this._roundToTotalStep(max, options, false))) {
|
|
max = this._roundToTotalStep(max, options, true, true);
|
|
}
|
|
return {
|
|
min: toDate(range.min),
|
|
max: max
|
|
};
|
|
},
|
|
currentRange: function () {
|
|
var options = this.options;
|
|
var round = options.roundToBaseUnit !== false;
|
|
var totalRange = this.categoriesRange();
|
|
var min = options.min;
|
|
var max = options.max;
|
|
if (!min) {
|
|
min = round ? this._roundToTotalStep(totalRange.min, options, false) : totalRange.min;
|
|
}
|
|
if (!max) {
|
|
max = round ? this._roundToTotalStep(totalRange.max, options, !options.justified) : totalRange.max;
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
datesRange: function () {
|
|
var range = this._categoryRange(this.options.srcCategories || this.options.categories);
|
|
return {
|
|
min: toDate(range.min),
|
|
max: toDate(range.max)
|
|
};
|
|
},
|
|
pan: function (delta) {
|
|
var axis = this, options = axis.options, baseUnit = options.baseUnit, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = this.currentRange(), totalLimits = this.totalLimits(), min = range.min, max = range.max, scale = size / (max - min), offset = round(delta / scale, DEFAULT_PRECISION), panRange, from, to;
|
|
from = addTicks(min, offset);
|
|
to = addTicks(max, offset);
|
|
panRange = this.limitRange(toTime(from), toTime(to), toTime(totalLimits.min), toTime(totalLimits.max), offset);
|
|
if (panRange) {
|
|
panRange.min = toDate(panRange.min);
|
|
panRange.max = toDate(panRange.max);
|
|
panRange.baseUnit = baseUnit;
|
|
panRange.baseUnitStep = options.baseUnitStep || 1;
|
|
panRange.userSetBaseUnit = options.userSetBaseUnit;
|
|
panRange.userSetBaseUnitStep = options.userSetBaseUnitStep;
|
|
return panRange;
|
|
}
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var pointsRange = CategoryAxis.fn.pointsRange.call(this, start, end);
|
|
var datesRange = this.currentRange();
|
|
var indicesRange = this.rangeIndices();
|
|
var scale = dateDiff(datesRange.max, datesRange.min) / (indicesRange.max - indicesRange.min);
|
|
var options = this.options;
|
|
var min = addTicks(datesRange.min, pointsRange.min * scale);
|
|
var max = addTicks(datesRange.min, pointsRange.max * scale);
|
|
return {
|
|
min: min,
|
|
max: max,
|
|
baseUnit: options.userSetBaseUnit,
|
|
baseUnitStep: options.userSetBaseUnitStep
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var options = this.options;
|
|
var totalLimits = this.totalLimits();
|
|
var currentRange = this.currentRange();
|
|
var baseUnit = options.baseUnit;
|
|
var baseUnitStep = options.baseUnitStep || 1;
|
|
var weekStartDay = options.weekStartDay;
|
|
var rangeMax = currentRange.max;
|
|
var rangeMin = currentRange.min;
|
|
var min = addDuration(rangeMin, delta * baseUnitStep, baseUnit, weekStartDay);
|
|
var max = addDuration(rangeMax, -delta * baseUnitStep, baseUnit, weekStartDay);
|
|
if (options.userSetBaseUnit == FIT) {
|
|
var autoBaseUnitSteps = options.autoBaseUnitSteps;
|
|
var maxDateGroups = options.maxDateGroups;
|
|
var baseUnitIndex = indexOf(baseUnit, BASE_UNITS);
|
|
var autoBaseUnitStep;
|
|
var diff = dateDiff(max, min);
|
|
var maxDiff = last(autoBaseUnitSteps[baseUnit]) * maxDateGroups * TIME_PER_UNIT[baseUnit];
|
|
var rangeDiff = dateDiff(rangeMax, rangeMin);
|
|
var ticks;
|
|
if (diff < TIME_PER_UNIT[baseUnit] && baseUnit !== SECONDS) {
|
|
baseUnit = BASE_UNITS[baseUnitIndex - 1];
|
|
autoBaseUnitStep = last(autoBaseUnitSteps[baseUnit]);
|
|
ticks = (rangeDiff - (maxDateGroups - 1) * autoBaseUnitStep * TIME_PER_UNIT[baseUnit]) / 2;
|
|
min = addTicks(rangeMin, ticks);
|
|
max = addTicks(rangeMax, -ticks);
|
|
} else if (diff > maxDiff && baseUnit !== YEARS) {
|
|
var stepIndex = 0;
|
|
do {
|
|
baseUnitIndex++;
|
|
baseUnit = BASE_UNITS[baseUnitIndex];
|
|
stepIndex = 0;
|
|
ticks = 2 * TIME_PER_UNIT[baseUnit];
|
|
do {
|
|
autoBaseUnitStep = autoBaseUnitSteps[baseUnit][stepIndex];
|
|
stepIndex++;
|
|
} while (stepIndex < autoBaseUnitSteps[baseUnit].length && ticks * autoBaseUnitStep < rangeDiff);
|
|
} while (baseUnit !== YEARS && ticks * autoBaseUnitStep < rangeDiff);
|
|
ticks = (ticks * autoBaseUnitStep - rangeDiff) / 2;
|
|
if (ticks > 0) {
|
|
min = addTicks(rangeMin, -ticks);
|
|
max = addTicks(rangeMax, ticks);
|
|
min = addTicks(min, limitValue(max, totalLimits.min, totalLimits.max) - max);
|
|
max = addTicks(max, limitValue(min, totalLimits.min, totalLimits.max) - min);
|
|
}
|
|
}
|
|
}
|
|
min = toDate(limitValue(min, totalLimits.min, totalLimits.max));
|
|
max = toDate(limitValue(max, totalLimits.min, totalLimits.max));
|
|
if (dateDiff(max, min) > 0) {
|
|
return {
|
|
min: min,
|
|
max: max,
|
|
baseUnit: options.userSetBaseUnit,
|
|
baseUnitStep: options.userSetBaseUnitStep
|
|
};
|
|
}
|
|
},
|
|
totalLimits: function () {
|
|
var options = this.options;
|
|
var datesRange = this.datesRange();
|
|
var min = this._roundToTotalStep(toDate(datesRange.min), options, false);
|
|
var max = datesRange.max;
|
|
if (!options.justified) {
|
|
max = this._roundToTotalStep(max, options, true, dateEquals(max, this._roundToTotalStep(max, options, false)));
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
range: function (options) {
|
|
options = options || this.options;
|
|
var categories = options.categories, autoUnit = options.baseUnit === FIT, baseUnit = autoUnit ? BASE_UNITS[0] : options.baseUnit, baseUnitStep = options.baseUnitStep || 1, stepOptions = {
|
|
baseUnit: baseUnit,
|
|
baseUnitStep: baseUnitStep,
|
|
weekStartDay: options.weekStartDay
|
|
}, categoryLimits = this._categoryRange(categories), min = toDate(options.min || categoryLimits.min), max = toDate(options.max || categoryLimits.max);
|
|
return {
|
|
min: this._roundToTotalStep(min, stepOptions, false),
|
|
max: this._roundToTotalStep(max, stepOptions, true, true)
|
|
};
|
|
},
|
|
autoBaseUnit: function (options) {
|
|
var axis = this, categoryLimits = this._categoryRange(options.categories), min = toDate(options.min || categoryLimits.min), max = toDate(options.max || categoryLimits.max), autoUnit = options.baseUnit === FIT, autoUnitIx = 0, baseUnit = autoUnit ? BASE_UNITS[autoUnitIx++] : options.baseUnit, span = max - min, units = span / TIME_PER_UNIT[baseUnit], totalUnits = units, maxDateGroups = options.maxDateGroups || axis.options.maxDateGroups, autoBaseUnitSteps = deepExtend({}, axis.options.autoBaseUnitSteps, options.autoBaseUnitSteps), unitSteps, step, nextStep;
|
|
while (!step || units >= maxDateGroups) {
|
|
unitSteps = unitSteps || autoBaseUnitSteps[baseUnit].slice(0);
|
|
nextStep = unitSteps.shift();
|
|
if (nextStep) {
|
|
step = nextStep;
|
|
units = totalUnits / step;
|
|
} else if (baseUnit === last(BASE_UNITS)) {
|
|
step = math.ceil(totalUnits / maxDateGroups);
|
|
break;
|
|
} else if (autoUnit) {
|
|
baseUnit = BASE_UNITS[autoUnitIx++] || last(BASE_UNITS);
|
|
totalUnits = span / TIME_PER_UNIT[baseUnit];
|
|
unitSteps = null;
|
|
} else {
|
|
if (units > maxDateGroups) {
|
|
step = math.ceil(totalUnits / maxDateGroups);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
options.baseUnitStep = step;
|
|
options.baseUnit = baseUnit;
|
|
},
|
|
_timeScale: function () {
|
|
var axis = this, range = axis.range(), options = axis.options, lineBox = axis.lineBox(), vertical = options.vertical, lineSize = vertical ? lineBox.height() : lineBox.width(), timeRange;
|
|
if (options.justified && options._collapse !== false) {
|
|
var categoryLimits = this._categoryRange(options.categories);
|
|
var maxCategory = toTime(categoryLimits.max);
|
|
timeRange = toDate(maxCategory) - range.min;
|
|
} else {
|
|
timeRange = range.max - range.min;
|
|
}
|
|
return lineSize / timeRange;
|
|
},
|
|
groupCategories: function (options) {
|
|
var axis = this, categories = options.categories, maxCategory = toDate(sparseArrayMax(categories)), baseUnit = options.baseUnit, baseUnitStep = options.baseUnitStep || 1, range = axis.range(options), max = range.max, date, nextDate, groups = [];
|
|
for (date = range.min; date < max; date = nextDate) {
|
|
groups.push(date);
|
|
nextDate = addDuration(date, baseUnitStep, baseUnit, options.weekStartDay);
|
|
if (nextDate > maxCategory && !options.max) {
|
|
break;
|
|
}
|
|
}
|
|
options.srcCategories = categories;
|
|
options.categories = groups;
|
|
},
|
|
_roundToTotalStep: function (value, options, upper, roundToNext) {
|
|
options = options || this.options;
|
|
var baseUnit = options.baseUnit;
|
|
var baseUnitStep = options.baseUnitStep || 1;
|
|
var start = this._groupsStart;
|
|
if (start) {
|
|
var step = dateIndex(value, start, baseUnit, baseUnitStep);
|
|
var roundedStep = upper ? math.ceil(step) : math.floor(step);
|
|
if (roundToNext) {
|
|
roundedStep++;
|
|
}
|
|
return addDuration(start, roundedStep * baseUnitStep, baseUnit, options.weekStartDay);
|
|
} else {
|
|
return addDuration(value, upper ? baseUnitStep : 0, baseUnit, options.weekStartDay);
|
|
}
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var options = this.options, dataItem = options.dataItems ? options.dataItems[index] : null, date = options.categories[index], baseUnit = options.baseUnit, visible = true, unitFormat = labelOptions.dateFormats[baseUnit];
|
|
if (options.justified) {
|
|
var roundedDate = floorDate(date, baseUnit, options.weekStartDay);
|
|
visible = dateEquals(roundedDate, date);
|
|
} else if (!options.roundToBaseUnit) {
|
|
visible = !dateEquals(this.range().max, date);
|
|
}
|
|
if (visible) {
|
|
labelOptions.format = labelOptions.format || unitFormat;
|
|
var text = this.axisLabelText(date, dataItem, labelOptions);
|
|
if (text) {
|
|
return new AxisLabel(date, text, index, dataItem, labelOptions);
|
|
}
|
|
}
|
|
},
|
|
categoryIndex: function (value) {
|
|
var axis = this;
|
|
var options = axis.options;
|
|
var categories = options.categories;
|
|
var index = -1;
|
|
if (categories.length) {
|
|
index = math.floor(dateIndex(toDate(value), categories[0], options.baseUnit, options.baseUnitStep || 1));
|
|
}
|
|
return index;
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
var axis = this;
|
|
if (typeof a === OBJECT) {
|
|
a = axis.categoryIndex(a);
|
|
}
|
|
if (typeof b === OBJECT) {
|
|
b = axis.categoryIndex(b);
|
|
}
|
|
return CategoryAxis.fn.getSlot.call(axis, a, b, limit);
|
|
}
|
|
});
|
|
var DateValueAxis = Axis.extend({
|
|
init: function (seriesMin, seriesMax, options) {
|
|
var axis = this;
|
|
options = options || {};
|
|
deepExtend(options, {
|
|
min: toDate(options.min),
|
|
max: toDate(options.max),
|
|
axisCrossingValue: toDate(options.axisCrossingValues || options.axisCrossingValue)
|
|
});
|
|
options = axis.applyDefaults(toDate(seriesMin), toDate(seriesMax), options);
|
|
Axis.fn.init.call(axis, options);
|
|
},
|
|
options: {
|
|
type: DATE,
|
|
majorGridLines: {
|
|
visible: true,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
labels: { dateFormats: DateLabelFormats }
|
|
},
|
|
applyDefaults: function (seriesMin, seriesMax, options) {
|
|
var axis = this, min = options.min || seriesMin, max = options.max || seriesMax, baseUnit = options.baseUnit || (max && min ? axis.timeUnits(absoluteDateDiff(max, min)) : HOURS), baseUnitTime = TIME_PER_UNIT[baseUnit], autoMin = floorDate(toTime(min) - 1, baseUnit) || toDate(max), autoMax = ceilDate(toTime(max) + 1, baseUnit), userMajorUnit = options.majorUnit ? options.majorUnit : undefined, majorUnit = userMajorUnit || dataviz.ceil(dataviz.autoMajorUnit(autoMin.getTime(), autoMax.getTime()), baseUnitTime) / baseUnitTime, actualUnits = duration(autoMin, autoMax, baseUnit), totalUnits = dataviz.ceil(actualUnits, majorUnit), unitsToAdd = totalUnits - actualUnits, head = math.floor(unitsToAdd / 2), tail = unitsToAdd - head;
|
|
if (!options.baseUnit) {
|
|
delete options.baseUnit;
|
|
}
|
|
options.baseUnit = options.baseUnit || baseUnit;
|
|
options.min = options.min || addDuration(autoMin, -head, baseUnit);
|
|
options.max = options.max || addDuration(autoMax, tail, baseUnit);
|
|
options.minorUnit = options.minorUnit || majorUnit / 5;
|
|
options.majorUnit = majorUnit;
|
|
this.totalMin = toTime(floorDate(toTime(seriesMin) - 1, baseUnit));
|
|
this.totalMax = toTime(ceilDate(toTime(seriesMax) + 1, baseUnit));
|
|
return options;
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: options.min,
|
|
max: options.max
|
|
};
|
|
},
|
|
getDivisions: function (stepValue) {
|
|
var options = this.options;
|
|
return math.floor(duration(options.min, options.max, options.baseUnit) / stepValue + 1);
|
|
},
|
|
getTickPositions: function (step) {
|
|
var options = this.options;
|
|
var vertical = options.vertical;
|
|
var reverse = options.reverse;
|
|
var lineBox = this.lineBox();
|
|
var dir = (vertical ? -1 : 1) * (reverse ? -1 : 1);
|
|
var startEdge = dir === 1 ? 1 : 2;
|
|
var start = lineBox[(vertical ? Y : X) + startEdge];
|
|
var divisions = this.getDivisions(step);
|
|
var timeRange = dateDiff(options.max, options.min);
|
|
var lineSize = vertical ? lineBox.height() : lineBox.width();
|
|
var scale = lineSize / timeRange;
|
|
var positions = [start];
|
|
for (var i = 1; i < divisions; i++) {
|
|
var date = addDuration(options.min, i * step, options.baseUnit);
|
|
var pos = start + dateDiff(date, options.min) * scale * dir;
|
|
positions.push(round(pos, COORD_PRECISION));
|
|
}
|
|
return positions;
|
|
},
|
|
getMajorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.majorUnit);
|
|
},
|
|
getMinorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.minorUnit);
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
return NumericAxis.fn.getSlot.call(this, toDate(a), toDate(b), limit);
|
|
},
|
|
getValue: function (point) {
|
|
var value = NumericAxis.fn.getValue.call(this, point);
|
|
return value !== null ? toDate(value) : null;
|
|
},
|
|
labelsCount: function () {
|
|
return this.getDivisions(this.options.majorUnit);
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var options = this.options;
|
|
var offset = index * options.majorUnit;
|
|
var date = options.min;
|
|
if (offset > 0) {
|
|
date = addDuration(date, offset, options.baseUnit);
|
|
}
|
|
var unitFormat = labelOptions.dateFormats[options.baseUnit];
|
|
labelOptions.format = labelOptions.format || unitFormat;
|
|
var text = this.axisLabelText(date, null, labelOptions);
|
|
return new AxisLabel(date, text, index, null, labelOptions);
|
|
},
|
|
timeUnits: function (delta) {
|
|
var unit = HOURS;
|
|
if (delta >= TIME_PER_YEAR) {
|
|
unit = YEARS;
|
|
} else if (delta >= TIME_PER_MONTH) {
|
|
unit = MONTHS;
|
|
} else if (delta >= TIME_PER_WEEK) {
|
|
unit = WEEKS;
|
|
} else if (delta >= TIME_PER_DAY) {
|
|
unit = DAYS;
|
|
}
|
|
return unit;
|
|
},
|
|
translateRange: function (delta, exact) {
|
|
var axis = this, options = axis.options, baseUnit = options.baseUnit, weekStartDay = options.weekStartDay, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = axis.range(), scale = size / dateDiff(range.max, range.min), offset = round(delta / scale, DEFAULT_PRECISION), from = addTicks(options.min, offset), to = addTicks(options.max, offset);
|
|
if (!exact) {
|
|
from = addDuration(from, 0, baseUnit, weekStartDay);
|
|
to = addDuration(to, 0, baseUnit, weekStartDay);
|
|
}
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, options = axis.options, rounds = math.abs(delta), from = options.min, to = options.max, range, step;
|
|
while (rounds--) {
|
|
range = dateDiff(from, to);
|
|
step = math.round(range * 0.1);
|
|
if (delta < 0) {
|
|
from = addTicks(from, step);
|
|
to = addTicks(to, -step);
|
|
} else {
|
|
from = addTicks(from, -step);
|
|
to = addTicks(to, step);
|
|
}
|
|
}
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var range = this.range();
|
|
return dateComparer(value, range.min) >= 0 && dateComparer(value, range.max) <= 0;
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.translateRange(delta, true);
|
|
var limittedRange = this.limitRange(toTime(range.min), toTime(range.max), this.totalMin, this.totalMax);
|
|
if (limittedRange) {
|
|
return {
|
|
min: toDate(limittedRange.min),
|
|
max: toDate(limittedRange.max)
|
|
};
|
|
}
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var startValue = this.getValue(start);
|
|
var endValue = this.getValue(end);
|
|
var min = math.min(startValue, endValue);
|
|
var max = math.max(startValue, endValue);
|
|
return {
|
|
min: toDate(min),
|
|
max: toDate(max)
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var range = this.scaleRange(delta);
|
|
var min = toDate(limitValue(toTime(range.min), this.totalMin, this.totalMax));
|
|
var max = toDate(limitValue(toTime(range.max), this.totalMin, this.totalMax));
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
});
|
|
var ClusterLayout = ChartElement.extend({
|
|
options: {
|
|
vertical: false,
|
|
gap: 0,
|
|
spacing: 0
|
|
},
|
|
reflow: function (box) {
|
|
var cluster = this, options = cluster.options, vertical = options.vertical, axis = vertical ? Y : X, children = cluster.children, gap = options.gap, spacing = options.spacing, count = children.length, slots = count + gap + spacing * (count - 1), slotSize = (vertical ? box.height() : box.width()) / slots, position = box[axis + 1] + slotSize * (gap / 2), childBox, i;
|
|
for (i = 0; i < count; i++) {
|
|
childBox = (children[i].box || box).clone();
|
|
childBox[axis + 1] = position;
|
|
childBox[axis + 2] = position + slotSize;
|
|
children[i].reflow(childBox);
|
|
if (i < count - 1) {
|
|
position += slotSize * spacing;
|
|
}
|
|
position += slotSize;
|
|
}
|
|
}
|
|
});
|
|
var StackWrap = ChartElement.extend({
|
|
options: { vertical: true },
|
|
reflow: function (targetBox) {
|
|
var options = this.options, vertical = options.vertical, positionAxis = vertical ? X : Y, children = this.children, box = this.box = new Box2D(), childrenCount = children.length, i;
|
|
for (i = 0; i < childrenCount; i++) {
|
|
var currentChild = children[i], childBox;
|
|
if (currentChild.visible !== false) {
|
|
childBox = currentChild.box.clone();
|
|
childBox.snapTo(targetBox, positionAxis);
|
|
if (i === 0) {
|
|
box = this.box = childBox.clone();
|
|
}
|
|
currentChild.reflow(childBox);
|
|
box.wrap(childBox);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var PointEventsMixin = {
|
|
click: function (chart, e) {
|
|
return chart.trigger(SERIES_CLICK, this.eventArgs(e));
|
|
},
|
|
hover: function (chart, e) {
|
|
return chart.trigger(SERIES_HOVER, this.eventArgs(e));
|
|
},
|
|
eventArgs: function (e) {
|
|
return {
|
|
value: this.value,
|
|
percentage: this.percentage,
|
|
category: this.category,
|
|
series: this.series,
|
|
dataItem: this.dataItem,
|
|
runningTotal: this.runningTotal,
|
|
total: this.total,
|
|
element: $((e || {}).target),
|
|
originalEvent: e,
|
|
point: this
|
|
};
|
|
}
|
|
};
|
|
var NoteMixin = {
|
|
createNote: function () {
|
|
var element = this, options = element.options.notes, text = element.noteText || options.label.text;
|
|
if (options.visible !== false && defined(text) && text !== null) {
|
|
element.note = new Note(element.value, text, element.dataItem, element.category, element.series, element.options.notes);
|
|
element.append(element.note);
|
|
}
|
|
}
|
|
};
|
|
var Bar = ChartElement.extend({
|
|
init: function (value, options) {
|
|
var bar = this;
|
|
ChartElement.fn.init.call(bar);
|
|
bar.options = options;
|
|
bar.color = options.color || WHITE;
|
|
bar.aboveAxis = valueOrDefault(bar.options.aboveAxis, true);
|
|
bar.value = value;
|
|
},
|
|
defaults: {
|
|
border: { width: 1 },
|
|
vertical: true,
|
|
overlay: { gradient: GLASS },
|
|
labels: {
|
|
visible: false,
|
|
format: '{0}'
|
|
},
|
|
opacity: 1,
|
|
notes: { label: {} }
|
|
},
|
|
render: function () {
|
|
if (this._rendered) {
|
|
return;
|
|
} else {
|
|
this._rendered = true;
|
|
}
|
|
this.createLabel();
|
|
this.createNote();
|
|
if (this.errorBar) {
|
|
this.append(this.errorBar);
|
|
}
|
|
},
|
|
createLabel: function () {
|
|
var options = this.options;
|
|
var labels = options.labels;
|
|
var labelText;
|
|
if (labels.visible) {
|
|
if (labels.template) {
|
|
var labelTemplate = template(labels.template);
|
|
labelText = labelTemplate({
|
|
dataItem: this.dataItem,
|
|
category: this.category,
|
|
value: this.value,
|
|
percentage: this.percentage,
|
|
runningTotal: this.runningTotal,
|
|
total: this.total,
|
|
series: this.series
|
|
});
|
|
} else {
|
|
labelText = this.formatValue(labels.format);
|
|
}
|
|
this.label = new BarLabel(labelText, deepExtend({ vertical: options.vertical }, options.labels));
|
|
this.append(this.label);
|
|
}
|
|
},
|
|
formatValue: function (format) {
|
|
return this.owner.formatPointValue(this, format);
|
|
},
|
|
reflow: function (targetBox) {
|
|
this.render();
|
|
var bar = this, label = bar.label;
|
|
bar.box = targetBox;
|
|
if (label) {
|
|
label.options.aboveAxis = bar.aboveAxis;
|
|
label.reflow(targetBox);
|
|
}
|
|
if (bar.note) {
|
|
bar.note.reflow(targetBox);
|
|
}
|
|
if (bar.errorBars) {
|
|
for (var i = 0; i < bar.errorBars.length; i++) {
|
|
bar.errorBars[i].reflow(targetBox);
|
|
}
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var bar = this;
|
|
var box = bar.box;
|
|
var options = bar.options;
|
|
var customVisual = options.visual;
|
|
if (bar.visible !== false) {
|
|
ChartElement.fn.createVisual.call(bar);
|
|
if (customVisual) {
|
|
var visual = this.rectVisual = customVisual({
|
|
category: bar.category,
|
|
dataItem: bar.dataItem,
|
|
value: bar.value,
|
|
sender: bar.getChart(),
|
|
series: bar.series,
|
|
percentage: bar.percentage,
|
|
runningTotal: bar.runningTotal,
|
|
total: bar.total,
|
|
rect: box.toRect(),
|
|
createVisual: function () {
|
|
var group = new draw.Group();
|
|
bar.createRect(group);
|
|
return group;
|
|
},
|
|
options: options
|
|
});
|
|
if (visual) {
|
|
bar.visual.append(visual);
|
|
}
|
|
} else if (box.width() > 0 && box.height() > 0) {
|
|
bar.createRect(bar.visual);
|
|
}
|
|
}
|
|
},
|
|
createRect: function (visual) {
|
|
var options = this.options;
|
|
var border = options.border;
|
|
var strokeOpacity = defined(border.opacity) ? border.opacity : options.opacity;
|
|
var rect = this.box.toRect();
|
|
rect.size.width = Math.round(rect.size.width);
|
|
var path = this.rectVisual = draw.Path.fromRect(rect, {
|
|
fill: {
|
|
color: this.color,
|
|
opacity: options.opacity
|
|
},
|
|
stroke: {
|
|
color: this.getBorderColor(),
|
|
width: border.width,
|
|
opacity: strokeOpacity,
|
|
dashType: border.dashType
|
|
}
|
|
});
|
|
var width = this.box.width();
|
|
var height = this.box.height();
|
|
var size = options.vertical ? width : height;
|
|
if (size > BAR_ALIGN_MIN_WIDTH) {
|
|
alignPathToPixel(path);
|
|
if (width < 1 || height < 1) {
|
|
path.options.stroke.lineJoin = 'round';
|
|
}
|
|
}
|
|
visual.append(path);
|
|
if (hasGradientOverlay(options)) {
|
|
visual.append(this.createGradientOverlay(path, { baseColor: this.color }, deepExtend({
|
|
end: !options.vertical ? [
|
|
0,
|
|
1
|
|
] : undefined
|
|
}, options.overlay)));
|
|
}
|
|
},
|
|
createHighlight: function (style) {
|
|
var highlight = draw.Path.fromRect(this.box.toRect(), style);
|
|
return alignPathToPixel(highlight);
|
|
},
|
|
highlightVisual: function () {
|
|
return this.rectVisual;
|
|
},
|
|
highlightVisualArgs: function () {
|
|
return {
|
|
options: this.options,
|
|
rect: this.box.toRect(),
|
|
visual: this.rectVisual
|
|
};
|
|
},
|
|
getBorderColor: function () {
|
|
var bar = this, options = bar.options, color = bar.color, border = options.border, borderColor = border.color, brightness = border._brightness || BAR_BORDER_BRIGHTNESS;
|
|
if (!defined(borderColor)) {
|
|
borderColor = new Color(color).brightness(brightness).toHex();
|
|
}
|
|
return borderColor;
|
|
},
|
|
tooltipAnchor: function (tooltipWidth, tooltipHeight) {
|
|
var bar = this, options = bar.options, box = bar.box, vertical = options.vertical, aboveAxis = bar.aboveAxis, clipBox = bar.owner.pane.clipBox() || box, x, y;
|
|
if (vertical) {
|
|
x = math.min(box.x2, clipBox.x2) + TOOLTIP_OFFSET;
|
|
y = aboveAxis ? math.max(box.y1, clipBox.y1) : math.min(box.y2, clipBox.y2) - tooltipHeight;
|
|
} else {
|
|
var x1 = math.max(box.x1, clipBox.x1), x2 = math.min(box.x2, clipBox.x2);
|
|
if (options.isStacked) {
|
|
x = aboveAxis ? x2 - tooltipWidth : x1;
|
|
y = math.max(box.y1, clipBox.y1) - tooltipHeight - TOOLTIP_OFFSET;
|
|
} else {
|
|
x = aboveAxis ? x2 + TOOLTIP_OFFSET : x1 - tooltipWidth - TOOLTIP_OFFSET;
|
|
y = math.max(box.y1, clipBox.y1);
|
|
}
|
|
}
|
|
return new Point2D(x, y);
|
|
},
|
|
overlapsBox: function (box) {
|
|
return this.box.overlaps(box);
|
|
}
|
|
});
|
|
deepExtend(Bar.fn, PointEventsMixin);
|
|
deepExtend(Bar.fn, NoteMixin);
|
|
var BarChartAnimation = draw.Animation.extend({
|
|
options: { duration: INITIAL_ANIMATION_DURATION },
|
|
setup: function () {
|
|
var element = this.element;
|
|
var options = this.options;
|
|
var bbox = element.bbox();
|
|
if (bbox) {
|
|
this.origin = options.origin;
|
|
var axis = options.vertical ? Y : X;
|
|
var fromScale = this.fromScale = new geom.Point(1, 1);
|
|
fromScale[axis] = START_SCALE;
|
|
element.transform(geom.transform().scale(fromScale.x, fromScale.y));
|
|
} else {
|
|
this.abort();
|
|
}
|
|
},
|
|
step: function (pos) {
|
|
var scaleX = interpolate(this.fromScale.x, 1, pos);
|
|
var scaleY = interpolate(this.fromScale.y, 1, pos);
|
|
this.element.transform(geom.transform().scale(scaleX, scaleY, this.origin));
|
|
},
|
|
abort: function () {
|
|
draw.Animation.fn.abort.call(this);
|
|
this.element.transform(null);
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(BAR, BarChartAnimation);
|
|
var FadeInAnimation = draw.Animation.extend({
|
|
options: {
|
|
duration: 200,
|
|
easing: LINEAR
|
|
},
|
|
setup: function () {
|
|
this.fadeTo = this.element.opacity();
|
|
this.element.opacity(0);
|
|
},
|
|
step: function (pos) {
|
|
this.element.opacity(pos * this.fadeTo);
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(FADEIN, FadeInAnimation);
|
|
var ErrorRangeCalculator = function (errorValue, series, field) {
|
|
var that = this;
|
|
that.initGlobalRanges(errorValue, series, field);
|
|
};
|
|
ErrorRangeCalculator.prototype = ErrorRangeCalculator.fn = {
|
|
percentRegex: /percent(?:\w*)\((\d+)\)/,
|
|
standardDeviationRegex: new RegExp('^' + STD_DEV + '(?:\\((\\d+(?:\\.\\d+)?)\\))?$'),
|
|
initGlobalRanges: function (errorValue, series, field) {
|
|
var that = this, data = series.data, deviationMatch = that.standardDeviationRegex.exec(errorValue);
|
|
if (deviationMatch) {
|
|
that.valueGetter = that.createValueGetter(series, field);
|
|
var average = that.getAverage(data), deviation = that.getStandardDeviation(data, average, false), multiple = deviationMatch[1] ? parseFloat(deviationMatch[1]) : 1, errorRange = {
|
|
low: average.value - deviation * multiple,
|
|
high: average.value + deviation * multiple
|
|
};
|
|
that.globalRange = function () {
|
|
return errorRange;
|
|
};
|
|
} else if (errorValue.indexOf && errorValue.indexOf(STD_ERR) >= 0) {
|
|
that.valueGetter = that.createValueGetter(series, field);
|
|
var standardError = that.getStandardError(data, that.getAverage(data));
|
|
that.globalRange = function (value) {
|
|
return {
|
|
low: value - standardError,
|
|
high: value + standardError
|
|
};
|
|
};
|
|
}
|
|
},
|
|
createValueGetter: function (series, field) {
|
|
var data = series.data, binder = SeriesBinder.current, valueFields = binder.valueFields(series), item = defined(data[0]) ? data[0] : {}, idx, srcValueFields, valueGetter;
|
|
if (isArray(item)) {
|
|
idx = field ? indexOf(field, valueFields) : 0;
|
|
valueGetter = getter('[' + idx + ']');
|
|
} else if (isNumber(item)) {
|
|
valueGetter = getter();
|
|
} else if (typeof item === OBJECT) {
|
|
srcValueFields = binder.sourceFields(series, valueFields);
|
|
valueGetter = getter(srcValueFields[indexOf(field, valueFields)]);
|
|
}
|
|
return valueGetter;
|
|
},
|
|
getErrorRange: function (pointValue, errorValue) {
|
|
var that = this, low, high, value;
|
|
if (!defined(errorValue)) {
|
|
return;
|
|
}
|
|
if (that.globalRange) {
|
|
return that.globalRange(pointValue);
|
|
}
|
|
if (isArray(errorValue)) {
|
|
low = pointValue - errorValue[0];
|
|
high = pointValue + errorValue[1];
|
|
} else if (isNumber(value = parseFloat(errorValue))) {
|
|
low = pointValue - value;
|
|
high = pointValue + value;
|
|
} else if (value = that.percentRegex.exec(errorValue)) {
|
|
var percentValue = pointValue * (parseFloat(value[1]) / 100);
|
|
low = pointValue - math.abs(percentValue);
|
|
high = pointValue + math.abs(percentValue);
|
|
} else {
|
|
throw new Error('Invalid ErrorBar value: ' + errorValue);
|
|
}
|
|
return {
|
|
low: low,
|
|
high: high
|
|
};
|
|
},
|
|
getStandardError: function (data, average) {
|
|
return this.getStandardDeviation(data, average, true) / math.sqrt(average.count);
|
|
},
|
|
getStandardDeviation: function (data, average, isSample) {
|
|
var squareDifferenceSum = 0, length = data.length, total = isSample ? average.count - 1 : average.count, value;
|
|
for (var i = 0; i < length; i++) {
|
|
value = this.valueGetter(data[i]);
|
|
if (isNumber(value)) {
|
|
squareDifferenceSum += math.pow(value - average.value, 2);
|
|
}
|
|
}
|
|
return math.sqrt(squareDifferenceSum / total);
|
|
},
|
|
getAverage: function (data) {
|
|
var sum = 0, count = 0, length = data.length, value;
|
|
for (var i = 0; i < length; i++) {
|
|
value = this.valueGetter(data[i]);
|
|
if (isNumber(value)) {
|
|
sum += value;
|
|
count++;
|
|
}
|
|
}
|
|
return {
|
|
value: sum / count,
|
|
count: count
|
|
};
|
|
}
|
|
};
|
|
var CategoricalChart = ChartElement.extend({
|
|
init: function (plotArea, options) {
|
|
var chart = this;
|
|
ChartElement.fn.init.call(chart, options);
|
|
chart.plotArea = plotArea;
|
|
chart.categoryAxis = plotArea.seriesCategoryAxis(options.series[0]);
|
|
chart.valueAxisRanges = {};
|
|
chart.points = [];
|
|
chart.categoryPoints = [];
|
|
chart.seriesPoints = [];
|
|
chart.seriesOptions = [];
|
|
chart._evalSeries = [];
|
|
chart.render();
|
|
},
|
|
options: {
|
|
series: [],
|
|
invertAxes: false,
|
|
isStacked: false,
|
|
clip: true
|
|
},
|
|
render: function () {
|
|
var chart = this;
|
|
chart.traverseDataPoints(proxy(chart.addValue, chart));
|
|
},
|
|
pointOptions: function (series, seriesIx) {
|
|
var options = this.seriesOptions[seriesIx];
|
|
if (!options) {
|
|
var defaults = this.pointType().fn.defaults;
|
|
this.seriesOptions[seriesIx] = options = deepExtend({}, defaults, { vertical: !this.options.invertAxes }, series);
|
|
}
|
|
return options;
|
|
},
|
|
plotValue: function (point) {
|
|
if (!point) {
|
|
return 0;
|
|
}
|
|
if (this.options.isStacked100 && isNumber(point.value)) {
|
|
var categoryIx = point.categoryIx;
|
|
var categoryPts = this.categoryPoints[categoryIx];
|
|
var categorySum = 0;
|
|
var otherValues = [];
|
|
for (var i = 0; i < categoryPts.length; i++) {
|
|
var other = categoryPts[i];
|
|
if (other) {
|
|
var stack = point.series.stack;
|
|
var otherStack = other.series.stack;
|
|
if (stack && otherStack && stack.group !== otherStack.group) {
|
|
continue;
|
|
}
|
|
if (isNumber(other.value)) {
|
|
categorySum += math.abs(other.value);
|
|
otherValues.push(math.abs(other.value));
|
|
}
|
|
}
|
|
}
|
|
if (categorySum > 0) {
|
|
return point.value / categorySum;
|
|
}
|
|
}
|
|
return point.value;
|
|
},
|
|
plotRange: function (point, startValue) {
|
|
var categoryIx = point.categoryIx;
|
|
var categoryPts = this.categoryPoints[categoryIx];
|
|
if (this.options.isStacked) {
|
|
startValue = startValue || 0;
|
|
var plotValue = this.plotValue(point);
|
|
var positive = plotValue >= 0;
|
|
var prevValue = startValue;
|
|
var isStackedBar = false;
|
|
for (var i = 0; i < categoryPts.length; i++) {
|
|
var other = categoryPts[i];
|
|
if (point === other) {
|
|
break;
|
|
}
|
|
var stack = point.series.stack;
|
|
var otherStack = other.series.stack;
|
|
if (stack && otherStack) {
|
|
if (typeof stack === STRING && stack !== otherStack) {
|
|
continue;
|
|
}
|
|
if (stack.group && stack.group !== otherStack.group) {
|
|
continue;
|
|
}
|
|
}
|
|
var otherValue = this.plotValue(other);
|
|
if (otherValue >= 0 && positive || otherValue < 0 && !positive) {
|
|
prevValue += otherValue;
|
|
plotValue += otherValue;
|
|
isStackedBar = true;
|
|
if (this.options.isStacked100) {
|
|
plotValue = math.min(plotValue, 1);
|
|
}
|
|
}
|
|
}
|
|
if (isStackedBar) {
|
|
prevValue -= startValue;
|
|
}
|
|
return [
|
|
prevValue,
|
|
plotValue
|
|
];
|
|
}
|
|
var series = point.series;
|
|
var valueAxis = this.seriesValueAxis(series);
|
|
var axisCrossingValue = this.categoryAxisCrossingValue(valueAxis);
|
|
return [
|
|
axisCrossingValue,
|
|
point.value || axisCrossingValue
|
|
];
|
|
},
|
|
stackLimits: function (axisName, stackName) {
|
|
var min = MAX_VALUE;
|
|
var max = MIN_VALUE;
|
|
for (var i = 0; i < this.categoryPoints.length; i++) {
|
|
var categoryPts = this.categoryPoints[i];
|
|
if (!categoryPts) {
|
|
continue;
|
|
}
|
|
for (var pIx = 0; pIx < categoryPts.length; pIx++) {
|
|
var point = categoryPts[pIx];
|
|
if (point) {
|
|
if (point.series.stack === stackName || point.series.axis === axisName) {
|
|
var to = this.plotRange(point, 0)[1];
|
|
if (defined(to) && isFinite(to)) {
|
|
max = math.max(max, to);
|
|
min = math.min(min, to);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
updateStackRange: function () {
|
|
var chart = this;
|
|
var chartSeries = chart.options.series;
|
|
var isStacked = chart.options.isStacked;
|
|
var limits;
|
|
var limitsCache = {};
|
|
if (isStacked) {
|
|
for (var i = 0; i < chartSeries.length; i++) {
|
|
var series = chartSeries[i];
|
|
var axisName = series.axis;
|
|
var key = axisName + series.stack;
|
|
limits = limitsCache[key];
|
|
if (!limits) {
|
|
limits = chart.stackLimits(axisName, series.stack);
|
|
var errorTotals = chart.errorTotals;
|
|
if (errorTotals) {
|
|
if (errorTotals.negative.length) {
|
|
limits.min = math.min(limits.min, sparseArrayMin(errorTotals.negative));
|
|
}
|
|
if (errorTotals.positive.length) {
|
|
limits.max = math.max(limits.max, sparseArrayMax(errorTotals.positive));
|
|
}
|
|
}
|
|
if (limits.min !== MAX_VALUE || limits.max !== MIN_VALUE) {
|
|
limitsCache[key] = limits;
|
|
} else {
|
|
limits = null;
|
|
}
|
|
}
|
|
if (limits) {
|
|
chart.valueAxisRanges[axisName] = limits;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
addErrorBar: function (point, data, categoryIx) {
|
|
var chart = this, value = point.value, series = point.series, seriesIx = point.seriesIx, errorBars = point.options.errorBars, errorRange, lowValue = data.fields[ERROR_LOW_FIELD], highValue = data.fields[ERROR_HIGH_FIELD];
|
|
if (isNumber(lowValue) && isNumber(highValue)) {
|
|
errorRange = {
|
|
low: lowValue,
|
|
high: highValue
|
|
};
|
|
} else if (errorBars && defined(errorBars.value)) {
|
|
chart.seriesErrorRanges = chart.seriesErrorRanges || [];
|
|
chart.seriesErrorRanges[seriesIx] = chart.seriesErrorRanges[seriesIx] || new ErrorRangeCalculator(errorBars.value, series, VALUE);
|
|
errorRange = chart.seriesErrorRanges[seriesIx].getErrorRange(value, errorBars.value);
|
|
}
|
|
if (errorRange) {
|
|
point.low = errorRange.low;
|
|
point.high = errorRange.high;
|
|
chart.addPointErrorBar(point, categoryIx);
|
|
}
|
|
},
|
|
addPointErrorBar: function (point, categoryIx) {
|
|
var chart = this, series = point.series, low = point.low, high = point.high, isVertical = !chart.options.invertAxes, options = point.options.errorBars, errorBar, stackedErrorRange;
|
|
if (chart.options.isStacked) {
|
|
stackedErrorRange = chart.stackedErrorRange(point, categoryIx);
|
|
low = stackedErrorRange.low;
|
|
high = stackedErrorRange.high;
|
|
} else {
|
|
var fields = {
|
|
categoryIx: categoryIx,
|
|
series: series
|
|
};
|
|
chart.updateRange({ value: low }, fields);
|
|
chart.updateRange({ value: high }, fields);
|
|
}
|
|
errorBar = new CategoricalErrorBar(low, high, isVertical, chart, series, options);
|
|
point.errorBars = [errorBar];
|
|
point.append(errorBar);
|
|
},
|
|
stackedErrorRange: function (point, categoryIx) {
|
|
var chart = this, plotValue = chart.plotRange(point, 0)[1] - point.value, low = point.low + plotValue, high = point.high + plotValue;
|
|
chart.errorTotals = chart.errorTotals || {
|
|
positive: [],
|
|
negative: []
|
|
};
|
|
if (low < 0) {
|
|
chart.errorTotals.negative[categoryIx] = math.min(chart.errorTotals.negative[categoryIx] || 0, low);
|
|
}
|
|
if (high > 0) {
|
|
chart.errorTotals.positive[categoryIx] = math.max(chart.errorTotals.positive[categoryIx] || 0, high);
|
|
}
|
|
return {
|
|
low: low,
|
|
high: high
|
|
};
|
|
},
|
|
addValue: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var categoryPoints = chart.categoryPoints[categoryIx];
|
|
if (!categoryPoints) {
|
|
chart.categoryPoints[categoryIx] = categoryPoints = [];
|
|
}
|
|
var seriesPoints = chart.seriesPoints[seriesIx];
|
|
if (!seriesPoints) {
|
|
chart.seriesPoints[seriesIx] = seriesPoints = [];
|
|
}
|
|
var point = chart.createPoint(data, fields);
|
|
if (point) {
|
|
$.extend(point, fields);
|
|
point.owner = chart;
|
|
point.dataItem = series.data[categoryIx];
|
|
point.noteText = data.fields.noteText;
|
|
chart.addErrorBar(point, data, categoryIx);
|
|
}
|
|
chart.points.push(point);
|
|
seriesPoints.push(point);
|
|
categoryPoints.push(point);
|
|
chart.updateRange(data.valueFields, fields);
|
|
},
|
|
evalPointOptions: function (options, value, category, categoryIx, series, seriesIx) {
|
|
var state = {
|
|
defaults: series._defaults,
|
|
excluded: [
|
|
'data',
|
|
'aggregate',
|
|
'_events',
|
|
'tooltip',
|
|
'template',
|
|
'visual',
|
|
'toggle',
|
|
'_outOfRangeMinPoint',
|
|
'_outOfRangeMaxPoint'
|
|
]
|
|
};
|
|
var doEval = this._evalSeries[seriesIx];
|
|
if (!defined(doEval)) {
|
|
this._evalSeries[seriesIx] = doEval = evalOptions(options, {}, state, true);
|
|
}
|
|
if (doEval) {
|
|
options = deepExtend({}, options);
|
|
evalOptions(options, {
|
|
value: value,
|
|
category: category,
|
|
index: categoryIx,
|
|
series: series,
|
|
dataItem: series.data[categoryIx]
|
|
}, state);
|
|
}
|
|
return options;
|
|
},
|
|
updateRange: function (data, fields) {
|
|
var chart = this, axisName = fields.series.axis, value = data.value, axisRange = chart.valueAxisRanges[axisName];
|
|
if (isFinite(value) && value !== null) {
|
|
axisRange = chart.valueAxisRanges[axisName] = axisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
axisRange.min = math.min(axisRange.min, value);
|
|
axisRange.max = math.max(axisRange.max, value);
|
|
}
|
|
},
|
|
seriesValueAxis: function (series) {
|
|
var plotArea = this.plotArea, axisName = series.axis, axis = axisName ? plotArea.namedValueAxes[axisName] : plotArea.valueAxis;
|
|
if (!axis) {
|
|
throw new Error('Unable to locate value axis with name ' + axisName);
|
|
}
|
|
return axis;
|
|
},
|
|
reflow: function (targetBox) {
|
|
var chart = this, pointIx = 0, categorySlots = chart.categorySlots = [], chartPoints = chart.points, categoryAxis = chart.categoryAxis, value, valueAxis, point;
|
|
chart.traverseDataPoints(function (data, fields) {
|
|
var categoryIx = fields.categoryIx;
|
|
var currentSeries = fields.series;
|
|
value = chart.pointValue(data);
|
|
valueAxis = chart.seriesValueAxis(currentSeries);
|
|
point = chartPoints[pointIx++];
|
|
var categorySlot = categorySlots[categoryIx];
|
|
if (!categorySlot) {
|
|
categorySlots[categoryIx] = categorySlot = chart.categorySlot(categoryAxis, categoryIx, valueAxis);
|
|
}
|
|
if (point) {
|
|
var plotRange = chart.plotRange(point, valueAxis.startValue());
|
|
var valueSlot = valueAxis.getSlot(plotRange[0], plotRange[1], !chart.options.clip);
|
|
if (valueSlot) {
|
|
var pointSlot = chart.pointSlot(categorySlot, valueSlot);
|
|
point.aboveAxis = chart.aboveAxis(point, valueAxis);
|
|
if (chart.options.isStacked100) {
|
|
point.percentage = chart.plotValue(point);
|
|
}
|
|
chart.reflowPoint(point, pointSlot);
|
|
} else {
|
|
point.visible = false;
|
|
}
|
|
}
|
|
});
|
|
chart.reflowCategories(categorySlots);
|
|
chart.box = targetBox;
|
|
},
|
|
aboveAxis: function (point, valueAxis) {
|
|
var axisCrossingValue = this.categoryAxisCrossingValue(valueAxis);
|
|
var value = point.value;
|
|
return valueAxis.options.reverse ? value < axisCrossingValue : value >= axisCrossingValue;
|
|
},
|
|
categoryAxisCrossingValue: function (valueAxis) {
|
|
var categoryAxis = this.categoryAxis, options = valueAxis.options, crossingValues = [].concat(options.axisCrossingValues || options.axisCrossingValue);
|
|
return crossingValues[categoryAxis.axisIndex || 0] || 0;
|
|
},
|
|
reflowPoint: function (point, pointSlot) {
|
|
point.reflow(pointSlot);
|
|
},
|
|
reflowCategories: function () {
|
|
},
|
|
pointSlot: function (categorySlot, valueSlot) {
|
|
var chart = this, options = chart.options, invertAxes = options.invertAxes, slotX = invertAxes ? valueSlot : categorySlot, slotY = invertAxes ? categorySlot : valueSlot;
|
|
return new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2);
|
|
},
|
|
categorySlot: function (categoryAxis, categoryIx) {
|
|
return categoryAxis.getSlot(categoryIx);
|
|
},
|
|
traverseDataPoints: function (callback) {
|
|
var chart = this, options = chart.options, series = options.series, categories = chart.categoryAxis.options.categories || [], count = categoriesCount(series), categoryIx, seriesIx, pointData, currentCategory, currentSeries, seriesCount = series.length;
|
|
for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
|
|
this._outOfRangeCallback(series[seriesIx], '_outOfRangeMinPoint', seriesIx, callback);
|
|
}
|
|
for (categoryIx = 0; categoryIx < count; categoryIx++) {
|
|
for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
currentCategory = categories[categoryIx];
|
|
pointData = this._bindPoint(currentSeries, seriesIx, categoryIx);
|
|
callback(pointData, {
|
|
category: currentCategory,
|
|
categoryIx: categoryIx,
|
|
series: currentSeries,
|
|
seriesIx: seriesIx
|
|
});
|
|
}
|
|
}
|
|
for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
|
|
this._outOfRangeCallback(series[seriesIx], '_outOfRangeMaxPoint', seriesIx, callback);
|
|
}
|
|
},
|
|
_outOfRangeCallback: function (series, field, seriesIx, callback) {
|
|
var outOfRangePoint = series[field];
|
|
if (outOfRangePoint) {
|
|
var categoryIx = outOfRangePoint.categoryIx;
|
|
var pointData = this._bindPoint(series, seriesIx, categoryIx, outOfRangePoint.item);
|
|
callback(pointData, {
|
|
category: outOfRangePoint.category,
|
|
categoryIx: categoryIx,
|
|
series: series,
|
|
seriesIx: seriesIx
|
|
});
|
|
}
|
|
},
|
|
_bindPoint: function (series, seriesIx, categoryIx, item) {
|
|
if (!this._bindCache) {
|
|
this._bindCache = [];
|
|
}
|
|
var bindCache = this._bindCache[seriesIx];
|
|
if (!bindCache) {
|
|
bindCache = this._bindCache[seriesIx] = [];
|
|
}
|
|
var data = bindCache[categoryIx];
|
|
if (!data) {
|
|
data = bindCache[categoryIx] = SeriesBinder.current.bindPoint(series, categoryIx, item);
|
|
}
|
|
return data;
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
if (point.value === null) {
|
|
return '';
|
|
}
|
|
return autoFormat(format, point.value);
|
|
},
|
|
pointValue: function (data) {
|
|
return data.valueFields.value;
|
|
}
|
|
});
|
|
var BarChart = CategoricalChart.extend({
|
|
options: { animation: { type: BAR } },
|
|
render: function () {
|
|
var chart = this;
|
|
CategoricalChart.fn.render.apply(chart);
|
|
chart.updateStackRange();
|
|
},
|
|
pointType: function () {
|
|
return Bar;
|
|
},
|
|
clusterType: function () {
|
|
return ClusterLayout;
|
|
},
|
|
stackType: function () {
|
|
return StackWrap;
|
|
},
|
|
stackLimits: function (axisName, stackName) {
|
|
var limits = CategoricalChart.fn.stackLimits.call(this, axisName, stackName);
|
|
return limits;
|
|
},
|
|
createPoint: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var value = chart.pointValue(data);
|
|
var options = chart.options;
|
|
var children = chart.children;
|
|
var isStacked = chart.options.isStacked;
|
|
var point;
|
|
var pointType = chart.pointType();
|
|
var pointOptions;
|
|
var cluster;
|
|
var clusterType = chart.clusterType();
|
|
pointOptions = this.pointOptions(series, seriesIx);
|
|
var labelOptions = pointOptions.labels;
|
|
if (isStacked) {
|
|
if (labelOptions.position == OUTSIDE_END) {
|
|
labelOptions.position = INSIDE_END;
|
|
}
|
|
}
|
|
pointOptions.isStacked = isStacked;
|
|
var color = data.fields.color || series.color;
|
|
if (value < 0 && pointOptions.negativeColor) {
|
|
color = pointOptions.negativeColor;
|
|
}
|
|
pointOptions = chart.evalPointOptions(pointOptions, value, category, categoryIx, series, seriesIx);
|
|
if (kendo.isFunction(series.color)) {
|
|
color = pointOptions.color;
|
|
}
|
|
point = new pointType(value, pointOptions);
|
|
point.color = color;
|
|
cluster = children[categoryIx];
|
|
if (!cluster) {
|
|
cluster = new clusterType({
|
|
vertical: options.invertAxes,
|
|
gap: options.gap,
|
|
spacing: options.spacing
|
|
});
|
|
chart.append(cluster);
|
|
}
|
|
if (isStacked) {
|
|
var stackWrap = chart.getStackWrap(series, cluster);
|
|
stackWrap.append(point);
|
|
} else {
|
|
cluster.append(point);
|
|
}
|
|
return point;
|
|
},
|
|
getStackWrap: function (series, cluster) {
|
|
var stack = series.stack;
|
|
var stackGroup = stack ? stack.group || stack : stack;
|
|
var wraps = cluster.children;
|
|
var stackWrap;
|
|
if (typeof stackGroup === STRING) {
|
|
for (var i = 0; i < wraps.length; i++) {
|
|
if (wraps[i]._stackGroup === stackGroup) {
|
|
stackWrap = wraps[i];
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
stackWrap = wraps[0];
|
|
}
|
|
if (!stackWrap) {
|
|
var stackType = this.stackType();
|
|
stackWrap = new stackType({ vertical: !this.options.invertAxes });
|
|
stackWrap._stackGroup = stackGroup;
|
|
cluster.append(stackWrap);
|
|
}
|
|
return stackWrap;
|
|
},
|
|
categorySlot: function (categoryAxis, categoryIx, valueAxis) {
|
|
var chart = this, options = chart.options, categorySlot = categoryAxis.getSlot(categoryIx), startValue = valueAxis.startValue(), stackAxis, zeroSlot;
|
|
if (options.isStacked) {
|
|
zeroSlot = valueAxis.getSlot(startValue, startValue, true);
|
|
stackAxis = options.invertAxes ? X : Y;
|
|
categorySlot[stackAxis + 1] = categorySlot[stackAxis + 2] = zeroSlot[stackAxis + 1];
|
|
}
|
|
return categorySlot;
|
|
},
|
|
reflowCategories: function (categorySlots) {
|
|
var chart = this, children = chart.children, childrenLength = children.length, i;
|
|
for (i = 0; i < childrenLength; i++) {
|
|
children[i].reflow(categorySlots[i]);
|
|
}
|
|
},
|
|
createAnimation: function () {
|
|
this._setAnimationOptions();
|
|
ChartElement.fn.createAnimation.call(this);
|
|
if (anyHasZIndex(this.options.series)) {
|
|
this._setChildrenAnimation();
|
|
}
|
|
},
|
|
_setChildrenAnimation: function () {
|
|
var points = this.points;
|
|
var point, pointVisual;
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
point = points[idx];
|
|
pointVisual = point.visual;
|
|
if (pointVisual && defined(pointVisual.options.zIndex)) {
|
|
point.options.animation = this.options.animation;
|
|
point.createAnimation();
|
|
}
|
|
}
|
|
},
|
|
_setAnimationOptions: function () {
|
|
var options = this.options;
|
|
var animation = options.animation || {};
|
|
var origin;
|
|
if (this.options.isStacked) {
|
|
var valueAxis = this.seriesValueAxis(options.series[0]);
|
|
origin = valueAxis.getSlot(valueAxis.startValue());
|
|
} else {
|
|
origin = this.categoryAxis.getSlot(0);
|
|
}
|
|
animation.origin = new geom.Point(origin.x1, origin.y1);
|
|
animation.vertical = !options.invertAxes;
|
|
}
|
|
});
|
|
var RangeBar = Bar.extend({
|
|
defaults: {
|
|
labels: { format: '{0} - {1}' },
|
|
tooltip: { format: '{1}' }
|
|
},
|
|
createLabel: function () {
|
|
var labels = this.options.labels;
|
|
var fromOptions = deepExtend({}, labels, labels.from);
|
|
var toOptions = deepExtend({}, labels, labels.to);
|
|
if (fromOptions.visible) {
|
|
this.labelFrom = this._createLabel(fromOptions);
|
|
this.append(this.labelFrom);
|
|
}
|
|
if (toOptions.visible) {
|
|
this.labelTo = this._createLabel(toOptions);
|
|
this.append(this.labelTo);
|
|
}
|
|
},
|
|
_createLabel: function (options) {
|
|
var labelText;
|
|
if (options.template) {
|
|
var labelTemplate = template(options.template);
|
|
labelText = labelTemplate({
|
|
dataItem: this.dataItem,
|
|
category: this.category,
|
|
value: this.value,
|
|
percentage: this.percentage,
|
|
runningTotal: this.runningTotal,
|
|
total: this.total,
|
|
series: this.series
|
|
});
|
|
} else {
|
|
labelText = this.formatValue(options.format);
|
|
}
|
|
return new BarLabel(labelText, deepExtend({ vertical: this.options.vertical }, options));
|
|
},
|
|
reflow: function (targetBox) {
|
|
this.render();
|
|
var rangeBar = this, labelFrom = rangeBar.labelFrom, labelTo = rangeBar.labelTo;
|
|
rangeBar.box = targetBox;
|
|
if (labelFrom) {
|
|
labelFrom.options.aboveAxis = rangeBar.value.from > rangeBar.value.to;
|
|
labelFrom.reflow(targetBox);
|
|
}
|
|
if (labelTo) {
|
|
labelTo.options.aboveAxis = rangeBar.value.to > rangeBar.value.from;
|
|
labelTo.reflow(targetBox);
|
|
}
|
|
if (rangeBar.note) {
|
|
rangeBar.note.reflow(targetBox);
|
|
}
|
|
}
|
|
});
|
|
var RangeBarChart = BarChart.extend({
|
|
pointType: function () {
|
|
return RangeBar;
|
|
},
|
|
pointValue: function (data) {
|
|
return data.valueFields;
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
if (point.value.from === null && point.value.to === null) {
|
|
return '';
|
|
}
|
|
return autoFormat(format, point.value.from, point.value.to);
|
|
},
|
|
plotLimits: CategoricalChart.fn.plotLimits,
|
|
plotRange: function (point) {
|
|
if (!point) {
|
|
return 0;
|
|
}
|
|
return [
|
|
point.value.from,
|
|
point.value.to
|
|
];
|
|
},
|
|
updateRange: function (value, fields) {
|
|
var chart = this, axisName = fields.series.axis, from = value.from, to = value.to, axisRange = chart.valueAxisRanges[axisName];
|
|
if (value !== null && isNumber(from) && isNumber(to)) {
|
|
axisRange = chart.valueAxisRanges[axisName] = axisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
axisRange.min = math.min(axisRange.min, from);
|
|
axisRange.max = math.max(axisRange.max, from);
|
|
axisRange.min = math.min(axisRange.min, to);
|
|
axisRange.max = math.max(axisRange.max, to);
|
|
}
|
|
},
|
|
aboveAxis: function (point) {
|
|
var value = point.value;
|
|
return value.from < value.to;
|
|
}
|
|
});
|
|
var BulletChart = CategoricalChart.extend({
|
|
init: function (plotArea, options) {
|
|
var chart = this;
|
|
chart.wrapData(options);
|
|
CategoricalChart.fn.init.call(chart, plotArea, options);
|
|
},
|
|
options: { animation: { type: BAR } },
|
|
wrapData: function (options) {
|
|
var series = options.series, i, data, seriesItem;
|
|
for (i = 0; i < series.length; i++) {
|
|
seriesItem = series[i];
|
|
data = seriesItem.data;
|
|
if (data && !isArray(data[0]) && typeof data[0] != OBJECT) {
|
|
seriesItem.data = [data];
|
|
}
|
|
}
|
|
},
|
|
reflowCategories: function (categorySlots) {
|
|
var chart = this, children = chart.children, childrenLength = children.length, i;
|
|
for (i = 0; i < childrenLength; i++) {
|
|
children[i].reflow(categorySlots[i]);
|
|
}
|
|
},
|
|
plotRange: function (point) {
|
|
var series = point.series;
|
|
var valueAxis = this.seriesValueAxis(series);
|
|
var axisCrossingValue = this.categoryAxisCrossingValue(valueAxis);
|
|
return [
|
|
axisCrossingValue,
|
|
point.value.current || axisCrossingValue
|
|
];
|
|
},
|
|
createPoint: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var value = data.valueFields;
|
|
var options = chart.options;
|
|
var children = chart.children;
|
|
var bullet;
|
|
var bulletOptions;
|
|
var cluster;
|
|
bulletOptions = deepExtend({
|
|
vertical: !options.invertAxes,
|
|
overlay: series.overlay,
|
|
categoryIx: categoryIx,
|
|
invertAxes: options.invertAxes
|
|
}, series);
|
|
var color = data.fields.color || series.color;
|
|
bulletOptions = chart.evalPointOptions(bulletOptions, value, category, categoryIx, series, seriesIx);
|
|
if (kendo.isFunction(series.color)) {
|
|
color = bulletOptions.color;
|
|
}
|
|
bullet = new Bullet(value, bulletOptions);
|
|
bullet.color = color;
|
|
cluster = children[categoryIx];
|
|
if (!cluster) {
|
|
cluster = new ClusterLayout({
|
|
vertical: options.invertAxes,
|
|
gap: options.gap,
|
|
spacing: options.spacing
|
|
});
|
|
chart.append(cluster);
|
|
}
|
|
cluster.append(bullet);
|
|
return bullet;
|
|
},
|
|
updateRange: function (value, fields) {
|
|
var chart = this, axisName = fields.series.axis, current = value.current, target = value.target, axisRange = chart.valueAxisRanges[axisName];
|
|
if (defined(current) && !isNaN(current) && defined(target && !isNaN(target))) {
|
|
axisRange = chart.valueAxisRanges[axisName] = axisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
axisRange.min = math.min.apply(math, [
|
|
axisRange.min,
|
|
current,
|
|
target
|
|
]);
|
|
axisRange.max = math.max.apply(math, [
|
|
axisRange.max,
|
|
current,
|
|
target
|
|
]);
|
|
}
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
return autoFormat(format, point.value.current, point.value.target);
|
|
},
|
|
pointValue: function (data) {
|
|
return data.valueFields.current;
|
|
},
|
|
aboveAxis: function (point) {
|
|
var value = point.value.current;
|
|
return value > 0;
|
|
},
|
|
createAnimation: function () {
|
|
var points = this.points;
|
|
var point;
|
|
this._setAnimationOptions();
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
point = points[idx];
|
|
point.options.animation = this.options.animation;
|
|
point.createAnimation();
|
|
}
|
|
},
|
|
_setAnimationOptions: BarChart.fn._setAnimationOptions
|
|
});
|
|
var Bullet = ChartElement.extend({
|
|
init: function (value, options) {
|
|
var bullet = this;
|
|
ChartElement.fn.init.call(bullet, options);
|
|
bullet.aboveAxis = bullet.options.aboveAxis;
|
|
bullet.color = options.color || WHITE;
|
|
bullet.value = value;
|
|
},
|
|
options: {
|
|
border: { width: 1 },
|
|
vertical: false,
|
|
opacity: 1,
|
|
target: {
|
|
shape: '',
|
|
border: {
|
|
width: 0,
|
|
color: 'green'
|
|
},
|
|
line: { width: 2 }
|
|
},
|
|
tooltip: { format: 'Current: {0}</br>Target: {1}' }
|
|
},
|
|
render: function () {
|
|
var bullet = this, options = bullet.options;
|
|
if (!bullet._rendered) {
|
|
bullet._rendered = true;
|
|
if (defined(bullet.value.target)) {
|
|
bullet.target = new Target({
|
|
type: options.target.shape,
|
|
background: options.target.color || bullet.color,
|
|
opacity: options.opacity,
|
|
zIndex: options.zIndex,
|
|
border: options.target.border,
|
|
vAlign: TOP,
|
|
align: RIGHT
|
|
});
|
|
bullet.append(bullet.target);
|
|
}
|
|
bullet.createNote();
|
|
}
|
|
},
|
|
reflow: function (box) {
|
|
this.render();
|
|
var bullet = this, options = bullet.options, chart = bullet.owner, target = bullet.target, invertAxes = options.invertAxes, valueAxis = chart.seriesValueAxis(bullet.options), categorySlot = chart.categorySlot(chart.categoryAxis, options.categoryIx, valueAxis), targetValueSlot = valueAxis.getSlot(bullet.value.target), targetSlotX = invertAxes ? targetValueSlot : categorySlot, targetSlotY = invertAxes ? categorySlot : targetValueSlot, targetSlot;
|
|
if (target) {
|
|
targetSlot = new Box2D(targetSlotX.x1, targetSlotY.y1, targetSlotX.x2, targetSlotY.y2);
|
|
target.options.height = invertAxes ? targetSlot.height() : options.target.line.width;
|
|
target.options.width = invertAxes ? options.target.line.width : targetSlot.width();
|
|
target.reflow(targetSlot);
|
|
}
|
|
if (bullet.note) {
|
|
bullet.note.reflow(box);
|
|
}
|
|
bullet.box = box;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var options = this.options;
|
|
var body = draw.Path.fromRect(this.box.toRect(), {
|
|
fill: {
|
|
color: this.color,
|
|
opacity: options.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
if (options.border.width > 0) {
|
|
body.options.set('stroke', {
|
|
color: options.border.color || this.color,
|
|
width: options.border.width,
|
|
dashType: options.border.dashType,
|
|
opacity: valueOrDefault(options.border.opacity, options.opacity)
|
|
});
|
|
}
|
|
this.bodyVisual = body;
|
|
alignPathToPixel(body);
|
|
this.visual.append(body);
|
|
},
|
|
createAnimation: function () {
|
|
if (this.bodyVisual) {
|
|
this.animation = draw.Animation.create(this.bodyVisual, this.options.animation);
|
|
}
|
|
},
|
|
tooltipAnchor: Bar.fn.tooltipAnchor,
|
|
createHighlight: function (style) {
|
|
return draw.Path.fromRect(this.box.toRect(), style);
|
|
},
|
|
highlightVisual: function () {
|
|
return this.bodyVisual;
|
|
},
|
|
highlightVisualArgs: function () {
|
|
return {
|
|
rect: this.box.toRect(),
|
|
visual: this.bodyVisual,
|
|
options: this.options
|
|
};
|
|
},
|
|
formatValue: function (format) {
|
|
var bullet = this;
|
|
return bullet.owner.formatPointValue(bullet, format);
|
|
}
|
|
});
|
|
deepExtend(Bullet.fn, PointEventsMixin);
|
|
deepExtend(Bullet.fn, NoteMixin);
|
|
var Target = ShapeElement.extend();
|
|
deepExtend(Target.fn, PointEventsMixin);
|
|
var ErrorBarBase = ChartElement.extend({
|
|
init: function (low, high, isVertical, chart, series, options) {
|
|
var errorBar = this;
|
|
errorBar.low = low;
|
|
errorBar.high = high;
|
|
errorBar.isVertical = isVertical;
|
|
errorBar.chart = chart;
|
|
errorBar.series = series;
|
|
ChartElement.fn.init.call(errorBar, options);
|
|
},
|
|
options: {
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: INITIAL_ANIMATION_DURATION
|
|
},
|
|
endCaps: true,
|
|
line: { width: 1 },
|
|
zIndex: 1
|
|
},
|
|
getAxis: function () {
|
|
},
|
|
reflow: function (targetBox) {
|
|
var linePoints, errorBar = this, endCaps = errorBar.options.endCaps, isVertical = errorBar.isVertical, axis = errorBar.getAxis(), valueBox = axis.getSlot(errorBar.low, errorBar.high), centerBox = targetBox.center(), capsWidth = errorBar.getCapsWidth(targetBox, isVertical), capValue = isVertical ? centerBox.x : centerBox.y, capStart = capValue - capsWidth, capEnd = capValue + capsWidth;
|
|
if (isVertical) {
|
|
linePoints = [
|
|
Point2D(centerBox.x, valueBox.y1),
|
|
Point2D(centerBox.x, valueBox.y2)
|
|
];
|
|
if (endCaps) {
|
|
linePoints.push(Point2D(capStart, valueBox.y1), Point2D(capEnd, valueBox.y1), Point2D(capStart, valueBox.y2), Point2D(capEnd, valueBox.y2));
|
|
}
|
|
errorBar.box = Box2D(capStart, valueBox.y1, capEnd, valueBox.y2);
|
|
} else {
|
|
linePoints = [
|
|
Point2D(valueBox.x1, centerBox.y),
|
|
Point2D(valueBox.x2, centerBox.y)
|
|
];
|
|
if (endCaps) {
|
|
linePoints.push(Point2D(valueBox.x1, capStart), Point2D(valueBox.x1, capEnd), Point2D(valueBox.x2, capStart), Point2D(valueBox.x2, capEnd));
|
|
}
|
|
errorBar.box = Box2D(valueBox.x1, capStart, valueBox.x2, capEnd);
|
|
}
|
|
errorBar.linePoints = linePoints;
|
|
},
|
|
getCapsWidth: function (box, isVertical) {
|
|
var boxSize = isVertical ? box.width() : box.height(), capsWidth = math.min(math.floor(boxSize / 2), DEFAULT_ERROR_BAR_WIDTH) || DEFAULT_ERROR_BAR_WIDTH;
|
|
return capsWidth;
|
|
},
|
|
createVisual: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var visual = options.visual;
|
|
if (visual) {
|
|
that.visual = visual({
|
|
low: that.low,
|
|
high: that.high,
|
|
rect: that.box.toRect(),
|
|
sender: that.getChart(),
|
|
options: {
|
|
endCaps: options.endCaps,
|
|
color: options.color,
|
|
line: options.line
|
|
},
|
|
createVisual: function () {
|
|
that.createDefaultVisual();
|
|
var defaultVisual = that.visual;
|
|
delete that.visual;
|
|
return defaultVisual;
|
|
}
|
|
});
|
|
} else {
|
|
that.createDefaultVisual();
|
|
}
|
|
},
|
|
createDefaultVisual: function () {
|
|
var errorBar = this, options = errorBar.options, lineOptions = {
|
|
stroke: {
|
|
color: options.color,
|
|
width: options.line.width,
|
|
dashType: options.line.dashType
|
|
}
|
|
}, linePoints = errorBar.linePoints;
|
|
ChartElement.fn.createVisual.call(this);
|
|
for (var idx = 0; idx < linePoints.length; idx += 2) {
|
|
var line = new draw.Path(lineOptions).moveTo(linePoints[idx].x, linePoints[idx].y).lineTo(linePoints[idx + 1].x, linePoints[idx + 1].y);
|
|
this.visual.append(line);
|
|
}
|
|
}
|
|
});
|
|
var CategoricalErrorBar = ErrorBarBase.extend({
|
|
getAxis: function () {
|
|
var errorBar = this, chart = errorBar.chart, series = errorBar.series, axis = chart.seriesValueAxis(series);
|
|
return axis;
|
|
}
|
|
});
|
|
var ScatterErrorBar = ErrorBarBase.extend({
|
|
getAxis: function () {
|
|
var errorBar = this, chart = errorBar.chart, series = errorBar.series, axes = chart.seriesAxes(series), axis = errorBar.isVertical ? axes.y : axes.x;
|
|
return axis;
|
|
}
|
|
});
|
|
var LinePoint = ChartElement.extend({
|
|
init: function (value, options) {
|
|
var point = this;
|
|
ChartElement.fn.init.call(point);
|
|
point.value = value;
|
|
point.options = options;
|
|
point.aboveAxis = valueOrDefault(point.options.aboveAxis, true);
|
|
point.tooltipTracking = true;
|
|
},
|
|
defaults: {
|
|
vertical: true,
|
|
markers: {
|
|
visible: true,
|
|
background: WHITE,
|
|
size: LINE_MARKER_SIZE,
|
|
type: CIRCLE,
|
|
border: { width: 2 },
|
|
opacity: 1
|
|
},
|
|
labels: {
|
|
visible: false,
|
|
position: ABOVE,
|
|
margin: getSpacing(3),
|
|
padding: getSpacing(4),
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: INITIAL_ANIMATION_DURATION
|
|
}
|
|
},
|
|
notes: { label: {} },
|
|
highlight: { markers: { border: {} } }
|
|
},
|
|
render: function () {
|
|
var point = this, options = point.options, markers = options.markers, labels = options.labels, labelText = point.value;
|
|
if (point._rendered) {
|
|
return;
|
|
} else {
|
|
point._rendered = true;
|
|
}
|
|
if (markers.visible && markers.size) {
|
|
point.marker = point.createMarker();
|
|
point.append(point.marker);
|
|
}
|
|
if (labels.visible) {
|
|
if (labels.template) {
|
|
var labelTemplate = template(labels.template);
|
|
labelText = labelTemplate({
|
|
dataItem: point.dataItem,
|
|
category: point.category,
|
|
value: point.value,
|
|
percentage: point.percentage,
|
|
series: point.series
|
|
});
|
|
} else if (labels.format) {
|
|
labelText = point.formatValue(labels.format);
|
|
}
|
|
point.label = new TextBox(labelText, deepExtend({
|
|
align: CENTER,
|
|
vAlign: CENTER,
|
|
margin: {
|
|
left: 5,
|
|
right: 5
|
|
},
|
|
zIndex: valueOrDefault(labels.zIndex, this.series.zIndex)
|
|
}, labels));
|
|
point.append(point.label);
|
|
}
|
|
point.createNote();
|
|
if (point.errorBar) {
|
|
point.append(point.errorBar);
|
|
}
|
|
},
|
|
markerBorder: function () {
|
|
var options = this.options.markers;
|
|
var background = options.background;
|
|
var border = deepExtend({ color: this.color }, options.border);
|
|
if (!defined(border.color)) {
|
|
border.color = new Color(background).brightness(BAR_BORDER_BRIGHTNESS).toHex();
|
|
}
|
|
return border;
|
|
},
|
|
createVisual: noop,
|
|
createMarker: function () {
|
|
var options = this.options.markers;
|
|
var marker = new ShapeElement({
|
|
type: options.type,
|
|
width: options.size,
|
|
height: options.size,
|
|
rotation: options.rotation,
|
|
background: options.background,
|
|
border: this.markerBorder(),
|
|
opacity: options.opacity,
|
|
zIndex: valueOrDefault(options.zIndex, this.series.zIndex),
|
|
animation: options.animation,
|
|
visual: options.visual
|
|
}, {
|
|
dataItem: this.dataItem,
|
|
value: this.value,
|
|
series: this.series,
|
|
category: this.category
|
|
});
|
|
return marker;
|
|
},
|
|
markerBox: function () {
|
|
if (!this.marker) {
|
|
this.marker = this.createMarker();
|
|
this.marker.reflow(this._childBox);
|
|
}
|
|
return this.marker.box;
|
|
},
|
|
reflow: function (targetBox) {
|
|
var point = this, options = point.options, vertical = options.vertical, aboveAxis = point.aboveAxis, childBox, center;
|
|
point.render();
|
|
point.box = targetBox;
|
|
childBox = targetBox.clone();
|
|
if (vertical) {
|
|
if (aboveAxis) {
|
|
childBox.y1 -= childBox.height();
|
|
} else {
|
|
childBox.y2 += childBox.height();
|
|
}
|
|
} else {
|
|
if (aboveAxis) {
|
|
childBox.x1 += childBox.width();
|
|
} else {
|
|
childBox.x2 -= childBox.width();
|
|
}
|
|
}
|
|
point._childBox = childBox;
|
|
if (point.marker) {
|
|
point.marker.reflow(childBox);
|
|
}
|
|
point.reflowLabel(childBox);
|
|
if (point.errorBars) {
|
|
for (var i = 0; i < point.errorBars.length; i++) {
|
|
point.errorBars[i].reflow(childBox);
|
|
}
|
|
}
|
|
if (point.note) {
|
|
var noteTargetBox = point.markerBox();
|
|
if (!(options.markers.visible && options.markers.size)) {
|
|
center = noteTargetBox.center();
|
|
noteTargetBox = Box2D(center.x, center.y, center.x, center.y);
|
|
}
|
|
point.note.reflow(noteTargetBox);
|
|
}
|
|
},
|
|
reflowLabel: function (box) {
|
|
var point = this, options = point.options, label = point.label, anchor = options.labels.position;
|
|
if (label) {
|
|
anchor = anchor === ABOVE ? TOP : anchor;
|
|
anchor = anchor === BELOW ? BOTTOM : anchor;
|
|
label.reflow(box);
|
|
label.box.alignTo(point.markerBox(), anchor);
|
|
label.reflow(label.box);
|
|
}
|
|
},
|
|
createHighlight: function () {
|
|
var highlight = this.options.highlight;
|
|
var markers = highlight.markers;
|
|
var defaultColor = this.markerBorder().color;
|
|
var options = this.options.markers;
|
|
var shadow = new ShapeElement({
|
|
type: options.type,
|
|
width: options.size,
|
|
height: options.size,
|
|
rotation: options.rotation,
|
|
background: markers.color || defaultColor,
|
|
border: {
|
|
color: markers.border.color,
|
|
width: markers.border.width,
|
|
opacity: valueOrDefault(markers.border.opacity, 1)
|
|
},
|
|
opacity: valueOrDefault(markers.opacity, 1)
|
|
});
|
|
shadow.reflow(this._childBox);
|
|
return shadow.getElement();
|
|
},
|
|
highlightVisual: function () {
|
|
return (this.marker || {}).visual;
|
|
},
|
|
highlightVisualArgs: function () {
|
|
var marker = this.marker;
|
|
var visual;
|
|
var rect;
|
|
if (marker) {
|
|
rect = marker.paddingBox.toRect();
|
|
visual = marker.visual;
|
|
} else {
|
|
var size = this.options.markers.size;
|
|
var halfSize = size / 2;
|
|
var center = this.box.center();
|
|
rect = new geom.Rect([
|
|
center.x - halfSize,
|
|
center.y - halfSize
|
|
], [
|
|
size,
|
|
size
|
|
]);
|
|
}
|
|
return {
|
|
options: this.options,
|
|
rect: rect,
|
|
visual: visual
|
|
};
|
|
},
|
|
tooltipAnchor: function (tooltipWidth, tooltipHeight) {
|
|
var point = this, markerBox = point.markerBox(), aboveAxis = point.aboveAxis, x = markerBox.x2 + TOOLTIP_OFFSET, y = aboveAxis ? markerBox.y1 - tooltipHeight : markerBox.y2, clipBox = point.owner.pane.clipBox(), showTooltip = !clipBox || clipBox.overlaps(markerBox);
|
|
if (showTooltip) {
|
|
return Point2D(x, y);
|
|
}
|
|
},
|
|
formatValue: function (format) {
|
|
var point = this;
|
|
return point.owner.formatPointValue(point, format);
|
|
},
|
|
overlapsBox: function (box) {
|
|
var markerBox = this.markerBox();
|
|
return markerBox.overlaps(box);
|
|
}
|
|
});
|
|
deepExtend(LinePoint.fn, PointEventsMixin);
|
|
deepExtend(LinePoint.fn, NoteMixin);
|
|
var Bubble = LinePoint.extend({
|
|
init: function (value, options) {
|
|
var point = this;
|
|
LinePoint.fn.init.call(point, value, options);
|
|
point.category = value.category;
|
|
},
|
|
defaults: {
|
|
labels: { position: CENTER },
|
|
highlight: {
|
|
opacity: 1,
|
|
border: {
|
|
width: 1,
|
|
opacity: 1
|
|
}
|
|
}
|
|
},
|
|
createHighlight: function () {
|
|
var highlight = this.options.highlight;
|
|
var border = highlight.border;
|
|
var markers = this.options.markers;
|
|
var center = this.box.center();
|
|
var radius = markers.size / 2 - border.width / 2;
|
|
var overlay = new draw.Circle(new geom.Circle([
|
|
center.x,
|
|
center.y
|
|
], radius), {
|
|
stroke: {
|
|
color: border.color || new Color(markers.background).brightness(BAR_BORDER_BRIGHTNESS).toHex(),
|
|
width: border.width,
|
|
opacity: border.opacity
|
|
},
|
|
fill: {
|
|
color: markers.background,
|
|
opacity: highlight.opacity
|
|
}
|
|
});
|
|
return overlay;
|
|
}
|
|
});
|
|
var LineSegment = ChartElement.extend({
|
|
init: function (linePoints, series, seriesIx) {
|
|
var segment = this;
|
|
ChartElement.fn.init.call(segment);
|
|
segment.linePoints = linePoints;
|
|
segment.series = series;
|
|
segment.seriesIx = seriesIx;
|
|
},
|
|
options: { closed: false },
|
|
points: function (visualPoints) {
|
|
var segment = this, linePoints = segment.linePoints.concat(visualPoints || []), points = [];
|
|
for (var i = 0, length = linePoints.length; i < length; i++) {
|
|
if (linePoints[i].visible !== false) {
|
|
points.push(linePoints[i]._childBox.toRect().center());
|
|
}
|
|
}
|
|
return points;
|
|
},
|
|
createVisual: function () {
|
|
var options = this.options;
|
|
var series = this.series;
|
|
var defaults = series._defaults;
|
|
var color = series.color;
|
|
if (isFn(color) && defaults) {
|
|
color = defaults.color;
|
|
}
|
|
var line = draw.Path.fromPoints(this.points(), {
|
|
stroke: {
|
|
color: color,
|
|
width: series.width,
|
|
opacity: series.opacity,
|
|
dashType: series.dashType
|
|
},
|
|
zIndex: series.zIndex
|
|
});
|
|
if (options.closed) {
|
|
line.close();
|
|
}
|
|
this.visual = line;
|
|
},
|
|
aliasFor: function (e, coords) {
|
|
var segment = this, seriesIx = segment.seriesIx;
|
|
return segment.parent.getNearestPoint(coords.x, coords.y, seriesIx);
|
|
}
|
|
});
|
|
var LineChartMixin = {
|
|
renderSegments: function () {
|
|
var chart = this, options = chart.options, series = options.series, seriesPoints = chart.seriesPoints, currentSeries, seriesIx, seriesCount = seriesPoints.length, sortedPoints, linePoints, point, pointIx, pointCount, lastSegment;
|
|
this._segments = [];
|
|
for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
sortedPoints = chart.sortPoints(seriesPoints[seriesIx]);
|
|
pointCount = sortedPoints.length;
|
|
linePoints = [];
|
|
for (pointIx = 0; pointIx < pointCount; pointIx++) {
|
|
point = sortedPoints[pointIx];
|
|
if (point) {
|
|
linePoints.push(point);
|
|
} else if (chart.seriesMissingValues(currentSeries) !== INTERPOLATE) {
|
|
if (linePoints.length > 1) {
|
|
lastSegment = chart.createSegment(linePoints, currentSeries, seriesIx, lastSegment);
|
|
this._addSegment(lastSegment);
|
|
}
|
|
linePoints = [];
|
|
}
|
|
}
|
|
if (linePoints.length > 1) {
|
|
lastSegment = chart.createSegment(linePoints, currentSeries, seriesIx, lastSegment);
|
|
this._addSegment(lastSegment);
|
|
}
|
|
}
|
|
this.children.unshift.apply(this.children, this._segments);
|
|
},
|
|
_addSegment: function (segment) {
|
|
this._segments.push(segment);
|
|
segment.parent = this;
|
|
},
|
|
sortPoints: function (points) {
|
|
return points;
|
|
},
|
|
seriesMissingValues: function (series) {
|
|
var missingValues = series.missingValues, assumeZero = !missingValues && this.options.isStacked;
|
|
return assumeZero ? ZERO : missingValues || INTERPOLATE;
|
|
},
|
|
getNearestPoint: function (x, y, seriesIx) {
|
|
var target = new Point2D(x, y);
|
|
var allPoints = this.seriesPoints[seriesIx];
|
|
var nearestPointDistance = MAX_VALUE;
|
|
var nearestPoint;
|
|
for (var i = 0; i < allPoints.length; i++) {
|
|
var point = allPoints[i];
|
|
if (point && defined(point.value) && point.value !== null && point.visible !== false) {
|
|
var pointBox = point.box;
|
|
var pointDistance = pointBox.center().distanceTo(target);
|
|
if (pointDistance < nearestPointDistance) {
|
|
nearestPoint = point;
|
|
nearestPointDistance = pointDistance;
|
|
}
|
|
}
|
|
}
|
|
return nearestPoint;
|
|
}
|
|
};
|
|
var ClipAnimationMixin = {
|
|
createAnimation: function () {
|
|
var root = this.getRoot();
|
|
if (root && (root.options || {}).transitions !== false) {
|
|
var box = root.box;
|
|
var clipPath = draw.Path.fromRect(box.toRect());
|
|
this.visual.clip(clipPath);
|
|
this.animation = new ClipAnimation(clipPath, { box: box });
|
|
if (anyHasZIndex(this.options.series)) {
|
|
this._setChildrenAnimation(clipPath);
|
|
}
|
|
}
|
|
},
|
|
_setChildrenAnimation: function (clipPath) {
|
|
var points = this.animationPoints();
|
|
var point;
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
point = points[idx];
|
|
if (point && point.visual && defined(point.visual.options.zIndex)) {
|
|
point.visual.clip(clipPath);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var LineChart = CategoricalChart.extend({
|
|
render: function () {
|
|
var chart = this;
|
|
CategoricalChart.fn.render.apply(chart);
|
|
chart.updateStackRange();
|
|
chart.renderSegments();
|
|
},
|
|
pointType: function () {
|
|
return LinePoint;
|
|
},
|
|
createPoint: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var value = data.valueFields.value;
|
|
var missingValues = chart.seriesMissingValues(series);
|
|
var point;
|
|
var pointOptions;
|
|
if (!defined(value) || value === null) {
|
|
if (missingValues === ZERO) {
|
|
value = 0;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
pointOptions = this.pointOptions(series, seriesIx);
|
|
pointOptions = chart.evalPointOptions(pointOptions, value, category, categoryIx, series, seriesIx);
|
|
var color = data.fields.color || series.color;
|
|
if (kendo.isFunction(series.color)) {
|
|
color = pointOptions.color;
|
|
}
|
|
point = new LinePoint(value, pointOptions);
|
|
point.color = color;
|
|
chart.append(point);
|
|
return point;
|
|
},
|
|
plotRange: function (point) {
|
|
var plotValue = this.plotValue(point);
|
|
if (this.options.isStacked) {
|
|
var categoryIx = point.categoryIx;
|
|
var categoryPts = this.categoryPoints[categoryIx];
|
|
for (var i = 0; i < categoryPts.length; i++) {
|
|
var other = categoryPts[i];
|
|
if (point === other) {
|
|
break;
|
|
}
|
|
plotValue += this.plotValue(other);
|
|
}
|
|
}
|
|
return [
|
|
plotValue,
|
|
plotValue
|
|
];
|
|
},
|
|
createSegment: function (linePoints, currentSeries, seriesIx) {
|
|
var pointType, style = currentSeries.style;
|
|
if (style === STEP) {
|
|
pointType = StepLineSegment;
|
|
} else if (style === SMOOTH) {
|
|
pointType = SplineSegment;
|
|
} else {
|
|
pointType = LineSegment;
|
|
}
|
|
return new pointType(linePoints, currentSeries, seriesIx);
|
|
},
|
|
animationPoints: function () {
|
|
var points = this.points;
|
|
var result = [];
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
result.push((points[idx] || {}).marker);
|
|
}
|
|
return result.concat(this._segments);
|
|
}
|
|
});
|
|
deepExtend(LineChart.fn, LineChartMixin, ClipAnimationMixin);
|
|
var ClipAnimation = draw.Animation.extend({
|
|
options: { duration: INITIAL_ANIMATION_DURATION },
|
|
setup: function () {
|
|
this._setEnd(this.options.box.x1);
|
|
},
|
|
step: function (pos) {
|
|
var box = this.options.box;
|
|
this._setEnd(interpolate(box.x1, box.x2, pos));
|
|
},
|
|
_setEnd: function (x) {
|
|
var element = this.element;
|
|
var segments = element.segments;
|
|
var topRight = segments[1].anchor();
|
|
var bottomRight = segments[2].anchor();
|
|
element.suspend();
|
|
topRight.setX(x);
|
|
element.resume();
|
|
bottomRight.setX(x);
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(CLIP, ClipAnimation);
|
|
var StepLineSegment = LineSegment.extend({
|
|
points: function (visualPoints) {
|
|
var segment = this, points;
|
|
points = segment.calculateStepPoints(segment.linePoints);
|
|
if (visualPoints && visualPoints.length) {
|
|
points = points.concat(segment.calculateStepPoints(visualPoints).reverse());
|
|
}
|
|
return points;
|
|
},
|
|
calculateStepPoints: function (points) {
|
|
var segment = this, chart = segment.parent, plotArea = chart.plotArea, categoryAxis = plotArea.seriesCategoryAxis(segment.series), isInterpolate = chart.seriesMissingValues(segment.series) === INTERPOLATE, length = points.length, reverse = categoryAxis.options.reverse, vertical = categoryAxis.options.vertical, dir = reverse ? 2 : 1, revDir = reverse ? 1 : 2, prevPoint, point, i, prevMarkerBoxCenter, markerBoxCenter, result = [];
|
|
for (i = 1; i < length; i++) {
|
|
prevPoint = points[i - 1];
|
|
point = points[i];
|
|
prevMarkerBoxCenter = prevPoint.markerBox().center();
|
|
markerBoxCenter = point.markerBox().center();
|
|
if (categoryAxis.options.justified) {
|
|
result.push(new geom.Point(prevMarkerBoxCenter.x, prevMarkerBoxCenter.y));
|
|
if (vertical) {
|
|
result.push(new geom.Point(prevMarkerBoxCenter.x, markerBoxCenter.y));
|
|
} else {
|
|
result.push(new geom.Point(markerBoxCenter.x, prevMarkerBoxCenter.y));
|
|
}
|
|
result.push(new geom.Point(markerBoxCenter.x, markerBoxCenter.y));
|
|
} else {
|
|
if (vertical) {
|
|
result.push(new geom.Point(prevMarkerBoxCenter.x, prevPoint.box[Y + dir]));
|
|
result.push(new geom.Point(prevMarkerBoxCenter.x, prevPoint.box[Y + revDir]));
|
|
if (isInterpolate) {
|
|
result.push(new geom.Point(prevMarkerBoxCenter.x, point.box[Y + dir]));
|
|
}
|
|
result.push(new geom.Point(markerBoxCenter.x, point.box[Y + dir]));
|
|
result.push(new geom.Point(markerBoxCenter.x, point.box[Y + revDir]));
|
|
} else {
|
|
result.push(new geom.Point(prevPoint.box[X + dir], prevMarkerBoxCenter.y));
|
|
result.push(new geom.Point(prevPoint.box[X + revDir], prevMarkerBoxCenter.y));
|
|
if (isInterpolate) {
|
|
result.push(new geom.Point(point.box[X + dir], prevMarkerBoxCenter.y));
|
|
}
|
|
result.push(new geom.Point(point.box[X + dir], markerBoxCenter.y));
|
|
result.push(new geom.Point(point.box[X + revDir], markerBoxCenter.y));
|
|
}
|
|
}
|
|
}
|
|
return result || [];
|
|
}
|
|
});
|
|
var SplineSegment = LineSegment.extend({
|
|
createVisual: function () {
|
|
var series = this.series;
|
|
var defaults = series._defaults;
|
|
var color = series.color;
|
|
if (isFn(color) && defaults) {
|
|
color = defaults.color;
|
|
}
|
|
var curveProcessor = new CurveProcessor(this.options.closed);
|
|
var segments = curveProcessor.process(this.points());
|
|
var curve = new draw.Path({
|
|
stroke: {
|
|
color: color,
|
|
width: series.width,
|
|
opacity: series.opacity,
|
|
dashType: series.dashType
|
|
},
|
|
zIndex: series.zIndex
|
|
});
|
|
curve.segments.push.apply(curve.segments, segments);
|
|
this.visual = curve;
|
|
}
|
|
});
|
|
var AreaSegmentMixin = {
|
|
points: function () {
|
|
var segment = this, chart = segment.parent, plotArea = chart.plotArea, invertAxes = chart.options.invertAxes, valueAxis = chart.seriesValueAxis(segment.series), valueAxisLineBox = valueAxis.lineBox(), categoryAxis = plotArea.seriesCategoryAxis(segment.series), categoryAxisLineBox = categoryAxis.lineBox(), end = invertAxes ? categoryAxisLineBox.x1 : categoryAxisLineBox.y1, stackPoints = segment.stackPoints, points = segment._linePoints(stackPoints), pos = invertAxes ? X : Y, firstPoint, lastPoint;
|
|
end = limitValue(end, valueAxisLineBox[pos + 1], valueAxisLineBox[pos + 2]);
|
|
if (!segment.stackPoints && points.length > 1) {
|
|
firstPoint = points[0];
|
|
lastPoint = last(points);
|
|
if (invertAxes) {
|
|
points.unshift(new geom.Point(end, firstPoint.y));
|
|
points.push(new geom.Point(end, lastPoint.y));
|
|
} else {
|
|
points.unshift(new geom.Point(firstPoint.x, end));
|
|
points.push(new geom.Point(lastPoint.x, end));
|
|
}
|
|
}
|
|
return points;
|
|
},
|
|
createVisual: function () {
|
|
var series = this.series;
|
|
var defaults = series._defaults;
|
|
var color = series.color;
|
|
if (isFn(color) && defaults) {
|
|
color = defaults.color;
|
|
}
|
|
this.visual = new draw.Group({ zIndex: series.zIndex });
|
|
this.createArea(color);
|
|
this.createLine(color);
|
|
},
|
|
createLine: function (color) {
|
|
var series = this.series;
|
|
var lineOptions = deepExtend({
|
|
color: color,
|
|
opacity: series.opacity
|
|
}, series.line);
|
|
if (lineOptions.visible !== false && lineOptions.width > 0) {
|
|
var line = draw.Path.fromPoints(this._linePoints(), {
|
|
stroke: {
|
|
color: lineOptions.color,
|
|
width: lineOptions.width,
|
|
opacity: lineOptions.opacity,
|
|
dashType: lineOptions.dashType,
|
|
lineCap: 'butt'
|
|
}
|
|
});
|
|
this.visual.append(line);
|
|
}
|
|
},
|
|
createArea: function (color) {
|
|
var series = this.series;
|
|
var area = draw.Path.fromPoints(this.points(), {
|
|
fill: {
|
|
color: color,
|
|
opacity: series.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
this.visual.append(area);
|
|
}
|
|
};
|
|
var AreaSegment = LineSegment.extend({
|
|
init: function (linePoints, stackPoints, currentSeries, seriesIx) {
|
|
var segment = this;
|
|
segment.stackPoints = stackPoints;
|
|
LineSegment.fn.init.call(segment, linePoints, currentSeries, seriesIx);
|
|
},
|
|
_linePoints: LineSegment.fn.points
|
|
});
|
|
deepExtend(AreaSegment.fn, AreaSegmentMixin);
|
|
var AreaChart = LineChart.extend({
|
|
createSegment: function (linePoints, currentSeries, seriesIx, prevSegment) {
|
|
var chart = this, options = chart.options, isStacked = options.isStacked, stackPoints, pointType, style = (currentSeries.line || {}).style;
|
|
if (isStacked && seriesIx > 0 && prevSegment) {
|
|
var missingValues = this.seriesMissingValues(currentSeries);
|
|
if (missingValues != 'gap') {
|
|
stackPoints = prevSegment.linePoints;
|
|
} else {
|
|
stackPoints = this._gapStackPoints(linePoints, seriesIx, style);
|
|
}
|
|
if (style !== STEP) {
|
|
stackPoints = stackPoints.slice(0).reverse();
|
|
}
|
|
}
|
|
if (style === SMOOTH) {
|
|
return new SplineAreaSegment(linePoints, prevSegment, isStacked, currentSeries, seriesIx);
|
|
}
|
|
if (style === STEP) {
|
|
pointType = StepAreaSegment;
|
|
} else {
|
|
pointType = AreaSegment;
|
|
}
|
|
return new pointType(linePoints, stackPoints, currentSeries, seriesIx);
|
|
},
|
|
reflow: function (targetBox) {
|
|
LineChart.fn.reflow.call(this, targetBox);
|
|
var stackPoints = this._stackPoints;
|
|
if (stackPoints) {
|
|
var stackPoint, pointSlot;
|
|
for (var idx = 0; idx < stackPoints.length; idx++) {
|
|
stackPoint = stackPoints[idx];
|
|
pointSlot = this.categoryAxis.getSlot(stackPoint.categoryIx);
|
|
stackPoint.reflow(pointSlot);
|
|
}
|
|
}
|
|
},
|
|
_gapStackPoints: function (linePoints, seriesIx, style) {
|
|
var seriesPoints = this.seriesPoints;
|
|
var startIdx = linePoints[0].categoryIx;
|
|
var endIdx = startIdx + linePoints.length;
|
|
var stackPoints = [];
|
|
var currentSeriesIx;
|
|
var point, gapStackPoint;
|
|
this._stackPoints = this._stackPoints || [];
|
|
for (var idx = startIdx; idx < endIdx; idx++) {
|
|
currentSeriesIx = seriesIx;
|
|
do {
|
|
currentSeriesIx--;
|
|
point = seriesPoints[currentSeriesIx][idx];
|
|
} while (currentSeriesIx > 0 && !point);
|
|
if (point) {
|
|
if (style !== STEP && idx > startIdx && !seriesPoints[currentSeriesIx][idx - 1]) {
|
|
stackPoints.push(this._previousSegmentPoint(idx, idx - 1, currentSeriesIx));
|
|
}
|
|
stackPoints.push(point);
|
|
if (style !== STEP && idx + 1 < endIdx && !seriesPoints[currentSeriesIx][idx + 1]) {
|
|
stackPoints.push(this._previousSegmentPoint(idx, idx + 1, currentSeriesIx));
|
|
}
|
|
} else {
|
|
gapStackPoint = this._createGapStackPoint(idx);
|
|
this._stackPoints.push(gapStackPoint);
|
|
stackPoints.push(gapStackPoint);
|
|
}
|
|
}
|
|
return stackPoints;
|
|
},
|
|
_previousSegmentPoint: function (categoryIx, segmentIx, seriesIdx) {
|
|
var seriesPoints = this.seriesPoints;
|
|
var point;
|
|
while (seriesIdx > 0 && !point) {
|
|
seriesIdx--;
|
|
point = seriesPoints[seriesIdx][segmentIx];
|
|
}
|
|
if (!point) {
|
|
point = this._createGapStackPoint(categoryIx);
|
|
this._stackPoints.push(point);
|
|
} else {
|
|
point = seriesPoints[seriesIdx][categoryIx];
|
|
}
|
|
return point;
|
|
},
|
|
_createGapStackPoint: function (categoryIx) {
|
|
var options = this.pointOptions({}, 0);
|
|
var point = new LinePoint(0, options);
|
|
point.categoryIx = categoryIx;
|
|
point.series = {};
|
|
return point;
|
|
},
|
|
seriesMissingValues: function (series) {
|
|
return series.missingValues || ZERO;
|
|
}
|
|
});
|
|
var SplineAreaSegment = AreaSegment.extend({
|
|
init: function (linePoints, prevSegment, isStacked, currentSeries, seriesIx) {
|
|
var segment = this;
|
|
segment.prevSegment = prevSegment;
|
|
segment.isStacked = isStacked;
|
|
LineSegment.fn.init.call(segment, linePoints, currentSeries, seriesIx);
|
|
},
|
|
strokeSegments: function () {
|
|
var segments = this._strokeSegments;
|
|
if (!segments) {
|
|
var curveProcessor = new CurveProcessor(this.options.closed);
|
|
var linePoints = LineSegment.fn.points.call(this);
|
|
segments = this._strokeSegments = curveProcessor.process(linePoints);
|
|
}
|
|
return segments;
|
|
},
|
|
createVisual: function () {
|
|
var series = this.series;
|
|
var defaults = series._defaults;
|
|
var color = series.color;
|
|
if (isFn(color) && defaults) {
|
|
color = defaults.color;
|
|
}
|
|
this.visual = new draw.Group({ zIndex: series.zIndex });
|
|
this.createFill({
|
|
fill: {
|
|
color: color,
|
|
opacity: series.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
this.createStroke({
|
|
stroke: deepExtend({
|
|
color: color,
|
|
opacity: series.opacity,
|
|
lineCap: 'butt'
|
|
}, series.line)
|
|
});
|
|
},
|
|
createFill: function (style) {
|
|
var strokeSegments = this.strokeSegments();
|
|
var fillSegments = strokeSegments.slice(0);
|
|
var prevSegment = this.prevSegment;
|
|
if (this.isStacked && prevSegment) {
|
|
var prevStrokeSegments = prevSegment.strokeSegments();
|
|
var prevAnchor = last(prevStrokeSegments).anchor();
|
|
fillSegments.push(new draw.Segment(prevAnchor, prevAnchor, last(strokeSegments).anchor()));
|
|
var stackSegments = $.map(prevStrokeSegments, function (segment) {
|
|
return new draw.Segment(segment.anchor(), segment.controlOut(), segment.controlIn());
|
|
}).reverse();
|
|
append(fillSegments, stackSegments);
|
|
var firstAnchor = fillSegments[0].anchor();
|
|
fillSegments.push(new draw.Segment(firstAnchor, firstAnchor, last(stackSegments).anchor()));
|
|
}
|
|
var fill = new draw.Path(style);
|
|
fill.segments.push.apply(fill.segments, fillSegments);
|
|
this.closeFill(fill);
|
|
this.visual.append(fill);
|
|
},
|
|
closeFill: function (fillPath) {
|
|
var segment = this, chart = segment.parent, prevSegment = segment.prevSegment, plotArea = chart.plotArea, invertAxes = chart.options.invertAxes, valueAxis = chart.seriesValueAxis(segment.series), valueAxisLineBox = valueAxis.lineBox(), categoryAxis = plotArea.seriesCategoryAxis(segment.series), categoryAxisLineBox = categoryAxis.lineBox(), end = invertAxes ? categoryAxisLineBox.x1 : categoryAxisLineBox.y1, pos = invertAxes ? X : Y, segments = segment.strokeSegments(), firstPoint = segments[0].anchor(), lastPoint = last(segments).anchor();
|
|
end = limitValue(end, valueAxisLineBox[pos + 1], valueAxisLineBox[pos + 2]);
|
|
if (!(chart.options.isStacked && prevSegment) && segments.length > 1) {
|
|
if (invertAxes) {
|
|
fillPath.lineTo(end, lastPoint.y).lineTo(end, firstPoint.y);
|
|
} else {
|
|
fillPath.lineTo(lastPoint.x, end).lineTo(firstPoint.x, end);
|
|
}
|
|
}
|
|
},
|
|
createStroke: function (style) {
|
|
if (style.stroke.width > 0) {
|
|
var stroke = new draw.Path(style);
|
|
stroke.segments.push.apply(stroke.segments, this.strokeSegments());
|
|
this.visual.append(stroke);
|
|
}
|
|
}
|
|
});
|
|
var StepAreaSegment = StepLineSegment.extend({
|
|
init: function (linePoints, stackPoints, currentSeries, seriesIx) {
|
|
var segment = this;
|
|
segment.stackPoints = stackPoints;
|
|
StepLineSegment.fn.init.call(segment, linePoints, currentSeries, seriesIx);
|
|
},
|
|
_linePoints: StepLineSegment.fn.points
|
|
});
|
|
deepExtend(StepAreaSegment.fn, AreaSegmentMixin);
|
|
var ScatterChart = ChartElement.extend({
|
|
init: function (plotArea, options) {
|
|
var chart = this;
|
|
ChartElement.fn.init.call(chart, options);
|
|
chart.plotArea = plotArea;
|
|
chart.xAxisRanges = {};
|
|
chart.yAxisRanges = {};
|
|
chart.points = [];
|
|
chart.seriesPoints = [];
|
|
chart.seriesOptions = [];
|
|
chart._evalSeries = [];
|
|
chart.render();
|
|
},
|
|
options: {
|
|
series: [],
|
|
tooltip: { format: '{0}, {1}' },
|
|
labels: { format: '{0}, {1}' },
|
|
clip: true
|
|
},
|
|
render: function () {
|
|
var chart = this;
|
|
chart.traverseDataPoints(proxy(chart.addValue, chart));
|
|
},
|
|
addErrorBar: function (point, field, fields) {
|
|
var errorRange, chart = this, value = point.value[field], valueErrorField = field + 'Value', lowField = field + 'ErrorLow', highField = field + 'ErrorHigh', seriesIx = fields.seriesIx, series = fields.series, errorBars = point.options.errorBars, lowValue = fields[lowField], highValue = fields[highField];
|
|
if (isNumber(value)) {
|
|
if (isNumber(lowValue) && isNumber(highValue)) {
|
|
errorRange = {
|
|
low: lowValue,
|
|
high: highValue
|
|
};
|
|
}
|
|
if (errorBars && defined(errorBars[valueErrorField])) {
|
|
chart.seriesErrorRanges = chart.seriesErrorRanges || {
|
|
x: [],
|
|
y: []
|
|
};
|
|
chart.seriesErrorRanges[field][seriesIx] = chart.seriesErrorRanges[field][seriesIx] || new ErrorRangeCalculator(errorBars[valueErrorField], series, field);
|
|
errorRange = chart.seriesErrorRanges[field][seriesIx].getErrorRange(value, errorBars[valueErrorField]);
|
|
}
|
|
if (errorRange) {
|
|
chart.addPointErrorBar(errorRange, point, field);
|
|
}
|
|
}
|
|
},
|
|
addPointErrorBar: function (errorRange, point, field) {
|
|
var chart = this, low = errorRange.low, high = errorRange.high, series = point.series, isVertical = field === Y, options = point.options.errorBars, item = {}, errorBar;
|
|
point[field + 'Low'] = low;
|
|
point[field + 'High'] = high;
|
|
point.errorBars = point.errorBars || [];
|
|
errorBar = new ScatterErrorBar(low, high, isVertical, chart, series, options);
|
|
point.errorBars.push(errorBar);
|
|
point.append(errorBar);
|
|
item[field] = low;
|
|
chart.updateRange(item, series);
|
|
item[field] = high;
|
|
chart.updateRange(item, series);
|
|
},
|
|
addValue: function (value, fields) {
|
|
var chart = this, point, x = value.x, y = value.y, seriesIx = fields.seriesIx, series = this.options.series[seriesIx], missingValues = this.seriesMissingValues(series), seriesPoints = chart.seriesPoints[seriesIx];
|
|
if (!(hasValue(x) && hasValue(y))) {
|
|
value = this.createMissingValue(value, missingValues);
|
|
}
|
|
if (value) {
|
|
point = chart.createPoint(value, fields);
|
|
if (point) {
|
|
extend(point, fields);
|
|
chart.addErrorBar(point, X, fields);
|
|
chart.addErrorBar(point, Y, fields);
|
|
}
|
|
chart.updateRange(value, fields.series);
|
|
}
|
|
chart.points.push(point);
|
|
seriesPoints.push(point);
|
|
},
|
|
seriesMissingValues: function (series) {
|
|
return series.missingValues;
|
|
},
|
|
createMissingValue: noop,
|
|
updateRange: function (value, series) {
|
|
var chart = this, x = value.x, y = value.y, xAxisName = series.xAxis, yAxisName = series.yAxis, xAxisRange = chart.xAxisRanges[xAxisName], yAxisRange = chart.yAxisRanges[yAxisName];
|
|
if (hasValue(x)) {
|
|
xAxisRange = chart.xAxisRanges[xAxisName] = xAxisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
if (typeof x === STRING) {
|
|
x = toDate(x);
|
|
}
|
|
xAxisRange.min = math.min(xAxisRange.min, x);
|
|
xAxisRange.max = math.max(xAxisRange.max, x);
|
|
}
|
|
if (hasValue(y)) {
|
|
yAxisRange = chart.yAxisRanges[yAxisName] = yAxisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
if (typeof y === STRING) {
|
|
y = toDate(y);
|
|
}
|
|
yAxisRange.min = math.min(yAxisRange.min, y);
|
|
yAxisRange.max = math.max(yAxisRange.max, y);
|
|
}
|
|
},
|
|
evalPointOptions: function (options, value, fields) {
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var state = {
|
|
defaults: series._defaults,
|
|
excluded: [
|
|
'data',
|
|
'tooltip',
|
|
'tempate',
|
|
'visual',
|
|
'toggle',
|
|
'_outOfRangeMinPoint',
|
|
'_outOfRangeMaxPoint'
|
|
]
|
|
};
|
|
var doEval = this._evalSeries[seriesIx];
|
|
if (!defined(doEval)) {
|
|
this._evalSeries[seriesIx] = doEval = evalOptions(options, {}, state, true);
|
|
}
|
|
if (doEval) {
|
|
options = deepExtend({}, options);
|
|
evalOptions(options, {
|
|
value: value,
|
|
series: series,
|
|
dataItem: fields.dataItem
|
|
}, state);
|
|
}
|
|
return options;
|
|
},
|
|
pointType: function () {
|
|
return LinePoint;
|
|
},
|
|
pointOptions: function (series, seriesIx) {
|
|
var options = this.seriesOptions[seriesIx];
|
|
if (!options) {
|
|
var defaults = this.pointType().fn.defaults;
|
|
this.seriesOptions[seriesIx] = options = deepExtend({}, defaults, {
|
|
markers: { opacity: series.opacity },
|
|
tooltip: { format: this.options.tooltip.format },
|
|
labels: { format: this.options.labels.format }
|
|
}, series);
|
|
}
|
|
return options;
|
|
},
|
|
createPoint: function (value, fields) {
|
|
var chart = this, series = fields.series, point;
|
|
var pointOptions = this.pointOptions(series, fields.seriesIx);
|
|
var color = fields.color || series.color;
|
|
pointOptions = chart.evalPointOptions(pointOptions, value, fields);
|
|
if (kendo.isFunction(series.color)) {
|
|
color = pointOptions.color;
|
|
}
|
|
point = new LinePoint(value, pointOptions);
|
|
point.color = color;
|
|
chart.append(point);
|
|
return point;
|
|
},
|
|
seriesAxes: function (series) {
|
|
var plotArea = this.plotArea, xAxisName = series.xAxis, xAxis = xAxisName ? plotArea.namedXAxes[xAxisName] : plotArea.axisX, yAxisName = series.yAxis, yAxis = yAxisName ? plotArea.namedYAxes[yAxisName] : plotArea.axisY;
|
|
if (!xAxis) {
|
|
throw new Error('Unable to locate X axis with name ' + xAxisName);
|
|
}
|
|
if (!yAxis) {
|
|
throw new Error('Unable to locate Y axis with name ' + yAxisName);
|
|
}
|
|
return {
|
|
x: xAxis,
|
|
y: yAxis
|
|
};
|
|
},
|
|
reflow: function (targetBox) {
|
|
var chart = this, chartPoints = chart.points, pointIx = 0, point, seriesAxes, limit = !chart.options.clip;
|
|
chart.traverseDataPoints(function (value, fields) {
|
|
point = chartPoints[pointIx++];
|
|
seriesAxes = chart.seriesAxes(fields.series);
|
|
var slotX = seriesAxes.x.getSlot(value.x, value.x, limit), slotY = seriesAxes.y.getSlot(value.y, value.y, limit), pointSlot;
|
|
if (point) {
|
|
if (slotX && slotY) {
|
|
pointSlot = chart.pointSlot(slotX, slotY);
|
|
point.reflow(pointSlot);
|
|
} else {
|
|
point.visible = false;
|
|
}
|
|
}
|
|
});
|
|
chart.box = targetBox;
|
|
},
|
|
pointSlot: function (slotX, slotY) {
|
|
return new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2);
|
|
},
|
|
traverseDataPoints: function (callback) {
|
|
var chart = this, options = chart.options, series = options.series, seriesPoints = chart.seriesPoints, pointIx, seriesIx, currentSeries, currentSeriesPoints, pointData, value, fields;
|
|
for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
currentSeriesPoints = seriesPoints[seriesIx];
|
|
if (!currentSeriesPoints) {
|
|
seriesPoints[seriesIx] = [];
|
|
}
|
|
for (pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
|
|
pointData = this._bindPoint(currentSeries, seriesIx, pointIx);
|
|
value = pointData.valueFields;
|
|
fields = pointData.fields;
|
|
callback(value, deepExtend({
|
|
pointIx: pointIx,
|
|
series: currentSeries,
|
|
seriesIx: seriesIx,
|
|
dataItem: currentSeries.data[pointIx],
|
|
owner: chart
|
|
}, fields));
|
|
}
|
|
}
|
|
},
|
|
_bindPoint: CategoricalChart.fn._bindPoint,
|
|
formatPointValue: function (point, format) {
|
|
var value = point.value;
|
|
return autoFormat(format, value.x, value.y);
|
|
},
|
|
animationPoints: function () {
|
|
var points = this.points;
|
|
var result = [];
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
result.push((points[idx] || {}).marker);
|
|
}
|
|
return result;
|
|
}
|
|
});
|
|
deepExtend(ScatterChart.fn, ClipAnimationMixin);
|
|
var ScatterLineChart = ScatterChart.extend({
|
|
render: function () {
|
|
var chart = this;
|
|
ScatterChart.fn.render.call(chart);
|
|
chart.renderSegments();
|
|
},
|
|
createSegment: function (linePoints, currentSeries, seriesIx) {
|
|
var pointType, style = currentSeries.style;
|
|
if (style === SMOOTH) {
|
|
pointType = SplineSegment;
|
|
} else {
|
|
pointType = LineSegment;
|
|
}
|
|
return new pointType(linePoints, currentSeries, seriesIx);
|
|
},
|
|
animationPoints: function () {
|
|
var points = ScatterChart.fn.animationPoints.call(this);
|
|
return points.concat(this._segments);
|
|
},
|
|
createMissingValue: function (value, missingValues) {
|
|
if (missingValues === ZERO) {
|
|
var missingValue = {
|
|
x: value.x,
|
|
y: value.y
|
|
};
|
|
if (!hasValue(missingValue.x)) {
|
|
missingValue.x = 0;
|
|
}
|
|
if (!hasValue(missingValue.y)) {
|
|
missingValue.y = 0;
|
|
}
|
|
return missingValue;
|
|
}
|
|
}
|
|
});
|
|
deepExtend(ScatterLineChart.fn, LineChartMixin);
|
|
var BubbleChart = ScatterChart.extend({
|
|
init: function (plotArea, options) {
|
|
this._maxSize = MIN_VALUE;
|
|
ScatterChart.fn.init.call(this, plotArea, options);
|
|
},
|
|
options: {
|
|
tooltip: { format: '{3}' },
|
|
labels: { format: '{3}' }
|
|
},
|
|
addValue: function (value, fields) {
|
|
if (value.size !== null && (value.size > 0 || value.size < 0 && fields.series.negativeValues.visible)) {
|
|
this._maxSize = math.max(this._maxSize, math.abs(value.size));
|
|
ScatterChart.fn.addValue.call(this, value, fields);
|
|
} else {
|
|
this.points.push(null);
|
|
this.seriesPoints[fields.seriesIx].push(null);
|
|
}
|
|
},
|
|
reflow: function (box) {
|
|
var chart = this;
|
|
chart.updateBubblesSize(box);
|
|
ScatterChart.fn.reflow.call(chart, box);
|
|
},
|
|
pointType: function () {
|
|
return Bubble;
|
|
},
|
|
createPoint: function (value, fields) {
|
|
var chart = this, series = fields.series, pointsCount = series.data.length, delay = fields.pointIx * (INITIAL_ANIMATION_DURATION / pointsCount), animationOptions = {
|
|
delay: delay,
|
|
duration: INITIAL_ANIMATION_DURATION - delay,
|
|
type: BUBBLE
|
|
}, point, pointOptions;
|
|
var color = fields.color || series.color;
|
|
if (value.size < 0 && series.negativeValues.visible) {
|
|
color = valueOrDefault(series.negativeValues.color, color);
|
|
}
|
|
pointOptions = deepExtend({
|
|
labels: {
|
|
animation: {
|
|
delay: delay,
|
|
duration: INITIAL_ANIMATION_DURATION - delay
|
|
}
|
|
}
|
|
}, this.pointOptions(series, fields.seriesIx), {
|
|
markers: {
|
|
type: CIRCLE,
|
|
border: series.border,
|
|
opacity: series.opacity,
|
|
animation: animationOptions
|
|
}
|
|
});
|
|
pointOptions = chart.evalPointOptions(pointOptions, value, fields);
|
|
if (kendo.isFunction(series.color)) {
|
|
color = pointOptions.color;
|
|
}
|
|
pointOptions.markers.background = color;
|
|
point = new Bubble(value, pointOptions);
|
|
point.color = color;
|
|
chart.append(point);
|
|
return point;
|
|
},
|
|
updateBubblesSize: function (box) {
|
|
var chart = this, options = chart.options, series = options.series, boxSize = math.min(box.width(), box.height()), seriesIx, pointIx;
|
|
for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
var currentSeries = series[seriesIx], seriesPoints = chart.seriesPoints[seriesIx], minSize = currentSeries.minSize || math.max(boxSize * 0.02, 10), maxSize = currentSeries.maxSize || boxSize * 0.2, minR = minSize / 2, maxR = maxSize / 2, minArea = math.PI * minR * minR, maxArea = math.PI * maxR * maxR, areaRange = maxArea - minArea, areaRatio = areaRange / chart._maxSize;
|
|
for (pointIx = 0; pointIx < seriesPoints.length; pointIx++) {
|
|
var point = seriesPoints[pointIx];
|
|
if (point) {
|
|
var area = math.abs(point.value.size) * areaRatio, r = math.sqrt((minArea + area) / math.PI), baseZIndex = valueOrDefault(point.options.zIndex, 0), zIndex = baseZIndex + (1 - r / maxR);
|
|
deepExtend(point.options, {
|
|
zIndex: zIndex,
|
|
markers: {
|
|
size: r * 2,
|
|
zIndex: zIndex
|
|
},
|
|
labels: { zIndex: zIndex + 1 }
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
var value = point.value;
|
|
return autoFormat(format, value.x, value.y, value.size, point.category);
|
|
},
|
|
createAnimation: noop,
|
|
createVisual: noop
|
|
});
|
|
var Candlestick = ChartElement.extend({
|
|
init: function (value, options) {
|
|
ChartElement.fn.init.call(this, options);
|
|
this.value = value;
|
|
},
|
|
options: {
|
|
border: { _brightness: 0.8 },
|
|
line: { width: 2 },
|
|
overlay: { gradient: GLASS },
|
|
tooltip: { format: '<table style=\'text-align: left;\'>' + '<th colspan=\'2\'>{4:d}</th>' + '<tr><td>Open:</td><td>{0:C}</td></tr>' + '<tr><td>High:</td><td>{1:C}</td></tr>' + '<tr><td>Low:</td><td>{2:C}</td></tr>' + '<tr><td>Close:</td><td>{3:C}</td></tr>' + '</table>' },
|
|
highlight: {
|
|
opacity: 1,
|
|
border: {
|
|
width: 1,
|
|
opacity: 1
|
|
},
|
|
line: {
|
|
width: 1,
|
|
opacity: 1
|
|
}
|
|
},
|
|
notes: {
|
|
visible: true,
|
|
label: {}
|
|
}
|
|
},
|
|
reflow: function (box) {
|
|
var point = this, options = point.options, chart = point.owner, value = point.value, valueAxis = chart.seriesValueAxis(options), points = [], mid, ocSlot, lhSlot;
|
|
ocSlot = valueAxis.getSlot(value.open, value.close);
|
|
lhSlot = valueAxis.getSlot(value.low, value.high);
|
|
ocSlot.x1 = lhSlot.x1 = box.x1;
|
|
ocSlot.x2 = lhSlot.x2 = box.x2;
|
|
point.realBody = ocSlot;
|
|
mid = lhSlot.center().x;
|
|
points.push([
|
|
[
|
|
mid,
|
|
lhSlot.y1
|
|
],
|
|
[
|
|
mid,
|
|
ocSlot.y1
|
|
]
|
|
]);
|
|
points.push([
|
|
[
|
|
mid,
|
|
ocSlot.y2
|
|
],
|
|
[
|
|
mid,
|
|
lhSlot.y2
|
|
]
|
|
]);
|
|
point.lines = points;
|
|
point.box = lhSlot.clone().wrap(ocSlot);
|
|
if (!point._rendered) {
|
|
point._rendered = true;
|
|
point.createNote();
|
|
}
|
|
point.reflowNote();
|
|
},
|
|
reflowNote: function () {
|
|
var point = this;
|
|
if (point.note) {
|
|
point.note.reflow(point.box);
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
this._mainVisual = this.mainVisual(this.options);
|
|
this.visual.append(this._mainVisual);
|
|
this.createOverlay();
|
|
},
|
|
mainVisual: function (options) {
|
|
var group = new draw.Group();
|
|
this.createBody(group, options);
|
|
this.createLines(group, options);
|
|
return group;
|
|
},
|
|
createBody: function (container, options) {
|
|
var body = draw.Path.fromRect(this.realBody.toRect(), {
|
|
fill: {
|
|
color: this.color,
|
|
opacity: options.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
if (options.border.width > 0) {
|
|
body.options.set('stroke', {
|
|
color: this.getBorderColor(),
|
|
width: options.border.width,
|
|
dashType: options.border.dashType,
|
|
opacity: valueOrDefault(options.border.opacity, options.opacity)
|
|
});
|
|
}
|
|
alignPathToPixel(body);
|
|
container.append(body);
|
|
if (hasGradientOverlay(options)) {
|
|
container.append(this.createGradientOverlay(body, { baseColor: this.color }, deepExtend({}, options.overlay)));
|
|
}
|
|
},
|
|
createLines: function (container, options) {
|
|
this.drawLines(container, options, this.lines, options.line);
|
|
},
|
|
drawLines: function (container, options, lines, lineOptions) {
|
|
if (!lines) {
|
|
return;
|
|
}
|
|
var lineStyle = {
|
|
stroke: {
|
|
color: lineOptions.color || this.color,
|
|
opacity: valueOrDefault(lineOptions.opacity, options.opacity),
|
|
width: lineOptions.width,
|
|
dashType: lineOptions.dashType,
|
|
lineCap: 'butt'
|
|
}
|
|
};
|
|
for (var i = 0; i < lines.length; i++) {
|
|
var line = draw.Path.fromPoints(lines[i], lineStyle);
|
|
alignPathToPixel(line);
|
|
container.append(line);
|
|
}
|
|
},
|
|
getBorderColor: function () {
|
|
var point = this, options = point.options, border = options.border, borderColor = border.color;
|
|
if (!defined(borderColor)) {
|
|
borderColor = new Color(point.color).brightness(border._brightness).toHex();
|
|
}
|
|
return borderColor;
|
|
},
|
|
createOverlay: function () {
|
|
var overlay = draw.Path.fromRect(this.box.toRect(), {
|
|
fill: {
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
stroke: null
|
|
});
|
|
this.visual.append(overlay);
|
|
},
|
|
createHighlight: function () {
|
|
var highlight = this.options.highlight;
|
|
var normalColor = this.color;
|
|
this.color = highlight.color || this.color;
|
|
var overlay = this.mainVisual(deepExtend({}, this.options, { line: { color: this.getBorderColor() } }, highlight));
|
|
this.color = normalColor;
|
|
return overlay;
|
|
},
|
|
highlightVisual: function () {
|
|
return this._mainVisual;
|
|
},
|
|
highlightVisualArgs: function () {
|
|
return {
|
|
options: this.options,
|
|
rect: this.box.toRect(),
|
|
visual: this._mainVisual
|
|
};
|
|
},
|
|
tooltipAnchor: function () {
|
|
var point = this, box = point.box, clipBox = point.owner.pane.clipBox() || box;
|
|
return new Point2D(box.x2 + TOOLTIP_OFFSET, math.max(box.y1, clipBox.y1) + TOOLTIP_OFFSET);
|
|
},
|
|
formatValue: function (format) {
|
|
var point = this;
|
|
return point.owner.formatPointValue(point, format);
|
|
},
|
|
overlapsBox: function (box) {
|
|
return this.box.overlaps(box);
|
|
}
|
|
});
|
|
deepExtend(Candlestick.fn, PointEventsMixin);
|
|
deepExtend(Candlestick.fn, NoteMixin);
|
|
var CandlestickChart = CategoricalChart.extend({
|
|
options: {},
|
|
reflowCategories: function (categorySlots) {
|
|
var chart = this, children = chart.children, childrenLength = children.length, i;
|
|
for (i = 0; i < childrenLength; i++) {
|
|
children[i].reflow(categorySlots[i]);
|
|
}
|
|
},
|
|
addValue: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var options = chart.options;
|
|
var value = data.valueFields;
|
|
var children = chart.children;
|
|
var valueParts = chart.splitValue(value);
|
|
var hasValue = areNumbers(valueParts);
|
|
var categoryPoints = chart.categoryPoints[categoryIx];
|
|
var dataItem = series.data[categoryIx];
|
|
var point, cluster;
|
|
if (!categoryPoints) {
|
|
chart.categoryPoints[categoryIx] = categoryPoints = [];
|
|
}
|
|
if (hasValue) {
|
|
point = chart.createPoint(data, fields);
|
|
}
|
|
cluster = children[categoryIx];
|
|
if (!cluster) {
|
|
cluster = new ClusterLayout({
|
|
vertical: options.invertAxes,
|
|
gap: options.gap,
|
|
spacing: options.spacing
|
|
});
|
|
chart.append(cluster);
|
|
}
|
|
if (point) {
|
|
chart.updateRange(value, fields);
|
|
cluster.append(point);
|
|
point.categoryIx = categoryIx;
|
|
point.category = category;
|
|
point.series = series;
|
|
point.seriesIx = seriesIx;
|
|
point.owner = chart;
|
|
point.dataItem = dataItem;
|
|
point.noteText = data.fields.noteText;
|
|
}
|
|
chart.points.push(point);
|
|
categoryPoints.push(point);
|
|
},
|
|
pointType: function () {
|
|
return Candlestick;
|
|
},
|
|
createPoint: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var value = data.valueFields;
|
|
var pointOptions = deepExtend({}, series);
|
|
var pointType = chart.pointType();
|
|
var color = data.fields.color || series.color;
|
|
pointOptions = chart.evalPointOptions(pointOptions, value, category, categoryIx, series, seriesIx);
|
|
if (series.type == CANDLESTICK) {
|
|
if (value.open > value.close) {
|
|
color = data.fields.downColor || series.downColor || series.color;
|
|
}
|
|
}
|
|
if (kendo.isFunction(series.color)) {
|
|
color = pointOptions.color;
|
|
}
|
|
var point = new pointType(value, pointOptions);
|
|
point.color = color;
|
|
return point;
|
|
},
|
|
splitValue: function (value) {
|
|
return [
|
|
value.low,
|
|
value.open,
|
|
value.close,
|
|
value.high
|
|
];
|
|
},
|
|
updateRange: function (value, fields) {
|
|
var chart = this, axisName = fields.series.axis, axisRange = chart.valueAxisRanges[axisName], parts = chart.splitValue(value);
|
|
axisRange = chart.valueAxisRanges[axisName] = axisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
axisRange = chart.valueAxisRanges[axisName] = {
|
|
min: math.min.apply(math, parts.concat([axisRange.min])),
|
|
max: math.max.apply(math, parts.concat([axisRange.max]))
|
|
};
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
var value = point.value;
|
|
return autoFormat(format, value.open, value.high, value.low, value.close, point.category);
|
|
},
|
|
animationPoints: function () {
|
|
return this.points;
|
|
}
|
|
});
|
|
deepExtend(CandlestickChart.fn, ClipAnimationMixin);
|
|
var OHLCPoint = Candlestick.extend({
|
|
reflow: function (box) {
|
|
var point = this, options = point.options, chart = point.owner, value = point.value, valueAxis = chart.seriesValueAxis(options), oPoints = [], cPoints = [], lhPoints = [], mid, oSlot, cSlot, lhSlot;
|
|
lhSlot = valueAxis.getSlot(value.low, value.high);
|
|
oSlot = valueAxis.getSlot(value.open, value.open);
|
|
cSlot = valueAxis.getSlot(value.close, value.close);
|
|
oSlot.x1 = cSlot.x1 = lhSlot.x1 = box.x1;
|
|
oSlot.x2 = cSlot.x2 = lhSlot.x2 = box.x2;
|
|
mid = lhSlot.center().x;
|
|
oPoints.push([
|
|
oSlot.x1,
|
|
oSlot.y1
|
|
]);
|
|
oPoints.push([
|
|
mid,
|
|
oSlot.y1
|
|
]);
|
|
cPoints.push([
|
|
mid,
|
|
cSlot.y1
|
|
]);
|
|
cPoints.push([
|
|
cSlot.x2,
|
|
cSlot.y1
|
|
]);
|
|
lhPoints.push([
|
|
mid,
|
|
lhSlot.y1
|
|
]);
|
|
lhPoints.push([
|
|
mid,
|
|
lhSlot.y2
|
|
]);
|
|
point.lines = [
|
|
oPoints,
|
|
cPoints,
|
|
lhPoints
|
|
];
|
|
point.box = lhSlot.clone().wrap(oSlot.clone().wrap(cSlot));
|
|
point.reflowNote();
|
|
},
|
|
createBody: $.noop
|
|
});
|
|
var OHLCChart = CandlestickChart.extend({
|
|
pointType: function () {
|
|
return OHLCPoint;
|
|
}
|
|
});
|
|
var BoxPlotChart = CandlestickChart.extend({
|
|
addValue: function (data, fields) {
|
|
var chart = this;
|
|
var categoryIx = fields.categoryIx;
|
|
var category = fields.category;
|
|
var series = fields.series;
|
|
var seriesIx = fields.seriesIx;
|
|
var options = chart.options;
|
|
var children = chart.children;
|
|
var value = data.valueFields;
|
|
var valueParts = chart.splitValue(value);
|
|
var hasValue = areNumbers(valueParts);
|
|
var categoryPoints = chart.categoryPoints[categoryIx];
|
|
var dataItem = series.data[categoryIx];
|
|
var point, cluster;
|
|
if (!categoryPoints) {
|
|
chart.categoryPoints[categoryIx] = categoryPoints = [];
|
|
}
|
|
if (hasValue) {
|
|
point = chart.createPoint(data, fields);
|
|
}
|
|
cluster = children[categoryIx];
|
|
if (!cluster) {
|
|
cluster = new ClusterLayout({
|
|
vertical: options.invertAxes,
|
|
gap: options.gap,
|
|
spacing: options.spacing
|
|
});
|
|
chart.append(cluster);
|
|
}
|
|
if (point) {
|
|
chart.updateRange(value, fields);
|
|
cluster.append(point);
|
|
point.categoryIx = categoryIx;
|
|
point.category = category;
|
|
point.series = series;
|
|
point.seriesIx = seriesIx;
|
|
point.owner = chart;
|
|
point.dataItem = dataItem;
|
|
}
|
|
chart.points.push(point);
|
|
categoryPoints.push(point);
|
|
},
|
|
pointType: function () {
|
|
return BoxPlot;
|
|
},
|
|
splitValue: function (value) {
|
|
return [
|
|
value.lower,
|
|
value.q1,
|
|
value.median,
|
|
value.q3,
|
|
value.upper
|
|
];
|
|
},
|
|
updateRange: function (value, fields) {
|
|
var chart = this, axisName = fields.series.axis, axisRange = chart.valueAxisRanges[axisName], parts = chart.splitValue(value).concat(chart.filterOutliers(value.outliers));
|
|
if (defined(value.mean)) {
|
|
parts = parts.concat(value.mean);
|
|
}
|
|
axisRange = chart.valueAxisRanges[axisName] = axisRange || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
axisRange = chart.valueAxisRanges[axisName] = {
|
|
min: math.min.apply(math, parts.concat([axisRange.min])),
|
|
max: math.max.apply(math, parts.concat([axisRange.max]))
|
|
};
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
var value = point.value;
|
|
return autoFormat(format, value.lower, value.q1, value.median, value.q3, value.upper, value.mean, point.category);
|
|
},
|
|
filterOutliers: function (items) {
|
|
var length = (items || []).length, result = [], i, item;
|
|
for (i = 0; i < length; i++) {
|
|
item = items[i];
|
|
if (defined(item)) {
|
|
appendIfNotNull(result, item);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
});
|
|
var BoxPlot = Candlestick.extend({
|
|
init: function (value, options) {
|
|
var point = this;
|
|
ChartElement.fn.init.call(point, options);
|
|
point.value = value;
|
|
point.createNote();
|
|
},
|
|
options: {
|
|
border: { _brightness: 0.8 },
|
|
line: { width: 2 },
|
|
mean: {
|
|
width: 2,
|
|
dashType: 'dash'
|
|
},
|
|
overlay: { gradient: GLASS },
|
|
tooltip: { format: '<table style=\'text-align: left;\'>' + '<th colspan=\'2\'>{6:d}</th>' + '<tr><td>Lower:</td><td>{0:C}</td></tr>' + '<tr><td>Q1:</td><td>{1:C}</td></tr>' + '<tr><td>Median:</td><td>{2:C}</td></tr>' + '<tr><td>Mean:</td><td>{5:C}</td></tr>' + '<tr><td>Q3:</td><td>{3:C}</td></tr>' + '<tr><td>Upper:</td><td>{4:C}</td></tr>' + '</table>' },
|
|
highlight: {
|
|
opacity: 1,
|
|
border: {
|
|
width: 1,
|
|
opacity: 1
|
|
},
|
|
line: {
|
|
width: 1,
|
|
opacity: 1
|
|
}
|
|
},
|
|
notes: {
|
|
visible: true,
|
|
label: {}
|
|
},
|
|
outliers: {
|
|
visible: true,
|
|
size: LINE_MARKER_SIZE,
|
|
type: CROSS,
|
|
background: WHITE,
|
|
border: {
|
|
width: 2,
|
|
opacity: 1
|
|
},
|
|
opacity: 0
|
|
},
|
|
extremes: {
|
|
visible: true,
|
|
size: LINE_MARKER_SIZE,
|
|
type: CIRCLE,
|
|
background: WHITE,
|
|
border: {
|
|
width: 2,
|
|
opacity: 1
|
|
},
|
|
opacity: 0
|
|
}
|
|
},
|
|
reflow: function (box) {
|
|
var point = this, options = point.options, chart = point.owner, value = point.value, valueAxis = chart.seriesValueAxis(options), mid, whiskerSlot, boxSlot, medianSlot, meanSlot;
|
|
boxSlot = valueAxis.getSlot(value.q1, value.q3);
|
|
point.boxSlot = boxSlot;
|
|
whiskerSlot = valueAxis.getSlot(value.lower, value.upper);
|
|
medianSlot = valueAxis.getSlot(value.median);
|
|
boxSlot.x1 = whiskerSlot.x1 = box.x1;
|
|
boxSlot.x2 = whiskerSlot.x2 = box.x2;
|
|
point.realBody = boxSlot;
|
|
if (value.mean) {
|
|
meanSlot = valueAxis.getSlot(value.mean);
|
|
point.meanPoints = [[
|
|
[
|
|
box.x1,
|
|
meanSlot.y1
|
|
],
|
|
[
|
|
box.x2,
|
|
meanSlot.y1
|
|
]
|
|
]];
|
|
}
|
|
mid = whiskerSlot.center().x;
|
|
point.whiskerPoints = [
|
|
[
|
|
[
|
|
mid - 5,
|
|
whiskerSlot.y1
|
|
],
|
|
[
|
|
mid + 5,
|
|
whiskerSlot.y1
|
|
],
|
|
[
|
|
mid,
|
|
whiskerSlot.y1
|
|
],
|
|
[
|
|
mid,
|
|
boxSlot.y1
|
|
]
|
|
],
|
|
[
|
|
[
|
|
mid - 5,
|
|
whiskerSlot.y2
|
|
],
|
|
[
|
|
mid + 5,
|
|
whiskerSlot.y2
|
|
],
|
|
[
|
|
mid,
|
|
whiskerSlot.y2
|
|
],
|
|
[
|
|
mid,
|
|
boxSlot.y2
|
|
]
|
|
]
|
|
];
|
|
point.medianPoints = [[
|
|
[
|
|
box.x1,
|
|
medianSlot.y1
|
|
],
|
|
[
|
|
box.x2,
|
|
medianSlot.y1
|
|
]
|
|
]];
|
|
point.box = whiskerSlot.clone().wrap(boxSlot);
|
|
point.reflowNote();
|
|
},
|
|
renderOutliers: function (options) {
|
|
var point = this, markers = options.markers || {}, value = point.value, outliers = value.outliers || [], outerFence = math.abs(value.q3 - value.q1) * 3, markersBorder, shape, outlierValue, i;
|
|
var elements = [];
|
|
for (i = 0; i < outliers.length; i++) {
|
|
outlierValue = outliers[i];
|
|
if (outlierValue < value.q3 + outerFence && outlierValue > value.q1 - outerFence) {
|
|
markers = options.outliers;
|
|
} else {
|
|
markers = options.extremes;
|
|
}
|
|
markersBorder = deepExtend({}, markers.border);
|
|
if (!defined(markersBorder.color)) {
|
|
if (defined(point.color)) {
|
|
markersBorder.color = point.color;
|
|
} else {
|
|
markersBorder.color = new Color(markers.background).brightness(BAR_BORDER_BRIGHTNESS).toHex();
|
|
}
|
|
}
|
|
shape = new ShapeElement({
|
|
type: markers.type,
|
|
width: markers.size,
|
|
height: markers.size,
|
|
rotation: markers.rotation,
|
|
background: markers.background,
|
|
border: markersBorder,
|
|
opacity: markers.opacity
|
|
});
|
|
shape.value = outlierValue;
|
|
elements.push(shape);
|
|
}
|
|
this.reflowOutliers(elements);
|
|
return elements;
|
|
},
|
|
reflowOutliers: function (outliers) {
|
|
var valueAxis = this.owner.seriesValueAxis(this.options);
|
|
var centerX = this.box.center().x;
|
|
for (var i = 0; i < outliers.length; i++) {
|
|
var outlierValue = outliers[i].value;
|
|
var markerBox = valueAxis.getSlot(outlierValue).move(centerX);
|
|
this.box = this.box.wrap(markerBox);
|
|
outliers[i].reflow(markerBox);
|
|
}
|
|
},
|
|
mainVisual: function (options) {
|
|
var group = Candlestick.fn.mainVisual.call(this, options);
|
|
var outliers = this.renderOutliers(options);
|
|
for (var i = 0; i < outliers.length; i++) {
|
|
var element = outliers[i].getElement();
|
|
if (element) {
|
|
group.append(element);
|
|
}
|
|
}
|
|
return group;
|
|
},
|
|
createLines: function (container, options) {
|
|
this.drawLines(container, options, this.whiskerPoints, options.line);
|
|
this.drawLines(container, options, this.medianPoints, options.median);
|
|
this.drawLines(container, options, this.meanPoints, options.mean);
|
|
},
|
|
getBorderColor: function () {
|
|
if (this.color) {
|
|
return this.color;
|
|
}
|
|
return Candlestick.getBorderColor.call(this);
|
|
}
|
|
});
|
|
deepExtend(BoxPlot.fn, PointEventsMixin);
|
|
var PieSegment = ChartElement.extend({
|
|
init: function (value, sector, options) {
|
|
var segment = this;
|
|
segment.value = value;
|
|
segment.sector = sector;
|
|
ChartElement.fn.init.call(segment, options);
|
|
},
|
|
options: {
|
|
color: WHITE,
|
|
overlay: { gradient: ROUNDED_BEVEL },
|
|
border: { width: 0.5 },
|
|
labels: {
|
|
visible: false,
|
|
distance: 35,
|
|
font: DEFAULT_FONT,
|
|
margin: getSpacing(0.5),
|
|
align: CIRCLE,
|
|
zIndex: 1,
|
|
position: OUTSIDE_END
|
|
},
|
|
animation: { type: PIE },
|
|
highlight: {
|
|
visible: true,
|
|
border: { width: 1 }
|
|
},
|
|
visible: true
|
|
},
|
|
render: function () {
|
|
var segment = this, options = segment.options, labels = options.labels, labelText = segment.value, labelTemplate;
|
|
if (segment._rendered || segment.visible === false) {
|
|
return;
|
|
} else {
|
|
segment._rendered = true;
|
|
}
|
|
if (labels.template) {
|
|
labelTemplate = template(labels.template);
|
|
labelText = labelTemplate({
|
|
dataItem: segment.dataItem,
|
|
category: segment.category,
|
|
value: segment.value,
|
|
series: segment.series,
|
|
percentage: segment.percentage
|
|
});
|
|
} else if (labels.format) {
|
|
labelText = autoFormat(labels.format, labelText);
|
|
}
|
|
if (labels.visible && labelText) {
|
|
segment.label = new TextBox(labelText, deepExtend({}, labels, {
|
|
align: CENTER,
|
|
vAlign: '',
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: segment.animationDelay
|
|
}
|
|
}));
|
|
segment.append(segment.label);
|
|
}
|
|
},
|
|
reflow: function (targetBox) {
|
|
var segment = this;
|
|
segment.render();
|
|
segment.box = targetBox;
|
|
segment.reflowLabel();
|
|
},
|
|
reflowLabel: function () {
|
|
var segment = this, sector = segment.sector.clone(), options = segment.options, label = segment.label, labelsOptions = options.labels, labelsDistance = labelsOptions.distance, angle = sector.middle(), lp, x1, labelWidth, labelHeight;
|
|
if (label) {
|
|
labelHeight = label.box.height();
|
|
labelWidth = label.box.width();
|
|
if (labelsOptions.position == CENTER) {
|
|
sector.r = math.abs((sector.r - labelHeight) / 2) + labelHeight;
|
|
lp = sector.point(angle);
|
|
label.reflow(Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
|
|
} else if (labelsOptions.position == INSIDE_END) {
|
|
sector.r = sector.r - labelHeight / 2;
|
|
lp = sector.point(angle);
|
|
label.reflow(Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
|
|
} else {
|
|
lp = sector.clone().expand(labelsDistance).point(angle);
|
|
if (lp.x >= sector.c.x) {
|
|
x1 = lp.x + labelWidth;
|
|
label.orientation = RIGHT;
|
|
} else {
|
|
x1 = lp.x - labelWidth;
|
|
label.orientation = LEFT;
|
|
}
|
|
label.reflow(Box2D(x1, lp.y - labelHeight, lp.x, lp.y));
|
|
}
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var segment = this, sector = segment.sector, options = segment.options;
|
|
ChartElement.fn.createVisual.call(this);
|
|
if (segment.value) {
|
|
if (options.visual) {
|
|
var startAngle = (sector.startAngle + 180) % 360;
|
|
var visual = options.visual({
|
|
category: segment.category,
|
|
dataItem: segment.dataItem,
|
|
value: segment.value,
|
|
series: segment.series,
|
|
percentage: segment.percentage,
|
|
center: new geom.Point(sector.c.x, sector.c.y),
|
|
radius: sector.r,
|
|
innerRadius: sector.ir,
|
|
startAngle: startAngle,
|
|
endAngle: startAngle + sector.angle,
|
|
options: options,
|
|
createVisual: function () {
|
|
var group = new draw.Group();
|
|
segment.createSegmentVisual(group);
|
|
return group;
|
|
}
|
|
});
|
|
if (visual) {
|
|
segment.visual.append(visual);
|
|
}
|
|
} else {
|
|
segment.createSegmentVisual(segment.visual);
|
|
}
|
|
}
|
|
},
|
|
createSegmentVisual: function (group) {
|
|
var segment = this, sector = segment.sector, options = segment.options, borderOptions = options.border || {}, border = borderOptions.width > 0 ? {
|
|
stroke: {
|
|
color: borderOptions.color,
|
|
width: borderOptions.width,
|
|
opacity: borderOptions.opacity,
|
|
dashType: borderOptions.dashType
|
|
}
|
|
} : {}, color = options.color, fill = {
|
|
color: color,
|
|
opacity: options.opacity
|
|
}, visual;
|
|
visual = segment.createSegment(sector, deepExtend({
|
|
fill: fill,
|
|
stroke: { opacity: options.opacity },
|
|
zIndex: options.zIndex
|
|
}, border));
|
|
group.append(visual);
|
|
if (hasGradientOverlay(options)) {
|
|
group.append(this.createGradientOverlay(visual, {
|
|
baseColor: color,
|
|
fallbackFill: fill
|
|
}, deepExtend({
|
|
center: [
|
|
sector.c.x,
|
|
sector.c.y
|
|
],
|
|
innerRadius: sector.ir,
|
|
radius: sector.r,
|
|
userSpace: true
|
|
}, options.overlay)));
|
|
}
|
|
},
|
|
createSegment: function (sector, options) {
|
|
if (options.singleSegment) {
|
|
return new draw.Circle(new geom.Circle(new geom.Point(sector.c.x, sector.c.y), sector.r), options);
|
|
} else {
|
|
return ShapeBuilder.current.createRing(sector, options);
|
|
}
|
|
},
|
|
createAnimation: function () {
|
|
var options = this.options;
|
|
var center = this.sector.c;
|
|
deepExtend(options, {
|
|
animation: {
|
|
center: [
|
|
center.x,
|
|
center.y
|
|
],
|
|
delay: this.animationDelay
|
|
}
|
|
});
|
|
ChartElement.fn.createAnimation.call(this);
|
|
},
|
|
createHighlight: function (options) {
|
|
var segment = this, highlight = segment.options.highlight || {}, border = highlight.border || {};
|
|
return segment.createSegment(segment.sector, deepExtend({}, options, {
|
|
fill: {
|
|
color: highlight.color,
|
|
opacity: highlight.opacity
|
|
},
|
|
stroke: {
|
|
opacity: border.opacity,
|
|
width: border.width,
|
|
color: border.color
|
|
}
|
|
}));
|
|
},
|
|
highlightVisual: function () {
|
|
return this.visual.children[0];
|
|
},
|
|
highlightVisualArgs: function () {
|
|
var sector = this.sector;
|
|
return {
|
|
options: this.options,
|
|
radius: sector.r,
|
|
innerRadius: sector.ir,
|
|
center: new geom.Point(sector.c.x, sector.c.y),
|
|
startAngle: sector.startAngle,
|
|
endAngle: sector.angle + sector.startAngle,
|
|
visual: this.visual
|
|
};
|
|
},
|
|
tooltipAnchor: function (width, height) {
|
|
var point = this, box = point.sector.adjacentBox(TOOLTIP_OFFSET, width, height);
|
|
return new Point2D(box.x1, box.y1);
|
|
},
|
|
formatValue: function (format) {
|
|
var point = this;
|
|
return point.owner.formatPointValue(point, format);
|
|
}
|
|
});
|
|
deepExtend(PieSegment.fn, PointEventsMixin);
|
|
var PieChartMixin = {
|
|
createLegendItem: function (value, point, options) {
|
|
var chart = this, legendOptions = chart.options.legend || {}, labelsOptions = legendOptions.labels || {}, inactiveItems = legendOptions.inactiveItems || {}, inactiveItemsLabels = inactiveItems.labels || {}, text, labelTemplate, markerColor, itemLabelOptions, pointVisible;
|
|
if (options && options.visibleInLegend !== false) {
|
|
pointVisible = options.visible !== false;
|
|
text = options.category || '';
|
|
labelTemplate = pointVisible ? labelsOptions.template : inactiveItemsLabels.template || labelsOptions.template;
|
|
if (labelTemplate) {
|
|
text = template(labelTemplate)({
|
|
text: text,
|
|
series: options.series,
|
|
dataItem: options.dataItem,
|
|
percentage: options.percentage,
|
|
value: value
|
|
});
|
|
}
|
|
if (pointVisible) {
|
|
itemLabelOptions = {};
|
|
markerColor = point.color;
|
|
} else {
|
|
itemLabelOptions = {
|
|
color: inactiveItemsLabels.color,
|
|
font: inactiveItemsLabels.font
|
|
};
|
|
markerColor = (inactiveItems.markers || {}).color;
|
|
}
|
|
if (text) {
|
|
chart.legendItems.push({
|
|
pointIndex: options.index,
|
|
text: text,
|
|
series: options.series,
|
|
markerColor: markerColor,
|
|
labels: itemLabelOptions
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var PieChart = ChartElement.extend({
|
|
init: function (plotArea, options) {
|
|
var chart = this;
|
|
ChartElement.fn.init.call(chart, options);
|
|
chart.plotArea = plotArea;
|
|
chart.points = [];
|
|
chart.legendItems = [];
|
|
chart.render();
|
|
},
|
|
options: {
|
|
startAngle: 90,
|
|
connectors: {
|
|
width: 1,
|
|
color: '#939393',
|
|
padding: 4
|
|
},
|
|
inactiveItems: {
|
|
markers: {},
|
|
labels: {}
|
|
}
|
|
},
|
|
render: function () {
|
|
var chart = this;
|
|
chart.traverseDataPoints(proxy(chart.addValue, chart));
|
|
},
|
|
traverseDataPoints: function (callback) {
|
|
var chart = this, options = chart.options, colors = chart.plotArea.options.seriesColors || [], colorsCount = colors.length, series = options.series, seriesCount = series.length, currentSeries, pointData, fields, seriesIx, angle, data, anglePerValue, value, plotValue, explode, total, currentAngle, i, pointIx = 0;
|
|
for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
data = currentSeries.data;
|
|
total = seriesTotal(currentSeries);
|
|
anglePerValue = 360 / total;
|
|
if (defined(currentSeries.startAngle)) {
|
|
currentAngle = currentSeries.startAngle;
|
|
} else {
|
|
currentAngle = options.startAngle;
|
|
}
|
|
if (seriesIx != seriesCount - 1) {
|
|
if (currentSeries.labels.position == OUTSIDE_END) {
|
|
currentSeries.labels.position = CENTER;
|
|
}
|
|
}
|
|
for (i = 0; i < data.length; i++) {
|
|
pointData = SeriesBinder.current.bindPoint(currentSeries, i);
|
|
value = pointData.valueFields.value;
|
|
plotValue = math.abs(value);
|
|
fields = pointData.fields;
|
|
angle = plotValue * anglePerValue;
|
|
explode = data.length != 1 && !!fields.explode;
|
|
if (!isFn(currentSeries.color)) {
|
|
currentSeries.color = fields.color || colors[i % colorsCount];
|
|
}
|
|
callback(value, new Ring(null, 0, 0, currentAngle, angle), {
|
|
owner: chart,
|
|
category: fields.category || '',
|
|
index: pointIx,
|
|
series: currentSeries,
|
|
seriesIx: seriesIx,
|
|
dataItem: data[i],
|
|
percentage: total !== 0 ? plotValue / total : 0,
|
|
explode: explode,
|
|
visibleInLegend: fields.visibleInLegend,
|
|
visible: fields.visible,
|
|
zIndex: seriesCount - seriesIx,
|
|
animationDelay: chart.animationDelay(i, seriesIx, seriesCount)
|
|
});
|
|
if (pointData.fields.visible !== false) {
|
|
currentAngle += angle;
|
|
}
|
|
pointIx++;
|
|
}
|
|
pointIx = 0;
|
|
}
|
|
},
|
|
evalSegmentOptions: function (options, value, fields) {
|
|
var series = fields.series;
|
|
evalOptions(options, {
|
|
value: value,
|
|
series: series,
|
|
dataItem: fields.dataItem,
|
|
category: fields.category,
|
|
percentage: fields.percentage
|
|
}, {
|
|
defaults: series._defaults,
|
|
excluded: [
|
|
'data',
|
|
'template',
|
|
'visual',
|
|
'toggle'
|
|
]
|
|
});
|
|
},
|
|
addValue: function (value, sector, fields) {
|
|
var chart = this, segment;
|
|
var segmentOptions = deepExtend({}, fields.series, { index: fields.index });
|
|
chart.evalSegmentOptions(segmentOptions, value, fields);
|
|
chart.createLegendItem(value, segmentOptions, fields);
|
|
if (fields.visible === false) {
|
|
return;
|
|
}
|
|
segment = new PieSegment(value, sector, segmentOptions);
|
|
extend(segment, fields);
|
|
chart.append(segment);
|
|
chart.points.push(segment);
|
|
},
|
|
reflow: function (targetBox) {
|
|
var chart = this, options = chart.options, box = targetBox.clone(), space = 5, minWidth = math.min(box.width(), box.height()), halfMinWidth = minWidth / 2, defaultPadding = minWidth - minWidth * 0.85, padding = valueOrDefault(options.padding, defaultPadding), newBox = Box2D(box.x1, box.y1, box.x1 + minWidth, box.y1 + minWidth), newBoxCenter = newBox.center(), seriesConfigs = chart.seriesConfigs || [], boxCenter = box.center(), points = chart.points, count = points.length, seriesCount = options.series.length, leftSideLabels = [], rightSideLabels = [], seriesConfig, seriesIndex, label, segment, sector, r, i, c;
|
|
padding = padding > halfMinWidth - space ? halfMinWidth - space : padding;
|
|
newBox.translate(boxCenter.x - newBoxCenter.x, boxCenter.y - newBoxCenter.y);
|
|
r = halfMinWidth - padding;
|
|
c = Point2D(r + newBox.x1 + padding, r + newBox.y1 + padding);
|
|
for (i = 0; i < count; i++) {
|
|
segment = points[i];
|
|
sector = segment.sector;
|
|
sector.r = r;
|
|
sector.c = c;
|
|
seriesIndex = segment.seriesIx;
|
|
if (seriesConfigs.length) {
|
|
seriesConfig = seriesConfigs[seriesIndex];
|
|
sector.ir = seriesConfig.ir;
|
|
sector.r = seriesConfig.r;
|
|
}
|
|
if (seriesIndex == seriesCount - 1 && segment.explode) {
|
|
sector.c = sector.clone().radius(sector.r * 0.15).point(sector.middle());
|
|
}
|
|
segment.reflow(newBox);
|
|
label = segment.label;
|
|
if (label) {
|
|
if (label.options.position === OUTSIDE_END) {
|
|
if (seriesIndex == seriesCount - 1) {
|
|
if (label.orientation === RIGHT) {
|
|
rightSideLabels.push(label);
|
|
} else {
|
|
leftSideLabels.push(label);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (leftSideLabels.length > 0) {
|
|
leftSideLabels.sort(chart.labelComparator(true));
|
|
chart.leftLabelsReflow(leftSideLabels);
|
|
}
|
|
if (rightSideLabels.length > 0) {
|
|
rightSideLabels.sort(chart.labelComparator(false));
|
|
chart.rightLabelsReflow(rightSideLabels);
|
|
}
|
|
chart.box = newBox;
|
|
},
|
|
leftLabelsReflow: function (labels) {
|
|
var chart = this, distances = chart.distanceBetweenLabels(labels);
|
|
chart.distributeLabels(distances, labels);
|
|
},
|
|
rightLabelsReflow: function (labels) {
|
|
var chart = this, distances = chart.distanceBetweenLabels(labels);
|
|
chart.distributeLabels(distances, labels);
|
|
},
|
|
distanceBetweenLabels: function (labels) {
|
|
var chart = this, points = chart.points, segment = points[points.length - 1], sector = segment.sector, firstBox = labels[0].box, count = labels.length - 1, lr = sector.r + segment.options.labels.distance, distances = [], secondBox, distance, i;
|
|
distance = round(firstBox.y1 - (sector.c.y - lr - firstBox.height() - firstBox.height() / 2));
|
|
distances.push(distance);
|
|
for (i = 0; i < count; i++) {
|
|
firstBox = labels[i].box;
|
|
secondBox = labels[i + 1].box;
|
|
distance = round(secondBox.y1 - firstBox.y2);
|
|
distances.push(distance);
|
|
}
|
|
distance = round(sector.c.y + lr - labels[count].box.y2 - labels[count].box.height() / 2);
|
|
distances.push(distance);
|
|
return distances;
|
|
},
|
|
distributeLabels: function (distances, labels) {
|
|
var chart = this, count = distances.length, remaining, left, right, i;
|
|
for (i = 0; i < count; i++) {
|
|
left = right = i;
|
|
remaining = -distances[i];
|
|
while (remaining > 0 && (left >= 0 || right < count)) {
|
|
remaining = chart._takeDistance(distances, i, --left, remaining);
|
|
remaining = chart._takeDistance(distances, i, ++right, remaining);
|
|
}
|
|
}
|
|
chart.reflowLabels(distances, labels);
|
|
},
|
|
_takeDistance: function (distances, anchor, position, amount) {
|
|
if (distances[position] > 0) {
|
|
var available = math.min(distances[position], amount);
|
|
amount -= available;
|
|
distances[position] -= available;
|
|
distances[anchor] += available;
|
|
}
|
|
return amount;
|
|
},
|
|
reflowLabels: function (distances, labels) {
|
|
var chart = this, points = chart.points, segment = points[points.length - 1], sector = segment.sector, labelsCount = labels.length, labelOptions = segment.options.labels, labelDistance = labelOptions.distance, boxY = sector.c.y - (sector.r + labelDistance) - labels[0].box.height(), label, boxX, box, i;
|
|
distances[0] += 2;
|
|
for (i = 0; i < labelsCount; i++) {
|
|
label = labels[i];
|
|
boxY += distances[i];
|
|
box = label.box;
|
|
boxX = chart.hAlignLabel(box.x2, sector.clone().expand(labelDistance), boxY, boxY + box.height(), label.orientation == RIGHT);
|
|
if (label.orientation == RIGHT) {
|
|
if (labelOptions.align !== CIRCLE) {
|
|
boxX = sector.r + sector.c.x + labelDistance;
|
|
}
|
|
label.reflow(Box2D(boxX + box.width(), boxY, boxX, boxY));
|
|
} else {
|
|
if (labelOptions.align !== CIRCLE) {
|
|
boxX = sector.c.x - sector.r - labelDistance;
|
|
}
|
|
label.reflow(Box2D(boxX - box.width(), boxY, boxX, boxY));
|
|
}
|
|
boxY += box.height();
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var chart = this, options = chart.options, connectors = options.connectors, points = chart.points, connectorLine, count = points.length, space = 4, sector, angle, segment, seriesIx, label, i;
|
|
ChartElement.fn.createVisual.call(this);
|
|
this._connectorLines = [];
|
|
for (i = 0; i < count; i++) {
|
|
segment = points[i];
|
|
sector = segment.sector;
|
|
angle = sector.middle();
|
|
label = segment.label;
|
|
seriesIx = { seriesId: segment.seriesIx };
|
|
if (label) {
|
|
connectorLine = new draw.Path({
|
|
stroke: {
|
|
color: connectors.color,
|
|
width: connectors.width
|
|
},
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: segment.animationDelay
|
|
}
|
|
});
|
|
if (label.options.position === OUTSIDE_END && segment.value !== 0) {
|
|
var box = label.box, centerPoint = sector.c, start = sector.point(angle), middle = Point2D(box.x1, box.center().y), sr, end, crossing;
|
|
start = sector.clone().expand(connectors.padding).point(angle);
|
|
connectorLine.moveTo(start.x, start.y);
|
|
if (label.orientation == RIGHT) {
|
|
end = Point2D(box.x1 - connectors.padding, box.center().y);
|
|
crossing = intersection(centerPoint, start, middle, end);
|
|
middle = Point2D(end.x - space, end.y);
|
|
crossing = crossing || middle;
|
|
crossing.x = math.min(crossing.x, middle.x);
|
|
if (chart.pointInCircle(crossing, sector.c, sector.r + space) || crossing.x < sector.c.x) {
|
|
sr = sector.c.x + sector.r + space;
|
|
if (segment.options.labels.align !== COLUMN) {
|
|
if (sr < middle.x) {
|
|
connectorLine.lineTo(sr, start.y);
|
|
} else {
|
|
connectorLine.lineTo(start.x + space * 2, start.y);
|
|
}
|
|
} else {
|
|
connectorLine.lineTo(sr, start.y);
|
|
}
|
|
connectorLine.lineTo(middle.x, end.y);
|
|
} else {
|
|
crossing.y = end.y;
|
|
connectorLine.lineTo(crossing.x, crossing.y);
|
|
}
|
|
} else {
|
|
end = Point2D(box.x2 + connectors.padding, box.center().y);
|
|
crossing = intersection(centerPoint, start, middle, end);
|
|
middle = Point2D(end.x + space, end.y);
|
|
crossing = crossing || middle;
|
|
crossing.x = math.max(crossing.x, middle.x);
|
|
if (chart.pointInCircle(crossing, sector.c, sector.r + space) || crossing.x > sector.c.x) {
|
|
sr = sector.c.x - sector.r - space;
|
|
if (segment.options.labels.align !== COLUMN) {
|
|
if (sr > middle.x) {
|
|
connectorLine.lineTo(sr, start.y);
|
|
} else {
|
|
connectorLine.lineTo(start.x - space * 2, start.y);
|
|
}
|
|
} else {
|
|
connectorLine.lineTo(sr, start.y);
|
|
}
|
|
connectorLine.lineTo(middle.x, end.y);
|
|
} else {
|
|
crossing.y = end.y;
|
|
connectorLine.lineTo(crossing.x, crossing.y);
|
|
}
|
|
}
|
|
connectorLine.lineTo(end.x, end.y);
|
|
this._connectorLines.push(connectorLine);
|
|
this.visual.append(connectorLine);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
labelComparator: function (reverse) {
|
|
reverse = reverse ? -1 : 1;
|
|
return function (a, b) {
|
|
a = (a.parent.sector.middle() + 270) % 360;
|
|
b = (b.parent.sector.middle() + 270) % 360;
|
|
return (a - b) * reverse;
|
|
};
|
|
},
|
|
hAlignLabel: function (originalX, sector, y1, y2, direction) {
|
|
var cx = sector.c.x, cy = sector.c.y, r = sector.r, t = math.min(math.abs(cy - y1), math.abs(cy - y2));
|
|
if (t > r) {
|
|
return originalX;
|
|
} else {
|
|
return cx + math.sqrt(r * r - t * t) * (direction ? 1 : -1);
|
|
}
|
|
},
|
|
pointInCircle: function (point, c, r) {
|
|
return sqr(c.x - point.x) + sqr(c.y - point.y) < sqr(r);
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
return autoFormat(format, point.value);
|
|
},
|
|
animationDelay: function (categoryIndex) {
|
|
return categoryIndex * PIE_SECTOR_ANIM_DELAY;
|
|
}
|
|
});
|
|
deepExtend(PieChart.fn, PieChartMixin);
|
|
var DonutSegment = PieSegment.extend({
|
|
options: {
|
|
overlay: { gradient: ROUNDED_GLASS },
|
|
labels: { position: CENTER },
|
|
animation: { type: PIE }
|
|
},
|
|
reflowLabel: function () {
|
|
var segment = this, sector = segment.sector.clone(), options = segment.options, label = segment.label, labelsOptions = options.labels, lp, angle = sector.middle(), labelHeight;
|
|
if (label) {
|
|
labelHeight = label.box.height();
|
|
if (labelsOptions.position == CENTER) {
|
|
sector.r -= (sector.r - sector.ir) / 2;
|
|
lp = sector.point(angle);
|
|
label.reflow(new Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
|
|
} else {
|
|
PieSegment.fn.reflowLabel.call(segment);
|
|
}
|
|
}
|
|
},
|
|
createSegment: function (sector, options) {
|
|
return ShapeBuilder.current.createRing(sector, options);
|
|
}
|
|
});
|
|
deepExtend(DonutSegment.fn, PointEventsMixin);
|
|
var DonutChart = PieChart.extend({
|
|
options: {
|
|
startAngle: 90,
|
|
connectors: {
|
|
width: 1,
|
|
color: '#939393',
|
|
padding: 4
|
|
}
|
|
},
|
|
addValue: function (value, sector, fields) {
|
|
var chart = this, segment;
|
|
var segmentOptions = deepExtend({}, fields.series, { index: fields.index });
|
|
chart.evalSegmentOptions(segmentOptions, value, fields);
|
|
chart.createLegendItem(value, segmentOptions, fields);
|
|
if (!value || fields.visible === false) {
|
|
return;
|
|
}
|
|
segment = new DonutSegment(value, sector, segmentOptions);
|
|
extend(segment, fields);
|
|
chart.append(segment);
|
|
chart.points.push(segment);
|
|
},
|
|
reflow: function (targetBox) {
|
|
var chart = this, options = chart.options, box = targetBox.clone(), space = 5, minWidth = math.min(box.width(), box.height()), halfMinWidth = minWidth / 2, defaultPadding = minWidth - minWidth * 0.85, padding = valueOrDefault(options.padding, defaultPadding), series = options.series, currentSeries, seriesCount = series.length, seriesWithoutSize = 0, holeSize, totalSize, size, margin = 0, i, r, ir = 0, currentSize = 0;
|
|
chart.seriesConfigs = [];
|
|
padding = padding > halfMinWidth - space ? halfMinWidth - space : padding;
|
|
totalSize = halfMinWidth - padding;
|
|
for (i = 0; i < seriesCount; i++) {
|
|
currentSeries = series[i];
|
|
if (i === 0) {
|
|
if (defined(currentSeries.holeSize)) {
|
|
holeSize = currentSeries.holeSize;
|
|
totalSize -= currentSeries.holeSize;
|
|
}
|
|
}
|
|
if (defined(currentSeries.size)) {
|
|
totalSize -= currentSeries.size;
|
|
} else {
|
|
seriesWithoutSize++;
|
|
}
|
|
if (defined(currentSeries.margin) && i != seriesCount - 1) {
|
|
totalSize -= currentSeries.margin;
|
|
}
|
|
}
|
|
if (!defined(holeSize)) {
|
|
currentSize = (halfMinWidth - padding) / (seriesCount + 0.75);
|
|
holeSize = currentSize * 0.75;
|
|
totalSize -= holeSize;
|
|
}
|
|
ir = holeSize;
|
|
for (i = 0; i < seriesCount; i++) {
|
|
currentSeries = series[i];
|
|
size = valueOrDefault(currentSeries.size, totalSize / seriesWithoutSize);
|
|
ir += margin;
|
|
r = ir + size;
|
|
chart.seriesConfigs.push({
|
|
ir: ir,
|
|
r: r
|
|
});
|
|
margin = currentSeries.margin || 0;
|
|
ir = r;
|
|
}
|
|
PieChart.fn.reflow.call(chart, targetBox);
|
|
},
|
|
animationDelay: function (categoryIndex, seriesIndex, seriesCount) {
|
|
return categoryIndex * DONUT_SECTOR_ANIM_DELAY + INITIAL_ANIMATION_DURATION * (seriesIndex + 1) / (seriesCount + 1);
|
|
}
|
|
});
|
|
var WaterfallChart = BarChart.extend({
|
|
render: function () {
|
|
BarChart.fn.render.call(this);
|
|
this.createSegments();
|
|
},
|
|
traverseDataPoints: function (callback) {
|
|
var series = this.options.series;
|
|
var categories = this.categoryAxis.options.categories || [];
|
|
var totalCategories = categoriesCount(series);
|
|
var isVertical = !this.options.invertAxes;
|
|
for (var seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
var currentSeries = series[seriesIx];
|
|
var total = 0;
|
|
var runningTotal = 0;
|
|
for (var categoryIx = 0; categoryIx < totalCategories; categoryIx++) {
|
|
var data = SeriesBinder.current.bindPoint(currentSeries, categoryIx);
|
|
var value = data.valueFields.value;
|
|
var summary = data.fields.summary;
|
|
var from = total;
|
|
var to;
|
|
if (summary) {
|
|
if (summary.toLowerCase() === 'total') {
|
|
data.valueFields.value = total;
|
|
from = 0;
|
|
to = total;
|
|
} else {
|
|
data.valueFields.value = runningTotal;
|
|
to = from - runningTotal;
|
|
runningTotal = 0;
|
|
}
|
|
} else if (isNumber(value)) {
|
|
runningTotal += value;
|
|
total += value;
|
|
to = total;
|
|
}
|
|
callback(data, {
|
|
category: categories[categoryIx],
|
|
categoryIx: categoryIx,
|
|
series: currentSeries,
|
|
seriesIx: seriesIx,
|
|
total: total,
|
|
runningTotal: runningTotal,
|
|
from: from,
|
|
to: to,
|
|
isVertical: isVertical
|
|
});
|
|
}
|
|
}
|
|
},
|
|
updateRange: function (value, fields) {
|
|
BarChart.fn.updateRange.call(this, { value: fields.to }, fields);
|
|
},
|
|
aboveAxis: function (point) {
|
|
return point.value >= 0;
|
|
},
|
|
plotRange: function (point) {
|
|
return [
|
|
point.from,
|
|
point.to
|
|
];
|
|
},
|
|
createSegments: function () {
|
|
var series = this.options.series;
|
|
var seriesPoints = this.seriesPoints;
|
|
var segments = this.segments = [];
|
|
for (var seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
var currentSeries = series[seriesIx];
|
|
var points = seriesPoints[seriesIx];
|
|
if (points) {
|
|
var prevPoint;
|
|
for (var pointIx = 0; pointIx < points.length; pointIx++) {
|
|
var point = points[pointIx];
|
|
if (point && prevPoint) {
|
|
var segment = new WaterfallSegment(prevPoint, point, currentSeries);
|
|
segments.push(segment);
|
|
this.append(segment);
|
|
}
|
|
prevPoint = point;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var WaterfallSegment = ChartElement.extend({
|
|
init: function (from, to, series) {
|
|
var segment = this;
|
|
ChartElement.fn.init.call(segment);
|
|
segment.from = from;
|
|
segment.to = to;
|
|
segment.series = series;
|
|
},
|
|
options: {
|
|
animation: {
|
|
type: FADEIN,
|
|
delay: INITIAL_ANIMATION_DURATION
|
|
}
|
|
},
|
|
linePoints: function () {
|
|
var points = [];
|
|
var from = this.from;
|
|
var fromBox = from.box;
|
|
var toBox = this.to.box;
|
|
if (from.isVertical) {
|
|
var y = from.aboveAxis ? fromBox.y1 : fromBox.y2;
|
|
points.push([
|
|
fromBox.x1,
|
|
y
|
|
], [
|
|
toBox.x2,
|
|
y
|
|
]);
|
|
} else {
|
|
var x = from.aboveAxis ? fromBox.x2 : fromBox.x1;
|
|
points.push([
|
|
x,
|
|
fromBox.y1
|
|
], [
|
|
x,
|
|
toBox.y2
|
|
]);
|
|
}
|
|
return points;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var line = this.series.line || {};
|
|
var path = draw.Path.fromPoints(this.linePoints(), {
|
|
stroke: {
|
|
color: line.color,
|
|
width: line.width,
|
|
opacity: line.opacity,
|
|
dashType: line.dashType
|
|
}
|
|
});
|
|
alignPathToPixel(path);
|
|
this.visual.append(path);
|
|
}
|
|
});
|
|
function returnSelf() {
|
|
return this;
|
|
}
|
|
var Pane = BoxElement.extend({
|
|
init: function (options) {
|
|
var pane = this;
|
|
BoxElement.fn.init.call(pane, options);
|
|
options = pane.options;
|
|
pane.id = kendo.guid();
|
|
pane.createTitle();
|
|
pane.content = new ChartElement();
|
|
pane.chartContainer = new ChartContainer({}, pane);
|
|
pane.append(pane.content);
|
|
pane.axes = [];
|
|
pane.charts = [];
|
|
},
|
|
options: {
|
|
zIndex: -1,
|
|
shrinkToFit: true,
|
|
title: { align: LEFT },
|
|
visible: true
|
|
},
|
|
createTitle: function () {
|
|
var pane = this;
|
|
var titleOptions = pane.options.title;
|
|
if (typeof titleOptions === OBJECT) {
|
|
titleOptions = deepExtend({}, titleOptions, {
|
|
align: titleOptions.position,
|
|
position: TOP
|
|
});
|
|
}
|
|
pane.title = Title.buildTitle(titleOptions, pane, Pane.fn.options.title);
|
|
},
|
|
appendAxis: function (axis) {
|
|
var pane = this;
|
|
pane.content.append(axis);
|
|
pane.axes.push(axis);
|
|
axis.pane = pane;
|
|
},
|
|
appendChart: function (chart) {
|
|
var pane = this;
|
|
if (pane.chartContainer.parent !== pane.content) {
|
|
pane.content.append(pane.chartContainer);
|
|
}
|
|
pane.charts.push(chart);
|
|
pane.chartContainer.append(chart);
|
|
chart.pane = pane;
|
|
},
|
|
empty: function () {
|
|
var pane = this, plotArea = pane.parent, i;
|
|
if (plotArea) {
|
|
for (i = 0; i < pane.axes.length; i++) {
|
|
plotArea.removeAxis(pane.axes[i]);
|
|
}
|
|
for (i = 0; i < pane.charts.length; i++) {
|
|
plotArea.removeChart(pane.charts[i]);
|
|
}
|
|
}
|
|
pane.axes = [];
|
|
pane.charts = [];
|
|
pane.content.destroy();
|
|
pane.content.children = [];
|
|
pane.chartContainer.children = [];
|
|
},
|
|
reflow: function (targetBox) {
|
|
var pane = this;
|
|
var content;
|
|
if (last(pane.children) === pane.content) {
|
|
content = pane.children.pop();
|
|
}
|
|
BoxElement.fn.reflow.call(pane, targetBox);
|
|
if (content) {
|
|
pane.children.push(content);
|
|
}
|
|
if (pane.title) {
|
|
pane.contentBox.y1 += pane.title.box.height();
|
|
}
|
|
},
|
|
visualStyle: function () {
|
|
var style = BoxElement.fn.visualStyle.call(this);
|
|
style.zIndex = -10;
|
|
return style;
|
|
},
|
|
renderComplete: function () {
|
|
if (this.options.visible) {
|
|
this.createGridLines();
|
|
}
|
|
},
|
|
stackRoot: returnSelf,
|
|
clipRoot: returnSelf,
|
|
createGridLines: function () {
|
|
var pane = this, axes = pane.axes, allAxes = axes.concat(pane.parent.axes), vGridLines = [], hGridLines = [], gridLines, i, j, axis, vertical, altAxis;
|
|
for (i = 0; i < axes.length; i++) {
|
|
axis = axes[i];
|
|
vertical = axis.options.vertical;
|
|
gridLines = vertical ? vGridLines : hGridLines;
|
|
for (j = 0; j < allAxes.length; j++) {
|
|
if (gridLines.length === 0) {
|
|
altAxis = allAxes[j];
|
|
if (vertical !== altAxis.options.vertical) {
|
|
append(gridLines, axis.createGridLines(altAxis));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
refresh: function () {
|
|
this.visual.clear();
|
|
this.content.parent = null;
|
|
this.content.createGradient = $.proxy(this.createGradient, this);
|
|
this.content.renderVisual();
|
|
this.content.parent = this;
|
|
if (this.title) {
|
|
this.visual.append(this.title.visual);
|
|
}
|
|
this.visual.append(this.content.visual);
|
|
this.renderComplete();
|
|
},
|
|
clipBox: function () {
|
|
return this.chartContainer.clipBox;
|
|
}
|
|
});
|
|
var ChartContainer = ChartElement.extend({
|
|
init: function (options, pane) {
|
|
var container = this;
|
|
ChartElement.fn.init.call(container, options);
|
|
container.pane = pane;
|
|
},
|
|
shouldClip: function () {
|
|
var container = this, children = container.children, length = children.length, i;
|
|
for (i = 0; i < length; i++) {
|
|
if (children[i].options.clip === true) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_clipBox: function () {
|
|
var container = this, pane = container.pane, axes = pane.axes, length = axes.length, clipBox = pane.box.clone(), axisValueField, idx, lineBox, axis;
|
|
for (idx = 0; idx < length; idx++) {
|
|
axis = axes[idx];
|
|
axisValueField = axis.options.vertical ? Y : X;
|
|
lineBox = axis.lineBox();
|
|
clipBox[axisValueField + 1] = lineBox[axisValueField + 1];
|
|
clipBox[axisValueField + 2] = lineBox[axisValueField + 2];
|
|
}
|
|
return clipBox;
|
|
},
|
|
createVisual: function () {
|
|
this.visual = new draw.Group({ zIndex: 0 });
|
|
if (this.shouldClip()) {
|
|
var clipBox = this.clipBox = this._clipBox();
|
|
var clipRect = clipBox.toRect();
|
|
var clipPath = draw.Path.fromRect(clipRect);
|
|
this.visual.clip(clipPath);
|
|
this.unclipLabels();
|
|
}
|
|
},
|
|
stackRoot: returnSelf,
|
|
unclipLabels: function () {
|
|
var container = this, charts = container.children, clipBox = container.clipBox, points, point, i, j, length;
|
|
for (i = 0; i < charts.length; i++) {
|
|
points = charts[i].points || {};
|
|
length = points.length;
|
|
for (j = 0; j < length; j++) {
|
|
point = points[j];
|
|
if (point && point.label && point.label.options.visible) {
|
|
if (point.overlapsBox(clipBox)) {
|
|
if (point.label.alignToClipBox) {
|
|
point.label.alignToClipBox(clipBox);
|
|
}
|
|
point.label.options.noclip = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
ChartElement.fn.destroy.call(this);
|
|
delete this.parent;
|
|
}
|
|
});
|
|
var PlotAreaBase = ChartElement.extend({
|
|
init: function (series, options) {
|
|
var plotArea = this;
|
|
ChartElement.fn.init.call(plotArea, options);
|
|
plotArea.series = series;
|
|
plotArea.initSeries();
|
|
plotArea.charts = [];
|
|
plotArea.options.legend.items = [];
|
|
plotArea.axes = [];
|
|
plotArea.crosshairs = [];
|
|
plotArea.createPanes();
|
|
plotArea.render();
|
|
plotArea.createCrosshairs();
|
|
},
|
|
options: {
|
|
series: [],
|
|
plotArea: { margin: {} },
|
|
background: '',
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
legend: {
|
|
inactiveItems: {
|
|
labels: { color: '#919191' },
|
|
markers: { color: '#919191' }
|
|
}
|
|
}
|
|
},
|
|
initSeries: function () {
|
|
var series = this.series, i, currentSeries;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
currentSeries.index = i;
|
|
}
|
|
},
|
|
createPanes: function () {
|
|
var plotArea = this, panes = [], paneOptions = plotArea.options.panes || [], i, panesLength = math.max(paneOptions.length, 1), currentPane;
|
|
for (i = 0; i < panesLength; i++) {
|
|
currentPane = new Pane(paneOptions[i]);
|
|
currentPane.paneIndex = i;
|
|
panes.push(currentPane);
|
|
plotArea.append(currentPane);
|
|
}
|
|
plotArea.panes = panes;
|
|
},
|
|
createCrosshairs: function (panes) {
|
|
var plotArea = this, i, j, pane, axis, currentCrosshair;
|
|
panes = panes || plotArea.panes;
|
|
for (i = 0; i < panes.length; i++) {
|
|
pane = panes[i];
|
|
for (j = 0; j < pane.axes.length; j++) {
|
|
axis = pane.axes[j];
|
|
if (axis.options.crosshair && axis.options.crosshair.visible) {
|
|
currentCrosshair = new Crosshair(axis, axis.options.crosshair);
|
|
plotArea.crosshairs.push(currentCrosshair);
|
|
pane.content.append(currentCrosshair);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
removeCrosshairs: function (pane) {
|
|
var plotArea = this, crosshairs = plotArea.crosshairs, axes = pane.axes, i, j;
|
|
for (i = crosshairs.length - 1; i >= 0; i--) {
|
|
for (j = 0; j < axes.length; j++) {
|
|
if (crosshairs[i].axis === axes[j]) {
|
|
crosshairs.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
hideCrosshairs: function () {
|
|
var crosshairs = this.crosshairs;
|
|
for (var idx = 0; idx < crosshairs.length; idx++) {
|
|
crosshairs[idx].hide();
|
|
}
|
|
},
|
|
findPane: function (name) {
|
|
var plotArea = this, panes = plotArea.panes, i, matchingPane;
|
|
for (i = 0; i < panes.length; i++) {
|
|
if (panes[i].options.name === name) {
|
|
matchingPane = panes[i];
|
|
break;
|
|
}
|
|
}
|
|
return matchingPane || panes[0];
|
|
},
|
|
findPointPane: function (point) {
|
|
var plotArea = this, panes = plotArea.panes, i, matchingPane;
|
|
for (i = 0; i < panes.length; i++) {
|
|
if (panes[i].box.containsPoint(point)) {
|
|
matchingPane = panes[i];
|
|
break;
|
|
}
|
|
}
|
|
return matchingPane;
|
|
},
|
|
appendAxis: function (axis) {
|
|
var plotArea = this, pane = plotArea.findPane(axis.options.pane);
|
|
pane.appendAxis(axis);
|
|
plotArea.axes.push(axis);
|
|
axis.plotArea = plotArea;
|
|
},
|
|
removeAxis: function (axisToRemove) {
|
|
var plotArea = this, i, axis, filteredAxes = [];
|
|
for (i = 0; i < plotArea.axes.length; i++) {
|
|
axis = plotArea.axes[i];
|
|
if (axisToRemove !== axis) {
|
|
filteredAxes.push(axis);
|
|
} else {
|
|
axis.destroy();
|
|
}
|
|
}
|
|
plotArea.axes = filteredAxes;
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
var plotArea = this;
|
|
plotArea.charts.push(chart);
|
|
if (pane) {
|
|
pane.appendChart(chart);
|
|
} else {
|
|
plotArea.append(chart);
|
|
}
|
|
},
|
|
removeChart: function (chartToRemove) {
|
|
var plotArea = this, i, chart, filteredCharts = [];
|
|
for (i = 0; i < plotArea.charts.length; i++) {
|
|
chart = plotArea.charts[i];
|
|
if (chart !== chartToRemove) {
|
|
filteredCharts.push(chart);
|
|
} else {
|
|
chart.destroy();
|
|
}
|
|
}
|
|
plotArea.charts = filteredCharts;
|
|
},
|
|
addToLegend: function (series) {
|
|
var count = series.length, data = [], i, currentSeries, text, legend = this.options.legend, labels = legend.labels || {}, inactiveItems = legend.inactiveItems || {}, inactiveItemsLabels = inactiveItems.labels || {}, color, itemLabelOptions, markerColor, defaults, seriesVisible, labelTemplate;
|
|
for (i = 0; i < count; i++) {
|
|
currentSeries = series[i];
|
|
seriesVisible = currentSeries.visible !== false;
|
|
if (currentSeries.visibleInLegend === false) {
|
|
continue;
|
|
}
|
|
text = currentSeries.name || '';
|
|
labelTemplate = seriesVisible ? labels.template : inactiveItemsLabels.template || labels.template;
|
|
if (labelTemplate) {
|
|
text = template(labelTemplate)({
|
|
text: text,
|
|
series: currentSeries
|
|
});
|
|
}
|
|
color = currentSeries.color;
|
|
defaults = currentSeries._defaults;
|
|
if (isFn(color) && defaults) {
|
|
color = defaults.color;
|
|
}
|
|
if (seriesVisible) {
|
|
itemLabelOptions = {};
|
|
markerColor = color;
|
|
} else {
|
|
itemLabelOptions = {
|
|
color: inactiveItemsLabels.color,
|
|
font: inactiveItemsLabels.font
|
|
};
|
|
markerColor = inactiveItems.markers.color;
|
|
}
|
|
if (text) {
|
|
data.push({
|
|
text: text,
|
|
labels: itemLabelOptions,
|
|
markerColor: markerColor,
|
|
series: currentSeries,
|
|
active: seriesVisible
|
|
});
|
|
}
|
|
}
|
|
append(legend.items, data);
|
|
},
|
|
groupAxes: function (panes) {
|
|
var xAxes = [], yAxes = [], paneAxes, axis, paneIx, axisIx;
|
|
for (paneIx = 0; paneIx < panes.length; paneIx++) {
|
|
paneAxes = panes[paneIx].axes;
|
|
for (axisIx = 0; axisIx < paneAxes.length; axisIx++) {
|
|
axis = paneAxes[axisIx];
|
|
if (axis.options.vertical) {
|
|
yAxes.push(axis);
|
|
} else {
|
|
xAxes.push(axis);
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
x: xAxes,
|
|
y: yAxes,
|
|
any: xAxes.concat(yAxes)
|
|
};
|
|
},
|
|
groupSeriesByPane: function () {
|
|
var plotArea = this, series = plotArea.series, seriesByPane = {}, i, pane, currentSeries;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
pane = plotArea.seriesPaneName(currentSeries);
|
|
if (seriesByPane[pane]) {
|
|
seriesByPane[pane].push(currentSeries);
|
|
} else {
|
|
seriesByPane[pane] = [currentSeries];
|
|
}
|
|
}
|
|
return seriesByPane;
|
|
},
|
|
filterVisibleSeries: function (series) {
|
|
var i, currentSeries, result = [];
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
if (currentSeries.visible !== false) {
|
|
result.push(currentSeries);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
reflow: function (targetBox) {
|
|
var plotArea = this, options = plotArea.options.plotArea, panes = plotArea.panes, margin = getSpacing(options.margin);
|
|
plotArea.box = targetBox.clone().unpad(margin);
|
|
plotArea.reflowPanes();
|
|
plotArea.reflowAxes(panes);
|
|
plotArea.reflowCharts(panes);
|
|
},
|
|
redraw: function (panes) {
|
|
var plotArea = this, i;
|
|
panes = [].concat(panes);
|
|
this.initSeries();
|
|
for (i = 0; i < panes.length; i++) {
|
|
plotArea.removeCrosshairs(panes[i]);
|
|
panes[i].empty();
|
|
}
|
|
plotArea.render(panes);
|
|
plotArea.reflowAxes(plotArea.panes);
|
|
plotArea.reflowCharts(panes);
|
|
plotArea.createCrosshairs(panes);
|
|
for (i = 0; i < panes.length; i++) {
|
|
panes[i].refresh();
|
|
}
|
|
},
|
|
axisCrossingValues: function (axis, crossingAxes) {
|
|
var options = axis.options, crossingValues = [].concat(options.axisCrossingValues || options.axisCrossingValue), valuesToAdd = crossingAxes.length - crossingValues.length, defaultValue = crossingValues[0] || 0, i;
|
|
for (i = 0; i < valuesToAdd; i++) {
|
|
crossingValues.push(defaultValue);
|
|
}
|
|
return crossingValues;
|
|
},
|
|
alignAxisTo: function (axis, targetAxis, crossingValue, targetCrossingValue) {
|
|
var slot = axis.getSlot(crossingValue, crossingValue, true), slotEdge = axis.options.reverse ? 2 : 1, targetSlot = targetAxis.getSlot(targetCrossingValue, targetCrossingValue, true), targetEdge = targetAxis.options.reverse ? 2 : 1, axisBox = axis.box.translate(targetSlot[X + targetEdge] - slot[X + slotEdge], targetSlot[Y + targetEdge] - slot[Y + slotEdge]);
|
|
if (axis.pane !== targetAxis.pane) {
|
|
axisBox.translate(0, axis.pane.box.y1 - targetAxis.pane.box.y1);
|
|
}
|
|
axis.reflow(axisBox);
|
|
},
|
|
alignAxes: function (xAxes, yAxes) {
|
|
var plotArea = this, xAnchor = xAxes[0], yAnchor = yAxes[0], xAnchorCrossings = plotArea.axisCrossingValues(xAnchor, yAxes), yAnchorCrossings = plotArea.axisCrossingValues(yAnchor, xAxes), leftAnchors = {}, rightAnchors = {}, topAnchors = {}, bottomAnchors = {}, pane, paneId, axis, i;
|
|
for (i = 0; i < yAxes.length; i++) {
|
|
axis = yAxes[i];
|
|
pane = axis.pane;
|
|
paneId = pane.id;
|
|
plotArea.alignAxisTo(axis, xAnchor, yAnchorCrossings[i], xAnchorCrossings[i]);
|
|
if (axis.options._overlap) {
|
|
continue;
|
|
}
|
|
if (round(axis.lineBox().x1) === round(xAnchor.lineBox().x1)) {
|
|
if (leftAnchors[paneId]) {
|
|
axis.reflow(axis.box.alignTo(leftAnchors[paneId].box, LEFT).translate(-axis.options.margin, 0));
|
|
}
|
|
leftAnchors[paneId] = axis;
|
|
}
|
|
if (round(axis.lineBox().x2) === round(xAnchor.lineBox().x2)) {
|
|
if (!axis._mirrored) {
|
|
axis.options.labels.mirror = !axis.options.labels.mirror;
|
|
axis._mirrored = true;
|
|
}
|
|
plotArea.alignAxisTo(axis, xAnchor, yAnchorCrossings[i], xAnchorCrossings[i]);
|
|
if (rightAnchors[paneId]) {
|
|
axis.reflow(axis.box.alignTo(rightAnchors[paneId].box, RIGHT).translate(axis.options.margin, 0));
|
|
}
|
|
rightAnchors[paneId] = axis;
|
|
}
|
|
if (i !== 0 && yAnchor.pane === axis.pane) {
|
|
axis.alignTo(yAnchor);
|
|
axis.reflow(axis.box);
|
|
}
|
|
}
|
|
for (i = 0; i < xAxes.length; i++) {
|
|
axis = xAxes[i];
|
|
pane = axis.pane;
|
|
paneId = pane.id;
|
|
plotArea.alignAxisTo(axis, yAnchor, xAnchorCrossings[i], yAnchorCrossings[i]);
|
|
if (axis.options._overlap) {
|
|
continue;
|
|
}
|
|
if (round(axis.lineBox().y1) === round(yAnchor.lineBox().y1)) {
|
|
if (!axis._mirrored) {
|
|
axis.options.labels.mirror = !axis.options.labels.mirror;
|
|
axis._mirrored = true;
|
|
}
|
|
plotArea.alignAxisTo(axis, yAnchor, xAnchorCrossings[i], yAnchorCrossings[i]);
|
|
if (topAnchors[paneId]) {
|
|
axis.reflow(axis.box.alignTo(topAnchors[paneId].box, TOP).translate(0, -axis.options.margin));
|
|
}
|
|
topAnchors[paneId] = axis;
|
|
}
|
|
if (round(axis.lineBox().y2, COORD_PRECISION) === round(yAnchor.lineBox().y2, COORD_PRECISION)) {
|
|
if (bottomAnchors[paneId]) {
|
|
axis.reflow(axis.box.alignTo(bottomAnchors[paneId].box, BOTTOM).translate(0, axis.options.margin));
|
|
}
|
|
bottomAnchors[paneId] = axis;
|
|
}
|
|
if (i !== 0) {
|
|
axis.alignTo(xAnchor);
|
|
axis.reflow(axis.box);
|
|
}
|
|
}
|
|
},
|
|
shrinkAxisWidth: function (panes) {
|
|
var plotArea = this, axes = plotArea.groupAxes(panes).any, axisBox = axisGroupBox(axes), overflowX = 0, i, currentPane, currentAxis;
|
|
for (i = 0; i < panes.length; i++) {
|
|
currentPane = panes[i];
|
|
if (currentPane.axes.length > 0) {
|
|
overflowX = math.max(overflowX, axisBox.width() - currentPane.contentBox.width());
|
|
}
|
|
}
|
|
if (overflowX !== 0) {
|
|
for (i = 0; i < axes.length; i++) {
|
|
currentAxis = axes[i];
|
|
if (!currentAxis.options.vertical) {
|
|
currentAxis.reflow(currentAxis.box.shrink(overflowX, 0));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
shrinkAxisHeight: function (panes) {
|
|
var i, currentPane, axes, overflowY, j, currentAxis, shrinked;
|
|
for (i = 0; i < panes.length; i++) {
|
|
currentPane = panes[i];
|
|
axes = currentPane.axes;
|
|
overflowY = math.max(0, axisGroupBox(axes).height() - currentPane.contentBox.height());
|
|
if (overflowY !== 0) {
|
|
for (j = 0; j < axes.length; j++) {
|
|
currentAxis = axes[j];
|
|
if (currentAxis.options.vertical) {
|
|
currentAxis.reflow(currentAxis.box.shrink(0, overflowY));
|
|
}
|
|
}
|
|
shrinked = true;
|
|
}
|
|
}
|
|
return shrinked;
|
|
},
|
|
fitAxes: function (panes) {
|
|
var plotArea = this, axes = plotArea.groupAxes(panes).any, offsetX = 0, paneAxes, paneBox, axisBox, offsetY, currentPane, currentAxis, i, j;
|
|
for (i = 0; i < panes.length; i++) {
|
|
currentPane = panes[i];
|
|
paneAxes = currentPane.axes;
|
|
paneBox = currentPane.contentBox;
|
|
if (paneAxes.length > 0) {
|
|
axisBox = axisGroupBox(paneAxes);
|
|
offsetX = math.max(offsetX, paneBox.x1 - axisBox.x1);
|
|
offsetY = math.max(paneBox.y1 - axisBox.y1, paneBox.y2 - axisBox.y2);
|
|
for (j = 0; j < paneAxes.length; j++) {
|
|
currentAxis = paneAxes[j];
|
|
currentAxis.reflow(currentAxis.box.translate(0, offsetY));
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < axes.length; i++) {
|
|
currentAxis = axes[i];
|
|
currentAxis.reflow(currentAxis.box.translate(offsetX, 0));
|
|
}
|
|
},
|
|
reflowAxes: function (panes) {
|
|
var plotArea = this, i, axes = plotArea.groupAxes(panes);
|
|
for (i = 0; i < panes.length; i++) {
|
|
plotArea.reflowPaneAxes(panes[i]);
|
|
}
|
|
if (axes.x.length > 0 && axes.y.length > 0) {
|
|
plotArea.alignAxes(axes.x, axes.y);
|
|
plotArea.shrinkAxisWidth(panes);
|
|
plotArea.autoRotateAxisLabels(axes);
|
|
plotArea.alignAxes(axes.x, axes.y);
|
|
if (plotArea.shrinkAxisWidth(panes)) {
|
|
plotArea.alignAxes(axes.x, axes.y);
|
|
}
|
|
plotArea.shrinkAxisHeight(panes);
|
|
plotArea.alignAxes(axes.x, axes.y);
|
|
if (plotArea.shrinkAxisHeight(panes)) {
|
|
plotArea.alignAxes(axes.x, axes.y);
|
|
}
|
|
plotArea.fitAxes(panes);
|
|
}
|
|
},
|
|
autoRotateAxisLabels: function (groupedAxes) {
|
|
var axes = this.axes;
|
|
var panes = this.panes;
|
|
var axis, idx, rotated;
|
|
for (idx = 0; idx < axes.length; idx++) {
|
|
axis = axes[idx];
|
|
if (axis.autoRotateLabels()) {
|
|
rotated = true;
|
|
}
|
|
}
|
|
if (rotated) {
|
|
for (idx = 0; idx < panes.length; idx++) {
|
|
this.reflowPaneAxes(panes[idx]);
|
|
}
|
|
if (groupedAxes.x.length > 0 && groupedAxes.y.length > 0) {
|
|
this.alignAxes(groupedAxes.x, groupedAxes.y);
|
|
this.shrinkAxisWidth(panes);
|
|
}
|
|
}
|
|
},
|
|
reflowPaneAxes: function (pane) {
|
|
var axes = pane.axes, i, length = axes.length;
|
|
if (length > 0) {
|
|
for (i = 0; i < length; i++) {
|
|
axes[i].reflow(pane.contentBox);
|
|
}
|
|
}
|
|
},
|
|
reflowCharts: function (panes) {
|
|
var plotArea = this, charts = plotArea.charts, count = charts.length, box = plotArea.box, chartPane, i;
|
|
for (i = 0; i < count; i++) {
|
|
chartPane = charts[i].pane;
|
|
if (!chartPane || inArray(chartPane, panes)) {
|
|
charts[i].reflow(box);
|
|
}
|
|
}
|
|
},
|
|
reflowPanes: function () {
|
|
var plotArea = this, box = plotArea.box, panes = plotArea.panes, panesLength = panes.length, i, currentPane, paneBox, remainingHeight = box.height(), remainingPanes = panesLength, autoHeightPanes = 0, top = box.y1, height, percents;
|
|
for (i = 0; i < panesLength; i++) {
|
|
currentPane = panes[i];
|
|
height = currentPane.options.height;
|
|
currentPane.options.width = box.width();
|
|
if (!currentPane.options.height) {
|
|
autoHeightPanes++;
|
|
} else {
|
|
if (height.indexOf && height.indexOf('%')) {
|
|
percents = parseInt(height, 10) / 100;
|
|
currentPane.options.height = percents * box.height();
|
|
}
|
|
currentPane.reflow(box.clone());
|
|
remainingHeight -= currentPane.options.height;
|
|
}
|
|
}
|
|
for (i = 0; i < panesLength; i++) {
|
|
currentPane = panes[i];
|
|
if (!currentPane.options.height) {
|
|
currentPane.options.height = remainingHeight / autoHeightPanes;
|
|
}
|
|
}
|
|
for (i = 0; i < panesLength; i++) {
|
|
currentPane = panes[i];
|
|
paneBox = box.clone().move(box.x1, top);
|
|
currentPane.reflow(paneBox);
|
|
remainingPanes--;
|
|
top += currentPane.options.height;
|
|
}
|
|
},
|
|
backgroundBox: function () {
|
|
var plotArea = this, axes = plotArea.axes, axesCount = axes.length, lineBox, box, i, j, axisA, axisB;
|
|
for (i = 0; i < axesCount; i++) {
|
|
axisA = axes[i];
|
|
for (j = 0; j < axesCount; j++) {
|
|
axisB = axes[j];
|
|
if (axisA.options.vertical !== axisB.options.vertical) {
|
|
lineBox = axisA.lineBox().clone().wrap(axisB.lineBox());
|
|
if (!box) {
|
|
box = lineBox;
|
|
} else {
|
|
box = box.wrap(lineBox);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return box || plotArea.box;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var bgBox = this.backgroundBox();
|
|
var options = this.options.plotArea;
|
|
var border = options.border || {};
|
|
var background = options.background;
|
|
var opacity = options.opacity;
|
|
if (util.isTransparent(background)) {
|
|
background = WHITE;
|
|
opacity = 0;
|
|
}
|
|
var bg = this._bgVisual = draw.Path.fromRect(bgBox.toRect(), {
|
|
fill: {
|
|
color: background,
|
|
opacity: opacity
|
|
},
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType
|
|
},
|
|
zIndex: -1
|
|
});
|
|
this.appendVisual(bg);
|
|
},
|
|
pointsByCategoryIndex: function (categoryIndex) {
|
|
var charts = this.charts, result = [], i, j, points, point, chart;
|
|
if (categoryIndex !== null) {
|
|
for (i = 0; i < charts.length; i++) {
|
|
chart = charts[i];
|
|
if (chart.pane.options.name === '_navigator') {
|
|
continue;
|
|
}
|
|
points = charts[i].categoryPoints[categoryIndex];
|
|
if (points && points.length) {
|
|
for (j = 0; j < points.length; j++) {
|
|
point = points[j];
|
|
if (point && defined(point.value) && point.value !== null) {
|
|
result.push(point);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
pointsBySeriesIndex: function (seriesIndex) {
|
|
var charts = this.charts, result = [], points, point, i, j, chart;
|
|
for (i = 0; i < charts.length; i++) {
|
|
chart = charts[i];
|
|
points = chart.points;
|
|
for (j = 0; j < points.length; j++) {
|
|
point = points[j];
|
|
if (point && point.options.index === seriesIndex) {
|
|
result.push(point);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
pointsBySeriesName: function (name) {
|
|
var charts = this.charts, result = [], points, point, i, j, chart;
|
|
for (i = 0; i < charts.length; i++) {
|
|
chart = charts[i];
|
|
points = chart.points;
|
|
for (j = 0; j < points.length; j++) {
|
|
point = points[j];
|
|
if (point && point.series.name === name) {
|
|
result.push(point);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
paneByPoint: function (point) {
|
|
var plotArea = this, panes = plotArea.panes, pane, i;
|
|
for (i = 0; i < panes.length; i++) {
|
|
pane = panes[i];
|
|
if (pane.box.containsPoint(point)) {
|
|
return pane;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var CategoricalPlotArea = PlotAreaBase.extend({
|
|
init: function (series, options) {
|
|
var plotArea = this;
|
|
plotArea.namedCategoryAxes = {};
|
|
plotArea.namedValueAxes = {};
|
|
plotArea.valueAxisRangeTracker = new AxisGroupRangeTracker();
|
|
if (series.length > 0) {
|
|
plotArea.invertAxes = inArray(series[0].type, [
|
|
BAR,
|
|
BULLET,
|
|
VERTICAL_LINE,
|
|
VERTICAL_AREA,
|
|
RANGE_BAR,
|
|
HORIZONTAL_WATERFALL
|
|
]);
|
|
for (var i = 0; i < series.length; i++) {
|
|
var stack = series[i].stack;
|
|
if (stack && stack.type === '100%') {
|
|
plotArea.stack100 = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
PlotAreaBase.fn.init.call(plotArea, series, options);
|
|
},
|
|
options: {
|
|
categoryAxis: { categories: [] },
|
|
valueAxis: {}
|
|
},
|
|
render: function (panes) {
|
|
var plotArea = this;
|
|
panes = panes || plotArea.panes;
|
|
plotArea.createCategoryAxes(panes);
|
|
plotArea.aggregateCategories(panes);
|
|
plotArea.createCategoryAxesLabels(panes);
|
|
plotArea.createCharts(panes);
|
|
plotArea.createValueAxes(panes);
|
|
},
|
|
removeAxis: function (axis) {
|
|
var plotArea = this, axisName = axis.options.name;
|
|
PlotAreaBase.fn.removeAxis.call(plotArea, axis);
|
|
if (axis instanceof CategoryAxis) {
|
|
delete plotArea.namedCategoryAxes[axisName];
|
|
} else {
|
|
plotArea.valueAxisRangeTracker.reset(axisName);
|
|
delete plotArea.namedValueAxes[axisName];
|
|
}
|
|
if (axis === plotArea.categoryAxis) {
|
|
delete plotArea.categoryAxis;
|
|
}
|
|
if (axis === plotArea.valueAxis) {
|
|
delete plotArea.valueAxis;
|
|
}
|
|
},
|
|
createCharts: function (panes) {
|
|
var seriesByPane = this.groupSeriesByPane();
|
|
for (var i = 0; i < panes.length; i++) {
|
|
var pane = panes[i];
|
|
var paneSeries = seriesByPane[pane.options.name || 'default'] || [];
|
|
this.addToLegend(paneSeries);
|
|
var visibleSeries = this.filterVisibleSeries(paneSeries);
|
|
if (!visibleSeries) {
|
|
continue;
|
|
}
|
|
var groups = this.groupSeriesByCategoryAxis(visibleSeries);
|
|
for (var groupIx = 0; groupIx < groups.length; groupIx++) {
|
|
this.createChartGroup(groups[groupIx], pane);
|
|
}
|
|
}
|
|
},
|
|
createChartGroup: function (series, pane) {
|
|
this.createAreaChart(filterSeriesByType(series, [
|
|
AREA,
|
|
VERTICAL_AREA
|
|
]), pane);
|
|
this.createBarChart(filterSeriesByType(series, [
|
|
COLUMN,
|
|
BAR
|
|
]), pane);
|
|
this.createRangeBarChart(filterSeriesByType(series, [
|
|
RANGE_COLUMN,
|
|
RANGE_BAR
|
|
]), pane);
|
|
this.createBulletChart(filterSeriesByType(series, [
|
|
BULLET,
|
|
VERTICAL_BULLET
|
|
]), pane);
|
|
this.createCandlestickChart(filterSeriesByType(series, CANDLESTICK), pane);
|
|
this.createBoxPlotChart(filterSeriesByType(series, BOX_PLOT), pane);
|
|
this.createOHLCChart(filterSeriesByType(series, OHLC), pane);
|
|
this.createWaterfallChart(filterSeriesByType(series, [
|
|
WATERFALL,
|
|
HORIZONTAL_WATERFALL
|
|
]), pane);
|
|
this.createLineChart(filterSeriesByType(series, [
|
|
LINE,
|
|
VERTICAL_LINE
|
|
]), pane);
|
|
},
|
|
aggregateCategories: function (panes) {
|
|
var plotArea = this, series = plotArea.srcSeries || plotArea.series, processedSeries = [], i, currentSeries, categoryAxis, axisPane, dateAxis;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
categoryAxis = plotArea.seriesCategoryAxis(currentSeries);
|
|
axisPane = plotArea.findPane(categoryAxis.options.pane);
|
|
dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
|
|
if ((dateAxis || currentSeries.categoryField) && inArray(axisPane, panes)) {
|
|
currentSeries = plotArea.aggregateSeries(currentSeries, categoryAxis);
|
|
} else if (isNumber(categoryAxis.options.min) || isNumber(categoryAxis.options.max)) {
|
|
currentSeries = plotArea.filterSeries(currentSeries, categoryAxis);
|
|
}
|
|
processedSeries.push(currentSeries);
|
|
}
|
|
plotArea.srcSeries = series;
|
|
plotArea.series = processedSeries;
|
|
},
|
|
filterSeries: function (currentSeries, categoryAxis) {
|
|
var range = categoryAxis.totalRangeIndices();
|
|
var justified = categoryAxis.options.justified;
|
|
var outOfRangePoints = inArray(currentSeries.type, [
|
|
LINE,
|
|
VERTICAL_LINE,
|
|
AREA,
|
|
VERTICAL_AREA
|
|
]);
|
|
var categoryIx;
|
|
range.min = isNumber(categoryAxis.options.min) ? math.floor(range.min) : 0;
|
|
range.max = isNumber(categoryAxis.options.max) ? justified ? math.floor(range.max) + 1 : math.ceil(range.max) : currentSeries.data.length;
|
|
currentSeries = deepExtend({}, currentSeries);
|
|
if (outOfRangePoints) {
|
|
var minCategory = range.min - 1;
|
|
var srcCategories = categoryAxis.options.srcCategories || [];
|
|
if (minCategory >= 0 && minCategory < currentSeries.data.length) {
|
|
categoryIx = minCategory;
|
|
currentSeries._outOfRangeMinPoint = {
|
|
item: currentSeries.data[categoryIx],
|
|
category: srcCategories[categoryIx],
|
|
categoryIx: -1
|
|
};
|
|
}
|
|
if (range.max < currentSeries.data.length) {
|
|
categoryIx = range.max;
|
|
currentSeries._outOfRangeMaxPoint = {
|
|
item: currentSeries.data[categoryIx],
|
|
category: srcCategories[categoryIx],
|
|
categoryIx: range.max - range.min
|
|
};
|
|
}
|
|
}
|
|
categoryAxis._seriesMax = math.max(categoryAxis._seriesMax || 0, currentSeries.data.length);
|
|
currentSeries.data = (currentSeries.data || []).slice(range.min, range.max);
|
|
return currentSeries;
|
|
},
|
|
aggregateSeries: function (series, categoryAxis) {
|
|
var axisOptions = categoryAxis.options, dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE), categories = axisOptions.categories, srcCategories = axisOptions.srcCategories || categories, srcData = series.data, srcPoints = [], result = deepExtend({}, series), aggregatorSeries = deepExtend({}, series), dataItems = axisOptions.dataItems || [], i, category, categoryIx, data, aggregator, getFn = getField, outOfRangeMinIdx = util.MIN_NUM, outOfRangeMinCategory, outOfRangeMaxCategory, outOfRangeMaxIdx = util.MAX_NUM, outOfRangePoints = inArray(series.type, [
|
|
LINE,
|
|
VERTICAL_LINE,
|
|
AREA,
|
|
VERTICAL_AREA
|
|
]);
|
|
result.data = data = [];
|
|
if (dateAxis) {
|
|
getFn = getDateField;
|
|
}
|
|
for (i = 0; i < srcData.length; i++) {
|
|
if (series.categoryField) {
|
|
category = getFn(series.categoryField, srcData[i]);
|
|
} else {
|
|
category = srcCategories[i];
|
|
}
|
|
if (defined(category)) {
|
|
categoryIx = categoryAxis.categoryIndex(category);
|
|
if (0 <= categoryIx && categoryIx < categories.length) {
|
|
srcPoints[categoryIx] = srcPoints[categoryIx] || [];
|
|
srcPoints[categoryIx].push(i);
|
|
} else if (outOfRangePoints) {
|
|
if (categoryIx < 0) {
|
|
if (categoryIx == outOfRangeMinIdx) {
|
|
outOfRangeMinCategory.points.push(i);
|
|
} else if (categoryIx > outOfRangeMinIdx) {
|
|
outOfRangeMinIdx = categoryIx;
|
|
outOfRangeMinCategory = {
|
|
category: category,
|
|
points: [i]
|
|
};
|
|
}
|
|
} else if (categoryIx >= categories.length) {
|
|
if (categoryIx == outOfRangeMaxIdx) {
|
|
outOfRangeMaxCategory.points.push(i);
|
|
} else if (categoryIx < outOfRangeMaxIdx) {
|
|
outOfRangeMaxIdx = categoryIx;
|
|
outOfRangeMaxCategory = {
|
|
category: category,
|
|
points: [i]
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
aggregator = new SeriesAggregator(aggregatorSeries, SeriesBinder.current, DefaultAggregates.current);
|
|
for (i = 0; i < categories.length; i++) {
|
|
data[i] = aggregator.aggregatePoints(srcPoints[i], categories[i]);
|
|
if (srcPoints[i]) {
|
|
dataItems[i] = data[i];
|
|
}
|
|
}
|
|
if (outOfRangeMinCategory && data.length) {
|
|
result._outOfRangeMinPoint = {
|
|
item: aggregator.aggregatePoints(outOfRangeMinCategory.points, outOfRangeMinCategory.category),
|
|
categoryIx: outOfRangeMinIdx,
|
|
category: outOfRangeMinCategory.category
|
|
};
|
|
}
|
|
if (outOfRangeMaxCategory && data.length) {
|
|
result._outOfRangeMaxPoint = {
|
|
item: aggregator.aggregatePoints(outOfRangeMaxCategory.points, outOfRangeMaxCategory.category),
|
|
categoryIx: outOfRangeMaxIdx,
|
|
category: outOfRangeMaxCategory.category
|
|
};
|
|
}
|
|
categoryAxis.options.dataItems = dataItems;
|
|
return result;
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
var plotArea = this, series = chart.options.series, categoryAxis = plotArea.seriesCategoryAxis(series[0]), categories = categoryAxis.options.categories, categoriesToAdd = math.max(0, categoriesCount(series) - categories.length);
|
|
while (categoriesToAdd--) {
|
|
categories.push('');
|
|
}
|
|
plotArea.valueAxisRangeTracker.update(chart.valueAxisRanges);
|
|
PlotAreaBase.fn.appendChart.call(plotArea, chart, pane);
|
|
},
|
|
seriesPaneName: function (series) {
|
|
var plotArea = this, options = plotArea.options, axisName = series.axis, axisOptions = [].concat(options.valueAxis), axis = $.grep(axisOptions, function (a) {
|
|
return a.name === axisName;
|
|
})[0], panes = options.panes || [{}], defaultPaneName = (panes[0] || {}).name || 'default', paneName = (axis || {}).pane || defaultPaneName;
|
|
return paneName;
|
|
},
|
|
seriesCategoryAxis: function (series) {
|
|
var plotArea = this, axisName = series.categoryAxis, axis = axisName ? plotArea.namedCategoryAxes[axisName] : plotArea.categoryAxis;
|
|
if (!axis) {
|
|
throw new Error('Unable to locate category axis with name ' + axisName);
|
|
}
|
|
return axis;
|
|
},
|
|
stackableChartOptions: function (firstSeries, pane) {
|
|
var stack = firstSeries.stack, isStacked100 = stack && stack.type === '100%', clip;
|
|
if (defined(pane.options.clip)) {
|
|
clip = pane.options.clip;
|
|
} else if (isStacked100) {
|
|
clip = false;
|
|
}
|
|
return {
|
|
isStacked: stack,
|
|
isStacked100: isStacked100,
|
|
clip: clip
|
|
};
|
|
},
|
|
groupSeriesByCategoryAxis: function (series) {
|
|
var unique = {};
|
|
var categoryAxes = $.map(series, function (s) {
|
|
var name = s.categoryAxis || '$$default$$';
|
|
if (!unique.hasOwnProperty(name)) {
|
|
unique[name] = true;
|
|
return name;
|
|
}
|
|
});
|
|
function groupSeries(axis, axisIx) {
|
|
return $.grep(series, function (s) {
|
|
return axisIx === 0 && !s.categoryAxis || s.categoryAxis == axis;
|
|
});
|
|
}
|
|
var groups = [];
|
|
for (var axisIx = 0; axisIx < categoryAxes.length; axisIx++) {
|
|
var axis = categoryAxes[axisIx];
|
|
var axisSeries = groupSeries(axis, axisIx);
|
|
if (axisSeries.length === 0) {
|
|
continue;
|
|
}
|
|
groups.push(axisSeries);
|
|
}
|
|
return groups;
|
|
},
|
|
createBarChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], barChart = new BarChart(plotArea, extend({
|
|
series: series,
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
spacing: firstSeries.spacing
|
|
}, plotArea.stackableChartOptions(firstSeries, pane)));
|
|
plotArea.appendChart(barChart, pane);
|
|
},
|
|
createRangeBarChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], rangeColumnChart = new RangeBarChart(plotArea, {
|
|
series: series,
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
spacing: firstSeries.spacing
|
|
});
|
|
plotArea.appendChart(rangeColumnChart, pane);
|
|
},
|
|
createBulletChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], bulletChart = new BulletChart(plotArea, {
|
|
series: series,
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
spacing: firstSeries.spacing,
|
|
clip: pane.options.clip
|
|
});
|
|
plotArea.appendChart(bulletChart, pane);
|
|
},
|
|
createLineChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], lineChart = new LineChart(plotArea, extend({
|
|
invertAxes: plotArea.invertAxes,
|
|
series: series
|
|
}, plotArea.stackableChartOptions(firstSeries, pane)));
|
|
plotArea.appendChart(lineChart, pane);
|
|
},
|
|
createAreaChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], areaChart = new AreaChart(plotArea, extend({
|
|
invertAxes: plotArea.invertAxes,
|
|
series: series
|
|
}, plotArea.stackableChartOptions(firstSeries, pane)));
|
|
plotArea.appendChart(areaChart, pane);
|
|
},
|
|
createOHLCChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], chart = new OHLCChart(plotArea, {
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
series: series,
|
|
spacing: firstSeries.spacing,
|
|
clip: pane.options.clip
|
|
});
|
|
plotArea.appendChart(chart, pane);
|
|
},
|
|
createCandlestickChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], chart = new CandlestickChart(plotArea, {
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
series: series,
|
|
spacing: firstSeries.spacing,
|
|
clip: pane.options.clip
|
|
});
|
|
plotArea.appendChart(chart, pane);
|
|
},
|
|
createBoxPlotChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], chart = new BoxPlotChart(plotArea, {
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
series: series,
|
|
spacing: firstSeries.spacing,
|
|
clip: pane.options.clip
|
|
});
|
|
plotArea.appendChart(chart, pane);
|
|
},
|
|
createWaterfallChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, firstSeries = series[0], waterfallChart = new WaterfallChart(plotArea, {
|
|
series: series,
|
|
invertAxes: plotArea.invertAxes,
|
|
gap: firstSeries.gap,
|
|
spacing: firstSeries.spacing
|
|
});
|
|
plotArea.appendChart(waterfallChart, pane);
|
|
},
|
|
axisRequiresRounding: function (categoryAxisName, categoryAxisIndex) {
|
|
var plotArea = this, centeredSeries = filterSeriesByType(plotArea.series, EQUALLY_SPACED_SERIES), seriesIx, seriesAxis;
|
|
for (seriesIx = 0; seriesIx < plotArea.series.length; seriesIx++) {
|
|
var currentSeries = plotArea.series[seriesIx];
|
|
if (currentSeries.type === LINE || currentSeries.type === AREA) {
|
|
var line = currentSeries.line;
|
|
if (line && line.style === STEP) {
|
|
centeredSeries.push(currentSeries);
|
|
}
|
|
}
|
|
}
|
|
for (seriesIx = 0; seriesIx < centeredSeries.length; seriesIx++) {
|
|
seriesAxis = centeredSeries[seriesIx].categoryAxis || '';
|
|
if (seriesAxis === categoryAxisName || !seriesAxis && categoryAxisIndex === 0) {
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
aggregatedAxis: function (categoryAxisName, categoryAxisIndex) {
|
|
var plotArea = this, series = plotArea.series, seriesIx, seriesAxis;
|
|
for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
seriesAxis = series[seriesIx].categoryAxis || '';
|
|
if ((seriesAxis === categoryAxisName || !seriesAxis && categoryAxisIndex === 0) && series[seriesIx].categoryField) {
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
createCategoryAxesLabels: function () {
|
|
var axes = this.axes;
|
|
for (var i = 0; i < axes.length; i++) {
|
|
if (axes[i] instanceof CategoryAxis) {
|
|
axes[i].createLabels();
|
|
}
|
|
}
|
|
},
|
|
createCategoryAxes: function (panes) {
|
|
var plotArea = this, invertAxes = plotArea.invertAxes, definitions = [].concat(plotArea.options.categoryAxis), i, axisOptions, axisPane, categories, type, name, categoryAxis, axes = [], primaryAxis;
|
|
for (i = 0; i < definitions.length; i++) {
|
|
axisOptions = definitions[i];
|
|
axisPane = plotArea.findPane(axisOptions.pane);
|
|
if (inArray(axisPane, panes)) {
|
|
name = axisOptions.name;
|
|
categories = axisOptions.categories || [];
|
|
type = axisOptions.type || '';
|
|
axisOptions = deepExtend({
|
|
vertical: invertAxes,
|
|
axisCrossingValue: invertAxes ? MAX_VALUE : 0,
|
|
_deferLabels: true
|
|
}, axisOptions);
|
|
if (!defined(axisOptions.justified)) {
|
|
axisOptions.justified = plotArea.isJustified();
|
|
}
|
|
if (plotArea.axisRequiresRounding(name, i)) {
|
|
axisOptions.justified = false;
|
|
}
|
|
if (isDateAxis(axisOptions, categories[0])) {
|
|
categoryAxis = new DateCategoryAxis(axisOptions);
|
|
} else {
|
|
categoryAxis = new CategoryAxis(axisOptions);
|
|
}
|
|
if (name) {
|
|
if (plotArea.namedCategoryAxes[name]) {
|
|
throw new Error('Category axis with name ' + name + ' is already defined');
|
|
}
|
|
plotArea.namedCategoryAxes[name] = categoryAxis;
|
|
}
|
|
categoryAxis.axisIndex = i;
|
|
axes.push(categoryAxis);
|
|
plotArea.appendAxis(categoryAxis);
|
|
}
|
|
}
|
|
primaryAxis = plotArea.categoryAxis || axes[0];
|
|
plotArea.categoryAxis = primaryAxis;
|
|
if (invertAxes) {
|
|
plotArea.axisY = primaryAxis;
|
|
} else {
|
|
plotArea.axisX = primaryAxis;
|
|
}
|
|
},
|
|
isJustified: function () {
|
|
var plotArea = this, series = plotArea.series, i, currentSeries;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
if (!inArray(currentSeries.type, [
|
|
AREA,
|
|
VERTICAL_AREA
|
|
])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
createValueAxes: function (panes) {
|
|
var plotArea = this, tracker = plotArea.valueAxisRangeTracker, defaultRange = tracker.query(), definitions = [].concat(plotArea.options.valueAxis), invertAxes = plotArea.invertAxes, baseOptions = { vertical: !invertAxes }, axisOptions, axisPane, valueAxis, primaryAxis, axes = [], range, axisType, defaultAxisRange, name, i;
|
|
if (plotArea.stack100) {
|
|
baseOptions.roundToMajorUnit = false;
|
|
baseOptions.labels = { format: 'P0' };
|
|
}
|
|
for (i = 0; i < definitions.length; i++) {
|
|
axisOptions = definitions[i];
|
|
axisPane = plotArea.findPane(axisOptions.pane);
|
|
if (inArray(axisPane, panes)) {
|
|
name = axisOptions.name;
|
|
defaultAxisRange = equalsIgnoreCase(axisOptions.type, LOGARITHMIC) ? {
|
|
min: 0.1,
|
|
max: 1
|
|
} : {
|
|
min: 0,
|
|
max: 1
|
|
};
|
|
range = tracker.query(name) || defaultRange || defaultAxisRange;
|
|
if (i === 0 && range && defaultRange) {
|
|
range.min = math.min(range.min, defaultRange.min);
|
|
range.max = math.max(range.max, defaultRange.max);
|
|
}
|
|
if (equalsIgnoreCase(axisOptions.type, LOGARITHMIC)) {
|
|
axisType = LogarithmicAxis;
|
|
} else {
|
|
axisType = NumericAxis;
|
|
}
|
|
valueAxis = new axisType(range.min, range.max, deepExtend({}, baseOptions, axisOptions));
|
|
if (name) {
|
|
if (plotArea.namedValueAxes[name]) {
|
|
throw new Error('Value axis with name ' + name + ' is already defined');
|
|
}
|
|
plotArea.namedValueAxes[name] = valueAxis;
|
|
}
|
|
valueAxis.axisIndex = i;
|
|
axes.push(valueAxis);
|
|
plotArea.appendAxis(valueAxis);
|
|
}
|
|
}
|
|
primaryAxis = plotArea.valueAxis || axes[0];
|
|
plotArea.valueAxis = primaryAxis;
|
|
if (invertAxes) {
|
|
plotArea.axisX = primaryAxis;
|
|
} else {
|
|
plotArea.axisY = primaryAxis;
|
|
}
|
|
},
|
|
click: function (chart, e) {
|
|
var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), pane = plotArea.pointPane(point), allAxes, i, axis, categories = [], values = [];
|
|
if (!pane) {
|
|
return;
|
|
}
|
|
allAxes = pane.axes;
|
|
for (i = 0; i < allAxes.length; i++) {
|
|
axis = allAxes[i];
|
|
if (axis.getValue) {
|
|
appendIfNotNull(values, axis.getValue(point));
|
|
} else {
|
|
appendIfNotNull(categories, axis.getCategory(point));
|
|
}
|
|
}
|
|
if (categories.length === 0) {
|
|
appendIfNotNull(categories, plotArea.categoryAxis.getCategory(point));
|
|
}
|
|
if (categories.length > 0 && values.length > 0) {
|
|
chart.trigger(PLOT_AREA_CLICK, {
|
|
element: $(e.target),
|
|
originalEvent: e,
|
|
category: singleItemOrArray(categories),
|
|
value: singleItemOrArray(values)
|
|
});
|
|
}
|
|
},
|
|
pointPane: function (point) {
|
|
var plotArea = this, panes = plotArea.panes, currentPane, i;
|
|
for (i = 0; i < panes.length; i++) {
|
|
currentPane = panes[i];
|
|
if (currentPane.contentBox.containsPoint(point)) {
|
|
return currentPane;
|
|
}
|
|
}
|
|
},
|
|
updateAxisOptions: function (axis, options) {
|
|
var axesOptions = axis instanceof CategoryAxis ? [].concat(this.options.categoryAxis) : [].concat(this.options.valueAxis);
|
|
deepExtend(axesOptions[axis.axisIndex], options);
|
|
}
|
|
});
|
|
var AxisGroupRangeTracker = Class.extend({
|
|
init: function () {
|
|
var tracker = this;
|
|
tracker.axisRanges = {};
|
|
},
|
|
update: function (chartAxisRanges) {
|
|
var tracker = this, axisRanges = tracker.axisRanges, range, chartRange, axisName;
|
|
for (axisName in chartAxisRanges) {
|
|
range = axisRanges[axisName];
|
|
chartRange = chartAxisRanges[axisName];
|
|
axisRanges[axisName] = range = range || {
|
|
min: MAX_VALUE,
|
|
max: MIN_VALUE
|
|
};
|
|
range.min = math.min(range.min, chartRange.min);
|
|
range.max = math.max(range.max, chartRange.max);
|
|
}
|
|
},
|
|
reset: function (axisName) {
|
|
this.axisRanges[axisName] = undefined;
|
|
},
|
|
query: function (axisName) {
|
|
return this.axisRanges[axisName];
|
|
}
|
|
});
|
|
var XYPlotArea = PlotAreaBase.extend({
|
|
init: function (series, options) {
|
|
var plotArea = this;
|
|
plotArea.namedXAxes = {};
|
|
plotArea.namedYAxes = {};
|
|
plotArea.xAxisRangeTracker = new AxisGroupRangeTracker();
|
|
plotArea.yAxisRangeTracker = new AxisGroupRangeTracker();
|
|
PlotAreaBase.fn.init.call(plotArea, series, options);
|
|
},
|
|
options: {
|
|
xAxis: {},
|
|
yAxis: {}
|
|
},
|
|
render: function (panes) {
|
|
var plotArea = this, seriesByPane = plotArea.groupSeriesByPane(), i, pane, paneSeries, filteredSeries;
|
|
panes = panes || plotArea.panes;
|
|
for (i = 0; i < panes.length; i++) {
|
|
pane = panes[i];
|
|
paneSeries = seriesByPane[pane.options.name || 'default'] || [];
|
|
plotArea.addToLegend(paneSeries);
|
|
filteredSeries = plotArea.filterVisibleSeries(paneSeries);
|
|
if (!filteredSeries) {
|
|
continue;
|
|
}
|
|
plotArea.createScatterChart(filterSeriesByType(filteredSeries, SCATTER), pane);
|
|
plotArea.createScatterLineChart(filterSeriesByType(filteredSeries, SCATTER_LINE), pane);
|
|
plotArea.createBubbleChart(filterSeriesByType(filteredSeries, BUBBLE), pane);
|
|
}
|
|
plotArea.createAxes(panes);
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
var plotArea = this;
|
|
plotArea.xAxisRangeTracker.update(chart.xAxisRanges);
|
|
plotArea.yAxisRangeTracker.update(chart.yAxisRanges);
|
|
PlotAreaBase.fn.appendChart.call(plotArea, chart, pane);
|
|
},
|
|
removeAxis: function (axis) {
|
|
var plotArea = this, axisName = axis.options.name;
|
|
PlotAreaBase.fn.removeAxis.call(plotArea, axis);
|
|
if (axis.options.vertical) {
|
|
plotArea.yAxisRangeTracker.reset(axisName);
|
|
delete plotArea.namedYAxes[axisName];
|
|
} else {
|
|
plotArea.xAxisRangeTracker.reset(axisName);
|
|
delete plotArea.namedXAxes[axisName];
|
|
}
|
|
if (axis === plotArea.axisX) {
|
|
delete plotArea.axisX;
|
|
}
|
|
if (axis === plotArea.axisY) {
|
|
delete plotArea.axisY;
|
|
}
|
|
},
|
|
seriesPaneName: function (series) {
|
|
var plotArea = this, options = plotArea.options, xAxisName = series.xAxis, xAxisOptions = [].concat(options.xAxis), xAxis = $.grep(xAxisOptions, function (a) {
|
|
return a.name === xAxisName;
|
|
})[0], yAxisName = series.yAxis, yAxisOptions = [].concat(options.yAxis), yAxis = $.grep(yAxisOptions, function (a) {
|
|
return a.name === yAxisName;
|
|
})[0], panes = options.panes || [{}], defaultPaneName = panes[0].name || 'default', paneName = (xAxis || {}).pane || (yAxis || {}).pane || defaultPaneName;
|
|
return paneName;
|
|
},
|
|
createScatterChart: function (series, pane) {
|
|
var plotArea = this;
|
|
if (series.length > 0) {
|
|
plotArea.appendChart(new ScatterChart(plotArea, {
|
|
series: series,
|
|
clip: pane.options.clip
|
|
}), pane);
|
|
}
|
|
},
|
|
createScatterLineChart: function (series, pane) {
|
|
var plotArea = this;
|
|
if (series.length > 0) {
|
|
plotArea.appendChart(new ScatterLineChart(plotArea, {
|
|
series: series,
|
|
clip: pane.options.clip
|
|
}), pane);
|
|
}
|
|
},
|
|
createBubbleChart: function (series, pane) {
|
|
var plotArea = this;
|
|
if (series.length > 0) {
|
|
plotArea.appendChart(new BubbleChart(plotArea, {
|
|
series: series,
|
|
clip: pane.options.clip
|
|
}), pane);
|
|
}
|
|
},
|
|
createXYAxis: function (options, vertical, axisIndex) {
|
|
var plotArea = this, axisName = options.name, namedAxes = vertical ? plotArea.namedYAxes : plotArea.namedXAxes, tracker = vertical ? plotArea.yAxisRangeTracker : plotArea.xAxisRangeTracker, axisOptions = deepExtend({}, options, { vertical: vertical }), isLog = equalsIgnoreCase(axisOptions.type, LOGARITHMIC), defaultRange = tracker.query(), defaultAxisRange = isLog ? {
|
|
min: 0.1,
|
|
max: 1
|
|
} : {
|
|
min: 0,
|
|
max: 1
|
|
}, range = tracker.query(axisName) || defaultRange || defaultAxisRange, axis, axisType, seriesIx, series = plotArea.series, currentSeries, seriesAxisName, firstPointValue, typeSamples = [
|
|
axisOptions.min,
|
|
axisOptions.max
|
|
], inferredDate, i;
|
|
for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
seriesAxisName = currentSeries[vertical ? 'yAxis' : 'xAxis'];
|
|
if (seriesAxisName == axisOptions.name || axisIndex === 0 && !seriesAxisName) {
|
|
firstPointValue = SeriesBinder.current.bindPoint(currentSeries, 0).valueFields;
|
|
typeSamples.push(firstPointValue[vertical ? 'y' : 'x']);
|
|
break;
|
|
}
|
|
}
|
|
if (axisIndex === 0 && defaultRange) {
|
|
range.min = math.min(range.min, defaultRange.min);
|
|
range.max = math.max(range.max, defaultRange.max);
|
|
}
|
|
for (i = 0; i < typeSamples.length; i++) {
|
|
if (typeSamples[i] instanceof Date) {
|
|
inferredDate = true;
|
|
break;
|
|
}
|
|
}
|
|
if (equalsIgnoreCase(axisOptions.type, DATE) || !axisOptions.type && inferredDate) {
|
|
axisType = DateValueAxis;
|
|
} else if (isLog) {
|
|
axisType = LogarithmicAxis;
|
|
} else {
|
|
axisType = NumericAxis;
|
|
}
|
|
axis = new axisType(range.min, range.max, axisOptions);
|
|
if (axisName) {
|
|
if (namedAxes[axisName]) {
|
|
throw new Error((vertical ? 'Y' : 'X') + ' axis with name ' + axisName + ' is already defined');
|
|
}
|
|
namedAxes[axisName] = axis;
|
|
}
|
|
plotArea.appendAxis(axis);
|
|
return axis;
|
|
},
|
|
createAxes: function (panes) {
|
|
var plotArea = this, options = plotArea.options, axisPane, xAxesOptions = [].concat(options.xAxis), xAxes = [], yAxesOptions = [].concat(options.yAxis), yAxes = [];
|
|
each(xAxesOptions, function (i) {
|
|
axisPane = plotArea.findPane(this.pane);
|
|
if (inArray(axisPane, panes)) {
|
|
xAxes.push(plotArea.createXYAxis(this, false, i));
|
|
}
|
|
});
|
|
each(yAxesOptions, function (i) {
|
|
axisPane = plotArea.findPane(this.pane);
|
|
if (inArray(axisPane, panes)) {
|
|
yAxes.push(plotArea.createXYAxis(this, true, i));
|
|
}
|
|
});
|
|
plotArea.axisX = plotArea.axisX || xAxes[0];
|
|
plotArea.axisY = plotArea.axisY || yAxes[0];
|
|
},
|
|
click: function (chart, e) {
|
|
var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), allAxes = plotArea.axes, i, length = allAxes.length, axis, xValues = [], yValues = [], currentValue, values;
|
|
for (i = 0; i < length; i++) {
|
|
axis = allAxes[i];
|
|
values = axis.options.vertical ? yValues : xValues;
|
|
currentValue = axis.getValue(point);
|
|
if (currentValue !== null) {
|
|
values.push(currentValue);
|
|
}
|
|
}
|
|
if (xValues.length > 0 && yValues.length > 0) {
|
|
chart.trigger(PLOT_AREA_CLICK, {
|
|
element: $(e.target),
|
|
originalEvent: e,
|
|
x: singleItemOrArray(xValues),
|
|
y: singleItemOrArray(yValues)
|
|
});
|
|
}
|
|
},
|
|
updateAxisOptions: function (axis, options) {
|
|
var vertical = axis.options.vertical;
|
|
var axes = this.groupAxes(this.panes);
|
|
var index = indexOf(axis, vertical ? axes.y : axes.x);
|
|
var axisOptions = [].concat(vertical ? this.options.yAxis : this.options.xAxis)[index];
|
|
deepExtend(axisOptions, options);
|
|
}
|
|
});
|
|
var PiePlotArea = PlotAreaBase.extend({
|
|
render: function () {
|
|
var plotArea = this, series = plotArea.series;
|
|
plotArea.createPieChart(series);
|
|
},
|
|
createPieChart: function (series) {
|
|
var plotArea = this, firstSeries = series[0], pieChart = new PieChart(plotArea, {
|
|
series: series,
|
|
padding: firstSeries.padding,
|
|
startAngle: firstSeries.startAngle,
|
|
connectors: firstSeries.connectors,
|
|
legend: plotArea.options.legend
|
|
});
|
|
plotArea.appendChart(pieChart);
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
PlotAreaBase.fn.appendChart.call(this, chart, pane);
|
|
append(this.options.legend.items, chart.legendItems);
|
|
}
|
|
});
|
|
var DonutPlotArea = PiePlotArea.extend({
|
|
render: function () {
|
|
var plotArea = this, series = plotArea.series;
|
|
plotArea.createDonutChart(series);
|
|
},
|
|
createDonutChart: function (series) {
|
|
var plotArea = this, firstSeries = series[0], donutChart = new DonutChart(plotArea, {
|
|
series: series,
|
|
padding: firstSeries.padding,
|
|
connectors: firstSeries.connectors,
|
|
legend: plotArea.options.legend
|
|
});
|
|
plotArea.appendChart(donutChart);
|
|
}
|
|
});
|
|
var PieAnimation = draw.Animation.extend({
|
|
options: {
|
|
easing: 'easeOutElastic',
|
|
duration: INITIAL_ANIMATION_DURATION
|
|
},
|
|
setup: function () {
|
|
this.element.transform(geom.transform().scale(START_SCALE, START_SCALE, this.options.center));
|
|
},
|
|
step: function (pos) {
|
|
this.element.transform(geom.transform().scale(pos, pos, this.options.center));
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(PIE, PieAnimation);
|
|
var BubbleAnimation = draw.Animation.extend({
|
|
options: { easing: 'easeOutElastic' },
|
|
setup: function () {
|
|
var center = this.center = this.element.bbox().center();
|
|
this.element.transform(geom.transform().scale(START_SCALE, START_SCALE, center));
|
|
},
|
|
step: function (pos) {
|
|
this.element.transform(geom.transform().scale(pos, pos, this.center));
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(BUBBLE, BubbleAnimation);
|
|
var Highlight = Class.extend({
|
|
init: function () {
|
|
this._points = [];
|
|
},
|
|
destroy: function () {
|
|
this._points = [];
|
|
},
|
|
show: function (points) {
|
|
points = [].concat(points);
|
|
this.hide();
|
|
for (var i = 0; i < points.length; i++) {
|
|
var point = points[i];
|
|
if (point && point.toggleHighlight && point.hasHighlight()) {
|
|
this.togglePointHighlight(point, true);
|
|
this._points.push(point);
|
|
}
|
|
}
|
|
},
|
|
togglePointHighlight: function (point, show) {
|
|
var toggleHandler = (point.options.highlight || {}).toggle;
|
|
if (toggleHandler) {
|
|
var eventArgs = {
|
|
category: point.category,
|
|
series: point.series,
|
|
dataItem: point.dataItem,
|
|
value: point.value,
|
|
preventDefault: preventDefault,
|
|
visual: point.highlightVisual(),
|
|
show: show
|
|
};
|
|
toggleHandler(eventArgs);
|
|
if (!eventArgs._defaultPrevented) {
|
|
point.toggleHighlight(show);
|
|
}
|
|
} else {
|
|
point.toggleHighlight(show);
|
|
}
|
|
},
|
|
hide: function () {
|
|
var points = this._points;
|
|
while (points.length) {
|
|
this.togglePointHighlight(points.pop(), false);
|
|
}
|
|
},
|
|
isHighlighted: function (element) {
|
|
var points = this._points;
|
|
for (var i = 0; i < points.length; i++) {
|
|
var point = points[i];
|
|
if (element == point) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var BaseTooltip = Observable.extend({
|
|
init: function (chartElement, options) {
|
|
var tooltip = this;
|
|
Observable.fn.init.call(tooltip);
|
|
tooltip.options = deepExtend({}, tooltip.options, options);
|
|
tooltip.chartElement = chartElement;
|
|
tooltip.template = BaseTooltip.template;
|
|
if (!tooltip.template) {
|
|
tooltip.template = BaseTooltip.template = renderTemplate('<div class=\'' + CSS_PREFIX + 'tooltip ' + CSS_PREFIX + 'chart-tooltip\' ' + 'style=\'display:none; position: absolute; font: #= d.font #;' + 'border: #= d.border.width #px solid;' + 'opacity: #= d.opacity #; filter: alpha(opacity=#= d.opacity * 100 #);\'>' + '</div>');
|
|
}
|
|
var padding = getSpacing(tooltip.options.padding || {}, 'auto');
|
|
tooltip.element = $(tooltip.template(tooltip.options)).css({
|
|
'padding-top': padding.top,
|
|
'padding-right': padding.right,
|
|
'padding-bottom': padding.bottom,
|
|
'padding-left': padding.left
|
|
});
|
|
tooltip.move = proxy(tooltip.move, tooltip);
|
|
tooltip._mouseleave = proxy(tooltip._mouseleave, tooltip);
|
|
var mobileScrollerSelector = kendo.format('[{0}=\'content\'],[{0}=\'scroller\']', kendo.attr('role'));
|
|
tooltip._mobileScroller = chartElement.closest(mobileScrollerSelector).data('kendoMobileScroller');
|
|
},
|
|
destroy: function () {
|
|
this._clearShowTimeout();
|
|
if (this.element) {
|
|
this.element.off(MOUSELEAVE_NS).remove();
|
|
this.element = null;
|
|
}
|
|
},
|
|
options: {
|
|
border: { width: 1 },
|
|
opacity: 1,
|
|
animation: { duration: TOOLTIP_ANIMATION_DURATION }
|
|
},
|
|
move: function () {
|
|
var tooltip = this, options = tooltip.options, element = tooltip.element, offset;
|
|
if (!tooltip.anchor || !tooltip.element) {
|
|
return;
|
|
}
|
|
offset = tooltip._offset();
|
|
if (!tooltip.visible) {
|
|
element.css({
|
|
top: offset.top,
|
|
left: offset.left
|
|
});
|
|
}
|
|
tooltip.visible = true;
|
|
tooltip._ensureElement(document.body);
|
|
element.stop(true, true).show().animate({
|
|
left: offset.left,
|
|
top: offset.top
|
|
}, options.animation.duration);
|
|
},
|
|
_clearShowTimeout: function () {
|
|
if (this.showTimeout) {
|
|
clearTimeout(this.showTimeout);
|
|
this.showTimeout = null;
|
|
}
|
|
},
|
|
_padding: function () {
|
|
if (!this._chartPadding) {
|
|
var chartElement = this.chartElement;
|
|
this._chartPadding = {
|
|
top: parseInt(chartElement.css('paddingTop'), 10),
|
|
left: parseInt(chartElement.css('paddingLeft'), 10)
|
|
};
|
|
}
|
|
return this._chartPadding;
|
|
},
|
|
_offset: function () {
|
|
var tooltip = this, size = tooltip._measure(), anchor = tooltip.anchor, chartPadding = tooltip._padding(), chartOffset = tooltip.chartElement.offset(), top = round(anchor.y + chartPadding.top + chartOffset.top), left = round(anchor.x + chartPadding.left + chartOffset.left), zoomLevel = kendo.support.zoomLevel(), viewport = $(window), scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0, scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || 0, movable = (this._mobileScroller || {}).movable;
|
|
if (!movable || movable.scale === 1) {
|
|
top += tooltip._fit(top - scrollTop, size.height, viewport.outerHeight() / zoomLevel);
|
|
left += tooltip._fit(left - scrollLeft, size.width, viewport.outerWidth() / zoomLevel);
|
|
} else {
|
|
var transform = geom.transform().scale(movable.scale, movable.scale, [
|
|
movable.x,
|
|
movable.y
|
|
]);
|
|
var point = new geom.Point(left, top).transform(transform);
|
|
left = point.x;
|
|
top = point.y;
|
|
}
|
|
return {
|
|
top: top,
|
|
left: left
|
|
};
|
|
},
|
|
setStyle: function (options, point) {
|
|
var background = options.background;
|
|
var border = options.border.color;
|
|
if (point) {
|
|
var pointColor = point.color || point.options.color;
|
|
background = valueOrDefault(background, pointColor);
|
|
border = valueOrDefault(border, pointColor);
|
|
}
|
|
if (!defined(options.color)) {
|
|
var brightness = new Color(background).percBrightness();
|
|
this.element.toggleClass(CSS_PREFIX + TOOLTIP_INVERSE, brightness > 180);
|
|
}
|
|
this.element.css({
|
|
backgroundColor: background,
|
|
borderColor: border,
|
|
font: options.font,
|
|
color: options.color,
|
|
opacity: options.opacity,
|
|
borderWidth: options.border.width
|
|
});
|
|
},
|
|
show: function () {
|
|
this._clearShowTimeout();
|
|
this.showTimeout = setTimeout(this.move, TOOLTIP_SHOW_DELAY);
|
|
},
|
|
hide: function () {
|
|
var tooltip = this;
|
|
clearTimeout(tooltip.showTimeout);
|
|
tooltip._hideElement();
|
|
if (tooltip.visible) {
|
|
tooltip.point = null;
|
|
tooltip.visible = false;
|
|
tooltip.index = null;
|
|
}
|
|
},
|
|
_measure: function () {
|
|
this._ensureElement();
|
|
var size = {
|
|
width: this.element.outerWidth(),
|
|
height: this.element.outerHeight()
|
|
};
|
|
return size;
|
|
},
|
|
_ensureElement: function () {
|
|
if (this.element) {
|
|
this.element.appendTo(document.body).on(MOUSELEAVE_NS, this._mouseleave);
|
|
}
|
|
},
|
|
_mouseleave: function (e) {
|
|
var target = e.relatedTarget;
|
|
var chart = this.chartElement[0];
|
|
if (target && target !== chart && !$.contains(chart, target)) {
|
|
this.trigger(LEAVE);
|
|
this.hide();
|
|
}
|
|
},
|
|
_hideElement: function () {
|
|
var tooltip = this;
|
|
var element = this.element;
|
|
if (element) {
|
|
element.fadeOut({
|
|
always: function () {
|
|
if (!tooltip.visible) {
|
|
element.off(MOUSELEAVE_NS).remove();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
_pointContent: function (point) {
|
|
var tooltip = this, options = deepExtend({}, tooltip.options, point.options.tooltip), content, tooltipTemplate;
|
|
if (defined(point.value)) {
|
|
content = point.value.toString();
|
|
}
|
|
if (options.template) {
|
|
tooltipTemplate = template(options.template);
|
|
content = tooltipTemplate({
|
|
value: point.value,
|
|
category: point.category,
|
|
series: point.series,
|
|
dataItem: point.dataItem,
|
|
percentage: point.percentage,
|
|
runningTotal: point.runningTotal,
|
|
total: point.total,
|
|
low: point.low,
|
|
high: point.high,
|
|
xLow: point.xLow,
|
|
xHigh: point.xHigh,
|
|
yLow: point.yLow,
|
|
yHigh: point.yHigh
|
|
});
|
|
} else if (options.format) {
|
|
content = point.formatValue(options.format);
|
|
}
|
|
return content;
|
|
},
|
|
_pointAnchor: function (point) {
|
|
var size = this._measure();
|
|
return point.tooltipAnchor(size.width, size.height);
|
|
},
|
|
_fit: function (offset, size, viewPortSize) {
|
|
var output = 0;
|
|
if (offset + size > viewPortSize) {
|
|
output = viewPortSize - (offset + size);
|
|
}
|
|
if (offset < 0) {
|
|
output = -offset;
|
|
}
|
|
return output;
|
|
}
|
|
});
|
|
var Tooltip = BaseTooltip.extend({
|
|
show: function (point) {
|
|
var tooltip = this, options = deepExtend({}, tooltip.options, point.options.tooltip);
|
|
if (!point || !point.tooltipAnchor || !tooltip.element) {
|
|
return;
|
|
}
|
|
tooltip.element.html(tooltip._pointContent(point));
|
|
tooltip.anchor = tooltip._pointAnchor(point);
|
|
if (tooltip.anchor) {
|
|
tooltip.setStyle(options, point);
|
|
BaseTooltip.fn.show.call(tooltip, point);
|
|
} else {
|
|
tooltip.hide();
|
|
}
|
|
}
|
|
});
|
|
var SharedTooltip = BaseTooltip.extend({
|
|
init: function (element, plotArea, options) {
|
|
var tooltip = this;
|
|
BaseTooltip.fn.init.call(tooltip, element, options);
|
|
tooltip.plotArea = plotArea;
|
|
},
|
|
options: {
|
|
sharedTemplate: '<table>' + '<th colspan=\'2\'>#= categoryText #</th>' + '# for(var i = 0; i < points.length; i++) { #' + '# var point = points[i]; #' + '<tr>' + '# if(point.series.name) { # ' + '<td> #= point.series.name #:</td>' + '# } #' + '<td>#= content(point) #</td>' + '</tr>' + '# } #' + '</table>',
|
|
categoryFormat: '{0:d}'
|
|
},
|
|
showAt: function (points, coords) {
|
|
var tooltip = this, options = tooltip.options, plotArea = tooltip.plotArea, axis = plotArea.categoryAxis, index = axis.pointCategoryIndex(coords), category = axis.getCategory(coords), slot = axis.getSlot(index), content;
|
|
points = $.grep(points, function (p) {
|
|
var tooltip = p.series.tooltip, excluded = tooltip && tooltip.visible === false;
|
|
return !excluded;
|
|
});
|
|
if (points.length > 0) {
|
|
content = tooltip._content(points, category);
|
|
tooltip.element.html(content);
|
|
tooltip.anchor = tooltip._slotAnchor(coords, slot);
|
|
tooltip.setStyle(options, points[0]);
|
|
BaseTooltip.fn.show.call(tooltip);
|
|
}
|
|
},
|
|
_slotAnchor: function (point, slot) {
|
|
var tooltip = this, plotArea = tooltip.plotArea, axis = plotArea.categoryAxis, anchor, size = this._measure(), hCenter = point.y - size.height / 2;
|
|
if (axis.options.vertical) {
|
|
anchor = Point2D(point.x, hCenter);
|
|
} else {
|
|
anchor = Point2D(slot.center().x, hCenter);
|
|
}
|
|
return anchor;
|
|
},
|
|
_content: function (points, category) {
|
|
var tooltip = this, template, content;
|
|
template = kendo.template(tooltip.options.sharedTemplate);
|
|
content = template({
|
|
points: points,
|
|
category: category,
|
|
categoryText: autoFormat(tooltip.options.categoryFormat, category),
|
|
content: tooltip._pointContent
|
|
});
|
|
return content;
|
|
}
|
|
});
|
|
var Crosshair = ChartElement.extend({
|
|
init: function (axis, options) {
|
|
ChartElement.fn.init.call(this, options);
|
|
this.axis = axis;
|
|
this.stickyMode = axis instanceof CategoryAxis;
|
|
},
|
|
options: {
|
|
color: BLACK,
|
|
width: 1,
|
|
zIndex: -1,
|
|
tooltip: { visible: false }
|
|
},
|
|
showAt: function (point) {
|
|
this.point = point;
|
|
this.moveLine();
|
|
this.line.visible(true);
|
|
var tooltipOptions = this.options.tooltip;
|
|
if (tooltipOptions.visible) {
|
|
if (!this.tooltip) {
|
|
this.tooltip = new CrosshairTooltip(this, deepExtend({}, tooltipOptions, { stickyMode: this.stickyMode }));
|
|
}
|
|
this.tooltip.showAt(point);
|
|
}
|
|
},
|
|
hide: function () {
|
|
this.line.visible(false);
|
|
if (this.tooltip) {
|
|
this.tooltip.hide();
|
|
}
|
|
},
|
|
moveLine: function () {
|
|
var crosshair = this, axis = crosshair.axis, vertical = axis.options.vertical, box = crosshair.getBox(), point = crosshair.point, dim = vertical ? Y : X, slot, lineStart, lineEnd;
|
|
lineStart = new geom.Point(box.x1, box.y1);
|
|
if (vertical) {
|
|
lineEnd = new geom.Point(box.x2, box.y1);
|
|
} else {
|
|
lineEnd = new geom.Point(box.x1, box.y2);
|
|
}
|
|
if (point) {
|
|
if (crosshair.stickyMode) {
|
|
slot = axis.getSlot(axis.pointCategoryIndex(point));
|
|
lineStart[dim] = lineEnd[dim] = slot.center()[dim];
|
|
} else {
|
|
lineStart[dim] = lineEnd[dim] = point[dim];
|
|
}
|
|
}
|
|
crosshair.box = box;
|
|
this.line.moveTo(lineStart).lineTo(lineEnd);
|
|
},
|
|
getBox: function () {
|
|
var crosshair = this, axis = crosshair.axis, axes = axis.pane.axes, length = axes.length, vertical = axis.options.vertical, box = axis.lineBox().clone(), dim = vertical ? X : Y, axisLineBox, currentAxis, i;
|
|
for (i = 0; i < length; i++) {
|
|
currentAxis = axes[i];
|
|
if (currentAxis.options.vertical != vertical) {
|
|
if (!axisLineBox) {
|
|
axisLineBox = currentAxis.lineBox().clone();
|
|
} else {
|
|
axisLineBox.wrap(currentAxis.lineBox());
|
|
}
|
|
}
|
|
}
|
|
box[dim + 1] = axisLineBox[dim + 1];
|
|
box[dim + 2] = axisLineBox[dim + 2];
|
|
return box;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var options = this.options;
|
|
this.line = new draw.Path({
|
|
stroke: {
|
|
color: options.color,
|
|
width: options.width,
|
|
opacity: options.opacity,
|
|
dashType: options.dashType
|
|
},
|
|
visible: false
|
|
});
|
|
this.moveLine();
|
|
this.visual.append(this.line);
|
|
},
|
|
destroy: function () {
|
|
var crosshair = this;
|
|
if (crosshair.tooltip) {
|
|
crosshair.tooltip.destroy();
|
|
}
|
|
ChartElement.fn.destroy.call(crosshair);
|
|
}
|
|
});
|
|
var CrosshairTooltip = BaseTooltip.extend({
|
|
init: function (crosshair, options) {
|
|
var tooltip = this, chartElement = crosshair.axis.getRoot().chart.element;
|
|
tooltip.crosshair = crosshair;
|
|
BaseTooltip.fn.init.call(tooltip, chartElement, deepExtend({}, tooltip.options, { background: crosshair.axis.plotArea.options.seriesColors[0] }, options));
|
|
tooltip.setStyle(tooltip.options);
|
|
},
|
|
options: { padding: 10 },
|
|
showAt: function (point) {
|
|
var tooltip = this, element = tooltip.element;
|
|
if (element) {
|
|
tooltip.point = point;
|
|
tooltip.element.html(tooltip.content(point));
|
|
tooltip.anchor = tooltip.getAnchor();
|
|
tooltip.move();
|
|
}
|
|
},
|
|
move: function () {
|
|
var tooltip = this, element = tooltip.element, offset = tooltip._offset();
|
|
tooltip._ensureElement();
|
|
element.css({
|
|
top: offset.top,
|
|
left: offset.left
|
|
}).show();
|
|
},
|
|
content: function (point) {
|
|
var tooltip = this, options = tooltip.options, axis = tooltip.crosshair.axis, axisOptions = axis.options, content, value, tooltipTemplate;
|
|
value = content = axis[options.stickyMode ? 'getCategory' : 'getValue'](point);
|
|
if (options.template) {
|
|
tooltipTemplate = template(options.template);
|
|
content = tooltipTemplate({ value: value });
|
|
} else if (options.format) {
|
|
content = autoFormat(options.format, value);
|
|
} else {
|
|
if (axisOptions.type === DATE) {
|
|
content = autoFormat(axisOptions.labels.dateFormats[axisOptions.baseUnit], value);
|
|
}
|
|
}
|
|
return content;
|
|
},
|
|
getAnchor: function () {
|
|
var tooltip = this, options = tooltip.options, position = options.position, crosshair = this.crosshair, vertical = !crosshair.axis.options.vertical, lineBox = crosshair.line.bbox(), size = this._measure(), halfWidth = size.width / 2, halfHeight = size.height / 2, padding = options.padding, anchor;
|
|
if (vertical) {
|
|
if (position === BOTTOM) {
|
|
anchor = lineBox.bottomLeft().translate(-halfWidth, padding);
|
|
} else {
|
|
anchor = lineBox.topLeft().translate(-halfWidth, -size.height - padding);
|
|
}
|
|
} else {
|
|
if (position === LEFT) {
|
|
anchor = lineBox.topLeft().translate(-size.width - padding, -halfHeight);
|
|
} else {
|
|
anchor = lineBox.topRight().translate(padding, -halfHeight);
|
|
}
|
|
}
|
|
return anchor;
|
|
},
|
|
hide: function () {
|
|
this.element.hide();
|
|
this.point = null;
|
|
},
|
|
destroy: function () {
|
|
BaseTooltip.fn.destroy.call(this);
|
|
this.point = null;
|
|
}
|
|
});
|
|
var Aggregates = {
|
|
min: function (values) {
|
|
var min = MAX_VALUE, length = values.length, i, n;
|
|
for (i = 0; i < length; i++) {
|
|
n = values[i];
|
|
if (isNumber(n)) {
|
|
min = math.min(min, n);
|
|
}
|
|
}
|
|
return min === MAX_VALUE ? values[0] : min;
|
|
},
|
|
max: function (values) {
|
|
var max = MIN_VALUE, length = values.length, i, n;
|
|
for (i = 0; i < length; i++) {
|
|
n = values[i];
|
|
if (isNumber(n)) {
|
|
max = math.max(max, n);
|
|
}
|
|
}
|
|
return max === MIN_VALUE ? values[0] : max;
|
|
},
|
|
sum: function (values) {
|
|
var length = values.length, sum = 0, i, n;
|
|
for (i = 0; i < length; i++) {
|
|
n = values[i];
|
|
if (isNumber(n)) {
|
|
sum += n;
|
|
}
|
|
}
|
|
return sum;
|
|
},
|
|
sumOrNull: function (values) {
|
|
var result = null;
|
|
if (countNumbers(values)) {
|
|
result = Aggregates.sum(values);
|
|
}
|
|
return result;
|
|
},
|
|
count: function (values) {
|
|
var length = values.length, count = 0, i, val;
|
|
for (i = 0; i < length; i++) {
|
|
val = values[i];
|
|
if (val !== null && defined(val)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
},
|
|
avg: function (values) {
|
|
var result = values[0], count = countNumbers(values);
|
|
if (count > 0) {
|
|
result = Aggregates.sum(values) / count;
|
|
}
|
|
return result;
|
|
},
|
|
first: function (values) {
|
|
var length = values.length, i, val;
|
|
for (i = 0; i < length; i++) {
|
|
val = values[i];
|
|
if (val !== null && defined(val)) {
|
|
return val;
|
|
}
|
|
}
|
|
return values[0];
|
|
}
|
|
};
|
|
function DefaultAggregates() {
|
|
this._defaults = {};
|
|
}
|
|
DefaultAggregates.prototype = {
|
|
register: function (seriesTypes, aggregates) {
|
|
for (var i = 0; i < seriesTypes.length; i++) {
|
|
this._defaults[seriesTypes[i]] = aggregates;
|
|
}
|
|
},
|
|
query: function (seriesType) {
|
|
return this._defaults[seriesType];
|
|
}
|
|
};
|
|
DefaultAggregates.current = new DefaultAggregates();
|
|
var Selection = Observable.extend({
|
|
init: function (chart, categoryAxis, options) {
|
|
var that = this, chartElement = chart.element, categoryAxisLineBox = categoryAxis.lineBox(), valueAxis = that.getValueAxis(categoryAxis), valueAxisLineBox = valueAxis.lineBox(), selectorPrefix = '.' + CSS_PREFIX, wrapper, padding;
|
|
Observable.fn.init.call(that);
|
|
that.options = deepExtend({}, that.options, options);
|
|
options = that.options;
|
|
that.chart = chart;
|
|
that.chartElement = chartElement;
|
|
that.categoryAxis = categoryAxis;
|
|
that._dateAxis = that.categoryAxis instanceof DateCategoryAxis;
|
|
that.valueAxis = valueAxis;
|
|
if (that._dateAxis) {
|
|
deepExtend(options, {
|
|
min: toDate(options.min),
|
|
max: toDate(options.max),
|
|
from: toDate(options.from),
|
|
to: toDate(options.to)
|
|
});
|
|
}
|
|
that.template = Selection.template;
|
|
if (!that.template) {
|
|
that.template = Selection.template = renderTemplate('<div class=\'' + CSS_PREFIX + 'selector\' ' + 'style=\'width: #= d.width #px; height: #= d.height #px;' + ' top: #= d.offset.top #px; left: #= d.offset.left #px;\'>' + '<div class=\'' + CSS_PREFIX + 'mask\'></div>' + '<div class=\'' + CSS_PREFIX + 'mask\'></div>' + '<div class=\'' + CSS_PREFIX + 'selection\'>' + '<div class=\'' + CSS_PREFIX + 'selection-bg\'></div>' + '<div class=\'' + CSS_PREFIX + 'handle ' + CSS_PREFIX + 'leftHandle\'><div></div></div>' + '<div class=\'' + CSS_PREFIX + 'handle ' + CSS_PREFIX + 'rightHandle\'><div></div></div>' + '</div></div>');
|
|
}
|
|
padding = {
|
|
left: parseInt(chartElement.css('paddingLeft'), 10),
|
|
right: parseInt(chartElement.css('paddingTop'), 10)
|
|
};
|
|
that.options = deepExtend({}, {
|
|
width: categoryAxisLineBox.width(),
|
|
height: valueAxisLineBox.height(),
|
|
padding: padding,
|
|
offset: {
|
|
left: valueAxisLineBox.x2 + padding.left,
|
|
top: valueAxisLineBox.y1 + padding.right
|
|
},
|
|
from: options.min,
|
|
to: options.max
|
|
}, options);
|
|
if (that.options.visible) {
|
|
that.wrapper = wrapper = $(that.template(that.options)).appendTo(chartElement);
|
|
that.selection = wrapper.find(selectorPrefix + 'selection');
|
|
that.leftMask = wrapper.find(selectorPrefix + 'mask').first();
|
|
that.rightMask = wrapper.find(selectorPrefix + 'mask').last();
|
|
that.leftHandle = wrapper.find(selectorPrefix + 'leftHandle');
|
|
that.rightHandle = wrapper.find(selectorPrefix + 'rightHandle');
|
|
that.options.selection = {
|
|
border: {
|
|
left: parseFloat(that.selection.css('border-left-width'), 10),
|
|
right: parseFloat(that.selection.css('border-right-width'), 10)
|
|
}
|
|
};
|
|
that.leftHandle.css('top', (that.selection.height() - that.leftHandle.height()) / 2);
|
|
that.rightHandle.css('top', (that.selection.height() - that.rightHandle.height()) / 2);
|
|
that.set(that._index(options.from), that._index(options.to));
|
|
that.bind(that.events, that.options);
|
|
that.wrapper[0].style.cssText = that.wrapper[0].style.cssText;
|
|
that.wrapper.on(MOUSEWHEEL_NS, proxy(that._mousewheel, that));
|
|
if (kendo.UserEvents) {
|
|
that.userEvents = new kendo.UserEvents(that.wrapper, {
|
|
global: true,
|
|
stopPropagation: true,
|
|
multiTouch: true,
|
|
fastTap: true,
|
|
start: proxy(that._start, that),
|
|
move: proxy(that._move, that),
|
|
end: proxy(that._end, that),
|
|
tap: proxy(that._tap, that),
|
|
gesturestart: proxy(that._gesturechange, that),
|
|
gesturechange: proxy(that._gesturechange, that)
|
|
});
|
|
} else {
|
|
that.leftHandle.add(that.rightHandle).removeClass(CSS_PREFIX + 'handle');
|
|
}
|
|
}
|
|
},
|
|
events: [
|
|
SELECT_START,
|
|
SELECT,
|
|
SELECT_END
|
|
],
|
|
options: {
|
|
visible: true,
|
|
mousewheel: { zoom: BOTH },
|
|
min: MIN_VALUE,
|
|
max: MAX_VALUE
|
|
},
|
|
destroy: function () {
|
|
var that = this, userEvents = that.userEvents;
|
|
if (userEvents) {
|
|
userEvents.destroy();
|
|
}
|
|
clearTimeout(that._mwTimeout);
|
|
that._state = null;
|
|
that.wrapper.remove();
|
|
},
|
|
_rangeEventArgs: function (range) {
|
|
var that = this;
|
|
return {
|
|
axis: that.categoryAxis.options,
|
|
from: that._value(range.from),
|
|
to: that._value(range.to)
|
|
};
|
|
},
|
|
_start: function (e) {
|
|
var that = this, options = that.options, target = $(e.event.target), args;
|
|
if (that._state || !target) {
|
|
return;
|
|
}
|
|
that.chart._unsetActivePoint();
|
|
that._state = {
|
|
moveTarget: target.parents('.k-handle').add(target).first(),
|
|
startLocation: e.x ? e.x.location : 0,
|
|
range: {
|
|
from: that._index(options.from),
|
|
to: that._index(options.to)
|
|
}
|
|
};
|
|
args = that._rangeEventArgs({
|
|
from: that._index(options.from),
|
|
to: that._index(options.to)
|
|
});
|
|
if (that.trigger(SELECT_START, args)) {
|
|
that.userEvents.cancel();
|
|
that._state = null;
|
|
}
|
|
},
|
|
_move: function (e) {
|
|
if (!this._state) {
|
|
return;
|
|
}
|
|
var that = this, state = that._state, options = that.options, categories = that.categoryAxis.options.categories, from = that._index(options.from), to = that._index(options.to), min = that._index(options.min), max = that._index(options.max), delta = state.startLocation - e.x.location, range = state.range, oldRange = {
|
|
from: range.from,
|
|
to: range.to
|
|
}, span = range.to - range.from, target = state.moveTarget, scale = that.wrapper.width() / (categories.length - 1), offset = math.round(delta / scale);
|
|
if (!target) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
if (target.is('.k-selection, .k-selection-bg')) {
|
|
range.from = math.min(math.max(min, from - offset), max - span);
|
|
range.to = math.min(range.from + span, max);
|
|
} else if (target.is('.k-leftHandle')) {
|
|
range.from = math.min(math.max(min, from - offset), max - 1);
|
|
range.to = math.max(range.from + 1, range.to);
|
|
} else if (target.is('.k-rightHandle')) {
|
|
range.to = math.min(math.max(min + 1, to - offset), max);
|
|
range.from = math.min(range.to - 1, range.from);
|
|
}
|
|
if (range.from !== oldRange.from || range.to !== oldRange.to) {
|
|
that.move(range.from, range.to);
|
|
that.trigger(SELECT, that._rangeEventArgs(range));
|
|
}
|
|
},
|
|
_end: function () {
|
|
var that = this, range = that._state.range;
|
|
delete that._state;
|
|
that.set(range.from, range.to);
|
|
that.trigger(SELECT_END, that._rangeEventArgs(range));
|
|
},
|
|
_gesturechange: function (e) {
|
|
if (!this._state) {
|
|
return;
|
|
}
|
|
var that = this, chart = that.chart, state = that._state, options = that.options, categoryAxis = that.categoryAxis, range = state.range, p0 = chart._toModelCoordinates(e.touches[0].x.location).x, p1 = chart._toModelCoordinates(e.touches[1].x.location).x, left = math.min(p0, p1), right = math.max(p0, p1);
|
|
e.preventDefault();
|
|
state.moveTarget = null;
|
|
range.from = categoryAxis.pointCategoryIndex(new dataviz.Point2D(left)) || options.min;
|
|
range.to = categoryAxis.pointCategoryIndex(new dataviz.Point2D(right)) || options.max;
|
|
that.move(range.from, range.to);
|
|
},
|
|
_tap: function (e) {
|
|
var that = this, options = that.options, coords = that.chart._eventCoordinates(e), categoryAxis = that.categoryAxis, categoryIx = categoryAxis.pointCategoryIndex(new dataviz.Point2D(coords.x, categoryAxis.box.y1)), from = that._index(options.from), to = that._index(options.to), min = that._index(options.min), max = that._index(options.max), span = to - from, mid = from + span / 2, offset = math.round(mid - categoryIx), range = {}, rightClick = e.event.which === 3;
|
|
if (that._state || rightClick) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
that.chart._unsetActivePoint();
|
|
if (!categoryAxis.options.justified) {
|
|
offset--;
|
|
}
|
|
range.from = math.min(math.max(min, from - offset), max - span);
|
|
range.to = math.min(range.from + span, max);
|
|
that._start(e);
|
|
if (that._state) {
|
|
that._state.range = range;
|
|
that.trigger(SELECT, that._rangeEventArgs(range));
|
|
that._end();
|
|
}
|
|
},
|
|
_mousewheel: function (e) {
|
|
var that = this, options = that.options, delta = mwDelta(e);
|
|
that._start({ event: { target: that.selection } });
|
|
if (that._state) {
|
|
var range = that._state.range;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (math.abs(delta) > 1) {
|
|
delta *= ZOOM_ACCELERATION;
|
|
}
|
|
if (options.mousewheel.reverse) {
|
|
delta *= -1;
|
|
}
|
|
if (that.expand(delta)) {
|
|
that.trigger(SELECT, {
|
|
axis: that.categoryAxis.options,
|
|
delta: delta,
|
|
originalEvent: e,
|
|
from: that._value(range.from),
|
|
to: that._value(range.to)
|
|
});
|
|
}
|
|
if (that._mwTimeout) {
|
|
clearTimeout(that._mwTimeout);
|
|
}
|
|
that._mwTimeout = setTimeout(function () {
|
|
that._end();
|
|
}, MOUSEWHEEL_DELAY);
|
|
}
|
|
},
|
|
_index: function (value) {
|
|
var that = this, categoryAxis = that.categoryAxis, categories = categoryAxis.options.categories, index = value;
|
|
if (value instanceof Date) {
|
|
index = lteDateIndex(value, categories);
|
|
if (!categoryAxis.options.justified && value > last(categories)) {
|
|
index += 1;
|
|
}
|
|
}
|
|
return index;
|
|
},
|
|
_value: function (index) {
|
|
var that = this, categoryAxis = this.categoryAxis, categories = categoryAxis.options.categories, value = index;
|
|
if (that._dateAxis) {
|
|
if (index > categories.length - 1) {
|
|
value = that.options.max;
|
|
} else {
|
|
value = categories[index];
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
_slot: function (value) {
|
|
var categoryAxis = this.categoryAxis;
|
|
var index = this._index(value);
|
|
return categoryAxis.getSlot(index, index, true);
|
|
},
|
|
move: function (from, to) {
|
|
var that = this, options = that.options, offset = options.offset, padding = options.padding, border = options.selection.border, leftMaskWidth, rightMaskWidth, box, distance;
|
|
box = that._slot(from);
|
|
leftMaskWidth = round(box.x1 - offset.left + padding.left);
|
|
that.leftMask.width(leftMaskWidth);
|
|
that.selection.css('left', leftMaskWidth);
|
|
box = that._slot(to);
|
|
rightMaskWidth = round(options.width - (box.x1 - offset.left + padding.left));
|
|
that.rightMask.width(rightMaskWidth);
|
|
distance = options.width - rightMaskWidth;
|
|
if (distance != options.width) {
|
|
distance += border.right;
|
|
}
|
|
that.rightMask.css('left', distance);
|
|
that.selection.width(math.max(options.width - (leftMaskWidth + rightMaskWidth) - border.right, 0));
|
|
},
|
|
set: function (from, to) {
|
|
var that = this, options = that.options, min = that._index(options.min), max = that._index(options.max);
|
|
from = limitValue(that._index(from), min, max);
|
|
to = limitValue(that._index(to), from + 1, max);
|
|
if (options.visible) {
|
|
that.move(from, to);
|
|
}
|
|
options.from = that._value(from);
|
|
options.to = that._value(to);
|
|
},
|
|
expand: function (delta) {
|
|
var that = this, options = that.options, min = that._index(options.min), max = that._index(options.max), zDir = options.mousewheel.zoom, from = that._index(options.from), to = that._index(options.to), range = {
|
|
from: from,
|
|
to: to
|
|
}, oldRange = deepExtend({}, range);
|
|
if (that._state) {
|
|
range = that._state.range;
|
|
}
|
|
if (zDir !== RIGHT) {
|
|
range.from = limitValue(limitValue(from - delta, 0, to - 1), min, max);
|
|
}
|
|
if (zDir !== LEFT) {
|
|
range.to = limitValue(limitValue(to + delta, range.from + 1, max), min, max);
|
|
}
|
|
if (range.from !== oldRange.from || range.to !== oldRange.to) {
|
|
that.set(range.from, range.to);
|
|
return true;
|
|
}
|
|
},
|
|
getValueAxis: function (categoryAxis) {
|
|
var axes = categoryAxis.pane.axes, axesCount = axes.length, i, axis;
|
|
for (i = 0; i < axesCount; i++) {
|
|
axis = axes[i];
|
|
if (axis.options.vertical !== categoryAxis.options.vertical) {
|
|
return axis;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var Pannable = Class.extend({
|
|
init: function (plotArea, options) {
|
|
this.plotArea = plotArea;
|
|
this.options = deepExtend({}, this.options, options);
|
|
},
|
|
options: {
|
|
key: 'none',
|
|
lock: 'none'
|
|
},
|
|
start: function (e) {
|
|
this._active = acceptKey(e.event, this.options.key);
|
|
},
|
|
move: function (e) {
|
|
if (this._active) {
|
|
var axisRanges = this.axisRanges = this._panAxes(e, X).concat(this._panAxes(e, Y));
|
|
if (axisRanges.length) {
|
|
this.axisRanges = axisRanges;
|
|
return toChartAxisRanges(axisRanges);
|
|
}
|
|
}
|
|
},
|
|
end: function () {
|
|
this._active = false;
|
|
},
|
|
pan: function () {
|
|
var plotArea = this.plotArea;
|
|
var axisRanges = this.axisRanges;
|
|
var range;
|
|
if (axisRanges.length) {
|
|
for (var idx = 0; idx < axisRanges.length; idx++) {
|
|
range = axisRanges[idx];
|
|
plotArea.updateAxisOptions(range.axis, range.range);
|
|
}
|
|
plotArea.redraw(plotArea.panes);
|
|
}
|
|
},
|
|
_panAxes: function (e, position) {
|
|
var plotArea = this.plotArea;
|
|
var delta = -e[position].delta;
|
|
var lock = (this.options.lock || '').toLowerCase();
|
|
var updatedAxes = [];
|
|
if (delta !== 0 && (lock || '').toLowerCase() != position) {
|
|
var axes = plotArea.axes;
|
|
var axis;
|
|
var range;
|
|
for (var idx = 0; idx < axes.length; idx++) {
|
|
axis = axes[idx];
|
|
if (position == X && !axis.options.vertical || position == Y && axis.options.vertical) {
|
|
range = axis.pan(delta);
|
|
if (range) {
|
|
range.limitRange = true;
|
|
updatedAxes.push({
|
|
axis: axis,
|
|
range: range
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return updatedAxes;
|
|
}
|
|
});
|
|
var ZoomSelection = Class.extend({
|
|
init: function (chart, options) {
|
|
this.chart = chart;
|
|
this.options = deepExtend({}, this.options, options);
|
|
this._marquee = $('<div class=\'k-marquee\'><div class=\'k-marquee-color\'></div></div>');
|
|
},
|
|
options: {
|
|
key: 'shift',
|
|
lock: 'none'
|
|
},
|
|
start: function (e) {
|
|
if (acceptKey(e.event, this.options.key)) {
|
|
var chart = this.chart;
|
|
var point = chart._toModelCoordinates(e.x.client, e.y.client);
|
|
var zoomPane = this._zoomPane = chart._plotArea.paneByPoint(point);
|
|
if (zoomPane && zoomPane.clipBox()) {
|
|
var clipBox = zoomPane.clipBox().clone();
|
|
var elementOffset = this._elementOffset();
|
|
clipBox.translate(elementOffset.left, elementOffset.top);
|
|
this._zoomPaneClipBox = clipBox;
|
|
this._marquee.appendTo(document.body).css({
|
|
left: e.x.client + 1,
|
|
top: e.y.client + 1,
|
|
width: 0,
|
|
height: 0
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
_elementOffset: function () {
|
|
var chartElement = this.chart.element;
|
|
var chartOffset = chartElement.offset();
|
|
return {
|
|
left: parseInt(chartElement.css('paddingTop'), 10) + chartOffset.left,
|
|
top: parseInt(chartElement.css('paddingLeft'), 10) + chartOffset.top
|
|
};
|
|
},
|
|
move: function (e) {
|
|
var zoomPane = this._zoomPane;
|
|
if (zoomPane) {
|
|
var selectionPosition = this._selectionPosition(e);
|
|
this._marquee.css(selectionPosition);
|
|
}
|
|
},
|
|
end: function (e) {
|
|
var zoomPane = this._zoomPane;
|
|
if (zoomPane) {
|
|
var elementOffset = this._elementOffset();
|
|
var selectionPosition = this._selectionPosition(e);
|
|
selectionPosition.left -= elementOffset.left;
|
|
selectionPosition.top -= elementOffset.top;
|
|
var start = {
|
|
x: selectionPosition.left,
|
|
y: selectionPosition.top
|
|
};
|
|
var end = {
|
|
x: selectionPosition.left + selectionPosition.width,
|
|
y: selectionPosition.top + selectionPosition.height
|
|
};
|
|
this._updateAxisRanges(start, end);
|
|
this._marquee.remove();
|
|
delete this._zoomPane;
|
|
return toChartAxisRanges(this.axisRanges);
|
|
}
|
|
},
|
|
zoom: function () {
|
|
var axisRanges = this.axisRanges;
|
|
if (axisRanges && axisRanges.length) {
|
|
var plotArea = this.chart._plotArea;
|
|
var axisRange;
|
|
for (var idx = 0; idx < axisRanges.length; idx++) {
|
|
axisRange = axisRanges[idx];
|
|
plotArea.updateAxisOptions(axisRange.axis, axisRange.range);
|
|
}
|
|
plotArea.redraw(plotArea.panes);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this._marquee.remove();
|
|
delete this._marquee;
|
|
},
|
|
_updateAxisRanges: function (start, end) {
|
|
var lock = (this.options.lock || '').toLowerCase();
|
|
var axisRanges = [];
|
|
var axes = this._zoomPane.axes;
|
|
var axis, vertical;
|
|
for (var idx = 0; idx < axes.length; idx++) {
|
|
axis = axes[idx];
|
|
vertical = axis.options.vertical;
|
|
if (!(lock == X && !vertical) && !(lock === Y && vertical)) {
|
|
var range = axis.pointsRange(start, end);
|
|
axisRanges.push({
|
|
axis: axis,
|
|
range: range
|
|
});
|
|
}
|
|
}
|
|
this.axisRanges = axisRanges;
|
|
},
|
|
_selectionPosition: function (e) {
|
|
var lock = (this.options.lock || '').toLowerCase();
|
|
var left = math.min(e.x.startLocation, e.x.location);
|
|
var top = math.min(e.y.startLocation, e.y.location);
|
|
var width = math.abs(e.x.initialDelta);
|
|
var height = math.abs(e.y.initialDelta);
|
|
var clipBox = this._zoomPaneClipBox;
|
|
if (lock == X) {
|
|
left = clipBox.x1;
|
|
width = clipBox.width();
|
|
}
|
|
if (lock == Y) {
|
|
top = clipBox.y1;
|
|
height = clipBox.height();
|
|
}
|
|
if (e.x.location > clipBox.x2) {
|
|
width = clipBox.x2 - e.x.startLocation;
|
|
}
|
|
if (e.x.location < clipBox.x1) {
|
|
width = e.x.startLocation - clipBox.x1;
|
|
}
|
|
if (e.y.location > clipBox.y2) {
|
|
height = clipBox.y2 - e.y.startLocation;
|
|
}
|
|
if (e.y.location < clipBox.y1) {
|
|
height = e.y.startLocation - clipBox.y1;
|
|
}
|
|
return {
|
|
left: math.max(left, clipBox.x1),
|
|
top: math.max(top, clipBox.y1),
|
|
width: width,
|
|
height: height
|
|
};
|
|
}
|
|
});
|
|
var MousewheelZoom = Class.extend({
|
|
init: function (chart, options) {
|
|
this.chart = chart;
|
|
this.options = deepExtend({}, this.options, options);
|
|
},
|
|
updateRanges: function (delta) {
|
|
var lock = (this.options.lock || '').toLowerCase();
|
|
var axisRanges = [];
|
|
var axes = this.chart._plotArea.axes;
|
|
var axis, vertical;
|
|
for (var idx = 0; idx < axes.length; idx++) {
|
|
axis = axes[idx];
|
|
vertical = axis.options.vertical;
|
|
if (!(lock == X && !vertical) && !(lock === Y && vertical)) {
|
|
var range = axis.zoomRange(-delta);
|
|
if (range) {
|
|
axisRanges.push({
|
|
axis: axis,
|
|
range: range
|
|
});
|
|
}
|
|
}
|
|
}
|
|
this.axisRanges = axisRanges;
|
|
return toChartAxisRanges(axisRanges);
|
|
},
|
|
zoom: function () {
|
|
var axisRanges = this.axisRanges;
|
|
if (axisRanges && axisRanges.length) {
|
|
var plotArea = this.chart._plotArea;
|
|
var axisRange;
|
|
for (var idx = 0; idx < axisRanges.length; idx++) {
|
|
axisRange = axisRanges[idx];
|
|
plotArea.updateAxisOptions(axisRange.axis, axisRange.range);
|
|
}
|
|
plotArea.redraw(plotArea.panes);
|
|
}
|
|
}
|
|
});
|
|
var SeriesAggregator = function (series, binder, defaultAggregates) {
|
|
var sa = this, canonicalFields = binder.canonicalFields(series), valueFields = binder.valueFields(series), sourceFields = binder.sourceFields(series, canonicalFields), seriesFields = sa._seriesFields = [], defaults = defaultAggregates.query(series.type), rootAggregate = series.aggregate || defaults, i;
|
|
sa._series = series;
|
|
sa._binder = binder;
|
|
for (i = 0; i < canonicalFields.length; i++) {
|
|
var field = canonicalFields[i], fieldAggregate;
|
|
if (typeof rootAggregate === OBJECT) {
|
|
fieldAggregate = rootAggregate[field];
|
|
} else if (i === 0 || inArray(field, valueFields)) {
|
|
fieldAggregate = rootAggregate;
|
|
} else {
|
|
break;
|
|
}
|
|
if (fieldAggregate) {
|
|
seriesFields.push({
|
|
canonicalName: field,
|
|
name: sourceFields[i],
|
|
transform: isFn(fieldAggregate) ? fieldAggregate : Aggregates[fieldAggregate]
|
|
});
|
|
}
|
|
}
|
|
};
|
|
SeriesAggregator.prototype = {
|
|
aggregatePoints: function (srcPoints, group) {
|
|
var sa = this, data = sa._bindPoints(srcPoints || []), series = sa._series, seriesFields = sa._seriesFields, i, field, srcValues, value, firstDataItem = data.dataItems[0], result = {};
|
|
if (firstDataItem && !isNumber(firstDataItem) && !isArray(firstDataItem)) {
|
|
var fn = function () {
|
|
};
|
|
fn.prototype = firstDataItem;
|
|
result = new fn();
|
|
}
|
|
for (i = 0; i < seriesFields.length; i++) {
|
|
field = seriesFields[i];
|
|
srcValues = sa._bindField(data.values, field.canonicalName);
|
|
value = field.transform(srcValues, series, data.dataItems, group);
|
|
if (value !== null && typeof value === OBJECT && !defined(value.length) && !(value instanceof Date)) {
|
|
result = value;
|
|
break;
|
|
} else {
|
|
if (defined(value)) {
|
|
ensureTree(field.name, result);
|
|
kendo.setter(field.name)(result, value);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_bindPoints: function (points) {
|
|
var sa = this, binder = sa._binder, series = sa._series, values = [], dataItems = [], i, pointIx;
|
|
for (i = 0; i < points.length; i++) {
|
|
pointIx = points[i];
|
|
values.push(binder.bindPoint(series, pointIx));
|
|
dataItems.push(series.data[pointIx]);
|
|
}
|
|
return {
|
|
values: values,
|
|
dataItems: dataItems
|
|
};
|
|
},
|
|
_bindField: function (data, field) {
|
|
var values = [], count = data.length, i, item, value, valueFields;
|
|
for (i = 0; i < count; i++) {
|
|
item = data[i];
|
|
valueFields = item.valueFields;
|
|
if (defined(valueFields[field])) {
|
|
value = valueFields[field];
|
|
} else {
|
|
value = item.fields[field];
|
|
}
|
|
values.push(value);
|
|
}
|
|
return values;
|
|
}
|
|
};
|
|
var ChartAxis = Class.extend({
|
|
init: function (axis) {
|
|
this._axis = axis;
|
|
},
|
|
slot: function (from, to, limit) {
|
|
if (!defined(limit)) {
|
|
limit = true;
|
|
}
|
|
return this._axis.slot(from, to, limit);
|
|
},
|
|
range: function () {
|
|
return this._axis.range();
|
|
}
|
|
});
|
|
function intersection(a1, a2, b1, b2) {
|
|
var result, ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y), ua;
|
|
if (u_b !== 0) {
|
|
ua = ua_t / u_b;
|
|
result = new Point2D(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y));
|
|
}
|
|
return result;
|
|
}
|
|
function applySeriesDefaults(options, themeOptions) {
|
|
var series = options.series, i, seriesLength = series.length, seriesType, seriesDefaults = options.seriesDefaults, commonDefaults = deepExtend({}, options.seriesDefaults), themeSeriesDefaults = themeOptions ? deepExtend({}, themeOptions.seriesDefaults) : {}, commonThemeDefaults = deepExtend({}, themeSeriesDefaults);
|
|
cleanupNestedSeriesDefaults(commonDefaults);
|
|
cleanupNestedSeriesDefaults(commonThemeDefaults);
|
|
for (i = 0; i < seriesLength; i++) {
|
|
seriesType = series[i].type || options.seriesDefaults.type;
|
|
var baseOptions = deepExtend({ data: [] }, commonThemeDefaults, themeSeriesDefaults[seriesType], { tooltip: options.tooltip }, commonDefaults, seriesDefaults[seriesType]);
|
|
series[i]._defaults = baseOptions;
|
|
series[i] = deepExtend({}, baseOptions, series[i]);
|
|
}
|
|
}
|
|
function cleanupNestedSeriesDefaults(seriesDefaults) {
|
|
delete seriesDefaults.bar;
|
|
delete seriesDefaults.column;
|
|
delete seriesDefaults.rangeColumn;
|
|
delete seriesDefaults.line;
|
|
delete seriesDefaults.verticalLine;
|
|
delete seriesDefaults.pie;
|
|
delete seriesDefaults.donut;
|
|
delete seriesDefaults.area;
|
|
delete seriesDefaults.verticalArea;
|
|
delete seriesDefaults.scatter;
|
|
delete seriesDefaults.scatterLine;
|
|
delete seriesDefaults.bubble;
|
|
delete seriesDefaults.candlestick;
|
|
delete seriesDefaults.ohlc;
|
|
delete seriesDefaults.boxPlot;
|
|
delete seriesDefaults.bullet;
|
|
delete seriesDefaults.verticalBullet;
|
|
delete seriesDefaults.polarArea;
|
|
delete seriesDefaults.polarLine;
|
|
delete seriesDefaults.radarArea;
|
|
delete seriesDefaults.radarLine;
|
|
delete seriesDefaults.waterfall;
|
|
}
|
|
function applySeriesColors(options) {
|
|
var series = options.series, colors = options.seriesColors || [], i, currentSeries, seriesColor, defaults;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
seriesColor = colors[i % colors.length];
|
|
currentSeries.color = currentSeries.color || seriesColor;
|
|
defaults = currentSeries._defaults;
|
|
if (defaults) {
|
|
defaults.color = defaults.color || seriesColor;
|
|
}
|
|
}
|
|
}
|
|
function resolveAxisAliases(options) {
|
|
var alias;
|
|
each([
|
|
CATEGORY,
|
|
VALUE,
|
|
X,
|
|
Y
|
|
], function () {
|
|
alias = this + 'Axes';
|
|
if (options[alias]) {
|
|
options[this + 'Axis'] = options[alias];
|
|
delete options[alias];
|
|
}
|
|
});
|
|
}
|
|
function applyAxisDefaults(options, themeOptions) {
|
|
var themeAxisDefaults = (themeOptions || {}).axisDefaults || {};
|
|
each([
|
|
CATEGORY,
|
|
VALUE,
|
|
X,
|
|
Y
|
|
], function () {
|
|
var axisName = this + 'Axis', axes = [].concat(options[axisName]), axisDefaults = options.axisDefaults || {};
|
|
axes = $.map(axes, function (axisOptions) {
|
|
var axisColor = (axisOptions || {}).color;
|
|
var result = deepExtend({}, themeAxisDefaults, themeAxisDefaults[axisName], axisDefaults, axisDefaults[axisName], {
|
|
line: { color: axisColor },
|
|
labels: { color: axisColor },
|
|
title: { color: axisColor }
|
|
}, axisOptions);
|
|
delete result[axisName];
|
|
return result;
|
|
});
|
|
options[axisName] = axes.length > 1 ? axes : axes[0];
|
|
});
|
|
}
|
|
function categoriesCount(series) {
|
|
var seriesCount = series.length, categories = 0, i;
|
|
for (i = 0; i < seriesCount; i++) {
|
|
categories = math.max(categories, series[i].data.length);
|
|
}
|
|
return categories;
|
|
}
|
|
function sqr(value) {
|
|
return value * value;
|
|
}
|
|
extend($.easing, {
|
|
easeOutElastic: function (n, d, first, diff) {
|
|
var s = 1.70158, p = 0, a = diff;
|
|
if (n === 0) {
|
|
return first;
|
|
}
|
|
if (n === 1) {
|
|
return first + diff;
|
|
}
|
|
if (!p) {
|
|
p = 0.5;
|
|
}
|
|
if (a < math.abs(diff)) {
|
|
a = diff;
|
|
s = p / 4;
|
|
} else {
|
|
s = p / (2 * math.PI) * math.asin(diff / a);
|
|
}
|
|
return a * math.pow(2, -10 * n) * math.sin((n * 1 - s) * (1.1 * math.PI) / p) + diff + first;
|
|
}
|
|
});
|
|
function getField(field, row) {
|
|
if (row === null) {
|
|
return row;
|
|
}
|
|
var get = getter(field, true);
|
|
return get(row);
|
|
}
|
|
function getDateField(field, row) {
|
|
if (row === null) {
|
|
return row;
|
|
}
|
|
var key = '_date_' + field, value = row[key];
|
|
if (!value) {
|
|
value = toDate(getter(field, true)(row));
|
|
row[key] = value;
|
|
}
|
|
return value;
|
|
}
|
|
function toDate(value) {
|
|
var result, i;
|
|
if (value instanceof Date) {
|
|
result = value;
|
|
} else if (typeof value === STRING) {
|
|
result = kendo.parseDate(value) || new Date(value);
|
|
} else if (value) {
|
|
if (isArray(value)) {
|
|
result = [];
|
|
for (i = 0; i < value.length; i++) {
|
|
result.push(toDate(value[i]));
|
|
}
|
|
} else {
|
|
result = new Date(value);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function toTime(value) {
|
|
if (isArray(value)) {
|
|
return map(value, toTime);
|
|
} else if (value) {
|
|
return toDate(value).getTime();
|
|
}
|
|
}
|
|
function addDuration(date, value, unit, weekStartDay) {
|
|
var result = date, hours;
|
|
if (date) {
|
|
date = toDate(date);
|
|
hours = date.getHours();
|
|
if (unit === YEARS) {
|
|
result = new Date(date.getFullYear() + value, 0, 1);
|
|
kendo.date.adjustDST(result, 0);
|
|
} else if (unit === MONTHS) {
|
|
result = new Date(date.getFullYear(), date.getMonth() + value, 1);
|
|
kendo.date.adjustDST(result, hours);
|
|
} else if (unit === WEEKS) {
|
|
result = addDuration(startOfWeek(date, weekStartDay), value * 7, DAYS);
|
|
kendo.date.adjustDST(result, hours);
|
|
} else if (unit === DAYS) {
|
|
result = new Date(date.getFullYear(), date.getMonth(), date.getDate() + value);
|
|
kendo.date.adjustDST(result, hours);
|
|
} else if (unit === HOURS) {
|
|
result = addTicks(new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours()), value * TIME_PER_HOUR);
|
|
} else if (unit === MINUTES) {
|
|
result = addTicks(date, value * TIME_PER_MINUTE);
|
|
if (result.getSeconds() > 0) {
|
|
result.setSeconds(0);
|
|
}
|
|
} else if (unit === SECONDS) {
|
|
result = addTicks(date, value * TIME_PER_SECOND);
|
|
}
|
|
if (result.getMilliseconds() > 0) {
|
|
result.setMilliseconds(0);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function startOfWeek(date, weekStartDay) {
|
|
var day = date.getDay(), daysToSubtract = 0;
|
|
if (!isNaN(day)) {
|
|
weekStartDay = weekStartDay || 0;
|
|
while (day !== weekStartDay) {
|
|
if (day === 0) {
|
|
day = 6;
|
|
} else {
|
|
day--;
|
|
}
|
|
daysToSubtract++;
|
|
}
|
|
}
|
|
return addTicks(date, -daysToSubtract * TIME_PER_DAY);
|
|
}
|
|
function floorDate(date, unit, weekStartDay) {
|
|
date = toDate(date);
|
|
return addDuration(date, 0, unit, weekStartDay);
|
|
}
|
|
function ceilDate(date, unit, weekStartDay) {
|
|
date = toDate(date);
|
|
if (date && floorDate(date, unit, weekStartDay).getTime() === date.getTime()) {
|
|
return date;
|
|
}
|
|
return addDuration(date, 1, unit, weekStartDay);
|
|
}
|
|
function dateDiff(a, b) {
|
|
var diff = a.getTime() - b, offsetDiff = a.getTimezoneOffset() - b.getTimezoneOffset();
|
|
return diff - (offsetDiff * diff > 0 ? offsetDiff * TIME_PER_MINUTE : 0);
|
|
}
|
|
function absoluteDateDiff(a, b) {
|
|
var diff = a.getTime() - b, offsetDiff = a.getTimezoneOffset() - b.getTimezoneOffset();
|
|
return diff - offsetDiff * TIME_PER_MINUTE;
|
|
}
|
|
function addTicks(date, ticks) {
|
|
var tzOffsetBefore = date.getTimezoneOffset(), result = new Date(date.getTime() + ticks), tzOffsetDiff = result.getTimezoneOffset() - tzOffsetBefore;
|
|
return new Date(result.getTime() + (ticks * tzOffsetDiff > 0 ? tzOffsetDiff * TIME_PER_MINUTE : 0));
|
|
}
|
|
function duration(a, b, unit) {
|
|
var diff;
|
|
if (unit === YEARS) {
|
|
diff = b.getFullYear() - a.getFullYear();
|
|
} else if (unit === MONTHS) {
|
|
diff = duration(a, b, YEARS) * 12 + b.getMonth() - a.getMonth();
|
|
} else if (unit === DAYS) {
|
|
diff = math.floor(dateDiff(b, a) / TIME_PER_DAY);
|
|
} else {
|
|
diff = math.floor(dateDiff(b, a) / TIME_PER_UNIT[unit]);
|
|
}
|
|
return diff;
|
|
}
|
|
function dateIndex(value, start, baseUnit, baseUnitStep) {
|
|
var index;
|
|
var date = toDate(value);
|
|
var startDate = toDate(start);
|
|
if (baseUnit == MONTHS) {
|
|
index = date.getMonth() - startDate.getMonth() + (date.getFullYear() - startDate.getFullYear()) * 12 + timeIndex(date, new Date(date.getFullYear(), date.getMonth()), DAYS) / new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
|
} else if (baseUnit == YEARS) {
|
|
index = date.getFullYear() - startDate.getFullYear() + dateIndex(date, new Date(date.getFullYear(), 0), MONTHS, 1) / 12;
|
|
} else if (baseUnit == DAYS || baseUnit == WEEKS) {
|
|
index = timeIndex(date, startDate, baseUnit);
|
|
} else {
|
|
index = dateDiff(date, start) / TIME_PER_UNIT[baseUnit];
|
|
}
|
|
return index / baseUnitStep;
|
|
}
|
|
function timeIndex(date, start, baseUnit) {
|
|
return absoluteDateDiff(date, start) / TIME_PER_UNIT[baseUnit];
|
|
}
|
|
function singleItemOrArray(array) {
|
|
return array.length === 1 ? array[0] : array;
|
|
}
|
|
function axisGroupBox(axes) {
|
|
var length = axes.length, box, i, axisBox;
|
|
if (length > 0) {
|
|
for (i = 0; i < length; i++) {
|
|
axisBox = axes[i].contentBox();
|
|
if (!box) {
|
|
box = axisBox.clone();
|
|
} else {
|
|
box.wrap(axisBox);
|
|
}
|
|
}
|
|
}
|
|
return box || Box2D();
|
|
}
|
|
function equalsIgnoreCase(a, b) {
|
|
if (a && b) {
|
|
return a.toLowerCase() === b.toLowerCase();
|
|
}
|
|
return a === b;
|
|
}
|
|
function dateEquals(a, b) {
|
|
if (a && b) {
|
|
return toTime(a) === toTime(b);
|
|
}
|
|
return a === b;
|
|
}
|
|
function appendIfNotNull(array, element) {
|
|
if (element !== null) {
|
|
array.push(element);
|
|
}
|
|
}
|
|
function lteDateIndex(date, sortedDates) {
|
|
var low = 0, high = sortedDates.length - 1, i, currentDate;
|
|
while (low <= high) {
|
|
i = math.floor((low + high) / 2);
|
|
currentDate = sortedDates[i];
|
|
if (currentDate < date) {
|
|
low = i + 1;
|
|
continue;
|
|
}
|
|
if (currentDate > date) {
|
|
high = i - 1;
|
|
continue;
|
|
}
|
|
while (dateEquals(sortedDates[i - 1], date)) {
|
|
i--;
|
|
}
|
|
return i;
|
|
}
|
|
if (sortedDates[i] <= date) {
|
|
return i;
|
|
} else {
|
|
return i - 1;
|
|
}
|
|
}
|
|
function isNumber(val) {
|
|
return typeof val === 'number' && !isNaN(val);
|
|
}
|
|
function countNumbers(values) {
|
|
var length = values.length, count = 0, i, num;
|
|
for (i = 0; i < length; i++) {
|
|
num = values[i];
|
|
if (isNumber(num)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
function areNumbers(values) {
|
|
return countNumbers(values) === values.length;
|
|
}
|
|
function axisRanges(axes) {
|
|
var i, axis, axisName, ranges = {};
|
|
for (i = 0; i < axes.length; i++) {
|
|
axis = axes[i];
|
|
axisName = axis.options.name;
|
|
if (axisName) {
|
|
ranges[axisName] = axis.range();
|
|
}
|
|
}
|
|
return ranges;
|
|
}
|
|
function evalOptions(options, context, state, dryRun) {
|
|
var property, propValue, excluded, defaults, depth, needsEval = false;
|
|
state = state || {};
|
|
excluded = state.excluded = state.excluded || [];
|
|
defaults = state.defaults = state.defaults || {};
|
|
depth = state.depth = state.depth || 0;
|
|
if (depth > MAX_EXPAND_DEPTH) {
|
|
return;
|
|
}
|
|
for (property in options) {
|
|
if (!inArray(property, state.excluded) && options.hasOwnProperty(property)) {
|
|
propValue = options[property];
|
|
if (isFn(propValue)) {
|
|
needsEval = true;
|
|
if (!dryRun) {
|
|
options[property] = valueOrDefault(propValue(context), defaults[property]);
|
|
}
|
|
} else if (typeof propValue === OBJECT) {
|
|
if (!dryRun) {
|
|
state.defaults = defaults[property];
|
|
}
|
|
state.depth++;
|
|
needsEval = evalOptions(propValue, context, state, dryRun) || needsEval;
|
|
state.depth--;
|
|
}
|
|
}
|
|
}
|
|
return needsEval;
|
|
}
|
|
function groupSeries(series, data) {
|
|
var result = [], nameTemplate, legacyTemplate = series.groupNameTemplate, groupIx, dataLength = data.length, seriesClone;
|
|
if (dataLength === 0) {
|
|
seriesClone = deepExtend({}, series);
|
|
seriesClone.visibleInLegend = false;
|
|
return [seriesClone];
|
|
}
|
|
if (defined(legacyTemplate)) {
|
|
kendo.logToConsole('\'groupNameTemplate\' is obsolete and will be removed in future versions. ' + 'Specify the group name template as \'series.name\'');
|
|
if (legacyTemplate) {
|
|
nameTemplate = template(legacyTemplate);
|
|
}
|
|
} else {
|
|
nameTemplate = template(series.name || '');
|
|
if (nameTemplate._slotCount === 0) {
|
|
nameTemplate = template(defined(series.name) ? '#= group.value #: #= series.name #' : '#= group.value #');
|
|
}
|
|
}
|
|
for (groupIx = 0; groupIx < dataLength; groupIx++) {
|
|
seriesClone = deepExtend({}, series);
|
|
if (!isFn(seriesClone.color)) {
|
|
seriesClone.color = undefined;
|
|
}
|
|
seriesClone._groupIx = groupIx;
|
|
result.push(seriesClone);
|
|
if (nameTemplate) {
|
|
seriesClone.name = nameTemplate({
|
|
series: seriesClone,
|
|
group: data[groupIx]
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function filterSeriesByType(series, types) {
|
|
var i, currentSeries, result = [];
|
|
types = [].concat(types);
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
if (inArray(currentSeries.type, types)) {
|
|
result.push(currentSeries);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function indexOf(item, arr) {
|
|
if (item instanceof Date) {
|
|
for (var i = 0, length = arr.length; i < length; i++) {
|
|
if (dateEquals(arr[i], item)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
} else {
|
|
return $.inArray(item, arr);
|
|
}
|
|
}
|
|
function sortDates(dates, comparer) {
|
|
comparer = comparer || dateComparer;
|
|
for (var i = 1, length = dates.length; i < length; i++) {
|
|
if (comparer(dates[i], dates[i - 1]) < 0) {
|
|
dates.sort(comparer);
|
|
break;
|
|
}
|
|
}
|
|
return dates;
|
|
}
|
|
function uniqueDates(srcDates, comparer) {
|
|
var i, dates = sortDates(srcDates, comparer), length = dates.length, result = length > 0 ? [dates[0]] : [];
|
|
comparer = comparer || dateComparer;
|
|
for (i = 1; i < length; i++) {
|
|
if (comparer(dates[i], last(result)) !== 0) {
|
|
result.push(dates[i]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function isDateAxis(axisOptions, sampleCategory) {
|
|
var type = axisOptions.type, dateCategory = sampleCategory instanceof Date;
|
|
return !type && dateCategory || equalsIgnoreCase(type, DATE);
|
|
}
|
|
function transpose(rows) {
|
|
var result = [], rowCount = rows.length, rowIx, row, colIx, colCount;
|
|
for (rowIx = 0; rowIx < rowCount; rowIx++) {
|
|
row = rows[rowIx];
|
|
colCount = row.length;
|
|
for (colIx = 0; colIx < colCount; colIx++) {
|
|
result[colIx] = result[colIx] || [];
|
|
result[colIx].push(row[colIx]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function ensureTree(fieldName, target) {
|
|
if (fieldName.indexOf('.') > -1) {
|
|
var parts = fieldName.split('.'), path = '', val;
|
|
while (parts.length > 1) {
|
|
path += parts.shift();
|
|
val = kendo.getter(path)(target) || {};
|
|
kendo.setter(path)(target, val);
|
|
path += '.';
|
|
}
|
|
}
|
|
}
|
|
function seriesTotal(series) {
|
|
var data = series.data;
|
|
var sum = 0;
|
|
for (var i = 0; i < data.length; i++) {
|
|
var pointData = SeriesBinder.current.bindPoint(series, i);
|
|
var value = pointData.valueFields.value;
|
|
if (typeof value === STRING) {
|
|
value = parseFloat(value);
|
|
}
|
|
if (isNumber(value) && pointData.fields.visible !== false) {
|
|
sum += math.abs(value);
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
function hasGradientOverlay(options) {
|
|
var overlay = options.overlay;
|
|
return overlay && overlay.gradient && overlay.gradient != 'none';
|
|
}
|
|
function anyHasZIndex(elements) {
|
|
for (var idx = 0; idx < elements.length; idx++) {
|
|
if (defined(elements[idx].zIndex)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
function preventDefault() {
|
|
this._defaultPrevented = true;
|
|
}
|
|
function pointByCategoryName(points, name) {
|
|
if (points) {
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
if (points[idx].category === name) {
|
|
return [points[idx]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function hasValue(value) {
|
|
return defined(value) && value !== null;
|
|
}
|
|
function toChartAxisRanges(axisRanges) {
|
|
var ranges = {};
|
|
var axisRange;
|
|
for (var idx = 0; idx < axisRanges.length; idx++) {
|
|
axisRange = axisRanges[idx];
|
|
if (axisRange.axis.options.name) {
|
|
ranges[axisRange.axis.options.name] = {
|
|
min: axisRange.range.min,
|
|
max: axisRange.range.max
|
|
};
|
|
}
|
|
}
|
|
return ranges;
|
|
}
|
|
function acceptKey(e, mouseKey) {
|
|
var key = (mouseKey || '').toLowerCase();
|
|
var accept = key == 'none' && !(e.ctrlKey || e.shiftKey || e.altKey) || e[key + 'Key'];
|
|
return accept;
|
|
}
|
|
function preloadFonts(options, callback) {
|
|
var fonts = [];
|
|
fetchFonts(options, fonts);
|
|
kendo.util.loadFonts(fonts, callback);
|
|
}
|
|
function fetchFonts(options, fonts, state) {
|
|
var MAX_DEPTH = 5;
|
|
state = state || { depth: 0 };
|
|
if (!options || state.depth > MAX_DEPTH || !document.fonts) {
|
|
return;
|
|
}
|
|
Object.keys(options).forEach(function (key) {
|
|
var value = options[key];
|
|
if (key === 'dataSource' || key[0] === '$' || !value) {
|
|
return;
|
|
}
|
|
if (key === 'font') {
|
|
fonts.push(value);
|
|
} else if (typeof value === 'object') {
|
|
state.depth++;
|
|
fetchFonts(value, fonts, state);
|
|
state.depth--;
|
|
}
|
|
});
|
|
}
|
|
dataviz.ui.plugin(Chart);
|
|
PlotAreaFactory.current.register(CategoricalPlotArea, [
|
|
BAR,
|
|
COLUMN,
|
|
LINE,
|
|
VERTICAL_LINE,
|
|
AREA,
|
|
VERTICAL_AREA,
|
|
CANDLESTICK,
|
|
OHLC,
|
|
BULLET,
|
|
VERTICAL_BULLET,
|
|
BOX_PLOT,
|
|
RANGE_COLUMN,
|
|
RANGE_BAR,
|
|
WATERFALL,
|
|
HORIZONTAL_WATERFALL
|
|
]);
|
|
PlotAreaFactory.current.register(XYPlotArea, [
|
|
SCATTER,
|
|
SCATTER_LINE,
|
|
BUBBLE
|
|
]);
|
|
PlotAreaFactory.current.register(PiePlotArea, [PIE]);
|
|
PlotAreaFactory.current.register(DonutPlotArea, [DONUT]);
|
|
SeriesBinder.current.register([
|
|
BAR,
|
|
COLUMN,
|
|
LINE,
|
|
VERTICAL_LINE,
|
|
AREA,
|
|
VERTICAL_AREA
|
|
], [VALUE], [
|
|
CATEGORY,
|
|
COLOR,
|
|
NOTE_TEXT,
|
|
ERROR_LOW_FIELD,
|
|
ERROR_HIGH_FIELD
|
|
]);
|
|
SeriesBinder.current.register([
|
|
RANGE_COLUMN,
|
|
RANGE_BAR
|
|
], [
|
|
FROM,
|
|
TO
|
|
], [
|
|
CATEGORY,
|
|
COLOR,
|
|
NOTE_TEXT
|
|
]);
|
|
SeriesBinder.current.register([
|
|
WATERFALL,
|
|
HORIZONTAL_WATERFALL
|
|
], [VALUE], [
|
|
CATEGORY,
|
|
COLOR,
|
|
NOTE_TEXT,
|
|
SUMMARY_FIELD
|
|
]);
|
|
DefaultAggregates.current.register([
|
|
BAR,
|
|
COLUMN,
|
|
LINE,
|
|
VERTICAL_LINE,
|
|
AREA,
|
|
VERTICAL_AREA,
|
|
WATERFALL,
|
|
HORIZONTAL_WATERFALL
|
|
], {
|
|
value: MAX,
|
|
color: FIRST,
|
|
noteText: FIRST,
|
|
errorLow: MIN,
|
|
errorHigh: MAX
|
|
});
|
|
DefaultAggregates.current.register([
|
|
RANGE_COLUMN,
|
|
RANGE_BAR
|
|
], {
|
|
from: MIN,
|
|
to: MAX,
|
|
color: FIRST,
|
|
noteText: FIRST
|
|
});
|
|
SeriesBinder.current.register([
|
|
SCATTER,
|
|
SCATTER_LINE,
|
|
BUBBLE
|
|
], [
|
|
X,
|
|
Y
|
|
], [
|
|
COLOR,
|
|
NOTE_TEXT,
|
|
X_ERROR_LOW_FIELD,
|
|
X_ERROR_HIGH_FIELD,
|
|
Y_ERROR_LOW_FIELD,
|
|
Y_ERROR_HIGH_FIELD
|
|
]);
|
|
SeriesBinder.current.register([BUBBLE], [
|
|
X,
|
|
Y,
|
|
'size'
|
|
], [
|
|
COLOR,
|
|
CATEGORY,
|
|
NOTE_TEXT
|
|
]);
|
|
SeriesBinder.current.register([
|
|
CANDLESTICK,
|
|
OHLC
|
|
], [
|
|
'open',
|
|
'high',
|
|
'low',
|
|
'close'
|
|
], [
|
|
CATEGORY,
|
|
COLOR,
|
|
'downColor',
|
|
NOTE_TEXT
|
|
]);
|
|
DefaultAggregates.current.register([
|
|
CANDLESTICK,
|
|
OHLC
|
|
], {
|
|
open: MAX,
|
|
high: MAX,
|
|
low: MIN,
|
|
close: MAX,
|
|
color: FIRST,
|
|
downColor: FIRST,
|
|
noteText: FIRST
|
|
});
|
|
SeriesBinder.current.register([BOX_PLOT], [
|
|
'lower',
|
|
'q1',
|
|
'median',
|
|
'q3',
|
|
'upper',
|
|
'mean',
|
|
'outliers'
|
|
], [
|
|
CATEGORY,
|
|
COLOR,
|
|
NOTE_TEXT
|
|
]);
|
|
DefaultAggregates.current.register([BOX_PLOT], {
|
|
lower: MAX,
|
|
q1: MAX,
|
|
median: MAX,
|
|
q3: MAX,
|
|
upper: MAX,
|
|
mean: MAX,
|
|
outliers: FIRST,
|
|
color: FIRST,
|
|
noteText: FIRST
|
|
});
|
|
SeriesBinder.current.register([
|
|
BULLET,
|
|
VERTICAL_BULLET
|
|
], [
|
|
'current',
|
|
'target'
|
|
], [
|
|
CATEGORY,
|
|
COLOR,
|
|
'visibleInLegend',
|
|
NOTE_TEXT
|
|
]);
|
|
DefaultAggregates.current.register([
|
|
BULLET,
|
|
VERTICAL_BULLET
|
|
], {
|
|
current: MAX,
|
|
target: MAX,
|
|
color: FIRST,
|
|
noteText: FIRST
|
|
});
|
|
SeriesBinder.current.register([
|
|
PIE,
|
|
DONUT
|
|
], [VALUE], [
|
|
CATEGORY,
|
|
COLOR,
|
|
'explode',
|
|
'visibleInLegend',
|
|
'visible'
|
|
]);
|
|
deepExtend(dataviz, {
|
|
EQUALLY_SPACED_SERIES: EQUALLY_SPACED_SERIES,
|
|
Aggregates: Aggregates,
|
|
AreaChart: AreaChart,
|
|
AreaSegment: AreaSegment,
|
|
AxisGroupRangeTracker: AxisGroupRangeTracker,
|
|
Bar: Bar,
|
|
BarChart: BarChart,
|
|
BarLabel: BarLabel,
|
|
BubbleChart: BubbleChart,
|
|
Bullet: Bullet,
|
|
BulletChart: BulletChart,
|
|
CandlestickChart: CandlestickChart,
|
|
Candlestick: Candlestick,
|
|
CategoricalChart: CategoricalChart,
|
|
CategoricalErrorBar: CategoricalErrorBar,
|
|
CategoricalPlotArea: CategoricalPlotArea,
|
|
CategoryAxis: CategoryAxis,
|
|
ChartAxis: ChartAxis,
|
|
ChartContainer: ChartContainer,
|
|
ClipAnimation: ClipAnimation,
|
|
ClusterLayout: ClusterLayout,
|
|
Crosshair: Crosshair,
|
|
CrosshairTooltip: CrosshairTooltip,
|
|
DateCategoryAxis: DateCategoryAxis,
|
|
DateValueAxis: DateValueAxis,
|
|
DefaultAggregates: DefaultAggregates,
|
|
DonutChart: DonutChart,
|
|
DonutPlotArea: DonutPlotArea,
|
|
DonutSegment: DonutSegment,
|
|
ErrorBarBase: ErrorBarBase,
|
|
ErrorRangeCalculator: ErrorRangeCalculator,
|
|
Highlight: Highlight,
|
|
SharedTooltip: SharedTooltip,
|
|
Legend: Legend,
|
|
LegendItem: LegendItem,
|
|
LegendLayout: LegendLayout,
|
|
LineChart: LineChart,
|
|
LinePoint: LinePoint,
|
|
LineSegment: LineSegment,
|
|
Pane: Pane,
|
|
PieAnimation: PieAnimation,
|
|
PieChart: PieChart,
|
|
PieChartMixin: PieChartMixin,
|
|
PiePlotArea: PiePlotArea,
|
|
PieSegment: PieSegment,
|
|
PlotAreaBase: PlotAreaBase,
|
|
PlotAreaFactory: PlotAreaFactory,
|
|
PointEventsMixin: PointEventsMixin,
|
|
RangeBar: RangeBar,
|
|
RangeBarChart: RangeBarChart,
|
|
ScatterChart: ScatterChart,
|
|
ScatterErrorBar: ScatterErrorBar,
|
|
ScatterLineChart: ScatterLineChart,
|
|
Selection: Selection,
|
|
SeriesAggregator: SeriesAggregator,
|
|
SeriesBinder: SeriesBinder,
|
|
ShapeElement: ShapeElement,
|
|
SplineSegment: SplineSegment,
|
|
SplineAreaSegment: SplineAreaSegment,
|
|
StackWrap: StackWrap,
|
|
Tooltip: Tooltip,
|
|
OHLCChart: OHLCChart,
|
|
OHLCPoint: OHLCPoint,
|
|
WaterfallChart: WaterfallChart,
|
|
WaterfallSegment: WaterfallSegment,
|
|
XYPlotArea: XYPlotArea,
|
|
MousewheelZoom: MousewheelZoom,
|
|
ZoomSelection: ZoomSelection,
|
|
Pannable: Pannable,
|
|
addDuration: addDuration,
|
|
areNumbers: areNumbers,
|
|
axisGroupBox: axisGroupBox,
|
|
categoriesCount: categoriesCount,
|
|
ceilDate: ceilDate,
|
|
countNumbers: countNumbers,
|
|
duration: duration,
|
|
ensureTree: ensureTree,
|
|
indexOf: indexOf,
|
|
isNumber: isNumber,
|
|
floorDate: floorDate,
|
|
filterSeriesByType: filterSeriesByType,
|
|
hasValue: hasValue,
|
|
lteDateIndex: lteDateIndex,
|
|
evalOptions: evalOptions,
|
|
seriesTotal: seriesTotal,
|
|
singleItemOrArray: singleItemOrArray,
|
|
sortDates: sortDates,
|
|
startOfWeek: startOfWeek,
|
|
transpose: transpose,
|
|
toDate: toDate,
|
|
toTime: toTime,
|
|
uniqueDates: uniqueDates
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.chart.polar', [
|
|
'kendo.dataviz.chart',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.chart.polar',
|
|
name: 'Polar Charts',
|
|
category: 'dataviz',
|
|
depends: ['dataviz.chart'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var math = Math, kendo = window.kendo, deepExtend = kendo.deepExtend, util = kendo.util, append = util.append, draw = kendo.drawing, geom = kendo.geometry, dataviz = kendo.dataviz, AreaSegment = dataviz.AreaSegment, Axis = dataviz.Axis, AxisGroupRangeTracker = dataviz.AxisGroupRangeTracker, BarChart = dataviz.BarChart, Box2D = dataviz.Box2D, CategoryAxis = dataviz.CategoryAxis, CategoricalChart = dataviz.CategoricalChart, CategoricalPlotArea = dataviz.CategoricalPlotArea, ChartElement = dataviz.ChartElement, CurveProcessor = dataviz.CurveProcessor, DonutSegment = dataviz.DonutSegment, LineChart = dataviz.LineChart, LineSegment = dataviz.LineSegment, LogarithmicAxis = dataviz.LogarithmicAxis, NumericAxis = dataviz.NumericAxis, PlotAreaBase = dataviz.PlotAreaBase, PlotAreaFactory = dataviz.PlotAreaFactory, Point2D = dataviz.Point2D, Ring = dataviz.Ring, ScatterChart = dataviz.ScatterChart, ScatterLineChart = dataviz.ScatterLineChart, SeriesBinder = dataviz.SeriesBinder, ShapeBuilder = dataviz.ShapeBuilder, SplineSegment = dataviz.SplineSegment, SplineAreaSegment = dataviz.SplineAreaSegment, getSpacing = dataviz.getSpacing, filterSeriesByType = dataviz.filterSeriesByType, limitValue = util.limitValue, round = dataviz.round;
|
|
var ARC = 'arc', BLACK = '#000', COORD_PRECISION = dataviz.COORD_PRECISION, DEFAULT_PADDING = 0.15, DEG_TO_RAD = math.PI / 180, GAP = 'gap', INTERPOLATE = 'interpolate', LOGARITHMIC = 'log', PLOT_AREA_CLICK = 'plotAreaClick', POLAR_AREA = 'polarArea', POLAR_LINE = 'polarLine', POLAR_SCATTER = 'polarScatter', RADAR_AREA = 'radarArea', RADAR_COLUMN = 'radarColumn', RADAR_LINE = 'radarLine', SMOOTH = 'smooth', X = 'x', Y = 'y', ZERO = 'zero', POLAR_CHARTS = [
|
|
POLAR_AREA,
|
|
POLAR_LINE,
|
|
POLAR_SCATTER
|
|
], RADAR_CHARTS = [
|
|
RADAR_AREA,
|
|
RADAR_COLUMN,
|
|
RADAR_LINE
|
|
];
|
|
var GridLinesMixin = {
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, radius = math.abs(axis.box.center().y - altAxis.lineBox().y1), majorAngles, minorAngles, skipMajor = false, gridLines = [];
|
|
if (options.majorGridLines.visible) {
|
|
majorAngles = axis.majorGridLineAngles(altAxis);
|
|
skipMajor = true;
|
|
gridLines = axis.renderMajorGridLines(majorAngles, radius, options.majorGridLines);
|
|
}
|
|
if (options.minorGridLines.visible) {
|
|
minorAngles = axis.minorGridLineAngles(altAxis, skipMajor);
|
|
append(gridLines, axis.renderMinorGridLines(minorAngles, radius, options.minorGridLines, altAxis, skipMajor));
|
|
}
|
|
return gridLines;
|
|
},
|
|
renderMajorGridLines: function (angles, radius, options) {
|
|
return this.renderGridLines(angles, radius, options);
|
|
},
|
|
renderMinorGridLines: function (angles, radius, options, altAxis, skipMajor) {
|
|
var radiusCallback = this.radiusCallback && this.radiusCallback(radius, altAxis, skipMajor);
|
|
return this.renderGridLines(angles, radius, options, radiusCallback);
|
|
},
|
|
renderGridLines: function (angles, radius, options, radiusCallback) {
|
|
var style = {
|
|
stroke: {
|
|
width: options.width,
|
|
color: options.color,
|
|
dashType: options.dashType
|
|
}
|
|
};
|
|
var center = this.box.center();
|
|
var circle = new geom.Circle([
|
|
center.x,
|
|
center.y
|
|
], radius);
|
|
var container = this.gridLinesVisual();
|
|
for (var i = 0; i < angles.length; i++) {
|
|
var line = new draw.Path(style);
|
|
if (radiusCallback) {
|
|
circle.radius = radiusCallback(angles[i]);
|
|
}
|
|
line.moveTo(circle.center).lineTo(circle.pointAt(angles[i]));
|
|
container.append(line);
|
|
}
|
|
return container.children;
|
|
},
|
|
gridLineAngles: function (altAxis, size, skip, step, skipAngles) {
|
|
var axis = this, divs = axis.intervals(size, skip, step, skipAngles), options = altAxis.options, altAxisVisible = options.visible && (options.line || {}).visible !== false;
|
|
return $.map(divs, function (d) {
|
|
var alpha = axis.intervalAngle(d);
|
|
if (!altAxisVisible || alpha !== 90) {
|
|
return alpha;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
var RadarCategoryAxis = CategoryAxis.extend({
|
|
options: {
|
|
startAngle: 90,
|
|
labels: { margin: getSpacing(10) },
|
|
majorGridLines: { visible: true },
|
|
justified: true
|
|
},
|
|
range: function () {
|
|
return {
|
|
min: 0,
|
|
max: this.options.categories.length
|
|
};
|
|
},
|
|
reflow: function (box) {
|
|
this.box = box;
|
|
this.reflowLabels();
|
|
},
|
|
lineBox: function () {
|
|
return this.box;
|
|
},
|
|
reflowLabels: function () {
|
|
var axis = this, labelOptions = axis.options.labels, skip = labelOptions.skip || 0, step = labelOptions.step || 1, measureBox = new Box2D(), labels = axis.labels, labelBox, i;
|
|
for (i = 0; i < labels.length; i++) {
|
|
labels[i].reflow(measureBox);
|
|
labelBox = labels[i].box;
|
|
labels[i].reflow(axis.getSlot(skip + i * step).adjacentBox(0, labelBox.width(), labelBox.height()));
|
|
}
|
|
},
|
|
intervals: function (size, skip, step, skipAngles) {
|
|
var axis = this, options = axis.options, categories = options.categories.length, angle = 0, divCount = categories / size || 1, divAngle = 360 / divCount, divs = [], i;
|
|
skip = skip || 0;
|
|
step = step || 1;
|
|
for (i = skip; i < divCount; i += step) {
|
|
if (options.reverse) {
|
|
angle = 360 - i * divAngle;
|
|
} else {
|
|
angle = i * divAngle;
|
|
}
|
|
angle = round(angle, COORD_PRECISION) % 360;
|
|
if (!(skipAngles && dataviz.inArray(angle, skipAngles))) {
|
|
divs.push(angle);
|
|
}
|
|
}
|
|
return divs;
|
|
},
|
|
majorIntervals: function () {
|
|
return this.intervals(1);
|
|
},
|
|
minorIntervals: function () {
|
|
return this.intervals(0.5);
|
|
},
|
|
intervalAngle: function (interval) {
|
|
return (360 + interval + this.options.startAngle) % 360;
|
|
},
|
|
majorAngles: function () {
|
|
return $.map(this.majorIntervals(), $.proxy(this.intervalAngle, this));
|
|
},
|
|
createLine: function () {
|
|
return [];
|
|
},
|
|
majorGridLineAngles: function (altAxis) {
|
|
var majorGridLines = this.options.majorGridLines;
|
|
return this.gridLineAngles(altAxis, 1, majorGridLines.skip, majorGridLines.step);
|
|
},
|
|
minorGridLineAngles: function (altAxis, skipMajor) {
|
|
var options = this.options;
|
|
var minorGridLines = options.minorGridLines;
|
|
var majorGridLines = options.majorGridLines;
|
|
var majorGridLineAngles = skipMajor ? this.intervals(1, majorGridLines.skip, majorGridLines.step) : null;
|
|
return this.gridLineAngles(altAxis, 0.5, minorGridLines.skip, minorGridLines.step, majorGridLineAngles);
|
|
},
|
|
radiusCallback: function (radius, altAxis, skipMajor) {
|
|
if (altAxis.options.type !== ARC) {
|
|
var minorAngle = 360 / (this.options.categories.length * 2);
|
|
var minorRadius = math.cos(minorAngle * DEG_TO_RAD) * radius;
|
|
var majorAngles = this.majorAngles();
|
|
var radiusCallback = function (angle) {
|
|
if (!skipMajor && dataviz.inArray(angle, majorAngles)) {
|
|
return radius;
|
|
} else {
|
|
return minorRadius;
|
|
}
|
|
};
|
|
return radiusCallback;
|
|
}
|
|
},
|
|
createPlotBands: function () {
|
|
var axis = this, options = axis.options, plotBands = options.plotBands || [], i, band, slot, singleSlot, head, tail;
|
|
var group = this._plotbandGroup = new draw.Group({ zIndex: -1 });
|
|
for (i = 0; i < plotBands.length; i++) {
|
|
band = plotBands[i];
|
|
slot = axis.plotBandSlot(band);
|
|
singleSlot = axis.getSlot(band.from);
|
|
head = band.from - math.floor(band.from);
|
|
slot.startAngle += head * singleSlot.angle;
|
|
tail = math.ceil(band.to) - band.to;
|
|
slot.angle -= (tail + head) * singleSlot.angle;
|
|
var ring = ShapeBuilder.current.createRing(slot, {
|
|
fill: {
|
|
color: band.color,
|
|
opacity: band.opacity
|
|
},
|
|
stroke: { opacity: band.opacity }
|
|
});
|
|
group.append(ring);
|
|
}
|
|
axis.appendVisual(group);
|
|
},
|
|
plotBandSlot: function (band) {
|
|
return this.getSlot(band.from, band.to - 1);
|
|
},
|
|
getSlot: function (from, to) {
|
|
var axis = this, options = axis.options, justified = options.justified, box = axis.box, divs = axis.majorAngles(), totalDivs = divs.length, slots, slotAngle = 360 / totalDivs, slotStart, angle;
|
|
if (options.reverse && !justified) {
|
|
from = (from + 1) % totalDivs;
|
|
}
|
|
from = limitValue(math.floor(from), 0, totalDivs - 1);
|
|
slotStart = divs[from];
|
|
if (justified) {
|
|
slotStart = slotStart - slotAngle / 2;
|
|
if (slotStart < 0) {
|
|
slotStart += 360;
|
|
}
|
|
}
|
|
to = limitValue(math.ceil(to || from), from, totalDivs - 1);
|
|
slots = to - from + 1;
|
|
angle = slotAngle * slots;
|
|
return new Ring(box.center(), 0, box.height() / 2, slotStart, angle);
|
|
},
|
|
slot: function (from, to) {
|
|
var slot = this.getSlot(from, to);
|
|
var startAngle = slot.startAngle + 180;
|
|
var endAngle = startAngle + slot.angle;
|
|
return new geom.Arc([
|
|
slot.c.x,
|
|
slot.c.y
|
|
], {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
radiusX: slot.r,
|
|
radiusY: slot.r
|
|
});
|
|
},
|
|
pointCategoryIndex: function (point) {
|
|
var axis = this, index = null, i, length = axis.options.categories.length, slot;
|
|
for (i = 0; i < length; i++) {
|
|
slot = axis.getSlot(i);
|
|
if (slot.containsPoint(point)) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
});
|
|
deepExtend(RadarCategoryAxis.fn, GridLinesMixin);
|
|
var RadarNumericAxisMixin = {
|
|
options: { majorGridLines: { visible: true } },
|
|
createPlotBands: function () {
|
|
var axis = this, options = axis.options, plotBands = options.plotBands || [], type = options.majorGridLines.type, altAxis = axis.plotArea.polarAxis, majorAngles = altAxis.majorAngles(), center = altAxis.box.center(), i, band, bandStyle, slot, ring;
|
|
var group = this._plotbandGroup = new draw.Group({ zIndex: -1 });
|
|
for (i = 0; i < plotBands.length; i++) {
|
|
band = plotBands[i];
|
|
bandStyle = {
|
|
fill: {
|
|
color: band.color,
|
|
opacity: band.opacity
|
|
},
|
|
stroke: { opacity: band.opacity }
|
|
};
|
|
slot = axis.getSlot(band.from, band.to, true);
|
|
ring = new Ring(center, center.y - slot.y2, center.y - slot.y1, 0, 360);
|
|
var shape;
|
|
if (type === ARC) {
|
|
shape = ShapeBuilder.current.createRing(ring, bandStyle);
|
|
} else {
|
|
shape = draw.Path.fromPoints(axis.plotBandPoints(ring, majorAngles), bandStyle).close();
|
|
}
|
|
group.append(shape);
|
|
}
|
|
axis.appendVisual(group);
|
|
},
|
|
plotBandPoints: function (ring, angles) {
|
|
var innerPoints = [], outerPoints = [];
|
|
var center = [
|
|
ring.c.x,
|
|
ring.c.y
|
|
];
|
|
var innerCircle = new geom.Circle(center, ring.ir);
|
|
var outerCircle = new geom.Circle(center, ring.r);
|
|
for (var i = 0; i < angles.length; i++) {
|
|
innerPoints.push(innerCircle.pointAt(angles[i]));
|
|
outerPoints.push(outerCircle.pointAt(angles[i]));
|
|
}
|
|
innerPoints.reverse();
|
|
innerPoints.push(innerPoints[0]);
|
|
outerPoints.push(outerPoints[0]);
|
|
return outerPoints.concat(innerPoints);
|
|
},
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, majorTicks = axis.radarMajorGridLinePositions(), majorAngles = altAxis.majorAngles(), minorTicks, center = altAxis.box.center(), gridLines = [];
|
|
if (options.majorGridLines.visible) {
|
|
gridLines = axis.renderGridLines(center, majorTicks, majorAngles, options.majorGridLines);
|
|
}
|
|
if (options.minorGridLines.visible) {
|
|
minorTicks = axis.radarMinorGridLinePositions();
|
|
append(gridLines, axis.renderGridLines(center, minorTicks, majorAngles, options.minorGridLines));
|
|
}
|
|
return gridLines;
|
|
},
|
|
renderGridLines: function (center, ticks, angles, options) {
|
|
var tickRadius, tickIx, angleIx;
|
|
var style = {
|
|
stroke: {
|
|
width: options.width,
|
|
color: options.color,
|
|
dashType: options.dashType
|
|
}
|
|
};
|
|
var container = this.gridLinesVisual();
|
|
for (tickIx = 0; tickIx < ticks.length; tickIx++) {
|
|
tickRadius = center.y - ticks[tickIx];
|
|
if (tickRadius > 0) {
|
|
var circle = new geom.Circle([
|
|
center.x,
|
|
center.y
|
|
], tickRadius);
|
|
if (options.type === ARC) {
|
|
container.append(new draw.Circle(circle, style));
|
|
} else {
|
|
var line = new draw.Path(style);
|
|
for (angleIx = 0; angleIx < angles.length; angleIx++) {
|
|
line.lineTo(circle.pointAt(angles[angleIx]));
|
|
}
|
|
line.close();
|
|
container.append(line);
|
|
}
|
|
}
|
|
}
|
|
return container.children;
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), altAxis = axis.plotArea.polarAxis, majorAngles = altAxis.majorAngles(), center = altAxis.box.center(), r = point.distanceTo(center), distance = r;
|
|
if (options.majorGridLines.type !== ARC && majorAngles.length > 1) {
|
|
var dx = point.x - center.x, dy = point.y - center.y, theta = (math.atan2(dy, dx) / DEG_TO_RAD + 540) % 360;
|
|
majorAngles.sort(function (a, b) {
|
|
return angularDistance(a, theta) - angularDistance(b, theta);
|
|
});
|
|
var midAngle = angularDistance(majorAngles[0], majorAngles[1]) / 2, alpha = angularDistance(theta, majorAngles[0]), gamma = 90 - midAngle, beta = 180 - alpha - gamma;
|
|
distance = r * (math.sin(beta * DEG_TO_RAD) / math.sin(gamma * DEG_TO_RAD));
|
|
}
|
|
return axis.axisType().fn.getValue.call(axis, new Point2D(lineBox.x1, lineBox.y2 - distance));
|
|
}
|
|
};
|
|
var RadarNumericAxis = NumericAxis.extend({
|
|
radarMajorGridLinePositions: function () {
|
|
return this.getTickPositions(this.options.majorUnit);
|
|
},
|
|
radarMinorGridLinePositions: function () {
|
|
var axis = this, options = axis.options, minorSkipStep = 0;
|
|
if (options.majorGridLines.visible) {
|
|
minorSkipStep = options.majorUnit;
|
|
}
|
|
return axis.getTickPositions(options.minorUnit, minorSkipStep);
|
|
},
|
|
axisType: function () {
|
|
return NumericAxis;
|
|
}
|
|
});
|
|
deepExtend(RadarNumericAxis.fn, RadarNumericAxisMixin);
|
|
var RadarLogarithmicAxis = LogarithmicAxis.extend({
|
|
radarMajorGridLinePositions: function () {
|
|
var axis = this, positions = [];
|
|
axis.traverseMajorTicksPositions(function (position) {
|
|
positions.push(position);
|
|
}, axis.options.majorGridLines);
|
|
return positions;
|
|
},
|
|
radarMinorGridLinePositions: function () {
|
|
var axis = this, positions = [];
|
|
axis.traverseMinorTicksPositions(function (position) {
|
|
positions.push(position);
|
|
}, axis.options.minorGridLines);
|
|
return positions;
|
|
},
|
|
axisType: function () {
|
|
return LogarithmicAxis;
|
|
}
|
|
});
|
|
deepExtend(RadarLogarithmicAxis.fn, RadarNumericAxisMixin);
|
|
var PolarAxis = Axis.extend({
|
|
init: function (options) {
|
|
var axis = this;
|
|
Axis.fn.init.call(axis, options);
|
|
options = axis.options;
|
|
options.minorUnit = options.minorUnit || axis.options.majorUnit / 2;
|
|
},
|
|
options: {
|
|
type: 'polar',
|
|
startAngle: 0,
|
|
reverse: false,
|
|
majorUnit: 60,
|
|
min: 0,
|
|
max: 360,
|
|
labels: { margin: getSpacing(10) },
|
|
majorGridLines: {
|
|
color: BLACK,
|
|
visible: true,
|
|
width: 1
|
|
},
|
|
minorGridLines: { color: '#aaa' }
|
|
},
|
|
getDivisions: function (stepValue) {
|
|
return NumericAxis.fn.getDivisions.call(this, stepValue) - 1;
|
|
},
|
|
reflow: function (box) {
|
|
this.box = box;
|
|
this.reflowLabels();
|
|
},
|
|
reflowLabels: function () {
|
|
var axis = this, options = axis.options, labelOptions = options.labels, skip = labelOptions.skip || 0, step = labelOptions.step || 1, measureBox = new Box2D(), divs = axis.intervals(options.majorUnit, skip, step), labels = axis.labels, labelBox, i;
|
|
for (i = 0; i < labels.length; i++) {
|
|
labels[i].reflow(measureBox);
|
|
labelBox = labels[i].box;
|
|
labels[i].reflow(axis.getSlot(divs[i]).adjacentBox(0, labelBox.width(), labelBox.height()));
|
|
}
|
|
},
|
|
lineBox: function () {
|
|
return this.box;
|
|
},
|
|
intervals: function (size, skip, step, skipAngles) {
|
|
var axis = this, options = axis.options, divisions = axis.getDivisions(size), min = options.min, current, divs = [], i;
|
|
skip = skip || 0;
|
|
step = step || 1;
|
|
for (i = skip; i < divisions; i += step) {
|
|
current = (360 + min + i * size) % 360;
|
|
if (!(skipAngles && dataviz.inArray(current, skipAngles))) {
|
|
divs.push(current);
|
|
}
|
|
}
|
|
return divs;
|
|
},
|
|
majorIntervals: function () {
|
|
return this.intervals(this.options.majorUnit);
|
|
},
|
|
minorIntervals: function () {
|
|
return this.intervals(this.options.minorUnit);
|
|
},
|
|
intervalAngle: function (i) {
|
|
return (540 - i - this.options.startAngle) % 360;
|
|
},
|
|
majorAngles: RadarCategoryAxis.fn.majorAngles,
|
|
createLine: function () {
|
|
return [];
|
|
},
|
|
majorGridLineAngles: function (altAxis) {
|
|
var majorGridLines = this.options.majorGridLines;
|
|
return this.gridLineAngles(altAxis, this.options.majorUnit, majorGridLines.skip, majorGridLines.step);
|
|
},
|
|
minorGridLineAngles: function (altAxis, skipMajor) {
|
|
var options = this.options;
|
|
var minorGridLines = options.minorGridLines;
|
|
var majorGridLines = options.majorGridLines;
|
|
var majorGridLineAngles = skipMajor ? this.intervals(options.majorUnit, majorGridLines.skip, majorGridLines.step) : null;
|
|
return this.gridLineAngles(altAxis, this.options.minorUnit, minorGridLines.skip, minorGridLines.step, majorGridLineAngles);
|
|
},
|
|
createPlotBands: RadarCategoryAxis.fn.createPlotBands,
|
|
plotBandSlot: function (band) {
|
|
return this.getSlot(band.from, band.to);
|
|
},
|
|
getSlot: function (a, b) {
|
|
var axis = this, options = axis.options, start = options.startAngle, box = axis.box, tmp;
|
|
a = limitValue(a, options.min, options.max);
|
|
b = limitValue(b || a, a, options.max);
|
|
if (options.reverse) {
|
|
a *= -1;
|
|
b *= -1;
|
|
}
|
|
a = (540 - a - start) % 360;
|
|
b = (540 - b - start) % 360;
|
|
if (b < a) {
|
|
tmp = a;
|
|
a = b;
|
|
b = tmp;
|
|
}
|
|
return new Ring(box.center(), 0, box.height() / 2, a, b - a);
|
|
},
|
|
slot: function (from, to) {
|
|
var options = this.options;
|
|
var start = 360 - options.startAngle;
|
|
var slot = this.getSlot(from, to);
|
|
var startAngle;
|
|
var endAngle;
|
|
var min;
|
|
var max;
|
|
if (!dataviz.util.defined(to)) {
|
|
to = from;
|
|
}
|
|
min = math.min(from, to);
|
|
max = math.max(from, to);
|
|
if (options.reverse) {
|
|
startAngle = min;
|
|
endAngle = max;
|
|
} else {
|
|
startAngle = 360 - max;
|
|
endAngle = 360 - min;
|
|
}
|
|
startAngle = (startAngle + start) % 360;
|
|
endAngle = (endAngle + start) % 360;
|
|
return new geom.Arc([
|
|
slot.c.x,
|
|
slot.c.y
|
|
], {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
radiusX: slot.r,
|
|
radiusY: slot.r
|
|
});
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, center = axis.box.center(), dx = point.x - center.x, dy = point.y - center.y, theta = math.round(math.atan2(dy, dx) / DEG_TO_RAD), start = options.startAngle;
|
|
if (!options.reverse) {
|
|
theta *= -1;
|
|
start *= -1;
|
|
}
|
|
return (theta + start + 360) % 360;
|
|
},
|
|
range: NumericAxis.fn.range,
|
|
labelsCount: NumericAxis.fn.labelsCount,
|
|
createAxisLabel: NumericAxis.fn.createAxisLabel
|
|
});
|
|
deepExtend(PolarAxis.fn, GridLinesMixin);
|
|
var RadarClusterLayout = ChartElement.extend({
|
|
options: {
|
|
gap: 1,
|
|
spacing: 0
|
|
},
|
|
reflow: function (sector) {
|
|
var cluster = this, options = cluster.options, children = cluster.children, gap = options.gap, spacing = options.spacing, count = children.length, slots = count + gap + spacing * (count - 1), slotAngle = sector.angle / slots, slotSector, angle = sector.startAngle + slotAngle * (gap / 2), i;
|
|
for (i = 0; i < count; i++) {
|
|
slotSector = sector.clone();
|
|
slotSector.startAngle = angle;
|
|
slotSector.angle = slotAngle;
|
|
if (children[i].sector) {
|
|
slotSector.r = children[i].sector.r;
|
|
}
|
|
children[i].reflow(slotSector);
|
|
children[i].sector = slotSector;
|
|
angle += slotAngle + slotAngle * spacing;
|
|
}
|
|
}
|
|
});
|
|
var RadarStackLayout = ChartElement.extend({
|
|
reflow: function (sector) {
|
|
var stack = this, reverse = stack.options.isReversed, children = stack.children, childrenCount = children.length, childSector, i, first = reverse ? childrenCount - 1 : 0, step = reverse ? -1 : 1;
|
|
stack.box = new Box2D();
|
|
for (i = first; i >= 0 && i < childrenCount; i += step) {
|
|
childSector = children[i].sector;
|
|
childSector.startAngle = sector.startAngle;
|
|
childSector.angle = sector.angle;
|
|
}
|
|
}
|
|
});
|
|
var RadarSegment = DonutSegment.extend({
|
|
init: function (value, options) {
|
|
DonutSegment.fn.init.call(this, value, null, options);
|
|
},
|
|
options: {
|
|
overlay: { gradient: null },
|
|
labels: { distance: 10 }
|
|
}
|
|
});
|
|
var RadarBarChart = BarChart.extend({
|
|
pointType: function () {
|
|
return RadarSegment;
|
|
},
|
|
clusterType: function () {
|
|
return RadarClusterLayout;
|
|
},
|
|
stackType: function () {
|
|
return RadarStackLayout;
|
|
},
|
|
categorySlot: function (categoryAxis, categoryIx) {
|
|
return categoryAxis.getSlot(categoryIx);
|
|
},
|
|
pointSlot: function (categorySlot, valueSlot) {
|
|
var slot = categorySlot.clone(), y = categorySlot.c.y;
|
|
slot.r = y - valueSlot.y1;
|
|
slot.ir = y - valueSlot.y2;
|
|
return slot;
|
|
},
|
|
reflow: CategoricalChart.fn.reflow,
|
|
reflowPoint: function (point, pointSlot) {
|
|
point.sector = pointSlot;
|
|
point.reflow();
|
|
},
|
|
options: {
|
|
clip: false,
|
|
animation: { type: 'pie' }
|
|
},
|
|
createAnimation: function () {
|
|
this.options.animation.center = this.box.toRect().center();
|
|
BarChart.fn.createAnimation.call(this);
|
|
}
|
|
});
|
|
var RadarLineChart = LineChart.extend({
|
|
options: { clip: false },
|
|
pointSlot: function (categorySlot, valueSlot) {
|
|
var valueRadius = categorySlot.c.y - valueSlot.y1, slot = Point2D.onCircle(categorySlot.c, categorySlot.middle(), valueRadius);
|
|
return new Box2D(slot.x, slot.y, slot.x, slot.y);
|
|
},
|
|
createSegment: function (linePoints, currentSeries, seriesIx) {
|
|
var segment, pointType, style = currentSeries.style;
|
|
if (style == SMOOTH) {
|
|
pointType = SplineSegment;
|
|
} else {
|
|
pointType = LineSegment;
|
|
}
|
|
segment = new pointType(linePoints, currentSeries, seriesIx);
|
|
if (linePoints.length === currentSeries.data.length) {
|
|
segment.options.closed = true;
|
|
}
|
|
return segment;
|
|
}
|
|
});
|
|
var RadarAreaSegment = AreaSegment.extend({
|
|
points: function () {
|
|
return LineSegment.fn.points.call(this, this.stackPoints);
|
|
}
|
|
});
|
|
var SplineRadarAreaSegment = SplineAreaSegment.extend({ closeFill: $.noop });
|
|
var RadarAreaChart = RadarLineChart.extend({
|
|
createSegment: function (linePoints, currentSeries, seriesIx, prevSegment) {
|
|
var chart = this, options = chart.options, isStacked = options.isStacked, stackPoints, segment, style = (currentSeries.line || {}).style;
|
|
if (style === SMOOTH) {
|
|
segment = new SplineRadarAreaSegment(linePoints, prevSegment, isStacked, currentSeries, seriesIx);
|
|
segment.options.closed = true;
|
|
} else {
|
|
if (isStacked && seriesIx > 0 && prevSegment) {
|
|
stackPoints = prevSegment.linePoints.slice(0).reverse();
|
|
}
|
|
linePoints.push(linePoints[0]);
|
|
segment = new RadarAreaSegment(linePoints, stackPoints, currentSeries, seriesIx);
|
|
}
|
|
return segment;
|
|
},
|
|
seriesMissingValues: function (series) {
|
|
return series.missingValues || ZERO;
|
|
}
|
|
});
|
|
var PolarScatterChart = ScatterChart.extend({
|
|
pointSlot: function (slotX, slotY) {
|
|
var valueRadius = slotX.c.y - slotY.y1, slot = Point2D.onCircle(slotX.c, slotX.startAngle, valueRadius);
|
|
return new Box2D(slot.x, slot.y, slot.x, slot.y);
|
|
},
|
|
options: { clip: false }
|
|
});
|
|
var PolarLineChart = ScatterLineChart.extend({
|
|
pointSlot: PolarScatterChart.fn.pointSlot,
|
|
options: { clip: false }
|
|
});
|
|
var PolarAreaSegment = AreaSegment.extend({
|
|
points: function () {
|
|
var segment = this, chart = segment.parent, plotArea = chart.plotArea, polarAxis = plotArea.polarAxis, center = polarAxis.box.center(), stackPoints = segment.stackPoints, points = LineSegment.fn.points.call(segment, stackPoints);
|
|
points.unshift([
|
|
center.x,
|
|
center.y
|
|
]);
|
|
points.push([
|
|
center.x,
|
|
center.y
|
|
]);
|
|
return points;
|
|
}
|
|
});
|
|
var SplinePolarAreaSegment = SplineAreaSegment.extend({
|
|
closeFill: function (fillPath) {
|
|
var center = this._polarAxisCenter();
|
|
fillPath.lineTo(center.x, center.y);
|
|
},
|
|
_polarAxisCenter: function () {
|
|
var chart = this.parent, plotArea = chart.plotArea, polarAxis = plotArea.polarAxis, center = polarAxis.box.center();
|
|
return center;
|
|
},
|
|
strokeSegments: function () {
|
|
var segments = this._strokeSegments;
|
|
if (!segments) {
|
|
var center = this._polarAxisCenter(), curveProcessor = new CurveProcessor(false), linePoints = LineSegment.fn.points.call(this);
|
|
linePoints.push(center);
|
|
segments = this._strokeSegments = curveProcessor.process(linePoints);
|
|
segments.pop();
|
|
}
|
|
return segments;
|
|
}
|
|
});
|
|
var PolarAreaChart = PolarLineChart.extend({
|
|
createSegment: function (linePoints, currentSeries, seriesIx) {
|
|
var segment, style = (currentSeries.line || {}).style;
|
|
if (style == SMOOTH) {
|
|
segment = new SplinePolarAreaSegment(linePoints, null, false, currentSeries, seriesIx);
|
|
} else {
|
|
segment = new PolarAreaSegment(linePoints, [], currentSeries, seriesIx);
|
|
}
|
|
return segment;
|
|
},
|
|
createMissingValue: function (value, missingValues) {
|
|
var missingValue;
|
|
if (dataviz.hasValue(value.x) && missingValues != INTERPOLATE) {
|
|
missingValue = {
|
|
x: value.x,
|
|
y: value.y
|
|
};
|
|
if (missingValues == ZERO) {
|
|
missingValue.y = 0;
|
|
}
|
|
}
|
|
return missingValue;
|
|
},
|
|
seriesMissingValues: function (series) {
|
|
return series.missingValues || ZERO;
|
|
},
|
|
_hasMissingValuesGap: function () {
|
|
var series = this.options.series;
|
|
for (var idx = 0; idx < series.length; idx++) {
|
|
if (this.seriesMissingValues(series[idx]) === GAP) {
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
sortPoints: function (points) {
|
|
var value, point;
|
|
points.sort(xComparer);
|
|
if (this._hasMissingValuesGap()) {
|
|
for (var idx = 0; idx < points.length; idx++) {
|
|
point = points[idx];
|
|
if (point) {
|
|
value = point.value;
|
|
if (!dataviz.hasValue(value.y) && this.seriesMissingValues(point.series) === GAP) {
|
|
delete points[idx];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return points;
|
|
}
|
|
});
|
|
var PolarPlotAreaBase = PlotAreaBase.extend({
|
|
init: function (series, options) {
|
|
var plotArea = this;
|
|
plotArea.valueAxisRangeTracker = new AxisGroupRangeTracker();
|
|
PlotAreaBase.fn.init.call(plotArea, series, options);
|
|
},
|
|
render: function () {
|
|
var plotArea = this;
|
|
plotArea.addToLegend(plotArea.series);
|
|
plotArea.createPolarAxis();
|
|
plotArea.createCharts();
|
|
plotArea.createValueAxis();
|
|
},
|
|
alignAxes: function () {
|
|
var axis = this.valueAxis;
|
|
var range = axis.range();
|
|
var crossingValue = axis.options.reverse ? range.max : range.min;
|
|
var slot = axis.getSlot(crossingValue);
|
|
var center = this.polarAxis.getSlot(0).c;
|
|
var axisBox = axis.box.translate(center.x - slot.x1, center.y - slot.y1);
|
|
axis.reflow(axisBox);
|
|
},
|
|
createValueAxis: function () {
|
|
var plotArea = this, tracker = plotArea.valueAxisRangeTracker, defaultRange = tracker.query(), range, valueAxis, axisOptions = plotArea.valueAxisOptions({
|
|
roundToMajorUnit: false,
|
|
zIndex: -1
|
|
}), axisType, axisDefaultRange;
|
|
if (axisOptions.type === LOGARITHMIC) {
|
|
axisType = RadarLogarithmicAxis;
|
|
axisDefaultRange = {
|
|
min: 0.1,
|
|
max: 1
|
|
};
|
|
} else {
|
|
axisType = RadarNumericAxis;
|
|
axisDefaultRange = {
|
|
min: 0,
|
|
max: 1
|
|
};
|
|
}
|
|
range = tracker.query(name) || defaultRange || axisDefaultRange;
|
|
if (range && defaultRange) {
|
|
range.min = math.min(range.min, defaultRange.min);
|
|
range.max = math.max(range.max, defaultRange.max);
|
|
}
|
|
valueAxis = new axisType(range.min, range.max, axisOptions);
|
|
plotArea.valueAxis = valueAxis;
|
|
plotArea.appendAxis(valueAxis);
|
|
},
|
|
reflowAxes: function () {
|
|
var plotArea = this, options = plotArea.options.plotArea, valueAxis = plotArea.valueAxis, polarAxis = plotArea.polarAxis, box = plotArea.box, defaultPadding = math.min(box.width(), box.height()) * DEFAULT_PADDING, padding = getSpacing(options.padding || {}, defaultPadding), axisBox = box.clone().unpad(padding), valueAxisBox = axisBox.clone().shrink(0, axisBox.height() / 2);
|
|
polarAxis.reflow(axisBox);
|
|
valueAxis.reflow(valueAxisBox);
|
|
var heightDiff = valueAxis.lineBox().height() - valueAxis.box.height();
|
|
valueAxis.reflow(valueAxis.box.unpad({ top: heightDiff }));
|
|
plotArea.axisBox = axisBox;
|
|
plotArea.alignAxes(axisBox);
|
|
},
|
|
backgroundBox: function () {
|
|
return this.box;
|
|
}
|
|
});
|
|
var RadarPlotArea = PolarPlotAreaBase.extend({
|
|
options: {
|
|
categoryAxis: { categories: [] },
|
|
valueAxis: {}
|
|
},
|
|
createPolarAxis: function () {
|
|
var plotArea = this, categoryAxis;
|
|
categoryAxis = new RadarCategoryAxis(plotArea.options.categoryAxis);
|
|
plotArea.polarAxis = categoryAxis;
|
|
plotArea.categoryAxis = categoryAxis;
|
|
plotArea.appendAxis(categoryAxis);
|
|
plotArea.aggregateCategories();
|
|
},
|
|
valueAxisOptions: function (defaults) {
|
|
var plotArea = this;
|
|
if (plotArea._hasBarCharts) {
|
|
deepExtend(defaults, {
|
|
majorGridLines: { type: ARC },
|
|
minorGridLines: { type: ARC }
|
|
});
|
|
}
|
|
if (plotArea._isStacked100) {
|
|
deepExtend(defaults, {
|
|
roundToMajorUnit: false,
|
|
labels: { format: 'P0' }
|
|
});
|
|
}
|
|
return deepExtend(defaults, plotArea.options.valueAxis);
|
|
},
|
|
appendChart: CategoricalPlotArea.fn.appendChart,
|
|
aggregateSeries: CategoricalPlotArea.fn.aggregateSeries,
|
|
aggregateCategories: function () {
|
|
CategoricalPlotArea.fn.aggregateCategories.call(this, this.panes);
|
|
},
|
|
filterSeries: function (currentSeries) {
|
|
return currentSeries;
|
|
},
|
|
createCharts: function () {
|
|
var plotArea = this, series = plotArea.filterVisibleSeries(plotArea.series), pane = plotArea.panes[0];
|
|
plotArea.createAreaChart(filterSeriesByType(series, [RADAR_AREA]), pane);
|
|
plotArea.createLineChart(filterSeriesByType(series, [RADAR_LINE]), pane);
|
|
plotArea.createBarChart(filterSeriesByType(series, [RADAR_COLUMN]), pane);
|
|
},
|
|
chartOptions: function (series) {
|
|
var options = { series: series };
|
|
var firstSeries = series[0];
|
|
if (firstSeries) {
|
|
var filteredSeries = this.filterVisibleSeries(series);
|
|
var stack = firstSeries.stack;
|
|
options.isStacked = stack && filteredSeries.length > 1;
|
|
options.isStacked100 = stack && stack.type === '100%' && filteredSeries.length > 1;
|
|
if (options.isStacked100) {
|
|
this._isStacked100 = true;
|
|
}
|
|
}
|
|
return options;
|
|
},
|
|
createAreaChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var areaChart = new RadarAreaChart(this, this.chartOptions(series));
|
|
this.appendChart(areaChart, pane);
|
|
},
|
|
createLineChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var lineChart = new RadarLineChart(this, this.chartOptions(series));
|
|
this.appendChart(lineChart, pane);
|
|
},
|
|
createBarChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var firstSeries = series[0];
|
|
var options = this.chartOptions(series);
|
|
options.gap = firstSeries.gap;
|
|
options.spacing = firstSeries.spacing;
|
|
var barChart = new RadarBarChart(this, options);
|
|
this.appendChart(barChart, pane);
|
|
this._hasBarCharts = true;
|
|
},
|
|
seriesCategoryAxis: function () {
|
|
return this.categoryAxis;
|
|
},
|
|
click: function (chart, e) {
|
|
var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), category, value;
|
|
category = plotArea.categoryAxis.getCategory(point);
|
|
value = plotArea.valueAxis.getValue(point);
|
|
if (category !== null && value !== null) {
|
|
chart.trigger(PLOT_AREA_CLICK, {
|
|
element: $(e.target),
|
|
category: category,
|
|
value: value
|
|
});
|
|
}
|
|
},
|
|
createCrosshairs: $.noop
|
|
});
|
|
var PolarPlotArea = PolarPlotAreaBase.extend({
|
|
options: {
|
|
xAxis: {},
|
|
yAxis: {}
|
|
},
|
|
createPolarAxis: function () {
|
|
var plotArea = this, polarAxis;
|
|
polarAxis = new PolarAxis(plotArea.options.xAxis);
|
|
plotArea.polarAxis = polarAxis;
|
|
plotArea.axisX = polarAxis;
|
|
plotArea.appendAxis(polarAxis);
|
|
},
|
|
valueAxisOptions: function (defaults) {
|
|
var plotArea = this;
|
|
return deepExtend(defaults, {
|
|
majorGridLines: { type: ARC },
|
|
minorGridLines: { type: ARC }
|
|
}, plotArea.options.yAxis);
|
|
},
|
|
createValueAxis: function () {
|
|
var plotArea = this;
|
|
PolarPlotAreaBase.fn.createValueAxis.call(plotArea);
|
|
plotArea.axisY = plotArea.valueAxis;
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
var plotArea = this;
|
|
plotArea.valueAxisRangeTracker.update(chart.yAxisRanges);
|
|
PlotAreaBase.fn.appendChart.call(plotArea, chart, pane);
|
|
},
|
|
createCharts: function () {
|
|
var plotArea = this, series = plotArea.filterVisibleSeries(plotArea.series), pane = plotArea.panes[0];
|
|
plotArea.createLineChart(filterSeriesByType(series, [POLAR_LINE]), pane);
|
|
plotArea.createScatterChart(filterSeriesByType(series, [POLAR_SCATTER]), pane);
|
|
plotArea.createAreaChart(filterSeriesByType(series, [POLAR_AREA]), pane);
|
|
},
|
|
createLineChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, lineChart = new PolarLineChart(plotArea, { series: series });
|
|
plotArea.appendChart(lineChart, pane);
|
|
},
|
|
createScatterChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, scatterChart = new PolarScatterChart(plotArea, { series: series });
|
|
plotArea.appendChart(scatterChart, pane);
|
|
},
|
|
createAreaChart: function (series, pane) {
|
|
if (series.length === 0) {
|
|
return;
|
|
}
|
|
var plotArea = this, areaChart = new PolarAreaChart(plotArea, { series: series });
|
|
plotArea.appendChart(areaChart, pane);
|
|
},
|
|
click: function (chart, e) {
|
|
var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), xValue, yValue;
|
|
xValue = plotArea.axisX.getValue(point);
|
|
yValue = plotArea.axisY.getValue(point);
|
|
if (xValue !== null && yValue !== null) {
|
|
chart.trigger(PLOT_AREA_CLICK, {
|
|
element: $(e.target),
|
|
x: xValue,
|
|
y: yValue
|
|
});
|
|
}
|
|
},
|
|
createCrosshairs: $.noop
|
|
});
|
|
function xComparer(a, b) {
|
|
return a.value.x - b.value.x;
|
|
}
|
|
function angularDistance(a, b) {
|
|
return 180 - math.abs(math.abs(a - b) - 180);
|
|
}
|
|
PlotAreaFactory.current.register(PolarPlotArea, POLAR_CHARTS);
|
|
PlotAreaFactory.current.register(RadarPlotArea, RADAR_CHARTS);
|
|
SeriesBinder.current.register(POLAR_CHARTS, [
|
|
X,
|
|
Y
|
|
], ['color']);
|
|
SeriesBinder.current.register(RADAR_CHARTS, ['value'], ['color']);
|
|
dataviz.DefaultAggregates.current.register(RADAR_CHARTS, {
|
|
value: 'max',
|
|
color: 'first'
|
|
});
|
|
deepExtend(dataviz, {
|
|
PolarAreaChart: PolarAreaChart,
|
|
PolarAxis: PolarAxis,
|
|
PolarLineChart: PolarLineChart,
|
|
PolarPlotArea: PolarPlotArea,
|
|
RadarAreaChart: RadarAreaChart,
|
|
RadarBarChart: RadarBarChart,
|
|
RadarCategoryAxis: RadarCategoryAxis,
|
|
RadarClusterLayout: RadarClusterLayout,
|
|
RadarLineChart: RadarLineChart,
|
|
RadarNumericAxis: RadarNumericAxis,
|
|
RadarPlotArea: RadarPlotArea,
|
|
SplinePolarAreaSegment: SplinePolarAreaSegment,
|
|
SplineRadarAreaSegment: SplineRadarAreaSegment,
|
|
RadarStackLayout: RadarStackLayout
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.chart.funnel', [
|
|
'kendo.dataviz.chart',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.chart.funnel',
|
|
name: 'Funnel Chart',
|
|
category: 'dataviz',
|
|
depends: ['dataviz.chart'],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, deepExtend = kendo.deepExtend, extend = $.extend, isFn = kendo.isFunction, template = kendo.template, util = kendo.util, append = util.append, draw = kendo.drawing, geom = kendo.geometry, dataviz = kendo.dataviz, Color = kendo.drawing.Color, ChartElement = dataviz.ChartElement, PieChartMixin = dataviz.PieChartMixin, PlotAreaBase = dataviz.PlotAreaBase, PlotAreaFactory = dataviz.PlotAreaFactory, Point2D = dataviz.Point2D, Box2D = dataviz.Box2D, SeriesBinder = dataviz.SeriesBinder, TextBox = dataviz.TextBox, autoFormat = dataviz.autoFormat, evalOptions = dataviz.evalOptions, limitValue = util.limitValue, seriesTotal = dataviz.seriesTotal;
|
|
var CATEGORY = 'category', COLOR = 'color', FUNNEL = 'funnel', VALUE = 'value', BLACK = 'black', WHITE = 'white';
|
|
var FunnelPlotArea = PlotAreaBase.extend({
|
|
render: function () {
|
|
var plotArea = this, series = plotArea.series;
|
|
plotArea.createFunnelChart(series);
|
|
},
|
|
createFunnelChart: function (series) {
|
|
var plotArea = this, firstSeries = series[0], funnelChart = new FunnelChart(plotArea, {
|
|
series: series,
|
|
legend: plotArea.options.legend,
|
|
neckRatio: firstSeries.neckRatio,
|
|
dynamicHeight: firstSeries.dynamicHeight,
|
|
dynamicSlope: firstSeries.dynamicSlope,
|
|
segmentSpacing: firstSeries.segmentSpacing,
|
|
highlight: firstSeries.highlight
|
|
});
|
|
plotArea.appendChart(funnelChart);
|
|
},
|
|
appendChart: function (chart, pane) {
|
|
PlotAreaBase.fn.appendChart.call(this, chart, pane);
|
|
append(this.options.legend.items, chart.legendItems);
|
|
}
|
|
});
|
|
var FunnelChart = ChartElement.extend({
|
|
init: function (plotArea, options) {
|
|
var chart = this;
|
|
ChartElement.fn.init.call(chart, options);
|
|
chart.plotArea = plotArea;
|
|
chart.points = [];
|
|
chart.labels = [];
|
|
chart.legendItems = [];
|
|
chart.render();
|
|
},
|
|
options: {
|
|
neckRatio: 0.3,
|
|
width: 300,
|
|
dynamicSlope: false,
|
|
dynamicHeight: true,
|
|
segmentSpacing: 0,
|
|
labels: {
|
|
visible: false,
|
|
align: 'center',
|
|
position: 'center'
|
|
}
|
|
},
|
|
formatPointValue: function (point, format) {
|
|
return autoFormat(format, point.value);
|
|
},
|
|
render: function () {
|
|
var chart = this, options = chart.options, colors = chart.plotArea.options.seriesColors || [], colorsCount = colors.length, series = options.series[0], pointData, fields, data = series.data;
|
|
if (!data) {
|
|
return;
|
|
}
|
|
var total = seriesTotal(series), value, i;
|
|
for (i = 0; i < data.length; i++) {
|
|
pointData = SeriesBinder.current.bindPoint(series, i);
|
|
value = pointData.valueFields.value;
|
|
if (value === null || value === undefined) {
|
|
continue;
|
|
}
|
|
fields = pointData.fields;
|
|
if (!isFn(series.color)) {
|
|
series.color = fields.color || colors[i % colorsCount];
|
|
}
|
|
fields = deepExtend({
|
|
index: i,
|
|
owner: chart,
|
|
series: series,
|
|
category: fields.category,
|
|
dataItem: data[i],
|
|
percentage: Math.abs(value) / total,
|
|
visibleInLegend: fields.visibleInLegend,
|
|
visible: fields.visible
|
|
}, fields);
|
|
var segment = chart.createSegment(value, fields);
|
|
var label = chart.createLabel(value, fields);
|
|
if (segment && label) {
|
|
segment.append(label);
|
|
}
|
|
}
|
|
},
|
|
evalSegmentOptions: function (options, value, fields) {
|
|
var series = fields.series;
|
|
evalOptions(options, {
|
|
value: value,
|
|
series: series,
|
|
dataItem: fields.dataItem,
|
|
index: fields.index
|
|
}, {
|
|
defaults: series._defaults,
|
|
excluded: [
|
|
'data',
|
|
'toggle',
|
|
'visual'
|
|
]
|
|
});
|
|
},
|
|
createSegment: function (value, fields) {
|
|
var chart = this, segment;
|
|
var seriesOptions = deepExtend({}, fields.series);
|
|
chart.evalSegmentOptions(seriesOptions, value, fields);
|
|
chart.createLegendItem(value, seriesOptions, fields);
|
|
if (fields.visible !== false) {
|
|
segment = new FunnelSegment(value, seriesOptions, fields);
|
|
extend(segment, fields);
|
|
chart.append(segment);
|
|
chart.points.push(segment);
|
|
return segment;
|
|
}
|
|
},
|
|
createLabel: function (value, fields) {
|
|
var chart = this, series = fields.series, dataItem = fields.dataItem, labels = deepExtend({}, chart.options.labels, series.labels), text = value, textBox;
|
|
if (labels.visible) {
|
|
if (labels.template) {
|
|
var labelTemplate = template(labels.template);
|
|
text = labelTemplate({
|
|
dataItem: dataItem,
|
|
value: value,
|
|
percentage: fields.percentage,
|
|
category: fields.category,
|
|
series: series
|
|
});
|
|
} else if (labels.format) {
|
|
text = autoFormat(labels.format, text);
|
|
}
|
|
if (!labels.color && labels.align === 'center') {
|
|
var brightnessValue = new Color(series.color).percBrightness();
|
|
if (brightnessValue > 180) {
|
|
labels.color = BLACK;
|
|
} else {
|
|
labels.color = WHITE;
|
|
}
|
|
}
|
|
chart.evalSegmentOptions(labels, value, fields);
|
|
textBox = new TextBox(text, deepExtend({ vAlign: labels.position }, labels));
|
|
chart.labels.push(textBox);
|
|
return textBox;
|
|
}
|
|
},
|
|
labelPadding: function () {
|
|
var labels = this.labels, label, align, width, padding = {
|
|
left: 0,
|
|
right: 0
|
|
}, i;
|
|
for (i = 0; i < labels.length; i++) {
|
|
label = labels[i];
|
|
align = label.options.align;
|
|
if (align !== 'center') {
|
|
width = labels[i].box.width();
|
|
if (align === 'left') {
|
|
padding.left = Math.max(padding.left, width);
|
|
} else {
|
|
padding.right = Math.max(padding.right, width);
|
|
}
|
|
}
|
|
}
|
|
return padding;
|
|
},
|
|
reflow: function (chartBox) {
|
|
var chart = this, options = chart.options, segments = chart.points, count = segments.length, decreasingWidth = options.neckRatio <= 1, i, height, lastUpperSide, points, percentage, offset, box = chartBox.clone().unpad(chart.labelPadding()), width = box.width(), previousHeight = 0, previousOffset = decreasingWidth ? 0 : (width - width / options.neckRatio) / 2, segmentSpacing = options.segmentSpacing, dynamicSlope = options.dynamicSlope, totalHeight = box.height() - segmentSpacing * (count - 1), neckRatio = decreasingWidth ? options.neckRatio * width : width;
|
|
if (!count) {
|
|
return;
|
|
}
|
|
if (dynamicSlope) {
|
|
var firstSegment = segments[0], maxSegment = firstSegment, nextSegment, nextPercentage;
|
|
$.each(segments, function (idx, val) {
|
|
if (val.percentage > maxSegment.percentage) {
|
|
maxSegment = val;
|
|
}
|
|
});
|
|
lastUpperSide = firstSegment.percentage / maxSegment.percentage * width;
|
|
previousOffset = (width - lastUpperSide) / 2;
|
|
for (i = 0; i < count; i++) {
|
|
percentage = segments[i].percentage;
|
|
nextSegment = segments[i + 1];
|
|
nextPercentage = nextSegment ? nextSegment.percentage : percentage;
|
|
points = segments[i].points = [];
|
|
height = options.dynamicHeight ? totalHeight * percentage : totalHeight / count;
|
|
if (!percentage) {
|
|
offset = nextPercentage ? 0 : width / 2;
|
|
} else {
|
|
offset = (width - lastUpperSide * (nextPercentage / percentage)) / 2;
|
|
}
|
|
offset = limitValue(offset, 0, width);
|
|
points.push(new geom.Point(box.x1 + previousOffset, box.y1 + previousHeight));
|
|
points.push(new geom.Point(box.x1 + width - previousOffset, box.y1 + previousHeight));
|
|
points.push(new geom.Point(box.x1 + width - offset, box.y1 + height + previousHeight));
|
|
points.push(new geom.Point(box.x1 + offset, box.y1 + height + previousHeight));
|
|
previousOffset = offset;
|
|
previousHeight += height + segmentSpacing;
|
|
lastUpperSide = limitValue(width - 2 * offset, 0, width);
|
|
}
|
|
} else {
|
|
var topMostWidth = decreasingWidth ? width : width - previousOffset * 2, finalNarrow = (topMostWidth - neckRatio) / 2;
|
|
for (i = 0; i < count; i++) {
|
|
points = segments[i].points = [];
|
|
percentage = segments[i].percentage;
|
|
offset = options.dynamicHeight ? finalNarrow * percentage : finalNarrow / count;
|
|
height = options.dynamicHeight ? totalHeight * percentage : totalHeight / count;
|
|
points.push(new geom.Point(box.x1 + previousOffset, box.y1 + previousHeight));
|
|
points.push(new geom.Point(box.x1 + width - previousOffset, box.y1 + previousHeight));
|
|
points.push(new geom.Point(box.x1 + width - previousOffset - offset, box.y1 + height + previousHeight));
|
|
points.push(new geom.Point(box.x1 + previousOffset + offset, box.y1 + height + previousHeight));
|
|
previousOffset += offset;
|
|
previousHeight += height + segmentSpacing;
|
|
}
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
segments[i].reflow(chartBox);
|
|
}
|
|
}
|
|
});
|
|
deepExtend(FunnelChart.fn, PieChartMixin);
|
|
var FunnelSegment = ChartElement.extend({
|
|
init: function (value, options, segmentOptions) {
|
|
var segment = this;
|
|
ChartElement.fn.init.call(segment, options);
|
|
segment.value = value;
|
|
segment.options.index = segmentOptions.index;
|
|
},
|
|
options: {
|
|
color: WHITE,
|
|
border: { width: 1 }
|
|
},
|
|
reflow: function (chartBox) {
|
|
var segment = this, points = segment.points, label = segment.children[0];
|
|
segment.box = new Box2D(points[0].x, points[0].y, points[1].x, points[2].y);
|
|
if (label) {
|
|
label.reflow(new Box2D(chartBox.x1, points[0].y, chartBox.x2, points[2].y));
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var segment = this;
|
|
var options = segment.options;
|
|
var visual;
|
|
ChartElement.fn.createVisual.call(this);
|
|
if (options.visual) {
|
|
visual = options.visual({
|
|
category: segment.category,
|
|
dataItem: segment.dataItem,
|
|
value: segment.value,
|
|
series: segment.series,
|
|
percentage: segment.percentage,
|
|
points: segment.points,
|
|
options: options,
|
|
createVisual: function () {
|
|
return segment.createPath();
|
|
}
|
|
});
|
|
} else {
|
|
visual = segment.createPath();
|
|
}
|
|
if (visual) {
|
|
this.visual.append(visual);
|
|
}
|
|
},
|
|
createPath: function () {
|
|
var options = this.options;
|
|
var border = options.border;
|
|
var path = draw.Path.fromPoints(this.points, {
|
|
fill: {
|
|
color: options.color,
|
|
opacity: options.opacity
|
|
},
|
|
stroke: {
|
|
color: border.color,
|
|
opacity: border.opacity,
|
|
width: border.width
|
|
}
|
|
}).close();
|
|
return path;
|
|
},
|
|
createHighlight: function (style) {
|
|
return draw.Path.fromPoints(this.points, style);
|
|
},
|
|
highlightVisual: function () {
|
|
return this.visual.children[0];
|
|
},
|
|
highlightVisualArgs: function () {
|
|
var path = draw.Path.fromPoints(this.points).close();
|
|
return {
|
|
options: this.options,
|
|
path: path
|
|
};
|
|
},
|
|
highlightOverlay: function (view, opt) {
|
|
var options = this.options, hlOptions = options.highlight || {};
|
|
if (hlOptions.visible === false) {
|
|
return;
|
|
}
|
|
var border = hlOptions.border || {};
|
|
var calcOptions = extend({}, opt, {
|
|
fill: hlOptions.color,
|
|
stroke: border.color,
|
|
strokeOpacity: border.opacity,
|
|
strokeWidth: border.width,
|
|
fillOpacity: hlOptions.opacity
|
|
});
|
|
var element = view.createPolyline(this.points, true, calcOptions);
|
|
return element;
|
|
},
|
|
tooltipAnchor: function (tooltipWidth) {
|
|
var box = this.box;
|
|
return new Point2D(box.center().x - tooltipWidth / 2, box.y1);
|
|
},
|
|
formatValue: function (format) {
|
|
var point = this;
|
|
return point.owner.formatPointValue(point, format);
|
|
}
|
|
});
|
|
deepExtend(FunnelSegment.fn, dataviz.PointEventsMixin);
|
|
PlotAreaFactory.current.register(FunnelPlotArea, [FUNNEL]);
|
|
SeriesBinder.current.register([FUNNEL], [VALUE], [
|
|
CATEGORY,
|
|
COLOR,
|
|
'visibleInLegend',
|
|
'visible'
|
|
]);
|
|
deepExtend(dataviz, {
|
|
FunnelChart: FunnelChart,
|
|
FunnelSegment: FunnelSegment
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.gauge', [
|
|
'kendo.dataviz.core',
|
|
'kendo.drawing',
|
|
'kendo.dataviz.themes'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.gauge',
|
|
name: 'Gauge',
|
|
category: 'dataviz',
|
|
description: 'Radial and Linear gauges.',
|
|
depends: [
|
|
'dataviz.core',
|
|
'dataviz.themes'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var math = Math, kendo = window.kendo, util = kendo.util, Widget = kendo.ui.Widget, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, autoMajorUnit = dataviz.autoMajorUnit, ChartElement = dataviz.ChartElement, NumericAxis = dataviz.NumericAxis, Axis = dataviz.Axis, Box2D = dataviz.Box2D, Class = kendo.Class, defined = util.defined, isNumber = util.isNumber, interpolateValue = dataviz.interpolateValue, getSpacing = dataviz.getSpacing, round = dataviz.round, geo = dataviz.geometry, draw = dataviz.drawing, Point = geo.Point, Group = draw.Group, Path = draw.Path, Rect = geo.Rect, Text = draw.Text;
|
|
var ANGULAR_SPEED = 150, LINEAR_SPEED = 250, ARROW = 'arrow', ARROW_POINTER = 'arrowPointer', BAR_POINTER = 'barPointer', BLACK = '#000', CAP_SIZE = 0.05, COORD_PRECISION = dataviz.COORD_PRECISION, MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, DEFAULT_HEIGHT = 200, DEFAULT_LINE_WIDTH = 0.5, DEFAULT_WIDTH = 200, DEFAULT_MIN_WIDTH = 60, DEFAULT_MIN_HEIGHT = 60, DEFAULT_MARGIN = 5, DEGREE = math.PI / 180, GEO_ARC_ADJUST_ANGLE = 180, INSIDE = 'inside', LINEAR = 'linear', NEEDLE = 'needle', OUTSIDE = 'outside', RADIAL_POINTER = 'radialPointer', X = 'x', Y = 'y';
|
|
var Pointer = Class.extend({
|
|
init: function (scale, options) {
|
|
var pointer = this;
|
|
var scaleOptions = scale.options;
|
|
ChartElement.fn.init.call(pointer, options);
|
|
options = pointer.options;
|
|
options.fill = options.color;
|
|
pointer.scale = scale;
|
|
if (defined(options.value)) {
|
|
options.value = math.min(math.max(options.value, scaleOptions.min), scaleOptions.max);
|
|
} else {
|
|
options.value = scaleOptions.min;
|
|
}
|
|
},
|
|
options: { color: BLACK },
|
|
value: function (newValue) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var value = options.value;
|
|
var scaleOptions = that.scale.options;
|
|
if (arguments.length === 0) {
|
|
return value;
|
|
}
|
|
options._oldValue = options._oldValue !== undefined ? options.value : scaleOptions.min;
|
|
options.value = math.min(math.max(newValue, scaleOptions.min), scaleOptions.max);
|
|
if (that.elements) {
|
|
that.repaint();
|
|
}
|
|
}
|
|
});
|
|
var RadialPointer = Pointer.extend({
|
|
options: {
|
|
shape: NEEDLE,
|
|
cap: { size: CAP_SIZE },
|
|
arrow: {
|
|
width: 16,
|
|
height: 14
|
|
},
|
|
animation: {
|
|
type: RADIAL_POINTER,
|
|
duration: ANGULAR_SPEED
|
|
}
|
|
},
|
|
setRadius: function (radius) {
|
|
var that = this;
|
|
if (radius) {
|
|
that.elements.clear();
|
|
that.render(that.parent, that.center, radius);
|
|
}
|
|
},
|
|
setAngle: function (angle) {
|
|
this.elements.transform(geo.transform().rotate(angle, this.center));
|
|
},
|
|
repaint: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var options = that.options;
|
|
var oldAngle = scale.slotAngle(options._oldValue);
|
|
var newAngle = scale.slotAngle(options.value);
|
|
if (options.animation.transitions === false) {
|
|
that.setAngle(newAngle);
|
|
} else {
|
|
new RadialPointerAnimation(that.elements, deepExtend(options.animation, {
|
|
oldAngle: oldAngle,
|
|
newAngle: newAngle
|
|
})).play();
|
|
}
|
|
},
|
|
render: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var center = scale.arc.center;
|
|
var options = that.options;
|
|
var elements = new Group();
|
|
if (options.animation !== false) {
|
|
deepExtend(options.animation, {
|
|
startAngle: 0,
|
|
center: center,
|
|
reverse: scale.options.reverse
|
|
});
|
|
}
|
|
if (options.shape === NEEDLE) {
|
|
elements.append(that._renderNeedle(), that._renderCap());
|
|
} else {
|
|
elements.append(that._renderArrow());
|
|
}
|
|
that.elements = elements;
|
|
that.setAngle(DEGREE);
|
|
return elements;
|
|
},
|
|
reflow: function (arc) {
|
|
var that = this;
|
|
var center = that.center = arc.center;
|
|
var radius = that.radius = arc.getRadiusX();
|
|
var capSize = that.capSize = Math.round(radius * that.options.cap.size);
|
|
that.bbox = Rect.fromPoints(new Point(center.x - capSize, center.y - capSize), new Point(center.x + capSize, center.y + capSize));
|
|
},
|
|
_renderNeedle: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var minorTickSize = that.scale.options.minorTicks.size;
|
|
var center = that.center;
|
|
var needleColor = options.color;
|
|
var needlePath = new Path({
|
|
fill: { color: needleColor },
|
|
stroke: {
|
|
color: needleColor,
|
|
width: DEFAULT_LINE_WIDTH
|
|
}
|
|
});
|
|
needlePath.moveTo(center.x + that.radius - minorTickSize, center.y).lineTo(center.x, center.y - that.capSize / 2).lineTo(center.x, center.y + that.capSize / 2).close();
|
|
return needlePath;
|
|
},
|
|
_renderCap: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var capColor = options.cap.color || options.color;
|
|
var circle = new geo.Circle(that.center, that.capSize);
|
|
var cap = new draw.Circle(circle, {
|
|
fill: { color: capColor },
|
|
stroke: { color: capColor }
|
|
});
|
|
return cap;
|
|
}
|
|
});
|
|
var RadialScale = NumericAxis.extend({
|
|
init: function (options) {
|
|
var scale = this;
|
|
scale.options = deepExtend({}, scale.options, options);
|
|
scale.options.majorUnit = scale.options.majorUnit || autoMajorUnit(scale.options.min, scale.options.max);
|
|
scale.options.minorUnit = scale.options.minorUnit || scale.options.majorUnit / 10;
|
|
Axis.fn.init.call(scale, scale.options);
|
|
},
|
|
options: {
|
|
min: 0,
|
|
max: 100,
|
|
majorTicks: {
|
|
size: 15,
|
|
align: INSIDE,
|
|
color: BLACK,
|
|
width: DEFAULT_LINE_WIDTH,
|
|
visible: true
|
|
},
|
|
minorTicks: {
|
|
size: 10,
|
|
align: INSIDE,
|
|
color: BLACK,
|
|
width: DEFAULT_LINE_WIDTH,
|
|
visible: true
|
|
},
|
|
startAngle: -30,
|
|
endAngle: 210,
|
|
labels: {
|
|
position: INSIDE,
|
|
padding: 2
|
|
}
|
|
},
|
|
render: function (center, radius) {
|
|
var that = this;
|
|
var arc = that.renderArc(center, radius);
|
|
that.bbox = arc.bbox();
|
|
that.labelElements = that.renderLabels();
|
|
that.ticks = that.renderTicks();
|
|
that.ranges = that.renderRanges();
|
|
},
|
|
reflow: function (bbox) {
|
|
var that = this;
|
|
var center = bbox.center();
|
|
var radius = math.min(bbox.height(), bbox.width()) / 2;
|
|
if (that.bbox !== undefined) {
|
|
that.bbox = that.arc.bbox();
|
|
that.radius(that.arc.getRadiusX());
|
|
that.repositionRanges();
|
|
that.renderLabels();
|
|
} else {
|
|
return that.render(center, radius);
|
|
}
|
|
},
|
|
slotAngle: function (value) {
|
|
var options = this.options;
|
|
var startAngle = options.startAngle;
|
|
var reverse = options.reverse;
|
|
var angle = options.endAngle - startAngle;
|
|
var min = options.min;
|
|
var max = options.max;
|
|
var result;
|
|
if (reverse) {
|
|
result = options.endAngle - (value - min) / (max - min) * angle;
|
|
} else {
|
|
result = (value - min) / (max - min) * angle + startAngle;
|
|
}
|
|
return result + GEO_ARC_ADJUST_ANGLE;
|
|
},
|
|
renderLabels: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var majorTickSize = options.majorTicks.size;
|
|
var arc = that.arc.clone();
|
|
var radius = arc.getRadiusX();
|
|
var tickAngles = that.tickAngles(arc, options.majorUnit);
|
|
var labels = that.labels;
|
|
var count = labels.length;
|
|
var labelsOptions = options.labels;
|
|
var padding = labelsOptions.padding;
|
|
var rangeDistance = radius * 0.05;
|
|
var rangeSize = options.rangeSize = options.rangeSize || radius * 0.1;
|
|
var ranges = options.ranges || [];
|
|
var halfWidth, halfHeight, labelAngle;
|
|
var angle, label, lp, i, cx, cy, isInside;
|
|
var labelsGroup = new Group();
|
|
var lbl, labelPos, prevLabelPos, labelTransform;
|
|
if (that.options.rangeDistance !== undefined) {
|
|
rangeDistance = that.options.rangeDistance;
|
|
} else {
|
|
that.options.rangeDistance = rangeDistance;
|
|
}
|
|
if (labelsOptions.position === INSIDE) {
|
|
radius -= majorTickSize;
|
|
if (ranges.length && that.labelElements === undefined) {
|
|
radius -= rangeSize + rangeDistance;
|
|
}
|
|
arc.setRadiusX(radius).setRadiusY(radius);
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
label = labels[i];
|
|
halfWidth = label.box.width() / 2;
|
|
halfHeight = label.box.height() / 2;
|
|
angle = tickAngles[i];
|
|
labelAngle = (angle - GEO_ARC_ADJUST_ANGLE) * DEGREE;
|
|
isInside = labelsOptions.position === INSIDE;
|
|
lp = arc.pointAt(angle);
|
|
cx = lp.x + math.cos(labelAngle) * (halfWidth + padding) * (isInside ? 1 : -1);
|
|
cy = lp.y + math.sin(labelAngle) * (halfHeight + padding) * (isInside ? 1 : -1);
|
|
label.reflow(new dataviz.Box2D(cx - halfWidth, cy - halfHeight, cx + halfWidth, cy + halfHeight));
|
|
labelPos = new Point(label.box.x1, label.box.y1);
|
|
if (that.labelElements === undefined) {
|
|
lbl = _buildLabel(label, options.labels);
|
|
labelsGroup.append(lbl);
|
|
} else {
|
|
lbl = that.labelElements.children[i];
|
|
prevLabelPos = lbl.bbox().origin;
|
|
labelTransform = lbl.transform() || geo.transform();
|
|
labelTransform.translate(labelPos.x - prevLabelPos.x, labelPos.y - prevLabelPos.y);
|
|
lbl.transform(labelTransform);
|
|
}
|
|
that.bbox = Rect.union(that.bbox, lbl.bbox());
|
|
}
|
|
return labelsGroup;
|
|
},
|
|
repositionRanges: function () {
|
|
var that = this;
|
|
var ranges = that.ranges.children;
|
|
var rangeSize = that.options.rangeSize;
|
|
var rangeDistance = that.options.rangeDistance;
|
|
var rangeRadius, newRadius;
|
|
if (ranges.length > 0) {
|
|
rangeRadius = that.getRangeRadius();
|
|
if (that.options.labels.position === INSIDE) {
|
|
rangeRadius += rangeSize + rangeDistance;
|
|
}
|
|
newRadius = rangeRadius + rangeSize / 2;
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
ranges[i]._geometry.setRadiusX(newRadius).setRadiusY(newRadius);
|
|
}
|
|
that.bbox = Rect.union(that.bbox, that.ranges.bbox());
|
|
}
|
|
},
|
|
renderRanges: function () {
|
|
var that = this;
|
|
var arc = that.arc;
|
|
var result = new Group();
|
|
var from, to;
|
|
var segments = that.rangeSegments();
|
|
var segmentsCount = segments.length;
|
|
var reverse = that.options.reverse;
|
|
var rangeSize = that.options.rangeSize;
|
|
var rangeDistance = that.options.rangeDistance;
|
|
var segment, rangeRadius, rangeGeom, i;
|
|
if (segmentsCount) {
|
|
rangeRadius = that.getRangeRadius();
|
|
that.radius(that.radius() - rangeSize - rangeDistance);
|
|
for (i = 0; i < segmentsCount; i++) {
|
|
segment = segments[i];
|
|
from = that.slotAngle(segment[reverse ? 'to' : 'from']);
|
|
to = that.slotAngle(segment[!reverse ? 'to' : 'from']);
|
|
if (to - from !== 0) {
|
|
rangeGeom = new geo.Arc(arc.center, {
|
|
radiusX: rangeRadius + rangeSize / 2,
|
|
radiusY: rangeRadius + rangeSize / 2,
|
|
startAngle: from,
|
|
endAngle: to
|
|
});
|
|
result.append(new draw.Arc(rangeGeom, {
|
|
stroke: {
|
|
width: rangeSize,
|
|
color: segment.color,
|
|
opacity: segment.opacity
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
rangeSegments: function () {
|
|
var gauge = this;
|
|
var options = gauge.options;
|
|
var ranges = options.ranges || [];
|
|
var count = ranges.length;
|
|
var range;
|
|
var segmentsCount;
|
|
var defaultColor = options.rangePlaceholderColor;
|
|
var segments = [];
|
|
var segment;
|
|
var min = options.min;
|
|
var max = options.max;
|
|
var i, j;
|
|
function rangeSegment(from, to, color, opacity) {
|
|
return {
|
|
from: from,
|
|
to: to,
|
|
color: color,
|
|
opacity: opacity
|
|
};
|
|
}
|
|
if (count) {
|
|
segments.push(rangeSegment(min, max, defaultColor));
|
|
for (i = 0; i < count; i++) {
|
|
range = getRange(ranges[i], min, max);
|
|
segmentsCount = segments.length;
|
|
for (j = 0; j < segmentsCount; j++) {
|
|
segment = segments[j];
|
|
if (segment.from <= range.from && range.from <= segment.to) {
|
|
segments.push(rangeSegment(range.from, range.to, range.color, range.opacity));
|
|
if (segment.from <= range.to && range.to <= segment.to) {
|
|
segments.push(rangeSegment(range.to, segment.to, defaultColor, range.opacity));
|
|
}
|
|
segment.to = range.from;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return segments;
|
|
},
|
|
getRangeRadius: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var majorTickSize = options.majorTicks.size;
|
|
var rangeSize = options.rangeSize;
|
|
var rangeDistance = options.rangeDistance;
|
|
var arc = that.arc;
|
|
var r;
|
|
if (options.labels.position === OUTSIDE) {
|
|
r = arc.getRadiusX() - majorTickSize - rangeDistance - rangeSize;
|
|
} else {
|
|
r = arc.getRadiusX() - rangeSize;
|
|
}
|
|
return r;
|
|
},
|
|
renderArc: function (center, radius) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var arc = that.arc = new geo.Arc(center, {
|
|
radiusX: radius,
|
|
radiusY: radius,
|
|
startAngle: options.startAngle + GEO_ARC_ADJUST_ANGLE,
|
|
endAngle: options.endAngle + GEO_ARC_ADJUST_ANGLE
|
|
});
|
|
return arc;
|
|
},
|
|
renderTicks: function () {
|
|
var that = this;
|
|
var arc = that.arc;
|
|
var options = that.options;
|
|
var labelsPosition = options.labels.position;
|
|
var allTicks = new Group();
|
|
var majorTickSize = options.majorTicks.size;
|
|
var minorTickSize = options.minorTicks.size;
|
|
var tickArc = arc.clone();
|
|
var radius = tickArc.getRadiusX();
|
|
function drawTicks(arc, tickAngles, unit, tickOptions) {
|
|
var ticks = new Group(), center = arc.center, radius = arc.getRadiusX(), i, tickStart, tickEnd, visible = tickOptions.visible;
|
|
if (visible) {
|
|
for (i = 0; i < tickAngles.length; i++) {
|
|
tickStart = arc.pointAt(tickAngles[i]);
|
|
tickEnd = new Point(center.x + radius - tickOptions.size, center.y).rotate(tickAngles[i], center);
|
|
ticks.append(new Path({
|
|
stroke: {
|
|
color: tickOptions.color,
|
|
width: tickOptions.width
|
|
}
|
|
}).moveTo(tickStart).lineTo(tickEnd));
|
|
}
|
|
}
|
|
return ticks;
|
|
}
|
|
that.majorTickAngles = that.tickAngles(arc, options.majorUnit);
|
|
that.majorTicks = drawTicks(tickArc, that.majorTickAngles, options.majorUnit, options.majorTicks);
|
|
allTicks.append(that.majorTicks);
|
|
that._tickDifference = majorTickSize - minorTickSize;
|
|
if (labelsPosition === OUTSIDE) {
|
|
tickArc.setRadiusX(radius - majorTickSize + minorTickSize).setRadiusY(radius - majorTickSize + minorTickSize);
|
|
}
|
|
that.minorTickAngles = that.normalizeTickAngles(that.tickAngles(arc, options.minorUnit));
|
|
that.minorTicks = drawTicks(tickArc, that.minorTickAngles, options.minorUnit, options.minorTicks, options.majorUnit);
|
|
allTicks.append(that.minorTicks);
|
|
return allTicks;
|
|
},
|
|
normalizeTickAngles: function (angles) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var skip = options.majorUnit / options.minorUnit;
|
|
for (var i = angles.length - 1; i >= 0; i--) {
|
|
if (i % skip === 0) {
|
|
angles.splice(i, 1);
|
|
}
|
|
}
|
|
return angles;
|
|
},
|
|
tickAngles: function (ring, stepValue) {
|
|
var scale = this;
|
|
var options = scale.options;
|
|
var reverse = options.reverse;
|
|
var range = options.max - options.min;
|
|
var angle = ring.endAngle - ring.startAngle;
|
|
var pos = ring.startAngle;
|
|
var tickCount = range / stepValue;
|
|
var step = angle / tickCount;
|
|
var positions = [];
|
|
var i;
|
|
if (reverse) {
|
|
pos += angle;
|
|
step = -step;
|
|
}
|
|
for (i = 0; i < tickCount; i++) {
|
|
positions.push(round(pos, COORD_PRECISION));
|
|
pos += step;
|
|
}
|
|
if (round(pos) <= ring.endAngle) {
|
|
positions.push(pos);
|
|
}
|
|
return positions;
|
|
},
|
|
radius: function (radius) {
|
|
var that = this;
|
|
if (radius) {
|
|
that.arc.setRadiusX(radius).setRadiusY(radius);
|
|
that.repositionTicks(that.majorTicks.children, that.majorTickAngles);
|
|
that.repositionTicks(that.minorTicks.children, that.minorTickAngles, true);
|
|
} else {
|
|
return that.arc.getRadiusX();
|
|
}
|
|
},
|
|
repositionTicks: function (ticks, tickAngles, minor) {
|
|
var that = this;
|
|
var diff = minor ? that._tickDifference || 0 : 0;
|
|
var tickArc = that.arc;
|
|
var radius = tickArc.getRadiusX();
|
|
if (minor && that.options.labels.position === OUTSIDE && diff !== 0) {
|
|
tickArc = that.arc.clone();
|
|
tickArc.setRadiusX(radius - diff).setRadiusY(radius - diff);
|
|
}
|
|
for (var i = 0; i < ticks.length; i++) {
|
|
var newPoint = tickArc.pointAt(tickAngles[i]);
|
|
var segments = ticks[i].segments;
|
|
var xDiff = newPoint.x - segments[0].anchor().x;
|
|
var yDiff = newPoint.y - segments[0].anchor().y;
|
|
ticks[i].transform(new geo.Transformation().translate(xDiff, yDiff));
|
|
}
|
|
}
|
|
});
|
|
var Gauge = Widget.extend({
|
|
init: function (element, userOptions) {
|
|
var gauge = this;
|
|
var options;
|
|
var themeOptions;
|
|
var themeName;
|
|
var themes = dataviz.ui.themes || {};
|
|
var theme;
|
|
kendo.destroy(element);
|
|
$(element).empty();
|
|
Widget.fn.init.call(gauge, element);
|
|
gauge.wrapper = gauge.element;
|
|
gauge._originalOptions = deepExtend({}, userOptions);
|
|
options = deepExtend({}, gauge.options, userOptions);
|
|
themeName = options.theme;
|
|
theme = themes[themeName] || themes[themeName.toLowerCase()];
|
|
themeOptions = themeName && theme ? theme.gauge : {};
|
|
gauge.options = deepExtend({}, themeOptions, options);
|
|
if ($.isArray(options.pointer)) {
|
|
for (var i = 0; i < options.pointer.length; i++) {
|
|
gauge.options.pointer[i] = deepExtend({}, themeOptions.pointer, options.pointer[i]);
|
|
}
|
|
}
|
|
gauge.element.addClass('k-gauge');
|
|
gauge.surface = gauge._createSurface();
|
|
gauge.redraw();
|
|
},
|
|
options: {
|
|
plotArea: {},
|
|
theme: 'default',
|
|
renderAs: '',
|
|
pointer: {},
|
|
scale: {},
|
|
gaugeArea: {}
|
|
},
|
|
destroy: function () {
|
|
this.surface.destroy();
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
var pointer = that.pointers[0];
|
|
if (arguments.length === 0) {
|
|
return pointer.value();
|
|
}
|
|
pointer.value(value);
|
|
that._setValueOptions(value);
|
|
},
|
|
_draw: function () {
|
|
var surface = this.surface;
|
|
surface.clear();
|
|
surface.draw(this._visuals);
|
|
},
|
|
exportVisual: function () {
|
|
return this._visuals;
|
|
},
|
|
allValues: function (values) {
|
|
var that = this;
|
|
var pointers = that.pointers;
|
|
var allValues = [];
|
|
var i;
|
|
if (arguments.length === 0) {
|
|
for (i = 0; i < pointers.length; i++) {
|
|
allValues.push(pointers[i].value());
|
|
}
|
|
return allValues;
|
|
}
|
|
if ($.isArray(values)) {
|
|
for (i = 0; i < values.length; i++) {
|
|
if (isNumber(values[i])) {
|
|
pointers[i].value(values[i]);
|
|
}
|
|
}
|
|
}
|
|
that._setValueOptions(values);
|
|
},
|
|
_setValueOptions: function (values) {
|
|
var pointers = [].concat(this.options.pointer);
|
|
values = [].concat(values);
|
|
for (var i = 0; i < values.length; i++) {
|
|
pointers[i].value = values[i];
|
|
}
|
|
},
|
|
_resize: function () {
|
|
var that = this;
|
|
var t = that.options.transitions;
|
|
var i;
|
|
that.options.transitions = false;
|
|
for (i = 0; i < that.pointers.length; i++) {
|
|
that.pointers[i].options.animation.transitions = false;
|
|
}
|
|
that.redraw();
|
|
that.options.transitions = t;
|
|
for (i = 0; i < that.pointers.length; i++) {
|
|
that.pointers[i].options.animation.transitions = t;
|
|
}
|
|
},
|
|
redraw: function () {
|
|
var that = this;
|
|
var size = deepExtend(that._getSize(), that.options.gaugeArea);
|
|
var wrapper = new Rect([
|
|
0,
|
|
0
|
|
], [
|
|
size.width,
|
|
size.height
|
|
]);
|
|
var bbox;
|
|
that.surface.clear();
|
|
that.gaugeArea = that._createGaugeArea();
|
|
that.surface.element.css({
|
|
width: size.width,
|
|
height: size.height
|
|
});
|
|
that._createModel();
|
|
bbox = _unpad(wrapper.bbox(), that._gaugeAreaMargin);
|
|
that.reflow(bbox);
|
|
},
|
|
_createGaugeArea: function () {
|
|
var that = this;
|
|
var options = that.options.gaugeArea;
|
|
var size = that.surface.size();
|
|
var border = options.border || {};
|
|
var areaGeometry = new Rect([
|
|
0,
|
|
0
|
|
], [
|
|
size.width,
|
|
size.height
|
|
]);
|
|
that._gaugeAreaMargin = options.margin || DEFAULT_MARGIN;
|
|
if (border.width > 0) {
|
|
areaGeometry = _unpad(areaGeometry, border.width);
|
|
}
|
|
var gaugeArea = Path.fromRect(areaGeometry, {
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType,
|
|
lineJoin: 'round',
|
|
lineCap: 'round'
|
|
},
|
|
fill: { color: options.background }
|
|
});
|
|
return gaugeArea;
|
|
},
|
|
_createSurface: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var size = that._getSize();
|
|
size = options.gaugeArea ? deepExtend(size, options.gaugeArea) : size;
|
|
var wrap = $('<div></div>').appendTo(that.element).css({
|
|
width: size.width,
|
|
height: size.height
|
|
});
|
|
return new draw.Surface.create(wrap, { type: options.renderAs });
|
|
},
|
|
getSize: function () {
|
|
return this._getSize();
|
|
},
|
|
_getSize: function () {
|
|
var that = this;
|
|
var element = that.element;
|
|
var width = element.width();
|
|
var height = element.height();
|
|
if (!width) {
|
|
width = DEFAULT_WIDTH;
|
|
}
|
|
if (!height) {
|
|
height = DEFAULT_HEIGHT;
|
|
}
|
|
return {
|
|
width: width,
|
|
height: height
|
|
};
|
|
}
|
|
});
|
|
var RadialGauge = Gauge.extend({
|
|
init: function (element, options) {
|
|
var radialGauge = this;
|
|
Gauge.fn.init.call(radialGauge, element, options);
|
|
kendo.notify(radialGauge, dataviz.ui);
|
|
},
|
|
options: {
|
|
name: 'RadialGauge',
|
|
transitions: true,
|
|
gaugeArea: { background: '' }
|
|
},
|
|
reflow: function (bbox) {
|
|
var that = this;
|
|
var pointers = that.pointers;
|
|
that.scale.reflow(bbox);
|
|
that._initialPlotArea = that.scale.bbox;
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow(that.scale.arc);
|
|
that._initialPlotArea = Rect.union(that._initialPlotArea, pointers[i].bbox);
|
|
}
|
|
that.fitScale(bbox);
|
|
that.alignScale(bbox);
|
|
that._buildVisual(that.gaugeArea, pointers, that.scale);
|
|
that._draw();
|
|
},
|
|
_buildVisual: function (gaugeArea, pointers, scale) {
|
|
var visuals = new Group();
|
|
var current;
|
|
visuals.append(gaugeArea);
|
|
visuals.append(scale.ticks);
|
|
visuals.append(scale.ranges);
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
current = pointers[i];
|
|
current.render();
|
|
visuals.append(current.elements);
|
|
current.value(current.options.value);
|
|
}
|
|
visuals.append(scale.labelElements);
|
|
this._visuals = visuals;
|
|
},
|
|
fitScale: function (bbox) {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var arc = scale.arc;
|
|
var plotAreaBox = that._initialPlotArea;
|
|
var step = math.abs(that.getDiff(plotAreaBox, bbox));
|
|
var min = round(step, COORD_PRECISION);
|
|
var max = round(-step, COORD_PRECISION);
|
|
var minDiff, midDiff, maxDiff, mid, oldDiff;
|
|
var staleFlag = 0;
|
|
var i = 0;
|
|
while (i++ < 100) {
|
|
staleFlag = oldDiff === maxDiff ? staleFlag + 1 : 0;
|
|
if (staleFlag > 5) {
|
|
break;
|
|
}
|
|
if (min != mid) {
|
|
minDiff = that.getPlotBox(min, bbox, arc);
|
|
if (0 <= minDiff && minDiff <= 2) {
|
|
break;
|
|
}
|
|
}
|
|
if (max != mid) {
|
|
maxDiff = that.getPlotBox(max, bbox, arc);
|
|
if (0 <= maxDiff && maxDiff <= 2) {
|
|
break;
|
|
}
|
|
}
|
|
if (minDiff > 0 && maxDiff > 0) {
|
|
mid = min * 2;
|
|
} else if (minDiff < 0 && maxDiff < 0) {
|
|
mid = max * 2;
|
|
} else {
|
|
mid = round((min + max) / 2 || 1, COORD_PRECISION);
|
|
}
|
|
midDiff = that.getPlotBox(mid, bbox, arc);
|
|
if (0 <= midDiff && midDiff <= 2) {
|
|
break;
|
|
}
|
|
oldDiff = maxDiff;
|
|
if (midDiff > 0) {
|
|
max = mid;
|
|
maxDiff = midDiff;
|
|
} else {
|
|
min = mid;
|
|
minDiff = midDiff;
|
|
}
|
|
}
|
|
},
|
|
getPlotBox: function (step, bbox, arc) {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var pointers = that.pointers;
|
|
var radius = arc.getRadiusX();
|
|
arc = arc.clone();
|
|
arc.setRadiusX(radius + step).setRadiusY(radius + step);
|
|
scale.arc = arc;
|
|
scale.reflow(bbox);
|
|
that.plotBbox = scale.bbox;
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow(arc);
|
|
that.plotBbox = Rect.union(that.plotBbox, pointers[i].bbox);
|
|
}
|
|
return that.getDiff(that.plotBbox, bbox);
|
|
},
|
|
getDiff: function (plotBox, box) {
|
|
return math.min(box.width() - plotBox.width(), box.height() - plotBox.height());
|
|
},
|
|
alignScale: function (bbox) {
|
|
var that = this;
|
|
var plotBoxCenter = that.plotBbox.center();
|
|
var boxCenter = bbox.center();
|
|
var paddingX = plotBoxCenter.x - boxCenter.x;
|
|
var paddingY = plotBoxCenter.y - boxCenter.y;
|
|
var scale = that.scale;
|
|
var pointers = that.pointers;
|
|
scale.arc.center.x -= paddingX;
|
|
scale.arc.center.y -= paddingY;
|
|
scale.reflow(bbox);
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow(scale.arc);
|
|
that.plotBbox = Rect.union(scale.bbox, pointers[i].bbox);
|
|
}
|
|
},
|
|
_createModel: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var pointers = options.pointer;
|
|
var scale = that.scale = new RadialScale(options.scale);
|
|
var current;
|
|
that.pointers = [];
|
|
pointers = $.isArray(pointers) ? pointers : [pointers];
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
current = new RadialPointer(scale, deepExtend({}, pointers[i], { animation: { transitions: options.transitions } }));
|
|
that.pointers.push(current);
|
|
}
|
|
}
|
|
});
|
|
var LinearGauge = Gauge.extend({
|
|
init: function (element, options) {
|
|
var linearGauge = this;
|
|
Gauge.fn.init.call(linearGauge, element, options);
|
|
kendo.notify(linearGauge, dataviz.ui);
|
|
},
|
|
options: {
|
|
name: 'LinearGauge',
|
|
transitions: true,
|
|
gaugeArea: { background: '' },
|
|
scale: { vertical: true }
|
|
},
|
|
reflow: function (bbox) {
|
|
var that = this;
|
|
var pointers = that.pointers;
|
|
var bboxX = bbox.origin.x;
|
|
var bboxY = bbox.origin.y;
|
|
var bbox2D = new dataviz.Box2D(bboxX, bboxX, bboxX + bbox.width(), bboxY + bbox.height());
|
|
that.scale.reflow(bbox2D);
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow();
|
|
}
|
|
that.bbox = that._getBox(bbox2D);
|
|
that._alignElements();
|
|
that._shrinkElements();
|
|
that._buildVisual();
|
|
that._draw();
|
|
},
|
|
_buildVisual: function () {
|
|
var that = this;
|
|
var visuals = new Group();
|
|
var scaleElements = that.scale.render();
|
|
var pointers = that.pointers;
|
|
var current;
|
|
visuals.append(that.gaugeArea);
|
|
visuals.append(scaleElements);
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
current = pointers[i];
|
|
visuals.append(current.render());
|
|
current.value(current.options.value);
|
|
}
|
|
that._visuals = visuals;
|
|
},
|
|
_createModel: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var pointers = options.pointer;
|
|
var scale = that.scale = new LinearScale(options.scale);
|
|
var current, currentOptions;
|
|
that.pointers = [];
|
|
pointers = $.isArray(pointers) ? pointers : [pointers];
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
currentOptions = deepExtend({}, pointers[i], { animation: { transitions: options.transitions } });
|
|
if (currentOptions.shape === ARROW) {
|
|
current = new ArrowLinearPointer(scale, currentOptions);
|
|
} else {
|
|
current = new BarLinearPointer(scale, currentOptions);
|
|
}
|
|
that.pointers.push(current);
|
|
}
|
|
},
|
|
_getSize: function () {
|
|
var gauge = this;
|
|
var element = gauge.element;
|
|
var width = element.width();
|
|
var height = element.height();
|
|
var vertical = gauge.options.scale.vertical;
|
|
if (!width) {
|
|
width = vertical ? DEFAULT_MIN_WIDTH : DEFAULT_WIDTH;
|
|
}
|
|
if (!height) {
|
|
height = vertical ? DEFAULT_HEIGHT : DEFAULT_MIN_HEIGHT;
|
|
}
|
|
return {
|
|
width: width,
|
|
height: height
|
|
};
|
|
},
|
|
_getBox: function (box) {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var pointers = that.pointers;
|
|
var boxCenter = box.center();
|
|
var plotAreaBox = pointers[0].box.clone().wrap(scale.box);
|
|
var size;
|
|
for (var i = 0; i < pointers.length; i++) {
|
|
plotAreaBox.wrap(pointers[i].box.clone());
|
|
}
|
|
if (scale.options.vertical) {
|
|
size = plotAreaBox.width() / 2;
|
|
plotAreaBox = new Box2D(boxCenter.x - size, box.y1, boxCenter.x + size, box.y2);
|
|
} else {
|
|
size = plotAreaBox.height() / 2;
|
|
plotAreaBox = new Box2D(box.x1, boxCenter.y - size, box.x2, boxCenter.y + size);
|
|
}
|
|
return plotAreaBox;
|
|
},
|
|
_alignElements: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var pointers = that.pointers;
|
|
var scaleBox = scale.box;
|
|
var box = pointers[0].box.clone().wrap(scale.box);
|
|
var plotAreaBox = that.bbox;
|
|
var diff, i;
|
|
for (i = 0; i < pointers.length; i++) {
|
|
box.wrap(pointers[i].box.clone());
|
|
}
|
|
if (scale.options.vertical) {
|
|
diff = plotAreaBox.center().x - box.center().x;
|
|
scale.reflow(new Box2D(scaleBox.x1 + diff, plotAreaBox.y1, scaleBox.x2 + diff, plotAreaBox.y2));
|
|
} else {
|
|
diff = plotAreaBox.center().y - box.center().y;
|
|
scale.reflow(new Box2D(plotAreaBox.x1, scaleBox.y1 + diff, plotAreaBox.x2, scaleBox.y2 + diff));
|
|
}
|
|
for (i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow(that.bbox);
|
|
}
|
|
},
|
|
_shrinkElements: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var pointers = that.pointers;
|
|
var scaleBox = scale.box.clone();
|
|
var pos = scale.options.vertical ? 'y' : 'x';
|
|
var pointerBox = pointers[0].box;
|
|
var i;
|
|
for (i = 0; i < pointers.length; i++) {
|
|
pointerBox.wrap(pointers[i].box.clone());
|
|
}
|
|
scaleBox[pos + 1] += math.max(scaleBox[pos + 1] - pointerBox[pos + 1], 0);
|
|
scaleBox[pos + 2] -= math.max(pointerBox[pos + 2] - scaleBox[pos + 2], 0);
|
|
scale.reflow(scaleBox);
|
|
for (i = 0; i < pointers.length; i++) {
|
|
pointers[i].reflow(that.bbox);
|
|
}
|
|
}
|
|
});
|
|
var LinearScale = NumericAxis.extend({
|
|
init: function (options) {
|
|
var scale = this;
|
|
scale.options = deepExtend({}, scale.options, options);
|
|
scale.options = deepExtend({}, scale.options, { labels: { mirror: scale.options.mirror } });
|
|
scale.options.majorUnit = scale.options.majorUnit || autoMajorUnit(scale.options.min, scale.options.max);
|
|
Axis.fn.init.call(scale, scale.options);
|
|
scale.options.minorUnit = scale.options.minorUnit || scale.options.majorUnit / 10;
|
|
},
|
|
options: {
|
|
min: 0,
|
|
max: 50,
|
|
majorTicks: {
|
|
size: 15,
|
|
align: INSIDE,
|
|
color: BLACK,
|
|
width: DEFAULT_LINE_WIDTH,
|
|
visible: true
|
|
},
|
|
minorTicks: {
|
|
size: 10,
|
|
align: INSIDE,
|
|
color: BLACK,
|
|
width: DEFAULT_LINE_WIDTH,
|
|
visible: true
|
|
},
|
|
line: { width: DEFAULT_LINE_WIDTH },
|
|
labels: {
|
|
position: INSIDE,
|
|
padding: 2
|
|
},
|
|
mirror: false,
|
|
_alignLines: false
|
|
},
|
|
render: function () {
|
|
var that = this;
|
|
var elements = that.elements = new Group();
|
|
var labels = that.renderLabels();
|
|
var scaleLine = that.renderLine();
|
|
var scaleTicks = that.renderTicks();
|
|
var ranges = that.renderRanges();
|
|
elements.append(scaleLine, labels, scaleTicks, ranges);
|
|
return elements;
|
|
},
|
|
renderRanges: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var min = options.min;
|
|
var max = options.max;
|
|
var ranges = options.ranges || [];
|
|
var vertical = options.vertical;
|
|
var mirror = options.labels.mirror;
|
|
var elements = new Group();
|
|
var count = ranges.length;
|
|
var rangeSize = options.rangeSize || options.minorTicks.size / 2;
|
|
var range, slot, slotX, slotY, i;
|
|
if (count) {
|
|
for (i = 0; i < count; i++) {
|
|
range = getRange(ranges[i], min, max);
|
|
slot = that.getSlot(range.from, range.to);
|
|
slotX = vertical ? that.lineBox() : slot;
|
|
slotY = vertical ? slot : that.lineBox();
|
|
if (vertical) {
|
|
slotX.x1 -= rangeSize * (mirror ? -1 : 1);
|
|
} else {
|
|
slotY.y2 += rangeSize * (mirror ? -1 : 1);
|
|
}
|
|
elements.append(Path.fromRect(new Rect([
|
|
slotX.x1,
|
|
slotY.y1
|
|
], [
|
|
slotX.x2 - slotX.x1,
|
|
slotY.y2 - slotY.y1
|
|
]), {
|
|
fill: {
|
|
color: range.color,
|
|
opacity: range.opacity
|
|
},
|
|
stroke: {}
|
|
}));
|
|
}
|
|
}
|
|
return elements;
|
|
},
|
|
renderLabels: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var labels = that.labels;
|
|
var elements = new Group();
|
|
for (var i = 0; i < labels.length; i++) {
|
|
elements.append(_buildLabel(labels[i], options.labels));
|
|
}
|
|
return elements;
|
|
},
|
|
renderLine: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var line = options.line;
|
|
var lineBox = that.lineBox();
|
|
var linePath;
|
|
var elements = new Group();
|
|
if (line.width > 0 && line.visible) {
|
|
linePath = new Path({
|
|
stroke: {
|
|
color: line.color,
|
|
dashType: line.dashType,
|
|
width: line.width
|
|
}
|
|
});
|
|
linePath.moveTo(lineBox.x1, lineBox.y1).lineTo(lineBox.x2, lineBox.y2);
|
|
elements.append(linePath);
|
|
}
|
|
return elements;
|
|
},
|
|
renderTicks: function () {
|
|
var that = this;
|
|
var ticks = new Group();
|
|
var options = that.options;
|
|
var lineBox = that.lineBox();
|
|
var mirror = options.labels.mirror;
|
|
var majorUnit = options.majorTicks.visible ? options.majorUnit : 0;
|
|
var tickLineOptions = {
|
|
_alignLines: options._alignLines,
|
|
vertical: options.vertical
|
|
};
|
|
function render(tickPositions, tickOptions) {
|
|
var i, count = tickPositions.length;
|
|
if (tickOptions.visible) {
|
|
for (i = tickOptions.skip; i < count; i += tickOptions.step) {
|
|
if (i % tickOptions.skipUnit === 0) {
|
|
continue;
|
|
}
|
|
tickLineOptions.tickX = mirror ? lineBox.x2 : lineBox.x2 - tickOptions.size;
|
|
tickLineOptions.tickY = mirror ? lineBox.y1 - tickOptions.size : lineBox.y1;
|
|
tickLineOptions.position = tickPositions[i];
|
|
ticks.append(that.renderAxisTick(tickLineOptions, tickOptions));
|
|
}
|
|
}
|
|
}
|
|
render(that.getMajorTickPositions(), options.majorTicks);
|
|
render(that.getMinorTickPositions(), deepExtend({}, { skipUnit: majorUnit / options.minorUnit }, options.minorTicks));
|
|
return ticks;
|
|
},
|
|
renderAxisTick: function (options, tickOptions) {
|
|
var tickX = options.tickX;
|
|
var tickY = options.tickY;
|
|
var position = options.position;
|
|
var start, end, tickPath;
|
|
if (options.vertical) {
|
|
start = new Point(tickX, position);
|
|
end = new Point(tickX + tickOptions.size, position);
|
|
} else {
|
|
start = new Point(position, tickY);
|
|
end = new Point(position, tickY + tickOptions.size);
|
|
}
|
|
tickPath = new Path({
|
|
stroke: {
|
|
color: tickOptions.color,
|
|
width: tickOptions.width
|
|
}
|
|
}).moveTo(start).lineTo(end);
|
|
return tickPath;
|
|
}
|
|
});
|
|
var LinearPointer = Pointer.extend({
|
|
init: function (scale, options) {
|
|
var pointer = this;
|
|
Pointer.fn.init.call(pointer, scale, options);
|
|
pointer.options = deepExtend({ track: { visible: defined(options.track) } }, pointer.options);
|
|
},
|
|
options: {
|
|
shape: BAR_POINTER,
|
|
track: { border: { width: 1 } },
|
|
color: BLACK,
|
|
border: { width: 1 },
|
|
opacity: 1,
|
|
margin: getSpacing(3),
|
|
animation: { type: BAR_POINTER },
|
|
visible: true
|
|
},
|
|
reflow: function () {
|
|
var pointer = this;
|
|
var options = pointer.options;
|
|
var scale = pointer.scale;
|
|
var scaleLine = scale.lineBox();
|
|
var trackSize = options.track.size || options.size;
|
|
var pointerHalfSize = options.size / 2;
|
|
var mirror = scale.options.mirror;
|
|
var margin = getSpacing(options.margin);
|
|
var vertical = scale.options.vertical;
|
|
var space = vertical ? margin[mirror ? 'left' : 'right'] : margin[mirror ? 'bottom' : 'top'];
|
|
var pointerBox, pointerRangeBox, trackBox;
|
|
space = mirror ? -space : space;
|
|
if (vertical) {
|
|
trackBox = new Box2D(scaleLine.x1 + space, scaleLine.y1, scaleLine.x1 + space, scaleLine.y2);
|
|
if (mirror) {
|
|
trackBox.x1 -= trackSize;
|
|
} else {
|
|
trackBox.x2 += trackSize;
|
|
}
|
|
if (options.shape !== BAR_POINTER) {
|
|
pointerRangeBox = new Box2D(scaleLine.x2 + space, scaleLine.y1 - pointerHalfSize, scaleLine.x2 + space, scaleLine.y2 + pointerHalfSize);
|
|
pointerBox = pointerRangeBox;
|
|
}
|
|
} else {
|
|
trackBox = new Box2D(scaleLine.x1, scaleLine.y1 - space, scaleLine.x2, scaleLine.y1 - space);
|
|
if (mirror) {
|
|
trackBox.y2 += trackSize;
|
|
} else {
|
|
trackBox.y1 -= trackSize;
|
|
}
|
|
if (options.shape !== BAR_POINTER) {
|
|
pointerRangeBox = new Box2D(scaleLine.x1 - pointerHalfSize, scaleLine.y1 - space, scaleLine.x2 + pointerHalfSize, scaleLine.y1 - space);
|
|
pointerBox = pointerRangeBox;
|
|
}
|
|
}
|
|
pointer.trackBox = trackBox;
|
|
pointer.pointerRangeBox = pointerRangeBox;
|
|
pointer.box = pointerBox || trackBox.clone().pad(options.border.width);
|
|
},
|
|
getElementOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
fill: {
|
|
color: options.color,
|
|
opacity: options.opacity
|
|
},
|
|
stroke: defined(options.border) ? {
|
|
color: options.border.width ? options.border.color || options.color : '',
|
|
width: options.border.width,
|
|
dashType: options.border.dashType,
|
|
opacity: options.opacity
|
|
} : null
|
|
};
|
|
},
|
|
_margin: function () {
|
|
var pointer = this;
|
|
var options = pointer.options;
|
|
var scale = pointer.scale;
|
|
var mirror = scale.options.mirror;
|
|
var margin = getSpacing(options.margin);
|
|
var vertical = scale.options.vertical;
|
|
var space = vertical ? margin[mirror ? 'left' : 'right'] : margin[mirror ? 'bottom' : 'top'];
|
|
return space;
|
|
}
|
|
});
|
|
var ArrowLinearPointer = LinearPointer.extend({
|
|
init: function (scale, options) {
|
|
LinearPointer.fn.init.call(this, scale, options);
|
|
if (this.options.size === undefined) {
|
|
this.options.size = this.scale.options.majorTicks.size * 0.6;
|
|
}
|
|
},
|
|
pointerShape: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var scale = that.scale;
|
|
var size = options.size;
|
|
var vertical = scale.options.vertical;
|
|
var halfSize = size / 2;
|
|
var sign = scale.options.mirror ? -1 : 1;
|
|
var reverse = scale.options.reverse;
|
|
var pos, shape;
|
|
if (vertical) {
|
|
pos = reverse ? 'y2' : 'y1';
|
|
shape = [
|
|
new Point(0, 0 - halfSize),
|
|
new Point(0 - sign * size, 0),
|
|
new Point(0, 0 + halfSize)
|
|
];
|
|
} else {
|
|
pos = reverse ? 'x1' : 'x2';
|
|
shape = [
|
|
new Point(0 - halfSize, 0),
|
|
new Point(0, 0 + sign * size),
|
|
new Point(0 + halfSize, 0)
|
|
];
|
|
}
|
|
return shape;
|
|
},
|
|
repaint: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var options = that.options;
|
|
var animation = new ArrowLinearPointerAnimation(that.elements, deepExtend(options.animation, {
|
|
vertical: scale.options.vertical,
|
|
mirror: scale.options.mirror,
|
|
margin: that._margin(options.margin),
|
|
from: scale.getSlot(options._oldValue),
|
|
to: scale.getSlot(options.value)
|
|
}));
|
|
if (options.animation.transitions === false) {
|
|
animation.options.duration = 0;
|
|
}
|
|
animation.setup();
|
|
animation.play();
|
|
},
|
|
render: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var elements = new Group();
|
|
var scale = that.scale;
|
|
var elementOptions = that.getElementOptions();
|
|
var shape = that.pointerShape(options.value);
|
|
options.animation.type = ARROW_POINTER;
|
|
elements = new Path({
|
|
stroke: elementOptions.stroke,
|
|
fill: elementOptions.fill
|
|
}).moveTo(shape[0]).lineTo(shape[1]).lineTo(shape[2]).close();
|
|
var slot = scale.getSlot(options.value);
|
|
elements.transform(geo.transform().translate(slot.x1, slot.y1));
|
|
that.elements = elements;
|
|
return elements;
|
|
}
|
|
});
|
|
var BarLinearPointer = LinearPointer.extend({
|
|
init: function (scale, options) {
|
|
LinearPointer.fn.init.call(this, scale, options);
|
|
if (this.options.size === undefined) {
|
|
this.options.size = this.scale.options.majorTicks.size * 0.3;
|
|
}
|
|
},
|
|
pointerShape: function (value) {
|
|
var that = this;
|
|
var options = that.options;
|
|
var scale = that.scale;
|
|
var vertical = scale.options.vertical;
|
|
var mirror = scale.options.mirror;
|
|
var dir = mirror == vertical ? -1 : 1;
|
|
var size = options.size * dir;
|
|
var minSlot = scale.getSlot(scale.options.min);
|
|
var slot = scale.getSlot(value);
|
|
var axis = vertical ? Y : X;
|
|
var sizeAxis = vertical ? X : Y;
|
|
var margin = that._margin() * dir;
|
|
var p1 = new Point();
|
|
p1[axis] = minSlot[axis + '1'];
|
|
p1[sizeAxis] = minSlot[sizeAxis + '1'];
|
|
var p2 = new Point();
|
|
p2[axis] = slot[axis + '1'];
|
|
p2[sizeAxis] = slot[sizeAxis + '1'];
|
|
if (vertical) {
|
|
p1.translate(margin, 0);
|
|
p2.translate(margin, 0);
|
|
} else {
|
|
p1.translate(0, margin);
|
|
p2.translate(0, margin);
|
|
}
|
|
var p3 = p2.clone();
|
|
var p4 = p1.clone();
|
|
if (vertical) {
|
|
p3.translate(size, 0);
|
|
p4.translate(size, 0);
|
|
} else {
|
|
p3.translate(0, size);
|
|
p4.translate(0, size);
|
|
}
|
|
return [
|
|
p1,
|
|
p2,
|
|
p3,
|
|
p4
|
|
];
|
|
},
|
|
repaint: function () {
|
|
var that = this;
|
|
var scale = that.scale;
|
|
var options = that.options;
|
|
var shape = that.pointerShape(options.value);
|
|
var pointerPath = that.elements.children[0];
|
|
var oldShape = that.pointerShape(options._oldValue);
|
|
pointerPath.moveTo(shape[0]).lineTo(shape[1]).lineTo(shape[2]).lineTo(shape[3]).close();
|
|
var animation = new BarLinearPointerAnimation(pointerPath, deepExtend(options.animation, {
|
|
reverse: scale.options.reverse,
|
|
vertical: scale.options.vertical,
|
|
oldPoints: [
|
|
oldShape[1],
|
|
oldShape[2]
|
|
],
|
|
newPoints: [
|
|
shape[1],
|
|
shape[2]
|
|
]
|
|
}));
|
|
if (options.animation.transitions === false) {
|
|
animation.options.duration = 0;
|
|
}
|
|
animation.setup();
|
|
animation.play();
|
|
},
|
|
render: function () {
|
|
var that = this;
|
|
var group = new Group();
|
|
var elementOptions = that.getElementOptions();
|
|
var pointer = new Path({
|
|
stroke: elementOptions.stroke,
|
|
fill: elementOptions.fill
|
|
});
|
|
group.append(pointer);
|
|
that.elements = group;
|
|
return group;
|
|
}
|
|
});
|
|
var RadialPointerAnimation = draw.Animation.extend({
|
|
init: function (element, options) {
|
|
draw.Animation.fn.init.call(this, element, options);
|
|
options = this.options;
|
|
options.duration = math.max(math.abs(options.newAngle - options.oldAngle) / options.duration * 1000, 1);
|
|
},
|
|
options: {
|
|
easing: LINEAR,
|
|
duration: ANGULAR_SPEED
|
|
},
|
|
step: function (pos) {
|
|
var anim = this;
|
|
var options = anim.options;
|
|
var angle = interpolateValue(options.oldAngle, options.newAngle, pos);
|
|
anim.element.transform(geo.transform().rotate(angle, options.center));
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(RADIAL_POINTER, RadialPointerAnimation);
|
|
var ArrowLinearPointerAnimation = draw.Animation.extend({
|
|
options: {
|
|
easing: LINEAR,
|
|
duration: LINEAR_SPEED
|
|
},
|
|
setup: function () {
|
|
var options = this.options;
|
|
var margin = options.margin;
|
|
var from = options.from;
|
|
var to = options.to;
|
|
var axis = options.vertical ? 'x1' : 'y1';
|
|
if (options.mirror == options.vertical) {
|
|
from[axis] -= margin;
|
|
to[axis] -= margin;
|
|
} else {
|
|
from[axis] += margin;
|
|
to[axis] += margin;
|
|
}
|
|
var fromScale = this.fromScale = new Point(from.x1, from.y1);
|
|
var toScale = this.toScale = new Point(to.x1, to.y1);
|
|
if (options.duration !== 0) {
|
|
options.duration = math.max(fromScale.distanceTo(toScale) / options.duration * 1000, 1);
|
|
}
|
|
},
|
|
step: function (pos) {
|
|
var translateX = interpolateValue(this.fromScale.x, this.toScale.x, pos);
|
|
var translateY = interpolateValue(this.fromScale.y, this.toScale.y, pos);
|
|
this.element.transform(geo.transform().translate(translateX, translateY));
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(ARROW_POINTER, ArrowLinearPointerAnimation);
|
|
var BarLinearPointerAnimation = draw.Animation.extend({
|
|
options: {
|
|
easing: LINEAR,
|
|
speed: LINEAR_SPEED
|
|
},
|
|
setup: function () {
|
|
var options = this.options;
|
|
var newPoints = options.newPoints;
|
|
var oldPoints = options.oldPoints;
|
|
var axis = this.axis = options.vertical ? Y : X;
|
|
var to = this.to = newPoints[0][axis];
|
|
var from = this.from = oldPoints[0][axis];
|
|
if (options.duration !== 0) {
|
|
options.duration = math.max(math.abs(to - from) / options.speed * 1000, 1);
|
|
}
|
|
this._set(from);
|
|
},
|
|
step: function (pos) {
|
|
var value = interpolateValue(this.from, this.to, pos);
|
|
this._set(value);
|
|
},
|
|
_set: function (value) {
|
|
var setter = 'set' + this.axis.toUpperCase();
|
|
var points = this.options.newPoints;
|
|
points[0][setter](value);
|
|
points[1][setter](value);
|
|
}
|
|
});
|
|
draw.AnimationFactory.current.register(BAR_POINTER, BarLinearPointerAnimation);
|
|
function _buildLabel(label, options) {
|
|
var labelBox = label.box;
|
|
var textBox = label.children[0].box;
|
|
var border = options.border || {};
|
|
var background = options.background || '';
|
|
var elements = new Group();
|
|
var styleBox, styleGeometry, wrapper;
|
|
wrapper = Path.fromRect(new Rect([
|
|
labelBox.x1,
|
|
labelBox.y1
|
|
], [
|
|
labelBox.width(),
|
|
labelBox.height()
|
|
]), { stroke: {} });
|
|
var text = new Text(label.text, new Point(textBox.x1, textBox.y1), {
|
|
font: options.font,
|
|
fill: { color: options.color }
|
|
});
|
|
styleGeometry = _pad(text.bbox().clone(), options.padding);
|
|
styleBox = Path.fromRect(styleGeometry, {
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType,
|
|
lineJoin: 'round',
|
|
lineCap: 'round'
|
|
},
|
|
fill: { color: background }
|
|
});
|
|
elements.append(wrapper);
|
|
elements.append(styleBox);
|
|
elements.append(text);
|
|
return elements;
|
|
}
|
|
function getRange(range, min, max) {
|
|
var from = defined(range.from) ? range.from : MIN_VALUE;
|
|
var to = defined(range.to) ? range.to : MAX_VALUE;
|
|
range.from = math.max(math.min(to, from), min);
|
|
range.to = math.min(math.max(to, from), max);
|
|
return range;
|
|
}
|
|
function _pad(bbox, value) {
|
|
var origin = bbox.getOrigin();
|
|
var size = bbox.getSize();
|
|
var spacing = getSpacing(value);
|
|
bbox.setOrigin([
|
|
origin.x - spacing.left,
|
|
origin.y - spacing.top
|
|
]);
|
|
bbox.setSize([
|
|
size.width + (spacing.left + spacing.right),
|
|
size.height + (spacing.top + spacing.bottom)
|
|
]);
|
|
return bbox;
|
|
}
|
|
function _unpad(bbox, value) {
|
|
var spacing = getSpacing(value);
|
|
spacing.left = -spacing.left;
|
|
spacing.top = -spacing.top;
|
|
spacing.right = -spacing.right;
|
|
spacing.bottom = -spacing.bottom;
|
|
return _pad(bbox, spacing);
|
|
}
|
|
dataviz.ui.plugin(RadialGauge);
|
|
dataviz.ui.plugin(LinearGauge);
|
|
dataviz.ExportMixin.extend(Gauge.fn);
|
|
deepExtend(dataviz, {
|
|
Gauge: Gauge,
|
|
RadialPointer: RadialPointer,
|
|
LinearPointer: LinearPointer,
|
|
ArrowLinearPointer: ArrowLinearPointer,
|
|
BarLinearPointer: BarLinearPointer,
|
|
LinearScale: LinearScale,
|
|
RadialScale: RadialScale,
|
|
LinearGauge: LinearGauge,
|
|
RadialGauge: RadialGauge
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.barcode', [
|
|
'kendo.dataviz.core',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.barcode',
|
|
name: 'Barcode',
|
|
category: 'dataviz',
|
|
description: 'Barcode widget',
|
|
depends: ['dataviz.core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, extend = $.extend, deepExtend = kendo.deepExtend, inArray = $.inArray, isPlainObject = $.isPlainObject, draw = kendo.drawing, geom = kendo.geometry, util = kendo.util, defined = util.defined, dataviz = kendo.dataviz, Box2D = dataviz.Box2D, TextBox = dataviz.TextBox, DEFAULT_WIDTH = 300, DEFAULT_HEIGHT = 100, DEFAULT_QUIETZONE_LENGTH = 10, numberRegex = /^\d+$/, alphanumericRegex = /^[a-z0-9]+$/i, InvalidCharacterErrorTemplate = 'Character \'{0}\' is not valid for symbology {1}';
|
|
function getNext(value, index, count) {
|
|
return value.substring(index, index + count);
|
|
}
|
|
var Encoding = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.setOptions(options);
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
that.options = extend({}, that.options, options);
|
|
that.quietZoneLength = that.options.addQuietZone ? 2 * that.options.quietZoneLength : 0;
|
|
},
|
|
encode: function (value, width, height) {
|
|
var that = this;
|
|
if (defined(value)) {
|
|
value += '';
|
|
}
|
|
that.initValue(value, width, height);
|
|
if (that.options.addQuietZone) {
|
|
that.addQuietZone();
|
|
}
|
|
that.addData();
|
|
if (that.options.addQuietZone) {
|
|
that.addQuietZone();
|
|
}
|
|
return {
|
|
baseUnit: that.baseUnit,
|
|
pattern: that.pattern
|
|
};
|
|
},
|
|
options: {
|
|
quietZoneLength: DEFAULT_QUIETZONE_LENGTH,
|
|
addQuietZone: true,
|
|
addCheckSum: true
|
|
},
|
|
initValue: function () {
|
|
},
|
|
addQuietZone: function () {
|
|
this.pattern.push(this.options.quietZoneLength || DEFAULT_QUIETZONE_LENGTH);
|
|
},
|
|
addData: function () {
|
|
},
|
|
invalidCharacterError: function (character) {
|
|
throw new Error(kendo.format(InvalidCharacterErrorTemplate, character, this.name));
|
|
}
|
|
});
|
|
var encodings = {};
|
|
var code39Base = Encoding.extend({
|
|
minBaseUnitLength: 0.7,
|
|
addData: function () {
|
|
var that = this, value = that.value;
|
|
that.addStart();
|
|
for (var idx = 0; idx < value.length; idx++) {
|
|
that.addCharacter(value.charAt(idx));
|
|
}
|
|
if (that.options.addCheckSum) {
|
|
that.pushCheckSum();
|
|
}
|
|
that.addStop();
|
|
that.prepareValues();
|
|
},
|
|
addCharacter: function (character) {
|
|
var that = this, charData = that.characterMap[character];
|
|
if (!charData) {
|
|
that.invalidCharacterError(character);
|
|
}
|
|
that.addBase(charData);
|
|
},
|
|
addBase: function () {
|
|
}
|
|
});
|
|
var code39ExtendedBase = {
|
|
addCharacter: function (character) {
|
|
var that = this;
|
|
if (that.characterMap[character]) {
|
|
that.addBase(that.characterMap[character]);
|
|
} else if (character.charCodeAt(0) > 127) {
|
|
that.invalidCharacterError(character);
|
|
} else {
|
|
that.addExtended(character.charCodeAt(0));
|
|
}
|
|
},
|
|
addExtended: function (code) {
|
|
var that = this, patterns;
|
|
for (var i = 0; i < that.extendedMappings.length; i++) {
|
|
if (patterns = that.extendedMappings[i].call(that, code)) {
|
|
for (var j = 0; j < patterns.length; j++) {
|
|
that.addBase(patterns[j]);
|
|
}
|
|
that.dataLength += patterns.length - 1;
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
extendedMappings: [
|
|
function (code) {
|
|
if (97 <= code && code <= 122) {
|
|
var that = this;
|
|
return [
|
|
that.characterMap[that.shiftCharacters[0]],
|
|
that.characterMap[String.fromCharCode(code - 32)]
|
|
];
|
|
}
|
|
},
|
|
function (code) {
|
|
if (33 <= code && code <= 58) {
|
|
var that = this;
|
|
return [
|
|
that.characterMap[that.shiftCharacters[1]],
|
|
that.characterMap[String.fromCharCode(code + 32)]
|
|
];
|
|
}
|
|
},
|
|
function (code) {
|
|
if (1 <= code && code <= 26) {
|
|
var that = this;
|
|
return [
|
|
that.characterMap[that.shiftCharacters[2]],
|
|
that.characterMap[String.fromCharCode(code + 64)]
|
|
];
|
|
}
|
|
},
|
|
function (code) {
|
|
var that = this, result, dataCharacter;
|
|
if (!that.specialAsciiCodes[code]) {
|
|
dataCharacter = Math.floor(code / 32) * 6 + (code - 27) % 32 + 64;
|
|
result = [
|
|
that.characterMap[that.shiftCharacters[3]],
|
|
that.characterMap[String.fromCharCode(dataCharacter)]
|
|
];
|
|
} else {
|
|
result = [];
|
|
for (var i = 0; i < that.specialAsciiCodes[code].length; i++) {
|
|
result.push(that.characterMap[that.shiftCharacters[3]]);
|
|
result.push(that.characterMap[that.specialAsciiCodes[code][i]]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
],
|
|
specialAsciiCodes: {
|
|
'0': ['U'],
|
|
'64': ['V'],
|
|
'96': ['W'],
|
|
'127': [
|
|
'T',
|
|
'X',
|
|
'Y',
|
|
'Z'
|
|
]
|
|
},
|
|
shiftValuesAsciiCodes: {
|
|
'39': 36,
|
|
'40': 47,
|
|
'41': 43,
|
|
'42': 37
|
|
},
|
|
characterMap: {
|
|
'+': false,
|
|
'/': false,
|
|
'$': false,
|
|
'%': false
|
|
},
|
|
shiftCharacters: [
|
|
'SHIFT0',
|
|
'SHIFT1',
|
|
'SHIFT2',
|
|
'SHIFT3'
|
|
]
|
|
};
|
|
encodings.code39 = code39Base.extend({
|
|
name: 'Code 39',
|
|
checkSumMod: 43,
|
|
minRatio: 2.5,
|
|
maxRatio: 3,
|
|
gapWidth: 1,
|
|
splitCharacter: '|',
|
|
initValue: function (value, width, height) {
|
|
var that = this;
|
|
that.width = width;
|
|
that.height = height;
|
|
that.value = value;
|
|
that.dataLength = value.length;
|
|
that.pattern = [];
|
|
that.patternString = '';
|
|
},
|
|
prepareValues: function () {
|
|
var that = this, baseUnit, minBaseUnit = that.minBaseUnitLength, ratio = that.maxRatio, minRatio = that.minRatio, minHeight = Math.max(0.15 * that.width, 24);
|
|
if (that.height < minHeight) {
|
|
throw new Error('Insufficient Height. The minimum height for value: ' + that.value + ' is: ' + minHeight);
|
|
}
|
|
while ((baseUnit = that.getBaseUnit(ratio)) < minBaseUnit && ratio > minRatio) {
|
|
ratio = parseFloat((ratio - 0.1).toFixed(1));
|
|
}
|
|
if (baseUnit < minBaseUnit) {
|
|
var minWidth = Math.ceil(that.getBaseWidth(minRatio) * minBaseUnit);
|
|
throw new Error('Insufficient width. The minimum width for value: ' + that.value + ' is: ' + minWidth);
|
|
}
|
|
that.ratio = ratio;
|
|
that.baseUnit = baseUnit;
|
|
that.patternString = that.patternString.substring(0, that.patternString.length - 1);
|
|
that.pattern = that.pattern.concat(that.patternString.replace(/ratio/g, ratio).split(that.splitCharacter));
|
|
},
|
|
getBaseUnit: function (ratio) {
|
|
return this.width / this.getBaseWidth(ratio);
|
|
},
|
|
getBaseWidth: function (ratio) {
|
|
var that = this, characterLength = 3 * (ratio + 2);
|
|
return that.quietZoneLength + characterLength * (that.dataLength + 2) + that.gapWidth * (that.dataLength + 1);
|
|
},
|
|
addStart: function () {
|
|
var that = this;
|
|
that.addPattern(that.characterMap.START.pattern);
|
|
that.addCharacterGap();
|
|
},
|
|
addBase: function (character) {
|
|
this.addPattern(character.pattern);
|
|
this.addCharacterGap();
|
|
},
|
|
addStop: function () {
|
|
this.addPattern(this.characterMap.START.pattern);
|
|
},
|
|
addPattern: function (pattern) {
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
this.patternString += this.patternMappings[pattern.charAt(i)];
|
|
}
|
|
},
|
|
addCharacterGap: function () {
|
|
var that = this;
|
|
that.patternString += that.gapWidth + that.splitCharacter;
|
|
},
|
|
patternMappings: {
|
|
'b': '1|',
|
|
'w': '1|',
|
|
'B': 'ratio|',
|
|
'W': 'ratio|'
|
|
},
|
|
characterMap: {
|
|
'0': {
|
|
'pattern': 'bwbWBwBwb',
|
|
'value': 0
|
|
},
|
|
'1': {
|
|
'pattern': 'BwbWbwbwB',
|
|
'value': 1
|
|
},
|
|
'2': {
|
|
'pattern': 'bwBWbwbwB',
|
|
'value': 2
|
|
},
|
|
'3': {
|
|
'pattern': 'BwBWbwbwb',
|
|
'value': 3
|
|
},
|
|
'4': {
|
|
'pattern': 'bwbWBwbwB',
|
|
'value': 4
|
|
},
|
|
'5': {
|
|
'pattern': 'BwbWBwbwb',
|
|
'value': 5
|
|
},
|
|
'6': {
|
|
'pattern': 'bwBWBwbwb',
|
|
'value': 6
|
|
},
|
|
'7': {
|
|
'pattern': 'bwbWbwBwB',
|
|
'value': 7
|
|
},
|
|
'8': {
|
|
'pattern': 'BwbWbwBwb',
|
|
'value': 8
|
|
},
|
|
'9': {
|
|
'pattern': 'bwBWbwBwb',
|
|
'value': 9
|
|
},
|
|
'A': {
|
|
'pattern': 'BwbwbWbwB',
|
|
'value': 10
|
|
},
|
|
'B': {
|
|
'pattern': 'bwBwbWbwB',
|
|
'value': 11
|
|
},
|
|
'C': {
|
|
'pattern': 'BwBwbWbwb',
|
|
'value': 12
|
|
},
|
|
'D': {
|
|
'pattern': 'bwbwBWbwB',
|
|
'value': 13
|
|
},
|
|
'E': {
|
|
'pattern': 'BwbwBWbwb',
|
|
'value': 14
|
|
},
|
|
'F': {
|
|
'pattern': 'bwBwBWbwb',
|
|
'value': 15
|
|
},
|
|
'G': {
|
|
'pattern': 'bwbwbWBwB',
|
|
'value': 16
|
|
},
|
|
'H': {
|
|
'pattern': 'BwbwbWBwb',
|
|
'value': 17
|
|
},
|
|
'I': {
|
|
'pattern': 'bwBwbWBwb',
|
|
'value': 18
|
|
},
|
|
'J': {
|
|
'pattern': 'bwbwBWBwb',
|
|
'value': 19
|
|
},
|
|
'K': {
|
|
'pattern': 'BwbwbwbWB',
|
|
'value': 20
|
|
},
|
|
'L': {
|
|
'pattern': 'bwBwbwbWB',
|
|
'value': 21
|
|
},
|
|
'M': {
|
|
'pattern': 'BwBwbwbWb',
|
|
'value': 22
|
|
},
|
|
'N': {
|
|
'pattern': 'bwbwBwbWB',
|
|
'value': 23
|
|
},
|
|
'O': {
|
|
'pattern': 'BwbwBwbWb',
|
|
'value': 24
|
|
},
|
|
'P': {
|
|
'pattern': 'bwBwBwbWb',
|
|
'value': 25
|
|
},
|
|
'Q': {
|
|
'pattern': 'bwbwbwBWB',
|
|
'value': 26
|
|
},
|
|
'R': {
|
|
'pattern': 'BwbwbwBWb',
|
|
'value': 27
|
|
},
|
|
'S': {
|
|
'pattern': 'bwBwbwBWb',
|
|
'value': 28
|
|
},
|
|
'T': {
|
|
'pattern': 'bwbwBwBWb',
|
|
'value': 29
|
|
},
|
|
'U': {
|
|
'pattern': 'BWbwbwbwB',
|
|
'value': 30
|
|
},
|
|
'V': {
|
|
'pattern': 'bWBwbwbwB',
|
|
'value': 31
|
|
},
|
|
'W': {
|
|
'pattern': 'BWBwbwbwb',
|
|
'value': 32
|
|
},
|
|
'X': {
|
|
'pattern': 'bWbwBwbwB',
|
|
'value': 33
|
|
},
|
|
'Y': {
|
|
'pattern': 'BWbwBwbwb',
|
|
'value': 34
|
|
},
|
|
'Z': {
|
|
'pattern': 'bWBwBwbwb',
|
|
'value': 35
|
|
},
|
|
'-': {
|
|
'pattern': 'bWbwbwBwB',
|
|
'value': 36
|
|
},
|
|
'.': {
|
|
'pattern': 'BWbwbwBwb',
|
|
'value': 37
|
|
},
|
|
' ': {
|
|
'pattern': 'bWBwbwBwb',
|
|
'value': 38
|
|
},
|
|
'$': {
|
|
'pattern': 'bWbWbWbwb',
|
|
'value': 39
|
|
},
|
|
'/': {
|
|
'pattern': 'bWbWbwbWb',
|
|
'value': 40
|
|
},
|
|
'+': {
|
|
'pattern': 'bWbwbWbWb',
|
|
'value': 41
|
|
},
|
|
'%': {
|
|
'pattern': 'bwbWbWbWb',
|
|
'value': 42
|
|
},
|
|
START: { pattern: 'bWbwBwBwb' }
|
|
},
|
|
options: { addCheckSum: false }
|
|
});
|
|
encodings.code39extended = encodings.code39.extend(deepExtend({}, code39ExtendedBase, {
|
|
name: 'Code 39 extended',
|
|
characterMap: {
|
|
SHIFT0: {
|
|
'pattern': 'bWbwbWbWb',
|
|
'value': 41
|
|
},
|
|
SHIFT1: {
|
|
'pattern': 'bWbWbwbWb',
|
|
'value': 40
|
|
},
|
|
SHIFT2: {
|
|
'pattern': 'bWbWbWbwb',
|
|
'value': 39
|
|
},
|
|
SHIFT3: {
|
|
'pattern': 'bwbWbWbWb',
|
|
'value': 42
|
|
}
|
|
}
|
|
}));
|
|
encodings.code93 = code39Base.extend({
|
|
name: 'Code 93',
|
|
cCheckSumTotal: 20,
|
|
kCheckSumTotal: 15,
|
|
checkSumMod: 47,
|
|
initValue: function (value, width, height) {
|
|
var that = this;
|
|
that.value = value;
|
|
that.width = width;
|
|
that.height = height;
|
|
that.pattern = [];
|
|
that.values = [];
|
|
that.dataLength = value.length;
|
|
},
|
|
prepareValues: function () {
|
|
var that = this, minHeight = Math.max(0.15 * that.width, 24);
|
|
if (that.height < minHeight) {
|
|
throw new Error('Insufficient Height');
|
|
}
|
|
that.setBaseUnit();
|
|
if (that.baseUnit < that.minBaseUnitLength) {
|
|
throw new Error('Insufficient Width');
|
|
}
|
|
},
|
|
setBaseUnit: function () {
|
|
var that = this, checkSumLength = 2;
|
|
that.baseUnit = that.width / (9 * (that.dataLength + 2 + checkSumLength) + that.quietZoneLength + 1);
|
|
},
|
|
addStart: function () {
|
|
var pattern = this.characterMap.START.pattern;
|
|
this.addPattern(pattern);
|
|
},
|
|
addStop: function () {
|
|
var that = this;
|
|
that.addStart();
|
|
that.pattern.push(that.characterMap.TERMINATION_BAR);
|
|
},
|
|
addBase: function (charData) {
|
|
this.addPattern(charData.pattern);
|
|
this.values.push(charData.value);
|
|
},
|
|
pushCheckSum: function () {
|
|
var that = this, checkValues = that._getCheckValues(), charData;
|
|
that.checksum = checkValues.join('');
|
|
for (var i = 0; i < checkValues.length; i++) {
|
|
charData = that.characterMap[that._findCharacterByValue(checkValues[i])];
|
|
that.addPattern(charData.pattern);
|
|
}
|
|
},
|
|
_getCheckValues: function () {
|
|
var that = this, values = that.values, length = values.length, wightedSum = 0, cValue, kValue, idx;
|
|
for (idx = length - 1; idx >= 0; idx--) {
|
|
wightedSum += that.weightedValue(values[idx], length - idx, that.cCheckSumTotal);
|
|
}
|
|
cValue = wightedSum % that.checkSumMod;
|
|
wightedSum = that.weightedValue(cValue, 1, that.kCheckSumTotal);
|
|
for (idx = length - 1; idx >= 0; idx--) {
|
|
wightedSum += that.weightedValue(values[idx], length - idx + 1, that.kCheckSumTotal);
|
|
}
|
|
kValue = wightedSum % that.checkSumMod;
|
|
return [
|
|
cValue,
|
|
kValue
|
|
];
|
|
},
|
|
_findCharacterByValue: function (value) {
|
|
for (var character in this.characterMap) {
|
|
if (this.characterMap[character].value === value) {
|
|
return character;
|
|
}
|
|
}
|
|
},
|
|
weightedValue: function (value, index, total) {
|
|
return (index % total || total) * value;
|
|
},
|
|
addPattern: function (pattern) {
|
|
var value;
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
value = parseInt(pattern.charAt(i), 10);
|
|
this.pattern.push(value);
|
|
}
|
|
},
|
|
characterMap: {
|
|
'0': {
|
|
'pattern': '131112',
|
|
'value': 0
|
|
},
|
|
'1': {
|
|
'pattern': '111213',
|
|
'value': 1
|
|
},
|
|
'2': {
|
|
'pattern': '111312',
|
|
'value': 2
|
|
},
|
|
'3': {
|
|
'pattern': '111411',
|
|
'value': 3
|
|
},
|
|
'4': {
|
|
'pattern': '121113',
|
|
'value': 4
|
|
},
|
|
'5': {
|
|
'pattern': '121212',
|
|
'value': 5
|
|
},
|
|
'6': {
|
|
'pattern': '121311',
|
|
'value': 6
|
|
},
|
|
'7': {
|
|
'pattern': '111114',
|
|
'value': 7
|
|
},
|
|
'8': {
|
|
'pattern': '131211',
|
|
'value': 8
|
|
},
|
|
'9': {
|
|
'pattern': '141111',
|
|
'value': 9
|
|
},
|
|
'A': {
|
|
'pattern': '211113',
|
|
'value': 10
|
|
},
|
|
'B': {
|
|
'pattern': '211212',
|
|
'value': 11
|
|
},
|
|
'C': {
|
|
'pattern': '211311',
|
|
'value': 12
|
|
},
|
|
'D': {
|
|
'pattern': '221112',
|
|
'value': 13
|
|
},
|
|
'E': {
|
|
'pattern': '221211',
|
|
'value': 14
|
|
},
|
|
'F': {
|
|
'pattern': '231111',
|
|
'value': 15
|
|
},
|
|
'G': {
|
|
'pattern': '112113',
|
|
'value': 16
|
|
},
|
|
'H': {
|
|
'pattern': '112212',
|
|
'value': 17
|
|
},
|
|
'I': {
|
|
'pattern': '112311',
|
|
'value': 18
|
|
},
|
|
'J': {
|
|
'pattern': '122112',
|
|
'value': 19
|
|
},
|
|
'K': {
|
|
'pattern': '132111',
|
|
'value': 20
|
|
},
|
|
'L': {
|
|
'pattern': '111123',
|
|
'value': 21
|
|
},
|
|
'M': {
|
|
'pattern': '111222',
|
|
'value': 22
|
|
},
|
|
'N': {
|
|
'pattern': '111321',
|
|
'value': 23
|
|
},
|
|
'O': {
|
|
'pattern': '121122',
|
|
'value': 24
|
|
},
|
|
'P': {
|
|
'pattern': '131121',
|
|
'value': 25
|
|
},
|
|
'Q': {
|
|
'pattern': '212112',
|
|
'value': 26
|
|
},
|
|
'R': {
|
|
'pattern': '212211',
|
|
'value': 27
|
|
},
|
|
'S': {
|
|
'pattern': '211122',
|
|
'value': 28
|
|
},
|
|
'T': {
|
|
'pattern': '211221',
|
|
'value': 29
|
|
},
|
|
'U': {
|
|
'pattern': '221121',
|
|
'value': 30
|
|
},
|
|
'V': {
|
|
'pattern': '222111',
|
|
'value': 31
|
|
},
|
|
'W': {
|
|
'pattern': '112122',
|
|
'value': 32
|
|
},
|
|
'X': {
|
|
'pattern': '112221',
|
|
'value': 33
|
|
},
|
|
'Y': {
|
|
'pattern': '122121',
|
|
'value': 34
|
|
},
|
|
'Z': {
|
|
'pattern': '123111',
|
|
'value': 35
|
|
},
|
|
'-': {
|
|
'pattern': '121131',
|
|
'value': 36
|
|
},
|
|
'.': {
|
|
'pattern': '311112',
|
|
'value': 37
|
|
},
|
|
' ': {
|
|
'pattern': '311211',
|
|
'value': 38
|
|
},
|
|
'$': {
|
|
'pattern': '321111',
|
|
'value': 39
|
|
},
|
|
'/': {
|
|
'pattern': '112131',
|
|
'value': 40
|
|
},
|
|
'+': {
|
|
'pattern': '113121',
|
|
'value': 41
|
|
},
|
|
'%': {
|
|
'pattern': '211131',
|
|
'value': 42
|
|
},
|
|
SHIFT0: {
|
|
'pattern': '122211',
|
|
'value': 46
|
|
},
|
|
SHIFT1: {
|
|
'pattern': '311121',
|
|
'value': 45
|
|
},
|
|
SHIFT2: {
|
|
'pattern': '121221',
|
|
'value': 43
|
|
},
|
|
SHIFT3: {
|
|
'pattern': '312111',
|
|
'value': 44
|
|
},
|
|
START: { 'pattern': '111141' },
|
|
TERMINATION_BAR: '1'
|
|
}
|
|
});
|
|
encodings.code93extended = encodings.code93.extend(deepExtend({}, code39ExtendedBase, {
|
|
name: 'Code 93 extended',
|
|
pushCheckSum: function () {
|
|
var that = this, checkValues = that._getCheckValues(), value;
|
|
that.checksum = checkValues.join('');
|
|
for (var i = 0; i < checkValues.length; i++) {
|
|
value = checkValues[i];
|
|
if (that.shiftValuesAsciiCodes[value]) {
|
|
that.addExtended(that.shiftValuesAsciiCodes[value]);
|
|
} else {
|
|
that.addPattern(that.characterMap[that._findCharacterByValue(value)].pattern);
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
var state128 = kendo.Class.extend({
|
|
init: function (encoding) {
|
|
this.encoding = encoding;
|
|
},
|
|
addStart: function () {
|
|
},
|
|
is: function () {
|
|
},
|
|
move: function () {
|
|
},
|
|
pushState: function () {
|
|
}
|
|
});
|
|
var state128AB = state128.extend({
|
|
FNC4: 'FNC4',
|
|
init: function (encoding, states) {
|
|
var that = this;
|
|
that.encoding = encoding;
|
|
that.states = states;
|
|
that._initMoves(states);
|
|
},
|
|
addStart: function () {
|
|
this.encoding.addPattern(this.START);
|
|
},
|
|
is: function (value, index) {
|
|
var code = value.charCodeAt(index);
|
|
return this.isCode(code);
|
|
},
|
|
move: function (encodingState) {
|
|
var that = this, idx = 0;
|
|
while (!that._moves[idx].call(that, encodingState) && idx < that._moves.length) {
|
|
idx++;
|
|
}
|
|
},
|
|
pushState: function (encodingState) {
|
|
var that = this, states = that.states, value = encodingState.value, maxLength = value.length, code;
|
|
if (inArray('C', states) >= 0) {
|
|
var numberMatch = value.substr(encodingState.index).match(/\d{4,}/g);
|
|
if (numberMatch) {
|
|
maxLength = value.indexOf(numberMatch[0], encodingState.index);
|
|
}
|
|
}
|
|
while ((code = encodingState.value.charCodeAt(encodingState.index)) >= 0 && that.isCode(code) && encodingState.index < maxLength) {
|
|
that.encoding.addPattern(that.getValue(code));
|
|
encodingState.index++;
|
|
}
|
|
},
|
|
_initMoves: function (states) {
|
|
var that = this;
|
|
that._moves = [];
|
|
if (inArray(that.FNC4, states) >= 0) {
|
|
that._moves.push(that._moveFNC);
|
|
}
|
|
if (inArray(that.shiftKey, states) >= 0) {
|
|
that._moves.push(that._shiftState);
|
|
}
|
|
that._moves.push(that._moveState);
|
|
},
|
|
_moveFNC: function (encodingState) {
|
|
if (encodingState.fnc) {
|
|
encodingState.fnc = false;
|
|
return encodingState.previousState == this.key;
|
|
}
|
|
},
|
|
_shiftState: function (encodingState) {
|
|
var that = this;
|
|
if (encodingState.previousState == that.shiftKey && (encodingState.index + 1 >= encodingState.value.length || that.encoding[that.shiftKey].is(encodingState.value, encodingState.index + 1))) {
|
|
that.encoding.addPattern(that.SHIFT);
|
|
encodingState.shifted = true;
|
|
return true;
|
|
}
|
|
},
|
|
_moveState: function () {
|
|
this.encoding.addPattern(this.MOVE);
|
|
return true;
|
|
},
|
|
SHIFT: 98
|
|
});
|
|
var states128 = {};
|
|
states128.A = state128AB.extend({
|
|
key: 'A',
|
|
shiftKey: 'B',
|
|
isCode: function (code) {
|
|
return 0 <= code && code < 96;
|
|
},
|
|
getValue: function (code) {
|
|
if (code < 32) {
|
|
return code + 64;
|
|
}
|
|
return code - 32;
|
|
},
|
|
MOVE: 101,
|
|
START: 103
|
|
});
|
|
states128.B = state128AB.extend({
|
|
key: 'B',
|
|
shiftKey: 'A',
|
|
isCode: function (code) {
|
|
return 32 <= code && code < 128;
|
|
},
|
|
getValue: function (code) {
|
|
return code - 32;
|
|
},
|
|
MOVE: 100,
|
|
START: 104
|
|
});
|
|
states128.C = state128.extend({
|
|
key: 'C',
|
|
addStart: function () {
|
|
this.encoding.addPattern(this.START);
|
|
},
|
|
is: function (value, index) {
|
|
var next4 = getNext(value, index, 4);
|
|
return (index + 4 <= value.length || value.length == 2) && numberRegex.test(next4);
|
|
},
|
|
move: function () {
|
|
this.encoding.addPattern(this.MOVE);
|
|
},
|
|
pushState: function (encodingState) {
|
|
var code;
|
|
while ((code = getNext(encodingState.value, encodingState.index, 2)) && numberRegex.test(code) && code.length == 2) {
|
|
this.encoding.addPattern(parseInt(code, 10));
|
|
encodingState.index += 2;
|
|
}
|
|
},
|
|
getValue: function (code) {
|
|
return code;
|
|
},
|
|
MOVE: 99,
|
|
START: 105
|
|
});
|
|
states128.FNC4 = state128.extend({
|
|
key: 'FNC4',
|
|
dependentStates: [
|
|
'A',
|
|
'B'
|
|
],
|
|
init: function (encoding, states) {
|
|
this.encoding = encoding;
|
|
this._initSubStates(states);
|
|
},
|
|
addStart: function (encodingState) {
|
|
var code = encodingState.value.charCodeAt(0) - 128, subState = this._getSubState(code);
|
|
this.encoding[subState].addStart();
|
|
},
|
|
is: function (value, index) {
|
|
var code = value.charCodeAt(index);
|
|
return this.isCode(code);
|
|
},
|
|
isCode: function (code) {
|
|
return 128 <= code && code < 256;
|
|
},
|
|
pushState: function (encodingState) {
|
|
var that = this, subState = that._initSubState(encodingState), encoding = that.encoding, length = subState.value.length;
|
|
encodingState.index += length;
|
|
if (length < 3) {
|
|
var code;
|
|
for (; subState.index < length; subState.index++) {
|
|
code = subState.value.charCodeAt(subState.index);
|
|
subState.state = that._getSubState(code);
|
|
if (subState.previousState != subState.state) {
|
|
subState.previousState = subState.state;
|
|
encoding[subState.state].move(subState);
|
|
}
|
|
encoding.addPattern(encoding[subState.state].MOVE);
|
|
encoding.addPattern(encoding[subState.state].getValue(code));
|
|
}
|
|
} else {
|
|
if (subState.state != subState.previousState) {
|
|
encoding[subState.state].move(subState);
|
|
}
|
|
that._pushStart(subState);
|
|
encoding.pushData(subState, that.subStates);
|
|
if (encodingState.index < encodingState.value.length) {
|
|
that._pushStart(subState);
|
|
}
|
|
}
|
|
encodingState.fnc = true;
|
|
encodingState.state = subState.state;
|
|
},
|
|
_pushStart: function (subState) {
|
|
var that = this;
|
|
that.encoding.addPattern(that.encoding[subState.state].MOVE);
|
|
that.encoding.addPattern(that.encoding[subState.state].MOVE);
|
|
},
|
|
_initSubState: function (encodingState) {
|
|
var that = this, subState = {
|
|
value: that._getAll(encodingState.value, encodingState.index),
|
|
index: 0
|
|
};
|
|
subState.state = that._getSubState(subState.value.charCodeAt(0));
|
|
subState.previousState = encodingState.previousState == that.key ? subState.state : encodingState.previousState;
|
|
return subState;
|
|
},
|
|
_initSubStates: function (states) {
|
|
var that = this;
|
|
that.subStates = [];
|
|
for (var i = 0; i < states.length; i++) {
|
|
if (inArray(states[i], that.dependentStates) >= 0) {
|
|
that.subStates.push(states[i]);
|
|
}
|
|
}
|
|
},
|
|
_getSubState: function (code) {
|
|
var that = this;
|
|
for (var i = 0; i < that.subStates.length; i++) {
|
|
if (that.encoding[that.subStates[i]].isCode(code)) {
|
|
return that.subStates[i];
|
|
}
|
|
}
|
|
},
|
|
_getAll: function (value, index) {
|
|
var code, result = '';
|
|
while ((code = value.charCodeAt(index++)) && this.isCode(code)) {
|
|
result += String.fromCharCode(code - 128);
|
|
}
|
|
return result;
|
|
}
|
|
});
|
|
states128.FNC1 = state128.extend({
|
|
key: 'FNC1',
|
|
startState: 'C',
|
|
dependentStates: [
|
|
'C',
|
|
'B'
|
|
],
|
|
startAI: '(',
|
|
endAI: ')',
|
|
init: function (encoding, states) {
|
|
this.encoding = encoding;
|
|
this.states = states;
|
|
},
|
|
addStart: function () {
|
|
this.encoding[this.startState].addStart();
|
|
},
|
|
is: function () {
|
|
return inArray(this.key, this.states) >= 0;
|
|
},
|
|
pushState: function (encodingState) {
|
|
var that = this, encoding = that.encoding, value = encodingState.value.replace(/\s/g, ''), regexSeparators = new RegExp('[' + that.startAI + that.endAI + ']', 'g'), index = encodingState.index, subState = { state: that.startState }, current, nextStart, separatorLength;
|
|
encoding.addPattern(that.START);
|
|
while (true) {
|
|
subState.index = 0;
|
|
separatorLength = value.charAt(index) === that.startAI ? 2 : 0;
|
|
current = separatorLength > 0 ? that.getBySeparator(value, index) : that.getByLength(value, index);
|
|
if (current.ai.length) {
|
|
nextStart = index + separatorLength + current.id.length + current.ai.length;
|
|
} else {
|
|
nextStart = value.indexOf(that.startAI, index + 1);
|
|
if (nextStart < 0) {
|
|
if (index + current.ai.max + current.id.length + separatorLength < value.length) {
|
|
throw new Error('Separators are required after variable length identifiers');
|
|
}
|
|
nextStart = value.length;
|
|
}
|
|
}
|
|
subState.value = value.substring(index, nextStart).replace(regexSeparators, '');
|
|
that.validate(current, subState.value);
|
|
encoding.pushData(subState, that.dependentStates);
|
|
if (nextStart >= value.length) {
|
|
break;
|
|
}
|
|
index = nextStart;
|
|
if (subState.state != that.startState) {
|
|
encoding[that.startState].move(subState);
|
|
subState.state = that.startState;
|
|
}
|
|
if (!current.ai.length) {
|
|
encoding.addPattern(that.START);
|
|
}
|
|
}
|
|
encodingState.index = encodingState.value.length;
|
|
},
|
|
validate: function (current, value) {
|
|
var code = value.substr(current.id.length), ai = current.ai;
|
|
if (!ai.type && !numberRegex.test(code)) {
|
|
throw new Error('Application identifier ' + current.id + ' is numeric only but contains non numeric character(s).');
|
|
}
|
|
if (ai.type == 'alphanumeric' && !alphanumericRegex.test(code)) {
|
|
throw new Error('Application identifier ' + current.id + ' is alphanumeric only but contains non alphanumeric character(s).');
|
|
}
|
|
if (ai.length && ai.length !== code.length) {
|
|
throw new Error('Application identifier ' + current.id + ' must be ' + ai.length + ' characters long.');
|
|
}
|
|
if (ai.min && ai.min > code.length) {
|
|
throw new Error('Application identifier ' + current.id + ' must be at least ' + ai.min + ' characters long.');
|
|
}
|
|
if (ai.max && ai.max < code.length) {
|
|
throw new Error('Application identifier ' + current.id + ' must be at most ' + ai.max + ' characters long.');
|
|
}
|
|
},
|
|
getByLength: function (value, index) {
|
|
var that = this, id, ai;
|
|
for (var i = 2; i <= 4; i++) {
|
|
id = getNext(value, index, i);
|
|
ai = that.getAI(id) || that.getAI(id.substring(0, id.length - 1));
|
|
if (ai) {
|
|
return {
|
|
id: id,
|
|
ai: ai
|
|
};
|
|
}
|
|
}
|
|
that.unsupportedAIError(id);
|
|
},
|
|
unsupportedAIError: function (id) {
|
|
throw new Error(kendo.format('\'{0}\' is not a supported Application Identifier'), id);
|
|
},
|
|
getBySeparator: function (value, index) {
|
|
var that = this, start = value.indexOf(that.startAI, index), end = value.indexOf(that.endAI, start), id = value.substring(start + 1, end), ai = that.getAI(id) || that.getAI(id.substr(id.length - 1));
|
|
if (!ai) {
|
|
that.unsupportedAIError(id);
|
|
}
|
|
return {
|
|
ai: ai,
|
|
id: id
|
|
};
|
|
},
|
|
getAI: function (id) {
|
|
var ai = this.applicationIdentifiers, multiKey = ai.multiKey;
|
|
if (ai[id]) {
|
|
return ai[id];
|
|
}
|
|
for (var i = 0; i < multiKey.length; i++) {
|
|
if (multiKey[i].ids && inArray(id, multiKey[i].ids) >= 0) {
|
|
return multiKey[i].type;
|
|
} else if (multiKey[i].ranges) {
|
|
var ranges = multiKey[i].ranges;
|
|
for (var j = 0; j < ranges.length; j++) {
|
|
if (ranges[j][0] <= id && id <= ranges[j][1]) {
|
|
return multiKey[i].type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
applicationIdentifiers: {
|
|
'22': {
|
|
max: 29,
|
|
type: 'alphanumeric'
|
|
},
|
|
'402': { length: 17 },
|
|
'7004': {
|
|
max: 4,
|
|
type: 'alphanumeric'
|
|
},
|
|
'242': {
|
|
max: 6,
|
|
type: 'alphanumeric'
|
|
},
|
|
'8020': {
|
|
max: 25,
|
|
type: 'alphanumeric'
|
|
},
|
|
'703': {
|
|
min: 3,
|
|
max: 30,
|
|
type: 'alphanumeric'
|
|
},
|
|
'8008': {
|
|
min: 8,
|
|
max: 12,
|
|
type: 'alphanumeric'
|
|
},
|
|
'253': {
|
|
min: 13,
|
|
max: 17,
|
|
type: 'alphanumeric'
|
|
},
|
|
'8003': {
|
|
min: 14,
|
|
max: 30,
|
|
type: 'alphanumeric'
|
|
},
|
|
multiKey: [
|
|
{
|
|
ids: [
|
|
'15',
|
|
'17',
|
|
'8005',
|
|
'8100'
|
|
],
|
|
ranges: [
|
|
[
|
|
11,
|
|
13
|
|
],
|
|
[
|
|
310,
|
|
316
|
|
],
|
|
[
|
|
320,
|
|
336
|
|
],
|
|
[
|
|
340,
|
|
369
|
|
]
|
|
],
|
|
type: { length: 6 }
|
|
},
|
|
{
|
|
ids: [
|
|
'240',
|
|
'241',
|
|
'250',
|
|
'251',
|
|
'400',
|
|
'401',
|
|
'403',
|
|
'7002',
|
|
'8004',
|
|
'8007',
|
|
'8110'
|
|
],
|
|
ranges: [[90 - 99]],
|
|
type: {
|
|
max: 30,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: ['7001'],
|
|
ranges: [[
|
|
410,
|
|
414
|
|
]],
|
|
type: { length: 13 }
|
|
},
|
|
{
|
|
ids: [
|
|
'10',
|
|
'21',
|
|
'254',
|
|
'420',
|
|
'8002'
|
|
],
|
|
type: {
|
|
max: 20,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: [
|
|
'00',
|
|
'8006',
|
|
'8017',
|
|
'8018'
|
|
],
|
|
type: { length: 18 }
|
|
},
|
|
{
|
|
ids: [
|
|
'01',
|
|
'02',
|
|
'8001'
|
|
],
|
|
type: { length: 14 }
|
|
},
|
|
{
|
|
ids: ['422'],
|
|
ranges: [[
|
|
424,
|
|
426
|
|
]],
|
|
type: { length: 3 }
|
|
},
|
|
{
|
|
ids: [
|
|
'20',
|
|
'8102'
|
|
],
|
|
type: { length: 2 }
|
|
},
|
|
{
|
|
ids: [
|
|
'30',
|
|
'37'
|
|
],
|
|
type: {
|
|
max: 8,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: [
|
|
'390',
|
|
'392'
|
|
],
|
|
type: {
|
|
max: 15,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: [
|
|
'421',
|
|
'423'
|
|
],
|
|
type: {
|
|
min: 3,
|
|
max: 15,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: [
|
|
'391',
|
|
'393'
|
|
],
|
|
type: {
|
|
min: 3,
|
|
max: 18,
|
|
type: 'alphanumeric'
|
|
}
|
|
},
|
|
{
|
|
ids: [
|
|
'7003',
|
|
'8101'
|
|
],
|
|
type: { length: 10 }
|
|
}
|
|
]
|
|
},
|
|
START: 102
|
|
});
|
|
var code128Base = Encoding.extend({
|
|
init: function (options) {
|
|
Encoding.fn.init.call(this, options);
|
|
this._initStates();
|
|
},
|
|
_initStates: function () {
|
|
var that = this;
|
|
for (var i = 0; i < that.states.length; i++) {
|
|
that[that.states[i]] = new states128[that.states[i]](that, that.states);
|
|
}
|
|
},
|
|
initValue: function (value, width, height) {
|
|
var that = this;
|
|
that.pattern = [];
|
|
that.value = value;
|
|
that.width = width;
|
|
that.height = height;
|
|
that.checkSum = 0;
|
|
that.totalUnits = 0;
|
|
that.index = 0;
|
|
that.position = 1;
|
|
},
|
|
addData: function () {
|
|
var that = this, encodingState = {
|
|
value: that.value,
|
|
index: 0,
|
|
state: ''
|
|
};
|
|
if (that.value.length === 0) {
|
|
return;
|
|
}
|
|
encodingState.state = encodingState.previousState = that.getNextState(encodingState, that.states);
|
|
that.addStart(encodingState);
|
|
that.pushData(encodingState, that.states);
|
|
that.addCheckSum();
|
|
that.addStop();
|
|
that.setBaseUnit();
|
|
},
|
|
pushData: function (encodingState, states) {
|
|
var that = this;
|
|
while (true) {
|
|
that[encodingState.state].pushState(encodingState);
|
|
if (encodingState.index >= encodingState.value.length) {
|
|
break;
|
|
}
|
|
if (!encodingState.shifted) {
|
|
encodingState.previousState = encodingState.state;
|
|
encodingState.state = that.getNextState(encodingState, states);
|
|
that[encodingState.state].move(encodingState);
|
|
} else {
|
|
var temp = encodingState.state;
|
|
encodingState.state = encodingState.previousState;
|
|
encodingState.previousState = temp;
|
|
encodingState.shifted = false;
|
|
}
|
|
}
|
|
},
|
|
addStart: function (encodingState) {
|
|
this[encodingState.state].addStart(encodingState);
|
|
this.position = 1;
|
|
},
|
|
addCheckSum: function () {
|
|
var that = this;
|
|
that.checksum = that.checkSum % 103;
|
|
that.addPattern(that.checksum);
|
|
},
|
|
addStop: function () {
|
|
this.addPattern(this.STOP);
|
|
},
|
|
setBaseUnit: function () {
|
|
var that = this;
|
|
that.baseUnit = that.width / (that.totalUnits + that.quietZoneLength);
|
|
},
|
|
addPattern: function (code) {
|
|
var that = this, pattern = that.characterMap[code].toString(), value;
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
value = parseInt(pattern.charAt(i), 10);
|
|
that.pattern.push(value);
|
|
that.totalUnits += value;
|
|
}
|
|
that.checkSum += code * that.position++;
|
|
},
|
|
getNextState: function (encodingState, states) {
|
|
for (var i = 0; i < states.length; i++) {
|
|
if (this[states[i]].is(encodingState.value, encodingState.index)) {
|
|
return states[i];
|
|
}
|
|
}
|
|
this.invalidCharacterError(encodingState.value.charAt(encodingState.index));
|
|
},
|
|
characterMap: [
|
|
212222,
|
|
222122,
|
|
222221,
|
|
121223,
|
|
121322,
|
|
131222,
|
|
122213,
|
|
122312,
|
|
132212,
|
|
221213,
|
|
221312,
|
|
231212,
|
|
112232,
|
|
122132,
|
|
122231,
|
|
113222,
|
|
123122,
|
|
123221,
|
|
223211,
|
|
221132,
|
|
221231,
|
|
213212,
|
|
223112,
|
|
312131,
|
|
311222,
|
|
321122,
|
|
321221,
|
|
312212,
|
|
322112,
|
|
322211,
|
|
212123,
|
|
212321,
|
|
232121,
|
|
111323,
|
|
131123,
|
|
131321,
|
|
112313,
|
|
132113,
|
|
132311,
|
|
211313,
|
|
231113,
|
|
231311,
|
|
112133,
|
|
112331,
|
|
132131,
|
|
113123,
|
|
113321,
|
|
133121,
|
|
313121,
|
|
211331,
|
|
231131,
|
|
213113,
|
|
213311,
|
|
213131,
|
|
311123,
|
|
311321,
|
|
331121,
|
|
312113,
|
|
312311,
|
|
332111,
|
|
314111,
|
|
221411,
|
|
431111,
|
|
111224,
|
|
111422,
|
|
121124,
|
|
121421,
|
|
141122,
|
|
141221,
|
|
112214,
|
|
112412,
|
|
122114,
|
|
122411,
|
|
142112,
|
|
142211,
|
|
241211,
|
|
221114,
|
|
413111,
|
|
241112,
|
|
134111,
|
|
111242,
|
|
121142,
|
|
121241,
|
|
114212,
|
|
124112,
|
|
124211,
|
|
411212,
|
|
421112,
|
|
421211,
|
|
212141,
|
|
214121,
|
|
412121,
|
|
111143,
|
|
111341,
|
|
131141,
|
|
114113,
|
|
114311,
|
|
411113,
|
|
411311,
|
|
113141,
|
|
114131,
|
|
311141,
|
|
411131,
|
|
211412,
|
|
211214,
|
|
211232,
|
|
2331112
|
|
],
|
|
STOP: 106
|
|
});
|
|
encodings.code128a = code128Base.extend({
|
|
name: 'Code 128 A',
|
|
states: ['A']
|
|
});
|
|
encodings.code128b = code128Base.extend({
|
|
name: 'Code 128 B',
|
|
states: ['B']
|
|
});
|
|
encodings.code128c = code128Base.extend({
|
|
name: 'Code 128 C',
|
|
states: ['C']
|
|
});
|
|
encodings.code128 = code128Base.extend({
|
|
name: 'Code 128',
|
|
states: [
|
|
'C',
|
|
'B',
|
|
'A',
|
|
'FNC4'
|
|
]
|
|
});
|
|
encodings['gs1-128'] = code128Base.extend({
|
|
name: 'Code GS1-128',
|
|
states: [
|
|
'FNC1',
|
|
'C',
|
|
'B'
|
|
]
|
|
});
|
|
var msiBase = Encoding.extend({
|
|
initValue: function (value, width) {
|
|
var that = this;
|
|
that.pattern = [];
|
|
that.value = value;
|
|
that.checkSumLength = 0;
|
|
that.width = width;
|
|
},
|
|
setBaseUnit: function () {
|
|
var that = this, startStopLength = 7;
|
|
that.baseUnit = that.width / (12 * (that.value.length + that.checkSumLength) + that.quietZoneLength + startStopLength);
|
|
},
|
|
addData: function () {
|
|
var that = this, value = that.value;
|
|
that.addPattern(that.START);
|
|
for (var i = 0; i < value.length; i++) {
|
|
that.addCharacter(value.charAt(i));
|
|
}
|
|
if (that.options.addCheckSum) {
|
|
that.addCheckSum();
|
|
}
|
|
that.addPattern(that.STOP);
|
|
that.setBaseUnit();
|
|
},
|
|
addCharacter: function (character) {
|
|
var that = this, pattern = that.characterMap[character];
|
|
if (!pattern) {
|
|
that.invalidCharacterError(character);
|
|
}
|
|
that.addPattern(pattern);
|
|
},
|
|
addPattern: function (pattern) {
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
this.pattern.push(parseInt(pattern.charAt(i), 10));
|
|
}
|
|
},
|
|
addCheckSum: function () {
|
|
var that = this, checkSumFunction = that.checkSums[that.checkSumType], checkValues;
|
|
checkValues = checkSumFunction.call(that.checkSums, that.value);
|
|
that.checksum = checkValues.join('');
|
|
for (var i = 0; i < checkValues.length; i++) {
|
|
that.checkSumLength++;
|
|
that.addPattern(that.characterMap[checkValues[i]]);
|
|
}
|
|
},
|
|
checkSums: {
|
|
Modulo10: function (value) {
|
|
var checkValues = [
|
|
0,
|
|
''
|
|
], odd = value.length % 2, idx, evenSum, oddSum;
|
|
for (idx = 0; idx < value.length; idx++) {
|
|
checkValues[(idx + odd) % 2] += parseInt(value.charAt(idx), 10);
|
|
}
|
|
oddSum = checkValues[0];
|
|
evenSum = (checkValues[1] * 2).toString();
|
|
for (idx = 0; idx < evenSum.length; idx++) {
|
|
oddSum += parseInt(evenSum.charAt(idx), 10);
|
|
}
|
|
return [(10 - oddSum % 10) % 10];
|
|
},
|
|
Modulo11: function (value) {
|
|
var weightedSum = 0, mod = 11, length = value.length, weight, checkValue;
|
|
for (var i = 0; i < length; i++) {
|
|
weight = ((length - i) % 6 || 6) + 1;
|
|
weightedSum += weight * value.charAt(i);
|
|
}
|
|
checkValue = (mod - weightedSum % mod) % mod;
|
|
if (checkValue != 10) {
|
|
return [checkValue];
|
|
}
|
|
return [
|
|
1,
|
|
0
|
|
];
|
|
},
|
|
Modulo11Modulo10: function (value) {
|
|
var checkValues = this.Modulo11(value), mod11Value;
|
|
mod11Value = value + checkValues[0];
|
|
return checkValues.concat(this.Modulo10(mod11Value));
|
|
},
|
|
Modulo10Modulo10: function (value) {
|
|
var checkValues = this.Modulo10(value), mod10Value;
|
|
mod10Value = value + checkValues[0];
|
|
return checkValues.concat(this.Modulo10(mod10Value));
|
|
}
|
|
},
|
|
characterMap: [
|
|
'12121212',
|
|
'12121221',
|
|
'12122112',
|
|
'12122121',
|
|
'12211212',
|
|
'12211221',
|
|
'12212112',
|
|
'12212121',
|
|
'21121212',
|
|
'21121221'
|
|
],
|
|
START: '21',
|
|
STOP: '121',
|
|
checkSumType: ''
|
|
});
|
|
encodings.msimod10 = msiBase.extend({
|
|
name: 'MSI Modulo10',
|
|
checkSumType: 'Modulo10'
|
|
});
|
|
encodings.msimod11 = msiBase.extend({
|
|
name: 'MSI Modulo11',
|
|
checkSumType: 'Modulo11'
|
|
});
|
|
encodings.msimod1110 = msiBase.extend({
|
|
name: 'MSI Modulo11 Modulo10',
|
|
checkSumType: 'Modulo11Modulo10'
|
|
});
|
|
encodings.msimod1010 = msiBase.extend({
|
|
name: 'MSI Modulo10 Modulo10',
|
|
checkSumType: 'Modulo10Modulo10'
|
|
});
|
|
encodings.code11 = Encoding.extend({
|
|
name: 'Code 11',
|
|
cCheckSumTotal: 10,
|
|
kCheckSumTotal: 9,
|
|
kCheckSumMinLength: 10,
|
|
checkSumMod: 11,
|
|
DASH_VALUE: 10,
|
|
DASH: '-',
|
|
START: '112211',
|
|
STOP: '11221',
|
|
initValue: function (value, width) {
|
|
var that = this;
|
|
that.pattern = [];
|
|
that.value = value;
|
|
that.width = width;
|
|
that.totalUnits = 0;
|
|
},
|
|
addData: function () {
|
|
var that = this;
|
|
var value = that.value;
|
|
that.addPattern(that.START);
|
|
for (var i = 0; i < value.length; i++) {
|
|
that.addCharacter(value.charAt(i));
|
|
}
|
|
if (that.options.addCheckSum) {
|
|
that.addCheckSum();
|
|
}
|
|
that.addPattern(that.STOP);
|
|
that.setBaseUnit();
|
|
},
|
|
setBaseUnit: function () {
|
|
var that = this;
|
|
that.baseUnit = that.width / (that.totalUnits + that.quietZoneLength);
|
|
},
|
|
addCheckSum: function () {
|
|
var that = this, value = that.value, length = value.length, cValue;
|
|
cValue = that.getWeightedSum(value, length, that.cCheckSumTotal) % that.checkSumMod;
|
|
that.checksum = cValue + '';
|
|
that.addPattern(that.characterMap[cValue]);
|
|
length++;
|
|
if (length >= that.kCheckSumMinLength) {
|
|
var kValue = (cValue + that.getWeightedSum(value, length, that.kCheckSumTotal)) % that.checkSumMod;
|
|
that.checksum += kValue;
|
|
that.addPattern(that.characterMap[kValue]);
|
|
}
|
|
},
|
|
getWeightedSum: function (value, length, total) {
|
|
var weightedSum = 0;
|
|
for (var i = 0; i < value.length; i++) {
|
|
weightedSum += this.weightedValue(this.getValue(value.charAt(i)), length, i, total);
|
|
}
|
|
return weightedSum;
|
|
},
|
|
weightedValue: function (value, length, index, total) {
|
|
var weight = (length - index) % total || total;
|
|
return weight * value;
|
|
},
|
|
getValue: function (character) {
|
|
var that = this;
|
|
if (!isNaN(character)) {
|
|
return parseInt(character, 10);
|
|
} else if (character !== that.DASH) {
|
|
that.invalidCharacterError(character);
|
|
}
|
|
return that.DASH_VALUE;
|
|
},
|
|
addCharacter: function (character) {
|
|
var that = this, value = that.getValue(character), pattern = that.characterMap[value];
|
|
that.addPattern(pattern);
|
|
},
|
|
addPattern: function (pattern) {
|
|
var value;
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
value = parseInt(pattern.charAt(i), 10);
|
|
this.pattern.push(value);
|
|
this.totalUnits += value;
|
|
}
|
|
},
|
|
characterMap: [
|
|
'111121',
|
|
'211121',
|
|
'121121',
|
|
'221111',
|
|
'112121',
|
|
'212111',
|
|
'122111',
|
|
'111221',
|
|
'211211',
|
|
'211111',
|
|
'112111'
|
|
],
|
|
options: { addCheckSum: true }
|
|
});
|
|
encodings.postnet = Encoding.extend({
|
|
name: 'Postnet',
|
|
START: '2',
|
|
VALID_CODE_LENGTHS: [
|
|
5,
|
|
9,
|
|
11
|
|
],
|
|
DIGIT_SEPARATOR: '-',
|
|
initValue: function (value, width, height) {
|
|
var that = this;
|
|
that.height = height;
|
|
that.width = width;
|
|
that.baseHeight = height / 2;
|
|
that.value = value.replace(new RegExp(that.DIGIT_SEPARATOR, 'g'), '');
|
|
that.pattern = [];
|
|
that.validate(that.value);
|
|
that.checkSum = 0;
|
|
that.setBaseUnit();
|
|
},
|
|
addData: function () {
|
|
var that = this, value = that.value;
|
|
that.addPattern(that.START);
|
|
for (var i = 0; i < value.length; i++) {
|
|
that.addCharacter(value.charAt(i));
|
|
}
|
|
if (that.options.addCheckSum) {
|
|
that.addCheckSum();
|
|
}
|
|
that.addPattern(that.START);
|
|
that.pattern.pop();
|
|
},
|
|
addCharacter: function (character) {
|
|
var that = this, pattern = that.characterMap[character];
|
|
that.checkSum += parseInt(character, 10);
|
|
that.addPattern(pattern);
|
|
},
|
|
addCheckSum: function () {
|
|
var that = this;
|
|
that.checksum = (10 - that.checkSum % 10) % 10;
|
|
that.addCharacter(that.checksum);
|
|
},
|
|
setBaseUnit: function () {
|
|
var that = this, startStopLength = 3;
|
|
that.baseUnit = that.width / ((that.value.length + 1) * 10 + startStopLength + that.quietZoneLength);
|
|
},
|
|
validate: function (value) {
|
|
var that = this;
|
|
if (!numberRegex.test(value)) {
|
|
that.invalidCharacterError(value.match(/[^0-9]/)[0]);
|
|
}
|
|
if (inArray(value.length, that.VALID_CODE_LENGTHS) < 0) {
|
|
throw new Error('Invalid value length. Valid lengths for the Postnet symbology are ' + that.VALID_CODE_LENGTHS.join(','));
|
|
}
|
|
},
|
|
addPattern: function (pattern) {
|
|
var that = this, y1;
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
y1 = that.height - that.baseHeight * pattern.charAt(i);
|
|
that.pattern.push({
|
|
width: 1,
|
|
y1: y1,
|
|
y2: that.height
|
|
});
|
|
that.pattern.push(1);
|
|
}
|
|
},
|
|
characterMap: [
|
|
'22111',
|
|
'11122',
|
|
'11212',
|
|
'11221',
|
|
'12112',
|
|
'12121',
|
|
'12211',
|
|
'21112',
|
|
'21121',
|
|
'21211'
|
|
]
|
|
});
|
|
encodings.ean13 = Encoding.extend({
|
|
initValue: function (value, width, height) {
|
|
value += '';
|
|
if (value.length != 12 || /\D/.test(value)) {
|
|
throw new Error('The value of the "EAN13" encoding should be 12 symbols');
|
|
}
|
|
var that = this;
|
|
that.pattern = [];
|
|
that.options.height = height;
|
|
that.baseUnit = width / (95 + that.quietZoneLength);
|
|
that.value = value;
|
|
that.checksum = that.calculateChecksum();
|
|
that.leftKey = value[0];
|
|
that.leftPart = value.substr(1, 6);
|
|
that.rightPart = value.substr(7) + that.checksum;
|
|
},
|
|
addData: function () {
|
|
var that = this;
|
|
that.addPieces(that.characterMap.start);
|
|
that.addSide(that.leftPart, that.leftKey);
|
|
that.addPieces(that.characterMap.middle);
|
|
that.addSide(that.rightPart);
|
|
that.addPieces(that.characterMap.start);
|
|
},
|
|
addSide: function (leftPart, key) {
|
|
var that = this;
|
|
for (var i = 0; i < leftPart.length; i++) {
|
|
if (key && parseInt(that.keyTable[key].charAt(i), 10)) {
|
|
that.addPieces(Array.prototype.slice.call(that.characterMap.digits[leftPart.charAt(i)]).reverse(), true);
|
|
} else {
|
|
that.addPieces(that.characterMap.digits[leftPart.charAt(i)], true);
|
|
}
|
|
}
|
|
},
|
|
addPieces: function (arrToAdd, limitedHeight) {
|
|
var that = this;
|
|
for (var i = 0; i < arrToAdd.length; i++) {
|
|
if (limitedHeight) {
|
|
that.pattern.push({
|
|
y1: 0,
|
|
y2: that.options.height * 0.95,
|
|
width: arrToAdd[i]
|
|
});
|
|
} else {
|
|
that.pattern.push(arrToAdd[i]);
|
|
}
|
|
}
|
|
},
|
|
calculateChecksum: function () {
|
|
var odd = 0, even = 0, value = this.value.split('').reverse().join('');
|
|
for (var i = 0; i < value.length; i++) {
|
|
if (i % 2) {
|
|
even += parseInt(value.charAt(i), 10);
|
|
} else {
|
|
odd += parseInt(value.charAt(i), 10);
|
|
}
|
|
}
|
|
var checksum = (10 - (3 * odd + even) % 10) % 10;
|
|
return checksum;
|
|
},
|
|
keyTable: [
|
|
'000000',
|
|
'001011',
|
|
'001101',
|
|
'001110',
|
|
'010011',
|
|
'011001',
|
|
'011100',
|
|
'010101',
|
|
'010110',
|
|
'011010'
|
|
],
|
|
characterMap: {
|
|
digits: [
|
|
[
|
|
3,
|
|
2,
|
|
1,
|
|
1
|
|
],
|
|
[
|
|
2,
|
|
2,
|
|
2,
|
|
1
|
|
],
|
|
[
|
|
2,
|
|
1,
|
|
2,
|
|
2
|
|
],
|
|
[
|
|
1,
|
|
4,
|
|
1,
|
|
1
|
|
],
|
|
[
|
|
1,
|
|
1,
|
|
3,
|
|
2
|
|
],
|
|
[
|
|
1,
|
|
2,
|
|
3,
|
|
1
|
|
],
|
|
[
|
|
1,
|
|
1,
|
|
1,
|
|
4
|
|
],
|
|
[
|
|
1,
|
|
3,
|
|
1,
|
|
2
|
|
],
|
|
[
|
|
1,
|
|
2,
|
|
1,
|
|
3
|
|
],
|
|
[
|
|
3,
|
|
1,
|
|
1,
|
|
2
|
|
]
|
|
],
|
|
start: [
|
|
1,
|
|
1,
|
|
1
|
|
],
|
|
middle: [
|
|
1,
|
|
1,
|
|
1,
|
|
1,
|
|
1
|
|
]
|
|
}
|
|
});
|
|
encodings.ean8 = encodings.ean13.extend({
|
|
initValue: function (value, width, height) {
|
|
var that = this;
|
|
if (value.length != 7 || /\D/.test(value)) {
|
|
throw new Error('Invalid value provided');
|
|
}
|
|
that.value = value;
|
|
that.options.height = height;
|
|
that.checksum = that.calculateChecksum(that.value);
|
|
that.leftPart = that.value.substr(0, 4);
|
|
that.rightPart = that.value.substr(4) + that.checksum;
|
|
that.pattern = [];
|
|
that.baseUnit = width / (67 + that.quietZoneLength);
|
|
}
|
|
});
|
|
var Barcode = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.element = $(element);
|
|
that.wrapper = that.element;
|
|
that.element.addClass('k-barcode').css('display', 'block');
|
|
that.surfaceWrap = $('<div />').css('position', 'relative').appendTo(this.element);
|
|
that.surface = draw.Surface.create(that.surfaceWrap, { type: that.options.renderAs });
|
|
that.setOptions(options);
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
that.type = (options.type || that.options.type).toLowerCase();
|
|
if (that.type == 'upca') {
|
|
that.type = 'ean13';
|
|
options.value = '0' + options.value;
|
|
}
|
|
if (that.type == 'upce') {
|
|
that.type = 'ean8';
|
|
options.value = '0' + options.value;
|
|
}
|
|
if (!encodings[that.type]) {
|
|
throw new Error('Encoding ' + that.type + 'is not supported.');
|
|
}
|
|
that.encoding = new encodings[that.type]();
|
|
that.options = extend(true, that.options, options);
|
|
if (!defined(options.value)) {
|
|
return;
|
|
}
|
|
that.redraw();
|
|
},
|
|
redraw: function () {
|
|
var size = this._getSize();
|
|
this.surface.clear();
|
|
this.surface.setSize({
|
|
width: size.width,
|
|
height: size.height
|
|
});
|
|
this.createVisual();
|
|
this.surface.draw(this.visual);
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.element);
|
|
},
|
|
_resize: function () {
|
|
this.redraw();
|
|
},
|
|
createVisual: function () {
|
|
this.visual = this._render();
|
|
},
|
|
_render: function () {
|
|
var that = this, options = that.options, value = options.value, textOptions = options.text, textMargin = dataviz.getSpacing(textOptions.margin), size = that._getSize(), border = options.border || {}, encoding = that.encoding, contentBox = Box2D(0, 0, size.width, size.height).unpad(border.width).unpad(options.padding), barHeight = contentBox.height(), result, textToDisplay, textHeight;
|
|
var visual = new draw.Group();
|
|
that.contentBox = contentBox;
|
|
visual.append(that._getBackground(size));
|
|
if (textOptions.visible) {
|
|
textHeight = draw.util.measureText(value, { font: textOptions.font }).height;
|
|
barHeight -= textHeight + textMargin.top + textMargin.bottom;
|
|
}
|
|
result = encoding.encode(value, contentBox.width(), barHeight);
|
|
if (textOptions.visible) {
|
|
textToDisplay = value;
|
|
if (options.checksum && defined(encoding.checksum)) {
|
|
textToDisplay += ' ' + encoding.checksum;
|
|
}
|
|
visual.append(that._getText(textToDisplay));
|
|
}
|
|
that.barHeight = barHeight;
|
|
this._bandsGroup = this._getBands(result.pattern, result.baseUnit);
|
|
visual.append(this._bandsGroup);
|
|
return visual;
|
|
},
|
|
exportVisual: function () {
|
|
return this._render();
|
|
},
|
|
_getSize: function () {
|
|
var that = this, element = that.element, size = new geom.Size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
if (element.width() > 0) {
|
|
size.width = element.width();
|
|
}
|
|
if (element.height() > 0) {
|
|
size.height = element.height();
|
|
}
|
|
if (that.options.width) {
|
|
size.width = that.options.width;
|
|
}
|
|
if (that.options.height) {
|
|
size.height = that.options.height;
|
|
}
|
|
return size;
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (!defined(value)) {
|
|
return that.options.value;
|
|
}
|
|
that.options.value = value + '';
|
|
that.redraw();
|
|
},
|
|
_getBands: function (pattern, baseUnit) {
|
|
var that = this, contentBox = that.contentBox, position = contentBox.x1, step, item;
|
|
var group = new draw.Group();
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
item = isPlainObject(pattern[i]) ? pattern[i] : {
|
|
width: pattern[i],
|
|
y1: 0,
|
|
y2: that.barHeight
|
|
};
|
|
step = item.width * baseUnit;
|
|
if (i % 2) {
|
|
var rect = geom.Rect.fromPoints(new geom.Point(position, item.y1 + contentBox.y1), new geom.Point(position + step, item.y2 + contentBox.y1));
|
|
var path = draw.Path.fromRect(rect, {
|
|
fill: { color: that.options.color },
|
|
stroke: null
|
|
});
|
|
group.append(path);
|
|
}
|
|
position += step;
|
|
}
|
|
return group;
|
|
},
|
|
_getBackground: function (size) {
|
|
var that = this, options = that.options, border = options.border || {};
|
|
var box = Box2D(0, 0, size.width, size.height).unpad(border.width / 2);
|
|
var path = draw.Path.fromRect(box.toRect(), {
|
|
fill: { color: options.background },
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType
|
|
}
|
|
});
|
|
return path;
|
|
},
|
|
_getText: function (value) {
|
|
var that = this, textOptions = that.options.text, text = that._textbox = new TextBox(value, {
|
|
font: textOptions.font,
|
|
color: textOptions.color,
|
|
align: 'center',
|
|
vAlign: 'bottom',
|
|
margin: textOptions.margin
|
|
});
|
|
text.reflow(that.contentBox);
|
|
text.renderVisual();
|
|
return text.visual;
|
|
},
|
|
options: {
|
|
name: 'Barcode',
|
|
renderAs: 'svg',
|
|
value: '',
|
|
type: 'code39',
|
|
checksum: false,
|
|
width: 0,
|
|
height: 0,
|
|
color: 'black',
|
|
background: 'white',
|
|
text: {
|
|
visible: true,
|
|
font: '16px Consolas, Monaco, Sans Mono, monospace, sans-serif',
|
|
color: 'black',
|
|
margin: {
|
|
top: 0,
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0
|
|
}
|
|
},
|
|
border: {
|
|
width: 0,
|
|
dashType: 'solid',
|
|
color: 'black'
|
|
},
|
|
padding: {
|
|
top: 0,
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0
|
|
}
|
|
}
|
|
});
|
|
dataviz.ExportMixin.extend(Barcode.fn);
|
|
dataviz.ui.plugin(Barcode);
|
|
kendo.deepExtend(dataviz, {
|
|
encodings: encodings,
|
|
Encoding: Encoding
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.qrcode', [
|
|
'kendo.dataviz.core',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.qrcode',
|
|
name: 'QRCode',
|
|
category: 'dataviz',
|
|
description: 'QRCode widget.',
|
|
depends: [
|
|
'dataviz.core',
|
|
'drawing'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, extend = $.extend, draw = kendo.drawing, dataviz = kendo.dataviz, Widget = kendo.ui.Widget, Box2D = dataviz.Box2D, terminator = '0000', NUMERIC = 'numeric', ALPHA_NUMERIC = 'alphanumeric', BYTE = 'byte', powersOfTwo = { '1': 0 }, powersOfTwoResult = { '0': 1 }, generatorPolynomials = [
|
|
[
|
|
1,
|
|
0
|
|
],
|
|
[
|
|
1,
|
|
25,
|
|
0
|
|
]
|
|
], irregularAlignmentPatternsStartDistance = {
|
|
15: 20,
|
|
16: 20,
|
|
18: 24,
|
|
19: 24,
|
|
22: 20,
|
|
24: 22,
|
|
26: 24,
|
|
28: 20,
|
|
30: 20,
|
|
31: 24,
|
|
32: 28,
|
|
33: 24,
|
|
36: 18,
|
|
37: 22,
|
|
39: 20,
|
|
40: 24
|
|
}, versionsCodewordsInformation = [
|
|
{
|
|
L: {
|
|
groups: [[
|
|
1,
|
|
19
|
|
]],
|
|
totalDataCodewords: 19,
|
|
errorCodewordsPerBlock: 7
|
|
},
|
|
M: {
|
|
groups: [[
|
|
1,
|
|
16
|
|
]],
|
|
totalDataCodewords: 16,
|
|
errorCodewordsPerBlock: 10
|
|
},
|
|
Q: {
|
|
groups: [[
|
|
1,
|
|
13
|
|
]],
|
|
totalDataCodewords: 13,
|
|
errorCodewordsPerBlock: 13
|
|
},
|
|
H: {
|
|
groups: [[
|
|
1,
|
|
9
|
|
]],
|
|
totalDataCodewords: 9,
|
|
errorCodewordsPerBlock: 17
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
1,
|
|
34
|
|
]],
|
|
totalDataCodewords: 34,
|
|
errorCodewordsPerBlock: 10
|
|
},
|
|
M: {
|
|
groups: [[
|
|
1,
|
|
28
|
|
]],
|
|
totalDataCodewords: 28,
|
|
errorCodewordsPerBlock: 16
|
|
},
|
|
Q: {
|
|
groups: [[
|
|
1,
|
|
22
|
|
]],
|
|
totalDataCodewords: 22,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
H: {
|
|
groups: [[
|
|
1,
|
|
16
|
|
]],
|
|
totalDataCodewords: 16,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
1,
|
|
55
|
|
]],
|
|
totalDataCodewords: 55,
|
|
errorCodewordsPerBlock: 15
|
|
},
|
|
M: {
|
|
groups: [[
|
|
1,
|
|
44
|
|
]],
|
|
totalDataCodewords: 44,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [[
|
|
2,
|
|
17
|
|
]],
|
|
totalDataCodewords: 34,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
H: {
|
|
groups: [[
|
|
2,
|
|
13
|
|
]],
|
|
totalDataCodewords: 26,
|
|
errorCodewordsPerBlock: 22
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
1,
|
|
80
|
|
]],
|
|
totalDataCodewords: 80,
|
|
errorCodewordsPerBlock: 20
|
|
},
|
|
M: {
|
|
groups: [[
|
|
2,
|
|
32
|
|
]],
|
|
totalDataCodewords: 64,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
Q: {
|
|
groups: [[
|
|
2,
|
|
24
|
|
]],
|
|
totalDataCodewords: 48,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
H: {
|
|
groups: [[
|
|
4,
|
|
9
|
|
]],
|
|
totalDataCodewords: 36,
|
|
errorCodewordsPerBlock: 16
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
1,
|
|
108
|
|
]],
|
|
totalDataCodewords: 108,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
M: {
|
|
groups: [[
|
|
2,
|
|
43
|
|
]],
|
|
totalDataCodewords: 86,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
2,
|
|
15
|
|
],
|
|
[
|
|
2,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 62,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
2,
|
|
11
|
|
],
|
|
[
|
|
2,
|
|
12
|
|
]
|
|
],
|
|
totalDataCodewords: 46,
|
|
errorCodewordsPerBlock: 22
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
2,
|
|
68
|
|
]],
|
|
totalDataCodewords: 136,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
M: {
|
|
groups: [[
|
|
4,
|
|
27
|
|
]],
|
|
totalDataCodewords: 108,
|
|
errorCodewordsPerBlock: 16
|
|
},
|
|
Q: {
|
|
groups: [[
|
|
4,
|
|
19
|
|
]],
|
|
totalDataCodewords: 76,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
H: {
|
|
groups: [[
|
|
4,
|
|
15
|
|
]],
|
|
totalDataCodewords: 60,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
2,
|
|
78
|
|
]],
|
|
totalDataCodewords: 156,
|
|
errorCodewordsPerBlock: 20
|
|
},
|
|
M: {
|
|
groups: [[
|
|
4,
|
|
31
|
|
]],
|
|
totalDataCodewords: 124,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
2,
|
|
14
|
|
],
|
|
[
|
|
4,
|
|
15
|
|
]
|
|
],
|
|
totalDataCodewords: 88,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
4,
|
|
13
|
|
],
|
|
[
|
|
1,
|
|
14
|
|
]
|
|
],
|
|
totalDataCodewords: 66,
|
|
errorCodewordsPerBlock: 26
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
2,
|
|
97
|
|
]],
|
|
totalDataCodewords: 194,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
2,
|
|
38
|
|
],
|
|
[
|
|
2,
|
|
39
|
|
]
|
|
],
|
|
totalDataCodewords: 154,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
4,
|
|
18
|
|
],
|
|
[
|
|
2,
|
|
19
|
|
]
|
|
],
|
|
totalDataCodewords: 110,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
4,
|
|
14
|
|
],
|
|
[
|
|
2,
|
|
15
|
|
]
|
|
],
|
|
totalDataCodewords: 86,
|
|
errorCodewordsPerBlock: 26
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
2,
|
|
116
|
|
]],
|
|
totalDataCodewords: 232,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
3,
|
|
36
|
|
],
|
|
[
|
|
2,
|
|
37
|
|
]
|
|
],
|
|
totalDataCodewords: 182,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
4,
|
|
16
|
|
],
|
|
[
|
|
4,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 132,
|
|
errorCodewordsPerBlock: 20
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
4,
|
|
12
|
|
],
|
|
[
|
|
4,
|
|
13
|
|
]
|
|
],
|
|
totalDataCodewords: 100,
|
|
errorCodewordsPerBlock: 24
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
2,
|
|
68
|
|
],
|
|
[
|
|
2,
|
|
69
|
|
]
|
|
],
|
|
totalDataCodewords: 274,
|
|
errorCodewordsPerBlock: 18
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
4,
|
|
43
|
|
],
|
|
[
|
|
1,
|
|
44
|
|
]
|
|
],
|
|
totalDataCodewords: 216,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
6,
|
|
19
|
|
],
|
|
[
|
|
2,
|
|
20
|
|
]
|
|
],
|
|
totalDataCodewords: 154,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
6,
|
|
15
|
|
],
|
|
[
|
|
2,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 122,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
4,
|
|
81
|
|
]],
|
|
totalDataCodewords: 324,
|
|
errorCodewordsPerBlock: 20
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
1,
|
|
50
|
|
],
|
|
[
|
|
4,
|
|
51
|
|
]
|
|
],
|
|
totalDataCodewords: 254,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
4,
|
|
22
|
|
],
|
|
[
|
|
4,
|
|
23
|
|
]
|
|
],
|
|
totalDataCodewords: 180,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
3,
|
|
12
|
|
],
|
|
[
|
|
8,
|
|
13
|
|
]
|
|
],
|
|
totalDataCodewords: 140,
|
|
errorCodewordsPerBlock: 24
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
2,
|
|
92
|
|
],
|
|
[
|
|
2,
|
|
93
|
|
]
|
|
],
|
|
totalDataCodewords: 370,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
6,
|
|
36
|
|
],
|
|
[
|
|
2,
|
|
37
|
|
]
|
|
],
|
|
totalDataCodewords: 290,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
4,
|
|
20
|
|
],
|
|
[
|
|
6,
|
|
21
|
|
]
|
|
],
|
|
totalDataCodewords: 206,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
7,
|
|
14
|
|
],
|
|
[
|
|
4,
|
|
15
|
|
]
|
|
],
|
|
totalDataCodewords: 158,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
4,
|
|
107
|
|
]],
|
|
totalDataCodewords: 428,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
8,
|
|
37
|
|
],
|
|
[
|
|
1,
|
|
38
|
|
]
|
|
],
|
|
totalDataCodewords: 334,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
8,
|
|
20
|
|
],
|
|
[
|
|
4,
|
|
21
|
|
]
|
|
],
|
|
totalDataCodewords: 244,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
12,
|
|
11
|
|
],
|
|
[
|
|
4,
|
|
12
|
|
]
|
|
],
|
|
totalDataCodewords: 180,
|
|
errorCodewordsPerBlock: 22
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
3,
|
|
115
|
|
],
|
|
[
|
|
1,
|
|
116
|
|
]
|
|
],
|
|
totalDataCodewords: 461,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
4,
|
|
40
|
|
],
|
|
[
|
|
5,
|
|
41
|
|
]
|
|
],
|
|
totalDataCodewords: 365,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
11,
|
|
16
|
|
],
|
|
[
|
|
5,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 261,
|
|
errorCodewordsPerBlock: 20
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
11,
|
|
12
|
|
],
|
|
[
|
|
5,
|
|
13
|
|
]
|
|
],
|
|
totalDataCodewords: 197,
|
|
errorCodewordsPerBlock: 24
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
5,
|
|
87
|
|
],
|
|
[
|
|
1,
|
|
88
|
|
]
|
|
],
|
|
totalDataCodewords: 523,
|
|
errorCodewordsPerBlock: 22
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
5,
|
|
41
|
|
],
|
|
[
|
|
5,
|
|
42
|
|
]
|
|
],
|
|
totalDataCodewords: 415,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
5,
|
|
24
|
|
],
|
|
[
|
|
7,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 295,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
11,
|
|
12
|
|
],
|
|
[
|
|
7,
|
|
13
|
|
]
|
|
],
|
|
totalDataCodewords: 223,
|
|
errorCodewordsPerBlock: 24
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
5,
|
|
98
|
|
],
|
|
[
|
|
1,
|
|
99
|
|
]
|
|
],
|
|
totalDataCodewords: 589,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
7,
|
|
45
|
|
],
|
|
[
|
|
3,
|
|
46
|
|
]
|
|
],
|
|
totalDataCodewords: 453,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
15,
|
|
19
|
|
],
|
|
[
|
|
2,
|
|
20
|
|
]
|
|
],
|
|
totalDataCodewords: 325,
|
|
errorCodewordsPerBlock: 24
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
3,
|
|
15
|
|
],
|
|
[
|
|
13,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 253,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
1,
|
|
107
|
|
],
|
|
[
|
|
5,
|
|
108
|
|
]
|
|
],
|
|
totalDataCodewords: 647,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
10,
|
|
46
|
|
],
|
|
[
|
|
1,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 507,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
1,
|
|
22
|
|
],
|
|
[
|
|
15,
|
|
23
|
|
]
|
|
],
|
|
totalDataCodewords: 367,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
2,
|
|
14
|
|
],
|
|
[
|
|
17,
|
|
15
|
|
]
|
|
],
|
|
totalDataCodewords: 283,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
5,
|
|
120
|
|
],
|
|
[
|
|
1,
|
|
121
|
|
]
|
|
],
|
|
totalDataCodewords: 721,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
9,
|
|
43
|
|
],
|
|
[
|
|
4,
|
|
44
|
|
]
|
|
],
|
|
totalDataCodewords: 563,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
17,
|
|
22
|
|
],
|
|
[
|
|
1,
|
|
23
|
|
]
|
|
],
|
|
totalDataCodewords: 397,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
2,
|
|
14
|
|
],
|
|
[
|
|
19,
|
|
15
|
|
]
|
|
],
|
|
totalDataCodewords: 313,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
3,
|
|
113
|
|
],
|
|
[
|
|
4,
|
|
114
|
|
]
|
|
],
|
|
totalDataCodewords: 795,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
3,
|
|
44
|
|
],
|
|
[
|
|
11,
|
|
45
|
|
]
|
|
],
|
|
totalDataCodewords: 627,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
17,
|
|
21
|
|
],
|
|
[
|
|
4,
|
|
22
|
|
]
|
|
],
|
|
totalDataCodewords: 445,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
9,
|
|
13
|
|
],
|
|
[
|
|
16,
|
|
14
|
|
]
|
|
],
|
|
totalDataCodewords: 341,
|
|
errorCodewordsPerBlock: 26
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
3,
|
|
107
|
|
],
|
|
[
|
|
5,
|
|
108
|
|
]
|
|
],
|
|
totalDataCodewords: 861,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
3,
|
|
41
|
|
],
|
|
[
|
|
13,
|
|
42
|
|
]
|
|
],
|
|
totalDataCodewords: 669,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
15,
|
|
24
|
|
],
|
|
[
|
|
5,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 485,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
15,
|
|
15
|
|
],
|
|
[
|
|
10,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 385,
|
|
errorCodewordsPerBlock: 28
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
4,
|
|
116
|
|
],
|
|
[
|
|
4,
|
|
117
|
|
]
|
|
],
|
|
totalDataCodewords: 932,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [[
|
|
17,
|
|
42
|
|
]],
|
|
totalDataCodewords: 714,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
17,
|
|
22
|
|
],
|
|
[
|
|
6,
|
|
23
|
|
]
|
|
],
|
|
totalDataCodewords: 512,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
19,
|
|
16
|
|
],
|
|
[
|
|
6,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 406,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
2,
|
|
111
|
|
],
|
|
[
|
|
7,
|
|
112
|
|
]
|
|
],
|
|
totalDataCodewords: 1006,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [[
|
|
17,
|
|
46
|
|
]],
|
|
totalDataCodewords: 782,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
7,
|
|
24
|
|
],
|
|
[
|
|
16,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 568,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [[
|
|
34,
|
|
13
|
|
]],
|
|
totalDataCodewords: 442,
|
|
errorCodewordsPerBlock: 24
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
4,
|
|
121
|
|
],
|
|
[
|
|
5,
|
|
122
|
|
]
|
|
],
|
|
totalDataCodewords: 1094,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
4,
|
|
47
|
|
],
|
|
[
|
|
14,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 860,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
11,
|
|
24
|
|
],
|
|
[
|
|
14,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 614,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
16,
|
|
15
|
|
],
|
|
[
|
|
14,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 464,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
6,
|
|
117
|
|
],
|
|
[
|
|
4,
|
|
118
|
|
]
|
|
],
|
|
totalDataCodewords: 1174,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
6,
|
|
45
|
|
],
|
|
[
|
|
14,
|
|
46
|
|
]
|
|
],
|
|
totalDataCodewords: 914,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
11,
|
|
24
|
|
],
|
|
[
|
|
16,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 664,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
30,
|
|
16
|
|
],
|
|
[
|
|
2,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 514,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
8,
|
|
106
|
|
],
|
|
[
|
|
4,
|
|
107
|
|
]
|
|
],
|
|
totalDataCodewords: 1276,
|
|
errorCodewordsPerBlock: 26
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
8,
|
|
47
|
|
],
|
|
[
|
|
13,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 1000,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
7,
|
|
24
|
|
],
|
|
[
|
|
22,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 718,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
22,
|
|
15
|
|
],
|
|
[
|
|
13,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 538,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
10,
|
|
114
|
|
],
|
|
[
|
|
2,
|
|
115
|
|
]
|
|
],
|
|
totalDataCodewords: 1370,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
19,
|
|
46
|
|
],
|
|
[
|
|
4,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1062,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
28,
|
|
22
|
|
],
|
|
[
|
|
6,
|
|
23
|
|
]
|
|
],
|
|
totalDataCodewords: 754,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
33,
|
|
16
|
|
],
|
|
[
|
|
4,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 596,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
8,
|
|
122
|
|
],
|
|
[
|
|
4,
|
|
123
|
|
]
|
|
],
|
|
totalDataCodewords: 1468,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
22,
|
|
45
|
|
],
|
|
[
|
|
3,
|
|
46
|
|
]
|
|
],
|
|
totalDataCodewords: 1128,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
8,
|
|
23
|
|
],
|
|
[
|
|
26,
|
|
24
|
|
]
|
|
],
|
|
totalDataCodewords: 808,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
12,
|
|
15
|
|
],
|
|
[
|
|
28,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 628,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
3,
|
|
117
|
|
],
|
|
[
|
|
10,
|
|
118
|
|
]
|
|
],
|
|
totalDataCodewords: 1531,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
3,
|
|
45
|
|
],
|
|
[
|
|
23,
|
|
46
|
|
]
|
|
],
|
|
totalDataCodewords: 1193,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
4,
|
|
24
|
|
],
|
|
[
|
|
31,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 871,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
11,
|
|
15
|
|
],
|
|
[
|
|
31,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 661,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
7,
|
|
116
|
|
],
|
|
[
|
|
7,
|
|
117
|
|
]
|
|
],
|
|
totalDataCodewords: 1631,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
21,
|
|
45
|
|
],
|
|
[
|
|
7,
|
|
46
|
|
]
|
|
],
|
|
totalDataCodewords: 1267,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
1,
|
|
23
|
|
],
|
|
[
|
|
37,
|
|
24
|
|
]
|
|
],
|
|
totalDataCodewords: 911,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
19,
|
|
15
|
|
],
|
|
[
|
|
26,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 701,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
5,
|
|
115
|
|
],
|
|
[
|
|
10,
|
|
116
|
|
]
|
|
],
|
|
totalDataCodewords: 1735,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
19,
|
|
47
|
|
],
|
|
[
|
|
10,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 1373,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
15,
|
|
24
|
|
],
|
|
[
|
|
25,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 985,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
23,
|
|
15
|
|
],
|
|
[
|
|
25,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 745,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
13,
|
|
115
|
|
],
|
|
[
|
|
3,
|
|
116
|
|
]
|
|
],
|
|
totalDataCodewords: 1843,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
2,
|
|
46
|
|
],
|
|
[
|
|
29,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1455,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
42,
|
|
24
|
|
],
|
|
[
|
|
1,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1033,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
23,
|
|
15
|
|
],
|
|
[
|
|
28,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 793,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [[
|
|
17,
|
|
115
|
|
]],
|
|
totalDataCodewords: 1955,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
10,
|
|
46
|
|
],
|
|
[
|
|
23,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1541,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
10,
|
|
24
|
|
],
|
|
[
|
|
35,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1115,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
19,
|
|
15
|
|
],
|
|
[
|
|
35,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 845,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
17,
|
|
115
|
|
],
|
|
[
|
|
1,
|
|
116
|
|
]
|
|
],
|
|
totalDataCodewords: 2071,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
14,
|
|
46
|
|
],
|
|
[
|
|
21,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1631,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
29,
|
|
24
|
|
],
|
|
[
|
|
19,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1171,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
11,
|
|
15
|
|
],
|
|
[
|
|
46,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 901,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
13,
|
|
115
|
|
],
|
|
[
|
|
6,
|
|
116
|
|
]
|
|
],
|
|
totalDataCodewords: 2191,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
14,
|
|
46
|
|
],
|
|
[
|
|
23,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1725,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
44,
|
|
24
|
|
],
|
|
[
|
|
7,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1231,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
59,
|
|
16
|
|
],
|
|
[
|
|
1,
|
|
17
|
|
]
|
|
],
|
|
totalDataCodewords: 961,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
12,
|
|
121
|
|
],
|
|
[
|
|
7,
|
|
122
|
|
]
|
|
],
|
|
totalDataCodewords: 2306,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
12,
|
|
47
|
|
],
|
|
[
|
|
26,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 1812,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
39,
|
|
24
|
|
],
|
|
[
|
|
14,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1286,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
22,
|
|
15
|
|
],
|
|
[
|
|
41,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 986,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
6,
|
|
121
|
|
],
|
|
[
|
|
14,
|
|
122
|
|
]
|
|
],
|
|
totalDataCodewords: 2434,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
6,
|
|
47
|
|
],
|
|
[
|
|
34,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 1914,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
46,
|
|
24
|
|
],
|
|
[
|
|
10,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1354,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
2,
|
|
15
|
|
],
|
|
[
|
|
64,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 1054,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
17,
|
|
122
|
|
],
|
|
[
|
|
4,
|
|
123
|
|
]
|
|
],
|
|
totalDataCodewords: 2566,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
29,
|
|
46
|
|
],
|
|
[
|
|
14,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 1992,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
49,
|
|
24
|
|
],
|
|
[
|
|
10,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1426,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
24,
|
|
15
|
|
],
|
|
[
|
|
46,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 1096,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
4,
|
|
122
|
|
],
|
|
[
|
|
18,
|
|
123
|
|
]
|
|
],
|
|
totalDataCodewords: 2702,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
13,
|
|
46
|
|
],
|
|
[
|
|
32,
|
|
47
|
|
]
|
|
],
|
|
totalDataCodewords: 2102,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
48,
|
|
24
|
|
],
|
|
[
|
|
14,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1502,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
42,
|
|
15
|
|
],
|
|
[
|
|
32,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 1142,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
20,
|
|
117
|
|
],
|
|
[
|
|
4,
|
|
118
|
|
]
|
|
],
|
|
totalDataCodewords: 2812,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
40,
|
|
47
|
|
],
|
|
[
|
|
7,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 2216,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
43,
|
|
24
|
|
],
|
|
[
|
|
22,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1582,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
10,
|
|
15
|
|
],
|
|
[
|
|
67,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 1222,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
},
|
|
{
|
|
L: {
|
|
groups: [
|
|
[
|
|
19,
|
|
118
|
|
],
|
|
[
|
|
6,
|
|
119
|
|
]
|
|
],
|
|
totalDataCodewords: 2956,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
M: {
|
|
groups: [
|
|
[
|
|
18,
|
|
47
|
|
],
|
|
[
|
|
31,
|
|
48
|
|
]
|
|
],
|
|
totalDataCodewords: 2334,
|
|
errorCodewordsPerBlock: 28
|
|
},
|
|
Q: {
|
|
groups: [
|
|
[
|
|
34,
|
|
24
|
|
],
|
|
[
|
|
34,
|
|
25
|
|
]
|
|
],
|
|
totalDataCodewords: 1666,
|
|
errorCodewordsPerBlock: 30
|
|
},
|
|
H: {
|
|
groups: [
|
|
[
|
|
20,
|
|
15
|
|
],
|
|
[
|
|
61,
|
|
16
|
|
]
|
|
],
|
|
totalDataCodewords: 1276,
|
|
errorCodewordsPerBlock: 30
|
|
}
|
|
}
|
|
], finderPattern = [
|
|
1,
|
|
0,
|
|
1,
|
|
1,
|
|
1
|
|
], alignmentPattern = [
|
|
1,
|
|
0,
|
|
1
|
|
], errorCorrectionPatterns = {
|
|
L: '01',
|
|
M: '00',
|
|
Q: '11',
|
|
H: '10'
|
|
}, formatMaskPattern = '101010000010010', formatGeneratorPolynomial = '10100110111', versionGeneratorPolynomial = '1111100100101', paddingCodewords = [
|
|
'11101100',
|
|
'00010001'
|
|
], finderPatternValue = 93, maskPatternConditions = [
|
|
function (row, column) {
|
|
return (row + column) % 2 === 0;
|
|
},
|
|
function (row) {
|
|
return row % 2 === 0;
|
|
},
|
|
function (row, column) {
|
|
return column % 3 === 0;
|
|
},
|
|
function (row, column) {
|
|
return (row + column) % 3 === 0;
|
|
},
|
|
function (row, column) {
|
|
return (Math.floor(row / 2) + Math.floor(column / 3)) % 2 === 0;
|
|
},
|
|
function (row, column) {
|
|
return row * column % 2 + row * column % 3 === 0;
|
|
},
|
|
function (row, column) {
|
|
return (row * column % 2 + row * column % 3) % 2 === 0;
|
|
},
|
|
function (row, column) {
|
|
return ((row + column) % 2 + row * column % 3) % 2 === 0;
|
|
}
|
|
], numberRegex = /^\d+/, alphaPattern = 'A-Z0-9 $%*+./:-', alphaExclusiveSet = 'A-Z $%*+./:-', alphaRegex = new RegExp('^[' + alphaExclusiveSet + ']+'), alphaNumericRegex = new RegExp('^[' + alphaPattern + ']+'), byteRegex = new RegExp('^[^' + alphaPattern + ']+'), initMinNumericBeforeAlpha = 8, initMinNumericBeforeByte = 5, initMinAlphaBeforeByte = 8, minNumericBeforeAlpha = 17, minNumericBeforeByte = 9, minAlphaBeforeByte = 16, round = Math.round;
|
|
function toDecimal(value) {
|
|
return parseInt(value, 2);
|
|
}
|
|
function toBitsString(value, length) {
|
|
var result = Number(value).toString(2);
|
|
if (result.length < length) {
|
|
result = new Array(length - result.length + 1).join(0) + result;
|
|
}
|
|
return result;
|
|
}
|
|
function splitInto(str, n) {
|
|
var result = [], idx = 0;
|
|
while (idx < str.length) {
|
|
result.push(str.substring(idx, idx + n));
|
|
idx += n;
|
|
}
|
|
return result;
|
|
}
|
|
var QRDataMode = kendo.Class.extend({
|
|
getVersionIndex: function (version) {
|
|
if (version < 10) {
|
|
return 0;
|
|
} else if (version > 26) {
|
|
return 2;
|
|
}
|
|
return 1;
|
|
},
|
|
getBitsCharacterCount: function (version) {
|
|
var mode = this;
|
|
return mode.bitsInCharacterCount[mode.getVersionIndex(version || 40)];
|
|
},
|
|
getModeCountString: function (length, version) {
|
|
var mode = this;
|
|
return mode.modeIndicator + toBitsString(length, mode.getBitsCharacterCount(version));
|
|
},
|
|
encode: function () {
|
|
},
|
|
getStringBitsLength: function () {
|
|
},
|
|
getValue: function () {
|
|
},
|
|
modeIndicator: '',
|
|
bitsInCharacterCount: []
|
|
});
|
|
var modes = {};
|
|
modes[NUMERIC] = QRDataMode.extend({
|
|
bitsInCharacterCount: [
|
|
10,
|
|
12,
|
|
14
|
|
],
|
|
modeIndicator: '0001',
|
|
getValue: function (character) {
|
|
return parseInt(character, 10);
|
|
},
|
|
encode: function (str, version) {
|
|
var mode = this, parts = splitInto(str, 3), result = mode.getModeCountString(str.length, version);
|
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
result += toBitsString(parts[i], 10);
|
|
}
|
|
return result + toBitsString(parts[i], 1 + 3 * parts[i].length);
|
|
},
|
|
getStringBitsLength: function (inputLength, version) {
|
|
var mod3 = inputLength % 3;
|
|
return 4 + this.getBitsCharacterCount(version) + 10 * Math.floor(inputLength / 3) + 3 * mod3 + (mod3 === 0 ? 0 : 1);
|
|
}
|
|
});
|
|
modes[ALPHA_NUMERIC] = QRDataMode.extend({
|
|
characters: {
|
|
'0': 0,
|
|
'1': 1,
|
|
'2': 2,
|
|
'3': 3,
|
|
'4': 4,
|
|
'5': 5,
|
|
'6': 6,
|
|
'7': 7,
|
|
'8': 8,
|
|
'9': 9,
|
|
'A': 10,
|
|
'B': 11,
|
|
'C': 12,
|
|
'D': 13,
|
|
'E': 14,
|
|
'F': 15,
|
|
'G': 16,
|
|
'H': 17,
|
|
'I': 18,
|
|
'J': 19,
|
|
'K': 20,
|
|
'L': 21,
|
|
'M': 22,
|
|
'N': 23,
|
|
'O': 24,
|
|
'P': 25,
|
|
'Q': 26,
|
|
'R': 27,
|
|
'S': 28,
|
|
'T': 29,
|
|
'U': 30,
|
|
'V': 31,
|
|
'W': 32,
|
|
'X': 33,
|
|
'Y': 34,
|
|
'Z': 35,
|
|
' ': 36,
|
|
'$': 37,
|
|
'%': 38,
|
|
'*': 39,
|
|
'+': 40,
|
|
'-': 41,
|
|
'.': 42,
|
|
'/': 43,
|
|
':': 44
|
|
},
|
|
bitsInCharacterCount: [
|
|
9,
|
|
11,
|
|
13
|
|
],
|
|
modeIndicator: '0010',
|
|
getValue: function (character) {
|
|
return this.characters[character];
|
|
},
|
|
encode: function (str, version) {
|
|
var mode = this, parts = splitInto(str, 2), result = mode.getModeCountString(str.length, version), value;
|
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
value = 45 * mode.getValue(parts[i].charAt(0)) + mode.getValue(parts[i].charAt(1));
|
|
result += toBitsString(value, 11);
|
|
}
|
|
value = parts[i].length == 2 ? 45 * mode.getValue(parts[i].charAt(0)) + mode.getValue(parts[i].charAt(1)) : mode.getValue(parts[i].charAt(0));
|
|
return result + toBitsString(value, 1 + 5 * parts[i].length);
|
|
},
|
|
getStringBitsLength: function (inputLength, version) {
|
|
return 4 + this.getBitsCharacterCount(version) + 11 * Math.floor(inputLength / 2) + 6 * (inputLength % 2);
|
|
}
|
|
});
|
|
modes[BYTE] = QRDataMode.extend({
|
|
bitsInCharacterCount: [
|
|
8,
|
|
16,
|
|
16
|
|
],
|
|
modeIndicator: '0100',
|
|
getValue: function (character) {
|
|
var code = character.charCodeAt(0);
|
|
if (code <= 127 || 160 <= code && code <= 255) {
|
|
return code;
|
|
} else {
|
|
throw new Error('Unsupported character: ' + character);
|
|
}
|
|
},
|
|
encode: function (str, version) {
|
|
var mode = this, result = mode.getModeCountString(str.length, version);
|
|
for (var i = 0; i < str.length; i++) {
|
|
result += toBitsString(mode.getValue(str.charAt(i)), 8);
|
|
}
|
|
return result;
|
|
},
|
|
getStringBitsLength: function (inputLength, version) {
|
|
return 4 + this.getBitsCharacterCount(version) + 8 * inputLength;
|
|
}
|
|
});
|
|
var modeInstances = {};
|
|
for (var mode in modes) {
|
|
modeInstances[mode] = new modes[mode]();
|
|
}
|
|
var FreeCellVisitor = function (matrix) {
|
|
var that = this, row = matrix.length - 1, column = matrix.length - 1, startColumn = column, dir = -1, c = 0;
|
|
that.move = function () {
|
|
row += dir * c;
|
|
c ^= 1;
|
|
column = startColumn - c;
|
|
};
|
|
that.getNextCell = function () {
|
|
while (matrix[row][column] !== undefined) {
|
|
that.move();
|
|
if (row < 0 || row >= matrix.length) {
|
|
dir = -dir;
|
|
startColumn -= startColumn != 8 ? 2 : 3;
|
|
column = startColumn;
|
|
row = dir < 0 ? matrix.length - 1 : 0;
|
|
}
|
|
}
|
|
return {
|
|
row: row,
|
|
column: column
|
|
};
|
|
};
|
|
that.getNextRemainderCell = function () {
|
|
that.move();
|
|
if (matrix[row][column] === undefined) {
|
|
return {
|
|
row: row,
|
|
column: column
|
|
};
|
|
}
|
|
};
|
|
};
|
|
function fillFunctionCell(matrices, bit, x, y) {
|
|
for (var i = 0; i < matrices.length; i++) {
|
|
matrices[i][x][y] = bit;
|
|
}
|
|
}
|
|
function fillDataCell(matrices, bit, x, y) {
|
|
for (var i = 0; i < maskPatternConditions.length; i++) {
|
|
matrices[i][x][y] = maskPatternConditions[i](x, y) ? bit ^ 1 : parseInt(bit, 10);
|
|
}
|
|
}
|
|
var fillData = function (matrices, blocks) {
|
|
var cellVisitor = new FreeCellVisitor(matrices[0]), block, codewordIdx, cell;
|
|
for (var blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
block = blocks[blockIdx];
|
|
codewordIdx = 0;
|
|
while (block.length > 0) {
|
|
for (var i = 0; i < block.length; i++) {
|
|
for (var j = 0; j < 8; j++) {
|
|
cell = cellVisitor.getNextCell();
|
|
fillDataCell(matrices, block[i][codewordIdx].charAt(j), cell.row, cell.column);
|
|
}
|
|
}
|
|
codewordIdx++;
|
|
while (block[0] && codewordIdx == block[0].length) {
|
|
block.splice(0, 1);
|
|
}
|
|
}
|
|
}
|
|
while (cell = cellVisitor.getNextRemainderCell()) {
|
|
fillDataCell(matrices, 0, cell.row, cell.column);
|
|
}
|
|
};
|
|
var padDataString = function (dataString, totalDataCodewords) {
|
|
var dataBitsCount = totalDataCodewords * 8, terminatorIndex = 0, paddingCodewordIndex = 0;
|
|
while (dataString.length < dataBitsCount && terminatorIndex < terminator.length) {
|
|
dataString += terminator.charAt(terminatorIndex++);
|
|
}
|
|
if (dataString.length % 8 !== 0) {
|
|
dataString += new Array(9 - dataString.length % 8).join('0');
|
|
}
|
|
while (dataString.length < dataBitsCount) {
|
|
dataString += paddingCodewords[paddingCodewordIndex];
|
|
paddingCodewordIndex ^= 1;
|
|
}
|
|
return dataString;
|
|
};
|
|
function generatePowersOfTwo() {
|
|
var result;
|
|
for (var power = 1; power < 255; power++) {
|
|
result = powersOfTwoResult[power - 1] * 2;
|
|
if (result > 255) {
|
|
result = result ^ 285;
|
|
}
|
|
powersOfTwoResult[power] = result;
|
|
powersOfTwo[result] = power;
|
|
}
|
|
result = powersOfTwoResult[power - 1] * 2 ^ 285;
|
|
powersOfTwoResult[power] = result;
|
|
powersOfTwoResult[-1] = 0;
|
|
}
|
|
var xorPolynomials = function (x, y) {
|
|
var result = [], idx = x.length - 2;
|
|
for (var i = idx; i >= 0; i--) {
|
|
result[i] = x[i] ^ y[i];
|
|
}
|
|
return result;
|
|
};
|
|
var multiplyPolynomials = function (x, y) {
|
|
var result = [];
|
|
for (var i = 0; i < x.length; i++) {
|
|
for (var j = 0; j < y.length; j++) {
|
|
if (result[i + j] === undefined) {
|
|
result[i + j] = (x[i] + (y[j] >= 0 ? y[j] : 0)) % 255;
|
|
} else {
|
|
result[i + j] = powersOfTwo[powersOfTwoResult[result[i + j]] ^ powersOfTwoResult[(x[i] + y[j]) % 255]];
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
function generateGeneratorPolynomials() {
|
|
var maxErrorCorrectionCodeWordsCount = 68;
|
|
for (var idx = 2; idx <= maxErrorCorrectionCodeWordsCount; idx++) {
|
|
var firstPolynomial = generatorPolynomials[idx - 1], secondPolynomial = [
|
|
idx,
|
|
0
|
|
];
|
|
generatorPolynomials[idx] = multiplyPolynomials(firstPolynomial, secondPolynomial);
|
|
}
|
|
}
|
|
generatePowersOfTwo();
|
|
generateGeneratorPolynomials();
|
|
function multiplyByConstant(polynomial, power) {
|
|
var result = [], idx = polynomial.length - 1;
|
|
do {
|
|
result[idx] = powersOfTwoResult[(polynomial[idx] + power) % 255];
|
|
idx--;
|
|
} while (polynomial[idx] !== undefined);
|
|
return result;
|
|
}
|
|
var generateErrorCodewords = function (data, errorCodewordsCount) {
|
|
var generator = generatorPolynomials[errorCodewordsCount - 1], result = new Array(errorCodewordsCount).concat(data), generatorPolynomial = new Array(result.length - generator.length).concat(generator), steps = data.length, errorCodewords = [], divisor, idx;
|
|
for (idx = 0; idx < steps; idx++) {
|
|
divisor = multiplyByConstant(generatorPolynomial, powersOfTwo[result[result.length - 1]]);
|
|
generatorPolynomial.splice(0, 1);
|
|
result = xorPolynomials(divisor, result);
|
|
}
|
|
for (idx = result.length - 1; idx >= 0; idx--) {
|
|
errorCodewords[errorCodewordsCount - 1 - idx] = toBitsString(result[idx], 8);
|
|
}
|
|
return errorCodewords;
|
|
};
|
|
var getBlocks = function (dataStream, versionCodewordsInformation) {
|
|
var codewordStart = 0, dataBlocks = [], errorBlocks = [], dataBlock, versionGroups = versionCodewordsInformation.groups, blockCodewordsCount, groupBlocksCount, messagePolynomial, codeword;
|
|
for (var groupIdx = 0; groupIdx < versionGroups.length; groupIdx++) {
|
|
groupBlocksCount = versionGroups[groupIdx][0];
|
|
for (var blockIdx = 0; blockIdx < groupBlocksCount; blockIdx++) {
|
|
blockCodewordsCount = versionGroups[groupIdx][1];
|
|
dataBlock = [];
|
|
messagePolynomial = [];
|
|
for (var codewordIdx = 1; codewordIdx <= blockCodewordsCount; codewordIdx++) {
|
|
codeword = dataStream.substring(codewordStart, codewordStart + 8);
|
|
dataBlock.push(codeword);
|
|
messagePolynomial[blockCodewordsCount - codewordIdx] = toDecimal(codeword);
|
|
codewordStart += 8;
|
|
}
|
|
dataBlocks.push(dataBlock);
|
|
errorBlocks.push(generateErrorCodewords(messagePolynomial, versionCodewordsInformation.errorCodewordsPerBlock));
|
|
}
|
|
}
|
|
return [
|
|
dataBlocks,
|
|
errorBlocks
|
|
];
|
|
};
|
|
var chooseMode = function (str, minNumericBeforeAlpha, minNumericBeforeByte, minAlphaBeforeByte, previousMode) {
|
|
var numeric = numberRegex.exec(str), numericMatch = numeric ? numeric[0] : '', alpha = alphaRegex.exec(str), alphaMatch = alpha ? alpha[0] : '', alphaNumeric = alphaNumericRegex.exec(str), alphaNumericMatch = alphaNumeric ? alphaNumeric[0] : '', mode, modeString;
|
|
if (numericMatch && (numericMatch.length >= minNumericBeforeAlpha || str.length == numericMatch.length || numericMatch.length >= minNumericBeforeByte && !alphaNumericRegex.test(str.charAt(numericMatch.length)))) {
|
|
mode = NUMERIC;
|
|
modeString = numericMatch;
|
|
} else if (alphaNumericMatch && (str.length == alphaNumericMatch.length || alphaNumericMatch.length >= minAlphaBeforeByte || previousMode == ALPHA_NUMERIC)) {
|
|
mode = ALPHA_NUMERIC;
|
|
modeString = numericMatch || alphaMatch;
|
|
} else {
|
|
mode = BYTE;
|
|
if (alphaNumericMatch) {
|
|
modeString = alphaNumericMatch + byteRegex.exec(str.substring(alphaNumericMatch.length))[0];
|
|
} else {
|
|
modeString = byteRegex.exec(str)[0];
|
|
}
|
|
}
|
|
return {
|
|
mode: mode,
|
|
modeString: modeString
|
|
};
|
|
};
|
|
var getModes = function (str) {
|
|
var modes = [], previousMode, idx = 0;
|
|
modes.push(chooseMode(str, initMinNumericBeforeAlpha, initMinNumericBeforeByte, initMinAlphaBeforeByte, previousMode));
|
|
previousMode = modes[0].mode;
|
|
str = str.substr(modes[0].modeString.length);
|
|
while (str.length > 0) {
|
|
var nextMode = chooseMode(str, minNumericBeforeAlpha, minNumericBeforeByte, minAlphaBeforeByte, previousMode);
|
|
if (nextMode.mode != previousMode) {
|
|
previousMode = nextMode.mode;
|
|
modes.push(nextMode);
|
|
idx++;
|
|
} else {
|
|
modes[idx].modeString += nextMode.modeString;
|
|
}
|
|
str = str.substr(nextMode.modeString.length);
|
|
}
|
|
return modes;
|
|
};
|
|
var getDataCodewordsCount = function (modes) {
|
|
var length = 0, mode;
|
|
for (var i = 0; i < modes.length; i++) {
|
|
mode = modeInstances[modes[i].mode];
|
|
length += mode.getStringBitsLength(modes[i].modeString.length);
|
|
}
|
|
return Math.ceil(length / 8);
|
|
};
|
|
var getVersion = function (dataCodewordsCount, errorCorrectionLevel) {
|
|
var x = 0, y = versionsCodewordsInformation.length - 1, version = Math.floor(versionsCodewordsInformation.length / 2);
|
|
do {
|
|
if (dataCodewordsCount < versionsCodewordsInformation[version][errorCorrectionLevel].totalDataCodewords) {
|
|
y = version;
|
|
} else {
|
|
x = version;
|
|
}
|
|
version = x + Math.floor((y - x) / 2);
|
|
} while (y - x > 1);
|
|
if (dataCodewordsCount <= versionsCodewordsInformation[x][errorCorrectionLevel].totalDataCodewords) {
|
|
return version + 1;
|
|
}
|
|
return y + 1;
|
|
};
|
|
var getDataString = function (modes, version) {
|
|
var dataString = '', mode;
|
|
for (var i = 0; i < modes.length; i++) {
|
|
mode = modeInstances[modes[i].mode];
|
|
dataString += mode.encode(modes[i].modeString, version);
|
|
}
|
|
return dataString;
|
|
};
|
|
var encodeFormatInformation = function (format) {
|
|
var formatNumber = toDecimal(format), encodedString, result = '';
|
|
if (formatNumber === 0) {
|
|
return '101010000010010';
|
|
} else {
|
|
encodedString = encodeBCH(toDecimal(format), formatGeneratorPolynomial, 15);
|
|
}
|
|
for (var i = 0; i < encodedString.length; i++) {
|
|
result += encodedString.charAt(i) ^ formatMaskPattern.charAt(i);
|
|
}
|
|
return result;
|
|
};
|
|
var encodeBCH = function (value, generatorPolynomial, codeLength) {
|
|
var generatorNumber = toDecimal(generatorPolynomial), polynomialLength = generatorPolynomial.length - 1, valueNumber = value << polynomialLength, length = codeLength - polynomialLength, valueString = toBitsString(value, length), result = dividePolynomials(valueNumber, generatorNumber);
|
|
result = valueString + toBitsString(result, polynomialLength);
|
|
return result;
|
|
};
|
|
var dividePolynomials = function (numberX, numberY) {
|
|
var yLength = numberY.toString(2).length, xLength = numberX.toString(2).length;
|
|
do {
|
|
numberX ^= numberY << xLength - yLength;
|
|
xLength = numberX.toString(2).length;
|
|
} while (xLength >= yLength);
|
|
return numberX;
|
|
};
|
|
function getNumberAt(str, idx) {
|
|
return parseInt(str.charAt(idx), 10);
|
|
}
|
|
var initMatrices = function (version) {
|
|
var matrices = [], modules = 17 + 4 * version;
|
|
for (var i = 0; i < maskPatternConditions.length; i++) {
|
|
matrices[i] = new Array(modules);
|
|
for (var j = 0; j < modules; j++) {
|
|
matrices[i][j] = new Array(modules);
|
|
}
|
|
}
|
|
return matrices;
|
|
};
|
|
var addFormatInformation = function (matrices, formatString) {
|
|
var matrix = matrices[0], x, y, idx = 0, length = formatString.length;
|
|
for (x = 0, y = 8; x <= 8; x++) {
|
|
if (x !== 6) {
|
|
fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y);
|
|
}
|
|
}
|
|
for (x = 8, y = 7; y >= 0; y--) {
|
|
if (y !== 6) {
|
|
fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y);
|
|
}
|
|
}
|
|
idx = 0;
|
|
for (y = matrix.length - 1, x = 8; y >= matrix.length - 8; y--) {
|
|
fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y);
|
|
}
|
|
fillFunctionCell(matrices, 1, matrix.length - 8, 8);
|
|
for (x = matrix.length - 7, y = 8; x < matrix.length; x++) {
|
|
fillFunctionCell(matrices, getNumberAt(formatString, length - 1 - idx++), x, y);
|
|
}
|
|
};
|
|
var encodeVersionInformation = function (version) {
|
|
return encodeBCH(version, versionGeneratorPolynomial, 18);
|
|
};
|
|
var addVersionInformation = function (matrices, dataString) {
|
|
var matrix = matrices[0], modules = matrix.length, x1 = 0, y1 = modules - 11, x2 = modules - 11, y2 = 0, quotient, mod, value;
|
|
for (var idx = 0; idx < dataString.length; idx++) {
|
|
quotient = Math.floor(idx / 3);
|
|
mod = idx % 3;
|
|
value = getNumberAt(dataString, dataString.length - idx - 1);
|
|
fillFunctionCell(matrices, value, x1 + quotient, y1 + mod);
|
|
fillFunctionCell(matrices, value, x2 + mod, y2 + quotient);
|
|
}
|
|
};
|
|
var addCentricPattern = function (matrices, pattern, x, y) {
|
|
var size = pattern.length + 2, length = pattern.length + 1, value;
|
|
for (var i = 0; i < pattern.length; i++) {
|
|
for (var j = i; j < size - i; j++) {
|
|
value = pattern[i];
|
|
fillFunctionCell(matrices, value, x + j, y + i);
|
|
fillFunctionCell(matrices, value, x + i, y + j);
|
|
fillFunctionCell(matrices, value, x + length - j, y + length - i);
|
|
fillFunctionCell(matrices, value, x + length - i, y + length - j);
|
|
}
|
|
}
|
|
};
|
|
var addFinderSeparator = function (matrices, direction, x, y) {
|
|
var nextX = x, nextY = y, matrix = matrices[0];
|
|
do {
|
|
fillFunctionCell(matrices, 0, nextX, y);
|
|
fillFunctionCell(matrices, 0, x, nextY);
|
|
nextX += direction[0];
|
|
nextY += direction[1];
|
|
} while (nextX >= 0 && nextX < matrix.length);
|
|
};
|
|
var addFinderPatterns = function (matrices) {
|
|
var modules = matrices[0].length;
|
|
addCentricPattern(matrices, finderPattern, 0, 0);
|
|
addFinderSeparator(matrices, [
|
|
-1,
|
|
-1
|
|
], 7, 7);
|
|
addCentricPattern(matrices, finderPattern, modules - 7, 0);
|
|
addFinderSeparator(matrices, [
|
|
1,
|
|
-1
|
|
], modules - 8, 7);
|
|
addCentricPattern(matrices, finderPattern, 0, modules - 7);
|
|
addFinderSeparator(matrices, [
|
|
-1,
|
|
1
|
|
], 7, modules - 8);
|
|
};
|
|
var addAlignmentPatterns = function (matrices, version) {
|
|
if (version < 2) {
|
|
return;
|
|
}
|
|
var matrix = matrices[0], modules = matrix.length, pointsCount = Math.floor(version / 7), points = [6], startDistance, distance, idx = 0;
|
|
if (startDistance = irregularAlignmentPatternsStartDistance[version]) {
|
|
distance = (modules - 13 - startDistance) / pointsCount;
|
|
} else {
|
|
startDistance = distance = (modules - 13) / (pointsCount + 1);
|
|
}
|
|
points.push(points[idx++] + startDistance);
|
|
while (points[idx] + distance < modules) {
|
|
points.push(points[idx++] + distance);
|
|
}
|
|
for (var i = 0; i < points.length; i++) {
|
|
for (var j = 0; j < points.length; j++) {
|
|
if (matrix[points[i]][points[j]] === undefined) {
|
|
addCentricPattern(matrices, alignmentPattern, points[i] - 2, points[j] - 2);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var addTimingFunctions = function (matrices) {
|
|
var row = 6, column = 6, value = 1, modules = matrices[0].length;
|
|
for (var i = 8; i < modules - 8; i++) {
|
|
fillFunctionCell(matrices, value, row, i);
|
|
fillFunctionCell(matrices, value, i, column);
|
|
value ^= 1;
|
|
}
|
|
};
|
|
var scoreMaskMatrixes = function (matrices) {
|
|
var scores = [], previousBits = [], darkModules = [], patterns = [], adjacentSameBits = [], matrix, i, row = 0, column = 1, modules = matrices[0].length;
|
|
for (i = 0; i < matrices.length; i++) {
|
|
scores[i] = 0;
|
|
darkModules[i] = 0;
|
|
adjacentSameBits[i] = [
|
|
0,
|
|
0
|
|
];
|
|
patterns[i] = [
|
|
0,
|
|
0
|
|
];
|
|
previousBits[i] = [];
|
|
}
|
|
for (i = 0; i < modules; i++) {
|
|
for (var j = 0; j < modules; j++) {
|
|
for (var k = 0; k < matrices.length; k++) {
|
|
matrix = matrices[k];
|
|
darkModules[k] += parseInt(matrix[i][j], 10);
|
|
if (previousBits[k][row] === matrix[i][j] && i + 1 < modules && j - 1 >= 0 && matrix[i + 1][j] == previousBits[k][row] && matrix[i + 1][j - 1] == previousBits[k][row]) {
|
|
scores[k] += 3;
|
|
}
|
|
scoreFinderPatternOccurance(k, patterns, scores, row, matrix[i][j]);
|
|
scoreFinderPatternOccurance(k, patterns, scores, column, matrix[j][i]);
|
|
scoreAdjacentSameBits(k, scores, previousBits, matrix[i][j], adjacentSameBits, row);
|
|
scoreAdjacentSameBits(k, scores, previousBits, matrix[j][i], adjacentSameBits, column);
|
|
}
|
|
}
|
|
}
|
|
var total = modules * modules, minIdx, min = Number.MAX_VALUE;
|
|
for (i = 0; i < scores.length; i++) {
|
|
scores[i] += calculateDarkModulesRatioScore(darkModules[i], total);
|
|
if (scores[i] < min) {
|
|
min = scores[i];
|
|
minIdx = i;
|
|
}
|
|
}
|
|
return minIdx;
|
|
};
|
|
function scoreFinderPatternOccurance(idx, patterns, scores, rowColumn, bit) {
|
|
patterns[idx][rowColumn] = (patterns[idx][rowColumn] << 1 ^ bit) % 128;
|
|
if (patterns[idx][rowColumn] == finderPatternValue) {
|
|
scores[idx] += 40;
|
|
}
|
|
}
|
|
function scoreAdjacentSameBits(idx, scores, previousBits, bit, adjacentBits, rowColumn) {
|
|
if (previousBits[idx][rowColumn] == bit) {
|
|
adjacentBits[idx][rowColumn]++;
|
|
} else {
|
|
previousBits[idx][rowColumn] = bit;
|
|
if (adjacentBits[idx][rowColumn] >= 5) {
|
|
scores[idx] += 3 + adjacentBits[idx][rowColumn] - 5;
|
|
}
|
|
adjacentBits[idx][rowColumn] = 1;
|
|
}
|
|
}
|
|
function calculateDarkModulesRatioScore(darkModules, total) {
|
|
var percent = Math.floor(darkModules / total * 100), mod5 = percent % 5, previous = Math.abs(percent - mod5 - 50), next = Math.abs(percent + 5 - mod5 - 50), score = 10 * Math.min(previous / 5, next / 5);
|
|
return score;
|
|
}
|
|
var EncodingResult = function (dataString, version) {
|
|
this.dataString = dataString;
|
|
this.version = version;
|
|
};
|
|
var IsoEncoder = function () {
|
|
this.getEncodingResult = function (inputString, errorCorrectionLevel) {
|
|
var modes = getModes(inputString), dataCodewordsCount = getDataCodewordsCount(modes), version = getVersion(dataCodewordsCount, errorCorrectionLevel), dataString = getDataString(modes, version);
|
|
return new EncodingResult(dataString, version);
|
|
};
|
|
};
|
|
var UTF8Encoder = function () {
|
|
this.mode = modeInstances[this.encodingMode];
|
|
};
|
|
UTF8Encoder.fn = UTF8Encoder.prototype = {
|
|
encodingMode: BYTE,
|
|
utfBOM: '111011111011101110111111',
|
|
initialModeCountStringLength: 20,
|
|
getEncodingResult: function (inputString, errorCorrectionLevel) {
|
|
var that = this, data = that.encode(inputString), dataCodewordsCount = that.getDataCodewordsCount(data), version = getVersion(dataCodewordsCount, errorCorrectionLevel), dataString = that.mode.getModeCountString(data.length / 8, version) + data;
|
|
return new EncodingResult(dataString, version);
|
|
},
|
|
getDataCodewordsCount: function (data) {
|
|
var that = this, dataLength = data.length, dataCodewordsCount = Math.ceil((that.initialModeCountStringLength + dataLength) / 8);
|
|
return dataCodewordsCount;
|
|
},
|
|
encode: function (str) {
|
|
var that = this, result = that.utfBOM;
|
|
for (var i = 0; i < str.length; i++) {
|
|
result += that.encodeCharacter(str.charCodeAt(i));
|
|
}
|
|
return result;
|
|
},
|
|
encodeCharacter: function (code) {
|
|
var bytesCount = this.getBytesCount(code), bc = bytesCount - 1, result = '';
|
|
if (bytesCount == 1) {
|
|
result = toBitsString(code, 8);
|
|
} else {
|
|
var significantOnes = 8 - bytesCount;
|
|
for (var i = 0; i < bc; i++) {
|
|
result = toBitsString(code >> i * 6 & 63 | 128, 8) + result;
|
|
}
|
|
result = (code >> bc * 6 | 255 >> significantOnes << significantOnes).toString(2) + result;
|
|
}
|
|
return result;
|
|
},
|
|
getBytesCount: function (code) {
|
|
var ranges = this.ranges;
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
if (code < ranges[i]) {
|
|
return i + 1;
|
|
}
|
|
}
|
|
},
|
|
ranges: [
|
|
128,
|
|
2048,
|
|
65536,
|
|
2097152,
|
|
67108864
|
|
]
|
|
};
|
|
var QRCodeDataEncoder = function (encoding) {
|
|
if (encoding && encoding.toLowerCase().indexOf('utf_8') >= 0) {
|
|
return new UTF8Encoder();
|
|
} else {
|
|
return new IsoEncoder();
|
|
}
|
|
};
|
|
var encodeData = function (inputString, errorCorrectionLevel, encoding) {
|
|
var encoder = new QRCodeDataEncoder(encoding), encodingResult = encoder.getEncodingResult(inputString, errorCorrectionLevel), version = encodingResult.version, versionInformation = versionsCodewordsInformation[version - 1][errorCorrectionLevel], dataString = padDataString(encodingResult.dataString, versionInformation.totalDataCodewords), blocks = getBlocks(dataString, versionInformation), matrices = initMatrices(version);
|
|
addFinderPatterns(matrices);
|
|
addAlignmentPatterns(matrices, version);
|
|
addTimingFunctions(matrices);
|
|
if (version >= 7) {
|
|
addVersionInformation(matrices, toBitsString(0, 18));
|
|
}
|
|
addFormatInformation(matrices, toBitsString(0, 15));
|
|
fillData(matrices, blocks);
|
|
var minIdx = scoreMaskMatrixes(matrices), optimalMatrix = matrices[minIdx];
|
|
if (version >= 7) {
|
|
addVersionInformation([optimalMatrix], encodeVersionInformation(version));
|
|
}
|
|
var formatString = errorCorrectionPatterns[errorCorrectionLevel] + toBitsString(minIdx, 3);
|
|
addFormatInformation([optimalMatrix], encodeFormatInformation(formatString));
|
|
return optimalMatrix;
|
|
};
|
|
var QRCodeDefaults = {
|
|
DEFAULT_SIZE: 200,
|
|
QUIET_ZONE_LENGTH: 4,
|
|
DEFAULT_ERROR_CORRECTION_LEVEL: 'L',
|
|
DEFAULT_BACKGROUND: '#fff',
|
|
DEFAULT_DARK_MODULE_COLOR: '#000',
|
|
MIN_BASE_UNIT_SIZE: 1
|
|
};
|
|
var QRCode = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.element = $(element);
|
|
that.wrapper = that.element;
|
|
that.element.addClass('k-qrcode');
|
|
that.surfaceWrap = $('<div />').css('position', 'relative').appendTo(this.element);
|
|
that.surface = draw.Surface.create(that.surfaceWrap, { type: that.options.renderAs });
|
|
that.setOptions(options);
|
|
},
|
|
redraw: function () {
|
|
var size = this._getSize();
|
|
this.surfaceWrap.css({
|
|
width: size,
|
|
height: size
|
|
});
|
|
this.surface.clear();
|
|
this.createVisual();
|
|
this.surface.draw(this.visual);
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.element);
|
|
},
|
|
_resize: function () {
|
|
this.redraw();
|
|
},
|
|
createVisual: function () {
|
|
this.visual = this._render();
|
|
},
|
|
exportVisual: function () {
|
|
return this._render();
|
|
},
|
|
_render: function () {
|
|
var that = this, value = that._value, baseUnit, border = that.options.border || {}, padding = that.options.padding || 0, borderWidth = border.width || 0, quietZoneSize, matrix, size, dataSize, contentSize;
|
|
border.width = borderWidth;
|
|
var visual = new draw.Group();
|
|
if (value) {
|
|
matrix = encodeData(value, that.options.errorCorrection, that.options.encoding);
|
|
size = that._getSize();
|
|
contentSize = size - 2 * (borderWidth + padding);
|
|
baseUnit = that._calculateBaseUnit(contentSize, matrix.length);
|
|
dataSize = matrix.length * baseUnit;
|
|
quietZoneSize = borderWidth + padding + (contentSize - dataSize) / 2;
|
|
visual.append(that._renderBackground(size, border));
|
|
visual.append(that._renderMatrix(matrix, baseUnit, quietZoneSize));
|
|
}
|
|
return visual;
|
|
},
|
|
_getSize: function () {
|
|
var that = this, size;
|
|
if (that.options.size) {
|
|
size = parseInt(that.options.size, 10);
|
|
} else {
|
|
var element = that.element, min = Math.min(element.width(), element.height());
|
|
if (min > 0) {
|
|
size = min;
|
|
} else {
|
|
size = QRCodeDefaults.DEFAULT_SIZE;
|
|
}
|
|
}
|
|
return size;
|
|
},
|
|
_calculateBaseUnit: function (size, matrixSize) {
|
|
var baseUnit = Math.floor(size / matrixSize);
|
|
if (baseUnit < QRCodeDefaults.MIN_BASE_UNIT_SIZE) {
|
|
throw new Error('Insufficient size.');
|
|
}
|
|
if (baseUnit * matrixSize >= size && baseUnit - 1 >= QRCodeDefaults.MIN_BASE_UNIT_SIZE) {
|
|
baseUnit--;
|
|
}
|
|
return baseUnit;
|
|
},
|
|
_renderMatrix: function (matrix, baseUnit, quietZoneSize) {
|
|
var path = new draw.MultiPath({
|
|
fill: { color: this.options.color },
|
|
stroke: null
|
|
});
|
|
for (var row = 0; row < matrix.length; row++) {
|
|
var y = quietZoneSize + row * baseUnit;
|
|
var column = 0;
|
|
while (column < matrix.length) {
|
|
while (matrix[row][column] === 0 && column < matrix.length) {
|
|
column++;
|
|
}
|
|
if (column < matrix.length) {
|
|
var x = column;
|
|
while (matrix[row][column] == 1) {
|
|
column++;
|
|
}
|
|
var x1 = round(quietZoneSize + x * baseUnit);
|
|
var y1 = round(y);
|
|
var x2 = round(quietZoneSize + column * baseUnit);
|
|
var y2 = round(y + baseUnit);
|
|
path.moveTo(x1, y1).lineTo(x1, y2).lineTo(x2, y2).lineTo(x2, y1).close();
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
},
|
|
_renderBackground: function (size, border) {
|
|
var box = Box2D(0, 0, size, size).unpad(border.width / 2);
|
|
return draw.Path.fromRect(box.toRect(), {
|
|
fill: { color: this.options.background },
|
|
stroke: {
|
|
color: border.color,
|
|
width: border.width
|
|
}
|
|
});
|
|
},
|
|
setOptions: function (options) {
|
|
var that = this;
|
|
options = options || {};
|
|
that.options = extend(that.options, options);
|
|
if (options.value !== undefined) {
|
|
that._value = that.options.value + '';
|
|
}
|
|
that.redraw();
|
|
},
|
|
value: function (value) {
|
|
var that = this;
|
|
if (value === undefined) {
|
|
return that._value;
|
|
}
|
|
that._value = value + '';
|
|
that.redraw();
|
|
},
|
|
options: {
|
|
name: 'QRCode',
|
|
renderAs: 'svg',
|
|
encoding: 'ISO_8859_1',
|
|
value: '',
|
|
errorCorrection: QRCodeDefaults.DEFAULT_ERROR_CORRECTION_LEVEL,
|
|
background: QRCodeDefaults.DEFAULT_BACKGROUND,
|
|
color: QRCodeDefaults.DEFAULT_DARK_MODULE_COLOR,
|
|
size: '',
|
|
padding: 0,
|
|
border: {
|
|
color: '',
|
|
width: 0
|
|
}
|
|
}
|
|
});
|
|
dataviz.ExportMixin.extend(QRCode.fn);
|
|
dataviz.ui.plugin(QRCode);
|
|
kendo.deepExtend(dataviz, {
|
|
QRCode: QRCode,
|
|
QRCodeDefaults: QRCodeDefaults,
|
|
QRCodeFunctions: {
|
|
FreeCellVisitor: FreeCellVisitor,
|
|
fillData: fillData,
|
|
padDataString: padDataString,
|
|
generateErrorCodewords: generateErrorCodewords,
|
|
xorPolynomials: xorPolynomials,
|
|
getBlocks: getBlocks,
|
|
multiplyPolynomials: multiplyPolynomials,
|
|
chooseMode: chooseMode,
|
|
getModes: getModes,
|
|
getDataCodewordsCount: getDataCodewordsCount,
|
|
getVersion: getVersion,
|
|
getDataString: getDataString,
|
|
encodeFormatInformation: encodeFormatInformation,
|
|
encodeBCH: encodeBCH,
|
|
dividePolynomials: dividePolynomials,
|
|
initMatrices: initMatrices,
|
|
addFormatInformation: addFormatInformation,
|
|
encodeVersionInformation: encodeVersionInformation,
|
|
addVersionInformation: addVersionInformation,
|
|
addCentricPattern: addCentricPattern,
|
|
addFinderSeparator: addFinderSeparator,
|
|
addFinderPatterns: addFinderPatterns,
|
|
addAlignmentPatterns: addAlignmentPatterns,
|
|
addTimingFunctions: addTimingFunctions,
|
|
scoreMaskMatrixes: scoreMaskMatrixes,
|
|
encodeData: encodeData,
|
|
UTF8Encoder: UTF8Encoder
|
|
},
|
|
QRCodeFields: {
|
|
modes: modeInstances,
|
|
powersOfTwo: powersOfTwo,
|
|
powersOfTwoResult: powersOfTwoResult,
|
|
generatorPolynomials: generatorPolynomials
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.stock', ['kendo.dataviz.chart'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.stockchart',
|
|
name: 'StockChart',
|
|
category: 'dataviz',
|
|
description: 'StockChart widget and associated financial series.',
|
|
depends: ['dataviz.chart']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Class = kendo.Class, Observable = kendo.Observable, deepExtend = kendo.deepExtend, math = Math, proxy = $.proxy, util = kendo.util, last = util.last, renderTemplate = util.renderTemplate, dataviz = kendo.dataviz, defined = util.defined, filterSeriesByType = dataviz.filterSeriesByType, template = kendo.template, Chart = dataviz.ui.Chart, Selection = dataviz.Selection, addDuration = dataviz.addDuration, limitValue = util.limitValue, lteDateIndex = dataviz.lteDateIndex, toDate = dataviz.toDate, toTime = dataviz.toTime;
|
|
var AUTO_CATEGORY_WIDTH = 28, CHANGE = 'change', CSS_PREFIX = 'k-', DRAG = 'drag', DRAG_END = 'dragEnd', NAVIGATOR_PANE = '_navigator', NAVIGATOR_AXIS = NAVIGATOR_PANE, EQUALLY_SPACED_SERIES = dataviz.EQUALLY_SPACED_SERIES, ZOOM_ACCELERATION = 3, ZOOM = 'zoom', ZOOM_END = 'zoomEnd';
|
|
var StockChart = Chart.extend({
|
|
init: function (element, userOptions) {
|
|
$(element).addClass(CSS_PREFIX + 'chart');
|
|
Chart.fn.init.call(this, element, userOptions);
|
|
},
|
|
_applyDefaults: function (options, themeOptions) {
|
|
var chart = this, width = chart.element.width() || dataviz.DEFAULT_WIDTH;
|
|
var stockDefaults = {
|
|
seriesDefaults: { categoryField: options.dateField },
|
|
axisDefaults: {
|
|
categoryAxis: {
|
|
name: 'default',
|
|
majorGridLines: { visible: false },
|
|
labels: { step: 2 },
|
|
majorTicks: { visible: false },
|
|
maxDateGroups: math.floor(width / AUTO_CATEGORY_WIDTH)
|
|
}
|
|
}
|
|
};
|
|
if (themeOptions) {
|
|
themeOptions = deepExtend({}, themeOptions, stockDefaults);
|
|
}
|
|
if (!chart._navigator) {
|
|
Navigator.setup(options, themeOptions);
|
|
}
|
|
Chart.fn._applyDefaults.call(chart, options, themeOptions);
|
|
},
|
|
_initDataSource: function (userOptions) {
|
|
var options = userOptions || {}, dataSource = options.dataSource, hasServerFiltering = dataSource && dataSource.serverFiltering, mainAxis = [].concat(options.categoryAxis)[0], naviOptions = options.navigator || {}, select = naviOptions.select, hasSelect = select && select.from && select.to, filter, dummyAxis;
|
|
if (hasServerFiltering && hasSelect) {
|
|
filter = [].concat(dataSource.filter || []);
|
|
dummyAxis = new dataviz.DateCategoryAxis(deepExtend({ baseUnit: 'fit' }, mainAxis, {
|
|
categories: [
|
|
select.from,
|
|
select.to
|
|
]
|
|
}));
|
|
dataSource.filter = Navigator.buildFilter(dummyAxis.range().min, select.to).concat(filter);
|
|
}
|
|
Chart.fn._initDataSource.call(this, userOptions);
|
|
},
|
|
options: {
|
|
name: 'StockChart',
|
|
dateField: 'date',
|
|
axisDefaults: {
|
|
categoryAxis: {
|
|
type: 'date',
|
|
baseUnit: 'fit',
|
|
justified: true
|
|
},
|
|
valueAxis: {
|
|
narrowRange: true,
|
|
labels: { format: 'C' }
|
|
}
|
|
},
|
|
navigator: {
|
|
select: {},
|
|
seriesDefaults: {
|
|
markers: { visible: false },
|
|
tooltip: {
|
|
visible: true,
|
|
template: '#= kendo.toString(category, \'d\') #'
|
|
},
|
|
line: { width: 2 }
|
|
},
|
|
hint: {},
|
|
visible: true
|
|
},
|
|
tooltip: { visible: true },
|
|
legend: { visible: false }
|
|
},
|
|
_resize: function () {
|
|
var t = this.options.transitions;
|
|
this.options.transitions = false;
|
|
this._fullRedraw();
|
|
this.options.transitions = t;
|
|
},
|
|
_redraw: function () {
|
|
var chart = this, navigator = chart._navigator;
|
|
if (!this._dirty() && navigator && navigator.dataSource) {
|
|
navigator.redrawSlaves();
|
|
} else {
|
|
chart._fullRedraw();
|
|
}
|
|
},
|
|
_dirty: function () {
|
|
var options = this.options;
|
|
var series = [].concat(options.series, options.navigator.series);
|
|
var seriesCount = $.grep(series, function (s) {
|
|
return s && s.visible;
|
|
}).length;
|
|
var dirty = this._seriesCount !== seriesCount;
|
|
this._seriesCount = seriesCount;
|
|
return dirty;
|
|
},
|
|
_fullRedraw: function () {
|
|
var chart = this, navigator = chart._navigator;
|
|
if (!navigator) {
|
|
navigator = chart._navigator = new Navigator(chart);
|
|
}
|
|
navigator._setRange();
|
|
Chart.fn._redraw.call(chart);
|
|
navigator._initSelection();
|
|
},
|
|
_onDataChanged: function () {
|
|
var chart = this;
|
|
Chart.fn._onDataChanged.call(chart);
|
|
chart._dataBound = true;
|
|
},
|
|
_bindCategoryAxis: function (axis, data, axisIx) {
|
|
var chart = this, categoryAxes = chart.options.categoryAxis, axesLength = categoryAxes.length, currentAxis;
|
|
Chart.fn._bindCategoryAxis.apply(this, arguments);
|
|
if (axis.name === NAVIGATOR_AXIS) {
|
|
while (axisIx < axesLength) {
|
|
currentAxis = categoryAxes[axisIx++];
|
|
if (currentAxis.pane == NAVIGATOR_PANE) {
|
|
currentAxis.categories = axis.categories;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_trackSharedTooltip: function (coords) {
|
|
var chart = this, plotArea = chart._plotArea, pane = plotArea.paneByPoint(coords);
|
|
if (pane && pane.options.name === NAVIGATOR_PANE) {
|
|
chart._unsetActivePoint();
|
|
} else {
|
|
Chart.fn._trackSharedTooltip.call(chart, coords);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var chart = this;
|
|
chart._navigator.destroy();
|
|
Chart.fn.destroy.call(chart);
|
|
}
|
|
});
|
|
var Navigator = Observable.extend({
|
|
init: function (chart) {
|
|
var navi = this;
|
|
navi.chart = chart;
|
|
navi.options = deepExtend({}, navi.options, chart.options.navigator);
|
|
navi._initDataSource();
|
|
if (!defined(navi.options.hint.visible)) {
|
|
navi.options.hint.visible = navi.options.visible;
|
|
}
|
|
chart.bind(DRAG, proxy(navi._drag, navi));
|
|
chart.bind(DRAG_END, proxy(navi._dragEnd, navi));
|
|
chart.bind(ZOOM, proxy(navi._zoom, navi));
|
|
chart.bind(ZOOM_END, proxy(navi._zoomEnd, navi));
|
|
},
|
|
options: {},
|
|
_initDataSource: function () {
|
|
var navi = this, options = navi.options, autoBind = options.autoBind, dsOptions = options.dataSource;
|
|
if (!defined(autoBind)) {
|
|
autoBind = navi.chart.options.autoBind;
|
|
}
|
|
navi._dataChangedHandler = proxy(navi._onDataChanged, navi);
|
|
if (dsOptions) {
|
|
navi.dataSource = kendo.data.DataSource.create(dsOptions).bind(CHANGE, navi._dataChangedHandler);
|
|
if (autoBind) {
|
|
navi.dataSource.fetch();
|
|
}
|
|
}
|
|
},
|
|
_onDataChanged: function () {
|
|
var navi = this, chart = navi.chart, series = chart.options.series, seriesIx, seriesLength = series.length, categoryAxes = chart.options.categoryAxis, axisIx, axesLength = categoryAxes.length, data = navi.dataSource.view(), currentSeries, currentAxis, naviCategories;
|
|
for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
|
|
currentSeries = series[seriesIx];
|
|
if (currentSeries.axis == NAVIGATOR_AXIS && chart._isBindable(currentSeries)) {
|
|
currentSeries.data = data;
|
|
}
|
|
}
|
|
for (axisIx = 0; axisIx < axesLength; axisIx++) {
|
|
currentAxis = categoryAxes[axisIx];
|
|
if (currentAxis.pane == NAVIGATOR_PANE) {
|
|
if (currentAxis.name == NAVIGATOR_AXIS) {
|
|
chart._bindCategoryAxis(currentAxis, data, axisIx);
|
|
naviCategories = currentAxis.categories;
|
|
} else {
|
|
currentAxis.categories = naviCategories;
|
|
}
|
|
}
|
|
}
|
|
if (chart._model) {
|
|
navi.redraw();
|
|
navi.filterAxes();
|
|
if (!chart.options.dataSource || chart.options.dataSource && chart._dataBound) {
|
|
navi.redrawSlaves();
|
|
}
|
|
}
|
|
},
|
|
destroy: function () {
|
|
var navi = this, dataSource = navi.dataSource;
|
|
if (dataSource) {
|
|
dataSource.unbind(CHANGE, navi._dataChangeHandler);
|
|
}
|
|
if (navi.selection) {
|
|
navi.selection.destroy();
|
|
}
|
|
},
|
|
redraw: function () {
|
|
this._redrawSelf();
|
|
this._initSelection();
|
|
},
|
|
_initSelection: function () {
|
|
var navi = this, chart = navi.chart, options = navi.options, axis = navi.mainAxis(), axisClone = clone(axis), range = axis.range(), min = range.min, max = range.max, groups = axis.options.categories, select = navi.options.select, selection = navi.selection, from = toDate(select.from), to = toDate(select.to);
|
|
if (groups.length === 0) {
|
|
return;
|
|
}
|
|
if (selection) {
|
|
selection.destroy();
|
|
selection.wrapper.remove();
|
|
}
|
|
axisClone.box = axis.box;
|
|
selection = navi.selection = new Selection(chart, axisClone, {
|
|
min: min,
|
|
max: max,
|
|
from: from,
|
|
to: to,
|
|
selectStart: $.proxy(navi._selectStart, navi),
|
|
select: $.proxy(navi._select, navi),
|
|
selectEnd: $.proxy(navi._selectEnd, navi),
|
|
mousewheel: { zoom: 'left' }
|
|
});
|
|
if (options.hint.visible) {
|
|
navi.hint = new NavigatorHint(chart.element, {
|
|
min: min,
|
|
max: max,
|
|
template: options.hint.template,
|
|
format: options.hint.format
|
|
});
|
|
}
|
|
},
|
|
_setRange: function () {
|
|
var plotArea = this.chart._createPlotArea(true);
|
|
var axis = plotArea.namedCategoryAxes[NAVIGATOR_AXIS];
|
|
var axisOpt = axis.options;
|
|
var range = axis.range();
|
|
var min = range.min;
|
|
var max = addDuration(range.max, axisOpt.baseUnitStep, axisOpt.baseUnit);
|
|
var select = this.options.select || {};
|
|
var from = toDate(select.from) || min;
|
|
if (from < min) {
|
|
from = min;
|
|
}
|
|
var to = toDate(select.to) || max;
|
|
if (to > max) {
|
|
to = max;
|
|
}
|
|
this.options.select = {
|
|
from: from,
|
|
to: to
|
|
};
|
|
this.filterAxes();
|
|
},
|
|
_redrawSelf: function (silent) {
|
|
var plotArea = this.chart._plotArea;
|
|
if (plotArea) {
|
|
plotArea.redraw(last(plotArea.panes), silent);
|
|
}
|
|
},
|
|
redrawSlaves: function () {
|
|
var navi = this, chart = navi.chart, plotArea = chart._plotArea, slavePanes = plotArea.panes.slice(0, -1);
|
|
plotArea.srcSeries = chart.options.series;
|
|
plotArea.redraw(slavePanes);
|
|
},
|
|
_drag: function (e) {
|
|
var navi = this, chart = navi.chart, coords = chart._eventCoordinates(e.originalEvent), navigatorAxis = navi.mainAxis(), naviRange = navigatorAxis.datesRange(), inNavigator = navigatorAxis.pane.box.containsPoint(coords), axis = chart._plotArea.categoryAxis, range = e.axisRanges[axis.options.name], select = navi.options.select, selection = navi.selection, duration, from, to;
|
|
if (!range || inNavigator || !selection) {
|
|
return;
|
|
}
|
|
if (select.from && select.to) {
|
|
duration = toTime(select.to) - toTime(select.from);
|
|
} else {
|
|
duration = toTime(selection.options.to) - toTime(selection.options.from);
|
|
}
|
|
from = toDate(limitValue(toTime(range.min), naviRange.min, toTime(naviRange.max) - duration));
|
|
to = toDate(limitValue(toTime(from) + duration, toTime(naviRange.min) + duration, naviRange.max));
|
|
navi.options.select = {
|
|
from: from,
|
|
to: to
|
|
};
|
|
if (navi._liveDrag()) {
|
|
navi.filterAxes();
|
|
navi.redrawSlaves();
|
|
}
|
|
selection.set(from, to);
|
|
navi.showHint(from, to);
|
|
},
|
|
_dragEnd: function () {
|
|
var navi = this;
|
|
navi.filterAxes();
|
|
navi.filterDataSource();
|
|
navi.redrawSlaves();
|
|
if (navi.hint) {
|
|
navi.hint.hide();
|
|
}
|
|
},
|
|
_liveDrag: function () {
|
|
var support = kendo.support, isTouch = support.touch, browser = support.browser, isFirefox = browser.mozilla, isOldIE = browser.msie && browser.version < 9;
|
|
return !isTouch && !isFirefox && !isOldIE;
|
|
},
|
|
readSelection: function () {
|
|
var navi = this, selection = navi.selection, src = selection.options, dst = navi.options.select;
|
|
dst.from = src.from;
|
|
dst.to = src.to;
|
|
},
|
|
filterAxes: function () {
|
|
var navi = this, select = navi.options.select || {}, chart = navi.chart, allAxes = chart.options.categoryAxis, from = select.from, to = select.to, i, axis;
|
|
for (i = 0; i < allAxes.length; i++) {
|
|
axis = allAxes[i];
|
|
if (axis.pane !== NAVIGATOR_PANE) {
|
|
axis.min = toDate(from);
|
|
axis.max = toDate(to);
|
|
}
|
|
}
|
|
},
|
|
filterDataSource: function () {
|
|
var navi = this, select = navi.options.select || {}, chart = navi.chart, chartDataSource = chart.dataSource, hasServerFiltering = chartDataSource && chartDataSource.options.serverFiltering, axisOptions;
|
|
if (navi.dataSource && hasServerFiltering) {
|
|
axisOptions = new dataviz.DateCategoryAxis(deepExtend({ baseUnit: 'fit' }, chart.options.categoryAxis[0], {
|
|
categories: [
|
|
select.from,
|
|
select.to
|
|
]
|
|
})).options;
|
|
chartDataSource.filter(Navigator.buildFilter(addDuration(axisOptions.min, -axisOptions.baseUnitStep, axisOptions.baseUnit), addDuration(axisOptions.max, axisOptions.baseUnitStep, axisOptions.baseUnit)));
|
|
}
|
|
},
|
|
_zoom: function (e) {
|
|
var navi = this, chart = navi.chart, delta = e.delta, axis = chart._plotArea.categoryAxis, select = navi.options.select, selection = navi.selection, categories = navi.mainAxis().options.categories, fromIx, toIx;
|
|
if (!selection) {
|
|
return;
|
|
}
|
|
fromIx = lteDateIndex(selection.options.from, categories);
|
|
toIx = lteDateIndex(selection.options.to, categories);
|
|
e.originalEvent.preventDefault();
|
|
if (math.abs(delta) > 1) {
|
|
delta *= ZOOM_ACCELERATION;
|
|
}
|
|
if (toIx - fromIx > 1) {
|
|
selection.expand(delta);
|
|
navi.readSelection();
|
|
} else {
|
|
axis.options.min = select.from;
|
|
select.from = axis.scaleRange(-e.delta).min;
|
|
}
|
|
if (!kendo.support.touch) {
|
|
navi.filterAxes();
|
|
navi.redrawSlaves();
|
|
}
|
|
selection.set(select.from, select.to);
|
|
navi.showHint(navi.options.select.from, navi.options.select.to);
|
|
},
|
|
_zoomEnd: function (e) {
|
|
this._dragEnd(e);
|
|
},
|
|
showHint: function (from, to) {
|
|
var navi = this, chart = navi.chart, plotArea = chart._plotArea;
|
|
if (navi.hint) {
|
|
navi.hint.show(from, to, plotArea.backgroundBox());
|
|
}
|
|
},
|
|
_selectStart: function (e) {
|
|
var chart = this.chart;
|
|
chart._selectStart.call(chart, e);
|
|
},
|
|
_select: function (e) {
|
|
var navi = this, chart = navi.chart;
|
|
navi.showHint(e.from, e.to);
|
|
chart._select.call(chart, e);
|
|
},
|
|
_selectEnd: function (e) {
|
|
var navi = this, chart = navi.chart;
|
|
if (navi.hint) {
|
|
navi.hint.hide();
|
|
}
|
|
navi.readSelection();
|
|
navi.filterAxes();
|
|
navi.filterDataSource();
|
|
navi.redrawSlaves();
|
|
chart._selectEnd.call(chart, e);
|
|
},
|
|
mainAxis: function () {
|
|
var plotArea = this.chart._plotArea;
|
|
if (plotArea) {
|
|
return plotArea.namedCategoryAxes[NAVIGATOR_AXIS];
|
|
}
|
|
}
|
|
});
|
|
Navigator.setup = function (options, themeOptions) {
|
|
options = options || {};
|
|
themeOptions = themeOptions || {};
|
|
var naviOptions = deepExtend({}, themeOptions.navigator, options.navigator), panes = options.panes = [].concat(options.panes), paneOptions = deepExtend({}, naviOptions.pane, { name: NAVIGATOR_PANE });
|
|
if (!naviOptions.visible) {
|
|
paneOptions.visible = false;
|
|
paneOptions.height = 0.1;
|
|
}
|
|
panes.push(paneOptions);
|
|
Navigator.attachAxes(options, naviOptions);
|
|
Navigator.attachSeries(options, naviOptions, themeOptions);
|
|
};
|
|
Navigator.attachAxes = function (options, naviOptions) {
|
|
var categoryAxes, valueAxes, series = naviOptions.series || [];
|
|
categoryAxes = options.categoryAxis = [].concat(options.categoryAxis);
|
|
valueAxes = options.valueAxis = [].concat(options.valueAxis);
|
|
var equallySpacedSeries = filterSeriesByType(series, EQUALLY_SPACED_SERIES);
|
|
var justifyAxis = equallySpacedSeries.length === 0;
|
|
var base = deepExtend({
|
|
type: 'date',
|
|
pane: NAVIGATOR_PANE,
|
|
roundToBaseUnit: !justifyAxis,
|
|
justified: justifyAxis,
|
|
_collapse: false,
|
|
majorTicks: { visible: true },
|
|
tooltip: { visible: false },
|
|
labels: { step: 1 },
|
|
autoBind: !naviOptions.dataSource,
|
|
autoBaseUnitSteps: {
|
|
minutes: [1],
|
|
hours: [
|
|
1,
|
|
2
|
|
],
|
|
days: [
|
|
1,
|
|
2
|
|
],
|
|
weeks: [],
|
|
months: [1],
|
|
years: [1]
|
|
},
|
|
_overlap: false
|
|
});
|
|
var user = naviOptions.categoryAxis;
|
|
categoryAxes.push(deepExtend({}, base, { maxDateGroups: 200 }, user, {
|
|
name: NAVIGATOR_AXIS,
|
|
baseUnit: 'fit',
|
|
baseUnitStep: 'auto',
|
|
labels: { visible: false },
|
|
majorTicks: { visible: false }
|
|
}), deepExtend({}, base, user, {
|
|
name: NAVIGATOR_AXIS + '_labels',
|
|
maxDateGroups: 20,
|
|
baseUnitStep: 'auto',
|
|
plotBands: [],
|
|
autoBaseUnitSteps: { minutes: [] }
|
|
}), deepExtend({}, base, user, {
|
|
name: NAVIGATOR_AXIS + '_ticks',
|
|
maxDateGroups: 200,
|
|
majorTicks: { width: 0.5 },
|
|
plotBands: [],
|
|
labels: {
|
|
visible: false,
|
|
mirror: true
|
|
}
|
|
}));
|
|
valueAxes.push(deepExtend({
|
|
name: NAVIGATOR_AXIS,
|
|
pane: NAVIGATOR_PANE,
|
|
majorGridLines: { visible: false },
|
|
visible: false
|
|
}, naviOptions.valueAxis));
|
|
};
|
|
Navigator.attachSeries = function (options, naviOptions, themeOptions) {
|
|
var series = options.series = options.series || [], navigatorSeries = [].concat(naviOptions.series || []), seriesColors = themeOptions.seriesColors, defaults = naviOptions.seriesDefaults, i;
|
|
for (i = 0; i < navigatorSeries.length; i++) {
|
|
series.push(deepExtend({
|
|
color: seriesColors[i % seriesColors.length],
|
|
categoryField: naviOptions.dateField,
|
|
visibleInLegend: false,
|
|
tooltip: { visible: false }
|
|
}, defaults, navigatorSeries[i], {
|
|
axis: NAVIGATOR_AXIS,
|
|
categoryAxis: NAVIGATOR_AXIS,
|
|
autoBind: !naviOptions.dataSource
|
|
}));
|
|
}
|
|
};
|
|
Navigator.buildFilter = function (from, to) {
|
|
return [
|
|
{
|
|
field: 'Date',
|
|
operator: 'gte',
|
|
value: toDate(from)
|
|
},
|
|
{
|
|
field: 'Date',
|
|
operator: 'lt',
|
|
value: toDate(to)
|
|
}
|
|
];
|
|
};
|
|
var NavigatorHint = Class.extend({
|
|
init: function (container, options) {
|
|
var hint = this;
|
|
hint.options = deepExtend({}, hint.options, options);
|
|
hint.container = container;
|
|
hint.chartPadding = {
|
|
top: parseInt(container.css('paddingTop'), 10),
|
|
left: parseInt(container.css('paddingLeft'), 10)
|
|
};
|
|
hint.template = hint.template;
|
|
if (!hint.template) {
|
|
hint.template = hint.template = renderTemplate('<div class=\'' + CSS_PREFIX + 'navigator-hint\' ' + 'style=\'display: none; position: absolute; top: 1px; left: 1px;\'>' + '<div class=\'' + CSS_PREFIX + 'tooltip ' + CSS_PREFIX + 'chart-tooltip\'> </div>' + '<div class=\'' + CSS_PREFIX + 'scroll\' />' + '</div>');
|
|
}
|
|
hint.element = $(hint.template()).appendTo(container);
|
|
},
|
|
options: {
|
|
format: '{0:d} - {1:d}',
|
|
hideDelay: 500
|
|
},
|
|
show: function (from, to, bbox) {
|
|
var hint = this, middle = toDate(toTime(from) + toTime(to - from) / 2), options = hint.options, text = kendo.format(hint.options.format, from, to), tooltip = hint.element.find('.' + CSS_PREFIX + 'tooltip'), scroll = hint.element.find('.' + CSS_PREFIX + 'scroll'), scrollWidth = bbox.width() * 0.4, minPos = bbox.center().x - scrollWidth, maxPos = bbox.center().x, posRange = maxPos - minPos, range = options.max - options.min, scale = posRange / range, offset = middle - options.min, hintTemplate;
|
|
if (hint._hideTimeout) {
|
|
clearTimeout(hint._hideTimeout);
|
|
}
|
|
if (!hint._visible) {
|
|
hint.element.stop(false, true).css('visibility', 'hidden').show();
|
|
hint._visible = true;
|
|
}
|
|
if (options.template) {
|
|
hintTemplate = template(options.template);
|
|
text = hintTemplate({
|
|
from: from,
|
|
to: to
|
|
});
|
|
}
|
|
tooltip.html(text).css({
|
|
left: bbox.center().x - tooltip.outerWidth() / 2,
|
|
top: bbox.y1
|
|
});
|
|
scroll.css({
|
|
width: scrollWidth,
|
|
left: minPos + offset * scale,
|
|
top: bbox.y1 + parseInt(tooltip.css('margin-top'), 10) + parseInt(tooltip.css('border-top-width'), 10) + tooltip.height() / 2
|
|
});
|
|
hint.element.css('visibility', 'visible');
|
|
},
|
|
hide: function () {
|
|
var hint = this;
|
|
if (hint._hideTimeout) {
|
|
clearTimeout(hint._hideTimeout);
|
|
}
|
|
hint._hideTimeout = setTimeout(function () {
|
|
hint._visible = false;
|
|
hint.element.fadeOut('slow');
|
|
}, hint.options.hideDelay);
|
|
}
|
|
});
|
|
function ClonedObject() {
|
|
}
|
|
function clone(obj) {
|
|
ClonedObject.prototype = obj;
|
|
return new ClonedObject();
|
|
}
|
|
dataviz.ui.plugin(StockChart);
|
|
deepExtend(dataviz, { Navigator: Navigator });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.sparkline', ['kendo.dataviz.chart'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.sparkline',
|
|
name: 'Sparkline',
|
|
category: 'dataviz',
|
|
description: 'Sparkline widgets.',
|
|
depends: ['dataviz.chart']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, dataviz = kendo.dataviz, Chart = dataviz.ui.Chart, ObservableArray = kendo.data.ObservableArray, SharedTooltip = dataviz.SharedTooltip, deepExtend = kendo.deepExtend, isArray = $.isArray, proxy = $.proxy, inArray = dataviz.inArray, math = Math;
|
|
var CSS_PREFIX = 'k-', DEAULT_BAR_WIDTH = 150, DEAULT_BULLET_WIDTH = 150, BAR = 'bar', BULLET = 'bullet', PIE = 'pie', LEAVE = 'leave', NO_CROSSHAIR = [
|
|
BAR,
|
|
BULLET
|
|
];
|
|
var Sparkline = Chart.extend({
|
|
init: function (element, userOptions) {
|
|
var chart = this, stage = chart.stage = $('<span />'), options = userOptions || {};
|
|
element = $(element).addClass(CSS_PREFIX + 'sparkline').empty().append(stage);
|
|
chart._initialWidth = math.floor(element.width());
|
|
options = wrapNumber(options);
|
|
if (isArray(options) || options instanceof ObservableArray) {
|
|
options = { seriesDefaults: { data: options } };
|
|
}
|
|
if (!options.series) {
|
|
options.series = [{ data: wrapNumber(options.data) }];
|
|
}
|
|
deepExtend(options, { seriesDefaults: { type: options.type } });
|
|
if (inArray(options.series[0].type, NO_CROSSHAIR) || inArray(options.seriesDefaults.type, NO_CROSSHAIR)) {
|
|
options = deepExtend({}, { categoryAxis: { crosshair: { visible: false } } }, options);
|
|
}
|
|
Chart.fn.init.call(chart, element, options);
|
|
},
|
|
options: {
|
|
name: 'Sparkline',
|
|
chartArea: { margin: 2 },
|
|
axisDefaults: {
|
|
visible: false,
|
|
majorGridLines: { visible: false },
|
|
valueAxis: { narrowRange: true }
|
|
},
|
|
seriesDefaults: {
|
|
type: 'line',
|
|
area: { line: { width: 0.5 } },
|
|
bar: { stack: true },
|
|
padding: 2,
|
|
width: 0.5,
|
|
overlay: { gradient: null },
|
|
highlight: { visible: false },
|
|
border: { width: 0 },
|
|
markers: {
|
|
size: 2,
|
|
visible: false
|
|
}
|
|
},
|
|
tooltip: {
|
|
visible: true,
|
|
shared: true
|
|
},
|
|
categoryAxis: {
|
|
crosshair: {
|
|
visible: true,
|
|
tooltip: { visible: false }
|
|
}
|
|
},
|
|
legend: { visible: false },
|
|
transitions: false,
|
|
pointWidth: 5,
|
|
panes: [{ clip: false }]
|
|
},
|
|
_modelOptions: function () {
|
|
var chart = this, chartOptions = chart.options, options, width = chart._initialWidth, stage = chart.stage;
|
|
chart.stage.children().hide();
|
|
var space = $('<span> </span>');
|
|
chart.stage.append(space);
|
|
options = deepExtend({
|
|
width: width ? width : chart._autoWidth(),
|
|
height: stage.height(),
|
|
transitions: chartOptions.transitions
|
|
}, chartOptions.chartArea, {
|
|
inline: true,
|
|
align: false
|
|
});
|
|
stage.css({
|
|
width: options.width,
|
|
height: options.height
|
|
});
|
|
space.remove();
|
|
chart.stage.children().show();
|
|
chart.surface.resize();
|
|
return options;
|
|
},
|
|
_createTooltip: function () {
|
|
var chart = this, options = chart.options, element = chart.element, tooltip;
|
|
if (chart._sharedTooltip()) {
|
|
tooltip = new SparklineSharedTooltip(element, chart._plotArea, options.tooltip);
|
|
} else {
|
|
tooltip = Chart.fn._createTooltip.call(chart);
|
|
}
|
|
tooltip.bind(LEAVE, proxy(chart._tooltipleave, chart));
|
|
return tooltip;
|
|
},
|
|
_surfaceWrap: function () {
|
|
return this.stage;
|
|
},
|
|
_autoWidth: function () {
|
|
var chart = this, options = chart.options, margin = dataviz.getSpacing(options.chartArea.margin), series = options.series, dsTotal = chart.dataSource.total(), seriesTotal = 0, width, i, currentSeries;
|
|
for (i = 0; i < series.length; i++) {
|
|
currentSeries = series[i];
|
|
if (currentSeries.type === BAR) {
|
|
return DEAULT_BAR_WIDTH;
|
|
}
|
|
if (currentSeries.type === BULLET) {
|
|
return DEAULT_BULLET_WIDTH;
|
|
}
|
|
if (currentSeries.type === PIE) {
|
|
return chart.stage.height();
|
|
}
|
|
if (currentSeries.data) {
|
|
seriesTotal = math.max(seriesTotal, currentSeries.data.length);
|
|
}
|
|
}
|
|
width = math.max(dsTotal, seriesTotal) * options.pointWidth;
|
|
if (width > 0) {
|
|
width += margin.left + margin.right;
|
|
}
|
|
return width;
|
|
}
|
|
});
|
|
var SparklineSharedTooltip = SharedTooltip.extend({
|
|
options: { animation: { duration: 0 } },
|
|
_anchor: function (point, slot) {
|
|
var anchor = SharedTooltip.fn._anchor.call(this, point, slot);
|
|
var size = this._measure();
|
|
anchor.y = -size.height - this.options.offset;
|
|
return anchor;
|
|
},
|
|
_hideElement: function () {
|
|
if (this.element) {
|
|
this.element.hide().remove();
|
|
}
|
|
}
|
|
});
|
|
function wrapNumber(x) {
|
|
return typeof x === 'number' ? [x] : x;
|
|
}
|
|
dataviz.ui.plugin(Sparkline);
|
|
deepExtend(dataviz, { SparklineSharedTooltip: SparklineSharedTooltip });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/location', ['kendo.drawing'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var math = Math, abs = math.abs, atan = math.atan, atan2 = math.atan2, cos = math.cos, max = math.max, min = math.min, sin = math.sin, tan = math.tan, kendo = window.kendo, Class = kendo.Class, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, util = kendo.util, defined = util.defined, deg = util.deg, rad = util.rad, round = util.round, sqr = util.sqr, valueOrDefault = util.valueOrDefault;
|
|
var Location = Class.extend({
|
|
init: function (lat, lng) {
|
|
if (arguments.length === 1) {
|
|
this.lat = lat[0];
|
|
this.lng = lat[1];
|
|
} else {
|
|
this.lat = lat;
|
|
this.lng = lng;
|
|
}
|
|
},
|
|
DISTANCE_ITERATIONS: 100,
|
|
DISTANCE_CONVERGENCE: 1e-12,
|
|
DISTANCE_PRECISION: 2,
|
|
FORMAT: '{0:N6},{1:N6}',
|
|
toArray: function () {
|
|
return [
|
|
this.lat,
|
|
this.lng
|
|
];
|
|
},
|
|
equals: function (loc) {
|
|
return loc && loc.lat === this.lat && loc.lng === this.lng;
|
|
},
|
|
clone: function () {
|
|
return new Location(this.lat, this.lng);
|
|
},
|
|
round: function (precision) {
|
|
this.lng = round(this.lng, precision);
|
|
this.lat = round(this.lat, precision);
|
|
return this;
|
|
},
|
|
wrap: function () {
|
|
this.lng = this.lng % 180;
|
|
this.lat = this.lat % 90;
|
|
return this;
|
|
},
|
|
distanceTo: function (dest, datum) {
|
|
return this.greatCircleTo(dest, datum).distance;
|
|
},
|
|
destination: function (distance, bearing, datum) {
|
|
bearing = rad(bearing);
|
|
datum = datum || dataviz.map.datums.WGS84;
|
|
var fromLat = rad(this.lat);
|
|
var fromLng = rad(this.lng);
|
|
var dToR = distance / kendo.dataviz.map.datums.WGS84.a;
|
|
var lat = math.asin(sin(fromLat) * cos(dToR) + cos(fromLat) * sin(dToR) * cos(bearing));
|
|
var lng = fromLng + atan2(sin(bearing) * sin(dToR) * cos(fromLat), cos(dToR) - sin(fromLat) * sin(lat));
|
|
return new Location(deg(lat), deg(lng));
|
|
},
|
|
greatCircleTo: function (dest, datum) {
|
|
dest = Location.create(dest);
|
|
datum = datum || dataviz.map.datums.WGS84;
|
|
if (!dest || this.clone().round(8).equals(dest.clone().round(8))) {
|
|
return {
|
|
distance: 0,
|
|
azimuthFrom: 0,
|
|
azimuthTo: 0
|
|
};
|
|
}
|
|
var a = datum.a;
|
|
var b = datum.b;
|
|
var f = datum.f;
|
|
var L = rad(dest.lng - this.lng);
|
|
var U1 = atan((1 - f) * tan(rad(this.lat)));
|
|
var sinU1 = sin(U1);
|
|
var cosU1 = cos(U1);
|
|
var U2 = atan((1 - f) * tan(rad(dest.lat)));
|
|
var sinU2 = sin(U2);
|
|
var cosU2 = cos(U2);
|
|
var lambda = L;
|
|
var prevLambda;
|
|
var i = this.DISTANCE_ITERATIONS;
|
|
var converged = false;
|
|
var sinLambda;
|
|
var cosLambda;
|
|
var sino;
|
|
var cosA2;
|
|
var coso;
|
|
var cos2om;
|
|
var sigma;
|
|
while (!converged && i-- > 0) {
|
|
sinLambda = sin(lambda);
|
|
cosLambda = cos(lambda);
|
|
sino = math.sqrt(sqr(cosU2 * sinLambda) + sqr(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
|
|
coso = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
|
sigma = atan2(sino, coso);
|
|
var sinA = cosU1 * cosU2 * sinLambda / sino;
|
|
cosA2 = 1 - sqr(sinA);
|
|
cos2om = 0;
|
|
if (cosA2 !== 0) {
|
|
cos2om = coso - 2 * sinU1 * sinU2 / cosA2;
|
|
}
|
|
prevLambda = lambda;
|
|
var C = f / 16 * cosA2 * (4 + f * (4 - 3 * cosA2));
|
|
lambda = L + (1 - C) * f * sinA * (sigma + C * sino * (cos2om + C * coso * (-1 + 2 * sqr(cos2om))));
|
|
converged = abs(lambda - prevLambda) <= this.DISTANCE_CONVERGENCE;
|
|
}
|
|
var u2 = cosA2 * (sqr(a) - sqr(b)) / sqr(b);
|
|
var A = 1 + u2 / 16384 * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)));
|
|
var B = u2 / 1024 * (256 + u2 * (-128 + u2 * (74 - 47 * u2)));
|
|
var deltao = B * sino * (cos2om + B / 4 * (coso * (-1 + 2 * sqr(cos2om)) - B / 6 * cos2om * (-3 + 4 * sqr(sino)) * (-3 + 4 * sqr(cos2om))));
|
|
var azimuthFrom = atan2(cosU2 * sinLambda, cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
|
|
var azimuthTo = atan2(cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
|
|
return {
|
|
distance: round(b * A * (sigma - deltao), this.DISTANCE_PRECISION),
|
|
azimuthFrom: deg(azimuthFrom),
|
|
azimuthTo: deg(azimuthTo)
|
|
};
|
|
}
|
|
});
|
|
Location.fn.toString = function () {
|
|
return kendo.format(this.FORMAT, this.lat, this.lng);
|
|
};
|
|
Location.fromLngLat = function (ll) {
|
|
return new Location(ll[1], ll[0]);
|
|
};
|
|
Location.fromLatLng = function (ll) {
|
|
return new Location(ll[0], ll[1]);
|
|
};
|
|
Location.create = function (a, b) {
|
|
if (defined(a)) {
|
|
if (a instanceof Location) {
|
|
return a.clone();
|
|
} else if (arguments.length === 1 && a.length === 2) {
|
|
return Location.fromLatLng(a);
|
|
} else {
|
|
return new Location(a, b);
|
|
}
|
|
}
|
|
};
|
|
var Extent = Class.extend({
|
|
init: function (nw, se) {
|
|
nw = Location.create(nw);
|
|
se = Location.create(se);
|
|
if (nw.lng + 180 > se.lng + 180 && nw.lat + 90 < se.lat + 90) {
|
|
this.se = nw;
|
|
this.nw = se;
|
|
} else {
|
|
this.se = se;
|
|
this.nw = nw;
|
|
}
|
|
},
|
|
contains: function (loc) {
|
|
var nw = this.nw, se = this.se, lng = valueOrDefault(loc.lng, loc[1]), lat = valueOrDefault(loc.lat, loc[0]);
|
|
return loc && lng + 180 >= nw.lng + 180 && lng + 180 <= se.lng + 180 && lat + 90 >= se.lat + 90 && lat + 90 <= nw.lat + 90;
|
|
},
|
|
center: function () {
|
|
var nw = this.nw;
|
|
var se = this.se;
|
|
var lng = nw.lng + (se.lng - nw.lng) / 2;
|
|
var lat = nw.lat + (se.lat - nw.lat) / 2;
|
|
return new Location(lat, lng);
|
|
},
|
|
containsAny: function (locs) {
|
|
var result = false;
|
|
for (var i = 0; i < locs.length; i++) {
|
|
result = result || this.contains(locs[i]);
|
|
}
|
|
return result;
|
|
},
|
|
include: function (loc) {
|
|
var nw = this.nw, se = this.se, lng = valueOrDefault(loc.lng, loc[1]), lat = valueOrDefault(loc.lat, loc[0]);
|
|
nw.lng = min(nw.lng, lng);
|
|
nw.lat = max(nw.lat, lat);
|
|
se.lng = max(se.lng, lng);
|
|
se.lat = min(se.lat, lat);
|
|
},
|
|
includeAll: function (locs) {
|
|
for (var i = 0; i < locs.length; i++) {
|
|
this.include(locs[i]);
|
|
}
|
|
},
|
|
edges: function () {
|
|
var nw = this.nw, se = this.se;
|
|
return {
|
|
nw: this.nw,
|
|
ne: new Location(nw.lat, se.lng),
|
|
se: this.se,
|
|
sw: new Location(se.lat, nw.lng)
|
|
};
|
|
},
|
|
toArray: function () {
|
|
var nw = this.nw, se = this.se;
|
|
return [
|
|
nw,
|
|
new Location(nw.lat, se.lng),
|
|
se,
|
|
new Location(se.lat, nw.lng)
|
|
];
|
|
},
|
|
overlaps: function (extent) {
|
|
return this.containsAny(extent.toArray()) || extent.containsAny(this.toArray());
|
|
}
|
|
});
|
|
Extent.World = new Extent([
|
|
90,
|
|
-180
|
|
], [
|
|
-90,
|
|
180
|
|
]);
|
|
Extent.create = function (a, b) {
|
|
if (a instanceof Extent) {
|
|
return a;
|
|
} else if (a && b) {
|
|
return new Extent(a, b);
|
|
} else if (a && a.length === 4 && !b) {
|
|
return new Extent([
|
|
a[0],
|
|
a[1]
|
|
], [
|
|
a[2],
|
|
a[3]
|
|
]);
|
|
}
|
|
};
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
Extent: Extent,
|
|
Location: Location
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/attribution', ['kendo.drawing'], f);
|
|
}(function () {
|
|
(function () {
|
|
var kendo = window.kendo, Widget = kendo.ui.Widget, template = kendo.template, valueOrDefault = kendo.util.valueOrDefault, defined = kendo.util.defined;
|
|
var Attribution = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._initOptions(options);
|
|
this.items = [];
|
|
this.element.addClass('k-widget k-attribution');
|
|
},
|
|
options: {
|
|
name: 'Attribution',
|
|
separator: ' | ',
|
|
itemTemplate: '#= text #'
|
|
},
|
|
filter: function (extent, zoom) {
|
|
this._extent = extent;
|
|
this._zoom = zoom;
|
|
this._render();
|
|
},
|
|
add: function (item) {
|
|
if (defined(item)) {
|
|
if (typeof item === 'string') {
|
|
item = { text: item };
|
|
}
|
|
this.items.push(item);
|
|
this._render();
|
|
}
|
|
},
|
|
remove: function (text) {
|
|
var result = [];
|
|
for (var i = 0; i < this.items.length; i++) {
|
|
var item = this.items[i];
|
|
if (item.text !== text) {
|
|
result.push(item);
|
|
}
|
|
}
|
|
this.items = result;
|
|
this._render();
|
|
},
|
|
clear: function () {
|
|
this.items = [];
|
|
this.element.empty();
|
|
},
|
|
_render: function () {
|
|
var result = [];
|
|
var itemTemplate = template(this.options.itemTemplate);
|
|
for (var i = 0; i < this.items.length; i++) {
|
|
var item = this.items[i];
|
|
var text = this._itemText(item);
|
|
if (text !== '') {
|
|
result.push(itemTemplate({ text: text }));
|
|
}
|
|
}
|
|
if (result.length > 0) {
|
|
this.element.empty().append(result.join(this.options.separator)).show();
|
|
} else {
|
|
this.element.hide();
|
|
}
|
|
},
|
|
_itemText: function (item) {
|
|
var text = '';
|
|
var inZoomLevel = this._inZoomLevel(item.minZoom, item.maxZoom);
|
|
var inArea = this._inArea(item.extent);
|
|
if (inZoomLevel && inArea) {
|
|
text += item.text;
|
|
}
|
|
return text;
|
|
},
|
|
_inZoomLevel: function (min, max) {
|
|
var result = true;
|
|
min = valueOrDefault(min, -Number.MAX_VALUE);
|
|
max = valueOrDefault(max, Number.MAX_VALUE);
|
|
result = this._zoom > min && this._zoom < max;
|
|
return result;
|
|
},
|
|
_inArea: function (area) {
|
|
var result = true;
|
|
if (area) {
|
|
result = area.contains(this._extent);
|
|
}
|
|
return result;
|
|
}
|
|
});
|
|
kendo.dataviz.ui.plugin(Attribution);
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/navigator', ['kendo.core'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo;
|
|
var Widget = kendo.ui.Widget;
|
|
var keys = kendo.keys;
|
|
var proxy = $.proxy;
|
|
var NS = '.kendoNavigator';
|
|
function button(dir) {
|
|
return kendo.format('<button class="k-button k-navigator-{0}">' + '<span class="k-icon k-i-arrow-{0}"/>' + '</button>', dir);
|
|
}
|
|
var BUTTONS = button('n') + button('e') + button('s') + button('w');
|
|
var Navigator = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._initOptions(options);
|
|
this.element.addClass('k-widget k-header k-shadow k-navigator').append(BUTTONS).on('click' + NS, '.k-button', proxy(this, '_click'));
|
|
var parentElement = this.element.parent().closest('[' + kendo.attr('role') + ']');
|
|
this._keyroot = parentElement.length > 0 ? parentElement : this.element;
|
|
this._tabindex(this._keyroot);
|
|
this._keydown = proxy(this._keydown, this);
|
|
this._keyroot.on('keydown', this._keydown);
|
|
},
|
|
options: {
|
|
name: 'Navigator',
|
|
panStep: 1
|
|
},
|
|
events: ['pan'],
|
|
dispose: function () {
|
|
this._keyroot.off('keydown', this._keydown);
|
|
},
|
|
_pan: function (x, y) {
|
|
var panStep = this.options.panStep;
|
|
this.trigger('pan', {
|
|
x: x * panStep,
|
|
y: y * panStep
|
|
});
|
|
},
|
|
_click: function (e) {
|
|
var x = 0;
|
|
var y = 0;
|
|
var button = $(e.currentTarget);
|
|
if (button.is('.k-navigator-n')) {
|
|
y = 1;
|
|
} else if (button.is('.k-navigator-s')) {
|
|
y = -1;
|
|
} else if (button.is('.k-navigator-e')) {
|
|
x = 1;
|
|
} else if (button.is('.k-navigator-w')) {
|
|
x = -1;
|
|
}
|
|
this._pan(x, y);
|
|
e.preventDefault();
|
|
},
|
|
_keydown: function (e) {
|
|
switch (e.which) {
|
|
case keys.UP:
|
|
this._pan(0, 1);
|
|
e.preventDefault();
|
|
break;
|
|
case keys.DOWN:
|
|
this._pan(0, -1);
|
|
e.preventDefault();
|
|
break;
|
|
case keys.RIGHT:
|
|
this._pan(1, 0);
|
|
e.preventDefault();
|
|
break;
|
|
case keys.LEFT:
|
|
this._pan(-1, 0);
|
|
e.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
kendo.dataviz.ui.plugin(Navigator);
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/zoom', ['kendo.core'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var kendo = window.kendo;
|
|
var Widget = kendo.ui.Widget;
|
|
var keys = kendo.keys;
|
|
var proxy = $.proxy;
|
|
function button(dir, symbol) {
|
|
return kendo.format('<button class="k-button k-zoom-{0}" title="zoom-{0}">{1}</button>', dir, symbol);
|
|
}
|
|
var NS = '.kendoZoomControl';
|
|
var BUTTONS = button('in', '+') + button('out', '-');
|
|
var PLUS = 187;
|
|
var MINUS = 189;
|
|
var FF_PLUS = 61;
|
|
var FF_MINUS = 173;
|
|
var ZoomControl = Widget.extend({
|
|
init: function (element, options) {
|
|
Widget.fn.init.call(this, element, options);
|
|
this._initOptions(options);
|
|
this.element.addClass('k-widget k-zoom-control k-button-wrap k-buttons-horizontal').append(BUTTONS).on('click' + NS, '.k-button', proxy(this, '_click'));
|
|
var parentElement = this.element.parent().closest('[' + kendo.attr('role') + ']');
|
|
this._keyroot = parentElement.length > 0 ? parentElement : this.element;
|
|
this._tabindex(this._keyroot);
|
|
this._keydown = proxy(this._keydown, this);
|
|
this._keyroot.on('keydown', this._keydown);
|
|
},
|
|
options: {
|
|
name: 'ZoomControl',
|
|
zoomStep: 1
|
|
},
|
|
events: ['change'],
|
|
_change: function (dir) {
|
|
var zoomStep = this.options.zoomStep;
|
|
this.trigger('change', { delta: dir * zoomStep });
|
|
},
|
|
_click: function (e) {
|
|
var button = $(e.currentTarget);
|
|
var dir = 1;
|
|
if (button.is('.k-zoom-out')) {
|
|
dir = -1;
|
|
}
|
|
this._change(dir);
|
|
e.preventDefault();
|
|
},
|
|
_keydown: function (e) {
|
|
switch (e.which) {
|
|
case keys.NUMPAD_PLUS:
|
|
case PLUS:
|
|
case FF_PLUS:
|
|
this._change(1);
|
|
break;
|
|
case keys.NUMPAD_MINUS:
|
|
case MINUS:
|
|
case FF_MINUS:
|
|
this._change(-1);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
kendo.dataviz.ui.plugin(ZoomControl);
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/crs', [
|
|
'dataviz/map/location',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var math = Math, atan = math.atan, exp = math.exp, pow = math.pow, sin = math.sin, log = math.log, tan = math.tan, kendo = window.kendo, Class = kendo.Class, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, g = kendo.geometry, Point = g.Point, map = dataviz.map, Location = map.Location, util = kendo.util, rad = util.rad, deg = util.deg, limit = util.limitValue;
|
|
var PI = math.PI, PI_DIV_2 = PI / 2, PI_DIV_4 = PI / 4, DEG_TO_RAD = PI / 180;
|
|
var WGS84 = {
|
|
a: 6378137,
|
|
b: 6356752.314245179,
|
|
f: 0.0033528106647474805,
|
|
e: 0.08181919084262149
|
|
};
|
|
var Mercator = Class.extend({
|
|
init: function (options) {
|
|
this._initOptions(options);
|
|
},
|
|
MAX_LNG: 180,
|
|
MAX_LAT: 85.0840590501,
|
|
INVERSE_ITERATIONS: 15,
|
|
INVERSE_CONVERGENCE: 1e-12,
|
|
options: {
|
|
centralMeridian: 0,
|
|
datum: WGS84
|
|
},
|
|
forward: function (loc, clamp) {
|
|
var proj = this, options = proj.options, datum = options.datum, r = datum.a, lng0 = options.centralMeridian, lat = limit(loc.lat, -proj.MAX_LAT, proj.MAX_LAT), lng = clamp ? limit(loc.lng, -proj.MAX_LNG, proj.MAX_LNG) : loc.lng, x = rad(lng - lng0) * r, y = proj._projectLat(lat);
|
|
return new Point(x, y);
|
|
},
|
|
_projectLat: function (lat) {
|
|
var datum = this.options.datum, ecc = datum.e, r = datum.a, y = rad(lat), ts = tan(PI_DIV_4 + y / 2), con = ecc * sin(y), p = pow((1 - con) / (1 + con), ecc / 2);
|
|
return r * log(ts * p);
|
|
},
|
|
inverse: function (point, clamp) {
|
|
var proj = this, options = proj.options, datum = options.datum, r = datum.a, lng0 = options.centralMeridian, lng = point.x / (DEG_TO_RAD * r) + lng0, lat = limit(proj._inverseY(point.y), -proj.MAX_LAT, proj.MAX_LAT);
|
|
if (clamp) {
|
|
lng = limit(lng, -proj.MAX_LNG, proj.MAX_LNG);
|
|
}
|
|
return new Location(lat, lng);
|
|
},
|
|
_inverseY: function (y) {
|
|
var proj = this, datum = proj.options.datum, r = datum.a, ecc = datum.e, ecch = ecc / 2, ts = exp(-y / r), phi = PI_DIV_2 - 2 * atan(ts), i;
|
|
for (i = 0; i <= proj.INVERSE_ITERATIONS; i++) {
|
|
var con = ecc * sin(phi), p = pow((1 - con) / (1 + con), ecch), dphi = PI_DIV_2 - 2 * atan(ts * p) - phi;
|
|
phi += dphi;
|
|
if (math.abs(dphi) <= proj.INVERSE_CONVERGENCE) {
|
|
break;
|
|
}
|
|
}
|
|
return deg(phi);
|
|
}
|
|
});
|
|
var SphericalMercator = Mercator.extend({
|
|
MAX_LAT: 85.0511287798,
|
|
_projectLat: function (lat) {
|
|
var r = this.options.datum.a, y = rad(lat), ts = tan(PI_DIV_4 + y / 2);
|
|
return r * log(ts);
|
|
},
|
|
_inverseY: function (y) {
|
|
var r = this.options.datum.a, ts = exp(-y / r);
|
|
return deg(PI_DIV_2 - 2 * atan(ts));
|
|
}
|
|
});
|
|
var Equirectangular = Class.extend({
|
|
forward: function (loc) {
|
|
return new Point(loc.lng, loc.lat);
|
|
},
|
|
inverse: function (point) {
|
|
return new Location(point.y, point.x);
|
|
}
|
|
});
|
|
var EPSG3857 = Class.extend({
|
|
init: function () {
|
|
var crs = this, proj = crs._proj = new SphericalMercator();
|
|
var c = this.c = 2 * PI * proj.options.datum.a;
|
|
this._tm = g.transform().translate(0.5, 0.5).scale(1 / c, -1 / c);
|
|
this._itm = g.transform().scale(c, -c).translate(-0.5, -0.5);
|
|
},
|
|
toPoint: function (loc, scale, clamp) {
|
|
var point = this._proj.forward(loc, clamp);
|
|
return point.transform(this._tm).scale(scale || 1);
|
|
},
|
|
toLocation: function (point, scale, clamp) {
|
|
point = point.clone().scale(1 / (scale || 1)).transform(this._itm);
|
|
return this._proj.inverse(point, clamp);
|
|
}
|
|
});
|
|
var EPSG3395 = Class.extend({
|
|
init: function () {
|
|
this._proj = new Mercator();
|
|
},
|
|
toPoint: function (loc) {
|
|
return this._proj.forward(loc);
|
|
},
|
|
toLocation: function (point) {
|
|
return this._proj.inverse(point);
|
|
}
|
|
});
|
|
var EPSG4326 = Class.extend({
|
|
init: function () {
|
|
this._proj = new Equirectangular();
|
|
},
|
|
toPoint: function (loc) {
|
|
return this._proj.forward(loc);
|
|
},
|
|
toLocation: function (point) {
|
|
return this._proj.inverse(point);
|
|
}
|
|
});
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
crs: {
|
|
EPSG3395: EPSG3395,
|
|
EPSG3857: EPSG3857,
|
|
EPSG4326: EPSG4326
|
|
},
|
|
datums: { WGS84: WGS84 },
|
|
projections: {
|
|
Equirectangular: Equirectangular,
|
|
Mercator: Mercator,
|
|
SphericalMercator: SphericalMercator
|
|
}
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/base', [
|
|
'kendo.core',
|
|
'dataviz/map/location'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, Extent = dataviz.map.Extent, util = kendo.util, defined = util.defined;
|
|
var Layer = Class.extend({
|
|
init: function (map, options) {
|
|
this._initOptions(options);
|
|
this.map = map;
|
|
this.element = $('<div class=\'k-layer\'></div>').css({
|
|
'zIndex': this.options.zIndex,
|
|
'opacity': this.options.opacity
|
|
}).appendTo(map.scrollElement);
|
|
this._beforeReset = proxy(this._beforeReset, this);
|
|
this._reset = proxy(this._reset, this);
|
|
this._resize = proxy(this._resize, this);
|
|
this._panEnd = proxy(this._panEnd, this);
|
|
this._activate();
|
|
this._updateAttribution();
|
|
},
|
|
destroy: function () {
|
|
this._deactivate();
|
|
},
|
|
show: function () {
|
|
this.reset();
|
|
this._activate();
|
|
this._applyExtent(true);
|
|
},
|
|
hide: function () {
|
|
this._deactivate();
|
|
this._setVisibility(false);
|
|
},
|
|
reset: function () {
|
|
this._beforeReset();
|
|
this._reset();
|
|
},
|
|
_reset: function () {
|
|
this._applyExtent();
|
|
},
|
|
_beforeReset: $.noop,
|
|
_resize: $.noop,
|
|
_panEnd: function () {
|
|
this._applyExtent();
|
|
},
|
|
_applyExtent: function () {
|
|
var options = this.options;
|
|
var zoom = this.map.zoom();
|
|
var matchMinZoom = !defined(options.minZoom) || zoom >= options.minZoom;
|
|
var matchMaxZoom = !defined(options.maxZoom) || zoom <= options.maxZoom;
|
|
var extent = Extent.create(options.extent);
|
|
var inside = !extent || extent.overlaps(this.map.extent());
|
|
this._setVisibility(matchMinZoom && matchMaxZoom && inside);
|
|
},
|
|
_setVisibility: function (visible) {
|
|
this.element.css('display', visible ? '' : 'none');
|
|
},
|
|
_activate: function () {
|
|
var map = this.map;
|
|
map.bind('beforeReset', this._beforeReset);
|
|
map.bind('reset', this._reset);
|
|
map.bind('resize', this._resize);
|
|
map.bind('panEnd', this._panEnd);
|
|
},
|
|
_deactivate: function () {
|
|
var map = this.map;
|
|
map.unbind('beforeReset', this._beforeReset);
|
|
map.unbind('reset', this._reset);
|
|
map.unbind('resize', this._resize);
|
|
map.unbind('panEnd', this._panEnd);
|
|
},
|
|
_updateAttribution: function () {
|
|
var attr = this.map.attribution;
|
|
if (attr) {
|
|
attr.add(this.options.attribution);
|
|
}
|
|
}
|
|
});
|
|
deepExtend(dataviz, { map: { layers: { Layer: Layer } } });
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/shape', [
|
|
'dataviz/map/layers/base',
|
|
'dataviz/map/location'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, DataSource = kendo.data.DataSource, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, last = kendo.util.last, defined = kendo.util.defined, g = kendo.geometry, d = kendo.drawing, Group = d.Group, map = dataviz.map, Location = map.Location, Layer = map.layers.Layer;
|
|
var ShapeLayer = Layer.extend({
|
|
init: function (map, options) {
|
|
Layer.fn.init.call(this, map, options);
|
|
this.surface = d.Surface.create(this.element, {
|
|
width: map.scrollElement.width(),
|
|
height: map.scrollElement.height()
|
|
});
|
|
this._initRoot();
|
|
this.movable = new kendo.ui.Movable(this.surface.element);
|
|
this._markers = [];
|
|
this._click = this._handler('shapeClick');
|
|
this.surface.bind('click', this._click);
|
|
this._mouseenter = this._handler('shapeMouseEnter');
|
|
this.surface.bind('mouseenter', this._mouseenter);
|
|
this._mouseleave = this._handler('shapeMouseLeave');
|
|
this.surface.bind('mouseleave', this._mouseleave);
|
|
this._initDataSource();
|
|
},
|
|
options: { autoBind: true },
|
|
destroy: function () {
|
|
Layer.fn.destroy.call(this);
|
|
this.surface.destroy();
|
|
this.dataSource.unbind('change', this._dataChange);
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
if (this.dataSource) {
|
|
this.dataSource.unbind('change', this._dataChange);
|
|
}
|
|
this.dataSource = kendo.data.DataSource.create(dataSource);
|
|
this.dataSource.bind('change', this._dataChange);
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
_reset: function () {
|
|
Layer.fn._reset.call(this);
|
|
this._translateSurface();
|
|
if (this._data) {
|
|
this._load(this._data);
|
|
}
|
|
},
|
|
_initRoot: function () {
|
|
this._root = new Group();
|
|
this.surface.draw(this._root);
|
|
},
|
|
_beforeReset: function () {
|
|
this.surface.clear();
|
|
this._initRoot();
|
|
},
|
|
_resize: function () {
|
|
this.surface.size(this.map.size());
|
|
},
|
|
_initDataSource: function () {
|
|
var dsOptions = this.options.dataSource;
|
|
this._dataChange = proxy(this._dataChange, this);
|
|
this.dataSource = DataSource.create(dsOptions).bind('change', this._dataChange);
|
|
if (dsOptions && this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
_dataChange: function (e) {
|
|
this._data = e.sender.view();
|
|
this._load(this._data);
|
|
},
|
|
_load: function (data) {
|
|
this._clearMarkers();
|
|
if (!this._loader) {
|
|
this._loader = new GeoJSONLoader(this.map, this.options.style, this);
|
|
}
|
|
var container = new Group();
|
|
for (var i = 0; i < data.length; i++) {
|
|
var shape = this._loader.parse(data[i]);
|
|
if (shape) {
|
|
container.append(shape);
|
|
}
|
|
}
|
|
this._root.clear();
|
|
this._root.append(container);
|
|
},
|
|
shapeCreated: function (shape) {
|
|
var cancelled = false;
|
|
if (shape instanceof d.Circle) {
|
|
cancelled = defined(this._createMarker(shape));
|
|
}
|
|
if (!cancelled) {
|
|
var args = {
|
|
layer: this,
|
|
shape: shape
|
|
};
|
|
cancelled = this.map.trigger('shapeCreated', args);
|
|
}
|
|
return cancelled;
|
|
},
|
|
_createMarker: function (shape) {
|
|
var marker = this.map.markers.bind({ location: shape.location }, shape.dataItem);
|
|
if (marker) {
|
|
this._markers.push(marker);
|
|
}
|
|
return marker;
|
|
},
|
|
_clearMarkers: function () {
|
|
for (var i = 0; i < this._markers.length; i++) {
|
|
this.map.markers.remove(this._markers[i]);
|
|
}
|
|
this._markers = [];
|
|
},
|
|
_panEnd: function (e) {
|
|
Layer.fn._panEnd.call(this, e);
|
|
this._translateSurface();
|
|
},
|
|
_translateSurface: function () {
|
|
var map = this.map;
|
|
var nw = map.locationToView(map.extent().nw);
|
|
if (this.surface.translate) {
|
|
this.surface.translate(nw);
|
|
this.movable.moveTo({
|
|
x: nw.x,
|
|
y: nw.y
|
|
});
|
|
}
|
|
},
|
|
_handler: function (event) {
|
|
var layer = this;
|
|
return function (e) {
|
|
if (e.element) {
|
|
var args = {
|
|
layer: layer,
|
|
shape: e.element,
|
|
originalEvent: e.originalEvent
|
|
};
|
|
layer.map.trigger(event, args);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
var GeoJSONLoader = Class.extend({
|
|
init: function (locator, defaultStyle, observer) {
|
|
this.observer = observer;
|
|
this.locator = locator;
|
|
this.style = defaultStyle;
|
|
},
|
|
parse: function (item) {
|
|
var root = new Group();
|
|
if (item.type === 'Feature') {
|
|
this._loadGeometryTo(root, item.geometry, item);
|
|
} else {
|
|
this._loadGeometryTo(root, item, item);
|
|
}
|
|
if (root.children.length < 2) {
|
|
root = root.children[0];
|
|
}
|
|
return root;
|
|
},
|
|
_shapeCreated: function (shape) {
|
|
var cancelled = false;
|
|
if (this.observer && this.observer.shapeCreated) {
|
|
cancelled = this.observer.shapeCreated(shape);
|
|
}
|
|
return cancelled;
|
|
},
|
|
_loadGeometryTo: function (container, geometry, dataItem) {
|
|
var coords = geometry.coordinates;
|
|
var i;
|
|
var path;
|
|
switch (geometry.type) {
|
|
case 'LineString':
|
|
path = this._loadPolygon(container, [coords], dataItem);
|
|
this._setLineFill(path);
|
|
break;
|
|
case 'MultiLineString':
|
|
for (i = 0; i < coords.length; i++) {
|
|
path = this._loadPolygon(container, [coords[i]], dataItem);
|
|
this._setLineFill(path);
|
|
}
|
|
break;
|
|
case 'Polygon':
|
|
this._loadPolygon(container, coords, dataItem);
|
|
break;
|
|
case 'MultiPolygon':
|
|
for (i = 0; i < coords.length; i++) {
|
|
this._loadPolygon(container, coords[i], dataItem);
|
|
}
|
|
break;
|
|
case 'Point':
|
|
this._loadPoint(container, coords, dataItem);
|
|
break;
|
|
case 'MultiPoint':
|
|
for (i = 0; i < coords.length; i++) {
|
|
this._loadPoint(container, coords[i], dataItem);
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
_setLineFill: function (path) {
|
|
var segments = path.segments;
|
|
if (segments.length < 4 || !segments[0].anchor().equals(last(segments).anchor())) {
|
|
path.options.fill = null;
|
|
}
|
|
},
|
|
_loadShape: function (container, shape) {
|
|
if (!this._shapeCreated(shape)) {
|
|
container.append(shape);
|
|
}
|
|
return shape;
|
|
},
|
|
_loadPolygon: function (container, rings, dataItem) {
|
|
var shape = this._buildPolygon(rings);
|
|
shape.dataItem = dataItem;
|
|
return this._loadShape(container, shape);
|
|
},
|
|
_buildPolygon: function (rings) {
|
|
var type = rings.length > 1 ? d.MultiPath : d.Path;
|
|
var path = new type(this.style);
|
|
for (var i = 0; i < rings.length; i++) {
|
|
for (var j = 0; j < rings[i].length; j++) {
|
|
var point = this.locator.locationToView(Location.fromLngLat(rings[i][j]));
|
|
if (j === 0) {
|
|
path.moveTo(point.x, point.y);
|
|
} else {
|
|
path.lineTo(point.x, point.y);
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
},
|
|
_loadPoint: function (container, coords, dataItem) {
|
|
var location = Location.fromLngLat(coords);
|
|
var point = this.locator.locationToView(location);
|
|
var circle = new g.Circle(point, 10);
|
|
var shape = new d.Circle(circle, this.style);
|
|
shape.dataItem = dataItem;
|
|
shape.location = location;
|
|
return this._loadShape(container, shape);
|
|
}
|
|
});
|
|
deepExtend(kendo.data, {
|
|
schemas: {
|
|
geojson: {
|
|
type: 'json',
|
|
data: function (data) {
|
|
if (data.type === 'FeatureCollection') {
|
|
return data.features;
|
|
}
|
|
if (data.type === 'GeometryCollection') {
|
|
return data.geometries;
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
},
|
|
transports: { geojson: { read: { dataType: 'json' } } }
|
|
});
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
layers: {
|
|
shape: ShapeLayer,
|
|
ShapeLayer: ShapeLayer
|
|
},
|
|
GeoJSONLoader: GeoJSONLoader
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/bubble', ['dataviz/map/layers/shape'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, getter = kendo.getter, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, util = kendo.util, defined = util.defined, g = kendo.geometry, d = kendo.drawing, map = dataviz.map, Location = map.Location, ShapeLayer = map.layers.ShapeLayer;
|
|
var BubbleLayer = ShapeLayer.extend({
|
|
options: {
|
|
autoBind: true,
|
|
locationField: 'location',
|
|
valueField: 'value',
|
|
minSize: 0,
|
|
maxSize: 100,
|
|
scale: 'sqrt',
|
|
symbol: 'circle'
|
|
},
|
|
_load: function (data) {
|
|
this.surface.clear();
|
|
if (data.length === 0) {
|
|
return;
|
|
}
|
|
var opt = this.options;
|
|
var getValue = getter(opt.valueField);
|
|
data = data.slice(0);
|
|
data.sort(function (a, b) {
|
|
return getValue(b) - getValue(a);
|
|
});
|
|
var scaleType = this._scaleType();
|
|
var scale;
|
|
for (var i = 0; i < data.length; i++) {
|
|
var dataItem = data[i];
|
|
var location = getter(opt.locationField)(dataItem);
|
|
var value = getter(opt.valueField)(dataItem);
|
|
if (defined(location) && defined(value)) {
|
|
if (!scale) {
|
|
scale = new scaleType([
|
|
0,
|
|
value
|
|
], [
|
|
opt.minSize,
|
|
opt.maxSize
|
|
]);
|
|
}
|
|
location = Location.create(location);
|
|
var center = this.map.locationToView(location);
|
|
var size = scale.map(value);
|
|
var symbol = this._createSymbol({
|
|
center: center,
|
|
size: size,
|
|
style: opt.style,
|
|
dataItem: dataItem,
|
|
location: location
|
|
});
|
|
symbol.dataItem = dataItem;
|
|
symbol.location = location;
|
|
symbol.value = value;
|
|
this._drawSymbol(symbol);
|
|
}
|
|
}
|
|
},
|
|
_scaleType: function () {
|
|
var scale = this.options.scale;
|
|
if (kendo.isFunction(scale)) {
|
|
return scale;
|
|
}
|
|
return dataviz.map.scales[scale];
|
|
},
|
|
_createSymbol: function (args) {
|
|
var symbol = this.options.symbol;
|
|
if (!kendo.isFunction(symbol)) {
|
|
symbol = dataviz.map.symbols[symbol];
|
|
}
|
|
return symbol(args);
|
|
},
|
|
_drawSymbol: function (shape) {
|
|
var args = {
|
|
layer: this,
|
|
shape: shape
|
|
};
|
|
var cancelled = this.map.trigger('shapeCreated', args);
|
|
if (!cancelled) {
|
|
this.surface.draw(shape);
|
|
}
|
|
}
|
|
});
|
|
var SqrtScale = kendo.Class.extend({
|
|
init: function (domain, range) {
|
|
this._domain = domain;
|
|
this._range = range;
|
|
var domainRange = Math.sqrt(domain[1]) - Math.sqrt(domain[0]);
|
|
var outputRange = range[1] - range[0];
|
|
this._ratio = outputRange / domainRange;
|
|
},
|
|
map: function (value) {
|
|
var rel = (Math.sqrt(value) - Math.sqrt(this._domain[0])) * this._ratio;
|
|
return this._range[0] + rel;
|
|
}
|
|
});
|
|
var Symbols = {
|
|
circle: function (args) {
|
|
var geo = new g.Circle(args.center, args.size / 2);
|
|
return new d.Circle(geo, args.style);
|
|
},
|
|
square: function (args) {
|
|
var path = new d.Path(args.style);
|
|
var halfSize = args.size / 2;
|
|
var center = args.center;
|
|
path.moveTo(center.x - halfSize, center.y - halfSize).lineTo(center.x + halfSize, center.y - halfSize).lineTo(center.x + halfSize, center.y + halfSize).lineTo(center.x - halfSize, center.y + halfSize).close();
|
|
return path;
|
|
}
|
|
};
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
layers: {
|
|
bubble: BubbleLayer,
|
|
BubbleLayer: BubbleLayer
|
|
},
|
|
scales: { sqrt: SqrtScale },
|
|
symbols: Symbols
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/tile', [
|
|
'dataviz/map/layers/base',
|
|
'dataviz/map/location'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var math = Math, proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, template = kendo.template, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, g = kendo.geometry, Point = g.Point, Layer = dataviz.map.layers.Layer, util = kendo.util, round = util.round, renderSize = util.renderSize, limit = util.limitValue;
|
|
var TileLayer = Layer.extend({
|
|
init: function (map, options) {
|
|
Layer.fn.init.call(this, map, options);
|
|
if (typeof this.options.subdomains === 'string') {
|
|
this.options.subdomains = this.options.subdomains.split('');
|
|
}
|
|
var viewType = this._viewType();
|
|
this._view = new viewType(this.element, this.options);
|
|
},
|
|
destroy: function () {
|
|
Layer.fn.destroy.call(this);
|
|
this._view.destroy();
|
|
this._view = null;
|
|
},
|
|
_beforeReset: function () {
|
|
var map = this.map;
|
|
var origin = map.locationToLayer(map.extent().nw).round();
|
|
this._view.viewOrigin(origin);
|
|
},
|
|
_reset: function () {
|
|
Layer.fn._reset.call(this);
|
|
this._updateView();
|
|
this._view.reset();
|
|
},
|
|
_viewType: function () {
|
|
return TileView;
|
|
},
|
|
_activate: function () {
|
|
Layer.fn._activate.call(this);
|
|
if (!kendo.support.mobileOS) {
|
|
if (!this._pan) {
|
|
this._pan = kendo.throttle(proxy(this._render, this), 100);
|
|
}
|
|
this.map.bind('pan', this._pan);
|
|
}
|
|
},
|
|
_deactivate: function () {
|
|
Layer.fn._deactivate.call(this);
|
|
if (this._pan) {
|
|
this.map.unbind('pan', this._pan);
|
|
}
|
|
},
|
|
_updateView: function () {
|
|
var view = this._view, map = this.map, extent = map.extent(), extentToPoint = {
|
|
nw: map.locationToLayer(extent.nw).round(),
|
|
se: map.locationToLayer(extent.se).round()
|
|
};
|
|
view.center(map.locationToLayer(map.center()));
|
|
view.extent(extentToPoint);
|
|
view.zoom(map.zoom());
|
|
},
|
|
_resize: function () {
|
|
this._render();
|
|
},
|
|
_panEnd: function (e) {
|
|
Layer.fn._panEnd.call(this, e);
|
|
this._render();
|
|
},
|
|
_render: function () {
|
|
this._updateView();
|
|
this._view.render();
|
|
}
|
|
});
|
|
var TileView = Class.extend({
|
|
init: function (element, options) {
|
|
this.element = element;
|
|
this._initOptions(options);
|
|
this.pool = new TilePool();
|
|
},
|
|
options: {
|
|
tileSize: 256,
|
|
subdomains: [
|
|
'a',
|
|
'b',
|
|
'c'
|
|
],
|
|
urlTemplate: ''
|
|
},
|
|
center: function (center) {
|
|
this._center = center;
|
|
},
|
|
extent: function (extent) {
|
|
this._extent = extent;
|
|
},
|
|
viewOrigin: function (origin) {
|
|
this._viewOrigin = origin;
|
|
},
|
|
zoom: function (zoom) {
|
|
this._zoom = zoom;
|
|
},
|
|
pointToTileIndex: function (point) {
|
|
return new Point(math.floor(point.x / this.options.tileSize), math.floor(point.y / this.options.tileSize));
|
|
},
|
|
tileCount: function () {
|
|
var size = this.size(), firstTileIndex = this.pointToTileIndex(this._extent.nw), nw = this._extent.nw, point = this.indexToPoint(firstTileIndex).translate(-nw.x, -nw.y);
|
|
return {
|
|
x: math.ceil((math.abs(point.x) + size.width) / this.options.tileSize),
|
|
y: math.ceil((math.abs(point.y) + size.height) / this.options.tileSize)
|
|
};
|
|
},
|
|
size: function () {
|
|
var nw = this._extent.nw, se = this._extent.se, diff = se.clone().translate(-nw.x, -nw.y);
|
|
return {
|
|
width: diff.x,
|
|
height: diff.y
|
|
};
|
|
},
|
|
indexToPoint: function (index) {
|
|
var x = index.x, y = index.y;
|
|
return new Point(x * this.options.tileSize, y * this.options.tileSize);
|
|
},
|
|
subdomainText: function () {
|
|
var subdomains = this.options.subdomains;
|
|
return subdomains[this.subdomainIndex++ % subdomains.length];
|
|
},
|
|
destroy: function () {
|
|
this.element.empty();
|
|
this.pool.empty();
|
|
},
|
|
reset: function () {
|
|
this.pool.reset();
|
|
this.subdomainIndex = 0;
|
|
this.render();
|
|
},
|
|
render: function () {
|
|
var size = this.tileCount(), firstTileIndex = this.pointToTileIndex(this._extent.nw), tile, x, y;
|
|
for (x = 0; x < size.x; x++) {
|
|
for (y = 0; y < size.y; y++) {
|
|
tile = this.createTile({
|
|
x: firstTileIndex.x + x,
|
|
y: firstTileIndex.y + y
|
|
});
|
|
if (!tile.visible) {
|
|
tile.show();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
createTile: function (currentIndex) {
|
|
var options = this.tileOptions(currentIndex);
|
|
var tile = this.pool.get(this._center, options);
|
|
if (tile.element.parent().length === 0) {
|
|
this.element.append(tile.element);
|
|
}
|
|
return tile;
|
|
},
|
|
tileOptions: function (currentIndex) {
|
|
var index = this.wrapIndex(currentIndex), point = this.indexToPoint(currentIndex), origin = this._viewOrigin, offset = point.clone().translate(-origin.x, -origin.y);
|
|
return {
|
|
index: index,
|
|
currentIndex: currentIndex,
|
|
point: point,
|
|
offset: roundPoint(offset),
|
|
zoom: this._zoom,
|
|
size: this.options.tileSize,
|
|
subdomain: this.subdomainText(),
|
|
urlTemplate: this.options.urlTemplate,
|
|
errorUrlTemplate: this.options.errorUrlTemplate
|
|
};
|
|
},
|
|
wrapIndex: function (index) {
|
|
var boundary = math.pow(2, this._zoom);
|
|
return {
|
|
x: this.wrapValue(index.x, boundary),
|
|
y: limit(index.y, 0, boundary - 1)
|
|
};
|
|
},
|
|
wrapValue: function (value, boundary) {
|
|
var remainder = math.abs(value) % boundary;
|
|
if (value >= 0) {
|
|
value = remainder;
|
|
} else {
|
|
value = boundary - (remainder === 0 ? boundary : remainder);
|
|
}
|
|
return value;
|
|
}
|
|
});
|
|
var ImageTile = Class.extend({
|
|
init: function (id, options) {
|
|
this.id = id;
|
|
this.visible = true;
|
|
this._initOptions(options);
|
|
this.createElement();
|
|
this.show();
|
|
},
|
|
options: {
|
|
urlTemplate: '',
|
|
errorUrlTemplate: ''
|
|
},
|
|
createElement: function () {
|
|
this.element = $('<img style=\'position: absolute; display: block;\' />').css({
|
|
width: this.options.size,
|
|
height: this.options.size
|
|
}).error(proxy(function (e) {
|
|
if (this.errorUrl()) {
|
|
e.target.setAttribute('src', this.errorUrl());
|
|
} else {
|
|
e.target.removeAttribute('src');
|
|
}
|
|
}, this));
|
|
},
|
|
show: function () {
|
|
var element = this.element[0];
|
|
element.style.top = renderSize(this.options.offset.y);
|
|
element.style.left = renderSize(this.options.offset.x);
|
|
var url = this.url();
|
|
if (url) {
|
|
element.setAttribute('src', url);
|
|
}
|
|
element.style.visibility = 'visible';
|
|
this.visible = true;
|
|
},
|
|
hide: function () {
|
|
this.element[0].style.visibility = 'hidden';
|
|
this.visible = false;
|
|
},
|
|
url: function () {
|
|
var urlResult = template(this.options.urlTemplate);
|
|
return urlResult(this.urlOptions());
|
|
},
|
|
errorUrl: function () {
|
|
var urlResult = template(this.options.errorUrlTemplate);
|
|
return urlResult(this.urlOptions());
|
|
},
|
|
urlOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
zoom: options.zoom,
|
|
subdomain: options.subdomain,
|
|
z: options.zoom,
|
|
x: options.index.x,
|
|
y: options.index.y,
|
|
s: options.subdomain,
|
|
quadkey: options.quadkey,
|
|
q: options.quadkey,
|
|
culture: options.culture,
|
|
c: options.culture
|
|
};
|
|
},
|
|
destroy: function () {
|
|
if (this.element) {
|
|
this.element.remove();
|
|
this.element = null;
|
|
}
|
|
}
|
|
});
|
|
var TilePool = Class.extend({
|
|
init: function () {
|
|
this._items = [];
|
|
},
|
|
options: { maxSize: 100 },
|
|
get: function (center, options) {
|
|
if (this._items.length >= this.options.maxSize) {
|
|
this._remove(center);
|
|
}
|
|
return this._create(options);
|
|
},
|
|
empty: function () {
|
|
var items = this._items;
|
|
for (var i = 0; i < items.length; i++) {
|
|
items[i].destroy();
|
|
}
|
|
this._items = [];
|
|
},
|
|
reset: function () {
|
|
var items = this._items;
|
|
for (var i = 0; i < items.length; i++) {
|
|
items[i].hide();
|
|
}
|
|
},
|
|
_create: function (options) {
|
|
var items = this._items;
|
|
var tile;
|
|
var id = util.hashKey(options.point.toString() + options.offset.toString() + options.zoom + options.urlTemplate);
|
|
for (var i = 0; i < items.length; i++) {
|
|
if (items[i].id === id) {
|
|
tile = items[i];
|
|
break;
|
|
}
|
|
}
|
|
if (tile) {
|
|
tile.show();
|
|
} else {
|
|
tile = new ImageTile(id, options);
|
|
this._items.push(tile);
|
|
}
|
|
return tile;
|
|
},
|
|
_remove: function (center) {
|
|
var items = this._items;
|
|
var maxDist = -1;
|
|
var index = -1;
|
|
for (var i = 0; i < items.length; i++) {
|
|
var dist = items[i].options.point.distanceTo(center);
|
|
if (dist > maxDist && !items[i].visible) {
|
|
index = i;
|
|
maxDist = dist;
|
|
}
|
|
}
|
|
if (index !== -1) {
|
|
items[index].destroy();
|
|
items.splice(index, 1);
|
|
}
|
|
}
|
|
});
|
|
function roundPoint(point) {
|
|
return new Point(round(point.x), round(point.y));
|
|
}
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
layers: {
|
|
tile: TileLayer,
|
|
TileLayer: TileLayer,
|
|
ImageTile: ImageTile,
|
|
TilePool: TilePool,
|
|
TileView: TileView
|
|
}
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/bing', ['dataviz/map/layers/tile'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, defined = kendo.util.defined, Extent = dataviz.map.Extent, Location = dataviz.map.Location, TileLayer = dataviz.map.layers.TileLayer, TileView = dataviz.map.layers.TileView;
|
|
var BingLayer = TileLayer.extend({
|
|
init: function (map, options) {
|
|
TileLayer.fn.init.call(this, map, options);
|
|
this._onMetadata = $.proxy(this._onMetadata, this);
|
|
this._fetchMetadata();
|
|
},
|
|
options: {
|
|
baseUrl: '//dev.virtualearth.net/REST/v1/Imagery/Metadata/',
|
|
imagerySet: 'road'
|
|
},
|
|
_fetchMetadata: function () {
|
|
var options = this.options;
|
|
if (!options.key) {
|
|
throw new Error('Bing tile layer: API key is required');
|
|
}
|
|
$.ajax({
|
|
url: options.baseUrl + options.imagerySet,
|
|
data: {
|
|
output: 'json',
|
|
include: 'ImageryProviders',
|
|
key: options.key,
|
|
uriScheme: this._scheme(window.location.protocol)
|
|
},
|
|
type: 'get',
|
|
dataType: 'jsonp',
|
|
jsonp: 'jsonp',
|
|
success: this._onMetadata
|
|
});
|
|
},
|
|
_scheme: function (proto) {
|
|
return proto.replace(':', '') === 'https' ? 'https' : 'http';
|
|
},
|
|
_onMetadata: function (data) {
|
|
if (data && data.resourceSets.length) {
|
|
var resource = this.resource = data.resourceSets[0].resources[0];
|
|
deepExtend(this._view.options, {
|
|
urlTemplate: resource.imageUrl.replace('{subdomain}', '#= subdomain #').replace('{quadkey}', '#= quadkey #').replace('{culture}', '#= culture #'),
|
|
subdomains: resource.imageUrlSubdomains
|
|
});
|
|
var options = this.options;
|
|
if (!defined(options.minZoom)) {
|
|
options.minZoom = resource.zoomMin;
|
|
}
|
|
if (!defined(options.maxZoom)) {
|
|
options.maxZoom = resource.zoomMax;
|
|
}
|
|
this._addAttribution();
|
|
if (this.element.css('display') !== 'none') {
|
|
this._reset();
|
|
}
|
|
}
|
|
},
|
|
_viewType: function () {
|
|
return BingView;
|
|
},
|
|
_addAttribution: function () {
|
|
var attr = this.map.attribution;
|
|
if (attr) {
|
|
var items = this.resource.imageryProviders;
|
|
if (items) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
var item = items[i];
|
|
for (var y = 0; y < item.coverageAreas.length; y++) {
|
|
var area = item.coverageAreas[y];
|
|
attr.add({
|
|
text: item.attribution,
|
|
minZoom: area.zoomMin,
|
|
maxZoom: area.zoomMax,
|
|
extent: new Extent(new Location(area.bbox[2], area.bbox[1]), new Location(area.bbox[0], area.bbox[3]))
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
imagerySet: function (value) {
|
|
if (value) {
|
|
this.options.imagerySet = value;
|
|
this.map.attribution.clear();
|
|
this._fetchMetadata();
|
|
} else {
|
|
return this.options.imagerySet;
|
|
}
|
|
}
|
|
});
|
|
var BingView = TileView.extend({
|
|
options: { culture: 'en-US' },
|
|
tileOptions: function (currentIndex) {
|
|
var options = TileView.fn.tileOptions.call(this, currentIndex);
|
|
options.culture = this.options.culture;
|
|
options.quadkey = this.tileQuadKey(this.wrapIndex(currentIndex));
|
|
return options;
|
|
},
|
|
tileQuadKey: function (index) {
|
|
var quadKey = '', digit, mask, i;
|
|
for (i = this._zoom; i > 0; i--) {
|
|
digit = 0;
|
|
mask = 1 << i - 1;
|
|
if ((index.x & mask) !== 0) {
|
|
digit++;
|
|
}
|
|
if ((index.y & mask) !== 0) {
|
|
digit += 2;
|
|
}
|
|
quadKey += digit;
|
|
}
|
|
return quadKey;
|
|
}
|
|
});
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
layers: {
|
|
bing: BingLayer,
|
|
BingLayer: BingLayer,
|
|
BingView: BingView
|
|
}
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/layers/marker', [
|
|
'dataviz/map/layers/base',
|
|
'dataviz/map/location',
|
|
'kendo.data',
|
|
'kendo.tooltip'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var doc = document, math = Math, indexOf = $.inArray, proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, DataSource = kendo.data.DataSource, Tooltip = kendo.ui.Tooltip, dataviz = kendo.dataviz, deepExtend = kendo.deepExtend, map = dataviz.map, Location = map.Location, Layer = map.layers.Layer;
|
|
var MarkerLayer = Layer.extend({
|
|
init: function (map, options) {
|
|
Layer.fn.init.call(this, map, options);
|
|
this._markerClick = proxy(this._markerClick, this);
|
|
this.element.on('click', '.k-marker', this._markerClick);
|
|
this.items = [];
|
|
this._initDataSource();
|
|
},
|
|
destroy: function () {
|
|
Layer.fn.destroy.call(this);
|
|
this.element.off('click', '.k-marker', this._markerClick);
|
|
this.dataSource.unbind('change', this._dataChange);
|
|
this.clear();
|
|
},
|
|
options: {
|
|
zIndex: 1000,
|
|
autoBind: true,
|
|
dataSource: {},
|
|
locationField: 'location',
|
|
titleField: 'title'
|
|
},
|
|
add: function (arg) {
|
|
if ($.isArray(arg)) {
|
|
for (var i = 0; i < arg.length; i++) {
|
|
this._addOne(arg[i]);
|
|
}
|
|
} else {
|
|
return this._addOne(arg);
|
|
}
|
|
},
|
|
remove: function (marker) {
|
|
marker.destroy();
|
|
var index = indexOf(marker, this.items);
|
|
if (index > -1) {
|
|
this.items.splice(index, 1);
|
|
}
|
|
},
|
|
clear: function () {
|
|
for (var i = 0; i < this.items.length; i++) {
|
|
this.items[i].destroy();
|
|
}
|
|
this.items = [];
|
|
},
|
|
update: function (marker) {
|
|
var loc = marker.location();
|
|
if (loc) {
|
|
marker.showAt(this.map.locationToView(loc));
|
|
var args = {
|
|
marker: marker,
|
|
layer: this
|
|
};
|
|
this.map.trigger('markerActivate', args);
|
|
}
|
|
},
|
|
_reset: function () {
|
|
Layer.fn._reset.call(this);
|
|
var items = this.items;
|
|
for (var i = 0; i < items.length; i++) {
|
|
this.update(items[i]);
|
|
}
|
|
},
|
|
bind: function (options, dataItem) {
|
|
var marker = map.Marker.create(options, this.options);
|
|
marker.dataItem = dataItem;
|
|
var args = {
|
|
marker: marker,
|
|
layer: this
|
|
};
|
|
var cancelled = this.map.trigger('markerCreated', args);
|
|
if (!cancelled) {
|
|
this.add(marker);
|
|
return marker;
|
|
}
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
if (this.dataSource) {
|
|
this.dataSource.unbind('change', this._dataChange);
|
|
}
|
|
this.dataSource = kendo.data.DataSource.create(dataSource);
|
|
this.dataSource.bind('change', this._dataChange);
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
_addOne: function (arg) {
|
|
var marker = Marker.create(arg, this.options);
|
|
marker.addTo(this);
|
|
return marker;
|
|
},
|
|
_initDataSource: function () {
|
|
var dsOptions = this.options.dataSource;
|
|
this._dataChange = proxy(this._dataChange, this);
|
|
this.dataSource = DataSource.create(dsOptions).bind('change', this._dataChange);
|
|
if (dsOptions && this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
_dataChange: function (e) {
|
|
this._load(e.sender.view());
|
|
},
|
|
_load: function (data) {
|
|
this._data = data;
|
|
this.clear();
|
|
var getLocation = kendo.getter(this.options.locationField);
|
|
var getTitle = kendo.getter(this.options.titleField);
|
|
for (var i = 0; i < data.length; i++) {
|
|
var dataItem = data[i];
|
|
this.bind({
|
|
location: getLocation(dataItem),
|
|
title: getTitle(dataItem)
|
|
}, dataItem);
|
|
}
|
|
},
|
|
_markerClick: function (e) {
|
|
var args = {
|
|
marker: $(e.target).data('kendoMarker'),
|
|
layer: this
|
|
};
|
|
this.map.trigger('markerClick', args);
|
|
}
|
|
});
|
|
var Marker = Class.extend({
|
|
init: function (options) {
|
|
this.options = options || {};
|
|
},
|
|
addTo: function (parent) {
|
|
this.layer = parent.markers || parent;
|
|
this.layer.items.push(this);
|
|
this.layer.update(this);
|
|
},
|
|
location: function (value) {
|
|
if (value) {
|
|
this.options.location = Location.create(value).toArray();
|
|
if (this.layer) {
|
|
this.layer.update(this);
|
|
}
|
|
return this;
|
|
} else {
|
|
return Location.create(this.options.location);
|
|
}
|
|
},
|
|
showAt: function (point) {
|
|
this.render();
|
|
this.element.css({
|
|
left: math.round(point.x),
|
|
top: math.round(point.y)
|
|
});
|
|
if (this.tooltip && this.tooltip.popup) {
|
|
this.tooltip.popup._position();
|
|
}
|
|
},
|
|
hide: function () {
|
|
if (this.element) {
|
|
this.element.remove();
|
|
this.element = null;
|
|
}
|
|
if (this.tooltip) {
|
|
this.tooltip.destroy();
|
|
this.tooltip = null;
|
|
}
|
|
},
|
|
destroy: function () {
|
|
this.layer = null;
|
|
this.hide();
|
|
},
|
|
render: function () {
|
|
if (!this.element) {
|
|
var options = this.options;
|
|
var layer = this.layer;
|
|
this.element = $(doc.createElement('span')).addClass('k-marker k-marker-' + kendo.toHyphens(options.shape || 'pin')).attr('title', options.title).attr(options.attributes || {}).data('kendoMarker', this).css('zIndex', options.zIndex);
|
|
if (layer) {
|
|
layer.element.append(this.element);
|
|
}
|
|
this.renderTooltip();
|
|
}
|
|
},
|
|
renderTooltip: function () {
|
|
var marker = this;
|
|
var title = marker.options.title;
|
|
var options = marker.options.tooltip || {};
|
|
if (options && Tooltip) {
|
|
var template = options.template;
|
|
if (template) {
|
|
var contentTemplate = kendo.template(template);
|
|
options.content = function (e) {
|
|
e.location = marker.location();
|
|
e.marker = marker;
|
|
return contentTemplate(e);
|
|
};
|
|
}
|
|
if (title || options.content || options.contentUrl) {
|
|
this.tooltip = new Tooltip(this.element, options);
|
|
this.tooltip.marker = this;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
Marker.create = function (arg, defaults) {
|
|
if (arg instanceof Marker) {
|
|
return arg;
|
|
}
|
|
return new Marker(deepExtend({}, defaults, arg));
|
|
};
|
|
deepExtend(dataviz, {
|
|
map: {
|
|
layers: {
|
|
marker: MarkerLayer,
|
|
MarkerLayer: MarkerLayer
|
|
},
|
|
Marker: Marker
|
|
}
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/map/main', [
|
|
'dataviz/map/crs',
|
|
'dataviz/map/location'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var doc = document, math = Math, min = math.min, pow = math.pow, proxy = $.proxy, kendo = window.kendo, Widget = kendo.ui.Widget, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, ui = dataviz.ui, g = kendo.geometry, Point = g.Point, map = dataviz.map, Extent = map.Extent, Location = map.Location, EPSG3857 = map.crs.EPSG3857, util = kendo.util, defined = util.defined, limit = util.limitValue, renderPos = util.renderPos, valueOrDefault = util.valueOrDefault;
|
|
var CSS_PREFIX = 'k-', FRICTION = 0.9, FRICTION_MOBILE = 0.93, MOUSEWHEEL = 'DOMMouseScroll mousewheel', VELOCITY_MULTIPLIER = 5;
|
|
var Map = Widget.extend({
|
|
init: function (element, options) {
|
|
kendo.destroy(element);
|
|
Widget.fn.init.call(this, element);
|
|
this._initOptions(options);
|
|
this.bind(this.events, options);
|
|
this.crs = new EPSG3857();
|
|
this.element.addClass(CSS_PREFIX + this.options.name.toLowerCase()).css('position', 'relative').empty().append(doc.createElement('div'));
|
|
this._viewOrigin = this._getOrigin();
|
|
this._initScroller();
|
|
this._initMarkers();
|
|
this._initControls();
|
|
this._initLayers();
|
|
this._reset();
|
|
this._mousewheel = proxy(this._mousewheel, this);
|
|
this.element.bind('click', proxy(this._click, this));
|
|
this.element.bind(MOUSEWHEEL, this._mousewheel);
|
|
},
|
|
options: {
|
|
name: 'Map',
|
|
controls: {
|
|
attribution: true,
|
|
navigator: { panStep: 100 },
|
|
zoom: true
|
|
},
|
|
layers: [],
|
|
layerDefaults: {
|
|
shape: {
|
|
style: {
|
|
fill: { color: '#fff' },
|
|
stroke: {
|
|
color: '#aaa',
|
|
width: 0.5
|
|
}
|
|
}
|
|
},
|
|
bubble: {
|
|
style: {
|
|
fill: {
|
|
color: '#fff',
|
|
opacity: 0.5
|
|
},
|
|
stroke: {
|
|
color: '#aaa',
|
|
width: 0.5
|
|
}
|
|
}
|
|
},
|
|
marker: {
|
|
shape: 'pinTarget',
|
|
tooltip: { position: 'top' }
|
|
}
|
|
},
|
|
center: [
|
|
0,
|
|
0
|
|
],
|
|
zoom: 3,
|
|
minSize: 256,
|
|
minZoom: 1,
|
|
maxZoom: 19,
|
|
markers: [],
|
|
markerDefaults: {
|
|
shape: 'pinTarget',
|
|
tooltip: { position: 'top' }
|
|
},
|
|
wraparound: true
|
|
},
|
|
events: [
|
|
'beforeReset',
|
|
'click',
|
|
'reset',
|
|
'pan',
|
|
'panEnd',
|
|
'markerActivate',
|
|
'markerClick',
|
|
'markerCreated',
|
|
'shapeClick',
|
|
'shapeCreated',
|
|
'shapeMouseEnter',
|
|
'shapeMouseLeave',
|
|
'zoomStart',
|
|
'zoomEnd'
|
|
],
|
|
destroy: function () {
|
|
this.scroller.destroy();
|
|
if (this.navigator) {
|
|
this.navigator.destroy();
|
|
}
|
|
if (this.attribution) {
|
|
this.attribution.destroy();
|
|
}
|
|
if (this.zoomControl) {
|
|
this.zoomControl.destroy();
|
|
}
|
|
this.markers.destroy();
|
|
for (var i = 0; i < this.layers.length; i++) {
|
|
this.layers[i].destroy();
|
|
}
|
|
Widget.fn.destroy.call(this);
|
|
},
|
|
zoom: function (level) {
|
|
var options = this.options;
|
|
if (defined(level)) {
|
|
level = math.round(limit(level, options.minZoom, options.maxZoom));
|
|
if (options.zoom !== level) {
|
|
options.zoom = level;
|
|
this._reset();
|
|
}
|
|
return this;
|
|
} else {
|
|
return options.zoom;
|
|
}
|
|
},
|
|
center: function (center) {
|
|
if (center) {
|
|
this.options.center = Location.create(center).toArray();
|
|
this._reset();
|
|
return this;
|
|
} else {
|
|
return Location.create(this.options.center);
|
|
}
|
|
},
|
|
extent: function (extent) {
|
|
if (extent) {
|
|
this._setExtent(extent);
|
|
return this;
|
|
} else {
|
|
return this._getExtent();
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
Widget.fn.setOptions.call(this, options);
|
|
this._reset();
|
|
},
|
|
locationToLayer: function (location, zoom) {
|
|
var clamp = !this.options.wraparound;
|
|
location = Location.create(location);
|
|
return this.crs.toPoint(location, this._layerSize(zoom), clamp);
|
|
},
|
|
layerToLocation: function (point, zoom) {
|
|
var clamp = !this.options.wraparound;
|
|
point = Point.create(point);
|
|
return this.crs.toLocation(point, this._layerSize(zoom), clamp);
|
|
},
|
|
locationToView: function (location) {
|
|
location = Location.create(location);
|
|
var origin = this.locationToLayer(this._viewOrigin);
|
|
var point = this.locationToLayer(location);
|
|
return point.translateWith(origin.scale(-1));
|
|
},
|
|
viewToLocation: function (point, zoom) {
|
|
var origin = this.locationToLayer(this._getOrigin(), zoom);
|
|
point = Point.create(point);
|
|
point = point.clone().translateWith(origin);
|
|
return this.layerToLocation(point, zoom);
|
|
},
|
|
eventOffset: function (e) {
|
|
var offset = this.element.offset();
|
|
var event = e.originalEvent || e;
|
|
var x = valueOrDefault(event.pageX, event.clientX) - offset.left;
|
|
var y = valueOrDefault(event.pageY, event.clientY) - offset.top;
|
|
return new g.Point(x, y);
|
|
},
|
|
eventToView: function (e) {
|
|
var cursor = this.eventOffset(e);
|
|
return this.locationToView(this.viewToLocation(cursor));
|
|
},
|
|
eventToLayer: function (e) {
|
|
return this.locationToLayer(this.eventToLocation(e));
|
|
},
|
|
eventToLocation: function (e) {
|
|
var cursor = this.eventOffset(e);
|
|
return this.viewToLocation(cursor);
|
|
},
|
|
viewSize: function () {
|
|
var element = this.element;
|
|
var scale = this._layerSize();
|
|
var width = element.width();
|
|
if (!this.options.wraparound) {
|
|
width = min(scale, width);
|
|
}
|
|
return {
|
|
width: width,
|
|
height: min(scale, element.height())
|
|
};
|
|
},
|
|
exportVisual: function () {
|
|
this._reset();
|
|
return false;
|
|
},
|
|
_setOrigin: function (origin, zoom) {
|
|
var size = this.viewSize(), topLeft;
|
|
origin = this._origin = Location.create(origin);
|
|
topLeft = this.locationToLayer(origin, zoom);
|
|
topLeft.x += size.width / 2;
|
|
topLeft.y += size.height / 2;
|
|
this.options.center = this.layerToLocation(topLeft, zoom).toArray();
|
|
return this;
|
|
},
|
|
_getOrigin: function (invalidate) {
|
|
var size = this.viewSize(), topLeft;
|
|
if (invalidate || !this._origin) {
|
|
topLeft = this.locationToLayer(this.center());
|
|
topLeft.x -= size.width / 2;
|
|
topLeft.y -= size.height / 2;
|
|
this._origin = this.layerToLocation(topLeft);
|
|
}
|
|
return this._origin;
|
|
},
|
|
_setExtent: function (extent) {
|
|
extent = Extent.create(extent);
|
|
this.center(extent.center());
|
|
var width = this.element.width();
|
|
var height = this.element.height();
|
|
for (var zoom = this.options.maxZoom; zoom >= this.options.minZoom; zoom--) {
|
|
var nw = this.locationToLayer(extent.nw, zoom);
|
|
var se = this.locationToLayer(extent.se, zoom);
|
|
var layerWidth = math.abs(se.x - nw.x);
|
|
var layerHeight = math.abs(se.y - nw.y);
|
|
if (layerWidth <= width && layerHeight <= height) {
|
|
break;
|
|
}
|
|
}
|
|
this.zoom(zoom);
|
|
},
|
|
_getExtent: function () {
|
|
var nw = this._getOrigin();
|
|
var bottomRight = this.locationToLayer(nw);
|
|
var size = this.viewSize();
|
|
bottomRight.x += size.width;
|
|
bottomRight.y += size.height;
|
|
var se = this.layerToLocation(bottomRight);
|
|
return new Extent(nw, se);
|
|
},
|
|
_zoomAround: function (pivot, level) {
|
|
this._setOrigin(this.layerToLocation(pivot, level), level);
|
|
this.zoom(level);
|
|
},
|
|
_initControls: function () {
|
|
var controls = this.options.controls;
|
|
if (ui.Attribution && controls.attribution) {
|
|
this._createAttribution(controls.attribution);
|
|
}
|
|
if (!kendo.support.mobileOS) {
|
|
if (ui.Navigator && controls.navigator) {
|
|
this._createNavigator(controls.navigator);
|
|
}
|
|
if (ui.ZoomControl && controls.zoom) {
|
|
this._createZoomControl(controls.zoom);
|
|
}
|
|
}
|
|
},
|
|
_createControlElement: function (options, defaultPos) {
|
|
var pos = options.position || defaultPos;
|
|
var posSelector = '.' + renderPos(pos).replace(' ', '.');
|
|
var wrap = $('.k-map-controls' + posSelector, this.element);
|
|
if (wrap.length === 0) {
|
|
wrap = $('<div>').addClass('k-map-controls ' + renderPos(pos)).appendTo(this.element);
|
|
}
|
|
return $('<div>').appendTo(wrap);
|
|
},
|
|
_createAttribution: function (options) {
|
|
var element = this._createControlElement(options, 'bottomRight');
|
|
this.attribution = new ui.Attribution(element, options);
|
|
},
|
|
_createNavigator: function (options) {
|
|
var element = this._createControlElement(options, 'topLeft');
|
|
var navigator = this.navigator = new ui.Navigator(element, options);
|
|
this._navigatorPan = proxy(this._navigatorPan, this);
|
|
navigator.bind('pan', this._navigatorPan);
|
|
this._navigatorCenter = proxy(this._navigatorCenter, this);
|
|
navigator.bind('center', this._navigatorCenter);
|
|
},
|
|
_navigatorPan: function (e) {
|
|
var map = this;
|
|
var scroller = map.scroller;
|
|
var x = scroller.scrollLeft + e.x;
|
|
var y = scroller.scrollTop - e.y;
|
|
var bounds = this._virtualSize;
|
|
var height = this.element.height();
|
|
var width = this.element.width();
|
|
x = limit(x, bounds.x.min, bounds.x.max - width);
|
|
y = limit(y, bounds.y.min, bounds.y.max - height);
|
|
map.scroller.one('scroll', function (e) {
|
|
map._scrollEnd(e);
|
|
});
|
|
map.scroller.scrollTo(-x, -y);
|
|
},
|
|
_navigatorCenter: function () {
|
|
this.center(this.options.center);
|
|
},
|
|
_createZoomControl: function (options) {
|
|
var element = this._createControlElement(options, 'topLeft');
|
|
var zoomControl = this.zoomControl = new ui.ZoomControl(element, options);
|
|
this._zoomControlChange = proxy(this._zoomControlChange, this);
|
|
zoomControl.bind('change', this._zoomControlChange);
|
|
},
|
|
_zoomControlChange: function (e) {
|
|
if (!this.trigger('zoomStart', { originalEvent: e })) {
|
|
this.zoom(this.zoom() + e.delta);
|
|
this.trigger('zoomEnd', { originalEvent: e });
|
|
}
|
|
},
|
|
_initScroller: function () {
|
|
var friction = kendo.support.mobileOS ? FRICTION_MOBILE : FRICTION;
|
|
var zoomable = this.options.zoomable !== false;
|
|
var scroller = this.scroller = new kendo.mobile.ui.Scroller(this.element.children(0), {
|
|
friction: friction,
|
|
velocityMultiplier: VELOCITY_MULTIPLIER,
|
|
zoom: zoomable,
|
|
mousewheelScrolling: false
|
|
});
|
|
scroller.bind('scroll', proxy(this._scroll, this));
|
|
scroller.bind('scrollEnd', proxy(this._scrollEnd, this));
|
|
scroller.userEvents.bind('gesturestart', proxy(this._scaleStart, this));
|
|
scroller.userEvents.bind('gestureend', proxy(this._scale, this));
|
|
this.scrollElement = scroller.scrollElement;
|
|
},
|
|
_initLayers: function () {
|
|
var defs = this.options.layers, layers = this.layers = [];
|
|
for (var i = 0; i < defs.length; i++) {
|
|
var options = defs[i];
|
|
var type = options.type || 'shape';
|
|
var defaults = this.options.layerDefaults[type];
|
|
var impl = dataviz.map.layers[type];
|
|
layers.push(new impl(this, deepExtend({}, defaults, options)));
|
|
}
|
|
},
|
|
_initMarkers: function () {
|
|
this.markers = new map.layers.MarkerLayer(this, this.options.markerDefaults);
|
|
this.markers.add(this.options.markers);
|
|
},
|
|
_scroll: function (e) {
|
|
var origin = this.locationToLayer(this._viewOrigin).round();
|
|
var movable = e.sender.movable;
|
|
var offset = new g.Point(movable.x, movable.y).scale(-1).scale(1 / movable.scale);
|
|
origin.x += offset.x;
|
|
origin.y += offset.y;
|
|
this._scrollOffset = offset;
|
|
this._setOrigin(this.layerToLocation(origin));
|
|
this.trigger('pan', {
|
|
originalEvent: e,
|
|
origin: this._getOrigin(),
|
|
center: this.center()
|
|
});
|
|
},
|
|
_scrollEnd: function (e) {
|
|
if (!this._scrollOffset || !this._panComplete()) {
|
|
return;
|
|
}
|
|
this._scrollOffset = null;
|
|
this._panEndTS = new Date();
|
|
this.trigger('panEnd', {
|
|
originalEvent: e,
|
|
origin: this._getOrigin(),
|
|
center: this.center()
|
|
});
|
|
},
|
|
_panComplete: function () {
|
|
return new Date() - (this._panEndTS || 0) > 50;
|
|
},
|
|
_scaleStart: function (e) {
|
|
if (this.trigger('zoomStart', { originalEvent: e })) {
|
|
var touch = e.touches[1];
|
|
if (touch) {
|
|
touch.cancel();
|
|
}
|
|
}
|
|
},
|
|
_scale: function (e) {
|
|
var scale = this.scroller.movable.scale;
|
|
var zoom = this._scaleToZoom(scale);
|
|
var gestureCenter = new g.Point(e.center.x, e.center.y);
|
|
var centerLocation = this.viewToLocation(gestureCenter, zoom);
|
|
var centerPoint = this.locationToLayer(centerLocation, zoom);
|
|
var originPoint = centerPoint.translate(-gestureCenter.x, -gestureCenter.y);
|
|
this._zoomAround(originPoint, zoom);
|
|
this.trigger('zoomEnd', { originalEvent: e });
|
|
},
|
|
_scaleToZoom: function (scaleDelta) {
|
|
var scale = this._layerSize() * scaleDelta;
|
|
var tiles = scale / this.options.minSize;
|
|
var zoom = math.log(tiles) / math.log(2);
|
|
return math.round(zoom);
|
|
},
|
|
_reset: function () {
|
|
if (this.attribution) {
|
|
this.attribution.filter(this.center(), this.zoom());
|
|
}
|
|
this._viewOrigin = this._getOrigin(true);
|
|
this._resetScroller();
|
|
this.trigger('beforeReset');
|
|
this.trigger('reset');
|
|
},
|
|
_resetScroller: function () {
|
|
var scroller = this.scroller;
|
|
var x = scroller.dimensions.x;
|
|
var y = scroller.dimensions.y;
|
|
var scale = this._layerSize();
|
|
var nw = this.extent().nw;
|
|
var topLeft = this.locationToLayer(nw).round();
|
|
scroller.movable.round = true;
|
|
scroller.reset();
|
|
scroller.userEvents.cancel();
|
|
var zoom = this.zoom();
|
|
scroller.dimensions.forcedMinScale = pow(2, this.options.minZoom - zoom);
|
|
scroller.dimensions.maxScale = pow(2, this.options.maxZoom - zoom);
|
|
var xBounds = {
|
|
min: -topLeft.x,
|
|
max: scale - topLeft.x
|
|
};
|
|
var yBounds = {
|
|
min: -topLeft.y,
|
|
max: scale - topLeft.y
|
|
};
|
|
if (this.options.wraparound) {
|
|
xBounds.max = 20 * scale;
|
|
xBounds.min = -xBounds.max;
|
|
}
|
|
if (this.options.pannable === false) {
|
|
var viewSize = this.viewSize();
|
|
xBounds.min = yBounds.min = 0;
|
|
xBounds.max = viewSize.width;
|
|
yBounds.max = viewSize.height;
|
|
}
|
|
x.makeVirtual();
|
|
y.makeVirtual();
|
|
x.virtualSize(xBounds.min, xBounds.max);
|
|
y.virtualSize(yBounds.min, yBounds.max);
|
|
this._virtualSize = {
|
|
x: xBounds,
|
|
y: yBounds
|
|
};
|
|
},
|
|
_renderLayers: function () {
|
|
var defs = this.options.layers, layers = this.layers = [], scrollWrap = this.scrollWrap;
|
|
scrollWrap.empty();
|
|
for (var i = 0; i < defs.length; i++) {
|
|
var options = defs[i];
|
|
var type = options.type || 'shape';
|
|
var defaults = this.options.layerDefaults[type];
|
|
var impl = dataviz.map.layers[type];
|
|
layers.push(new impl(this, deepExtend({}, defaults, options)));
|
|
}
|
|
},
|
|
_layerSize: function (zoom) {
|
|
zoom = valueOrDefault(zoom, this.options.zoom);
|
|
return this.options.minSize * pow(2, zoom);
|
|
},
|
|
_click: function (e) {
|
|
if (!this._panComplete()) {
|
|
return;
|
|
}
|
|
var cursor = this.eventOffset(e);
|
|
this.trigger('click', {
|
|
originalEvent: e,
|
|
location: this.viewToLocation(cursor)
|
|
});
|
|
},
|
|
_mousewheel: function (e) {
|
|
e.preventDefault();
|
|
var delta = dataviz.mwDelta(e) > 0 ? -1 : 1;
|
|
var options = this.options;
|
|
var fromZoom = this.zoom();
|
|
var toZoom = limit(fromZoom + delta, options.minZoom, options.maxZoom);
|
|
if (options.zoomable !== false && toZoom !== fromZoom) {
|
|
if (!this.trigger('zoomStart', { originalEvent: e })) {
|
|
var cursor = this.eventOffset(e);
|
|
var location = this.viewToLocation(cursor);
|
|
var postZoom = this.locationToLayer(location, toZoom);
|
|
var origin = postZoom.translate(-cursor.x, -cursor.y);
|
|
this._zoomAround(origin, toZoom);
|
|
this.trigger('zoomEnd', { originalEvent: e });
|
|
}
|
|
}
|
|
}
|
|
});
|
|
dataviz.ui.plugin(Map);
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.map', [
|
|
'kendo.data',
|
|
'kendo.userevents',
|
|
'kendo.tooltip',
|
|
'kendo.mobile.scroller',
|
|
'kendo.draganddrop',
|
|
'kendo.drawing',
|
|
'dataviz/map/location',
|
|
'dataviz/map/attribution',
|
|
'dataviz/map/navigator',
|
|
'dataviz/map/zoom',
|
|
'dataviz/map/crs',
|
|
'dataviz/map/layers/base',
|
|
'dataviz/map/layers/shape',
|
|
'dataviz/map/layers/bubble',
|
|
'dataviz/map/layers/tile',
|
|
'dataviz/map/layers/bing',
|
|
'dataviz/map/layers/marker',
|
|
'dataviz/map/main'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.map',
|
|
name: 'Map',
|
|
category: 'dataviz',
|
|
description: 'The Kendo DataViz Map displays spatial data',
|
|
depends: [
|
|
'data',
|
|
'userevents',
|
|
'tooltip',
|
|
'dataviz.core',
|
|
'drawing',
|
|
'mobile.scroller'
|
|
]
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/utils', ['kendo.core'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, diagram = kendo.dataviz.diagram = {}, deepExtend = kendo.deepExtend, isArray = $.isArray, EPSILON = 0.000001;
|
|
var Utils = {};
|
|
deepExtend(Utils, {
|
|
isNearZero: function (num) {
|
|
return Math.abs(num) < EPSILON;
|
|
},
|
|
isDefined: function (obj) {
|
|
return typeof obj !== 'undefined';
|
|
},
|
|
isUndefined: function (obj) {
|
|
return typeof obj === 'undefined' || obj === null;
|
|
},
|
|
isObject: function (obj) {
|
|
return obj === Object(obj);
|
|
},
|
|
has: function (obj, key) {
|
|
return Object.hasOwnProperty.call(obj, key);
|
|
},
|
|
isString: function (obj) {
|
|
return Object.prototype.toString.call(obj) == '[object String]';
|
|
},
|
|
isBoolean: function (obj) {
|
|
return Object.prototype.toString.call(obj) == '[object Boolean]';
|
|
},
|
|
isType: function (obj, type) {
|
|
return Object.prototype.toString.call(obj) == '[object ' + type + ']';
|
|
},
|
|
isNumber: function (obj) {
|
|
return !isNaN(parseFloat(obj)) && isFinite(obj);
|
|
},
|
|
isEmpty: function (obj) {
|
|
if (obj === null) {
|
|
return true;
|
|
}
|
|
if (isArray(obj) || Utils.isString(obj)) {
|
|
return obj.length === 0;
|
|
}
|
|
for (var key in obj) {
|
|
if (Utils.has(obj, key)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
simpleExtend: function (destination, source) {
|
|
if (!Utils.isObject(source)) {
|
|
return;
|
|
}
|
|
for (var name in source) {
|
|
destination[name] = source[name];
|
|
}
|
|
},
|
|
initArray: function createIdArray(size, value) {
|
|
var array = [];
|
|
for (var i = 0; i < size; ++i) {
|
|
array[i] = value;
|
|
}
|
|
return array;
|
|
},
|
|
serializePoints: function (points) {
|
|
var res = [];
|
|
for (var i = 0; i < points.length; i++) {
|
|
var p = points[i];
|
|
res.push(p.x + ';' + p.y);
|
|
}
|
|
return res.join(';');
|
|
},
|
|
deserializePoints: function (s) {
|
|
var v = s.split(';'), points = [];
|
|
if (v.length % 2 !== 0) {
|
|
throw 'Not an array of points.';
|
|
}
|
|
for (var i = 0; i < v.length; i += 2) {
|
|
points.push(new diagram.Point(parseInt(v[i], 10), parseInt(v[i + 1], 10)));
|
|
}
|
|
return points;
|
|
},
|
|
randomInteger: function (lower, upper) {
|
|
return parseInt(Math.floor(Math.random() * upper) + lower, 10);
|
|
},
|
|
DFT: function (el, func) {
|
|
func(el);
|
|
if (el.childNodes) {
|
|
for (var i = 0; i < el.childNodes.length; i++) {
|
|
var item = el.childNodes[i];
|
|
this.DFT(item, func);
|
|
}
|
|
}
|
|
},
|
|
getMatrixAngle: function (m) {
|
|
if (m === null || m.d === 0) {
|
|
return 0;
|
|
}
|
|
return Math.atan2(m.b, m.d) * 180 / Math.PI;
|
|
},
|
|
getMatrixScaling: function (m) {
|
|
var sX = Math.sqrt(m.a * m.a + m.c * m.c);
|
|
var sY = Math.sqrt(m.b * m.b + m.d * m.d);
|
|
return [
|
|
sX,
|
|
sY
|
|
];
|
|
}
|
|
});
|
|
function Range(start, stop, step) {
|
|
if (typeof start == 'undefined' || typeof stop == 'undefined') {
|
|
return [];
|
|
}
|
|
if (step && Utils.sign(stop - start) != Utils.sign(step)) {
|
|
throw 'The sign of the increment should allow to reach the stop-value.';
|
|
}
|
|
step = step || 1;
|
|
start = start || 0;
|
|
stop = stop || start;
|
|
if ((stop - start) / step === Infinity) {
|
|
throw 'Infinite range defined.';
|
|
}
|
|
var range = [], i = -1, j;
|
|
function rangeIntegerScale(x) {
|
|
var k = 1;
|
|
while (x * k % 1) {
|
|
k *= 10;
|
|
}
|
|
return k;
|
|
}
|
|
var k = rangeIntegerScale(Math.abs(step));
|
|
start *= k;
|
|
stop *= k;
|
|
step *= k;
|
|
if (start > stop && step > 0) {
|
|
step = -step;
|
|
}
|
|
if (step < 0) {
|
|
while ((j = start + step * ++i) >= stop) {
|
|
range.push(j / k);
|
|
}
|
|
} else {
|
|
while ((j = start + step * ++i) <= stop) {
|
|
range.push(j / k);
|
|
}
|
|
}
|
|
return range;
|
|
}
|
|
function findRadian(start, end) {
|
|
if (start == end) {
|
|
return 0;
|
|
}
|
|
var sngXComp = end.x - start.x, sngYComp = start.y - end.y, atan = Math.atan(sngXComp / sngYComp);
|
|
if (sngYComp >= 0) {
|
|
return sngXComp < 0 ? atan + 2 * Math.PI : atan;
|
|
}
|
|
return atan + Math.PI;
|
|
}
|
|
Utils.sign = function (number) {
|
|
return number ? number < 0 ? -1 : 1 : 0;
|
|
};
|
|
Utils.findAngle = function (center, end) {
|
|
return findRadian(center, end) * 180 / Math.PI;
|
|
};
|
|
Utils.forEach = function (arr, iterator, thisRef) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
iterator.call(thisRef, arr[i], i, arr);
|
|
}
|
|
};
|
|
Utils.any = function (arr, predicate) {
|
|
for (var i = 0; i < arr.length; ++i) {
|
|
if (predicate(arr[i])) {
|
|
return arr[i];
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
Utils.remove = function (arr, what) {
|
|
var ax;
|
|
while ((ax = Utils.indexOf(arr, what)) !== -1) {
|
|
arr.splice(ax, 1);
|
|
}
|
|
return arr;
|
|
};
|
|
Utils.contains = function (arr, obj) {
|
|
return Utils.indexOf(arr, obj) !== -1;
|
|
};
|
|
Utils.indexOf = function (arr, what) {
|
|
return $.inArray(what, arr);
|
|
};
|
|
Utils.fold = function (list, iterator, acc, context) {
|
|
var initial = arguments.length > 2;
|
|
for (var i = 0; i < list.length; i++) {
|
|
var value = list[i];
|
|
if (!initial) {
|
|
acc = value;
|
|
initial = true;
|
|
} else {
|
|
acc = iterator.call(context, acc, value, i, list);
|
|
}
|
|
}
|
|
if (!initial) {
|
|
throw 'Reduce of empty array with no initial value';
|
|
}
|
|
return acc;
|
|
};
|
|
Utils.find = function (arr, iterator, context) {
|
|
var result;
|
|
Utils.any(arr, function (value, index, list) {
|
|
if (iterator.call(context, value, index, list)) {
|
|
result = value;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
return result;
|
|
};
|
|
Utils.first = function (arr, constraint, context) {
|
|
if (arr.length === 0) {
|
|
return null;
|
|
}
|
|
if (Utils.isUndefined(constraint)) {
|
|
return arr[0];
|
|
}
|
|
return Utils.find(arr, constraint, context);
|
|
};
|
|
Utils.insert = function (arr, element, position) {
|
|
arr.splice(position, 0, element);
|
|
return arr;
|
|
};
|
|
Utils.all = function (arr, iterator, context) {
|
|
var result = true;
|
|
var value;
|
|
for (var i = 0; i < arr.length; i++) {
|
|
value = arr[i];
|
|
result = result && iterator.call(context, value, i, arr);
|
|
if (!result) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
Utils.clear = function (arr) {
|
|
arr.splice(0, arr.length);
|
|
};
|
|
Utils.bisort = function (a, b, sortfunc) {
|
|
if (Utils.isUndefined(a)) {
|
|
throw 'First array is not specified.';
|
|
}
|
|
if (Utils.isUndefined(b)) {
|
|
throw 'Second array is not specified.';
|
|
}
|
|
if (a.length != b.length) {
|
|
throw 'The two arrays should have equal length';
|
|
}
|
|
var all = [], i;
|
|
for (i = 0; i < a.length; i++) {
|
|
all.push({
|
|
'x': a[i],
|
|
'y': b[i]
|
|
});
|
|
}
|
|
if (Utils.isUndefined(sortfunc)) {
|
|
all.sort(function (m, n) {
|
|
return m.x - n.x;
|
|
});
|
|
} else {
|
|
all.sort(function (m, n) {
|
|
return sortfunc(m.x, n.x);
|
|
});
|
|
}
|
|
Utils.clear(a);
|
|
Utils.clear(b);
|
|
for (i = 0; i < all.length; i++) {
|
|
a.push(all[i].x);
|
|
b.push(all[i].y);
|
|
}
|
|
};
|
|
Utils.addRange = function (arr, range) {
|
|
arr.push.apply(arr, range);
|
|
};
|
|
var Easing = {
|
|
easeInOut: function (pos) {
|
|
return -Math.cos(pos * Math.PI) / 2 + 0.5;
|
|
}
|
|
};
|
|
var Ticker = kendo.Class.extend({
|
|
init: function () {
|
|
this.adapters = [];
|
|
this.target = 0;
|
|
this.tick = 0;
|
|
this.interval = 20;
|
|
this.duration = 800;
|
|
this.lastTime = null;
|
|
this.handlers = [];
|
|
var _this = this;
|
|
this.transition = Easing.easeInOut;
|
|
this.timerDelegate = function () {
|
|
_this.onTimerEvent();
|
|
};
|
|
},
|
|
addAdapter: function (a) {
|
|
this.adapters.push(a);
|
|
},
|
|
onComplete: function (handler) {
|
|
this.handlers.push(handler);
|
|
},
|
|
removeHandler: function (handler) {
|
|
this.handlers = $.grep(this.handlers, function (h) {
|
|
return h !== handler;
|
|
});
|
|
},
|
|
trigger: function () {
|
|
var _this = this;
|
|
if (this.handlers) {
|
|
Utils.forEach(this.handlers, function (h) {
|
|
return h.call(_this.caller !== null ? _this.caller : _this);
|
|
});
|
|
}
|
|
},
|
|
onStep: function () {
|
|
},
|
|
seekTo: function (to) {
|
|
this.seekFromTo(this.tick, to);
|
|
},
|
|
seekFromTo: function (from, to) {
|
|
this.target = Math.max(0, Math.min(1, to));
|
|
this.tick = Math.max(0, Math.min(1, from));
|
|
this.lastTime = new Date().getTime();
|
|
if (!this.intervalId) {
|
|
this.intervalId = window.setInterval(this.timerDelegate, this.interval);
|
|
}
|
|
},
|
|
stop: function () {
|
|
if (this.intervalId) {
|
|
window.clearInterval(this.intervalId);
|
|
this.intervalId = null;
|
|
this.trigger();
|
|
}
|
|
},
|
|
play: function (origin) {
|
|
if (this.adapters.length === 0) {
|
|
return;
|
|
}
|
|
if (origin !== null) {
|
|
this.caller = origin;
|
|
}
|
|
this.initState();
|
|
this.seekFromTo(0, 1);
|
|
},
|
|
reverse: function () {
|
|
this.seekFromTo(1, 0);
|
|
},
|
|
initState: function () {
|
|
if (this.adapters.length === 0) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < this.adapters.length; i++) {
|
|
this.adapters[i].initState();
|
|
}
|
|
},
|
|
propagate: function () {
|
|
var value = this.transition(this.tick);
|
|
for (var i = 0; i < this.adapters.length; i++) {
|
|
this.adapters[i].update(value);
|
|
}
|
|
},
|
|
onTimerEvent: function () {
|
|
var now = new Date().getTime();
|
|
var timePassed = now - this.lastTime;
|
|
this.lastTime = now;
|
|
var movement = timePassed / this.duration * (this.tick < this.target ? 1 : -1);
|
|
if (Math.abs(movement) >= Math.abs(this.tick - this.target)) {
|
|
this.tick = this.target;
|
|
} else {
|
|
this.tick += movement;
|
|
}
|
|
try {
|
|
this.propagate();
|
|
} finally {
|
|
this.onStep.call(this);
|
|
if (this.target == this.tick) {
|
|
this.stop();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
kendo.deepExtend(diagram, {
|
|
init: function (element) {
|
|
kendo.init(element, diagram.ui);
|
|
},
|
|
Utils: Utils,
|
|
Range: Range,
|
|
Ticker: Ticker
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/math', [
|
|
'dataviz/diagram/utils',
|
|
'kendo.dataviz.core'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Class = kendo.Class, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, Utils = diagram.Utils, Point = dataviz.Point2D, isFunction = kendo.isFunction, contains = Utils.contains, map = $.map;
|
|
var HITTESTAREA = 3, EPSILON = 0.000001;
|
|
deepExtend(Point.fn, {
|
|
plus: function (p) {
|
|
return new Point(this.x + p.x, this.y + p.y);
|
|
},
|
|
minus: function (p) {
|
|
return new Point(this.x - p.x, this.y - p.y);
|
|
},
|
|
offset: function (value) {
|
|
return new Point(this.x - value, this.y - value);
|
|
},
|
|
times: function (s) {
|
|
return new Point(this.x * s, this.y * s);
|
|
},
|
|
normalize: function () {
|
|
if (this.length() === 0) {
|
|
return new Point();
|
|
}
|
|
return this.times(1 / this.length());
|
|
},
|
|
length: function () {
|
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
},
|
|
toString: function () {
|
|
return '(' + this.x + ',' + this.y + ')';
|
|
},
|
|
lengthSquared: function () {
|
|
return this.x * this.x + this.y * this.y;
|
|
},
|
|
middleOf: function MiddleOf(p, q) {
|
|
return new Point(q.x - p.x, q.y - p.y).times(0.5).plus(p);
|
|
},
|
|
toPolar: function (useDegrees) {
|
|
var factor = 1;
|
|
if (useDegrees) {
|
|
factor = 180 / Math.PI;
|
|
}
|
|
var a = Math.atan2(Math.abs(this.y), Math.abs(this.x));
|
|
var halfpi = Math.PI / 2;
|
|
var len = this.length();
|
|
if (this.x === 0) {
|
|
if (this.y === 0) {
|
|
return new Polar(0, 0);
|
|
}
|
|
if (this.y > 0) {
|
|
return new Polar(len, factor * halfpi);
|
|
}
|
|
if (this.y < 0) {
|
|
return new Polar(len, factor * 3 * halfpi);
|
|
}
|
|
} else if (this.x > 0) {
|
|
if (this.y === 0) {
|
|
return new Polar(len, 0);
|
|
}
|
|
if (this.y > 0) {
|
|
return new Polar(len, factor * a);
|
|
}
|
|
if (this.y < 0) {
|
|
return new Polar(len, factor * (4 * halfpi - a));
|
|
}
|
|
} else {
|
|
if (this.y === 0) {
|
|
return new Polar(len, 2 * halfpi);
|
|
}
|
|
if (this.y > 0) {
|
|
return new Polar(len, factor * (2 * halfpi - a));
|
|
}
|
|
if (this.y < 0) {
|
|
return new Polar(len, factor * (2 * halfpi + a));
|
|
}
|
|
}
|
|
},
|
|
isOnLine: function (from, to) {
|
|
if (from.x > to.x) {
|
|
var temp = to;
|
|
to = from;
|
|
from = temp;
|
|
}
|
|
var r1 = new Rect(from.x, from.y).inflate(HITTESTAREA, HITTESTAREA), r2 = new Rect(to.x, to.y).inflate(HITTESTAREA, HITTESTAREA), o1, u1;
|
|
if (r1.union(r2).contains(this)) {
|
|
if (from.x === to.x || from.y === to.y) {
|
|
return true;
|
|
} else if (from.y < to.y) {
|
|
o1 = r1.x + (r2.x - r1.x) * (this.y - (r1.y + r1.height)) / (r2.y + r2.height - (r1.y + r1.height));
|
|
u1 = r1.x + r1.width + (r2.x + r2.width - (r1.x + r1.width)) * (this.y - r1.y) / (r2.y - r1.y);
|
|
} else {
|
|
o1 = r1.x + (r2.x - r1.x) * (this.y - r1.y) / (r2.y - r1.y);
|
|
u1 = r1.x + r1.width + (r2.x + r2.width - (r1.x + r1.width)) * (this.y - (r1.y + r1.height)) / (r2.y + r2.height - (r1.y + r1.height));
|
|
}
|
|
return this.x > o1 && this.x < u1;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
deepExtend(Point, {
|
|
parse: function (str) {
|
|
var tempStr = str.slice(1, str.length - 1), xy = tempStr.split(','), x = parseInt(xy[0], 10), y = parseInt(xy[1], 10);
|
|
if (!isNaN(x) && !isNaN(y)) {
|
|
return new Point(x, y);
|
|
}
|
|
}
|
|
});
|
|
var PathDefiner = Class.extend({
|
|
init: function (p, left, right) {
|
|
this.point = p;
|
|
this.left = left;
|
|
this.right = right;
|
|
}
|
|
});
|
|
var Rect = Class.extend({
|
|
init: function (x, y, width, height) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
this.width = width || 0;
|
|
this.height = height || 0;
|
|
},
|
|
contains: function (point) {
|
|
return point.x >= this.x && point.x <= this.x + this.width && point.y >= this.y && point.y <= this.y + this.height;
|
|
},
|
|
inflate: function (dx, dy) {
|
|
if (dy === undefined) {
|
|
dy = dx;
|
|
}
|
|
this.x -= dx;
|
|
this.y -= dy;
|
|
this.width += 2 * dx + 1;
|
|
this.height += 2 * dy + 1;
|
|
return this;
|
|
},
|
|
offset: function (dx, dy) {
|
|
var x = dx, y = dy;
|
|
if (dx instanceof Point) {
|
|
x = dx.x;
|
|
y = dx.y;
|
|
}
|
|
this.x += x;
|
|
this.y += y;
|
|
return this;
|
|
},
|
|
union: function (r) {
|
|
var x1 = Math.min(this.x, r.x);
|
|
var y1 = Math.min(this.y, r.y);
|
|
var x2 = Math.max(this.x + this.width, r.x + r.width);
|
|
var y2 = Math.max(this.y + this.height, r.y + r.height);
|
|
return new Rect(x1, y1, x2 - x1, y2 - y1);
|
|
},
|
|
center: function () {
|
|
return new Point(this.x + this.width / 2, this.y + this.height / 2);
|
|
},
|
|
top: function () {
|
|
return new Point(this.x + this.width / 2, this.y);
|
|
},
|
|
right: function () {
|
|
return new Point(this.x + this.width, this.y + this.height / 2);
|
|
},
|
|
bottom: function () {
|
|
return new Point(this.x + this.width / 2, this.y + this.height);
|
|
},
|
|
left: function () {
|
|
return new Point(this.x, this.y + this.height / 2);
|
|
},
|
|
topLeft: function () {
|
|
return new Point(this.x, this.y);
|
|
},
|
|
topRight: function () {
|
|
return new Point(this.x + this.width, this.y);
|
|
},
|
|
bottomLeft: function () {
|
|
return new Point(this.x, this.y + this.height);
|
|
},
|
|
bottomRight: function () {
|
|
return new Point(this.x + this.width, this.y + this.height);
|
|
},
|
|
clone: function () {
|
|
return new Rect(this.x, this.y, this.width, this.height);
|
|
},
|
|
isEmpty: function () {
|
|
return !this.width && !this.height;
|
|
},
|
|
equals: function (rect) {
|
|
return this.x === rect.x && this.y === rect.y && this.width === rect.width && this.height === rect.height;
|
|
},
|
|
rotatedBounds: function (angle) {
|
|
var rect = this.clone(), points = this.rotatedPoints(angle), tl = points[0], tr = points[1], br = points[2], bl = points[3];
|
|
rect.x = Math.min(br.x, tl.x, tr.x, bl.x);
|
|
rect.y = Math.min(br.y, tl.y, tr.y, bl.y);
|
|
rect.width = Math.max(br.x, tl.x, tr.x, bl.x) - rect.x;
|
|
rect.height = Math.max(br.y, tl.y, tr.y, bl.y) - rect.y;
|
|
return rect;
|
|
},
|
|
rotatedPoints: function (angle) {
|
|
var rect = this, c = rect.center(), br = rect.bottomRight().rotate(c, 360 - angle), tl = rect.topLeft().rotate(c, 360 - angle), tr = rect.topRight().rotate(c, 360 - angle), bl = rect.bottomLeft().rotate(c, 360 - angle);
|
|
return [
|
|
tl,
|
|
tr,
|
|
br,
|
|
bl
|
|
];
|
|
},
|
|
toString: function (delimiter) {
|
|
delimiter = delimiter || ' ';
|
|
return this.x + delimiter + this.y + delimiter + this.width + delimiter + this.height;
|
|
},
|
|
scale: function (scaleX, scaleY, staicPoint, adornerCenter, angle) {
|
|
var tl = this.topLeft();
|
|
var thisCenter = this.center();
|
|
tl.rotate(thisCenter, 360 - angle).rotate(adornerCenter, angle);
|
|
var delta = staicPoint.minus(tl);
|
|
var scaled = new Point(delta.x * scaleX, delta.y * scaleY);
|
|
var position = delta.minus(scaled);
|
|
tl = tl.plus(position);
|
|
tl.rotate(adornerCenter, 360 - angle).rotate(thisCenter, angle);
|
|
this.x = tl.x;
|
|
this.y = tl.y;
|
|
this.width *= scaleX;
|
|
this.height *= scaleY;
|
|
},
|
|
zoom: function (zoom) {
|
|
this.x *= zoom;
|
|
this.y *= zoom;
|
|
this.width *= zoom;
|
|
this.height *= zoom;
|
|
return this;
|
|
},
|
|
overlaps: function (rect) {
|
|
var bottomRight = this.bottomRight();
|
|
var rectBottomRight = rect.bottomRight();
|
|
var overlaps = !(bottomRight.x < rect.x || bottomRight.y < rect.y || rectBottomRight.x < this.x || rectBottomRight.y < this.y);
|
|
return overlaps;
|
|
}
|
|
});
|
|
var Size = Class.extend({
|
|
init: function (width, height) {
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
});
|
|
Size.prototype.Empty = new Size(0, 0);
|
|
Rect.toRect = function (rect) {
|
|
if (!(rect instanceof Rect)) {
|
|
rect = new Rect(rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
return rect;
|
|
};
|
|
Rect.empty = function () {
|
|
return new Rect(0, 0, 0, 0);
|
|
};
|
|
Rect.fromPoints = function (p, q) {
|
|
if (isNaN(p.x) || isNaN(p.y) || isNaN(q.x) || isNaN(q.y)) {
|
|
throw 'Some values are NaN.';
|
|
}
|
|
return new Rect(Math.min(p.x, q.x), Math.min(p.y, q.y), Math.abs(p.x - q.x), Math.abs(p.y - q.y));
|
|
};
|
|
function isNearZero(num) {
|
|
return Math.abs(num) < EPSILON;
|
|
}
|
|
function intersectLine(start1, end1, start2, end2, isSegment) {
|
|
var tangensdiff = (end1.x - start1.x) * (end2.y - start2.y) - (end1.y - start1.y) * (end2.x - start2.x);
|
|
if (isNearZero(tangensdiff)) {
|
|
return;
|
|
}
|
|
var num1 = (start1.y - start2.y) * (end2.x - start2.x) - (start1.x - start2.x) * (end2.y - start2.y);
|
|
var num2 = (start1.y - start2.y) * (end1.x - start1.x) - (start1.x - start2.x) * (end1.y - start1.y);
|
|
var r = num1 / tangensdiff;
|
|
var s = num2 / tangensdiff;
|
|
if (isSegment && (r < 0 || r > 1 || s < 0 || s > 1)) {
|
|
return;
|
|
}
|
|
return new Point(start1.x + r * (end1.x - start1.x), start1.y + r * (end1.y - start1.y));
|
|
}
|
|
var Intersect = {
|
|
lines: function (start1, end1, start2, end2) {
|
|
return intersectLine(start1, end1, start2, end2);
|
|
},
|
|
segments: function (start1, end1, start2, end2) {
|
|
return intersectLine(start1, end1, start2, end2, true);
|
|
},
|
|
rectWithLine: function (rect, start, end) {
|
|
return Intersect.segments(start, end, rect.topLeft(), rect.topRight()) || Intersect.segments(start, end, rect.topRight(), rect.bottomRight()) || Intersect.segments(start, end, rect.bottomLeft(), rect.bottomRight()) || Intersect.segments(start, end, rect.topLeft(), rect.bottomLeft());
|
|
},
|
|
rects: function (rect1, rect2, angle) {
|
|
var tl = rect2.topLeft(), tr = rect2.topRight(), bl = rect2.bottomLeft(), br = rect2.bottomRight();
|
|
var center = rect2.center();
|
|
if (angle) {
|
|
tl = tl.rotate(center, angle);
|
|
tr = tr.rotate(center, angle);
|
|
bl = bl.rotate(center, angle);
|
|
br = br.rotate(center, angle);
|
|
}
|
|
var intersect = rect1.contains(tl) || rect1.contains(tr) || rect1.contains(bl) || rect1.contains(br) || Intersect.rectWithLine(rect1, tl, tr) || Intersect.rectWithLine(rect1, tl, bl) || Intersect.rectWithLine(rect1, tr, br) || Intersect.rectWithLine(rect1, bl, br);
|
|
if (!intersect) {
|
|
tl = rect1.topLeft();
|
|
tr = rect1.topRight();
|
|
bl = rect1.bottomLeft();
|
|
br = rect1.bottomRight();
|
|
if (angle) {
|
|
var reverseAngle = 360 - angle;
|
|
tl = tl.rotate(center, reverseAngle);
|
|
tr = tr.rotate(center, reverseAngle);
|
|
bl = bl.rotate(center, reverseAngle);
|
|
br = br.rotate(center, reverseAngle);
|
|
}
|
|
intersect = rect2.contains(tl) || rect2.contains(tr) || rect2.contains(bl) || rect2.contains(br);
|
|
}
|
|
return intersect;
|
|
}
|
|
};
|
|
var RectAlign = Class.extend({
|
|
init: function (container) {
|
|
this.container = Rect.toRect(container);
|
|
},
|
|
align: function (content, alignment) {
|
|
var alignValues = alignment.toLowerCase().split(' ');
|
|
for (var i = 0; i < alignValues.length; i++) {
|
|
content = this._singleAlign(content, alignValues[i]);
|
|
}
|
|
return content;
|
|
},
|
|
_singleAlign: function (content, alignment) {
|
|
if (isFunction(this[alignment])) {
|
|
return this[alignment](content);
|
|
} else {
|
|
return content;
|
|
}
|
|
},
|
|
left: function (content) {
|
|
return this._align(content, this._left);
|
|
},
|
|
center: function (content) {
|
|
return this._align(content, this._center);
|
|
},
|
|
right: function (content) {
|
|
return this._align(content, this._right);
|
|
},
|
|
stretch: function (content) {
|
|
return this._align(content, this._stretch);
|
|
},
|
|
top: function (content) {
|
|
return this._align(content, this._top);
|
|
},
|
|
middle: function (content) {
|
|
return this._align(content, this._middle);
|
|
},
|
|
bottom: function (content) {
|
|
return this._align(content, this._bottom);
|
|
},
|
|
_left: function (container, content) {
|
|
content.x = container.x;
|
|
},
|
|
_center: function (container, content) {
|
|
content.x = (container.width - content.width) / 2 || 0;
|
|
},
|
|
_right: function (container, content) {
|
|
content.x = container.width - content.width;
|
|
},
|
|
_top: function (container, content) {
|
|
content.y = container.y;
|
|
},
|
|
_middle: function (container, content) {
|
|
content.y = (container.height - content.height) / 2 || 0;
|
|
},
|
|
_bottom: function (container, content) {
|
|
content.y = container.height - content.height;
|
|
},
|
|
_stretch: function (container, content) {
|
|
content.x = 0;
|
|
content.y = 0;
|
|
content.height = container.height;
|
|
content.width = container.width;
|
|
},
|
|
_align: function (content, alignCalc) {
|
|
content = Rect.toRect(content);
|
|
alignCalc(this.container, content);
|
|
return content;
|
|
}
|
|
});
|
|
var Polar = Class.extend({
|
|
init: function (r, a) {
|
|
this.r = r;
|
|
this.angle = a;
|
|
}
|
|
});
|
|
var Matrix = Class.extend({
|
|
init: function (a, b, c, d, e, f) {
|
|
this.a = a || 0;
|
|
this.b = b || 0;
|
|
this.c = c || 0;
|
|
this.d = d || 0;
|
|
this.e = e || 0;
|
|
this.f = f || 0;
|
|
},
|
|
plus: function (m) {
|
|
this.a += m.a;
|
|
this.b += m.b;
|
|
this.c += m.c;
|
|
this.d += m.d;
|
|
this.e += m.e;
|
|
this.f += m.f;
|
|
},
|
|
minus: function (m) {
|
|
this.a -= m.a;
|
|
this.b -= m.b;
|
|
this.c -= m.c;
|
|
this.d -= m.d;
|
|
this.e -= m.e;
|
|
this.f -= m.f;
|
|
},
|
|
times: function (m) {
|
|
return new Matrix(this.a * m.a + this.c * m.b, this.b * m.a + this.d * m.b, this.a * m.c + this.c * m.d, this.b * m.c + this.d * m.d, this.a * m.e + this.c * m.f + this.e, this.b * m.e + this.d * m.f + this.f);
|
|
},
|
|
apply: function (p) {
|
|
return new Point(this.a * p.x + this.c * p.y + this.e, this.b * p.x + this.d * p.y + this.f);
|
|
},
|
|
applyRect: function (r) {
|
|
return Rect.fromPoints(this.apply(r.topLeft()), this.apply(r.bottomRight()));
|
|
},
|
|
toString: function () {
|
|
return 'matrix(' + this.a + ' ' + this.b + ' ' + this.c + ' ' + this.d + ' ' + this.e + ' ' + this.f + ')';
|
|
}
|
|
});
|
|
deepExtend(Matrix, {
|
|
fromSVGMatrix: function (vm) {
|
|
var m = new Matrix();
|
|
m.a = vm.a;
|
|
m.b = vm.b;
|
|
m.c = vm.c;
|
|
m.d = vm.d;
|
|
m.e = vm.e;
|
|
m.f = vm.f;
|
|
return m;
|
|
},
|
|
fromMatrixVector: function (v) {
|
|
var m = new Matrix();
|
|
m.a = v.a;
|
|
m.b = v.b;
|
|
m.c = v.c;
|
|
m.d = v.d;
|
|
m.e = v.e;
|
|
m.f = v.f;
|
|
return m;
|
|
},
|
|
fromList: function (v) {
|
|
if (v.length !== 6) {
|
|
throw 'The given list should consist of six elements.';
|
|
}
|
|
var m = new Matrix();
|
|
m.a = v[0];
|
|
m.b = v[1];
|
|
m.c = v[2];
|
|
m.d = v[3];
|
|
m.e = v[4];
|
|
m.f = v[5];
|
|
return m;
|
|
},
|
|
translation: function (x, y) {
|
|
var m = new Matrix();
|
|
m.a = 1;
|
|
m.b = 0;
|
|
m.c = 0;
|
|
m.d = 1;
|
|
m.e = x;
|
|
m.f = y;
|
|
return m;
|
|
},
|
|
unit: function () {
|
|
return new Matrix(1, 0, 0, 1, 0, 0);
|
|
},
|
|
rotation: function (angle, x, y) {
|
|
var m = new Matrix();
|
|
m.a = Math.cos(angle * Math.PI / 180);
|
|
m.b = Math.sin(angle * Math.PI / 180);
|
|
m.c = -m.b;
|
|
m.d = m.a;
|
|
m.e = x - x * m.a + y * m.b || 0;
|
|
m.f = y - y * m.a - x * m.b || 0;
|
|
return m;
|
|
},
|
|
scaling: function (scaleX, scaleY) {
|
|
var m = new Matrix();
|
|
m.a = scaleX;
|
|
m.b = 0;
|
|
m.c = 0;
|
|
m.d = scaleY;
|
|
m.e = 0;
|
|
m.f = 0;
|
|
return m;
|
|
},
|
|
parse: function (v) {
|
|
var parts, nums;
|
|
if (v) {
|
|
v = v.trim();
|
|
if (v.slice(0, 6).toLowerCase() === 'matrix') {
|
|
nums = v.slice(7, v.length - 1).trim();
|
|
parts = nums.split(',');
|
|
if (parts.length === 6) {
|
|
return Matrix.fromList(map(parts, function (p) {
|
|
return parseFloat(p);
|
|
}));
|
|
}
|
|
parts = nums.split(' ');
|
|
if (parts.length === 6) {
|
|
return Matrix.fromList(map(parts, function (p) {
|
|
return parseFloat(p);
|
|
}));
|
|
}
|
|
}
|
|
if (v.slice(0, 1) === '(' && v.slice(v.length - 1) === ')') {
|
|
v = v.substr(1, v.length - 1);
|
|
}
|
|
if (v.indexOf(',') > 0) {
|
|
parts = v.split(',');
|
|
if (parts.length === 6) {
|
|
return Matrix.fromList(map(parts, function (p) {
|
|
return parseFloat(p);
|
|
}));
|
|
}
|
|
}
|
|
if (v.indexOf(' ') > 0) {
|
|
parts = v.split(' ');
|
|
if (parts.length === 6) {
|
|
return Matrix.fromList(map(parts, function (p) {
|
|
return parseFloat(p);
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
return parts;
|
|
}
|
|
});
|
|
var MatrixVector = Class.extend({
|
|
init: function (a, b, c, d, e, f) {
|
|
this.a = a || 0;
|
|
this.b = b || 0;
|
|
this.c = c || 0;
|
|
this.d = d || 0;
|
|
this.e = e || 0;
|
|
this.f = f || 0;
|
|
},
|
|
fromMatrix: function FromMatrix(m) {
|
|
var v = new MatrixVector();
|
|
v.a = m.a;
|
|
v.b = m.b;
|
|
v.c = m.c;
|
|
v.d = m.d;
|
|
v.e = m.e;
|
|
v.f = m.f;
|
|
return v;
|
|
}
|
|
});
|
|
function normalVariable(mean, deviation) {
|
|
var x, y, r;
|
|
do {
|
|
x = Math.random() * 2 - 1;
|
|
y = Math.random() * 2 - 1;
|
|
r = x * x + y * y;
|
|
} while (!r || r > 1);
|
|
return mean + deviation * x * Math.sqrt(-2 * Math.log(r) / r);
|
|
}
|
|
function randomId(length) {
|
|
if (Utils.isUndefined(length)) {
|
|
length = 10;
|
|
}
|
|
var result = '';
|
|
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
for (var i = length; i > 0; --i) {
|
|
result += chars.charAt(Math.round(Math.random() * (chars.length - 1)));
|
|
}
|
|
return result;
|
|
}
|
|
var Geometry = {
|
|
_distanceToLineSquared: function (p, a, b) {
|
|
function d2(pt1, pt2) {
|
|
return (pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y);
|
|
}
|
|
if (a === b) {
|
|
return d2(p, a);
|
|
}
|
|
var vx = b.x - a.x, vy = b.y - a.y, dot = (p.x - a.x) * vx + (p.y - a.y) * vy;
|
|
if (dot < 0) {
|
|
return d2(a, p);
|
|
}
|
|
dot = (b.x - p.x) * vx + (b.y - p.y) * vy;
|
|
if (dot < 0) {
|
|
return d2(b, p);
|
|
}
|
|
dot = (b.x - p.x) * vy - (b.y - p.y) * vx;
|
|
return dot * dot / (vx * vx + vy * vy);
|
|
},
|
|
distanceToLine: function (p, a, b) {
|
|
return Math.sqrt(this._distanceToLineSquared(p, a, b));
|
|
},
|
|
distanceToPolyline: function (p, points) {
|
|
var minimum = Number.MAX_VALUE;
|
|
if (Utils.isUndefined(points) || points.length === 0) {
|
|
return Number.MAX_VALUE;
|
|
}
|
|
for (var s = 0; s < points.length - 1; s++) {
|
|
var p1 = points[s];
|
|
var p2 = points[s + 1];
|
|
var d = this._distanceToLineSquared(p, p1, p2);
|
|
if (d < minimum) {
|
|
minimum = d;
|
|
}
|
|
}
|
|
return Math.sqrt(minimum);
|
|
}
|
|
};
|
|
var HashTable = kendo.Class.extend({
|
|
init: function () {
|
|
this._buckets = [];
|
|
this.length = 0;
|
|
},
|
|
add: function (key, value) {
|
|
var obj = this._createGetBucket(key);
|
|
if (Utils.isDefined(value)) {
|
|
obj.value = value;
|
|
}
|
|
return obj;
|
|
},
|
|
get: function (key) {
|
|
if (this._bucketExists(key)) {
|
|
return this._createGetBucket(key);
|
|
}
|
|
return null;
|
|
},
|
|
set: function (key, value) {
|
|
this.add(key, value);
|
|
},
|
|
containsKey: function (key) {
|
|
return this._bucketExists(key);
|
|
},
|
|
remove: function (key) {
|
|
if (this._bucketExists(key)) {
|
|
var hashId = this._hash(key);
|
|
delete this._buckets[hashId];
|
|
this.length--;
|
|
return key;
|
|
}
|
|
},
|
|
forEach: function (func) {
|
|
var hashes = this._hashes();
|
|
for (var i = 0, len = hashes.length; i < len; i++) {
|
|
var hash = hashes[i];
|
|
var bucket = this._buckets[hash];
|
|
if (Utils.isUndefined(bucket)) {
|
|
continue;
|
|
}
|
|
func(bucket);
|
|
}
|
|
},
|
|
clone: function () {
|
|
var ht = new HashTable();
|
|
var hashes = this._hashes();
|
|
for (var i = 0, len = hashes.length; i < len; i++) {
|
|
var hash = hashes[i];
|
|
var bucket = this._buckets[hash];
|
|
if (Utils.isUndefined(bucket)) {
|
|
continue;
|
|
}
|
|
ht.add(bucket.key, bucket.value);
|
|
}
|
|
return ht;
|
|
},
|
|
_hashes: function () {
|
|
var hashes = [];
|
|
for (var hash in this._buckets) {
|
|
if (this._buckets.hasOwnProperty(hash)) {
|
|
hashes.push(hash);
|
|
}
|
|
}
|
|
return hashes;
|
|
},
|
|
_bucketExists: function (key) {
|
|
var hashId = this._hash(key);
|
|
return Utils.isDefined(this._buckets[hashId]);
|
|
},
|
|
_createGetBucket: function (key) {
|
|
var hashId = this._hash(key);
|
|
var bucket = this._buckets[hashId];
|
|
if (Utils.isUndefined(bucket)) {
|
|
bucket = { key: key };
|
|
this._buckets[hashId] = bucket;
|
|
this.length++;
|
|
}
|
|
return bucket;
|
|
},
|
|
_hash: function (key) {
|
|
if (Utils.isNumber(key)) {
|
|
return key;
|
|
}
|
|
if (Utils.isString(key)) {
|
|
return this._hashString(key);
|
|
}
|
|
if (Utils.isObject(key)) {
|
|
return this._objectHashId(key);
|
|
}
|
|
throw 'Unsupported key type.';
|
|
},
|
|
_hashString: function (s) {
|
|
var result = 0;
|
|
if (s.length === 0) {
|
|
return result;
|
|
}
|
|
for (var i = 0; i < s.length; i++) {
|
|
var ch = s.charCodeAt(i);
|
|
result = result * 32 - result + ch;
|
|
}
|
|
return result;
|
|
},
|
|
_objectHashId: function (key) {
|
|
var id = key._hashId;
|
|
if (Utils.isUndefined(id)) {
|
|
id = randomId();
|
|
key._hashId = id;
|
|
}
|
|
return id;
|
|
}
|
|
});
|
|
var Dictionary = kendo.Observable.extend({
|
|
init: function (dictionary) {
|
|
var that = this;
|
|
kendo.Observable.fn.init.call(that);
|
|
this._hashTable = new HashTable();
|
|
this.length = 0;
|
|
if (Utils.isDefined(dictionary)) {
|
|
if ($.isArray(dictionary)) {
|
|
for (var i = 0; i < dictionary.length; i++) {
|
|
this.add(dictionary[i]);
|
|
}
|
|
} else {
|
|
dictionary.forEach(function (k, v) {
|
|
this.add(k, v);
|
|
}, this);
|
|
}
|
|
}
|
|
},
|
|
add: function (key, value) {
|
|
var entry = this._hashTable.get(key);
|
|
if (!entry) {
|
|
entry = this._hashTable.add(key);
|
|
this.length++;
|
|
this.trigger('changed');
|
|
}
|
|
entry.value = value;
|
|
},
|
|
set: function (key, value) {
|
|
this.add(key, value);
|
|
},
|
|
get: function (key) {
|
|
var entry = this._hashTable.get(key);
|
|
if (entry) {
|
|
return entry.value;
|
|
}
|
|
throw new Error('Cannot find key ' + key);
|
|
},
|
|
containsKey: function (key) {
|
|
return this._hashTable.containsKey(key);
|
|
},
|
|
remove: function (key) {
|
|
if (this.containsKey(key)) {
|
|
this.trigger('changed');
|
|
this.length--;
|
|
return this._hashTable.remove(key);
|
|
}
|
|
},
|
|
forEach: function (func, thisRef) {
|
|
this._hashTable.forEach(function (entry) {
|
|
func.call(thisRef, entry.key, entry.value);
|
|
});
|
|
},
|
|
forEachValue: function (func, thisRef) {
|
|
this._hashTable.forEach(function (entry) {
|
|
func.call(thisRef, entry.value);
|
|
});
|
|
},
|
|
forEachKey: function (func, thisRef) {
|
|
this._hashTable.forEach(function (entry) {
|
|
func.call(thisRef, entry.key);
|
|
});
|
|
},
|
|
keys: function () {
|
|
var keys = [];
|
|
this.forEachKey(function (key) {
|
|
keys.push(key);
|
|
});
|
|
return keys;
|
|
}
|
|
});
|
|
var Queue = kendo.Class.extend({
|
|
init: function () {
|
|
this._tail = null;
|
|
this._head = null;
|
|
this.length = 0;
|
|
},
|
|
enqueue: function (value) {
|
|
var entry = {
|
|
value: value,
|
|
next: null
|
|
};
|
|
if (!this._head) {
|
|
this._head = entry;
|
|
this._tail = this._head;
|
|
} else {
|
|
this._tail.next = entry;
|
|
this._tail = this._tail.next;
|
|
}
|
|
this.length++;
|
|
},
|
|
dequeue: function () {
|
|
if (this.length < 1) {
|
|
throw new Error('The queue is empty.');
|
|
}
|
|
var value = this._head.value;
|
|
this._head = this._head.next;
|
|
this.length--;
|
|
return value;
|
|
},
|
|
contains: function (item) {
|
|
var current = this._head;
|
|
while (current) {
|
|
if (current.value === item) {
|
|
return true;
|
|
}
|
|
current = current.next;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
var Set = kendo.Observable.extend({
|
|
init: function (resource) {
|
|
var that = this;
|
|
kendo.Observable.fn.init.call(that);
|
|
this._hashTable = new HashTable();
|
|
this.length = 0;
|
|
if (Utils.isDefined(resource)) {
|
|
if (resource instanceof HashTable) {
|
|
resource.forEach(function (d) {
|
|
this.add(d);
|
|
});
|
|
} else if (resource instanceof Dictionary) {
|
|
resource.forEach(function (k, v) {
|
|
this.add({
|
|
key: k,
|
|
value: v
|
|
});
|
|
}, this);
|
|
}
|
|
}
|
|
},
|
|
contains: function (item) {
|
|
return this._hashTable.containsKey(item);
|
|
},
|
|
add: function (item) {
|
|
var entry = this._hashTable.get(item);
|
|
if (!entry) {
|
|
this._hashTable.add(item, item);
|
|
this.length++;
|
|
this.trigger('changed');
|
|
}
|
|
},
|
|
get: function (item) {
|
|
if (this.contains(item)) {
|
|
return this._hashTable.get(item).value;
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
hash: function (item) {
|
|
return this._hashTable._hash(item);
|
|
},
|
|
remove: function (item) {
|
|
if (this.contains(item)) {
|
|
this._hashTable.remove(item);
|
|
this.length--;
|
|
this.trigger('changed');
|
|
}
|
|
},
|
|
forEach: function (func, context) {
|
|
this._hashTable.forEach(function (kv) {
|
|
func(kv.value);
|
|
}, context);
|
|
},
|
|
toArray: function () {
|
|
var r = [];
|
|
this.forEach(function (d) {
|
|
r.push(d);
|
|
});
|
|
return r;
|
|
}
|
|
});
|
|
var Node = kendo.Class.extend({
|
|
init: function (id, shape) {
|
|
this.links = [];
|
|
this.outgoing = [];
|
|
this.incoming = [];
|
|
this.weight = 1;
|
|
if (Utils.isDefined(id)) {
|
|
this.id = id;
|
|
} else {
|
|
this.id = randomId();
|
|
}
|
|
if (Utils.isDefined(shape)) {
|
|
this.associatedShape = shape;
|
|
var b = shape.bounds();
|
|
this.width = b.width;
|
|
this.height = b.height;
|
|
this.x = b.x;
|
|
this.y = b.y;
|
|
} else {
|
|
this.associatedShape = null;
|
|
}
|
|
this.data = null;
|
|
this.type = 'Node';
|
|
this.shortForm = 'Node \'' + this.id + '\'';
|
|
this.isVirtual = false;
|
|
},
|
|
isIsolated: function () {
|
|
return Utils.isEmpty(this.links);
|
|
},
|
|
bounds: function (r) {
|
|
if (!Utils.isDefined(r)) {
|
|
return new diagram.Rect(this.x, this.y, this.width, this.height);
|
|
}
|
|
this.x = r.x;
|
|
this.y = r.y;
|
|
this.width = r.width;
|
|
this.height = r.height;
|
|
},
|
|
isLinkedTo: function (node) {
|
|
var that = this;
|
|
return Utils.any(that.links, function (link) {
|
|
return link.getComplement(that) === node;
|
|
});
|
|
},
|
|
getChildren: function () {
|
|
if (this.outgoing.length === 0) {
|
|
return [];
|
|
}
|
|
var children = [];
|
|
for (var i = 0, len = this.outgoing.length; i < len; i++) {
|
|
var link = this.outgoing[i];
|
|
children.push(link.getComplement(this));
|
|
}
|
|
return children;
|
|
},
|
|
getParents: function () {
|
|
if (this.incoming.length === 0) {
|
|
return [];
|
|
}
|
|
var parents = [];
|
|
for (var i = 0, len = this.incoming.length; i < len; i++) {
|
|
var link = this.incoming[i];
|
|
parents.push(link.getComplement(this));
|
|
}
|
|
return parents;
|
|
},
|
|
clone: function () {
|
|
var copy = new Node();
|
|
if (Utils.isDefined(this.weight)) {
|
|
copy.weight = this.weight;
|
|
}
|
|
if (Utils.isDefined(this.balance)) {
|
|
copy.balance = this.balance;
|
|
}
|
|
if (Utils.isDefined(this.owner)) {
|
|
copy.owner = this.owner;
|
|
}
|
|
copy.associatedShape = this.associatedShape;
|
|
copy.x = this.x;
|
|
copy.y = this.y;
|
|
copy.width = this.width;
|
|
copy.height = this.height;
|
|
return copy;
|
|
},
|
|
adjacentTo: function (node) {
|
|
return this.isLinkedTo(node) !== null;
|
|
},
|
|
removeLink: function (link) {
|
|
if (link.source === this) {
|
|
Utils.remove(this.links, link);
|
|
Utils.remove(this.outgoing, link);
|
|
link.source = null;
|
|
}
|
|
if (link.target === this) {
|
|
Utils.remove(this.links, link);
|
|
Utils.remove(this.incoming, link);
|
|
link.target = null;
|
|
}
|
|
},
|
|
hasLinkTo: function (node) {
|
|
return Utils.any(this.outgoing, function (link) {
|
|
return link.target === node;
|
|
});
|
|
},
|
|
degree: function () {
|
|
return this.links.length;
|
|
},
|
|
incidentWith: function (link) {
|
|
return contains(this.links, link);
|
|
},
|
|
getLinksWith: function (node) {
|
|
return Utils.all(this.links, function (link) {
|
|
return link.getComplement(this) === node;
|
|
}, this);
|
|
},
|
|
getNeighbors: function () {
|
|
var neighbors = [];
|
|
Utils.forEach(this.incoming, function (e) {
|
|
neighbors.push(e.getComplement(this));
|
|
}, this);
|
|
Utils.forEach(this.outgoing, function (e) {
|
|
neighbors.push(e.getComplement(this));
|
|
}, this);
|
|
return neighbors;
|
|
}
|
|
});
|
|
var Link = kendo.Class.extend({
|
|
init: function (source, target, id, connection) {
|
|
if (Utils.isUndefined(source)) {
|
|
throw 'The source of the new link is not set.';
|
|
}
|
|
if (Utils.isUndefined(target)) {
|
|
throw 'The target of the new link is not set.';
|
|
}
|
|
var sourceFound, targetFound;
|
|
if (Utils.isString(source)) {
|
|
sourceFound = new Node(source);
|
|
} else {
|
|
sourceFound = source;
|
|
}
|
|
if (Utils.isString(target)) {
|
|
targetFound = new Node(target);
|
|
} else {
|
|
targetFound = target;
|
|
}
|
|
this.source = sourceFound;
|
|
this.target = targetFound;
|
|
this.source.links.push(this);
|
|
this.target.links.push(this);
|
|
this.source.outgoing.push(this);
|
|
this.target.incoming.push(this);
|
|
if (Utils.isDefined(id)) {
|
|
this.id = id;
|
|
} else {
|
|
this.id = randomId();
|
|
}
|
|
if (Utils.isDefined(connection)) {
|
|
this.associatedConnection = connection;
|
|
} else {
|
|
this.associatedConnection = null;
|
|
}
|
|
this.type = 'Link';
|
|
this.shortForm = 'Link \'' + this.source.id + '->' + this.target.id + '\'';
|
|
},
|
|
getComplement: function (node) {
|
|
if (this.source !== node && this.target !== node) {
|
|
throw 'The given node is not incident with this link.';
|
|
}
|
|
return this.source === node ? this.target : this.source;
|
|
},
|
|
getCommonNode: function (link) {
|
|
if (this.source === link.source || this.source === link.target) {
|
|
return this.source;
|
|
}
|
|
if (this.target === link.source || this.target === link.target) {
|
|
return this.target;
|
|
}
|
|
return null;
|
|
},
|
|
isBridging: function (v1, v2) {
|
|
return this.source === v1 && this.target === v2 || this.source === v2 && this.target === v1;
|
|
},
|
|
getNodes: function () {
|
|
return [
|
|
this.source,
|
|
this.target
|
|
];
|
|
},
|
|
incidentWith: function (node) {
|
|
return this.source === node || this.target === node;
|
|
},
|
|
adjacentTo: function (link) {
|
|
return contains(this.source.links, link) || contains(this.target.links, link);
|
|
},
|
|
changeSource: function (node) {
|
|
Utils.remove(this.source.links, this);
|
|
Utils.remove(this.source.outgoing, this);
|
|
node.links.push(this);
|
|
node.outgoing.push(this);
|
|
this.source = node;
|
|
},
|
|
changeTarget: function (node) {
|
|
Utils.remove(this.target.links, this);
|
|
Utils.remove(this.target.incoming, this);
|
|
node.links.push(this);
|
|
node.incoming.push(this);
|
|
this.target = node;
|
|
},
|
|
changesNodes: function (v, w) {
|
|
if (this.source === v) {
|
|
this.changeSource(w);
|
|
} else if (this.target === v) {
|
|
this.changeTarget(w);
|
|
}
|
|
},
|
|
reverse: function () {
|
|
var oldSource = this.source;
|
|
var oldTarget = this.target;
|
|
this.source = oldTarget;
|
|
Utils.remove(oldSource.outgoing, this);
|
|
this.source.outgoing.push(this);
|
|
this.target = oldSource;
|
|
Utils.remove(oldTarget.incoming, this);
|
|
this.target.incoming.push(this);
|
|
return this;
|
|
},
|
|
directTo: function (target) {
|
|
if (this.source !== target && this.target !== target) {
|
|
throw 'The given node is not incident with this link.';
|
|
}
|
|
if (this.target !== target) {
|
|
this.reverse();
|
|
}
|
|
},
|
|
createReverseEdge: function () {
|
|
var r = this.clone();
|
|
r.reverse();
|
|
r.reversed = true;
|
|
return r;
|
|
},
|
|
clone: function () {
|
|
var clone = new Link(this.source, this.target);
|
|
return clone;
|
|
}
|
|
});
|
|
var Graph = kendo.Class.extend({
|
|
init: function (idOrDiagram) {
|
|
this.links = [];
|
|
this.nodes = [];
|
|
this._nodeMap = new Dictionary();
|
|
this.diagram = null;
|
|
this._root = null;
|
|
if (Utils.isDefined(idOrDiagram)) {
|
|
if (Utils.isString(idOrDiagram)) {
|
|
this.id = idOrDiagram;
|
|
} else {
|
|
this.diagram = idOrDiagram;
|
|
this.id = idOrDiagram.id;
|
|
}
|
|
} else {
|
|
this.id = randomId();
|
|
}
|
|
this.bounds = new Rect();
|
|
this._hasCachedRelationships = false;
|
|
this.type = 'Graph';
|
|
},
|
|
cacheRelationships: function (forceRebuild) {
|
|
if (Utils.isUndefined(forceRebuild)) {
|
|
forceRebuild = false;
|
|
}
|
|
if (this._hasCachedRelationships && !forceRebuild) {
|
|
return;
|
|
}
|
|
for (var i = 0, len = this.nodes.length; i < len; i++) {
|
|
var node = this.nodes[i];
|
|
node.children = this.getChildren(node);
|
|
node.parents = this.getParents(node);
|
|
}
|
|
this._hasCachedRelationships = true;
|
|
},
|
|
assignLevels: function (startNode, offset, visited) {
|
|
if (!startNode) {
|
|
throw 'Start node not specified.';
|
|
}
|
|
if (Utils.isUndefined(offset)) {
|
|
offset = 0;
|
|
}
|
|
this.cacheRelationships();
|
|
if (Utils.isUndefined(visited)) {
|
|
visited = new Dictionary();
|
|
Utils.forEach(this.nodes, function (n) {
|
|
visited.add(n, false);
|
|
});
|
|
}
|
|
visited.set(startNode, true);
|
|
startNode.level = offset;
|
|
var children = startNode.children;
|
|
for (var i = 0, len = children.length; i < len; i++) {
|
|
var child = children[i];
|
|
if (!child || visited.get(child)) {
|
|
continue;
|
|
}
|
|
this.assignLevels(child, offset + 1, visited);
|
|
}
|
|
},
|
|
root: function (value) {
|
|
if (Utils.isUndefined(value)) {
|
|
if (!this._root) {
|
|
var found = Utils.first(this.nodes, function (n) {
|
|
return n.incoming.length === 0;
|
|
});
|
|
if (found) {
|
|
return found;
|
|
}
|
|
return Utils.first(this.nodes);
|
|
} else {
|
|
return this._root;
|
|
}
|
|
} else {
|
|
this._root = value;
|
|
}
|
|
},
|
|
getConnectedComponents: function () {
|
|
this.componentIndex = 0;
|
|
this.setItemIndices();
|
|
var componentId = Utils.initArray(this.nodes.length, -1);
|
|
for (var v = 0; v < this.nodes.length; v++) {
|
|
if (componentId[v] === -1) {
|
|
this._collectConnectedNodes(componentId, v);
|
|
this.componentIndex++;
|
|
}
|
|
}
|
|
var components = [], i;
|
|
for (i = 0; i < this.componentIndex; ++i) {
|
|
components[i] = new Graph();
|
|
}
|
|
for (i = 0; i < componentId.length; ++i) {
|
|
var graph = components[componentId[i]];
|
|
graph.addNodeAndOutgoings(this.nodes[i]);
|
|
}
|
|
components.sort(function (a, b) {
|
|
return b.nodes.length - a.nodes.length;
|
|
});
|
|
return components;
|
|
},
|
|
_collectConnectedNodes: function (setIds, nodeIndex) {
|
|
setIds[nodeIndex] = this.componentIndex;
|
|
var node = this.nodes[nodeIndex];
|
|
Utils.forEach(node.links, function (link) {
|
|
var next = link.getComplement(node);
|
|
var nextId = next.index;
|
|
if (setIds[nextId] === -1) {
|
|
this._collectConnectedNodes(setIds, nextId);
|
|
}
|
|
}, this);
|
|
},
|
|
calcBounds: function () {
|
|
if (this.isEmpty()) {
|
|
this.bounds = new Rect();
|
|
return this.bounds;
|
|
}
|
|
var b = null;
|
|
for (var i = 0, len = this.nodes.length; i < len; i++) {
|
|
var node = this.nodes[i];
|
|
if (!b) {
|
|
b = node.bounds();
|
|
} else {
|
|
b = b.union(node.bounds());
|
|
}
|
|
}
|
|
this.bounds = b;
|
|
return this.bounds;
|
|
},
|
|
getSpanningTree: function (root) {
|
|
var tree = new Graph();
|
|
var map = new Dictionary(), source, target;
|
|
tree.root = root.clone();
|
|
tree.root.level = 0;
|
|
tree.root.id = root.id;
|
|
map.add(root, tree.root);
|
|
root.level = 0;
|
|
var visited = [];
|
|
var remaining = [];
|
|
tree._addNode(tree.root);
|
|
visited.push(root);
|
|
remaining.push(root);
|
|
var levelCount = 1;
|
|
while (remaining.length > 0) {
|
|
var next = remaining.pop();
|
|
for (var ni = 0; ni < next.links.length; ni++) {
|
|
var link = next.links[ni];
|
|
var cn = link.getComplement(next);
|
|
if (contains(visited, cn)) {
|
|
continue;
|
|
}
|
|
cn.level = next.level + 1;
|
|
if (levelCount < cn.level + 1) {
|
|
levelCount = cn.level + 1;
|
|
}
|
|
if (!contains(remaining, cn)) {
|
|
remaining.push(cn);
|
|
}
|
|
if (!contains(visited, cn)) {
|
|
visited.push(cn);
|
|
}
|
|
if (map.containsKey(next)) {
|
|
source = map.get(next);
|
|
} else {
|
|
source = next.clone();
|
|
source.level = next.level;
|
|
source.id = next.id;
|
|
map.add(next, source);
|
|
}
|
|
if (map.containsKey(cn)) {
|
|
target = map.get(cn);
|
|
} else {
|
|
target = cn.clone();
|
|
target.level = cn.level;
|
|
target.id = cn.id;
|
|
map.add(cn, target);
|
|
}
|
|
var newLink = new Link(source, target);
|
|
tree.addLink(newLink);
|
|
}
|
|
}
|
|
var treeLevels = [];
|
|
for (var i = 0; i < levelCount; i++) {
|
|
treeLevels.push([]);
|
|
}
|
|
Utils.forEach(tree.nodes, function (node) {
|
|
treeLevels[node.level].push(node);
|
|
});
|
|
tree.treeLevels = treeLevels;
|
|
tree.cacheRelationships();
|
|
return tree;
|
|
},
|
|
takeRandomNode: function (excludedNodes, incidenceLessThan) {
|
|
if (Utils.isUndefined(excludedNodes)) {
|
|
excludedNodes = [];
|
|
}
|
|
if (Utils.isUndefined(incidenceLessThan)) {
|
|
incidenceLessThan = 4;
|
|
}
|
|
if (this.nodes.length === 0) {
|
|
return null;
|
|
}
|
|
if (this.nodes.length === 1) {
|
|
return contains(excludedNodes, this.nodes[0]) ? null : this.nodes[0];
|
|
}
|
|
var pool = $.grep(this.nodes, function (node) {
|
|
return !contains(excludedNodes, node) && node.degree() <= incidenceLessThan;
|
|
});
|
|
if (Utils.isEmpty(pool)) {
|
|
return null;
|
|
}
|
|
return pool[Utils.randomInteger(0, pool.length)];
|
|
},
|
|
isEmpty: function () {
|
|
return Utils.isEmpty(this.nodes);
|
|
},
|
|
isHealthy: function () {
|
|
return Utils.all(this.links, function (link) {
|
|
return contains(this.nodes, link.source) && contains(this.nodes, link.target);
|
|
}, this);
|
|
},
|
|
getParents: function (n) {
|
|
if (!this.hasNode(n)) {
|
|
throw 'The given node is not part of this graph.';
|
|
}
|
|
return n.getParents();
|
|
},
|
|
getChildren: function (n) {
|
|
if (!this.hasNode(n)) {
|
|
throw 'The given node is not part of this graph.';
|
|
}
|
|
return n.getChildren();
|
|
},
|
|
addLink: function (sourceOrLink, target, owner) {
|
|
if (Utils.isUndefined(sourceOrLink)) {
|
|
throw 'The source of the link is not defined.';
|
|
}
|
|
if (Utils.isUndefined(target)) {
|
|
if (Utils.isDefined(sourceOrLink.type) && sourceOrLink.type === 'Link') {
|
|
this.addExistingLink(sourceOrLink);
|
|
return;
|
|
} else {
|
|
throw 'The target of the link is not defined.';
|
|
}
|
|
}
|
|
var foundSource = this.getNode(sourceOrLink);
|
|
if (Utils.isUndefined(foundSource)) {
|
|
foundSource = this.addNode(sourceOrLink);
|
|
}
|
|
var foundTarget = this.getNode(target);
|
|
if (Utils.isUndefined(foundTarget)) {
|
|
foundTarget = this.addNode(target);
|
|
}
|
|
var newLink = new Link(foundSource, foundTarget);
|
|
if (Utils.isDefined(owner)) {
|
|
newLink.owner = owner;
|
|
}
|
|
this.links.push(newLink);
|
|
return newLink;
|
|
},
|
|
removeAllLinks: function () {
|
|
while (this.links.length > 0) {
|
|
var link = this.links[0];
|
|
this.removeLink(link);
|
|
}
|
|
},
|
|
addExistingLink: function (link) {
|
|
if (this.hasLink(link)) {
|
|
return;
|
|
}
|
|
this.links.push(link);
|
|
if (this.hasNode(link.source.id)) {
|
|
var s = this.getNode(link.source.id);
|
|
link.changeSource(s);
|
|
} else {
|
|
this.addNode(link.source);
|
|
}
|
|
if (this.hasNode(link.target.id)) {
|
|
var t = this.getNode(link.target.id);
|
|
link.changeTarget(t);
|
|
} else {
|
|
this.addNode(link.target);
|
|
}
|
|
},
|
|
hasLink: function (linkOrId) {
|
|
if (Utils.isString(linkOrId)) {
|
|
return Utils.any(this.links, function (link) {
|
|
return link.id === linkOrId;
|
|
});
|
|
}
|
|
if (linkOrId.type === 'Link') {
|
|
return contains(this.links, linkOrId);
|
|
}
|
|
throw 'The given object is neither an identifier nor a Link.';
|
|
},
|
|
getNode: function (nodeOrId) {
|
|
var id = nodeOrId.id || nodeOrId;
|
|
if (this._nodeMap.containsKey(id)) {
|
|
return this._nodeMap.get(id);
|
|
}
|
|
},
|
|
hasNode: function (nodeOrId) {
|
|
var id = nodeOrId.id || nodeOrId;
|
|
return this._nodeMap.containsKey(id);
|
|
},
|
|
_addNode: function (node) {
|
|
this.nodes.push(node);
|
|
this._nodeMap.add(node.id, node);
|
|
},
|
|
_removeNode: function (node) {
|
|
Utils.remove(this.nodes, node);
|
|
this._nodeMap.remove(node.id);
|
|
},
|
|
removeNode: function (nodeOrId) {
|
|
var n = nodeOrId;
|
|
if (Utils.isString(nodeOrId)) {
|
|
n = this.getNode(nodeOrId);
|
|
}
|
|
if (Utils.isDefined(n)) {
|
|
var links = n.links;
|
|
n.links = [];
|
|
for (var i = 0, len = links.length; i < len; i++) {
|
|
var link = links[i];
|
|
this.removeLink(link);
|
|
}
|
|
this._removeNode(n);
|
|
} else {
|
|
throw 'The identifier should be a Node or the Id (string) of a node.';
|
|
}
|
|
},
|
|
areConnected: function (n1, n2) {
|
|
return Utils.any(this.links, function (link) {
|
|
return link.source == n1 && link.target == n2 || link.source == n2 && link.target == n1;
|
|
});
|
|
},
|
|
removeLink: function (link) {
|
|
Utils.remove(this.links, link);
|
|
Utils.remove(link.source.outgoing, link);
|
|
Utils.remove(link.source.links, link);
|
|
Utils.remove(link.target.incoming, link);
|
|
Utils.remove(link.target.links, link);
|
|
},
|
|
addNode: function (nodeOrId, layoutRect, owner) {
|
|
var newNode = null;
|
|
if (!Utils.isDefined(nodeOrId)) {
|
|
throw 'No Node or identifier for a new Node is given.';
|
|
}
|
|
if (Utils.isString(nodeOrId)) {
|
|
if (this.hasNode(nodeOrId)) {
|
|
return this.getNode(nodeOrId);
|
|
}
|
|
newNode = new Node(nodeOrId);
|
|
} else {
|
|
if (this.hasNode(nodeOrId)) {
|
|
return this.getNode(nodeOrId);
|
|
}
|
|
newNode = nodeOrId;
|
|
}
|
|
if (Utils.isDefined(layoutRect)) {
|
|
newNode.bounds(layoutRect);
|
|
}
|
|
if (Utils.isDefined(owner)) {
|
|
newNode.owner = owner;
|
|
}
|
|
this._addNode(newNode);
|
|
return newNode;
|
|
},
|
|
addNodeAndOutgoings: function (node) {
|
|
if (!this.hasNode(node)) {
|
|
this._addNode(node);
|
|
}
|
|
var newLinks = node.outgoing;
|
|
node.outgoing = [];
|
|
Utils.forEach(newLinks, function (link) {
|
|
this.addExistingLink(link);
|
|
}, this);
|
|
},
|
|
setItemIndices: function () {
|
|
var i;
|
|
for (i = 0; i < this.nodes.length; ++i) {
|
|
this.nodes[i].index = i;
|
|
}
|
|
for (i = 0; i < this.links.length; ++i) {
|
|
this.links[i].index = i;
|
|
}
|
|
},
|
|
clone: function (saveMapping) {
|
|
var copy = new Graph();
|
|
var save = Utils.isDefined(saveMapping) && saveMapping === true;
|
|
if (save) {
|
|
copy.nodeMap = new Dictionary();
|
|
copy.linkMap = new Dictionary();
|
|
}
|
|
var map = new Dictionary();
|
|
Utils.forEach(this.nodes, function (nOriginal) {
|
|
var nCopy = nOriginal.clone();
|
|
map.set(nOriginal, nCopy);
|
|
copy._addNode(nCopy);
|
|
if (save) {
|
|
copy.nodeMap.set(nCopy, nOriginal);
|
|
}
|
|
});
|
|
Utils.forEach(this.links, function (linkOriginal) {
|
|
if (map.containsKey(linkOriginal.source) && map.containsKey(linkOriginal.target)) {
|
|
var linkCopy = copy.addLink(map.get(linkOriginal.source), map.get(linkOriginal.target));
|
|
if (save) {
|
|
copy.linkMap.set(linkCopy, linkOriginal);
|
|
}
|
|
}
|
|
});
|
|
return copy;
|
|
},
|
|
linearize: function (addIds) {
|
|
return Graph.Utils.linearize(this, addIds);
|
|
},
|
|
depthFirstTraversal: function (startNode, action) {
|
|
if (Utils.isUndefined(startNode)) {
|
|
throw 'You need to supply a starting node.';
|
|
}
|
|
if (Utils.isUndefined(action)) {
|
|
throw 'You need to supply an action.';
|
|
}
|
|
if (!this.hasNode(startNode)) {
|
|
throw 'The given start-node is not part of this graph';
|
|
}
|
|
var foundNode = this.getNode(startNode);
|
|
var visited = [];
|
|
this._dftIterator(foundNode, action, visited);
|
|
},
|
|
_dftIterator: function (node, action, visited) {
|
|
action(node);
|
|
visited.push(node);
|
|
var children = node.getChildren();
|
|
for (var i = 0, len = children.length; i < len; i++) {
|
|
var child = children[i];
|
|
if (contains(visited, child)) {
|
|
continue;
|
|
}
|
|
this._dftIterator(child, action, visited);
|
|
}
|
|
},
|
|
breadthFirstTraversal: function (startNode, action) {
|
|
if (Utils.isUndefined(startNode)) {
|
|
throw 'You need to supply a starting node.';
|
|
}
|
|
if (Utils.isUndefined(action)) {
|
|
throw 'You need to supply an action.';
|
|
}
|
|
if (!this.hasNode(startNode)) {
|
|
throw 'The given start-node is not part of this graph';
|
|
}
|
|
var foundNode = this.getNode(startNode);
|
|
var queue = new Queue();
|
|
var visited = [];
|
|
queue.enqueue(foundNode);
|
|
while (queue.length > 0) {
|
|
var node = queue.dequeue();
|
|
action(node);
|
|
visited.push(node);
|
|
var children = node.getChildren();
|
|
for (var i = 0, len = children.length; i < len; i++) {
|
|
var child = children[i];
|
|
if (contains(visited, child) || contains(queue, child)) {
|
|
continue;
|
|
}
|
|
queue.enqueue(child);
|
|
}
|
|
}
|
|
},
|
|
_stronglyConnectedComponents: function (excludeSingleItems, node, indices, lowLinks, connected, stack, index) {
|
|
indices.add(node, index);
|
|
lowLinks.add(node, index);
|
|
index++;
|
|
stack.push(node);
|
|
var children = node.getChildren(), next;
|
|
for (var i = 0, len = children.length; i < len; i++) {
|
|
next = children[i];
|
|
if (!indices.containsKey(next)) {
|
|
this._stronglyConnectedComponents(excludeSingleItems, next, indices, lowLinks, connected, stack, index);
|
|
lowLinks.add(node, Math.min(lowLinks.get(node), lowLinks.get(next)));
|
|
} else if (contains(stack, next)) {
|
|
lowLinks.add(node, Math.min(lowLinks.get(node), indices.get(next)));
|
|
}
|
|
}
|
|
if (lowLinks.get(node) === indices.get(node)) {
|
|
var component = [];
|
|
do {
|
|
next = stack.pop();
|
|
component.push(next);
|
|
} while (next !== node);
|
|
if (!excludeSingleItems || component.length > 1) {
|
|
connected.push(component);
|
|
}
|
|
}
|
|
},
|
|
findCycles: function (excludeSingleItems) {
|
|
if (Utils.isUndefined(excludeSingleItems)) {
|
|
excludeSingleItems = true;
|
|
}
|
|
var indices = new Dictionary();
|
|
var lowLinks = new Dictionary();
|
|
var connected = [];
|
|
var stack = [];
|
|
for (var i = 0, len = this.nodes.length; i < len; i++) {
|
|
var node = this.nodes[i];
|
|
if (indices.containsKey(node)) {
|
|
continue;
|
|
}
|
|
this._stronglyConnectedComponents(excludeSingleItems, node, indices, lowLinks, connected, stack, 0);
|
|
}
|
|
return connected;
|
|
},
|
|
isAcyclic: function () {
|
|
return Utils.isEmpty(this.findCycles());
|
|
},
|
|
isSubGraph: function (other) {
|
|
var otherArray = other.linearize();
|
|
var thisArray = this.linearize();
|
|
return Utils.all(otherArray, function (s) {
|
|
return contains(thisArray, s);
|
|
});
|
|
},
|
|
makeAcyclic: function () {
|
|
if (this.isEmpty() || this.nodes.length <= 1 || this.links.length <= 1) {
|
|
return [];
|
|
}
|
|
if (this.nodes.length == 2) {
|
|
var result = [];
|
|
if (this.links.length > 1) {
|
|
var oneLink = this.links[0];
|
|
var oneNode = oneLink.source;
|
|
for (var i = 0, len = this.links.length; i < len; i++) {
|
|
var link = this.links[i];
|
|
if (link.source == oneNode) {
|
|
continue;
|
|
}
|
|
var rev = link.reverse();
|
|
result.push(rev);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
var copy = this.clone(true);
|
|
var N = this.nodes.length;
|
|
var intensityCatalog = new Dictionary();
|
|
var flowIntensity = function (node) {
|
|
if (node.outgoing.length === 0) {
|
|
return 2 - N;
|
|
} else if (node.incoming.length === 0) {
|
|
return N - 2;
|
|
} else {
|
|
return node.outgoing.length - node.incoming.length;
|
|
}
|
|
};
|
|
var catalogEqualIntensity = function (node, intensityCatalog) {
|
|
var intensity = flowIntensity(node, N);
|
|
if (!intensityCatalog.containsKey(intensity)) {
|
|
intensityCatalog.set(intensity, []);
|
|
}
|
|
intensityCatalog.get(intensity).push(node);
|
|
};
|
|
Utils.forEach(copy.nodes, function (v) {
|
|
catalogEqualIntensity(v, intensityCatalog);
|
|
});
|
|
var sourceStack = [];
|
|
var targetStack = [];
|
|
while (copy.nodes.length > 0) {
|
|
var source, target, intensity;
|
|
if (intensityCatalog.containsKey(2 - N)) {
|
|
var targets = intensityCatalog.get(2 - N);
|
|
while (targets.length > 0) {
|
|
target = targets.pop();
|
|
for (var li = 0; li < target.links.length; li++) {
|
|
var targetLink = target.links[li];
|
|
source = targetLink.getComplement(target);
|
|
intensity = flowIntensity(source, N);
|
|
Utils.remove(intensityCatalog.get(intensity), source);
|
|
source.removeLink(targetLink);
|
|
catalogEqualIntensity(source, intensityCatalog);
|
|
}
|
|
copy._removeNode(target);
|
|
targetStack.unshift(target);
|
|
}
|
|
}
|
|
if (intensityCatalog.containsKey(N - 2)) {
|
|
var sources = intensityCatalog.get(N - 2);
|
|
while (sources.length > 0) {
|
|
source = sources.pop();
|
|
for (var si = 0; si < source.links.length; si++) {
|
|
var sourceLink = source.links[si];
|
|
target = sourceLink.getComplement(source);
|
|
intensity = flowIntensity(target, N);
|
|
Utils.remove(intensityCatalog.get(intensity), target);
|
|
target.removeLink(sourceLink);
|
|
catalogEqualIntensity(target, intensityCatalog);
|
|
}
|
|
sourceStack.push(source);
|
|
copy._removeNode(source);
|
|
}
|
|
}
|
|
if (copy.nodes.length > 0) {
|
|
for (var k = N - 3; k > 2 - N; k--) {
|
|
if (intensityCatalog.containsKey(k) && intensityCatalog.get(k).length > 0) {
|
|
var maxdiff = intensityCatalog.get(k);
|
|
var v = maxdiff.pop();
|
|
for (var ri = 0; ri < v.links.length; ri++) {
|
|
var ril = v.links[ri];
|
|
var u = ril.getComplement(v);
|
|
intensity = flowIntensity(u, N);
|
|
Utils.remove(intensityCatalog.get(intensity), u);
|
|
u.removeLink(ril);
|
|
catalogEqualIntensity(u, intensityCatalog);
|
|
}
|
|
sourceStack.push(v);
|
|
copy._removeNode(v);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sourceStack = sourceStack.concat(targetStack);
|
|
var vertexOrder = new Dictionary();
|
|
for (var kk = 0; kk < this.nodes.length; kk++) {
|
|
vertexOrder.set(copy.nodeMap.get(sourceStack[kk]), kk);
|
|
}
|
|
var reversedEdges = [];
|
|
Utils.forEach(this.links, function (link) {
|
|
if (vertexOrder.get(link.source) > vertexOrder.get(link.target)) {
|
|
link.reverse();
|
|
reversedEdges.push(link);
|
|
}
|
|
});
|
|
return reversedEdges;
|
|
}
|
|
});
|
|
Graph.Predefined = {
|
|
EightGraph: function () {
|
|
return Graph.Utils.parse([
|
|
'1->2',
|
|
'2->3',
|
|
'3->4',
|
|
'4->1',
|
|
'3->5',
|
|
'5->6',
|
|
'6->7',
|
|
'7->3'
|
|
]);
|
|
},
|
|
Mindmap: function () {
|
|
return Graph.Utils.parse([
|
|
'0->1',
|
|
'0->2',
|
|
'0->3',
|
|
'0->4',
|
|
'0->5',
|
|
'1->6',
|
|
'1->7',
|
|
'7->8',
|
|
'2->9',
|
|
'9->10',
|
|
'9->11',
|
|
'3->12',
|
|
'12->13',
|
|
'13->14',
|
|
'4->15',
|
|
'4->16',
|
|
'15->17',
|
|
'15->18',
|
|
'18->19',
|
|
'18->20',
|
|
'14->21',
|
|
'14->22',
|
|
'5->23',
|
|
'23->24',
|
|
'23->25',
|
|
'6->26'
|
|
]);
|
|
},
|
|
ThreeGraph: function () {
|
|
return Graph.Utils.parse([
|
|
'1->2',
|
|
'2->3',
|
|
'3->1'
|
|
]);
|
|
},
|
|
BinaryTree: function (levels) {
|
|
if (Utils.isUndefined(levels)) {
|
|
levels = 5;
|
|
}
|
|
return Graph.Utils.createBalancedTree(levels, 2);
|
|
},
|
|
Linear: function (length) {
|
|
if (Utils.isUndefined(length)) {
|
|
length = 10;
|
|
}
|
|
return Graph.Utils.createBalancedTree(length, 1);
|
|
},
|
|
Tree: function (levels, siblingsCount) {
|
|
return Graph.Utils.createBalancedTree(levels, siblingsCount);
|
|
},
|
|
Forest: function (levels, siblingsCount, trees) {
|
|
return Graph.Utils.createBalancedForest(levels, siblingsCount, trees);
|
|
},
|
|
Workflow: function () {
|
|
return Graph.Utils.parse([
|
|
'0->1',
|
|
'1->2',
|
|
'2->3',
|
|
'1->4',
|
|
'4->3',
|
|
'3->5',
|
|
'5->6',
|
|
'6->3',
|
|
'6->7',
|
|
'5->4'
|
|
]);
|
|
},
|
|
Grid: function (n, m) {
|
|
var g = new diagram.Graph();
|
|
if (n <= 0 && m <= 0) {
|
|
return g;
|
|
}
|
|
for (var i = 0; i < n + 1; i++) {
|
|
var previous = null;
|
|
for (var j = 0; j < m + 1; j++) {
|
|
var node = new Node(i.toString() + '.' + j.toString());
|
|
g.addNode(node);
|
|
if (previous) {
|
|
g.addLink(previous, node);
|
|
}
|
|
if (i > 0) {
|
|
var left = g.getNode((i - 1).toString() + '.' + j.toString());
|
|
g.addLink(left, node);
|
|
}
|
|
previous = node;
|
|
}
|
|
}
|
|
return g;
|
|
}
|
|
};
|
|
Graph.Utils = {
|
|
parse: function (graphString) {
|
|
var previousLink, graph = new diagram.Graph(), parts = graphString.slice();
|
|
for (var i = 0, len = parts.length; i < len; i++) {
|
|
var part = parts[i];
|
|
if (Utils.isString(part)) {
|
|
if (part.indexOf('->') < 0) {
|
|
throw 'The link should be specified as \'a->b\'.';
|
|
}
|
|
var p = part.split('->');
|
|
if (p.length != 2) {
|
|
throw 'The link should be specified as \'a->b\'.';
|
|
}
|
|
previousLink = new Link(p[0], p[1]);
|
|
graph.addLink(previousLink);
|
|
}
|
|
if (Utils.isObject(part)) {
|
|
if (!previousLink) {
|
|
throw 'Specification found before Link definition.';
|
|
}
|
|
kendo.deepExtend(previousLink, part);
|
|
}
|
|
}
|
|
return graph;
|
|
},
|
|
linearize: function (graph, addIds) {
|
|
if (Utils.isUndefined(graph)) {
|
|
throw 'Expected an instance of a Graph object in slot one.';
|
|
}
|
|
if (Utils.isUndefined(addIds)) {
|
|
addIds = false;
|
|
}
|
|
var lin = [];
|
|
for (var i = 0, len = graph.links.length; i < len; i++) {
|
|
var link = graph.links[i];
|
|
lin.push(link.source.id + '->' + link.target.id);
|
|
if (addIds) {
|
|
lin.push({ id: link.id });
|
|
}
|
|
}
|
|
return lin;
|
|
},
|
|
_addShape: function (kendoDiagram, p, id, shapeDefaults) {
|
|
if (Utils.isUndefined(p)) {
|
|
p = new diagram.Point(0, 0);
|
|
}
|
|
if (Utils.isUndefined(id)) {
|
|
id = randomId();
|
|
}
|
|
shapeDefaults = kendo.deepExtend({
|
|
width: 20,
|
|
height: 20,
|
|
id: id,
|
|
radius: 10,
|
|
fill: '#778899',
|
|
data: 'circle',
|
|
undoable: false,
|
|
x: p.x,
|
|
y: p.y
|
|
}, shapeDefaults);
|
|
return kendoDiagram.addShape(shapeDefaults);
|
|
},
|
|
_addConnection: function (diagram, from, to, options) {
|
|
return diagram.connect(from, to, options);
|
|
},
|
|
createDiagramFromGraph: function (diagram, graph, doLayout, randomSize) {
|
|
if (Utils.isUndefined(diagram)) {
|
|
throw 'The diagram surface is undefined.';
|
|
}
|
|
if (Utils.isUndefined(graph)) {
|
|
throw 'No graph specification defined.';
|
|
}
|
|
if (Utils.isUndefined(doLayout)) {
|
|
doLayout = true;
|
|
}
|
|
if (Utils.isUndefined(randomSize)) {
|
|
randomSize = false;
|
|
}
|
|
var width = diagram.element.clientWidth || 200;
|
|
var height = diagram.element.clientHeight || 200;
|
|
var map = [], node, shape;
|
|
for (var i = 0, len = graph.nodes.length; i < len; i++) {
|
|
node = graph.nodes[i];
|
|
var p = node.position;
|
|
if (Utils.isUndefined(p)) {
|
|
if (Utils.isDefined(node.x) && Utils.isDefined(node.y)) {
|
|
p = new Point(node.x, node.y);
|
|
} else {
|
|
p = new Point(Utils.randomInteger(10, width - 20), Utils.randomInteger(10, height - 20));
|
|
}
|
|
}
|
|
var opt = {};
|
|
if (node.id === '0') {
|
|
} else if (randomSize) {
|
|
kendo.deepExtend(opt, {
|
|
width: Math.random() * 150 + 20,
|
|
height: Math.random() * 80 + 50,
|
|
data: 'rectangle',
|
|
fill: { color: '#778899' }
|
|
});
|
|
}
|
|
shape = this._addShape(diagram, p, node.id, opt);
|
|
var bounds = shape.bounds();
|
|
if (Utils.isDefined(bounds)) {
|
|
node.x = bounds.x;
|
|
node.y = bounds.y;
|
|
node.width = bounds.width;
|
|
node.height = bounds.height;
|
|
}
|
|
map[node.id] = shape;
|
|
}
|
|
for (var gli = 0; gli < graph.links.length; gli++) {
|
|
var link = graph.links[gli];
|
|
var sourceShape = map[link.source.id];
|
|
if (Utils.isUndefined(sourceShape)) {
|
|
continue;
|
|
}
|
|
var targetShape = map[link.target.id];
|
|
if (Utils.isUndefined(targetShape)) {
|
|
continue;
|
|
}
|
|
this._addConnection(diagram, sourceShape, targetShape, { id: link.id });
|
|
}
|
|
if (doLayout) {
|
|
var l = new diagram.SpringLayout(diagram);
|
|
l.layoutGraph(graph, { limitToView: false });
|
|
for (var shi = 0; shi < graph.nodes.length; shi++) {
|
|
node = graph.nodes[shi];
|
|
shape = map[node.id];
|
|
shape.bounds(new Rect(node.x, node.y, node.width, node.height));
|
|
}
|
|
}
|
|
},
|
|
createBalancedTree: function (levels, siblingsCount) {
|
|
if (Utils.isUndefined(levels)) {
|
|
levels = 3;
|
|
}
|
|
if (Utils.isUndefined(siblingsCount)) {
|
|
siblingsCount = 3;
|
|
}
|
|
var g = new diagram.Graph(), counter = -1, lastAdded = [], news;
|
|
if (levels <= 0 || siblingsCount <= 0) {
|
|
return g;
|
|
}
|
|
var root = new Node((++counter).toString());
|
|
g.addNode(root);
|
|
g.root = root;
|
|
lastAdded.push(root);
|
|
for (var i = 0; i < levels; i++) {
|
|
news = [];
|
|
for (var j = 0; j < lastAdded.length; j++) {
|
|
var parent = lastAdded[j];
|
|
for (var k = 0; k < siblingsCount; k++) {
|
|
var item = new Node((++counter).toString());
|
|
g.addLink(parent, item);
|
|
news.push(item);
|
|
}
|
|
}
|
|
lastAdded = news;
|
|
}
|
|
return g;
|
|
},
|
|
createBalancedForest: function (levels, siblingsCount, treeCount) {
|
|
if (Utils.isUndefined(levels)) {
|
|
levels = 3;
|
|
}
|
|
if (Utils.isUndefined(siblingsCount)) {
|
|
siblingsCount = 3;
|
|
}
|
|
if (Utils.isUndefined(treeCount)) {
|
|
treeCount = 5;
|
|
}
|
|
var g = new diagram.Graph(), counter = -1, lastAdded = [], news;
|
|
if (levels <= 0 || siblingsCount <= 0 || treeCount <= 0) {
|
|
return g;
|
|
}
|
|
for (var t = 0; t < treeCount; t++) {
|
|
var root = new Node((++counter).toString());
|
|
g.addNode(root);
|
|
lastAdded = [root];
|
|
for (var i = 0; i < levels; i++) {
|
|
news = [];
|
|
for (var j = 0; j < lastAdded.length; j++) {
|
|
var parent = lastAdded[j];
|
|
for (var k = 0; k < siblingsCount; k++) {
|
|
var item = new Node((++counter).toString());
|
|
g.addLink(parent, item);
|
|
news.push(item);
|
|
}
|
|
}
|
|
lastAdded = news;
|
|
}
|
|
}
|
|
return g;
|
|
},
|
|
createRandomConnectedGraph: function (nodeCount, maxIncidence, isTree) {
|
|
if (Utils.isUndefined(nodeCount)) {
|
|
nodeCount = 40;
|
|
}
|
|
if (Utils.isUndefined(maxIncidence)) {
|
|
maxIncidence = 4;
|
|
}
|
|
if (Utils.isUndefined(isTree)) {
|
|
isTree = false;
|
|
}
|
|
var g = new diagram.Graph(), counter = -1;
|
|
if (nodeCount <= 0) {
|
|
return g;
|
|
}
|
|
var root = new Node((++counter).toString());
|
|
g.addNode(root);
|
|
if (nodeCount === 1) {
|
|
return g;
|
|
}
|
|
if (nodeCount > 1) {
|
|
for (var i = 1; i < nodeCount; i++) {
|
|
var poolNode = g.takeRandomNode([], maxIncidence);
|
|
if (!poolNode) {
|
|
break;
|
|
}
|
|
var newNode = g.addNode(i.toString());
|
|
g.addLink(poolNode, newNode);
|
|
}
|
|
if (!isTree && nodeCount > 1) {
|
|
var randomAdditions = Utils.randomInteger(1, nodeCount);
|
|
for (var ri = 0; ri < randomAdditions; ri++) {
|
|
var n1 = g.takeRandomNode([], maxIncidence);
|
|
var n2 = g.takeRandomNode([], maxIncidence);
|
|
if (n1 && n2 && !g.areConnected(n1, n2)) {
|
|
g.addLink(n1, n2);
|
|
}
|
|
}
|
|
}
|
|
return g;
|
|
}
|
|
},
|
|
randomDiagram: function (diagram, shapeCount, maxIncidence, isTree, randomSize) {
|
|
var g = kendo.dataviz.diagram.Graph.Utils.createRandomConnectedGraph(shapeCount, maxIncidence, isTree);
|
|
Graph.Utils.createDiagramFromGraph(diagram, g, false, randomSize);
|
|
}
|
|
};
|
|
kendo.deepExtend(diagram, {
|
|
init: function (element) {
|
|
kendo.init(element, diagram.ui);
|
|
},
|
|
Point: Point,
|
|
Intersect: Intersect,
|
|
Geometry: Geometry,
|
|
Rect: Rect,
|
|
Size: Size,
|
|
RectAlign: RectAlign,
|
|
Matrix: Matrix,
|
|
MatrixVector: MatrixVector,
|
|
normalVariable: normalVariable,
|
|
randomId: randomId,
|
|
Dictionary: Dictionary,
|
|
HashTable: HashTable,
|
|
Queue: Queue,
|
|
Set: Set,
|
|
Node: Node,
|
|
Link: Link,
|
|
Graph: Graph,
|
|
PathDefiner: PathDefiner
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/svg', [
|
|
'kendo.drawing',
|
|
'dataviz/diagram/math'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Class = kendo.Class, deepExtend = kendo.deepExtend, Point = diagram.Point, Rect = diagram.Rect, Matrix = diagram.Matrix, Utils = diagram.Utils, isNumber = Utils.isNumber, isString = Utils.isString, MatrixVector = diagram.MatrixVector, g = kendo.geometry, d = kendo.drawing, defined = kendo.util.defined, inArray = $.inArray;
|
|
var TRANSPARENT = 'transparent', Markers = {
|
|
none: 'none',
|
|
arrowStart: 'ArrowStart',
|
|
filledCircle: 'FilledCircle',
|
|
arrowEnd: 'ArrowEnd'
|
|
}, FULL_CIRCLE_ANGLE = 360, START = 'start', END = 'end', WIDTH = 'width', HEIGHT = 'height', X = 'x', Y = 'y';
|
|
diagram.Markers = Markers;
|
|
function diffNumericOptions(options, fields) {
|
|
var elementOptions = this.options;
|
|
var hasChanges = false;
|
|
var value, field;
|
|
for (var i = 0; i < fields.length; i++) {
|
|
field = fields[i];
|
|
value = options[field];
|
|
if (isNumber(value) && elementOptions[field] !== value) {
|
|
elementOptions[field] = value;
|
|
hasChanges = true;
|
|
}
|
|
}
|
|
return hasChanges;
|
|
}
|
|
var Scale = Class.extend({
|
|
init: function (x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
},
|
|
toMatrix: function () {
|
|
return Matrix.scaling(this.x, this.y);
|
|
},
|
|
toString: function () {
|
|
return kendo.format('scale({0},{1})', this.x, this.y);
|
|
},
|
|
invert: function () {
|
|
return new Scale(1 / this.x, 1 / this.y);
|
|
}
|
|
});
|
|
var Translation = Class.extend({
|
|
init: function (x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
},
|
|
toMatrixVector: function () {
|
|
return new MatrixVector(0, 0, 0, 0, this.x, this.y);
|
|
},
|
|
toMatrix: function () {
|
|
return Matrix.translation(this.x, this.y);
|
|
},
|
|
toString: function () {
|
|
return kendo.format('translate({0},{1})', this.x, this.y);
|
|
},
|
|
plus: function (delta) {
|
|
this.x += delta.x;
|
|
this.y += delta.y;
|
|
},
|
|
times: function (factor) {
|
|
this.x *= factor;
|
|
this.y *= factor;
|
|
},
|
|
length: function () {
|
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
},
|
|
normalize: function () {
|
|
if (this.Length === 0) {
|
|
return;
|
|
}
|
|
this.times(1 / this.length());
|
|
},
|
|
invert: function () {
|
|
return new Translation(-this.x, -this.y);
|
|
}
|
|
});
|
|
var Rotation = Class.extend({
|
|
init: function (angle, x, y) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
this.angle = angle;
|
|
},
|
|
toString: function () {
|
|
if (this.x && this.y) {
|
|
return kendo.format('rotate({0},{1},{2})', this.angle, this.x, this.y);
|
|
} else {
|
|
return kendo.format('rotate({0})', this.angle);
|
|
}
|
|
},
|
|
toMatrix: function () {
|
|
return Matrix.rotation(this.angle, this.x, this.y);
|
|
},
|
|
center: function () {
|
|
return new Point(this.x, this.y);
|
|
},
|
|
invert: function () {
|
|
return new Rotation(FULL_CIRCLE_ANGLE - this.angle, this.x, this.y);
|
|
}
|
|
});
|
|
Rotation.ZERO = new Rotation(0);
|
|
Rotation.create = function (rotation) {
|
|
return new Rotation(rotation.angle, rotation.x, rotation.y);
|
|
};
|
|
Rotation.parse = function (str) {
|
|
var values = str.slice(1, str.length - 1).split(','), angle = values[0], x = values[1], y = values[2];
|
|
var rotation = new Rotation(angle, x, y);
|
|
return rotation;
|
|
};
|
|
var CompositeTransform = Class.extend({
|
|
init: function (x, y, scaleX, scaleY, angle, center) {
|
|
this.translate = new Translation(x, y);
|
|
if (scaleX !== undefined && scaleY !== undefined) {
|
|
this.scale = new Scale(scaleX, scaleY);
|
|
}
|
|
if (angle !== undefined) {
|
|
this.rotate = center ? new Rotation(angle, center.x, center.y) : new Rotation(angle);
|
|
}
|
|
},
|
|
toString: function () {
|
|
var toString = function (transform) {
|
|
return transform ? transform.toString() : '';
|
|
};
|
|
return toString(this.translate) + toString(this.rotate) + toString(this.scale);
|
|
},
|
|
render: function (visual) {
|
|
visual._transform = this;
|
|
visual._renderTransform();
|
|
},
|
|
toMatrix: function () {
|
|
var m = Matrix.unit();
|
|
if (this.translate) {
|
|
m = m.times(this.translate.toMatrix());
|
|
}
|
|
if (this.rotate) {
|
|
m = m.times(this.rotate.toMatrix());
|
|
}
|
|
if (this.scale) {
|
|
m = m.times(this.scale.toMatrix());
|
|
}
|
|
return m;
|
|
},
|
|
invert: function () {
|
|
var rotate = this.rotate ? this.rotate.invert() : undefined, rotateMatrix = rotate ? rotate.toMatrix() : Matrix.unit(), scale = this.scale ? this.scale.invert() : undefined, scaleMatrix = scale ? scale.toMatrix() : Matrix.unit();
|
|
var translatePoint = new Point(-this.translate.x, -this.translate.y);
|
|
translatePoint = rotateMatrix.times(scaleMatrix).apply(translatePoint);
|
|
var translate = new Translation(translatePoint.x, translatePoint.y);
|
|
var transform = new CompositeTransform();
|
|
transform.translate = translate;
|
|
transform.rotate = rotate;
|
|
transform.scale = scale;
|
|
return transform;
|
|
}
|
|
});
|
|
var AutoSizeableMixin = {
|
|
_setScale: function () {
|
|
var options = this.options;
|
|
var originWidth = this._originWidth;
|
|
var originHeight = this._originHeight;
|
|
var scaleX = options.width / originWidth;
|
|
var scaleY = options.height / originHeight;
|
|
if (!isNumber(scaleX)) {
|
|
scaleX = 1;
|
|
}
|
|
if (!isNumber(scaleY)) {
|
|
scaleY = 1;
|
|
}
|
|
this._transform.scale = new Scale(scaleX, scaleY);
|
|
},
|
|
_setTranslate: function () {
|
|
var options = this.options;
|
|
var x = options.x || 0;
|
|
var y = options.y || 0;
|
|
this._transform.translate = new Translation(x, y);
|
|
},
|
|
_initSize: function () {
|
|
var options = this.options;
|
|
var transform = false;
|
|
if (options.autoSize !== false && (defined(options.width) || defined(options.height))) {
|
|
this._measure(true);
|
|
this._setScale();
|
|
transform = true;
|
|
}
|
|
if (defined(options.x) || defined(options.y)) {
|
|
this._setTranslate();
|
|
transform = true;
|
|
}
|
|
if (transform) {
|
|
this._renderTransform();
|
|
}
|
|
},
|
|
_updateSize: function (options) {
|
|
var update = false;
|
|
if (this.options.autoSize !== false && this._diffNumericOptions(options, [
|
|
WIDTH,
|
|
HEIGHT
|
|
])) {
|
|
update = true;
|
|
this._measure(true);
|
|
this._setScale();
|
|
}
|
|
if (this._diffNumericOptions(options, [
|
|
X,
|
|
Y
|
|
])) {
|
|
update = true;
|
|
this._setTranslate();
|
|
}
|
|
if (update) {
|
|
this._renderTransform();
|
|
}
|
|
return update;
|
|
}
|
|
};
|
|
var Element = Class.extend({
|
|
init: function (options) {
|
|
var element = this;
|
|
element.options = deepExtend({}, element.options, options);
|
|
element.id = element.options.id;
|
|
element._originSize = Rect.empty();
|
|
element._transform = new CompositeTransform();
|
|
},
|
|
visible: function (value) {
|
|
return this.drawingContainer().visible(value);
|
|
},
|
|
redraw: function (options) {
|
|
if (options && options.id) {
|
|
this.id = options.id;
|
|
}
|
|
},
|
|
position: function (x, y) {
|
|
var options = this.options;
|
|
if (!defined(x)) {
|
|
return new Point(options.x, options.y);
|
|
}
|
|
if (defined(y)) {
|
|
options.x = x;
|
|
options.y = y;
|
|
} else if (x instanceof Point) {
|
|
options.x = x.x;
|
|
options.y = x.y;
|
|
}
|
|
this._transform.translate = new Translation(options.x, options.y);
|
|
this._renderTransform();
|
|
},
|
|
rotate: function (angle, center) {
|
|
if (defined(angle)) {
|
|
this._transform.rotate = new Rotation(angle, center.x, center.y);
|
|
this._renderTransform();
|
|
}
|
|
return this._transform.rotate || Rotation.ZERO;
|
|
},
|
|
drawingContainer: function () {
|
|
return this.drawingElement;
|
|
},
|
|
_renderTransform: function () {
|
|
var matrix = this._transform.toMatrix();
|
|
this.drawingContainer().transform(new g.Matrix(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f));
|
|
},
|
|
_hover: function () {
|
|
},
|
|
_diffNumericOptions: diffNumericOptions,
|
|
_measure: function (force) {
|
|
var rect;
|
|
if (!this._measured || force) {
|
|
var box = this._boundingBox() || new g.Rect();
|
|
var startPoint = box.topLeft();
|
|
rect = new Rect(startPoint.x, startPoint.y, box.width(), box.height());
|
|
this._originSize = rect;
|
|
this._originWidth = rect.width;
|
|
this._originHeight = rect.height;
|
|
this._measured = true;
|
|
} else {
|
|
rect = this._originSize;
|
|
}
|
|
return rect;
|
|
},
|
|
_boundingBox: function () {
|
|
return this.drawingElement.rawBBox();
|
|
}
|
|
});
|
|
var VisualBase = Element.extend({
|
|
init: function (options) {
|
|
Element.fn.init.call(this, options);
|
|
options = this.options;
|
|
options.fill = normalizeDrawingOptions(options.fill);
|
|
options.stroke = normalizeDrawingOptions(options.stroke);
|
|
},
|
|
options: {
|
|
stroke: {
|
|
color: 'gray',
|
|
width: 1
|
|
},
|
|
fill: { color: TRANSPARENT }
|
|
},
|
|
fill: function (color, opacity) {
|
|
this._fill({
|
|
color: getColor(color),
|
|
opacity: opacity
|
|
});
|
|
},
|
|
stroke: function (color, width, opacity) {
|
|
this._stroke({
|
|
color: getColor(color),
|
|
width: width,
|
|
opacity: opacity
|
|
});
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
var stroke = options.stroke;
|
|
var fill = options.fill;
|
|
if (stroke) {
|
|
this._stroke(normalizeDrawingOptions(stroke));
|
|
}
|
|
if (fill) {
|
|
this._fill(normalizeDrawingOptions(fill));
|
|
}
|
|
Element.fn.redraw.call(this, options);
|
|
}
|
|
},
|
|
_hover: function (show) {
|
|
var drawingElement = this.drawingElement;
|
|
var options = this.options;
|
|
var hover = options.hover;
|
|
if (hover && hover.fill) {
|
|
var fill = show ? normalizeDrawingOptions(hover.fill) : options.fill;
|
|
drawingElement.fill(fill.color, fill.opacity);
|
|
}
|
|
},
|
|
_stroke: function (strokeOptions) {
|
|
var options = this.options;
|
|
deepExtend(options, { stroke: strokeOptions });
|
|
strokeOptions = options.stroke;
|
|
var stroke = null;
|
|
if (strokeOptions.width > 0) {
|
|
stroke = {
|
|
color: strokeOptions.color,
|
|
width: strokeOptions.width,
|
|
opacity: strokeOptions.opacity,
|
|
dashType: strokeOptions.dashType
|
|
};
|
|
}
|
|
this.drawingElement.options.set('stroke', stroke);
|
|
},
|
|
_fill: function (fillOptions) {
|
|
var options = this.options;
|
|
deepExtend(options, { fill: fillOptions || {} });
|
|
var fill = options.fill;
|
|
if (fill.gradient) {
|
|
var gradient = fill.gradient;
|
|
var GradientClass = gradient.type === 'radial' ? d.RadialGradient : d.LinearGradient;
|
|
this.drawingElement.fill(new GradientClass(gradient));
|
|
} else {
|
|
this.drawingElement.fill(fill.color, fill.opacity);
|
|
}
|
|
}
|
|
});
|
|
var TextBlock = VisualBase.extend({
|
|
init: function (options) {
|
|
options = this._textColor(options);
|
|
VisualBase.fn.init.call(this, options);
|
|
this._font();
|
|
this._initText();
|
|
this._initSize();
|
|
},
|
|
options: {
|
|
fontSize: 15,
|
|
fontFamily: 'sans-serif',
|
|
stroke: { width: 0 },
|
|
fill: { color: 'black' },
|
|
autoSize: true
|
|
},
|
|
_initText: function () {
|
|
var options = this.options;
|
|
this.drawingElement = new d.Text(defined(options.text) ? options.text : '', new g.Point(), { font: options.font });
|
|
this._fill();
|
|
this._stroke();
|
|
},
|
|
_textColor: function (options) {
|
|
if (options && options.color) {
|
|
options = deepExtend({}, options, { fill: { color: options.color } });
|
|
}
|
|
return options;
|
|
},
|
|
_font: function () {
|
|
var options = this.options;
|
|
if (options.fontFamily && defined(options.fontSize)) {
|
|
options.font = options.fontSize + 'px ' + options.fontFamily;
|
|
} else {
|
|
delete options.font;
|
|
}
|
|
},
|
|
content: function (text) {
|
|
return this.drawingElement.content(text);
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
var sizeChanged = false;
|
|
var textOptions = this.options;
|
|
options = this._textColor(options);
|
|
VisualBase.fn.redraw.call(this, options);
|
|
if (options.fontFamily || defined(options.fontSize)) {
|
|
deepExtend(textOptions, {
|
|
fontFamily: options.fontFamily,
|
|
fontSize: options.fontSize
|
|
});
|
|
this._font();
|
|
this.drawingElement.options.set('font', textOptions.font);
|
|
sizeChanged = true;
|
|
}
|
|
if (options.text) {
|
|
this.content(options.text);
|
|
sizeChanged = true;
|
|
}
|
|
if (!this._updateSize(options) && sizeChanged) {
|
|
this._initSize();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
deepExtend(TextBlock.fn, AutoSizeableMixin);
|
|
var Rectangle = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
this._initPath();
|
|
this._setPosition();
|
|
},
|
|
_setPosition: function () {
|
|
var options = this.options;
|
|
var x = options.x;
|
|
var y = options.y;
|
|
if (defined(x) || defined(y)) {
|
|
this.position(x || 0, y || 0);
|
|
}
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
VisualBase.fn.redraw.call(this, options);
|
|
if (this._diffNumericOptions(options, [
|
|
WIDTH,
|
|
HEIGHT
|
|
])) {
|
|
this._drawPath();
|
|
}
|
|
if (this._diffNumericOptions(options, [
|
|
X,
|
|
Y
|
|
])) {
|
|
this._setPosition();
|
|
}
|
|
}
|
|
},
|
|
_initPath: function () {
|
|
var options = this.options;
|
|
this.drawingElement = new d.Path({
|
|
stroke: options.stroke,
|
|
closed: true
|
|
});
|
|
this._fill();
|
|
this._drawPath();
|
|
},
|
|
_drawPath: function () {
|
|
var drawingElement = this.drawingElement;
|
|
var sizeOptions = sizeOptionsOrDefault(this.options);
|
|
var width = sizeOptions.width;
|
|
var height = sizeOptions.height;
|
|
drawingElement.segments.elements([
|
|
createSegment(0, 0),
|
|
createSegment(width, 0),
|
|
createSegment(width, height),
|
|
createSegment(0, height)
|
|
]);
|
|
}
|
|
});
|
|
var MarkerBase = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
var anchor = this.options.anchor;
|
|
this.anchor = new g.Point(anchor.x, anchor.y);
|
|
this.createElement();
|
|
},
|
|
options: {
|
|
stroke: {
|
|
color: TRANSPARENT,
|
|
width: 0
|
|
},
|
|
fill: { color: 'black' }
|
|
},
|
|
_transformToPath: function (point, path) {
|
|
var transform = path.transform();
|
|
if (point && transform) {
|
|
point = point.transformCopy(transform);
|
|
}
|
|
return point;
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
if (options.position) {
|
|
this.options.position = options.position;
|
|
}
|
|
VisualBase.fn.redraw.call(this, options);
|
|
}
|
|
}
|
|
});
|
|
var CircleMarker = MarkerBase.extend({
|
|
options: {
|
|
radius: 4,
|
|
anchor: {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
},
|
|
createElement: function () {
|
|
var options = this.options;
|
|
this.drawingElement = new d.Circle(new g.Circle(this.anchor, options.radius), {
|
|
fill: options.fill,
|
|
stroke: options.stroke
|
|
});
|
|
},
|
|
positionMarker: function (path) {
|
|
var options = this.options;
|
|
var position = options.position;
|
|
var segments = path.segments;
|
|
var targetSegment;
|
|
var point;
|
|
if (position == START) {
|
|
targetSegment = segments[0];
|
|
} else {
|
|
targetSegment = segments[segments.length - 1];
|
|
}
|
|
if (targetSegment) {
|
|
point = this._transformToPath(targetSegment.anchor(), path);
|
|
this.drawingElement.transform(g.transform().translate(point.x, point.y));
|
|
}
|
|
}
|
|
});
|
|
var ArrowMarker = MarkerBase.extend({
|
|
options: {
|
|
path: 'M 0 0 L 10 5 L 0 10 L 3 5 z',
|
|
anchor: {
|
|
x: 10,
|
|
y: 5
|
|
}
|
|
},
|
|
createElement: function () {
|
|
var options = this.options;
|
|
this.drawingElement = d.Path.parse(options.path, {
|
|
fill: options.fill,
|
|
stroke: options.stroke
|
|
});
|
|
},
|
|
positionMarker: function (path) {
|
|
var points = this._linePoints(path);
|
|
var start = points.start;
|
|
var end = points.end;
|
|
var transform = g.transform();
|
|
if (start) {
|
|
transform.rotate(lineAngle(start, end), end);
|
|
}
|
|
if (end) {
|
|
var anchor = this.anchor;
|
|
var translate = end.clone().translate(-anchor.x, -anchor.y);
|
|
transform.translate(translate.x, translate.y);
|
|
}
|
|
this.drawingElement.transform(transform);
|
|
},
|
|
_linePoints: function (path) {
|
|
var options = this.options;
|
|
var segments = path.segments;
|
|
var startPoint, endPoint, targetSegment;
|
|
if (options.position == START) {
|
|
targetSegment = segments[0];
|
|
if (targetSegment) {
|
|
endPoint = targetSegment.anchor();
|
|
startPoint = targetSegment.controlOut();
|
|
var nextSegment = segments[1];
|
|
if (!startPoint && nextSegment) {
|
|
startPoint = nextSegment.anchor();
|
|
}
|
|
}
|
|
} else {
|
|
targetSegment = segments[segments.length - 1];
|
|
if (targetSegment) {
|
|
endPoint = targetSegment.anchor();
|
|
startPoint = targetSegment.controlIn();
|
|
var prevSegment = segments[segments.length - 2];
|
|
if (!startPoint && prevSegment) {
|
|
startPoint = prevSegment.anchor();
|
|
}
|
|
}
|
|
}
|
|
if (endPoint) {
|
|
return {
|
|
start: this._transformToPath(startPoint, path),
|
|
end: this._transformToPath(endPoint, path)
|
|
};
|
|
}
|
|
}
|
|
});
|
|
var MarkerPathMixin = {
|
|
_getPath: function (position) {
|
|
var path = this.drawingElement;
|
|
if (path instanceof d.MultiPath) {
|
|
if (position == START) {
|
|
path = path.paths[0];
|
|
} else {
|
|
path = path.paths[path.paths.length - 1];
|
|
}
|
|
}
|
|
if (path && path.segments.length) {
|
|
return path;
|
|
}
|
|
},
|
|
_normalizeMarkerOptions: function (options) {
|
|
var startCap = options.startCap;
|
|
var endCap = options.endCap;
|
|
if (isString(startCap)) {
|
|
options.startCap = { type: startCap };
|
|
}
|
|
if (isString(endCap)) {
|
|
options.endCap = { type: endCap };
|
|
}
|
|
},
|
|
_removeMarker: function (position) {
|
|
var marker = this._markers[position];
|
|
if (marker) {
|
|
this.drawingContainer().remove(marker.drawingElement);
|
|
delete this._markers[position];
|
|
}
|
|
},
|
|
_createMarkers: function () {
|
|
var options = this.options;
|
|
this._normalizeMarkerOptions(options);
|
|
this._markers = {};
|
|
this._markers[START] = this._createMarker(options.startCap, START);
|
|
this._markers[END] = this._createMarker(options.endCap, END);
|
|
},
|
|
_createMarker: function (options, position) {
|
|
var type = (options || {}).type;
|
|
var path = this._getPath(position);
|
|
var markerType, marker;
|
|
if (!path) {
|
|
this._removeMarker(position);
|
|
return;
|
|
}
|
|
if (type == Markers.filledCircle) {
|
|
markerType = CircleMarker;
|
|
} else if (type == Markers.arrowStart || type == Markers.arrowEnd) {
|
|
markerType = ArrowMarker;
|
|
} else {
|
|
this._removeMarker(position);
|
|
}
|
|
if (markerType) {
|
|
marker = new markerType(deepExtend({}, options, { position: position }));
|
|
marker.positionMarker(path);
|
|
this.drawingContainer().append(marker.drawingElement);
|
|
return marker;
|
|
}
|
|
},
|
|
_positionMarker: function (position) {
|
|
var marker = this._markers[position];
|
|
if (marker) {
|
|
var path = this._getPath(position);
|
|
if (path) {
|
|
marker.positionMarker(path);
|
|
} else {
|
|
this._removeMarker(position);
|
|
}
|
|
}
|
|
},
|
|
_capMap: {
|
|
start: 'startCap',
|
|
end: 'endCap'
|
|
},
|
|
_redrawMarker: function (pathChange, position, options) {
|
|
this._normalizeMarkerOptions(options);
|
|
var pathOptions = this.options;
|
|
var cap = this._capMap[position];
|
|
var pathCapType = (pathOptions[cap] || {}).type;
|
|
var optionsCap = options[cap];
|
|
var created = false;
|
|
if (optionsCap) {
|
|
pathOptions[cap] = deepExtend({}, pathOptions[cap], optionsCap);
|
|
if (optionsCap.type && pathCapType != optionsCap.type) {
|
|
this._removeMarker(position);
|
|
this._markers[position] = this._createMarker(pathOptions[cap], position);
|
|
created = true;
|
|
} else if (this._markers[position]) {
|
|
this._markers[position].redraw(optionsCap);
|
|
}
|
|
} else if (pathChange && !this._markers[position] && pathOptions[cap]) {
|
|
this._markers[position] = this._createMarker(pathOptions[cap], position);
|
|
created = true;
|
|
}
|
|
return created;
|
|
},
|
|
_redrawMarkers: function (pathChange, options) {
|
|
if (!this._redrawMarker(pathChange, START, options) && pathChange) {
|
|
this._positionMarker(START);
|
|
}
|
|
if (!this._redrawMarker(pathChange, END, options) && pathChange) {
|
|
this._positionMarker(END);
|
|
}
|
|
}
|
|
};
|
|
var Path = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
this.container = new d.Group();
|
|
this._createElements();
|
|
this._initSize();
|
|
},
|
|
options: { autoSize: true },
|
|
drawingContainer: function () {
|
|
return this.container;
|
|
},
|
|
data: function (value) {
|
|
var options = this.options;
|
|
if (value) {
|
|
if (options.data != value) {
|
|
options.data = value;
|
|
this._setData(value);
|
|
this._initSize();
|
|
this._redrawMarkers(true, {});
|
|
}
|
|
} else {
|
|
return options.data;
|
|
}
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
VisualBase.fn.redraw.call(this, options);
|
|
var pathOptions = this.options;
|
|
var data = options.data;
|
|
if (defined(data) && pathOptions.data != data) {
|
|
pathOptions.data = data;
|
|
this._setData(data);
|
|
if (!this._updateSize(options)) {
|
|
this._initSize();
|
|
}
|
|
this._redrawMarkers(true, options);
|
|
} else {
|
|
this._updateSize(options);
|
|
this._redrawMarkers(false, options);
|
|
}
|
|
}
|
|
},
|
|
_createElements: function () {
|
|
var options = this.options;
|
|
this.drawingElement = d.Path.parse(options.data || '', { stroke: options.stroke });
|
|
this._fill();
|
|
this.container.append(this.drawingElement);
|
|
this._createMarkers();
|
|
},
|
|
_setData: function (data) {
|
|
var drawingElement = this.drawingElement;
|
|
var multipath = d.Path.parse(data || '');
|
|
var paths = multipath.paths.slice(0);
|
|
multipath.paths.elements([]);
|
|
drawingElement.paths.elements(paths);
|
|
}
|
|
});
|
|
deepExtend(Path.fn, AutoSizeableMixin);
|
|
deepExtend(Path.fn, MarkerPathMixin);
|
|
var Line = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
this.container = new d.Group();
|
|
this._initPath();
|
|
this._createMarkers();
|
|
},
|
|
drawingContainer: function () {
|
|
return this.container;
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
options = options || {};
|
|
var from = options.from;
|
|
var to = options.to;
|
|
if (from) {
|
|
this.options.from = from;
|
|
}
|
|
if (to) {
|
|
this.options.to = to;
|
|
}
|
|
if (from || to) {
|
|
this._drawPath();
|
|
this._redrawMarkers(true, options);
|
|
} else {
|
|
this._redrawMarkers(false, options);
|
|
}
|
|
VisualBase.fn.redraw.call(this, options);
|
|
}
|
|
},
|
|
_initPath: function () {
|
|
var options = this.options;
|
|
var drawingElement = this.drawingElement = new d.Path({ stroke: options.stroke });
|
|
this._fill();
|
|
this._drawPath();
|
|
this.container.append(drawingElement);
|
|
},
|
|
_drawPath: function () {
|
|
var options = this.options;
|
|
var drawingElement = this.drawingElement;
|
|
var from = options.from || new Point();
|
|
var to = options.to || new Point();
|
|
drawingElement.segments.elements([
|
|
createSegment(from.x, from.y),
|
|
createSegment(to.x, to.y)
|
|
]);
|
|
}
|
|
});
|
|
deepExtend(Line.fn, MarkerPathMixin);
|
|
var Polyline = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
this.container = new d.Group();
|
|
this._initPath();
|
|
this._createMarkers();
|
|
},
|
|
drawingContainer: function () {
|
|
return this.container;
|
|
},
|
|
points: function (points) {
|
|
var options = this.options;
|
|
if (points) {
|
|
options.points = points;
|
|
this._updatePath();
|
|
} else {
|
|
return options.points;
|
|
}
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
var points = options.points;
|
|
VisualBase.fn.redraw.call(this, options);
|
|
if (points && this._pointsDiffer(points)) {
|
|
this.points(points);
|
|
this._redrawMarkers(true, options);
|
|
} else {
|
|
this._redrawMarkers(false, options);
|
|
}
|
|
}
|
|
},
|
|
_initPath: function () {
|
|
var options = this.options;
|
|
this.drawingElement = new d.Path({ stroke: options.stroke });
|
|
this._fill();
|
|
this.container.append(this.drawingElement);
|
|
if (options.points) {
|
|
this._updatePath();
|
|
}
|
|
},
|
|
_pointsDiffer: function (points) {
|
|
var currentPoints = this.options.points;
|
|
var differ = currentPoints.length !== points.length;
|
|
if (!differ) {
|
|
for (var i = 0; i < points.length; i++) {
|
|
if (currentPoints[i].x !== points[i].x || currentPoints[i].y !== points[i].y) {
|
|
differ = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return differ;
|
|
},
|
|
_updatePath: function () {
|
|
var drawingElement = this.drawingElement;
|
|
var options = this.options;
|
|
var points = options.points;
|
|
var segments = [];
|
|
var point;
|
|
for (var i = 0; i < points.length; i++) {
|
|
point = points[i];
|
|
segments.push(createSegment(point.x, point.y));
|
|
}
|
|
drawingElement.segments.elements(segments);
|
|
},
|
|
options: { points: [] }
|
|
});
|
|
deepExtend(Polyline.fn, MarkerPathMixin);
|
|
var Image = Element.extend({
|
|
init: function (options) {
|
|
Element.fn.init.call(this, options);
|
|
this._initImage();
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
if (options.source) {
|
|
this.drawingElement.src(options.source);
|
|
}
|
|
if (this._diffNumericOptions(options, [
|
|
WIDTH,
|
|
HEIGHT,
|
|
X,
|
|
Y
|
|
])) {
|
|
this.drawingElement.rect(this._rect());
|
|
}
|
|
Element.fn.redraw.call(this, options);
|
|
}
|
|
},
|
|
_initImage: function () {
|
|
var options = this.options;
|
|
var rect = this._rect();
|
|
this.drawingElement = new d.Image(options.source, rect, {});
|
|
},
|
|
_rect: function () {
|
|
var sizeOptions = sizeOptionsOrDefault(this.options);
|
|
var origin = new g.Point(sizeOptions.x, sizeOptions.y);
|
|
var size = new g.Size(sizeOptions.width, sizeOptions.height);
|
|
return new g.Rect(origin, size);
|
|
}
|
|
});
|
|
var Group = Element.extend({
|
|
init: function (options) {
|
|
this.children = [];
|
|
Element.fn.init.call(this, options);
|
|
this.drawingElement = new d.Group();
|
|
this._initSize();
|
|
},
|
|
options: { autoSize: false },
|
|
append: function (visual) {
|
|
this.drawingElement.append(visual.drawingContainer());
|
|
this.children.push(visual);
|
|
this._childrenChange = true;
|
|
},
|
|
remove: function (visual) {
|
|
if (this._remove(visual)) {
|
|
this._childrenChange = true;
|
|
}
|
|
},
|
|
_remove: function (visual) {
|
|
var index = inArray(visual, this.children);
|
|
if (index >= 0) {
|
|
this.drawingElement.removeAt(index);
|
|
this.children.splice(index, 1);
|
|
return true;
|
|
}
|
|
},
|
|
clear: function () {
|
|
this.drawingElement.clear();
|
|
this.children = [];
|
|
this._childrenChange = true;
|
|
},
|
|
toFront: function (visuals) {
|
|
var visual;
|
|
for (var i = 0; i < visuals.length; i++) {
|
|
visual = visuals[i];
|
|
if (this._remove(visual)) {
|
|
this.append(visual);
|
|
}
|
|
}
|
|
},
|
|
toBack: function (visuals) {
|
|
this._reorderChildren(visuals, 0);
|
|
},
|
|
toIndex: function (visuals, indices) {
|
|
this._reorderChildren(visuals, indices);
|
|
},
|
|
_reorderChildren: function (visuals, indices) {
|
|
var group = this.drawingElement;
|
|
var drawingChildren = group.children.slice(0);
|
|
var children = this.children;
|
|
var fixedPosition = isNumber(indices);
|
|
var i, index, toIndex, drawingElement, visual;
|
|
for (i = 0; i < visuals.length; i++) {
|
|
visual = visuals[i];
|
|
drawingElement = visual.drawingContainer();
|
|
index = inArray(visual, children);
|
|
if (index >= 0) {
|
|
drawingChildren.splice(index, 1);
|
|
children.splice(index, 1);
|
|
toIndex = fixedPosition ? indices : indices[i];
|
|
drawingChildren.splice(toIndex, 0, drawingElement);
|
|
children.splice(toIndex, 0, visual);
|
|
}
|
|
}
|
|
group.clear();
|
|
group.append.apply(group, drawingChildren);
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
if (this._childrenChange) {
|
|
this._childrenChange = false;
|
|
if (!this._updateSize(options)) {
|
|
this._initSize();
|
|
}
|
|
} else {
|
|
this._updateSize(options);
|
|
}
|
|
Element.fn.redraw.call(this, options);
|
|
}
|
|
},
|
|
_boundingBox: function () {
|
|
var children = this.children;
|
|
var boundingBox;
|
|
var visual, childBoundingBox;
|
|
for (var i = 0; i < children.length; i++) {
|
|
visual = children[i];
|
|
if (visual.visible() && visual._includeInBBox !== false) {
|
|
childBoundingBox = visual.drawingContainer().clippedBBox(null);
|
|
if (childBoundingBox) {
|
|
if (boundingBox) {
|
|
boundingBox = g.Rect.union(boundingBox, childBoundingBox);
|
|
} else {
|
|
boundingBox = childBoundingBox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return boundingBox;
|
|
}
|
|
});
|
|
deepExtend(Group.fn, AutoSizeableMixin);
|
|
var Layout = Group.extend({
|
|
init: function (rect, options) {
|
|
this.children = [];
|
|
Element.fn.init.call(this, options);
|
|
this.drawingElement = new d.Layout(toDrawingRect(rect), options);
|
|
this._initSize();
|
|
},
|
|
rect: function (rect) {
|
|
if (rect) {
|
|
this.drawingElement.rect(toDrawingRect(rect));
|
|
} else {
|
|
var drawingRect = this.drawingElement.rect();
|
|
if (drawingRect) {
|
|
return new Rect(drawingRect.origin.x, drawingRect.origin.y, drawingRect.size.width, drawingRect.size.height);
|
|
}
|
|
}
|
|
},
|
|
reflow: function () {
|
|
this.drawingElement.reflow();
|
|
},
|
|
redraw: function (options) {
|
|
kendo.deepExtend(this.drawingElement.options, options);
|
|
Group.fn.redraw.call(this, options);
|
|
}
|
|
});
|
|
var Circle = VisualBase.extend({
|
|
init: function (options) {
|
|
VisualBase.fn.init.call(this, options);
|
|
this._initCircle();
|
|
this._initSize();
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
var circleOptions = this.options;
|
|
if (options.center) {
|
|
deepExtend(circleOptions, { center: options.center });
|
|
this._center.move(circleOptions.center.x, circleOptions.center.y);
|
|
}
|
|
if (this._diffNumericOptions(options, ['radius'])) {
|
|
this._circle.setRadius(circleOptions.radius);
|
|
}
|
|
this._updateSize(options);
|
|
VisualBase.fn.redraw.call(this, options);
|
|
}
|
|
},
|
|
_initCircle: function () {
|
|
var options = this.options;
|
|
var width = options.width;
|
|
var height = options.height;
|
|
var radius = options.radius;
|
|
if (!defined(radius)) {
|
|
if (!defined(width)) {
|
|
width = height;
|
|
}
|
|
if (!defined(height)) {
|
|
height = width;
|
|
}
|
|
options.radius = radius = Math.min(width, height) / 2;
|
|
}
|
|
var center = options.center || {
|
|
x: radius,
|
|
y: radius
|
|
};
|
|
this._center = new g.Point(center.x, center.y);
|
|
this._circle = new g.Circle(this._center, radius);
|
|
this.drawingElement = new d.Circle(this._circle, { stroke: options.stroke });
|
|
this._fill();
|
|
}
|
|
});
|
|
deepExtend(Circle.fn, AutoSizeableMixin);
|
|
var Canvas = Class.extend({
|
|
init: function (element, options) {
|
|
options = options || {};
|
|
this.element = element;
|
|
this.surface = d.Surface.create(element, options);
|
|
if (kendo.isFunction(this.surface.translate)) {
|
|
this.translate = this._translate;
|
|
}
|
|
this.drawingElement = new d.Group();
|
|
this._viewBox = new Rect(0, 0, options.width, options.height);
|
|
this.size(this._viewBox);
|
|
},
|
|
bounds: function () {
|
|
var box = this.drawingElement.clippedBBox();
|
|
return new Rect(0, 0, box.width(), box.height());
|
|
},
|
|
size: function (size) {
|
|
var viewBox = this._viewBox;
|
|
if (defined(size)) {
|
|
viewBox.width = size.width;
|
|
viewBox.height = size.height;
|
|
this.surface.setSize(size);
|
|
}
|
|
return {
|
|
width: viewBox.width,
|
|
height: viewBox.height
|
|
};
|
|
},
|
|
_translate: function (x, y) {
|
|
var viewBox = this._viewBox;
|
|
if (defined(x) && defined(y)) {
|
|
viewBox.x = x;
|
|
viewBox.y = y;
|
|
this.surface.translate({
|
|
x: x,
|
|
y: y
|
|
});
|
|
}
|
|
return {
|
|
x: viewBox.x,
|
|
y: viewBox.y
|
|
};
|
|
},
|
|
draw: function () {
|
|
this.surface.draw(this.drawingElement);
|
|
},
|
|
append: function (visual) {
|
|
this.drawingElement.append(visual.drawingContainer());
|
|
return this;
|
|
},
|
|
remove: function (visual) {
|
|
this.drawingElement.remove(visual.drawingContainer());
|
|
},
|
|
insertBefore: function () {
|
|
},
|
|
clear: function () {
|
|
this.drawingElement.clear();
|
|
},
|
|
destroy: function (clearHtml) {
|
|
this.surface.destroy();
|
|
if (clearHtml) {
|
|
$(this.element).remove();
|
|
}
|
|
}
|
|
});
|
|
function sizeOptionsOrDefault(options) {
|
|
return {
|
|
x: options.x || 0,
|
|
y: options.y || 0,
|
|
width: options.width || 0,
|
|
height: options.height || 0
|
|
};
|
|
}
|
|
function normalizeDrawingOptions(options) {
|
|
if (options) {
|
|
var drawingOptions = options;
|
|
if (isString(drawingOptions)) {
|
|
drawingOptions = { color: drawingOptions };
|
|
}
|
|
if (drawingOptions.color) {
|
|
drawingOptions.color = getColor(drawingOptions.color);
|
|
}
|
|
return drawingOptions;
|
|
}
|
|
}
|
|
function getColor(value) {
|
|
var color;
|
|
if (value != TRANSPARENT) {
|
|
color = new d.Color(value).toHex();
|
|
} else {
|
|
color = value;
|
|
}
|
|
return color;
|
|
}
|
|
function lineAngle(p1, p2) {
|
|
var xDiff = p2.x - p1.x;
|
|
var yDiff = p2.y - p1.y;
|
|
var angle = kendo.util.deg(Math.atan2(yDiff, xDiff));
|
|
return angle;
|
|
}
|
|
function createSegment(x, y) {
|
|
return new d.Segment(new g.Point(x, y));
|
|
}
|
|
function toDrawingRect(rect) {
|
|
if (rect) {
|
|
return new g.Rect([
|
|
rect.x,
|
|
rect.y
|
|
], [
|
|
rect.width,
|
|
rect.height
|
|
]);
|
|
}
|
|
}
|
|
kendo.deepExtend(diagram, {
|
|
init: function (element) {
|
|
kendo.init(element, diagram.ui);
|
|
},
|
|
diffNumericOptions: diffNumericOptions,
|
|
Element: Element,
|
|
Scale: Scale,
|
|
Translation: Translation,
|
|
Rotation: Rotation,
|
|
Circle: Circle,
|
|
Group: Group,
|
|
Rectangle: Rectangle,
|
|
Canvas: Canvas,
|
|
Path: Path,
|
|
Layout: Layout,
|
|
Line: Line,
|
|
MarkerBase: MarkerBase,
|
|
ArrowMarker: ArrowMarker,
|
|
CircleMarker: CircleMarker,
|
|
Polyline: Polyline,
|
|
CompositeTransform: CompositeTransform,
|
|
TextBlock: TextBlock,
|
|
Image: Image,
|
|
VisualBase: VisualBase
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/services', [
|
|
'kendo.drawing',
|
|
'dataviz/diagram/svg'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, dataviz = kendo.dataviz, diagram = dataviz.diagram, Class = kendo.Class, Group = diagram.Group, Rect = diagram.Rect, Rectangle = diagram.Rectangle, Utils = diagram.Utils, isUndefined = Utils.isUndefined, Point = diagram.Point, Circle = diagram.Circle, Ticker = diagram.Ticker, deepExtend = kendo.deepExtend, Movable = kendo.ui.Movable, browser = kendo.support.browser, defined = kendo.util.defined, inArray = $.inArray, proxy = $.proxy;
|
|
var Cursors = {
|
|
arrow: 'default',
|
|
grip: 'pointer',
|
|
cross: 'pointer',
|
|
add: 'pointer',
|
|
move: 'move',
|
|
select: 'pointer',
|
|
south: 's-resize',
|
|
east: 'e-resize',
|
|
west: 'w-resize',
|
|
north: 'n-resize',
|
|
rowresize: 'row-resize',
|
|
colresize: 'col-resize'
|
|
}, HIT_TEST_DISTANCE = 10, AUTO = 'Auto', TOP = 'Top', RIGHT = 'Right', LEFT = 'Left', BOTTOM = 'Bottom', DEFAULT_SNAP_SIZE = 10, DEFAULT_SNAP_ANGLE = 10, DRAG_START = 'dragStart', DRAG = 'drag', DRAG_END = 'dragEnd', ITEMROTATE = 'itemRotate', ITEMBOUNDSCHANGE = 'itemBoundsChange', MIN_SNAP_SIZE = 5, MIN_SNAP_ANGLE = 5, MOUSE_ENTER = 'mouseEnter', MOUSE_LEAVE = 'mouseLeave', ZOOM_START = 'zoomStart', ZOOM_END = 'zoomEnd', SCROLL_MIN = -20000, SCROLL_MAX = 20000, FRICTION = 0.9, FRICTION_MOBILE = 0.93, VELOCITY_MULTIPLIER = 5, TRANSPARENT = 'transparent', PAN = 'pan', ROTATED = 'rotated';
|
|
diagram.Cursors = Cursors;
|
|
function selectSingle(item, meta) {
|
|
if (item.isSelected) {
|
|
if (meta.ctrlKey) {
|
|
item.select(false);
|
|
}
|
|
} else {
|
|
item.diagram.select(item, { addToSelection: meta.ctrlKey });
|
|
}
|
|
}
|
|
var PositionAdapter = kendo.Class.extend({
|
|
init: function (layoutState) {
|
|
this.layoutState = layoutState;
|
|
this.diagram = layoutState.diagram;
|
|
},
|
|
initState: function () {
|
|
this.froms = [];
|
|
this.tos = [];
|
|
this.subjects = [];
|
|
function pusher(id, bounds) {
|
|
var shape = this.diagram.getShapeById(id);
|
|
if (shape) {
|
|
this.subjects.push(shape);
|
|
this.froms.push(shape.bounds().topLeft());
|
|
this.tos.push(bounds.topLeft());
|
|
}
|
|
}
|
|
this.layoutState.nodeMap.forEach(pusher, this);
|
|
},
|
|
update: function (tick) {
|
|
if (this.subjects.length <= 0) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < this.subjects.length; i++) {
|
|
this.subjects[i].position(new Point(this.froms[i].x + (this.tos[i].x - this.froms[i].x) * tick, this.froms[i].y + (this.tos[i].y - this.froms[i].y) * tick));
|
|
}
|
|
}
|
|
});
|
|
var LayoutUndoUnit = Class.extend({
|
|
init: function (initialState, finalState, animate) {
|
|
if (isUndefined(animate)) {
|
|
this.animate = false;
|
|
} else {
|
|
this.animate = animate;
|
|
}
|
|
this._initialState = initialState;
|
|
this._finalState = finalState;
|
|
this.title = 'Diagram layout';
|
|
},
|
|
undo: function () {
|
|
this.setState(this._initialState);
|
|
},
|
|
redo: function () {
|
|
this.setState(this._finalState);
|
|
},
|
|
setState: function (state) {
|
|
var diagram = state.diagram;
|
|
if (this.animate) {
|
|
state.linkMap.forEach(function (id, points) {
|
|
var conn = diagram.getShapeById(id);
|
|
conn.visible(false);
|
|
if (conn) {
|
|
conn.points(points);
|
|
}
|
|
});
|
|
var ticker = new Ticker();
|
|
ticker.addAdapter(new PositionAdapter(state));
|
|
ticker.onComplete(function () {
|
|
state.linkMap.forEach(function (id) {
|
|
var conn = diagram.getShapeById(id);
|
|
conn.visible(true);
|
|
});
|
|
});
|
|
ticker.play();
|
|
} else {
|
|
state.nodeMap.forEach(function (id, bounds) {
|
|
var shape = diagram.getShapeById(id);
|
|
if (shape) {
|
|
shape.position(bounds.topLeft());
|
|
}
|
|
});
|
|
state.linkMap.forEach(function (id, points) {
|
|
var conn = diagram.getShapeById(id);
|
|
if (conn) {
|
|
conn.points(points);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
var CompositeUnit = Class.extend({
|
|
init: function (unit) {
|
|
this.units = [];
|
|
this.title = 'Composite unit';
|
|
if (unit !== undefined) {
|
|
this.units.push(unit);
|
|
}
|
|
},
|
|
add: function (undoUnit) {
|
|
this.units.push(undoUnit);
|
|
},
|
|
undo: function () {
|
|
for (var i = 0; i < this.units.length; i++) {
|
|
this.units[i].undo();
|
|
}
|
|
},
|
|
redo: function () {
|
|
for (var i = 0; i < this.units.length; i++) {
|
|
this.units[i].redo();
|
|
}
|
|
}
|
|
});
|
|
var ConnectionEditUnit = Class.extend({
|
|
init: function (item, redoSource, redoTarget) {
|
|
this.item = item;
|
|
this._redoSource = redoSource;
|
|
this._redoTarget = redoTarget;
|
|
if (defined(redoSource)) {
|
|
this._undoSource = item.source();
|
|
}
|
|
if (defined(redoTarget)) {
|
|
this._undoTarget = item.target();
|
|
}
|
|
this.title = 'Connection Editing';
|
|
},
|
|
undo: function () {
|
|
if (this._undoSource !== undefined) {
|
|
this.item._updateConnector(this._undoSource, 'source');
|
|
}
|
|
if (this._undoTarget !== undefined) {
|
|
this.item._updateConnector(this._undoTarget, 'target');
|
|
}
|
|
this.item.updateModel();
|
|
},
|
|
redo: function () {
|
|
if (this._redoSource !== undefined) {
|
|
this.item._updateConnector(this._redoSource, 'source');
|
|
}
|
|
if (this._redoTarget !== undefined) {
|
|
this.item._updateConnector(this._redoTarget, 'target');
|
|
}
|
|
this.item.updateModel();
|
|
}
|
|
});
|
|
var ConnectionEditUndoUnit = Class.extend({
|
|
init: function (item, undoSource, undoTarget) {
|
|
this.item = item;
|
|
this._undoSource = undoSource;
|
|
this._undoTarget = undoTarget;
|
|
this._redoSource = item.source();
|
|
this._redoTarget = item.target();
|
|
this.title = 'Connection Editing';
|
|
},
|
|
undo: function () {
|
|
this.item._updateConnector(this._undoSource, 'source');
|
|
this.item._updateConnector(this._undoTarget, 'target');
|
|
this.item.updateModel();
|
|
},
|
|
redo: function () {
|
|
this.item._updateConnector(this._redoSource, 'source');
|
|
this.item._updateConnector(this._redoTarget, 'target');
|
|
this.item.updateModel();
|
|
}
|
|
});
|
|
var DeleteConnectionUnit = Class.extend({
|
|
init: function (connection) {
|
|
this.connection = connection;
|
|
this.diagram = connection.diagram;
|
|
this.targetConnector = connection.targetConnector;
|
|
this.title = 'Delete connection';
|
|
},
|
|
undo: function () {
|
|
this.diagram._addConnection(this.connection, false);
|
|
},
|
|
redo: function () {
|
|
this.diagram.remove(this.connection, false);
|
|
}
|
|
});
|
|
var DeleteShapeUnit = Class.extend({
|
|
init: function (shape) {
|
|
this.shape = shape;
|
|
this.diagram = shape.diagram;
|
|
this.title = 'Deletion';
|
|
},
|
|
undo: function () {
|
|
this.diagram._addShape(this.shape, false);
|
|
this.shape.select(false);
|
|
},
|
|
redo: function () {
|
|
this.shape.select(false);
|
|
this.diagram.remove(this.shape, false);
|
|
}
|
|
});
|
|
var TransformUnit = Class.extend({
|
|
init: function (shapes, undoStates, adorner) {
|
|
this.shapes = shapes;
|
|
this.undoStates = undoStates;
|
|
this.title = 'Transformation';
|
|
this.redoStates = [];
|
|
this.adorner = adorner;
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
this.redoStates.push(shape.bounds());
|
|
}
|
|
},
|
|
undo: function () {
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
shape.bounds(this.undoStates[i]);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape, this.redoStates[i], this.undoStates[i]);
|
|
}
|
|
shape.updateModel();
|
|
}
|
|
if (this.adorner) {
|
|
this.adorner.refreshBounds();
|
|
this.adorner.refresh();
|
|
}
|
|
},
|
|
redo: function () {
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
shape.bounds(this.redoStates[i]);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape, this.undoStates[i], this.redoStates[i]);
|
|
}
|
|
shape.updateModel();
|
|
}
|
|
if (this.adorner) {
|
|
this.adorner.refreshBounds();
|
|
this.adorner.refresh();
|
|
}
|
|
}
|
|
});
|
|
var AddConnectionUnit = Class.extend({
|
|
init: function (connection, diagram) {
|
|
this.connection = connection;
|
|
this.diagram = diagram;
|
|
this.title = 'New connection';
|
|
},
|
|
undo: function () {
|
|
this.diagram.remove(this.connection, false);
|
|
},
|
|
redo: function () {
|
|
this.diagram._addConnection(this.connection, false);
|
|
}
|
|
});
|
|
var AddShapeUnit = Class.extend({
|
|
init: function (shape, diagram) {
|
|
this.shape = shape;
|
|
this.diagram = diagram;
|
|
this.title = 'New shape';
|
|
},
|
|
undo: function () {
|
|
this.diagram.deselect();
|
|
this.diagram.remove(this.shape, false);
|
|
},
|
|
redo: function () {
|
|
this.diagram._addShape(this.shape, false);
|
|
}
|
|
});
|
|
var PanUndoUnit = Class.extend({
|
|
init: function (initialPosition, finalPosition, diagram) {
|
|
this.initial = initialPosition;
|
|
this.finalPos = finalPosition;
|
|
this.diagram = diagram;
|
|
this.title = 'Pan Unit';
|
|
},
|
|
undo: function () {
|
|
this.diagram.pan(this.initial);
|
|
},
|
|
redo: function () {
|
|
this.diagram.pan(this.finalPos);
|
|
}
|
|
});
|
|
var RotateUnit = Class.extend({
|
|
init: function (adorner, shapes, undoRotates) {
|
|
this.shapes = shapes;
|
|
this.undoRotates = undoRotates;
|
|
this.title = 'Rotation';
|
|
this.redoRotates = [];
|
|
this.redoAngle = adorner._angle;
|
|
this.adorner = adorner;
|
|
this.center = adorner._innerBounds.center();
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
this.redoRotates.push(shape.rotate().angle);
|
|
}
|
|
},
|
|
undo: function () {
|
|
var i, shape;
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
shape.rotate(this.undoRotates[i], this.center, false);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape);
|
|
}
|
|
shape.updateModel();
|
|
}
|
|
if (this.adorner) {
|
|
this.adorner._initialize();
|
|
this.adorner.refresh();
|
|
}
|
|
},
|
|
redo: function () {
|
|
var i, shape;
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
shape.rotate(this.redoRotates[i], this.center, false);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape);
|
|
}
|
|
shape.updateModel();
|
|
}
|
|
if (this.adorner) {
|
|
this.adorner._initialize();
|
|
this.adorner.refresh();
|
|
}
|
|
}
|
|
});
|
|
var ToFrontUnit = Class.extend({
|
|
init: function (diagram, items, initialIndices) {
|
|
this.diagram = diagram;
|
|
this.indices = initialIndices;
|
|
this.items = items;
|
|
this.title = 'Rotate Unit';
|
|
},
|
|
undo: function () {
|
|
this.diagram._toIndex(this.items, this.indices);
|
|
},
|
|
redo: function () {
|
|
this.diagram.toFront(this.items, false);
|
|
}
|
|
});
|
|
var ToBackUnit = Class.extend({
|
|
init: function (diagram, items, initialIndices) {
|
|
this.diagram = diagram;
|
|
this.indices = initialIndices;
|
|
this.items = items;
|
|
this.title = 'Rotate Unit';
|
|
},
|
|
undo: function () {
|
|
this.diagram._toIndex(this.items, this.indices);
|
|
},
|
|
redo: function () {
|
|
this.diagram.toBack(this.items, false);
|
|
}
|
|
});
|
|
var UndoRedoService = kendo.Observable.extend({
|
|
init: function (options) {
|
|
kendo.Observable.fn.init.call(this, options);
|
|
this.bind(this.events, options);
|
|
this.stack = [];
|
|
this.index = 0;
|
|
this.capacity = 100;
|
|
},
|
|
events: [
|
|
'undone',
|
|
'redone'
|
|
],
|
|
begin: function () {
|
|
this.composite = new CompositeUnit();
|
|
},
|
|
cancel: function () {
|
|
this.composite = undefined;
|
|
},
|
|
commit: function (execute) {
|
|
if (this.composite.units.length > 0) {
|
|
this._restart(this.composite, execute);
|
|
}
|
|
this.composite = undefined;
|
|
},
|
|
addCompositeItem: function (undoUnit) {
|
|
if (this.composite) {
|
|
this.composite.add(undoUnit);
|
|
} else {
|
|
this.add(undoUnit);
|
|
}
|
|
},
|
|
add: function (undoUnit, execute) {
|
|
this._restart(undoUnit, execute);
|
|
},
|
|
pop: function () {
|
|
if (this.index > 0) {
|
|
this.stack.pop();
|
|
this.index--;
|
|
}
|
|
},
|
|
count: function () {
|
|
return this.stack.length;
|
|
},
|
|
undo: function () {
|
|
if (this.index > 0) {
|
|
this.index--;
|
|
this.stack[this.index].undo();
|
|
this.trigger('undone');
|
|
}
|
|
},
|
|
redo: function () {
|
|
if (this.stack.length > 0 && this.index < this.stack.length) {
|
|
this.stack[this.index].redo();
|
|
this.index++;
|
|
this.trigger('redone');
|
|
}
|
|
},
|
|
_restart: function (composite, execute) {
|
|
this.stack.splice(this.index, this.stack.length - this.index);
|
|
this.stack.push(composite);
|
|
if (execute !== false) {
|
|
this.redo();
|
|
} else {
|
|
this.index++;
|
|
}
|
|
if (this.stack.length > this.capacity) {
|
|
this.stack.splice(0, this.stack.length - this.capacity);
|
|
this.index = this.capacity;
|
|
}
|
|
},
|
|
clear: function () {
|
|
this.stack = [];
|
|
this.index = 0;
|
|
}
|
|
});
|
|
var EmptyTool = Class.extend({
|
|
init: function (toolService) {
|
|
this.toolService = toolService;
|
|
},
|
|
start: function () {
|
|
},
|
|
move: function () {
|
|
},
|
|
end: function () {
|
|
},
|
|
tryActivate: function () {
|
|
return false;
|
|
},
|
|
getCursor: function () {
|
|
return Cursors.arrow;
|
|
}
|
|
});
|
|
function noMeta(meta) {
|
|
return meta.ctrlKey === false && meta.altKey === false && meta.shiftKey === false;
|
|
}
|
|
function tryActivateSelection(options, meta) {
|
|
var enabled = options !== false;
|
|
if (options.key && options.key != 'none') {
|
|
enabled = meta[options.key + 'Key'];
|
|
}
|
|
return enabled;
|
|
}
|
|
var ScrollerTool = EmptyTool.extend({
|
|
init: function (toolService) {
|
|
var tool = this;
|
|
var friction = kendo.support.mobileOS ? FRICTION_MOBILE : FRICTION;
|
|
EmptyTool.fn.init.call(tool, toolService);
|
|
var diagram = tool.toolService.diagram, canvas = diagram.canvas;
|
|
var scroller = diagram.scroller = tool.scroller = $(diagram.scrollable).kendoMobileScroller({
|
|
friction: friction,
|
|
velocityMultiplier: VELOCITY_MULTIPLIER,
|
|
mousewheelScrolling: false,
|
|
zoom: false,
|
|
scroll: proxy(tool._move, tool)
|
|
}).data('kendoMobileScroller');
|
|
if (canvas.translate) {
|
|
tool.movableCanvas = new Movable(canvas.element);
|
|
}
|
|
var virtualScroll = function (dimension, min, max) {
|
|
dimension.makeVirtual();
|
|
dimension.virtualSize(min || SCROLL_MIN, max || SCROLL_MAX);
|
|
};
|
|
virtualScroll(scroller.dimensions.x);
|
|
virtualScroll(scroller.dimensions.y);
|
|
scroller.disable();
|
|
},
|
|
tryActivate: function (p, meta) {
|
|
var toolService = this.toolService;
|
|
var options = toolService.diagram.options.pannable;
|
|
var enabled = meta.ctrlKey;
|
|
if (defined(options.key)) {
|
|
if (!options.key || options.key == 'none') {
|
|
enabled = noMeta(meta);
|
|
} else {
|
|
enabled = meta[options.key + 'Key'] && !(meta.ctrlKey && defined(toolService.hoveredItem));
|
|
}
|
|
}
|
|
return options !== false && enabled && !defined(toolService.hoveredAdorner) && !defined(toolService._hoveredConnector);
|
|
},
|
|
start: function () {
|
|
this.scroller.enable();
|
|
},
|
|
move: function () {
|
|
},
|
|
_move: function (args) {
|
|
var tool = this, diagram = tool.toolService.diagram, canvas = diagram.canvas, scrollPos = new Point(args.scrollLeft, args.scrollTop);
|
|
if (canvas.translate) {
|
|
diagram._storePan(scrollPos.times(-1));
|
|
tool.movableCanvas.moveTo(scrollPos);
|
|
canvas.translate(scrollPos.x, scrollPos.y);
|
|
} else {
|
|
scrollPos = scrollPos.plus(diagram._pan.times(-1));
|
|
}
|
|
diagram.trigger(PAN, { pan: scrollPos });
|
|
},
|
|
end: function () {
|
|
this.scroller.disable();
|
|
},
|
|
getCursor: function () {
|
|
return Cursors.move;
|
|
}
|
|
});
|
|
var PointerTool = Class.extend({
|
|
init: function (toolService) {
|
|
this.toolService = toolService;
|
|
},
|
|
tryActivate: function () {
|
|
return true;
|
|
},
|
|
start: function (p, meta) {
|
|
var toolService = this.toolService, diagram = toolService.diagram, hoveredItem = toolService.hoveredItem, selectable = diagram.options.selectable;
|
|
if (hoveredItem) {
|
|
if (tryActivateSelection(selectable, meta)) {
|
|
selectSingle(hoveredItem, meta);
|
|
}
|
|
if (hoveredItem.adorner) {
|
|
this.adorner = hoveredItem.adorner;
|
|
this.handle = this.adorner._hitTest(p);
|
|
}
|
|
}
|
|
if (!this.handle) {
|
|
this.handle = diagram._resizingAdorner._hitTest(p);
|
|
if (this.handle) {
|
|
this.adorner = diagram._resizingAdorner;
|
|
}
|
|
}
|
|
if (this.adorner) {
|
|
if (!this.adorner.isDragHandle(this.handle) || !diagram.trigger(DRAG_START, {
|
|
shapes: this.adorner.shapes,
|
|
connections: []
|
|
})) {
|
|
this.adorner.start(p);
|
|
} else {
|
|
toolService.startPoint = p;
|
|
toolService.end(p);
|
|
}
|
|
}
|
|
},
|
|
move: function (p) {
|
|
if (this.adorner) {
|
|
this.adorner.move(this.handle, p);
|
|
if (this.adorner.isDragHandle(this.handle)) {
|
|
this.toolService.diagram.trigger(DRAG, {
|
|
shapes: this.adorner.shapes,
|
|
connections: []
|
|
});
|
|
}
|
|
}
|
|
},
|
|
end: function (p, meta) {
|
|
var diagram = this.toolService.diagram, service = this.toolService, adorner = this.adorner, unit;
|
|
if (adorner) {
|
|
if (!adorner.isDragHandle(this.handle) || !diagram.trigger(DRAG_END, {
|
|
shapes: adorner.shapes,
|
|
connections: []
|
|
})) {
|
|
unit = adorner.stop();
|
|
if (unit) {
|
|
diagram.undoRedoService.add(unit, false);
|
|
}
|
|
} else {
|
|
adorner.cancel();
|
|
}
|
|
}
|
|
if (service.hoveredItem) {
|
|
this.toolService.triggerClick({
|
|
item: service.hoveredItem,
|
|
point: p,
|
|
meta: meta
|
|
});
|
|
}
|
|
this.adorner = undefined;
|
|
this.handle = undefined;
|
|
},
|
|
getCursor: function (p) {
|
|
return this.toolService.hoveredItem ? this.toolService.hoveredItem._getCursor(p) : Cursors.arrow;
|
|
}
|
|
});
|
|
var SelectionTool = Class.extend({
|
|
init: function (toolService) {
|
|
this.toolService = toolService;
|
|
},
|
|
tryActivate: function (p, meta) {
|
|
var toolService = this.toolService;
|
|
var enabled = tryActivateSelection(toolService.diagram.options.selectable, meta);
|
|
return enabled && !defined(toolService.hoveredItem) && !defined(toolService.hoveredAdorner);
|
|
},
|
|
start: function (p) {
|
|
var diagram = this.toolService.diagram;
|
|
diagram.deselect();
|
|
diagram.selector.start(p);
|
|
},
|
|
move: function (p) {
|
|
var diagram = this.toolService.diagram;
|
|
diagram.selector.move(p);
|
|
},
|
|
end: function (p, meta) {
|
|
var diagram = this.toolService.diagram, hoveredItem = this.toolService.hoveredItem;
|
|
var rect = diagram.selector.bounds();
|
|
if ((!hoveredItem || !hoveredItem.isSelected) && !meta.ctrlKey) {
|
|
diagram.deselect();
|
|
}
|
|
if (!rect.isEmpty()) {
|
|
diagram.selectArea(rect);
|
|
}
|
|
diagram.selector.end();
|
|
},
|
|
getCursor: function () {
|
|
return Cursors.arrow;
|
|
}
|
|
});
|
|
var ConnectionTool = Class.extend({
|
|
init: function (toolService) {
|
|
this.toolService = toolService;
|
|
this.type = 'ConnectionTool';
|
|
},
|
|
tryActivate: function () {
|
|
return this.toolService._hoveredConnector;
|
|
},
|
|
start: function (p, meta) {
|
|
var diagram = this.toolService.diagram, connector = this.toolService._hoveredConnector, connection = diagram._createConnection({}, connector._c, p);
|
|
if (canDrag(connection) && !diagram.trigger(DRAG_START, {
|
|
shapes: [],
|
|
connections: [connection]
|
|
}) && diagram._addConnection(connection)) {
|
|
this.toolService._connectionManipulation(connection, connector._c.shape, true);
|
|
this.toolService._removeHover();
|
|
selectSingle(this.toolService.activeConnection, meta);
|
|
} else {
|
|
connection.source(null);
|
|
this.toolService.end(p);
|
|
}
|
|
},
|
|
move: function (p) {
|
|
var toolService = this.toolService;
|
|
var connection = toolService.activeConnection;
|
|
connection.target(p);
|
|
toolService.diagram.trigger(DRAG, {
|
|
shapes: [],
|
|
connections: [connection]
|
|
});
|
|
return true;
|
|
},
|
|
end: function (p) {
|
|
var toolService = this.toolService, d = toolService.diagram, connection = toolService.activeConnection, hoveredItem = toolService.hoveredItem, connector = toolService._hoveredConnector, target;
|
|
if (!connection) {
|
|
return;
|
|
}
|
|
if (connector && connector._c != connection.sourceConnector) {
|
|
target = connector._c;
|
|
} else if (hoveredItem && hoveredItem instanceof diagram.Shape) {
|
|
target = hoveredItem.getConnector(AUTO) || hoveredItem.getConnector(p);
|
|
} else {
|
|
target = p;
|
|
}
|
|
connection.target(target);
|
|
if (!d.trigger(DRAG_END, {
|
|
shapes: [],
|
|
connections: [connection]
|
|
})) {
|
|
connection.updateModel();
|
|
d._syncConnectionChanges();
|
|
} else {
|
|
d.remove(connection, false);
|
|
d.undoRedoService.pop();
|
|
}
|
|
toolService._connectionManipulation();
|
|
},
|
|
getCursor: function () {
|
|
return Cursors.arrow;
|
|
}
|
|
});
|
|
var ConnectionEditTool = Class.extend({
|
|
init: function (toolService) {
|
|
this.toolService = toolService;
|
|
this.type = 'ConnectionTool';
|
|
},
|
|
tryActivate: function (p, meta) {
|
|
var toolService = this.toolService, diagram = toolService.diagram, selectable = diagram.options.selectable, item = toolService.hoveredItem, isActive = tryActivateSelection(selectable, meta) && item && item.path && !(item.isSelected && meta.ctrlKey);
|
|
if (isActive) {
|
|
this._c = item;
|
|
}
|
|
return isActive;
|
|
},
|
|
start: function (p, meta) {
|
|
var connection = this._c;
|
|
selectSingle(connection, meta);
|
|
var adorner = connection.adorner;
|
|
if (canDrag(connection) && adorner && !this.toolService.diagram.trigger(DRAG_START, {
|
|
shapes: [],
|
|
connections: [connection]
|
|
})) {
|
|
this.handle = adorner._hitTest(p);
|
|
adorner.start(p);
|
|
} else {
|
|
this.toolService.startPoint = p;
|
|
this.toolService.end(p);
|
|
}
|
|
},
|
|
move: function (p) {
|
|
var adorner = this._c.adorner;
|
|
if (canDrag(this._c) && adorner) {
|
|
adorner.move(this.handle, p);
|
|
this.toolService.diagram.trigger(DRAG, {
|
|
shapes: [],
|
|
connections: [this._c]
|
|
});
|
|
return true;
|
|
}
|
|
},
|
|
end: function (p, meta) {
|
|
var connection = this._c;
|
|
var adorner = connection.adorner;
|
|
var toolService = this.toolService;
|
|
var diagram = toolService.diagram;
|
|
if (adorner) {
|
|
toolService.triggerClick({
|
|
item: connection,
|
|
point: p,
|
|
meta: meta
|
|
});
|
|
if (canDrag(connection)) {
|
|
var unit = adorner.stop(p);
|
|
if (!diagram.trigger(DRAG_END, {
|
|
shapes: [],
|
|
connections: [connection]
|
|
})) {
|
|
diagram.undoRedoService.add(unit, false);
|
|
connection.updateModel();
|
|
diagram._syncConnectionChanges();
|
|
} else {
|
|
unit.undo();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getCursor: function () {
|
|
return Cursors.move;
|
|
}
|
|
});
|
|
function testKey(key, str) {
|
|
return str.charCodeAt(0) == key || str.toUpperCase().charCodeAt(0) == key;
|
|
}
|
|
var ToolService = Class.extend({
|
|
init: function (diagram) {
|
|
this.diagram = diagram;
|
|
this.tools = [
|
|
new ScrollerTool(this),
|
|
new ConnectionEditTool(this),
|
|
new ConnectionTool(this),
|
|
new SelectionTool(this),
|
|
new PointerTool(this)
|
|
];
|
|
this.activeTool = undefined;
|
|
},
|
|
start: function (p, meta) {
|
|
meta = deepExtend({}, meta);
|
|
if (this.activeTool) {
|
|
this.activeTool.end(p, meta);
|
|
}
|
|
this._updateHoveredItem(p);
|
|
this._activateTool(p, meta);
|
|
this.activeTool.start(p, meta);
|
|
this._updateCursor(p);
|
|
this.diagram.focus();
|
|
this.startPoint = p;
|
|
return true;
|
|
},
|
|
move: function (p, meta) {
|
|
meta = deepExtend({}, meta);
|
|
var updateHovered = true;
|
|
if (this.activeTool) {
|
|
updateHovered = this.activeTool.move(p, meta);
|
|
}
|
|
if (updateHovered) {
|
|
this._updateHoveredItem(p);
|
|
}
|
|
this._updateCursor(p);
|
|
return true;
|
|
},
|
|
end: function (p, meta) {
|
|
meta = deepExtend({}, meta);
|
|
if (this.activeTool) {
|
|
this.activeTool.end(p, meta);
|
|
}
|
|
this.activeTool = undefined;
|
|
this._updateCursor(p);
|
|
return true;
|
|
},
|
|
keyDown: function (key, meta) {
|
|
var diagram = this.diagram;
|
|
meta = deepExtend({
|
|
ctrlKey: false,
|
|
metaKey: false,
|
|
altKey: false
|
|
}, meta);
|
|
if ((meta.ctrlKey || meta.metaKey) && !meta.altKey) {
|
|
if (testKey(key, 'a')) {
|
|
diagram.selectAll();
|
|
diagram._destroyToolBar();
|
|
return true;
|
|
} else if (testKey(key, 'z')) {
|
|
diagram.undo();
|
|
diagram._destroyToolBar();
|
|
return true;
|
|
} else if (testKey(key, 'y')) {
|
|
diagram.redo();
|
|
diagram._destroyToolBar();
|
|
return true;
|
|
} else if (testKey(key, 'c')) {
|
|
diagram.copy();
|
|
diagram._destroyToolBar();
|
|
} else if (testKey(key, 'x')) {
|
|
diagram.cut();
|
|
diagram._destroyToolBar();
|
|
} else if (testKey(key, 'v')) {
|
|
diagram.paste();
|
|
diagram._destroyToolBar();
|
|
} else if (testKey(key, 'l')) {
|
|
diagram.layout();
|
|
diagram._destroyToolBar();
|
|
} else if (testKey(key, 'd')) {
|
|
diagram._destroyToolBar();
|
|
diagram.copy();
|
|
diagram.paste();
|
|
}
|
|
} else if (key === 46 || key === 8) {
|
|
var toRemove = this.diagram._triggerRemove(diagram.select());
|
|
if (toRemove.length) {
|
|
this.diagram.remove(toRemove, true);
|
|
this.diagram._syncChanges();
|
|
this.diagram._destroyToolBar();
|
|
}
|
|
return true;
|
|
} else if (key === 27) {
|
|
this._discardNewConnection();
|
|
diagram.deselect();
|
|
diagram._destroyToolBar();
|
|
return true;
|
|
}
|
|
},
|
|
wheel: function (p, meta) {
|
|
var diagram = this.diagram, delta = meta.delta, z = diagram.zoom(), options = diagram.options, zoomRate = options.zoomRate, zoomOptions = {
|
|
point: p,
|
|
meta: meta,
|
|
zoom: z
|
|
};
|
|
if (diagram.trigger(ZOOM_START, zoomOptions)) {
|
|
return;
|
|
}
|
|
if (delta < 0) {
|
|
z += zoomRate;
|
|
} else {
|
|
z -= zoomRate;
|
|
}
|
|
z = kendo.dataviz.round(Math.max(options.zoomMin, Math.min(options.zoomMax, z)), 2);
|
|
zoomOptions.zoom = z;
|
|
diagram.zoom(z, zoomOptions);
|
|
diagram.trigger(ZOOM_END, zoomOptions);
|
|
return true;
|
|
},
|
|
setTool: function (tool, index) {
|
|
tool.toolService = this;
|
|
this.tools[index] = tool;
|
|
},
|
|
triggerClick: function (data) {
|
|
if (this.startPoint.equals(data.point)) {
|
|
this.diagram.trigger('click', data);
|
|
}
|
|
},
|
|
_discardNewConnection: function () {
|
|
if (this.newConnection) {
|
|
this.diagram.remove(this.newConnection);
|
|
this.newConnection = undefined;
|
|
}
|
|
},
|
|
_activateTool: function (p, meta) {
|
|
for (var i = 0; i < this.tools.length; i++) {
|
|
var tool = this.tools[i];
|
|
if (tool.tryActivate(p, meta)) {
|
|
this.activeTool = tool;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
_updateCursor: function (p) {
|
|
var element = this.diagram.element;
|
|
var cursor = this.activeTool ? this.activeTool.getCursor(p) : this.hoveredAdorner ? this.hoveredAdorner._getCursor(p) : this.hoveredItem ? this.hoveredItem._getCursor(p) : Cursors.arrow;
|
|
element.css({ cursor: cursor });
|
|
if (browser.msie && browser.version == 7) {
|
|
element[0].style.cssText = element[0].style.cssText;
|
|
}
|
|
},
|
|
_connectionManipulation: function (connection, disabledShape, isNew) {
|
|
this.activeConnection = connection;
|
|
this.disabledShape = disabledShape;
|
|
if (isNew) {
|
|
this.newConnection = this.activeConnection;
|
|
} else {
|
|
this.newConnection = undefined;
|
|
}
|
|
},
|
|
_updateHoveredItem: function (p) {
|
|
var hit = this._hitTest(p);
|
|
var diagram = this.diagram;
|
|
if (hit != this.hoveredItem && (!this.disabledShape || hit != this.disabledShape)) {
|
|
if (this.hoveredItem) {
|
|
diagram.trigger(MOUSE_LEAVE, { item: this.hoveredItem });
|
|
this.hoveredItem._hover(false);
|
|
}
|
|
if (hit && hit.options.enable) {
|
|
diagram.trigger(MOUSE_ENTER, { item: hit });
|
|
this.hoveredItem = hit;
|
|
this.hoveredItem._hover(true);
|
|
} else {
|
|
this.hoveredItem = undefined;
|
|
}
|
|
}
|
|
},
|
|
_removeHover: function () {
|
|
if (this.hoveredItem) {
|
|
this.hoveredItem._hover(false);
|
|
this.hoveredItem = undefined;
|
|
}
|
|
},
|
|
_hitTest: function (point) {
|
|
var hit, d = this.diagram, item, i;
|
|
if (this._hoveredConnector) {
|
|
this._hoveredConnector._hover(false);
|
|
this._hoveredConnector = undefined;
|
|
}
|
|
if (d._connectorsAdorner._visible) {
|
|
hit = d._connectorsAdorner._hitTest(point);
|
|
if (hit) {
|
|
return hit;
|
|
}
|
|
}
|
|
hit = this.diagram._resizingAdorner._hitTest(point);
|
|
if (hit) {
|
|
this.hoveredAdorner = d._resizingAdorner;
|
|
if (hit.x !== 0 || hit.y !== 0) {
|
|
return;
|
|
}
|
|
hit = undefined;
|
|
} else {
|
|
this.hoveredAdorner = undefined;
|
|
}
|
|
if (!this.activeTool || this.activeTool.type !== 'ConnectionTool') {
|
|
var selectedConnections = [];
|
|
for (i = 0; i < d._selectedItems.length; i++) {
|
|
item = d._selectedItems[i];
|
|
if (item instanceof diagram.Connection) {
|
|
selectedConnections.push(item);
|
|
}
|
|
}
|
|
hit = this._hitTestItems(selectedConnections, point);
|
|
}
|
|
return hit || this._hitTestElements(point);
|
|
},
|
|
_hitTestElements: function (point) {
|
|
var diagram = this.diagram;
|
|
var shapeHit = this._hitTestItems(diagram.shapes, point);
|
|
var connectionHit = this._hitTestItems(diagram.connections, point);
|
|
var hit;
|
|
if ((!this.activeTool || this.activeTool.type != 'ConnectionTool') && shapeHit && connectionHit && !hitTestShapeConnectors(shapeHit, point)) {
|
|
var mainLayer = diagram.mainLayer;
|
|
var shapeIdx = inArray(shapeHit.visual, mainLayer.children);
|
|
var connectionIdx = inArray(connectionHit.visual, mainLayer.children);
|
|
hit = shapeIdx > connectionIdx ? shapeHit : connectionHit;
|
|
}
|
|
return hit || shapeHit || connectionHit;
|
|
},
|
|
_hitTestItems: function (array, point) {
|
|
var i, item, hit;
|
|
for (i = array.length - 1; i >= 0; i--) {
|
|
item = array[i];
|
|
hit = item._hitTest(point);
|
|
if (hit) {
|
|
return hit;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
var ConnectionRouterBase = kendo.Class.extend({
|
|
init: function () {
|
|
}
|
|
});
|
|
var LinearConnectionRouter = ConnectionRouterBase.extend({
|
|
init: function (connection) {
|
|
var that = this;
|
|
ConnectionRouterBase.fn.init.call(that);
|
|
this.connection = connection;
|
|
},
|
|
hitTest: function (p) {
|
|
var rec = this.getBounds().inflate(HIT_TEST_DISTANCE);
|
|
if (!rec.contains(p)) {
|
|
return false;
|
|
}
|
|
return diagram.Geometry.distanceToPolyline(p, this.connection.allPoints()) < HIT_TEST_DISTANCE;
|
|
},
|
|
getBounds: function () {
|
|
var points = this.connection.allPoints(), s = points[0], e = points[points.length - 1], right = Math.max(s.x, e.x), left = Math.min(s.x, e.x), top = Math.min(s.y, e.y), bottom = Math.max(s.y, e.y);
|
|
for (var i = 1; i < points.length - 1; ++i) {
|
|
right = Math.max(right, points[i].x);
|
|
left = Math.min(left, points[i].x);
|
|
top = Math.min(top, points[i].y);
|
|
bottom = Math.max(bottom, points[i].y);
|
|
}
|
|
return new Rect(left, top, right - left, bottom - top);
|
|
}
|
|
});
|
|
var PolylineRouter = LinearConnectionRouter.extend({
|
|
init: function (connection) {
|
|
var that = this;
|
|
LinearConnectionRouter.fn.init.call(that);
|
|
this.connection = connection;
|
|
},
|
|
route: function () {
|
|
}
|
|
});
|
|
var CascadingRouter = LinearConnectionRouter.extend({
|
|
SAME_SIDE_DISTANCE_RATIO: 5,
|
|
init: function (connection) {
|
|
var that = this;
|
|
LinearConnectionRouter.fn.init.call(that);
|
|
this.connection = connection;
|
|
},
|
|
routePoints: function (start, end, sourceConnector, targetConnector) {
|
|
var result;
|
|
if (sourceConnector && targetConnector) {
|
|
result = this._connectorPoints(start, end, sourceConnector, targetConnector);
|
|
} else {
|
|
result = this._floatingPoints(start, end, sourceConnector);
|
|
}
|
|
return result;
|
|
},
|
|
route: function () {
|
|
var sourceConnector = this.connection._resolvedSourceConnector;
|
|
var targetConnector = this.connection._resolvedTargetConnector;
|
|
var start = this.connection.sourcePoint();
|
|
var end = this.connection.targetPoint();
|
|
var points = this.routePoints(start, end, sourceConnector, targetConnector);
|
|
this.connection.points(points);
|
|
},
|
|
_connectorSides: [
|
|
{
|
|
name: 'Top',
|
|
axis: 'y',
|
|
boundsPoint: 'topLeft',
|
|
secondarySign: 1
|
|
},
|
|
{
|
|
name: 'Left',
|
|
axis: 'x',
|
|
boundsPoint: 'topLeft',
|
|
secondarySign: 1
|
|
},
|
|
{
|
|
name: 'Bottom',
|
|
axis: 'y',
|
|
boundsPoint: 'bottomRight',
|
|
secondarySign: -1
|
|
},
|
|
{
|
|
name: 'Right',
|
|
axis: 'x',
|
|
boundsPoint: 'bottomRight',
|
|
secondarySign: -1
|
|
}
|
|
],
|
|
_connectorSide: function (connector, targetPoint) {
|
|
var position = connector.position();
|
|
var shapeBounds = connector.shape.bounds(ROTATED);
|
|
var bounds = {
|
|
topLeft: shapeBounds.topLeft(),
|
|
bottomRight: shapeBounds.bottomRight()
|
|
};
|
|
var sides = this._connectorSides;
|
|
var min = kendo.util.MAX_NUM;
|
|
var sideDistance;
|
|
var minSide;
|
|
var axis;
|
|
var side;
|
|
for (var idx = 0; idx < sides.length; idx++) {
|
|
side = sides[idx];
|
|
axis = side.axis;
|
|
sideDistance = Math.round(Math.abs(position[axis] - bounds[side.boundsPoint][axis]));
|
|
if (sideDistance < min) {
|
|
min = sideDistance;
|
|
minSide = side;
|
|
} else if (sideDistance === min && (position[axis] - targetPoint[axis]) * side.secondarySign > (position[minSide.axis] - targetPoint[minSide.axis]) * minSide.secondarySign) {
|
|
minSide = side;
|
|
}
|
|
}
|
|
return minSide.name;
|
|
},
|
|
_sameSideDistance: function (connector) {
|
|
var bounds = connector.shape.bounds(ROTATED);
|
|
return Math.min(bounds.width, bounds.height) / this.SAME_SIDE_DISTANCE_RATIO;
|
|
},
|
|
_connectorPoints: function (start, end, sourceConnector, targetConnector) {
|
|
var sourceConnectorSide = this._connectorSide(sourceConnector, end);
|
|
var targetConnectorSide = this._connectorSide(targetConnector, start);
|
|
var deltaX = end.x - start.x;
|
|
var deltaY = end.y - start.y;
|
|
var sameSideDistance = this._sameSideDistance(sourceConnector);
|
|
var result = [];
|
|
var pointX, pointY;
|
|
if (sourceConnectorSide === TOP || sourceConnectorSide == BOTTOM) {
|
|
if (targetConnectorSide == TOP || targetConnectorSide == BOTTOM) {
|
|
if (sourceConnectorSide == targetConnectorSide) {
|
|
if (sourceConnectorSide == TOP) {
|
|
pointY = Math.min(start.y, end.y) - sameSideDistance;
|
|
} else {
|
|
pointY = Math.max(start.y, end.y) + sameSideDistance;
|
|
}
|
|
result = [
|
|
new Point(start.x, pointY),
|
|
new Point(end.x, pointY)
|
|
];
|
|
} else {
|
|
result = [
|
|
new Point(start.x, start.y + deltaY / 2),
|
|
new Point(end.x, start.y + deltaY / 2)
|
|
];
|
|
}
|
|
} else {
|
|
result = [new Point(start.x, end.y)];
|
|
}
|
|
} else {
|
|
if (targetConnectorSide == LEFT || targetConnectorSide == RIGHT) {
|
|
if (sourceConnectorSide == targetConnectorSide) {
|
|
if (sourceConnectorSide == LEFT) {
|
|
pointX = Math.min(start.x, end.x) - sameSideDistance;
|
|
} else {
|
|
pointX = Math.max(start.x, end.x) + sameSideDistance;
|
|
}
|
|
result = [
|
|
new Point(pointX, start.y),
|
|
new Point(pointX, end.y)
|
|
];
|
|
} else {
|
|
result = [
|
|
new Point(start.x + deltaX / 2, start.y),
|
|
new Point(start.x + deltaX / 2, start.y + deltaY)
|
|
];
|
|
}
|
|
} else {
|
|
result = [new Point(end.x, start.y)];
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_floatingPoints: function (start, end, sourceConnector) {
|
|
var sourceConnectorSide = sourceConnector ? this._connectorSide(sourceConnector, end) : null;
|
|
var cascadeStartHorizontal = this._startHorizontal(start, end, sourceConnectorSide);
|
|
var points = [
|
|
start,
|
|
start,
|
|
end,
|
|
end
|
|
];
|
|
var deltaX = end.x - start.x;
|
|
var deltaY = end.y - start.y;
|
|
var length = points.length;
|
|
var shiftX;
|
|
var shiftY;
|
|
for (var idx = 1; idx < length - 1; ++idx) {
|
|
if (cascadeStartHorizontal) {
|
|
if (idx % 2 !== 0) {
|
|
shiftX = deltaX / (length / 2);
|
|
shiftY = 0;
|
|
} else {
|
|
shiftX = 0;
|
|
shiftY = deltaY / ((length - 1) / 2);
|
|
}
|
|
} else {
|
|
if (idx % 2 !== 0) {
|
|
shiftX = 0;
|
|
shiftY = deltaY / (length / 2);
|
|
} else {
|
|
shiftX = deltaX / ((length - 1) / 2);
|
|
shiftY = 0;
|
|
}
|
|
}
|
|
points[idx] = new Point(points[idx - 1].x + shiftX, points[idx - 1].y + shiftY);
|
|
}
|
|
idx--;
|
|
if (cascadeStartHorizontal && idx % 2 !== 0 || !cascadeStartHorizontal && idx % 2 === 0) {
|
|
points[length - 2] = new Point(points[length - 1].x, points[length - 2].y);
|
|
} else {
|
|
points[length - 2] = new Point(points[length - 2].x, points[length - 1].y);
|
|
}
|
|
return [
|
|
points[1],
|
|
points[2]
|
|
];
|
|
},
|
|
_startHorizontal: function (start, end, sourceSide) {
|
|
var horizontal;
|
|
if (sourceSide !== null && (sourceSide === RIGHT || sourceSide === LEFT)) {
|
|
horizontal = true;
|
|
} else {
|
|
horizontal = Math.abs(start.x - end.x) > Math.abs(start.y - end.y);
|
|
}
|
|
return horizontal;
|
|
}
|
|
});
|
|
var AdornerBase = Class.extend({
|
|
init: function (diagram, options) {
|
|
var that = this;
|
|
that.diagram = diagram;
|
|
that.options = deepExtend({}, that.options, options);
|
|
that.visual = new Group();
|
|
that.diagram._adorners.push(that);
|
|
},
|
|
refresh: function () {
|
|
}
|
|
});
|
|
var ConnectionEditAdorner = AdornerBase.extend({
|
|
init: function (connection, options) {
|
|
var that = this, diagram;
|
|
that.connection = connection;
|
|
diagram = that.connection.diagram;
|
|
that._ts = diagram.toolService;
|
|
AdornerBase.fn.init.call(that, diagram, options);
|
|
var sp = that.connection.sourcePoint();
|
|
var tp = that.connection.targetPoint();
|
|
that.spVisual = new Circle(deepExtend(that.options.handles, { center: sp }));
|
|
that.epVisual = new Circle(deepExtend(that.options.handles, { center: tp }));
|
|
that.visual.append(that.spVisual);
|
|
that.visual.append(that.epVisual);
|
|
},
|
|
options: { handles: {} },
|
|
_getCursor: function () {
|
|
return Cursors.move;
|
|
},
|
|
start: function (p) {
|
|
this.handle = this._hitTest(p);
|
|
this.startPoint = p;
|
|
this._initialSource = this.connection.source();
|
|
this._initialTarget = this.connection.target();
|
|
switch (this.handle) {
|
|
case -1:
|
|
if (this.connection.targetConnector) {
|
|
this._ts._connectionManipulation(this.connection, this.connection.targetConnector.shape);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (this.connection.sourceConnector) {
|
|
this._ts._connectionManipulation(this.connection, this.connection.sourceConnector.shape);
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
move: function (handle, p) {
|
|
switch (handle) {
|
|
case -1:
|
|
this.connection.source(p);
|
|
break;
|
|
case 1:
|
|
this.connection.target(p);
|
|
break;
|
|
default:
|
|
var delta = p.minus(this.startPoint);
|
|
this.startPoint = p;
|
|
if (!this.connection.sourceConnector) {
|
|
this.connection.source(this.connection.sourcePoint().plus(delta));
|
|
}
|
|
if (!this.connection.targetConnector) {
|
|
this.connection.target(this.connection.targetPoint().plus(delta));
|
|
}
|
|
break;
|
|
}
|
|
this.refresh();
|
|
return true;
|
|
},
|
|
stop: function (p) {
|
|
var ts = this.diagram.toolService, item = ts.hoveredItem, target;
|
|
if (ts._hoveredConnector) {
|
|
target = ts._hoveredConnector._c;
|
|
} else if (item && item instanceof diagram.Shape) {
|
|
target = item.getConnector(AUTO) || item.getConnector(p);
|
|
} else {
|
|
target = p;
|
|
}
|
|
if (this.handle === -1) {
|
|
this.connection.source(target);
|
|
} else if (this.handle === 1) {
|
|
this.connection.target(target);
|
|
}
|
|
this.handle = undefined;
|
|
this._ts._connectionManipulation();
|
|
return new ConnectionEditUndoUnit(this.connection, this._initialSource, this._initialTarget);
|
|
},
|
|
_hitTest: function (point) {
|
|
var sourcePoint = this.connection.sourcePoint();
|
|
var targetPoint = this.connection.targetPoint();
|
|
var radiusX = this.options.handles.width / 2 + HIT_TEST_DISTANCE;
|
|
var radiusY = this.options.handles.height / 2 + HIT_TEST_DISTANCE;
|
|
var sourcePointDistance = sourcePoint.distanceTo(point);
|
|
var targetPointDistance = targetPoint.distanceTo(point);
|
|
var sourceHandle = new Rect(sourcePoint.x, sourcePoint.y).inflate(radiusX, radiusY).contains(point);
|
|
var targetHandle = new Rect(targetPoint.x, targetPoint.y).inflate(radiusX, radiusY).contains(point);
|
|
var handle = 0;
|
|
if (sourceHandle && (!targetHandle || sourcePointDistance < targetPointDistance)) {
|
|
handle = -1;
|
|
} else if (targetHandle && (!sourceHandle || targetPointDistance < sourcePointDistance)) {
|
|
handle = 1;
|
|
}
|
|
return handle;
|
|
},
|
|
refresh: function () {
|
|
this.spVisual.redraw({ center: this.diagram.modelToLayer(this.connection.sourcePoint()) });
|
|
this.epVisual.redraw({ center: this.diagram.modelToLayer(this.connection.targetPoint()) });
|
|
}
|
|
});
|
|
var ConnectorsAdorner = AdornerBase.extend({
|
|
init: function (diagram, options) {
|
|
var that = this;
|
|
AdornerBase.fn.init.call(that, diagram, options);
|
|
that._refreshHandler = function (e) {
|
|
if (e.item == that.shape) {
|
|
that.refresh();
|
|
}
|
|
};
|
|
},
|
|
show: function (shape) {
|
|
var that = this, len, i, ctr;
|
|
that._visible = true;
|
|
that.shape = shape;
|
|
that.diagram.bind(ITEMBOUNDSCHANGE, that._refreshHandler);
|
|
len = shape.connectors.length;
|
|
that.connectors = [];
|
|
that.visual.clear();
|
|
for (i = 0; i < len; i++) {
|
|
ctr = new ConnectorVisual(shape.connectors[i]);
|
|
that.connectors.push(ctr);
|
|
that.visual.append(ctr.visual);
|
|
}
|
|
that.visual.visible(true);
|
|
that.refresh();
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
that.diagram.unbind(ITEMBOUNDSCHANGE, that._refreshHandler);
|
|
that.shape = undefined;
|
|
that._visible = undefined;
|
|
that.visual.visible(false);
|
|
},
|
|
_hitTest: function (p) {
|
|
var ctr, i;
|
|
for (i = 0; i < this.connectors.length; i++) {
|
|
ctr = this.connectors[i];
|
|
if (ctr._hitTest(p)) {
|
|
ctr._hover(true);
|
|
this.diagram.toolService._hoveredConnector = ctr;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
refresh: function () {
|
|
if (this.shape) {
|
|
var bounds = this.shape.bounds();
|
|
bounds = this.diagram.modelToLayer(bounds);
|
|
this.visual.position(bounds.topLeft());
|
|
$.each(this.connectors, function () {
|
|
this.refresh();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
function hitToOppositeSide(hit, bounds) {
|
|
var result;
|
|
if (hit.x == -1 && hit.y == -1) {
|
|
result = bounds.bottomRight();
|
|
} else if (hit.x == 1 && hit.y == 1) {
|
|
result = bounds.topLeft();
|
|
} else if (hit.x == -1 && hit.y == 1) {
|
|
result = bounds.topRight();
|
|
} else if (hit.x == 1 && hit.y == -1) {
|
|
result = bounds.bottomLeft();
|
|
} else if (hit.x === 0 && hit.y == -1) {
|
|
result = bounds.bottom();
|
|
} else if (hit.x === 0 && hit.y == 1) {
|
|
result = bounds.top();
|
|
} else if (hit.x == 1 && hit.y === 0) {
|
|
result = bounds.left();
|
|
} else if (hit.x == -1 && hit.y === 0) {
|
|
result = bounds.right();
|
|
}
|
|
return result;
|
|
}
|
|
var ResizingAdorner = AdornerBase.extend({
|
|
init: function (diagram, options) {
|
|
var that = this;
|
|
AdornerBase.fn.init.call(that, diagram, options);
|
|
that._manipulating = false;
|
|
that.map = [];
|
|
that.shapes = [];
|
|
that._initSelection();
|
|
that._createHandles();
|
|
that.redraw();
|
|
that.diagram.bind('select', function (e) {
|
|
that._initialize(e.selected);
|
|
});
|
|
that._refreshHandler = function () {
|
|
if (!that._internalChange) {
|
|
that.refreshBounds();
|
|
that.refresh();
|
|
}
|
|
};
|
|
that._rotatedHandler = function () {
|
|
if (that.shapes.length == 1) {
|
|
that._angle = that.shapes[0].rotate().angle;
|
|
}
|
|
that._refreshHandler();
|
|
};
|
|
that.diagram.bind(ITEMBOUNDSCHANGE, that._refreshHandler).bind(ITEMROTATE, that._rotatedHandler);
|
|
that.refreshBounds();
|
|
that.refresh();
|
|
},
|
|
options: {
|
|
handles: {
|
|
fill: { color: '#fff' },
|
|
stroke: { color: '#282828' },
|
|
height: 7,
|
|
width: 7,
|
|
hover: {
|
|
fill: { color: '#282828' },
|
|
stroke: { color: '#282828' }
|
|
}
|
|
},
|
|
selectable: {
|
|
stroke: {
|
|
color: '#778899',
|
|
width: 1,
|
|
dashType: 'dash'
|
|
},
|
|
fill: { color: TRANSPARENT }
|
|
},
|
|
offset: 10
|
|
},
|
|
_initSelection: function () {
|
|
var that = this;
|
|
var diagram = that.diagram;
|
|
var selectable = diagram.options.selectable;
|
|
var options = deepExtend({}, that.options.selectable, selectable);
|
|
that.rect = new Rectangle(options);
|
|
that.visual.append(that.rect);
|
|
},
|
|
_resizable: function () {
|
|
return this.options.editable && this.options.editable.resize !== false;
|
|
},
|
|
_handleOptions: function () {
|
|
return (this.options.editable.resize || {}).handles || this.options.handles;
|
|
},
|
|
_createHandles: function () {
|
|
var handles, item, y, x;
|
|
if (this._resizable()) {
|
|
handles = this._handleOptions();
|
|
for (x = -1; x <= 1; x++) {
|
|
for (y = -1; y <= 1; y++) {
|
|
if (x !== 0 || y !== 0) {
|
|
item = new Rectangle(handles);
|
|
item.drawingElement._hover = proxy(this._hover, this);
|
|
this.map.push({
|
|
x: x,
|
|
y: y,
|
|
visual: item
|
|
});
|
|
this.visual.append(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
bounds: function (value) {
|
|
if (value) {
|
|
this._innerBounds = value.clone();
|
|
this._bounds = this.diagram.modelToLayer(value).inflate(this.options.offset, this.options.offset);
|
|
} else {
|
|
return this._bounds;
|
|
}
|
|
},
|
|
_hitTest: function (p) {
|
|
var tp = this.diagram.modelToLayer(p), i, hit, handleBounds, handlesCount = this.map.length, handle;
|
|
if (this._angle) {
|
|
tp = tp.clone().rotate(this._bounds.center(), this._angle);
|
|
}
|
|
if (this._resizable()) {
|
|
for (i = 0; i < handlesCount; i++) {
|
|
handle = this.map[i];
|
|
hit = new Point(handle.x, handle.y);
|
|
handleBounds = this._getHandleBounds(hit);
|
|
handleBounds.offset(this._bounds.x, this._bounds.y);
|
|
if (handleBounds.contains(tp)) {
|
|
return hit;
|
|
}
|
|
}
|
|
}
|
|
if (this._bounds.contains(tp)) {
|
|
return new Point(0, 0);
|
|
}
|
|
},
|
|
_getHandleBounds: function (p) {
|
|
if (this._resizable()) {
|
|
var handles = this._handleOptions(), w = handles.width, h = handles.height, r = new Rect(0, 0, w, h);
|
|
if (p.x < 0) {
|
|
r.x = -w / 2;
|
|
} else if (p.x === 0) {
|
|
r.x = Math.floor(this._bounds.width / 2) - w / 2;
|
|
} else if (p.x > 0) {
|
|
r.x = this._bounds.width + 1 - w / 2;
|
|
}
|
|
if (p.y < 0) {
|
|
r.y = -h / 2;
|
|
} else if (p.y === 0) {
|
|
r.y = Math.floor(this._bounds.height / 2) - h / 2;
|
|
} else if (p.y > 0) {
|
|
r.y = this._bounds.height + 1 - h / 2;
|
|
}
|
|
return r;
|
|
}
|
|
},
|
|
_getCursor: function (point) {
|
|
var hit = this._hitTest(point);
|
|
if (hit && hit.x >= -1 && hit.x <= 1 && hit.y >= -1 && hit.y <= 1 && this._resizable()) {
|
|
var angle = this._angle;
|
|
if (angle) {
|
|
angle = 360 - angle;
|
|
hit.rotate(new Point(0, 0), angle);
|
|
hit = new Point(Math.round(hit.x), Math.round(hit.y));
|
|
}
|
|
if (hit.x == -1 && hit.y == -1) {
|
|
return 'nw-resize';
|
|
}
|
|
if (hit.x == 1 && hit.y == 1) {
|
|
return 'se-resize';
|
|
}
|
|
if (hit.x == -1 && hit.y == 1) {
|
|
return 'sw-resize';
|
|
}
|
|
if (hit.x == 1 && hit.y == -1) {
|
|
return 'ne-resize';
|
|
}
|
|
if (hit.x === 0 && hit.y == -1) {
|
|
return 'n-resize';
|
|
}
|
|
if (hit.x === 0 && hit.y == 1) {
|
|
return 's-resize';
|
|
}
|
|
if (hit.x == 1 && hit.y === 0) {
|
|
return 'e-resize';
|
|
}
|
|
if (hit.x == -1 && hit.y === 0) {
|
|
return 'w-resize';
|
|
}
|
|
}
|
|
return this._manipulating ? Cursors.move : Cursors.select;
|
|
},
|
|
_initialize: function () {
|
|
var that = this, i, item, items = that.diagram.select();
|
|
that.shapes = [];
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if (item instanceof diagram.Shape) {
|
|
that.shapes.push(item);
|
|
item._rotationOffset = new Point();
|
|
}
|
|
}
|
|
that._angle = that.shapes.length == 1 ? that.shapes[0].rotate().angle : 0;
|
|
that._startAngle = that._angle;
|
|
that._rotates();
|
|
that._positions();
|
|
that.refreshBounds();
|
|
that.refresh();
|
|
that.redraw();
|
|
},
|
|
_rotates: function () {
|
|
var that = this, i, shape;
|
|
that.initialRotates = [];
|
|
for (i = 0; i < that.shapes.length; i++) {
|
|
shape = that.shapes[i];
|
|
that.initialRotates.push(shape.rotate().angle);
|
|
}
|
|
},
|
|
_positions: function () {
|
|
var that = this, i, shape;
|
|
that.initialStates = [];
|
|
for (i = 0; i < that.shapes.length; i++) {
|
|
shape = that.shapes[i];
|
|
that.initialStates.push(shape.bounds());
|
|
}
|
|
},
|
|
_hover: function (value, element) {
|
|
if (this._resizable()) {
|
|
var handleOptions = this._handleOptions(), hover = handleOptions.hover, stroke = handleOptions.stroke, fill = handleOptions.fill;
|
|
if (value && Utils.isDefined(hover.stroke)) {
|
|
stroke = deepExtend({}, stroke, hover.stroke);
|
|
}
|
|
if (value && Utils.isDefined(hover.fill)) {
|
|
fill = hover.fill;
|
|
}
|
|
element.stroke(stroke.color, stroke.width, stroke.opacity);
|
|
element.fill(fill.color, fill.opacity);
|
|
}
|
|
},
|
|
start: function (p) {
|
|
this._sp = p;
|
|
this._cp = p;
|
|
this._lp = p;
|
|
this._manipulating = true;
|
|
this._internalChange = true;
|
|
this.shapeStates = [];
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
this.shapeStates.push(shape.bounds());
|
|
}
|
|
},
|
|
redraw: function () {
|
|
var i, handle, visibleHandles = this._resizable();
|
|
for (i = 0; i < this.map.length; i++) {
|
|
handle = this.map[i];
|
|
handle.visual.visible(visibleHandles);
|
|
}
|
|
},
|
|
angle: function (value) {
|
|
if (defined(value)) {
|
|
this._angle = value;
|
|
}
|
|
return this._angle;
|
|
},
|
|
rotate: function () {
|
|
var center = this._innerBounds.center();
|
|
var currentAngle = this.angle();
|
|
this._internalChange = true;
|
|
for (var i = 0; i < this.shapes.length; i++) {
|
|
var shape = this.shapes[i];
|
|
currentAngle = (currentAngle + this.initialRotates[i] - this._startAngle) % 360;
|
|
shape.rotate(currentAngle, center);
|
|
}
|
|
this.refresh();
|
|
},
|
|
move: function (handle, p) {
|
|
var delta, dragging, dtl = new Point(), dbr = new Point(), bounds, center, shape, i, angle, newBounds, changed = 0, staticPoint, scaleX, scaleY;
|
|
if (handle.y === -2 && handle.x === -1) {
|
|
center = this._innerBounds.center();
|
|
this._angle = this._truncateAngle(Utils.findAngle(center, p));
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
angle = (this._angle + this.initialRotates[i] - this._startAngle) % 360;
|
|
shape.rotate(angle, center);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape);
|
|
}
|
|
this._rotating = true;
|
|
}
|
|
this.refresh();
|
|
} else {
|
|
if (this.shouldSnap()) {
|
|
var thr = this._truncateDistance(p.minus(this._lp));
|
|
if (thr.x === 0 && thr.y === 0) {
|
|
this._cp = p;
|
|
return;
|
|
}
|
|
delta = thr;
|
|
this._lp = new Point(this._lp.x + thr.x, this._lp.y + thr.y);
|
|
} else {
|
|
delta = p.minus(this._cp);
|
|
}
|
|
if (this.isDragHandle(handle)) {
|
|
dbr = dtl = delta;
|
|
dragging = true;
|
|
} else {
|
|
if (this._angle) {
|
|
delta.rotate(new Point(0, 0), this._angle);
|
|
}
|
|
if (handle.x == -1) {
|
|
dtl.x = delta.x;
|
|
} else if (handle.x == 1) {
|
|
dbr.x = delta.x;
|
|
}
|
|
if (handle.y == -1) {
|
|
dtl.y = delta.y;
|
|
} else if (handle.y == 1) {
|
|
dbr.y = delta.y;
|
|
}
|
|
}
|
|
if (!dragging) {
|
|
staticPoint = hitToOppositeSide(handle, this._innerBounds);
|
|
scaleX = (this._innerBounds.width + delta.x * handle.x) / this._innerBounds.width;
|
|
scaleY = (this._innerBounds.height + delta.y * handle.y) / this._innerBounds.height;
|
|
}
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
bounds = shape.bounds();
|
|
if (dragging) {
|
|
if (!canDrag(shape)) {
|
|
continue;
|
|
}
|
|
newBounds = this._displaceBounds(bounds, dtl, dbr, dragging);
|
|
} else {
|
|
newBounds = bounds.clone();
|
|
newBounds.scale(scaleX, scaleY, staticPoint, this._innerBounds.center(), shape.rotate().angle);
|
|
var newCenter = newBounds.center();
|
|
newCenter.rotate(bounds.center(), -this._angle);
|
|
newBounds = new Rect(newCenter.x - newBounds.width / 2, newCenter.y - newBounds.height / 2, newBounds.width, newBounds.height);
|
|
}
|
|
if (newBounds.width >= shape.options.minWidth && newBounds.height >= shape.options.minHeight) {
|
|
var oldBounds = bounds;
|
|
shape.bounds(newBounds);
|
|
if (shape.hasOwnProperty('layout')) {
|
|
shape.layout(shape, oldBounds, newBounds);
|
|
}
|
|
if (oldBounds.width !== newBounds.width || oldBounds.height !== newBounds.height) {
|
|
shape.rotate(shape.rotate().angle);
|
|
}
|
|
changed += 1;
|
|
}
|
|
}
|
|
if (changed) {
|
|
if (changed == i) {
|
|
newBounds = this._displaceBounds(this._innerBounds, dtl, dbr, dragging);
|
|
this.bounds(newBounds);
|
|
} else {
|
|
this.refreshBounds();
|
|
}
|
|
this.refresh();
|
|
}
|
|
this._positions();
|
|
}
|
|
this._cp = p;
|
|
},
|
|
isDragHandle: function (handle) {
|
|
return handle.x === 0 && handle.y === 0;
|
|
},
|
|
cancel: function () {
|
|
var shapes = this.shapes;
|
|
var states = this.shapeStates;
|
|
for (var idx = 0; idx < shapes.length; idx++) {
|
|
shapes[idx].bounds(states[idx]);
|
|
}
|
|
this.refreshBounds();
|
|
this.refresh();
|
|
this._manipulating = undefined;
|
|
this._internalChange = undefined;
|
|
this._rotating = undefined;
|
|
},
|
|
_truncatePositionToGuides: function (bounds) {
|
|
if (this.diagram.ruler) {
|
|
return this.diagram.ruler.truncatePositionToGuides(bounds);
|
|
}
|
|
return bounds;
|
|
},
|
|
_truncateSizeToGuides: function (bounds) {
|
|
if (this.diagram.ruler) {
|
|
return this.diagram.ruler.truncateSizeToGuides(bounds);
|
|
}
|
|
return bounds;
|
|
},
|
|
_truncateAngle: function (a) {
|
|
var snap = this.snapOptions();
|
|
var snapAngle = Math.max(snap.angle || DEFAULT_SNAP_ANGLE, MIN_SNAP_ANGLE);
|
|
return snap ? Math.floor(a % 360 / snapAngle) * snapAngle : a % 360;
|
|
},
|
|
_truncateDistance: function (d) {
|
|
if (d instanceof diagram.Point) {
|
|
return new diagram.Point(this._truncateDistance(d.x), this._truncateDistance(d.y));
|
|
} else {
|
|
var snap = this.snapOptions() || {};
|
|
var snapSize = Math.max(snap.size || DEFAULT_SNAP_SIZE, MIN_SNAP_SIZE);
|
|
return snap ? Math.floor(d / snapSize) * snapSize : d;
|
|
}
|
|
},
|
|
snapOptions: function () {
|
|
var editable = this.diagram.options.editable;
|
|
var snap = ((editable || {}).drag || {}).snap || {};
|
|
return snap;
|
|
},
|
|
shouldSnap: function () {
|
|
var editable = this.diagram.options.editable;
|
|
var drag = (editable || {}).drag;
|
|
var snap = (drag || {}).snap;
|
|
return editable !== false && drag !== false && snap !== false;
|
|
},
|
|
_displaceBounds: function (bounds, dtl, dbr, dragging) {
|
|
var tl = bounds.topLeft().plus(dtl), br = bounds.bottomRight().plus(dbr), newBounds = Rect.fromPoints(tl, br), newCenter;
|
|
if (!dragging) {
|
|
newCenter = newBounds.center();
|
|
newCenter.rotate(bounds.center(), -this._angle);
|
|
newBounds = new Rect(newCenter.x - newBounds.width / 2, newCenter.y - newBounds.height / 2, newBounds.width, newBounds.height);
|
|
}
|
|
return newBounds;
|
|
},
|
|
stop: function () {
|
|
var unit, i, shape;
|
|
if (this._cp != this._sp) {
|
|
if (this._rotating) {
|
|
unit = new RotateUnit(this, this.shapes, this.initialRotates);
|
|
this._rotating = false;
|
|
} else if (this._diffStates()) {
|
|
if (this.diagram.ruler) {
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
var bounds = shape.bounds();
|
|
bounds = this._truncateSizeToGuides(this._truncatePositionToGuides(bounds));
|
|
shape.bounds(bounds);
|
|
this.refreshBounds();
|
|
this.refresh();
|
|
}
|
|
}
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
shape.updateModel();
|
|
}
|
|
unit = new TransformUnit(this.shapes, this.shapeStates, this);
|
|
this.diagram._syncShapeChanges();
|
|
}
|
|
}
|
|
this._manipulating = undefined;
|
|
this._internalChange = undefined;
|
|
this._rotating = undefined;
|
|
return unit;
|
|
},
|
|
_diffStates: function () {
|
|
var shapes = this.shapes;
|
|
var states = this.shapeStates;
|
|
for (var idx = 0; idx < shapes.length; idx++) {
|
|
if (!shapes[idx].bounds().equals(states[idx])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
refreshBounds: function () {
|
|
var bounds = this.shapes.length == 1 ? this.shapes[0].bounds().clone() : this.diagram.boundingBox(this.shapes, true);
|
|
this.bounds(bounds);
|
|
},
|
|
refresh: function () {
|
|
var that = this, b, bounds;
|
|
if (this.shapes.length > 0) {
|
|
bounds = this.bounds();
|
|
this.visual.visible(true);
|
|
this.visual.position(bounds.topLeft());
|
|
$.each(this.map, function () {
|
|
b = that._getHandleBounds(new Point(this.x, this.y));
|
|
this.visual.position(b.topLeft());
|
|
});
|
|
this.visual.position(bounds.topLeft());
|
|
var center = new Point(bounds.width / 2, bounds.height / 2);
|
|
this.visual.rotate(this._angle, center);
|
|
this.rect.redraw({
|
|
width: bounds.width,
|
|
height: bounds.height
|
|
});
|
|
if (this.rotationThumb) {
|
|
var thumb = this.options.editable.rotate.thumb;
|
|
this._rotationThumbBounds = new Rect(bounds.center().x, bounds.y + thumb.y, 0, 0).inflate(thumb.width);
|
|
this.rotationThumb.redraw({ x: bounds.width / 2 - thumb.width / 2 });
|
|
}
|
|
} else {
|
|
this.visual.visible(false);
|
|
}
|
|
}
|
|
});
|
|
var Selector = Class.extend({
|
|
init: function (diagram) {
|
|
var selectable = diagram.options.selectable;
|
|
this.options = deepExtend({}, this.options, selectable);
|
|
this.visual = new Rectangle(this.options);
|
|
this.diagram = diagram;
|
|
},
|
|
options: {
|
|
stroke: {
|
|
color: '#778899',
|
|
width: 1,
|
|
dashType: 'dash'
|
|
},
|
|
fill: { color: TRANSPARENT }
|
|
},
|
|
start: function (p) {
|
|
this._sp = this._ep = p;
|
|
this.refresh();
|
|
this.diagram._adorn(this, true);
|
|
},
|
|
end: function () {
|
|
this._sp = this._ep = undefined;
|
|
this.diagram._adorn(this, false);
|
|
},
|
|
bounds: function (value) {
|
|
if (value) {
|
|
this._bounds = value;
|
|
}
|
|
return this._bounds;
|
|
},
|
|
move: function (p) {
|
|
this._ep = p;
|
|
this.refresh();
|
|
},
|
|
refresh: function () {
|
|
if (this._sp) {
|
|
var visualBounds = Rect.fromPoints(this.diagram.modelToLayer(this._sp), this.diagram.modelToLayer(this._ep));
|
|
this.bounds(Rect.fromPoints(this._sp, this._ep));
|
|
this.visual.position(visualBounds.topLeft());
|
|
this.visual.redraw({
|
|
height: visualBounds.height + 1,
|
|
width: visualBounds.width + 1
|
|
});
|
|
}
|
|
}
|
|
});
|
|
var ConnectorVisual = Class.extend({
|
|
init: function (connector) {
|
|
this.options = deepExtend({}, connector.options);
|
|
this._c = connector;
|
|
this.visual = new Circle(this.options);
|
|
this.refresh();
|
|
},
|
|
_hover: function (value) {
|
|
var options = this.options, hover = options.hover, stroke = options.stroke, fill = options.fill;
|
|
if (value && Utils.isDefined(hover.stroke)) {
|
|
stroke = deepExtend({}, stroke, hover.stroke);
|
|
}
|
|
if (value && Utils.isDefined(hover.fill)) {
|
|
fill = hover.fill;
|
|
}
|
|
this.visual.redraw({
|
|
stroke: stroke,
|
|
fill: fill
|
|
});
|
|
},
|
|
refresh: function () {
|
|
var p = this._c.shape.diagram.modelToView(this._c.position()), relative = p.minus(this._c.shape.bounds('transformed').topLeft()), value = new Rect(p.x, p.y, 0, 0);
|
|
value.inflate(this.options.width / 2, this.options.height / 2);
|
|
this._visualBounds = value;
|
|
this.visual.redraw({ center: new Point(relative.x, relative.y) });
|
|
},
|
|
_hitTest: function (p) {
|
|
var tp = this._c.shape.diagram.modelToView(p);
|
|
return this._visualBounds.contains(tp);
|
|
}
|
|
});
|
|
function canDrag(element) {
|
|
var editable = element.options.editable;
|
|
return editable && editable.drag !== false;
|
|
}
|
|
function hitTestShapeConnectors(shape, point) {
|
|
var connector, position, rect;
|
|
for (var idx = 0; idx < shape.connectors.length; idx++) {
|
|
connector = shape.connectors[idx];
|
|
position = connector.position();
|
|
rect = new Rect(position.x, position.y);
|
|
rect.inflate(HIT_TEST_DISTANCE, HIT_TEST_DISTANCE);
|
|
if (rect.contains(point)) {
|
|
return connector;
|
|
}
|
|
}
|
|
}
|
|
deepExtend(diagram, {
|
|
CompositeUnit: CompositeUnit,
|
|
TransformUnit: TransformUnit,
|
|
PanUndoUnit: PanUndoUnit,
|
|
AddShapeUnit: AddShapeUnit,
|
|
AddConnectionUnit: AddConnectionUnit,
|
|
DeleteShapeUnit: DeleteShapeUnit,
|
|
DeleteConnectionUnit: DeleteConnectionUnit,
|
|
ConnectionEditAdorner: ConnectionEditAdorner,
|
|
ConnectionTool: ConnectionTool,
|
|
ConnectorVisual: ConnectorVisual,
|
|
UndoRedoService: UndoRedoService,
|
|
ResizingAdorner: ResizingAdorner,
|
|
Selector: Selector,
|
|
ToolService: ToolService,
|
|
ConnectorsAdorner: ConnectorsAdorner,
|
|
LayoutUndoUnit: LayoutUndoUnit,
|
|
ConnectionEditUnit: ConnectionEditUnit,
|
|
ToFrontUnit: ToFrontUnit,
|
|
ToBackUnit: ToBackUnit,
|
|
ConnectionRouterBase: ConnectionRouterBase,
|
|
PolylineRouter: PolylineRouter,
|
|
CascadingRouter: CascadingRouter,
|
|
SelectionTool: SelectionTool,
|
|
ScrollerTool: ScrollerTool,
|
|
PointerTool: PointerTool,
|
|
ConnectionEditTool: ConnectionEditTool,
|
|
RotateUnit: RotateUnit
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/layout', ['dataviz/diagram/math'], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Graph = diagram.Graph, Node = diagram.Node, Link = diagram.Link, deepExtend = kendo.deepExtend, Size = diagram.Size, Rect = diagram.Rect, Dictionary = diagram.Dictionary, Set = diagram.Set, HyperTree = diagram.Graph, Utils = diagram.Utils, Point = diagram.Point, EPSILON = 0.000001, DEG_TO_RAD = Math.PI / 180, contains = Utils.contains, grep = $.grep;
|
|
var LayoutBase = kendo.Class.extend({
|
|
defaultOptions: {
|
|
type: 'Tree',
|
|
subtype: 'Down',
|
|
roots: null,
|
|
animate: false,
|
|
limitToView: false,
|
|
friction: 0.9,
|
|
nodeDistance: 50,
|
|
iterations: 300,
|
|
horizontalSeparation: 90,
|
|
verticalSeparation: 50,
|
|
underneathVerticalTopOffset: 15,
|
|
underneathHorizontalOffset: 15,
|
|
underneathVerticalSeparation: 15,
|
|
grid: {
|
|
width: 1500,
|
|
offsetX: 50,
|
|
offsetY: 50,
|
|
componentSpacingX: 20,
|
|
componentSpacingY: 20
|
|
},
|
|
layerSeparation: 50,
|
|
layeredIterations: 2,
|
|
startRadialAngle: 0,
|
|
endRadialAngle: 360,
|
|
radialSeparation: 150,
|
|
radialFirstLevelSeparation: 200,
|
|
keepComponentsInOneRadialLayout: false,
|
|
ignoreContainers: true,
|
|
layoutContainerChildren: false,
|
|
ignoreInvisible: true,
|
|
animateTransitions: false
|
|
},
|
|
init: function () {
|
|
},
|
|
gridLayoutComponents: function (components) {
|
|
if (!components) {
|
|
throw 'No components supplied.';
|
|
}
|
|
Utils.forEach(components, function (c) {
|
|
c.calcBounds();
|
|
});
|
|
components.sort(function (a, b) {
|
|
return b.bounds.width - a.bounds.width;
|
|
});
|
|
var maxWidth = this.options.grid.width, offsetX = this.options.grid.componentSpacingX, offsetY = this.options.grid.componentSpacingY, height = 0, startX = this.options.grid.offsetX, startY = this.options.grid.offsetY, x = startX, y = startY, i, resultLinkSet = [], resultNodeSet = [];
|
|
while (components.length > 0) {
|
|
if (x >= maxWidth) {
|
|
x = startX;
|
|
y += height + offsetY;
|
|
height = 0;
|
|
}
|
|
var component = components.pop();
|
|
this.moveToOffset(component, new Point(x, y));
|
|
for (i = 0; i < component.nodes.length; i++) {
|
|
resultNodeSet.push(component.nodes[i]);
|
|
}
|
|
for (i = 0; i < component.links.length; i++) {
|
|
resultLinkSet.push(component.links[i]);
|
|
}
|
|
var boundingRect = component.bounds;
|
|
var currentHeight = boundingRect.height;
|
|
if (currentHeight <= 0 || isNaN(currentHeight)) {
|
|
currentHeight = 0;
|
|
}
|
|
var currentWidth = boundingRect.width;
|
|
if (currentWidth <= 0 || isNaN(currentWidth)) {
|
|
currentWidth = 0;
|
|
}
|
|
if (currentHeight >= height) {
|
|
height = currentHeight;
|
|
}
|
|
x += currentWidth + offsetX;
|
|
}
|
|
return {
|
|
nodes: resultNodeSet,
|
|
links: resultLinkSet
|
|
};
|
|
},
|
|
moveToOffset: function (component, p) {
|
|
var i, j, bounds = component.bounds, deltax = p.x - bounds.x, deltay = p.y - bounds.y;
|
|
for (i = 0; i < component.nodes.length; i++) {
|
|
var node = component.nodes[i];
|
|
var nodeBounds = node.bounds();
|
|
if (nodeBounds.width === 0 && nodeBounds.height === 0 && nodeBounds.x === 0 && nodeBounds.y === 0) {
|
|
nodeBounds = new Rect(0, 0, 0, 0);
|
|
}
|
|
nodeBounds.x += deltax;
|
|
nodeBounds.y += deltay;
|
|
node.bounds(nodeBounds);
|
|
}
|
|
for (i = 0; i < component.links.length; i++) {
|
|
var link = component.links[i];
|
|
if (link.points) {
|
|
var newpoints = [];
|
|
var points = link.points;
|
|
for (j = 0; j < points.length; j++) {
|
|
var pt = points[j];
|
|
pt.x += deltax;
|
|
pt.y += deltay;
|
|
newpoints.push(pt);
|
|
}
|
|
link.points = newpoints;
|
|
}
|
|
}
|
|
this.currentHorizontalOffset += bounds.width + this.options.grid.offsetX;
|
|
return new Point(deltax, deltay);
|
|
},
|
|
transferOptions: function (options) {
|
|
this.options = kendo.deepExtend({}, this.defaultOptions);
|
|
if (Utils.isUndefined(options)) {
|
|
return;
|
|
}
|
|
this.options = kendo.deepExtend(this.options, options || {});
|
|
}
|
|
});
|
|
var DiagramToHyperTreeAdapter = kendo.Class.extend({
|
|
init: function (diagram) {
|
|
this.nodeMap = new Dictionary();
|
|
this.shapeMap = new Dictionary();
|
|
this.nodes = [];
|
|
this.edges = [];
|
|
this.edgeMap = new Dictionary();
|
|
this.finalNodes = [];
|
|
this.finalLinks = [];
|
|
this.ignoredConnections = [];
|
|
this.ignoredShapes = [];
|
|
this.hyperMap = new Dictionary();
|
|
this.hyperTree = new Graph();
|
|
this.finalGraph = null;
|
|
this.diagram = diagram;
|
|
},
|
|
convert: function (options) {
|
|
if (Utils.isUndefined(this.diagram)) {
|
|
throw 'No diagram to convert.';
|
|
}
|
|
this.options = kendo.deepExtend({
|
|
ignoreInvisible: true,
|
|
ignoreContainers: true,
|
|
layoutContainerChildren: false
|
|
}, options || {});
|
|
this.clear();
|
|
this._renormalizeShapes();
|
|
this._renormalizeConnections();
|
|
this.finalNodes = new Dictionary(this.nodes);
|
|
this.finalLinks = new Dictionary(this.edges);
|
|
this.finalGraph = new Graph();
|
|
this.finalNodes.forEach(function (n) {
|
|
this.finalGraph.addNode(n);
|
|
}, this);
|
|
this.finalLinks.forEach(function (l) {
|
|
this.finalGraph.addExistingLink(l);
|
|
}, this);
|
|
return this.finalGraph;
|
|
},
|
|
mapConnection: function (connection) {
|
|
return this.edgeMap.get(connection.id);
|
|
},
|
|
mapShape: function (shape) {
|
|
return this.nodeMap.get(shape.id);
|
|
},
|
|
getEdge: function (a, b) {
|
|
return Utils.first(a.links, function (link) {
|
|
return link.getComplement(a) === b;
|
|
});
|
|
},
|
|
clear: function () {
|
|
this.finalGraph = null;
|
|
this.hyperTree = !this.options.ignoreContainers && this.options.layoutContainerChildren ? new HyperTree() : null;
|
|
this.hyperMap = !this.options.ignoreContainers && this.options.layoutContainerChildren ? new Dictionary() : null;
|
|
this.nodeMap = new Dictionary();
|
|
this.shapeMap = new Dictionary();
|
|
this.nodes = [];
|
|
this.edges = [];
|
|
this.edgeMap = new Dictionary();
|
|
this.ignoredConnections = [];
|
|
this.ignoredShapes = [];
|
|
this.finalNodes = [];
|
|
this.finalLinks = [];
|
|
},
|
|
listToRoot: function (containerGraph) {
|
|
var list = [];
|
|
var s = containerGraph.container;
|
|
if (!s) {
|
|
return list;
|
|
}
|
|
list.push(s);
|
|
while (s.parentContainer) {
|
|
s = s.parentContainer;
|
|
list.push(s);
|
|
}
|
|
list.reverse();
|
|
return list;
|
|
},
|
|
firstNonIgnorableContainer: function (shape) {
|
|
if (shape.isContainer && !this._isIgnorableItem(shape)) {
|
|
return shape;
|
|
}
|
|
return !shape.parentContainer ? null : this.firstNonIgnorableContainer(shape.parentContainer);
|
|
},
|
|
isContainerConnection: function (a, b) {
|
|
if (a.isContainer && this.isDescendantOf(a, b)) {
|
|
return true;
|
|
}
|
|
return b.isContainer && this.isDescendantOf(b, a);
|
|
},
|
|
isDescendantOf: function (scope, a) {
|
|
if (!scope.isContainer) {
|
|
throw 'Expecting a container.';
|
|
}
|
|
if (scope === a) {
|
|
return false;
|
|
}
|
|
if (contains(scope.children, a)) {
|
|
return true;
|
|
}
|
|
var containers = [];
|
|
for (var i = 0, len = scope.children.length; i < len; i++) {
|
|
var c = scope.children[i];
|
|
if (c.isContainer && this.isDescendantOf(c, a)) {
|
|
containers.push(c);
|
|
}
|
|
}
|
|
return containers.length > 0;
|
|
},
|
|
isIgnorableItem: function (shape) {
|
|
if (this.options.ignoreInvisible) {
|
|
if (shape.isCollapsed && this._isVisible(shape)) {
|
|
return false;
|
|
}
|
|
if (!shape.isCollapsed && this._isVisible(shape)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
return shape.isCollapsed && !this._isTop(shape);
|
|
}
|
|
},
|
|
isShapeMapped: function (shape) {
|
|
return shape.isCollapsed && !this._isVisible(shape) && !this._isTop(shape);
|
|
},
|
|
leastCommonAncestor: function (a, b) {
|
|
if (!a) {
|
|
throw 'Parameter should not be null.';
|
|
}
|
|
if (!b) {
|
|
throw 'Parameter should not be null.';
|
|
}
|
|
if (!this.hyperTree) {
|
|
throw 'No hypertree available.';
|
|
}
|
|
var al = this.listToRoot(a);
|
|
var bl = this.listToRoot(b);
|
|
var found = null;
|
|
if (Utils.isEmpty(al) || Utils.isEmpty(bl)) {
|
|
return this.hyperTree.root.data;
|
|
}
|
|
var xa = al[0];
|
|
var xb = bl[0];
|
|
var i = 0;
|
|
while (xa === xb) {
|
|
found = al[i];
|
|
i++;
|
|
if (i >= al.length || i >= bl.length) {
|
|
break;
|
|
}
|
|
xa = al[i];
|
|
xb = bl[i];
|
|
}
|
|
if (!found) {
|
|
return this.hyperTree.root.data;
|
|
} else {
|
|
return grep(this.hyperTree.nodes, function (n) {
|
|
return n.data.container === found;
|
|
});
|
|
}
|
|
},
|
|
_isTop: function (item) {
|
|
return !item.parentContainer;
|
|
},
|
|
_isVisible: function (shape) {
|
|
if (!shape.visible()) {
|
|
return false;
|
|
}
|
|
return !shape.parentContainer ? shape.visible() : this._isVisible(shape.parentContainer);
|
|
},
|
|
_isCollapsed: function (shape) {
|
|
if (shape.isContainer && shape.isCollapsed) {
|
|
return true;
|
|
}
|
|
return shape.parentContainer && this._isCollapsed(shape.parentContainer);
|
|
},
|
|
_renormalizeShapes: function () {
|
|
if (this.options.ignoreContainers) {
|
|
for (var i = 0, len = this.diagram.shapes.length; i < len; i++) {
|
|
var shape = this.diagram.shapes[i];
|
|
if (this.options.ignoreInvisible && !this._isVisible(shape) || shape.isContainer) {
|
|
this.ignoredShapes.push(shape);
|
|
continue;
|
|
}
|
|
var node = new Node(shape.id, shape);
|
|
node.isVirtual = false;
|
|
this.nodeMap.add(shape.id, node);
|
|
this.nodes.push(node);
|
|
}
|
|
} else {
|
|
throw 'Containers are not supported yet, but stay tuned.';
|
|
}
|
|
},
|
|
_renormalizeConnections: function () {
|
|
if (this.diagram.connections.length === 0) {
|
|
return;
|
|
}
|
|
for (var i = 0, len = this.diagram.connections.length; i < len; i++) {
|
|
var conn = this.diagram.connections[i];
|
|
if (this.isIgnorableItem(conn)) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
var source = !conn.sourceConnector ? null : conn.sourceConnector.shape;
|
|
var sink = !conn.targetConnector ? null : conn.targetConnector.shape;
|
|
if (!source || !sink) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
if (contains(this.ignoredShapes, source) && !this.shapeMap.containsKey(source)) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
if (contains(this.ignoredShapes, sink) && !this.shapeMap.containsKey(sink)) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
if (this.shapeMap.containsKey(source)) {
|
|
source = this.shapeMap[source];
|
|
}
|
|
if (this.shapeMap.containsKey(sink)) {
|
|
sink = this.shapeMap[sink];
|
|
}
|
|
var sourceNode = this.mapShape(source);
|
|
var sinkNode = this.mapShape(sink);
|
|
if (sourceNode === sinkNode || this.areConnectedAlready(sourceNode, sinkNode)) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
if (sourceNode === null || sinkNode === null) {
|
|
throw 'A shape was not mapped to a node.';
|
|
}
|
|
if (this.options.ignoreContainers) {
|
|
if (sourceNode.isVirtual || sinkNode.isVirtual) {
|
|
this.ignoredConnections.push(conn);
|
|
continue;
|
|
}
|
|
var newEdge = new Link(sourceNode, sinkNode, conn.id, conn);
|
|
this.edgeMap.add(conn.id, newEdge);
|
|
this.edges.push(newEdge);
|
|
} else {
|
|
throw 'Containers are not supported yet, but stay tuned.';
|
|
}
|
|
}
|
|
},
|
|
areConnectedAlready: function (n, m) {
|
|
return Utils.any(this.edges, function (l) {
|
|
return l.source === n && l.target === m || l.source === m && l.target === n;
|
|
});
|
|
}
|
|
});
|
|
var SpringLayout = LayoutBase.extend({
|
|
init: function (diagram) {
|
|
var that = this;
|
|
LayoutBase.fn.init.call(that);
|
|
if (Utils.isUndefined(diagram)) {
|
|
throw 'Diagram is not specified.';
|
|
}
|
|
this.diagram = diagram;
|
|
},
|
|
layout: function (options) {
|
|
this.transferOptions(options);
|
|
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
|
|
var graph = adapter.convert(options);
|
|
if (graph.isEmpty()) {
|
|
return;
|
|
}
|
|
var components = graph.getConnectedComponents();
|
|
if (Utils.isEmpty(components)) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < components.length; i++) {
|
|
var component = components[i];
|
|
this.layoutGraph(component, options);
|
|
}
|
|
var finalNodeSet = this.gridLayoutComponents(components);
|
|
return new diagram.LayoutState(this.diagram, finalNodeSet);
|
|
},
|
|
layoutGraph: function (graph, options) {
|
|
if (Utils.isDefined(options)) {
|
|
this.transferOptions(options);
|
|
}
|
|
this.graph = graph;
|
|
var initialTemperature = this.options.nodeDistance * 9;
|
|
this.temperature = initialTemperature;
|
|
var guessBounds = this._expectedBounds();
|
|
this.width = guessBounds.width;
|
|
this.height = guessBounds.height;
|
|
for (var step = 0; step < this.options.iterations; step++) {
|
|
this.refineStage = step >= this.options.iterations * 5 / 6;
|
|
this.tick();
|
|
this.temperature = this.refineStage ? initialTemperature / 30 : initialTemperature * (1 - step / (2 * this.options.iterations));
|
|
}
|
|
},
|
|
tick: function () {
|
|
var i;
|
|
for (i = 0; i < this.graph.nodes.length; i++) {
|
|
this._repulsion(this.graph.nodes[i]);
|
|
}
|
|
for (i = 0; i < this.graph.links.length; i++) {
|
|
this._attraction(this.graph.links[i]);
|
|
}
|
|
for (i = 0; i < this.graph.nodes.length; i++) {
|
|
var node = this.graph.nodes[i];
|
|
var offset = Math.sqrt(node.dx * node.dx + node.dy * node.dy);
|
|
if (offset === 0) {
|
|
return;
|
|
}
|
|
node.x += Math.min(offset, this.temperature) * node.dx / offset;
|
|
node.y += Math.min(offset, this.temperature) * node.dy / offset;
|
|
if (this.options.limitToView) {
|
|
node.x = Math.min(this.width, Math.max(node.width / 2, node.x));
|
|
node.y = Math.min(this.height, Math.max(node.height / 2, node.y));
|
|
}
|
|
}
|
|
},
|
|
_shake: function (node) {
|
|
var rho = Math.random() * this.options.nodeDistance / 4;
|
|
var alpha = Math.random() * 2 * Math.PI;
|
|
node.x += rho * Math.cos(alpha);
|
|
node.y -= rho * Math.sin(alpha);
|
|
},
|
|
_InverseSquareForce: function (d, n, m) {
|
|
var force;
|
|
if (!this.refineStage) {
|
|
force = Math.pow(d, 2) / Math.pow(this.options.nodeDistance, 2);
|
|
} else {
|
|
var deltax = n.x - m.x;
|
|
var deltay = n.y - m.y;
|
|
var wn = n.width / 2;
|
|
var hn = n.height / 2;
|
|
var wm = m.width / 2;
|
|
var hm = m.height / 2;
|
|
force = Math.pow(deltax, 2) / Math.pow(wn + wm + this.options.nodeDistance, 2) + Math.pow(deltay, 2) / Math.pow(hn + hm + this.options.nodeDistance, 2);
|
|
}
|
|
return force * 4 / 3;
|
|
},
|
|
_SquareForce: function (d, n, m) {
|
|
return 1 / this._InverseSquareForce(d, n, m);
|
|
},
|
|
_repulsion: function (n) {
|
|
n.dx = 0;
|
|
n.dy = 0;
|
|
Utils.forEach(this.graph.nodes, function (m) {
|
|
if (m === n) {
|
|
return;
|
|
}
|
|
while (n.x === m.x && n.y === m.y) {
|
|
this._shake(m);
|
|
}
|
|
var vx = n.x - m.x;
|
|
var vy = n.y - m.y;
|
|
var distance = Math.sqrt(vx * vx + vy * vy);
|
|
var r = this._SquareForce(distance, n, m) * 2;
|
|
n.dx += vx / distance * r;
|
|
n.dy += vy / distance * r;
|
|
}, this);
|
|
},
|
|
_attraction: function (link) {
|
|
var t = link.target;
|
|
var s = link.source;
|
|
if (s === t) {
|
|
return;
|
|
}
|
|
while (s.x === t.x && s.y === t.y) {
|
|
this._shake(t);
|
|
}
|
|
var vx = s.x - t.x;
|
|
var vy = s.y - t.y;
|
|
var distance = Math.sqrt(vx * vx + vy * vy);
|
|
var a = this._InverseSquareForce(distance, s, t) * 5;
|
|
var dx = vx / distance * a;
|
|
var dy = vy / distance * a;
|
|
t.dx += dx;
|
|
t.dy += dy;
|
|
s.dx -= dx;
|
|
s.dy -= dy;
|
|
},
|
|
_expectedBounds: function () {
|
|
var size, N = this.graph.nodes.length, ratio = 1.5, multiplier = 4;
|
|
if (N === 0) {
|
|
return size;
|
|
}
|
|
size = Utils.fold(this.graph.nodes, function (s, node) {
|
|
var area = node.width * node.height;
|
|
if (area > 0) {
|
|
s += Math.sqrt(area);
|
|
return s;
|
|
}
|
|
return 0;
|
|
}, 0, this);
|
|
var av = size / N;
|
|
var squareSize = av * Math.ceil(Math.sqrt(N));
|
|
var width = squareSize * Math.sqrt(ratio);
|
|
var height = squareSize / Math.sqrt(ratio);
|
|
return {
|
|
width: width * multiplier,
|
|
height: height * multiplier
|
|
};
|
|
}
|
|
});
|
|
var TreeLayoutProcessor = kendo.Class.extend({
|
|
init: function (options) {
|
|
this.center = null;
|
|
this.options = options;
|
|
},
|
|
layout: function (treeGraph, root) {
|
|
this.graph = treeGraph;
|
|
if (!this.graph.nodes || this.graph.nodes.length === 0) {
|
|
return;
|
|
}
|
|
if (!contains(this.graph.nodes, root)) {
|
|
throw 'The given root is not in the graph.';
|
|
}
|
|
this.center = root;
|
|
this.graph.cacheRelationships();
|
|
this.layoutSwitch();
|
|
},
|
|
layoutLeft: function (left) {
|
|
this.setChildrenDirection(this.center, 'Left', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
var h = 0, w = 0, y, i, node;
|
|
for (i = 0; i < left.length; i++) {
|
|
node = left[i];
|
|
node.TreeDirection = 'Left';
|
|
var s = this.measure(node, Size.Empty);
|
|
w = Math.max(w, s.Width);
|
|
h += s.height + this.options.verticalSeparation;
|
|
}
|
|
h -= this.options.verticalSeparation;
|
|
var x = this.center.x - this.options.horizontalSeparation;
|
|
y = this.center.y + (this.center.height - h) / 2;
|
|
for (i = 0; i < left.length; i++) {
|
|
node = left[i];
|
|
var p = new Point(x - node.Size.width, y);
|
|
this.arrange(node, p);
|
|
y += node.Size.height + this.options.verticalSeparation;
|
|
}
|
|
},
|
|
layoutRight: function (right) {
|
|
this.setChildrenDirection(this.center, 'Right', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
var h = 0, w = 0, y, i, node;
|
|
for (i = 0; i < right.length; i++) {
|
|
node = right[i];
|
|
node.TreeDirection = 'Right';
|
|
var s = this.measure(node, Size.Empty);
|
|
w = Math.max(w, s.Width);
|
|
h += s.height + this.options.verticalSeparation;
|
|
}
|
|
h -= this.options.verticalSeparation;
|
|
var x = this.center.x + this.options.horizontalSeparation + this.center.width;
|
|
y = this.center.y + (this.center.height - h) / 2;
|
|
for (i = 0; i < right.length; i++) {
|
|
node = right[i];
|
|
var p = new Point(x, y);
|
|
this.arrange(node, p);
|
|
y += node.Size.height + this.options.verticalSeparation;
|
|
}
|
|
},
|
|
layoutUp: function (up) {
|
|
this.setChildrenDirection(this.center, 'Up', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
var w = 0, y, node, i;
|
|
for (i = 0; i < up.length; i++) {
|
|
node = up[i];
|
|
node.TreeDirection = 'Up';
|
|
var s = this.measure(node, Size.Empty);
|
|
w += s.width + this.options.horizontalSeparation;
|
|
}
|
|
w -= this.options.horizontalSeparation;
|
|
var x = this.center.x + this.center.width / 2 - w / 2;
|
|
for (i = 0; i < up.length; i++) {
|
|
node = up[i];
|
|
y = this.center.y - this.options.verticalSeparation - node.Size.height;
|
|
var p = new Point(x, y);
|
|
this.arrange(node, p);
|
|
x += node.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
},
|
|
layoutDown: function (down) {
|
|
var node, i;
|
|
this.setChildrenDirection(this.center, 'Down', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
var w = 0, y;
|
|
for (i = 0; i < down.length; i++) {
|
|
node = down[i];
|
|
node.treeDirection = 'Down';
|
|
var s = this.measure(node, Size.Empty);
|
|
w += s.width + this.options.horizontalSeparation;
|
|
}
|
|
w -= this.options.horizontalSeparation;
|
|
var x = this.center.x + this.center.width / 2 - w / 2;
|
|
y = this.center.y + this.options.verticalSeparation + this.center.height;
|
|
for (i = 0; i < down.length; i++) {
|
|
node = down[i];
|
|
var p = new Point(x, y);
|
|
this.arrange(node, p);
|
|
x += node.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
},
|
|
layoutRadialTree: function () {
|
|
this.setChildrenDirection(this.center, 'Radial', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
this.previousRoot = null;
|
|
var startAngle = this.options.startRadialAngle * DEG_TO_RAD;
|
|
var endAngle = this.options.endRadialAngle * DEG_TO_RAD;
|
|
if (endAngle <= startAngle) {
|
|
throw 'Final angle should not be less than the start angle.';
|
|
}
|
|
this.maxDepth = 0;
|
|
this.origin = new Point(this.center.x, this.center.y);
|
|
this.calculateAngularWidth(this.center, 0);
|
|
if (this.maxDepth > 0) {
|
|
this.radialLayout(this.center, this.options.radialFirstLevelSeparation, startAngle, endAngle);
|
|
}
|
|
this.center.Angle = endAngle - startAngle;
|
|
},
|
|
tipOverTree: function (down, startFromLevel) {
|
|
if (Utils.isUndefined(startFromLevel)) {
|
|
startFromLevel = 0;
|
|
}
|
|
this.setChildrenDirection(this.center, 'Down', false);
|
|
this.setChildrenLayout(this.center, 'Default', false);
|
|
this.setChildrenLayout(this.center, 'Underneath', false, startFromLevel);
|
|
var w = 0, y, node, i;
|
|
for (i = 0; i < down.length; i++) {
|
|
node = down[i];
|
|
node.TreeDirection = 'Down';
|
|
var s = this.measure(node, Size.Empty);
|
|
w += s.width + this.options.horizontalSeparation;
|
|
}
|
|
w -= this.options.horizontalSeparation;
|
|
w -= down[down.length - 1].width;
|
|
w += down[down.length - 1].associatedShape.bounds().width;
|
|
var x = this.center.x + this.center.width / 2 - w / 2;
|
|
y = this.center.y + this.options.verticalSeparation + this.center.height;
|
|
for (i = 0; i < down.length; i++) {
|
|
node = down[i];
|
|
var p = new Point(x, y);
|
|
this.arrange(node, p);
|
|
x += node.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
},
|
|
calculateAngularWidth: function (n, d) {
|
|
if (d > this.maxDepth) {
|
|
this.maxDepth = d;
|
|
}
|
|
var aw = 0, w = 1000, h = 1000, diameter = d === 0 ? 0 : Math.sqrt(w * w + h * h) / d;
|
|
if (n.children.length > 0) {
|
|
for (var i = 0, len = n.children.length; i < len; i++) {
|
|
var child = n.children[i];
|
|
aw += this.calculateAngularWidth(child, d + 1);
|
|
}
|
|
aw = Math.max(diameter, aw);
|
|
} else {
|
|
aw = diameter;
|
|
}
|
|
n.sectorAngle = aw;
|
|
return aw;
|
|
},
|
|
sortChildren: function (n) {
|
|
var basevalue = 0, i;
|
|
if (n.parents.length > 1) {
|
|
throw 'Node is not part of a tree.';
|
|
}
|
|
var p = n.parents[0];
|
|
if (p) {
|
|
var pl = new Point(p.x, p.y);
|
|
var nl = new Point(n.x, n.y);
|
|
basevalue = this.normalizeAngle(Math.atan2(pl.y - nl.y, pl.x - nl.x));
|
|
}
|
|
var count = n.children.length;
|
|
if (count === 0) {
|
|
return null;
|
|
}
|
|
var angle = [];
|
|
var idx = [];
|
|
for (i = 0; i < count; ++i) {
|
|
var c = n.children[i];
|
|
var l = new Point(c.x, c.y);
|
|
idx[i] = i;
|
|
angle[i] = this.normalizeAngle(-basevalue + Math.atan2(l.y - l.y, l.x - l.x));
|
|
}
|
|
Utils.bisort(angle, idx);
|
|
var col = [];
|
|
var children = n.children;
|
|
for (i = 0; i < count; ++i) {
|
|
col.push(children[idx[i]]);
|
|
}
|
|
return col;
|
|
},
|
|
normalizeAngle: function (angle) {
|
|
while (angle > Math.PI * 2) {
|
|
angle -= 2 * Math.PI;
|
|
}
|
|
while (angle < 0) {
|
|
angle += Math.PI * 2;
|
|
}
|
|
return angle;
|
|
},
|
|
radialLayout: function (node, radius, startAngle, endAngle) {
|
|
var deltaTheta = endAngle - startAngle;
|
|
var deltaThetaHalf = deltaTheta / 2;
|
|
var parentSector = node.sectorAngle;
|
|
var fraction = 0;
|
|
var sorted = this.sortChildren(node);
|
|
for (var i = 0, len = sorted.length; i < len; i++) {
|
|
var childNode = sorted[i];
|
|
var cp = childNode;
|
|
var childAngleFraction = cp.sectorAngle / parentSector;
|
|
if (childNode.children.length > 0) {
|
|
this.radialLayout(childNode, radius + this.options.radialSeparation, startAngle + fraction * deltaTheta, startAngle + (fraction + childAngleFraction) * deltaTheta);
|
|
}
|
|
this.setPolarLocation(childNode, radius, startAngle + fraction * deltaTheta + childAngleFraction * deltaThetaHalf);
|
|
cp.angle = childAngleFraction * deltaTheta;
|
|
fraction += childAngleFraction;
|
|
}
|
|
},
|
|
setPolarLocation: function (node, radius, angle) {
|
|
node.x = this.origin.x + radius * Math.cos(angle);
|
|
node.y = this.origin.y + radius * Math.sin(angle);
|
|
node.BoundingRectangle = new Rect(node.x, node.y, node.width, node.height);
|
|
},
|
|
setChildrenDirection: function (node, direction, includeStart) {
|
|
var rootDirection = node.treeDirection;
|
|
this.graph.depthFirstTraversal(node, function (n) {
|
|
n.treeDirection = direction;
|
|
});
|
|
if (!includeStart) {
|
|
node.treeDirection = rootDirection;
|
|
}
|
|
},
|
|
setChildrenLayout: function (node, layout, includeStart, startFromLevel) {
|
|
if (Utils.isUndefined(startFromLevel)) {
|
|
startFromLevel = 0;
|
|
}
|
|
var rootLayout = node.childrenLayout;
|
|
if (startFromLevel > 0) {
|
|
this.graph.assignLevels(node);
|
|
this.graph.depthFirstTraversal(node, function (s) {
|
|
if (s.level >= startFromLevel + 1) {
|
|
s.childrenLayout = layout;
|
|
}
|
|
});
|
|
} else {
|
|
this.graph.depthFirstTraversal(node, function (s) {
|
|
s.childrenLayout = layout;
|
|
});
|
|
if (!includeStart) {
|
|
node.childrenLayout = rootLayout;
|
|
}
|
|
}
|
|
},
|
|
measure: function (node, givenSize) {
|
|
var w = 0, h = 0, s;
|
|
var result = new Size(0, 0);
|
|
if (!node) {
|
|
throw '';
|
|
}
|
|
var b = node.associatedShape.bounds();
|
|
var shapeWidth = b.width;
|
|
var shapeHeight = b.height;
|
|
if (node.parents.length !== 1) {
|
|
throw 'Node not in a spanning tree.';
|
|
}
|
|
var parent = node.parents[0];
|
|
if (node.treeDirection === 'Undefined') {
|
|
node.treeDirection = parent.treeDirection;
|
|
}
|
|
if (Utils.isEmpty(node.children)) {
|
|
result = new Size(Math.abs(shapeWidth) < EPSILON ? 50 : shapeWidth, Math.abs(shapeHeight) < EPSILON ? 25 : shapeHeight);
|
|
} else if (node.children.length === 1) {
|
|
switch (node.treeDirection) {
|
|
case 'Radial':
|
|
s = this.measure(node.children[0], givenSize);
|
|
w = shapeWidth + this.options.radialSeparation * Math.cos(node.AngleToParent) + s.width;
|
|
h = shapeHeight + Math.abs(this.options.radialSeparation * Math.sin(node.AngleToParent)) + s.height;
|
|
break;
|
|
case 'Left':
|
|
case 'Right':
|
|
switch (node.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
break;
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
s = this.measure(node.children[0], givenSize);
|
|
w = shapeWidth + s.width + this.options.underneathHorizontalOffset;
|
|
h = shapeHeight + this.options.underneathVerticalTopOffset + s.height;
|
|
break;
|
|
case 'Default':
|
|
s = this.measure(node.children[0], givenSize);
|
|
w = shapeWidth + this.options.horizontalSeparation + s.width;
|
|
h = Math.max(shapeHeight, s.height);
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the Radial layout measuring.';
|
|
}
|
|
break;
|
|
case 'Up':
|
|
case 'Down':
|
|
switch (node.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
s = this.measure(node.children[0], givenSize);
|
|
w = Math.max(shapeWidth, s.width + this.options.underneathHorizontalOffset);
|
|
h = shapeHeight + this.options.underneathVerticalTopOffset + s.height;
|
|
break;
|
|
case 'Default':
|
|
s = this.measure(node.children[0], givenSize);
|
|
h = shapeHeight + this.options.verticalSeparation + s.height;
|
|
w = Math.max(shapeWidth, s.width);
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the Down layout measuring.';
|
|
}
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the layout measuring.';
|
|
}
|
|
result = new Size(w, h);
|
|
} else {
|
|
var i, childNode;
|
|
switch (node.treeDirection) {
|
|
case 'Left':
|
|
case 'Right':
|
|
switch (node.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
w = shapeWidth;
|
|
h = shapeHeight + this.options.underneathVerticalTopOffset;
|
|
for (i = 0; i < node.children.length; i++) {
|
|
childNode = node.children[i];
|
|
s = this.measure(childNode, givenSize);
|
|
w = Math.max(w, s.width + this.options.underneathHorizontalOffset);
|
|
h += s.height + this.options.underneathVerticalSeparation;
|
|
}
|
|
h -= this.options.underneathVerticalSeparation;
|
|
break;
|
|
case 'Default':
|
|
w = shapeWidth;
|
|
h = 0;
|
|
for (i = 0; i < node.children.length; i++) {
|
|
childNode = node.children[i];
|
|
s = this.measure(childNode, givenSize);
|
|
w = Math.max(w, shapeWidth + this.options.horizontalSeparation + s.width);
|
|
h += s.height + this.options.verticalSeparation;
|
|
}
|
|
h -= this.options.verticalSeparation;
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the Right layout measuring.';
|
|
}
|
|
break;
|
|
case 'Up':
|
|
case 'Down':
|
|
switch (node.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
w = shapeWidth;
|
|
h = shapeHeight + this.options.underneathVerticalTopOffset;
|
|
for (i = 0; i < node.children.length; i++) {
|
|
childNode = node.children[i];
|
|
s = this.measure(childNode, givenSize);
|
|
w = Math.max(w, s.width + this.options.underneathHorizontalOffset);
|
|
h += s.height + this.options.underneathVerticalSeparation;
|
|
}
|
|
h -= this.options.underneathVerticalSeparation;
|
|
break;
|
|
case 'Default':
|
|
w = 0;
|
|
h = 0;
|
|
for (i = 0; i < node.children.length; i++) {
|
|
childNode = node.children[i];
|
|
s = this.measure(childNode, givenSize);
|
|
w += s.width + this.options.horizontalSeparation;
|
|
h = Math.max(h, s.height + this.options.verticalSeparation + shapeHeight);
|
|
}
|
|
w -= this.options.horizontalSeparation;
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the Down layout measuring.';
|
|
}
|
|
break;
|
|
default:
|
|
throw 'Unhandled TreeDirection in the layout measuring.';
|
|
}
|
|
result = new Size(w, h);
|
|
}
|
|
node.SectorAngle = Math.sqrt(w * w / 4 + h * h / 4);
|
|
node.Size = result;
|
|
return result;
|
|
},
|
|
arrange: function (n, p) {
|
|
var i, pp, child, node, childrenwidth, b = n.associatedShape.bounds();
|
|
var shapeWidth = b.width;
|
|
var shapeHeight = b.height;
|
|
if (Utils.isEmpty(n.children)) {
|
|
n.x = p.x;
|
|
n.y = p.y;
|
|
n.BoundingRectangle = new Rect(p.x, p.y, shapeWidth, shapeHeight);
|
|
} else {
|
|
var x, y;
|
|
var selfLocation;
|
|
switch (n.treeDirection) {
|
|
case 'Left':
|
|
switch (n.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
selfLocation = p;
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
|
|
for (i = 0; i < node.children.length; i++) {
|
|
node = node.children[i];
|
|
x = selfLocation.x - node.associatedShape.width - this.options.underneathHorizontalOffset;
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
y += node.Size.height + this.options.underneathVerticalSeparation;
|
|
}
|
|
break;
|
|
case 'Default':
|
|
selfLocation = new Point(p.x + n.Size.width - shapeWidth, p.y + (n.Size.height - shapeHeight) / 2);
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
x = selfLocation.x - this.options.horizontalSeparation;
|
|
y = p.y;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
pp = new Point(x - node.Size.width, y);
|
|
this.arrange(node, pp);
|
|
y += node.Size.height + this.options.verticalSeparation;
|
|
}
|
|
break;
|
|
default:
|
|
throw 'Unsupported TreeDirection';
|
|
}
|
|
break;
|
|
case 'Right':
|
|
switch (n.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
selfLocation = p;
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
x = p.x + shapeWidth + this.options.underneathHorizontalOffset;
|
|
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
y += node.Size.height + this.options.underneathVerticalSeparation;
|
|
}
|
|
break;
|
|
case 'Default':
|
|
selfLocation = new Point(p.x, p.y + (n.Size.height - shapeHeight) / 2);
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
x = p.x + shapeWidth + this.options.horizontalSeparation;
|
|
y = p.y;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
y += node.Size.height + this.options.verticalSeparation;
|
|
}
|
|
break;
|
|
default:
|
|
throw 'Unsupported TreeDirection';
|
|
}
|
|
break;
|
|
case 'Up':
|
|
selfLocation = new Point(p.x + (n.Size.width - shapeWidth) / 2, p.y + n.Size.height - shapeHeight);
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
if (Math.abs(selfLocation.x - p.x) < EPSILON) {
|
|
childrenwidth = 0;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
child = n.children[i];
|
|
childrenwidth += child.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
childrenwidth -= this.options.horizontalSeparation;
|
|
x = p.x + (shapeWidth - childrenwidth) / 2;
|
|
} else {
|
|
x = p.x;
|
|
}
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
y = selfLocation.y - this.options.verticalSeparation - node.Size.height;
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
x += node.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
break;
|
|
case 'Down':
|
|
switch (n.childrenLayout) {
|
|
case 'TopAlignedWithParent':
|
|
case 'BottomAlignedWithParent':
|
|
break;
|
|
case 'Underneath':
|
|
selfLocation = p;
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
x = p.x + this.options.underneathHorizontalOffset;
|
|
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
y += node.Size.height + this.options.underneathVerticalSeparation;
|
|
}
|
|
break;
|
|
case 'Default':
|
|
selfLocation = new Point(p.x + (n.Size.width - shapeWidth) / 2, p.y);
|
|
n.x = selfLocation.x;
|
|
n.y = selfLocation.y;
|
|
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
|
|
if (Math.abs(selfLocation.x - p.x) < EPSILON) {
|
|
childrenwidth = 0;
|
|
for (i = 0; i < n.children.length; i++) {
|
|
child = n.children[i];
|
|
childrenwidth += child.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
childrenwidth -= this.options.horizontalSeparation;
|
|
x = p.x + (shapeWidth - childrenwidth) / 2;
|
|
} else {
|
|
x = p.x;
|
|
}
|
|
for (i = 0; i < n.children.length; i++) {
|
|
node = n.children[i];
|
|
y = selfLocation.y + this.options.verticalSeparation + shapeHeight;
|
|
pp = new Point(x, y);
|
|
this.arrange(node, pp);
|
|
x += node.Size.width + this.options.horizontalSeparation;
|
|
}
|
|
break;
|
|
default:
|
|
throw 'Unsupported TreeDirection';
|
|
}
|
|
break;
|
|
case 'None':
|
|
break;
|
|
default:
|
|
throw 'Unsupported TreeDirection';
|
|
}
|
|
}
|
|
},
|
|
layoutSwitch: function () {
|
|
if (!this.center) {
|
|
return;
|
|
}
|
|
if (Utils.isEmpty(this.center.children)) {
|
|
return;
|
|
}
|
|
var type = this.options.subtype;
|
|
if (Utils.isUndefined(type)) {
|
|
type = 'Down';
|
|
}
|
|
var single, male, female, leftcount;
|
|
var children = this.center.children;
|
|
switch (type.toLowerCase()) {
|
|
case 'radial':
|
|
case 'radialtree':
|
|
this.layoutRadialTree();
|
|
break;
|
|
case 'mindmaphorizontal':
|
|
case 'mindmap':
|
|
single = this.center.children;
|
|
if (this.center.children.length === 1) {
|
|
this.layoutRight(single);
|
|
} else {
|
|
leftcount = children.length / 2;
|
|
male = grep(this.center.children, function (n) {
|
|
return Utils.indexOf(children, n) < leftcount;
|
|
});
|
|
female = grep(this.center.children, function (n) {
|
|
return Utils.indexOf(children, n) >= leftcount;
|
|
});
|
|
this.layoutLeft(male);
|
|
this.layoutRight(female);
|
|
}
|
|
break;
|
|
case 'mindmapvertical':
|
|
single = this.center.children;
|
|
if (this.center.children.length === 1) {
|
|
this.layoutDown(single);
|
|
} else {
|
|
leftcount = children.length / 2;
|
|
male = grep(this.center.children, function (n) {
|
|
return Utils.indexOf(children, n) < leftcount;
|
|
});
|
|
female = grep(this.center.children, function (n) {
|
|
return Utils.indexOf(children, n) >= leftcount;
|
|
});
|
|
this.layoutUp(male);
|
|
this.layoutDown(female);
|
|
}
|
|
break;
|
|
case 'right':
|
|
this.layoutRight(this.center.children);
|
|
break;
|
|
case 'left':
|
|
this.layoutLeft(this.center.children);
|
|
break;
|
|
case 'up':
|
|
case 'bottom':
|
|
this.layoutUp(this.center.children);
|
|
break;
|
|
case 'down':
|
|
case 'top':
|
|
this.layoutDown(this.center.children);
|
|
break;
|
|
case 'tipover':
|
|
case 'tipovertree':
|
|
if (this.options.tipOverTreeStartLevel < 0) {
|
|
throw 'The tip-over level should be a positive integer.';
|
|
}
|
|
this.tipOverTree(this.center.children, this.options.tipOverTreeStartLevel);
|
|
break;
|
|
case 'undefined':
|
|
case 'none':
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
var TreeLayout = LayoutBase.extend({
|
|
init: function (diagram) {
|
|
var that = this;
|
|
LayoutBase.fn.init.call(that);
|
|
if (Utils.isUndefined(diagram)) {
|
|
throw 'No diagram specified.';
|
|
}
|
|
this.diagram = diagram;
|
|
},
|
|
layout: function (options) {
|
|
this.transferOptions(options);
|
|
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
|
|
this.graph = adapter.convert();
|
|
var finalNodeSet = this.layoutComponents();
|
|
return new diagram.LayoutState(this.diagram, finalNodeSet);
|
|
},
|
|
layoutComponents: function () {
|
|
if (this.graph.isEmpty()) {
|
|
return;
|
|
}
|
|
var components = this.graph.getConnectedComponents();
|
|
if (Utils.isEmpty(components)) {
|
|
return;
|
|
}
|
|
var layout = new TreeLayoutProcessor(this.options);
|
|
var trees = [];
|
|
for (var i = 0; i < components.length; i++) {
|
|
var component = components[i];
|
|
var treeGraph = this.getTree(component);
|
|
if (!treeGraph) {
|
|
throw 'Failed to find a spanning tree for the component.';
|
|
}
|
|
var root = treeGraph.root;
|
|
var tree = treeGraph.tree;
|
|
layout.layout(tree, root);
|
|
trees.push(tree);
|
|
}
|
|
return this.gridLayoutComponents(trees);
|
|
},
|
|
getTree: function (graph) {
|
|
var root = null;
|
|
if (this.options.roots && this.options.roots.length > 0) {
|
|
for (var i = 0, len = graph.nodes.length; i < len; i++) {
|
|
var node = graph.nodes[i];
|
|
for (var j = 0; j < this.options.roots.length; j++) {
|
|
var givenRootShape = this.options.roots[j];
|
|
if (givenRootShape === node.associatedShape) {
|
|
root = node;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!root) {
|
|
root = graph.root();
|
|
if (!root) {
|
|
throw 'Unable to find a root for the tree.';
|
|
}
|
|
}
|
|
return this.getTreeForRoot(graph, root);
|
|
},
|
|
getTreeForRoot: function (graph, root) {
|
|
var tree = graph.getSpanningTree(root);
|
|
if (Utils.isUndefined(tree) || tree.isEmpty()) {
|
|
return null;
|
|
}
|
|
return {
|
|
tree: tree,
|
|
root: tree.root
|
|
};
|
|
}
|
|
});
|
|
var LayeredLayout = LayoutBase.extend({
|
|
init: function (diagram) {
|
|
var that = this;
|
|
LayoutBase.fn.init.call(that);
|
|
if (Utils.isUndefined(diagram)) {
|
|
throw 'Diagram is not specified.';
|
|
}
|
|
this.diagram = diagram;
|
|
},
|
|
layout: function (options) {
|
|
this.transferOptions(options);
|
|
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
|
|
var graph = adapter.convert(options);
|
|
if (graph.isEmpty()) {
|
|
return;
|
|
}
|
|
var components = graph.getConnectedComponents();
|
|
if (Utils.isEmpty(components)) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < components.length; i++) {
|
|
var component = components[i];
|
|
this.layoutGraph(component, options);
|
|
}
|
|
var finalNodeSet = this.gridLayoutComponents(components);
|
|
return new diagram.LayoutState(this.diagram, finalNodeSet);
|
|
},
|
|
_initRuntimeProperties: function () {
|
|
for (var k = 0; k < this.graph.nodes.length; k++) {
|
|
var node = this.graph.nodes[k];
|
|
node.layer = -1;
|
|
node.downstreamLinkCount = 0;
|
|
node.upstreamLinkCount = 0;
|
|
node.isVirtual = false;
|
|
node.uBaryCenter = 0;
|
|
node.dBaryCenter = 0;
|
|
node.upstreamPriority = 0;
|
|
node.downstreamPriority = 0;
|
|
node.gridPosition = 0;
|
|
}
|
|
},
|
|
_prepare: function (graph) {
|
|
var current = [], i, l, link;
|
|
var layerMap = new Dictionary();
|
|
var layerCount = 0;
|
|
var targetLayer, next, target;
|
|
Utils.forEach(graph.nodes, function (node) {
|
|
if (node.incoming.length === 0) {
|
|
layerMap.set(node, 0);
|
|
current.push(node);
|
|
}
|
|
});
|
|
while (current.length > 0) {
|
|
next = current.shift();
|
|
for (i = 0; i < next.outgoing.length; i++) {
|
|
link = next.outgoing[i];
|
|
target = link.target;
|
|
if (layerMap.containsKey(target)) {
|
|
targetLayer = Math.max(layerMap.get(next) + 1, layerMap.get(target));
|
|
} else {
|
|
targetLayer = layerMap.get(next) + 1;
|
|
}
|
|
layerMap.set(target, targetLayer);
|
|
if (targetLayer > layerCount) {
|
|
layerCount = targetLayer;
|
|
}
|
|
if (!contains(current, target)) {
|
|
current.push(target);
|
|
}
|
|
}
|
|
}
|
|
var sortedNodes = layerMap.keys();
|
|
sortedNodes.sort(function (o1, o2) {
|
|
var o1layer = layerMap.get(o1);
|
|
var o2layer = layerMap.get(o2);
|
|
return Utils.sign(o2layer - o1layer);
|
|
});
|
|
for (var n = 0; n < sortedNodes.length; ++n) {
|
|
var node = sortedNodes[n];
|
|
var minLayer = Number.MAX_VALUE;
|
|
if (node.outgoing.length === 0) {
|
|
continue;
|
|
}
|
|
for (l = 0; l < node.outgoing.length; ++l) {
|
|
link = node.outgoing[l];
|
|
minLayer = Math.min(minLayer, layerMap.get(link.target));
|
|
}
|
|
if (minLayer > 1) {
|
|
layerMap.set(node, minLayer - 1);
|
|
}
|
|
}
|
|
this.layers = [];
|
|
var layer;
|
|
for (i = 0; i < layerCount + 1; i++) {
|
|
layer = [];
|
|
layer.linksTo = {};
|
|
this.layers.push(layer);
|
|
}
|
|
layerMap.forEach(function (node, layer) {
|
|
node.layer = layer;
|
|
this.layers[layer].push(node);
|
|
}, this);
|
|
for (l = 0; l < this.layers.length; l++) {
|
|
layer = this.layers[l];
|
|
for (i = 0; i < layer.length; i++) {
|
|
layer[i].gridPosition = i;
|
|
}
|
|
}
|
|
},
|
|
layoutGraph: function (graph, options) {
|
|
if (Utils.isUndefined(graph)) {
|
|
throw 'No graph given or graph analysis of the diagram failed.';
|
|
}
|
|
if (Utils.isDefined(options)) {
|
|
this.transferOptions(options);
|
|
}
|
|
this.graph = graph;
|
|
graph.setItemIndices();
|
|
var reversedEdges = graph.makeAcyclic();
|
|
this._initRuntimeProperties();
|
|
this._prepare(graph, options);
|
|
this._dummify();
|
|
this._optimizeCrossings();
|
|
this._swapPairs();
|
|
this.arrangeNodes();
|
|
this._moveThingsAround();
|
|
this._dedummify();
|
|
Utils.forEach(reversedEdges, function (e) {
|
|
if (e.points) {
|
|
e.points.reverse();
|
|
}
|
|
});
|
|
},
|
|
setMinDist: function (m, n, minDist) {
|
|
var l = m.layer;
|
|
var i = m.layerIndex;
|
|
this.minDistances[l][i] = minDist;
|
|
},
|
|
getMinDist: function (m, n) {
|
|
var dist = 0, i1 = m.layerIndex, i2 = n.layerIndex, l = m.layer, min = Math.min(i1, i2), max = Math.max(i1, i2);
|
|
for (var k = min; k < max; ++k) {
|
|
dist += this.minDistances[l][k];
|
|
}
|
|
return dist;
|
|
},
|
|
placeLeftToRight: function (leftClasses) {
|
|
var leftPos = new Dictionary(), n, node;
|
|
for (var c = 0; c < this.layers.length; ++c) {
|
|
var classNodes = leftClasses[c];
|
|
if (!classNodes) {
|
|
continue;
|
|
}
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
if (!leftPos.containsKey(node)) {
|
|
this.placeLeft(node, leftPos, c);
|
|
}
|
|
}
|
|
var d = Number.POSITIVE_INFINITY;
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
var rightSibling = this.rightSibling(node);
|
|
if (rightSibling && this.nodeLeftClass.get(rightSibling) !== c) {
|
|
d = Math.min(d, leftPos.get(rightSibling) - leftPos.get(node) - this.getMinDist(node, rightSibling));
|
|
}
|
|
}
|
|
if (d === Number.POSITIVE_INFINITY) {
|
|
var D = [];
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
var neighbors = [];
|
|
Utils.addRange(neighbors, this.upNodes.get(node));
|
|
Utils.addRange(neighbors, this.downNodes.get(node));
|
|
for (var e = 0; e < neighbors.length; e++) {
|
|
var neighbor = neighbors[e];
|
|
if (this.nodeLeftClass.get(neighbor) < c) {
|
|
D.push(leftPos.get(neighbor) - leftPos.get(node));
|
|
}
|
|
}
|
|
}
|
|
D.sort();
|
|
if (D.length === 0) {
|
|
d = 0;
|
|
} else if (D.length % 2 === 1) {
|
|
d = D[this.intDiv(D.length, 2)];
|
|
} else {
|
|
d = (D[this.intDiv(D.length, 2) - 1] + D[this.intDiv(D.length, 2)]) / 2;
|
|
}
|
|
}
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
leftPos.set(node, leftPos.get(node) + d);
|
|
}
|
|
}
|
|
return leftPos;
|
|
},
|
|
placeRightToLeft: function (rightClasses) {
|
|
var rightPos = new Dictionary(), n, node;
|
|
for (var c = 0; c < this.layers.length; ++c) {
|
|
var classNodes = rightClasses[c];
|
|
if (!classNodes) {
|
|
continue;
|
|
}
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
if (!rightPos.containsKey(node)) {
|
|
this.placeRight(node, rightPos, c);
|
|
}
|
|
}
|
|
var d = Number.NEGATIVE_INFINITY;
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
var leftSibling = this.leftSibling(node);
|
|
if (leftSibling && this.nodeRightClass.get(leftSibling) !== c) {
|
|
d = Math.max(d, rightPos.get(leftSibling) - rightPos.get(node) + this.getMinDist(leftSibling, node));
|
|
}
|
|
}
|
|
if (d === Number.NEGATIVE_INFINITY) {
|
|
var D = [];
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
var neighbors = [];
|
|
Utils.addRange(neighbors, this.upNodes.get(node));
|
|
Utils.addRange(neighbors, this.downNodes.get(node));
|
|
for (var e = 0; e < neighbors.length; e++) {
|
|
var neighbor = neighbors[e];
|
|
if (this.nodeRightClass.get(neighbor) < c) {
|
|
D.push(rightPos.get(node) - rightPos.get(neighbor));
|
|
}
|
|
}
|
|
}
|
|
D.sort();
|
|
if (D.length === 0) {
|
|
d = 0;
|
|
} else if (D.length % 2 === 1) {
|
|
d = D[this.intDiv(D.length, 2)];
|
|
} else {
|
|
d = (D[this.intDiv(D.length, 2) - 1] + D[this.intDiv(D.length, 2)]) / 2;
|
|
}
|
|
}
|
|
for (n = 0; n < classNodes.length; n++) {
|
|
node = classNodes[n];
|
|
rightPos.set(node, rightPos.get(node) + d);
|
|
}
|
|
}
|
|
return rightPos;
|
|
},
|
|
_getLeftWing: function () {
|
|
var leftWing = { value: null };
|
|
var result = this.computeClasses(leftWing, 1);
|
|
this.nodeLeftClass = leftWing.value;
|
|
return result;
|
|
},
|
|
_getRightWing: function () {
|
|
var rightWing = { value: null };
|
|
var result = this.computeClasses(rightWing, -1);
|
|
this.nodeRightClass = rightWing.value;
|
|
return result;
|
|
},
|
|
computeClasses: function (wingPair, d) {
|
|
var currentWing = 0, wing = wingPair.value = new Dictionary();
|
|
for (var l = 0; l < this.layers.length; ++l) {
|
|
currentWing = l;
|
|
var layer = this.layers[l];
|
|
for (var n = d === 1 ? 0 : layer.length - 1; 0 <= n && n < layer.length; n += d) {
|
|
var node = layer[n];
|
|
if (!wing.containsKey(node)) {
|
|
wing.set(node, currentWing);
|
|
if (node.isVirtual) {
|
|
var ndsinl = this._nodesInLink(node);
|
|
for (var kk = 0; kk < ndsinl.length; kk++) {
|
|
var vnode = ndsinl[kk];
|
|
wing.set(vnode, currentWing);
|
|
}
|
|
}
|
|
} else {
|
|
currentWing = wing.get(node);
|
|
}
|
|
}
|
|
}
|
|
var wings = [];
|
|
for (var i = 0; i < this.layers.length; i++) {
|
|
wings.push(null);
|
|
}
|
|
wing.forEach(function (node, classIndex) {
|
|
if (wings[classIndex] === null) {
|
|
wings[classIndex] = [];
|
|
}
|
|
wings[classIndex].push(node);
|
|
});
|
|
return wings;
|
|
},
|
|
_isVerticalLayout: function () {
|
|
return this.options.subtype.toLowerCase() === 'up' || this.options.subtype.toLowerCase() === 'down' || this.options.subtype.toLowerCase() === 'vertical';
|
|
},
|
|
_isHorizontalLayout: function () {
|
|
return this.options.subtype.toLowerCase() === 'right' || this.options.subtype.toLowerCase() === 'left' || this.options.subtype.toLowerCase() === 'horizontal';
|
|
},
|
|
_isIncreasingLayout: function () {
|
|
return this.options.subtype.toLowerCase() === 'right' || this.options.subtype.toLowerCase() === 'down';
|
|
},
|
|
_moveThingsAround: function () {
|
|
var i, l, node, layer, n, w;
|
|
for (l = 0; l < this.layers.length; ++l) {
|
|
layer = this.layers[l];
|
|
layer.sort(this._gridPositionComparer);
|
|
}
|
|
this.minDistances = [];
|
|
for (l = 0; l < this.layers.length; ++l) {
|
|
layer = this.layers[l];
|
|
this.minDistances[l] = [];
|
|
for (n = 0; n < layer.length; ++n) {
|
|
node = layer[n];
|
|
node.layerIndex = n;
|
|
this.minDistances[l][n] = this.options.nodeDistance;
|
|
if (n < layer.length - 1) {
|
|
if (this._isVerticalLayout()) {
|
|
this.minDistances[l][n] += (node.width + layer[n + 1].width) / 2;
|
|
} else {
|
|
this.minDistances[l][n] += (node.height + layer[n + 1].height) / 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.downNodes = new Dictionary();
|
|
this.upNodes = new Dictionary();
|
|
Utils.forEach(this.graph.nodes, function (node) {
|
|
this.downNodes.set(node, []);
|
|
this.upNodes.set(node, []);
|
|
}, this);
|
|
Utils.forEach(this.graph.links, function (link) {
|
|
var origin = link.source;
|
|
var dest = link.target;
|
|
var down = null, up = null;
|
|
if (origin.layer > dest.layer) {
|
|
down = link.source;
|
|
up = link.target;
|
|
} else {
|
|
up = link.source;
|
|
down = link.target;
|
|
}
|
|
this.downNodes.get(up).push(down);
|
|
this.upNodes.get(down).push(up);
|
|
}, this);
|
|
this.downNodes.forEachValue(function (list) {
|
|
list.sort(this._gridPositionComparer);
|
|
}, this);
|
|
this.upNodes.forEachValue(function (list) {
|
|
list.sort(this._gridPositionComparer);
|
|
}, this);
|
|
for (l = 0; l < this.layers.length - 1; ++l) {
|
|
layer = this.layers[l];
|
|
for (w = 0; w < layer.length - 1; w++) {
|
|
var currentNode = layer[w];
|
|
if (!currentNode.isVirtual) {
|
|
continue;
|
|
}
|
|
var currDown = this.downNodes.get(currentNode)[0];
|
|
if (!currDown.isVirtual) {
|
|
continue;
|
|
}
|
|
for (n = w + 1; n < layer.length; ++n) {
|
|
node = layer[n];
|
|
if (!node.isVirtual) {
|
|
continue;
|
|
}
|
|
var downNode = this.downNodes.get(node)[0];
|
|
if (!downNode.isVirtual) {
|
|
continue;
|
|
}
|
|
if (currDown.gridPosition > downNode.gridPosition) {
|
|
var pos = currDown.gridPosition;
|
|
currDown.gridPosition = downNode.gridPosition;
|
|
downNode.gridPosition = pos;
|
|
var i1 = currDown.layerIndex;
|
|
var i2 = downNode.layerIndex;
|
|
this.layers[l + 1][i1] = downNode;
|
|
this.layers[l + 1][i2] = currDown;
|
|
currDown.layerIndex = i2;
|
|
downNode.layerIndex = i1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var leftClasses = this._getLeftWing();
|
|
var rightClasses = this._getRightWing();
|
|
var leftPos = this.placeLeftToRight(leftClasses);
|
|
var rightPos = this.placeRightToLeft(rightClasses);
|
|
var x = new Dictionary();
|
|
Utils.forEach(this.graph.nodes, function (node) {
|
|
x.set(node, (leftPos.get(node) + rightPos.get(node)) / 2);
|
|
});
|
|
var order = new Dictionary();
|
|
var placed = new Dictionary();
|
|
for (l = 0; l < this.layers.length; ++l) {
|
|
layer = this.layers[l];
|
|
var sequenceStart = -1, sequenceEnd = -1;
|
|
for (n = 0; n < layer.length; ++n) {
|
|
node = layer[n];
|
|
order.set(node, 0);
|
|
placed.set(node, false);
|
|
if (node.isVirtual) {
|
|
if (sequenceStart === -1) {
|
|
sequenceStart = n;
|
|
} else if (sequenceStart === n - 1) {
|
|
sequenceStart = n;
|
|
} else {
|
|
sequenceEnd = n;
|
|
order.set(layer[sequenceStart], 0);
|
|
if (x.get(node) - x.get(layer[sequenceStart]) === this.getMinDist(layer[sequenceStart], node)) {
|
|
placed.set(layer[sequenceStart], true);
|
|
} else {
|
|
placed.set(layer[sequenceStart], false);
|
|
}
|
|
sequenceStart = n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var directions = [
|
|
1,
|
|
-1
|
|
];
|
|
Utils.forEach(directions, function (d) {
|
|
var start = d === 1 ? 0 : this.layers.length - 1;
|
|
for (var l = start; 0 <= l && l < this.layers.length; l += d) {
|
|
var layer = this.layers[l];
|
|
var virtualStartIndex = this._firstVirtualNode(layer);
|
|
var virtualStart = null;
|
|
var sequence = null;
|
|
if (virtualStartIndex !== -1) {
|
|
virtualStart = layer[virtualStartIndex];
|
|
sequence = [];
|
|
for (i = 0; i < virtualStartIndex; i++) {
|
|
sequence.push(layer[i]);
|
|
}
|
|
} else {
|
|
virtualStart = null;
|
|
sequence = layer;
|
|
}
|
|
if (sequence.length > 0) {
|
|
this._sequencer(x, null, virtualStart, d, sequence);
|
|
for (i = 0; i < sequence.length - 1; ++i) {
|
|
this.setMinDist(sequence[i], sequence[i + 1], x.get(sequence[i + 1]) - x.get(sequence[i]));
|
|
}
|
|
if (virtualStart) {
|
|
this.setMinDist(sequence[sequence.length - 1], virtualStart, x.get(virtualStart) - x.get(sequence[sequence.length - 1]));
|
|
}
|
|
}
|
|
while (virtualStart) {
|
|
var virtualEnd = this.nextVirtualNode(layer, virtualStart);
|
|
if (!virtualEnd) {
|
|
virtualStartIndex = virtualStart.layerIndex;
|
|
sequence = [];
|
|
for (i = virtualStartIndex + 1; i < layer.length; i++) {
|
|
sequence.push(layer[i]);
|
|
}
|
|
if (sequence.length > 0) {
|
|
this._sequencer(x, virtualStart, null, d, sequence);
|
|
for (i = 0; i < sequence.length - 1; ++i) {
|
|
this.setMinDist(sequence[i], sequence[i + 1], x.get(sequence[i + 1]) - x.get(sequence[i]));
|
|
}
|
|
this.setMinDist(virtualStart, sequence[0], x.get(sequence[0]) - x.get(virtualStart));
|
|
}
|
|
} else if (order.get(virtualStart) === d) {
|
|
virtualStartIndex = virtualStart.layerIndex;
|
|
var virtualEndIndex = virtualEnd.layerIndex;
|
|
sequence = [];
|
|
for (i = virtualStartIndex + 1; i < virtualEndIndex; i++) {
|
|
sequence.push(layer[i]);
|
|
}
|
|
if (sequence.length > 0) {
|
|
this._sequencer(x, virtualStart, virtualEnd, d, sequence);
|
|
}
|
|
placed.set(virtualStart, true);
|
|
}
|
|
virtualStart = virtualEnd;
|
|
}
|
|
this.adjustDirections(l, d, order, placed);
|
|
}
|
|
}, this);
|
|
var fromLayerIndex = this._isIncreasingLayout() ? 0 : this.layers.length - 1;
|
|
var reachedFinalLayerIndex = function (k, ctx) {
|
|
if (ctx._isIncreasingLayout()) {
|
|
return k < ctx.layers.length;
|
|
} else {
|
|
return k >= 0;
|
|
}
|
|
};
|
|
var layerIncrement = this._isIncreasingLayout() ? +1 : -1, offset = 0;
|
|
function maximumHeight(layer, ctx) {
|
|
var height = Number.MIN_VALUE;
|
|
for (var n = 0; n < layer.length; ++n) {
|
|
var node = layer[n];
|
|
if (ctx._isVerticalLayout()) {
|
|
height = Math.max(height, node.height);
|
|
} else {
|
|
height = Math.max(height, node.width);
|
|
}
|
|
}
|
|
return height;
|
|
}
|
|
for (i = fromLayerIndex; reachedFinalLayerIndex(i, this); i += layerIncrement) {
|
|
layer = this.layers[i];
|
|
var height = maximumHeight(layer, this);
|
|
for (n = 0; n < layer.length; ++n) {
|
|
node = layer[n];
|
|
if (this._isVerticalLayout()) {
|
|
node.x = x.get(node);
|
|
node.y = offset + height / 2;
|
|
} else {
|
|
node.x = offset + height / 2;
|
|
node.y = x.get(node);
|
|
}
|
|
}
|
|
offset += this.options.layerSeparation + height;
|
|
}
|
|
},
|
|
adjustDirections: function (l, d, order, placed) {
|
|
if (l + d < 0 || l + d >= this.layers.length) {
|
|
return;
|
|
}
|
|
var prevBridge = null, prevBridgeTarget = null;
|
|
var layer = this.layers[l + d];
|
|
for (var n = 0; n < layer.length; ++n) {
|
|
var nextBridge = layer[n];
|
|
if (nextBridge.isVirtual) {
|
|
var nextBridgeTarget = this.getNeighborOnLayer(nextBridge, l);
|
|
if (nextBridgeTarget.isVirtual) {
|
|
if (prevBridge) {
|
|
var p = placed.get(prevBridgeTarget);
|
|
var clayer = this.layers[l];
|
|
var i1 = prevBridgeTarget.layerIndex;
|
|
var i2 = nextBridgeTarget.layerIndex;
|
|
for (var i = i1 + 1; i < i2; ++i) {
|
|
if (clayer[i].isVirtual) {
|
|
p = p && placed.get(clayer[i]);
|
|
}
|
|
}
|
|
if (p) {
|
|
order.set(prevBridge, d);
|
|
var j1 = prevBridge.layerIndex;
|
|
var j2 = nextBridge.layerIndex;
|
|
for (var j = j1 + 1; j < j2; ++j) {
|
|
if (layer[j].isVirtual) {
|
|
order.set(layer[j], d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
prevBridge = nextBridge;
|
|
prevBridgeTarget = nextBridgeTarget;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getNeighborOnLayer: function (node, l) {
|
|
var neighbor = this.upNodes.get(node)[0];
|
|
if (neighbor.layer === l) {
|
|
return neighbor;
|
|
}
|
|
neighbor = this.downNodes.get(node)[0];
|
|
if (neighbor.layer === l) {
|
|
return neighbor;
|
|
}
|
|
return null;
|
|
},
|
|
_sequencer: function (x, virtualStart, virtualEnd, dir, sequence) {
|
|
if (sequence.length === 1) {
|
|
this._sequenceSingle(x, virtualStart, virtualEnd, dir, sequence[0]);
|
|
}
|
|
if (sequence.length > 1) {
|
|
var r = sequence.length, t = this.intDiv(r, 2);
|
|
this._sequencer(x, virtualStart, virtualEnd, dir, sequence.slice(0, t));
|
|
this._sequencer(x, virtualStart, virtualEnd, dir, sequence.slice(t));
|
|
this.combineSequences(x, virtualStart, virtualEnd, dir, sequence);
|
|
}
|
|
},
|
|
_sequenceSingle: function (x, virtualStart, virtualEnd, dir, node) {
|
|
var neighbors = dir === -1 ? this.downNodes.get(node) : this.upNodes.get(node);
|
|
var n = neighbors.length;
|
|
if (n !== 0) {
|
|
if (n % 2 === 1) {
|
|
x.set(node, x.get(neighbors[this.intDiv(n, 2)]));
|
|
} else {
|
|
x.set(node, (x.get(neighbors[this.intDiv(n, 2) - 1]) + x.get(neighbors[this.intDiv(n, 2)])) / 2);
|
|
}
|
|
if (virtualStart) {
|
|
x.set(node, Math.max(x.get(node), x.get(virtualStart) + this.getMinDist(virtualStart, node)));
|
|
}
|
|
if (virtualEnd) {
|
|
x.set(node, Math.min(x.get(node), x.get(virtualEnd) - this.getMinDist(node, virtualEnd)));
|
|
}
|
|
}
|
|
},
|
|
combineSequences: function (x, virtualStart, virtualEnd, dir, sequence) {
|
|
var r = sequence.length, t = this.intDiv(r, 2);
|
|
var leftHeap = [], i, c, n, neighbors, neighbor, pair;
|
|
for (i = 0; i < t; ++i) {
|
|
c = 0;
|
|
neighbors = dir === -1 ? this.downNodes.get(sequence[i]) : this.upNodes.get(sequence[i]);
|
|
for (n = 0; n < neighbors.length; ++n) {
|
|
neighbor = neighbors[n];
|
|
if (x.get(neighbor) >= x.get(sequence[i])) {
|
|
c++;
|
|
} else {
|
|
c--;
|
|
leftHeap.push({
|
|
k: x.get(neighbor) + this.getMinDist(sequence[i], sequence[t - 1]),
|
|
v: 2
|
|
});
|
|
}
|
|
}
|
|
leftHeap.push({
|
|
k: x.get(sequence[i]) + this.getMinDist(sequence[i], sequence[t - 1]),
|
|
v: c
|
|
});
|
|
}
|
|
if (virtualStart) {
|
|
leftHeap.push({
|
|
k: x.get(virtualStart) + this.getMinDist(virtualStart, sequence[t - 1]),
|
|
v: Number.MAX_VALUE
|
|
});
|
|
}
|
|
leftHeap.sort(this._positionDescendingComparer);
|
|
var rightHeap = [];
|
|
for (i = t; i < r; ++i) {
|
|
c = 0;
|
|
neighbors = dir === -1 ? this.downNodes.get(sequence[i]) : this.upNodes.get(sequence[i]);
|
|
for (n = 0; n < neighbors.length; ++n) {
|
|
neighbor = neighbors[n];
|
|
if (x.get(neighbor) <= x.get(sequence[i])) {
|
|
c++;
|
|
} else {
|
|
c--;
|
|
rightHeap.push({
|
|
k: x.get(neighbor) - this.getMinDist(sequence[i], sequence[t]),
|
|
v: 2
|
|
});
|
|
}
|
|
}
|
|
rightHeap.push({
|
|
k: x.get(sequence[i]) - this.getMinDist(sequence[i], sequence[t]),
|
|
v: c
|
|
});
|
|
}
|
|
if (virtualEnd) {
|
|
rightHeap.push({
|
|
k: x.get(virtualEnd) - this.getMinDist(virtualEnd, sequence[t]),
|
|
v: Number.MAX_VALUE
|
|
});
|
|
}
|
|
rightHeap.sort(this._positionAscendingComparer);
|
|
var leftRes = 0, rightRes = 0;
|
|
var m = this.getMinDist(sequence[t - 1], sequence[t]);
|
|
while (x.get(sequence[t]) - x.get(sequence[t - 1]) < m) {
|
|
if (leftRes < rightRes) {
|
|
if (leftHeap.length === 0) {
|
|
x.set(sequence[t - 1], x.get(sequence[t]) - m);
|
|
break;
|
|
} else {
|
|
pair = leftHeap.shift();
|
|
leftRes = leftRes + pair.v;
|
|
x.set(sequence[t - 1], pair.k);
|
|
x.set(sequence[t - 1], Math.max(x.get(sequence[t - 1]), x.get(sequence[t]) - m));
|
|
}
|
|
} else {
|
|
if (rightHeap.length === 0) {
|
|
x.set(sequence[t], x.get(sequence[t - 1]) + m);
|
|
break;
|
|
} else {
|
|
pair = rightHeap.shift();
|
|
rightRes = rightRes + pair.v;
|
|
x.set(sequence[t], pair.k);
|
|
x.set(sequence[t], Math.min(x.get(sequence[t]), x.get(sequence[t - 1]) + m));
|
|
}
|
|
}
|
|
}
|
|
for (i = t - 2; i >= 0; i--) {
|
|
x.set(sequence[i], Math.min(x.get(sequence[i]), x.get(sequence[t - 1]) - this.getMinDist(sequence[i], sequence[t - 1])));
|
|
}
|
|
for (i = t + 1; i < r; i++) {
|
|
x.set(sequence[i], Math.max(x.get(sequence[i]), x.get(sequence[t]) + this.getMinDist(sequence[i], sequence[t])));
|
|
}
|
|
},
|
|
placeLeft: function (node, leftPos, leftClass) {
|
|
var pos = Number.NEGATIVE_INFINITY;
|
|
Utils.forEach(this._getComposite(node), function (v) {
|
|
var leftSibling = this.leftSibling(v);
|
|
if (leftSibling && this.nodeLeftClass.get(leftSibling) === this.nodeLeftClass.get(v)) {
|
|
if (!leftPos.containsKey(leftSibling)) {
|
|
this.placeLeft(leftSibling, leftPos, leftClass);
|
|
}
|
|
pos = Math.max(pos, leftPos.get(leftSibling) + this.getMinDist(leftSibling, v));
|
|
}
|
|
}, this);
|
|
if (pos === Number.NEGATIVE_INFINITY) {
|
|
pos = 0;
|
|
}
|
|
Utils.forEach(this._getComposite(node), function (v) {
|
|
leftPos.set(v, pos);
|
|
});
|
|
},
|
|
placeRight: function (node, rightPos, rightClass) {
|
|
var pos = Number.POSITIVE_INFINITY;
|
|
Utils.forEach(this._getComposite(node), function (v) {
|
|
var rightSibling = this.rightSibling(v);
|
|
if (rightSibling && this.nodeRightClass.get(rightSibling) === this.nodeRightClass.get(v)) {
|
|
if (!rightPos.containsKey(rightSibling)) {
|
|
this.placeRight(rightSibling, rightPos, rightClass);
|
|
}
|
|
pos = Math.min(pos, rightPos.get(rightSibling) - this.getMinDist(v, rightSibling));
|
|
}
|
|
}, this);
|
|
if (pos === Number.POSITIVE_INFINITY) {
|
|
pos = 0;
|
|
}
|
|
Utils.forEach(this._getComposite(node), function (v) {
|
|
rightPos.set(v, pos);
|
|
});
|
|
},
|
|
leftSibling: function (node) {
|
|
var layer = this.layers[node.layer], layerIndex = node.layerIndex;
|
|
return layerIndex === 0 ? null : layer[layerIndex - 1];
|
|
},
|
|
rightSibling: function (node) {
|
|
var layer = this.layers[node.layer];
|
|
var layerIndex = node.layerIndex;
|
|
return layerIndex === layer.length - 1 ? null : layer[layerIndex + 1];
|
|
},
|
|
_getComposite: function (node) {
|
|
return node.isVirtual ? this._nodesInLink(node) : [node];
|
|
},
|
|
arrangeNodes: function () {
|
|
var i, l, ni, layer, node;
|
|
for (l = 0; l < this.layers.length; l++) {
|
|
layer = this.layers[l];
|
|
for (ni = 0; ni < layer.length; ni++) {
|
|
node = layer[ni];
|
|
node.upstreamPriority = node.upstreamLinkCount;
|
|
node.downstreamPriority = node.downstreamLinkCount;
|
|
}
|
|
}
|
|
var maxLayoutIterations = 2;
|
|
for (var it = 0; it < maxLayoutIterations; it++) {
|
|
for (i = this.layers.length - 1; i >= 1; i--) {
|
|
this.layoutLayer(false, i);
|
|
}
|
|
for (i = 0; i < this.layers.length - 1; i++) {
|
|
this.layoutLayer(true, i);
|
|
}
|
|
}
|
|
var gridPos = Number.MAX_VALUE;
|
|
for (l = 0; l < this.layers.length; l++) {
|
|
layer = this.layers[l];
|
|
for (ni = 0; ni < layer.length; ni++) {
|
|
node = layer[ni];
|
|
gridPos = Math.min(gridPos, node.gridPosition);
|
|
}
|
|
}
|
|
if (gridPos < 0) {
|
|
for (l = 0; l < this.layers.length; l++) {
|
|
layer = this.layers[l];
|
|
for (ni = 0; ni < layer.length; ni++) {
|
|
node = layer[ni];
|
|
node.gridPosition = node.gridPosition - gridPos;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
layoutLayer: function (down, layer) {
|
|
var iconsidered;
|
|
var considered;
|
|
if (down) {
|
|
considered = this.layers[iconsidered = layer + 1];
|
|
} else {
|
|
considered = this.layers[iconsidered = layer - 1];
|
|
}
|
|
var sorted = [];
|
|
for (var n = 0; n < considered.length; n++) {
|
|
sorted.push(considered[n]);
|
|
}
|
|
sorted.sort(function (n1, n2) {
|
|
var n1Priority = (n1.upstreamPriority + n1.downstreamPriority) / 2;
|
|
var n2Priority = (n2.upstreamPriority + n2.downstreamPriority) / 2;
|
|
if (Math.abs(n1Priority - n2Priority) < 0.0001) {
|
|
return 0;
|
|
}
|
|
if (n1Priority < n2Priority) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
});
|
|
Utils.forEach(sorted, function (node) {
|
|
var nodeGridPos = node.gridPosition;
|
|
var nodeBaryCenter = this.calcBaryCenter(node);
|
|
var nodePriority = (node.upstreamPriority + node.downstreamPriority) / 2;
|
|
if (Math.abs(nodeGridPos - nodeBaryCenter) < 0.0001) {
|
|
return;
|
|
}
|
|
if (Math.abs(nodeGridPos - nodeBaryCenter) < 0.25 + 0.0001) {
|
|
return;
|
|
}
|
|
if (nodeGridPos < nodeBaryCenter) {
|
|
while (nodeGridPos < nodeBaryCenter) {
|
|
if (!this.moveRight(node, considered, nodePriority)) {
|
|
break;
|
|
}
|
|
nodeGridPos = node.gridPosition;
|
|
}
|
|
} else {
|
|
while (nodeGridPos > nodeBaryCenter) {
|
|
if (!this.moveLeft(node, considered, nodePriority)) {
|
|
break;
|
|
}
|
|
nodeGridPos = node.gridPosition;
|
|
}
|
|
}
|
|
}, this);
|
|
if (iconsidered > 0) {
|
|
this.calcDownData(iconsidered - 1);
|
|
}
|
|
if (iconsidered < this.layers.length - 1) {
|
|
this.calcUpData(iconsidered + 1);
|
|
}
|
|
},
|
|
moveRight: function (node, layer, priority) {
|
|
var index = Utils.indexOf(layer, node);
|
|
if (index === layer.length - 1) {
|
|
node.gridPosition = node.gridPosition + 0.5;
|
|
return true;
|
|
}
|
|
var rightNode = layer[index + 1];
|
|
var rightNodePriority = (rightNode.upstreamPriority + rightNode.downstreamPriority) / 2;
|
|
if (rightNode.gridPosition > node.gridPosition + 1) {
|
|
node.gridPosition = node.gridPosition + 0.5;
|
|
return true;
|
|
}
|
|
if (rightNodePriority > priority || Math.abs(rightNodePriority - priority) < 0.0001) {
|
|
return false;
|
|
}
|
|
if (this.moveRight(rightNode, layer, priority)) {
|
|
node.gridPosition = node.gridPosition + 0.5;
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
moveLeft: function (node, layer, priority) {
|
|
var index = Utils.indexOf(layer, node);
|
|
if (index === 0) {
|
|
node.gridPosition = node.gridPosition - 0.5;
|
|
return true;
|
|
}
|
|
var leftNode = layer[index - 1];
|
|
var leftNodePriority = (leftNode.upstreamPriority + leftNode.downstreamPriority) / 2;
|
|
if (leftNode.gridPosition < node.gridPosition - 1) {
|
|
node.gridPosition = node.gridPosition - 0.5;
|
|
return true;
|
|
}
|
|
if (leftNodePriority > priority || Math.abs(leftNodePriority - priority) < 0.0001) {
|
|
return false;
|
|
}
|
|
if (this.moveLeft(leftNode, layer, priority)) {
|
|
node.gridPosition = node.gridPosition - 0.5;
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
mapVirtualNode: function (node, link) {
|
|
this.nodeToLinkMap.set(node, link);
|
|
if (!this.linkToNodeMap.containsKey(link)) {
|
|
this.linkToNodeMap.set(link, []);
|
|
}
|
|
this.linkToNodeMap.get(link).push(node);
|
|
},
|
|
_nodesInLink: function (node) {
|
|
return this.linkToNodeMap.get(this.nodeToLinkMap.get(node));
|
|
},
|
|
_dummify: function () {
|
|
this.linkToNodeMap = new Dictionary();
|
|
this.nodeToLinkMap = new Dictionary();
|
|
var layer, pos, newNode, node, r, newLink, i, l, links = this.graph.links.slice(0);
|
|
var layers = this.layers;
|
|
var addLinkBetweenLayers = function (upLayer, downLayer, link) {
|
|
layers[upLayer].linksTo[downLayer] = layers[upLayer].linksTo[downLayer] || [];
|
|
layers[upLayer].linksTo[downLayer].push(link);
|
|
};
|
|
for (l = 0; l < links.length; l++) {
|
|
var link = links[l];
|
|
var o = link.source;
|
|
var d = link.target;
|
|
var oLayer = o.layer;
|
|
var dLayer = d.layer;
|
|
var oPos = o.gridPosition;
|
|
var dPos = d.gridPosition;
|
|
var step = (dPos - oPos) / Math.abs(dLayer - oLayer);
|
|
var p = o;
|
|
if (oLayer - dLayer > 1) {
|
|
for (i = oLayer - 1; i > dLayer; i--) {
|
|
newNode = new Node();
|
|
newNode.x = o.x;
|
|
newNode.y = o.y;
|
|
newNode.width = o.width / 100;
|
|
newNode.height = o.height / 100;
|
|
layer = layers[i];
|
|
pos = (i - dLayer) * step + oPos;
|
|
if (pos > layer.length) {
|
|
pos = layer.length;
|
|
}
|
|
if (oPos >= layers[oLayer].length - 1 && dPos >= layers[dLayer].length - 1) {
|
|
pos = layer.length;
|
|
} else if (oPos === 0 && dPos === 0) {
|
|
pos = 0;
|
|
}
|
|
newNode.layer = i;
|
|
newNode.uBaryCenter = 0;
|
|
newNode.dBaryCenter = 0;
|
|
newNode.upstreamLinkCount = 0;
|
|
newNode.downstreamLinkCount = 0;
|
|
newNode.gridPosition = pos;
|
|
newNode.isVirtual = true;
|
|
Utils.insert(layer, newNode, pos);
|
|
for (r = pos + 1; r < layer.length; r++) {
|
|
node = layer[r];
|
|
node.gridPosition = node.gridPosition + 1;
|
|
}
|
|
newLink = new Link(p, newNode);
|
|
newLink.depthOfDumminess = 0;
|
|
addLinkBetweenLayers(i - 1, i, newLink);
|
|
p = newNode;
|
|
this.graph._addNode(newNode);
|
|
this.graph.addLink(newLink);
|
|
newNode.index = this.graph.nodes.length - 1;
|
|
this.mapVirtualNode(newNode, link);
|
|
}
|
|
addLinkBetweenLayers(dLayer - 1, dLayer, newLink);
|
|
link.changeSource(p);
|
|
link.depthOfDumminess = oLayer - dLayer - 1;
|
|
} else if (oLayer - dLayer < -1) {
|
|
for (i = oLayer + 1; i < dLayer; i++) {
|
|
newNode = new Node();
|
|
newNode.x = o.x;
|
|
newNode.y = o.y;
|
|
newNode.width = o.width / 100;
|
|
newNode.height = o.height / 100;
|
|
layer = layers[i];
|
|
pos = (i - oLayer) * step + oPos;
|
|
if (pos > layer.length) {
|
|
pos = layer.length;
|
|
}
|
|
if (oPos >= layers[oLayer].length - 1 && dPos >= layers[dLayer].length - 1) {
|
|
pos = layer.length;
|
|
} else if (oPos === 0 && dPos === 0) {
|
|
pos = 0;
|
|
}
|
|
newNode.layer = i;
|
|
newNode.uBaryCenter = 0;
|
|
newNode.dBaryCenter = 0;
|
|
newNode.upstreamLinkCount = 0;
|
|
newNode.downstreamLinkCount = 0;
|
|
newNode.gridPosition = pos;
|
|
newNode.isVirtual = true;
|
|
pos &= pos;
|
|
Utils.insert(layer, newNode, pos);
|
|
for (r = pos + 1; r < layer.length; r++) {
|
|
node = layer[r];
|
|
node.gridPosition = node.gridPosition + 1;
|
|
}
|
|
newLink = new Link(p, newNode);
|
|
newLink.depthOfDumminess = 0;
|
|
addLinkBetweenLayers(i - 1, i, newLink);
|
|
p = newNode;
|
|
this.graph._addNode(newNode);
|
|
this.graph.addLink(newLink);
|
|
newNode.index = this.graph.nodes.length - 1;
|
|
this.mapVirtualNode(newNode, link);
|
|
}
|
|
addLinkBetweenLayers(dLayer - 1, dLayer, link);
|
|
link.changeSource(p);
|
|
link.depthOfDumminess = dLayer - oLayer - 1;
|
|
} else {
|
|
addLinkBetweenLayers(oLayer, dLayer, link);
|
|
}
|
|
}
|
|
},
|
|
_dedummify: function () {
|
|
var dedum = true;
|
|
while (dedum) {
|
|
dedum = false;
|
|
for (var l = 0; l < this.graph.links.length; l++) {
|
|
var link = this.graph.links[l];
|
|
if (!link.depthOfDumminess) {
|
|
continue;
|
|
}
|
|
var points = [];
|
|
points.unshift({
|
|
x: link.target.x,
|
|
y: link.target.y
|
|
});
|
|
points.unshift({
|
|
x: link.source.x,
|
|
y: link.source.y
|
|
});
|
|
var temp = link;
|
|
var depthOfDumminess = link.depthOfDumminess;
|
|
for (var d = 0; d < depthOfDumminess; d++) {
|
|
var node = temp.source;
|
|
var prevLink = node.incoming[0];
|
|
points.unshift({
|
|
x: prevLink.source.x,
|
|
y: prevLink.source.y
|
|
});
|
|
temp = prevLink;
|
|
}
|
|
link.changeSource(temp.source);
|
|
link.depthOfDumminess = 0;
|
|
if (points.length > 2) {
|
|
points.splice(0, 1);
|
|
points.splice(points.length - 1);
|
|
link.points = points;
|
|
} else {
|
|
link.points = [];
|
|
}
|
|
dedum = true;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
_optimizeCrossings: function () {
|
|
var moves = -1, i;
|
|
var maxIterations = 3;
|
|
var iter = 0;
|
|
while (moves !== 0) {
|
|
if (iter++ > maxIterations) {
|
|
break;
|
|
}
|
|
moves = 0;
|
|
for (i = this.layers.length - 1; i >= 1; i--) {
|
|
moves += this.optimizeLayerCrossings(false, i);
|
|
}
|
|
for (i = 0; i < this.layers.length - 1; i++) {
|
|
moves += this.optimizeLayerCrossings(true, i);
|
|
}
|
|
}
|
|
},
|
|
calcUpData: function (layer) {
|
|
if (layer === 0) {
|
|
return;
|
|
}
|
|
var considered = this.layers[layer], i, l, link;
|
|
var upLayer = new Set();
|
|
var temp = this.layers[layer - 1];
|
|
for (i = 0; i < temp.length; i++) {
|
|
upLayer.add(temp[i]);
|
|
}
|
|
for (i = 0; i < considered.length; i++) {
|
|
var node = considered[i];
|
|
var sum = 0;
|
|
var total = 0;
|
|
for (l = 0; l < node.incoming.length; l++) {
|
|
link = node.incoming[l];
|
|
if (upLayer.contains(link.source)) {
|
|
total++;
|
|
sum += link.source.gridPosition;
|
|
}
|
|
}
|
|
for (l = 0; l < node.outgoing.length; l++) {
|
|
link = node.outgoing[l];
|
|
if (upLayer.contains(link.target)) {
|
|
total++;
|
|
sum += link.target.gridPosition;
|
|
}
|
|
}
|
|
if (total > 0) {
|
|
node.uBaryCenter = sum / total;
|
|
node.upstreamLinkCount = total;
|
|
} else {
|
|
node.uBaryCenter = i;
|
|
node.upstreamLinkCount = 0;
|
|
}
|
|
}
|
|
},
|
|
calcDownData: function (layer) {
|
|
if (layer === this.layers.length - 1) {
|
|
return;
|
|
}
|
|
var considered = this.layers[layer], i, l, link;
|
|
var downLayer = new Set();
|
|
var temp = this.layers[layer + 1];
|
|
for (i = 0; i < temp.length; i++) {
|
|
downLayer.add(temp[i]);
|
|
}
|
|
for (i = 0; i < considered.length; i++) {
|
|
var node = considered[i];
|
|
var sum = 0;
|
|
var total = 0;
|
|
for (l = 0; l < node.incoming.length; l++) {
|
|
link = node.incoming[l];
|
|
if (downLayer.contains(link.source)) {
|
|
total++;
|
|
sum += link.source.gridPosition;
|
|
}
|
|
}
|
|
for (l = 0; l < node.outgoing.length; l++) {
|
|
link = node.outgoing[l];
|
|
if (downLayer.contains(link.target)) {
|
|
total++;
|
|
sum += link.target.gridPosition;
|
|
}
|
|
}
|
|
if (total > 0) {
|
|
node.dBaryCenter = sum / total;
|
|
node.downstreamLinkCount = total;
|
|
} else {
|
|
node.dBaryCenter = i;
|
|
node.downstreamLinkCount = 0;
|
|
}
|
|
}
|
|
},
|
|
optimizeLayerCrossings: function (down, layer) {
|
|
var iconsidered;
|
|
var considered;
|
|
if (down) {
|
|
considered = this.layers[iconsidered = layer + 1];
|
|
} else {
|
|
considered = this.layers[iconsidered = layer - 1];
|
|
}
|
|
var presorted = considered.slice(0);
|
|
if (down) {
|
|
this.calcUpData(iconsidered);
|
|
} else {
|
|
this.calcDownData(iconsidered);
|
|
}
|
|
var that = this;
|
|
considered.sort(function (n1, n2) {
|
|
var n1BaryCenter = that.calcBaryCenter(n1), n2BaryCenter = that.calcBaryCenter(n2);
|
|
if (Math.abs(n1BaryCenter - n2BaryCenter) < 0.0001) {
|
|
if (n1.degree() === n2.degree()) {
|
|
return that.compareByIndex(n1, n2);
|
|
} else if (n1.degree() < n2.degree()) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
var compareValue = (n2BaryCenter - n1BaryCenter) * 1000;
|
|
if (compareValue > 0) {
|
|
return -1;
|
|
} else if (compareValue < 0) {
|
|
return 1;
|
|
}
|
|
return that.compareByIndex(n1, n2);
|
|
});
|
|
var i, moves = 0;
|
|
for (i = 0; i < considered.length; i++) {
|
|
if (considered[i] !== presorted[i]) {
|
|
moves++;
|
|
}
|
|
}
|
|
if (moves > 0) {
|
|
var inode = 0;
|
|
for (i = 0; i < considered.length; i++) {
|
|
var node = considered[i];
|
|
node.gridPosition = inode++;
|
|
}
|
|
}
|
|
return moves;
|
|
},
|
|
_swapPairs: function () {
|
|
var maxIterations = this.options.layeredIterations;
|
|
var iter = 0;
|
|
while (true) {
|
|
if (iter++ > maxIterations) {
|
|
break;
|
|
}
|
|
var downwards = iter % 4 <= 1;
|
|
var secondPass = iter % 4 === 1;
|
|
for (var l = downwards ? 0 : this.layers.length - 1; downwards ? l <= this.layers.length - 1 : l >= 0; l += downwards ? 1 : -1) {
|
|
var layer = this.layers[l];
|
|
var hasSwapped = false;
|
|
var calcCrossings = true;
|
|
var memCrossings = 0;
|
|
for (var n = 0; n < layer.length - 1; n++) {
|
|
var up = 0;
|
|
var down = 0;
|
|
var crossBefore = 0;
|
|
if (calcCrossings) {
|
|
if (l !== 0) {
|
|
up = this.countLinksCrossingBetweenTwoLayers(l - 1, l);
|
|
}
|
|
if (l !== this.layers.length - 1) {
|
|
down = this.countLinksCrossingBetweenTwoLayers(l, l + 1);
|
|
}
|
|
if (downwards) {
|
|
up *= 2;
|
|
} else {
|
|
down *= 2;
|
|
}
|
|
crossBefore = up + down;
|
|
} else {
|
|
crossBefore = memCrossings;
|
|
}
|
|
if (crossBefore === 0) {
|
|
continue;
|
|
}
|
|
var node1 = layer[n];
|
|
var node2 = layer[n + 1];
|
|
var node1GridPos = node1.gridPosition;
|
|
var node2GridPos = node2.gridPosition;
|
|
layer[n] = node2;
|
|
layer[n + 1] = node1;
|
|
node1.gridPosition = node2GridPos;
|
|
node2.gridPosition = node1GridPos;
|
|
up = 0;
|
|
if (l !== 0) {
|
|
up = this.countLinksCrossingBetweenTwoLayers(l - 1, l);
|
|
}
|
|
down = 0;
|
|
if (l !== this.layers.length - 1) {
|
|
down = this.countLinksCrossingBetweenTwoLayers(l, l + 1);
|
|
}
|
|
if (downwards) {
|
|
up *= 2;
|
|
} else {
|
|
down *= 2;
|
|
}
|
|
var crossAfter = up + down;
|
|
var revert = false;
|
|
if (secondPass) {
|
|
revert = crossAfter >= crossBefore;
|
|
} else {
|
|
revert = crossAfter > crossBefore;
|
|
}
|
|
if (revert) {
|
|
node1 = layer[n];
|
|
node2 = layer[n + 1];
|
|
node1GridPos = node1.gridPosition;
|
|
node2GridPos = node2.gridPosition;
|
|
layer[n] = node2;
|
|
layer[n + 1] = node1;
|
|
node1.gridPosition = node2GridPos;
|
|
node2.gridPosition = node1GridPos;
|
|
memCrossings = crossBefore;
|
|
calcCrossings = false;
|
|
} else {
|
|
hasSwapped = true;
|
|
calcCrossings = true;
|
|
}
|
|
}
|
|
if (hasSwapped) {
|
|
if (l !== this.layers.length - 1) {
|
|
this.calcUpData(l + 1);
|
|
}
|
|
if (l !== 0) {
|
|
this.calcDownData(l - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
countLinksCrossingBetweenTwoLayers: function (ulayer, dlayer) {
|
|
var links = this.layers[ulayer].linksTo[dlayer];
|
|
var link1, link2, n11, n12, n21, n22, l1, l2;
|
|
var crossings = 0;
|
|
var length = links.length;
|
|
for (l1 = 0; l1 < length; l1++) {
|
|
link1 = links[l1];
|
|
for (l2 = l1 + 1; l2 < length; l2++) {
|
|
link2 = links[l2];
|
|
if (link1.target.layer === dlayer) {
|
|
n11 = link1.source;
|
|
n12 = link1.target;
|
|
} else {
|
|
n11 = link1.target;
|
|
n12 = link1.source;
|
|
}
|
|
if (link2.target.layer === dlayer) {
|
|
n21 = link2.source;
|
|
n22 = link2.target;
|
|
} else {
|
|
n21 = link2.target;
|
|
n22 = link2.source;
|
|
}
|
|
var n11gp = n11.gridPosition;
|
|
var n12gp = n12.gridPosition;
|
|
var n21gp = n21.gridPosition;
|
|
var n22gp = n22.gridPosition;
|
|
if ((n11gp - n21gp) * (n12gp - n22gp) < 0) {
|
|
crossings++;
|
|
}
|
|
}
|
|
}
|
|
return crossings;
|
|
},
|
|
calcBaryCenter: function (node) {
|
|
var upstreamLinkCount = node.upstreamLinkCount;
|
|
var downstreamLinkCount = node.downstreamLinkCount;
|
|
var uBaryCenter = node.uBaryCenter;
|
|
var dBaryCenter = node.dBaryCenter;
|
|
if (upstreamLinkCount > 0 && downstreamLinkCount > 0) {
|
|
return (uBaryCenter + dBaryCenter) / 2;
|
|
}
|
|
if (upstreamLinkCount > 0) {
|
|
return uBaryCenter;
|
|
}
|
|
if (downstreamLinkCount > 0) {
|
|
return dBaryCenter;
|
|
}
|
|
return 0;
|
|
},
|
|
_gridPositionComparer: function (x, y) {
|
|
if (x.gridPosition < y.gridPosition) {
|
|
return -1;
|
|
}
|
|
if (x.gridPosition > y.gridPosition) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
},
|
|
_positionAscendingComparer: function (x, y) {
|
|
return x.k < y.k ? -1 : x.k > y.k ? 1 : 0;
|
|
},
|
|
_positionDescendingComparer: function (x, y) {
|
|
return x.k < y.k ? 1 : x.k > y.k ? -1 : 0;
|
|
},
|
|
_firstVirtualNode: function (layer) {
|
|
for (var c = 0; c < layer.length; c++) {
|
|
if (layer[c].isVirtual) {
|
|
return c;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
compareByIndex: function (o1, o2) {
|
|
var i1 = o1.index;
|
|
var i2 = o2.index;
|
|
if (i1 < i2) {
|
|
return 1;
|
|
}
|
|
if (i1 > i2) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
},
|
|
intDiv: function (numerator, denominator) {
|
|
return (numerator - numerator % denominator) / denominator;
|
|
},
|
|
nextVirtualNode: function (layer, node) {
|
|
var nodeIndex = node.layerIndex;
|
|
for (var i = nodeIndex + 1; i < layer.length; ++i) {
|
|
if (layer[i].isVirtual) {
|
|
return layer[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
var LayoutState = kendo.Class.extend({
|
|
init: function (diagram, graphOrNodes) {
|
|
if (Utils.isUndefined(diagram)) {
|
|
throw 'No diagram given';
|
|
}
|
|
this.diagram = diagram;
|
|
this.nodeMap = new Dictionary();
|
|
this.linkMap = new Dictionary();
|
|
this.capture(graphOrNodes ? graphOrNodes : diagram);
|
|
},
|
|
capture: function (diagramOrGraphOrNodes) {
|
|
var node, nodes, shape, i, conn, link, links;
|
|
if (diagramOrGraphOrNodes instanceof diagram.Graph) {
|
|
for (i = 0; i < diagramOrGraphOrNodes.nodes.length; i++) {
|
|
node = diagramOrGraphOrNodes.nodes[i];
|
|
shape = node.associatedShape;
|
|
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
|
|
}
|
|
for (i = 0; i < diagramOrGraphOrNodes.links.length; i++) {
|
|
link = diagramOrGraphOrNodes.links[i];
|
|
conn = link.associatedConnection;
|
|
this.linkMap.set(conn.visual.id, link.points());
|
|
}
|
|
} else if (diagramOrGraphOrNodes instanceof Array) {
|
|
nodes = diagramOrGraphOrNodes;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
node = nodes[i];
|
|
shape = node.associatedShape;
|
|
if (shape) {
|
|
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
|
|
}
|
|
}
|
|
} else if (diagramOrGraphOrNodes.hasOwnProperty('links') && diagramOrGraphOrNodes.hasOwnProperty('nodes')) {
|
|
nodes = diagramOrGraphOrNodes.nodes;
|
|
links = diagramOrGraphOrNodes.links;
|
|
for (i = 0; i < nodes.length; i++) {
|
|
node = nodes[i];
|
|
shape = node.associatedShape;
|
|
if (shape) {
|
|
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
|
|
}
|
|
}
|
|
for (i = 0; i < links.length; i++) {
|
|
link = links[i];
|
|
conn = link.associatedConnection;
|
|
if (conn) {
|
|
this.linkMap.set(conn.visual.id, link.points);
|
|
}
|
|
}
|
|
} else {
|
|
var shapes = this.diagram.shapes;
|
|
var connections = this.diagram.connections;
|
|
for (i = 0; i < shapes.length; i++) {
|
|
shape = shapes[i];
|
|
this.nodeMap.set(shape.visual.id, shape.bounds());
|
|
}
|
|
for (i = 0; i < connections.length; i++) {
|
|
conn = connections[i];
|
|
this.linkMap.set(conn.visual.id, conn.points());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
deepExtend(diagram, {
|
|
init: function (element) {
|
|
kendo.init(element, diagram.ui);
|
|
},
|
|
SpringLayout: SpringLayout,
|
|
TreeLayout: TreeLayout,
|
|
GraphAdapter: DiagramToHyperTreeAdapter,
|
|
LayeredLayout: LayeredLayout,
|
|
LayoutBase: LayoutBase,
|
|
LayoutState: LayoutState
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('dataviz/diagram/dom', [
|
|
'kendo.data',
|
|
'kendo.draganddrop',
|
|
'kendo.toolbar',
|
|
'kendo.editable',
|
|
'kendo.window',
|
|
'kendo.dropdownlist',
|
|
'kendo.dataviz.core',
|
|
'kendo.dataviz.themes',
|
|
'dataviz/diagram/svg',
|
|
'dataviz/diagram/services',
|
|
'dataviz/diagram/layout'
|
|
], f);
|
|
}(function () {
|
|
(function ($, undefined) {
|
|
var dataviz = kendo.dataviz, draw = kendo.drawing, geom = kendo.geometry, diagram = dataviz.diagram, Widget = kendo.ui.Widget, Class = kendo.Class, proxy = $.proxy, deepExtend = kendo.deepExtend, extend = $.extend, HierarchicalDataSource = kendo.data.HierarchicalDataSource, Canvas = diagram.Canvas, Group = diagram.Group, Rectangle = diagram.Rectangle, Circle = diagram.Circle, CompositeTransform = diagram.CompositeTransform, Rect = diagram.Rect, Path = diagram.Path, DeleteShapeUnit = diagram.DeleteShapeUnit, DeleteConnectionUnit = diagram.DeleteConnectionUnit, TextBlock = diagram.TextBlock, Image = diagram.Image, Point = diagram.Point, Intersect = diagram.Intersect, ConnectionEditAdorner = diagram.ConnectionEditAdorner, UndoRedoService = diagram.UndoRedoService, ToolService = diagram.ToolService, Selector = diagram.Selector, ResizingAdorner = diagram.ResizingAdorner, ConnectorsAdorner = diagram.ConnectorsAdorner, Cursors = diagram.Cursors, Utils = diagram.Utils, Observable = kendo.Observable, ToBackUnit = diagram.ToBackUnit, ToFrontUnit = diagram.ToFrontUnit, PolylineRouter = diagram.PolylineRouter, CascadingRouter = diagram.CascadingRouter, isUndefined = Utils.isUndefined, isDefined = Utils.isDefined, defined = kendo.util.defined, isArray = $.isArray, isFunction = kendo.isFunction, isString = Utils.isString, isPlainObject = $.isPlainObject, math = Math;
|
|
var NS = '.kendoDiagram', CASCADING = 'cascading', ITEMBOUNDSCHANGE = 'itemBoundsChange', CHANGE = 'change', CLICK = 'click', DRAG = 'drag', DRAG_END = 'dragEnd', DRAG_START = 'dragStart', MOUSE_ENTER = 'mouseEnter', MOUSE_LEAVE = 'mouseLeave', ERROR = 'error', AUTO = 'Auto', TOP = 'Top', RIGHT = 'Right', LEFT = 'Left', BOTTOM = 'Bottom', MAXINT = 9007199254740992, SELECT = 'select', ITEMROTATE = 'itemRotate', PAN = 'pan', ZOOM_START = 'zoomStart', ZOOM_END = 'zoomEnd', NONE = 'none', DEFAULT_CANVAS_WIDTH = 600, DEFAULT_CANVAS_HEIGHT = 600, DEFAULT_SHAPE_TYPE = 'rectangle', DEFAULT_SHAPE_WIDTH = 100, DEFAULT_SHAPE_HEIGHT = 100, DEFAULT_SHAPE_MINWIDTH = 20, DEFAULT_SHAPE_MINHEIGHT = 20, DEFAULT_SHAPE_POSITION = 0, DEFAULT_CONNECTION_BACKGROUND = 'Yellow', MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, ABSOLUTE = 'absolute', TRANSFORMED = 'transformed', ROTATED = 'rotated', TRANSPARENT = 'transparent', WIDTH = 'width', HEIGHT = 'height', X = 'x', Y = 'y', MOUSEWHEEL_NS = 'DOMMouseScroll' + NS + ' mousewheel' + NS, MOBILE_ZOOM_RATE = 0.05, MOBILE_PAN_DISTANCE = 5, BUTTON_TEMPLATE = '<a class="k-button k-button-icontext #=className#" href="\\#"><span class="#=iconClass# #=imageClass#"></span>#=text#</a>', CONNECTION_CONTENT_OFFSET = 5;
|
|
diagram.DefaultConnectors = [
|
|
{ name: TOP },
|
|
{ name: BOTTOM },
|
|
{ name: LEFT },
|
|
{ name: RIGHT },
|
|
{
|
|
name: AUTO,
|
|
position: function (shape) {
|
|
return shape.getPosition('center');
|
|
}
|
|
}
|
|
];
|
|
var defaultButtons = {
|
|
cancel: {
|
|
text: 'Cancel',
|
|
imageClass: 'k-cancel',
|
|
className: 'k-diagram-cancel',
|
|
iconClass: 'k-icon'
|
|
},
|
|
update: {
|
|
text: 'Update',
|
|
imageClass: 'k-update',
|
|
className: 'k-diagram-update',
|
|
iconClass: 'k-icon'
|
|
}
|
|
};
|
|
diagram.shapeDefaults = function (extra) {
|
|
var defaults = {
|
|
type: DEFAULT_SHAPE_TYPE,
|
|
path: '',
|
|
autoSize: true,
|
|
visual: null,
|
|
x: DEFAULT_SHAPE_POSITION,
|
|
y: DEFAULT_SHAPE_POSITION,
|
|
minWidth: DEFAULT_SHAPE_MINWIDTH,
|
|
minHeight: DEFAULT_SHAPE_MINHEIGHT,
|
|
width: DEFAULT_SHAPE_WIDTH,
|
|
height: DEFAULT_SHAPE_HEIGHT,
|
|
hover: {},
|
|
editable: {
|
|
connect: true,
|
|
tools: []
|
|
},
|
|
connectors: diagram.DefaultConnectors,
|
|
rotation: { angle: 0 }
|
|
};
|
|
Utils.simpleExtend(defaults, extra);
|
|
return defaults;
|
|
};
|
|
function mwDelta(e) {
|
|
var origEvent = e.originalEvent, delta = 0;
|
|
if (origEvent.wheelDelta) {
|
|
delta = -origEvent.wheelDelta / 40;
|
|
delta = delta > 0 ? math.ceil(delta) : math.floor(delta);
|
|
} else if (origEvent.detail) {
|
|
delta = origEvent.detail;
|
|
}
|
|
return delta;
|
|
}
|
|
function isAutoConnector(connector) {
|
|
return connector.options.name.toLowerCase() === AUTO.toLowerCase();
|
|
}
|
|
function closestConnector(point, shape) {
|
|
var minimumDistance = MAXINT, resCtr, ctrs = shape.connectors;
|
|
for (var i = 0; i < ctrs.length; i++) {
|
|
var ctr = ctrs[i];
|
|
if (!isAutoConnector(ctr)) {
|
|
var dist = point.distanceTo(ctr.position());
|
|
if (dist < minimumDistance) {
|
|
minimumDistance = dist;
|
|
resCtr = ctr;
|
|
}
|
|
}
|
|
}
|
|
return resCtr;
|
|
}
|
|
function indicesOfItems(group, visuals) {
|
|
var i, indices = [], visual;
|
|
var children = group.drawingContainer().children;
|
|
var length = children.length;
|
|
for (i = 0; i < visuals.length; i++) {
|
|
visual = visuals[i];
|
|
for (var j = 0; j < length; j++) {
|
|
if (children[j] == visual.drawingContainer()) {
|
|
indices.push(j);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return indices;
|
|
}
|
|
var DiagramElement = Observable.extend({
|
|
init: function (options) {
|
|
var that = this;
|
|
that.dataItem = (options || {}).dataItem;
|
|
Observable.fn.init.call(that);
|
|
that.options = deepExtend({ id: diagram.randomId() }, that.options, options);
|
|
that.isSelected = false;
|
|
that.visual = new Group({
|
|
id: that.options.id,
|
|
autoSize: that.options.autoSize
|
|
});
|
|
that.id = that.options.id;
|
|
that._template();
|
|
},
|
|
options: {
|
|
hover: {},
|
|
cursor: Cursors.grip,
|
|
content: { align: 'center middle' },
|
|
selectable: true,
|
|
serializable: true,
|
|
enable: true
|
|
},
|
|
_getCursor: function (point) {
|
|
if (this.adorner) {
|
|
return this.adorner._getCursor(point);
|
|
}
|
|
return this.options.cursor;
|
|
},
|
|
visible: function (value) {
|
|
if (isUndefined(value)) {
|
|
return this.visual.visible();
|
|
} else {
|
|
this.visual.visible(value);
|
|
}
|
|
},
|
|
bounds: function () {
|
|
},
|
|
refresh: function () {
|
|
this.visual.redraw();
|
|
},
|
|
position: function (point) {
|
|
this.options.x = point.x;
|
|
this.options.y = point.y;
|
|
this.visual.position(point);
|
|
},
|
|
toString: function () {
|
|
return this.options.id;
|
|
},
|
|
serialize: function () {
|
|
var json = deepExtend({}, { options: this.options });
|
|
if (this.dataItem) {
|
|
json.dataItem = this.dataItem.toString();
|
|
}
|
|
return json;
|
|
},
|
|
_content: function (content) {
|
|
if (content !== undefined) {
|
|
var options = this.options;
|
|
if (diagram.Utils.isString(content)) {
|
|
options.content.text = content;
|
|
} else {
|
|
deepExtend(options.content, content);
|
|
}
|
|
var contentOptions = options.content;
|
|
var contentVisual = this._contentVisual;
|
|
if (!contentVisual) {
|
|
this._createContentVisual(contentOptions);
|
|
} else {
|
|
this._updateContentVisual(contentOptions);
|
|
}
|
|
}
|
|
return this.options.content.text;
|
|
},
|
|
_createContentVisual: function (options) {
|
|
if (options.text) {
|
|
this._contentVisual = new TextBlock(options);
|
|
this._contentVisual._includeInBBox = false;
|
|
this.visual.append(this._contentVisual);
|
|
}
|
|
},
|
|
_updateContentVisual: function (options) {
|
|
this._contentVisual.redraw(options);
|
|
},
|
|
_hitTest: function (point) {
|
|
var bounds = this.bounds();
|
|
return this.visible() && bounds.contains(point) && this.options.enable;
|
|
},
|
|
_template: function () {
|
|
var that = this;
|
|
if (that.options.content.template) {
|
|
var data = that.dataItem || {}, elementTemplate = kendo.template(that.options.content.template, { paramName: 'dataItem' });
|
|
that.options.content.text = elementTemplate(data);
|
|
}
|
|
},
|
|
_canSelect: function () {
|
|
return this.options.selectable !== false;
|
|
},
|
|
toJSON: function () {
|
|
return { id: this.options.id };
|
|
}
|
|
});
|
|
var Connector = Class.extend({
|
|
init: function (shape, options) {
|
|
this.options = deepExtend({}, this.options, options);
|
|
this.connections = [];
|
|
this.shape = shape;
|
|
},
|
|
options: {
|
|
width: 7,
|
|
height: 7,
|
|
fill: { color: DEFAULT_CONNECTION_BACKGROUND },
|
|
hover: {}
|
|
},
|
|
position: function () {
|
|
if (this.options.position) {
|
|
return this.options.position(this.shape);
|
|
} else {
|
|
return this.shape.getPosition(this.options.name);
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
return {
|
|
shapeId: this.shape.toString(),
|
|
connector: this.options.name
|
|
};
|
|
}
|
|
});
|
|
Connector.parse = function (diagram, str) {
|
|
var tempStr = str.split(':'), id = tempStr[0], name = tempStr[1] || AUTO;
|
|
for (var i = 0; i < diagram.shapes.length; i++) {
|
|
var shape = diagram.shapes[i];
|
|
if (shape.options.id == id) {
|
|
return shape.getConnector(name.trim());
|
|
}
|
|
}
|
|
};
|
|
var Shape = DiagramElement.extend({
|
|
init: function (options, diagram) {
|
|
var that = this;
|
|
DiagramElement.fn.init.call(that, options);
|
|
this.diagram = diagram;
|
|
this.updateOptionsFromModel();
|
|
options = that.options;
|
|
that.connectors = [];
|
|
that.type = options.type;
|
|
that.createShapeVisual();
|
|
that.updateBounds();
|
|
that.content(that.content());
|
|
that._createConnectors();
|
|
},
|
|
options: diagram.shapeDefaults(),
|
|
_setOptionsFromModel: function (model) {
|
|
var modelOptions = filterShapeDataItem(model || this.dataItem);
|
|
this.options = deepExtend({}, this.options, modelOptions);
|
|
this.redrawVisual();
|
|
if (this.options.content) {
|
|
this._template();
|
|
this.content(this.options.content);
|
|
}
|
|
},
|
|
updateOptionsFromModel: function (model, field) {
|
|
if (this.diagram && this.diagram._isEditable) {
|
|
var modelOptions = filterShapeDataItem(model || this.dataItem);
|
|
if (model && field) {
|
|
if (!dataviz.inArray(field, [
|
|
'x',
|
|
'y',
|
|
'width',
|
|
'height'
|
|
])) {
|
|
if (this.options.visual) {
|
|
this.redrawVisual();
|
|
} else if (modelOptions.type) {
|
|
this.options = deepExtend({}, this.options, modelOptions);
|
|
this.redrawVisual();
|
|
}
|
|
if (this.options.content) {
|
|
this._template();
|
|
this.content(this.options.content);
|
|
}
|
|
} else {
|
|
var bounds = this.bounds();
|
|
bounds[field] = model[field];
|
|
this.bounds(bounds);
|
|
}
|
|
} else {
|
|
this.options = deepExtend({}, this.options, modelOptions);
|
|
}
|
|
}
|
|
},
|
|
redrawVisual: function () {
|
|
this.visual.clear();
|
|
this._contentVisual = null;
|
|
this.options.dataItem = this.dataItem;
|
|
this.createShapeVisual();
|
|
this.updateBounds();
|
|
},
|
|
updateModel: function (syncChanges) {
|
|
var diagram = this.diagram;
|
|
if (diagram && diagram._isEditable) {
|
|
var bounds = this._bounds;
|
|
var model = this.dataItem;
|
|
if (model) {
|
|
diagram._suspendModelRefresh();
|
|
if (defined(model.x) && bounds.x !== model.x) {
|
|
model.set('x', bounds.x);
|
|
}
|
|
if (defined(model.y) && bounds.y !== model.y) {
|
|
model.set('y', bounds.y);
|
|
}
|
|
if (defined(model.width) && bounds.width !== model.width) {
|
|
model.set('width', bounds.width);
|
|
}
|
|
if (defined(model.height) && bounds.height !== model.height) {
|
|
model.set('height', bounds.height);
|
|
}
|
|
this.dataItem = model;
|
|
diagram._resumeModelRefresh();
|
|
if (syncChanges) {
|
|
diagram._syncShapeChanges();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
updateBounds: function () {
|
|
var bounds = this.visual._measure(true);
|
|
var options = this.options;
|
|
this.bounds(new Rect(options.x, options.y, bounds.width, bounds.height));
|
|
this._rotate();
|
|
this._alignContent();
|
|
},
|
|
content: function (content) {
|
|
var result = this._content(content);
|
|
this._alignContent();
|
|
return result;
|
|
},
|
|
_alignContent: function () {
|
|
var contentOptions = this.options.content || {};
|
|
var contentVisual = this._contentVisual;
|
|
if (contentVisual && contentOptions.align) {
|
|
var containerRect = this.visual._measure();
|
|
var aligner = new diagram.RectAlign(containerRect);
|
|
var contentBounds = contentVisual.drawingElement.bbox(null);
|
|
var contentRect = new Rect(0, 0, contentBounds.width(), contentBounds.height());
|
|
var alignedBounds = aligner.align(contentRect, contentOptions.align);
|
|
contentVisual.position(alignedBounds.topLeft());
|
|
}
|
|
},
|
|
_createConnectors: function () {
|
|
var options = this.options, length = options.connectors.length, connectorDefaults = options.connectorDefaults, connector, i;
|
|
for (i = 0; i < length; i++) {
|
|
connector = new Connector(this, deepExtend({}, connectorDefaults, options.connectors[i]));
|
|
this.connectors.push(connector);
|
|
}
|
|
},
|
|
bounds: function (value) {
|
|
var bounds;
|
|
if (value) {
|
|
if (isString(value)) {
|
|
switch (value) {
|
|
case TRANSFORMED:
|
|
bounds = this._transformedBounds();
|
|
break;
|
|
case ABSOLUTE:
|
|
bounds = this._transformedBounds();
|
|
var pan = this.diagram._pan;
|
|
bounds.x += pan.x;
|
|
bounds.y += pan.y;
|
|
break;
|
|
case ROTATED:
|
|
bounds = this._rotatedBounds();
|
|
break;
|
|
default:
|
|
bounds = this._bounds;
|
|
}
|
|
} else {
|
|
this._setBounds(value);
|
|
this._triggerBoundsChange();
|
|
if (!(this.diagram && this.diagram._layouting)) {
|
|
this.refreshConnections();
|
|
}
|
|
}
|
|
} else {
|
|
bounds = this._bounds;
|
|
}
|
|
return bounds;
|
|
},
|
|
_setBounds: function (rect) {
|
|
var options = this.options;
|
|
var topLeft = rect.topLeft();
|
|
var x = options.x = topLeft.x;
|
|
var y = options.y = topLeft.y;
|
|
var width = options.width = math.max(rect.width, options.minWidth);
|
|
var height = options.height = math.max(rect.height, options.minHeight);
|
|
this._bounds = new Rect(x, y, width, height);
|
|
this.visual.redraw({
|
|
x: x,
|
|
y: y,
|
|
width: width,
|
|
height: height
|
|
});
|
|
},
|
|
position: function (point) {
|
|
if (point) {
|
|
this.bounds(new Rect(point.x, point.y, this._bounds.width, this._bounds.height));
|
|
} else {
|
|
return this._bounds.topLeft();
|
|
}
|
|
},
|
|
clone: function () {
|
|
var json = this.serialize();
|
|
json.options.id = diagram.randomId();
|
|
if (this.diagram && this.diagram._isEditable && defined(this.dataItem)) {
|
|
json.options.dataItem = cloneDataItem(this.dataItem);
|
|
}
|
|
return new Shape(json.options);
|
|
},
|
|
select: function (value) {
|
|
var diagram = this.diagram, selected, deselected;
|
|
if (isUndefined(value)) {
|
|
value = true;
|
|
}
|
|
if (this._canSelect()) {
|
|
if (this.isSelected != value) {
|
|
selected = [];
|
|
deselected = [];
|
|
this.isSelected = value;
|
|
if (this.isSelected) {
|
|
diagram._selectedItems.push(this);
|
|
selected.push(this);
|
|
} else {
|
|
Utils.remove(diagram._selectedItems, this);
|
|
deselected.push(this);
|
|
}
|
|
if (!diagram._internalSelection) {
|
|
diagram._selectionChanged(selected, deselected);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
rotate: function (angle, center, undoable) {
|
|
var rotate = this.visual.rotate();
|
|
if (angle !== undefined) {
|
|
if (undoable !== false && this.diagram && this.diagram.undoRedoService && angle !== rotate.angle) {
|
|
this.diagram.undoRedoService.add(new diagram.RotateUnit(this.diagram._resizingAdorner, [this], [rotate.angle]), false);
|
|
}
|
|
var b = this.bounds(), sc = new Point(b.width / 2, b.height / 2), deltaAngle, newPosition;
|
|
if (center) {
|
|
deltaAngle = angle - rotate.angle;
|
|
newPosition = b.center().rotate(center, 360 - deltaAngle).minus(sc);
|
|
this._rotationOffset = this._rotationOffset.plus(newPosition.minus(b.topLeft()));
|
|
this.position(newPosition);
|
|
}
|
|
this.visual.rotate(angle, sc);
|
|
this.options.rotation.angle = angle;
|
|
if (this.diagram && this.diagram._connectorsAdorner) {
|
|
this.diagram._connectorsAdorner.refresh();
|
|
}
|
|
this.refreshConnections();
|
|
if (this.diagram) {
|
|
this.diagram.trigger(ITEMROTATE, { item: this });
|
|
}
|
|
}
|
|
return rotate;
|
|
},
|
|
connections: function (type) {
|
|
var result = [], i, j, con, cons, ctr;
|
|
for (i = 0; i < this.connectors.length; i++) {
|
|
ctr = this.connectors[i];
|
|
cons = ctr.connections;
|
|
for (j = 0, cons; j < cons.length; j++) {
|
|
con = cons[j];
|
|
if (type == 'out') {
|
|
var source = con.source();
|
|
if (source.shape && source.shape == this) {
|
|
result.push(con);
|
|
}
|
|
} else if (type == 'in') {
|
|
var target = con.target();
|
|
if (target.shape && target.shape == this) {
|
|
result.push(con);
|
|
}
|
|
} else {
|
|
result.push(con);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
refreshConnections: function () {
|
|
$.each(this.connections(), function () {
|
|
this.refresh();
|
|
});
|
|
},
|
|
getConnector: function (nameOrPoint) {
|
|
var i, ctr;
|
|
if (isString(nameOrPoint)) {
|
|
nameOrPoint = nameOrPoint.toLocaleLowerCase();
|
|
for (i = 0; i < this.connectors.length; i++) {
|
|
ctr = this.connectors[i];
|
|
if (ctr.options.name.toLocaleLowerCase() == nameOrPoint) {
|
|
return ctr;
|
|
}
|
|
}
|
|
} else if (nameOrPoint instanceof Point) {
|
|
return closestConnector(nameOrPoint, this);
|
|
} else {
|
|
return this.connectors.length ? this.connectors[0] : null;
|
|
}
|
|
},
|
|
getPosition: function (side) {
|
|
var b = this.bounds(), fnName = side.charAt(0).toLowerCase() + side.slice(1);
|
|
if (isFunction(b[fnName])) {
|
|
return this._transformPoint(b[fnName]());
|
|
}
|
|
return b.center();
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
var shapeOptions = this.options;
|
|
var boundsChange;
|
|
this.shapeVisual.redraw(this._visualOptions(options));
|
|
if (this._diffNumericOptions(options, [
|
|
WIDTH,
|
|
HEIGHT,
|
|
X,
|
|
Y
|
|
])) {
|
|
this.bounds(new Rect(shapeOptions.x, shapeOptions.y, shapeOptions.width, shapeOptions.height));
|
|
boundsChange = true;
|
|
}
|
|
if (options.connectors) {
|
|
shapeOptions.connectors = options.connectors;
|
|
this._updateConnectors();
|
|
}
|
|
shapeOptions = deepExtend(shapeOptions, options);
|
|
if (options.rotation || boundsChange) {
|
|
this._rotate();
|
|
}
|
|
if (shapeOptions.content) {
|
|
this.content(shapeOptions.content);
|
|
}
|
|
}
|
|
},
|
|
_updateConnectors: function () {
|
|
var connections = this.connections();
|
|
this.connectors = [];
|
|
this._createConnectors();
|
|
var connection;
|
|
var source;
|
|
var target;
|
|
for (var idx = 0; idx < connections.length; idx++) {
|
|
connection = connections[idx];
|
|
source = connection.source();
|
|
target = connection.target();
|
|
if (source.shape && source.shape === this) {
|
|
connection.source(this.getConnector(source.options.name) || null);
|
|
} else if (target.shape && target.shape === this) {
|
|
connection.target(this.getConnector(target.options.name) || null);
|
|
}
|
|
connection.updateModel();
|
|
}
|
|
},
|
|
_diffNumericOptions: diagram.diffNumericOptions,
|
|
_visualOptions: function (options) {
|
|
return {
|
|
data: options.path,
|
|
source: options.source,
|
|
hover: options.hover,
|
|
fill: options.fill,
|
|
stroke: options.stroke
|
|
};
|
|
},
|
|
_triggerBoundsChange: function () {
|
|
if (this.diagram) {
|
|
this.diagram.trigger(ITEMBOUNDSCHANGE, {
|
|
item: this,
|
|
bounds: this._bounds.clone()
|
|
});
|
|
}
|
|
},
|
|
_transformPoint: function (point) {
|
|
var rotate = this.rotate(), bounds = this.bounds(), tl = bounds.topLeft();
|
|
if (rotate.angle) {
|
|
point.rotate(rotate.center().plus(tl), 360 - rotate.angle);
|
|
}
|
|
return point;
|
|
},
|
|
_transformedBounds: function () {
|
|
var bounds = this.bounds(), tl = bounds.topLeft(), br = bounds.bottomRight();
|
|
return Rect.fromPoints(this.diagram.modelToView(tl), this.diagram.modelToView(br));
|
|
},
|
|
_rotatedBounds: function () {
|
|
var bounds = this.bounds().rotatedBounds(this.rotate().angle), tl = bounds.topLeft(), br = bounds.bottomRight();
|
|
return Rect.fromPoints(tl, br);
|
|
},
|
|
_rotate: function () {
|
|
var rotation = this.options.rotation;
|
|
if (rotation && rotation.angle) {
|
|
this.rotate(rotation.angle);
|
|
}
|
|
this._rotationOffset = new Point();
|
|
},
|
|
_hover: function (value) {
|
|
var options = this.options, hover = options.hover, stroke = options.stroke, fill = options.fill;
|
|
if (value && isDefined(hover.stroke)) {
|
|
stroke = deepExtend({}, stroke, hover.stroke);
|
|
}
|
|
if (value && isDefined(hover.fill)) {
|
|
fill = hover.fill;
|
|
}
|
|
this.shapeVisual.redraw({
|
|
stroke: stroke,
|
|
fill: fill
|
|
});
|
|
if (options.editable && options.editable.connect) {
|
|
this.diagram._showConnectors(this, value);
|
|
}
|
|
},
|
|
_hitTest: function (value) {
|
|
if (this.visible()) {
|
|
var bounds = this.bounds(), rotatedPoint, angle = this.rotate().angle;
|
|
if (value.isEmpty && !value.isEmpty()) {
|
|
return Intersect.rects(value, bounds, angle ? angle : 0);
|
|
} else {
|
|
rotatedPoint = value.clone().rotate(bounds.center(), angle);
|
|
if (bounds.contains(rotatedPoint)) {
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
return { shapeId: this.options.id };
|
|
},
|
|
createShapeVisual: function () {
|
|
var options = this.options;
|
|
var visualOptions = this._visualOptions(options);
|
|
var visualTemplate = options.visual;
|
|
var type = (options.type + '').toLocaleLowerCase();
|
|
var shapeVisual;
|
|
visualOptions.width = options.width;
|
|
visualOptions.height = options.height;
|
|
if (isFunction(visualTemplate)) {
|
|
shapeVisual = visualTemplate.call(this, options);
|
|
} else if (visualOptions.data) {
|
|
shapeVisual = new Path(visualOptions);
|
|
translateToOrigin(shapeVisual);
|
|
} else if (type == 'rectangle') {
|
|
shapeVisual = new Rectangle(visualOptions);
|
|
} else if (type == 'circle') {
|
|
shapeVisual = new Circle(visualOptions);
|
|
} else if (type == 'text') {
|
|
shapeVisual = new TextBlock(visualOptions);
|
|
} else if (type == 'image') {
|
|
shapeVisual = new Image(visualOptions);
|
|
} else {
|
|
shapeVisual = new Path(visualOptions);
|
|
}
|
|
this.shapeVisual = shapeVisual;
|
|
this.visual.append(this.shapeVisual);
|
|
}
|
|
});
|
|
var Connection = DiagramElement.extend({
|
|
init: function (from, to, options) {
|
|
var that = this;
|
|
DiagramElement.fn.init.call(that, options);
|
|
this.updateOptionsFromModel();
|
|
this._initRouter();
|
|
that.path = new diagram.Polyline(that.options);
|
|
that.path.fill(TRANSPARENT);
|
|
that.visual.append(that.path);
|
|
that._sourcePoint = that._targetPoint = new Point();
|
|
that._setSource(from);
|
|
that._setTarget(to);
|
|
that.content(that.options.content);
|
|
that.definers = [];
|
|
if (defined(options) && options.points) {
|
|
that.points(options.points);
|
|
}
|
|
},
|
|
options: {
|
|
hover: { stroke: {} },
|
|
startCap: NONE,
|
|
endCap: NONE,
|
|
points: [],
|
|
selectable: true,
|
|
fromConnector: AUTO,
|
|
toConenctor: AUTO
|
|
},
|
|
_setOptionsFromModel: function (model) {
|
|
this.updateOptionsFromModel(model || this.dataItem);
|
|
},
|
|
updateOptionsFromModel: function (model) {
|
|
if (this.diagram && this.diagram._isEditable) {
|
|
var dataMap = this.diagram._dataMap;
|
|
var options = filterConnectionDataItem(model || this.dataItem);
|
|
if (model) {
|
|
if (defined(options.from)) {
|
|
var from = dataMap[options.from];
|
|
if (from && defined(options.fromConnector)) {
|
|
from = from.getConnector(options.fromConnector);
|
|
}
|
|
this.source(from);
|
|
} else if (defined(options.fromX) && defined(options.fromY)) {
|
|
this.source(new Point(options.fromX, options.fromY));
|
|
}
|
|
if (defined(options.to)) {
|
|
var to = dataMap[options.to];
|
|
if (to && defined(options.toConnector)) {
|
|
to = to.getConnector(options.toConnector);
|
|
}
|
|
this.target(to);
|
|
} else if (defined(options.toX) && defined(options.toY)) {
|
|
this.target(new Point(options.toX, options.toY));
|
|
}
|
|
if (defined(options.type) && this.type() !== options.type) {
|
|
this.points([]);
|
|
this.type(options.type);
|
|
}
|
|
this.dataItem = model;
|
|
this._template();
|
|
this.redraw(this.options);
|
|
} else {
|
|
this.options = deepExtend({}, options, this.options);
|
|
}
|
|
}
|
|
},
|
|
updateModel: function (syncChanges) {
|
|
if (this.diagram && this.diagram._isEditable) {
|
|
if (this.diagram.connectionsDataSource) {
|
|
var model = this.diagram.connectionsDataSource.getByUid(this.dataItem.uid);
|
|
if (model) {
|
|
this.diagram._suspendModelRefresh();
|
|
if (defined(this.options.fromX) && this.options.fromX !== null) {
|
|
clearField('from', model);
|
|
clearField('fromConnector', model);
|
|
model.set('fromX', this.options.fromX);
|
|
model.set('fromY', this.options.fromY);
|
|
} else {
|
|
model.set('from', this.options.from);
|
|
if (defined(model.fromConnector)) {
|
|
model.set('fromConnector', this.sourceConnector ? this.sourceConnector.options.name : null);
|
|
}
|
|
clearField('fromX', model);
|
|
clearField('fromY', model);
|
|
}
|
|
if (defined(this.options.toX) && this.options.toX !== null) {
|
|
clearField('to', model);
|
|
clearField('toConnector', model);
|
|
model.set('toX', this.options.toX);
|
|
model.set('toY', this.options.toY);
|
|
} else {
|
|
model.set('to', this.options.to);
|
|
if (defined(model.toConnector)) {
|
|
model.set('toConnector', this.targetConnector ? this.targetConnector.options.name : null);
|
|
}
|
|
clearField('toX', model);
|
|
clearField('toY', model);
|
|
}
|
|
if (defined(this.options.type) && defined(model.type)) {
|
|
model.set('type', this.options.type);
|
|
}
|
|
this.dataItem = model;
|
|
this.diagram._resumeModelRefresh();
|
|
if (syncChanges) {
|
|
this.diagram._syncConnectionChanges();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
sourcePoint: function () {
|
|
return this._resolvedSourceConnector ? this._resolvedSourceConnector.position() : this._sourcePoint;
|
|
},
|
|
_setSource: function (source) {
|
|
var shapeSource = source instanceof Shape;
|
|
var defaultConnector = this.options.fromConnector || AUTO;
|
|
var dataItem;
|
|
if (shapeSource && !source.getConnector(defaultConnector)) {
|
|
return;
|
|
}
|
|
if (source !== undefined) {
|
|
this.from = source;
|
|
}
|
|
this._removeFromSourceConnector();
|
|
if (source === null) {
|
|
if (this.sourceConnector) {
|
|
this._sourcePoint = (this._resolvedSourceConnector || this.sourceConnector).position();
|
|
this._clearSourceConnector();
|
|
this._setFromOptions(null, this._sourcePoint);
|
|
}
|
|
} else if (source instanceof Connector) {
|
|
dataItem = source.shape.dataItem;
|
|
if (dataItem) {
|
|
this._setFromOptions(dataItem.id);
|
|
}
|
|
this.sourceConnector = source;
|
|
this.sourceConnector.connections.push(this);
|
|
} else if (source instanceof Point) {
|
|
this._setFromOptions(null, source);
|
|
this._sourcePoint = source;
|
|
if (this.sourceConnector) {
|
|
this._clearSourceConnector();
|
|
}
|
|
} else if (shapeSource) {
|
|
dataItem = source.dataItem;
|
|
if (dataItem) {
|
|
this._setFromOptions(dataItem.id);
|
|
}
|
|
this.sourceConnector = source.getConnector(defaultConnector);
|
|
this.sourceConnector.connections.push(this);
|
|
}
|
|
},
|
|
source: function (source, undoable) {
|
|
if (isDefined(source)) {
|
|
if (undoable && this.diagram) {
|
|
this.diagram.undoRedoService.addCompositeItem(new diagram.ConnectionEditUnit(this, source));
|
|
}
|
|
this._setSource(source);
|
|
this.refresh();
|
|
}
|
|
return this.sourceConnector ? this.sourceConnector : this._sourcePoint;
|
|
},
|
|
_setFromOptions: function (from, fromPoint) {
|
|
this.options.from = from;
|
|
if (fromPoint) {
|
|
this.options.fromX = fromPoint.x;
|
|
this.options.fromY = fromPoint.y;
|
|
} else {
|
|
this.options.fromX = null;
|
|
this.options.fromY = null;
|
|
}
|
|
},
|
|
sourceDefiner: function (value) {
|
|
if (value) {
|
|
if (value instanceof diagram.PathDefiner) {
|
|
value.left = null;
|
|
this._sourceDefiner = value;
|
|
this.source(value.point);
|
|
} else {
|
|
throw 'The sourceDefiner needs to be a PathDefiner.';
|
|
}
|
|
} else {
|
|
if (!this._sourceDefiner) {
|
|
this._sourceDefiner = new diagram.PathDefiner(this.sourcePoint(), null, null);
|
|
}
|
|
return this._sourceDefiner;
|
|
}
|
|
},
|
|
targetPoint: function () {
|
|
return this._resolvedTargetConnector ? this._resolvedTargetConnector.position() : this._targetPoint;
|
|
},
|
|
_setTarget: function (target) {
|
|
var shapeTarget = target instanceof Shape;
|
|
var defaultConnector = this.options.toConnector || AUTO;
|
|
var dataItem;
|
|
if (shapeTarget && !target.getConnector(defaultConnector)) {
|
|
return;
|
|
}
|
|
if (target !== undefined) {
|
|
this.to = target;
|
|
}
|
|
this._removeFromTargetConnector();
|
|
if (target === null) {
|
|
if (this.targetConnector) {
|
|
this._targetPoint = (this._resolvedTargetConnector || this.targetConnector).position();
|
|
this._clearTargetConnector();
|
|
this._setToOptions(null, this._targetPoint);
|
|
}
|
|
} else if (target instanceof Connector) {
|
|
dataItem = target.shape.dataItem;
|
|
if (dataItem) {
|
|
this._setToOptions(dataItem.id);
|
|
}
|
|
this.targetConnector = target;
|
|
this.targetConnector.connections.push(this);
|
|
} else if (target instanceof Point) {
|
|
this._setToOptions(null, target);
|
|
this._targetPoint = target;
|
|
if (this.targetConnector) {
|
|
this._clearTargetConnector();
|
|
}
|
|
} else if (shapeTarget) {
|
|
dataItem = target.dataItem;
|
|
if (dataItem) {
|
|
this._setToOptions(dataItem.id);
|
|
}
|
|
this.targetConnector = target.getConnector(defaultConnector);
|
|
this.targetConnector.connections.push(this);
|
|
}
|
|
},
|
|
target: function (target, undoable) {
|
|
if (isDefined(target)) {
|
|
if (undoable && this.diagram) {
|
|
this.diagram.undoRedoService.addCompositeItem(new diagram.ConnectionEditUnit(this, undefined, target));
|
|
}
|
|
this._setTarget(target);
|
|
this.refresh();
|
|
}
|
|
return this.targetConnector ? this.targetConnector : this._targetPoint;
|
|
},
|
|
_setToOptions: function (to, toPoint) {
|
|
this.options.to = to;
|
|
if (toPoint) {
|
|
this.options.toX = toPoint.x;
|
|
this.options.toY = toPoint.y;
|
|
} else {
|
|
this.options.toX = null;
|
|
this.options.toY = null;
|
|
}
|
|
},
|
|
targetDefiner: function (value) {
|
|
if (value) {
|
|
if (value instanceof diagram.PathDefiner) {
|
|
value.right = null;
|
|
this._targetDefiner = value;
|
|
this.target(value.point);
|
|
} else {
|
|
throw 'The sourceDefiner needs to be a PathDefiner.';
|
|
}
|
|
} else {
|
|
if (!this._targetDefiner) {
|
|
this._targetDefiner = new diagram.PathDefiner(this.targetPoint(), null, null);
|
|
}
|
|
return this._targetDefiner;
|
|
}
|
|
},
|
|
_updateConnectors: function () {
|
|
this._updateConnector(this.source(), 'source');
|
|
this._updateConnector(this.target(), 'target');
|
|
},
|
|
_updateConnector: function (instance, name) {
|
|
var that = this;
|
|
var diagram = that.diagram;
|
|
if (instance instanceof Connector && !diagram.getShapeById(instance.shape.id)) {
|
|
var dataItem = instance.shape.dataItem;
|
|
var connectorName = instance.options.name;
|
|
var setNewTarget = function () {
|
|
var shape = diagram._dataMap[dataItem.id];
|
|
instance = shape.getConnector(connectorName);
|
|
that[name](instance, false);
|
|
that.updateModel();
|
|
};
|
|
if (diagram._dataMap[dataItem.id]) {
|
|
setNewTarget();
|
|
} else {
|
|
var inactiveItem = diagram._inactiveShapeItems.getByUid(dataItem.uid);
|
|
if (inactiveItem) {
|
|
diagram._deferredConnectionUpdates.push(inactiveItem.onActivate(setNewTarget));
|
|
}
|
|
}
|
|
} else {
|
|
that[name](instance, false);
|
|
}
|
|
},
|
|
content: function (content) {
|
|
var result = this._content(content);
|
|
if (defined(content)) {
|
|
this._alignContent();
|
|
}
|
|
return result;
|
|
},
|
|
_createContentVisual: function (options) {
|
|
var visual;
|
|
if (isFunction(options.visual)) {
|
|
visual = options.visual.call(this, options);
|
|
} else if (options.text) {
|
|
visual = new TextBlock(options);
|
|
}
|
|
if (visual) {
|
|
this._contentVisual = visual;
|
|
visual._includeInBBox = false;
|
|
this.visual.append(visual);
|
|
}
|
|
return visual;
|
|
},
|
|
_updateContentVisual: function (options) {
|
|
if (isFunction(options.visual)) {
|
|
this.visual.remove(this._contentVisual);
|
|
this._createContentVisual(options);
|
|
} else {
|
|
this._contentVisual.redraw(options);
|
|
}
|
|
},
|
|
_alignContent: function () {
|
|
if (this._contentVisual) {
|
|
var offset = CONNECTION_CONTENT_OFFSET;
|
|
var points = this.allPoints();
|
|
var endIdx = math.floor(points.length / 2);
|
|
var startIdx = endIdx - 1;
|
|
while (startIdx > 0 && points[startIdx].equals(points[endIdx])) {
|
|
startIdx--;
|
|
endIdx++;
|
|
}
|
|
var endPoint = points[endIdx];
|
|
var startPoint = points[startIdx];
|
|
var boundingBox = this._contentVisual._measure();
|
|
var width = boundingBox.width;
|
|
var height = boundingBox.height;
|
|
var alignToPath = points.length % 2 === 0;
|
|
var distance = startPoint.distanceTo(endPoint);
|
|
if (alignToPath && points.length > 2 && distance > 0 && (startPoint.y === endPoint.y && distance < width || startPoint.x === endPoint.x && distance < height)) {
|
|
alignToPath = false;
|
|
offset = 0;
|
|
}
|
|
var point;
|
|
if (alignToPath) {
|
|
var angle = kendo.util.deg(math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x));
|
|
point = new Point((endPoint.x - startPoint.x) / 2 + startPoint.x, (endPoint.y - startPoint.y) / 2 + startPoint.y);
|
|
if (math.abs(angle) === 90) {
|
|
point.x += offset;
|
|
point.y -= height / 2;
|
|
} else if (angle % 180 === 0) {
|
|
point.x -= width / 2;
|
|
point.y -= height + offset;
|
|
} else if (angle < -90 || 0 < angle && angle < 90) {
|
|
point.y -= height;
|
|
} else if (angle < 0 || angle > 90) {
|
|
point.x -= width;
|
|
point.y -= height;
|
|
}
|
|
} else {
|
|
var midIdx = math.floor(points.length / 2);
|
|
point = points[midIdx].clone();
|
|
startPoint = points[midIdx - 1];
|
|
endPoint = points[midIdx + 1];
|
|
var offsetX = startPoint.x <= point.x && endPoint.x <= point.x ? offset : -boundingBox.width - offset;
|
|
var offsetY = startPoint.y <= point.y && endPoint.y <= point.y ? offset : -boundingBox.height - offset;
|
|
point.x += offsetX;
|
|
point.y += offsetY;
|
|
}
|
|
this._contentVisual.position(point);
|
|
}
|
|
},
|
|
select: function (value) {
|
|
var diagram = this.diagram, selected, deselected;
|
|
if (this._canSelect()) {
|
|
if (this.isSelected !== value) {
|
|
this.isSelected = value;
|
|
selected = [];
|
|
deselected = [];
|
|
if (this.isSelected) {
|
|
this.adorner = new ConnectionEditAdorner(this, this.options.selection);
|
|
diagram._adorn(this.adorner, true);
|
|
diagram._selectedItems.push(this);
|
|
selected.push(this);
|
|
} else {
|
|
if (this.adorner) {
|
|
diagram._adorn(this.adorner, false);
|
|
Utils.remove(diagram._selectedItems, this);
|
|
this.adorner = undefined;
|
|
deselected.push(this);
|
|
}
|
|
}
|
|
if (this.adorner) {
|
|
this.adorner.refresh();
|
|
}
|
|
if (!diagram._internalSelection) {
|
|
diagram._selectionChanged(selected, deselected);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
bounds: function (value) {
|
|
if (value && !isString(value)) {
|
|
this._bounds = value;
|
|
} else {
|
|
return this._bounds;
|
|
}
|
|
},
|
|
type: function (value) {
|
|
var options = this.options;
|
|
if (value) {
|
|
if (value !== options.type) {
|
|
options.type = value;
|
|
this._initRouter();
|
|
this.refresh();
|
|
}
|
|
} else {
|
|
return options.type;
|
|
}
|
|
},
|
|
_initRouter: function () {
|
|
var type = (this.options.type || '').toLowerCase();
|
|
if (type == CASCADING) {
|
|
this._router = new CascadingRouter(this);
|
|
} else {
|
|
this._router = new PolylineRouter(this);
|
|
}
|
|
},
|
|
points: function (value) {
|
|
if (value) {
|
|
this.definers = [];
|
|
for (var i = 0; i < value.length; i++) {
|
|
var definition = value[i];
|
|
if (definition instanceof diagram.Point) {
|
|
this.definers.push(new diagram.PathDefiner(definition));
|
|
} else if (definition.hasOwnProperty('x') && definition.hasOwnProperty('y')) {
|
|
this.definers.push(new diagram.PathDefiner(new Point(definition.x, definition.y)));
|
|
} else {
|
|
throw 'A Connection point needs to be a Point or an object with x and y properties.';
|
|
}
|
|
}
|
|
} else {
|
|
var pts = [];
|
|
if (isDefined(this.definers)) {
|
|
for (var k = 0; k < this.definers.length; k++) {
|
|
pts.push(this.definers[k].point);
|
|
}
|
|
}
|
|
return pts;
|
|
}
|
|
},
|
|
allPoints: function () {
|
|
var pts = [this.sourcePoint()];
|
|
if (this.definers) {
|
|
for (var k = 0; k < this.definers.length; k++) {
|
|
pts.push(this.definers[k].point);
|
|
}
|
|
}
|
|
pts.push(this.targetPoint());
|
|
return pts;
|
|
},
|
|
refresh: function () {
|
|
this._resolveConnectors();
|
|
this._refreshPath();
|
|
this._alignContent();
|
|
if (this.adorner) {
|
|
this.adorner.refresh();
|
|
}
|
|
},
|
|
_resolveConnectors: function () {
|
|
var connection = this, sourcePoint, targetPoint, source = connection.source(), target = connection.target(), autoSourceShape, autoTargetShape;
|
|
if (source instanceof Point) {
|
|
sourcePoint = source;
|
|
} else if (source instanceof Connector) {
|
|
if (isAutoConnector(source)) {
|
|
autoSourceShape = source.shape;
|
|
} else {
|
|
connection._resolvedSourceConnector = source;
|
|
sourcePoint = source.position();
|
|
}
|
|
}
|
|
if (target instanceof Point) {
|
|
targetPoint = target;
|
|
} else if (target instanceof Connector) {
|
|
if (isAutoConnector(target)) {
|
|
autoTargetShape = target.shape;
|
|
} else {
|
|
connection._resolvedTargetConnector = target;
|
|
targetPoint = target.position();
|
|
}
|
|
}
|
|
if (sourcePoint) {
|
|
if (autoTargetShape) {
|
|
connection._resolvedTargetConnector = closestConnector(sourcePoint, autoTargetShape);
|
|
}
|
|
} else if (autoSourceShape) {
|
|
if (targetPoint) {
|
|
connection._resolvedSourceConnector = closestConnector(targetPoint, autoSourceShape);
|
|
} else if (autoTargetShape) {
|
|
this._resolveAutoConnectors(autoSourceShape, autoTargetShape);
|
|
}
|
|
}
|
|
},
|
|
_resolveAutoConnectors: function (autoSourceShape, autoTargetShape) {
|
|
var minNonConflict = MAXINT;
|
|
var minDist = MAXINT;
|
|
var sourceConnectors = autoSourceShape.connectors;
|
|
var targetConnectors;
|
|
var minNonConflictSource, minNonConflictTarget;
|
|
var sourcePoint, targetPoint;
|
|
var minSource, minTarget;
|
|
var sourceConnector, targetConnector;
|
|
var sourceIdx, targetIdx;
|
|
var dist;
|
|
for (sourceIdx = 0; sourceIdx < sourceConnectors.length; sourceIdx++) {
|
|
sourceConnector = sourceConnectors[sourceIdx];
|
|
if (!isAutoConnector(sourceConnector)) {
|
|
sourcePoint = sourceConnector.position();
|
|
targetConnectors = autoTargetShape.connectors;
|
|
for (targetIdx = 0; targetIdx < targetConnectors.length; targetIdx++) {
|
|
targetConnector = targetConnectors[targetIdx];
|
|
if (!isAutoConnector(targetConnector)) {
|
|
targetPoint = targetConnector.position();
|
|
dist = math.round(sourcePoint.distanceTo(targetPoint));
|
|
if (dist < minNonConflict && this.diagram && this._testRoutePoints(sourcePoint, targetPoint, sourceConnector, targetConnector)) {
|
|
minNonConflict = dist;
|
|
minNonConflictSource = sourceConnector;
|
|
minNonConflictTarget = targetConnector;
|
|
}
|
|
if (dist < minDist) {
|
|
minSource = sourceConnector;
|
|
minTarget = targetConnector;
|
|
minDist = dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (minNonConflictSource) {
|
|
minSource = minNonConflictSource;
|
|
minTarget = minNonConflictTarget;
|
|
}
|
|
this._resolvedSourceConnector = minSource;
|
|
this._resolvedTargetConnector = minTarget;
|
|
},
|
|
_testRoutePoints: function (sourcePoint, targetPoint, sourceConnector, targetConnector) {
|
|
var router = this._router;
|
|
var passRoute = true;
|
|
if (router instanceof CascadingRouter) {
|
|
var points = router.routePoints(sourcePoint, targetPoint, sourceConnector, targetConnector), start, end, rect;
|
|
points.unshift(sourcePoint);
|
|
points.push(targetPoint);
|
|
for (var idx = 1; idx < points.length; idx++) {
|
|
start = points[idx - 1];
|
|
end = points[idx];
|
|
rect = new Rect(math.min(start.x, end.x), math.min(start.y, end.y), math.abs(start.x - end.x), math.abs(start.y - end.y));
|
|
if (rect.width > 0) {
|
|
rect.x++;
|
|
rect.width -= 2;
|
|
}
|
|
if (rect.height > 0) {
|
|
rect.y++;
|
|
rect.height -= 2;
|
|
}
|
|
if (!rect.isEmpty() && this.diagram._shapesQuadTree.hitTestRect(rect)) {
|
|
passRoute = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return passRoute;
|
|
},
|
|
redraw: function (options) {
|
|
if (options) {
|
|
this.options = deepExtend({}, this.options, options);
|
|
var points = this.options.points;
|
|
if (defined(points) && points.length > 0) {
|
|
this.points(points);
|
|
this._refreshPath();
|
|
}
|
|
if (options && options.content || options.text) {
|
|
this.content(options.content);
|
|
}
|
|
this.path.redraw({
|
|
fill: options.fill,
|
|
stroke: options.stroke,
|
|
startCap: options.startCap,
|
|
endCap: options.endCap
|
|
});
|
|
}
|
|
},
|
|
clone: function () {
|
|
var json = this.serialize();
|
|
if (this.diagram && this.diagram._isEditable && defined(this.dataItem)) {
|
|
json.options.dataItem = cloneDataItem(this.dataItem);
|
|
}
|
|
return new Connection(this.from, this.to, json.options);
|
|
},
|
|
serialize: function () {
|
|
var from = this.from.toJSON ? this.from.toJSON : this.from.toString(), to = this.to.toJSON ? this.to.toJSON : this.to.toString();
|
|
var json = deepExtend({}, {
|
|
options: this.options,
|
|
from: from,
|
|
to: to
|
|
});
|
|
if (defined(this.dataItem)) {
|
|
json.dataItem = this.dataItem.toString();
|
|
}
|
|
json.options.points = this.points();
|
|
return json;
|
|
},
|
|
_hitTest: function (value) {
|
|
if (this.visible()) {
|
|
var p = new Point(value.x, value.y), from = this.sourcePoint(), to = this.targetPoint();
|
|
if (value.isEmpty && !value.isEmpty() && value.contains(from) && value.contains(to)) {
|
|
return this;
|
|
}
|
|
if (this._router.hitTest(p)) {
|
|
return this;
|
|
}
|
|
}
|
|
},
|
|
_hover: function (value) {
|
|
var color = (this.options.stroke || {}).color;
|
|
if (value && isDefined(this.options.hover.stroke.color)) {
|
|
color = this.options.hover.stroke.color;
|
|
}
|
|
this.path.redraw({ stroke: { color: color } });
|
|
},
|
|
_refreshPath: function () {
|
|
if (!defined(this.path)) {
|
|
return;
|
|
}
|
|
this._drawPath();
|
|
this.bounds(this._router.getBounds());
|
|
},
|
|
_drawPath: function () {
|
|
if (this._router) {
|
|
this._router.route();
|
|
}
|
|
var source = this.sourcePoint();
|
|
var target = this.targetPoint();
|
|
var points = this.points();
|
|
this.path.redraw({ points: [source].concat(points, [target]) });
|
|
},
|
|
_clearSourceConnector: function () {
|
|
this.sourceConnector = undefined;
|
|
this._resolvedSourceConnector = undefined;
|
|
},
|
|
_clearTargetConnector: function () {
|
|
this.targetConnector = undefined;
|
|
this._resolvedTargetConnector = undefined;
|
|
},
|
|
_removeFromSourceConnector: function () {
|
|
if (this.sourceConnector) {
|
|
Utils.remove(this.sourceConnector.connections, this);
|
|
}
|
|
},
|
|
_removeFromTargetConnector: function () {
|
|
if (this.targetConnector) {
|
|
Utils.remove(this.targetConnector.connections, this);
|
|
}
|
|
},
|
|
toJSON: function () {
|
|
var connection = this;
|
|
var from, to, point;
|
|
if (connection.from && connection.from.toJSON) {
|
|
from = connection.from.toJSON();
|
|
} else {
|
|
point = connection._sourcePoint;
|
|
from = {
|
|
x: point.x,
|
|
y: point.y
|
|
};
|
|
}
|
|
if (connection.to && connection.to.toJSON) {
|
|
to = connection.to.toJSON();
|
|
} else {
|
|
point = connection._targetPoint;
|
|
to = {
|
|
x: point.x,
|
|
y: point.y
|
|
};
|
|
}
|
|
return {
|
|
from: from,
|
|
to: to
|
|
};
|
|
}
|
|
});
|
|
var Diagram = Widget.extend({
|
|
init: function (element, userOptions) {
|
|
var that = this;
|
|
kendo.destroy(element);
|
|
Widget.fn.init.call(that, element, userOptions);
|
|
that._initTheme();
|
|
that._initElements();
|
|
that._extendLayoutOptions(that.options);
|
|
that._initDefaults(userOptions);
|
|
that._initCanvas();
|
|
that.mainLayer = new Group({ id: 'main-layer' });
|
|
that.canvas.append(that.mainLayer);
|
|
that._shapesQuadTree = new ShapesQuadTree(that);
|
|
that._pan = new Point();
|
|
that._adorners = [];
|
|
that.adornerLayer = new Group({ id: 'adorner-layer' });
|
|
that.canvas.append(that.adornerLayer);
|
|
that._createHandlers();
|
|
that._initialize();
|
|
that._resizingAdorner = new ResizingAdorner(that, { editable: that.options.editable });
|
|
that._connectorsAdorner = new ConnectorsAdorner(that);
|
|
that._adorn(that._resizingAdorner, true);
|
|
that._adorn(that._connectorsAdorner, true);
|
|
that.selector = new Selector(that);
|
|
that._clipboard = [];
|
|
that.pauseMouseHandlers = false;
|
|
that._fetchFreshData();
|
|
that._createGlobalToolBar();
|
|
that._createOptionElements();
|
|
that.zoom(that.options.zoom);
|
|
that.canvas.draw();
|
|
},
|
|
options: {
|
|
name: 'Diagram',
|
|
theme: 'default',
|
|
layout: '',
|
|
zoomRate: 0.1,
|
|
zoom: 1,
|
|
zoomMin: 0,
|
|
zoomMax: 2,
|
|
dataSource: {},
|
|
draggable: true,
|
|
template: '',
|
|
autoBind: true,
|
|
editable: {
|
|
rotate: {},
|
|
resize: {},
|
|
text: true,
|
|
tools: [],
|
|
drag: {
|
|
snap: {
|
|
size: 10,
|
|
angle: 10
|
|
}
|
|
},
|
|
remove: true
|
|
},
|
|
pannable: { key: 'ctrl' },
|
|
selectable: { key: 'none' },
|
|
tooltip: {
|
|
enabled: true,
|
|
format: '{0}'
|
|
},
|
|
copy: {
|
|
enabled: true,
|
|
offsetX: 20,
|
|
offsetY: 20
|
|
},
|
|
shapeDefaults: diagram.shapeDefaults({ undoable: true }),
|
|
connectionDefaults: {
|
|
editable: { tools: [] },
|
|
type: CASCADING
|
|
},
|
|
shapes: [],
|
|
connections: []
|
|
},
|
|
events: [
|
|
ZOOM_END,
|
|
ZOOM_START,
|
|
PAN,
|
|
SELECT,
|
|
ITEMROTATE,
|
|
ITEMBOUNDSCHANGE,
|
|
CHANGE,
|
|
CLICK,
|
|
MOUSE_ENTER,
|
|
MOUSE_LEAVE,
|
|
'toolBarClick',
|
|
'save',
|
|
'cancel',
|
|
'edit',
|
|
'remove',
|
|
'add',
|
|
'dataBound',
|
|
DRAG_START,
|
|
DRAG,
|
|
DRAG_END
|
|
],
|
|
items: function () {
|
|
return $();
|
|
},
|
|
_createGlobalToolBar: function () {
|
|
var editable = this.options.editable;
|
|
if (editable) {
|
|
var tools = editable.tools;
|
|
if (this._isEditable && tools !== false && (!tools || tools.length === 0)) {
|
|
tools = [
|
|
'createShape',
|
|
'undo',
|
|
'redo',
|
|
'rotateClockwise',
|
|
'rotateAnticlockwise'
|
|
];
|
|
}
|
|
if (tools && tools.length) {
|
|
this.toolBar = new DiagramToolBar(this, {
|
|
tools: tools || {},
|
|
click: proxy(this._toolBarClick, this),
|
|
modal: false
|
|
});
|
|
this.toolBar.element.css({ textAlign: 'left' });
|
|
this.element.prepend(this.toolBar.element);
|
|
this._resize();
|
|
}
|
|
}
|
|
},
|
|
createShape: function () {
|
|
if (this.editor && this.editor.end() || !this.editor) {
|
|
var dataSource = this.dataSource;
|
|
var view = dataSource.view() || [];
|
|
var index = view.length;
|
|
var model = createModel(dataSource, {});
|
|
var shape = this._createShape(model, {});
|
|
if (!this.trigger('add', { shape: shape })) {
|
|
dataSource.insert(index, model);
|
|
var inactiveItem = this._inactiveShapeItems.getByUid(model.uid);
|
|
inactiveItem.element = shape;
|
|
this.edit(shape);
|
|
}
|
|
}
|
|
},
|
|
_createShape: function (dataItem, options) {
|
|
options = deepExtend({}, this.options.shapeDefaults, options);
|
|
options.dataItem = dataItem;
|
|
var shape = new Shape(options, this);
|
|
return shape;
|
|
},
|
|
createConnection: function () {
|
|
if (this.editor && this.editor.end() || !this.editor) {
|
|
var connectionsDataSource = this.connectionsDataSource;
|
|
var view = connectionsDataSource.view() || [];
|
|
var index = view.length;
|
|
var model = createModel(connectionsDataSource, {});
|
|
var connection = this._createConnection(model);
|
|
if (!this.trigger('add', { connection: connection })) {
|
|
this._connectionsDataMap[model.uid] = connection;
|
|
connectionsDataSource.insert(index, model);
|
|
this.addConnection(connection, false);
|
|
this.edit(connection);
|
|
}
|
|
}
|
|
},
|
|
_createConnection: function (dataItem, source, target) {
|
|
var options = deepExtend({}, this.options.connectionDefaults);
|
|
options.dataItem = dataItem;
|
|
var connection = new Connection(source || new Point(), target || new Point(), options);
|
|
return connection;
|
|
},
|
|
editModel: function (dataItem, editorType) {
|
|
this.cancelEdit();
|
|
var editors, template;
|
|
var editable = this.options.editable;
|
|
if (editorType == 'shape') {
|
|
editors = editable.shapeEditors;
|
|
template = editable.shapeTemplate;
|
|
} else if (editorType == 'connection') {
|
|
var connectionSelectorHandler = proxy(connectionSelector, this);
|
|
editors = deepExtend({}, {
|
|
from: connectionSelectorHandler,
|
|
to: connectionSelectorHandler
|
|
}, editable.connectionEditors);
|
|
template = editable.connectionTemplate;
|
|
} else {
|
|
return;
|
|
}
|
|
this.editor = new PopupEditor(this.element, {
|
|
update: proxy(this._update, this),
|
|
cancel: proxy(this._cancel, this),
|
|
model: dataItem,
|
|
type: editorType,
|
|
target: this,
|
|
editors: editors,
|
|
template: template
|
|
});
|
|
this.trigger('edit', this._editArgs());
|
|
},
|
|
edit: function (item) {
|
|
if (item.dataItem) {
|
|
var editorType = item instanceof Shape ? 'shape' : 'connection';
|
|
this.editModel(item.dataItem, editorType);
|
|
}
|
|
},
|
|
cancelEdit: function () {
|
|
if (this.editor) {
|
|
this._getEditDataSource().cancelChanges(this.editor.model);
|
|
this._destroyEditor();
|
|
}
|
|
},
|
|
saveEdit: function () {
|
|
if (this.editor && this.editor.end() && !this.trigger('save', this._editArgs())) {
|
|
this._getEditDataSource().sync();
|
|
}
|
|
},
|
|
_update: function () {
|
|
if (this.editor && this.editor.end() && !this.trigger('save', this._editArgs())) {
|
|
this._getEditDataSource().sync();
|
|
this._destroyEditor();
|
|
}
|
|
},
|
|
_cancel: function () {
|
|
if (this.editor && !this.trigger('cancel', this._editArgs())) {
|
|
var model = this.editor.model;
|
|
this._getEditDataSource().cancelChanges(model);
|
|
var element = this._connectionsDataMap[model.uid] || this._dataMap[model.id];
|
|
if (element) {
|
|
element._setOptionsFromModel(model);
|
|
}
|
|
this._destroyEditor();
|
|
}
|
|
},
|
|
_getEditDataSource: function () {
|
|
return this.editor.options.type === 'shape' ? this.dataSource : this.connectionsDataSource;
|
|
},
|
|
_editArgs: function () {
|
|
var result = { container: this.editor.wrapper };
|
|
result[this.editor.options.type] = this.editor.model;
|
|
return result;
|
|
},
|
|
_destroyEditor: function () {
|
|
if (this.editor) {
|
|
this.editor.close();
|
|
this.editor = null;
|
|
}
|
|
},
|
|
_initElements: function () {
|
|
this.wrapper = this.element.empty().css('position', 'relative').attr('tabindex', 0).addClass('k-widget k-diagram');
|
|
this.scrollable = $('<div />').appendTo(this.element);
|
|
},
|
|
_initDefaults: function (userOptions) {
|
|
var options = this.options;
|
|
var editable = options.editable;
|
|
var shapeDefaults = options.shapeDefaults;
|
|
var connectionDefaults = options.connectionDefaults;
|
|
var userShapeDefaults = (userOptions || {}).shapeDefaults;
|
|
if (editable === false) {
|
|
shapeDefaults.editable = false;
|
|
connectionDefaults.editable = false;
|
|
} else {
|
|
copyDefaultOptions(editable, shapeDefaults.editable, [
|
|
'drag',
|
|
'remove',
|
|
'connect'
|
|
]);
|
|
copyDefaultOptions(editable, connectionDefaults.editable, [
|
|
'drag',
|
|
'remove'
|
|
]);
|
|
}
|
|
if (userShapeDefaults && userShapeDefaults.connectors) {
|
|
options.shapeDefaults.connectors = userShapeDefaults.connectors;
|
|
}
|
|
},
|
|
_initCanvas: function () {
|
|
var canvasContainer = $('<div class=\'k-layer\'></div>').appendTo(this.scrollable)[0];
|
|
var viewPort = this.viewport();
|
|
this.canvas = new Canvas(canvasContainer, {
|
|
width: viewPort.width || DEFAULT_CANVAS_WIDTH,
|
|
height: viewPort.height || DEFAULT_CANVAS_HEIGHT
|
|
});
|
|
},
|
|
_createHandlers: function () {
|
|
var that = this;
|
|
var element = that.element;
|
|
element.on(MOUSEWHEEL_NS, proxy(that._wheel, that));
|
|
if (!kendo.support.touch && !kendo.support.mobileOS) {
|
|
that.toolService = new ToolService(that);
|
|
this.scroller.wrapper.on('mousemove' + NS, proxy(that._mouseMove, that)).on('mouseup' + NS, proxy(that._mouseUp, that)).on('mousedown' + NS, proxy(that._mouseDown, that)).on('mouseover' + NS, proxy(that._mouseover, that)).on('mouseout' + NS, proxy(that._mouseout, that));
|
|
element.on('keydown' + NS, proxy(that._keydown, that));
|
|
} else {
|
|
that._userEvents = new kendo.UserEvents(element, {
|
|
multiTouch: true,
|
|
tap: proxy(that._tap, that)
|
|
});
|
|
that._userEvents.bind([
|
|
'gesturestart',
|
|
'gesturechange',
|
|
'gestureend'
|
|
], {
|
|
gesturestart: proxy(that._gestureStart, that),
|
|
gesturechange: proxy(that._gestureChange, that),
|
|
gestureend: proxy(that._gestureEnd, that)
|
|
});
|
|
that.toolService = new ToolService(that);
|
|
if (that.options.pannable !== false) {
|
|
that.scroller.enable();
|
|
}
|
|
}
|
|
this._syncHandler = proxy(that._syncChanges, that);
|
|
that._resizeHandler = proxy(that.resize, that, false);
|
|
kendo.onResize(that._resizeHandler);
|
|
this.bind(ZOOM_START, proxy(that._destroyToolBar, that));
|
|
this.bind(PAN, proxy(that._destroyToolBar, that));
|
|
},
|
|
_tap: function (e) {
|
|
var toolService = this.toolService;
|
|
var p = this._caculateMobilePosition(e);
|
|
toolService._updateHoveredItem(p);
|
|
if (toolService.hoveredItem) {
|
|
var item = toolService.hoveredItem;
|
|
if (this.options.selectable !== false) {
|
|
this._destroyToolBar();
|
|
if (item.isSelected) {
|
|
item.select(false);
|
|
} else {
|
|
this.select(item, { addToSelection: true });
|
|
}
|
|
this._createToolBar();
|
|
}
|
|
this.trigger('click', {
|
|
item: item,
|
|
point: p
|
|
});
|
|
}
|
|
},
|
|
_caculateMobilePosition: function (e) {
|
|
return this.documentToModel(Point(e.x.location, e.y.location));
|
|
},
|
|
_gestureStart: function (e) {
|
|
this._destroyToolBar();
|
|
this.scroller.disable();
|
|
var initialCenter = this.documentToModel(new Point(e.center.x, e.center.y));
|
|
var eventArgs = {
|
|
point: initialCenter,
|
|
zoom: this.zoom()
|
|
};
|
|
if (this.trigger(ZOOM_START, eventArgs)) {
|
|
return;
|
|
}
|
|
this._gesture = e;
|
|
this._initialCenter = initialCenter;
|
|
},
|
|
_gestureChange: function (e) {
|
|
var previousGesture = this._gesture;
|
|
var initialCenter = this._initialCenter;
|
|
var center = this.documentToView(new Point(e.center.x, e.center.y));
|
|
var scaleDelta = e.distance / previousGesture.distance;
|
|
var zoom = this._zoom;
|
|
var updateZoom = false;
|
|
if (math.abs(scaleDelta - 1) >= MOBILE_ZOOM_RATE) {
|
|
this._zoom = zoom = this._getValidZoom(zoom * scaleDelta);
|
|
this.options.zoom = zoom;
|
|
this._gesture = e;
|
|
updateZoom = true;
|
|
}
|
|
var zoomedPoint = initialCenter.times(zoom);
|
|
var pan = center.minus(zoomedPoint);
|
|
if (updateZoom || this._pan.distanceTo(pan) >= MOBILE_PAN_DISTANCE) {
|
|
this._panTransform(pan);
|
|
this._updateAdorners();
|
|
}
|
|
e.preventDefault();
|
|
},
|
|
_gestureEnd: function () {
|
|
if (this.options.pannable !== false) {
|
|
this.scroller.enable();
|
|
}
|
|
this.trigger(ZOOM_END, {
|
|
point: this._initialCenter,
|
|
zoom: this.zoom()
|
|
});
|
|
},
|
|
_resize: function () {
|
|
var viewport = this.viewport();
|
|
if (this.canvas) {
|
|
this.canvas.size(viewport);
|
|
}
|
|
if (this.scrollable && this.toolBar) {
|
|
this.scrollable.height(viewport.height);
|
|
}
|
|
},
|
|
_mouseover: function (e) {
|
|
var node = e.target._kendoNode;
|
|
if (node && node.srcElement._hover) {
|
|
node.srcElement._hover(true, node.srcElement);
|
|
}
|
|
},
|
|
_mouseout: function (e) {
|
|
var node = e.target._kendoNode;
|
|
if (node && node.srcElement._hover) {
|
|
node.srcElement._hover(false, node.srcElement);
|
|
}
|
|
},
|
|
_initTheme: function () {
|
|
var that = this, themes = dataviz.ui.themes || {}, themeName = ((that.options || {}).theme || '').toLowerCase(), themeOptions = (themes[themeName] || {}).diagram;
|
|
that.options = deepExtend({}, themeOptions, that.options);
|
|
if (that.options.editable === true) {
|
|
deepExtend(that.options, { editable: (themeOptions || {}).editable });
|
|
}
|
|
},
|
|
_createOptionElements: function () {
|
|
var options = this.options;
|
|
var shapesLength = options.shapes.length;
|
|
if (shapesLength) {
|
|
this._createShapes();
|
|
}
|
|
if (options.connections.length) {
|
|
this._createConnections();
|
|
}
|
|
if (shapesLength && options.layout) {
|
|
this.layout(options.layout);
|
|
}
|
|
},
|
|
_createShapes: function () {
|
|
var that = this, options = that.options, shapes = options.shapes, shape, i;
|
|
for (i = 0; i < shapes.length; i++) {
|
|
shape = shapes[i];
|
|
that.addShape(shape);
|
|
}
|
|
},
|
|
_createConnections: function () {
|
|
var diagram = this, options = diagram.options, defaults = options.connectionDefaults, connections = options.connections, conn, source, target, i;
|
|
for (i = 0; i < connections.length; i++) {
|
|
conn = connections[i];
|
|
source = diagram._findConnectionTarget(conn.from);
|
|
target = diagram._findConnectionTarget(conn.to);
|
|
diagram.connect(source, target, deepExtend({}, defaults, conn));
|
|
}
|
|
},
|
|
_findConnectionTarget: function (options) {
|
|
var diagram = this;
|
|
var shapeId = isString(options) ? options : options.shapeId || options.id;
|
|
var target;
|
|
if (shapeId) {
|
|
target = diagram.getShapeById(shapeId);
|
|
if (options.connector) {
|
|
target = target.getConnector(options.connector);
|
|
}
|
|
} else {
|
|
target = new Point(options.x || 0, options.y || 0);
|
|
}
|
|
return target;
|
|
},
|
|
destroy: function () {
|
|
var that = this;
|
|
Widget.fn.destroy.call(that);
|
|
if (this._userEvents) {
|
|
this._userEvents.destroy();
|
|
}
|
|
kendo.unbindResize(that._resizeHandler);
|
|
that.clear();
|
|
that.element.off(NS);
|
|
that.scroller.wrapper.off(NS);
|
|
that.canvas.destroy(true);
|
|
that.canvas = undefined;
|
|
that._destroyEditor();
|
|
that.destroyScroller();
|
|
that._destroyGlobalToolBar();
|
|
that._destroyToolBar();
|
|
},
|
|
destroyScroller: function () {
|
|
var scroller = this.scroller;
|
|
if (!scroller) {
|
|
return;
|
|
}
|
|
scroller.destroy();
|
|
scroller.element.remove();
|
|
this.scroller = null;
|
|
},
|
|
save: function () {
|
|
var json = {
|
|
shapes: [],
|
|
connections: []
|
|
};
|
|
var i, connection, shape;
|
|
for (i = 0; i < this.shapes.length; i++) {
|
|
shape = this.shapes[i];
|
|
if (shape.options.serializable) {
|
|
json.shapes.push(shape.options);
|
|
}
|
|
}
|
|
for (i = 0; i < this.connections.length; i++) {
|
|
connection = this.connections[i];
|
|
json.connections.push(deepExtend({}, connection.options, connection.toJSON()));
|
|
}
|
|
return json;
|
|
},
|
|
focus: function () {
|
|
if (!this.element.is(kendo._activeElement())) {
|
|
var element = this.element, scrollContainer = element[0], containers = [], offsets = [], documentElement = document.documentElement, i;
|
|
do {
|
|
scrollContainer = scrollContainer.parentNode;
|
|
if (scrollContainer.scrollHeight > scrollContainer.clientHeight) {
|
|
containers.push(scrollContainer);
|
|
offsets.push(scrollContainer.scrollTop);
|
|
}
|
|
} while (scrollContainer != documentElement);
|
|
element.focus();
|
|
for (i = 0; i < containers.length; i++) {
|
|
containers[i].scrollTop = offsets[i];
|
|
}
|
|
}
|
|
},
|
|
load: function (options) {
|
|
this.clear();
|
|
this.setOptions(options);
|
|
this._createShapes();
|
|
this._createConnections();
|
|
},
|
|
setOptions: function (options) {
|
|
deepExtend(this.options, options);
|
|
},
|
|
clear: function () {
|
|
var that = this;
|
|
that.select(false);
|
|
that.mainLayer.clear();
|
|
that._shapesQuadTree.clear();
|
|
that._initialize();
|
|
},
|
|
connect: function (source, target, options) {
|
|
var connection;
|
|
if (this.connectionsDataSource && this._isEditable) {
|
|
var dataItem = this.connectionsDataSource.add({});
|
|
connection = this._connectionsDataMap[dataItem.uid];
|
|
connection.source(source);
|
|
connection.target(target);
|
|
connection.redraw(options);
|
|
connection.updateModel();
|
|
} else {
|
|
connection = new Connection(source, target, deepExtend({}, this.options.connectionDefaults, options));
|
|
this.addConnection(connection);
|
|
}
|
|
return connection;
|
|
},
|
|
connected: function (source, target) {
|
|
for (var i = 0; i < this.connections.length; i++) {
|
|
var c = this.connections[i];
|
|
if (c.from == source && c.to == target) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
addConnection: function (connection, undoable) {
|
|
if (undoable !== false) {
|
|
this.undoRedoService.add(new diagram.AddConnectionUnit(connection, this), false);
|
|
}
|
|
connection.diagram = this;
|
|
connection._setOptionsFromModel();
|
|
connection.refresh();
|
|
this.mainLayer.append(connection.visual);
|
|
this.connections.push(connection);
|
|
this.trigger(CHANGE, {
|
|
added: [connection],
|
|
removed: []
|
|
});
|
|
return connection;
|
|
},
|
|
_addConnection: function (connection, undoable) {
|
|
var connectionsDataSource = this.connectionsDataSource;
|
|
var dataItem;
|
|
if (connectionsDataSource && this._isEditable) {
|
|
dataItem = createModel(connectionsDataSource, cloneDataItem(connection.dataItem));
|
|
connection.dataItem = dataItem;
|
|
connection.updateModel();
|
|
if (!this.trigger('add', { connection: connection })) {
|
|
this._connectionsDataMap[dataItem.uid] = connection;
|
|
connectionsDataSource.add(dataItem);
|
|
this.addConnection(connection, undoable);
|
|
connection._updateConnectors();
|
|
return connection;
|
|
}
|
|
} else if (!this.trigger('add', { connection: connection })) {
|
|
this.addConnection(connection, undoable);
|
|
connection._updateConnectors();
|
|
return connection;
|
|
}
|
|
},
|
|
addShape: function (item, undoable) {
|
|
var shape, shapeDefaults = this.options.shapeDefaults;
|
|
if (item instanceof Shape) {
|
|
shape = item;
|
|
} else if (!(item instanceof kendo.Class)) {
|
|
shapeDefaults = deepExtend({}, shapeDefaults, item || {});
|
|
shape = new Shape(shapeDefaults, this);
|
|
} else {
|
|
return;
|
|
}
|
|
if (undoable !== false) {
|
|
this.undoRedoService.add(new diagram.AddShapeUnit(shape, this), false);
|
|
}
|
|
this.shapes.push(shape);
|
|
if (shape.diagram !== this) {
|
|
this._shapesQuadTree.insert(shape);
|
|
shape.diagram = this;
|
|
}
|
|
this.mainLayer.append(shape.visual);
|
|
this.trigger(CHANGE, {
|
|
added: [shape],
|
|
removed: []
|
|
});
|
|
return shape;
|
|
},
|
|
_addShape: function (shape, undoable) {
|
|
var that = this;
|
|
var dataSource = that.dataSource;
|
|
var dataItem;
|
|
if (dataSource && this._isEditable) {
|
|
dataItem = createModel(dataSource, cloneDataItem(shape.dataItem));
|
|
shape.dataItem = dataItem;
|
|
shape.updateModel();
|
|
if (!this.trigger('add', { shape: shape })) {
|
|
this.dataSource.add(dataItem);
|
|
var inactiveItem = this._inactiveShapeItems.getByUid(dataItem.uid);
|
|
inactiveItem.element = shape;
|
|
inactiveItem.undoable = undoable;
|
|
return shape;
|
|
}
|
|
} else if (!this.trigger('add', { shape: shape })) {
|
|
return this.addShape(shape, undoable);
|
|
}
|
|
},
|
|
remove: function (items, undoable) {
|
|
items = isArray(items) ? items.slice(0) : [items];
|
|
var elements = splitDiagramElements(items);
|
|
var shapes = elements.shapes;
|
|
var connections = elements.connections;
|
|
var i;
|
|
if (!defined(undoable)) {
|
|
undoable = true;
|
|
}
|
|
if (undoable) {
|
|
this.undoRedoService.begin();
|
|
}
|
|
this._suspendModelRefresh();
|
|
for (i = shapes.length - 1; i >= 0; i--) {
|
|
this._removeItem(shapes[i], undoable, connections);
|
|
}
|
|
for (i = connections.length - 1; i >= 0; i--) {
|
|
this._removeItem(connections[i], undoable);
|
|
}
|
|
this._resumeModelRefresh();
|
|
if (undoable) {
|
|
this.undoRedoService.commit(false);
|
|
}
|
|
this.trigger(CHANGE, {
|
|
added: [],
|
|
removed: items
|
|
});
|
|
},
|
|
_removeShapeDataItem: function (item) {
|
|
if (this._isEditable) {
|
|
this.dataSource.remove(item.dataItem);
|
|
delete this._dataMap[item.dataItem.id];
|
|
}
|
|
},
|
|
_removeConnectionDataItem: function (item) {
|
|
if (this._isEditable) {
|
|
this.connectionsDataSource.remove(item.dataItem);
|
|
delete this._connectionsDataMap[item.dataItem.uid];
|
|
}
|
|
},
|
|
_triggerRemove: function (items) {
|
|
var toRemove = [];
|
|
var item, args, editable;
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
item = items[idx];
|
|
editable = item.options.editable;
|
|
if (item instanceof Shape) {
|
|
args = { shape: item };
|
|
} else {
|
|
args = { connection: item };
|
|
}
|
|
if (editable && editable.remove !== false && !this.trigger('remove', args)) {
|
|
toRemove.push(item);
|
|
}
|
|
}
|
|
return toRemove;
|
|
},
|
|
undo: function () {
|
|
this.undoRedoService.undo();
|
|
},
|
|
redo: function () {
|
|
this.undoRedoService.redo();
|
|
},
|
|
select: function (item, options) {
|
|
if (isDefined(item)) {
|
|
options = deepExtend({ addToSelection: false }, options);
|
|
var addToSelection = options.addToSelection, items = [], selected = [], i, element;
|
|
if (!addToSelection) {
|
|
this.deselect();
|
|
}
|
|
this._internalSelection = true;
|
|
if (item instanceof Array) {
|
|
items = item;
|
|
} else if (item instanceof DiagramElement) {
|
|
items = [item];
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
element = items[i];
|
|
if (element.select(true)) {
|
|
selected.push(element);
|
|
}
|
|
}
|
|
this._selectionChanged(selected, []);
|
|
this._internalSelection = false;
|
|
} else {
|
|
return this._selectedItems;
|
|
}
|
|
},
|
|
selectAll: function () {
|
|
this.select(this.shapes.concat(this.connections));
|
|
},
|
|
selectArea: function (rect) {
|
|
var i, items, item;
|
|
this._internalSelection = true;
|
|
var selected = [];
|
|
if (rect instanceof Rect) {
|
|
items = this.shapes.concat(this.connections);
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if ((!rect || item._hitTest(rect)) && item.options.enable) {
|
|
if (item.select(true)) {
|
|
selected.push(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._selectionChanged(selected, []);
|
|
this._internalSelection = false;
|
|
},
|
|
deselect: function (item) {
|
|
this._internalSelection = true;
|
|
var deselected = [], items = [], element, i;
|
|
if (item instanceof Array) {
|
|
items = item;
|
|
} else if (item instanceof DiagramElement) {
|
|
items.push(item);
|
|
} else if (!isDefined(item)) {
|
|
items = this._selectedItems.slice(0);
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
element = items[i];
|
|
if (element.select(false)) {
|
|
deselected.push(element);
|
|
}
|
|
}
|
|
this._selectionChanged([], deselected);
|
|
this._internalSelection = false;
|
|
},
|
|
toFront: function (items, undoable) {
|
|
if (!items) {
|
|
items = this._selectedItems.slice();
|
|
}
|
|
var result = this._getDiagramItems(items), indices;
|
|
if (!defined(undoable) || undoable) {
|
|
indices = indicesOfItems(this.mainLayer, result.visuals);
|
|
var unit = new ToFrontUnit(this, items, indices);
|
|
this.undoRedoService.add(unit);
|
|
} else {
|
|
this.mainLayer.toFront(result.visuals);
|
|
this._fixOrdering(result, true);
|
|
}
|
|
},
|
|
toBack: function (items, undoable) {
|
|
if (!items) {
|
|
items = this._selectedItems.slice();
|
|
}
|
|
var result = this._getDiagramItems(items), indices;
|
|
if (!defined(undoable) || undoable) {
|
|
indices = indicesOfItems(this.mainLayer, result.visuals);
|
|
var unit = new ToBackUnit(this, items, indices);
|
|
this.undoRedoService.add(unit);
|
|
} else {
|
|
this.mainLayer.toBack(result.visuals);
|
|
this._fixOrdering(result, false);
|
|
}
|
|
},
|
|
bringIntoView: function (item, options) {
|
|
var viewport = this.viewport();
|
|
var aligner = new diagram.RectAlign(viewport);
|
|
var current, rect, original, newPan;
|
|
if (viewport.width === 0 || viewport.height === 0) {
|
|
return;
|
|
}
|
|
options = deepExtend({
|
|
animate: false,
|
|
align: 'center middle'
|
|
}, options);
|
|
if (options.align == 'none') {
|
|
options.align = 'center middle';
|
|
}
|
|
if (item instanceof DiagramElement) {
|
|
rect = item.bounds(TRANSFORMED);
|
|
} else if (isArray(item)) {
|
|
rect = this.boundingBox(item);
|
|
} else if (item instanceof Rect) {
|
|
rect = item.clone();
|
|
}
|
|
original = rect.clone();
|
|
rect.zoom(this._zoom);
|
|
if (rect.width > viewport.width || rect.height > viewport.height) {
|
|
this._zoom = this._getValidZoom(math.min(viewport.width / original.width, viewport.height / original.height));
|
|
rect = original.clone().zoom(this._zoom);
|
|
}
|
|
this._zoomMainLayer();
|
|
current = rect.clone();
|
|
aligner.align(rect, options.align);
|
|
newPan = rect.topLeft().minus(current.topLeft());
|
|
this.pan(newPan.times(-1), options.animate);
|
|
},
|
|
alignShapes: function (direction) {
|
|
if (isUndefined(direction)) {
|
|
direction = 'Left';
|
|
}
|
|
var items = this.select(), val, item, i;
|
|
if (items.length === 0) {
|
|
return;
|
|
}
|
|
switch (direction.toLowerCase()) {
|
|
case 'left':
|
|
case 'top':
|
|
val = MAX_VALUE;
|
|
break;
|
|
case 'right':
|
|
case 'bottom':
|
|
val = MIN_VALUE;
|
|
break;
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if (item instanceof Shape) {
|
|
switch (direction.toLowerCase()) {
|
|
case 'left':
|
|
val = math.min(val, item.options.x);
|
|
break;
|
|
case 'top':
|
|
val = math.min(val, item.options.y);
|
|
break;
|
|
case 'right':
|
|
val = math.max(val, item.options.x);
|
|
break;
|
|
case 'bottom':
|
|
val = math.max(val, item.options.y);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
var undoStates = [];
|
|
var shapes = [];
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if (item instanceof Shape) {
|
|
shapes.push(item);
|
|
undoStates.push(item.bounds());
|
|
switch (direction.toLowerCase()) {
|
|
case 'left':
|
|
case 'right':
|
|
item.position(new Point(val, item.options.y));
|
|
break;
|
|
case 'top':
|
|
case 'bottom':
|
|
item.position(new Point(item.options.x, val));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
var unit = new diagram.TransformUnit(shapes, undoStates);
|
|
this.undoRedoService.add(unit, false);
|
|
},
|
|
zoom: function (zoom, options) {
|
|
if (zoom) {
|
|
var staticPoint = options ? options.point : new diagram.Point(0, 0);
|
|
zoom = this._zoom = this._getValidZoom(zoom);
|
|
if (!isUndefined(staticPoint)) {
|
|
staticPoint = new diagram.Point(math.round(staticPoint.x), math.round(staticPoint.y));
|
|
var zoomedPoint = staticPoint.times(zoom);
|
|
var viewportVector = this.modelToView(staticPoint);
|
|
var raw = viewportVector.minus(zoomedPoint);
|
|
this._storePan(new diagram.Point(math.round(raw.x), math.round(raw.y)));
|
|
}
|
|
if (options) {
|
|
options.zoom = zoom;
|
|
}
|
|
this._panTransform();
|
|
this._updateAdorners();
|
|
}
|
|
return this._zoom;
|
|
},
|
|
_getPan: function (pan) {
|
|
var canvas = this.canvas;
|
|
if (!canvas.translate) {
|
|
pan = pan.plus(this._pan);
|
|
}
|
|
return pan;
|
|
},
|
|
pan: function (pan, animate) {
|
|
if (pan instanceof Point) {
|
|
var that = this;
|
|
var scroller = that.scroller;
|
|
pan = that._getPan(pan);
|
|
pan = pan.times(-1);
|
|
if (animate) {
|
|
scroller.animatedScrollTo(pan.x, pan.y, function () {
|
|
that._updateAdorners();
|
|
});
|
|
} else {
|
|
scroller.scrollTo(pan.x, pan.y);
|
|
that._updateAdorners();
|
|
}
|
|
}
|
|
},
|
|
viewport: function () {
|
|
var element = this.element;
|
|
var width = element.width();
|
|
var height = element.height();
|
|
if (this.toolBar) {
|
|
height -= this.toolBar.element.outerHeight();
|
|
}
|
|
return new Rect(0, 0, width, height);
|
|
},
|
|
copy: function () {
|
|
if (this.options.copy.enabled) {
|
|
this._clipboard = [];
|
|
this._copyOffset = 1;
|
|
for (var i = 0; i < this._selectedItems.length; i++) {
|
|
var item = this._selectedItems[i];
|
|
this._clipboard.push(item);
|
|
}
|
|
}
|
|
},
|
|
cut: function () {
|
|
if (this.options.copy.enabled) {
|
|
this._clipboard = [];
|
|
this._copyOffset = 0;
|
|
for (var i = 0; i < this._selectedItems.length; i++) {
|
|
var item = this._selectedItems[i];
|
|
this._clipboard.push(item);
|
|
}
|
|
this.remove(this._clipboard, true);
|
|
}
|
|
},
|
|
paste: function () {
|
|
if (this._clipboard.length > 0) {
|
|
var item, copied, i;
|
|
var mapping = {};
|
|
var elements = splitDiagramElements(this._clipboard);
|
|
var connections = elements.connections;
|
|
var shapes = elements.shapes;
|
|
var offset = {
|
|
x: this._copyOffset * this.options.copy.offsetX,
|
|
y: this._copyOffset * this.options.copy.offsetY
|
|
};
|
|
this.deselect();
|
|
for (i = 0; i < shapes.length; i++) {
|
|
item = shapes[i];
|
|
copied = item.clone();
|
|
mapping[item.id] = copied;
|
|
copied.position(new Point(item.options.x + offset.x, item.options.y + offset.y));
|
|
copied.diagram = this;
|
|
copied = this._addShape(copied);
|
|
if (copied) {
|
|
copied.select();
|
|
}
|
|
}
|
|
for (i = 0; i < connections.length; i++) {
|
|
item = connections[i];
|
|
copied = this._addConnection(item.clone());
|
|
if (copied) {
|
|
this._updateCopiedConnection(copied, item, 'source', mapping, offset);
|
|
this._updateCopiedConnection(copied, item, 'target', mapping, offset);
|
|
copied.select(true);
|
|
copied.updateModel();
|
|
}
|
|
}
|
|
this._syncChanges();
|
|
this._copyOffset += 1;
|
|
}
|
|
},
|
|
_updateCopiedConnection: function (connection, sourceConnection, connectorName, mapping, offset) {
|
|
var onActivate, inactiveItem, targetShape;
|
|
var target = sourceConnection[connectorName]();
|
|
var diagram = this;
|
|
if (target instanceof Connector && mapping[target.shape.id]) {
|
|
targetShape = mapping[target.shape.id];
|
|
if (diagram.getShapeById(targetShape.id)) {
|
|
connection[connectorName](targetShape.getConnector(target.options.name));
|
|
} else {
|
|
inactiveItem = diagram._inactiveShapeItems.getByUid(targetShape.dataItem.uid);
|
|
if (inactiveItem) {
|
|
onActivate = function (item) {
|
|
targetShape = diagram._dataMap[item.id];
|
|
connection[connectorName](targetShape.getConnector(target.options.name));
|
|
connection.updateModel();
|
|
};
|
|
diagram._deferredConnectionUpdates.push(inactiveItem.onActivate(onActivate));
|
|
}
|
|
}
|
|
} else {
|
|
connection[connectorName](new Point(sourceConnection[connectorName + 'Point']().x + offset.x, sourceConnection[connectorName + 'Point']().y + offset.y));
|
|
}
|
|
},
|
|
boundingBox: function (items, origin) {
|
|
var rect = Rect.empty(), temp, di = isDefined(items) ? this._getDiagramItems(items) : { shapes: this.shapes };
|
|
if (di.shapes.length > 0) {
|
|
var item = di.shapes[0];
|
|
rect = item.bounds(ROTATED);
|
|
for (var i = 1; i < di.shapes.length; i++) {
|
|
item = di.shapes[i];
|
|
temp = item.bounds(ROTATED);
|
|
if (origin === true) {
|
|
temp.x -= item._rotationOffset.x;
|
|
temp.y -= item._rotationOffset.y;
|
|
}
|
|
rect = rect.union(temp);
|
|
}
|
|
}
|
|
return rect;
|
|
},
|
|
_containerOffset: function () {
|
|
var containerOffset = this.element.offset();
|
|
if (this.toolBar) {
|
|
containerOffset.top += this.toolBar.element.outerHeight();
|
|
}
|
|
return containerOffset;
|
|
},
|
|
documentToView: function (point) {
|
|
var containerOffset = this._containerOffset();
|
|
return new Point(point.x - containerOffset.left, point.y - containerOffset.top);
|
|
},
|
|
viewToDocument: function (point) {
|
|
var containerOffset = this._containerOffset();
|
|
return new Point(point.x + containerOffset.left, point.y + containerOffset.top);
|
|
},
|
|
viewToModel: function (point) {
|
|
return this._transformWithMatrix(point, this._matrixInvert);
|
|
},
|
|
modelToView: function (point) {
|
|
return this._transformWithMatrix(point, this._matrix);
|
|
},
|
|
modelToLayer: function (point) {
|
|
return this._transformWithMatrix(point, this._layerMatrix);
|
|
},
|
|
layerToModel: function (point) {
|
|
return this._transformWithMatrix(point, this._layerMatrixInvert);
|
|
},
|
|
documentToModel: function (point) {
|
|
var viewPoint = this.documentToView(point);
|
|
if (!this.canvas.translate) {
|
|
viewPoint.x = viewPoint.x + this.scroller.scrollLeft;
|
|
viewPoint.y = viewPoint.y + this.scroller.scrollTop;
|
|
}
|
|
return this.viewToModel(viewPoint);
|
|
},
|
|
modelToDocument: function (point) {
|
|
return this.viewToDocument(this.modelToView(point));
|
|
},
|
|
_transformWithMatrix: function (point, matrix) {
|
|
var result = point;
|
|
if (point instanceof Point) {
|
|
if (matrix) {
|
|
result = matrix.apply(point);
|
|
}
|
|
} else {
|
|
var tl = this._transformWithMatrix(point.topLeft(), matrix), br = this._transformWithMatrix(point.bottomRight(), matrix);
|
|
result = Rect.fromPoints(tl, br);
|
|
}
|
|
return result;
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.options.dataSource = dataSource;
|
|
this._dataSource();
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
setConnectionsDataSource: function (dataSource) {
|
|
this.options.connectionsDataSource = dataSource;
|
|
this._connectionDataSource();
|
|
if (this.options.autoBind) {
|
|
this.connectionsDataSource.fetch();
|
|
}
|
|
},
|
|
layout: function (options) {
|
|
this._layouting = true;
|
|
var type;
|
|
if (isUndefined(options)) {
|
|
options = this.options.layout;
|
|
}
|
|
if (isUndefined(options) || isUndefined(options.type)) {
|
|
type = 'Tree';
|
|
} else {
|
|
type = options.type;
|
|
}
|
|
var l;
|
|
switch (type.toLowerCase()) {
|
|
case 'tree':
|
|
l = new diagram.TreeLayout(this);
|
|
break;
|
|
case 'layered':
|
|
l = new diagram.LayeredLayout(this);
|
|
break;
|
|
case 'forcedirected':
|
|
case 'force':
|
|
case 'spring':
|
|
case 'springembedder':
|
|
l = new diagram.SpringLayout(this);
|
|
break;
|
|
default:
|
|
throw 'Layout algorithm \'' + type + '\' is not supported.';
|
|
}
|
|
var initialState = new diagram.LayoutState(this);
|
|
var finalState = l.layout(options);
|
|
if (finalState) {
|
|
var unit = new diagram.LayoutUndoUnit(initialState, finalState, options ? options.animate : null);
|
|
this.undoRedoService.add(unit);
|
|
}
|
|
this._layouting = false;
|
|
this._redrawConnections();
|
|
},
|
|
getShapeById: function (id) {
|
|
var found;
|
|
found = Utils.first(this.shapes, function (s) {
|
|
return s.visual.id === id;
|
|
});
|
|
if (found) {
|
|
return found;
|
|
}
|
|
found = Utils.first(this.connections, function (c) {
|
|
return c.visual.id === id;
|
|
});
|
|
return found;
|
|
},
|
|
_extendLayoutOptions: function (options) {
|
|
if (options.layout) {
|
|
options.layout = deepExtend(diagram.LayoutBase.fn.defaultOptions || {}, options.layout);
|
|
}
|
|
},
|
|
_selectionChanged: function (selected, deselected) {
|
|
if (selected.length || deselected.length) {
|
|
this.trigger(SELECT, {
|
|
selected: selected,
|
|
deselected: deselected
|
|
});
|
|
}
|
|
},
|
|
_getValidZoom: function (zoom) {
|
|
return math.min(math.max(zoom, this.options.zoomMin), this.options.zoomMax);
|
|
},
|
|
_panTransform: function (pos) {
|
|
var diagram = this, pan = pos || diagram._pan;
|
|
if (diagram.canvas.translate) {
|
|
diagram.scroller.scrollTo(pan.x, pan.y);
|
|
diagram._zoomMainLayer();
|
|
} else {
|
|
diagram._storePan(pan);
|
|
diagram._transformMainLayer();
|
|
}
|
|
},
|
|
_finishPan: function () {
|
|
this.trigger(PAN, {
|
|
total: this._pan,
|
|
delta: Number.NaN
|
|
});
|
|
},
|
|
_storePan: function (pan) {
|
|
this._pan = pan;
|
|
this._storeViewMatrix();
|
|
},
|
|
_zoomMainLayer: function () {
|
|
var zoom = this._zoom;
|
|
var transform = new CompositeTransform(0, 0, zoom, zoom);
|
|
transform.render(this.mainLayer);
|
|
this._storeLayerMatrix(transform);
|
|
this._storeViewMatrix();
|
|
},
|
|
_transformMainLayer: function () {
|
|
var pan = this._pan, zoom = this._zoom;
|
|
var transform = new CompositeTransform(pan.x, pan.y, zoom, zoom);
|
|
transform.render(this.mainLayer);
|
|
this._storeLayerMatrix(transform);
|
|
this._storeViewMatrix();
|
|
},
|
|
_storeLayerMatrix: function (canvasTransform) {
|
|
this._layerMatrix = canvasTransform.toMatrix();
|
|
this._layerMatrixInvert = canvasTransform.invert().toMatrix();
|
|
},
|
|
_storeViewMatrix: function () {
|
|
var pan = this._pan, zoom = this._zoom;
|
|
var transform = new CompositeTransform(pan.x, pan.y, zoom, zoom);
|
|
this._matrix = transform.toMatrix();
|
|
this._matrixInvert = transform.invert().toMatrix();
|
|
},
|
|
_toIndex: function (items, indices) {
|
|
var result = this._getDiagramItems(items);
|
|
this.mainLayer.toIndex(result.visuals, indices);
|
|
this._fixOrdering(result, false);
|
|
},
|
|
_fixOrdering: function (result, toFront) {
|
|
var shapePos = toFront ? this.shapes.length - 1 : 0, conPos = toFront ? this.connections.length - 1 : 0, i, item;
|
|
for (i = 0; i < result.shapes.length; i++) {
|
|
item = result.shapes[i];
|
|
Utils.remove(this.shapes, item);
|
|
Utils.insert(this.shapes, item, shapePos);
|
|
}
|
|
for (i = 0; i < result.cons.length; i++) {
|
|
item = result.cons[i];
|
|
Utils.remove(this.connections, item);
|
|
Utils.insert(this.connections, item, conPos);
|
|
}
|
|
},
|
|
_getDiagramItems: function (items) {
|
|
var i, result = {}, args = items;
|
|
result.visuals = [];
|
|
result.shapes = [];
|
|
result.cons = [];
|
|
if (!items) {
|
|
args = this._selectedItems.slice();
|
|
} else if (!isArray(items)) {
|
|
args = [items];
|
|
}
|
|
for (i = 0; i < args.length; i++) {
|
|
var item = args[i];
|
|
if (item instanceof Shape) {
|
|
result.shapes.push(item);
|
|
result.visuals.push(item.visual);
|
|
} else if (item instanceof Connection) {
|
|
result.cons.push(item);
|
|
result.visuals.push(item.visual);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
_removeItem: function (item, undoable, removedConnections) {
|
|
item.select(false);
|
|
if (item instanceof Shape) {
|
|
this._removeShapeDataItem(item);
|
|
this._removeShape(item, undoable, removedConnections);
|
|
} else if (item instanceof Connection) {
|
|
this._removeConnectionDataItem(item);
|
|
this._removeConnection(item, undoable);
|
|
}
|
|
this.mainLayer.remove(item.visual);
|
|
},
|
|
_removeShape: function (shape, undoable, removedConnections) {
|
|
var i, connection, connector, sources = [], targets = [];
|
|
this.toolService._removeHover();
|
|
if (undoable) {
|
|
this.undoRedoService.addCompositeItem(new DeleteShapeUnit(shape));
|
|
}
|
|
Utils.remove(this.shapes, shape);
|
|
this._shapesQuadTree.remove(shape);
|
|
for (i = 0; i < shape.connectors.length; i++) {
|
|
connector = shape.connectors[i];
|
|
for (var j = 0; j < connector.connections.length; j++) {
|
|
connection = connector.connections[j];
|
|
if (!removedConnections || !dataviz.inArray(connection, removedConnections)) {
|
|
if (connection.sourceConnector == connector) {
|
|
sources.push(connection);
|
|
} else if (connection.targetConnector == connector) {
|
|
targets.push(connection);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < sources.length; i++) {
|
|
sources[i].source(null, undoable);
|
|
sources[i].updateModel();
|
|
}
|
|
for (i = 0; i < targets.length; i++) {
|
|
targets[i].target(null, undoable);
|
|
targets[i].updateModel();
|
|
}
|
|
},
|
|
_removeConnection: function (connection, undoable) {
|
|
if (connection.sourceConnector) {
|
|
Utils.remove(connection.sourceConnector.connections, connection);
|
|
}
|
|
if (connection.targetConnector) {
|
|
Utils.remove(connection.targetConnector.connections, connection);
|
|
}
|
|
if (undoable) {
|
|
this.undoRedoService.addCompositeItem(new DeleteConnectionUnit(connection));
|
|
}
|
|
Utils.remove(this.connections, connection);
|
|
},
|
|
_removeDataItems: function (items, recursive) {
|
|
var item, children, shape, idx;
|
|
items = isArray(items) ? items : [items];
|
|
while (items.length) {
|
|
item = items.shift();
|
|
shape = this._dataMap[item.uid];
|
|
if (shape) {
|
|
this._removeShapeConnections(shape);
|
|
this._removeItem(shape, false);
|
|
delete this._dataMap[item.uid];
|
|
if (recursive && item.hasChildren && item.loaded()) {
|
|
children = item.children.data();
|
|
for (idx = 0; idx < children.length; idx++) {
|
|
items.push(children[idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_removeShapeConnections: function (shape) {
|
|
var connections = shape.connections();
|
|
var idx;
|
|
if (connections) {
|
|
for (idx = 0; idx < connections.length; idx++) {
|
|
this._removeItem(connections[idx], false);
|
|
}
|
|
}
|
|
},
|
|
_addDataItem: function (dataItem, undoable) {
|
|
if (!defined(dataItem)) {
|
|
return;
|
|
}
|
|
var shape = this._dataMap[dataItem.id];
|
|
if (shape) {
|
|
return shape;
|
|
}
|
|
var options = deepExtend({}, this.options.shapeDefaults);
|
|
options.dataItem = dataItem;
|
|
shape = new Shape(options, this);
|
|
this.addShape(shape, undoable !== false);
|
|
this._dataMap[dataItem.id] = shape;
|
|
return shape;
|
|
},
|
|
_addDataItemByUid: function (dataItem) {
|
|
if (!defined(dataItem)) {
|
|
return;
|
|
}
|
|
var shape = this._dataMap[dataItem.uid];
|
|
if (shape) {
|
|
return shape;
|
|
}
|
|
var options = deepExtend({}, this.options.shapeDefaults);
|
|
options.dataItem = dataItem;
|
|
shape = new Shape(options, this);
|
|
this.addShape(shape);
|
|
this._dataMap[dataItem.uid] = shape;
|
|
return shape;
|
|
},
|
|
_addDataItems: function (items, parent) {
|
|
var item, idx, shape, parentShape, connection;
|
|
for (idx = 0; idx < items.length; idx++) {
|
|
item = items[idx];
|
|
shape = this._addDataItemByUid(item);
|
|
parentShape = this._addDataItemByUid(parent);
|
|
if (parentShape && !this.connected(parentShape, shape)) {
|
|
connection = this.connect(parentShape, shape);
|
|
}
|
|
}
|
|
},
|
|
_refreshSource: function (e) {
|
|
var that = this, node = e.node, action = e.action, items = e.items, options = that.options, idx, dataBound;
|
|
if (e.field) {
|
|
return;
|
|
}
|
|
if (action == 'remove') {
|
|
this._removeDataItems(e.items, true);
|
|
} else {
|
|
if ((!action || action === 'itemloaded') && !this._bindingRoots) {
|
|
this._bindingRoots = true;
|
|
dataBound = true;
|
|
}
|
|
if (!action && !node) {
|
|
that.clear();
|
|
}
|
|
this._addDataItems(items, node);
|
|
for (idx = 0; idx < items.length; idx++) {
|
|
items[idx].load();
|
|
}
|
|
}
|
|
if (options.layout && (dataBound || action == 'remove' || action == 'add')) {
|
|
that.layout(options.layout);
|
|
}
|
|
if (dataBound) {
|
|
this.trigger('dataBound');
|
|
this._bindingRoots = false;
|
|
}
|
|
},
|
|
_mouseDown: function (e) {
|
|
var p = this._calculatePosition(e);
|
|
if (e.which == 1 && this.toolService.start(p, this._meta(e))) {
|
|
this._destroyToolBar();
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_addItem: function (item) {
|
|
if (item instanceof Shape) {
|
|
this.addShape(item);
|
|
} else if (item instanceof Connection) {
|
|
this.addConnection(item);
|
|
}
|
|
},
|
|
_mouseUp: function (e) {
|
|
var p = this._calculatePosition(e);
|
|
if (e.which == 1 && this.toolService.end(p, this._meta(e))) {
|
|
this._createToolBar();
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_createToolBar: function () {
|
|
var diagram = this.toolService.diagram;
|
|
if (!this.singleToolBar && diagram.select().length === 1) {
|
|
var element = diagram.select()[0];
|
|
if (element && element.options.editable !== false) {
|
|
var editable = element.options.editable;
|
|
var tools = editable.tools;
|
|
if (this._isEditable && tools.length === 0) {
|
|
if (element instanceof Shape) {
|
|
tools = [
|
|
'edit',
|
|
'rotateClockwise',
|
|
'rotateAnticlockwise'
|
|
];
|
|
} else if (element instanceof Connection) {
|
|
tools = ['edit'];
|
|
}
|
|
if (editable && editable.remove !== false) {
|
|
tools.push('delete');
|
|
}
|
|
}
|
|
if (tools && tools.length) {
|
|
var padding = 20;
|
|
var point;
|
|
this.singleToolBar = new DiagramToolBar(diagram, {
|
|
tools: tools,
|
|
click: proxy(this._toolBarClick, this),
|
|
modal: true
|
|
});
|
|
var popupWidth = this.singleToolBar._popup.element.outerWidth();
|
|
var popupHeight = this.singleToolBar._popup.element.outerHeight();
|
|
if (element instanceof Shape) {
|
|
var shapeBounds = this.modelToView(element.bounds(ROTATED));
|
|
point = Point(shapeBounds.x, shapeBounds.y).minus(Point((popupWidth - shapeBounds.width) / 2, popupHeight + padding));
|
|
} else if (element instanceof Connection) {
|
|
var connectionBounds = this.modelToView(element.bounds());
|
|
point = Point(connectionBounds.x, connectionBounds.y).minus(Point((popupWidth - connectionBounds.width - 20) / 2, popupHeight + padding));
|
|
}
|
|
if (point) {
|
|
if (!this.canvas.translate) {
|
|
point = point.minus(Point(this.scroller.scrollLeft, this.scroller.scrollTop));
|
|
}
|
|
point = this.viewToDocument(point);
|
|
point = Point(math.max(point.x, 0), math.max(point.y, 0));
|
|
this.singleToolBar.showAt(point);
|
|
} else {
|
|
this._destroyToolBar();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_toolBarClick: function (e) {
|
|
this.trigger('toolBarClick', e);
|
|
this._destroyToolBar();
|
|
},
|
|
_mouseMove: function (e) {
|
|
if (this.pauseMouseHandlers) {
|
|
return;
|
|
}
|
|
var p = this._calculatePosition(e);
|
|
if ((e.which === 0 || e.which == 1) && this.toolService.move(p, this._meta(e))) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_keydown: function (e) {
|
|
if (this.toolService.keyDown(e.keyCode, this._meta(e))) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_wheel: function (e) {
|
|
var delta = mwDelta(e), p = this._calculatePosition(e), meta = deepExtend(this._meta(e), { delta: delta });
|
|
if (this.toolService.wheel(p, meta)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_meta: function (e) {
|
|
return {
|
|
ctrlKey: e.ctrlKey,
|
|
metaKey: e.metaKey,
|
|
altKey: e.altKey,
|
|
shiftKey: e.shiftKey
|
|
};
|
|
},
|
|
_calculatePosition: function (e) {
|
|
var pointEvent = e.pageX === undefined ? e.originalEvent : e, point = new Point(pointEvent.pageX, pointEvent.pageY), offset = this.documentToModel(point);
|
|
return offset;
|
|
},
|
|
_normalizePointZoom: function (point) {
|
|
return point.times(1 / this.zoom());
|
|
},
|
|
_initialize: function () {
|
|
this.shapes = [];
|
|
this._selectedItems = [];
|
|
this.connections = [];
|
|
this._dataMap = {};
|
|
this._connectionsDataMap = {};
|
|
this._inactiveShapeItems = new InactiveItemsCollection();
|
|
this._deferredConnectionUpdates = [];
|
|
this.undoRedoService = new UndoRedoService({
|
|
undone: this._syncHandler,
|
|
redone: this._syncHandler
|
|
});
|
|
this.id = diagram.randomId();
|
|
},
|
|
_fetchFreshData: function () {
|
|
var that = this;
|
|
that._dataSource();
|
|
if (that._isEditable) {
|
|
that._connectionDataSource();
|
|
}
|
|
if (that.options.autoBind) {
|
|
if (that._isEditable) {
|
|
this._loadingShapes = true;
|
|
this._loadingConnections = true;
|
|
that.dataSource.fetch();
|
|
that.connectionsDataSource.fetch();
|
|
} else {
|
|
that.dataSource.fetch();
|
|
}
|
|
}
|
|
},
|
|
_dataSource: function () {
|
|
if (defined(this.options.connectionsDataSource)) {
|
|
this._isEditable = true;
|
|
var dsOptions = this.options.dataSource || {};
|
|
var ds = isArray(dsOptions) ? { data: dsOptions } : dsOptions;
|
|
if (this.dataSource && this._shapesRefreshHandler) {
|
|
this.dataSource.unbind('change', this._shapesRefreshHandler).unbind('requestStart', this._shapesRequestStartHandler).unbind('error', this._shapesErrorHandler);
|
|
} else {
|
|
this._shapesRefreshHandler = proxy(this._refreshShapes, this);
|
|
this._shapesRequestStartHandler = proxy(this._shapesRequestStart, this);
|
|
this._shapesErrorHandler = proxy(this._error, this);
|
|
}
|
|
this.dataSource = kendo.data.DataSource.create(ds).bind('change', this._shapesRefreshHandler).bind('requestStart', this._shapesRequestStartHandler).bind('error', this._shapesErrorHandler);
|
|
} else {
|
|
this._treeDataSource();
|
|
this._isEditable = false;
|
|
}
|
|
},
|
|
_connectionDataSource: function () {
|
|
var dsOptions = this.options.connectionsDataSource;
|
|
if (dsOptions) {
|
|
var ds = isArray(dsOptions) ? { data: dsOptions } : dsOptions;
|
|
if (this.connectionsDataSource && this._connectionsRefreshHandler) {
|
|
this.connectionsDataSource.unbind('change', this._connectionsRefreshHandler).unbind('requestStart', this._connectionsRequestStartHandler).unbind('error', this._connectionsErrorHandler);
|
|
} else {
|
|
this._connectionsRefreshHandler = proxy(this._refreshConnections, this);
|
|
this._connectionsRequestStartHandler = proxy(this._connectionsRequestStart, this);
|
|
this._connectionsErrorHandler = proxy(this._connectionsError, this);
|
|
}
|
|
this.connectionsDataSource = kendo.data.DataSource.create(ds).bind('change', this._connectionsRefreshHandler).bind('requestStart', this._connectionsRequestStartHandler).bind('error', this._connectionsErrorHandler);
|
|
}
|
|
},
|
|
_shapesRequestStart: function (e) {
|
|
if (e.type == 'read') {
|
|
this._loadingShapes = true;
|
|
}
|
|
},
|
|
_connectionsRequestStart: function (e) {
|
|
if (e.type == 'read') {
|
|
this._loadingConnections = true;
|
|
}
|
|
},
|
|
_error: function () {
|
|
this._loadingShapes = false;
|
|
},
|
|
_connectionsError: function () {
|
|
this._loadingConnections = false;
|
|
},
|
|
_refreshShapes: function (e) {
|
|
if (e.action === 'remove') {
|
|
if (this._shouldRefresh()) {
|
|
this._removeShapes(e.items);
|
|
}
|
|
} else if (e.action === 'itemchange') {
|
|
if (this._shouldRefresh()) {
|
|
this._updateShapes(e.items, e.field);
|
|
}
|
|
} else if (e.action === 'add') {
|
|
this._inactiveShapeItems.add(e.items);
|
|
} else if (e.action === 'sync') {
|
|
this._syncShapes(e.items);
|
|
} else {
|
|
this.refresh();
|
|
}
|
|
},
|
|
_shouldRefresh: function () {
|
|
return !this._suspended;
|
|
},
|
|
_suspendModelRefresh: function () {
|
|
this._suspended = (this._suspended || 0) + 1;
|
|
},
|
|
_resumeModelRefresh: function () {
|
|
this._suspended = math.max((this._suspended || 0) - 1, 0);
|
|
},
|
|
refresh: function () {
|
|
this._loadingShapes = false;
|
|
if (!this._loadingConnections) {
|
|
this._rebindShapesAndConnections();
|
|
}
|
|
},
|
|
_rebindShapesAndConnections: function () {
|
|
this.clear();
|
|
this._addShapes(this.dataSource.view());
|
|
if (this.connectionsDataSource) {
|
|
this._addConnections(this.connectionsDataSource.view(), false);
|
|
}
|
|
if (this.options.layout) {
|
|
this.layout(this.options.layout);
|
|
} else {
|
|
this._redrawConnections();
|
|
}
|
|
this.trigger('dataBound');
|
|
},
|
|
refreshConnections: function () {
|
|
this._loadingConnections = false;
|
|
if (!this._loadingShapes) {
|
|
this._rebindShapesAndConnections();
|
|
}
|
|
},
|
|
_redrawConnections: function () {
|
|
var connections = this.connections;
|
|
for (var idx = 0; idx < connections.length; idx++) {
|
|
connections[idx].refresh();
|
|
}
|
|
},
|
|
_removeShapes: function (items) {
|
|
var dataMap = this._dataMap;
|
|
var item, i;
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if (dataMap[item.id]) {
|
|
this.remove(dataMap[item.id], false);
|
|
dataMap[item.id] = null;
|
|
}
|
|
}
|
|
},
|
|
_syncShapes: function () {
|
|
var diagram = this;
|
|
var inactiveItems = diagram._inactiveShapeItems;
|
|
inactiveItems.forEach(function (inactiveItem) {
|
|
var dataItem = inactiveItem.dataItem;
|
|
var shape = inactiveItem.element;
|
|
if (!dataItem.isNew()) {
|
|
if (shape) {
|
|
shape._setOptionsFromModel();
|
|
diagram.addShape(shape, inactiveItem.undoable);
|
|
diagram._dataMap[dataItem.id] = shape;
|
|
} else {
|
|
diagram._addDataItem(dataItem);
|
|
}
|
|
inactiveItem.activate();
|
|
inactiveItems.remove(dataItem);
|
|
}
|
|
});
|
|
},
|
|
_updateShapes: function (items, field) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
var dataItem = items[i];
|
|
var shape = this._dataMap[dataItem.id];
|
|
if (shape) {
|
|
shape.updateOptionsFromModel(dataItem, field);
|
|
}
|
|
}
|
|
},
|
|
_addShapes: function (dataItems) {
|
|
for (var i = 0; i < dataItems.length; i++) {
|
|
this._addDataItem(dataItems[i], false);
|
|
}
|
|
},
|
|
_refreshConnections: function (e) {
|
|
if (e.action === 'remove') {
|
|
if (this._shouldRefresh()) {
|
|
this._removeConnections(e.items);
|
|
}
|
|
} else if (e.action === 'add') {
|
|
this._addConnections(e.items);
|
|
} else if (e.action === 'sync') {
|
|
} else if (e.action === 'itemchange') {
|
|
if (this._shouldRefresh()) {
|
|
this._updateConnections(e.items);
|
|
}
|
|
} else {
|
|
this.refreshConnections();
|
|
}
|
|
},
|
|
_removeConnections: function (items) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
this.remove(this._connectionsDataMap[items[i].uid], false);
|
|
this._connectionsDataMap[items[i].uid] = null;
|
|
}
|
|
},
|
|
_updateConnections: function (items) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
var dataItem = items[i];
|
|
var connection = this._connectionsDataMap[dataItem.uid];
|
|
connection.updateOptionsFromModel(dataItem);
|
|
}
|
|
},
|
|
_addConnections: function (connections, undoable) {
|
|
var length = connections.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var dataItem = connections[i];
|
|
this._addConnectionDataItem(dataItem, undoable);
|
|
}
|
|
},
|
|
_addConnectionDataItem: function (dataItem, undoable) {
|
|
if (!this._connectionsDataMap[dataItem.uid]) {
|
|
var from = this._validateConnector(dataItem.from);
|
|
if (!defined(from) || from === null) {
|
|
from = new Point(dataItem.fromX, dataItem.fromY);
|
|
}
|
|
var to = this._validateConnector(dataItem.to);
|
|
if (!defined(to) || to === null) {
|
|
to = new Point(dataItem.toX, dataItem.toY);
|
|
}
|
|
if (defined(from) && defined(to)) {
|
|
var options = deepExtend({}, this.options.connectionDefaults);
|
|
options.dataItem = dataItem;
|
|
var connection = new Connection(from, to, options);
|
|
this._connectionsDataMap[dataItem.uid] = connection;
|
|
this.addConnection(connection, undoable);
|
|
}
|
|
}
|
|
},
|
|
_validateConnector: function (value) {
|
|
var connector;
|
|
if (defined(value) && value !== null) {
|
|
connector = this._dataMap[value];
|
|
}
|
|
return connector;
|
|
},
|
|
_treeDataSource: function () {
|
|
var that = this, options = that.options, dataSource = options.dataSource;
|
|
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
|
|
if (!dataSource.fields) {
|
|
dataSource.fields = [
|
|
{ field: 'text' },
|
|
{ field: 'url' },
|
|
{ field: 'spriteCssClass' },
|
|
{ field: 'imageUrl' }
|
|
];
|
|
}
|
|
if (that.dataSource && that._refreshHandler) {
|
|
that._unbindDataSource();
|
|
}
|
|
that._refreshHandler = proxy(that._refreshSource, that);
|
|
that._errorHandler = proxy(that._error, that);
|
|
that.dataSource = HierarchicalDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(ERROR, that._errorHandler);
|
|
},
|
|
_unbindDataSource: function () {
|
|
var that = this;
|
|
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(ERROR, that._errorHandler);
|
|
},
|
|
_adorn: function (adorner, isActive) {
|
|
if (isActive !== undefined && adorner) {
|
|
if (isActive) {
|
|
this._adorners.push(adorner);
|
|
this.adornerLayer.append(adorner.visual);
|
|
} else {
|
|
Utils.remove(this._adorners, adorner);
|
|
this.adornerLayer.remove(adorner.visual);
|
|
}
|
|
}
|
|
},
|
|
_showConnectors: function (shape, value) {
|
|
if (value) {
|
|
this._connectorsAdorner.show(shape);
|
|
} else {
|
|
this._connectorsAdorner.destroy();
|
|
}
|
|
},
|
|
_updateAdorners: function () {
|
|
var adorners = this._adorners;
|
|
for (var i = 0; i < adorners.length; i++) {
|
|
var adorner = adorners[i];
|
|
if (adorner.refreshBounds) {
|
|
adorner.refreshBounds();
|
|
}
|
|
adorner.refresh();
|
|
}
|
|
},
|
|
_refresh: function () {
|
|
for (var i = 0; i < this.connections.length; i++) {
|
|
this.connections[i].refresh();
|
|
}
|
|
},
|
|
_destroyToolBar: function () {
|
|
if (this.singleToolBar) {
|
|
this.singleToolBar.hide();
|
|
this.singleToolBar.destroy();
|
|
this.singleToolBar = null;
|
|
}
|
|
},
|
|
_destroyGlobalToolBar: function () {
|
|
if (this.toolBar) {
|
|
this.toolBar.hide();
|
|
this.toolBar.destroy();
|
|
this.toolBar = null;
|
|
}
|
|
},
|
|
exportDOMVisual: function () {
|
|
var viewBox = this.canvas._viewBox;
|
|
var scrollOffset = geom.transform().translate(-viewBox.x, -viewBox.y);
|
|
var viewRect = new geom.Rect([
|
|
0,
|
|
0
|
|
], [
|
|
viewBox.width,
|
|
viewBox.height
|
|
]);
|
|
var clipPath = draw.Path.fromRect(viewRect);
|
|
var wrap = new draw.Group({ transform: scrollOffset });
|
|
var clipWrap = new draw.Group({ clip: clipPath });
|
|
var root = this.canvas.drawingElement.children[0];
|
|
clipWrap.append(wrap);
|
|
wrap.children.push(root);
|
|
return clipWrap;
|
|
},
|
|
exportVisual: function () {
|
|
var scale = geom.transform().scale(1 / this._zoom);
|
|
var wrap = new draw.Group({ transform: scale });
|
|
var root = this.mainLayer.drawingElement;
|
|
wrap.children.push(root);
|
|
return wrap;
|
|
},
|
|
_syncChanges: function () {
|
|
this._syncShapeChanges();
|
|
this._syncConnectionChanges();
|
|
},
|
|
_syncShapeChanges: function () {
|
|
if (this.dataSource && this._isEditable) {
|
|
this.dataSource.sync();
|
|
}
|
|
},
|
|
_syncConnectionChanges: function () {
|
|
var that = this;
|
|
if (that.connectionsDataSource && that._isEditable) {
|
|
$.when.apply($, that._deferredConnectionUpdates).then(function () {
|
|
that.connectionsDataSource.sync();
|
|
});
|
|
that.deferredConnectionUpdates = [];
|
|
}
|
|
}
|
|
});
|
|
dataviz.ExportMixin.extend(Diagram.fn, true);
|
|
if (kendo.PDFMixin) {
|
|
kendo.PDFMixin.extend(Diagram.fn);
|
|
}
|
|
function filterShapeDataItem(dataItem) {
|
|
var result = {};
|
|
dataItem = dataItem || {};
|
|
if (defined(dataItem.text) && dataItem.text !== null) {
|
|
result.text = dataItem.text;
|
|
}
|
|
if (defined(dataItem.x) && dataItem.x !== null) {
|
|
result.x = dataItem.x;
|
|
}
|
|
if (defined(dataItem.y) && dataItem.y !== null) {
|
|
result.y = dataItem.y;
|
|
}
|
|
if (defined(dataItem.width) && dataItem.width !== null) {
|
|
result.width = dataItem.width;
|
|
}
|
|
if (defined(dataItem.height) && dataItem.height !== null) {
|
|
result.height = dataItem.height;
|
|
}
|
|
if (defined(dataItem.type) && dataItem.type !== null) {
|
|
result.type = dataItem.type;
|
|
}
|
|
return result;
|
|
}
|
|
function filterConnectionDataItem(dataItem) {
|
|
var result = {};
|
|
dataItem = dataItem || {};
|
|
if (defined(dataItem.text) && dataItem.text !== null) {
|
|
result.content = dataItem.text;
|
|
}
|
|
if (defined(dataItem.type) && dataItem.type !== null) {
|
|
result.type = dataItem.type;
|
|
}
|
|
if (defined(dataItem.from) && dataItem.from !== null) {
|
|
result.from = dataItem.from;
|
|
}
|
|
if (defined(dataItem.fromConnector) && dataItem.fromConnector !== null) {
|
|
result.fromConnector = dataItem.fromConnector;
|
|
}
|
|
if (defined(dataItem.fromX) && dataItem.fromX !== null) {
|
|
result.fromX = dataItem.fromX;
|
|
}
|
|
if (defined(dataItem.fromY) && dataItem.fromY !== null) {
|
|
result.fromY = dataItem.fromY;
|
|
}
|
|
if (defined(dataItem.to) && dataItem.to !== null) {
|
|
result.to = dataItem.to;
|
|
}
|
|
if (defined(dataItem.toConnector) && dataItem.toConnector !== null) {
|
|
result.toConnector = dataItem.toConnector;
|
|
}
|
|
if (defined(dataItem.toX) && dataItem.toX !== null) {
|
|
result.toX = dataItem.toX;
|
|
}
|
|
if (defined(dataItem.toY) && dataItem.toY !== null) {
|
|
result.toY = dataItem.toY;
|
|
}
|
|
return result;
|
|
}
|
|
var DiagramToolBar = kendo.Observable.extend({
|
|
init: function (diagram, options) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.diagram = diagram;
|
|
this.options = deepExtend({}, this.options, options);
|
|
this._tools = [];
|
|
this.createToolBar();
|
|
this.createTools();
|
|
this.appendTools();
|
|
if (this.options.modal) {
|
|
this.createPopup();
|
|
}
|
|
this.bind(this.events, options);
|
|
},
|
|
events: ['click'],
|
|
createPopup: function () {
|
|
this.container = $('<div/>').append(this.element);
|
|
this._popup = this.container.kendoPopup({}).getKendoPopup();
|
|
},
|
|
appendTools: function () {
|
|
for (var i = 0; i < this._tools.length; i++) {
|
|
var tool = this._tools[i];
|
|
if (tool.buttons && tool.buttons.length || !defined(tool.buttons)) {
|
|
this._toolBar.add(tool);
|
|
}
|
|
}
|
|
},
|
|
createToolBar: function () {
|
|
this.element = $('<div/>');
|
|
this._toolBar = this.element.kendoToolBar({
|
|
click: proxy(this.click, this),
|
|
resizable: false
|
|
}).getKendoToolBar();
|
|
this.element.css('border', 'none');
|
|
},
|
|
createTools: function () {
|
|
for (var i = 0; i < this.options.tools.length; i++) {
|
|
this.createTool(this.options.tools[i]);
|
|
}
|
|
},
|
|
createTool: function (tool) {
|
|
if (!isPlainObject(tool)) {
|
|
tool = { name: tool };
|
|
}
|
|
var toolName = tool.name + 'Tool';
|
|
if (this[toolName]) {
|
|
this[toolName](tool);
|
|
} else {
|
|
this._tools.push(deepExtend({}, tool, { attributes: this._setAttributes({ action: tool.name }) }));
|
|
}
|
|
},
|
|
showAt: function (point) {
|
|
if (this._popup) {
|
|
this._popup.open(point.x, point.y);
|
|
}
|
|
},
|
|
hide: function () {
|
|
if (this._popup) {
|
|
this._popup.close();
|
|
}
|
|
},
|
|
newGroup: function () {
|
|
return {
|
|
type: 'buttonGroup',
|
|
buttons: []
|
|
};
|
|
},
|
|
editTool: function () {
|
|
this._tools.push({
|
|
spriteCssClass: 'k-icon k-i-pencil',
|
|
showText: 'overflow',
|
|
type: 'button',
|
|
text: 'Edit',
|
|
attributes: this._setAttributes({ action: 'edit' })
|
|
});
|
|
},
|
|
deleteTool: function () {
|
|
this._tools.push({
|
|
spriteCssClass: 'k-icon k-i-close',
|
|
showText: 'overflow',
|
|
type: 'button',
|
|
text: 'Delete',
|
|
attributes: this._setAttributes({ action: 'delete' })
|
|
});
|
|
},
|
|
rotateAnticlockwiseTool: function (options) {
|
|
this._appendGroup('rotate');
|
|
this._rotateGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-rotateccw',
|
|
showText: 'overflow',
|
|
text: 'RotateAnticlockwise',
|
|
group: 'rotate',
|
|
attributes: this._setAttributes({
|
|
action: 'rotateAnticlockwise',
|
|
step: options.step
|
|
})
|
|
});
|
|
},
|
|
rotateClockwiseTool: function (options) {
|
|
this._appendGroup('rotate');
|
|
this._rotateGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-rotatecw',
|
|
attributes: this._setAttributes({
|
|
action: 'rotateClockwise',
|
|
step: options.step
|
|
}),
|
|
showText: 'overflow',
|
|
text: 'RotateClockwise',
|
|
group: 'rotate'
|
|
});
|
|
},
|
|
createShapeTool: function () {
|
|
this._appendGroup('create');
|
|
this._createGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-shape',
|
|
showText: 'overflow',
|
|
text: 'CreateShape',
|
|
group: 'create',
|
|
attributes: this._setAttributes({ action: 'createShape' })
|
|
});
|
|
},
|
|
createConnectionTool: function () {
|
|
this._appendGroup('create');
|
|
this._createGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-connector',
|
|
showText: 'overflow',
|
|
text: 'CreateConnection',
|
|
group: 'create',
|
|
attributes: this._setAttributes({ action: 'createConnection' })
|
|
});
|
|
},
|
|
undoTool: function () {
|
|
this._appendGroup('history');
|
|
this._historyGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-undo',
|
|
showText: 'overflow',
|
|
text: 'Undo',
|
|
group: 'history',
|
|
attributes: this._setAttributes({ action: 'undo' })
|
|
});
|
|
},
|
|
redoTool: function () {
|
|
this._appendGroup('history');
|
|
this._historyGroup.buttons.push({
|
|
spriteCssClass: 'k-icon k-i-redo',
|
|
showText: 'overflow',
|
|
text: 'Redo',
|
|
group: 'history',
|
|
attributes: this._setAttributes({ action: 'redo' })
|
|
});
|
|
},
|
|
_appendGroup: function (name) {
|
|
var prop = '_' + name + 'Group';
|
|
if (!this[prop]) {
|
|
this[prop] = this.newGroup();
|
|
this._tools.push(this[prop]);
|
|
}
|
|
},
|
|
_setAttributes: function (attributes) {
|
|
var attr = {};
|
|
if (attributes.action) {
|
|
attr[kendo.attr('action')] = attributes.action;
|
|
}
|
|
if (attributes.step) {
|
|
attr[kendo.attr('step')] = attributes.step;
|
|
}
|
|
return attr;
|
|
},
|
|
_getAttributes: function (element) {
|
|
var attr = {};
|
|
var action = element.attr(kendo.attr('action'));
|
|
if (action) {
|
|
attr.action = action;
|
|
}
|
|
var step = element.attr(kendo.attr('step'));
|
|
if (step) {
|
|
attr.step = step;
|
|
}
|
|
return attr;
|
|
},
|
|
click: function (e) {
|
|
var attributes = this._getAttributes($(e.target));
|
|
var action = attributes.action;
|
|
if (action && this[action]) {
|
|
this[action](attributes);
|
|
}
|
|
this.trigger('click', this.eventData(action, e.target));
|
|
},
|
|
eventData: function (action, target) {
|
|
var elements = this.selectedElements(), length = elements.length, shapes = [], connections = [], element;
|
|
for (var idx = 0; idx < length; idx++) {
|
|
element = elements[idx];
|
|
if (element instanceof Shape) {
|
|
shapes.push(element);
|
|
} else {
|
|
connections.push(element);
|
|
}
|
|
}
|
|
return {
|
|
shapes: shapes,
|
|
connections: connections,
|
|
action: action,
|
|
target: target
|
|
};
|
|
},
|
|
'delete': function () {
|
|
var diagram = this.diagram;
|
|
var toRemove = diagram._triggerRemove(this.selectedElements());
|
|
if (toRemove.length) {
|
|
this.diagram.remove(toRemove, true);
|
|
this.diagram._syncChanges();
|
|
}
|
|
},
|
|
edit: function () {
|
|
this.diagram.edit(this.selectedElements()[0]);
|
|
},
|
|
rotateClockwise: function (options) {
|
|
var angle = parseFloat(options.step || 90);
|
|
this._rotate(angle);
|
|
},
|
|
rotateAnticlockwise: function (options) {
|
|
var angle = parseFloat(options.step || 90);
|
|
this._rotate(-angle);
|
|
},
|
|
_rotate: function (angle) {
|
|
var adorner = this.diagram._resizingAdorner;
|
|
adorner.angle(adorner.angle() + angle);
|
|
adorner.rotate();
|
|
},
|
|
selectedElements: function () {
|
|
return this.diagram.select();
|
|
},
|
|
createShape: function () {
|
|
this.diagram.createShape();
|
|
},
|
|
createConnection: function () {
|
|
this.diagram.createConnection();
|
|
},
|
|
undo: function () {
|
|
this.diagram.undo();
|
|
},
|
|
redo: function () {
|
|
this.diagram.redo();
|
|
},
|
|
destroy: function () {
|
|
this.diagram = null;
|
|
this.element = null;
|
|
this.options = null;
|
|
if (this._toolBar) {
|
|
this._toolBar.destroy();
|
|
}
|
|
if (this._popup) {
|
|
this._popup.destroy();
|
|
}
|
|
}
|
|
});
|
|
var Editor = kendo.Observable.extend({
|
|
init: function (element, options) {
|
|
kendo.Observable.fn.init.call(this);
|
|
this.options = extend(true, {}, this.options, options);
|
|
this.element = element;
|
|
this.model = this.options.model;
|
|
this.fields = this._getFields();
|
|
this._initContainer();
|
|
this.createEditable();
|
|
},
|
|
options: { editors: {} },
|
|
_initContainer: function () {
|
|
this.wrapper = this.element;
|
|
},
|
|
createEditable: function () {
|
|
var options = this.options;
|
|
this.editable = new kendo.ui.Editable(this.wrapper, {
|
|
fields: this.fields,
|
|
target: options.target,
|
|
clearContainer: false,
|
|
model: this.model
|
|
});
|
|
},
|
|
_isEditable: function (field) {
|
|
return this.model.editable && this.model.editable(field);
|
|
},
|
|
_getFields: function () {
|
|
var fields = [];
|
|
var modelFields = this.model.fields;
|
|
for (var field in modelFields) {
|
|
var result = {};
|
|
if (this._isEditable(field)) {
|
|
var editor = this.options.editors[field];
|
|
if (editor) {
|
|
result.editor = editor;
|
|
}
|
|
result.field = field;
|
|
fields.push(result);
|
|
}
|
|
}
|
|
return fields;
|
|
},
|
|
end: function () {
|
|
return this.editable.end();
|
|
},
|
|
destroy: function () {
|
|
this.editable.destroy();
|
|
this.editable.element.find('[' + kendo.attr('container-for') + ']').empty();
|
|
this.model = this.wrapper = this.element = this.columns = this.editable = null;
|
|
}
|
|
});
|
|
var PopupEditor = Editor.extend({
|
|
init: function (element, options) {
|
|
Editor.fn.init.call(this, element, options);
|
|
this.bind(this.events, this.options);
|
|
this.open();
|
|
},
|
|
events: [
|
|
'update',
|
|
'cancel'
|
|
],
|
|
options: {
|
|
window: {
|
|
modal: true,
|
|
resizable: false,
|
|
draggable: true,
|
|
title: 'Edit',
|
|
visible: false
|
|
}
|
|
},
|
|
_initContainer: function () {
|
|
var that = this;
|
|
this.wrapper = $('<div class="k-popup-edit-form"/>').attr(kendo.attr('uid'), this.model.uid);
|
|
var formContent = '';
|
|
if (this.options.template) {
|
|
formContent += this._renderTemplate();
|
|
this.fields = [];
|
|
} else {
|
|
formContent += this._renderFields();
|
|
}
|
|
formContent += this._renderButtons();
|
|
this.wrapper.append($('<div class="k-edit-form-container"/>').append(formContent));
|
|
this.window = new kendo.ui.Window(this.wrapper.appendTo(this.element), this.options.window);
|
|
this.window.bind('close', function (e) {
|
|
if (e.userTriggered) {
|
|
e.sender.element.focus();
|
|
that._cancelClick(e);
|
|
}
|
|
});
|
|
this._attachButtonEvents();
|
|
},
|
|
_renderTemplate: function () {
|
|
var template = this.options.template;
|
|
if (typeof template === 'string') {
|
|
template = window.unescape(template);
|
|
}
|
|
template = kendo.template(template)(this.model);
|
|
return template;
|
|
},
|
|
_renderFields: function () {
|
|
var form = '';
|
|
for (var i = 0; i < this.fields.length; i++) {
|
|
var field = this.fields[i];
|
|
form += '<div class="k-edit-label"><label for="' + field.field + '">' + (field.field || '') + '</label></div>';
|
|
if (this._isEditable(field.field)) {
|
|
form += '<div ' + kendo.attr('container-for') + '="' + field.field + '" class="k-edit-field"></div>';
|
|
}
|
|
}
|
|
return form;
|
|
},
|
|
_renderButtons: function () {
|
|
var form = '<div class="k-edit-buttons k-state-default">';
|
|
form += this._createButton('update');
|
|
form += this._createButton('cancel');
|
|
form += '</div>';
|
|
return form;
|
|
},
|
|
_createButton: function (name) {
|
|
return kendo.template(BUTTON_TEMPLATE)(defaultButtons[name]);
|
|
},
|
|
_attachButtonEvents: function () {
|
|
this._cancelClickHandler = proxy(this._cancelClick, this);
|
|
this.window.element.on(CLICK + NS, 'a.k-diagram-cancel', this._cancelClickHandler);
|
|
this._updateClickHandler = proxy(this._updateClick, this);
|
|
this.window.element.on(CLICK + NS, 'a.k-diagram-update', this._updateClickHandler);
|
|
},
|
|
_updateClick: function (e) {
|
|
e.preventDefault();
|
|
this.trigger('update');
|
|
},
|
|
_cancelClick: function (e) {
|
|
e.preventDefault();
|
|
this.trigger('cancel');
|
|
},
|
|
open: function () {
|
|
this.window.center().open();
|
|
},
|
|
close: function () {
|
|
this.window.bind('deactivate', proxy(this.destroy, this)).close();
|
|
},
|
|
destroy: function () {
|
|
this.window.close().destroy();
|
|
this.window.element.off(CLICK + NS, 'a.k-diagram-cancel', this._cancelClickHandler);
|
|
this.window.element.off(CLICK + NS, 'a.k-diagram-update', this._updateClickHandler);
|
|
this._cancelClickHandler = null;
|
|
this._editUpdateClickHandler = null;
|
|
this.window = null;
|
|
Editor.fn.destroy.call(this);
|
|
}
|
|
});
|
|
function connectionSelector(container, options) {
|
|
var model = this.dataSource.reader.model;
|
|
if (model) {
|
|
var textField = model.fn.fields.text ? 'text' : model.idField;
|
|
$('<input name=\'' + options.field + '\' />').appendTo(container).kendoDropDownList({
|
|
dataValueField: model.idField,
|
|
dataTextField: textField,
|
|
dataSource: this.dataSource.data().toJSON(),
|
|
optionLabel: ' ',
|
|
valuePrimitive: true
|
|
});
|
|
}
|
|
}
|
|
function InactiveItem(dataItem) {
|
|
this.dataItem = dataItem;
|
|
this.callbacks = [];
|
|
}
|
|
InactiveItem.fn = InactiveItem.prototype = {
|
|
onActivate: function (callback) {
|
|
var deffered = $.Deferred();
|
|
this.callbacks.push({
|
|
callback: callback,
|
|
deferred: deffered
|
|
});
|
|
return deffered;
|
|
},
|
|
activate: function () {
|
|
var callbacks = this.callbacks;
|
|
var item;
|
|
for (var idx = 0; idx < callbacks.length; idx++) {
|
|
item = this.callbacks[idx];
|
|
item.callback(this.dataItem);
|
|
item.deferred.resolve();
|
|
}
|
|
this.callbacks = [];
|
|
}
|
|
};
|
|
function InactiveItemsCollection() {
|
|
this.items = {};
|
|
}
|
|
InactiveItemsCollection.fn = InactiveItemsCollection.prototype = {
|
|
add: function (items) {
|
|
for (var idx = 0; idx < items.length; idx++) {
|
|
this.items[items[idx].uid] = new InactiveItem(items[idx]);
|
|
}
|
|
},
|
|
forEach: function (callback) {
|
|
for (var uid in this.items) {
|
|
callback(this.items[uid]);
|
|
}
|
|
},
|
|
getByUid: function (uid) {
|
|
return this.items[uid];
|
|
},
|
|
remove: function (item) {
|
|
delete this.items[item.uid];
|
|
}
|
|
};
|
|
var QuadRoot = Class.extend({
|
|
init: function () {
|
|
this.shapes = [];
|
|
},
|
|
_add: function (shape, bounds) {
|
|
this.shapes.push({
|
|
bounds: bounds,
|
|
shape: shape
|
|
});
|
|
shape._quadNode = this;
|
|
},
|
|
insert: function (shape, bounds) {
|
|
this._add(shape, bounds);
|
|
},
|
|
remove: function (shape) {
|
|
var shapes = this.shapes;
|
|
var length = shapes.length;
|
|
for (var idx = 0; idx < length; idx++) {
|
|
if (shapes[idx].shape === shape) {
|
|
shapes.splice(idx, 1);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
hitTestRect: function (rect) {
|
|
var shapes = this.shapes;
|
|
var length = shapes.length;
|
|
for (var i = 0; i < length; i++) {
|
|
if (this._testRect(shapes[i].shape, rect)) {
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
_testRect: function (shape, rect) {
|
|
var angle = shape.rotate().angle;
|
|
var bounds = shape.bounds();
|
|
var hit;
|
|
if (!angle) {
|
|
hit = bounds.overlaps(rect);
|
|
} else {
|
|
hit = Intersect.rects(rect, bounds, -angle);
|
|
}
|
|
return hit;
|
|
}
|
|
});
|
|
var QuadNode = QuadRoot.extend({
|
|
init: function (rect) {
|
|
QuadRoot.fn.init.call(this);
|
|
this.children = [];
|
|
this.rect = rect;
|
|
},
|
|
inBounds: function (rect) {
|
|
var nodeRect = this.rect;
|
|
var nodeBottomRight = nodeRect.bottomRight();
|
|
var bottomRight = rect.bottomRight();
|
|
var inBounds = nodeRect.x <= rect.x && nodeRect.y <= rect.y && bottomRight.x <= nodeBottomRight.x && bottomRight.y <= nodeBottomRight.y;
|
|
return inBounds;
|
|
},
|
|
overlapsBounds: function (rect) {
|
|
return this.rect.overlaps(rect);
|
|
},
|
|
insert: function (shape, bounds) {
|
|
var inserted = false;
|
|
var children = this.children;
|
|
var length = children.length;
|
|
if (this.inBounds(bounds)) {
|
|
if (!length && this.shapes.length < 4) {
|
|
this._add(shape, bounds);
|
|
} else {
|
|
if (!length) {
|
|
this._initChildren();
|
|
}
|
|
for (var idx = 0; idx < children.length; idx++) {
|
|
if (children[idx].insert(shape, bounds)) {
|
|
inserted = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!inserted) {
|
|
this._add(shape, bounds);
|
|
}
|
|
}
|
|
inserted = true;
|
|
}
|
|
return inserted;
|
|
},
|
|
_initChildren: function () {
|
|
var rect = this.rect, children = this.children, shapes = this.shapes, center = rect.center(), halfWidth = rect.width / 2, halfHeight = rect.height / 2, childIdx, shapeIdx;
|
|
children.push(new QuadNode(new Rect(rect.x, rect.y, halfWidth, halfHeight)), new QuadNode(new Rect(center.x, rect.y, halfWidth, halfHeight)), new QuadNode(new Rect(rect.x, center.y, halfWidth, halfHeight)), new QuadNode(new Rect(center.x, center.y, halfWidth, halfHeight)));
|
|
for (shapeIdx = shapes.length - 1; shapeIdx >= 0; shapeIdx--) {
|
|
for (childIdx = 0; childIdx < children.length; childIdx++) {
|
|
if (children[childIdx].insert(shapes[shapeIdx].shape, shapes[shapeIdx].bounds)) {
|
|
shapes.splice(shapeIdx, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
hitTestRect: function (rect) {
|
|
var idx;
|
|
var children = this.children;
|
|
var length = children.length;
|
|
var hit = false;
|
|
if (this.overlapsBounds(rect)) {
|
|
if (QuadRoot.fn.hitTestRect.call(this, rect)) {
|
|
hit = true;
|
|
} else {
|
|
for (idx = 0; idx < length; idx++) {
|
|
if (children[idx].hitTestRect(rect)) {
|
|
hit = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hit;
|
|
}
|
|
});
|
|
var ShapesQuadTree = Class.extend({
|
|
ROOT_SIZE: 1000,
|
|
init: function (diagram) {
|
|
var boundsChangeHandler = proxy(this._boundsChange, this);
|
|
diagram.bind(ITEMBOUNDSCHANGE, boundsChangeHandler);
|
|
diagram.bind(ITEMROTATE, boundsChangeHandler);
|
|
this.initRoots();
|
|
},
|
|
initRoots: function () {
|
|
this.rootMap = {};
|
|
this.root = new QuadRoot();
|
|
},
|
|
clear: function () {
|
|
this.initRoots();
|
|
},
|
|
_boundsChange: function (e) {
|
|
if (e.item._quadNode) {
|
|
e.item._quadNode.remove(e.item);
|
|
}
|
|
this.insert(e.item);
|
|
},
|
|
insert: function (shape) {
|
|
var bounds = shape.bounds(ROTATED);
|
|
var rootSize = this.ROOT_SIZE;
|
|
var sectors = this.getSectors(bounds);
|
|
var x = sectors[0][0];
|
|
var y = sectors[1][0];
|
|
if (this.inRoot(sectors)) {
|
|
this.root.insert(shape, bounds);
|
|
} else {
|
|
if (!this.rootMap[x]) {
|
|
this.rootMap[x] = {};
|
|
}
|
|
if (!this.rootMap[x][y]) {
|
|
this.rootMap[x][y] = new QuadNode(new Rect(x * rootSize, y * rootSize, rootSize, rootSize));
|
|
}
|
|
this.rootMap[x][y].insert(shape, bounds);
|
|
}
|
|
},
|
|
remove: function (shape) {
|
|
if (shape._quadNode) {
|
|
shape._quadNode.remove(shape);
|
|
}
|
|
},
|
|
inRoot: function (sectors) {
|
|
return sectors[0].length > 1 || sectors[1].length > 1;
|
|
},
|
|
getSectors: function (rect) {
|
|
var rootSize = this.ROOT_SIZE;
|
|
var bottomRight = rect.bottomRight();
|
|
var bottomX = math.floor(bottomRight.x / rootSize);
|
|
var bottomY = math.floor(bottomRight.y / rootSize);
|
|
var sectors = [
|
|
[],
|
|
[]
|
|
];
|
|
for (var x = math.floor(rect.x / rootSize); x <= bottomX; x++) {
|
|
sectors[0].push(x);
|
|
}
|
|
for (var y = math.floor(rect.y / rootSize); y <= bottomY; y++) {
|
|
sectors[1].push(y);
|
|
}
|
|
return sectors;
|
|
},
|
|
hitTestRect: function (rect) {
|
|
var sectors = this.getSectors(rect);
|
|
var xIdx, yIdx, x, y;
|
|
var root;
|
|
if (this.root.hitTestRect(rect)) {
|
|
return true;
|
|
}
|
|
for (xIdx = 0; xIdx < sectors[0].length; xIdx++) {
|
|
x = sectors[0][xIdx];
|
|
for (yIdx = 0; yIdx < sectors[1].length; yIdx++) {
|
|
y = sectors[1][yIdx];
|
|
root = (this.rootMap[x] || {})[y];
|
|
if (root && root.hitTestRect(rect)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
function cloneDataItem(dataItem) {
|
|
var result = dataItem;
|
|
if (dataItem instanceof kendo.data.Model) {
|
|
result = dataItem.toJSON();
|
|
result[dataItem.idField] = dataItem._defaultId;
|
|
}
|
|
return result;
|
|
}
|
|
function splitDiagramElements(elements) {
|
|
var connections = [];
|
|
var shapes = [];
|
|
var element, idx;
|
|
for (idx = 0; idx < elements.length; idx++) {
|
|
element = elements[idx];
|
|
if (element instanceof Shape) {
|
|
shapes.push(element);
|
|
} else {
|
|
connections.push(element);
|
|
}
|
|
}
|
|
return {
|
|
shapes: shapes,
|
|
connections: connections
|
|
};
|
|
}
|
|
function createModel(dataSource, model) {
|
|
if (dataSource.reader.model) {
|
|
return new dataSource.reader.model(model);
|
|
}
|
|
return new kendo.data.ObservableObject(model);
|
|
}
|
|
function clearField(field, model) {
|
|
if (defined(model[field])) {
|
|
model.set(field, null);
|
|
}
|
|
}
|
|
function copyDefaultOptions(mainOptions, elementOptions, fields) {
|
|
var field;
|
|
for (var idx = 0; idx < fields.length; idx++) {
|
|
field = fields[idx];
|
|
if (elementOptions && !defined(elementOptions[field])) {
|
|
elementOptions[field] = mainOptions[field];
|
|
}
|
|
}
|
|
}
|
|
function translateToOrigin(visual) {
|
|
var bbox = visual.drawingContainer().clippedBBox(null);
|
|
if (bbox.origin.x !== 0 || bbox.origin.y !== 0) {
|
|
visual.position(-bbox.origin.x, -bbox.origin.y);
|
|
}
|
|
}
|
|
dataviz.ui.plugin(Diagram);
|
|
deepExtend(diagram, {
|
|
Shape: Shape,
|
|
Connection: Connection,
|
|
Connector: Connector,
|
|
DiagramToolBar: DiagramToolBar,
|
|
QuadNode: QuadNode,
|
|
QuadRoot: QuadRoot,
|
|
ShapesQuadTree: ShapesQuadTree,
|
|
PopupEditor: PopupEditor
|
|
});
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.diagram', [
|
|
'kendo.data',
|
|
'kendo.draganddrop',
|
|
'kendo.userevents',
|
|
'kendo.mobile.scroller',
|
|
'kendo.drawing',
|
|
'dataviz/diagram/utils',
|
|
'dataviz/diagram/math',
|
|
'dataviz/diagram/svg',
|
|
'dataviz/diagram/services',
|
|
'dataviz/diagram/layout',
|
|
'dataviz/diagram/dom'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.diagram',
|
|
name: 'Diagram',
|
|
category: 'dataviz',
|
|
description: 'The Kendo DataViz Diagram ',
|
|
depends: [
|
|
'data',
|
|
'userevents',
|
|
'mobile.scroller',
|
|
'draganddrop',
|
|
'drawing',
|
|
'dataviz.core',
|
|
'dataviz.themes',
|
|
'toolbar'
|
|
],
|
|
features: [
|
|
{
|
|
id: 'dataviz.diagram-pdf-export',
|
|
name: 'PDF export',
|
|
description: 'Export Diagram as PDF',
|
|
depends: ['pdf']
|
|
},
|
|
{
|
|
id: 'dataviz.diagram-editing',
|
|
name: 'Editing',
|
|
description: 'Support for model editing',
|
|
depends: [
|
|
'editable',
|
|
'window',
|
|
'dropdownlist'
|
|
]
|
|
}
|
|
]
|
|
};
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.treemap', [
|
|
'kendo.data',
|
|
'kendo.userevents',
|
|
'kendo.dataviz.themes'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.treeMap',
|
|
name: 'TreeMap',
|
|
category: 'dataviz',
|
|
description: 'The Kendo DataViz TreeMap',
|
|
depends: [
|
|
'data',
|
|
'userevents',
|
|
'dataviz.themes'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var math = Math, proxy = $.proxy, isArray = $.isArray, kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, template = kendo.template, deepExtend = kendo.deepExtend, HierarchicalDataSource = kendo.data.HierarchicalDataSource, getter = kendo.getter, dataviz = kendo.dataviz;
|
|
var NS = '.kendoTreeMap', CHANGE = 'change', DATA_BOUND = 'dataBound', ITEM_CREATED = 'itemCreated', MAX_VALUE = Number.MAX_VALUE, MOUSEOVER_NS = 'mouseover' + NS, MOUSELEAVE_NS = 'mouseleave' + NS, UNDEFINED = 'undefined';
|
|
var TreeMap = Widget.extend({
|
|
init: function (element, options) {
|
|
kendo.destroy(element);
|
|
$(element).empty();
|
|
Widget.fn.init.call(this, element, options);
|
|
this.wrapper = this.element;
|
|
this._initTheme(this.options);
|
|
this.element.addClass('k-widget k-treemap');
|
|
this._setLayout();
|
|
this._originalOptions = deepExtend({}, this.options);
|
|
this._initDataSource();
|
|
this._attachEvents();
|
|
kendo.notify(this, dataviz.ui);
|
|
},
|
|
options: {
|
|
name: 'TreeMap',
|
|
theme: 'default',
|
|
autoBind: true,
|
|
textField: 'text',
|
|
valueField: 'value',
|
|
colorField: 'color'
|
|
},
|
|
events: [
|
|
DATA_BOUND,
|
|
ITEM_CREATED
|
|
],
|
|
_initTheme: function (options) {
|
|
var that = this, themes = dataviz.ui.themes || {}, themeName = ((options || {}).theme || '').toLowerCase(), themeOptions = (themes[themeName] || {}).treeMap;
|
|
that.options = deepExtend({}, themeOptions, options);
|
|
},
|
|
_attachEvents: function () {
|
|
this.element.on(MOUSEOVER_NS, proxy(this._mouseover, this)).on(MOUSELEAVE_NS, proxy(this._mouseleave, this));
|
|
this._resizeHandler = proxy(this.resize, this, false);
|
|
kendo.onResize(this._resizeHandler);
|
|
},
|
|
_setLayout: function () {
|
|
if (this.options.type === 'horizontal') {
|
|
this._layout = new SliceAndDice(false);
|
|
this._view = new SliceAndDiceView(this, this.options);
|
|
} else if (this.options.type === 'vertical') {
|
|
this._layout = new SliceAndDice(true);
|
|
this._view = new SliceAndDiceView(this, this.options);
|
|
} else {
|
|
this._layout = new Squarified();
|
|
this._view = new SquarifiedView(this, this.options);
|
|
}
|
|
},
|
|
_initDataSource: function () {
|
|
var that = this, options = that.options, dataSource = options.dataSource;
|
|
that._dataChangeHandler = proxy(that._onDataChange, that);
|
|
that.dataSource = HierarchicalDataSource.create(dataSource).bind(CHANGE, that._dataChangeHandler);
|
|
if (dataSource) {
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
}
|
|
}
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var that = this;
|
|
that.dataSource.unbind(CHANGE, that._dataChangeHandler);
|
|
that.dataSource = dataSource.bind(CHANGE, that._dataChangeHandler);
|
|
if (dataSource) {
|
|
if (that.options.autoBind) {
|
|
that.dataSource.fetch();
|
|
}
|
|
}
|
|
},
|
|
_onDataChange: function (e) {
|
|
var node = e.node;
|
|
var items = e.items;
|
|
var options = this.options;
|
|
var item, i;
|
|
if (!node) {
|
|
this._cleanItems();
|
|
this.element.empty();
|
|
item = this._wrapItem(items[0]);
|
|
this._layout.createRoot(item, this.element.outerWidth(), this.element.outerHeight(), this.options.type === 'vertical');
|
|
this._view.createRoot(item);
|
|
this._root = item;
|
|
this._colorIdx = 0;
|
|
} else {
|
|
if (items.length) {
|
|
var root = this._getByUid(node.uid);
|
|
root.children = [];
|
|
items = new kendo.data.Query(items)._sortForGrouping(options.valueField, 'desc');
|
|
for (i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
root.children.push(this._wrapItem(item));
|
|
}
|
|
var htmlSize = this._view.htmlSize(root);
|
|
this._layout.compute(root.children, root.coord, htmlSize);
|
|
this._setColors(root.children);
|
|
this._view.render(root);
|
|
}
|
|
}
|
|
for (i = 0; i < items.length; i++) {
|
|
items[i].load();
|
|
}
|
|
if (node) {
|
|
this.trigger(DATA_BOUND, { node: node });
|
|
}
|
|
},
|
|
_cleanItems: function () {
|
|
var that = this;
|
|
that.angular('cleanup', function () {
|
|
return { elements: that.element.find('.k-leaf div,.k-treemap-title,.k-treemap-title-vertical') };
|
|
});
|
|
},
|
|
_setColors: function (items) {
|
|
var colors = this.options.colors;
|
|
var colorIdx = this._colorIdx;
|
|
var color = colors[colorIdx % colors.length];
|
|
var colorRange, item;
|
|
if (isArray(color)) {
|
|
colorRange = colorsByLength(color[0], color[1], items.length);
|
|
}
|
|
var leafNodes = false;
|
|
for (var i = 0; i < items.length; i++) {
|
|
item = items[i];
|
|
if (!defined(item.color)) {
|
|
if (colorRange) {
|
|
item.color = colorRange[i];
|
|
} else {
|
|
item.color = color;
|
|
}
|
|
}
|
|
if (!item.dataItem.hasChildren) {
|
|
leafNodes = true;
|
|
}
|
|
}
|
|
if (leafNodes) {
|
|
this._colorIdx++;
|
|
}
|
|
},
|
|
_contentSize: function (root) {
|
|
this.view.renderHeight(root);
|
|
},
|
|
_wrapItem: function (item) {
|
|
var wrap = {};
|
|
if (defined(this.options.valueField)) {
|
|
wrap.value = getField(this.options.valueField, item);
|
|
}
|
|
if (defined(this.options.colorField)) {
|
|
wrap.color = getField(this.options.colorField, item);
|
|
}
|
|
if (defined(this.options.textField)) {
|
|
wrap.text = getField(this.options.textField, item);
|
|
}
|
|
wrap.level = item.level();
|
|
wrap.dataItem = item;
|
|
return wrap;
|
|
},
|
|
_getByUid: function (uid) {
|
|
var items = [this._root];
|
|
var item;
|
|
while (items.length) {
|
|
item = items.pop();
|
|
if (item.dataItem.uid === uid) {
|
|
return item;
|
|
}
|
|
if (item.children) {
|
|
items = items.concat(item.children);
|
|
}
|
|
}
|
|
},
|
|
dataItem: function (node) {
|
|
var uid = $(node).attr(kendo.attr('uid')), dataSource = this.dataSource;
|
|
return dataSource && dataSource.getByUid(uid);
|
|
},
|
|
findByUid: function (uid) {
|
|
return this.element.find('.k-treemap-tile[' + kendo.attr('uid') + '=\'' + uid + '\']');
|
|
},
|
|
_mouseover: function (e) {
|
|
var target = $(e.target);
|
|
if (target.hasClass('k-leaf')) {
|
|
this._removeActiveState();
|
|
target.removeClass('k-state-hover').addClass('k-state-hover');
|
|
}
|
|
},
|
|
_removeActiveState: function () {
|
|
this.element.find('.k-state-hover').removeClass('k-state-hover');
|
|
},
|
|
_mouseleave: function () {
|
|
this._removeActiveState();
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.element.off(NS);
|
|
if (this.dataSource) {
|
|
this.dataSource.unbind(CHANGE, this._dataChangeHandler);
|
|
}
|
|
this._root = null;
|
|
kendo.unbindResize(this._resizeHandler);
|
|
kendo.destroy(this.element);
|
|
},
|
|
items: function () {
|
|
return $();
|
|
},
|
|
getSize: function () {
|
|
return kendo.dimensions(this.element);
|
|
},
|
|
_resize: function () {
|
|
var root = this._root;
|
|
if (root) {
|
|
var element = this.element;
|
|
var rootElement = element.children();
|
|
root.coord.width = element.outerWidth();
|
|
root.coord.height = element.outerHeight();
|
|
rootElement.css({
|
|
width: root.coord.width,
|
|
height: root.coord.height
|
|
});
|
|
this._resizeItems(root, rootElement);
|
|
}
|
|
},
|
|
_resizeItems: function (root, element) {
|
|
if (root.children && root.children.length) {
|
|
var elements = element.children('.k-treemap-wrap').children();
|
|
var child, childElement;
|
|
this._layout.compute(root.children, root.coord, { text: this._view.titleSize(root, element) });
|
|
for (var idx = 0; idx < root.children.length; idx++) {
|
|
child = root.children[idx];
|
|
childElement = elements.filter('[' + kendo.attr('uid') + '=\'' + child.dataItem.uid + '\']');
|
|
this._view.setItemSize(child, childElement);
|
|
this._resizeItems(child, childElement);
|
|
}
|
|
}
|
|
},
|
|
setOptions: function (options) {
|
|
var dataSource = options.dataSource;
|
|
options.dataSource = undefined;
|
|
this._originalOptions = deepExtend(this._originalOptions, options);
|
|
this.options = deepExtend({}, this._originalOptions);
|
|
this._setLayout();
|
|
this._initTheme(this.options);
|
|
Widget.fn._setEvents.call(this, options);
|
|
if (dataSource) {
|
|
this.setDataSource(HierarchicalDataSource.create(dataSource));
|
|
}
|
|
if (this.options.autoBind) {
|
|
this.dataSource.fetch();
|
|
}
|
|
}
|
|
});
|
|
var Squarified = Class.extend({
|
|
createRoot: function (root, width, height) {
|
|
root.coord = {
|
|
width: width,
|
|
height: height,
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
},
|
|
leaf: function (tree) {
|
|
return !tree.children;
|
|
},
|
|
layoutChildren: function (items, coord) {
|
|
var parentArea = coord.width * coord.height;
|
|
var totalArea = 0, itemsArea = [], i;
|
|
for (i = 0; i < items.length; i++) {
|
|
itemsArea[i] = parseFloat(items[i].value);
|
|
totalArea += itemsArea[i];
|
|
}
|
|
for (i = 0; i < itemsArea.length; i++) {
|
|
items[i].area = parentArea * itemsArea[i] / totalArea;
|
|
}
|
|
var minimumSideValue = this.layoutHorizontal() ? coord.height : coord.width;
|
|
var firstElement = [items[0]];
|
|
var tail = items.slice(1);
|
|
this.squarify(tail, firstElement, minimumSideValue, coord);
|
|
},
|
|
squarify: function (tail, initElement, width, coord) {
|
|
this.computeDim(tail, initElement, width, coord);
|
|
},
|
|
computeDim: function (tail, initElement, width, coord) {
|
|
if (tail.length + initElement.length == 1) {
|
|
var element = tail.length == 1 ? tail : initElement;
|
|
this.layoutLast(element, width, coord);
|
|
return;
|
|
}
|
|
if (tail.length >= 2 && initElement.length === 0) {
|
|
initElement = [tail[0]];
|
|
tail = tail.slice(1);
|
|
}
|
|
if (tail.length === 0) {
|
|
if (initElement.length > 0) {
|
|
this.layoutRow(initElement, width, coord);
|
|
}
|
|
return;
|
|
}
|
|
var firstElement = tail[0];
|
|
if (this.worstAspectRatio(initElement, width) >= this.worstAspectRatio([firstElement].concat(initElement), width)) {
|
|
this.computeDim(tail.slice(1), initElement.concat([firstElement]), width, coord);
|
|
} else {
|
|
var newCoords = this.layoutRow(initElement, width, coord);
|
|
this.computeDim(tail, [], newCoords.dim, newCoords);
|
|
}
|
|
},
|
|
layoutLast: function (items, w, coord) {
|
|
items[0].coord = coord;
|
|
},
|
|
layoutRow: function (items, width, coord) {
|
|
if (this.layoutHorizontal()) {
|
|
return this.layoutV(items, width, coord);
|
|
} else {
|
|
return this.layoutH(items, width, coord);
|
|
}
|
|
},
|
|
orientation: 'h',
|
|
layoutVertical: function () {
|
|
return this.orientation === 'v';
|
|
},
|
|
layoutHorizontal: function () {
|
|
return this.orientation === 'h';
|
|
},
|
|
layoutChange: function () {
|
|
this.orientation = this.layoutVertical() ? 'h' : 'v';
|
|
},
|
|
worstAspectRatio: function (items, width) {
|
|
if (!items || items.length === 0) {
|
|
return MAX_VALUE;
|
|
}
|
|
var areaSum = 0, maxArea = 0, minArea = MAX_VALUE;
|
|
for (var i = 0; i < items.length; i++) {
|
|
var area = items[i].area;
|
|
areaSum += area;
|
|
minArea = minArea < area ? minArea : area;
|
|
maxArea = maxArea > area ? maxArea : area;
|
|
}
|
|
return math.max(width * width * maxArea / (areaSum * areaSum), areaSum * areaSum / (width * width * minArea));
|
|
},
|
|
compute: function (children, rootCoord, htmlSize) {
|
|
if (!(rootCoord.width >= rootCoord.height && this.layoutHorizontal())) {
|
|
this.layoutChange();
|
|
}
|
|
if (children && children.length > 0) {
|
|
var newRootCoord = {
|
|
width: rootCoord.width,
|
|
height: rootCoord.height - htmlSize.text,
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
this.layoutChildren(children, newRootCoord);
|
|
}
|
|
},
|
|
layoutV: function (items, width, coord) {
|
|
var totalArea = this._totalArea(items), top = 0;
|
|
width = round(totalArea / width);
|
|
for (var i = 0; i < items.length; i++) {
|
|
var height = round(items[i].area / width);
|
|
items[i].coord = {
|
|
height: height,
|
|
width: width,
|
|
top: coord.top + top,
|
|
left: coord.left
|
|
};
|
|
top += height;
|
|
}
|
|
var ans = {
|
|
height: coord.height,
|
|
width: coord.width - width,
|
|
top: coord.top,
|
|
left: coord.left + width
|
|
};
|
|
ans.dim = math.min(ans.width, ans.height);
|
|
if (ans.dim != ans.height) {
|
|
this.layoutChange();
|
|
}
|
|
return ans;
|
|
},
|
|
layoutH: function (items, width, coord) {
|
|
var totalArea = this._totalArea(items);
|
|
var height = round(totalArea / width), top = coord.top, left = 0;
|
|
for (var i = 0; i < items.length; i++) {
|
|
items[i].coord = {
|
|
height: height,
|
|
width: round(items[i].area / height),
|
|
top: top,
|
|
left: coord.left + left
|
|
};
|
|
left += items[i].coord.width;
|
|
}
|
|
var ans = {
|
|
height: coord.height - height,
|
|
width: coord.width,
|
|
top: coord.top + height,
|
|
left: coord.left
|
|
};
|
|
ans.dim = math.min(ans.width, ans.height);
|
|
if (ans.dim != ans.width) {
|
|
this.layoutChange();
|
|
}
|
|
return ans;
|
|
},
|
|
_totalArea: function (items) {
|
|
var total = 0;
|
|
for (var i = 0; i < items.length; i++) {
|
|
total += items[i].area;
|
|
}
|
|
return total;
|
|
}
|
|
});
|
|
var SquarifiedView = Class.extend({
|
|
init: function (treeMap, options) {
|
|
this.options = deepExtend({}, this.options, options);
|
|
this.treeMap = treeMap;
|
|
this.element = $(treeMap.element);
|
|
this.offset = 0;
|
|
},
|
|
titleSize: function (item, element) {
|
|
var text = element.children('.k-treemap-title');
|
|
return text.height();
|
|
},
|
|
htmlSize: function (root) {
|
|
var rootElement = this._getByUid(root.dataItem.uid);
|
|
var htmlSize = { text: 0 };
|
|
if (root.children) {
|
|
this._clean(rootElement);
|
|
var text = this._getText(root);
|
|
if (text) {
|
|
var title = this._createTitle(root);
|
|
rootElement.append(title);
|
|
this._compile(title, root.dataItem);
|
|
htmlSize.text = title.height();
|
|
}
|
|
rootElement.append(this._createWrap());
|
|
this.offset = (rootElement.outerWidth() - rootElement.innerWidth()) / 2;
|
|
}
|
|
return htmlSize;
|
|
},
|
|
_compile: function (element, dataItem) {
|
|
this.treeMap.angular('compile', function () {
|
|
return {
|
|
elements: element,
|
|
data: [{ dataItem: dataItem }]
|
|
};
|
|
});
|
|
},
|
|
_getByUid: function (uid) {
|
|
return this.element.find('.k-treemap-tile[' + kendo.attr('uid') + '=\'' + uid + '\']');
|
|
},
|
|
render: function (root) {
|
|
var rootElement = this._getByUid(root.dataItem.uid);
|
|
var children = root.children;
|
|
if (children) {
|
|
var rootWrap = rootElement.find('.k-treemap-wrap');
|
|
for (var i = 0; i < children.length; i++) {
|
|
var leaf = children[i];
|
|
var htmlElement = this._createLeaf(leaf);
|
|
rootWrap.append(htmlElement);
|
|
this._compile(htmlElement.children(), leaf.dataItem);
|
|
this.treeMap.trigger(ITEM_CREATED, { element: htmlElement });
|
|
}
|
|
}
|
|
},
|
|
createRoot: function (root) {
|
|
var htmlElement = this._createLeaf(root);
|
|
this.element.append(htmlElement);
|
|
this._compile(htmlElement.children(), root.dataItem);
|
|
this.treeMap.trigger(ITEM_CREATED, { element: htmlElement });
|
|
},
|
|
_clean: function (root) {
|
|
this.treeMap.angular('cleanup', function () {
|
|
return { elements: root.children(':not(.k-treemap-wrap)') };
|
|
});
|
|
root.css('background-color', '');
|
|
root.removeClass('k-leaf');
|
|
root.removeClass('k-inverse');
|
|
root.empty();
|
|
},
|
|
_createLeaf: function (item) {
|
|
return this._createTile(item).css('background-color', item.color).addClass('k-leaf').toggleClass('k-inverse', this._tileColorBrightness(item) > 180).append($('<div></div>').html(this._getText(item)));
|
|
},
|
|
_createTile: function (item) {
|
|
var tile = $('<div class=\'k-treemap-tile\'></div>');
|
|
this.setItemSize(item, tile);
|
|
if (defined(item.dataItem) && defined(item.dataItem.uid)) {
|
|
tile.attr(kendo.attr('uid'), item.dataItem.uid);
|
|
}
|
|
return tile;
|
|
},
|
|
_itemCoordinates: function (item) {
|
|
var coordinates = {
|
|
width: item.coord.width,
|
|
height: item.coord.height,
|
|
left: item.coord.left,
|
|
top: item.coord.top
|
|
};
|
|
if (coordinates.left && this.offset) {
|
|
coordinates.width += this.offset * 2;
|
|
} else {
|
|
coordinates.width += this.offset;
|
|
}
|
|
if (coordinates.top) {
|
|
coordinates.height += this.offset * 2;
|
|
} else {
|
|
coordinates.height += this.offset;
|
|
}
|
|
return coordinates;
|
|
},
|
|
setItemSize: function (item, element) {
|
|
var coordinates = this._itemCoordinates(item);
|
|
element.css({
|
|
width: coordinates.width,
|
|
height: coordinates.height,
|
|
left: coordinates.left,
|
|
top: coordinates.top
|
|
});
|
|
},
|
|
_getText: function (item) {
|
|
var text = item.text;
|
|
if (this.options.template) {
|
|
text = this._renderTemplate(item);
|
|
}
|
|
return text;
|
|
},
|
|
_renderTemplate: function (item) {
|
|
var titleTemplate = template(this.options.template);
|
|
return titleTemplate({
|
|
dataItem: item.dataItem,
|
|
text: item.text
|
|
});
|
|
},
|
|
_createTitle: function (item) {
|
|
return $('<div class=\'k-treemap-title\'></div>').append($('<div></div>').html(this._getText(item)));
|
|
},
|
|
_createWrap: function () {
|
|
return $('<div class=\'k-treemap-wrap\'></div>');
|
|
},
|
|
_tileColorBrightness: function (item) {
|
|
return colorBrightness(item.color);
|
|
}
|
|
});
|
|
var SliceAndDice = Class.extend({
|
|
createRoot: function (root, width, height, vertical) {
|
|
root.coord = {
|
|
width: width,
|
|
height: height,
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
root.vertical = vertical;
|
|
},
|
|
init: function (vertical) {
|
|
this.vertical = vertical;
|
|
this.quotient = vertical ? 1 : 0;
|
|
},
|
|
compute: function (children, rootCoord, htmlSize) {
|
|
if (children.length > 0) {
|
|
var width = rootCoord.width;
|
|
var height = rootCoord.height;
|
|
if (this.vertical) {
|
|
height -= htmlSize.text;
|
|
} else {
|
|
width -= htmlSize.text;
|
|
}
|
|
var newRootCoord = {
|
|
width: width,
|
|
height: height,
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
this.layoutChildren(children, newRootCoord);
|
|
}
|
|
},
|
|
layoutChildren: function (items, coord) {
|
|
var parentArea = coord.width * coord.height;
|
|
var totalArea = 0;
|
|
var itemsArea = [];
|
|
var i;
|
|
for (i = 0; i < items.length; i++) {
|
|
var item = items[i];
|
|
itemsArea[i] = parseFloat(items[i].value);
|
|
totalArea += itemsArea[i];
|
|
item.vertical = this.vertical;
|
|
}
|
|
for (i = 0; i < itemsArea.length; i++) {
|
|
items[i].area = parentArea * itemsArea[i] / totalArea;
|
|
}
|
|
this.sliceAndDice(items, coord);
|
|
},
|
|
sliceAndDice: function (items, coord) {
|
|
var totalArea = this._totalArea(items);
|
|
if (items[0].level % 2 === this.quotient) {
|
|
this.layoutHorizontal(items, coord, totalArea);
|
|
} else {
|
|
this.layoutVertical(items, coord, totalArea);
|
|
}
|
|
},
|
|
layoutHorizontal: function (items, coord, totalArea) {
|
|
var left = 0;
|
|
for (var i = 0; i < items.length; i++) {
|
|
var item = items[i];
|
|
var width = item.area / (totalArea / coord.width);
|
|
item.coord = {
|
|
height: coord.height,
|
|
width: width,
|
|
top: coord.top,
|
|
left: coord.left + left
|
|
};
|
|
left += width;
|
|
}
|
|
},
|
|
layoutVertical: function (items, coord, totalArea) {
|
|
var top = 0;
|
|
for (var i = 0; i < items.length; i++) {
|
|
var item = items[i];
|
|
var height = item.area / (totalArea / coord.height);
|
|
item.coord = {
|
|
height: height,
|
|
width: coord.width,
|
|
top: coord.top + top,
|
|
left: coord.left
|
|
};
|
|
top += height;
|
|
}
|
|
},
|
|
_totalArea: function (items) {
|
|
var total = 0;
|
|
for (var i = 0; i < items.length; i++) {
|
|
total += items[i].area;
|
|
}
|
|
return total;
|
|
}
|
|
});
|
|
var SliceAndDiceView = SquarifiedView.extend({
|
|
htmlSize: function (root) {
|
|
var rootElement = this._getByUid(root.dataItem.uid);
|
|
var htmlSize = {
|
|
text: 0,
|
|
offset: 0
|
|
};
|
|
if (root.children) {
|
|
this._clean(rootElement);
|
|
var text = this._getText(root);
|
|
if (text) {
|
|
var title = this._createTitle(root);
|
|
rootElement.append(title);
|
|
this._compile(title, root.dataItem);
|
|
if (root.vertical) {
|
|
htmlSize.text = title.height();
|
|
} else {
|
|
htmlSize.text = title.width();
|
|
}
|
|
}
|
|
rootElement.append(this._createWrap());
|
|
this.offset = (rootElement.outerWidth() - rootElement.innerWidth()) / 2;
|
|
}
|
|
return htmlSize;
|
|
},
|
|
titleSize: function (item, element) {
|
|
var size;
|
|
if (item.vertical) {
|
|
size = element.children('.k-treemap-title').height();
|
|
} else {
|
|
size = element.children('.k-treemap-title-vertical').width();
|
|
}
|
|
return size;
|
|
},
|
|
_createTitle: function (item) {
|
|
var title;
|
|
if (item.vertical) {
|
|
title = $('<div class=\'k-treemap-title\'></div>');
|
|
} else {
|
|
title = $('<div class=\'k-treemap-title-vertical\'></div>');
|
|
}
|
|
return title.append($('<div></div>').html(this._getText(item)));
|
|
}
|
|
});
|
|
function getField(field, row) {
|
|
if (row === null) {
|
|
return row;
|
|
}
|
|
var get = getter(field, true);
|
|
return get(row);
|
|
}
|
|
function defined(value) {
|
|
return typeof value !== UNDEFINED;
|
|
}
|
|
function colorsByLength(min, max, length) {
|
|
var minRGBtoDecimal = rgbToDecimal(min);
|
|
var maxRGBtoDecimal = rgbToDecimal(max);
|
|
var isDarker = colorBrightness(min) - colorBrightness(max) < 0;
|
|
var colors = [];
|
|
colors.push(min);
|
|
for (var i = 0; i < length; i++) {
|
|
var rgbColor = {
|
|
r: colorByIndex(minRGBtoDecimal.r, maxRGBtoDecimal.r, i, length, isDarker),
|
|
g: colorByIndex(minRGBtoDecimal.g, maxRGBtoDecimal.g, i, length, isDarker),
|
|
b: colorByIndex(minRGBtoDecimal.b, maxRGBtoDecimal.b, i, length, isDarker)
|
|
};
|
|
colors.push(buildColorFromRGB(rgbColor));
|
|
}
|
|
colors.push(max);
|
|
return colors;
|
|
}
|
|
function colorByIndex(min, max, index, length, isDarker) {
|
|
var minColor = math.min(math.abs(min), math.abs(max));
|
|
var maxColor = math.max(math.abs(min), math.abs(max));
|
|
var step = (maxColor - minColor) / (length + 1);
|
|
var currentStep = step * (index + 1);
|
|
var color;
|
|
if (isDarker) {
|
|
color = minColor + currentStep;
|
|
} else {
|
|
color = maxColor - currentStep;
|
|
}
|
|
return color;
|
|
}
|
|
function buildColorFromRGB(color) {
|
|
return '#' + decimalToRgb(color.r) + decimalToRgb(color.g) + decimalToRgb(color.b);
|
|
}
|
|
function rgbToDecimal(color) {
|
|
color = color.replace('#', '');
|
|
var rgbColor = colorToRGB(color);
|
|
return {
|
|
r: rgbToHex(rgbColor.r),
|
|
g: rgbToHex(rgbColor.g),
|
|
b: rgbToHex(rgbColor.b)
|
|
};
|
|
}
|
|
function decimalToRgb(number) {
|
|
var result = math.round(number).toString(16).toUpperCase();
|
|
if (result.length === 1) {
|
|
result = '0' + result;
|
|
}
|
|
return result;
|
|
}
|
|
function colorToRGB(color) {
|
|
var colorLength = color.length;
|
|
var rgbColor = {};
|
|
if (colorLength === 3) {
|
|
rgbColor.r = color[0];
|
|
rgbColor.g = color[1];
|
|
rgbColor.b = color[2];
|
|
} else {
|
|
rgbColor.r = color.substring(0, 2);
|
|
rgbColor.g = color.substring(2, 4);
|
|
rgbColor.b = color.substring(4, 6);
|
|
}
|
|
return rgbColor;
|
|
}
|
|
function rgbToHex(rgb) {
|
|
return parseInt(rgb.toString(16), 16);
|
|
}
|
|
function colorBrightness(color) {
|
|
var brightness = 0;
|
|
if (color) {
|
|
color = rgbToDecimal(color);
|
|
brightness = math.sqrt(0.241 * color.r * color.r + 0.691 * color.g * color.g + 0.068 * color.b * color.b);
|
|
}
|
|
return brightness;
|
|
}
|
|
function round(value) {
|
|
var power = math.pow(10, 4);
|
|
return math.round(value * power) / power;
|
|
}
|
|
dataviz.ui.plugin(TreeMap);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz', [
|
|
'kendo.core',
|
|
'kendo.fx',
|
|
'kendo.router',
|
|
'kendo.view',
|
|
'kendo.data.odata',
|
|
'kendo.data.xml',
|
|
'kendo.data',
|
|
'kendo.data.signalr',
|
|
'kendo.binder',
|
|
'kendo.userevents',
|
|
'kendo.draganddrop',
|
|
'kendo.mobile.scroller',
|
|
'kendo.popup',
|
|
'kendo.tooltip',
|
|
'kendo.drawing',
|
|
'kendo.dataviz.core',
|
|
'kendo.dataviz.themes',
|
|
'kendo.dataviz.chart',
|
|
'kendo.dataviz.chart.polar',
|
|
'kendo.dataviz.chart.funnel',
|
|
'kendo.dataviz.gauge',
|
|
'kendo.dataviz.barcode',
|
|
'kendo.dataviz.qrcode',
|
|
'kendo.dataviz.stock',
|
|
'kendo.dataviz.sparkline',
|
|
'kendo.dataviz.map',
|
|
'kendo.dataviz.diagram',
|
|
'kendo.dataviz.treemap',
|
|
'kendo.angular',
|
|
'kendo.webcomponents',
|
|
'kendo.angular2'
|
|
], f);
|
|
}(function () {
|
|
'bundle all';
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.modalview', [
|
|
'kendo.mobile.shim',
|
|
'kendo.mobile.view'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.modalview',
|
|
name: 'ModalView',
|
|
category: 'mobile',
|
|
description: 'The Kendo ModalView is used to present self-contained functionality in the context of the current task.',
|
|
depends: [
|
|
'mobile.shim',
|
|
'mobile.view'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Shim = ui.Shim, Widget = ui.Widget, BEFORE_OPEN = 'beforeOpen', OPEN = 'open', CLOSE = 'close', INIT = 'init', WRAP = '<div class="km-modalview-wrapper" />';
|
|
var ModalView = ui.View.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that._id();
|
|
that._wrap();
|
|
that._shim();
|
|
if (!this.options.$angular) {
|
|
that._layout();
|
|
that._scroller();
|
|
that._model();
|
|
}
|
|
that.element.css('display', '');
|
|
that.trigger(INIT);
|
|
},
|
|
events: [
|
|
INIT,
|
|
BEFORE_OPEN,
|
|
OPEN,
|
|
CLOSE
|
|
],
|
|
options: {
|
|
name: 'ModalView',
|
|
modal: true,
|
|
width: null,
|
|
height: null
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.shim.destroy();
|
|
},
|
|
open: function (target) {
|
|
var that = this;
|
|
that.target = $(target);
|
|
that.shim.show();
|
|
that._invokeNgController();
|
|
that.trigger('show', { view: that });
|
|
},
|
|
openFor: function (target) {
|
|
if (!this.trigger(BEFORE_OPEN, { target: target })) {
|
|
this.open(target);
|
|
this.trigger(OPEN, { target: target });
|
|
}
|
|
},
|
|
close: function () {
|
|
if (this.element.is(':visible') && !this.trigger(CLOSE)) {
|
|
this.shim.hide();
|
|
}
|
|
},
|
|
_wrap: function () {
|
|
var that = this, element = that.element, options = that.options, width, height;
|
|
width = element[0].style.width || 'auto';
|
|
height = element[0].style.height || 'auto';
|
|
element.addClass('km-modalview').wrap(WRAP);
|
|
that.wrapper = element.parent().css({
|
|
width: options.width || width || 300,
|
|
height: options.height || height || 300
|
|
}).addClass(height == 'auto' ? ' km-auto-height' : '');
|
|
element.css({
|
|
width: '',
|
|
height: ''
|
|
});
|
|
},
|
|
_shim: function () {
|
|
var that = this;
|
|
that.shim = new Shim(that.wrapper, {
|
|
modal: that.options.modal,
|
|
position: 'center center',
|
|
align: 'center center',
|
|
effect: 'fade:in',
|
|
className: 'km-modalview-root',
|
|
hide: function (e) {
|
|
if (that.trigger(CLOSE)) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
ui.plugin(ModalView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.drawer', [
|
|
'kendo.mobile.view',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.drawer',
|
|
name: 'Drawer',
|
|
category: 'mobile',
|
|
description: 'The Kendo Mobile Drawer widget provides slide to reveal global application toolbox',
|
|
depends: [
|
|
'mobile.view',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, os = kendo.support.mobileOS, Transition = kendo.effects.Transition, roleSelector = kendo.roleSelector, AXIS = 'x', ui = mobile.ui, SWIPE_TO_OPEN = !(os.ios && os.majorVersion == 7 && !os.appMode), BEFORE_SHOW = 'beforeShow', INIT = 'init', SHOW = 'show', HIDE = 'hide', AFTER_HIDE = 'afterHide', NULL_VIEW = { enable: $.noop };
|
|
var Drawer = ui.View.extend({
|
|
init: function (element, options) {
|
|
$(element).parent().prepend(element);
|
|
mobile.ui.Widget.fn.init.call(this, element, options);
|
|
if (!this.options.$angular) {
|
|
this._layout();
|
|
this._scroller();
|
|
}
|
|
this._model();
|
|
var pane = this.element.closest(roleSelector('pane')).data('kendoMobilePane'), userEvents;
|
|
if (pane) {
|
|
this.pane = pane;
|
|
this.pane.bind('viewShow', function (e) {
|
|
drawer._viewShow(e);
|
|
});
|
|
this.pane.bind('sameViewRequested', function () {
|
|
drawer.hide();
|
|
});
|
|
userEvents = this.userEvents = new kendo.UserEvents(pane.element, {
|
|
fastTap: true,
|
|
filter: roleSelector('view splitview'),
|
|
allowSelection: true
|
|
});
|
|
} else {
|
|
this.currentView = NULL_VIEW;
|
|
var container = $(this.options.container);
|
|
if (!container) {
|
|
throw new Error('The drawer needs a container configuration option set.');
|
|
}
|
|
userEvents = this.userEvents = new kendo.UserEvents(container, {
|
|
fastTap: true,
|
|
allowSelection: true
|
|
});
|
|
this._attachTransition(container);
|
|
}
|
|
var drawer = this;
|
|
var hide = function (e) {
|
|
if (drawer.visible) {
|
|
drawer.hide();
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
if (this.options.swipeToOpen && SWIPE_TO_OPEN) {
|
|
userEvents.bind('press', function () {
|
|
drawer.transition.cancel();
|
|
});
|
|
userEvents.bind('start', function (e) {
|
|
drawer._start(e);
|
|
});
|
|
userEvents.bind('move', function (e) {
|
|
drawer._update(e);
|
|
});
|
|
userEvents.bind('end', function (e) {
|
|
drawer._end(e);
|
|
});
|
|
userEvents.bind('tap', hide);
|
|
} else {
|
|
userEvents.bind('press', hide);
|
|
}
|
|
this.leftPositioned = this.options.position === 'left';
|
|
this.visible = false;
|
|
this.element.hide().addClass('km-drawer').addClass(this.leftPositioned ? 'km-left-drawer' : 'km-right-drawer');
|
|
this.trigger(INIT);
|
|
},
|
|
options: {
|
|
name: 'Drawer',
|
|
position: 'left',
|
|
views: [],
|
|
swipeToOpenViews: [],
|
|
swipeToOpen: true,
|
|
title: '',
|
|
container: null
|
|
},
|
|
events: [
|
|
BEFORE_SHOW,
|
|
HIDE,
|
|
AFTER_HIDE,
|
|
INIT,
|
|
SHOW
|
|
],
|
|
show: function () {
|
|
if (this._activate()) {
|
|
this._show();
|
|
}
|
|
},
|
|
hide: function () {
|
|
if (!this.currentView) {
|
|
return;
|
|
}
|
|
this.currentView.enable();
|
|
Drawer.current = null;
|
|
this._moveViewTo(0);
|
|
this.trigger(HIDE, { view: this });
|
|
},
|
|
openFor: function () {
|
|
if (this.visible) {
|
|
this.hide();
|
|
} else {
|
|
this.show();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
ui.View.fn.destroy.call(this);
|
|
this.userEvents.destroy();
|
|
},
|
|
_activate: function () {
|
|
if (this.visible) {
|
|
return true;
|
|
}
|
|
var visibleOnCurrentView = this._currentViewIncludedIn(this.options.views);
|
|
if (!visibleOnCurrentView || this.trigger(BEFORE_SHOW, { view: this })) {
|
|
return false;
|
|
}
|
|
this._setAsCurrent();
|
|
this.element.show();
|
|
this.trigger(SHOW, { view: this });
|
|
this._invokeNgController();
|
|
return true;
|
|
},
|
|
_currentViewIncludedIn: function (views) {
|
|
if (!this.pane || !views.length) {
|
|
return true;
|
|
}
|
|
var view = this.pane.view();
|
|
return $.inArray(view.id.replace('#', ''), views) > -1 || $.inArray(view.element.attr('id'), views) > -1;
|
|
},
|
|
_show: function () {
|
|
this.currentView.enable(false);
|
|
this.visible = true;
|
|
var offset = this.element.width();
|
|
if (!this.leftPositioned) {
|
|
offset = -offset;
|
|
}
|
|
this._moveViewTo(offset);
|
|
},
|
|
_setAsCurrent: function () {
|
|
if (Drawer.last !== this) {
|
|
if (Drawer.last) {
|
|
Drawer.last.element.hide();
|
|
}
|
|
this.element.show();
|
|
}
|
|
Drawer.last = this;
|
|
Drawer.current = this;
|
|
},
|
|
_moveViewTo: function (offset) {
|
|
this.userEvents.cancel();
|
|
this.transition.moveTo({
|
|
location: offset,
|
|
duration: 400,
|
|
ease: Transition.easeOutExpo
|
|
});
|
|
},
|
|
_viewShow: function (e) {
|
|
if (this.currentView) {
|
|
this.currentView.enable();
|
|
}
|
|
if (this.currentView === e.view) {
|
|
this.hide();
|
|
return;
|
|
}
|
|
this.currentView = e.view;
|
|
this._attachTransition(e.view.element);
|
|
},
|
|
_attachTransition: function (element) {
|
|
var that = this, movable = this.movable, currentOffset = movable && movable.x;
|
|
if (this.transition) {
|
|
this.transition.cancel();
|
|
this.movable.moveAxis('x', 0);
|
|
}
|
|
movable = this.movable = new kendo.ui.Movable(element);
|
|
this.transition = new Transition({
|
|
axis: AXIS,
|
|
movable: this.movable,
|
|
onEnd: function () {
|
|
if (movable[AXIS] === 0) {
|
|
element[0].style.cssText = '';
|
|
that.element.hide();
|
|
that.trigger(AFTER_HIDE);
|
|
that.visible = false;
|
|
}
|
|
}
|
|
});
|
|
if (currentOffset) {
|
|
element.addClass('k-fx-hidden');
|
|
kendo.animationFrame(function () {
|
|
element.removeClass('k-fx-hidden');
|
|
that.movable.moveAxis(AXIS, currentOffset);
|
|
that.hide();
|
|
});
|
|
}
|
|
},
|
|
_start: function (e) {
|
|
var userEvents = e.sender;
|
|
if (Math.abs(e.x.velocity) < Math.abs(e.y.velocity) || kendo.triggeredByInput(e.event) || !this._currentViewIncludedIn(this.options.swipeToOpenViews)) {
|
|
userEvents.cancel();
|
|
return;
|
|
}
|
|
var leftPositioned = this.leftPositioned, visible = this.visible, canMoveLeft = leftPositioned && visible || !leftPositioned && !Drawer.current, canMoveRight = !leftPositioned && visible || leftPositioned && !Drawer.current, leftSwipe = e.x.velocity < 0;
|
|
if (canMoveLeft && leftSwipe || canMoveRight && !leftSwipe) {
|
|
if (this._activate()) {
|
|
userEvents.capture();
|
|
return;
|
|
}
|
|
}
|
|
userEvents.cancel();
|
|
},
|
|
_update: function (e) {
|
|
var movable = this.movable, newPosition = movable.x + e.x.delta, limitedPosition;
|
|
if (this.leftPositioned) {
|
|
limitedPosition = Math.min(Math.max(0, newPosition), this.element.width());
|
|
} else {
|
|
limitedPosition = Math.max(Math.min(0, newPosition), -this.element.width());
|
|
}
|
|
this.movable.moveAxis(AXIS, limitedPosition);
|
|
e.event.preventDefault();
|
|
e.event.stopPropagation();
|
|
},
|
|
_end: function (e) {
|
|
var velocity = e.x.velocity, pastHalf = Math.abs(this.movable.x) > this.element.width() / 2, velocityThreshold = 0.8, shouldShow;
|
|
if (this.leftPositioned) {
|
|
shouldShow = velocity > -velocityThreshold && (velocity > velocityThreshold || pastHalf);
|
|
} else {
|
|
shouldShow = velocity < velocityThreshold && (velocity < -velocityThreshold || pastHalf);
|
|
}
|
|
if (shouldShow) {
|
|
this._show();
|
|
} else {
|
|
this.hide();
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Drawer);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.splitview', ['kendo.mobile.pane'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.splitview',
|
|
name: 'SplitView',
|
|
category: 'mobile',
|
|
description: 'The mobile SplitView is a tablet-specific view that consists of two or more mobile Pane widgets.',
|
|
depends: ['mobile.pane']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, EXPANED_PANE_SHIM = '<div class=\'km-expanded-pane-shim\' />', View = ui.View;
|
|
var SplitView = View.extend({
|
|
init: function (element, options) {
|
|
var that = this, pane, modalViews;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
$.extend(that, options);
|
|
that._id();
|
|
if (!that.options.$angular) {
|
|
that._layout();
|
|
that._overlay();
|
|
} else {
|
|
that._overlay();
|
|
}
|
|
that._style();
|
|
modalViews = element.children(that._locate('modalview'));
|
|
if (!that.options.$angular) {
|
|
kendo.mobile.init(modalViews);
|
|
} else {
|
|
modalViews.each(function (idx, element) {
|
|
kendo.compileMobileDirective($(element), options.$angular[0]);
|
|
});
|
|
}
|
|
that.panes = [];
|
|
that._paramsHistory = [];
|
|
if (!that.options.$angular) {
|
|
that.content.children(kendo.roleSelector('pane')).each(function () {
|
|
pane = kendo.initWidget(this, {}, ui.roles);
|
|
that.panes.push(pane);
|
|
});
|
|
} else {
|
|
that.element.children(kendo.directiveSelector('pane')).each(function () {
|
|
pane = kendo.compileMobileDirective($(this), options.$angular[0]);
|
|
that.panes.push(pane);
|
|
});
|
|
that.element.children(kendo.directiveSelector('header footer')).each(function () {
|
|
kendo.compileMobileDirective($(this), options.$angular[0]);
|
|
});
|
|
}
|
|
that.expandedPaneShim = $(EXPANED_PANE_SHIM).appendTo(that.element);
|
|
that._shimUserEvents = new kendo.UserEvents(that.expandedPaneShim, {
|
|
fastTap: true,
|
|
tap: function () {
|
|
that.collapsePanes();
|
|
}
|
|
});
|
|
},
|
|
_locate: function (selectors) {
|
|
return this.options.$angular ? kendo.directiveSelector(selectors) : kendo.roleSelector(selectors);
|
|
},
|
|
options: {
|
|
name: 'SplitView',
|
|
style: 'horizontal'
|
|
},
|
|
expandPanes: function () {
|
|
this.element.addClass('km-expanded-splitview');
|
|
},
|
|
collapsePanes: function () {
|
|
this.element.removeClass('km-expanded-splitview');
|
|
},
|
|
_layout: function () {
|
|
var that = this, element = that.element;
|
|
that.transition = kendo.attrValue(element, 'transition');
|
|
kendo.mobile.ui.View.prototype._layout.call(this);
|
|
kendo.mobile.init(this.header.add(this.footer));
|
|
that.element.addClass('km-splitview');
|
|
that.content.addClass('km-split-content');
|
|
},
|
|
_style: function () {
|
|
var style = this.options.style, element = this.element, styles;
|
|
if (style) {
|
|
styles = style.split(' ');
|
|
$.each(styles, function () {
|
|
element.addClass('km-split-' + this);
|
|
});
|
|
}
|
|
},
|
|
showStart: function () {
|
|
var that = this;
|
|
that.element.css('display', '');
|
|
if (!that.inited) {
|
|
that.inited = true;
|
|
$.each(that.panes, function () {
|
|
if (this.options.initial) {
|
|
this.navigateToInitial();
|
|
} else {
|
|
this.navigate('');
|
|
}
|
|
});
|
|
that.trigger('init', { view: that });
|
|
} else {
|
|
this._invokeNgController();
|
|
}
|
|
that.trigger('show', { view: that });
|
|
}
|
|
});
|
|
ui.plugin(SplitView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.application', [
|
|
'kendo.mobile.pane',
|
|
'kendo.router'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.application',
|
|
name: 'Application',
|
|
category: 'mobile',
|
|
description: 'The Mobile application provides a framework to build native looking web applications on mobile devices.',
|
|
depends: [
|
|
'mobile.pane',
|
|
'router'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, support = kendo.support, Widget = mobile.ui.Widget, Pane = mobile.ui.Pane, DEFAULT_OS = 'ios7', OS = support.mobileOS, BERRYPHONEGAP = OS.device == 'blackberry' && OS.flatVersion >= 600 && OS.flatVersion < 1000 && OS.appMode, FONT_SIZE_COEF = 0.93, VERTICAL = 'km-vertical', CHROME = OS.browser === 'chrome', BROKEN_WEBVIEW_RESIZE = OS.ios && OS.flatVersion >= 700 && OS.flatVersion < 800 && (OS.appMode || CHROME), INITIALLY_HORIZONTAL = Math.abs(window.orientation) / 90 == 1, HORIZONTAL = 'km-horizontal', MOBILE_PLATFORMS = {
|
|
ios7: {
|
|
ios: true,
|
|
browser: 'default',
|
|
device: 'iphone',
|
|
flatVersion: '700',
|
|
majorVersion: '7',
|
|
minorVersion: '0.0',
|
|
name: 'ios',
|
|
tablet: false
|
|
},
|
|
ios: {
|
|
ios: true,
|
|
browser: 'default',
|
|
device: 'iphone',
|
|
flatVersion: '612',
|
|
majorVersion: '6',
|
|
minorVersion: '1.2',
|
|
name: 'ios',
|
|
tablet: false
|
|
},
|
|
android: {
|
|
android: true,
|
|
browser: 'default',
|
|
device: 'android',
|
|
flatVersion: '442',
|
|
majorVersion: '4',
|
|
minorVersion: '4.2',
|
|
name: 'android',
|
|
tablet: false
|
|
},
|
|
blackberry: {
|
|
blackberry: true,
|
|
browser: 'default',
|
|
device: 'blackberry',
|
|
flatVersion: '710',
|
|
majorVersion: '7',
|
|
minorVersion: '1.0',
|
|
name: 'blackberry',
|
|
tablet: false
|
|
},
|
|
meego: {
|
|
meego: true,
|
|
browser: 'default',
|
|
device: 'meego',
|
|
flatVersion: '850',
|
|
majorVersion: '8',
|
|
minorVersion: '5.0',
|
|
name: 'meego',
|
|
tablet: false
|
|
},
|
|
wp: {
|
|
wp: true,
|
|
browser: 'default',
|
|
device: 'wp',
|
|
flatVersion: '800',
|
|
majorVersion: '8',
|
|
minorVersion: '0.0',
|
|
name: 'wp',
|
|
tablet: false
|
|
}
|
|
}, viewportTemplate = kendo.template('<meta content="initial-scale=#: data.scale #, maximum-scale=#: data.scale #, user-scalable=no#=data.height#" name="viewport" />', { usedWithBlock: false }), systemMeta = kendo.template('<meta name="apple-mobile-web-app-capable" content="#= data.webAppCapable === false ? \'no\' : \'yes\' #" /> ' + '<meta name="apple-mobile-web-app-status-bar-style" content="#=data.statusBarStyle#" /> ' + '<meta name="msapplication-tap-highlight" content="no" /> ', { usedWithBlock: false }), clipTemplate = kendo.template('<style>.km-view { clip: rect(0 #= data.width #px #= data.height #px 0); }</style>', { usedWithBlock: false }), ENABLE_CLIP = OS.android && OS.browser != 'chrome' || OS.blackberry, iconMeta = kendo.template('<link rel="apple-touch-icon' + (OS.android ? '-precomposed' : '') + '" # if(data.size) { # sizes="#=data.size#" #}# href="#=data.icon#" />', { usedWithBlock: false }), HIDEBAR = (OS.device == 'iphone' || OS.device == 'ipod') && OS.majorVersion < 7, SUPPORT_SWIPE_TO_GO_BACK = (OS.device == 'iphone' || OS.device == 'ipod') && OS.majorVersion >= 7, HISTORY_TRANSITION = SUPPORT_SWIPE_TO_GO_BACK ? 'none' : null, BARCOMPENSATION = OS.browser == 'mobilesafari' ? 60 : 0, STATUS_BAR_HEIGHT = 20, WINDOW = $(window), SCREEN = window.screen, HEAD = $('head'), INIT = 'init', proxy = $.proxy;
|
|
function osCssClass(os, options) {
|
|
var classes = [];
|
|
if (OS) {
|
|
classes.push('km-on-' + OS.name);
|
|
}
|
|
if (os.skin) {
|
|
classes.push('km-' + os.skin);
|
|
} else {
|
|
if (os.name == 'ios' && os.majorVersion > 6) {
|
|
classes.push('km-ios7');
|
|
} else {
|
|
classes.push('km-' + os.name);
|
|
}
|
|
}
|
|
if (os.name == 'ios' && os.majorVersion < 7 || os.name != 'ios') {
|
|
classes.push('km-' + os.name + os.majorVersion);
|
|
}
|
|
classes.push('km-' + os.majorVersion);
|
|
classes.push('km-m' + (os.minorVersion ? os.minorVersion[0] : 0));
|
|
if (os.variant && (os.skin && os.skin === os.name || !os.skin || os.setDefaultPlatform === false)) {
|
|
classes.push('km-' + (os.skin ? os.skin : os.name) + '-' + os.variant);
|
|
}
|
|
if (os.cordova) {
|
|
classes.push('km-cordova');
|
|
}
|
|
if (os.appMode) {
|
|
classes.push('km-app');
|
|
} else {
|
|
classes.push('km-web');
|
|
}
|
|
if (options && options.statusBarStyle) {
|
|
classes.push('km-' + options.statusBarStyle + '-status-bar');
|
|
}
|
|
return classes.join(' ');
|
|
}
|
|
function wp8Background(os) {
|
|
return 'km-wp-' + (os.noVariantSet ? parseInt($('<div style=\'background: Background\' />').css('background-color').split(',')[1], 10) === 0 ? 'dark' : 'light' : os.variant + ' km-wp-' + os.variant + '-force');
|
|
}
|
|
function isOrientationHorizontal(element) {
|
|
return OS.wp ? element.css('animation-name') == '-kendo-landscape' : Math.abs(window.orientation) / 90 == 1;
|
|
}
|
|
function getOrientationClass(element) {
|
|
return isOrientationHorizontal(element) ? HORIZONTAL : VERTICAL;
|
|
}
|
|
function setMinimumHeight(pane) {
|
|
pane.parent().addBack().css('min-height', window.innerHeight);
|
|
}
|
|
function applyViewportHeight() {
|
|
$('meta[name=viewport]').remove();
|
|
HEAD.append(viewportTemplate({ height: ', width=device-width' + (isOrientationHorizontal() ? ', height=' + window.innerHeight + 'px' : support.mobileOS.flatVersion >= 600 && support.mobileOS.flatVersion < 700 ? ', height=' + window.innerWidth + 'px' : ', height=device-height') }));
|
|
}
|
|
var Application = Widget.extend({
|
|
init: function (element, options) {
|
|
mobile.application = this;
|
|
$($.proxy(this, 'bootstrap', element, options));
|
|
},
|
|
bootstrap: function (element, options) {
|
|
element = $(element);
|
|
if (!element[0]) {
|
|
element = $(document.body);
|
|
}
|
|
Widget.fn.init.call(this, element, options);
|
|
this.element.removeAttr('data-' + kendo.ns + 'role');
|
|
this._setupPlatform();
|
|
this._attachMeta();
|
|
this._setupElementClass();
|
|
this._attachHideBarHandlers();
|
|
var paneOptions = $.extend({}, this.options);
|
|
delete paneOptions.name;
|
|
var that = this, startHistory = function () {
|
|
that.pane = new Pane(that.element, paneOptions);
|
|
that.pane.navigateToInitial();
|
|
if (that.options.updateDocumentTitle) {
|
|
that._setupDocumentTitle();
|
|
}
|
|
that._startHistory();
|
|
that.trigger(INIT);
|
|
};
|
|
if (this.options.$angular) {
|
|
setTimeout(startHistory);
|
|
} else {
|
|
startHistory();
|
|
}
|
|
},
|
|
options: {
|
|
name: 'Application',
|
|
hideAddressBar: true,
|
|
browserHistory: true,
|
|
historyTransition: HISTORY_TRANSITION,
|
|
modelScope: window,
|
|
statusBarStyle: 'black',
|
|
transition: '',
|
|
retina: false,
|
|
platform: null,
|
|
skin: null,
|
|
updateDocumentTitle: true,
|
|
useNativeScrolling: false
|
|
},
|
|
events: [INIT],
|
|
navigate: function (url, transition) {
|
|
this.pane.navigate(url, transition);
|
|
},
|
|
replace: function (url, transition) {
|
|
this.pane.replace(url, transition);
|
|
},
|
|
scroller: function () {
|
|
return this.view().scroller;
|
|
},
|
|
hideLoading: function () {
|
|
if (this.pane) {
|
|
this.pane.hideLoading();
|
|
} else {
|
|
throw new Error('The mobile application instance is not fully instantiated. Please consider activating loading in the application init event handler.');
|
|
}
|
|
},
|
|
showLoading: function () {
|
|
if (this.pane) {
|
|
this.pane.showLoading();
|
|
} else {
|
|
throw new Error('The mobile application instance is not fully instantiated. Please consider activating loading in the application init event handler.');
|
|
}
|
|
},
|
|
changeLoadingMessage: function (message) {
|
|
if (this.pane) {
|
|
this.pane.changeLoadingMessage(message);
|
|
} else {
|
|
throw new Error('The mobile application instance is not fully instantiated. Please consider changing the message in the application init event handler.');
|
|
}
|
|
},
|
|
view: function () {
|
|
return this.pane.view();
|
|
},
|
|
skin: function (skin) {
|
|
var that = this;
|
|
if (!arguments.length) {
|
|
return that.options.skin;
|
|
}
|
|
that.options.skin = skin || '';
|
|
that.element[0].className = 'km-pane';
|
|
that._setupPlatform();
|
|
that._setupElementClass();
|
|
return that.options.skin;
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.pane.destroy();
|
|
this.router.destroy();
|
|
},
|
|
_setupPlatform: function () {
|
|
var that = this, platform = that.options.platform, skin = that.options.skin, split = [], os = OS || MOBILE_PLATFORMS[DEFAULT_OS];
|
|
if (platform) {
|
|
os.setDefaultPlatform = true;
|
|
if (typeof platform === 'string') {
|
|
split = platform.split('-');
|
|
os = $.extend({ variant: split[1] }, os, MOBILE_PLATFORMS[split[0]]);
|
|
} else {
|
|
os = platform;
|
|
}
|
|
}
|
|
if (skin) {
|
|
split = skin.split('-');
|
|
if (!OS) {
|
|
os.setDefaultPlatform = false;
|
|
}
|
|
os = $.extend({}, os, {
|
|
skin: split[0],
|
|
variant: split[1]
|
|
});
|
|
}
|
|
if (!os.variant) {
|
|
os.noVariantSet = true;
|
|
os.variant = 'dark';
|
|
}
|
|
that.os = os;
|
|
that.osCssClass = osCssClass(that.os, that.options);
|
|
if (os.name == 'wp') {
|
|
if (!that.refreshBackgroundColorProxy) {
|
|
that.refreshBackgroundColorProxy = $.proxy(function () {
|
|
if (that.os.variant && (that.os.skin && that.os.skin === that.os.name) || !that.os.skin) {
|
|
that.element.removeClass('km-wp-dark km-wp-light km-wp-dark-force km-wp-light-force').addClass(wp8Background(that.os));
|
|
}
|
|
}, that);
|
|
}
|
|
$(document).off('visibilitychange', that.refreshBackgroundColorProxy);
|
|
$(document).off('resume', that.refreshBackgroundColorProxy);
|
|
if (!os.skin) {
|
|
that.element.parent().css('overflow', 'hidden');
|
|
$(document).on('visibilitychange', that.refreshBackgroundColorProxy);
|
|
$(document).on('resume', that.refreshBackgroundColorProxy);
|
|
that.refreshBackgroundColorProxy();
|
|
}
|
|
}
|
|
},
|
|
_startHistory: function () {
|
|
if (this.options.browserHistory) {
|
|
this.router = new kendo.Router({
|
|
pushState: this.options.pushState,
|
|
root: this.options.root,
|
|
hashBang: this.options.hashBang
|
|
});
|
|
this.pane.bindToRouter(this.router);
|
|
this.router.start();
|
|
} else {
|
|
if (!this.options.initial) {
|
|
this.pane.navigate('');
|
|
}
|
|
}
|
|
},
|
|
_resizeToScreenHeight: function () {
|
|
var includeStatusBar = $('meta[name=apple-mobile-web-app-status-bar-style]').attr('content').match(/black-translucent|hidden/), element = this.element, height;
|
|
if (CHROME) {
|
|
height = window.innerHeight;
|
|
} else {
|
|
if (isOrientationHorizontal(element)) {
|
|
if (includeStatusBar) {
|
|
if (INITIALLY_HORIZONTAL) {
|
|
height = SCREEN.availWidth + STATUS_BAR_HEIGHT;
|
|
} else {
|
|
height = SCREEN.availWidth;
|
|
}
|
|
} else {
|
|
if (INITIALLY_HORIZONTAL) {
|
|
height = SCREEN.availWidth;
|
|
} else {
|
|
height = SCREEN.availWidth - STATUS_BAR_HEIGHT;
|
|
}
|
|
}
|
|
} else {
|
|
if (includeStatusBar) {
|
|
if (INITIALLY_HORIZONTAL) {
|
|
height = SCREEN.availHeight;
|
|
} else {
|
|
height = SCREEN.availHeight + STATUS_BAR_HEIGHT;
|
|
}
|
|
} else {
|
|
if (INITIALLY_HORIZONTAL) {
|
|
height = SCREEN.availHeight - STATUS_BAR_HEIGHT;
|
|
} else {
|
|
height = SCREEN.availHeight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
element.height(height);
|
|
},
|
|
_setupElementClass: function () {
|
|
var that = this, size, element = that.element;
|
|
element.parent().addClass('km-root km-' + (that.os.tablet ? 'tablet' : 'phone'));
|
|
element.addClass(that.osCssClass + ' ' + getOrientationClass(element));
|
|
if (this.options.useNativeScrolling) {
|
|
element.parent().addClass('km-native-scrolling');
|
|
}
|
|
if (CHROME) {
|
|
element.addClass('km-ios-chrome');
|
|
}
|
|
if (support.wpDevicePixelRatio) {
|
|
element.parent().css('font-size', support.wpDevicePixelRatio + 'em');
|
|
}
|
|
if (this.options.retina) {
|
|
element.parent().addClass('km-retina');
|
|
element.parent().css('font-size', support.devicePixelRatio * FONT_SIZE_COEF + 'em');
|
|
}
|
|
if (BERRYPHONEGAP) {
|
|
applyViewportHeight();
|
|
}
|
|
if (that.options.useNativeScrolling) {
|
|
element.parent().addClass('km-native-scrolling');
|
|
} else if (ENABLE_CLIP) {
|
|
size = (screen.availWidth > screen.availHeight ? screen.availWidth : screen.availHeight) + 200;
|
|
$(clipTemplate({
|
|
width: size,
|
|
height: size
|
|
})).appendTo(HEAD);
|
|
}
|
|
if (BROKEN_WEBVIEW_RESIZE) {
|
|
that._resizeToScreenHeight();
|
|
}
|
|
kendo.onResize(function () {
|
|
element.removeClass('km-horizontal km-vertical').addClass(getOrientationClass(element));
|
|
if (that.options.useNativeScrolling) {
|
|
setMinimumHeight(element);
|
|
}
|
|
if (BROKEN_WEBVIEW_RESIZE) {
|
|
that._resizeToScreenHeight();
|
|
}
|
|
if (BERRYPHONEGAP) {
|
|
applyViewportHeight();
|
|
}
|
|
kendo.resize(element);
|
|
});
|
|
},
|
|
_clearExistingMeta: function () {
|
|
HEAD.find('meta').filter('[name|=\'apple-mobile-web-app\'],[name|=\'msapplication-tap\'],[name=\'viewport\']').remove();
|
|
},
|
|
_attachMeta: function () {
|
|
var options = this.options, icon = options.icon, size;
|
|
this._clearExistingMeta();
|
|
if (!BERRYPHONEGAP) {
|
|
HEAD.prepend(viewportTemplate({
|
|
height: '',
|
|
scale: this.options.retina ? 1 / support.devicePixelRatio : '1.0'
|
|
}));
|
|
}
|
|
HEAD.prepend(systemMeta(options));
|
|
if (icon) {
|
|
if (typeof icon === 'string') {
|
|
icon = { '': icon };
|
|
}
|
|
for (size in icon) {
|
|
HEAD.prepend(iconMeta({
|
|
icon: icon[size],
|
|
size: size
|
|
}));
|
|
}
|
|
}
|
|
if (options.useNativeScrolling) {
|
|
setMinimumHeight(this.element);
|
|
}
|
|
},
|
|
_attachHideBarHandlers: function () {
|
|
var that = this, hideBar = proxy(that, '_hideBar');
|
|
if (support.mobileOS.appMode || !that.options.hideAddressBar || !HIDEBAR || that.options.useNativeScrolling) {
|
|
return;
|
|
}
|
|
that._initialHeight = {};
|
|
WINDOW.on('load', hideBar);
|
|
kendo.onResize(function () {
|
|
setTimeout(window.scrollTo, 0, 0, 1);
|
|
});
|
|
},
|
|
_setupDocumentTitle: function () {
|
|
var that = this, defaultTitle = document.title;
|
|
that.pane.bind('viewShow', function (e) {
|
|
var title = e.view.title;
|
|
document.title = title !== undefined ? title : defaultTitle;
|
|
});
|
|
},
|
|
_hideBar: function () {
|
|
var that = this, element = that.element;
|
|
element.height(kendo.support.transforms.css + 'calc(100% + ' + BARCOMPENSATION + 'px)');
|
|
$(window).trigger(kendo.support.resize);
|
|
}
|
|
});
|
|
kendo.mobile.Application = Application;
|
|
kendo.ui.plugin(Application, kendo.mobile, 'Mobile');
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.button', ['kendo.userevents'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.button',
|
|
name: 'Button',
|
|
category: 'mobile',
|
|
description: 'The Button widget navigates between mobile Application views when pressed.',
|
|
depends: ['userevents']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, Widget = ui.Widget, support = kendo.support, os = support.mobileOS, ANDROID3UP = os.android && os.flatVersion >= 300, CLICK = 'click', DISABLED = 'disabled', DISABLEDSTATE = 'km-state-disabled';
|
|
function highlightButton(widget, event, highlight) {
|
|
$(event.target).closest('.km-button,.km-detail').toggleClass('km-state-active', highlight);
|
|
if (ANDROID3UP && widget.deactivateTimeoutID) {
|
|
clearTimeout(widget.deactivateTimeoutID);
|
|
widget.deactivateTimeoutID = 0;
|
|
}
|
|
}
|
|
function createBadge(value) {
|
|
return $('<span class="km-badge">' + value + '</span>');
|
|
}
|
|
var Button = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
var useTap = that.options.clickOn === 'up';
|
|
that._wrap();
|
|
that._style();
|
|
if (!useTap) {
|
|
that.element.attr('data-navigate-on-press', true);
|
|
}
|
|
that.options.enable = that.options.enable && !that.element.attr(DISABLED);
|
|
that.enable(that.options.enable);
|
|
that._userEvents = new kendo.UserEvents(that.element, {
|
|
allowSelection: !useTap,
|
|
fastTap: true,
|
|
press: function (e) {
|
|
that._activate(e);
|
|
},
|
|
release: function (e) {
|
|
highlightButton(that, e, false);
|
|
if (!useTap) {
|
|
e.event.stopPropagation();
|
|
}
|
|
}
|
|
});
|
|
that._userEvents.bind(useTap ? 'tap' : 'press', function (e) {
|
|
that._release(e);
|
|
});
|
|
if (ANDROID3UP) {
|
|
that.element.on('move', function (e) {
|
|
that._timeoutDeactivate(e);
|
|
});
|
|
}
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this._userEvents.destroy();
|
|
},
|
|
events: [CLICK],
|
|
options: {
|
|
name: 'Button',
|
|
icon: '',
|
|
style: '',
|
|
badge: '',
|
|
clickOn: 'up',
|
|
enable: true
|
|
},
|
|
badge: function (value) {
|
|
var badge = this.badgeElement = this.badgeElement || createBadge(value).appendTo(this.element);
|
|
if (value || value === 0) {
|
|
badge.html(value);
|
|
return this;
|
|
}
|
|
if (value === false) {
|
|
badge.empty().remove();
|
|
this.badgeElement = false;
|
|
return this;
|
|
}
|
|
return badge.html();
|
|
},
|
|
enable: function (enable) {
|
|
var element = this.element;
|
|
if (typeof enable == 'undefined') {
|
|
enable = true;
|
|
}
|
|
this.options.enable = enable;
|
|
if (enable) {
|
|
element.removeAttr(DISABLED);
|
|
} else {
|
|
element.attr(DISABLED, DISABLED);
|
|
}
|
|
element.toggleClass(DISABLEDSTATE, !enable);
|
|
},
|
|
_timeoutDeactivate: function (e) {
|
|
if (!this.deactivateTimeoutID) {
|
|
this.deactivateTimeoutID = setTimeout(highlightButton, 500, this, e, false);
|
|
}
|
|
},
|
|
_activate: function (e) {
|
|
var activeElement = document.activeElement, nodeName = activeElement ? activeElement.nodeName : '';
|
|
if (this.options.enable) {
|
|
highlightButton(this, e, true);
|
|
if (nodeName == 'INPUT' || nodeName == 'TEXTAREA') {
|
|
activeElement.blur();
|
|
}
|
|
}
|
|
},
|
|
_release: function (e) {
|
|
var that = this;
|
|
if (e.which > 1) {
|
|
return;
|
|
}
|
|
if (!that.options.enable) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (that.trigger(CLICK, {
|
|
target: $(e.target),
|
|
button: that.element
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_style: function () {
|
|
var style = this.options.style, element = this.element, styles;
|
|
if (style) {
|
|
styles = style.split(' ');
|
|
$.each(styles, function () {
|
|
element.addClass('km-' + this);
|
|
});
|
|
}
|
|
},
|
|
_wrap: function () {
|
|
var that = this, icon = that.options.icon, badge = that.options.badge, iconSpan = '<span class="km-icon km-' + icon, element = that.element.addClass('km-button'), span = element.children('span:not(.km-icon)').addClass('km-text'), image = element.find('img').addClass('km-image');
|
|
if (!span[0] && element.html()) {
|
|
span = element.wrapInner('<span class="km-text" />').children('span.km-text');
|
|
}
|
|
if (!image[0] && icon) {
|
|
if (!span[0]) {
|
|
iconSpan += ' km-notext';
|
|
}
|
|
that.iconElement = element.prepend($(iconSpan + '" />'));
|
|
}
|
|
if (badge || badge === 0) {
|
|
that.badgeElement = createBadge(badge).appendTo(element);
|
|
}
|
|
}
|
|
});
|
|
var BackButton = Button.extend({
|
|
options: {
|
|
name: 'BackButton',
|
|
style: 'back'
|
|
},
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Button.fn.init.call(that, element, options);
|
|
if (typeof that.element.attr('href') === 'undefined') {
|
|
that.element.attr('href', '#:back');
|
|
}
|
|
}
|
|
});
|
|
var DetailButton = Button.extend({
|
|
options: {
|
|
name: 'DetailButton',
|
|
style: ''
|
|
},
|
|
init: function (element, options) {
|
|
Button.fn.init.call(this, element, options);
|
|
},
|
|
_style: function () {
|
|
var style = this.options.style + ' detail', element = this.element;
|
|
if (style) {
|
|
var styles = style.split(' ');
|
|
$.each(styles, function () {
|
|
element.addClass('km-' + this);
|
|
});
|
|
}
|
|
},
|
|
_wrap: function () {
|
|
var that = this, icon = that.options.icon, iconSpan = '<span class="km-icon km-' + icon, element = that.element, span = element.children('span'), image = element.find('img').addClass('km-image');
|
|
if (!image[0] && icon) {
|
|
if (!span[0]) {
|
|
iconSpan += ' km-notext';
|
|
}
|
|
element.prepend($(iconSpan + '" />'));
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(Button);
|
|
ui.plugin(BackButton);
|
|
ui.plugin(DetailButton);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.buttongroup', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.buttongroup',
|
|
name: 'ButtonGroup',
|
|
category: 'mobile',
|
|
description: 'The Kendo mobile ButtonGroup widget is a linear set of grouped buttons.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, ACTIVE = 'km-state-active', DISABLE = 'km-state-disabled', SELECT = 'select', SELECTOR = 'li:not(.' + ACTIVE + ')';
|
|
function createBadge(value) {
|
|
return $('<span class="km-badge">' + value + '</span>');
|
|
}
|
|
var ButtonGroup = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.element.addClass('km-buttongroup').find('li').each(that._button);
|
|
that.element.on(that.options.selectOn, SELECTOR, '_select');
|
|
that._enable = true;
|
|
that.select(that.options.index);
|
|
if (!that.options.enable) {
|
|
that._enable = false;
|
|
that.wrapper.addClass(DISABLE);
|
|
}
|
|
},
|
|
events: [SELECT],
|
|
options: {
|
|
name: 'ButtonGroup',
|
|
selectOn: 'down',
|
|
index: -1,
|
|
enable: true
|
|
},
|
|
current: function () {
|
|
return this.element.find('.' + ACTIVE);
|
|
},
|
|
select: function (li) {
|
|
var that = this, index = -1;
|
|
if (li === undefined || li === -1 || !that._enable || $(li).is('.' + DISABLE)) {
|
|
return;
|
|
}
|
|
that.current().removeClass(ACTIVE);
|
|
if (typeof li === 'number') {
|
|
index = li;
|
|
li = $(that.element[0].children[li]);
|
|
} else if (li.nodeType) {
|
|
li = $(li);
|
|
index = li.index();
|
|
}
|
|
li.addClass(ACTIVE);
|
|
that.selectedIndex = index;
|
|
},
|
|
badge: function (item, value) {
|
|
var buttongroup = this.element, badge;
|
|
if (!isNaN(item)) {
|
|
item = buttongroup.children().get(item);
|
|
}
|
|
item = buttongroup.find(item);
|
|
badge = $(item.children('.km-badge')[0] || createBadge(value).appendTo(item));
|
|
if (value || value === 0) {
|
|
badge.html(value);
|
|
return this;
|
|
}
|
|
if (value === false) {
|
|
badge.empty().remove();
|
|
return this;
|
|
}
|
|
return badge.html();
|
|
},
|
|
enable: function (enable) {
|
|
var wrapper = this.wrapper;
|
|
if (typeof enable == 'undefined') {
|
|
enable = true;
|
|
}
|
|
if (enable) {
|
|
wrapper.removeClass(DISABLE);
|
|
} else {
|
|
wrapper.addClass(DISABLE);
|
|
}
|
|
this._enable = this.options.enable = enable;
|
|
},
|
|
_button: function () {
|
|
var button = $(this).addClass('km-button'), icon = kendo.attrValue(button, 'icon'), badge = kendo.attrValue(button, 'badge'), span = button.children('span'), image = button.find('img').addClass('km-image');
|
|
if (!span[0]) {
|
|
span = button.wrapInner('<span/>').children('span');
|
|
}
|
|
span.addClass('km-text');
|
|
if (!image[0] && icon) {
|
|
button.prepend($('<span class="km-icon km-' + icon + '"/>'));
|
|
}
|
|
if (badge || badge === 0) {
|
|
createBadge(badge).appendTo(button);
|
|
}
|
|
},
|
|
_select: function (e) {
|
|
if (e.which > 1 || e.isDefaultPrevented() || !this._enable) {
|
|
return;
|
|
}
|
|
this.select(e.currentTarget);
|
|
this.trigger(SELECT, { index: this.selectedIndex });
|
|
}
|
|
});
|
|
ui.plugin(ButtonGroup);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.collapsible', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.collapsible',
|
|
name: 'Collapsible',
|
|
category: 'mobile',
|
|
description: 'The Kendo mobile Collapsible widget provides ability for creating collapsible blocks of content.',
|
|
depends: [
|
|
'core',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, COLLAPSIBLE = 'km-collapsible', HEADER = 'km-collapsible-header', CONTENT = 'km-collapsible-content', INSET = 'km-collapsibleinset', HEADER_WRAPPER = '<div data-role=\'collapsible-header\' class=\'' + HEADER + '\'></div>', CONTENT_WRAPPER = '<div data-role=\'collapsible-content\' class=\'' + CONTENT + '\'></div>', COLLAPSED = 'km-collapsed', EXPANDED = 'km-expanded', ANIMATED = 'km-animated', LEFT = 'left', EXAPND = 'expand', COLLAPSE = 'collapse';
|
|
var Collapsible = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, container = $(element);
|
|
Widget.fn.init.call(that, container, options);
|
|
container.addClass(COLLAPSIBLE);
|
|
that._buildHeader();
|
|
that.content = container.children().not(that.header).wrapAll(CONTENT_WRAPPER).parent();
|
|
that._userEvents = new kendo.UserEvents(that.header, {
|
|
fastTap: true,
|
|
tap: function () {
|
|
that.toggle();
|
|
}
|
|
});
|
|
container.addClass(that.options.collapsed ? COLLAPSED : EXPANDED);
|
|
if (that.options.inset) {
|
|
container.addClass(INSET);
|
|
}
|
|
if (that.options.animation) {
|
|
that.content.addClass(ANIMATED);
|
|
that.content.height(0);
|
|
if (that.options.collapsed) {
|
|
that.content.hide();
|
|
}
|
|
} else if (that.options.collapsed) {
|
|
that.content.hide();
|
|
}
|
|
},
|
|
events: [
|
|
EXAPND,
|
|
COLLAPSE
|
|
],
|
|
options: {
|
|
name: 'Collapsible',
|
|
collapsed: true,
|
|
collapseIcon: 'arrow-n',
|
|
expandIcon: 'arrow-s',
|
|
iconPosition: LEFT,
|
|
animation: true,
|
|
inset: false
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this._userEvents.destroy();
|
|
},
|
|
expand: function (instant) {
|
|
var icon = this.options.collapseIcon, content = this.content, ios = kendo.support.mobileOS.ios;
|
|
if (!this.trigger(EXAPND)) {
|
|
if (icon) {
|
|
this.header.find('.km-icon').removeClass().addClass('km-icon km-' + icon);
|
|
}
|
|
this.element.removeClass(COLLAPSED).addClass(EXPANDED);
|
|
if (this.options.animation && !instant) {
|
|
content.off('transitionend');
|
|
content.show();
|
|
if (ios) {
|
|
content.removeClass(ANIMATED);
|
|
}
|
|
content.height(this._getContentHeight());
|
|
if (ios) {
|
|
content.addClass(ANIMATED);
|
|
}
|
|
kendo.resize(content);
|
|
} else {
|
|
content.show();
|
|
}
|
|
}
|
|
},
|
|
collapse: function (instant) {
|
|
var icon = this.options.expandIcon, content = this.content;
|
|
if (!this.trigger(COLLAPSE)) {
|
|
if (icon) {
|
|
this.header.find('.km-icon').removeClass().addClass('km-icon km-' + icon);
|
|
}
|
|
this.element.removeClass(EXPANDED).addClass(COLLAPSED);
|
|
if (this.options.animation && !instant) {
|
|
content.one('transitionend', function () {
|
|
content.hide();
|
|
});
|
|
content.height(0);
|
|
} else {
|
|
content.hide();
|
|
}
|
|
}
|
|
},
|
|
toggle: function (instant) {
|
|
if (this.isCollapsed()) {
|
|
this.expand(instant);
|
|
} else {
|
|
this.collapse(instant);
|
|
}
|
|
},
|
|
isCollapsed: function () {
|
|
return this.element.hasClass(COLLAPSED);
|
|
},
|
|
resize: function () {
|
|
if (!this.isCollapsed() && this.options.animation) {
|
|
this.content.height(this._getContentHeight());
|
|
}
|
|
},
|
|
_buildHeader: function () {
|
|
var header = this.element.children(':header').wrapAll(HEADER_WRAPPER), iconSpan = $('<span class="km-icon"/>'), icon = this.options.collapsed ? this.options.expandIcon : this.options.collapseIcon, iconPosition = this.options.iconPosition;
|
|
if (icon) {
|
|
header.prepend(iconSpan);
|
|
iconSpan.addClass('km-' + icon);
|
|
}
|
|
this.header = header.parent();
|
|
this.header.addClass('km-icon-' + iconPosition);
|
|
},
|
|
_getContentHeight: function () {
|
|
var style = this.content.attr('style'), height;
|
|
this.content.css({
|
|
position: 'absolute',
|
|
visibility: 'hidden',
|
|
height: 'auto'
|
|
});
|
|
height = this.content.height();
|
|
this.content.attr('style', style ? style : '');
|
|
return height;
|
|
}
|
|
});
|
|
ui.plugin(Collapsible);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.listview', [
|
|
'kendo.data',
|
|
'kendo.userevents',
|
|
'kendo.mobile.button'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.listview',
|
|
name: 'ListView',
|
|
category: 'mobile',
|
|
description: 'The Kendo Mobile ListView widget is used to display flat or grouped list of items.',
|
|
depends: [
|
|
'data',
|
|
'userevents',
|
|
'mobile.button'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, Node = window.Node, mobile = kendo.mobile, ui = mobile.ui, DataSource = kendo.data.DataSource, Widget = ui.DataBoundWidget, ITEM_SELECTOR = '.km-list > li, > li:not(.km-group-container)', HIGHLIGHT_SELECTOR = '.km-listview-link, .km-listview-label', ICON_SELECTOR = '[' + kendo.attr('icon') + ']', proxy = $.proxy, attrValue = kendo.attrValue, GROUP_CLASS = 'km-group-title', ACTIVE_CLASS = 'km-state-active', GROUP_WRAPPER = '<div class="' + GROUP_CLASS + '"><div class="km-text"></div></div>', GROUP_TEMPLATE = kendo.template('<li><div class="' + GROUP_CLASS + '"><div class="km-text">#= this.headerTemplate(data) #</div></div><ul>#= kendo.render(this.template, data.items)#</ul></li>'), WRAPPER = '<div class="km-listview-wrapper" />', SEARCH_TEMPLATE = kendo.template('<form class="km-filter-form"><div class="km-filter-wrap"><input type="search" placeholder="#=placeholder#"/><a href="\\#" class="km-filter-reset" title="Clear"><span class="km-icon km-clear"></span><span class="km-text">Clear</span></a></div></form>'), NS = '.kendoMobileListView', STYLED = 'styled', DATABOUND = 'dataBound', DATABINDING = 'dataBinding', ITEM_CHANGE = 'itemChange', CLICK = 'click', CHANGE = 'change', PROGRESS = 'progress', FUNCTION = 'function', whitespaceRegExp = /^\s+$/, buttonRegExp = /button/;
|
|
function whitespace() {
|
|
return this.nodeType === Node.TEXT_NODE && this.nodeValue.match(whitespaceRegExp);
|
|
}
|
|
function addIcon(item, icon) {
|
|
if (icon && !item[0].querySelector('.km-icon')) {
|
|
item.prepend('<span class="km-icon km-' + icon + '"/>');
|
|
}
|
|
}
|
|
function enhanceItem(item) {
|
|
addIcon(item, attrValue(item, 'icon'));
|
|
addIcon(item, attrValue(item.children(ICON_SELECTOR), 'icon'));
|
|
}
|
|
function enhanceLinkItem(item) {
|
|
var parent = item.parent(), itemAndDetailButtons = item.add(parent.children(kendo.roleSelector('detailbutton'))), otherNodes = parent.contents().not(itemAndDetailButtons).not(whitespace);
|
|
if (otherNodes.length) {
|
|
return;
|
|
}
|
|
item.addClass('km-listview-link').attr(kendo.attr('role'), 'listview-link');
|
|
addIcon(item, attrValue(parent, 'icon'));
|
|
addIcon(item, attrValue(item, 'icon'));
|
|
}
|
|
function enhanceCheckBoxItem(label) {
|
|
if (!label[0].querySelector('input[type=checkbox],input[type=radio]')) {
|
|
return;
|
|
}
|
|
var item = label.parent();
|
|
if (item.contents().not(label).not(function () {
|
|
return this.nodeType == 3;
|
|
})[0]) {
|
|
return;
|
|
}
|
|
label.addClass('km-listview-label');
|
|
label.children('[type=checkbox],[type=radio]').addClass('km-widget km-icon km-check');
|
|
}
|
|
function putAt(element, top) {
|
|
$(element).css('transform', 'translate3d(0px, ' + top + 'px, 0px)');
|
|
}
|
|
var HeaderFixer = kendo.Class.extend({
|
|
init: function (listView) {
|
|
var scroller = listView.scroller();
|
|
if (!scroller) {
|
|
return;
|
|
}
|
|
this.options = listView.options;
|
|
this.element = listView.element;
|
|
this.scroller = listView.scroller();
|
|
this._shouldFixHeaders();
|
|
var headerFixer = this;
|
|
var cacheHeaders = function () {
|
|
headerFixer._cacheHeaders();
|
|
};
|
|
listView.bind('resize', cacheHeaders);
|
|
listView.bind(STYLED, cacheHeaders);
|
|
listView.bind(DATABOUND, cacheHeaders);
|
|
scroller.bind('scroll', function (e) {
|
|
headerFixer._fixHeader(e);
|
|
});
|
|
},
|
|
_fixHeader: function (e) {
|
|
if (!this.fixedHeaders) {
|
|
return;
|
|
}
|
|
var i = 0, scroller = this.scroller, headers = this.headers, scrollTop = e.scrollTop, headerPair, offset, header;
|
|
do {
|
|
headerPair = headers[i++];
|
|
if (!headerPair) {
|
|
header = $('<div />');
|
|
break;
|
|
}
|
|
offset = headerPair.offset;
|
|
header = headerPair.header;
|
|
} while (offset + 1 > scrollTop);
|
|
if (this.currentHeader != i) {
|
|
scroller.fixedContainer.html(header.clone());
|
|
this.currentHeader = i;
|
|
}
|
|
},
|
|
_shouldFixHeaders: function () {
|
|
this.fixedHeaders = this.options.type === 'group' && this.options.fixedHeaders;
|
|
},
|
|
_cacheHeaders: function () {
|
|
this._shouldFixHeaders();
|
|
if (!this.fixedHeaders) {
|
|
return;
|
|
}
|
|
var headers = [], offset = this.scroller.scrollTop;
|
|
this.element.find('.' + GROUP_CLASS).each(function (_, header) {
|
|
header = $(header);
|
|
headers.unshift({
|
|
offset: header.position().top + offset,
|
|
header: header
|
|
});
|
|
});
|
|
this.headers = headers;
|
|
this._fixHeader({ scrollTop: offset });
|
|
}
|
|
});
|
|
var DEFAULT_PULL_PARAMETERS = function () {
|
|
return { page: 1 };
|
|
};
|
|
var RefreshHandler = kendo.Class.extend({
|
|
init: function (listView) {
|
|
var handler = this, options = listView.options, scroller = listView.scroller(), pullParameters = options.pullParameters || DEFAULT_PULL_PARAMETERS;
|
|
this.listView = listView;
|
|
this.scroller = scroller;
|
|
listView.bind('_dataSource', function (e) {
|
|
handler.setDataSource(e.dataSource);
|
|
});
|
|
scroller.setOptions({
|
|
pullToRefresh: true,
|
|
pull: function () {
|
|
if (!handler._pulled) {
|
|
handler._pulled = true;
|
|
handler.dataSource.read(pullParameters.call(listView, handler._first));
|
|
}
|
|
},
|
|
messages: {
|
|
pullTemplate: options.messages.pullTemplate,
|
|
releaseTemplate: options.messages.releaseTemplate,
|
|
refreshTemplate: options.messages.refreshTemplate
|
|
}
|
|
});
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var handler = this;
|
|
this._first = dataSource.view()[0];
|
|
this.dataSource = dataSource;
|
|
dataSource.bind('change', function () {
|
|
handler._change();
|
|
});
|
|
dataSource.bind('error', function () {
|
|
handler._change();
|
|
});
|
|
},
|
|
_change: function () {
|
|
var scroller = this.scroller, dataSource = this.dataSource;
|
|
if (this._pulled) {
|
|
scroller.pullHandled();
|
|
}
|
|
if (this._pulled || !this._first) {
|
|
var view = dataSource.view();
|
|
if (view[0]) {
|
|
this._first = view[0];
|
|
}
|
|
}
|
|
this._pulled = false;
|
|
}
|
|
});
|
|
var VirtualList = kendo.Observable.extend({
|
|
init: function (options) {
|
|
var list = this;
|
|
kendo.Observable.fn.init.call(list);
|
|
list.buffer = options.buffer;
|
|
list.height = options.height;
|
|
list.item = options.item;
|
|
list.items = [];
|
|
list.footer = options.footer;
|
|
list.buffer.bind('reset', function () {
|
|
list.refresh();
|
|
});
|
|
},
|
|
refresh: function () {
|
|
var buffer = this.buffer, items = this.items, endReached = false;
|
|
while (items.length) {
|
|
items.pop().destroy();
|
|
}
|
|
this.offset = buffer.offset;
|
|
var itemConstructor = this.item, prevItem, item;
|
|
for (var idx = 0; idx < buffer.viewSize; idx++) {
|
|
if (idx === buffer.total()) {
|
|
endReached = true;
|
|
break;
|
|
}
|
|
item = itemConstructor(this.content(this.offset + items.length));
|
|
item.below(prevItem);
|
|
prevItem = item;
|
|
items.push(item);
|
|
}
|
|
this.itemCount = items.length;
|
|
this.trigger('reset');
|
|
this._resize();
|
|
if (endReached) {
|
|
this.trigger('endReached');
|
|
}
|
|
},
|
|
totalHeight: function () {
|
|
if (!this.items[0]) {
|
|
return 0;
|
|
}
|
|
var list = this, items = list.items, top = items[0].top, bottom = items[items.length - 1].bottom, averageItemHeight = (bottom - top) / list.itemCount, remainingItemsCount = list.buffer.length - list.offset - list.itemCount;
|
|
return (this.footer ? this.footer.height : 0) + bottom + remainingItemsCount * averageItemHeight;
|
|
},
|
|
batchUpdate: function (top) {
|
|
var height = this.height(), items = this.items, item, initialOffset = this.offset;
|
|
if (!items[0]) {
|
|
return;
|
|
}
|
|
if (this.lastDirection) {
|
|
while (items[items.length - 1].bottom > top + height * 2) {
|
|
if (this.offset === 0) {
|
|
break;
|
|
}
|
|
this.offset--;
|
|
item = items.pop();
|
|
item.update(this.content(this.offset));
|
|
item.above(items[0]);
|
|
items.unshift(item);
|
|
}
|
|
} else {
|
|
while (items[0].top < top - height) {
|
|
var nextIndex = this.offset + this.itemCount;
|
|
if (nextIndex === this.buffer.total()) {
|
|
this.trigger('endReached');
|
|
break;
|
|
}
|
|
if (nextIndex === this.buffer.length) {
|
|
break;
|
|
}
|
|
item = items.shift();
|
|
item.update(this.content(this.offset + this.itemCount));
|
|
item.below(items[items.length - 1]);
|
|
items.push(item);
|
|
this.offset++;
|
|
}
|
|
}
|
|
if (initialOffset !== this.offset) {
|
|
this._resize();
|
|
}
|
|
},
|
|
update: function (top) {
|
|
var list = this, items = this.items, item, firstItem, lastItem, height = this.height(), itemCount = this.itemCount, padding = height / 2, up = (this.lastTop || 0) > top, topBorder = top - padding, bottomBorder = top + height + padding;
|
|
if (!items[0]) {
|
|
return;
|
|
}
|
|
this.lastTop = top;
|
|
this.lastDirection = up;
|
|
if (up) {
|
|
if (items[0].top > topBorder && items[items.length - 1].bottom > bottomBorder + padding && this.offset > 0) {
|
|
this.offset--;
|
|
item = items.pop();
|
|
firstItem = items[0];
|
|
item.update(this.content(this.offset));
|
|
items.unshift(item);
|
|
item.above(firstItem);
|
|
list._resize();
|
|
}
|
|
} else {
|
|
if (items[items.length - 1].bottom < bottomBorder && items[0].top < topBorder - padding) {
|
|
var nextIndex = this.offset + itemCount;
|
|
if (nextIndex === this.buffer.total()) {
|
|
this.trigger('endReached');
|
|
} else if (nextIndex !== this.buffer.length) {
|
|
item = items.shift();
|
|
lastItem = items[items.length - 1];
|
|
items.push(item);
|
|
item.update(this.content(this.offset + this.itemCount));
|
|
list.offset++;
|
|
item.below(lastItem);
|
|
list._resize();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
content: function (index) {
|
|
return this.buffer.at(index);
|
|
},
|
|
destroy: function () {
|
|
this.unbind();
|
|
},
|
|
_resize: function () {
|
|
var items = this.items, top = 0, bottom = 0, firstItem = items[0], lastItem = items[items.length - 1];
|
|
if (firstItem) {
|
|
top = firstItem.top;
|
|
bottom = lastItem.bottom;
|
|
}
|
|
this.trigger('resize', {
|
|
top: top,
|
|
bottom: bottom
|
|
});
|
|
if (this.footer) {
|
|
this.footer.below(lastItem);
|
|
}
|
|
}
|
|
});
|
|
kendo.mobile.ui.VirtualList = VirtualList;
|
|
var VirtualListViewItem = kendo.Class.extend({
|
|
init: function (listView, dataItem) {
|
|
var element = listView.append([dataItem], true)[0], height = element.offsetHeight;
|
|
$.extend(this, {
|
|
top: 0,
|
|
element: element,
|
|
listView: listView,
|
|
height: height,
|
|
bottom: height
|
|
});
|
|
},
|
|
update: function (dataItem) {
|
|
this.element = this.listView.setDataItem(this.element, dataItem);
|
|
},
|
|
above: function (item) {
|
|
if (item) {
|
|
this.height = this.element.offsetHeight;
|
|
this.top = item.top - this.height;
|
|
this.bottom = item.top;
|
|
putAt(this.element, this.top);
|
|
}
|
|
},
|
|
below: function (item) {
|
|
if (item) {
|
|
this.height = this.element.offsetHeight;
|
|
this.top = item.bottom;
|
|
this.bottom = this.top + this.height;
|
|
putAt(this.element, this.top);
|
|
}
|
|
},
|
|
destroy: function () {
|
|
kendo.destroy(this.element);
|
|
$(this.element).remove();
|
|
}
|
|
});
|
|
var LOAD_ICON = '<div><span class="km-icon"></span><span class="km-loading-left"></span><span class="km-loading-right"></span></div>';
|
|
var VirtualListViewLoadingIndicator = kendo.Class.extend({
|
|
init: function (listView) {
|
|
this.element = $('<li class="km-load-more km-scroller-refresh" style="display: none"></li>').appendTo(listView.element);
|
|
this._loadIcon = $(LOAD_ICON).appendTo(this.element);
|
|
},
|
|
enable: function () {
|
|
this.element.show();
|
|
this.height = this.element.outerHeight(true);
|
|
},
|
|
disable: function () {
|
|
this.element.hide();
|
|
this.height = 0;
|
|
},
|
|
below: function (item) {
|
|
if (item) {
|
|
this.top = item.bottom;
|
|
this.bottom = this.height + this.top;
|
|
putAt(this.element, this.top);
|
|
}
|
|
}
|
|
});
|
|
var VirtualListViewPressToLoadMore = VirtualListViewLoadingIndicator.extend({
|
|
init: function (listView, buffer) {
|
|
this._loadIcon = $(LOAD_ICON).hide();
|
|
this._loadButton = $('<a class="km-load">' + listView.options.messages.loadMoreText + '</a>').hide();
|
|
this.element = $('<li class="km-load-more" style="display: none"></li>').append(this._loadIcon).append(this._loadButton).appendTo(listView.element);
|
|
var loadMore = this;
|
|
this._loadButton.kendoMobileButton().data('kendoMobileButton').bind('click', function () {
|
|
loadMore._hideShowButton();
|
|
buffer.next();
|
|
});
|
|
buffer.bind('resize', function () {
|
|
loadMore._showLoadButton();
|
|
});
|
|
this.height = this.element.outerHeight(true);
|
|
this.disable();
|
|
},
|
|
_hideShowButton: function () {
|
|
this._loadButton.hide();
|
|
this.element.addClass('km-scroller-refresh');
|
|
this._loadIcon.css('display', 'block');
|
|
},
|
|
_showLoadButton: function () {
|
|
this._loadButton.show();
|
|
this.element.removeClass('km-scroller-refresh');
|
|
this._loadIcon.hide();
|
|
}
|
|
});
|
|
var VirtualListViewItemBinder = kendo.Class.extend({
|
|
init: function (listView) {
|
|
var binder = this;
|
|
this.chromeHeight = listView.wrapper.children().not(listView.element).outerHeight() || 0;
|
|
this.listView = listView;
|
|
this.scroller = listView.scroller();
|
|
this.options = listView.options;
|
|
listView.bind('_dataSource', function (e) {
|
|
binder.setDataSource(e.dataSource, e.empty);
|
|
});
|
|
listView.bind('resize', function () {
|
|
if (!binder.list.items.length) {
|
|
return;
|
|
}
|
|
binder.scroller.reset();
|
|
binder.buffer.range(0);
|
|
binder.list.refresh();
|
|
});
|
|
this.scroller.makeVirtual();
|
|
this.scroller.bind('scroll', function (e) {
|
|
binder.list.update(e.scrollTop);
|
|
});
|
|
this.scroller.bind('scrollEnd', function (e) {
|
|
binder.list.batchUpdate(e.scrollTop);
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this.list.unbind();
|
|
this.buffer.unbind();
|
|
},
|
|
setDataSource: function (dataSource, empty) {
|
|
var binder = this, options = this.options, listView = this.listView, scroller = listView.scroller(), pressToLoadMore = options.loadMore, pageSize, buffer, footer;
|
|
this.dataSource = dataSource;
|
|
pageSize = dataSource.pageSize() || options.virtualViewSize;
|
|
if (!pageSize && !empty) {
|
|
throw new Error('the DataSource does not have page size configured. Page Size setting is mandatory for the mobile listview virtual scrolling to work as expected.');
|
|
}
|
|
if (this.buffer) {
|
|
this.buffer.destroy();
|
|
}
|
|
buffer = new kendo.data.Buffer(dataSource, Math.floor(pageSize / 2), pressToLoadMore);
|
|
if (pressToLoadMore) {
|
|
footer = new VirtualListViewPressToLoadMore(listView, buffer);
|
|
} else {
|
|
footer = new VirtualListViewLoadingIndicator(listView);
|
|
}
|
|
if (this.list) {
|
|
this.list.destroy();
|
|
}
|
|
var list = new VirtualList({
|
|
buffer: buffer,
|
|
footer: footer,
|
|
item: function (dataItem) {
|
|
return new VirtualListViewItem(listView, dataItem);
|
|
},
|
|
height: function () {
|
|
return scroller.height();
|
|
}
|
|
});
|
|
list.bind('resize', function () {
|
|
binder.updateScrollerSize();
|
|
listView.updateSize();
|
|
});
|
|
list.bind('reset', function () {
|
|
binder.footer.enable();
|
|
});
|
|
list.bind('endReached', function () {
|
|
footer.disable();
|
|
binder.updateScrollerSize();
|
|
});
|
|
buffer.bind('expand', function () {
|
|
list.lastDirection = false;
|
|
list.batchUpdate(scroller.scrollTop);
|
|
});
|
|
$.extend(this, {
|
|
buffer: buffer,
|
|
scroller: scroller,
|
|
list: list,
|
|
footer: footer
|
|
});
|
|
},
|
|
updateScrollerSize: function () {
|
|
this.scroller.virtualSize(0, this.list.totalHeight() + this.chromeHeight);
|
|
},
|
|
refresh: function () {
|
|
this.list.refresh();
|
|
},
|
|
reset: function () {
|
|
this.buffer.range(0);
|
|
this.list.refresh();
|
|
}
|
|
});
|
|
var ListViewItemBinder = kendo.Class.extend({
|
|
init: function (listView) {
|
|
var binder = this;
|
|
this.listView = listView;
|
|
this.options = listView.options;
|
|
var itemBinder = this;
|
|
this._refreshHandler = function (e) {
|
|
itemBinder.refresh(e);
|
|
};
|
|
this._progressHandler = function () {
|
|
listView.showLoading();
|
|
};
|
|
listView.bind('_dataSource', function (e) {
|
|
binder.setDataSource(e.dataSource);
|
|
});
|
|
},
|
|
destroy: function () {
|
|
this._unbindDataSource();
|
|
},
|
|
reset: function () {
|
|
},
|
|
refresh: function (e) {
|
|
var action = e && e.action, dataItems = e && e.items, listView = this.listView, dataSource = this.dataSource, prependOnRefresh = this.options.appendOnRefresh, view = dataSource.view(), groups = dataSource.group(), groupedMode = groups && groups[0], item;
|
|
if (action === 'itemchange') {
|
|
if (!listView._hasBindingTarget()) {
|
|
item = listView.findByDataItem(dataItems)[0];
|
|
if (item) {
|
|
listView.setDataItem(item, dataItems[0]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
var removedItems, addedItems, addedDataItems;
|
|
var adding = action === 'add' && !groupedMode || prependOnRefresh && !listView._filter;
|
|
var removing = action === 'remove' && !groupedMode;
|
|
if (adding) {
|
|
removedItems = [];
|
|
} else if (removing) {
|
|
removedItems = listView.findByDataItem(dataItems);
|
|
}
|
|
if (listView.trigger(DATABINDING, {
|
|
action: action || 'rebind',
|
|
items: dataItems,
|
|
removedItems: removedItems,
|
|
index: e && e.index
|
|
})) {
|
|
if (this._shouldShowLoading()) {
|
|
listView.hideLoading();
|
|
}
|
|
return;
|
|
}
|
|
if (action === 'add' && !groupedMode) {
|
|
var index = view.indexOf(dataItems[0]);
|
|
if (index > -1) {
|
|
addedItems = listView.insertAt(dataItems, index);
|
|
addedDataItems = dataItems;
|
|
}
|
|
} else if (action === 'remove' && !groupedMode) {
|
|
addedItems = [];
|
|
listView.remove(dataItems);
|
|
} else if (groupedMode) {
|
|
listView.replaceGrouped(view);
|
|
} else if (prependOnRefresh && !listView._filter) {
|
|
addedItems = listView.prepend(view);
|
|
addedDataItems = view;
|
|
} else {
|
|
listView.replace(view);
|
|
}
|
|
if (this._shouldShowLoading()) {
|
|
listView.hideLoading();
|
|
}
|
|
listView.trigger(DATABOUND, {
|
|
ns: ui,
|
|
addedItems: addedItems,
|
|
addedDataItems: addedDataItems
|
|
});
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
if (this.dataSource) {
|
|
this._unbindDataSource();
|
|
}
|
|
this.dataSource = dataSource;
|
|
dataSource.bind(CHANGE, this._refreshHandler);
|
|
if (this._shouldShowLoading()) {
|
|
this.dataSource.bind(PROGRESS, this._progressHandler);
|
|
}
|
|
},
|
|
_unbindDataSource: function () {
|
|
this.dataSource.unbind(CHANGE, this._refreshHandler).unbind(PROGRESS, this._progressHandler);
|
|
},
|
|
_shouldShowLoading: function () {
|
|
var options = this.options;
|
|
return !options.pullToRefresh && !options.loadMore && !options.endlessScroll;
|
|
}
|
|
});
|
|
var ListViewFilter = kendo.Class.extend({
|
|
init: function (listView) {
|
|
var filter = this, filterable = listView.options.filterable, events = 'change paste', that = this;
|
|
this.listView = listView;
|
|
this.options = filterable;
|
|
listView.element.before(SEARCH_TEMPLATE({ placeholder: filterable.placeholder || 'Search...' }));
|
|
if (filterable.autoFilter !== false) {
|
|
events += ' keyup';
|
|
}
|
|
this.element = listView.wrapper.find('.km-search-form');
|
|
this.searchInput = listView.wrapper.find('input[type=search]').closest('form').on('submit' + NS, function (e) {
|
|
e.preventDefault();
|
|
}).end().on('focus' + NS, function () {
|
|
filter._oldFilter = filter.searchInput.val();
|
|
}).on(events.split(' ').join(NS + ' ') + NS, proxy(this._filterChange, this));
|
|
this.clearButton = listView.wrapper.find('.km-filter-reset').on(CLICK, proxy(this, '_clearFilter')).hide();
|
|
this._dataSourceChange = $.proxy(this._refreshInput, this);
|
|
listView.bind('_dataSource', function (e) {
|
|
e.dataSource.bind('change', that._dataSourceChange);
|
|
});
|
|
},
|
|
_refreshInput: function () {
|
|
var appliedFilters = this.listView.dataSource.filter();
|
|
var searchInput = this.listView._filter.searchInput;
|
|
if (!appliedFilters || appliedFilters.filters[0].field !== this.listView.options.filterable.field) {
|
|
searchInput.val('');
|
|
} else {
|
|
searchInput.val(appliedFilters.filters[0].value);
|
|
}
|
|
},
|
|
_search: function (expr) {
|
|
this._filter = true;
|
|
this.clearButton[expr ? 'show' : 'hide']();
|
|
this.listView.dataSource.filter(expr);
|
|
},
|
|
_filterChange: function (e) {
|
|
var filter = this;
|
|
if (e.type == 'paste' && this.options.autoFilter !== false) {
|
|
setTimeout(function () {
|
|
filter._applyFilter();
|
|
}, 1);
|
|
} else {
|
|
this._applyFilter();
|
|
}
|
|
},
|
|
_applyFilter: function () {
|
|
var options = this.options, value = this.searchInput.val(), expr = value.length ? {
|
|
field: options.field,
|
|
operator: options.operator || 'startswith',
|
|
ignoreCase: options.ignoreCase,
|
|
value: value
|
|
} : null;
|
|
if (value === this._oldFilter) {
|
|
return;
|
|
}
|
|
this._oldFilter = value;
|
|
this._search(expr);
|
|
},
|
|
_clearFilter: function (e) {
|
|
this.searchInput.val('');
|
|
this._search(null);
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
var ListView = Widget.extend({
|
|
init: function (element, options) {
|
|
var listView = this;
|
|
Widget.fn.init.call(this, element, options);
|
|
element = this.element;
|
|
options = this.options;
|
|
if (options.scrollTreshold) {
|
|
options.scrollThreshold = options.scrollTreshold;
|
|
}
|
|
element.on('down', HIGHLIGHT_SELECTOR, '_highlight').on('move up cancel', HIGHLIGHT_SELECTOR, '_dim');
|
|
this._userEvents = new kendo.UserEvents(element, {
|
|
fastTap: true,
|
|
filter: ITEM_SELECTOR,
|
|
allowSelection: true,
|
|
tap: function (e) {
|
|
listView._click(e);
|
|
}
|
|
});
|
|
element.css('-ms-touch-action', 'auto');
|
|
element.wrap(WRAPPER);
|
|
this.wrapper = this.element.parent();
|
|
this._headerFixer = new HeaderFixer(this);
|
|
this._itemsCache = {};
|
|
this._templates();
|
|
this.virtual = options.endlessScroll || options.loadMore;
|
|
this._style();
|
|
if (this.options.$angular && (this.virtual || this.options.pullToRefresh)) {
|
|
setTimeout($.proxy(this, '_start'));
|
|
} else {
|
|
this._start();
|
|
}
|
|
},
|
|
_start: function () {
|
|
var options = this.options;
|
|
if (this.options.filterable) {
|
|
this._filter = new ListViewFilter(this);
|
|
}
|
|
if (this.virtual) {
|
|
this._itemBinder = new VirtualListViewItemBinder(this);
|
|
} else {
|
|
this._itemBinder = new ListViewItemBinder(this);
|
|
}
|
|
if (this.options.pullToRefresh) {
|
|
this._pullToRefreshHandler = new RefreshHandler(this);
|
|
}
|
|
this.setDataSource(options.dataSource);
|
|
this._enhanceItems(this.items());
|
|
kendo.notify(this, ui);
|
|
},
|
|
events: [
|
|
CLICK,
|
|
DATABINDING,
|
|
DATABOUND,
|
|
ITEM_CHANGE
|
|
],
|
|
options: {
|
|
name: 'ListView',
|
|
style: '',
|
|
type: 'flat',
|
|
autoBind: true,
|
|
fixedHeaders: false,
|
|
template: '#:data#',
|
|
headerTemplate: '<span class="km-text">#:value#</span>',
|
|
appendOnRefresh: false,
|
|
loadMore: false,
|
|
endlessScroll: false,
|
|
scrollThreshold: 30,
|
|
pullToRefresh: false,
|
|
messages: {
|
|
loadMoreText: 'Press to load more',
|
|
pullTemplate: 'Pull to refresh',
|
|
releaseTemplate: 'Release to refresh',
|
|
refreshTemplate: 'Refreshing'
|
|
},
|
|
pullOffset: 140,
|
|
filterable: false,
|
|
virtualViewSize: null
|
|
},
|
|
refresh: function () {
|
|
this._itemBinder.refresh();
|
|
},
|
|
reset: function () {
|
|
this._itemBinder.reset();
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
var emptyDataSource = !dataSource;
|
|
this.dataSource = DataSource.create(dataSource);
|
|
this.trigger('_dataSource', {
|
|
dataSource: this.dataSource,
|
|
empty: emptyDataSource
|
|
});
|
|
if (this.options.autoBind && !emptyDataSource) {
|
|
this.items().remove();
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.element);
|
|
this._userEvents.destroy();
|
|
if (this._itemBinder) {
|
|
this._itemBinder.destroy();
|
|
}
|
|
this.element.unwrap();
|
|
delete this.element;
|
|
delete this.wrapper;
|
|
delete this._userEvents;
|
|
},
|
|
items: function () {
|
|
if (this.options.type === 'group') {
|
|
return this.element.find('.km-list').children();
|
|
} else {
|
|
return this.element.children().not('.km-load-more');
|
|
}
|
|
},
|
|
scroller: function () {
|
|
if (!this._scrollerInstance) {
|
|
this._scrollerInstance = this.element.closest('.km-scroll-wrapper').data('kendoMobileScroller');
|
|
}
|
|
return this._scrollerInstance;
|
|
},
|
|
showLoading: function () {
|
|
var view = this.view();
|
|
if (view && view.loader) {
|
|
view.loader.show();
|
|
}
|
|
},
|
|
hideLoading: function () {
|
|
var view = this.view();
|
|
if (view && view.loader) {
|
|
view.loader.hide();
|
|
}
|
|
},
|
|
insertAt: function (dataItems, index, triggerChange) {
|
|
var listView = this;
|
|
return listView._renderItems(dataItems, function (items) {
|
|
if (index === 0) {
|
|
listView.element.prepend(items);
|
|
} else if (index === -1) {
|
|
listView.element.append(items);
|
|
} else {
|
|
listView.items().eq(index - 1).after(items);
|
|
}
|
|
if (triggerChange) {
|
|
for (var i = 0; i < items.length; i++) {
|
|
listView.trigger(ITEM_CHANGE, {
|
|
item: items.eq(i),
|
|
data: dataItems[i],
|
|
ns: ui
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
append: function (dataItems, triggerChange) {
|
|
return this.insertAt(dataItems, -1, triggerChange);
|
|
},
|
|
prepend: function (dataItems, triggerChange) {
|
|
return this.insertAt(dataItems, 0, triggerChange);
|
|
},
|
|
replace: function (dataItems) {
|
|
this.options.type = 'flat';
|
|
this._angularItems('cleanup');
|
|
this.element.empty();
|
|
this._userEvents.cancel();
|
|
this._style();
|
|
return this.insertAt(dataItems, 0);
|
|
},
|
|
replaceGrouped: function (groups) {
|
|
this.options.type = 'group';
|
|
this._angularItems('cleanup');
|
|
this.element.empty();
|
|
var items = $(kendo.render(this.groupTemplate, groups));
|
|
this._enhanceItems(items.children('ul').children('li'));
|
|
this.element.append(items);
|
|
mobile.init(items);
|
|
this._style();
|
|
this._angularItems('compile');
|
|
},
|
|
remove: function (dataItems) {
|
|
var items = this.findByDataItem(dataItems);
|
|
this.angular('cleanup', function () {
|
|
return { elements: items };
|
|
});
|
|
kendo.destroy(items);
|
|
items.remove();
|
|
},
|
|
findByDataItem: function (dataItems) {
|
|
var selectors = [];
|
|
for (var idx = 0, length = dataItems.length; idx < length; idx++) {
|
|
selectors[idx] = '[data-' + kendo.ns + 'uid=' + dataItems[idx].uid + ']';
|
|
}
|
|
return this.element.find(selectors.join(','));
|
|
},
|
|
setDataItem: function (item, dataItem) {
|
|
var listView = this, replaceItem = function (items) {
|
|
var newItem = $(items[0]);
|
|
kendo.destroy(item);
|
|
listView.angular('cleanup', function () {
|
|
return { elements: [$(item)] };
|
|
});
|
|
$(item).replaceWith(newItem);
|
|
listView.trigger(ITEM_CHANGE, {
|
|
item: newItem,
|
|
data: dataItem,
|
|
ns: ui
|
|
});
|
|
};
|
|
return this._renderItems([dataItem], replaceItem)[0];
|
|
},
|
|
updateSize: function () {
|
|
this._size = this.getSize();
|
|
},
|
|
_renderItems: function (dataItems, callback) {
|
|
var items = $(kendo.render(this.template, dataItems));
|
|
callback(items);
|
|
this.angular('compile', function () {
|
|
return {
|
|
elements: items,
|
|
data: dataItems.map(function (data) {
|
|
return { dataItem: data };
|
|
})
|
|
};
|
|
});
|
|
mobile.init(items);
|
|
this._enhanceItems(items);
|
|
return items;
|
|
},
|
|
_dim: function (e) {
|
|
this._toggle(e, false);
|
|
},
|
|
_highlight: function (e) {
|
|
this._toggle(e, true);
|
|
},
|
|
_toggle: function (e, highlight) {
|
|
if (e.which > 1) {
|
|
return;
|
|
}
|
|
var clicked = $(e.currentTarget), item = clicked.parent(), role = attrValue(clicked, 'role') || '', plainItem = !role.match(buttonRegExp), prevented = e.isDefaultPrevented();
|
|
if (plainItem) {
|
|
item.toggleClass(ACTIVE_CLASS, highlight && !prevented);
|
|
}
|
|
},
|
|
_templates: function () {
|
|
var template = this.options.template, headerTemplate = this.options.headerTemplate, dataIDAttribute = ' data-uid="#=arguments[0].uid || ""#"', templateProxy = {}, groupTemplateProxy = {};
|
|
if (typeof template === FUNCTION) {
|
|
templateProxy.template = template;
|
|
template = '#=this.template(data)#';
|
|
}
|
|
this.template = proxy(kendo.template('<li' + dataIDAttribute + '>' + template + '</li>'), templateProxy);
|
|
groupTemplateProxy.template = this.template;
|
|
if (typeof headerTemplate === FUNCTION) {
|
|
groupTemplateProxy._headerTemplate = headerTemplate;
|
|
headerTemplate = '#=this._headerTemplate(data)#';
|
|
}
|
|
groupTemplateProxy.headerTemplate = kendo.template(headerTemplate);
|
|
this.groupTemplate = proxy(GROUP_TEMPLATE, groupTemplateProxy);
|
|
},
|
|
_click: function (e) {
|
|
if (e.event.which > 1 || e.event.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
var dataItem, item = e.target, target = $(e.event.target), buttonElement = target.closest(kendo.roleSelector('button', 'detailbutton', 'backbutton')), button = kendo.widgetInstance(buttonElement, ui), id = item.attr(kendo.attr('uid'));
|
|
if (id) {
|
|
dataItem = this.dataSource.getByUid(id);
|
|
}
|
|
if (this.trigger(CLICK, {
|
|
target: target,
|
|
item: item,
|
|
dataItem: dataItem,
|
|
button: button
|
|
})) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
_styleGroups: function () {
|
|
var rootItems = this.element.children();
|
|
rootItems.children('ul').addClass('km-list');
|
|
rootItems.each(function () {
|
|
var li = $(this), groupHeader = li.contents().first();
|
|
li.addClass('km-group-container');
|
|
if (!groupHeader.is('ul') && !groupHeader.is('div.' + GROUP_CLASS)) {
|
|
groupHeader.wrap(GROUP_WRAPPER);
|
|
}
|
|
});
|
|
},
|
|
_style: function () {
|
|
var options = this.options, grouped = options.type === 'group', element = this.element, inset = options.style === 'inset';
|
|
element.addClass('km-listview').toggleClass('km-list', !grouped).toggleClass('km-virtual-list', this.virtual).toggleClass('km-listinset', !grouped && inset).toggleClass('km-listgroup', grouped && !inset).toggleClass('km-listgroupinset', grouped && inset);
|
|
if (!element.parents('.km-listview')[0]) {
|
|
element.closest('.km-content').toggleClass('km-insetcontent', inset);
|
|
}
|
|
if (grouped) {
|
|
this._styleGroups();
|
|
}
|
|
this.trigger(STYLED);
|
|
},
|
|
_enhanceItems: function (items) {
|
|
items.each(function () {
|
|
var item = $(this), child, enhanced = false;
|
|
item.children().each(function () {
|
|
child = $(this);
|
|
if (child.is('a')) {
|
|
enhanceLinkItem(child);
|
|
enhanced = true;
|
|
} else if (child.is('label')) {
|
|
enhanceCheckBoxItem(child);
|
|
enhanced = true;
|
|
}
|
|
});
|
|
if (!enhanced) {
|
|
enhanceItem(item);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
ui.plugin(ListView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.navbar', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.navbar',
|
|
name: 'NavBar',
|
|
category: 'mobile',
|
|
description: 'The Kendo mobile NavBar widget is used inside a mobile View or Layout Header element to display an application navigation bar.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, Widget = ui.Widget;
|
|
function createContainer(align, element) {
|
|
var items = element.find('[' + kendo.attr('align') + '=' + align + ']');
|
|
if (items[0]) {
|
|
return $('<div class="km-' + align + 'item" />').append(items).prependTo(element);
|
|
}
|
|
}
|
|
function toggleTitle(centerElement) {
|
|
var siblings = centerElement.siblings(), noTitle = !!centerElement.children('ul')[0], showTitle = !!siblings[0] && $.trim(centerElement.text()) === '', android = !!(kendo.mobile.application && kendo.mobile.application.element.is('.km-android'));
|
|
centerElement.prevAll().toggleClass('km-absolute', noTitle);
|
|
centerElement.toggleClass('km-show-title', showTitle);
|
|
centerElement.toggleClass('km-fill-title', showTitle && !$.trim(centerElement.html()));
|
|
centerElement.toggleClass('km-no-title', noTitle);
|
|
centerElement.toggleClass('km-hide-title', android && !siblings.children().is(':visible'));
|
|
}
|
|
var NavBar = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
element = that.element;
|
|
that.container().bind('show', $.proxy(this, 'refresh'));
|
|
element.addClass('km-navbar').wrapInner($('<div class="km-view-title km-show-title" />'));
|
|
that.leftElement = createContainer('left', element);
|
|
that.rightElement = createContainer('right', element);
|
|
that.centerElement = element.find('.km-view-title');
|
|
},
|
|
options: { name: 'NavBar' },
|
|
title: function (value) {
|
|
this.element.find(kendo.roleSelector('view-title')).text(value);
|
|
toggleTitle(this.centerElement);
|
|
},
|
|
refresh: function (e) {
|
|
var view = e.view;
|
|
this.title(view.options.title);
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.element);
|
|
}
|
|
});
|
|
ui.plugin(NavBar);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.scrollview', [
|
|
'kendo.fx',
|
|
'kendo.data',
|
|
'kendo.draganddrop'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.scrollview',
|
|
name: 'ScrollView',
|
|
category: 'mobile',
|
|
description: 'The Kendo Mobile ScrollView widget is used to scroll content wider than the device screen.',
|
|
depends: [
|
|
'fx',
|
|
'data',
|
|
'draganddrop'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, proxy = $.proxy, Transition = kendo.effects.Transition, Pane = kendo.ui.Pane, PaneDimensions = kendo.ui.PaneDimensions, Widget = ui.DataBoundWidget, DataSource = kendo.data.DataSource, Buffer = kendo.data.Buffer, BatchBuffer = kendo.data.BatchBuffer, math = Math, abs = math.abs, ceil = math.ceil, round = math.round, max = math.max, min = math.min, floor = math.floor, CHANGE = 'change', CHANGING = 'changing', REFRESH = 'refresh', CURRENT_PAGE_CLASS = 'km-current-page', VIRTUAL_PAGE_CLASS = 'km-virtual-page', FUNCTION = 'function', ITEM_CHANGE = 'itemChange', CLEANUP = 'cleanup', VIRTUAL_PAGE_COUNT = 3, LEFT_PAGE = -1, CETER_PAGE = 0, RIGHT_PAGE = 1, LEFT_SWIPE = -1, NUDGE = 0, RIGHT_SWIPE = 1;
|
|
var Pager = kendo.Class.extend({
|
|
init: function (scrollView) {
|
|
var that = this, element = $('<ol class=\'km-pages\'/>');
|
|
scrollView.element.append(element);
|
|
this._changeProxy = proxy(that, '_change');
|
|
this._refreshProxy = proxy(that, '_refresh');
|
|
scrollView.bind(CHANGE, this._changeProxy);
|
|
scrollView.bind(REFRESH, this._refreshProxy);
|
|
$.extend(that, {
|
|
element: element,
|
|
scrollView: scrollView
|
|
});
|
|
},
|
|
items: function () {
|
|
return this.element.children();
|
|
},
|
|
_refresh: function (e) {
|
|
var pageHTML = '';
|
|
for (var idx = 0; idx < e.pageCount; idx++) {
|
|
pageHTML += '<li/>';
|
|
}
|
|
this.element.html(pageHTML);
|
|
this.items().eq(e.page).addClass(CURRENT_PAGE_CLASS);
|
|
},
|
|
_change: function (e) {
|
|
this.items().removeClass(CURRENT_PAGE_CLASS).eq(e.page).addClass(CURRENT_PAGE_CLASS);
|
|
},
|
|
destroy: function () {
|
|
this.scrollView.unbind(CHANGE, this._changeProxy);
|
|
this.scrollView.unbind(REFRESH, this._refreshProxy);
|
|
this.element.remove();
|
|
}
|
|
});
|
|
kendo.mobile.ui.ScrollViewPager = Pager;
|
|
var TRANSITION_END = 'transitionEnd', DRAG_START = 'dragStart', DRAG_END = 'dragEnd';
|
|
var ElasticPane = kendo.Observable.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
kendo.Observable.fn.init.call(this);
|
|
this.element = element;
|
|
this.container = element.parent();
|
|
var movable, transition, userEvents, dimensions, dimension, pane;
|
|
movable = new kendo.ui.Movable(that.element);
|
|
transition = new Transition({
|
|
axis: 'x',
|
|
movable: movable,
|
|
onEnd: function () {
|
|
that.trigger(TRANSITION_END);
|
|
}
|
|
});
|
|
userEvents = new kendo.UserEvents(element, {
|
|
fastTap: true,
|
|
start: function (e) {
|
|
if (abs(e.x.velocity) * 2 >= abs(e.y.velocity)) {
|
|
userEvents.capture();
|
|
} else {
|
|
userEvents.cancel();
|
|
}
|
|
that.trigger(DRAG_START, e);
|
|
transition.cancel();
|
|
},
|
|
allowSelection: true,
|
|
end: function (e) {
|
|
that.trigger(DRAG_END, e);
|
|
}
|
|
});
|
|
dimensions = new PaneDimensions({
|
|
element: that.element,
|
|
container: that.container
|
|
});
|
|
dimension = dimensions.x;
|
|
dimension.bind(CHANGE, function () {
|
|
that.trigger(CHANGE);
|
|
});
|
|
pane = new Pane({
|
|
dimensions: dimensions,
|
|
userEvents: userEvents,
|
|
movable: movable,
|
|
elastic: true
|
|
});
|
|
$.extend(that, {
|
|
duration: options && options.duration || 1,
|
|
movable: movable,
|
|
transition: transition,
|
|
userEvents: userEvents,
|
|
dimensions: dimensions,
|
|
dimension: dimension,
|
|
pane: pane
|
|
});
|
|
this.bind([
|
|
TRANSITION_END,
|
|
DRAG_START,
|
|
DRAG_END,
|
|
CHANGE
|
|
], options);
|
|
},
|
|
size: function () {
|
|
return {
|
|
width: this.dimensions.x.getSize(),
|
|
height: this.dimensions.y.getSize()
|
|
};
|
|
},
|
|
total: function () {
|
|
return this.dimension.getTotal();
|
|
},
|
|
offset: function () {
|
|
return -this.movable.x;
|
|
},
|
|
updateDimension: function () {
|
|
this.dimension.update(true);
|
|
},
|
|
refresh: function () {
|
|
this.dimensions.refresh();
|
|
},
|
|
moveTo: function (offset) {
|
|
this.movable.moveAxis('x', -offset);
|
|
},
|
|
transitionTo: function (offset, ease, instant) {
|
|
if (instant) {
|
|
this.moveTo(-offset);
|
|
} else {
|
|
this.transition.moveTo({
|
|
location: offset,
|
|
duration: this.duration,
|
|
ease: ease
|
|
});
|
|
}
|
|
}
|
|
});
|
|
kendo.mobile.ui.ScrollViewElasticPane = ElasticPane;
|
|
var ScrollViewContent = kendo.Observable.extend({
|
|
init: function (element, pane, options) {
|
|
var that = this;
|
|
kendo.Observable.fn.init.call(this);
|
|
that.element = element;
|
|
that.pane = pane;
|
|
that._getPages();
|
|
this.page = 0;
|
|
this.pageSize = options.pageSize || 1;
|
|
this.contentHeight = options.contentHeight;
|
|
this.enablePager = options.enablePager;
|
|
this.pagerOverlay = options.pagerOverlay;
|
|
},
|
|
scrollTo: function (page, instant) {
|
|
this.page = page;
|
|
this.pane.transitionTo(-page * this.pane.size().width, Transition.easeOutExpo, instant);
|
|
},
|
|
paneMoved: function (swipeType, bounce, callback, instant) {
|
|
var that = this, pane = that.pane, width = pane.size().width * that.pageSize, approx = round, ease = bounce ? Transition.easeOutBack : Transition.easeOutExpo, snap, nextPage;
|
|
if (swipeType === LEFT_SWIPE) {
|
|
approx = ceil;
|
|
} else if (swipeType === RIGHT_SWIPE) {
|
|
approx = floor;
|
|
}
|
|
nextPage = approx(pane.offset() / width);
|
|
snap = max(that.minSnap, min(-nextPage * width, that.maxSnap));
|
|
if (nextPage != that.page) {
|
|
if (callback && callback({
|
|
currentPage: that.page,
|
|
nextPage: nextPage
|
|
})) {
|
|
snap = -that.page * pane.size().width;
|
|
}
|
|
}
|
|
pane.transitionTo(snap, ease, instant);
|
|
},
|
|
updatePage: function () {
|
|
var pane = this.pane, page = round(pane.offset() / pane.size().width);
|
|
if (page != this.page) {
|
|
this.page = page;
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
forcePageUpdate: function () {
|
|
return this.updatePage();
|
|
},
|
|
resizeTo: function (size) {
|
|
var pane = this.pane, width = size.width;
|
|
this.pageElements.width(width);
|
|
if (this.contentHeight === '100%') {
|
|
var containerHeight = this.element.parent().height();
|
|
if (this.enablePager === true) {
|
|
var pager = this.element.parent().find('ol.km-pages');
|
|
if (!this.pagerOverlay && pager.length) {
|
|
containerHeight -= pager.outerHeight(true);
|
|
}
|
|
}
|
|
this.element.css('height', containerHeight);
|
|
this.pageElements.css('height', containerHeight);
|
|
}
|
|
pane.updateDimension();
|
|
if (!this._paged) {
|
|
this.page = floor(pane.offset() / width);
|
|
}
|
|
this.scrollTo(this.page, true);
|
|
this.pageCount = ceil(pane.total() / width);
|
|
this.minSnap = -(this.pageCount - 1) * width;
|
|
this.maxSnap = 0;
|
|
},
|
|
_getPages: function () {
|
|
this.pageElements = this.element.find(kendo.roleSelector('page'));
|
|
this._paged = this.pageElements.length > 0;
|
|
}
|
|
});
|
|
kendo.mobile.ui.ScrollViewContent = ScrollViewContent;
|
|
var VirtualScrollViewContent = kendo.Observable.extend({
|
|
init: function (element, pane, options) {
|
|
var that = this;
|
|
kendo.Observable.fn.init.call(this);
|
|
that.element = element;
|
|
that.pane = pane;
|
|
that.options = options;
|
|
that._templates();
|
|
that.page = options.page || 0;
|
|
that.pages = [];
|
|
that._initPages();
|
|
that.resizeTo(that.pane.size());
|
|
that.pane.dimension.forceEnabled();
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
this.dataSource = DataSource.create(dataSource);
|
|
this._buffer();
|
|
this._pendingPageRefresh = false;
|
|
this._pendingWidgetRefresh = false;
|
|
},
|
|
_viewShow: function () {
|
|
var that = this;
|
|
if (that._pendingWidgetRefresh) {
|
|
setTimeout(function () {
|
|
that._resetPages();
|
|
}, 0);
|
|
that._pendingWidgetRefresh = false;
|
|
}
|
|
},
|
|
_buffer: function () {
|
|
var itemsPerPage = this.options.itemsPerPage;
|
|
if (this.buffer) {
|
|
this.buffer.destroy();
|
|
}
|
|
if (itemsPerPage > 1) {
|
|
this.buffer = new BatchBuffer(this.dataSource, itemsPerPage);
|
|
} else {
|
|
this.buffer = new Buffer(this.dataSource, itemsPerPage * 3);
|
|
}
|
|
this._resizeProxy = proxy(this, '_onResize');
|
|
this._resetProxy = proxy(this, '_onReset');
|
|
this._endReachedProxy = proxy(this, '_onEndReached');
|
|
this.buffer.bind({
|
|
'resize': this._resizeProxy,
|
|
'reset': this._resetProxy,
|
|
'endreached': this._endReachedProxy
|
|
});
|
|
},
|
|
_templates: function () {
|
|
var template = this.options.template, emptyTemplate = this.options.emptyTemplate, templateProxy = {}, emptyTemplateProxy = {};
|
|
if (typeof template === FUNCTION) {
|
|
templateProxy.template = template;
|
|
template = '#=this.template(data)#';
|
|
}
|
|
this.template = proxy(kendo.template(template), templateProxy);
|
|
if (typeof emptyTemplate === FUNCTION) {
|
|
emptyTemplateProxy.emptyTemplate = emptyTemplate;
|
|
emptyTemplate = '#=this.emptyTemplate(data)#';
|
|
}
|
|
this.emptyTemplate = proxy(kendo.template(emptyTemplate), emptyTemplateProxy);
|
|
},
|
|
_initPages: function () {
|
|
var pages = this.pages, element = this.element, page;
|
|
for (var i = 0; i < VIRTUAL_PAGE_COUNT; i++) {
|
|
page = new Page(element);
|
|
pages.push(page);
|
|
}
|
|
this.pane.updateDimension();
|
|
},
|
|
resizeTo: function (size) {
|
|
var pages = this.pages, pane = this.pane;
|
|
for (var i = 0; i < pages.length; i++) {
|
|
pages[i].setWidth(size.width);
|
|
}
|
|
if (this.options.contentHeight === 'auto') {
|
|
this.element.css('height', this.pages[1].element.height());
|
|
} else if (this.options.contentHeight === '100%') {
|
|
var containerHeight = this.element.parent().height();
|
|
if (this.options.enablePager === true) {
|
|
var pager = this.element.parent().find('ol.km-pages');
|
|
if (!this.options.pagerOverlay && pager.length) {
|
|
containerHeight -= pager.outerHeight(true);
|
|
}
|
|
}
|
|
this.element.css('height', containerHeight);
|
|
pages[0].element.css('height', containerHeight);
|
|
pages[1].element.css('height', containerHeight);
|
|
pages[2].element.css('height', containerHeight);
|
|
}
|
|
pane.updateDimension();
|
|
this._repositionPages();
|
|
this.width = size.width;
|
|
},
|
|
scrollTo: function (page) {
|
|
var buffer = this.buffer, dataItem;
|
|
buffer.syncDataSource();
|
|
dataItem = buffer.at(page);
|
|
if (!dataItem) {
|
|
return;
|
|
}
|
|
this._updatePagesContent(page);
|
|
this.page = page;
|
|
},
|
|
paneMoved: function (swipeType, bounce, callback, instant) {
|
|
var that = this, pane = that.pane, width = pane.size().width, offset = pane.offset(), thresholdPassed = Math.abs(offset) >= width / 3, ease = bounce ? kendo.effects.Transition.easeOutBack : kendo.effects.Transition.easeOutExpo, isEndReached = that.page + 2 > that.buffer.total(), nextPage, delta = 0;
|
|
if (swipeType === RIGHT_SWIPE) {
|
|
if (that.page !== 0) {
|
|
delta = -1;
|
|
}
|
|
} else if (swipeType === LEFT_SWIPE && !isEndReached) {
|
|
delta = 1;
|
|
} else if (offset > 0 && (thresholdPassed && !isEndReached)) {
|
|
delta = 1;
|
|
} else if (offset < 0 && thresholdPassed) {
|
|
if (that.page !== 0) {
|
|
delta = -1;
|
|
}
|
|
}
|
|
nextPage = that.page;
|
|
if (delta) {
|
|
nextPage = delta > 0 ? nextPage + 1 : nextPage - 1;
|
|
}
|
|
if (callback && callback({
|
|
currentPage: that.page,
|
|
nextPage: nextPage
|
|
})) {
|
|
delta = 0;
|
|
}
|
|
if (delta === 0) {
|
|
that._cancelMove(ease, instant);
|
|
} else if (delta === -1) {
|
|
that._moveBackward(instant);
|
|
} else if (delta === 1) {
|
|
that._moveForward(instant);
|
|
}
|
|
},
|
|
updatePage: function () {
|
|
var pages = this.pages;
|
|
if (this.pane.offset() === 0) {
|
|
return false;
|
|
}
|
|
if (this.pane.offset() > 0) {
|
|
pages.push(this.pages.shift());
|
|
this.page++;
|
|
this.setPageContent(pages[2], this.page + 1);
|
|
} else {
|
|
pages.unshift(this.pages.pop());
|
|
this.page--;
|
|
this.setPageContent(pages[0], this.page - 1);
|
|
}
|
|
this._repositionPages();
|
|
this._resetMovable();
|
|
return true;
|
|
},
|
|
forcePageUpdate: function () {
|
|
var offset = this.pane.offset(), threshold = this.pane.size().width * 3 / 4;
|
|
if (abs(offset) > threshold) {
|
|
return this.updatePage();
|
|
}
|
|
return false;
|
|
},
|
|
_resetMovable: function () {
|
|
this.pane.moveTo(0);
|
|
},
|
|
_moveForward: function (instant) {
|
|
this.pane.transitionTo(-this.width, kendo.effects.Transition.easeOutExpo, instant);
|
|
},
|
|
_moveBackward: function (instant) {
|
|
this.pane.transitionTo(this.width, kendo.effects.Transition.easeOutExpo, instant);
|
|
},
|
|
_cancelMove: function (ease, instant) {
|
|
this.pane.transitionTo(0, ease, instant);
|
|
},
|
|
_resetPages: function () {
|
|
this.page = this.options.page || 0;
|
|
this._updatePagesContent(this.page);
|
|
this._repositionPages();
|
|
this.trigger('reset');
|
|
},
|
|
_onResize: function () {
|
|
this.pageCount = ceil(this.dataSource.total() / this.options.itemsPerPage);
|
|
if (this._pendingPageRefresh) {
|
|
this._updatePagesContent(this.page);
|
|
this._pendingPageRefresh = false;
|
|
}
|
|
this.trigger('resize');
|
|
},
|
|
_onReset: function () {
|
|
this.pageCount = ceil(this.dataSource.total() / this.options.itemsPerPage);
|
|
this._resetPages();
|
|
},
|
|
_onEndReached: function () {
|
|
this._pendingPageRefresh = true;
|
|
},
|
|
_repositionPages: function () {
|
|
var pages = this.pages;
|
|
pages[0].position(LEFT_PAGE);
|
|
pages[1].position(CETER_PAGE);
|
|
pages[2].position(RIGHT_PAGE);
|
|
},
|
|
_updatePagesContent: function (offset) {
|
|
var pages = this.pages, currentPage = offset || 0;
|
|
this.setPageContent(pages[0], currentPage - 1);
|
|
this.setPageContent(pages[1], currentPage);
|
|
this.setPageContent(pages[2], currentPage + 1);
|
|
},
|
|
setPageContent: function (page, index) {
|
|
var buffer = this.buffer, template = this.template, emptyTemplate = this.emptyTemplate, view = null;
|
|
if (index >= 0) {
|
|
view = buffer.at(index);
|
|
if ($.isArray(view) && !view.length) {
|
|
view = null;
|
|
}
|
|
}
|
|
this.trigger(CLEANUP, { item: page.element });
|
|
if (view !== null) {
|
|
page.content(template(view));
|
|
} else {
|
|
page.content(emptyTemplate({}));
|
|
}
|
|
kendo.mobile.init(page.element);
|
|
this.trigger(ITEM_CHANGE, {
|
|
item: page.element,
|
|
data: view,
|
|
ns: kendo.mobile.ui
|
|
});
|
|
}
|
|
});
|
|
kendo.mobile.ui.VirtualScrollViewContent = VirtualScrollViewContent;
|
|
var Page = kendo.Class.extend({
|
|
init: function (container) {
|
|
this.element = $('<div class=\'' + VIRTUAL_PAGE_CLASS + '\'></div>');
|
|
this.width = container.width();
|
|
this.element.width(this.width);
|
|
container.append(this.element);
|
|
},
|
|
content: function (theContent) {
|
|
this.element.html(theContent);
|
|
},
|
|
position: function (position) {
|
|
this.element.css('transform', 'translate3d(' + this.width * position + 'px, 0, 0)');
|
|
},
|
|
setWidth: function (width) {
|
|
this.width = width;
|
|
this.element.width(width);
|
|
}
|
|
});
|
|
kendo.mobile.ui.VirtualPage = Page;
|
|
var ScrollView = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
element = that.element;
|
|
kendo.stripWhitespace(element[0]);
|
|
element.wrapInner('<div/>').addClass('km-scrollview');
|
|
if (this.options.enablePager) {
|
|
this.pager = new Pager(this);
|
|
if (this.options.pagerOverlay) {
|
|
element.addClass('km-scrollview-overlay');
|
|
}
|
|
}
|
|
that.inner = element.children().first();
|
|
that.page = 0;
|
|
that.inner.css('height', options.contentHeight);
|
|
that.pane = new ElasticPane(that.inner, {
|
|
duration: this.options.duration,
|
|
transitionEnd: proxy(this, '_transitionEnd'),
|
|
dragStart: proxy(this, '_dragStart'),
|
|
dragEnd: proxy(this, '_dragEnd'),
|
|
change: proxy(this, REFRESH)
|
|
});
|
|
that.bind('resize', function () {
|
|
that.pane.refresh();
|
|
});
|
|
that.page = options.page;
|
|
var empty = this.inner.children().length === 0;
|
|
var content = empty ? new VirtualScrollViewContent(that.inner, that.pane, options) : new ScrollViewContent(that.inner, that.pane, options);
|
|
content.page = that.page;
|
|
content.bind('reset', function () {
|
|
this._pendingPageRefresh = false;
|
|
that._syncWithContent();
|
|
that.trigger(REFRESH, {
|
|
pageCount: content.pageCount,
|
|
page: content.page
|
|
});
|
|
});
|
|
content.bind('resize', function () {
|
|
that.trigger(REFRESH, {
|
|
pageCount: content.pageCount,
|
|
page: content.page
|
|
});
|
|
});
|
|
content.bind(ITEM_CHANGE, function (e) {
|
|
that.trigger(ITEM_CHANGE, e);
|
|
that.angular('compile', function () {
|
|
return {
|
|
elements: e.item,
|
|
data: [{ dataItem: e.data }]
|
|
};
|
|
});
|
|
});
|
|
content.bind(CLEANUP, function (e) {
|
|
that.angular('cleanup', function () {
|
|
return { elements: e.item };
|
|
});
|
|
});
|
|
that._content = content;
|
|
that.setDataSource(options.dataSource);
|
|
var mobileContainer = that.container();
|
|
if (mobileContainer.nullObject) {
|
|
that.viewInit();
|
|
that.viewShow();
|
|
} else {
|
|
mobileContainer.bind('show', proxy(this, 'viewShow')).bind('init', proxy(this, 'viewInit'));
|
|
}
|
|
},
|
|
options: {
|
|
name: 'ScrollView',
|
|
page: 0,
|
|
duration: 400,
|
|
velocityThreshold: 0.8,
|
|
contentHeight: 'auto',
|
|
pageSize: 1,
|
|
itemsPerPage: 1,
|
|
bounceVelocityThreshold: 1.6,
|
|
enablePager: true,
|
|
pagerOverlay: false,
|
|
autoBind: true,
|
|
template: '',
|
|
emptyTemplate: ''
|
|
},
|
|
events: [
|
|
CHANGING,
|
|
CHANGE,
|
|
REFRESH
|
|
],
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
kendo.destroy(this.element);
|
|
},
|
|
viewInit: function () {
|
|
if (this.options.autoBind) {
|
|
this._content.scrollTo(this._content.page, true);
|
|
}
|
|
},
|
|
viewShow: function () {
|
|
this.pane.refresh();
|
|
},
|
|
refresh: function () {
|
|
var content = this._content;
|
|
content.resizeTo(this.pane.size());
|
|
this.page = content.page;
|
|
this.trigger(REFRESH, {
|
|
pageCount: content.pageCount,
|
|
page: content.page
|
|
});
|
|
},
|
|
content: function (html) {
|
|
this.element.children().first().html(html);
|
|
this._content._getPages();
|
|
this.pane.refresh();
|
|
},
|
|
value: function (item) {
|
|
var dataSource = this.dataSource;
|
|
if (item) {
|
|
this.scrollTo(dataSource.indexOf(item), true);
|
|
} else {
|
|
return dataSource.at(this.page);
|
|
}
|
|
},
|
|
scrollTo: function (page, instant) {
|
|
this._content.scrollTo(page, instant);
|
|
this._syncWithContent();
|
|
},
|
|
prev: function () {
|
|
var that = this, prevPage = that.page - 1;
|
|
if (that._content instanceof VirtualScrollViewContent) {
|
|
that._content.paneMoved(RIGHT_SWIPE, undefined, function (eventData) {
|
|
return that.trigger(CHANGING, eventData);
|
|
});
|
|
} else if (prevPage > -1) {
|
|
that.scrollTo(prevPage);
|
|
}
|
|
},
|
|
next: function () {
|
|
var that = this, nextPage = that.page + 1;
|
|
if (that._content instanceof VirtualScrollViewContent) {
|
|
that._content.paneMoved(LEFT_SWIPE, undefined, function (eventData) {
|
|
return that.trigger(CHANGING, eventData);
|
|
});
|
|
} else if (nextPage < that._content.pageCount) {
|
|
that.scrollTo(nextPage);
|
|
}
|
|
},
|
|
setDataSource: function (dataSource) {
|
|
if (!(this._content instanceof VirtualScrollViewContent)) {
|
|
return;
|
|
}
|
|
var emptyDataSource = !dataSource;
|
|
this.dataSource = DataSource.create(dataSource);
|
|
this._content.setDataSource(this.dataSource);
|
|
if (this.options.autoBind && !emptyDataSource) {
|
|
this.dataSource.fetch();
|
|
}
|
|
},
|
|
items: function () {
|
|
return this.element.find('.' + VIRTUAL_PAGE_CLASS);
|
|
},
|
|
_syncWithContent: function () {
|
|
var pages = this._content.pages, buffer = this._content.buffer, data, element;
|
|
this.page = this._content.page;
|
|
data = buffer ? buffer.at(this.page) : undefined;
|
|
if (!(data instanceof Array)) {
|
|
data = [data];
|
|
}
|
|
element = pages ? pages[1].element : undefined;
|
|
this.trigger(CHANGE, {
|
|
page: this.page,
|
|
element: element,
|
|
data: data
|
|
});
|
|
},
|
|
_dragStart: function () {
|
|
if (this._content.forcePageUpdate()) {
|
|
this._syncWithContent();
|
|
}
|
|
},
|
|
_dragEnd: function (e) {
|
|
var that = this, velocity = e.x.velocity, velocityThreshold = this.options.velocityThreshold, swipeType = NUDGE, bounce = abs(velocity) > this.options.bounceVelocityThreshold;
|
|
if (velocity > velocityThreshold) {
|
|
swipeType = RIGHT_SWIPE;
|
|
} else if (velocity < -velocityThreshold) {
|
|
swipeType = LEFT_SWIPE;
|
|
}
|
|
this._content.paneMoved(swipeType, bounce, function (eventData) {
|
|
return that.trigger(CHANGING, eventData);
|
|
});
|
|
},
|
|
_transitionEnd: function () {
|
|
if (this._content.updatePage()) {
|
|
this._syncWithContent();
|
|
}
|
|
}
|
|
});
|
|
ui.plugin(ScrollView);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.switch', [
|
|
'kendo.fx',
|
|
'kendo.userevents'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.switch',
|
|
name: 'Switch',
|
|
category: 'mobile',
|
|
description: 'The mobile Switch widget is used to display two exclusive choices.',
|
|
depends: [
|
|
'fx',
|
|
'userevents'
|
|
]
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, support = kendo.support, CHANGE = 'change', SWITCHON = 'km-switch-on', SWITCHOFF = 'km-switch-off', MARGINLEFT = 'margin-left', ACTIVE_STATE = 'km-state-active', DISABLED_STATE = 'km-state-disabled', DISABLED = 'disabled', TRANSFORMSTYLE = support.transitions.css + 'transform', proxy = $.proxy;
|
|
function limitValue(value, minLimit, maxLimit) {
|
|
return Math.max(minLimit, Math.min(maxLimit, value));
|
|
}
|
|
var SWITCH_MARKUP = '<span class="km-switch km-widget"> <span class="km-switch-wrapper"><span class="km-switch-background"></span></span> <span class="km-switch-container"><span class="km-switch-handle" > <span class="km-switch-label-on">{0}</span> <span class="km-switch-label-off">{1}</span> </span> </span>';
|
|
var Switch = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this, checked;
|
|
Widget.fn.init.call(that, element, options);
|
|
options = that.options;
|
|
that.wrapper = $(kendo.format(SWITCH_MARKUP, options.onLabel, options.offLabel));
|
|
that.handle = that.wrapper.find('.km-switch-handle');
|
|
that.background = that.wrapper.find('.km-switch-background');
|
|
that.wrapper.insertBefore(that.element).prepend(that.element);
|
|
that._drag();
|
|
that.origin = parseInt(that.background.css(MARGINLEFT), 10);
|
|
that.constrain = 0;
|
|
that.snapPoint = 0;
|
|
element = that.element[0];
|
|
element.type = 'checkbox';
|
|
that._animateBackground = true;
|
|
checked = that.options.checked;
|
|
if (checked === null) {
|
|
checked = element.checked;
|
|
}
|
|
that.check(checked);
|
|
that.options.enable = that.options.enable && !that.element.attr(DISABLED);
|
|
that.enable(that.options.enable);
|
|
that.refresh();
|
|
kendo.notify(that, kendo.mobile.ui);
|
|
},
|
|
refresh: function () {
|
|
var that = this, handleWidth = that.handle.outerWidth(true);
|
|
that.width = that.wrapper.width();
|
|
that.constrain = that.width - handleWidth;
|
|
that.snapPoint = that.constrain / 2;
|
|
if (typeof that.origin != 'number') {
|
|
that.origin = parseInt(that.background.css(MARGINLEFT), 10);
|
|
}
|
|
that.background.data('origin', that.origin);
|
|
that.check(that.element[0].checked);
|
|
},
|
|
events: [CHANGE],
|
|
options: {
|
|
name: 'Switch',
|
|
onLabel: 'on',
|
|
offLabel: 'off',
|
|
checked: null,
|
|
enable: true
|
|
},
|
|
check: function (check) {
|
|
var that = this, element = that.element[0];
|
|
if (check === undefined) {
|
|
return element.checked;
|
|
}
|
|
that._position(check ? that.constrain : 0);
|
|
element.checked = check;
|
|
that.wrapper.toggleClass(SWITCHON, check).toggleClass(SWITCHOFF, !check);
|
|
},
|
|
value: function () {
|
|
return this.check.apply(this, arguments);
|
|
},
|
|
destroy: function () {
|
|
Widget.fn.destroy.call(this);
|
|
this.userEvents.destroy();
|
|
},
|
|
toggle: function () {
|
|
var that = this;
|
|
that.check(!that.element[0].checked);
|
|
},
|
|
enable: function (enable) {
|
|
var element = this.element, wrapper = this.wrapper;
|
|
if (typeof enable == 'undefined') {
|
|
enable = true;
|
|
}
|
|
this.options.enable = enable;
|
|
if (enable) {
|
|
element.removeAttr(DISABLED);
|
|
} else {
|
|
element.attr(DISABLED, DISABLED);
|
|
}
|
|
wrapper.toggleClass(DISABLED_STATE, !enable);
|
|
},
|
|
_resize: function () {
|
|
this.refresh();
|
|
},
|
|
_move: function (e) {
|
|
var that = this;
|
|
e.preventDefault();
|
|
that._position(limitValue(that.position + e.x.delta, 0, that.width - that.handle.outerWidth(true)));
|
|
},
|
|
_position: function (position) {
|
|
var that = this;
|
|
that.position = position;
|
|
that.handle.css(TRANSFORMSTYLE, 'translatex(' + position + 'px)');
|
|
if (that._animateBackground) {
|
|
that.background.css(MARGINLEFT, that.origin + position);
|
|
}
|
|
},
|
|
_start: function () {
|
|
if (!this.options.enable) {
|
|
this.userEvents.cancel();
|
|
} else {
|
|
this.userEvents.capture();
|
|
this.handle.addClass(ACTIVE_STATE);
|
|
}
|
|
},
|
|
_stop: function () {
|
|
var that = this;
|
|
that.handle.removeClass(ACTIVE_STATE);
|
|
that._toggle(that.position > that.snapPoint);
|
|
},
|
|
_toggle: function (checked) {
|
|
var that = this, handle = that.handle, element = that.element[0], value = element.checked, duration = kendo.mobile.application && kendo.mobile.application.os.wp ? 100 : 200, distance;
|
|
that.wrapper.toggleClass(SWITCHON, checked).toggleClass(SWITCHOFF, !checked);
|
|
that.position = distance = checked * that.constrain;
|
|
if (that._animateBackground) {
|
|
that.background.kendoStop(true, true).kendoAnimate({
|
|
effects: 'slideMargin',
|
|
offset: distance,
|
|
reset: true,
|
|
reverse: !checked,
|
|
axis: 'left',
|
|
duration: duration
|
|
});
|
|
}
|
|
handle.kendoStop(true, true).kendoAnimate({
|
|
effects: 'slideTo',
|
|
duration: duration,
|
|
offset: distance + 'px,0',
|
|
reset: true,
|
|
complete: function () {
|
|
if (value !== checked) {
|
|
element.checked = checked;
|
|
that.trigger(CHANGE, { checked: checked });
|
|
}
|
|
}
|
|
});
|
|
},
|
|
_drag: function () {
|
|
var that = this;
|
|
that.userEvents = new kendo.UserEvents(that.wrapper, {
|
|
fastTap: true,
|
|
tap: function () {
|
|
if (that.options.enable) {
|
|
that._toggle(!that.element[0].checked);
|
|
}
|
|
},
|
|
start: proxy(that._start, that),
|
|
move: proxy(that._move, that),
|
|
end: proxy(that._stop, that)
|
|
});
|
|
}
|
|
});
|
|
ui.plugin(Switch);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile.tabstrip', ['kendo.core'], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'mobile.tabstrip',
|
|
name: 'TabStrip',
|
|
category: 'mobile',
|
|
description: 'The mobile TabStrip widget is used inside a mobile view or layout footer element to display an application-wide group of navigation buttons.',
|
|
depends: ['core']
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, ACTIVE_STATE_CLASS = 'km-state-active', SELECT = 'select';
|
|
function createBadge(value) {
|
|
return $('<span class="km-badge">' + value + '</span>');
|
|
}
|
|
var TabStrip = Widget.extend({
|
|
init: function (element, options) {
|
|
var that = this;
|
|
Widget.fn.init.call(that, element, options);
|
|
that.container().bind('show', $.proxy(this, 'refresh'));
|
|
that.element.addClass('km-tabstrip').find('a').each(that._buildButton).eq(that.options.selectedIndex).addClass(ACTIVE_STATE_CLASS);
|
|
that.element.on('down', 'a', '_release');
|
|
},
|
|
events: [SELECT],
|
|
switchTo: function (url) {
|
|
var tabs = this.element.find('a'), tab, path, idx = 0, length = tabs.length;
|
|
if (isNaN(url)) {
|
|
for (; idx < length; idx++) {
|
|
tab = tabs[idx];
|
|
path = tab.href.replace(/(\#.+)(\?.+)$/, '$1');
|
|
if (path.indexOf(url, path.length - url.length) !== -1) {
|
|
this._setActiveItem($(tab));
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
this._setActiveItem(tabs.eq(url));
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
switchByFullUrl: function (url) {
|
|
var tab;
|
|
tab = this.element.find('a[href$=\'' + url + '\']');
|
|
this._setActiveItem(tab);
|
|
},
|
|
clear: function () {
|
|
this.currentItem().removeClass(ACTIVE_STATE_CLASS);
|
|
},
|
|
currentItem: function () {
|
|
return this.element.children('.' + ACTIVE_STATE_CLASS);
|
|
},
|
|
badge: function (item, value) {
|
|
var tabstrip = this.element, badge;
|
|
if (!isNaN(item)) {
|
|
item = tabstrip.children().get(item);
|
|
}
|
|
item = tabstrip.find(item);
|
|
badge = $(item.find('.km-badge')[0] || createBadge(value).insertAfter(item.children('.km-icon')));
|
|
if (value || value === 0) {
|
|
badge.html(value);
|
|
return this;
|
|
}
|
|
if (value === false) {
|
|
badge.empty().remove();
|
|
return this;
|
|
}
|
|
return badge.html();
|
|
},
|
|
_release: function (e) {
|
|
if (e.which > 1) {
|
|
return;
|
|
}
|
|
var that = this, item = $(e.currentTarget);
|
|
if (item[0] === that.currentItem()[0]) {
|
|
return;
|
|
}
|
|
if (that.trigger(SELECT, { item: item })) {
|
|
e.preventDefault();
|
|
} else {
|
|
that._setActiveItem(item);
|
|
}
|
|
},
|
|
_setActiveItem: function (item) {
|
|
if (!item[0]) {
|
|
return;
|
|
}
|
|
this.clear();
|
|
item.addClass(ACTIVE_STATE_CLASS);
|
|
},
|
|
_buildButton: function () {
|
|
var button = $(this), icon = kendo.attrValue(button, 'icon'), badge = kendo.attrValue(button, 'badge'), image = button.find('img'), iconSpan = $('<span class="km-icon"/>');
|
|
button.addClass('km-button').attr(kendo.attr('role'), 'tab').contents().not(image).wrapAll('<span class="km-text"/>');
|
|
if (image[0]) {
|
|
image.addClass('km-image').prependTo(button);
|
|
} else {
|
|
button.prepend(iconSpan);
|
|
if (icon) {
|
|
iconSpan.addClass('km-' + icon);
|
|
if (badge || badge === 0) {
|
|
createBadge(badge).insertAfter(iconSpan);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
refresh: function (e) {
|
|
var url = e.view.id;
|
|
if (url && !this.switchTo(e.view.id)) {
|
|
this.switchTo(url);
|
|
}
|
|
},
|
|
options: {
|
|
name: 'TabStrip',
|
|
selectedIndex: 0,
|
|
enable: true
|
|
}
|
|
});
|
|
ui.plugin(TabStrip);
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.mobile', [
|
|
'kendo.core',
|
|
'kendo.fx',
|
|
'kendo.data.odata',
|
|
'kendo.data.xml',
|
|
'kendo.data',
|
|
'kendo.data.signalr',
|
|
'kendo.binder',
|
|
'kendo.validator',
|
|
'kendo.router',
|
|
'kendo.view',
|
|
'kendo.userevents',
|
|
'kendo.draganddrop',
|
|
'kendo.popup',
|
|
'kendo.touch',
|
|
'kendo.mobile.popover',
|
|
'kendo.mobile.loader',
|
|
'kendo.mobile.scroller',
|
|
'kendo.mobile.shim',
|
|
'kendo.mobile.view',
|
|
'kendo.mobile.modalview',
|
|
'kendo.mobile.drawer',
|
|
'kendo.mobile.splitview',
|
|
'kendo.mobile.pane',
|
|
'kendo.mobile.application',
|
|
'kendo.mobile.actionsheet',
|
|
'kendo.mobile.button',
|
|
'kendo.mobile.buttongroup',
|
|
'kendo.mobile.collapsible',
|
|
'kendo.mobile.listview',
|
|
'kendo.mobile.navbar',
|
|
'kendo.mobile.scrollview',
|
|
'kendo.mobile.switch',
|
|
'kendo.mobile.tabstrip',
|
|
'kendo.angular',
|
|
'kendo.webcomponents',
|
|
'kendo.angular2'
|
|
], f);
|
|
}(function () {
|
|
'bundle all';
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.all', [
|
|
'kendo.web',
|
|
'kendo.dataviz',
|
|
'kendo.mobile',
|
|
'kendo.drawing',
|
|
'kendo.dom'
|
|
], f);
|
|
}(function () {
|
|
'bundle all';
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
})); |