UNPKG

tui-calendar

Version:
395 lines (328 loc) 8.77 kB
/** * @fileoverview Common collections. * @author NHN FE Development Lab <dl_javascript@nhn.com> */ 'use strict'; var util = require('tui-code-snippet'); var forEachProp = util.forEachOwnProperties, forEachArr = util.forEachArray, isFunc = util.isFunction, isObj = util.isObject; var aps = Array.prototype.slice; /** * Common collection. * * It need function for get model's unique id. * * if the function is not supplied then it use default function {@link Collection#getItemID} * @constructor * @param {function} [getItemIDFn] function for get model's id. */ function Collection(getItemIDFn) { /** * @type {object.<string, *>} */ this.items = {}; /** * @type {number} */ this.length = 0; if (isFunc(getItemIDFn)) { /** * @type {function} */ this.getItemID = getItemIDFn; } } /********** * static props **********/ /** * Combind supplied function filters and condition. * @param {...function} filters - function filters * @returns {function} combined filter */ Collection.and = function(filters) { var cnt; filters = aps.call(arguments); cnt = filters.length; return function(item) { var i = 0; for (; i < cnt; i += 1) { if (!filters[i].call(null, item)) { return false; } } return true; }; }; /********** * prototype props **********/ /** * get model's unique id. * @param {object} item model instance. * @returns {number} model unique id. */ Collection.prototype.getItemID = function(item) { return String(item._id); }; /** * add models. * @param {...*} item models to add this collection. */ Collection.prototype.add = function(item) { var self = this, id, ownItems; if (arguments.length > 1) { forEachArr(aps.call(arguments), function(o) { self.add(o); }); return; } id = this.getItemID(item); ownItems = this.items; if (!ownItems[id]) { this.length += 1; } ownItems[id] = item; }; /** * remove models. * @param {...(object|string|number)} id model instance or unique id to delete. * @returns {array} deleted model list. */ Collection.prototype.remove = function(id) { var self = this, removed = [], ownItems, itemToRemove; if (!this.length) { return removed; } if (arguments.length > 1) { removed = util.map(aps.call(arguments), function(_id) { return self.remove(_id); }); return removed; } ownItems = this.items; if (isObj(id)) { id = this.getItemID(id); } if (!ownItems[id]) { return removed; } this.length -= 1; itemToRemove = ownItems[id]; delete ownItems[id]; return itemToRemove; }; /** * remove all models in collection. */ Collection.prototype.clear = function() { this.items = {}; this.length = 0; }; /** * check collection has specific model. * @param {(object|string|number|function)} id model instance or id or filter function to check * @returns {boolean} is has model? */ Collection.prototype.has = function(id) { var isFilter, has; if (!this.length) { return false; } isFilter = isFunc(id); has = false; if (isFilter) { this.each(function(item) { if (id(item) === true) { has = true; return false; // returning false can stop this loop } return true; }); } else { id = isObj(id) ? this.getItemID(id) : id; has = util.isExisty(this.items[id]); } return has; }; /** * invoke callback when model exist in collection. * @param {(string|number)} id model unique id. * @param {function} fn the callback. * @param {*} [context] callback context. */ Collection.prototype.doWhenHas = function(id, fn, context) { var item = this.items[id]; if (!util.isExisty(item)) { return; } fn.call(context || this, item); }; /** * Search model. and return new collection. * @param {function} filter filter function. * @returns {Collection} new collection with filtered models. * @example * collection.find(function(item) { * return item.edited === true; * }); * * function filter1(item) { * return item.edited === false; * } * * function filter2(item) { * return item.disabled === false; * } * * collection.find(Collection.and(filter1, filter2)); */ Collection.prototype.find = function(filter) { var result = new Collection(); if (this.hasOwnProperty('getItemID')) { result.getItemID = this.getItemID; } this.each(function(item) { if (filter(item) === true) { result.add(item); } }); return result; }; /** * Group element by specific key values. * * if key parameter is function then invoke it and use returned value. * @param {(string|number|function|array)} key key property or getter function. * if string[] supplied, create each collection before grouping. * @param {function} [groupFunc] - function that return each group's key * @returns {object.<string, Collection>} grouped object * @example * * // pass `string`, `number`, `boolean` type value then group by property value. * collection.groupBy('gender'); // group by 'gender' property value. * collection.groupBy(50); // group by '50' property value. * * // pass `function` then group by return value. each invocation `function` is called with `(item)`. * collection.groupBy(function(item) { * if (item.score > 60) { * return 'pass'; * } * return 'fail'; * }); * * // pass `array` with first arguments then create each collection before grouping. * collection.groupBy(['go', 'ruby', 'javascript']); * // result: { 'go': empty Collection, 'ruby': empty Collection, 'javascript': empty Collection } * * // can pass `function` with `array` then group each elements. * collection.groupBy(['go', 'ruby', 'javascript'], function(item) { * if (item.isFast) { * return 'go'; * } * * return item.name; * }); */ Collection.prototype.groupBy = function(key, groupFunc) { var result = {}, collection, baseValue, keyIsFunc = isFunc(key), getItemIDFn = this.getItemID; if (util.isArray(key)) { util.forEachArray(key, function(k) { result[String(k)] = new Collection(getItemIDFn); }); if (!groupFunc) { return result; } key = groupFunc; keyIsFunc = true; } this.each(function(item) { if (keyIsFunc) { baseValue = key(item); } else { baseValue = item[key]; if (isFunc(baseValue)) { baseValue = baseValue.apply(item); } } collection = result[baseValue]; if (!collection) { collection = result[baseValue] = new Collection(getItemIDFn); } collection.add(item); }); return result; }; /** * Return single item in collection. * * Returned item is inserted in this collection firstly. * @param {function} [filter] - function filter * @returns {object} item. */ Collection.prototype.single = function(filter) { var result, useFilter = util.isFunction(filter); this.each(function(item) { if (!useFilter) { result = item; return false; // returning false can stop this loop } if (filter(item)) { result = item; return false; // returning false can stop this loop } return true; }, this); return result; }; /** * sort a basis of supplied compare function. * @param {function} compareFunction compareFunction * @returns {array} sorted array. */ Collection.prototype.sort = function(compareFunction) { var arr = []; this.each(function(item) { arr.push(item); }); if (isFunc(compareFunction)) { arr = arr.sort(compareFunction); } return arr; }; /** * iterate each model element. * * when iteratee return false then break the loop. * @param {function} iteratee iteratee(item, index, items) * @param {*} [context] context */ Collection.prototype.each = function(iteratee, context) { forEachProp(this.items, iteratee, context || this); }; /** * return new array with collection items. * @returns {array} new array. */ Collection.prototype.toArray = function() { if (!this.length) { return []; } return util.map(this.items, function(item) { return item; }); }; module.exports = Collection;