@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,654 lines (1,351 loc) • 220 kB
JavaScript
import './kendo.core.js';
import './kendo.data.odata.js';
import './kendo.data.xml.js';
/*
This code is copied/inspired by the internal @progress/kendo-data-query repo:
https://github.com/telerik/kendo-data-query/tree/develop/src/filtering
!!!
1. If updates are to be synced consider the accentFoldingFiltering,
which at the moment is not present as a feature in the data-query-package.
2. Double-check available operators as well.
3. Make sure objs are strings -> i.e., replace (a || "") to (a + '')
!!!
In future, if we consider reusing the code directly we should revise the above omissions.
*/
const logic = {
"or": {
concat: (acc, fn) => a => acc(a) || fn(a),
identity: () => false
},
"and": {
concat: (acc, fn) => a => acc(a) && fn(a),
identity: () => true
}
};
const operatorsMap = {
isnullorempty: (a) => kendo.isBlank(a) || a === '',
isnotnullorempty: (a) => kendo.isPresent(a) && a !== '',
contains: (a, b) => (a + '').indexOf(b) >= 0,
doesnotcontain: (a, b) => (a + '').indexOf(b) === -1,
doesnotendwith: (a, b) => (a + '').indexOf(b, (a || "").length - (b || "").length) < 0,
doesnotstartwith: (a, b) => (a + '').lastIndexOf(b, 0) === -1,
endswith: (a, b) => (a + '').indexOf(b, (a || "").length - (b || "").length) >= 0,
eq: (a, b) => a === b,
gt: (a, b) => a > b,
gte: (a, b) => a >= b,
isempty: (a) => a === '',
isnotempty: (a) => a !== '',
isnotnull: (a) => kendo.isPresent(a),
isnull: (a) => kendo.isBlank(a),
lt: (a, b) => a < b,
lte: (a, b) => a <= b,
neq: (a, b) => a != b,
startswith: (a, b) => (a + '').lastIndexOf(b, 0) === 0
};
const dateRegExp = /^\/Date\((.*?)\)\/$/;
const convertValue = (value, ignoreCase, accentFoldingFiltering) => {
if (value != null && kendo.isString(value)) {
const date = dateRegExp.exec(value);
if (date) {
return new Date(+date[1]).getTime();
} else if (ignoreCase) {
return accentFoldingFiltering ? value.toLocaleLowerCase(accentFoldingFiltering) : value.toLowerCase();
}
} else if (value != null && kendo.isDate(value)) {
return value.getTime();
}
return value;
};
const typedGetter = (prop, value, ignoreCase, accentFoldingFiltering) => {
if (!kendo.isPresent(value)) {
return prop;
}
let acc = prop;
if (kendo.isString(value)) {
const date = dateRegExp.exec(value);
if (date) {
value = new Date(+date[1]);
} else {
acc = a => {
const x = prop(a);
if (typeof x === 'string' && ignoreCase) {
return accentFoldingFiltering ? x.toLocaleLowerCase(accentFoldingFiltering) : x.toLowerCase();
} else {
return kendo.isNumeric(x) ? x + "" : x;
}
};
}
}
if (kendo.isDate(value)) {
return a => {
const x = acc(a);
return kendo.isDate(x) ? x.getTime() : x;
};
}
return acc;
};
const transformFilter = ({ field, ignoreCase, value, operator = 'eq', accentFoldingFiltering }) => {
field = !kendo.isPresent(field) ? a => a : field;
ignoreCase = kendo.isPresent(ignoreCase) ? ignoreCase : true;
const itemProp = typedGetter(
kendo.isFunction(field) ? field : kendo.getter(field, true),
value,
ignoreCase,
accentFoldingFiltering
);
value = convertValue(value, ignoreCase, accentFoldingFiltering);
const op = kendo.isFunction(operator) ? operator : operatorsMap[operator];
return a => op(itemProp(a), value, ignoreCase);
};
const isCompositeFilterDescriptor = (source) => kendo.isPresent(source.filters);
const transformCompositeFilter = (filter) => {
const accentFoldingFiltering = filter.accentFoldingFiltering;
const combiner = logic[filter.logic || 'and'];
return filter.filters
.filter(kendo.isPresent)
.map(x => (isCompositeFilterDescriptor(x) ? transformCompositeFilter(extendAccentFolding(x, accentFoldingFiltering)) : transformFilter(extendAccentFolding(x, accentFoldingFiltering))))
.reduce(combiner.concat, combiner.identity);
};
const extendAccentFolding = (filter, accentFoldingFiltering) => (kendo.isPresent(accentFoldingFiltering) ? Object.assign(filter, { accentFoldingFiltering }) : filter);
const filterExprNoEval = function(expr) {
return transformCompositeFilter(expr);
};
const __meta__ = {
id: "data",
name: "Data source",
category: "framework",
description: "Powerful component for using local and remote data.Fully supports CRUD, Sorting, Paging, Filtering, Grouping, and Aggregates.",
depends: [ "core" ],
features: [ {
id: "data-odata",
name: "OData",
description: "Support for accessing Open Data Protocol (OData) services.",
depends: [ "data.odata" ]
}, {
id: "data-signalr",
name: "SignalR",
description: "Support for binding to SignalR hubs.",
depends: [ "data.signalr" ]
}, {
id: "data-XML",
name: "XML",
description: "Support for binding to XML.",
depends: [ "data.xml" ]
}]
};
(function($, undefined$1) {
var extend = $.extend,
isPlainObject = $.isPlainObject,
isEmptyObject = $.isEmptyObject,
isArray = Array.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",
ASCENDING = "asc",
CREATE = "create",
READ = "read",
UPDATE = "update",
DESTROY = "destroy",
CHANGE = "change",
SYNC = "sync",
GET = "get",
ERROR = "error",
REQUESTSTART = "requestStart",
PROGRESS = "progress",
REQUESTEND = "requestEnd",
ITEMSLOADED = "itemsLoaded",
ITEMLOAD = "itemLoad",
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\((.*?)\)\/$/,
objectKeys = [];
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);
that._loadPromises = [];
that._loadedNodes = [];
},
at: function(index) {
return this[index];
},
toJSON: function(serializeFunctions) {
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(serializeFunctions);
}
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) {
var isGroup = object.hasOwnProperty("hasSubgroups");
that.trigger(CHANGE, {
field: e.field,
node: e.node,
index: e.index,
items: e.items || [this],
action: e.node || isGroup ? (e.action || "itemloaded") : "itemchange"
});
});
object.bind(ITEMLOAD, function(e) {
that._loadPromises.push(e.promise);
that._loading = true;
e.promise.done(function() {
that._loadedNodes.push(e.node);
var index = that._loadPromises.indexOf(e.promise);
that._loadPromises.splice(index, 1);
if (!that._loadPromises.length) {
that._loading = false;
that.trigger(ITEMSLOADED, { collection: that, nodes: that._loadedNodes });
that._loadedNodes = [];
}
});
});
}
return object;
},
loading: function() {
return this._loading;
},
push: function() {
var index = this.length,
items = this.wrapAll(arguments),
result;
result = push.apply(this, items);
if (!this.omitChangeEvent) {
this.trigger(CHANGE, {
action: "add",
index: index,
items: items
});
}
return result;
},
slice: slice,
sort: [].sort,
join: join,
pop: function() {
var length = this.length, result = pop.apply(this);
if (length) {
this.trigger(CHANGE, {
action: "remove",
index: length - 1,
items: [result]
});
}
return result;
},
splice: function(index, howMany, item) {
var items = this.wrapAll(slice.call(arguments, 2)),
result, i, len;
result = splice.apply(this, [index, howMany].concat(items));
if (result.length) {
if (!this.omitChangeEvent) {
this.trigger(CHANGE, {
action: "remove",
index: index,
items: this.omitCache && this.omitCache.length ? result.concat(this.omitCache) : result
});
this.omitCache = [];
}
for (i = 0, len = result.length; i < len; i++) {
if (result[i] && result[i].children) {
result[i].unbind(CHANGE);
}
}
}
if (item) {
if (!this.omitChangeEvent) {
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, thisArg) {
var idx = 0;
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
callback.call(context, this[idx], idx, this);
}
},
map: function(callback, thisArg) {
var idx = 0;
var result = [];
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
result[idx] = callback.call(context, this[idx], idx, this);
}
return result;
},
reduce: function(callback) {
var idx = 0,
result,
length = this.length;
if (arguments.length == 2) {
result = arguments[1];
} else if (idx < length) {
result = this[idx++];
}
for (; idx < length; idx++) {
result = callback(result, this[idx], idx, this);
}
return result;
},
reduceRight: function(callback) {
var idx = this.length - 1,
result;
if (arguments.length == 2) {
result = arguments[1];
} else if (idx > 0) {
result = this[idx--];
}
for (; idx >= 0; idx--) {
result = callback(result, this[idx], idx, this);
}
return result;
},
filter: function(callback, thisArg) {
var idx = 0;
var result = [];
var item;
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
item = this[idx];
if (callback.call(context, item, idx, this)) {
result[result.length] = item;
}
}
return result;
},
find: function(callback, thisArg) {
var idx = 0;
var item;
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
item = this[idx];
if (callback.call(context, item, idx, this)) {
return item;
}
}
},
every: function(callback, thisArg) {
var idx = 0;
var item;
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
item = this[idx];
if (!callback.call(context, item, idx, this)) {
return false;
}
}
return true;
},
some: function(callback, thisArg) {
var idx = 0;
var item;
var length = this.length;
var context = thisArg || window;
for (; idx < length; idx++) {
item = this[idx];
if (callback.call(context, item, idx, this)) {
return true;
}
}
return false;
},
// non-standard collection methods
remove: function(item) {
var idx = this.indexOf(item);
if (idx !== -1) {
this.splice(idx, 1);
}
},
empty: function() {
this.splice(0, this.length);
}
});
// Polyfill for Symbol.iterator
if (typeof Symbol !== "undefined" && Symbol.iterator && !ObservableArray.prototype[Symbol.iterator]) {
ObservableArray.prototype[Symbol.iterator] = [][Symbol.iterator];
}
var LazyObservableArray = ObservableArray.extend({
init: function(data, type, events) {
var parentFn = function() { return this; };
Observable.fn.init.call(this);
this.type = type || ObservableObject;
if (events) {
this._events = events;
}
for (var idx = 0; idx < data.length; idx++) {
this[idx] = data[idx];
}
this.length = idx;
this._parent = parentFn.bind(this);
this._loadPromises = [];
this._loadedNodes = [];
},
at: function(index) {
var item = this[index];
if (!(item instanceof this.type)) {
item = this[index] = this.wrap(item, this._parent);
} else {
item.parent = this._parent;
}
return item;
}
});
function eventHandler(context, type, field, prefix) {
return function(e) {
var event = {}, key;
for (key in e) {
event[key] = e[key];
}
if (prefix) {
event.field = field + "." + e.field;
} else {
event.field = field;
}
if (type == CHANGE && context._notifyChange) {
context._notifyChange(event);
}
context.trigger(type, event);
};
}
function isPrimitiveType(value) {
return (typeof value === "object" && Object.getPrototypeOf(value) === Object.getPrototypeOf({}))
|| Object.getPrototypeOf(value) === Object.getPrototypeOf(new Date())
|| typeof value !== "object";
}
function ownKeys(value, ignoreObjectKeys) {
var props = [];
var protoKeys = [];
var keys, filteredObjectKeys;
value = value || {};
if (!isPrimitiveType(value)) {
protoKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(value)).filter(f => f.indexOf("__") !== 0);
}
keys = Object.getOwnPropertyNames(value).concat(protoKeys);
filteredObjectKeys = objectKeys.filter(function(key) {
return keys.indexOf(key) < 0;
});
while (value) {
Object.getOwnPropertyNames(value).forEach(function(prop) {
if (props.indexOf(prop) === -1 && (!ignoreObjectKeys || filteredObjectKeys.indexOf(prop) < 0)) {
props.push(prop);
}
});
value = Object.getPrototypeOf(value);
}
return props;
}
objectKeys = ownKeys({}, false);
var ObservableObject = Observable.extend({
init: function(value) {
var that = this,
member,
keys = ownKeys(value, true),
parent = function() {
return that;
};
Observable.fn.init.call(this);
this._handlers = {};
keys.forEach(function(field) {
member = value[field];
if (typeof member === "object" && member && !member.getTime && field.charAt(0) != "_") {
member = that.wrap(member, field, parent);
}
that[field] = member;
});
that.uid = kendo.guid();
},
shouldSerialize: function(field, serializeFunctions) {
// This way we cover both if the serializeFunctions parameter is an object OR a boolean.
if (typeof serializeFunctions === "object") {
serializeFunctions = (serializeFunctions && serializeFunctions[field]);
}
return this.hasOwnProperty(field) && field !== "_handlers" && field !== "_events" && (serializeFunctions || typeof this[field] !== FUNCTION) && field !== "uid";
},
forEach: function(f) {
for (var i in this) {
if (this.shouldSerialize(i)) {
f(this[i], i);
}
}
},
toJSON: function(serializeFunctions) {
var result = {}, value, field;
for (field in this) {
if (this.shouldSerialize(field, serializeFunctions)) {
value = this[field];
if (value instanceof ObservableObject || value instanceof ObservableArray) {
value = value.toJSON(serializeFunctions);
}
result[field] = value;
}
}
return result;
},
get: function(field) {
var that = this, result;
that.trigger(GET, { field: field });
if (field === "this") {
result = that;
} else {
result = kendo.getter(field, true)(that);
}
return result;
},
_set: function(field, value) {
var that = this;
var composite = field.indexOf(".") >= 0;
if (composite) {
var paths = field.split("."),
path = "";
while (paths.length > 1) {
path += paths.shift();
var obj = kendo.getter(path, true)(that);
if (obj instanceof ObservableObject) {
obj.set(paths.join("."), value);
return composite;
}
path += ".";
}
}
kendo.setter(field)(that, value);
return composite;
},
set: function(field, value) {
var that = this,
isSetPrevented = false,
composite = field.indexOf(".") >= 0,
current = kendo.getter(field, true)(that);
if (current !== value) {
if (current instanceof Observable && this._handlers[field]) {
if (this._handlers[field].get) {
current.unbind(GET, this._handlers[field].get);
}
current.unbind(CHANGE, this._handlers[field].change);
}
isSetPrevented = that.trigger("set", { field: field, value: value });
if (!isSetPrevented) {
if (!composite) {
value = that.wrap(value, field, function() { return that; });
}
if ((!that._set(field, value) || field.indexOf("(") >= 0 || field.indexOf("[") >= 0)) {
that.trigger(CHANGE, { field: field });
}
}
}
return isSetPrevented;
},
parent: noop,
wrap: function(object, field, parent) {
var that = this;
var get;
var change;
var type = toString.call(object);
if (object != null && (type === "[object Object]" || type === "[object Array]")) {
var isObservableArray = object instanceof ObservableArray;
var isDataSource = object instanceof DataSource;
if (type === "[object Object]" && !isDataSource && !isObservableArray) {
if (!(object instanceof ObservableObject)) {
object = new ObservableObject(object);
}
get = eventHandler(that, GET, field, true);
object.bind(GET, get);
change = eventHandler(that, CHANGE, field, true);
object.bind(CHANGE, change);
that._handlers[field] = { get: get, change: change };
} else if (type === "[object Array]" || isObservableArray || isDataSource) {
if (!isObservableArray && !isDataSource) {
object = new ObservableArray(object);
}
change = eventHandler(that, CHANGE, field, false);
object.bind(CHANGE, change);
that._handlers[field] = { change: change };
}
object.parent = parent;
}
return object;
}
});
function equal(x, y) {
if (x === y) {
return true;
}
var xtype = kendo.type(x), ytype = kendo.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) {
if (typeof value === STRING && value.toLowerCase() === "null") {
return null;
}
return kendo.parseFloat(value);
},
"date": function(value) {
if (typeof value === STRING && value.toLowerCase() === "null") {
return null;
}
return kendo.parseDate(value);
},
"boolean": function(value) {
if (typeof value === STRING) {
if (value.toLowerCase() === "null") {
return null;
} else {
return value.toLowerCase() === "true";
}
}
return value != null ? !!value : value;
},
"string": function(value) {
if (typeof value === STRING && value.toLowerCase() === "null") {
return null;
}
return value != null ? (value + "") : value;
},
"default": function(value) {
return value;
}
};
var defaultValues = {
"string": "",
"number": 0,
"date": new Date(),
"boolean": false,
"default": ""
};
function getFieldByName(obj, name) {
var field,
fieldName;
for (fieldName in obj) {
field = obj[fieldName];
if (isPlainObject(field) && field.field && field.field === name) {
return field;
} else if (field === name) {
return field;
}
}
return null;
}
var Model = ObservableObject.extend({
init: function(data) {
var that = this;
if (!data || $.isEmptyObject(data)) {
data = $.extend({}, that.defaults, data);
if (that._initializers) {
for (var idx = 0; idx < that._initializers.length; idx++) {
var name = that._initializers[idx];
data[name] = that.defaults[name]();
}
}
}
ObservableObject.fn.init.call(that, data);
that.dirty = false;
that.dirtyFields = {};
if (that.idField) {
that.id = that.get(that.idField);
if (that.id === undefined$1) {
that.id = that._defaultId;
}
}
},
shouldSerialize: function(field) {
return ObservableObject.fn.shouldSerialize.call(this, field) &&
field !== "uid" && !(this.idField !== "id" && field === "id") &&
field !== "dirty" && field !== "dirtyFields" && 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;
this.dirtyFields[e.field] = true;
}
},
editable: function(field) {
field = (this.fields || {})[field];
return field ? field.editable !== false : true;
},
set: function(field, value) {
var that = this;
var dirty = that.dirty;
if (that.editable(field)) {
value = that._parse(field, value);
if (!equal(value, that.get(field))) {
that.dirty = true;
that.dirtyFields[field] = true;
if (ObservableObject.fn.set.call(that, field, value) && !dirty) {
that.dirty = dirty;
if (!that.dirty) {
that.dirtyFields[field] = false;
}
}
} else {
that.trigger("equalSet", { field: field, value: value });
}
}
},
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;
that.dirtyFields = {};
},
isNew: function() {
return this.id === this._defaultId;
}
});
Model.define = function(base, options) {
if (options === undefined$1) {
options = base;
base = Model;
}
var model,
proto = extend({ defaults: {} }, options),
name,
field,
type,
value,
idx,
length,
fields = {},
originalName,
id = proto.id,
functionFields = [];
if (id) {
proto.idField = id;
}
if (proto.id) {
delete proto.id;
}
if (id) {
proto.defaults[id] = proto._defaultId = "";
}
if (toString.call(proto.fields) === "[object Array]") {
for (idx = 0, length = proto.fields.length; idx < length; idx++) {
field = proto.fields[idx];
if (typeof field === STRING) {
fields[field] = {};
} else if (field.field) {
fields[field.field] = field;
}
}
proto.fields = fields;
}
for (name in proto.fields) {
field = proto.fields[name];
type = field.type || "default";
value = null;
originalName = name;
let nameToUse = typeof (field.field) === STRING ? field.field : name;
if (!field.nullable || field.defaultValue) {
value = proto.defaults[originalName !== nameToUse ? originalName : nameToUse] = field.defaultValue !== undefined$1 ? field.defaultValue : defaultValues[type.toLowerCase()];
if (typeof value === "function") {
functionFields.push(nameToUse);
}
}
if (options.id === nameToUse) {
proto._defaultId = value;
}
proto.defaults[originalName !== nameToUse ? originalName : nameToUse] = value;
if ($.isPlainObject(field)) {
field.parse = field.parse || parsers[type];
} else {
field = {
parse: parsers[type]
};
}
}
if (functionFields.length > 0) {
proto._initializers = functionFields;
}
model = base.extend(proto);
model.define = function(options) {
return Model.define(model, options);
};
if (proto.fields) {
model.fields = proto.fields;
model.idField = proto.idField;
}
return model;
};
var Comparer = {
selector: function(field) {
return isFunction(field) ? field : getter(field);
},
compare: function(field) {
var selector = this.selector(field);
return function(a, b) {
a = selector(a);
b = selector(b);
if (a == null && b == null) {
return 0;
}
if (a == null) {
return -1;
}
if (b == null) {
return 1;
}
if (a.localeCompare) {
return a.localeCompare(b);
}
return a > b ? 1 : (a < b ? -1 : 0);
};
},
create: function(sort) {
var compare = sort.compare || this.compare(sort.field);
if (sort.dir == "desc") {
return function(a, b) {
return compare(b, a, true);
};
}
return compare;
},
combine: function(comparers) {
return function(a, b) {
var result = comparers[0](a, b),
idx,
length;
for (idx = 1, length = comparers.length; idx < length; idx ++) {
result = result || comparers[idx](a, b);
}
return result;
};
}
};
var StableComparer = extend({}, Comparer, {
asc: function(field) {
var selector = this.selector(field);
return function(a, b) {
var valueA = selector(a);
var valueB = selector(b);
if (valueA && valueA.getTime && valueB && valueB.getTime) {
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB) {
return a.__position - b.__position;
}
if (valueA == null) {
return -1;
}
if (valueB == null) {
return 1;
}
if (valueA.localeCompare) {
return valueA.localeCompare(valueB);
}
return valueA > valueB ? 1 : -1;
};
},
desc: function(field) {
var selector = this.selector(field);
return function(a, b) {
var valueA = selector(a);
var valueB = selector(b);
if (valueA && valueA.getTime && valueB && valueB.getTime) {
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB) {
return a.__position - b.__position;
}
if (valueA == null) {
return 1;
}
if (valueB == null) {
return -1;
}
if (valueB.localeCompare) {
return valueB.localeCompare(valueA);
}
return valueA < valueB ? 1 : -1;
};
},
create: function(sort) {
return this[sort.dir](sort.field);
}
});
map = function(array, callback) {
var idx, length = array.length, result = new Array(length);
for (idx = 0; idx < length; idx++) {
result[idx] = callback(array[idx], idx, array);
}
return result;
};
var operators = (function() {
function quote(str) {
if (typeof str == "string") {
str = str.replace(/[\r\n]+/g, "");
}
return JSON.stringify(str);
}
function textOp(impl) {
return function(a, b, ignore, accentFoldingFiltering) {
b += "";
if (ignore) {
a = "(" + a + " + '').toString()" + ((accentFoldingFiltering) ? ".toLocaleLowerCase('" + accentFoldingFiltering + "')" : ".toLowerCase()");
b = ((accentFoldingFiltering) ? b.toLocaleLowerCase(accentFoldingFiltering) : b.toLowerCase());
}
return impl(a, quote(b), ignore);
};
}
function operator(op, a, b, ignore, accentFoldingFiltering) {
if (b != null) {
if (typeof b === STRING) {
var date = dateRegExp.exec(b);
if (date) {
b = new Date(+date[1]);
} else if (ignore) {
b = quote(((accentFoldingFiltering) ? b.toLocaleLowerCase(accentFoldingFiltering) : b.toLowerCase()));
a = "((" + a + " || '')+'')" + ((accentFoldingFiltering) ? ".toLocaleLowerCase('" + accentFoldingFiltering + "')" : ".toLowerCase()");
} else {
b = quote(b);
}
}
if (b.getTime) {
//b looks like a Date
a = "(" + a + "&&" + a + ".getTime?" + a + ".getTime():" + a + ")";
b = b.getTime();
}
}
return a + " " + op + " " + b;
}
function getMatchRegexp(pattern) {
// take a pattern, as supported by Excel match filter, and
// convert it to the equivalent JS regular expression.
// Excel patterns support:
//
// * - match any sequence of characters
// ? - match a single character
//
// to match a literal * or ?, they must be prefixed by a tilde (~)
for (var rx = "/^", esc = false, i = 0; i < pattern.length; ++i) {
var ch = pattern.charAt(i);
if (esc) {
rx += "\\" + ch;
} else if (ch == "~") {
esc = true;
continue;
} else if (ch == "*") {
rx += ".*";
} else if (ch == "?") {
rx += ".";
} else if (".+^$()[]{}|\\/\n\r\u2028\u2029\xA0".indexOf(ch) >= 0) {
rx += "\\" + ch;
} else {
rx += ch;
}
esc = false;
}
return rx + "$/";
}
return {
quote: function(value) {
if (value && value.getTime) {
return "new Date(" + value.getTime() + ")";
}
return quote(value);
},
eq: function(a, b, ignore, accentFoldingFiltering) {
return operator("==", a, b, ignore, accentFoldingFiltering);
},
neq: function(a, b, ignore, accentFoldingFiltering) {
return operator("!=", a, b, ignore, accentFoldingFiltering);
},
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: textOp(function(a, b) {
return a + ".lastIndexOf(" + b + ", 0) == 0";
}),
doesnotstartwith: textOp(function(a, b) {
return a + ".lastIndexOf(" + b + ", 0) == -1";
}),
endswith: textOp(function(a, b) {
var n = b ? b.length - 2 : 0;
return a + ".indexOf(" + b + ", " + a + ".length - " + n + ") >= 0";
}),
doesnotendwith: textOp(function(a, b) {
var n = b ? b.length - 2 : 0;
return a + ".indexOf(" + b + ", " + a + ".length - " + n + ") < 0";
}),
contains: textOp(function(a, b) {
return a + ".indexOf(" + b + ") >= 0";
}),
doesnotcontain: textOp(function(a, b) {
return a + ".indexOf(" + b + ") == -1";
}),
matches: textOp(function(a, b) {
b = b.substring(1, b.length - 1);
return getMatchRegexp(b) + ".test(" + a + ")";
}),
doesnotmatch: textOp(function(a, b) {
b = b.substring(1, b.length - 1);
return "!" + getMatchRegexp(b) + ".test(" + a + ")";
}),
isempty: function(a) {
return a + " === ''";
},
isnotempty: function(a) {
return a + " !== ''";
},
isnull: function(a) {
return "(" + a + " == null)";
},
isnotnull: function(a) {
return "(" + a + " != null)";
},
isnullorempty: function(a) {
return "(" + a + " === null) || (" + a + " === '')";
},
isnotnullorempty: function(a) {
return "(" + a + " !== null) && (" + a + " !== '')";
}
};
})();
function Query(data) {
this.data = data || [];
}
// Continue to support legacy unsafe-eval for the spreadsheet
Query.filterExpr = function(expression, options = { noEval: false }) {
if (options.noEval) {
// using no-eval for most cases
return filterExprNoEval(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 + ", " + operators.quote(filter.value) + ")";
operatorFunctions.push(operator);
} else {
filter = operators[(operator || "eq").toLowerCase()](expr, filter.value, filter.ignoreCase !== undefined$1 ? filter.ignoreCase : true, expression.accentFoldingFiltering);
}
}
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$1 ? [descriptor] : []);
return grep(descriptors, function(d) { return !!d.dir; });
}
}
function sortFields(sorts, dir) {
var sortObject = {};
if (sorts) {
var descriptor = typeof sorts === STRING ? { field: sorts, dir: dir } : sorts,
descriptors = isArray(descriptor) ? descriptor : (descriptor !== undefined$1 ? [descriptor] : []);
for (var i = 0; i < descriptors.length; i++) {
sortObject[descriptors[i].field] = { dir: descriptors[i].dir, index: i + 1 };
}
}
return sortObject;
}
var operatorMap = {
"==": "eq",
equals: "eq",
isequalto: "eq",
equalto: "eq",
equal: "eq",
"!=": "neq",
ne: "neq",
notequals: "neq",
isnotequalto: "neq",
notequalto: "neq",
notequal: "neq",
"<": "lt",
islessthan: "lt",
lessthan: "lt",
less: "lt",
"<=": "lte",
le: "lte",
islessthanorequalto: "lte",
lessthanequal: "lte",
">": "gt",
isgreaterthan: "gt",
greaterthan: "gt",
greater: "gt",
">=": "gte",
isgreaterthanorequalto: "gte",
greaterthanequal: "gte",
ge: "gte",
notsubstringof: "doesnotcontain",
isnull: "isnull",
isempty: "isempty",
isnotempty: "isnotempty"
};
function normalizeOperator(expression) {
var idx,
length,
filter,
operator,
filters = expression.filters;
if (filters) {
for (idx = 0, length = filters.length; idx < length; idx++) {
filter = filters[idx];
operator = filter.operator;
if (operator && typeof operator === STRING) {
filter.operator = operatorMap[operator.toLowerCase()] || operator;
}
normalizeOperator(filter);
}
}
}
function normalizeFilter(expression) {
if (expression && !isEmptyObject(expression)) {
if (isArray(expression) || !expression.filters) {
expression = {
logic: "and",
filters: isArray(expression) ? expression : [expression]
};
}
normalizeOperator(expression);
return expression;
}
}
Query.normalizeFilter = normalizeFilter;
function compareDescriptor(f1, f2) {
if (f1.logic || f2.logic) {
return false;
}
return f1.field === f2.field && f1.value === f2.value && f1.operator === f2.operator;
}
function normalizeDescriptor(filter) {
filter = filter || {};
if (isEmptyObject(filter)) {
return { logic: "and", filters: [] };
}
return normalizeFilter(filter);
}
function fieldComparer(a, b) {
if (b.logic || (a.field > b.field)) {
return 1;
} else if (a.field < b.field) {
return -1;
} else {
return 0;
}
}
function hasNotFetchedItems(items, start, end) {
for (let idx = start; idx < end; idx++) {
if (items[idx].notFetched) {
return true;
}
}
return false;
}
function compareFilters(expr1, expr2) {
expr1 = normalizeDescriptor(expr1);
expr2 = normalizeDescriptor(expr2);
if (expr1.logic !== expr2.logic) {
return false;
}
var f1, f2;
var filters1 = (expr1.filters || []).slice();
var filters2 = (expr2.filters || []).slice();
if (filters1.length !== filters2.length) {
return false;
}
filters1 = filters1.sort(fieldComparer);
filters2 = filters2.sort(fieldComparer);
for (var idx = 0; idx < filters1.length; idx++) {
f1 = filters1[idx];
f2 = filters2[idx];
if (f1.logic && f2.logic) {
if (!compareFilters(f1, f2)) {
return false;
}
} else if (!compareDescriptor(f1, f2)) {
return false;
}
}
return true;
}
Query.compareFilters = compareFilters;
function normalizeAggregate(expressions) {
return isArray(expressions) ? expressions : [expressions];
}
function normalizeGroup(field, dir, compare, skipItemSorting) {
var descriptor = typeof field === STRING ? { field: field, dir: dir, compare: compare, sk