UNPKG

enhanced-spotify-api

Version:

Object-oriented library to work with Spotify's API. Includes wrapper for regular endpoints and additional functionality and grouping of requests.

395 lines (352 loc) 10.7 kB
const Models = require('../../index'); /** * Creates a new Container instance * * @param {Array | Item | object | string} data (optional) Data to be preloaded, * Single or multiple items */ function Container(items) { this.items = {}; this.order = []; if (items) { if (items instanceof Array) { this.concat(items); } else if (items instanceof Models[this.type] || typeof (items) === 'string' || typeof (items) === 'object') { this.push(items); } else { throw new Error(`${this.name}.constructor: Invalid Parameter "items"`); } } } Container.prototype = { /** * Adds new item to Container object * * @param {Item | object | string } item Item instance, item object or item ID to add */ push(item) { if (item instanceof Models[this.type]) { if (!(item.id in this.items)) { this.items[item.id] = item; } this.order.push(item.id); } else if (typeof (item) === 'object') { if ('id' in item) { if (!(item.id in this.items)) { this.items[item.id] = new Models[this.type](item); } this.order.push(item.id); } else if (this.uri_type in item) { if (!(item[this.uri_type].id in this.items)) { this.items[item[this.uri_type].id] = new Models[this.type](item[this.uri_type]); const itemKeys = Object.keys(item); for (let i = 0; i < itemKeys.length; i += 1) { if (itemKeys[i] !== this.uri_type) { this.items[item[this.uri_type].id][itemKeys[i]] = item[itemKeys[i]]; } } } this.order.push(item[this.uri_type].id); } else { throw new Error(`${this.name}.push: Invalid Parameter "item"`); } } else if (typeof (item) === 'string') { if (!(item in this.items)) { this.items[item] = new Models[this.type](item); } this.order.push(item); } else { throw new Error(`${this.name}.push: Invalid Parameter "item"`); } }, /** * Adds new items to Container object * * @param {Items | Array } items Another Items instance or array of Item instances, * item objects, or item IDs to concat */ concat(items) { if (items instanceof Models[this.name]) { for (let i = 0; i < items.order.length; i += 1) { if (!(items.order[i] in this.items)) { this.items[items.order[i]] = items.items[items.order[i]]; } this.order.push(items.order[i]); } } else if (items instanceof Array) { for (let i = 0; i < items.length; i += 1) { this.push(items[i]); } } else { throw new Error(`${this.name}.concat: Invalid Parameter "items"`); } }, /** * Returns number of items in Container * * @returns {number} Number of items in Container */ size() { return this.order.length; }, /** * Returns boolean if item is contained * * @param {Item | object | string } item Item instance, item data, or item ID to remove * @returns {boolean} Whether item is contained */ includes(item) { let id = null; if (typeof (item) === 'string') { id = item; } else if ((item instanceof Models[this.type] || typeof (item) === 'object') && 'id' in item) { id = item.id; } else { throw new Error(`${this.name}.includes: Invalid Parameter "item"`); } return (id in this.items); }, /** * Find index of an item * * @param {Item | object | string } item Item instance, item data, or item ID to remove * @param {number} start Where to start searching from, * Negative values will start at the given position from the end (Inclusive) * @returns {number} Index of item */ indexOf(item, start) { if (start > this.order.length - 1) { throw new Error(`${this.name}.findIndex: Invalid Parameter "start"`); } let id = null; if (typeof (item) === 'string') { id = item; } else if ((item instanceof Models[this.type] || typeof (item) === 'object') && 'id' in item) { id = item.id; } else { throw new Error(`${this.name}.findIndex: Invalid Parameter "item"`); } return this.order.indexOf(id, start); }, /** * Returns item at a given index * * @param {number} index Index of the item desired, * Negative value will return item that far from the end * @returns {Item} Item at a given index */ get(index) { return this.items[this.order[index >= 0 ? index : (this.order.length - 1) - index]]; }, /** * Returns array of IDs in order * * @returns {Array} Array of IDs */ getIDs() { return this.order; }, /** * Returns array of IDs in order with no repeats * * @returns {Array} Array of IDs */ getIDsNoRepeats() { const ids = []; for (let i = 0; i < this.order.length; i += 1) { if (!ids.includes(this.order[i])) { ids.push(this.order[i]); } } return ids; }, /** * Returns array of URIs in order * @returns {Array} Array of URIs */ getURIs() { return this.order.map((id) => `spotify:${this.uri_type}:${id}`); }, /** * Returns array of URIs with no repeats * * @returns {Array} Array of URIs */ getURIsNoRepeats() { const ids = this.getIDsNoRepeats(); return ids.map((id) => `spotify:${this.uri_type}:${id}`); }, /** * Removes last item * * @returns {Item} Removed item */ pop() { if (this.order.length) { const id = this.order.pop(); const item = this.items[id]; if (!(this.order.includes(id))) { delete this.items[id]; } return item; } return null; }, /** * Removes first item * * @returns {Item} Removed item */ shift() { if (this.order.length) { const id = this.order.shift(); const item = this.items[id]; if (!(this.order.includes(id))) { delete this.items[id]; } return item; } return null; }, /** * Removes elements * * @param {Number} start Start of removal * @param {Number} end End of removal (Exclusive) * @returns {Items} Removed Items */ slice(start, end) { const ids = this.order.splice( start || 0, end - start || this.order.length - start, ); const items = new Models[this.name](ids.map((id) => this.items[id])); for (let i = 0; i < ids.length; i += 1) { if (!(this.order.includes(ids[i]))) { delete this.items[ids[i]]; } } return items; }, /** * Removes an item from the Container object * * @param {Item | object | string } item Item instance, item data, or item ID to remove * @returns {Item} Deleted item */ remove(item) { let id = null; if (item instanceof Models[this.type] || typeof (item) === 'object') { id = item.id; } else if (typeof (item) === 'string') { id = item; } else { throw new Error(`${this.name}.remove: Invalid Parameter "item"`); } this.order = this.order.filter((itemID) => itemID !== id); const deletedItem = this.items[id]; delete this.items[id]; return deletedItem; }, /** * Removes multiple items by index from the Container object * * @param {Array} indexes Indexes to be removed * @returns {Items} Deleted items */ removeIndexes(indexes) { const sorted = indexes.sort((a, b) => b - a); if (sorted[0] > (this.order.length - 1) || sorted[sorted.length - 1] < 0) { throw new Error(`${this.name}.removeIndexes: Invalid Parameter "indexes", out of range.`); } const deleted = new Models[this.name](); for (let i = 0; i < sorted.length; i += 1) { const id = this.order[sorted[i]]; this.order.splice(sorted[i], 1); deleted.push(this.items[id]); if (!this.order.includes(id)) { delete this.items[id]; } } deleted.reverse(); return deleted; }, /** * Runs a function on each item * * @param {Function} method Function to be run on each item * @param {Function} thisArg Value to use as "this" when executing callback */ forEach(method, thisArg) { const items = this.order.map((item) => this.items[item]); items.forEach(method, thisArg || this); }, /** * Returns Items object with filtered items * * @param {Function} method Method to filter by * @param {Function} thisArg Value to use as "this" when executing callback * @returns {Items} Filtered Items object */ filter(method, thisArg) { const newContainer = new Models[this.name](); const items = this.order.map((item) => this.items[item]); const filteredItems = items.filter(method, thisArg || this); newContainer.concat(filteredItems); return newContainer; }, /** * Sort items * @param {Function} compareFunction Sorting method */ sort(compareFunction) { const items = this.order.map((item) => this.items[item]); const sortedItems = items.sort(compareFunction); this.order = sortedItems.map((item) => item.id); }, /** * Reverses order of items */ reverse() { this.order.reverse(); }, /** * Adds property with value to a given item * * @param {string} id ID of item to alter * @param {string} field Field to set value to * @param {*} value Value to set */ setProperty(id, field, value) { if (id in this.items) { this.items[id][field] = value; } else { throw new Error(`${this.constructor.name}.setProperty: ID does not exist.`); } }, }; /** * Adds functionality to Class * * @param {object} methods Object containing new methods to be added as properties */ Container.addMethods = function addMethods(methods) { const methodNames = Object.keys(methods); for (let i = 0; i < methodNames.length; i += 1) { this.prototype[methodNames[i]] = methods[methodNames[i]]; } }; /** * Replaces a method within the Class * * @param {string} name Name of the method to replace * @param {function} method Function to replace with */ Container.override = function override(name, method) { if (name in this.prototype) { this.prototype[name] = method; } else { throw new Error('Container.override: \'name\' does not exist.'); } }; module.exports = Container;