Batman
Batman includes a number of useful, general purpose helper functions and references. They can all be found attached to the Batman
object and can optionally be exported into the global namespace with a $
prefix.
-
container
Batman.container
points to either thewindow
object if running in the browser, or theglobal
object if running in node. This is useful if you want to add something to the global scope in all environments. -
@typeOf(object) : string
typeOf
determines a more specific type of anobject
than the nativetypeof
operator in JavaScript. This is useful for a number of situations like dealing withObject
promoted strings and numbers, or arrays which look likeobject
s totypeof
. UsetypeOf
when you need more than"object"
fromtypeof
.Note:
typeOf
is substantially slower thantypeof
.typeOf
works in a somewhat hackish manner by getting theObject::toString
representation of the object and slicing it to retrieve the name of the constructor.test 'typeOf returns "String" for both strings and Object strings', -> primitive = "test" objectified = new String("test") equal typeof primitive, "string" equal typeof objectified, "object" equal Batman.typeOf(primitive), "String" equal Batman.typeOf(objectified), "String" test 'typeOf returns Array for arrays', -> array = []; equal typeof array, "object" equal Batman.typeOf(array), "Array"
-
@mixin(subject, objects...) : subject
mixin
, occasionally known elsewhere asextend
ormerge
, flattens a series of objects onto the subject. Key/value pairs on objects passed as later arguments (arguments with a higher index) take precedence over earlier arguments. Returns thesubject
passed in with the new values.mixin
also has special properties that make it different than the canonicalextend
functions:- If the
subject
has aset
function,subject.set(key, value)
will be used to apply keys instead ofsubject[key] = value
. This means that if the subject is aBatman.Object
, observers and thus bindings on the object will be notified when other (Batmanified or not) objects are mixed into it. - If a mixed-in
object
has aninitialize
function defined, that function will be called and passed thesubject
. This is useful for custom extension logic, similar toself.included
in Ruby. For this reason, the keysinitialize
anduninitialize
are skipped bymixin
. mixin
only iterates over keys for which thehasOwnProperty
test passes.
Note:
mixin
is destructive to (only) the first argument. If you need a non-destructive version ofmixin
, just pass an empty object as the first object, and all keys from the successive arguments will be applied to the empty object.test 'mixin merges argument objects', -> subject = {} deepEqual Batman.mixin(subject, {fit: true}, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject" deepEqual subject, {fit: true, fly: true, funky: true}, "the subject is modified destructively" test 'mixin merges argument objects', -> unmodified = {fit: true} deepEqual Batman.mixin({}, unmodified, {fly: true}, {funky: true}), {fit: true, fly: true, funky: true}, "mixin returns the subject" deepEqual unmodified, {fit: true}, "argument objects are untouched allowing non-destructive merge" test 'mixed in objects passed as higher indexed arguments take precedence', -> subject = {} deepEqual Batman.mixin(subject, {x: 1, y: 1}, {x: 2}), {x: 2, y: 1}
- If the
-
@unmixin(subject, objects...) : subject
unmixin
"unmerges" the passed objects from thesubject
. If a key exists on any of theobjects
it will bedelete
d from thesubject
. Returns thesubject
.unmixin
, similar tomixin
, supports calling anuninitialize
function for each of theobjects
being unmixed in. If anuninitialize
function exists on eachtest 'unmixin removes keys found on the unmixined objects on the subject', -> subject = {fit: true, fly: true, funky: true} deepEqual Batman.unmixin(subject, {fit: true}, {fly: true}), {funky: true}, "unmixin returns the subject" deepEqual subject, {funky: true}, "the subject is destructively modified."
-
@functionName(function) : string
functionName
returns the name of a given function, if any. Works with Internet Explorer 7/8/9, FireFox, Chrome, and Safari.test 'functionName returns the name of a given function', -> equal Batman.functionName("".toString), 'toString'
-
@isChildOf(parent : HTMLElement, child : HTMLElement) : boolean
isChildOf
is a simple DOM helper which returns a boolean describing if the passedchild
node can be found in the descendants of the passedparent
node. -
@setImmediate(callback : Function) : object
setImmediate
(and its sisterclearImmediate
) are a more efficient version ofsetTimeout(callback, 0)
. Due to timer resolution issues, setTimeout passed a timeout of 0 doesn't actually execute the function as soon as the JS execution stack has been emptied, but at minimum 4ms and maxmium 25ms after. For this reason Batman provides a cross browser implementation ofsetImmediate
which does its best to call the callback immediately after the stack empties. Batman'ssetImmediate
polyfill uses the native version if available,window.postmessage
trickery if supported, and falls back onsetTimeout(->, 0)
.setImmediate
returns a handle which can be passed toclearImmediate
to cancel the future calling of the callback. -
@clearImmediate(handle)
clearImmediate
stops the calling of a callback in the future when passed itshandle
(which is returned from thesetImmediate
call used to enqueue it). -
@forEach(iterable : object, iterator : Function[, context : Object])
The
forEach
Batman helper is a universal iteration helper. When passed aniterable
object, the helper will call theiterator
(optionally in thecontext
) for each item in theiterable
. Theiterable
can be:- something which has its own
forEach
, in which case theiterator
will just be passed toiterable.forEach
. - an array like object, in which case a JavaScript
for(;;)
loop will be used to iterate over each entry - or an object, in which case a JavaScript
for-in
loop will be used to iterate over each entry.
The
forEach
helper is useful for iterating over objects when the type of those objects isn't guaranteed.test 'forEach iterates over objects with forEach defined', -> results = [] set = new Batman.SimpleSet('a') Batman.forEach(set, (x) -> results.push(x)) deepEqual results, ['a'] test 'forEach iterates over array like objects', -> results = [] ArrayLike = -> ArrayLike:: = [] imitation = new ArrayLike Array::push.call(imitation, "a") Array::push.call(imitation, "b") Batman.forEach(imitation, (x) -> results.push(x)) deepEqual results, ['a', 'b'] test 'forEach iterates over objects', -> result = {} object = {x: true} Batman.forEach(object, (key, val) -> result[key] = val) deepEqual result, object
- something which has its own
-
@objectHasKey(object, key) : boolean
objectHasKey
returns a boolean describing the presence of thekey
in the passedobject
.objectHasKey
delegates to theobject
'shasKey
function if present, and otherwise just does a check using the JavaScriptin
operator.test 'objectHasKey verifies if a key is present in an object', -> subject = {fit: true} ok Batman.objectHasKey(subject, 'fit') equal Batman.objectHasKey(subject, 'flirty'), false test 'objectHasKey verifies if a key is present in an object with `hasKey` defined', -> subject = new Batman.SimpleHash {fit: true} ok Batman.objectHasKey(subject, 'fit') equal Batman.objectHasKey(subject, 'flirty'), false
-
@contains(object, item) : boolean
contains
returns a boolean describing if the givenobject
has memberitem
. Membership in this context is defined as:- the result of
object.has(item)
if theobject
has ahas
function defined - the result of
item in object
if theobject
is arraylike - the result of the Batman.objectHasKey otherwise
Note: When passed an object without a
has
function,contains
will returntrue
if theobject
hasitem
as akey
, not as a value at any key.contains
is useful for checking item membership when the type of the object can't be relied on. - the result of
-
@get(object, key) : value
get
is a general purpose function for retrieving the value from akey
on anobject
of an indeterminate type. This is useful if code needs to work with bothBatman.Object
s and Plain Old JavaScript Objects.get
has the following semantics:- if the
object
has aget
function defined, return the result ofobject.get(key)
if the object does not have a
get
function defined, use an ephemeralBatman.Property
to retrieve the key. This is equivalent toobject[key]
for single segmentkey
s, but if thekey
is multi-segment (example: 'product.customer.name'),get
will do nested gets until the eitherundefined
or the end of the keypath is reached.test 'get returns the value at a key on a POJO', -> subject = {fit: true} equal Batman.get(subject, 'fit'), true equal Batman.get(subject, 'flirty'), undefined
test 'get returns the value at a key on a Batman.Object', -> subject = Batman {fit: true} equal Batman.get(subject, 'fit'), true equal Batman.get(subject, 'flirty'), undefined
test 'get returns the value at a deep key on a POJO', -> subject = {customer: {name: "Joe"}} equal Batman.get(subject, 'customer.name'), "Joe" equal Batman.get(subject, 'customer.age'), undefined
test 'get returns the value at a deep key on a Batman.Object', -> subject = Batman {customer: {name: "Joe"}} equal Batman.get(subject, 'customer.name'), "Joe" equal Batman.get(subject, 'customer.age'), undefined
- if the
-
@getPath(base, segments) : string
getPath
returns the hash value denoted by the specified path, which consists of an array of nested hash keys. See examples below for more detail.test "takes a base and an array of keys and returns the corresponding nested value", -> @complexObject = new Batman.Object hash: new Batman.Hash foo: new Batman.Object(bar: 'nested value'), "foo.bar": 'flat value' equal Batman.getPath(@complexObject, ['hash', 'foo', 'bar']), 'nested value' equal Batman.getPath(@complexObject, ['hash', 'foo.bar']), 'flat value' strictEqual Batman.getPath(@complexObject, ['hash', 'not-foo', 'bar']), undefined test "returns just the base if the key array is empty", -> @complexObject = new Batman.Object hash: new Batman.Hash foo: new Batman.Object(bar: 'nested value'), "foo.bar": 'flat value' strictEqual Batman.getPath(@complexObject, []), @complexObject strictEqual Batman.getPath(null, []), null test "returns undefined if the base is null-ish", -> @complexObject = new Batman.Object hash: new Batman.Hash foo: new Batman.Object(bar: 'nested value'), "foo.bar": 'flat value' strictEqual Batman.getPath(null, ['foo']), undefined strictEqual Batman.getPath(undefined, ['foo']), undefined test "returns falsy values", -> @complexObject = new Batman.Object hash: new Batman.Hash foo: new Batman.Object(bar: 'nested value'), "foo.bar": 'flat value' strictEqual Batman.getPath(num: 0, ['num']), 0 strictEqual Batman.getPath(thing: null, ['thing']), null
-
@escapeHTML(input) : string
escapeHTML
takes a string of unknown origin and makes it safe for display on a web page by encoding control characters in HTML into their HTML entities.Warning: Do not rely on
escapeHTML
to purge unsafe data from user submitted content. WhileescapeHTML
is applied to every binding's contents by default, it should not be your only line of defence against script injection attacks.test 'escapeHTML encodes special characters into HTML entities', -> equal Batman.escapeHTML("& < > \" '"), "& < > " '"