phaser/src/structs/List.js

917 lines
20 KiB
JavaScript
Raw Normal View History

2018-02-12 16:01:20 +00:00
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../utils/Class');
2018-03-19 21:57:46 +00:00
/**
* @callback EachListCallback
*
* @param {any} item - [description]
* @param {...*} [arguments] - Additional arguments that will be passed to the callback, after the child.
*/
2018-02-09 04:35:23 +00:00
/**
* @classdesc
* [description]
*
* @class List
* @memberOf Phaser.Structs
* @constructor
* @since 3.0.0
*
* @param {any} parent - [description]
*/
var List = new Class({
initialize:
function List (parent)
{
2018-02-09 04:35:23 +00:00
/**
* The parent of this list.
*
* @name Phaser.Structs.List#parent
* @type {any}
* @since 3.0.0
*/
this.parent = parent;
2018-02-09 04:35:23 +00:00
/**
* The objects that belong to this collection.
*
* @name Phaser.Structs.List#list
* @type {array}
* @default []
* @since 3.0.0
*/
this.list = [];
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#position
* @type {integer}
* @default 0
* @since 3.0.0
*/
this.position = 0;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#add
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
add: function (child)
{
// Is child already in this display list?
if (this.getIndex(child) === -1)
{
this.list.push(child);
}
return child;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#addAt
* @since 3.0.0
*
* @param {object} child - [description]
* @param {integer} index - [description]
*
* @return {object} [description]
*/
addAt: function (child, index)
{
if (index === undefined) { index = 0; }
if (this.list.length === 0)
{
return this.add(child);
}
if (index >= 0 && index <= this.list.length)
{
if (this.getIndex(child) === -1)
{
this.list.splice(index, 0, child);
}
}
return child;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#addMultiple
* @since 3.0.0
*
* @param {array} children - [description]
*
* @return {array} [description]
*/
addMultiple: function (children)
{
if (Array.isArray(children))
{
for (var i = 0; i < children.length; i++)
{
this.add(children[i]);
}
}
return children;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#getAt
* @since 3.0.0
*
* @param {integer} index - [description]
*
* @return {object} [description]
*/
getAt: function (index)
{
return this.list[index];
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#getIndex
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {integer} [description]
*/
getIndex: function (child)
{
// Return -1 if given child isn't a child of this display list
return this.list.indexOf(child);
},
2018-02-09 04:35:23 +00:00
/**
* Given an array of objects, sort the array and return it,
* so that the objects are in index order with the lowest at the bottom.
*
* @method Phaser.Structs.List#sort
* @since 3.0.0
*
* @param {array} children - [description]
*
* @return {array} [description]
*/
sort: function (children)
{
if (children === undefined) { children = this.list; }
return children.sort(this.sortIndexHandler.bind(this));
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#sortIndexHandler
* @since 3.0.0
*
* @param {object} childA - [description]
* @param {object} childB - [description]
*
* @return {integer} [description]
*/
sortIndexHandler: function (childA, childB)
{
// The lower the index, the lower down the display list they are
var indexA = this.getIndex(childA);
var indexB = this.getIndex(childB);
if (indexA < indexB)
{
return -1;
}
else if (indexA > indexB)
{
return 1;
}
// Technically this shouldn't happen, but if the GO wasn't part of this display list then it'll
// have an index of -1, so in some cases it can
return 0;
},
/**
2018-02-09 04:35:23 +00:00
* Gets the first item from the set based on the property strictly equaling the value given.
* Returns null if not found.
*
* @method Phaser.Structs.List#getByKey
* @since 3.0.0
*
* @param {string} property - The property to check against the value.
* @param {any} value - The value to check if the property strictly equals.
*
* @return {any} The item that was found, or null if nothing matched.
*/
getByKey: function (property, value)
{
for (var i = 0; i < this.list.length; i++)
{
if (this.list[i][property] === value)
{
return this.list[i];
}
}
return null;
},
/**
2018-02-09 04:35:23 +00:00
* Searches the Group for the first instance of a child with the `name`
* property matching the given argument. Should more than one child have
* the same name only the first instance is returned.
*
* @method Phaser.Structs.List#getByName
* @since 3.0.0
*
* @param {string} name - The name to search for.
*
* @return {any} The first child with a matching name, or null if none were found.
*/
getByName: function (name)
{
return this.getByKey('name', name);
},
/**
2018-02-09 04:35:23 +00:00
* Returns a random child from the group.
*
* @method Phaser.Structs.List#getRandom
* @since 3.0.0
*
* @param {integer} [startIndex=0] - Offset from the front of the group (lowest child).
* @param {integer} [length=(to top)] - Restriction on the number of values you want to randomly select from.
*
* @return {any} A random child of this Group.
*/
getRandom: function (startIndex, length)
{
if (startIndex === undefined) { startIndex = 0; }
if (length === undefined) { length = this.list.length; }
if (length === 0 || length > this.list.length)
{
return null;
}
var randomIndex = startIndex + Math.floor(Math.random() * length);
return this.list[randomIndex];
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#getFirst
* @since 3.0.0
*
2018-03-19 01:00:21 +00:00
* @param {string} property - [description]
* @param {any} value - [description]
* @param {number} [startIndex=0] - [description]
* @param {number} [endIndex] - [description]
2018-02-09 04:35:23 +00:00
*
2018-03-19 01:00:21 +00:00
* @return {any} [description]
2018-02-09 04:35:23 +00:00
*/
getFirst: function (property, value, startIndex, endIndex)
{
if (startIndex === undefined) { startIndex = 0; }
if (endIndex === undefined) { endIndex = this.list.length; }
for (var i = startIndex; i < endIndex; i++)
{
var child = this.list[i];
if (child[property] === value)
{
return child;
}
}
return null;
},
/**
2018-02-09 04:35:23 +00:00
* Returns all children in this List.
*
* You can optionally specify a matching criteria using the `property` and `value` arguments.
*
* For example: `getAll('visible', true)` would return only children that have their visible property set.
*
* Optionally you can specify a start and end index. For example if this List had 100 children,
* and you set `startIndex` to 0 and `endIndex` to 50, it would return matches from only
* the first 50 children in the List.
*
* @method Phaser.Structs.List#getAll
* @since 3.0.0
*
* @param {string} [property] - An optional property to test against the value argument.
* @param {any} [value] - If property is set then Child.property must strictly equal this value to be included in the results.
* @param {integer} [startIndex=0] - The first child index to start the search from.
* @param {integer} [endIndex] - The last child index to search up until.
*
* @return {array} [description]
*/
getAll: function (property, value, startIndex, endIndex)
{
if (startIndex === undefined) { startIndex = 0; }
if (endIndex === undefined) { endIndex = this.list.length; }
var output = [];
for (var i = startIndex; i < endIndex; i++)
{
var child = this.list[i];
if (property)
{
if (child[property] === value)
{
output.push(child);
}
}
else
{
output.push(child);
}
}
return output;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#count
* @since 3.0.0
*
* @param {string} property - [description]
* @param {any} value - [description]
*
* @return {integer} [description]
*/
count: function (property, value)
{
var total = 0;
for (var i = 0; i < this.list.length; i++)
{
var child = this.list[i];
if (child[property] === value)
{
total++;
}
}
return total;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#swap
* @since 3.0.0
*
* @param {object} child1 - [description]
* @param {object} child2 - [description]
*/
swap: function (child1, child2)
{
if (child1 === child2)
{
return;
}
var index1 = this.getIndex(child1);
var index2 = this.getIndex(child2);
if (index1 < 0 || index2 < 0)
{
throw new Error('List.swap: Supplied objects must be children of the same list');
}
this.list[index1] = child2;
this.list[index2] = child1;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#moveTo
* @since 3.0.0
*
* @param {object} child - [description]
* @param {integer} index - [description]
*
* @return {object} [description]
*/
moveTo: function (child, index)
{
var currentIndex = this.getIndex(child);
if (currentIndex === -1 || index < 0 || index >= this.list.length)
{
throw new Error('List.moveTo: The supplied index is out of bounds');
}
// Remove
this.list.splice(currentIndex, 1);
// Add in new location
this.list.splice(index, 0, child);
return child;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#remove
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
remove: function (child)
{
var index = this.list.indexOf(child);
if (index !== -1)
{
this.list.splice(index, 1);
}
2018-03-19 01:00:21 +00:00
return child;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#removeAt
* @since 3.0.0
*
* @param {integer} index - [description]
*
* @return {object} [description]
*/
removeAt: function (index)
{
var child = this.list[index];
if (child)
{
this.children.splice(index, 1);
}
return child;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#removeBetween
* @since 3.0.0
*
* @param {integer} beginIndex - [description]
* @param {integer} endIndex - [description]
*
* @return {array} [description]
*/
removeBetween: function (beginIndex, endIndex)
{
if (beginIndex === undefined) { beginIndex = 0; }
if (endIndex === undefined) { endIndex = this.list.length; }
var range = endIndex - beginIndex;
if (range > 0 && range <= endIndex)
{
var removed = this.list.splice(beginIndex, range);
return removed;
}
else if (range === 0 && this.list.length === 0)
{
return [];
}
else
{
throw new Error('List.removeBetween: Range Error, numeric values are outside the acceptable range');
}
},
/**
2018-02-09 04:35:23 +00:00
* Removes all the items.
*
* @method Phaser.Structs.List#removeAll
* @since 3.0.0
*
* @return {Phaser.Structs.List} This List object.
*/
removeAll: function ()
{
var i = this.list.length;
while (i--)
{
this.remove(this.list[i]);
}
return this;
},
/**
2018-02-09 04:35:23 +00:00
* Brings the given child to the top of this List.
*
* @method Phaser.Structs.List#bringToTop
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
bringToTop: function (child)
{
if (this.getIndex(child) < this.list.length)
{
this.remove(child);
this.add(child);
}
return child;
},
/**
2018-02-09 04:35:23 +00:00
* Sends the given child to the bottom of this List.
*
* @method Phaser.Structs.List#sendToBack
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
sendToBack: function (child)
{
if (this.getIndex(child) > 0)
{
this.remove(child);
this.addAt(child, 0);
}
return child;
},
/**
2018-02-09 04:35:23 +00:00
* Moves the given child up one place in this group unless it's already at the top.
*
* @method Phaser.Structs.List#moveUp
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
moveUp: function (child)
{
var a = this.getIndex(child);
if (a !== -1 && a < this.list.length - 1)
{
var b = this.getAt(a + 1);
if (b)
{
this.swap(child, b);
}
}
return child;
},
/**
2018-02-09 04:35:23 +00:00
* Moves the given child down one place in this group unless it's already at the bottom.
*
* @method Phaser.Structs.List#moveDown
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {object} [description]
*/
moveDown: function (child)
{
var a = this.getIndex(child);
if (a > 0)
{
var b = this.getAt(a - 1);
if (b)
{
this.swap(child, b);
}
}
return child;
},
/**
2018-02-09 04:35:23 +00:00
* Reverses the order of all children in this List.
*
* @method Phaser.Structs.List#reverse
* @since 3.0.0
*
* @return {Phaser.Structs.List} This List object.
*/
reverse: function ()
{
this.list.reverse();
return this;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @method Phaser.Structs.List#shuffle
* @since 3.0.0
*
* @return {Phaser.Structs.List} This List object.
*/
shuffle: function ()
{
for (var i = this.list.length - 1; i > 0; i--)
{
var j = Math.floor(Math.random() * (i + 1));
var temp = this.list[i];
this.list[i] = this.list[j];
this.list[j] = temp;
}
return this;
},
/**
2018-02-09 04:35:23 +00:00
* Replaces a child of this List with the given newChild. The newChild cannot be a member of this List.
*
* @method Phaser.Structs.List#replace
* @since 3.0.0
*
* @param {object} oldChild - The child in this List that will be replaced.
* @param {object} newChild - The child to be inserted into this List.
*
* @return {object} Returns the oldChild that was replaced within this group.
*/
replace: function (oldChild, newChild)
{
var index = this.getIndex(oldChild);
if (index !== -1)
{
this.remove(oldChild);
this.addAt(newChild, index);
return oldChild;
}
},
/**
2018-02-09 04:35:23 +00:00
* [description]
*
* @method Phaser.Structs.List#exists
* @since 3.0.0
*
* @param {object} child - [description]
*
* @return {boolean} True if the item is found in the list, otherwise false.
*/
exists: function (child)
{
return (this.list.indexOf(child) > -1);
},
/**
2018-02-09 04:35:23 +00:00
* Sets the property `key` to the given value on all members of this List.
*
* @method Phaser.Structs.List#setAll
* @since 3.0.0
*
* @param {string} key - [description]
* @param {any} value - [description]
*/
setAll: function (key, value)
{
for (var i = 0; i < this.list.length; i++)
{
if (this.list[i])
{
this.list[i][key] = value;
}
}
},
/**
2018-02-09 04:35:23 +00:00
* Passes all children to the given callback.
*
* @method Phaser.Structs.List#each
* @since 3.0.0
*
2018-03-19 21:57:46 +00:00
* @param {EachListCallback} callback - The function to call.
2018-02-09 04:35:23 +00:00
* @param {object} [thisArg] - Value to use as `this` when executing callback.
* @param {...*} [arguments] - Additional arguments that will be passed to the callback, after the child.
*/
each: function (callback, thisArg)
{
var args = [ null ];
for (var i = 1; i < arguments.length; i++)
{
args.push(arguments[i]);
}
for (i = 0; i < this.list.length; i++)
{
args[0] = this.list[i];
callback.apply(thisArg, args);
}
},
/**
* [description]
*
* @method Phaser.Structs.List#shutdown
* @since 3.0.0
*/
shutdown: function ()
{
this.removeAll();
},
/**
* [description]
*
* @method Phaser.Structs.List#destroy
* @since 3.0.0
*/
destroy: function ()
{
this.removeAll();
this.list = [];
this.parent = null;
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#length
* @type {integer}
* @readOnly
* @since 3.0.0
*/
length: {
get: function ()
{
return this.list.length;
}
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#first
* @type {integer}
* @readOnly
* @since 3.0.0
*/
first: {
get: function ()
{
this.position = 0;
if (this.list.length > 0)
{
return this.list[0];
}
else
{
return null;
}
}
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#last
* @type {integer}
* @readOnly
* @since 3.0.0
*/
last: {
get: function ()
{
if (this.list.length > 0)
{
this.position = this.list.length - 1;
return this.list[this.position];
}
else
{
return null;
}
}
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#next
* @type {integer}
* @readOnly
* @since 3.0.0
*/
next: {
get: function ()
{
if (this.position < this.list.length)
{
this.position++;
return this.list[this.position];
}
else
{
return null;
}
}
},
2018-02-09 04:35:23 +00:00
/**
* [description]
*
* @name Phaser.Structs.List#previous
* @type {integer}
* @readOnly
* @since 3.0.0
*/
previous: {
get: function ()
{
if (this.position > 0)
{
this.position--;
return this.list[this.position];
}
else
{
return null;
}
}
}
});
module.exports = List;