mirror of
https://github.com/FullScreenShenanigans/MenuGraphr.git
synced 2026-08-12 02:18:35 -07:00
Initial TypeScript commit
Doesn't quite compile yet, though it's close. TSLint hasn't happened yet.
This commit is contained in:
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
declare module MenuGraphr {
|
||||
export interface IMenuGraphrSettings {
|
||||
GameStarter: GameStartr.IGameStartr;
|
||||
schemas?: any;
|
||||
aliases?: any;
|
||||
replacements?: any;
|
||||
replacerKey?: string;
|
||||
replaceFromItemsHolder?: any;
|
||||
replacementStatistics?: any;
|
||||
}
|
||||
|
||||
export interface IMenuGraphr {
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,241 @@
|
||||
declare module ChangeLinr {
|
||||
export interface IChangeLinrTransform {
|
||||
(data: any, key: string, attributes: any, scope: IChangeLinr): any;
|
||||
}
|
||||
|
||||
export interface IChangeLinrCache {
|
||||
[i: string]: any;
|
||||
}
|
||||
|
||||
export interface IChangeLinrCacheFull {
|
||||
[i: string]: {
|
||||
[i: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IChangeLinrSettings {
|
||||
pipeline: string[];
|
||||
transforms: {
|
||||
[i: string]: IChangeLinrTransform
|
||||
};
|
||||
doMakeCache?: boolean;
|
||||
doUseCache?: boolean;
|
||||
}
|
||||
|
||||
export interface IChangeLinr {
|
||||
getCache(): IChangeLinrCache;
|
||||
getCached(key: string): any;
|
||||
getCacheFull(): IChangeLinrCacheFull;
|
||||
getDoMakeCache(): boolean;
|
||||
getDoUseCache(): boolean;
|
||||
process(data: any, key?: string, attributes?: any): any;
|
||||
processFull(data: any, key?: string, attributes?: any): any;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module ChangeLinr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A general utility for transforming raw input to processed output. This is
|
||||
* done by keeping an Array of transform Functions to process input on.
|
||||
* Outcomes for inputs are cached so repeat runs are O(1).
|
||||
*/
|
||||
export class ChangeLinr implements IChangeLinr {
|
||||
/**
|
||||
* Functions that may be used to transform data, keyed by name.
|
||||
*/
|
||||
private transforms: {
|
||||
[i: string]: IChangeLinrTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ordered listing of Function names to be applied to raw input.
|
||||
*/
|
||||
private pipeline: string[];
|
||||
|
||||
/**
|
||||
* Cached output of previous results of the the pipeline.
|
||||
*/
|
||||
private cache: IChangeLinrCache;
|
||||
|
||||
/**
|
||||
* Cached output of each step of the pipeline.
|
||||
*/
|
||||
private cacheFull: IChangeLinrCacheFull;
|
||||
|
||||
/**
|
||||
* Whether this should be caching responses.
|
||||
*/
|
||||
private doMakeCache: boolean;
|
||||
|
||||
/**
|
||||
* Whether this should be retrieving and using cached results.
|
||||
*/
|
||||
private doUseCache: boolean;
|
||||
|
||||
/**
|
||||
* @param {IChangeLinrSettings} settings
|
||||
*/
|
||||
constructor(settings: IChangeLinrSettings) {
|
||||
var i: number;
|
||||
|
||||
if (typeof settings.pipeline === "undefined") {
|
||||
throw new Error("No pipeline given to ChangeLinr.");
|
||||
}
|
||||
this.pipeline = settings.pipeline || [];
|
||||
|
||||
if (typeof settings.transforms === "undefined") {
|
||||
throw new Error("No transforms given to ChangeLinr.");
|
||||
}
|
||||
this.transforms = settings.transforms || {};
|
||||
|
||||
this.doMakeCache = typeof settings.doMakeCache === "undefined"
|
||||
? true : settings.doMakeCache;
|
||||
|
||||
this.doUseCache = typeof settings.doUseCache === "undefined"
|
||||
? true : settings.doUseCache;
|
||||
|
||||
this.cache = {};
|
||||
this.cacheFull = {};
|
||||
|
||||
// Ensure the pipeline is formatted correctly
|
||||
for (i = 0; i < this.pipeline.length; ++i) {
|
||||
// Don't allow null/false transforms
|
||||
if (!this.pipeline[i]) {
|
||||
throw new Error("Pipe[" + i + "] is invalid.");
|
||||
}
|
||||
|
||||
// Make sure each part of the pipeline exists
|
||||
if (!this.transforms.hasOwnProperty(this.pipeline[i])) {
|
||||
if (!this.transforms.hasOwnProperty(this.pipeline[i])) {
|
||||
throw new Error(
|
||||
"Pipe[" + i + "] (\"" + this.pipeline[i] + "\") "
|
||||
+ "not found in transforms."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Also make sure each part of the pipeline is a Function
|
||||
if (!(this.transforms[this.pipeline[i]] instanceof Function)) {
|
||||
throw new Error(
|
||||
"Pipe[" + i + "] (\"" + this.pipeline[i] + "\") "
|
||||
+ "is not a valid Function from transforms."
|
||||
);
|
||||
}
|
||||
|
||||
this.cacheFull[i] = this.cacheFull[this.pipeline[i]] = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Simple gets
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {Mixed} The cached output of this.process and this.processFull.
|
||||
*/
|
||||
getCache(): IChangeLinrCache {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} key The key under which the output was processed
|
||||
* @return {Mixed} The cached output filed under the given key.
|
||||
*/
|
||||
getCached(key: string): any {
|
||||
return this.cache[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} A complete listing of the cached outputs from all
|
||||
* processed information, from each pipeline transform.
|
||||
*/
|
||||
getCacheFull(): IChangeLinrCacheFull {
|
||||
return this.cacheFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Boolean} Whether the cache object is being kept.
|
||||
*/
|
||||
getDoMakeCache(): boolean {
|
||||
return this.doMakeCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Boolean} Whether previously cached output is being used in new
|
||||
* process requests.
|
||||
*/
|
||||
getDoUseCache(): boolean {
|
||||
return this.doUseCache;
|
||||
}
|
||||
|
||||
|
||||
/* Core processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Applies a series of transforms to input data. If doMakeCache is on, the
|
||||
* outputs of this are stored in cache and cacheFull.
|
||||
*
|
||||
* @param {Mixed} data The data to be transformed.
|
||||
* @param {String} [key] They key under which the data is to be stored.
|
||||
* If needed but not provided, defaults to data.
|
||||
* @param {Object} [attributes] Any extra attributes to be given to the
|
||||
* transform Functions.
|
||||
* @return {Mixed} The final output of the pipeline.
|
||||
*/
|
||||
process(data: any, key: string = undefined, attributes: any = undefined): any {
|
||||
var i: number;
|
||||
|
||||
if (typeof key === "undefined" && (this.doMakeCache || this.doUseCache)) {
|
||||
key = data;
|
||||
}
|
||||
|
||||
// If this keyed input was already processed, get that
|
||||
if (this.doUseCache && this.cache.hasOwnProperty(key)) {
|
||||
return this.cache[key];
|
||||
}
|
||||
|
||||
// Apply (and optionally cache) each transform in order
|
||||
for (i = 0; i < this.pipeline.length; ++i) {
|
||||
data = this.transforms[this.pipeline[i]](data, key, attributes, this);
|
||||
|
||||
if (this.doMakeCache) {
|
||||
this.cacheFull[this.pipeline[i]][key] = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.doMakeCache) {
|
||||
this.cache[key] = data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of this.process that returns the complete output from each
|
||||
* pipelined transform Function in an Object.
|
||||
*
|
||||
* @param {Mixed} data The data to be transformed.
|
||||
* @param {String} [key] They key under which the data is to be stored.
|
||||
* If needed but not provided, defaults to data.
|
||||
* @param {Object} [attributes] Any extra attributes to be given to the
|
||||
* transform Functions.
|
||||
* @return {Object} The complete output of the transforms.
|
||||
*/
|
||||
processFull(raw: any, key: string, attributes: any = undefined): any {
|
||||
var output: any = {},
|
||||
i: number;
|
||||
|
||||
this.process(raw, key, attributes);
|
||||
|
||||
for (i = 0; i < this.pipeline.length; ++i) {
|
||||
output[i] = output[this.pipeline[i]] = this.cacheFull[this.pipeline[i]][key];
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
declare module FPSAnalyzr {
|
||||
export interface IFPSAnalyzrSettings {
|
||||
maxKept?: number;
|
||||
getTimestamp?: any;
|
||||
}
|
||||
|
||||
export interface IFPSAnalyzr {
|
||||
getTimestamp: () => number;
|
||||
measure(time?: number): void;
|
||||
addFPS(fps: number): void;
|
||||
getMaxKept(): number;
|
||||
getNumRecorded(): number;
|
||||
getTimeCurrent(): number;
|
||||
getTicker(): number;
|
||||
getMeasurements(): any;
|
||||
getDifferences(): any;
|
||||
getAverage(): number;
|
||||
getMedian(): number;
|
||||
getExtremes(): number[];
|
||||
getRange(): number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module FPSAnalyzr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A general utility for obtaining and analyzing framerate measurements. The
|
||||
* most recent measurements are kept up to a certain point (either an infinite
|
||||
* number or a set amount). Options for analyzing the data such as getting the
|
||||
* mean, median, extremes, etc. are available.
|
||||
*/
|
||||
export class FPSAnalyzr implements IFPSAnalyzr {
|
||||
/**
|
||||
* Function to generate a current timestamp, commonly performance.now.
|
||||
*/
|
||||
public getTimestamp: () => number;
|
||||
|
||||
/**
|
||||
* How many FPS measurements to keep at any given time, at most.
|
||||
*/
|
||||
private maxKept: number;
|
||||
|
||||
/**
|
||||
* A recent history of FPS measurements (normally an Array). These are
|
||||
* stored as changes in millisecond timestamps.
|
||||
*/
|
||||
private measurements: Array<number> | { [i: number]: number };
|
||||
|
||||
/**
|
||||
* The actual number of FPS measurements currently known.
|
||||
*/
|
||||
private numRecorded: number;
|
||||
|
||||
/**
|
||||
* The current position in the internal measurements listing.
|
||||
*/
|
||||
private ticker: number;
|
||||
|
||||
/**
|
||||
* The most recent timestamp from getTimestamp.
|
||||
*/
|
||||
private timeCurrent: number;
|
||||
|
||||
/**
|
||||
* @param {IFPSAnalyzrSettings} [settings]
|
||||
*/
|
||||
constructor(settings: IFPSAnalyzrSettings = {}) {
|
||||
this.maxKept = settings.maxKept || 35;
|
||||
this.numRecorded = 0;
|
||||
this.ticker = -1;
|
||||
|
||||
// If maxKept is a Number, make the measurements array that long
|
||||
// If it's infinite, make measurements an {} (infinite array)
|
||||
this.measurements = isFinite(this.maxKept) ? new Array(this.maxKept) : {};
|
||||
|
||||
// Headless browsers like PhantomJS won't know performance, so Date.now
|
||||
// is used as a backup
|
||||
if (typeof settings.getTimestamp === "undefined") {
|
||||
if (typeof performance === "undefined") {
|
||||
this.getTimestamp = function (): number {
|
||||
return Date.now();
|
||||
};
|
||||
} else {
|
||||
this.getTimestamp = (
|
||||
performance.now
|
||||
|| (<any>performance).webkitNow
|
||||
|| (<any>performance).mozNow
|
||||
|| (<any>performance).msNow
|
||||
|| (<any>performance).oNow
|
||||
).bind(performance);
|
||||
}
|
||||
} else {
|
||||
this.getTimestamp = settings.getTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Public interface
|
||||
*/
|
||||
|
||||
/**
|
||||
* Standard public measurement function.
|
||||
* Marks the current timestamp as timeCurrent, and adds an FPS measurement
|
||||
* if there was a previous timeCurrent.
|
||||
*
|
||||
* @param {DOMHighResTimeStamp} time An optional timestamp, without which
|
||||
* getTimestamp() is used instead.
|
||||
*/
|
||||
measure(time: number = this.getTimestamp()): void {
|
||||
if (this.timeCurrent) {
|
||||
this.addFPS(1000 / (time - this.timeCurrent));
|
||||
}
|
||||
|
||||
this.timeCurrent = time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an FPS measurement to measurements, and increments the associated
|
||||
* count variables.
|
||||
*
|
||||
* @param {Number} fps An FPS calculated as the difference between two
|
||||
* timestamps.
|
||||
*/
|
||||
addFPS(fps: number): void {
|
||||
this.ticker = (this.ticker += 1) % this.maxKept;
|
||||
this.measurements[this.ticker] = fps;
|
||||
this.numRecorded += 1;
|
||||
}
|
||||
|
||||
|
||||
/* Gets
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {Number} The number of FPS measurements to keep.
|
||||
*/
|
||||
getMaxKept(): number {
|
||||
return this.maxKept;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The actual number of FPS measurements currently known.
|
||||
*/
|
||||
getNumRecorded(): number {
|
||||
return this.numRecorded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The most recent performance.now timestamp.
|
||||
*/
|
||||
getTimeCurrent(): number {
|
||||
return this.timeCurrent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The current position in measurements.
|
||||
*/
|
||||
getTicker(): number {
|
||||
return this.ticker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get function for a copy of the measurements listing (if the number of
|
||||
* measurements is less than the max, that size is used)
|
||||
*
|
||||
* @return {Object} An object (normally an Array) of the most recent FPS
|
||||
* measurements
|
||||
*/
|
||||
getMeasurements(): Array<number> | { [i: number]: number } {
|
||||
var fpsKeptReal: number = Math.min(this.maxKept, this.numRecorded),
|
||||
copy: any,
|
||||
i: number;
|
||||
|
||||
if (isFinite(this.maxKept)) {
|
||||
copy = new Array(fpsKeptReal);
|
||||
} else {
|
||||
copy = {};
|
||||
copy.length = fpsKeptReal;
|
||||
}
|
||||
|
||||
for (i = fpsKeptReal - 1; i >= 0; --i) {
|
||||
copy[i] = this.measurements[i];
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get function for a copy of the measurements listing, but with the FPS
|
||||
* measurements transformed back into time differences
|
||||
*
|
||||
* @return {Object} An object (normally an Array) of the most recent FPS
|
||||
* time differences
|
||||
*/
|
||||
getDifferences(): any {
|
||||
var copy: any = this.getMeasurements(),
|
||||
i: number;
|
||||
|
||||
for (i = copy.length - 1; i >= 0; --i) {
|
||||
copy[i] = 1000 / copy[i];
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The average recorded FPS measurement.
|
||||
*/
|
||||
getAverage(): number {
|
||||
var total: number = 0,
|
||||
max: number = Math.min(this.maxKept, this.numRecorded),
|
||||
i: number;
|
||||
|
||||
for (i = max - 1; i >= 0; --i) {
|
||||
total += this.measurements[i];
|
||||
}
|
||||
|
||||
return total / max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The median recorded FPS measurement.
|
||||
* @remarks This is O(n*log(n)), where n is the size of the history,
|
||||
* as it creates a copy of the history and sorts it.
|
||||
*/
|
||||
getMedian(): number {
|
||||
var copy: any = this.getMeasurementsSorted(),
|
||||
fpsKeptReal: number = copy.length,
|
||||
fpsKeptHalf: number = Math.floor(fpsKeptReal / 2);
|
||||
|
||||
if (copy.length % 2 === 0) {
|
||||
return copy[fpsKeptHalf];
|
||||
} else {
|
||||
return (copy[fpsKeptHalf - 2] + copy[fpsKeptHalf]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number[]} An Array containing the lowest and highest recorded
|
||||
* FPS measurements, in that order.
|
||||
*/
|
||||
getExtremes(): number[] {
|
||||
var lowest: number = this.measurements[0],
|
||||
highest: number = lowest,
|
||||
max: number = Math.min(this.maxKept, this.numRecorded),
|
||||
fps: number,
|
||||
i: number;
|
||||
|
||||
for (i = max - 1; i >= 0; --i) {
|
||||
fps = this.measurements[i];
|
||||
if (fps > highest) {
|
||||
highest = fps;
|
||||
} else if (fps < lowest) {
|
||||
lowest = fps;
|
||||
}
|
||||
}
|
||||
|
||||
return [lowest, highest];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The range of recorded FPS measurements
|
||||
*/
|
||||
getRange(): number {
|
||||
var extremes: number[] = this.getExtremes();
|
||||
return extremes[1] - extremes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private getMeasurementsSorted(): number[] {
|
||||
var copy: number[],
|
||||
i: string;
|
||||
|
||||
if (this.measurements.constructor === Array) {
|
||||
copy = (<number[]>this.measurements).sort();
|
||||
} else {
|
||||
copy = [];
|
||||
|
||||
for (i in this.measurements) {
|
||||
if (this.measurements.hasOwnProperty(i)) {
|
||||
if (this.measurements[i] === undefined) {
|
||||
break;
|
||||
}
|
||||
copy[i] = this.measurements[i];
|
||||
}
|
||||
}
|
||||
|
||||
copy.sort();
|
||||
}
|
||||
|
||||
if (this.numRecorded < this.maxKept) {
|
||||
copy.length = this.numRecorded;
|
||||
}
|
||||
|
||||
return copy.sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,431 @@
|
||||
/// <reference path="FPSAnalyzr-0.2.1.ts" />
|
||||
|
||||
declare module GamesRunnr {
|
||||
export interface IGamesRunnrSettings {
|
||||
// The Array of Functions to run on each upkeep.
|
||||
games: any[];
|
||||
|
||||
// How often, in milliseconds, to call upkeep when playing (defaults to
|
||||
// 1000 / 60).
|
||||
interval?: number;
|
||||
|
||||
// A multiplier for interval that can be set independently.
|
||||
speed?: number;
|
||||
|
||||
// Whether scheduling timeouts should adjust to elapsed upkeep time.
|
||||
adjustFramerate?: boolean;
|
||||
|
||||
// A callback to run when upkeep is paused.
|
||||
onPause?: (...args: any[]) => void;
|
||||
|
||||
// A callback to run when upkeep is played.
|
||||
onPlay?: (...args: any[]) => void;
|
||||
|
||||
// Arguments to be passed to onPause and onPlay (by default, [this])
|
||||
callbackArguments?: any[];
|
||||
|
||||
// A Function to replace setTimeout.
|
||||
/**
|
||||
* A Function to replace setTimeout as the upkeepScheduler.
|
||||
*/
|
||||
upkeepScheduler?: (callback: Function, timeout: number) => number;
|
||||
|
||||
/**
|
||||
* A Function to replace clearTimeout as the upkeepCanceller.
|
||||
*/
|
||||
upkeepCanceller?: (handle: number) => void;
|
||||
|
||||
/**
|
||||
* A scope for games to be run on (defaults to the calling GamesRunnr).
|
||||
*/
|
||||
scope?: any;
|
||||
|
||||
/**
|
||||
* An FPSAnalyzr to provide statistics on automated playback. If not
|
||||
* provided, a new one will be made.
|
||||
*/
|
||||
FPSAnalyzer?: FPSAnalyzr.IFPSAnalyzr;
|
||||
|
||||
/**
|
||||
* Settings to create a new FPSAnalyzr, if one isn't provided.
|
||||
*/
|
||||
FPSAnalyzerSettings?: FPSAnalyzr.IFPSAnalyzrSettings;
|
||||
}
|
||||
|
||||
export interface IGamesRunnr {
|
||||
getFPSAnalyzer(): FPSAnalyzr.IFPSAnalyzr;
|
||||
getPaused(): boolean;
|
||||
getGames(): any[];
|
||||
getInterval(): number;
|
||||
getSpeed(): number;
|
||||
getOnPause(): any;
|
||||
getOnPlay(): any;
|
||||
getCallbackArguments(): any[];
|
||||
getUpkeepScheduler(): (callback: Function, timeout: number) => number;
|
||||
getUpkeepCanceller(): (handle: number) => void;
|
||||
upkeep(): void;
|
||||
upkeepTimed(): number;
|
||||
play(): void;
|
||||
pause(): void;
|
||||
step(times?: number): void;
|
||||
togglePause(): void;
|
||||
setInterval(interval: number): void;
|
||||
setSpeed(speed: number): void;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module GamesRunnr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A class to continuously series of "game" Functions. Each game is run in a
|
||||
* set order and the group is run as a whole at a particular interval, with a
|
||||
* configurable speed. Playback can be triggered manually, or driven by a timer
|
||||
* with pause and play hooks. For automated playback, statistics are
|
||||
* available via an internal FPSAnalyzer.
|
||||
*/
|
||||
export class GamesRunnr implements IGamesRunnr {
|
||||
/**
|
||||
* Functions to be run, in order, on each upkeep.
|
||||
*/
|
||||
private games: any[];
|
||||
|
||||
/**
|
||||
* Optional trigger Function for this.pause.
|
||||
*/
|
||||
private onPause: (...args: any[]) => void;
|
||||
|
||||
/**
|
||||
* Optional trigger Function for this.play.
|
||||
*/
|
||||
private onPlay: (...args: any[]) => void;
|
||||
|
||||
/**
|
||||
* Arguments to be passed to the optional trigger Functions.
|
||||
*/
|
||||
private callbackArguments: any[];
|
||||
|
||||
/**
|
||||
* Reference to the next upkeep, such as setTimeout's returned int.
|
||||
*/
|
||||
private upkeepNext: number;
|
||||
|
||||
/**
|
||||
* Function used to schedule the next upkeep, such as setTimeout.
|
||||
*/
|
||||
private upkeepScheduler: (callback: Function, timeout: number) => number;
|
||||
|
||||
/**
|
||||
* Function used to cancel the next upkeep, such as clearTimeout
|
||||
*/
|
||||
private upkeepCanceller: (handle: number) => void;
|
||||
|
||||
/**
|
||||
* this.upkeep bound to this GamesRunnr, for use in upkeepScheduler.
|
||||
*/
|
||||
private upkeepBound: any;
|
||||
|
||||
/**
|
||||
* Whether the game is currently paused.
|
||||
*/
|
||||
private paused: boolean;
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, between each upkeep.
|
||||
*/
|
||||
private interval: number;
|
||||
|
||||
/**
|
||||
* The playback rate multiplier (defaults to 1, for no change).
|
||||
*/
|
||||
private speed: number;
|
||||
|
||||
/**
|
||||
* The actual speed, as (1 / speed) * interval.
|
||||
*/
|
||||
private intervalReal: number;
|
||||
|
||||
/**
|
||||
* An internal FPSAnalyzr object that measures on each upkeep.
|
||||
*/
|
||||
private FPSAnalyzer: FPSAnalyzr.IFPSAnalyzr;
|
||||
|
||||
/**
|
||||
* An object to set as the scope for games, if not this GamesRunnr.
|
||||
*/
|
||||
private scope: any;
|
||||
|
||||
/**
|
||||
* Whether scheduling timeouts should adjust to elapsed upkeep time.
|
||||
*/
|
||||
private adjustFramerate: boolean;
|
||||
|
||||
/**
|
||||
* @param {IGamesRunnrSettings} settings
|
||||
*/
|
||||
constructor(settings: IGamesRunnrSettings) {
|
||||
var i: number;
|
||||
|
||||
if (typeof settings.games === "undefined") {
|
||||
throw new Error("No games given to GamesRunnr.");
|
||||
}
|
||||
|
||||
this.games = settings.games;
|
||||
this.interval = settings.interval || 1000 / 60;
|
||||
this.speed = settings.speed || 1;
|
||||
this.onPause = settings.onPause;
|
||||
this.onPlay = settings.onPlay;
|
||||
this.callbackArguments = settings.callbackArguments || [this];
|
||||
this.adjustFramerate = settings.adjustFramerate;
|
||||
this.FPSAnalyzer = settings.FPSAnalyzer || new FPSAnalyzr.FPSAnalyzr(settings.FPSAnalyzerSettings);
|
||||
|
||||
this.scope = settings.scope || this;
|
||||
this.paused = true;
|
||||
|
||||
this.upkeepScheduler = settings.upkeepScheduler || function (handler: any, timeout: number): number {
|
||||
return setTimeout(handler, timeout);
|
||||
};
|
||||
this.upkeepCanceller = settings.upkeepCanceller || function (handle: number): void {
|
||||
clearTimeout(handle);
|
||||
};
|
||||
|
||||
this.upkeepBound = this.upkeep.bind(this);
|
||||
|
||||
for (i = 0; i < this.games.length; i += 1) {
|
||||
this.games[i] = this.games[i].bind(this.scope);
|
||||
}
|
||||
|
||||
this.setIntervalReal();
|
||||
}
|
||||
|
||||
|
||||
/* Gets
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {FPSAnalyzer} The FPSAnalyzer used in the GamesRunnr.
|
||||
*/
|
||||
getFPSAnalyzer(): FPSAnalyzr.IFPSAnalyzr {
|
||||
return this.FPSAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Boolean} Whether this is paused.
|
||||
*/
|
||||
getPaused(): boolean {
|
||||
return this.paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Function[]} The Array of game Functions.
|
||||
*/
|
||||
getGames(): any[] {
|
||||
return this.games;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The interval between upkeeps.
|
||||
*/
|
||||
getInterval(): number {
|
||||
return this.interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Number} The speed multiplier being applied to the interval.
|
||||
*/
|
||||
getSpeed(): number {
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Function} The optional trigger to be called on pause.
|
||||
*/
|
||||
getOnPause(): any {
|
||||
return this.onPause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Function} The optional trigger to be called on play.
|
||||
*/
|
||||
getOnPlay(): any {
|
||||
return this.onPlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array} Arguments to be given to the optional trigger Functions.
|
||||
*/
|
||||
getCallbackArguments(): any[] {
|
||||
return this.callbackArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Function} Function used to schedule the next upkeep.
|
||||
*/
|
||||
getUpkeepScheduler(): (callback: Function, timeout: number) => number {
|
||||
return this.upkeepScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Function} Function used to cancel the next upkeep.
|
||||
*/
|
||||
getUpkeepCanceller(): (handle: number) => void {
|
||||
return this.upkeepCanceller;
|
||||
}
|
||||
|
||||
|
||||
/* Runtime
|
||||
*/
|
||||
|
||||
/**
|
||||
* Meaty function, run every <interval*speed> milliseconds, to mark an FPS
|
||||
* measurement and run every game once.
|
||||
*/
|
||||
upkeep(): void {
|
||||
if (this.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevents double upkeeping, in case a new upkeepNext was scheduled.
|
||||
this.upkeepCanceller(this.upkeepNext);
|
||||
|
||||
if (this.adjustFramerate) {
|
||||
this.upkeepNext = this.upkeepScheduler(this.upkeepBound, this.intervalReal - (this.upkeepTimed() | 0));
|
||||
} else {
|
||||
this.upkeepNext = this.upkeepScheduler(this.upkeepBound, this.intervalReal);
|
||||
this.games.forEach(this.run);
|
||||
}
|
||||
|
||||
if (this.FPSAnalyzer) {
|
||||
this.FPSAnalyzer.measure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility for this.upkeep that calls the same games.forEach(run), timing
|
||||
* the total execution time.
|
||||
*
|
||||
* @return {Number} The total time spent, in milliseconds.
|
||||
*/
|
||||
upkeepTimed(): number {
|
||||
if (!this.FPSAnalyzer) {
|
||||
throw new Error("An internal FPSAnalyzr is required for upkeepTimed.");
|
||||
}
|
||||
|
||||
var now: number = this.FPSAnalyzer.getTimestamp();
|
||||
this.games.forEach(this.run);
|
||||
return this.FPSAnalyzer.getTimestamp() - now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues execution of this.upkeep by calling it. If an onPlay has been
|
||||
* defined, it's called before.
|
||||
*/
|
||||
play(): void {
|
||||
if (!this.paused) {
|
||||
return;
|
||||
}
|
||||
this.paused = false;
|
||||
|
||||
if (this.onPlay) {
|
||||
this.onPlay.apply(this, this.callbackArguments);
|
||||
}
|
||||
|
||||
this.upkeep();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops execution of this.upkeep, and cancels the next call. If an onPause
|
||||
* has been defined, it's called after.
|
||||
*/
|
||||
pause(): void {
|
||||
if (this.paused) {
|
||||
return;
|
||||
}
|
||||
this.paused = true;
|
||||
|
||||
if (this.onPause) {
|
||||
this.onPause.apply(this, this.callbackArguments);
|
||||
}
|
||||
|
||||
this.upkeepCanceller(this.upkeepNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls upkeep a <num or 1> number of times, immediately.
|
||||
*
|
||||
* @param {Number} [num] How many times to upkeep, if not 1.
|
||||
*/
|
||||
step(times: number = 1): void {
|
||||
this.play();
|
||||
this.pause();
|
||||
if (times > 0) {
|
||||
this.step(times - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles whether this is paused, and calls the appropriate Function.
|
||||
*/
|
||||
togglePause(): void {
|
||||
this.paused ? this.play() : this.pause();
|
||||
}
|
||||
|
||||
|
||||
/* Games manipulations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the interval between between upkeeps.
|
||||
*
|
||||
* @param {Number} The new time interval in milliseconds.
|
||||
*/
|
||||
setInterval(interval: number): void {
|
||||
var intervalReal: number = Number(interval);
|
||||
|
||||
if (isNaN(intervalReal)) {
|
||||
throw new Error("Invalid interval given to setInterval: " + interval);
|
||||
}
|
||||
|
||||
this.interval = intervalReal;
|
||||
this.setIntervalReal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed multiplier for the interval.
|
||||
*
|
||||
* @param {Number} The new speed multiplier. 2 will cause interval to be
|
||||
* twice as fast, and 0.5 will be half as fast.
|
||||
*/
|
||||
setSpeed(speed: number): void {
|
||||
var speedReal: number = Number(speed);
|
||||
|
||||
if (isNaN(speedReal)) {
|
||||
throw new Error("Invalid speed given to setSpeed: " + speed);
|
||||
}
|
||||
|
||||
this.speed = speedReal;
|
||||
this.setIntervalReal();
|
||||
}
|
||||
|
||||
|
||||
/* Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the intervalReal variable, which is interval * (inverse of speed).
|
||||
*/
|
||||
private setIntervalReal(): void {
|
||||
this.intervalReal = (1 / this.speed) * this.interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Curry function to fun a given function. Used in games.forEach(game).
|
||||
*
|
||||
* @param {Function} game
|
||||
*/
|
||||
private run(game: Function): void {
|
||||
game();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,220 @@
|
||||
declare module MapScreenr {
|
||||
export interface IMapScreenrSettings {
|
||||
/**
|
||||
* How wide the MapScreenr should be.
|
||||
*/
|
||||
width: number;
|
||||
|
||||
/**
|
||||
* How tall the MapScreenr should be.
|
||||
*/
|
||||
height: number;
|
||||
|
||||
/**
|
||||
* A mapping of Functions to generate member variables that should be
|
||||
* recomputed on screen change, keyed by variable name.
|
||||
*/
|
||||
variables?: any;
|
||||
|
||||
/**
|
||||
* Arguments to be passed to variable Functions.
|
||||
*/
|
||||
variableArgs?: any[];
|
||||
}
|
||||
|
||||
export interface IMapScreenr {
|
||||
variables: any;
|
||||
variableArgs: any[];
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
middleX: number;
|
||||
middleY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
clearScreen(): void;
|
||||
setMiddleX(): void;
|
||||
setMiddleY(): void;
|
||||
setVariables(): void;
|
||||
shift(dx: number, dy: number): void;
|
||||
shiftX(dx: number): void;
|
||||
shiftY(dy: number): void;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module MapScreenr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A simple container for Map attributes given by switching to an Area within
|
||||
* that map. A bounding box of the current viewport is kept, along with any
|
||||
* other information desired.
|
||||
*/
|
||||
export class MapScreenr implements IMapScreenr {
|
||||
/**
|
||||
* A listing of variable Functions to be calculated on screen resets.
|
||||
*/
|
||||
public variables: { [i: string]: Function };
|
||||
|
||||
/**
|
||||
* Arguments to be passed into variable computation Functions.
|
||||
*/
|
||||
public variableArgs: any[];
|
||||
|
||||
/**
|
||||
* Top of the MapScreenr's bounding box.
|
||||
*/
|
||||
public top: number;
|
||||
|
||||
/**
|
||||
* Right of the MapScreenr's bounding box.
|
||||
*/
|
||||
public right: number;
|
||||
|
||||
/**
|
||||
* Bottom of the MapScreenr's bounding box.
|
||||
*/
|
||||
public bottom: number;
|
||||
|
||||
/**
|
||||
* Left of the MapScreenr's bounding box.
|
||||
*/
|
||||
public left: number;
|
||||
|
||||
/**
|
||||
* Horizontal midpoint of the MapScreenr's bounding box.
|
||||
*/
|
||||
public middleX: number;
|
||||
|
||||
/**
|
||||
* Vertical midpoint of the MapScreenr's bounding box.
|
||||
*/
|
||||
public middleY: number;
|
||||
|
||||
/**
|
||||
* Width of the MapScreenr's bounding box.
|
||||
*/
|
||||
public width: number;
|
||||
|
||||
/**
|
||||
* Height of the MapScreenr's bounding box.
|
||||
*/
|
||||
public height: number;
|
||||
|
||||
/**
|
||||
* Resets the MapScreenr. All members of the settings argument are copied
|
||||
* to the MapScreenr itself, though only width and height are required.
|
||||
*
|
||||
* @param {IMapScreenrSettings} settings
|
||||
*/
|
||||
constructor(settings: IMapScreenrSettings) {
|
||||
var name: string;
|
||||
|
||||
if (typeof settings.width === "undefined") {
|
||||
throw new Error("No width given to MapScreenr.");
|
||||
}
|
||||
if (typeof settings.height === "undefined") {
|
||||
throw new Error("No height given to MapScreenr.");
|
||||
}
|
||||
|
||||
for (name in settings) {
|
||||
if (settings.hasOwnProperty(name)) {
|
||||
(<any>this)[name] = settings[name];
|
||||
}
|
||||
}
|
||||
|
||||
this.variables = settings.variables || {};
|
||||
this.variableArgs = settings.variableArgs || [];
|
||||
}
|
||||
|
||||
|
||||
/* State changes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Completely clears the MapScreenr for use in a new Area. Positioning is
|
||||
* reset to (0,0) and user-configured variables are recalculated.
|
||||
*/
|
||||
clearScreen(): void {
|
||||
this.left = 0;
|
||||
this.top = 0;
|
||||
this.right = this.width;
|
||||
this.bottom = this.height;
|
||||
|
||||
this.setMiddleX();
|
||||
this.setMiddleY();
|
||||
|
||||
this.setVariables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes middleX as the midpoint between left and right.
|
||||
*/
|
||||
setMiddleX(): void {
|
||||
this.middleX = (this.left + this.right) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes middleY as the midpoint between top and bottom.
|
||||
*/
|
||||
setMiddleY(): void {
|
||||
this.middleY = (this.top + this.bottom) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all variable Functions with variableArgs to recalculate their
|
||||
* values.
|
||||
*/
|
||||
setVariables(): void {
|
||||
var i: string;
|
||||
|
||||
for (i in this.variables) {
|
||||
if (this.variables.hasOwnProperty(i)) {
|
||||
this[i] = this.variables[i].apply(this, this.variableArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Element shifting
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shifts the MapScreenr horizontally and vertically via shiftX and shiftY.
|
||||
*
|
||||
* @param {Number} dx
|
||||
* @param {Number} dy
|
||||
*/
|
||||
shift(dx: number, dy: number): void {
|
||||
if (dx) {
|
||||
this.shiftX(dx);
|
||||
}
|
||||
|
||||
if (dy) {
|
||||
this.shiftY(dy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the MapScreenr horizontally by changing left and right by the dx.
|
||||
*
|
||||
* @param {Number} dx
|
||||
*/
|
||||
shiftX(dx: number): void {
|
||||
this.left += dx;
|
||||
this.right += dx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the MapScreenr vertically by changing top and bottom by the dy.
|
||||
*
|
||||
* @param {Number} dy
|
||||
*/
|
||||
shiftY(dy: number): void {
|
||||
this.top += dy;
|
||||
this.bottom += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
/// <reference path="ItemsHoldr-0.2.1.ts" />
|
||||
var ModAttachr;
|
||||
(function (_ModAttachr) {
|
||||
"use strict";
|
||||
/**
|
||||
* An addon for for extensible modding functionality. "Mods" register triggers
|
||||
* such as "onModEnable" or "onReset" that can be triggered.
|
||||
*/
|
||||
var ModAttachr = (function () {
|
||||
/**
|
||||
* @param {IModAttachrSettings} [settings]
|
||||
*/
|
||||
function ModAttachr(settings) {
|
||||
this.mods = {};
|
||||
this.events = {};
|
||||
if (!settings) {
|
||||
return;
|
||||
}
|
||||
this.scopeDefault = settings.scopeDefault;
|
||||
// If a ItemsHoldr is provided, use it
|
||||
if (settings.ItemsHoldr) {
|
||||
this.ItemsHolder = settings.ItemsHoldr;
|
||||
}
|
||||
else if (settings.storeLocally) {
|
||||
// If one isn't provided by storeLocally is still true, make one
|
||||
this.ItemsHolder = new ItemsHoldr.ItemsHoldr();
|
||||
}
|
||||
if (settings.mods) {
|
||||
this.addMods(settings.mods);
|
||||
}
|
||||
}
|
||||
/* Simple gets
|
||||
*/
|
||||
/**
|
||||
* @return {Object} An Object keying each mod by their name.
|
||||
*/
|
||||
ModAttachr.prototype.getMods = function () {
|
||||
return this.mods;
|
||||
};
|
||||
/**
|
||||
* @param {String} name The name of the mod to return.
|
||||
* @return {Object} The mod keyed by the name.
|
||||
*/
|
||||
ModAttachr.prototype.getMod = function (name) {
|
||||
return this.mods[name];
|
||||
};
|
||||
/**
|
||||
* @return {Object} An Object keying each event by their name.
|
||||
*/
|
||||
ModAttachr.prototype.getEvents = function () {
|
||||
return this.events;
|
||||
};
|
||||
/**
|
||||
* @return {Object[]} The mods associated with a particular event.
|
||||
*/
|
||||
ModAttachr.prototype.getEvent = function (name) {
|
||||
return this.events[name];
|
||||
};
|
||||
/**
|
||||
* @return {ItemsHoldr} The ItemsHoldr if storeLocally is true, or undefined
|
||||
* otherwise.
|
||||
*/
|
||||
ModAttachr.prototype.getItemsHolder = function () {
|
||||
return this.ItemsHolder;
|
||||
};
|
||||
/* Alterations
|
||||
*/
|
||||
/**
|
||||
* Adds a mod to the pool of mods, listing it under all the relevant events.
|
||||
* If the event is enabled, the "onModEnable" event for it is triggered.
|
||||
*
|
||||
* @param {Object} mod A summary Object for a mod, containing at the very
|
||||
* least a name and Object of events.
|
||||
*/
|
||||
ModAttachr.prototype.addMod = function (mod) {
|
||||
var modEvents = mod.events, name;
|
||||
for (name in modEvents) {
|
||||
if (!modEvents.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
if (!this.events.hasOwnProperty(name)) {
|
||||
this.events[name] = [mod];
|
||||
}
|
||||
else {
|
||||
this.events[name].push(mod);
|
||||
}
|
||||
}
|
||||
// Mod scope defaults to the ModAttacher's scopeDefault.
|
||||
mod.scope = mod.scope || this.scopeDefault;
|
||||
// Record the mod in the ModAttachr's mods listing.
|
||||
this.mods[mod.name] = mod;
|
||||
// If the mod is enabled, trigger its "onModEnable" event
|
||||
if (mod.enabled && mod.events.hasOwnProperty("onModEnable")) {
|
||||
this.fireModEvent("onModEnable", mod.name, arguments);
|
||||
}
|
||||
// If there's a ItemsHoldr, record the mod in it
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.addItem(mod.name, {
|
||||
"valueDefault": 0,
|
||||
"storeLocally": true
|
||||
});
|
||||
// If there was already a (true) value, immediately enable the mod
|
||||
if (this.ItemsHolder.getItem(mod.name)) {
|
||||
this.enableMod(mod.name);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Adds each mod in a given Array.
|
||||
*
|
||||
* @param {Array} mods
|
||||
*/
|
||||
ModAttachr.prototype.addMods = function (mods) {
|
||||
for (var i = 0; i < mods.length; i += 1) {
|
||||
this.addMod(mods[i]);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Enables a mod of the given name, if it exists. The onModEnable event is
|
||||
* called for the mod.
|
||||
*
|
||||
* @param {String} name The name of the mod to enable.
|
||||
*/
|
||||
ModAttachr.prototype.enableMod = function (name) {
|
||||
var mod = this.mods[name], args;
|
||||
if (!mod) {
|
||||
throw new Error("No mod of name: '" + name + "'");
|
||||
}
|
||||
mod.enabled = true;
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
args[0] = mod;
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.setItem(name, true);
|
||||
}
|
||||
if (mod.events.hasOwnProperty("onModEnable")) {
|
||||
return this.fireModEvent("onModEnable", mod.name, arguments);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Enables any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
ModAttachr.prototype.enableMods = function () {
|
||||
var names = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
names[_i - 0] = arguments[_i];
|
||||
}
|
||||
names.forEach(this.enableMod.bind(this));
|
||||
};
|
||||
/**
|
||||
* Disables a mod of the given name, if it exists. The onModDisable event is
|
||||
* called for the mod.
|
||||
*
|
||||
* @param {String} name The name of the mod to disable.
|
||||
*/
|
||||
ModAttachr.prototype.disableMod = function (name) {
|
||||
var mod = this.mods[name], args;
|
||||
if (!this.mods[name]) {
|
||||
throw new Error("No mod of name: '" + name + "'");
|
||||
}
|
||||
this.mods[name].enabled = false;
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
args[0] = mod;
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.setItem(name, false);
|
||||
}
|
||||
if (mod.events.hasOwnProperty("onModDisable")) {
|
||||
return this.fireModEvent("onModDisable", mod.name, args);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Disables any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
ModAttachr.prototype.disableMods = function () {
|
||||
var names = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
names[_i - 0] = arguments[_i];
|
||||
}
|
||||
names.forEach(this.disableMod.bind(this));
|
||||
};
|
||||
/**
|
||||
* Toggles a mod via enableMod/disableMod of the given name, if it exists.
|
||||
*
|
||||
* @param {String} name The name of the mod to toggle.
|
||||
*/
|
||||
ModAttachr.prototype.toggleMod = function (name) {
|
||||
var mod = this.mods[name];
|
||||
if (!mod) {
|
||||
throw new Error("No mod found under " + name);
|
||||
}
|
||||
if (mod.enabled) {
|
||||
return this.disableMod(name);
|
||||
}
|
||||
else {
|
||||
return this.enableMod(name);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Toggles any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
ModAttachr.prototype.toggleMods = function () {
|
||||
var names = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
names[_i - 0] = arguments[_i];
|
||||
}
|
||||
names.forEach(this.toggleMod.bind(this));
|
||||
};
|
||||
/* Actions
|
||||
*/
|
||||
/**
|
||||
* Fires an event, which calls all functions listed undder mods for that
|
||||
* event. Any number of arguments may be given.
|
||||
*
|
||||
* @param {String} event The name of the event to fire.
|
||||
*/
|
||||
ModAttachr.prototype.fireEvent = function (event) {
|
||||
var extraArgs = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
extraArgs[_i - 1] = arguments[_i];
|
||||
}
|
||||
var fires = this.events[event], args = Array.prototype.splice.call(arguments, 0), mod, i;
|
||||
// If no triggers were defined for this event, that's ok: just stop.
|
||||
if (!fires) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < fires.length; i += 1) {
|
||||
mod = fires[i];
|
||||
args[0] = mod;
|
||||
if (mod.enabled) {
|
||||
mod.events[event].apply(mod.scope, args);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Fires an event specifically for one mod, rather than all mods containing
|
||||
* that event.
|
||||
*
|
||||
* @param {String} eventName The name of the event to fire.
|
||||
* @param {String} modName The name of the mod to fire the event.
|
||||
*/
|
||||
ModAttachr.prototype.fireModEvent = function (eventName, modName) {
|
||||
var extraArgs = [];
|
||||
for (var _i = 2; _i < arguments.length; _i++) {
|
||||
extraArgs[_i - 2] = arguments[_i];
|
||||
}
|
||||
var mod = this.mods[modName], args = Array.prototype.slice.call(arguments, 2), fires;
|
||||
if (!mod) {
|
||||
throw new Error("Unknown mod requested: '" + modName + "'");
|
||||
}
|
||||
args[0] = mod;
|
||||
fires = mod.events[eventName];
|
||||
if (!fires) {
|
||||
throw new Error("Mod does not contain event: '" + eventName + "'");
|
||||
}
|
||||
return fires.apply(mod.scope, args);
|
||||
};
|
||||
return ModAttachr;
|
||||
})();
|
||||
_ModAttachr.ModAttachr = ModAttachr;
|
||||
})(ModAttachr || (ModAttachr = {}));
|
||||
@@ -0,0 +1,379 @@
|
||||
/// <reference path="ItemsHoldr-0.2.1.ts" />
|
||||
|
||||
declare module ModAttachr {
|
||||
export interface IModAttachrMod {
|
||||
// The user-readable name of the mod.
|
||||
name: string;
|
||||
|
||||
// The mapping of events to callback Functions to be evaluated.
|
||||
events: { [i: string]: IModEvent };
|
||||
|
||||
// The scope to call event Functions from, if necessary.
|
||||
scope?: any;
|
||||
|
||||
// Whether the mod is currently enabled (by default, false).
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
interface IModEvent {
|
||||
(...args: any[]): any;
|
||||
}
|
||||
|
||||
export interface IModAttachrSettings {
|
||||
/**
|
||||
* Mods to be immediately added via addMod.
|
||||
*/
|
||||
mods?: any[];
|
||||
|
||||
/**
|
||||
* A ItemsHoldr to store mod status locally.
|
||||
*/
|
||||
ItemsHoldr?: ItemsHoldr.IItemsHoldr;
|
||||
|
||||
/**
|
||||
* Whether there should be a ItemsHoldr created if one isn't given.
|
||||
*/
|
||||
storeLocally?: boolean;
|
||||
|
||||
/**
|
||||
* A default scope to apply mod events from, if not the ModAttachr.
|
||||
*/
|
||||
scopeDefault?: any;
|
||||
}
|
||||
|
||||
export interface IModAttachr {
|
||||
getMods(): any;
|
||||
getMod(name: string): IModAttachrMod;
|
||||
getEvents(): any;
|
||||
getEvent(name: string): IModAttachrMod[];
|
||||
getItemsHolder(): ItemsHoldr.IItemsHoldr;
|
||||
addMod(mod: IModAttachrMod): void;
|
||||
addMods(mods: IModAttachrMod[]): void;
|
||||
enableMod(name: string): void;
|
||||
enableMods(...names: string[]): void;
|
||||
disableMod(name: string): void;
|
||||
disableMods(...names: string[]): void;
|
||||
toggleMod(name: string): void;
|
||||
toggleMods(...names: string[]): void;
|
||||
fireEvent(event: string, ...extraArgs: any[]): void;
|
||||
fireModEvent(eventName: string, modName: string, ...extraArgs: any[]): any;
|
||||
}
|
||||
}
|
||||
|
||||
module ModAttachr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* An addon for for extensible modding functionality. "Mods" register triggers
|
||||
* such as "onModEnable" or "onReset" that can be triggered.
|
||||
*/
|
||||
export class ModAttachr implements IModAttachr {
|
||||
/**
|
||||
* For each event, the listing of mods that attach to that event.
|
||||
*/
|
||||
private events: { [i: string]: IModAttachrMod[] };
|
||||
|
||||
/**
|
||||
* All known mods, keyed by name.
|
||||
*/
|
||||
private mods: { [i: string]: IModAttachrMod };
|
||||
|
||||
/**
|
||||
* A ItemsHoldr object that may be used to store mod status.
|
||||
*/
|
||||
private ItemsHolder: ItemsHoldr.IItemsHoldr;
|
||||
|
||||
/**
|
||||
* A default scope to apply mod events from, if not this ModAttachr.
|
||||
*/
|
||||
private scopeDefault: any;
|
||||
|
||||
/**
|
||||
* @param {IModAttachrSettings} [settings]
|
||||
*/
|
||||
constructor(settings: IModAttachrSettings) {
|
||||
this.mods = {};
|
||||
this.events = {};
|
||||
|
||||
if (!settings) {
|
||||
return;
|
||||
}
|
||||
this.scopeDefault = settings.scopeDefault;
|
||||
|
||||
// If a ItemsHoldr is provided, use it
|
||||
if (settings.ItemsHoldr) {
|
||||
this.ItemsHolder = settings.ItemsHoldr;
|
||||
} else if (settings.storeLocally) {
|
||||
// If one isn't provided by storeLocally is still true, make one
|
||||
this.ItemsHolder = new ItemsHoldr.ItemsHoldr();
|
||||
}
|
||||
|
||||
if (settings.mods) {
|
||||
this.addMods(settings.mods);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Simple gets
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {Object} An Object keying each mod by their name.
|
||||
*/
|
||||
getMods(): any {
|
||||
return this.mods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} name The name of the mod to return.
|
||||
* @return {Object} The mod keyed by the name.
|
||||
*/
|
||||
getMod(name: string): IModAttachrMod {
|
||||
return this.mods[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} An Object keying each event by their name.
|
||||
*/
|
||||
getEvents(): any {
|
||||
return this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object[]} The mods associated with a particular event.
|
||||
*/
|
||||
getEvent(name: string): IModAttachrMod[] {
|
||||
return this.events[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {ItemsHoldr} The ItemsHoldr if storeLocally is true, or undefined
|
||||
* otherwise.
|
||||
*/
|
||||
getItemsHolder(): ItemsHoldr.IItemsHoldr {
|
||||
return this.ItemsHolder;
|
||||
}
|
||||
|
||||
|
||||
/* Alterations
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a mod to the pool of mods, listing it under all the relevant events.
|
||||
* If the event is enabled, the "onModEnable" event for it is triggered.
|
||||
*
|
||||
* @param {Object} mod A summary Object for a mod, containing at the very
|
||||
* least a name and Object of events.
|
||||
*/
|
||||
addMod(mod: IModAttachrMod): void {
|
||||
var modEvents: any = mod.events,
|
||||
name: string;
|
||||
|
||||
for (name in modEvents) {
|
||||
if (!modEvents.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.events.hasOwnProperty(name)) {
|
||||
this.events[name] = [mod];
|
||||
} else {
|
||||
this.events[name].push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// Mod scope defaults to the ModAttacher's scopeDefault.
|
||||
mod.scope = mod.scope || this.scopeDefault;
|
||||
|
||||
// Record the mod in the ModAttachr's mods listing.
|
||||
this.mods[mod.name] = mod;
|
||||
|
||||
// If the mod is enabled, trigger its "onModEnable" event
|
||||
if (mod.enabled && mod.events.hasOwnProperty("onModEnable")) {
|
||||
this.fireModEvent("onModEnable", mod.name, arguments);
|
||||
}
|
||||
|
||||
// If there's a ItemsHoldr, record the mod in it
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.addItem(mod.name, {
|
||||
"valueDefault": 0,
|
||||
"storeLocally": true
|
||||
});
|
||||
|
||||
// If there was already a (true) value, immediately enable the mod
|
||||
if (this.ItemsHolder.getItem(mod.name)) {
|
||||
this.enableMod(mod.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds each mod in a given Array.
|
||||
*
|
||||
* @param {Array} mods
|
||||
*/
|
||||
addMods(mods: IModAttachrMod[]): void {
|
||||
for (var i: number = 0; i < mods.length; i += 1) {
|
||||
this.addMod(mods[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a mod of the given name, if it exists. The onModEnable event is
|
||||
* called for the mod.
|
||||
*
|
||||
* @param {String} name The name of the mod to enable.
|
||||
*/
|
||||
enableMod(name: string): void {
|
||||
var mod: IModAttachrMod = this.mods[name],
|
||||
args: any[];
|
||||
|
||||
if (!mod) {
|
||||
throw new Error("No mod of name: '" + name + "'");
|
||||
}
|
||||
|
||||
mod.enabled = true;
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
args[0] = mod;
|
||||
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.setItem(name, true);
|
||||
}
|
||||
|
||||
if (mod.events.hasOwnProperty("onModEnable")) {
|
||||
return this.fireModEvent("onModEnable", mod.name, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
enableMods(...names: string[]): void {
|
||||
names.forEach(this.enableMod.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables a mod of the given name, if it exists. The onModDisable event is
|
||||
* called for the mod.
|
||||
*
|
||||
* @param {String} name The name of the mod to disable.
|
||||
*/
|
||||
disableMod(name: string): void {
|
||||
var mod: IModAttachrMod = this.mods[name],
|
||||
args: any[];
|
||||
|
||||
if (!this.mods[name]) {
|
||||
throw new Error("No mod of name: '" + name + "'");
|
||||
}
|
||||
|
||||
this.mods[name].enabled = false;
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
args[0] = mod;
|
||||
|
||||
if (this.ItemsHolder) {
|
||||
this.ItemsHolder.setItem(name, false);
|
||||
}
|
||||
|
||||
if (mod.events.hasOwnProperty("onModDisable")) {
|
||||
return this.fireModEvent("onModDisable", mod.name, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
disableMods(...names: string[]): void {
|
||||
names.forEach(this.disableMod.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a mod via enableMod/disableMod of the given name, if it exists.
|
||||
*
|
||||
* @param {String} name The name of the mod to toggle.
|
||||
*/
|
||||
toggleMod(name: string): void {
|
||||
var mod: IModAttachrMod = this.mods[name];
|
||||
|
||||
if (!mod) {
|
||||
throw new Error("No mod found under " + name);
|
||||
}
|
||||
|
||||
if (mod.enabled) {
|
||||
return this.disableMod(name);
|
||||
} else {
|
||||
return this.enableMod(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles any number of mods, given as any number of Strings or Arrays of
|
||||
* Strings.
|
||||
*
|
||||
* @param {...String} names
|
||||
*/
|
||||
toggleMods(...names: string[]): void {
|
||||
names.forEach(this.toggleMod.bind(this));
|
||||
}
|
||||
|
||||
|
||||
/* Actions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fires an event, which calls all functions listed undder mods for that
|
||||
* event. Any number of arguments may be given.
|
||||
*
|
||||
* @param {String} event The name of the event to fire.
|
||||
*/
|
||||
fireEvent(event: string, ...extraArgs: any[]): void {
|
||||
var fires: any[] = this.events[event],
|
||||
args: any[] = Array.prototype.splice.call(arguments, 0),
|
||||
mod: IModAttachrMod,
|
||||
i: number;
|
||||
|
||||
// If no triggers were defined for this event, that's ok: just stop.
|
||||
if (!fires) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < fires.length; i += 1) {
|
||||
mod = fires[i];
|
||||
args[0] = mod;
|
||||
if (mod.enabled) {
|
||||
mod.events[event].apply(mod.scope, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an event specifically for one mod, rather than all mods containing
|
||||
* that event.
|
||||
*
|
||||
* @param {String} eventName The name of the event to fire.
|
||||
* @param {String} modName The name of the mod to fire the event.
|
||||
*/
|
||||
fireModEvent(eventName: string, modName: string, ...extraArgs: any[]): any {
|
||||
var mod: IModAttachrMod = this.mods[modName],
|
||||
args: any[] = Array.prototype.slice.call(arguments, 2),
|
||||
fires: IModEvent;
|
||||
|
||||
if (!mod) {
|
||||
throw new Error("Unknown mod requested: '" + modName + "'");
|
||||
}
|
||||
|
||||
args[0] = mod;
|
||||
fires = mod.events[eventName];
|
||||
|
||||
if (!fires) {
|
||||
throw new Error("Mod does not contain event: '" + eventName + "'");
|
||||
}
|
||||
|
||||
return fires.apply(mod.scope, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,365 @@
|
||||
declare module ObjectMakr {
|
||||
export interface IObjectMakrClassInheritance {
|
||||
[i: string]: string | IObjectMakrClassInheritance;
|
||||
}
|
||||
|
||||
export interface IObjectMakrClassProperties {
|
||||
[i: string]: any;
|
||||
}
|
||||
|
||||
export interface IObjectMakrSettings {
|
||||
inheritance: any;
|
||||
properties?: { [i: string]: any };
|
||||
doPropertiesFull?: boolean;
|
||||
indexMap?: any;
|
||||
onMake?: string;
|
||||
}
|
||||
|
||||
export interface IObjectMakrClassFunction {
|
||||
new ();
|
||||
}
|
||||
|
||||
export interface IObjectMakr {
|
||||
getInheritance(): any;
|
||||
getProperties(): any;
|
||||
getPropertiesOf(title: string): any;
|
||||
getFullProperties(): any;
|
||||
getFullPropertiesOf(title: string): any;
|
||||
getFunctions(): any;
|
||||
getFunction(name: string): Function;
|
||||
hasFunction(name: string): boolean;
|
||||
getIndexMap(): any;
|
||||
make(name: string, settings?: any): any;
|
||||
}
|
||||
}
|
||||
|
||||
module ObjectMakr {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* An Abstract Factory for JavaScript classes that automates the process of
|
||||
* setting constructors' prototypal inheritance. A sketch of class inheritance
|
||||
* and a listing of properties for each class is taken in, and dynamically
|
||||
* accessible function constructors are made available.
|
||||
*/
|
||||
export class ObjectMakr implements IObjectMakr {
|
||||
/**
|
||||
* The sketch of class inheritance, keyed by name.
|
||||
*/
|
||||
private inheritance: IObjectMakrClassInheritance;
|
||||
|
||||
/**
|
||||
* Type properties for each class.
|
||||
*/
|
||||
private properties: IObjectMakrClassProperties;
|
||||
|
||||
/**
|
||||
* The actual Functions for the classes to be made.
|
||||
*/
|
||||
private functions: { [i: string]: IObjectMakrClassFunction; };
|
||||
|
||||
/**
|
||||
* Whether a full property mapping should be made for each type.
|
||||
*/
|
||||
private doPropertiesFull: boolean;
|
||||
|
||||
/**
|
||||
* If doPropertiesFull is true, a version of properties that contains the
|
||||
* sum properties for each type (rather than missing inherited ones).
|
||||
*/
|
||||
private propertiesFull: any;
|
||||
|
||||
/**
|
||||
* Optionally, how properties can be mapped from an Object to keys.
|
||||
*/
|
||||
private indexMap: any;
|
||||
|
||||
/**
|
||||
* Optionally, a String index for each generated Object's Function to
|
||||
* be run when made.
|
||||
*/
|
||||
private onMake: string;
|
||||
|
||||
/**
|
||||
* @param {IObjectMakrSettings} settings
|
||||
*/
|
||||
constructor(settings: IObjectMakrSettings) {
|
||||
if (typeof settings.inheritance === "undefined") {
|
||||
throw new Error("No inheritance mapping given to ObjectMakr.");
|
||||
}
|
||||
|
||||
this.inheritance = settings.inheritance;
|
||||
this.properties = settings.properties || {};
|
||||
this.doPropertiesFull = settings.doPropertiesFull;
|
||||
this.indexMap = settings.indexMap;
|
||||
this.onMake = settings.onMake;
|
||||
|
||||
this.functions = {};
|
||||
|
||||
if (this.doPropertiesFull) {
|
||||
this.propertiesFull = {};
|
||||
}
|
||||
|
||||
if (this.indexMap) {
|
||||
this.processProperties(this.properties);
|
||||
}
|
||||
|
||||
this.processFunctions(this.inheritance, Object, "Object");
|
||||
}
|
||||
|
||||
|
||||
/* Simple gets
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {Object} The complete inheritance mapping Object.
|
||||
*/
|
||||
getInheritance(): any {
|
||||
return this.inheritance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The complete properties mapping Object.
|
||||
*/
|
||||
getProperties(): any {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The properties Object for a particular class.
|
||||
*/
|
||||
getPropertiesOf(title: string): any {
|
||||
return this.properties[title];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The full properties Object, if doPropertiesFull is on.
|
||||
*/
|
||||
getFullProperties(): any {
|
||||
return this.propertiesFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The full properties Object for a particular class, if
|
||||
* doPropertiesFull is on.
|
||||
*/
|
||||
getFullPropertiesOf(title: string): any {
|
||||
return this.doPropertiesFull ? this.propertiesFull[title] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object} The full mapping of class constructors.
|
||||
*/
|
||||
getFunctions(): any {
|
||||
return this.functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} name The name of a class to retrieve.
|
||||
* @return {Function} The constructor for the given class.
|
||||
*/
|
||||
getFunction(name: string): Function {
|
||||
return this.functions[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} type The name of a class to check for.
|
||||
* @return {Boolean} Whether that class exists.
|
||||
*/
|
||||
hasFunction(name: string): boolean {
|
||||
return this.functions.hasOwnProperty(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Mixed} The optional mapping of indices.
|
||||
*/
|
||||
getIndexMap(): any {
|
||||
return this.indexMap;
|
||||
}
|
||||
|
||||
|
||||
/* Core usage
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given type and returns it.
|
||||
* If desired, any settings are applied to it (deep copy using proliferate).
|
||||
* @param {String} type The type for which a new object of is being made.
|
||||
* @param {Objetct} [settings] Additional attributes to add to the newly
|
||||
* created Object.
|
||||
* @return {Mixed}
|
||||
*/
|
||||
make(name: string, settings: any = undefined): any {
|
||||
var output: any;
|
||||
|
||||
// Make sure the type actually exists in functions
|
||||
if (!this.functions.hasOwnProperty(name)) {
|
||||
throw new Error("Unknown type given to ObjectMakr: " + name);
|
||||
}
|
||||
|
||||
// Create the new object, copying any given settings
|
||||
output = new this.functions[name]();
|
||||
if (settings) {
|
||||
this.proliferate(output, settings);
|
||||
}
|
||||
|
||||
// onMake triggers are handled respecting doPropertiesFull.
|
||||
if (this.onMake && output[this.onMake]) {
|
||||
if (this.doPropertiesFull) {
|
||||
output[this.onMake](
|
||||
output, name, this.properties[name], this.propertiesFull[name]
|
||||
);
|
||||
} else {
|
||||
output[this.onMake](
|
||||
output, name, this.properties[name], this.functions[name].prototype
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/* Core parsing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parser that calls processPropertyArray on all properties given as arrays
|
||||
*
|
||||
* @param {Object} properties The object of function properties
|
||||
* @remarks Only call this if indexMap is given as an array
|
||||
*/
|
||||
private processProperties(properties: any): void {
|
||||
var name: string;
|
||||
|
||||
// For each of the given properties:
|
||||
for (name in properties) {
|
||||
if (this.properties.hasOwnProperty(name)) {
|
||||
// If it's an array, replace it with a mapped version
|
||||
if (this.properties[name] instanceof Array) {
|
||||
this.properties[name] = this.processPropertyArray(this.properties[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output properties object with the mapping shown in indexMap
|
||||
*
|
||||
* @param {Array} properties An array with indiced versions of properties
|
||||
* @example indexMap = ["width", "height"];
|
||||
* properties = [7, 14];
|
||||
* output = processPropertyArray(properties);
|
||||
* // output is now { "width": 7, "height": 14 }
|
||||
*/
|
||||
private processPropertyArray(properties: any[]): any {
|
||||
var output: any = {},
|
||||
i: number;
|
||||
|
||||
// For each [i] in properties, set that property as under indexMap[i]
|
||||
for (i = properties.length - 1; i >= 0; --i) {
|
||||
output[this.indexMap[i]] = properties[i];
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive parser to generate each function, starting from the base.
|
||||
*
|
||||
* @param {Object} base An object whose keys are the names of functions to
|
||||
* made, and whose values are objects whose keys are
|
||||
* for children that inherit from these functions
|
||||
* @param {Function} parent The parent function of the functions about to
|
||||
* be made
|
||||
* @param {String} parentName The name of the parent Function to be
|
||||
* inherited from.
|
||||
* @remarks This may use eval, which is evil and almost never a good idea,
|
||||
* but here it's the only way to make functions with dynamic names.
|
||||
*/
|
||||
private processFunctions(base: any, parent: any, parentName: string): void {
|
||||
var name: string,
|
||||
ref: string;
|
||||
|
||||
// For each name in the current object:
|
||||
for (name in base) {
|
||||
if (base.hasOwnProperty(name)) {
|
||||
this.functions[name] = <IObjectMakrClassFunction>(new Function());
|
||||
|
||||
// This sets the function as inheriting from the parent
|
||||
this.functions[name].prototype = new parent();
|
||||
this.functions[name].prototype.constructor = this.functions[name];
|
||||
|
||||
// Add each property from properties to the function prototype
|
||||
for (ref in this.properties[name]) {
|
||||
if (this.properties[name].hasOwnProperty(ref)) {
|
||||
this.functions[name].prototype[ref] = this.properties[name][ref];
|
||||
}
|
||||
}
|
||||
|
||||
// If the entire property tree is being mapped, copy everything
|
||||
// from both this and its parent to its equivalent
|
||||
if (this.doPropertiesFull) {
|
||||
this.propertiesFull[name] = {};
|
||||
|
||||
if (parentName) {
|
||||
for (ref in this.propertiesFull[parentName]) {
|
||||
if (this.propertiesFull[parentName].hasOwnProperty(ref)) {
|
||||
this.propertiesFull[name][ref]
|
||||
= this.propertiesFull[parentName][ref];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ref in this.properties[name]) {
|
||||
if (this.properties[name].hasOwnProperty(ref)) {
|
||||
this.propertiesFull[name][ref] = this.properties[name][ref];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.processFunctions(base[name], this.functions[name], name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Proliferates all members of the donor to the recipient recursively, as
|
||||
* a deep copy.
|
||||
*
|
||||
* @param {Object} recipient An object receiving the donor's members.
|
||||
* @param {Object} donor An object whose members are copied to recipient.
|
||||
* @param {Boolean} [noOverride] If recipient properties may be overriden
|
||||
* (by default, false).
|
||||
*/
|
||||
private proliferate(recipient: any, donor: any, noOverride: boolean = false): void {
|
||||
var setting: any,
|
||||
i: string;
|
||||
|
||||
// For each attribute of the donor
|
||||
for (i in donor) {
|
||||
// If noOverride is specified, don't override if it already exists
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user