UNPKG

@omneedia/socketcluster

Version:

SocketCluster - A Highly parallelized WebSocket server cluster to make the most of multi-core machines/instances.

387 lines (301 loc) 9.21 kB
'use strict'; /** * Constants. */ var errorMessage; errorMessage = 'An argument without append, prepend, ' + 'or detach methods was given to `List'; /** * Creates a new List: A linked list is a bit like an Array, but * knows nothing about how many items are in it, and knows only about its * first (`head`) and last (`tail`) items. Each item (e.g. `head`, `tail`, * &c.) knows which item comes before or after it (its more like the * implementation of the DOM in JavaScript). * @global * @private * @constructor * @class Represents an instance of List. */ function List(/*items...*/) { if (arguments.length) { return List.from(arguments); } } var ListPrototype; ListPrototype = List.prototype; /** * Creates a new list from the arguments (each a list item) passed in. * @name List.of * @param {...ListItem} [items] - Zero or more items to attach. * @returns {list} - A new instance of List. */ List.of = function (/*items...*/) { return List.from.call(this, arguments); }; /** * Creates a new list from the given array-like object (each a list item) * passed in. * @name List.from * @param {ListItem[]} [items] - The items to append. * @returns {list} - A new instance of List. */ List.from = function (items) { var list = new this(), length, iterator, item; if (items && (length = items.length)) { iterator = -1; while (++iterator < length) { item = items[iterator]; if (item !== null && item !== undefined) { list.append(item); } } } return list; }; /** * List#head * Default to `null`. */ ListPrototype.head = null; /** * List#tail * Default to `null`. */ ListPrototype.tail = null; /** * Returns the list's items as an array. This does *not* detach the items. * @name List#toArray * @returns {ListItem[]} - An array of (still attached) ListItems. */ ListPrototype.toArray = function () { var item = this.head, result = []; while (item) { result.push(item); item = item.next; } return result; }; /** * Prepends the given item to the list: Item will be the new first item * (`head`). * @name List#prepend * @param {ListItem} item - The item to prepend. * @returns {ListItem} - An instance of ListItem (the given item). */ ListPrototype.prepend = function (item) { if (!item) { return false; } if (!item.append || !item.prepend || !item.detach) { throw new Error(errorMessage + '#prepend`.'); } var self, head; // Cache self. self = this; // If self has a first item, defer prepend to the first items prepend // method, and return the result. head = self.head; if (head) { return head.prepend(item); } // ...otherwise, there is no `head` (or `tail`) item yet. // Detach the prependee. item.detach(); // Set the prependees parent list to reference self. item.list = self; // Set self's first item to the prependee, and return the item. self.head = item; return item; }; /** * Appends the given item to the list: Item will be the new last item (`tail`) * if the list had a first item, and its first item (`head`) otherwise. * @name List#append * @param {ListItem} item - The item to append. * @returns {ListItem} - An instance of ListItem (the given item). */ ListPrototype.append = function (item) { if (!item) { return false; } if (!item.append || !item.prepend || !item.detach) { throw new Error(errorMessage + '#append`.'); } var self, head, tail; // Cache self. self = this; // If self has a last item, defer appending to the last items append // method, and return the result. tail = self.tail; if (tail) { return tail.append(item); } // If self has a first item, defer appending to the first items append // method, and return the result. head = self.head; if (head) { return head.append(item); } // ...otherwise, there is no `tail` or `head` item yet. // Detach the appendee. item.detach(); // Set the appendees parent list to reference self. item.list = self; // Set self's first item to the appendee, and return the item. self.head = item; return item; }; /** * Creates a new ListItem: A linked list item is a bit like DOM node: * It knows only about its "parent" (`list`), the item before it (`prev`), * and the item after it (`next`). * @global * @private * @constructor * @class Represents an instance of ListItem. */ function ListItem() {} List.Item = ListItem; var ListItemPrototype = ListItem.prototype; ListItemPrototype.next = null; ListItemPrototype.prev = null; ListItemPrototype.list = null; /** * Detaches the item operated on from its parent list. * @name ListItem#detach * @returns {ListItem} - The item operated on. */ ListItemPrototype.detach = function () { // Cache self, the parent list, and the previous and next items. var self = this, list = self.list, prev = self.prev, next = self.next; // If the item is already detached, return self. if (!list) { return self; } // If self is the last item in the parent list, link the lists last item // to the previous item. if (list.tail === self) { list.tail = prev; } // If self is the first item in the parent list, link the lists first item // to the next item. if (list.head === self) { list.head = next; } // If both the last and first items in the parent list are the same, // remove the link to the last item. if (list.tail === list.head) { list.tail = null; } // If a previous item exists, link its next item to selfs next item. if (prev) { prev.next = next; } // If a next item exists, link its previous item to selfs previous item. if (next) { next.prev = prev; } // Remove links from self to both the next and previous items, and to the // parent list. self.prev = self.next = self.list = null; // Return self. return self; }; /** * Prepends the given item *before* the item operated on. * @name ListItem#prepend * @param {ListItem} item - The item to prepend. * @returns {ListItem} - The item operated on, or false when that item is not * attached. */ ListItemPrototype.prepend = function (item) { if (!item || !item.append || !item.prepend || !item.detach) { throw new Error(errorMessage + 'Item#prepend`.'); } // Cache self, the parent list, and the previous item. var self = this, list = self.list, prev = self.prev; // If self is detached, return false. if (!list) { return false; } // Detach the prependee. item.detach(); // If self has a previous item... if (prev) { // ...link the prependees previous item, to selfs previous item. item.prev = prev; // ...link the previous items next item, to self. prev.next = item; } // Set the prependees next item to self. item.next = self; // Set the prependees parent list to selfs parent list. item.list = list; // Set the previous item of self to the prependee. self.prev = item; // If self is the first item in the parent list, link the lists first item // to the prependee. if (self === list.head) { list.head = item; } // If the the parent list has no last item, link the lists last item to // self. if (!list.tail) { list.tail = self; } // Return the prependee. return item; }; /** * Appends the given item *after* the item operated on. * @name ListItem#append * @param {ListItem} item - The item to append. * @returns {ListItem} - The item operated on, or false when that item is not * attached. */ ListItemPrototype.append = function (item) { // If item is falsey, return false. if (!item || !item.append || !item.prepend || !item.detach) { throw new Error(errorMessage + 'Item#append`.'); } // Cache self, the parent list, and the next item. var self = this, list = self.list, next = self.next; // If self is detached, return false. if (!list) { return false; } // Detach the appendee. item.detach(); // If self has a next item... if (next) { // ...link the appendees next item, to selfs next item. item.next = next; // ...link the next items previous item, to the appendee. next.prev = item; } // Set the appendees previous item to self. item.prev = self; // Set the appendees parent list to selfs parent list. item.list = list; // Set the next item of self to the appendee. self.next = item; // If the the parent list has no last item or if self is the parent lists // last item, link the lists last item to the appendee. if (self === list.tail || !list.tail) { list.tail = item; } // Return the appendee. return item; }; /** * Expose `List`. */ module.exports = List;