Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.googlecode.wicket.kendo.ui.resource.kendo.web.js Maven / Gradle / Ivy
/*
* Kendo UI Web v2013.3.1119 (http://kendoui.com)
* Copyright 2013 Telerik AD. All rights reserved.
*
* Kendo UI Web commercial licenses may be obtained at
* https://www.kendoui.com/purchase/license-agreement/kendo-ui-web-commercial.aspx
* If you do not own a commercial license, this file shall be governed by the
* GNU General Public License (GPL) version 3.
* For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html
*/
(function($, evil, 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+?)px\s*(\d+?)px\s*(\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,
globalize = window.Globalize;
kendo.version = "2013.3.1119";
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 (typeof proto[member] === OBJECT && !(proto[member] instanceof Array) && proto[member] !== null) {
// Merge object members
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);
};
}
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.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 "+e(" + rest + ")+";
} else {
return ";" + part + ";o+=";
}
}
}
var argumentNameRegExp = /^\w+/,
encodeRegExp = /\$\{([^}]*)\}/g,
escapedCurlyRegExp = /\\\}/g,
curlyRegExp = /__CURLY__/g,
escapedSharpRegExp = /\\#/g,
sharpRegExp = /__SHARP__/g,
zeros = ["", "0", "00", "000", "0000"];
Template = {
paramName: "data", // name of the parameter of the generated template
useWithBlock: true, // whether to wrap the template in a with() block
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 o,e=kendo.htmlEncode;",
fn,
parts,
idx;
if (isFunction(template)) {
if (template.length === 2) {
//looks like jQuery.template
return function(d) {
return template($, { data: d }).join("");
};
}
return template;
}
functionBody += useWithBlock ? "with(" + paramName + "){" : "";
functionBody += "o=";
parts = template
.replace(escapedCurlyRegExp, "__CURLY__")
.replace(encodeRegExp, "#=e($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 o;";
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;
}
//JSON stringify
(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});
};
}
})();
// Date and Number formatting
(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|"[^"]*"|'[^']*'/g,
standardFormatRegExp = /^(n|c|p|e)(\d*)$/i,
literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g,
commaRegExp = /\,/g,
EMPTY = "",
POINT = ".",
COMMA = ",",
SHARP = "#",
ZERO = "0",
PLACEHOLDER = "??",
EN = "en-US",
objectToString = {}.toString;
//cultures
kendo.cultures["en-US"] = {
name: EN,
numberFormat: {
pattern: ["-n"],
decimals: 2,
",": ",",
".": ".",
groupSize: [3],
percent: {
pattern: ["-n %", "n %"],
decimals: 2,
",": ",",
".": ".",
groupSize: [3],
symbol: "%"
},
currency: {
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;
}
function expandNumberFormat(numberFormat) {
numberFormat.groupSizes = numberFormat.groupSize;
numberFormat.percent.groupSizes = numberFormat.percent.groupSize;
numberFormat.currency.groupSizes = numberFormat.currency.groupSize;
}
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;
if (globalize) {
expandNumberFormat(culture.numberFormat);
}
} else {
return cultures.current;
}
};
kendo.findCulture = findCulture;
kendo.getCulture = getCulture;
//set current culture to en-US.
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 result;
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 = math.floor(date.getMilliseconds() / 10);
} else if (match === "fff") {
result = date.getMilliseconds();
} else if (match === "tt") {
result = date.getHours() < 12 ? calendar.AM[0] : calendar.PM[0];
}
return result !== undefined ? result : match.slice(1, match.length - 1);
});
}
//number formatting
function formatNumber(number, format, culture) {
culture = getCulture(culture);
var numberFormat = culture.numberFormat,
groupSize = numberFormat.groupSize[0],
groupSeparator = numberFormat[COMMA],
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;
//return empty string if no number
if (number === undefined) {
return EMPTY;
}
if (!isFinite(number)) {
return number;
}
//if no format then return number.toString() or number.toLocaleString() if culture.name is not defined
if (!format) {
return culture.name.length ? number.toLocaleString() : number.toString();
}
formatAndPrecision = standardFormatRegExp.exec(format);
// standard formatting
if (formatAndPrecision) {
format = formatAndPrecision[1].toLowerCase();
isCurrency = format === "c";
isPercent = format === "p";
if (isCurrency || isPercent) {
//get specific number format information if format is currency or percent
numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
groupSize = numberFormat.groupSize[0];
groupSeparator = numberFormat[COMMA];
decimal = numberFormat[POINT];
precision = numberFormat.decimals;
symbol = numberFormat.symbol;
pattern = numberFormat.pattern[negative ? 0 : 1];
}
customPrecision = formatAndPrecision[2];
if (customPrecision) {
precision = +customPrecision;
}
//return number in exponential format
if (format === "e") {
return customPrecision ? number.toExponential(precision) : number.toExponential(); // toExponential() and toExponential(undefined) differ in FF #653438.
}
// multiply if format is percent
if (isPercent) {
number *= 100;
}
number = round(number, precision);
number = number.split(POINT);
integer = number[0];
fraction = number[1];
//exclude "-" if number is negative.
if (negative) {
integer = integer.substring(1);
}
value = integer;
integerLength = integer.length;
//add group separator to the number if it is longer enough
if (integerLength >= groupSize) {
value = EMPTY;
for (idx = 0; idx < integerLength; idx++) {
if (idx > 0 && (integerLength - idx) % groupSize === 0) {
value += groupSeparator;
}
value += integer.charAt(idx);
}
}
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;
}
//custom formatting
//
//separate format by sections.
//make number positive
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]) {
//get negative format
format = format[1];
hasNegativeFormat = true;
} else if (number === 0) {
//format for zeros
format = format[2] || format[0];
if (format.indexOf(SHARP) == -1 && format.indexOf(ZERO) == -1) {
//return format if it is string constant.
return format;
}
} else {
format = format[0];
}
percentIndex = format.indexOf("%");
currencyIndex = format.indexOf("$");
isPercent = percentIndex != -1;
isCurrency = currencyIndex != -1;
//multiply number if the format has percent
if (isPercent) {
number *= 100;
}
if (isCurrency && format[currencyIndex - 1] === "\\") {
format = format.split("\\").join("");
isCurrency = false;
}
if (isCurrency || isPercent) {
//get specific number format information if format is currency or percent
numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
groupSize = numberFormat.groupSize[0];
groupSeparator = numberFormat[COMMA];
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);
//define the index of the first digit placeholder
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);
//define the index of the last digit placeholder
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;
//add group separator to the number if it is longer enough
if (hasGroup) {
if (integerLength === groupSize && integerLength < decimalIndex - startZeroIndex) {
integer = groupSeparator + integer;
} else if (integerLength > groupSize) {
value = EMPTY;
for (idx = 0; idx < integerLength; idx++) {
if (idx > 0 && (integerLength - idx) % groupSize === 0) {
value += groupSeparator;
}
value += integer.charAt(idx);
}
integer = value;
}
}
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 (end >= start) {
number += format.substring(end + 1);
}
//replace symbol placeholders
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 round = function(value, precision) {
var power = Math.pow(10, precision || 0);
return (Math.round(value * power) / power).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 : "";
};
if (globalize) {
toString = proxy(globalize.format, globalize);
}
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\((.*?)\)\/$/,
formatsSequence = ["G", "g", "d", "F", "D", "y", "m", "T", "t"],
numberRegExp = {
2: /^\d{1,2}/,
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);
}
//if date's day is different than the typed one - adjust
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,
subValue;
for (; i < length; i++) {
name = names[i];
nameLength = name.length;
subValue = value.substr(valueIdx, nameLength);
if (lower) {
subValue = subValue.toLowerCase();
}
if (subValue == name) {
valueIdx += nameLength;
return i + 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, ISO8601, matches,
amDesignators, pmDesignators,
hoursOffset, minutesOffset,
hasTime;
if (!format) {
format = "d"; //shord date format
}
//if format is part of the patterns get real format
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);
}
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; //because month is zero based
} 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");
milliseconds = getNumber(count);
if (milliseconds !== null && count > 3) {
milliseconds = parseInt(milliseconds.toString().substring(0, 3), 10);
}
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") {
if (!ISO8601) {
return null;
}
checkLiteral();
continue;
}
matches = value.substr(valueIdx, 6)
.match(count > 2 ? longTimeZoneRegExp : shortTimeZoneRegExp);
if (!matches) {
return null;
}
matches = matches[0];
valueIdx = matches.length;
matches = matches.split(":");
hoursOffset = parseInt(matches[0], 10);
if (outOfRange(hoursOffset, -12, 13)) {
return null;
}
if (count > 2) {
minutesOffset = parseInt(matches[1], 10);
if (isNaN(minutesOffset) || outOfRange(minutesOffset, 0, 59)) {
return null;
}
}
} else if (ch === "T") {
ISO8601 = checkLiteral();
} 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;
}
kendo.parseDate = function(value, formats, culture) {
if (objectToString.call(value) === "[object Date]") {
return value;
}
var idx = 0,
date = null,
length, patterns;
if (value && value.indexOf("/D") === 0) {
date = dateRegExp.exec(value);
if (date) {
return new Date(parseInt(date[1], 10));
}
}
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.push(
"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: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"
);
}
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;
//handle exponential number
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;
};
if (globalize) {
kendo.parseDate = function (value, format, culture) {
if (objectToString.call(value) === "[object Date]") {
return value;
}
return globalize.parseDate(value, format, culture);
};
kendo.parseFloat = function (value, culture) {
if (typeof value === NUMBER) {
return value;
}
if (value === undefined || value === null) {
return null;
}
value = globalize.parseFloat(value, culture);
return isNaN(value) ? null : value;
};
}
})();
function wrap(element, autosize) {
var browser = support.browser,
percentage,
isRtl = element.css("direction") == "rtl";
if (!element.parent().hasClass("k-animation-container")) {
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)),
left = (-radius[1]) + blur,
right = (+radius[1]) + blur,
bottom = (+radius[2]) + blur,
width = element[0].style.width,
height = element[0].style.height,
percentWidth = percentRegExp.test(width),
percentHeight = percentRegExp.test(height);
if (browser.opera) { // Box shadow can't be retrieved in Opera
left = right = bottom = 5;
}
percentage = percentWidth || percentHeight;
if (!percentWidth && (!autosize || (autosize && width))) { width = element.outerWidth(); }
if (!percentHeight && (!autosize || (autosize && height))) { height = element.outerHeight(); }
element.wrap(
$("
")
.addClass("k-animation-container")
.css({
width: width,
height: height,
marginLeft: left * (isRtl ? 1 : -1),
paddingLeft: left,
paddingRight: right,
paddingBottom: 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,
DataSource = kendo.data.DataSource,
property,
propValue,
propType,
destProp;
for (property in source) {
propValue = source[property];
propType = typeof propValue;
if (propType === OBJECT && propValue !== null && propValue.constructor !== Array &&
propValue.constructor !== ObservableArray && propValue.constructor !== DataSource) {
if (propValue instanceof Date) {
destination[property] = new Date(propValue.getTime());
} 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() {
support.scrollbar = function() {
var div = document.createElement("div"),
result;
div.style.cssText = "overflow:scroll;overflow-x:hidden;zoom:1;clear:both";
div.innerHTML = " ";
document.body.appendChild(div);
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");
// Internet Explorer does not support setting the innerHTML of TBODY and TABLE elements
try {
table.innerHTML = " ";
support.tbodyInnerHtml = true;
} catch (e) {
support.tbodyInnerHtml = false;
}
support.touch = "ontouchstart" in window;
support.msPointers = navigator.msPointerEnabled;
support.pointers = navigator.pointerEnabled;
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;
}
});
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) {
//window.outerWidth throws error when in IE showModalDialog.
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 = {
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+)?)/,
wp: /(Windows Phone(?: OS)?)\s(\d+)\.(\d+(\.\d+)?)/,
windows: /(MSIE)\s+(\d+)\.(\d+(\.\d+)?)/,
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/,
meego: /meego|ffos/
},
formFactorRxs = {
tablet: /playbook|ipad|fire/i
},
browserRxs = {
omini: /Opera\sMini/i,
omobile: /Opera\sMobi/i,
firefox: /Firefox|Fennec/i,
mobilesafari: /version\/.*safari/i,
chrome: /chrome/i,
webkit: /webkit/i,
ie: /MSIE|Windows\sPhone/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; } // Break if not Metro/Mobile Windows
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.appMode = window.navigator.standalone || (/file|local|wmapp/).test(window.location.protocol) || typeof window.PhoneGap !== UNDEFINED || typeof window.cordova !== UNDEFINED; // Use file protocol to detect appModes.
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.majorVersion > 4) || (mobileOS.android && mobileOS.majorVersion > 2) || mobileOS.wp) {
support.hasNativeScrolling = mobileOS;
}
support.mouseAndTouchPresent = support.touch && !(support.mobileOS.ios || support.mobileOS.android);
support.detectBrowser = function(ua) {
var browser = false, match = [],
browserRxs = {
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()] = true;
browser.version = parseInt(document.documentMode || match[2], 10);
break;
}
}
}
return browser;
};
support.browser = support.detectBrowser(navigator.userAgent);
support.zoomLevel = function() {
try {
return support.touch ? (document.documentElement.clientWidth / window.innerWidth) :
support.browser.msie && support.browser.version >= 10 ? ((top || window).outerWidth / (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) {
// add browser-specific CSS class
var cssClass,
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";
}
if (cssClass) {
$(document.documentElement).addClass("k-" + cssClass + " k-" + cssClass + majorVersion);
}
})(support.browser);
support.eventCapture = document.documentElement.addEventListener;
var input = document.createElement("input");
support.placeholder = "placeholder" 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;
})();
support.stableSort = (function() {
var sorted = [0,1,2,3,4,5,6,7,8,9,10,11,12].sort(function() { return 0; } );
return sorted[0] === 0 && sorted[1] === 1 && sorted[2] === 2 && sorted[3] === 3 && sorted[4] === 4 &&
sorted[5] === 5 && sorted[6] === 6 && sorted[7] === 7 && sorted[8] === 8 &&
sorted[9] === 9 && sorted[10] === 10 && sorted[11] === 11 && sorted[12] === 12;
})();
support.matchesSelector = elementProto.webkitMatchesSelector || elementProto.mozMatchesSelector ||
elementProto.msMatchesSelector || elementProto.oMatchesSelector || elementProto.matchesSelector ||
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)); // old IE detection
})();
function size(obj) {
var result = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key) && key != "toJSON") { // Ignore fake IE7 toJSON.
result++;
}
}
return result;
}
function getOffset(element, type, positioned) {
if (!type) {
type = "offset";
}
var result = element[type](),
mobileOS = support.mobileOS;
if (support.touch && mobileOS.ios && mobileOS.flatVersion < 410) { // Extra processing only in broken iOS'
var offset = type == "offset" ? result : element.offset(),
position = (result.left == offset.left && result.top == offset.top);
if (position) {
return {
top: result.top - window.scrollY,
left: result.left - window.scrollX
};
}
}
// TODO: Switch to browser detection here
if ((kendo.support.pointers || kendo.support.msPointers) && !positioned) { // IE10 touch zoom is living in a separate viewport.
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, {
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); // call the external complete callback with the element
}
element.dequeue();
},
transitionPromise: function(element, destination, options) {
var container = kendo.wrap(element);
container.append(destination);
element.hide();
destination.show();
if (options.completeCallback) {
options.completeCallback(element); // call the external complete callback with the element
}
return element;
}
});
function prepareAnimationOptions(options, duration, reverse, complete) {
if (typeof options === STRING) {
// options is the list of effect names separated by space e.g. animate(element, "fadeIn slideDown")
// only callback is provided e.g. animate(element, options, function() {});
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({
//default options
effects: {},
duration: 400, //jQuery default duration
reverse: false,
init: noop,
teardown: noop,
hide: false
}, options, { completeCallback: options.complete, complete: noop }); // Move external complete callback, so deferred.resolve can be always executed.
}
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 animateTo(element, destination, options, duration, reverse, complete) {
return effects.transitionPromise(element, destination, prepareAnimationOptions(options, duration, reverse, complete));
}
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);
},
kendoAnimateTo: function(destination, options, duration, reverse, complete) {
return animateTo(this, destination, 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;
function htmlEncode(value) {
return ("" + value).replace(ampRegExp, "&").replace(ltRegExp, "<").replace(gtRegExp, ">");
}
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, {
ui: kendo.ui || {},
fx: kendo.fx || fx,
effects: kendo.effects || effects,
mobile: kendo.mobile || {},
data: kendo.data || {},
dataviz: kendo.dataviz || {ui: { roles: {}}},
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
},
support: kendo.support || support,
animate: kendo.animate || animate,
ns: "",
attr: function(value) {
return "data-" + kendo.ns + value;
},
wrap: wrap,
deepExtend: deepExtend,
getComputedStyles: getComputedStyles,
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 = wrapExpression(expression.split("."), paramName);
} else {
expression = paramName + expression;
}
return expression;
},
getter: function(expression, safe) {
return getterCache[expression] = getterCache[expression] || 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);
},
triggeredByInput: function(e) {
return (/^(label|input|textarea|select)$/i).test(e.target.tagName);
},
logToConsole: function(message) {
var console = window.console;
if (typeof(console) != "undefined" && console.log) {
console.log(message);
}
}
});
var Widget = Observable.extend( {
init: function(element, options) {
var that = this;
that.element = kendo.jQuery(element).handler(that);
Observable.fn.init.call(that);
options = that.options = extend(true, {}, that.options, options);
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) {
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]);
}
}
$.extend(that.options, options);
that.bind(that.events, options);
},
resize: function(force) {
var size = this.getSize(),
currentSize = this._size;
if (force || !currentSize || size.width !== currentSize.width || size.height !== currentSize.height) {
this._resize(size);
this.trigger("resize", size);
this._size = 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();
}
});
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+)(:[^\}]+)?\}/,
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 = evil("(" + 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;
// Preserve backwards compatibility with (element, options, namespace) signature, where namespace was kendo.ui
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;
}
if (role.indexOf(".") === -1) {
widget = roles[role];
} else { // full namespace path - like kendo.ui.Widget
widget = kendo.getter(role)(window);
}
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);
}
}
result = $(element).data("kendo" + widget.fn.options.prefix + widget.fn.options.name);
if (!result) {
result = new widget(element, options);
} else {
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 widget = kendo.widgetInstance($(this));
if (widget) {
widget.destroy();
}
});
};
kendo.resize = function(element) {
$(element).each(function() {
var child = $(this), widget;
if (!child.is(":visible") &&
$.inArray(child.attr("data-role"), ["slider", "rangeslider"]) === -1) {
return;
}
if (child.is("[data-" + kendo.ns + "role]")) {
widget = kendo.widgetInstance(child);
if (widget) {
widget.resize();
}
}
kendo.resize(child.children());
});
};
kendo.parseOptions = parseOptions;
extend(kendo.ui, {
Widget: Widget,
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 = $("")
.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;
$.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() {
new widget(this, options);
});
}
return value;
};
$.fn[getter] = function() {
return this.data(name);
};
}
});
var ContainerNullObject = { bind: function () { return this; } };
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);
},
container: function() {
var element = this.element.closest(kendo.roleSelector("view layout modalview drawer"));
return kendo.widgetInstance(element, kendo.mobile.ui) || ContainerNullObject;
}
});
extend(kendo.mobile, {
init: function(element) {
kendo.init(element, kendo.mobile.ui, kendo.ui, kendo.dataviz.ui);
},
ui: {
Widget: MobileWidget,
roles: {},
plugin: function(widget) {
kendo.ui.plugin(widget, kendo.mobile.ui, "Mobile");
}
}
});
kendo.touchScroller = function(elements, options) {
// return the first touch scroller
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, suite) {
var role = element.data(kendo.ns + "role");
// HACK!!! mobile view scroller widgets are instantiated on data-role="content" elements. We need to discover them when resizing.
if (role === "content") {
role = "scroller";
}
if (!suite) {
suite = { roles: $.extend({}, kendo.mobile.ui.roles, kendo.dataviz.ui.roles, kendo.ui.roles) };
}
var widget = suite.roles[role];
if (widget) {
return element.data("kendo" + widget.fn.options.prefix + widget.fn.options.name);
}
};
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 !$(element).parents().addBack().filter(function() {
return $.css(this,"visibility") === "hidden" || $.expr.filters.hidden(this);
}).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)) { // IE10
// Create MSPointerEnter/MSPointerLeave events using mouseover/out and event-time checks
$.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;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
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");
// support for event map signature
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);
// setup mouse trap
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;
});
return rules[jQuery.inArray(utcTime, rules) - 1];
}
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 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 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_MINUTE: MS_PER_MINUTE,
setTime: setTime,
addDays: addDays,
today: today,
toInvariantTime: toInvariantTime,
firstDayOfMonth: firstDayOfMonth,
lastDayOfMonth: lastDayOfMonth,
getMilliseconds: getMilliseconds
//TODO methods: combine date portion and time portion from arguments - date1, date 2
};
})();
kendo.stripWhitespace = function(element) {
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);
}
}
};
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() {
var callback = animationQueue.shift();
kendo.animationFrame(function() {
callback();
if (animationFrame[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;
};
})(jQuery, eval);
/*global kendo_module:true */
if (typeof kendo_module === "undefined") {
kendo_module = function(){};
}
kendo_module({
id: "core",
name: "Core",
category: "framework",
description: "The core of the Kendo framework."
});
kendo_module({
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",
support = kendo.support,
location = window.location,
history = window.history,
CHECK_URL_INTERVAL = 50,
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 stripRoot(root, url) {
if (url.indexOf(root) === 0) {
return (url.substr(root.length)).replace(/\/\//g, '/');
} else {
return root;
}
}
var PushStateAdapter = kendo.Class.extend({
init: function(root) {
this.root = root;
},
navigate: function(to) {
history.pushState({}, document.title, absoluteURL(to, this.root));
return this.current();
},
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");
}
});
var HashAdapter = kendo.Class.extend({
navigate: function(to) {
location.hash = to;
return to;
},
change: function(callback) {
if (support.hashChange) {
$(window).bind("hashchange.kendo", callback);
} else {
this._interval = setInterval(callback, CHECK_URL_INTERVAL);
}
},
stop: function() {
$(window).unbind("popstate.kendo");
clearInterval(this._interval);
},
current: function() {
return location.hash.replace(hashStrip, '');
}
});
var History = kendo.Observable.extend({
start: function(options) {
options = options || {};
this.bind([CHANGE, BACK], options);
if (this._started) {
return;
}
this._started = true;
var pathname = location.pathname,
hash = location.hash,
pushState = support.pushState && options.pushState,
root = options.root || "/",
atRoot = root === pathname;
this.adapter = pushState ? new PushStateAdapter(root) : new HashAdapter();
if (options.pushState && !support.pushState && !atRoot) {
location.replace(root + '#' + stripRoot(root, pathname));
return true; // browser will reload at this point.
}
if (pushState) {
var fixedUrl;
if (root === pathname + "/") {
fixedUrl = root;
}
if (atRoot && hash) {
fixedUrl = absoluteURL(hash.replace(hashStrip, ''), root);
}
if (fixedUrl) {
history.replaceState({}, document.title, fixedUrl);
}
}
this.root = root;
this.current = this.adapter.current();
this.locations = [this.current];
this.adapter.change($.proxy(this, "_checkUrl"));
},
stop: function() {
if (!this._started) {
return;
}
this.adapter.stop();
this.unbind(CHANGE);
this._started = false;
},
change: function(callback) {
this.bind(CHANGE, callback);
},
navigate: function(to, silent) {
if (to === "#:back") {
history.back();
return;
}
to = to.replace(hashStrip, '');
if (this.current === to || this.current === decodeURIComponent(to)) {
return;
}
if (!silent) {
if (this.trigger(CHANGE, { url: to })) {
return;
}
}
this.current = this.adapter.navigate(to);
this.locations.push(this.current);
},
_checkUrl: function() {
var current = this.adapter.current(),
back = current === this.locations[this.locations.length - 2],
prev = this.current;
if (this.current === current || this.current === decodeURIComponent(current)) {
return;
}
this.current = current;
if (back && this.trigger("back", { url: prev, to: current })) {
history.forward();
this.current = prev;
return;
}
if (this.trigger(CHANGE, { url: current })) {
if (back) {
history.forward();
} else {
history.back();
}
this.current = prev;
return;
}
if (back) {
this.locations.pop();
} else {
this.locations.push(current);
}
}
});
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",
optionalParam = /\((.*?)\)/g,
namedParam = /(\(\?)?:\w+/g,
splatParam = /\*\w+/g,
escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
function namedParamReplace(match, optional) {
return optional ? match : '([^\/]+)';
}
function routeToRegExp(route) {
return new RegExp('^' + route
.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, namedParamReplace)
.replace(splatParam, '(.*?)') + '$');
}
function stripUrl(url) {
return url.replace(/(\?.*)|(#.*)/g, "");
}
var Route = kendo.Class.extend({
init: function(route, callback) {
if (!(route instanceof RegExp)) {
route = routeToRegExp(route);
}
this.route = route;
this._callback = callback;
},
callback: function(url) {
var params,
idx = 0,
length,
queryStringParams = kendo.parseQueryStringParams(url);
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) {
if (this.route.test(url)) {
this.callback(url);
return true;
} else {
return false;
}
}
});
var Router = Observable.extend({
init: function(options) {
Observable.fn.init.call(this);
this.routes = [];
this.pushState = options ? options.pushState : false;
if (options && options.root) {
this.root = options.root;
}
this.bind([INIT, ROUTE_MISSING, CHANGE], options);
},
destroy: function() {
history.unbind(CHANGE, this._urlChangedProxy);
history.unbind(BACK, this._backProxy);
this.unbind();
},
start: function() {
var that = this,
backProxy = function(e) { that._back(e); },
urlChangedProxy = function(e) { that._urlChanged(e); };
history.start({
change: urlChangedProxy,
back: backProxy,
pushState: that.pushState,
root: that.root
});
var initEventObject = { url: history.current || "/" };
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));
},
navigate: function(url, silent) {
kendo.history.navigate(url, silent);
},
_back: function(e) {
if (this.trigger(BACK, { url: e.url, to: e.to })) {
e.preventDefault();
}
},
_urlChanged: function(e) {
var url = e.url;
if (!url) {
url = "/";
}
if (this.trigger(CHANGE, { url: e.url, params: kendo.parseQueryStringParams(e.url) })) {
e.preventDefault();
return;
}
var idx = 0,
routes = this.routes,
route,
length = routes.length;
for (; idx < length; idx ++) {
route = routes[idx];
if (route.worksWith(url)) {
return;
}
}
if (this.trigger(ROUTE_MISSING, { url: url, params: kendo.parseQueryStringParams(url) })) {
e.preventDefault();
}
}
});
kendo.Router = Router;
})();
kendo_module({
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" ],
hidden: false
});
(function($, undefined) {
var kendo = window.kendo,
Observable = kendo.Observable,
SCRIPT = "SCRIPT",
INIT = "init",
SHOW = "show",
HIDE = "hide",
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.tagName = options.tagName || "div";
that.model = options.model;
that._wrap = options.wrap !== false;
that.bind([ INIT, SHOW, HIDE ], options);
},
render: function(container) {
var that = this,
notInitialized = !that.element;
// The order below matters - kendo.bind should be happen when the element is in the DOM, and SHOW should be triggered after INIT.
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.trigger(SHOW);
}
return that.element;
},
hide: function() {
this.element.detach();
this.trigger(HIDE);
},
destroy: function() {
var element = this.element;
if (element) {
kendo.unbind(element);
kendo.destroy(element);
element.remove();
}
},
_createElement: function() {
var that = this,
element,
content;
try {
content = $(document.getElementById(that.content) || that.content); // support passing id without #
} catch(e) {
if (sizzleErrorRegExp.test(e.message)) {
content = that.content;
}
}
element = $("<" + that.tagName + " />").append(content[0].tagName === SCRIPT ? content.html() : content);
// drop the wrapper if asked - this seems like the easiest (although not very intuitive) way to avoid messing up templates with questionable content, like the one below
//
if (!that._wrap) {
element = element.contents();
}
return element;
}
});
var Layout = View.extend({
init: function(content, options) {
View.fn.init.call(this, content, options);
this.regions = {};
},
showIn: function(container, view) {
var previousView = this.regions[container];
if (previousView) {
previousView.hide();
}
view.render(this.render().find(container), previousView);
this.regions[container] = view;
}
});
kendo.Layout = Layout;
kendo.View = View;
})(window.kendo.jQuery);
kendo_module({
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 + ")",
ios7 = support.mobileOS && support.mobileOS.majorVersion == 7,
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);
}
}
});
/* jQuery support for all transform animations (FF 3.5/3.6, Opera 10.x, IE9 */
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); // TODO: this should fire a kendoAnimate session instead.
}
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();
}), // Support for old zoomIn/fadeOut style, now deprecated.
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 parseTransitionEffects(options) {
var effects = options.effects;
if (effects === "zoom") {
effects = "zoom:in fade:in";
}
if (effects === "fade") {
effects = "fade:in";
}
if (effects === "slide") {
effects = "tile:left";
}
if (/^slide:(.+)$/.test(effects)) {
effects = "tile:" + RegExp.$1;
}
if (effects === "overlay") {
effects = "slideIn:left";
}
if (/^overlay:(.+)$/.test(effects)) {
effects = "slideIn:" + RegExp.$1;
}
options.effects = kendo.parseEffects(effects);
if (ios7 && effects == "tile:left") {
options.previousDivisor = 3;
}
return options;
}
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);
/**
* Use transitionEnd event for browsers who support it - but duplicate it with setTimeout, as the transitionEnd event will not be triggered if no CSS properties change.
* This should be cleaned up at some point (widget by widget), and refactored to widgets not relying on the complete callback if no transition occurs.
*
* For IE9 and below, resort to setTimeout.
*/
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();
}
}
// legacy support for options.properties
for (var effectName in options.effects) {
extend(end, options.effects[effectName].properties);
}
// Show the element initially
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); // Nudge
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(); // call next animation from the queue
if (options.hide) {
element.data("olddisplay", element.css("display")).hide();
}
this.restoreCallback();
if (hasZoom && !transforms) {
setTimeout($.proxy(this, "restoreCallback"), 0); // Again jQuery callback in IE8-
}
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 { // Not sure how would an fx promise reach this state - means that you call kendoAnimate with no valid effects? Why?
if (!element.is(":visible")) {
element.css({ display: element.data("olddisplay") || "block" }).css("display");
}
if (options.init) {
options.init();
}
element.dequeue();
effectSet.complete();
}
};
fx.transitionPromise = function(element, destination, options) {
fx.animateTo(element, destination, options);
return element;
};
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 }); // Stop animate from showing/hiding the element to be able to hide it later on.
} else {
elements.each(function() {
var element = $(this),
multiple = {};
each(transformProps, function(idx, value) { // remove transforms to avoid IE and older browsers confusion
var params,
currentValue = properties ? properties[value]+ " " : null; // We need to match
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 }); // Stop animate from showing/hiding the element to be able to hide it later on.
});
}
}
},
animateTo: function(element, destination, options) {
var direction,
commonParent = element.parents().filter(destination.parents()).first(),
both = $().add(element.parent()).add(destination.parent()),
isAbsolute = element.css(POSITION) == "absolute",
originalOverflow, originalPosition;
if (!isAbsolute) {
originalPosition = both.css(POSITION);
both.css(POSITION, "absolute");
}
options = parseTransitionEffects(options);
if (!support.mobileOS.android) {
originalOverflow = commonParent.css(OVERFLOW);
commonParent.css(OVERFLOW, "hidden");
}
$.each(options.effects, function(name, definition) {
direction = direction || definition.direction;
});
function complete(animatedElement) {
destination[0].style.cssText = "";
element.each(function() { this.style.cssText = ""; });
if (!support.mobileOS.android) {
commonParent.css(OVERFLOW, originalOverflow);
}
if (!isAbsolute) {
both.css(POSITION, originalPosition);
}
if (options.completeCallback) {
options.completeCallback.call(element, animatedElement);
}
}
options.complete = browser.msie ? function() { setTimeout(complete, 0); } : complete;
options.previous = (options.reverse ? destination : element);
options.reset = true; // Reset transforms if there are any.
// execute callback only once, and hook up derived animations to previous view only once.
(options.reverse ? element : destination).each(function() {
$(this).kendoAnimate(extend(true, {}, options));
options.complete = null;
options.previous = null;
});
}
});
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 = [];
}
},
// Public API
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); // Nudge
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(); // call next animation from the queue
that.restoreCallback();
if (that.shouldHide()) {
element.data("olddisplay", element.css("display")).hide();
}
if (hasZoom && !transforms) {
setTimeout($.proxy(that, "restoreCallback"), 0); // Again jQuery callback in IE8-
}
that.teardown();
},
/////////////////////////// Support for kendo.animate;
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); // jQuery animate complete callback in IE is called before the last animation step!
}
}
});
var TRANSFER_START_STATE = { position: "absolute", marginLeft: 0, marginTop: 0, scale: 1 };
/**
* Intersection point formulas are taken from here - http://zonalandeducation.com/mmts/intersections/intersectionOfTwoLines1/intersectionOfTwoLines1.html
* Formula for a linear function from two points from here - http://demo.activemath.org/ActiveMath2/search/show.cmd?id=mbase://AC_UK_calculus/functions/ex_linear_equation_two_points
* The transform origin point is the intersection point of the two lines from the top left corners/top right corners of the element and target.
* The math and variables below MAY BE SIMPLIFIED (zeroes removed), but this would make the formula too cryptic.
*/
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,
options = that.options,
reverse = that._reverse,
target = options.target,
offset,
currentScale = animationProperty(element, "scale"),
targetOffset = target.offset(),
scale = target.outerHeight() / element.outerHeight();
extend(start, TRANSFER_START_STATE);
end.scale = 1;
element.css(TRANSFORM, "scale(1)").css(TRANSFORM);
offset = element.offset();
element.css(TRANSFORM, "scale(" + currentScale + ")");
var x1 = 0,
y1 = 0,
x2 = targetOffset.left - offset.left,
y2 = targetOffset.top - offset.top,
x3 = x1 + element.outerWidth(),
y3 = y1,
x4 = x2 + target.outerWidth(),
y4 = y2,
Z1 = (y2 - y1) / (x2 - x1),
Z2 = (y4 - y3) / (x4 - x3),
X = (y1 - y3 - Z1 * x1 + Z2 * x3) / (Z2 - Z1),
Y = y1 + Z1 * (X - x1);
start.top = offset.top;
start.left = offset.left;
start.transformOrigin = X + PX + " " + Y + PX;
if (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";
// hack to trigger transition end.
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 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.done()) {
this._started = true;
kendo.animationFrame(this._tickProxy);
}
},
cancel: function() {
this._started = false;
this.onCancel();
},
_tick: function() {
var that = this;
if (!that._started) { return; }
that.tick();
if (!that.done()) {
kendo.queueAnimation(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;
})(window.kendo.jQuery);
kendo_module({
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"
},
mappers = {
pageSize: $.noop,
page: $.noop,
filter: function(params, filter) {
if (filter) {
params.$filter = toOdataFilter(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) {
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);
} else {
ignoreCase = filter.ignoreCase;
field = field.replace(/\./g, "/");
filter = odataFilters[operator];
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") {
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") {
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;
}
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, // to prevent jQuery from adding cache buster
dataType: "jsonp",
jsonp: "$callback"
},
update: {
cache: true,
dataType: "json",
contentType: "application/json", // to inform the server the the request body is JSON encoded
type: "PUT" // can be PUT or MERGE
},
create: {
cache: true,
dataType: "json",
contentType: "application/json",
type: "POST" // must be POST to create new entity
},
destroy: {
cache: true,
dataType: "json",
type: "DELETE"
},
parameterMap: function(options, type) {
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]);
} 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;
}
}
}
});
})(window.kendo.jQuery);
kendo_module({
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)) {
if (model.fields) {
each(model.fields, function(field, value) {
if (isPlainObject(value) && 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 = kendo.data.Model.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) {
// text nodes or CDATA are stored as #text field
result["#text"] = node.nodeValue;
} else if (nodeType === 1) {
// elements are stored as fields
parsedNode = this.parseDOM(node);
nodeName = node.nodeName;
member = result[nodeName];
if (isArray(member)) {
// elements of same nodeName are stored as array
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(/^\//, "") // remove the first "/"
.replace(/\//g, "."); // replace all "/" with "."
if (member.indexOf("@") >= 0) {
// replace @attribute with '["@attribute"]'
return member.replace(/\.?(@.*)/, raw? '$1':'["$1"]');
}
if (member.indexOf("text()") >= 0) {
// replace ".text()" with '["#text"]'
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);
kendo_module({
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-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);
},
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 || "itemchange") : "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,
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].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;
},
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;
},
// non-standard collection methods
remove: function(item) {
this.splice(this.indexOf(item), 1);
},
empty: function() {
this.splice(0, this.length);
}
});
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);
for (field in value) {
member = value[field];
if (field.charAt(0) != "_") {
member = that.wrap(member, field, parent);
}
that[field] = member;
}
that.uid = kendo.guid();
},
shouldSerialize: function(field) {
return this.hasOwnProperty(field) && 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,
current = kendo.getter(field, true)(that);
if (current !== value) {
if (!that.trigger("set", { field: field, value: value })) {
if (!that._set(field, that.wrap(value, field, function() { return that; })) || field.indexOf("(") >= 0 || field.indexOf("[") >= 0) {
that.trigger(CHANGE, { field: field });
}
}
}
},
parent: noop,
wrap: function(object, field, parent) {
var that = this,
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);
}
if (object.parent() != parent()) {
object.bind(GET, eventHandler(that, GET, field, true));
object.bind(CHANGE, eventHandler(that, CHANGE, field, true));
}
} else if (type === "[object Array]" || isObservableArray || isDataSource) {
if (!isObservableArray && !isDataSource) {
object = new ObservableArray(object);
}
if (object.parent() != parent()) {
object.bind(CHANGE, eventHandler(that, CHANGE, field, false));
}
}
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);
}
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;
if (that.editable(field)) {
value = that._parse(field, value);
if (!equal(value, that.get(field))) {
that.dirty = true;
ObservableObject.fn.set.call(that, field, value, initiator);
}
}
},
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;
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 (options.id === name) {
proto._defaultId = value;
}
proto.defaults[originalName !== name ? originalName : name] = value;
field.parse = field.parse || parsers[type];
}
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) {
//b looks like a Date
a = "(" + a + "?" + a + ".getTime():" + a + ")";
b = b.getTime();
}
}
return a + " " + op + " " + b;
}
return {
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";
},
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";
},
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";
}
};
})();
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);
//Nested function fields or operators - update their index e.g. __o[0] -> __o[1]
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 + ", " + 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"
};
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 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 = {};
if (aggregates && aggregates.length) {
for(idx = 0, len = this.data.length; idx < len; idx++) {
calculateAggregate(result, aggregates, this.data[idx], idx, len);
}
}
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) {
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] || {};
accumulator[field][functionName] = functions[functionName.toLowerCase()](accumulator[field][functionName], item, kendo.accessor(field), index, length);
}
}
var functions = {
sum: function(accumulator, item, accessor) {
return (accumulator || 0) + accessor.get(item);
},
count: function(accumulator) {
return (accumulator || 0) + 1;
},
average: function(accumulator, item, accessor, index, length) {
accumulator = (accumulator || 0) + accessor.get(item);
if(index == length - 1) {
accumulator = accumulator / length;
}
return accumulator;
},
max: function(accumulator, item, accessor) {
var value = accessor.get(item);
accumulator = accumulator || 0;
if(accumulator < value) {
accumulator = value;
}
return accumulator;
},
min: function(accumulator, item, accessor) {
var value = accessor.get(item);
if (!isNumber(accumulator)) {
accumulator = value;
}
if(accumulator > value && isNumber(value)) {
accumulator = value;
}
return accumulator;
}
};
function isNumber(val) {
return typeof val === "number" && !isNaN(val);
}
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,
filter = options.filter,
skip = options.skip,
take = options.take;
if (filter) {
query = query.filter(filter);
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()
};
};
function calculateAggregates(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);
}
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;
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,
length;
for (idx = 0, length = data.length; idx < length; idx++) {
record = data[idx];
for (getter in getters) {
originalName = fieldNames[getter];
if (originalName && originalName !== getter) {
record[originalName] = 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);
}
if (that.model) {
var dataFunction = proxy(that.data, that),
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.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, start, count) {
var group,
idx = 0,
items;
while (dest.length && count) {
group = dest[idx];
items = group.items;
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, start, count);
} else {
items = items.slice(start, count);
count -= items.length;
target.items = target.items.concat(items);
}
dest.splice(idx--, 1);
} else {
items = items.slice(start, count);
count -= items.length;
group.items = items;
if (!group.items.length) {
dest.splice(idx--, 1);
count -= start;
}
}
start = 0;
if (++idx >= dest.length) {
break;
}
}
if (idx < dest.length) {
dest.splice(idx, dest.length - idx);
}
}
function flattenGroups(data) {
var idx, length, result = [];
for (idx = 0, length = data.length; idx < length; idx++) {
if (data[idx].hasSubgroups) {
result = result.concat(flattenGroups(data[idx].items));
} else {
result = result.concat(data[idx].items.slice());
}
}
return result;
}
function wrapGroupItems(data, model) {
var idx, length, group, items;
if (model) {
for (idx = 0, length = data.length; idx < length; idx++) {
group = data[idx];
items = group.items;
if (group.hasSubgroups) {
wrapGroupItems(items, model);
} else if (items.length && !(items[0] instanceof model)) {
items.type = model;
items.wrapAll(items, items);
}
}
}
}
function eachGroupItems(data, func) {
var idx, length;
for (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 removeModel(data, model) {
var idx, length;
for (idx = 0, length = data.length; idx < length; idx++) {
if (data[idx].uid == model.uid) {
model = data[idx];
data.splice(idx, 1);
return model;
}
}
}
function wrapInEmptyGroup(groups, model) {
var 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: {}
};
}
return parent;
}
function indexOfPristineModel(data, model) {
if (model) {
return indexOf(data, function(item) {
return item[model.idField] === model.id;
});
}
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._pristine = [];
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;
Observable.fn.init.call(that);
that.transport = Transport.create(options, data);
that.reader = new kendo.data.readers[options.schema.type || "json" ](options.schema);
model = that.reader.model || {};
that._data = that._observe(that._data);
that.bind([ERROR, CHANGE, REQUESTSTART, SYNC, REQUESTEND, PROGRESS], options);
},
options: {
data: [],
schema: {
modelBase: Model
},
serverSorting: false,
serverPaging: false,
serverFiltering: false,
serverGrouping: false,
serverAggregates: false,
batch: false
},
_isServerGrouped: function() {
var group = this.group() || [];
return this.options.serverGrouping && group.length;
},
_flatData: function(data) {
if (this._isServerGrouped()) {
return flattenGroups(data);
}
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[index];
},
data: function(value) {
var that = this;
if (value !== undefined) {
that._data = this._observe(value);
that._ranges = [];
that._addRange(that._data);
that._total = that._data.length;
that._process(that._data);
} else {
return that._data;
}
},
view: function() {
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);
}
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, wrapInEmptyGroup(this.group(), model));
} else {
this._data.splice(index, 0, model);
}
return model;
},
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;
}
});
return model;
},
sync: function() {
var that = this,
idx,
length,
created = [],
updated = [],
destroyed = that._destroyed,
data = that._flatData(that._data);
if (!that.reader.model) {
return;
}
for (idx = 0, length = data.length; idx < length; idx++) {
if (data[idx].isNew()) {
created.push(data[idx]);
} else if (data[idx].dirty) {
updated.push(data[idx]);
}
}
var promises = that._send("create", created);
promises.push.apply(promises ,that._send("update", updated));
promises.push.apply(promises ,that._send("destroy", destroyed));
$.when.apply(null, promises)
.then(function() {
var idx, length;
for (idx = 0, length = arguments.length; idx < length; idx++){
that._accept(arguments[idx]);
}
that._change({ action: "sync" });
that.trigger(SYNC);
});
},
cancelChanges: function(model) {
var that = this;
if (model instanceof kendo.data.Model) {
that._cancelModel(model);
} else {
that._destroyed = [];
that._data = that._observe(that._pristineData);
if (that.options.serverPaging) {
that._total = that.reader.total(that._pristine);
}
that._change();
}
},
hasChanges: function() {
var idx,
length,
data = this._data;
if (this._destroyed.length) {
return true;
}
for (idx = 0, length = data.length; idx < length; idx++) {
if (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 ? wrapInEmptyGroup(that.group(), 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(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),
idx;
this._eachItem(this._data, function(items) {
idx = indexOfModel(items, model);
if (idx != -1) {
if (!model.isNew() && pristine) {
items[idx].accept(pristine);
} else {
items.splice(idx, 1);
}
}
});
},
_promise: function(data, models, type) {
var that = this,
transport = that.transport;
return $.Deferred(function(deferred) {
that.trigger(REQUESTSTART, { type: type });
transport[type].call(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);
that._queueRequest(params, function() {
if (!that.trigger(REQUESTSTART, { type: "read" })) {
that.trigger(PROGRESS);
that._ranges = [];
that.transport.read({
data: params,
success: proxy(that.success, that),
error: proxy(that.error, that)
});
} else {
that._dequeueRequest();
}
});
},
success: function(data) {
var that = this,
options = that.options;
that.trigger(REQUESTEND, { response: data, type: "read" });
data = that.reader.parse(data);
if (that._handleCustomErrors(data)) {
that._dequeueRequest();
return;
}
that._pristine = isPlainObject(data) ? $.extend(true, {}, data) : data.slice ? data.slice(0) : data;
that._total = that.reader.total(data);
if (that._aggregate && options.serverAggregates) {
that._aggregateResult = that.reader.aggregates(data);
}
data = that._readData(data);
that._pristineData = data.slice(0);
that._data = that._observe(data);
that._addRange(that._data);
that._process(that._data);
that._dequeueRequest();
},
_addRange: function(data) {
var that = this,
start = that._skip || 0,
end = start + that._flatData(data).length;
that._ranges.push({ start: start, end: end, data: data });
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;
},
_observe: function(data) {
var that = this,
model = that.reader.model,
wrap = false;
if (model && data.length) {
wrap = !(data[0] instanceof model);
}
if (data instanceof ObservableArray) {
if (wrap) {
data.type = that.reader.model;
data.wrapAll(data, data);
}
} else {
data = new ObservableArray(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);
},
_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")) {
that.sync();
} else {
var total = parseInt(that._total || that.reader.total(that._pristine), 10);
if (action === "add") {
total += e.items.length;
} else if (action === "remove") {
total -= e.items.length;
} else if (action !== "itemchange" && action !== "sync" && !that.options.serverPaging) {
total = that.reader.total(that._pristine);
}
that._total = total;
that._process(that._data, e);
}
},
_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 = calculateAggregates(data, options);
}
result = Query.process(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);
},
_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 = options.skip;
that._take = options.take;
if(that._skip === undefined) {
that._skip = 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 that = this,
result,
remote = that.options.serverSorting || that.options.serverPaging || that.options.serverFiltering || that.options.serverGrouping || that.options.serverAggregates;
if (remote || ((that._data === undefined || that._data.length === 0) && !that._destroyed.length)) {
that.read(that._mergeState(options));
} else {
if (!that.trigger(REQUESTSTART, { type: "read" })) {
that.trigger(PROGRESS);
result = Query.process(that._data, that._mergeState(options));
if (!that.options.serverFiltering) {
if (result.total !== undefined) {
that._total = result.total;
} else {
that._total = that._data.length;
}
}
that._view = result.data;
that._aggregateResult = calculateAggregates(that._data, options);
that.trigger(REQUESTEND, { });
that.trigger(CHANGE, { items: result.data });
}
}
},
fetch: function(callback) {
var that = this;
return $.Deferred(function(deferred) {
var success = function(e) {
that.unbind(ERROR, error);
deferred.resolve();
if (callback) {
callback.call(that, e);
}
};
var error = function(e) {
deferred.reject(e);
};
that.one(CHANGE, success);
that.one(ERROR, error);
that._query();
}).promise();
},
_query: function(options) {
var that = this;
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 = 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._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._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() {
return this._aggregateResult;
},
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;
},
range: function(skip, take) {
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._skip = skip > that.skip() ? math.min(size, (that.totalPages() - 1) * that.take()) : pageSkip;
that._take = take;
var paging = that.options.serverPaging;
var sorting = that.options.serverSorting;
var filtering = that.options.serverFiltering;
try {
that.options.serverPaging = true;
if (!that._isServerGrouped() && !(that.group() && that.group().length)) {
that.options.serverSorting = true;
}
that.options.serverFiltering = true;
if (paging) {
that._data = data = that._observe(data);
}
that._process(data);
} finally {
that.options.serverPaging = paging;
that.options.serverSorting = sorting;
that.options.serverFiltering = filtering;
}
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);
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 = Query.process(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, startIndex, endIndex) {
if (this._isServerGrouped()) {
var temp = range.toJSON(),
prevGroup;
if (data.length) {
prevGroup = data[data.length - 1];
}
mergeGroups(prevGroup, temp, startIndex, endIndex);
return data.concat(temp);
}
return data.concat(range.slice(startIndex, endIndex));
},
skip: function() {
var that = this;
if (that._skip === undefined) {
return (that._page !== undefined ? (that._page - 1) * (that.take() || 1) : undefined);
}
return that._skip;
},
take: function() {
return this._take || this._pageSize;
},
_prefetchSuccessHandler: function (skip, size, callback) {
var that = this;
return function(data) {
var found = false,
range = { start: skip, end: size, data: [] },
idx,
length;
that._dequeueRequest();
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);
}
that.trigger(REQUESTEND, { response: data, type: "read" });
data = that.reader.parse(data);
range.data = that._observe(that._readData(data));
range.end = range.start + that._flatData(range.data).length;
that._ranges.sort( function(x, y) { return x.start - y.start; } );
that._total = that.reader.total(data);
if (callback) {
callback();
}
};
},
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)
});
} else {
that._dequeueRequest();
}
});
}, 100);
} 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;
}
});
var Transport = {};
Transport.create = function(options, data) {
var transport,
transportOptions = options.transport;
if (transportOptions) {
transportOptions.read = typeof transportOptions.read === STRING ? { url: transportOptions.read } : transportOptions.read;
if (options.type) {
if (kendo.data.transports[options.type] && !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) {
options = options && options.push ? { data: options } : 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 (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;
return dataSource instanceof DataSource ? dataSource : new DataSource(dataSource);
};
function inferSelect(select, fields) {
var options = $(select)[0].children,
idx,
length,
data = [],
record,
firstField = fields[0],
secondField = fields[1],
value,
option;
for (idx = 0, length = options.length; idx < length; idx++) {
record = {};
option = options[idx];
if (option.disabled) {
continue;
}
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({
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
}
}
};
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[childrenField] || 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) {
data[that.idField || "id"] = that.id;
if (parameterMap) {
data = parameterMap(data);
}
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();
},
load: function() {
var that = this,
options = {};
if (that.hasChildren) {
that._initChildren();
options[that.idField || "id"] = that.id;
if (!that._loaded) {
that.children._data = undefined;
}
that.children.one(CHANGE, function() {
that._loaded = true;
that._updateChildrenField();
})
._query(options);
} else {
that.loaded(true);
}
},
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, data, children;
node = DataSource.fn[method].call(this, value);
if (node) {
return node;
}
data = this._flatData(this.data());
if (!data) {
return;
}
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();
});
this._syncWithDataSource();
this.setViewSize(viewSize);
},
setViewSize: function(viewSize) {
this.viewSize = viewSize;
this._recalculate();
},
at: function(index) {
var pageSize = this.pageSize, item;
if (index >= this.total()) {
this.trigger("endreached", {index: index });
return;
}
if (!this.useRanges) {
return this.dataSource.view()[index];
}
if (this.useRanges) {
// out of range request
if (index < this.dataOffset || index > this.skip + pageSize) {
var offset = Math.floor(index / pageSize) * pageSize;
this.range(offset);
}
// prefetch
if (index === this.prefetchThreshold) {
this._prefetch();
}
// mid-range jump - prefetchThreshold and nextPageThreshold may be equal, do not change to else if
if (index === this.midPageThreshold) {
this.range(this.nextMidRange);
}
// next range jump
else if (index === this.nextPageThreshold) {
this.range(this.nextFullRange);
}
// pull-back
else if (index === this.pullBackThreshold) {
if (this.offset === this.skip) { // from full range to mid range
this.range(this.previousMidRange);
} else { // from mid range to full range
this.range(this.previousFullRange);
}
}
item = this.dataSource.at(index - this.dataOffset);
}
if (item === undefined) {
this.trigger("endreached", { index: index });
}
return item;
},
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, // this calculation relies that the buffer has already jumped into the mid range segment
pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize + pageSize;
this.offset = offset;
this.dataSource.prefetch(pageSkip, pageSize, function() {
buffer._goToRange(offset, true);
});
},
range: function(offset) {
if (this.offset === offset) {
return;
}
var buffer = this,
pageSize = this.pageSize,
pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize + pageSize,
dataSource = this.dataSource;
this.offset = offset;
this._recalculate();
if (dataSource.inRange(offset, pageSize)) {
this._goToRange(offset);
} else if (this.prefetch) {
dataSource.prefetch(pageSkip, pageSize, function() {
buffer._goToRange(offset, 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);
},
_change: function() {
var dataSource = this.dataSource,
firstItemUid = dataSource.firstItemUid();
this.length = this.useRanges ? dataSource.lastRange().end : dataSource.view().length;
if (this._firstItemUid !== firstItemUid || !this.useRanges) {
this._syncWithDataSource();
this._recalculate();
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 === undefined) {
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,
LocalTransport: LocalTransport,
RemoteTransport: RemoteTransport,
Cache: Cache,
DataReader: DataReader,
Model: Model,
Buffer: Buffer,
BatchBuffer: BatchBuffer
});
})(window.kendo.jQuery);
kendo_module({
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 = {},
splice = Array.prototype.splice,
Class = kendo.Class,
innerText,
proxy = $.proxy,
VALUE = "value",
SOURCE = "source",
EVENTS = "events",
CHECKED = "checked",
CHANGE = "change";
(function() {
var a = document.createElement("a");
if (a.innerText !== undefined) {
innerText = "innerText";
} else if (a.textContent !== undefined) {
innerText = "textContent";
}
})();
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);
// Traverse the observable hierarchy if the binding is not resolved at the current level.
while (result === undefined && source) {
source = that.parents[++index];
if (source instanceof ObservableObject) {
result = source.get(path);
}
}
// second pass try to get the parent from the object hierarchy
if (result === undefined) {
source = that.source; //get the initial source
while (result === undefined && source) {
source = source.parent();
if (source instanceof ObservableObject) {
result = source.get(path);
}
}
}
// If the result is a function - invoke it
if (typeof result === "function") {
index = path.lastIndexOf(".");
// If the function is a member of a nested observable object make that nested observable the context (this) of the function
if (index > 0) {
source = source.get(path.substring(0, index));
}
// Invoke the function
that.start(source);
result = result.call(source, that.source);
that.stop(source);
}
// If the binding is resolved by a parent object
if (source && source !== that.source) {
that.currentSource = source; // save parent object
// Listen for changes in the parent object
source.unbind(CHANGE, that._change)
.bind(CHANGE, that._change);
}
that.stop(that.source);
return result;
},
set: function(value) {
var that = this,
source = that.currentSource || that.source;
source.set(that.path, value);
},
destroy: function() {
if (this.observable) {
this.source.unbind(CHANGE, this._change);
}
}
});
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() {
}
});
binders.attr = Binder.extend({
refresh: function(key) {
this.element.setAttribute(key, this.bindings.attr[key].get());
}
});
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();
if (text == null) {
text = "";
}
this.element[innerText] = text;
}
});
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 = Binder.extend({
init: function(element, bindings, options) {
Binder.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;
var value = this.element.value;
var type = this.element.type;
if (type == "date") {
value = kendo.parseDate(value, "yyyy-MM-dd");
} else if (type == "datetime-local") {
value = kendo.parseDate(value, ["yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm"] );
} else if (type == "number") {
value = kendo.parseFloat(value);
}
this.bindings[VALUE].set(value);
this._initChange = false;
},
refresh: function() {
if (!this._initChange) {
var value = this.bindings[VALUE].get();
if (value == null) {
value = "";
}
var type = this.element.type;
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('#:{1}# ',
options.valueField || options.textField, options.textField || options.valueField);
} else {
template = "#:data# ";
}
} else if (nodeName == "tbody") {
template = "#:data# ";
} else if (nodeName == "ul" || nodeName == "ol") {
template = "#:data# ";
} else {
template = "#:data#";
}
template = kendo.template(template);
}
return template;
},
destroy: function() {
var source = this.bindings.source.get();
source.unbind(CHANGE, this._change);
},
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);
element.removeChild(child);
}
},
render: function() {
var source = this.bindings.source.get(),
parents,
idx,
length,
element = this.container(),
template = this.template(),
parent;
if (source instanceof kendo.data.DataSource) {
source = source.view();
}
if (!(source instanceof ObservableArray) && toString.call(source) !== "[object Array]") {
if (source.parent) {
parent = source.parent;
}
source = new ObservableArray([source]);
if (source.parent) {
source.parent = parent;
}
}
if (this.bindings.template) {
unbindElementChildren(element);
$(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: Binder.extend({
init: function(element, bindings, options) {
Binder.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") {
this.bindings[CHECKED].set(value);
} else if (element.type == "checkbox") {
var source = this.bindings[CHECKED].get();
var index;
if (source instanceof ObservableArray) {
value = this.element.value;
if (value !== "on" && value !== "off") {
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,
element = this.element;
if (element.type == "checkbox") {
if (source instanceof ObservableArray) {
value = this.element.value;
if (source.indexOf(value) >= 0) {
value = true;
}
}
element.checked = value === true;
} else if (element.type == "radio" && value != null) {
if (element.value === value.toString()) {
element.checked = true;
}
}
},
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 = {
value: Binder.extend({
init: function(target, bindings, options) {
Binder.fn.init.call(this, target, bindings, options);
this._change = proxy(this.change, this);
$(this.element).change(this._change);
},
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(value);
}
}
if (field) {
source = this.bindings.source.get();
for (valueIndex = 0; valueIndex < values.length; valueIndex++) {
for (idx = 0, length = source.length; idx < length; idx++) {
if (source[idx].get(field) == values[valueIndex]) {
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 || !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,
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);
}
for (optionIndex = 0; optionIndex < options.length; optionIndex++) {
optionValue = options[optionIndex].value;
if (optionValue === "" && value !== "") {
optionValue = options[optionIndex].text;
}
if (optionValue == value) {
options[optionIndex].selected = true;
found = true;
}
}
}
},
destroy: function() {
$(this.element).off(CHANGE, this._change);
}
})
};
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: 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.source._parents()));
},
dataBinding: function() {
var idx,
length,
widget = this.widget,
items = widget.items();
for (idx = 0, length = items.length; idx < length; idx++) {
unbindElementTree(items[idx]);
}
},
_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 = widget.items(),
dataSource = widget.dataSource,
view = dataSource.view(),
parents,
groups = dataSource.group() || [];
if (items.length) {
if (groups.length) {
view = flattenGroups(view);
}
parents = this.bindings.source._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;
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.source.get();
if (widget.dataSource instanceof kendo.data.DataSource && widget.dataSource != source) {
if (source instanceof kendo.data.DataSource) {
widget.setDataSource(source);
} else if (source && source._dataSource) {
widget.setDataSource(source._dataSource);
} else {
widget.dataSource.data(source);
}
}
}
},
destroy: function() {
var widget = this.widget;
widget.unbind("dataBinding", this._dataBinding);
widget.unbind("dataBound", this._dataBound);
widget.unbind("itemChange", this._itemChange);
}
}),
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;
},
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 (this.bindings.source) {
source = this.bindings.source.get();
}
if (value === "" && (isObservableObject || this.options.valuePrimitive)) {
value = null;
} else {
if (!source || source instanceof kendo.data.DataSource) {
source = this.widget.dataSource.view();
}
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 field = this.options.dataValueField || this.options.dataTextField,
value = this.bindings.value.get(),
idx = 0, length,
values = [];
if (field) {
if (value instanceof ObservableArray) {
for (length = value.length; idx < length; idx++) {
values[idx] = value[idx].get(field);
}
value = values;
} else if (value instanceof ObservableObject) {
value = value.get(field);
}
}
this.widget.value(value);
}
this._initChange = false;
},
destroy: function() {
this.widget.unbind(CHANGE, this._change);
}
}),
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) {
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);
splice.call(oldValues, i, 1);
removeIndex = i;
} else {
i += 1;
}
old = oldValues[i];
}
splice.apply(oldValues, [oldValues.length, 0].concat(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 field = this.options.dataValueField || this.options.dataTextField,
value = this.bindings.value.get(),
idx = 0, length,
values = [],
selectedValue;
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);
}
}
this.widget.value(value);
}
},
destroy: function() {
this.widget.unbind(CHANGE, this._change);
}
})
}
};
var BindingTarget = Class.extend( {
init: function(target, options) {
this.target = target;
this.options = options;
this.toDestroy = [];
},
bind: function(bindings) {
var nodeName = this.target.nodeName.toLowerCase(),
key,
hasValue,
hasSource,
hasEvents,
specificBinders = binders[nodeName] || {};
for (key in bindings) {
if (key == VALUE) {
hasValue = true;
} else if (key == SOURCE) {
hasSource = true;
} else if (key == EVENTS) {
hasEvents = true;
} else {
this.applyBinding(key, bindings, specificBinders);
}
}
if (hasSource) {
this.applyBinding(SOURCE, bindings, specificBinders);
}
if (hasValue) {
this.applyBinding(VALUE, bindings, specificBinders);
}
if (hasEvents) {
this.applyBinding(EVENTS, bindings, specificBinders);
}
},
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( {
bind: function(bindings) {
var that = this,
binding,
hasValue = false,
hasSource = false,
specificBinders = binders.widget[that.target.options.name.toLowerCase()] || {};
for (binding in bindings) {
if (binding == VALUE) {
hasValue = true;
} else if (binding == SOURCE) {
hasSource = true;
} else {
that.applyBinding(binding, bindings);
}
}
if (hasSource) {
that.applyBinding(SOURCE, bindings);
}
if (hasValue) {
that.applyBinding(VALUE, bindings, specificBinders[VALUE]);
}
},
applyBinding: function(name, bindings, specificBinder) {
var binder = specificBinder || 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 flattenGroups(data) {
var idx, length, result = [];
for (idx = 0, length = data.length; idx < length; idx++) {
if (data[idx].hasSubgroups) {
result = result.concat(flattenGroups(data[idx].items));
} else {
result = result.concat.apply(result, data[idx].items);
}
}
return result;
}
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);
}
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;
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);
}
target.bind(bindings);
}
if (target) {
element.kendoBindingTarget = target;
}
if (deep && children) {
// https://github.com/telerik/kendo/issues/1240 for the weirdness.
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) {
var bindingTarget = element.kendoBindingTarget;
if (bindingTarget) {
bindingTarget.destroy();
if ($.support.deleteExpando) {
delete element.kendoBindingTarget;
} else if (element.removeAttribute) {
element.removeAttribute("kendoBindingTarget");
} else {
element.kendoBindingTarget = null;
}
}
}
function unbindElementTree(element) {
unbindElement(element);
unbindElementChildren(element);
}
function unbindElementChildren(element) {
var children = element.children;
if (children) {
for (var idx = 0, length = children.length; idx < length; idx++) {
unbindElementTree(children[idx]);
}
}
}
function unbind(dom) {
var idx, length;
dom = $(dom);
for (idx = 0, length = dom.length; idx < length; idx++ ) {
unbindElementTree(dom[idx]);
}
}
function notify(widget, namespace) {
var element = widget.element,
bindingTarget = element[0].kendoBindingTarget;
if (bindingTarget) {
bind(element, bindingTarget.source, namespace);
}
}
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);
kendo_module({
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",
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);
}
var Validator = Widget.extend({
init: function(element, options) {
var that = this,
resolved = resolveRules(element);
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._errors = {};
that._attachEvents();
},
events: [ "validate" ],
options: {
name: "Validator",
errorTemplate: '' +
' #=message# ',
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"
},
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 (((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);
},
_submit: function(e) {
if (!this.validate()) {
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
return false;
}
return true;
},
_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, INPUTSELECTOR, function() {
that.validateInput($(this));
});
that.element.on("click" + NS, CHECKBOXSELECTOR, function() {
that.validateInput($(this));
});
} else {
that.element.on(BLUR + NS, function() {
that.validateInput(that.element);
});
if (that.element.is(CHECKBOXSELECTOR)) {
that.element.on("click" + NS, function() {
that.validateInput(that.element);
});
}
}
}
},
validate: function() {
var inputs;
var idx;
var result = false;
var length;
this._errors = {};
if (!this.element.is(INPUTSELECTOR)) {
var invalid = false;
inputs = this.element.find(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 });
return result;
},
validateInput: function(input) {
input = $(input);
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)).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) }));
that._decorateMessageContainer(messageLabel, fieldName);
if (!lbl.replaceWith(messageLabel).length) {
messageLabel.insertAfter(input);
}
messageLabel.show();
input.attr("aria-invalid", true);
}
input.toggleClass(INVALIDINPUT, !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 = $(),
children = this.element[0].getElementsByTagName("*");
for (var idx = 0, length = children.length; idx < length; idx++) {
var element = children[idx];
if (invalidMsgRegExp.test(element.className)) {
var attr = element.getAttribute(kendo.attr("for"));
if (attr === fieldName) {
containers = containers.add(element);
}
}
}
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));
},
_checkValidity: function(input) {
var rules = this.options.rules,
rule;
for (rule in rules) {
if (!rules[rule](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);
kendo_module({
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.ie ? 5 : 0, // WP8 and W8 are very sensitive and always report move.
// UserEvents events
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";
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, // hardcoded ID for API call;
event: e,
target: e.target,
currentTarget: e.target,
location: e
});
}
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
});
}
}
else if (support.pointers || support.msPointers) {
touches.push({
location: originalEvent,
event: e,
target: e.target,
currentTarget: currentTarget,
id: originalEvent.pointerId
});
} else {
touches.push({
id: 1, // hardcoded ID for mouse event;
event: e,
target: e.target,
currentTarget: currentTarget,
location: e
});
}
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; // Firing manually events in tests can make this 0;
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) {
var that = this;
extend(that, {
x: new TouchAxis("X", touchInfo.location),
y: new TouchAxis("Y", touchInfo.location),
userEvents: userEvents,
target: target,
currentTarget: touchInfo.currentTarget,
initialTouch: touchInfo.target,
id: touchInfo.id,
_moved: false,
_finished: false
});
that.press = function() {
that._trigger(PRESS, touchInfo);
that._holdTimeout = setTimeout(function() {
that._trigger(HOLD, touchInfo);
}, userEvents.minHold);
};
},
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();
}
}
// Event handlers may cancel the drag in the START event handler, hence the double check for pressed.
if (!that._finished) {
that._trigger(MOVE, touchInfo);
}
},
end: function(touchInfo) {
var that = this;
that.endTime = now();
if (that._finished) { return; }
if (that._moved) {
that._trigger(END, touchInfo);
} else {
that._trigger(TAP, touchInfo);
}
clearTimeout(that._holdTimeout);
that._trigger(RELEASE, touchInfo);
that.dispose();
},
dispose: function() {
var that = this,
userEvents = that.userEvents,
activeTouches = userEvents.touches;
that._finished = true;
activeTouches.splice($.inArray(that, 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.userEvents.threshold;
}
});
function preventTrigger(e) {
e.preventDefault();
var target = $(e.data.root), // Determine the correct parent to receive the event and bubble.
parent = target.closest(".k-widget").parent();
if (!parent[0]) {
parent = target.parent();
}
var fakeEventData = $.extend(true, {}, e, { target: target[0] });
parent.trigger($.Event(e.type, fakeEventData));
}
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.eventNS = ns;
element = $(element).handler(that);
Observable.fn.init.call(that);
extend(that, {
element: element,
surface: options.global ? $(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 (support.pointers || support.msPointers) {
element.css("-ms-touch-action", "pinch-zoom double-tap-zoom");
}
if (options.preventDragEvent) {
element.on(kendo.applyEventMap("dragstart", ns), kendo.preventDefault);
}
element.on(kendo.applyEventMap("mousedown selectstart", 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;
},
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, data);
},
// API
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 })) {
preventTrigger(e);
}
},
_start: function(e) {
var that = this,
idx = 0,
filter = that.filter,
target,
touches = getTouches(e),
length = touches.length,
touch;
if (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); // target.is(filter) ? target : target.closest(filter, that.element);
} 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);
},
_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,
stopPropagation: $.noop,
preventDefault: $.noop
});
}
});
kendo.getTouches = getTouches;
kendo.touchDelta = touchDelta;
kendo.UserEvents = UserEvents;
})(window.kendo.jQuery);
kendo_module({
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,
Class = kendo.Class,
Widget = kendo.ui.Widget,
Observable = kendo.Observable,
UserEvents = kendo.UserEvents,
proxy = $.proxy,
extend = $.extend,
getOffset = kendo.getOffset,
draggables = {},
dropTargets = {},
dropAreas = {},
lastDropTarget,
OS = support.mobileOS,
invalidZeroEvents = OS && OS.android,
mobileChrome = (invalidZeroEvents && OS.browser == "chrome"),
KEYUP = "keyup",
CHANGE = "change",
// Draggable events
DRAGSTART = "dragstart",
HOLD = "hold",
DRAG = "drag",
DRAGEND = "dragend",
DRAGCANCEL = "dragcancel",
// DropTarget events
DRAGENTER = "dragenter",
DRAGLEAVE = "dragleave",
DROP = "drop";
function contains(parent, child) {
try {
return $.contains(parent, child) || parent == child;
} catch (e) {
return false;
}
}
function elementUnderCursor(e) {
if (mobileChrome) {
return document.elementFromPoint(e.x.screen, e.y.screen);
} else {
return document.elementFromPoint(e.x.client, e.y.client);
}
}
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 (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: 1000,
_virtualMax: -1000
});
},
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();
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);
},
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(["move", "end", "gesturestart", "gesturechange"], {
gesturestart: function(e) {
that.gesture = e;
that.offset = that.dimensions.container.offset();
},
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) {
// Resist shrinking. Instead of shrinking from 1 to 0.5, it will shrink to 0.5 + (1 /* minScale */ - 0.5) * 0.8 = 0.9;
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;
}
});
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() {
var groupName = this.options.group,
group = dropTargets[groupName] || dropAreas[groupName],
i;
if (group.length > 1) {
Widget.fn.destroy.call(this);
for (i = 0; i < group.length; i++) {
if (group[i] == this) {
group.splice(i, 1);
break;
}
}
} else {
DropTarget.destroyGroup(groupName);
}
},
_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 );
}
},
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,
stopPropagation: 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)
});
that._afterEndHandler = proxy(that._afterEnd, that);
that.captureEscape = function(e) {
if (e.keyCode === kendo.keys.ESC) {
that._trigger(DRAGCANCEL, {event: e});
that.userEvents.cancel();
}
};
},
events: [
HOLD,
DRAGSTART,
DRAG,
DRAGEND,
DRAGCANCEL
],
options: {
name: "Draggable",
distance: 5,
group: "default",
cursorOffset: null,
axis: null,
container: null,
holdToDrag: false,
dropped: false
},
cancelHold: function() {
this._activated = false;
},
_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);
},
_start: function(e) {
var that = this,
options = that.options,
container = options.container,
hint = options.hint;
if (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, // the Window's z-index is 10000 and can be raised because of z-stacking
left: offset.left,
top: offset.top
})
.appendTo(document.body);
}
draggables[options.group] = that;
that.dropped = false;
if (container) {
that.boundaries = containerBoundaries(container, that.hint);
}
if (that._trigger(DRAGSTART, e)) {
that.userEvents.cancel();
that._afterEnd();
}
$(document).on(KEYUP, that.captureEscape);
},
_hold: function(e) {
this.currentTarget = e.target;
if (this._trigger(HOLD, e)) {
this.userEvents.cancel();
} else {
this._activated = true;
}
},
_drag: function(e) {
var that = this;
e.preventDefault();
that._withDropTarget(e, 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 });
});
that._trigger(DRAG, e);
if (that.hint) {
that._updateHint(e);
}
},
_end: function(e) {
var that = this;
that._withDropTarget(e, function(target, targetElement) {
if (target) {
target._drop(extend({}, e, { dropTarget: $(targetElement) }));
lastDropTarget = null;
}
});
that._trigger(DRAGEND, e);
that._cancel(e.event);
},
_cancel: function() {
var that = this;
that._activated = false;
if (that.hint && !that.dropped) {
setTimeout(function() {
that.hint.stop(true, true).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,
dropTarget: e.dropTarget
}
));
},
_withDropTarget: function(e, callback) {
var that = this,
target, result,
options = that.options,
targets = dropTargets[options.group],
areas = dropAreas[options.group];
if (targets && targets.length || areas && areas.length) {
target = elementUnderCursor(e);
if (that.hint && contains(that.hint[0], target)) {
that.hint.hide();
target = elementUnderCursor(e);
// IE8 does not return the element in iframe from first attempt
if (!target) {
target = elementUnderCursor(e);
}
that.hint.show();
}
result = checkTarget(target, 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();
},
_afterEnd: function() {
var that = this;
if (that.hint) {
that.hint.remove();
}
delete draggables[that.options.group];
that.trigger("destroy");
$(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
});
})(window.kendo.jQuery);
kendo_module({
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: [ "core", "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";
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));
},
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._outOfBounds()) {
that._snapBack();
} else {
that._end();
}
},
done: function() {
return abs(this.velocity) < 1;
},
start: function(e) {
var that = this;
if (!that.dimension.enabled) { return; }
if (that._outOfBounds()) {
that._snapBack();
} else {
that.velocity = Math.max(Math.min(
e.touch[that.axis].velocity * that.velocityMultiplier,
MAX_VELOCITY), -MAX_VELOCITY);
if (that.velocity) {
that.tapCapture.captureNext();
Animation.fn.start.call(that);
} else {
that._end();
}
}
},
tick: function() {
var that = this,
dimension = that.dimension,
friction = that._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();
},
_outOfBounds: function() {
return this.dimension.outOfBounds(this.movable[this.axis]);
},
_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);
},
setCoordinates: function(from, to) {
this.offset = {};
this.origin = from;
this.destination = to;
},
_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 = $('
');
extend(that, options, {
element: element,
elementSize: 0,
movable: new Movable(element),
scrollMovable: options.movable,
size: horizontal ? "width" : "height"
});
that.scrollMovable.bind(CHANGE, proxy(that._move, that));
that.container.append(element);
},
_move: 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 (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() {
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('');
extend(that, {
scrollElement: element,
fixedContainer: element.children().first()
});
return;
}
element
.css("overflow", "hidden")
.addClass("km-scroll-wrapper")
.wrapInner('
')
.prepend('');
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, {
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,
verticalSwipe = velocityY * 2 >= velocityX;
if (!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
});
});
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");
dimensions.refresh();
if (that.options.pullToRefresh) {
that._initPullToRefresh();
}
},
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,
elastic: true,
useNative: false,
avoidScrolling: function() { return false; },
pullToRefresh: false,
pullTemplate: "Pull to refresh",
releaseTemplate: "Release to refresh",
refreshTemplate: "Refreshing"
},
events: [
PULL,
SCROLL,
RESIZE
],
_resize: function() {
if (!this._native) {
this.dimensions.refresh();
}
this.reset();
},
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);
}
},
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(x);
this.scrollElement.scrollTop(y);
} else {
this.dimensions.refresh();
this.movable.moveTo({x: x, y: y});
}
},
animatedScrollTo: function(x, y) {
var from = { x: this.movable.x, y: this.movable.y },
to = { x: x, y: y };
this.animatedScroller.setCoordinates(from, to);
this.animatedScroller.start();
},
pullHandled: function() {
var that = this;
that.refreshHint.removeClass(REFRESHCLASS);
that.hintContainer.html(that.pullTemplate({}));
that.yinertia.onEnd();
that.xinertia.onEnd();
},
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.pullTemplate);
that.releaseTemplate = kendo.template(that.options.releaseTemplate);
that.refreshTemplate = kendo.template(that.options.refreshTemplate);
that.scrollElement.prepend('' + that.pullTemplate({}) + ' ');
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,
scrollBar = new ScrollBar({
axis: axis,
movable: movable,
dimension: dimension,
container: that.element
});
that.pane[axis].bind(CHANGE, function() {
scrollBar.show();
});
that[axis + "inertia"] = new DragInertia({
axis: axis,
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);
kendo_module({
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",
indicatorTmpl = kendo.template('', { useWithBlock:false }),
hint = function(target) {
return $('')
.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")))
.prepend(' ');
},
dropCue = $('
'),
nameSpecialCharRegExp = /("|\%|'|\[|\]|\$|\.|\,|\:|\;|\+|\*|\&|\!|\#|\(|\)|<|>|\=|\?|\@|\^|\{|\}|\~|\/|\||`)/g;
function dropCueOffsetTop(element) {
return element.position().top + 3;
}
var Groupable = Widget.extend({
init: function(element, options) {
var that = this,
groupContainer,
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
});
groupContainer = 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(groupContainer)).css(horizontalCuePosition, 0).appendTo(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(groupContainer), left: left}).appendTo(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 = proxy(that.refresh, that);
that.dataSource.bind("change", that._refreshHandler);
}
},
refresh: function() {
var that = this,
dataSource = that.dataSource;
that.groupContainer.empty().append(
$.map(dataSource.group() || [], function(item) {
var fieldName = item.field.replace(nameSpecialCharRegExp, "\\$1");
var element = that.element.find(that.options.filter).filter("[" + kendo.attr("field") + "=" + 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)
.kendoDropTarget("destroy")
.kendoDraggable("destroy");
if (!that.options.draggable) {
that.draggable.destroy();
}
if (that.dataSource && that._refreshHandler) {
that.dataSource.unbind("change", that._refreshHandler);
}
},
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.is(":empty")) {
groupContainer.html(this.options.messages.empty);
}
}
});
kendo.ui.plugin(Groupable);
})(window.kendo.jQuery);
kendo_module({
id: "reorderable",
name: "Reorderable",
category: "framework",
depends: [ "core", "draganddrop" ],
advanced: true
});
(function ($, undefined) {
var kendo = window.kendo,
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,
filter: options.filter,
hint: options.hint
});
that.reorderDropCue = $('');
element.find(draggable.options.filter).kendoDropTarget({
group: draggable.options.group,
dragenter: function(e) {
if (!that._draggable) {
return;
}
var dropTarget = this.element, offset,
same = dropTarget[0] === that._draggable[0];
toggleHintClass(e.draggable.hint, same);
if (!same) {
offset = kendo.getOffset(dropTarget);
that.reorderDropCue.css({
height: dropTarget.outerHeight(),
top: offset.top,
left: offset.left + (dropTarget.index() > that._draggable.index() ? dropTarget.outerWidth() : 0)
})
.appendTo(document.body);
}
},
dragleave: function(e) {
toggleHintClass(e.draggable.hint, true);
that.reorderDropCue.remove();
},
drop: function() {
if (!that._draggable) {
return;
}
var draggableElement = that._draggable[0],
dropTarget = this.element[0],
container;
if (draggableElement !== dropTarget) {
container = element.find(draggable.options.filter);
that.trigger(CHANGE, {
element: that._draggable,
oldIndex: container.index(draggableElement),
newIndex: container.index(dropTarget)
});
}
}
});
draggable.bind([ "dragcancel", "dragend", "dragstart" ],
{
dragcancel: function() {
that.reorderDropCue.remove();
that._draggable = null;
},
dragend: function() {
that.reorderDropCue.remove();
that._draggable = null;
},
dragstart: function(e) {
that._draggable = e.currentTarget;
}
}
);
},
options: {
name: "Reorderable",
filter: "*"
},
events: [
CHANGE
],
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
if (that.draggable) {
that.draggable.destroy();
}
kendo.destroy(that.element);
}
});
kendo.ui.plugin(Reorderable);
})(window.kendo.jQuery);
kendo_module({
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: 0,
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() {
// Overrides base widget resize
},
_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,
handle = $(e.currentTarget),
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);
kendo_module({
id: "sortable",
name: "Sortable",
category: "framework",
depends: [ "data" ],
advanced: true
});
(function($, undefined) {
var kendo = window.kendo,
proxy = $.proxy,
DIR = "dir",
ASC = "asc",
SINGLE = "single",
FIELD = "field",
DESC = "desc",
NS = ".kendoSortable",
TLINK = ".k-link",
ARIASORT = "aria-sort",
Widget = kendo.ui.Widget;
var Sortable = 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(' ').find(TLINK);
}
that.link = link;
that.element.on("click" + NS, proxy(that._click, that));
},
options: {
name: "Sortable",
mode: SINGLE,
allowUnsort: true,
compare: null,
filter: ""
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
that.element.off(NS);
that.dataSource.unbind("change", that._refreshHandler);
},
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) {
$(' ').appendTo(that.link);
element.attr(ARIASORT, "ascending");
} else if (dir === DESC) {
$(' ').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,
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 });
}
that.dataSource.sort(sort);
}
});
kendo.ui.plugin(Sortable);
})(window.kendo.jQuery);
kendo_module({
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",
supportEventDelegation = false;
(function($) {
(function() {
$('
')
.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 = $("");
that._lastActive = null;
that.element.addClass(SELECTABLE);
multiple = that.options.multiple;
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
},
_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;
//in case of hierarchy or right-click
if (target.closest("." + SELECTABLE)[0] !== that.element[0] || whichCode && whichCode == 3 || buttonCode && buttonCode == 2) {
return;
}
selected = target.hasClass(SELECTED);
if (!multiple || !ctrlKey) {
that.clear();
}
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),
ctrlKey = e.event.ctrlKey || e.event.metaKey;
that._downTarget = target;
//in case of hierarchy
if (target.closest("." + SELECTABLE)[0] !== that.element[0]) {
that.userEvents.cancel();
that._downTarget = null;
return;
}
that._marquee
.appendTo(document.body)
.css({
left: e.x.client + 1,
top: e.y.client + 1,
width: 0,
height: 0
});
if (!ctrlKey) {
that.clear();
}
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)
},
items = that.element.find(that.options.filter);
that._marquee.css(position);
invalidateSelectables(items, that._downTarget[0], 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);
that.value(that.element.find(that.options.filter + "." + ACTIVE));
that._lastActive = that._downTarget;
},
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);
},
_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) {
var selector = "input,a,textarea,.k-multiselect-wrap,select",
msie = kendo.support.browser.msie;
if ($(e.event.target).is(selector)) {
this.userEvents.cancel();
this._downTarget = null;
} else if (!msie || (msie && !$(kendo._activeElement()).is(selector))) {
e.preventDefault();
}
},
clear: function() {
var items = this.element.find(this.options.filter + "." + SELECTED);
this._unselect(items);
},
selectRange: function(start, end) {
var that = this,
found = false,
idx,
length,
tmp,
toSelect,
items = that.element.find(that.options.filter),
selectElement = proxy(that._selectElement, that);
start = $(start)[0];
end = $(end)[0];
for (idx = 0, length = items.length; idx < length; idx ++) {
toSelect = items[idx];
if(found) {
selectElement(toSelect);
found = toSelect !== end;
} else if(toSelect === start) {
found = start !== end;
selectElement(toSelect);
} else if(toSelect === end) {
tmp = start;
start = end;
end = tmp;
found = true;
selectElement(toSelect);
} else {
$(toSelect).removeClass(SELECTED);
}
}
that._notify(CHANGE);
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
that.element.off(NS);
that.userEvents.destroy();
}
});
function collision(element, position) {
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);
}
function invalidateSelectables(items, target, position, ctrlKey) {
var idx,
length,
toSelect;
for (idx = 0, length = items.length; idx < length; idx ++) {
toSelect = items.eq(idx);
if (collision(toSelect, position)) {
if(toSelect.hasClass(SELECTED)) {
if(ctrlKey && target !== toSelect[0]) {
toSelect.removeClass(SELECTED).addClass(UNSELECTING);
}
} else if (!toSelect.hasClass(ACTIVE) && !toSelect.hasClass(UNSELECTING)) {
toSelect.addClass(ACTIVE);
}
} else {
if (toSelect.hasClass(ACTIVE)) {
toSelect.removeClass(ACTIVE);
} else if(ctrlKey && toSelect.hasClass(UNSELECTING)) {
toSelect.removeClass(UNSELECTING).addClass(SELECTED);
}
}
}
}
kendo.ui.plugin(Selectable);
})(window.kendo.jQuery);
kendo_module({
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);
},
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) {
this.trigger(CLICK, {event: e});
}
},
_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 && el.nodeValue.trim().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 = $(' ').prependTo(element);
}
span.addClass("k-i-" + icon);
} else if (spriteCssClass) {
span = element.children("span.k-sprite").first();
if (!span[0]) {
span = $(' ').prependTo(element);
}
span.addClass(spriteCssClass);
} else if (imageUrl) {
img = element.children("img.k-image").first();
if (!img[0]) {
img = $(' ').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);
}
});
kendo.ui.plugin(Button);
})(window.kendo.jQuery);
kendo_module({
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('');
function button(template, idx, text, numeric) {
return template( {
idx: idx,
text: text,
ns: kendo.ns,
numeric: numeric
});
}
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);
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 = $('').appendTo(that.element);
}
}
if (options.input) {
if (!that.element.find(".k-pager-input").length) {
that.element.append('');
}
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){
$('")
.appendTo(that.element)
.find("select")
.html($.map($.isArray(options.pageSizes) ? options.pageSizes : [5,10,20], function(page){
return "" + page + " ";
}).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('");
}
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('');
}
}
that.element
.on(CLICK + NS , "a", proxy(that._click, that))
.addClass("k-pager-wrap k-widget");
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);
},
events: [
CHANGE
],
options: {
name: "Pager",
selectTemplate: '#=text# ',
linkTemplate: '#=text# ',
buttonCount: 10,
autoBind: true,
numeric: true,
info: true,
input: false,
previousNext: true,
pageSizes: false,
refresh: false,
messages: {
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"
}
},
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,
html = "",
reminder,
page = that.page(),
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);
}
for (idx = start; idx <= end; idx++) {
html += button(idx == page ? that.selectTemplate : linkTemplate, idx, idx, true);
}
if (end < totalPages) {
html += button(linkTemplate, idx, "...", false);
}
if (html === "") {
html = that.selectTemplate({ text: 0 });
}
that.list.html(html);
}
if (options.info) {
if (total > 0) {
html = kendo.format(options.messages.display,
(page - 1) * pageSize + 1, // first item in the page
Math.min(page * pageSize, total), // last item in the page
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 +
' ' +
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) {
that.element
.find(".k-pager-sizes select")
.val(pageSize)
.filter("[" + kendo.attr("role") + "=dropdownlist]")
.kendoDropDownList("value", pageSize)
.kendoDropDownList("text", pageSize); // handles custom values
}
},
_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 pageSize = parseInt(e.currentTarget.value, 10);
if (!isNaN(pageSize)){
this.dataSource.pageSize(pageSize);
}
},
_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());
},
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);
kendo_module({
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,
activeElement = kendo._activeElement,
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",
WINDOW = $(window),
DOCUMENT_ELEMENT = $(document.documentElement),
RESIZE_SCROLL = "resize scroll",
cssPrefix = support.transitions.css,
TRANSFORM = cssPrefix + "transform",
extend = $.extend,
NS = ".kendoPopup",
styles = ["font-family",
"font-size",
"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(" ") : [];
if (that.collisions.length === 1) {
that.collisions.push(that.collisions[0]);
}
parentPopup = $(that.options.anchor).closest(".k-popup,.k-group").filter(":not([class^=km-])"); // When popup is in another popup, make it relative.
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 }); // Forcing refresh causes flickering in mobile.
that.trigger(ACTIVATE);
}
});
extend(options.animation.close, {
complete: function() {
that.wrapper.hide();
var location = that.wrapper.data(LOCATION),
anchor = $(options.anchor),
direction, dirClass;
if (location) {
that.wrapper.css(location);
}
if (options.anchor != BODY) {
direction = (anchor[0].className.match(ACTIVEBORDERREGEXP) || ["", "down"])[1];
dirClass = ACTIVEBORDER + "-" + direction;
anchor
.removeClass(dirClass)
.children(ACTIVECHILDREN)
.removeClass(ACTIVE)
.removeClass(dirClass);
element.removeClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse);
}
that._closing = false;
that.trigger(DEACTIVATE);
}
});
that._mousedownProxy = function(e) {
that._mousedown(e);
};
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,
collision: "flip fit",
viewport: window,
copyAnchorStyles: true,
autosize: false,
modal: false,
animation: {
open: {
effects: "slideIn:down",
transition: true,
duration: 200
},
close: { // if close animation effects are defined, they will be used instead of open.reverse
duration: 100,
hide: true
}
}
},
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(MOUSEDOWN, that._mousedownProxy);
WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy);
}
if (options.appendTo[0] === document.body) {
parent = element.parent(".k-animation-container");
if (parent[0]) {
parent.remove();
} else {
element.remove();
}
}
kendo.destroy(that.element.children());
},
open: function(x, y) {
var that = this,
fixed = { isFixed: !isNaN(parseInt(y,10)), x: x, y: y },
element = that.element,
options = that.options,
direction = "down",
animation, wrapper,
anchor = $(options.anchor);
if (!that.visible()) {
if (options.copyAnchorStyles) {
element.css(kendo.getComputedStyles(anchor[0], styles));
}
if (element.data("animating") || that.trigger(OPEN)) {
return;
}
if (!options.modal) {
DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy)
.bind(MOUSEDOWN, that._mousedownProxy);
// this binding hangs iOS in editor
if (!(support.mobileOS.ios || support.mobileOS.android)) {
WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy)
.bind(RESIZE_SCROLL, that._resizeProxy);
}
}
that.wrapper = wrapper = kendo.wrap(element, options.autosize)
.css({
overflow: HIDDEN,
display: "block",
position: ABSOLUTE
});
if (support.mobileOS.android) {
wrapper.add(anchor).css(TRANSFORM, "translatez(0)"); // Android is VERY slow otherwise. Should be tested in other droids as well since it may cause blur.
}
wrapper.css(POSITION);
if ($(options.appendTo)[0] == document.body) {
wrapper.css(TOP, "-10000px");
}
animation = extend(true, {}, options.animation.open);
that.flipped = that._position(fixed);
animation.effects = kendo.parseEffects(animation.effects, that.flipped);
direction = animation.effects.slideIn ? animation.effects.slideIn.direction : direction;
if (options.anchor != BODY) {
var dirClass = ACTIVEBORDER + "-" + direction;
element.addClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse);
anchor
.addClass(dirClass)
.children(ACTIVECHILDREN)
.addClass(ACTIVE)
.addClass(dirClass);
}
element.data(EFFECTS, animation.effects)
.kendoStop(true)
.kendoAnimate(animation);
}
},
toggle: function() {
var that = this;
that[that.visible() ? CLOSE : OPEN]();
},
visible: function() {
return this.element.is(":" + VISIBLE);
},
close: function() {
var that = this,
options = that.options, wrap,
animation, openEffects, closeEffects;
if (that.visible()) {
wrap = (that.wrapper[0] ? that.wrapper : kendo.wrap(that.element).hide());
if (that._closing || that.trigger(CLOSE)) {
return;
}
// Close all inclusive popups.
that.element.find(".k-popup").each(function () {
var that = $(this),
popup = that.data("kendoPopup");
if (popup) {
popup.close();
}
});
DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy);
WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy);
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 }); // stop callback will remove hidden overflow
that.element.kendoAnimate(animation);
}
},
_resize: function(e) {
var that = this;
if (e.type === "resize") {
clearTimeout(that._resizeTimeout);
that._resizeTimeout = setTimeout(function() {
that._position();
that._resizeTimeout = null;
}, 50);
} else {
if (!that._hovered && !contains(that.element[0], activeElement())) {
that.close();
}
}
},
_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;
}
// This MAY result in popup not closing in certain cases.
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;
},
_position: function(fixed) {
var that = this,
element = that.element.css(POSITION, ""),
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,
idx = 0, length;
siblingContainer = anchor.parents().filter(wrapper.siblings());
if (siblingContainer[0]) {
parentZIndex = Number($(siblingContainer).css("zIndex"));
if (parentZIndex) {
zIndex = parentZIndex + 1;
} else {
parents = anchor.parentsUntil(siblingContainer);
for (length = parents.length; idx < length; idx++) {
parentZIndex = Number($(parents[idx]).css("zIndex"));
if (parentZIndex && zIndex < parentZIndex) {
zIndex = parentZIndex + 1;
}
}
}
}
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 the parent is positioned, get the current positions
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)) { // Needed to reset the popup location after every closure - fixes the resize bugs.
wrapper.data(LOCATION, extend({}, pos));
}
var offsets = extend({}, offset),
location = extend({}, pos);
if (collisions[0] === "fit") {
location.top += that._fit(offsets.top, wrapper.outerHeight(), viewport.height() / zoomLevel);
}
if (collisions[1] === "fit") {
location.left += that._fit(offsets.left, wrapper.outerWidth(), viewport.width() / zoomLevel);
}
var flipPos = extend({}, location);
if (collisions[0] === "flip") {
location.top += that._flip(offsets.top, element.outerHeight(), anchor.outerHeight(), viewport.height() / zoomLevel, origins[0], positions[0], wrapper.outerHeight());
}
if (collisions[1] === "flip") {
location.left += that._flip(offsets.left, element.outerWidth(), anchor.outerWidth(), viewport.width() / 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);
kendo_module({
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 = '',
IFRAMETEMPLATE = kendo.template(
""),
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);
// perform AJAX request
that._ajaxRequest(contentOptions);
} else {
element.hide();
iframe = element.find("." + KCONTENTFRAME)[0];
if (iframe) {
// refresh existing 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 });
that.content.html(contentOptions || "");
} else {
that.content.html(contentOptions);
}
},
_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);// animation can be too long to hide the element before it is shown again
}
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();
}
this.element.off(NS);
DOCUMENT.off("keydown" + NS, this._documentKeyDownHandler);
Widget.fn.destroy.call(this);
}
});
kendo.ui.plugin(Tooltip);
})(window.kendo.jQuery);
kendo_module({
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,
ID = "id",
LI = "li",
CHANGE = "change",
CHARACTER = "character",
FOCUSED = "k-state-focused",
HOVER = "k-state-hover",
LOADING = "k-loading",
OPEN = "open",
CLOSE = "close",
SELECT = "select",
SELECTED = "selected",
PROGRESS = "progress",
REQUESTEND = "requestEnd",
WIDTH = "width",
extend = $.extend,
proxy = $.proxy,
browser = support.browser,
isIE8 = browser.msie && browser.version < 9,
quotRegExp = /"/g,
alternativeNames = {
"ComboBox": "DropDownList",
"DropDownList": "ComboBox"
};
var List = Widget.extend({
init: function(element, options) {
var that = this,
ns = that.ns,
id;
Widget.fn.init.call(that, element, options);
element = that.element;
that._isSelect = element.is(SELECT);
that._template();
that.ul = $('')
.css({ overflow: support.kineticScrollNeeded ? "": "auto" })
.on("mouseenter" + ns, LI, function() { $(this).addClass(HOVER); })
.on("mouseleave" + ns, LI, function() { $(this).removeClass(HOVER); })
.on("click" + ns, LI, proxy(that._click, that))
.attr({
tabIndex: -1,
role: "listbox",
"aria-hidden": true
});
that.list = $("
")
.append(that.ul)
.on("mousedown" + ns, function(e) {
e.preventDefault();
});
id = element.attr(ID);
if (id) {
that.list.attr(ID, id + "-list");
that.ul.attr(ID, id + "_listbox");
that._optionID = id + "_option_selected";
}
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)
});
},
_filterSource: function(filter) {
var that = this,
options = that.options,
dataSource = that.dataSource,
expression = dataSource.filter() || {};
removeFiltersForField(expression, options.dataTextField);
if (filter) {
expression = expression.filters || [];
expression.push(filter);
}
dataSource.filter(expression);
},
_header: function() {
var template = this.options.headerTemplate;
var header;
if ($.isFunction(template)) {
template = template();
}
if (template) {
this.list.prepend(template);
header = this.ul.prev();
this.header = header[0] ? header : null;
}
},
_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;
}
}
},
items: function() {
return this.ul[0].children;
},
current: function(candidate) {
var that = this,
id = that._optionID;
if (candidate !== undefined) {
if (that._current) {
that._current
.removeClass(FOCUSED)
.removeAttr("aria-selected")
.removeAttr(ID);
that._focused
.removeAttr("aria-activedescendant");
}
if (candidate) {
candidate.addClass(FOCUSED);
that._scroll(candidate);
if (id) {
candidate.attr("id", id);
that._focused.attr("aria-activedescendant", id);
}
}
that._current = candidate;
} else {
return that._current;
}
},
destroy: function() {
var that = this,
ns = that.ns;
Widget.fn.destroy.call(that);
that._unbindDataSource();
that.ul.off(ns);
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) {
index = that.selectedIndex;
}
return that._data()[index];
},
_accessors: function() {
var that = this,
element = that.element,
options = that.options,
getter = kendo.getter,
textField = element.attr(kendo.attr("text-field")),
valueField = element.attr(kendo.attr("value-field"));
if (textField) {
options.dataTextField = textField;
}
if (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;
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", !options.filter || options.filter === "none" ? "off" : "polite");
},
_blur: function() {
var that = this;
that._change();
that.close();
},
_change: function() {
var that = this,
index = that.selectedIndex,
optionValue = that.options.value,
value = that.value(),
trigger;
if (that._isSelect && !that._bound && optionValue) {
value = optionValue;
}
if (value !== that._old) {
trigger = true;
} else if (index !== undefined && index !== that._oldIndex) {
trigger = true;
}
if (trigger) {
that._old = value;
that._oldIndex = index;
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_click: function(e) {
if (!e.isDefaultPrevented()) {
this._accept($(e.currentTarget));
}
},
_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]"));
}
},
_focus: function(li) {
var that = this;
if (that.popup.visible() && li && that.trigger(SELECT, {item: li})) {
that.close();
return;
}
that._select(li);
that._triggerCascade();
that._blur();
},
_index: function(value) {
var that = this,
idx,
length,
data = that._data();
for (idx = 0, length = data.length; idx < length; idx++) {
if (that._dataValue(data[idx]) == value) {
return idx;
}
}
return -1;
},
_dataValue: function(dataItem) {
var value = this._value(dataItem);
if (value === undefined) {
value = this._text(dataItem);
}
return value;
},
_height: function(length) {
if (length) {
var that = this,
list = that.list,
visible = that.popup.visible(),
height = that.options.height;
list = list.add(list.parent(".k-animation-container")).show()
.height(that.ul[0].scrollHeight > height ? height : "auto");
if (!visible) {
list.hide();
}
}
},
_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 = computedStyle ? parseFloat(computedStyle.width) : wrapper.outerWidth();
if (computedStyle && (browser.mozilla || browser.msie)) { // getComputedStyle returns different box in FF and IE.
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;
},
_popup: function() {
var that = this,
list = that.list,
focused = that._focused,
options = that.options,
wrapper = that.wrapper;
that.popup = new ui.Popup(list, extend({}, options.popup, {
anchor: wrapper,
open: function(e) {
that._adjustListWidth();
if (that.trigger(OPEN)) {
e.preventDefault();
} else {
focused.attr("aria-expanded", true);
that.ul.attr("aria-hidden", false);
}
},
close: function(e) {
if (that.trigger(CLOSE)) {
e.preventDefault();
} else {
focused.attr("aria-expanded", false);
that.ul.attr("aria-hidden", true);
}
},
animation: options.animation,
isRtl: support.isRtl(wrapper)
}));
that.popup.one(OPEN, function() {
that._height(that._data().length);
});
that._touchScroller = kendo.touchScroller(that.popup.element);
},
_makeUnselectable: function() {
if (isIE8) {
this.list.find("*").attr("unselectable", "on");
}
},
_toggleHover: function(e) {
$(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter");
},
_toggle: function(open) {
var that = this;
open = open !== undefined? open : !that.popup.visible();
if (!support.touch && that._focused[0] !== activeElement()) {
that._focused.focus();
}
that[open ? OPEN : CLOSE]();
},
_scroll: function (item) {
if (!item) {
return;
}
if (item[0]) {
item = item[0];
}
var ul = this.ul[0],
itemOffsetTop = item.offsetTop,
itemOffsetHeight = item.offsetHeight,
ulScrollTop = ul.scrollTop,
ulOffsetHeight = ul.clientHeight,
bottomDistance = itemOffsetTop + itemOffsetHeight,
touchScroller = this._touchScroller,
yDimension, headerHeight;
if (touchScroller) {
yDimension = touchScroller.dimensions.y;
if (yDimension.enabled && itemOffsetTop > yDimension.size) {
itemOffsetTop = itemOffsetTop - yDimension.size + itemOffsetHeight + 4;
touchScroller.scrollTo(0, -itemOffsetTop);
}
} else {
headerHeight = this.header ? this.header.outerHeight() : 0;
ul.scrollTop = ulScrollTop > itemOffsetTop ?
(itemOffsetTop - headerHeight) : bottomDistance > (ulScrollTop + ulOffsetHeight) ?
(bottomDistance - ulOffsetHeight - headerHeight) : ulScrollTop;
}
},
_template: function() {
var that = this,
options = that.options,
template = options.template,
hasDataSource = options.dataSource;
if (that._isSelect && that.element[0].length) {
if (!hasDataSource) {
options.dataTextField = options.dataTextField || "text";
options.dataValueField = options.dataValueField || "value";
}
}
if (!template) {
that.template = kendo.template('${' + kendo.expr(options.dataTextField, "data") + "} ", { useWithBlock: false });
} else {
template = kendo.template(template);
that.template = function(data) {
return '' + template(data) + " ";
};
}
},
_triggerCascade: function() {
var that = this,
value = that.value();
if ((!that._bound && value) || that._old !== value) {
that.trigger("cascade");
}
},
_unbindDataSource: function() {
var that = this;
that.dataSource.unbind(CHANGE, that._refreshHandler)
.unbind(PROGRESS, that._progressHandler)
.unbind(REQUESTEND, that._requestEndHandler)
.unbind("error", that._errorHandler);
}
});
extend(List, {
caret: function(element) {
var caret,
selection = element.ownerDocument.selection;
if (selection) {
caret = Math.abs(selection.createRange().moveStart(CHARACTER, -element.value.length));
} else {
caret = element.selectionStart;
}
return caret;
},
selectText: function (element, selectionStart, selectionEnd) {
try {
if (element.createTextRange) {
element.focus();
var textRange = element.createTextRange();
textRange.collapse(true);
textRange.moveStart(CHARACTER, selectionStart);
textRange.moveEnd(CHARACTER, selectionEnd - selectionStart);
textRange.select();
} else {
element.setSelectionRange(selectionStart, selectionEnd);
}
} catch(e) { /* element is not focused or it is not in the DOM */ }
},
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;
}
});
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) {
this.options.dataSource = dataSource;
this._dataSource();
this._bound = false;
if (this.options.autoBind) {
this.dataSource.fetch();
}
},
close: function() {
this.popup.close();
},
select: function(li) {
var that = this;
if (li === undefined) {
return that.selectedIndex;
} else {
that._select(li);
that._triggerCascade();
that._old = that._accessor();
that._oldIndex = that.selectedIndex;
}
},
_accessor: function(value, idx) {
var element = this.element[0],
isSelect = this._isSelect,
selectedIndex = element.selectedIndex,
option;
if (value === undefined) {
if (isSelect) {
if (selectedIndex > -1) {
option = element.options[selectedIndex];
if (option) {
value = option.value;
}
}
} else {
value = element.value;
}
return value;
} else {
if (isSelect) {
if (selectedIndex > -1) {
element.options[selectedIndex].removeAttribute(SELECTED);
}
element.selectedIndex = idx;
option = element.options[idx];
if (option) {
option.setAttribute(SELECTED, SELECTED);
}
} else {
element.value = value;
}
}
},
_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 () {
that._focused.attr("aria-busy", true);
that._arrow.addClass(LOADING);
}, 100);
},
_requestEnd: function() {
this._request = false;
},
_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._refreshHandler) {
that._unbindDataSource();
} else {
that._refreshHandler = proxy(that.refresh, that);
that._progressHandler = proxy(that._showBusy, that);
that._requestEndHandler = proxy(that._requestEnd, that);
that._errorHandler = proxy(that._hideBusy, that);
}
that.dataSource = kendo.data.DataSource.create(dataSource)
.bind(CHANGE, that._refreshHandler)
.bind(PROGRESS, that._progressHandler)
.bind(REQUESTEND, that._requestEndHandler)
.bind("error", that._errorHandler);
},
_get: function(li) {
var that = this,
data = that._data(),
idx, length;
if (typeof li === "function") {
for (idx = 0, length = data.length; idx < length; idx++) {
if (li(data[idx])) {
li = idx;
break;
}
}
}
if (typeof li === "number") {
if (li < 0) {
return $();
}
li = $(that.ul[0].children[li]);
}
if (li && li.nodeType) {
li = $(li);
}
return li;
},
_move: function(e) {
var that = this,
key = e.keyCode,
ul = that.ul[0],
methodName = that.popup.visible() ? "_select" : "_accept",
current = that._current,
down = key === keys.DOWN,
firstChild,
pressed;
if (key === keys.UP || down) {
if (e.altKey) {
that.toggle(down);
} else {
firstChild = ul.firstChild;
if (!firstChild && !that._accessor() && that._state !== "filter") {
if (!that._fetch) {
that.dataSource.one(CHANGE, function() {
that._move(e);
that._fetch = false;
});
that._fetch = true;
that._filterSource();
}
e.preventDefault();
return true; //pressed
}
if (down) {
if (!current || (that.selectedIndex === -1 && !that.value() && current[0] === firstChild)) {
current = firstChild;
} else {
current = current[0].nextSibling;
if (!current && firstChild === ul.lastChild) {
current = firstChild;
}
}
that[methodName](current);
} else {
current = current ? current[0].previousSibling : ul.lastChild;
if (!current && firstChild === ul.lastChild) {
current = firstChild;
}
that[methodName](current);
}
}
e.preventDefault();
pressed = true;
} else if (key === keys.ENTER || key === keys.TAB) {
if (that.popup.visible()) {
e.preventDefault();
}
that._accept(current);
pressed = true;
} else if (key === keys.ESC) {
if (that.popup.visible()) {
e.preventDefault();
}
that.close();
pressed = true;
}
return pressed;
},
_selectItem: function() {
var that = this,
options = that.options,
index = that.selectedIndex,
useOptionIndex,
value;
useOptionIndex = that._isSelect && !that._initial && !options.value && options.index && !that._bound;
if (!useOptionIndex) {
value = that._selectedValue || options.value || that._accessor();
}
if (value) {
that.value(value);
} else if (!that._bound || index > -1) {
if (!that._bound) {
index = options.index;
}
that.select(index);
}
},
_fetchItems: function(value) {
var that = this,
hasItems = that.ul[0].firstChild;
//if request is started avoid datasource.fetch
if (that._request) {
return true;
}
if (!that._fetch && !hasItems) {
if (that.options.cascadeFrom) {
return !hasItems;
}
that.dataSource.one(CHANGE, function() {
that.value(value);
that._fetch = false;
});
that._fetch = true;
that.dataSource.fetch();
return true;
}
},
_options: function(data, optionLabel) {
var that = this,
element = that.element,
selectedIndex = element[0].selectedIndex,
length = data.length,
options = "",
option,
dataItem,
dataText,
dataValue,
idx = 0;
if (optionLabel) {
idx = 1;
options = optionLabel;
if (optionLabel.indexOf($(element[0].firstChild).text()) === -1) {
selectedIndex += 1;
}
}
for (; idx < length; idx++) {
option = "";
if (dataText !== undefined) {
option += htmlEncode(dataText);
}
option += " ";
options += option;
}
element.html(options);
element[0].selectedIndex = selectedIndex === -1 ? 0 : selectedIndex;
},
_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);
}
},
_cascade: function() {
var that = this,
options = that.options,
cascade = options.cascadeFrom,
parent, parentElement,
select, valueField,
change;
if (cascade) {
that._selectedValue = options.value || that._accessor();
parentElement = $("#" + cascade);
parent = parentElement.data("kendo" + options.name);
if (!parent) {
parent = parentElement.data("kendo" + alternativeNames[options.name]);
}
if (!parent) {
return;
}
options.autoBind = false;
valueField = options.cascadeFromField || parent.options.dataValueField;
change = function() {
var value = that._selectedValue || that.value();
if (value) {
that.value(value);
if (!that.dataSource.view()[0] || that.selectedIndex === -1) {
that._clearSelection(parent, true);
}
} else {
that.select(options.index);
}
that.enable();
that._triggerCascade();
};
select = function() {
var dataItem = parent.dataItem(),
filterValue = dataItem ? parent._value(dataItem) : null,
expressions, filters;
if (filterValue || filterValue === 0) {
expressions = that.dataSource.filter() || {};
removeFiltersForField(expressions, valueField);
filters = expressions.filters || [];
filters.push({
field: valueField,
operator: "eq",
value: filterValue
});
that.dataSource
.one(CHANGE, change)
.filter(filters);
} else {
that.enable(false);
that._clearSelection(parent);
that._triggerCascade();
}
};
parent.bind("cascade", function() { select(); });
//refresh was called
if (parent._bound) {
select();
} else if (!parent.value()) {
that.enable(false);
}
}
}
});
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;
}
});
}
}
})(window.kendo.jQuery);
kendo_module({
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('#=data.value# ', { useWithBlock: false }),
emptyCellTemplate = template(' ', { 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._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;
if (link.href.indexOf("#") != -1) {
e.preventDefault();
}
that._click($(link));
})
.on("mouseup" + ns, 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;
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) {
normalize(options);
Widget.fn.setOptions.call(this, options);
},
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 (+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
}, that[currentView.name])));
makeUnselectable(to);
that._animate({
from: from,
to: to,
vertical: vertical,
future: future
});
that._focus(value);
that.trigger(NAVIGATE);
}
if (view === views[options.depth] && selectedValue) {
that._class("k-state-selected", currentView.toDateString(selectedValue));
}
that._class(FOCUSED, currentView.toDateString(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;
}
value = parse(value, options.format, options.culture);
if (value !== null) {
value = new DATE(+value);
if (!isInRange(value, min, max)) {
value = null;
}
}
that._value = value;
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,
currentValue = new DATE(+that._current),
isRtl = kendo.support.isRtl(that.wrapper),
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);
}
that._focus(restrictValue(currentValue, options.min, options.max));
}
}
if (prevent) {
e.preventDefault();
}
return that._current;
},
_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.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) {
to.insertAfter(from);
from.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("
");
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.remove();
to.unwrap();
that._focusView(active);
that._oldTable = undefined;
}
});
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, //active state before from's blur
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.remove();
to.css({
position: "static",
top: 0,
left: 0
});
that._focusView(active);
that._oldTable = undefined;
}
});
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, value) {
var that = this,
id = that._cellID,
cell = that._cell;
if (cell) {
cell.removeAttr(ARIA_SELECTED)
.removeAttr(ID);
}
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) {
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 = link.attr(kendo.attr(VALUE)).split("/");
//Safari cannot create correctly date from "1/1/2090"
value = new DATE(value[0], value[1], value[2]);
adjustDST(value, 0);
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, view.toDateString(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 = $('').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,
active = that.options.focusOnNav !== false,
links;
if (!element.find(".k-header")[0]) {
element.html('');
}
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._focusView(active); that.navigateUp(); });
that[PREVARROW] = links.eq(0).on(CLICK, function() { that._focusView(active); that.navigateToPast(); });
that[NEXTARROW] = links.eq(2).on(CLICK, function() { that._focusView(active); 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)) {
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,
link = that._today;
if (toggle === undefined) {
toggle = isInRange(getToday(), options.min, options.max);
}
if (link) {
link.off(CLICK);
if (toggle) {
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],
today = getToday();
e.preventDefault();
if (that._view.compare(that._current, today) === 0 && that._index == depth) {
that._changeView = false;
}
that._value = today;
that.navigate(today, depth);
that.trigger(CHANGE);
},
_templates: function() {
var that = this,
options = that.options,
footer = options.footer,
month = options.month,
content = month.content,
empty = month.empty;
that.month = {
content: template('' + (content || "#=data.value#") + ' ', { useWithBlock: !!content }),
empty: template('' + (empty || " ") + " ", { useWithBlock: !!empty })
};
if (footer !== false) {
that.footer = template(footer || '#= kendo.toString(data,"D","' + options.culture +'") #', { useWithBlock: false });
}
}
});
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 = '';
for (; idx < 7; idx++) {
html += '' + shortNames[idx] + ' ';
}
today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());
adjustDST(today, 0);
today = +today;
return view({
cells: 42,
perRow: 7,
html: html += ' ',
start: new DATE(start.getFullYear(), start.getMonth(), start.getDate()),
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,
build: function(date) {
var cssClass = [],
day = date.getDay(),
linkClass = "",
url = "#";
if (date < firstDayOfMonth || date > lastDayOfMonth) {
cssClass.push(OTHERMONTH);
}
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 || '';
for(; idx < length; idx++) {
if (idx > 0 && idx % cellsPerRow === 0) {
html += ' ';
}
data = build(start, idx);
html += isInRange(start, min, max) ? content(data) : empty(data);
setter(start, 1);
}
return html + "
";
}
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) {
$(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;
}
calendar.isEqualDatePart = isEqualDatePart;
calendar.makeUnselectable = makeUnselectable;
calendar.restrictValue = restrictValue;
calendar.isInRange = isInRange;
calendar.normalize = normalize;
calendar.viewsEnum = views;
kendo.calendar = calendar;
})(window.kendo.jQuery);
kendo_module({
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 = "
",
SPAN = " ",
ns = ".kendoDatePicker",
CLICK = "click" + ns,
OPEN = "open",
CLOSE = "close",
CHANGE = "change",
DATEVIEW = "dateView",
DISABLED = "disabled",
READONLY = "readonly",
DEFAULT = "k-state-default",
FOCUSED = "k-state-focused",
SELECTED = "k-state-selected",
STATEDISABLED = "k-state-disabled",
HOVER = "k-state-hover",
KEYDOWN = "keydown" + ns,
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 ($.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,
sharedCalendar = DatePicker.sharedCalendar,
div = $(DIV).attr(ARIA_HIDDEN, "true")
.addClass("k-calendar-container")
.appendTo(body);
if (!sharedCalendar) {
sharedCalendar = DatePicker.sharedCalendar = new ui.Calendar($(DIV).attr(ID, kendo.guid()).hide().appendTo(body), { focusOnNav: false });
calendar.makeUnselectable(sharedCalendar.element);
}
that.calendar = sharedCalendar;
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._templates();
that.value(options.value);
};
DateView.prototype = {
_calendar: function() {
var that = this,
popup = that.popup,
options = that.options,
calendar = that.calendar,
element = calendar.element;
if (element.data(DATEVIEW) !== that) {
element.appendTo(popup.element)
.data(DATEVIEW, that)
.off(CLICK + " " + KEYDOWN)
.on(CLICK, "td:has(.k-link)", proxy(that._click, that))
.on(MOUSEDOWN, preventDefault)
.show();
calendar.unbind(CHANGE)
.bind(CHANGE, options);
calendar.month = that.month;
calendar.options.dates = options.dates;
calendar.options.depth = options.depth;
calendar.options.culture = options.culture;
calendar._footer(that.footer);
calendar.min(options.min);
calendar.max(options.max);
calendar._value = null;
calendar.navigate(that._value || that._current, options.start);
that.value(that._value);
}
},
destroy: function() {
var that = this,
calendar = that.calendar,
element = calendar.element,
dv = element.data(DATEVIEW),
popups;
if (dv === undefined || dv === that) {
popups = $(".k-calendar-container");
if (popups.length > 1) {
element.hide().appendTo(document.body);
} else {
element.off(ns);
calendar.destroy();
calendar.element.remove();
DatePicker.sharedCalendar = null;
}
}
that.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;
if (key == keys.ESC) {
that.close();
return;
}
if (e.altKey) {
if (key == keys.DOWN) {
that.open();
e.preventDefault();
} else if (key == keys.UP) {
that.close();
e.preventDefault();
}
return;
}
if (!that.popup.visible()){
return;
}
if (selectIsClicked && calendar._cell.hasClass(SELECTED)) {
that.close();
e.preventDefault();
return;
}
that._current = calendar._move(e);
},
value: function(value) {
var that = this,
calendar = that.calendar,
options = that.options;
that._value = value;
that._current = new DATE(+restrictValue(value, options.min, options.max));
if (calendar.element.data(DATEVIEW) === that) {
calendar.value(value);
}
},
_click: function(e) {
if (e.currentTarget.className.indexOf(SELECTED) !== -1) {
this.close();
}
},
_option: function(option, value) {
var that = this,
options = that.options,
calendar = that.calendar;
options[option] = value;
if (calendar.element.data(DATEVIEW) === that) {
calendar[option](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('' + (content || "#=data.value#") + ' ', { useWithBlock: !!content }),
empty: template("" + (empty || " ") + " ", { useWithBlock: !!empty })
};
if (footer !== false) {
that.footer = template(footer || '#= kendo.toString(data,"D","' + options.culture +'") #', { useWithBlock: false });
}
}
};
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;
normalize(options);
that._wrapper();
that.dateView = new DateView(extend({}, options, {
id: element.attr(ID),
anchor: that.wrapper,
change: function() {
// calendar is the current scope
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);
if (!date) {
that.dateView.value(date);
} else {
that.dateView._current = date;
that.dateView.calendar._focus(date);
}
}
element.attr(ARIA_EXPANDED, true);
div.attr(ARIA_HIDDEN, false);
}
}
}));
div = that.dateView.div;
that._icon();
try {
element[0].setAttribute("type", "text");
} catch(e) {
element[0].type = "text";
}
element
.addClass("k-input")
.attr({
role: "textbox",
"aria-haspopup": true,
"aria-expanded": false,
"aria-owns": that.dateView._dateViewID
});
that._reset();
that._template();
disabled = element.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,
dateView = that.dateView,
dateViewOptions = dateView.options;
Widget.fn.setOptions.call(that, options);
normalize(that.options);
dateView.options = extend(dateViewOptions, that.options, {
change: dateViewOptions.change,
close: dateViewOptions.close,
open: dateViewOptions.open
});
},
_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("blur" + 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;
value = that._update(value);
if (+that._old != +value) {
that._old = value;
that._oldText = that.element.val();
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_keydown: function(e) {
var that = this,
dateView = that.dateView,
value = that.element.val();
if (!dateView.popup.visible() && e.keyCode == keys.ENTER && value !== that._oldText) {
that._change(value);
} else {
dateView.move(e);
that._updateARIA(dateView._current);
}
},
_icon: function() {
var that = this,
element = that.element,
icon;
icon = element.next("span.k-select");
if (!icon[0]) {
icon = $('select ').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,
date = parse(value, options.parseFormats, options.culture),
formattedValue;
if (+date === +that._value) {
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._form = form.on("reset", that._resetHandler);
}
},
_template: function() {
this._ariaTemplate = template(this.options.ARIATemplate);
},
_updateARIA: function(date) {
this.element.attr("aria-label", this._ariaTemplate({ current: date }));
}
});
ui.plugin(DatePicker);
})(window.kendo.jQuery);
kendo_module({
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" ]
} ]
});
(function ($, undefined) {
var kendo = window.kendo,
support = kendo.support,
activeElement = kendo._activeElement,
placeholderSupported = support.placeholder,
ui = kendo.ui,
keys = kendo.keys,
DataSource = kendo.data.DataSource,
List = ui.List,
ARIA_DISABLED = "aria-disabled",
ARIA_READONLY = "aria-readonly",
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,
caretPosition = List.caret,
selectText = List.selectText,
proxy = $.proxy;
function indexOfWordAtCaret(caret, text, separator) {
return separator ? text.substring(0, caret).split(separator).length - 1 : 0;
}
function wordAtCaret(caret, text, separator) {
return text.split(separator)[indexOfWordAtCaret(caret, text, separator)];
}
function replaceWordAtCaret(caret, text, word, separator) {
var words = text.split(separator);
words.splice(indexOfWordAtCaret(caret, text, separator), 1, word);
if (separator && words[words.length - 1] !== "") {
words.push("");
}
return words.join(separator);
}
function moveCaretAtEnd(element) {
var length = element.value.length;
selectText(element, length, length);
}
var AutoComplete = List.extend({
init: function (element, options) {
var that = this, wrapper;
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("paste" + ns, proxy(that._search, that))
.on("focus" + ns, function () {
that._prev = that._accessor();
that._placeholder(false);
wrapper.addClass(FOCUSED);
})
.on("blur" + 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();
kendo.notify(that);
},
options: {
name: "AutoComplete",
enabled: true,
suggest: false,
template: "",
dataTextField: "",
minLength: 1,
delay: 200,
height: 200,
filter: "startswith",
ignoreCase: true,
highlightFirst: false,
separator: null,
placeholder: "",
animation: {},
value: null
},
_dataSource: function() {
var that = this;
if (that.dataSource && that._refreshHandler) {
that._unbindDataSource();
} else {
that._refreshHandler = proxy(that.refresh, that);
that._progressHandler = proxy(that._showBusy, that);
}
that.dataSource = DataSource.create(that.options.dataSource)
.bind("change", that._refreshHandler)
.bind("progress", that._progressHandler);
},
setDataSource: function(dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
},
events: [
"open",
"close",
"change",
"select",
"dataBinding",
"dataBound"
],
setOptions: function(options) {
List.fn.setOptions.call(this, options);
this._template();
this._accessors();
this._aria();
},
_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,
current = that._current;
if (current) {
current.removeClass(SELECTED);
}
that.current(null);
that.popup.close();
},
destroy: function() {
var that = this;
that.element.off(ns);
that.wrapper.off(ns);
List.fn.destroy.call(that);
},
refresh: function () {
var that = this,
ul = that.ul[0],
popup = that.popup,
options = that.options,
data = that._data(),
length = data.length,
action;
that.trigger("dataBinding");
ul.innerHTML = kendo.render(that.template, data);
that._height(length);
if (popup.visible()) {
popup._position();
}
if (length) {
if (options.highlightFirst) {
that.current($(ul.firstChild));
}
if (options.suggest) {
that.suggest($(ul.firstChild));
}
}
if (that._open) {
that._open = false;
action = length ? "open" : "close";
if (that._typing && that.element[0] !== activeElement()) {
action = "close";
}
popup[action]();
that._typing = undefined;
}
if (that._touchScroller) {
that._touchScroller.reset();
}
that._makeUnselectable();
that._hideBusy();
that.trigger("dataBound");
},
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();
that._current = null;
clearTimeout(that._typing);
if (separator) {
word = wordAtCaret(caretPosition(that.element[0]), word, separator);
}
length = word.length;
if (!length) {
that.popup.close();
} else if (length >= that.options.minLength) {
that._open = true;
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],
caret = caretPosition(element),
separator = that.options.separator,
words = value.split(separator),
wordIndex = indexOfWordAtCaret(caret, value, separator),
selectionEnd = caret,
idx;
if (key == keys.BACKSPACE || key == keys.DELETE) {
that._last = undefined;
return;
}
word = word || "";
if (typeof word !== "string") {
idx = List.inArray(word[0], that.ul[0]);
if (idx > -1) {
word = that._text(that._data()[idx]);
} else {
word = "";
}
}
if (caret <= 0) {
caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1;
}
idx = value.substring(0, caret).lastIndexOf(separator);
idx = idx > -1 ? caret - (idx + separator.length) : caret;
value = words[wordIndex].substring(0, idx);
if (word) {
idx = word.toLowerCase().indexOf(value.toLowerCase());
if (idx > -1) {
word = word.substring(idx + value.length);
selectionEnd = caret + word.length;
value += word;
}
if (separator && words[words.length - 1] !== "") {
words.push("");
}
}
words[wordIndex] = value;
that._accessor(words.join(separator || ""));
if (element === activeElement()) {
selectText(element, caret, selectionEnd);
}
},
value: function (value) {
if (value !== undefined) {
this._accessor(value);
this._old = value;
} else {
return this._accessor();
}
},
_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;
}
},
_accept: function (li) {
var that = this;
that._focus(li);
moveCaretAtEnd(that.element[0]);
},
_keydown: function (e) {
var that = this,
ul = that.ul[0],
key = e.keyCode,
current = that._current,
visible = that.popup.visible();
that._last = key;
if (key === keys.DOWN) {
if (visible) {
that._move(current ? current.next() : $(ul.firstChild));
}
e.preventDefault();
} else if (key === keys.UP) {
if (visible) {
that._move(current ? current.prev() : $(ul.lastChild));
}
e.preventDefault();
} else if (key === keys.ENTER || key === keys.TAB) {
if (key === keys.ENTER && that.popup.visible()) {
e.preventDefault();
}
that._accept(current);
} else if (key === keys.ESC) {
if (that.popup.visible()) {
e.preventDefault();
}
that.close();
} else {
that._search();
}
},
_move: function (li) {
var that = this;
li = li[0] ? li : null;
that.current(li);
if (that.options.suggest) {
that.suggest(li);
}
},
_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) {
List.selectText(element[0], 0, 0);
}
}
},
_search: function () {
var that = this;
clearTimeout(that._typing);
that._typing = setTimeout(function () {
if (that._prev !== that._accessor()) {
that._prev = that._accessor();
that.search();
}
}, that.options.delay);
},
_select: function (li) {
var that = this,
separator = that.options.separator,
data = that._data(),
text,
idx;
li = $(li);
if (li[0] && !li.hasClass(SELECTED)) {
idx = List.inArray(li[0], that.ul[0]);
if (idx > -1) {
data = data[idx];
text = that._text(data);
if (separator) {
text = replaceWordAtCaret(caretPosition(that.element[0]), that._accessor(), text, separator);
}
that._accessor(text);
that.current(li.addClass(SELECTED));
}
}
},
_loader: function() {
this._loading = $(' ').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(" ").parent();
}
//aria
wrapper.attr("tabindex", -1);
wrapper.attr("role", "presentation");
//end
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);
kendo_module({
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" ]
} ]
});
(function($, undefined) {
var kendo = window.kendo,
ui = kendo.ui,
Select = ui.Select,
os = kendo.support.mobileOS,
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",
SELECTED = "k-state-selected",
HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns,
TABINDEX = "tabindex",
proxy = $.proxy;
var DropDownList = Select.extend( {
init: function(element, options) {
var that = this,
index = options && options.index,
optionLabel, useOptionLabel, text;
that.ns = ns;
options = $.isArray(options) ? { dataSource: options } : options;
Select.fn.init.call(that, element, options);
that._focusHandler = function() {
that.wrapper.focus();
};
options = that.options;
element = that.element.on("focus" + ns, that._focusHandler);
this._inputTemplate();
that._reset();
that._word = "";
that._wrapper();
that._tabindex();
that.wrapper.data(TABINDEX, that.wrapper.attr(TABINDEX));
that._aria();
that._span();
that._popup();
that._mobile();
that._dataSource();
that._ignoreCase();
that._enable();
that._oldIndex = that.selectedIndex = -1;
that._cascade();
if (index !== undefined) {
options.index = index;
}
if (options.autoBind) {
that.dataSource.fetch();
} else if (that.selectedIndex === -1) {
text = options.text || "";
if (!text) {
optionLabel = options.optionLabel,
useOptionLabel = optionLabel && options.index === 0;
if (that._isSelect) {
if (useOptionLabel) {
text = optionLabel;
} else {
text = element.children(":selected").text();
}
} else if (!element[0].value && useOptionLabel) {
text = optionLabel;
}
}
that._textAccessor(text);
}
kendo.notify(that);
},
options: {
name: "DropDownList",
enabled: true,
autoBind: true,
index: 0,
text: null,
value: null,
template: "",
valueTemplate: "",
delay: 500,
height: 200,
dataTextField: "",
dataValueField: "",
optionLabel: "",
cascadeFrom: "",
cascadeFromField: "",
ignoreCase: true,
animation: {}
},
events: [
"open",
"close",
CHANGE,
"select",
"dataBinding",
"dataBound",
"cascade"
],
setOptions: function(options) {
Select.fn.setOptions.call(this, options);
this._template();
this._inputTemplate();
this._accessors();
this._aria();
},
destroy: function() {
var that = this;
that.wrapper.off(ns);
that.element.off(ns);
that._inputWrapper.off(ns);
Select.fn.destroy.call(that);
},
open: function() {
var that = this;
if (!that.ul[0].firstChild) {
that._open = true;
if (!that._request) {
that.dataSource.fetch();
}
} else {
that.popup.open();
that._scroll(that._current);
}
},
toggle: function(toggle) {
this._toggle(toggle);
},
refresh: function() {
var that = this,
data = that._data(),
length = data.length,
optionLabel = that.options.optionLabel;
that.trigger("dataBinding");
if (that._current) {
that.current(null);
}
that.ul[0].innerHTML = kendo.render(that.template, data);
that._height(length);
if (that.popup.visible()) {
that.popup._position();
}
if (that._isSelect) {
if (optionLabel && length) {
optionLabel = that._optionLabelText(optionLabel);
optionLabel = '' + optionLabel + " ";
}
that._options(data, optionLabel);
}
if (that._open) {
that._open = false;
that.toggle(!!length);
}
that._hideBusy();
that._makeUnselectable();
if (!that._fetch) {
if (length) {
that._selectItem();
} else if (that._textAccessor() !== optionLabel) {
that.element.val("");
that._textAccessor("");
}
}
that._bound = !!length;
that.trigger("dataBound");
},
search: function(word) {
if (word) {
var that = this,
ignoreCase = that.options.ignoreCase;
if (ignoreCase) {
word = word.toLowerCase();
}
that._select(function(dataItem) {
var text = that._text(dataItem);
if (text !== undefined) {
text = (text + "");
if (ignoreCase) {
text = text.toLowerCase();
}
return text.indexOf(word) === 0;
}
});
}
},
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;
dataItem = that._select(function(data) {
data = that._text(data);
if (ignoreCase) {
data = (data + "").toLowerCase();
}
return data === loweredText;
});
if (dataItem) {
text = dataItem;
}
}
that._textAccessor(text);
} else {
return that._textAccessor();
}
},
value: function(value) {
var that = this,
idx, hasValue;
if (value !== undefined) {
if (value !== null) {
value = value.toString();
}
that._selectedValue = value;
hasValue = value || (that.options.optionLabel && !that.element[0].disabled && value === "");
if (hasValue && that._fetchItems(value)) {
return;
}
idx = that._index(value);
that.select(idx > -1 ? idx : 0);
} else {
return that._accessor();
}
},
_editable: function(options) {
var that = this,
element = that.element,
disable = options.disable,
readonly = options.readonly,
wrapper = that.wrapper.off(ns),
dropDownWrapper = that._inputWrapper.off(HOVEREVENTS),
focusin = function() {
dropDownWrapper.addClass(FOCUSED);
that._blured = false;
},
focusout = function() {
if (!that._blured) {
that._triggerCascade();
var isIFrame = window.self !== window.top;
if (kendo.support.mobileOS.ios && isIFrame) {
that._change();
} else {
that._blur();
}
dropDownWrapper.removeClass(FOCUSED);
that._blured = true;
element.blur();
}
};
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("click" + ns, function(e) {
that._blured = false;
e.preventDefault();
that.toggle();
})
.on("keydown" + ns, proxy(that._keydown, that))
.on("keypress" + ns, proxy(that._keypress, that))
.on("focusin" + ns, focusin)
.on("focusout" + ns, focusout);
} else {
if (disable) {
wrapper.removeAttr(TABINDEX);
dropDownWrapper
.addClass(STATEDISABLED)
.removeClass(DEFAULT);
} else {
dropDownWrapper
.addClass(DEFAULT)
.removeClass(STATEDISABLED);
wrapper
.on("focusin" + ns, focusin)
.on("focusout" + ns, focusout);
}
element.attr(DISABLED, disable)
.attr(READONLY, readonly);
wrapper.attr(ARIA_DISABLED, disable)
.attr(ARIA_READONLY, readonly);
}
},
_accept: function(li) {
this._focus(li);
},
_optionLabelText: function() {
var options = this.options,
dataTextField = options.dataTextField,
optionLabel = options.optionLabel;
if (optionLabel && dataTextField && typeof optionLabel === "object") {
return this._text(optionLabel);
}
return optionLabel;
},
_data: function() {
var that = this,
options = that.options,
optionLabel = options.optionLabel,
textField = options.dataTextField,
valueField = options.dataValueField,
data = that.dataSource.view(),
length = data.length,
first = optionLabel,
idx = 0;
if (optionLabel && length) {
if (typeof optionLabel === "object") {
first = optionLabel;
} else if (textField) {
first = {};
textField = textField.split(".");
valueField = valueField.split(".");
assign(first, valueField, "");
assign(first, textField, optionLabel);
}
first = new kendo.data.ObservableArray([first]);
for (; idx < length; idx++) {
first.push(data[idx]);
}
data = first;
}
return data;
},
_keydown: function(e) {
var that = this,
key = e.keyCode,
keys = kendo.keys,
ul = that.ul[0];
if (key === keys.LEFT) {
key = keys.UP;
} else if (key === keys.RIGHT) {
key = keys.DOWN;
}
e.keyCode = key;
that._move(e);
if (key === keys.HOME) {
e.preventDefault();
that._select(ul.firstChild);
} else if (key === keys.END) {
e.preventDefault();
that._select(ul.lastChild);
}
},
_selectNext: function(word, index) {
var that = this, text,
startIndex = index,
data = that._data(),
length = data.length,
ignoreCase = that.options.ignoreCase,
action = function(text, index) {
text = text + "";
if (ignoreCase) {
text = text.toLowerCase();
}
if (text.indexOf(word) === 0) {
that._select(index);
that._triggerEvents();
return true;
}
};
for (; index < length; index++) {
text = that._text(data[index]);
if (text && action(text, index)) {
return true;
}
}
if (startIndex > 0) {
index = 0;
for (; index <= startIndex; index++) {
text = that._text(data[index]);
if (text && action(text, index)) {
return true;
}
}
}
return false;
},
_keypress: function(e) {
if (e.charCode === 0) {
return;
}
var that = this,
character = String.fromCharCode(e.charCode || e.keyCode),
index = that.selectedIndex,
word = that._word;
if (that.options.ignoreCase) {
character = character.toLowerCase();
}
if (character === " ") {
e.preventDefault();
}
if (that._last === character && word.length <= 1 && index > -1) {
if (!word) {
word = character;
}
if (that._selectNext(word, index + 1)) {
return;
}
}
that._word = word + character;
that._last = character;
that._search();
},
_popup: function() {
Select.fn._popup.call(this);
this.popup.one("open", function() {
this.wrapper = kendo.wrap(this.element)
.addClass("km-popup");
});
},
_search: function() {
var that = this,
dataSource = that.dataSource,
index = that.selectedIndex,
word = that._word;
clearTimeout(that._typing);
that._typing = setTimeout(function() {
that._word = "";
}, that.options.delay);
if (!that.ul[0].firstChild) {
dataSource.one(CHANGE, function () {
if (dataSource.data()[0]) {
that._selectNext(word, index);
}
}).fetch();
return;
}
that._selectNext(word, index);
that._triggerEvents();
},
_select: function(li) {
var that = this,
current = that._current,
data = null,
value,
idx;
li = that._get(li);
if (li && li[0] && !li.hasClass(SELECTED)) {
if (current) {
current.removeClass(SELECTED);
}
idx = ui.List.inArray(li[0], that.ul[0]);
if (idx > -1) {
data = that._data()[idx];
value = that._value(data);
that.selectedIndex = idx;
that._textAccessor(data);
that._accessor(value !== undefined ? value : that._text(data), idx);
that._selectedValue = that._accessor();
that.current(li.addClass(SELECTED));
if (that._optionID) {
that._current.attr("aria-selected", true);
}
}
}
return data;
},
_triggerEvents: function() {
if (!this.popup.visible()) {
this._triggerCascade();
this._change();
}
},
_mobile: function() {
var that = this,
popup = that.popup,
root = popup.element.parents(".km-root").eq(0);
if (root.length && os) {
popup.options.animation.open.effects = (os.android || os.meego) ? "fadeIn" : (os.ios || os.wp) ? "slideIn:up" : popup.options.animation.open.effects;
}
},
_span: function() {
var that = this,
wrapper = that.wrapper,
SELECTOR = "span.k-input",
span;
span = wrapper.find(SELECTOR);
if (!span[0]) {
wrapper.append(' select ')
.append(that.element);
span = wrapper.find(SELECTOR);
}
that.span = span;
that._inputWrapper = $(wrapper[0].firstChild);
that._arrow = wrapper.find(".k-icon").mousedown(function(e) { e.preventDefault(); });
},
_wrapper: function() {
var that = this,
element = that.element,
DOMelement = element[0],
wrapper;
wrapper = element.parent();
if (!wrapper.is("span.k-widget")) {
wrapper = element.wrap(" ").parent();
wrapper[0].style.cssText = DOMelement.style.cssText;
}
element.hide();
that._focused = that.wrapper = wrapper
.addClass("k-widget k-dropdown k-header")
.addClass(DOMelement.className)
.css("display", "")
.attr({
unselectable: "on",
role: "listbox",
"aria-haspopup": true,
"aria-expanded": false
});
},
_clearSelection: function() {
var that = this,
optionLabel = that.options.optionLabel;
if (that.dataSource.view()[0] && optionLabel) {
that.select(0);
return;
}
that.selectedIndex = -1;
that.element.val("");
that._textAccessor(optionLabel);
},
_inputTemplate: function() {
var that = this,
template = that.options.valueTemplate;
if (!template) {
template = $.proxy(kendo.template('#:this._text(data)#'), that);
} else {
template = kendo.template(template);
}
that.valueTemplate = template;
},
_textAccessor: function(text) {
var dataItem = this.dataItem();
var span = this.span;
if (text !== undefined) {
if ($.isPlainObject(text) || text instanceof kendo.data.ObservableObject) {
dataItem = text;
} else if (!dataItem || this._text(dataItem) !== text) {
if (this.options.dataTextField) {
dataItem = {};
dataItem[this.options.dataTextField] = text;
dataItem[this.options.dataValueField] = this._accessor();
} else {
dataItem = text;
}
}
span.html(this.valueTemplate(dataItem));
} else {
return span.text();
}
}
});
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;
}
ui.plugin(DropDownList);
})(window.kendo.jQuery);
kendo_module({
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" ]
} ]
});
(function($, undefined) {
var kendo = window.kendo,
ui = kendo.ui,
List = ui.List,
Select = ui.Select,
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_SELECTED = "k-state-selected",
STATE_FILTER = "filter",
STATE_ACCEPT = "accept",
STATE_REBIND = "rebind",
HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns,
NULL = null,
proxy = $.proxy;
var ComboBox = Select.extend({
init: function(element, options) {
var that = this, text;
that.ns = ns;
options = $.isArray(options) ? { dataSource: options } : options;
Select.fn.init.call(that, element, options);
that._focusHandler = function() {
that.input.focus();
};
options = that.options;
element = that.element.on("focus" + ns, that._focusHandler);
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._cascade();
that._aria();
that._oldIndex = that.selectedIndex = -1;
if (options.autoBind) {
that._filterSource();
} else {
text = options.text;
if (!text && that._isSelect) {
text = element.children(":selected").text();
}
if (text) {
that.input.val(text);
that._prev = text;
}
}
if (!text) {
that._placeholder();
}
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,
template: "",
filter: "none",
placeholder: "",
suggest: false,
cascadeFrom: "",
cascadeFromField: "",
ignoreCase: true,
animation: {}
},
events:[
"open",
"close",
CHANGE,
"select",
"dataBinding",
"dataBound",
"cascade"
],
setOptions: function(options) {
Select.fn.setOptions.call(this, options);
this._template();
this._accessors();
this._aria();
},
current: function(li) {
var that = this,
current = that._current;
if (li === undefined) {
return current;
}
if (current) {
current.removeClass(STATE_SELECTED);
}
Select.fn.current.call(that, li);
},
destroy: function() {
var that = this;
that.input.off(ns);
that.element.off(ns);
that._inputWrapper.off(ns);
Select.fn.destroy.call(that);
},
_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, function() { that.toggle(); })
.on(MOUSEDOWN, function(e) { e.preventDefault(); });
that.input
.on("keydown" + ns, proxy(that._keydown, that))
.on("focus" + ns, function() {
wrapper.addClass(FOCUSED);
that._placeholder(false);
})
.on("blur" + ns, function() {
wrapper.removeClass(FOCUSED);
clearTimeout(that._typing);
if (that.options.text !== that.input.val()) {
that.text(that.text());
}
that._placeholder();
that._blur();
that.element.blur();
});
} 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,
serverFiltering = that.dataSource.options.serverFiltering;
if (that.popup.visible()) {
return;
}
if (!that.ul[0].firstChild || (that._state === STATE_ACCEPT && !serverFiltering)) {
that._open = true;
that._state = STATE_REBIND;
that._filterSource();
} else {
that.popup.open();
that._scroll(that._current);
}
},
refresh: function() {
var that = this,
ul = that.ul[0],
options = that.options,
state = that._state,
data = that._data(),
length = data.length,
hasChild, value, open, custom;
that.trigger("dataBinding");
ul.innerHTML = kendo.render(that.template, data);
that._height(length);
if (that.popup.visible()) {
that.popup._position();
}
if (that._isSelect) {
hasChild = that.element[0].firstChild;
if (state === STATE_REBIND) {
that._state = "";
value = that.value();
}
custom = that._option;
that._option = undefined;
that._options(data);
if (custom && custom[0].selected) {
that._custom(custom.val());
} else if (!that._bound && !hasChild) {
that._custom("");
}
}
if (length) {
if (options.highlightFirst) {
that.current($(ul.firstChild));
}
if (options.suggest && that.input.val() && that._request !== undefined /*first refresh ever*/) {
that.suggest($(ul.firstChild));
}
}
if (state !== STATE_FILTER && !that._fetch) {
that._selectItem();
}
if (that._open) {
that._open = false;
open = !!length;
if (that._typing && that.input[0] !== activeElement()) {
open = false;
}
that.toggle(open);
that._typing = undefined;
}
if (that._touchScroller) {
that._touchScroller.reset();
}
that._makeUnselectable();
that._hideBusy();
that._bound = true;
that.trigger("dataBound");
},
search: function(word) {
word = typeof word === "string" ? word : this.text();
var that = this,
length = word.length,
options = that.options,
ignoreCase = options.ignoreCase,
filter = options.filter,
field = options.dataTextField;
clearTimeout(that._typing);
if (length >= options.minLength) {
that._state = STATE_FILTER;
if (filter === "none") {
that._filter(word);
} else {
that._open = true;
that._filterSource({
value: ignoreCase ? word.toLowerCase() : word,
field: field,
operator: filter,
ignoreCase: ignoreCase
});
}
}
},
suggest: function(word) {
var that = this,
element = that.input[0],
value = that.text(),
caret = List.caret(element),
key = that._last,
idx;
if (key == keys.BACKSPACE || key == keys.DELETE) {
that._last = undefined;
return;
}
word = word || "";
if (typeof word !== "string") {
idx = List.inArray(word[0], that.ul[0]);
if (idx > -1) {
word = that._text(that.dataSource.view()[idx]);
} else {
word = "";
}
}
if (caret <= 0) {
caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1;
}
if (word) {
idx = word.toLowerCase().indexOf(value.toLowerCase());
if (idx > -1) {
value += word.substring(idx + value.length);
}
} else {
value = value.substring(0, caret);
}
if (value.length !== caret || !word) {
element.value = value;
if (element === activeElement()) {
List.selectText(element, caret, value.length);
}
}
},
text: function (text) {
text = text === null ? "" : text;
var that = this,
input = that.input[0],
ignoreCase = that.options.ignoreCase,
loweredText = text,
dataItem;
if (text !== undefined) {
dataItem = that.dataItem();
if (dataItem && that._text(dataItem) === text && that._value(dataItem).toString() === that._old) {
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._custom(text);
input.value = text;
}
that._prev = input.value;
that._triggerCascade();
} else {
return input.value;
}
},
toggle: function(toggle) {
var that = this;
that._toggle(toggle);
},
value: function(value) {
var that = this,
options = that.options,
idx;
if (value !== undefined) {
if (value !== null) {
value = value.toString();
}
that._selectedValue = value;
if (!that._open && value && that._fetchItems(value)) {
return;
}
idx = that._index(value);
if (idx > -1) {
that.select(idx);
} else {
that.current(NULL);
that._custom(value);
if (options.value !== value || options.text !== that.input.val()) {
that.text(value);
that._placeholder();
}
}
that._old = that._accessor();
that._oldIndex = that.selectedIndex;
} else {
return that._accessor();
}
},
_accept: function(li) {
var that = this;
if (li) {
that._focus(li);
} else {
that.text(that.text());
that._change();
}
},
_custom: function(value) {
var that = this,
element = that.element,
custom = that._option;
if (that._state === STATE_FILTER) {
that._state = STATE_ACCEPT;
}
if (that._isSelect) {
if (!custom) {
custom = that._option = $(" ");
element.append(custom);
}
custom.text(value);
custom[0].selected = true;
} else {
element.val(value);
}
that._selectedValue = value;
},
_filter: function(word) {
var that = this,
options = that.options,
dataSource = that.dataSource,
ignoreCase = options.ignoreCase,
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.data()[0]) {
that.search(word);
}
}).fetch();
return;
}
if (that._highlight(predicate) !== -1) {
if (options.suggest && that._current) {
that.suggest(that._current);
}
that.open();
}
that._hideBusy();
},
_highlight: function(li) {
var that = this, idx;
if (li === undefined || li === null) {
return -1;
}
li = that._get(li);
idx = List.inArray(li[0], that.ul[0]);
if (idx == -1) {
if (that.options.highlightFirst && !that.text()) {
li = that.ul[0].firstChild;
if (li) {
li = $(li);
}
} else {
li = NULL;
}
}
that.current(li);
return idx;
},
_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('select ')
.append(that.element);
input = wrapper.find(SELECTOR);
}
input[0].style.cssText = element.style.cssText;
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._typing);
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()) {
List.selectText(input[0], 0, 0);
}
}
},
_search: function() {
var that = this;
that._typing = setTimeout(function() {
var value = that.text();
if (that._prev !== value) {
that._prev = value;
that.search(value);
}
}, that.options.delay);
},
_select: function(li) {
var that = this,
text,
value,
data = that._data(),
idx = that._highlight(li);
that.selectedIndex = idx;
if (idx !== -1) {
if (that._state === STATE_FILTER) {
that._state = STATE_ACCEPT;
}
that._current.addClass(STATE_SELECTED);
data = data[idx];
text = that._text(data);
value = that._value(data);
that._prev = that.input[0].value = text;
that._accessor(value !== undefined ? value : text, idx);
that._selectedValue = that._accessor();
that._placeholder();
if (that._optionID) {
that._current.attr("aria-selected", true);
}
}
},
_wrapper: function() {
var that = this,
element = that.element,
wrapper = element.parent();
if (!wrapper.is("span.k-widget")) {
wrapper = element.hide().wrap(" ").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,
hasValue = parent._selectedValue || parent.value(),
custom = hasValue && parent.selectedIndex === -1;
if (isFiltered || !hasValue || custom) {
that.value("");
}
}
});
ui.plugin(ComboBox);
})(window.kendo.jQuery);
kendo_module({
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" ]
} ]
});
(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",
OPEN = "open",
CLOSE = "close",
CHANGE = "change",
PROGRESS = "progress",
SELECT = "select",
NEXT = "nextSibling",
PREV = "previousSibling",
HIDE = ' style="display:none"',
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, data;
that.ns = ns;
List.fn.init.call(that, element, options);
that._wrapper();
that._tagList();
that._input();
that._textContainer();
that._loader();
that._tabindex(that.input);
element = that.element.attr("multiple", "multiple").hide();
options = that.options;
data = options.value;
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._values = [];
that._dataItems = [];
that._reset();
that._enable();
that._placeholder();
if (options.autoBind) {
that.dataSource.fetch();
} else if (data) {
if (!isArray(data)) {
data = [data];
}
if ($.isPlainObject(data[0]) || !options.dataValueField) {
that._retrieveData = true;
that.dataSource.data(data);
that.value(that._initialValues);
}
}
kendo.notify(that);
},
options: {
name: "MultiSelect",
enabled: true,
autoBind: true,
autoClose: true,
highlightFirst: true,
dataTextField: "",
dataValueField: "",
filter: "startswith",
ignoreCase: true,
minLength: 0,
delay: 100,
value: null,
maxSelectedItems: null,
itemTemplate: "",
tagTemplate: "",
placeholder: "",
height: 200,
animation: {}
},
events: [
OPEN,
CLOSE,
CHANGE,
SELECT,
"dataBinding",
"dataBound"
],
setDataSource: function(dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
if (this.options.autoBind) {
this.dataSource.fetch();
}
},
setOptions: function(options) {
List.fn.setOptions.call(this, options);
this._template();
this._accessors();
this._aria(this.tagList.attr(ID));
},
current: function(candidate) {
this.currentTag(null);
return List.fn.current.call(this, candidate);
},
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._dataItems;
},
destroy: function() {
var that = this,
ns = that.ns;
that.wrapper.off(ns);
that.tagList.off(ns);
that.input.off(ns);
List.fn.destroy.call(that);
},
_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, function(e) {
var deleteButton = e.target.className.indexOf("k-delete") !== -1;
e.preventDefault();
if (!deleteButton) {
if (that.input[0] !== activeElement()) {
that.input.focus();
}
if (that.options.minLength === 0) {
that.open();
}
}
});
that.input.on(KEYDOWN, proxy(that._keydown, that))
.on("paste" + ns, proxy(that._search, that))
.on("focus" + ns, function() { that._placeholder(false); })
.on("blur" + ns, function() {
clearTimeout(that._typing);
that._placeholder();
that.close();
if (that._state === FILTER) {
that._state = ACCEPT;
}
that.element.blur();
});
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, ".k-delete", function(e) {
that._unselect($(e.target).closest(LI));
that._change();
that.close();
});
} 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._visibleItems) {
that.close();
} else {
that.current(that.options.highlightFirst ? first(that.ul[0]) : null);
that.popup._position();
}
},
close: function() {
this.popup.close();
this.current(null);
},
open: function() {
var that = this;
if (!that.ul[0].firstChild || that._state === ACCEPT || that._retrieveData) {
that._state = "";
that._open = true;
that._retrieveData = false;
that._filterSource();
} else if (that._visibleItems && that._allowSelection()) {
that.popup.open();
that.current(that.options.highlightFirst ? first(that.ul[0]) : null);
}
},
toggle: function(toggle) {
toggle = toggle !== undefined ? toggle : !this.popup.visible();
this[toggle ? OPEN : CLOSE]();
},
refresh: function() {
var that = this,
li = null,
length;
that.trigger("dataBinding");
length = that._render(that.dataSource.view());
that._height(length);
if (that._setInitialValues) {
that._setInitialValues = false;
that.value(that._initialValues);
}
if (that._open) {
that._open = false;
that.toggle(length);
}
if (that.popup.visible()) {
that.popup._position();
if (that.options.highlightFirst) {
li = first(that.ul[0]);
}
}
that.current(li);
if (that._touchScroller) {
that._touchScroller.reset();
}
that._makeUnselectable();
that._hideBusy();
that.trigger("dataBound");
},
search: function(word) {
var that = this,
options = that.options,
ignoreCase = options.ignoreCase,
filter = options.filter,
field = options.dataTextField,
inputValue = that.input.val();
if (options.placeholder === inputValue) {
inputValue = "";
}
clearTimeout(that._typing);
word = typeof word === "string" ? word : inputValue;
if (word.length >= options.minLength) {
that._state = FILTER;
that._open = true;
that._filterSource({
value: ignoreCase ? word.toLowerCase() : word,
field: field,
operator: filter,
ignoreCase: ignoreCase
});
}
},
value: function(value) {
var that = this,
tags = $(that.tagList[0].children),
length = tags.length,
dataItemIndex,
idx = 0;
if (value === undefined) {
return that._values;
}
if (that._fetchItems(value)) {
return;
}
for (; idx < length; idx++) {
that._unselect(tags.eq(idx));
}
if (value !== null) {
value = isArray(value) || value instanceof ObservableArray ? value : [value];
for (idx = 0, length = value.length; idx < length; idx++) {
dataItemIndex = that._index(value[idx]);
if (dataItemIndex > -1) {
that._select(dataItemIndex);
}
}
that._old = that._values.slice();
}
},
_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._refreshHandler = proxy(that.refresh, that);
that._progressHandler = proxy(that._showBusy, that);
}
that.dataSource = kendo.data.DataSource.create(dataSource)
.bind(CHANGE, that._refreshHandler)
.bind(PROGRESS, that._progressHandler);
},
_fetchItems: function(value) {
var that = this;
var isEmptyArray = $.isArray(value) && value.length === 0;
if (isEmptyArray || !value) {
return;
}
if (!that._fetch && !that.ul[0].firstChild) {
that.dataSource.one(CHANGE, function() {
that.value(value);
that._fetch = false;
});
that._fetch = true;
that.dataSource.fetch();
return true;
}
},
_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._form = form.on("reset", that._resetHandler);
}
},
_initValue: function() {
var that = this,
value = that.options.value || that.element.val();
if (value === null) {
value = [];
} else {
if (!isArray(value)) {
value = [value];
}
value = that._mapValues(value);
}
that._old = that._initialValues = value;
that._setInitialValues = !!value[0];
},
_mapValues: function(values) {
var that = this;
if (values && $.isPlainObject(values[0])) {
values = $.map(values, function(dataItem) { return that._value(dataItem); });
}
return values;
},
_change: function() {
var that = this,
value = that.value();
if (!compare(value, that._old)) {
that._old = value.slice();
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_click: function(e) {
var that = this,
li = $(e.currentTarget);
if (!e.isDefaultPrevented()) {
if (that.trigger(SELECT, {item: li})) {
that._close();
return;
}
that._select(li);
that._change();
that._close();
}
},
_item: function(item, direction) {
item = item[direction]();
if (item[0] && !item.is(":visible")) {
item = this._item(item, direction);
}
return item;
},
_keydown: function(e) {
var that = this,
key = e.keyCode,
tag = that._currentTag,
current = that._current,
hasValue = that.input.val(),
isRtl = kendo.support.isRtl(that.wrapper),
visible = that.popup.visible();
if (key === keys.DOWN) {
e.preventDefault();
if (!visible) {
that.open();
return;
}
if (current) {
current = sibling(current[0], NEXT);
} else {
current = first(that.ul[0]);
}
if (current) {
that.current($(current));
}
} else if (key === keys.UP) {
if (visible) {
if (current) {
current = sibling(current[0], PREV);
} else {
current = last(that.ul[0]);
}
that.current($(current));
if (!that._current[0]) {
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) {
that.current(first(that.ul[0]));
} else if (!hasValue) {
tag = that.tagList[0].firstChild;
if (tag) {
that.currentTag($(tag));
}
}
} else if (key === keys.END) {
if (visible) {
that.current(last(that.ul[0]));
} 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._unselect(tag);
that._change();
that._close();
}
} else {
clearTimeout(that._typing);
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._busy = null;
},
_showBusy: function () {
var that = this;
if (that._busy) {
return;
}
that._busy = setTimeout(function () {
that.input.attr("aria-busy", true);
that._loading.removeClass(HIDDENCLASS);
}, 100);
},
_placeholder: function(show) {
var that = this,
input = that.input,
active = activeElement();
if (show === undefined) {
show = false;
if (input[0] !== active) {
show = !that._dataItems[0];
}
that.wrapper.removeClass(FOCUSEDCLASS);
} else {
that.wrapper.addClass(FOCUSEDCLASS);
}
that._prev = "";
input.toggleClass("k-readonly", show)
.val(show ? that.options.placeholder : "");
if (input[0] === active) {
List.selectText(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(dataItem, selected) {
var option = "";
if (dataText !== undefined) {
option += kendo.htmlEncode(dataText);
}
return option += " ";
},
_render: function(data) {
var that = this,
length = data.length,
template = that.itemTemplate,
values = that._dataItems.slice(0),
visibleItems = 0, idx = 0,
options = "", html = "",
dataItem, selected;
for (; idx < length; idx++) {
dataItem = data[idx];
selected = that._selected(values, dataItem);
html += template(dataItem, idx, selected);
options += that._option(dataItem, selected);
if (!selected) {
visibleItems += 1;
}
}
length = values.length;
if (length) {
for (idx = 0; idx < length; idx++) {
options += that._option(values[idx], true);
}
}
that.ul[0].innerHTML = html;
that.element.html(options);
that._visibleItems = visibleItems;
return visibleItems;
},
_selected: function(values, dataItem) {
var that = this,
textAccessor = that._text,
valueAccessor = that._value,
value = valueAccessor(dataItem),
length = values.length,
selected = false,
dataValue,
idx = 0;
if (value === undefined) {
value = textAccessor(dataItem);
}
for (; idx < length; idx++) {
dataItem = values[idx];
dataValue = valueAccessor(dataItem);
if (dataValue === undefined) {
dataValue = textAccessor(dataItem);
}
if (dataValue !== undefined && dataValue === value) {
selected = true;
break;
}
}
if (selected) {
values.splice(idx, 1);
}
return selected;
},
_search: function() {
var that = this;
that._typing = 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._values.length;
},
_select: function(li) {
var that = this,
values = that._values,
dataItem,
idx;
if (!that._allowSelection()) {
return;
}
if (!isNaN(li)) {
idx = li;
that.ul[0].children[idx].style.display = "none";
} else {
idx = li.hide().data("idx");
}
that.element[0].children[idx].selected = true;
dataItem = that.dataSource.view()[idx];
that.tagList.append(that.tagTemplate(dataItem));
that._dataItems.push(dataItem);
values.push(that._dataValue(dataItem));
that._visibleItems -= 1;
that.currentTag(null);
that._placeholder();
that._height(that._visibleItems);
if (that._state === FILTER) {
that._state = ACCEPT;
}
},
_unselect: function(tag) {
var that = this,
index = tag.index(),
dataItem, value,
options, option, length;
tag.remove();
that.currentTag(null);
that._values.splice(index, 1);
dataItem = that._dataItems.splice(index, 1)[0];
value = that._dataValue(dataItem);
index = that._index(value);
if (index !== -1) {
$(that.ul[0].children[index]).show();
that.element[0].children[index].selected = false;
that._visibleItems += 1;
that._height(that._visibleItems);
} else {
index = that.dataSource.view().length;
options = that.element[0].children;
length = options.length;
for (; index < length; index++) {
option = options[index];
if (option.value == value) {
option.selected = false;
break;
}
}
}
that._placeholder();
},
_template: function() {
var that = this,
options = that.options,
itemTemplate = options.itemTemplate,
tagTemplate = options.tagTemplate,
hasDataSource = options.dataSource,
textTemplate;
if (that.element[0].length && !hasDataSource) {
options.dataTextField = options.dataTextField || "text";
options.dataValueField = options.dataValueField || "value";
}
textTemplate = kendo.template("#:" + kendo.expr(options.dataTextField, "data") + "#", { useWithBlock: false });
itemTemplate = itemTemplate ? kendo.template(itemTemplate) : textTemplate;
tagTemplate = tagTemplate ? kendo.template(tagTemplate) : textTemplate;
that.itemTemplate = function(data, idx, hide) {
return '' + itemTemplate(data) + ' ';
};
that.tagTemplate = function(data) {
return '' + tagTemplate(data) + ' delete ';
};
},
_input: function() {
var that = this,
accessKey = that.element[0].accessKey,
input = that._innerWrapper.children("input.k-input");
if (!input[0]) {
input = $(' ').appendTo(that._innerWrapper);
}
that.element.removeAttr("accesskey");
that._focused = that.input = input.attr({
"accesskey": accessKey,
"role": "listbox",
"aria-expanded": false
});
},
_tagList: function() {
var that = this,
tagList = that._innerWrapper.children("ul");
if (!tagList[0]) {
tagList = $('').appendTo(that._innerWrapper);
}
that.tagList = tagList;
},
_loader: function() {
this._loading = $(' ').insertAfter(this.input);
},
_textContainer: function() {
var computedStyles = kendo.getComputedStyles(this.input[0], styles);
computedStyles.position = "absolute";
computedStyles.visibility = "hidden";
this._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('').parent();
wrapper[0].style.cssText = element[0].style.cssText;
$('
').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;
}
function first(ul) {
var item = ul.firstChild;
if (item && item.style.display === "none") {
item = sibling(item, NEXT);
}
if (item) {
return $(item);
}
return item;
}
function last(ul) {
var item = ul.lastChild;
if (item && item.style.display === "none") {
item = sibling(item, PREV);
}
if (item) {
return $(item);
}
return item;
}
function sibling(item, direction) {
item = item[direction];
if (item && item.style.display === "none") {
item = sibling(item, direction);
}
return item;
}
ui.plugin(MultiSelect);
})(window.kendo.jQuery);
kendo_module({
id: "colorpicker",
name: "Color tools",
category: "web",
description: "Color selection widgets",
depends: [ "core", "popup", "slider", "userevents" ]
});
(function($, parseInt, undefined){
// WARNING: removing the following jshint declaration and turning
// == into === to make JSHint happy will break functionality.
/*jshint eqnull:true */
var kendo = window.kendo,
Class = kendo.Class,
ui = kendo.ui,
Widget = ui.Widget,
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 = parse(options.value);
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 = parse(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;
}
if (enable) {
$(".k-disabled-overlay", this.wrapper).remove();
} else {
this.wrapper.append("
");
}
this._onEnable(enable);
},
_select: function(color, nohooks) {
var prev = this._value;
color = this.color(color);
if (!nohooks) {
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 = parse(color);
if (color && !color.equals(self.color())) {
if (type == "change") {
// UI is already updated. setting _value directly
// rather than calling self.color(color) to avoid an
// endless loop.
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 parse(x); });
}
element.addClass("k-widget k-colorpalette")
.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", 0)
.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.removeAttr("tabIndex");
} else {
this.wrapper.attr("tabIndex", 0);
}
},
_keydown: function(e) {
var selected,
that = this,
wrapper = that.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, that.options.columns);
} else if (keyCode == KEYS.UP) {
selected = relative(items, current, -that.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);
selected = $(selected);
$(current).removeClass(ITEMSELECTEDCLASS).removeAttr("aria-selected");
selected.addClass(ITEMSELECTEDCLASS).attr("aria-selected", true);
try {
var color = parse(selected.css(BACKGROUNDCOLOR));
that._triggerSelect(color);
} catch(ex) {}
}
},
_updateUI: function(color) {
var that = this,
el = null;
that.wrapper.find(".k-item." + ITEMSELECTEDCLASS)
.removeClass(ITEMSELECTEDCLASS)
.removeAttr("aria-selected");
that.wrapper.find(".k-item").each(function(){
var c = parse($(this).css(BACKGROUNDCOLOR));
if (c && c.equals(color)) {
el = this;
}
});
$(el).addClass(ITEMSELECTEDCLASS).attr("aria-selected", true);
},
_template: kendo.template(
'' +
'# for (var i = 0; i < colors.length; ++i) { #' +
'# if (i && i % columns == 0) { # # } #' +
' ' +
'# } #' +
'
'
)
});
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 || new _RGB(1, 0, 0, 1));
element
.find("input.k-color-value").on(KEYDOWN_NS, function(ev){
var input = this;
if (ev.keyCode == KEYS.ENTER) {
try {
var color = parse(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 = parse(input.value, true);
if (color) {
that._updateUI(color, true);
}
}, 10);
}
}).end()
.on(CLICK_NS, ".k-controls button.apply", function(){
// calling select for the currently displayed
// color will trigger the "change" event.
that._select(that._getHSV());
})
.on(CLICK_NS, ".k-controls button.cancel", function(){
// but on cancel, we simply select the previous
// value (again, triggers "change" event).
that._updateUI(that.color());
that._cancel();
});
if (isIE8) {
// IE filters require absolute URLs
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", 0);
} 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 new _HSV(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({
// saturation is 0 on the left side, full (1) on the right
left: color.s * rect.width() + "px",
// value is 0 on the bottom, full on the top.
top: (1 - color.v) * rect.height() + "px"
});
that._hueElements.css(BACKGROUNDCOLOR, new _HSV(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) { #' +
'' +
'# } #' +
'' +
' ' +
'# if (opacity) { #' +
' ' +
'# } #' +
'# if (buttons) { #' +
'#: messages.apply # #: messages.cancel #
' +
'# } #'
)
});
/* -----[ color utils ]----- */
function hex(n, width, pad) {
if (!pad) { pad = "0"; }
n = n.toString(16);
while (width > n.length) {
n = "0" + n;
}
return n;
}
function fixed(n) {
return parseFloat((+n).toFixed(3));
}
var Color = 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 + ", " + fixed(this.a) + ")";
},
toDisplay: function() {
if (isIE8) {
return this.toCss(); // no RGBA support; does it support any opacity in colors?
}
return this.toCssRgba();
},
equals: function(c) { return c === this || c !== null && this.toCssRgba() == parse(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.30, 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);
},
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();
},
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);
},
toBytes: function() {
return this.toRGB().toBytes();
}
});
function parse(color, nothrow) {
if (color == null ||
color == "transparent" /* IE8 does this */)
{
return null;
}
if (color instanceof Color) {
return color;
}
var m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
if (m) {
return new _Bytes(parseInt(m[1], 16),
parseInt(m[2], 16),
parseInt(m[3], 16), 1);
}
m = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color);
if (m) {
return new _Bytes(parseInt(m[1] + m[1], 16),
parseInt(m[2] + m[2], 16),
parseInt(m[3] + m[3], 16), 1);
}
m = /^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/.exec(color);
if (m) {
return new _Bytes(parseInt(m[1], 10),
parseInt(m[2], 10),
parseInt(m[3], 10), 1);
}
m = /^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)/.exec(color);
if (m) {
return new _Bytes(parseInt(m[1], 10),
parseInt(m[2], 10),
parseInt(m[3], 10), parseFloat(m[4]));
}
m = /^rgb\(\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*\)/.exec(color);
if (m) {
return new _RGB(parseFloat(m[1]) / 100,
parseFloat(m[2]) / 100,
parseFloat(m[3]) / 100, 1);
}
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);
if (m) {
return new _RGB(parseFloat(m[1]) / 100,
parseFloat(m[2]) / 100,
parseFloat(m[3]) / 100, parseFloat(m[4]));
}
if (!nothrow) {
throw new Error("Cannot parse color: " + color);
}
return undefined;
}
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];
}
/* -----[ The ColorPicker widget ]----- */
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 = parse(value, true);
} else {
value = parse(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);
}
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("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", 0)
.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-icon", bind(that.toggle, that))
.on(CLICK_NS, that.options.toolIcon ? ".k-tool-icon" : ".k-selected-color", function(){
that.trigger("activate");
});
}
},
_template: kendo.template(
''
),
options: {
name: "ColorPicker",
palette: null,
columns: 10,
toolIcon: null,
value: null,
messages: APPLY_CANCEL,
opacity: false,
buttons: true,
preview: true
},
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) {
if (value) {
if (this._isInputTypeColor() || value.a == 1) {
// seems that input type="color" doesn't support opacity
// in colors; the only accepted format is hex #RRGGBB
this.element.val(value.toCss());
} else {
this.element.val(value.toCssRgba());
}
}
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 = this.options;
var selectorType;
if (options.palette) {
selectorType = ColorPalette;
} else {
selectorType = FlatColorPicker;
}
options._standalone = false;
delete options.select;
delete options.change;
delete options.cancel;
var selector = this._selector = new selectorType($("
").appendTo(document.body), options);
that._popup = popup = selector.wrapper.kendoPopup({
anchor: that.wrapper
}).data("kendoPopup");
selector.bind({
select: function(ev){
that._updateUI(parse(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);
kendo.parseColor = parse;
kendo.Color = {
fromBytes: function(r, g, b, a) {
return new _Bytes(r, g, b, a != null ? a : 1);
},
fromRGB: function(r, g, b, a) {
return new _RGB(r, g, b, a != null ? a : 1);
},
fromHSV: function(h, s, v, a) {
return new _HSV(h, s, v, a != null ? a : 1);
}
};
})(jQuery, parseInt);
kendo_module({
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, "");
}
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"));
link = element.find(".k-header-column-menu");
if (!link[0]) {
link = element.prepend(' ').find(".k-header-column-menu");
}
that.link = link
.attr("tabindex", -1)
.on("click" + NS, proxy(that._click, that));
that.wrapper = $('
');
},
_init: function() {
var that = this;
that.pane = that.element.closest(kendo.roleSelector("pane")).data("kendoMobilePane");
if (that.pane) {
that._isMobile = true;
}
if (that._isMobile) {
that._createMobileMenu();
} else {
that._createMenu();
}
that._sort();
that._columns();
that._filter();
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"
},
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
}));
that.popup = that.wrapper[POPUP]({
anchor: that.link,
open: proxy(that._open, that),
activate: proxy(that._activate, that),
close: that.options.closeCallback
}).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,
messages: options.messages,
sortable: options.sortable,
filterable: options.filterable,
columns: that._ownerColumns(),
showColumns: options.columns
});
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();
});
},
destroy: function() {
var that = this;
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.unbind("columnShow", that._updateColumnsMenuHandler);
that.owner.unbind("columnHide", that._updateColumnsMenuHandler);
}
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);
},
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();
}
});
},
_activate: function() {
this.menu.element.focus();
},
_ownerColumns: function() {
var columns = 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)
};
});
},
_sort: function() {
var that = this;
if (that.options.sortable) {
that.refresh();
that._refreshHandler = proxy(that.refresh, that);
that.dataSource.bind(CHANGE, that._refreshHandler);
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,
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 === true || sortable.mode === "single") {
sort = [ { field: that.field, dir: dir } ];
} else {
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 });
}
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.menu.bind(SELECT, function(e) {
var item = $(e.item),
input,
index,
column,
columns = 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];
index = inArray(column, columns);
if (column.hidden === true) {
that.owner.showColumn(index);
} else {
that.owner.hideColumn(index);
}
});
}
},
_updateColumnsMenu: function() {
var attr = kendo.attr("field"),
visible = grep(this._ownerColumns(), function(field) {
return !field.hidden;
}),
visibleDataFields = grep(visible, function(field) {
return field.originalField;
}).length;
visible = map(visible, function(col) {
return col.field;
});
this.wrapper
.find(".k-columns-item input[" + attr + "]")
.prop("checked", false)
.filter(function() {
return inArray($(this).attr(attr), visible) > -1;
})
.prop("checked", true)
.prop("disabled", visibleDataFields == 1);
},
_filter: function() {
var that = this,
options = that.options;
if (options.filterable !== false) {
that.filterMenu = that.wrapper.find(".k-filterable")[FILTERMENU](
extend(true, {}, {
appendToElement: true,
dataSource: options.dataSource,
values: options.values,
field: that.field
},
options.filterable)
).data(FILTERMENU);
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);
}
});
}
}
},
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);
}
}
}
});
var template = ''+
'#if(sortable){#'+
' ${messages.sortAscending} '+
' ${messages.sortDescending} '+
'#if(showColumns || filterable){#'+
' '+
'#}#'+
'#}#'+
'#if(showColumns){#'+
' ${messages.columns} '+
'#if(filterable){#'+
' '+
'#}#'+
'#}#'+
'#if(filterable){#'+
' ${messages.filter} '+
'#}#'+
' ';
var mobileTemplate =
''+
''+
'
'+
'${field} '+
'#if(sortable){#'+
' ${messages.sortAscending} '+
' ${messages.sortDescending} '+
'#}#'+
'#if(filterable){#'+
''+
''+
' '+
'${messages.filter} '+
' '+
'#}#'+
' '+
'#if(showColumns){#'+
'${messages.columns} '+
'#}#'+
''+
'
';
var MobileMenu = Widget.extend({
init: function(element, options) {
Widget.fn.init.call(this, element, options);
this.element.on("click" + NS, "li:not(.k-separator)", "_click");
},
events: [ SELECT ],
_click: function(e) {
if (this.trigger(SELECT, { item: e.currentTarget })) {
e.preventDefault();
}
},
close: function() {
this.options.pane.navigate("");
},
destroy: function() {
Widget.fn.destroy.call(this);
this.element.off(NS);
}
});
ui.plugin(ColumnMenu);
})(window.kendo.jQuery);
kendo_module({
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" ],
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-paging",
name: "Paging",
description: "Suppot for grid paging",
depends: [ "pager" ]
}, {
id: "grid-selection",
name: "Selection",
description: "Support for row selection",
depends: [ "selectable" ]
}, {
id: "grid-sorting",
name: "Sorting",
description: "Support for grid sorting",
depends: [ "sortable" ]
}, {
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" ]
} ]
});
(function($, undefined) {
var kendo = window.kendo,
ui = kendo.ui,
DataSource = kendo.data.DataSource,
Groupable = ui.Groupable,
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,
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,.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",
COLUMNRESIZE = "columnResize",
COLUMNREORDER = "columnReorder",
CLICK = "click",
HEIGHT = "height",
TABINDEX = "tabIndex",
FUNCTION = "function",
STRING = "string",
DELETECONFIRM = "Are you sure you want to delete this record?",
CONFIRMDELETE = "Delete",
CANCELDELETE = "Cancel",
formatRegExp = /(\}|\#)/ig,
templateHashRegExp = /#/ig,
whitespaceRegExp = "[\\x20\\t\\r\\n\\f]",
nonDataCellsRegExp = new RegExp("(^|" + whitespaceRegExp + ")" + "(k-group-cell|k-hierarchy-cell)" + "(" + whitespaceRegExp + "|$)"),
COMMANDBUTTONTMPL = ' #=text# ',
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
},
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();
}
},
wrap: function() {
var that = this,
// workaround for IE issue where scroll is not raised if container is same width as the scrollbar
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('
')
.parent()
.bind("DOMMouseScroll" + NS + " mousewheel" + NS, proxy(that._wheelScroll, that));
if (kendo.support.kineticScrollNeeded) {
that.drag = new kendo.UserEvents(that.wrapper, {
global: true,
move: function(e) {
that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta);
wrapper.scrollLeft(wrapper.scrollLeft() - e.x.delta);
e.preventDefault();
}
});
}
that.verticalScrollbar = $('
')
.css({
width: scrollbar
}).appendTo(element)
.bind("scroll" + NS, proxy(that._scroll, that));
},
_wheelScroll: function(e) {
var that = this,
scrollTop = that.verticalScrollbar.scrollTop(),
originalEvent = e.originalEvent,
deltaY = originalEvent.wheelDeltaY,
delta;
if (originalEvent.wheelDelta) { // Webkit and IE
if (deltaY === undefined || deltaY) { // IE does not have deltaY, thus always scroll (horizontal scrolling is treated as vertical)
delta = originalEvent.wheelDelta;
}
} else if (originalEvent.detail && originalEvent.axis === originalEvent.VERTICAL_AXIS) { // Firefox and Opera
delta = (-originalEvent.detail) * 10;
}
if (delta) {
e.preventDefault();
that.verticalScrollbar.scrollTop(scrollTop + (-delta));
}
},
_scroll: function(e) {
var that = this,
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;
if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) {
that.wrapper[0].scrollTop = that._scrollTop;
}
},
_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) {
if (firstItemIndex < (currentSkip + take) - take * prefetchAt && firstItemIndex > take) {
dataSource.prefetch(currentSkip - take, take);
}
if (lastItemIndex > currentSkip + take * prefetchAt) {
dataSource.prefetch(currentSkip + take, take);
}
}
return fetching;
},
_page: function(skip, take) {
var that = this,
dataSource = that.dataSource;
clearTimeout(that._timeout);
that._fetching = true;
that._rangeStart = skip;
if (dataSource.inRange(skip, take)) {
dataSource.range(skip, take);
} else {
kendo.ui.progress(that.wrapper.parent(), true);
that._timeout = setTimeout(function() {
dataSource.range(skip, take);
}, 100);
}
},
refresh: function() {
var that = this,
html = "",
maxHeight = 250000,
dataSource = that.dataSource,
rangeStart = that._rangeStart,
scrollbar = !kendo.support.kineticScrollNeeded ? kendo.support.scrollbar() : 0,
wrapperElement = that.wrapper[0],
totalHeight,
idx,
itemHeight;
kendo.ui.progress(that.wrapper.parent(), false);
clearTimeout(that._timeout);
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 += '
';
}
if (totalHeight % maxHeight) {
html += '
';
}
that.verticalScrollbar.html(html);
wrapperElement.scrollTop = that._scrollTop;
if (that.drag) {
that.drag.cancel();
}
if (rangeStart && !that._fetching) { // we are rebound from outside local range should be reset
that._rangeStart = dataSource.skip();
}
that._fetching = false;
}
});
function groupCells(count) {
return new Array(count + 1).join(' ');
}
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-grid-update",
iconClass: "k-icon"
},
canceledit: {
text: "Cancel",
imageClass: "k-cancel",
className: "k-grid-cancel",
iconClass: "k-icon"
}
};
function heightAboveHeader(context) {
var top = 0;
$('> .k-grouping-header, > .k-grid-toolbar', context).each(function () {
top += this.offsetHeight;
});
return top;
}
function cursor(context, value) {
$('th, th .k-grid-filter, th .k-link', context)
.add(document.body)
.css('cursor', value);
}
function buildEmptyAggregatesObject(aggregates) {
var idx,
length,
aggregate = {},
fieldsMap = {};
if (!isEmptyObject(aggregates)) {
if (!isArray(aggregates)){
aggregates = [aggregates];
}
for (idx = 0, length = aggregates.length; idx < length; idx++) {
aggregate[aggregates[idx].aggregate] = 0;
fieldsMap[aggregates[idx].field] = aggregate;
}
}
return fieldsMap;
}
function reorder(selector, sourceIndex, destIndex) {
var source = selector.eq(sourceIndex),
dest = selector.eq(destIndex);
source[sourceIndex > destIndex ? "insertBefore" : "insertAfter"](dest);
}
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 visibleColumns(columns) {
return grep(columns, function(column) {
return !column.hidden;
});
}
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*;?)*/ig, "");
}
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(' ', typeof width === STRING? width : width + "px");
}
return " ";
});
if (hasDetails || colgroup.find(".k-hierarchy-col").length) {
cols.splice(0, 0, ' ');
}
if (colgroup.length) {
colgroup.remove();
}
colgroup = $(new Array(groups + 1).join(' ') + cols.join(""));
if (!colgroup.is("colgroup")) {
colgroup = $(" ").append(colgroup);
}
table.prepend(colgroup);
// fill gap after column hiding
if (browser.msie && browser.version == 8) {
table.css("display", "inline-table");
window.setTimeout(function(){table.css("display", "");}, 1);
}
}
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) {
var isForiegnKey = columnValues && columnValues.length && isPlainObject(columnValues[0]) && "value" in columnValues[0],
groupValue = isForiegnKey ? convertToObject(columnValues)[value] : value;
groupValue = groupValue != null ? groupValue : "";
return format ? kendo.format(format, groupValue) : 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];
}
}
var Grid = Widget.extend({
init: function(element, options) {
var that = this;
options = isArray(options) ? { dataSource: options } : options;
Widget.fn.init.call(that, element, options);
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._details();
that._editable();
that._attachCustomCommandsEvent();
if (that.options.autoBind) {
that.dataSource.fetch();
} else {
that._footer();
}
kendo.notify(that);
},
events: [
CHANGE,
"dataBinding",
"cancel",
DATABOUND,
DETAILEXPAND,
DETAILCOLLAPSE,
DETAILINIT,
FILTERMENUINIT,
COLUMNMENUINIT,
EDIT,
SAVE,
REMOVE,
SAVECHANGES,
COLUMNRESIZE,
COLUMNREORDER,
COLUMNSHOW,
COLUMNHIDE
],
setDataSource: function(dataSource) {
var that = this;
that.options.dataSource = dataSource;
that._dataSource();
that._pageable();
if (that.options.groupable) {
that._groupable();
}
that._thead();
if (that.virtualScrollable) {
that.virtualScrollable.setDataSource(that.options.dataSource);
}
if (that.options.autoBind) {
dataSource.fetch();
}
},
options: {
name: "Grid",
columns: [],
toolbar: null,
autoBind: true,
filterable: false,
scrollable: true,
sortable: false,
selectable: false,
navigatable: false,
pageable: false,
editable: false,
groupable: false,
rowTemplate: "",
altRowTemplate: "",
dataSource: {},
height: null,
resizable: false,
reorderable: false,
columnMenu: false,
detailTemplate: null,
columnResizeHandleWidth: 3
},
destroy: function() {
var that = this,
element;
Widget.fn.destroy.call(that);
if (that.pager) {
that.pager.destroy();
}
if (that.groupable) {
that.groupable.destroy();
}
if (that.options.reorderable) {
that.wrapper.data("kendoReorderable").destroy();
}
if (that.resizable) {
that.resizable.destroy();
if (that._resizeUserEvents) {
if (that._resizeHandleDocumentClickHandler) {
$(document).off("click", that._resizeHandleDocumentClickHandler);
}
that._resizeUserEvents.destroy();
}
}
if (that.virtualScrollable) {
that.virtualScrollable.destroy();
}
that._destroyColumnAttachments();
that._destroyEditable();
that.dataSource.unbind(CHANGE, that._refreshHandler)
.unbind(PROGRESS, that._progressHandler)
.unbind(ERROR, that._errorHandler);
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.pane) {
that.pane.destroy();
}
element.off(NS);
kendo.destroy(that.wrapper);
},
setOptions: function(options) {
var that = this;
Widget.fn.setOptions.call(this, options);
that._templates();
},
items: function() {
return this.tbody.children(':not(.k-grouping-row,.k-detail-row,.k-group-footer)');
},
_destroyColumnAttachments: function() {
var that = this;
that.resizeHandle = null;
that.thead.find("th").each(function(){
var th = $(this),
filterMenu = th.data("kendoFilterMenu"),
sortable = th.data("kendoSortable"),
columnMenu = th.data("kendoColumnMenu");
if (filterMenu) {
filterMenu.destroy();
}
if (sortable) {
sortable.destroy();
}
if (columnMenu) {
columnMenu.destroy();
}
});
},
_attachCustomCommandsEvent: function() {
var that = this,
columns = 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 = $("").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 left;
if (!resizeHandle) {
resizeHandle = that.resizeHandle = $('');
container.append(resizeHandle);
}
if (!isRtl) {
left = th[0].offsetWidth;
th.prevAll(":visible").each(function() {
left += this.offsetWidth;
});
} else {
var headerWrap = th.closest(".k-grid-header-wrap"),
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 = th.position().left - webkitCorrection + firefoxCorrection - ieCorrection;
}
resizeHandle.css({
top: scrollable ? 0 : heightAboveHeader(that.wrapper),
left: left - indicatorWidth,
height: th.outerHeight(),
width: indicatorWidth * 3
})
.data("th", th)
.show();
},
_positionColumnResizeHandle: function(container) {
var that = this,
resizeHandle = that.resizeHandle,
indicatorWidth = that.options.columnResizeHandleWidth;
that.thead.on("mousemove" + NS, "th:not(.k-group-cell,.k-hierarchy-cell)", function(e) {
var th = $(this),
clientX = e.clientX,
winScrollLeft = $(window).scrollLeft(),
position = th.offset().left + (!isRtl ? this.offsetWidth : 0);
if(clientX + winScrollLeft > position - indicatorWidth && clientX + winScrollLeft < position + indicatorWidth) {
that._createResizeHandle(container, th);
} else if (resizeHandle) {
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");
this.resizeHandle.hide();
}
},
_positionColumnResizeHandleTouch: function(container) {
var that = this;
that._resizeUserEvents = new kendo.UserEvents(that.thead, {
filter: "th:not(.k-group-cell,.k-hierarchy-cell)",
threshold: 10,
hold: function(e) {
var th = $(e.target);
e.preventDefault();
th.addClass("k-column-active");
that._createResizeHandle(container, 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,
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);
}
that.resizable = new ui.Resizable(container, {
handle: ".k-resize-handle",
hint: function(handle) {
return $('
').css({
height: handle.data("th").outerHeight() + that.tbody.attr("clientHeight")
});
},
start: function(e) {
th = $(e.currentTarget).data("th");
if (isMobile) {
that._hideResizeHandle();
}
var index = $.inArray(th[0], th.parent().children(":visible")),
contentTable = that.tbody.parent(),
footer = that.footer || $();
cursor(that.wrapper, 'col-resize');
if (options.scrollable) {
col = that.thead.parent().find("col:eq(" + index + ")")
.add(contentTable.children("colgroup").find("col:eq(" + index + ")"))
.add(footer.find("colgroup").find("col:eq(" + index + ")"));
} else {
col = contentTable.children("colgroup").find("col:eq(" + index + ")");
}
columnStart = e.x.location;
columnWidth = th.outerWidth();
gridWidth = that.tbody.outerWidth(); // IE returns 0 if grid is empty and scrolling is enabled
},
resize: function(e) {
var rtlMultiplier = isRtl ? -1 : 1,
width = columnWidth + (e.x.location * rtlMultiplier) - (columnStart * rtlMultiplier),
footer = that.footer || $();
if (width > 10) {
col.css('width', width);
if (options.scrollable && gridWidth) {
that._footerWidth = gridWidth + (e.x.location * rtlMultiplier) - (columnStart * rtlMultiplier);
that.tbody.parent()
.add(that.thead.parent())
.add(footer.find("table"))
.css('width', that._footerWidth);
}
}
},
resizeend: function() {
var newWidth = th.outerWidth(),
column;
cursor(that.wrapper, "");
if (columnWidth != newWidth) {
column = that.columns[th.parent().find("th:not(.k-group-cell,.k-hierarchy-cell)").index(th)];
column.width = newWidth;
that.trigger(COLUMNRESIZE, {
column: column,
oldWidth: columnWidth,
newWidth: newWidth
});
}
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(),
filter: that.content ? ".k-grid-header:first " + HEADERCELLS : "table:first>.k-grid-header " + HEADERCELLS,
drag: function() {
that._hideResizeHandle();
},
hint: function(target) {
return $('')
.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(' ');
}
}).data("kendoDraggable");
}
},
_reorderable: function() {
var that = this;
if (that.options.reorderable) {
that.wrapper.kendoReorderable({
draggable: that._draggableInstance,
change: function(e) {
var newIndex = inArray(that.columns[e.newIndex], that.columns),
column = that.columns[e.oldIndex];
that.trigger(COLUMNREORDER, {
newIndex: newIndex,
oldIndex: inArray(column, that.columns),
column: column
});
that.reorderColumn(newIndex, column);
}
});
}
},
reorderColumn: function(destIndex, column) {
var that = this,
sourceIndex = inArray(column, that.columns),
colSourceIndex = inArray(column, visibleColumns(that.columns)),
colDestIndex = inArray(that.columns[destIndex], visibleColumns(that.columns)),
rows,
idx,
length,
footer = that.footer || that.wrapper.find(".k-grid-footer");
if (sourceIndex === destIndex) {
return;
}
that._hideResizeHandle();
that.columns.splice(sourceIndex, 1);
that.columns.splice(destIndex, 0, column);
that._templates();
reorder(that.thead.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
if (that.options.scrollable) {
reorder(that.tbody.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
}
reorder(that.thead.find(".k-header:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
if (footer && footer.length) {
reorder(footer.find(".k-grid-footer-wrap>table>colgroup>col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
reorder(footer.find(".k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
}
rows = that.tbody.children(":not(.k-grouping-row,.k-detail-row)");
for (idx = 0, length = rows.length; idx < length; idx += 1) {
reorder(rows.eq(idx).find(">td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
}
},
cellIndex: function(td) {
return $(td).parent().children('td:not(.k-group-cell,.k-hierarchy-cell)').index(td);
},
_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) {
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);
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] || $(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() {
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 {
//Required for the MVC server wrapper delete button
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 = that.columns[that.cellIndex(cell)],
model = that._modelForContainer(cell);
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,
change: function(e) {
if (that.trigger(SAVE, { values: e.values, container: cell, model: model } )) {
e.preventDefault();
}
}
}).data("kendoEditable");
cell.parent().addClass("k-grid-edit-row");
that.trigger(EDIT, { container: cell, model: model });
}
},
_destroyEditable: function() {
var that = this;
var destroy = function() {
if (that.editable) {
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();
}
}
},
_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,
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 = that.columns[that.cellIndex(cell)];
cell.parent().removeClass("k-grid-edit-row");
that._destroyEditable(); // editable should be destoryed before content of the container is changed
that._displayCell(cell, column, model);
if (cell.hasClass("k-dirty-cell")) {
$(' ').prependTo(cell);
}
},
_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));
},
removeRow: function(row) {
if (!this._confirmation(row)) {
return;
}
this._removeRow(row);
},
_removeRow: function(row) {
var that = this,
model,
mode;
row = $(row).hide();
model = that._modelForContainer(row);
if (model && !that.trigger(REMOVE, { row: row, model: model })) {
mode = that._editMode();
if (mode !== "incell") {
that.cancelRow();
}
that.dataSource.remove(model);
if (mode === "inline" || mode === "popup") {
that.dataSource.sync();
}
}
},
_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 navigatable = that.options.navigatable;
var container;
that.cancelRow();
if (model) {
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 = that.columns[cell.index()];
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;
container.on(CLICK + NS, "a.k-grid-cancel", function(e) {
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.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
focusTable(that.table, true);
}
});
container.on(CLICK + NS, "a.k-grid-update", function(e) {
e.preventDefault();
e.stopPropagation();
that.saveRow();
});
}
},
_createPopupEditor: function(model) {
var that = this,
html = '';
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) {
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.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
focusTable(that.table, true);
}
}
}
}, options));
} else {
html += "";
that.editView = that.pane.append(
''+
''+
html +
'
');
container = that._editContainer = that.editView.element.find(".k-popup-edit-form");
}
that.editable = that._editContainer
.kendoEditable({
fields: fields,
model: model,
clearContainer: false
}).data("kendoEditable");
// TODO: Replace this code with labels and for="ID"
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 = [];
row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() {
cell = $(this);
column = 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 = row
.addClass("k-grid-edit-row")
.kendoEditable({
fields: fields,
model: model,
clearContainer: false
}).data("kendoEditable");
that.trigger(EDIT, { container: row, model: model });
},
cancelRow: function() {
var that = this,
container = that._editContainer,
model;
if (container) {
model = that._modelForContainer(container);
that._destroyEditable();
that.dataSource.cancelChanges(model);
if (that._editMode() !== "popup") {
that._displayRow(container);
} else {
that._displayRow(that.items().filter("[" + 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);
if (model) {
row.replaceWith($((row.hasClass("k-alt") ? that.altRowTemplate : that.rowTemplate)(model)));
}
},
_showMessage: function(messages, row) {
var that = this;
if (!that._isMobile) {
return window.confirm(messages.title);
}
var template = kendo.template('');
var html = $(template(messages)).appendTo(that.view.element);
var actionSheet = new kendo.mobile.ui.ActionSheet(html, {
cancel: messages.cancelDelete,
cancelTemplate: '#:cancel# ',
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 ? DELETECONFIRM : editable.confirmation;
if (confirmation !== false && confirmation != null) {
return that._showMessage({
confirmDelete: editable.confirmDelete || CONFIRMDELETE,
cancelDelete: editable.cancelDelete || CANCELDELETE,
title: confirmation === true ? DELETECONFIRM : 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,
row = that.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);
}
}
},
_firstEditableColumnIndex: function(container) {
var that = this,
column,
columns = 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) {
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 = $('
')
.html(toolbar({}))
.prependTo(wrapper);
}
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(); });
}
}
},
_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: "" };
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;
}
if (command.attr && isPlainObject(command.attr)) {
command.attr = stringifyAttributes(command.attr);
}
options = extend(true, options, defaultCommands[commandName], command);
} else {
options = extend(true, options, defaultCommands[commandName]);
}
return kendo.template(template)(options);
},
_hasFooters: function() {
var cols = this.columns,
len = cols.length,
j;
for (j = 0; j < len; j++) {
if (cols[j].footerTemplate !== "" || cols[j].groupFooterTemplate !== "") {
return true;
}
}
return false;
},
_groupable: function() {
var that = this;
if (that._groupableClickHandler) {
that.table.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();
};
}
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,
GROUPINGDRAGGABLES = HEADERCELLS + ":visible[" + kendo.attr("field") + "]",
GROUPINGFILTER = HEADERCELLS + "[" + kendo.attr("field") + "]";
if (groupable) {
if(!wrapper.has("div.k-grouping-header")[0]) {
$("
").addClass("k-grouping-header").prependTo(wrapper);
}
if (that.groupable) {
that.groupable.destroy();
}
that.groupable = new Groupable(wrapper, extend({}, groupable, {
draggable: that._draggableInstance,
groupContainer: ">div.k-grouping-header",
dataSource: that.dataSource,
draggableElements: that.content ? ".k-grid-header:first " + GROUPINGDRAGGABLES : "table:first>.k-grid-header " + GROUPINGDRAGGABLES,
filter: that.content ? ".k-grid-header:first " + GROUPINGFILTER : "table:first>.k-grid-header " + GROUPINGFILTER,
allowDrag: that.options.reorderable
}));
}
},
_selectable: function() {
var that = this,
multi,
cell,
notString = [],
selectable = that.options.selectable;
if (selectable) {
multi = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1;
cell = typeof selectable === STRING && selectable.toLowerCase().indexOf("cell") > -1;
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 + ")";
}
that.selectable = new kendo.ui.Selectable(that.table, {
filter: ">" + (cell ? SELECTION_CELL_SELECTOR : "tbody>tr" + notString),
aria: true,
multiple: multi,
change: function() {
that.trigger(CHANGE);
}
});
if (that.options.navigatable) {
that.table.on("keydown" + NS, function(e) {
var current = that.current();
if (e.keyCode === keys.SPACEBAR && e.target == that.table[0] &&
!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(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);
}
});
}
}
},
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();
},
current: function(element) {
var that = this,
scrollable = that.options.scrollable,
current = that._current,
table = that.table.add(that.thead.parent());
if (element !== undefined && element.length) {
if (!current || current[0] !== element[0]) {
if (current) {
current.removeClass(FOCUSED).removeAttr("id");
table.removeAttr("aria-activedescendant");
}
element.attr("id", that._cellId);
that._current = element.addClass(FOCUSED);
table.attr("aria-activedescendant", that._cellId);
if(element.length && scrollable) {
if ($.contains(that.content[0], element[0])) {
that._scrollTo(element.parent()[0], that.content[0]);
}
if (scrollable.virtual) {
that._scrollTo(element[0], that.content.find(">.k-virtual-scrollable-wrap")[0]);
} else {
that._scrollTo(element[0], that.content[0]);
}
}
}
}
return that._current;
},
_removeCurrent: function() {
if (this._current) {
this._current.removeClass(FOCUSED);
this._current = null;
}
},
_scrollTo: function(element, container) {
var elementToLowercase = element.tagName.toLowerCase(),
isHorizontal = elementToLowercase === "td" || elementToLowercase === "th",
elementOffset = element[isHorizontal ? "offsetLeft" : "offsetTop"],
elementOffsetDir = element[isHorizontal ? "offsetWidth" : "offsetHeight"],
containerScroll = container[isHorizontal ? "scrollLeft" : "scrollTop"],
containerOffsetDir = container[isHorizontal ? "clientWidth" : "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[isHorizontal ? "scrollLeft" : "scrollTop"] = result;
},
_navigatable: function() {
var that = this,
currentProxy = proxy(that.current, that),
table = that.table,
headerTable = that.thead.parent(),
dataTable = table,
isRtl = kendo.support.isRtl(that.element);
if (!that.options.navigatable) {
return;
}
if (that.options.scrollable) {
dataTable = table.add(headerTable);
headerTable.attr(TABINDEX, -1);
//required for FF
//that.content.attr("tabindex", -1);
}
headerTable.on("keydown" + NS, function(e) {
if (e.altKey && e.keyCode == keys.DOWN) {
currentProxy().find(".k-grid-filter, .k-header-column-menu").click();
e.stopImmediatePropagation();
}
})
.find("a.k-link").attr("tabIndex", -1);
table
.attr(TABINDEX, math.max(table.attr(TABINDEX) || 0, 0))
.on("mousedown" + NS + " keydown" + NS, ".k-detail-cell", function(e) {
if (e.target !== e.currentTarget) {
e.stopImmediatePropagation();
}
});
dataTable
.on((kendo.support.touch ? "touchstart" + NS : "mousedown" + NS), NAVROW + ">" + NAVCELL, proxy(tableClick, that))
.on("focus" + NS, function() {
if (kendo.support.touch) {
return;
}
var current = currentProxy();
if (current && current.is(":visible")) {
current.addClass(FOCUSED);
} else {
currentProxy($(this).find(FIRSTNAVITEM));
}
if (this == table[0]) {
headerTable.attr(TABINDEX, -1);
table.attr(TABINDEX, 0);
} else {
table.attr(TABINDEX, -1);
headerTable.attr(TABINDEX, 0);
}
})
.on("focusout" + NS, function() {
var current = currentProxy();
if (current) {
current.removeClass(FOCUSED);
}
})
.on("keydown" + NS, function(e) {
var key = e.keyCode,
handled = false,
canHandle = !e.isDefaultPrevented() && !$(e.target).is(":button,a,:input,a>.k-icon"),
pageable = that.options.pageable,
dataSource = that.dataSource,
isInCell = that._editMode() == "incell",
active,
currentIndex,
row,
index,
tableToFocus,
shiftKey = e.shiftKey,
current = currentProxy();
if (current && current.is("th")) {
canHandle = true;
}
if (canHandle && key == keys.UP) {
if (current) {
row = current.parent().prevAll(NAVROW).first();
if (!row[0]) {
tableToFocus = that.thead.parent();
focusTable(tableToFocus, true);
row = tableToFocus.find(NAVROW).first();
}
index = current.index();
current = row.children().eq(index);
if (!current[0] || !current.is(NAVCELL)) {
current = row.children(NAVCELL).first();
}
} else {
current = table.find(FIRSTNAVITEM);
}
handled = true;
currentProxy(current);
} else if (canHandle && key == keys.DOWN) {
if (current) {
row = current.parent().nextAll(NAVROW).first();
if (!row[0] && current.is("th")) {
focusTable(that.tbody.parent());
row = that.tbody.find(NAVROW).first();
}
index = current.index();
current = row.children().eq(index);
if (!current[0] || !current.is(NAVCELL)) {
current = row.children(NAVCELL).first();
}
} else {
current = table.find(FIRSTNAVITEM);
}
handled = true;
currentProxy(current);
} else if (canHandle && key == (isRtl ? keys.RIGHT : keys.LEFT)) {
currentProxy(current ? current.prevAll(DATA_CELL + ":first") : table.find(FIRSTNAVITEM));
handled = true;
} else if (canHandle && key == (isRtl ? keys.LEFT : keys.RIGHT)) {
if (current) {
if (current.next()[0]) {
current = current.nextAll(DATA_CELL + ":first");
}
} else {
current = table.find(FIRSTNAVITEM);
}
handled = true;
currentProxy(current);
} else if (canHandle && pageable && keys.PAGEDOWN == key) {
dataSource.page(dataSource.page() + 1);
handled = true;
} else if (canHandle && pageable && keys.PAGEUP == key) {
dataSource.page(dataSource.page() - 1);
handled = true;
} else if (key == keys.ENTER || keys.F2 == key) {
current = current ? current : table.find(FIRSTNAVITEM);
if (current.is("th")) {
current.find(".k-link").click();
handled = true;
} else if (current.parent().is(".k-master-row,.k-grouping-row")) {
current.parent().find(".k-icon:first").click();
handled = true;
} else {
var focusable = current.find(":kendoFocusable:first");
if (!current.hasClass("k-edit-cell") && focusable[0] && current.hasClass("k-state-focused")) {
focusable.focus();
handled = true;
} else if (that.options.editable && !$(e.target).is(":button,.k-button,textarea")) {
var container = $(e.target).closest("[role=gridcell]");
if (!container[0]) {
container = current;
}
that._handleEditing(container);
handled = true;
}
}
} else if (keys.ESC == key) {
active = activeElement();
if (current && $.contains(current[0], active) && !current.hasClass("k-edit-cell") && !current.parent().hasClass("k-grid-edit-row")) {
focusTable(that.table[0], true);
handled = true;
} else if (that._editContainer && (!current || that._editContainer.has(current[0]) || current[0] === that._editContainer[0])) {
if (isInCell) {
that.closeCell(true);
} else {
currentIndex = that.items().index($(current).parent());
if (active) {
active.blur();
}
that.cancelRow();
if (currentIndex >= 0) {
that.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
}
}
if (browser.msie && browser.version < 9) {
document.body.focus();
}
focusTable(table, true);
handled = true;
}
} else if (keys.TAB == key) {
var cell;
current = $(current);
if (that.options.editable && isInCell) {
cell = $(activeElement()).closest(".k-edit-cell");
if (cell[0] && cell[0] !== current[0]) {
current = cell;
}
}
cell = shiftKey ? current.prevAll(DATA_CELL + ":first") : current.nextAll(":visible:first");
if (!cell.length) {
cell = current.parent()[shiftKey ? "prevAll" : "nextAll"]("tr:not(.k-grouping-row):not(.k-detail-row):visible:first")
.children(DATA_CELL + (shiftKey ? ":last" : ":first"));
}
if (!current.is("th") && cell.length && that.options.editable && isInCell) {
that._handleEditing(current, cell);
handled = true;
}
}
if (handled) {
//prevent browser scrolling
e.preventDefault();
//required in hierarchy
e.stopPropagation();
}
});
},
_handleEditing: function(current, next) {
var that = this,
active = $(activeElement()),
mode = that._editMode(),
isIE = browser.msie,
oldIE = isIE && browser.version < 9,
editContainer = that._editContainer,
focusable,
isEdited;
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.change().triggerHandler("blur");
} else {
active.blur();
if (isIE) {
//IE10 with jQuery 1.9.x does not trigger blur handler
//numeric textbox does trigger change
active.blur();
}
}
}
if (!that.editable) {
focusTable(that.table);
return;
}
if (that.editable.end()) {
if (mode == "incell") {
that.closeCell();
} else {
that.saveRow();
isEdited = true;
}
} else {
if (mode == "incell") {
that.current(editContainer);
} else {
that.current(editContainer.children().filter(DATA_CELL).first());
}
focusable = editContainer.find(":kendoFocusable:first")[0];
if (focusable) {
focusable.focus();
}
return;
}
}
if (next) {
that.current(next);
}
if (oldIE) {
document.body.focus();
}
focusTable(that.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("
").parent();
}
that.wrapper = wrapper.addClass("k-grid k-widget k-secondary");
if (height) {
that.wrapper.css(HEIGHT, height);
table.css(HEIGHT, "auto");
}
that._initMobile();
},
_initMobile: function() {
var options = this.options;
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(
'
'
)
.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";
}
},
_tbody: function() {
var that = this,
table = that.table,
tbody;
tbody = table.find(">tbody");
if (!tbody.length) {
tbody = $(" ").appendTo(table);
}
that.tbody = tbody;
},
_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 = $('').insertBefore(that.table);
}
// workaround for IE issue where scroll is not raised if container is same width as the scrollbar
header.css((isRtl ? "padding-left" : "padding-right"), scrollable.virtual ? scrollbar + 1 : scrollbar);
table = $('');
if (isIE7) {
table.attr("cellspacing", 0);
}
table.append(that.thead);
header.empty().append($('').append(table));
that.content = that.table.parent();
if (that.content.is(".k-virtual-scrollable-wrap")) {
that.content = that.content.parent();
}
if (!that.content.is(".k-grid-content, .k-virtual-scrollable-wrap")) {
that.content = that.table.wrap('
').parent();
}
if (hasVirtualScroll) {
that.virtualScrollable = new VirtualScrollable(that.content, {
dataSource: that.dataSource,
itemHeight: proxy(that._averageRowHeight, that)
});
}
that.scrollables = header.children(".k-grid-header-wrap");
// the footer may exists if rendered from the server
var footer = that.wrapper.find(".k-grid-footer"),
webKitRtlCorrection = (isRtl && browser.webkit) ? scrollbar : 0;
if (footer.length) {
that.scrollables = that.scrollables.add(footer.children(".k-grid-footer-wrap"));
}
if (scrollable.virtual) {
that.content.find(">.k-virtual-scrollable-wrap").bind("scroll" + NS, function () {
that.scrollables.scrollLeft(this.scrollLeft + webKitRtlCorrection);
});
} else {
that.content.bind("scroll" + NS, function () {
that.scrollables.scrollLeft(this.scrollLeft + webKitRtlCorrection);
});
var touchScroller = kendo.touchScroller(that.content);
if (touchScroller && touchScroller.movable) {
touchScroller.movable.bind("change", function(e) {
that.scrollables.scrollLeft(-e.sender.x);
});
}
}
}
},
_setContentWidth: function() {
var that = this,
hiddenDivClass = 'k-grid-content-expander',
hiddenDiv = '
',
resizable = that.resizable,
expander;
if (that.options.scrollable) {
expander = that.table.parent().children('.' + hiddenDivClass);
that._setContentWidthHandler = proxy(that._setContentWidth, that);
if (!that.dataSource.view().length) {
if (!expander[0]) {
expander = $(hiddenDiv).appendTo(that.table.parent());
if (resizable) {
resizable.bind("resize", that._setContentWidthHandler);
}
}
expander.width(that.thead.width());
} else if (expander[0]) {
expander.remove();
if (resizable) {
resizable.unbind("resize", that._setContentWidthHandler);
}
}
}
},
_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) {
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)) { // set content height only if needed
if (height > scrollbar * 2) { // do not set height if proper scrollbar cannot be displayed
that.content.height(height);
} else {
that.content.height(scrollbar * 2 + 1);
}
}
}
},
_averageRowHeight: function() {
var that = this,
rowHeight = that._rowHeight;
if (!that._rowHeight) {
that._rowHeight = rowHeight = that.table.outerHeight() / that.items().length;
that._sum = rowHeight;
that._measures = 1;
}
var currentRowHeight = that.table.outerHeight() / that.items().length;
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,
model = e.model,
row = that.tbody.find("tr[" + kendo.attr("uid") + "=" + model.uid +"]"),
cell,
column,
isAlt = row.hasClass("k-alt"),
tmp,
idx = that.items().index(row),
length;
if (row.children(".k-edit-cell").length && !that.options.rowTemplate) {
row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() {
cell = $(this);
column = that.columns[that.cellIndex(cell)];
if (column.field === e.field) {
if (!cell.hasClass("k-edit-cell")) {
that._displayCell(cell, column, model);
$(' ').prependTo(cell);
} else {
cell.addClass("k-dirty-cell");
}
}
});
} else if (!row.hasClass("k-grid-edit-row")) {
tmp = (isAlt ? that.altRowTemplate : that.rowTemplate)(model);
row.replaceWith(tmp);
tmp = that.items().eq(idx);
for (idx = 0, length = that.columns.length; idx < length; idx++) {
column = that.columns[idx];
if (column.field === e.field) {
cell = tmp.children(":not(.k-group-cell,.k-hierarchy-cell)").eq(idx);
$(' ').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 = $('').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) {
aggregates = !isEmptyObject(aggregates) ? aggregates : buildEmptyAggregatesObject(that.dataSource.aggregate());
html = $(that._wrapFooter(footerTemplate(aggregates)));
if (footer.length) {
var tmp = html;
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);
}
}
} 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
.not(".k-grid-footer-wrap")
.add(footerWrap);
}
if (that._footerWidth) {
footer.find("table").css('width', that._footerWidth);
}
if (footerWrap) {
footerWrap.scrollLeft(that.content.scrollLeft());
}
}
},
_wrapFooter: function(footerRow) {
var that = this,
html = "",
scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;
if (that.options.scrollable) {
html = $('');
that._appendCols(html.find("table"));
html.css((isRtl ? "padding-left" : "padding-right"), scrollbar); // Update inner fix.
return html;
}
return '';
},
_columnMenu: function() {
var that = this,
menu,
columns = that.columns,
column,
options = that.options,
columnMenu = options.columnMenu,
menuOptions,
sortable,
filterable,
isMobile = this._isMobile,
closeCallback = function() {
focusTable(that.thead.parent(), true);
},
initCallback = function(e) {
that.trigger(COLUMNMENUINIT, { field: e.field, container: e.container });
},
cell;
if (columnMenu) {
if (typeof columnMenu == "boolean") {
columnMenu = {};
}
that.thead
.find("th:not(.k-hierarchy-cell,.k-group-cell)")
.each(function (index) {
column = columns[index];
cell = $(this);
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;
filterable = options.filterable && column.filterable !== false && columnMenu.filterable !== false ? extend({}, column.filterable, options.filterable) : false;
menuOptions = {
dataSource: that.dataSource,
values: column.values,
columns: columnMenu.columns,
sortable: sortable,
filterable: filterable,
messages: columnMenu.messages,
owner: that,
closeCallback: closeCallback,
init: initCallback,
filter: isMobile ? ":not(.k-column-active)" : ""
};
cell.kendoColumnMenu(menuOptions);
}
});
}
},
_filterable: function() {
var that = this,
columns = that.columns,
cell,
filterMenu,
closeCallback = function() {
focusTable(that.thead.parent(), true);
},
filterable = that.options.filterable;
if (filterable && !that.options.columnMenu) {
that.thead
.find("th:not(.k-hierarchy-cell,.k-group-cell)")
.each(function(index) {
cell = $(this);
if (columns[index].filterable !== false && !columns[index].command && (columns[index].field || cell.attr("data-" + kendo.ns + "field"))) {
filterMenu = cell.data("kendoFilterMenu");
if (filterMenu) {
filterMenu.destroy();
}
var columnFilterable = columns[index].filterable;
var options = extend({},
filterable,
columnFilterable,
{
dataSource: that.dataSource,
values: columns[index].values,
closeCallback: closeCallback,
init: function(e) {
that.trigger(FILTERMENUINIT, { field: e.field, container: e.container });
}
}
);
if (columnFilterable && columnFilterable.messages) {
options.messages = extend(true, {}, filterable.messages, columnFilterable.messages);
}
cell.kendoFilterMenu(options);
}
});
}
},
_sortable: function() {
var that = this,
columns = that.columns,
column,
cell,
sortableInstance,
sortable = that.options.sortable;
if (sortable) {
that.thead
.find("th:not(.k-hierarchy-cell,.k-group-cell)")
.each(function(index) {
column = columns[index];
if (column.sortable !== false && !column.command && column.field) {
cell = $(this);
sortableInstance = cell.data("kendoSortable");
if (sortableInstance) {
sortableInstance.destroy();
}
cell.attr("data-" + kendo.ns +"field", column.field)
.kendoSortable(extend({}, sortable, column.sortable, { dataSource: that.dataSource, aria: true, filter: ":not(.k-column-active)" }));
}
});
}
},
_columns: function(columns) {
var that = this,
table = that.table,
encoded,
cols = table.find("col"),
dataSource = that.options.dataSource;
// using HTML5 data attributes as a configuration option e.g. Foo
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")),
menu = th.attr(kendo.attr("menu"));
if (!field) {
field = th.text().replace(/\s|[^A-z0-9]/g, "");
}
return {
field: field,
type: type,
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));
that.columns = map(columns, function(column) {
column = typeof column === STRING ? { field: column } : column;
if (column.hidden) {
column.attributes = addHiddenStyle(column.attributes);
column.footerAttributes = addHiddenStyle(column.footerAttributes);
column.headerAttributes = addHiddenStyle(column.headerAttributes);
}
return extend({ encoded: encoded }, column);
});
},
_groups: function() {
var group = this.dataSource.group();
return group ? group.length : 0;
},
_tmpl: function(rowTemplate, alt) {
var that = this,
settings = extend({}, kendo.Template, that.options.templateSettings),
idx,
length = that.columns.length,
template,
state = { storage: {}, count: 0 },
column,
type,
hasDetails = that._hasDetails(),
className = [],
groups = that._groups();
if (!rowTemplate) {
rowTemplate = " ";
if (groups > 0) {
rowTemplate += groupCells(groups);
}
if (hasDetails) {
rowTemplate += ' ';
}
for (idx = 0; idx < length; idx++) {
column = that.columns[idx];
template = column.template;
type = typeof template;
rowTemplate += "";
rowTemplate += that._cellTmpl(column, state);
rowTemplate += " ";
}
rowTemplate += " ";
}
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)) + "#";
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();
that.rowTemplate = that._tmpl(options.rowTemplate);
that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, true);
if (that._hasDetails()) {
that.detailTemplate = that._detailTmpl(options.detailTemplate || "");
}
if ((that._group && !isEmptyObject(aggregates)) || (!isEmptyObject(aggregates) && !footer.length) ||
grep(that.columns, function(column) { return column.footerTemplate; }).length) {
that.footerTemplate = that._footerTmpl(aggregates, "footerTemplate", "k-footer-template");
}
if (groups && grep(that.columns, function(column) { return column.groupFooterTemplate; }).length) {
aggregates = $.map(groups, function(g) { return g.aggregates; });
that.groupFooterTemplate = that._footerTmpl(aggregates, "groupFooterTemplate", "k-group-footer");
}
},
_footerTmpl: function(aggregates, templateName, rowClass) {
var that = this,
settings = extend({}, kendo.Template, that.options.templateSettings),
paramName = settings.paramName,
html = "",
idx,
length,
columns = that.columns,
template,
type,
storage = {},
count = 0,
scope = {},
groups = that._groups(),
fieldsMap = buildEmptyAggregatesObject(aggregates),
column;
html += '';
if (groups > 0) {
html += groupCells(groups);
}
if (that._hasDetails()) {
html += ' ';
}
for (idx = 0, length = that.columns.length; idx < length; idx++) {
column = columns[idx];
template = column[templateName];
type = typeof template;
html += "";
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 += " ";
}
html += ' ';
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(that.columns).length,
type = typeof template;
html += '';
if (groups > 0) {
html += groupCells(groups);
}
html += '";
if (type === FUNCTION) {
templateFunctionStorage["tmpl" + templateFunctionCount] = template;
html += "#=this.tmpl" + templateFunctionCount + "(" + paramName + ")#";
templateFunctionCount ++;
} else {
html += template;
}
html += " ";
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;
},
_details: function() {
var that = this;
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);
if(hasDetails && !masterRow.next().hasClass("k-detail-row")) {
data = that.dataItem(masterRow);
$(detailTemplate(data))
.addClass(masterRow.hasClass("k-alt") ? "k-alt" : "")
.insertAfter(masterRow);
that.trigger(DETAILINIT, { masterRow: masterRow, detailRow: masterRow.next(), data: data, detailCell: masterRow.next().find(".k-detail-cell") });
}
detailRow = masterRow.next();
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();
},
_thead: function() {
var that = this,
columns = that.columns,
hasDetails = that._hasDetails() && columns.length,
idx,
length,
html = "",
thead = that.table.find(">thead"),
tr,
text,
th;
if (!thead.length) {
thead = $(" ").insertBefore(that.tbody);
}
tr = that.element.find("tr:has(th):first");
if (!tr.length) {
tr = thead.children().first();
if (!tr.length) {
tr = $(" ");
}
}
if (!tr.children().length) {
if (hasDetails) {
html += ' ';
}
for (idx = 0, length = columns.length; idx < length; idx++) {
th = columns[idx];
text = that._headerCellText(th);
if (!th.command) {
html += "" + text + " ";
} else {
html += "" + text + " ";
}
}
tr.html(html);
} else if (hasDetails && !tr.find(".k-hierarchy-cell")[0]) {
tr.prepend(' ');
}
tr.find("th").addClass("k-header");
if(!that.options.scrollable) {
thead.addClass("k-grid-header");
}
tr.find("script").remove().end().appendTo(thead);
if (that.thead) {
that._destroyColumnAttachments();
}
that.thead = thead;
that._sortable();
that._filterable();
that._scrollable();
that._updateCols();
that._resizable();
that._draggable();
that._reorderable();
if (that.groupable) {
that._attachGroupable();
}
that._columnMenu();
},
_updateCols: function() {
var that = this;
that._appendCols(that.thead.parent().add(that.table));
},
_appendCols: function(table) {
var that = this;
normalizeCols(table, visibleColumns(that.columns), that._hasDetails(), that._groups());
},
_autoColumns: function(schema) {
if (schema && schema.toJSON) {
var that = this,
field;
schema = schema.toJSON();
for (field in schema) {
that.columns.push({ field: field });
}
that._thead();
that._templates();
}
},
_rowsHtml: function(data) {
var that = this,
html = "",
idx,
length,
rowTemplate = that.rowTemplate,
altRowTemplate = that.altRowTemplate;
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) {
var that = this,
html = "",
idx,
length,
field = group.field,
column = grep(that.columns, function(column) { return column.field == field; })[0] || { },
template = column.groupHeaderTemplate,
text = (column.title || field) + ': ' + formatGroupValue(group.value, column.format, column.values),
data = extend({}, { field: group.field, value: group.value }, group.aggregates[group.field]),
footerDefaults = that._groupAggregatesDefaultObject || {},
groupItems = group.items;
if (template) {
text = typeof template === FUNCTION ? template(data) : kendo.template(template)(data);
}
html += '' + groupCells(level) +
'' +
'' +
' ' + text +
'
';
if(group.hasSubgroups) {
for(idx = 0, length = groupItems.length; idx < length; idx++) {
html += that._groupRowHtml(groupItems[idx], colspan - 1, level + 1);
}
} else {
html += that._rowsHtml(groupItems);
}
if (that.groupFooterTemplate) {
html += that.groupFooterTemplate(extend(footerDefaults, group.aggregates));
}
return html;
},
collapseGroup: function(group) {
group = $(group).find(".k-icon").addClass("k-i-expand").removeClass("k-i-collapse").end();
var level = group.find(".k-group-cell").length,
footerCount = 1,
offset,
tr;
group.find("td:first").attr("aria-expanded", false);
group.nextAll("tr").each(function() {
tr = $(this);
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)) {
return false;
}
tr.hide();
});
},
expandGroup: function(group) {
group = $(group).find(".k-icon").addClass("k-i-collapse").removeClass("k-i-expand").end();
var that = this,
level = group.find(".k-group-cell").length,
tr,
offset,
groupsCount = 1;
group.find("td:first").attr("aria-expanded", true);
group.nextAll("tr").each(function () {
tr = $(this);
offset = tr.find(".k-group-cell").length;
if (offset <= level) {
return false;
}
if (offset == level + 1 && !tr.hasClass("k-detail-row")) {
tr.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();
}
}
if (tr.hasClass("k-grouping-row")) {
groupsCount ++;
}
if (tr.hasClass("k-group-footer")) {
if (groupsCount == 1) {
tr.show();
} else {
groupsCount --;
}
}
});
},
_updateHeader: function(groups) {
var that = this,
cells = that.thead.find("th.k-group-cell"),
length = cells.length;
if(groups > length) {
$(new Array(groups - length + 1).join('')).prependTo(that.thead.find("tr"));
} else if(groups < length) {
length = length - groups;
$(grep(cells, function(item, index) { return length > index; } )).remove();
}
},
_firstDataItem: function(data, grouped) {
if(data && grouped) {
if(data.hasSubgroups) {
data = this._firstDataItem(data.items[0], grouped);
} else {
data = data.items[0];
}
}
return data;
},
hideColumn: function(column) {
var that = this,
rows,
row,
cell,
tables,
idx,
cols,
colWidth,
width = 0,
length,
footer = that.footer || that.wrapper.find(".k-grid-footer"),
columns = that.columns,
columnIndex;
if (typeof column == "number") {
column = columns[column];
} else {
column = grep(columns, function(item) {
return item.field === column;
})[0];
}
if (!column || column.hidden) {
return;
}
columnIndex = inArray(column, visibleColumns(columns));
column.hidden = true;
column.attributes = addHiddenStyle(column.attributes);
column.footerAttributes = addHiddenStyle(column.footerAttributes);
column.headerAttributes = addHiddenStyle(column.headerAttributes);
that._templates();
that._updateCols();
setCellVisibility(that.thead.find(">tr")[0].cells, columnIndex, false);
if (footer[0]) {
that._appendCols(footer.find("table:first"));
setCellVisibility(footer.find(".k-footer-template")[0].cells, columnIndex, false);
}
rows = that.tbody.children();
for (idx = 0, length = rows.length; 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);
}
}
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.width(width);
that._footerWidth = width;
}
if(browser.msie && browser.version == 8) {
tables.css("display", "inline-table");
setTimeout(function() {
tables.css("display", "table");
}, 1);
}
that.trigger(COLUMNHIDE, { column: column });
},
showColumn: function(column) {
var that = this,
rows,
idx,
length,
row,
cell,
tables,
width,
colWidth,
cols,
columns = that.columns,
footer = that.footer || that.wrapper.find(".k-grid-footer"),
columnIndex;
if (typeof column == "number") {
column = columns[column];
} else {
column = grep(columns, function(item) {
return item.field === column;
})[0];
}
if (!column || !column.hidden) {
return;
}
columnIndex = inArray(column, columns);
column.hidden = false;
column.attributes = removeHiddenStyle(column.attributes);
column.footerAttributes = removeHiddenStyle(column.footerAttributes);
column.headerAttributes = removeHiddenStyle(column.headerAttributes);
that._templates();
that._updateCols();
setCellVisibility(that.thead.find(">tr")[0].cells, columnIndex, true);
if (footer[0]) {
that._appendCols(footer.find("table:first"));
setCellVisibility(footer.find(".k-footer-template")[0].cells, columnIndex, true);
}
rows = that.tbody.children();
for (idx = 0, length = rows.length; 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);
}
}
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.width(width);
that._footerWidth = width;
}
}
that.trigger(COLUMNSHOW, { column: column });
},
_progress: function(toggle) {
var that = this,
element = that.element.is("table") ? that.element.parent() : (that.content && that.content.length ? that.content : that.element);
kendo.ui.progress(element, toggle);
},
refresh: function(e) {
var that = this,
length,
idx,
html = "",
data = that.dataSource.view(),
navigatable = that.options.navigatable,
tbody,
placeholder,
currentIndex,
current = $(that.current()),
isCurrentInHeader = false,
groups = (that.dataSource.group() || []).length,
colspan = groups + visibleColumns(that.columns).length,
active;
if (e && e.action === "itemchange" && that.editable) { // skip rebinding if editing is in progress
return;
}
e = e || {};
if (that.trigger("dataBinding", { action: e.action || "rebind", index: e.index, items: e.items })) {
return;
}
active = activeElement();
if (navigatable && (that.table[0] === active || $.contains(that.table[0], active) || (that._editContainer && that._editContainer.data("kendoWindow")))) {
isCurrentInHeader = current.is("th");
currentIndex = 0;
if (isCurrentInHeader) {
currentIndex = that.thead.find("th:not(.k-group-cell)").index(current);
}
}
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._updateHeader(groups);
that._group = groups > 0;
}
if(groups > 0) {
if (that.detailTemplate) {
colspan++;
}
if (that.groupFooterTemplate) {
that._groupAggregatesDefaultObject = buildEmptyAggregatesObject(that.dataSource.aggregate());
}
for (idx = 0, length = data.length; idx < length; idx++) {
html += that._groupRowHtml(data[idx], colspan, 0);
}
} else {
html += that._rowsHtml(data);
}
if (tbodySupportsInnerHtml) {
that.tbody[0].innerHTML = html;
} else {
placeholder = document.createElement("div");
placeholder.innerHTML = "";
tbody = placeholder.firstChild.firstChild;
that.table[0].replaceChild(tbody, that.tbody[0]);
that.tbody = $(tbody);
}
that._footer();
that._setContentHeight();
that._setContentWidth();
if (currentIndex >= 0) {
that._removeCurrent();
if (!isCurrentInHeader) {
that.current(that.items().eq(currentIndex).children().filter(DATA_CELL).first());
} else {
that.current(that.thead.find("th:not(.k-group-cell)").eq(currentIndex));
}
if (that._current) {
focusTable(that._current.closest("table")[0], true);
}
}
that.trigger(DATABOUND);
}
});
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;
if (direct === true) {
table = $(table);
var condition = msie && table.parent().is(".k-grid-content,.k-grid-header-wrap"),
scrollTop, scrollLeft;
if (condition) {
scrollTop = table.parent().scrollTop();
scrollLeft = table.parent().scrollLeft();
}
if (msie) {
try {
//The setActive method does not cause the document to scroll to the active object in the current page
table[0].setActive();
} catch(e) {
table[0].focus();
}
} else {
table[0].focus(); //because preventDefault bellow, IE cannot focus the table alternative is unselectable=on
}
if (condition) {
table.parent().scrollTop(scrollTop);
table.parent().scrollLeft(scrollLeft);
}
} else {
$(table).one("focusin", function(e) { e.preventDefault(); }).focus();
}
}
function tableClick(e) {
var currentTarget = $(e.currentTarget),
isHeader = currentTarget.is("th"),
currentTable = currentTarget.closest("table")[0];
if (kendo.support.touch) {
return;
}
if (currentTable !== this.table[0] && currentTable !== this.thead.parent()[0]) {
return;
}
this.current(currentTarget);
if (isHeader || !$(e.target).is(":button,a,:input,a>.k-icon,textarea,span.k-icon,span.k-link,.k-input,.k-multiselect-wrap")) {
setTimeout(function() {
//Do not focus if widget, because in IE8 a DDL will be closed
if (!(isIE8 && $(kendo._activeElement()).hasClass("k-widget"))) {
//DOMElement.focus() only for header, because IE doesn't really focus the table
focusTable(currentTable, true);
}
});
}
if (isHeader) {
e.preventDefault(); //if any problem occurs, call preventDefault only for the clicked header links
}
}
ui.plugin(Grid);
ui.plugin(VirtualScrollable);
})(window.kendo.jQuery);
kendo_module({
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 = ">*",
PROGRESS = "progress",
ERROR = "error",
FOCUSED = "k-state-focused",
SELECTED = "k-state-selected",
KEDITITEM = "k-edit-item",
STRING = "string",
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 = Widget.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();
},
_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();
},
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();
item.replaceWith(template(data));
item = that.items().eq(idx);
item.attr(kendo.attr("uid"), data.uid);
that.trigger("itemChange", {
item: item,
data: data
});
}
}
return;
}
if (that.trigger(DATABINDING, { action: e.action || "rebind", items: e.items, index: e.index })) {
return;
}
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.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 = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1;
if (multi) {
that.element.attr("aria-multiselectable", true);
}
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"),
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(validate) {
var that = this,
editable = that.editable,
data,
index,
template = that.template,
valid = true;
if (editable) {
if (validate) {
valid = editable.end();
}
if (valid) {
if (editable.element.index() % 2) {
template = that.altTemplate;
}
data = that._modelFromElement(editable.element);
that._destroyEditable();
index = editable.element.index();
editable.element.replaceWith(template(data));
that.items().eq(index).attr(kendo.attr("uid"), data.uid);
}
}
return valid;
},
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 }).data("kendoEditable");
that.trigger(EDIT, { model: data, item: container });
},
save: function() {
var that = this,
editable = that.editable,
model;
if (!editable) {
return;
}
editable = editable.element;
model = that._modelFromElement(editable);
if (!that.trigger(SAVE, { model: model, item: editable }) && that._closeEditable(true)) {
that.dataSource.sync();
}
},
remove: function(item) {
var that = this,
dataSource = that.dataSource,
data = that._modelFromElement(item);
if (!that.trigger(REMOVE, { model: data, item: item })) {
item.hide();
dataSource.remove(data);
dataSource.sync();
}
},
add: function() {
var that = this,
dataSource = that.dataSource,
index = dataSource.indexOf((dataSource.view() || [])[0]);
if (index < 0) {
index = 0;
}
that.cancel();
dataSource.insert(index, {});
that.edit(that.element.children().first());
},
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(false);
}
}
},
_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();
}
if (that.selectable) {
that.selectable.destroy();
}
kendo.destroy(that.element);
}
});
kendo.ui.plugin(ListView);
})(window.kendo.jQuery);
kendo_module({
id: "imagebrowser",
name: "ImageBrowser",
category: "web",
description: "",
hidden: true,
depends: [ "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 = ".kendoImageBrowser",
BREADCRUBMSNS = ".kendoBreadcrumbs",
SEARCHBOXNS = ".kendoSearchBox",
NAMEFIELD = "name",
SIZEFIELD = "size",
TYPEFIELD = "type",
DEFAULTSORTORDER = { field: TYPEFIELD, dir: "asc" },
EMPTYTILE = kendo.template('${text} '),
TOOLBARTMPL = '';
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() {
//updates are handled by the upload
},
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 ImageBrowser = Widget.extend({
init: function(element, options) {
var that = this;
options = options || {};
Widget.fn.init.call(that, element, options);
that.element.addClass("k-imagebrowser k-secondary");
that.element
.on(CLICK + NS, ".k-toolbar button:not(.k-state-disabled):has(.k-delete)", proxy(that._deleteClick, that))
.on(CLICK + NS, ".k-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: "ImageBrowser",
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: "*.png,*.gif,*.jpg,*.jpeg"
},
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);
if (that.arrangeBy) {
that.arrangeBy.destroy();
}
kendo.destroy(that.element);
},
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;
}
},
_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-imagebrowser-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-imagebrowser-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]");
that._scroll();
});
}
} 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 && !that._showMessage(kendo.format(that.options.messages.overwriteFile, fileName), "confirm")) {
return null;
}
if (file) {
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 = $('')
.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)
});
},
_dblClick: function(e) {
var that = this,
li = $(e.currentTarget);
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 || "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);
},
_navigation: function() {
var that = this,
navigation = $('
')
.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();
},
_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 = $(" ", { alt: name }),
urlJoin = "?";
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=" + that.path() + encodeURIComponent(name);
}
// IE8 will trigger the load event immediately when the src is assigned
// if the image is loaded from the cache
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);
}
},
_editTmpl: function() {
var html = '';
html += '#if(' + TYPEFIELD + ' == "d") { #';
html += '
';
html += "#}else{#";
html += '
';
html += "#}#";
html += '#if(' + TYPEFIELD + ' == "d") { #';
html += ' ';
html += "#}#";
html += ' ';
return proxy(kendo.template(html), { sizeFormatter: sizeFormatter } );
},
_itemTmpl: function() {
var that = this,
html = '';
html += '#if(' + TYPEFIELD + ' == "d") { #';
html += '
';
html += "#}else{#";
if (that.options.transport && that.options.transport.thumbnailUrl) {
html += '
';
} else {
html += '
';
}
html += "#}#";
html += '${' + NAMEFIELD + '} ';
html += '#if(' + TYPEFIELD + ' == "f") { # ${this.sizeFormatter(' + SIZEFIELD + ')} #}#';
html += ' ';
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($('
')).parent();
if (!placeholderSupported) {
$('' + this.options.label + ' ').insertBefore(element);
}
$(' ').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($('
')).parent();
}
overlay = wrapper.find(".k-breadcrumbs-wrap");
if (!overlay.length) {
overlay = $('
').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 += 'root ';
}
html += '' + segments[idx] + ' ';
html += '> ';
}
}
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(ImageBrowser);
kendo.ui.plugin(Breadcrumbs);
kendo.ui.plugin(SearchBox);
})(window.kendo.jQuery);
kendo_module({
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" ]
} ]
});
(function($,undefined) {
// Imports ================================================================
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,
NS = ".kendoEditor",
keys = kendo.keys;
// options can be: template (as string), cssClass, title, defaultValue
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:
'',
buttonTemplate:
'#= data.title # ',
colorPickerTemplate:
'
',
comboBoxTemplate:
' ',
dropDownListTemplate:
' ',
separatorTemplate:
' ',
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",
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",
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",
linkWebAddress: "Web address",
linkText: "Text",
linkToolTip: "ToolTip",
linkOpenInNewWindow: "Open link in new window",
dialogUpdate: "Update",
dialogInsert: "Insert",
dialogButtonSeparator: "or",
dialogCancel: "Cancel",
createTable: "Create 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 = editorNS.Dom.name(element);
/* suppress initialization in mobile webkit devices (w/o proper contenteditable support) */
if (!supportedBrowser) {
return;
}
Widget.fn.init.call(that, element, options);
that.options = deepExtend({}, that.options, options);
element = that.element;
element.closest("form").on("submit" + NS, function () {
that.update();
});
toolbarOptions = extend({}, that.options);
toolbarOptions.editor = that;
if (type == "textarea") {
that._wrapTextarea();
toolbarContainer = that.wrapper.find(".k-editor-toolbar");
if (element[0].id) {
toolbarContainer.attr("aria-controls", element[0].id);
}
} else {
that.element.addClass("k-widget k-editor k-editor-inline");
toolbarOptions.popup = true;
toolbarContainer = $('').insertBefore(element);
}
that.toolbar = new editorNS.Toolbar(toolbarContainer[0], toolbarOptions);
that.toolbar.bindTo(that);
that._initializeContentElement(that);
that.keyboard = new editorNS.Keyboard([
new editorNS.TypingHandler(that),
new editorNS.SystemHandler(that)
]);
that.clipboard = new editorNS.Clipboard(this);
that.undoRedoStack = new editorNS.UndoRedoStack();
if (options && options.value) {
value = options.value;
} else if (that.textarea) {
// indented HTML introduces problematic ranges in IE
value = element.val().replace(/[\r\n\v\f\t ]+/ig, " ");
} else {
value = element[0].innerHTML;
}
that.value(value);
$(document)
.on("mousedown", proxy(that._endTyping, that))
.on("mouseup", proxy(that._mouseup, that));
kendo.notify(that);
},
_endTyping: function() {
var keyboard = this.keyboard;
try {
if (keyboard.isTypingInProgress()) {
keyboard.endTyping(true);
this.saveSelection();
}
} catch (e) { }
},
_selectionChange: function() {
this._selectionStarted = false;
this.saveSelection();
this.trigger("select", {});
},
_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 domain = document.domain;
var src = 'javascript:""';
if (domain != location.hostname) {
// relax same-origin policy
src = "javascript:document.write(" +
"''" +
")";
}
textarea.hide();
iframe = $("", { frameBorder: "0" })[0];
$(iframe)
.css("display", "")
.addClass("k-content")
.insertBefore(textarea);
iframe.src = src;
wnd = iframe.contentWindow || iframe;
doc = wnd.document || iframe.contentDocument;
$(iframe).one("load", function() {
var styleTools = editor.toolbar.items().filter(".k-decorated");
styleTools.kendoSelectBox("decorate", doc);
});
doc.open();
doc.write(
"" +
" " +
"" +
"" +
$.map(stylesheets, function(href){
return " ";
}).join("") +
""
);
doc.close();
return wnd;
},
_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;
$(doc).on("mouseup" + NS, proxy(editor._mouseup, editor));
} else {
editor.window = window;
doc = editor.document = document;
editor.body = editor.element[0];
blurTrigger = editor.body;
var styleTools = editor.toolbar.items().filter(".k-decorated");
styleTools.kendoSelectBox("decorate", doc);
}
$(blurTrigger)
.on("blur" + NS, function () {
var old = editor.textarea ? editor.textarea.val() : editor._oldValue;
var value = editor.options.encoded ? editor.encodedValue() : editor.value();
editor.update();
if (value != old) {
editor.trigger("change");
}
});
try {
doc.execCommand("enableInlineTableEditing", null, false);
} catch(e) { }
if (kendo.support.touch) {
$(doc).on("selectionchange" + NS, function() {
editor._selectionChange();
});
}
$(editor.body)
.on("keydown" + NS, function (e) {
var range;
if (e.keyCode === keys.F10) {
// Handling with timeout to avoid the default IE menu
setTimeout(proxy(editor.toolbar.focus, editor.toolbar), 100);
e.preventDefault();
return;
} else if (e.keyCode === keys.BACKSPACE) {
range = editor.getRange();
var ancestor,
emptyParagraphContent = browser.msie ? '' : ' ',
dom = kendo.ui.editor.Dom;
range.deleteContents();
ancestor = range.commonAncestorContainer;
if (dom.name(ancestor) === "p" && ancestor.innerHTML === "") {
ancestor.innerHTML = emptyParagraphContent;
range.setStart(ancestor, 0);
range.collapse(true);
editor.selectRange(range);
}
} else if (e.keyCode == keys.LEFT || e.keyCode == keys.RIGHT) {
// skip bom nodes when navigating with arrows
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);
})
.on("keyup" + NS, 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);
})
.on("mousedown" + NS, function(e) {
editor._selectionStarted = true;
var target = $(e.target);
if (!browser.gecko && e.which == 2 && target.is("a[href]")) {
window.open(target.attr("href"), "_new");
}
})
.on("click" + NS, 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);
}
})
.on("cut" + NS + " paste" + NS, function (e) {
editor.clipboard["on" + e.type](e);
})
.on("focusin" + NS, function() {
$(this).addClass("k-state-active");
editor.toolbar.show();
})
.on("focusout" + NS, function() {
setTimeout(function() {
var active = kendo._activeElement();
var body = editor.body;
var toolbar = editor.toolbar;
if (active != body && !$.contains(body, active) && !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) {
// preserve updated value before re-initializing
// don't use update() to prevent the editor from encoding the content too early
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,
stylesheets: [],
dialogOptions: {
modal: true, resizable: false, draggable: true,
animation: false
},
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() {
var that = this;
Widget.fn.destroy.call(that);
$(that.window)
.add(that.document)
.add(that.body)
.add(that.wrapper)
.add(that.element.closest("form"))
.off(NS);
$(document).off("mousedown", proxy(that._endTyping, that))
.off("mouseup", proxy(that._mouseup, that));
that.toolbar.destroy();
kendo.destroy(that.wrapper);
},
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,
dom = editorNS.Dom,
currentHtml = editorNS.Serializer.domToXhtml(body);
if (html === undefined) {
return currentHtml;
}
if (html == currentHtml) {
return;
}
var onerrorRe = /onerror\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/i;
// handle null value passed as a parameter
html = (html || "")
// Some browsers do not allow setting CDATA sections through innerHTML so we encode them
.replace(//g, "")
// Encode script tags to avoid execution and lost content (IE)
.replace(/'); } },
b: { start: function () { result.push(''); }, end: function () { result.push(' '); } },
i: { start: function () { result.push(''); }, end: function () { result.push(' '); } },
u: { start: function () { result.push(''); }, end: function () { result.push(' '); } },
iframe: { start: function (node) { result.push(''); } },
font: {
start: function (node) {
result.push('');
},
end: function () {
result.push(' ');
}
}
};
function styleAttr(cssText) {
// In IE < 8 the style attribute does not return proper nodeValue
var trim = $.trim;
var css = trim(cssText).split(';');
var i, length = css.length;
var match;
var property, value;
for (i = 0, length = css.length; i < length; i++) {
if (!css[i].length) {
continue;
}
match = cssDeclaration.exec(css[i]);
// IE8 does not provide a value for 'inherit'
if (!match) {
continue;
}
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, "");
}
result.push(property);
result.push(':');
result.push(value);
result.push(';');
}
}
function attr(node) {
var specifiedAttributes = [];
var attributes = node.attributes;
var attribute, i, l;
var name, value, specified;
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 });
}
}
for (i = 0, l = attributes.length; i < l; i++) {
attribute = attributes[i];
name = attribute.nodeName;
value = attribute.nodeValue;
specified = attribute.specified;
// In IE < 8 the 'value' attribute is not returned as 'specified'. The same goes for type="text"
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.indexOf('_moz') >= 0) {
specified = false;
}
if (specified) {
specifiedAttributes.push(attribute);
}
}
if (!specifiedAttributes.length) {
return;
}
specifiedAttributes.sort(function (a, b) {
return a.nodeName > b.nodeName ? 1 : a.nodeName < b.nodeName ? -1 : 0;
});
for (i = 0, l = specifiedAttributes.length; i < l; i++) {
attribute = specifiedAttributes[i];
name = attribute.nodeName;
value = attribute.nodeValue;
if (name.toLowerCase() == "contenteditable" && (dom.is(node, "table") || dom.is(node, "td"))) {
continue;
}
if (name == "class" && value == "k-table") {
continue;
}
result.push(' ');
result.push(name);
result.push('="');
if (name == 'style') {
styleAttr(value || node.style.cssText);
} else if (name == 'src' || name == 'href') {
result.push(node.getAttribute(name, 2));
} else {
result.push(dom.fillAttrs[name] ? name : value);
}
result.push('"');
}
}
function children(node, skip) {
for (var childNode = node.firstChild; childNode; childNode = childNode.nextSibling) {
child(childNode, skip);
}
}
function text(node) {
return node.nodeValue.replace(/\ufeff/g, "");
}
function child(node, skip) {
var nodeType = node.nodeType,
tagName, mapper,
parent, value, previous;
if (nodeType == 1) {
tagName = dom.name(node);
if (!tagName || ((node.attributes._moz_dirty || node.attributes._moz_editor_bogus_node) && dom.is(node, 'br')) || node.className == "k-marker") {
return;
}
if (dom.isInline(node) && node.childNodes.length == 1 && node.firstChild.nodeType == 3&& !text(node.firstChild)) {
return;
}
mapper = tagMap[tagName];
if (mapper) {
mapper.start(node);
children(node);
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 && $.support.leadingWhitespace) {
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(dom.encode(value));
} else if (nodeType == 4) {
result.push('');
} else if (nodeType == 8) {
if (node.data.indexOf('[CDATA[') < 0) {
result.push('');
} else {
result.push('');
}
}
}
if (root.childNodes.length == 1 && root.firstChild.nodeType == 3) {
return dom.encode(text(root.firstChild).replace(/[\r\n\v\f\t ]+/, ' '));
}
children(root);
result = result.join('');
// if serialized dom contains only whitespace elements, consider it empty (required field validation)
if (result.replace(brRe, "").replace(emptyPRe, "") === "") {
return "";
}
return result;
}
};
extend(Editor, {
Serializer: Serializer
});
})(window.kendo.jQuery);
(function($) {
// Imports ================================================================
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, /* not part of the spec; used when cloning ranges, traversing the dom and creating fragments */
startContainer: doc,
endContainer: doc,
commonAncestorContainer: doc,
startOffset: 0,
endOffset: 0,
collapsed: true
});
},
// Positioning Methods
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);
}
},
// Editing Methods
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 () {
// clone subtree
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 () {
// fast copy
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 /* not part of the spec; used to update the original range when calling extractContents() on clones */
});
},
// used for debug purposes
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('');
}
});
/* can be used in Range.compareBoundaryPoints if we need it one day */
function compareBoundaries(start, end, startOffset, endOffset) {
if (start == end) {
return endOffset - startOffset;
}
// end is child of start
var container = end;
while (container && container.parentNode != start) {
container = container.parentNode;
}
if (container) {
return findNodeIndex(container) - startOffset;
}
// start is child of end
container = start;
while (container && container.parentNode != end) {
container = container.parentNode;
}
if (container) {
return endOffset - findNodeIndex(container) - 1;
}
// deep traversal
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) {
// range was initially invalid (e.g. when cloned from invalid range) - it must be fixed
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();
// end container should be adopted first in order to prevent selection with negative length
adoptContainer(textRange, range, false);
adoptContainer(textRange, range, true);
textRange.select();
},
removeAllRanges: function () {
this.ownerDocument.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) && // check for full body selection
!(startContainer == endContainer && isDataNode(startContainer) && startContainer.parentNode == body)) { // but not when single textnode is selected
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) {
// find anchor node and offset
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;
// visible data nodes need a text offset
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 {
// create a cursor element node to position range (since we can't select text nodes)
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;
cursorNode.innerHTML = "\ufeff";
cursor.collapse(start);
parent = cursor.parentElement();
if (!dom.isAncestorOrSelf(commonAncestor, parent)) {
parent = commonAncestor;
}
// detect range end points
// insert cursorNode within the textRange parent and move the cursor until it gets outside of the textRange
do {
parent.insertBefore(cursorNode, cursorNode.previousSibling);
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.isWhitespace(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) {
var that = this;
that.range = range;
that.rootNode = RangeUtils.documentFromRange(range);
that.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.nodeType == 3 && node.nodeValue.length < offset) {
offset -= node.nodeValue.length;
node = node.nextSibling;
}
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 = ' ';
}
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);
}
range.setStartBefore(that.start);
range.setEndAfter(that.end);
normalize(range.commonAncestorContainer);
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);
}
},
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;
}
// merely accessing the siblings will solve range issues in IE
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;
// collapsed range
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 startIndex = findNodeIndex(start), startParent = start.parentNode;
var endIndex = findNodeIndex(end), endParent = end.parentNode;
for (var startPointer = start; startPointer.previousSibling; startPointer = startPointer.previousSibling) {
if (startPointer.nodeType == 3 && startPointer.previousSibling.nodeType == 3) {
startIndex--;
}
}
for (var endPointer = end; endPointer.previousSibling; endPointer = endPointer.previousSibling) {
if (endPointer.nodeType == 3 && endPointer.previousSibling.nodeType == 3) {
endIndex--;
}
}
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);
},
getMarkers: function(range) {
var markers = [];
new RangeIterator(range).traverse(function (node) {
if (node.className == 'k-marker') {
markers.push(node);
}
});
return markers;
},
image: function (range) {
var nodes = [];
new RangeIterator(range).traverse(function (node) {
if (dom.is(node, 'img')) {
nodes.push(node);
}
});
if (nodes.length == 1) {
return nodes[0];
}
},
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);
(function($) {
// Imports ================================================================
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;
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 startRestorePoint = new RestorePoint(range);
var html = options.html || options.value || '';
editor.selectRange(range);
editor.clipboard.paste(html, options);
var genericCommand = new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange()));
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;
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 UndoRedoStack = Class.extend({
init: function() {
this.stack = [];
this.currentCommandIndex = -1;
},
push: function (command) {
this.stack = this.stack.slice(0, this.currentCommandIndex + 1);
this.currentCommandIndex = this.stack.push(command) - 1;
},
undo: function () {
if (this.canUndo()) {
this.stack[this.currentCommandIndex--].undo();
}
},
redo: function () {
if (this.canRedo()) {
this.stack[++this.currentCommandIndex].redo();
}
},
canUndo: function () {
return this.currentCommandIndex >= 0;
},
canRedo: function () {
return this.currentCommandIndex != this.stack.length - 1;
}
});
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();
}
if (!evt.isDefaultPrevented() && isTypingKey && !keyboard.isTypingInProgress()) {
var range = editor.getRange();
that.startRestorePoint = new RestorePoint(range);
keyboard.startTyping(function () {
editor.selectionRestorePoint = that.endRestorePoint = new RestorePoint(editor.getRange());
var genericCommand = new GenericCommand(that.startRestorePoint, that.endRestorePoint);
genericCommand.editor = editor;
editor.undoRedoStack.push(genericCommand);
});
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 SystemHandler = Class.extend({
init: function(editor) {
this.editor = editor;
this.systemCommandIsInProgress = false;
},
createUndoCommand: function () {
var that = this;
that.endRestorePoint = new RestorePoint(that.editor.getRange());
var command = new GenericCommand(that.startRestorePoint, that.endRestorePoint);
command.editor = that.editor;
that.editor.undoRedoStack.push(command);
that.startRestorePoint = that.endRestorePoint;
},
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 (e) {
var that = this;
if (that.systemCommandIsInProgress && that.changed()) {
that.systemCommandIsInProgress = false;
that.createUndoCommand(e);
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);
},
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() {
this.typingInProgress = false;
if (this.onEndTyping) {
this.onEndTyping();
}
},
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 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);
},
_contentModification: function(before, after) {
var that = this;
var editor = that.editor;
var range = editor.getRange();
var startRestorePoint = new RestorePoint(range);
dom.persistScrollTop(editor.document);
before.call(that, editor, range);
setTimeout(function() {
after.call(that, editor, range);
var endRestorePoint = new RestorePoint(editor.getRange());
var genericCommand = new GenericCommand(startRestorePoint, endRestorePoint);
genericCommand.editor = editor;
editor.undoRedoStack.push(genericCommand);
editor._selectionChange();
});
},
_fixTagNesting: function(html) {
var tags = /<(\/?)([a-z][a-z0-9]*)([^>]*)>/gi;
var stack = [];
var dom = editorNS.Dom;
html = html.replace(tags, function(match, closing, tagName) {
closing = !!closing;
tagName = tagName.toLowerCase();
var result = "";
var inline = dom.inline[tagName];
function closeLastTag() {
result = "" + stack.pop() + ">" + result;
}
if (closing) {
if (!stack.length) {
return "";
}
do {
if (dom.block[stack[stack.length-1]] && inline) {
return result;
}
closeLastTag();
} while (stack.length && stack[stack.length-1] != tagName);
} else {
if (!inline) {
while (dom.inline[stack[stack.length-1]]) {
closeLastTag();
}
}
stack.push(tagName);
result += match;
}
return result;
});
while (stack.length) {
html += "" + stack.pop() + ">";
}
return html;
},
oncut: function() {
this._contentModification($.noop, $.noop);
},
onpaste: function(e) {
this._contentModification(
function beforePaste(editor, range) {
var clipboardNode = dom.create(editor.document, 'div', {
className:'k-paste-container',
innerHTML: "\ufeff"
});
editor.body.appendChild(clipboardNode);
if (editor.body.createTextRange) {
e.preventDefault();
var r = editor.createRange();
r.selectNodeContents(clipboardNode);
editor.selectRange(r);
var textRange = editor.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 = "", args = { 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();
html = this._fixTagNesting(html.replace(/\ufeff/g, ""));
args.html = html;
editor.trigger("paste", args);
editor.clipboard.paste(args.html, { clean: true });
}
);
},
splittableParent: function(block, node) {
var parentNode, body;
if (block) {
return dom.parentOfType(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) {
// remove br elements which immediately precede block elements
html = html.replace(/( (\s| )*)+(<\/?(div|p|li|col|t))/ig, "$3");
// remove empty inline elements
html = html.replace(/<(a|span)[^>]*><\/\1>/ig, "");
}
// It is possible in IE to copy just tags
html = html.replace(/^ $/g, 'li>');
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(' '));
}
$(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);
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 MSWordFormatCleaner = Cleaner.extend({
init: function() {
this.replacements = [
/<\?xml[^>]*>/gi, '',
//g, '', /* comments */
/"/g, "'", /* encoded quotes (in attributes) */
/(?: [\s\r\n]+| )*(<\/?(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)[^>]*>)(?: [\s\r\n]+| )*/g, '$1',
/ /g, ' ',
/ (?!\n)/g, ' ',
/]*)>(\s| )+]*>(\s| )*<\/tr>/gi, '',
/]*>(\s| )*<\/tbody>/gi, '',
/]*>(\s| )*<\/table>/gi, '',
/ /g, ' ',
/^\s*( )+/gi, '',
/( | ]*>)+\s*$/gi, '',
/mso-[^;"]*;?/ig, '', /* office-related CSS attributes */
/<(\/?)b(\s[^>]*)?>/ig, '<$1strong$2>',
/<(\/?)i(\s[^>]*)?>/ig, '<$1em$2>',
/<\/?(meta|link|style|o:|v:|x:)[^>]*>((?:.|\n)*?<\/(meta|link|style|o:|v:|x:)[^>]*>)?/ig, '', /* external references and namespaced tags */
/style=(["|'])\s*\1/g, '' /* empty style attributes */
];
},
applicable: function(html) {
return (/class="?Mso|style="[^"]*mso-/i).test(html);
},
stripEmptyAnchors: function(html) {
return html.replace(/]*)>\s*<\/a>/ig, function(a, attributes) {
if (!attributes || attributes.indexOf("href") < 0) {
return "";
}
return a;
});
},
listType: function(html) {
var startingSymbol;
if (/^(]*texhtml[^>]*>)?]*(Symbol|Wingdings)[^>]*>/i.test(html)) {
startingSymbol = true;
}
html = html.replace(/<\/?\w+[^>]*>/g, '').replace(/ /g, '\u00a0');
if ((!startingSymbol && /^[\u2022\u00b7\u00a7\u00d8o]\u00a0+/.test(html)) ||
(startingSymbol && /^.\u00a0+/.test(html))) {
return 'ul';
}
if (/^\s*\w+[\.\)]\u00a0{2,}/.test(html)) {
return 'ol';
}
},
lists: function(placeholder) {
var blockChildren = $(dom.blockElements.join(','), placeholder),
lastMargin = -1,
lastType,
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);
if (!type || dom.name(p) != '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];
}
}
}
}
dom.remove(p.firstChild);
li = dom.create(document, 'li', {innerHTML:p.innerHTML});
list.appendChild(li);
dom.remove(p);
lastMargin = margin;
lastType = type;
}
},
removeAttributes: function(element) {
var attributes = element.attributes,
i = attributes.length;
while (i--) {
element.removeAttributeNode(attributes[i]);
}
},
createColGroup: function(row) {
var cells = row.cells, colgroup;
if (cells.length < 2) {
return;
}
colgroup = $($.map(cells, function(cell) {
var width = cell.width;
if (width && parseInt(width, 10) !== 0) {
return kendo.format(' ', width);
}
return " ";
}).join(""));
// jquery 1.9/2.0 discrepancy
if (!colgroup.is("colgroup")) {
colgroup = $(" ").append(colgroup);
}
colgroup.prependTo($(row).closest("table"));
},
convertHeaders: function(row) {
var cells = row.cells,
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 (var i = 0; i < boldedCells.length; i++) {
dom.unwrap(boldedCells[i]);
}
$(row).closest("table").find("colgroup").after(
"" +
$.map(cells, function(cell) {
return "" + $(cell).html() + " ";
}).join("") +
" "
).end().end().remove();
}
},
removeParagraphs: function(cells) {
var i, j, len, cell, paragraphs;
for (i = 0; i < cells.length; i++) {
this.removeAttributes(cells[i]);
// remove paragraphs and insert line breaks between them
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,
firstRow, i;
for (i = 0; i < tables.length; i++) {
firstRow = tables[i].rows[0];
that.createColGroup(firstRow);
that.convertHeaders(firstRow);
that.removeAttributes(tables[i]);
that.removeParagraphs(tables.eq(i).find("td,th"));
that.removeDefaultColors(tables.eq(i).find("span"));
}
},
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.lists(placeholder);
that.tables(placeholder);
html = placeholder.innerHTML.replace(/\s+class="?[^"\s>]*"?/ig, '');
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>$/, '$1'
];
},
applicable: function(html) {
return (/class="?Apple-style-span|style="[^"]*-webkit-nbsp-mode/i).test(html);
}
});
extend(editorNS, {
Command: Command,
GenericCommand: GenericCommand,
InsertHtmlCommand: InsertHtmlCommand,
InsertHtmlTool: InsertHtmlTool,
UndoRedoStack: UndoRedoStack,
TypingHandler: TypingHandler,
SystemHandler: SystemHandler,
Keyboard: Keyboard,
Clipboard: Clipboard,
Cleaner: Cleaner,
MSWordFormatCleaner: MSWordFormatCleaner,
WebkitFormatCleaner: WebkitFormatCleaner
});
registerTool("insertHtml", new InsertHtmlTool({template: new ToolTemplate({template: EditorUtils.dropDownListTemplate, title: "Insert HTML", initialValue: "Insert HTML"})}));
})(window.kendo.jQuery);
(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) {
// full node selection
return 0;
} else {
return elementNodesCount + textNodesCount;
}
},
findSuitable: function (sourceNode, skip) {
if (!skip && this.numberOfSiblings(sourceNode) > 0) {
return null;
}
return dom.parentOfType(sourceNode, this.format[0].tags);
},
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) {
var that = this;
that.finder = new InlineFormatFinder(format);
that.attributes = extend({}, format[0].attr, values);
that.tag = format[0].tags[0];
},
wrap: function(node) {
return dom.wrap(node, dom.create(node.ownerDocument, this.tag, this.attributes));
},
activate: function(range, nodes) {
var that = this;
if (that.finder.isFormatted(nodes)) {
that.split(range);
that.remove(nodes);
} else {
that.apply(nodes);
}
},
toggle: function (range) {
var nodes = RangeUtils.textNodes(range);
if (nodes.length > 0) {
this.activate(range, nodes);
}
},
apply: function (nodes) {
var that = this,
formatNodes = [],
i, l, node, formatNode;
for (i = 0, l = nodes.length; i < l; i++) {
node = nodes[i];
formatNode = that.finder.findSuitable(node);
if (formatNode) {
dom.attr(formatNode, that.attributes);
} else {
formatNode = that.wrap(node);
}
formatNodes.push(formatNode);
}
that.consolidate(formatNodes);
},
remove: function (nodes) {
var that = this,
i, l, formatNode;
for (i = 0, l = nodes.length; i < l; i++) {
formatNode = that.finder.findFormat(nodes[i]);
if (formatNode) {
if (that.attributes && that.attributes.style) {
dom.unstyle(formatNode, that.attributes.style);
if (!formatNode.style.cssText && !formatNode.attributes["class"]) {
dom.unwrap(formatNode);
}
} else {
dom.unwrap(formatNode);
}
}
}
},
split: function (range) {
var nodes = RangeUtils.textNodes(range),
l = nodes.length,
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) {
var that = this;
that.format = format;
that.greedyProperty = greedyProperty;
InlineFormatFinder.fn.init.call(that, format);
},
getInlineCssValue: function(node) {
var attributes = node.attributes,
trim = $.trim,
i, l, attribute, name, attributeValue, css, pair, cssIndex, len, 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),
parents = $node.parentsUntil("[contentEditable]").addBack(),
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) {
var that = this;
InlineFormatter.fn.init.call(that, format, values);
that.greedyProperty = greedyProperty;
that.values = values;
that.finder = new GreedyInlineFormatFinder(format, greedyProperty);
},
activate: function(range, nodes) {
var that = this,
camelCase,
greedyProperty = that.greedyProperty,
action = "apply";
that.split(range);
if (greedyProperty) {
camelCase = greedyProperty.replace(/-([a-z])/, function(all, letter) { return letter.toUpperCase(); });
if (that.values.style[camelCase] == "inherit") {
action = "remove";
}
}
that[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) {
var that = this;
Tool.fn.init.call(that, options);
// IE has single selection hence we are using select box instead of combobox
that.type = (kendo.support.browser.msie || kendo.support.touch) ? "kendoDropDownList" : "kendoComboBox";
that.format = [{ tags: ["span"] }];
that.finder = new GreedyInlineFormatFinder(that.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[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, {
value: $.isArray(palette) ? palette[0] : "#000",
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);
(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++) {
candidate = dom.ofType(nodes[i], format[0].tags) ? nodes[i] : dom.parentOfType(nodes[i], format[0].tags);
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;
for (i = 0, len = format.length; i < len; i++) {
node = sourceNode;
tags = format[i].tags;
attributes = format[i].attr;
while (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 = $.filter("img", nodes);
var imageFormat = EditorUtils.formatByName("img", this.format);
var imageAttributes = attributes(imageFormat);
$.each(images, function() {
dom.attr(this, imageAttributes);
});
// only images were selected, no need to wrap
if (images.length == nodes.length) {
return;
}
var nonImages = $.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,
blocks = dom.blockParents(nodes),
formatTag = format[0].tags[0],
i, len, list, formatter, range,
element;
if (blocks.length && blocks[0].attributes.contentEditable) {
// do not break out of contentEditable elements
blocks = [];
}
if (blocks.length) {
for (i = 0, len = blocks.length; i < len; i++) {
if (dom.is(blocks[i], "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 {
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
});
registerFormat("justifyLeft", [ { tags: dom.blockElements, attr: { style: { textAlign: "left"}} }, { tags: ["img"], attr: { style: { "float": "left"}} } ]);
registerTool("justifyLeft", new BlockFormatTool({format: formats.justifyLeft, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Left"})}));
registerFormat("justifyCenter", [ { tags: dom.blockElements, attr: { style: { textAlign: "center"}} }, { tags: ["img"], attr: { style: { display: "block", marginLeft: "auto", marginRight: "auto"}} } ]);
registerTool("justifyCenter", new BlockFormatTool({format: formats.justifyCenter, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Center"})}));
registerFormat("justifyRight", [ { tags: dom.blockElements, attr: { style: { textAlign: "right"}} }, { tags: ["img"], attr: { style: { "float": "right"}} } ]);
registerTool("justifyRight", new BlockFormatTool({format: formats.justifyRight, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Right"})}));
registerFormat("justifyFull", [ { tags: dom.blockElements, attr: { style: { textAlign: "justify"}} } ]);
registerTool("justifyFull", new BlockFormatTool({format: formats.justifyFull, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Full"})}));
})(window.kendo.jQuery);
(function($) {
// Imports ================================================================
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;
range.insertNode(marker);
if (!marker.parentNode) {
// inserting paragraph in Firefox full body range
container = range.commonAncestorContainer;
container.innerHTML = "";
container.appendChild(marker);
}
normalize(marker.parentNode);
return marker;
},
_moveFocus: function(range, next) {
if (dom.is(next, 'img')) {
range.setStartBefore(next);
} else {
range.selectNodeContents(next);
var textNode = RangeUtils.textNodes(range)[0];
if (textNode) {
range.selectNodeContents(textNode);
} else {
while (next.childNodes.length && !dom.is(next.firstChild, "br")) {
next = next.firstChild;
}
range.selectNodeContents(next);
}
}
},
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);
},
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) {
rng = range.cloneRange();
rng.selectNode(li);
// hitting 'enter' in empty li
if (!RangeUtils.textNodes(rng).length) {
paragraph = dom.create(doc, 'p');
if (li.nextSibling) {
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 && !marker.nextSibling) {
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;
if (dom.is(next, 'li') && next.firstChild && !dom.is(next.firstChild, 'br')) {
next = next.firstChild;
}
dom.remove(parent);
this.clean(previous);
this.clean(next);
// normalize updates the caret display in Gecko
normalize(previous);
}
normalize(next);
this._moveFocus(range, next);
range.collapse(true);
dom.scrollTo(next);
RangeUtils.selectRange(range);
},
clean: function(node) {
if (node.firstChild && dom.is(node.firstChild, 'br')) {
dom.remove(node.firstChild);
}
if (dom.isDataNode(node) && !node.nodeValue) {
node = node.parentNode;
}
if (node) {
while (node.firstChild && node.firstChild.nodeType == 1) {
node = node.firstChild;
}
if (!dom.isEmpty(node) && /^\s*$/.test(node.innerHTML)) {
node.innerHTML = editorNS.emptyElementContent;
}
}
}
});
var NewLineCommand = Command.extend({
init: function(options) {
this.options = options;
Command.fn.init.call(this, options);
},
exec: function () {
var range = this.getRange(),
br = dom.create(RangeUtils.documentFromRange(range), 'br'),
filler;
range.deleteContents();
range.insertNode(br);
normalize(br.parentNode);
if (!kendo.support.browser.msie && (!br.nextSibling || dom.isWhitespace(br.nextSibling))) {
// Gecko and WebKit cannot put the caret after only one br.
filler = br.cloneNode(true);
filler.setAttribute('_moz_dirty', '');
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);
(function($) {
// Imports ================================================================
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 = [], formatNode;
for (var i = 0; i < nodes.length; i++) {
if ((formatNode = this.findFormat(nodes[i])) && dom.name(formatNode) == this.tag) {
formatNodes.push(formatNode);
}
}
if (formatNodes.length < 1) {
return false;
}
if (formatNodes.length != nodes.length) {
return false;
}
// check if sequential lists are selected
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 = dom.parentOfType(nodes[0], this.tags);
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;
},
split: function (range) {
var nodes = textNodes(range),
start, end;
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) {
var parents = $(formatNode).parents("ul,ol");
if (parents[0]) {
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;
}
// merge with previous list
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;
}
// merge with next list
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)) {
// merging lists
$.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;
// split nodes into sections that need to be different lists
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 = $(ul).parents('ul,ol');
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: "Remove Link"})}));
registerTool("insertOrderedList", new ListTool({tag:'ol', template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Remove Link"})}));
})(window.kendo.jQuery);
(function($, undefined) {
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,
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),
markers, doc,
formatter, a;
if (attributes.innerHTML) {
markers = RangeUtils.getMarkers(range);
doc = RangeUtils.documentFromRange(range);
range.deleteContents();
a = dom.create(doc, "a", attributes);
range.insertNode(a);
if (dom.name(a.parentNode) == "a") {
dom.insertAfter(a, a.parentNode);
}
if (markers.length > 1) {
dom.insertAfter(markers[markers.length - 1], a);
dom.insertAfter(markers[1], a);
dom[nodes.length > 0 ? "insertBefore" : "insertAfter"](markers[0], a);
}
} else {
formatter = new InlineFormatter([{ tags: ["a"]}], attributes);
formatter.finder = this.finder;
formatter.apply(nodes);
}
}
});
var UnlinkCommand = Command.extend({
init: function(options) {
options.formatter = /** @ignore */ {
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) {
var cmd = this;
cmd.options = options;
Command.fn.init.call(cmd, options);
cmd.formatter = new LinkFormatter();
if (!options.url) {
cmd.attributes = null;
cmd.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(
'"
)({
messages: this.editor.options.messages
});
},
exec: function () {
var that = this,
range = that.getRange(),
collapsed = range.collapsed,
nodes,
initialText = "",
messages = that.editor.options.messages;
range = that.lockRange(true);
nodes = textNodes(range);
function apply(e) {
var element = dialog.element,
href = $("#k-editor-link-url", element).val(),
title, text, target;
if (href && href != "http://") {
if (href.indexOf("@") > 0 && !/^(\w+:)|(\/\/)/i.test(href)) {
href = "mailto:" + href;
}
that.attributes = { href: href };
title = $("#k-editor-link-title", element).val();
if (title) {
that.attributes.title = title;
}
text = $("#k-editor-link-text", element).val();
if (!text && !initialText) {
that.attributes.innerHTML = href;
} else if (text && (text !== initialText)) {
that.attributes.innerHTML = dom.stripBom(text);
}
target = $("#k-editor-link-target", element).is(":checked");
that.attributes.target = target ? "_blank" : null;
that.formatter.apply(range, that.attributes);
}
close(e);
if (that.change) {
that.change();
}
}
function close(e) {
e.preventDefault();
dialog.destroy();
dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
that.releaseRange(range);
}
function linkText(nodes) {
var text = "";
if (nodes.length == 1) {
text = nodes[0].nodeValue;
} else if (nodes.length) {
text = nodes[0].nodeValue + nodes[1].nodeValue;
}
return dom.stripBom(text);
}
var a = nodes.length ? that.formatter.finder.findSuitable(nodes[0]) : null;
var dialog = this.createDialog(that._dialogTemplate(), {
title: messages.createLink,
close: close,
visible: false
})
.find(".k-dialog-insert").click(apply).end()
.find(".k-dialog-close").click(close).end()
.find(".k-edit-field input").keydown(function (e) {
var keys = kendo.keys;
if (e.keyCode == keys.ENTER) {
apply(e);
} else if (e.keyCode == keys.ESC) {
close(e);
}
}).end()
// IE < 8 returns absolute url if getAttribute is not used
.find("#k-editor-link-url").val(a ? a.getAttribute("href", 2) : "http://").end()
.find("#k-editor-link-text").val(linkText(nodes)).end()
.find("#k-editor-link-title").val(a ? a.title : "").end()
.find("#k-editor-link-target").attr("checked", a ? a.target == "_blank" : false).end()
.data("kendoWindow")
.center().open();
if (nodes.length > 0 && !collapsed) {
initialText = $("#k-editor-link-text", dialog.element).val();
}
$("#k-editor-link-url", dialog.element).focus().select();
},
redo: function () {
var that = this,
range = that.lockRange(true);
that.formatter.apply(range, that.attributes);
that.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);
(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";
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://") {
if (!img) {
img = dom.create(doc, "img", attributes);
img.onload = img.onerror = function () {
img.removeAttribute("complete");
img.removeAttribute("width");
img.removeAttribute("height");
};
range.deleteContents();
range.insertNode(img);
if (!img.nextSibling) {
dom.insertAfter(doc.createTextNode("\ufeff"), img);
}
range.setStartAfter(img);
range.setEndAfter(img);
RangeUtils.selectRange(range);
return true;
} else {
dom.attr(img, attributes);
}
}
return false;
},
_dialogTemplate: function(showBrowser) {
return kendo.template(
''
)({
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),
dialog,
options = that.editor.options,
messages = options.messages,
imageBrowser = options.imageBrowser,
showBrowser = !!(kendo.ui.ImageBrowser && imageBrowser && imageBrowser.transport && imageBrowser.transport.read !== undefined);
function apply(e) {
var element = dialog.element;
that.attributes = {
src: element.find(KEDITORIMAGEURL).val().replace(/ /g, "%20"),
alt: element.find(KEDITORIMAGETITLE).val()
};
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);
}
}
dialog = this.createDialog(that._dialogTemplate(showBrowser), {
title: messages.insertImage,
close: close,
visible: false,
resizable: showBrowser
})
.toggleClass("k-imagebrowser-dialog", showBrowser)
.find(".k-dialog-insert").click(apply).end()
.find(".k-dialog-close").click(close).end()
.find(".k-edit-field input").keydown(keyDown).end()
// IE < 8 returns absolute url if getAttribute is not used
.find(KEDITORIMAGEURL).val(img ? img.getAttribute("src", 2) : "http://").end()
.find(KEDITORIMAGETITLE).val(img ? img.alt : "").end()
.data("kendoWindow");
if (showBrowser) {
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);
(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);
that.value(that.options.title);
// overlay drop-down with popout for snappier interaction
if (kendo.support.mobileOS.ios) {
that._initSelectOverlay();
}
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"
},
_initSelectOverlay: function() {
var element = $(this.element);
var select = $("
");
var wrapper = element.closest(".k-widget");
var selectBox = this;
select.on("change", function() {
selectBox.value(this.value);
selectBox.trigger("change");
});
this.bind("dataBound", function() {
var value = selectBox.value();
var view = this.dataSource.view();
var item;
var html = "";
for (var i = 0; i < view.length; i++) {
item = view[i];
html += "
" + item.text + " ";
}
select.html(html);
});
select.insertAfter(wrapper);
},
value: function(value) {
var that = this,
result = DropDownList.fn.value.call(that, value);
if (value === undefined) {
return result;
}
if (value !== DropDownList.fn.value.call(that)) {
that.text(that.options.title);
if (that._current) {
that._current.removeClass("k-state-selected");
}
that.current(null);
that._oldIndex = that.selectedIndex = -1;
}
},
decorate: function(doc) {
var items = this.dataSource.data(),
i, tag, className;
for (i = 0; i < items.length; i++) {
tag = items[i].tag || "span";
className = items[i].className;
items[i].style = dom.inlineStyle(doc, tag, { className : className }) + ";display:inline-block";
}
this.dataSource.trigger("change");
}
});
kendo.ui.plugin(SelectBox);
kendo.ui.editor.SelectBox = SelectBox;
})(window.kendo.jQuery);
(function($, undefined) {
// Imports ================================================================
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();
// listParent will be ul or ol in case of invalid dom -
if (listParent.is("li,ul,ol") && !indent(list[0])) {
// skip already processed nodes
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]) {
// removing format on sibling LI elements
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 = /** @ignore */ {
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);
(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: "",
indent: function(content) {
return content.replace(/<\/(p|li|ul|ol|h[1-6]|table|tr|td|th)>/ig, "$1>\n")
.replace(/<(ul|ol)([^>]*)>
\n /ig, "
\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);
(function($) {
var kendo = window.kendo,
Editor = kendo.ui.editor,
Tool = Editor.Tool,
ToolTemplate = Editor.ToolTemplate,
DelayedExecutionTool = Editor.DelayedExecutionTool,
dom = Editor.Dom,
dropDownListTemplate = Editor.EditorUtils.dropDownListTemplate,
registerTool = Editor.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: 90
},
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 () {
Tool.exec(editor, toolName, this.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(
'
#:data.text# '
)
});
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] != element) {
break;
} else if (n == nodes.length - 1) {
return item.value;
}
}
}
return "";
},
update: function(ui, nodes) {
var selectBox = $(ui).data(this.type);
// necessary until formatBlock is deleted
if (!selectBox) {
return;
}
var dataSource = selectBox.dataSource,
items = dataSource.data(),
i, context,
ancestor = dom.commonAncestor.apply(null, nodes);
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);
}
});
function deprecatedFormattingTool(name, property, finder) {
return FormattingTool.extend({
init: function(options) {
FormattingTool.fn.init.call(this, options);
this.finder = finder;
},
command: function(args) {
var item = args.value;
// pre-process value for backwards compatibility
if ($.isPlainObject(item)) {
item[property] = item.value;
} else {
args.value = {};
args.value[property] = item;
}
return FormattingTool.fn.command.call(this, args);
},
initialize: function(ui, initOptions) {
var console = window.console,
i, items = this.options.items;
for (i = 0; i < items.length; i++) {
items[i][property] = items[i].value;
}
if (console) {
console.warn("The `" + this.options.name + "` tool has been deprecated in favor of the `formatting` tool. See http://docs.kendoui.com/getting-started/changes-and-backward-compatibility for more information");
}
FormattingTool.fn.initialize.call(this, ui, initOptions);
}
});
}
var StyleTool = deprecatedFormattingTool("style", "className", new Editor.GreedyInlineFormatFinder([{ tags: ["span"] }], "className"));
var FormatBlockTool = deprecatedFormattingTool("formatBlock", "tag", new Editor.BlockFormatFinder([{ tags: dom.blockElements }]));
$.extend(Editor, {
FormattingTool: FormattingTool,
StyleTool: StyleTool,
FormatBlockTool: FormatBlockTool
});
registerTool("formatting", new FormattingTool({template: new ToolTemplate({template: dropDownListTemplate, title: "Format"})}));
registerTool("style", new StyleTool({template: new ToolTemplate({template: dropDownListTemplate, title: "Styles"})}));
registerTool("formatBlock", new FormatBlockTool({template: new ToolTemplate({template: dropDownListTemplate})}));
})(window.kendo.jQuery);
(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 focusable = "a.k-tool:not(.k-state-disabled)," +
".k-widget.k-colorpicker,.k-selectbox,.k-dropdown,.k-combobox .k-input";
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();
}
},
events: [
"execute"
],
groups: {
basic: ["bold", "italic", "underline", "strikethrough"],
scripts: ["subscript", "superscript" ],
alignment: ["justifyLeft", "justifyCenter", "justifyRight", "justifyFull" ],
links: ["insertImage", "createLink", "unlink"],
lists: ["insertUnorderedList", "insertOrderedList", "indent", "outdent"],
tables: [ "createTable", "addColumnLeft", "addColumnRight", "addRowAbove", "addRowBelow", "deleteRow", "deleteColumn" ],
advanced: [ "viewHtml" ]
},
_initPopup: function() {
this.window = $(this.element)
.wrap("")
.parent()
.prepend("
")
.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");
},
items: function() {
return this.element.children().find("> *, select");
},
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;
// detach from editor that was previously listened to
if (that._editor) {
that._editor.unbind("select", proxy(that._update, that));
}
that._editor = editor;
// re-initialize the tools
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 = that.tools[toolName],
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._update, that));
that._updateContext();
if (window) {
window.wrapper.css({top: "", left: "", width: ""});
}
},
show: function() {
var that = this,
window = that.window,
editorOptions = that.options.editor,
wrapper, editorElement;
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));
}
// track content position when other parts of page change
if (!window._moved) {
wrapper.css("top", parseInt(editorElement.offset().top, 10) - wrapper.outerHeight() - parseInt(that.window.element.css("padding-bottom"), 10));
wrapper.css("left", parseInt(editorElement.offset().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);
// Chrome can't focus something which has already been focused
element.attr(TABINDEX, tabIndex || 0).focus()
.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: {}, // tools collection is copied from defaultTools during initialization
isCustomTool: function(toolName) {
return !(toolName in kendo.ui.Editor.defaultTools);
},
// expand the tools parameter to contain tool options objects
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]) {
// configured tool
result[name] = extend({}, defaultTools[name]);
extend(result[name].options, currentTool);
} else {
// custom tool
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]) {
// tool by name
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;
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) {
group.appendTo(element);
}
}
function startGroup() {
group = $("
");
}
element.empty();
startGroup();
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();
$("
").appendTo(that.element);
startGroup();
}
if (!template) {
continue;
}
newGroupName = that.toolGroupFor(toolName);
if (groupName != newGroupName) {
endGroup();
startGroup();
groupName = newGroupName;
}
template = stringify(template);
toolElement = $(template).appendTo(group);
if (newGroupName == "custom") {
endGroup();
startGroup();
}
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");
}
this.updateGroups();
},
updateGroups: function() {
$(this.element).children().each(function() {
$(this).children().filter(function(){
return this.style.display !== "none";
})
.first().addClass("k-group-start").end()
.last().addClass("k-group-end").end();
});
},
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();
}
},
_attachEvents: function() {
var that = this,
buttons = "[role=button].k-tool",
enabledButtons = buttons + ":not(.k-state-disabled)",
disabledButtons = buttons + ".k-state-disabled";
that.element
.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 focusElement,
keyCode = e.keyCode;
function move(direction, constrain) {
var tools = that.element.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, true);
}
} else if (keyCode == keys.ESC) {
focusElement = that._editor;
} else if (keyCode == keys.TAB && !(e.ctrlKey || e.altKey)) {
// skip tabbing to disabled tools, and focus the editing area when running out of tools
if (e.shiftKey) {
focusElement = move(-1);
} else {
focusElement = move(1);
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|state-hover|header|combobox|dropdown|selectbox|colorpicker)$/i.test(x);
});
return tool[0] ? tool[0].substring(tool[0].lastIndexOf("-") + 1) : "custom";
},
// update tool state
_update: 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._updateContext();
},
_updateContext: function() {
this.element.children().children().each(function() {
var tool = $(this);
tool.css("display", tool.hasClass("k-state-disabled") ? "none" : "");
});
this.updateGroups();
}
});
$.extend(editorNS, {
Toolbar: Toolbar
});
})(window.jQuery);
(function($, undefined) {
var kendo = window.kendo,
extend = $.extend,
proxy = $.proxy,
Editor = kendo.ui.editor,
dom = Editor.Dom,
EditorUtils = Editor.EditorUtils,
Command = Editor.Command,
NS = ".kendoEditor",
ACTIVESTATE = "k-state-active",
SELECTEDSTATE = "k-state-selected",
Tool = Editor.Tool,
ToolTemplate = Editor.ToolTemplate,
BlockFormatFinder = Editor.BlockFormatFinder,
registerTool = Editor.EditorUtils.registerTool;
var editableCell = "
" + Editor.emptyElementContent + " ";
var tableFormatFinder = new BlockFormatFinder([{tags:["table"]}]);
var TableCommand = Command.extend({
_tableHtml: function(rows, columns) {
rows = rows || 1;
columns = columns || 1;
return "
" +
new Array(rows + 1).join("" + new Array(columns + 1).join(editableCell) + " ") +
"
";
},
exec: function() {
var options = this.options,
editor = this.editor,
range,
tableHtml = this._tableHtml(options.rows, options.columns),
insertedTable;
editor.selectRange(options.range);
editor.clipboard.paste(tableHtml);
range = editor.getRange();
insertedTable = $("table[data-last]", editor.document).removeAttr("data-last");
range.selectNodeContents(insertedTable.find("td")[0]);
editor.selectRange(range);
}
});
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));
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);
},
_toggle: function(e) {
var button = $(e.target).closest(".k-tool");
if (!button.hasClass("k-state-disabled")) {
this.popup().toggle();
}
},
update: function(ui) {
this.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:
""
}));
},
_activate: function() {
var that = this,
element = that._popup.element,
status = element.find(".k-status"),
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;
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
};
}
function valid(p) {
return p.row > 0 && p.col > 0 && p.row <= rows && p.col <= cols;
}
element
.on("mousemove" + NS, function(e) {
var t = tableFromLocation(e);
if (valid(t)) {
status.text(kendo.format("Create a {0} x {1} table", t.row, t.col));
cells.each(function(i) {
$(this).toggleClass(
SELECTEDSTATE,
i % cols < t.col && i / cols < t.row
);
});
} else {
status.text("Cancel");
cells.removeClass(SELECTEDSTATE);
}
})
.on("mouseleave" + NS, function() {
cells.removeClass(SELECTEDSTATE);
status.text("Cancel");
})
.on("mouseup" + NS, function(e) {
var t = tableFromLocation(e);
if (valid(t)) {
that._editor.exec("createTable", {
rows: t.row,
columns: t.col
});
that._popup.close();
}
});
},
_open: function() {
PopupTool.fn._open.call(this);
this.popup().element.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);
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(),
row = dom.closest(range.endContainer, "tr"),
table = dom.closest(row, "table"),
rowCount = table.rows.length,
focusElement;
if (rowCount == 1) {
focusElement = dom.next(table) || dom.prev(table);
dom.remove(table);
} else {
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) || 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"})}));
//registerTool("mergeCells", new Tool({ template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Merge cells"})}));
})(window.kendo.jQuery);
kendo_module({
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,
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;
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("blur" + ns, proxy(that._focusout, that))
.attr("role", "spinbutton");
options.placeholder = options.placeholder || element.attr("placeholder");
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(e) {
that._toggleText(false);
if (e.type === FOCUS) {
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]");
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('
');
}
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;
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_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 = $('
').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.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;
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());
}
},
_keypress: function(e) {
if (e.which === 0 || e.keyCode === keys.BACKSPACE) {
return;
}
var element = this.element;
var character = String.fromCharCode(e.which);
var selection = caret(element[0]);
var selectionStart = selection[0];
var selectionEnd = selection[1];
var min = this.options.min;
var value = element.val();
value = value.substring(0, selectionStart) + character + value.substring(selectionEnd);
if ((min !== null && min >= 0 && value.charAt(0) === "-") || !this._numericRegex().test(value)) {
e.preventDefault();
}
},
_numericRegex: function() {
var that = this;
var options = that.options;
var numberFormat = that._format(options.format);
var separator = numberFormat[POINT];
var precision = 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.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('
').parent();
wrapper = wrapper.wrap("
").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._form = form.on("reset", that._resetHandler);
}
}
});
function buttonHtml(className, text) {
return '
' + text + ' ';
}
function caret(element, position) {
var range,
isPosition = position !== undefined;
if (element.selectionStart !== undefined) {
if (isPosition) {
element.focus();
element.setSelectionRange(position, position);
} else {
position = [element.selectionStart, element.selectionEnd];
}
} else if (document.selection) {
if ($(element).is(":visible")) {
element.focus();
}
range = document.selection.createRange();
if (isPosition) {
range.move("character", position);
range.select();
} else {
var rangeElement = element.createTextRange(),
rangeDuplicated = rangeElement.duplicate(),
selectionStart, selectionEnd;
rangeElement.moveToBookmark(range.getBookmark());
rangeDuplicated.setEndPoint('EndToStart', rangeElement);
selectionStart = rangeDuplicated.text.length;
selectionEnd = selectionStart + rangeElement.text.length;
position = [selectionStart, selectionEnd];
}
}
return position;
}
ui.plugin(NumericTextBox);
})(window.kendo.jQuery);
kendo_module({
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,
MOUSEENTER = pointers ? "pointerover" : (msPointers ? "MSPointerOver" : "mouseenter"),
MOUSELEAVE = pointers ? "pointerout" : (msPointers ? "MSPointerOut" : "mouseleave"),
mobile = touch || msPointers || pointers,
KENDOPOPUP = "kendoPopup",
DEFAULTSTATE = "k-state-default",
HOVERSTATE = "k-state-hover",
FOCUSEDSTATE = "k-state-focused",
DISABLEDSTATE = "k-state-disabled",
groupSelector = ".k-group",
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)",
templates = {
content: template(
"
#= content(item) #
"
),
group: template(
"
" +
"#= renderItems(data) #" +
" "
),
itemWrapper: template(
"<#= tag(item) # class='#= textClass(item) #'#= textAttributes(item) #>" +
"#= image(item) ##= sprite(item) ##= text(item) #" +
"#= arrow(data) #" +
"#= tag(item) #>"
),
item: template(
"
" +
"#= itemWrapper(data) #" +
"# if (item.items) { #" +
"#= subGroup({ items: item.items, menu: menu, group: { expanded: item.expanded } }) #" +
"# } else if (item.content || item.contentUrl) { #" +
"#= renderContent(data) #" +
"# } #" +
" "
),
image: template("
"),
arrow: template("
"),
sprite: template("
"),
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";
},
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() // exclude groups, real links, templates and empty text nodes
.filter(function() { return (!this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue))); })
.wrapAll("
");
}
updateArrow(item);
updateFirstLast(item);
}
function updateArrow (item) {
item = $(item);
item.find("> .k-link > [class*=k-i-arrow]").remove();
item.filter(":has(.k-group)")
.children(".k-link:not(:has([class*=k-i-arrow]))")
.each(function () {
var item = $(this),
parent = item.parent().parent();
item.append("");
});
}
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, 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: { // if close animation effects are defined, they will be used instead of open.reverse
duration: 100
}
},
orientation: "horizontal",
direction: "default",
openOnClick: false,
closeOnClick: true,
hoverDelay: 100
},
_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);
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
that.element.off(NS);
if (that._documentClickHandler) {
$(document).unbind("click", that._documentClickHandler);
}
},
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-group, > .k-animation-container > .k-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)) { // is JSON
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 {
items = $(item);
groups = items.find("> ul")
.addClass("k-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");
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-group:first:hidden"),
popup;
if (ul[0] && that.trigger(OPEN, { item: li[0] }) === false) {
if (!ul.find(".k-group")[0] && ul.children(".k-item").length > 1) {
ul.css({maxHeight: $(window).height(), overflow: "auto"});
} 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.trigger(ACTIVATE, { item: this.wrapper.parent() }); },
deactivate: function() { that.trigger(DEACTIVATE, { item: this.wrapper.parent() }); },
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.trigger(CLOSE, { item: li[0] })) {
li.css(ZINDEX, li.data(ZINDEX));
li.removeData(ZINDEX);
if (mobile) {
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) {
var that = this,
element = that.element;
items = element.find(items);
if (!items.length) {
items = element.find(">.k-item");
}
items.each(function () {
var li = $(this);
clearTimeout(li.data(TIMER));
li.data(TIMER, setTimeout(function () {
var popup = li.find(".k-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,
items;
element.addClass("k-widget k-reset k-header " + MENU).addClass(MENU + "-" + this.options.orientation);
element.find("li > ul")
.addClass("k-group")
.attr("role", "menu")
.attr("aria-hidden", element.is(":visible"))
.end()
.find("li > div")
.addClass("k-content")
.attr("tabindex", "-1"); // Capture the focus before the Menu
items = element.find("> li,.k-group > li");
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(".k-menu")[0]) {
return;
}
if ((!that.options.openOnClick || that.clicked) && !touch) {
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);
}, 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 == e.originalEvent.MSPOINTER_TYPE_TOUCH) &&
!contains(e.currentTarget, e.relatedTarget || e.target) && hasChildren) {
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,
isLink = (!!href && href !== $("
").attr("href"));
if (element.children(templateSelector)[0]) {
return;
}
if (element.hasClass(DISABLEDSTATE)) {
e.preventDefault();
return;
}
if (!e.handled && that.trigger(SELECT, { item: element[0] }) && !formNode) { // We shouldn't stop propagation and shoudn't prevent form elements.
e.preventDefault();
}
e.handled = true;
childGroup = element.children(groupSelector + ",.k-animation-container");
childGroupVisible = childGroup.is(":visible");
if (options.closeOnClick && !isLink && (!childGroup.length || (options.openOnClick && childGroupVisible))) {
element.removeClass(HOVERSTATE).css("height"); // Force refresh for Chrome
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 ((!element.parent().hasClass(MENU) || !options.openOnClick) && !kendo.support.touch) {
return;
}
if (!isLink && !formNode) {
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-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(); // needed to handle ESC in column menu only when a root item is focused
}
},
_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-group") || menuIsVertical;
},
_itemHasChildren: function (item) {
if (!item.length) {
return false;
}
return item.children("ul.k-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 (item[0].id === id) {
item.removeAttr("id");
}
}
if (nextItem.length) {
if (nextItem[0].id) {
id = nextItem[0].id;
}
nextItem.addClass(FOCUSEDSTATE);
that._oldHoverItem = nextItem;
if (id) {
that.element.removeAttr("aria-activedescendant");
nextItem.attr("id", id);
that.element.attr("aria-activedescendant", id);
}
}
},
_findRootParent: function (item) {
if (item.parent().hasClass("k-menu")) {
return item;
} else {
return item.parentsUntil(".k-menu", "li.k-item").last();
}
},
_isRootItem: function (item) {
return item.parent().hasClass("k-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-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-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;
},
_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: {} } };
}
}
});
// client-side rendering
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));
}
});
kendo.ui.plugin(Menu);
})(window.kendo.jQuery);
kendo_module({
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 = '
',
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);
$('
').attr(attr).appendTo(container).kendoNumericTextBox({ format: options.format });
$('
').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;
$('
').attr(attr).appendTo(container).kendoDatePicker({ format: options.format });
$('
').hide().appendTo(container);
},
"string": function(container, options) {
var attr = createAttributes(options);
$('
').attr(attr).appendTo(container);
},
"boolean": function(container, options) {
var attr = createAttributes(options);
$('
').attr(attr).appendTo(container);
},
"values": function(container, options) {
var attr = createAttributes(options);
$('
') .attr(attr).appendTo(container);
$('
').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;
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,
isBoolean = typeof e.value === "boolean",
input,
preventChangeTrigger = that._validationEventInProgress,
values = {};
values[e.field] = e.value;
input = $(':input[' + kendo.attr("bind") + '="' + (isBoolean ? 'checked:' : 'value:') + e.field.replace(nameSpecialCharRegExp, "\\$1") + '"]', that.element);
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;
Widget.fn.destroy.call(that);
that.options.model.unbind("set", that._validateProxy);
kendo.unbind(that.element);
kendo.destroy(that.element);
that.element.removeData("kendoValidator");
},
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 (!length) {
modelFields = model.fields || model;
for (fieldName in modelFields) {
addValidationRules(modelFields[fieldName], rules);
}
}
convertToValueBinding(container);
kendo.bind(container, that.options.model);
that.options.model.bind("set", that._validateProxy);
that.validatable = container.kendoValidator({
validateOnBlur: false,
errorTemplate: that.options.errorTemplate || undefined,
rules: rules }).data("kendoValidator");
var focusable = container.find(":kendoFocusable:first").focus();
if (oldIE) {
focusable.focus();
}
}
});
ui.plugin(Editable);
})(window.kendo.jQuery);
kendo_module({
id: "filtermenu",
name: "Filtering Menu",
category: "framework",
depends: [ "datepicker", "numerictextbox", "dropdownlist" ],
advanced: true
});
(function($, undefined) {
var kendo = window.kendo,
ui = kendo.ui,
proxy = $.proxy,
POPUP = "kendoPopup",
INIT = "init",
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 =
'
' +
'
#=messages.info#
'+
'
'+
' ' +
'#=messages.isTrue#' +
' ' +
'
'+
' ' +
'#=messages.isFalse#' +
' ' +
'
' +
'#=messages.filter# '+
'#=messages.clear# '+
'
' +
'
';
var defaultTemplate =
'
' +
'
#=messages.info#
'+
'
'+
'#for(var op in operators){#'+
'#=operators[op]# ' +
'#}#'+
' '+
'#if(values){#' +
'
' +
' ' +
'#}else{#' +
'
'+
'#}#' +
'#if(extra){#'+
'
'+
'#=messages.and# '+
'#=messages.or# '+
' '+
'
'+
'#for(var op in operators){#'+
'#=operators[op]# '+
'#}#'+
' '+
'#if(values){#' +
'
' +
' '+
'#}else{#' +
'
'+
'#}#' +
'#}#'+
'
'+
'#=messages.filter# '+
'#=messages.clear# '+
'
'+
'
';
var defaultMobileTemplate =
''+
''+
'
';
var booleanMobileTemplate =
'';
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.value || item;
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-filterable").find(".k-grid-filter");
if (!link[0]) {
link = element.prepend(' ').find(".k-grid-filter");
}
link.attr("tabindex", -1).on("click" + NS, proxy(that._click, that));
}
that.link = link || $();
that.dataSource = options.dataSource;
that.field = options.field || element.attr(kendo.attr("field"));
that.model = that.dataSource.reader.model;
that._parse = function(value) {
return 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) { // get the first operator
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) { // force creation if used in column menu
that._init();
} else {
that.refresh(); //refresh if DataSource is fitered before menu is created
}
},
_init: function() {
var that = this,
ui = that.options.ui,
setUI = isFunction(ui),
role;
that.pane = that.element.closest(kendo.roleSelector("pane")).data("kendoMobilePane");
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 });
},
_createForm: function(role) {
var that = this,
options = that.options,
operators = that.operators || {},
type = that.type;
operators = operators[type] || options.operators[type];
that.form = $('')
.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: that.options.closeCallback
}).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 = $("
")
.html(kendo.template(type === "boolean" ? booleanMobileTemplate : defaultMobileTemplate)({
field: 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) {
//NOTE: binding the form element directly causes weird error in IE when grid is bound through MVVM and column is sorted
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();
}
}
if (that.view) {
that.view.purge();
}
that.link.unbind(NS);
if (that._refreshHandler) {
that.dataSource.unbind("change", that._refreshHandler);
}
},
_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;
},
_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.field);
filters = $.grep(filters, function(filter) {
return filter.value !== "" && filter.value != null;
});
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();
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"
},
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"
},
date: {
eq: EQ,
neq: NEQ,
gte: "Is after or equal to",
gt: "Is after",
lte: "Is before or equal to",
lt: "Is before"
},
enums: {
eq: EQ,
neq: NEQ
}
},
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"
}
}
});
ui.plugin(FilterMenu);
})(window.kendo.jQuery);
kendo_module({
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 + " > .k-link",
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(
"#= content(item) #
"
),
group: template(
"" +
"#= renderItems(data) #" +
" "
),
itemWrapper: template(
"<#= tag(item) # class='#= textClass(item, group) #' #= contentUrl(item) ##= textAttributes(item) #>" +
"#= image(item) ##= sprite(item) ##= text(item) #" +
"#= arrow(data) #" +
"#= tag(item) #>"
),
item: template(
"" +
"#= itemWrapper(data) #" +
"# if (item.items) { #" +
"#= subGroup({ items: item.items, panelBar: panelBar, group: { expanded: item.expanded } }) #" +
"# } else if (item.content || item.contentUrl) { #" +
"#= renderContent(data) #" +
"# } #" +
" "
),
image: template(" "),
arrow: template(" "),
sprite: template(" "),
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(" ");
});
}
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);
}
kendo.notify(that);
},
events: [
EXPAND,
COLLAPSE,
SELECT,
ACTIVATE,
ERROR,
CONTENTLOAD
],
options: {
name: "PanelBar",
animation: {
expand: {
effects: "expand:vertical",
duration: 200
},
collapse: { // if collapse animation effects are defined, they will be used instead of expand.reverse
duration: 200
}
},
expandMode: "multiple"
},
destroy: function() {
Widget.fn.destroy.call(this);
this.element.off(NS);
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 = {};
useAnimation = useAnimation !== false;
element = this.element.find(element);
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 = {};
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();
}
that.element
.find(element)
.each(function () {
var item = $(this),
link = item.children(LINKSELECTOR);
if (item.hasClass(DISABLEDCLASS)) {
return that;
}
that._updateSelected(link);
});
return that;
},
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) {
if (focused[0].id === id) {
focused.removeAttr("id");
}
focused
.children(LINKSELECTOR)
.removeClass(FOCUSEDCLASS);
}
if (candidate) {
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.next();
if (group[0]) {
next = group.children("." + FIRST);
}
if (!next[0]) {
next = item.parent(VISIBLEGROUP).parent(ITEM).next();
}
if (!next[0] || !next.is(":visible")) {
next = this._first();
}
if (next.hasClass(DISABLEDCLASS)) {
next = this._nextItem(next);
}
return next;
},
_prevItem: function(item) {
if (!item) {
return this._last();
}
var prev = item.prev(),
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 (plain || $.isArray(item)) { // is JSON
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 {
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) { // IE10 doesn't apply list-style: none on invisible items otherwise.
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); //url can be undefined
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 = " ";
if (contentUrls && contentUrls[index] && item[0].parentNode == root) {
wrapElement = '';
}
item
.contents() // exclude groups, real links, templates and empty text nodes
.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),
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)) {
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) {
return;
}
element
.parent()
.attr(ARIA_EXPANDED, !visibility)
.attr(ARIA_HIDDEN, visibility)
.toggleClass(defaultState, 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 });
} else {
animation = extend( { complete: function (element) {
that._triggerEvent(ACTIVATE, element.closest(ITEM));
} }, animation );
}
element
.kendoStop(true, true)
.kendoAnimate( animation );
},
_collapseAllExpanded: function (item) {
var that = this, children, stopExpand = false;
if (item.children(LINKSELECTOR).hasClass("k-header")) {
var groups = item.find(GROUPS).add(item.find(CONTENTS));
if (groups.is(VISIBLE)) {
stopExpand = true;
}
if (!(groups.is(VISIBLE) || groups.length === 0)) {
children = $(that.element).children();
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) {
try {
contentElement.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");
}
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);
},
_animations: function(options) {
if (options && ("animation" in options) && !options.animation) {
options.animation = { expand: { effects: {} }, collapse: { hide: true, effects: {} } };
}
}
});
// client-side rendering
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);
kendo_module({
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: " "
};
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) + "%");
}
}
}
},
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]);
if (options.type !== PROGRESSTYPE.CHUNK && progressWrapperSize > 98) {
that.progressWrapper.addClass(KPROGRESSBARCOMPLETE);
}
if (options.showStatus) {
if (options.type === PROGRESSTYPE.VALUE) {
that.progressStatus.text(currentValue);
} else {
that.progressStatus.text(math.floor(that._calculatePercentage(currentValue)) + "%");
}
}
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 = DEFAULTCHUNKCOUNT;
}
html += "";
for (var i = options.chunkCount - 1; i >= 0; i--) {
html += " ";
}
html += " ";
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 = $("
").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);
kendo_module({
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",
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(
"#= content(item) #
"
),
itemWrapper: template(
"<#= tag(item) # class='k-link'#= contentUrl(item) ##= textAttributes(item) #>" +
"#= image(item) ##= sprite(item) ##= text(item) #" +
"#= tag(item) #>"
),
item: template(
"" +
"#= itemWrapper(data) #" +
" "
),
image: template(" "),
sprite: template(" "),
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() // exclude groups, real links, templates and empty text nodes
.filter(function() { return (!this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !trim(this.nodeValue))); })
.wrapAll(" ");
}
});
}
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);
}
var TabStrip = Widget.extend({
init: function(element, options) {
var that = this;
Widget.fn.init.call(that, element, options);
that._animations(that.options);
if (that.element.is("ul")) {
that.wrapper = that.element.wrapAll("
").parent();
} else {
that.wrapper = that.element;
}
options = that.options;
that._isRtl = kendo.support.isRtl(that.wrapper);
that._tabindex();
that._updateClasses();
that._dataSource();
if (options.dataSource) {
that.dataSource.fetch();
}
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("keydown" + NS, $.proxy(that._keydown, that))
.on("focus" + NS, $.proxy(that._active, that))
.on("blur" + NS, function() { that._current(null); });
that.wrapper.children(".k-tabstrip-items")
.on(CLICK + NS, ".k-state-disabled .k-link", false)
.on(CLICK + NS, " > " + NAVIGATABLEITEMS, function(e) {
if (that.wrapper[0] !== document.activeElement) {
that.wrapper.focus();
}
if (that._click($(e.currentTarget))) {
e.preventDefault();
}
});
var selectedItems = that.tabGroup.children("li." + ACTIVESTATE),
content = that.contentHolder(selectedItems.index());
if (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";
}
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) {
this.options.dataSource = dataSource;
this._dataSource();
dataSource.fetch();
},
_animations: function(options) {
if (options && ("animation" in options) && !options.animation) {
options.animation = { open: { effects: {} }, close: { effects: {} } }; // No animation
}
},
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 animation = this.options.animation;
this._animations(options);
options.animation = extend(true, animation, options.animation);
Widget.fn.setOptions.call(this, options);
},
events: [
SELECT,
ACTIVATE,
ERROR,
CONTENTLOAD,
"change",
"dataBinding",
"dataBound"
],
options: {
name: "TabStrip",
dataTextField: "",
dataContentField: "",
dataImageUrlField: "",
dataUrlField: "",
dataSpriteCssClass: "",
dataContentUrlField: "",
animation: {
open: {
effects: "expand:vertical fadeIn",
duration: 200
},
close: { // if close animation effects are defined, they will be used instead of open.reverse
duration: 200
}
},
collapsible: false
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
if (that._refreshHandler) {
that.dataSource.unbind("change", that._refreshHandler);
}
that.wrapper.off(NS);
kendo.destroy(that.wrapper);
},
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) {
that.tabGroup.append(this);
that.wrapper.append(inserted.contents[idx]);
});
updateFirstLast(that.tabGroup);
that._updateContentElements();
return that;
},
insertBefore: function (tab, referenceTab) {
var that = this,
inserted = that._create(tab),
referenceContent = $(that.contentElement(referenceTab.index()));
each(inserted.tabs, function (idx) {
referenceTab.before(this);
referenceContent.before(inserted.contents[idx]);
});
updateFirstLast(that.tabGroup);
that._updateContentElements();
return that;
},
insertAfter: function (tab, referenceTab) {
var that = this,
inserted = that._create(tab),
referenceContent = $(that.contentElement(referenceTab.index()));
each(inserted.tabs, function (idx) {
referenceTab.after(this);
referenceContent.after(inserted.contents[idx]);
});
updateFirstLast(that.tabGroup);
that._updateContentElements();
return that;
},
remove: function (elements) {
var that = this,
type = typeof elements,
contents = $();
if (type === "string") {
elements = that.tabGroup.find(elements);
} else if (type === "number") {
elements = that.tabGroup.children().eq(elements);
}
elements.each(function () {
contents.push(that.contentElement($(this).index()));
});
elements.remove();
contents.remove();
that._updateContentElements();
return that;
},
_create: function (tab) {
var plain = $.isPlainObject(tab),
that = this, tabs, contents;
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 (value.content || value.contentUrl) {
return $(TabStrip.renderContent({
item: extend(value, { index: idx })
}));
}
});
} else {
tabs = $(tab);
contents = $("
");
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 = $("").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 // Remove empty text nodes
.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 || [],
tabStripID = that.element.attr("id") || kendo.guid(),
contentElements = that.wrapper.children("div");
that.tabGroup.find(".k-item").each(function(idx) {
var currentContent = contentElements.eq(idx),
id = tabStripID + "-" + (idx+1);
this.setAttribute("aria-controls", id);
if (!currentContent.length && contentUrls[idx]) {
$("
").appendTo(that.wrapper).attr("id", id);
} else {
currentContent.attr("id", id);
if (!$(this).children(".k-loading")[0] && !contentUrls[idx]) {
$(" ").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"); // refresh the contents
if (kendo.kineticScrollNeeded && kendo.mobile.ui.Scroller) {
kendo.touchScroller(that.contentElements);
that.contentElements = that.contentElements.children(".km-scroll-container");
}
},
_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;
},
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) {
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 });
// deactivate previously active tab
if (kendo.size(animation.effects)) {
oldTab.kendoRemoveClass(ACTIVESTATE, { duration: close.duration });
item.kendoRemoveClass(HOVERSTATE, { duration: close.duration });
} else {
oldTab.removeClass(ACTIVESTATE);
item.removeClass(HOVERSTATE);
}
// handle content elements
var contentAnimators = that.contentAnimators;
if (that.inRequest) {
that.xhr.abort();
that.inRequest = false;
}
if (contentAnimators.length === 0) {
oldTab.removeClass(TABONTOP);
item.addClass(TABONTOP) // change these directly to bring the tab on top.
.css("z-index");
item.addClass(ACTIVESTATE);
that._current(item);
that.trigger("change");
return false;
}
var visibleContents = contentAnimators.filter("." + ACTIVESTATE),
contentHolder = that.contentHolder(itemIndex),
contentElement = contentHolder.closest(".k-content");
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 () {
oldTab.removeClass(TABONTOP);
item.addClass(TABONTOP) // change these directly to bring the tab on top.
.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);
contentElement
.addClass(ACTIVESTATE)
.removeAttr("aria-hidden")
.kendoStop(true, true)
.attr("aria-expanded", true)
.kendoAnimate( extend({ init: function () {
that.trigger(ACTIVATE, { item: item[0], contentElement: contentHolder[0] });
} }, animation, { complete: function () { item.removeAttr("data-animating"); } } ) );
},
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");
});
}
};
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,
idTest = new RegExp("-" + (itemIndex + 1) + "$");
if (contentElements) {
for (var i = 0, len = contentElements.length; i < len; i++) {
if (idTest.test(contentElements.closest(".k-content")[i].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 = {},
fakeProgress = false,
statusIcon = element.find(".k-loading").removeClass("k-complete");
if (!statusIcon[0]) {
statusIcon = $(" ").prependTo(element);
}
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(),
loaded = 10,
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);
}
});
}
if (!current.progress) {
fakeProgress = setInterval(function () {
current.progress({ lengthComputable: true, loaded: Math.min(loaded, 100), total: 100 });
}, 100);
loaded += 10;
}
return request;
},
progress: function(evt) {
if (evt.lengthComputable) {
var percent = parseInt((evt.loaded / evt.total * 100), 10) + "%";
statusIcon.width(percent);
}
},
error: function (xhr, status) {
if (that.trigger("error", { xhr: xhr, status: status })) {
this.complete();
}
},
complete: function () {
that.inRequest = false;
clearInterval(fakeProgress);
statusIcon.css("width", "");
},
success: function (data) {
statusIcon.addClass("k-complete");
try {
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.trigger(CONTENTLOAD, { item: element[0], contentElement: content[0] });
}
});
}
});
// client-side rendering
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);
kendo_module({
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,
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 = " ",
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.ul = $('')
.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 = $("
")
.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.template = kendo.template('#=data# ', { 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, length);
},
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);
}
}
html += template(toString(start, format, options.culture));
}
that._html(html, length);
},
bind: function() {
var that = this,
dates = that.options.dates;
if (dates && dates[0]) {
that.dataBind(dates);
} else {
that.refresh();
}
},
_html: function(html, length) {
var that = this;
that.ul[0].innerHTML = html;
that._height(length);
that.current(null);
that.select(that._value);
},
scroll: function(item) {
if (!item) {
return;
}
var ul = this.ul[0],
itemOffsetTop = item.offsetTop,
itemOffsetHeight = item.offsetHeight,
ulScrollTop = ul.scrollTop,
ulOffsetHeight = ul.clientHeight,
bottomDistance = itemOffsetTop + itemOffsetHeight,
touchScroller = this._touchScroller,
yDimension;
if (touchScroller) {
yDimension = touchScroller.dimensions.y;
if (yDimension.enabled && itemOffsetTop > yDimension.size) {
itemOffsetTop = itemOffsetTop - yDimension.size + itemOffsetHeight + 4;
touchScroller.scrollTo(0, -itemOffsetTop);
}
} else {
ul.scrollTop = ulScrollTop > itemOffsetTop ?
itemOffsetTop : bottomDistance > (ulScrollTop + ulOffsetHeight) ?
bottomDistance - ulOffsetHeight : ulScrollTop;
}
},
select: function(li) {
var that = this,
options = that.options,
current = that._current;
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;
}
}
that.current(li);
},
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);
if (!e.isDefaultPrevented()) {
that.select(li);
that.options.change(li.text(), true);
that.close();
}
},
_height: function(length) {
if (length) {
var that = this,
list = that.list,
parent = list.parent(".k-animation-container"),
height = that.options.height;
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 = kendo.parseDate(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)) { // getComputedStyle returns different box in FF and IE.
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)
}));
that._touchScroller = kendo.touchScroller(that.popup.element);
},
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;
normalize(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": "textbox",
"aria-haspopup": true,
"aria-expanded": false,
"aria-owns": timeView._timeViewID
});
disabled = element.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,
timeView = that.timeView,
timeViewOptions = timeView.options;
Widget.fn.setOptions.call(that, options);
normalize(that.options);
timeView.options = extend(timeViewOptions, that.options, {
active: timeViewOptions.active,
change: timeViewOptions.change,
close: timeViewOptions.close,
open: timeViewOptions.open
});
timeView.ul[0].innerHTML = "";
},
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("blur" + 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();
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_icon: function() {
var that = this,
element = that.element,
arrow;
arrow = element.next("span.k-select");
if (!arrow[0]) {
arrow = $('select ').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);
}
},
_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._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);
kendo_module({
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 = " ",
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(1900, 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;
normalize(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": "textbox",
"aria-haspopup": true,
"aria-expanded": false
});
that._midnight = getMilliseconds(options.min) + getMilliseconds(options.max) === 0;
disabled = element.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, "G")#'
},
events: [
OPEN,
CLOSE,
CHANGE
],
setOptions: function(options) {
var that = this,
dateViewOptions = that.dateView.options,
timeViewOptions = that.timeView.options,
min, max, currentValue;
Widget.fn.setOptions.call(that, options);
normalize(that.options);
options = that.options;
min = options.min;
max = 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);
}
extend(dateViewOptions, options, {
change: dateViewOptions.change,
close: dateViewOptions.close,
open: dateViewOptions.open
});
extend(timeViewOptions, options, {
format: options.timeFormat,
active: timeViewOptions.active,
change: timeViewOptions.change,
close: timeViewOptions.close,
open: timeViewOptions.open,
min: min,
max: max
});
that.timeView.ul[0].innerHTML = "";
},
_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("blur" + 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;
value = that._update(value);
if (+that._old != +value) {
that._old = value;
that._oldText = that.element.val();
that.trigger(CHANGE);
// trigger the DOM change event so any subscriber gets notified
that.element.trigger(CHANGE);
}
},
_option: function(option, value) {
var that = this,
options = that.options,
timeView = that.timeView,
timeViewOptions = timeView.options,
current = that._value || that._old;
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);
that._midnight = getMilliseconds(options.min) + getMilliseconds(options.max) === 0;
if (current && isEqualDatePart(value, current)) {
if (that._midnight && option == "max") {
timeViewOptions[option] = MAX;
timeView.dataBind([MAX]);
return;
}
timeViewOptions[option] = value;
} 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,
date = parse(value, options.parseFormats, options.culture),
rebind, timeViewOptions, old, skip, formattedValue;
if (+date === +that._value) {
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;
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);
}
},
_views: function() {
var that = this,
element = that.element,
options = that.options,
id = element.attr("id"),
dateView, timeView,
div, ul,
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;
if (msValue === msMin || msValue === msMax) {
current = new DATE(+that._value);
current.setFullYear(value.getFullYear(), value.getMonth(), value.getDate());
if (isInRange(current, msMin, msMax)) {
value = current;
}
}
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 (that.element.val() !== that._oldText) {
date = parse(element.val(), options.parseFormats, options.culture);
if (!date) {
that.dateView.value(date);
} else {
that.dateView._current = date;
that.dateView.calendar._focus(date);
}
}
div.attr(ARIA_HIDDEN, false);
element.attr(ARIA_EXPANDED, true)
.attr(ARIA_OWNS, dateView._dateViewID);
}
}
}));
div = dateView.div;
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),
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 {
ul.attr(ARIA_HIDDEN, false);
element.attr(ARIA_EXPANDED, true)
.attr(ARIA_OWNS, timeView._timeViewID);
}
},
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 = $('select select ').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._form = form.on("reset", that._resetHandler);
}
},
_template: function() {
this._ariaTemplate = kendo.template(this.options.ARIATemplate);
},
_updateARIA: function(date) {
this.element.attr("aria-label", this._ariaTemplate({ current: date }));
}
});
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,
timeFormat;
options.format = extractFormat(options.format || patterns.g);
options.timeFormat = timeFormat = extractFormat(options.timeFormat || patterns.t);
kendo.DateView.normalize(options);
if ($.inArray(timeFormat, options.parseFormats) === -1) {
options.parseFormats.splice(1, 0, timeFormat);
}
}
ui.plugin(DateTimePicker);
})(window.kendo.jQuery);
kendo_module({
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", "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",
NAVIGATE = "navigate",
EXPAND = "expand",
CHANGE = "change",
ERROR = "error",
CHECKED = "checked",
COLLAPSE = "collapse",
DRAGSTART = "dragstart",
DRAG = "drag",
DROP = "drop",
DRAGEND = "dragend",
DATABOUND = "dataBound",
CLICK = "click",
VISIBILITY = "visibility",
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 : //DOM2
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.children("div").find(".k-checkbox:first :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 = $("
").prependTo(node);
}
if (!toggleButton.length && group.length) {
toggleButton = $(" ").prependTo(wrapper);
} else if (!group.length || !group.children().length) {
toggleButton.remove();
group.remove();
}
if (checkbox.length) {
$(" ").appendTo(wrapper).append(checkbox);
}
if (!innerWrapper.length) {
innerWrapper = node.children("a").eq(0).addClass("k-in");
if (!innerWrapper.length) {
innerWrapper = $(" ");
}
innerWrapper.appendTo(wrapper);
if (wrapper.length) {
moveContents(wrapper[0].nextSibling, innerWrapper[0]);
}
}
}
TreeView = Widget.extend({
init: function (element, options) {
var that = this,
dataInit,
inferred = false,
hasDataSource = options && !!options.dataSource,
list;
if (isArray(options)) {
dataInit = true;
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();
// render treeview if it's not already rendered
if (!element.hasClass(KTREEVIEW)) {
that._wrapper();
if (list) {
that.root = element;
that._group(that.wrapper);
}
} else {
// otherwise just initialize properties
that.wrapper = element;
that.root = element.children("ul").eq(0);
}
that._tabindex();
if (!that.wrapper.filter("[role=tree]").length) {
that.wrapper.attr("role", "tree");
}
that._dataSource(inferred);
that._attachEvents();
that._dragging();
if (!inferred) {
if (options.autoBind) {
that._progress(true);
that.dataSource.fetch();
}
} else {
that._attachUids();
}
if (options.checkboxes && options.checkboxes.checkChildren) {
that.updateIndeterminate();
}
if (that.element[0].id) {
that._ariaId = kendo.format("{0}_tv_active", that.element[0].id);
}
},
_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 :checkbox,.k-plus,.k-minus", proxy(that._mousedown, that))
.on("change" + NS, ".k-checkbox :checkbox", proxy(that._checkboxChange, that))
.on("click" + NS, ".k-checkbox :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);
}
},
_attachUids: function(root, dataSource) {
var that = this,
data,
uidAttr = kendo.attr("uid");
root = root || that.root;
dataSource = dataSource || that.dataSource;
data = dataSource.view();
root.children("li").each(function(index, item) {
item = $(item).attr(uidAttr, data[index].uid);
item.attr("role", "treeitem");
that._attachUids(item.children("ul"), data[index].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) {
this.dragging = new TreeViewDragAndDrop(this);
} else if (!enabled && dragging) {
dragging.destroy();
this.dragging = null;
}
},
_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) {
var result = "k-in";
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) {
return group.expanded !== true ? " style='display:none'" : "";
},
groupCssClass: function(group) {
var cssClass = "k-group";
if (group.firstLevel) {
cssClass += " k-treeview-lines";
}
return cssClass;
},
dragClue: templateNoWith(
""
),
group: templateNoWith(
"" +
"#= data.renderItems(data) #" +
" "
),
itemContent: templateNoWith(
"# var imageUrl = " + fieldAccessor("imageUrl") + "(data.item); #" +
"# var spriteCssClass = " + fieldAccessor("spriteCssClass") + "(data.item); #" +
"# if (imageUrl) { #" +
" " +
"# } #" +
"# if (spriteCssClass) { #" +
" " +
"# } #" +
"#= data.treeview.template(data) #"
),
itemElement: templateNoWith(
"# var item = data.item, r = data.r; #" +
"# var url = " + fieldAccessor("url") + "(item); #" +
"" +
"# if (item.hasChildren) { #" +
" " +
"# } #" +
"# if (data.treeview.checkboxes) { #" +
"" +
"#= data.treeview.checkboxes.template(data) #" +
" " +
"# } #" +
"# var tag = url ? 'a' : 'span'; #" +
"# var textAttr = url ? ' href=\\'' + url + '\\'' : ''; #" +
"<#=tag# class='#= r.textClass(item) #'#= textAttr #>" +
"#= r.itemContent(data) #" +
"#=tag#>" +
"
"
),
item: templateNoWith(
"# var item = data.item, r = data.r; #" +
"" +
"#= r.itemElement(data) #" +
" "
),
loading: templateNoWith(
"
Loading..."
),
retry: templateNoWith(
"Request failed. " +
"Retry "
)
};
},
items: function() {
return this.element.find(".k-item");
},
setDataSource: function(dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
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
],
options: {
name: "TreeView",
dataSource: {},
animation: {
expand: {
effects: "expand:vertical",
duration: 200
}, collapse: {
duration: 100
}
},
dragAndDrop: false,
checkboxes: false,
autoBind: true,
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;
}
},
// generates accessor function for a given field name, honoring the data*Field arrays
_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;
}
checkboxes(node)
.data("indeterminate", !all)
.prop("indeterminate", !all)
.prop(CHECKED, all && siblings[0].checked);
},
updateIndeterminate: function(node) {
// top-down update of inital indeterminate state for all nodes
node = node || this.wrapper;
var subnodes = subGroup(node).children(), i;
if (subnodes.length) {
for (i = 0; i < subnodes.length; i++) {
this.updateIndeterminate(subnodes.eq(i));
}
this._setIndeterminate(node);
}
},
_bubbleIndeterminate: function(node) {
// bottom-up setting of indeterminate state of parent nodes
if (!node.length) {
return;
}
var parentNode = this.parent(node),
checkbox;
if (parentNode.length) {
this._setIndeterminate(parentNode);
checkbox = parentNode.children("div").find(".k-checkbox :checkbox");
if (checkbox.prop("indeterminate") === false) {
this.dataItem(parentNode).set(CHECKED, checkbox.prop(CHECKED));
} else {
this.dataItem(parentNode).checked = false;
}
this._bubbleIndeterminate(parentNode);
}
},
_checkboxChange: function(e) {
var checkbox = $(e.target),
isChecked = checkbox.prop(CHECKED),
node = checkbox.closest(NODE);
this.dataItem(node).set(CHECKED, isChecked);
},
_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;
// suppress initial focus state on touch devices (until keyboard is used)
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();
// expanded node with no children
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: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('
').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);
// iterate over child nodes
that._group(node);
});
},
_checkboxes: function() {
var options = this.options,
checkboxOptions = options.checkboxes,
checkboxTemplate;
if (checkboxOptions || options.checkboxTemplate) {
if (options.checkboxTemplate) {
checkboxTemplate = options.checkboxTemplate;
} else {
checkboxTemplate = " ";
}
checkboxOptions = extend({
template: checkboxTemplate
}, options.checkboxes);
if (typeof checkboxOptions.template == STRING) {
checkboxOptions.template = template(checkboxOptions.template);
}
options.checkboxes = checkboxOptions;
}
},
_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;
// li
node.removeClass("k-first k-last")
.addClass(templates.wrapperCssClass(groupData, nodeData));
// div
wrapper.removeClass("k-top k-mid k-bot")
.addClass(templates.cssClass(groupData, nodeData));
// span
wrapper.children(".k-in").removeClass("k-in k-state-default k-state-disabled")
.addClass(templates.textClass(nodeData));
// toggle button
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;
}
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());
// render sub-nodes
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;
},
_updateNode: function(field, items) {
var that = this, i, node, item,
isChecked, isCollapsed,
context = { treeview: that.options, item: item },
shouldUpdate = false;
function access() {
shouldUpdate = true;
}
function setCheckedState(root, state) {
root.find(".k-checkbox :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, true);
} else {
node.attr(ARIASELECTED, false);
}
} else {
if ($.inArray(field, that.options.dataTextField) >= 0) {
shouldUpdate = true;
} else {
context.item = items[0];
context.item.bind("get", access);
that.templates.itemContent(context);
context.item.unbind("set", access);
}
for (i = 0; i < items.length; i++) {
context.item = item = items[i];
if (field == "spriteCssClass" || field == "imageUrl" || shouldUpdate) {
that.findByUid(item.uid).find(">div>.k-in").html(that.templates.itemContent(context));
}
if (field == CHECKED) {
node = that.findByUid(item.uid);
isChecked = item[field];
setCheckedState(node.children("div"), 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(that.findByUid(item.uid), item, item[field]);
} else if (field == "enabled") {
node = that.findByUid(item.uid);
node.find(".k-checkbox :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.removeAttr(ARIASELECTED)
.attr(ARIADISABLED, true);
}
that._updateNodeClasses(node, {}, { enabled: item[field], expanded: !isCollapsed });
}
}
}
},
_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) {
// insert node into DOM
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");
}
},
refresh: function(e) {
var that = this,
parentNode = that.wrapper,
node = e.node,
action = e.action,
items = e.items,
options = that.options,
loadOnDemand = options.loadOnDemand,
checkChildren = options.checkboxes && options.checkboxes.checkChildren,
i;
if (e.field) {
return that._updateNode(e.field, items);
}
if (node) {
parentNode = that.findByUid(node.uid);
that._progress(parentNode, false);
}
if (checkChildren && action != "remove" && 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") {
that._remove(that.findByUid(items[0].uid), false);
} else {
if (node) {
subGroup(parentNode).empty();
if (!items.length) {
updateNodeHtml(parentNode);
} else {
this._appendItems(e.index, items, parentNode);
this._bubbleIndeterminate(subGroup(parentNode).children().last());
}
} else {
that.root = that.wrapper.html(that._renderGroup({
items: items,
group: {
firstLevel: true,
expanded: true
}
})).children("ul");
}
}
for (i = 0; i < items.length; i++) {
if (!loadOnDemand || items[i].expanded) {
items[i].load();
}
}
that.trigger(DATABOUND, {
node: node ? parentNode : undefined
});
},
_error: function(e) {
var that = this,
node = e.node && that.findByUid(e.node.uid);
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(this.templates.retry);
}
},
_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);
dataItem.set("selected", false);
delete dataItem.selected;
});
if (node.length) {
that.dataItem(node).set("selected", true);
}
that.trigger(CHANGE);
},
_toggle: function(node, dataItem, expand) {
var that = this,
options = that.options,
contents = nodeContents(node),
direction = expand ? "expand" : "collapse",
animation = options.animation[direction],
loaded;
if (contents.data("animating")) {
return;
}
if (!that._trigger(direction, node)) {
that._expanded(node, expand);
loaded = dataItem && dataItem.loaded();
if (loaded && contents.children().length > 0) {
that._updateNodeClasses(node, {}, { expanded: expand });
if (contents.css("display") == (expand ? "block" : "none")) {
return;
}
if (!expand) {
contents.css("height", contents.height()).css("height");
}
contents.kendoStop(true, true).kendoAnimate(extend({ reset: true }, animation, {
complete: function() {
if (expand) {
contents.css("height", "");
}
}
}));
} else if (expand) {
if (options.loadOnDemand) {
that._progress(node, true);
}
contents.remove();
dataItem.load();
}
}
},
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.element.off(NS);
that._unbindDataSource();
if (that.dragging) {
that.dragging.destroy();
}
kendo.destroy(that.element);
},
_expanded: function(node, value) {
var expandedAttr = kendo.attr("expanded"),
dataItem = this.dataItem(node);
if (arguments.length == 1) {
return node.attr(expandedAttr) === "true" || (dataItem && dataItem.expanded);
}
if (nodeContents(node).data("animating")) {
return;
}
if (dataItem) {
dataItem.set("expanded", value);
// necessary when expanding an item yields an error and the item is not expanded as a result
value = dataItem.expanded;
}
if (value) {
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;
if (arguments.length == 1) {
showProgress = node;
if (showProgress) {
element.html(this.templates.loading);
} 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;
if (parentNode && parentNode[0] != destTreeview.element[0]) {
referenceDataItem = destTreeview.dataItem(parentNode);
if (!referenceDataItem.loaded()) {
destTreeview._progress(parentNode, true);
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(this, destDataSource, nodeData);
},
_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 {
// items will be converted to new Node instances
model = model.toJSON();
}
var parentNode = data.parent();
if (parentNode) {
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 that = this,
group = that.root;
success = success || $.noop;
if (parentNode) {
group = subGroup(parentNode);
}
return that._dataSourceMove(nodeData, group, parentNode, function (dataSource, model) {
var inserted;
function add() {
if (parentNode) {
that._expanded(parentNode, true);
}
var data = dataSource.data(),
index = Math.max(data.length, 0);
return that._insert(data, model, index);
}
if (!dataSource.data()) {
dataSource.one(CHANGE, function() {
success(add());
});
return null;
} else {
inserted = add();
success(inserted);
return inserted;
}
});
},
_remove: function (node, keepData) {
var that = this,
parentNode,
prevSibling, nextSibling;
node = $(node, that.element);
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) {
return this.element.find(".k-item[" + kendo.attr("uid") + "=" + uid + "]");
},
expandPath: function(path, complete) {
var dataSource = this.dataSource;
var node = dataSource.get(path[0]);
complete = complete || $.noop;
// expand loaded nodes
while (path.length > 0 && (node.expanded || node.loaded())) {
node.set("expanded", true);
path.shift();
node = dataSource.get(path[0]);
}
if (!path.length) {
return complete();
}
// expand async nodes
dataSource.bind("change", function expandLevel(e) {
// listen to the change event to know when the node has been loaded
var id = e.node && e.node.id;
// proceed if the change is caused by the last fetching
if (id && id === path[0]) {
path.shift();
if (path.length) {
dataSource.get(path[0]).set("expanded", true);
} else {
complete();
}
}
});
node.set("expanded", true);
},
_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);
}
});
function TreeViewDragAndDrop(treeview) {
var that = this;
that.treeview = treeview;
that.hovered = treeview.element;
that._draggable = new ui.Draggable(treeview.element, {
filter: "div:not(.k-state-disabled) .k-in",
hint: function(node) {
return treeview.templates.dragClue({
item: treeview.dataItem(node),
treeview: treeview.options
});
},
cursorOffset: {
left: 10,
top: kendo.support.touch || kendo.support.msPointers || kendo.support.pointers ? -40 / kendo.support.zoomLevel() : 10
},
dragstart: proxy(that.dragstart, that),
dragcancel: proxy(that.dragcancel, that),
drag: proxy(that.drag, that),
dragend: proxy(that.dragend, that)
});
}
TreeViewDragAndDrop.prototype = {
_removeTouchHover: function() {
var that = this;
if (kendo.support.touch && that.hovered) {
that.hovered.find("." + KSTATEHOVER).removeClass(KSTATEHOVER);
that.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) {
var that = this,
treeview = that.treeview,
sourceNode = that.sourceNode = e.currentTarget.closest(NODE);
if (treeview.trigger(DRAGSTART, { sourceNode: sourceNode[0] })) {
e.preventDefault();
}
that.dropHint = $("
")
.css(VISIBILITY, "hidden")
.appendTo(treeview.element);
},
drag: function (e) {
var that = this,
treeview = that.treeview,
sourceNode = that.sourceNode,
dropTarget = that.dropTarget = $(kendo.eventTarget(e)),
statusClass, closestTree = dropTarget.closest(".k-treeview"),
hoveredItem, hoveredItemPos, itemHeight, itemTop, itemContent, delta,
insertOnTop, insertOnBottom, addChild;
if (!closestTree.length) {
// dragging node outside of treeview
statusClass = "k-denied";
that._removeTouchHover();
} else if ($.contains(sourceNode[0], dropTarget[0])) {
// dragging node within itself
statusClass = "k-denied";
} else {
// moving or reordering node
statusClass = "k-insert-middle";
hoveredItem = dropTarget.closest(".k-top,.k-mid,.k-bot");
if (hoveredItem.length) {
itemHeight = hoveredItem.outerHeight();
itemTop = kendo.getOffset(hoveredItem).top;
itemContent = dropTarget.closest(".k-in");
delta = itemHeight / (itemContent.length > 0 ? 4 : 2);
insertOnTop = e.y.location < (itemTop + delta);
insertOnBottom = (itemTop + itemHeight - delta) < e.y.location;
that._removeTouchHover();
addChild = itemContent.length && !insertOnTop && !insertOnBottom;
that.hovered = addChild ? closestTree : false;
that.dropHint.css(VISIBILITY, addChild ? "hidden" : "visible");
itemContent.toggleClass(KSTATEHOVER, addChild);
if (addChild) {
statusClass = "k-add";
} else {
hoveredItemPos = hoveredItem.position();
hoveredItemPos.top += insertOnTop ? 0 : itemHeight;
that.dropHint
.css(hoveredItemPos)
[insertOnTop ? "prependTo" : "appendTo"](dropTarget.closest(NODE).children("div:first"));
if (insertOnTop && hoveredItem.hasClass("k-top")) {
statusClass = "k-insert-top";
}
if (insertOnBottom && hoveredItem.hasClass("k-bot")) {
statusClass = "k-insert-bottom";
}
}
} else if (dropTarget[0] != that.dropHint[0]) {
if (closestTree[0] != treeview.element[0]) {
// moving node to different treeview without children
statusClass = "k-add";
} else {
statusClass = "k-denied";
}
}
}
treeview.trigger(DRAG, {
sourceNode: sourceNode[0],
dropTarget: dropTarget[0],
pageY: e.y.location,
pageX: e.x.location,
statusClass: statusClass.substring(2),
setStatusClass: function (value) {
statusClass = value;
}
});
if (statusClass.indexOf("k-insert") !== 0) {
that.dropHint.css(VISIBILITY, "hidden");
}
that._hintStatus(statusClass);
},
dragcancel: function() {
this.dropHint.remove();
},
dragend: function () {
var that = this,
treeview = that.treeview,
dropPosition = "over",
sourceNode = that.sourceNode,
destinationNode,
dropHint = that.dropHint,
dropTarget = that.dropTarget,
e, dropPrevented;
if (dropHint.css(VISIBILITY) == "visible") {
dropPosition = dropHint.prevAll(".k-in").length > 0 ? "after" : "before";
destinationNode = dropHint.closest(NODE);
} else if (dropTarget) {
destinationNode = dropTarget.closest(NODE);
// moving node to root element
if (!destinationNode.length) {
destinationNode = dropTarget.closest(".k-treeview");
}
}
e = {
sourceNode: sourceNode[0],
destinationNode: destinationNode[0],
valid: that._hintStatus() != "k-denied",
setValid: function(newValid) {
this.valid = newValid;
},
dropTarget: dropTarget[0],
dropPosition: dropPosition
};
dropPrevented = treeview.trigger(DROP, e);
dropHint.remove();
that._removeTouchHover();
if (!e.valid || dropPrevented) {
that._draggable.dropped = e.valid;
return;
}
that._draggable.dropped = true;
function triggerDragEnd(sourceNode) {
treeview.trigger(DRAGEND, {
sourceNode: sourceNode && sourceNode[0],
destinationNode: destinationNode[0],
dropPosition: dropPosition
});
}
// perform reorder / move
// different handling is necessary because append might be async in remote bound tree
if (dropPosition == "over") {
treeview.append(sourceNode, destinationNode, triggerDragEnd);
} else {
if (dropPosition == "before") {
sourceNode = treeview.insertBefore(sourceNode, destinationNode);
} else if (dropPosition == "after") {
sourceNode = treeview.insertAfter(sourceNode, destinationNode);
}
triggerDragEnd(sourceNode);
}
},
destroy: function() {
this._draggable.destroy();
}
};
ui.plugin(TreeView);
})(window.kendo.jQuery);
kendo_module({
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",
PRECISION = 3,
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 = 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}";
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._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), // left arrow
40: step(-options.smallStep), // down arrow
39: step(+1 * rtlDirectionSign * options.smallStep), // right arrow
38: step(+options.smallStep), // up arrow
35: setValue(options.max), // end
36: setValue(options.min), // home
33: step(+options.largeStep), // page up
34: step(-options.largeStep) // page down
};
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();
},
_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 ((1000 * options.largeStep) % (1000 * 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];
if (value % options.smallStep === 0 && value % options.largeStep === 0) {
item.addClass("k-tick-large")
.html("" + item.attr("title") + " ");
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;
}
//set step size
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;
that._values[i] = val += options.smallStep;
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).val(options.selectionStart);
inputs.eq(1).val(options.selectionEnd);
} else {
element.val(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);
}
}
});
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 "";
}
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 "" + options[type + "ButtonTitle"] + " ";
}
function createSliderItems (options, distance) {
var result = "",
count = math.floor(round(distance / options.smallStep)) + 1,
i;
for(i = 0; i < count; i++) {
result += " ";
}
result += " ";
return result;
}
function createTrack (options, element) {
var dragHandleCount = element.is("input") ? 1 : 2,
firstDragHandleTitle = dragHandleCount == 2 ? options.leftDragHandleTitle : options.dragHandleTitle;
return "
" +
"
Drag " +
(dragHandleCount > 1 ? "
Drag " : "") +
"
";
}
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 round(value) {
value = parseFloat(value, 10);
var 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;
}
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.val(options.min);
}
options.value = math.max(math.min(options.value, options.max), options.min);
dragHandle = that.wrapper.find(DRAG_HANDLE);
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))];
},
destroy: function() {
var that = this;
Widget.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();
}
});
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) {
// add reference to the last active drag handle.
this.owner._activeDragHandle = this;
// HACK to initiate click on the line
this.draggable.userEvents.cancel();
this.draggable.userEvents._start(e);
},
_dragstart: function(e) {
var that = this,
owner = that.owner,
options = that.options;
if (!options.enabled) {
e.preventDefault();
return;
}
// add reference to the last active drag handle.
this.owner._activeDragHandle = this;
owner.element.off(MOUSE_OVER);
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(); // if user changes window while tooltip is visible, a second one will be created
that.tooltipDiv = $("
").appendTo(document.body);
html = owner._getFormattedValue(that.val || owner.value(), that);
if (!that.type) {
colloutCssClass = "k-callout-" + (owner._isHorizontal ? 's' : 'e');
that.tooltipInnerDiv = "
";
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);
}
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";
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).val(options.min);
}
if (!defined(options.selectionEnd) || options.selectionEnd === null) {
options.selectionEnd = options.max;
inputs.eq(1).val(options.max);
}
var dragHandles = that.wrapper.find(DRAG_HANDLE);
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")) {
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() {
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(selectionStartValue, 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);
});
},
destroy: function() {
var that = this;
Widget.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);
kendo_module({
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 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._resizeHandler = function() {
that.resize();
};
that._initPanes();
that.resizing = new PaneResizing(that);
that.element.triggerHandler("init" + NS);
},
events: [
EXPAND,
COLLAPSE,
CONTENTLOAD,
ERROR,
RESIZE,
LAYOUTCHANGE
],
_attachEvents: function() {
var that = this,
orientation = that.options.orientation;
// do not use delegated events to increase performance of nested elements
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, function() { that._panes().append("
"); })
.on("mouseup" + NS, function() { that._panes().children(".k-splitter-overlay").remove(); })
.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", that._resizeHandler);
},
_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("resize", that._resizeHandler);
},
options: {
name: "Splitter",
orientation: HORIZONTAL,
panes: []
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
that._detachEvents();
if (that.resizing) {
that.resizing.destroy();
}
kendo.destroy(that.element);
},
_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 that = this,
panesConfig = that.options.panes || [];
that.element
.addClass("k-widget").addClass("k-splitter")
.children(":not(script)")
.each(function (index, pane) {
var config = panesConfig && panesConfig[index];
that._initPane(pane, config);
})
.end();
that.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(" ");
if (kendo.isLocalUrl(url)) {
jQuery.ajax({
url: url,
data: data || {},
type: "GET",
dataType: "html",
success: function (data) {
pane.html(data);
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("");
}
}
},
_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 ? "
" : "";
},
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-secondary 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)
);
},
_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() {
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]();
if (splitBarsCount === 0) {
splitBarsCount = panes.length - 1;
panes.slice(0, splitBarsCount)
.after("
");
that._updateSplitBars();
splitBars = element.children(".k-splitbar");
} else {
that._updateSplitBars();
}
// discard splitbar sizes from total size
splitBars.each(function() {
totalSize -= this[isHorizontal ? "offsetWidth" : "offsetHeight"];
});
var sizedPanesWidth = 0,
sizedPanesCount = 0,
freeSizedPanes = $();
panes.css({ position: "absolute", top: 0 })
[sizingProperty](function() {
var config = $(this).data(PANE) || {}, size;
if (config.collapsed) {
size = 0;
$(this).css("overflow", "hidden");
} else if (isFluid(config.size)) {
freeSizedPanes = freeSizedPanes.add(this);
return;
} else { // sized in px/%, not collapsed
size = parseInt(config.size, 10);
if (isPercentageSize(config.size)) {
size = Math.floor(size * totalSize / 100);
}
}
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);
// arrange panes
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(":not(script)")
.css(alternateSizingProperty, element[alternateSizingProperty]())
.each(function (i, child) {
child.style[positioningProperty] = Math.floor(sum) + "px";
sum += child[sizingDomProperty];
});
that._detachEvents();
that._attachEvents();
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();
}
return paneElement;
},
append: function(config) {
config = config || {};
var that = this,
paneElement = $("
").appendTo(that.element);
return that._addPane(config, that.options.panes.length, paneElement);
},
insertBefore: function(config, referencePane) {
referencePane = $(referencePane);
config = config || {};
var that = this,
idx = referencePane.index(".k-pane"),
paneElement = $("
").insertBefore($(referencePane));
return that._addPane(config, idx, paneElement);
},
insertAfter: function(config, referencePane) {
referencePane = $(referencePane);
config = config || {};
var that = this,
idx = referencePane.index(".k-pane"),
paneElement = $("
").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($(element).index(".k-pane"), 1);
$(element).remove();
});
that._removeSplitBars();
if (that.options.panes.length) {
that.resize();
}
}
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();
},
isResizing: function() {
return this._resizable.resizing;
},
_createHint: function(handle) {
var that = this;
return $("
")
.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);
kendo_module({
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,
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));
},
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
},
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) {
//check if source input is a DOM element. Required for some unit tests
if (!sourceInput[0].nodeType) {
return;
}
var that = this,
input = sourceInput.clone().val("");
input
.insertAfter(that.element)
.data("kendoUpload", that);
$(that.element)
.hide()
.removeAttr("id")
.off(NS);
that._activeInput(input);
},
_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));
},
_onInputChange: function(e) {
var upload = this,
input = $(e.target),
prevented = upload.trigger(SELECT, { files: inputFiles(input) });
if (prevented) {
upload._addInput(input);
input.remove();
} else {
upload._module.onSelect(e);
}
},
_onDrop: function (e) {
var dt = e.originalEvent.dataTransfer,
that = this,
droppedFiles = dt.files;
stopEvent(e);
if (droppedFiles.length > 0) {
var prevented = that.trigger(SELECT, { files: getAllFileInfo(droppedFiles) });
if (!prevented) {
that._module.onSelect({target : $(".k-dropzone", that.wrapper) }, droppedFiles);
}
}
},
_renderInitialFiles: function(files) {
var that = this;
var idx = 0;
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%');
$(".k-upload-status", fileEntry).prepend("100% ");
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 = $("" +
" " +
" " +
"" + name + " " +
" " +
" ");
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 fileList = $(".k-upload-files", that.wrapper);
var options = that.options;
var template = options.template;
var templateData;
if (fileList.length === 0) {
fileList = $("").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 = $("" + template(templateData) + " ");
fileEntry.find(".k-upload-action").addClass("k-button k-button-bare");
}
fileEntry
.appendTo(fileList)
.data(data);
if (!that._async) {
$(".k-progress", fileEntry).width('100%');
}
if (!that.multiple && existingFileEntries.length > 0) {
that._module.onRemove({target : $(existingFileEntries, that.wrapper)});
}
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(" ")
.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 $(
"" +
" " +
" "
);
}
else {
return $(
"" +
actionText +
" "
);
}
},
_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() {
this._module.onSaveSelected();
return false;
},
_onFileProgress: function(e, percentComplete) {
var progressPct;
if (!this.options.template) {
progressPct = $(".k-upload-pct", e.target);
if (progressPct.length === 0) {
$(".k-upload-status", e.target).prepend(" ");
}
$(".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(" ");
}
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 = '' + localization.headerStatusUploading +
'' + localization.statusUploading + ' ' +
' ';
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);
// Prevent submitting an empty input
input.attr("disabled", "disabled");
window.setTimeout(function() {
// Restore the input so the Upload remains functional
// in case the user cancels the form submit
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("
");
var ns = that._ns;
var dropZone = $(".k-dropzone", that.wrapper)
.append($("" + that.localization.dropFilesHere + " "))
.on("dragenter" + ns, stopEvent)
.on("dragover" + ns, function(e) { e.preventDefault(); })
.on("drop" + ns, $.proxy(this._onDrop, this));
bindDragEventWrappers(dropZone, ns,
function() { dropZone.addClass("k-dropzone-hovered"); },
function() { dropZone.removeClass("k-dropzone-hovered"); });
bindDragEventWrappers($(document), ns,
function() {
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, getAntiForgeryTokens());
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
});
},
_wrapInput: function(input) {
var that = this;
var options = that.options;
input.wrap("");
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("" + this.localization.select + " ");
return input.closest(".k-upload");
},
_checkAllComplete: function() {
if ($(".k-file.k-file-progress", this.wrapper).length === 0) {
this.trigger(COMPLETE);
}
}
});
// Synchronous upload module
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) {
var upload = this.upload;
var sourceInput = $(e.target);
upload._addInput(sourceInput);
var file = upload._enqueueFile(getFileName(sourceInput), {
"relatedInput" : sourceInput, "fileNames": inputFiles(sourceInput)
});
upload._fileAction(file, REMOVE);
},
onRemove: function(e) {
var fileEntry = getFileEntry(e);
fileEntry.data("relatedInput").remove();
this.upload._removeFileEntry(fileEntry);
}
};
// Iframe upload module
var iframeUploadModule = function(upload) {
this.name = "iframeUploadModule";
this.element = upload.wrapper;
this.upload = upload;
this.iframes = [];
};
Upload._frameId = 0;
iframeUploadModule.prototype = {
onSelect: function(e) {
var upload = this.upload,
sourceInput = $(e.target);
var fileEntry = this.prepareUpload(sourceInput);
if (upload.options.async.autoUpload) {
this.performUpload(fileEntry);
} else {
if (upload._supportsRemove()) {
this.upload._fileAction(fileEntry, REMOVE);
}
upload._showUploadButton();
}
},
prepareUpload: function(sourceInput) {
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": inputFiles(sourceInput) });
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, getAntiForgeryTokens());
for (var key in e.data) {
var dataInput = form.find("input[name='" + key + "']");
if (dataInput.length === 0) {
dataInput = $(" ", { type: "hidden", name: key })
.appendTo(form);
}
dataInput.val(e.data[key]);
}
upload._fileAction(fileEntry, CANCEL);
upload._fileState(fileEntry, "uploading");
$(fileEntry).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 $(
""
);
},
createForm: function(action, target) {
return $(
"");
},
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);
}
};
// FormData upload module
var formDataUploadModule = function(upload) {
this.name = "formDataUploadModule";
this.element = upload.wrapper;
this.upload = upload;
};
formDataUploadModule.prototype = {
onSelect: function(e, rawFiles) {
var upload = this.upload,
module = this,
sourceElement = $(e.target),
files = rawFiles ? getAllFileInfo(rawFiles) : this.inputFiles(sourceElement),
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;
},
inputFiles: function(sourceInput) {
return inputFiles(sourceInput);
},
performUpload: function(fileEntry) {
var upload = this.upload,
formData = this.createFormData(fileEntry.data("files")),
xhr = new XMLHttpRequest(),
e = {
files: fileEntry.data("fileNames"),
XMLHttpRequest: xhr
};
if (!upload.trigger(UPLOAD, e)) {
upload._fileAction(fileEntry, CANCEL);
upload._hideUploadButton();
upload._showHeaderUploadStatus();
e.data = $.extend({ }, e.data, getAntiForgeryTokens());
for (var key in e.data) {
formData.append(key, e.data[key]);
}
upload._fileState(fileEntry, "uploading");
$(fileEntry).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);
}
},
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 = "true";
xhr.send(data);
},
createFormData: function(files) {
var formData = new FormData(),
upload = this.upload,
i,
length = files.length;
for (i = 0; i < length; i++) {
formData.append(
upload.options.async.saveField || upload.name,
files[i].rawFile
);
}
return formData;
},
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() {
// Exclude removed file entries and self
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();
}
};
// Helper functions
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) {
// Older Firefox versions (before 3.6) use fileName and fileSize
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 removeUploadedFile(fileEntry, upload, data) {
if (!upload._supportsRemove()) {
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) {
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");
}
function getAntiForgeryTokens() {
var tokens = { },
csrf_token = $("meta[name=csrf-token]").attr("content"),
csrf_param = $("meta[name=csrf-param]").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.ui.plugin(Upload);
})(window.kendo.jQuery);
kendo_module({
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",
// classNames
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",
// constants
VISIBLE = ":visible",
HIDDEN = "hidden",
CURSOR = "cursor",
// events
OPEN = "open",
ACTIVATE = "activate",
DEACTIVATE = "deactivate",
CLOSE = "close",
REFRESH = "refresh",
RESIZE = "resize",
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",
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 sizingAction(actionId, callback) {
return function() {
var that = this,
wrapper = that.wrapper,
style = wrapper[0].style,
options = that.options;
if (options.isMaximized || options.isMinimized) {
return;
}
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);
if (actionId == "maximize") {
that.wrapper.children(KWINDOWTITLEBAR).find(PIN_UNPIN).parent().hide();
} else {
that.wrapper.children(KWINDOWTITLEBAR).find(PIN_UNPIN).parent().show();
}
return that;
};
}
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)[0] || document.body);
that._animations();
if (content && !isPlainObject(content)) {
content = options.content = { url: content };
}
// remove script blocks to prevent double-execution
element.find("script").filter(function() {
return !this.type || this.type.toLowerCase().indexOf("script") >= 0;
}).remove();
if (!element.parent().is(that.appendTo) && (options.position.top === undefined || options.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 (!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();
}
if (position.top !== undefined) {
position.top = position.top.toString();
}
if (position.left !== undefined) {
position.left = position.left.toString();
}
wrapper.css({
top: position.top || offset.top || "",
left: position.left || offset.left || ""
});
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, function () { $(this).addClass(KHOVERSTATE); })
.on("mouseleave" + NS, TITLEBAR_BUTTONS, function () { $(this).removeClass(KHOVERSTATE); })
.on("click" + NS, "> " + TITLEBAR_BUTTONS, proxy(that._windowActionHandler, that));
windowContent
.on("keydown" + NS, proxy(that._keydown, that))
.on("focus" + NS, function() { wrapper.addClass(KFOCUSEDSTATE); })
.on("blur" + NS, function() { wrapper.removeClass(KFOCUSEDSTATE); });
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 = function(e) {
return that._onDocumentResize(e);
};
$(window).on("resize", that._resizeHandler);
if (options.visible) {
that.trigger(OPEN);
that.trigger(ACTIVATE);
}
kendo.notify(that);
},
_dimensions: function() {
var that = this,
wrapper = that.wrapper,
options = that.options,
w = options.width,
h = options.height,
maxh = options.maxHeight;
that.title(options.title);
each(["minWidth","minHeight","maxWidth","maxHeight"], function(_, prop) {
var value = options[prop];
if (value && value != Infinity) {
wrapper.css(prop, value);
}
});
if (maxh && maxh != Infinity) {
that.element.css("maxHeight", maxh);
}
if (w) {
if (w.toString().indexOf("%") > 0) {
wrapper.width(w);
} else {
wrapper.width(constrain(w, options.minWidth, options.maxWidth));
}
}
if (h) {
if (h.toString().indexOf("%") > 0) {
wrapper.height(h);
} else {
wrapper.height(constrain(h, options.minHeight, options.maxHeight));
}
}
if (!options.visible) {
wrapper.hide();
}
},
_animations: function() {
var options = this.options;
if (options.animation === false) {
options.animation = { open: { effects: {} }, close: { hide: true, effects: {} } };
}
},
_resize: function() {
kendo.resize(this.element.children());
},
_resizable: function() {
var resizable = this.options.resizable;
var wrapper = this.wrapper;
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);
} else if (this.resizing) {
wrapper
.off("dblclick" + NS)
.children(KWINDOWRESIZEHANDLES).remove();
this.resizing.destroy();
this.resizing = null;
}
},
_draggable: function() {
var draggable = this.options.draggable;
if (draggable) {
this.dragging = new WindowDragging(this, draggable.dragHandle || KWINDOWTITLEBAR);
} else if (this.dragging) {
this.dragging.destroy();
this.dragging = null;
}
},
setOptions: function(options) {
Widget.fn.setOptions.call(this, options);
this._animations();
this._dimensions();
this._resizable();
this._draggable();
},
events:[
OPEN,
ACTIVATE,
DEACTIVATE,
CLOSE,
REFRESH,
RESIZE,
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,
position: {},
content: null,
visible: null,
height: null,
width: null
},
_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(true);
}
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 = $("
");
}
overlay
.insertBefore(wrapper[0])
.toggle(visible)
.css(ZINDEX, parseInt(wrapper.css(ZINDEX), 10) - 1);
return overlay;
},
_windowActionHandler: function (e) {
var target = $(e.target).closest(".k-window-action").find(".k-icon"),
that = this;
if (that._closing) {
return;
}
each({
"k-i-close": function() { that._close(true); },
"k-i-maximize": that.maximize,
"k-i-minimize": that.minimize,
"k-i-restore": that.restore,
"k-i-refresh": that.refresh,
"k-i-pin": that.pin,
"k-i-unpin": that.unpin
}, function (commandName, handler) {
if (target.hasClass(commandName)) {
e.preventDefault();
handler.call(that);
return false;
}
});
},
_modals: function() {
var that = this;
return $(KWINDOW).filter(function() {
var wnd = $(this);
var options = that._object(wnd).options;
return options.modal && options.visible && wnd.is(VISIBLE);
}).sort(function(a, b){
return +$(a).css("zIndex") - +$(b).css("zIndex");
});
},
_object: function(element) {
var content = element.children(KWINDOWCONTENT);
return content.data("kendoWindow") || content.data("kendo" + this.options.name);
},
center: function () {
var that = this,
wrapper = that.wrapper,
documentWindow = $(window);
if (that.options.isMaximized) {
return that;
}
wrapper.css({
left: documentWindow.scrollLeft() + Math.max(0, (documentWindow.width() - wrapper.width()) / 2),
top: documentWindow.scrollTop() + Math.max(0, (documentWindow.height() - wrapper.height()) / 2)
});
return that;
},
title: function (text) {
var that = this,
wrapper = that.wrapper,
options = that.options,
titleBar = wrapper.children(KWINDOWTITLEBAR),
title = titleBar.children(KWINDOWTITLE),
titleBarHeight = titleBar.outerHeight();
if (!arguments.length) {
return title.text();
}
if (text === false) {
wrapper.addClass("k-window-titleless");
titleBar.remove();
} else {
if (!titleBar.length) {
wrapper.prepend(templates.titlebar(extend(templates, options)));
}
wrapper.css("padding-top", titleBarHeight);
titleBar.css("margin-top", -titleBarHeight);
}
title.text(text);
that.options.title = text;
return that;
},
content: function (html) {
var content = this.wrapper.children(KWINDOWCONTENT),
scrollContainer = content.children(".km-scroll-container");
content = scrollContainer[0] ? scrollContainer : content;
if (!html) {
return content.html();
}
kendo.destroy(this.element.children());
content.html(html);
return this;
},
open: function () {
var that = this,
wrapper = that.wrapper,
options = that.options,
showOptions = options.animation.open,
contentElement = wrapper.children(KWINDOWCONTENT),
initialOverflow = contentElement.css(OVERFLOW),
overlay;
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: function() {
if (options.autoFocus) {
that.element.focus();
}
that.trigger(ACTIVATE);
contentElement.css(OVERFLOW, initialOverflow);
}
});
}
}
if (options.isMaximized) {
that._documentScrollTop = $(document).scrollTop();
$("html, body").css(OVERFLOW, HIDDEN);
}
return that;
},
_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 = options.animation.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(userTriggered) {
var that = this,
wrapper = that.wrapper,
options = that.options,
showOptions = options.animation.open,
hideOptions = options.animation.close;
if (wrapper.is(VISIBLE) && !that.trigger(CLOSE, { userTriggered: !!userTriggered })) {
that._closing = true;
options.visible = false;
$(KWINDOW).each(function(i, element) {
var contentElement = $(element).children(KWINDOWCONTENT);
// Remove overlay set by toFront
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: function() {
wrapper.hide().css("opacity","");
that.trigger(DEACTIVATE);
var lastModal = that._object(that._modals().last());
if (lastModal) {
lastModal.toFront();
}
}
});
}
if (that.options.isMaximized) {
$("html, body").css(OVERFLOW, "");
if (that._documentScrollTop && that._documentScrollTop > 0) {
$(document).scrollTop(that._documentScrollTop);
}
}
},
close: function () {
this._close(false);
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);
}
// Add overlay to windows with iframes and lower z-index to prevent
// trapping of events when resizing / dragging
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);
}
}
}
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;
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) {
$(document).scrollTop(this._documentScrollTop);
}
options.isMaximized = options.isMinimized = false;
that.resize();
return that;
},
maximize: sizingAction("maximize", function() {
var that = this,
wrapper = that.wrapper,
position = wrapper.position();
extend(that.restoreOptions, {
left: position.left,
top: position.top
});
wrapper.css({
left: 0,
top: 0,
position: "fixed"
})
.addClass(MAXIMIZEDSTATE);
this._documentScrollTop = $(document).scrollTop();
$("html, body").css(OVERFLOW, HIDDEN);
that.options.isMaximized = true;
that._onDocumentResize();
}),
minimize: 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),
w, h;
if (!that.options.isMaximized) {
return;
}
w = wnd.width();
h = wnd.height() - 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) {
// perform AJAX request
that._ajaxRequest(options);
} else {
iframe = element.find("." + KCONTENTFRAME)[0];
if (iframe) {
// refresh existing iframe
iframe.src = url || iframe.src;
} else {
// render new iframe
element.html(templates.contentFrame(extend({}, initOptions, { content: options })));
}
element.find("." + KCONTENTFRAME)
.unbind("load" + NS)
.on("load" + NS, function(){
that.trigger(REFRESH);
});
}
} else {
if (options.template) {
// refresh template
that.content(template(options.template)({}));
}
that.trigger(REFRESH);
}
return that;
},
_ajaxRequest: function (options) {
var that = this,
contentTemplate = options.template,
refreshIcon = that.wrapper.find(".k-window-titlebar .k-i-refresh"),
loadingIconTimeout = setTimeout(function () {
refreshIcon.addClass(LOADING);
}, 100);
$.ajax(extend({
type: "GET",
dataType: "html",
cache: false,
error: function (xhr, status) {
that.trigger(ERROR, {
status: status,
xhr: xhr
});
},
complete: function () {
clearTimeout(loadingIconTimeout);
refreshIcon.removeClass(LOADING);
},
success: function (data) {
if (contentTemplate) {
data = template(contentTemplate)(data || {});
}
that.content(data);
that.element.prop("scrollTop", 0);
that.trigger(REFRESH);
}
}, options));
},
destroy: function () {
var wrapper = this.wrapper;
Widget.fn.destroy.call(this);
kendo.destroy(wrapper);
if (this.resizing) {
this.resizing.destroy();
}
if (this.dragging) {
this.dragging.destroy();
}
this.element.children("iframe").remove();
wrapper.find(".k-resize-handle,.k-window-titlebar").off(NS);
wrapper.remove().off(NS);
$(window).off("resize", this._resizeHandler);
this._removeOverlay(true);
},
_createWindow: function() {
var that = this,
contentHtml = that.element,
options = that.options,
iframeSrcAttributes,
wrapper,
isRtl = kendo.support.isRtl(contentHtml);
if (options.scrollable === false) {
contentHtml.attr("style", "overflow:hidden;");
}
wrapper = $(templates.wrapper(options));
if (options.title !== false) {
wrapper.append(templates.titlebar(extend(templates, options)));
}
// Collect the src attributes of all iframes and then set them to empty string.
// This seems to fix this IE9 "feature": http://msdn.microsoft.com/en-us/library/gg622929%28v=VS.85%29.aspx?ppud=4
iframeSrcAttributes = contentHtml.find("iframe:not(.k-content)").map(function() {
var src = this.getAttribute("src");
this.src = "";
return src;
});
// Make sure the wrapper is appended to the body only once. IE9+ will throw exceptions if you move iframes in DOM
wrapper
.toggleClass("k-rtl", isRtl)
.appendTo(that.appendTo)
.append(contentHtml)
.find("iframe:not(.k-content)").each(function(index) {
// Restore the src attribute of the iframes when they are part of the live DOM tree
this.src = iframeSrcAttributes[index];
});
wrapper.find(".k-window-title")
.css(isRtl ? "left" : "right", wrapper.find(".k-window-actions").outerWidth() + 10);
contentHtml.show();
contentHtml.find("[data-role=editor]").each(function() {
var editor = $(this).data("kendoEditor");
if (editor) {
editor.refresh();
}
});
}
});
templates = {
wrapper: template("
"),
action: template(
"" +
"#= name # " +
" "
),
titlebar: template(
""
),
overlay: "
",
contentFrame: template(
""
),
resizeHandle: template("
")
};
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)
});
}
WindowResizing.prototype = {
dragstart: function (e) {
var that = this,
wnd = that.owner,
wrapper = wnd.wrapper;
that.elementPadding = parseInt(wnd.wrapper.css("padding-top"), 10);
that.initialCursorPosition = 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);
wrapper
.append(templates.overlay)
.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.initialCursorPosition,
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
.find(KOVERLAY).remove().end()
.children(KWINDOWRESIZEHANDLES).not(e.currentTarget).show();
$(BODY).css(CURSOR, "");
if (wnd.touchScroller) {
wnd.touchScroller.reset();
}
if (e.keyCode == 27) {
wrapper.css(that.initialCursorPosition)
.css(that.initialSize);
}
return false;
},
destroy: function() {
this._draggable.destroy();
}
};
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(); // at least 20px remain visible
}
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,
coordinates = {
left: Math.max(e.x.client - wnd.startPosition.left, wnd.minLeftPosition),
top: Math.max(e.y.client - wnd.startPosition.top, wnd.minTopPosition)
};
$(wnd.wrapper).css(coordinates);
},
_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() {
this._draggable.destroy();
}
};
kendo.ui.plugin(Window);
})(window.kendo.jQuery);
kendo_module({
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 '' +
'' +
tableRows.join(" ") +
' ' +
'
';
}
function allDayTable(tableRows, className) {
if (!tableRows.length) {
return "";
}
return "" + table(tableRows, className) + "
";
}
function timesHeader(columnLevelCount, allDaySlot, rowCount) {
var tableRows = [];
if (rowCount > 0) {
for (var idx = 0; idx < columnLevelCount; idx++) {
tableRows.push(" ");
}
}
if (allDaySlot) {
tableRows.push('' + allDaySlot.text + ' ');
}
if (rowCount < 1) {
return $();
}
return $('' + table(tableRows) + '
');
}
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 ++) {
th.push('' + level[columnIndex].text + " ");
}
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('' + (cellContent ? cellContent(columnIndex) : ' ') + '');
}
allDayTableRows.push(td.join(""));
}
return $(
''
);
}
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] += ' ' + level[rowIndex].text + " ";
}
}
for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
rowHeaderRows.push(rows[rowIndex]);
}
if (rowCount < 1) {
return $();
}
return $('' + table(rowHeaderRows) + '
');
}
function content() {
return $(
''
);
}
var HINT = '';
kendo.ui.scheduler = {};
var ResourceView = kendo.Class.extend({
init: function(index) {
this._index = index;
this._timeSlotCollections = [];
this._daySlotCollections = [];
},
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);
if (x >= slot.offsetLeft && x < slot.offsetLeft + slot.clientWidth &&
y >= slot.offsetTop && y < slot.offsetTop + slot.clientHeight) {
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);
var end = start;
if (startTime < endTime) {
end = this._endSlot(endTime, collections);
}
return this._continuousRange(TimeSlotRange, collections, start, end);
},
daySlotRanges: function(startTime, endTime, isAllDay) {
var collections = this._daySlotCollections;
var start = this._startSlot(startTime, collections, isAllDay);
var end = start;
if (startTime < endTime) {
end = this._endSlot(endTime, collections, isAllDay);
}
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 = kendo.date.toUtcTime(event.start);
var endTime = kendo.date.toUtcTime(event.end);
if (isDay === undefined) {
isDay = event.isMultiDay();
}
if (isDay) {
return this.daySlotRanges(startTime, endTime, event.isAllDay);
}
if (event.startTime) {
startTime = kendo.date.getMilliseconds(event.startTime) + kendo.date.toUtcTime(kendo.date.getDate(event.start));
}
if (event.endTime) {
endTime = kendo.date.getMilliseconds(event.endTime) + kendo.date.toUtcTime(kendo.date.getDate(event.end));
}
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) {
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
var collection = collections[collectionIndex];
if (collection.endInRange(date)) {
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);
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 startSlot = this.start;
var endSlot = this.end;
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"];
} 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;
}
return {
top: top,
bottom: bottom
};
},
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() {
var diffs = [];
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
var slot = this._slots[slotIndex];
var offsetTop = slot.offsetTop;
slot.refresh();
diffs[slotIndex] = slot.offsetTop - offsetTop;
}
for (var eventIndex = 0; eventIndex < this._events.length; eventIndex++) {
var event = this._events[eventIndex];
event.element.css({
top: event.element[0].offsetTop + diffs[event.slotIndex]
});
}
},
startInRange: function(date) {
return this._start <= date && date < this._end;
},
endInRange: function(date) {
return this._start <= date && date <= this._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) {
var slot = new TimeSlot(element, start, end, this._groupIndex, this._collectionIndex, this._slots.length);
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({
refresh: function() {
this.offsetTop = this.element.offsetTop;
},
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 difference = y - offset.top;
var duration = this.end - this.start;
var 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 difference = y - offset.top;
var duration = this.end - this.start;
var 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;
this.firstChildHeight = this.element.firstChild.offsetHeight + 3;
this.firstChildTop = this.element.firstChild.offsetTop;
},
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._scrollbar = scrollbar();
this._isRtl = kendo.support.isRtl(element);
this._resizeHint = $();
this._moveHint = $();
this._cellId = kendo.guid();
this._resourcesForGroups();
this._selectedSlots = [];
},
_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.groups.push(resourceView);
return resourceView;
},
dateForTitle: function() {
return kendo.format(this.options.selectedDateFormat, 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) {
if (!this.inRange(selection)) {
var slot = this.groups[0].firstSlot();
selection.isAllDay = slot.isDaySlot;
selection.start = slot.startDate();
selection.end = slot.endDate();
}
},
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;
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 = $();
for (idx = 0; idx < length; idx ++) {
if ($.inArray(groupEvents[idx].uid, events) > -1) {
result = result.add(groupEvents[idx].element);
}
}
if (result[0]) {
result.addClass("k-state-selected").attr("aria-selected", true);
this.current(result.last()[0]);
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;
},
_scrollbarOffset: function(value, multiday) {
if (!this._isRtl || (multiday && !this._isVerticallyGrouped()) || !kendo.support.browser.webkit) {
return value;
}
return this._scrollbarWidth + value;
},
_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.at(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);
},
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) {
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({
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 = $('');
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;
this._scrollbarWidth = 0;
if (isSchedulerHeightSet(that.element)) { // set content height only if needed
if (height > scrollbar * 2) { // do not set height if proper scrollbar cannot be displayed
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));
this._scrollbarWidth = scrollbarWidth;
} 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 $("").append(this.timesHeader.add(this.datesHeader).wrap("").parent());
},
_bottomSection: function(columnLevels, rowLevels, rowCount) {
this.times = times(rowLevels, rowCount);
this.content = content(columnLevels[columnLevels.length - 1], rowLevels[rowLevels.length - 1]);
return $(" ").append(this.times.add(this.content).wrap("").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) {
return createLayoutConfiguration("columns", resources, inner);
},
_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) {
return createLayoutConfiguration("rows", resources, inner);
},
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();
}
},
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;
if (groupIndex >= this.groups.length - 1) {
return;
}
if (this._isVerticallyGrouped()) {
if (!group.timeSlotCollectionCount()) {
collection = group._collection(0, true);
return collection.at(slot.index);
} else {
collection = group._collection(0, group.daySlotCollectionCount());
return isDay ? collection.last() : 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);
}
}
}
});
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) {
var resource = resources[0];
if (resource) {
var configuration = [];
var data = resource.dataSource.view();
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
var obj = {
text: kendo.getter(resource.dataTextField)(data[dataIndex]),
className: "k-slot-cell"
};
obj[name] = createLayoutConfiguration(name, resources.slice(1), inner);
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);
kendo_module({
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('' +
'
#:kendo.format("{0:t} - {1:t}", start, end)#
' +
'
${title}
' +
'
'),
DAY_VIEW_ALL_DAY_EVENT_TEMPLATE = kendo.template(''),
DATA_HEADER_TEMPLATE = kendo.template("#=kendo.toString(date, 'ddd M/dd')# "),
ALLDAY_EVENT_WRAPPER_STRING = '' +
'
' +
'# if(data.tail || data.middle) {#' +
' ' +
'#}#' +
'# if(data.isException()) {#' +
' ' +
'# } else if(data.isRecurring()) {#' +
' ' +
'# } #' +
' ' +
'{0}' +
'
' +
'#if (showDelete) {#' +
' ' +
'#}#' +
'# if(data.head || data.middle) {#' +
' ' +
'#}#' +
' ' +
'#if(resizable && !singleDay && !data.tail && !data.middle){#' +
'
' +
'#}#' +
'#if(resizable && !singleDay && !data.head && !data.middle){#' +
'
' +
'#}#' +
'
',
EVENT_WRAPPER_STRING = '' +
'
' +
'# if(data.isException()) {#' +
' ' +
'# } else if(data.isRecurring()) {#' +
' ' +
'# } #' +
' ' +
'{0}' +
'
' +
'#if (showDelete) {#' +
' ' +
'#}#' +
' ' +
'
' +
'# if(data.tail || data.middle) {#' +
' ' +
'# } #' +
' ' +
'
' +
'# if(data.head || data.middle) {#' +
' ' +
'# } #' +
' ' +
'# if(resizable && !data.tail && !data.middle) {#' +
'
' +
'# } #' +
'# if(resizable && !data.head && !data.middle) {#' +
'
' +
'# } #' +
'
';
function toInvariantTime(date) {
var staticDate = new Date(1980, 1, 1, 0, 0, 0);
setTime(staticDate, getMilliseconds(date));
return staticDate;
}
function isInDateRange(value, min, max) {
var msMin = min.getTime(),
msMax = max.getTime(),
msValue;
msValue = value.getTime();
return msValue >= msMin && msValue <= msMax;
}
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();
//this handles all day event which is over multiple slots but starts
//after one of the time events
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();
},
_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,
this._scrollbarOffset(start.offsetLeft, multiday),
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, startTime: start, end: end, endTime: 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 + this._scrollbarOffset(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 = kendo.date.addDays(this.startDate(), 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));
}
view.addDaySlotCollection(this._dates[0], this._dates[this._dates.length - 1]);
}
this._timeSlotGroups(groupCount, columnCount);
if (this.options.allDaySlot) {
this._daySlotGroups(groupCount, columnCount);
}
},
options: {
name: "MultiDayView",
selectedDateFormat: "{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: " ",
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"
}
},
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);
},
_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;
if (that.options.editable.create !== false) {
that._addUserEvents = new kendo.UserEvents(that.element, {
filter: ".k-scheduler-content td",
tap: function(e) {
if (!$(e.target).parent().hasClass("k-scheduler-header-all-day")) {
var slot = that._slotByPosition(e.x.location, e.y.location);
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, {
filter: ".k-scheduler-header-all-day td",
tap: function(e) {
var slot = that._slotByPosition(e.x.location, e.y.location);
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, {
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) {
idx = resources.length && that._groupOrientation() !== "vertical" ? idx % dates.length : idx;
return that.allDaySlotTemplate({ date: dates[idx] });
}
});
}
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);
} else {
columns = this._createColumnsLayout(resources, columns);
}
}
return {
columns: columns,
rows: rows
};
},
_footer: function() {
var options = this.options;
if (options.footer !== false) {
var html = '";
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); //convert the date to 1/2/1980 and sets the time
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 allDayVerticalGroupRow = "";
var slotTemplate = this.slotTemplate;
if (resources.length) {
if (that._groupOrientation() === "vertical") {
rowCount = this._rowCountForLevel(this.rowLevels.length - 2);
if (options.allDaySlot) {
allDayVerticalGroupRow = ' ";
}
} else {
groupsCount = this._columnCountForLevel(this.columnLevels.length - 2);
}
}
html += '';
var appendRow = function(date, majorTick) {
var content = "";
var idx;
var length;
var classes = "";
var tmplDate;
content = '';
for (var groupIdx = 0; 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 += '";
tmplDate = kendo.date.getDate(dates[idx]);
kendo.date.setTime(tmplDate, kendo.date.getMilliseconds(date));
content += slotTemplate({ date: tmplDate });
content += " ";
}
}
content += " ";
return content;
};
for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
html += allDayVerticalGroupRow;
html += this._forTimeRange(start, end, appendRow);
}
html += ' ';
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.datesHeader) {
that.datesHeader.off(NS);
}
if (that.element) {
that.element.off(NS);
}
SchedulerView.fn.destroy.call(this);
if (that.footer) {
that.footer.remove();
}
if (this._isMobile()) {
if (that.options.editable.create !== false) {
that._addUserEvents.destroy();
that._allDayUserEvents.destroy();
}
if (that.options.editable.update !== false) {
that._editUserEvents.destroy();
}
}
},
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: this._scrollbarOffset(start.offsetLeft + leftOffset, true),
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._updateAllDayHeaderHeight(eventHeight * rows.length + eventHeight);
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.10,
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 ? this._scrollbarOffset(eventRightOffset) : 0) + startSlot.offsetLeft + idx * columnWidth + 2 + "px";
}
}
},
_positionEvent: function(event, element, slotRange) {
var start = event.start;
var end = event.end;
if (event.startTime) {
start = kendo.date.getMilliseconds(event.startTime) + kendo.date.toUtcTime(kendo.date.getDate(event.start));
}
if (event.endTime) {
end = kendo.date.getMilliseconds(event.endTime) + kendo.date.toUtcTime(kendo.date.getDate(event.end));
}
var rect = slotRange.innerRect(start, end, false);
var height = rect.bottom - rect.top - 2; /* two times border width */
if (height < 0) {
height = 0;
}
element.css( {
top: rect.top,
height: height
} );
this._arrangeColumns(element, rect.top, element[0].clientHeight, slotRange);
},
_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;
},
_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 = getMilliseconds(event.startTime || event.start);
var eventEndTime = getMilliseconds(event.endTime || event.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 eventEndDate = event.end;
if (event.isAllDay) {
eventEndDate = getDate(event.end);
}
if ((!isInDateRange(getDate(event.start), startDate, endDate) &&
!isInDateRange(eventEndDate, startDate, endDate)) ||
(isOneDayEvent && eventStartTime < startTime && eventEndTime > endTime)) {
middle = true;
} else if (getDate(event.start) < startDate || (isOneDayEvent && eventStartTime < startTime)) {
tail = true;
} else if ((eventEndDate > endDate && !isOneDayEvent) || (isOneDayEvent && eventEndTime > endTime)) {
head = true;
}
var resources = this.eventResources(event);
return $(template(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: event.startTime || event.start,
end: event.endTime || event.end
})));
},
_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 (kendo.date.getDate(endTime) > kendo.date.getDate(startTime)) {
endTime = kendo.date.getDate(endTime);
setTime(endTime, MS_PER_DAY - 1);
}
endTime = getMilliseconds(endTime);
startTime = getMilliseconds(startTime);
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 slotStart = this.startDate();
var slotEnd = new Date(this.endDate().getTime() + MS_PER_DAY - 1);
return (isInDateRange(event.start, slotStart, slotEnd) ||
isInDateRange(event.end, slotStart, slotEnd) ||
isInDateRange(slotStart, event.start, event.end) ||
isInDateRange(slotEnd, event.start, event.end)) &&
(!isInDateRange(event.end, slotStart, slotStart) || isInDateRange(event.end, event.start, event.start) || event.isAllDay );
},
_updateAllDayHeaderHeight: function(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);
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();
this._updateAllDayHeaderHeight(this._allDayHeaderHeight);
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);
} else {
this._renderEvents(events, 0, 0);
}
this.refreshLayout();
this.trigger("activate");
},
_renderGroups: function(events, resources, offset) {
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) {
offset = this._renderGroups(eventsFilteredByResource, resources.slice(1), offset++);
} else {
this._renderEvents(eventsFilteredByResource, offset++);
}
}
}
return offset;
},
_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: {
title: "Day"
},
name: "day"
}),
WeekView: MultiDayView.extend({
options: {
title: "Week",
selectedDateFormat: "{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: {
title: "Work Week",
selectedDateFormat: "{0:D} - {1:D}"
},
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);
}
})
});
})(window.kendo.jQuery);
kendo_module({
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";
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.name = "agenda";
this._eventTemplate = kendo.template(options.eventTemplate);
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);
},
_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;
if (event.isAllDay) {
end = kendo.date.nextDay(end);
}
var eventDurationInDays = Math.ceil((end - start) / kendo.date.MS_PER_DAY);
if (!event.isAllDay && eventDurationInDays === 1 && kendo.date.getDate(end).getTime() !== kendo.date.getDate(start).getTime()) {
eventDurationInDays += 1;
}
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 (task.end <= this.endDate() && task.start >= this.startDate()) {
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(
'{1} ',
groups[idx].rowSpan,
this._groupTemplate({ value: groups[idx].text }),
groups[idx].className
));
}
}
if (taskIndex === 0) {
if (isPhoneView) {
headerCells.push(kendo.format(
'{0} ',
this._dateTemplate({ date: date })
));
tableRows.push('' : ">") + headerCells.join("") + " ");
} else {
tableRow.push(kendo.format(
'{1} ',
tasks.length,
this._dateTemplate({ date: date }),
taskGroupIndex == tasksGroups.length - 1 && !groups.length ? " k-last" : ""
));
}
}
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(
'{0}{1}{2}
{3} ',
task.tail || task.middle ? ' ' : "",
this._timeTemplate(task.clone({ start: task.startTime || task.start, end: task.endTime || task.end })),
task.head || task.middle ? ' ' : "",
this._eventTemplate(task.clone({ showDelete: showDelete }))
));
tableRows.push('' : ">") + tableRow.join("") + " ");
}
}
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, []));
}
}
this._eventsList = flattenTaskGroups(groups);
this.refreshLayout();
this.trigger("activate");
},
_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) {
cell = $(cell);
if (cell.hasClass("k-scheduler-datecolumn")) {
return;
}
var index = cell.parent().index();
var 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")[0];
if (row) {
this._scrollTo(row, this.content[0]);
}
},
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}",
eventTemplate: '' +
'# if (resources[0]) {#' +
'
' +
"# } #" +
"# if (data.isException()) { #" +
'
' +
'# } else if (data.isRecurring()) {#' +
'
' +
"# } #" +
'#:title#' +
'#if (showDelete) {#' +
'
' +
'#}#' +
'
',
eventTimeTemplate: "#if(data.isAllDay) {#" +
"all day" +
"#} else { #" +
'#=kendo.format(format, start, end)#' +
"# } #",
eventDateTemplate: '' +
'#=kendo.toString(date, "dd")#' +
' ' +
'' +
'#=kendo.toString(date,"dddd")#' +
' ' +
'' +
'#=kendo.toString(date, "y")#' +
' ',
eventGroupTemplate: '' +
'#=value#' +
' ',
messages: {
event: "Event",
date: "Date",
time: "Time"
}
}
});
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);
kendo_module({
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,
proxy = $.proxy,
getDate = kendo.date.getDate,
MS_PER_DAY = kendo.date.MS_PER_DAY,
NUMBER_OF_ROWS = 6,
NUMBER_OF_COLUMNS = 7,
DAY_TEMPLATE = kendo.template('#:kendo.toString(date, "dd")# '),
EVENT_WRAPPER_STRING = '' +
'
' +
'# if(data.tail || data.middle) {#' +
' ' +
'#}#' +
'# if(data.isException()) {#' +
' ' +
'# } else if(data.isRecurring()) {#' +
' ' +
'#}#' +
' ' +
'{0}' +
'
' +
'#if (showDelete) {#' +
' ' +
'#}#' +
'# if(data.head || data.middle) {#' +
' ' +
'#}#' +
' ' +
'# if(resizable && !data.tail && !data.middle) {#' +
'
' +
'#}#' +
'# if(resizable && !data.head && !data.middle) {#' +
'
' +
'#}#' +
'
',
EVENT_TEMPLATE = kendo.template('');
var MORE_BUTTON_TEMPLATE = kendo.template(
'...
'
);
ui.MonthView = SchedulerView.extend({
init: function(element, options) {
var that = this;
SchedulerView.fn.init.call(that, element, options);
that.title = that.options.title;
that.name = "month";
that._templates();
that._editable();
that._renderLayout(that.options.date);
that._groups();
},
_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);
this.dayTemplate = kendo.template(options.dayTemplate, settings);
},
dateForTitle: function() {
return kendo.format(this.options.selectedDateFormat, 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;
if (that.options.editable.create !== false) {
that._addUserEvents = new kendo.UserEvents(that.element, {
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, {
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 = '';
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();
}
html += " ";
this.content.find("table").html(html);
},
_createCalendar: function() {
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 resources = this.groupedResources;
if (resources.length) {
if (!this._isVerticallyGrouped()) {
horizontalGroupCount = this._columnCountForLevel(resources.length - 1);
}
}
this._slotIndices = {};
for (var rowIdx = 0; rowIdx < cellCount / cellsPerRow; rowIdx++) {
html += "";
weekStartDates.push(start);
var startIdx = rowIdx*cellsPerRow;
for (var groupIdx = 0; groupIdx < horizontalGroupCount; groupIdx++) {
html += this._createRow(start, startIdx, cellsPerRow);
}
start = kendo.date.addDays(start, cellsPerRow);
html += " ";
}
this._weekStartDates = weekStartDates;
this._endDate = kendo.date.previousDay(start);
return html;
},
_createRow: function(startDate, startIdx, cellsPerRow) {
var min = this._firstDayOfMonth;
var max = this._lastDayOfMonth;
var content = this.dayTemplate;
var classes = "";
var html = "";
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 += "";
html += content({ date: startDate });
html += " ";
this._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 = []; //add hidden cells in order to sync the content rows
for (var idx = 0; idx < 6; idx++) {
inner.push({ text: "
", className: "k-hidden k-slot-cell" });
}
rows = this._createRowsLayout(resources, inner);
} else {
columns = this._createColumnsLayout(resources, columns);
}
}
return {
columns: columns,
rows: rows
};
},
_eventTmpl: function(template) {
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(EVENT_WRAPPER_STRING, html), settings);
if (state.count > 0) {
tmpl = proxy(tmpl, state.storage);
}
return tmpl;
},
_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;
return $(this.eventTemplate(event));
},
_isInDateSlot: function(event) {
var slotStart = this.startDate();
var slotEnd = new Date(this.endDate().getTime() + MS_PER_DAY - 1);
return (isInDateRange(event.start, slotStart, slotEnd) ||
isInDateRange(event.end, slotStart, slotEnd) ||
isInDateRange(slotStart, event.start, event.end) ||
isInDateRange(slotEnd, event.start, event.end)) &&
(!isInDateRange(event.end, slotStart, slotStart) || isInDateRange(event.end, event.start, event.start) || 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('
',
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: this._scrollbarOffset(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 = this._scrollbarOffset(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
});
this.content[0].appendChild(element[0]);
}
},
_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;
left = this._scrollbarOffset(left);
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");
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(this.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.firstChild.offsetHeight + 3;
var start = kendo.date.toUtcTime(kendo.date.addDays(this.startDate(), cellCount));
cellCount ++;
var eventCount = Math.floor((clientHeight - firstChildHeight) / (this.options.eventHeight + 3)) - 1;// add space for the more button
cell.setAttribute("role", "gridcell");
cell.setAttribute("aria-selected", false);
collection.addDaySlot(cell, start, start + kendo.date.MS_PER_DAY, 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()) {
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,
editable: true,
selectedDateFormat: "{0:y}",
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.getTime(),
msMax = max.getTime(),
msValue;
msValue = value.getTime();
return msValue >= msMin && msValue <= msMax;
}
})(window.kendo.jQuery);
kendo_module({
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,
date = kendo.date,
setTime = date.setTime,
setDayOfWeek = date.setDayOfWeek,
adjustDST = date.adjustDST,
firstDayOfMonth = date.firstDayOfMonth,
getMilliseconds = date.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,
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,
weekStart = rule.weekStart,
weekDayRules = ruleWeekValues(weekDays, date, weekStart),
hours = date.getHours(),
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 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;
if (frequency === "hourly") {
diff = Math.floor((current - start) / (60 * kendo.date.MS_PER_MINUTE));
excess = intervalExcess(diff, interval);
if (excess !== 0) {
this._hour(current, rule, excess);
modified = true;
}
} else if (frequency === "daily") {
diff = Math.floor((current - start) / kendo.date.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) {
kendo.date.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) {
if (diff !== 0 && diff < interval) {
diff = interval - diff;
} else {
diff = diff % interval;
}
return diff;
}
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(),
firstWeekLength = Math.abs(7 - (firstWeekDay + 7 - (weekStart || 7))) || 7;
return Math.ceil((date.getDate() - firstWeekLength) / 7) + 1;
}
function normalizeOffset(date, offset, weekStart) {
if (offset < 0) {
offset = numberOfWeeks(date, weekStart) + (offset + 1);
}
return offset;
}
function numberOfWeeks(date, weekStart) {
return weekInMonth(new Date(date.getFullYear(), date.getMonth() + 1, 0), weekStart);
}
function isInWeek(date, weekDayRule, weekStart) {
var offset = weekDayRule.offset,
weekNumber = weekInMonth(date, weekStart);
if (!allowFirstWeek(date, weekDayRule, weekStart)) {
weekNumber -= 1;
}
return weekNumber === normalizeOffset(date, offset, weekStart);
}
function allowFirstWeek(date, weekDayRule, weekStart) {
var day = weekDayRule.day,
offset = weekDayRule.offset,
firstDay, allow;
if (!offset) {
return true;
}
firstDay = firstDayOfMonth(date).getDay();
if (firstDay < weekStart) {
firstDay += weekStart;
}
if (day < weekStart) {
day += weekStart;
}
allow = day >= firstDay;
if (!allow && offset < 0 && normalizeOffset(date, offset, weekStart) !== 1) {
allow = true;
}
return allow;
}
function ruleWeekValues(weekDays, date, weekStart) {
var currentDay = date.getDay(),
length = weekDays.length,
weekDay, day, offset,
weekNumber,
result = [],
idx = 0;
if (currentDay < weekStart) {
currentDay += 7;
}
for (;idx < length; idx++) {
weekDay = weekDays[idx];
offset = weekDay.offset;
day = weekDay.day;
if (day < weekStart) {
day += 7;
}
weekNumber = weekInMonth(date, weekStart);
if (!allowFirstWeek(date, weekDay, weekStart)) {
weekNumber -= 1;
}
offset = offset ? normalizeOffset(date, offset, weekStart) : weekNumber;
if (weekNumber < offset) {
result.push(weekDay);
} else if (weekNumber === offset) {
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 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 normalizeEventsByPosition(events, start, rule) {
var periodEvents = events.slice(rule._startIdx);
var periodEventsLength = periodEvents.length;
var positions = rule.positions;
var list = [];
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; //convert to zero based index
}
event = periodEvents[position];
if (event && event.start >= start) {
list.push(event);
}
}
events = events.slice(0, rule._startIdx).concat(list);
rule._startIdx = events.length;
return events;
}
function expand(event, start, end, zone) {
var rule = parseRule(event.recurrenceRule),
startTime, endTime, endDate,
hours, minutes, seconds,
durationMS, startPeriod, inPeriod,
ruleStart, ruleEnd,
useEventStart, freqName,
exceptionDates,
eventStartTime,
eventStartMS,
eventStart,
count, freq,
positions,
current,
events = [];
if (!rule) {
return [event];
}
positions = rule.positions;
current = positions ? 0 : 1;
ruleStart = rule.start;
ruleEnd = rule.end;
if (ruleStart || ruleEnd) {
event = event.clone({
start: ruleStart ? new Date(ruleStart.value) : undefined,
end: ruleEnd ? new Date(ruleEnd.value) : undefined
});
}
eventStart = event.start;
eventStartMS = eventStart.getTime();
eventStartTime = getMilliseconds(eventStart);
exceptionDates = parseExceptions(event.recurrenceException, 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);
rule._startPeriod = new Date(start);
rule._endPeriod = endPeriodByFreq(start, rule);
rule._startIdx = 0;
}
durationMS = event.duration();
rule._startTime = startTime = kendo.date.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) {
endTime = new Date(rule._startTime);
setTime(endTime, durationMS);
if (eventStartMS !== start.getTime() || eventStartTime !== getMilliseconds(startTime)) {
events.push(event.toOccurrence({
start: new Date(start),
startTime: new Date(startTime),
end: endDate,
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) {
events = normalizeEventsByPosition(events, eventStart, rule);
rule._endPeriod = endPeriodByFreq(start, rule);
current = events.length;
}
if (count && count === current) {
break;
}
} else {
if (count && count === current) {
break;
}
current++;
freq.next(start, rule);
freq.limit(start, end, rule);
}
}
return events;
}
function parseUTCDate(value, zone) {
value = kendo.parseDate(value, DATE_FORMATS); //Parse UTC to local time
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;
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 = parseUTCDate(value, tzid || zone);
}
}
if (value) {
return {
value: value,
tzid: tzid
};
}
}
function parseRule(recur, zone) {
var instance = {},
idx = 0, length,
splits, value,
property,
weekStart,
weekDays,
ruleValue = false,
rule, part, parts,
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)) {
parts = recur.split(" ");
}
for (idx = 0, length = parts.length; idx < length; idx++) {
part = 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("RRULE") !== -1) {
rule = part.substring(6);
} else {
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 || "";
value = timezone.convert(value, tzid || zone || value.getTimezoneOffset(), "Etc/UTC");
if (tzid) {
tzid = ";TZID=" + tzid;
}
return tzid + ":" + kendo.toString(value, "yyyyMMddTHHmmssZ") + " ";
}
function serialize(rule, zone) {
var weekStart = rule.weekStart,
ruleString = "FREQ=" + rule.freq.toUpperCase(),
until = rule.until,
start = rule.start || "",
end = rule.end || "";
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 (start || end) {
ruleString = start + end + "RRULE:" + ruleString;
}
return ruleString;
}
kendo.recurrence = {
rule: {
parse: parseRule,
serialize: serialize
},
expand: expand,
dayInYear: dayInYear,
weekInYear: weekInYear,
weekInMonth: weekInMonth,
numberOfWeeks: numberOfWeeks,
isException: isException
};
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 += ' ' + shortNames[idx] + " ";
}
return result;
};
var RECURRENCE_VIEW_TEMPLATE = kendo.template(
'# if (frequency !== "never") { #' +
'#:messages.repeatEvery#
' +
' #:messages.interval#
' +
'# } #' +
'# if (frequency === "weekly") { #' +
'#:messages.repeatOn#
' +
'#=weekDayCheckBoxes(firstWeekDay)#
' +
'# } else if (frequency === "monthly") { #' +
'#:messages.repeatOn#
' +
'' +
'# } else if (frequency === "yearly") { #' +
'#:messages.repeatOn#
' +
'' +
'# } #' +
'# if (frequency !== "never") { #' +
'#:end.label#
' +
'' +
'# } #'
);
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 || date.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",
daily: "Daily",
weekly: "Weekly",
monthly: "Monthly",
yearly: "Yearly"
},
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:",
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.value();
var offset = Number(that._weekDayOffset.value());
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 || start,
change: function() {
rule.until = this.value();
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);
Widget.fn.destroy.call(that);
},
value: function(value) {
var that = this,
timezone = that.options.timezone;
if (value === undefined) {
if (!that._value.freq) {
return "";
}
return serialize(that._value, timezone);
}
that._value = parseRule(value, timezone) || {};
that._frequency.value(that._value.freq || "");
that._initView(that._frequency.value());
},
_initContainer: function() {
var element = this.element,
container = $('
'),
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 = $(' '),
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
};
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('#:headerTitle#
' +
'
' +
'
'
);
var RECURRENCE_REPEAT_PATTERN_TEMPLATE = kendo.template(
'# if (frequency !== "never") { #' +
'#:messages.repeatEvery#
' +
' #:messages.interval#
' +
'# } #' +
'# if (frequency === "weekly") { #' +
'#:messages.repeatOn#
' +
'#=weekDayCheckBoxes(firstWeekDay)#
' +
'# } else if (frequency === "monthly") { #' +
'#:messages.repeatBy#
' +
'
' +
'' +
'
#:messages.day#
' +
'
' +
'
' +
'' +
'
#:messages.every#
' +
'
' +
'
#:messages.day#
' +
'
' +
'
' +
'# } else if (frequency === "yearly") { #' +
'#:messages.repeatBy#
' +
'
' +
'' +
'
#:messages.day#
' +
'
' +
'
' +
'' +
'
#:messages.every#
' +
'
' +
'
#:messages.day#
' +
'
' +
'
' +
'#:messages.month#
' +
'
' +
'# } #'
);
var RECURRENCE_END_PATTERN_TEMPLATE = kendo.template(
'# if (endPattern === "count") { #' +
'#:messages.after#
' +
'
' +
'# } else if (endPattern === "until") { #' +
'#:messages.on#
' +
'
' +
'# } #'
);
var RECURRENCE_GROUP_BUTTON_TEMPLATE = kendo.template(
''
);
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('#:text# ');
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: ""
},
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);
},
_initRepeatButton: function() {
var that = this;
var freq = that.options.messages.frequencies[this._value.freq || "never"];
that._repeatButton = $('' + freq + ' ')
.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 = $('Ends
').insertAfter(that.element.parent(".k-edit-field"));
var endEditField = $('')
.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 = '';
return html;
},
showDialog: function(options) {
var html = kendo.format("';
var wrapper = this.element;
var 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();
},
_createPopupEditor: function(model) {
var that = this;
var editable = that.options.editable;
var html = '';
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));
var editableWidget = container.kendoEditable({
fields: editableFields,
model: model,
clearContainer: false,
validateOnBlur: true
}).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 });
});
} else {
that.trigger(CANCEL, { container: container, model: model });
}
return editableWidget;
},
_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 = Widget.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 = proxy(that.resize, that);
that.wrapper.on("mousedown" + NS + " selectstart" + NS, function(e) {
e.preventDefault();
});
if (that.options.editable && that.options.editable.resize !== false) {
that._resizable();
}
that._movable();
$(window).on("resize" + NS, that._resizeHandler);
if(that.options.messages && that.options.messages.recurrence) {
recurrence.options = that.options.messages.recurrence;
}
that._selectable();
that._ariaId = kendo.guid();
that._createEditor();
},
_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";
},
_selectable: function() {
var that = this,
wrapper = that.wrapper,
selectEvent = kendo.support.mobileOS ? "touchend" : "mousedown";
if (!that.options.selectable) {
return;
}
that._tabindex();
wrapper.on(selectEvent, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event", function(e) {
that._createSelection(e.currentTarget);
wrapper.focus();
});
var mouseMoveHandler = $.proxy(that._mouseMove, that);
wrapper.on("mousedown" + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td", function() {
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($(".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 view = this.view();
var wrapper = this.wrapper;
var current = view.current();
var selection = this._selection;
if (current) {
current.removeAttribute("id");
current.removeAttribute("aria-label");
wrapper.removeAttr("aria-activedescendant");
}
view.select(selection);
current = view.current();
if (current) {
var labelFormat;
var data = selection;
var events = this._selectedEvents();
var slots = view._selectedSlots;
if (events[0]) {
data = events[0] || selection;
labelFormat = kendo.format(this.options.messages.ariaEventLabel, data.title, data.start, data.start);
} else {
labelFormat = kendo.format(this.options.messages.ariaSlotLabel, data.start, data.end);
}
current.setAttribute("id", this._ariaId);
current.setAttribute("aria-label", labelFormat);
wrapper.attr("aria-activedescendant", this._ariaId);
this.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) {
// add/edit event
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(selection);
}
} else if (key === keys.DELETE) {
that.removeEvent(selection.events[0]);
} else if (key >= 49 && key <= 57) {
// switch to view 1-9
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;
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) {
if (this._shiftKey && selection.start && selection.end) {
var backward = dataItem.end < selection.end,
view = this.view();
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: "",
min: new Date(1900, 0, 1),
max: new Date(2099, 11, 31),
messages: {
today: "Today",
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"
},
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"
},
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);
}
if (that.calendar) {
that.calendar.destroy();
that.popup.destroy();
}
if (that.view()) {
that.view().destroy();
}
if (that._editor) {
that._editor.destroy();
}
element = that.element
.add(that.wrapper)
.add(that.toolbar)
.add(that.popup);
element.off(NS);
$(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.children(".k-event, .k-task");
},
_movable: function() {
var startSlot;
var endSlot;
var startTime;
var endTime;
var event;
var that = this;
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)) {
that._moveDraggable = new kendo.ui.Draggable(that.element, {
distance: 0,
filter: ".k-event",
holdToDrag: isMobile
});
if (movable) {
that._moveDraggable.bind("dragstart", function(e) {
var view = that.view();
var eventElement = e.currentTarget;
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")));
startSlot = view._slotByPosition(e.x.location, e.y.location);
startTime = startSlot.startOffset(e.x.location, e.y.location, that.options.snap);
endSlot = 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);
if (!slot) {
return;
}
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
var distance = endTime - startTime;
view._updateMoveHint(event, slot.groupIndex, distance);
var range = moveEventRange(event, distance);
if (!that.trigger("move", {
event: event,
slot: { element: slot.element, start: slot.startDate(), end: slot.endDate() },
resources: view._resourceBySlot(slot),
start: range.start,
end: range.end
})) {
endSlot = slot;
} else {
view._updateMoveHint(event, slot.groupIndex, distance);
}
})
.bind("dragend", function(e) {
that.view()._removeMoveHint();
var distance = endTime - startTime;
var range = moveEventRange(event, 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() || kendo.stringify(endResources) != kendo.stringify(startResources))) {
that._updateEvent(null, event, $.extend({ start: start, end: end }, endResources));
}
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");
});
that._moveDraggable.userEvents.bind("press", function(e) {
e.preventDefault();
});
}
}
},
_resizable: function() {
var startTime;
var endTime;
var event;
var slot;
var that = this;
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];
}
}
}
that._resizeDraggable = new kendo.ui.Draggable(that.element, {
distance: 0,
filter: ".k-resize-handle",
dragstart: function(e) {
var dragHandle = $(e.currentTarget);
var eventElement = dragHandle.closest(".k-event");
var uid = eventElement.attr(kendo.attr("uid"));
event = that.occurrenceByUid(uid);
slot = that.view()._slotByPosition(e.x.location, e.y.location);
if (that.trigger("resizeStart", { event: event })) {
e.preventDefault();
}
startTime = kendo.date.toUtcTime(event.start);
endTime = kendo.date.toUtcTime(event.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(event.start) >= view._timeSlotInterval()) {
if (event.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(event.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(event.start))) {
if (event.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 == "west") {
if (slot.isDaySlot && kendo.date.toUtcTime(kendo.date.getDate(event.end)) >= kendo.date.toUtcTime(kendo.date.getDate(slot.startDate()))) {
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(event, slot.groupIndex, startTime, endTime);
} else {
startTime = originalStart;
endTime = originalEnd;
}
},
dragend: function(e) {
var dragHandle = $(e.currentTarget);
var start = new Date(event.start.getTime());
var end = new Date(event.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") {
end = kendo.date.getDate(kendo.timezone.toLocalDate(endTime));
} else if (dir == "west") {
start = new Date(kendo.timezone.toLocalDate(startTime));
start.setHours(0);
start.setMinutes(0);
}
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 (event.start.getTime() != start.getTime() || event.end.getTime() != end.getTime()) {
that._updateEvent(dir, event, { start: start, end: end });
}
}
},
dragcancel: function() {
that.view()._removeResizeHint();
}
});
},
_updateEvent: function(dir, event, eventInfo) {
var that = this;
var updateEvent = function(event) {
try {
that._preventRefresh = true;
event.update(eventInfo);
} finally {
that._preventRefresh = false;
}
that.refresh();
if (!that.trigger(SAVE, { event: event })) {
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;
}
}
updateEvent(head);
};
var updateOcurrence = function() {
var exception = recurrenceHead(event).toOccurrence({ start: event.start, end: event.end });
updateEvent(that.dataSource.add(exception));
};
var recurrenceMessages = that.options.messages.recurrenceMessages;
if (event.recurrenceRule || event.isOccurrence()) {
that.showDialog({
model: event,
title: recurrenceMessages.editWindowTitle,
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
buttons: [
{ text: recurrenceMessages.editWindowOccurrence, click: updateOcurrence },
{ text: recurrenceMessages.editWindowSeries, click: updateSeries }
]
});
} 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 text = typeof editable.confirmation === STRING ? editable.confirmation : DELETECONFIRM;
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.showDialog({
model: model,
text: text,
title: messages.deleteWindowTitle,
buttons: buttons
});
} 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.dirty) {
this._convertDates(model, "remove");
}
if (model.isRecurrenceHead()) {
this.dataSource._removeExceptions(model);
}
this.dataSource.sync();
}
},
cancelEvent: function() {
var editor = this._editor;
var container = editor.container;
var model;
if (container) {
model = this._modelForContainer(container);
this.dataSource.cancelChanges(model);
//TODO: handle the cancel in UI
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._createPopupEditor(model);
},
_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.showDialog({
model: model,
title: recurrenceMessages.editWindowTitle,
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
buttons: [
{ text: recurrenceMessages.editWindowOccurrence, click: editOccurrence },
{ text: recurrenceMessages.editWindowSeries, click: editSeries }
]
});
},
_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, {
timezone: that.dataSource.reader.timezone,
resources: that.resources,
createButton: proxy(this._createButton, this)
}));
} else {
editor = that._editor = new PopupEditor(this.wrapper, extend({}, this.options, {
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()) {
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 deleteOcurrence = function() {
var occurrence = currentModel.recurrenceId ? currentModel : currentModel.toOccurrence();
that._removeEvent(occurrence);
};
var deleteSeries = function() {
if (currentModel.recurrenceId) {
currentModel = that.dataSource.get(currentModel.recurrenceId);
}
that._removeEvent(currentModel);
};
var recurrenceMessages = that.options.messages.recurrenceMessages;
that.showDialog({
model: model,
title: recurrenceMessages.deleteWindowTitle,
text: recurrenceMessages.deleteRecurring ? recurrenceMessages.deleteRecurring : DELETERECURRING,
buttons: [
{ text: recurrenceMessages.deleteWindowOccurrence, click: deleteOcurrence },
{ text: recurrenceMessages.deleteWindowSeries, click: deleteSeries }
]
});
},
_unbindView: function(view) {
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;
that.toolbar
.find(".k-scheduler-views li")
.removeClass("k-state-selected")
.end()
.find(".k-view-" + name)
.addClass("k-state-selected");
}
},
view: function(name) {
var that = this;
if (name) {
that._selectView(name);
that.rebind();
return;
}
return that._selectedView;
},
_renderView: function(name) {
var view = this._initializeView(name);
this._bindView(view);
this._model.set("formattedDate", view.dateForTitle());
return view;
},
resize: function(force) {
var size = this.getSize(),
currentSize = this._size;
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 = {};
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.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;
if (!selected || view.selected) {
selected = name;
}
}
}
if (selected) {
this._selectedViewName = selected; // toolbar is not rendered yet
}
},
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 } });
}
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);
},
_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: this.options.date,
formattedDate: ""
});
that._model.bind("change", function(e) {
if (e.field === "selectedDate") {
that.view(that._selectedViewName);
}
});
},
_wrapper: function() {
var height = this.options.height;
this.wrapper = this.element
.addClass("k-widget k-scheduler k-floatwrap")
.attr("role", "grid")
.attr("aria-multiselectable", true);
if (this._isMobile()) {
this.wrapper.addClass("k-scheduler-mobile");
}
if (this._isMobilePhoneView()) {
this.wrapper.addClass("k-scheduler-phone");
}
if (height) {
this.wrapper.css("height", height);
}
},
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 template = this._isMobilePhoneView() ? MOBILETOOLBARTEMPLATE : TOOLBARTEMPLATE;
var toolbar = $(template({
messages: options.messages,
ns: kendo.ns,
views: that.views
}));
that.wrapper.append(toolbar);
that.toolbar = toolbar;
kendo.bind(that.toolbar, that._model);
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; // TODO: Not good - refactor
}
if (!that.trigger("navigate", { view: that._selectedViewName, action: action, date: date })) {
that.date(date);
}
});
toolbar.on(CLICK + NS, ".k-scheduler-views li", 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);
}
});
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 = $('');
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 view = this.view();
this._progress(false);
e = e || {};
if (!view) {
return;
}
if (e && e.action === "itemchange" && (this._editor.editable || this._preventRefresh)) { // skip rebinding if editing is in progress
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(),
element: slot.element,
isDaySlot: slot.isDaySlot
};
},
slotByElement: function(element) {
var offset = $(element).offset();
return this.slotByPosition(offset.left, offset.top);
}
});
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"
}
};
ui.plugin(Scheduler);
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.value(that.options.value);
},
options: {
name: "TimezoneEditor",
value: "",
optionLabel: "No timezone"
},
events: [ "change" ],
_zoneTitlePicker: function() {
var that = this,
zoneTitle = $(' ').appendTo(that.wrapper);
that._zoneTitle = new kendo.ui.DropDownList(zoneTitle, {
dataSource: kendo.timezone.zones_titles,
dataValueField: "other_zone",
dataTextField: "name",
optionLabel: that.options.optionLabel,
cascade: function() {
if (!this.value()) {
that._zone.wrapper.hide();
}
}
});
},
_zonePicker: function() {
var that = this,
zone = $(' ').appendTo(this.wrapper);
that._zone = new kendo.ui.DropDownList(zone, {
dataValueField: "zone",
dataTextField: "territory",
dataSource: that._zonesQuery.data,
cascadeFrom: that._zoneTitleId,
cascade: function() {
that._value = this.value();
that.trigger("change");
},
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);
if (this._moveDraggable) {
this._moveDraggable.destroy();
}
if (this._resizeDraggable) {
this._resizeDraggable.destroy();
}
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.value("");
}
}
});
ui.plugin(TimezoneEditor);
var ZONETITLEOPTIONTEMPLATE = kendo.template('#=name# ');
var ZONEOPTIONTEMPLATE = kendo.template('#=territory# ');
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: this.options.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 = $('' + options + ' ')
.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 = $(' ')
.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);