Source

Hummingbird.js

import { enlist, release, releaseSingle, sleep, wake, createTimer, addTimer } from './manager'
import { calculateFPS, isNumber, toggleLife } from './utils'
import { SECONDS, FLAPS } from './constants'

/**
 * A Hummingbird is a single object that holds a single method to be called on a set
 * iteration speed.
 *
 * @class
 * @category API
 * @constructor
 *
 * @param {function} handler - The method called on each tick based on speed
 * @param {object} [args] - Optional parameters
 * @param {number} [args.speed=30] - The frequency that the {@link Hummingbird}'s handler will be called
 * @param {string} [args.speedIn={@link FLAPS}] - How to interpret the <code>speed</code>
 * @param {*} [args.scope] - What <code>this</code> will be on the handler
 * @param {number} [args.lifeSpan=undefined] - Length of time the handler will be active
 * @param {string} [args.lifeSpanIn={@link SECONDS}] - How to interpret <code>lifeSpan</code>
 * @param {function} [args.onComplete=undefined] - Called when <code>lifeSpan</code> value is reached
 * @param {array} [args.onCompleteParams] - Parameters passed through <code>onComplete</code>
 *
 * @example
 * import { Hummingbird } from 'hummingbird'
 *
 * function foo() { ... }
 * function bar() { ... }
 *
 * // basic creation, using default speed (30 fps)
 * const fooBird = new Hummingbird(foo)
 *
 * // complex creation, things to note:
 * // - 'new' is optional
 * // - use 'args' object to pass in optional parameters
 * // - does not require being assinged to variable
 * Hummingbird(bar, {
 * 	// interpret speed in seconds
 * 	speedIn: SECONDS,
 * 	// will call handler once every 1.3 seconds
 * 	speed: 1.3,
 * 	// interpret lifeSpan in flaps
 * 	lifeSpanIn: FLAPS,
 * 	// will call handler only 3 times
 * 	lifeSpan: 3,
 * 	// if lifeSpan, add a handler
 * 	onComplete: function(a, b) {
 * 		// a = true, b = 'ruh'
 * 	},
 * 	// provide params to the handler
 * 	onCompleteParams: [true, 'ruh']
 * })
 */
const Hummingbird = function(handler, args) {
	args = args || {}
	const T = this

	// allow for omitting 'new'
	if (!(T instanceof Hummingbird)) {
		return new Hummingbird(handler, args)
	}

	T._handler = handler

	// Optional args:
	T.speedIn = args.speedIn || FLAPS
	T._speed = calculateFPS(args.speed || 30, T.speedIn)
	T.scope = args.scope || null
	T.name = args.name || 'hb-' + Math.round(Date.now() * Math.random())

	if (args.lifeSpan) {
		T.lifeSpan = args.lifeSpan
		T.lifeSpanIn = args.lifeSpanIn || SECONDS
		T.onComplete = args.onComplete || function() {}
		T.onCompleteParams = args.onCompleteParams
		T.lifeTimer = createTimer(T)
	}

	T._parent = null
	T.isReleased = false

	// init
	enlist(T)
}

Hummingbird.prototype = {
	/**
	 * The iteration speed of the instance.
	 * @param {number} val
	 * @returns {Hummingbird} Itself for method chaining
	 * @example
	 * // continued from Constructor example
	 * // changes the speed from the default 30 fps to 24 fps
	 * foobird.speed(24)
	 */
	speed: function(val) {
		const T = this
		if (val & isNumber(val)) {
			releaseSingle(T)
			T._speed = calculateFPS(val, T.speedIn)
			enlist(T, true)
			return T
		} else {
			return T._speed
		}
	},

	/**
	 * Will sleep (pause) this Hummingbird instance so it will not be called on iterations. If this
	 * is the only and/or last active instance, the entire engine will stop running.
	 * @returns {Hummingbird} Itself for method chaining
	 * @see {@link sleep Hummingbird.sleep}
	 * @example
	 * // continued from Constructor example
	 * foobird.sleep()
	 */
	sleep: function() {
		sleep(this)
		return this
	},

	/**
	 * Will wake (resume) this Hummingbird instance so it will not be called on iterations.
	 * If this instance has been released, calling this will act as if creating a new instance
	 * without the memory bloat of actually creating. This includes reseting lifeSpan to run again.
	 * This will activate the entire engine if it is not already running.
	 * @returns {Hummingbird} Itself for method chaining
	 * @see {@link wake Hummingbird.wake}
	 * @example
	 * // continued from Constructor example
	 * foobird.wake()
	 */
	wake: function() {
		const T = this
		if (T.isReleased) {
			enlist(T)
			if (T.lifeTimer) addTimer(T.lifeTimer)
			T.isReleased = false
		} else {
			wake(T)
		}
		return T
	},

	/**
	 * Will release (remove) this Hummingbird instance from the engine. Note that this does NOT
	 * destroy itself and any external variables holding reference to the instance are still valid.
	 * Be mindful of assigning instances to variables repeatedly as it can cause a bloat without
	 * allowing for proper garbage collection.
	 * @returns {Hummingbird} Itself for method chaining
	 * @see {@link release Hummingbird.release}
	 * @example
	 * // continued from Constructor example
	 * foobird.release()
	 */
	release: function() {
		const T = this
		if (!T.isReleased) {
			releaseSingle(T)
			T.isReleased = true
		}
		return T
	},

	/**
	 * The string representation of the Hummingbird
	 * @returns {String}
	 */
	toString: () => 'Hummingbird',

	// internally called methods
	squawk: function() {
		const T = this
		T._handler.call(T.scope, T)
		return T
	},
	sleepLife: function() {
		toggleLife(this, true)
	},
	wakeLife: function() {
		toggleLife(this, false)
	}
}

Hummingbird.release = release
Hummingbird.sleep = sleep
Hummingbird.wake = wake
//Hummingbird.defaultFps

export default Hummingbird