sproutcore 1.0.1037 → 1.0.1042

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/History.txt +31 -0
  2. data/README.txt +3 -1
  3. data/Rakefile +11 -4
  4. data/VERSION.yml +3 -3
  5. data/buildtasks/build.rake +5 -0
  6. data/frameworks/sproutcore/Buildfile +3 -1
  7. data/frameworks/sproutcore/frameworks/animation/Buildfile +3 -0
  8. data/frameworks/sproutcore/frameworks/animation/LICENSE +25 -0
  9. data/frameworks/sproutcore/frameworks/animation/README.md +102 -0
  10. data/frameworks/sproutcore/frameworks/animation/core.js +934 -0
  11. data/frameworks/sproutcore/frameworks/animation/tests/core.js +65 -0
  12. data/frameworks/sproutcore/frameworks/datastore/models/record.js +28 -16
  13. data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +5 -2
  14. data/frameworks/sproutcore/frameworks/datastore/system/many_array.js +4 -0
  15. data/frameworks/sproutcore/frameworks/datastore/system/query.js +27 -13
  16. data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +36 -6
  17. data/frameworks/sproutcore/frameworks/datastore/system/store.js +7 -7
  18. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/storeDidChangeProperties.js +2 -1
  19. data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +13 -0
  20. data/frameworks/sproutcore/frameworks/debug/invoke_once_last_debugging.js +250 -0
  21. data/frameworks/sproutcore/frameworks/desktop/english.lproj/list_item.css +0 -12
  22. data/frameworks/sproutcore/frameworks/desktop/english.lproj/menu_item_view.css +3 -6
  23. data/frameworks/sproutcore/frameworks/desktop/english.lproj/panel.css +0 -8
  24. data/frameworks/sproutcore/frameworks/desktop/english.lproj/picker.css +0 -4
  25. data/frameworks/sproutcore/frameworks/desktop/english.lproj/segmented.css +1 -0
  26. data/frameworks/sproutcore/frameworks/desktop/english.lproj/well.css +0 -1
  27. data/frameworks/sproutcore/frameworks/desktop/mixins/border.js +1 -1
  28. data/frameworks/sproutcore/frameworks/desktop/panes/alert.js +2 -1
  29. data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +11 -4
  30. data/frameworks/sproutcore/frameworks/desktop/panes/palette.js +2 -0
  31. data/frameworks/sproutcore/frameworks/desktop/panes/panel.js +1 -5
  32. data/frameworks/sproutcore/frameworks/desktop/panes/picker.js +24 -23
  33. data/frameworks/sproutcore/frameworks/desktop/panes/select_button.js +91 -60
  34. data/frameworks/sproutcore/frameworks/desktop/panes/sheet.js +124 -24
  35. data/frameworks/sproutcore/frameworks/desktop/system/drag.js +5 -5
  36. data/frameworks/sproutcore/frameworks/desktop/system/root_responder.js +33 -25
  37. data/frameworks/sproutcore/frameworks/desktop/tests/panes/pane_page.js +41 -0
  38. data/frameworks/sproutcore/frameworks/desktop/tests/panes/select_button/methods.js +30 -1
  39. data/frameworks/sproutcore/frameworks/desktop/tests/panes/select_button/ui.js +13 -0
  40. data/frameworks/sproutcore/frameworks/desktop/tests/panes/sheet/ui.js +27 -21
  41. data/frameworks/sproutcore/frameworks/desktop/tests/views/button/methods.js +81 -1
  42. data/frameworks/sproutcore/frameworks/desktop/tests/views/radio/methods.js +3 -4
  43. data/frameworks/sproutcore/frameworks/desktop/views/button.js +65 -36
  44. data/frameworks/sproutcore/frameworks/desktop/views/collection.js +4 -7
  45. data/frameworks/sproutcore/frameworks/desktop/views/disclosure.js +8 -4
  46. data/frameworks/sproutcore/frameworks/desktop/views/list.js +1 -1
  47. data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +38 -5
  48. data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +5 -1
  49. data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +4 -1
  50. data/frameworks/sproutcore/frameworks/desktop/views/progress.js +19 -13
  51. data/frameworks/sproutcore/frameworks/desktop/views/radio.js +30 -2
  52. data/frameworks/sproutcore/frameworks/desktop/views/scroll.js +2 -3
  53. data/frameworks/sproutcore/frameworks/desktop/views/segmented.js +14 -17
  54. data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +5 -3
  55. data/frameworks/sproutcore/frameworks/desktop/views/slider.js +4 -2
  56. data/frameworks/sproutcore/frameworks/desktop/views/split.js +58 -59
  57. data/frameworks/sproutcore/frameworks/desktop/views/tab.js +2 -1
  58. data/frameworks/sproutcore/frameworks/desktop/views/well.js +1 -1
  59. data/frameworks/sproutcore/frameworks/foundation/core.js +6 -0
  60. data/frameworks/sproutcore/frameworks/foundation/english.lproj/view.css +2 -1
  61. data/frameworks/sproutcore/frameworks/foundation/mixins/button.js +33 -30
  62. data/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +8 -4
  63. data/frameworks/sproutcore/frameworks/foundation/mixins/string.js +15 -10
  64. data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +61 -12
  65. data/frameworks/sproutcore/frameworks/foundation/system/bundle.js +2 -1
  66. data/frameworks/sproutcore/frameworks/foundation/system/core_query.js +151 -131
  67. data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +29 -23
  68. data/frameworks/sproutcore/frameworks/foundation/system/event.js +18 -10
  69. data/frameworks/sproutcore/frameworks/foundation/system/page.js +7 -5
  70. data/frameworks/sproutcore/frameworks/foundation/system/ready.js +1 -0
  71. data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +9 -6
  72. data/frameworks/sproutcore/frameworks/foundation/system/request.js +41 -6
  73. data/frameworks/sproutcore/frameworks/foundation/system/response.js +89 -24
  74. data/frameworks/sproutcore/frameworks/foundation/system/routes.js +1 -1
  75. data/frameworks/sproutcore/frameworks/foundation/system/utils.js +0 -1
  76. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/array_case.js +27 -8
  77. data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_core.js +6 -6
  78. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_style.js +2 -2
  79. data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +65 -3
  80. data/frameworks/sproutcore/frameworks/foundation/tests/views/pane/append_remove.js +1 -1
  81. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/convertLayouts.js +145 -0
  82. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/didAppendToDocument.js +48 -0
  83. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/nextValidKeyView.js +91 -0
  84. data/frameworks/sproutcore/frameworks/foundation/validators/number.js +9 -5
  85. data/frameworks/sproutcore/frameworks/foundation/views/field.js +16 -14
  86. data/frameworks/sproutcore/frameworks/foundation/views/text_field.js +89 -67
  87. data/frameworks/sproutcore/frameworks/foundation/views/view.js +221 -73
  88. data/frameworks/sproutcore/frameworks/runtime/core.js +43 -22
  89. data/frameworks/sproutcore/frameworks/runtime/mixins/array.js +6 -0
  90. data/frameworks/sproutcore/frameworks/runtime/mixins/copyable.js +1 -1
  91. data/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +53 -34
  92. data/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +7 -3
  93. data/frameworks/sproutcore/frameworks/runtime/system/binding.js +19 -19
  94. data/frameworks/sproutcore/frameworks/runtime/system/logger.js +132 -88
  95. data/frameworks/sproutcore/frameworks/runtime/system/object.js +15 -9
  96. data/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +6 -0
  97. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +69 -0
  98. data/frameworks/sproutcore/frameworks/runtime/tests/system/logger.js +14 -2
  99. data/frameworks/sproutcore/license.js +3 -1
  100. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-repeat-x.psd +0 -0
  101. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_222222.png +0 -0
  102. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_454545.png +0 -0
  103. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_888888.png +0 -0
  104. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_ffffff.png +0 -0
  105. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/panels/sprite-x.png +0 -0
  106. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/panels/sprite-y.png +0 -0
  107. data/frameworks/sproutcore/themes/standard_theme/english.lproj/images/sc-theme-repeat-x.png +0 -0
  108. data/frameworks/sproutcore/themes/standard_theme/english.lproj/images/sc-theme-ysprite.png +0 -0
  109. data/frameworks/sproutcore/themes/standard_theme/english.lproj/list_item.css +15 -1
  110. data/frameworks/sproutcore/themes/standard_theme/english.lproj/menu_item_view.css +9 -0
  111. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panel.css +33 -0
  112. data/frameworks/sproutcore/themes/standard_theme/english.lproj/picker.css +17 -0
  113. data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +9 -6
  114. data/frameworks/sproutcore/themes/standard_theme/english.lproj/tab.css +0 -4
  115. data/frameworks/sproutcore/themes/standard_theme/english.lproj/well.css +36 -0
  116. data/gen/controller/templates/controllers/@filename@.js +1 -1
  117. data/lib/sproutcore/builders/minify.rb +45 -13
  118. data/lib/sproutcore/helpers/static_helper.rb +2 -2
  119. data/lib/sproutcore/models/manifest_entry.rb +42 -1
  120. data/lib/sproutcore/tools/build.rb +18 -2
  121. data/spec/lib/models/manifest_entry/hyperdomain_prefix.rb +34 -0
  122. data/vendor/yui-compressor/SCyuicompressor-2.4.2.jar +0 -0
  123. metadata +28 -22
  124. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/background-fat.jpg +0 -0
  125. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/background-thin.jpg +0 -0
  126. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-edge.png +0 -0
  127. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-left-corner.png +0 -0
  128. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-right-corner.png +0 -0
  129. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/left-edge.png +0 -0
  130. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/overlay.png +0 -0
  131. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/right-edge.png +0 -0
  132. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-edge.png +0 -0
  133. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-left-corner.png +0 -0
  134. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-right-corner.png +0 -0
