Guest User

Untitled

a guest
Apr 26th, 2018
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.77 KB | None | 0 0
  1. // ==========================================================================
  2. // Project: SproutCore Costello - Property Observing Library
  3. // Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
  4. // Portions ©2008-2009 Apple Inc. All rights reserved.
  5. // License: Licened under MIT license (see license.js)
  6. // ==========================================================================
  7.  
  8. var system = require('system', 'default');
  9. var SC, SproutCore, YES, NO;
  10.  
  11. // ........................................
  12. // GLOBAL CONSTANTS
  13. //
  14. // Most global constants should be defined inside of the SC namespace.
  15. // However the following two are useful enough and generally benign enough
  16. // to put into the global object.
  17. YES = true ;
  18. NO = false ;
  19.  
  20. // prevent a console.log from blowing things up if we are on a browser that
  21. // does not support it -- only do when running on a browser platform
  22. var UNDEFINED = 'undefined';
  23. if (system.platform === 'tiki') {
  24. if (UNDEFINED === typeof console) console = system.console;
  25. if (UNDEFINED === typeof sc_require) sc_require = function() {};
  26. if (UNDEFINED === typeof sc_resource) sc_resource = function() {} ;
  27. }
  28.  
  29. // ........................................
  30. // BOOTSTRAP
  31. //
  32. // The root namespace and some common utility methods are defined here. The
  33. // rest of the methods go into the mixin defined below.
  34.  
  35. /**
  36. @namespace
  37.  
  38. The SproutCore namespace. All SproutCore methods and functions are defined
  39. inside of this namespace. You generally should not add new properties to
  40. this namespace as it may be overwritten by future versions of SproutCore.
  41.  
  42. You can also use the shorthand "SC" instead of "SproutCore".
  43.  
  44. SproutCore-Base is a framework that provides core functions for SproutCore
  45. including cross-platform functions, support for property observing and
  46. objects. It's focus is on small size and performance. You can use this
  47. in place of or along-side other cross-platform libraries such as jQuery or
  48. Prototype.
  49.  
  50. The core Base framework is based on the jQuery API with a number of
  51. performance optimizations.
  52. */
  53. SC = SproutCore = {} ;
  54.  
  55. /**
  56. Register a new key/value pair in the global object. You must register all
  57. top-level namespaces in this way for them to be found via bindings etc.
  58.  
  59. @param {String} key the name to expose.
  60. @param {Object} value the value to expose
  61. @returns {Object} SC namespace
  62. */
  63. SC.global = function(key, value) {
  64. system.global[key] = value;
  65. return SC ;
  66. };
  67.  
  68. /**
  69. Removes a global object from the top-level namespace. You should almost
  70. never call this. It is only used for unit testing.
  71.  
  72. @param {String} key the name to expose.
  73. @returns {Object} SC namespace
  74. */
  75. SC.global.remove = function(key) {
  76. delete system.global[key];
  77. return SC;
  78. };
  79.  
  80. SC.global('SC', SC);
  81.  
  82. /**
  83. Adds properties to a target object.
  84.  
  85. Takes the root object and adds the attributes for any additional
  86. arguments passed.
  87.  
  88. @param target {Object} the target object to extend
  89. @param properties {Object} one or more objects with properties to copy.
  90. @returns {Object} the target object.
  91. @static
  92. */
  93. SC.mixin = function() {
  94. // copy reference to target object
  95. var target = arguments[0] || {};
  96. var idx = 1;
  97. var length = arguments.length ;
  98. var options ;
  99.  
  100. // Handle case where we have only one item...extend SC
  101. if (length === 1) {
  102. target = this || {};
  103. idx=0;
  104. }
  105.  
  106. for ( ; idx < length; idx++ ) {
  107. if (!(options = arguments[idx])) continue ;
  108. for(var key in options) {
  109. if (!options.hasOwnProperty(key)) continue ;
  110. var copy = options[key] ;
  111. if (target===copy) continue ; // prevent never-ending loop
  112. if (copy !== undefined) target[key] = copy ;
  113. }
  114. }
  115.  
  116. return target;
  117. } ;
  118.  
  119. /**
  120. Adds properties to a target object. Unlike SC.mixin, however, if the target
  121. already has a value for a property, it will not be overwritten.
  122.  
  123. Takes the root object and adds the attributes for any additional
  124. arguments passed.
  125.  
  126. @param target {Object} the target object to extend
  127. @param properties {Object} one or more objects with properties to copy.
  128. @returns {Object} the target object.
  129. @static
  130. */
  131. SC.supplement = function() {
  132. // copy reference to target object
  133. var target = arguments[0] || {};
  134. var idx = 1;
  135. var length = arguments.length ;
  136. var options ;
  137.  
  138. // Handle case where we have only one item...extend SC
  139. if (length === 1) {
  140. target = this || {};
  141. idx=0;
  142. }
  143.  
  144. for ( ; idx < length; idx++ ) {
  145. if (!(options = arguments[idx])) continue ;
  146. for(var key in options) {
  147. if (!options.hasOwnProperty(key)) continue ;
  148. var src = target[key] ;
  149. var copy = options[key] ;
  150. if (target===copy) continue ; // prevent never-ending loop
  151. if (copy !== undefined && src === undefined) target[key] = copy ;
  152. }
  153. }
  154.  
  155. return target;
  156. } ;
  157.  
  158. /**
  159. Alternative to mixin. Provided for compatibility with jQuery.
  160. @function
  161. */
  162. SC.extend = SC.mixin ;
  163.  
  164. // ..........................................................
  165. // CORE FUNCTIONS
  166. //
  167. // Enough with the bootstrap code. Let's define some core functions
  168.  
  169. SC.mixin(/** @scope SC */ {
  170.  
  171. // ........................................
  172. // GLOBAL CONSTANTS
  173. //
  174. T_ERROR: 'error',
  175. T_OBJECT: 'object',
  176. T_NULL: 'null',
  177. T_CLASS: 'class',
  178. T_HASH: 'hash',
  179. T_FUNCTION: 'function',
  180. T_UNDEFINED: 'undefined',
  181. T_NUMBER: 'number',
  182. T_BOOL: 'boolean',
  183. T_ARRAY: 'array',
  184. T_STRING: 'string',
  185.  
  186. // ........................................
  187. // TYPING & ARRAY MESSAGING
  188. //
  189.  
  190. /**
  191. Returns a consistant type for the passed item.
  192.  
  193. Use this instead of the built-in typeOf() to get the type of an item.
  194. It will return the same result across all browsers and includes a bit
  195. more detail. Here is what will be returned:
  196.  
  197. | Return Value Constant | Meaning |
  198. | SC.T_STRING | String primitive |
  199. | SC.T_NUMBER | Number primitive |
  200. | SC.T_BOOLEAN | Boolean primitive |
  201. | SC.T_NULL | Null value |
  202. | SC.T_UNDEFINED | Undefined value |
  203. | SC.T_FUNCTION | A function |
  204. | SC.T_ARRAY | An instance of Array |
  205. | SC.T_CLASS | A SproutCore class (created using SC.Object.extend()) |
  206. | SC.T_OBJECT | A SproutCore object instance |
  207. | SC.T_HASH | A JavaScript object not inheriting from SC.Object |
  208.  
  209. @param item {Object} the item to check
  210. @returns {String} the type
  211. */
  212. typeOf: function(item) {
  213. if (item === undefined) return SC.T_UNDEFINED ;
  214. if (item === null) return SC.T_NULL ;
  215. var ret = typeof(item) ;
  216. if (ret == "object") {
  217. if (item instanceof Array) {
  218. ret = SC.T_ARRAY ;
  219. } else if (item instanceof Function) {
  220. ret = item.isClass ? SC.T_CLASS : SC.T_FUNCTION ;
  221.  
  222. // NB: typeOf() may be called before SC.Error has had a chance to load
  223. // so this code checks for the presence of SC.Error first just to make
  224. // sure. No error instance can exist before the class loads anyway so
  225. // this is safe.
  226. } else if (SC.Error && (item instanceof SC.Error)) {
  227. ret = SC.T_ERROR ;
  228. } else if (item.isObject === true) {
  229. ret = SC.T_OBJECT ;
  230. } else ret = SC.T_HASH ;
  231. } else if (ret === SC.T_FUNCTION) ret = (item.isClass) ? SC.T_CLASS : SC.T_FUNCTION;
  232. return ret ;
  233. },
  234.  
  235. /**
  236. Returns YES if the passed value is null or undefined. This avoids errors
  237. from JSLint complaining about use of ==, which can be technically
  238. confusing.
  239.  
  240. @param {Object} obj value to test
  241. @returns {Boolean}
  242. */
  243. none: function(obj) {
  244. return obj===null || obj===undefined;
  245. },
  246.  
  247. /**
  248. Verifies that a value is either null or an empty string. Return false if
  249. the object is not a string.
  250.  
  251. @param {Object} obj value to test
  252. @returns {Boolean}
  253. */
  254. empty: function(obj) {
  255. return obj===null || obj===undefined || obj==='';
  256. },
  257.  
  258. /**
  259. Returns YES if the passed object is an array or array-like. Instances
  260. of the NodeList class return false.
  261.  
  262. Unlike SC.typeOf this method returns true even if the passed object is
  263. not formally array but appears to be array-like (i.e. has a length
  264. property, responds to .objectAt, etc.)
  265.  
  266. @param obj {Object} the object to test
  267. @returns {Boolean}
  268. */
  269. isArray: function(obj) {
  270. if (obj && obj.objectAt) return YES ; // fast path
  271.  
  272. var len = (obj ? obj.length : null), type = SC.typeOf(obj);
  273. return !(SC.none(len) || (type === SC.T_FUNCTION) || (type === SC.T_STRING) || obj.setInterval) ;
  274. },
  275.  
  276. /**
  277. Makes an object into an Array if it is not array or array-like already.
  278. Unlike SC.A(), this method will not clone the object if it is already
  279. an array.
  280.  
  281. @param {Object} obj object to convert
  282. @returns {Array} Actual array
  283. */
  284. makeArray: function(obj) {
  285. return SC.isArray(obj) ? obj : SC.A(obj);
  286. },
  287.  
  288. /**
  289. Converts the passed object to an Array. If the object appears to be
  290. array-like, a new array will be cloned from it. Otherwise, a new array
  291. will be created with the item itself as the only item in the array.
  292.  
  293. @param object {Object} any enumerable or array-like object.
  294. @returns {Array} Array of items
  295. */
  296. A: function(obj) {
  297. // null or undefined -- fast path
  298. if (SC.none(obj)) return [] ;
  299.  
  300. // primitive -- fast path
  301. if (obj.slice instanceof Function) {
  302. // do we have a string?
  303. if (typeof(obj) === 'string') return [obj] ;
  304. else return obj.slice() ;
  305. }
  306.  
  307. // enumerable -- fast path
  308. if (obj.toArray) return obj.toArray() ;
  309.  
  310. // if not array-like, then just wrap in array.
  311. if (!SC.isArray(obj)) return [obj];
  312.  
  313. // when all else fails, do a manual convert...
  314. var ret = [], len = obj.length;
  315. while(--len >= 0) ret[len] = obj[len];
  316. return ret ;
  317. },
  318.  
  319. // ..........................................................
  320. // GUIDS & HASHES
  321. //
  322.  
  323. guidKey: "_sc_guid_" + new Date().getTime(),
  324.  
  325. // Used for guid generation...
  326. _nextGUID: 0, _numberGuids: [], _stringGuids: {}, _keyCache: {},
  327.  
  328. /**
  329. Returns a unique GUID for the object. If the object does not yet have
  330. a guid, one will be assigned to it. You can call this on any object,
  331. SC.Object-based or not, but be aware that it will add a _guid property.
  332.  
  333. You can also use this method on DOM Element objects.
  334.  
  335. @param obj {Object} any object, string, number, Element, or primitive
  336. @returns {String} the unique guid for this instance.
  337. */
  338. guidFor: function(obj) {
  339.  
  340. // special cases where we don't want to add a key to object
  341. if (obj === undefined) return "(undefined)" ;
  342. if (obj === null) return '(null)' ;
  343. if (obj === Object) return '(Object)';
  344. if (obj === Array) return '(Array)';
  345.  
  346. var guidKey = this.guidKey ;
  347. if (obj[guidKey]) return obj[guidKey] ;
  348.  
  349. switch(typeof obj) {
  350. case SC.T_NUMBER:
  351. return (this._numberGuids[obj] = this._numberGuids[obj] || ("nu" + obj));
  352. case SC.T_STRING:
  353. return (this._stringGuids[obj] = this._stringGuids[obj] || ("st" + obj));
  354. case SC.T_BOOL:
  355. return (obj) ? "(true)" : "(false)" ;
  356. default:
  357. return SC.generateGuid(obj);
  358. }
  359. },
  360.  
  361. /**
  362. Returns a key name that combines the named key + prefix. This is more
  363. efficient than simply combining strings because it uses a cache
  364. internally for performance.
  365.  
  366. @param {String} prefix the prefix to attach to the key
  367. @param {String} key key
  368. @returns {String} result
  369. */
  370. keyFor: function(prefix, key) {
  371. var ret, pcache = this._keyCache[prefix];
  372. if (!pcache) pcache = this._keyCache[prefix] = {}; // get cache for prefix
  373. ret = pcache[key];
  374. if (!ret) ret = pcache[key] = prefix + '_' + key ;
  375. return ret ;
  376. },
  377.  
  378. /**
  379. Generates a new guid, optionally saving the guid to the object that you
  380. pass in. You will rarely need to use this method. Instead you should
  381. call SC.guidFor(obj), which return an existing guid if available.
  382.  
  383. @param {Object} obj the object to assign the guid to
  384. @returns {String} the guid
  385. */
  386. generateGuid: function(obj) {
  387. var ret = ("sc" + (this._nextGUID++));
  388. if (obj) obj[this.guidKey] = ret ;
  389. return ret ;
  390. },
  391.  
  392. /**
  393. Returns a unique hash code for the object. If the object implements
  394. a hash() method, the value of that method will be returned. Otherwise,
  395. this will return the same value as guidFor().
  396.  
  397. Unlike guidFor(), this method allows you to implement logic in your
  398. code to cause two separate instances of the same object to be treated as
  399. if they were equal for comparisons and other functions.
  400.  
  401. IMPORTANT: If you implement a hash() method, it MUST falseT return a
  402. number or a string that contains only a number. Typically hash codes
  403. are strings that begin with a "%".
  404.  
  405. @param obj {Object} the object
  406. @returns {String} the hash code for this instance.
  407. */
  408. hashFor: function(obj) {
  409. return (obj && obj.hash && (typeof obj.hash === SC.T_FUNCTION)) ? obj.hash() : this.guidFor(obj) ;
  410. },
  411.  
  412. /**
  413. This will compare the two object values using their hash codes.
  414.  
  415. @param a {Object} first value to compare
  416. @param b {Object} the second value to compare
  417. @returns {Boolean} YES if the two have equal hash code values.
  418.  
  419. */
  420. isEqual: function(a,b) {
  421. // shortcut a few places.
  422. if (a === null) {
  423. return b === null ;
  424. } else if (a === undefined) {
  425. return b === undefined ;
  426.  
  427. // finally, check their hash-codes
  428. } else return this.hashFor(a) === this.hashFor(b) ;
  429. },
  430.  
  431.  
  432. /**
  433. This will compare two javascript values of possibly different types.
  434. It will tell you which one is greater than the other by returning
  435. -1 if the first is smaller than the second,
  436. 0 if both are equal,
  437. 1 if the first is greater than the second.
  438.  
  439. The order is calculated based on SC.ORDER_DEFINITION , if types are different.
  440. In case they have the same type an appropriate comparison for this type is made.
  441.  
  442. @param v {Object} first value to compare
  443. @param w {Object} the second value to compare
  444. @returns {NUMBER} -1 if v < w, 0 if v = w and 1 if v > w.
  445.  
  446. */
  447. compare: function (v, w) {
  448.  
  449. var type1 = SC.typeOf(v);
  450. var type2 = SC.typeOf(w);
  451. var type1Index = SC.ORDER_DEFINITION.indexOf(type1);
  452. var type2Index = SC.ORDER_DEFINITION.indexOf(type2);
  453.  
  454. if (type1Index < type2Index) return -1;
  455. if (type1Index > type2Index) return 1;
  456.  
  457. // ok - types are equal - so we have to check values now
  458. switch (type1) {
  459. case SC.T_BOOL:
  460. case SC.T_NUMBER:
  461. if (v<w) return -1;
  462. if (v>w) return 1;
  463. return 0;
  464.  
  465. case SC.T_STRING:
  466. if (v.localeCompare(w)<0) return -1;
  467. if (v.localeCompare(w)>0) return 1;
  468. return 0;
  469.  
  470. case SC.T_ARRAY:
  471. var l = Math.min(v.length,w.length);
  472. var r = 0;
  473. var i = 0;
  474. while (r===0 && i < l) {
  475. r = arguments.callee(v[i],w[i]);
  476. if ( r !== 0 ) return r;
  477. i++;
  478. }
  479.  
  480. // all elements are equal now
  481. // shorter array should be ordered first
  482. if (v.length < w.length) return -1;
  483. if (v.length > w.length) return 1;
  484. // arrays are equal now
  485. return 0;
  486.  
  487. case SC.T_OBJECT:
  488. if (v.constructor.isComparable === YES) return v.constructor.compare(v, w);
  489. return 0;
  490.  
  491. default:
  492. return 0;
  493. }
  494. },
  495.  
  496. // ..........................................................
  497. // OBJECT MANAGEMENT
  498. //
  499.  
  500. /**
  501. Empty function. Useful for some operations.
  502.  
  503. @returns {Object}
  504. */
  505. K: function() { return this; },
  506.  
  507. /**
  508. Empty array. Useful for some optimizations.
  509.  
  510. @property {Array}
  511. */
  512. EMPTY_ARRAY: [],
  513.  
  514. /**
  515. Empty hash. Useful for some optimizations.
  516.  
  517. @property {Hash}
  518. */
  519. EMPTY_HASH: {},
  520.  
  521. /**
  522. Empty range. Useful for some optimizations.
  523.  
  524. @property {Range}
  525. */
  526. EMPTY_RANGE: {start: 0, length: 0},
  527.  
  528. /**
  529. Creates a new object with the passed object as its prototype.
  530.  
  531. This method uses JavaScript's native inheritence method to create a new
  532. object.
  533.  
  534. You cannot use beget() to create new SC.Object-based objects, but you
  535. can use it to beget Arrays, Hashes, Sets and objects you build yourself.
  536. Note that when you beget() a new object, this method will also call the
  537. didBeget() method on the object you passed in if it is defined. You can
  538. use this method to perform any other setup needed.
  539.  
  540. In general, you will not use beget() often as SC.Object is much more
  541. useful, but for certain rare algorithms, this method can be very useful.
  542.  
  543. For more information on using beget(), see the section on beget() in
  544. Crockford's JavaScript: The Good Parts.
  545.  
  546. @param obj {Object} the object to beget
  547. @returns {Object} the new object.
  548. */
  549. beget: function(obj) {
  550. if (SC.none(obj)) return null ;
  551. var K = SC.K; K.prototype = obj ;
  552. var ret = new K();
  553. K.prototype = null ; // avoid leaks
  554. if (SC.typeOf(obj.didBeget) === SC.T_FUNCTION) ret = obj.didBeget(ret);
  555. return ret ;
  556. },
  557.  
  558. /**
  559. Creates a clone of the passed object. This function can take just about
  560. any type of object and create a clone of it, including primitive values
  561. (which are not actually cloned because they are immutable).
  562.  
  563. If the passed object implements the clone() method, then this function
  564. will simply call that method and return the result.
  565.  
  566. @param object {Object} the object to clone
  567. @returns {Object} the cloned object
  568. */
  569. copy: function(object) {
  570. var ret = object ;
  571.  
  572. // fast path
  573. if (object && object.isCopyable) return object.copy();
  574.  
  575. switch (SC.typeOf(object)) {
  576. case SC.T_ARRAY:
  577. if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
  578. ret = object.clone() ;
  579. } else ret = object.slice() ;
  580. break ;
  581.  
  582. case SC.T_HASH:
  583. case SC.T_OBJECT:
  584. if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
  585. ret = object.clone() ;
  586. } else {
  587. ret = {} ;
  588. for(var key in object) ret[key] = object[key] ;
  589. }
  590. }
  591.  
  592. return ret ;
  593. },
  594.  
  595. /**
  596. Returns a new object combining the values of all passed hashes.
  597.  
  598. @param object {Object} one or more objects
  599. @returns {Object} new Object
  600. */
  601. merge: function() {
  602. var ret = {}, len = arguments.length, idx;
  603. for(idx=0;idx<len;idx++) SC.mixin(ret, arguments[idx]);
  604. return ret ;
  605. },
  606.  
  607. /**
  608. Returns all of the keys defined on an object or hash. This is useful
  609. when inspecting objects for debugging.
  610.  
  611. @param {Object} obj
  612. @returns {Array} array of keys
  613. */
  614. keys: function(obj) {
  615. var ret = [];
  616. for(var key in obj) ret.push(key);
  617. return ret;
  618. },
  619.  
  620. /**
  621. Convenience method to inspect an object. This method will attempt to
  622. convert the object into a useful string description.
  623. */
  624. inspect: function(obj) {
  625. var v, ret = [] ;
  626. for(var key in obj) {
  627. v = obj[key] ;
  628. if (v === 'toString') continue ; // ignore useless items
  629. if (SC.typeOf(v) === SC.T_FUNCTION) v = "function() { ... }" ;
  630. ret.push(key + ": " + v) ;
  631. }
  632. return "{" + ret.join(" , ") + "}" ;
  633. },
  634.  
  635. /**
  636. Returns a tuple containing the object and key for the specified property
  637. path. If no object could be found to match the property path, then
  638. returns null.
  639.  
  640. This is the standard method used throughout SproutCore to resolve property
  641. paths.
  642.  
  643. @param path {String} the property path
  644. @param root {Object} optional parameter specifying the place to start
  645. @returns {Array} array with [object, property] if found or null
  646. */
  647. tupleForPropertyPath: function(path, root) {
  648.  
  649. // if the passed path is itself a tuple, return it
  650. if (SC.typeOf(path) === SC.T_ARRAY) return path ;
  651.  
  652. // find the key. It is the last . or first *
  653. var key ;
  654. var stopAt = path.indexOf('*') ;
  655. if (stopAt < 0) stopAt = path.lastIndexOf('.') ;
  656. key = (stopAt >= 0) ? path.slice(stopAt+1) : path ;
  657.  
  658. // convert path to object.
  659. var obj = this.objectForPropertyPath(path, root, stopAt) ;
  660. return (obj && key) ? [obj,key] : null ;
  661. },
  662.  
  663. /**
  664. Finds the object for the passed path or array of path components. This is
  665. the standard method used in SproutCore to traverse object paths.
  666.  
  667. @param path {String} the path
  668. @param root {Object} optional root object. global is used otherwise
  669. @param stopAt {Integer} optional point to stop searching the path.
  670. @returns {Object} the found object or undefined.
  671. */
  672. objectForPropertyPath: function(path, root, stopAt) {
  673.  
  674. var loc, nextDotAt, key, max ;
  675.  
  676. if (!root) root = system.global ;
  677.  
  678. // faster method for strings
  679. if (SC.typeOf(path) === SC.T_STRING) {
  680. if (stopAt === undefined) stopAt = path.length ;
  681. loc = 0 ;
  682. while((root) && (loc < stopAt)) {
  683. nextDotAt = path.indexOf('.', loc) ;
  684. if ((nextDotAt < 0) || (nextDotAt > stopAt)) nextDotAt = stopAt;
  685. key = path.slice(loc, nextDotAt);
  686. root = root.get ? root.get(key) : root[key] ;
  687. loc = nextDotAt+1;
  688. }
  689. if (loc < stopAt) root = undefined; // hit a dead end. :(
  690.  
  691. // older method using an array
  692. } else {
  693.  
  694. loc = 0; max = path.length; key = null;
  695. while((loc < max) && root) {
  696. key = path[loc++];
  697. if (key) root = (root.get) ? root.get(key) : root[key] ;
  698. }
  699. if (loc < max) root = undefined ;
  700. }
  701.  
  702. return root ;
  703. },
  704.  
  705.  
  706. // ..........................................................
  707. // LOCALIZATION SUPPORT
  708. //
  709.  
  710. /**
  711. Known loc strings
  712.  
  713. @property {Hash}
  714. */
  715. STRINGS: {},
  716.  
  717. /**
  718. This is a simplified handler for installing a bunch of strings. This
  719. ignores the language name and simply applies the passed strings hash.
  720.  
  721. @param {String} lang the language the strings are for
  722. @param {Hash} strings hash of strings
  723. @returns {SC} receiver
  724. */
  725. stringsFor: function(lang, strings) {
  726. SC.mixin(SC.STRINGS, strings);
  727. return this ;
  728. }
  729.  
  730.  
  731. }); // end mixin
  732.  
  733. /** @private Aliasn for SC.clone() */
  734. SC.clone = SC.copy ;
  735.  
  736. /** @private Alias for SC.A() */
  737. SC.$A = SC.A;
  738.  
  739. /** @private Provided for compatibility with old HTML templates. */
  740. SC.didLoad = SC.K ;
  741.  
  742. /** @private Used by SC.compare */
  743. SC.ORDER_DEFINITION = [ SC.T_ERROR,
  744. SC.T_UNDEFINED,
  745. SC.T_NULL,
  746. SC.T_BOOL,
  747. SC.T_NUMBER,
  748. SC.T_STRING,
  749. SC.T_ARRAY,
  750. SC.T_HASH,
  751. SC.T_OBJECT,
  752. SC.T_FUNCTION,
  753. SC.T_CLASS ];
  754.  
  755.  
  756. // ........................................
  757. // FUNCTION ENHANCEMENTS
  758. //
  759.  
  760. SC.mixin(Function.prototype,
  761. /** @lends Function.prototype */ {
  762.  
  763. /**
  764. Indicates that the function should be treated as a computed property.
  765.  
  766. Computed properties are methods that you want to treat as if they were
  767. static properties. When you use get() or set() on a computed property,
  768. the object will call the property method and return its value instead of
  769. returning the method itself. This makes it easy to create "virtual
  770. properties" that are computed dynamically from other properties.
  771.  
  772. Consider the following example:
  773.  
  774. {{{
  775. contact = SC.Object.create({
  776.  
  777. firstName: "Charles",
  778. lastName: "Jolley",
  779.  
  780. // This is a computed property!
  781. fullName: function() {
  782. return this.getEach('firstName','lastName').compact().join(' ') ;
  783. }.property('firstName', 'lastName'),
  784.  
  785. // this is not
  786. getFullName: function() {
  787. return this.getEach('firstName','lastName').compact().join(' ') ;
  788. }
  789. });
  790.  
  791. contact.get('firstName') ;
  792. --> "Charles"
  793.  
  794. contact.get('fullName') ;
  795. --> "Charles Jolley"
  796.  
  797. contact.get('getFullName') ;
  798. --> function()
  799. }}}
  800.  
  801. Note that when you get the fullName property, SproutCore will call the
  802. fullName() function and return its value whereas when you get() a property
  803. that contains a regular method (such as getFullName above), then the
  804. function itself will be returned instead.
  805.  
  806. h2. Using Dependent Keys
  807.  
  808. Computed properties are often computed dynamically from other member
  809. properties. Whenever those properties change, you need to notify any
  810. object that is observing the computed property that the computed property
  811. has changed also. We call these properties the computed property is based
  812. upon "dependent keys".
  813.  
  814. For example, in the contact object above, the fullName property depends on
  815. the firstName and lastName property. If either property value changes,
  816. any observer watching the fullName property will need to be notified as
  817. well.
  818.  
  819. You inform SproutCore of these dependent keys by passing the key names
  820. as parameters to the property() function. Whenever the value of any key
  821. you name here changes, the computed property will be marked as changed
  822. also.
  823.  
  824. You should always register dependent keys for computed properties to
  825. ensure they update.
  826.  
  827. h2. Using Computed Properties as Setters
  828.  
  829. Computed properties can be used to modify the state of an object as well
  830. as to return a value. Unlike many other key-value system, you use the
  831. same method to both get and set values on a computed property. To
  832. write a setter, simply declare two extra parameters: key and value.
  833.  
  834. Whenever your property function is called as a setter, the value
  835. parameter will be set. Whenever your property is called as a getter the
  836. value parameter will be undefined.
  837.  
  838. For example, the following object will split any full name that you set
  839. into a first name and last name components and save them.
  840.  
  841. {{{
  842. contact = SC.Object.create({
  843.  
  844. fullName: function(key, value) {
  845. if (value !== undefined) {
  846. var parts = value.split(' ') ;
  847. this.beginPropertyChanges()
  848. .set('firstName', parts[0])
  849. .set('lastName', parts[1])
  850. .endPropertyChanges() ;
  851. }
  852. return this.getEach('firstName', 'lastName').compact().join(' ');
  853. }.property('firstName','lastName')
  854.  
  855. }) ;
  856.  
  857. }}}
  858.  
  859. h2. Why Use The Same Method for Getters and Setters?
  860.  
  861. Most property-based frameworks expect you to write two methods for each
  862. property but SproutCore only uses one. We do this because most of the time
  863. when you write a setter is is basically a getter plus some extra work.
  864. There is little added benefit in writing both methods when you can
  865. conditionally exclude part of it. This helps to keep your code more
  866. compact and easier to maintain.
  867.  
  868. @param dependentKeys {String...} optional set of dependent keys
  869. @returns {Function} the declared function instance
  870. */
  871. property: function() {
  872. this.dependentKeys = SC.$A(arguments) ;
  873. var guid = SC.guidFor(this) ;
  874. this.cacheKey = "__cache__" + guid ;
  875. this.lastSetValueKey = "__lastValue__" + guid ;
  876. this.isProperty = YES ;
  877. return this ;
  878. },
  879.  
  880. /**
  881. You can call this method on a computed property to indicate that the
  882. property is cacheable (or not cacheable). By default all computed
  883. properties are not cached. Enabling this feature will allow SproutCore
  884. to cache the return value of your computed property and to use that
  885. value until one of your dependent properties changes or until you
  886. invoke propertyDidChange() and name the computed property itself.
  887.  
  888. If you do not specify this option, computed properties are assumed to be
  889. not cacheable.
  890.  
  891. @param {Boolean} aFlag optionally indicate cacheable or no, default YES
  892. @returns {Function} reciever
  893. */
  894. cacheable: function(aFlag) {
  895. this.isProperty = YES ; // also make a property just in case
  896. if (!this.dependentKeys) this.dependentKeys = [] ;
  897. this.isCacheable = (aFlag === undefined) ? YES : aFlag ;
  898. return this ;
  899. },
  900.  
  901. /**
  902. Indicates that the computed property is volatile. Normally SproutCore
  903. assumes that your computed property is idempotent. That is, calling
  904. set() on your property more than once with the same value has the same
  905. effect as calling it only once.
  906.  
  907. All non-computed properties are idempotent and normally you should make
  908. your computed properties behave the same way. However, if you need to
  909. make your property change its return value everytime your method is
  910. called, you may chain this to your property to make it volatile.
  911.  
  912. If you do not specify this option, properties are assumed to be
  913. non-volatile.
  914.  
  915. @param {Boolean} aFlag optionally indicate state, default to YES
  916. @returns {Function} receiver
  917. */
  918. idempotent: function(aFlag) {
  919. this.isProperty = YES; // also make a property just in case
  920. if (!this.dependentKeys) this.dependentKeys = [] ;
  921. this.isVolatile = (aFlag === undefined) ? YES : aFlag ;
  922. return this ;
  923. },
  924.  
  925. /**
  926. Declare that a function should observe an object at the named path. Note
  927. that the path is used only to construct the observation one time.
  928.  
  929. @returns {Function} receiver
  930. */
  931. observes: function(propertyPaths) {
  932. // sort property paths into local paths (i.e just a property name) and
  933. // full paths (i.e. those with a . or * in them)
  934. var loc = arguments.length, local = null, paths = null ;
  935. while(--loc >= 0) {
  936. var path = arguments[loc] ;
  937. // local
  938. if ((path.indexOf('.')<0) && (path.indexOf('*')<0)) {
  939. if (!local) local = this.localPropertyPaths = [] ;
  940. local.push(path);
  941.  
  942. // regular
  943. } else {
  944. if (!paths) paths = this.propertyPaths = [] ;
  945. paths.push(path) ;
  946. }
  947. }
  948. return this ;
  949. }
  950.  
  951. });
  952.  
  953. // ..........................................................
  954. // STRING ENHANCEMENT
  955. //
  956.  
  957. // Interpolate string. looks for %@ or %@1; to control the order of params.
  958. /**
  959. Apply formatting options to the string. This will look for occurrences
  960. of %@ in your string and substitute them with the arguments you pass into
  961. this method. If you want to control the specific order of replacement,
  962. you can add a number after the key as well to indicate which argument
  963. you want to insert.
  964.  
  965. Ordered insertions are most useful when building loc strings where values
  966. you need to insert may appear in different orders.
  967.  
  968. h3. Examples
  969.  
  970. {{{
  971. "Hello %@ %@".fmt('John', 'Doe') => "Hello John Doe"
  972. "Hello %@2, %@1".fmt('John', 'Doe') => "Hello Doe, John"
  973. }}}
  974.  
  975. @param args {Object...} optional arguments
  976. @returns {String} formatted string
  977. */
  978. String.prototype.fmt = function() {
  979. // first, replace any ORDERED replacements.
  980. var args = arguments;
  981. var idx = 0; // the current index for non-numerical replacements
  982. return this.replace(/%@([0-9]+)?/g, function(s, argIndex) {
  983. argIndex = (argIndex) ? parseInt(argIndex,0)-1 : idx++ ;
  984. s =args[argIndex];
  985. return ((s===null) ? '(null)' : (s===undefined) ? '' : s).toString();
  986. }) ;
  987. };
  988.  
  989. /**
  990. Localizes the string. This will look up the reciever string as a key
  991. in the current Strings hash. If the key matches, the loc'd value will be
  992. used. The resulting string will also be passed through fmt() to insert
  993. any variables.
  994.  
  995. @param args {Object...} optional arguments to interpolate also
  996. @returns {String} the localized and formatted string.
  997. */
  998. String.prototype.loc = function() {
  999. var str = SC.STRINGS[this] || this;
  1000. return str.fmt.apply(str,arguments) ;
  1001. };
  1002.  
  1003.  
  1004.  
  1005. /**
  1006. Splits the string into words, separated by spaces. Empty strings are
  1007. removed from the results.
  1008.  
  1009. @returns {Array} an array of non-empty strings
  1010. */
  1011. String.prototype.w = function() {
  1012. var ary = [], ary2 = this.split(' '), len = ary2.length ;
  1013. for (var idx=0; idx<len; ++idx) {
  1014. var str = ary2[idx] ;
  1015. if (str.length !== 0) ary.push(str) ; // skip empty strings
  1016. }
  1017. return ary ;
  1018. };
  1019.  
  1020. // place into exports - you can import the base namespace or symbols directly
  1021. module.exports = SC;
  1022. SC.SC = SC;
  1023. SC.SproutCore = SproutCore;
  1024. SC.YES = YES;
  1025. SC.NO = NO ;
Add Comment
Please, Sign In to add comment