@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
JavaScript
;
/**
* 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;