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.containerpoints to either thewindowobject if running in the browser, or theglobalobject if running in node. This is useful if you want to add something to the global scope in all environments. -
@typeOf(object) : string
typeOfdetermines a more specific type of anobjectthan the nativetypeofoperator in JavaScript. This is useful for a number of situations like dealing withObjectpromoted strings and numbers, or arrays which look likeobjects totypeof. UsetypeOfwhen you need more than"object"fromtypeof.Note:
typeOfis substantially slower thantypeof.typeOfworks in a somewhat hackish manner by getting theObject::toStringrepresentation 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 asextendormerge, 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 thesubjectpassed in with the new values.mixinalso has special properties that make it different than the canonicalextendfunctions:- If the
subjecthas asetfunction,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
objecthas aninitializefunction defined, that function will be called and passed thesubject. This is useful for custom extension logic, similar toself.includedin Ruby. For this reason, the keysinitializeanduninitializeare skipped bymixin. mixinonly iterates over keys for which thehasOwnPropertytest passes.
Note:
mixinis 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 theobjectsit will bedeleted from thesubject. Returns thesubject.unmixin, similar tomixin, supports calling anuninitializefunction for each of theobjectsbeing unmixed in. If anuninitializefunction 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
functionNamereturns 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
isChildOfis a simple DOM helper which returns a boolean describing if the passedchildnode can be found in the descendants of the passedparentnode. -
@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 ofsetImmediatewhich does its best to call the callback immediately after the stack empties. Batman'ssetImmediatepolyfill uses the native version if available,window.postmessagetrickery if supported, and falls back onsetTimeout(->, 0).setImmediatereturns a handle which can be passed toclearImmediateto cancel the future calling of the callback. -
@clearImmediate(handle)
clearImmediatestops the calling of a callback in the future when passed itshandle(which is returned from thesetImmediatecall used to enqueue it). -
@forEach(iterable : object, iterator : Function[, context : Object])
The
forEachBatman helper is a universal iteration helper. When passed aniterableobject, the helper will call theiterator(optionally in thecontext) for each item in theiterable. Theiterablecan be:- something which has its own
forEach, in which case theiteratorwill 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-inloop will be used to iterate over each entry.
The
forEachhelper 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
objectHasKeyreturns a boolean describing the presence of thekeyin the passedobject.objectHasKeydelegates to theobject'shasKeyfunction if present, and otherwise just does a check using the JavaScriptinoperator.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
containsreturns a boolean describing if the givenobjecthas memberitem. Membership in this context is defined as:- the result of
object.has(item)if theobjecthas ahasfunction defined - the result of
item in objectif theobjectis arraylike - the result of the Batman.objectHasKey otherwise
Note: When passed an object without a
hasfunction,containswill returntrueif theobjecthasitemas akey, not as a value at any key.containsis useful for checking item membership when the type of the object can't be relied on. - the result of
-
@get(object, key) : value
getis a general purpose function for retrieving the value from akeyon anobjectof an indeterminate type. This is useful if code needs to work with bothBatman.Objects and Plain Old JavaScript Objects.gethas the following semantics:- if the
objecthas agetfunction defined, return the result ofobject.get(key) if the object does not have a
getfunction defined, use an ephemeralBatman.Propertyto retrieve the key. This is equivalent toobject[key]for single segmentkeys, but if thekeyis multi-segment (example: 'product.customer.name'),getwill do nested gets until the eitherundefinedor 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
getPathreturns 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
escapeHTMLtakes 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
escapeHTMLto purge unsafe data from user submitted content. WhileescapeHTMLis 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("& < > \" '"), "& < > " '"
