/* Start ----------------------------------------------------- license.js*/

//@license
// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//
// For more information about SproutCore, visit http://www.sproutcore.com
//
//
// ==========================================================================
//@license


/* End ------------------------------------------------------- license.js*/

/* Start ----------------------------------------------------- core.js*/

// ==========================================================================
// SproutCore
// Author: Charles Jolley
// copyright 2006-2008, Sprout Systems, Inc.
// ==========================================================================

/*global $type, $I, $A, NodeList */

// this is used by the JavascriptCompile class on the server side.  You can
// use this to automatically determine the order javascript files need to be
// included in.  On the client side, this is a NOP.
var require = require || function require() { } ;
require('license') ;

// ........................................
// GLOBAL CONSTANTS
// 
// Most global constants should be defined inside of the SC namespace.  
// However the following two are useful enough and generally benign enough
// to put into the global object.
var YES = true ; 
var NO = false ;

// prevent a console.log from blowing things up if we are on a browser that
// does not support it
if (typeof console === 'undefined') {
  var console = console || window.console || {} ;
  console.log = console.info = console.warn = console.error = function(){};
}

// ........................................
// BOOTSTRAP
// 
// The root namespace and some common utility methods are defined here. The
// rest of the methods go into the mixin defined below.

/**
  @namespace
  
  The SproutCore namespace.  All SproutCore methods and functions are defined
  inside of this namespace.  You generally should not add new properties to
  this namespace as it may be overwritten by future versions of SproutCore.
  
  You can also use the shorthand "SC" instead of "SproutCore".
  
  SproutCore-Base is a framework that provides core functions for SproutCore
  including cross-platform functions, support for property observing and
  objects.  It's focus is on small size and performance.  You can use this 
  in place of or along-side other cross-platform libraries such as jQuery or
  Prototype.
  
  The core Base framework is based on the jQuery API with a number of 
  performance optimizations.
*/
var SC = SC || {} ; 
var SproutCore = SproutCore || SC ;

/**
  Adds properties to a target object.
  
  Takes the root object and adds the attributes for any additional 
  arguments passed.

  @param target {Object} the target object to extend
  @param properties {Object} one or more objects with properties to copy.
  @returns {Object} the target object.
  @static
*/
SC.mixin = function() {
  // copy reference to target object
  var target = arguments[0] || {};
  var idx = 1;
  var length = arguments.length ;
  var options ;

  // Handle case where we have only one item...extend SC
  if (length === 1) {
    target = this || {};
    idx=0;
  }

  for ( ; idx < length; idx++ ) {
    if (!(options = arguments[idx])) continue ;
    for(var key in options) {
      if (!options.hasOwnProperty(key)) continue ;
      var src = target[key];
      var copy = options[key] ;
      if (target===copy) continue ; // prevent never-ending loop
      if (copy !== undefined) target[key] = copy ;
    }
  }
  
  return target;
} ;

/** 
  Alternative to mixin.  Provided for compatibility with jQuery.
  @function 
*/
SC.extend = SC.mixin ;


// Enough with the bootstrap code.  Let's define some core functions
SC.mixin(/** @scope SC */ {
  
  // ........................................
  // GLOBAL CONSTANTS
  // 
  T_ERROR:     'error',
  T_OBJECT:    'object',
  T_NULL:      'null',
  T_CLASS:     'class',
  T_HASH:      'hash',
  T_FUNCTION:  'function',
  T_UNDEFINED: 'undefined',
  T_NUMBER:    'number',
  T_BOOL:      'boolean',
  T_ARRAY:     'array',
  T_STRING:    'string',

  // ........................................
  // CORE HELPER METHODS
  //   

  /**
    Returns a consistant type for the passed item.
    
    Use this instead of the built-in typeOf() to get the type of an item. 
    It will return the same result across all browsers and includes a bit 
    more detail.  Here is what will be returned:
    
    | Return Value Constant | Meaning |
    | SC.T_STRING | String primitive |
    | SC.T_NUMBER | Number primitive |
    | SC.T_BOOLEAN | Boolean primitive |
    | SC.T_NULL | Null value |
    | SC.T_UNDEFINED | Undefined value |
    | SC.T_FUNCTION | A function |
    | SC.T_ARRAY | An instance of Array |
    | SC.T_CLASS | A SproutCore class (created using SC.Object.extend()) |
    | SC.T_OBJECT | A SproutCore object instance |
    | SC.T_HASH | A JavaScript object not inheriting from SC.Object |
    
    @param item {Object} the item to check
    @returns {String} the type
  */  
  typeOf: function(item) {
    if (item === undefined) return SC.T_UNDEFINED ;
    if (item === null) return SC.T_NULL ; 
    var ret = typeof(item) ;
    if (ret == "object") {
      if (item instanceof Array) {
        ret = SC.T_ARRAY ;
      } else if (item instanceof Function) {
        ret = item.isClass ? SC.T_CLASS : SC.T_FUNCTION ;
        
      // NB: typeOf() may be called before SC.Error has had a chance to load
      // so this code checks for the presence of SC.Error first just to make
      // sure.  No error instance can exist before the class loads anyway so
      // this is safe.
      } else if (SC.Error && (item instanceof SC.Error)) {
        ret = SC.T_ERROR ;        
      } else if (item.isObject === true) {
        ret = SC.T_OBJECT ;
      } else ret = SC.T_HASH ;
    } else if (ret === SC.T_FUNCTION) ret = (item.isClass) ? SC.T_CLASS : SC.T_FUNCTION;
    return ret ;
  },
  
  /**
    Returns YES if the passed value is null or undefined.  This avoids errors
    from JSLint complaining about use of ==, which can be technically 
    confusing.
  */
  none: function(obj) {
    return obj===null || obj===undefined;  
  },
  
  /**
    Returns YES if the passed object is an array or array-like. Instances
    of the NodeList class return NO.
    
    Unlike SC.$type this method returns true even if the passed object is 
    not formally array but appears to be array-like (i.e. has a length 
    property, responds to .objectAt, etc.)
    
    @param obj {Object} the object to test
    @returns {Boolean} 
  */
  isArray: function(obj) {
    if (obj && obj.objectAt) return YES ; // fast path

    var len = (obj ? obj.length : null), type = SC.typeOf(obj);
    return !(SC.none(len) || (type === SC.T_FUNCTION) || (type === SC.T_STRING) || obj.setInterval) ;
  },
  
  /**
    Makes an object into an Array if it is not array or array-like already.
    Unlike SC.$A(), this method will not clone the object if it is already
    an array.
  */
  makeArray: function(obj) {
    return SC.isArray(obj) ? obj : SC.$A(obj);
  },
  
  /**
    Converts the passed object to an Array.  If the object appears to be 
    array-like, a new array will be cloned from it.  Otherwise, a new array
    will be created with the item itself as the only item in the array.
        
    @param object {Object} any enumerable or array-like object.
    @returns {Array} Array of items
  */
  $A: function(obj) {
    
    // null or undefined -- fast path
    if (SC.none(obj)) return [] ;
    
    // primitive -- fast path
    if (obj.slice instanceof Function) return obj.slice() ; 
    
    // enumerable -- fast path
    if (obj.toArray) return obj.toArray() ;

    // if not array-like, then just wrap in array.
    if (!SC.isArray(obj)) return [obj];

    // when all else fails, do a manual convert...
    var ret = [], len = obj.length;
    while(--len >= 0) ret[len] = obj[len];
    return ret ;
  },

  guidKey: "_sc_guid_" + new Date().getTime(),
  
  /**
    Returns a unique GUID for the object.  If the object does not yet have
    a guid, one will be assigned to it.  You can call this on any object,
    SC.Object-based or not, but be aware that it will add a _guid property.
    
    You can also use this method on DOM Element objects.
    
    @param obj {Object} any object, string, number, Element, or primitive
    @returns {String} the unique guid for this instance.
  */
  guidFor: function(obj) {
    if (obj === undefined) return "(undefined)" ;
    if (obj === null) return '(null)' ;
    var guidKey = this.guidKey ;
    if (obj[guidKey]) return obj[guidKey] ;
    
    switch(SC.$type(obj)) {
      case SC.T_NUMBER:
        return (this._numberGuids[obj] = this._numberGuids[obj] || ("#" + obj));
      case SC.T_STRING:
        return (this._stringGuids[obj] = this._stringGuids[obj] || ("$" + obj));
      case SC.T_BOOL:
        return (obj) ? "(true)" : "(false)" ;
      default:
        return SC.generateGuid(obj);
    }
  },
  
  /**
    Returns the cachekey for the named key + prefix. Uses a cache internally
    for performance.
  */
  keyFor: function() {
    var cache = {};
    return function keyFor(prefix, key) {
      var ret, pcache = cache[prefix];
      if (!pcache) pcache = cache[prefix] = {}; // get cache for prefix
      ret = pcache[key];
      if (!ret) ret = pcache[key] = prefix + "_" + key ;
      return ret ;
    } ;
  }(),
  
  /**
    Generates a new guid, optionally saving the guid to the object that you
    pass in.  You will rarely need to use this method.  Instead you should
    call SC.guidFor(obj), which return an existing guid if available.
    
    @param {Object} obj the object to assign the guid to
    @returns {String} the guid
  */
  generateGuid: function(obj) { 
    var ret = ("@" + (SC._nextGUID++)); 
    if (obj) obj[SC.guidKey] = ret ;
    return ret ;
  },
  
  _nextGUID: 0, _numberGuids: [], _stringGuids: {},

  /**
    Returns a unique hash code for the object.  If the object implements
    a hash() method, the value of that method will be returned.  Otherwise,
    this will return the same value as guidFor().  
    
    Unlike guidFor(), this method allows you to implement logic in your 
    code to cause two separate instances of the same object to be treated as
    if they were equal for comparisons and other functions.
    
    IMPORTANT:  If you implement a hash() method, it MUST NOT return a number
    or a string that contains only a number.  Typically hash codes are strings
    that begin with a "%".
    
    @param obj {Object} the object
    @returns {String} the hash code for this instance.
  */
  hashFor: function(obj) {
    return (obj && obj.hash && SC.$type(obj.hash) === SC.T_FUNCTION) ? obj.hash() : this.guidFor(obj) ;
  },

  /**
    This will compare the two object values using their hash codes.
    
    @param a {Object} first value to compare
    @param b {Object} the second value to compare
    @returns {Boolean} YES if the two have equal hash code values.
    
  */
  isEqual: function(a,b) {
    // shortcut a few places.
    if (a === null) {
      return b === null ;
    } else if (a === undefined) {
      return b === undefined ;
    
    // finally, check their hash-codes
    } else return SC.hashFor(a) === SC.hashFor(b) ;
  },

  _numberGuids: [],
  
  _stringGuids: {},
  
  /** 
    Empty function.  Useful for some operations. 
  */
  K: function() { return this; },
  
  /** Empty array.  Useful for some optimizations. */
  A: [],
  
  /**
    Creates a new object with the passed object as its prototype.
    
    This method uses JavaScript's native inheritence method to create a new 
    object.    
    
    You cannot use beget() to create new SC.Object-based objects, but you
    can use it to beget Arrays, Hashes, Sets and objects you build yourself.
    Note that when you beget() a new object, this method will also call the
    didBeget() method on the object you passed in if it is defined.  You can
    use this method to perform any other setup needed.
    
    In general, you will not use beget() often as SC.Object is much more 
    useful, but for certain rare algorithms, this method can be very useful.
    
    For more information on using beget(), see the section on beget() in 
    Crockford's JavaScript: The Good Parts.
    
    @param obj {Object} the object to beget
    @returns {Object} the new object.
  */
  beget: function(obj) {
    if (SC.none(obj)) return null ;
    var K = SC.K; K.prototype = obj ;
    var ret = new K();
    K.prototype = null ; // avoid leaks
    if (SC.$type(obj.didBeget) === SC.T_FUNCTION) ret = obj.didBeget(ret); 
    return ret ;
  },
  
  /**
    Creates a clone of the passed object.  This function can take just about
    any type of object and create a clone of it, including primitive values
    (which are not actually cloned because they are immutable).
    
    If the passed object implements the clone() method, then this function
    will simply call that method and return the result.
    
    @param object {Object} the object to clone
    @returns {Object} the cloned object
  */
  clone: function(object) {
    var ret = object ;
    switch (SC.typeOf(object)) {
    case SC.T_ARRAY:
      if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
        ret = object.clone() ;
      } else ret = object.slice() ;
      break ;
    
    case SC.T_HASH:
    case SC.T_OBJECT:
      if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
        ret = object.clone() ;
      } else {
        ret = {} ;
        for(var key in object) ret[key] = object[key] ;
      }
    }
    
    return ret ;
  },
  
  /**
    Returns a new object combining the values of all passed hashes.
  
    @param object {Object} one or more objects
    @returns {Object} new Object
  */
  merge: function() {
    var ret = {}, len = arguments.length, idx;
    for(idx=0;idx<len;idx++) SC.mixin(ret, arguments[idx]);
    return ret ;
  },
  
  /**
    Returns all of the keys defined on an object or hash.  This is useful
    when inspecting objects for debugging.
    
    @param {Object} obj
    @returns {Array} array of keys
  */
  keys: function(obj) {
    var ret = [];
    for(var key in obj) ret.push(key);
    return ret;
  },
  
  /**
    Convenience method to inspect an object.  This method will attempt to 
    convert the object into a useful string description.
  */
  inspect: function(obj) {
    var v, ret = [] ;
    for(var key in obj) {
      v = obj[key] ;
      if (v === 'toString') continue ; // ignore useless items
      if (SC.typeOf(v) === SC.T_FUNCTION) v = "function() { ... }" ;
      ret.push(key + ": " + v) ;
    }
    return "{" + ret.join(" , ") + "}" ;
  },

  /**
    Returns a tuple containing the object and key for the specified property 
    path.  If no object could be found to match the property path, then 
    returns null.
    
    This is the standard method used throughout SproutCore to resolve property
    paths.
    
    @param path {String} the property path
    @param root {Object} optional parameter specifying the place to start
    @returns {Array} array with [object, property] if found or null
  */
  tupleForPropertyPath: function(path, root) {
    
    // if the passed path is itself a tuple, return it
    if (SC.$type(path) === SC.T_ARRAY) return path ;

    // find the key.  It is the last . or first *
    var key ;
    var stopAt = path.indexOf('*') ;
    if (stopAt < 0) stopAt = path.lastIndexOf('.') ;
    key = (stopAt >= 0) ? path.slice(stopAt+1) : path ;
    
    // convert path to object.
    var obj = this.objectForPropertyPath(path, root, stopAt) ;
    return (obj && key) ? [obj,key] : null ;
  },

  /** 
    Finds the object for the passed path or array of path components.  This is 
    the standard method used in SproutCore to traverse object paths.
    
    @param path {String} the path
    @param root {Object} optional root object.  window is used otherwise
    @param stopAt {Integer} optional point to stop searching the path.
    @returns {Object} the found object or undefined.
  */
  objectForPropertyPath: function(path, root, stopAt) {

    var loc, nextDotAt, key, max ;
    
    if (!root) root = window ;
    
    // faster method for strings
    if (SC.$type(path) === SC.T_STRING) {
      if (stopAt === undefined) stopAt = path.length ;
      loc = 0 ;
      while((root) && (loc < stopAt)) {
        nextDotAt = path.indexOf('.', loc) ;
        if ((nextDotAt < 0) || (nextDotAt > stopAt)) nextDotAt = stopAt;
        key = path.slice(loc, nextDotAt);
        root = root.get ? root.get(key) : root[key] ;
        loc = nextDotAt+1; 
      }
      if (loc < stopAt) root = undefined; // hit a dead end. :(
        
    // older method using an array
    } else {

      loc = 0; max = path.length; key = null;
      while((loc < max) && root) {
        key = path[loc++];
        if (key) root = (root.get) ? root.get(key) : root[key] ;
      }
      if (loc < max) root = undefined ;
    }
    
    return root ;
  },
  
  /**
    This function will restore the few global functions defined by SproutCore
    to their original values.  You can call this method if the globals 
    defined by SproutCore conflict with another library you are using.  The
    current global methods restored by this method are:
    
    - $type()
    - $I()
    - $A()
    
    @returns {SC} SproutCore namespace
  */
  noConflict: function() {
    $type = SC._originalGlobals.$type ;
    $I = SC._originalGlobals.$I ;
    $A = SC._originalGlobals.$A ;
    return this ;
  },
  
  /**
    Reads or writes data from a global cache.  You can use this facility to
    store information about an object without actually adding properties to
    the object itself.  This is needed especially when working with DOM,
    which can leak easily in IE.
    
    To read data, simply pass in the reference element (used as a key) and
    the name of the value to read.  To write, also include the data.
    
    You can also just pass an object to retrieve the entire cache.
    
    @param elem {Object} An object or Element to use as scope
    @param name {String} Optional name of the value to read/write
    @param data {Object} Optional data.  If passed, write.
    @returns {Object} the value of the named data
  */
  data: function(elem, name, data) {
    elem = (elem === window) ? "@window" : elem ;
    var hash = SC.hashFor(elem) ; // get the hash key
    
    // Generate the data cache if needed
    var cache = SC._data_cache ;
    if (!cache) SC._data_cache = cache = {} ;
    
    // Now get cache for element
    var elemCache = cache[hash] ;
    if (name && !elemCache) cache[hash] = elemCache = {} ;
    
    // Write data if provided 
    if (elemCache && (data !== undefined)) elemCache[name] = data ;
    
    return (name) ? elemCache[name] : elemCache ;
  },
  
  /**
    Removes data from the global cache.  This is used throughout the
    framework to hold data without creating memory leaks.
    
    You can remove either a single item on the cache or all of the cached 
    data for an object.
    
    @param elem {Object} An object or Element to use as scope
    @param name {String} optional name to remove. 
    @returns {Object} the value or cache that was removed
  */
  removeData: function(elem, name) {
    elem = (elem === window) ? "@window" : elem ;
    var hash = SC.hashFor(elem) ;
    
    // return undefined if no cache is defined
    var cache = SC._data_cache ;
    if (!cache) return undefined ;
    
    // return undefined if the elem cache is undefined
    var elemCache = cache[hash] ;
    if (!elemCache) return undefined;
    
    // get the return value
    var ret = (name) ? elemCache[name] : elemCache ;
    
    // and delete as appropriate
    if (name) {
      delete elemCache[name] ;
    } else {
      delete cache[hash] ;
    }
    
    return ret ;
  },
  
  /**
    Create or update a SproutCore namespace.  If you also pass a constructor
    function, this function will be called with the new namespace object as
    the first parameter so you can set it up within a private closure.
    
    If a namespace does not already exist, this method will create it.
    Otherwise, it will work with the existing namespace.  A namespace is 
    simply a SproutCore object instance.
    
    @param name {String} name of namespace
    @param op {Function|Hash} applied to namespace.
    @returns {SC.Object} the namespace
  */
  namespace: function(name, op) {
    // walk path to find or create...
    var obj, loc = 0, len = name.length, next, key, root = window;
    while(loc<len) {
      next = name.indexOf('.'); 
      if (next < 0) next = len ;
      key = name.slice(loc, next); 
      obj = root[key];
      if (!obj) obj = root[key] = SC.Object.create();
      loc = next+1;
    }
    
    // obj now containts the namespace.  If a function was passed, execute it
    // if a hash was passed, copy its properties.
    switch(SC.typeOf(op)) {
      case SC.T_FUNCTION:
        op(obj);
        break;
      case SC.T_HASH:
        SC.mixin(obj, op);
        break;
      default:
        // do nothing;
    }
    
    // and return obj
    return obj;
  }
  
});

/** Alias for SC.typeOf() */
SC.$type = SC.typeOf ;
  
/** @private Provided for compatibility with old HTML templates. */
SC.didLoad = SC.K ;

// ........................................
// GLOBAL EXPORTS
//   
// These can be restored using SC.restoreGlobals();
var $type, $I, $A ;
SC._originalGlobals = { $type: $type,  $I: $I, $A: $A } ;
$type = SC.typeOf; 
$I = SC.inspect ;
$A = SC.$A ;

// ........................................
// FUNCTION ENHANCEMENTS
//
// Enhance function.
SC.mixin(Function.prototype,
/** @scope Function.prototype */ {
  
  /**
    Indicates that the function should be treated as a computed property.
    
    Computed properties are methods that you want to treat as if they were
    static properties.  When you use get() or set() on a computed property,
    the object will call the property method and return its value instead of 
    returning the method itself.  This makes it easy to create "virtual 
    properties" that are computed dynamically from other properties.
    
    Consider the following example:
    
    {{{
      contact = SC.Object.create({

        firstName: "Charles",
        lastName: "Jolley",
        
        // This is a computed property!
        fullName: function() {
          return this.getEach('firstName','lastName').compact().join(' ') ;
        }.property('firstName', 'lastName'),
        
        // this is not
        getFullName: function() {
          return this.getEach('firstName','lastName').compact().join(' ') ;
        }
      });

      contact.get('firstName') ;
      --> "Charles"
      
      contact.get('fullName') ;
      --> "Charles Jolley"
      
      contact.get('getFullName') ;
      --> function()
    }}}
    
    Note that when you get the fullName property, SproutCore will call the
    fullName() function and return its value whereas when you get() a property
    that contains a regular method (such as getFullName above), then the 
    function itself will be returned instead.
    
    h2. Using Dependent Keys

    Computed properties are often computed dynamically from other member 
    properties.  Whenever those properties change, you need to notify any
    object that is observing the computed property that the computed property
    has changed also.  We call these properties the computed property is based
    upon "dependent keys".
    
    For example, in the contact object above, the fullName property depends on
    the firstName and lastName property.  If either property value changes,
    any observer watching the fullName property will need to be notified as 
    well.
    
    You inform SproutCore of these dependent keys by passing the key names
    as parameters to the property() function.  Whenever the value of any key
    you name here changes, the computed property will be marked as changed
    also.
    
    You should always register dependent keys for computed properties to 
    ensure they update.
    
    h2. Using Computed Properties as Setters
    
    Computed properties can be used to modify the state of an object as well
    as to return a value.  Unlike many other key-value system, you use the 
    same method to both get and set values on a computed property.  To 
    write a setter, simply declare two extra parameters: key and value.
    
    Whenever your property function is called as a setter, the value 
    parameter will be set.  Whenever your property is called as a getter the
    value parameter will be undefined.
    
    For example, the following object will split any full name that you set
    into a first name and last name components and save them.
    
    {{{
      contact = SC.Object.create({
        
        fullName: function(key, value) {
          if (value !== undefined) {
            var parts = value.split(' ') ;
            this.beginPropertyChanges()
              .set('firstName', parts[0])
              .set('lastName', parts[1])
            .endPropertyChanges() ;
          }
          return this.getEach('firstName', 'lastName').compact().join(' ');
        }.property('firstName','lastName')
        
      }) ;
      
    }}}
    
    bq. *Why Use The Same Method for Getters and Setters?*  Most property-
    based frameworks expect you to write two methods for each property but
    SproutCore only uses one.  We do this because most of the time when
    you write a setter is is basically a getter plus some extra work.  There 
    is little added benefit in writing both methods when you can conditionally
    exclude part of it.  This helps to keep your code more compact and easier
    to maintain.
    
    @param dependentKeys {String...} optional set of dependent keys
    @returns {Function} the declared function instance
  */
  property: function() {
    this.dependentKeys = SC.$A(arguments) ; 
    this.cacheKey = "__cache__" + SC.guidFor(this) ;
    this.lastSetValueKey = "__lastValue__" + SC.guidFor(this) ;
    this.isProperty = true; return this; 
  },
  
  /**
    You can call this method on a computed property to indicate that the 
    property is cacheable (or not cacheable).  By default all computed 
    properties are not cached.  Enabling this feature will allow SproutCore
    to cache the return value of your computed property and to use that
    value until one of your dependent properties changes or until you 
    invoke propertyDidChange() and name the computed property itself.
    
    If you do not specify this option, computed properties are assumed to be
    not cacheable.
    
    @param {Boolean} aFlag optionally indicate cacheable or no, default YES
    @returns {Function} reciever
  */
  cacheable: function(aFlag) {
    this.isProperty = YES;  // also make a property just in case
    if (!this.dependentKeys) this.dependentKeys = [] ;
    this.isCacheable = (aFlag === undefined) ? YES : aFlag ;
    return this;
  },
  
  /**
    Makes the computed property as an outlet.  Outlets can be setup en-masse
    when the object is first instantiated by calling initOutlets(). (This
    method is defined in SC.Observable).
  */
  outlet: function(aFlag) {
    this.autoconfiguredOutlet = (aFlag === undefined) ? YES : aFlag;
    return this ;
  },
  
  /**
    Indicates that the computed property is volatile.  Normally SproutCore 
    assumes that your computed property is indempotent.  That is, calling 
    set() on your property more than once with the same value has the same
    effect as calling it only once.  
    
    All non-computed properties are indempotent and normally you should make
    your computed properties behave the same way.  However, if you need to
    make your property change its return value everytime your method is
    called, you may chain this to your property to make it volatile.
    
    If you do not specify this option, properties are assumed to be 
    non-volatile. 
    
    @param {Boolean} aFlag optionally indicate state, default to YES
    @returns {Function} receiver
  */
  indempotent: function(aFlag) {
    this.isProperty = YES;  // also make a property just in case
    if (!this.dependentKeys) this.dependentKeys = [] ;
    this.isVolatile = !((aFlag === undefined) ? NO : aFlag) ;
    return this;
  },
  
  /**  
    Declare that a function should observe an object at the named path.  Note
    that the path is used only to construct the observation one time.
  */
  observes: function(propertyPaths) { 
    
    // sort property paths into local paths (i.e just a property name) and
    // full paths (i.e. those with a . or * in them)
    var loc = arguments.length, local = null, paths = null;
    while(--loc >= 0) {
      var path = arguments[loc] ;
      // local
      if ((path.indexOf('.')<0) && (path.indexOf('*')<0)) {
        if (!local) local = this.localPropertyPaths = [];
        local.push(path);
        
      // regular
      } else {
        if (!paths) paths = this.propertyPaths = [];
        paths.push(path) ;
      }
    }
    return this;
  },
  
  typeConverter: function() {
    this.isTypeConverter = true; return this ;
  },
  
  /**
    Creates a timer that will execute the function after a specified 
    period of time.
    
    If you pass an optional set of arguments, the arguments will be passed
    to the function as well.  Otherwise the function should have the 
    signature:
    
    {{{
      function functionName(timer)
    }}}

    @param target {Object} optional target object to use as this
    @param interval {Number} the time to wait, in msec
    @returns {SC.Timer} scheduled timer
  */
  invokeLater: function(target, interval) {
    if (interval === undefined) interval = 1 ;
    var f = this;
    if (arguments.length > 2) {
      var args = SC.$A(arguments).slice(2,arguments.length);
      args.unshift(target);
      f = f.bind.apply(f, args) ;
    }
    return SC.Timer.schedule({ target: target, action: f, interval: interval });
  }    
  
}) ;


/* End ------------------------------------------------------- core.js*/

/* Start ----------------------------------------------------- system/enumerator.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

/**
  @class
  
  An object that iterates over all of the values in an object.  
  
  An instance of this object is returned everytime you call the 
  enumerator() method on an object that implements the SC.Enumerable mixin.
  
  Once you create an enumerator instance, you can call nextObject() on it
  until you can iterated through the entire collection.  Once you have
  exhausted the enumerator, you can reuse it if you want by calling reset().
  
  @extends Object
  @since SproutCore 1.0
*/
SC.Enumerator = function(enumerableObject) {
  this.enumerable = enumerableObject ;
  this.reset() ;
  return this ;
} ;

SC.Enumerator.prototype = {
  
  /** 
    Returns the next object in the enumeration or undefined when complete.
    
    @returns {Object} the next object or undefined
  */
  nextObject: function() {
    var index = this._index ;
    var len = this._length;
    if (index >= len) return undefined ; // nothing to do
    
    // get the value
    var ret = this.enumerable.nextObject(index, this._previousObject, this._context) ;
    this._previousObject = ret ;
    this._index = index + 1 ;
    
    if (index >= len) {
      this._context = SC.Enumerator._pushContext(this._context); 
    }
    
    return ret ;
  },
  
  /**
    Resets the enumerator to the beginning.  This is a nice way to reuse
    an existing enumerator. 
    
    @returns {Object} this
  */
  reset: function() {
    var e = this.enumerable ;
    if (!e) throw SC.$error("Enumerator has been destroyed");
    
    var len = this._length = (e.get) ? e.get('length') : e.length ;
    this._index = 0;
    this._previousObject = null ;
    this._context = (len > 0) ? SC.Enumerator._popContext() : null;
  },
  
  /**
    Releases the enumerators enumerable object.  You cannot use this object
    anymore.  This is not often needed but it is useful when you need to 
    make sure memory gets cleared.
    
    @returns {Object} null 
  */
  destroy: function() {
    this.enumerable = this._length = this._index = this._previousObject = this._context = null;
  }
  
} ;

/**
  Use this method to manually create a new Enumerator object.  Usually you
  will not access this method directly but instead call enumerator() on the
  item you want to enumerate.

  @param {SC.Enumerable}  The enumerable object.
  @returns {SC.Enumerator} the enumerator
*/
SC.Enumerator.create = function(enumerableObject) {
  return new SC.Enumerator(enumerableObject) ;
};

// Private context caching methods.  This avoids recreating lots of context
// objects.

SC.Enumerator._popContext = function() {
  var ret = (this._contextCache) ? this._contextCache.pop() : null ;
  return ret || {} ;
} ;

SC.Enumerator._pushContext = function(context) {
  var cache = this._contextCache = this._contextCache || [] ;
  cache.push(context);
  return null ;
}; 



/* End ------------------------------------------------------- system/enumerator.js*/

/* Start ----------------------------------------------------- system/mixins/enumerable.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

require('core') ;
require('system/enumerator');

/**
  @namespace

  This mixin defines the common interface implemented by enumerable objects 
  in SproutCore.  Most of these methods follow the standard Array iteration
  API defined up to JavaScript 1.8 (excluding language-specific features that
  cannot be emulated in older versions of JavaScript).
  
  This mixin is applied automatically to the Array class on page load, so you
  can use any of these methods on simple arrays.  If Array already implements
  one of these methods, the mixin will not override them.
  
  h3. Writing Your Own Enumerable

  To make your own custom class enumerable, you need two items:
  
  1. You must have a length property.  This property should change whenever
     the number of items in your enumerable object changes.  If you using this
     with an SC.Object subclass, you should be sure to change the length 
     property using set().
     
  2. If you must implement nextObject().  See documentation.
    
  Once you have these two methods implement, apply the SC.Enumerable mixin
  to your class and you will be able to enumerate the contents of your object
  like any other collection.
  
  h3. Using SproutCore Enumeration with Other Libraries
  
  Many other libraries provide some kind of iterator or enumeration like
  facility.  This is often where the most common API conflicts occur. 
  SproutCore's API is designed to be as friendly as possible with other 
  libraries by implementing only methods that mostly correspond to the
  JavaScript 1.8 API.  
  
  @static
  @since SproutCore 1.0
*/
SC.Enumerable = {

  /**
    Implement this method to make your class enumerable.
    
    This method will be call repeatedly during enumeration.  The index value
    will always begin with 0 and increment monotonically.  You don't have to
    rely on the index value to determine what object to return, but you should
    always check the value and start from the beginning when you see the
    requested index is 0.
    
    The previousObject is the object that was returned from the last call
    to nextObject for the current iteration.  This is a useful way to 
    manage iteration if you are tracing a linked list, for example.
    
    Finally the context paramter will always contain a hash you can use as 
    a "scratchpad" to maintain any other state you need in order to iterate
    properly.  The context object is reused and is not reset between 
    iterations so make sure you setup the context with a fresh state whenever
    the index parameter is 0.
    
    Generally iterators will continue to call nextObject until the index
    reaches the your current length-1.  If you run out of data before this 
    time for some reason, you should simply return undefined.
    
    The default impementation of this method simply looks up the index.
    This works great on any Array-like objects.
    
    @param index {Number} the current index of the iteration
    @param previousObject {Object} the value returned by the last call to nextObject.
    @param context {Object} a context object you can use to maintain state.
    @returns {Object} the next object in the iteration or undefined   
  */ 
  nextObject: function(index, previousObject, context) {
    return (this.objectAt) ? this.objectAt(index) : this[index] ;
  },
  
  /**
    Returns a new enumerator for this object.  See SC.Enumerator for
    documentation on how to use this object.  Enumeration is an alternative
    to using one of the other iterators described here.
    
    @returns {SC.Enumerator} an enumerator for the receiver
  */
  enumerator: function() { return SC.Enumerator.create(this); },
  
  /**
    Iterates through the enumerable, calling the passed function on each
    item.  This method corresponds to the forEach() method defined in 
    JavaScript 1.6.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(item, index, enumerable) ;      
    }}}
    
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.
    
    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.
    
    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Object} this
  */
  forEach: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      callback.call(target, next, idx, this);
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return this ;
  },
  
  /**
    Retrieves the named value on each member object.  This is more efficient
    than using one of the wrapper methods defined here.  Objects that 
    implement SC.Observable will use the get() method, otherwise the property
    will be accessed directly.
    
    @param {String} key the key to retrieve
    @returns {Array} extracted values
  */
  getEach: function(key) {
    var len = (this.get)? this.get('length') : this.length ;
    var ret = [] ;

    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      var obj = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      ret[ret.length] = obj;
      last = next ;
    }
    last = null;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Sets the value on the named property for each member.  This is more
    efficient than using other methods defined on this helper.  If the object
    implements SC.Observable, the value will be changed to set(), otherwise
    it will be set directly.  null objects are skipped.
    
    @param {String} key the key to set
    @param {Object} value the object to set
    @returns {Object} receiver
  */
  setEach: function(key, value) {
    var len = (this.get)? this.get('length') : this.length ;

    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      if (next) {
        if (next.set) {
          next.set(key, value) ;
        } else next[key] = value ;
      }
      last = next ;
    }
    last = null;
    context = SC.Enumerator._pushContext(context);
    return this ;
  },
  
  /**
    Maps all of the items in the enumeration to another value, returning 
    a new array.  This method corresponds to map() defined in JavaScript 1.6.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(item, index, enumerable) ;      
    }}}
    
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.
    
    It should return the mapped value.
    
    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.
    
    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Array} The mapped array.
  */
  map: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var ret  = [];
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      ret[idx] = callback.call(target, next, idx, this) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Similar to map, this specialized function returns the value of the named
    property on all items in the enumeration.
    
    @params key {String} name of the property
    @returns {Array} The mapped array.
  */
  mapProperty: function(key) {
    var len = (this.get) ? this.get('length') : this.length ;
    var ret  = [];
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      ret[idx] = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Returns an array with all of the items in the enumeration that the passed
    function returns YES for. This method corresponds to filter() defined in 
    JavaScript 1.6.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(item, index, enumerable) ;      
    }}}
    
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.
    
    It should return the YES to include the item in the results, NO otherwise.
    
    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.
    
    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Array} A filtered array.
  */
  filter: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var ret  = [];
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      if(callback.call(target, next, idx, this)) ret.push(next) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Returns an array with just the items with the matched property.  You
    can pass an optional second argument with the target value.  Otherwise
    this will match any property that evaluates to true.
    
    @params key {String} the property to test
    @param value {String} optional value to test against.
    @returns {Array} filtered array
  */
  filterProperty: function(key, value) {
    var len = (this.get) ? this.get('length') : this.length ;
    var ret  = [];
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      var matched = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      if (matched) ret.push(next) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },
    
  /**
    Returns the first item in the array for which the callback returns YES.
    This method works similar to the filter() method defined in JavaScript 1.6
    except that it will stop working on the array once a match is found.

    The callback method you provide should have the following signature (all
    parameters are optional):

    {{{
      function(item, index, enumerable) ;      
    }}}

    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.

    It should return the YES to include the item in the results, NO otherwise.

    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.

    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Object} Found item or null.
  */
  find: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var next, found = NO, ret = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len && !found;idx++) {
      next = this.nextObject(idx, last, context) ;
      if (found = callback.call(target, next, idx, this)) ret = next ;
      last = next ;
    }
    next = last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Returns an the first item with a property matching the passed value.  You
    can pass an optional second argument with the target value.  Otherwise
    this will match any property that evaluates to true.
    
    This method works much like the more generic find() method.
    
    @params key {String} the property to test
    @param value {String} optional value to test against.
    @returns {Object} found item or null
  */
  findProperty: function(key, value) {
    var len = (this.get) ? this.get('length') : this.length ;
    var found = NO, ret = null, last = null, next, cur ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len && !found;idx++) {
      next = this.nextObject(idx, last, context) ;
      cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      found = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      if (found) ret = next ;
      last = next ;
    }
    last = next = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },
      
  /**
    Returns YES if the passed function returns YES for every item in the
    enumeration.  This corresponds with the every() method in JavaScript 1.6.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(item, index, enumerable) ;      
    }}}
    
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.
    
    It should return the YES or NO.
    
    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.
    
    h4. Example Usage
    
    {{{
      if (people.every(isEngineer)) { Paychecks.addBigBonus(); }
    }}}
    
    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Boolean} 
  */
  every: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var ret  = YES;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;ret && (idx<len);idx++) {
      var next = this.nextObject(idx, last, context) ;
      if(!callback.call(target, next, idx, this)) ret = NO ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Returns YES if the passed property resolves to true for all items in the
    enumerable.  This method is often simpler/faster than using a callback.

    @params key {String} the property to test
    @param value {String} optional value to test against.
    @returns {Array} filtered array
  */
  everyProperty: function(key, value) {
    var len = (this.get) ? this.get('length') : this.length ;
    var ret  = YES;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;ret && (idx<len);idx++) {
      var next = this.nextObject(idx, last, context) ;
      var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      ret = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },
  
  
  /**
    Returns YES if the passed function returns true for any item in the 
    enumeration. This corresponds with the every() method in JavaScript 1.6.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(item, index, enumerable) ;      
    }}}
    
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.
    
    It should return the YES to include the item in the results, NO otherwise.
    
    Note that in addition to a callback, you can also pass an optional target
    object that will be set as "this" on the context.  This is a good way
    to give your iterator function access to the current object.
    
    h4. Usage Example
    
    {{{
      if (people.some(isManager)) { Paychecks.addBiggerBonus(); }
    }}}
    
    @params callback {Function} the callback to execute
    @params target {Object} the target object to use
    @returns {Array} A filtered array.
  */
  some: function(callback, target) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;
    if (target === undefined) target = null;
    
    var ret  = NO;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;(!ret) && (idx<len);idx++) {
      var next = this.nextObject(idx, last, context) ;
      if(callback.call(target, next, idx, this)) ret = YES ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Returns YES if the passed property resolves to true for any item in the
    enumerable.  This method is often simpler/faster than using a callback.

    @params key {String} the property to test
    @param value {String} optional value to test against.
    @returns {Boolean} YES 
  */
  someProperty: function(key, value) {
    var len = (this.get) ? this.get('length') : this.length ;
    var ret  = NO;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0; !ret && (idx<len); idx++) {
      var next = this.nextObject(idx, last, context) ;
      var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      ret = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;  // return the invert
  },

  /**
    This will combine the values of the enumerator into a single value. It 
    is a useful way to collect a summary value from an enumeration.  This
    corresponds to the reduce() method defined in JavaScript 1.8.
    
    The callback method you provide should have the following signature (all
    parameters are optional):
    
    {{{
      function(previousValue, item, index, enumerable) ;      
    }}}
    
    - *previousValue* is the value returned by the last call to the iterator.
    - *item* is the current item in the iteration.
    - *index* is the current index in the iteration
    - *enumerable* is the enumerable object itself.

    Return the new cumulative value.

    In addition to the callback you can also pass an initialValue.  An error
    will be raised if you do not pass an initial value and the enumerator is
    empty.

    Note that unlike the other methods, this method does not allow you to 
    pass a target object to set as this for the callback.  It's part of the
    spec. Sorry.
    
    @params callback {Function} the callback to execute
    @params initialValue {Object} initial value for the reduce
    @params reducerProperty {String} internal use only.  May not be available.
    @returns {Array} A filtered array.
  */
  reduce: function(callback, initialValue, reducerProperty) {
    if (typeof callback !== "function") throw new TypeError() ;
    var len = (this.get) ? this.get('length') : this.length ;

    // no value to return if no initial value & empty
    if (len===0 && initialValue === undefined) throw new TypeError();
    
    var ret  = initialValue;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      
      // while ret is still undefined, just set the first value we get as ret.
      // this is not the ideal behavior actually but it matches the FireFox
      // implementation... :(
      if (next != null) {
        if (ret === undefined) {
          ret = next ;
        } else {
          ret = callback.call(null, ret, next, idx, this, reducerProperty);
        }
      }
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    
    // uh oh...we never found a value!
    if (ret === undefined) throw new TypeError() ;
    return ret ;
  },
  
  /**
    Invokes the named method on every object in the receiver that
    implements it.  This method corresponds to the implementation in
    Prototype 1.6.
    
    @param methodName {String} the name of the method
    @param args {Object...} optional arguments to pass as well.
    @returns {Array} return values from calling invoke.
  */
  invoke: function(methodName) {
    var len = (this.get) ? this.get('length') : this.length ;
    if (len <= 0) return [] ; // nothing to invoke....

    // collect the arguments
    var args = [] ;
    var alen = arguments.length ;
    if (alen > 1) {
      for(var idx=1;idx<alen;idx++) args.push(arguments[idx]) ;
    }
    
    // call invoke
    var ret = [] ;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      var method = (next) ? next[methodName] : null ;
      if (method) ret[idx] = method.apply(next, args) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },

  /**
    Invokes the passed method and optional arguments on the receiver elements
    as long as the methods return value matches the target value.  This is 
    a useful way to attempt to apply changes to a collection of objects unless
    or until one fails.

    @param targetValue {Object} the target return value
    @param methodName {String} the name of the method
    @param args {Object...} optional arguments to pass as well.
    @returns {Array} return values from calling invoke.
  */
  invokeWhile: function(targetValue, methodName) {
    var len = (this.get) ? this.get('length') : this.length ;
    if (len <= 0) return null; // nothing to invoke....

    // collect the arguments
    var args = [] ;
    var alen = arguments.length ;
    if (alen > 2) {
      for(var idx=2;idx<alen;idx++) args.push(arguments[idx]) ;
    }
    
    // call invoke
    var ret = targetValue ;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;(ret === targetValue) && (idx<len);idx++) {
      var next = this.nextObject(idx, last, context) ;
      var method = (next) ? next[methodName] : null ;
      if (method) ret = method.apply(next, args) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  },
  
  /**
    Simply converts the enumerable into a genuine array.  The order, of
    course, is not gauranteed.  Corresponds to the method implemented by 
    Prototype.
        
    @returns {Array} the enumerable as an array.
  */
  toArray: function() {
    var len = (this.get) ? this.get('length') : this.length ;
    if (len <= 0) return [] ; // nothing to invoke....
    
    // call invoke
    var ret = [] ;
    var last = null ;
    var context = SC.Enumerator._popContext();
    for(var idx=0;idx<len;idx++) {
      var next = this.nextObject(idx, last, context) ;
      ret.push(next) ;
      last = next ;
    }
    last = null ;
    context = SC.Enumerator._pushContext(context);
    return ret ;
  }        
} ;

// Build in a separate function to avoid unintential leaks through closures...
SC._buildReducerFor = function(reducerKey, reducerProperty) {
  return function(key, value) {
    var reducer = this[reducerKey] ;
    
    if (SC.typeOf(reducer) !== SC.T_FUNCTION) {
      return (this.unknownProperty) ? this.unknownProperty(key, value) : null;
    } else {
      // Invoke the reduce method defined in enumerable instead of using the
      // one implemented in the receiver.  The receiver might be a native 
      // implementation that does not support reducerProperty.
      var ret = SC.Enumerable.reduce.call(this, reducer, null, reducerProperty) ;
      console.log("RET = %@".fmt(ret)) ;
      return ret ;
    }
  }.property('[]') ;
};

SC.Reducers = {
  /**
    This property will trigger anytime the enumerable's content changes.
    You can observe this property to be notified of changes to the enumerables
    content.
    
    For plain enumerables, this property is read only.  SC.Array overrides
    this method.
  */
  '[]': function(key, value) { return this ; }.property(),

  /**
    Invoke this method when the contents of your enumerable has changed.
    This will notify any observers watching for content changes.
  */
  enumerableContentDidChange: function() {
    this.notifyPropertyChange('[]') ;
    if (this.ownerRecord && this.ownerRecord.recordDidChange) {
      this.ownerRecord.recordDidChange(this) ;
    }
  },
  
  /**
    Call this method from your unknownProperty() handler to implement 
    automatic reduced properties.  A reduced property is a property that 
    collects its contents dynamically from your array contents.  Reduced 
    properties always begin with "@".  Getting this property will call 
    reduce() on your array with the function matching the key name as the
    processor.
    
    The return value of this will be either the return value from the 
    reduced property or undefined, which means this key is not a reduced 
    property.  You can call this at the top of your unknownProperty handler
    like so:
    
    {{{
      unknownProperty: function(key, value) {
        var ret = this.handleReduceProperty(key, value) ;
        if (ret === undefined) {
          // process like normal
        }
      }
    }}}
     
    @param key {String} the reduce property key
    @param value {Object} a value or undefined.
    @param generateProperty {Boolean} only set to false if you do not want an
      optimized computed property handler generated for this.  Not common.
    @returns {Object} the reduced property or undefined
  */
  reducedProperty: function(key, value, generateProperty) {
     
    if (key[0] !== '@') return undefined ; // not a reduced property
    
    // get the reducer key and the reducer
    var matches = key.match(/^@([^(]*)(\(([^)]*)\))?$/) ;
    if (!matches || matches.length < 2) return undefined ; // no match
    
    var reducerKey = matches[1]; // = 'max' if key = '@max(balance)'
    var reducerProperty = matches[3] ; // = 'balance' if key = '@max(balance)'
    var reducerKey = "reduce%@".fmt(reducerKey.capitalize()) ; 
    var reducer = this[reducerKey] ;

    // if there is no reduce function defined for this key, then we can't 
    // build a reducer for it.
    if (SC.typeOf(reducer) !== SC.T_FUNCTION) return undefined;
    
    // if we can't generate the property, just run reduce
    if (generateProperty === NO) {
      return SC.Enumerable.reduce.call(this, reducer, null, reducerProperty) ;
    }

    // ok, found the reducer.  Let's build the computed property and install
    var func = SC._buildReducerFor(reducerKey, reducerProperty);
    var p = this.constructor.prototype ;
    
    if (p) {
      p[key] = func ;
      
      // add the function to the properties array so that new instances
      // will have their dependent key registered.
      var props = p._properties || [] ;
      props.push(key) ;
      p._properties = props ;
      this.registerDependentKey(key, '[]') ;
    }
    
    // and reduce anyway...
    return SC.Enumerable.reduce.call(this, reducer, null, reducerProperty) ;
  },
  
  /** 
    Reducer for @max reduced property.
  */
  reduceMax: function(previousValue, item, index, e, reducerProperty) {
    if (reducerProperty && item) {
      item = (item.get) ? item.get(reducerProperty) : item[reducerProperty];
    }
    if (previousValue == null) return item ;
    return (item > previousValue) ? item : previousValue ;
  },

  /** 
    Reducer for @maxObject reduced property.
  */
  reduceMaxObject: function(previousItem, item, index, e, reducerProperty) {
    
    // get the value for both the previous and current item.  If no
    // reducerProperty was supplied, use the items themselves.
    var previousValue = previousItem, itemValue = item ;
    if (reducerProperty) {
      if (item) {
        itemValue = (item.get) ? item.get(reducerProperty) : item[reducerProperty] ;
      }
      
      if (previousItem) {
        previousItemValue = (previousItem.get) ? previousItem.get(reducerProperty) : previousItem[reducerProperty] ;
      }
    }
    if (previousValue == null) return item ;
    return (itemValue > previousValue) ? item : previousItem ;
  },

  /** 
    Reducer for @min reduced property.
  */
  reduceMin: function(previousValue, item, index, e, reducerProperty) {
    if (reducerProperty && item) {
      item = (item.get) ? item.get(reducerProperty) : item[reducerProperty];
    }
    if (previousValue == null) return item ;
    return (item < previousValue) ? item : previousValue ;
  },

  /** 
    Reducer for @maxObject reduced property.
  */
  reduceMinObject: function(previousItem, item, index, e, reducerProperty) {

    // get the value for both the previous and current item.  If no
    // reducerProperty was supplied, use the items themselves.
    var previousValue = previousItem, itemValue = item ;
    if (reducerProperty) {
      if (item) {
        itemValue = (item.get) ? item.get(reducerProperty) : item[reducerProperty] ;
      }
      
      if (previousItem) {
        previousItemValue = (previousItem.get) ? previousItem.get(reducerProperty) : previousItem[reducerProperty] ;
      }
    }
    if (previousValue == null) return item ;
    return (itemValue < previousValue) ? item : previousItem ;
  },

  /** 
    Reducer for @average reduced property.
  */
  reduceAverage: function(previousValue, item, index, e, reducerProperty) {
    if (reducerProperty && item) {
      item = (item.get) ? item.get(reducerProperty) : item[reducerProperty];
    }
    var ret = (previousValue || 0) + item ;
    var len = (e.get) ? e.get('length') : e.length;
    if (index >= len-1) ret = ret / len; //avg after last item.
    return ret ; 
  },

  /** 
    Reducer for @sum reduced property.
  */
  reduceSum: function(previousValue, item, index, e, reducerProperty) {
    if (reducerProperty && item) {
      item = (item.get) ? item.get(reducerProperty) : item[reducerProperty];
    }
    return (previousValue == null) ? item : previousValue + item ;
  }
} ;

// Apply reducers...
SC.mixin(SC.Enumerable, SC.Reducers) ;
SC.mixin(Array.prototype, SC.Reducers) ;

// ......................................................
// ARRAY SUPPORT
//

// Implement the same enhancements on Array.  We use specialized methods
// because working with arrays are so common.
(function() {
  
  // These methods will be applied even if they already exist b/c we do it
  // better.
  var alwaysMixin = {
    
    // this is supported so you can get an enumerator.  The rest of the
    // methods do not use this just to squeeze every last ounce of perf as
    // possible.
    nextObject: SC.Enumerable.nextObject,
    enumerator: SC.Enumerable.enumerator,

    // see above...
    mapProperty: function(key) {
      var len = this.length ;
      var ret  = [];
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        ret[idx] = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
      }
      return ret ;
    },

    filterProperty: function(key, value) {
      var len = this.length ;
      var ret  = [];
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
        var matched = (value === undefined) ? !!cur : SC.isEqual(cur, value);
        if (matched) ret.push(next) ;
      }
      return ret ;
    },    
    
    find: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      var next, ret = null, found = NO;
      for(var idx=0;idx<len && !found;idx++) {
        next = this[idx] ;
        if(found = callback.call(target, next, idx, this)) ret = next ;
      }
      next = null;
      return ret ;
    },

    findProperty: function(key, value) {
      var len = this.length ;
      var next, cur, found=NO, ret=null;
      for(var idx=0;idx<len && !found;idx++) {
        cur = (next=this[idx]) ? ((next.get) ? next.get(key): next[key]):null;
        found = (value === undefined) ? !!cur : SC.isEqual(cur, value);
        if (found) ret = next ;
      }
      next=null;
      return ret ;
    },    

    everyProperty: function(key, value) {
      var len = this.length ;
      var ret  = YES;
      for(var idx=0;ret && (idx<len);idx++) {
        var next = this[idx] ;
        var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
        ret = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      }
      return ret ;
    },
    
    someProperty: function(key, value) {
      var len = this.length ;
      var ret  = NO;
      for(var idx=0; !ret && (idx<len); idx++) {
        var next = this[idx] ;
        var cur = (next) ? ((next.get) ? next.get(key) : next[key]) : null;
        ret = (value === undefined) ? !!cur : SC.isEqual(cur, value);
      }
      return ret ;  // return the invert
    },
    
    invoke: function(methodName) {
      var len = this.length ;
      if (len <= 0) return [] ; // nothing to invoke....

      // collect the arguments
      var args = [] ;
      var alen = arguments.length ;
      if (alen > 1) {
        for(var idx=1;idx<alen;idx++) args.push(arguments[idx]) ;
      }

      // call invoke
      var ret = [] ;
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        var method = (next) ? next[methodName] : null ;
        if (method) ret[idx] = method.apply(next, args) ;
      }
      return ret ;
    },

    invokeWhile: function(targetValue, methodName) {
      var len = this.length ;
      if (len <= 0) return null ; // nothing to invoke....

      // collect the arguments
      var args = [] ;
      var alen = arguments.length ;
      if (alen > 2) {
        for(var idx=2;idx<alen;idx++) args.push(arguments[idx]) ;
      }

      // call invoke
      var ret = targetValue ;
      for(var idx=0;(ret === targetValue) && (idx<len);idx++) {
        var next = this[idx] ;
        var method = (next) ? next[methodName] : null ;
        if (method) ret = method.apply(next, args) ;
      }
      return ret ;
    },

    toArray: function() {
      var len = this.length ;
      if (len <= 0) return [] ; // nothing to invoke....

      // call invoke
      var ret = [] ;
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        ret.push(next) ;
      }
      return ret ;
    },
    
    getEach: function(key) {
      var ret = [];
      var len = this.length ;
      for(var idx=0;idx<len;idx++) {
        var obj = this[idx];
        ret[idx] = (obj) ? ((obj.get) ? obj.get(key) : obj[key]) : null;
      }
      return ret ;
    },
    
    setEach: function(key, value) {
      var len = this.length;
      for(var idx=0;idx<len;idx++) {
        var obj = this[idx];
        if (obj) {
          if (obj.set) {
            obj.set(key, value);
          } else obj[key] = value ;
        }
      }
      return this ;
    }
    
  }; 
  
  // These methods will only be applied if they are not already defined b/c 
  // the browser is probably getting it.
  var mixinIfMissing = {

    forEach: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        callback.call(target, next, idx, this);
      }
      return this ;
    },

    map: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      var ret  = [];
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        ret[idx] = callback.call(target, next, idx, this) ;
      }
      return ret ;
    },

    filter: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      var ret  = [];
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;
        if(callback.call(target, next, idx, this)) ret.push(next) ;
      }
      return ret ;
    },

    every: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      var ret  = YES;
      for(var idx=0;ret && (idx<len);idx++) {
        var next = this[idx] ;
        if(!callback.call(target, next, idx, this)) ret = NO ;
      }
      return ret ;
    },

    some: function(callback, target) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;
      if (target === undefined) target = null;

      var ret  = NO;
      for(var idx=0;(!ret) && (idx<len);idx++) {
        var next = this[idx] ;
        if(callback.call(target, next, idx, this)) ret = YES ;
      }
      return ret ;
    },

    reduce: function(callback, initialValue, reducerProperty) {
      if (typeof callback !== "function") throw new TypeError() ;
      var len = this.length ;

      // no value to return if no initial value & empty
      if (len===0 && initialValue === undefined) throw new TypeError();

      var ret  = initialValue;
      for(var idx=0;idx<len;idx++) {
        var next = this[idx] ;

        // while ret is still undefined, just set the first value we get as 
        // ret. this is not the ideal behavior actually but it matches the 
        // FireFox implementation... :(
        if (next != null) {
          if (ret === undefined) {
            ret = next ;
          } else {
            ret = callback.call(null, ret, next, idx, this, reducerProperty);
          }
        }
      }

      // uh oh...we never found a value!
      if (ret === undefined) throw new TypeError() ;
      return ret ;
    }   
  };
  
  // Apply methods if missing...
  // Also override prototype for now.  Our methods are functionally identical
  // and don't break the browsers.
  for(var key in mixinIfMissing) {
    if (!mixinIfMissing.hasOwnProperty(key)) continue ;
    if (!Array.prototype[key] || (Prototype && Prototype.Version.match(/^1\.6/))) {
      Array.prototype[key] = mixinIfMissing[key] ;
    }
  }
  
  // Apply other methods...
  SC.mixin(Array.prototype, alwaysMixin) ;
  
})() ;



/* End ------------------------------------------------------- system/mixins/enumerable.js*/

/* Start ----------------------------------------------------- system/set.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

require('system/mixins/enumerable') ;
require('system/mixins/observable') ;

/**
  @class 
  
  An unordered collection of objects.
  
  A Set works a bit like an array except that its items are not ordered.  
  You can create a set to efficiently test for membership for an object. You 
  can also iterate through a set just like an array, even accessing objects
  by index, however there is no gaurantee as to their order.
  
  Note that SC.Set is a primitive object, like an array.  It does implement
  limited key-value observing support but it does not extend from SC.Object
  so you should not subclass it.
  
  h1. Creating a Set
  
  You can create a set like you would most objects using SC.Set.create() or
  new SC.Set().  Most new sets you create will be empty, but you can also
  initialize the set with some content by passing an array or other enumerable
  of objects to the constructor.
  
  Finally, you can pass in an existing set and the set will be copied.  You
  can also create a copy of a set by calling SC.Set#clone().
  
  {{{
    // creates a new empty set
    var foundNames = SC.Set.create();
    
    // creates a set with four names in it.
    var names = SC.Set.create(["Charles", "Peter", "Chris", "Erich"]) ;

    // creates a copy of the names set.
    var namesCopy = SC.Set.create(names);
    
    // same as above.
    var anotherNamesCopy = names.clone();
  }}}
  
  h1. Adding/Removing Objects
  
  You generally add or removed objects from a set using add() or remove().
  You can add any type of object including primitives such as numbers,
  strings, and booleans.
  
  Note that objects can only exist one time in a set.  If you call add() on
  a set with the same object multiple times, the object will only be added 
  once.  Likewise, calling remove() with the same object multiple times will
  remove the object the first time and have no effect on future calls until 
  you add the object to the set again.
  
  Note that you cannot add/remove null or undefined to a set.  Any attempt to
  do so will be ignored.  
  
  In addition to add/remove you can also call push()/pop().  Push behaves just
  like add() but pop(), unlike remove() will pick an arbitrary object, remove
  it and return it.  This is a good way to use a set as a job queue when you
  don't care which order the jobs are executed in.
  
  h1. Testing for an Object
  
  To test for an object's presence in a set you simply call SC.Set#contains().
  This method tests for the object's hash, which is generally the same as the
  object's _guid but if you implement the hash() method on the object, it will
  use the return value from that method instead.
  
  @extends Object
  @extends SC.Enumerable 
  @since SproutCore 0.9.15
*/
SC.Set = function(items) {
  if (items && items.length > 0) {
    var idx = (items.get) ? items.get('length') : items.length ;
    if (items.objectAt) {
      while(--idx >= 0) this.add(items.objectAt(idx)) ;
    } else {
      while(--idx >= 0) this.add(items[idx]) ;
    }
  }
  return this ;
} ;

SC.Set.prototype = {
    
  /**
    This property will change as the number of objects in the set changes.

    @type number
  */
  length: 0,
  
  /**
    Clears the set 
  */
  clear: function() { this.length = 0; },
  
  /**
    Call this method to test for membership.
  */
  contains: function(obj) {
    
    // because of the way a set is "reset", the guid for an object may 
    // still be stored as a key, but points to an index that is beyond the
    // length.  Therefore the found idx must both be defined and less than
    // the current length.
    if (obj === null) return NO ;
    var idx = this[SC.hashFor(obj)] ;
    return ((idx != null) && (idx < this.length)) ;
  },
  
  /**
    Call this method to add an object. performs a basic add.
    
    If the object is already in the set it will not be added again.
    
    @param obj {Object} the object to add
    @returns {Object} this
  */
  add: function(obj) {
    if (obj == null) return this; // cannot add null to a set.
    
    var guid = SC.hashFor(obj) ;
    var idx = this[guid] ;
    var len = this.length ;
    if ((idx == null) || (idx >= len)) {
      this[len] = obj ;
      this[guid] = len ;
      this.length = len+1;
    }
    return this ;
  },
  
  /**
    Add all the items in the passed array.
  */
  addEach: function(objects) {
    var idx = objects.length ;
    while(--idx >= 0) this.add(objects[idx]) ;
  },  
  
  /**
    Removes the object from the set if it is found.
    
    If the object is not in the set, nothing will be changed.
    
    @param obj {Object} the object to remove
    @returns {this} this
  */  
  remove: function(obj) {
    
    if (obj == null) return this ;
    var guid = SC.hashFor(obj);
    var idx = this[guid] ;
    var len = this.length;
    
    if ((idx == null) || (idx >= len)) return this; // not in set.

    // clear the guid key
    delete this[guid] ;
    
    // to clear the index, we will swap the object stored in the last index.
    // if this is the last object, just reduce the length.
    if (idx < (len-1)) {
      var obj = this[idx] = this[len-1];
      this[SC.hashFor(obj)] = idx ;
    }
    
    // reduce the length
    this.length = len-1;
    return this ;
  },

  /**
    Removes an arbitrary object from the set and returns it.
    
    @returns {Object} an object from the set or null
  */
  pop: function() {
    var obj = (this.length > 0) ? this[this.length-1] : null ;
    if (obj) this.remove(obj) ;
    return obj ;
  },
  
  /**
    Removes all the items in the passed array.
  */
  removeEach: function(objects) {
    var idx = objects.length ;
    while(--idx >= 0) this.remove(objects[idx]) ;
  },  

  /**
   Clones the set into a new set.  
  */
  clone: function() {
    return SC.Set.create(this)    
  },

  // .......................................
  // PRIVATE 
  _each: function(iterator) {
    var len = this.length ;
    for(var idx=0;idx<len;idx++) iterator(this[idx]) ;
  },
  
  toString: function() {
    return "SC.Set<%@>".fmt(SC.$A(this)) ;
  }  
  
} ;

SC.Set.prototype.slice = SC.Set.prototype.clone ;

// Make this enumerable and observable
SC.mixin(SC.Set.prototype, SC.Enumerable, SC.Observable) ;

SC.Set.prototype.push = SC.Set.prototype.unshift = SC.Set.prototype.add ;
SC.Set.prototype.shift = SC.Set.prototype.pop ;


/**
  To create a set, pass an array of items instead of a hash.
*/
SC.Set.create = function(items) { return new SC.Set(items); };


/* End ------------------------------------------------------- system/set.js*/

/* Start ----------------------------------------------------- private/observer_set.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

// ........................................................................
// ObserverSet
//
// This private class is used to store information about obversers on a 
// particular key.  Note that this object is not observable.  You create new
// instances by calling SC.beget(SC._ObserverSet) ;
//
SC._ObserverSet = {

  // the number of targets in the set.
  targets: 0,
  
  _membersCacheIsValid: NO,
  
  // adds the named target/method observer to the set.  The method must be
  // a function, not a string..
  add: function(target, method) {
    var targetGuid = (target) ? SC.guidFor(target) : "__this__";
    
    // get the set of methods
    var methods = this[targetGuid] ;
    if (!methods) {
      methods = this[targetGuid] = SC.Set.create() ;
      methods.target = target ;
      methods.isTargetSet = YES ; // used for getMembers().
      this.targets++ ;
    }
    
    methods.add(method) ;
    this._membersCacheIsValid = NO ;
  },
  
  // removes the named target/method observer from the set.  If this is the
  // last method for the named target, then the number of targets will also
  // be reduced.
  //
  // returns YES if the items was removed, NO if it was not found.
  remove: function(target, method) {
    var targetGuid = (target) ? SC.guidFor(target) : "__this__";
    
    // get the set of methods
    var methods = this[targetGuid] ;    
    if (!methods) return NO ;
    
    methods.remove(method) ;
    if (methods.length <= 0) {
      methods.target = null;
      methods.isTargetSet = NO ;
      delete this[targetGuid] ;
      this.targets-- ;
    }
    
    this._membersCacheIsValid = NO;
    
    return YES ;
  },
  
  // Invokes the target/method pairs in the receiver.  Used by SC.RunLoop
  invokeMethods: function() {
    // iterate through the set, look for sets.
    for(var key in this) {
      if (!this.hasOwnProperty(key)) continue ;
      var value = this[key] ;
      if (value && value.isTargetSet) {
        var idx = value.length;
        var target = value.target ;
        while(--idx>=0) value[idx].call(target);
      }
    }
  },
  
  // Returns an array of target/method pairs.  This is cached.
  getMembers: function() {
    if (this._membersCacheIsValid) return this._members ;
    
    // need to recache, reset the array...
    if (!this._members) {
      this._members = [] ;
    } else this._members.length = 0 ; // reset
    var ret = this._members ;

    // iterate through the set, look for sets.
    for(var key in this) {
      if (!this.hasOwnProperty(key)) continue ;
      var value = this[key] ;
      if (value && value.isTargetSet) {
        var idx = value.length;
        var target = value.target ;
        while(--idx>=0) ret.push([target, value[idx]]) ;
      }
    }

    this._membersCacheIsValid = YES ;
    return ret ;
  },
  
  // Returns a new instance of the set with the contents cloned.
  clone: function() {
    var oldSet, newSet, key, ret = SC._ObserverSet.create() ;
    for(key in this) {
      if (!this.hasOwnProperty(key)) continue ;
      oldSet = this[key];
      if (oldSet && oldSet.isTargetSet) {
        newSet = oldSet.clone();
        newSet.target = oldSet.target ;
        ret[key] = newSet ;
      }
    }
    ret.targets = this.targets ;
    ret._membersCacheIsValid = NO ;
    return ret ;
  },
  
  // Creates a new instance of the observer set.
  create: function() { return SC.beget(this); }
  
} ;

SC._ObserverSet.slice = SC._ObserverSet.clone ;




/* End ------------------------------------------------------- private/observer_set.js*/

/* Start ----------------------------------------------------- system/mixins/observable.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/set');
require('private/observer_set') ;

/*globals logChange */

/**
  @namespace 
  
  Key-Value-Observing (KVO) simply allows one object to observe changes to a 
  property on another object. It is one of the fundamental ways that models, 
  controllers and views communicate with each other in a SproutCore 
  application.  Any object that has this module applied to it can be used in 
  KVO-operations.
  
  This module is applied automatically to all objects that inherit from
  SC.Object, which includes most objects bundled with the SproutCore 
  framework.  You will not generally apply this module to classes yourself,
  but you will use the features provided by this module frequently, so it is
  important to understand how to use it.
  
  h2. Enabling Key Value Observing

  With KVO, you can write functions that will be called automatically whenever 
  a property on a particular object changes.  You can use this feature to
  reduce the amount of "glue code" that you often write to tie the various 
  parts of your application together.
  
  To use KVO, just use the KVO-aware methods get() and set() to access 
  properties instead of accessing properties directly.  Instead of writing:
  
  {{{
    var aName = contact.firstName ;
    contact.firstName = 'Charles' ;
  }}}

  use:

  {{{
    var aName = contact.get('firstName') ;
    contact.set('firstName', 'Charles') ;
  }}}
  
  get() and set() work just like the normal "dot operators" provided by 
  JavaScript but they provide you with much more power, including not only
  observing but computed properties as well.

  h2. Observing Property Changes

  You typically observe property changes simply by adding the observes() 
  call to the end of your method declarations in classes that you write.  For
  example:
  
  {{{
    SC.Object.create({
      valueObserver: function() {
        // Executes whenever the "Value" property changes
      }.observes('value')
    }) ;
  }}}
  
  Although this is the most common way to add an observer, this capability is
  actually built into the SC.Object class on top of two methods defined in
  this mixin called addObserver() and removeObserver().  You can use these two
  methods to add and remove observers yourself if you need to do so at run 
  time.  
  
  To add an observer for a property, just call:
  
  {{{
    object.addObserver('propertyKey', targetObject, targetAction) ;
  }}}
  
  This will call the 'targetAction' method on the targetObject to be called
  whenever the value of the propertyKey changes.
  
  h2. Observer Parameters
  
  An observer function typically does not need to accept any parameters, 
  however you can accept certain arguments when writing generic observers. 
  An observer function can have the following arguments:
  
  {{{
    propertyObserver(target, key, value, revision) ;
  }}}
  
  - *target* - This is the object whose value changed.  Usually this.
  - *key* - The key of the value that changed
  - *value* - this property is no longer used.  It will always be null
  - *revision* - this is the revision of the target object
  
  h2. Implementing Manual Change Notifications
  
  Sometimes you may want to control the rate at which notifications for 
  a property are delivered, for example by checking first to make sure 
  that the value has changed.
  
  To do this, you need to implement a computed property for the property 
  you want to change and override automaticallyNotifiesObserversFor().
  
  The example below will only notify if the "balance" property value actually
  changes:
  
  {{{
    
    automaticallyNotifiesObserversFor: function(key) {
      return (key === 'balance') ? NO : arguments.callee.base.apply(this,arguments) ;
    },
    
    balance: function(key, value) {
      var balance = this._balance ;
      if ((value !== undefined) && (balance !== value)) {
        this.propertyWillChange(key) ;
        balance = this._balance = value ;
        this.propertyDidChange(key) ;
      }
      return balance ;
    }
    
  }}}
  
  h1. Implementation Details
  
  Internally, SproutCore keeps track of observable information by adding a
  number of properties to the object adopting the observable.  All of these
  properties begin with "_kvo_" to separate them from the rest of your object.
  
  @static
  @since SproutCore 1.0
*/
SC.Observable = {

  /**
    Determines whether observers should be automatically notified of changes
    to a key.
    
    If you are manually implementing change notifications for a property, you
    can override this method to return NO for properties you do not want the
    observing system to automatically notify for.
    
    The default implementation always returns YES.
    
    @param key {String} the key that is changing
    @returns {Boolean} YES if automatic notification should occur.
  */
  automaticallyNotifiesObserversFor: function(key) { 
    return YES;
  },

  // ..........................................
  // PROPERTIES
  // 
  // Use these methods to get/set properties.  This will handle observing
  // notifications as well as allowing you to define functions that can be 
  // used as properties.

  /**  
    Retrieves the value of key from the object.
    
    This method is generally very similar to using object[key] or object.key,
    however it supports both computed properties and the unknownProperty
    handler.
    
    *Computed Properties*
    
    Computed properties are methods defined with the property() modifier
    declared at the end, such as:
    
    {{{
      fullName: function() {
        return this.getEach('firstName', 'lastName').compact().join(' ');
      }.property('firstName', 'lastName')
    }}}
    
    When you call get() on a computed property, the property function will be
    called and the return value will be returned instead of the function
    itself.
    
    *Unknown Properties*
    
    Likewise, if you try to call get() on a property whose values is
    undefined, the unknownProperty() method will be called on the object.
    If this method reutrns any value other than undefined, it will be returned
    instead.  This allows you to implement "virtual" properties that are 
    not defined upfront.
    
    @param key {String} the property to retrieve
    @returns {Object} the property value or undefined.
    
  */
  get: function(key) {
    var ret = this[key] ;
    if (ret === undefined) {
      return this.unknownProperty(key) ;
    } else if (ret && ret.isProperty) {
      if (ret.isCacheable) {
        return (this[ret.cacheKey] !== undefined) ? this[ret.cacheKey] : (this[ret.cacheKey] = ret.call(this,key)) ;
      } else return ret.call(this,key);
    } else return ret ;
  },

  /**  
    Sets the key equal to value.
    
    This method is generally very similar to calling object[key] = value or
    object.key = value, except that it provides support for computed 
    properties, the unknownProperty() method and property observers.
    
    *Computed Properties*
    
    If you try to set a value on a key that has a computed property handler
    defined (see the get() method for an example), then set() will call
    that method, passing both the value and key instead of simply changing 
    the value itself.  This is useful for those times when you need to 
    implement a property that is composed of one or more member
    properties.
    
    *Unknown Properties*
    
    If you try to set a value on a key that is undefined in the target 
    object, then the unknownProperty() handler will be called instead.  This
    gives you an opportunity to implement complex "virtual" properties that
    are not predefined on the obejct.  If unknownProperty() returns 
    undefined, then set() will simply set the value on the object.
    
    *Property Observers*
    
    In addition to changing the property, set() will also register a 
    property change with the object.  Unless you have placed this call 
    inside of a beginPropertyChanges() and endPropertyChanges(), any "local"
    observers (i.e. observer methods declared on the same object), will be
    called immediately.  Any "remote" observers (i.e. observer methods 
    declared on another object) will be placed in a queue and called at a
    later time in a coelesced manner.
    
    *Chaining*
    
    In addition to property changes, set() returns the value of the object
    itself so you can do chaining like this:
    
    {{{
      record.set('firstName', 'Charles').set('lastName', 'Jolley');
    }}}
    
    @param key {String} the property to set
    @param value {Object} the value to set or null.
    @returns {this}
  */
  set: function(key, value) {
    var func = this[key], ret = value, dependents ;
    
    var notify = this.automaticallyNotifiesObserversFor(key) ;
    
    // set the value.
    if (func && func.isProperty) {
      if (func.isVolatile || (this[func.lastSetValueKey] !== value)) {
        this[func.lastSetValueKey] = value ;
        if (notify) this.propertyWillChange(key) ;
        ret = func.call(this,key,value) ;

        // update cached value
        if (func.isCacheable) this[func.cacheKey] = ret ;

        if (notify) this.propertyDidChange(key, ret) ;
        
      }

    } else if (func === undefined) {
      if (notify) this.propertyWillChange(key) ;
      this.unknownProperty(key,value) ;
      if (notify) this.propertyDidChange(key, ret) ;

    } else {
      if (this[key] !== value) {
        if (notify) this.propertyWillChange(key) ;
        ret = this[key] = value ;
        if (notify) this.propertyDidChange(key, ret) ;
      }
    }
    
    // if there are any dependent keys and they use caching, then clear the
    // cache.
    if (dependents = this._kvo_cachedDependents) {
      dependents = this._kvo_cachedDependents[key] ;
      if (dependents && dependents.length > 0) {
        var idx = dependents.length;
        while(--idx>=0) {
          func = dependents[idx];
          this[func.cacheKey] = this[func.lastSetValueKey] = undefined;
        }
      }
    }
    
    return this ;
  },

  /**  
    Called whenever you try to get or set an undefined property.
    
    This is a generic property handler.  If you define it, it will be called
    when the named property is not yet set in the object.  The default does
    nothing.
    
    @param key {String} the key that was requested
    @param value {Object} The value if called as a setter, undefined if called as a getter.
    @returns {Object} The new value for key.
  */
  unknownProperty: function(key,value) {
    if (!(value === undefined)) { this[key] = value; }
    return value ;
  },

  /**  
    Begins a grouping of property changes.
    
    You can use this method to group property changes so that notifications
    will not be sent until the changes are finished.  If you plan to make a 
    large number of changes to an object at one time, you should call this 
    method at the beginning of the changes to suspend change notifications.
    When you are done making changes, all endPropertyChanges() to allow 
    notification to resume.
    
    @returns {this}
  */
  beginPropertyChanges: function() {
    this._kvo_changeLevel = (this._kvo_changeLevel || 0) + 1; 
    return this;
  },

  /**  
    Ends a grouping of property changes.
    
    You can use this method to group property changes so that notifications
    will not be sent until the changes are finished.  If you plan to make a 
    large number of changes to an object at one time, you should call 
    beginPropertyChanges() at the beginning of the changes to suspend change 
    notifications. When you are done making changes, call this method to allow 
    notification to resume.
    
    @returns {this}
  */
  endPropertyChanges: function() {
    this._kvo_changeLevel = (this._kvo_changeLevel || 1) - 1 ;
    var level = this._kvo_changeLevel;
    if ((level<=0) && this._kvo_changes && (this._kvo_changes.length>0)) {
      this._notifyPropertyObservers() ;
    } 
    return this ;
  },

  /**  
    Notify the observer system that a property is about to change.

    Sometimes you need to change a value directly or indirectly without 
    actually calling get() or set() on it.  In this case, you can use this 
    method and propertyDidChange() instead.  Calling these two methods 
    together will notify all observers that the property has potentially 
    changed value.
    
    Note that you must always call propertyWillChange and propertyDidChange as 
    a pair.  If you do not, it may get the property change groups out of order 
    and cause notifications to be delivered more often than you would like.
    
    @param key {String} The property key that is about to change.
    @returns {this}
  */
  propertyWillChange: function(key) {
    return this ;
  },

  /**  
    Notify the observer system that a property has just changed.

    Sometimes you need to change a value directly or indirectly without 
    actually calling get() or set() on it.  In this case, you can use this 
    method and propertyWillChange() instead.  Calling these two methods 
    together will notify all observers that the property has potentially 
    changed value.
    
    Note that you must always call propertyWillChange and propertyDidChange as 
    a pair. If you do not, it may get the property change groups out of order 
    and cause notifications to be delivered more often than you would like.
    
    @param key {String} The property key that has just changed.
    @param value {Object} The new value of the key.  May be null.
    @returns {this}
  */
  propertyDidChange: function(key,value) {

    this._kvo_revision = (this._kvo_revision || 0) + 1; 
    var level = this._kvo_changeLevel || 0 ;

    // clear any cached value
    var func = this[key] ;
    if (func && (func instanceof Function) && func.isCacheable) {
      this[func.cacheKey] = this[func.lastSetValueKey] = undefined ;
    }
    
    // save in the change set if queuing changes
    var suspended ;
    if ((level > 0) || (suspended=SC.Observers.isObserveringSuspended)) {
      var changes = this._kvo_changes ;
      if (!changes) changes = this._kvo_changes = SC.Set.create() ;
      changes.add(key) ;
      
      if (suspended) SC.Observers.objectHasPendingChanges(this) ;
      
    // otherwise notify property observers immediately
    } else this._notifyPropertyObservers(key) ;
    
    return this ;
  },

  // ..........................................
  // DEPENDENT KEYS
  // 

  /**
    Use this to indicate that one key changes if other keys it depends on 
    change.
    
    You generally do not call this method, but instead pass dependent keys to
    your property() method when you declare a computed property.
    
    You can call this method during your init to register the keys that should
    trigger a change notification for your computed properties.  
    
    @param key {String} the dependent key followed by any keys the key depends on.
    @returns {Object} this
  */  
  registerDependentKey: function(key) {
    var idx = arguments.length ;
    var dependents = this._kvo_dependents ;
    if (!dependents) this._kvo_dependents = dependents = {} ;

    // the cached dependents hash contains computed properties that are 
    // dependent and cached.  It is important not to define 
    // _kvo_cachedDependents until this feature is actually used for perf
    // reasons.
    var cached = this._kvo_cachedDependents ;
    var dep, func, array, arrayIdx, queue;
    
    // note that we store dependents as simple arrays instead of using set.
    // we assume that in general you won't call registerDependentKey() more
    // than once for a particular base key.  Even if you do, the added cost
    // of having dups is minor.
    
    // for each key, build array of dependents, add this key...
    // note that we ignore the first argument since it is the key...
    while(--idx >= 1) {
      dep = arguments[idx] ;
      
      // handle the case where the user passes arrays of keys...
      if (SC.$type(dep) === SC.T_ARRAY) {
        array = dep ;  arrayIdx = array.length;
        while(--arrayIdx >= 0) {
          dep = array[arrayIdx] ;
          
          // add to dependents
          queue = dependents[dep] ;
          if (!queue) queue = dependents[dep] = [] ;
          queue.push(key) ;

          // add function 
          func = this[key];
          if (func && (func instanceof Function) && func.isCacheable) {
            if (!cached) this._kvo_cachedDependents = cached = {};
            queue = cached[dep] ;
            if (!queue) queue = cached[dep] = [] ;
            queue.push(func) ;
          }
        }
        
      // otherwise, just add the key.
      } else {
        queue = dependents[dep] ;
        if (!queue) queue = dependents[dep] = [] ;
        queue.push(key) ;
          
        // add to cached dependents if needed
        func = this[key];
        if (func && (func instanceof Function) && func.isCacheable) {
          if (!cached) this._kvo_cachedDependents = cached = {};
          queue = cached[dep] ;
          if (!queue) queue = cached[dep] = [] ;
          queue.push(func) ;
        }
      }
    }
  },
  
  // ..........................................
  // OBSERVERS
  // 
  
  _kvo_for: function(kvoKey, type) {
    var ret = this[kvoKey] ;

    if (!this._kvo_cloned) this._kvo_cloned = {} ;
    
    // if the item does not exist, create it.  Unless type is passed, 
    // assume array.
    if (!ret) {
      ret = this[kvoKey] = (type === undefined) ? [] : type.create();
      this._kvo_cloned[kvoKey] = YES ;
      
    // if item does exist but has not been cloned, then clone it.  Note
    // that all types must implement slice().0
    } else if (!this._kvo_cloned[kvoKey]) {
      ret = this[kvoKey] = ret.slice();
      this._kvo_cloned[kvoKey] = YES; 
    }
    
    return ret ;
  },

  /**  
    Adds an observer on a property.
    
    This is the core method used to register an observer for a property.
    
    Once you call this method, anytime the key's value is set, your observer
    will be notified.  Note that the observers are triggered anytime the
    value is set, regardless of whether it has actually changed.  Your
    observer should be prepared to handle that.
    
    @param key {String} the key to observer
    @param target {Object} the target object to invoke
    @param method {String|Function} the method to invoke.
    @returns {SC.Object} self
  */
  addObserver: function(key,target,method) {
    
    var kvoKey, chain, chains, observers;
    
    // normalize.  if a function is passed to target, make it the method.
    if (method === undefined) {
      method = target; target = this ;
    }
    if (!target) target = this ;
    if (SC.$type(method) === SC.T_STRING) method = target[method] ;
    if (!method) throw "You must pass a method to addObserver()" ;

    // Normalize key...
    key = key.toString() ;
    if (key.indexOf('.') >= 0) {
      
      // create the chain and save it for later so we can tear it down if 
      // needed.
      chain = SC._ChainObserver.createChain(this, key, target, method);
      chain.masterTarget = target;  chain.masterMethod = method ;
      
      // Save in set for chain observers.
      this._kvo_for(SC.keyFor('_kvo_chains', key)).push(chain);
      
    // Create observers if needed...
    } else {
      
      // Special case to support reduced properties.  If the property 
      // key begins with '@' and its value is unknown, then try to get its
      // value.  This will configure the dependent keys if needed.
      if ((this[key] === undefined) && (key.indexOf('@') === 0)) {
        this.get(key) ;
      }

      if (target === this) target = null ; // use null for observers only.
      kvoKey = SC.keyFor('_kvo_observers', key);
      this._kvo_for(kvoKey, SC._ObserverSet).add(target, method);
      this._kvo_for('_kvo_observed_keys', SC.Set).add(key) ;
    }
    
    return this;
  },

  removeObserver: function(key, target, method) {
    
    var kvoKey, chains, chain, observers, idx ;
    
    // normalize.  if a function is passed to target, make it the method.
    if (method === undefined) {
      method = target; target = this ;
    }
    if (!target) target = this ;
    if (SC.$type(method) === SC.T_STRING) method = target[method] ;
    if (!method) throw "You must pass a method to addObserver()" ;

    // if the key contains a '.', this is a chained observer.
    key = key.toString() ;
    if (key.indexOf('.') >= 0) {
      
      // try to find matching chains
      kvoKey = SC.keyFor('_kvo_chains', key);
      if (chains = this[kvoKey]) {
        
        // if chains have not been cloned yet, do so now.
        chains = this._kvo_for(kvoKey) ;
        
        // remove any chains
        idx = chains.length;
        while(--idx >= 0) {
          chain = chains[idx];
          if (chain && (chain.masterTarget===target) && (chain.masterMethod===method)) {
            chains[idx] = chain.destroyChain() ;
          }
        }
      }
      
    // otherwise, just like a normal observer.
    } else {
      if (target === this) target = null ; // use null for observers only.
      kvoKey = SC.keyFor('_kvo_observers', key) ;
      if (observers = this[kvoKey]) {
        // if observers have not been cloned yet, do so now
        observers = this._kvo_for(kvoKey) ;
        observers.remove(target, method) ;
        if (observers.targets <= 0) {
          this._kvo_for('_kvo_observed_keys', SC.Set).remove(key);
        }
      }
    }
    
    return this;
  },
  

  /**
    This method will register any observers and computed properties saved on
    the object.  Normally you do not need to call this method youself.  It
    is invoked automatically just before property notifications are sent and
    from the init() method of SC.Object.  You may choose to call this
    from your own initialization method if you are using SC.Observable in
    a non-SC.Object-based object.
    
    This method looks for several private variables, which you can setup,
    to initialize:
    
      - _observers: this should contain an array of key names for observers
        you need to configure.
        
      - _bindings: this should contain an array of key names that configure
        bindings.
        
      - _properties: this should contain an array of key names for computed
        properties.
        
    @returns {Object} this
  */
  initObservable: function() {
    if (this._observableInited) return ;
    this._observableInited = YES ;
    
    var loc, keys, key, value, observer, propertyPaths, propertyPathsLength ;
    
    // Loop through observer functions and register them
    if (keys = this._observers) {
      var len = keys.length ;
      for(loc=0;loc<len;loc++) {
        key = keys[loc]; observer = this[key] ;
        propertyPaths = observer.propertyPaths ;
        propertyPathsLength = (propertyPaths) ? propertyPaths.length : 0 ;
        for(var ploc=0;ploc<propertyPathsLength;ploc++) {
          var path = propertyPaths[ploc] ;
          var dotIndex = path.indexOf('.') ;
          // handle most common case, observing a local property
          if (dotIndex < 0) {
            this.addObserver(path, this, observer) ;

          // next most common case, use a chained observer
          } else if (path.indexOf('*') === 0) {
            this.addObserver(path.slice(1), this, observer) ;
            
          // otherwise register the observer in the observers queue.  This 
          // will add the observer now or later when the named path becomes
          // available.
          } else {
            var root = null ;
            
            // handle special cases for observers that look to the local root
            if (dotIndex === 0) {
              root = this; path = path.slice(1) ;
            } else if (dotIndex===4 && path.slice(0,5) === 'this.') {
              root = this; path = path.slice(5) ;
            } else if (dotIndex<0 && path.length===4 && path === 'this') {
              root = this; path = '';
            }
            
            SC.Observers.addObserver(path, this, observer, root); 
          }
        }
      }
    }

    // Add Bindings
    this.bindings = []; // will be filled in by the bind() method.
    if (keys = this._bindings) {
      for(loc=0;loc<keys.length;loc++) {
        // get propertyKey
        key = keys[loc] ; value = this[key] ;
        var propertyKey = key.slice(0,-7) ; // contentBinding => content
        this[key] = this.bind(propertyKey, value) ;
      }
    }

    // Add Properties
    if (keys = this._properties) {
      for(loc=0;loc<keys.length;loc++) {
        key = keys[loc] ; value = this[key] ;
        if (value && value.dependentKeys && (value.dependentKeys.length>0)) {
          var args = value.dependentKeys.slice() ;
          args.unshift(key) ;
          this.registerDependentKey.apply(this,args) ;
        }
      }
    }
    
  },
  
  // ..........................................
  // NOTIFICATION
  // 

  /**
    Returns an array with all of the observers registered for the specified
    key.  This is intended for debugging purposes only.  You generally do not
    want to rely on this method for production code.
    
    @params key {String} the key to evaluate
    @returns {Array} array of Observer objects, describing the observer.
  */
  observersForKey: function(key) {
    var observers = this._kvo_for('_kvo_observers', key) ;
    return observers.getMembers() || [] ;
  },
  
  // this private method actually notifies the observers for any keys in the
  // observer queue.  If you pass a key it will be added to the queue.
  _notifyPropertyObservers: function(key) {

    if (!this._observableInited) this.initObservable() ;
    
    SC.Observers.flush() ; // hookup as many observers as possible.

    var observers, changes, dependents, starObservers, idx, keys, rev ;
    var members, membersLength, member, memberLoc, target, method, loc, func ;

    // Get any starObservers -- they will be notified of all changes.
    starObservers =  this['_kvo_observers_*'] ;
    
    // prevent notifications from being sent until complete
    this._kvo_changeLevel = (this._kvo_changeLevel || 0) + 1; 

    // keep sending notifications as long as there are changes
    while(((changes = this._kvo_changes) && (changes.length > 0)) || key) {
      
      // increment revision
      rev = this.propertyRevision++;
      
      // save the current set of changes and swap out the kvo_changes so that
      // any set() calls by observers will be saved in a new set.
      if (!changes) changes = SC.Set.create() ;
      this._kvo_changes = this._kvo_altChanges ;
      this._kvo_altChanges = null ; 

      // Add the passed key to the changes set.  If a '*' was passed, then
      // add all keys in the observers to the set...
      // once finished, clear the key so the loop will end.
      if (key === '*') {
        changes.add('*') ;
        changes.addEach(this._kvo_for('_kvo_observed_keys', SC.Set));

      } else if (key) changes.add(key) ;

      // Now go through the set and add all dependent keys...
      if (dependents = this._kvo_dependents) {

        // NOTE: each time we loop, we check the changes length, this
        // way any dependent keys added to the set will also be evaluated...
        for(idx=0;idx<changes.length;idx++) {
          key = changes[idx] ;
          keys = dependents[key] ;
          
          // for each dependent key, add to set of changes.  Also, if key
          // value is a cacheable property, clear the cached value...
          if (keys && (loc = keys.length)) {
            while(--loc >= 0) {
              changes.add(key = keys[loc]);
              if ((func = this[key]) && func.isCacheable) {
                this[func.cacheKey] = undefined;
              } // if (func=)
            } // while (--loc)
          } // if (keys && 
        } // for(idx...
      } // if (dependents...)

      // now iterate through all changed keys and notify observers.
      while(changes.length > 0) {
        key = changes.pop() ; // the changed key

        // find any observers and notify them...
        observers = this[SC.keyFor('_kvo_observers', key)];
        if (observers) {
          members = observers.getMembers() ;
          membersLength = members.length ;
          for(memberLoc=0;memberLoc < membersLength; memberLoc++) {
            member = members[memberLoc] ;
            if (member[2] === rev) continue ; // skip notified items.
            target = member[0] || this; method = member[1] ; member[2] = rev;
            method.call(target, this, key, null, rev) ;
          }
        }

        // look for local observers.  Local observers are added by SC.Object
        // as an optimization to avoid having to add observers for every 
        // instance when you are just observing your local object.
        members = this[SC.keyFor('_kvo_local', key)];
        if (members) {
          membersLength = members.length ;
          for(memberLoc=0;memberLoc<membersLength;memberLoc++) {
            member = members[memberLoc];
            method = this[member] ; // try to find observer function
            if (method) method.call(this, this, key, null, rev);
          }
        }
        
        // if there are starObservers, do the same thing for them
        if (starObservers && key !== '*') {          
          members = starObservers.getMembers() ;
          membersLength = members.length ;
          for(memberLoc=0;memberLoc < membersLength; memberLoc++) {
            member = members[memberLoc] ;
            target = member[0] || this; method = member[1] ;
            method.call(target, this, key, null, rev) ;
          }
        }

        // if there is a default property observer, call that also
        if (this.propertyObserver) {
          this.propertyObserver(this, key, null, rev);
        }
      } // while(changes.length>0)

      // changes set should be empty. save this set so it can be reused later
      this._kvo_altChanges = changes ;
      
      // key is no longer needed; clear it to avoid infinite loops
      key = null ; 
      
    } // while (changes)
    
    // done with loop, reduce change level so that future sets can resume
    this._kvo_changeLevel = (this._kvo_changeLevel || 1) - 1; 
    return YES ; // finished successfully
  },

  // ..........................................
  // BINDINGS
  // 
    
  /**  
    Manually add a new binding to an object.  This is the same as doing
    the more familiar propertyBinding: 'property.path' approach.
  */
  bind: function(toKey, fromPropertyPath) {

    var binding ;

    // if a string or array (i.e. tuple) is passed, convert this into a
    // binding.  If a binding default was provided, use that.
    var pathType = SC.$type(fromPropertyPath) ;
    if (pathType === SC.T_STRING || pathType === SC.T_ARRAY) {
      binding = this[toKey + 'BindingDefault'] || SC.Binding;
      binding = binding.beget().from(fromPropertyPath) ;
    } else binding = fromPropertyPath ;

    // finish configuring the binding and then connect it.
    binding = binding.to(toKey, this).connect() ;
    this.bindings.push(binding) ;
    
    return binding ;
  },
  
  /**  
    didChangeFor makes it easy for you to verify that you haven't seen any
    changed values.  You need to use this if your method observes multiple
    properties.  To use this, call it like this:
  
    if (this.didChangeFor('render','height','width')) {
       // DO SOMETHING HERE IF CHANGED.
    }
  */  
  didChangeFor: function(context) { 
    
    context = SC.hashFor(context) ; // get a hash key we can use in caches.
    
    // setup caches...
    var valueCache = this._kvo_didChange_valueCache ;
    if (!valueCache) valueCache = this._kvo_didChange_valueCache = {};
    var revisionCache = this._kvo_didChange_revisionCache;
    if (!revisionCache) revisionCache=this._kvo_didChange_revisionCache={};

    // get the cache of values and revisions already seen in this context
    var seenValues = valueCache[context] || {} ;
    var seenRevisions = revisionCache[context] || {} ;
    
    // prepare too loop!
    var ret = false ;
    var currentRevision = this._kvo_revision || 0  ;
    var idx = arguments.length ;
    while(--idx >= 1) {  // NB: loop only to 1 to ignore context arg.
      var key = arguments[idx];
      
      // has the kvo revision changed since the last time we did this?
      if (seenRevisions[key] != currentRevision) {
        // yes, check the value with the last seen value
        var value = this.get(key) ;
        if (seenValues[key] !== value) ret = true ; // did change!
      }
      seenRevisions[key] = currentRevision;
    }
    
    valueCache[context] = seenValues ;
    revisionCache[context] = seenRevisions ;
    return ret ;
  },



  /**
    Sets the property only if the passed value is different from the
    current value.  Depending on how expensive a get() is on this property,
    this may be more efficient.
    
    @param key {String} the key to change
    @param value {Object} the value to change
    @returns {this}
  */
  setIfChanged: function(key, value) {
    return (this.get(key) !== value) ? this.set(key, value) : this ;
  },
  
  /**  
    Navigates the property path, returning the value at that point.
    
    If any object in the path is undefined, returns undefined.
  */
  getPath: function(path) {
    var tuple = SC.tupleForPropertyPath(path, this) ;
    if (tuple === null || tuple[0] === null) return undefined ;
    return tuple[0].get(tuple[1]) ;
  },
  
  /**
    Navigates the property path, finally setting the value.
    
    @param path {String} the property path to set
    @param value {Object} the value to set
    @returns {this}
  */
  setPath: function(path, value) {
    if (path.indexOf('.') >= 0) {
      var tuple = SC.tupleForPropertyPath(path, this) ;
      if (!tuple[0]) return null ;
      tuple[0].set(tuple[1], value) ;
    } else this.set(path, value) ; // shortcut
    return this;
  },

  /**
    Navigates the property path, finally setting the value but only if 
    the value does not match the current value.  This will avoid sending
    unecessary change notifications.
    
    @param path {String} the property path to set
    @param value {Object} the value to set
    @returns {Object} this
  */
  setPathIfChanged: function(path, value) {
    if (path.indexOf('.') >= 0) {
      var tuple = SC.tupleForPropertyPath(path, this) ;
      if (!tuple[0]) return null ;
      if (tuple[0].get(tuple[1]) !== value) {
        tuple[0].set(tuple[1], value) ;
      }
    } else this.setIfChanged(path, value) ; // shortcut
    return this;
  },
  
  /** 
    Convenience method to get an array of properties.
    
    Pass in multiple property keys or an array of property keys.  This
    method uses getPath() so you can also pass key paths.

    @returns {Array} Values of property keys.
  */
  getEach: function() {
    var keys = SC.$A(arguments).flatten() ;
    var ret = [];
    for(var idx=0; idx<keys.length;idx++) {
      ret[ret.length] = this.getPath(keys[idx]);
    }
    return ret ;
  },
  
  
  /**  
    Increments the value of a property.
    
    @param key {String} property name
    @returns {Number} new value of property
  */
  incrementProperty: function(key) { 
    this.set(key,(this.get(key) || 0)+1); 
    return this.get(key) ;
  },

  /**  
    decrements a property
    
    @param key {String} property name
    @returns {Number} new value of property
  */
  decrementProperty: function(key) {
    this.set(key,(this.get(key) || 0) - 1 ) ;
    return this.get(key) ;
  },

  /**  
    Inverts a property.  Property should be a bool.
    
    @param key {String} property name
    @param value {Object} optional parameter for "true" value
    @param alt {Object} optional parameter for "false" value
    @returns {Object} new value
  */
  toggleProperty: function(key,value,alt) { 
    if (value === undefined) value = true ;
    if (alt === undefined) alt = false ;
    value = (this.get(key) == value) ? alt : value ;
    this.set(key,value);
    return this.get(key) ;
  },

  /**  
    Generic property observer called whenever a property on the receiver 
    changes.
    
    If you need to observe a large number of properties on your object, it
    is sometimes more efficient to implement this observer only and then to
    handle requests yourself.  Although this observer will be triggered 
    more often than an observer registered on a specific property, it also
    does not need to be registered which can make it faster to setup your 
    object instance.
    
    You will often implement this observer using a switch statement on the
    key parameter, taking appropriate action. 
    
    @param observer {null} no longer used; usually null
    @param target {Object} the target of the change.  usually this
    @param key {String} the name of the property that changed
    @param value {Object} the new value of the property.
    @param revision {Number} a revision you can use to quickly detect changes.
    @returns {void}
  */
  propertyObserver: function(observer,target,key,value, revision) {},

  /**
    Convenience method to call propertyWillChange/propertyDidChange.
    
    Sometimes you need to notify observers that a property has changed value 
    without actually changing this value.  In those cases, you can use this 
    method as a convenience instead of calling propertyWillChange() and 
    propertyDidChange().
    
    @param key {String} The property key that has just changed.
    @param value {Object} The new value of the key.  May be null.
    @returns {this}
  */
  notifyPropertyChange: function(key, value) {
    this.propertyWillChange(key) ;
    this.propertyDidChange(key, value) ;
    return this; 
  },
  
  /**  
    Notifies all of observers of a property changes.
    
    Sometimes when you make a major update to your object, it is cheaper to
    simply notify all observers that their property might have changed than
    to figure out specifically which properties actually did change.
    
    In those cases, you can simply call this method to notify all property
    observers immediately.  Note that this ignores property groups.
    
    @returns {this}
  */
  allPropertiesDidChange: function() {
    this._notifyPropertyObservers('*') ;
    return this ;
  },

  addProbe: function(key) { this.addObserver(key,logChange); },
  removeProbe: function(key) { this.removeObserver(key,logChange); },

  /**
    Logs the named properties to the console.
    
    @param propertyNames one or more property names
  */
  logProperty: function() {
    var props = SC.$A(arguments) ;
    for(var idx=0;idx<props.length; idx++) {
      var prop = props[idx] ;
      console.log('%@:%@: '.fmt(SC.guidFor(this), prop), this.get(prop)) ;
    }
  },
  
  /**  
    This method will listen for the observed value to change one time and 
    then will remove itself.  You can also set an optional timeout that
    will cause the function to be triggered (and the observer removed) after
    a set amount of time even if the value never changes.  The function
    can expect an extra parameter, 'didTimeout', set to true.
  
    The returned value is the function actually set as the observer. You
    can manually remove this observer by calling the cancel() method on it.
  */
  observeOnce: function(key, target, method, timeout) {
    
    // fixup the params
    var targetType = SC.$type(target) ;
    if (targetType === SC.T_FUNCTION) {
      if ((SC.$type(method) === SC.T_NUMBER) && (timeout === undefined)) {
        timeout = method ;
      }
      method = target ;
      target = this ;
    }
    
    // convert the method to a function if needed...
    if (SC.$type(method) === SC.T_STRING) method = target[method] ;
    if (!method) throw "You must pass a valid method to observeOnce()";

    var timeoutObject = null ;

    // define a custom observer that will call the target method and remove
    // itself as an observer.
    var handler = function(observer, target, property, value, rev, didTimeout) {
      // invoke method...
      method.call(this, observer, target, property, value, rev, didTimeout);
      
      // remove observer...
      target.removeObserver(key, this, handler) ;
      
      // if there is a timeout, invalidate it.
      if (timeoutObject) { timeoutObject.invalidate();}
      
      // avoid memory leaks
      handler = target = method = timeoutObject = null;
    } ;

    // now add observer
    target.addObserver(key, target, handler) ;
    if (timeout) {
      timeoutObject = function() {
        handler(null, target, key, target.get(key), target.propertyRevision, true) ;
        handler = target = method = timeoutObject = null;
      }.invokeLater(this, timeout) ;
    }

    handler.cancel = function() { 
      target.removeObserver(key, target, handler); 
      handler = target = method = timeoutObject = null;
    } ;

    return handler ;
  },

  propertyRevision: 1
    
} ;

SC.mixin(Array.prototype, SC.Observable) ;

// ........................................................................
// OBSERVER QUEUE
//
// This queue is used to hold observers when the object you tried to observe
// does not exist yet.  This queue is flushed just before any property 
// notification is sent.
SC.Observers = {
  queue: [],
  
  // Attempt to add the named observer.  If the observer cannot be found, put
  // it into a queue for later.
  addObserver: function(propertyPath, target, method, pathRoot) {
    var tuple ;

    // try to get the tuple for this.
    if (SC.$type(propertyPath) === SC.T_STRING) {
      tuple = SC.tupleForPropertyPath(propertyPath, pathRoot) ;
    } else {
      tuple = propertyPath; 
    }

    // if a tuple was found, add the observer immediately...
    if (tuple) {
      tuple[0].addObserver(tuple[1],target, method) ;
      
    // otherwise, save this in the queue.
    } else {
      this.queue.push([propertyPath, target, method, pathRoot]) ;
    }
  },

  // Remove the observer.  If it is already in the queue, remove it.  Also
  // if already found on the object, remove that.
  removeObserver: function(propertyPath, target, method, pathRoot) {
    var idx, queue, tuple, item;
    
    tuple = SC.tupleForPropertyPath(propertyPath, pathRoot) ;
    if (tuple) {
      tuple[0].removeObserver(tuple[1], target, method) ;
    } 

    idx = this.queue.length; queue = this.queue ;
    while(--idx >= 0) {
      item = queue[idx] ;
      if ((item[0] === propertyPath) && (item[1] === target) && (item[2] == method) && (item[3] === pathRoot)) queue[idx] = null ;
    }
  },
  
  // Flush the queue.  Attempt to add any saved observers.
  flush: function() {
    var oldQueue = this.queue ;
    var newQueue = (this.queue = []) ; 
    var idx = oldQueue.length ;
    while(--idx >= 0) {
      var item = oldQueue[idx] ;
      if (!item) continue ;
      
      var tuple = SC.tupleForPropertyPath(item[0], item[3]);
      if (tuple) {
        tuple[0].addObserver(tuple[1], item[1], item[2]) ;
      } else newQueue.push(item) ;
    }
  },
  
  isObserveringSuspended: 0,
  _pending: SC.Set.create(),
  
  objectHasPendingChanges: function(obj) {
    this._pending.add(obj) ; // save for later
  },

  // temporarily suspends all property change notifications.
  suspendPropertyObserving: function() {
    this.isObservingSuspended++ ;
  },
  
  // resume change notifications.  This will call notifications to be
  // delivered for all pending objects.
  resumePropertyObserving: function() {
    var pending ;
    if(--this.isObservingSuspended <= 0) {
      pending = this._pending ;
      this._pending = SC.Set.create() ;
      while(pending.length > 0) {
        pending.pop()._notifyPropertyObservers() ;
      }
      pending = null ;
    }
  }
  
} ;



/* End ------------------------------------------------------- system/mixins/observable.js*/

/* Start ----------------------------------------------------- system/mixins/array.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

require('system/mixins/enumerable') ;

SC.OUT_OF_RANGE_EXCEPTION = "Index out of range" ;

/**
  @namespace
  
  This module implements Observer-friendly Array-like behavior.  This mixin is 
  picked up by the Array class as well as other controllers, etc. that want to  
  appear to be arrays.
  
  Unlike SC.Enumerable, this mixin defines methods specifically for 
  collections that provide index-ordered access to their contents.  When you
  are designing code that needs to accept any kind of Array-like object, you
  should use these methods instead of Array primitives because these will 
  properly notify observers of changes to the array. 
  
  Although these methods are efficient, they do add a layer of indirection to
  your application so it is a good idea to use them only when you need the 
  flexibility of using both true JavaScript arrays and "virtual" arrays such
  as controllers and collections.
  
  You can use the methods defined in this module to access and modify array 
  contents in a KVO-friendly way.  You can also be notified whenever the 
  membership if an array changes by observing the "[]" property.

  To support SC.Array in your own class, you must override two
  primitives to use it: replace() and objectAt().  

  Note that the SC.Array mixin also incorporates the SC.Enumerable mixin.  All
  SC.Array-like objects are also enumerable.

  @extends SC.Enumerable
  @since SproutCore 0.9.0
*/
SC.Array = {

/**
  @field {Number} length
  
  Your array must support the length property.  your replace methods should
  set this property whenever it changes.
*/
  // length: 0,
  
/**
  This is one of the primitves you must implement to support SC.Array.  You 
  should replace amt objects started at idx with the objects in the passed 
  array.  You should also call this.enumerableContentDidChange() ;
  
  @param {Number} idx 
    Starting index in the array to replace.  If idx >= length, then append to 
    the end of the array.

  @param {Number} amt 
    Number of elements that should be removed from the array, starting at 
    *idx*.

  @param {Array} objects 
    An array of zero or more objects that should be inserted into the array at 
    *idx* 
*/
  replace: function(idx, amt, objects) {
    throw "replace() must be implemented to support SC.Array" ;
  },

/**
  This is one of the primitives you must implement to support SC.Array.  
  Returns the object at the named index.  If your object supports retrieving 
  the value of an array item using get() (i.e. myArray.get(0)), then you do
  not need to implement this method yourself.
  
  @param {Number} idx
    The index of the item to return.  If idx exceeds the current length, 
    return null.
*/
  objectAt: function(idx)
  {
    if (idx < 0) return undefined ;
    if (idx >= this.get('length')) return undefined;
    return this.get(idx);
  },
  
  /**
    @field []

    This is the handler for the special array content property.  If you get
    this property, it will return this.  If you set this property it a new 
    array, it will replace the current content.
    
    This property overrides the default property defined in SC.Enumerable.
  */
  '[]': function(key, value) {
    if (value !== undefined) {
      this.replace(0, this.get('length'), value) ;
    }  
    return this ;
  }.property(),
  
/**  
  This will use the primitive replace() method to insert an object at the 
  specified index.
  
  @param {Number} idx index of insert the object at.
  @param {Object} object object to insert
*/
  insertAt: function(idx, object) {
    if (idx > this.get('length')) throw SC.OUT_OF_RANGE_EXCEPTION ;
    this.replace(idx,0,[object]) ;
    return this ;
  },
  
  /**
    Remove an object at the specified index using the replace() primitive method.
  
    @param {Number} idx index of object to remove
  */
  removeAt: function(idx) {
    if ((idx < 0) || (idx >= this.get('length'))) throw SC.OUT_OF_RANGE_EXCEPTION;
    var ret = this.objectAt(idx) ;
    this.replace(idx,1,[]);
    return ret ;
  },
  
  /**
    Search the array of this object, removing any occurrences of it.
    @param {object} obj object to remove
  */
  removeObject: function(obj) {
    var loc = this.get('length') || 0;
    while(--loc >= 0) {
      var curObject = this.objectAt(loc) ;
      if (curObject == obj) this.removeAt(loc) ;
    }
    return this ;
  },
  
  /**
    Push the object onto the end of the array.  Works just like push() but it 
    is KVO-compliant.
  */
  pushObject: function(obj) {
    this.insertAt(this.get('length'), obj) ;
    return obj ;
  },
  
  /**
    Pop object from array or nil if none are left.  Works just like pop() but 
    it is KVO-compliant.
  */
  popObject: function() {
    var len = this.get('length') ;
    if (len == 0) return null ;
    
    var ret = this.objectAt(len-1) ;
    this.removeAt(len-1) ;
    return ret ;
  },
  
  /**
    Shift an object from start of array or nil if none are left.  Works just 
    like shift() but it is KVO-compliant.
  */
  shiftObject: function() {
    if (this.get('length') == 0) return null ;
    var ret = this.objectAt(0) ;
    this.removeAt(0) ;
    return ret ;
  },
  
  /**
    Unshift an object to start of array.  Works just like unshift() but it is 
    KVO-compliant.
  */
  unshiftObject: function(obj) {
    this.insertAt(0, obj) ;
    return obj ;
  },
  
  /**  
    Compares each item in the array.  Returns true if they are equal.
  */
  isEqual: function(ary) {
    if (!ary) return false ;
    if (ary == this) return true;
    
    var loc = ary.get('length') ;
    if (loc != this.get('length')) return false ;

    while(--loc >= 0) {
      if (!SC.isEqual(ary.objectAt(loc), this.objectAt(loc))) return false ;
    }
    return true ;
  },
  
  /**
    Generates a new array with the contents of the old array, sans any null
    values.
    
    @returns {Array}
  */
  compact: function() { return this.without(null); },
  
  /**
    Generates a new array with the contents of the old array, sans the passed
    value.
    
    @param {Object} value
    @returns {Array}
  */
  without: function(value) {
    if (this.indexOf(value) < 0) return this; // value not present.
    var ret = [] ;
    this.forEach(function(k) { if (k !== value) ret[ret.length] = k; }) ;
    return ret ;
  },
  
  /**
    Generates a new array with only unique values from the contents of the
    old array.
    
    @returns {Array}
  */
  uniq: function() {
    var found = {}; // use this to keep track of what is added.
    var ret = [] ;
    this.forEach(function(k){
      var hash = SC.hashFor(k);
      if (!found[hash]) {
        found[hash] = YES; ret[ret.length] = k;
      } 
    });
    found = null;
    return ret ;
  }
    
} ;

// Add SC.Array to the built-in array before we add SC.Enumerable to SC.Array
// since built-in Array's are already enumerable.
SC.mixin(Array.prototype, SC.Array) ; 
SC.Array = SC.mixin({}, SC.Enumerable, SC.Array) ;

// Add any extra methods to SC.Array that are native to the built-in Array.
/**
  Returns a new array that is a slice of the receiver.  This implementation
  uses the observable array methods to retrieve the objects for the new 
  slice.
  
  @param beginIndex {Integer} (Optional) index to begin slicing from.     
  @param endIndex {Integer} (Optional) index to end the slice at.
  @returns {Array} New array with specified slice
*/
SC.Array.slice = function(beginIndex, endIndex) {
  var ret = []; 
  var length = this.get('length') ;
  if (beginIndex == null) beginIndex = 0 ;
  if ((endIndex == null) || (endIndex > length)) endIndex = length ;
  while(beginIndex < endIndex) ret[ret.length] = this.objectAt(beginIndex++) ;
  return ret ;
}  ;


// ......................................................
// ARRAY SUPPORT
//
// Implement the same enhancements on Array.  We use specialized methods
// because working with arrays are so common.
(function() {
  SC.mixin(Array.prototype, {

    // primitive for array support.
    replace: function(idx, amt, objects) {
      if (!objects || objects.length == 0) {
        this.splice(idx, amt) ;
      } else {
        var args = [idx, amt].concat(objects) ;
        this.splice.apply(this,args) ;
      }
      this.enumerableContentDidChange() ;
      return this ;
    },
  
    // If you ask for an unknown property, then try to collect the value
    // from member items.
    unknownProperty: function(key, value) {
      var ret = this.reducedProperty(key, value) ;
      if (ret === undefined) {
        ret = (value === undefined) ? this.invoke('get', key) : null ;
      }
      return ret ;
    }
    
  }) ;
  
})() ;



/* End ------------------------------------------------------- system/mixins/array.js*/

/* Start ----------------------------------------------------- system/object.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core') ;
require('system/mixins/observable') ;
require('system/mixins/array') ;

/*globals $$sel */

SC.BENCHMARK_OBJECTS = NO;

/** @class

  Root object for the SproutCore framework.  SC.Object is the root class for
  most classes defined by SproutCore.  It builds on top of the native object
  support provided by JavaScript to provide support for class-like 
  inheritance, automatic bindings, properties observers, and more.  
  
  Most of the classes you define in your application should inherit from 
  SC.Object or one of its subclasses.  If you are writing objects of your
  own, you should read this documentation to learn some of the details of 
  how SC.Object's behave and how they differ from other frameworks.
  
  h2. About SproutCore Classes
  
  JavaScript is not a class-based language.  Instead it uses a type of 
  inheritence inspired by self called "prototypical" inheritance. 
  ...

  h2. Using SproutCore objects with other JavaScript object.
  
  You can create a SproutCore object just like any other object...
  obj = new SC.Object() ;
  
  @extends SC.Observable 
  @author Charles Jolley
  @constructor
  @since SproutCore 1.0
*/
SC.Object = function(props) { return this.__init(props); };

SC.mixin(SC.Object, /** @scope SC.Object @static */ {

  /**
    Adds the passed properties to the object's class definition.  You can pass
    as many hashes as you want, including Mixins, and they will be added in
    the order they are passed.
    
    @params {Hash} props the properties you want to add.
    @returns {Object} receiver
  */
  mixin: function(props) {
    var len = arguments.length, loc ;
    for(loc =0;loc<len;loc++) SC.mixin(this, arguments[loc]);
    return this ;
  },
  
  // ..........................................
  // CREATING CLASSES AND INSTANCES
  //
  
  /**
    Creates a new subclass of the receiver, adding any passed properties to
    the instance definition of the new class.  You should use this method
    when you plan to create several objects based on a class with similar 
    properties.
    
    h2. Init
    
    If you define an init() method, it will be called when you create 
    instances of your new class.  Since SproutCore uses the init() method to
    do important setup, you must be sure to always call arguments.callee.base.apply(this,arguments) somewhere
    in your init() to allow the normal setup to proceed.
    
    @params {Hash} props the methods of properties you want to add
    @returns {Class} A new object class
  */
  extend: function(props) {   
    var bench = SC.BENCHMARK_OBJECTS ;
    if (bench) SC.Benchmark.start('SC.Object.extend') ;

    // build a new constructor and copy class methods.  Do this before adding
    // any other properties so they are not overwritten by the copy.
    var prop, ret = function(props) { return this.__init(props); } ;
    for(prop in this) {
      if (!this.hasOwnProperty(prop)) continue ;
      ret[prop] = this[prop];
    }
    if (this.hasOwnProperty('toString')) ret.toString = this.toString;

    // now setup superclass, guid
    ret.superclass = this ;
    SC.generateGuid(ret); // setup guid

    // setup new prototype and add properties to ti
    var base = (ret.prototype = SC.beget(this.prototype));
    var idx, len = arguments.length;
    for(idx=0;idx<len;idx++) SC._object_extend(base, arguments[idx]) ;
    base.constructor = ret; // save constructor

    if (bench) SC.Benchmark.end('SC.Object.extend') ;
    return ret ;
  },

  /**
    Creates a new instance of the class.

    Unlike most frameworks, you do not pass paramters into the init funciton
    for an object.  Instead, you pass a hash of additonal properties you want
    to have assigned to the object when it is first created.  This is
    functionally like creating a anonymous subclass of the receiver and then
    instantiating it, but more efficient.
    
    You can use create() like you would a normal constructor in a class-based
    system, or you can use it to create highly customized singleton objects
    such as controllers or app-level objects.  This is often more efficient
    than creating subclasses and than instantiating them.
    
    @param {Hash} props optional hash of method or properties to add to the instance.
    @returns {SC.Object} new instance of the receiver class.
  */
  create: function(props) { var C=this; return new C(arguments); },

  /**
    Takes an array of hashes and returns newly created instances.
    
    This convenience method will take an array of properties and simply
    instantiates objects from them.
    
    @params {Array} array Array of hashes with properties to assigned to each object.
    @returns {Array} instantiated objects.
  */
  createEach: function(array) {
    return array.map(function(props) { return this.create(props); }, this);
  },

  /**
    Adding this function to the end of a view declaration will define the 
    class as an outlet that can be constructed using the outlet() method 
    (instead of get()).
    
    @returns {Outlet} a specially constructed function that will be used to
     build the outlet later.
  */
  outlet: function() {
    var obj = this ;
    return function() {
      var ret = obj.create() ; ret.owner = this ; return ret ;
    }.property().cacheable().outlet() ;
  },
  
  /**
    Always YES since this is a class.
  */
  isClass: YES,
  
  toString: function() { return SC._object_className(this); },
  
  // ..........................................
  // PROPERTY SUPPORT METHODS
  //

  /** 
    Returns YES if the receiver is a subclass of the named class.  If the 
    receiver is the class passed, this will return NO since the class is not
    a subclass of itself.  See also kindOf().
    
    @param {Class} scClass class to compare
    @returns {Boolean} 
  */
  subclassOf: function(scClass) {
    if (this === scClass) return NO ;
    var t = this ;
    while(t = t.superclass) if (t === scClass) return YES ;
    return NO ;
  },

  /**
    Returns YES if the receiver is the passed class or is a subclass of the 
    passed class.  See also subclassOf().
    
    @param {Class} scClass class to compare
    @returns {Boolean} 
  */
  kindOf: function(scClass) { 
    return (this === scClass) || this.subclassOf(scClass) ;
  }
  
}) ;

// ..........................................
// DEFAULT OBJECT INSTANCE
// 
SC.Object.prototype = {
  
  /** @private
    This is the first method invoked on a new instance.  It will first apply
    any added properties to the new instance and then calls the real init()
    method.
    
    @param {Array} extensions an array-like object with hashes to apply.
    @returns {Object} receiver
  */
  __init: function(extensions) {
    // apply any new properties
    var idx, len = (extensions) ? extensions.length : 0;
    for(idx=0;idx<len;idx++) SC._object_extend(this, extensions[idx]) ;
    SC.generateGuid(this) ; // add guid
    this.init() ; // call real init

    // Call 'initMixin' methods to automatically setup modules.
    var inits = this.initMixin; len = (inits) ? inits.length : 0 ;
    for(idx=0;idx < len; idx++) inits[idx].call(this);

    return this ; // done!
  },

  /** 
    You can call this method on an object to mixin one or more hashes of 
    properties on the receiver object.  In addition to simply copying 
    properties, this method will also prepare the properties for use in 
    bindings, computed properties, etc.
    
    If you plan to use this method, you should call it before you call
    the inherited init method from SC.Object or else your instance may not 
    function properly.  
    
    @param {Hash} ext a hash to copy.  Only one.
    @returns {Object} receiver
  */
  mixin: function() {
    var idx, len = arguments.length;
    for(idx=0;idx<len;idx++) SC.mixin(this, arguments[idx]) ;
    
    // call initMixin
    for(idx=0;idx<len;idx++) {
      var init = arguments[idx].initMixin ;
      if (init) init.call(this) ;
    }
    return this ;
  },

  /**
    This method is invoked automatically whenever a new object is 
    instantiated.  You can override this method as you like to setup your
    new object.  
    
    Within your object, be sure to call arguments.callee.base.apply(this,arguments) to ensure that the built-in
    init method is also called or your observers and computed properties may
    not be configured.
    
    Although the default init() method returns the receiver, the return value
    is ignored.
    
    @returns {Object} reciever
  */
  init: function() {
    this.initObservable();
    return this ;
  },

  /**
    Set to NO once this object has been destroyed. 
  */
  isDestroyed: NO,
  
  /**
    Call this method when you are finished with an object to teardown its
    contents.  Because JavaScript is garbage collected, you do not usually 
    need to call this method.  However, you may choose to do so for certain
    objects, especially views, in order to let them reclaim memory they 
    consume immediately.
    
    @returns {SC.Object} receiver
  */
  destroy: function() {
    if (this.get('isDestroyed')) return this; // nothing to do
    this.set('isDestroyed', YES);

    // destroy any mixins
    var idx, inits = this.destroyMixin, len = (inits) ? inits.length : 0 ;
    for(idx=0;idx < len; idx++) inits[idx].call(this);
    
    return this ;
  },

  /**
    Always YES since this is an object and not a class.
  */
  isObject: true,
  
  /**
    Returns YES if the named value is an executable function.
    
    @param methodName {String} the property name to check
    @returns {Boolean}
  */
  respondsTo: function( methodName ) {
    return !!(SC.typeOf(this[methodName]) === SC.T_FUNCTION);
  },
  
  /**
    Attemps to invoked the named method, passing the included two arguments.  
    Returns NO if the method is either not implemented or if the handler returns 
    NO (indicating that it did not handle the event).  This method is invoked to 
    deliver actions from menu items and to deliver events.  You can override 
    this method to provide additional handling if you prefer.
    
    @param {String} methodName
    @param {Object} arg1
    @param {Object} arg2
    @returns {Boolean} YES if handled, NO if not handled
  */
  tryToPerform: function(methodName, arg1, arg2) {
    return this.respondsTo(methodName) && this[methodName](arg1, arg2);
  },
  
  /**  
    EXPERIMENTAL: You can use this to call super in any method.  
    
    This currently does not work in some Safari 2 or earlier.  Instead you
    should use:
    
    arguments.callee.base.apply(this,arguments);
    
    @params args {*args} any arguments you want to pass along.
    @returns {Object} return value from super
  */
  superclass: function(args) {
    var caller = arguments.callee.caller; 
    if (!caller) throw "superclass cannot determine the caller method" ;
    return caller.superclass ? caller.superclass.apply(this, arguments) : null;
  },
  
  /**
    Returns all the keys defined on this object, excluding any defined in
    parent classes unless you pass all.
    
    @param {Boolean} all OPTIONAL: if YES return all keys, NO return only keys belonging to object itself.  Defaults to NO.
    @returns {Array} keys
  */
  keys: function(all) {
    var ret = []; 
    for(var key in this) { 
      if (all || ret.hasOwnProperty(key)) ret.push(key); 
    } 
    return ret ;  
  },

  /**  
    returns YES if the receiver is an instance of the named class.  See also
    kindOf().
    
    @param {Class} scClass the class
    @returns {Boolean}
  */
  instanceOf: function(scClass) {
    return this.constructor === scClass ;  
  },
  
  /**  
    Returns true if the receiver is an instance of the named class or any 
    subclass of the named class.  See also instanceOf().
    
    @param scClass {Class} the class
    @returns {Boolean}
  */
  kindOf: function(scClass) { return this.constructor.kindOf(scClass); },

  /** @private */
  toString: function() {
    if (!this.__toString) {
      this.__toString = "%@:%@".fmt(SC._object_className(this.constructor), SC.guidFor(this));
    } 
    return this.__toString ;
  },
  
  // ..........................................
  // OUTLETS
  // 

  /**  
    Activates any outlet connections in object and syncs any bindings.  This
    method is called automatically for view classes but may be used for any
    object.
    
    @returns {void}
  */
  awake: function(key) { 
    this.outlets.forEach(function(key) { this.get(key); },this) ;
    this.bindings.invoke('sync'); 
  },
  
  /**  
    Array of outlets to awake automatically.
    
    If you have outlets defined on a class, add this array with their
    property names to have them awake automatically.  This array is merged
    with the parent class outlet's array automatically when you call extend().
    
    @type {Array}
    @field
  */
  outlets: [],

  /**
    Invokes the named method after the specified period of time.
    
    This is a convenience method that will create a single run timer to
    invoke a method after a period of time.  The method should have the
    signature:
    
    {{{
      methodName: function(timer)
    }}}
    
    If you would prefer to pass your own parameters instead, you can instead
    call invokeLater() directly on the function object itself.
    
    @param interval {Number} period from current time to schedule.
    @param methodName {String} method name to perform.
    @returns {SC.Timer} scheduled timer.
  */
  invokeLater: function(methodName, interval) {
    if (interval === undefined) interval = 1 ;
    var f = methodName ;
    if (arguments.length > 2) {
      var args =SC.$A(arguments).slice(2);
      args.unshift(this);
      if (SC.$type(f) === SC.T_STRING) f = this[methodName] ;
      f = f.bind.apply(f, args) ;
    }
    return SC.Timer.schedule({ target: this, action: f, interval: interval });
  },

  /**
    Invokes the passed method or method name one time during the runloop.
    
    @param {Funciton} method
    @returns {SC.Object} receiver
  */
  invokeOnce: function(method) {
    SC.runLoop.invokeOnce(this, method);
    return this ;
  },
  
  /**
    Lookup the named property path and then invoke the passed function, 
    passing the resulting value to the function.
    
    This method is a useful way to handle deferred loading of properties.  
    If you want to defer loading a property, you can override this method.
    When the method is called, passing a deferred property, you can load the
    property before invoking the callback method.
    
    You can even swap out the receiver object.  
    
    The callback method should have the signature:
    
    function callback(objectAtPath, sourceObject) { ... }
    
    You may pass either a function itself or a target/method pair.
    
    @param {String} pathName
    @param {Object} target or method
    @param {Function} method
    @returns {SC.Object} receiver
  */
  invokeWith: function(pathName, target, method) {
    // normalize target/method
    if (method === undefined) {
      method = target; target = this;
    }
    if (!target) target = this ;
    if (SC.typeOf(method) === SC.T_STRING) method = target[method];
    
    // get value
    var v = this.getPath(pathName);
    
    // invoke method
    method.call(target, v, this);
    return this ;
  },
  
  /**
    The properties named in this array will be concatenated in subclasses
    instead of replaced.  This allows you to name special properties that
    should contain any values you specify plus values specified by parents.
    It is used by SproutCore and is available for your use, though you should
    limit the number of properties you include in this list as it adds a 
    slight overhead to new class and instance creation.
    
    @property
  */
  concatenatedProperties: ['concatenatedProperties', 'initMixin', 'destroyMixin']  

} ;

// bootstrap the constructor for SC.Object.
SC.Object.prototype.constructor = SC.Object;

// Add observable to mixin
SC.mixin(SC.Object.prototype, SC.Observable) ;

/* 
  Private helper methods.  These are not kept as part of the class
  definition because SC.Object is copied frequently and we want to keep the
  number of class methods to a minimum.
*/
SC.mixin({

  /** @private
    Augments the base object with the added property hashes.  This will also
    register observers and computed properties.
  */
  _object_extend: function(base, ext) {
    if (!ext) return base; // nothing to do
    
    // set _kvo_cloned for later use
    base._kvo_cloned = null;
    
    // get some common vars
    var key, idx, len, cur, cprops = base.concatenatedProperties, K = SC.K ;
    var p1,p2;
    
    // first, save any concat props.  use old or new array or concat
    idx = (cprops) ? cprops.length : 0 ;
    var concats = (idx>0) ? {} : null;
    while(--idx>=0) {
      key = cprops[idx]; p1 = base[key]; p2 = ext[key];

      if (p1) {
        if (!(p1 instanceof Array)) p1 = SC.$A(p1);
        concats[key] = (p2) ? p1.concat(p2) : p2 ;
      } else {
        if (!(p2 instanceof Array)) p2 = SC.$A(p2);
        concats[key] = p2 ;
      }
    }

    // setup arrays for bindings, observers, and properties.  Normally, just
    // save the arrays from the base.  If these need to be changed during 
    // processing, then they will be cloned first.
    var bindings = base._bindings, clonedBindings = NO;
    var observers = base._observers, clonedObservers = NO;
    var properties = base._properties, clonedProperties = NO;
    var paths, pathLoc, local ;

    // outlets are treated a little differently because you can manually 
    // name outlets in the passed in hash. If this is the case, then clone
    // the array first.
    var outlets = base.outlets, clonedOutlets = NO ;
    if (ext.outlets) { 
      outlets = (outlets || SC.A).concat(ext.outlets);
      clonedOutlets = YES ;
    }
    
    // now copy properties, add superclass to func.
    for(key in ext) {

      if (key === '_kvo_cloned') continue; // do not copy
      
      // avoid copying builtin methods
      if (!ext.hasOwnProperty(key)) continue ; 

      // get the value.  use concats if defined
      var value = (concats.hasOwnProperty(key) ? concats[key] : null) || ext[key] ;

      // Possibly add to a bindings.
      if (key.slice(-7) === "Binding") {
        if (!clonedBindings) {
          bindings = (bindings || SC.A).slice() ;
          clonedBindings = YES ;
        }

        if (bindings === null) bindings = (base._bindings || SC.A).slice();
        bindings[bindings.length] = key ;
        
      // Also add observers, outlets, and properties for functions...
      } else if (value && (value instanceof Function)) {

        // add super to funcs.  Be sure not to set the base of a func to 
        // itself to avoid infinite loops.
        if (!value.superclass && (value !== (cur=base[key]))) {
          value.superclass = value.base = cur || K;
        }

        // handle regular observers
        if (value.propertyPaths) {
          if (!clonedObservers) {
            observers = (observers || SC.A).slice() ;
            clonedObservers = YES ;
          }
          observers[observers.length] = key ;

        // handle local properties
        } else if (paths = value.localPropertyPaths) {
          pathLoc = paths.length;
          while(--pathLoc >= 0) {
            local = base._kvo_for(SC.keyFor('_kvo_local', paths[pathLoc]), SC.Set);
            local.add(key);
          }
          
        // handle computed properties
        } else if (value.dependentKeys) {
          if (!clonedProperties) {
            properties = (properties || SC.A).slice() ;
            clonedProperties = YES ;
          }
          properties[properties.length] = key ;
          
        // handle outlets
        } else if (value.autoconfiguredOutlet) {
          if (!clonedOutlets) {
            outlets = (outlets || SC.A).slice();
            clonedOutlets = YES ;
          }
          outlets[outlets.length] = key ;          
        }
      }

      // copy property
      base[key] = value ;
    }

    // copy bindings, observers, and properties 
    base._bindings = bindings || [];
    base._observers = observers || [] ;
    base._properties = properties || [] ;
    base.outlets = outlets || [];

    // toString is usually skipped.  Don't do that!
    if (ext.hasOwnProperty('toString')) base.toString = ext.toString;
    
    return base ;
  },

  /** @private
    Returns the name of this class.  If the name is not known, triggers
    a search.  This can be expensive the first time it is called.
  */
  _object_className: function(obj) {
    if (!SC.isReady) return ''; // class names are not available until ready
    if (!obj.__className) SC._object_findClassNames() ;
    if (obj.__className) return obj.__className ;

    // if no direct classname was found, walk up class chain looking for a 
    // match.
    var ret = obj ;
    while(ret && !ret.__className) ret = ret.superclass; 
    return (ret && ret.__className) ? ret.__className : 'Anonymous';
  },

  /** @private
    This is a way of performing brute-force introspection.  This searches 
    through all the top-level properties looking for classes.  When it finds
    one, it saves the class path name.
  */
  _object_findClassNames: function() {
    
    if (SC._object_foundObjectClassNames) return ;
    SC._object_foundObjectClassNames = true ;
    
    var seen = [] ;
    var searchObject = function(root, object, levels) {
      levels-- ;

      // not the fastest, but safe
      if (seen.indexOf(object) >= 0) return ;
      seen.push(object) ;

      for(var key in object) {
        if (key == '__scope__') continue ;
        if (key == 'superclass') continue ;
        if (!key.match(/^[A-Z0-9]/)) continue ;

        var path = (root) ? [root,key].join('.') : key ;
        var value = object[key] ;


        switch(SC.$type(value)) {
        case SC.T_CLASS:
          if (!value.__className) value.__className = path;
          if (levels>=0) searchObject(path, value, levels) ;
          break ;

        case SC.T_OBJECT:
          if (levels>=0) searchObject(path, value, levels) ;
          break ;

        case SC.T_HASH:
          if (((root) || (path==='SC')) && (levels>=0)) searchObject(path, value, levels) ;
          break ;

        default:
          break;
        }
      }
    } ;
    
    searchObject(null, window, 2) ;
    
    // Internet Explorer doesn's loop over global variables...
    if ( SC.browser.isIE ) {
      searchObject('SC', SC, 2) ; // get names for the SC classes
      
      // get names for the model classes, including nested namespaces (untested)
      for ( var i = 0; i < SC.Server.servers.length; i++ ) {
        var server = SC.Server.servers[i];
        if (server.prefix) {
          for (var prefixLoc = 0; prefixLoc < server.prefix.length; prefixLoc++) {
            var prefixParts = server.prefix[prefixLoc].split('.');
            var namespace = window;
            var namespaceName;
            for (var prefixPartsLoc = 0; prefixPartsLoc < prefixParts.length; prefixPartsLoc++) {
              namespace = namespace[prefixParts[prefixPartsLoc]] ;
              namespaceName = prefixParts[prefixPartsLoc];
            }
            searchObject(namespaceName, namespace, 2) ;
          }
        }
      }
    }
  }
    
}) ;

function logChange(target,key,value) {
  console.log("CHANGE: %@[%@] = %@".fmt(target, key, target.get(key)));
}




/* End ------------------------------------------------------- system/object.js*/

/* Start ----------------------------------------------------- system/locale.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

require('system/object');

/**
  The Locale defined information about a specific locale, including date and
  number formatting conventions, and localization strings.  You can define
  various locales by adding them to the SC.locales hash, keyed by language
  and/or country code.
  
  On page load, the default locale will be chosen based on the current 
  languages and saved at SC.Locale.current.  This locale is used for 
  localization, etc.
  
  h2. Creating a new locale
  
  You can create a locale by simply extending the SC.Locale class and adding
  it to the locales hash:
  
  {{{
    SC.Locale.locales['en'] = SC.Locale.extend({ .. config .. }) ;
  }}}
  
  Alternatively, you could choose to base your locale on another locale by
  extending that locale:
  
  {{{
    SC.Locale.locales['en-US'] = SC.Locale.locales['en'].extend({ ... }) ;
  }}}
  
  Note that if you do not define your own strings property, then your locale
  will inherit any strings added to the parent locale.  Otherwise you must
  implement your own strings instead.
  
  @extends SC.Object
  @since SproutCore 1.0
*/
SC.Locale = SC.Object.extend({
  
  init: function() {
    // make sure we know the name of our own locale.
    if (!this.language) SC.Locale._assignLocales();
    
    // Make sure we have strings that were set using the new API.  To do this
    // we check to a bool that is set by one of the string helpers.  This 
    // indicates that the new API was used. If the new API was not used, we
    // check to see if the old API was used (which places strings on the 
    // String class). 
    if (!this.hasStrings) {
      var langs = this._deprecatedLanguageCodes || [] ;
      langs.push(this.language);
      var idx = langs.length ;
      var strings = null ;
      while(!strings && --idx >= 0) {
        strings = String[langs[idx]];
      }
      if (strings) {
        this.hasStrings = YES; 
        this.strings = strings ;
      }
    }
  },
  
  /** Set to YES when strings have been added to this locale. */
  hasStrings: NO,
  
  /** The strings hash for this locale. */
  strings: {},
  
  toString: function() {
    if (!this.language) SC.Locale._assignLocales() ;
    return "SC.Locale["+this.language+"]"+SC.guidFor(this) ;
  },
  
  /** 
    Returns the localized version of the string or the string if no match
    was found.
    
    @param {String} string
    @param {String} optional default string to return instead
    @returns {String}
  */
  locWithDefault: function(string, def) {
    return this.strings[string] || def || string ;
  }
  
  
}) ;

SC.Locale.mixin(/** @scope SC.Locale */ {

  /**
    If YES, localization will favor the detected language instead of the
    preferred one.
  */
  useAutodetectedLanguage: NO,
  
  /**
    This property is set by the build tools to the current build language.
  */
  preferredLanguage: null,
  
  /** 
    Invoked at the start of SproutCore's document onready handler to setup 
    the currentLocale.  This will use the language properties you have set on
    the locale to make a decision.
  */
  createCurrentLocale: function() {

    // get values from String if defined for compatibility with < 1.0 build 
    // tools.
    var autodetect = (String.useAutodetectedLanguage !== undefined) ? String.useAutodetectedLanguage : this.useAutodetectedLanguage; 
    var preferred = (String.preferredLanguage !== undefined) ? String.preferredLanguage : this.preferredLanguage ;

    // determine the language
    var lang = ((autodetect) ? this.browser.language : null) || preferred || this.browser.language || 'en';
    lang = SC.Locale.normalizeLanguage(lang) ;

    // get the locale class.  If a class cannot be found, fall back to generic
    // language then to english.
    var klass = this.localeClassFor(lang) ;

    // if the detected language does not match the current language (or there
    // is none) then set it up.
    if (lang != this.currentLanguage) {
      this.currentLanguage = lang ; // save language
      this.currentLocale = klass.create(); // setup locale
    }
    return this.currentLocale ;
  },

  /**
    Finds the locale class for the names language code or creates on based on
    its most likely parent.
  */
  localeClassFor: function(lang) {
    lang = SC.Locale.normalizeLanguage(lang) ;
    var parent, klass = this.locales[lang];
    
    // if locale class was not found and there is a broader-based locale
    // present, create a new locale based on that.
    if (!klass && ((parent = lang.split('-')[0]) !== lang) && (klass = this.locales[parent])) {
      klass = this.locales[lang] = klass.extend() ;      
    }
    
    // otherwise, try to create a new locale based on english.
    if (!klass) klass = this.locales[lang] = this.locales.en.extend();
    
    return klass;
  },

  /** 
    Shorthand method to define the settings for a particular locale.
    The settings you pass here will be applied directly to the locale you
    designate.  

    If you are already holding a reference to a locale definition, you can
    also use this method to operate on the receiver.
    
    If the locale you name does not exist yet, this method will create the 
    locale for you, based on the most closely related locale or english.  For 
    example, if you name the locale 'fr-CA', you will be creating a locale for
    French as it is used in Canada.  This will be based on the general French
    locale (fr), since that is more generic.  On the other hand, if you create
    a locale for manadarin (cn), it will be based on generic english (en) 
    since there is no broader language code to match against.

    @param {String} localeName
    @param {Hash} options
    @returns {SC.Locale} the defined locale
  */
  define: function(localeName, options) {
    var locale ;
    if (options===undefined && (SC.typeOf(localeName) !== SC.T_STRING)) {
      locale = this; options = localeName ;
    } else locale = SC.Locale.localeClassFor(localeName) ;
    SC.mixin(locale.prototype, options) ;
    return locale ;
  },
  
  /**
    Gets the current options for the receiver locale.  This is useful for 
    inspecting registered locales that have not been instantiated.
    
    @returns {Hash} options + instance methods
  */
  options: function() { return this.prototype; },
  
  /**
    Adds the passed hash of strings to the locale's strings table.  Note that
    if the receiver locale inherits its strings from its parent, then the 
    strings table will be cloned first.
    
    @returns {Object} receiver
  */
  addStrings: function(stringsHash) {
    // make sure the target strings hash exists and belongs to the locale
    var strings = this.prototype.strings ;
    if (strings) {
      if (!this.prototype.hasOwnProperty(strings)) {
        this.prototype.strings = SC.clone(strings) ;
      }
    } else strings = this.prototype.strings = {} ;
    
    // add strings hash
    if (stringsHash) SC.mixin(strings, stringsHash) ;
    this.prototype.hasStrings = YES ;
    return this;
  },
  
  _map: { english: 'en', french: 'fr', german: 'de', japanese: 'ja', jp: 'ja', spanish: 'es' },
  
  /**
    Normalizes the passed language into a two-character language code.
    This method allows you to specify common languages in their full english
    name (i.e. English, French, etc). and it will be treated like their two
    letter code equivalent.
    
    @param {String} languageCode
    @returns {String} normalized code
  */
  normalizeLanguage: function(languageCode) {
    if (!languageCode) return 'en' ;
    return SC.Locale._map[languageCode.toLowerCase()] || languageCode ;
  },
  
  // this method is called once during init to walk the installed locales 
  // and make sure they know their own names.
  _assignLocales: function() {
    for(var key in this.locales) this.locales[key].prototype.language = key;
  },
  
  toString: function() {
    if (!this.prototype.language) SC.Locale._assignLocales() ;
    return "SC.Locale["+this.prototype.language+"]" ;
  },
  
  // make sure important properties are copied to new class. 
  extend: function() {
    var ret= SC.Object.extend.apply(this, arguments) ;
    ret.addStrings= SC.Locale.addStrings;
    ret.define = SC.Locale.define ;
    ret.options = SC.Locale.options ;
    ret.toString = SC.Locale.toString ;
    return ret ;
  }
    
}) ;

/** 
  This locales hash contains all of the locales defined by SproutCore and
  by your own application.  See the SC.Locale class definition for the
  various properties you can set on your own locales.
  
  @property {Hash}
*/
SC.Locale.locales = {
  en: SC.Locale.extend({ _deprecatedLanguageCodes: ['English'] }),
  fr: SC.Locale.extend({ _deprecatedLanguageCodes: ['French'] }),
  de: SC.Locale.extend({ _deprecatedLanguageCodes: ['German'] }),
  ja: SC.Locale.extend({ _deprecatedLanguageCodes: ['Japanese', 'jp'] }),
  es: SC.Locale.extend({ _deprecatedLanguageCodes: ['Spanish'] })
} ;




/**
  This special helper will store the strings you pass in the locale matching
  the language code.  If a locale is not defined from the language code you
  specify, then one will be created for you with the english locale as the 
  parent.
  
  @param {String} languageCode
  @param {Hash} strings
  @returns {Object} receiver 
*/
SC.stringsFor = function(languageCode, strings) {
  // get the locale, creating one if needed.
  var locale = SC.Locale.localeClassFor(languageCode);
  locale.addStrings(strings) ;
  return this ;
} ;




/* End ------------------------------------------------------- system/locale.js*/

/* Start ----------------------------------------------------- strings.js*/

// ========================================================================
// Sprout Core
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/locale');

// English Strings.
SC.stringsFor('English', {
  "Invalid.CreditCard(%@)": "%@ is not a valid credit card number",
  "Invalid.Email(%@)": "%@ is not a valid email address",
  "Invalid.NotEmpty(%@)": "%@ must not be empty",
  "Invalid.Password": "Your passwords do not match.  Please try typing them again.",
  "Invalid.General(%@)": "%@ is invalid.  Please try again.",
  "Invalid.Number(%@)": "%@ is not a number."
}) ;


/* End ------------------------------------------------------- strings.js*/

/* Start ----------------------------------------------------- system/browser.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core');

/** Detects the current browser type. Borrowed from jQuery + prototype */
SC.browser = (function() {
  
  var userAgent = navigator.userAgent.toLowerCase();
  var version = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1] ;

  var browser = /** @scope SC.browser */ {
    
    /** The current browser version */
    version: version,
    
    /** non-zero if webkit-based browser */
    safari: (/webkit/).test( userAgent ) ? version : 0,
    
    /** non-zero if this is an opera-based browser */
    opera: (/opera/).test( userAgent ) ? version : 0,
    
    /** non-zero if this is IE */
    msie: (/msie/).test( userAgent ) && !(/opera/).test( userAgent ) ? version : 0,
    
    /** non-zero if this is a miozilla based browser */
    mozilla: (/mozilla/).test( userAgent ) && !(/(compatible|webkit)/).test( userAgent ) ? version : 0,
    
    /** non-zero if this is mobile safari */
    mobileSafari: (/Apple.*Mobile.*Safari/).test(userAgent) ? version : 0,
    
    /** non-zero if we are on windows */
    windows: !!(/(Windows)/).test(userAgent),
    
    /** non-zero if we are on a max */
    mac: !!((/(Macintosh)/).test(userAgent) || (/(Mac OS X)/).test(userAgent)),
    
    language: ((navigator.language || navigator.browserLanguage).split('-', 1)[0])
  };
  
  // Add more SC-like descriptions...
  SC.extend(browser, /** @scope SC.browser */ {
    
    isOpera: !!browser.opera,
    isIe: !!browser.msie,
    isIE: !!browser.msie,
    isSafari: !!browser.safari,
    isMobileSafari: !!browser.mobileSafari,
    isMozilla: !!browser.mozilla,
    isWindows: !!browser.windows,
    isMac: !!browser.mac,

    /**
      The current browser name.  This is useful for switch statements. */
    current: (browser.msie) ? 'msie' : (browser.mozilla) ? 'mozilla' : (browser.safari) ? 'safari' : (browser.opera) ? 'opera' : 'unknown'
    
  }) ;
  
  return browser ;

})();



/* End ------------------------------------------------------- system/browser.js*/

/* Start ----------------------------------------------------- system/builder.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/mixins/enumerable') ;

/**
  The Builder class makes it easy to create new chained-builder API's such as
  those provided by CoreQuery or jQuery.  Usually you will not create a new
  builder yourself, but you will often use instances of the Builder object to
  configure parts of the UI such as menus and views.
  
  h1. Anatomy of a Builder
  
  You can create a new Builder much like you would any other class in 
  SproutCore.  For example, you could create a new CoreQuery-type object with
  the following:
  
  {{{
    SC.$ = SC.Builder.create({
      // methods you can call go here.
    });
  }}}
  
  Unlike most classes in SproutCore, Builder objects are actually functions 
  that you can call to create new instances.  In the example above, to use 
  the builder, you must call it like a function:
  
  {{{
    buildit = SC.$();
  }}}
  
  In addition to defining a function like this, all builder objects also have
  an 'fn' property that contains a hash of all of the helper methods defined
  on the builder function.  Once a builder has been created, you can add 
  addition "plugins" for the builder by simply adding new methods to the
  fn property.
  
  h1. Writing Builder Functions
  
  All builders share a few things in comming:
  
  - when a new builder is created, it's init() method will be called.  The default version of this method simply copies the passed parameters into the builder as content, but you can override this with anything you want.
  
  - the content the builder works on is stored as indexed properties (i.e. 0,1,2,3, like an array).  The builder should also have a length property if you want it treated like an array.
    
  - Builders also maintain a stack of previous builder instances which you can pop off at any time.
    
  To get content back out of a builder once you are ready with it, you can
  call the method done().  This will return an array or a single object, if 
  the builder only works on a single item.
  
  You should write your methods using the getEach() iterator to work on your
  member objects.  All builders implement SC.Enumerable in the fn() method.

  CoreQuery = SC.Builder.create({
    ...
  }) ;
  
  CoreQuery = new SC.Builder(properties) {
    
  } ;

  CoreQuery2 = CoreQuery.extend() {
  }
  
  @constructor
*/
SC.Builder = function (props) { return SC.Builder.create(props); };

/** 
  Create a new builder object, applying the passed properties to the 
  builder's fn property hash.
  
  @param {Hash} properties
  @returns {SC.Builder}
*/
SC.Builder.create = function create(props) { 
  
  // generate new fn with built-in properties and copy props
  var fn = SC.mixin(SC.beget(this.fn), props||{}) ;
  if (props.hasOwnProperty('toString')) fn.toString = props.toString;
  
  // generate new constructor and hook in the fn
  var construct = function() {
    var ret = SC.beget(fn); // NOTE: using closure here...
    
    // the defaultClass is usually this for this constructor. 
    // e.g. SC.View.build() -> this = SC.View
    ret.defaultClass = this ;
    ret.constructor = construct ;

    // now init the builder object.
    return ret.init.apply(ret, arguments) ;
  } ;
  construct.fn = construct.prototype = fn ;

  // the create() method can be used to extend a new builder.
  // eg. SC.View.buildCustom = SC.View.build.extend({ ...props... })
  construct.extend = SC.Builder.create ;
  construct.mixin = SC.Builder.mixin ;
  
  return construct; // return new constructor
} ;

SC.Builder.mixin = function() {
  var len = arguments.length, idx;
  for(idx=0;idx<len;idx++) SC.mixin(this, arguments[idx]);
  return this ;
};

/** This is the default set of helper methods defined for new builders. */
SC.Builder.fn = {

  /** 
    Default init method for builders.  This method accepts either a single
    content object or an array of content objects and copies them onto the 
    receiver.  You can override this to provide any kind of init behavior 
    that you want.  Any parameters passed to the builder method will be 
    forwarded to your init method.
    
    @returns {SC.Builder} receiver
  */
  init: function(content) {
    if (content !== undefined) {
      if (SC.typeOf(content) === T_ARRAY) {
        var loc=content.length;
        while(--loc >= 0) {
          this[loc] = content.objectAt ? content.objectAt(loc) : content[loc];
        }
        this.length = content.length ;
      } else {
        this[0] = content; this.length=1;
      }
    }
    return this ;
  },
  
  /** Return the number of elements in the matched set. */
  size: function() { return this.length; },
  
  /** 
    Take an array of elements and push it onto the stack (making it the
    new matched set.)  The receiver will be saved so it can be popped later.
    
    @param {Object|Array} content
    @returns {SC.Builder} new isntance
  */
  pushStack: function() {
    // Build a new CoreQuery matched element set
    var ret = this.constructor.apply(this,arguments);

    // Add the old object onto the stack (as a reference)
    ret.prevObject = this;

    // Return the newly-formed element set
    return ret;
  },

  /**
    Returns the previous object on the stack so you can continue with that
    transform.  If there is no previous item on the stack, an empty set will
    be returned.
  */
  end: function() { 
    return this.prevObject || this.constructor(); 
  },
  
  // toString describes the builder
  toString: function() { 
    return "%@$(%@)".fmt(this.defaultClass.toString(), 
      SC.$A(this).invoke('toString').join(',')); 
  },
  
  /** You can enhance the fn using this mixin method. */
  mixin: SC.Builder.mixin
  
};

// Apply SC.Enumerable.  Whenever possible we want to use the Array version
// because it might be native code.
(function() {
  var enumerable = SC.Enumerable, fn = SC.Builder.fn, key, value ;
  for(key in enumerable) {
    if (!enumerable.hasOwnProperty(key)) continue ;
    value = Array.prototype[key] || enumerable[key];
    fn[key] = value ;
  }
})();





/* End ------------------------------------------------------- system/builder.js*/

/* Start ----------------------------------------------------- system/core_query.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core');
require('system/mixins/enumerable');
require('system/builder') ;

/**
  CoreQuery is a simplified DOM manipulation library used internally by 
  SproutCore to find and edit DOM elements.  Outside of SproutCore, you 
  should generally use a more full-featured DOM library such as Prototype
  or jQuery.
  
  CoreQuery itself is a subset of jQuery with some additional plugins.  If
  you have jQuery already loaded when SproutCore loads, in fact, it will 
  replace CoreQuery with the full jQuery library and install any CoreQuery
  plugins, including support for the SC.Enumerable mixin.
  
  Much of this code is adapted from jQuery 1.2.6, which is available under an
  MIT license just like SproutCore.
  
  h1. Using CoreQuery
  
  You can work with CoreQuery much like you would work with jQuery.  The core
  manipulation object is exposed as SC.$.  To find some elements on the page
  you just pass in a selector like this:
  
  {{{
    var cq = SC.$('p');
  }}}
  
  The object returned from this call is a CoreQuery object that implements 
  SC.Enumerable as well as a number of other useful manipulation methods.  
  Often times we call this object the "matched set", because it usually an
  array of elements matching the selector key you passed.
  
  To work with the matched set, just call the various helper methods on it.
  Here are some of the more useful ones:
  
  {{{
    // change all of the text red
    cq.css('color','red');
    
    // hide/show the set
    cq.hide();  cq.show();
    
    // set the text content of the set
    cq.text("Hello World!");
    
  }}}
  
  Of course, you can also chain these methods, just like jQuery.  Here is 
  how you might find all of the headings in your page, change their text and
  color:
  
  {{{
    SC.$('h1').text('Hello World!').css('color','red');
  }}}
  
  h1. Using CoreQuery with Views
  
  Usually you will not want to just blindly edit the HTML content in your
  application.  Instead, you will use CoreQuery to update the portion of the
  page managed by your SC.View instances.  Every SC.View instance has a $()
  property just like SC.$().  The difference is that this function will start
  searching from the root of the view.  For example, you could use the 
  following code in your updateDisplay method to set your content and color:
  
  {{{
    updateDisplay: function() {
      this.$().text(this.get('value')).css('color','red');
    }
  }}}
  
  You could also work on content within your view, for example this will 
  change the title on your view held in the span.title element:
  
  {{{
    updateDisplay: function() {
      this.$('span.title').text('Hello World');
      this.$().setClassName('sc-enabled', YES) ;
    }
  }}}

  @class
  @extends SC.Builder.fn
*/
SC.CoreQuery = (function() {
  // Define CoreQuery inside of its own scope to support some jQuery idioms.
  
  // A simple way to check for HTML strings or ID strings
  // (both of which we optimize for)
  var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
  // Is it a simple selector
  isSimple = /^.[^:#\[\.]*$/,
  undefined ;

  var styleFloat = SC.browser.msie ? "styleFloat" : "cssFloat";

  // used for the find() method.
  var chars = (SC.browser.safari && parseInt(SC.browser.version,0) < 417) ?
      "(?:[\\w*_-]|\\\\.)" :
      "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)" ;
  var quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)") ;
  var singleClass = new RegExp("^([#.]?)(" + chars + "*)");
  var quickSplit = new RegExp("([#.]?)(" + chars + "*)",'g');

  // Constants used in CQ.css()
  var LEFT_RIGHT = ["Left", "Right"];
  var TOP_BOTTOM = ["Top", "Bottom"];
  var CSS_DISPLAY_PROPS = {  
    position: "absolute", visibility: "hidden", display:"block" 
  } ;

  var getWH = function getWH(elem, name, which) {
    var val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
    var padding = 0, border = 0, loc=which.length, dim;
    while(--loc>=0) {
      dim = which[loc];
      padding += parseFloat(CQ.curCSS( elem, "padding" + dim, true)) || 0;
      border += parseFloat(CQ.curCSS( elem, "border" + dim + "Width", true)) ||0;   
    }
    val -= Math.round(padding + border);
    return val;
  } ;

  var expando = SC.guidKey, uuid = 0, windowData = {},
    // exclude the following css properties to add px
    exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
    // cache defaultView
    defaultView = document.defaultView || {};

  // A helper method for determining if an element's values are broken
  var styleIsBorked = function styleIsBorked( elem ) {
    if ( !SC.browser.safari ) return false;

    // defaultView is cached
    var ret = defaultView.getComputedStyle( elem, null );
    return !ret || ret.getPropertyValue("color") == "";
  } ;

  

  // Helper function used by the dimensions and offset modules
  function num(elem, prop) {
    return elem[0] && parseInt( CQ.curCSS(elem[0], prop, true), 10 ) || 0;
  } ;

  var CoreQuery, CQ ;
  
  // implement core methods here from jQuery that we want available all the
  // time.  Use this area to implement jQuery-compatible methods ONLY.
  // New methods should be added at the bottom of the file, where they will
  // be installed as plugins on CoreQuery or jQuery. 
  CQ = CoreQuery = SC.Builder.create( /** @scope SC.CoreQuery.fn */ {
    
    /** Indicates that this is a jQuery-like object. */
    jquery: 'SC.CoreQuery',
    
    /** 
      Called on a new CoreQuery instance when it is first created.  You
      can pass a variety of options to the CoreQuery constructor function 
      including:
      
      - a simple selector: this will find the element and return it
      - element or array of elements - this will return a query with them
      - html-string: this will convert to DOM.
      
      @returns {CoreQuery} CoreQuery instance
    */
    init: function( selector, context ) {
      
      // Make sure that a selection was provided
      selector = selector || document;

      // Handle $(DOMElement)
      if ( selector.nodeType ) {
        this[0] = selector;
        this.length = 1;
        return this ;

      // Handle HTML strings
      } else if ( typeof selector == "string" ) {
        // Are we dealing with HTML string or an ID?
        var match = quickExpr.exec( selector );

        // Verify a match, and that no context was specified for #id
        if ( match && (match[1] || !context) ) {

          // HANDLE: $(html) -> $(array)
          if ( match[1] )
            selector = CQ.clean( [ match[1] ], context );

          // HANDLE: $("#id")
          else {
            var elem = document.getElementById( match[3] );

            // Make sure an element was located
            if ( elem ){
              // Handle the case where IE and Opera return items
              // by name instead of ID
              if ( elem.id != match[3] ) return CQ().find( selector );

              // Otherwise, we inject the element directly into the jQuery object
              return CQ( elem );
            }
            selector = [];
          }

        // HANDLE: $(expr, [context])
        // (which is just equivalent to: $(content).find(expr)
        } else return CQ( context ).find( selector );

      // HANDLE: $(function)
      // Shortcut for document ready
      } else if (SC.typeOf(selector) === SC.T_FUNCTION) {
        return SC.ready(selector);
      }

      return this.setArray(CQ.makeArray(selector));
    },

    /** Return the number of elements in the matched set. */
    size: function() { return this.length; },

    /** Return the nth element of the working array OR return a clean array
      with the result set, if no number is passed.
      
      @param {Number} num (Optional)
      @returns {Object|Array}
    */
    get: function( num ) {
      return num == undefined ? CQ.makeArray(this) : this[num];
    },

    /** 
      Find subelements matching the passed selector.  Note that CoreQuery
      supports only a very simplified selector search method.  See 
      CoreQuery.find() for more information.
      
      @param {String} selector
      @returns {CoreQuery} new instance with match
    */
    find: function( selector ) {
      var elems = CQ.map(this, function(elem){
        return CQ.find( selector, elem );
      });

      return this.pushStack(elems);
    },

    /**
      Filters the matching set to include only those matching the passed 
      selector.  Note that CoreQuery supports only a simplified selector 
      search method.  See CoreQuery.find() for more information.
      
      Also note that CoreQuery implements SC.Enumerable, which means you can
      also call this method with a callback and target and the callback will
      be executed on every element in the matching set to return a result.
    
      @param {String} selector
      @returns {CoreQuery}
    */
    filter: function( selector ) {
      return this.pushStack(
        (SC.typeOf(selector) === T_FUNCTION) &&
        CQ.grep(this, function(elem, i){
          return selector.call( elem, i );
        }) || CQ.multiFilter( selector, this ) );
    },

    /**
      Returns the results not matching the passed selector.  This is the 
      opposite of filter.
      
      
      @param {String} selector
      @returns {CoreQuery}
    */
    not: function( selector ) {
      if ( selector.constructor == String ) {
        // test special case where just one selector is passed in
        if ( isSimple.test( selector ) )
          return this.pushStack( CQ.multiFilter( selector, this, true ) );
        else
          selector = CQ.multiFilter( selector, this );
      }

      var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
      return this.filter(function() {
        return isArrayLike ? CQ.inArray( this, selector ) < 0 : this != selector;
      });
    },
    
    /**    
      Force the current matched set of elements to become the specified array 
      of elements (destroying the stack in the process) You should use 
      pushStack() in order to do this, but maintain the stack.
      
      This method is mostly used internally.  You will not need to use it 
      yourself very often.
      
      @param {Array} elems
      @returns {CoreQuery} receiver
    */
    setArray: function( elems ) {
      // Resetting the length to 0, then using the native Array push
      // is a super-fast way to populate an object with array-like properties
      this.length = 0;
      Array.prototype.push.apply( this, elems );
      return this;
    },
    
    /** 
      Executes the passed function on every element in the CoreQuery object.
      Returns an array with the return values.  Note that null values will
      be omitted from the resulting set.  This differs from SC.Enumerable and
      the JavaScript standard. 
      
      The callback must have the signature:
      
      {{{
        function(currentElement, currentIndex) { return mappedValue; }
      }}}
      
      Note that "this" on the function will also be the currentElement.
      
      @param {Function} callback
      @returns {CoreQuery} results
    */
    map: function( callback ) {
      return this.pushStack( CQ.map(this, function(elem, i){
        return callback.call( elem, i, elem );
      }));
    },
    
    /**    
      Execute a callback for every element in the matched set. (You can seed 
      the arguments with an array of args, but this is only used internally.)
      
      @param {Function} callback
      @param {Object} args
      @returns {CoreQuery} receiver
    */
    each: function( callback, args ) {
      return CQ.each( this, callback, args );
    },

    /** 
      Determine the position of an element within a matched set of elements.
      jQuery-compatible name for indexOf().
      
      @param {Element} elem
      @returns {Number} location
    */
    index: function( elem ) {
      if (elem && elem.jquery) elem = elem[0];
      return Array.prototype.indexOf.call(this, elem);
    },

    /**
      Returns a new CoreQuery object that contains just the matching item.
      
      @param {Number} i
      @returns {CoreQuery}
    */
    eq: function( i ) {
      return this.slice( i, +i + 1 );
    },

    /** 
      Slice the CoreQuery result set just like you might slice and array.
      Returns a new CoreQuery object with the result set.

      @returns {CoreQuery}
    */
    slice: function() {
      return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
    },

    /** Adds the relevant elements to the existing matching set. */
    add: function( selector ) {
      return this.pushStack( CQ.merge(
        this.get(),
        typeof selector == 'string' ?
          CQ( selector ) :
          CQ.makeArray( selector )
      ).uniq()) ;
    },
    
    /** 
      Get to set the named attribute value on the element.  You can either
      pass in the name of an attribute you would like to read from the first
      matched element, a single attribute/value pair to set on all elements
      or a hash of attribute/value pairs to set on all elements.
      
      @param {String} name attribute name
      @param {Object} value attribute value
      @param {String} type ?
      @returns {CoreQuery} receiver
    */
    attr: function( name, value, type ) {
      var options = name;

      // Look for the case where we're accessing a style value
      if ( name.constructor == String )
        if ( value === undefined )
          return this[0] && CQ[ type || "attr" ]( this[0], name );

        else {
          options = {};
          options[ name ] = value;
        }

      // Check to see if we're setting style values
      return this.each(function(i){
        // Set all the styles
        for ( name in options )
          CQ.attr(
            (type)?this.style:this,
            name, CQ.prop( this, options[ name ], type, i, name ));
      });
    },
    
    html: function( value ) {
      if (value === undefined) return (this[0]) ? this[0].innerHTML : null;
      this.each(function() { this.innerHTML = value; });
      return this;
      // return value == undefined ?
      //  (this[0] ? this[0].innerHTML : null) :
      //  this.empty().append( value );
    },

    andSelf: function() { return this.add( this.prevObject ); },

    /** 
      Returns YES if every element in the matching set matches the passed
      selector.  Remember that only simple selectors are supported in 
      CoreQuery.
      
      @param {String} selector
      @return {Boolean} 
    */
    is: function( selector ) {
      return !!selector && CQ.multiFilter( selector, this ).length > 0;
    },

    /**
      Returns YES if every element in the matching set has the named CSS
      class.
      
      @param {String} className
      @returns {Boolean}
    */
    hasClass: function( className ) {
      return Array.prototype.every.call(this, function(elem) {
        return (elem.nodeType!=1) || CQ.className.has(elem, className) ;
      });
    },

    /** 
      Provides a standardized, cross-browser method to get and set the 
      value attribute of a form element.  Optionally pass a value to set or
      no value to get.
      
      @param {Object} value
      @return {Object|CoreQuery}
    */
    val: function( value ) {
      
      // get the value
      if ( value == undefined ) {     
        var elem = this[0];
        if (elem) {
          if(CQ.nodeName(elem, 'option'))
            return (elem.attributes.value || {}).specified ? elem.value : elem.text;

          // We need to handle select boxes special
          if ( CQ.nodeName( elem, "select" ) ) {
            var index = elem.selectedIndex,
              values = [],
              options = elem.options,
              one = elem.type == "select-one";

            // Nothing was selected
            if ( index < 0 ) return null;

            // Loop through all the selected options
            var i, max = one ? index+1:options.length;
            for (i = one ? index : 0; i < max; i++ ) {
              var option = options[ i ];
              if ( option.selected ) {
                value = CQ(option).val(); // get value
                if (one) return value; // We don't need an array for one
                values.push( value ); // Multi-Selects return an array
              }
            }

            return values;        
          }

          // Everything else, we just grab the value
          return (elem.value || "").replace(/\r/g, "");
        }
        return undefined;
        
      // otherwise set the value
      } else {
        if( value.constructor == Number ) value += ''; // force to string
        this.each(function(){
          if ( this.nodeType != 1 ) return;
          
          // handle radio/checkbox.  set the checked value
          if (SC.typeOf(value) === SC.T_ARRAY && (/radio|checkbox/).test(this.type)) {
            this.checked = (CQ.inArray(this.value, value) >= 0 ||
              CQ.inArray(this.name, value) >= 0);
              
          // handle selects
          } else if ( CQ.nodeName( this, "select" ) ) {
            var values = CQ.makeArray(value);
            CQ( "option", this ).each(function(){
              this.selected = (CQ.inArray( this.value, values ) >= 0 ||
                CQ.inArray( this.text, values ) >= 0);
            });

            if (!values.length) this.selectedIndex = -1;

          // otherwise, just set the value property
          } else this.value = value;
        });       
      }
      return this ;
    },

    /** 
      Returns a clone of the matching set of elements.  Note that this will
      NOT clone event handlers like the jQuery version does becaue CoreQuery
      does not deal with events.
    */
    clone: function() {
      // Do the clone
      var ret = this.map(function(){
        if ( SC.browser.msie && !SC.isXMLDoc(this) ) {
          // IE copies events bound via attachEvent when
          // using cloneNode. Calling detachEvent on the
          // clone will also remove the events from the orignal
          // In order to get around this, we use innerHTML.
          // Unfortunately, this means some modifications to
          // attributes in IE that are actually only stored
          // as properties will not be copied (such as the
          // the name attribute on an input).
          var clone = this.cloneNode(true),
            container = document.createElement("div");
          container.appendChild(clone);
          return SC.clean([container.innerHTML])[0];
        } else return this.cloneNode(true);
      });

      // Need to set the expando to null on the cloned set if it exists
      // removeData doesn't work here, IE removes it from the original as well
      // this is primarily for IE but the data expando shouldn't be copied 
      // over in any browser
      var clone = ret.find("*").andSelf().each(function(){
        if ( this[ SC.guidKey ] != undefined )
          this[ SC.guidKey ] = null;
      });

      // Return the cloned set
      return ret;
    },

    /** 
      Set or retrieve the specified CSS value.  Pass only a key to get the
      current value, pass a key and value to change it.
      
      @param {String} key
      @param {Object} value
      @returns {Object|CoreQuery}
    */
    css: function( key, value ) {
      // ignore negative width and height values
      if ((key == 'width' || key == 'height') && parseFloat(value,0) < 0 ) {
        value = undefined;
      }
      return this.attr( key, value, "curCSS" );
    },

    /**
      Set or retrieve the text content of an element.  Pass a text element to
      update or set to end it.
      
      @param {String} text
      @returns {String|CoreQuery}
    */
    text: function( text ) {
      if ( typeof text != "object" && text != null )
        return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );

      var ret = "";

      CQ.each( text || this, function(){
        CQ.each( this.childNodes, function(){
          if ( this.nodeType != 8 )
            ret += this.nodeType != 1 ?
              this.nodeValue : CQ.fn.text( [ this ] );
        });
      });

      return ret;
    },

    /** Simple method to show elements without animation. */
    show: function() {
      var isVisible = SC.$.isVisible;
      this.each(function() {
        if (!isVisible(this)) {
          
          // try to restore to natural layout as defined by CSS
          this.style.display = this.oldblock || '';
          
          // handle edge case where the CSS style is none so we can't detect
          // the natural display state.
          if (CQ.css(this,'display') == 'none') {
            var elem = CQ('<' + this.tagName + '/>');
            CQ('body').append(elem);
            this.style.display = elem.css('display');
            // edge case where we still can't get the display
            if (this.style.display === 'none') this.style.display = 'block';
            elem.remove(); elem = null;
          }
        }
      }) ;
      return this ;
    },

    /** Simple method to hide elements without animation. */
    hide: function() {
      var isVisible = SC.$.isVisible;
      this.each(function() {
        if (isVisible(this)) {
          this.oldblock = this.oldblock || CQ.css(this,'display');
          this.style.display = 'none';
        }
      }) ;
      return this ;
    },
    
    /** 
      Low-level dom manipulation method used by append(), before(), after()
      among others.  Unlike the jQuery version, this version does not execute
      <script> tags.  That is generally not a good way to input scripts.
    */
    domManip: function( args, table, reverse, callback ) {
      var clone = this.length > 1, elems;

      return this.each(function(){
        if ( !elems ) {
          elems = CQ.clean( args, this.ownerDocument );
          if (reverse) elems.reverse();
        }

        var obj = this;
        if ( table && CQ.nodeName( this, "table" ) && CQ.nodeName( elems[0], "tr" ) ) {
          obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") );
        }

        CQ.each(elems, function(){
          var elem = clone ? CQ( this ).clone( true )[0] : this;
          // Inject the elements into the document
          callback.call( obj, elem );
        });
      });
    },
    
    append: function() {
      return this.domManip(arguments, true, false, function(elem){
        if (this.nodeType == 1)
          this.appendChild( elem );
      });
    },

    prepend: function() {
      return this.domManip(arguments, true, true, function(elem){
        if (this.nodeType == 1)
          this.insertBefore( elem, this.firstChild );
      });
    },

    before: function() {
      return this.domManip(arguments, false, false, function(elem){
        this.parentNode.insertBefore( elem, this );
      });
    },

    after: function() {
      return this.domManip(arguments, false, true, function(elem){
        this.parentNode.insertBefore( elem, this.nextSibling );
      });
    },

    replaceWith: function( value ) {
      return this.after( value ).remove();
    },

    removeData: function( key ){
      return this.each(function(){ SC.removeData( this, key ); });
    }

  }) ;
  
  // add useful helper methods to CoreQuery
  CoreQuery.mixin(/** @scope SC.CoreQuery */ {
    
    nodeName: function( elem, name ) {
      return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
    },
    
    /**
      Execute the passed callback on the elems array, returning an array with
      the mapped values.  Note that null return values are left out of the
      resulting mapping array.  This differs from the standard map() function
      defined by SC.Enumerable and the JavaScript standard.
      
      The callback must have the signature:
      
      {{{
        function(currentElement, currentIndex) { return mappedValue; }
      }}}
      
      Note that "this" on the function will also be the currentElement.
      
      @param {Array} elems
      @param {Function} callback
      @returns {Array} mapped elements
    */
    map: function( elems, callback ) {
      var ret = [];

      // Go through the array, translating each of the items to their
      // new value (or values).
      for ( var i = 0, length = elems.length; i < length; i++ ) {
        var value = callback( elems[ i ], i );

        if ( value != null )
          ret[ ret.length ] = value;
      }
      
      return ret.concat.apply([],ret) ;
    },

    /** 
      Executes the passed callback on each item in the iterable object
      passed.  This deviates from the standard getEach() method defined in
      SC.Enumerable and in the JavaScript standards.
      
      @param {Array} object
      @param {Function} callback
      @param {Object} args internal use only
      @returns {Object} object
    */
    each: function( object, callback, args ) {
      var name, i = 0, length = object.length;

      if ( args ) {
        if ( length == undefined ) {
          for ( name in object )
            if ( callback.apply( object[ name ], args ) === false )
              break;
        } else
          for ( ; i < length; )
            if ( callback.apply( object[ i++ ], args ) === false )
              break;

      // A special, fast, case for the most common use of each
      } else {
        if ( length == undefined ) {
          for ( name in object )
            if ( callback.call( object[ name ], name, object[ name ] ) === false )
              break;
        } else
          for ( var value = object[0];
            i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
      }

      return object;
    },
    
    isXMLDoc: function( elem ) {
      return elem.documentElement && !elem.body ||
        elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
    },
    
    clean: function( elems, context ) {
      var ret = [];
      context = context || document;
      // !context.createElement fails in IE with an error but returns typeof 'object'
      if (typeof context.createElement == 'undefined') {
        context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
      }

      CQ.each(elems, function(i, elem){
        if ( typeof elem == 'number' ) elem += '';
        if ( !elem ) return;

        // Convert html string into DOM nodes
        if ( typeof elem == "string" ) {
          // Fix "XHTML"-style tags in all browsers
          elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
            return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
              all :
              front + "></" + tag + ">";
          });

          // Trim whitespace, otherwise indexOf won't work as expected
          var tags = elem.trim().toLowerCase(), 
              div = context.createElement("div");

          var wrap =
            // option or optgroup
            !tags.indexOf("<opt") &&
            [ 1, "<select multiple='multiple'>", "</select>" ] ||

            !tags.indexOf("<leg") &&
            [ 1, "<fieldset>", "</fieldset>" ] ||

            tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
            [ 1, "<table>", "</table>" ] ||

            !tags.indexOf("<tr") &&
            [ 2, "<table><tbody>", "</tbody></table>" ] ||

            // <thead> matched above
            (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
            [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||

            !tags.indexOf("<col") &&
            [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

            // IE can't serialize <link> and <script> tags normally
            SC.browser.msie &&
            [ 1, "div<div>", "</div>" ] ||

            [ 0, "", "" ];

          // Go to html and back, then peel off extra wrappers
          div.innerHTML = wrap[1] + elem + wrap[2];

          // Move to the right depth
          while ( wrap[0]-- ) div = div.lastChild;

          // Remove IE's autoinserted <tbody> from table fragments
          if ( SC.browser.msie ) {

            // String was a <table>, *may* have spurious <tbody>
            var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
              div.firstChild && div.firstChild.childNodes :

              // String was a bare <thead> or <tfoot>
              wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
                div.childNodes :
                [];

            for ( var j = tbody.length - 1; j >= 0 ; --j )
              if ( CQ.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
                tbody[ j ].parentNode.removeChild( tbody[ j ] );

            // IE completely kills leading whitespace when innerHTML is used
            if ( /^\s/.test( elem ) )
              div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );

          }

          elem = CQ.makeArray( div.childNodes );
        }

        if (elem.length === 0 && (!CQ.nodeName( elem, "form" ) && !CQ.nodeName( elem, "select" ))) return;

        if (elem[0] == undefined || CQ.nodeName( elem, "form" ) || elem.options) ret.push( elem );

        else ret = CQ.merge( ret, elem );

      });

      return ret;
    },
    
    /** 
      Core element finder function in SC.CoreQuery.  CoreQuery supports only
      a very simple set of finders.  Namely, you can specify the following
      simple types of selectors:
      
      - .class-name: this will find all elements with the matching class name
      - #id: this will find all elements matching the ID
      - tagname: this will find all elements with the matching tags.
      
      You can also do some basic joined expressions like:
      
      {{{
        tagname.class-name and tagname#id
      }}}
      
      Finally, you can do simple compound searches like
      
      {{{
        tagname .class-name tagname#id
      }}}
      
      You can also pass multiple selectors separated by commas.  The return
      set will be the OR of all the result set.
      
      {{{
        #item1,#item2,#item3
      }}}
      
      You cannot do any child searches, psuedo-selectors or other complex 
      searches.  These are only the kinds of selectors that can be parsed
      quickly and use built-in methods on the browser.
      
      @param {String} t selector
      @param {Element} context
      @returns {Array} matched elements
    */
    find: function( t, context ) {
      
      // Quickly handle non-string expressions
      if ( typeof t != "string" ) return [ t ];

      // if the selector contains commas, then we actually want to search
      // multiple selectors.
      if (t.indexOf(',')>=0) {
        var ret = t.split(',').map(function(sel) {
          return CQ.find(sel,context);
        });

        // flatten arrays
        return ret.concat.apply([],ret).uniq() ;
      }
      
      // check to make sure context is a DOM element or a document
      if ( context && context.nodeType != 1 && context.nodeType != 9) {
        return [];
      }

      // Set the correct context (if none is provided)
      context = context || document;

      // Initialize the search.  split the selector into pieces
      var ret = [context], nodeName, inFindMode = YES;
      var parts = t.match(quickSplit), len = parts.length, m ;
      
      // loop through each part and either find or filter as needed
      for(var idx=0;idx<len;idx++) {
        t = parts[idx]; // the current selector to parse
        
        // handle space separators.  this just resets to find mode
        if (t === ' ' || t === '') {
          inFindMode = YES ;
          
        // if we are in find mode, then use the current selector to
        // find new elements that are children. at the end, leave findMode.
        } else if (inFindMode) {

          // split into parts to test result
          m = singleClass.exec(t);
          
          // handle special case where we get a tag name followed by an ID.
          // in this case, just swap the two and proceed.
          if ((m[1] === '') && (idx<(len-1)) && (parts[idx+1].charAt(0)==='#')) {
            t = parts[idx+1]; parts[idx+1] = parts[idx]; // swap
            m = singleClass.exec(t); // reparse
          }

          // now loop through and find elements based on tag
          var next = [], retlen = ret.length, retidx, cur, val = m[2], found;
          for(retidx=0;retidx<retlen;retidx++) {
            cur = ret[retidx]; 
            switch(m[1]) {
            case '': // tag
              if (!val) val = '*';
              // Handle IE7 being really dumb about <object>s
              if ( val == "*" && cur.nodeName.toLowerCase() == "object" ) {
                val = "param";
              }
              
              next = CQ.merge(next, cur.getElementsByTagName(val));
              break;
              
            case '#': // id
              // handle special case where we are searching the document
              if (cur === document) {
                found = document.getElementById(val) ;
                
                // if this is IE, verify that it didn't search by name
                if (SC.browser.msie && next && next.getAttribute('id')!==val){
                  found = NO; // clear
                } else {
                  if (found) next.push(found) ;
                  found = YES ; // do not do slow search
                }
              } else found = NO;
              
              // Otherwise, we have to do a slow search
              if (!found) {
                // the return value of getElementsByTagName is not an Array
                // so we need to fake it.
                found = cur.getElementsByTagName('*') ;
                found = Array.prototype.find.call(found, function(el){
                  return el.getAttribute && (el.getAttribute('id')===val);
                }) ;
                if (found) next.push(found) ;
              }
              break ;
              
            case '.': // class
              if (cur.getElementsByClassName) {
                next = CQ.merge(next, cur.getElementsByClassName(val));
              } else {
                next = CQ.merge(next, 
                  CQ.classFilter(cur.getElementsByTagName('*'),val));
              }
              break;
            default:
              // do nothing
            }
          }
          delete ret; ret = next ; // swap array
          inFindMode = NO;
          
        // if we are not in findMode then simply filter the results.
        } else ret = CQ.filter(t, ret) ;
      }
      
      // remove original context if still there
      if (ret && ret[0] == context) ret.shift();
      return ret.uniq() ; // make sure no duplicated are returned
    },

    classFilter: function(r,m,not){
      m = " " + m + " ";
      var tmp = [];
      for ( var i = 0; r[i]; i++ ) {
        var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
        if ( !not && pass || not && !pass )
          tmp.push( r[i] );
      }
      return tmp;
    },
    
    /** 
      Filters a set of elements according to those matching the passed
      selector.  The selector can contain only tag, class, and id options.
      
      The CoreQuery filter function is only capable of handling simple querys
      such as a tag, class or ID.  You cannot combine them.  Instead call
      filter several times.
      
      @param {String} t the selector to filter by
      @param {Array} r the element to filter
      @param {Boolean} not invert filter
      @returns {Array} filtered array
    */
    filter: function(t,r,not) {
      // split into parts to test result
      var m = singleClass.exec(t), val = m[2], kind = m[1], filter ;
      if (kind === '.') { // special case class
        return CQ.classFilter(CQ.makeArray(r), val, not) ;
      } else {
        if (kind === '#') { // id
          filter = function(el) {
            var ret=el && el.getAttribute && (el.getAttribute('id') === val);
            return (not) ? !ret : ret ;
          } ;
          
        } else { // tag
          filter = function(el) {
            var ret= CQ.nodeName(el, val);
            return (not) ? !ret : ret ;
          } ;
        }
        
        // the return value may not be a real instance of Array, so fake it.
        return Array.prototype.filter.call(CQ.makeArray(r), filter) ;
      }
    },

    /** @private Accepts filters separated by commas. */
    multiFilter: function( expr, elems, not ) {
      expr = (expr.indexOf(',')) ? expr.split(',') : [expr];
      var loc=expr.length,cur,ret=[];
      while(--loc >= 0) { // unit tests expect reverse iteration
        cur = CQ.filter(expr[loc].trim(), elems, not) ;
        ret = not ? elems = cur : CQ.merge(cur,ret);
      }
      return ret ;
    },

    /** 
      Merge two result sets together.  This method knows how to handle 
      the special iterables returned by IE as well.  Used internally.
    */
    merge: function(first, second) {
      // We have to loop this way because IE & Opera overwrite the length
      // expando of getElementsByTagName
      var i = 0, elem, pos = first.length;
      // Also, we need to make sure that the correct elements are being 
      // returned (IE returns comment nodes in a '*' query)
      if ( SC.browser.msie ) {
        while ( elem = second[ i++ ] ) {
          if ( elem.nodeType != 8 ) first[ pos++ ] = elem;
        }

      } else {
        while ( elem = second[ i++ ] ) first[ pos++ ] = elem;
      }

      return first;
    },
    
    // makeArray is the CoreQuery version of $A().
    makeArray: function(array) {
      var ret = [];

      if( array != null ){
        var i = array.length;
        // The window, strings (and functions) also have 'length'
        if( i == null || typeof array == 'string' || array.setInterval )
          ret[0] = array;
        else
          while( i )
            ret[--i] = array[i];
      }

      return ret;
    },

    inArray: function(elem,array) {
      return (array.indexOf) ? array.indexOf(elem) : Array.prototype.indexOf.call(array, elem);
    },
    
    // Check to see if the W3C box model is being used
    boxModel: !SC.browser.msie || document.compatMode == "CSS1Compat",

    props: {
      "for": "htmlFor",
      "class": "className",
      "float": styleFloat,
      cssFloat: styleFloat,
      styleFloat: styleFloat,
      readonly: "readOnly",
      maxlength: "maxLength",
      cellspacing: "cellSpacing",
      rowspan: "rowSpan"
    },
    
    /** @private Prepares a property string for insertion. */
    prop: function( elem, value, type, i, name ) {
      // Handle executable functions
      if (SC.typeOf(value) === SC.T_FUNCTION) value = value.call(elem, i);

      // Handle passing in a number to a CSS property
      return value && (value.constructor === Number) && type == "curCSS" && !exclude.test( name ) ? value + "px" : value;
    },
    
    
    grep: function( elems, callback, inv ) {
      var ret = [];

      // Go through the array, only saving the items
      // that pass the validator function
      for ( var i = 0, length = elems.length; i < length; i++ ) {
        if ( !inv != !callback( elems[ i ], i ) ) ret.push( elems[ i ] );
      }
      return ret;
    },
    
    /** @private internal use only */
    className: {

      // internal only, use addClass("class")
      add: function( elem, classNames ) {
        var has = CQ.className.has ;
        CQ.each((classNames || "").split(/\s+/), function(i, className){
          if ( elem.nodeType == 1 && !has( elem.className, className ) )
            elem.className += (elem.className ? " " : "") + className;
        });
      },

      // internal only, use removeClass("class")
      remove: function( elem, classNames ) {
        if (elem.nodeType == 1) {
          elem.className = classNames != undefined ?
            CQ.grep(elem.className.split(/\s+/), function(className){
              return !CQ.className.has( classNames, className );
            }).join(" ") : "";
        }
      },

      // internal only, use hasClass("class")
      has: function( elem, className ) {
        return CQ.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
      }
    },
    
    /** @private A method for quickly swapping in/out CSS properties to get 
      correct calculations */
    swap: function( elem, options, callback, direction, arg ) {
      var old = {};
      // Remember the old values, and insert the new ones
      for ( var name in options ) {
        old[ name ] = elem.style[ name ];
        elem.style[ name ] = options[ name ];
      }

      var ret = callback(elem, direction, arg );

      // Revert the old values
      for ( var name in options ) elem.style[ name ] = old[ name ];
      return ret ;
    },
    
    /** returns a normalized value for the specified style name. */
    css: function( elem, name, force ) {
      // handle special case for width/height
      if ( name == "width" || name == "height" ) {
        var val, which = (name == "width") ? LEFT_RIGHT : TOP_BOTTOM,
        props = CSS_DISPLAY_PROPS;

        val = SC.$.isVisible(elem) ? getWH(elem,name,which) : CQ.swap(elem,props,getWH,name,which) ;

        return Math.max(0, val);
      }

      return CQ.curCSS( elem, name, force );
    },

    /** @private internal method to retrieve current CSS. */
    curCSS: function( elem, name, force ) {
      var ret, style = elem.style;

      // We need to handle opacity special in IE
      if ( name == "opacity" && SC.browser.msie ) {
        ret = CQ.attr( style, "opacity" );
        return ret == "" ? "1" : ret;
      }
      
      // Opera sometimes will give the wrong display answer, this fixes it, 
      // see #2037
      if ( SC.browser.opera && name === "display" ) {
        var save = style.outline;
        style.outline = "0 solid black";
        style.outline = save;
      }

      // Make sure we're using the right name for getting the float value
      var isFloat = name.match(/float/i); 
      if (isFloat) name = styleFloat;

      // simple case to collect the value
      if ( !force && style && style[ name ] ) {
        ret = style[ name ];

      // otherwise try to use cached computed value
      } else if ( defaultView.getComputedStyle ) {

        // Only "float" is needed here
        if (isFloat) name = "float";

        name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();

        // get the computed style and verify its not broken.
        var computedStyle = defaultView.getComputedStyle( elem, null );
        if ( computedStyle && !styleIsBorked(elem, defaultView) ) {
          ret = computedStyle.getPropertyValue( name );

        // If the element isn't reporting its values properly in Safari
        // then some display: none elements are involved
        } else {
          var swap = [], stack = [], a = elem, i = 0;

          // Locate all of the parent display: none elements
          for ( ; a && styleIsBorked(a); a = a.parentNode ) stack.unshift(a);

          // Go through and make them visible, but in reverse
          // (It would be better if we knew the exact display type that they 
          // had)
          for ( ; i < stack.length; i++ ) {
            if (styleIsBorked(stack[i])) {
              swap[i] = stack[i].style.display;
              stack[i].style.display = "block";
            }
          }

          // Since we flip the display style, we have to handle that
          // one special, otherwise get the value
          ret = (name == "display" && swap[stack.length-1]!=null) ? "none" :
            (computedStyle && computedStyle.getPropertyValue(name)) || "";

          // Finally, revert the display styles back
          for ( i = 0; i < swap.length; i++ ) {
            if (swap[i]!=null) stack[i].style.display = swap[i];
          }
        }

        // We should always get a number back from opacity
        if (name == "opacity" && ret == "") ret = "1";

      } else if (elem.currentStyle) {
        // var camelCase = name.camelize();

        ret = elem.currentStyle[ name ] || elem.currentStyle[ name.camelize() ];

        // From the awesome hack by Dean Edwards
        // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
        // If we're not dealing with a regular pixel number
        // but a number that has a weird ending, we need to convert it to 
        // pixels
        if (!(/^\d+(px)?$/i).test(ret) && (/^\d/).test(ret)) {
          // Remember the original values
          var left = style.left, rsLeft = elem.runtimeStyle.left;

          // Put in the new values to get a computed value out
          elem.runtimeStyle.left = elem.currentStyle.left;
          style.left = ret || 0;
          ret = style.pixelLeft + "px";

          // Revert the changed values
          style.left = left;
          elem.runtimeStyle.left = rsLeft;
        }
      }

      return ret;
    },

    /** @private
      returns all of the actual nodes (excluding whitespace, comments, etc) in 
      the passed element.
    */
    dir: function( elem, dir ){
      var matched = [], cur = elem[dir];
      while ( cur && cur != document ) {
        if ( cur.nodeType == 1 ) matched.push( cur );
        cur = cur[dir];
      }
      return matched;
    },

    /** @private
      Returns the nth actual node (not whitespace, comment, etc) in the passed
      element.
    */
    nth: function(cur,result,dir,elem){
      result = result || 1;
      var num = 0;
      for ( ; cur; cur = cur[dir] ) {
        if ( cur.nodeType == 1 && ++num == result ) break;
      }
      return cur;
    },

    /** @private Finds the regular element-style siblings. */
    sibling: function( n, elem ) {
      var r = [];
      for ( ; n; n = n.nextSibling ) {
        if ( n.nodeType == 1 && n != elem ) r.push( n );
      }
      return r;
    },
    
    /** Primitive helper can read or update an attribute on an element. */
    attr: function( elem, name, value ) {
      // don't set attributes on text and comment nodes
      if (!elem || elem.nodeType == 3 || elem.nodeType == 8) return undefined;

      var notxml = !CQ.isXMLDoc( elem ),
        set = value !== undefined,
        msie = SC.browser.msie;

      // Try to normalize/fix the name
      name = notxml && CQ.props[ name ] || name;

      // Only do all the following if this is a node (faster for style)
      // IE elem.getAttribute passes even for style
      if ( elem.tagName ) {

        // These attributes require special treatment
        var special = /href|src|style/.test( name );

        // Safari mis-reports the default selected property of a hidden option
        // Accessing the parent's selectedIndex property fixes it
        if ( name == "selected" && SC.browser.safari ) {
          elem.parentNode.selectedIndex;
        }

        // If applicable, access the attribute via the DOM 0 way
        if ( name in elem && notxml && !special ) {
          if ( set ){
            // We can't allow the type property to be changed (since it causes 
            // problems in IE)
            if ( name == "type" && CQ.nodeName( elem, "input" ) && elem.parentNode ) {
              throw "type property can't be changed";
            }

            elem[ name ] = value;
          }

          // browsers index elements by id/name on forms, give priority to 
          // attributes.
          if( CQ.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
            return elem.getAttributeNode( name ).nodeValue;
          }

          return elem[ name ];
        }

        if ( msie && notxml &&  name === "style" )
          return CQ.attr( elem.style, "cssText", value );

        // convert the value to a string (all browsers do this but IE) see 
        // #1070 (jQuery)
        if ( set ) elem.setAttribute( name, "" + value );

        // Some attributes require a special call on IE
        var attr = (msie && notxml && special)
            ? elem.getAttribute( name, 2 )
            : elem.getAttribute( name );

        // Non-existent attributes return null, we normalize to undefined
        return attr === null ? undefined : attr;
      }

      // elem is actually elem.style ... set the style

      // IE uses filters for opacity
      if ( msie && name == "opacity" ) {
        if ( set ) {
          // IE has trouble with opacity if it does not have layout
          // Force it by setting the zoom level
          elem.zoom = 1;

          // Set the alpha filter to set the opacity
          elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
            (parseInt(value,0) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
        }

        return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
          (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
          "";
      }

      name = name.camelize();
      if ( set ) elem[ name ] = value;

      return elem[ name ];
    }
        
  }) ;
  
  CQ.fn.init.prototype = CQ.fn;
  
  // Create a new generic handlers. 
  CQ.each({
    parent: function(elem){return elem.parentNode;},

    parents: function(elem){return CQ.dir(elem,"parentNode");},

    next: function(elem){return CQ.nth(elem,2,"nextSibling");},

    prev: function(elem){return CQ.nth(elem,2,"previousSibling");},
    
    nextAll: function(elem){
      return CQ.dir(elem,"nextSibling");
    },
    
    prevAll: function(elem){
      return CQ.dir(elem,"previousSibling");
    },
    
    siblings: function(elem){
      return CQ.sibling(elem.parentNode.firstChild,elem);
    },
    
    children: function(elem){return CQ.sibling(elem.firstChild);},
    
    contents: function(elem){
      return CQ.nodeName(elem,"iframe") ?
      elem.contentDocument||elem.contentWindow.document :
      CQ.makeArray(elem.childNodes);
    }
    
  }, function(name, fn){
    CQ.fn[ name ] = function( selector ) {
      var ret = CQ.map( this, fn );

      if ( selector && typeof selector == "string" )
        ret = CQ.multiFilter( selector, ret );

      return this.pushStack(ret.uniq());
    };
  });
  
  CQ.each({
    appendTo: "append",
    prependTo: "prepend",
    insertBefore: "before",
    insertAfter: "after",
    replaceAll: "replaceWith"
  }, function(name, original){
    CQ.fn[ name ] = function() {
      var args = arguments;

      return this.each(function(){
        for ( var i = 0, length = args.length; i < length; i++ )
          CQ( args[ i ] )[ original ]( this );
      });
    };
  });
  
  CQ.each({
    removeAttr: function( name ) {
      CQ.attr( this, name, "" );
      if (this.nodeType == 1) this.removeAttribute( name );
    },

    addClass: function( classNames ) {
      CQ.className.add( this, classNames );
    },

    removeClass: function( classNames ) {
      CQ.className.remove( this, classNames );
    },

    toggleClass: function( classNames ) {
      CQ.className[ CQ.className.has( this, classNames ) ? "remove" : "add" ]( this, classNames );
    },

    /**  
      Removes either all elements or elements matching the selector.  Note
      that this does NOT account for event handling, since events are not
      managed by CoreQuery, unlike jQuery.
    */
    remove: function( selector ) {
      if ( !selector || CQ.filter( selector, [ this ] ).length ) {
        if (this.parentNode) this.parentNode.removeChild( this );
      }
    },

    /** 
      Removes the contents of the receiver, leaving it empty.  Note that this
      does NOT deal with Event handling since that is not managed by 
      CoreQuery.
    */
    empty: function() {
      while ( this.firstChild ) this.removeChild( this.firstChild );
    }
    
  }, function(name, fn){
    CQ.fn[name] = function(){ return this.each(fn, arguments); };
  });
  
  // Setup width and height functions
  CQ.each([ "Height", "Width" ], function(i, name){
    var type = name.toLowerCase();

    CQ.fn[ type ] = function( size ) {
      
      // Get window width or height
      if(this[0] === window) {
        
        // Opera reports document.body.client[Width/Height] properly in both 
        // quirks and standards
        if (SC.browser.opera) {
          ret = document.body["client" + name];

        // Safari reports inner[Width/Height] just fine (Mozilla and Opera 
        // include scroll bar widths)
        } else if (SC.browser.safari) {
          ret = window["inner" + name] ;

        // Everyone else use document.documentElement or document.body 
        // depending on Quirks vs Standards mode
        } else if (document.compatMode) {
          ret = documentElement['client' + name];
        } else ret = document.body['client' + name];
        
      // get document width or height
      } else if (this[0] === document) {
        // Either scroll[Width/Height] or offset[Width/Height], whichever is 
        // greater
        ret = Math.max(
          Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]),
          Math.max(document.body["offset" + name], document.documentElement["offset" + name])) ;        
          
      // get/set element width/or height
      } else {
        if (size == undefined) {
          return this.length ? CQ.css(this[0], type) : null ;

          // Set the width or height on the element (default to pixels if value is unitless)
        } else {
          return this.css(type, (size.constructor==String) ? size : size+"px");
        }
      }
      return ret ;
    };
    
    var tl = i ? "Left"  : "Top",  // top or left
      br = i ? "Right" : "Bottom"; // bottom or right

    // innerHeight and innerWidth
    CQ.fn["inner" + name] = function(){
      return this[ name.toLowerCase() ]() +
        num(this, "padding" + tl) +
        num(this, "padding" + br);
    };

    // outerHeight and outerWidth
    CQ.fn["outer" + name] = function(margin) {
      return this["inner" + name]() +
        num(this, "border" + tl + "Width") +
        num(this, "border" + br + "Width") +
        (margin ? num(this, "margin" + tl) + num(this, "margin" + br) : 0);
    };
    
  });
    
  // The Offset Method
  // Originally By Brandon Aaron, part of the Dimension Plugin
  // http://jquery.com/plugins/project/dimensions
  
  /** Calculates the offset for the first passed element. */
  CoreQuery.fn.offset = function() {
    var left = 0, top = 0, elem = this[0], br = SC.browser, results;
    if (!elem) return undefined; 

    function border(elem) {
      add( CQ.curCSS(elem, "borderLeftWidth", true), CQ.curCSS(elem, "borderTopWidth", true) );
    }

    function add(l, t) {
      left += parseInt(l, 10) || 0;
      top += parseInt(t, 10) || 0;
    }

    var parent       = elem.parentNode,
        offsetChild  = elem,
        offsetParent = elem.offsetParent,
        doc          = elem.ownerDocument,
        safari2      = br.safari && parseInt(br.version,0) < 522 && !(/adobeair/i).test(br.userAgent),
        css          = CQ.curCSS,
        fixed        = CQ.css(elem, "position") == "fixed";

    // Use getBoundingClientRect if available
    if (!(br.mozilla && elem==document.body) && elem.getBoundingClientRect){
      var box = elem.getBoundingClientRect();

      // Add the document scroll offsets
      add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
        box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));

      // IE adds the HTML element's border, by default it is medium which is 
      // 2px IE 6 and 7 quirks mode the border width is overwritable by the 
      // following css html { border: 0; } IE 7 standards mode, the border is 
      // always 2px This border/offset is typically represented by the 
      // clientLeft and clientTop properties
      // However, in IE6 and 7 quirks mode the clientLeft and clientTop 
      // properties are not updated when overwriting it via CSS
      // Therefore this method will be off by 2px in IE while in quirksmode
      add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );

    // Otherwise loop through the offsetParents and parentNodes
    } else {

      // Initial element offsets
      add( elem.offsetLeft, elem.offsetTop );

      // Get parent offsets
      while ( offsetParent ) {
        // Add offsetParent offsets
        add( offsetParent.offsetLeft, offsetParent.offsetTop );

        // Mozilla and Safari > 2 does not include the border on offset parents
        // However Mozilla adds the border for table or table cells
        if ( br.mozilla && !(/^t(able|d|h)$/i).test(offsetParent.tagName) || br.safari && !safari2 ) border( offsetParent );

        // Add the document scroll offsets if position is fixed on any 
        // offsetParent
        if (!fixed && css(offsetParent, "position") == "fixed") fixed = true;

        // Set offsetChild to previous offsetParent unless it is the body 
        // element
        offsetChild  = (/^body$/i).test(offsetParent.tagName) ? offsetChild : offsetParent;
        // Get next offsetParent
        offsetParent = offsetParent.offsetParent;
      }

      // Get parent scroll offsets
      while ( parent && parent.tagName && !(/^body|html$/i).test(parent.tagName)) {
        
        // Remove parent scroll UNLESS that parent is inline or a table to 
        // work around Opera inline/table scrollLeft/Top bug
        if ( !(/^inline|table.*$/i).test(css(parent, "display")) ) {
          // Subtract parent scroll offsets
          add( -parent.scrollLeft, -parent.scrollTop );
        }

        // Mozilla does not add the border for a parent that has overflow != 
        // visible
        if ( mozilla && css(parent, "overflow") != "visible" ) border(parent);

        // Get next parent
        parent = parent.parentNode;
      }

      // Safari <= 2 doubles body offsets with a fixed position 
      // element/offsetParent or absolutely positioned offsetChild
      // Mozilla doubles body offsets with a non-absolutely positioned 
      // offsetChild
      if ((safari2 && (fixed || css(offsetChild, "position") == "absolute"))||
        (br.mozilla && css(offsetChild, "position") != "absolute") ) {
          add( -doc.body.offsetLeft, -doc.body.offsetTop );
        }

      // Add the document scroll offsets if position is fixed
      if ( fixed ) {
        add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
          Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
      }
    }

    // Return an object with top and left properties
    results = { top: top, left: left };

    return results;
  };

  CoreQuery.fn.mixin({
    position: function() {
      var left = 0, top = 0, results;

      if ( this[0] ) {
        // Get *real* offsetParent
        var offsetParent = this.offsetParent(),

        // Get correct offsets
        offset       = this.offset(),
        parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();

        // Subtract element margins
        // note: when an element has margin: auto the offsetLeft and marginLeft 
        // are the same in Safari causing offset.left to incorrectly be 0
        offset.top  -= num( this, 'marginTop' );
        offset.left -= num( this, 'marginLeft' );

        // Add offsetParent borders
        parentOffset.top  += num( offsetParent, 'borderTopWidth' );
        parentOffset.left += num( offsetParent, 'borderLeftWidth' );

        // Subtract the two offsets
        results = {
          top:  offset.top  - parentOffset.top,
          left: offset.left - parentOffset.left
        };
      }

      return results;
    },

    offsetParent: function() {
      var offsetParent = this[0].offsetParent || document.body;
      while ( offsetParent && (!(/^body|html$/i).test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') ) {
        offsetParent = offsetParent.offsetParent;
      }
      return CQ(offsetParent);
    }
  }) ;


  // Create scrollLeft and scrollTop methods
  CQ.each( ['Left', 'Top'], function(i, name) {
    var method = 'scroll' + name;

    CQ.fn[ method ] = function(val) {
      if (!this[0]) return;

      return val != undefined ?

        // Set the scroll offset
        this.each(function() {
          this == window || this == document ?
            window.scrollTo(
              !i ? val : CQ(window).scrollLeft(),
               i ? val : CQ(window).scrollTop()
            ) :
            this[ method ] = val;
        }) :

        // Return the scroll offset
        this[0] == window || this[0] == document ?
          self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
            CQ.boxModel && document.documentElement[ method ] ||
            document.body[ method ] : this[0][ method ];
    };
  });
  
  
  return CoreQuery ;
}()) ;

// Install CoreQuery or jQuery, depending on what is available, as SC.$().
SC.$ = (typeof jQuery == "undefined") ? SC.CoreQuery : jQuery ;

// Add some plugins to CoreQuery.  If jQuery is installed, it will get these
// also. -- test in system/core_query/additions
SC.$.fn.mixin(/** @scope SC.CoreQuery.prototype */ {
  
  isCoreQuery: YES, // walk like a duck
  
  /** @private - better loggin */
  toString: function() {
    var values = [];
    var len = this.length, idx=0;
    for(idx=0;idx<len;idx++) {
      values[idx] = '%@: %@'.fmt(idx,(this[idx]) ? this[idx].toString() : '(null)');
    }
    return "<$:%@>(%@)".fmt(SC.guidFor(this),values.join(' , '));  
  },
  
  /** 
    Returns YES if all member elements are visible.  This is provided as a
    common test since CoreQuery does not support filtering by 
    psuedo-selector.
  */
  isVisible: function() {
    return Array.prototype.every.call(this, function(elem){
      return SC.$.isVisible(elem);
    });
  },
    
  /** Returns a new CQ object with only the first item in the object. */
  first: function() {
    return this.pushStack([this[0]]);
  },
  
  /** Returns a new CQ object with only the last item in the set. */
  last: function() {
    return this.pushStack([this[this.length-1]]);
  },
  
  /** 
    Attempts to find the views managing the passed DOM elements and returns
    them.   This will start with the matched element and walk up the DOM until
    it finds an element managed by a view.
    
    @returns {Array} array of views or null.
  */
  view: function() {
    return this.map(function() { 
      var ret, guidKey = SC.viewKey, dom = this;
      while(dom && (dom !== document) && (!dom[guidKey])) dom = dom.parentNode;
      ret = (dom) ? SC.View.views[dom[guidKey]] : null;
      dom =null;
      return ret ;
    });
  },
  
  /**
    You can either pass a single class name and a boolean indicating whether
    the value should be added or removed, or you can pass a hash with all
    the class names you want to add or remove with a boolean indicating 
    whether they should be there or not.
    
    This is far more efficient than using addClass/removeClass.
  */
  setClass: function(className, shouldAdd) {
    if (SC.none(className)) return this; //nothing to do
    var isHash = SC.typeOf(className) !== SC.T_STRING ;
    var fix = this._fixupClass, key;
    this.each(function() {
      if (this.nodeType !== 1) return; // nothing to do
      
      // collect the class name from the element and build an array
      var classNames = this.className.split(/\s+/), didChange = NO;
      
      // loop through hash or just fix single className
      if (isHash) {
        for(var key in className) {
          if (!className.hasOwnProperty(key)) continue ;
          didChange = fix(classNames, key, className[key]) || didChange;
        } 
      } else didChange = fix(classNames, className, shouldAdd);

      // if classNames were changed, join them and set...
      if (didChange) this.className = classNames.join(' ');
    });
    return this ;
  },

  /** @private used by setClass */
  _fixupClass: function(classNames, name, shouldAdd) {
    var indexOf = classNames.indexOf(name);
    // if should add, add class...
    if (shouldAdd) {
      if (indexOf < 0) { classNames.push(name); return YES ; }
      
    // otherwise, null out class name (this will leave some extra spaces)
    } else if (indexOf >= 0) { classNames[indexOf]=null; return YES; }
    return NO ;
  },
  
  /**
    Returns YES if any of the matched elements have the passed element or CQ object as a child element.
  */
  within: function(el) {
    el = SC.$(el); // make into CQ object
    var ret, elCur, myCur, idx, len = el.length;
    var loc = this.length;
    while(!ret && (--loc >= 0)) {
      myCur = this[loc];
      for(idx=0; !ret && (idx<len); idx++) {
        elCur = el[idx];
        while(elCur && (elCur !== myCur)) elCur = elCur.parentNode;
        ret = elCur === myCur ;
      }
    }
    myCur = elCur = null ; // clear memory
    return ret ;
  }
  
});

/** 
  Make CoreQuery enumerable.  Since some methods need to be disambiguated,
  we will implement some wrapper functions here. 
  
  Note that SC.Enumerable is implemented on SC.Builder, which means the
  CoreQuery object inherits this automatically.  jQuery does not extend from
  SC.Builder though, so we reapply SC.Enumerable just to be safe.
*/
(function() {
  var original = {};
  
  var wrappers = {
    
    // if you call find with a selector, then use the jQuery way.  If you 
    // call with a function/target, use Enumerable way
    find: function(callback,target) {
      return (target !== undefined) ? SC.Enumerable.find.call(this, callback, target) : original.find.call(this, callback) ;
    },

    // ditto for filter - execute SC.Enumerable style if a target is passed.
    filter: function(callback,target) {
      return (target !== undefined) ? 
        this.pushStack(SC.Enumerable.filter.call(this, callback, target)) : 
        original.filter.call(this, callback) ;
    },
    
    // filterProperty is an SC.Enumerable thing, but it needs to be wrapped
    // in a CoreQuery object.
    filterProperty: function(key, value) {
      return this.pushStack(
        SC.Enumerable.filterProperty.call(this,key,value));
    },
    
    // indexOf() is best implemented using the jQuery index()
    indexOf: SC.$.index,
    
    // map() is a little tricky because jQuery is non-standard.  If you pass
    // a context object, we will treat it like SC.Enumerable.  Otherwise use
    // jQuery.
    map: function(callback, target) {
      return (target !== undefined) ?  
        SC.Enumerable.map.call(this, callback, target) : 
        original.map.call(this, callback);
    }
  };

  // loop through an update some enumerable methods.  If this is CoreQuery,
  // we just need to patch up the wrapped methods.  If this is jQuery, we
  // need to go through the entire set of SC.Enumerable.
  var isCoreQuery = SC.$.jquery === 'SC.CoreQuery';
  var fn = SC.$.fn, enumerable = isCoreQuery ? wrappers : SC.Enumerable ;
  for(var key in enumerable) {
    if (!enumerable.hasOwnProperty(key)) continue ;
    var value = enumerable[key];
    if (key in wrappers) {
      original[key] = fn[key]; value = wrappers[key];
    }
    fn[key] = value;
  }
})();

// Add some global helper methods.
SC.mixin(SC.$, {
  
  /** @private helper method to determine if an element is visible.  Exposed
   for use in testing. */
  isVisible: function(elem) {
    var CQ = SC.$;
    return ("hidden"!=elem.type) && (CQ.css(elem,"display")!="none") && (CQ.css(elem,"visibility")!="hidden");
  }
  
}) ;




/* End ------------------------------------------------------- system/core_query.js*/

/* Start ----------------------------------------------------- system/event.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core');
require('system/core_query') ;

/**
  The event class provides a simple cross-platform library for capturing and
  delivering events on DOM elements and other objects.  While this library
  is based on code from both jQuery and Prototype.js, it includes a number of
  additional features including support for handler objects and event 
  delegation.

  Since native events are implemented very unevenly across browsers,
  SproutCore will convert all native events into a standardized instance of
  this special event class.  
  
  SproutCore events implement the standard W3C event API as well as some 
  additional helper methods.

  @constructor
  @param {Event} originalEvent
  @returns {SC.Event} event instance
  
  @since SproutCore 1.0
*/
SC.Event = function(originalEvent) { 

  // copy properties from original event, if passed in.
  if (originalEvent) {
    this.originalEvent = originalEvent ;
    var props = SC.Event._props, len = props.length, idx = len ;
    while(--idx >= 0) {
      var key = props[idx] ;
      this[key] = originalEvent[key] ;
    }
  }

  // Fix timeStamp
  this.timeStamp = this.timeStamp || Date.now();

  // Fix target property, if necessary
  // Fixes #1925 where srcElement might not be defined either
  if (!this.target) this.target = this.srcElement || document; 

  // check if target is a textnode (safari)
  if (this.target.nodeType === 3 ) this.target = this.target.parentNode;

  // Add relatedTarget, if necessary
  if (!this.relatedTarget && this.fromElement) {
    this.relatedTarget = (this.fromElement === this.target) ? this.toElement : this.fromElement;
  }

  // Calculate pageX/Y if missing and clientX/Y available
  if (SC.none(this.pageX) && !SC.none(this.clientX)) {
    var doc = document.documentElement, body = document.body;
    this.pageX = this.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
    this.pageY = this.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
  }

  // Add which for key events
  if (!this.which && ((this.charCode || originalEvent.charCode === 0) ? this.charCode : this.keyCode)) {
    this.which = this.charCode || this.keyCode;
  }

  // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
  if (!this.metaKey && this.ctrlKey) this.metaKey = this.ctrlKey;

  // Add which for click: 1 == left; 2 == middle; 3 == right
  // Note: button is not normalized, so don't use it
  if (!this.which && this.button) {
    this.which = ((this.button & 1) ? 1 : ((this.button & 2) ? 3 : ( (this.button & 4) ? 2 : 0 ) ));
  }
  
  // normalize wheelDelta, wheelDeltaX, & wheelDeltaY for Safari
  if (SC.browser.safari && originalEvent.wheelDelta!==undefined) {
    this.wheelDelta = this.wheelDeltaY = 0-(originalEvent.wheelDeltaY || originalEvent.wheelDelta)/120;
    this.wheelDeltaX = 0-(originalEvent.wheelDeltaX||0)/120 ;
    
  // normalize wheelDelta for Firefox
  // note that we multiple the delta on FF to make it's acceleration more 
  // natural.
  } else if (!SC.none(originalEvent.detail)) {
    var detail = Math.floor(originalEvent.detail * 2);
    if (originalEvent.axis && (originalEvent.axis === originalEvent.HORIZONTAL_AXIS)) {
      this.wheelDeltaX = detail;
      this.wheelDeltaY = this.wheelDelta = 0;
    } else {
      this.wheelDeltaY = this.wheelDelta = detail ;
      this.wheelDeltaX = 0 ;
    }
    
  // handle all other legacy browser
  } else {
    this.wheelDelta = this.wheelDeltaY = SC.browser.msie ? 0-originalEvent.wheelDelta : originalEvent.wheelDelta ;
    this.wheelDeltaX = 0 ;
  }
  
  return this; 
} ;

SC.mixin(SC.Event, /** @scope SC.Event */ {

  /** 
    Standard method to create a new event.  Pass the native browser event you
    wish to wrap if needed.
  */
  create: function(e) { return new SC.Event(e); },

  // the code below was borrowed from jQuery, Dean Edwards, and Prototype.js
  
  /**
    Bind an event to an element.

    This method will cause the passed handler to be executed whenever a
    relevant event occurs on the named element.  This method supports a
    variety of handler types, depending on the kind of support you need.
    
    h2. Simple Function Handlers
    
      SC.Event.add(anElement, "click", myClickHandler) ;
      
    The most basic type of handler you can pass is a function.  This function
    will be executed everytime an event of the type you specify occurs on the
    named element.  You can optionally pass an additional context object which
    will be included on the event in the event.data property.
    
    When your handler function is called the, the function's "this" property
    will point to the element the event occurred on.
    
    The click handler for this method must have a method signature like:
    
      function(event) { return YES|NO; }
      
    h2. Method Invocations
    
      SC.Event.add(anElement, "click", myObject, myObject.aMethod) ;
      
    Optionally you can specify a target object and a method on the object to 
    be invoked when the event occurs.  This will invoke the method function
    with the target object you pass as "this".  The method should have a 
    signature like:
    
      function(event, targetElement) { return YES|NO; }
      
    Like function handlers, you can pass an additional context data paramater
    that will be included on the event in the event.data property.
      
    h2. Handler Return Values
    
    Both handler functions should return YES if you want the event to 
    continue to propagate and NO if you want it to stop.  Returning NO will
    both stop bubbling of the event and will prevent any default action 
    taken by the browser.  You can also control these two behaviors separately
    by calling the stopPropagation() or preventDefault() methods on the event
    itself, returning YES from your method.
    
    h2. Limitations
    
    Although SproutCore's event implementation is based on jQuery, it is 
    much simpler in design.  Notably, it does not support namespaced events
    and you can only pass a single type at a time.
    
    If you need more advanced event handling, consider the SC.ClassicResponder 
    functionality provided by SproutCore or use your favorite DOM library.

    @param {Element} elem a DOM element, window, or document object
    @param {String} eventType the event type you want to respond to
    @param {Object} target The target object for a method call or a function.
    @param {Object} method optional method or method name if target passed
    @param {Object} context optional context to pass to the handler as event.data
    @returns {Object} receiver
  */
  add: function(elem, eventType, target, method, context) {

    // if a CQ object is passed in, either call add on each item in the 
    // matched set, or simply get the first element and use that.
    if (elem && elem.isCoreQuery) {
      if (elem.length > 0) {
        elem.forEach(function(e) { 
          this.add(e, eventType, target, method, context);
        }, this);
        return this;
      } else elem = elem.get(0);
    }
    if (!elem) return this; // nothing to do
    
    // cannot register events on text nodes, etc.
    if ( elem.nodeType == 3 || elem.nodeType == 8 ) return SC.Event;

    // For whatever reason, IE has trouble passing the window object
    // around, causing it to be cloned in the process
    if (SC.browser.msie && elem.setInterval) elem = window;

    // if target is a function, treat it as the method, with optional context
    if (SC.typeOf(target) === SC.T_FUNCTION) {
      context = method; method = target; target = null;
      
    // handle case where passed method is a key on the target.
    } else if (target && SC.typeOf(method) === SC.T_STRING) {
      method = target[method] ;
    }

    // Get the handlers queue for this element/eventType.  If the queue does
    // not exist yet, create it and also setup the shared listener for this
    // eventType.
    var events = SC.data(elem, "events") || SC.data(elem, "events", {}) ;
    var handlers = events[eventType]; 
    if (!handlers) {
      handlers = events[eventType] = {} ;
      this._addEventListener(elem, eventType) ;
    }
    
    // Build the handler array and add to queue
    handlers[SC.guidFor(method)] = [target, method, context];
    SC.Event._global[eventType] = YES ; // optimization for global triggers

    // Nullify elem to prevent memory leaks in IE
    elem = events = handlers = null ;
    return this ;
  },

  /**
    Removes a specific handler or all handlers for an event or event+type.

    To remove a specific handler, you must pass in the same function or the
    same target and method as you passed into SC.Event.add().  See that method
    for full documentation on the parameters you can pass in.
    
    If you omit a specific handler but provide both an element and eventType,
    then all handlers for that element will be removed.  If you provide only
    and element, then all handlers for all events on that element will be
    removed.
    
    h2. Limitations
    
    Although SproutCore's event implementation is based on jQuery, it is 
    much simpler in design.  Notably, it does not support namespaced events
    and you can only pass a single type at a time.
    
    If you need more advanced event handling, consider the SC.ClassicResponder 
    functionality provided by SproutCore or use your favorite DOM library.
    
    @param {Element} elem a DOM element, window, or document object
    @param {String} eventType the event type to remove
    @param {Object} target The target object for a method call.  Or a function.
    @param {Object} method optional name of method
    @returns {Object} receiver
  */
  remove: function(elem, eventType, target, method) {

    // if a CQ object is passed in, either call add on each item in the 
    // matched set, or simply get the first element and use that.
    if (elem && elem.isCoreQuery) {
      if (elem.length > 0) {
        elem.forEach(function(e) { 
          this.remove(e, eventType, target, method);
        }, this);
        return this;
      } else elem = elem.get(0);
    }
    if (!elem) return this; // nothing to do
    
    // don't do events on text and comment nodes
    if ( elem.nodeType == 3 || elem.nodeType == 8 ) return SC.Event;

    // For whatever reason, IE has trouble passing the window object
    // around, causing it to be cloned in the process
    if (SC.browser.msie && elem.setInterval) elem = window;

    var handlers, key, events = SC.data(elem, "events") ;
    if (!events) return this ; // nothing to do if no events are registered

    // if no type is provided, remove all types for this element.
    if (eventType === undefined) {
      for(eventType in events) this.remove(elem, eventType) ;

    // otherwise, remove the handler for this specific eventType if found
    } else if (handlers = events[eventType]) {

      var cleanupHandlers = NO ;
      
      // if a target/method is provided, remove only that one
      if (target || method) {
        
        // normalize the target/method
        if (SC.typeOf(target) === SC.T_FUNCTION) {
          method = target; target = null ;
        } else if (SC.typeOf(method) === SC.T_STRING) {
          method = target[method] ;
        }
        
        delete events[SC.guidFor(method)] ;
        
        // check to see if there are handlers left on this event/eventType.
        // if not, then cleanup the handlers.
        key = null ;
        for(key in handlers) break ;
        if (key===null) cleanupHandlers = YES ;

      // otherwise, just cleanup all handlers
      } else cleanupHandlers = YES ;
      
      // If there are no more handlers left on this event type, remove 
      // eventType hash from queue.
      if (cleanupHandlers) {
        delete events[eventType] ;
        this._removeEventListener(elem, eventType) ;
      }
      
      // verify that there are still events registered on this element.  If 
      // there aren't, cleanup the element completely to avoid memory leaks.
      key = null ;
      for(key in events) break;
      if(!key) {
        SC.removeData(elem, "events") ;
        delete this._elements[SC.guidFor(elem)]; // important to avoid leaks
      }
      
    }
    
    elem = events = handlers = null ; // avoid memory leaks
    return this ;
  },

  /**
    Trigger an event execution immediately.  You can use this method to 
    simulate arbitrary events on arbitary elements.

    h2. Limitations
    
    Note that although this is based on the jQuery implementation, it is 
    much simpler.  Notably namespaced events are not supported and you cannot
    trigger events globally.
    
    If you need more advanced event handling, consider the SC.ClassicResponder 
    functionality provided by SproutCore or use your favorite DOM library.

    @param elem {Element} the target element
    @param eventType {String} the event type
    @param args {Array} optional argument or arguments to pass to handler.
    @param donative ??
    @returns {Boolean} Return value of trigger or undefined if not fired
  */
  trigger: function(elem, eventType, args, donative) {

    // if a CQ object is passed in, either call add on each item in the 
    // matched set, or simply get the first element and use that.
    if (elem && elem.isCoreQuery) {
      if (elem.length > 0) {
        elem.forEach(function(e) { 
          this.trigger(e, eventType, args, donative);
        }, this);
        return this;
      } else elem = elem.get(0);
    }
    if (!elem) return this; // nothing to do

    // don't do events on text and comment nodes
    if ( elem.nodeType == 3 || elem.nodeType == 8 ) return undefined;
    
    // Normalize to an array
    args = SC.$A(args) ;

    var ret, fn = SC.typeOf(elem[eventType] || null) === SC.T_FUNCTION ;

    // Get the event to pass, creating a fake one if necessary
    var event = args[0];
    if (!event || !event.preventDefault) {
      event = {
        type: eventType,
        target: elem,
        preventDefault: function(){},
        stopPropagation: function(){},
        timeStamp: Date.now(),
        normalized: YES
      } ;
      args.unshift(event) ;
    }

    event.type = eventType ;

    // Trigger the event
    ret = SC.Event.handle.apply(elem, args) ;

    // Handle triggering native .onfoo handlers
    var onfoo = elem["on" + eventType] ;
    var isClick = SC.CoreQuery.nodeName(elem, 'a') && eventType === 'click';
    if ((!fn || isClick) && onfoo && onfoo.apply(elem, args) === NO) ret = NO;

    // Trigger the native events (except for clicks on links)
    if (fn && donative !== NO && ret !== NO && !isClick) {
      this.triggered = YES;
      try {
        elem[ eventType ]();
      // prevent IE from throwing an error for some hidden elements
      } catch (e) {}
    }
    
    this.triggered = NO;

    return ret;
  },

  /**
    This method will handle the passed event, finding any registered listeners
    and executing them.  If you have an event you want handled, you can 
    manually invoke this method.  This function expects it's "this" value to
    be the element the event occurred on, so you should always call this 
    method like:
    
      SC.Event.handle.call(element, event) ;
      
    Note that like other parts of this library, the handle function does not
    support namespaces.
    
    @param event {Event} the event to handle
    @returns {Boolean}
  */
  handle: function(event) {

    // ignore events triggered after window is unloaded or if double-called
    // from within a trigger.
    if ((typeof SC === "undefined") || SC.Event.triggered) return YES ;
    
    // returned undefined or NO
    var val, ret, namespace, all, handlers, args;

    // normalize event across browsers.  The new event will actually wrap the
    // real event with a normalized API.
    args = SC.$A(arguments);
    args[0] = event = SC.Event.normalizeEvent(event || window.event);

    // get the handlers for this event type
    handlers = (SC.data(this, "events") || {})[event.type];
    if (!handlers) return NO ; // nothing to do

    // invoke all handlers
    for (var key in handlers ) {
      var handler = handlers[key];
      var method = handler[1] ;

      // Pass in a reference to the handler function itself
      // So that we can later remove it
      event.handler = method;
      event.data = event.context = handler[2];

      var target = handler[0] || this ;
      ret = method.apply( target, args );
      
      if (val !== NO) val = ret;

      // if method returned NO, do not continue.  Stop propogation and
      // return default.  Note that we test explicitly for NO since 
      // if the handler returns no specific value, we do not want to stop.
      if ( ret === NO ) {
        event.preventDefault();
        event.stopPropagation();
      }
    }

    return val;
  },

  /**
    This method is called just before the window unloads to unhook all 
    registered events.
  */
  unload: function() {
    var key, elements = this._elements ;
    for(key in elements) this.remove(elements[key]) ;
    
    // just in case some book-keeping was screwed up.  avoid memory leaks
    for(key in elements) delete elements[key] ;
    delete this._elements ; 
  },
  
  /**
    This hash contains handlers for special or custom events.  You can add
    your own handlers for custom events here by simply naming the event and
    including a hash with the following properties:
    
     - setup: this function should setup the handler or return NO
     - teardown: this function should remove the event listener
     
  */
  special: {
    
    ready: {
      setup: function() {
        // Make sure the ready event is setup
        SC._bindReady() ;
        return;
      },

      teardown: function() { return; }

    },

    /** @private
        Implement support for mouseenter on browsers other than IE */
    mouseenter: {
      setup: function() {
        if ( SC.browser.msie ) return NO;
        SC.Event.add(this, 'mouseover', SC.Event.special.mouseover.handler);
        return YES;
      },

      teardown: function() {
        if ( SC.browser.msie ) return NO;
        SC.Event.remove(this, 'mouseover', SC.Event.special.mouseover.handler);
        return YES;
      },

      handler: function(event) {
        // If we actually just moused on to a sub-element, ignore it
        if ( SC.Event._withinElement(event, this) ) return YES;
        // Execute the right handlers by setting the event type to mouseenter
        event.type = "mouseenter";
        return SC.Event.handle.apply(this, arguments);
      }
    },

    /** @private
        Implement support for mouseleave on browsers other than IE */
    mouseleave: {
      setup: function() {
        if ( SC.browser.msie ) return NO;
        SC.Event.add(this, "mouseout", SC.Event.special.mouseleave.handler);
        return YES;
      },

      teardown: function() {
        if ( SC.browser.msie ) return NO;
        SC.Event.remove(this, "mouseout", SC.Event.special.mouseleave.handler);
        return YES;
      },

      handler: function(event) {
        // If we actually just moused on to a sub-element, ignore it
        if ( SC.Event._withinElement(event, this) ) return YES;
        // Execute the right handlers by setting the event type to mouseleave
        event.type = "mouseleave";
        return SC.Event.handle.apply(this, arguments);
      }
    }
  },

  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,
    
  _withinElement: function(event, elem) {
    // Check if mouse(over|out) are still within the same parent element
    var parent = event.relatedTarget;
    
    // Traverse up the tree
    while ( parent && parent != elem ) {
      try { parent = parent.parentNode; } catch(error) { parent = elem; }
    }

    // Return YES if we actually just moused on to a sub-element
    return parent === elem;
  },
  
  /** @private
    Adds the primary event listener for the named type on the element.
    
    If the event type has a special handler defined in SC.Event.special, 
    then that handler will be used.  Otherwise the normal browser method will
    be used.
    
    @param elem {Element} the target element
    @param eventType {String} the event type
  */
  _addEventListener: function(elem, eventType) {
    var listener, special = this.special[eventType] ;

    // Check for a special event handler
    // Only use addEventListener/attachEvent if the special
    // events handler returns NO
    if ( !special || special.setup.call(elem)===NO) {
      
      // Save element in cache.  This must be removed later to avoid 
      // memory leaks.
      var guid = SC.guidFor(elem) ;
      this._elements[guid] = elem;
      
      listener = SC.data(elem, "listener") || SC.data(elem, "listener", 
       function() {
         return SC.Event.handle.apply(SC.Event._elements[guid], arguments); 
      }) ;
      
      // Bind the global event handler to the element
      if (elem.addEventListener) {
        elem.addEventListener(eventType, listener, NO);
      } else if (elem.attachEvent) {
        elem.attachEvent("on" + eventType, listener);
      }
    }
    
    elem = special = listener = null ; // avoid memory leak
  },

  /** @private
    Removes the primary event listener for the named type on the element.
    
    If the event type has a special handler defined in SC.Event.special, 
    then that handler will be used.  Otherwise the normal browser method will
    be used.
    
    Note that this will not clear the _elements hash from the element.  You
    must call SC.Event.unload() on unload to make sure that is cleared.
    
    @param elem {Element} the target element
    @param eventType {String} the event type
  */
  _removeEventListener: function(elem, eventType) {
    var listener, special = SC.Event.special[eventType] ;
    if (!special || (special.teardown.call(elem)===NO)) {
      listener = SC.data(elem, "listener") ;
      if (listener) {
        if (elem.removeEventListener) {
          elem.removeEventListener(eventType, listener, NO);
        } else if (elem.detachEvent) {
          elem.detachEvent("on" + eventType, listener);
        }
      }
    }
    
    elem = special = listener = null ;
  },

  _elements: {},
  
  // implement preventDefault() in a cross platform way
  
  /** @private Take an incoming event and convert it to a normalized event. */
  normalizeEvent: function(event) {
    if (event == window.event)
      return SC.Event.create(event) ; // IE can't do event.normalized on an Event object
    else
      return event.normalized ? event : SC.Event.create(event) ;
  },
  
  _global: {},

  /** @private properties to copy from native event onto the event */
  _props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view which".split(" ")
  
}) ;

SC.Event.prototype = {

  /**
    Set to YES if you have called either preventDefault() or stopPropagation().  This allows a generic event handler to notice if you want to provide detailed control over how the browser handles the real event.
  */
  hasCustomEventHandling: NO,
  
  /**
    Indicates that you want to allow the normal default behavior.  Sets
    the hasCustomEventHandling property to YES but does not cancel the event.
    
    @returns {SC.Event} receiver
  */
  allowDefault: function() {
    this.hasCustomEventHandling = YES ;
    return this ;  
  },
  
  /** 
    Implements W3C standard.  Will prevent the browser from performing its
    default action on this event.
    
    @returns {SC.Event} receiver
  */
  preventDefault: function() {
    var evt = this.originalEvent ;
    if (evt) {
      if (evt.preventDefault) evt.preventDefault() ;
      evt.returnValue = NO ; // IE
    }
    this.hasCustomEventHandling = YES ;
    return this ;
  },

  /**
    Implements W3C standard.  Prevents further bubbling of the event.
    
    @returns {SC.Event} receiver
  */
  stopPropagation: function() {
    var evt = this.originalEvent ;
    if (evt) {
      if (evt.stopPropagation) evt.stopPropagation() ;
      evt.cancelBubble = YES ; // IE
    }
    this.hasCustomEventHandling = YES ; 
    return this ;
  },

  /** 
    Stops both the default action and further propogation.  This is more 
    convenient than calling both.
    
    @returns {SC.Event} receiver
  */
  stop: function() {
    return this.preventDefault().stopPropagation();
  },
  
  /** Always YES to indicate the event was normalized. */
  normalized: YES,

  /** Returns the pressed character (found in this.which) as a string. */
  getCharString: function() { 
    return (this.charCode>0) ? String.fromCharCode(this.which) : null;
  },
  
  /** Returns character codes for the event.  The first value is the normalized code string, with any shift or ctrl characters added to the begining.  The second value is the char string by itself.
  
    @returns {Array}
  */
  commandCodes: function() {
    var code=this.keyCode, ret=null, key=null, modifiers='', lowercase ;
    
    // handle function keys.
    if (code) {
      ret = SC.FUNCTION_KEYS[code] ;
      if (!ret && (this.altKey || this.ctrlKey)) ret =SC.PRINTABLE_KEYS[code];
      if (ret) {
        if (this.altKey) modifiers += 'alt_' ;
        if (this.ctrlKey) modifiers += 'ctrl_' ;
        if (this.shiftKey) modifiers += 'shift_' ;
      }
    }

    // otherwise just go get the right key.
    if (!ret) {
      code = this.which ;
      key = ret = String.fromCharCode(code) ;
      lowercase = ret.toLowerCase() ;
      if (ret != lowercase) {
        modifiers = 'shift_' ;
        ret = lowercase ;
      } else ret = null ;
    }

    if (ret) ret = modifiers + ret ;
    return [ret, key] ;
  }
    
} ;

// Also provide a Prototype-like API so that people can use either one.

/** Alias for add() method.  This provides a Prototype-like API. */
SC.Event.observe = SC.Event.add ;

/** Alias for remove() method.  This provides a Prototype-like API */
SC.Event.stopObserving = SC.Event.remove ;

/** Alias for trigger() method.  This provides a Prototype-like API */
SC.Event.fire = SC.Event.trigger;

// Register unload handler to eliminate any registered handlers
// This avoids leaks in IE and issues with mouseout or other handlers on 
// other browsers.
SC.Event.add(window, 'unload', SC.Event, SC.Event.unload) ;

SC.MODIFIER_KEYS = {
  16:'shift', 17:'ctrl', 18: 'alt'
};

SC.FUNCTION_KEYS = {
  8: 'backspace',  9: 'tab',  13: 'return',  19: 'pause',  27: 'escape',  
  33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 
  37: 'left', 38: 'up', 39: 'right', 40: 'down', 44: 'printscreen', 
  45: 'insert', 46: 'delete', 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 
  116: 'f5', 117: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 
  123: 'f12', 144: 'numlock', 145: 'scrolllock'
} ;

SC.PRINTABLE_KEYS = {
  32: ' ', 48:"0", 49:"1", 50:"2", 51:"3", 52:"4", 53:"5", 54:"6", 55:"7",
  56:"8", 57:"9", 59:";", 61:"=", 65:"a", 66:"b", 67:"c", 68:"d", 69:"e",
  70:"f", 71:"g", 72:"h", 73:"i", 74:"j", 75:"k", 76:"l", 77:"m", 78:"n",
  79:"o", 80:"p", 81:"q", 82:"r", 83:"s", 84:"t", 85:"u", 86:"v", 87:"w",
  88:"x", 89:"y", 90:"z", 107:"+", 109:"-", 110:".", 188:",", 190:".",
  191:"/", 192:"`", 219:"[", 220:"\\", 221:"]", 222:"\""
} ;


/* End ------------------------------------------------------- system/event.js*/

/* Start ----------------------------------------------------- system/binding.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/object') ;

/**
  Debug parameter you can turn on.  This will log all bindings that fire to
  the console.  This should be disabled in production code.  Note that you
  can also enable this from the console or temporarily.
*/
SC.LOG_BINDING_NOTIFICATIONS = NO ;

/**
  Performance paramter.  This will benchmark the time spent firing each 
  binding.
*/
SC.BENCHMARK_BINDING_NOTIFICATIONS = NO ;

/**
  Performance parameter.  This will benchmark the time spend configuring each
  binding.  
*/
SC.BENCHMARK_BINDING_SETUP = NO;
  
/** 
  Default placeholder for multiple values in bindings.
*/
SC.MULTIPLE_PLACEHOLDER = '@@MULT@@' ;

/**
  Default placeholder for null values in bindings.
*/
SC.NULL_PLACEHOLDER = '@@NULL@@' ;

/**
  Default placeholder for empty values in bindings.
*/
SC.EMPTY_PLACEHOLDER = '@@EMPTY@@' ;


/**
  A binding simply connects the properties of two objects so that whenever the
  value of one property changes, the other property will be changed also.  You
  do not usually work with Binding objects directly but instead describe
  bindings in your class definition using something like:
  
    valueBinding: "MyApp.someController.title"
    
  This will create a binding from "MyApp.someController.title" to the "value"
  property of your object instance automatically.  Now the two values will be
  kept in sync.
  
  h2. Customizing Your Bindings
  
  In addition to synchronizing values, bindings can also perform some basic 
  transforms on values.  These transforms can help to make sure the data fed 
  into one object always meets the expectations of that object regardless of
  what the other object outputs.
  
  To customize a binding, you can use one of the many helper methods defined 
  on SC.Binding like so:
  
    valueBinding: SC.Binding.single("MyApp.someController.title") 
    
  This will create a binding just like the example above, except that now the
  binding will convert the value of MyApp.someController.title to a single 
  object (removing any arrays) before applying it to the "value" property of
  your object.
  
  You can also chain helper methods to build custom bindings like so:
  
    valueBinding: SC.Binding.single("MyApp.someController.title").notEmpty("(EMPTY)")
    
  This will force the value of MyApp.someController.title to be a single value
  and then check to see if the value is "empty" (null, undefined, empty array,
  or an empty string).  If it is empty, the value will be set to the string 
  "(EMPTY)".
  
  h2. One Way Bindings
  
  One especially useful binding customization you can use is the oneWay() 
  helper.  This helper tells SproutCore that you are only interested in 
  receiving changes on the object you are binding from.  For example, if you
  are binding to a preference and you want to be notified if the preference 
  has changed, but your object will not be changing the preference itself, you
  could do:
  
    bigTitlesBinding: SC.Binding.oneWay("MyApp.preferencesController.bigTitles")
    
  This way if the value of MyApp.preferencesController.bigTitles changes the
  "bigTitles" property of your object will change also.  However, if you change
  the value of your "bigTitles" property, it will not update the 
  preferencesController.
  
  One way bindings are almost twice as fast to setup and twice as fast to 
  execute because the binding only has to worry about changes to one side.
  
  You should consider using one way bindings anytime you have an object that 
  may be created frequently and you do not intend to change a property; only 
  to monitor it for changes. (such as in the example above).
      
  h2. Adding Custom Transforms
  
  In addition to using the standard helpers provided by SproutCore, you can 
  also defined your own custom transform functions which will be used to 
  convert the value.  To do this, just define your transform function and add
  it to the binding with the transform() helper.  The following example will 
  not allow Integers less than ten.  Note that it checks the value of the 
  bindings and allows all other values to pass:
  
  {{{
    valueBinding: SC.Binding.transform(function(value, binding) {
      return ((SC.$type(value) === SC.T_NUMBER) && (value < 10)) ? 10 : value;      
    }).from("MyApp.someController.value")
  }}}
  
  If you would like to instead use this transform on a number of bindings,
  you can also optionally add your own helper method to SC.Binding.  This
  method should simply return the value of this.transform(). The example 
  below adds a new helper called notLessThan() which will limit the value to
  be not less than the passed minimum:
  
  {{{
    SC.Binding.notLessThan = function(minValue) {
      return this.transform(function(value, binding) {
        return ((SC.$type(value) === SC.T_NUMBER) && (value < minValue)) ? minValue : value ;
      }) ;
    } ;
  }}}
  
  You could specify this in your core.js file, for example.  Then anywhere in 
  your application you can use it to define bindings like so:
  
  {{{
    valueBinding: SC.Binding.from("MyApp.someController.value").notLessThan(10)
  }}}
  
  Also, remember that helpers are chained so you can use your helper along with
  any other helpers.  The example below will create a one way binding that 
  does not allow empty values or values less than 10:
  
  {{{
    valueBinding: SC.Binding.oneWay("MyApp.someController.value").notEmpty().notLessThan(10)
  }}}
  
  Note that the built in helper methods all allow you to pass a "from" property
  path so you don't have to use the from() helper to set the path.  You can do
  the same thing with your own helper methods if you like, but it is not 
  required.
  
  h2. Creating Custom Binding Templates
  
  Another way you can customize bindings is to create a binding template.  A
  template is simply a binding that is already partially or completely 
  configured.  You can specify this template anywhere in your app and then use 
  it instead of designating your own custom bindings.  This is a bit faster on
  app startup but it is mostly useful in making your code less verbose.
  
  For example, let's say you will be frequently creating one way, not empty 
  bindings that allow values greater than 10 throughout your app.  You could
  create a binding template in your core.js like this:
  
    MyApp.LimitBinding = SC.Binding.oneWay().notEmpty().notLessThan(10);
  
  Then anywhere you want to use this binding, just refer to the template like 
  so:
  
    valueBinding: MyApp.LimitBinding.beget("MyApp.someController.value")
    
  Note that when you use binding templates, it is very important that you always
  start by using beget() to extend the template.  If you do not do this, you 
  will end up using the same binding instance throughout your app which will 
  lead to erratic behavior.
  
  h2. How to Manually Activate a Binding

  All of the examples above show you how to configure a custom binding, but the
  result of these customizations will be a binding template, not a fully active
  binding.  The binding will actually become active only when you instantiate
  the object the binding belongs to.  It is useful however, to understand what
  actually happens when the binding is activated.
  
  For a binding to function it must have at least a "from" property and a "to"
  property.  The from property path points to the object/key that you want to
  bind from while the to path points to the object/key you want to bind to.  
  
  When you define a custom binding, you are usually describing the property 
  you want to bind from (such as "MyApp.someController.value" in the examples
  above).  When your object is created, it will automatically assign the value
  you want to bind "to" based on the name of your binding key.  In the examples
  above, during init, SproutCore objects will effectively call something like
  this on your binding:
  
    binding = this.valueBinding.beget().to("value", this) ;
    
  This creates a new binding instance based on the template you provide, and 
  sets the to path to the "value" property of the new object.  Now that the 
  binding is fully configured with a "from" and a "to", it simply needs to be
  connected to become active.  This is done through the connect() method:
  
    binding.connect() ;
    
  Now that the binding is connected, it will observe both the from and to side 
  and relay changes.
  
  If you ever needed to do so (you almost never will, but it is useful to 
  understand this anyway), you could manually create an active binding by 
  doing the following:
  
  {{{
    SC.Binding.from("MyApp.someController.value")
     .to("MyApp.anotherObject.value")
     .connect();
  }}}
     
  You could also use the bind() helper method provided by SC.Object. (This is 
  the same method used by SC.Object.init() to setup your bindings):
  
  {{{
    MyApp.anotherObject.bind("value", "MyApp.someController.value") ;
  }}}

  Both of these code fragments have the same effect as doing the most friendly
  form of binding creation like so:
  
  {{{
    MyApp.anotherObject = SC.Object.create({
      valueBinding: "MyApp.someController.value",
      
      // OTHER CODE FOR THIS OBJECT...
      
    }) ;
  }}}
  
  SproutCore's built in binding creation method make it easy to automatically
  create bindings for you.  You should always use the highest-level APIs 
  available, even if you understand how to it works underneath.
  
  
  @extends Object
  
*/
SC.Binding = {
  
  /**
    This is the core method you use to create a new binding instance.  The
    binding instance will have the receiver instance as its parent which means
    any configuration you have there will be inherited.  
    
    The returned instance will also have its parentBinding property set to the 
    receiver.

    @param fromPath {String} optional from path.
    @returns {SC.Binding} new binding instance
  */
  beget: function(fromPath) {
    var ret = SC.beget(this) ;
    ret.parentBinding = this;
    if (fromPath !== undefined) ret = ret.from(fromPath) ;
    return ret ;
  },
  
  /**
    Returns a builder function for compatibility.  
  */
  builder: function() {
    var binding = this ;
    var ret = function(fromProperty) { return binding.beget().from(fromProperty); };
    ret.beget = function() { return binding.beget(); } ;
    return ret ;
  },
  
  /**
    This will set "from" property path to the specified value.  It will not
    attempt to resolve this property path to an actual object/property tuple
    until you connect the binding.

    The binding will search for the property path starting at the root level 
    unless you specify an alternate root object as the second paramter to this 
    method.  Alternatively, you can begin your property path with either "." or
    "*", which will use the root object of the to side be default.  This special
    behavior is used to support the high-level API provided by SC.Object.
    
    @param propertyPath {String|Tuple} A property path or tuple
    @param root {Object} optional root object to use when resolving the path.
    @returns {SC.Binding} this
  */
  from: function(propertyPath, root) {
    
    // if the propertyPath is null/undefined, return this.  This allows the
    // method to be called from other methods when the fromPath might be 
    // optional. (cf single(), multiple())
    if (!propertyPath) return this ;
    
    // beget if needed.
    var binding = (this === SC.Binding) ? this.beget() : this ;
    binding._fromPropertyPath = propertyPath ;
    binding._fromRoot = root ;
    binding._fromTuple = null ;
    return binding ;
  },
  
  /**
   This will set the "to" property path to the specified value.  It will not 
   attempt to reoslve this property path to an actual object/property tuple
   until you connect the binding.
    
    @param propertyPath {String|Tuple} A property path or tuple
    @param root {Object} optional root object to use when resolving the path.
    @returns {SC.Binding} this
  */
  to: function(propertyPath, root) {
    // beget if needed.
    var binding = (this === SC.Binding) ? this.beget() : this ;
    binding._toPropertyPath = propertyPath ;
    binding._toRoot = root ;
    binding._toTuple = null ; // clear out any existing one.
    return binding ;
  },

  /**
    Attempts to connect this binding instance so that it can receive and relay
    changes.  This method will raise an exception if you have not set the 
    from/to properties yet.
    
    @returns {SC.Binding} this
  */
  connect: function() {
    // If the binding is already connected, do nothing.
    if (this.isConnected) return this ;
    this.isConnected = YES ;
    this._connectionPending = YES ; // its connected but not really...    
    SC.Binding._connectQueue.add(this) ;
    return this; 
  },
  
  /** @private
    Actually connects the binding.  This is done at the end of the runloop
    to give you time to setup your entire object graph before the bindings 
    try to activate.
  */
  _connect: function() {
    if (!this._connectionPending) return; //nothing to do
    this._connectionPending = NO ;

    var path, root ;
    var bench = SC.BENCHMARK_BINDING_SETUP;

    if (bench) SC.Benchmark.start("SC.Binding.connect()");
    
    // try to connect the from side.
    // as a special behavior, if the from property path begins with either a 
    // . or * and the fromRoot is null, use the toRoot instead.  This allows 
    // for support for the SC.Object shorthand:
    //
    // contentBinding: "*owner.value"
    //
    path = this._fromPropertyPath; root = this._fromRoot ;
    if (SC.$type(path) === SC.T_STRING) {
      
      // if the first character is a '.', this is a static path.  make the 
      // toRoot the default root.
      if (path.indexOf('.') === 0) {
        path = path.slice(1);
        if (!root) root = this._toRoot ;
        
      // if the first character is a '*', then setup a tuple since this is a 
      // chained path.
      } else if (path.indexOf('*') === 0) {
        path = [this._fromRoot || this._toRoot, path.slice(1)] ;
        root = null ;
      }
    }
    SC.Observers.addObserver(path, this, this.fromPropertyDidChange, root) ;
    
    // try to connect the to side
    if (!this._oneWay) {
      path = this._toPropertyPath; root = this._toRoot ;
      SC.Observers.addObserver(path, this, this.toPropertyDidChange, root) ;  
    }

    if (bench) SC.Benchmark.end("SC.Binding.connect()");

    // now try to sync if needed
    if (this._syncOnConnect) {
      this._syncOnConnect = NO ;
      if (bench) SC.Benchmark.start("SC.Binding.connect().sync");
      this.sync();
      if (bench) SC.Benchmark.end("SC.Binding.connect().sync");
    }
  },
  
  /**
    Disconnects the binding instance.  Changes will no longer be relayed.  You
    will not usually need to call this method.
    
    @returns {SC.Binding} this
  */
  disconnect: function() {
    if (!this.isConnected) return this; // nothing to do.
    
    // if connection is still pending, just cancel
    if (this._connectionPending) {
      this._connectionPending = NO ;
      
    // connection is completed, disconnect.
    } else {
      SC.Observers.removeObserver(this._fromPropertyPath, this, this.fromPropertyDidChange, this._fromRoot) ;
      if (!this._oneWay) {
        SC.Observers.removeObserver(this._toPropertyPath, this, this.toPropertyDidChange, this._toRoot) ;
      }
    }
    
    this.isConnected = NO ;
    return this ;  
  },

  /**
    Invoked whenever the value of the "from" property changes.  This will mark
    the binding as dirty if the value has changed.
  */
  fromPropertyDidChange: function(target, key) {
    var v = target.get(key) ;

    //console.log("fromPropertyDidChange: %@ v = %@".fmt(this, v)) ;
    
    // if the new value is different from the current binding value, then 
    // schedule to register an update.
    if (v !== this._bindingValue) {

      this._setBindingValue(v) ;
      this._changePending = YES ;
      SC.Binding._changeQueue.add(this) ; // save for later.  
    }
  },

  /**
    Invoked whenever the value of the "to" property changes.  This will mark the
    binding as dirty only if:
    
    - the binding is not one way
    - the value does not match the stored transformedBindingValue
    
    if the value does not match the transformedBindingValue, then it will become
    the new bindingValue. 
  */
  toPropertyDidChange: function(target, key) {
    if (this._oneWay) return; // nothing to do
    
    var v = target.get(key) ;
    
    // if the new value is different from the current binding value, then 
    // schedule to register an update.
    if (v !== this._transformedBindingValue) {
      this._setBindingValue(v) ;
      this._changePending = YES ;
      SC.Binding._changeQueue.add(this) ; // save for later.  
    }
  },
  
  _setBindingValue: function(v) {
    this._bindingValue = v ;
    
    // apply any transforms to get the to property value also
    var transforms = this._transforms;
    if (transforms) {
      var len = transforms.length ;
      for(var idx=0;idx<len;idx++) {
        var transform = transforms[idx] ;
        v = transform(v, this) ;
      }
    }

    // if error objects are not allowed, and the value is an error, then
    // change it to null.
    if (this._noError && SC.$type(v) === SC.T_ERROR) v = null ;
    
    this._transformedBindingValue = v;
  },
  
  _connectQueue: SC.Set.create(),
  _alternateConnectQueue: SC.Set.create(),
  _changeQueue: SC.Set.create(),
  _alternateChangeQueue: SC.Set.create(),
  _changePending: NO,

  /**
    Call this method on SC.Binding to flush all bindings with changed pending.
    
    @returns {Boolean} YES if changes were flushed.
  */
  flushPendingChanges: function() {
    
    // don't allow flushing more than one at a time
    if (this._isFlushing) return NO; 
    this._isFlushing = YES ;

    var didFlush = NO ;
    var log = SC.LOG_BINDING_NOTIFICATIONS ;
    
    // connect any bindings
    var queue, binding ;
    while((queue = this._connectQueue).length >0) {
      this._connectQueue = this._alternateConnectQueue ;
      this._alternateConnectQueue = queue ;
      while(binding = queue.pop()) binding._connect() ;
    }
    
    // loop through the changed queue...
    while ((queue = this._changeQueue).length > 0) {
      if (log) console.log("Begin: Trigger changed bindings") ;
      
      didFlush = YES ;
      
      // first, swap the change queues.  This way any binding changes that
      // happen while we flush the current queue can be queued up.
      this._changeQueue = this._alternateChangeQueue ;
      this._alternateChangeQueue = queue ;
      
      // next, apply any bindings in the current queue.  This may cause 
      // additional bindings to trigger, which will end up in the new active 
      // queue.
      while(binding = queue.pop()) binding.applyBindingValue() ;
      
      // now loop back and see if there are additional changes pending in the
      // active queue.  Repeat this until all bindings that need to trigger 
      // have triggered.
      if (log) console.log("End: Trigger changed bindings") ;
    }
    
    // clean up
    this._isFlushing = NO ;
    return didFlush ;
  },
  
  /**
    This method is called at the end of the Run Loop to relay the changed 
    binding value from one side to the other.
  */
  applyBindingValue: function() {
    this._changePending = NO ;

    // compute the binding targets if needed.
    this._computeBindingTargets() ;
    
    var v = this._bindingValue ;
    var tv = this._transformedBindingValue ;
    var bench = SC.BENCHMARK_BINDING_NOTIFICATIONS ;
    var log = SC.LOG_BINDING_NOTIFICATIONS ; 
    
    // the from property value will always be the binding value, update if 
    // needed.
    if (!this._oneWay && this._fromTarget) {
      if (log) console.log("%@: %@ -> %@".fmt(this, v, tv)) ;
      if (bench) SC.Benchmark.start(this.toString() + "->") ;
      this._fromTarget.setPathIfChanged(this._fromPropertyKey, v) ;
      if (bench) SC.Benchmark.end(this.toString() + "->") ;
    }
    
    // update the to value with the transformed value if needed.
    if (this._toTarget) {
      if (log) console.log("%@: %@ <- %@".fmt(this, v, tv)) ;
      if (bench) SC.Benchmark.start(this.toString() + "<-") ;
      this._toTarget.setPathIfChanged(this._toPropertyKey, tv) ;
      if (bench) SC.Benchmark.start(this.toString() + "<-") ;
    }
  },

  /**
    Calling this method on a binding will cause it to check the value of the 
    from side of the binding matches the current expected value of the 
    binding. If not, it will relay the change as if the from side's value has 
    just changed.
    
    This method is useful when you are dynamically connecting bindings to a 
    network of objects that may have already been initialized.
  */
  sync: function() {

    // do nothing if not connected
    if (!this.isConnected) return this;
    
    // connection is pending, just note that we should sync also
    if (this._connectionPending) {
      this._syncOnConnect = YES ;
      
    // we are connected, go ahead and sync
    } else {
      this._computeBindingTargets() ;
      var target = this._fromTarget ;
      var key = this._fromPropertyKey ;
      if (!target || !key) return this ; // nothing to do

      // get the new value
      var v = target.getPath(key) ;

      // if the new value is different from the current binding value, then 
      // schedule to register an update.
      if (v !== this._bindingValue) {
        this._setBindingValue(v) ;
        this._changePending = YES ;
        SC.Binding._changeQueue.add(this) ; // save for later.  
      }
    }
    
    return this ;
  },
  
  // set if you call sync() when the binding connection is still pending.
  _syncOnConnect: NO,
  
  _computeBindingTargets: function() {
    if (!this._fromTarget) {

      var path, root, tuple ;
      
      // if the fromPropertyPath begins with a . or * then we may use the 
      // toRoot as the root object.  Similar code exists in connect() so if 
      // you make a change to one be sure to update the other.
      path = this._fromPropertyPath; root = this._fromRoot ;
      if (SC.$type(path) === SC.T_STRING) {
        
        // static path beginning with the toRoot
        if (path.indexOf('.') === 0) {
          path = path.slice(1) ; // remove the .
          if (!root) root = this._toRoot; // use the toRoot optionally
          
        // chained path beginning with toRoot.  Setup a tuple
        } else if (path.indexOf('*') === 0) {
          path = [root || this._toRoot, path.slice(1)];
          root = null ;
        }
      }
      
      tuple = SC.tupleForPropertyPath(path, root) ;
      if (tuple) {
        this._fromTarget = tuple[0]; this._fromPropertyKey = tuple[1] ;
      }
    }

    if (!this._toTarget) {
      path = this._toPropertyPath; root = this._toRoot ;
      tuple = SC.tupleForPropertyPath(path, root) ;
      if (tuple) {
        this._toTarget = tuple[0]; this._toPropertyKey = tuple[1] ;
      }
    }
  },
  
  /**
    Configures the binding as one way.  A one-way binding will relay changes
    on the "from" side to the "to" side, but not the other way around.  This
    means that if you change the "to" side directly, the "from" side may have 
    a different value.
    
    @param fromPath {String} optional from path to connect.
    @param aFlag {Boolean} Optionally pass NO to set the binding back to two-way
    @returns {SC.Binding} this
  */
  oneWay: function(fromPath, aFlag) {
    
    // If fromPath is a bool but aFlag is undefined, swap.
    if ((aFlag === undefined) && (SC.$type(fromPath) === SC.T_BOOL)) {
      aFlag = fromPath; fromPath = null ;
    }
    
    // beget if needed.
    var binding = this.from(fromPath) ;
    if (binding === SC.Binding) binding = binding.beget() ;
    binding._oneWay = (aFlag === undefined) ? YES : aFlag ;
    return binding ;
  },
  
  /**
    Adds the specified transform function to the array of transform functions.
    
    The function you pass must have the following signature:
    
    {{{
      function(value) {} ;
    }}}
    
    It must return either the transformed value or an error object.  
        
    Transform functions are chained, so they are called in order.  If you are
    extending a binding and want to reset the transforms, you can call
    resetTransform() first.
    
    @param transformFunc {Function} the transform function.
    @returns {SC.Binding} this
  */
  transform: function(transformFunc) {
    var binding = (this === SC.Binding) ? this.beget() : this ;
    var t = binding._transforms ;
    
    // clone the transform array if this comes from the parent
    if (t && (t === binding.parentBinding._transform)) {
      t = binding._transforms = t.slice() ;
    }
    
    // create the transform array if needed.
    if (!t) t = binding._transforms = [] ;
    
    // add the transform function
    t.push(transformFunc) ;
    return binding;
  },
  
  /**
    Resets the transforms for the binding.  After calling this method the 
    binding will no longer transform values.  You can then add new transforms
    as needed.
  
    @returns {SC.Binding} this
  */
  resetTransforms: function() {
    var binding = (this === SC.Binding) ? this.beget() : this ;
    binding._transforms = null ; return binding ;
  },
  
  /**
    Specifies that the binding should not return error objects.  If the value
    of a binding is an Error object, it will be transformed to a null value
    instead.
    
    Note that this is not a transform function since it will be called at the
    end of the transform chain.
    
    @param fromPath {String} optional from path to connect.
    @param aFlag {Boolean} optionally pass NO to allow error objects again.
    @returns {SC.Binding} this
  */
  noError: function(fromPath, aFlag) {
    // If fromPath is a bool but aFlag is undefined, swap.
    if ((aFlag === undefined) && (SC.$type(fromPath) === SC.T_BOOL)) {
      aFlag = fromPath; fromPath = null ;
    }
    
    // beget if needed.
    var binding = this.from(fromPath) ;
    if (binding === SC.Binding) binding = binding.beget() ;
    binding._noError = (aFlag === undefined) ? YES : aFlag ;
    return binding ;
  },
  
  /**
    Adds a transform to the chain that will allow only single values to pass.
    This will allow single values, nulls, and error values to pass through.  If
    you pass an array, it will be mapped as so:
    
    {{{
      [] => null
      [a] => a
      [a,b,c] => Multiple Placeholder
    }}}
    
    You can pass in an optional multiple placeholder or it will use the 
    default.
    
    Note that this transform will only happen on forwarded valued.  Reverse
    values are send unchanged.
    
    @param fromPath {String} from path or null
    @param placeholder {Object} optional placeholder value.
    @returns {SC.Binding} this
  */
  single: function(fromPath, placeholder) {
    if (placeholder === undefined) {
      placeholder = SC.MULTIPLE_PLACEHOLDER ;
    }
    return this.from(fromPath).transform(function(value, isForward) {
      if (SC.isArray(value)) {
        value = (value.length>1) ? placeholder : (value.length<=0) ? null : (value.objectAt ? value.objectAt(0) : value[0]);
      }
      return value ;
    }) ;
  },
  
  /** 
    Adds a transform that will return the placeholder value if the value is 
    null, undefined, an empty array or an empty string.  See also notNull().
    
    @param fromPath {String} from path or null
    @param placeholder {Object} optional placeholder.
    @returns {SC.Binding} this
  */
  notEmpty: function(fromPath, placeholder) {
    if (placeholder === undefined) placeholder = SC.EMPTY_PLACEHOLDER ;
    return this.from(fromPath).transform(function(value, isForward) {
      if (SC.none(value) || (value === '') || (SC.isArray(value) && value.length === 0)) {
        value = placeholder ;
      }
      return value ;
    }) ;
  },
  
  /**
    Adds a transform that will return the placeholder value if the value is
    null.  Otherwise it will passthrough untouched.  See also notEmpty().
    
    @param fromPath {String} from path or null
    @param placeholder {Object} optional placeholder;
    @returns {SC.Binding} this
  */
  notNull: function(fromPath, placeholder) {
    if (placeholder === undefined) placeholder = SC.EMPTY_PLACEHOLDER ;
    return this.from(fromPath).transform(function(value, isForward) {
      if (SC.none(value)) value = placeholder ;
      return value ;
    }) ;
  },

  /** 
    Adds a transform that will convert the passed value to an array.  If 
    the value is null or undefined, it will be converted to an empty array.

    @param fromPath {String} optional from path
    @returns {SC.Binding} this
  */
  multiple: function(fromPath) {
    return this.from(fromPath).transform(function(value) {
      if (!SC.isArray(value)) value = (value == null) ? [] : [value] ;
      return value ;
    }) ;
  },
  
  /**
    Adds a transform to convert the value to a bool value.  If the value is
    an array it will return YES if array is not empty.  If the value is a string
    it will return YES if the string is not empty.
  
    @param fromPath {String} optional from path
    @returns {SC.Binding} this
  */
  bool: function(fromPath) {
    return this.from(fromPath).transform(function(v) {
      var t = SC.$type(v) ;
      if (t === SC.T_ERROR) return v ;
      return (t == SC.T_ARRAY) ? (v.length > 0) : (v === '') ? NO : !!v ;
    }) ;
  },
  
  /**
    Adds a transform to convert the value to the inverse of a bool value.  This
    uses the same transform as bool() but inverts it.
    
    @param fromPath {String} optional from path
    @returns {SC.Binding} this
  */
  not: function(fromPath) {
    return this.from(fromPath).transform(function(v) {
      var t = SC.$type(v) ;
      if (t === SC.T_ERROR) return v ;
      return !((t == SC.T_ARRAY) ? (v.length > 0) : (v === '') ? NO : !!v) ;
    }) ;
  },
  
  /**
    Adds a transform that will return YES if the value is null, NO otherwise.
    
    @returns {SC.Binding} this
  */
  isNull: function(fromPath) {
    return this.from(fromPath).transform(function(v) { 
      var t = SC.$type(v) ;
      return (t === SC.T_ERROR) ? v : SC.none(v) ;
    });
  },
  
  toString: function() {
    var from = this._fromRoot ? "<%@>:%@".fmt(this._fromRoot,this._fromPropertyPath) : this._fromPropertyPath;

    var to = this._toRoot ? "<%@>:%@".fmt(this._toRoot,this._toPropertyPath) : this._toPropertyPath;
    
    return "SC.Binding%@(%@ -> %@)".fmt(SC.guidFor(this), from, to) ;
  }  
} ;

/** 
  Shorthand method to define a binding.  This is the same as calling:
  
  {{{
    SC.binding(path) = SC.Binding.from(path)
  }}}
*/
SC.binding = function(path, root) { return SC.Binding.from(path,root); } ;


// ......................................
// DEPRECATED
//
// The transforms below are deprecated but still available for backwards 
// compatibility.  Instead of using these methods, however, you should use
// the helpers.  For example, where before you would have done:
//
//  contentBinding: SC.Binding.Single('MyApp.myController.count') ;
//
// you should do:
//
//  contentBinding. SC.Binding.from('MyApp.myController.count').single();
//
// and for defaults:
//
//  contentBindingDefault: SC.Binding.single()
//
SC.Binding.From = SC.Binding.NoChange = SC.Binding.builder();

SC.Binding.Single = SC.Binding.single().builder() ;
SC.Binding.SingleNull = SC.Binding.single(null).builder() ;
SC.Binding.SingleNoError = SC.Binding.Single.beget().noError().builder() ;
SC.Binding.SingleNullNoError = SC.Binding.SingleNull.beget().noError().builder() ;
SC.Binding.Multiple = SC.Binding.multiple().builder() ;
SC.Binding.MultipleNoError = SC.Binding.multiple().noError().builder() ;

SC.Binding.Bool = SC.Binding.bool().builder() ;
SC.Binding.Not = SC.Binding.bool().not().builder() ;
SC.Binding.NotNull = SC.Binding.isNull().not().builder() ;
SC.Binding.IsNull = SC.Binding.isNull().builder() ;

// No Error versions.
SC.Binding.BoolNoError = SC.Binding.Bool.beget().noError().builder();
SC.Binding.NotNullNoError = SC.Binding.NotNull.beget().noError().builder();
SC.Binding.NotNoError = SC.Binding.Not.beget().noError().builder();
SC.Binding.IsNullNoError = SC.Binding.IsNull.beget().noError().builder() ;



/* End ------------------------------------------------------- system/binding.js*/

/* Start ----------------------------------------------------- views/mixins/responder.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core') ;
require('system/object') ;
require('application/input_manager');

/** @static

  The Responder mixin provides common methods needed to respond to user-interface events in SproutCore.
  
  @namespace
  @since SproutCore 1.0
*/
SC.Responder = {

  /** @property
    The pane this responder belongs to.  This is used to determine where you belong to in the responder chain.
  */
  pane: null,
  
  /** @property
    This is the nextResponder in the responder chain.  If the receiver does not
    implement a particular event handler, it will bubble to the next responder.
  */
  nextResponder: null,
  
  /** @property 
    YES if the view is currently first responder.  This property is always edited 
    by the pane during its makeFirstResponder() method.
  */
  isFirstResponder: NO,
  
  /** @property
    YES if the view is currently first responder and the pane the view belongs to
    is also key pane.  While this property is set, you should expect to receive 
    keyboard events.
  */
  isKeyResponder: NO,
  
  /** @property
    Set to YES if your view is willing to accept first responder status.  This is used when calculcating key responder loop.
  */
  acceptsFirstResponder: NO,
  
  /** 
    Call this method on your view or responder to make it become first responder.
    
    @returns {SC.Responder} receiver
  */
  becomeFirstResponder: function() {  
    var pane = this.get('pane');
    if (pane && this.get('acceptsFirstResponder')) {
      if (pane.get('firstResponder') !== this) pane.makeFirstResponder(this);
    } 
    return this ;
  },
  
  /**
    Call this method on your view or responder to resign your first responder status. Normally this is not necessary since you will lose first responder status automatically when another view becomes first responder.
    
    @returns {SC.Responder} receiver
  */
  resignFirstResponder: function() {
    var pane = this.get('pane');
    if (pane && (pane.get('firstResponder') === this)) {
      pane.makeFirstResponder(null);
    }
    return YES;  
  },

  /**
    This method will be called just before you lose first responder status.
  */
  willLoseFirstResponder: function() {},
  
  /**
    This method is invoked just before you lost the key responder status.  The passed view is the view that is about to gain keyResponder status.  This gives you a chance to do any early setup.
    
    Remember that you can gain/lose key responder status either because another view in the same pane is becoming first responder or because another pane is about to become key.
    
    @param {SC.Responder} responder
  */
  willLoseKeyResponderTo: function(responder) {},
  
  /**
    This method is invoked just before you become the key responder.  The passed view is the view that is about to lose keyResponder status.  You can use this to do any setup before the view changes.
    
    Remember that you can gain/lose key responder status either because another view in the same pane is becoming first responder or because another pane is about to become key.
    
    @param {SC.Responder} responder
  */
  willBecomeKeyResponderFrom: function(responder) {},
  
  /**
    Invokved just after the responder loses key responder status.
  */
  didLoseKeyResponderTo: function(responder) {},
  
  /**
    Invoked just after the responder gains key responder status.
  */
  didBecomeKeyResponderFrom: function(responder) {},
  
  /**
    This method will be called just before you become the first responder
    so you can make changes.
  */
  didBecomeFirstResponder: function() {},
  
  /**
    This method will process a key input event, attempting to convert it to an appropriate action method and sending it up the responder chain.  The event is converted using the SC.KEY_BINDINGS hash, which maps key events into method names.  If no key binding is found, then the key event will be passed along using an insertText() method.
    
    @param {SC.Event} event
    @returns {Object} object that handled event, if any
  */
  interpretKeyEvents: function(event) {
    var codes = event.commandCodes();
    var cmd = codes[0]; var chr = codes[1];
    if (!cmd && !chr) return null ;  //nothing to do.

    // if this is a command key, try to do something about it.
    if (cmd) {
      var methodName = SC.MODIFIED_KEY_BINDINGS[cmd] || SC.BASE_KEY_BINDINGS[cmd.split('_').last()];
      if (methodName) {
        var target = this, pane = this.get('pane'), handler = null;
        while(target && !(handler = target.tryToPerform(methodName, event))){
          target = (target===pane)? null: target.get('nextResponder') ;
        }
        return handler ;
      }
    } 

    //if(cmd == "space") chr = " ";
    if ( chr && this.respondsTo('insertText')) {
      // if we haven't returned yet and there is plain text, then do an insert 
      // of the text.  Since this is not an action, do not send it up the 
      // responder chain.
     return this.insertText(chr);
    }

    return null ; //nothing to do.
  },
  
  /**
    This method is invoked by interpretKeyEvents() when you receive a key event matching some plain text.  You can use this to actually insert the text into your application, if needed.
    
    @param {SC.Event} event
    @returns {Object} receiver or object that handled event
  */
  insertText: function(chr) {
    return this ;
  }

};


/* End ------------------------------------------------------- views/mixins/responder.js*/

/* Start ----------------------------------------------------- system/mixins/delegate_support.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/**
  @namespace
  
  Support methods for the Delegate design pattern.
  
  The Delegate design pattern makes it easy to delegate a portion of your 
  application logic to another object.  This is most often used in views to 
  delegate various application-logic decisions to controllers in order to 
  avoid having to bake application-logic directly into the view itself.
  
  The methods provided by this mixin make it easier to implement this pattern
  but they are not required to support delegates.
  
  h2. The Pattern
  
  The delegate design pattern typically means that you provide a property,
  usually ending in "delegate", that can be set to another object in the 
  system.  
  
  When events occur or logic decisions need to be made that you would prefer
  to delegate, you can call methods on the delegate if it is set.  If the 
  delegate is not set, you should provide some default functionality instead.
  
  Note that typically delegates are not observable, hence it is not necessary
  to use get() to retrieve the value of the delegate.
  
*/
SC.DelegateSupport = {  
  
  /**
    Invokes the named method on the delegate that you pass.  If no delegate
    is defined or if the delegate does not implement the method, then a 
    method of the same name on the receiver will be called instead.  
    
    You can pass any arguments you want to pass onto the delegate after the
    delegate and methodName.
    
    @param {Object} delegate a delegate object.  May be null.
    @param {String} methodName a method name
    @param {*} args (OPTIONAL) any additional arguments
    
    @returns value returned by delegate
  */
  invokeDelegateMethod: function(delegate, methodName, args) {
    args = SC.$A(arguments); args = args.slice(2, args.length) ;
    if (!delegate || !delegate[methodName]) delegate = this ;
    return delegate[methodName].apply(delegate, args) ;
  },
  
  /**
    Gets the named property from the delegate if the delegate exists and it
    defines the property.  Otherwise, gets the property from the receiver.
    
    @param {Object} delegate the delegate or null
    @param {String} key the property to get.
  */
  getDelegateProperty: function(delegate, key) {
    return (delegate && (delegate[key] != null)) ? delegate.get(key) : this.get(key) ;
  }
  
};


/* End ------------------------------------------------------- system/mixins/delegate_support.js*/

/* Start ----------------------------------------------------- system/mixins/string.js*/

// ==========================================================================
// SproutCore -- JavaScript Application Framework
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
// ==========================================================================

// These are basic enhancements to the string class used throughout 
// SproutCore.
SC.STRING_TITLEIZE_REGEXP = (/([\s|\-|\_|\n])([^\s|\-|\_|\n]?)/g);

/**
  @namespace
  
  SproutCore implements a variety of enhancements to the built-in String 
  object that make it easy to perform common substitutions and conversions.
  
  Most of the utility methods defined here mirror those found in Prototype
  1.6.
  
  @since SproutCore 1.0
*/
SC.String = {
  
  // Interpolate string. looks for %@ or %@1; to control the order of params.
  /**
    Apply formatting options to the string.  This will look for occurrences
    of %@ in your string and substitute them with the arguments you pass into
    this method.  If you want to control the specific order of replacement, 
    you can add a number after the key as well to indicate which argument 
    you want to insert.  

    Ordered insertions are most useful when building loc strings where values
    you need to insert may appear in different orders.

    h3. Examples
    
    {{{
      "Hello %@ %@".fmt('John', 'Doe') => "Hello John Doe"
      "Hello %@2, %@1".fmt('John', 'Doe') => "Hello Doe, John"
    }}}
    
    @param args {Object...} optional arguments
    @returns {String} formatted string
  */
  fmt: function() {
    // first, replace any ORDERED replacements.
    var args = arguments;
    var idx  = 0; // the current index for non-numerical replacements
    return this.replace(/%@([0-9]+)?/g, function(s, argIndex) {
      argIndex = (argIndex) ? parseInt(argIndex,0)-1 : idx++ ;
      s =args[argIndex];
      return ((s===null) ? '(null)' : (s==undefined) ? '' : s).toString(); 
    }) ;
  },

  /**
    Localizes the string.  This will look up the reciever string as a key 
    in the current Strings hash.  If the key matches, the loc'd value will be
    used.  The resulting string will also be passed through fmt() to insert
    any variables.
    
    @param args {Object...} optional arguments to interpolate also
    @returns {String} the localized and formatted string.
  */
  loc: function() {
    // NB: This could be implemented as a wrapper to locWithDefault() but
    // it would add some overhead to deal with the arguments and adds stack
    // frames, so we are keeping the implementation separate.
    var str = SC.Locale.currentLocale.locWithDefault(this) || this;
    return str.fmt.apply(str,arguments) ;
  },

  /**
    Works just like loc() except that it will return the passed default 
    string if a matching key is not found.
    
    @param {String} def the default to return
    @param {Object...} args optional formatting arguments
    @returns {String} localized and formatted string
  */
  locWithDefault: function(def) {
    var str = SC.Locale.currentLocale.locWithDefault(def) || this;
    var args = SC.$A(arguments); args.shift(); // remove def param
    return str.fmt.apply(str,args) ;
  },
  
  /** 
    Capitalizes a string.

    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | My favorite items |
    | css-class-name | Css-class-name |
    | action_name | Action_name |
    | innerHTML | InnerHTML |

    @return {String} capitalized string
  */
  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.slice(1) ;
  },
  
  /**
    Capitalizes every word in a string.  Unlike titleize, spaces or dashes 
    will remain in-tact.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | My Favorite Items |
    | css-class-name | Css-Class-Name |
    | action_name | Action_Name |
    | innerHTML | InnerHTML |

    @returns {String} capitalized string
  */
  capitalizeEach: function() {
    return this.replace(SC.STRING_TITLEIZE_REGEXP, 
      function(str,sep,character) { 
        return (character) ? (sep + character.toUpperCase()) : sep;
      }).capitalize() ;
  },

  /**
    Converts a string to a title.  This will decamelize the string, convert
    separators to spaces and capitalize every word.

    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | My Favorite Items |
    | css-class-name | Css Class Name |
    | action_name | Action Name |
    | innerHTML | Inner HTML |

    @return {String} titleized string.
  */
  titleize: function() {
    var ret = this.replace(/([a-z])([A-Z])/g,'$1_$2'); // decamelize
    return ret.replace(SC.STRING_TITLEIZE_REGEXP, 
      function(str,separater,character) { 
        return (character) ? (' ' + character.toUpperCase()) : ' ';
      }).capitalize() ;
  },
  
  /**
    Camelizes a string.  This will take any words separated by spaces, dashes
    or underscores and convert them into camelCase.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | myFavoriteItems |
    | css-class-name | cssClassName |
    | action_name | actionName |
    | innerHTML | innerHTML |

    @returns {String} camelized string
  */
  camelize: function() {
    var ret = this.replace(SC.STRING_TITLEIZE_REGEXP, 
      function(str,separater,character) { 
        return (character) ? character.toUpperCase() : '' ;
      }) ;
    var first = ret.charAt(0), lower = first.toLowerCase() ;
    return (first !== lower) ? (lower + ret.slice(1)) : ret ;
  },
  
  /**
    Converts the string into a class name.  This method will camelize your 
    string and then capitalize the first letter.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | MyFavoriteItems |
    | css-class-name | CssClassName |
    | action_name | ActionName |
    | innerHTML | InnerHtml |

    @returns {String}
  */
  classify: function() {
    var ret = this.replace(SC.STRING_TITLEIZE_REGEXP, 
      function(str,separater,character) { 
        return (character) ? character.toUpperCase() : '' ;
      }) ;
    var first = ret.charAt(0), upper = first.toUpperCase() ;
    return (first !== upper) ? (upper + ret.slice(1)) : ret ;
  },
  
  /**
    Converts a camelized string into all lower case separated by underscores.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | my favorite items |
    | css-class-name | css-class-name |
    | action_name | action_name |
    | innerHTML | inner_html |

    @returns {String} the decamelized string.
  */
  decamelize: function() { 
    return this.replace(/([a-z])([A-Z])/g,'$1_$2').toLowerCase();
  },

  /**
    Converts a camelized string or a string with spaces or underscores into
    a string with components separated by dashes.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | my-favorite-items |
    | css-class-name | css-class-name |
    | action_name | action-name |
    | innerHTML | inner-html |

    @returns {String} the dasherized string.
  */
  dasherize: function() {
    return this.decamelize().replace(/[ _]/g,'-') ;  
  },
  
  /**
    Converts a camelized string or a string with dashes or underscores into
    a string with components separated by spaces.
    
    h2. Examples
    
    | *Input String* | *Output String* |
    | my favorite items | my favorite items |
    | css-class-name | css class name |
    | action_name | action name |
    | innerHTML | inner html |

    @returns {String} the humanized string.
  */
  humanize: function() {
    return this.decamelize().replace(/[-_]/g,' ') ;
  },
  
  /**
    Removes any extra whitespace from the edges of the strings. This method is 
    also aliased as strip().
    
    @returns {String} the trimmed string
  */
  trim: function () {
    return this.replace(/^\s+|\s+$/g,"");
  },
  
  /** Splits the string into words, separated by spaces.  */
  w: function() { return this.split(' '); }
    
} ;

SC.String.strip = SC.String.trim; // convenience alias.

// Apply SC.String mixin to built-in String object
SC.mixin(String.prototype, SC.String) ;


/* End ------------------------------------------------------- system/mixins/string.js*/

/* Start ----------------------------------------------------- views/view.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/browser');
require('system/object') ;
require('system/core_query');
require('system/event');
require('system/binding');

require('views/mixins/responder') ;
require('system/mixins/delegate_support') ;
require('system/mixins/string') ;

SC.viewKey = SC.guidKey + "_view" ;

/** @private */
SC.DISPLAY_LOCATION_QUEUE = 'updateDisplayLocationIfNeeded';
// SC.VIEW_HIERARCHY_QUEUE = 'updateViewHierarchyIfNeeded'; // <-- How about using this name? -E

/** @private */
SC.UPDATE_CHILD_LAYOUT_QUEUE   = 'updateChildLayoutIfNeeded';

/** @private */
SC.DISPLAY_LAYOUT_QUEUE   = 'updateDisplayLayoutIfNeeded';
// SC.APPLY_LAYOUT_QUEUE   = 'applyLayoutIfNeeded'; // <-- How about using this name? -E

/** @private */
SC.DISPLAY_UPDATE_QUEUE   = 'updateDisplayIfNeeded';

/** @private Properties that require the empty element to be recached. */
SC.EMPTY_ELEMENT_PROPERTIES = 'emptyElement tagName styleClass'.w();

/** Select a horizontal layout for various views.*/
SC.LAYOUT_HORIZONTAL = 'sc-layout-horizontal';

/** Select a vertical layout for various views.*/
SC.LAYOUT_VERTICAL = 'sc-layout-vertical';

/** @private */
SC._VIEW_DEFAULT_DIMS = 'marginTop marginLeft'.w();

/**
  Layout properties needed to anchor a view to the top.
*/
SC.ANCHOR_TOP = { top: 0 };

/**
  Layout properties needed to anchor a view to the left.
*/
SC.ANCHOR_LEFT = { left: 0 };

/*
  Layout properties to anchor a view to the top left 
*/
SC.ANCHOR_TOP_LEFT = { top: 0, left: 0 };

/**
  Layout properties to anchoe view to the bottom.
*/
SC.ANCHOR_BOTTOM = { bottom: 0 };

/**
  Layout properties to anchor a view to the right.
*/
SC.ANCHOR_RIGHT = { right: 0 } ;

/**
  Layout properties to anchor a view to the bottom right.
*/
SC.ANCHOR_BOTTOM_RIGHT = { bottom: 0, right: 0 };

/**
  Layout properties to take up the full width of a parent view.
*/
SC.FULL_WIDTH = { left: 0, right: 0 };

/**
  Layout properties to take up the full height of a parent view.
*/
SC.FULL_HEIGHT = { top: 0, bottom: 0 };

/**
  Layout properties to center.  Note that you must also specify a width and
  height for this to work.
*/
SC.ANCHOR_CENTER = { centerX: 0, centerY: 0 };

/** 
  @class
  
  Base class for managing a view.  View's provide two functions:
  
  1. They translate state and events into drawing instructions for the 
     web browser and
  
  2. They act as first responders for incoming keyboard, mouse, and 
     touch events.
  
  h2. View Initialization
  
  When a view is setup, there are several methods you can override that 
  will be called at different times depending on how your view is created.
  Here is a guide to which method you want to override and when:
  
  - *prepareDisplay:* override this method for perform one-time setup on 
    your view's HTML, such as copying in CSS class names and rendering 
    structural HTML based on configuration options.  This method will only 
    be called once when the view is created and the HTML it produces may be 
    saved and reused at runtime, avoiding a call to this method again.
    
  - *updateDisplay:* override this method to update your HTML to reflect 
    any changes to the state of your view.  This method will also be called 
    once on view init unless you set updateContentOnPrepare to NO.
    
  - *init:* override this method for any general object setup (such as 
    observers, starting timers and animations, etc) that you need to happen 
    everytime the view is created, regardless of whether or not its HTML has 
    been cached.
  
  @extends SC.Object
  @extends SC.Responder
  @extends SC.DelegateSupport
  @since SproutCore 1.0
*/
SC.View = SC.Object.extend(SC.Responder, SC.DelegateSupport,
/** @scope SC.View.prototype */ {

  concatenatedProperties: ['outlets','displayProperties', 'layoutProperties', 'styleClass', 'updateChildLayoutMixin', 'updateDisplayMixin', 'prepareDisplayMixin'],
  
  /** 
    The current pane. 
    @property {SC.Pane}
  */
  pane: function() {
    var view = this;
    while(view && !view.isPane) view = view.get('parentView');
    return view;
  }.property('parentView').cacheable(),
  
  /**
    The page this view was instantiated from.  This is set by the page object
    during instantiation.
    
    @property {SC.Page}
  */
  page: null,
    
  /** 
    The current split view this view is embedded in (may be null). 
    @property {SC.SplitView}
  */
  splitView: function() {
    // console.log('splitView called');
    var view = this;
    while(view && !view.isSplitView) view = view.get('parentView');
    return view;
  }.property('parentView').cacheable(),
  
  /**
    If the view is currently inserted into the DOM of a parent view, this
    property will point to the parent of the view.
  */
  parentView: null,

  // ..........................................................
  // CHILD VIEW SUPPORT
  // 
  
  /** 
    Array of child views.  You should never edit this array directly unless
    you are implementing createChildViews().  Most of the time, you should
    use the accessor methods such as appendChild(), insertBefore() and 
    removeChild().
    
    @property {Array} 
  */
  childViews: [],
  
  /** Outlets */
  outlets: [],
  
  /** 
    Set to true when the item is enabled. 

    Changing this property by default calls the 
    updateChildViewEnabledStates(), which will simply change the isEnabled 
    property on all child views as well.  You can add your own observer on
    this property to make specific changes to the appearance of your view as 
    well. 
    
    Note that if you apply the SC.Control mixin, changing this property will
    also automatically add or remove a 'disabled' CSS class name as well.
    
    This property is observable and bindable.
    
    @property {Boolean}
  */
  isEnabled: YES,
  isEnabledBindingDefault: SC.Binding.oneWay().bool(),
  
  updateChildViewEnabledStates: function() {
    var isEnabled = this.get('isEnabled');
    this.get('childViews').invoke('set','isEnabled', isEnabled);
  }.observes('isEnabled'),
  
  /**
    Insert the view into the the receiver's childNodes array.
    
    The view will be added to the childNodes array before the beforeView.  If 
    beforeView is null, then the view will be added to the end of the array.  
    This will also add the view's rootElement DOM node to the receivers 
    containerElement DOM node as a child.

    If the specified view already belongs to another parent, it will be 
    removed from that view first.
    
    @param {SC.View} view
    @param {SC.View} beforeView
    @returns {SC.View} the receiver
  */
  insertBefore: function(view, beforeView) { 
    
    view.beginPropertyChanges(); // limit notifications

    // remove view from old parent if needed.  Also notify views.
    if (view.get('parentView')) view.removeFromParent() ;
    if (this.willAddChild) this.willAddChild(this, beforeView) ;
    if (view.willAddToParent) view.willAddToParent(this, beforeView) ;

    // set parentView of child
    view.set('parentView', this);
    
    // add to childView's array.
    var idx, childViews = this.get('childViews') ;
    idx = (beforeView) ? childViews.indexOf(beforeView) : childViews.length;
    if (idx<0) idx = childViews.length ;
    childViews.insertAt(idx, view) ;

    // The DOM will need some fixing up, note this on the view.
    view.parentViewDidChange() ;
    view.displayLayoutDidChange() ;

    // notify views
    if (this.didAddChild) this.didAddChild(this, beforeView) ;
    if (view.didAddToParent) view.didAddToParent(this, beforeView) ;
    
    view.endPropertyChanges();
    
    return this ;
  },
  
  /**
    Removes the child view from the parent view.  
    
    @param {SC.View} view
    @returns {SC.View} receiver
  */
  removeChild: function(view) {
    if (!view) return this; // nothing to do
    if (view.parentView !== this) {
      throw "%@.removeChild(%@) must belong to parent".fmt(this,view);
    }

    // notify views
    if (view.willRemoveFromParent) view.willRemoveFromParent() ;
    if (this.willRemoveChild) this.willRemoveChild(view) ;

    // update parent node
    view.set('parentView', null) ;
    
    // remove view from childViews array.
    var childViews = this.get('childViews') ;
    var idx = childViews.indexOf(view) ;
    if (idx>=0) childViews.removeAt(idx);

    // The DOM will need some fixing up, note this on the view.
    view.parentViewDidChange() ;

    // notify views
    if (view.didRemoveFromParent) view.didRemoveFromParent(this) ;
    if (this.didRemoveChild) this.didRemoveChild(view);
    
    return this ;
  },
  
  /**
    Removes all children from the parentView.  
    
    @returns {SC.View} receiver 
  */
  removeAllChildren: function() {
    var childViews = this.get('childViews'), view ;
    while(view = childViews.objectAt(childViews.get('length')-1)) {
      this.removeChild(view) ;
    }
    return this ;
  },
  
  /** 
    Removes the view from its parentView, if one is found.  Otherwise
    does nothing.
    
    @returns {SC.View} receiver
  */
  removeFromParent: function() {
    var parent = this.get('parentView') ;
    if (parent) parent.removeChild(this) ;
    return this ;
  },

  /**
    Replace the oldView with the specified view in the receivers childNodes 
    array. This will also replace the DOM node of the oldView with the DOM 
    node of the new view in the receivers DOM.

    If the specified view already belongs to another parent, it will be 
    removed from that view first.

    @param view {SC.View} the view to insert in the DOM
    @param view {SC.View} the view to remove from the DOM.
    @returns {SC.View} the receiver
  */
  replaceChild: function(view, oldView) {
    
    // suspend notifications
    view.beginPropertyChanges();
    oldView.beginPropertyChanges();
    this.beginPropertyChanges();
    
    this.insertBefore(view,oldView).removeChild(oldView) ;
    
    // resume notifications
    this.endPropertyChanges();
    oldView.endPropertyChanges();
    view.endPropertyChanges(); 
    
    return this;
  },

  /**
    Appends the specified view to the end of the receivers childViews array.  
    This is equivalent to calling insertBefore(view, null);
    
    @param view {SC.View} the view to insert
    @returns {SC.View} the receiver 
  */
  appendChild: function(view) {
    return this.insertBefore(view, null);
  },
    
  /** 
    This method is called whenever the receiver's parentView has changed.  
    The default implementation of this method marks the view's display 
    location as dirty so that it will update at the end of the run loop.
    
    You will not usually need to override or call this method yourself, though
    if you manually patch the parentView hierarchy for some reason, you should
    call this method to notify the view that it's parentView has changed.
    
    @returns {SC.View} receiver
  */
  parentViewDidChange: function() {
    this.set('displayLocationNeedsUpdate', YES) ;
    this.recomputeIsVisibleInWindow() ;
    SC.View.scheduleInRunLoop(SC.DISPLAY_LOCATION_QUEUE, this);
    return this ;
  }.observes('isVisible'),
  
  /**
    Set to YES when the view's display location is dirty.  You can call 
    updateDisplayLocationIfNeeded() to clear this flag if it is set.
    
    @property {Boolean}
  */
  displayLocationNeedsUpdate: NO,
  
  /**
    Calls updateDisplayLocation(), but only if the view's display location
    currently needs to be updated.  This method is called automatically at 
    the end of a run loop if you have called parentViewDidChange() at some
    point.
    
    @property {Boolean} force This property is ignored.
    @returns {Boolean} YES if the location was updated 
  */
  updateDisplayLocationIfNeeded: function(force) {
    if (!this.get('displayLocationNeedsUpdate')) return YES;
    this.set('displayLocationNeedsUpdate', NO) ;
    this.updateDisplayLocation() ;
    return YES ;
  },

  /**
    This method is called to actually update a view's DOM element in the 
    display tree to match the current settings on the view.  This method is
    usually only called one time at the end of a run loop and only if the 
    view's location has changed in the view hierarchy.
    
    You will not usually need to override this method, but you can if you 
    need to perform some custom display location work.
    
    @returns {SC.View} receiver
  */
  updateDisplayLocation: function() {
    // collect some useful value
    // if there is no node for some reason, just exit
    var node = this.rootElement ;
    if (!node) return this; // nothing to do
    
    // parents...
    var parentView = this.get('parentView') ;
    var parentNode = (parentView) ? parentView.$container().get(0) : null ;
    
    
    // if we should belong to a parent, make sure we are added to the right
    // place in the array.  Note that we assume parentNode is only non-null if
    // parentView is also non-null.
    if (parentNode) {
      var siblings = parentView.get('childViews') ;
      var nextView = siblings.objectAt(siblings.indexOf(this)+1);
      var nextNode = (nextView) ? nextView.rootElement : null ;
    
      // add to parentNode if needed.  If we do add, then also notify view
      // that its parentView has resized since joining a parentView has the
      // same effect.
      if ((node.parentNode!==parentNode) || (node.nextSibling!==nextNode)) {
        
        // before we add to parent node, make sure that the nextNode is 
        // already in the DOM.
        if (nextView && nextNode) nextView.updateDisplayLocationIfNeeded();
        
        parentNode.insertBefore(node, nextNode) ;
        this.parentViewDidResize();
      }
      
    // if we do not belong to a parent, then remove if needed.  Do not notify
    // view that parentViewDidResize since we are moving ourselves from a 
    // parentNode.  No good can come of it.
    } else {
      if (node.parentNode) node.parentNode.removeChild(node);
    }

    // finally, update visibility of element as needed if we are in a parent
    if (parentView) {
      var $ = this.$(), isVisible = this.get('isVisible') ;
      ((isVisible) ? $.show() : $.hide()); 
      if (!isVisible && this.get('isVisibleInWindow')) {
        this.recomputeIsVisibleInWindow();
        // do this only after we have gone offscreen.
      }
    }
    
    parentNode = parentView = node = null ; // avoid memory leaks
    return this ; 
  },
  
  
  /**
    Determines if the view is visible on the screen, even if it is in the
    view hierarchy.  This is considered part of the layout and so changing
    it will trigger a layout update.
  */
  isVisible: YES,
  
  /**
    This property is true only if the view and all of its parent views are
    currently visible in the window.  It updates automatically.
  */
  isVisibleInWindow: NO,
  
  /**
    Recomputes the isVisibleInWindow property based on the visibility of the 
    view and its parent.  If the recomputed value differs from the current 
    isVisibleInWindow state, this method will also call 
    recomputIsVisibleInWindow() on its child views as well.  As an optional 
    optimization, you can pass the isVisibleInWindow state of the parentView 
    if you already know it.
    
    You will not generally need to call or override this method yourself. It 
    is used by the SC.View hierarchy to relay window visibility changes up 
    and down the chain.
    
    @property {Boolean} parentViewIsVisible
    @returns {SC.View} receiver 
  */
  recomputeIsVisibleInWindow: function(parentViewIsVisible) {
    var last = this.get('isVisibleInWindow') ;
    var cur = this.get('isVisible'), parentView ;
    
    // isVisibleInWindow = isVisible && parentView.isVisibleInWindow
    // this approach only goes up to the parentView if necessary.
    if (cur) {
      cur = (parentViewIsVisible === undefined) ? 
       ((parentView=this.get('parentView')) ? 
         parentView.get('isVisibleInWindow') : NO) : parentViewIsVisible ;
    }
    
    // if the state has changed, update it and notify children
    if (last !== cur) {
      this.set('isVisibleInWindow', cur) ;
      var childViews = this.get('childViews'), idx = childViews.length;
      while(--idx>=0) childViews[idx].recomputeIsVisibleInWindow(cur);
      
      // if we were firstResponder, resign firstResponder also
      if (!cur && this.get('isFirstResponder')) {
        this.resignFirstResponder();
      }
      
    }
    
    return this ;
  },

  // .......................................................
  // SC.RESPONDER SUPPORT
  //

  /** @property
    The nextResponder is usually the parentView.
  */
  nextResponder: function() {
    return this.get('parentView');
  }.property('parentView').cacheable(),

  /**
    Recursively travels down the view hierarchy looking for a view that 
    implements the key equivalent (returning to YES to indicate it handled 
    the event).  You can override this method to handle specific key 
    equivalents yourself.
    
    The keystring is a string description of the key combination pressed.
    The evt is the event itself. If you handle the equivalent, return YES.
    Otherwise, you should just return sc_super.
    
    @param {String} keystring
    @param {SC.Event} evt
    @returns {Boolean}
  */
  performKeyEquivalent: function(keystring, evt) {
    var ret = null, childViews = this.get('childViews'), len = childViews.length, idx=-1;
    while(!ret && (++idx<len)) {
      ret = childViews[idx].performKeyEquivalent(keystring, evt);
    }
    return ret ;
  },

  // .......................................................
  // CORE DISPLAY METHODS
  //

  /** @private 
    Setup a view, but do not finish waking it up. 
    - configure childViews
    - generate DOM + plug in outlets/childViews unless rootElement is defined
    - register the view with the global views hash, which is used for mgmt
  */
  init: function() {
    var parentView, path, root, idx, len, lp, dp;
    
    arguments.callee.base.apply(this,arguments);
    SC.View.views[SC.guidFor(this)] = this; // register w/ views
    
    // setup child views.  be sure to clone the child views array first
    this.childViews = this.childViews ? this.childViews.slice() : [];
    this.createChildViews() ; // setup child Views
    
    // if no rootElement is provided, generate the display HTML for the view.
    if (!this.rootElement) {
      
      // if a rootElementPath is provided and we have a parentView with HTML
      // already, try to find the rootElement in that template.  Otherwise,
      // generate the HTML ourselves...
      parentView = this.get('parentView');
      path = this.rootElementPath;
      root = (parentView) ? parentView.rootElement : null;      
      if (parentView && path && root) {
        idx=0; 
        len = path.length;
        while(root && idx<len) root = root.childNodes[path[idx++]];
        if (root) this.rootElement = root;
        
      } else {
        this.prepareDisplay();
        if (this.get('updateChildLayoutOnPrepare')) this.childLayoutDidChange() ;
        if (this.get('updateDisplayOnPrepare')) this.displayDidChange() ;
      }

      parentView = path = root = null;
    }

    // save this guid on the DOM element for reverse lookups.
    if (this.rootElement) this.rootElement[SC.viewKey] = SC.guidFor(this) ;
    
    // register layout property observers .. this could be optimized into the
    // class creation mechanism if local observers did not require explicit 
    // setup.
    lp = this.get('childLayoutProperties'); 
    idx = lp.length;
    while(--idx >= 0) {
      this.addObserver(lp[idx], this, this.childLayoutDidChange);
    }
    
    // register display property observers .. this could be optimized into the
    // class creation mechanism if local observers did not require explicit 
    // setup.
    dp = this.get('displayProperties'); 
    idx = dp.length;
    while(--idx >= 0) {
      this.addObserver(dp[idx], this, this.displayDidChange);
    }
    
    // register for drags
    if (this.get('isDropTarget')) SC.Drag.addDropTarget(this) ;
    
    // register scroll views for autoscroll during drags
    if (this.get('isScrollable')) SC.Drag.addScrollableView(this) ;
  },

  /**
    Wakes up the view. The default implementation immediately syncs any 
    bindings, which may cause the view to need its display updated. You 
    can override this method to perform any additional setup. Be sure to 
    call sc_super to setup bindings and to call awake on childViews.
    
    @returns {void}
  */
  awake: function() {
    arguments.callee.base.apply(this,arguments);
    var childViews = this.get('childViews');
    if (childViews) childViews.invoke('awake');
  },
    
  /** 
    You must call this method on a view to destroy the view (and all of its 
    child views). This will remove the view from any parent node, then make 
    sure that the DOM element managed by the view can be released by the 
    memory manager.
  */
  destroy: function() {
    if (this.get('isDestroyed')) return this; // nothing to do
     
    arguments.callee.base.apply(this,arguments);
    
    // remove from parent if found
    this.removeFromParent() ;

    // now save rootElement and call primitive destroy method.  This will
    // cleanup children but not actually remove the DOM from any view it
    // might be in etc.  This way we only do this once for the top view.
    var rootElement = this.rootElement ;
    this._destroy(); // core destroy method

    // if rootElement still belongs to a parent somewhere, remove it
    if (rootElement.parentNode) {
      rootElement.parentNode.removeChild(rootElement) ;
    } 
    
    // unregister for drags
    if (this.get('isDropTarget')) SC.Drag.removeDropTarget(this) ;

    // unregister for autoscroll during drags
    if (this.get('isScrollable')) SC.Drag.removeScrollableView(this) ;
    return this; // done with cleanup
  },
  
  isDestroyed: NO,
  
  _destroy: function() {
    // if destroyed, do nothing
    if (this.get('isDestroyed')) return this ;
    
    // first destroy any children.
    var childViews = this.get('childViews') ;
    if (childViews.length > 0) {
      childViews = childViews.slice() ;
      var loc = childViews.length;
      while(--loc >= 0) childViews[loc]._destroy() ;
    }
    
    // next remove view from global hash
    delete SC.View.views[SC.guidFor(this)];

    // can cleanup rootElement and containerElement (if set)
    delete this.rootElement; delete this._CQ;
    delete this.page;
    
    // mark as destroyed so we don't do this again
    this.set('isDestroyed', YES) ;
    return this ;
  },
  
  /** 
    This method is called when your view is first created to setup any 
    child views that are already defined on your class.  If any are 
    found, it will instantiate them for you.
    
    The default implementation of this method simply steps through your 
    childViews array, which is expects to either be empty or to contain 
    View designs that can be instantiated
    
    Alternatively, you can implement this method yourself in your own 
    subclasses to look for views defined on specific properties and then 
    build a childViews array yourself.
    
    Note that when you implement this method yourself, you should never 
    instantiate views directly.  Instead, you should use 
    this.createChildView() method instead.  This method can be much faster 
    in a production environment than creating views yourself.

    @returns {SC.View} receiver
  */
  createChildViews: function() {
    var childViews = this.get('childViews');
    var views, loc, view ;

    this.beginPropertyChanges() ;

    // swap the array
    loc = (childViews) ? childViews.length : 0 ;
    while(--loc >= 0) {
      view = childViews[loc] ;
      if (view && view.isClass) {
        view = this.createChildView(view) ; // instantiate if needed
      }
      childViews[loc] =view ;
    }
    
    this.endPropertyChanges();
    return this ;
  },
  
  /**
    Instantiates a view to be added to the childViews array during view 
    initialization. You generally will not call this method directly unless 
    you are overriding createChildViews(). Note that this method will 
    automatically configure the correct settings on the new view instance to 
    act as a child of the parent.
    
    @param {Class} viewClass
    @param {Hash} attrs optional attributes to add
    @returns {SC.View} new instance
  */
  createChildView: function(view, attrs) {
    // attrs should always exist...
    if (!attrs) attrs = {} ;
    
    // try to find a matching DOM element by tracing the path
    var root = this.rootElement ;
    var path = view.prototype.rootElementPath || attrs.rootElementPath;
    var idx=0, len = (path) ? path.length : 0 ;
    while((idx<len) && root) root = root.childNodes[path[idx++]];

    if (root) attrs.rootElement = root ;
    attrs.owner = attrs.parentView = this ;
    if (!attrs.page) attrs.page = this.page ;
    
    // Now add this to the attributes and create.
    view = view.create(attrs) ;
    root = attrs = path = null  ; // clean up
    return view ;
  },
  
  /**
    This method is called when the view is created without a root element.
    You should override this method to setup the DOM according to the initial
    state of the view.  The resulting DOM will be dumped to a file and 
    reloaded during a production environment so do not depend on this method 
    being called.
    
    The default implementation will simply create DOM from the emptyElement
    property defined on the view and set it as the rootElement.  It will also
    insert any childViews DOM elements into the rootElement.
  */
  prepareDisplay: function() {
    
    //console.log('%@:prepareDisplay'.fmt(this));
    
    var root, element, html, con =this.constructor, cq, styleClass ;
    
    // if emptyElement is not overridden by the instance, then use a cached
    // DOM from the class.  Note that we don't use get() in the test below 
    // because we are interested in comparing the actual value of the 
    // property, not the output.
    var differs = SC.EMPTY_ELEMENT_PROPERTIES.find(function(k){
      return this[k] !== this.constructor.prototype[k];
    },this);    
    
    if (!differs) {
      if (!this._cachedEmptyElement || (this._emptyElementCachedForClassGuid !== SC.guidFor(con))) {
        styleClass = this.get('styleClass').join(' ');
        html = this.get('emptyElement').fmt(this.get('tagName'));
        cq = SC.$(html).addClass(styleClass);
        cq.setClass('allow-select', this.get('isTextSelectable')); 
        
        con.prototype._cachedEmptyElement = cq.get(0);        
        con.prototype._emptyElementCachedForClassGuid = SC.guidFor(con) ;
      }

      root = this._cachedEmptyElement.cloneNode(true);
      
    // otherwise, we can't cache the DOM because it is overridden by instance
    } else {
      styleClass = this.get('styleClass').join(' ');
      html = this.get('emptyElement').fmt(this.get('tagName'));
      cq = SC.$(html).addClass(styleClass);
      cq.setClass('allow-select', this.get('isTextSelectable'));
      root = cq.get(0);
    }
    this.rootElement = root ;

    // save this guid on the DOM element for reverse lookups.
    if (root) root[SC.viewKey] = SC.guidFor(this) ;
    
    // also, update the layout to match the frame
    this.updateDisplayLayout();
    
    // now add DOM for child views if needed.
    // get the containerElement or use rootElement -- append to this
    var container = this.$container().get(0);
    var idx, childViews = this.get('childViews'), max = childViews.length;
    for(idx=0;idx<max;idx++) {
      element = childViews[idx].rootElement;
      if (element) container.appendChild(element) ;
    }
    
    // clear out some local variables that hold DOM to avoid memory leaks
    root = container = element = cq = null; 

    // call all prepareDisplayMixins...
    var mixins = this.prepareDisplayMixin, len = (mixins) ? mixins.length : 0;
    for(idx=0;idx<len;idx++) mixins[idx].call(this);
  },
  
  /**
    By default, when you have to layout the display, it will also be laid out
    once even before any properties or observers are setup.  This is usually
    the behavior you want, but if you do something significantly different 
    during the prepareDisplay method, you may want to turn this off.
  */
  updateChildLayoutOnPrepare: YES,
  
  /**
    By default, when you have to prepare the display, it will also be updated
    once even before any properties or observers are setup.  This is usually
    the behavior you want, but if you do something significantly different 
    during the prepareDisplay method, you may want to turn this off.
  */
  updateDisplayOnPrepare: YES,
  
  /** 
    Returns a CoreQuery object that selects elements starting with the 
    views rootElement.  You can pass a selector to this or pass no parameters
    to get a CQ object the selects the view's rootElement.
    
    @param {String} selector
    @param {Object} context not usually needed
    @returns {SC.CoreQuery} CoreQuery or jQuery object
  */
  $: function(selector, context) {
    if (arguments.length===0) {
      if(!this._CQ) this._CQ = SC.$(this.rootElement);
      return this._CQ;
    } else return SC.$(selector, (context || this.rootElement)) ;
  },
  
  /**
    Returns a CoreQuery object that selects the elements starting with the 
    view's containerElement.  You can pass a selector to this or pass no 
    parameters to get a CQ object that selects the view's containerElement.
    
    For many views, their container element and root element are the same.
    This means that calling view.$() and view.$container() will yield the 
    same results.  However, if the view has a containerSelector property set, 
    then the container will differ.
  */
  $container: function(selector, context) {
    var sel = this.get('containerSelector') ;
    if (arguments.length === 0) {
      return (sel) ? this.$(sel) : this.$() ;
    } else {
      return (sel) ? this.$(selector, context || this.$(sel).get(0)) : this.$(selector,context);
    }
  },

  /**
    If you want elements inserted anywhere other than the rootElement of your
    view, you should name a selector to find the matching elements here.
  */
  containerSelector: null,

  /**
    Describe the template HTML for new elements.  This will be used to create 
    new HTML when you generate your view programatically.
  */
  emptyElement: '<%@1></%@1>',
  
  /**
    Optional tag name for the emptyElement.  Use %@1 in your empty element
    string to replace with the tag name.
  */
  tagName: 'div',
  
  /** 
    Optional css class name to add to the root element of the view when it 
    is first generated.  Use this property to bind the output HTML to some 
    CSS.
  */
  styleClass: ['sc-view'],
  
  /**
    Determines if the user can select text within the view.  Normally this is
    set to NO to disable text selection.  You should set this to YES if you
    are creating a view that includes editable text.  Otherwise, settings this
    to YES will probably make your controls harder to use and it is not 
    recommended.
    
    This property is used when first preparing the view's display.  If you
    change it once the view has been created, it will have no effect.
    
    @property {Boolean}
  */
  isTextSelectable: NO,
  
  /**
    Dumps the HTML needs for the emptyElement when restoring this view 
    hierarchy later.  This is mostly used by the view builder but may be
    useful at other times.
  */
  computeEmptyElement: function() {
    
    // make sure any pending display updates are handled -- even if offscreen
    this.performDisplayUpdates(YES); 
    
    var root = this.rootElement ;
    if (!root) return '' ;
    
    // if rootElement is in parent, remove from parent so we can place in our
    // own div.
    var parentNode = root.parentNode, next = root.nextSibling ;
    if (parentNode) parentNode.removeChild(root) ;
    
    var b = SC.$('<div></div>').append(root) ;
    var ret = b.html(); // get innerHTML

    if (parentNode) {
      parentNode.insertBefore(root, next) ;
    } else SC.$(root).remove() ;
    
    b = root = parentNode = next = null ; // avoid memory leaks
    return ret ; // return string
  },
  
  /** 
    This method is invoked whenever the display state of the view has changed.
    You should override this method to update your DOM element to match the
    current state of the view.
    
    Unlike prepareDisplay(), this method will be called at least once whenever
    your app is started and thereafter as often as needed.  It will not be
    optimized out during the build process.
    
    The default implementation does nothing.
    
    @returns {SC.View} receiver
  */
  updateDisplay: function() {
    //console.log('%@: updateDisplay()'.fmt(this));
    
    var mixins = this.updateDisplayMixin, len = (mixins) ? mixins.length : 0;
    for(var idx=0;idx<len;idx++) mixins[idx].call(this);
  },

  /** 
    Call this method whenever the view's state changes in such as way that
    requires the views display to be updated.  This will schedule the view
    for display at the end of the runloop.
  */
  displayDidChange: function() {
    this.set('displayNeedsUpdate', YES) ;
    SC.View.scheduleInRunLoop(SC.DISPLAY_UPDATE_QUEUE, this);
    return this ;
  },
  
  displayNeedsUpdate: NO,
  
  /**
    This method will update the display location, but only if it needs an 
    update.  Returns YES if the method was able to execute, NO if it needs
    to be called again later.
  */
  updateDisplayIfNeeded: function(force) {
    if (!this.get('displayNeedsUpdate')) return YES;
    if (!force & !this.get('isVisibleInWindow')) return NO ;
    this.set('displayNeedsUpdate', NO) ;
    this.updateDisplay() ;
    return YES;
  },
  
  /** 
    You can set this array to include any properties that should immediately
    invalidate the display.  The display will be automatically invalidated
    when one of these properties change.
  */
  displayProperties: [],
  
  // ...........................................
  // LAYOUT
  //

  /** 
    This method is invoked whenever the layout of the view's child views has 
    changed. You should override this method to update your child views to match 
    the current state of the view.
    
    The default implementation does nothing.
    
    @returns {SC.View} receiver
  */
  updateChildLayout: function() {
    //console.log('%@: updateLayout()'.fmt(this));
    
    var mixins = this.updateChildLayoutMixin, len = (mixins) ? mixins.length : 0;
    for(var idx=0;idx<len;idx++) mixins[idx].call(this);
  },

  /** 
    Call this method whenever the child view layout changes in such as way that
    requires the view's child view layout to be updated.  This will schedule the 
    view for layout at the end of the runloop.
  */
  childLayoutDidChange: function() {
    this.set('childLayoutNeedsUpdate', YES) ;
    SC.View.scheduleInRunLoop(SC.UPDATE_CHILD_LAYOUT_QUEUE, this);
    return this ;
  },
  
  childLayoutNeedsUpdate: NO,
  
  /**
    This method will update the display location, but only if it needs an 
    update.  Returns YES if the method was able to execute, NO if it needs
    to be called again later.
  */
  updateChildLayoutIfNeeded: function(force) {
    if (!this.get('childLayoutNeedsUpdate')) return YES;
    if (!force & !this.get('isVisibleInWindow')) return NO ;
    this.set('childLayoutNeedsUpdate', NO) ;
    this.updateChildLayout() ;
    return YES;
  },
  
  /** 
    You can set this array to include any properties that should immediately
    invalidate the layout.  The layout will be automatically invalidated
    when one of these properties change.
  */
  childLayoutProperties: [],
  
  /** 
    This convenience method will take the current layout, apply any changes
    you pass and set it again.  It is more convenient than having to do this
    yourself sometimes.
    
    You can pass just a key/value pair or a hash with several pairs.  You can
    also pass a null value to delete a property.
    
    This method will avoid actually setting the layout if the value you pass
    does not edit the layout.
    
    @param {String|Hash} key
    @param {Object} value
    @returns {SC.View} receiver
  */
  adjust: function(key, value) {
    var layout = SC.clone(this.get('layout')), didChange = NO, cur;
    
    if (key === undefined) return this ; // nothing to do.
    
    // handle string case
    if (SC.typeOf(key) === SC.T_STRING) {
      cur = layout[key];
      if (SC.none(value)) {
        if (cur !== undefined) didChange = YES ;
        delete layout[key];
      } else {
        if (cur !== value) didChange = YES ;
        layout[key] = value ;
      }
      
    // handle hash -- do it this way to avoid creating memory unless needed
    } else {
      var hash = key;
      for(key in hash) {
        if (!hash.hasOwnProperty(key)) continue;
        value = hash[key];
        cur = layout[key] ;
        
        if (value === null) {
          if (cur !== undefined) didChange = YES ;
          delete layout[key] ;
        } else if (value !== undefined) {
          if (cur !== value) didChange = YES ;
          layout[key] = value ;
        }
      }
    }
    
    // now set adjusted layout
    if (didChange) this.set('layout', layout) ;
    
    return this ;
  },
  
  /** 
    The layout describes how you want your view to be positions on the 
    screen.  You can define the following properties:
    
    - left: the left edge
    - top: the top edge
    - right: the right edge
    - bottom: the bottom edge
    - height: the height
    - width: the width
    - centerX: an offset from center X 
    - centerY: an offset from center Y
    - minWidth: a minimum width
    - minHeight: a minimum height
    - maxWidth: a maximum width
    - maxHeight: a maximum height
    
    Note that you can only use certain combinations to set layout.  For 
    example, you may set left/right or left/width, but not left/width/right,
    since that combination doesn't make sense.
    
    Likewise, you may set a minWidth/minHeight, or maxWidth/maxHeight, but
    if you also set the width/height explicitly, then those constraints won't
    matter as much.
    
    Layout is designed to maximize reliance on the browser's rendering 
    engine to keep your app up to date.
  */
  layout: { top: 0, left: 0, bottom: 0, right: 0 },

  /**
    Converts a frame from the receiver's offset to the target offset.  Both
    the receiver and the target must belong to the same pane.  If you pass
    null, the conversion will be to the pane level.
  */
  convertFrameToView: function(frame, targetView) {
    var myX=0, myY=0, targetX=0, targetY=0, view = this, next, f;

    // walk up this side
    while(next = view.get('parentView')) {
      f = next.get('frame'); /* console.log($I(f)); */ myX += f.x; myY += f.y ;
      view = next ; 
    }

    // walk up other size
    if (targetView) {
      view = targetView ;
      while(next = view.get('parentView')) {
        f = next.get('frame'); targetX += f.x; targetY += f.y ;
        view = next ; 
      }
    }
    
    // now we can figure how to translate the origin.
    myX = frame.x + myX - targetX ;
    myY = frame.y + myY - targetY ;
    return { x: myX, y: myY, width: frame.width, height: frame.height };
  },

  /**
    Converts a clipping frame from the receiver's offset to the target offset. 
    Both the receiver and the target must belong to the same pane.  If you pass
    null, the conversion will be to the pane level.
  */
  convertClippingFrameToView: function(clippingFrame, targetView) {
    var myX=0, myY=0, targetX=0, targetY=0, view = this, next = this, f;
    
    // walk up this side
    do {
      f = next.get('frame'); /* console.log($I(f)); */ myX += f.x; myY += f.y ;
      view = next ; 
    } while (next = view.get('parentView'))

    // walk up other size
    if (targetView) {
      view = targetView ;
      while(next = view.get('parentView')) {
        f = next.get('frame'); targetX += f.x; targetY += f.y ;
        view = next ; 
      }
    }
    
    // now we can figure how to translate the origin.
    myX = clippingFrame.x + myX - targetX ;
    myY = clippingFrame.y + myY - targetY ;
    return { x: myX, y: myY, width: clippingFrame.width, height: clippingFrame.height };
  },

  /**
    Converts a frame offset in the coordinates of another view system to 
    the reciever's view.
  */
  convertFrameFromView: function(frame, targetView) {
    var myX=0, myY=0, targetX=0, targetY=0, view = this, next, f;

    // walk up this side
    while(next = view.get('parentView')) {
      f = view.get('frame'); myX += f.x; myY += f.y ;
      view = next ; 
    }

    // walk up other size
    if (targetView) {
      view = targetView ;
      while(next = view.get('parentView')) {
        f = view.get('frame'); targetX += f.x; targetY += f.y ;
        view = next ; 
      }
    }
    
    // now we can figure how to translate the origin.
    myX = frame.x - myX + targetX ;
    myY = frame.y - myY + targetY ;
    return { x: myX, y: myY, width: frame.width, height: frame.height };
  },
  
  /**
    Frame describes the current bounding rect for your view.  This is always
    measured from the top-left corner of the parent view.
    
    @property {Rect}
  */
  frame: function() {
    return this.computeFrameWithParentFrame(null);
  }.property('layout').cacheable(),
  
  /**
    Computes what the frame of this view would be if the parent were resized
    to the passed dimensions.  You can use this method to project the size of
    a frame based on the resize behavior of the parent.
    
    This method is used especially by the scroll view to automatically 
    calculate when scrollviews should be visible.
  
    Passing null for the parent dimensions will use the actual current 
    parent dimensions.  This is the same method used to calculate the current
    frame when it changes.
    
    @param {Rect} pdim the projected parent dimensions
    @returns {Rect} the computed frame
  */
  computeFrameWithParentFrame: function(pdim) {
    var layout = this.get('layout') ;
    var f = {} ;

    // handle left aligned and left/right 
    if (!SC.none(layout.left)) {
      f.x = Math.floor(layout.left) ;
      if (layout.width !== undefined) {
        f.width = Math.floor(layout.width) ;
      } else { // better have layout.right!
        if (!pdim) pdim = this.computeParentDimensions(layout);
        f.width = Math.floor(pdim.width - f.x - (layout.right || 0)) ;
      }
      
    // handle right aligned
    } else if (!SC.none(layout.right)) {
      if (!pdim) pdim = this.computeParentDimensions(layout);
      if (SC.none(layout.width)) {
        f.width = pdim.width - layout.right ;
        f.x = 0;
      } else {
        f.width = Math.floor(layout.width || 0) ;
        f.x = Math.floor(pdim.width - layout.right - f.width) ;
      }

    // handle centered
    } else if (!SC.none(layout.centerX)) {
      if (!pdim) pdim = this.computeParentDimensions(layout); 
      f.width = Math.floor(layout.width || 0);
      f.x = Math.floor((pdim.width - f.width)/2 + layout.centerX);
    } else {
      f.x = 0 ; // fallback
      if (SC.none(layout.width)) {
        if (!pdim) pdim = this.computeParentDimensions(layout); 
        f.width = Math.floor(pdim.width) ;
      } else f.width = layout.width;
    }


    // handle top aligned and top/bottom 
    if (!SC.none(layout.top)) {
      f.y = Math.floor(layout.top) ;
      if (layout.height !== undefined) {
        f.height = Math.floor(layout.height) ;
      } else { // better have layout.bottm!
        if (!pdim) pdim = this.computeParentDimensions(layout);
        f.height = Math.floor(pdim.height - f.y - (layout.bottom || 0)) ;
      }
      
    // handle bottom aligned
    } else if (!SC.none(layout.bottom)) {
      if (!pdim) pdim = this.computeParentDimensions(layout);
      if (SC.none(layout.height)) {
        f.height = pdim.height - layout.bottom;
        f.y = 0;
      } else {
        f.height = Math.floor(layout.height || 0) ;
        f.y = Math.floor(pdim.height - layout.bottom - f.height) ;
      }

    // handle centered
    } else if (!SC.none(layout.centerY)) {
      if (!pdim) pdim = this.computeParentDimensions(layout); 
      f.height = Math.floor(layout.height || 0);
      f.y = Math.floor((pdim.height - f.height)/2 + layout.centerY);

    // fallback
    } else {
      f.y = 0 ; // fallback
      if (SC.none(layout.height)) {
        if (!pdim) pdim = this.computeParentDimensions(layout); 
        f.height = Math.floor(pdim.height) ;
      } else f.height = layout.height;
    }

    // make sure the width/height fix min/max...
    if (!SC.none(layout.maxHeight) && (f.height > layout.maxHeight)) f.height = layout.maxHeight;
    if (!SC.none(layout.minHeight) && (f.height < layout.minHeight)) f.height = layout.minHeight;
    if (!SC.none(layout.maxWidth) && (f.width > layout.maxWidth)) f.width = layout.maxWidth;
    if (!SC.none(layout.minWidth) && (f.width < layout.minWidth)) f.width = layout.minWidth;

    // make sure width/height are never < 0
    if (f.height < 0) f.height = 0;
    if (f.width < 0) f.width = 0;
    
    
    return f;
  },
  
  /**
    The clipping frame returns the visible portion of the view, taking into
    account the clippingFrame of the parent view.  Keep in mind that the 
    clippingFrame is in the context of the view itself, not it's parent view.
    
    Normally this will be calculate based on the intersection of your own 
    clippingFrame and your parentView's clippingFrame.  SC.ClipView may also
    shift this by a certain amount.    
  */
  clippingFrame: function() {
    var pv= this.get('parentView'), f = this.get('frame'), ret = f ;
    if (pv) {
     pv = pv.get('clippingFrame');
     ret = SC.intersectRects(pv, f);
    }
    ret.x -= f.x; ret.y -= f.y;
    return ret ;
  }.property('parentView', 'frame').cacheable(),
  
  /** @private
    Whenever the clippingFrame changes, this observer will fire, notifying
    child views that their frames have also changed.
  */
  _view_clippingFrameDidChange: function() {
    this.get('childViews').invoke('notifyPropertyChange', 'clippingFrame');
  }.observes('clippingFrame'),
  
  /**
    LayoutStyle describes the current styles to be written to your element
    based on the layout you defined.  Both layoutStyle and frame reset when
    you edit the layout property.  Both are read only.
  */
  /** 
    Computes the layout style settings needed for the current anchor.
  */
  layoutStyle: function() {
    var layout = this.get('layout'), ret = {}, pdim = null;

    // X DIRECTION
    
    // handle left aligned and left/right
    if (!SC.none(layout.left)) {
      ret.left = Math.floor(layout.left);
      if (layout.width !== undefined) {
        ret.width = Math.floor(layout.width) ;
        ret.right = null ;
      } else {
        ret.width = null ;
        ret.right = Math.floor(layout.right || 0) ;
      }
      ret.marginLeft = 0 ;
      
    // handle right aligned
    } else if (!SC.none(layout.right)) {
      ret.right = Math.floor(layout.right) ;
      ret.marginLeft = 0 ;

      if (SC.none(layout.width)) {
        ret.left = 0;
        ret.width = null;
      } else {
        ret.left = null ;
        ret.width = Math.floor(layout.width || 0) ;
      }
      
    // handle centered
    } else if (!SC.none(layout.centerX)) {
      ret.left = "50%";
      ret.width = Math.floor(layout.width || 0) ;
      ret.marginLeft = Math.floor(layout.centerX - ret.width/2) ;
      ret.right = null ;
    
    // if width defined, assume top/left of zero
    } else if (!SC.none(layout.width)) {
      ret.left =  0;
      ret.right = null;
      ret.width = Math.floor(layout.width);
      ret.marginLeft = 0;
      
    // fallback, full width.
    } else {
      ret.left = 0;
      ret.right = 0;
      ret.width = null ;
      ret.marginLeft= 0;
    }

    // handle min/max
    ret.minWidth = (layout.minWidth === undefined) ? null : layout.minWidth;
    ret.maxWidth = (layout.maxWidth === undefined) ? null : layout.maxWidth;

    // Y DIRECTION
    
    // handle left aligned and left/right
    if (!SC.none(layout.top)) {
      ret.top = Math.floor(layout.top);
      if (layout.height !== undefined) {
        ret.height = Math.floor(layout.height) ;
        ret.bottom = null ;
      } else {
        ret.height = null ;
        ret.bottom = Math.floor(layout.bottom || 0) ;
      }
      ret.marginTop = 0 ;
      
    // handle right aligned
    } else if (!SC.none(layout.bottom)) {
      ret.marginTop = 0 ;
      ret.bottom = Math.floor(layout.bottom) ;
      if (SC.none(layout.height)) {
        ret.top = 0;
        ret.height = null ;
      } else {
        ret.top = null ;
        ret.height = Math.floor(layout.height || 0) ;
      }
      
    // handle centered
    } else if (!SC.none(layout.centerY)) {
      ret.top = "50%";
      ret.height = Math.floor(layout.height || 0) ;
      ret.marginTop = Math.floor(layout.centerY - ret.height/2) ;
      ret.bottom = null ;
    
    } else if (!SC.none(layout.height)) {
      ret.top = 0;
      ret.bottom = null;
      ret.height = Math.floor(layout.height || 0);
      ret.marginTop = 0;
      
    // fallback, full width.
    } else {
      ret.top = 0;
      ret.bottom = 0;
      ret.height = null ;
      ret.marginTop= 0;
    }

    // handle min/max
    ret.minHeight = (layout.minHeight === undefined) ? null : layout.minHeight;
    ret.maxHeight = (layout.maxHeight === undefined) ? null : layout.maxHeight;

    // if zIndex is set, use it.  otherwise let default shine through
    ret.zIndex = SC.none(layout.zIndex) ? null : layout.zIndex.toString();
    
    // set default values to null to allow built-in CSS to shine through
    // currently applies only to marginLeft & marginTop
    var dims = SC._VIEW_DEFAULT_DIMS, loc = dims.length, x;
    while(--loc >=0) {
      x = dims[loc];
      if (ret[x]===0) ret[x]=null;
    }

    return ret ;
  }.property('layout').cacheable(),

  
  computeParentDimensions: function(frame) {
    var pv = this.get('parentView'), pframe = (pv) ? pv.get('frame') : null;
    return {
      width: ((pframe) ? pframe.width : ((frame.left||0)+(frame.width||0)+(frame.right||0))) || 0,
      height: ((pframe) ? pframe.height : ((frame.top||0)+(frame.height||0)+(frame.bottom||0))) || 0
    } ;
  },
  
  /** 
    This method may be called on your view whenever the parent view resizes.

    The default version of this method will reset the frame and then call 
    viewDidResize().  You will not usually override this method, but you may
    override the viewDidResize() method.
  */
  parentViewDidResize: function() {
    var layout = this.get('layout') ;

    // only resizes if the layout does something other than left/top - fixed
    // size.
    var isFixed = (layout.left!==undefined) && (layout.top!==undefined) && (layout.width !== undefined) && (layout.height !== undefined);

    if (!isFixed) {
      this.notifyPropertyChange('frame') ;
      this.viewDidResize();
    }
  },
  
  /**
    This method is invoked on your view when the view is about to begin a
    live resize session, such as a split view being resized. During the live
    resize session, you may choose to delay expensive drawing operations until
    the live resize in complete.
    
    @returns {void}
  */
  viewWillStartLiveResize: function() {
    var ary = this.get('childViews');
    for ( var idx = 0, len = ary.length; idx < len; idx++ ) {
      ary[idx].viewWillStartLiveResize();
    }
  },

  /**
    This method is invoked on your view when the view has completed a live resize 
    session, such as a split view being resized. Any expensive drawing you have
    delayed can be done at this point, by calling this.displayDidChange().
    
    @returns {void}
  */
  viewDidEndLiveResize: function() {
    var ary = this.get('childViews');
    for ( var idx = 0, len = ary.length; idx < len; idx++ ) {
      ary[idx].viewDidEndLiveResize();
    }
  },

  /**
    This method is invoked on your view when the view resizes due to a layout
    change or due to the parent view resizing.  You can override this method
    to implement your own layout if you like, such as performing a grid 
    layout.
    
    The default implementation simply calls parentViewDidResize on all of
    your children.
  */
  viewDidResize: function() {
    this.get('childViews').invoke('parentViewDidResize') ;
  }.observes('layout'),  

  /** 
    This method is called whenever a property changes that invalidates the 
    layout of the view.  Changing the layout will do this automatically, but 
    you can add others if you want.
  */
  displayLayoutDidChange: function() {
    // console.log('displayLayoutDidChange');
    this.beginPropertyChanges() ;
    this.set('displayLayoutNeedsUpdate', YES);
    this.notifyPropertyChange('frame') ;
    this.notifyPropertyChange('layoutStyle') ;
    this.endPropertyChanges() ;
    
    SC.View.scheduleInRunLoop(SC.DISPLAY_LAYOUT_QUEUE, this);
    return this ;
  }.observes('layout'),
  
  /**
    This method will update the display layout, but only if it needs an 
    update.  
  */
  updateDisplayLayoutIfNeeded: function(force) {
    if (!this.get('displayLayoutNeedsUpdate')) return YES;
    if (!force && !this.get('isVisibleInWindow')) return NO ;
    this.set('displayLayoutNeedsUpdate', NO) ;
    this.updateDisplayLayout() ;
    return YES ;
  },

  /**
    This method is called whenever the display layout has become invalid and
    the view needs its display updated again.  This will generally only 
    happen once at the end of the run loop.
  */
  updateDisplayLayout: function() {
    // console.log('updateDisplayLayout');
    var $ = this.$(), layoutStyle = this.get('layoutStyle'); // get style
    $.css(layoutStyle) ; // todo: add animation here.
  },
  
  /**
    Force immediate update of all display-related items that are pending in
    the receiver and its child views.  If you pass YES for the force flag,
    then the display will be updated even if the view is offscreen.  This 
    will force a display if no update is needed.
    
    @param force {Boolean}
    @returns {SC.View} receiver
  */
  performDisplayUpdates: function(force) {
    this.updateDisplayLocationIfNeeded(force);
    this.updateDisplayLayoutIfNeeded(force);
    this.updateDisplayIfNeeded(force);
    
    var childViews = this.get('childViews'), loc = childViews.length;
    while(--loc>=0) childViews[loc].performDisplayUpdates(force);
    return this ;
  }
  
}); 

SC.View.mixin(/** @scope SC.View @static */ {

  /** @private walk like a duck -- used by SC.Page */
  isViewClass: YES,
  
  /** 
    This method works just like extend() except that it will also preserve
    the passed attributes in case you want to use a view builder later, if 
    needed.
    
    @param {Hash} attrs Attributes to add to view
    @returns {Class} SC.View subclass to create
    @function
  */ 
  design: function() {
    if (this.isDesign) return this; // only run design one time
    var ret = this.extend.apply(this, arguments);
    ret.isDesign = YES ;
    if (SC.ViewDesigner) {
      SC.ViewDesigner.didLoadDesign(ret, this, SC.$A(arguments));
    }
    return ret ;
  },

  /**
    Helper applies the layout to the prototype. 
  */
  layout: function(layout) {
    this.prototype.layout = layout ;
    return this ;
  },
  
  /**
    Helper applies the styleClass to the prototype
  */
  styleClass: function(sc) {
    sc = (this.prototype.styleClass || []).concat(sc);
    this.prototype.styleClass = sc;
    return this ;
  },
  
  /**
    Help applies the tagName
  */
  tagName: function(tg) {
    this.prototype.tagName = tg;
    return this ;
  },
  
  /**
    Helper adds the childView
  */
  childView: function(cv) {
    var childViews = this.prototype.childViews || [];
    if (childViews === this.superclass.prototype.childViews) childViews = [];
    childViews.push(cv) ;
    this.prototype.childViews = childViews;
    return this ;
  },
  
  /**
    Helper adds a binding to a design
  */
  bind: function(keyName, path) {
    var p = this.prototype, s = this.superclass.prototype;
    var bindings = p._bindings ;
    if (!bindings || bindings === s._bindings) {
      bindings = p._bindings = (bindings || []).slice() ;
    }  
    
    keyName = keyName + "Binding";
    p[keyName] = path ;
    bindings.push(keyName);
    
    return this ;
  },

  /**
    Helper sets a generic property on a design.
  */
  prop: function(keyName, value) {
    this.prototype[keyName] = value;
    return this ;
  },
  
  /**
    Used to construct a localization for a view.  The default implementation
    will simply return the passed attributes.
  */
  localization: function(attrs, rootElement) { 
    // add rootElement
    if (rootElement) attrs.rootElement = SC.$(rootElement).get(0);
    return attrs; 
  },
  
  /**
    Creates a view instance, first finding the DOM element you name and then
    using that as the root element.  You should not use this method very 
    often, but it is sometimes useful if you want to attach to already 
    existing HTML.
    
    @param {String|Element} element
    @param {Hash} attrs
    @returns {SC.View} instance
  */
  viewFor: function(element, attrs) {
    var args = SC.$A(arguments); // prepare to edit
    if (SC.none(element)) {
      args.shift(); // remove if no element passed
    } else args[0] = { rootElement: SC.$(element).get(0) } ;
    var ret = this.create.apply(this, arguments) ;
    args = args[0] = null;
    return ret ;
  },
    
  /**
    Create a new view with the passed attributes hash.  If you have the 
    Designer module loaded, this will also create a peer designer if needed.
  */
  create: function() {
    var C=this, ret = new C(arguments); 
    if (SC.ViewDesigner) {
      SC.ViewDesigner.didCreateView(ret, SC.$A(arguments));
    }
    return ret ; 
  },
  
  /**
    Applies the passed localization hash to the component views.  Call this
    method before you call create().  Returns the receiver.  Typically you
    will do something like this:
    
    view = SC.View.design({...}).loc(localizationHash).create();
    
    @param {Hash} loc 
    @param rootElement {String} optional rootElement with prepped HTML
    @returns {SC.View} receiver
  */
  loc: function(loc) {
    var childLocs = loc.childViews;
    delete loc.childViews; // clear out child views before applying to attrs
    
    this.applyLocalizedAttributes(loc) ;
    if (SC.ViewDesigner) {
      SC.ViewDesigner.didLoadLocalization(this, SC.$A(arguments));
    }
    
    // apply localization recursively to childViews
    var childViews = this.prototype.childViews, idx = childViews.length;
    while(--idx>=0) {
      var viewClass = childViews[idx];
      loc = childLocs[idx];
      if (loc && viewClass && viewClass.loc) viewClass.loc(loc) ;
    }
    
    return this; // done!
  },
  
  /**
    Internal method actually updates the localizated attributes on the view
    class.  This is overloaded in design mode to also save the attributes.
  */
  applyLocalizedAttributes: function(loc) {
    SC.mixin(this.prototype, loc) ;
  },
  
  views: {},
  
  /**
    Called by the runloop at the end of the runloop to update any scheduled
    view queues.  Returns YES if some items were flushed from the queue.
  */
  flushPendingQueues: function() {
    return this.runLoopQueue.flush() ;
  },
  
  /**
    Called by view instances to add them to a queue with the specified named.
  */
  scheduleInRunLoop: function(queueName, view) {
    this.runLoopQueue.add(queueName, view);
  },
  
  /** @private
  Manages the queue of views that need to have some method executed. */
  runLoopQueue: {
    add: function(queueName, view) {
      var queue = this[queueName] ;
      if (!queue) queue = this[queueName] = SC.Set.create();
      queue.add(view) ;
    },
    
    // flushes all queues in order.  Return YES if any of the queus actually
    // had something to do, so that this will repeat.
    flush: function() {
      var needsFlush = NO, order = this.order, len = order.length, idx;
      for(idx=0;idx<len;idx++) {
        if (this.flushQueue(order[idx])) needsFlush = YES;
      }
      return needsFlush;
    },

    // flush a single queue.  Any views that cannot execute will be put 
    // back into the queue.
    flushQueue: function(queueName) {
      var didExec = NO, queue = this[queueName], view ;
      if (!queue) return NO ;
      
      delete this[queueName] ;// reset queue
      while(view = queue.pop()) {
        if (view[queueName]()) {
          didExec = YES ;
        } else this.add(queueName, view);
      }
      return didExec;
    },
    
    order: [SC.DISPLAY_LOCATION_QUEUE, SC.UPDATE_CHILD_LAYOUT_QUEUE, SC.DISPLAY_LAYOUT_QUEUE, SC.DISPLAY_UPDATE_QUEUE]
  }
    
}) ;

// .......................................................
// OUTLET BUILDER
//

/** 
  Generates a computed property that will look up the passed property path
  the first time you try to get the value.  Use this whenever you want to 
  define an outlet that points to another view or object.  The root object
  used for the path will be the receiver.
*/
SC.outlet = function(path) {
  return function(key) {
    return (this[key] = SC.objectForPropertyPath(path, this)) ;
  }.property().outlet();
};

/** @private on unload clear cached divs. */
SC.View.unload = function() {
  
  // delete view items this way to ensure the views are cleared.  The hash
  // itself may be owned by multiple view subclasses.
  var views = SC.View.views;
  if (views) {
    for(var key in views) {
      if (!views.hasOwnProperty(key)) continue ;
      delete views[key];
    }
  }
  
} ;

SC.Event.add(window, 'unload', SC.View, SC.View.unload) ;


/* End ------------------------------------------------------- views/view.js*/

/* Start ----------------------------------------------------- views/panes/pane.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/view');

/** @class
  A Pane is like a regular view except that it does not need to live within a 
  parent view.  You usually use a Pane to form the root of a view hierarchy in 
  your application, such as your main application view or for floating palettes, 
  popups, menus, etc.
  
  Usually you will not work directly with the SC.Pane class, but with one of its 
  subclasses such as SC.MainPane, SC.DialogPane, or SC.PopupPane.

  h1. Showing a Pane
  
  To make a pane visible, you need to add it to your HTML document.  The 
  simplest way to do this is to call the append() method:
  
  {{{
     myPane = SC.Pane.create();
     myPane.append(); // adds the pane to the document
  }}}
  
  This will insert your pane into the end of your HTML document body, causing it 
  to display on screen.  It will also register your pane with the 
  SC.RootResponder for the document so you can start to receive keyboard, mouse, 
  and touch events.
  
  If you need more specific control for where you pane appears in the document, 
  you can use several other insertion methods such as appendTo(), prependTo(), 
  before() and after().  These methods all take a CoreQuery object or a selector 
  to indicate where in your HTML document you would like you pane to be 
  inserted.
  
  Once a pane is inserted into the document, it will be sized and positioned 
  according to the layout you have specified.  It will then automatically resize 
  with the window if needed, relaying resize notifications to children as well.
  
  h1. Hiding a Pane
  
  When you are finished with a pane, you can hide the pane by calling the 
  remove() method.  This method will actually remove the Pane from the document 
  body, as well as deregistering it from the RootResponder so that it no longer 
  receives events.
  
  The isVisibleInWindow method will also change to NO for the Pane and all of 
  its childViews and the views will no longer have their updateDisplay methods 
  called.  
  
  You can readd a pane to the document again any time in the future by using any 
  of the insertion methods defined in the previous section.
  
  h1. Receiving Events
  
  Your pane and its child views will automatically receive any mouse or touch 
  events as long as it is on the screen.  To receive keyboard events, however, 
  you must focus the keyboard on your pane by calling makeKeyPane() on the pane 
  itself.  This will cause the RootResponder to route keyboard events to your 
  pane.  The pane, in turn, will route those events to its current keyView, if 
  there is any.
  
  Note that all SC.Views (anything that implements SC.ClassicResponder, really) 
  will be notified when it is about or gain or lose keyboard focus.  These 
  notifications are sent both when the view is made keyView of a particular pane 
  and when the pane is made keyPane for the entire application.
  
  You can prevent your Pane from becoming key by setting the acceptsKeyFocus to 
  NO on the pane.  This is useful when creating palettes and other popups that 
  should not steal keyboard control from another view.

  @extends SC.View
  @since SproutCore 1.0
*/
SC.Pane = SC.View.extend({

  /** 
    Returns YES for easy detection of when you reached the pane. 
    @property {Boolean}
  */
  isPane: YES,
  
  /** 
    Set to the current page when the pane is instantiated from a page object.
    @property {SC.Page}
  */
  page: null,
  
  // .......................................................
  // ROOT RESPONDER SUPPORT
  //

  /**
    The rootResponder for this pane.  Whenever you add a pane to a document, this 
    property will be set to the rootResponder that is now forwarding events to 
    the pane.
    
    @property {SC.Responder}
  */
  rootResponder: null,  
  
  /** Last known window size. */
  currentWindowSize: null,
  
  /** The parent dimensions are always the last known window size. */
  computeParentDimensions: function(frame) {
    var pframe = this.get('currentWindowSize');
    return {
      width: (pframe) ? pframe.width : 1000,
      height: (pframe) ? pframe.height : 1000
    } ;
  },

  /** 
    Invoked by the root responder whenever the window resizes.  This should
    simply begin the process of notifying children that the view size has
    changed, if needed.
  */
  windowSizeDidChange: function(oldSize, newSize) {
    this.set('currentWindowSize', newSize) ;
    this.parentViewDidResize(); // start notifications.
  },
  
  /**
    Attempts to send the event down the responder chain for this pane.  If you 
    pass a target, this method will begin with the target and work up the 
    responder chain.  Otherwise, it will begin with the current firstResponder 
    and walk up the chain looking for any responder that implements a handler 
    for the passed method and returns YES when executed.
    
    @param {String} action
    @param {SC.Event} evt
    @param {Object} target
    @returns {Object} object that handled the event
  */
  sendEvent: function(action, evt, target) {
    var handler ;
    
    // walk up the responder chain looking for a method to handle the event
    if (!target) target = this.get('firstResponder') ;
    while(target && !target.tryToPerform(action, evt)) {

      // even if someone tries to fill in the nextResponder on the pane, stop
      // searching when we hit the pane.
      target = (target === this) ? null : target.get('nextResponder') ;
    }
    
    // if no handler was found in the responder chain, try the default
    if (!target && (target = this.get('defaultResponder'))) {
      target = target.tryToPerform(action, evt) ? target : null ;
    }
        
    return evt.mouseHandler || target ;
  },

  // .......................................................
  // HANDLE FIRST RESPONDER AND KEY RESPONDER STATUS
  //

  /** @property
    The default responder.  Set this to point to a responder object that can 
    respond to events when no other view in the hierarchy handles them.
  */
  defaultResponder: null,
  
  /** @property
    The first responder.  This is the first view that should receive action 
    events.  Whenever you click on a view, it will usually become firstResponder. 
  */
  firstResponder: null,
  
  /** @property
    If YES, this pane can become the key pane.  You may want to set this to NO 
    for certain types of panes.  For example, a palette may never want to 
    become key.  The default value is YES.
  */
  acceptsKeyPane: YES,
  
  /** @property
    This is set to YES when your pane is currently the target of key events. 
  */
  isKeyPane: NO,

  /**
    Make the pane receive key events.  Until you call this method, the 
    keyView set for this pane will not receive key events. 
  
    @returns {SC.Pane} receiver
  */
  becomeKeyPane: function() {
    if (this.get('isKeyPane')) return this ;
    if (this.rootResponder) this.rootResponder.makeKeyPane(this) ;
    return this ;
  },
  
  /**
    Remove the pane view status from the pane.  This will simply set the 
    keyPane on the rootResponder to null.
    
    @returns {SC.Pane} receiver
  */
  resignKeyPane: function() {
    if (!this.get('isKeyPane')) return this ;
    if (this.rootResponder) this.rootResponder.makeKeyPane(null);
    return this ;
  },
  
  /**
    Makes the passed view (or any object that implements SC.Responder) into 
    the new firstResponder for this pane.  This will cause the current first
    responder to lose its responder status and possibly keyResponder status as
    well.
    
    @param {SC.View} view
    @returns {SC.Pane} receiver
  */
  makeFirstResponder: function(view) {
    var current=this.get('firstResponder'), isKeyPane=this.get('isKeyPane');
    if (current === view) return this ; // nothing to do
    
    // notify current of firstResponder change
    if (current) current.willLoseFirstResponder();
    
    // if we are currently key pane, then notify key views of change also
    if (isKeyPane) {
      if (current) current.willLoseKeyResponderTo(view) ;
      if (view) view.willBecomeKeyResponderFrom(current) ;
    }
    
    // change setting
    if (current) {
      current.beginPropertyChanges()
        .set('isFirstResponder', NO).set('isKeyResponder', NO)
      .endPropertyChanges();
    }

    this.set('firstResponder', view) ;
    
    if (view) {
      view.beginPropertyChanges()
        .set('isFirstResponder', YES).set('isKeyResponder', isKeyPane)
      .endPropertyChanges();
    }
    
    // and notify again if needed.
    if (isKeyPane) {
      if (view) view.didBecomeKeyResponderFrom(current) ; 
      if (current) current.didLoseKeyResponderTo(view) ;
    }
    
    if (view) view.didBecomeFirstResponder();
    return this ;
  },
  
  /** @private method forwards status changes in a generic way. */
  _forwardKeyChange: function(shouldForward, methodName, pane) {
    var keyView, responder, newKeyView, isKey;
    if (shouldForward && (responder = this.get('firstResponder'))) {
      newKeyView = (pane) ? pane.get('firstResponder') : null ;
      keyView = this.get('firstResponder') ;
      if (keyView) keyView[methodName](newKeyView);
      
      if ((isKey !== undefined) && responder) {
        responder.set('isKeyResponder', isKey);
      }
    } 
  },
  
  /**
    Called just before the pane loses it's keyPane status.  This will notify 
    the current keyView, if there is one, that it is about to lose focus, 
    giving it one last opportunity to save its state. 
    
    @param {SC.Pane} pane
    @returns {SC.Pane} reciever
  */
  willLoseKeyPaneTo: function(pane) {
    this._forwardKeyChange(this.get('isKeyPane'), 'willLoseKeyResponderTo', pane, NO);
    return this ;
  },
  
  /**
    Called just before the pane becomes keyPane.  Notifies the current keyView 
    that it is about to gain focus.  The keyView can use this opportunity to 
    prepare itself, possibly stealing any value it might need to steal from the 
    current key view.
    
    @param {SC.Pane} pane
    @returns {SC.Pane} receiver
  */
  willBecomeKeyPaneFrom: function(pane) {
    this._forwardKeyChange(!this.get('isKeyPane'), 'willBecomeKeyResponderFrom', pane);
    return this ;
  },


  /**
    Called just after the pane has lost its keyPane status.  Notifies the 
    current keyView of the change.  The keyView can use this method to do any 
    final cleanup and changes its own display value if needed.
    
    @param {SC.Pane} pane
    @returns {SC.Pane} reciever
  */
  didLoseKeyPaneTo: function(pane) {
    var isKeyPane = this.get('isKeyPane');
    this.set('isKeyPane', NO);
    this._forwardKeyChange(isKeyPane, 'didLoseKeyResponderTo', pane);
    return this ;
  },
  
  /**
    Called just after the keyPane focus has changed to the receiver.  Notifies 
    the keyView of its new status.  The keyView should use this method to update 
    its display and actually set focus on itself at the browser level if needed.
    
    @param {SC.Pane} pane
    @returns {SC.Pane} receiver

  */
  didBecomeKeyPaneFrom: function(pane) {
    var isKeyPane = this.get('isKeyPane');
    this.set('isKeyPane', YES);
    this._forwardKeyChange(!isKeyPane, 'didBecomeKeyResponderFrom', pane, YES);
    return this ;
  },
  
  // .......................................................
  // MAIN PANE SUPPORT
  //
  
  isMainPane: NO,
  
  /**
    Invoked when the view is about to lose its mainPane status.  The default 
    implementation will also remove the pane from the document since you can't 
    have more than one mainPane in the document at a time.
  */
  blurMainTo: function(pane) {
    this.set('isMainPane', NO) ;
    // this.remove() ;
  },
  
  /** 
    Invokes when the view is about to become the new mainPane.  The default 
    implementation simply updates the isMainPane property.  In your subclass, 
    you should make sure your pane has been added to the document before 
    trying to make it the mainPane.  See SC.MainPane for more information.
  */
  focusMainFrom: function(pane) {
    this.set('isMainPane', YES);
  },
  
  // .......................................................
  // ADDING/REMOVE PANES TO SCREEN
  //  
  
  /**
    Inserts the pane at the end of the document.  This will also add the pane 
    to the rootResponder.
    
    @param {SC.RootResponder} rootResponder
    @returns {SC.Pane} receiver

  */
  append: function() { 
    return this.appendTo('body') ;
  },

  /**
    Removes the pane from the dsocument.  This will remove the
    DOM node and deregister you from the document window.
  */
  remove: function() {
    if (!this.get('isVisibleInWindow')) return this; // nothing to do

    // add to the DOM
    var dom = this.rootElement ;
    if (dom.parentNode) dom.parentNode.removeChild(dom);
    dom = null;
    
    // remove from the RootResponder also
    var responder = this.rootResponder ;
    if (this.get('isKeyPane')) responder.makeKeyPane(null) ; // orders matter, remove keyPane first
    if (this.get('isMainPane')) responder.makeMainPane(null);
    responder.panes.remove(this) ;
    this.rootResponder = responder = null ;

    // clean up some of my own properties    
    this.set('isPaneAttached', NO) ;
    this.parentViewDidChange() ;
  },

  /** 
    Inserts the pane into the DOM as the last child of the passed DOM element. 
    You can pass in either a CoreQuery object or a selector, which will be 
    converted to a CQ object.  You can optionally pass in the rootResponder 
    to use for this operation.  Normally you will not need to pass this as 
    the default responder is suitable.
    
    @param {String|CoreQuery} sel
    @returns {SC.Pane} receiver
  */
  appendTo: function(sel) {
    SC.$(sel).append(this.$()) ; // add to DOM
    return this.paneDidAttach(); // do the rest of the setup
  },

  /** 
    inserts the pane's rootElement into the top of the passed DOM element
  */
  prependTo: function(sel) {
    SC.$(sel).prepend(this.$()) ; // add to DOM
    return this.paneDidAttach(); // do the rest of the setup
  },

  /** 
    inserts the pane's rootElement into the hierarchy before the passed element.
  */
  before: function(sel) {
    this.$().before(SC.$(sel));
    return this.paneDidAttach(); // do the rest of the setup
  },

  /** 
    inserts the pane's rootElement into the hierarchy after the passed 
    element.
  */
  after: function(sel) {
    this.$().after(SC.$(sel));
    return this.paneDidAttach(); // do the rest of the setup
  },
  
  /**
    This method has no effect in the pane.  Instead use remove().
  */
  removeFromParent: function() { },
  
  /** @private
    Called when the pane is attached to a DOM element in a window, this will 
    change the view status to be visible in the window and also register 
    with the rootResponder.
  */
  paneDidAttach: function() {

    // hook into root responder
    var responder = (this.rootResponder = SC.RootResponder.responder);
    responder.panes.add(this);
    if (this.get('isKeyPane')) responder.makeKeyPane(this);
  
    // set currentWindowSize
    this.set('currentWindowSize', responder.computeWindowSize()) ;
    
    // update my own location
    this.set('isPaneAttached', YES) ;
    this.parentViewDidChange() ;
    return this ;
  },
  
  /** @property
    YES when the pane is currently attached to a document DOM.  Read only.
  */
  isPaneAttached: NO,

  /**
    Updates the isVisibleInWindow state on the pane and its childViews if 
    necessary.  This works much like SC.View's default implementation, but it
    does not need a parentView to function.
    
    @param {Boolean} parentViewIsVisible (ignored)
    @returns {SC.Pane} receiver
  */
  recomputeIsVisibleInWindow: function(parentViewIsVisible) {
    var last = this.get('isVisibleInWindow') ;
    var cur = this.get('isPaneAttached') && this.get('isVisible') ;
    
    // if the state has changed, update it and notify children
    if (last != cur) {
      this.set('isVisibleInWindow', cur) ;
      var childViews = this.get('childViews'), idx = childViews.length;
      while(--idx>=0) childViews[idx].recomputeIsVisibleInWindow(cur);
    }
  },
  
  updateDisplayLocation: function() {
    // note: the normal code here to update node location is removed 
    // because we don't need it for panes.
    
    // update visibility of element as needed
    var v, isVisible = this.get('isVisible') ;
    v = isVisible ? this.$().show() : this.$().hide();
    
    if (!isVisible && this.get('isVisibleInWindow')) {
      this.recomputeIsVisibleInWindow();
      // do this only after we have gone offscreen.
    }
    
    return this ; 
  },

  init: function() {
    var hasRootElement = !!this.rootElement ;
    arguments.callee.base.apply(this,arguments) ;
    if (hasRootElement) this.paneDidAttach() ;
    // this.paneDidAttach() ;
  },
  
  emptyElement: '<div class="sc-pane"></div>'
  
}) ;



/* End ------------------------------------------------------- views/panes/pane.js*/

/* Start ----------------------------------------------------- views/panes/main_pane.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/panes/pane');

/** @class

  Most SproutCore applications have a main pane, which dominates the application 
  page.  You can extend from this view to implement your own main pane.  This 
  class will automatically make itself main whenever you append it to a document, 
  removing any other main pane that might be currently in place.  If you do have 
  another already focused as the keyPane, this view will also make itself key 
  automatically.  The default way to use the main pane is to simply add it to 
  your page like this:
  
  {{{
    SC.MainPane.create().append();
  }}}
  
  This will cause your root view to display.  The default layout for a MainPane 
  is to cover the entire document window and to resize with the window.

  @extends SC.Pane
  @since SproutCore 1.0
*/
SC.MainPane = SC.Pane.extend({

  layout: { left:0, right:0, top:0, bottom:0 },
  
  /** @private - extends SC.Pane's method */
  paneDidAttach: function() {
    var ret = arguments.callee.base.apply(this,arguments);
    var responder = this.rootResponder;
    responder.makeMainPane(this);
    if (!responder.get('keyRootView')) responder.makeKeyPane(this);
    return ret ;
  },
  
  acceptsKeyFocus: YES,
  
  emptyElement: '<div class="sc-pane sc-main-pane"></div>'
});

/* End ------------------------------------------------------- views/panes/main_pane.js*/

/* Start ----------------------------------------------------- views/panes/dialog_pane.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/panes/pane');

/** @class

  Most SproutCore applications need modal dialogs. The default way to use the 
  dialog pane is to simply add it to your page like this:
  
  {{{
    SC.DialogPane.create({
      dialogView: SC.View.extend({
        layout: { width: 400, height: 200, centerX: 0, centerY: 0 }
      })
    }).append();
  }}}
  
  This will cause your dialog to display.  The default layout for a DialogPane 
  is to cover the entire document window with a semi-opaque background, and to 
  resize with the window.
  
  @extends SC.Pane
  @author Erich Ocean
  @since SproutCore 1.0
*/
SC.DialogPane = SC.Pane.extend({

  emptyElement: '<div class="sc-pane sc-dialog-pane"></div>',

  layout: { left:0, right:0, top:0, bottom:0 },
  
  acceptsKeyFocus: YES,
  
  // mouseDown: function(evt) {
  //   console.log('mouseDown invoked on %@'.fmt(this));
  //   return YES ;
  // },
  
  /**
    Set this to the view you want to act as the dialog within the dialog pane.
    
    @type {SC.View}
  */
  dialogView: null,
  
  mouseDown: function(evt) { return YES; },
  
  /** @private */
  dialogViewBindingDefault: SC.Binding.single(),
  
  /** @private - extends SC.Pane's method */
  paneDidAttach: function() {
    var ret = arguments.callee.base.apply(this,arguments);
    var responder = this.rootResponder;
    responder.makeMainPane(this);
    responder.makeKeyPane(this);
    return ret ;
  },
  
  /**
    Replaces any child views with the passed new content.  
    
    This method is automatically called whenever your dialogView property 
    changes.  You can override it if you want to provide some behavior other
    than the default.
    
    @param {SC.View} newContent the new dialog view or null.
  */
  replaceContent: function(newContent) {
    this.removeAllChildren() ;
    if (newContent) this.appendChild(newContent) ;
  },

  /** @private */
  createChildViews: function() {
    // if dialogView is defined, then create the content
    var view = this.dialogView ;
    if (view) {
      view = this.dialogView = this.createChildView(view) ;
      this.childViews = [view] ;
    }
  },
  
  /**
    Invoked whenever the content property changes.  This method will simply
    call replaceContent.  Override replaceContent to change how the view is
    swapped out.
  */
  dialogViewDidChange: function() {
    this.replaceContent(this.get('dialogView'));
  }.observes('dialogView')
  
});

/* End ------------------------------------------------------- views/panes/dialog_pane.js*/

/* Start ----------------------------------------------------- views/mixins/inline_editor_delegate.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/**
  @namespace
  
  The inline editor delegate receives notifications from the inline text
  editor before, during, and after the user completes inline editing.
  
  The inline editor delegate is used by views that work with the inline
  editor.  You may need to implement this protocol if you want to
  use the inline editor in your own custom views.
  
  @since SproutCore 1.0
*/
SC.InlineEditorDelegate = {
  
    /**
     This is a  classname you can apply to the inline editor field
     to configure it's styling, in addition to the the editor's 
     default style-cloning behavior.
      
      @property inlineEditorClassName {String} A class name to use with the inline editor.
    */
    inlineEditorClassName: "",
  
  
    /**
      Called just before the inline edit displays itself but after it has been 
      configured for display.  
      
      You can use this method to make last minute changes to the display of 
      the inline editor or to collect its value.
      
      @param inlineEditor {SC.InlineTextFieldView} The inline editor.
      @returns {void}
    */
    inlineEditorWillBeginEditing: function(inlineEditor) {},

    /**
      Called just after an inline editor displays itself.
      
      You can use this method to perform any hiding or other view changes
      you need to perform on your own view to make room for the new editor.
      
      Note tht editors are placed over the top of views in the page, not 
      inside of them from a DOM perspective.
      
      @param inlineEditor {SC.InlineTextFieldView} The inline editor.
      @returns {void}
    */
    inlineEditorDidBeginEditing: function(inlineEditor) {},
    
    /**
      Called just before an inline editor tries to end editing and hide 
      itself.
      
      You can use this method to control whether the inline editor will
      actually be allowed to end editing.  For example, you might disallow
      the editor to end editing if the new value fails validation.
      
      @param inlineEditor {SC.InlineTextFieldView} the inline editor
      @param finalValue {Object} the final value
      @returns {Boolean} YES to allow the editor to end editing.
    */
    inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
      return YES ;
    },
    
    /**
      Called just after the inline editor has ended editing. You can use this 
      method to save the final value of the inline editor and to perform any 
      other cleanup you need to do.
      
      @param inlineEditor {SC.InlineTextFieldView} the inline editor
      @param finalValue {Object} the final value
      @returns {void}
    */
    inlineEditorDidEndEditing: function(inlineEditor, finalValue) {}
};


/* End ------------------------------------------------------- views/mixins/inline_editor_delegate.js*/

/* Start ----------------------------------------------------- desktop.platform/views/list_item.js*/

// ==========================================================================
// SC.ListItemView
// ==========================================================================

require('core') ;
require('deprecated/views/classic_view') ;
require('mixins/control');
require('mixins/inline_editor_delegate');

SC.LIST_ITEM_ACTION_CANCEL = 'sc-list-item-cancel-action';
SC.LIST_ITEM_ACTION_REFRESH = 'sc-list-item-cancel-refresh';
SC.LIST_ITEM_ACTION_EJECT = 'sc-list-item-cancel-eject';

/** @class

  Many times list items need to display a lot more than just a label of text.
  You often need to include checkboxes, icons, extra counts and an action or 
  warning icon to the far right. 
  
  A ListItemView can implement all of this for you in a more efficient way 
  than you might get if you simply put together a list item on your own using
  views.


  @extends SC.View
  @extends SC.Control
  @extends SC.InlineEditorDelegate
  @extends SC.Editable
  @author  Charles Jolley  
  @since SproutCore 1.0
*/
SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
/** @scope SC.ListItemView.prototype */ {
  
  styleClass: 'sc-list-item-view sc-collection-item'.w(),
  
  /**
    The content object the list item will display.
  */
  content: null,
  
  /**
    (displayDelegate) True if you want the item view to display an icon.
    
    If false, the icon on the list item view will be hidden.  Otherwise,
    space will be left for the icon next to the list item view.
  */
  hasContentIcon: NO,
  
  /**
    (displayDelegate) True if you want space to be allocated for a branch 
    arrow.
    
    If false, the space for the branch arrow will be collapsed.
  */
  hasContentBranch: NO,
  
  /**
    (displayDelegate) The name of the property used for the checkbox value.
    
    The checkbox will only be visible if this key is not null.
    
    @type {String}
  */
  contentCheckboxKey: null,
  
  /**
    (displayDelegate) Property key to use for the icon url

    This property will be checked on the content object to determine the 
    icon to display.  It must return either a URL or a CSS class name.
  */
  contentIconKey: null,
  
  /**
    (displayDelegate) The name of the property used for label itself
    
    If null, then the content object itself will be used..
  */
  contentValueKey: null,
  
  /**
    (displayDelegate) The name of the property used to find the count of 
    unread items. 
    
    The count will only be visible if this property is not null and the 
    returned value is not 0.
  */
  contentUnreadCountKey: null,
  
  /**
    (displayDelegate) The name of the property used to determine if the item
    is a branch or leaf (i.e. if the branch icon should be displayed to the
    right edge.)
    
    If this is null, then the branch view will be completely hidden.
    Otherwise space will be allocated for it.
  */
  contentIsBranchKey: null,
  
  /**
    YES if the item view is currently editing.
  */
  isEditing: NO,
  
  contentPropertyDidChange: function() {
    if (this.get('isEditing')) this.discardEditing() ;
    this.render() ;  
  },
  
  
  /**
    Regenerates the innerHTML for this view and updates it if necessary.
  */
  render: function() {
    var html = [] ;
    var content = this.get('content') ;
    var del = this.displayDelegate ;
    
    // handle checkbox
    var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
    if (checkboxKey) {
      var checkboxValue = (content && content.get) ? content.get(checkboxKey) : false ;
      html.push(this.renderCheckboxHtml(checkboxValue)) ;
    }

    // handle icon
    if (this.getDelegateProperty(del, 'hasContentIcon')) {
       var iconKey = this.getDelegateProperty(del,'contentIconKey') ;
       var icon = (iconKey && content && content.get) ? content.get(iconKey) : null ;
       html.push(this.renderIconHtml(icon));
    }
    
    // handle label
    var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
    var label = (labelKey && content && content.get) ? content.get(labelKey) : null ;
    html.push(this.renderLabelHtml(label));
    
    // handle unread count
    var countKey = this.getDelegateProperty(del, 'contentUnreadCountKey') ;
    var count = (countKey && content && content.get) ? content.get(countKey) : null ;
    if ((count != null) && (count != 0)) {
      html.push(this.renderCountHtml(count));
    }
    
    // handle action 
    var actionKey = this.getDelegateProperty(del, 'listItemActionProperty') ;
    var actionClassName = (actionKey && content && content.get) ? content.get(actionKey) : null ;
    if (actionClassName) {
       html.push(this.renderActionHtml(actionClassName));
    }
    this.setClassName('sc-has-action', actionClassName) ;
    
    // handle branch
    if (this.getDelegateProperty(del, 'hasContentBranch')) {
      var branchKey = this.getDelegateProperty(del, 'contentIsBranchKey');
      var hasBranch = (branchKey && content && content.get) ? content.get(branchKey) : false ;
      html.push(this.renderBranchHtml(hasBranch));
      this.setClassName('sc-has-branch', true) ;
    } else this.setClassName('sc-has-branch', false) ;
    
    html = html.join('') ;
    if (html != this._lastRenderedHtml) {
      this._lastRenderedHtml = html ;
      this.set('innerHTML', html) ;
    }
  },
  
  /**
    Generates the HTML string used to represent the checkbox for your list
    item.  Override this to return your own custom HTML.  The default version
    will use the HTML provided by SC.CheckboxView.
    
    @returns {String}
    @param state {String} the checkbox state.  YES, NO, or SC.MIXED_STATE
  */
  renderCheckboxHtml: function(state) {
    var ret ;
    
    // Note: this basically takes the HTML from the checkbox view and then
    // inserts class names as necessary.  This is cached to avoid using too
    // much memory.
    if (state === SC.MIXED_STATE) {
      ret = SC.ListItemView._mixedCheckboxHtml ;
      if (!ret) {
        ret = SC.CheckboxView.prototype.emptyElement ;
        ret = ret.replace('class="', 'class="mixed ') ;
        SC.ListItemView._mixedCheckboxHtml = ret ;
      }
    } else if (state) {
      ret = SC.ListItemView._selectedCheckboxHtml ;
      if (!ret) {
        ret = SC.CheckboxView.prototype.emptyElement ;
        ret = ret.replace('class="', 'class="sel ') ;
        SC.ListItemView._selectedCheckboxHtml = ret ;
      }
    } else {
      ret = SC.ListItemView._normalCheckboxHtml ;
      if (!ret) {
        ret = SC.CheckboxView.prototype.emptyElement ;
        SC.ListItemView._normalCheckboxHtml = ret ;
      }
    }
    return ret ;
  },
  
  /** 
     renderIconHtml generates the html string used to represent the icon for 
     your list item.  override this to return your own custom HTML
     
     @returns {String}
     @param icon {String} the icon property based on your view's contentIconKey
   */
   renderIconHtml: function(icon){
     var html = [];
     // get a class name and url to include if relevant
     var url = null, className = null ;
     if (icon && SC.ImageView.valueIsUrl(icon)) {
       url = icon; className = '' ;
     } else {
       className = icon; url = '/sc-apps/sc-statechart/sproutcore/en/desktop/ac2c8cef664802102e1d7f3875d5dc0ea4e0f1ae/blank.gif' ;
     }
     html.push('<img class="sc-icon ');
     html.push(className || '');
     html.push('" src="');
     html.push(url || '/sc-apps/sc-statechart/sproutcore/en/desktop/ac2c8cef664802102e1d7f3875d5dc0ea4e0f1ae/blank.gif') ;
     html.push('" />') ;
     html=html.join('');
     return html;
   },
   
   /** 
       renderLabelHtml generates the html string used to represent the label 
       for your list item.  override this to return your own custom HTML
       
       @returns {String}
       @param label {String} the label property based on your view's 
        contentValueKey
     */
   renderLabelHtml: function(label){
     var html = [];
     html.push('<span class="sc-label">') ;
     html.push(label || '') ;
     html.push('</span>') ;
     return html.join('');    
   },
   
   /**
      Finds and retrieves the element containing the label.  This is used
      for inline editing.  If you override renderLabelHtml() you probably
      need to override this as well.
  */
   findLabelElement: function() {
     return this.$class('sc-label') ;
   },
   
   /** 
        renderCountHtml generates the html string used to represent the count 
        (like unread count) for your list item.  override this to return your 
        own custom HTML
        
        @returns {String}
        @param count {Integer} the label property based on your view's 
         contentValueKey
    */
   renderCountHtml: function(count) {
     var html= [];
      html.push('<span class="sc-count"><span class="inner">') ;
       html.push(count.toString()) ;
       html.push('</span></span>') ;
       return html.join('');
   },
   
   /** 
      renderActionHtml generates the html string used to represent the 
      action item for your list item.  override this to return your own 
      custom HTML
    
      @returns {String}
      @param actionClassName {String} the name of the action item.
    */
   renderActionHtml: function(actionClassName){
     var html = [];
     html.push('<img src="') ;
     html.push('/sc-apps/sc-statechart/sproutcore/en/desktop/ac2c8cef664802102e1d7f3875d5dc0ea4e0f1ae/blank.gif') ;
     html.push('" class="sc-action" />') ;
     return html.join('');
   },
   
   /** 
     renderBranchHtml generates the html string used to represent the 
     branch arrow. override this to return your own custom HTML
     @returns {String}
     @arguments {Boolean} whehter the branch is 
   */
   
   renderBranchHtml: function(hasBranch) {
     var html = [];
     html.push('<span class="sc-branch ');
     html.push(hasBranch ? 'sc-branch-visible' : 'sc-branch-hidden') ;
     html.push('">&nbsp;</span>');
     return html.join('');
   },
   
   _isInsideElementWithClassName: function(className, evt) {
     var el = Event.element(evt) ;
     var rootElement = this.rootElement;
     var ret = NO ;
     while(!ret && el && (el !== rootElement)) {
       if (Element.hasClassName(el, className)) ret = YES ;
       el = el.parentNode ;
     }
     
     rootElement = el = null ; //avoid memory leaks
     return ret ;
   },
   
   /** @private 
    mouseDown is handled only for clicks on the checkbox view or or action
    button.
   */
   mouseDown: function(evt) {
     var del = this.displayDelegate ;
     var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
     if (checkboxKey) {
       if (this._isInsideElementWithClassName('sc-checkbox-view', evt)) {
         this._addCheckboxActiveState() ;
         this._isMouseDownOnCheckbox = YES ;
         this._isMouseInsideCheckbox = YES ;
         return true ;
       }
     }  
     
     return false ; // otherwise let normal handlers do it...
   },

   mouseUp: function(evt) {
     var ret= NO ;
     if (this._isMouseDownOnCheckbox) {
       
       // update only if mouse inside on mouse up...
       if (this._isMouseInsideCheckbox) {
         var del = this.displayDelegate ;
         var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
         var content = this.get('content') ;
         if (content && content.get) {
           var value = content.get(checkboxKey) ;
           value = (value === SC.MIXED_STATE) ? YES : !value ;
           content.set(checkboxKey, value) ;
         }
       }

       this._removeCheckboxActiveState() ;
       ret = YES ;
     } 

     this._isMouseInsideCheckbox = this._isMouseDownOnCheckbox = NO ;
     return ret ;
   },
   
   mouseOut: function(evt) {
     if (this._isMouseDownOnCheckbox) {
       this._removeCheckboxActiveState() ;
       this._isMouseInsideCheckbox = NO ;
     }  
     return NO ;
   },
   
   mouseOver: function(evt) {
     if (this._isMouseDownOnCheckbox) {
       this._addCheckboxActiveState() ;
       this._isMouseInsideCheckbox = YES ;
     }  
     return NO ;
   },
   
   _addCheckboxActiveState: function() {
     var el = this.$sel('.sc-checkbox-view') ;
     if (this.get('isEnabled')) Element.addClassName(el, 'active') ;
     el = null ;
   },
   
   _removeCheckboxActiveState: function() {
     var el = this.$sel('.sc-checkbox-view') ;
     Element.removeClassName(el, 'active') ;
     el = null ;
   },
   
   /** 
    Returns true if a click is on the label text itself to enable editing.
    
    Note that if you override renderLabelHtml(), you probably need to override 
    this as well.
  
    @param evt {Event} the mouseUp event.
    @returns {Boolean} YES if the mouse was on the content element itself.
   */
   contentHitTest: function(evt) {
     
     // if not content value is returned, not much to do.
     var del = this.displayDelegate ;
     var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
     if (!labelKey) return NO ;
     
     // get the element to check for.
     var el = this.findLabelElement() ;
     if (!el) return NO ; // no label to check for.
     
     var cur = Event.element(evt) ;
     while(cur && (cur != (this.rootElement)) && (cur != window)) {
       if (cur === el) return YES ;
       cur = cur.parentNode ;
     }
     
     return NO;
   },
   
   beginEditing: function() {
     
     if (this.get('isEditing')) return YES ;
     
     var content = this.get('content') ;
     var del = this.displayDelegate ;
     var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
     var v = (labelKey && content && content.get) ? content.get(labelKey) : null ;
     
     var f = this.get('frame') ;
     var el = this.findLabelElement() ;
     if (!el) return NO ;

     // if the label has a large line height, try to adjust it to something
     // more reasonable so that it looks right when we show the popup editor.
     var oldLineHeight = Element.getStyle(el, 'lineHeight') ;
     var fontSize = parseInt(Element.getStyle(el, 'fontSize'), 0) ;
     var lineHeight = parseInt(oldLineHeight, 0) ;
     var lineHeightShift = 0;
     
     if (fontSize && lineHeight) {
       var targetLineHeight = fontSize * 1.5 ;
       if (targetLineHeight < lineHeight) {
         Element.setStyle(el, { lineHeight: '1.5' }) ;
         lineHeightShift = (lineHeight - targetLineHeight) / 2; 
       } else oldLineHeight = null ;
     }
     
     f.x += el.offsetLeft ;
     f.y += el.offsetTop + lineHeightShift - 2;
     f.height = el.offsetHeight ;
     f.width = (f.width - 30 - el.offsetLeft) ;
     f = this.convertFrameToView(f, null) ;
     
     var ret = SC.InlineTextFieldView.beginEditing({
       frame: f, 
       exampleElement: el, 
       delegate: this, 
       value: v
     }) ;

     // restore old line height for original item if the old line height 
     // was saved.
     if (oldLineHeight) Element.setStyle(el, { lineHeight: oldLineHeight }) ;
     
     // Done!  If this failed, then set editing back to no.
     return ret ;
   },
   
   commitEditing: function() {
     if (!this.get('isEditing')) return YES ;
     return SC.InlineTextFieldView.commitEditing();
   },
   
   discardEditing: function() {
     if (!this.get('isEditing')) return YES ;
     return SC.InlineTextFieldView.discardEditing();
   },
   
   /** @private
     Set editing to true so edits will no longer be allowed.
   */
   inlineEditorWillBeginEditing: function(inlineEditor) {
     this.set('isEditing', YES);
   },

   /** @private 
     Hide the label view while the inline editor covers it.
   */
   inlineEditorDidBeginEditing: function(inlineEditor) {
     var el = this.findLabelElement() ;
     this._oldOpacity = Element.getStyle(el, 'opacity') ;
     Element.setStyle(el, { opacity: 0.0 }) ;
   },

   /** @private
     Could check with a validator someday...
   */
   inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
     return YES ;
   },

   /** @private
     Update the field value and make it visible again.
   */
   inlineEditorDidEndEditing: function(inlineEditor, finalValue) {
     this.set('isEditing', NO) ;

     var content = this.get('content') ;
     var del = this.displayDelegate ;
     var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
     if (labelKey && content && content.set) {
       content.set(labelKey, finalValue) ;
     }
     
     // force a refresh, otherwise the label will never be visible again
     // b/c its opacity is 0.
     this._lastRenderedHtml = null;
     this.render() ;
   }   
  
}) ;


/* End ------------------------------------------------------- desktop.platform/views/list_item.js*/

/* Start ----------------------------------------------------- validators/validator.js*/

// ==========================================================================
// Validators
// Author: Charles Jolley
// ==========================================================================

require('system/object');

SC.VALIDATE_OK = YES;
SC.VALIDATE_NO_CHANGE = NO;

/**
  @class
  
  Validators provide a way for you to implement simple form field validation
  and transformation.  To use a validator, simply name the validator in the
  "validate" attribute in your text field.  For example, if you want to
  validate a field using the PhoneNumberValidator use this:

  <input value="1234567890" validate="phone-number" />

  Validators get notified at three points.  You can implement one or all
  of these methods to support validation.  All of the validate methods except
  for validateKeypress behave the same way.  You are passed a form, field,
  and possibly the oldValue.  You are expected to return Validator.OK or
  an error string.  Inside this method you typically do one of all of the
  following:

  1. You can simply validate the field value and return OK or an error str
  
  2. You can modify the field value (for example, you could format the
     string to match some predefined format).
     
  3. If you need to roundtrip the server first to perform validation, you can
     return Validator.OK, then save the form and field info until after the
     roundtrip.  On return, if there is a problem, first verify the field
     value has not changed and then call form.errorFor(field,str) ;

  @extends SC.Object
  @since SproutCore 1.0
*/
SC.Validator = SC.Object.extend(
/** @scope SC.Validator.prototype */ {

  // ..........................................
  // OBJECT VALUE CONVERSION
  //
  // The following methods are used to convert the string value of a field
  // to and from an object value.  The default implementations return
  // the string, but you can override this to provide specific behaviors. 
  // For example, you might add or remove a dollar sign or convert the 
  // value to a number.
  
/**
  Returns the value to set in the field for the passed object value.  
  
  The form and view to be set MAY (but will not always) be passed also.  You
  should override this method to help convert an input object into a value
  that can be displayed by the field.  For example, you might convert a 
  date to a property formatted string or a number to a properly formatted
  value.
  
  @param {Object} object The object to transform
  @param {SC.FormView} form The form this field belongs to. (optional)
  @param {SC.View} view The view the value is required for.
  @returns {Object} a value (usually a string) suitable for display
*/
  fieldValueForObject: function(object, form, view) { return object; },
  
  /**
    Returns the object value for the passed string.
    
    The form and view MAY (but wil not always) be passed also.  You should
    override this method to convert a field value, such as string, into an
    object value suitable for consumption by the rest of the app.  For example
    you may convert a string into a date or a number.
    
    @param {String} value the field value.  (Usually a String).
    @param {SC.FormView} form The form this field belongs to. (optional)
    @param {SC.View} view The view this value was pulled from.
    @returns {Object} an object suitable for consumption by the app.
  */
  objectForFieldValue: function(value, form, view) { return value; },
  
  // ..........................................
  // VALIDATION PRIMITIVES
  //

  /**
    Validate the field value.  
    
    You can implement standard behavior for your validator by using the validate() and validateError() methods.  validate() should return NO if the field is not valid, YES otherwise.  If you return NO from this method, then the validateError() method will be called so you can generate an error object describing the specific problem.

    @param {SC.FormView} form the form this view belongs to
    @param {SC.View} field the field to validate.  Responds to fieldValue.
    @returns {Boolean} YES if field is valid.
  */
  validate: function(form, field) { return true; },

  /**
    Returns an error object if the field is invalid.
  
    This is the other standard validator method that can be used to impement basic validation.  Return an error object explaining why the field is not valid.  It will only be called if validate() returned NO.
    
    The default implementation of htis method returns a generic error message with the loc string "Invalid.Generate({fieldValue})".  You can simply define this loc string in strings.js if you prefer or you can override this method to provide a more specific error message.
  
    @param {SC.FormView} form the form this view belongs to
    @param {SC.View} field the field to validate.  Responds to fieldValue.
    @returns {SC.Error} an error object
  */
  validateError: function(form, field) { 
    return SC.$error(
      "Invalid.General(%@)".loc(field.get('fieldValue')),
      field.get('fieldKey')) ; 
  },

  // ..........................................
  // VALIDATION API
  //

  /**
    Invoked just before the user ends editing of the field.

    This is a primitive validation method.  You can implement the two higher-level methods (validate() and validateError()) if you prefer.
    
    The default implementation calls your validate() method and then validateError() if valiate() returns NO.  This method should return SC.VALIDATE_OK if validation succeeded or an error object if it fails.
  
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate
    @param {Object} oldValue: the value of the field before the change

    @returns SC.VALIDATE_OK or an error object.
  
  */
  validateChange: function(form, field, oldValue) { 
    return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field);
  },

  /**
    Invoked just before the form is submitted.
  
    This method gives your validators one last chance to perform validation
    on the form as a whole.  The default version does the same thing as the 
    validateChange() method.
  
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate

    @returns SC.VALIDATE_OK or an error object.
  
  */  
  validateSubmit: function(form, field) { 
    return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field);
  },

  /**
    Invoked 1ms after the user types a key (if a change is allowed).  
  
    You can use this validate the new partial string and return an error if 
    needed. The default will validate a partial only if there was already an 
    error. This allows the user to try to get it right before you bug them.
  
    Unlike the other methods, you should return SC.VALIDATE_NO_CHANGE if you
    did not actually validate the partial string.  If you return 
    SC.VALIDATE_OK then any showing errors will be hidden.
  
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate

    @returns SC.VALIDATE_OK, SC.VALIDATE_NO_CHANGE or an error object.
  */  
  validatePartial: function(form, field) { 
    if (!field.get('isValid')) {
      return this.validate(form,field) ? SC.VALIDATE_OK : this.validateError(form, field);
    } else return SC.VALIDATE_NO_CHANGE ;
  },
  
  /**
    Invoked when the user presses a key.  
  
    This method is used to restrict the letters and numbers the user is 
    allowed to enter.  You should not use this method to perform full 
    validation on the field.  Instead use validatePartial().
  
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate
    @param {String} char the characters being added
    
    @returns {Boolean} YES if allowed, NO otherwise
  */
  validateKeyDown: function(form, field,charStr) { return true; },

  // .....................................
  // OTHER METHODS

  /**
    Called on all validators when they are attached to a field.  
  
    You can use this to do any setup that you need.  The default does nothing.
    
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate
  */
  attachTo: function(form,field) { },

  /**
    Called on a validator just before it is removed from a field.  You can 
    tear down any setup you did for the attachTo() method.
    
    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate
  */
  detachFrom: function(form, field) {}

}) ;

SC.Validator.mixin(/** @scope SC.Validator */ {

  /**
    Return value when validation was performed and value is OK.
  */
  OK: true, 
  
  /**
    Return value when validation was not performed.
  */
  NO_CHANGE: false,  

  /**
    Invoked by a field whenever a validator is attached to the field.
    
    The passed validatorKey can be a validator instance, a validator class
    or a string naming a validator. To make your validator
    visible, you should name your validator under the SC.Validator base.
    for example SC.Validator.Number would get used for the 'number' 
    validator key.
  
    This understands validatorKey strings in the following format:

    * 'key' or 'multiple_words' will find validators Key and MultipleWords

    * if you want to share a single validator among multiple fields (for
      example to validate that two passwords are the same) set a name inside
      brackets. i.e. 'password[pwd]'.

    @param {SC.FormView} form the form for the field
    @param {SC.View} field the field to validate
    @param {Object} validatorKey the key to validate
    
    @returns {SC.Validator} validator instance or null
  */  
  findFor: function(form,field, validatorKey) {
    
    // Convert the validator into a validator instance.
    var validator ;
    if (!validatorKey) return ; // nothing to do...
    
    if (validatorKey instanceof SC.Validator) {
      validator = validatorKey ;
    } else if (validatorKey.isClass) {
      validator = validatorKey.create() ;
      
    } else if (SC.$type(validatorKey) === SC.T_STRING) {

      // extract optional key name
      var name = null ;
      var m = validatorKey.match(/^(.+)\[(.*)\]/) ;
      if (m) {
        validatorKey = m[1] ; name = m[2]; 
      }
      
      // convert the validatorKey name into a class.
      validatorKey = validatorKey.classify() ;
      var validatorClass = SC.Validator[validatorKey] ;
      if (SC.none(validatorClass)) {
        throw "validator %@ not found for %@".fmt(validatorKey, field) ;
      } else if (name) {

        // if a key was also passed, then find the validator in the list of
        // validators for the form.  Otherwise, just create a new instance.
        if (!form) {
          throw "named validator (%@) could not be found for field %@ because the field does not belong to a form".fmt(name,field) ;
        }
        
        if (!form._validatorHash) form._validatorHash = {} ;
        validator = (name) ? form._validatorHash[name] : null ;
        if (!validator) validator = validatorClass.create() ;
        if (name) form._validatorHash[name] = validator ;
      } else validator = validatorClass.create() ;
    } 
    
    return validator ;
  },
  
  /**
    Convenience class method to call the fieldValueForObject() instance
    method you define in your subclass.
  */
  fieldValueForObject: function(object, form, field) {
    return this.prototype.fieldValueForObject(object,form,field) ;
  },

  /**
    Convenience class method to call the objectForFieldValue() instance
    method you define in your subclass.
  */
  objectForFieldValue: function(value, form, field) {
    return this.prototype.objectForFieldValue(value,form,field) ;
  }
  
}) ;



/* End ------------------------------------------------------- validators/validator.js*/

/* Start ----------------------------------------------------- validators/number.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('validators/validator') ;

/**
  Handles parsing and validating of numbers.
  
  @extends SC.Validator
  @author Charles Jolley
  @version 1.0
  @class
*/
SC.Validator.Number = SC.Validator.extend(
/** @scope SC.Validator.Number.prototype */ {

  /**
    Number of decimal places to show.  
    
    If 0, then numbers will be treated as integers.  Otherwise, numbers will
    show with a fixed number of decimals.
  */
  places: 0,
  
  fieldValueForObject: function(object, form, field) {
    switch(SC.$type(object)) {
      case SC.T_NUMBER:
        object = object.toFixed(this.get('places')) ;
        break ;
      case SC.T_NULL:
      case SC.T_UNDEFINED:
        object = '';
        break ;
    }
    return object ;
  },

  objectForFieldValue: function(value, form, field) {
    
    // strip out commas
    value = value.replace(/,/g,'');
    
    switch(SC.$type(value)) {
      case SC.T_STRING:
        if (value.length == '') {
          value = null ;
        } else if (this.get('places') > 0) {
          value = parseFloat(value) ;
        } else {
          value = parseInt(value,0) ;
        }
        break ;
      case SC.T_NULL:
      case SC.T_UNDEFINED:
        value = null ;
        break ;
    }
    return value ;
  },
  
  validate: function(form, field) { 
    var value = field.get('fieldValue') ;
    return (value == '') || !(isNaN(value) || isNaN(parseFloat(value))) ; 
  },
  
  validateError: function(form, field) {
    var label = field.get('errorLabel') || 'Field' ;
    return SC.$error("Invalid.Number(%@)".loc(label), label) ;
  },
  
  /** 
    Allow only numbers, dashes, period, and commas
  */
  validateKeyDown: function(form, field, charStr) {
    return !!charStr.match(/[0-9\.,\-]/);
  }
    
}) ;


/* End ------------------------------------------------------- validators/number.js*/

/* Start ----------------------------------------------------- deprecated/core.js*/

// ==========================================================================
// SproutCore
// copyright 2006-2008, Sprout Systems, Inc.
// ==========================================================================

require('core');

/* Core additions to SC */

SC.mixin(/** @scope SC */ {
  
  /**  
    @deprecated
    
    Call this method during setup of your app to queue up methods to be 
    called once the entire document has finished loading.  If you call this
    method once the document has already loaded, then the function will be
    called immediately.
    
    Any function you register with this method will be called just before
    main.
    
    You should instead use ready(), which takes the same paramters.
    
    @param target {Object} optional target object.  Or just pass a method.
    @param method {Function} the method to call.
    @return {void}
  */
  callOnLoad: function(target, method) { 
    return SC.ready(target, method) ;
  },
  
  /** @deprecated  Use guidFor() instead. */
  getGUID: SC.guidFor
  
  
});

// Returns the passed item as an array.  If the item is already an array,
// it is returned as is.  If it is not an array, it is placed into one.  If
// it is null, an empty array is returned.
Array.from = SC.$A ;


/* End ------------------------------------------------------- deprecated/core.js*/

/* Start ----------------------------------------------------- validators/not_empty.js*/

// ==========================================================================
// Not Empty Validator
// Author: Charles Jolley
//
// Force a field to be not empty.
//
// ==========================================================================

require('validators/validator') ;

/**
  Requires some content in field, but does not check the specific content.
  
  @class
  @extends SC.Validator
  @author Charles Jolley
  @version 1.0
*/
SC.Validator.NotEmpty = SC.Validator.extend(
/** @scope SC.Validator.NotEmpty.prototype */ {
  
  validate: function(form, field) {
    var value = field.get('fieldValue'); 
    var ret = !!value ;
    if (ret && value.length) ret = value.length > 0 ;
    return ret ;
  },
  
  validateError: function(form, field) {
    var label = field.get('errorLabel') || 'Field' ;
    return SC.$error("Invalid.NotEmpty(%@)".loc(label.capitalize()), field.get('errorLabel'));
  }
    
}) ;


/* End ------------------------------------------------------- validators/not_empty.js*/

/* Start ----------------------------------------------------- system/mixins/selection_support.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/**
  @namespace 
  
  Implements common selection management properties for controllers.

  Selection can be managed by any controller in your applications.  This
  mixin provides some common management features you might want such as
  disabling selection, or restricting empty or multiple selections.

  To use this mixin, simply add it to any controller you want to manage 
  selection and call updateSelectionAfterContentChange()
  whenever your source content changes.  You can also override the properties
  defined below to configure how the selection management will treat your 
  content.
  
  This mixin assumes the arrangedObjects property will return the array of 
  content you want the selection to reflect.
  
  Add this mixin to any controller you want to manage selection.  It is 
  already applied to the CollectionController and ArrayController.

*/
SC.SelectionSupport = {
  
  /** 
    Call this method whenever  your source content changes to ensure the 
    selection always remains up-to-date and valid.
  */
  updateSelectionAfterContentChange: function() {
    var objects = SC.makeArray(this.get('arrangedObjects')) ;
    var currentSelection = SC.makeArray(this.get('selection')) ;
    var sel = [] ;

    // the new selection is the current selection that exists in 
    // arrangedObjects or an empty selection if selection is not allowed.
    var max = currentSelection.get('length') ;
    if (this.get('allowsSelection')) {
      for(var idx=0;idx<max;idx++) {
        var obj = currentSelection.objectAt(idx) ;
        if (objects.indexOf(obj) >= 0) sel.push(obj) ;
      }
    }

    // if the new selection is a multiple selection, then get the first
    // object.
    var selectionLength = sel.get('length') ;
    if ((selectionLength > 1) && !this.get('allowsMultipleSelection')) {
      sel = [sel.objectAt(0)] ;
    }

    // if the selection is empty, select the first item.
    if ((selectionLength == 0) && !this.get('allowsEmptySelection')) {
      if (objects.get('length') > 0) sel = [objects.objectAt(0)];
    }

    // update the selection.
    this.set('selection',sel) ;
  },
  
  /**
    @field {Array} arrangedObjects
    
    Returns the set of content objects the selection should be a part of.
    Selections in general may contain objects outside of this content, but
    this set will be used when enforcing items such as no empty selection.
    
    The default version of this property returns the receiver.
  */
  arrangedObjects: function() {
    return this;
  }.property(),
  
  /**  
    @field {Array} selection 

    This is the current selection.  You can make this selection and another
    controller's selection work in concert by binding them together. You
    generally have a master selection that relays changes TO all the others.
  */
  selection: function(key,value)
  {
    if (value !== undefined) {
      
      // always force to an array
      value = SC.makeArray(value) ;
    
      var allowsSelection         = this.get('allowsSelection');
      var allowsEmptySelection    = this.get('allowsEmptySelection');
      var allowsMultipleSelection = this.get('allowsMultipleSelection');
    
      // are we even allowing selection at all?
      // if not, then bail out.
      if ( !allowsSelection ) return this._selection;
    
      // ok, new decide if the *type* of seleciton is allowed...
      switch ( value.get('length') )
      {
        case 0:
          // check to see if we're attemting to set an empty array
          // if that's not allowed, set to the first available item in 
          // arrangedObjects
          if (!allowsEmptySelection) {
            var objects = this.get('arrangedObjects') ;
            if (objects.get('length') > 0) value = [objects.objectAt(0)];
          }
          this._selection = value ;
          break;
        case 1:
          this._selection = value;
          break;
        default:
          // fall through for >= 2 items...
          // only allow if configured for multi-select
          this._selection = allowsMultipleSelection ? value : this._selection;
          break;
      }
    }
  
    return this._selection;
  }.property(),

  /**
    If true, selection is allowed.

    @type bool
  */
  allowsSelection: true,

  /**
    If true, multiple selection is allowed.

    @type bool
  */
  allowsMultipleSelection: true,

  /**
    If true, allow empty selection

    @type bool
  */
  allowsEmptySelection: true,
  
  /**
    YES if the receiver currently has a non-zero selection.
    
    @property
    @type {Boolean}
  */
  hasSelection: function() {
    var sel = this.get('selection') ;
    return !!sel && (sel.get('length') > 0) ;
  }.property('selection')
    
};



/* End ------------------------------------------------------- system/mixins/selection_support.js*/

/* Start ----------------------------------------------------- models/store.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/object') ;

/**
  @class

  The Store is where you can find all of your records.  You should also
  use this to define your various types of records, since this will be
  used to automatically update from data coming from the server.

  You should create a store for each application.  This allows the records
  for apps to be kept separate, even if they live in the same page.

  @extends SC.Object
  @static
  @since SproutCore 1.0
*/
SC.Store = SC.Object.create(
/** @scope SC.Store.prototype */ {
  
  /**
    Pushes updated data to all the named records.  
  
    This method is often called from a server to update the store with the 
    included record objects.
  
    You can use this method yourself to mass update the store whenever you 
    retrieve new records from the server.  The first parameter should contain
    an array of JSON-compatible hashes.  The hashes can have any properties 
    you want but they should at least contain the following two keys:
  
    - guid: This is a unique identifier for the record. 
    - type: The name of the record type.  I.e. "Contact" or "Photo"
  
    @param dataHashes {Array} array of hash records.  See discussion.
    @param dataSource {Object} the data source.  Usually a server object.
    @param recordType {SC.Record} optional record type, used if type is not 
      found in the data hashes itself.
    @param isLoaded {Boolean} YES if the data hashes represent the full set of 
      data loaded from the server.  NO otherwise.

    @returns {Array} Array of records that were actually created/updated.
  */
  updateRecords: function(dataHashes, dataSource, recordType, isLoaded) {
    //console.log('SC.Store#updateRecords();')
    this.set('updateRecordsInProgress',true) ;
    var store = this ; 
    var ret = [] ;
    if (!recordType) recordType = SC.Record ;
    this.beginPropertyChanges() ;

    //SC.Benchmark._bench(function() { 
    dataHashes.each(function(data) {
      var rt = data.recordType || recordType; 
      if (data.recordType !== undefined) delete data.recordType;
      
      var pkValue = data[rt.primaryKey()] ;      
      var rec = store.getRecordFor(pkValue,rt,true) ;
      rec.dataSource = dataSource ;
      if (data.refreshURL) rec.refreshURL = data.refreshURL;
      if (data.updateURL) rec.updateURL = data.updateURL;
      if (data.destroyURL) rec.destroyURL = data.destroyURL;
      rec.updateAttributes(data, isLoaded, isLoaded) ;
      if (rec.needsAddToStore) store.addRecord(rec) ;
      ret.push(rec) ;
    });

    this.endPropertyChanges() ;
    this.set('updateRecordsInProgress',false) ;
    return ret ;
  },

  // ....................................
  // Record dataSource methods
  //

  refreshRecords: function(records) {},
  
  createRecords: function(recs) {
    recs.invoke('set','newRecord','false') ;
    this.commitRecords(recs); 
  },
  
  commitRecords: function(recs) {
     
    recs.invoke('set','isDirty','false'); 
    },
  
  destroyRecords: function(recs) {
    var store = this ;
    recs.each(function(r) { 
      r.set('isDeleted',true); store.removeRecord(r);
    });
  },
  
  // ....................................
  // Record Helpers
  //
  // Boolean Flag to tell whether the store is dirty
  hasChanged: NO,
  
  /**
    Add a record instance to the store.  The record will now be monitored for
    changes.
  */
  addRecord: function(rec) {
    // save record in a cache
    rec.needsAddToStore = false;
    var guid = rec._storeKey();
    var records = this._records[guid] || [];
    records.push(rec);
    this._records[guid] = records; 

    // global record cache
    if (!this._quickCache) this._quickCache = {};

    // records are cached by Class type
    var records = this._quickCache[guid] || {};
    var pkey = rec.get(rec.primaryKey);
    records[pkey] = rec;
    this._quickCache[guid] = records;

    // and start observing it.
    rec.addObserver('*',this, this.recordDidChange) ;
    this.recordDidChange(rec) ;
  },

  /**
    remove a record instance from the store.  The record will no longer be
    monitored for changes and may be deleted.
  */  
  removeRecord: function(rec) {
    // remove from cache
    var guid = rec._storeKey();
    var records = this._records[guid] || [];
    records = records.without(rec);
    this._records[guid] = records;
    
    // remove from quick cache
    if (this._quickCache)
    {
      var records = this._quickCache[guid] || {};
      var pkey = rec.get(rec.primaryKey);
      delete records[pkey];
      this._quickCache[guid] = records;
    }

    // and stop observing it.
    rec.removeObserver('*',this, this.recordDidChange) ;
    this.recordDidChange(rec) ; // this will remove from cols since destroyed.
  },
  
  cleanRecord: function(recId) {
    var dirty = this._dirtyRecords;
    if (dirty.hasOwnProperty(recId)) {
      console.log('SC.Store#cleanRecord: Clean: %@'.fmt(recId));
      delete dirty[recId];
    }
    var count = 0;
    for(var elem in dirty){
      if (dirty.hasOwnProperty(elem)){
        count = count + 1;
      }
    }
    if (count === 0) {
      //console.log('Store set to NO');
      this.set('hasChanged', NO);
    }
    this.set('_dirtyRecords', dirty);
  },

  /**
    Since records are cached by primaryKey, whenever that key changes we need 
    to re-cache it in the proper place
    
    @param {string} oldkey Previous primary key
    @param {string} newkey New primary key
    @param {SC.Record} rec The object to relocate
    @returns {SC.Record} The record passed in
  **/
  relocateRecord: function( oldkey, newkey, rec )
  {
    if (!this._quickCache) return rec;
    
    var classKey = rec._storeKey();
    var records  = this._quickCache[classKey] || {};

    records[newkey] = rec;
    delete records[oldkey];
    
    this._quickCache[classKey] = records;
    
    return rec;
  },

  /**
    You can pass any number of condition hashes to this, ending with a
    recordType.  It will AND the results of each condition hash.
  */  
  findRecords: function() {
    var allConditions = SC.$A(arguments) ;
    var recordType = allConditions.pop() ;
    var guid = recordType._storeKey() ;

    // initial set of records.
    var records = this._records[guid] ;
    
    while(allConditions.length > 0) {
      var conditions = allConditions.pop() ;
      var ret = [] ; var loc = (records) ? records.length : 0;
      while(--loc >= 0) {
        var rec = records[loc] ;
        if ((rec.constructor == recordType) || (rec.constructor.coreRecordType == recordType)) {
          if (rec.matchConditions(conditions)) ret.push(rec) ;
        }
      }
      records = ret ;
    }

    // clone records...
    return SC.$A(records) ;
  },
  
  // private method used by Record and Store. Returns null if the record does not exist.
  _getRecordFor: function(pkValue,recordType) {
    var guid = recordType._storeKey() ;
    var records = (this._quickCache) ? this._quickCache[guid] : null;
    var ret = (records) ? records[pkValue] : null ;
    return ret ;
  },
  
  /**
    finds the record with the primary key value.  If the record does not 
    exist, creates it.
  */
  getRecordFor: function(pkValue,recordType,dontAutoaddRecord) {
    var ret = this._getRecordFor(pkValue,recordType) ;
    if (!ret) {
      var opts = {}; opts[recordType.primaryKey()] = pkValue;
      ret = recordType.create(opts) ;
      if (dontAutoaddRecord) {
        ret.needsAddToStore = true ;
      } else this.addRecord(ret) ;
    }
    return ret ;
  },
  
  /** @property
    Returns an array of all records in the store.  Mostly used for storing.
  */
  records: function() {
    var ret = [] ;
    if (this._quickCache) {
      for(var key in this._quickCache) {
        var recs = this._quickCache[key] ;
        for(var recKey in recs) {
          ret.push(recs[recKey]) ;
        }
      }
    }
    return ret ;
  }.property(),
  
  // ....................................
  // Collection Helpers
  //
  
  addCollection: function(collection) {
    var guid = collection.recordType._storeKey() ;
    var collections = this._collections[guid] || [] ;
    collections.push(collection) ;
    this._collections[guid] = collections ;
  },
  
  removeCollection: function(collection) {
    var guid = collection.recordType._storeKey() ;
    var collections = this._collections[guid] || [] ;
    collections = collections.without(collection) ;
    this._collections[guid] = collections ;
  },
  
  listFor: function(opts) {
    var conditions = opts.conditions || {} ;
    var order = opts.order || ['guid'] ;
    var records = this.findRecords(conditions,opts.recordType) ;
    var count = records.length ;

    // sort
    records = records.sort(function(a,b) { return a.compareTo(b,order); }) ;
    
    // slice if needed.
    if (opts.limit && (opts.limit > 0)) {
      var start = (opts.offset) ? opts.offset : 0 ;
      var end = start + opts.limit ;
      records = records.slice(start,end) ;      
    }
    
    // now run callback.
    if (opts.callback) opts.callback(records,count) ; 
  },
  
  dirtyRecords: function(){
    var dirty = this.get('_dirtyRecords') || {};
    var returnAry = [];
    for(var elem in dirty){
      if (dirty[elem] !== null) returnAry.push(dirty[elem]);
    }
    return returnAry;
  }.property('_dirtyRecords'),
  
  // ....................................
  // PRIVATE
  //
  _records: {}, _changedRecords: {}, _collections: {}, _dirtyRecords: {},
  
  /** @private
    called whenever properties on a record change.
  */  
  recordDidChange: function(rec) {
    // add to changed records.  This will eventually notify collections.
    var guid    = rec._storeKey(),
        changed = this.get('_changedRecords') || {},
        dirty = this.get('_dirtyRecords') || {},
        records = changed[guid] || {} ;
    records[SC.guidFor(rec)] = rec ;

    changed[guid] = records ;    
    this.set('_changedRecords',changed) ;
    
    // Set the global record changes
    var dirtyId = rec.get('guid');
    var changeCount = rec.get('changeCount');
    if (changeCount > 0) {
      console.log('SC.Store#recordDidChange: Adding To Dirty: %@'.fmt(dirtyId));
      dirty[dirtyId] = rec;
      this.set('hasChanged', YES);
    }
    this.set('_dirtyRecords', dirty);
    this._changedRecordsObserver() ;
  },
  
  // invoked whenever the changedRecords hash is updated. This will notify
  // collections.
  _changedRecordsObserver: function() {
    // process changedRecords to notify collections.
    for(var guid in this._changedRecords) {
      var collections = this._collections[guid] ;
      if (collections && collections.length>0) {
        
        // collect records into array.  Faster than using uniq.
        var records = [] ;
        for(var key in this._changedRecords[guid]) {
          records.push(this._changedRecords[guid][key]) ;
        }
        var cloc = collections.length ;
        while(--cloc >= 0) {
          
          // for each collection watching this type of record, notify.
          var col = collections[cloc] ;
          col.beginPropertyChanges() ;
          try {
            // for each record...
            var rloc = records.length ;
            while(--rloc >= 0) {
              var record = records[rloc] ;
              // notify only if record type matches.
              if (col.recordType == record.constructor) {
                col.recordDidChange(record) ;
              }
            }
          }
          catch (e) {
            console.log('EXCEPTION: While notifying collection') ;
          }
          col.endPropertyChanges() ;
          
        }
      }
    }
    
    // then clear changed records to start again.
    this._changedRecords = {} ;
    //this.set('hasChanged', NO);
  }.observes('_changedRecords')
    
}) ;


/* End ------------------------------------------------------- models/store.js*/

/* Start ----------------------------------------------------- models/record.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('system/object') ;
require('models/store') ;

/**
  @class

  A Record is the core model class in SproutCore. It is analogous to 
  NSManagedObject in Core Data and EOEnterpriseObject in the Enterprise
  Objects Framework (aka WebObjects), or ActiveRecord::Base in Rails.
  
  To create a new model class, in your SproutCore workspace, do:
  
  {{{
    $ sc-gen model my_app/my_model
  }}}

  This will create MyApp.MyModel in clients/my_app/models/my_model.js.
  
  The core attributes hash is used to store the values of a record in a 
  format that can be easily passed to/from the server.  The values should 
  generally be stored in their raw string form.  References to external 
  records should be stored as primary keys.
  
  Normally you do not need to work with the attributes hash directly.  
  Instead you should use get/set on normal record properties.  If the 
  property is not defined on the object, then the record will check the 
  attributes hash instead.
  
  You can bulk update attributes from the server using the 
  updateAttributes() method.

  @extends SC.Object
  @since SproutCore 1.0
*/
SC.Record = SC.Object.extend(
/** @scope SC.Record.prototype */ {
  
  // ...............................
  // PROPERTIES
  //

  /**
    Override this with the properties you want the record to manage.
    
    @field
    @type {Array}
  */
  properties: ['guid'],
  
  /**
    This is the primary key used to distinguish records.  If the keys
    match, the records are assumed to be identical.
    
    @field
    @type {String}
  */
  primaryKey: 'guid',
  
  /**
    When a new empty record is created, this will be set to true.  It will be
    set to false again the first time the record is committed.
    
    @field
    @type {Boolean}
  */
  newRecord: false,
  
  /**
    Set to non-zero whenever the record has uncommitted changes.
    
    @field
    @type {Number}
  */
  changeCount: 0,
  
  /**
    Set to true when the record is deleted.  Will cause it to be removed
    from any member collections.  Once no more objects hold references to it,
    the property will be disabled.
    
    @field
    @type {Boolean}
  */
  isDeleted: false,
  
  // ...............................
  // CRUD OPERATIONS
  //

  /**
    Set this URL to point to the type of resource this record is. 
    
    If you are using SC.Server, then put a '%@' where you expect the 
    primaryKey to be inserted to identify the record.
    
    @field
    @type {String}
  */
  resourceURL: null,
  
  /**
    The item providing the data for this.  Set to either the store or a
    Server.  Setting it to the Store will make refresh and commit effectively
    null-ops.
    
    @field
    @type {SC.Store or SC.Server}
  */
  dataSource: SC.Store,

  /**
    The URL where this record can be refreshed. Usually you would send the value
    for this URL from the server in response to requests from Sproutcore.
    
    @field
    @type {String}
  */
  refreshURL: null,

  /**
    The URL where this record can be updated. Usually you would send the value
    for this URL from the server in response to requests from Sproutcore.
    
    @field
    @type {String}
  */
  updateURL: null,

  /**
    The URL where this record can be destroyed. Usually you would send the value
    for this URL from the server in response to requests from Sproutcore.
    
    @field
    @type {String}
  */
  destroyURL: null,


  init: function()
  {
    arguments.callee.base.apply(this,arguments);
    
    var primaryKeyName = this.get('primaryKey');
    if (!this.get(primaryKeyName))
    {
      // no primary key passed for a new record.
      // we'll need to create one so that it can be cached in SC.Store
      // if this isn't desired behavior, override generateTempPrimaryKey to return false.
      var value = this.generateTempPrimaryKey();
      if (value) this.set(primaryKeyName, value);
    }
  },

  generateTempPrimaryKey: function()
  {
    return "@" + SC.guidFor(this);
  },

  /**
    Invoked by the UI to request the model object be updated from the server.
    
    Override to actually support server changes.
  */
  refresh: function() { 
    if (!this.get('newRecord')) this.dataSource.refreshRecords([this]); 
  },
  
  /**
    Invoked by the UI to tell the model this record should be saved. Override
    to support server changes.  Note that this is used to support both the
    create and update components of CRUD.
  */
  commit: function() {  
    // no longer a new record once changes have been committed.
    if (this.get('newRecord')) {
      this.dataSource.createRecords([this]) ;
    } else {
      this.dataSource.commitRecords([this]) ;
    }
  },
  
  /**
    This can delete the record.  The non-server version just sets isDeleted.
  */
  destroy: function() { this.dataSource.destroyRecords([this]) ; },

  // ...............................
  // ATTRIBUTES
  //
  // The core attributes hash is used to store the values of a record in a 
  // format that can be easily passed to/from the server.  The values should 
  // generally be stored in their raw string form.  References to external 
  // records should be stored as primary keys.
  //
  // Normally you do not need to work with the attributes hash directly.  
  // Instead you should use get/set on normal record properties.  If the 
  // property is not defined on the object, then the record will check the 
  // attributes hash instead.
  // 
  // You can bulk update attributes from the server using the 
  // updateAttributes() method.
  
  /**
    Gets an attribute, converting it to the proper format.
  
    @param {string} key the attribute you want to read
    @returns {value} the value of the key, or null if it doesn't exist
  **/
  readAttribute: function(key) {
    if (!this._cachedAttributes) this._cachedAttributes = {} ;
    var ret = this._cachedAttributes[key] ;
    if (ret === undefined) {
      var attr = this._attributes ;
      ret = (attr) ? attr[key] : undefined ;
      ret = ret || this[key] ; // also check properties...
      if (ret !== undefined) {
        var recordType = this._getRecordType(key+'Type') ;
        ret = this._propertyFromAttribute(ret, recordType) ;
      }
      this._cachedAttributes[key] = ret ;
    }
    return (ret === undefined) ? null : ret;
  },

  /**
    Updates the attribute, converting it back to the property format.
  
    @param {String} key the attribute you want to read
    @param {Object} value the attribute you want to read
    @returns {Object} the value of the key, or null if it doesn't exist
  **/
  writeAttribute: function(key, value) {
    var recordType = this._getRecordType(key+'Type') ;
    var ret = this._attributeFromProperty(value, recordType) ;
    if (!this._attributes) this._attributes = {} ;
    this._attributes[key] = ret ;
    if (this._cachedAttributes) delete this._cachedAttributes[key];  // clear cache.
    this.recordDidChange() ;
    return value ;  
  },
  
  /**
    You can invoke this method anytime you need to make the record as dirty
    and needing a commit to the server.
  */
  recordDidChange: function() {
    this.incrementProperty('changeCount') ;
  },
  
  /**
    This will take the incoming set of attributes and update internal set.
    
    Note that if the attributes have never been set, then the object you pass 
    in may become the new set of attribute.  This assumes the attrs you pass 
    in will not be modified later.  This method also assumes it is coming from 
    the server, so the change count will be reset.
  
    @param {Object} newAttrs the new attributes for the object
    @param {Boolean} replace should the overwrite the in-place attributes, or  replace them entirely
    @returns {Boolean} isLoaded is the object loaded?
  **/
  updateAttributes: function(newAttrs, replace, isLoaded) {
    //console.log('Replace: %@, isLoaded: %@'.fmt(replace, isLoaded));
    var changed = false ;
    if (this._attributes && (replace !== true)) {
      for(var key in newAttrs) {
        if (!newAttrs.hasOwnProperty(key)) continue ;
        if (!changed) changed = (this._attributes[key] != newAttrs[key]) ;
        this._attributes[key] = newAttrs[key] ;
      }
    } else {
      this._attributes = newAttrs ;
      changed = true ;
    }
    
    this._cachedAttributes = {} ; // reset cache.
    
    if (changed) {
      this.beginPropertyChanges() ;
      this.set('isLoaded',isLoaded) ;
      this.set('changeCount',0) ;
      this.allPropertiesDidChange() ;
      this.endPropertyChanges() ;

      if (SC.Store) SC.Store.recordDidChange(this) ;
    }
  },
  
  /**
    This will return the current set of attributes as a hash you can send back to the server.
  
    @returns {Object} the current attributes of the receiver
  **/
  attributes: function() {
    return Object.clone(this._attributes) ;
  }.property(),
  
  /**
    If you try to get/set a property not defined by the record, then this 
    method will be called. It will try to get the value from the set of 
    attributes.
  
    @param {String} key the attribute being get/set
    @param {Object} value the value to set the key to, if present
    @returns {Object} the value
  **/
  unknownProperty: function( key, value )
  {
    if (value !== undefined) {
      
      // if we're modifying the PKEY, then SC.Store needs to relocate where 
      // this record is cached. store the old key, update the value, then let 
      // the store do the housekeeping...
      var primaryKeyName = this.get('primaryKey');
      if (key == primaryKeyName)
      {
        var oldPrimaryKey  = this.get(key);
        var newPrimaryKey  = value;
      }
      
      this.writeAttribute(key,value);
      
      // no need to relocate if there wasn't an old key...
      if ((key == primaryKeyName) && oldPrimaryKey) SC.Store.relocateRecord( oldPrimaryKey, newPrimaryKey, this );
      
    } else {
      value = this.readAttribute(key);
    }
    return value;
  },
  
  _attributeFromProperty: function(value,recordType) {
    if (value && value instanceof Array) {
      var that = this;
      return value.map(function(v) { 
        return that._attributeFromProperty(v,recordType); 
      }) ;
    } else {
      var typeConverter = this._pickTypeConverter(recordType) ;
      if (typeConverter) return typeConverter(value,'out') ;
      if (recordType) {
        return (value) ? value.get(recordType.primaryKey()) : null ;
      } else return value ;
    }
  },
  
  _propertyFromAttribute: function(value,recordType) {
    if (value && value instanceof Array) {
      var max = value.get('length') ;
      var ret = new Array(max) ; 
      for(var idx=0;idx<max;idx++) {
        var v = value.objectAt(idx) ; 
        ret[idx] = this._propertyFromAttribute(v, recordType) ; 
      }
      ret.ownerRecord = this ;
      return ret ;
      
    } else {
      var typeConverter = this._pickTypeConverter(recordType) ;
      if (typeConverter) return typeConverter(value,'in') ;
      if (recordType) {
        if (!value) return null ;
        return SC.Store.getRecordFor(value,recordType) ;
      } else return value ;
    }
  },
  
  _getRecordType: function(recordTypeKey) {
    var type = this[recordTypeKey] ;
    if (type && (typeof(type) == "string")) {
      type = eval(type) ; // look up type.
      if (type) this[recordTypeKey] = type ;
    }
    return type ;
  },
  
  // ...............................
  // SORTING AND COMPARING RECORDS
  //
  
  valueForSortKey: function(key) { return this.get(key); },

  /**
    Compares the receiver to the passed object, using the array of keys to
    determine the order.  You can use this method as part of a call to sort()
    on an array.
    
    @param object {SC.Record} the other record
    @param orderBy {Array} array of one or more keys. Optional.
    @returns {Number} -1, 0, 1
  */
  compareTo: function(object, orderBy) {
    if (!orderBy) orderBy = [this.get('primaryKey')] ;
    var ret = SC.Record.SORT_SAME ; var loc ;
    for(loc=0; (ret == SC.Record.SORT_SAME && loc<orderBy.length); loc++) {
      var key = orderBy[loc] ;
      
      // determine order
      var asc = true ;
      if (key.match(/ DESC$/)) { 
        asc = false; key = key.slice(0,-5); 
      } else if (key.match(/ ASC$/)) {
        asc = true; key = key.slice(0,-4); 
      }

      // if key contains a .dot then we need to get the value for the key.
      var keys = key.split('.') ;
      key = keys.shift() ;
      
      // get values for key.
      var a = this.valueForSortKey(key) ;
      var b = object.valueForSortKey(key) ;
      
      // convert the values to comparable values.
      a = this._comparableValueFor(a,keys) ;
      b = this._comparableValueFor(b,keys) ;
      
      // compare values
      if (asc) {
        ret = (a<b) ? SC.Record.SORT_BEFORE : ((a>b) ? SC.Record.SORT_AFTER : SC.Record.SORT_SAME) ;
      } else {
        ret = (a>b) ? SC.Record.SORT_BEFORE : ((a<b) ? SC.Record.SORT_AFTER : SC.Record.SORT_SAME) ;
      }
    }
    return ret ;
  },
  
  _comparableValueFor: function(value, keys) {
    if (keys && keys.length > 0) {
      var key ; var loc = 0 ;
      while(value && (loc < keys.length)) {
        key = keys[loc]; 
        value = (value.get) ? value.get(key) : value[key] ;
        loc++ ;
      }
    
    // handle records.
    } else value = SC.guidFor(value) ;
    return value ;
  },
  
  /**  
    Used to match records to a set of conditions.  By default, this will
    call matchCondition on each condition.
    
    @param conditions {Hash} hash of conditions
    @returns {Boolean} true if the receiver matches the hash of conditions.
  */
  matchConditions: function(conditions) {
    for(var key in conditions) {
      var value = conditions[key] ;
      if (value instanceof Array) {
        var loc = value.length ; var isMatch = false ;
        while(--loc >= 0) {
          if (this.matchCondition(key,value[loc])) isMatch = true ;
        }
        if (!isMatch) return false ;
      } else if (!this.matchCondition(key,value)) return false ;      
    }
    return true ;
  },

  /**
    Returns true if the value of key matches the passed value.  This is used
    by matchConditions().
     
    @param key {String} the key name
    @param value {Object} the value to match agains
    @returns {Boolean} true if matched
  */
  matchCondition: function(key, value) {
    var recValue = this.get(key) ;    
    var isMatch ;
    var loc ;

    // The passed in value appears to be another record instance.
    // just check for equality with the record as an optimization.
    if (value && value.primaryKey) { 
      if (SC.$type(recValue) === SC.T_ARRAY) {
        loc = recValue.length ;
        while(--loc >= 0) { 
          if (recValue === value) return true; 
        }
      } else return recValue === value ;

    // Otherwise, do a more in-depth compare
    } else { 
      if (SC.$type(recValue) === SC.T_ARRAY) {
        loc = recValue.length ;
        while(--loc >= 0) { 
          if (this._matchValue(recValue[loc],value)) return true; 
        }
      } else return this._matchValue(recValue,value) ;
    }
    return false ;
  },

  _matchValue: function(recValue,value) {
    // if we get here with recValue as a record, we must compare by guid, so grab it
    if (recValue && recValue.primaryKey) recValue = recValue.get(recValue.get('primaryKey')) ;
    var stringify = (value instanceof RegExp);
    if (stringify)  {
      if (recValue == null) return false ;
      return recValue.toString().match(value) ;
    } else {
       return recValue==value ;
    }
  },
  
  // ...............................
  // PRIVATE
  //
  
  toString: function() {
    var that = this ;  
    var ret = this.get('properties').map(function(key) {
      var value = that.get(key) ; 
      if (typeof(value) == "string") value = '"' + value + '"' ;
      if (value === undefined) value = "(undefined)" ;
      if (value === null) value = "(null)" ;
      return [key,value].join('=') ;
    }) ;
    return this.constructor.toString() + '({ ' + ret.join(', ') + ' })' ;
  },
  
  propertyObserver: function(observing,target,key,value) {
    //if ((target == this) && this.properties.include(key)) this.incrementProperty('changeCount') ;
  },
  
  concatenatedProperties: ['properties'],

  /**
    This method should be used by the server to push updated data into a
    record.  The data should be a hash with strings and arrays.  This will
    use any types you define to convert the values into their correct type.
    Note that references to external objects should be a string with the
    primaryKey value of the record.
  
    @param data {Hash} the data hash
    @param isLoaded {Boolean} true if the hash contains a full set of data for the record vs just a summary.
    @returns {void}
  */  
  updateProperties: function(data,isLoaded) {
    var rec = this ;
    
    // for each property, if there is a value in the passed data, convert it to
    // the configured type.
    this.beginPropertyChanges() ;
    if (isLoaded) this.set('isLoaded',true) ;
    try {
      var loc = this.properties.length ;
      while(--loc >= 0) {
        var prop = this.properties[loc] ;
        var newValue = data[prop] ;

        //if (prop == 'tags') debugger ;
        
        // handle null values
        if (newValue === null) {
          if (rec.get(prop) != null) rec.set(prop,null) ;
          
        // handle defined, non-null values
        } else if (newValue !== undefined) {
          
          var oldValue = rec.get(prop) ;            
          
          // get type information
          var recordType = rec.get(prop + 'Type') ;
          var typeConverter = this._pickTypeConverter(recordType) ;
          if (typeConverter) recordType = null ;
 
          // if array, convert each object.
          var isSame ; var rec = this ;
          if (newValue instanceof Array) {
            newValue = newValue.map(function(nv) {
              return rec._convertValueIn(nv,typeConverter,recordType) ;
            }) ;
            isSame = newValue.isEqual(oldValue) ;
          } else {
            newValue = this._convertValueIn(newValue,typeConverter,recordType);
            isSame = newValue == oldValue ;
          }
          
          // set value
          if (!isSame) this.set(prop,newValue) ;
           
        }
      }
    }
    
    catch(e) {
      console.log(SC.guidFor(this) + ': Exception raised on UPDATE: ' + e) ;
    }

    this.endPropertyChanges() ;   
    this.set('changeCount',0) ;        
  },
  
  // this is used for the update.  It should return a hash with current state
  // of the record.  This uses the types to automatically marshall properties.
  getPropertyData: function() {
    var ret = {} ;
    var properties = this.get('properties') || []; var loc = properties.length;
    while(--loc >= 0) {
      var key = properties[loc] ;
      var value = this.get(key) ;
      var recordType = this[key + 'Type'] ;
      var typeConverter = this._pickTypeConverter(recordType) ;
      if (typeConverter) recordType = null ;
      
      // if there is a type, use that to make the conversion.
      if (value instanceof Array) {
        var ary = [] ;
        for(var vloc=0;vloc<value.length;vloc++) {
          var v = value[vloc] ;
          ary.push(this._convertValueOut(v,typeConverter,recordType));          
        }
        value = ary ;
      } else value = this._convertValueOut(value,typeConverter,recordType);
      
      // set key
      ret[key] = value ;
    }
    return ret ;
  },
  
  _pickTypeConverter: function(recordType) {
    var typeConverter = null ;
    if (recordType && recordType.isTypeConverter) {
      typeConverter = recordType; recordType = null ;
    } else if(recordType) switch(recordType) {
      case Date:
        typeConverter = SC.Record.Date; recordType = null ;
        break ;
      case Number:
        typeConverter = SC.Record.Number; recordType = null;
        break;
      case String:
        typeConverter = null; recordType = null ;
        break ;
    }          
    return typeConverter;
  },
  
  _convertValueOut: function(value,typeConverter,recordType) {
    if (typeConverter) return typeConverter(value,'out') ;
    if (recordType) {
      return (value) ? value.get(recordType.primaryKey()) : null ;
    } else return value ;
  },
  
  _convertValueIn: function(value,typeConverter,recordType) {
    if (typeConverter) return typeConverter(value,'in') ;
    if (recordType) {
      return SC.Store.getRecordFor(value,recordType) ;
    } else return value ;
  },
  
  // used by the store
  _storeKey: function() { 
    if (!this.constructor._storeKey) debugger ;
    return this.constructor._storeKey(); 
  },
  
  // Store Cleaner function
  _cleanRecordInStore: function(){
    var cleanCount = this.get('changeCount');
    
    if (cleanCount > 0) {
      //console.log('Cleaning Record: %@'.fmt(this.get('guid')));
      console.log('ChangeCount: %@'.fmt(cleanCount));
    } else {
      SC.Store.cleanRecord(this.get('guid'));
    }
  }.observes('changeCount')
     
}) ;

// Class Methods
SC.Record.mixin(
/** @static SC.Record */ {

  // Constants for sorting
  SORT_BEFORE: -1, SORT_AFTER: 1, SORT_SAME: 0,

  /** 
    Used to find the first object matching the specified conditions.  You can 
    pass in either a simple guid or one or more hashes of conditions.
  */
  find: function(guid) {
    var args ;
    if (typeof(guid) == 'object') {
      args = SC.$A(arguments) ;
      args.push(this) ;
      var ret = SC.Store.findRecords.apply(SC.Store,args) ;
      return (ret && ret.length > 0) ? ret[0] : null ;
    } else return SC.Store._getRecordFor(guid,this) ;
  },
  
  findOrCreate: function(guid) {
    var ret = this.find(guid) ;
    if (!ret) {
      var opts = (typeof(guid) == "object") ? guid : { guid: guid } ;
      ret = this.create(opts) ;
      SC.Store.addRecord(ret) ;
    }
    return ret ;
  },
  
  // Same as find except returns all records matching the passed conditions.
  findAll: function(filter) {
    if (!filter) filter = {} ;
    args = SC.$A(arguments) ; args.push(this) ; // add type
    return SC.Store.findRecords.apply(SC.Store,args) ;  
  },
  
  // Returns a collection with any passed settings and the receiver as a 
  // record type.
  collection: function(opts) {
    if (!opts) opts = {} ;
    opts.recordType = this;
    return SC.Collection.create(opts) ;
  },
  
  /// POSSIBLY REMOVE?
  
  // defines coreRecordType as the first level of extension from SC.Record.
  // e.g. for SC.Record > Contact > Person,  the core record type is Contact.
  extend: function() {
    var ret = SC.Object.extend.apply(this,arguments) ;
    if (ret.coreRecordType == null) ret.coreRecordType = ret ;
    return ret ;
  },  

  // used by the store
  _storeKey: function() {
    return (this.coreRecordType) ? SC.guidFor(this.coreRecordType) : SC.guidFor(this) ;
  },
  
  primaryKey: function() { return this.prototype.primaryKey; },

  // this is set by extend to point to the core record type used to store
  // the record in the pool.  The coreRecordType is always the first record
  // type created.
  coreRecordType: null,

  resourceURL: function() { return this.prototype.resourceURL; },
  
  // This will add a property function for your record with a collection
  // of records with the given type that belong to your record.
  hasMany: function(recordTypeString,conditionKey,opts) {
    opts = (opts === undefined) ? {} : Object.clone(opts) ;
    var conditions = opts.conditions || {} ;
    opts.conditions = conditions ;

    var privateKey = '_' + conditionKey + SC.generateGuid() ;
    return function() {
      if (!this[privateKey]) {
        var recordType = eval(recordTypeString);
        conditions[conditionKey] = this ;
        this[privateKey] = recordType.collection(opts) ;
        this[privateKey].refresh() ; // get the initial data set.
      }
      return this[privateKey] ;
    }.property();
  },
  
  // This will create a new record with the type.  Include the data and an
  // optional data source.
  newRecord: function(attrs,dataSource) {
    if (!dataSource) dataSource = SC.Store ;
    var rec = this.create({ dataSource: dataSource }) ;
    rec.beginPropertyChanges();
    rec.set('newRecord',true);
    for(var key in attrs) {
      if (attrs.hasOwnProperty(key)) rec.set(key,attrs[key]) ;
    }
    rec.endPropertyChanges() ;
    SC.Store.addRecord(rec) ;
    return rec; 
  }
    
}) ;

SC.Record.newObject = SC.Record.newRecord; // clone method

// Built in Type Converters.  You can also use an SC.Record.
SC.Record.Date = function(value,direction) {
  if (direction == 'out') {
    if (value instanceof Date) value = value.utcFormat() ;
    
  } else if (typeof(value) == "string") {
    // try to parse date. trim any decimal numbers at end since Rails sends
    // this sometimes.
    var ret = new Date( Date.parse(value.replace(/\.\d+$/,'')) );
    if (ret) value = ret ;
  }
  return value ;
}.typeConverter() ;

SC.Record.Number = function(value,direction) {
  if (direction == 'out') {
    if (typeof(value) == "number") value = value.toString() ;
  
  } else if (typeof(value) == "string") {
    var ret = (value.match('.')) ? parseFloat(value) : parseInt(value,0) ;
    if (ret) value = ret ;
  }
  return value ;
}.typeConverter() ;

SC.Record.Flag = function(value, direction) {
  if (direction == 'out') {
    return value = (value) ? 't' : 'f' ;
  } else if (typeof(value) == "string") {
    return !('false0'.match(value.toLowerCase())) ;
  } else return (value) ? true : false ;
}.typeConverter() ;

SC.Record.Bool = SC.Record.Flag ;



/* End ------------------------------------------------------- models/record.js*/

/* Start ----------------------------------------------------- deprecated/globals.js*/

// ==========================================================================
// SproutCore
// Author: Charles Jolley
// copyright 2006-2008, Sprout Systems, Inc.
// ==========================================================================

// Globally defined variables are no longer supported to allow SproutCore to 
// work with other libraries.
// Export the type variables into the global space.
var T_ERROR = SC.T_ERROR ;
var T_OBJECT = SC.T_OBJECT ;
var T_NULL = SC.T_NULL ;
var T_CLASS = SC.T_CLASS ;
var T_HASH = SC.T_HASH ;
var T_FUNCTION = SC.T_FUNCTION ;
var T_UNDEFINED = SC.T_UNDEFINED ;
var T_NUMBER = SC.T_NUMBER ;
var T_BOOL = SC.T_BOOL ;
var T_ARRAY = SC.T_ARRAY ;
var T_STRING = SC.T_STRING ;


/* End ------------------------------------------------------- deprecated/globals.js*/

/* Start ----------------------------------------------------- views/mixins/button.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/** @static

  This mixin implements many of the basic state-handling attributes for 
  button-like views, including an auto-updated title, and mapping the current
  value to an isSelected state.
  
  Usually you will not work with this mixin directly.  Instead, you should use
  a class that incorporates the mixin such as SC.ButtonView, SC.CheckboxView
  or SC.RadioView.
  
  This mixin assumes you have already applied the SC.Control and 
  SC.DelegateSupport mixins as well.
  
  @since SproutCore 1.0  
*/
SC.Button = {
  
  // ..........................................................
  // VALUE PROPERTIES
  // 
  
  /**
    Used to automatically update the state of the button view for toggle style
    buttons.

    for toggle style buttons, you can set the value and it will be used to
    update the isSelected state of the button view.  The value will also
    change as the user selects or deselects.  You can control which values
    the button will treat as isSelected by setting the toggleOnValue and 
    toggleOffValue.  Alternatively, if you leave these properties set to
    YES or NO, the button will do its best to convert a value to an 
    appropriate state:
  
    - null, false, 0  -> isSelected = false
    - any other single value -> isSelected = true
    - array -> if all values are the same state: that state.  otherwise MIXED.
    
    @property {Object}
  */  
  value: null,
  
  /**
    Value of a selected toggle button.
  
    for a toggle button, set this to any object value you want.  The button
    will be selected if the value property equals the targetValue.  If the
    value is an array of multiple items that contains the targetValue, then
    the button will be set to a mixed state.

    default is YES
    
    @property {Object}
  */
  toggleOnValue: YES,

  /**
    Value of an unselected toggle button.
  
    For a toggle button, set this to any object value you want.  When the
    user toggle's the button off, the value of the button will be set to this
    value.
  
    default is NO 
  
    @property {Object}
  */
  toggleOffValue: NO,
  
  // ..........................................................
  // TITLE 
  // 
  
  /**
    If YES, then the title will be localized.
  */
  localize: NO,
  localizeBindingDefault: SC.Binding.bool(),

  /**
    The button title.  If localize is YES, then this should be the localization key to display.  Otherwise, this will be the actual string displayed in the title.  This property is observable and bindable.
    
    @property {String}
  */  
  title: '',

  /**
    If you set this property, the title property will be updated automatically
    from the content using the key you specify.
  */
  contentTitleKey: null,
  
  /**
    The button icon.  Set this to either a URL or a CSS class name (for 
    spriting).  To display an icon, you must set hasIcon to YES when the 
    button is created.  Note that if you pass a URL, it must contain at 
    least one slash to be detected as such.
    
    @property {String}
  */
  icon: null,

  /**
    If you set this property, the icon will be updated automatically from the
    content using the key you specify.
  */
  contentIconKey: null,
  
  /**
    The computed display title.  This is generated by localizing the title property if necessary.
    
    @property {String}
  */
  displayTitle: function() {
    var ret = this.get('title');
    return (ret && this.get('localize')) ? ret.loc() : (ret || '');
  }.property('title','localize').cacheable(),
  
  /**
    The selector path to the element that contains the button title.   You should only set this property when you first configure the button.  Changing it will not cause the button to redisplay.
  */
  titleSelector: '.sc-button-label',

  /** @private - update title display */
  updateDisplayMixin: function() {
    var icon = this.get('icon') ;
    var needsTitle = NO;
    
    // get the icon.  If there is an icon, then get the image and update it.
    // if there is no image element yet, create it and insert it just before
    // title.
    if (icon) {
      var blank = '/sc-apps/sc-statechart/sproutcore/en/desktop/ac2c8cef664802102e1d7f3875d5dc0ea4e0f1ae/blank.gif';
      var img = this.$('img.icon') ;
      if (img.length === 0) {
        img = SC.$('<img src=%@ alt="" class="icon" />'.fmt(blank)) ;
        this.$(this.get('titleSelector') || 'label').text('').prepend(img)
          .append(SC.$('<span class="inner"></span>'));
        needsTitle = YES ;
      }
      
      // this is a URL...set it as src
      if (icon.indexOf('/') >= 0) {
        // wipe any previous sprite and set src
        img.attr('class', 'icon').attr('src', icon);
          
      // this is a sprite. set as class
      } else img.addClass(icon).attr('src', blank) ;
    }
    this.$().setClass('icon', !!icon) ;

    // get the title of the button.  if the display title has changed, then 
    // update the HTML.
    var title = this.get('displayTitle') ;
    if (needsTitle || (title !== this._button_title)) {
      this._button_title = title ;
      var cq = this.$(this.get('titleSelector') || 'label');
      if (icon) cq = cq.find('span.inner') ;
      cq.text(title);  
    }
    
  },

  /**
    Updates the value, title, and icon keys based on the content object, if 
    set.
  */
  contentPropertyDidChange: function(target, key) {
    var del = this.get('displayDelegate');
    var content = this.get('content'), value ;

    var valueKey = this.getDelegateProperty(del, 'contentValueKey') ;
    if (valueKey && (key === valueKey || key === '*')) {
      this.set('value', content ? content.get(valueKey) : null) ;
    }

    var titleKey = this.getDelegateProperty(del, 'contentTitleKey') ;
    if (titleKey && (key === titleKey || key === '*')) {
      this.set('title', content ? content.get(titleKey) : null) ;
    }

    var iconKey = this.getDelegateProperty(del, 'contentIconKey');
    if (iconKey && (key === iconKey || key === '*')) {
      this.set('icon', content ? content.get(iconKey) : null) ;
    }
  },

  /** @private - when title changes, dirty display. */
  _button_displayObserver: function() {
    this.displayDidChange();
  }.observes('title', 'icon'),

  /**
    The key equivalent that should trigger this button on the page.
  */
  keyEquivalent: null,
  
  /**
    Handle a key equivalent if set.  Trigger the default action for the 
    button.  Depending on the implementation this may vary.
    
    @param {String} keystring
    @param {SC.Event} evt
    @returns {Boolean}  YES if handled, NO otherwise
  */
  performKeyEquivalent: function(keystring, evt) {
    if (!this.get('isEnabled')) return NO;
    var keyEquivalent = this.get('keyEquivalent');
    if (keyEquivalent && (keyEquivalent === keystring)) {
      // button has defined a keyEquivalent and it matches!
      // if triggering succeeded, true will be returned and the operation will 
      // be handeled (i.e performKeyEquivalent will cease crawling the view 
      // tree)
      return this.triggerAction(evt);
    }
    return YES;
  },

  /**
    Your class should implement this method to perform the default action on
    the button.  This is used to implement keyboard control.  Your button
    may make this change in its own way also.
  */
  triggerAction: function(evt) {
    throw "SC.Button.triggerAction() is not defined in %@".fmt(this);
  },

  // ..........................................................
  // VALUE <-> isSelected STATE MANAGEMNT
  // 

  /**
    This is the standard logic to compute a proposed isSelected state for a
    new value.  This takes into account the toggleOnValue/toggleOffValue 
    properties, among other things.  It may return YES, NO, or SC.MIXED_STATE.
    
    @param {Object} value
    @returns {Boolean} return state
  */
  computeIsSelectedForValue: function(value) {
    var targetValue = this.get('toggleOnValue') ;
    var state, next ;
    
    if (SC.$type(value) === SC.T_ARRAY) {

      // treat a single item array like a single value
      if (value.length === 1) {
        state = (value[0] == targetValue) ;
        
      // for a multiple item array, check the states of all items.
      } else {
        state = null;
        value.find(function(x) {
          next = (x == targetValue) ;
          if (state === null) {
            state = next ;
          } else if (next !== state) state = SC.MIXED_STATE ;
          return state === SC.MIXED_STATE ; // stop when we hit a mixed state.
        });
      }
      
    // for single values, just compare to the toggleOnValue...use truthiness
    } else {
      state = (value == targetValue) ;
    }
    return state ;
  },
  
  prepareDisplayMixin: function() {
    // if value is not null, update isSelected to match value.  If value is
    // null, we assume you may be using isSelected only.  
    if (!SC.none(this.get('value'))) this._button_valueDidChange();  
  },
  
  /** @private
    Whenever the button value changes, update the selected state to match.
  */
  _button_valueDidChange: function() {
    var value = this.get('value');  
    var state = this.computeIsSelectedForValue(value);
    this.set('isSelected', state) ; // set new state...
  }.observes('value'),
  
  /** @private
    Whenever the selected state is changed, make sure the button value is also updated.  Note that this may be called because the value has just changed.  In that case this should do nothing.
  */
  _button_isSelectedDidChange: function() {
    var newState = this.get('isSelected');
    var curState = this.computeIsSelectedForValue(this.get('value'));
    
    // fix up the value, but only if computed state does not match.
    // never fix up value if isSelected is set to MIXED_STATE since this can
    // only come from the value.
    if ((newState !== SC.MIXED_STATE) && (curState !== newState)) {
      var valueKey = (newState) ? 'toggleOnValue' : 'toggleOffValue' ;
      this.set('value', this.get(valueKey));
    }
  }.observes('isSelected')
  
} ;


/* End ------------------------------------------------------- views/mixins/button.js*/

/* Start ----------------------------------------------------- design.mode/views/mixins/button.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/mixins/button');

SC.Button.Designer = {

  designProperties: 'title'.w()
  
};

/* End ------------------------------------------------------- design.mode/views/mixins/button.js*/

/* Start ----------------------------------------------------- application/run_loop.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core') ;

/**
  @class

  The run loop provides a universal system for coordinating events within
  your application.  The run loop processes timers as well as pending 
  observer notifications within your application.
  
  Typically you will not work with a run loop directly but instead user 
  SC.Timer objects and property observing to indirectly trigger actions on
  the run loop.  The only time you may need to work with the run loop is if
  you implement a setTimeout or event handler yourself.  In these cases, you
  should begin and end your function handler with a call to beginRunLoop()
  and endRunLoop().  This will give the run loop a chance to process any
  pending events on your application.
  
  h2. Using the Loop Start Time
  
  Sometimes you need to schedule events such as timers and you want to make
  sure all of the events you schedule occur at the same time.  If you want to
  keep items in sync you can't use Date.now() because that value will 
  increment as your code executes.  Instead, you should get the loop start
  time and use that.  For example, if you want to schedule three timers to
  repeat until 1 second, 2 seconds and 3 seconds from now you might do:
  
  {{{
    var t1 = SC.Timer.schedule({ 
      interval: 100,
      action: 'timer1', 
      repeats: YES, 
      until: SC.runLoop.get('startTime') + 1000 }) ;

    var t2 = SC.Timer.schedule({ 
      interval: 100,
      action: 'timer1', 
      repeats: YES, 
      until: SC.runLoop.get('startTime') + 2000 }) ;

    var t3 = SC.Timer.schedule({ 
      interval: 100,
      action: 'timer1', 
      repeats: YES, 
      until: SC.runLoop.get('startTime') + 3000 }) ;
  }}}
  
  This will ensure that each timer uses the exact same start time when 
  scheduling.  This is critical if you want to keep animations in sync.
  
  @extends SC.Object
  @author Charles Jolley
  @version 1.0
  @since SproutCore 1.0
*/
SC.runLoop = SC.Object.create({
  
  runLevel: 0,
  
  /**
    Maximum time we allow things to run before taking a break.
  */
  maxRunTime: 3000,
  
  /**
    Call this method whenver you begin executing code.  
  
    This is typically invoked automatically for you from event handlers and 
    the timeout handler.  If you call setTimeout() or setInterval() yourself, 
    you may need to invoke this yourself.
  
    @returns {SC.RunLoop} receiver
  */
  beginRunLoop: function() {
    this._start = Date.now() ;  
  },

  /**
    Call this method whenever you are done executing code.
    
    This is typically invoked automatically for you from event handlers and
    the timeout handler.  If you call setTimeout() or setInterval() yourself
    you may need to invoke this yourself.
    
    @returns {void}
  */
  endRunLoop: function() {
    
    // at the end of a runloop, flush all the delayed actions we may have 
    // stored up.  Note that if any of these queues actually run, we will 
    // step through all of them again.  This way any changes get flushed
    // out completely.
    var didChange ;
    
    do {
      didChange = NO ;
      
      // flush any expired timers, possibly cancelling the timeout.
      // no need to check for didChange here since the other items will run
      // anyway...
      this._flushExpiredTimers();
      
      // flush any pending changed bindings.  This could actually trigger a 
      // lot of code to execute.
      didChange = didChange || SC.Binding.flushPendingChanges();

      // flush any invokeOnce() methods
      didChange = didChange || this._flushInvokeQueue();
      
      // Go ahead and ask any changed views to re-render
      didChange = didChange || SC.View.flushPendingQueues();
    } while(didChange) ;
    this._start = null ;
  },
    
  _flushInvokeQueue: function() {
    var queue = this._invokeQueue, hadContent = NO, len, idx, handler ;
    if (queue && queue.targets > 0) {
      this._invokeQueue = null; // reset queue.
      hadContent = YES; // has targets!
      if (hadContent) queue.invokeMethods();
    }
    return hadContent ;
  },
  
  /**
    Invokes the passed target/method pair once at the end of the runloop.
    You can call this method as many times as you like and the method will
    only be invoked once.  
    
    Usually you will not call this method directly but use invokeOnce() 
    defined on SC.Object.
    
    @param {Object} target
    @param {Function} method
    @returns {SC.RunLoop} receiver
  */
  invokeOnce: function(target, method) {
    // normalize
    if (method === undefined) { 
      method = target; target = this ;
    }
    if (SC.typeOf(method) === SC.T_STRING) method = target[method];
    if (!this._invokeQueue) this._invokeQueue = SC._ObserverSet.create();
    this._invokeQueue.add(target, method);
    return this ;
  },
  
  /**
    The time the current run loop began executing.
    
    All timers scheduled during this run loop will begin executing as if 
    they were scheduled at this time.
  
    @type {Number}
    @field
  */
  startTime: function() {
    if (!this._start) this._start = Date.now();
    return this._start ;  
  }.property(),

  // timers are stored in the hash with a double linked list to keep them
  // in order.
  scheduleTimer: function(timer, runTime) {
    
    if (!timer) throw "scheduleTimer requires a timer" ; // nothing to do
    
    if (!this._timers) this._timers = {} ;
    var guid = SC.guidFor(timer) ;

    // either remove the timer record or create a new one and add it.
    var t = this._timers[guid];
    if (t) {
      if (t.prev) t.prev.next = t.next ;
      if (t.next) t.next.prev = t.prev ;
      t.next = t.prev = null ;
      t.at = runTime ;
    } else {
      t = this._timers[guid] = { 
        timer: timer, 
        at: runTime, 
        guid: guid,
        next: null, prev: null 
      } ;
    }

    // now walk the chain to figure out where to insert the timer.  If the
    // timer goes at the front, also reschedule the next timeout.
    var cur = this._next ;
    if (!cur || cur.at > runTime) {
      this._next = t ;
      t.next = cur ;
      if (cur) cur.prev = t.next ;
      this._rescheduleTimeout() ;
      
    } else {
      // find the item to insert after
      while(cur.next && cur.next.at <= runTime) cur = cur.next ;
      t.next = cur.next ;
      if (cur.next) cur.next.prev = t ;
      cur.next = t ;
      t.prev = cur ;
    }
  },
  
  cancelTimer: function(timer) {
    
    if (!timer) return ; //nothing to do
    
    if (!this._timers) this._timers = {} ;
    var guid = SC.guidFor(timer) ;
    var t = this._timers[guid] ;
    
    // if a timer record was found, remove it from the list.  
    // if timer was at the front, reschedule the timeout.
    if (t) {
      if (t.next) t.next.prev = t.prev ;
      if (t.prev) t.prev.next = t.next ;
      if (this._next === t) {
        this._next = t.next ;
        this._rescheduleTimeout() ;
      }
      t.next = t.prev = t.timer = null ; // clear objects
    }
  },

  timerPausedStateDidChange: function(timer) {
    this._rescheduleTimeout() ;
  },
  
  // determines the next time the timeout needs to trigger and reschedules
  // if necessary.  If you pass in the next timer to use, then it will be 
  // scheduled instead of searching the timers.
  _rescheduleTimeout: function() {
    
    // if we are currently flushing, don't do this since it will happen
    // later anyway
    if (this._flushing) return ;
    
    if (!this._timers) this._timers = {} ;
    
    // find next timer to trigger.  If the first unpaused timer.
    var rec = this._next ;
    while(rec && (!rec.timer || rec.timer.get('isPaused')) ) rec = rec.next ;

    // if no next timer was found, then cancel any timer.
    if (!rec) {
      this._timeoutAt = 0 ;
      if (this._timeout) clearTimeout(this._timeout) ;
      this._timeout = null ;

    // determine if we need to reschedule
    } else if ((this._timeoutAt === 0) || (rec.at !== this._timeoutAt)) {
      if (this._timeout) clearTimeout(this._timeout) ;

      var delay = Math.max(rec.at - Date.now(),0) ;
      this._timeout = setTimeout(this._timeoutAction, delay) ;
      this.timeoutAt = rec.at ;
    }
  },
  
  // called when the timeout is executed.  Find any timers that have 
  // expired and call them.  
  _timeoutAction: function() {
    var rl = SC.runLoop;
    rl._timeout = null; rl._timeoutAt = 0 ;
    rl.beginRunLoop() ;
    rl._flushExpiredTimers() ;
    rl.endRunLoop() ;
  },
  
  // finds any timers that might have expired.  This will also find the
  // next timer to execute and reschedule it if needed.
  _flushExpiredTimers: function() {
    if (!this._timers) this._timers = {} ;
    var now = this.get('startTime') ;
    var max = now + this.get('maxRunTime');  // max time we are allowed to run timers

    this._flushing = YES ;
    
    // work down the list, do not fire a timer more than once per loop.
    var fired = {} ;
    var rec = this._next ;
    while(rec && (rec.at <= now) && (Date.now() < max)) {
      
      // if rec has been fired, go on to next one.
      var guid = SC.guidFor(rec.timer) ;
      if (fired[guid]) {
        rec = rec.next;
        
      // otherwise, remove rec from list and then fire it.
      } else {
        var next = rec.next;
        
        if (this._next === rec) this._next = rec.next ;
        if (rec.next) rec.next.prev = rec.prev ;
        if (rec.prev) rec.prev.next = rec.next ;
        delete this._timers[rec.guid] ;
        
        fired[guid] = YES ; 
        if (rec.timer) rec.timer.fire() ;
        
        // finish clean up.
        rec.next = rec.prev = rec.timer = null ;
        rec = next ;
      }
    }
    
    this._flushing = NO ;
    
    // schedule next timer if needed.
    this._rescheduleTimeout() ;
  }
  
  
}) ;



/* End ------------------------------------------------------- application/run_loop.js*/

/* Start ----------------------------------------------------- server/server.js*/

// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('core') ;
require('model/store');

SC.URL_ENCODED_FORMAT = 'url-encoded' ;
SC.JSON_FORMAT = 'json';

// The Server object knows how to send requests to the server and how to
// get things back from the server.  It automatically handles situations
// such as 304 caching and queuing requests to send to the server later if
// the computer becomes disconnected from the internet.

// The Server object is designed to work with a resource oriented application.
// That is, you do someting like this:
//
// Server.request('resource','verb',{ parameters })
// or
// Server.create('resource',{ parameters })
// Server.refresh('resource',{ parameters })
// Server.update('resource',{ parameters })
// Server.destroy('resource',{ parameters })
//
// parameters include:
// onSuccess -- passes back returned text
// onFailure --
//
//
// SC.Record.refresh
// SC.Record.commit --> create/update
// SC.Record.destroy
SC.Server = SC.Object.extend({
  
  // Set this to the prefix for your app.  Server will use this to convert
  // record_type properties into recordTypes.
  prefix: null,
  
  // Set this string to the format to be used to set your resource and verb.
  urlFormat: '/%@/%@',
  
  // Set this string to either rails or json to set the post transport protocol
  postFormat: SC.URL_ENCODED_FORMAT,
  
  // Set this string to true when escaping the JSON string is necessary
  escapeJSON: true,
  
  // call this in your main to preload any data sent from the server with the
  // initial page load.
  preload: function(clientData) {
    if ((!clientData) || (clientData.size == 0)) return ;
    this.refreshRecordsWithData(clientData,SC.Record,null,false);
  },
  
  // This is the root method for accessing a server resource.  Pass in the
  // resource URL, verb name, and any parameters.  There are several special-
  // purpose parameters used also:
  //
  // onSuccess -- function invoked when request completes. Expects the format
  //              didSucceed(status,ajaxRequest,cacheCode,context)
  // onFailure -- function invoked when request fails. Same format.
  // requestContext -- simply passed back.
  // cacheCode -- String indicating the time of the last refresh.
  // url -- override the default url building with this url.
  //
  request: function(resource, action, ids, params, method) {

    // Get Settings and Options
    if (!params) params = {} ;
    var opts = {} ;
    var onSuccess = params.onSuccess; delete params.onSuccess;
    var onNotModified = params.onNotModified; delete params.onNotModified ;
    var onFailure = params.onFailure ; delete params.onFailure ;
    var context = params.requestContext ; delete params.requestContext ;
    var accept = params.accept ; delete params.accept ;
    var cacheCode = params.cacheCode; delete params.cacheCode ;
    var url = params.url; delete params.url;

    opts.requestHeaders = {'Accept': 'application/json, text/javascript, application/xml, text/xml, text/html, */*'} ;
    if (accept) opts.requestHeaders['Accept'] = accept ;
    if (cacheCode) opts.requestHeaders['Sproutit-Cache'] = cacheCode ;
    opts.method = method || 'get' ;

    if (!url) url = this.urlFor(resource, action, ids, params, opts.method) ;

    // handle ids
    if (ids && ids.length > 1) {
      params.ids = [ids].flatten().join(',') ;
    }    
    
    // adds a custom HTTP header for remote requests
    opts.requestHeaders = {'X-SproutCore-Version' : '1.0'} ;

    // convert parameters.
    var parameters = this._toQueryString(params) ;
    if (parameters && parameters.length > 0) opts.parameters = parameters ;
    
    var request = null ; //will container the ajax request
    
    // Save callback functions.
    opts.onSuccess = function(transport) {
      var cacheCode = request.getHeader('Last-Modified') ;
      if ((transport.status == '200') && (transport.responseText == '304 Not Modified')) {
        if (onNotModified) onNotModified(transport.status, transport, cacheCode,context);
      } else {
        if (onSuccess) onSuccess(transport.status, transport, cacheCode,context);
      }
    } ;
    
    opts.onFailure = function(transport) {
      var cacheCode = request.getHeader('Last-Modified') ;
      if (onFailure) onFailure(transport.status, transport, cacheCode,context);
    } ; 
    
    console.log('REQUEST: %@ %@'.fmt(opts.method, url)) ;
    
    request = new Ajax.Request(url,opts) ;
  },

  /**
    Generates the URL that is going to be called by this server. Note that you
    should only return relative URLs. You can only call resources that are on
    the same domain as where this script was downloaded from.

    @param {String} resource  the URL where the collection of the resource can be queried
    @param {String} action    the action that should be performed on the resource
    @param {Array} ids        array of identifiers of your model instances
    @param {Array} params     parameters that were passed to the SC.Server#request method
    @param {String} method    the HTTP method that will be used
    @returns {String} the URL to use in the request to the backend server
  **/
  urlFor: function(resource, action, ids, params, method) {
    var idPart = (ids && ids.length == 1) ? ids[0] : '';
    return this.urlFormat.format(resource, action) + idPart;
  },

  // RECORD METHODS
  // These methods do the basic record changes.

  
  // ..........................................
  // LIST
  // This is the method called by a collection to get an updated list of
  // records.
  listFor: function(opts) {
    var recordType = opts.recordType ;
    var resource = recordType.resourceURL() ;

    if (!resource) return false ;
    
    var order = opts.order || 'id' ;
    if (!(order instanceof Array)) order = [order] ;
    order = order.map(function(str){
      return str.decamelize() ; //rubyify
    }).join(',') ;

    params = {} ;
    if (opts.conditions) {
      var conditions = this._decamelizeData(opts.conditions) ;
      for(var key in conditions) {
        params[key] = conditions[key] ;
      }
    }
    
    params.requestContext = opts ;
    params.onSuccess = this._listSuccess.bind(this) ;
    params.onNotModified = this._listNotModified.bind(this) ;
    params.onFailure = this._listFailure.bind(this) ;
    if (opts.cacheCode) params.cacheCode = opts.cacheCode ;
    if (opts.offset) params.offset = opts.offset;
    if (opts.limit) params.limit = opts.limit ;
    if (order) params.order = order ;
    this.request(resource, this._listForAction, null, params, this._listMethod) ;
  },
  
  _listForAction: 'list',
  _listMethod: 'get',
  
  _listSuccess: function(status, transport, cacheCode, context) {
    var json = eval('json='+transport.responseText) ;
    if (!json) { console.log('invalid json!'); return; }
    
    // first, build any records passed back
    if (json.records) {
      this.refreshRecordsWithData(json.records,context.recordType,cacheCode,false);
    }
    
    // next, convert the list of ids into records.
    var recs = (json.ids) ? json.ids.map(function(guid) {
      return SC.Store.getRecordFor(guid,context.recordType) ;
    }) : [] ;
    
    // now invoke callback
    if (context.callback) context.callback(recs,json.count,cacheCode) ;
  },
  
  _listNotModified: function(status, transport, cacheCode, context) {
    if (context.callback) context.callback(null,null,null) ;
  },
  
  _listFailure: function(status, transport, cacheCode, records) {
    console.log('listFailed!') ;
  },
  
    
  // ..........................................
  // CREATE
  
  // send the records back to create them. added a special parameter to
  // the hash for each record, _guid, which will be used onSuccess.
  createRecords: function(records) { 
    if (!records || records.length == 0) return ;

    records = this._recordsByResource(records) ; // sort by resource.
    for(var resource in records) {
      if (resource == '*') continue ;
      
      var curRecords = records[resource] ;

      // collect data for records
      var server = this ; var context = {} ;
      var data = curRecords.map(function(rec) {
        var recData = server._decamelizeData(rec.getPropertyData()) ;
        recData._guid = SC.guidFor(rec) ;
        context[SC.guidFor(rec)] = rec ;
        return recData ;
      }) ;

      //