Config
Table of Contents

Namespace

O

The only new global variable introduced by the library. All Classes and Functions are stored under this namespace.

/*global window, O */

"use strict";

if ( typeof O === 'undefined' ) {
 window.O = {};
}

( function ( NS ) {

Method

O.meta( object )

Returns an object representing the metadata for the given object. This has the following properties:

objectThe original object the metadata is for. A metadata object may be shared with other objects for which the original object is the prototype until they need to write to it. This reference is used to detect whether the metadata is inherited, as it's quicker than using Object#hasOwnProperty.
dependentsA mapping of keys in the object to computed properties that depend on them. This only maps to direct dependents and is constructed as the computed properties are added to the object. This is shared with the prototype object (even after a separate metadata object has been created) until a new computed property is added or removed from the object, at which point it is cloned so the modifications do not affect the parent object.
allDependentsA mapping of keys in the object to the full set of computed properties that depend on them, even indirectly. This is shared with the prototype object (even after a separate metadata object has been created) until a new computed property is added or removed from the object. The allDependents map is calculated lazily as required; you should use the O.ComputedProps#propertiesDependentOnKey method to fetch the list.
cacheA mapping of keys to the last returned value of cacheable computed properties.
observersA mapping of keys to an array of observers for that key. Event listeners are also in here, mapped from a key of 'event' + the event type.
changedNull, or if the depth property is >1, an object mapping keys or properties that have changed value, to an object holding the old and possibly the new value.
depthThe number of calls to O.ObservableProps#beginPropertyChanges without a corresponding call to O.ObservableProps#endPropertyChanges.
pathObserversA mapping of keys to a list of paths they observe.
bindingsA mapping of keys to Binding objects.
initsA mapping of mixin names to a reference count of the number of properties requiring a call to its init/destroy methods.
isInitialisedBoolean: have the necessary init methods been called?
For example:

{
    object: {
        w: O.bind( 'z.b' ),
        x: 5,
        y: function () {
            return this.get( 'x' ) * 2;
        }.property( 'x' ),
        z: function () {
            [...]
        }.property( 'y' ),
        onX: function () {
            [...]
        }.observes( 'x', 'z.a' )
    },
    dependents: {
        x: [ 'y' ],
        y: [ 'z' ]
    },
    allDependents: {
        x: [ 'y', 'z' ]
        // Note, in this example 'y' has not yet been calculated, since
        // it has not been required yet.
    },
    cache: {
        y: 10
    },
    observers: {
        x: [ { object: null, method: 'onX' } ]
    },
    changed: null,
    depth: 0,
    pathObservers: {
        onX: [ 'z.a' ]
    },
    bindings: {
        w: Binding
    },
    inits: {
        Bindings: 1,
        Observers: 1
    },
    isInitialised: true
}

Parameters

objectObject The object to fetch the metadata for.

Returns

Object The metadata for the object.

var Metadata = function ( object ) {
 this.object = object;
 this.dependents = {};
 this.allDependents = {};
 this.cache = {};
 this.observers = {};
 this.changed = null;
 this.depth = 0;
 this.pathObservers = {};
 this.bindings = {};
 this.inits = {};
 this.isInitialised = false;

 object.__meta__ = this;
};

var meta = NS.meta = function ( object ) {
 var data = object.__meta__;
 if ( !data ) {
   data = new Metadata( object );
 } else if ( data.object !== object ) {
   // Until the set of computed properties on the object changes, the
   // 'dependents' information is identical to that of the parent so
   // can be shared. The computed 'allDependents will be calculated
   // when needed and stored in the parent meta object, so be available
   // to all other objects of the same type. The dependents property
   // is copied on write (and the allDependents then reset and
   // calculated separately for the object).
   data = Object.create( data );
   data.object = object;

   // The cache should always be separate.
   data.cache = {};

   // Inherit observers, bindings and init/destructors.
   // The individual properties in these objects will be copied on
   // write, leaving any unaltered properties shared with the parent.
   // Path observers are rare enough that we don't waste time and space
   // creating a new object here, but rather wait until a write is made
   // to data.pathObservers, at which point the inheriting object is
   // created.
   data.observers = Object.create( data.observers );
   data.changed = null;
   data.depth = 0;
   data.bindings = Object.create( data.bindings );
   data.inits = Object.create( data.inits );

   object.__meta__ = data;
 }
 return data;
};

Method

O.guid( item )

Returns a unique ID (within the scope of this instance of the application) for the item passed in.

Parameters

item* The item to get an id for.

Returns

String The id for the item.

var guid = 0;
NS.guid = function ( item ) {
 if ( item === null ) {
   return 'null';
 }
 switch ( typeof item ) {
   case 'boolean':
     return item ? 'true' : 'false';
   case 'number':
     return 'num:' + item.toString( 36 );
   case 'string':
     return 'str:' + item;
   case 'undefined':
     return 'undefined';
 }
 if ( item instanceof Date ) {
   return 'date:' + (+item);
 }
 return item.__guid__ || ( item.__guid__ =
   'id:' + ( guid += 1 ).toString( 36 )
 );
};

Method

O.mixin( object, extras, doNotOverwrite )

Add properties to an object, doing the necessary setup and teardown to ensure special properties (computed, bound, observed etc.), are registered correctly.

Parameters

objectObject The object to add properties to.
extrasObject The extra properties to add.
doNotOverwriteBoolean If true, if there is a existing property in object with the same name as one in extras, it won't be added to the object.

Returns

Object Returns the object parameter.

var mix = NS.mixin = function ( object, extras, doNotOverwrite ) {
 if ( extras ) {
   var force = !doNotOverwrite,
     key, old, value, metadata;

   for ( key in extras ) {
     if ( key !== '__meta__' &&
         ( force || !object.hasOwnProperty( key ) ) ) {
       old = object[ key ];
       value = extras[ key ];
       if ( old && old.__teardownProperty__ ) {
         if ( !metadata ) { metadata = meta( object ); }
         old.__teardownProperty__( metadata, key, object );
       }
       if ( value && value.__setupProperty__ ) {
         if ( !metadata ) { metadata = meta( object ); }
         value.__setupProperty__( metadata, key, object );
       }
       object[ key ] = value;
     }
   }
 }
 return object;
};

Function

O.extend( base, extras, doNotOverwrite )

Add all properties of one object to another, overwriting any existing properties with the same name, unless the doNotOverwrite parameter is set. Only adds properties actually on the object, not any properties on the prototype chain.

Parameters

baseObject The object to be extended.
extrasObject The object whose properties are to be added to base.
doNotOverwriteBoolan Optional If true, will not overwrite a property on the base object with the property of the same name on the extras object.

Returns

Object Returns base.

var extend = NS.extend = function ( base, extras, doNotOverwrite ) {
 for ( var key in extras ) {
   if ( extras.hasOwnProperty( key ) &&
       ( !doNotOverwrite || !base.hasOwnProperty( key ) ) ) {
     base[ key ] = extras[ key ];
   }
 }
 return base;
};

Function

O.merge( base, extras )

Add all properties of one object to another, recursively merging if a key corresponds to another object on both 'base' and 'extras' objects. Only adds properties actually on the object, not any properties on the prototype chain.

Parameters

baseObject The object to be extended.
extrasObject The object whose properties are to be merged into base.

Returns

Object Returns base.

var merge = NS.merge = function ( base, extras ) {
 for ( var key in extras ) {
   if ( extras.hasOwnProperty( key ) ) {
     if ( base.hasOwnProperty( key ) &&
         base[ key ] && extras[ key ] &&
         typeof base[ key ] === 'object' &&
         typeof extras[ key ] === 'object' ) {
       merge( base[ key ], extras[ key ] );
     } else {
       base[ key ] = extras[ key ];
     }
   }
 }
 return base;
};

Function

O.clone( value )

Creates a deep copy of a value. Only works on native JS types; do not use with DOM objects or custom objects.

Parameters

value* The value to be copied.

Returns

* The clone of the value.

var clone = NS.clone = function ( value ) {
 var cloned = value,
   l, key;
 if ( value && typeof value === 'object' ) {
   if ( value instanceof Array ) {
     cloned = [];
     l = value.length;
     while ( l-- ) {
       cloned[l] = clone( value[l] );
     }
   } else if ( value instanceof Date ) {
     cloned = new Date( value );
   } else {
     cloned = {};
     for ( key in value ) {
       cloned[ key ] = clone( value[ key ] );
     }
   }
 }
 return cloned;
};

Function

O.isEqual( a, b )

Compares two values to see if they are equal. Will only work with basic JavaScript types (i.e. the ones that can be encoded in JSON).

Parameters

a* The first value.
b* The second value.

Returns

Boolean Are the values equal, i.e. are they identical primitives, or are the both arrays or objects with equal members?

var isEqual = NS.isEqual = function ( a, b ) {
 var i, l, key, constructor;
 if ( a === b ) {
   return true;
 }
 if ( a && b && typeof a === 'object' && typeof b === 'object' ) {
   if ( a instanceof Array ) {
     if ( b instanceof Array && a.length === b.length ) {
       for ( i = 0, l = a.length; i < l; i += 1 ) {
         if ( !isEqual( a[i], b[i] ) ) {
           return false;
         }
       }
       return true;
     }
   } else if ( a instanceof Date ) {
     return ( +a === +b );
   } else {
     constructor = a.constructor;
     if ( a.constructor !== b.constructor ) {
       return false;
     }
     if ( constructor.isEqual ) {
       return constructor.isEqual( a, b );
     }
     for ( key in a ) {
       if ( !isEqual( a[ key ], b[ key ] ) ) {
         return false;
       }
     }
     for ( key in b ) {
       if ( !isEqual( a[ key ], b[ key ] ) ) {
         return false;
       }
     }
     return true;
   }
 }
 return false;
};

Function

O.Class( params )

The Class function takes an object containing the instance functions for a new class and returns a constructor function with each of these methods in its prototype. It also supports inheritance and mixins, via the special Extends and Mixin properties respectively.

The returned constructor function will be the init method passed in the params. If the prototype has no function with the name 'init', an empty function will be used, or if the class inherits, then the superclass init function will be called.

For example:

> var MyClass = O.Class({ sayBoo: function (){ alert( 'boo' ); } });
> var instance = new MyClass();
> instance.sayBoo(); // Alerts 'boo'.

Parameters

paramsObject An object containing methods or properties to configure this class.

Returns

Constructor The constructor function for the new class.

NS.Class = function ( params ) {
 var parent = params.Extends,
   mixins = params.Mixin,
   init = params.init || ( parent ?
     function () { parent.apply( this, arguments ); } :
     function () {} ),
   proto, i, l;

 if ( parent ) {
   proto = parent.prototype;
   init.parent = proto;
   init.prototype = Object.create( proto );
   init.prototype.constructor = init;
   delete params.Extends;
 }

 if ( mixins ) {
   if ( !( mixins instanceof Array ) ) {
     mixins = [ mixins ];
   }
   for ( i = 0, l = mixins.length; i < l; i += 1 ) {
     init.implement( mixins[i], true );
   }
   delete params.Mixin;
 }

 init.implement( params, true );

 return init;
};

Function

O.sortByProperties( properties )

Creates a comparison function which takes two objects and returns -1/0/1 to indicate whether the first object is before or after the other. Comparison is made by considering each of the properties in the array in turn on the two objects until the objects have non-equal values for a property. If the property values are integer like strings, they will first be converted to numbers for comparison. Other strings will be compared case-insensitively.

Parameters

properties{String[]} The properties to sort the objects by, in order of precedence. Can also supply just a String for one property.

Returns

Function This function may be passed to the Array#sort method to sort the array of objects by the properties specified.

var isNumber = /^\d+$/;
NS.sortByProperties = function ( properties ) {
 if ( !( properties instanceof Array ) ) {
   properties = [ properties ];
 }
 var l = properties.length;

 return function ( a, b ) {
   var hasGet = !!a.get,
     i, prop, aVal, bVal, type;
   for ( i = 0; i < l; i += 1 ) {
     prop = properties[i];
     aVal = hasGet ? a.get( prop ) : a[ prop ];
     bVal = hasGet ? b.get( prop ) : b[ prop ];
     type = typeof aVal;

     // Must be the same type
     if ( type === typeof bVal ) {
       if ( type === 'boolean' && aVal !== bVal ) {
         return aVal ? -1 : 1;
       }
       if ( type === 'string' ) {
         if ( isNumber.test( aVal ) && isNumber.test( bVal ) ) {
           aVal = +aVal;
           bVal = +bVal;
         } else {
           aVal = aVal.toLowerCase();
           bVal = bVal.toLowerCase();
         }
       }
       if ( aVal < bVal ) {
         return -1;
       }
       if ( aVal > bVal ) {
         return 1;
       }
     }
   }
   return 0;
 };
};

Method

Function#implement( methods, force )

Adds a set of methods or other properties to the prototype of a function, so all instances will have access to them.

Parameters

methodsObject The methods or properties to add to the prototype.
forceBoolean Unless this is true, existing methods/properties will not be overwritten.

Returns

Function Returns self.

Function.prototype.implement = function ( methods, force ) {
 mix( this.prototype, methods, !force );
 return this;
};

Method

Function#extend( methods, force )

Adds a set of static methods/properties to the function.

Parameters

methodsObject The methods/properties to add.
forceBoolean Unless this is true, existing methods/properties will not be overwritten.

Returns

Function Returns self.

Function.prototype.extend = function ( methods, force ) {
 extend( this, methods, !force );
 return this;
};

}( O ) );
Animation
Application
Core
DataStore
DOM
DragDrop
Foundation
IO
Localisation
Selection
Parser
TimeZones
Storage
Touch
CollectionViews
UA
ContainerViews
ControlViews
PanelViews
View