@@ -71,10 +71,10 @@ var SproutCore = SproutCore || SC ;
71
71
  */
72
72
  SC.mixin = function() {
73
73
  // copy reference to target object
74
- var target = arguments[0] || {};
75
- var idx = 1;
76
- var length = arguments.length ;
77
- var options ;
74
+ var target = arguments[0] || {},
75
+ idx = 1,
76
+ length = arguments.length ,
77
+ options, copy , key;
78
78
 
79
79
  // Handle case where we have only one item...extend SC
80
80
  if (length === 1) {
@@ -84,9 +84,9 @@ SC.mixin = function() {
84
84
 
85
85
  for ( ; idx < length; idx++ ) {
86
86
  if (!(options = arguments[idx])) continue ;
87
- for(var key in options) {
87
+ for(key in options) {
88
88
  if (!options.hasOwnProperty(key)) continue ;
89
- var copy = options[key] ;
89
+ copy = options[key] ;
90
90
  if (target===copy) continue ; // prevent never-ending loop
91
91
  if (copy !== undefined) target[key] = copy ;
92
92
  }
@@ -424,11 +424,30 @@ SC.mixin(/** @scope SC */ {
424
424
 
425
425
  */
426
426
  compare: function (v, w) {
427
+ // Doing a '===' check is very cheap, so in the case of equality, checking
428
+ // this up-front is a big win.
429
+ if (v === w) return 0;
427
430
 
428
431
  var type1 = SC.typeOf(v);
429
432
  var type2 = SC.typeOf(w);
430
- var type1Index = SC.ORDER_DEFINITION.indexOf(type1);
431
- var type2Index = SC.ORDER_DEFINITION.indexOf(type2);
433
+
434
+ // If we haven't yet generated a reverse-mapping of SC.ORDER_DEFINITION,
435
+ // do so now.
436
+ var mapping = SC.ORDER_DEFINITION_MAPPING;
437
+ if (!mapping) {
438
+ var order = SC.ORDER_DEFINITION;
439
+ mapping = SC.ORDER_DEFINITION_MAPPING = {};
440
+ var idx, len;
441
+ for (idx = 0, len = order.length; idx < len; ++idx) {
442
+ mapping[order[idx]] = idx;
443
+ }
444
+
445
+ // We no longer need SC.ORDER_DEFINITION.
446
+ delete SC.ORDER_DEFINITION;
447
+ }
448
+
449
+ var type1Index = mapping[type1];
450
+ var type2Index = mapping[type2];
432
451
 
433
452
  if (type1Index < type2Index) return -1;
434
453
  if (type1Index > type2Index) return 1;
@@ -442,24 +461,28 @@ SC.mixin(/** @scope SC */ {
442
461
  return 0;
443
462
 
444
463
  case SC.T_STRING:
445
- if (v.localeCompare(w)<0) return -1;
446
- if (v.localeCompare(w)>0) return 1;
464
+ var comp = v.localeCompare(w);
465
+ if (comp<0) return -1;
466
+ if (comp>0) return 1;
447
467
  return 0;
448
468
 
449
469
  case SC.T_ARRAY:
450
- var l = Math.min(v.length,w.length);
470
+ var vLen = v.length;
471
+ var wLen = w.length;
472
+ var l = Math.min(vLen, wLen);
451
473
  var r = 0;
452
474
  var i = 0;
475
+ var thisFunc = arguments.callee;
453
476
  while (r===0 && i < l) {
454
- r = arguments.callee(v[i],w[i]);
455
- if ( r !== 0 ) return r;
477
+ r = thisFunc(v[i],w[i]);
456
478
  i++;
457
479
  }
480
+ if (r !== 0) return r;
458
481
 
459
482
  // all elements are equal now
460
483
  // shorter array should be ordered first
461
- if (v.length < w.length) return -1;
462
- if (v.length > w.length) return 1;
484
+ if (vLen < wLen) return -1;
485
+ if (vLen > wLen) return 1;
463
486
  // arrays are equal now
464
487
  return 0;
465
488
 
@@ -956,8 +979,8 @@ SC.mixin(Function.prototype,
956
979
  */
957
980
  String.prototype.fmt = function() {
958
981
  // first, replace any ORDERED replacements.
959
- var args = arguments;
960
- var idx = 0; // the current index for non-numerical replacements
982
+ var args = arguments,
983
+ idx = 0; // the current index for non-numerical replacements
961
984
  return this.replace(/%@([0-9]+)?/g, function(s, argIndex) {
962
985
  argIndex = (argIndex) ? parseInt(argIndex,0)-1 : idx++ ;
963
986
  s =args[argIndex];
@@ -988,12 +1011,10 @@ String.prototype.loc = function() {
988
1011
  @returns {Array} an array of non-empty strings
989
1012
  */
990
1013
  String.prototype.w = function() {
991
- var ary = [], ary2 = this.split(' '), len = ary2.length ;
992
- for (var idx=0; idx<len; ++idx) {
993
- var str = ary2[idx] ;
1014
+ var ary = [], ary2 = this.split(' '), len = ary2.length, str, idx=0;
1015
+ for (idx=0; idx<len; ++idx) {
1016
+ str = ary2[idx] ;
994
1017
  if (str.length !== 0) ary.push(str) ; // skip empty strings
995
1018
  }
996
1019
  return ary ;
997
1020
  };
998
-
999
-
@@ -351,6 +351,12 @@ SC.Array = {
351
351
  addRangeObserver: function(indexes, target, method, context) {
352
352
  var rangeob = this._array_rangeObservers;
353
353
  if (!rangeob) rangeob = this._array_rangeObservers = SC.CoreSet.create() ;
354
+
355
+ // The first time a range observer is added, cache the current length so
356
+ // we can properly notify observers the first time through
357
+ if (this._array_oldLength===undefined) {
358
+ this._array_oldLength = this.get('length') ;
359
+ }
354
360
 
355
361
  var C = this.rangeObserverClass ;
356
362
  var isDeep = NO; //disable this feature for now
@@ -54,7 +54,7 @@ SC.Copyable = {
54
54
  frozenCopy: function() {
55
55
  var isFrozen = this.get ? this.get('isFrozen') : this.isFrozen;
56
56
  if (isFrozen === YES) return this;
57
- else if (isFrozen === undefined) throw "%@ does not support freezing";
57
+ else if (isFrozen === undefined) throw "%@ does not support freezing".fmt(this);
58
58
  else return this.copy().freeze();
59
59
  }
60
60
  };
@@ -275,8 +275,9 @@ SC.Observable = {
275
275
  cachedep, cache, idx, dfunc ;
276
276
 
277
277
  // if there are any dependent keys and they use caching, then clear the
278
- // cache.
279
- if (this._kvo_cacheable && (cache = this._kvo_cache)) {
278
+ // cache. (If we're notifying, then propertyDidChange will do this for
279
+ // us.)
280
+ if (!notify && this._kvo_cacheable && (cache = this._kvo_cache)) {
280
281
  // lookup the cached dependents for this key. if undefined, compute.
281
282
  // note that if cachdep is set to null is means we figure out it has no
282
283
  // cached dependencies already. this is different from undefined.
@@ -720,7 +721,7 @@ SC.Observable = {
720
721
  }
721
722
  if (!target) target = this ;
722
723
  if (SC.typeOf(method) === SC.T_STRING) method = target[method] ;
723
- if (!method) throw "You must pass a method to addObserver()" ;
724
+ if (!method) throw "You must pass a method to removeObserver()" ;
724
725
 
725
726
  // if the key contains a '.', this is a chained observer.
726
727
  key = key.toString() ;
@@ -808,18 +809,19 @@ SC.Observable = {
808
809
  if (this._observableInited) return ;
809
810
  this._observableInited = YES ;
810
811
 
811
- var loc, keys, key, value, observer, propertyPaths, propertyPathsLength ;
812
+ var loc, keys, key, value, observer, propertyPaths, propertyPathsLength,
813
+ len, ploc, path, dotIndex, root, propertyKey, keysLen;
812
814
 
813
815
  // Loop through observer functions and register them
814
816
  if (keys = this._observers) {
815
- var len = keys.length ;
817
+ len = keys.length ;
816
818
  for(loc=0;loc<len;loc++) {
817
819
  key = keys[loc]; observer = this[key] ;
818
820
  propertyPaths = observer.propertyPaths ;
819
821
  propertyPathsLength = (propertyPaths) ? propertyPaths.length : 0 ;
820
- for(var ploc=0;ploc<propertyPathsLength;ploc++) {
821
- var path = propertyPaths[ploc] ;
822
- var dotIndex = path.indexOf('.') ;
822
+ for(ploc=0;ploc<propertyPathsLength;ploc++) {
823
+ path = propertyPaths[ploc] ;
824
+ dotIndex = path.indexOf('.') ;
823
825
  // handle most common case, observing a local property
824
826
  if (dotIndex < 0) {
825
827
  this.addObserver(path, this, observer) ;
@@ -832,7 +834,7 @@ SC.Observable = {
832
834
  // will add the observer now or later when the named path becomes
833
835
  // available.
834
836
  } else {
835
- var root = null ;
837
+ root = null ;
836
838
 
837
839
  // handle special cases for observers that look to the local root
838
840
  if (dotIndex === 0) {
@@ -852,17 +854,17 @@ SC.Observable = {
852
854
  // Add Bindings
853
855
  this.bindings = []; // will be filled in by the bind() method.
854
856
  if (keys = this._bindings) {
855
- for(loc=0;loc<keys.length;loc++) {
857
+ for(loc=0, keysLen = keys.length; loc < keysLen;loc++) {
856
858
  // get propertyKey
857
859
  key = keys[loc] ; value = this[key] ;
858
- var propertyKey = key.slice(0,-7) ; // contentBinding => content
860
+ propertyKey = key.slice(0,-7) ; // contentBinding => content
859
861
  this[key] = this.bind(propertyKey, value) ;
860
862
  }
861
863
  }
862
864
 
863
865
  // Add Properties
864
866
  if (keys = this._properties) {
865
- for(loc=0;loc<keys.length;loc++) {
867
+ for(loc=0, keysLen = keys.length; loc<keysLen;loc++) {
866
868
  key = keys[loc];
867
869
  if (value = this[key]) {
868
870
 
@@ -904,10 +906,10 @@ SC.Observable = {
904
906
 
905
907
  SC.Observers.flush(this) ; // hookup as many observers as possible.
906
908
 
907
- var log = SC.LOG_OBSERVERS && !(this.LOG_OBSERVING===NO) ;
908
- var observers, changes, dependents, starObservers, idx, keys, rev ;
909
- var members, membersLength, member, memberLoc, target, method, loc, func ;
910
- var context, spaces, cache ;
909
+ var log = SC.LOG_OBSERVERS && !(this.LOG_OBSERVING===NO),
910
+ observers, changes, dependents, starObservers, idx, keys, rev,
911
+ members, membersLength, member, memberLoc, target, method, loc, func,
912
+ context, spaces, cache ;
911
913
 
912
914
  if (log) {
913
915
  spaces = SC.KVO_SPACES = (SC.KVO_SPACES || '') + ' ';
@@ -1068,14 +1070,14 @@ SC.Observable = {
1068
1070
  */
1069
1071
  bind: function(toKey, target, method) {
1070
1072
 
1071
- var binding ;
1073
+ var binding , pathType;
1072
1074
 
1073
1075
  // normalize...
1074
1076
  if (method !== undefined) target = [target, method];
1075
1077
 
1076
1078
  // if a string or array (i.e. tuple) is passed, convert this into a
1077
1079
  // binding. If a binding default was provided, use that.
1078
- var pathType = SC.typeOf(target) ;
1080
+ pathType = SC.typeOf(target) ;
1079
1081
  if (pathType === SC.T_STRING || pathType === SC.T_ARRAY) {
1080
1082
  binding = this[toKey + 'BindingDefault'] || SC.Binding;
1081
1083
  binding = binding.beget().from(target) ;
@@ -1098,30 +1100,31 @@ SC.Observable = {
1098
1100
  }
1099
1101
  */
1100
1102
  didChangeFor: function(context) {
1101
-
1103
+ var valueCache, revisionCache, seenValues, seenRevisions, ret,
1104
+ currentRevision, idx, key, value;
1102
1105
  context = SC.hashFor(context) ; // get a hash key we can use in caches.
1103
1106
 
1104
1107
  // setup caches...
1105
- var valueCache = this._kvo_didChange_valueCache ;
1108
+ valueCache = this._kvo_didChange_valueCache ;
1106
1109
  if (!valueCache) valueCache = this._kvo_didChange_valueCache = {};
1107
- var revisionCache = this._kvo_didChange_revisionCache;
1110
+ revisionCache = this._kvo_didChange_revisionCache;
1108
1111
  if (!revisionCache) revisionCache=this._kvo_didChange_revisionCache={};
1109
1112
 
1110
1113
  // get the cache of values and revisions already seen in this context
1111
- var seenValues = valueCache[context] || {} ;
1112
- var seenRevisions = revisionCache[context] || {} ;
1114
+ seenValues = valueCache[context] || {} ;
1115
+ seenRevisions = revisionCache[context] || {} ;
1113
1116
 
1114
1117
  // prepare too loop!
1115
- var ret = false ;
1116
- var currentRevision = this._kvo_revision || 0 ;
1117
- var idx = arguments.length ;
1118
+ ret = false ;
1119
+ currentRevision = this._kvo_revision || 0 ;
1120
+ idx = arguments.length ;
1118
1121
  while(--idx >= 1) { // NB: loop only to 1 to ignore context arg.
1119
- var key = arguments[idx];
1122
+ key = arguments[idx];
1120
1123
 
1121
1124
  // has the kvo revision changed since the last time we did this?
1122
1125
  if (seenRevisions[key] != currentRevision) {
1123
1126
  // yes, check the value with the last seen value
1124
- var value = this.get(key) ;
1127
+ value = this.get(key) ;
1125
1128
  if (seenValues[key] !== value) {
1126
1129
  ret = true ; // did change!
1127
1130
  seenValues[key] = value;
@@ -1206,9 +1209,9 @@ SC.Observable = {
1206
1209
  @returns {Array} Values of property keys.
1207
1210
  */
1208
1211
  getEach: function() {
1209
- var keys = SC.A(arguments) ;
1210
- var ret = [];
1211
- for(var idx=0; idx<keys.length;idx++) {
1212
+ var keys = SC.A(arguments),
1213
+ ret = [], idx, idxLen;
1214
+ for(idx=0, idxLen = keys.length; idx < idxLen;idx++) {
1212
1215
  ret[ret.length] = this.getPath(keys[idx]);
1213
1216
  }
1214
1217
  return ret ;
@@ -1298,9 +1301,10 @@ SC.Observable = {
1298
1301
  @param {String...} propertyNames one or more property names
1299
1302
  */
1300
1303
  logProperty: function() {
1301
- var props = SC.$A(arguments) ;
1302
- for(var idx=0;idx<props.length; idx++) {
1303
- var prop = props[idx] ;
1304
+ var props = SC.$A(arguments),
1305
+ prop, propsLen, idx;
1306
+ for(idx=0, propsLen = props.length; idx<propsLen; idx++) {
1307
+ prop = props[idx] ;
1304
1308
  console.log('%@:%@: '.fmt(SC.guidFor(this), prop), this.get(prop)) ;
1305
1309
  }
1306
1310
  },
@@ -1314,5 +1318,20 @@ SC.logChange = function logChange(target, key, value) {
1314
1318
  console.log("CHANGE: %@[%@] => %@".fmt(target, key, target.get(key))) ;
1315
1319
  };
1316
1320
 
1321
+ /**
1322
+ Retrieves a property from an object, using get() if the
1323
+ object implements SC.Observable.
1324
+
1325
+ @param {Object} object the object to query
1326
+ @param {String} key the property to retrieve
1327
+ */
1328
+ SC.mixin(SC, {
1329
+ get: function(object, key) {
1330
+ if (!object) return undefined;
1331
+ if (object.get) return object.get(key);
1332
+ return object[key];
1333
+ }
1334
+ });
1335
+
1317
1336
  // Make all Array's observable
1318
1337
  SC.mixin(Array.prototype, SC.Observable) ;
@@ -28,8 +28,12 @@ SC.ObserverSet = {
28
28
  _membersCacheIsValid: NO,
29
29
 
30
30
  /**
31
- adds the named target/method observer to the set. The method must be
32
- a function, not a string..
31
+ Adds the named target/method observer to the set. The method must be
32
+ a function, not a string.
33
+
34
+ Note that in debugging mode only, this method is overridden to also record
35
+ the name of the object and function that resulted in the target/method
36
+ being added.
33
37
  */
34
38
  add: function(target, method, context) {
35
39
  var targetGuid = (target) ? SC.guidFor(target) : "__this__";
@@ -48,7 +52,7 @@ SC.ObserverSet = {
48
52
  // implementation is intentionally lazy.
49
53
  if (context !== undefined) {
50
54
  var contexts = methods.contexts ;
51
- if (!context) contexts = {};
55
+ if (!context) context = methods.contexts = {} ;
52
56
  contexts[SC.guidFor(method)] = context ;
53
57
  }
54
58
 
@@ -296,8 +296,8 @@ SC.Binding = {
296
296
  Returns a builder function for compatibility.
297
297
  */
298
298
  builder: function() {
299
- var binding = this ;
300
- var ret = function(fromProperty) { return binding.beget().from(fromProperty); };
299
+ var binding = this,
300
+ ret = function(fromProperty) { return binding.beget().from(fromProperty); };
301
301
  ret.beget = function() { return binding.beget(); } ;
302
302
  return ret ;
303
303
  },
@@ -376,8 +376,8 @@ SC.Binding = {
376
376
  if (!this._connectionPending) return; //nothing to do
377
377
  this._connectionPending = NO ;
378
378
 
379
- var path, root ;
380
- var bench = SC.BENCHMARK_BINDING_SETUP;
379
+ var path, root,
380
+ bench = SC.BENCHMARK_BINDING_SETUP;
381
381
 
382
382
  if (bench) SC.Benchmark.start("SC.Binding.connect()");
383
383
 
@@ -507,7 +507,7 @@ SC.Binding = {
507
507
  _computeBindingValue: function() {
508
508
  var source = this._bindingSource,
509
509
  key = this._bindingKey,
510
- v;
510
+ v, idx;
511
511
 
512
512
  if (!source) return ; // nothing to do
513
513
  this._bindingValue = v = source.getPath(key);
@@ -515,9 +515,10 @@ SC.Binding = {
515
515
  // apply any transforms to get the to property value also
516
516
  var transforms = this._transforms;
517
517
  if (transforms) {
518
- var len = transforms.length ;
519
- for(var idx=0;idx<len;idx++) {
520
- var transform = transforms[idx] ;
518
+ var len = transforms.length,
519
+ transform;
520
+ for(idx=0;idx<len;idx++) {
521
+ transform = transforms[idx] ;
521
522
  v = transform(v, this) ;
522
523
  }
523
524
  }
@@ -547,11 +548,10 @@ SC.Binding = {
547
548
  this._isFlushing = YES ;
548
549
  SC.Observers.suspendPropertyObserving();
549
550
 
550
- var didFlush = NO ;
551
- var log = SC.LOG_BINDINGS ;
552
-
553
- // connect any bindings
554
- var queue, binding ;
551
+ var didFlush = NO,
552
+ log = SC.LOG_BINDINGS,
553
+ // connect any bindings
554
+ queue, binding ;
555
555
  while((queue = this._connectQueue).length >0) {
556
556
  this._connectQueue = this._alternateConnectQueue ;
557
557
  this._alternateConnectQueue = queue ;
@@ -598,10 +598,10 @@ SC.Binding = {
598
598
  this._computeBindingTargets() ;
599
599
  this._computeBindingValue();
600
600
 
601
- var v = this._bindingValue ;
602
- var tv = this._transformedBindingValue ;
603
- var bench = SC.BENCHMARK_BINDING_NOTIFICATIONS ;
604
- var log = SC.LOG_BINDINGS ;
601
+ var v = this._bindingValue,
602
+ tv = this._transformedBindingValue,
603
+ bench = SC.BENCHMARK_BINDING_NOTIFICATIONS,
604
+ log = SC.LOG_BINDINGS ;
605
605
 
606
606
  // the from property value will always be the binding value, update if
607
607
  // needed.
@@ -642,8 +642,8 @@ SC.Binding = {
642
642
  // we are connected, go ahead and sync
643
643
  } else {
644
644
  this._computeBindingTargets() ;
645
- var target = this._fromTarget ;
646
- var key = this._fromPropertyKey ;
645
+ var target = this._fromTarget,
646
+ key = this._fromPropertyKey ;
647
647
  if (!target || !key) return this ; // nothing to do
648
648
 
649
649
  // get the new value
@@ -5,7 +5,7 @@
5
5
 
6
6
  /**
7
7
  If {@link SC.Logger.format} is true, this delimiter will be put between arguments.
8
-
8
+
9
9
  @property {String}
10
10
  */
11
11
  SC.LOGGER_LOG_DELIMITER = ", ";
@@ -13,7 +13,7 @@ SC.LOGGER_LOG_DELIMITER = ", ";
13
13
  /**
14
14
  If {@link SC.Logger.error} falls back onto {@link SC.Logger.log}, this will be
15
15
  prepended to the output.
16
-
16
+
17
17
  @property {String}
18
18
  */
19
19
  SC.LOGGER_LOG_ERROR = "ERROR: ";
@@ -21,7 +21,7 @@ SC.LOGGER_LOG_ERROR = "ERROR: ";
21
21
  /**
22
22
  If {@link SC.Logger.info} falls back onto {@link SC.Logger.log}, this will be
23
23
  prepended to the output.
24
-
24
+
25
25
  @property {String}
26
26
  */
27
27
  SC.LOGGER_LOG_INFO = "INFO: ";
@@ -29,86 +29,102 @@ SC.LOGGER_LOG_INFO = "INFO: ";
29
29
  /**
30
30
  If {@link SC.Logger.warn} falls back onto {@link SC.Logger.log}, this will be
31
31
  prepended to the output.
32
-
32
+
33
33
  @property {String}
34
34
  */
35
35
  SC.LOGGER_LOG_WARN = "WARNING: ";
36
36
 
37
+ /**
38
+ If {@link SC.Logger.debug} falls back onto {@link SC.Logger.log}, this will be
39
+ prepended to the output.
40
+
41
+ @property {String}
42
+ */
43
+ SC.LOGGER_LOG_DEBUG = "DEBUG: ";
44
+
37
45
  /** @class
38
-
46
+
39
47
  Object to allow for safe logging actions, such as using the browser console.
40
-
48
+
41
49
  The FireFox plugin Firebug was used as a function reference. Please see
42
50
  {@link <a href="http://getfirebug.com/logging.html">Firebug Logging Reference</a>}
43
51
  for further information.
44
-
52
+
45
53
  @author Colin Campbell
54
+ @author Benedikt Böhm
46
55
  @extends SC.Object
47
56
  @since Sproutcore 1.0
48
57
  @see <a href="http://getfirebug.com/logging.html">Firebug Logging Reference</a>
49
58
  */
50
- SC.Logger = SC.Object.create({
51
-
59
+ SC.Logger = SC.Object.create({
60
+
52
61
  // ..........................................................
53
62
  // PROPERTIES
54
- //
55
-
63
+ //
64
+
65
+ /**
66
+ Whether or not to enable debug logging.
67
+
68
+ @property: {Boolean}
69
+ */
70
+ debugEnabled: NO,
71
+
56
72
  /**
57
73
  Computed property that checks for the existence of the reporter object.
58
-
74
+
59
75
  @property {Boolean}
60
76
  */
61
77
  exists: function() {
62
78
  return typeof(this.get('reporter')) !== 'undefined' && this.get('reporter') != null;
63
79
  }.property('reporter').cacheable(),
64
-
80
+
65
81
  /**
66
82
  If console.log does not exist, SC.Logger will use window.alert instead.
67
-
83
+
68
84
  This property is only used inside {@link SC.Logger.log}. If fallBackOnLog is
69
85
  false and you call a different function, an alert will not be opened.
70
-
86
+
71
87
  @property {Boolean}
72
88
  */
73
89
  fallBackOnAlert: NO,
74
-
90
+
75
91
  /**
76
92
  If some function, such as console.dir, does not exist,
77
93
  SC.Logger will try console.log if this is true.
78
-
94
+
79
95
  @property {Boolean}
80
96
  */
81
97
  fallBackOnLog: YES,
82
-
98
+
83
99
  /**
84
100
  Whether or not to format multiple arguments together
85
101
  or let the browser deal with that.
86
-
102
+
87
103
  @property {Boolean}
88
104
  */
89
105
  format: YES,
90
-
106
+
91
107
  /**
92
108
  The reporter is the object which implements the actual logging functions.
93
-
109
+
94
110
  @default The browser's console
95
111
  @property {Object}
96
112
  */
97
113
  reporter: console,
98
-
114
+
99
115
  // ..........................................................
100
116
  // METHODS
101
- //
117
+ //
102
118
 
103
119
  /**
104
120
  Log output to the console, but only if it exists.
105
-
121
+
106
122
  @param {String|Array|Function|Object}
107
123
  @returns {Boolean} true if reporter.log exists, false otherwise
108
124
  */
109
125
  log: function() {
110
126
  var reporter = this.get('reporter');
111
-
127
+
112
128
  // log through the reporter
113
129
  if (this.get('exists') && typeof(reporter.log) === "function") {
114
130
  if (this.get('format')) {
@@ -119,7 +135,7 @@ SC.Logger = SC.Object.create({
119
135
  }
120
136
  return true;
121
137
  }
122
-
138
+
123
139
  // log through alert
124
140
  else if (this.fallBackOnAlert) {
125
141
  var s = this.get('format') ? this._argumentsToString.apply(this, arguments) : arguments;
@@ -135,40 +151,68 @@ SC.Logger = SC.Object.create({
135
151
  }
136
152
  return false;
137
153
  },
138
-
154
+
155
+ /**
156
+ Log a debug message to the console.
157
+
158
+ Logs the response using {@link SC.Logger.log} if reporter.debug does not exist and
159
+ {@link SC.Logger.fallBackOnLog} is true.
160
+
161
+ @param {String|Array|Function|Object}
162
+ @returns {Boolean} true if logged to reporter, false if not
163
+ */
164
+ debug: function() {
165
+ var reporter = this.get('reporter');
166
+
167
+ if (this.get('debugEnabled') !== YES) {
168
+ return false;
169
+ }
170
+
171
+ if (this.get('exists') && (typeof reporter.debug === "function")) {
172
+ reporter.debug.apply(reporter, arguments);
173
+ return true;
174
+ }
175
+ else if (this.fallBackOnLog) {
176
+ var a = this._argumentsToArray(arguments);
177
+ if (typeof(a.unshift) === "function") a.unshift(SC.LOGGER_LOG_DEBUG);
178
+ return this.log.apply(this, a);
179
+ }
180
+ return false;
181
+ },
182
+
139
183
  /**
140
184
  Prints the properties of an object.
141
-
185
+
142
186
  Logs the object using {@link SC.Logger.log} if the reporter.dir function does not exist and
143
187
  {@link SC.Logger.fallBackOnLog} is true.
144
-
188
+
145
189
  @param {Object}
146
190
  @returns {Boolean} true if logged to console, false if not
147
191
  */
148
192
  dir: function() {
149
193
  var reporter = this.get('reporter');
150
-
151
- if (this.get('exists') && typeof(reporter.dir) === "function") {
194
+
195
+ if (this.get('exists') && typeof(reporter.dir) === "function") {
152
196
  // Firebug's console.dir doesn't support multiple objects here
153
197
  // but maybe custom reporters will
154
- reporter.dir.apply(reporter, arguments);
155
- return true;
156
- }
157
- return (this.fallBackOnLog) ? this.log.apply(this, arguments) : false;
198
+ reporter.dir.apply(reporter, arguments);
199
+ return true;
200
+ }
201
+ return (this.fallBackOnLog) ? this.log.apply(this, arguments) : false;
158
202
  },
159
-
203
+
160
204
  /**
161
205
  Prints an XML outline for any HTML or XML object.
162
-
206
+
163
207
  Logs the object using {@link SC.Logger.log} if reporter.dirxml function does not exist and
164
208
  {@lnk SC.Logger.fallBackOnLog} is true.
165
-
209
+
166
210
  @param {Object}
167
211
  @returns {Boolean} true if logged to reporter, false if not
168
212
  */
169
213
  dirxml: function() {
170
214
  var reporter = this.get('reporter');
171
-
215
+
172
216
  if (this.get('exists') && typeof(reporter.dirxml) === "function") {
173
217
  // Firebug's console.dirxml doesn't support multiple objects here
174
218
  // but maybe custom reporters will
@@ -177,19 +221,19 @@ SC.Logger = SC.Object.create({
177
221
  }
178
222
  return (this.fallBackOnLog) ? this.log.apply(this, arguments) : false;
179
223
  },
180
-
224
+
181
225
  /**
182
226
  Log an error to the console
183
-
227
+
184
228
  Logs the error using {@link SC.Logger.log} if reporter.error does not exist and
185
229
  {@link SC.Logger.fallBackOnLog} is true.
186
-
230
+
187
231
  @param {String|Array|Function|Object}
188
232
  @returns {Boolean} true if logged to reporter, false if not
189
233
  */
190
234
  error: function() {
191
235
  var reporter = this.get('reporter');
192
-
236
+
193
237
  if (this.get('exists') && typeof(reporter.error) === "function") {
194
238
  reporter.error.apply(reporter, arguments);
195
239
  return true;
@@ -201,53 +245,53 @@ SC.Logger = SC.Object.create({
201
245
  }
202
246
  return false;
203
247
  },
204
-
248
+
205
249
  /**
206
250
  Every log after this call until {@link SC.Logger.groupEnd} is called
207
251
  will be indented for readability. You can create as many levels
208
252
  as you want.
209
-
253
+
210
254
  @param {String} [title] An optional title to display above the group
211
255
  @returns {Boolean} true if reporter.group exists, false otherwise
212
256
  */
213
257
  group: function(s) {
214
258
  var reporter = this.get('reporter');
215
-
259
+
216
260
  if (this.get('exists') && typeof(reporter.group) === "function") {
217
261
  reporter.group(s);
218
262
  return true;
219
263
  }
220
264
  return false;
221
265
  },
222
-
266
+
223
267
  /**
224
268
  Ends a group declared with {@link SC.Logger.group}.
225
-
269
+
226
270
  @returns {Boolean} true if the reporter.groupEnd exists, false otherwise
227
271
  @see SC.Logger.group
228
272
  */
229
273
  groupEnd: function() {
230
274
  var reporter = this.get('reporter');
231
-
275
+
232
276
  if (this.get('exists') && typeof(reporter.groupEnd) === "function") {
233
277
  reporter.groupEnd();
234
278
  return true;
235
279
  }
236
280
  return false;
237
281
  },
238
-
282
+
239
283
  /**
240
284
  Log an information response to the reporter.
241
-
285
+
242
286
  Logs the response using {@link SC.Logger.log} if reporter.info does not exist and
243
287
  {@link SC.Logger.fallBackOnLog} is true.
244
-
288
+
245
289
  @param {String|Array|Function|Object}
246
290
  @returns {Boolean} true if logged to reporter, false if not
247
291
  */
248
292
  info: function() {
249
293
  var reporter = this.get('reporter');
250
-
294
+
251
295
  if (this.get('exists') && typeof(reporter.info) === "function") {
252
296
  reporter.info.apply(reporter, arguments);
253
297
  return true;
@@ -259,101 +303,101 @@ SC.Logger = SC.Object.create({
259
303
  }
260
304
  return false;
261
305
  },
262
-
306
+
263
307
  /**
264
308
  Begins the JavaScript profiler, if it exists. Call {@link SC.Logger.profileEnd}
265
309
  to end the profiling process and receive a report.
266
-
310
+
267
311
  @returns {Boolean} true if reporter.profile exists, false otherwise
268
312
  */
269
313
  profile: function() {
270
314
  var reporter = this.get('reporter');
271
-
315
+
272
316
  if (this.get('exists') && typeof(reporter.profile) === "function") {
273
317
  reporter.profile();
274
318
  return true;
275
319
  }
276
320
  return false;
277
321
  },
278
-
322
+
279
323
  /**
280
324
  Ends the JavaScript profiler, if it exists.
281
-
325
+
282
326
  @returns {Boolean} true if reporter.profileEnd exists, false otherwise
283
327
  @see SC.Logger.profile
284
328
  */
285
329
  profileEnd: function() {
286
330
  var reporter = this.get('reporter');
287
-
331
+
288
332
  if (this.get('exists') && typeof(reporter.profileEnd) === "function") {
289
333
  reporter.profileEnd();
290
334
  return true;
291
335
  }
292
336
  return false;
293
337
  },
294
-
338
+
295
339
  /**
296
340
  Measure the time between when this function is called and
297
341
  {@link SC.Logger.timeEnd} is called.
298
-
342
+
299
343
  @param {String} name The name of the profile to begin
300
344
  @returns {Boolean} true if reporter.time exists, false otherwise
301
345
  @see SC.Logger.timeEnd
302
346
  */
303
347
  time: function(name) {
304
348
  var reporter = this.get('reporter');
305
-
349
+
306
350
  if (this.get('exists') && typeof(reporter.time) === "function") {
307
351
  reporter.time(name);
308
352
  return true;
309
353
  }
310
354
  return false;
311
355
  },
312
-
356
+
313
357
  /**
314
358
  Ends the profile specified.
315
-
359
+
316
360
  @param {String} name The name of the profile to end
317
361
  @returns {Boolean} true if reporter.timeEnd exists, false otherwise
318
362
  @see SC.Logger.time
319
363
  */
320
364
  timeEnd: function(name) {
321
365
  var reporter = this.get('reporter');
322
-
366
+
323
367
  if (this.get('exists') && typeof(reporter.timeEnd) === "function") {
324
368
  reporter.timeEnd(name);
325
369
  return true;
326
370
  }
327
371
  return false;
328
372
  },
329
-
373
+
330
374
  /**
331
375
  Prints a stack-trace.
332
-
376
+
333
377
  @returns {Boolean} true if reporter.trace exists, false otherwise
334
378
  */
335
379
  trace: function() {
336
380
  var reporter = this.get('reporter');
337
-
381
+
338
382
  if (this.get('exists') && typeof(reporter.trace) === "function") {
339
383
  reporter.trace();
340
384
  return true;
341
385
  }
342
386
  return false;
343
387
  },
344
-
388
+
345
389
  /**
346
390
  Log a warning to the console.
347
-
391
+
348
392
  Logs the warning using {@link SC.Logger.log} if reporter.warning does not exist and
349
393
  {@link SC.Logger.fallBackOnLog} is true.
350
-
394
+
351
395
  @param {String|Array|Function|Object}
352
396
  @returns {Boolean} true if logged to reporter, false if not
353
397
  */
354
398
  warn: function() {
355
399
  var reporter = this.get('reporter');
356
-
400
+
357
401
  if (this.get('exists') && typeof(reporter.warn) === "function") {
358
402
  reporter.warn.apply(reporter, arguments);
359
403
  return true;
@@ -365,17 +409,17 @@ SC.Logger = SC.Object.create({
365
409
  }
366
410
  return false;
367
411
  },
368
-
412
+
369
413
  // ..........................................................
370
414
  // INTERNAL SUPPORT
371
- //
372
-
415
+ //
416
+
373
417
  /**
374
418
  @private
375
-
419
+
376
420
  The arguments function property doesn't support Array#unshift. This helper
377
421
  copies the elements of arguments to a blank array.
378
-
422
+
379
423
  @param {Array} arguments The arguments property of a function
380
424
  @returns {Array} An array containing the elements of arguments parameter
381
425
  */
@@ -387,22 +431,22 @@ SC.Logger = SC.Object.create({
387
431
  }
388
432
  return a;
389
433
  },
390
-
434
+
391
435
  /**
392
436
  @private
393
-
437
+
394
438
  Formats the arguments array of a function by creating a string
395
439
  with SC.LOGGER_LOG_DELIMITER between the elements.
396
-
440
+
397
441
  @returns {String} A string of formatted arguments
398
442
  */
399
443
  _argumentsToString: function() {
400
- var s = "";
401
- for (var i = 0; i<arguments.length - 1; i++) {
402
- s += arguments[i] + SC.LOGGER_LOG_DELIMITER;
403
- }
404
- s += arguments[arguments.length-1];
405
- return s;
444
+ var s = "";
445
+ for (var i = 0; i<arguments.length - 1; i++) {
446
+ s += arguments[i] + SC.LOGGER_LOG_DELIMITER;
447
+ }
448
+ s += arguments[arguments.length-1];
449
+ return s;
406
450
  }
407
-
408
- });
451
+
452
+ });