waves/public/assets/g/mario/References/EightBittr-0.2.0.ts
2025-04-09 17:11:14 -05:00

1124 lines
42 KiB
TypeScript

declare module EightBittr {
/**
* A basic representation of an in-game Thing. Size, velocity, and position
* are stored, as well as a reference to the parent IEightBittr.
*/
export interface IThing {
EightBitter: IEightBittr;
top: number;
right: number;
bottom: number;
left: number;
width: number;
height: number;
xvel: number;
yvel: number;
}
/**
* Settings to initialize a new instance of the IEightBittr interface.
*/
export interface IEightBittrSettings {
/**
* How much to expand each pixel from raw sizing measurements to in-game.
*/
unitsize?: number;
/**
* A list of member attributes to copy from the source to the IEightBittr.
*/
constants?: string[];
/**
* Where the constants should be copied from, if not the IEightBittr itself.
*/
constantsSource?: any;
}
/**
* A summary of times for reset Functions and the overall operation.
*/
export interface IResetTime {
/**
* The name of the reset Function called.
*/
name: string;
/**
* The performance.now timestamp of when the reset started.
*/
timeStart: number;
/**
* The performance.now timestamp of when the reset finished.
*/
timeEnd: number;
/**
* Exactly how long it took between timeStart and timeEnd.
*/
timeTaken: number;
}
/**
* A summary of times for reset Functions and the overall operation.
*/
export interface IResetTimes {
/**
* What order the reset Functions were called.
*/
order: string[];
/**
* The total start, end, and taken times for the reset operation.
*/
total: IResetTime;
/**
* Start, end, and taken times for individual reset Functions, in order.
*/
times: IResetTime[];
}
/**
* An interface used exclusively as the parent of IGameStartr. IEightBittr
* contains useful functions for manipulating IThings that are independent of
* the required GameStartr modules.
*/
export interface IEightBittr {
/**
* How much to expand each pixel from raw sizing measurements to in-game.
*/
unitsize: number;
/**
* Timed result summaries of resetTimed, if it was called.
*/
resetTimes: EightBittr.IResetTimes;
/**
* Resets the EightBittr by calling all of the named reset member Functions
* on itself.
*
* @param EightBitter
* @param resets The ordered Array of reset Functions to be called.
* @param customs Additional arguments to pass to all reset Functions.
*/
reset(EightBitter: IEightBittr, resets: string[], customs?: any): void;
/**
* Resets the EightBittr in a timed manner by calling all the named reset
* member Functions on itself and adding the time (in milliseconds) along
* along with the total process time to an Array, which is then returned.
*
* @param EightBitter
* @param resets The ordered Array of reset Functions to be called.
* @param customs Additional arguments to pass to all reset Functions.
*/
resetTimed(EightBitter: IEightBittr, resets: string[], customs?: any): void;
/**
* Creates and returns a new HTML <canvas> element with no image smoothing.
*
* @param width How wide the canvas should be.
* @param height How tall the canvas should be.
* @returns A canvas of the given width and height.
*/
createCanvas(width: number, height: number): HTMLCanvasElement;
/**
* Shifts a Thing vertically by changing its top and bottom attributes.
*
* @param thing The Thing to shift vertically.
* @param dy How far to shift the Thing.
*/
shiftVert(thing: IThing, dy: number): void;
/**
* Shifts a Thing horizontally by changing its top and bottom attributes.
*
* @param thing The Thing to shift horizontally.
* @param dy How far to shift the Thing.
*/
shiftHoriz(thing: IThing, dx: number): void;
/**
* Sets the top of a Thing to a set number, changing the bottom based on its
* height and the EightBittr's unisize.
*
* @param thing The Thing to shift vertically.
* @param top Where the Thing's top should be.
*/
setTop(thing: IThing, top: number): void;
/**
* Sets the right of a Thing to a set number, changing the left based on its
* width and the EightBittr's unisize.
*
* @param thing The Thing to shift horizontally.
* @param top Where the Thing's right should be.
*/
setRight(thing: IThing, right: number): void;
/**
* Sets the bottom of a Thing to a set number, changing the top based on its
* height and the EightBittr's unisize.
*
* @param thing The Thing to shift vertically.
* @param top Where the Thing's bottom should be.
*/
setBottom(thing: IThing, bottom: number): void;
/**
* Sets the left of a Thing to a set number, changing the right based on its
* width and the EightBittr's unisize.
*
* @param thing The Thing to shift horizontally.
* @param top Where the Thing's left should be.
*/
setLeft(thing: IThing, left: number): void;
/**
* Shifts a Thing so that it is horizontally centered on the given x.
*
* @param thing The Thing to shift horizontally.
* @param x Where the Thing's horizontal midpoint should be.
*/
setMidX(thing: IThing, x: number): void;
/**
* Shifts a Thing so that it is vertically centered on the given y.
*
* @param thing The Thing to shift vertically.
* @param y Where the Thing's vertical midpoint should be.
*/
setMidY(thing: IThing, y: number): void;
/**
* Shifts a Thing so that it is centered on the given x and y.
*
* @param thing The Thing to shift vertically and horizontally.
* @param x Where the Thing's horizontal midpoint should be.
* @param y Where the Thing's vertical midpoint should be.
*/
setMid(thing: IThing, x: number, y: number): void;
/**
* @param thing
* @returns The horizontal midpoint of the Thing.
*/
getMidX(thing: IThing): number;
/**
* @param thing
* @returns The vertical midpoint of the Thing.
*/
getMidY(thing: IThing): number;
/**
* Shifts a Thing so that its midpoint is centered on the midpoint of the
* other Thing.
*
* @param thing The Thing to be shifted.
* @param other The Thing whose midpoint is referenced.
*/
setMidObj(thing: IThing, other: IThing): void;
/**
* Shifts a Thing so that its horizontal midpoint is centered on the
* midpoint of the other Thing.
*
* @param thing The Thing to be shifted horizontally.
* @param other The Thing whose horizontal midpoint is referenced.
*/
setMidXObj(thing: IThing, other: IThing): void;
/**
* Shifts a Thing so that its vertical midpoint is centered on the
* midpoint of the other Thing.
*
* @param thing The Thing to be shifted vertically.
* @param other The Thing whose vertical midpoint is referenced.
*/
setMidYObj(thing: IThing, other: IThing): void;
/**
* @param thing
* @param other
* @returns Whether the first Thing's midpoint is to the left of the other's.
*/
objectToLeft(thing: IThing, other: IThing): boolean;
/**
* Shifts a Thing's top up, then sets the bottom (similar to a shiftVert and
* a setTop combined).
*
* @param thing The Thing to be shifted vertically.
* @param dy How far to shift the Thing vertically.
*/
updateTop(thing: IThing, dy: number): void;
/**
* Shifts a Thing's right, then sets the left (similar to a shiftHoriz and a
* setRight combined).
*
* @param thing The Thing to be shifted horizontally.
* @param dx How far to shift the Thing horizontally.
*/
updateRight(thing: IThing, dx: number): void;
/**
* Shifts a Thing's bottom down, then sets the bottom (similar to a
* shiftVert and a setBottom combined).
*
* @param thing The Thing to be shifted vertically.
* @param dy How far to shift the Thing vertically.
*/
updateBottom(thing: IThing, dy: number): void;
/**
* Shifts a Thing's left, then sets the right (similar to a shiftHoriz and a
* setLeft combined).
*
* @param thing The Thing to be shifted horizontally.
* @param dx How far to shift the Thing horizontally.
*/
updateLeft(thing: IThing, dx: number): void;
/**
* Shifts a Thing toward a target x, but limits the total distance allowed.
* Distance is computed as from the Thing's horizontal midpoint.
*
* @param thing The Thing to be shifted horizontally.
* @param x How far to shift the Thing horizontally.
* @param maxDistance The maximum distance the Thing can be shifted.
*/
slideToX(thing: IThing, x: number, maxDistance: number): void;
/**
* Shifts a Thing toward a target y, but limits the total distance allowed.
* Distance is computed as from the Thing's vertical midpoint.
*
* @param thing The Thing to be shifted vertically.
* @param x How far to shift the Thing vertically.
* @param maxDistance The maximum distance the Thing can be shifted.
*/
slideToY(thing: IThing, y: number, maxDistance: number): void;
/**
* Ensures the current object is an EightBittr by throwing an error if it
* is not. This should be used for functions in any EightBittr descendants
* that have to call 'this' to ensure their caller is what the programmer
* expected it to be.
*
* @param current The scope that should be an EightBittr.
*/
ensureCorrectCaller(current: any): IEightBittr;
/**
* "Proliferates" all properties of a donor onto a recipient by copying each
* of them and recursing onto child Objects. This is a deep copy.
*
* @param recipient An object to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferate(recipient: any, donor: any, noOverride?: boolean): any;
/**
* Identical to proliferate, but instead of checking whether the recipient
* hasOwnProperty on properties, it just checks if they're truthy.
*
* @param recipient An object to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferateHard(recipient: any, donor: any, noOverride?: boolean): any;
/**
* Identical to proliferate, but tailored for HTML elements because many
* element attributes don't play nicely with JavaScript Array standards.
* Looking at you, HTMLCollection!
*
* @param recipient An HTMLElement to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferateElement(recipient: HTMLElement, donor: any, noOverride?: boolean): HTMLElement;
/**
* Creates and returns an HTMLElement of the specified type. Any additional
* settings Objects may be given to be proliferated onto the Element via
* proliferateElement.
*
* @param type The tag of the Element to be created.
* @param settings Additional settings to proliferated onto the Element.
* @returns {HTMLElement}
*/
createElement(tag?: string, ...args: any[]): HTMLElement;
/**
* Follows a path inside an Object recursively, based on a given path.
*
* @param object A container to follow a path inside.
* @param path The ordered names of attributes to descend into.
* @param num The starting index in path (by default, 0).
* @returns The discovered property within object, or undefined if the
* full path doesn't exist.
*/
followPathHard(object: any, path: string[], index?: number): any;
/**
* Switches an object from one Array to another using splice and push.
*
* @param object The object to move between Arrays.
* @param arrayOld The Array to take the object out of.
* @param arrayNew The Array to move the object into.
*/
arraySwitch(object: any, arrayOld: any[], arrayNew: any[]): void;
/**
* Sets a Thing's position within an Array to the front by splicing and then
* unshifting it.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
*/
arrayToBeginning(object: any, array: any[]): void;
/**
* Sets a Thing's position within an Array to the front by splicing and then
* pushing it.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
*/
arrayToEnd(object: any, array: any[]): void;
/**
* Sets a Thing's position within an Array to a specific index by splicing
* it out, then back in.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
* @param index Where the object should be moved to in the Array.
*/
arrayToIndex(object: any, array: any[], index: number): void;
}
}
module EightBittr {
"use strict";
/**
* An abstract class used exclusively as the parent of GameStartr. EightBittr
* contains useful functions for manipulating Things that are independent of
* the required GameStartr modules.
*/
export class EightBittr implements IEightBittr {
/**
* How much to expand each pixel from raw sizing measurements to in-game.
*/
public unitsize: number;
/**
* Timed result summaries of resetTimed, if it was called.
*/
public resetTimes: EightBittr.IResetTimes;
/**
* Any custom settings passed in during construction to be passed to
* reset Functions.
*/
protected customs: any;
/**
* A listing of the names of all member variables of this EightBittr
* that should be pulled in from the setting's constantsSource.
*/
protected constants: string[];
/**
* Initializes a new instance of the EightBittr class. Constants are copied
* onto the EightBittr from the designated source.
*
* @param settings Any optional custom settings.
*/
constructor(settings: IEightBittrSettings = {}) {
var EightBitter: EightBittr = EightBittr.prototype.ensureCorrectCaller(this),
constants: any = settings.constants,
constantsSource: any = settings.constantsSource || EightBitter,
i: number;
EightBitter.unitsize = settings.unitsize || 1;
EightBitter.constants = constants;
if (constants) {
for (i = 0; i < constants.length; i += 1) {
EightBitter[constants[i]] = constantsSource[constants[i]];
}
}
}
/* Resets
*/
/**
* Resets the EightBittr by calling all of the named reset member Functions
* on itself.
*
* @param EightBitter
* @param resets The ordered Array of reset Functions to be called.
* @param customs Additional arguments to pass to all reset Functions.
*/
reset(EightBitter: EightBittr, resets: string[], customs?: any): void {
var reset: string,
i: number;
EightBitter.customs = customs;
for (i = 0; i < resets.length; i += 1) {
reset = resets[i];
if (!EightBitter[reset]) {
throw new Error(reset + " is missing on a resetting EightBittr.");
}
EightBitter[reset](EightBitter, customs);
}
}
/**
* Resets the EightBittr in a timed manner by calling all the named reset
* member Functions on itself and adding the time (in milliseconds) along
* along with the total process time to an Array, which is then returned.
*
* @param EightBitter
* @param resets The ordered Array of reset Functions to be called.
* @param customs Additional arguments to pass to all reset Functions.
*/
resetTimed(EightBitter: EightBittr, resets: string[], customs?: any): void {
var timeStartTotal: number = performance.now(),
timeEndTotal: number,
timeStart: number,
timeEnd: number,
i: number;
this.resetTimes = <any>{
order: resets,
times: []
};
for (i = 0; i < resets.length; i += 1) {
timeStart = performance.now();
EightBitter[resets[i]](EightBitter, customs);
timeEnd = performance.now();
this.resetTimes.times.push({
"name": resets[i],
"timeStart": timeStart,
"timeEnd": timeEnd,
"timeTaken": timeEnd - timeStart
});
}
timeEndTotal = performance.now();
this.resetTimes.total = {
"name": "resetTimed",
"timeStart": timeStartTotal,
"timeEnd": timeEndTotal,
"timeTaken": timeEndTotal - timeStartTotal
};
}
/* HTML Functions
*/
/**
* Creates and returns a new HTML <canvas> element with no image smoothing.
*
* @param width How wide the canvas should be.
* @param height How tall the canvas should be.
* @returns A canvas of the given width and height height.
*/
createCanvas(width: number, height: number): HTMLCanvasElement {
var canvas: HTMLCanvasElement = document.createElement("canvas"),
// context: CanvasRenderingContext2D = canvas.getContext("2d");
context: any = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
// For speed's sake, disable image smoothing in the first supported browsers
if (typeof context.imageSmoothingEnabled !== "undefined") {
context.imageSmoothingEnabled = false;
} else if (typeof context.webkitImageSmoothingEnabled !== "undefined") {
context.webkitImageSmoothingEnabled = false;
} else if (typeof context.mozImageSmoothingEnabled !== "undefined") {
context.mozImageSmoothingEnabled = false;
} else if (typeof context.msImageSmoothingEnabled !== "undefined") {
context.msImageSmoothingEnabled = false;
} else if (typeof context.oImageSmoothingEnabled !== "undefined") {
context.oImageSmoothingEnabled = false;
}
return canvas;
}
/* Physics functions
*/
/**
* Shifts a Thing vertically by changing its top and bottom attributes.
*
* @param thing The Thing to shift vertically.
* @param dy How far to shift the Thing.
*/
shiftVert(thing: IThing, dy: number): void {
thing.top += dy;
thing.bottom += dy;
}
/**
* Shifts a Thing horizontally by changing its top and bottom attributes.
*
* @param thing The Thing to shift horizontally.
* @param dy How far to shift the Thing.
*/
shiftHoriz(thing: IThing, dx: number): void {
thing.left += dx;
thing.right += dx;
}
/**
* Sets the top of a Thing to a set number, changing the bottom based on its
* height and the EightBittr's unisize.
*
* @param thing The Thing to shift vertically.
* @param top Where the Thing's top should be.
*/
setTop(thing: IThing, top: number): void {
thing.top = top;
thing.bottom = thing.top + thing.height * thing.EightBitter.unitsize;
}
/**
* Sets the right of a Thing to a set number, changing the left based on its
* width and the EightBittr's unisize.
*
* @param thing The Thing to shift horizontally.
* @param top Where the Thing's right should be.
*/
setRight(thing: IThing, right: number): void {
thing.right = right;
thing.left = thing.right - thing.width * thing.EightBitter.unitsize;
}
/**
* Sets the bottom of a Thing to a set number, changing the top based on its
* height and the EightBittr's unisize.
*
* @param thing The Thing to shift vertically.
* @param top Where the Thing's bottom should be.
*/
setBottom(thing: IThing, bottom: number): void {
thing.bottom = bottom;
thing.top = thing.bottom - thing.height * thing.EightBitter.unitsize;
}
/**
* Sets the left of a Thing to a set number, changing the right based on its
* width and the EightBittr's unisize.
*
* @param thing The Thing to shift horizontally.
* @param top Where the Thing's left should be.
*/
setLeft(thing: IThing, left: number): void {
thing.left = left;
thing.right = thing.left + thing.width * thing.EightBitter.unitsize;
}
/**
* Shifts a Thing so that it is horizontally centered on the given x.
*
* @param thing The Thing to shift horizontally.
* @param x Where the Thing's horizontal midpoint should be.
*/
setMidX(thing: IThing, x: number): void {
thing.EightBitter.setLeft(
thing,
x - thing.width * thing.EightBitter.unitsize / 2);
}
/**
* Shifts a Thing so that it is vertically centered on the given y.
*
* @param thing The Thing to shift vertically.
* @param y Where the Thing's vertical midpoint should be.
*/
setMidY(thing: IThing, y: number): void {
thing.EightBitter.setTop(
thing,
y - thing.height * thing.EightBitter.unitsize / 2);
}
/**
* Shifts a Thing so that it is centered on the given x and y.
*
* @param thing The Thing to shift vertically and horizontally.
* @param x Where the Thing's horizontal midpoint should be.
* @param y Where the Thing's vertical midpoint should be.
*/
setMid(thing: IThing, x: number, y: number): void {
thing.EightBitter.setMidX(thing, x);
thing.EightBitter.setMidY(thing, y);
}
/**
* @param thing
* @returns The horizontal midpoint of the Thing.
*/
getMidX(thing: IThing): number {
return thing.left + thing.width * thing.EightBitter.unitsize / 2;
}
/**
* @param thing
* @returns The vertical midpoint of the Thing.
*/
getMidY(thing: IThing): number {
return thing.top + thing.height * thing.EightBitter.unitsize / 2;
}
/**
* Shifts a Thing so that its midpoint is centered on the midpoint of the
* other Thing.
*
* @param thing The Thing to be shifted.
* @param other The Thing whose midpoint is referenced.
*/
setMidObj(thing: IThing, other: IThing): void {
thing.EightBitter.setMidXObj(thing, other);
thing.EightBitter.setMidYObj(thing, other);
}
/**
* Shifts a Thing so that its horizontal midpoint is centered on the
* midpoint of the other Thing.
*
* @param thing The Thing to be shifted horizontally.
* @param other The Thing whose horizontal midpoint is referenced.
*/
setMidXObj(thing: IThing, other: IThing): void {
thing.EightBitter.setLeft(
thing,
thing.EightBitter.getMidX(other)
- (thing.width * thing.EightBitter.unitsize / 2)
);
}
/**
* Shifts a Thing so that its vertical midpoint is centered on the
* midpoint of the other Thing.
*
* @param thing The Thing to be shifted vertically.
* @param other The Thing whose vertical midpoint is referenced.
*/
setMidYObj(thing: IThing, other: IThing): void {
thing.EightBitter.setTop(
thing,
thing.EightBitter.getMidY(other)
- (thing.height * thing.EightBitter.unitsize / 2)
);
}
/**
* @param thing
* @param other
* @returns Whether the first Thing's midpoint is to the left of the other's.
*/
objectToLeft(thing: IThing, other: IThing): boolean {
return (
thing.EightBitter.getMidX(thing) < thing.EightBitter.getMidX(other)
);
}
/**
* Shifts a Thing's top up, then sets the bottom (similar to a shiftVert and
* a setTop combined).
*
* @param thing The Thing to be shifted vertically.
* @param dy How far to shift the Thing vertically.
*/
updateTop(thing: IThing, dy: number): void {
// If a dy is provided, move the thing's top that much
thing.top += dy || 0;
// Make the thing's bottom dependent on the top
thing.bottom = thing.top + thing.height * thing.EightBitter.unitsize;
}
/**
* Shifts a Thing's right, then sets the left (similar to a shiftHoriz and a
* setRight combined).
*
* @param thing The Thing to be shifted horizontally.
* @param dx How far to shift the Thing horizontally.
*/
updateRight(thing: IThing, dx: number): void {
// If a dx is provided, move the thing's right that much
thing.right += dx || 0;
// Make the thing's left dependent on the right
thing.left = thing.right - thing.width * thing.EightBitter.unitsize;
}
/**
* Shifts a Thing's bottom down, then sets the bottom (similar to a
* shiftVert and a setBottom combined).
*
* @param thing The Thing to be shifted vertically.
* @param dy How far to shift the Thing vertically.
*/
updateBottom(thing: IThing, dy: number): void {
// If a dy is provided, move the thing's bottom that much
thing.bottom += dy || 0;
// Make the thing's top dependent on the top
thing.top = thing.bottom - thing.height * thing.EightBitter.unitsize;
}
/**
* Shifts a Thing's left, then sets the right (similar to a shiftHoriz and a
* setLeft combined).
*
* @param thing The Thing to be shifted horizontally.
* @param dx How far to shift the Thing horizontally.
*/
updateLeft(thing: IThing, dx: number): void {
// If a dx is provided, move the thing's left that much
thing.left += dx || 0;
// Make the thing's right dependent on the left
thing.right = thing.left + thing.width * thing.EightBitter.unitsize;
}
/**
* Shifts a Thing toward a target x, but limits the total distance allowed.
* Distance is computed as from the Thing's horizontal midpoint.
*
* @param thing The Thing to be shifted horizontally.
* @param x How far to shift the Thing horizontally.
* @param maxDistance The maximum distance the Thing can be shifted.
*/
slideToX(thing: IThing, x: number, maxDistance: number): void {
var midx: number = thing.EightBitter.getMidX(thing);
// If no maxSpeed is provided, assume Infinity (so it doesn't matter)
maxDistance = maxDistance || Infinity;
// Thing to the left? Slide to the right.
if (midx < x) {
thing.EightBitter.shiftHoriz(thing, Math.min(maxDistance, x - midx));
} else {
// Thing to the right? Slide to the left.
thing.EightBitter.shiftHoriz(thing, Math.max(-maxDistance, x - midx));
}
}
/**
* Shifts a Thing toward a target y, but limits the total distance allowed.
* Distance is computed as from the Thing's vertical midpoint.
*
* @param thing The Thing to be shifted vertically.
* @param x How far to shift the Thing vertically.
* @param maxDistance The maximum distance the Thing can be shifted.
*/
slideToY(thing: IThing, y: number, maxDistance: number): void {
var midy: number = thing.EightBitter.getMidY(thing);
// If no maxSpeed is provided, assume Infinity (so it doesn't matter)
maxDistance = maxDistance || Infinity;
// Thing above? slide down.
if (midy < y) {
thing.EightBitter.shiftVert(thing, Math.min(maxDistance, y - midy));
} else {
// Thing below? Slide up.
thing.EightBitter.shiftVert(thing, Math.max(-maxDistance, y - midy));
}
}
/* EightBittr utilities
*/
/**
* Ensures the current object is an EightBittr by throwing an error if it
* is not. This should be used for functions in any EightBittr descendants
* that have to call 'this' to ensure their caller is what the programmer
* expected it to be.
*
* @param current The scope that should be an EightBittr.
*/
ensureCorrectCaller(current: any): EightBittr {
if (!(current instanceof EightBittr)) {
throw new Error("A function requires the caller ('this') to be the "
+ "manipulated EightBittr object. Unfortunately, 'this' is a "
+ typeof (this) + ".");
}
return current;
}
/* General utilities
*/
/**
* "Proliferates" all properties of a donor onto a recipient by copying each
* of them and recursing onto child Objects. This is a deep copy.
*
* @param recipient An object to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferate(recipient: any, donor: any, noOverride: boolean = false): any {
var setting: any,
i: string;
// For each attribute of the donor:
for (i in donor) {
if (donor.hasOwnProperty(i)) {
// If noOverride, don't override already existing properties
if (noOverride && recipient.hasOwnProperty(i)) {
continue;
}
// If it's an object, recurse on a new version of it
setting = donor[i];
if (typeof setting === "object") {
if (!recipient.hasOwnProperty(i)) {
recipient[i] = new setting.constructor();
}
this.proliferate(recipient[i], setting, noOverride);
} else {
// Regular primitives are easy to copy otherwise
recipient[i] = setting;
}
}
}
return recipient;
}
/**
* Identical to proliferate, but instead of checking whether the recipient
* hasOwnProperty on properties, it just checks if they're truthy.
*
* @param recipient An object to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferateHard(recipient: any, donor: any, noOverride?: boolean): any {
var setting: any,
i: string;
// For each attribute of the donor:
for (i in donor) {
if (donor.hasOwnProperty(i)) {
// If noOverride, don't override already existing properties
if (noOverride && recipient[i]) {
continue;
}
// If it's an object, recurse on a new version of it
setting = donor[i];
if (typeof setting === "object") {
if (!recipient[i]) {
recipient[i] = new setting.constructor();
}
this.proliferate(recipient[i], setting, noOverride);
} else {
// Regular primitives are easy to copy otherwise
recipient[i] = setting;
}
}
}
return recipient;
}
/**
* Identical to proliferate, but tailored for HTML elements because many
* element attributes don't play nicely with JavaScript Array standards.
* Looking at you, HTMLCollection!
*
* @param recipient An HTMLElement to receive properties from the donor.
* @param donor An object do donoate properties to the recipient.
* @param noOverride Whether pre-existing properties of the recipient should
* be skipped (defaults to false).
* @returns recipient
*/
proliferateElement(recipient: HTMLElement, donor: any, noOverride: boolean = false): HTMLElement {
var setting: any,
i: string,
j: number;
// For each attribute of the donor:
for (i in donor) {
if (donor.hasOwnProperty(i)) {
// If noOverride, don't override already existing properties
if (noOverride && recipient.hasOwnProperty(i)) {
continue;
}
setting = donor[i];
// Special cases for HTML elements
switch (i) {
// Children and options: just append all of them directly
case "children":
case "children":
if (typeof (setting) !== "undefined") {
for (j = 0; j < setting.length; j += 1) {
recipient.appendChild(setting[j]);
}
}
break;
// Style: proliferate (instead of making a new Object)
case "style":
this.proliferate(recipient[i], setting);
break;
// By default, use the normal proliferate logic
default:
// If it's null, don't do anything (like .textContent)
if (setting === null) {
recipient[i] = null;
} else if (typeof setting === "object") {
// If it's an object, recurse on a new version of it
if (!recipient.hasOwnProperty(i)) {
recipient[i] = new setting.constructor();
}
this.proliferate(recipient[i], setting, noOverride);
} else {
// Regular primitives are easy to copy otherwise
recipient[i] = setting;
}
break;
}
}
}
return recipient;
}
/**
* Creates and returns an HTMLElement of the specified type. Any additional
* settings Objects may be given to be proliferated onto the Element via
* proliferateElement.
*
* @param type The tag of the Element to be created.
* @param settings Additional settings to proliferated onto the Element.
* @returns {HTMLElement}
*/
createElement(tag?: string, ...args: any[]): HTMLElement {
var EightBitter: EightBittr = EightBittr.prototype.ensureCorrectCaller(this),
element: HTMLElement = document.createElement(tag || "div"),
i: number;
// For each provided object, add those settings to the element
for (i = 0; i < args.length; i += 1) {
EightBitter.proliferateElement(element, args[i]);
}
return element;
}
/**
* Follows a path inside an Object recursively, based on a given path.
*
* @param object A container to follow a path inside.
* @param path The ordered names of attributes to descend into.
* @param num The starting index in path (by default, 0).
* @returns The discovered property within object, or undefined if the
* full path doesn't exist.
*/
followPathHard(object: any, path: string[], index: number = 0): any {
for (var i: number = index; i < path.length; i += 1) {
if (typeof object[path[i]] === "undefined") {
return undefined;
} else {
object = object[path[i]];
}
}
return object;
}
/**
* Switches an object from one Array to another using splice and push.
*
* @param object The object to move between Arrays.
* @param arrayOld The Array to take the object out of.
* @param arrayNew The Array to move the object into.
*/
arraySwitch(object: any, arrayOld: any[], arrayNew: any[]): void {
arrayOld.splice(arrayOld.indexOf(object), 1);
arrayNew.push(object);
}
/**
* Sets a Thing's position within an Array to the front by splicing and then
* unshifting it.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
*/
arrayToBeginning(object: any, array: any[]): void {
array.splice(array.indexOf(object), 1);
array.unshift(object);
}
/**
* Sets a Thing's position within an Array to the front by splicing and then
* pushing it.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
*/
arrayToEnd(object: IThing, array: any[]): void {
array.splice(array.indexOf(object), 1);
array.push(object);
}
/**
* Sets a Thing's position within an Array to a specific index by splicing
* it out, then back in.
*
* @param object The object to move within the Array.
* @param array An Array currently containing the object.
* @param index Where the object should be moved to in the Array.
*/
arrayToIndex(object: IThing, array: any[], index: number): void {
array.splice(array.indexOf(object), 1);
array.splice(index, 0, object);
}
};
}